xref: /netbsd-src/usr.sbin/installboot/ffs.c (revision 87ba0e2a319653af510fae24a6d2975d3589735b)
1*87ba0e2aSchs /*	$NetBSD: ffs.c,v 1.33 2022/11/17 06:40:40 chs Exp $	*/
28eb8919eSlukem 
38eb8919eSlukem /*-
48eb8919eSlukem  * Copyright (c) 2002 The NetBSD Foundation, Inc.
58eb8919eSlukem  * All rights reserved.
68eb8919eSlukem  *
78eb8919eSlukem  * This code is derived from software contributed to The NetBSD Foundation
88eb8919eSlukem  * by Matt Fredette.
98eb8919eSlukem  *
108eb8919eSlukem  * Redistribution and use in source and binary forms, with or without
118eb8919eSlukem  * modification, are permitted provided that the following conditions
128eb8919eSlukem  * are met:
138eb8919eSlukem  * 1. Redistributions of source code must retain the above copyright
148eb8919eSlukem  *    notice, this list of conditions and the following disclaimer.
158eb8919eSlukem  * 2. Redistributions in binary form must reproduce the above copyright
168eb8919eSlukem  *    notice, this list of conditions and the following disclaimer in the
178eb8919eSlukem  *    documentation and/or other materials provided with the distribution.
188eb8919eSlukem  *
198eb8919eSlukem  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
208eb8919eSlukem  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
218eb8919eSlukem  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
228eb8919eSlukem  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
238eb8919eSlukem  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
248eb8919eSlukem  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
258eb8919eSlukem  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
268eb8919eSlukem  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
278eb8919eSlukem  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
288eb8919eSlukem  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
298eb8919eSlukem  * POSSIBILITY OF SUCH DAMAGE.
308eb8919eSlukem  */
318eb8919eSlukem 
32b2f78261Sjmc #if HAVE_NBTOOL_CONFIG_H
33b2f78261Sjmc #include "nbtool_config.h"
34b2f78261Sjmc #endif
35b2f78261Sjmc 
368eb8919eSlukem #include <sys/cdefs.h>
37e5f39b5eStsutsui #if !defined(__lint)
38*87ba0e2aSchs __RCSID("$NetBSD: ffs.c,v 1.33 2022/11/17 06:40:40 chs Exp $");
398eb8919eSlukem #endif	/* !__lint */
408eb8919eSlukem 
418eb8919eSlukem #include <sys/param.h>
42b2f78261Sjmc 
43b2f78261Sjmc #if !HAVE_NBTOOL_CONFIG_H
448eb8919eSlukem #include <sys/mount.h>
45b2f78261Sjmc #endif
468eb8919eSlukem 
478eb8919eSlukem #include <assert.h>
488eb8919eSlukem #include <err.h>
498eb8919eSlukem #include <errno.h>
508eb8919eSlukem #include <fcntl.h>
518eb8919eSlukem #include <stdarg.h>
528eb8919eSlukem #include <stdio.h>
538eb8919eSlukem #include <stdlib.h>
548eb8919eSlukem #include <string.h>
558eb8919eSlukem #include <unistd.h>
568eb8919eSlukem 
578eb8919eSlukem #include "installboot.h"
588eb8919eSlukem 
59f00029f8Sjdc /* From <dev/raidframe/raidframevar.h> */
60f00029f8Sjdc #define RF_PROTECTED_SECTORS 64L
61373c7523Sjdc 
628eb8919eSlukem #undef DIRBLKSIZ
638eb8919eSlukem 
648eb8919eSlukem #include <ufs/ufs/dinode.h>
658eb8919eSlukem #include <ufs/ufs/dir.h>
668eb8919eSlukem #include <ufs/ffs/fs.h>
678eb8919eSlukem #include <ufs/ffs/ffs_extern.h>
688c767ff5Sdsl #ifndef NO_FFS_SWAP
698eb8919eSlukem #include <ufs/ufs/ufs_bswap.h>
708c767ff5Sdsl #else
718c767ff5Sdsl #define	ffs_sb_swap(fs_a, fs_b)
728c767ff5Sdsl #define	ffs_dinode1_swap(inode_a, inode_b)
738c767ff5Sdsl #define	ffs_dinode2_swap(inode_a, inode_b)
748c767ff5Sdsl #endif
758eb8919eSlukem 
76373c7523Sjdc static int	ffs_match_common(ib_params *, off_t);
77d1a5b782Schristos static int	ffs_read_disk_block(ib_params *, uint64_t, int, char []);
7842614ed3Sfvdl static int	ffs_find_disk_blocks_ufs1(ib_params *, ino_t,
7942614ed3Sfvdl 		    int (*)(ib_params *, void *, uint64_t, uint32_t), void *);
8042614ed3Sfvdl static int	ffs_find_disk_blocks_ufs2(ib_params *, ino_t,
8142614ed3Sfvdl 		    int (*)(ib_params *, void *, uint64_t, uint32_t), void *);
8242614ed3Sfvdl static int	ffs_findstage2_ino(ib_params *, void *, uint64_t, uint32_t);
8342614ed3Sfvdl static int	ffs_findstage2_blocks(ib_params *, void *, uint64_t, uint32_t);
8442614ed3Sfvdl 
8542614ed3Sfvdl static int is_ufs2;
861bdd92eeSlukem 
871bdd92eeSlukem 
888eb8919eSlukem /* This reads a disk block from the filesystem. */
898eb8919eSlukem static int
ffs_read_disk_block(ib_params * params,uint64_t blkno,int size,char blk[])90d1a5b782Schristos ffs_read_disk_block(ib_params *params, uint64_t blkno, int size, char blk[])
918eb8919eSlukem {
928eb8919eSlukem 	int	rv;
938eb8919eSlukem 
941a478e40Slukem 	assert(params != NULL);
958eb8919eSlukem 	assert(params->filesystem != NULL);
968eb8919eSlukem 	assert(params->fsfd != -1);
978eb8919eSlukem 	assert(size > 0);
988eb8919eSlukem 	assert(blk != NULL);
998eb8919eSlukem 
10017ad8eceStsutsui 	rv = pread(params->fsfd, blk, size, blkno * params->sectorsize);
1018eb8919eSlukem 	if (rv == -1) {
102ad1f16e9She 		warn("Reading block %llu in `%s'",
103ad1f16e9She 		    (unsigned long long)blkno, params->filesystem);
1048eb8919eSlukem 		return (0);
1058eb8919eSlukem 	} else if (rv != size) {
106ad1f16e9She 		warnx("Reading block %llu in `%s': short read",
107ad1f16e9She 		    (unsigned long long)blkno, params->filesystem);
1088eb8919eSlukem 		return (0);
1098eb8919eSlukem 	}
1108eb8919eSlukem 
1118eb8919eSlukem 	return (1);
1128eb8919eSlukem }
1138eb8919eSlukem 
1148eb8919eSlukem /*
1158eb8919eSlukem  * This iterates over the data blocks belonging to an inode,
1168eb8919eSlukem  * making a callback each iteration with the disk block number
1178eb8919eSlukem  * and the size.
1188eb8919eSlukem  */
1198eb8919eSlukem static int
ffs_find_disk_blocks_ufs1(ib_params * params,ino_t ino,int (* callback)(ib_params *,void *,uint64_t,uint32_t),void * state)12042614ed3Sfvdl ffs_find_disk_blocks_ufs1(ib_params *params, ino_t ino,
12142614ed3Sfvdl 	int (*callback)(ib_params *, void *, uint64_t, uint32_t),
1228eb8919eSlukem 	void *state)
1238eb8919eSlukem {
12442614ed3Sfvdl 	char		sbbuf[SBLOCKSIZE];
1258eb8919eSlukem 	struct fs	*fs;
1268eb8919eSlukem 	char		inodebuf[MAXBSIZE];
12742614ed3Sfvdl 	struct ufs1_dinode	*inode;
1288eb8919eSlukem 	int		level_i;
12942614ed3Sfvdl 	int32_t	blk, lblk, nblk;
1308eb8919eSlukem 	int		rv;
1318eb8919eSlukem #define LEVELS 4
1328eb8919eSlukem 	struct {
133a3ff3a30Sfvdl 		int32_t		*blknums;
1348eb8919eSlukem 		unsigned long	blkcount;
1358eb8919eSlukem 		char		diskbuf[MAXBSIZE];
1368eb8919eSlukem 	} level[LEVELS];
1378eb8919eSlukem 
1381a478e40Slukem 	assert(params != NULL);
1391bdd92eeSlukem 	assert(params->fstype != NULL);
1401a478e40Slukem 	assert(callback != NULL);
1411a478e40Slukem 	assert(state != NULL);
1421a478e40Slukem 
1438eb8919eSlukem 	/* Read the superblock. */
144798d0c81She 	if (!ffs_read_disk_block(params, params->fstype->sblockloc, SBLOCKSIZE,
14542614ed3Sfvdl 	    sbbuf))
1468eb8919eSlukem 		return (0);
1478eb8919eSlukem 	fs = (struct fs *)sbbuf;
1482f5213daShe #ifndef NO_FFS_SWAP
1491bdd92eeSlukem 	if (params->fstype->needswap)
1508eb8919eSlukem 		ffs_sb_swap(fs, fs);
1512f5213daShe #endif
1528eb8919eSlukem 
1538eb8919eSlukem 	if (fs->fs_inopb <= 0) {
1548eb8919eSlukem 		warnx("Bad inopb %d in superblock in `%s'",
1558eb8919eSlukem 		    fs->fs_inopb, params->filesystem);
1568eb8919eSlukem 		return (0);
1578eb8919eSlukem 	}
1588eb8919eSlukem 
1598eb8919eSlukem 	/* Read the inode. */
160373c7523Sjdc 	if (! ffs_read_disk_block(params,
1612737439dSdholland 		FFS_FSBTODB(fs, ino_to_fsba(fs, ino)) + params->fstype->offset,
1628eb8919eSlukem 		fs->fs_bsize, inodebuf))
1638eb8919eSlukem 		return (0);
16442614ed3Sfvdl 	inode = (struct ufs1_dinode *)inodebuf;
1658eb8919eSlukem 	inode += ino_to_fsbo(fs, ino);
1662f5213daShe #ifndef NO_FFS_SWAP
1671bdd92eeSlukem 	if (params->fstype->needswap)
16842614ed3Sfvdl 		ffs_dinode1_swap(inode, inode);
1692f5213daShe #endif
1708eb8919eSlukem 
1718eb8919eSlukem 	/* Get the block count and initialize for our block walk. */
1728eb8919eSlukem 	nblk = howmany(inode->di_size, fs->fs_bsize);
1738eb8919eSlukem 	lblk = 0;
1748eb8919eSlukem 	level_i = 0;
1758eb8919eSlukem 	level[0].blknums = &inode->di_db[0];
176dcd34a91Sdholland 	level[0].blkcount = UFS_NDADDR;
1778eb8919eSlukem 	level[1].blknums = &inode->di_ib[0];
1788eb8919eSlukem 	level[1].blkcount = 1;
1798eb8919eSlukem 	level[2].blknums = &inode->di_ib[1];
1808eb8919eSlukem 	level[2].blkcount = 1;
1818eb8919eSlukem 	level[3].blknums = &inode->di_ib[2];
1828eb8919eSlukem 	level[3].blkcount = 1;
1838eb8919eSlukem 
1848eb8919eSlukem 	/* Walk the data blocks. */
1858eb8919eSlukem 	while (nblk > 0) {
1868eb8919eSlukem 
1878eb8919eSlukem 		/*
1888eb8919eSlukem 		 * If there are no more blocks at this indirection
1898eb8919eSlukem 		 * level, move up one indirection level and loop.
1908eb8919eSlukem 		 */
1918eb8919eSlukem 		if (level[level_i].blkcount == 0) {
1928eb8919eSlukem 			if (++level_i == LEVELS)
1938eb8919eSlukem 				break;
1948eb8919eSlukem 			continue;
1958eb8919eSlukem 		}
1968eb8919eSlukem 
1978eb8919eSlukem 		/* Get the next block at this level. */
1988eb8919eSlukem 		blk = *(level[level_i].blknums++);
1998eb8919eSlukem 		level[level_i].blkcount--;
2001bdd92eeSlukem 		if (params->fstype->needswap)
2018eb8919eSlukem 			blk = bswap32(blk);
2028eb8919eSlukem 
2038eb8919eSlukem #if 0
2048eb8919eSlukem 		fprintf(stderr, "ino %lu blk %lu level %d\n", ino, blk,
2058eb8919eSlukem 		    level_i);
2068eb8919eSlukem #endif
2078eb8919eSlukem 
2088eb8919eSlukem 		/*
2098eb8919eSlukem 		 * If we're not at the direct level, descend one
2108eb8919eSlukem 		 * level, read in that level's new block list,
2118eb8919eSlukem 		 * and loop.
2128eb8919eSlukem 		 */
2138eb8919eSlukem 		if (level_i > 0) {
2148eb8919eSlukem 			level_i--;
2158eb8919eSlukem 			if (blk == 0)
2168eb8919eSlukem 				memset(level[level_i].diskbuf, 0, MAXBSIZE);
2178eb8919eSlukem 			else if (! ffs_read_disk_block(params,
2182737439dSdholland 				FFS_FSBTODB(fs, blk) + params->fstype->offset,
2198eb8919eSlukem 				fs->fs_bsize, level[level_i].diskbuf))
2208eb8919eSlukem 				return (0);
221a3ff3a30Sfvdl 			/* XXX ondisk32 */
2228eb8919eSlukem 			level[level_i].blknums =
223a3ff3a30Sfvdl 				(int32_t *)level[level_i].diskbuf;
224f1333577Sdholland 			level[level_i].blkcount = FFS_NINDIR(fs);
2258eb8919eSlukem 			continue;
2268eb8919eSlukem 		}
2278eb8919eSlukem 
2288eb8919eSlukem 		/* blk is the next direct level block. */
2298eb8919eSlukem #if 0
2308eb8919eSlukem 		fprintf(stderr, "ino %lu db %lu blksize %lu\n", ino,
2312737439dSdholland 		    FFS_FSBTODB(fs, blk), ffs_sblksize(fs, inode->di_size, lblk));
2328eb8919eSlukem #endif
2338eb8919eSlukem 		rv = (*callback)(params, state,
2342737439dSdholland 		    FFS_FSBTODB(fs, blk) + params->fstype->offset,
235f1333577Sdholland 		    ffs_sblksize(fs, (int64_t)inode->di_size, lblk));
23642614ed3Sfvdl 		lblk++;
23742614ed3Sfvdl 		nblk--;
23842614ed3Sfvdl 		if (rv != 1)
23942614ed3Sfvdl 			return (rv);
24042614ed3Sfvdl 	}
24142614ed3Sfvdl 
24242614ed3Sfvdl 	if (nblk != 0) {
243c4ee9f6dSchristos 		warnx("Inode %llu in `%s' ran out of blocks?",
244c4ee9f6dSchristos 		    (unsigned long long)ino, params->filesystem);
24542614ed3Sfvdl 		return (0);
24642614ed3Sfvdl 	}
24742614ed3Sfvdl 
24842614ed3Sfvdl 	return (1);
24942614ed3Sfvdl }
25042614ed3Sfvdl 
25142614ed3Sfvdl /*
25242614ed3Sfvdl  * This iterates over the data blocks belonging to an inode,
25342614ed3Sfvdl  * making a callback each iteration with the disk block number
25442614ed3Sfvdl  * and the size.
25542614ed3Sfvdl  */
25642614ed3Sfvdl static int
ffs_find_disk_blocks_ufs2(ib_params * params,ino_t ino,int (* callback)(ib_params *,void *,uint64_t,uint32_t),void * state)25742614ed3Sfvdl ffs_find_disk_blocks_ufs2(ib_params *params, ino_t ino,
25842614ed3Sfvdl 	int (*callback)(ib_params *, void *, uint64_t, uint32_t),
25942614ed3Sfvdl 	void *state)
26042614ed3Sfvdl {
26142614ed3Sfvdl 	char		sbbuf[SBLOCKSIZE];
26242614ed3Sfvdl 	struct fs	*fs;
26342614ed3Sfvdl 	char		inodebuf[MAXBSIZE];
26442614ed3Sfvdl 	struct ufs2_dinode	*inode;
26542614ed3Sfvdl 	int		level_i;
26642614ed3Sfvdl 	int64_t		blk, lblk, nblk;
26742614ed3Sfvdl 	int		rv;
26842614ed3Sfvdl #define LEVELS 4
26942614ed3Sfvdl 	struct {
27042614ed3Sfvdl 		int64_t		*blknums;
27142614ed3Sfvdl 		unsigned long	blkcount;
27242614ed3Sfvdl 		char		diskbuf[MAXBSIZE];
27342614ed3Sfvdl 	} level[LEVELS];
27442614ed3Sfvdl 
27542614ed3Sfvdl 	assert(params != NULL);
27642614ed3Sfvdl 	assert(params->fstype != NULL);
27742614ed3Sfvdl 	assert(callback != NULL);
27842614ed3Sfvdl 	assert(state != NULL);
27942614ed3Sfvdl 
28042614ed3Sfvdl 	/* Read the superblock. */
281798d0c81She 	if (!ffs_read_disk_block(params, params->fstype->sblockloc, SBLOCKSIZE,
28242614ed3Sfvdl 	    sbbuf))
28342614ed3Sfvdl 		return (0);
28442614ed3Sfvdl 	fs = (struct fs *)sbbuf;
2852f5213daShe #ifndef NO_FFS_SWAP
28642614ed3Sfvdl 	if (params->fstype->needswap)
28742614ed3Sfvdl 		ffs_sb_swap(fs, fs);
2882f5213daShe #endif
28942614ed3Sfvdl 
29042614ed3Sfvdl 	if (fs->fs_inopb <= 0) {
29142614ed3Sfvdl 		warnx("Bad inopb %d in superblock in `%s'",
29242614ed3Sfvdl 		    fs->fs_inopb, params->filesystem);
29342614ed3Sfvdl 		return (0);
29442614ed3Sfvdl 	}
29542614ed3Sfvdl 
29642614ed3Sfvdl 	/* Read the inode. */
2974f0a5e98Sbad 	if (! ffs_read_disk_block(params,
2982737439dSdholland 		FFS_FSBTODB(fs, ino_to_fsba(fs, ino)) + params->fstype->offset,
29942614ed3Sfvdl 		fs->fs_bsize, inodebuf))
30042614ed3Sfvdl 		return (0);
30142614ed3Sfvdl 	inode = (struct ufs2_dinode *)inodebuf;
30242614ed3Sfvdl 	inode += ino_to_fsbo(fs, ino);
3032f5213daShe #ifndef NO_FFS_SWAP
30442614ed3Sfvdl 	if (params->fstype->needswap)
30542614ed3Sfvdl 		ffs_dinode2_swap(inode, inode);
3062f5213daShe #endif
30742614ed3Sfvdl 
30842614ed3Sfvdl 	/* Get the block count and initialize for our block walk. */
30942614ed3Sfvdl 	nblk = howmany(inode->di_size, fs->fs_bsize);
31042614ed3Sfvdl 	lblk = 0;
31142614ed3Sfvdl 	level_i = 0;
31242614ed3Sfvdl 	level[0].blknums = &inode->di_db[0];
313dcd34a91Sdholland 	level[0].blkcount = UFS_NDADDR;
31442614ed3Sfvdl 	level[1].blknums = &inode->di_ib[0];
31542614ed3Sfvdl 	level[1].blkcount = 1;
31642614ed3Sfvdl 	level[2].blknums = &inode->di_ib[1];
31742614ed3Sfvdl 	level[2].blkcount = 1;
31842614ed3Sfvdl 	level[3].blknums = &inode->di_ib[2];
31942614ed3Sfvdl 	level[3].blkcount = 1;
32042614ed3Sfvdl 
32142614ed3Sfvdl 	/* Walk the data blocks. */
32242614ed3Sfvdl 	while (nblk > 0) {
32342614ed3Sfvdl 
32442614ed3Sfvdl 		/*
32542614ed3Sfvdl 		 * If there are no more blocks at this indirection
32642614ed3Sfvdl 		 * level, move up one indirection level and loop.
32742614ed3Sfvdl 		 */
32842614ed3Sfvdl 		if (level[level_i].blkcount == 0) {
32942614ed3Sfvdl 			if (++level_i == LEVELS)
33042614ed3Sfvdl 				break;
33142614ed3Sfvdl 			continue;
33242614ed3Sfvdl 		}
33342614ed3Sfvdl 
33442614ed3Sfvdl 		/* Get the next block at this level. */
33542614ed3Sfvdl 		blk = *(level[level_i].blknums++);
33642614ed3Sfvdl 		level[level_i].blkcount--;
33742614ed3Sfvdl 		if (params->fstype->needswap)
33842614ed3Sfvdl 			blk = bswap64(blk);
33942614ed3Sfvdl 
34042614ed3Sfvdl #if 0
34142614ed3Sfvdl 		fprintf(stderr, "ino %lu blk %llu level %d\n", ino,
34242614ed3Sfvdl 		    (unsigned long long)blk, level_i);
34342614ed3Sfvdl #endif
34442614ed3Sfvdl 
34542614ed3Sfvdl 		/*
34642614ed3Sfvdl 		 * If we're not at the direct level, descend one
34742614ed3Sfvdl 		 * level, read in that level's new block list,
34842614ed3Sfvdl 		 * and loop.
34942614ed3Sfvdl 		 */
35042614ed3Sfvdl 		if (level_i > 0) {
35142614ed3Sfvdl 			level_i--;
35242614ed3Sfvdl 			if (blk == 0)
35342614ed3Sfvdl 				memset(level[level_i].diskbuf, 0, MAXBSIZE);
35442614ed3Sfvdl 			else if (! ffs_read_disk_block(params,
3552737439dSdholland 				FFS_FSBTODB(fs, blk) + params->fstype->offset,
35642614ed3Sfvdl 				fs->fs_bsize, level[level_i].diskbuf))
35742614ed3Sfvdl 				return (0);
35842614ed3Sfvdl 			level[level_i].blknums =
35942614ed3Sfvdl 				(int64_t *)level[level_i].diskbuf;
360f1333577Sdholland 			level[level_i].blkcount = FFS_NINDIR(fs);
36142614ed3Sfvdl 			continue;
36242614ed3Sfvdl 		}
36342614ed3Sfvdl 
36442614ed3Sfvdl 		/* blk is the next direct level block. */
36542614ed3Sfvdl #if 0
36642614ed3Sfvdl 		fprintf(stderr, "ino %lu db %llu blksize %lu\n", ino,
3672737439dSdholland 		    FFS_FSBTODB(fs, blk), ffs_sblksize(fs, inode->di_size, lblk));
36842614ed3Sfvdl #endif
36942614ed3Sfvdl 		rv = (*callback)(params, state,
3702737439dSdholland 		    FFS_FSBTODB(fs, blk) + params->fstype->offset,
371f1333577Sdholland 		    ffs_sblksize(fs, (int64_t)inode->di_size, lblk));
3728eb8919eSlukem 		lblk++;
3738eb8919eSlukem 		nblk--;
3748eb8919eSlukem 		if (rv != 1)
3758eb8919eSlukem 			return (rv);
3768eb8919eSlukem 	}
3778eb8919eSlukem 
3788eb8919eSlukem 	if (nblk != 0) {
379c4ee9f6dSchristos 		warnx("Inode %llu in `%s' ran out of blocks?",
380c4ee9f6dSchristos 		    (unsigned long long)ino, params->filesystem);
3818eb8919eSlukem 		return (0);
3828eb8919eSlukem 	}
3838eb8919eSlukem 
3848eb8919eSlukem 	return (1);
3858eb8919eSlukem }
3868eb8919eSlukem 
3878eb8919eSlukem /*
3888eb8919eSlukem  * This callback reads a block of the root directory,
3898eb8919eSlukem  * searches for an entry for the secondary bootstrap,
3908eb8919eSlukem  * and saves the inode number if one is found.
3918eb8919eSlukem  */
3928eb8919eSlukem static int
ffs_findstage2_ino(ib_params * params,void * _ino,uint64_t blk,uint32_t blksize)3938eb8919eSlukem ffs_findstage2_ino(ib_params *params, void *_ino,
39442614ed3Sfvdl 	uint64_t blk, uint32_t blksize)
3958eb8919eSlukem {
3968eb8919eSlukem 	char		dirbuf[MAXBSIZE];
3978eb8919eSlukem 	struct direct	*de, *ede;
3988eb8919eSlukem 	uint32_t	ino;
3998eb8919eSlukem 
4001a478e40Slukem 	assert(params != NULL);
4011bdd92eeSlukem 	assert(params->fstype != NULL);
402033fe17bSlukem 	assert(params->stage2 != NULL);
4031a478e40Slukem 	assert(_ino != NULL);
4041a478e40Slukem 
4058eb8919eSlukem 	/* Skip directory holes. */
4068eb8919eSlukem 	if (blk == 0)
4078eb8919eSlukem 		return (1);
4088eb8919eSlukem 
4098eb8919eSlukem 	/* Read the directory block. */
4108eb8919eSlukem 	if (! ffs_read_disk_block(params, blk, blksize, dirbuf))
4118eb8919eSlukem 		return (0);
4128eb8919eSlukem 
4138eb8919eSlukem 	/* Loop over the directory entries. */
4148eb8919eSlukem 	de = (struct direct *)&dirbuf[0];
4158eb8919eSlukem 	ede = (struct direct *)&dirbuf[blksize];
4168eb8919eSlukem 	while (de < ede) {
417327a5715Sthorpej 		ino = de->d_fileno;
4181bdd92eeSlukem 		if (params->fstype->needswap) {
4198eb8919eSlukem 			ino = bswap32(ino);
4208eb8919eSlukem 			de->d_reclen = bswap16(de->d_reclen);
4218eb8919eSlukem 		}
4228eb8919eSlukem 		if (ino != 0 && strcmp(de->d_name, params->stage2) == 0) {
4238eb8919eSlukem 			*((uint32_t *)_ino) = ino;
4248eb8919eSlukem 			return (2);
4258eb8919eSlukem 		}
426a4a641e5Spk 		if (de->d_reclen == 0)
427a4a641e5Spk 			break;
4288eb8919eSlukem 		de = (struct direct *)((char *)de + de->d_reclen);
4298eb8919eSlukem 	}
4308eb8919eSlukem 
4318eb8919eSlukem 	return (1);
4328eb8919eSlukem }
4338eb8919eSlukem 
4348eb8919eSlukem struct findblks_state {
4358eb8919eSlukem 	uint32_t	maxblk;
4368eb8919eSlukem 	uint32_t	nblk;
4378eb8919eSlukem 	ib_block	*blocks;
4388eb8919eSlukem };
4398eb8919eSlukem 
4408eb8919eSlukem /* This callback records the blocks of the secondary bootstrap. */
4418eb8919eSlukem static int
ffs_findstage2_blocks(ib_params * params,void * _state,uint64_t blk,uint32_t blksize)4428eb8919eSlukem ffs_findstage2_blocks(ib_params *params, void *_state,
44342614ed3Sfvdl 	uint64_t blk, uint32_t blksize)
4448eb8919eSlukem {
4458eb8919eSlukem 	struct findblks_state *state = _state;
4468eb8919eSlukem 
4471a478e40Slukem 	assert(params != NULL);
448033fe17bSlukem 	assert(params->stage2 != NULL);
4491a478e40Slukem 	assert(_state != NULL);
4501a478e40Slukem 
4518eb8919eSlukem 	if (state->nblk == state->maxblk) {
452966b42a3Slukem 		warnx("Secondary bootstrap `%s' has too many blocks (max %d)",
453966b42a3Slukem 		    params->stage2, state->maxblk);
4548eb8919eSlukem 		return (0);
4558eb8919eSlukem 	}
4568eb8919eSlukem 	state->blocks[state->nblk].block = blk;
4578eb8919eSlukem 	state->blocks[state->nblk].blocksize = blksize;
4588eb8919eSlukem 	state->nblk++;
4598eb8919eSlukem 	return (1);
4608eb8919eSlukem }
4618eb8919eSlukem 
4621bdd92eeSlukem /*
463b5589158Slukem  *	publicly visible functions
4641bdd92eeSlukem  */
4658eb8919eSlukem 
46642614ed3Sfvdl static off_t sblock_try[] = SBLOCKSEARCH;
46742614ed3Sfvdl 
4688eb8919eSlukem int
ffs_match(ib_params * params)4698eb8919eSlukem ffs_match(ib_params *params)
4708eb8919eSlukem {
471373c7523Sjdc 	return ffs_match_common(params, (off_t) 0);
472373c7523Sjdc }
473373c7523Sjdc 
474373c7523Sjdc int
raid_match(ib_params * params)475373c7523Sjdc raid_match(ib_params *params)
476373c7523Sjdc {
477373c7523Sjdc 	/* XXX Assumes 512 bytes / sector */
47817ad8eceStsutsui 	if (params->sectorsize != 512) {
479373c7523Sjdc 		warnx("Media is %d bytes/sector."
480373c7523Sjdc 			"  RAID is only supported on 512 bytes/sector media.",
48117ad8eceStsutsui 			params->sectorsize);
482373c7523Sjdc 		return 0;
483373c7523Sjdc 	}
484373c7523Sjdc 	return ffs_match_common(params, (off_t) RF_PROTECTED_SECTORS);
485373c7523Sjdc }
486373c7523Sjdc 
487373c7523Sjdc int
ffs_match_common(ib_params * params,off_t offset)488373c7523Sjdc ffs_match_common(ib_params *params, off_t offset)
489373c7523Sjdc {
49042614ed3Sfvdl 	char		sbbuf[SBLOCKSIZE];
4918eb8919eSlukem 	struct fs	*fs;
49242614ed3Sfvdl 	int i;
49342614ed3Sfvdl 	off_t loc;
4948eb8919eSlukem 
4951a478e40Slukem 	assert(params != NULL);
4961bdd92eeSlukem 	assert(params->fstype != NULL);
4971a478e40Slukem 
4988eb8919eSlukem 	fs = (struct fs *)sbbuf;
49942614ed3Sfvdl 	for (i = 0; sblock_try[i] != -1; i++) {
50017ad8eceStsutsui 		loc = sblock_try[i] / params->sectorsize + offset;
50142614ed3Sfvdl 		if (!ffs_read_disk_block(params, loc, SBLOCKSIZE, sbbuf))
50242614ed3Sfvdl 			continue;
50342614ed3Sfvdl 		switch (fs->fs_magic) {
50442614ed3Sfvdl 		case FS_UFS2_MAGIC:
505*87ba0e2aSchs 		case FS_UFS2EA_MAGIC:
50642614ed3Sfvdl 			is_ufs2 = 1;
50742614ed3Sfvdl 			/* FALLTHROUGH */
50842614ed3Sfvdl 		case FS_UFS1_MAGIC:
5091bdd92eeSlukem 			params->fstype->needswap = 0;
5101bdd92eeSlukem 			params->fstype->blocksize = fs->fs_bsize;
51142614ed3Sfvdl 			params->fstype->sblockloc = loc;
512373c7523Sjdc 			params->fstype->offset = offset;
51343d718b9Sdsl 			break;
5148c767ff5Sdsl #ifndef FFS_NO_SWAP
51542614ed3Sfvdl 		case FS_UFS2_MAGIC_SWAPPED:
516*87ba0e2aSchs 		case FS_UFS2EA_MAGIC_SWAPPED:
51742614ed3Sfvdl 			is_ufs2 = 1;
51842614ed3Sfvdl 			/* FALLTHROUGH */
51942614ed3Sfvdl 		case FS_UFS1_MAGIC_SWAPPED:
5201bdd92eeSlukem 			params->fstype->needswap = 1;
5211bdd92eeSlukem 			params->fstype->blocksize = bswap32(fs->fs_bsize);
52242614ed3Sfvdl 			params->fstype->sblockloc = loc;
523373c7523Sjdc 			params->fstype->offset = offset;
52443d718b9Sdsl 			break;
5258c767ff5Sdsl #endif
52642614ed3Sfvdl 		default:
52742614ed3Sfvdl 			continue;
52842614ed3Sfvdl 		}
52943d718b9Sdsl 		if (!is_ufs2 && sblock_try[i] == SBLOCK_UFS2)
53043d718b9Sdsl 			continue;
53143d718b9Sdsl 		return 1;
5328eb8919eSlukem 	}
5338eb8919eSlukem 
53442614ed3Sfvdl 	return (0);
5351bdd92eeSlukem }
5361bdd92eeSlukem 
5378eb8919eSlukem int
ffs_findstage2(ib_params * params,uint32_t * maxblk,ib_block * blocks)5388eb8919eSlukem ffs_findstage2(ib_params *params, uint32_t *maxblk, ib_block *blocks)
5398eb8919eSlukem {
5408eb8919eSlukem 	int			rv;
5418eb8919eSlukem 	uint32_t		ino;
5428eb8919eSlukem 	struct findblks_state	state;
5438eb8919eSlukem 
5441a478e40Slukem 	assert(params != NULL);
5458eb8919eSlukem 	assert(params->stage2 != NULL);
5461a478e40Slukem 	assert(maxblk != NULL);
5471a478e40Slukem 	assert(blocks != NULL);
5488eb8919eSlukem 
5491bdd92eeSlukem 	if (params->flags & IB_STAGE2START)
5501bdd92eeSlukem 		return (hardcode_stage2(params, maxblk, blocks));
5511bdd92eeSlukem 
5528eb8919eSlukem 	/* The secondary bootstrap must be clearly in /. */
5538eb8919eSlukem 	if (params->stage2[0] == '/')
5548eb8919eSlukem 		params->stage2++;
5558eb8919eSlukem 	if (strchr(params->stage2, '/') != NULL) {
5568eb8919eSlukem 		warnx("The secondary bootstrap `%s' must be in /",
5578eb8919eSlukem 		    params->stage2);
558984db960Sapb 		warnx("(Path must be relative to the file system in `%s')",
559984db960Sapb 		    params->filesystem);
5608eb8919eSlukem 		return (0);
5618eb8919eSlukem 	}
5628eb8919eSlukem 
5638eb8919eSlukem 	/* Get the inode number of the secondary bootstrap. */
56442614ed3Sfvdl 	if (is_ufs2)
565dcd34a91Sdholland 		rv = ffs_find_disk_blocks_ufs2(params, UFS_ROOTINO,
56642614ed3Sfvdl 		    ffs_findstage2_ino, &ino);
56742614ed3Sfvdl 	else
568dcd34a91Sdholland 		rv = ffs_find_disk_blocks_ufs1(params, UFS_ROOTINO,
56942614ed3Sfvdl 		    ffs_findstage2_ino, &ino);
570966b42a3Slukem 	if (rv != 2) {
571966b42a3Slukem 		warnx("Could not find secondary bootstrap `%s' in `%s'",
572966b42a3Slukem 		    params->stage2, params->filesystem);
573984db960Sapb 		warnx("(Path must be relative to the file system in `%s')",
574984db960Sapb 		    params->filesystem);
5758eb8919eSlukem 		return (0);
576966b42a3Slukem 	}
5778eb8919eSlukem 
5788eb8919eSlukem 	/* Record the disk blocks of the secondary bootstrap. */
5798eb8919eSlukem 	state.maxblk = *maxblk;
5808eb8919eSlukem 	state.nblk = 0;
5818eb8919eSlukem 	state.blocks = blocks;
58242614ed3Sfvdl 	if (is_ufs2)
58342614ed3Sfvdl 		rv = ffs_find_disk_blocks_ufs2(params, ino,
58442614ed3Sfvdl 		    ffs_findstage2_blocks, &state);
585eb9a70f8Sdsl 	else
58642614ed3Sfvdl 		rv = ffs_find_disk_blocks_ufs1(params, ino,
58742614ed3Sfvdl 		    ffs_findstage2_blocks, &state);
588966b42a3Slukem 	if (! rv) {
5898eb8919eSlukem 		return (0);
590966b42a3Slukem 	}
5918eb8919eSlukem 
5928eb8919eSlukem 	*maxblk = state.nblk;
5938eb8919eSlukem 	return (1);
5948eb8919eSlukem }
595