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