xref: /minix3/usr.sbin/installboot/ffs.c (revision 84d9c625bfea59e274550651111ae9edfdc40fbd)
1*84d9c625SLionel Sambuc /*	$NetBSD: ffs.c,v 1.32 2013/06/23 02:06:06 dholland Exp $	*/
29f8e6353SEvgeniy Ivanov 
39f8e6353SEvgeniy Ivanov /*-
49f8e6353SEvgeniy Ivanov  * Copyright (c) 2002 The NetBSD Foundation, Inc.
59f8e6353SEvgeniy Ivanov  * All rights reserved.
69f8e6353SEvgeniy Ivanov  *
79f8e6353SEvgeniy Ivanov  * This code is derived from software contributed to The NetBSD Foundation
89f8e6353SEvgeniy Ivanov  * by Matt Fredette.
99f8e6353SEvgeniy Ivanov  *
109f8e6353SEvgeniy Ivanov  * Redistribution and use in source and binary forms, with or without
119f8e6353SEvgeniy Ivanov  * modification, are permitted provided that the following conditions
129f8e6353SEvgeniy Ivanov  * are met:
139f8e6353SEvgeniy Ivanov  * 1. Redistributions of source code must retain the above copyright
149f8e6353SEvgeniy Ivanov  *    notice, this list of conditions and the following disclaimer.
159f8e6353SEvgeniy Ivanov  * 2. Redistributions in binary form must reproduce the above copyright
169f8e6353SEvgeniy Ivanov  *    notice, this list of conditions and the following disclaimer in the
179f8e6353SEvgeniy Ivanov  *    documentation and/or other materials provided with the distribution.
189f8e6353SEvgeniy Ivanov  *
199f8e6353SEvgeniy Ivanov  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
209f8e6353SEvgeniy Ivanov  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
219f8e6353SEvgeniy Ivanov  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
229f8e6353SEvgeniy Ivanov  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
239f8e6353SEvgeniy Ivanov  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
249f8e6353SEvgeniy Ivanov  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
259f8e6353SEvgeniy Ivanov  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
269f8e6353SEvgeniy Ivanov  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
279f8e6353SEvgeniy Ivanov  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
289f8e6353SEvgeniy Ivanov  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
299f8e6353SEvgeniy Ivanov  * POSSIBILITY OF SUCH DAMAGE.
309f8e6353SEvgeniy Ivanov  */
319f8e6353SEvgeniy Ivanov 
329f8e6353SEvgeniy Ivanov #if HAVE_NBTOOL_CONFIG_H
339f8e6353SEvgeniy Ivanov #include "nbtool_config.h"
349f8e6353SEvgeniy Ivanov #endif
359f8e6353SEvgeniy Ivanov 
369f8e6353SEvgeniy Ivanov #include <sys/cdefs.h>
379f8e6353SEvgeniy Ivanov #if !defined(__lint)
38*84d9c625SLionel Sambuc __RCSID("$NetBSD: ffs.c,v 1.32 2013/06/23 02:06:06 dholland Exp $");
399f8e6353SEvgeniy Ivanov #endif	/* !__lint */
409f8e6353SEvgeniy Ivanov 
419f8e6353SEvgeniy Ivanov #include <sys/param.h>
429f8e6353SEvgeniy Ivanov 
439f8e6353SEvgeniy Ivanov #if !HAVE_NBTOOL_CONFIG_H
449f8e6353SEvgeniy Ivanov #include <sys/mount.h>
459f8e6353SEvgeniy Ivanov #endif
469f8e6353SEvgeniy Ivanov 
479f8e6353SEvgeniy Ivanov #include <assert.h>
489f8e6353SEvgeniy Ivanov #include <err.h>
499f8e6353SEvgeniy Ivanov #include <errno.h>
509f8e6353SEvgeniy Ivanov #include <fcntl.h>
519f8e6353SEvgeniy Ivanov #include <stdarg.h>
529f8e6353SEvgeniy Ivanov #include <stdio.h>
539f8e6353SEvgeniy Ivanov #include <stdlib.h>
549f8e6353SEvgeniy Ivanov #include <string.h>
559f8e6353SEvgeniy Ivanov #include <unistd.h>
569f8e6353SEvgeniy Ivanov 
579f8e6353SEvgeniy Ivanov #include "installboot.h"
589f8e6353SEvgeniy Ivanov 
599f8e6353SEvgeniy Ivanov /* From <dev/raidframe/raidframevar.h> */
609f8e6353SEvgeniy Ivanov #define RF_PROTECTED_SECTORS 64L
619f8e6353SEvgeniy Ivanov 
629f8e6353SEvgeniy Ivanov #undef DIRBLKSIZ
639f8e6353SEvgeniy Ivanov 
649f8e6353SEvgeniy Ivanov #include <ufs/ufs/dinode.h>
659f8e6353SEvgeniy Ivanov #include <ufs/ufs/dir.h>
669f8e6353SEvgeniy Ivanov #include <ufs/ffs/fs.h>
679f8e6353SEvgeniy Ivanov #include <ufs/ffs/ffs_extern.h>
689f8e6353SEvgeniy Ivanov #ifndef NO_FFS_SWAP
699f8e6353SEvgeniy Ivanov #include <ufs/ufs/ufs_bswap.h>
709f8e6353SEvgeniy Ivanov #else
719f8e6353SEvgeniy Ivanov #define	ffs_sb_swap(fs_a, fs_b)
729f8e6353SEvgeniy Ivanov #define	ffs_dinode1_swap(inode_a, inode_b)
739f8e6353SEvgeniy Ivanov #define	ffs_dinode2_swap(inode_a, inode_b)
749f8e6353SEvgeniy Ivanov #endif
759f8e6353SEvgeniy Ivanov 
769f8e6353SEvgeniy Ivanov static int	ffs_match_common(ib_params *, off_t);
779f8e6353SEvgeniy Ivanov static int	ffs_read_disk_block(ib_params *, uint64_t, int, char []);
789f8e6353SEvgeniy Ivanov static int	ffs_find_disk_blocks_ufs1(ib_params *, ino_t,
799f8e6353SEvgeniy Ivanov 		    int (*)(ib_params *, void *, uint64_t, uint32_t), void *);
809f8e6353SEvgeniy Ivanov static int	ffs_find_disk_blocks_ufs2(ib_params *, ino_t,
819f8e6353SEvgeniy Ivanov 		    int (*)(ib_params *, void *, uint64_t, uint32_t), void *);
829f8e6353SEvgeniy Ivanov static int	ffs_findstage2_ino(ib_params *, void *, uint64_t, uint32_t);
839f8e6353SEvgeniy Ivanov static int	ffs_findstage2_blocks(ib_params *, void *, uint64_t, uint32_t);
849f8e6353SEvgeniy Ivanov 
859f8e6353SEvgeniy Ivanov static int is_ufs2;
869f8e6353SEvgeniy Ivanov 
879f8e6353SEvgeniy Ivanov 
889f8e6353SEvgeniy Ivanov /* This reads a disk block from the filesystem. */
899f8e6353SEvgeniy Ivanov static int
ffs_read_disk_block(ib_params * params,uint64_t blkno,int size,char blk[])909f8e6353SEvgeniy Ivanov ffs_read_disk_block(ib_params *params, uint64_t blkno, int size, char blk[])
919f8e6353SEvgeniy Ivanov {
929f8e6353SEvgeniy Ivanov 	int	rv;
939f8e6353SEvgeniy Ivanov 
949f8e6353SEvgeniy Ivanov 	assert(params != NULL);
959f8e6353SEvgeniy Ivanov 	assert(params->filesystem != NULL);
969f8e6353SEvgeniy Ivanov 	assert(params->fsfd != -1);
979f8e6353SEvgeniy Ivanov 	assert(size > 0);
989f8e6353SEvgeniy Ivanov 	assert(blk != NULL);
999f8e6353SEvgeniy Ivanov 
1009f8e6353SEvgeniy Ivanov 	rv = pread(params->fsfd, blk, size, blkno * params->sectorsize);
1019f8e6353SEvgeniy Ivanov 	if (rv == -1) {
1029f8e6353SEvgeniy Ivanov 		warn("Reading block %llu in `%s'",
1039f8e6353SEvgeniy Ivanov 		    (unsigned long long)blkno, params->filesystem);
1049f8e6353SEvgeniy Ivanov 		return (0);
1059f8e6353SEvgeniy Ivanov 	} else if (rv != size) {
1069f8e6353SEvgeniy Ivanov 		warnx("Reading block %llu in `%s': short read",
1079f8e6353SEvgeniy Ivanov 		    (unsigned long long)blkno, params->filesystem);
1089f8e6353SEvgeniy Ivanov 		return (0);
1099f8e6353SEvgeniy Ivanov 	}
1109f8e6353SEvgeniy Ivanov 
1119f8e6353SEvgeniy Ivanov 	return (1);
1129f8e6353SEvgeniy Ivanov }
1139f8e6353SEvgeniy Ivanov 
1149f8e6353SEvgeniy Ivanov /*
1159f8e6353SEvgeniy Ivanov  * This iterates over the data blocks belonging to an inode,
1169f8e6353SEvgeniy Ivanov  * making a callback each iteration with the disk block number
1179f8e6353SEvgeniy Ivanov  * and the size.
1189f8e6353SEvgeniy Ivanov  */
1199f8e6353SEvgeniy Ivanov static int
ffs_find_disk_blocks_ufs1(ib_params * params,ino_t ino,int (* callback)(ib_params *,void *,uint64_t,uint32_t),void * state)1209f8e6353SEvgeniy Ivanov ffs_find_disk_blocks_ufs1(ib_params *params, ino_t ino,
1219f8e6353SEvgeniy Ivanov 	int (*callback)(ib_params *, void *, uint64_t, uint32_t),
1229f8e6353SEvgeniy Ivanov 	void *state)
1239f8e6353SEvgeniy Ivanov {
1249f8e6353SEvgeniy Ivanov 	char		sbbuf[SBLOCKSIZE];
1259f8e6353SEvgeniy Ivanov 	struct fs	*fs;
1269f8e6353SEvgeniy Ivanov 	char		inodebuf[MAXBSIZE];
1279f8e6353SEvgeniy Ivanov 	struct ufs1_dinode	*inode;
1289f8e6353SEvgeniy Ivanov 	int		level_i;
1299f8e6353SEvgeniy Ivanov 	int32_t	blk, lblk, nblk;
1309f8e6353SEvgeniy Ivanov 	int		rv;
1319f8e6353SEvgeniy Ivanov #define LEVELS 4
1329f8e6353SEvgeniy Ivanov 	struct {
1339f8e6353SEvgeniy Ivanov 		int32_t		*blknums;
1349f8e6353SEvgeniy Ivanov 		unsigned long	blkcount;
1359f8e6353SEvgeniy Ivanov 		char		diskbuf[MAXBSIZE];
1369f8e6353SEvgeniy Ivanov 	} level[LEVELS];
1379f8e6353SEvgeniy Ivanov 
1389f8e6353SEvgeniy Ivanov 	assert(params != NULL);
1399f8e6353SEvgeniy Ivanov 	assert(params->fstype != NULL);
1409f8e6353SEvgeniy Ivanov 	assert(callback != NULL);
1419f8e6353SEvgeniy Ivanov 	assert(state != NULL);
1429f8e6353SEvgeniy Ivanov 
1439f8e6353SEvgeniy Ivanov 	/* Read the superblock. */
1449f8e6353SEvgeniy Ivanov 	if (!ffs_read_disk_block(params, params->fstype->sblockloc, SBLOCKSIZE,
1459f8e6353SEvgeniy Ivanov 	    sbbuf))
1469f8e6353SEvgeniy Ivanov 		return (0);
1479f8e6353SEvgeniy Ivanov 	fs = (struct fs *)sbbuf;
1489f8e6353SEvgeniy Ivanov #ifndef NO_FFS_SWAP
1499f8e6353SEvgeniy Ivanov 	if (params->fstype->needswap)
1509f8e6353SEvgeniy Ivanov 		ffs_sb_swap(fs, fs);
1519f8e6353SEvgeniy Ivanov #endif
1529f8e6353SEvgeniy Ivanov 
1539f8e6353SEvgeniy Ivanov 	if (fs->fs_inopb <= 0) {
1549f8e6353SEvgeniy Ivanov 		warnx("Bad inopb %d in superblock in `%s'",
1559f8e6353SEvgeniy Ivanov 		    fs->fs_inopb, params->filesystem);
1569f8e6353SEvgeniy Ivanov 		return (0);
1579f8e6353SEvgeniy Ivanov 	}
1589f8e6353SEvgeniy Ivanov 
1599f8e6353SEvgeniy Ivanov 	/* Read the inode. */
1609f8e6353SEvgeniy Ivanov 	if (! ffs_read_disk_block(params,
161*84d9c625SLionel Sambuc 		FFS_FSBTODB(fs, ino_to_fsba(fs, ino)) + params->fstype->offset,
1629f8e6353SEvgeniy Ivanov 		fs->fs_bsize, inodebuf))
1639f8e6353SEvgeniy Ivanov 		return (0);
1649f8e6353SEvgeniy Ivanov 	inode = (struct ufs1_dinode *)inodebuf;
1659f8e6353SEvgeniy Ivanov 	inode += ino_to_fsbo(fs, ino);
1669f8e6353SEvgeniy Ivanov #ifndef NO_FFS_SWAP
1679f8e6353SEvgeniy Ivanov 	if (params->fstype->needswap)
1689f8e6353SEvgeniy Ivanov 		ffs_dinode1_swap(inode, inode);
1699f8e6353SEvgeniy Ivanov #endif
1709f8e6353SEvgeniy Ivanov 
1719f8e6353SEvgeniy Ivanov 	/* Get the block count and initialize for our block walk. */
1729f8e6353SEvgeniy Ivanov 	nblk = howmany(inode->di_size, fs->fs_bsize);
1739f8e6353SEvgeniy Ivanov 	lblk = 0;
1749f8e6353SEvgeniy Ivanov 	level_i = 0;
1759f8e6353SEvgeniy Ivanov 	level[0].blknums = &inode->di_db[0];
176*84d9c625SLionel Sambuc 	level[0].blkcount = UFS_NDADDR;
1779f8e6353SEvgeniy Ivanov 	level[1].blknums = &inode->di_ib[0];
1789f8e6353SEvgeniy Ivanov 	level[1].blkcount = 1;
1799f8e6353SEvgeniy Ivanov 	level[2].blknums = &inode->di_ib[1];
1809f8e6353SEvgeniy Ivanov 	level[2].blkcount = 1;
1819f8e6353SEvgeniy Ivanov 	level[3].blknums = &inode->di_ib[2];
1829f8e6353SEvgeniy Ivanov 	level[3].blkcount = 1;
1839f8e6353SEvgeniy Ivanov 
1849f8e6353SEvgeniy Ivanov 	/* Walk the data blocks. */
1859f8e6353SEvgeniy Ivanov 	while (nblk > 0) {
1869f8e6353SEvgeniy Ivanov 
1879f8e6353SEvgeniy Ivanov 		/*
1889f8e6353SEvgeniy Ivanov 		 * If there are no more blocks at this indirection
1899f8e6353SEvgeniy Ivanov 		 * level, move up one indirection level and loop.
1909f8e6353SEvgeniy Ivanov 		 */
1919f8e6353SEvgeniy Ivanov 		if (level[level_i].blkcount == 0) {
1929f8e6353SEvgeniy Ivanov 			if (++level_i == LEVELS)
1939f8e6353SEvgeniy Ivanov 				break;
1949f8e6353SEvgeniy Ivanov 			continue;
1959f8e6353SEvgeniy Ivanov 		}
1969f8e6353SEvgeniy Ivanov 
1979f8e6353SEvgeniy Ivanov 		/* Get the next block at this level. */
1989f8e6353SEvgeniy Ivanov 		blk = *(level[level_i].blknums++);
1999f8e6353SEvgeniy Ivanov 		level[level_i].blkcount--;
2009f8e6353SEvgeniy Ivanov 		if (params->fstype->needswap)
2019f8e6353SEvgeniy Ivanov 			blk = bswap32(blk);
2029f8e6353SEvgeniy Ivanov 
2039f8e6353SEvgeniy Ivanov #if 0
2049f8e6353SEvgeniy Ivanov 		fprintf(stderr, "ino %lu blk %lu level %d\n", ino, blk,
2059f8e6353SEvgeniy Ivanov 		    level_i);
2069f8e6353SEvgeniy Ivanov #endif
2079f8e6353SEvgeniy Ivanov 
2089f8e6353SEvgeniy Ivanov 		/*
2099f8e6353SEvgeniy Ivanov 		 * If we're not at the direct level, descend one
2109f8e6353SEvgeniy Ivanov 		 * level, read in that level's new block list,
2119f8e6353SEvgeniy Ivanov 		 * and loop.
2129f8e6353SEvgeniy Ivanov 		 */
2139f8e6353SEvgeniy Ivanov 		if (level_i > 0) {
2149f8e6353SEvgeniy Ivanov 			level_i--;
2159f8e6353SEvgeniy Ivanov 			if (blk == 0)
2169f8e6353SEvgeniy Ivanov 				memset(level[level_i].diskbuf, 0, MAXBSIZE);
2179f8e6353SEvgeniy Ivanov 			else if (! ffs_read_disk_block(params,
218*84d9c625SLionel Sambuc 				FFS_FSBTODB(fs, blk) + params->fstype->offset,
2199f8e6353SEvgeniy Ivanov 				fs->fs_bsize, level[level_i].diskbuf))
2209f8e6353SEvgeniy Ivanov 				return (0);
2219f8e6353SEvgeniy Ivanov 			/* XXX ondisk32 */
2229f8e6353SEvgeniy Ivanov 			level[level_i].blknums =
2239f8e6353SEvgeniy Ivanov 				(int32_t *)level[level_i].diskbuf;
224*84d9c625SLionel Sambuc 			level[level_i].blkcount = FFS_NINDIR(fs);
2259f8e6353SEvgeniy Ivanov 			continue;
2269f8e6353SEvgeniy Ivanov 		}
2279f8e6353SEvgeniy Ivanov 
2289f8e6353SEvgeniy Ivanov 		/* blk is the next direct level block. */
2299f8e6353SEvgeniy Ivanov #if 0
2309f8e6353SEvgeniy Ivanov 		fprintf(stderr, "ino %lu db %lu blksize %lu\n", ino,
231*84d9c625SLionel Sambuc 		    FFS_FSBTODB(fs, blk), ffs_sblksize(fs, inode->di_size, lblk));
2329f8e6353SEvgeniy Ivanov #endif
2339f8e6353SEvgeniy Ivanov 		rv = (*callback)(params, state,
234*84d9c625SLionel Sambuc 		    FFS_FSBTODB(fs, blk) + params->fstype->offset,
235*84d9c625SLionel Sambuc 		    ffs_sblksize(fs, (int64_t)inode->di_size, lblk));
2369f8e6353SEvgeniy Ivanov 		lblk++;
2379f8e6353SEvgeniy Ivanov 		nblk--;
2389f8e6353SEvgeniy Ivanov 		if (rv != 1)
2399f8e6353SEvgeniy Ivanov 			return (rv);
2409f8e6353SEvgeniy Ivanov 	}
2419f8e6353SEvgeniy Ivanov 
2429f8e6353SEvgeniy Ivanov 	if (nblk != 0) {
2439f8e6353SEvgeniy Ivanov 		warnx("Inode %llu in `%s' ran out of blocks?",
2449f8e6353SEvgeniy Ivanov 		    (unsigned long long)ino, params->filesystem);
2459f8e6353SEvgeniy Ivanov 		return (0);
2469f8e6353SEvgeniy Ivanov 	}
2479f8e6353SEvgeniy Ivanov 
2489f8e6353SEvgeniy Ivanov 	return (1);
2499f8e6353SEvgeniy Ivanov }
2509f8e6353SEvgeniy Ivanov 
2519f8e6353SEvgeniy Ivanov /*
2529f8e6353SEvgeniy Ivanov  * This iterates over the data blocks belonging to an inode,
2539f8e6353SEvgeniy Ivanov  * making a callback each iteration with the disk block number
2549f8e6353SEvgeniy Ivanov  * and the size.
2559f8e6353SEvgeniy Ivanov  */
2569f8e6353SEvgeniy Ivanov static int
ffs_find_disk_blocks_ufs2(ib_params * params,ino_t ino,int (* callback)(ib_params *,void *,uint64_t,uint32_t),void * state)2579f8e6353SEvgeniy Ivanov ffs_find_disk_blocks_ufs2(ib_params *params, ino_t ino,
2589f8e6353SEvgeniy Ivanov 	int (*callback)(ib_params *, void *, uint64_t, uint32_t),
2599f8e6353SEvgeniy Ivanov 	void *state)
2609f8e6353SEvgeniy Ivanov {
2619f8e6353SEvgeniy Ivanov 	char		sbbuf[SBLOCKSIZE];
2629f8e6353SEvgeniy Ivanov 	struct fs	*fs;
2639f8e6353SEvgeniy Ivanov 	char		inodebuf[MAXBSIZE];
2649f8e6353SEvgeniy Ivanov 	struct ufs2_dinode	*inode;
2659f8e6353SEvgeniy Ivanov 	int		level_i;
2669f8e6353SEvgeniy Ivanov 	int64_t		blk, lblk, nblk;
2679f8e6353SEvgeniy Ivanov 	int		rv;
2689f8e6353SEvgeniy Ivanov #define LEVELS 4
2699f8e6353SEvgeniy Ivanov 	struct {
2709f8e6353SEvgeniy Ivanov 		int64_t		*blknums;
2719f8e6353SEvgeniy Ivanov 		unsigned long	blkcount;
2729f8e6353SEvgeniy Ivanov 		char		diskbuf[MAXBSIZE];
2739f8e6353SEvgeniy Ivanov 	} level[LEVELS];
2749f8e6353SEvgeniy Ivanov 
2759f8e6353SEvgeniy Ivanov 	assert(params != NULL);
2769f8e6353SEvgeniy Ivanov 	assert(params->fstype != NULL);
2779f8e6353SEvgeniy Ivanov 	assert(callback != NULL);
2789f8e6353SEvgeniy Ivanov 	assert(state != NULL);
2799f8e6353SEvgeniy Ivanov 
2809f8e6353SEvgeniy Ivanov 	/* Read the superblock. */
2819f8e6353SEvgeniy Ivanov 	if (!ffs_read_disk_block(params, params->fstype->sblockloc, SBLOCKSIZE,
2829f8e6353SEvgeniy Ivanov 	    sbbuf))
2839f8e6353SEvgeniy Ivanov 		return (0);
2849f8e6353SEvgeniy Ivanov 	fs = (struct fs *)sbbuf;
2859f8e6353SEvgeniy Ivanov #ifndef NO_FFS_SWAP
2869f8e6353SEvgeniy Ivanov 	if (params->fstype->needswap)
2879f8e6353SEvgeniy Ivanov 		ffs_sb_swap(fs, fs);
2889f8e6353SEvgeniy Ivanov #endif
2899f8e6353SEvgeniy Ivanov 
2909f8e6353SEvgeniy Ivanov 	if (fs->fs_inopb <= 0) {
2919f8e6353SEvgeniy Ivanov 		warnx("Bad inopb %d in superblock in `%s'",
2929f8e6353SEvgeniy Ivanov 		    fs->fs_inopb, params->filesystem);
2939f8e6353SEvgeniy Ivanov 		return (0);
2949f8e6353SEvgeniy Ivanov 	}
2959f8e6353SEvgeniy Ivanov 
2969f8e6353SEvgeniy Ivanov 	/* Read the inode. */
2979f8e6353SEvgeniy Ivanov 	if (! ffs_read_disk_block(params,
298*84d9c625SLionel Sambuc 		FFS_FSBTODB(fs, ino_to_fsba(fs, ino)) + params->fstype->offset,
2999f8e6353SEvgeniy Ivanov 		fs->fs_bsize, inodebuf))
3009f8e6353SEvgeniy Ivanov 		return (0);
3019f8e6353SEvgeniy Ivanov 	inode = (struct ufs2_dinode *)inodebuf;
3029f8e6353SEvgeniy Ivanov 	inode += ino_to_fsbo(fs, ino);
3039f8e6353SEvgeniy Ivanov #ifndef NO_FFS_SWAP
3049f8e6353SEvgeniy Ivanov 	if (params->fstype->needswap)
3059f8e6353SEvgeniy Ivanov 		ffs_dinode2_swap(inode, inode);
3069f8e6353SEvgeniy Ivanov #endif
3079f8e6353SEvgeniy Ivanov 
3089f8e6353SEvgeniy Ivanov 	/* Get the block count and initialize for our block walk. */
3099f8e6353SEvgeniy Ivanov 	nblk = howmany(inode->di_size, fs->fs_bsize);
3109f8e6353SEvgeniy Ivanov 	lblk = 0;
3119f8e6353SEvgeniy Ivanov 	level_i = 0;
3129f8e6353SEvgeniy Ivanov 	level[0].blknums = &inode->di_db[0];
313*84d9c625SLionel Sambuc 	level[0].blkcount = UFS_NDADDR;
3149f8e6353SEvgeniy Ivanov 	level[1].blknums = &inode->di_ib[0];
3159f8e6353SEvgeniy Ivanov 	level[1].blkcount = 1;
3169f8e6353SEvgeniy Ivanov 	level[2].blknums = &inode->di_ib[1];
3179f8e6353SEvgeniy Ivanov 	level[2].blkcount = 1;
3189f8e6353SEvgeniy Ivanov 	level[3].blknums = &inode->di_ib[2];
3199f8e6353SEvgeniy Ivanov 	level[3].blkcount = 1;
3209f8e6353SEvgeniy Ivanov 
3219f8e6353SEvgeniy Ivanov 	/* Walk the data blocks. */
3229f8e6353SEvgeniy Ivanov 	while (nblk > 0) {
3239f8e6353SEvgeniy Ivanov 
3249f8e6353SEvgeniy Ivanov 		/*
3259f8e6353SEvgeniy Ivanov 		 * If there are no more blocks at this indirection
3269f8e6353SEvgeniy Ivanov 		 * level, move up one indirection level and loop.
3279f8e6353SEvgeniy Ivanov 		 */
3289f8e6353SEvgeniy Ivanov 		if (level[level_i].blkcount == 0) {
3299f8e6353SEvgeniy Ivanov 			if (++level_i == LEVELS)
3309f8e6353SEvgeniy Ivanov 				break;
3319f8e6353SEvgeniy Ivanov 			continue;
3329f8e6353SEvgeniy Ivanov 		}
3339f8e6353SEvgeniy Ivanov 
3349f8e6353SEvgeniy Ivanov 		/* Get the next block at this level. */
3359f8e6353SEvgeniy Ivanov 		blk = *(level[level_i].blknums++);
3369f8e6353SEvgeniy Ivanov 		level[level_i].blkcount--;
3379f8e6353SEvgeniy Ivanov 		if (params->fstype->needswap)
3389f8e6353SEvgeniy Ivanov 			blk = bswap64(blk);
3399f8e6353SEvgeniy Ivanov 
3409f8e6353SEvgeniy Ivanov #if 0
3419f8e6353SEvgeniy Ivanov 		fprintf(stderr, "ino %lu blk %llu level %d\n", ino,
3429f8e6353SEvgeniy Ivanov 		    (unsigned long long)blk, level_i);
3439f8e6353SEvgeniy Ivanov #endif
3449f8e6353SEvgeniy Ivanov 
3459f8e6353SEvgeniy Ivanov 		/*
3469f8e6353SEvgeniy Ivanov 		 * If we're not at the direct level, descend one
3479f8e6353SEvgeniy Ivanov 		 * level, read in that level's new block list,
3489f8e6353SEvgeniy Ivanov 		 * and loop.
3499f8e6353SEvgeniy Ivanov 		 */
3509f8e6353SEvgeniy Ivanov 		if (level_i > 0) {
3519f8e6353SEvgeniy Ivanov 			level_i--;
3529f8e6353SEvgeniy Ivanov 			if (blk == 0)
3539f8e6353SEvgeniy Ivanov 				memset(level[level_i].diskbuf, 0, MAXBSIZE);
3549f8e6353SEvgeniy Ivanov 			else if (! ffs_read_disk_block(params,
355*84d9c625SLionel Sambuc 				FFS_FSBTODB(fs, blk) + params->fstype->offset,
3569f8e6353SEvgeniy Ivanov 				fs->fs_bsize, level[level_i].diskbuf))
3579f8e6353SEvgeniy Ivanov 				return (0);
3589f8e6353SEvgeniy Ivanov 			level[level_i].blknums =
3599f8e6353SEvgeniy Ivanov 				(int64_t *)level[level_i].diskbuf;
360*84d9c625SLionel Sambuc 			level[level_i].blkcount = FFS_NINDIR(fs);
3619f8e6353SEvgeniy Ivanov 			continue;
3629f8e6353SEvgeniy Ivanov 		}
3639f8e6353SEvgeniy Ivanov 
3649f8e6353SEvgeniy Ivanov 		/* blk is the next direct level block. */
3659f8e6353SEvgeniy Ivanov #if 0
3669f8e6353SEvgeniy Ivanov 		fprintf(stderr, "ino %lu db %llu blksize %lu\n", ino,
367*84d9c625SLionel Sambuc 		    FFS_FSBTODB(fs, blk), ffs_sblksize(fs, inode->di_size, lblk));
3689f8e6353SEvgeniy Ivanov #endif
3699f8e6353SEvgeniy Ivanov 		rv = (*callback)(params, state,
370*84d9c625SLionel Sambuc 		    FFS_FSBTODB(fs, blk) + params->fstype->offset,
371*84d9c625SLionel Sambuc 		    ffs_sblksize(fs, (int64_t)inode->di_size, lblk));
3729f8e6353SEvgeniy Ivanov 		lblk++;
3739f8e6353SEvgeniy Ivanov 		nblk--;
3749f8e6353SEvgeniy Ivanov 		if (rv != 1)
3759f8e6353SEvgeniy Ivanov 			return (rv);
3769f8e6353SEvgeniy Ivanov 	}
3779f8e6353SEvgeniy Ivanov 
3789f8e6353SEvgeniy Ivanov 	if (nblk != 0) {
3799f8e6353SEvgeniy Ivanov 		warnx("Inode %llu in `%s' ran out of blocks?",
3809f8e6353SEvgeniy Ivanov 		    (unsigned long long)ino, params->filesystem);
3819f8e6353SEvgeniy Ivanov 		return (0);
3829f8e6353SEvgeniy Ivanov 	}
3839f8e6353SEvgeniy Ivanov 
3849f8e6353SEvgeniy Ivanov 	return (1);
3859f8e6353SEvgeniy Ivanov }
3869f8e6353SEvgeniy Ivanov 
3879f8e6353SEvgeniy Ivanov /*
3889f8e6353SEvgeniy Ivanov  * This callback reads a block of the root directory,
3899f8e6353SEvgeniy Ivanov  * searches for an entry for the secondary bootstrap,
3909f8e6353SEvgeniy Ivanov  * and saves the inode number if one is found.
3919f8e6353SEvgeniy Ivanov  */
3929f8e6353SEvgeniy Ivanov static int
ffs_findstage2_ino(ib_params * params,void * _ino,uint64_t blk,uint32_t blksize)3939f8e6353SEvgeniy Ivanov ffs_findstage2_ino(ib_params *params, void *_ino,
3949f8e6353SEvgeniy Ivanov 	uint64_t blk, uint32_t blksize)
3959f8e6353SEvgeniy Ivanov {
3969f8e6353SEvgeniy Ivanov 	char		dirbuf[MAXBSIZE];
3979f8e6353SEvgeniy Ivanov 	struct direct	*de, *ede;
3989f8e6353SEvgeniy Ivanov 	uint32_t	ino;
3999f8e6353SEvgeniy Ivanov 
4009f8e6353SEvgeniy Ivanov 	assert(params != NULL);
4019f8e6353SEvgeniy Ivanov 	assert(params->fstype != NULL);
4029f8e6353SEvgeniy Ivanov 	assert(params->stage2 != NULL);
4039f8e6353SEvgeniy Ivanov 	assert(_ino != NULL);
4049f8e6353SEvgeniy Ivanov 
4059f8e6353SEvgeniy Ivanov 	/* Skip directory holes. */
4069f8e6353SEvgeniy Ivanov 	if (blk == 0)
4079f8e6353SEvgeniy Ivanov 		return (1);
4089f8e6353SEvgeniy Ivanov 
4099f8e6353SEvgeniy Ivanov 	/* Read the directory block. */
4109f8e6353SEvgeniy Ivanov 	if (! ffs_read_disk_block(params, blk, blksize, dirbuf))
4119f8e6353SEvgeniy Ivanov 		return (0);
4129f8e6353SEvgeniy Ivanov 
4139f8e6353SEvgeniy Ivanov 	/* Loop over the directory entries. */
4149f8e6353SEvgeniy Ivanov 	de = (struct direct *)&dirbuf[0];
4159f8e6353SEvgeniy Ivanov 	ede = (struct direct *)&dirbuf[blksize];
4169f8e6353SEvgeniy Ivanov 	while (de < ede) {
4179f8e6353SEvgeniy Ivanov 		ino = de->d_fileno;
4189f8e6353SEvgeniy Ivanov 		if (params->fstype->needswap) {
4199f8e6353SEvgeniy Ivanov 			ino = bswap32(ino);
4209f8e6353SEvgeniy Ivanov 			de->d_reclen = bswap16(de->d_reclen);
4219f8e6353SEvgeniy Ivanov 		}
4229f8e6353SEvgeniy Ivanov 		if (ino != 0 && strcmp(de->d_name, params->stage2) == 0) {
4239f8e6353SEvgeniy Ivanov 			*((uint32_t *)_ino) = ino;
4249f8e6353SEvgeniy Ivanov 			return (2);
4259f8e6353SEvgeniy Ivanov 		}
4269f8e6353SEvgeniy Ivanov 		if (de->d_reclen == 0)
4279f8e6353SEvgeniy Ivanov 			break;
4289f8e6353SEvgeniy Ivanov 		de = (struct direct *)((char *)de + de->d_reclen);
4299f8e6353SEvgeniy Ivanov 	}
4309f8e6353SEvgeniy Ivanov 
4319f8e6353SEvgeniy Ivanov 	return (1);
4329f8e6353SEvgeniy Ivanov }
4339f8e6353SEvgeniy Ivanov 
4349f8e6353SEvgeniy Ivanov struct findblks_state {
4359f8e6353SEvgeniy Ivanov 	uint32_t	maxblk;
4369f8e6353SEvgeniy Ivanov 	uint32_t	nblk;
4379f8e6353SEvgeniy Ivanov 	ib_block	*blocks;
4389f8e6353SEvgeniy Ivanov };
4399f8e6353SEvgeniy Ivanov 
4409f8e6353SEvgeniy Ivanov /* This callback records the blocks of the secondary bootstrap. */
4419f8e6353SEvgeniy Ivanov static int
ffs_findstage2_blocks(ib_params * params,void * _state,uint64_t blk,uint32_t blksize)4429f8e6353SEvgeniy Ivanov ffs_findstage2_blocks(ib_params *params, void *_state,
4439f8e6353SEvgeniy Ivanov 	uint64_t blk, uint32_t blksize)
4449f8e6353SEvgeniy Ivanov {
4459f8e6353SEvgeniy Ivanov 	struct findblks_state *state = _state;
4469f8e6353SEvgeniy Ivanov 
4479f8e6353SEvgeniy Ivanov 	assert(params != NULL);
4489f8e6353SEvgeniy Ivanov 	assert(params->stage2 != NULL);
4499f8e6353SEvgeniy Ivanov 	assert(_state != NULL);
4509f8e6353SEvgeniy Ivanov 
4519f8e6353SEvgeniy Ivanov 	if (state->nblk == state->maxblk) {
4529f8e6353SEvgeniy Ivanov 		warnx("Secondary bootstrap `%s' has too many blocks (max %d)",
4539f8e6353SEvgeniy Ivanov 		    params->stage2, state->maxblk);
4549f8e6353SEvgeniy Ivanov 		return (0);
4559f8e6353SEvgeniy Ivanov 	}
4569f8e6353SEvgeniy Ivanov 	state->blocks[state->nblk].block = blk;
4579f8e6353SEvgeniy Ivanov 	state->blocks[state->nblk].blocksize = blksize;
4589f8e6353SEvgeniy Ivanov 	state->nblk++;
4599f8e6353SEvgeniy Ivanov 	return (1);
4609f8e6353SEvgeniy Ivanov }
4619f8e6353SEvgeniy Ivanov 
4629f8e6353SEvgeniy Ivanov /*
4639f8e6353SEvgeniy Ivanov  *	publicly visible functions
4649f8e6353SEvgeniy Ivanov  */
4659f8e6353SEvgeniy Ivanov 
4669f8e6353SEvgeniy Ivanov static off_t sblock_try[] = SBLOCKSEARCH;
4679f8e6353SEvgeniy Ivanov 
4689f8e6353SEvgeniy Ivanov int
ffs_match(ib_params * params)4699f8e6353SEvgeniy Ivanov ffs_match(ib_params *params)
4709f8e6353SEvgeniy Ivanov {
4719f8e6353SEvgeniy Ivanov 	return ffs_match_common(params, (off_t) 0);
4729f8e6353SEvgeniy Ivanov }
4739f8e6353SEvgeniy Ivanov 
4749f8e6353SEvgeniy Ivanov int
raid_match(ib_params * params)4759f8e6353SEvgeniy Ivanov raid_match(ib_params *params)
4769f8e6353SEvgeniy Ivanov {
4779f8e6353SEvgeniy Ivanov 	/* XXX Assumes 512 bytes / sector */
4789f8e6353SEvgeniy Ivanov 	if (params->sectorsize != 512) {
4799f8e6353SEvgeniy Ivanov 		warnx("Media is %d bytes/sector."
4809f8e6353SEvgeniy Ivanov 			"  RAID is only supported on 512 bytes/sector media.",
4819f8e6353SEvgeniy Ivanov 			params->sectorsize);
4829f8e6353SEvgeniy Ivanov 		return 0;
4839f8e6353SEvgeniy Ivanov 	}
4849f8e6353SEvgeniy Ivanov 	return ffs_match_common(params, (off_t) RF_PROTECTED_SECTORS);
4859f8e6353SEvgeniy Ivanov }
4869f8e6353SEvgeniy Ivanov 
4879f8e6353SEvgeniy Ivanov int
ffs_match_common(ib_params * params,off_t offset)4889f8e6353SEvgeniy Ivanov ffs_match_common(ib_params *params, off_t offset)
4899f8e6353SEvgeniy Ivanov {
4909f8e6353SEvgeniy Ivanov 	char		sbbuf[SBLOCKSIZE];
4919f8e6353SEvgeniy Ivanov 	struct fs	*fs;
4929f8e6353SEvgeniy Ivanov 	int i;
4939f8e6353SEvgeniy Ivanov 	off_t loc;
4949f8e6353SEvgeniy Ivanov 
4959f8e6353SEvgeniy Ivanov 	assert(params != NULL);
4969f8e6353SEvgeniy Ivanov 	assert(params->fstype != NULL);
4979f8e6353SEvgeniy Ivanov 
4989f8e6353SEvgeniy Ivanov 	fs = (struct fs *)sbbuf;
4999f8e6353SEvgeniy Ivanov 	for (i = 0; sblock_try[i] != -1; i++) {
5009f8e6353SEvgeniy Ivanov 		loc = sblock_try[i] / params->sectorsize + offset;
5019f8e6353SEvgeniy Ivanov 		if (!ffs_read_disk_block(params, loc, SBLOCKSIZE, sbbuf))
5029f8e6353SEvgeniy Ivanov 			continue;
5039f8e6353SEvgeniy Ivanov 		switch (fs->fs_magic) {
5049f8e6353SEvgeniy Ivanov 		case FS_UFS2_MAGIC:
5059f8e6353SEvgeniy Ivanov 			is_ufs2 = 1;
5069f8e6353SEvgeniy Ivanov 			/* FALLTHROUGH */
5079f8e6353SEvgeniy Ivanov 		case FS_UFS1_MAGIC:
5089f8e6353SEvgeniy Ivanov 			params->fstype->needswap = 0;
5099f8e6353SEvgeniy Ivanov 			params->fstype->blocksize = fs->fs_bsize;
5109f8e6353SEvgeniy Ivanov 			params->fstype->sblockloc = loc;
5119f8e6353SEvgeniy Ivanov 			params->fstype->offset = offset;
5129f8e6353SEvgeniy Ivanov 			break;
5139f8e6353SEvgeniy Ivanov #ifndef FFS_NO_SWAP
5149f8e6353SEvgeniy Ivanov 		case FS_UFS2_MAGIC_SWAPPED:
5159f8e6353SEvgeniy Ivanov 			is_ufs2 = 1;
5169f8e6353SEvgeniy Ivanov 			/* FALLTHROUGH */
5179f8e6353SEvgeniy Ivanov 		case FS_UFS1_MAGIC_SWAPPED:
5189f8e6353SEvgeniy Ivanov 			params->fstype->needswap = 1;
5199f8e6353SEvgeniy Ivanov 			params->fstype->blocksize = bswap32(fs->fs_bsize);
5209f8e6353SEvgeniy Ivanov 			params->fstype->sblockloc = loc;
5219f8e6353SEvgeniy Ivanov 			params->fstype->offset = offset;
5229f8e6353SEvgeniy Ivanov 			break;
5239f8e6353SEvgeniy Ivanov #endif
5249f8e6353SEvgeniy Ivanov 		default:
5259f8e6353SEvgeniy Ivanov 			continue;
5269f8e6353SEvgeniy Ivanov 		}
5279f8e6353SEvgeniy Ivanov 		if (!is_ufs2 && sblock_try[i] == SBLOCK_UFS2)
5289f8e6353SEvgeniy Ivanov 			continue;
5299f8e6353SEvgeniy Ivanov 		return 1;
5309f8e6353SEvgeniy Ivanov 	}
5319f8e6353SEvgeniy Ivanov 
5329f8e6353SEvgeniy Ivanov 	return (0);
5339f8e6353SEvgeniy Ivanov }
5349f8e6353SEvgeniy Ivanov 
5359f8e6353SEvgeniy Ivanov int
ffs_findstage2(ib_params * params,uint32_t * maxblk,ib_block * blocks)5369f8e6353SEvgeniy Ivanov ffs_findstage2(ib_params *params, uint32_t *maxblk, ib_block *blocks)
5379f8e6353SEvgeniy Ivanov {
5389f8e6353SEvgeniy Ivanov 	int			rv;
5399f8e6353SEvgeniy Ivanov 	uint32_t		ino;
5409f8e6353SEvgeniy Ivanov 	struct findblks_state	state;
5419f8e6353SEvgeniy Ivanov 
5429f8e6353SEvgeniy Ivanov 	assert(params != NULL);
5439f8e6353SEvgeniy Ivanov 	assert(params->stage2 != NULL);
5449f8e6353SEvgeniy Ivanov 	assert(maxblk != NULL);
5459f8e6353SEvgeniy Ivanov 	assert(blocks != NULL);
5469f8e6353SEvgeniy Ivanov 
5479f8e6353SEvgeniy Ivanov 	if (params->flags & IB_STAGE2START)
5489f8e6353SEvgeniy Ivanov 		return (hardcode_stage2(params, maxblk, blocks));
5499f8e6353SEvgeniy Ivanov 
5509f8e6353SEvgeniy Ivanov 	/* The secondary bootstrap must be clearly in /. */
5519f8e6353SEvgeniy Ivanov 	if (params->stage2[0] == '/')
5529f8e6353SEvgeniy Ivanov 		params->stage2++;
5539f8e6353SEvgeniy Ivanov 	if (strchr(params->stage2, '/') != NULL) {
5549f8e6353SEvgeniy Ivanov 		warnx("The secondary bootstrap `%s' must be in /",
5559f8e6353SEvgeniy Ivanov 		    params->stage2);
5569f8e6353SEvgeniy Ivanov 		warnx("(Path must be relative to the file system in `%s')",
5579f8e6353SEvgeniy Ivanov 		    params->filesystem);
5589f8e6353SEvgeniy Ivanov 		return (0);
5599f8e6353SEvgeniy Ivanov 	}
5609f8e6353SEvgeniy Ivanov 
5619f8e6353SEvgeniy Ivanov 	/* Get the inode number of the secondary bootstrap. */
5629f8e6353SEvgeniy Ivanov 	if (is_ufs2)
563*84d9c625SLionel Sambuc 		rv = ffs_find_disk_blocks_ufs2(params, UFS_ROOTINO,
5649f8e6353SEvgeniy Ivanov 		    ffs_findstage2_ino, &ino);
5659f8e6353SEvgeniy Ivanov 	else
566*84d9c625SLionel Sambuc 		rv = ffs_find_disk_blocks_ufs1(params, UFS_ROOTINO,
5679f8e6353SEvgeniy Ivanov 		    ffs_findstage2_ino, &ino);
5689f8e6353SEvgeniy Ivanov 	if (rv != 2) {
5699f8e6353SEvgeniy Ivanov 		warnx("Could not find secondary bootstrap `%s' in `%s'",
5709f8e6353SEvgeniy Ivanov 		    params->stage2, params->filesystem);
5719f8e6353SEvgeniy Ivanov 		warnx("(Path must be relative to the file system in `%s')",
5729f8e6353SEvgeniy Ivanov 		    params->filesystem);
5739f8e6353SEvgeniy Ivanov 		return (0);
5749f8e6353SEvgeniy Ivanov 	}
5759f8e6353SEvgeniy Ivanov 
5769f8e6353SEvgeniy Ivanov 	/* Record the disk blocks of the secondary bootstrap. */
5779f8e6353SEvgeniy Ivanov 	state.maxblk = *maxblk;
5789f8e6353SEvgeniy Ivanov 	state.nblk = 0;
5799f8e6353SEvgeniy Ivanov 	state.blocks = blocks;
5809f8e6353SEvgeniy Ivanov 	if (is_ufs2)
5819f8e6353SEvgeniy Ivanov 		rv = ffs_find_disk_blocks_ufs2(params, ino,
5829f8e6353SEvgeniy Ivanov 		    ffs_findstage2_blocks, &state);
5839f8e6353SEvgeniy Ivanov 	else
5849f8e6353SEvgeniy Ivanov 		rv = ffs_find_disk_blocks_ufs1(params, ino,
5859f8e6353SEvgeniy Ivanov 		    ffs_findstage2_blocks, &state);
5869f8e6353SEvgeniy Ivanov 	if (! rv) {
5879f8e6353SEvgeniy Ivanov 		return (0);
5889f8e6353SEvgeniy Ivanov 	}
5899f8e6353SEvgeniy Ivanov 
5909f8e6353SEvgeniy Ivanov 	*maxblk = state.nblk;
5919f8e6353SEvgeniy Ivanov 	return (1);
5929f8e6353SEvgeniy Ivanov }
593