diff options
-rw-r--r-- | include/applets.h | 4 | ||||
-rw-r--r-- | include/usage.h | 67 | ||||
-rw-r--r-- | util-linux/Config.in | 6 | ||||
-rw-r--r-- | util-linux/Kbuild | 1 | ||||
-rw-r--r-- | util-linux/mkfs_ext2.c | 479 |
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)) | |||
258 | IF_MICROCOM(APPLET(microcom, _BB_DIR_USR_BIN, _BB_SUID_DROP)) | 258 | IF_MICROCOM(APPLET(microcom, _BB_DIR_USR_BIN, _BB_SUID_DROP)) |
259 | IF_MKDIR(APPLET_NOFORK(mkdir, mkdir, _BB_DIR_BIN, _BB_SUID_DROP, mkdir)) | 259 | IF_MKDIR(APPLET_NOFORK(mkdir, mkdir, _BB_DIR_BIN, _BB_SUID_DROP, mkdir)) |
260 | IF_MKFS_VFAT(APPLET_ODDNAME(mkdosfs, mkfs_vfat, _BB_DIR_SBIN, _BB_SUID_DROP, mkfs_vfat)) | 260 | IF_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)) | 261 | IF_MKFS_EXT2(APPLET_ODDNAME(mke2fs, mkfs_ext2, _BB_DIR_SBIN, _BB_SUID_DROP, mkfs_ext2)) |
262 | IF_MKFIFO(APPLET(mkfifo, _BB_DIR_USR_BIN, _BB_SUID_DROP)) | 262 | IF_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)) | 263 | IF_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)) |
265 | IF_MKFS_MINIX(APPLET_ODDNAME(mkfs.minix, mkfs_minix, _BB_DIR_SBIN, _BB_SUID_DROP, mkfs_minix)) | 265 | IF_MKFS_MINIX(APPLET_ODDNAME(mkfs.minix, mkfs_minix, _BB_DIR_SBIN, _BB_SUID_DROP, mkfs_minix)) |
266 | IF_MKFS_VFAT(APPLET_ODDNAME(mkfs.vfat, mkfs_vfat, _BB_DIR_SBIN, _BB_SUID_DROP, mkfs_vfat)) | 266 | IF_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 | ||
242 | config MKFS_EXT2 | ||
243 | bool "mkfs_ext2" | ||
244 | default n | ||
245 | help | ||
246 | Utility to create EXT2 filesystems. | ||
247 | |||
242 | config GETOPT | 248 | config 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 | |||
22 | lib-$(CONFIG_IPCS) += ipcs.o | 22 | lib-$(CONFIG_IPCS) += ipcs.o |
23 | lib-$(CONFIG_LOSETUP) += losetup.o | 23 | lib-$(CONFIG_LOSETUP) += losetup.o |
24 | lib-$(CONFIG_MDEV) += mdev.o | 24 | lib-$(CONFIG_MDEV) += mdev.o |
25 | lib-$(CONFIG_MKFS_EXT2) += mkfs_ext2.o | ||
25 | lib-$(CONFIG_MKFS_MINIX) += mkfs_minix.o | 26 | lib-$(CONFIG_MKFS_MINIX) += mkfs_minix.o |
26 | lib-$(CONFIG_MKFS_VFAT) += mkfs_vfat.o | 27 | lib-$(CONFIG_MKFS_VFAT) += mkfs_vfat.o |
27 | lib-$(CONFIG_MKSWAP) += mkswap.o | 28 | lib-$(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 | ||
30 | void BUG_unsupported_field_size(void); | ||
31 | #define STORE_LE(field, value) \ | ||
32 | do { \ | ||
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 | ||
44 | struct 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 | |||
62 | static 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? | ||
71 | static ALWAYS_INLINE unsigned div_roundup(uint32_t size, uint32_t n) | ||
72 | { | ||
73 | return (size + n-1) / n; | ||
74 | } | ||
75 | |||
76 | static 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 | ||
91 | static 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 | |||
98 | static 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 | |||
105 | static 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 | ||
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, | ||
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 | if (!(option_mask32 & OPT_n)) { | ||
166 | xlseek(fd, off, SEEK_SET); | ||
167 | xwrite(fd, buf, size); | ||
168 | } | ||
169 | } | ||
170 | |||
171 | int mkfs_ext2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
172 | int 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 | } | ||