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