diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2009-10-20 00:06:03 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2009-10-20 00:06:03 +0200 |
commit | 05647b55512d0fec74fe6d69553a773e852674df (patch) | |
tree | e12249ce2de19cfc4280f2850574958202eb8d9c /util-linux/mkfs_ext2.c | |
parent | 3ef4f77620a9f5f9a7c1247e29ea9c14e07b8a30 (diff) | |
download | busybox-w32-05647b55512d0fec74fe6d69553a773e852674df.tar.gz busybox-w32-05647b55512d0fec74fe6d69553a773e852674df.tar.bz2 busybox-w32-05647b55512d0fec74fe6d69553a773e852674df.zip |
mkfs_ext2: compat fixes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'util-linux/mkfs_ext2.c')
-rw-r--r-- | util-linux/mkfs_ext2.c | 387 |
1 files changed, 197 insertions, 190 deletions
diff --git a/util-linux/mkfs_ext2.c b/util-linux/mkfs_ext2.c index 38654ef04..d0ebc1dd9 100644 --- a/util-linux/mkfs_ext2.c +++ b/util-linux/mkfs_ext2.c | |||
@@ -10,6 +10,10 @@ | |||
10 | #include "libbb.h" | 10 | #include "libbb.h" |
11 | #include <linux/fs.h> | 11 | #include <linux/fs.h> |
12 | #include <linux/ext2_fs.h> | 12 | #include <linux/ext2_fs.h> |
13 | #include <sys/user.h> /* PAGE_SIZE */ | ||
14 | #ifndef PAGE_SIZE | ||
15 | # define PAGE_SIZE 4096 | ||
16 | #endif | ||
13 | #include "volume_id/volume_id_internal.h" | 17 | #include "volume_id/volume_id_internal.h" |
14 | 18 | ||
15 | #define ENABLE_FEATURE_MKFS_EXT2_RESERVED_GDT 0 | 19 | #define ENABLE_FEATURE_MKFS_EXT2_RESERVED_GDT 0 |
@@ -26,8 +30,8 @@ | |||
26 | //#define LINUX_S_IFWHT 0160000 | 30 | //#define LINUX_S_IFWHT 0160000 |
27 | //#define EXT2_FEATURE_INCOMPAT_WHITEOUT 0x0020 | 31 | //#define EXT2_FEATURE_INCOMPAT_WHITEOUT 0x0020 |
28 | 32 | ||
29 | // storage helper | 33 | // storage helpers |
30 | void BUG_unsupported_field_size(void); | 34 | char BUG_wrong_field_size(void); |
31 | #define STORE_LE(field, value) \ | 35 | #define STORE_LE(field, value) \ |
32 | do { \ | 36 | do { \ |
33 | if (sizeof(field) == 4) \ | 37 | if (sizeof(field) == 4) \ |
@@ -37,9 +41,12 @@ do { \ | |||
37 | else if (sizeof(field) == 1) \ | 41 | else if (sizeof(field) == 1) \ |
38 | field = (value); \ | 42 | field = (value); \ |
39 | else \ | 43 | else \ |
40 | BUG_unsupported_field_size(); \ | 44 | BUG_wrong_field_size(); \ |
41 | } while (0) | 45 | } while (0) |
42 | 46 | ||
47 | #define FETCH_LE32(field) \ | ||
48 | (sizeof(field) == 4 ? cpu_to_le32(field) : BUG_wrong_field_size()) | ||
49 | |||
43 | // All fields are little-endian | 50 | // All fields are little-endian |
44 | struct ext2_dir { | 51 | struct ext2_dir { |
45 | uint32_t inode1; | 52 | uint32_t inode1; |
@@ -68,10 +75,10 @@ static unsigned int_log2(unsigned arg) | |||
68 | } | 75 | } |
69 | 76 | ||
70 | // taken from mkfs_minix.c. libbb candidate? | 77 | // taken from mkfs_minix.c. libbb candidate? |
71 | static unsigned div_roundup(uint32_t size, uint32_t n) | 78 | static unsigned div_roundup(uint64_t size, uint32_t n) |
72 | { | 79 | { |
73 | // Overflow-resistant | 80 | // Overflow-resistant |
74 | uint32_t res = size / n; | 81 | uint64_t res = size / n; |
75 | if (res * n != size) | 82 | if (res * n != size) |
76 | res++; | 83 | res++; |
77 | return res; | 84 | return res; |
@@ -83,8 +90,8 @@ static void allocate(uint8_t *bitmap, uint32_t blocksize, uint32_t start, uint32 | |||
83 | memset(bitmap, 0, blocksize); | 90 | memset(bitmap, 0, blocksize); |
84 | i = start / 8; | 91 | i = start / 8; |
85 | memset(bitmap, 0xFF, i); | 92 | memset(bitmap, 0xFF, i); |
86 | bitmap[i] = 0xFF >> (8-(start&7)); | 93 | bitmap[i] = 0xFF >> (8 - (start & 7)); |
87 | //bb_info_msg("ALLOC: [%u][%u][%u]: [%u]:=[%x]", blocksize, start, end, blocksize - end/8 - 1, (uint8_t)(0xFF << (8-(end&7)))); | 94 | //bb_info_msg("ALLOC: [%u][%u][%u]: [%u-%u]:=[%x],[%x]", blocksize, start, end, start/8, blocksize - end/8 - 1, 0xFF >> (8 - (start & 7)), (uint8_t)(0xFF << (8-(end&7)))); |
88 | i = end / 8; | 95 | i = end / 8; |
89 | bitmap[blocksize - i - 1] = 0xFF << (8 - (end & 7)); | 96 | bitmap[blocksize - i - 1] = 0xFF << (8 - (end & 7)); |
90 | memset(bitmap + blocksize - i, 0xFF, i); // N.B. no overflow here! | 97 | memset(bitmap + blocksize - i, 0xFF, i); // N.B. no overflow here! |
@@ -167,15 +174,15 @@ int mkfs_ext2_main(int argc UNUSED_PARAM, char **argv) | |||
167 | unsigned blocksize, blocksize_log2; | 174 | unsigned blocksize, blocksize_log2; |
168 | unsigned nreserved = 5; | 175 | unsigned nreserved = 5; |
169 | unsigned long long kilobytes; | 176 | unsigned long long kilobytes; |
170 | uint32_t nblocks; | 177 | uint32_t nblocks, nblocks_full; |
171 | uint32_t ngroups; | 178 | uint32_t ngroups; |
172 | uint32_t bytes_per_inode; | 179 | uint32_t bytes_per_inode; |
173 | uint32_t first_data_block; | 180 | uint32_t first_data_block; |
174 | uint32_t ninodes_per_group; | 181 | uint32_t inodes_per_group; |
175 | uint32_t gdtsz, itsz; | 182 | uint32_t gdtsz, itsz; |
176 | time_t timestamp; | 183 | time_t timestamp; |
177 | unsigned opts; | 184 | unsigned opts; |
178 | const char *label; | 185 | const char *label = ""; |
179 | struct stat st; | 186 | struct stat st; |
180 | struct ext2_super_block *sb; // superblock | 187 | struct ext2_super_block *sb; // superblock |
181 | struct ext2_group_desc *gd; // group descriptors | 188 | struct ext2_group_desc *gd; // group descriptors |
@@ -245,145 +252,85 @@ int mkfs_ext2_main(int argc UNUSED_PARAM, char **argv) | |||
245 | nblocks = kilobytes; | 252 | nblocks = kilobytes; |
246 | if (nblocks != kilobytes) | 253 | if (nblocks != kilobytes) |
247 | bb_error_msg_and_die("block count doesn't fit in 32 bits"); | 254 | bb_error_msg_and_die("block count doesn't fit in 32 bits"); |
255 | #define kilobytes kilobytes_unused_after_this | ||
256 | |||
257 | if (blocksize < PAGE_SIZE) | ||
258 | nblocks &= ~((PAGE_SIZE / blocksize)-1); | ||
259 | |||
260 | // N.B. killing e2fsprogs feature! Unused blocks don't account in calculations | ||
261 | nblocks_full = nblocks; | ||
262 | retry: | ||
248 | if (nblocks < 8) | 263 | if (nblocks < 8) |
249 | bb_error_msg_and_die("need >= 8 blocks"); | 264 | bb_error_msg_and_die("need >= 8 blocks"); |
250 | #define kilobytes kilobytes_unused_after_this | ||
251 | 265 | ||
252 | // number of bits in one block, i.e. 8*blocksize | 266 | // number of bits in one block, i.e. 8*blocksize |
253 | #define blocks_per_group (8 * blocksize) | 267 | #define blocks_per_group (8 * blocksize) |
254 | 268 | ||
255 | /* e2fsprogs-1.41.9 | ||
256 | overhead = 2 + fs->inode_blocks_per_group; | ||
257 | if (has_super(fs->group_desc_count - 1)) | ||
258 | overhead += 1 + fs->desc_blocks + super->s_reserved_gdt_blocks; | ||
259 | rem = (nblocks - first_data_block) % blocks_per_group); | ||
260 | if ((fs->group_desc_count == 1) && rem && (rem < overhead)) { | ||
261 | retval = EXT2_ET_TOOSMALL; | ||
262 | goto cleanup; | ||
263 | } | ||
264 | if (rem && (rem < overhead+50)) { | ||
265 | nblocks -= rem; | ||
266 | goto retry; | ||
267 | } | ||
268 | */ | ||
269 | |||
270 | // N.B. a block group can have no more than blocks_per_group blocks | 269 | // N.B. a block group can have no more than blocks_per_group blocks |
271 | first_data_block = (EXT2_MIN_BLOCK_SIZE == blocksize); | 270 | first_data_block = (EXT2_MIN_BLOCK_SIZE == blocksize); |
272 | ngroups = div_roundup(nblocks - first_data_block, blocks_per_group); | 271 | ngroups = div_roundup(nblocks - first_data_block, blocks_per_group); |
273 | if (0 == ngroups) | 272 | if (0 == ngroups) |
274 | bb_error_msg_and_die("ngroups"); | 273 | bb_error_msg_and_die("ngroups"); |
275 | 274 | ||
276 | { | 275 | gdtsz = div_roundup(ngroups, blocksize / sizeof(*gd)); |
277 | // ninodes is the total number of inodes (files) in the file system | 276 | // TODO: reserved blocks must be marked as such in the bitmaps, |
278 | uint32_t ninodes = nblocks / (bytes_per_inode >> blocksize_log2); | 277 | // or resulting filesystem is corrupt |
279 | if (ninodes < EXT2_GOOD_OLD_FIRST_INO+1) | ||
280 | ninodes = EXT2_GOOD_OLD_FIRST_INO+1; | ||
281 | ninodes_per_group = div_roundup(ninodes, ngroups); | ||
282 | // minimum number because the first EXT2_GOOD_OLD_FIRST_INO-1 are reserved | ||
283 | if (ninodes_per_group < 16) | ||
284 | ninodes_per_group = 16; | ||
285 | } | ||
286 | |||
287 | // TODO: 5?/5 WE MUST NOT DEPEND ON WHETHER DEVICE IS /dev/zero 'ed OR NOT | ||
288 | // TODO: 3/5 refuse if mounted | ||
289 | // TODO: 4/5 compat options | ||
290 | // TODO: 1/5 sanity checks | ||
291 | // TODO: 0/5 more verbose error messages | ||
292 | // TODO: 0/5 info printing | ||
293 | // TODO: 2/5 bigendianness! Spot where it comes to play! sb->, gd-> | ||
294 | // TODO: 2/5 reserved GDT: how to mark but not allocate? | ||
295 | // TODO: 3/5 dir_index? | ||
296 | |||
297 | // fill the superblock | ||
298 | sb = xzalloc(blocksize); | ||
299 | sb->s_rev_level = 1; // revision 1 filesystem | ||
300 | sb->s_magic = EXT2_SUPER_MAGIC; | ||
301 | sb->s_inode_size = sizeof(*inode); | ||
302 | sb->s_first_ino = EXT2_GOOD_OLD_FIRST_INO; | ||
303 | sb->s_log_block_size = sb->s_log_frag_size = blocksize_log2 - EXT2_MIN_BLOCK_LOG_SIZE; | ||
304 | // first 1024 bytes of the device are for boot record. If block size is 1024 bytes, then | ||
305 | // the first block available for data is 1, otherwise 0 | ||
306 | sb->s_first_data_block = first_data_block; // 0 or 1 | ||
307 | // block and inode bitmaps occupy no more than one block, so maximum number of blocks is | ||
308 | sb->s_blocks_per_group = sb->s_frags_per_group = blocks_per_group; | ||
309 | timestamp = time(NULL); | ||
310 | sb->s_mkfs_time = sb->s_wtime = sb->s_lastcheck = timestamp; | ||
311 | sb->s_state = 1; | ||
312 | sb->s_creator_os = EXT2_OS_LINUX; | ||
313 | sb->s_checkinterval = 24*60*60 * 180; // 180 days | ||
314 | sb->s_errors = EXT2_ERRORS_DEFAULT; | ||
315 | sb->s_feature_compat = EXT2_FEATURE_COMPAT_SUPP | ||
316 | | (EXT2_FEATURE_COMPAT_RESIZE_INO * ENABLE_FEATURE_MKFS_EXT2_RESERVED_GDT) | ||
317 | | (EXT2_FEATURE_COMPAT_DIR_INDEX * ENABLE_FEATURE_MKFS_EXT2_DIR_INDEX) | ||
318 | ; | ||
319 | // e2fsprogs-1.41.9 doesn't like EXT2_FEATURE_INCOMPAT_WHITEOUT | ||
320 | sb->s_feature_incompat = EXT2_FEATURE_INCOMPAT_FILETYPE;// | EXT2_FEATURE_INCOMPAT_WHITEOUT; | ||
321 | sb->s_feature_ro_compat = EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER; | ||
322 | sb->s_flags = EXT2_FLAGS_SIGNED_HASH * ENABLE_FEATURE_MKFS_EXT2_DIR_INDEX; | ||
323 | generate_uuid(sb->s_uuid); | ||
324 | if (ENABLE_FEATURE_MKFS_EXT2_DIR_INDEX) { | ||
325 | sb->s_def_hash_version = EXT2_HASH_HALF_MD4; | ||
326 | generate_uuid((uint8_t *)sb->s_hash_seed); | ||
327 | } | ||
328 | /* | ||
329 | * From e2fsprogs: add "jitter" to the superblock's check interval so that we | ||
330 | * don't check all the filesystems at the same time. We use a | ||
331 | * kludgy hack of using the UUID to derive a random jitter value. | ||
332 | */ | ||
333 | sb->s_max_mnt_count = EXT2_DFL_MAX_MNT_COUNT | ||
334 | + (sb->s_uuid[ARRAY_SIZE(sb->s_uuid)-1] % EXT2_DFL_MAX_MNT_COUNT); | ||
335 | |||
336 | sb->s_blocks_count = nblocks; | ||
337 | |||
338 | // reserve blocks for superuser | ||
339 | sb->s_r_blocks_count = ((uint64_t) nblocks * nreserved) / 100; | ||
340 | |||
341 | gdtsz = div_roundup(ngroups, EXT2_DESC_PER_BLOCK(sb)); | ||
342 | /* | ||
343 | * From e2fsprogs: Calculate the number of GDT blocks to reserve for online | ||
344 | * filesystem growth. | ||
345 | * The absolute maximum number of GDT blocks we can reserve is determined by | ||
346 | * the number of block pointers that can fit into a single block. | ||
347 | */ | ||
348 | /* We set it at 1024x the current filesystem size, or | ||
349 | * the upper block count limit (2^32), whichever is lower. | ||
350 | */ | ||
351 | if (ENABLE_FEATURE_MKFS_EXT2_RESERVED_GDT) { | 278 | if (ENABLE_FEATURE_MKFS_EXT2_RESERVED_GDT) { |
279 | /* | ||
280 | * From e2fsprogs: Calculate the number of GDT blocks to reserve for online | ||
281 | * filesystem growth. | ||
282 | * The absolute maximum number of GDT blocks we can reserve is determined by | ||
283 | * the number of block pointers that can fit into a single block. | ||
284 | * We set it at 1024x the current filesystem size, or | ||
285 | * the upper block count limit (2^32), whichever is lower. | ||
286 | */ | ||
352 | uint32_t rgdtsz = 0xFFFFFFFF; // maximum block number | 287 | uint32_t rgdtsz = 0xFFFFFFFF; // maximum block number |
353 | if (nblocks < rgdtsz / 1024) | 288 | if (nblocks < rgdtsz / 1024) |
354 | rgdtsz = nblocks * 1024; | 289 | rgdtsz = nblocks * 1024; |
355 | rgdtsz = div_roundup(rgdtsz - first_data_block, blocks_per_group); | 290 | rgdtsz = div_roundup(rgdtsz - first_data_block, blocks_per_group); |
356 | rgdtsz = div_roundup(rgdtsz, EXT2_DESC_PER_BLOCK(sb)) - gdtsz; | 291 | rgdtsz = div_roundup(rgdtsz, blocksize / sizeof(*gd)) - gdtsz; |
357 | if (rgdtsz > EXT2_ADDR_PER_BLOCK(sb)) | 292 | if (rgdtsz > blocksize / sizeof(uint32_t)) |
358 | rgdtsz = EXT2_ADDR_PER_BLOCK(sb); | 293 | rgdtsz = blocksize / sizeof(uint32_t); |
359 | STORE_LE(sb->s_reserved_gdt_blocks, rgdtsz); | 294 | //TODO: STORE_LE(sb->s_reserved_gdt_blocks, rgdtsz); |
360 | gdtsz += rgdtsz; | 295 | gdtsz += rgdtsz; |
361 | } | 296 | } |
362 | 297 | ||
363 | // N.B. a block group can have no more than 8*blocksize inodes | 298 | { |
364 | if (ninodes_per_group > blocks_per_group) | 299 | // N.B. e2fsprogs does as follows! |
365 | ninodes_per_group = blocks_per_group; | 300 | // ninodes is the total number of inodes (files) in the file system |
366 | // adjust inodes per group so they completely fill the inode table blocks in the descriptor | 301 | uint32_t ninodes = nblocks_full / (blocksize >= 4096 ? 1 : 4096 / blocksize); |
367 | ninodes_per_group = (div_roundup(ninodes_per_group * EXT2_INODE_SIZE(sb), blocksize) << blocksize_log2) / EXT2_INODE_SIZE(sb); | 302 | uint32_t overhead, remainder; |
368 | // make sure the number of inodes per group is a multiple of 8 | 303 | if (ninodes < EXT2_GOOD_OLD_FIRST_INO+1) |
369 | ninodes_per_group &= ~7; | 304 | ninodes = EXT2_GOOD_OLD_FIRST_INO+1; |
370 | sb->s_inodes_per_group = ninodes_per_group;// = div_roundup(ninodes_per_group * sb->s_inode_size, blocksize); | 305 | inodes_per_group = div_roundup(ninodes, ngroups); |
371 | // total ninodes | 306 | // minimum number because the first EXT2_GOOD_OLD_FIRST_INO-1 are reserved |
372 | sb->s_inodes_count = ninodes_per_group * ngroups; | 307 | if (inodes_per_group < 16) |
373 | 308 | inodes_per_group = 16; | |
374 | itsz = ninodes_per_group * sb->s_inode_size / blocksize; | 309 | |
375 | sb->s_free_inodes_count = sb->s_inodes_count - EXT2_GOOD_OLD_FIRST_INO; | 310 | // N.B. a block group can have no more than 8*blocksize inodes |
376 | 311 | if (inodes_per_group > blocks_per_group) | |
377 | // write the label, if any | 312 | inodes_per_group = blocks_per_group; |
378 | if (opts & OPT_L) | 313 | // adjust inodes per group so they completely fill the inode table blocks in the descriptor |
379 | safe_strncpy((char *)sb->s_volume_name, label, sizeof(sb->s_volume_name)); | 314 | inodes_per_group = (div_roundup(inodes_per_group * sizeof(*inode), blocksize) << blocksize_log2) / sizeof(*inode); |
380 | 315 | // make sure the number of inodes per group is a multiple of 8 | |
381 | #if 1 | 316 | inodes_per_group &= ~7; |
382 | /* if (fs_param.s_blocks_count != s->s_blocks_count) | 317 | itsz = div_roundup(inodes_per_group * sizeof(*inode), blocksize); |
383 | fprintf(stderr, _("warning: %u blocks unused.\n\n"), | 318 | |
384 | fs_param.s_blocks_count - s->s_blocks_count); | 319 | // the last block needs more attention: doesn't it too small for possible overhead? |
385 | */ | 320 | overhead = (has_super(ngroups - 1) ? (1/*sb*/ + gdtsz) : 0) + 1/*bbmp*/ + 1/*ibmp*/ + itsz; |
321 | remainder = (nblocks - first_data_block) % blocks_per_group; | ||
322 | if ((1 == ngroups) && remainder && (remainder < overhead)) | ||
323 | bb_error_msg_and_die("way small device"); | ||
324 | if (remainder && (remainder < overhead + 50)) { | ||
325 | //bb_info_msg("CHOP[%u]", remainder); | ||
326 | nblocks -= remainder; | ||
327 | goto retry; | ||
328 | } | ||
329 | } | ||
386 | 330 | ||
331 | // print info | ||
332 | if (nblocks_full - nblocks) | ||
333 | printf("warning: %u blocks unused\n\n", nblocks_full - nblocks); | ||
387 | printf( | 334 | printf( |
388 | "Filesystem label=%s\n" | 335 | "Filesystem label=%s\n" |
389 | "OS type: Linux\n" | 336 | "OS type: Linux\n" |
@@ -396,16 +343,17 @@ int mkfs_ext2_main(int argc UNUSED_PARAM, char **argv) | |||
396 | "%u block groups\n" | 343 | "%u block groups\n" |
397 | "%u blocks per group, %u fragments per group\n" | 344 | "%u blocks per group, %u fragments per group\n" |
398 | "%u inodes per group" | 345 | "%u inodes per group" |
399 | , (char *)sb->s_volume_name | 346 | , label |
400 | , blocksize, sb->s_log_block_size | 347 | , blocksize, blocksize_log2 - EXT2_MIN_BLOCK_LOG_SIZE |
401 | , blocksize, sb->s_log_block_size | 348 | , blocksize, blocksize_log2 - EXT2_MIN_BLOCK_LOG_SIZE |
402 | , sb->s_inodes_count, sb->s_blocks_count | 349 | , inodes_per_group * ngroups, nblocks |
403 | , sb->s_r_blocks_count, nreserved | 350 | //, div_roundup((uint64_t) nblocks * nreserved, 100), nreserved |
351 | , (unsigned)((uint64_t) nblocks_full * nreserved / 100), nreserved | ||
404 | , first_data_block | 352 | , first_data_block |
405 | , gdtsz * EXT2_DESC_PER_BLOCK(sb) * blocks_per_group | 353 | , gdtsz * (blocksize / sizeof(*gd)) * blocks_per_group |
406 | , ngroups | 354 | , ngroups |
407 | , blocks_per_group, blocks_per_group | 355 | , blocks_per_group, blocks_per_group |
408 | , ninodes_per_group | 356 | , inodes_per_group |
409 | ); | 357 | ); |
410 | { | 358 | { |
411 | const char *fmt = "\nSuperblock backups stored on blocks:\n" | 359 | const char *fmt = "\nSuperblock backups stored on blocks:\n" |
@@ -420,84 +368,149 @@ int mkfs_ext2_main(int argc UNUSED_PARAM, char **argv) | |||
420 | } | 368 | } |
421 | } | 369 | } |
422 | bb_putchar('\n'); | 370 | bb_putchar('\n'); |
423 | #endif | ||
424 | 371 | ||
372 | // dry run? -> we are done | ||
425 | if (opts & OPT_n) | 373 | if (opts & OPT_n) |
426 | goto done; | 374 | goto done; |
427 | 375 | ||
376 | // TODO: 3/5 refuse if mounted | ||
377 | // TODO: 4/5 compat options | ||
378 | // TODO: 1/5 sanity checks | ||
379 | // TODO: 0/5 more verbose error messages | ||
380 | // TODO: 4/5 bigendianness: recheck, wait for ARM reporters | ||
381 | // TODO: 2/5 reserved GDT: how to mark but not allocate? | ||
382 | // TODO: 3/5 dir_index? | ||
383 | |||
384 | // fill the superblock | ||
385 | sb = xzalloc(blocksize); | ||
386 | STORE_LE(sb->s_rev_level, 1); // revision 1 filesystem | ||
387 | STORE_LE(sb->s_magic, EXT2_SUPER_MAGIC); | ||
388 | STORE_LE(sb->s_inode_size, sizeof(*inode)); | ||
389 | STORE_LE(sb->s_first_ino, EXT2_GOOD_OLD_FIRST_INO); | ||
390 | STORE_LE(sb->s_log_block_size, blocksize_log2 - EXT2_MIN_BLOCK_LOG_SIZE); | ||
391 | STORE_LE(sb->s_log_frag_size, blocksize_log2 - EXT2_MIN_BLOCK_LOG_SIZE); | ||
392 | // first 1024 bytes of the device are for boot record. If block size is 1024 bytes, then | ||
393 | // the first block available for data is 1, otherwise 0 | ||
394 | STORE_LE(sb->s_first_data_block, first_data_block); // 0 or 1 | ||
395 | // block and inode bitmaps occupy no more than one block, so maximum number of blocks is | ||
396 | STORE_LE(sb->s_blocks_per_group, blocks_per_group); | ||
397 | STORE_LE(sb->s_frags_per_group, blocks_per_group); | ||
398 | // blocks | ||
399 | STORE_LE(sb->s_blocks_count, nblocks); | ||
400 | // reserve blocks for superuser | ||
401 | STORE_LE(sb->s_r_blocks_count, (uint32_t)((uint64_t) nblocks_full * nreserved / 100)); | ||
402 | // ninodes | ||
403 | STORE_LE(sb->s_inodes_per_group, inodes_per_group); | ||
404 | STORE_LE(sb->s_inodes_count, inodes_per_group * ngroups); | ||
405 | STORE_LE(sb->s_free_inodes_count, inodes_per_group * ngroups - EXT2_GOOD_OLD_FIRST_INO); | ||
406 | // timestamps | ||
407 | timestamp = time(NULL); | ||
408 | STORE_LE(sb->s_mkfs_time, timestamp); | ||
409 | STORE_LE(sb->s_wtime, timestamp); | ||
410 | STORE_LE(sb->s_lastcheck, timestamp); | ||
411 | // misc | ||
412 | STORE_LE(sb->s_state, 1); // TODO: what's 1? | ||
413 | STORE_LE(sb->s_creator_os, EXT2_OS_LINUX); | ||
414 | STORE_LE(sb->s_checkinterval, 24*60*60 * 180); // 180 days | ||
415 | STORE_LE(sb->s_errors, EXT2_ERRORS_DEFAULT); | ||
416 | STORE_LE(sb->s_feature_compat, EXT2_FEATURE_COMPAT_SUPP | ||
417 | | (EXT2_FEATURE_COMPAT_RESIZE_INO * ENABLE_FEATURE_MKFS_EXT2_RESERVED_GDT) | ||
418 | | (EXT2_FEATURE_COMPAT_DIR_INDEX * ENABLE_FEATURE_MKFS_EXT2_DIR_INDEX) | ||
419 | ); | ||
420 | // e2fsprogs-1.41.9 doesn't like EXT2_FEATURE_INCOMPAT_WHITEOUT | ||
421 | STORE_LE(sb->s_feature_incompat, EXT2_FEATURE_INCOMPAT_FILETYPE);// | EXT2_FEATURE_INCOMPAT_WHITEOUT; | ||
422 | STORE_LE(sb->s_feature_ro_compat, EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER); | ||
423 | STORE_LE(sb->s_flags, EXT2_FLAGS_SIGNED_HASH * ENABLE_FEATURE_MKFS_EXT2_DIR_INDEX); | ||
424 | generate_uuid(sb->s_uuid); | ||
425 | if (ENABLE_FEATURE_MKFS_EXT2_DIR_INDEX) { | ||
426 | STORE_LE(sb->s_def_hash_version, EXT2_HASH_HALF_MD4); | ||
427 | generate_uuid((uint8_t *)sb->s_hash_seed); | ||
428 | } | ||
429 | /* | ||
430 | * From e2fsprogs: add "jitter" to the superblock's check interval so that we | ||
431 | * don't check all the filesystems at the same time. We use a | ||
432 | * kludgy hack of using the UUID to derive a random jitter value. | ||
433 | */ | ||
434 | STORE_LE(sb->s_max_mnt_count, EXT2_DFL_MAX_MNT_COUNT | ||
435 | + (sb->s_uuid[ARRAY_SIZE(sb->s_uuid)-1] % EXT2_DFL_MAX_MNT_COUNT)); | ||
436 | |||
437 | // write the label, if any | ||
438 | if (label) //opts & OPT_L) | ||
439 | safe_strncpy((char *)sb->s_volume_name, label, sizeof(sb->s_volume_name)); | ||
440 | |||
428 | // fill group descriptors | 441 | // fill group descriptors |
429 | gd = xzalloc(gdtsz * blocksize); | 442 | gd = xzalloc(gdtsz * blocksize); |
443 | buf = xmalloc(blocksize); | ||
430 | sb->s_free_blocks_count = 0; | 444 | sb->s_free_blocks_count = 0; |
431 | for (i = 0, pos = first_data_block, n = nblocks; | 445 | for (i = 0, pos = first_data_block, n = nblocks - first_data_block; |
432 | i < ngroups; | 446 | i < ngroups; |
433 | i++, pos += blocks_per_group, n -= blocks_per_group | 447 | i++, pos += blocks_per_group, n -= blocks_per_group |
434 | ) { | 448 | ) { |
435 | uint32_t overhead = pos + (has_super(i) ? (1/*sb*/ + gdtsz) : 0); | 449 | uint32_t overhead = pos + (has_super(i) ? (1/*sb*/ + gdtsz) : 0); |
436 | gd[i].bg_block_bitmap = overhead + 0; | 450 | uint32_t fb; |
437 | gd[i].bg_inode_bitmap = overhead + 1; | 451 | STORE_LE(gd[i].bg_block_bitmap, overhead + 0); |
438 | gd[i].bg_inode_table = overhead + 2; | 452 | STORE_LE(gd[i].bg_inode_bitmap, overhead + 1); |
453 | STORE_LE(gd[i].bg_inode_table, overhead + 2); | ||
439 | overhead = overhead - pos + 1/*bbmp*/ + 1/*ibmp*/ + itsz; | 454 | overhead = overhead - pos + 1/*bbmp*/ + 1/*ibmp*/ + itsz; |
440 | gd[i].bg_free_inodes_count = ninodes_per_group; | 455 | gd[i].bg_free_inodes_count = inodes_per_group; |
441 | //gd[i].bg_used_dirs_count = 0; | 456 | //STORE_LE(gd[i].bg_used_dirs_count, 0); |
442 | // N.B. both root and lost+found dirs are within the first block group, thus +2 | 457 | // N.B. both root and lost+found dirs are within the first block group, thus +2 |
443 | if (0 == i) { | 458 | if (0 == i) { |
444 | overhead += 2; | 459 | overhead += 2; |
445 | gd[i].bg_used_dirs_count = 2; | 460 | STORE_LE(gd[i].bg_used_dirs_count, 2); |
446 | gd[i].bg_free_inodes_count -= EXT2_GOOD_OLD_FIRST_INO; | 461 | gd[i].bg_free_inodes_count -= EXT2_GOOD_OLD_FIRST_INO; |
447 | } | 462 | } |
448 | // N.B. the following is pure heuristics! | 463 | // // N.B. the following is pure heuristics! |
449 | // Likely to cope with 1024-byte blocks, when first block is for boot sectors | 464 | // // Likely to cope with 1024-byte blocks, when first block is for boot sectors |
450 | if (ngroups-1 == i) { | 465 | // if (ngroups-1 == i) { |
451 | overhead += first_data_block; | 466 | // n -= first_data_block; |
452 | } | 467 | // } |
453 | gd[i].bg_free_blocks_count = (n < blocks_per_group ? n : blocks_per_group) - overhead; | 468 | |
454 | sb->s_free_blocks_count += gd[i].bg_free_blocks_count; | 469 | // mark preallocated blocks as allocated |
470 | fb = (n < blocks_per_group ? n : blocks_per_group) - overhead; | ||
471 | //bb_info_msg("ALLOC: [%u][%u][%u]", blocksize, overhead, blocks_per_group - (fb + overhead)); | ||
472 | allocate(buf, blocksize, | ||
473 | overhead, | ||
474 | blocks_per_group - (fb + overhead) | ||
475 | ); | ||
476 | // dump block bitmap | ||
477 | PUT((uint64_t)(FETCH_LE32(gd[i].bg_block_bitmap)) * blocksize, buf, blocksize); | ||
478 | STORE_LE(gd[i].bg_free_blocks_count, fb); | ||
479 | |||
480 | // mark preallocated inodes as allocated | ||
481 | allocate(buf, blocksize, | ||
482 | inodes_per_group - gd[i].bg_free_inodes_count, | ||
483 | blocks_per_group - inodes_per_group | ||
484 | ); | ||
485 | // dump inode bitmap | ||
486 | //PUT((uint64_t)(FETCH_LE32(gd[i].bg_block_bitmap)) * blocksize, buf, blocksize); | ||
487 | //but it's right after block bitmap, so we can just: | ||
488 | xwrite(fd, buf, blocksize); | ||
489 | STORE_LE(gd[i].bg_free_inodes_count, gd[i].bg_free_inodes_count); | ||
490 | |||
491 | // count overall free blocks | ||
492 | sb->s_free_blocks_count += fb; | ||
455 | } | 493 | } |
456 | STORE_LE(sb->s_free_blocks_count, sb->s_free_blocks_count); | 494 | STORE_LE(sb->s_free_blocks_count, sb->s_free_blocks_count); |
457 | 495 | ||
458 | // dump filesystem skeleton structures | 496 | // dump filesystem skeleton structures |
459 | // printf("Writing superblocks and filesystem accounting information: "); | 497 | // printf("Writing superblocks and filesystem accounting information: "); |
460 | buf = xmalloc(blocksize); | ||
461 | for (i = 0, pos = first_data_block; i < ngroups; i++, pos += blocks_per_group) { | 498 | for (i = 0, pos = first_data_block; i < ngroups; i++, pos += blocks_per_group) { |
462 | uint32_t overhead = has_super(i) ? (1/*sb*/ + gdtsz) : 0; | ||
463 | uint32_t start; | ||
464 | uint32_t end; | ||
465 | |||
466 | // dump superblock and group descriptors and their backups | 499 | // dump superblock and group descriptors and their backups |
467 | if (overhead) { // N.B. in fact, we want (has_super(i)) condition, but it is equal to (overhead != 0) and is cheaper | 500 | if (has_super(i)) { |
468 | // N.B. 1024 byte blocks are special | 501 | // N.B. 1024 byte blocks are special |
469 | PUT(((uint64_t)pos << blocksize_log2) + ((0 == i && 0 == first_data_block) ? 1024 : 0), sb, 1024);//blocksize); | 502 | PUT(((uint64_t)pos << blocksize_log2) + ((0 == i && 0 == first_data_block) ? 1024 : 0), sb, 1024);//blocksize); |
470 | PUT(((uint64_t)pos << blocksize_log2) + blocksize, gd, gdtsz * blocksize); | 503 | PUT(((uint64_t)pos << blocksize_log2) + blocksize, gd, gdtsz * blocksize); |
471 | } | 504 | } |
472 | |||
473 | start = overhead + 1/*bbmp*/ + 1/*ibmp*/ + itsz; | ||
474 | if (i == 0) | ||
475 | start += 2; // for "/" and "/lost+found" | ||
476 | end = blocks_per_group - (start + gd[i].bg_free_blocks_count); | ||
477 | |||
478 | // mark preallocated blocks as allocated | ||
479 | allocate(buf, blocksize, start, end); | ||
480 | // dump block bitmap | ||
481 | PUT((uint64_t)(pos + overhead) * blocksize, buf, blocksize); | ||
482 | |||
483 | // mark preallocated inodes as allocated | ||
484 | allocate(buf, blocksize, | ||
485 | ninodes_per_group - gd[i].bg_free_inodes_count, | ||
486 | blocks_per_group - ninodes_per_group | ||
487 | ); | ||
488 | // dump inode bitmap | ||
489 | //PUT((uint64_t)(pos + overhead + 1) * blocksize, buf, blocksize); | ||
490 | //but it's right after block bitmap, so we can just: | ||
491 | xwrite(fd, buf, blocksize); | ||
492 | } | 505 | } |
493 | 506 | ||
494 | // zero boot sectors | 507 | // zero boot sectors |
495 | memset(buf, 0, blocksize); | 508 | memset(buf, 0, blocksize); |
496 | PUT(0, buf, 1024); // N.B. 1024 <= blocksize | 509 | PUT(0, buf, 1024); // N.B. 1024 <= blocksize, so buf[0..1023] contains zeros |
497 | // zero inode tables | 510 | // zero inode tables |
498 | for (i = 0; i < ngroups; ++i) | 511 | for (i = 0; i < ngroups; ++i) |
499 | for (n = 0; n < itsz; ++n) | 512 | for (n = 0; n < itsz; ++n) |
500 | PUT((uint64_t)(gd[i].bg_inode_table + n) * blocksize, buf, blocksize); | 513 | PUT((uint64_t)(FETCH_LE32(gd[i].bg_inode_table) + n) * blocksize, buf, blocksize); |
501 | 514 | ||
502 | // prepare directory inode | 515 | // prepare directory inode |
503 | inode = (struct ext2_inode *)buf; | 516 | inode = (struct ext2_inode *)buf; |
@@ -511,13 +524,13 @@ int mkfs_ext2_main(int argc UNUSED_PARAM, char **argv) | |||
511 | 524 | ||
512 | // dump root dir inode | 525 | // dump root dir inode |
513 | STORE_LE(inode->i_links_count, 3); // "/.", "/..", "/lost+found/.." point to this inode | 526 | STORE_LE(inode->i_links_count, 3); // "/.", "/..", "/lost+found/.." point to this inode |
514 | STORE_LE(inode->i_block[0], gd[0].bg_inode_table + itsz); | 527 | STORE_LE(inode->i_block[0], FETCH_LE32(gd[0].bg_inode_table) + itsz); |
515 | PUT(((uint64_t)gd[0].bg_inode_table << blocksize_log2) + (EXT2_ROOT_INO-1) * sizeof(*inode), buf, sizeof(*inode)); | 528 | PUT(((uint64_t)FETCH_LE32(gd[0].bg_inode_table) << blocksize_log2) + (EXT2_ROOT_INO-1) * sizeof(*inode), buf, sizeof(*inode)); |
516 | 529 | ||
517 | // dump lost+found dir inode | 530 | // dump lost+found dir inode |
518 | STORE_LE(inode->i_links_count, 2); // both "/lost+found" and "/lost+found/." point to this inode | 531 | STORE_LE(inode->i_links_count, 2); // both "/lost+found" and "/lost+found/." point to this inode |
519 | STORE_LE(inode->i_block[0], inode->i_block[0]+1); // use next block //= gd[0].bg_inode_table + itsz + 1; | 532 | STORE_LE(inode->i_block[0], inode->i_block[0]+1); // use next block |
520 | PUT(((uint64_t)gd[0].bg_inode_table << blocksize_log2) + (EXT2_GOOD_OLD_FIRST_INO-1) * sizeof(*inode), buf, sizeof(*inode)); | 533 | PUT(((uint64_t)FETCH_LE32(gd[0].bg_inode_table) << blocksize_log2) + (EXT2_GOOD_OLD_FIRST_INO-1) * sizeof(*inode), buf, sizeof(*inode)); |
521 | 534 | ||
522 | // dump directories | 535 | // dump directories |
523 | memset(buf, 0, blocksize); | 536 | memset(buf, 0, blocksize); |
@@ -534,7 +547,7 @@ int mkfs_ext2_main(int argc UNUSED_PARAM, char **argv) | |||
534 | STORE_LE(dir->name_len2, 2); | 547 | STORE_LE(dir->name_len2, 2); |
535 | STORE_LE(dir->file_type2, EXT2_FT_DIR); | 548 | STORE_LE(dir->file_type2, EXT2_FT_DIR); |
536 | dir->name2[0] = '.'; dir->name2[1] = '.'; | 549 | dir->name2[0] = '.'; dir->name2[1] = '.'; |
537 | PUT((uint64_t)(gd[0].bg_inode_table + itsz + 1) * blocksize, buf, blocksize); | 550 | PUT((uint64_t)(FETCH_LE32(gd[0].bg_inode_table) + itsz + 1) * blocksize, buf, blocksize); |
538 | 551 | ||
539 | // dump root dir block | 552 | // dump root dir block |
540 | STORE_LE(dir->inode1, EXT2_ROOT_INO); | 553 | STORE_LE(dir->inode1, EXT2_ROOT_INO); |
@@ -544,13 +557,7 @@ int mkfs_ext2_main(int argc UNUSED_PARAM, char **argv) | |||
544 | STORE_LE(dir->name_len3, 10); | 557 | STORE_LE(dir->name_len3, 10); |
545 | STORE_LE(dir->file_type3, EXT2_FT_DIR); | 558 | STORE_LE(dir->file_type3, EXT2_FT_DIR); |
546 | strcpy(dir->name3, "lost+found"); | 559 | strcpy(dir->name3, "lost+found"); |
547 | PUT((uint64_t)(gd[0].bg_inode_table + itsz + 0) * blocksize, buf, blocksize); | 560 | PUT((uint64_t)(FETCH_LE32(gd[0].bg_inode_table) + itsz + 0) * blocksize, buf, blocksize); |
548 | |||
549 | // bb_info_msg("done\n" | ||
550 | // "This filesystem will be automatically checked every %u mounts or\n" | ||
551 | // "180 days, whichever comes first. Use tune2fs -c or -i to override.", | ||
552 | // sb->s_max_mnt_count | ||
553 | // ); | ||
554 | 561 | ||
555 | done: | 562 | done: |
556 | // cleanup | 563 | // cleanup |