root/sys/arch/hppa/stand/xxboot/readufs_lfs.c
/*      $NetBSD: readufs_lfs.c,v 1.3 2015/08/20 07:50:08 christos Exp $ */
/*      from Id: readufs_lfs.c,v 1.8 2003/12/16 13:54:11 itohy Exp      */

/*
 * FS specific support for 4.4BSD Log-structured Filesystem
 *
 * Written in 1999, 2002, 2003 by ITOH Yasufumi.
 * Public domain.
 *
 * Intended to be used for boot programs (first stage).
 * DON'T ADD ANY FANCY FEATURE.  THIS SHALL BE COMPACT.
 */

#include "readufs.h"

#include <sys/mount.h>
#include <ufs/lfs/lfs.h>

#ifndef USE_UFS1
 #error LFS currently requires USE_UFS1
#endif

static struct lfs32_dinode      ifile_dinode;

#define fsi     (*ufsinfo)
#define fsi_lfs fsi.fs_u.u_lfs

/*
 * Read and check superblock.
 * If it is an LFS, save information from the superblock.
 */
int
try_lfs(void)
{
        struct ufs_info *ufsinfo = &ufs_info;
        struct dlfs     sblk, sblk2;
        struct dlfs     *s = &sblk;
        daddr_t         sbpos;
        int             fsbshift;

#ifdef DEBUG_WITH_STDIO
        printf("trying LFS\n");
#endif
        sbpos =  btodb(LFS_LABELPAD);

        /* read primary superblock */
        for (;;) {
#ifdef DEBUG_WITH_STDIO
                printf("LFS: reading primary sblk at: 0x%x\n", (unsigned)sbpos);
#endif
                RAW_READ(&sblk, sbpos, sizeof sblk);

#ifdef DEBUG_WITH_STDIO
                printf("LFS: sblk: magic: 0x%x, version: %d\n",
                        sblk.dlfs_magic, sblk.dlfs_version);
#endif

                if (sblk.dlfs_magic != LFS_MAGIC)
                        return 1;

#ifdef DEBUG_WITH_STDIO
                printf("LFS: bsize %d, fsize %d, bshift %d, blktodb %d, fsbtodb %d, inopf %d, inopb %d\n",
                    sblk.dlfs_bsize, sblk.dlfs_fsize,
                    sblk.dlfs_bshift, sblk.dlfs_blktodb, sblk.dlfs_fsbtodb,
                    sblk.dlfs_inopf, sblk.dlfs_inopb);
#endif
                if ((fsi_lfs.version = sblk.dlfs_version) == 1) {
                        fsbshift = 0;
                        break;
                } else {
                        daddr_t sbpos1;
#if 0
                        fsbshift = sblk.dlfs_bshift - sblk.dlfs_blktodb + sblk.dlfs_fsbtodb - DEV_BSHIFT;
#endif
                        fsbshift = sblk.dlfs_fsbtodb;
                        sbpos1 = sblk.dlfs_sboffs[0] << fsbshift;
                        if (sbpos == sbpos1)
                                break;
#ifdef DEBUG_WITH_STDIO
                        printf("LFS: correcting primary sblk location\n");
#endif
                        sbpos = sbpos1;
                }
        }

#ifdef DEBUG_WITH_STDIO
        printf("fsbshift: %d\n", fsbshift);
        printf("sboff[1]: %d\n", sblk.dlfs_sboffs[1]);
#endif

        if (sblk.dlfs_sboffs[1] > 0) {
#ifdef DEBUG_WITH_STDIO
                printf("LFS: reading secondary sblk at: 0x%x\n",
                    sblk.dlfs_sboffs[1] << fsbshift);
#endif
                /* read secondary superblock */
                RAW_READ(&sblk2, (daddr_t) sblk.dlfs_sboffs[1] << fsbshift,
                    sizeof sblk2);

#ifdef DEBUG_WITH_STDIO
                printf("LFS: sblk2: magic: 0x%x, version: %d\n",
                        sblk2.dlfs_magic, sblk2.dlfs_version);
#endif

                if (sblk2.dlfs_magic == LFS_MAGIC) {
                        if (fsi_lfs.version == 1) {
                                if (sblk.dlfs_inopf > sblk2.dlfs_inopf)
                                        s = &sblk2;
                        } else {
                                if (sblk.dlfs_serial > sblk2.dlfs_serial)
                                        s = &sblk2;
                        }
                }
        }

        /* This partition looks like an LFS. */
#if 0
        fsi.get_inode = get_lfs_inode;
#endif
        /*
         * version 1: disk addr is in disk sector --- no shifting
         * version 2: disk addr is in fragment
         */
        fsi.fsbtodb = fsbshift;

        /* Get information from the superblock. */
        fsi.bsize = s->dlfs_bsize;
        fsi.nindir = s->dlfs_nindir;
        fsi_lfs.idaddr = s->dlfs_idaddr;
        fsi_lfs.ibsize = (fsi_lfs.version == 1) ? s->dlfs_bsize : s->dlfs_fsize;

        /*
         * version 1: number of inode per block
         * version 2: number of inode per fragment (but in dlfs_inopb)
         */
        fsi_lfs.inopb = s->dlfs_inopb;

        fsi_lfs.ifpb = s->dlfs_ifpb;
        fsi_lfs.ioffset = s->dlfs_cleansz + s->dlfs_segtabsz;

        /* ifile is always used to look-up other inodes, so keep its inode. */
        if (get_lfs_inode(LFS_IFILE_INUM, (union ufs_dinode *)&ifile_dinode))
                return 1;       /* OOPS, failed to find inode of ifile! */

        fsi.fstype = UFSTYPE_LFS;

        return 0;
}

