1 | /* |
2 | * Copyright (c) 2015 Grzegorz Kostka (kostka.grzegorz@gmail.com) |
3 | * All rights reserved. |
4 | * |
5 | * Redistribution and use in source and binary forms, with or without |
6 | * modification, are permitted provided that the following conditions |
7 | * are met: |
8 | * |
9 | * - Redistributions of source code must retain the above copyright |
10 | * notice, this list of conditions and the following disclaimer. |
11 | * - Redistributions in binary form must reproduce the above copyright |
12 | * notice, this list of conditions and the following disclaimer in the |
13 | * documentation and/or other materials provided with the distribution. |
14 | * - The name of the author may not be used to endorse or promote products |
15 | * derived from this software without specific prior written permission. |
16 | * |
17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
18 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
19 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
20 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
21 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
22 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
23 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
24 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
26 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
27 | */ |
28 | |
29 | /** @addtogroup lwext4 |
30 | * @{ |
31 | */ |
32 | /** |
33 | * @file ext4_mkfs.c |
34 | * @brief |
35 | */ |
36 | |
37 | #include <ext4_config.h> |
38 | #include <ext4_types.h> |
39 | #include <ext4_misc.h> |
40 | #include <ext4_errno.h> |
41 | #include <ext4_debug.h> |
42 | |
43 | #include <ext4_super.h> |
44 | #include <ext4_block_group.h> |
45 | #include <ext4_dir.h> |
46 | #include <ext4_dir_idx.h> |
47 | #include <ext4_fs.h> |
48 | #include <ext4_inode.h> |
49 | #include <ext4_ialloc.h> |
50 | #include <ext4_mkfs.h> |
51 | |
52 | #include <inttypes.h> |
53 | #include <string.h> |
54 | #include <stdlib.h> |
55 | |
56 | struct fs_aux_info { |
57 | struct ext4_sblock *sb; |
58 | uint8_t *bg_desc_blk; |
59 | struct xattr_list_element *xattrs; |
60 | uint32_t first_data_block; |
61 | uint64_t len_blocks; |
62 | uint32_t inode_table_blocks; |
63 | uint32_t groups; |
64 | uint32_t bg_desc_blocks; |
65 | uint32_t default_i_flags; |
66 | uint32_t blocks_per_ind; |
67 | uint32_t blocks_per_dind; |
68 | uint32_t blocks_per_tind; |
69 | }; |
70 | |
71 | static inline int log_2(int j) |
72 | { |
73 | int i; |
74 | |
75 | for (i = 0; j > 0; i++) |
76 | j >>= 1; |
77 | |
78 | return i - 1; |
79 | } |
80 | |
81 | static int sb2info(struct ext4_sblock *sb, struct ext4_mkfs_info *info) |
82 | { |
83 | if (to_le16(sb->magic) != EXT4_SUPERBLOCK_MAGIC) |
84 | return EINVAL; |
85 | |
86 | info->block_size = 1024 << to_le32(sb->log_block_size); |
87 | info->blocks_per_group = to_le32(sb->blocks_per_group); |
88 | info->inodes_per_group = to_le32(sb->inodes_per_group); |
89 | info->inode_size = to_le16(sb->inode_size); |
90 | info->inodes = to_le32(sb->inodes_count); |
91 | info->feat_ro_compat = to_le32(sb->features_read_only); |
92 | info->feat_compat = to_le32(sb->features_compatible); |
93 | info->feat_incompat = to_le32(sb->features_incompatible); |
94 | info->bg_desc_reserve_blocks = to_le16(sb->s_reserved_gdt_blocks); |
95 | info->label = sb->volume_name; |
96 | info->len = (uint64_t)info->block_size * ext4_sb_get_blocks_cnt(s: sb); |
97 | info->dsc_size = to_le16(sb->desc_size); |
98 | memcpy(dest: info->uuid, src: sb->uuid, UUID_SIZE); |
99 | |
100 | return EOK; |
101 | } |
102 | |
103 | static uint32_t compute_blocks_per_group(struct ext4_mkfs_info *info) |
104 | { |
105 | return info->block_size * 8; |
106 | } |
107 | |
108 | static uint32_t compute_inodes(struct ext4_mkfs_info *info) |
109 | { |
110 | return (uint32_t)EXT4_DIV_ROUND_UP(info->len, info->block_size) / 4; |
111 | } |
112 | |
113 | static uint32_t compute_inodes_per_group(struct ext4_mkfs_info *info) |
114 | { |
115 | uint32_t blocks = (uint32_t)EXT4_DIV_ROUND_UP(info->len, info->block_size); |
116 | uint32_t block_groups = EXT4_DIV_ROUND_UP(blocks, info->blocks_per_group); |
117 | uint32_t inodes = EXT4_DIV_ROUND_UP(info->inodes, block_groups); |
118 | inodes = EXT4_ALIGN(inodes, (info->block_size / info->inode_size)); |
119 | |
120 | /* After properly rounding up the number of inodes/group, |
121 | * make sure to update the total inodes field in the info struct. |
122 | */ |
123 | info->inodes = inodes * block_groups; |
124 | |
125 | return inodes; |
126 | } |
127 | |
128 | |
129 | static uint32_t compute_journal_blocks(struct ext4_mkfs_info *info) |
130 | { |
131 | uint32_t journal_blocks = (uint32_t)EXT4_DIV_ROUND_UP(info->len, |
132 | info->block_size) / 64; |
133 | if (journal_blocks < 1024) |
134 | journal_blocks = 1024; |
135 | if (journal_blocks > 32768) |
136 | journal_blocks = 32768; |
137 | return journal_blocks; |
138 | } |
139 | |
140 | static bool has_superblock(struct ext4_mkfs_info *info, uint32_t bgid) |
141 | { |
142 | if (!(info->feat_ro_compat & EXT4_FRO_COM_SPARSE_SUPER)) |
143 | return true; |
144 | |
145 | return ext4_sb_sparse(group: bgid); |
146 | } |
147 | |
148 | static int create_fs_aux_info(struct fs_aux_info *aux_info, |
149 | struct ext4_mkfs_info *info) |
150 | { |
151 | aux_info->first_data_block = (info->block_size > 1024) ? 0 : 1; |
152 | aux_info->len_blocks = info->len / info->block_size; |
153 | aux_info->inode_table_blocks = EXT4_DIV_ROUND_UP(info->inodes_per_group * |
154 | info->inode_size, info->block_size); |
155 | aux_info->groups = (uint32_t)EXT4_DIV_ROUND_UP(aux_info->len_blocks - |
156 | aux_info->first_data_block, info->blocks_per_group); |
157 | aux_info->blocks_per_ind = info->block_size / sizeof(uint32_t); |
158 | aux_info->blocks_per_dind = |
159 | aux_info->blocks_per_ind * aux_info->blocks_per_ind; |
160 | aux_info->blocks_per_tind = |
161 | aux_info->blocks_per_dind * aux_info->blocks_per_dind; |
162 | |
163 | aux_info->bg_desc_blocks = |
164 | EXT4_DIV_ROUND_UP(aux_info->groups * info->dsc_size, |
165 | info->block_size); |
166 | |
167 | aux_info->default_i_flags = EXT4_INODE_FLAG_NOATIME; |
168 | |
169 | uint32_t last_group_size = aux_info->len_blocks % info->blocks_per_group; |
170 | uint32_t = 2 + aux_info->inode_table_blocks; |
171 | if (has_superblock(info, bgid: aux_info->groups - 1)) |
172 | last_header_size += 1 + aux_info->bg_desc_blocks + |
173 | info->bg_desc_reserve_blocks; |
174 | |
175 | if (last_group_size > 0 && last_group_size < last_header_size) { |
176 | aux_info->groups--; |
177 | aux_info->len_blocks -= last_group_size; |
178 | } |
179 | |
180 | aux_info->sb = ext4_calloc(count: 1, EXT4_SUPERBLOCK_SIZE); |
181 | if (!aux_info->sb) |
182 | return ENOMEM; |
183 | |
184 | aux_info->bg_desc_blk = ext4_calloc(count: 1, size: info->block_size); |
185 | if (!aux_info->bg_desc_blk) |
186 | return ENOMEM; |
187 | |
188 | aux_info->xattrs = NULL; |
189 | |
190 | |
191 | ext4_dbg(DEBUG_MKFS, DBG_INFO "create_fs_aux_info\n" ); |
192 | ext4_dbg(DEBUG_MKFS, DBG_NONE "first_data_block: %" PRIu32"\n" , |
193 | aux_info->first_data_block); |
194 | ext4_dbg(DEBUG_MKFS, DBG_NONE "len_blocks: %" PRIu64"\n" , |
195 | aux_info->len_blocks); |
196 | ext4_dbg(DEBUG_MKFS, DBG_NONE "inode_table_blocks: %" PRIu32"\n" , |
197 | aux_info->inode_table_blocks); |
198 | ext4_dbg(DEBUG_MKFS, DBG_NONE "groups: %" PRIu32"\n" , |
199 | aux_info->groups); |
200 | ext4_dbg(DEBUG_MKFS, DBG_NONE "bg_desc_blocks: %" PRIu32"\n" , |
201 | aux_info->bg_desc_blocks); |
202 | ext4_dbg(DEBUG_MKFS, DBG_NONE "default_i_flags: %" PRIu32"\n" , |
203 | aux_info->default_i_flags); |
204 | ext4_dbg(DEBUG_MKFS, DBG_NONE "blocks_per_ind: %" PRIu32"\n" , |
205 | aux_info->blocks_per_ind); |
206 | ext4_dbg(DEBUG_MKFS, DBG_NONE "blocks_per_dind: %" PRIu32"\n" , |
207 | aux_info->blocks_per_dind); |
208 | ext4_dbg(DEBUG_MKFS, DBG_NONE "blocks_per_tind: %" PRIu32"\n" , |
209 | aux_info->blocks_per_tind); |
210 | |
211 | return EOK; |
212 | } |
213 | |
214 | static void release_fs_aux_info(struct fs_aux_info *aux_info) |
215 | { |
216 | if (aux_info->sb) |
217 | ext4_free(pointer: aux_info->sb); |
218 | if (aux_info->bg_desc_blk) |
219 | ext4_free(pointer: aux_info->bg_desc_blk); |
220 | } |
221 | |
222 | |
223 | /* Fill in the superblock memory buffer based on the filesystem parameters */ |
224 | static void fill_sb(struct fs_aux_info *aux_info, struct ext4_mkfs_info *info) |
225 | { |
226 | struct ext4_sblock *sb = aux_info->sb; |
227 | |
228 | sb->inodes_count = to_le32(info->inodes_per_group * aux_info->groups); |
229 | |
230 | ext4_sb_set_blocks_cnt(s: sb, cnt: aux_info->len_blocks); |
231 | ext4_sb_set_free_blocks_cnt(s: sb, cnt: aux_info->len_blocks); |
232 | sb->free_inodes_count = to_le32(info->inodes_per_group * aux_info->groups); |
233 | |
234 | sb->reserved_blocks_count_lo = to_le32(0); |
235 | sb->first_data_block = to_le32(aux_info->first_data_block); |
236 | sb->log_block_size = to_le32(log_2(info->block_size / 1024)); |
237 | sb->log_cluster_size = to_le32(log_2(info->block_size / 1024)); |
238 | sb->blocks_per_group = to_le32(info->blocks_per_group); |
239 | sb->frags_per_group = to_le32(info->blocks_per_group); |
240 | sb->inodes_per_group = to_le32(info->inodes_per_group); |
241 | sb->mount_time = to_le32(0); |
242 | sb->write_time = to_le32(0); |
243 | sb->mount_count = to_le16(0); |
244 | sb->max_mount_count = to_le16(0xFFFF); |
245 | sb->magic = to_le16(EXT4_SUPERBLOCK_MAGIC); |
246 | sb->state = to_le16(EXT4_SUPERBLOCK_STATE_VALID_FS); |
247 | sb->errors = to_le16(EXT4_SUPERBLOCK_ERRORS_RO); |
248 | sb->minor_rev_level = to_le16(0); |
249 | sb->last_check_time = to_le32(0); |
250 | sb->check_interval = to_le32(0); |
251 | sb->creator_os = to_le32(EXT4_SUPERBLOCK_OS_LINUX); |
252 | sb->rev_level = to_le32(1); |
253 | sb->def_resuid = to_le16(0); |
254 | sb->def_resgid = to_le16(0); |
255 | |
256 | sb->first_inode = to_le32(EXT4_GOOD_OLD_FIRST_INO); |
257 | sb->inode_size = to_le16(info->inode_size); |
258 | sb->block_group_index = to_le16(0); |
259 | |
260 | sb->features_compatible = to_le32(info->feat_compat); |
261 | sb->features_incompatible = to_le32(info->feat_incompat); |
262 | sb->features_read_only = to_le32(info->feat_ro_compat); |
263 | |
264 | memcpy(dest: sb->uuid, src: info->uuid, UUID_SIZE); |
265 | |
266 | memset(dest: sb->volume_name, c: 0, size: sizeof(sb->volume_name)); |
267 | strncpy(dest: sb->volume_name, src: info->label, max_size: sizeof(sb->volume_name)); |
268 | memset(dest: sb->last_mounted, c: 0, size: sizeof(sb->last_mounted)); |
269 | |
270 | sb->algorithm_usage_bitmap = to_le32(0); |
271 | sb->s_prealloc_blocks = 0; |
272 | sb->s_prealloc_dir_blocks = 0; |
273 | sb->s_reserved_gdt_blocks = to_le16(info->bg_desc_reserve_blocks); |
274 | |
275 | if (info->feat_compat & EXT4_FCOM_HAS_JOURNAL) |
276 | sb->journal_inode_number = to_le32(EXT4_JOURNAL_INO); |
277 | |
278 | sb->journal_backup_type = 1; |
279 | sb->journal_dev = to_le32(0); |
280 | sb->last_orphan = to_le32(0); |
281 | sb->hash_seed[0] = to_le32(0x11111111); |
282 | sb->hash_seed[1] = to_le32(0x22222222); |
283 | sb->hash_seed[2] = to_le32(0x33333333); |
284 | sb->hash_seed[3] = to_le32(0x44444444); |
285 | sb->default_hash_version = EXT2_HTREE_HALF_MD4; |
286 | sb->checksum_type = 1; |
287 | sb->desc_size = to_le16(info->dsc_size); |
288 | sb->default_mount_opts = to_le32(0); |
289 | sb->first_meta_bg = to_le32(0); |
290 | sb->mkfs_time = to_le32(0); |
291 | |
292 | sb->reserved_blocks_count_hi = to_le32(0); |
293 | sb->min_extra_isize = to_le32(sizeof(struct ext4_inode) - |
294 | EXT4_GOOD_OLD_INODE_SIZE); |
295 | sb->want_extra_isize = to_le32(sizeof(struct ext4_inode) - |
296 | EXT4_GOOD_OLD_INODE_SIZE); |
297 | sb->flags = to_le32(EXT4_SUPERBLOCK_FLAGS_SIGNED_HASH); |
298 | } |
299 | |
300 | |
301 | static int write_bgroup_block(struct ext4_blockdev *bd, |
302 | struct fs_aux_info *aux_info, |
303 | struct ext4_mkfs_info *info, |
304 | uint32_t blk) |
305 | { |
306 | int r = EOK; |
307 | uint32_t j; |
308 | struct ext4_block b; |
309 | |
310 | uint32_t block_size = ext4_sb_get_block_size(s: aux_info->sb); |
311 | |
312 | for (j = 0; j < aux_info->groups; j++) { |
313 | uint64_t bg_start_block = aux_info->first_data_block + |
314 | j * info->blocks_per_group; |
315 | uint32_t blk_off = 0; |
316 | |
317 | blk_off += aux_info->bg_desc_blocks; |
318 | if (has_superblock(info, bgid: j)) { |
319 | bg_start_block++; |
320 | blk_off += info->bg_desc_reserve_blocks; |
321 | } |
322 | |
323 | uint64_t dsc_blk = bg_start_block + blk; |
324 | |
325 | r = ext4_block_get_noread(bdev: bd, b: &b, lba: dsc_blk); |
326 | if (r != EOK) |
327 | return r; |
328 | |
329 | memcpy(dest: b.data, src: aux_info->bg_desc_blk, size: block_size); |
330 | |
331 | ext4_bcache_set_dirty(buf: b.buf); |
332 | r = ext4_block_set(bdev: bd, b: &b); |
333 | if (r != EOK) |
334 | return r; |
335 | } |
336 | |
337 | return r; |
338 | } |
339 | |
340 | static int write_bgroups(struct ext4_blockdev *bd, struct fs_aux_info *aux_info, |
341 | struct ext4_mkfs_info *info) |
342 | { |
343 | int r = EOK; |
344 | |
345 | struct ext4_block b; |
346 | struct ext4_bgroup *bg_desc; |
347 | |
348 | uint32_t i; |
349 | uint32_t bg_free_blk = 0; |
350 | uint64_t sb_free_blk = 0; |
351 | uint32_t block_size = ext4_sb_get_block_size(s: aux_info->sb); |
352 | uint32_t dsc_size = ext4_sb_get_desc_size(s: aux_info->sb); |
353 | uint32_t dsc_per_block = block_size / dsc_size; |
354 | uint32_t k = 0; |
355 | |
356 | for (i = 0; i < aux_info->groups; i++) { |
357 | uint64_t bg_start_block = aux_info->first_data_block + |
358 | aux_info->first_data_block + i * info->blocks_per_group; |
359 | uint32_t blk_off = 0; |
360 | |
361 | bg_desc = (void *)(aux_info->bg_desc_blk + k * dsc_size); |
362 | bg_free_blk = info->blocks_per_group - |
363 | aux_info->inode_table_blocks; |
364 | |
365 | bg_free_blk -= 2; |
366 | blk_off += aux_info->bg_desc_blocks; |
367 | |
368 | if (i == (aux_info->groups - 1)) |
369 | bg_free_blk -= aux_info->first_data_block; |
370 | |
371 | if (has_superblock(info, bgid: i)) { |
372 | bg_start_block++; |
373 | blk_off += info->bg_desc_reserve_blocks; |
374 | bg_free_blk -= info->bg_desc_reserve_blocks + 1; |
375 | bg_free_blk -= aux_info->bg_desc_blocks; |
376 | } |
377 | |
378 | ext4_bg_set_block_bitmap(bg: bg_desc, s: aux_info->sb, |
379 | blk: bg_start_block + blk_off + 1); |
380 | |
381 | ext4_bg_set_inode_bitmap(bg: bg_desc, s: aux_info->sb, |
382 | blk: bg_start_block + blk_off + 2); |
383 | |
384 | ext4_bg_set_inode_table_first_block(bg: bg_desc, |
385 | s: aux_info->sb, |
386 | blk: bg_start_block + blk_off + 3); |
387 | |
388 | ext4_bg_set_free_blocks_count(bg: bg_desc, s: aux_info->sb, |
389 | cnt: bg_free_blk); |
390 | |
391 | ext4_bg_set_free_inodes_count(bg: bg_desc, |
392 | s: aux_info->sb, to_le32(aux_info->sb->inodes_per_group)); |
393 | |
394 | ext4_bg_set_used_dirs_count(bg: bg_desc, s: aux_info->sb, cnt: 0); |
395 | |
396 | ext4_bg_set_flag(bg: bg_desc, |
397 | EXT4_BLOCK_GROUP_BLOCK_UNINIT | |
398 | EXT4_BLOCK_GROUP_INODE_UNINIT); |
399 | |
400 | sb_free_blk += bg_free_blk; |
401 | |
402 | r = ext4_block_get_noread(bdev: bd, b: &b, lba: bg_start_block + blk_off + 1); |
403 | if (r != EOK) |
404 | return r; |
405 | memset(dest: b.data, c: 0, size: block_size); |
406 | ext4_bcache_set_dirty(buf: b.buf); |
407 | r = ext4_block_set(bdev: bd, b: &b); |
408 | if (r != EOK) |
409 | return r; |
410 | r = ext4_block_get_noread(bdev: bd, b: &b, lba: bg_start_block + blk_off + 2); |
411 | if (r != EOK) |
412 | return r; |
413 | memset(dest: b.data, c: 0, size: block_size); |
414 | ext4_bcache_set_dirty(buf: b.buf); |
415 | r = ext4_block_set(bdev: bd, b: &b); |
416 | if (r != EOK) |
417 | return r; |
418 | |
419 | if (++k != dsc_per_block) |
420 | continue; |
421 | |
422 | k = 0; |
423 | r = write_bgroup_block(bd, aux_info, info, blk: i / dsc_per_block); |
424 | if (r != EOK) |
425 | return r; |
426 | |
427 | } |
428 | |
429 | r = write_bgroup_block(bd, aux_info, info, blk: i / dsc_per_block); |
430 | if (r != EOK) |
431 | return r; |
432 | |
433 | ext4_sb_set_free_blocks_cnt(s: aux_info->sb, cnt: sb_free_blk); |
434 | return r; |
435 | } |
436 | |
437 | static int write_sblocks(struct ext4_blockdev *bd, struct fs_aux_info *aux_info, |
438 | struct ext4_mkfs_info *info) |
439 | { |
440 | uint64_t offset; |
441 | uint32_t i; |
442 | int r; |
443 | |
444 | /* write out the backup superblocks */ |
445 | for (i = 1; i < aux_info->groups; i++) { |
446 | if (has_superblock(info, bgid: i)) { |
447 | offset = info->block_size * (aux_info->first_data_block |
448 | + i * info->blocks_per_group); |
449 | |
450 | aux_info->sb->block_group_index = to_le16(i); |
451 | r = ext4_block_writebytes(bdev: bd, off: offset, buf: aux_info->sb, |
452 | EXT4_SUPERBLOCK_SIZE); |
453 | if (r != EOK) |
454 | return r; |
455 | } |
456 | } |
457 | |
458 | /* write out the primary superblock */ |
459 | aux_info->sb->block_group_index = to_le16(0); |
460 | return ext4_block_writebytes(bdev: bd, off: 1024, buf: aux_info->sb, |
461 | EXT4_SUPERBLOCK_SIZE); |
462 | } |
463 | |
464 | |
465 | int ext4_mkfs_read_info(struct ext4_blockdev *bd, struct ext4_mkfs_info *info) |
466 | { |
467 | int r; |
468 | struct ext4_sblock *sb = NULL; |
469 | r = ext4_block_init(bdev: bd); |
470 | if (r != EOK) |
471 | return r; |
472 | |
473 | sb = ext4_malloc(EXT4_SUPERBLOCK_SIZE); |
474 | if (!sb) |
475 | goto Finish; |
476 | |
477 | |
478 | r = ext4_sb_read(bdev: bd, s: sb); |
479 | if (r != EOK) |
480 | goto Finish; |
481 | |
482 | r = sb2info(sb, info); |
483 | |
484 | Finish: |
485 | if (sb) |
486 | ext4_free(pointer: sb); |
487 | ext4_block_fini(bdev: bd); |
488 | return r; |
489 | } |
490 | |
491 | static int mkfs_init(struct ext4_blockdev *bd, struct ext4_mkfs_info *info) |
492 | { |
493 | int r; |
494 | struct fs_aux_info aux_info; |
495 | memset(dest: &aux_info, c: 0, size: sizeof(struct fs_aux_info)); |
496 | |
497 | r = create_fs_aux_info(aux_info: &aux_info, info); |
498 | if (r != EOK) |
499 | goto Finish; |
500 | |
501 | fill_sb(aux_info: &aux_info, info); |
502 | |
503 | r = write_bgroups(bd, aux_info: &aux_info, info); |
504 | if (r != EOK) |
505 | goto Finish; |
506 | |
507 | r = write_sblocks(bd, aux_info: &aux_info, info); |
508 | if (r != EOK) |
509 | goto Finish; |
510 | |
511 | Finish: |
512 | release_fs_aux_info(aux_info: &aux_info); |
513 | return r; |
514 | } |
515 | |
516 | static int init_bgs(struct ext4_fs *fs) |
517 | { |
518 | int r = EOK; |
519 | struct ext4_block_group_ref ref; |
520 | uint32_t i; |
521 | uint32_t bg_count = ext4_block_group_cnt(s: &fs->sb); |
522 | for (i = 0; i < bg_count; ++i) { |
523 | r = ext4_fs_get_block_group_ref(fs, bgid: i, ref: &ref); |
524 | if (r != EOK) |
525 | break; |
526 | |
527 | r = ext4_fs_put_block_group_ref(ref: &ref); |
528 | if (r != EOK) |
529 | break; |
530 | } |
531 | return r; |
532 | } |
533 | |
534 | static int alloc_inodes(struct ext4_fs *fs) |
535 | { |
536 | int r = EOK; |
537 | int i; |
538 | struct ext4_inode_ref inode_ref; |
539 | for (i = 1; i < 12; ++i) { |
540 | int filetype = EXT4_DE_REG_FILE; |
541 | |
542 | switch (i) { |
543 | case EXT4_ROOT_INO: |
544 | case EXT4_GOOD_OLD_FIRST_INO: |
545 | filetype = EXT4_DE_DIR; |
546 | break; |
547 | default: |
548 | break; |
549 | } |
550 | |
551 | r = ext4_fs_alloc_inode(fs, inode_ref: &inode_ref, filetype); |
552 | if (r != EOK) |
553 | return r; |
554 | |
555 | ext4_inode_set_mode(sb: &fs->sb, inode: inode_ref.inode, mode: 0); |
556 | |
557 | switch (i) { |
558 | case EXT4_ROOT_INO: |
559 | case EXT4_JOURNAL_INO: |
560 | ext4_fs_inode_blocks_init(fs, inode_ref: &inode_ref); |
561 | break; |
562 | } |
563 | |
564 | ext4_fs_put_inode_ref(ref: &inode_ref); |
565 | } |
566 | |
567 | return r; |
568 | } |
569 | |
570 | static int create_dirs(struct ext4_fs *fs) |
571 | { |
572 | int r = EOK; |
573 | struct ext4_inode_ref root; |
574 | struct ext4_inode_ref child; |
575 | |
576 | r = ext4_fs_get_inode_ref(fs, EXT4_ROOT_INO, ref: &root); |
577 | if (r != EOK) |
578 | return r; |
579 | |
580 | r = ext4_fs_get_inode_ref(fs, EXT4_GOOD_OLD_FIRST_INO, ref: &child); |
581 | if (r != EOK) |
582 | return r; |
583 | |
584 | ext4_inode_set_mode(sb: &fs->sb, inode: child.inode, |
585 | EXT4_INODE_MODE_DIRECTORY | 0777); |
586 | |
587 | ext4_inode_set_mode(sb: &fs->sb, inode: root.inode, |
588 | EXT4_INODE_MODE_DIRECTORY | 0777); |
589 | |
590 | #if CONFIG_DIR_INDEX_ENABLE |
591 | /* Initialize directory index if supported */ |
592 | if (ext4_sb_feature_com(s: &fs->sb, EXT4_FCOM_DIR_INDEX)) { |
593 | r = ext4_dir_dx_init(dir: &root, parent: &root); |
594 | if (r != EOK) |
595 | return r; |
596 | |
597 | r = ext4_dir_dx_init(dir: &child, parent: &root); |
598 | if (r != EOK) |
599 | return r; |
600 | |
601 | ext4_inode_set_flag(inode: root.inode, EXT4_INODE_FLAG_INDEX); |
602 | ext4_inode_set_flag(inode: child.inode, EXT4_INODE_FLAG_INDEX); |
603 | } else |
604 | #endif |
605 | { |
606 | r = ext4_dir_add_entry(parent: &root, name: "." , name_len: strlen(s: "." ), child: &root); |
607 | if (r != EOK) |
608 | return r; |
609 | |
610 | r = ext4_dir_add_entry(parent: &root, name: ".." , name_len: strlen(s: ".." ), child: &root); |
611 | if (r != EOK) |
612 | return r; |
613 | |
614 | r = ext4_dir_add_entry(parent: &child, name: "." , name_len: strlen(s: "." ), child: &child); |
615 | if (r != EOK) |
616 | return r; |
617 | |
618 | r = ext4_dir_add_entry(parent: &child, name: ".." , name_len: strlen(s: ".." ), child: &root); |
619 | if (r != EOK) |
620 | return r; |
621 | } |
622 | |
623 | r = ext4_dir_add_entry(parent: &root, name: "lost+found" , name_len: strlen(s: "lost+found" ), child: &child); |
624 | if (r != EOK) |
625 | return r; |
626 | |
627 | ext4_inode_set_links_cnt(inode: root.inode, cnt: 3); |
628 | ext4_inode_set_links_cnt(inode: child.inode, cnt: 2); |
629 | |
630 | child.dirty = true; |
631 | root.dirty = true; |
632 | ext4_fs_put_inode_ref(ref: &child); |
633 | ext4_fs_put_inode_ref(ref: &root); |
634 | return r; |
635 | } |
636 | |
637 | static int create_journal_inode(struct ext4_fs *fs, |
638 | struct ext4_mkfs_info *info) |
639 | { |
640 | int ret; |
641 | struct ext4_inode_ref inode_ref; |
642 | uint64_t blocks_count; |
643 | |
644 | if (!info->journal) |
645 | return EOK; |
646 | |
647 | ret = ext4_fs_get_inode_ref(fs, EXT4_JOURNAL_INO, ref: &inode_ref); |
648 | if (ret != EOK) |
649 | return ret; |
650 | |
651 | struct ext4_inode *inode = inode_ref.inode; |
652 | |
653 | ext4_inode_set_mode(sb: &fs->sb, inode, EXT4_INODE_MODE_FILE | 0600); |
654 | ext4_inode_set_links_cnt(inode, cnt: 1); |
655 | |
656 | blocks_count = ext4_inode_get_blocks_count(sb: &fs->sb, inode); |
657 | |
658 | while (blocks_count++ < info->journal_blocks) |
659 | { |
660 | ext4_fsblk_t fblock; |
661 | ext4_lblk_t iblock; |
662 | struct ext4_block blk; |
663 | |
664 | ret = ext4_fs_append_inode_dblk(inode_ref: &inode_ref, fblock: &fblock, iblock: &iblock); |
665 | if (ret != EOK) |
666 | goto Finish; |
667 | |
668 | if (iblock != 0) |
669 | continue; |
670 | |
671 | ret = ext4_block_get(bdev: fs->bdev, b: &blk, lba: fblock); |
672 | if (ret != EOK) |
673 | goto Finish; |
674 | |
675 | |
676 | struct jbd_sb * jbd_sb = (struct jbd_sb * )blk.data; |
677 | memset(dest: jbd_sb, c: 0, size: sizeof(struct jbd_sb)); |
678 | |
679 | jbd_sb->header.magic = to_be32(JBD_MAGIC_NUMBER); |
680 | jbd_sb->header.blocktype = to_be32(JBD_SUPERBLOCK_V2); |
681 | jbd_sb->blocksize = to_be32(info->block_size); |
682 | jbd_sb->maxlen = to_be32(info->journal_blocks); |
683 | jbd_sb->nr_users = to_be32(1); |
684 | jbd_sb->first = to_be32(1); |
685 | jbd_sb->sequence = to_be32(1); |
686 | |
687 | ext4_bcache_set_dirty(buf: blk.buf); |
688 | ret = ext4_block_set(bdev: fs->bdev, b: &blk); |
689 | if (ret != EOK) |
690 | goto Finish; |
691 | } |
692 | |
693 | memcpy(dest: fs->sb.journal_blocks, src: inode->blocks, size: sizeof(inode->blocks)); |
694 | |
695 | Finish: |
696 | ext4_fs_put_inode_ref(ref: &inode_ref); |
697 | |
698 | return ret; |
699 | } |
700 | |
701 | int ext4_mkfs(struct ext4_fs *fs, struct ext4_blockdev *bd, |
702 | struct ext4_mkfs_info *info, int fs_type) |
703 | { |
704 | int r; |
705 | |
706 | r = ext4_block_init(bdev: bd); |
707 | if (r != EOK) |
708 | return r; |
709 | |
710 | bd->fs = fs; |
711 | |
712 | if (info->len == 0) |
713 | info->len = bd->part_size; |
714 | |
715 | if (info->block_size == 0) |
716 | info->block_size = 4096; /*Set block size to default value*/ |
717 | |
718 | /* Round down the filesystem length to be a multiple of the block size */ |
719 | info->len &= ~((uint64_t)info->block_size - 1); |
720 | |
721 | if (info->journal_blocks == 0) |
722 | info->journal_blocks = compute_journal_blocks(info); |
723 | |
724 | if (info->blocks_per_group == 0) |
725 | info->blocks_per_group = compute_blocks_per_group(info); |
726 | |
727 | if (info->inodes == 0) |
728 | info->inodes = compute_inodes(info); |
729 | |
730 | if (info->inode_size == 0) |
731 | info->inode_size = 256; |
732 | |
733 | if (info->label == NULL) |
734 | info->label = "" ; |
735 | |
736 | info->inodes_per_group = compute_inodes_per_group(info); |
737 | |
738 | switch (fs_type) { |
739 | case F_SET_EXT2: |
740 | info->feat_compat = EXT2_SUPPORTED_FCOM; |
741 | info->feat_ro_compat = EXT2_SUPPORTED_FRO_COM; |
742 | info->feat_incompat = EXT2_SUPPORTED_FINCOM; |
743 | break; |
744 | case F_SET_EXT3: |
745 | info->feat_compat = EXT3_SUPPORTED_FCOM; |
746 | info->feat_ro_compat = EXT3_SUPPORTED_FRO_COM; |
747 | info->feat_incompat = EXT3_SUPPORTED_FINCOM; |
748 | break; |
749 | case F_SET_EXT4: |
750 | info->feat_compat = EXT4_SUPPORTED_FCOM; |
751 | info->feat_ro_compat = EXT4_SUPPORTED_FRO_COM; |
752 | info->feat_incompat = EXT4_SUPPORTED_FINCOM; |
753 | break; |
754 | } |
755 | |
756 | /*TODO: handle this features some day...*/ |
757 | info->feat_incompat &= ~EXT4_FINCOM_META_BG; |
758 | info->feat_incompat &= ~EXT4_FINCOM_FLEX_BG; |
759 | info->feat_incompat &= ~EXT4_FINCOM_64BIT; |
760 | |
761 | info->feat_ro_compat &= ~EXT4_FRO_COM_METADATA_CSUM; |
762 | info->feat_ro_compat &= ~EXT4_FRO_COM_GDT_CSUM; |
763 | info->feat_ro_compat &= ~EXT4_FRO_COM_DIR_NLINK; |
764 | info->feat_ro_compat &= ~EXT4_FRO_COM_EXTRA_ISIZE; |
765 | info->feat_ro_compat &= ~EXT4_FRO_COM_HUGE_FILE; |
766 | |
767 | if (info->journal) |
768 | info->feat_compat |= EXT4_FCOM_HAS_JOURNAL; |
769 | |
770 | if (info->dsc_size == 0) { |
771 | |
772 | if (info->feat_incompat & EXT4_FINCOM_64BIT) |
773 | info->dsc_size = EXT4_MAX_BLOCK_GROUP_DESCRIPTOR_SIZE; |
774 | else |
775 | info->dsc_size = EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE; |
776 | } |
777 | |
778 | info->bg_desc_reserve_blocks = 0; |
779 | |
780 | ext4_dbg(DEBUG_MKFS, DBG_INFO "Creating filesystem with parameters:\n" ); |
781 | ext4_dbg(DEBUG_MKFS, DBG_NONE "Size: %" PRIu64"\n" , info->len); |
782 | ext4_dbg(DEBUG_MKFS, DBG_NONE "Block size: %" PRIu32"\n" , |
783 | info->block_size); |
784 | ext4_dbg(DEBUG_MKFS, DBG_NONE "Blocks per group: %" PRIu32"\n" , |
785 | info->blocks_per_group); |
786 | ext4_dbg(DEBUG_MKFS, DBG_NONE "Inodes per group: %" PRIu32"\n" , |
787 | info->inodes_per_group); |
788 | ext4_dbg(DEBUG_MKFS, DBG_NONE "Inode size: %" PRIu32"\n" , |
789 | info->inode_size); |
790 | ext4_dbg(DEBUG_MKFS, DBG_NONE "Inodes: %" PRIu32"\n" , info->inodes); |
791 | ext4_dbg(DEBUG_MKFS, DBG_NONE "Journal blocks: %" PRIu32"\n" , |
792 | info->journal_blocks); |
793 | ext4_dbg(DEBUG_MKFS, DBG_NONE "Features ro_compat: 0x%x\n" , |
794 | info->feat_ro_compat); |
795 | ext4_dbg(DEBUG_MKFS, DBG_NONE "Features compat: 0x%x\n" , |
796 | info->feat_compat); |
797 | ext4_dbg(DEBUG_MKFS, DBG_NONE "Features incompat: 0x%x\n" , |
798 | info->feat_incompat); |
799 | ext4_dbg(DEBUG_MKFS, DBG_NONE "BG desc reserve: %" PRIu32"\n" , |
800 | info->bg_desc_reserve_blocks); |
801 | ext4_dbg(DEBUG_MKFS, DBG_NONE "Descriptor size: %" PRIu16"\n" , |
802 | info->dsc_size); |
803 | ext4_dbg(DEBUG_MKFS, DBG_NONE "journal: %s\n" , |
804 | info->journal ? "yes" : "no" ); |
805 | ext4_dbg(DEBUG_MKFS, DBG_NONE "Label: %s\n" , info->label); |
806 | |
807 | struct ext4_bcache bc; |
808 | |
809 | memset(dest: &bc, c: 0, size: sizeof(struct ext4_bcache)); |
810 | ext4_block_set_lb_size(bdev: bd, lb_bsize: info->block_size); |
811 | |
812 | r = ext4_bcache_init_dynamic(bc: &bc, CONFIG_BLOCK_DEV_CACHE_SIZE, |
813 | itemsize: info->block_size); |
814 | if (r != EOK) |
815 | goto block_fini; |
816 | |
817 | /*Bind block cache to block device*/ |
818 | r = ext4_block_bind_bcache(bdev: bd, bc: &bc); |
819 | if (r != EOK) |
820 | goto cache_fini; |
821 | |
822 | r = ext4_block_cache_write_back(bdev: bd, on_off: 1); |
823 | if (r != EOK) |
824 | goto cache_fini; |
825 | |
826 | r = mkfs_init(bd, info); |
827 | if (r != EOK) |
828 | goto cache_fini; |
829 | |
830 | r = ext4_fs_init(fs, bdev: bd, read_only: false); |
831 | if (r != EOK) |
832 | goto cache_fini; |
833 | |
834 | r = init_bgs(fs); |
835 | if (r != EOK) |
836 | goto fs_fini; |
837 | |
838 | r = alloc_inodes(fs); |
839 | if (r != EOK) |
840 | goto fs_fini; |
841 | |
842 | r = create_dirs(fs); |
843 | if (r != EOK) |
844 | goto fs_fini; |
845 | |
846 | r = create_journal_inode(fs, info); |
847 | if (r != EOK) |
848 | goto fs_fini; |
849 | |
850 | fs_fini: |
851 | ext4_fs_fini(fs); |
852 | |
853 | cache_fini: |
854 | ext4_block_cache_write_back(bdev: bd, on_off: 0); |
855 | ext4_bcache_fini_dynamic(bc: &bc); |
856 | |
857 | block_fini: |
858 | ext4_block_fini(bdev: bd); |
859 | |
860 | return r; |
861 | } |
862 | |
863 | /** |
864 | * @} |
865 | */ |
866 | |