blob: 56203c789bf58e3675ac9d6c13dee4b9eb09537e [file] [log] [blame]
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +02001/* 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 "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// from e2fsprogs
19#define s_reserved_gdt_blocks s_padding1
20#define s_mkfs_time s_reserved[0]
21#define s_flags s_reserved[22]
22#define EXT2_HASH_HALF_MD4 1
23#define EXT2_FLAGS_SIGNED_HASH 0x0001
24
25// whiteout: for writable overlays
26//#define LINUX_S_IFWHT 0160000
27//#define EXT2_FEATURE_INCOMPAT_WHITEOUT 0x0020
28
29// storage helper
30void BUG_unsupported_field_size(void);
31#define STORE_LE(field, value) \
32do { \
33 if (sizeof(field) == 4) \
34 field = cpu_to_le32(value); \
35 else if (sizeof(field) == 2) \
36 field = cpu_to_le16(value); \
37 else if (sizeof(field) == 1) \
38 field = (value); \
39 else \
40 BUG_unsupported_field_size(); \
41} while (0)
42
43// All fields are little-endian
44struct ext2_dir {
45 uint32_t inode1;
46 uint16_t rec_len1;
47 uint8_t name_len1;
48 uint8_t file_type1;
49 char name1[4];
50 uint32_t inode2;
51 uint16_t rec_len2;
52 uint8_t name_len2;
53 uint8_t file_type2;
54 char name2[4];
55 uint32_t inode3;
56 uint16_t rec_len3;
57 uint8_t name_len3;
58 uint8_t file_type3;
59 char name3[12];
60};
61
Denys Vlasenko9c2708e2009-10-17 23:27:29 +020062static unsigned int_log2(unsigned arg)
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +020063{
Denys Vlasenko9c2708e2009-10-17 23:27:29 +020064 unsigned r = 0;
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +020065 while ((arg >>= 1) != 0)
66 r++;
67 return r;
68}
69
70// taken from mkfs_minix.c. libbb candidate?
Denys Vlasenko9c2708e2009-10-17 23:27:29 +020071static unsigned div_roundup(uint32_t size, uint32_t n)
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +020072{
Denys Vlasenkof9d3a912009-10-18 18:05:27 +020073 // Overflow-resistant
74 uint32_t res = size / n;
75 if (res * n != size)
76 res++;
77 return res;
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +020078}
79
80static void allocate(uint8_t *bitmap, uint32_t blocksize, uint32_t start, uint32_t end)
81{
82 uint32_t i;
83 memset(bitmap, 0, blocksize);
Denys Vlasenko77da1ca2009-10-18 16:29:30 +020084 i = start / 8;
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +020085 memset(bitmap, 0xFF, i);
86 bitmap[i] = 0xFF >> (8-(start&7));
87//bb_info_msg("ALLOC: [%u][%u][%u]: [%u]:=[%x]", blocksize, start, end, blocksize - end/8 - 1, (uint8_t)(0xFF << (8-(end&7))));
Denys Vlasenko77da1ca2009-10-18 16:29:30 +020088 i = end / 8;
89 bitmap[blocksize - i - 1] = 0xFF << (8 - (end & 7));
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +020090 memset(bitmap + blocksize - i, 0xFF, i); // N.B. no overflow here!
91}
92
Denys Vlasenko45887752009-10-17 23:13:31 +020093static uint32_t has_super(uint32_t x)
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +020094{
Denys Vlasenko77da1ca2009-10-18 16:29:30 +020095 // 0, 1 and powers of 3, 5, 7 up to 2^32 limit
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +020096 static const uint32_t supers[] = {
97 0, 1, 3, 5, 7, 9, 25, 27, 49, 81, 125, 243, 343, 625, 729,
98 2187, 2401, 3125, 6561, 15625, 16807, 19683, 59049, 78125,
99 117649, 177147, 390625, 531441, 823543, 1594323, 1953125,
100 4782969, 5764801, 9765625, 14348907, 40353607, 43046721,
101 48828125, 129140163, 244140625, 282475249, 387420489,
102 1162261467, 1220703125, 1977326743, 3486784401/* >2^31 */,
103 };
Denys Vlasenko77da1ca2009-10-18 16:29:30 +0200104 const uint32_t *sp = supers + ARRAY_SIZE(supers);
Denys Vlasenko7d8ab842009-10-17 23:23:45 +0200105 while (1) {
Denys Vlasenko77da1ca2009-10-18 16:29:30 +0200106 sp--;
Denys Vlasenko7d8ab842009-10-17 23:23:45 +0200107 if (x == *sp)
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200108 return 1;
Denys Vlasenko77da1ca2009-10-18 16:29:30 +0200109 if (x > *sp)
Denys Vlasenko7d8ab842009-10-17 23:23:45 +0200110 return 0;
Denys Vlasenko7d8ab842009-10-17 23:23:45 +0200111 }
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200112}
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200113
114/* Standard mke2fs 1.41.9:
115 * Usage: mke2fs [-c|-l filename] [-b block-size] [-f fragment-size]
116 * [-i bytes-per-inode] [-I inode-size] [-J journal-options]
117 * [-G meta group size] [-N number-of-inodes]
118 * [-m reserved-blocks-percentage] [-o creator-os]
119 * [-g blocks-per-group] [-L volume-label] [-M last-mounted-directory]
120 * [-O feature[,...]] [-r fs-revision] [-E extended-option[,...]]
121 * [-T fs-type] [-U UUID] [-jnqvFSV] device [blocks-count]
122*/
123// N.B. not commented below options are taken and silently ignored
124enum {
125 OPT_c = 1 << 0,
126 OPT_l = 1 << 1,
127 OPT_b = 1 << 2, // block size, in bytes
128 OPT_f = 1 << 3,
129 OPT_i = 1 << 4, // bytes per inode
130 OPT_I = 1 << 5,
131 OPT_J = 1 << 6,
132 OPT_G = 1 << 7,
133 OPT_N = 1 << 8,
134 OPT_m = 1 << 9, // percentage of blocks reserved for superuser
135 OPT_o = 1 << 10,
136 OPT_g = 1 << 11,
137 OPT_L = 1 << 12, // label
138 OPT_M = 1 << 13,
139 OPT_O = 1 << 14,
140 OPT_r = 1 << 15,
141 OPT_E = 1 << 16,
142 OPT_T = 1 << 17,
143 OPT_U = 1 << 18,
144 OPT_j = 1 << 19,
Denys Vlasenko77da1ca2009-10-18 16:29:30 +0200145 OPT_n = 1 << 20, // dry run: do not write anything
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200146 OPT_q = 1 << 21,
147 OPT_v = 1 << 22,
148 OPT_F = 1 << 23,
149 OPT_S = 1 << 24,
150 //OPT_V = 1 << 25, // -V version. bbox applets don't support that
151};
152
153#define fd 3 /* predefined output descriptor */
154
155static void PUT(uint64_t off, void *buf, uint32_t size)
156{
Denys Vlasenko77da1ca2009-10-18 16:29:30 +0200157// bb_info_msg("PUT[%llu]:[%u]", off, size);
158 xlseek(fd, off, SEEK_SET);
159 xwrite(fd, buf, size);
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200160}
161
162int mkfs_ext2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
163int mkfs_ext2_main(int argc UNUSED_PARAM, char **argv)
164{
165 unsigned i, pos, n;
Denys Vlasenko77da1ca2009-10-18 16:29:30 +0200166 unsigned bs, blocksize, blocksize_log2;
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200167 unsigned nreserved = 5;
Denys Vlasenkof9d3a912009-10-18 18:05:27 +0200168 unsigned long long nblocks_ull;
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200169 uint32_t nblocks;
170 uint32_t ngroups;
171 unsigned bytes_per_inode;
172 uint32_t nblocks_per_group;
173 uint32_t first_data_block;
174 uint32_t ninodes;
175 uint32_t ninodes_per_group;
176 uint32_t gdtsz, rgdtsz, itsz;
177 time_t timestamp;
178 unsigned opts;
179 const char *label;
180 struct stat st;
181 struct ext2_super_block *sb; // superblock
182 struct ext2_group_desc *gd; // group descriptors
183 struct ext2_inode *inode;
184 struct ext2_dir *dir;
185 uint8_t *buf;
186
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200187 opt_complementary = "-1:b+:m+:i+";
188 opts = getopt32(argv, "cl:b:f:i:I:J:G:N:m:o:g:L:M:O:r:E:T:U:jnqvFS",
189 NULL, &bs, NULL, &bytes_per_inode, NULL, NULL, NULL, NULL,
190 &nreserved, NULL, NULL, &label, NULL, NULL, NULL, NULL, NULL, NULL);
191 argv += optind; // argv[0] -- device
192
Denys Vlasenkof9d3a912009-10-18 18:05:27 +0200193 // reserved blocks percentage
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200194 if (nreserved > 50)
195 bb_error_msg_and_die("-%c is bad", 'm');
196
197 // check the device is a block device
198 xstat(argv[0], &st);
199 if (!S_ISBLK(st.st_mode) && !(opts & OPT_F))
200 bb_error_msg_and_die("not a block device");
201
202 // check if it is mounted
203 // N.B. what if we format a file? find_mount_point will return false negative since
204 // it is loop block device which mounted!
205 if (find_mount_point(argv[0], 0))
206 bb_error_msg_and_die("can't format mounted filesystem");
207
Denys Vlasenkof9d3a912009-10-18 18:05:27 +0200208 // open the device, get size in kbytes
209 xmove_fd(xopen3(argv[0], O_WRONLY | O_CREAT, 0666), fd);
210 if (argv[1]) {
211 nblocks_ull = xatoull(argv[1]);
212 } else {
213 nblocks_ull = (uoff_t)xlseek(fd, 0, SEEK_END) / 1024;
214 }
215
216 // block size is a multiple of 1024
217 blocksize = 1024;
218 if (nblocks_ull >= 512*1024) // mke2fs 1.41.9 compat
219 blocksize = 4096;
220 if (EXT2_MAX_BLOCK_SIZE > 4096) {
221 // nblocks_ull >> 22 == size in 4gigabyte chunks.
222 // if it is >= 16k gigs, blocksize must be increased.
223 // Try "mke2fs -F image_std $((16 * 1024*1024*1024))"
224 while ((nblocks_ull >> 22) >= blocksize)
225 blocksize *= 2;
226 }
227 if (opts & OPT_b)
228 blocksize = bs;
229 if (blocksize < EXT2_MIN_BLOCK_SIZE
230 || blocksize > EXT2_MAX_BLOCK_SIZE
231 || (blocksize & (blocksize - 1)) // not power of 2
232 ) {
233 bb_error_msg_and_die("blocksize %u is bad", blocksize);
234 }
235 blocksize_log2 = int_log2(blocksize);
236 nblocks_ull >>= (blocksize_log2 - EXT2_MIN_BLOCK_LOG_SIZE);
237 // nblocks: the total number of blocks in the filesystem
238 nblocks = nblocks_ull;
239 if (nblocks != nblocks_ull)
240 bb_error_msg_and_die("block count doesn't fit in 32 bits");
241 if (nblocks < 8)
242 bb_error_msg_and_die("need >= 8 blocks");
243
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200244 // TODO: 5?/5 WE MUST NOT DEPEND ON WHETHER DEVICE IS /dev/zero 'ed OR NOT
245 // TODO: 3/5 refuse if mounted
246 // TODO: 4/5 compat options
247 // TODO: 1/5 sanity checks
248 // TODO: 0/5 more verbose error messages
249 // TODO: 0/5 info printing
250 // TODO: 2/5 bigendianness! Spot where it comes to play! sb->, gd->
251 // TODO: 2/5 reserved GDT: how to mark but not allocate?
Denys Vlasenko77da1ca2009-10-18 16:29:30 +0200252 // TODO: 3/5 dir_index?
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200253
254 // fill the superblock
255 sb = xzalloc(blocksize);
256 sb->s_rev_level = 1; // revision 1 filesystem
257 sb->s_magic = EXT2_SUPER_MAGIC;
258 sb->s_inode_size = sizeof(*inode);
259 sb->s_first_ino = EXT2_GOOD_OLD_FIRST_INO;
Denys Vlasenko77da1ca2009-10-18 16:29:30 +0200260 sb->s_log_block_size = sb->s_log_frag_size = blocksize_log2 - EXT2_MIN_BLOCK_LOG_SIZE;
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200261 // first 1024 bytes of the device are for boot record. If block size is 1024 bytes, then
262 // the first block available for data is 1, otherwise 0
263 first_data_block = sb->s_first_data_block = (EXT2_MIN_BLOCK_SIZE == blocksize);
264 // block and inode bitmaps occupy no more than one block, so maximum number of blocks is
265 // number of bits in one block, i.e. 8*blocksize
Denys Vlasenko77da1ca2009-10-18 16:29:30 +0200266 nblocks_per_group = sb->s_blocks_per_group = sb->s_frags_per_group = sb->s_inodes_per_group = 8 * blocksize;
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200267 timestamp = time(NULL);
268 sb->s_mkfs_time = sb->s_wtime = sb->s_lastcheck = timestamp;
269 sb->s_state = 1;
270 sb->s_creator_os = EXT2_OS_LINUX;
271 sb->s_max_mnt_count = EXT2_DFL_MAX_MNT_COUNT;
272 sb->s_checkinterval = 24*60*60 * 180; // 180 days
273 sb->s_errors = EXT2_ERRORS_DEFAULT;
274 sb->s_feature_compat = EXT2_FEATURE_COMPAT_SUPP
275 | (EXT2_FEATURE_COMPAT_RESIZE_INO * ENABLE_FEATURE_MKFS_EXT2_RESERVED_GDT)
276 | (EXT2_FEATURE_COMPAT_DIR_INDEX * ENABLE_FEATURE_MKFS_EXT2_DIR_INDEX)
277 ;
278 // e2fsprogs-1.41.9 doesn't like EXT2_FEATURE_INCOMPAT_WHITEOUT
279 sb->s_feature_incompat = EXT2_FEATURE_INCOMPAT_FILETYPE;// | EXT2_FEATURE_INCOMPAT_WHITEOUT;
280 sb->s_feature_ro_compat = EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
281 sb->s_flags = EXT2_FLAGS_SIGNED_HASH * ENABLE_FEATURE_MKFS_EXT2_DIR_INDEX;
282 generate_uuid(sb->s_uuid);
Denys Vlasenko77da1ca2009-10-18 16:29:30 +0200283 if (ENABLE_FEATURE_MKFS_EXT2_DIR_INDEX) {
284 sb->s_def_hash_version = EXT2_HASH_HALF_MD4;
285 generate_uuid((uint8_t *)sb->s_hash_seed);
286 }
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200287 /*
288 * From e2fsprogs: add "jitter" to the superblock's check interval so that we
289 * don't check all the filesystems at the same time. We use a
290 * kludgy hack of using the UUID to derive a random jitter value.
291 */
Denys Vlasenko69d9edc2009-10-17 23:47:36 +0200292 sb->s_max_mnt_count += sb->s_uuid[ARRAY_SIZE(sb->s_uuid)-1] % EXT2_DFL_MAX_MNT_COUNT;
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200293
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200294 sb->s_blocks_count = nblocks;
295
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200296 // reserve blocks for superuser
297 sb->s_r_blocks_count = ((uint64_t) nblocks * nreserved) / 100;
298
299 // N.B. a block group can have no more than nblocks_per_group blocks
300 ngroups = div_roundup(nblocks - first_data_block, nblocks_per_group);
301 if (0 == ngroups)
302 bb_error_msg_and_die("ngroups");
303 gdtsz = div_roundup(ngroups, EXT2_DESC_PER_BLOCK(sb));
304 /*
305 * From e2fsprogs: Calculate the number of GDT blocks to reserve for online
306 * filesystem growth.
307 * The absolute maximum number of GDT blocks we can reserve is determined by
308 * the number of block pointers that can fit into a single block.
309 */
310 /* We set it at 1024x the current filesystem size, or
311 * the upper block count limit (2^32), whichever is lower.
312 */
313#if ENABLE_FEATURE_MKFS_EXT2_RESERVED_GDT
314 rgdtsz = 0xFFFFFFFF; // maximum block number
315 if (nblocks < rgdtsz / 1024)
316 rgdtsz = nblocks * 1024;
317 rgdtsz = div_roundup(rgdtsz - first_data_block, nblocks_per_group);
318 rgdtsz = div_roundup(rgdtsz, EXT2_DESC_PER_BLOCK(sb)) - gdtsz;
319 if (rgdtsz > EXT2_ADDR_PER_BLOCK(sb))
320 rgdtsz = EXT2_ADDR_PER_BLOCK(sb);
321 sb->s_reserved_gdt_blocks = rgdtsz;
322 //bb_info_msg("RSRVD[%u]", n);
323#else
324 rgdtsz = 0;
325#endif
326
327 // ninodes is the total number of inodes (files) in the file system
328 if (!(opts & OPT_i)) {
329 bytes_per_inode = 16384;
330 if (nblocks < 512*1024)
331 bytes_per_inode = 4096;
332 if (nblocks < 3*1024)
333 bytes_per_inode = 8192;
334 }
Denys Vlasenko77da1ca2009-10-18 16:29:30 +0200335 ninodes = nblocks / (bytes_per_inode >> blocksize_log2);
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200336 if (ninodes < EXT2_GOOD_OLD_FIRST_INO+1)
337 ninodes = EXT2_GOOD_OLD_FIRST_INO+1;
338 ninodes_per_group = div_roundup(ninodes, ngroups);
339 if (ninodes_per_group < 16)
Denys Vlasenko77da1ca2009-10-18 16:29:30 +0200340 ninodes_per_group = 16; // minimum number because the first EXT2_GOOD_OLD_FIRST_INO-1 are reserved
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200341 // N.B. a block group can have no more than 8*blocksize = sb->s_inodes_per_group inodes
342 if (ninodes_per_group > sb->s_inodes_per_group)
343 ninodes_per_group = sb->s_inodes_per_group;
344 // adjust inodes per group so they completely fill the inode table blocks in the descriptor
Denys Vlasenko77da1ca2009-10-18 16:29:30 +0200345 ninodes_per_group = (div_roundup(ninodes_per_group * EXT2_INODE_SIZE(sb), blocksize) << blocksize_log2) / EXT2_INODE_SIZE(sb);
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200346 // make sure the number of inodes per group is a multiple of 8
347 ninodes_per_group &= ~7;
348 sb->s_inodes_per_group = ninodes_per_group;// = div_roundup(ninodes_per_group * sb->s_inode_size, blocksize);
349 // total ninodes
350 ninodes = sb->s_inodes_count = ninodes_per_group * ngroups;
351
352 itsz = ninodes_per_group * sb->s_inode_size / blocksize;
353 sb->s_free_inodes_count = sb->s_inodes_count - EXT2_GOOD_OLD_FIRST_INO;
354
355 // write the label, if any
356 if (opts & OPT_L)
357 safe_strncpy((char *)sb->s_volume_name, label, sizeof(sb->s_volume_name));
358
Denys Vlasenko77da1ca2009-10-18 16:29:30 +0200359#if 1
360/* if (fs_param.s_blocks_count != s->s_blocks_count)
361 fprintf(stderr, _("warning: %u blocks unused.\n\n"),
362 fs_param.s_blocks_count - s->s_blocks_count);
363*/
364
365 printf(
366 "Filesystem label=%s\n"
367 "OS type: Linux\n"
368 "Block size=%u (log=%u)\n"
369 "Fragment size=%u (log=%u)\n"
370 "%u inodes, %u blocks\n"
371 "%u blocks (%u%%) reserved for the super user\n"
372 "First data block=%u\n"
373// "Maximum filesystem blocks=%lu\n"
374 "%u block groups\n"
375 "%u blocks per group, %u fragments per group\n"
376 "%u inodes per group"
377 , (char *)sb->s_volume_name
378 , blocksize, sb->s_log_block_size
379 , blocksize, sb->s_log_block_size
380 , sb->s_inodes_count, sb->s_blocks_count
381 , sb->s_r_blocks_count, nreserved
382 , first_data_block
383// , (rgdtsz + gdtsz) * EXT2_DESC_PER_BLOCK(sb) * nblocks_per_group
384 , ngroups
385 , nblocks_per_group, nblocks_per_group
386 , ninodes_per_group
387 );
388 {
389 const char *fmt = "\nSuperblock backups stored on blocks: %u";
390 pos = first_data_block;
391 for (i = 1; i < ngroups; i++) {
392 pos += nblocks_per_group;
393 if (has_super(i)) {
394 printf(fmt, (unsigned)pos);
395 fmt = ", %u";
396 }
397 }
398 }
399 bb_putchar('\n');
400#endif
401
402 if (opts & OPT_n)
403 return EXIT_SUCCESS;
404
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200405 // fill group descriptors
406 gd = xzalloc((gdtsz + rgdtsz) * blocksize);
407 sb->s_free_blocks_count = 0;
408 for (i = 0, pos = first_data_block, n = nblocks;
409 i < ngroups;
410 i++, pos += nblocks_per_group, n -= nblocks_per_group
411 ) {
Denys Vlasenko45887752009-10-17 23:13:31 +0200412 uint32_t overhead = pos + (has_super(i) ? (1/*sb*/ + gdtsz + rgdtsz) : 0);
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200413 gd[i].bg_block_bitmap = overhead + 0;
414 gd[i].bg_inode_bitmap = overhead + 1;
415 gd[i].bg_inode_table = overhead + 2;
416 overhead = overhead - pos + 1/*bbmp*/ + 1/*ibmp*/ + itsz;
417 gd[i].bg_free_inodes_count = ninodes_per_group;
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200418 //gd[i].bg_used_dirs_count = 0;
Denys Vlasenko77da1ca2009-10-18 16:29:30 +0200419 // N.B. both root and lost+found dirs are within the first block group, thus +2
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200420 if (0 == i) {
421 overhead += 2;
422 gd[i].bg_used_dirs_count = 2;
423 gd[i].bg_free_inodes_count -= EXT2_GOOD_OLD_FIRST_INO;
424 }
425 // N.B. the following is pure heuristics!
426 // Likely to cope with 1024-byte blocks, when first block is for boot sectors
427 if (ngroups-1 == i) {
428 overhead += first_data_block;
429 }
430 gd[i].bg_free_blocks_count = (n < nblocks_per_group ? n : nblocks_per_group) - overhead;
431 sb->s_free_blocks_count += gd[i].bg_free_blocks_count;
432 }
433 STORE_LE(sb->s_free_blocks_count, sb->s_free_blocks_count);
434
435 // dump filesystem skeleton structures
Denys Vlasenko77da1ca2009-10-18 16:29:30 +0200436// printf("Writing superblocks and filesystem accounting information: ");
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200437 buf = xmalloc(blocksize);
438 for (i = 0, pos = first_data_block; i < ngroups; i++, pos += nblocks_per_group) {
Denys Vlasenko45887752009-10-17 23:13:31 +0200439 uint32_t overhead = has_super(i) ? (1/*sb*/ + gdtsz + rgdtsz) : 0;
440 uint32_t start;// = has_super(i) ? (1/*sb*/ + gdtsz + rgdtsz) : 0;
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200441 uint32_t end;
442
443 // dump superblock and group descriptors and their backups
444 if (overhead) { // N.B. in fact, we want (has_super(i)) condition, but it is equal to (overhead != 0) and is cheaper
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200445 // N.B. 1024 byte blocks are special
Denys Vlasenko77da1ca2009-10-18 16:29:30 +0200446 PUT(((uint64_t)pos << blocksize_log2) + ((0 == i && 0 == first_data_block) ? 1024 : 0), sb, 1024);//blocksize);
447 PUT(((uint64_t)pos << blocksize_log2) + blocksize, gd, (gdtsz + rgdtsz) * blocksize);
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200448 }
449
Denys Vlasenko45887752009-10-17 23:13:31 +0200450 start = overhead + 1/*bbmp*/ + 1/*ibmp*/ + itsz;
451 if (i == 0)
Denys Vlasenko77da1ca2009-10-18 16:29:30 +0200452 start += 2; // for "/" and "/lost+found"
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200453 end = nblocks_per_group - (start + gd[i].bg_free_blocks_count);
Denys Vlasenko77da1ca2009-10-18 16:29:30 +0200454
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200455 // mark preallocated blocks as allocated
456 allocate(buf, blocksize, start, end);
457 // dump block bitmap
Denys Vlasenko77da1ca2009-10-18 16:29:30 +0200458 PUT((uint64_t)(pos + overhead) * blocksize, buf, blocksize);
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200459
460 // mark preallocated inodes as allocated
461 allocate(buf, blocksize,
462 ninodes_per_group - gd[i].bg_free_inodes_count,
Denys Vlasenko77da1ca2009-10-18 16:29:30 +0200463 8 * blocksize - ninodes_per_group
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200464 );
465 // dump inode bitmap
Denys Vlasenko77da1ca2009-10-18 16:29:30 +0200466 //PUT((uint64_t)(pos + overhead + 1) * blocksize, buf, blocksize);
467 //but it's right after block bitmap, so we can just:
468 xwrite(fd, buf, blocksize);
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200469 }
470
471 // zero boot sectors
472 memset(buf, 0, blocksize);
473 PUT(0, buf, 1024); // N.B. 1024 <= blocksize
474 // zero inode tables
475 for (i = 0; i < ngroups; ++i)
476 for (n = 0; n < itsz; ++n)
Denys Vlasenko77da1ca2009-10-18 16:29:30 +0200477 PUT((uint64_t)(gd[i].bg_inode_table + n) * blocksize, buf, blocksize);
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200478
479 // prepare directory inode
480 inode = (struct ext2_inode *)buf;
481 STORE_LE(inode->i_mode, S_IFDIR | S_IRWXU | S_IRGRP | S_IROTH | S_IXGRP | S_IXOTH);
Denys Vlasenko77da1ca2009-10-18 16:29:30 +0200482 STORE_LE(inode->i_mtime, timestamp);
483 STORE_LE(inode->i_atime, timestamp);
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200484 STORE_LE(inode->i_ctime, timestamp);
485 STORE_LE(inode->i_size, blocksize);
486 // N.B. inode->i_blocks stores the number of 512 byte data blocks. Why on Earth?!
487 STORE_LE(inode->i_blocks, blocksize / 512);
488
489 // dump root dir inode
490 STORE_LE(inode->i_links_count, 3); // "/.", "/..", "/lost+found/.." point to this inode
491 STORE_LE(inode->i_block[0], gd[0].bg_inode_table + itsz);
Denys Vlasenko77da1ca2009-10-18 16:29:30 +0200492 PUT(((uint64_t)gd[0].bg_inode_table << blocksize_log2) + (EXT2_ROOT_INO-1) * sizeof(*inode), buf, sizeof(*inode));
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200493
494 // dump lost+found dir inode
495 STORE_LE(inode->i_links_count, 2); // both "/lost+found" and "/lost+found/." point to this inode
496 STORE_LE(inode->i_block[0], inode->i_block[0]+1); // use next block //= gd[0].bg_inode_table + itsz + 1;
Denys Vlasenko77da1ca2009-10-18 16:29:30 +0200497 PUT(((uint64_t)gd[0].bg_inode_table << blocksize_log2) + (EXT2_GOOD_OLD_FIRST_INO-1) * sizeof(*inode), buf, sizeof(*inode));
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200498
499 // dump directories
500 memset(buf, 0, blocksize);
501 dir = (struct ext2_dir *)buf;
502
503 // dump lost+found dir block
504 STORE_LE(dir->inode1, EXT2_GOOD_OLD_FIRST_INO);
505 STORE_LE(dir->rec_len1, 12);
506 STORE_LE(dir->name_len1, 1);
507 STORE_LE(dir->file_type1, EXT2_FT_DIR);
508 dir->name1[0] = '.';
509 STORE_LE(dir->inode2, EXT2_ROOT_INO);
510 STORE_LE(dir->rec_len2, blocksize - 12);
511 STORE_LE(dir->name_len2, 2);
512 STORE_LE(dir->file_type2, EXT2_FT_DIR);
513 dir->name2[0] = '.'; dir->name2[1] = '.';
Denys Vlasenko77da1ca2009-10-18 16:29:30 +0200514 PUT((uint64_t)(gd[0].bg_inode_table + itsz + 1) * blocksize, buf, blocksize);
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200515
516 // dump root dir block
517 STORE_LE(dir->inode1, EXT2_ROOT_INO);
518 STORE_LE(dir->rec_len2, 12);
519 STORE_LE(dir->inode3, EXT2_GOOD_OLD_FIRST_INO);
520 STORE_LE(dir->rec_len3, blocksize - 12 - 12);
521 STORE_LE(dir->name_len3, 10);
522 STORE_LE(dir->file_type3, EXT2_FT_DIR);
523 strcpy(dir->name3, "lost+found");
Denys Vlasenko77da1ca2009-10-18 16:29:30 +0200524 PUT((uint64_t)(gd[0].bg_inode_table + itsz + 0) * blocksize, buf, blocksize);
525
526// bb_info_msg("done\n"
527// "This filesystem will be automatically checked every %u mounts or\n"
528// "%u days, whichever comes first. Use tune2fs -c or -i to override.",
529// sb->s_max_mnt_count, sb->s_checkinterval / (3600 * 24)
530// );
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200531
532 // cleanup
533 if (ENABLE_FEATURE_CLEAN_UP) {
534 free(buf);
535 free(gd);
536 free(sb);
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200537 }
538
Denys Vlasenko77da1ca2009-10-18 16:29:30 +0200539 xclose(fd);
Vladimir Dronnikov823b4e62009-10-17 21:38:19 +0200540 return EXIT_SUCCESS;
541}