ext2fs.c revision 1.3 1 /* $NetBSD: ext2fs.c,v 1.3 2008/10/12 16:03:27 apb Exp $ */
2
3 /*
4 * Copyright (c) 1997 Manuel Bouyer.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by Manuel Bouyer.
17 * 4. The name of the author may not be used to endorse or promote products
18 * derived from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 /*-
33 * Copyright (c) 2002 The NetBSD Foundation, Inc.
34 * All rights reserved.
35 *
36 * This code is derived from software contributed to The NetBSD Foundation
37 * by Matt Fredette.
38 *
39 * Redistribution and use in source and binary forms, with or without
40 * modification, are permitted provided that the following conditions
41 * are met:
42 * 1. Redistributions of source code must retain the above copyright
43 * notice, this list of conditions and the following disclaimer.
44 * 2. Redistributions in binary form must reproduce the above copyright
45 * notice, this list of conditions and the following disclaimer in the
46 * documentation and/or other materials provided with the distribution.
47 *
48 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
49 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
50 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
51 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
52 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
53 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
54 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
55 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
56 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
57 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
58 * POSSIBILITY OF SUCH DAMAGE.
59 */
60
61 #if HAVE_NBTOOL_CONFIG_H
62 #include "nbtool_config.h"
63 #endif
64
65 #include <sys/cdefs.h>
66 #if defined(__RCSID) && !defined(__lint)
67 __RCSID("$NetBSD: ext2fs.c,v 1.3 2008/10/12 16:03:27 apb Exp $");
68 #endif /* !__lint */
69
70 #include <sys/param.h>
71
72 #if !HAVE_NBTOOL_CONFIG_H
73 #include <sys/mount.h>
74 #endif
75
76 #include <assert.h>
77 #include <err.h>
78 #include <errno.h>
79 #include <fcntl.h>
80 #include <stdarg.h>
81 #include <stdio.h>
82 #include <stdlib.h>
83 #include <string.h>
84 #include <unistd.h>
85
86 #include "installboot.h"
87
88 #include <ufs/ext2fs/ext2fs_dinode.h>
89 #include <ufs/ext2fs/ext2fs_dir.h>
90 #include <ufs/ext2fs/ext2fs.h>
91
92 static int ext2fs_read_disk_block(ib_params *, uint64_t, int, uint8_t []);
93 static int ext2fs_read_sblock(ib_params *, struct m_ext2fs *fs);
94 static int ext2fs_read_gdblock(ib_params *, struct m_ext2fs *fs);
95 static int ext2fs_find_disk_blocks(ib_params *, ino_t,
96 int (*)(ib_params *, void *, uint64_t, uint32_t), void *);
97 static int ext2fs_findstage2_ino(ib_params *, void *, uint64_t, uint32_t);
98 static int ext2fs_findstage2_blocks(ib_params *, void *, uint64_t,
99 uint32_t);
100
101
102 /* This reads a disk block from the file system. */
103 /* XXX: should be shared with ffs.c? */
104 static int
105 ext2fs_read_disk_block(ib_params *params, uint64_t blkno, int size,
106 uint8_t blk[])
107 {
108 int rv;
109
110 assert(params != NULL);
111 assert(params->filesystem != NULL);
112 assert(params->fsfd != -1);
113 assert(size > 0);
114 assert(blk != NULL);
115
116 rv = pread(params->fsfd, blk, size, blkno * DEV_BSIZE);
117 if (rv == -1) {
118 warn("Reading block %llu in `%s'",
119 (unsigned long long)blkno, params->filesystem);
120 return 0;
121 } else if (rv != size) {
122 warnx("Reading block %llu in `%s': short read",
123 (unsigned long long)blkno, params->filesystem);
124 return 0;
125 }
126
127 return 1;
128 }
129
130 static int
131 ext2fs_read_sblock(ib_params *params, struct m_ext2fs *fs)
132 {
133 uint8_t sbbuf[SBSIZE];
134
135 if (ext2fs_read_disk_block(params, SBOFF / DEV_BSIZE, SBSIZE,
136 sbbuf) == 0)
137
138 e2fs_sbload((void *)sbbuf, &fs->e2fs);
139
140 if (fs->e2fs.e2fs_magic != E2FS_MAGIC)
141 return 0;
142
143 if (fs->e2fs.e2fs_rev > E2FS_REV1 ||
144 (fs->e2fs.e2fs_rev == E2FS_REV1 &&
145 (fs->e2fs.e2fs_first_ino != EXT2_FIRSTINO ||
146 fs->e2fs.e2fs_inode_size != EXT2_DINODE_SIZE ||
147 (fs->e2fs.e2fs_features_incompat & ~EXT2F_INCOMPAT_SUPP) != 0)))
148 return 0;
149
150 fs->e2fs_ncg =
151 howmany(fs->e2fs.e2fs_bcount - fs->e2fs.e2fs_first_dblock,
152 fs->e2fs.e2fs_bpg);
153 /* XXX assume hw bsize = 512 */
154 fs->e2fs_fsbtodb = fs->e2fs.e2fs_log_bsize + 1;
155 fs->e2fs_bsize = MINBSIZE << fs->e2fs.e2fs_log_bsize;
156 fs->e2fs_bshift = LOG_MINBSIZE + fs->e2fs.e2fs_log_bsize;
157 fs->e2fs_qbmask = fs->e2fs_bsize - 1;
158 fs->e2fs_bmask = ~fs->e2fs_qbmask;
159 fs->e2fs_ngdb =
160 howmany(fs->e2fs_ncg, fs->e2fs_bsize / sizeof(struct ext2_gd));
161 fs->e2fs_ipb = fs->e2fs_bsize / EXT2_DINODE_SIZE;
162 fs->e2fs_itpg = fs->e2fs.e2fs_ipg / fs->e2fs_ipb;
163
164 return 1;
165 }
166
167 static int
168 ext2fs_read_gdblock(ib_params *params, struct m_ext2fs *fs)
169 {
170 uint8_t gdbuf[MAXBSIZE];
171 uint32_t gdpb;
172 int i;
173
174 gdpb = fs->e2fs_bsize / sizeof(struct ext2_gd);
175
176 for (i = 0; i < fs->e2fs_ngdb; i++) {
177 if (ext2fs_read_disk_block(params, fsbtodb(fs,
178 fs->e2fs.e2fs_first_dblock + 1 /* superblock */ + i),
179 SBSIZE, gdbuf) == 0)
180 return 0;
181
182 e2fs_cgload((struct ext2_gd *)gdbuf, &fs->e2fs_gd[gdpb * i],
183 (i == (fs->e2fs_ngdb - 1)) ?
184 (fs->e2fs_ncg - gdpb * i) * sizeof(struct ext2_gd):
185 fs->e2fs_bsize);
186 }
187
188 return 1;
189 }
190
191 /*
192 * This iterates over the data blocks belonging to an inode,
193 * making a callback each iteration with the disk block number
194 * and the size.
195 */
196 static int
197 ext2fs_find_disk_blocks(ib_params *params, ino_t ino,
198 int (*callback)(ib_params *, void *, uint64_t, uint32_t),
199 void *state)
200 {
201 uint8_t sbbuf[sizeof(struct m_ext2fs)];
202 struct m_ext2fs *fs;
203 uint8_t inodebuf[MAXBSIZE];
204 struct ext2fs_dinode inode_store, *inode;
205 int level_i;
206 int32_t blk, lblk, nblk;
207 int rv;
208 #define LEVELS 4
209 struct {
210 uint32_t *blknums;
211 unsigned long blkcount;
212 uint8_t diskbuf[MAXBSIZE];
213 } level[LEVELS];
214
215 assert(params != NULL);
216 assert(params->fstype != NULL);
217 assert(callback != NULL);
218 assert(state != NULL);
219
220 /* Read the superblock. */
221 fs = (void *)sbbuf;
222 if (ext2fs_read_sblock(params, fs) == 0)
223 return 0;
224
225 fs->e2fs_gd = malloc(sizeof(struct ext2_gd) * fs->e2fs_ncg);
226 if (fs->e2fs_gd == NULL) {
227 warnx("Can't allocate memofy for group descriptors");
228 return 0;
229 }
230
231 if (ext2fs_read_gdblock(params, fs) == 0) {
232 warnx("Can't read group descriptors");
233 return 0;
234 }
235
236 if (fs->e2fs_ipb <= 0) {
237 warnx("Bad ipb %d in superblock in `%s'",
238 fs->e2fs_ipb, params->filesystem);
239 return 0;
240 }
241
242 /* Read the inode. */
243 if (ext2fs_read_disk_block(params,
244 fsbtodb(fs, ino_to_fsba(fs, ino)) + params->fstype->offset,
245 fs->e2fs_bsize, inodebuf))
246 return 0;
247 inode = (void *)inodebuf;
248 e2fs_iload(&inode[ino_to_fsbo(fs, ino)], &inode_store);
249 inode = &inode_store;
250
251 /* Get the block count and initialize for our block walk. */
252 nblk = howmany(inode->e2di_size, fs->e2fs_bsize);
253 lblk = 0;
254 level_i = 0;
255 level[0].blknums = &inode->e2di_blocks[0];
256 level[0].blkcount = NDADDR;
257 level[1].blknums = &inode->e2di_blocks[NDADDR + 0];
258 level[1].blkcount = 1;
259 level[2].blknums = &inode->e2di_blocks[NDADDR + 1];
260 level[2].blkcount = 1;
261 level[3].blknums = &inode->e2di_blocks[NDADDR + 2];
262 level[3].blkcount = 1;
263
264 /* Walk the data blocks. */
265 while (nblk > 0) {
266
267 /*
268 * If there are no more blocks at this indirection
269 * level, move up one indirection level and loop.
270 */
271 if (level[level_i].blkcount == 0) {
272 if (++level_i == LEVELS)
273 break;
274 continue;
275 }
276
277 /* Get the next block at this level. */
278 blk = fs2h32(*(level[level_i].blknums++));
279 level[level_i].blkcount--;
280
281 #if 0
282 fprintf(stderr, "ino %lu blk %lu level %d\n", ino, blk,
283 level_i);
284 #endif
285
286 /*
287 * If we're not at the direct level, descend one
288 * level, read in that level's new block list,
289 * and loop.
290 */
291 if (level_i > 0) {
292 level_i--;
293 if (blk == 0)
294 memset(level[level_i].diskbuf, 0, MAXBSIZE);
295 else if (ext2fs_read_disk_block(params,
296 fsbtodb(fs, blk) + params->fstype->offset,
297 fs->e2fs_bsize, level[level_i].diskbuf) == 0)
298 return 0;
299 /* XXX ondisk32 */
300 level[level_i].blknums =
301 (uint32_t *)level[level_i].diskbuf;
302 level[level_i].blkcount = NINDIR(fs);
303 continue;
304 }
305
306 /* blk is the next direct level block. */
307 #if 0
308 fprintf(stderr, "ino %lu db %lu blksize %lu\n", ino,
309 fsbtodb(fs, blk), sblksize(fs, inode->di_size, lblk));
310 #endif
311 rv = (*callback)(params, state,
312 fsbtodb(fs, blk) + params->fstype->offset, fs->e2fs_bsize);
313 lblk++;
314 nblk--;
315 if (rv != 1)
316 return rv;
317 }
318
319 if (nblk != 0) {
320 warnx("Inode %llu in `%s' ran out of blocks?",
321 (unsigned long long)ino, params->filesystem);
322 return 0;
323 }
324
325 return 1;
326 }
327
328 /*
329 * This callback reads a block of the root directory,
330 * searches for an entry for the secondary bootstrap,
331 * and saves the inode number if one is found.
332 */
333 static int
334 ext2fs_findstage2_ino(ib_params *params, void *_ino,
335 uint64_t blk, uint32_t blksize)
336 {
337 uint8_t dirbuf[MAXBSIZE];
338 struct ext2fs_direct *de, *ede;
339 uint32_t ino;
340
341 assert(params != NULL);
342 assert(params->fstype != NULL);
343 assert(params->stage2 != NULL);
344 assert(_ino != NULL);
345
346 /* Skip directory holes. */
347 if (blk == 0)
348 return 1;
349
350 /* Read the directory block. */
351 if (ext2fs_read_disk_block(params, blk, blksize, dirbuf) == 0)
352 return 0;
353
354 /* Loop over the directory entries. */
355 de = (struct ext2fs_direct *)&dirbuf[0];
356 ede = (struct ext2fs_direct *)&dirbuf[blksize];
357 while (de < ede) {
358 ino = fs2h32(de->e2d_ino);
359 if (ino != 0 && strcmp(de->e2d_name, params->stage2) == 0) {
360 *((uint32_t *)_ino) = ino;
361 return (2);
362 }
363 if (fs2h16(de->e2d_reclen) == 0)
364 break;
365 de = (struct ext2fs_direct *)((char *)de +
366 fs2h16(de->e2d_reclen));
367 }
368
369 return 1;
370 }
371
372 struct findblks_state {
373 uint32_t maxblk;
374 uint32_t nblk;
375 ib_block *blocks;
376 };
377
378 /* This callback records the blocks of the secondary bootstrap. */
379 static int
380 ext2fs_findstage2_blocks(ib_params *params, void *_state,
381 uint64_t blk, uint32_t blksize)
382 {
383 struct findblks_state *state = _state;
384
385 assert(params != NULL);
386 assert(params->stage2 != NULL);
387 assert(_state != NULL);
388
389 if (state->nblk == state->maxblk) {
390 warnx("Secondary bootstrap `%s' has too many blocks (max %d)",
391 params->stage2, state->maxblk);
392 return (0);
393 }
394 state->blocks[state->nblk].block = blk;
395 state->blocks[state->nblk].blocksize = blksize;
396 state->nblk++;
397 return 1;
398 }
399
400 /*
401 * publicly visible functions
402 */
403
404 int
405 ext2fs_match(ib_params *params)
406 {
407 uint8_t sbbuf[sizeof(struct m_ext2fs)];
408 struct m_ext2fs *fs;
409
410 assert(params != NULL);
411 assert(params->fstype != NULL);
412
413 /* Read the superblock. */
414 fs = (void *)sbbuf;
415 if (ext2fs_read_sblock(params, fs) == 0)
416 return 0;
417
418 params->fstype->needswap = 0;
419 params->fstype->blocksize = fs->e2fs_bsize;
420 params->fstype->offset = 0;
421
422 return 1;
423 }
424
425 int
426 ext2fs_findstage2(ib_params *params, uint32_t *maxblk, ib_block *blocks)
427 {
428 int rv;
429 uint32_t ino;
430 struct findblks_state state;
431
432 assert(params != NULL);
433 assert(params->stage2 != NULL);
434 assert(maxblk != NULL);
435 assert(blocks != NULL);
436
437 if (params->flags & IB_STAGE2START)
438 return hardcode_stage2(params, maxblk, blocks);
439
440 /* The secondary bootstrap must be clearly in /. */
441 if (params->stage2[0] == '/')
442 params->stage2++;
443 if (strchr(params->stage2, '/') != NULL) {
444 warnx("The secondary bootstrap `%s' must be in /",
445 params->stage2);
446 warnx("(Path must be relative to the file system in `%s')",
447 params->filesystem);
448 return 0;
449 }
450
451 /* Get the inode number of the secondary bootstrap. */
452 rv = ext2fs_find_disk_blocks(params, EXT2_ROOTINO,
453 ext2fs_findstage2_ino, &ino);
454 if (rv != 2) {
455 warnx("Could not find secondary bootstrap `%s' in `%s'",
456 params->stage2, params->filesystem);
457 warnx("(Path must be relative to the file system in `%s')",
458 params->filesystem);
459 return 0;
460 }
461
462 /* Record the disk blocks of the secondary bootstrap. */
463 state.maxblk = *maxblk;
464 state.nblk = 0;
465 state.blocks = blocks;
466 rv = ext2fs_find_disk_blocks(params, ino,
467 ext2fs_findstage2_blocks, &state);
468 if (rv == 0)
469 return 0;
470
471 *maxblk = state.nblk;
472 return 1;
473 }
474