/*
 * Get inode from disk.
 */
int
get_lfs_inode(ino32_t ino, union ufs_dinode *dibuf)
{
        struct ufs_info *ufsinfo = &ufs_info;
        daddr_t daddr;
        char *buf = alloca(fsi.bsize);
        struct lfs32_dinode *di, *diend;
        int i;

        /* Get fs block which contains the specified inode. */
        if (ino == LFS_IFILE_INUM)
                daddr = fsi_lfs.idaddr;
        else {
#ifdef DEBUG_WITH_STDIO
                printf("LFS: ino: %d\nifpb: %d, bsize: %d\n",
                        ino, fsi_lfs.ifpb, fsi.bsize);
#endif
                ufs_read((union ufs_dinode *) &ifile_dinode, buf,
                         ino / fsi_lfs.ifpb + fsi_lfs.ioffset,
                         fsi.bsize);
                i = ino % fsi_lfs.ifpb;
                daddr = (fsi_lfs.version == 1) ?
                    ((IFILE_V1 *) buf + i)->if_daddr
                    : ((IFILE32 *) buf + i)->if_daddr;
        }
#ifdef DEBUG_WITH_STDIO
        printf("LFS(%d): daddr: %d\n", ino, (int) daddr);
#endif

        if (daddr == LFS_UNUSED_DADDR)
                return 1;

        /* Read the inode block. */
        RAW_READ(buf, daddr << fsi.fsbtodb, fsi_lfs.ibsize);

        /* Search for the inode. */
        di = (struct lfs32_dinode *) buf;
        diend = di + fsi_lfs.inopb;

        for ( ; di < diend; di++)
                if (di->di_inumber == ino)
                        goto found;
        /* not found */
        return 1;

found:
#ifdef DEBUG_WITH_STDIO
        printf("LFS: dinode(%d): mode 0%o, nlink %d, inumber %d, size %d, uid %d, db[0] %d\n",
                ino, di->di_mode, di->di_nlink, di->di_inumber,
                (int) di->di_size, di->di_uid, di->di_db[0]);
#endif

#if 0   /* currently UFS1 only */
#if defined(USE_UFS1) && defined(USE_UFS2)
        /* XXX for DI_SIZE() macro */
        if (ufsinfo->ufstype != UFSTYPE_UFS1)
                di->di1.di_size = di->si2.di_size;
#endif
#endif

        dibuf->dil32 = *di;

        return 0;
}