/*      $NetBSD: readufs_ffs.c,v 1.2 2022/11/17 06:40:39 chs Exp $      */
/*      from Id: readufs_ffs.c,v 1.8 2004/06/12 04:26:39 itohy Exp      */

/*
* FS specific support for 4.2BSD Fast 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 <ufs/ffs/fs.h>

#define fsi     (*ufsinfo)
#define fsi_ffs fsi.fs_u.u_ffs

/*
* Read and check superblock.
* If it is an FFS, save information from the superblock.
*/
int
try_ffs(void)
{
       union {
               struct fs       sblk;
               unsigned char   pad[SBLOCKSIZE];
       } buf;
       struct ufs_info *ufsinfo = &ufs_info;
       static int sblocs[] = SBLOCKSEARCH;
       int *sbl;
       int magic;

#ifdef DEBUG_WITH_STDIO
       printf("trying FFS\n");
#endif
       /* read FFS superblock */
       for (sbl = sblocs; ; sbl++) {
               if (*sbl == -1)
                       return 1;

               RAW_READ(&buf, (daddr_t) btodb(*sbl), SBLOCKSIZE);

               magic = buf.sblk.fs_magic;
#ifdef DEBUG_WITH_STDIO
               printf("FFS: sblk: pos %d magic 0x%x\n", btodb(*sbl), magic);
#endif
#ifdef USE_UFS1
               if (magic == FS_UFS1_MAGIC
                   && !(buf.sblk.fs_old_flags & FS_FLAGS_UPDATED)) {
                       if (*sbl == SBLOCK_UFS2)
                               /* might be an alternate suberblock */
                               continue;
                       break;
               }
#endif
               if (*sbl != buf.sblk.fs_sblockloc)
                       /* must be an alternate suberblock */
                       continue;

#ifdef USE_UFS1
               if (magic == FS_UFS1_MAGIC)
                       break;
#endif
#ifdef USE_UFS2
               if (magic == FS_UFS2_MAGIC || magic == FS_UFS2EA_MAGIC) {
#ifdef USE_UFS1
                       fsi.ufstype = UFSTYPE_UFS2;
#endif
                       break;
               }
#endif
       }

       /*
        * XXX <ufs/ffs/fs.h> always uses fs_magic
        * (UFS1 only or UFS2 only is impossible)
        */
       fsi_ffs.magic = magic;
#ifdef DEBUG_WITH_STDIO
       printf("FFS: detected UFS%d format\n",
              (magic == FS_UFS2_MAGIC || magic == FS_UFS2EA_MAGIC) + 1);
#endif

       /* This partition looks like an FFS. */
       fsi.fstype = UFSTYPE_FFS;
#if 0
       fsi.get_inode = get_ffs_inode;
#endif

       /* Get information from the superblock. */
       fsi.bsize = buf.sblk.fs_bsize;
       fsi.fsbtodb = buf.sblk.fs_fsbtodb;
       fsi.nindir = buf.sblk.fs_nindir;

       fsi_ffs.iblkno = buf.sblk.fs_iblkno;
       fsi_ffs.old_cgoffset = buf.sblk.fs_old_cgoffset;
       fsi_ffs.old_cgmask = buf.sblk.fs_old_cgmask;
       fsi_ffs.fragshift = buf.sblk.fs_fragshift;
       fsi_ffs.inopb = buf.sblk.fs_inopb;
       fsi_ffs.ipg = buf.sblk.fs_ipg;
       fsi_ffs.fpg = buf.sblk.fs_fpg;

       return 0;
}

/* for inode macros */
#define fs_ipg          fs_u.u_ffs.ipg
#define fs_iblkno       fs_u.u_ffs.iblkno
#define fs_old_cgoffset fs_u.u_ffs.old_cgoffset
#define fs_old_cgmask   fs_u.u_ffs.old_cgmask
#define fs_fpg          fs_u.u_ffs.fpg
#define fs_magic        fs_u.u_ffs.magic
#define fs_inopb        fs_u.u_ffs.inopb
#define fs_fragshift    fs_u.u_ffs.fragshift
#define fs_fsbtodb      fsbtodb

/*
* Get inode from disk.
*/
int
get_ffs_inode(ino32_t ino, union ufs_dinode *dibuf)
{
       struct ufs_info *ufsinfo = &ufs_info;
       union ufs_dinode *buf = alloca((size_t) fsi.bsize);
       union ufs_dinode *di;
       unsigned ioff;

       RAW_READ(buf, FFS_FSBTODB(&fsi, ino_to_fsba(&fsi, ino)),
                       (size_t) fsi.bsize);

       ioff = ino_to_fsbo(&fsi, ino);

#if defined(USE_UFS1) && defined(USE_UFS2)
       if (ufsinfo->ufstype == UFSTYPE_UFS1)
               di = (void *) &(&buf->di1)[ioff];
       else {
               di = (void *) &(&buf->di2)[ioff];

               /* XXX for DI_SIZE() macro */
               di->di1.di_size = di->di2.di_size;
       }
#else
       di = &buf[ioff];
#endif

#ifdef DEBUG_WITH_STDIO
       printf("FFS: dinode(%d): mode 0%o, nlink %d, size %u\n",
               ino, di->di_common.di_mode, di->di_common.di_nlink,
               (unsigned) DI_SIZE(di));
#endif

       if (di->di_common.di_mode == 0)
               return 1;       /* unused inode (file is not found) */

       *dibuf = *di;

       return 0;
}