1 |
|
|
/* $OpenBSD: mke2fs.c,v 1.16 2016/03/14 20:30:34 natano Exp $ */ |
2 |
|
|
/* $NetBSD: mke2fs.c,v 1.13 2009/10/19 18:41:08 bouyer Exp $ */ |
3 |
|
|
|
4 |
|
|
/*- |
5 |
|
|
* Copyright (c) 2007 Izumi Tsutsui. All rights reserved. |
6 |
|
|
* |
7 |
|
|
* Redistribution and use in source and binary forms, with or without |
8 |
|
|
* modification, are permitted provided that the following conditions |
9 |
|
|
* are met: |
10 |
|
|
* 1. Redistributions of source code must retain the above copyright |
11 |
|
|
* notice, this list of conditions and the following disclaimer. |
12 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
13 |
|
|
* notice, this list of conditions and the following disclaimer in the |
14 |
|
|
* documentation and/or other materials provided with the distribution. |
15 |
|
|
* |
16 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
17 |
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
18 |
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
19 |
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
20 |
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
21 |
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
22 |
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
23 |
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
24 |
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
25 |
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
26 |
|
|
*/ |
27 |
|
|
|
28 |
|
|
/* |
29 |
|
|
* Copyright (c) 1980, 1989, 1993 |
30 |
|
|
* The Regents of the University of California. All rights reserved. |
31 |
|
|
* |
32 |
|
|
* Redistribution and use in source and binary forms, with or without |
33 |
|
|
* modification, are permitted provided that the following conditions |
34 |
|
|
* are met: |
35 |
|
|
* 1. Redistributions of source code must retain the above copyright |
36 |
|
|
* notice, this list of conditions and the following disclaimer. |
37 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
38 |
|
|
* notice, this list of conditions and the following disclaimer in the |
39 |
|
|
* documentation and/or other materials provided with the distribution. |
40 |
|
|
* 3. Neither the name of the University nor the names of its contributors |
41 |
|
|
* may be used to endorse or promote products derived from this software |
42 |
|
|
* without specific prior written permission. |
43 |
|
|
* |
44 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
45 |
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
46 |
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
47 |
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
48 |
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
49 |
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
50 |
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
51 |
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
52 |
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
53 |
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
54 |
|
|
* SUCH DAMAGE. |
55 |
|
|
*/ |
56 |
|
|
|
57 |
|
|
/* |
58 |
|
|
* Copyright (c) 1997 Manuel Bouyer. |
59 |
|
|
* |
60 |
|
|
* Redistribution and use in source and binary forms, with or without |
61 |
|
|
* modification, are permitted provided that the following conditions |
62 |
|
|
* are met: |
63 |
|
|
* 1. Redistributions of source code must retain the above copyright |
64 |
|
|
* notice, this list of conditions and the following disclaimer. |
65 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
66 |
|
|
* notice, this list of conditions and the following disclaimer in the |
67 |
|
|
* documentation and/or other materials provided with the distribution. |
68 |
|
|
* |
69 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
70 |
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
71 |
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
72 |
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
73 |
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
74 |
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
75 |
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
76 |
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
77 |
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
78 |
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
79 |
|
|
*/ |
80 |
|
|
|
81 |
|
|
/* |
82 |
|
|
* mke2fs.c: "re-invent (dumb but non-GPLed) wheel as a fun project" |
83 |
|
|
* |
84 |
|
|
* In spite of this name, there is no piece of code |
85 |
|
|
* derived from GPLed e2fsprogs written for Linux. |
86 |
|
|
* I referred them only to see how each structure |
87 |
|
|
* member should be initialized. |
88 |
|
|
* |
89 |
|
|
* Reference: |
90 |
|
|
* - All NetBSD sources under src/sys/ufs/ext2fs and src/sbin/fsck_ext2fs |
91 |
|
|
* - Ext2fs Home Page |
92 |
|
|
* http://e2fsprogs.sourceforge.net/ext2.html |
93 |
|
|
* - Design and Implementation of the Second Extended Filesystem |
94 |
|
|
* http://e2fsprogs.sourceforge.net/ext2intro.html |
95 |
|
|
* - Linux Documentation "The Second Extended Filesystem" |
96 |
|
|
* src/linux/Documentation/filesystems/ext2.txt |
97 |
|
|
* in the Linux kernel distribution |
98 |
|
|
*/ |
99 |
|
|
|
100 |
|
|
#include <sys/param.h> /* MAXBSIZE powerof2 roundup setbit isset MIN */ |
101 |
|
|
#include <sys/types.h> |
102 |
|
|
#include <sys/mman.h> |
103 |
|
|
#include <sys/time.h> |
104 |
|
|
#include <ufs/ext2fs/ext2fs_dinode.h> |
105 |
|
|
#include <ufs/ext2fs/ext2fs_dir.h> |
106 |
|
|
#include <ufs/ext2fs/ext2fs.h> |
107 |
|
|
#include <sys/ioctl.h> |
108 |
|
|
|
109 |
|
|
#include <err.h> |
110 |
|
|
#include <errno.h> |
111 |
|
|
#include <inttypes.h> |
112 |
|
|
#include <string.h> |
113 |
|
|
#include <unistd.h> |
114 |
|
|
#include <stdint.h> |
115 |
|
|
#include <stdlib.h> |
116 |
|
|
#include <stddef.h> |
117 |
|
|
#include <stdio.h> |
118 |
|
|
|
119 |
|
|
#include "extern.h" |
120 |
|
|
|
121 |
|
|
static void initcg(uint); |
122 |
|
|
static void zap_old_sblock(daddr32_t); |
123 |
|
|
static uint cgoverhead(uint); |
124 |
|
|
static int fsinit(const struct timeval *); |
125 |
|
|
static int makedir(struct ext2fs_direct *, int); |
126 |
|
|
static void copy_dir(struct ext2fs_direct *, struct ext2fs_direct *); |
127 |
|
|
static void init_resizeino(const struct timeval *); |
128 |
|
|
static uint32_t alloc(uint32_t, uint16_t); |
129 |
|
|
static void iput(struct ext2fs_dinode *, ino_t); |
130 |
|
|
static void rdfs(daddr32_t, int, void *); |
131 |
|
|
static void wtfs(daddr32_t, int, void *); |
132 |
|
|
static int ilog2(uint); |
133 |
|
|
static int skpc(int, size_t, uint8_t *); |
134 |
|
|
static void uuid_get(struct m_ext2fs *); |
135 |
|
|
|
136 |
|
|
/* XXX: some of these macro should be into <ufs/ext2fs/ext2fs.h>? */ |
137 |
|
|
#define EXT2_DEF_MAX_MNT_COUNT 20 |
138 |
|
|
#define EXT2_DEF_FSCKINTV (180 * 24 * 60 * 60) /* 180 days */ |
139 |
|
|
#define EXT2_RESERVED_INODES (EXT2_FIRSTINO - 1) |
140 |
|
|
#define EXT2_UMASK 0755 |
141 |
|
|
|
142 |
|
|
#define EXT2_INO_INDEX(ino) ((ino) - 1) /* no inode zero */ |
143 |
|
|
|
144 |
|
|
#define EXT2_LOSTFOUNDSIZE 16384 |
145 |
|
|
#define EXT2_LOSTFOUNDINO EXT2_FIRSTINO /* XXX: not quite */ |
146 |
|
|
#define EXT2_LOSTFOUNDUMASK 0700 |
147 |
|
|
|
148 |
|
|
#define EXT2_RESIZEINOUMASK 0600 |
149 |
|
|
|
150 |
|
|
#define NBLOCK_SUPERBLOCK 1 |
151 |
|
|
#define NBLOCK_BLOCK_BITMAP 1 |
152 |
|
|
#define NBLOCK_INODE_BITMAP 1 |
153 |
|
|
|
154 |
|
|
#define cgbase(fs, c) \ |
155 |
|
|
((fs)->e2fs.e2fs_first_dblock + (fs)->e2fs.e2fs_bpg * (c)) |
156 |
|
|
|
157 |
|
|
#define rounddown(x,y) (((x)/(y))*(y)) |
158 |
|
|
|
159 |
|
|
/* |
160 |
|
|
* ext2fs super block and group descriptor structures |
161 |
|
|
* |
162 |
|
|
* We don't have to use or setup whole in-memory m_ext2fs structure, |
163 |
|
|
* but prepare it to use several macro defined in kernel headers. |
164 |
|
|
*/ |
165 |
|
|
union { |
166 |
|
|
struct m_ext2fs m_ext2fs; |
167 |
|
|
char pad[SBSIZE]; |
168 |
|
|
} ext2fsun; |
169 |
|
|
#define sblock ext2fsun.m_ext2fs |
170 |
|
|
#define gd ext2fsun.m_ext2fs.e2fs_gd |
171 |
|
|
|
172 |
|
|
static uint8_t *iobuf; /* for superblock and group descriptors */ |
173 |
|
|
static int iobufsize; |
174 |
|
|
|
175 |
|
|
static uint8_t buf[MAXBSIZE]; /* for initcg() and makedir() ops */ |
176 |
|
|
|
177 |
|
|
static int fd; |
178 |
|
|
|
179 |
|
|
extern int max_cols; |
180 |
|
|
|
181 |
|
|
void |
182 |
|
|
mke2fs(const char *fsys, int f) |
183 |
|
|
{ |
184 |
|
|
struct timeval tv; |
185 |
|
|
int64_t minfssize; |
186 |
|
|
uint bcount, fbcount, ficount; |
187 |
|
|
uint blocks_gd, blocks_per_cg, inodes_per_cg, iblocks_per_cg; |
188 |
|
|
uint minblocks_per_cg, blocks_lastcg; |
189 |
|
|
uint ncg, cylno, sboff; |
190 |
|
|
int i, len, col, delta, fld_width; |
191 |
|
|
|
192 |
|
|
gettimeofday(&tv, NULL); |
193 |
|
|
fd = f; |
194 |
|
|
|
195 |
|
|
/* |
196 |
|
|
* collect and verify the block and fragment sizes |
197 |
|
|
*/ |
198 |
|
|
if (!powerof2(bsize)) { |
199 |
|
|
errx(EXIT_FAILURE, |
200 |
|
|
"block size must be a power of 2, not %u\n", |
201 |
|
|
bsize); |
202 |
|
|
} |
203 |
|
|
if (!powerof2(fsize)) { |
204 |
|
|
errx(EXIT_FAILURE, |
205 |
|
|
"fragment size must be a power of 2, not %u\n", |
206 |
|
|
fsize); |
207 |
|
|
} |
208 |
|
|
if (fsize < sectorsize) { |
209 |
|
|
errx(EXIT_FAILURE, |
210 |
|
|
"fragment size %u is too small, minimum is %u\n", |
211 |
|
|
fsize, sectorsize); |
212 |
|
|
} |
213 |
|
|
if (bsize < MINBSIZE) { |
214 |
|
|
errx(EXIT_FAILURE, |
215 |
|
|
"block size %u is too small, minimum is %u\n", |
216 |
|
|
bsize, MINBSIZE); |
217 |
|
|
} |
218 |
|
|
if (bsize > EXT2_MAXBSIZE) { |
219 |
|
|
errx(EXIT_FAILURE, |
220 |
|
|
"block size %u is too large, maximum is %u\n", |
221 |
|
|
bsize, MAXBSIZE); |
222 |
|
|
} |
223 |
|
|
if (bsize != fsize) { |
224 |
|
|
/* |
225 |
|
|
* There is no fragment support on current ext2fs (yet?), |
226 |
|
|
* but some kernel code refers fsize or fpg as bsize or bpg |
227 |
|
|
* and Linux seems to set the same values to them. |
228 |
|
|
*/ |
229 |
|
|
errx(EXIT_FAILURE, |
230 |
|
|
"block size (%u) can't be different from " |
231 |
|
|
"fragment size (%u)\n", |
232 |
|
|
bsize, fsize); |
233 |
|
|
} |
234 |
|
|
|
235 |
|
|
/* variable inodesize is REV1 feature */ |
236 |
|
|
if (Oflag == 0 && inodesize != EXT2_REV0_DINODE_SIZE) { |
237 |
|
|
errx(EXIT_FAILURE, "GOOD_OLD_REV file system format" |
238 |
|
|
" doesn't support %d byte inode\n", inodesize); |
239 |
|
|
} |
240 |
|
|
|
241 |
|
|
sblock.e2fs.e2fs_log_bsize = ilog2(bsize) - LOG_MINBSIZE; |
242 |
|
|
sblock.e2fs.e2fs_log_fsize = ilog2(fsize) - LOG_MINFSIZE; |
243 |
|
|
|
244 |
|
|
sblock.e2fs_bsize = bsize; |
245 |
|
|
sblock.e2fs_fsize = fsize; |
246 |
|
|
sblock.e2fs_bshift = sblock.e2fs.e2fs_log_bsize + LOG_MINBSIZE; |
247 |
|
|
sblock.e2fs_qbmask = sblock.e2fs_bsize - 1; |
248 |
|
|
sblock.e2fs_bmask = ~sblock.e2fs_qbmask; |
249 |
|
|
sblock.e2fs_fsbtodb = ilog2(sblock.e2fs_bsize) - ilog2(sectorsize); |
250 |
|
|
sblock.e2fs_ipb = sblock.e2fs_bsize / inodesize; |
251 |
|
|
|
252 |
|
|
/* |
253 |
|
|
* Ext2fs preserves BBSIZE (1024 bytes) space at the top for |
254 |
|
|
* bootloader (though it is not enough at all for our bootloader). |
255 |
|
|
* If bsize == BBSIZE we have to preserve one block. |
256 |
|
|
* If bsize > BBSIZE, the first block already contains BBSIZE space |
257 |
|
|
* before superblock because superblock is allocated at SBOFF and |
258 |
|
|
* bsize is a power of two (i.e. 2048 bytes or more). |
259 |
|
|
*/ |
260 |
|
|
sblock.e2fs.e2fs_first_dblock = (sblock.e2fs_bsize > BBSIZE) ? 0 : 1; |
261 |
|
|
minfssize = fsbtodb(&sblock, |
262 |
|
|
sblock.e2fs.e2fs_first_dblock + |
263 |
|
|
NBLOCK_SUPERBLOCK + |
264 |
|
|
1 /* at least one group descriptor */ + |
265 |
|
|
NBLOCK_BLOCK_BITMAP + |
266 |
|
|
NBLOCK_INODE_BITMAP + |
267 |
|
|
1 /* at least one inode table block */ + |
268 |
|
|
1 /* at least one data block for rootdir */ + |
269 |
|
|
1 /* at least one data block for data */ |
270 |
|
|
); /* XXX and more? */ |
271 |
|
|
|
272 |
|
|
if (fssize < minfssize) |
273 |
|
|
errx(EXIT_FAILURE, "Filesystem size %" PRId64 |
274 |
|
|
" < minimum size of %" PRId64 "\n", fssize, minfssize); |
275 |
|
|
|
276 |
|
|
bcount = dbtofsb(&sblock, fssize); |
277 |
|
|
|
278 |
|
|
/* |
279 |
|
|
* While many people claim that ext2fs is a (bad) clone of ufs/ffs, |
280 |
|
|
* it isn't actual ffs so maybe we should call it "block group" |
281 |
|
|
* as their native name rather than ffs derived "cylinder group." |
282 |
|
|
* But we'll use the latter here since other kernel sources use it. |
283 |
|
|
* (I also agree "cylinder" based allocation is obsolete though) |
284 |
|
|
*/ |
285 |
|
|
|
286 |
|
|
/* maybe "simple is the best" */ |
287 |
|
|
blocks_per_cg = sblock.e2fs_bsize * NBBY; |
288 |
|
|
|
289 |
|
|
ncg = howmany(bcount - sblock.e2fs.e2fs_first_dblock, blocks_per_cg); |
290 |
|
|
blocks_gd = howmany(sizeof(struct ext2_gd) * ncg, bsize); |
291 |
|
|
|
292 |
|
|
/* check range of inode number */ |
293 |
|
|
if (num_inodes < EXT2_FIRSTINO) |
294 |
|
|
num_inodes = EXT2_FIRSTINO; /* needs reserved inodes + 1 */ |
295 |
|
|
if (num_inodes > UINT16_MAX * ncg) |
296 |
|
|
num_inodes = UINT16_MAX * ncg; /* ext2bgd_nifree is uint16_t */ |
297 |
|
|
|
298 |
|
|
inodes_per_cg = num_inodes / ncg; |
299 |
|
|
iblocks_per_cg = howmany(inodesize * inodes_per_cg, bsize); |
300 |
|
|
|
301 |
|
|
/* Check that the last cylinder group has enough space for inodes */ |
302 |
|
|
minblocks_per_cg = |
303 |
|
|
NBLOCK_BLOCK_BITMAP + |
304 |
|
|
NBLOCK_INODE_BITMAP + |
305 |
|
|
iblocks_per_cg + |
306 |
|
|
1; /* at least one data block */ |
307 |
|
|
if (Oflag == 0 || cg_has_sb(ncg - 1) != 0) |
308 |
|
|
minblocks_per_cg += NBLOCK_SUPERBLOCK + blocks_gd; |
309 |
|
|
|
310 |
|
|
blocks_lastcg = bcount - sblock.e2fs.e2fs_first_dblock - |
311 |
|
|
blocks_per_cg * (ncg - 1); |
312 |
|
|
if (blocks_lastcg < minblocks_per_cg) { |
313 |
|
|
/* |
314 |
|
|
* Since we make all the cylinder groups the same size, the |
315 |
|
|
* last will only be small if there are more than one |
316 |
|
|
* cylinder groups. If the last one is too small to store |
317 |
|
|
* filesystem data, just kill it. |
318 |
|
|
* |
319 |
|
|
* XXX: Does fsck_ext2fs(8) properly handle this case? |
320 |
|
|
*/ |
321 |
|
|
bcount -= blocks_lastcg; |
322 |
|
|
ncg--; |
323 |
|
|
blocks_lastcg = blocks_per_cg; |
324 |
|
|
blocks_gd = howmany(sizeof(struct ext2_gd) * ncg, bsize); |
325 |
|
|
inodes_per_cg = num_inodes / ncg; |
326 |
|
|
} |
327 |
|
|
/* roundup inodes_per_cg to make it use whole inode table blocks */ |
328 |
|
|
inodes_per_cg = roundup(inodes_per_cg, sblock.e2fs_ipb); |
329 |
|
|
num_inodes = inodes_per_cg * ncg; |
330 |
|
|
iblocks_per_cg = inodes_per_cg / sblock.e2fs_ipb; |
331 |
|
|
|
332 |
|
|
/* XXX: probably we should check these adjusted values again */ |
333 |
|
|
|
334 |
|
|
sblock.e2fs.e2fs_bcount = bcount; |
335 |
|
|
sblock.e2fs.e2fs_icount = num_inodes; |
336 |
|
|
|
337 |
|
|
sblock.e2fs_ncg = ncg; |
338 |
|
|
sblock.e2fs_ngdb = blocks_gd; |
339 |
|
|
sblock.e2fs_itpg = iblocks_per_cg; |
340 |
|
|
|
341 |
|
|
sblock.e2fs.e2fs_rbcount = sblock.e2fs.e2fs_bcount * minfree / 100; |
342 |
|
|
/* e2fs_fbcount will be accounted later */ |
343 |
|
|
/* e2fs_ficount will be accounted later */ |
344 |
|
|
|
345 |
|
|
sblock.e2fs.e2fs_bpg = blocks_per_cg; |
346 |
|
|
sblock.e2fs.e2fs_fpg = blocks_per_cg; |
347 |
|
|
|
348 |
|
|
sblock.e2fs.e2fs_ipg = inodes_per_cg; |
349 |
|
|
|
350 |
|
|
sblock.e2fs.e2fs_mtime = 0; |
351 |
|
|
sblock.e2fs.e2fs_wtime = (u_int32_t)tv.tv_sec; |
352 |
|
|
sblock.e2fs.e2fs_mnt_count = 0; |
353 |
|
|
/* XXX: should add some entropy to avoid checking all fs at once? */ |
354 |
|
|
sblock.e2fs.e2fs_max_mnt_count = EXT2_DEF_MAX_MNT_COUNT; |
355 |
|
|
|
356 |
|
|
sblock.e2fs.e2fs_magic = E2FS_MAGIC; |
357 |
|
|
sblock.e2fs.e2fs_state = E2FS_ISCLEAN; |
358 |
|
|
sblock.e2fs.e2fs_beh = E2FS_BEH_DEFAULT; |
359 |
|
|
sblock.e2fs.e2fs_minrev = 0; |
360 |
|
|
sblock.e2fs.e2fs_lastfsck = (u_int32_t)tv.tv_sec; |
361 |
|
|
sblock.e2fs.e2fs_fsckintv = EXT2_DEF_FSCKINTV; |
362 |
|
|
|
363 |
|
|
/* |
364 |
|
|
* Maybe we can use E2FS_OS_FREEBSD here and it would be more proper, |
365 |
|
|
* but the purpose of this newfs_ext2fs(8) command is to provide |
366 |
|
|
* a filesystem which can be recognized by firmware on some |
367 |
|
|
* Linux based appliances that can load bootstrap files only from |
368 |
|
|
* (their native) ext2fs, and anyway we will (and should) try to |
369 |
|
|
* act like them as much as possible. |
370 |
|
|
* |
371 |
|
|
* Anyway, I hope that all newer such boxes will keep their support |
372 |
|
|
* for the "GOOD_OLD_REV" ext2fs. |
373 |
|
|
*/ |
374 |
|
|
sblock.e2fs.e2fs_creator = E2FS_OS_LINUX; |
375 |
|
|
|
376 |
|
|
if (Oflag == 0) { |
377 |
|
|
sblock.e2fs.e2fs_rev = E2FS_REV0; |
378 |
|
|
sblock.e2fs.e2fs_features_compat = 0; |
379 |
|
|
sblock.e2fs.e2fs_features_incompat = 0; |
380 |
|
|
sblock.e2fs.e2fs_features_rocompat = 0; |
381 |
|
|
} else { |
382 |
|
|
sblock.e2fs.e2fs_rev = E2FS_REV1; |
383 |
|
|
/* |
384 |
|
|
* e2fsprogs say "REV1" is "dynamic" so |
385 |
|
|
* it isn't quite a version and maybe it means |
386 |
|
|
* "extended from REV0 so check compat features." |
387 |
|
|
* |
388 |
|
|
* XXX: We don't have any native tool to activate |
389 |
|
|
* the EXT2F_COMPAT_RESIZE feature and |
390 |
|
|
* fsck_ext2fs(8) might not fix structures for it. |
391 |
|
|
*/ |
392 |
|
|
sblock.e2fs.e2fs_features_compat = EXT2F_COMPAT_RESIZE; |
393 |
|
|
sblock.e2fs.e2fs_features_incompat = EXT2F_INCOMPAT_FTYPE; |
394 |
|
|
sblock.e2fs.e2fs_features_rocompat = |
395 |
|
|
EXT2F_ROCOMPAT_SPARSESUPER | EXT2F_ROCOMPAT_LARGEFILE; |
396 |
|
|
} |
397 |
|
|
|
398 |
|
|
sblock.e2fs.e2fs_ruid = geteuid(); |
399 |
|
|
sblock.e2fs.e2fs_rgid = getegid(); |
400 |
|
|
|
401 |
|
|
sblock.e2fs.e2fs_first_ino = EXT2_FIRSTINO; |
402 |
|
|
sblock.e2fs.e2fs_inode_size = inodesize; |
403 |
|
|
|
404 |
|
|
/* e2fs_block_group_nr is set on writing superblock to each group */ |
405 |
|
|
|
406 |
|
|
uuid_get(&sblock); |
407 |
|
|
if (volname != NULL) { |
408 |
|
|
if (strlen(volname) > sizeof(sblock.e2fs.e2fs_vname)) |
409 |
|
|
errx(EXIT_FAILURE, "Volume name is too long"); |
410 |
|
|
strlcpy(sblock.e2fs.e2fs_vname, volname, |
411 |
|
|
sizeof(sblock.e2fs.e2fs_vname)); |
412 |
|
|
} |
413 |
|
|
|
414 |
|
|
sblock.e2fs.e2fs_fsmnt[0] = '\0'; |
415 |
|
|
sblock.e2fs_fsmnt[0] = '\0'; |
416 |
|
|
|
417 |
|
|
sblock.e2fs.e2fs_algo = 0; /* XXX unsupported? */ |
418 |
|
|
sblock.e2fs.e2fs_prealloc = 0; /* XXX unsupported? */ |
419 |
|
|
sblock.e2fs.e2fs_dir_prealloc = 0; /* XXX unsupported? */ |
420 |
|
|
|
421 |
|
|
/* calculate blocks for reserved group descriptors for resize */ |
422 |
|
|
sblock.e2fs.e2fs_reserved_ngdb = 0; |
423 |
|
|
if (sblock.e2fs.e2fs_rev > E2FS_REV0 && |
424 |
|
|
(sblock.e2fs.e2fs_features_compat & EXT2F_COMPAT_RESIZE) != 0) { |
425 |
|
|
uint64_t target_blocks; |
426 |
|
|
uint target_ncg, target_ngdb, reserved_ngdb; |
427 |
|
|
|
428 |
|
|
/* reserve descriptors for size as 1024 times as current */ |
429 |
|
|
target_blocks = |
430 |
|
|
(sblock.e2fs.e2fs_bcount - sblock.e2fs.e2fs_first_dblock) |
431 |
|
|
* 1024ULL; |
432 |
|
|
/* number of blocks must be in uint32_t */ |
433 |
|
|
if (target_blocks > UINT32_MAX) |
434 |
|
|
target_blocks = UINT32_MAX; |
435 |
|
|
target_ncg = howmany(target_blocks, sblock.e2fs.e2fs_bpg); |
436 |
|
|
target_ngdb = howmany(sizeof(struct ext2_gd) * target_ncg, |
437 |
|
|
sblock.e2fs_bsize); |
438 |
|
|
/* |
439 |
|
|
* Reserved group descriptor blocks are preserved as |
440 |
|
|
* the second level double indirect reference blocks in |
441 |
|
|
* the EXT2_RESIZEINO inode, so the maximum number of |
442 |
|
|
* the blocks is NINDIR(fs). |
443 |
|
|
* (see also descriptions in init_resizeino() function) |
444 |
|
|
* |
445 |
|
|
* We check a number including current e2fs_ngdb here |
446 |
|
|
* because they will be moved into reserved gdb on |
447 |
|
|
* possible future size shrink, though e2fsprogs don't |
448 |
|
|
* seem to care about it. |
449 |
|
|
*/ |
450 |
|
|
if (target_ngdb > NINDIR(&sblock)) |
451 |
|
|
target_ngdb = NINDIR(&sblock); |
452 |
|
|
|
453 |
|
|
reserved_ngdb = target_ngdb - sblock.e2fs_ngdb; |
454 |
|
|
|
455 |
|
|
/* make sure reserved_ngdb fits in the last cg */ |
456 |
|
|
if (reserved_ngdb >= blocks_lastcg - cgoverhead(ncg - 1)) |
457 |
|
|
reserved_ngdb = blocks_lastcg - cgoverhead(ncg - 1); |
458 |
|
|
if (reserved_ngdb == 0) { |
459 |
|
|
/* if no space for reserved gdb, disable the feature */ |
460 |
|
|
sblock.e2fs.e2fs_features_compat &= |
461 |
|
|
~EXT2F_COMPAT_RESIZE; |
462 |
|
|
} |
463 |
|
|
sblock.e2fs.e2fs_reserved_ngdb = reserved_ngdb; |
464 |
|
|
} |
465 |
|
|
|
466 |
|
|
/* |
467 |
|
|
* Initialize group descriptors |
468 |
|
|
*/ |
469 |
|
|
gd = calloc(sblock.e2fs_ngdb, bsize); |
470 |
|
|
if (gd == NULL) |
471 |
|
|
errx(EXIT_FAILURE, "Can't allocate descriptors buffer"); |
472 |
|
|
|
473 |
|
|
fbcount = 0; |
474 |
|
|
ficount = 0; |
475 |
|
|
for (cylno = 0; cylno < ncg; cylno++) { |
476 |
|
|
uint boffset; |
477 |
|
|
|
478 |
|
|
boffset = cgbase(&sblock, cylno); |
479 |
|
|
if (sblock.e2fs.e2fs_rev == E2FS_REV0 || |
480 |
|
|
(sblock.e2fs.e2fs_features_rocompat & |
481 |
|
|
EXT2F_ROCOMPAT_SPARSESUPER) == 0 || |
482 |
|
|
cg_has_sb(cylno)) { |
483 |
|
|
boffset += NBLOCK_SUPERBLOCK + sblock.e2fs_ngdb; |
484 |
|
|
if (sblock.e2fs.e2fs_rev > E2FS_REV0 && |
485 |
|
|
(sblock.e2fs.e2fs_features_compat & |
486 |
|
|
EXT2F_COMPAT_RESIZE) != 0) |
487 |
|
|
boffset += sblock.e2fs.e2fs_reserved_ngdb; |
488 |
|
|
} |
489 |
|
|
gd[cylno].ext2bgd_b_bitmap = boffset; |
490 |
|
|
boffset += NBLOCK_BLOCK_BITMAP; |
491 |
|
|
gd[cylno].ext2bgd_i_bitmap = boffset; |
492 |
|
|
boffset += NBLOCK_INODE_BITMAP; |
493 |
|
|
gd[cylno].ext2bgd_i_tables = boffset; |
494 |
|
|
if (cylno == (ncg - 1)) |
495 |
|
|
gd[cylno].ext2bgd_nbfree = |
496 |
|
|
blocks_lastcg - cgoverhead(cylno); |
497 |
|
|
else |
498 |
|
|
gd[cylno].ext2bgd_nbfree = |
499 |
|
|
sblock.e2fs.e2fs_bpg - cgoverhead(cylno); |
500 |
|
|
fbcount += gd[cylno].ext2bgd_nbfree; |
501 |
|
|
gd[cylno].ext2bgd_nifree = sblock.e2fs.e2fs_ipg; |
502 |
|
|
if (cylno == 0) { |
503 |
|
|
/* take reserved inodes off nifree */ |
504 |
|
|
gd[cylno].ext2bgd_nifree -= EXT2_RESERVED_INODES; |
505 |
|
|
} |
506 |
|
|
ficount += gd[cylno].ext2bgd_nifree; |
507 |
|
|
gd[cylno].ext2bgd_ndirs = 0; |
508 |
|
|
} |
509 |
|
|
sblock.e2fs.e2fs_fbcount = fbcount; |
510 |
|
|
sblock.e2fs.e2fs_ficount = ficount; |
511 |
|
|
|
512 |
|
|
/* |
513 |
|
|
* Dump out summary information about file system. |
514 |
|
|
*/ |
515 |
|
|
if (verbosity > 0) { |
516 |
|
|
printf("%s: %u.%1uMB (%" PRId64 " sectors) " |
517 |
|
|
"block size %u, fragment size %u\n", |
518 |
|
|
fsys, |
519 |
|
|
(uint)(((uint64_t)bcount * bsize) / (1024 * 1024)), |
520 |
|
|
(uint)((uint64_t)bcount * bsize - |
521 |
|
|
rounddown((uint64_t)bcount * bsize, 1024 * 1024)) |
522 |
|
|
/ 1024 / 100, |
523 |
|
|
fssize, bsize, fsize); |
524 |
|
|
printf("\tusing %u block groups of %u.0MB, %u blks, " |
525 |
|
|
"%u inodes.\n", |
526 |
|
|
ncg, bsize * sblock.e2fs.e2fs_bpg / (1024 * 1024), |
527 |
|
|
sblock.e2fs.e2fs_bpg, sblock.e2fs.e2fs_ipg); |
528 |
|
|
} |
529 |
|
|
|
530 |
|
|
/* |
531 |
|
|
* allocate space for superblock and group descriptors |
532 |
|
|
*/ |
533 |
|
|
iobufsize = (NBLOCK_SUPERBLOCK + sblock.e2fs_ngdb) * sblock.e2fs_bsize; |
534 |
|
|
iobuf = mmap(0, iobufsize, PROT_READ|PROT_WRITE, |
535 |
|
|
MAP_ANON|MAP_PRIVATE, -1, 0); |
536 |
|
|
if (iobuf == MAP_FAILED) |
537 |
|
|
errx(EXIT_FAILURE, "Cannot allocate I/O buffer\n"); |
538 |
|
|
|
539 |
|
|
/* |
540 |
|
|
* We now start writing to the filesystem |
541 |
|
|
*/ |
542 |
|
|
|
543 |
|
|
if (!Nflag) { |
544 |
|
|
static const uint pbsize[] = { 1024, 2048, 4096, 0 }; |
545 |
|
|
uint pblock, epblock; |
546 |
|
|
/* |
547 |
|
|
* Validate the given file system size. |
548 |
|
|
* Verify that its last block can actually be accessed. |
549 |
|
|
* Convert to file system fragment sized units. |
550 |
|
|
*/ |
551 |
|
|
if (fssize <= 0) |
552 |
|
|
errx(EXIT_FAILURE, "Preposterous size %" PRId64 "\n", |
553 |
|
|
fssize); |
554 |
|
|
wtfs(fssize - 1, sectorsize, iobuf); |
555 |
|
|
|
556 |
|
|
/* |
557 |
|
|
* Ensure there is nothing that looks like a filesystem |
558 |
|
|
* superblock anywhere other than where ours will be. |
559 |
|
|
* If fsck_ext2fs finds the wrong one all hell breaks loose! |
560 |
|
|
* |
561 |
|
|
* XXX: needs to check how fsck_ext2fs programs even |
562 |
|
|
* on other OSes determine alternate superblocks |
563 |
|
|
*/ |
564 |
|
|
for (i = 0; pbsize[i] != 0; i++) { |
565 |
|
|
epblock = (uint64_t)bcount * bsize / pbsize[i]; |
566 |
|
|
for (pblock = ((pbsize[i] == SBSIZE) ? 1 : 0); |
567 |
|
|
pblock < epblock; |
568 |
|
|
pblock += pbsize[i] * NBBY /* bpg */) |
569 |
|
|
zap_old_sblock((daddr32_t)pblock * |
570 |
|
|
pbsize[i] / sectorsize); |
571 |
|
|
} |
572 |
|
|
} |
573 |
|
|
|
574 |
|
|
if (verbosity >= 3) |
575 |
|
|
printf("super-block backups (for fsck_ext2fs -b #) at:\n"); |
576 |
|
|
/* If we are printing more than one line of numbers, line up columns */ |
577 |
|
|
fld_width = verbosity < 4 ? 1 : snprintf(NULL, 0, "%" PRIu64, |
578 |
|
|
(uint64_t)cgbase(&sblock, ncg - 1)); |
579 |
|
|
if (Nflag && verbosity == 3) |
580 |
|
|
/* Leave space to add " ..." after one row of numbers */ |
581 |
|
|
max_cols -= 4; |
582 |
|
|
#define BASE 0x10000 /* For some fixed-point maths */ |
583 |
|
|
col = 0; |
584 |
|
|
delta = verbosity > 2 ? 0 : max_cols * BASE / ncg; |
585 |
|
|
for (cylno = 0; cylno < ncg; cylno++) { |
586 |
|
|
fflush(stdout); |
587 |
|
|
initcg(cylno); |
588 |
|
|
if (verbosity < 2) |
589 |
|
|
continue; |
590 |
|
|
/* the first one is a master, not backup */ |
591 |
|
|
if (cylno == 0) |
592 |
|
|
continue; |
593 |
|
|
/* skip if this cylinder doesn't have a backup */ |
594 |
|
|
if (sblock.e2fs.e2fs_rev > E2FS_REV0 && |
595 |
|
|
(sblock.e2fs.e2fs_features_rocompat & |
596 |
|
|
EXT2F_ROCOMPAT_SPARSESUPER) != 0 && |
597 |
|
|
cg_has_sb(cylno) == 0) |
598 |
|
|
continue; |
599 |
|
|
|
600 |
|
|
if (delta > 0) { |
601 |
|
|
if (Nflag) |
602 |
|
|
/* No point doing dots for -N */ |
603 |
|
|
break; |
604 |
|
|
/* Print dots scaled to end near RH margin */ |
605 |
|
|
for (col += delta; col > BASE; col -= BASE) |
606 |
|
|
printf("."); |
607 |
|
|
continue; |
608 |
|
|
} |
609 |
|
|
/* Print superblock numbers */ |
610 |
|
|
len = printf(" %*" PRIu64 "," + !col, fld_width, |
611 |
|
|
(uint64_t)cgbase(&sblock, cylno)); |
612 |
|
|
col += len; |
613 |
|
|
if (col + len < max_cols) |
614 |
|
|
/* Next number fits */ |
615 |
|
|
continue; |
616 |
|
|
/* Next number won't fit, need a newline */ |
617 |
|
|
if (verbosity <= 3) { |
618 |
|
|
/* Print dots for subsequent cylinder groups */ |
619 |
|
|
delta = sblock.e2fs_ncg - cylno - 1; |
620 |
|
|
if (delta != 0) { |
621 |
|
|
if (Nflag) { |
622 |
|
|
printf(" ..."); |
623 |
|
|
break; |
624 |
|
|
} |
625 |
|
|
delta = max_cols * BASE / delta; |
626 |
|
|
} |
627 |
|
|
} |
628 |
|
|
col = 0; |
629 |
|
|
printf("\n"); |
630 |
|
|
} |
631 |
|
|
#undef BASE |
632 |
|
|
if (col > 0) |
633 |
|
|
printf("\n"); |
634 |
|
|
if (Nflag) |
635 |
|
|
return; |
636 |
|
|
|
637 |
|
|
/* |
638 |
|
|
* Now construct the initial file system, |
639 |
|
|
*/ |
640 |
|
|
if (fsinit(&tv) == 0) |
641 |
|
|
errx(EXIT_FAILURE, "Error making filesystem"); |
642 |
|
|
/* |
643 |
|
|
* Write out the superblock and group descriptors |
644 |
|
|
*/ |
645 |
|
|
sblock.e2fs.e2fs_block_group_nr = 0; |
646 |
|
|
sboff = 0; |
647 |
|
|
if (cgbase(&sblock, 0) == 0) { |
648 |
|
|
/* |
649 |
|
|
* If the first block contains the boot block sectors, |
650 |
|
|
* (i.e. in case of sblock.e2fs.e2fs_bsize > BBSIZE) |
651 |
|
|
* we have to preserve data in it. |
652 |
|
|
*/ |
653 |
|
|
sboff = SBOFF; |
654 |
|
|
} |
655 |
|
|
e2fs_sbsave(&sblock.e2fs, (struct ext2fs *)(iobuf + sboff)); |
656 |
|
|
e2fs_cgsave(gd, (struct ext2_gd *)(iobuf + sblock.e2fs_bsize), |
657 |
|
|
sizeof(struct ext2_gd) * sblock.e2fs_ncg); |
658 |
|
|
wtfs(fsbtodb(&sblock, cgbase(&sblock, 0)) + sboff / sectorsize, |
659 |
|
|
iobufsize - sboff, iobuf + sboff); |
660 |
|
|
|
661 |
|
|
munmap(iobuf, iobufsize); |
662 |
|
|
} |
663 |
|
|
|
664 |
|
|
/* |
665 |
|
|
* Initialize a cylinder (block) group. |
666 |
|
|
*/ |
667 |
|
|
void |
668 |
|
|
initcg(uint cylno) |
669 |
|
|
{ |
670 |
|
|
uint nblcg, i, j, sboff; |
671 |
|
|
struct ext2fs_dinode *dp; |
672 |
|
|
|
673 |
|
|
/* |
674 |
|
|
* Make a copy of the superblock and group descriptors. |
675 |
|
|
*/ |
676 |
|
|
if (sblock.e2fs.e2fs_rev == E2FS_REV0 || |
677 |
|
|
(sblock.e2fs.e2fs_features_rocompat & |
678 |
|
|
EXT2F_ROCOMPAT_SPARSESUPER) == 0 || |
679 |
|
|
cg_has_sb(cylno)) { |
680 |
|
|
sblock.e2fs.e2fs_block_group_nr = cylno; |
681 |
|
|
sboff = 0; |
682 |
|
|
if (cgbase(&sblock, cylno) == 0) { |
683 |
|
|
/* preserve data in bootblock in cg0 */ |
684 |
|
|
sboff = SBOFF; |
685 |
|
|
} |
686 |
|
|
e2fs_sbsave(&sblock.e2fs, (struct ext2fs *)(iobuf + sboff)); |
687 |
|
|
e2fs_cgsave(gd, (struct ext2_gd *)(iobuf + |
688 |
|
|
sblock.e2fs_bsize * NBLOCK_SUPERBLOCK), |
689 |
|
|
sizeof(struct ext2_gd) * sblock.e2fs_ncg); |
690 |
|
|
/* write superblock and group descriptor backups */ |
691 |
|
|
wtfs(fsbtodb(&sblock, cgbase(&sblock, cylno)) + |
692 |
|
|
sboff / sectorsize, iobufsize - sboff, iobuf + sboff); |
693 |
|
|
} |
694 |
|
|
|
695 |
|
|
/* |
696 |
|
|
* Initialize block bitmap. |
697 |
|
|
*/ |
698 |
|
|
memset(buf, 0, sblock.e2fs_bsize); |
699 |
|
|
if (cylno == (sblock.e2fs_ncg - 1)) { |
700 |
|
|
/* The last group could have less blocks than e2fs_bpg. */ |
701 |
|
|
nblcg = sblock.e2fs.e2fs_bcount - |
702 |
|
|
cgbase(&sblock, sblock.e2fs_ncg - 1); |
703 |
|
|
for (i = nblcg; i < roundup(nblcg, NBBY); i++) |
704 |
|
|
setbit(buf, i); |
705 |
|
|
memset(&buf[i / NBBY], ~0U, sblock.e2fs.e2fs_bpg - i); |
706 |
|
|
} |
707 |
|
|
/* set overhead (superblock, group descriptor etc.) blocks used */ |
708 |
|
|
for (i = 0; i < cgoverhead(cylno) / NBBY; i++) |
709 |
|
|
buf[i] = ~0; |
710 |
|
|
i = i * NBBY; |
711 |
|
|
for (; i < cgoverhead(cylno); i++) |
712 |
|
|
setbit(buf, i); |
713 |
|
|
wtfs(fsbtodb(&sblock, gd[cylno].ext2bgd_b_bitmap), sblock.e2fs_bsize, |
714 |
|
|
buf); |
715 |
|
|
|
716 |
|
|
/* |
717 |
|
|
* Initialize inode bitmap. |
718 |
|
|
* |
719 |
|
|
* Assume e2fs_ipg is a multiple of NBBY since |
720 |
|
|
* it's a multiple of e2fs_ipb (as we did above). |
721 |
|
|
* Note even (possibly smaller) the last group has the same e2fs_ipg. |
722 |
|
|
*/ |
723 |
|
|
i = sblock.e2fs.e2fs_ipg / NBBY; |
724 |
|
|
memset(buf, 0, i); |
725 |
|
|
memset(buf + i, ~0U, sblock.e2fs_bsize - i); |
726 |
|
|
if (cylno == 0) { |
727 |
|
|
/* mark reserved inodes */ |
728 |
|
|
for (i = 1; i < EXT2_FIRSTINO; i++) |
729 |
|
|
setbit(buf, EXT2_INO_INDEX(i)); |
730 |
|
|
} |
731 |
|
|
wtfs(fsbtodb(&sblock, gd[cylno].ext2bgd_i_bitmap), sblock.e2fs_bsize, |
732 |
|
|
buf); |
733 |
|
|
|
734 |
|
|
/* |
735 |
|
|
* Initialize inode tables. |
736 |
|
|
* |
737 |
|
|
* Just initialize generation numbers for NFS security. |
738 |
|
|
* XXX: sys/ufs/ext2fs/ext2fs_alloc.c:ext2fs_valloc() seems |
739 |
|
|
* to override these generated numbers. |
740 |
|
|
*/ |
741 |
|
|
memset(buf, 0, sblock.e2fs_bsize); |
742 |
|
|
for (i = 0; i < sblock.e2fs_itpg; i++) { |
743 |
|
|
for (j = 0; j < sblock.e2fs_ipb; j++) { |
744 |
|
|
dp = (struct ext2fs_dinode *)(buf + inodesize * j); |
745 |
|
|
/* If there is some bias in arc4random(), keep it. */ |
746 |
|
|
dp->e2di_gen = htole32(arc4random()); |
747 |
|
|
} |
748 |
|
|
wtfs(fsbtodb(&sblock, gd[cylno].ext2bgd_i_tables + i), |
749 |
|
|
sblock.e2fs_bsize, buf); |
750 |
|
|
} |
751 |
|
|
} |
752 |
|
|
|
753 |
|
|
/* |
754 |
|
|
* Zap possible lingering old superblock data |
755 |
|
|
*/ |
756 |
|
|
static void |
757 |
|
|
zap_old_sblock(daddr32_t sec) |
758 |
|
|
{ |
759 |
|
|
static daddr32_t cg0_data; |
760 |
|
|
uint32_t oldfs[SBSIZE / sizeof(uint32_t)]; |
761 |
|
|
static const struct fsm { |
762 |
|
|
uint32_t offset; |
763 |
|
|
uint32_t magic; |
764 |
|
|
uint32_t mask; |
765 |
|
|
} fs_magics[] = { |
766 |
|
|
{offsetof(struct ext2fs, e2fs_magic) / 4, E2FS_MAGIC, 0xffff}, |
767 |
|
|
{offsetof(struct ext2fs, e2fs_magic) / 4, |
768 |
|
|
E2FS_MAGIC << 16, 0xffff0000}, |
769 |
|
|
{14, 0xef530000, 0xffff0000}, /* EXT2FS (big) */ |
770 |
|
|
{0x55c / 4, 0x00011954, ~0U}, /* FS_UFS1_MAGIC */ |
771 |
|
|
{0x55c / 4, 0x19540119, ~0U}, /* FS_UFS2_MAGIC */ |
772 |
|
|
{0, 0x70162, ~0U}, /* LFS_MAGIC */ |
773 |
|
|
{.offset = ~0U}, |
774 |
|
|
}; |
775 |
|
|
const struct fsm *fsm; |
776 |
|
|
|
777 |
|
|
if (Nflag) |
778 |
|
|
return; |
779 |
|
|
|
780 |
|
|
/* don't override data before superblock */ |
781 |
|
|
if (sec < SBOFF / sectorsize) |
782 |
|
|
return; |
783 |
|
|
|
784 |
|
|
if (cg0_data == 0) { |
785 |
|
|
cg0_data = |
786 |
|
|
((daddr32_t)sblock.e2fs.e2fs_first_dblock + cgoverhead(0)) * |
787 |
|
|
sblock.e2fs_bsize / sectorsize; |
788 |
|
|
} |
789 |
|
|
|
790 |
|
|
/* Ignore anything that is beyond our filesystem */ |
791 |
|
|
if (sec >= fssize) |
792 |
|
|
return; |
793 |
|
|
/* Zero anything inside our filesystem... */ |
794 |
|
|
if (sec >= sblock.e2fs.e2fs_first_dblock * bsize / sectorsize) { |
795 |
|
|
/* ...unless we will write that area anyway */ |
796 |
|
|
if (sec >= cg0_data) |
797 |
|
|
/* assume iobuf is zero'ed here */ |
798 |
|
|
wtfs(sec, roundup(SBSIZE, sectorsize), iobuf); |
799 |
|
|
return; |
800 |
|
|
} |
801 |
|
|
|
802 |
|
|
/* |
803 |
|
|
* The sector might contain boot code, so we must validate it |
804 |
|
|
* |
805 |
|
|
* XXX: ext2fs won't preserve data after SBOFF, |
806 |
|
|
* but first_dblock could have a different value. |
807 |
|
|
*/ |
808 |
|
|
rdfs(sec, sizeof(oldfs), &oldfs); |
809 |
|
|
for (fsm = fs_magics;; fsm++) { |
810 |
|
|
uint32_t v; |
811 |
|
|
if (fsm->mask == 0) |
812 |
|
|
return; |
813 |
|
|
v = oldfs[fsm->offset]; |
814 |
|
|
if ((v & fsm->mask) == fsm->magic || |
815 |
|
|
(swap32(v) & fsm->mask) == fsm->magic) |
816 |
|
|
break; |
817 |
|
|
} |
818 |
|
|
|
819 |
|
|
/* Just zap the magic number */ |
820 |
|
|
oldfs[fsm->offset] = 0; |
821 |
|
|
wtfs(sec, sizeof(oldfs), &oldfs); |
822 |
|
|
} |
823 |
|
|
|
824 |
|
|
/* |
825 |
|
|
* uint cgoverhead(uint c) |
826 |
|
|
* |
827 |
|
|
* Return a number of reserved blocks on the specified group. |
828 |
|
|
* XXX: should be shared with src/sbin/fsck_ext2fs/setup.c |
829 |
|
|
*/ |
830 |
|
|
uint |
831 |
|
|
cgoverhead(uint c) |
832 |
|
|
{ |
833 |
|
|
uint overh; |
834 |
|
|
|
835 |
|
|
overh = NBLOCK_BLOCK_BITMAP + NBLOCK_INODE_BITMAP + sblock.e2fs_itpg; |
836 |
|
|
|
837 |
|
|
if (sblock.e2fs.e2fs_rev == E2FS_REV0 || |
838 |
|
|
(sblock.e2fs.e2fs_features_rocompat & |
839 |
|
|
EXT2F_ROCOMPAT_SPARSESUPER) == 0 || |
840 |
|
|
cg_has_sb(c) != 0) { |
841 |
|
|
overh += NBLOCK_SUPERBLOCK + sblock.e2fs_ngdb; |
842 |
|
|
|
843 |
|
|
if (sblock.e2fs.e2fs_rev > E2FS_REV0 && |
844 |
|
|
(sblock.e2fs.e2fs_features_compat & |
845 |
|
|
EXT2F_COMPAT_RESIZE) != 0) |
846 |
|
|
overh += sblock.e2fs.e2fs_reserved_ngdb; |
847 |
|
|
} |
848 |
|
|
|
849 |
|
|
return overh; |
850 |
|
|
} |
851 |
|
|
|
852 |
|
|
/* |
853 |
|
|
* Initialize the file system |
854 |
|
|
*/ |
855 |
|
|
|
856 |
|
|
#define LOSTDIR /* e2fsck complains if there is no lost+found */ |
857 |
|
|
|
858 |
|
|
#define PREDEFDIR 2 |
859 |
|
|
|
860 |
|
|
#ifdef LOSTDIR |
861 |
|
|
#define PREDEFROOTDIR (PREDEFDIR + 1) |
862 |
|
|
#else |
863 |
|
|
#define PREDEFROOTDIR PREDEFDIR |
864 |
|
|
#endif |
865 |
|
|
|
866 |
|
|
struct ext2fs_direct root_dir[] = { |
867 |
|
|
{ EXT2_ROOTINO, 0, 1, 0, "." }, |
868 |
|
|
{ EXT2_ROOTINO, 0, 2, 0, ".." }, |
869 |
|
|
#ifdef LOSTDIR |
870 |
|
|
{ EXT2_LOSTFOUNDINO, 0, 10, 0, "lost+found" }, |
871 |
|
|
#endif |
872 |
|
|
}; |
873 |
|
|
|
874 |
|
|
#ifdef LOSTDIR |
875 |
|
|
struct ext2fs_direct lost_found_dir[] = { |
876 |
|
|
{ EXT2_LOSTFOUNDINO, 0, 1, 0, "." }, |
877 |
|
|
{ EXT2_ROOTINO, 0, 2, 0, ".." }, |
878 |
|
|
}; |
879 |
|
|
struct ext2fs_direct pad_dir = { 0, sizeof(struct ext2fs_direct), 0, 0, "" }; |
880 |
|
|
#endif |
881 |
|
|
|
882 |
|
|
int |
883 |
|
|
fsinit(const struct timeval *tv) |
884 |
|
|
{ |
885 |
|
|
struct ext2fs_dinode node; |
886 |
|
|
#ifdef LOSTDIR |
887 |
|
|
uint i, nblks_lostfound, blk; |
888 |
|
|
#endif |
889 |
|
|
|
890 |
|
|
/* |
891 |
|
|
* Initialize the inode for the resizefs feature |
892 |
|
|
*/ |
893 |
|
|
if (sblock.e2fs.e2fs_rev > E2FS_REV0 && |
894 |
|
|
(sblock.e2fs.e2fs_features_compat & EXT2F_COMPAT_RESIZE) != 0) |
895 |
|
|
init_resizeino(tv); |
896 |
|
|
|
897 |
|
|
/* |
898 |
|
|
* Initialize the node |
899 |
|
|
*/ |
900 |
|
|
|
901 |
|
|
#ifdef LOSTDIR |
902 |
|
|
/* |
903 |
|
|
* Create the lost+found directory |
904 |
|
|
*/ |
905 |
|
|
if (sblock.e2fs.e2fs_rev > E2FS_REV0 && |
906 |
|
|
sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE) { |
907 |
|
|
lost_found_dir[0].e2d_type = EXT2_FT_DIR; |
908 |
|
|
lost_found_dir[1].e2d_type = EXT2_FT_DIR; |
909 |
|
|
} |
910 |
|
|
(void)makedir(lost_found_dir, nitems(lost_found_dir)); |
911 |
|
|
|
912 |
|
|
/* prepare a bit large directory for preserved files */ |
913 |
|
|
nblks_lostfound = EXT2_LOSTFOUNDSIZE / sblock.e2fs_bsize; |
914 |
|
|
/* ...but only with direct blocks */ |
915 |
|
|
if (nblks_lostfound > NDADDR) |
916 |
|
|
nblks_lostfound = NDADDR; |
917 |
|
|
|
918 |
|
|
memset(&node, 0, sizeof(node)); |
919 |
|
|
node.e2di_mode = EXT2_IFDIR | EXT2_LOSTFOUNDUMASK; |
920 |
|
|
node.e2di_uid_low = geteuid(); |
921 |
|
|
node.e2di_size = sblock.e2fs_bsize * nblks_lostfound; |
922 |
|
|
node.e2di_atime = (u_int32_t)tv->tv_sec; |
923 |
|
|
node.e2di_ctime = (u_int32_t)tv->tv_sec; |
924 |
|
|
node.e2di_mtime = (u_int32_t)tv->tv_sec; |
925 |
|
|
node.e2di_gid_low = getegid(); |
926 |
|
|
node.e2di_nlink = PREDEFDIR; |
927 |
|
|
/* e2di_nblock is a number of disk blocks, not ext2fs blocks */ |
928 |
|
|
node.e2di_nblock = fsbtodb(&sblock, nblks_lostfound); |
929 |
|
|
node.e2di_blocks[0] = alloc(sblock.e2fs_bsize, node.e2di_mode); |
930 |
|
|
if (node.e2di_blocks[0] == 0) { |
931 |
|
|
printf("%s: can't allocate block for lost+found\n", __func__); |
932 |
|
|
return 0; |
933 |
|
|
} |
934 |
|
|
for (i = 1; i < nblks_lostfound; i++) { |
935 |
|
|
blk = alloc(sblock.e2fs_bsize, 0); |
936 |
|
|
if (blk == 0) { |
937 |
|
|
printf("%s: can't allocate blocks for lost+found\n", |
938 |
|
|
__func__); |
939 |
|
|
return 0; |
940 |
|
|
} |
941 |
|
|
node.e2di_blocks[i] = blk; |
942 |
|
|
} |
943 |
|
|
wtfs(fsbtodb(&sblock, node.e2di_blocks[0]), sblock.e2fs_bsize, buf); |
944 |
|
|
pad_dir.e2d_reclen = sblock.e2fs_bsize; |
945 |
|
|
for (i = 1; i < nblks_lostfound; i++) { |
946 |
|
|
memset(buf, 0, sblock.e2fs_bsize); |
947 |
|
|
copy_dir(&pad_dir, (struct ext2fs_direct *)buf); |
948 |
|
|
wtfs(fsbtodb(&sblock, node.e2di_blocks[i]), sblock.e2fs_bsize, |
949 |
|
|
buf); |
950 |
|
|
} |
951 |
|
|
iput(&node, EXT2_LOSTFOUNDINO); |
952 |
|
|
#endif |
953 |
|
|
/* |
954 |
|
|
* create the root directory |
955 |
|
|
*/ |
956 |
|
|
memset(&node, 0, sizeof(node)); |
957 |
|
|
if (sblock.e2fs.e2fs_rev > E2FS_REV0 && |
958 |
|
|
sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE) { |
959 |
|
|
root_dir[0].e2d_type = EXT2_FT_DIR; |
960 |
|
|
root_dir[1].e2d_type = EXT2_FT_DIR; |
961 |
|
|
#ifdef LOSTDIR |
962 |
|
|
root_dir[2].e2d_type = EXT2_FT_DIR; |
963 |
|
|
#endif |
964 |
|
|
} |
965 |
|
|
node.e2di_mode = EXT2_IFDIR | EXT2_UMASK; |
966 |
|
|
node.e2di_uid_low = geteuid(); |
967 |
|
|
node.e2di_size = makedir(root_dir, nitems(root_dir)); |
968 |
|
|
node.e2di_atime = (u_int32_t)tv->tv_sec; |
969 |
|
|
node.e2di_ctime = (u_int32_t)tv->tv_sec; |
970 |
|
|
node.e2di_mtime = (u_int32_t)tv->tv_sec; |
971 |
|
|
node.e2di_gid_low = getegid(); |
972 |
|
|
node.e2di_nlink = PREDEFROOTDIR; |
973 |
|
|
/* e2di_nblock is a number of disk block, not ext2fs block */ |
974 |
|
|
node.e2di_nblock = fsbtodb(&sblock, 1); |
975 |
|
|
node.e2di_blocks[0] = alloc(node.e2di_size, node.e2di_mode); |
976 |
|
|
if (node.e2di_blocks[0] == 0) { |
977 |
|
|
printf("%s: can't allocate block for root dir\n", __func__); |
978 |
|
|
return 0; |
979 |
|
|
} |
980 |
|
|
wtfs(fsbtodb(&sblock, node.e2di_blocks[0]), sblock.e2fs_bsize, buf); |
981 |
|
|
iput(&node, EXT2_ROOTINO); |
982 |
|
|
return 1; |
983 |
|
|
} |
984 |
|
|
|
985 |
|
|
/* |
986 |
|
|
* Construct a set of directory entries in "buf". |
987 |
|
|
* return size of directory. |
988 |
|
|
*/ |
989 |
|
|
int |
990 |
|
|
makedir(struct ext2fs_direct *protodir, int entries) |
991 |
|
|
{ |
992 |
|
|
uint8_t *cp; |
993 |
|
|
uint i, spcleft; |
994 |
|
|
uint dirblksiz; |
995 |
|
|
|
996 |
|
|
dirblksiz = sblock.e2fs_bsize; |
997 |
|
|
memset(buf, 0, dirblksiz); |
998 |
|
|
spcleft = dirblksiz; |
999 |
|
|
for (cp = buf, i = 0; i < entries - 1; i++) { |
1000 |
|
|
protodir[i].e2d_reclen = EXT2FS_DIRSIZ(protodir[i].e2d_namlen); |
1001 |
|
|
copy_dir(&protodir[i], (struct ext2fs_direct *)cp); |
1002 |
|
|
cp += protodir[i].e2d_reclen; |
1003 |
|
|
spcleft -= protodir[i].e2d_reclen; |
1004 |
|
|
} |
1005 |
|
|
protodir[i].e2d_reclen = spcleft; |
1006 |
|
|
copy_dir(&protodir[i], (struct ext2fs_direct *)cp); |
1007 |
|
|
return dirblksiz; |
1008 |
|
|
} |
1009 |
|
|
|
1010 |
|
|
/* |
1011 |
|
|
* Copy a direntry to a buffer, in fs byte order |
1012 |
|
|
*/ |
1013 |
|
|
static void |
1014 |
|
|
copy_dir(struct ext2fs_direct *dir, struct ext2fs_direct *dbuf) |
1015 |
|
|
{ |
1016 |
|
|
|
1017 |
|
|
memcpy(dbuf, dir, EXT2FS_DIRSIZ(dir->e2d_namlen)); |
1018 |
|
|
dbuf->e2d_ino = htole32(dir->e2d_ino); |
1019 |
|
|
dbuf->e2d_reclen = htole16(dir->e2d_reclen); |
1020 |
|
|
} |
1021 |
|
|
|
1022 |
|
|
/* |
1023 |
|
|
* void init_resizeino(const struct timeval *tv); |
1024 |
|
|
* |
1025 |
|
|
* Initialize the EXT2_RESEIZE_INO inode to preserve |
1026 |
|
|
* reserved group descriptor blocks for future growth of this ext2fs. |
1027 |
|
|
*/ |
1028 |
|
|
void |
1029 |
|
|
init_resizeino(const struct timeval *tv) |
1030 |
|
|
{ |
1031 |
|
|
struct ext2fs_dinode node; |
1032 |
|
|
uint64_t isize; |
1033 |
|
|
uint32_t *dindir_block, *reserved_gdb; |
1034 |
|
|
uint nblock, i, cylno, n; |
1035 |
|
|
|
1036 |
|
|
memset(&node, 0, sizeof(node)); |
1037 |
|
|
|
1038 |
|
|
/* |
1039 |
|
|
* Note this function only prepares required structures for |
1040 |
|
|
* future resize. It's a quite different work to implement |
1041 |
|
|
* a utility like resize_ext2fs(8) which handles actual |
1042 |
|
|
* resize ops even on offline. |
1043 |
|
|
* |
1044 |
|
|
* Anyway, I'm not sure if there is any documentation about |
1045 |
|
|
* this resize ext2fs feature and related data structures, |
1046 |
|
|
* and I've written this function based on things what I see |
1047 |
|
|
* on some existing implementation and real file system data |
1048 |
|
|
* created by existing tools. To be honest, they are not |
1049 |
|
|
* so easy to read, so I will try to implement it here without |
1050 |
|
|
* any dumb optimization for people who would eventually |
1051 |
|
|
* work on "yet another wheel" like resize_ext2fs(8). |
1052 |
|
|
*/ |
1053 |
|
|
|
1054 |
|
|
/* |
1055 |
|
|
* I'm not sure what type is appropriate for this inode. |
1056 |
|
|
* The release notes of e2fsprogs says they changed e2fsck to allow |
1057 |
|
|
* IFREG for RESIZEINO since a certain resize tool used it. Hmm. |
1058 |
|
|
*/ |
1059 |
|
|
node.e2di_mode = EXT2_IFREG | EXT2_RESIZEINOUMASK; |
1060 |
|
|
node.e2di_uid_low = geteuid(); |
1061 |
|
|
node.e2di_atime = (u_int32_t)tv->tv_sec; |
1062 |
|
|
node.e2di_ctime = (u_int32_t)tv->tv_sec; |
1063 |
|
|
node.e2di_mtime = (u_int32_t)tv->tv_sec; |
1064 |
|
|
node.e2di_gid_low = getegid(); |
1065 |
|
|
node.e2di_nlink = 1; |
1066 |
|
|
|
1067 |
|
|
/* |
1068 |
|
|
* To preserve the reserved group descriptor blocks, |
1069 |
|
|
* EXT2_RESIZEINO uses only double indirect reference |
1070 |
|
|
* blocks in its inode entries. |
1071 |
|
|
* |
1072 |
|
|
* All entries for direct, single indirect and triple |
1073 |
|
|
* indirect references are left zero'ed. Maybe it's safe |
1074 |
|
|
* because no write operation will happen with this inode. |
1075 |
|
|
* |
1076 |
|
|
* We have to allocate a block for the first level double |
1077 |
|
|
* indirect reference block. Indexes of inode entries in |
1078 |
|
|
* this first level dindirect block are corresponding to |
1079 |
|
|
* indexes of group descriptors including both used (e2fs_ngdb) |
1080 |
|
|
* and reserved (e2fs_reserved_ngdb) group descriptor blocks. |
1081 |
|
|
* |
1082 |
|
|
* Inode entries of indexes for used (e2fs_ngdb) descriptors are |
1083 |
|
|
* left zero'ed. Entries for reserved (e2fs_reserved_ngdb) ones |
1084 |
|
|
* have block numbers of actual reserved group descriptors |
1085 |
|
|
* allocated at block group zero. This means e2fs_reserved_ngdb |
1086 |
|
|
* blocks are reserved as the second level dindirect reference |
1087 |
|
|
* blocks, and they actually contain block numbers of indirect |
1088 |
|
|
* references. It may be safe since they don't have to keep any |
1089 |
|
|
* data yet. |
1090 |
|
|
* |
1091 |
|
|
* Each these second dindirect blocks (i.e. reserved group |
1092 |
|
|
* descriptor blocks in the first block group) should have |
1093 |
|
|
* block numbers of its backups in all other block groups. |
1094 |
|
|
* I.e. reserved_ngdb[0] block in block group 0 contains block |
1095 |
|
|
* numbers of resreved_ngdb[0] from group 1 through (e2fs_ncg - 1). |
1096 |
|
|
* The number of backups can be determined by the |
1097 |
|
|
* EXT2_ROCOMPAT_SPARSESUPER feature and cg_has_sb() macro |
1098 |
|
|
* as done in the above initcg() function. |
1099 |
|
|
*/ |
1100 |
|
|
|
1101 |
|
|
/* set e2di_size which occupies whole blocks through DINDIR blocks */ |
1102 |
|
|
isize = (uint64_t)sblock.e2fs_bsize * NDADDR + |
1103 |
|
|
(uint64_t)sblock.e2fs_bsize * NINDIR(&sblock) + |
1104 |
|
|
(uint64_t)sblock.e2fs_bsize * NINDIR(&sblock) * NINDIR(&sblock); |
1105 |
|
|
if (isize > UINT32_MAX && |
1106 |
|
|
(sblock.e2fs.e2fs_features_rocompat & |
1107 |
|
|
EXT2F_ROCOMPAT_LARGEFILE) == 0) { |
1108 |
|
|
/* XXX should enable it here and update all backups? */ |
1109 |
|
|
errx(EXIT_FAILURE, "%s: large_file rocompat feature is " |
1110 |
|
|
"required to enable resize feature for this filesystem\n", |
1111 |
|
|
__func__); |
1112 |
|
|
} |
1113 |
|
|
/* upper 32bit is stored into e2di_size_hi on REV1 feature */ |
1114 |
|
|
node.e2di_size = isize & UINT32_MAX; |
1115 |
|
|
node.e2di_size_hi = isize >> 32; |
1116 |
|
|
|
1117 |
|
|
#define SINGLE 0 /* index of single indirect block */ |
1118 |
|
|
#define DOUBLE 1 /* index of double indirect block */ |
1119 |
|
|
#define TRIPLE 2 /* index of triple indirect block */ |
1120 |
|
|
|
1121 |
|
|
/* zero out entries for direct references */ |
1122 |
|
|
for (i = 0; i < NDADDR; i++) |
1123 |
|
|
node.e2di_blocks[i] = 0; |
1124 |
|
|
/* also zero out entries for single and triple indirect references */ |
1125 |
|
|
node.e2di_blocks[NDADDR + SINGLE] = 0; |
1126 |
|
|
node.e2di_blocks[NDADDR + TRIPLE] = 0; |
1127 |
|
|
|
1128 |
|
|
/* allocate a block for the first level double indirect reference */ |
1129 |
|
|
node.e2di_blocks[NDADDR + DOUBLE] = |
1130 |
|
|
alloc(sblock.e2fs_bsize, node.e2di_mode); |
1131 |
|
|
if (node.e2di_blocks[NDADDR + DOUBLE] == 0) |
1132 |
|
|
errx(EXIT_FAILURE, "%s: Can't allocate a dindirect block", |
1133 |
|
|
__func__); |
1134 |
|
|
|
1135 |
|
|
/* account this first block */ |
1136 |
|
|
nblock = fsbtodb(&sblock, 1); |
1137 |
|
|
|
1138 |
|
|
/* allocate buffer to set data in the dindirect block */ |
1139 |
|
|
dindir_block = malloc(sblock.e2fs_bsize); |
1140 |
|
|
if (dindir_block == NULL) |
1141 |
|
|
errx(EXIT_FAILURE, |
1142 |
|
|
"%s: Can't allocate buffer for a dindirect block", |
1143 |
|
|
__func__); |
1144 |
|
|
|
1145 |
|
|
/* allocate buffer to set data in the group descriptor blocks */ |
1146 |
|
|
reserved_gdb = malloc(sblock.e2fs_bsize); |
1147 |
|
|
if (reserved_gdb == NULL) |
1148 |
|
|
errx(EXIT_FAILURE, |
1149 |
|
|
"%s: Can't allocate buffer for group descriptor blocks", |
1150 |
|
|
__func__); |
1151 |
|
|
|
1152 |
|
|
/* |
1153 |
|
|
* Setup block entries in the first level dindirect blocks |
1154 |
|
|
*/ |
1155 |
|
|
for (i = 0; i < sblock.e2fs_ngdb; i++) { |
1156 |
|
|
/* no need to handle used group descriptor blocks */ |
1157 |
|
|
dindir_block[i] = 0; |
1158 |
|
|
} |
1159 |
|
|
for (; i < sblock.e2fs_ngdb + sblock.e2fs.e2fs_reserved_ngdb; i++) { |
1160 |
|
|
/* |
1161 |
|
|
* point reserved group descriptor block in the first |
1162 |
|
|
* (i.e. master) block group |
1163 |
|
|
* |
1164 |
|
|
* XXX: e2fsprogs seem to use "(i % NINDIR(&sblock))" here |
1165 |
|
|
* to store maximum NINDIR(&sblock) reserved gdbs. |
1166 |
|
|
* I'm not sure what will be done on future filesystem |
1167 |
|
|
* shrink in that case on their way. |
1168 |
|
|
*/ |
1169 |
|
|
if (i >= NINDIR(&sblock)) |
1170 |
|
|
errx(EXIT_FAILURE, "%s: too many reserved " |
1171 |
|
|
"group descriptors (%u) for resize inode", |
1172 |
|
|
__func__, sblock.e2fs.e2fs_reserved_ngdb); |
1173 |
|
|
dindir_block[i] = |
1174 |
|
|
htole32(cgbase(&sblock, 0) + NBLOCK_SUPERBLOCK + i); |
1175 |
|
|
|
1176 |
|
|
/* |
1177 |
|
|
* Setup block entries in the second dindirect blocks |
1178 |
|
|
* (which are primary reserved group descriptor blocks) |
1179 |
|
|
* to point their backups. |
1180 |
|
|
*/ |
1181 |
|
|
for (n = 0, cylno = 1; cylno < sblock.e2fs_ncg; cylno++) { |
1182 |
|
|
/* skip block groups without backup */ |
1183 |
|
|
if ((sblock.e2fs.e2fs_features_rocompat & |
1184 |
|
|
EXT2F_ROCOMPAT_SPARSESUPER) != 0 && |
1185 |
|
|
cg_has_sb(cylno) == 0) |
1186 |
|
|
continue; |
1187 |
|
|
|
1188 |
|
|
if (n >= NINDIR(&sblock)) |
1189 |
|
|
errx(EXIT_FAILURE, "%s: too many block groups " |
1190 |
|
|
"for the resize feature", __func__); |
1191 |
|
|
/* |
1192 |
|
|
* These blocks are already reserved in |
1193 |
|
|
* initcg() so no need to use alloc() here. |
1194 |
|
|
*/ |
1195 |
|
|
reserved_gdb[n++] = htole32(cgbase(&sblock, cylno) + |
1196 |
|
|
NBLOCK_SUPERBLOCK + i); |
1197 |
|
|
nblock += fsbtodb(&sblock, 1); |
1198 |
|
|
} |
1199 |
|
|
for (; n < NINDIR(&sblock); n++) |
1200 |
|
|
reserved_gdb[n] = 0; |
1201 |
|
|
|
1202 |
|
|
/* write group descriptor block as the second dindirect refs */ |
1203 |
|
|
wtfs(fsbtodb(&sblock, letoh32(dindir_block[i])), |
1204 |
|
|
sblock.e2fs_bsize, reserved_gdb); |
1205 |
|
|
nblock += fsbtodb(&sblock, 1); |
1206 |
|
|
} |
1207 |
|
|
for (; i < NINDIR(&sblock); i++) { |
1208 |
|
|
/* leave trailing entries unallocated */ |
1209 |
|
|
dindir_block[i] = 0; |
1210 |
|
|
} |
1211 |
|
|
free(reserved_gdb); |
1212 |
|
|
|
1213 |
|
|
/* finally write the first level dindirect block */ |
1214 |
|
|
wtfs(fsbtodb(&sblock, node.e2di_blocks[NDADDR + DOUBLE]), |
1215 |
|
|
sblock.e2fs_bsize, dindir_block); |
1216 |
|
|
free(dindir_block); |
1217 |
|
|
|
1218 |
|
|
node.e2di_nblock = nblock; |
1219 |
|
|
iput(&node, EXT2_RESIZEINO); |
1220 |
|
|
} |
1221 |
|
|
|
1222 |
|
|
/* |
1223 |
|
|
* uint32_t alloc(uint32_t size, uint16_t mode) |
1224 |
|
|
* |
1225 |
|
|
* Allocate a block (from cylinder group 0) |
1226 |
|
|
* Reference: src/sys/ufs/ext2fs/ext2fs_alloc.c:ext2fs_alloccg() |
1227 |
|
|
*/ |
1228 |
|
|
uint32_t |
1229 |
|
|
alloc(uint32_t size, uint16_t mode) |
1230 |
|
|
{ |
1231 |
|
|
uint32_t loc, bno; |
1232 |
|
|
uint8_t *bbp; |
1233 |
|
|
uint len, map, i; |
1234 |
|
|
|
1235 |
|
|
if (gd[0].ext2bgd_nbfree == 0) |
1236 |
|
|
return 0; |
1237 |
|
|
|
1238 |
|
|
if (size > sblock.e2fs_bsize) |
1239 |
|
|
return 0; |
1240 |
|
|
|
1241 |
|
|
bbp = malloc(sblock.e2fs_bsize); |
1242 |
|
|
if (bbp == NULL) |
1243 |
|
|
return 0; |
1244 |
|
|
rdfs(fsbtodb(&sblock, gd[0].ext2bgd_b_bitmap), sblock.e2fs_bsize, bbp); |
1245 |
|
|
|
1246 |
|
|
/* XXX: kernel uses e2fs_fpg here */ |
1247 |
|
|
len = sblock.e2fs.e2fs_bpg / NBBY; |
1248 |
|
|
|
1249 |
|
|
#if 0 /* no need block allocation for root or lost+found dir */ |
1250 |
|
|
for (loc = 0; loc < len; loc++) { |
1251 |
|
|
if (bbp[loc] == 0) { |
1252 |
|
|
bno = loc * NBBY; |
1253 |
|
|
goto gotit; |
1254 |
|
|
} |
1255 |
|
|
} |
1256 |
|
|
#endif |
1257 |
|
|
|
1258 |
|
|
loc = skpc(~0U, len, bbp); |
1259 |
|
|
if (loc == 0) { |
1260 |
|
|
free(bbp); |
1261 |
|
|
return 0; |
1262 |
|
|
} |
1263 |
|
|
loc = len - loc; |
1264 |
|
|
map = bbp[loc]; |
1265 |
|
|
bno = loc * NBBY; |
1266 |
|
|
for (i = 0; i < NBBY; i++, bno++) { |
1267 |
|
|
if ((map & (1 << i)) == 0) |
1268 |
|
|
goto gotit; |
1269 |
|
|
} |
1270 |
|
|
free(bbp); |
1271 |
|
|
return 0; |
1272 |
|
|
|
1273 |
|
|
gotit: |
1274 |
|
|
if (isset(bbp, bno)) |
1275 |
|
|
errx(EXIT_FAILURE, "%s: inconsistent bitmap\n", __func__); |
1276 |
|
|
|
1277 |
|
|
setbit(bbp, bno); |
1278 |
|
|
wtfs(fsbtodb(&sblock, gd[0].ext2bgd_b_bitmap), sblock.e2fs_bsize, bbp); |
1279 |
|
|
free(bbp); |
1280 |
|
|
/* XXX: modified group descriptors won't be written into backups */ |
1281 |
|
|
gd[0].ext2bgd_nbfree--; |
1282 |
|
|
if ((mode & EXT2_IFDIR) != 0) |
1283 |
|
|
gd[0].ext2bgd_ndirs++; |
1284 |
|
|
sblock.e2fs.e2fs_fbcount--; |
1285 |
|
|
|
1286 |
|
|
return sblock.e2fs.e2fs_first_dblock + bno; |
1287 |
|
|
} |
1288 |
|
|
|
1289 |
|
|
/* |
1290 |
|
|
* void iput(struct ext2fs_dinode *ip, ino_t ino) |
1291 |
|
|
* |
1292 |
|
|
* Put an inode entry into the corresponding table. |
1293 |
|
|
*/ |
1294 |
|
|
static void |
1295 |
|
|
iput(struct ext2fs_dinode *ip, ino_t ino) |
1296 |
|
|
{ |
1297 |
|
|
daddr32_t d; |
1298 |
|
|
uint c, i; |
1299 |
|
|
struct ext2fs_dinode *dp; |
1300 |
|
|
uint8_t *bp; |
1301 |
|
|
|
1302 |
|
|
bp = malloc(sblock.e2fs_bsize); |
1303 |
|
|
if (bp == NULL) |
1304 |
|
|
errx(EXIT_FAILURE, "%s: can't allocate buffer for inode\n", |
1305 |
|
|
__func__); |
1306 |
|
|
|
1307 |
|
|
/* |
1308 |
|
|
* Reserved inodes are allocated and accounted in initcg() |
1309 |
|
|
* so skip checks of the bitmap and allocation for them. |
1310 |
|
|
*/ |
1311 |
|
|
if (ino >= EXT2_FIRSTINO) { |
1312 |
|
|
c = ino_to_cg(&sblock, ino); |
1313 |
|
|
|
1314 |
|
|
/* sanity check */ |
1315 |
|
|
if (gd[c].ext2bgd_nifree == 0) |
1316 |
|
|
errx(EXIT_FAILURE, |
1317 |
|
|
"%s: no free inode %" PRIu64 " in block group %u\n", |
1318 |
|
|
__func__, (uint64_t)ino, c); |
1319 |
|
|
|
1320 |
|
|
/* update inode bitmap */ |
1321 |
|
|
rdfs(fsbtodb(&sblock, gd[0].ext2bgd_i_bitmap), |
1322 |
|
|
sblock.e2fs_bsize, bp); |
1323 |
|
|
|
1324 |
|
|
/* more sanity */ |
1325 |
|
|
if (isset(bp, EXT2_INO_INDEX(ino))) |
1326 |
|
|
errx(EXIT_FAILURE, "%s: inode %" PRIu64 |
1327 |
|
|
" already in use\n", __func__, (uint64_t)ino); |
1328 |
|
|
setbit(bp, EXT2_INO_INDEX(ino)); |
1329 |
|
|
wtfs(fsbtodb(&sblock, gd[0].ext2bgd_i_bitmap), |
1330 |
|
|
sblock.e2fs_bsize, bp); |
1331 |
|
|
gd[c].ext2bgd_nifree--; |
1332 |
|
|
sblock.e2fs.e2fs_ficount--; |
1333 |
|
|
} |
1334 |
|
|
|
1335 |
|
|
if (ino >= sblock.e2fs.e2fs_ipg * sblock.e2fs_ncg) |
1336 |
|
|
errx(EXIT_FAILURE, "%s: inode value out of range (%" PRIu64 |
1337 |
|
|
").\n", __func__, (uint64_t)ino); |
1338 |
|
|
|
1339 |
|
|
/* update an inode entry in the table */ |
1340 |
|
|
d = fsbtodb(&sblock, ino_to_fsba(&sblock, ino)); |
1341 |
|
|
rdfs(d, sblock.e2fs_bsize, bp); |
1342 |
|
|
|
1343 |
|
|
dp = (struct ext2fs_dinode *)(bp + |
1344 |
|
|
inodesize * ino_to_fsbo(&sblock, ino)); |
1345 |
|
|
e2fs_isave(&sblock, ip, dp); |
1346 |
|
|
/* e2fs_i_bswap() doesn't swap e2di_blocks addrs */ |
1347 |
|
|
if ((ip->e2di_mode & EXT2_IFMT) != EXT2_IFLNK) { |
1348 |
|
|
for (i = 0; i < NDADDR + NIADDR; i++) |
1349 |
|
|
dp->e2di_blocks[i] = htole32(ip->e2di_blocks[i]); |
1350 |
|
|
} |
1351 |
|
|
/* If there is some bias in arc4random(), keep it. */ |
1352 |
|
|
dp->e2di_gen = htole32(arc4random()); |
1353 |
|
|
|
1354 |
|
|
wtfs(d, sblock.e2fs_bsize, bp); |
1355 |
|
|
free(bp); |
1356 |
|
|
} |
1357 |
|
|
|
1358 |
|
|
/* |
1359 |
|
|
* Read a block from the file system |
1360 |
|
|
*/ |
1361 |
|
|
void |
1362 |
|
|
rdfs(daddr32_t bno, int size, void *bf) |
1363 |
|
|
{ |
1364 |
|
|
int n; |
1365 |
|
|
off_t offset; |
1366 |
|
|
|
1367 |
|
|
offset = bno; |
1368 |
|
|
n = pread(fd, bf, size, offset * sectorsize); |
1369 |
|
|
if (n != size) |
1370 |
|
|
err(EXIT_FAILURE, "%s: read error for sector %" PRId64, |
1371 |
|
|
__func__, (int64_t)bno); |
1372 |
|
|
} |
1373 |
|
|
|
1374 |
|
|
/* |
1375 |
|
|
* Write a block to the file system |
1376 |
|
|
*/ |
1377 |
|
|
void |
1378 |
|
|
wtfs(daddr32_t bno, int size, void *bf) |
1379 |
|
|
{ |
1380 |
|
|
int n; |
1381 |
|
|
off_t offset; |
1382 |
|
|
|
1383 |
|
|
if (Nflag) |
1384 |
|
|
return; |
1385 |
|
|
offset = bno; |
1386 |
|
|
n = pwrite(fd, bf, size, offset * sectorsize); |
1387 |
|
|
if (n != size) |
1388 |
|
|
err(EXIT_FAILURE, "%s: write error for sector %" PRId64, |
1389 |
|
|
__func__, (int64_t)bno); |
1390 |
|
|
} |
1391 |
|
|
|
1392 |
|
|
int |
1393 |
|
|
ilog2(uint val) |
1394 |
|
|
{ |
1395 |
|
|
|
1396 |
|
|
if (val == 0 || !powerof2(val)) |
1397 |
|
|
errx(EXIT_FAILURE, "%s: %u is not a power of 2\n", |
1398 |
|
|
__func__, val); |
1399 |
|
|
|
1400 |
|
|
return ffs(val) - 1; |
1401 |
|
|
} |
1402 |
|
|
|
1403 |
|
|
/* |
1404 |
|
|
* int skpc(int mask, size_t size, uint8_t *cp) |
1405 |
|
|
* |
1406 |
|
|
* Locate an unsigned character of value mask inside cp[]. |
1407 |
|
|
* (from src/sys/lib/libkern/skpc.c) |
1408 |
|
|
*/ |
1409 |
|
|
int |
1410 |
|
|
skpc(int mask, size_t size, uint8_t *cp) |
1411 |
|
|
{ |
1412 |
|
|
uint8_t *end; |
1413 |
|
|
|
1414 |
|
|
end = &cp[size]; |
1415 |
|
|
while (cp < end && *cp == (uint8_t)mask) |
1416 |
|
|
cp++; |
1417 |
|
|
|
1418 |
|
|
return end - cp; |
1419 |
|
|
} |
1420 |
|
|
|
1421 |
|
|
static void |
1422 |
|
|
uuid_get(struct m_ext2fs *sb) |
1423 |
|
|
{ |
1424 |
|
|
unsigned char buf[sizeof(sb->e2fs.e2fs_uuid)]; |
1425 |
|
|
|
1426 |
|
|
arc4random_buf(buf, sizeof(buf)); |
1427 |
|
|
/* UUID version 4: random */ |
1428 |
|
|
buf[6] &= 0x0f; |
1429 |
|
|
buf[6] |= 0x40; |
1430 |
|
|
/* RFC4122 variant */ |
1431 |
|
|
buf[8] &= 0x3f; |
1432 |
|
|
buf[8] |= 0x80; |
1433 |
|
|
memcpy(sb->e2fs.e2fs_uuid, buf, sizeof(sb->e2fs.e2fs_uuid)); |
1434 |
|
|
} |