Home | History | Annotate | Line # | Download | only in installboot
ffs.c revision 1.8
      1 /*	$NetBSD: ffs.c,v 1.8 2003/04/02 10:39:48 fvdl 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.8 2003/04/02 10:39:48 fvdl 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'", blkno, params->filesystem);
     95 		return (0);
     96 	} else if (rv != size) {
     97 		warnx("Reading block %llu in `%s': short read", blkno,
     98 		    params->filesystem);
     99 		return (0);
    100 	}
    101 
    102 	return (1);
    103 }
    104 
    105 /*
    106  * This iterates over the data blocks belonging to an inode,
    107  * making a callback each iteration with the disk block number
    108  * and the size.
    109  */
    110 static int
    111 ffs_find_disk_blocks_ufs1(ib_params *params, ino_t ino,
    112 	int (*callback)(ib_params *, void *, uint64_t, uint32_t),
    113 	void *state)
    114 {
    115 	char		sbbuf[SBLOCKSIZE];
    116 	struct fs	*fs;
    117 	char		inodebuf[MAXBSIZE];
    118 	struct ufs1_dinode	*inode;
    119 	int		level_i;
    120 	int32_t	blk, lblk, nblk;
    121 	int		rv;
    122 #define LEVELS 4
    123 	struct {
    124 		int32_t		*blknums;
    125 		unsigned long	blkcount;
    126 		char		diskbuf[MAXBSIZE];
    127 	} level[LEVELS];
    128 
    129 	assert(params != NULL);
    130 	assert(params->fstype != NULL);
    131 	assert(callback != NULL);
    132 	assert(state != NULL);
    133 
    134 	/* Read the superblock. */
    135 	if (!ffs_read_disk_block(params, SBLOCKSIZE, params->fstype->sblockloc,
    136 	    sbbuf))
    137 		return (0);
    138 	fs = (struct fs *)sbbuf;
    139 	if (params->fstype->needswap)
    140 		ffs_sb_swap(fs, fs);
    141 
    142 	if (fs->fs_inopb <= 0) {
    143 		warnx("Bad inopb %d in superblock in `%s'",
    144 		    fs->fs_inopb, params->filesystem);
    145 		return (0);
    146 	}
    147 
    148 	/* Read the inode. */
    149 	if (! ffs_read_disk_block(params, fsbtodb(fs, ino_to_fsba(fs, ino)),
    150 		fs->fs_bsize, inodebuf))
    151 		return (0);
    152 	inode = (struct ufs1_dinode *)inodebuf;
    153 	inode += ino_to_fsbo(fs, ino);
    154 	if (params->fstype->needswap)
    155 		ffs_dinode1_swap(inode, inode);
    156 
    157 	/* Get the block count and initialize for our block walk. */
    158 	nblk = howmany(inode->di_size, fs->fs_bsize);
    159 	lblk = 0;
    160 	level_i = 0;
    161 	level[0].blknums = &inode->di_db[0];
    162 	level[0].blkcount = NDADDR;
    163 	level[1].blknums = &inode->di_ib[0];
    164 	level[1].blkcount = 1;
    165 	level[2].blknums = &inode->di_ib[1];
    166 	level[2].blkcount = 1;
    167 	level[3].blknums = &inode->di_ib[2];
    168 	level[3].blkcount = 1;
    169 
    170 	/* Walk the data blocks. */
    171 	while (nblk > 0) {
    172 
    173 		/*
    174 		 * If there are no more blocks at this indirection
    175 		 * level, move up one indirection level and loop.
    176 		 */
    177 		if (level[level_i].blkcount == 0) {
    178 			if (++level_i == LEVELS)
    179 				break;
    180 			continue;
    181 		}
    182 
    183 		/* Get the next block at this level. */
    184 		blk = *(level[level_i].blknums++);
    185 		level[level_i].blkcount--;
    186 		if (params->fstype->needswap)
    187 			blk = bswap32(blk);
    188 
    189 #if 0
    190 		fprintf(stderr, "ino %lu blk %lu level %d\n", ino, blk,
    191 		    level_i);
    192 #endif
    193 
    194 		/*
    195 		 * If we're not at the direct level, descend one
    196 		 * level, read in that level's new block list,
    197 		 * and loop.
    198 		 */
    199 		if (level_i > 0) {
    200 			level_i--;
    201 			if (blk == 0)
    202 				memset(level[level_i].diskbuf, 0, MAXBSIZE);
    203 			else if (! ffs_read_disk_block(params,
    204 				fsbtodb(fs, blk),
    205 				fs->fs_bsize, level[level_i].diskbuf))
    206 				return (0);
    207 			/* XXX ondisk32 */
    208 			level[level_i].blknums =
    209 				(int32_t *)level[level_i].diskbuf;
    210 			level[level_i].blkcount = NINDIR(fs);
    211 			continue;
    212 		}
    213 
    214 		/* blk is the next direct level block. */
    215 #if 0
    216 		fprintf(stderr, "ino %lu db %lu blksize %lu\n", ino,
    217 		    fsbtodb(fs, blk), sblksize(fs, inode->di_size, lblk));
    218 #endif
    219 		rv = (*callback)(params, state,
    220 		    fsbtodb(fs, blk), sblksize(fs, inode->di_size, lblk));
    221 		lblk++;
    222 		nblk--;
    223 		if (rv != 1)
    224 			return (rv);
    225 	}
    226 
    227 	if (nblk != 0) {
    228 		warnx("Inode %d in `%s' ran out of blocks?", ino,
    229 		    params->filesystem);
    230 		return (0);
    231 	}
    232 
    233 	return (1);
    234 }
    235 
    236 /*
    237  * This iterates over the data blocks belonging to an inode,
    238  * making a callback each iteration with the disk block number
    239  * and the size.
    240  */
    241 static int
    242 ffs_find_disk_blocks_ufs2(ib_params *params, ino_t ino,
    243 	int (*callback)(ib_params *, void *, uint64_t, uint32_t),
    244 	void *state)
    245 {
    246 	char		sbbuf[SBLOCKSIZE];
    247 	struct fs	*fs;
    248 	char		inodebuf[MAXBSIZE];
    249 	struct ufs2_dinode	*inode;
    250 	int		level_i;
    251 	int64_t	blk, lblk, nblk;
    252 	int		rv;
    253 #define LEVELS 4
    254 	struct {
    255 		int64_t		*blknums;
    256 		unsigned long	blkcount;
    257 		char		diskbuf[MAXBSIZE];
    258 	} level[LEVELS];
    259 
    260 	assert(params != NULL);
    261 	assert(params->fstype != NULL);
    262 	assert(callback != NULL);
    263 	assert(state != NULL);
    264 
    265 	/* Read the superblock. */
    266 	if (!ffs_read_disk_block(params, SBLOCKSIZE, params->fstype->sblockloc,
    267 	    sbbuf))
    268 		return (0);
    269 	fs = (struct fs *)sbbuf;
    270 	if (params->fstype->needswap)
    271 		ffs_sb_swap(fs, fs);
    272 
    273 	if (fs->fs_inopb <= 0) {
    274 		warnx("Bad inopb %d in superblock in `%s'",
    275 		    fs->fs_inopb, params->filesystem);
    276 		return (0);
    277 	}
    278 
    279 	/* Read the inode. */
    280 	if (! ffs_read_disk_block(params, fsbtodb(fs, ino_to_fsba(fs, ino)),
    281 		fs->fs_bsize, inodebuf))
    282 		return (0);
    283 	inode = (struct ufs2_dinode *)inodebuf;
    284 	inode += ino_to_fsbo(fs, ino);
    285 	if (params->fstype->needswap)
    286 		ffs_dinode2_swap(inode, inode);
    287 
    288 	/* Get the block count and initialize for our block walk. */
    289 	nblk = howmany(inode->di_size, fs->fs_bsize);
    290 	lblk = 0;
    291 	level_i = 0;
    292 	level[0].blknums = &inode->di_db[0];
    293 	level[0].blkcount = NDADDR;
    294 	level[1].blknums = &inode->di_ib[0];
    295 	level[1].blkcount = 1;
    296 	level[2].blknums = &inode->di_ib[1];
    297 	level[2].blkcount = 1;
    298 	level[3].blknums = &inode->di_ib[2];
    299 	level[3].blkcount = 1;
    300 
    301 	/* Walk the data blocks. */
    302 	while (nblk > 0) {
    303 
    304 		/*
    305 		 * If there are no more blocks at this indirection
    306 		 * level, move up one indirection level and loop.
    307 		 */
    308 		if (level[level_i].blkcount == 0) {
    309 			if (++level_i == LEVELS)
    310 				break;
    311 			continue;
    312 		}
    313 
    314 		/* Get the next block at this level. */
    315 		blk = *(level[level_i].blknums++);
    316 		level[level_i].blkcount--;
    317 		if (params->fstype->needswap)
    318 			blk = bswap64(blk);
    319 
    320 #if 0
    321 		fprintf(stderr, "ino %lu blk %llu level %d\n", ino,
    322 		    (unsigned long long)blk, level_i);
    323 #endif
    324 
    325 		/*
    326 		 * If we're not at the direct level, descend one
    327 		 * level, read in that level's new block list,
    328 		 * and loop.
    329 		 */
    330 		if (level_i > 0) {
    331 			level_i--;
    332 			if (blk == 0)
    333 				memset(level[level_i].diskbuf, 0, MAXBSIZE);
    334 			else if (! ffs_read_disk_block(params,
    335 				fsbtodb(fs, blk),
    336 				fs->fs_bsize, level[level_i].diskbuf))
    337 				return (0);
    338 			level[level_i].blknums =
    339 				(int64_t *)level[level_i].diskbuf;
    340 			level[level_i].blkcount = NINDIR(fs);
    341 			continue;
    342 		}
    343 
    344 		/* blk is the next direct level block. */
    345 #if 0
    346 		fprintf(stderr, "ino %lu db %llu blksize %lu\n", ino,
    347 		    fsbtodb(fs, blk), sblksize(fs, inode->di_size, lblk));
    348 #endif
    349 		rv = (*callback)(params, state,
    350 		    fsbtodb(fs, blk), sblksize(fs, inode->di_size, lblk));
    351 		lblk++;
    352 		nblk--;
    353 		if (rv != 1)
    354 			return (rv);
    355 	}
    356 
    357 	if (nblk != 0) {
    358 		warnx("Inode %d in `%s' ran out of blocks?", ino,
    359 		    params->filesystem);
    360 		return (0);
    361 	}
    362 
    363 	return (1);
    364 }
    365 
    366 /*
    367  * This callback reads a block of the root directory,
    368  * searches for an entry for the secondary bootstrap,
    369  * and saves the inode number if one is found.
    370  */
    371 static int
    372 ffs_findstage2_ino(ib_params *params, void *_ino,
    373 	uint64_t blk, uint32_t blksize)
    374 {
    375 	char		dirbuf[MAXBSIZE];
    376 	struct direct	*de, *ede;
    377 	uint32_t	ino;
    378 
    379 	assert(params != NULL);
    380 	assert(params->fstype != NULL);
    381 	assert(params->stage2 != NULL);
    382 	assert(_ino != NULL);
    383 
    384 	/* Skip directory holes. */
    385 	if (blk == 0)
    386 		return (1);
    387 
    388 	/* Read the directory block. */
    389 	if (! ffs_read_disk_block(params, blk, blksize, dirbuf))
    390 		return (0);
    391 
    392 	/* Loop over the directory entries. */
    393 	de = (struct direct *)&dirbuf[0];
    394 	ede = (struct direct *)&dirbuf[blksize];
    395 	while (de < ede) {
    396 		ino = de->d_ino;
    397 		if (params->fstype->needswap) {
    398 			ino = bswap32(ino);
    399 			de->d_reclen = bswap16(de->d_reclen);
    400 		}
    401 		if (ino != 0 && strcmp(de->d_name, params->stage2) == 0) {
    402 			*((uint32_t *)_ino) = ino;
    403 			return (2);
    404 		}
    405 		if (de->d_reclen == 0)
    406 			break;
    407 		de = (struct direct *)((char *)de + de->d_reclen);
    408 	}
    409 
    410 	return (1);
    411 }
    412 
    413 struct findblks_state {
    414 	uint32_t	maxblk;
    415 	uint32_t	nblk;
    416 	ib_block	*blocks;
    417 };
    418 
    419 /* This callback records the blocks of the secondary bootstrap. */
    420 static int
    421 ffs_findstage2_blocks(ib_params *params, void *_state,
    422 	uint64_t blk, uint32_t blksize)
    423 {
    424 	struct findblks_state *state = _state;
    425 
    426 	assert(params != NULL);
    427 	assert(params->stage2 != NULL);
    428 	assert(_state != NULL);
    429 
    430 	if (state->nblk == state->maxblk) {
    431 		warnx("Secondary bootstrap `%s' has too many blocks (max %d)",
    432 		    params->stage2, state->maxblk);
    433 		return (0);
    434 	}
    435 	state->blocks[state->nblk].block = blk;
    436 	state->blocks[state->nblk].blocksize = blksize;
    437 	state->nblk++;
    438 	return (1);
    439 }
    440 
    441 /*
    442  *	publically visible functions
    443  */
    444 
    445 static off_t sblock_try[] = SBLOCKSEARCH;
    446 
    447 int
    448 ffs_match(ib_params *params)
    449 {
    450 	char		sbbuf[SBLOCKSIZE];
    451 	struct fs	*fs;
    452 	int i;
    453 	off_t loc;
    454 
    455 	assert(params != NULL);
    456 	assert(params->fstype != NULL);
    457 
    458 	fs = (struct fs *)sbbuf;
    459 	for (i = 0; sblock_try[i] != -1; i++) {
    460 		loc = sblock_try[i];
    461 		if (!ffs_read_disk_block(params, loc, SBLOCKSIZE, sbbuf))
    462 			continue;
    463 		switch (fs->fs_magic) {
    464 		case FS_UFS2_MAGIC:
    465 			is_ufs2 = 1;
    466 			/* FALLTHROUGH */
    467 		case FS_UFS1_MAGIC:
    468 			params->fstype->needswap = 0;
    469 			params->fstype->blocksize = fs->fs_bsize;
    470 			params->fstype->sblockloc = loc;
    471 			return (1);
    472 		case FS_UFS2_MAGIC_SWAPPED:
    473 			is_ufs2 = 1;
    474 			/* FALLTHROUGH */
    475 		case FS_UFS1_MAGIC_SWAPPED:
    476 			params->fstype->needswap = 1;
    477 			params->fstype->blocksize = bswap32(fs->fs_bsize);
    478 			params->fstype->sblockloc = loc;
    479 			return (1);
    480 		default:
    481 			continue;
    482 		}
    483 	}
    484 
    485 	return (0);
    486 }
    487 
    488 int
    489 ffs_findstage2(ib_params *params, uint32_t *maxblk, ib_block *blocks)
    490 {
    491 	int			rv;
    492 	uint32_t		ino;
    493 	struct findblks_state	state;
    494 
    495 	assert(params != NULL);
    496 	assert(params->stage2 != NULL);
    497 	assert(maxblk != NULL);
    498 	assert(blocks != NULL);
    499 
    500 	if (params->flags & IB_STAGE2START)
    501 		return (hardcode_stage2(params, maxblk, blocks));
    502 
    503 	/* The secondary bootstrap must be clearly in /. */
    504 	if (params->stage2[0] == '/')
    505 		params->stage2++;
    506 	if (strchr(params->stage2, '/') != NULL) {
    507 		warnx("The secondary bootstrap `%s' must be in /",
    508 		    params->stage2);
    509 		return (0);
    510 	}
    511 
    512 	/* Get the inode number of the secondary bootstrap. */
    513 	if (is_ufs2)
    514 		rv = ffs_find_disk_blocks_ufs2(params, ROOTINO,
    515 		    ffs_findstage2_ino, &ino);
    516 	else
    517 		rv = ffs_find_disk_blocks_ufs1(params, ROOTINO,
    518 		    ffs_findstage2_ino, &ino);
    519 	if (rv != 2) {
    520 		warnx("Could not find secondary bootstrap `%s' in `%s'",
    521 		    params->stage2, params->filesystem);
    522 		return (0);
    523 	}
    524 
    525 	/* Record the disk blocks of the secondary bootstrap. */
    526 	state.maxblk = *maxblk;
    527 	state.nblk = 0;
    528 	state.blocks = blocks;
    529 	if (is_ufs2)
    530 		rv = ffs_find_disk_blocks_ufs2(params, ino,
    531 		    ffs_findstage2_blocks, &state);
    532 		rv = ffs_find_disk_blocks_ufs1(params, ino,
    533 		    ffs_findstage2_blocks, &state);
    534 	if (! rv) {
    535 		return (0);
    536 	}
    537 
    538 	*maxblk = state.nblk;
    539 	return (1);
    540 }
    541