Home | History | Annotate | Line # | Download | only in installboot
ffs.c revision 1.12
      1 /*	$NetBSD: ffs.c,v 1.12 2003/05/30 09:22:50 dsl Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2002 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Matt Fredette.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. All advertising materials mentioning features or use of this software
     19  *    must display the following acknowledgement:
     20  *	This product includes software developed by the NetBSD
     21  *	Foundation, Inc. and its contributors.
     22  * 4. Neither the name of The NetBSD Foundation nor the names of its
     23  *    contributors may be used to endorse or promote products derived
     24  *    from this software without specific prior written permission.
     25  *
     26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     36  * POSSIBILITY OF SUCH DAMAGE.
     37  */
     38 
     39 #include <sys/cdefs.h>
     40 #if defined(__RCSID) && !defined(__lint)
     41 __RCSID("$NetBSD: ffs.c,v 1.12 2003/05/30 09:22:50 dsl Exp $");
     42 #endif	/* !__lint */
     43 
     44 #include <sys/param.h>
     45 #include <sys/mount.h>
     46 
     47 #include <assert.h>
     48 #include <err.h>
     49 #include <errno.h>
     50 #include <fcntl.h>
     51 #include <stdarg.h>
     52 #include <stdio.h>
     53 #include <stdlib.h>
     54 #include <string.h>
     55 #include <unistd.h>
     56 
     57 #include "installboot.h"
     58 
     59 #undef DIRBLKSIZ
     60 
     61 #include <ufs/ufs/dinode.h>
     62 #include <ufs/ufs/dir.h>
     63 #include <ufs/ffs/fs.h>
     64 #include <ufs/ffs/ffs_extern.h>
     65 #include <ufs/ufs/ufs_bswap.h>
     66 
     67 static int	ffs_read_disk_block(ib_params *, uint64_t, int, char *);
     68 static int	ffs_find_disk_blocks_ufs1(ib_params *, ino_t,
     69 		    int (*)(ib_params *, void *, uint64_t, uint32_t), void *);
     70 static int	ffs_find_disk_blocks_ufs2(ib_params *, ino_t,
     71 		    int (*)(ib_params *, void *, uint64_t, uint32_t), void *);
     72 static int	ffs_findstage2_ino(ib_params *, void *, uint64_t, uint32_t);
     73 static int	ffs_findstage2_blocks(ib_params *, void *, uint64_t, uint32_t);
     74 
     75 static int is_ufs2;
     76 
     77 
     78 /* This reads a disk block from the filesystem. */
     79 static int
     80 ffs_read_disk_block(ib_params *params, uint64_t blkno, int size, char *blk)
     81 {
     82 	int	rv;
     83 
     84 	assert(params != NULL);
     85 	assert(blk != NULL);
     86 	assert(params->filesystem != NULL);
     87 	assert(params->fsfd != -1);
     88 	assert(blkno >= 0);
     89 	assert(size > 0);
     90 	assert(blk != NULL);
     91 
     92 	rv = pread(params->fsfd, blk, size, blkno * DEV_BSIZE);
     93 	if (rv == -1) {
     94 		warn("Reading block %llu in `%s'",
     95 		    (unsigned long long)blkno, params->filesystem);
     96 		return (0);
     97 	} else if (rv != size) {
     98 		warnx("Reading block %llu in `%s': short read",
     99 		    (unsigned long long)blkno, params->filesystem);
    100 		return (0);
    101 	}
    102 
    103 	return (1);
    104 }
    105 
    106 /*
    107  * This iterates over the data blocks belonging to an inode,
    108  * making a callback each iteration with the disk block number
    109  * and the size.
    110  */
    111 static int
    112 ffs_find_disk_blocks_ufs1(ib_params *params, ino_t ino,
    113 	int (*callback)(ib_params *, void *, uint64_t, uint32_t),
    114 	void *state)
    115 {
    116 	char		sbbuf[SBLOCKSIZE];
    117 	struct fs	*fs;
    118 	char		inodebuf[MAXBSIZE];
    119 	struct ufs1_dinode	*inode;
    120 	int		level_i;
    121 	int32_t	blk, lblk, nblk;
    122 	int		rv;
    123 #define LEVELS 4
    124 	struct {
    125 		int32_t		*blknums;
    126 		unsigned long	blkcount;
    127 		char		diskbuf[MAXBSIZE];
    128 	} level[LEVELS];
    129 
    130 	assert(params != NULL);
    131 	assert(params->fstype != NULL);
    132 	assert(callback != NULL);
    133 	assert(state != NULL);
    134 
    135 	/* Read the superblock. */
    136 	if (!ffs_read_disk_block(params, params->fstype->sblockloc, SBLOCKSIZE,
    137 	    sbbuf))
    138 		return (0);
    139 	fs = (struct fs *)sbbuf;
    140 	if (params->fstype->needswap)
    141 		ffs_sb_swap(fs, fs);
    142 
    143 	if (fs->fs_inopb <= 0) {
    144 		warnx("Bad inopb %d in superblock in `%s'",
    145 		    fs->fs_inopb, params->filesystem);
    146 		return (0);
    147 	}
    148 
    149 	/* Read the inode. */
    150 	if (! ffs_read_disk_block(params, fsbtodb(fs, ino_to_fsba(fs, ino)),
    151 		fs->fs_bsize, inodebuf))
    152 		return (0);
    153 	inode = (struct ufs1_dinode *)inodebuf;
    154 	inode += ino_to_fsbo(fs, ino);
    155 	if (params->fstype->needswap)
    156 		ffs_dinode1_swap(inode, inode);
    157 
    158 	/* Get the block count and initialize for our block walk. */
    159 	nblk = howmany(inode->di_size, fs->fs_bsize);
    160 	lblk = 0;
    161 	level_i = 0;
    162 	level[0].blknums = &inode->di_db[0];
    163 	level[0].blkcount = NDADDR;
    164 	level[1].blknums = &inode->di_ib[0];
    165 	level[1].blkcount = 1;
    166 	level[2].blknums = &inode->di_ib[1];
    167 	level[2].blkcount = 1;
    168 	level[3].blknums = &inode->di_ib[2];
    169 	level[3].blkcount = 1;
    170 
    171 	/* Walk the data blocks. */
    172 	while (nblk > 0) {
    173 
    174 		/*
    175 		 * If there are no more blocks at this indirection
    176 		 * level, move up one indirection level and loop.
    177 		 */
    178 		if (level[level_i].blkcount == 0) {
    179 			if (++level_i == LEVELS)
    180 				break;
    181 			continue;
    182 		}
    183 
    184 		/* Get the next block at this level. */
    185 		blk = *(level[level_i].blknums++);
    186 		level[level_i].blkcount--;
    187 		if (params->fstype->needswap)
    188 			blk = bswap32(blk);
    189 
    190 #if 0
    191 		fprintf(stderr, "ino %lu blk %lu level %d\n", ino, blk,
    192 		    level_i);
    193 #endif
    194 
    195 		/*
    196 		 * If we're not at the direct level, descend one
    197 		 * level, read in that level's new block list,
    198 		 * and loop.
    199 		 */
    200 		if (level_i > 0) {
    201 			level_i--;
    202 			if (blk == 0)
    203 				memset(level[level_i].diskbuf, 0, MAXBSIZE);
    204 			else if (! ffs_read_disk_block(params,
    205 				fsbtodb(fs, blk),
    206 				fs->fs_bsize, level[level_i].diskbuf))
    207 				return (0);
    208 			/* XXX ondisk32 */
    209 			level[level_i].blknums =
    210 				(int32_t *)level[level_i].diskbuf;
    211 			level[level_i].blkcount = NINDIR(fs);
    212 			continue;
    213 		}
    214 
    215 		/* blk is the next direct level block. */
    216 #if 0
    217 		fprintf(stderr, "ino %lu db %lu blksize %lu\n", ino,
    218 		    fsbtodb(fs, blk), sblksize(fs, inode->di_size, lblk));
    219 #endif
    220 		rv = (*callback)(params, state,
    221 		    fsbtodb(fs, blk), sblksize(fs, inode->di_size, lblk));
    222 		lblk++;
    223 		nblk--;
    224 		if (rv != 1)
    225 			return (rv);
    226 	}
    227 
    228 	if (nblk != 0) {
    229 		warnx("Inode %d in `%s' ran out of blocks?", ino,
    230 		    params->filesystem);
    231 		return (0);
    232 	}
    233 
    234 	return (1);
    235 }
    236 
    237 /*
    238  * This iterates over the data blocks belonging to an inode,
    239  * making a callback each iteration with the disk block number
    240  * and the size.
    241  */
    242 static int
    243 ffs_find_disk_blocks_ufs2(ib_params *params, ino_t ino,
    244 	int (*callback)(ib_params *, void *, uint64_t, uint32_t),
    245 	void *state)
    246 {
    247 	char		sbbuf[SBLOCKSIZE];
    248 	struct fs	*fs;
    249 	char		inodebuf[MAXBSIZE];
    250 	struct ufs2_dinode	*inode;
    251 	int		level_i;
    252 	int64_t	blk, lblk, nblk;
    253 	int		rv;
    254 #define LEVELS 4
    255 	struct {
    256 		int64_t		*blknums;
    257 		unsigned long	blkcount;
    258 		char		diskbuf[MAXBSIZE];
    259 	} level[LEVELS];
    260 
    261 	assert(params != NULL);
    262 	assert(params->fstype != NULL);
    263 	assert(callback != NULL);
    264 	assert(state != NULL);
    265 
    266 	/* Read the superblock. */
    267 	if (!ffs_read_disk_block(params, params->fstype->sblockloc, SBLOCKSIZE,
    268 	    sbbuf))
    269 		return (0);
    270 	fs = (struct fs *)sbbuf;
    271 	if (params->fstype->needswap)
    272 		ffs_sb_swap(fs, fs);
    273 
    274 	if (fs->fs_inopb <= 0) {
    275 		warnx("Bad inopb %d in superblock in `%s'",
    276 		    fs->fs_inopb, params->filesystem);
    277 		return (0);
    278 	}
    279 
    280 	/* Read the inode. */
    281 	if (! ffs_read_disk_block(params, fsbtodb(fs, ino_to_fsba(fs, ino)),
    282 		fs->fs_bsize, inodebuf))
    283 		return (0);
    284 	inode = (struct ufs2_dinode *)inodebuf;
    285 	inode += ino_to_fsbo(fs, ino);
    286 	if (params->fstype->needswap)
    287 		ffs_dinode2_swap(inode, inode);
    288 
    289 	/* Get the block count and initialize for our block walk. */
    290 	nblk = howmany(inode->di_size, fs->fs_bsize);
    291 	lblk = 0;
    292 	level_i = 0;
    293 	level[0].blknums = &inode->di_db[0];
    294 	level[0].blkcount = NDADDR;
    295 	level[1].blknums = &inode->di_ib[0];
    296 	level[1].blkcount = 1;
    297 	level[2].blknums = &inode->di_ib[1];
    298 	level[2].blkcount = 1;
    299 	level[3].blknums = &inode->di_ib[2];
    300 	level[3].blkcount = 1;
    301 
    302 	/* Walk the data blocks. */
    303 	while (nblk > 0) {
    304 
    305 		/*
    306 		 * If there are no more blocks at this indirection
    307 		 * level, move up one indirection level and loop.
    308 		 */
    309 		if (level[level_i].blkcount == 0) {
    310 			if (++level_i == LEVELS)
    311 				break;
    312 			continue;
    313 		}
    314 
    315 		/* Get the next block at this level. */
    316 		blk = *(level[level_i].blknums++);
    317 		level[level_i].blkcount--;
    318 		if (params->fstype->needswap)
    319 			blk = bswap64(blk);
    320 
    321 #if 0
    322 		fprintf(stderr, "ino %lu blk %llu level %d\n", ino,
    323 		    (unsigned long long)blk, level_i);
    324 #endif
    325 
    326 		/*
    327 		 * If we're not at the direct level, descend one
    328 		 * level, read in that level's new block list,
    329 		 * and loop.
    330 		 */
    331 		if (level_i > 0) {
    332 			level_i--;
    333 			if (blk == 0)
    334 				memset(level[level_i].diskbuf, 0, MAXBSIZE);
    335 			else if (! ffs_read_disk_block(params,
    336 				fsbtodb(fs, blk),
    337 				fs->fs_bsize, level[level_i].diskbuf))
    338 				return (0);
    339 			level[level_i].blknums =
    340 				(int64_t *)level[level_i].diskbuf;
    341 			level[level_i].blkcount = NINDIR(fs);
    342 			continue;
    343 		}
    344 
    345 		/* blk is the next direct level block. */
    346 #if 0
    347 		fprintf(stderr, "ino %lu db %llu blksize %lu\n", ino,
    348 		    fsbtodb(fs, blk), sblksize(fs, inode->di_size, lblk));
    349 #endif
    350 		rv = (*callback)(params, state,
    351 		    fsbtodb(fs, blk), sblksize(fs, inode->di_size, lblk));
    352 		lblk++;
    353 		nblk--;
    354 		if (rv != 1)
    355 			return (rv);
    356 	}
    357 
    358 	if (nblk != 0) {
    359 		warnx("Inode %d in `%s' ran out of blocks?", ino,
    360 		    params->filesystem);
    361 		return (0);
    362 	}
    363 
    364 	return (1);
    365 }
    366 
    367 /*
    368  * This callback reads a block of the root directory,
    369  * searches for an entry for the secondary bootstrap,
    370  * and saves the inode number if one is found.
    371  */
    372 static int
    373 ffs_findstage2_ino(ib_params *params, void *_ino,
    374 	uint64_t blk, uint32_t blksize)
    375 {
    376 	char		dirbuf[MAXBSIZE];
    377 	struct direct	*de, *ede;
    378 	uint32_t	ino;
    379 
    380 	assert(params != NULL);
    381 	assert(params->fstype != NULL);
    382 	assert(params->stage2 != NULL);
    383 	assert(_ino != NULL);
    384 
    385 	/* Skip directory holes. */
    386 	if (blk == 0)
    387 		return (1);
    388 
    389 	/* Read the directory block. */
    390 	if (! ffs_read_disk_block(params, blk, blksize, dirbuf))
    391 		return (0);
    392 
    393 	/* Loop over the directory entries. */
    394 	de = (struct direct *)&dirbuf[0];
    395 	ede = (struct direct *)&dirbuf[blksize];
    396 	while (de < ede) {
    397 		ino = de->d_ino;
    398 		if (params->fstype->needswap) {
    399 			ino = bswap32(ino);
    400 			de->d_reclen = bswap16(de->d_reclen);
    401 		}
    402 		if (ino != 0 && strcmp(de->d_name, params->stage2) == 0) {
    403 			*((uint32_t *)_ino) = ino;
    404 			return (2);
    405 		}
    406 		if (de->d_reclen == 0)
    407 			break;
    408 		de = (struct direct *)((char *)de + de->d_reclen);
    409 	}
    410 
    411 	return (1);
    412 }
    413 
    414 struct findblks_state {
    415 	uint32_t	maxblk;
    416 	uint32_t	nblk;
    417 	ib_block	*blocks;
    418 };
    419 
    420 /* This callback records the blocks of the secondary bootstrap. */
    421 static int
    422 ffs_findstage2_blocks(ib_params *params, void *_state,
    423 	uint64_t blk, uint32_t blksize)
    424 {
    425 	struct findblks_state *state = _state;
    426 
    427 	assert(params != NULL);
    428 	assert(params->stage2 != NULL);
    429 	assert(_state != NULL);
    430 
    431 	if (state->nblk == state->maxblk) {
    432 		warnx("Secondary bootstrap `%s' has too many blocks (max %d)",
    433 		    params->stage2, state->maxblk);
    434 		return (0);
    435 	}
    436 	state->blocks[state->nblk].block = blk;
    437 	state->blocks[state->nblk].blocksize = blksize;
    438 	state->nblk++;
    439 	return (1);
    440 }
    441 
    442 /*
    443  *	publically visible functions
    444  */
    445 
    446 static off_t sblock_try[] = SBLOCKSEARCH;
    447 
    448 int
    449 ffs_match(ib_params *params)
    450 {
    451 	char		sbbuf[SBLOCKSIZE];
    452 	struct fs	*fs;
    453 	int i;
    454 	off_t loc;
    455 
    456 	assert(params != NULL);
    457 	assert(params->fstype != NULL);
    458 
    459 	fs = (struct fs *)sbbuf;
    460 	for (i = 0; sblock_try[i] != -1; i++) {
    461 		loc = sblock_try[i] / DEV_BSIZE;
    462 		if (!ffs_read_disk_block(params, loc, SBLOCKSIZE, sbbuf))
    463 			continue;
    464 		switch (fs->fs_magic) {
    465 		case FS_UFS2_MAGIC:
    466 			is_ufs2 = 1;
    467 			/* FALLTHROUGH */
    468 		case FS_UFS1_MAGIC:
    469 			params->fstype->needswap = 0;
    470 			params->fstype->blocksize = fs->fs_bsize;
    471 			params->fstype->sblockloc = loc;
    472 			return (1);
    473 		case FS_UFS2_MAGIC_SWAPPED:
    474 			is_ufs2 = 1;
    475 			/* FALLTHROUGH */
    476 		case FS_UFS1_MAGIC_SWAPPED:
    477 			params->fstype->needswap = 1;
    478 			params->fstype->blocksize = bswap32(fs->fs_bsize);
    479 			params->fstype->sblockloc = loc;
    480 			return (1);
    481 		default:
    482 			continue;
    483 		}
    484 	}
    485 
    486 	return (0);
    487 }
    488 
    489 int
    490 ffs_findstage2(ib_params *params, uint32_t *maxblk, ib_block *blocks)
    491 {
    492 	int			rv;
    493 	uint32_t		ino;
    494 	struct findblks_state	state;
    495 
    496 	assert(params != NULL);
    497 	assert(params->stage2 != NULL);
    498 	assert(maxblk != NULL);
    499 	assert(blocks != NULL);
    500 
    501 	if (params->flags & IB_STAGE2START)
    502 		return (hardcode_stage2(params, maxblk, blocks));
    503 
    504 	/* The secondary bootstrap must be clearly in /. */
    505 	if (params->stage2[0] == '/')
    506 		params->stage2++;
    507 	if (strchr(params->stage2, '/') != NULL) {
    508 		warnx("The secondary bootstrap `%s' must be in /",
    509 		    params->stage2);
    510 		return (0);
    511 	}
    512 
    513 	/* Get the inode number of the secondary bootstrap. */
    514 	if (is_ufs2)
    515 		rv = ffs_find_disk_blocks_ufs2(params, ROOTINO,
    516 		    ffs_findstage2_ino, &ino);
    517 	else
    518 		rv = ffs_find_disk_blocks_ufs1(params, ROOTINO,
    519 		    ffs_findstage2_ino, &ino);
    520 	if (rv != 2) {
    521 		warnx("Could not find secondary bootstrap `%s' in `%s'",
    522 		    params->stage2, params->filesystem);
    523 		return (0);
    524 	}
    525 
    526 	/* Record the disk blocks of the secondary bootstrap. */
    527 	state.maxblk = *maxblk;
    528 	state.nblk = 0;
    529 	state.blocks = blocks;
    530 	if (is_ufs2)
    531 		rv = ffs_find_disk_blocks_ufs2(params, ino,
    532 		    ffs_findstage2_blocks, &state);
    533 	else
    534 		rv = ffs_find_disk_blocks_ufs1(params, ino,
    535 		    ffs_findstage2_blocks, &state);
    536 	if (! rv) {
    537 		return (0);
    538 	}
    539 
    540 	*maxblk = state.nblk;
    541 	return (1);
    542 }
    543