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