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