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