aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/applets.h4
-rw-r--r--include/usage.h67
-rw-r--r--util-linux/Config.in6
-rw-r--r--util-linux/Kbuild1
-rw-r--r--util-linux/mkfs_ext2.c479
5 files changed, 524 insertions, 33 deletions
diff --git a/include/applets.h b/include/applets.h
index 9b6b54e63..9ca84623b 100644
--- a/include/applets.h
+++ b/include/applets.h
@@ -258,9 +258,9 @@ IF_MESG(APPLET(mesg, _BB_DIR_USR_BIN, _BB_SUID_DROP))
258IF_MICROCOM(APPLET(microcom, _BB_DIR_USR_BIN, _BB_SUID_DROP)) 258IF_MICROCOM(APPLET(microcom, _BB_DIR_USR_BIN, _BB_SUID_DROP))
259IF_MKDIR(APPLET_NOFORK(mkdir, mkdir, _BB_DIR_BIN, _BB_SUID_DROP, mkdir)) 259IF_MKDIR(APPLET_NOFORK(mkdir, mkdir, _BB_DIR_BIN, _BB_SUID_DROP, mkdir))
260IF_MKFS_VFAT(APPLET_ODDNAME(mkdosfs, mkfs_vfat, _BB_DIR_SBIN, _BB_SUID_DROP, mkfs_vfat)) 260IF_MKFS_VFAT(APPLET_ODDNAME(mkdosfs, mkfs_vfat, _BB_DIR_SBIN, _BB_SUID_DROP, mkfs_vfat))
261//IF_MKE2FS(APPLET(mke2fs, _BB_DIR_SBIN, _BB_SUID_DROP)) 261IF_MKFS_EXT2(APPLET_ODDNAME(mke2fs, mkfs_ext2, _BB_DIR_SBIN, _BB_SUID_DROP, mkfs_ext2))
262IF_MKFIFO(APPLET(mkfifo, _BB_DIR_USR_BIN, _BB_SUID_DROP)) 262IF_MKFIFO(APPLET(mkfifo, _BB_DIR_USR_BIN, _BB_SUID_DROP))
263//IF_MKE2FS(APPLET_ODDNAME(mkfs.ext2, mke2fs, _BB_DIR_SBIN, _BB_SUID_DROP, mkfs_ext2)) 263IF_MKFS_EXT2(APPLET_ODDNAME(mkfs.ext2, mkfs_ext2, _BB_DIR_SBIN, _BB_SUID_DROP, mkfs_ext2))
264//IF_MKE2FS(APPLET_ODDNAME(mkfs.ext3, mke2fs, _BB_DIR_SBIN, _BB_SUID_DROP, mkfs_ext3)) 264//IF_MKE2FS(APPLET_ODDNAME(mkfs.ext3, mke2fs, _BB_DIR_SBIN, _BB_SUID_DROP, mkfs_ext3))
265IF_MKFS_MINIX(APPLET_ODDNAME(mkfs.minix, mkfs_minix, _BB_DIR_SBIN, _BB_SUID_DROP, mkfs_minix)) 265IF_MKFS_MINIX(APPLET_ODDNAME(mkfs.minix, mkfs_minix, _BB_DIR_SBIN, _BB_SUID_DROP, mkfs_minix))
266IF_MKFS_VFAT(APPLET_ODDNAME(mkfs.vfat, mkfs_vfat, _BB_DIR_SBIN, _BB_SUID_DROP, mkfs_vfat)) 266IF_MKFS_VFAT(APPLET_ODDNAME(mkfs.vfat, mkfs_vfat, _BB_DIR_SBIN, _BB_SUID_DROP, mkfs_vfat))
diff --git a/include/usage.h b/include/usage.h
index 3cb05e090..0215fa5bf 100644
--- a/include/usage.h
+++ b/include/usage.h
@@ -2702,37 +2702,6 @@
2702 "/tmp/foo/bar/baz: No such file or directory\n" \ 2702 "/tmp/foo/bar/baz: No such file or directory\n" \
2703 "$ mkdir -p /tmp/foo/bar/baz\n" 2703 "$ mkdir -p /tmp/foo/bar/baz\n"
2704 2704
2705#define mke2fs_trivial_usage \
2706 "[-c|-l filename] [-b block-size] [-f fragment-size] [-g blocks-per-group] " \
2707 "[-i bytes-per-inode] [-j] [-J journal-options] [-N number-of-inodes] [-n] " \
2708 "[-m reserved-blocks-percentage] [-o creator-os] [-O feature[,...]] [-q] " \
2709 "[r fs-revision-level] [-E extended-options] [-v] [-F] [-L volume-label] " \
2710 "[-M last-mounted-directory] [-S] [-T filesystem-type] " \
2711 "device [blocks-count]"
2712#define mke2fs_full_usage "\n\n" \
2713 " -b size Block size in bytes" \
2714 "\n -c Check for bad blocks before creating" \
2715 "\n -E opts Set extended options" \
2716 "\n -f size Fragment size in bytes" \
2717 "\n -F Force (ignore sanity checks)" \
2718 "\n -g num Number of blocks in a block group" \
2719 "\n -i ratio The bytes/inode ratio" \
2720 "\n -j Create a journal (ext3)" \
2721 "\n -J opts Set journal options (size/device)" \
2722 "\n -l file Read bad blocks list from file" \
2723 "\n -L lbl Set the volume label" \
2724 "\n -m percent Percent of fs blocks to reserve for admin" \
2725 "\n -M dir Set last mounted directory" \
2726 "\n -n Don't actually create anything" \
2727 "\n -N num Number of inodes to create" \
2728 "\n -o os Set the 'creator os' field" \
2729 "\n -O features Dir_index/filetype/has_journal/journal_dev/sparse_super" \
2730 "\n -q Quiet" \
2731 "\n -r rev Set filesystem revision" \
2732 "\n -S Write superblock and group descriptors only" \
2733 "\n -T fs-type Set usage type (news/largefile/largefile4)" \
2734 "\n -v Verbose" \
2735
2736#define mkfifo_trivial_usage \ 2705#define mkfifo_trivial_usage \
2737 "[OPTIONS] name" 2706 "[OPTIONS] name"
2738#define mkfifo_full_usage "\n\n" \ 2707#define mkfifo_full_usage "\n\n" \
@@ -2743,6 +2712,42 @@
2743 "\n -Z Set security context" \ 2712 "\n -Z Set security context" \
2744 ) 2713 )
2745 2714
2715#define mkfs_ext2_trivial_usage \
2716 /* "[-c|-l filename] " */ \
2717 "[-b BLK_SIZE] " \
2718 /* "[-f fragment-size] [-g blocks-per-group] " */ \
2719 "[-i BYTES_PER_INODE] " \
2720 /* "[-j] [-J journal-options] [-N number-of-inodes] [-n] " */ \
2721 "[-m RESERVED_PERCENT] " \
2722 /* "[-o creator-os] [-O feature[,...]] [-q] " */ \
2723 /* "[r fs-revision-level] [-E extended-options] [-v] [-F] " */ \
2724 "[-L LABEL] " \
2725 /* "[-M last-mounted-directory] [-S] [-T filesystem-type] " */ \
2726 "DEVICE [BLK_COUNT]"
2727#define mkfs_ext2_full_usage "\n" \
2728 "\n -b BLK_SIZE Block size in bytes" \
2729/* "\n -c Check for bad blocks before creating" */ \
2730/* "\n -E opts Set extended options" */ \
2731/* "\n -f size Fragment size in bytes" */ \
2732/* "\n -F Force (ignore sanity checks)" */ \
2733/* "\n -g num Number of blocks in a block group" */ \
2734 "\n -i BYTES The bytes/inode ratio" \
2735/* "\n -j Create a journal (ext3)" */ \
2736/* "\n -J opts Set journal options (size/device)" */ \
2737/* "\n -l file Read bad blocks list from file" */ \
2738 "\n -L LABEL Set the volume label" \
2739 "\n -m PERCENT Percent of blocks to reserve for admin" \
2740/* "\n -M dir Set last mounted directory" */ \
2741/* "\n -n Don't actually create anything" */ \
2742/* "\n -N num Number of inodes to create" */ \
2743/* "\n -o os Set the 'creator os' field" */ \
2744/* "\n -O features Dir_index/filetype/has_journal/journal_dev/sparse_super" */ \
2745/* "\n -q Quiet" */ \
2746/* "\n -r rev Set filesystem revision" */ \
2747/* "\n -S Write superblock and group descriptors only" */ \
2748/* "\n -T fs-type Set usage type (news/largefile/largefile4)" */ \
2749/* "\n -v Verbose" */ \
2750
2746#define mkfs_minix_trivial_usage \ 2751#define mkfs_minix_trivial_usage \
2747 "[-c | -l filename] [-nXX] [-iXX] /dev/name [blocks]" 2752 "[-c | -l filename] [-nXX] [-iXX] /dev/name [blocks]"
2748#define mkfs_minix_full_usage "\n\n" \ 2753#define mkfs_minix_full_usage "\n\n" \
diff --git a/util-linux/Config.in b/util-linux/Config.in
index 7cf17575c..578d059c1 100644
--- a/util-linux/Config.in
+++ b/util-linux/Config.in
@@ -239,6 +239,12 @@ config MKFS_VFAT
239 help 239 help
240 Utility to create FAT32 filesystems. 240 Utility to create FAT32 filesystems.
241 241
242config MKFS_EXT2
243 bool "mkfs_ext2"
244 default n
245 help
246 Utility to create EXT2 filesystems.
247
242config GETOPT 248config GETOPT
243 bool "getopt" 249 bool "getopt"
244 default n 250 default n
diff --git a/util-linux/Kbuild b/util-linux/Kbuild
index eaad3319d..7befe0678 100644
--- a/util-linux/Kbuild
+++ b/util-linux/Kbuild
@@ -22,6 +22,7 @@ lib-$(CONFIG_IPCRM) += ipcrm.o
22lib-$(CONFIG_IPCS) += ipcs.o 22lib-$(CONFIG_IPCS) += ipcs.o
23lib-$(CONFIG_LOSETUP) += losetup.o 23lib-$(CONFIG_LOSETUP) += losetup.o
24lib-$(CONFIG_MDEV) += mdev.o 24lib-$(CONFIG_MDEV) += mdev.o
25lib-$(CONFIG_MKFS_EXT2) += mkfs_ext2.o
25lib-$(CONFIG_MKFS_MINIX) += mkfs_minix.o 26lib-$(CONFIG_MKFS_MINIX) += mkfs_minix.o
26lib-$(CONFIG_MKFS_VFAT) += mkfs_vfat.o 27lib-$(CONFIG_MKFS_VFAT) += mkfs_vfat.o
27lib-$(CONFIG_MKSWAP) += mkswap.o 28lib-$(CONFIG_MKSWAP) += mkswap.o
diff --git a/util-linux/mkfs_ext2.c b/util-linux/mkfs_ext2.c
new file mode 100644
index 000000000..0f5e0148f
--- /dev/null
+++ b/util-linux/mkfs_ext2.c
@@ -0,0 +1,479 @@
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 "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
62static inline int int_log2(int arg)
63{
64 int r = 0;
65 while ((arg >>= 1) != 0)
66 r++;
67 return r;
68}
69
70// taken from mkfs_minix.c. libbb candidate?
71static ALWAYS_INLINE unsigned div_roundup(uint32_t size, uint32_t n)
72{
73 return (size + n-1) / n;
74}
75
76static void allocate(uint8_t *bitmap, uint32_t blocksize, uint32_t start, uint32_t end)
77{
78 uint32_t i;
79 memset(bitmap, 0, blocksize);
80 i = start/8;
81 memset(bitmap, 0xFF, i);
82 bitmap[i] = 0xFF >> (8-(start&7));
83//bb_info_msg("ALLOC: [%u][%u][%u]: [%u]:=[%x]", blocksize, start, end, blocksize - end/8 - 1, (uint8_t)(0xFF << (8-(end&7))));
84 i = end/8;
85 bitmap[blocksize - i - 1] = 0xFF << (8-(end&7));
86 memset(bitmap + blocksize - i, 0xFF, i); // N.B. no overflow here!
87}
88
89#if 0
90// TODO: get rid of FPU
91static bool is_power_of(uint32_t x, uint16_t n)
92{
93// return (!(x % n) && is_power_of(x / n, n));
94 double z = logf(x)/logf(n);
95 return (z == (int)z);
96}
97
98static bool has_super(uint32_t x)
99{
100 return (0 == x || 1 == x || is_power_of(x, 3) || is_power_of(x, 5) || is_power_of(x, 7));
101}
102
103#else
104
105static bool has_super(uint32_t x)
106{
107 static const uint32_t supers[] = {
108 0, 1, 3, 5, 7, 9, 25, 27, 49, 81, 125, 243, 343, 625, 729,
109 2187, 2401, 3125, 6561, 15625, 16807, 19683, 59049, 78125,
110 117649, 177147, 390625, 531441, 823543, 1594323, 1953125,
111 4782969, 5764801, 9765625, 14348907, 40353607, 43046721,
112 48828125, 129140163, 244140625, 282475249, 387420489,
113 1162261467, 1220703125, 1977326743, 3486784401/* >2^31 */,
114 };
115 for (int i = sizeof(supers)/sizeof(supers[0]); --i >= 0; )
116 if (x == supers[i])
117 return 1;
118 return 0;
119}
120#endif
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
132enum {
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,
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
163static void PUT(uint64_t off, void *buf, uint32_t size)
164{
165 if (!(option_mask32 & OPT_n)) {
166 xlseek(fd, off, SEEK_SET);
167 xwrite(fd, buf, size);
168 }
169}
170
171int mkfs_ext2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
172int mkfs_ext2_main(int argc UNUSED_PARAM, char **argv)
173{
174 unsigned i, pos, n;
175 unsigned bs, blocksize;
176 unsigned nreserved = 5;
177 uint32_t nblocks;
178 uint32_t ngroups;
179 unsigned bytes_per_inode;
180 uint32_t nblocks_per_group;
181 uint32_t first_data_block;
182 uint32_t ninodes;
183 uint32_t ninodes_per_group;
184 uint32_t gdtsz, rgdtsz, itsz;
185 time_t timestamp;
186 unsigned opts;
187 const char *label;
188 struct stat st;
189 struct ext2_super_block *sb; // superblock
190 struct ext2_group_desc *gd; // group descriptors
191 struct ext2_inode *inode;
192 struct ext2_dir *dir;
193 uint8_t *buf;
194
195 bs = EXT2_MIN_BLOCK_SIZE;
196 opt_complementary = "-1:b+:m+:i+";
197 opts = getopt32(argv, "cl:b:f:i:I:J:G:N:m:o:g:L:M:O:r:E:T:U:jnqvFS",
198 NULL, &bs, NULL, &bytes_per_inode, NULL, NULL, NULL, NULL,
199 &nreserved, NULL, NULL, &label, NULL, NULL, NULL, NULL, NULL, NULL);
200 argv += optind; // argv[0] -- device
201
202 // block size minimax, block size is a multiple of minimum
203 blocksize = bs;
204 if (blocksize < EXT2_MIN_BLOCK_SIZE
205 || blocksize > EXT2_MAX_BLOCK_SIZE
206 || (blocksize & (blocksize - 1)) // not power of 2
207 ) {
208 bb_error_msg_and_die("-%c is bad", 'b');
209 }
210
211 // reserved blocks count
212 if (nreserved > 50)
213 bb_error_msg_and_die("-%c is bad", 'm');
214
215 // check the device is a block device
216 xstat(argv[0], &st);
217 if (!S_ISBLK(st.st_mode) && !(opts & OPT_F))
218 bb_error_msg_and_die("not a block device");
219
220 // check if it is mounted
221 // N.B. what if we format a file? find_mount_point will return false negative since
222 // it is loop block device which mounted!
223 if (find_mount_point(argv[0], 0))
224 bb_error_msg_and_die("can't format mounted filesystem");
225
226 // TODO: 5?/5 WE MUST NOT DEPEND ON WHETHER DEVICE IS /dev/zero 'ed OR NOT
227 // TODO: 3/5 refuse if mounted
228 // TODO: 4/5 compat options
229 // TODO: 1/5 sanity checks
230 // TODO: 0/5 more verbose error messages
231 // TODO: 0/5 info printing
232 // TODO: 2/5 bigendianness! Spot where it comes to play! sb->, gd->
233 // TODO: 2/5 reserved GDT: how to mark but not allocate?
234 // TODO: 0/5 dir_index?
235
236 // fill the superblock
237 sb = xzalloc(blocksize);
238 sb->s_rev_level = 1; // revision 1 filesystem
239 sb->s_magic = EXT2_SUPER_MAGIC;
240 sb->s_inode_size = sizeof(*inode);
241 sb->s_first_ino = EXT2_GOOD_OLD_FIRST_INO;
242 sb->s_log_block_size = sb->s_log_frag_size = int_log2(blocksize >> EXT2_MIN_BLOCK_LOG_SIZE);
243 // first 1024 bytes of the device are for boot record. If block size is 1024 bytes, then
244 // the first block available for data is 1, otherwise 0
245 first_data_block = sb->s_first_data_block = (EXT2_MIN_BLOCK_SIZE == blocksize);
246 // block and inode bitmaps occupy no more than one block, so maximum number of blocks is
247 // number of bits in one block, i.e. 8*blocksize
248 nblocks_per_group = sb->s_blocks_per_group = sb->s_frags_per_group = sb->s_inodes_per_group = 8*blocksize;
249 timestamp = time(NULL);
250 sb->s_mkfs_time = sb->s_wtime = sb->s_lastcheck = timestamp;
251 sb->s_state = 1;
252 sb->s_creator_os = EXT2_OS_LINUX;
253 sb->s_max_mnt_count = EXT2_DFL_MAX_MNT_COUNT;
254 sb->s_checkinterval = 24*60*60 * 180; // 180 days
255 sb->s_errors = EXT2_ERRORS_DEFAULT;
256 sb->s_feature_compat = EXT2_FEATURE_COMPAT_SUPP
257 | (EXT2_FEATURE_COMPAT_RESIZE_INO * ENABLE_FEATURE_MKFS_EXT2_RESERVED_GDT)
258 | (EXT2_FEATURE_COMPAT_DIR_INDEX * ENABLE_FEATURE_MKFS_EXT2_DIR_INDEX)
259 ;
260 // e2fsprogs-1.41.9 doesn't like EXT2_FEATURE_INCOMPAT_WHITEOUT
261 sb->s_feature_incompat = EXT2_FEATURE_INCOMPAT_FILETYPE;// | EXT2_FEATURE_INCOMPAT_WHITEOUT;
262 sb->s_feature_ro_compat = EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
263 sb->s_flags = EXT2_FLAGS_SIGNED_HASH * ENABLE_FEATURE_MKFS_EXT2_DIR_INDEX;
264 generate_uuid(sb->s_uuid);
265#if ENABLE_FEATURE_MKFS_EXT2_DIR_INDEX
266 sb->s_def_hash_version = EXT2_HASH_HALF_MD4;
267 generate_uuid((uint8_t *)sb->s_hash_seed);
268#endif
269 /*
270 * From e2fsprogs: add "jitter" to the superblock's check interval so that we
271 * don't check all the filesystems at the same time. We use a
272 * kludgy hack of using the UUID to derive a random jitter value.
273 */
274 for (i = 0, n = 0; i < sizeof(sb->s_uuid); i++)
275 n += sb->s_uuid[i];
276 sb->s_max_mnt_count += n % EXT2_DFL_MAX_MNT_COUNT;
277
278 // open the device, get number of blocks
279 xmove_fd(xopen3(argv[0], O_WRONLY | O_CREAT, 0666), fd);
280 if (argv[1]) {
281 nblocks = xatou(argv[1]);
282 } else {
283 nblocks = ((uoff_t)xlseek(fd, 0, SEEK_END)) / blocksize;
284 xlseek(fd, 0, SEEK_SET);
285 }
286 sb->s_blocks_count = nblocks;
287
288 // nblocks is the total number of blocks in the filesystem
289 if (nblocks < 8)
290 bb_error_msg_and_die("nblocks");
291 // reserve blocks for superuser
292 sb->s_r_blocks_count = ((uint64_t) nblocks * nreserved) / 100;
293
294 // N.B. a block group can have no more than nblocks_per_group blocks
295 ngroups = div_roundup(nblocks - first_data_block, nblocks_per_group);
296 if (0 == ngroups)
297 bb_error_msg_and_die("ngroups");
298 gdtsz = div_roundup(ngroups, EXT2_DESC_PER_BLOCK(sb));
299 /*
300 * From e2fsprogs: Calculate the number of GDT blocks to reserve for online
301 * filesystem growth.
302 * The absolute maximum number of GDT blocks we can reserve is determined by
303 * the number of block pointers that can fit into a single block.
304 */
305 /* We set it at 1024x the current filesystem size, or
306 * the upper block count limit (2^32), whichever is lower.
307 */
308#if ENABLE_FEATURE_MKFS_EXT2_RESERVED_GDT
309 rgdtsz = 0xFFFFFFFF; // maximum block number
310 if (nblocks < rgdtsz / 1024)
311 rgdtsz = nblocks * 1024;
312 rgdtsz = div_roundup(rgdtsz - first_data_block, nblocks_per_group);
313 rgdtsz = div_roundup(rgdtsz, EXT2_DESC_PER_BLOCK(sb)) - gdtsz;
314 if (rgdtsz > EXT2_ADDR_PER_BLOCK(sb))
315 rgdtsz = EXT2_ADDR_PER_BLOCK(sb);
316 sb->s_reserved_gdt_blocks = rgdtsz;
317 //bb_info_msg("RSRVD[%u]", n);
318#else
319 rgdtsz = 0;
320#endif
321
322 // ninodes is the total number of inodes (files) in the file system
323 if (!(opts & OPT_i)) {
324 bytes_per_inode = 16384;
325 if (nblocks < 512*1024)
326 bytes_per_inode = 4096;
327 if (nblocks < 3*1024)
328 bytes_per_inode = 8192;
329 }
330 ninodes = nblocks / (bytes_per_inode / blocksize);
331 if (ninodes < EXT2_GOOD_OLD_FIRST_INO+1)
332 ninodes = EXT2_GOOD_OLD_FIRST_INO+1;
333 ninodes_per_group = div_roundup(ninodes, ngroups);
334 if (ninodes_per_group < 16)
335 ninodes_per_group = 16; // minimum number because the first 10 are reserved
336 // N.B. a block group can have no more than 8*blocksize = sb->s_inodes_per_group inodes
337 if (ninodes_per_group > sb->s_inodes_per_group)
338 ninodes_per_group = sb->s_inodes_per_group;
339 // adjust inodes per group so they completely fill the inode table blocks in the descriptor
340 ninodes_per_group = ((div_roundup(ninodes_per_group * EXT2_INODE_SIZE(sb), blocksize) * blocksize) / EXT2_INODE_SIZE(sb));
341 // make sure the number of inodes per group is a multiple of 8
342 ninodes_per_group &= ~7;
343 sb->s_inodes_per_group = ninodes_per_group;// = div_roundup(ninodes_per_group * sb->s_inode_size, blocksize);
344 // total ninodes
345 ninodes = sb->s_inodes_count = ninodes_per_group * ngroups;
346
347 itsz = ninodes_per_group * sb->s_inode_size / blocksize;
348 sb->s_free_inodes_count = sb->s_inodes_count - EXT2_GOOD_OLD_FIRST_INO;
349
350 // write the label, if any
351 if (opts & OPT_L)
352 safe_strncpy((char *)sb->s_volume_name, label, sizeof(sb->s_volume_name));
353
354 // fill group descriptors
355 gd = xzalloc((gdtsz + rgdtsz) * blocksize);
356 sb->s_free_blocks_count = 0;
357 for (i = 0, pos = first_data_block, n = nblocks;
358 i < ngroups;
359 i++, pos += nblocks_per_group, n -= nblocks_per_group
360 ) {
361 uint32_t overhead = pos + has_super(i) * (1/*sb*/ + gdtsz + rgdtsz);
362 gd[i].bg_block_bitmap = overhead + 0;
363 gd[i].bg_inode_bitmap = overhead + 1;
364 gd[i].bg_inode_table = overhead + 2;
365 overhead = overhead - pos + 1/*bbmp*/ + 1/*ibmp*/ + itsz;
366 gd[i].bg_free_inodes_count = ninodes_per_group;
367 // N.B. both root and lost+found dirs are within the first block group, thus +2
368 //gd[i].bg_used_dirs_count = 0;
369 if (0 == i) {
370 overhead += 2;
371 gd[i].bg_used_dirs_count = 2;
372 gd[i].bg_free_inodes_count -= EXT2_GOOD_OLD_FIRST_INO;
373 }
374 // N.B. the following is pure heuristics!
375 // Likely to cope with 1024-byte blocks, when first block is for boot sectors
376 if (ngroups-1 == i) {
377 overhead += first_data_block;
378 }
379 gd[i].bg_free_blocks_count = (n < nblocks_per_group ? n : nblocks_per_group) - overhead;
380 sb->s_free_blocks_count += gd[i].bg_free_blocks_count;
381 }
382 STORE_LE(sb->s_free_blocks_count, sb->s_free_blocks_count);
383
384 // dump filesystem skeleton structures
385 buf = xmalloc(blocksize);
386 for (i = 0, pos = first_data_block; i < ngroups; i++, pos += nblocks_per_group) {
387 uint32_t overhead = has_super(i) * (1/*sb*/ + gdtsz + rgdtsz);
388 uint32_t start;// = has_super(i) * (1/*sb*/ + gdtsz + rgdtsz);
389 uint32_t end;
390
391 // dump superblock and group descriptors and their backups
392 if (overhead) { // N.B. in fact, we want (has_super(i)) condition, but it is equal to (overhead != 0) and is cheaper
393//bb_info_msg("SUPER@[%d]", pos);
394 // N.B. 1024 byte blocks are special
395 PUT(blocksize * pos + 1024 * (0 == i && 0 == first_data_block), sb, blocksize);
396 PUT(blocksize * pos + blocksize, gd, (gdtsz + rgdtsz) * blocksize);
397 }
398
399 start = overhead + 1/*bbmp*/ + 1/*ibmp*/ + itsz + (0 == i) * 2; // +2: /, /lost+found
400 end = nblocks_per_group - (start + gd[i].bg_free_blocks_count);
401 // mark preallocated blocks as allocated
402 allocate(buf, blocksize, start, end);
403 // dump block bitmap
404 PUT((pos + overhead) * blocksize, buf, blocksize);
405
406 // mark preallocated inodes as allocated
407 allocate(buf, blocksize,
408 ninodes_per_group - gd[i].bg_free_inodes_count,
409 8*blocksize - ninodes_per_group
410 );
411 // dump inode bitmap
412 PUT((pos + overhead + 1) * blocksize, buf, blocksize);
413
414 }
415
416 // zero boot sectors
417 memset(buf, 0, blocksize);
418 PUT(0, buf, 1024); // N.B. 1024 <= blocksize
419 // zero inode tables
420 for (i = 0; i < ngroups; ++i)
421 for (n = 0; n < itsz; ++n)
422 PUT((gd[i].bg_inode_table + n) * blocksize, buf, blocksize);
423
424 // prepare directory inode
425 inode = (struct ext2_inode *)buf;
426 STORE_LE(inode->i_mode, S_IFDIR | S_IRWXU | S_IRGRP | S_IROTH | S_IXGRP | S_IXOTH);
427 inode->i_mtime = inode->i_atime = timestamp;
428 STORE_LE(inode->i_ctime, timestamp);
429 STORE_LE(inode->i_size, blocksize);
430 // N.B. inode->i_blocks stores the number of 512 byte data blocks. Why on Earth?!
431 STORE_LE(inode->i_blocks, blocksize / 512);
432
433 // dump root dir inode
434 STORE_LE(inode->i_links_count, 3); // "/.", "/..", "/lost+found/.." point to this inode
435 STORE_LE(inode->i_block[0], gd[0].bg_inode_table + itsz);
436 PUT(gd[0].bg_inode_table * blocksize + (EXT2_ROOT_INO-1) * sizeof(*inode), buf, sizeof(*inode));
437
438 // dump lost+found dir inode
439 STORE_LE(inode->i_links_count, 2); // both "/lost+found" and "/lost+found/." point to this inode
440 STORE_LE(inode->i_block[0], inode->i_block[0]+1); // use next block //= gd[0].bg_inode_table + itsz + 1;
441 PUT(gd[0].bg_inode_table * blocksize + (EXT2_GOOD_OLD_FIRST_INO-1) * sizeof(*inode), buf, sizeof(*inode));
442
443 // dump directories
444 memset(buf, 0, blocksize);
445 dir = (struct ext2_dir *)buf;
446
447 // dump lost+found dir block
448 STORE_LE(dir->inode1, EXT2_GOOD_OLD_FIRST_INO);
449 STORE_LE(dir->rec_len1, 12);
450 STORE_LE(dir->name_len1, 1);
451 STORE_LE(dir->file_type1, EXT2_FT_DIR);
452 dir->name1[0] = '.';
453 STORE_LE(dir->inode2, EXT2_ROOT_INO);
454 STORE_LE(dir->rec_len2, blocksize - 12);
455 STORE_LE(dir->name_len2, 2);
456 STORE_LE(dir->file_type2, EXT2_FT_DIR);
457 dir->name2[0] = '.'; dir->name2[1] = '.';
458 PUT((gd[0].bg_inode_table + itsz + 1) * blocksize, buf, blocksize);
459
460 // dump root dir block
461 STORE_LE(dir->inode1, EXT2_ROOT_INO);
462 STORE_LE(dir->rec_len2, 12);
463 STORE_LE(dir->inode3, EXT2_GOOD_OLD_FIRST_INO);
464 STORE_LE(dir->rec_len3, blocksize - 12 - 12);
465 STORE_LE(dir->name_len3, 10);
466 STORE_LE(dir->file_type3, EXT2_FT_DIR);
467 strcpy(dir->name3, "lost+found");
468 PUT((gd[0].bg_inode_table + itsz + 0) * blocksize, buf, blocksize);
469
470 // cleanup
471 if (ENABLE_FEATURE_CLEAN_UP) {
472 free(buf);
473 free(gd);
474 free(sb);
475 close(fd);
476 }
477
478 return EXIT_SUCCESS;
479}