da27dc77d1e623d38b707a647555ed4a02e3e946
[oweals/busybox.git] / util-linux / mkfs_ext2.c
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 tarball for details.
9  */
10 #include "libbb.h"
11 #include <linux/fs.h>
12 #include <linux/ext2_fs.h>
13 #include <sys/user.h> /* PAGE_SIZE */
14 #ifndef PAGE_SIZE
15 # define PAGE_SIZE 4096
16 #endif
17 #include "volume_id/volume_id_internal.h"
18
19 #define ENABLE_FEATURE_MKFS_EXT2_RESERVED_GDT 0
20 #define ENABLE_FEATURE_MKFS_EXT2_DIR_INDEX 1
21
22 // from e2fsprogs
23 #define s_reserved_gdt_blocks s_padding1
24 #define s_mkfs_time           s_reserved[0]
25 #define s_flags               s_reserved[22]
26 #define EXT2_HASH_HALF_MD4     1
27 #define EXT2_FLAGS_SIGNED_HASH 0x0001
28
29 // whiteout: for writable overlays
30 //#define LINUX_S_IFWHT                  0160000
31 //#define EXT2_FEATURE_INCOMPAT_WHITEOUT 0x0020
32
33 // storage helpers
34 char BUG_wrong_field_size(void);
35 #define STORE_LE(field, value) \
36 do { \
37         if (sizeof(field) == 4) \
38                 field = cpu_to_le32(value); \
39         else if (sizeof(field) == 2) \
40                 field = cpu_to_le16(value); \
41         else if (sizeof(field) == 1) \
42                 field = (value); \
43         else \
44                 BUG_wrong_field_size(); \
45 } while (0)
46
47 #define FETCH_LE32(field) \
48         (sizeof(field) == 4 ? cpu_to_le32(field) : BUG_wrong_field_size())
49
50 // All fields are little-endian
51 struct ext2_dir {
52         uint32_t inode1;
53         uint16_t rec_len1;
54         uint8_t  name_len1;
55         uint8_t  file_type1;
56         char     name1[4];
57         uint32_t inode2;
58         uint16_t rec_len2;
59         uint8_t  name_len2;
60         uint8_t  file_type2;
61         char     name2[4];
62         uint32_t inode3;
63         uint16_t rec_len3;
64         uint8_t  name_len3;
65         uint8_t  file_type3;
66         char     name3[12];
67 };
68
69 static unsigned int_log2(unsigned arg)
70 {
71         unsigned r = 0;
72         while ((arg >>= 1) != 0)
73                 r++;
74         return r;
75 }
76
77 // taken from mkfs_minix.c. libbb candidate?
78 // why "uint64_t size"? we never use it for anything >32 bits
79 static uint32_t div_roundup(uint64_t size, uint32_t n)
80 {
81         // Overflow-resistant
82         uint32_t res = size / n;
83         if (res * n != size)
84                 res++;
85         return res;
86 }
87
88 static void allocate(uint8_t *bitmap, uint32_t blocksize, uint32_t start, uint32_t end)
89 {
90         uint32_t i;
91         memset(bitmap, 0, blocksize);
92         i = start / 8;
93         memset(bitmap, 0xFF, i);
94         bitmap[i] = 0xFF >> (8 - (start & 7));
95 //bb_info_msg("ALLOC: [%u][%u][%u]: [%u-%u]:=[%x],[%x]", blocksize, start, end, start/8, blocksize - end/8 - 1, 0xFF >> (8 - (start & 7)), (uint8_t)(0xFF << (8-(end&7))));
96         i = end / 8;
97         bitmap[blocksize - i - 1] = 0xFF << (8 - (end & 7));
98         memset(bitmap + blocksize - i, 0xFF, i); // N.B. no overflow here!
99 }
100
101 static uint32_t has_super(uint32_t x)
102 {
103         // 0, 1 and powers of 3, 5, 7 up to 2^32 limit
104         static const uint32_t supers[] = {
105                 0, 1, 3, 5, 7, 9, 25, 27, 49, 81, 125, 243, 343, 625, 729,
106                 2187, 2401, 3125, 6561, 15625, 16807, 19683, 59049, 78125,
107                 117649, 177147, 390625, 531441, 823543, 1594323, 1953125,
108                 4782969, 5764801, 9765625, 14348907, 40353607, 43046721,
109                 48828125, 129140163, 244140625, 282475249, 387420489,
110                 1162261467, 1220703125, 1977326743, 3486784401/* >2^31 */,
111         };
112         const uint32_t *sp = supers + ARRAY_SIZE(supers);
113         while (1) {
114                 sp--;
115                 if (x == *sp)
116                         return 1;
117                 if (x > *sp)
118                         return 0;
119         }
120 }
121
122 /* Standard mke2fs 1.41.9:
123  * Usage: mke2fs [-c|-l filename] [-b block-size] [-f fragment-size]
124  *      [-i bytes-per-inode] [-I inode-size] [-J journal-options]
125  *      [-G meta group size] [-N number-of-inodes]
126  *      [-m reserved-blocks-percentage] [-o creator-os]
127  *      [-g blocks-per-group] [-L volume-label] [-M last-mounted-directory]
128  *      [-O feature[,...]] [-r fs-revision] [-E extended-option[,...]]
129  *      [-T fs-type] [-U UUID] [-jnqvFSV] device [blocks-count]
130 */
131 // N.B. not commented below options are taken and silently ignored
132 enum {
133         OPT_c = 1 << 0,
134         OPT_l = 1 << 1,
135         OPT_b = 1 << 2,         // block size, in bytes
136         OPT_f = 1 << 3,
137         OPT_i = 1 << 4,         // bytes per inode
138         OPT_I = 1 << 5,
139         OPT_J = 1 << 6,
140         OPT_G = 1 << 7,
141         OPT_N = 1 << 8,
142         OPT_m = 1 << 9,         // percentage of blocks reserved for superuser
143         OPT_o = 1 << 10,
144         OPT_g = 1 << 11,
145         OPT_L = 1 << 12,        // label
146         OPT_M = 1 << 13,
147         OPT_O = 1 << 14,
148         OPT_r = 1 << 15,
149         OPT_E = 1 << 16,
150         OPT_T = 1 << 17,
151         OPT_U = 1 << 18,
152         OPT_j = 1 << 19,
153         OPT_n = 1 << 20,        // dry run: do not write anything
154         OPT_q = 1 << 21,
155         OPT_v = 1 << 22,
156         OPT_F = 1 << 23,
157         OPT_S = 1 << 24,
158         //OPT_V = 1 << 25,      // -V version. bbox applets don't support that
159 };
160
161 #define fd 3    /* predefined output descriptor */
162
163 static void PUT(uint64_t off, void *buf, uint32_t size)
164 {
165 //      bb_info_msg("PUT[%llu]:[%u]", off, size);
166         xlseek(fd, off, SEEK_SET);
167         xwrite(fd, buf, size);
168 }
169
170 int mkfs_ext2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
171 int mkfs_ext2_main(int argc UNUSED_PARAM, char **argv)
172 {
173         unsigned i, pos, n;
174         unsigned bs, bpi;
175         unsigned blocksize, blocksize_log2;
176         unsigned reserved_percent = 5;
177         unsigned long long kilobytes;
178         uint32_t nblocks, nblocks_full, nreserved;
179         uint32_t ngroups;
180         uint32_t bytes_per_inode;
181         uint32_t first_data_block;
182         uint32_t inodes_per_group;
183         uint32_t gdtsz, itsz;
184         time_t timestamp;
185         unsigned opts;
186         const char *label = "";
187         struct stat st;
188         struct ext2_super_block *sb; // superblock
189         struct ext2_group_desc *gd; // group descriptors
190         struct ext2_inode *inode;
191         struct ext2_dir *dir;
192         uint8_t *buf;
193
194         opt_complementary = "-1:b+:m+:i+";
195         opts = getopt32(argv, "cl:b:f:i:I:J:G:N:m:o:g:L:M:O:r:E:T:U:jnqvFS",
196                 NULL, &bs, NULL, &bpi, NULL, NULL, NULL, NULL,
197                 &reserved_percent, NULL, NULL, &label, NULL, NULL, NULL, NULL, NULL, NULL);
198         argv += optind; // argv[0] -- device
199
200         // check the device is a block device
201         xmove_fd(xopen(argv[0], O_WRONLY), fd);
202         fstat(fd, &st);
203         if (!S_ISBLK(st.st_mode) && !(opts & OPT_F))
204                 bb_error_msg_and_die("not a block device");
205
206         // check if it is mounted
207         // N.B. what if we format a file? find_mount_point will return false negative since
208         // it is loop block device which mounted!
209         if (find_mount_point(argv[0], 0))
210                 bb_error_msg_and_die("can't format mounted filesystem");
211
212         // open the device, get size in kbytes
213         if (argv[1]) {
214                 kilobytes = xatoull(argv[1]);
215         } else {
216                 kilobytes = (uoff_t)xlseek(fd, 0, SEEK_END) / 1024;
217         }
218
219         bytes_per_inode = 16384;
220         if (kilobytes < 512*1024)
221                 bytes_per_inode = 4096;
222         if (kilobytes < 3*1024)
223                 bytes_per_inode = 8192;
224         if (opts & OPT_i)
225                 bytes_per_inode = bpi;
226
227         // Determine block size
228         // block size is a multiple of 1024
229         blocksize = 1024;
230         if (kilobytes >= 512*1024) // mke2fs 1.41.9 compat
231                 blocksize = 4096;
232         if (EXT2_MAX_BLOCK_SIZE > 4096) {
233                 // kilobytes >> 22 == size in 4gigabyte chunks.
234                 // if size >= 16k gigs, blocksize must be increased.
235                 // Try "mke2fs -F image $((16 * 1024*1024*1024))"
236                 while ((kilobytes >> 22) >= blocksize)
237                         blocksize *= 2;
238         }
239         if (opts & OPT_b)
240                 blocksize = bs;
241         if (blocksize < EXT2_MIN_BLOCK_SIZE
242          || blocksize > EXT2_MAX_BLOCK_SIZE
243          || (blocksize & (blocksize - 1)) // not power of 2
244         ) {
245                 bb_error_msg_and_die("blocksize %u is bad", blocksize);
246         }
247         // number of bits in one block, i.e. 8*blocksize
248 #define blocks_per_group (8 * blocksize)
249         first_data_block = (EXT2_MIN_BLOCK_SIZE == blocksize);
250         blocksize_log2 = int_log2(blocksize);
251
252         // Determine number of blocks
253         kilobytes >>= (blocksize_log2 - EXT2_MIN_BLOCK_LOG_SIZE);
254         nblocks = kilobytes;
255         if (nblocks != kilobytes)
256                 bb_error_msg_and_die("block count doesn't fit in 32 bits");
257 #define kilobytes kilobytes_unused_after_this
258         if (blocksize < PAGE_SIZE)
259                 nblocks &= ~((PAGE_SIZE >> blocksize_log2)-1);
260         // Experimentally, standard mke2fs won't work on images smaller than 60k
261         if (nblocks < 60)
262                 bb_error_msg_and_die("need >= 60 blocks");
263
264         // How many reserved blocks?
265         if (reserved_percent > 50)
266                 bb_error_msg_and_die("-%c is bad", 'm');
267         //nreserved = div_roundup((uint64_t) nblocks * reserved_percent, 100);
268         nreserved = (uint64_t)nblocks * reserved_percent / 100;
269
270         // N.B. killing e2fsprogs feature! Unused blocks don't account in calculations
271         nblocks_full = nblocks;
272
273         // If last block group is too small, nblocks may be decreased in order
274         // to discard it, and control returns here to recalculate some
275         // parameters.
276         // Note: blocksize and bytes_per_inode are never recalculated.
277  retry:
278         // N.B. a block group can have no more than blocks_per_group blocks
279         ngroups = div_roundup(nblocks - first_data_block, blocks_per_group);
280         if (0 == ngroups)
281                 bb_error_msg_and_die("ngroups");
282
283         gdtsz = div_roundup(ngroups, blocksize / sizeof(*gd));
284         // TODO: reserved blocks must be marked as such in the bitmaps,
285         // or resulting filesystem is corrupt
286         if (ENABLE_FEATURE_MKFS_EXT2_RESERVED_GDT) {
287                 /*
288                  * From e2fsprogs: Calculate the number of GDT blocks to reserve for online
289                  * filesystem growth.
290                  * The absolute maximum number of GDT blocks we can reserve is determined by
291                  * the number of block pointers that can fit into a single block.
292                  * We set it at 1024x the current filesystem size, or
293                  * the upper block count limit (2^32), whichever is lower.
294                  */
295                 uint32_t rgdtsz = 0xFFFFFFFF; // maximum block number
296                 if (nblocks < rgdtsz / 1024)
297                         rgdtsz = nblocks * 1024;
298                 rgdtsz = div_roundup(rgdtsz - first_data_block, blocks_per_group);
299                 rgdtsz = div_roundup(rgdtsz, blocksize / sizeof(*gd)) - gdtsz;
300                 if (rgdtsz > blocksize / sizeof(uint32_t))
301                         rgdtsz = blocksize / sizeof(uint32_t);
302                 //TODO: STORE_LE(sb->s_reserved_gdt_blocks, rgdtsz);
303                 gdtsz += rgdtsz;
304         }
305
306         {
307                 // N.B. e2fsprogs does as follows!
308                 // ninodes is the total number of inodes (files) in the file system
309                 uint32_t ninodes = nblocks_full / (blocksize >= 4096 ? 1 : 4096 / blocksize);
310                 uint32_t overhead, remainder;
311                 if (ninodes < EXT2_GOOD_OLD_FIRST_INO+1)
312                         ninodes = EXT2_GOOD_OLD_FIRST_INO+1;
313                 inodes_per_group = div_roundup(ninodes, ngroups);
314                 // minimum number because the first EXT2_GOOD_OLD_FIRST_INO-1 are reserved
315                 if (inodes_per_group < 16)
316                         inodes_per_group = 16;
317
318                 // a block group can have no more than 8*blocksize inodes
319                 if (inodes_per_group > blocks_per_group)
320                         inodes_per_group = blocks_per_group;
321                 // adjust inodes per group so they completely fill the inode table blocks in the descriptor
322                 inodes_per_group = (div_roundup(inodes_per_group * sizeof(*inode), blocksize) * blocksize) / sizeof(*inode);
323                 // make sure the number of inodes per group is a multiple of 8
324                 inodes_per_group &= ~7;
325                 itsz = div_roundup(inodes_per_group * sizeof(*inode), blocksize);
326
327                 // the last group needs more attention: isn't it too small for possible overhead?
328                 overhead = (has_super(ngroups - 1) ? (1/*sb*/ + gdtsz) : 0) + 1/*bbmp*/ + 1/*ibmp*/ + itsz;
329                 remainder = (nblocks - first_data_block) % blocks_per_group;
330                 if ((1 == ngroups) && remainder && (remainder < overhead))
331                         bb_error_msg_and_die("way small device");
332                 if (remainder && (remainder < overhead + 50/* e2fsprogs hardcoded */)) {
333 //bb_info_msg("CHOP[%u]", remainder);
334                         nblocks -= remainder;
335                         goto retry;
336                 }
337         }
338
339         // print info
340         if (nblocks_full - nblocks)
341                 printf("warning: %u blocks unused\n\n", nblocks_full - nblocks);
342         printf(
343                 "Filesystem label=%s\n"
344                 "OS type: Linux\n"
345                 "Block size=%u (log=%u)\n"
346                 "Fragment size=%u (log=%u)\n"
347                 "%u inodes, %u blocks\n"
348                 "%u blocks (%u%%) reserved for the super user\n"
349                 "First data block=%u\n"
350                 "Maximum filesystem blocks=%u\n"
351                 "%u block groups\n"
352                 "%u blocks per group, %u fragments per group\n"
353                 "%u inodes per group"
354                 , label
355                 , blocksize, blocksize_log2 - EXT2_MIN_BLOCK_LOG_SIZE
356                 , blocksize, blocksize_log2 - EXT2_MIN_BLOCK_LOG_SIZE
357                 , inodes_per_group * ngroups, nblocks
358                 , nreserved, reserved_percent
359                 , first_data_block
360                 , gdtsz * (blocksize / sizeof(*gd)) * blocks_per_group
361                 , ngroups
362                 , blocks_per_group, blocks_per_group
363                 , inodes_per_group
364         );
365         {
366                 const char *fmt = "\nSuperblock backups stored on blocks:\n"
367                         "\t%u";
368                 pos = first_data_block;
369                 for (i = 1; i < ngroups; i++) {
370                         pos += blocks_per_group;
371                         if (has_super(i)) {
372                                 printf(fmt, (unsigned)pos);
373                                 fmt = ", %u";
374                         }
375                 }
376         }
377         bb_putchar('\n');
378
379         // dry run? -> we are done
380         if (opts & OPT_n)
381                 goto done;
382
383         // TODO: 3/5 refuse if mounted
384         // TODO: 4/5 compat options
385         // TODO: 1/5 sanity checks
386         // TODO: 0/5 more verbose error messages
387         // TODO: 4/5 bigendianness: recheck, wait for ARM reporters
388         // TODO: 2/5 reserved GDT: how to mark but not allocate?
389         // TODO: 3/5 dir_index?
390
391         // fill the superblock
392         sb = xzalloc(blocksize);
393         STORE_LE(sb->s_rev_level, 1); // revision 1 filesystem
394         STORE_LE(sb->s_magic, EXT2_SUPER_MAGIC);
395         STORE_LE(sb->s_inode_size, sizeof(*inode));
396         STORE_LE(sb->s_first_ino, EXT2_GOOD_OLD_FIRST_INO);
397         STORE_LE(sb->s_log_block_size, blocksize_log2 - EXT2_MIN_BLOCK_LOG_SIZE);
398         STORE_LE(sb->s_log_frag_size, blocksize_log2 - EXT2_MIN_BLOCK_LOG_SIZE);
399         // first 1024 bytes of the device are for boot record. If block size is 1024 bytes, then
400         // the first block available for data is 1, otherwise 0
401         STORE_LE(sb->s_first_data_block, first_data_block); // 0 or 1
402         // block and inode bitmaps occupy no more than one block, so maximum number of blocks is
403         STORE_LE(sb->s_blocks_per_group, blocks_per_group);
404         STORE_LE(sb->s_frags_per_group, blocks_per_group);
405         // blocks
406         STORE_LE(sb->s_blocks_count, nblocks);
407         // reserve blocks for superuser
408         STORE_LE(sb->s_r_blocks_count, nreserved);
409         // ninodes
410         STORE_LE(sb->s_inodes_per_group, inodes_per_group);
411         STORE_LE(sb->s_inodes_count, inodes_per_group * ngroups);
412         STORE_LE(sb->s_free_inodes_count, inodes_per_group * ngroups - EXT2_GOOD_OLD_FIRST_INO);
413         // timestamps
414         timestamp = time(NULL);
415         STORE_LE(sb->s_mkfs_time, timestamp);
416         STORE_LE(sb->s_wtime, timestamp);
417         STORE_LE(sb->s_lastcheck, timestamp);
418         // misc
419         STORE_LE(sb->s_state, 1); // TODO: what's 1?
420         STORE_LE(sb->s_creator_os, EXT2_OS_LINUX);
421         STORE_LE(sb->s_checkinterval, 24*60*60 * 180); // 180 days
422         STORE_LE(sb->s_errors, EXT2_ERRORS_DEFAULT);
423         STORE_LE(sb->s_feature_compat, EXT2_FEATURE_COMPAT_SUPP
424                 | (EXT2_FEATURE_COMPAT_RESIZE_INO * ENABLE_FEATURE_MKFS_EXT2_RESERVED_GDT)
425                 | (EXT2_FEATURE_COMPAT_DIR_INDEX * ENABLE_FEATURE_MKFS_EXT2_DIR_INDEX)
426         );
427         // e2fsck from 1.41.9 doesn't like EXT2_FEATURE_INCOMPAT_WHITEOUT
428         STORE_LE(sb->s_feature_incompat, EXT2_FEATURE_INCOMPAT_FILETYPE);// | EXT2_FEATURE_INCOMPAT_WHITEOUT;
429         STORE_LE(sb->s_feature_ro_compat, EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER);
430         STORE_LE(sb->s_flags, EXT2_FLAGS_SIGNED_HASH * ENABLE_FEATURE_MKFS_EXT2_DIR_INDEX);
431         generate_uuid(sb->s_uuid);
432         if (ENABLE_FEATURE_MKFS_EXT2_DIR_INDEX) {
433                 STORE_LE(sb->s_def_hash_version, EXT2_HASH_HALF_MD4);
434                 generate_uuid((uint8_t *)sb->s_hash_seed);
435         }
436         /*
437          * From e2fsprogs: add "jitter" to the superblock's check interval so that we
438          * don't check all the filesystems at the same time.  We use a
439          * kludgy hack of using the UUID to derive a random jitter value.
440          */
441         STORE_LE(sb->s_max_mnt_count, EXT2_DFL_MAX_MNT_COUNT
442                 + (sb->s_uuid[ARRAY_SIZE(sb->s_uuid)-1] % EXT2_DFL_MAX_MNT_COUNT));
443
444         // write the label
445         safe_strncpy((char *)sb->s_volume_name, label, sizeof(sb->s_volume_name));
446
447         // fill group descriptors
448         gd = xzalloc(gdtsz * blocksize);
449         buf = xmalloc(blocksize);
450         sb->s_free_blocks_count = 0;
451         for (i = 0, pos = first_data_block, n = nblocks - first_data_block;
452                 i < ngroups;
453                 i++, pos += blocks_per_group, n -= blocks_per_group
454         ) {
455                 uint32_t overhead = pos + (has_super(i) ? (1/*sb*/ + gdtsz) : 0);
456                 uint32_t fb;
457                 STORE_LE(gd[i].bg_block_bitmap, overhead + 0);
458                 STORE_LE(gd[i].bg_inode_bitmap, overhead + 1);
459                 STORE_LE(gd[i].bg_inode_table, overhead + 2);
460                 overhead = overhead - pos + 1/*bbmp*/ + 1/*ibmp*/ + itsz;
461                 gd[i].bg_free_inodes_count = inodes_per_group;
462                 //STORE_LE(gd[i].bg_used_dirs_count, 0);
463                 // N.B. both root and lost+found dirs are within the first block group, thus +2
464                 if (0 == i) {
465                         overhead += 2;
466                         STORE_LE(gd[i].bg_used_dirs_count, 2);
467                         gd[i].bg_free_inodes_count -= EXT2_GOOD_OLD_FIRST_INO;
468                 }
469
470                 // mark preallocated blocks as allocated
471                 fb = (n < blocks_per_group ? n : blocks_per_group) - overhead;
472 //bb_info_msg("ALLOC: [%u][%u][%u]", blocksize, overhead, blocks_per_group - (fb + overhead));
473                 allocate(buf, blocksize,
474                         overhead,
475                         blocks_per_group - (fb + overhead)
476                 );
477                 // dump block bitmap
478                 PUT((uint64_t)(FETCH_LE32(gd[i].bg_block_bitmap)) * blocksize, buf, blocksize);
479                 STORE_LE(gd[i].bg_free_blocks_count, fb);
480
481                 // mark preallocated inodes as allocated
482                 allocate(buf, blocksize,
483                         inodes_per_group - gd[i].bg_free_inodes_count,
484                         blocks_per_group - inodes_per_group
485                 );
486                 // dump inode bitmap
487                 //PUT((uint64_t)(FETCH_LE32(gd[i].bg_block_bitmap)) * blocksize, buf, blocksize);
488                 //but it's right after block bitmap, so we can just:
489                 xwrite(fd, buf, blocksize);
490                 STORE_LE(gd[i].bg_free_inodes_count, gd[i].bg_free_inodes_count);
491
492                 // count overall free blocks
493                 sb->s_free_blocks_count += fb;
494         }
495         STORE_LE(sb->s_free_blocks_count, sb->s_free_blocks_count);
496
497         // dump filesystem skeleton structures
498 //      printf("Writing superblocks and filesystem accounting information: ");
499         for (i = 0, pos = first_data_block; i < ngroups; i++, pos += blocks_per_group) {
500                 // dump superblock and group descriptors and their backups
501                 if (has_super(i)) {
502                         // N.B. 1024 byte blocks are special
503                         PUT(((uint64_t)pos * blocksize) + ((0 == i && 0 == first_data_block) ? 1024 : 0), sb, 1024);//blocksize);
504                         PUT(((uint64_t)pos * blocksize) + blocksize, gd, gdtsz * blocksize);
505                 }
506         }
507
508         // zero boot sectors
509         memset(buf, 0, blocksize);
510         PUT(0, buf, 1024); // N.B. 1024 <= blocksize, so buf[0..1023] contains zeros
511         // zero inode tables
512         for (i = 0; i < ngroups; ++i)
513                 for (n = 0; n < itsz; ++n)
514                         PUT((uint64_t)(FETCH_LE32(gd[i].bg_inode_table) + n) * blocksize, buf, blocksize);
515
516         // prepare directory inode
517         inode = (struct ext2_inode *)buf;
518         STORE_LE(inode->i_mode, S_IFDIR | S_IRWXU | S_IRGRP | S_IROTH | S_IXGRP | S_IXOTH);
519         STORE_LE(inode->i_mtime, timestamp);
520         STORE_LE(inode->i_atime, timestamp);
521         STORE_LE(inode->i_ctime, timestamp);
522         STORE_LE(inode->i_size, blocksize);
523         // N.B. inode->i_blocks stores the number of 512 byte data blocks. Why on Earth?!
524         STORE_LE(inode->i_blocks, blocksize / 512);
525
526         // dump root dir inode
527         STORE_LE(inode->i_links_count, 3); // "/.", "/..", "/lost+found/.." point to this inode
528         STORE_LE(inode->i_block[0], FETCH_LE32(gd[0].bg_inode_table) + itsz);
529         PUT(((uint64_t)FETCH_LE32(gd[0].bg_inode_table) * blocksize) + (EXT2_ROOT_INO-1) * sizeof(*inode), buf, sizeof(*inode));
530
531         // dump lost+found dir inode
532         STORE_LE(inode->i_links_count, 2); // both "/lost+found" and "/lost+found/." point to this inode
533         STORE_LE(inode->i_block[0], inode->i_block[0]+1); // use next block
534         PUT(((uint64_t)FETCH_LE32(gd[0].bg_inode_table) * blocksize) + (EXT2_GOOD_OLD_FIRST_INO-1) * sizeof(*inode), buf, sizeof(*inode));
535
536         // dump directories
537         memset(buf, 0, blocksize);
538         dir = (struct ext2_dir *)buf;
539
540         // dump lost+found dir block
541         STORE_LE(dir->inode1, EXT2_GOOD_OLD_FIRST_INO);
542         STORE_LE(dir->rec_len1, 12);
543         STORE_LE(dir->name_len1, 1);
544         STORE_LE(dir->file_type1, EXT2_FT_DIR);
545         dir->name1[0] = '.';
546         STORE_LE(dir->inode2, EXT2_ROOT_INO);
547         STORE_LE(dir->rec_len2, blocksize - 12);
548         STORE_LE(dir->name_len2, 2);
549         STORE_LE(dir->file_type2, EXT2_FT_DIR);
550         dir->name2[0] = '.'; dir->name2[1] = '.';
551         PUT((uint64_t)(FETCH_LE32(gd[0].bg_inode_table) + itsz + 1) * blocksize, buf, blocksize);
552
553         // dump root dir block
554         STORE_LE(dir->inode1, EXT2_ROOT_INO);
555         STORE_LE(dir->rec_len2, 12);
556         STORE_LE(dir->inode3, EXT2_GOOD_OLD_FIRST_INO);
557         STORE_LE(dir->rec_len3, blocksize - 12 - 12);
558         STORE_LE(dir->name_len3, 10);
559         STORE_LE(dir->file_type3, EXT2_FT_DIR);
560         strcpy(dir->name3, "lost+found");
561         PUT((uint64_t)(FETCH_LE32(gd[0].bg_inode_table) + itsz + 0) * blocksize, buf, blocksize);
562
563  done:
564         // cleanup
565         if (ENABLE_FEATURE_CLEAN_UP) {
566                 free(buf);
567                 free(gd);
568                 free(sb);
569         }
570
571         xclose(fd);
572         return EXIT_SUCCESS;
573 }