busybox/util-linux/mkfs_ext2.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * mkfs_ext2: utility to create EXT2 filesystem
   4 * inspired by genext2fs
   5 *
   6 * Busybox'ed (2009) by Vladimir Dronnikov <dronnikov@gmail.com>
   7 *
   8 * Licensed under GPLv2, see file LICENSE in this source tree.
   9 */
  10
  11//usage:#define mkfs_ext2_trivial_usage
  12//usage:       "[-Fn] "
  13/* //usage:    "[-c|-l filename] " */
  14//usage:       "[-b BLK_SIZE] "
  15/* //usage:    "[-f fragment-size] [-g blocks-per-group] " */
  16//usage:       "[-i INODE_RATIO] [-I INODE_SIZE] "
  17/* //usage:    "[-j] [-J journal-options] [-N number-of-inodes] " */
  18//usage:       "[-m RESERVED_PERCENT] "
  19/* //usage:    "[-o creator-os] [-O feature[,...]] [-q] " */
  20/* //usage:    "[r fs-revision-level] [-E extended-options] [-v] [-F] " */
  21//usage:       "[-L LABEL] "
  22/* //usage:    "[-M last-mounted-directory] [-S] [-T filesystem-type] " */
  23//usage:       "BLOCKDEV [KBYTES]"
  24//usage:#define mkfs_ext2_full_usage "\n\n"
  25//usage:       "        -b BLK_SIZE     Block size, bytes"
  26/* //usage:  "\n        -c              Check device for bad blocks" */
  27/* //usage:  "\n        -E opts         Set extended options" */
  28/* //usage:  "\n        -f size         Fragment size in bytes" */
  29//usage:     "\n        -F              Force"
  30/* //usage:  "\n        -g N            Number of blocks in a block group" */
  31//usage:     "\n        -i RATIO        Max number of files is filesystem_size / RATIO"
  32//usage:     "\n        -I BYTES        Inode size (min 128)"
  33/* //usage:  "\n        -j              Create a journal (ext3)" */
  34/* //usage:  "\n        -J opts         Set journal options (size/device)" */
  35/* //usage:  "\n        -l file         Read bad blocks list from file" */
  36//usage:     "\n        -L LBL          Volume label"
  37//usage:     "\n        -m PERCENT      Percent of blocks to reserve for admin"
  38/* //usage:  "\n        -M dir          Set last mounted directory" */
  39//usage:     "\n        -n              Dry run"
  40/* //usage:  "\n        -N N            Number of inodes to create" */
  41/* //usage:  "\n        -o os           Set the 'creator os' field" */
  42/* //usage:  "\n        -O features     Dir_index/filetype/has_journal/journal_dev/sparse_super" */
  43/* //usage:  "\n        -q              Quiet" */
  44/* //usage:  "\n        -r rev          Set filesystem revision" */
  45/* //usage:  "\n        -S              Write superblock and group descriptors only" */
  46/* //usage:  "\n        -T fs-type      Set usage type (news/largefile/largefile4)" */
  47/* //usage:  "\n        -v              Verbose" */
  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// storage helpers
  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// All fields are little-endian
  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// taken from mkfs_minix.c. libbb candidate?
 105// "uint32_t size", since we never use it for anything >32 bits
 106static uint32_t div_roundup(uint32_t size, uint32_t n)
 107{
 108        // Overflow-resistant
 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//bb_error_msg("ALLOC: [%u][%u][%u]: [%u-%u]:=[%x],[%x]", blocksize, start, end, start/8, blocksize - end/8 - 1, (1 << (start & 7)) - 1, (uint8_t)(0xFF00 >> (end & 7)));
 120        memset(bitmap, 0, blocksize);
 121        i = start / 8;
 122        memset(bitmap, 0xFF, i);
 123        bitmap[i] = (1 << (start & 7)) - 1; //0..7 => 00000000..01111111
 124        i = end / 8;
 125        bitmap[blocksize - i - 1] |= 0x7F00 >> (end & 7); //0..7 => 00000000..11111110
 126        memset(bitmap + blocksize - i, 0xFF, i); // N.B. no overflow here!
 127}
 128
 129static uint32_t has_super(uint32_t x)
 130{
 131        // 0, 1 and powers of 3, 5, 7 up to 2^32 limit
 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/* >2^31 */,
 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    /* predefined output descriptor */
 151
 152static void PUT(uint64_t off, void *buf, uint32_t size)
 153{
 154        //bb_error_msg("PUT[%llu]:[%u]", off, size);
 155        xlseek(fd, off, SEEK_SET);
 156        xwrite(fd, buf, size);
 157}
 158
 159// 128 and 256-byte inodes:
 160// 128-byte inode is described by struct ext2_inode.
 161// 256-byte one just has these fields appended:
 162//      __u16   i_extra_isize;
 163//      __u16   i_pad1;
 164//      __u32   i_ctime_extra;  /* extra Change time (nsec << 2 | epoch) */
 165//      __u32   i_mtime_extra;  /* extra Modification time (nsec << 2 | epoch) */
 166//      __u32   i_atime_extra;  /* extra Access time (nsec << 2 | epoch) */
 167//      __u32   i_crtime;       /* File creation time */
 168//      __u32   i_crtime_extra; /* extra File creation time (nsec << 2 | epoch)*/
 169//      __u32   i_version_hi;   /* high 32 bits for 64-bit version */
 170// the rest is padding.
 171//
 172// linux/ext2_fs.h has "#define i_size_high i_dir_acl" which suggests that even
 173// 128-byte inode is capable of describing large files (i_dir_acl is meaningful
 174// only for directories, which never need i_size_high).
 175//
 176// Standard mke2fs creates a filesystem with 256-byte inodes if it is
 177// bigger than 0.5GB.
 178
 179// Standard mke2fs 1.41.9:
 180// Usage: mke2fs [-c|-l filename] [-b block-size] [-f fragment-size]
 181//      [-i bytes-per-inode] [-I inode-size] [-J journal-options]
 182//      [-G meta group size] [-N number-of-inodes]
 183//      [-m reserved-blocks-percentage] [-o creator-os]
 184//      [-g blocks-per-group] [-L volume-label] [-M last-mounted-directory]
 185//      [-O feature[,...]] [-r fs-revision] [-E extended-option[,...]]
 186//      [-T fs-type] [-U UUID] [-jnqvFSV] device [blocks-count]
 187//
 188// Options not commented below are taken but silently ignored:
 189enum {
 190        OPT_c = 1 << 0,
 191        OPT_l = 1 << 1,
 192        OPT_b = 1 << 2,         // block size, in bytes
 193        OPT_f = 1 << 3,
 194        OPT_i = 1 << 4,         // bytes per inode
 195        OPT_I = 1 << 5,         // custom inode size, in bytes
 196        OPT_J = 1 << 6,
 197        OPT_G = 1 << 7,
 198        OPT_N = 1 << 8,
 199        OPT_m = 1 << 9,         // percentage of blocks reserved for superuser
 200        OPT_o = 1 << 10,
 201        OPT_g = 1 << 11,
 202        OPT_L = 1 << 12,        // label
 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,        // dry run: do not write anything
 211        OPT_q = 1 << 21,
 212        OPT_v = 1 << 22,
 213        OPT_F = 1 << 23,
 214        OPT_S = 1 << 24,
 215        //OPT_V = 1 << 25,      // -V version. bbox applets don't support that
 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; // superblock
 240        struct ext2_group_desc *gd; // group descriptors
 241        struct ext2_inode *inode;
 242        struct ext2_dir *dir;
 243        uint8_t *buf;
 244
 245        // using global "option_mask32" instead of local "opts":
 246        // we are register starved here
 247        opt_complementary = "-1:b+:i+:I+:m+";
 248        /*opts =*/ getopt32(argv, "cl:b:f:i:I:J:G:N:m:o:g:L:M:O:r:E:T:U:jnqvFS",
 249                /*lbfi:*/ NULL, &bs, NULL, &bpi,
 250                /*IJGN:*/ &user_inodesize, NULL, NULL, NULL,
 251                /*mogL:*/ &reserved_percent, NULL, NULL, &label,
 252                /*MOrE:*/ NULL, NULL, NULL, NULL,
 253                /*TU:*/ NULL, NULL);
 254        argv += optind; // argv[0] -- device
 255
 256        // open the device, check the device is a block device
 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        // check if it is mounted
 263        // N.B. what if we format a file? find_mount_point will return false negative since
 264        // it is loop block device which is mounted!
 265        if (find_mount_point(argv[0], 0))
 266                bb_error_msg_and_die("can't format mounted filesystem");
 267
 268        // get size in kbytes
 269        kilobytes = get_volume_size_in_bytes(fd, argv[1], 1024, /*extend:*/ !(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        // Determine block size and inode size
 280        // block size is a multiple of 1024
 281        // inode size is a multiple of 128
 282        blocksize = 1024;
 283        inodesize = sizeof(struct ext2_inode); // 128
 284        if (kilobytes >= 512*1024) { // mke2fs 1.41.9 compat
 285                blocksize = 4096;
 286                inodesize = 256;
 287        }
 288        if (EXT2_MAX_BLOCK_SIZE > 4096) {
 289                // kilobytes >> 22 == size in 4gigabyte chunks.
 290                // if size >= 16k gigs, blocksize must be increased.
 291                // Try "mke2fs -F image $((16 * 1024*1024*1024))"
 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)) // not power of 2
 300        ) {
 301                bb_error_msg_and_die("blocksize %u is bad", blocksize);
 302        }
 303        // Do we have custom inode size?
 304        if (option_mask32 & OPT_I) {
 305                if (user_inodesize < sizeof(*inode)
 306                 || user_inodesize > blocksize
 307                 || (user_inodesize & (user_inodesize - 1)) // not power of 2
 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        // number of bits in one block, i.e. 8*blocksize
 318#define blocks_per_group (8 * blocksize)
 319        first_block = (EXT2_MIN_BLOCK_SIZE == blocksize);
 320        blocksize_log2 = int_log2(blocksize);
 321
 322        // Determine number of blocks
 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        // Experimentally, standard mke2fs won't work on images smaller than 60k
 329        if (nblocks < 60)
 330                bb_error_msg_and_die("need >= 60 blocks");
 331
 332        // How many reserved blocks?
 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        // N.B. killing e2fsprogs feature! Unused blocks don't account in calculations
 338        nblocks_full = nblocks;
 339
 340        // If last block group is too small, nblocks may be decreased in order
 341        // to discard it, and control returns here to recalculate some
 342        // parameters.
 343        // Note: blocksize and bytes_per_inode are never recalculated.
 344 retry:
 345        // N.B. a block group can have no more than blocks_per_group blocks
 346        ngroups = div_roundup(nblocks - first_block, blocks_per_group);
 347
 348        group_desc_blocks = div_roundup(ngroups, blocksize / sizeof(*gd));
 349        // TODO: reserved blocks must be marked as such in the bitmaps,
 350        // or resulting filesystem is corrupt
 351        if (ENABLE_FEATURE_MKFS_EXT2_RESERVED_GDT) {
 352                /*
 353                 * From e2fsprogs: Calculate the number of GDT blocks to reserve for online
 354                 * filesystem growth.
 355                 * The absolute maximum number of GDT blocks we can reserve is determined by
 356                 * the number of block pointers that can fit into a single block.
 357                 * We set it at 1024x the current filesystem size, or
 358                 * the upper block count limit (2^32), whichever is lower.
 359                 */
 360                uint32_t reserved_group_desc_blocks = 0xFFFFFFFF; // maximum block number
 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                //TODO: STORE_LE(sb->s_reserved_gdt_blocks, reserved_group_desc_blocks);
 368                group_desc_blocks += reserved_group_desc_blocks;
 369        }
 370
 371        {
 372                // N.B. e2fsprogs does as follows!
 373                uint32_t overhead, remainder;
 374                // ninodes is the max number of inodes in this filesystem
 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                // minimum number because the first EXT2_GOOD_OLD_FIRST_INO-1 are reserved
 380                if (inodes_per_group < 16)
 381                        inodes_per_group = 16;
 382                // a block group can't have more inodes than blocks
 383                if (inodes_per_group > blocks_per_group)
 384                        inodes_per_group = blocks_per_group;
 385                // adjust inodes per group so they completely fill the inode table blocks in the descriptor
 386                inodes_per_group = (div_roundup(inodes_per_group * inodesize, blocksize) * blocksize) / inodesize;
 387                // make sure the number of inodes per group is a multiple of 8
 388                inodes_per_group &= ~7;
 389                inode_table_blocks = div_roundup(inodes_per_group * inodesize, blocksize);
 390
 391                // to be useful, lost+found should occupy at least 2 blocks (but not exceeding 16*1024 bytes),
 392                // and at most EXT2_NDIR_BLOCKS. So reserve these blocks right now
 393                /* Or e2fsprogs comment verbatim (what does it mean?):
 394                 * Ensure that lost+found is at least 2 blocks, so we always
 395                 * test large empty blocks for big-block filesystems. */
 396                lost_and_found_blocks = MIN(EXT2_NDIR_BLOCKS, 16 >> (blocksize_log2 - EXT2_MIN_BLOCK_LOG_SIZE));
 397
 398                // the last group needs more attention: isn't it too small for possible overhead?
 399                overhead = (has_super(ngroups - 1) ? (1/*sb*/ + group_desc_blocks) : 0) + 1/*bbmp*/ + 1/*ibmp*/ + inode_table_blocks;
 400                remainder = (nblocks - first_block) % blocks_per_group;
 401                ////can't happen, nblocks >= 60 guarantees this
 402                ////if ((1 == ngroups)
 403                //// && remainder
 404                //// && (remainder < overhead + 1/* "/" */ + lost_and_found_blocks)
 405                ////) {
 406                ////    bb_error_msg_and_die("way small device");
 407                ////}
 408
 409                // Standard mke2fs uses 50. Looks like a bug in our calculation
 410                // of "remainder" or "overhead" - we don't match standard mke2fs
 411                // when we transition from one group to two groups
 412                // (a bit after 8M image size), but it works for two->three groups
 413                // transition (at 16M).
 414                if (remainder && (remainder < overhead + 50)) {
 415//bb_error_msg("CHOP[%u]", remainder);
 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        // TODO: 3/5 refuse if mounted
 467        // TODO: 4/5 compat options
 468        // TODO: 1/5 sanity checks
 469        // TODO: 0/5 more verbose error messages
 470        // TODO: 4/5 bigendianness: recheck, wait for ARM reporters
 471        // TODO: 2/5 reserved GDT: how to mark but not allocate?
 472        // TODO: 3/5 dir_index?
 473
 474        // fill the superblock
 475        sb = xzalloc(1024);
 476        STORE_LE(sb->s_rev_level, EXT2_DYNAMIC_REV); // revision 1 filesystem
 477        STORE_LE(sb->s_magic, EXT2_SUPER_MAGIC);
 478        STORE_LE(sb->s_inode_size, inodesize);
 479        // set "Required extra isize" and "Desired extra isize" fields to 28
 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        // first 1024 bytes of the device are for boot record. If block size is 1024 bytes, then
 488        // the first block is 1, otherwise 0
 489        STORE_LE(sb->s_first_data_block, first_block);
 490        // block and inode bitmaps occupy no more than one block, so maximum number of blocks is
 491        STORE_LE(sb->s_blocks_per_group, blocks_per_group);
 492        STORE_LE(sb->s_frags_per_group, blocks_per_group);
 493        // blocks
 494        STORE_LE(sb->s_blocks_count, nblocks);
 495        // reserve blocks for superuser
 496        STORE_LE(sb->s_r_blocks_count, nreserved);
 497        // ninodes
 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        // timestamps
 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        // misc. Values are chosen to match mke2fs 1.41.9
 507        STORE_LE(sb->s_state, 1); // TODO: what's 1?
 508        STORE_LE(sb->s_creator_os, EXT2_OS_LINUX);
 509        STORE_LE(sb->s_checkinterval, 24*60*60 * 180); // 180 days
 510        STORE_LE(sb->s_errors, EXT2_ERRORS_DEFAULT);
 511        // mke2fs 1.41.9 also sets EXT3_FEATURE_COMPAT_RESIZE_INODE
 512        // and if >= 0.5GB, EXT3_FEATURE_RO_COMPAT_LARGE_FILE.
 513        // we use values which match "mke2fs -O ^resize_inode":
 514        // in this case 1.41.9 never sets EXT3_FEATURE_RO_COMPAT_LARGE_FILE.
 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         * From e2fsprogs: add "jitter" to the superblock's check interval so that we
 529         * don't check all the filesystems at the same time.  We use a
 530         * kludgy hack of using the UUID to derive a random jitter value.
 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        // write the label
 537        safe_strncpy((char *)sb->s_volume_name, label, sizeof(sb->s_volume_name));
 538
 539        // calculate filesystem skeleton structures
 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/*sb*/ + group_desc_blocks) : 0);
 548                uint32_t free_blocks;
 549                // fill group descriptors
 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/*bbmp*/ + 1/*ibmp*/ + inode_table_blocks;
 554                gd[i].bg_free_inodes_count = inodes_per_group;
 555                //STORE_LE(gd[i].bg_used_dirs_count, 0);
 556                // N.B. both "/" and "/lost+found" are within the first block group
 557                // "/" occupies 1 block, "/lost+found" occupies lost_and_found_blocks...
 558                if (0 == i) {
 559                        // ... thus increased overhead for the first block group ...
 560                        overhead += 1 + lost_and_found_blocks;
 561                        // ... and 2 used directories
 562                        STORE_LE(gd[i].bg_used_dirs_count, 2);
 563                        // well known reserved inodes belong to the first block too
 564                        gd[i].bg_free_inodes_count -= EXT2_GOOD_OLD_FIRST_INO;
 565                }
 566
 567                // cache free block count of the group
 568                free_blocks = (n < blocks_per_group ? n : blocks_per_group) - overhead;
 569
 570                // mark preallocated blocks as allocated
 571//bb_error_msg("ALLOC: [%u][%u][%u]", blocksize, overhead, blocks_per_group - (free_blocks + overhead));
 572                allocate(buf, blocksize,
 573                        // reserve "overhead" blocks
 574                        overhead,
 575                        // mark unused trailing blocks
 576                        blocks_per_group - (free_blocks + overhead)
 577                );
 578                // dump block bitmap
 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                // mark preallocated inodes as allocated
 583                allocate(buf, blocksize,
 584                        // mark reserved inodes
 585                        inodes_per_group - gd[i].bg_free_inodes_count,
 586                        // mark unused trailing inodes
 587                        blocks_per_group - inodes_per_group
 588                );
 589                // dump inode bitmap
 590                //PUT((uint64_t)(FETCH_LE32(gd[i].bg_block_bitmap)) * blocksize, buf, blocksize);
 591                //but it's right after block bitmap, so we can just:
 592                xwrite(fd, buf, blocksize);
 593                STORE_LE(gd[i].bg_free_inodes_count, gd[i].bg_free_inodes_count);
 594
 595                // count overall free blocks
 596                sb->s_free_blocks_count += free_blocks;
 597        }
 598        STORE_LE(sb->s_free_blocks_count, sb->s_free_blocks_count);
 599
 600        // dump filesystem skeleton structures
 601//      printf("Writing superblocks and filesystem accounting information: ");
 602        for (i = 0, pos = first_block; i < ngroups; i++, pos += blocks_per_group) {
 603                // dump superblock and group descriptors and their backups
 604                if (has_super(i)) {
 605                        // N.B. 1024 byte blocks are special
 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        // zero boot sectors
 614        memset(buf, 0, blocksize);
 615        // Disabled: standard mke2fs doesn't do this, and
 616        // on SPARC this destroys Sun disklabel.
 617        // Users who need/want zeroing can easily do it with dd.
 618        //PUT(0, buf, 1024); // N.B. 1024 <= blocksize, so buf[0..1023] contains zeros
 619
 620        // zero inode tables
 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        // prepare directory inode
 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        // inode->i_blocks stores the number of 512 byte data blocks
 634        // (512, because it goes directly to struct stat without scaling)
 635        STORE_LE(inode->i_blocks, blocksize / 512);
 636
 637        // dump root dir inode
 638        STORE_LE(inode->i_links_count, 3); // "/.", "/..", "/lost+found/.." point to this inode
 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        // dump lost+found dir inode
 644        STORE_LE(inode->i_links_count, 2); // both "/lost+found" and "/lost+found/." point to this inode
 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); // use next block
 650//bb_error_msg("LAST BLOCK USED[%u]", i + n);
 651        PUT(((uint64_t)FETCH_LE32(gd[0].bg_inode_table) * blocksize) + (EXT2_GOOD_OLD_FIRST_INO-1) * inodesize,
 652                                buf, inodesize);
 653
 654        // dump directories
 655        memset(buf, 0, blocksize);
 656        dir = (struct ext2_dir *)buf;
 657
 658        // dump 2nd+ blocks of "/lost+found"
 659        STORE_LE(dir->rec_len1, blocksize); // e2fsck 1.41.4 compat (1.41.9 does not need this)
 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        // dump 1st block of "/lost+found"
 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        // dump root dir block
 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        // cleanup
 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