1
2
3
4
5
6
7
8
9
10#include "libbb.h"
11#include <linux/fs.h>
12#include <linux/ext2_fs.h>
13
14#define ENABLE_FEATURE_MKFS_EXT2_RESERVED_GDT 0
15#define ENABLE_FEATURE_MKFS_EXT2_DIR_INDEX 1
16
17
18#define s_reserved_gdt_blocks s_padding1
19#define s_mkfs_time s_reserved[0]
20#define s_flags s_reserved[22]
21
22#define EXT2_HASH_HALF_MD4 1
23#define EXT2_FLAGS_SIGNED_HASH 0x0001
24#define EXT2_FLAGS_UNSIGNED_HASH 0x0002
25
26
27char BUG_wrong_field_size(void);
28#define STORE_LE(field, value) \
29do { \
30 if (sizeof(field) == 4) \
31 field = SWAP_LE32(value); \
32 else if (sizeof(field) == 2) \
33 field = SWAP_LE16(value); \
34 else if (sizeof(field) == 1) \
35 field = (value); \
36 else \
37 BUG_wrong_field_size(); \
38} while (0)
39
40#define FETCH_LE32(field) \
41 (sizeof(field) == 4 ? SWAP_LE32(field) : BUG_wrong_field_size())
42
43
44struct ext2_dir {
45 uint32_t inode1;
46 uint16_t rec_len1;
47 uint8_t name_len1;
48 uint8_t file_type1;
49 char name1[4];
50 uint32_t inode2;
51 uint16_t rec_len2;
52 uint8_t name_len2;
53 uint8_t file_type2;
54 char name2[4];
55 uint32_t inode3;
56 uint16_t rec_len3;
57 uint8_t name_len3;
58 uint8_t file_type3;
59 char name3[12];
60};
61
62static unsigned int_log2(unsigned arg)
63{
64 unsigned r = 0;
65 while ((arg >>= 1) != 0)
66 r++;
67 return r;
68}
69
70
71
72static uint32_t div_roundup(uint32_t size, uint32_t n)
73{
74
75 uint32_t res = size / n;
76 if (res * n != size)
77 res++;
78 return res;
79}
80
81static void allocate(uint8_t *bitmap, uint32_t blocksize, uint32_t start, uint32_t end)
82{
83 uint32_t i;
84
85
86 memset(bitmap, 0, blocksize);
87 i = start / 8;
88 memset(bitmap, 0xFF, i);
89 bitmap[i] = (1 << (start & 7)) - 1;
90 i = end / 8;
91 bitmap[blocksize - i - 1] |= 0x7F00 >> (end & 7);
92 memset(bitmap + blocksize - i, 0xFF, i);
93}
94
95static uint32_t has_super(uint32_t x)
96{
97
98 static const uint32_t supers[] = {
99 0, 1, 3, 5, 7, 9, 25, 27, 49, 81, 125, 243, 343, 625, 729,
100 2187, 2401, 3125, 6561, 15625, 16807, 19683, 59049, 78125,
101 117649, 177147, 390625, 531441, 823543, 1594323, 1953125,
102 4782969, 5764801, 9765625, 14348907, 40353607, 43046721,
103 48828125, 129140163, 244140625, 282475249, 387420489,
104 1162261467, 1220703125, 1977326743, 3486784401,
105 };
106 const uint32_t *sp = supers + ARRAY_SIZE(supers);
107 while (1) {
108 sp--;
109 if (x == *sp)
110 return 1;
111 if (x > *sp)
112 return 0;
113 }
114}
115
116#define fd 3
117
118static void PUT(uint64_t off, void *buf, uint32_t size)
119{
120
121 xlseek(fd, off, SEEK_SET);
122 xwrite(fd, buf, size);
123}
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155enum {
156 OPT_c = 1 << 0,
157 OPT_l = 1 << 1,
158 OPT_b = 1 << 2,
159 OPT_f = 1 << 3,
160 OPT_i = 1 << 4,
161 OPT_I = 1 << 5,
162 OPT_J = 1 << 6,
163 OPT_G = 1 << 7,
164 OPT_N = 1 << 8,
165 OPT_m = 1 << 9,
166 OPT_o = 1 << 10,
167 OPT_g = 1 << 11,
168 OPT_L = 1 << 12,
169 OPT_M = 1 << 13,
170 OPT_O = 1 << 14,
171 OPT_r = 1 << 15,
172 OPT_E = 1 << 16,
173 OPT_T = 1 << 17,
174 OPT_U = 1 << 18,
175 OPT_j = 1 << 19,
176 OPT_n = 1 << 20,
177 OPT_q = 1 << 21,
178 OPT_v = 1 << 22,
179 OPT_F = 1 << 23,
180 OPT_S = 1 << 24,
181
182};
183
184int mkfs_ext2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
185int mkfs_ext2_main(int argc UNUSED_PARAM, char **argv)
186{
187 unsigned i, pos, n;
188 unsigned bs, bpi;
189 unsigned blocksize, blocksize_log2;
190 unsigned inodesize, user_inodesize;
191 unsigned reserved_percent = 5;
192 unsigned long long kilobytes;
193 uint32_t nblocks, nblocks_full;
194 uint32_t nreserved;
195 uint32_t ngroups;
196 uint32_t bytes_per_inode;
197 uint32_t first_block;
198 uint32_t inodes_per_group;
199 uint32_t group_desc_blocks;
200 uint32_t inode_table_blocks;
201 uint32_t lost_and_found_blocks;
202 time_t timestamp;
203 const char *label = "";
204 struct stat st;
205 struct ext2_super_block *sb;
206 struct ext2_group_desc *gd;
207 struct ext2_inode *inode;
208 struct ext2_dir *dir;
209 uint8_t *buf;
210
211
212
213 opt_complementary = "-1:b+:i+:I+:m+";
214 getopt32(argv, "cl:b:f:i:I:J:G:N:m:o:g:L:M:O:r:E:T:U:jnqvFS",
215 NULL, &bs, NULL, &bpi,
216 &user_inodesize, NULL, NULL, NULL,
217 &reserved_percent, NULL, NULL, &label,
218 NULL, NULL, NULL, NULL,
219 NULL, NULL);
220 argv += optind;
221
222
223 xmove_fd(xopen(argv[0], O_WRONLY), fd);
224 xfstat(fd, &st, argv[0]);
225 if (!S_ISBLK(st.st_mode) && !(option_mask32 & OPT_F))
226 bb_error_msg_and_die("%s: not a block device", argv[0]);
227
228
229
230
231 if (find_mount_point(argv[0], 0))
232 bb_error_msg_and_die("can't format mounted filesystem");
233
234
235 kilobytes = get_volume_size_in_bytes(fd, argv[1], 1024, !(option_mask32 & OPT_n)) / 1024;
236
237 bytes_per_inode = 16384;
238 if (kilobytes < 512*1024)
239 bytes_per_inode = 4096;
240 if (kilobytes < 3*1024)
241 bytes_per_inode = 8192;
242 if (option_mask32 & OPT_i)
243 bytes_per_inode = bpi;
244
245
246
247
248 blocksize = 1024;
249 inodesize = sizeof(struct ext2_inode);
250 if (kilobytes >= 512*1024) {
251 blocksize = 4096;
252 inodesize = 256;
253 }
254 if (EXT2_MAX_BLOCK_SIZE > 4096) {
255
256
257
258 while ((kilobytes >> 22) >= blocksize)
259 blocksize *= 2;
260 }
261 if (option_mask32 & OPT_b)
262 blocksize = bs;
263 if (blocksize < EXT2_MIN_BLOCK_SIZE
264 || blocksize > EXT2_MAX_BLOCK_SIZE
265 || (blocksize & (blocksize - 1))
266 ) {
267 bb_error_msg_and_die("blocksize %u is bad", blocksize);
268 }
269
270 if (option_mask32 & OPT_I) {
271 if (user_inodesize < sizeof(*inode)
272 || user_inodesize > blocksize
273 || (user_inodesize & (user_inodesize - 1))
274 ) {
275 bb_error_msg("-%c is bad", 'I');
276 } else {
277 inodesize = user_inodesize;
278 }
279 }
280
281 if ((int32_t)bytes_per_inode < blocksize)
282 bb_error_msg_and_die("-%c is bad", 'i');
283
284#define blocks_per_group (8 * blocksize)
285 first_block = (EXT2_MIN_BLOCK_SIZE == blocksize);
286 blocksize_log2 = int_log2(blocksize);
287
288
289 kilobytes >>= (blocksize_log2 - EXT2_MIN_BLOCK_LOG_SIZE);
290 nblocks = kilobytes;
291 if (nblocks != kilobytes)
292 bb_error_msg_and_die("block count doesn't fit in 32 bits");
293#define kilobytes kilobytes_unused_after_this
294
295 if (nblocks < 60)
296 bb_error_msg_and_die("need >= 60 blocks");
297
298
299 if (reserved_percent > 50)
300 bb_error_msg_and_die("-%c is bad", 'm');
301 nreserved = (uint64_t)nblocks * reserved_percent / 100;
302
303
304 nblocks_full = nblocks;
305
306
307
308
309
310 retry:
311
312 ngroups = div_roundup(nblocks - first_block, blocks_per_group);
313
314 group_desc_blocks = div_roundup(ngroups, blocksize / sizeof(*gd));
315
316
317 if (ENABLE_FEATURE_MKFS_EXT2_RESERVED_GDT) {
318
319
320
321
322
323
324
325
326 uint32_t reserved_group_desc_blocks = 0xFFFFFFFF;
327 if (nblocks < reserved_group_desc_blocks / 1024)
328 reserved_group_desc_blocks = nblocks * 1024;
329 reserved_group_desc_blocks = div_roundup(reserved_group_desc_blocks - first_block, blocks_per_group);
330 reserved_group_desc_blocks = div_roundup(reserved_group_desc_blocks, blocksize / sizeof(*gd)) - group_desc_blocks;
331 if (reserved_group_desc_blocks > blocksize / sizeof(uint32_t))
332 reserved_group_desc_blocks = blocksize / sizeof(uint32_t);
333
334 group_desc_blocks += reserved_group_desc_blocks;
335 }
336
337 {
338
339 uint32_t overhead, remainder;
340
341 uint32_t ninodes = ((uint64_t) nblocks_full * blocksize) / bytes_per_inode;
342 if (ninodes < EXT2_GOOD_OLD_FIRST_INO+1)
343 ninodes = EXT2_GOOD_OLD_FIRST_INO+1;
344 inodes_per_group = div_roundup(ninodes, ngroups);
345
346 if (inodes_per_group < 16)
347 inodes_per_group = 16;
348
349 if (inodes_per_group > blocks_per_group)
350 inodes_per_group = blocks_per_group;
351
352 inodes_per_group = (div_roundup(inodes_per_group * inodesize, blocksize) * blocksize) / inodesize;
353
354 inodes_per_group &= ~7;
355 inode_table_blocks = div_roundup(inodes_per_group * inodesize, blocksize);
356
357
358
359
360
361
362 lost_and_found_blocks = MIN(EXT2_NDIR_BLOCKS, 16 >> (blocksize_log2 - EXT2_MIN_BLOCK_LOG_SIZE));
363
364
365 overhead = (has_super(ngroups - 1) ? (1 + group_desc_blocks) : 0) + 1 + 1 + inode_table_blocks;
366 remainder = (nblocks - first_block) % blocks_per_group;
367
368
369
370
371
372
373
374
375
376
377
378
379
380 if (remainder && (remainder < overhead + 50)) {
381
382 nblocks -= remainder;
383 goto retry;
384 }
385 }
386
387 if (nblocks_full - nblocks)
388 printf("warning: %u blocks unused\n\n", nblocks_full - nblocks);
389 printf(
390 "Filesystem label=%s\n"
391 "OS type: Linux\n"
392 "Block size=%u (log=%u)\n"
393 "Fragment size=%u (log=%u)\n"
394 "%u inodes, %u blocks\n"
395 "%u blocks (%u%%) reserved for the super user\n"
396 "First data block=%u\n"
397 "Maximum filesystem blocks=%u\n"
398 "%u block groups\n"
399 "%u blocks per group, %u fragments per group\n"
400 "%u inodes per group"
401 , label
402 , blocksize, blocksize_log2 - EXT2_MIN_BLOCK_LOG_SIZE
403 , blocksize, blocksize_log2 - EXT2_MIN_BLOCK_LOG_SIZE
404 , inodes_per_group * ngroups, nblocks
405 , nreserved, reserved_percent
406 , first_block
407 , group_desc_blocks * (blocksize / (unsigned)sizeof(*gd)) * blocks_per_group
408 , ngroups
409 , blocks_per_group, blocks_per_group
410 , inodes_per_group
411 );
412 {
413 const char *fmt = "\nSuperblock backups stored on blocks:\n"
414 "\t%u";
415 pos = first_block;
416 for (i = 1; i < ngroups; i++) {
417 pos += blocks_per_group;
418 if (has_super(i)) {
419 printf(fmt, (unsigned)pos);
420 fmt = ", %u";
421 }
422 }
423 }
424 bb_putchar('\n');
425
426 if (option_mask32 & OPT_n) {
427 if (ENABLE_FEATURE_CLEAN_UP)
428 close(fd);
429 return EXIT_SUCCESS;
430 }
431
432
433
434
435
436
437
438
439
440
441 sb = xzalloc(1024);
442 STORE_LE(sb->s_rev_level, EXT2_DYNAMIC_REV);
443 STORE_LE(sb->s_magic, EXT2_SUPER_MAGIC);
444 STORE_LE(sb->s_inode_size, inodesize);
445
446 if (inodesize != sizeof(*inode))
447 STORE_LE(sb->s_reserved[21], 0x001C001C);
448 STORE_LE(sb->s_first_ino, EXT2_GOOD_OLD_FIRST_INO);
449 STORE_LE(sb->s_log_block_size, blocksize_log2 - EXT2_MIN_BLOCK_LOG_SIZE);
450 STORE_LE(sb->s_log_frag_size, blocksize_log2 - EXT2_MIN_BLOCK_LOG_SIZE);
451
452
453 STORE_LE(sb->s_first_data_block, first_block);
454
455 STORE_LE(sb->s_blocks_per_group, blocks_per_group);
456 STORE_LE(sb->s_frags_per_group, blocks_per_group);
457
458 STORE_LE(sb->s_blocks_count, nblocks);
459
460 STORE_LE(sb->s_r_blocks_count, nreserved);
461
462 STORE_LE(sb->s_inodes_per_group, inodes_per_group);
463 STORE_LE(sb->s_inodes_count, inodes_per_group * ngroups);
464 STORE_LE(sb->s_free_inodes_count, inodes_per_group * ngroups - EXT2_GOOD_OLD_FIRST_INO);
465
466 timestamp = time(NULL);
467 STORE_LE(sb->s_mkfs_time, timestamp);
468 STORE_LE(sb->s_wtime, timestamp);
469 STORE_LE(sb->s_lastcheck, timestamp);
470
471 STORE_LE(sb->s_state, 1);
472 STORE_LE(sb->s_creator_os, EXT2_OS_LINUX);
473 STORE_LE(sb->s_checkinterval, 24*60*60 * 180);
474 STORE_LE(sb->s_errors, EXT2_ERRORS_DEFAULT);
475
476
477
478
479 STORE_LE(sb->s_feature_compat, EXT2_FEATURE_COMPAT_SUPP
480 | (EXT2_FEATURE_COMPAT_RESIZE_INO * ENABLE_FEATURE_MKFS_EXT2_RESERVED_GDT)
481 | (EXT2_FEATURE_COMPAT_DIR_INDEX * ENABLE_FEATURE_MKFS_EXT2_DIR_INDEX)
482 );
483 STORE_LE(sb->s_feature_incompat, EXT2_FEATURE_INCOMPAT_FILETYPE);
484 STORE_LE(sb->s_feature_ro_compat, EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER);
485 STORE_LE(sb->s_flags, EXT2_FLAGS_UNSIGNED_HASH * ENABLE_FEATURE_MKFS_EXT2_DIR_INDEX);
486 generate_uuid(sb->s_uuid);
487 if (ENABLE_FEATURE_MKFS_EXT2_DIR_INDEX) {
488 STORE_LE(sb->s_def_hash_version, EXT2_HASH_HALF_MD4);
489 generate_uuid((uint8_t *)sb->s_hash_seed);
490 }
491
492
493
494
495
496 STORE_LE(sb->s_max_mnt_count,
497 EXT2_DFL_MAX_MNT_COUNT
498 + (sb->s_uuid[ARRAY_SIZE(sb->s_uuid)-1] % EXT2_DFL_MAX_MNT_COUNT));
499
500
501 safe_strncpy((char *)sb->s_volume_name, label, sizeof(sb->s_volume_name));
502
503
504 gd = xzalloc(group_desc_blocks * blocksize);
505 buf = xmalloc(blocksize);
506 sb->s_free_blocks_count = 0;
507 for (i = 0, pos = first_block, n = nblocks - first_block;
508 i < ngroups;
509 i++, pos += blocks_per_group, n -= blocks_per_group
510 ) {
511 uint32_t overhead = pos + (has_super(i) ? (1 + group_desc_blocks) : 0);
512 uint32_t free_blocks;
513
514 STORE_LE(gd[i].bg_block_bitmap, overhead + 0);
515 STORE_LE(gd[i].bg_inode_bitmap, overhead + 1);
516 STORE_LE(gd[i].bg_inode_table, overhead + 2);
517 overhead = overhead - pos + 1 + 1 + inode_table_blocks;
518 gd[i].bg_free_inodes_count = inodes_per_group;
519
520
521
522 if (0 == i) {
523
524 overhead += 1 + lost_and_found_blocks;
525
526 STORE_LE(gd[i].bg_used_dirs_count, 2);
527
528 gd[i].bg_free_inodes_count -= EXT2_GOOD_OLD_FIRST_INO;
529 }
530
531
532 free_blocks = (n < blocks_per_group ? n : blocks_per_group) - overhead;
533
534
535
536 allocate(buf, blocksize,
537
538 overhead,
539
540 blocks_per_group - (free_blocks + overhead)
541 );
542
543 PUT((uint64_t)(FETCH_LE32(gd[i].bg_block_bitmap)) * blocksize, buf, blocksize);
544 STORE_LE(gd[i].bg_free_blocks_count, free_blocks);
545
546
547 allocate(buf, blocksize,
548
549 inodes_per_group - gd[i].bg_free_inodes_count,
550
551 blocks_per_group - inodes_per_group
552 );
553
554
555
556 xwrite(fd, buf, blocksize);
557 STORE_LE(gd[i].bg_free_inodes_count, gd[i].bg_free_inodes_count);
558
559
560 sb->s_free_blocks_count += free_blocks;
561 }
562 STORE_LE(sb->s_free_blocks_count, sb->s_free_blocks_count);
563
564
565
566 for (i = 0, pos = first_block; i < ngroups; i++, pos += blocks_per_group) {
567
568 if (has_super(i)) {
569
570 PUT(((uint64_t)pos * blocksize) + ((0 == i && 1024 != blocksize) ? 1024 : 0),
571 sb, 1024);
572 PUT(((uint64_t)pos * blocksize) + blocksize,
573 gd, group_desc_blocks * blocksize);
574 }
575 }
576
577
578 memset(buf, 0, blocksize);
579 PUT(0, buf, 1024);
580
581 for (i = 0; i < ngroups; ++i)
582 for (n = 0; n < inode_table_blocks; ++n)
583 PUT((uint64_t)(FETCH_LE32(gd[i].bg_inode_table) + n) * blocksize,
584 buf, blocksize);
585
586
587 inode = (struct ext2_inode *)buf;
588 STORE_LE(inode->i_mode, S_IFDIR | S_IRWXU | S_IRGRP | S_IROTH | S_IXGRP | S_IXOTH);
589 STORE_LE(inode->i_mtime, timestamp);
590 STORE_LE(inode->i_atime, timestamp);
591 STORE_LE(inode->i_ctime, timestamp);
592 STORE_LE(inode->i_size, blocksize);
593
594
595 STORE_LE(inode->i_blocks, blocksize / 512);
596
597
598 STORE_LE(inode->i_links_count, 3);
599 STORE_LE(inode->i_block[0], FETCH_LE32(gd[0].bg_inode_table) + inode_table_blocks);
600 PUT(((uint64_t)FETCH_LE32(gd[0].bg_inode_table) * blocksize) + (EXT2_ROOT_INO-1) * inodesize,
601 buf, inodesize);
602
603
604 STORE_LE(inode->i_links_count, 2);
605 STORE_LE(inode->i_size, lost_and_found_blocks * blocksize);
606 STORE_LE(inode->i_blocks, (lost_and_found_blocks * blocksize) / 512);
607 n = FETCH_LE32(inode->i_block[0]) + 1;
608 for (i = 0; i < lost_and_found_blocks; ++i)
609 STORE_LE(inode->i_block[i], i + n);
610
611 PUT(((uint64_t)FETCH_LE32(gd[0].bg_inode_table) * blocksize) + (EXT2_GOOD_OLD_FIRST_INO-1) * inodesize,
612 buf, inodesize);
613
614
615 memset(buf, 0, blocksize);
616 dir = (struct ext2_dir *)buf;
617
618
619 STORE_LE(dir->rec_len1, blocksize);
620 for (i = 1; i < lost_and_found_blocks; ++i)
621 PUT((uint64_t)(FETCH_LE32(gd[0].bg_inode_table) + inode_table_blocks + 1+i) * blocksize,
622 buf, blocksize);
623
624
625 STORE_LE(dir->inode1, EXT2_GOOD_OLD_FIRST_INO);
626 STORE_LE(dir->rec_len1, 12);
627 STORE_LE(dir->name_len1, 1);
628 STORE_LE(dir->file_type1, EXT2_FT_DIR);
629 dir->name1[0] = '.';
630 STORE_LE(dir->inode2, EXT2_ROOT_INO);
631 STORE_LE(dir->rec_len2, blocksize - 12);
632 STORE_LE(dir->name_len2, 2);
633 STORE_LE(dir->file_type2, EXT2_FT_DIR);
634 dir->name2[0] = '.'; dir->name2[1] = '.';
635 PUT((uint64_t)(FETCH_LE32(gd[0].bg_inode_table) + inode_table_blocks + 1) * blocksize, buf, blocksize);
636
637
638 STORE_LE(dir->inode1, EXT2_ROOT_INO);
639 STORE_LE(dir->rec_len2, 12);
640 STORE_LE(dir->inode3, EXT2_GOOD_OLD_FIRST_INO);
641 STORE_LE(dir->rec_len3, blocksize - 12 - 12);
642 STORE_LE(dir->name_len3, 10);
643 STORE_LE(dir->file_type3, EXT2_FT_DIR);
644 strcpy(dir->name3, "lost+found");
645 PUT((uint64_t)(FETCH_LE32(gd[0].bg_inode_table) + inode_table_blocks + 0) * blocksize, buf, blocksize);
646
647
648 if (ENABLE_FEATURE_CLEAN_UP) {
649 free(buf);
650 free(gd);
651 free(sb);
652 }
653
654 xclose(fd);
655 return EXIT_SUCCESS;
656}
657