root/usr/src/grub/grub-0.97/stage2/fsys_ufs.c
/*  
 *  GRUB  --  GRand Unified Bootloader
 *  Copyright (C) 2006 Free Software Foundation, Inc.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
/*
 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/* From Solaris usr/src/stand/lib/fs/ufs/ufsops.c */

#ifdef  FSYS_UFS

#include "shared.h"
#include "filesys.h"

#include "ufs.h"

/* These are the pools of buffers, etc. */

#define SUPERBLOCK ((struct fs *)(FSYS_BUF + 0x2000))
#define INODE ((struct icommon *)(FSYS_BUF + 0x1000))
#define DIRENT (FSYS_BUF + 0x4000)
#define INDIRBLK1 ((grub_daddr32_t *)(FSYS_BUF + 0x4000)) /* 2+ indir blk */
#define INDIRBLK0 ((grub_daddr32_t *)(FSYS_BUF+ 0x6000))  /* 1st indirect blk */

static int indirblk0, indirblk1;

static int openi(grub_ino_t);
static grub_ino_t dlook(grub_ino_t, char *);
static grub_daddr32_t sbmap(grub_daddr32_t);

/* read superblock and check fs magic */
int
ufs_mount(void)
{
        if (! IS_PC_SLICE_TYPE_SOLARIS(current_slice) ||
            !devread(UFS_SBLOCK, 0, UFS_SBSIZE, (char *)SUPERBLOCK) ||
            SUPERBLOCK->fs_magic != UFS_MAGIC)
                return 0;

        return 1;
}


/*
 * searching for a file, if successful, inode will be loaded in INODE
 * The entry point should really be named ufs_open(char *pathname).
 * For now, keep it consistent with the rest of fsys modules.
 */
int
ufs_dir(char *dirname)
{
        grub_ino_t inode = ROOTINO;     /* start from root */
        char *fname, ch;

        indirblk0 = indirblk1 = 0;

        /* skip leading slashes */
        while (*dirname == '/')
                dirname++;

        while (inode && *dirname && !isspace(*dirname)) {
                if (!openi(inode))
                        return 0;

                /* parse for next path component */
                fname = dirname;
                while (*dirname && !isspace(*dirname) && *dirname != '/')
                        dirname++;
                ch = *dirname;
                *dirname = 0;   /* ensure null termination */

                inode = dlook(inode, fname);
                *dirname = ch;
                while (*dirname == '/')
                        dirname++;
        }

        /* return 1 only if inode exists and is a regular file */
        if  (! openi(inode))
                return (0);
        filepos = 0;
        filemax = INODE->ic_sizelo;
        return (inode && ((INODE->ic_smode & IFMT) == IFREG));
}

/*
 * This is the high-level read function.
 */
int
ufs_read(char *buf, int len)
{
        int off, size, ret = 0, ok;
        grub_daddr32_t lblk, dblk;

        while (len) {
                off = blkoff(SUPERBLOCK, filepos);
                lblk = lblkno(SUPERBLOCK, filepos);
                size = SUPERBLOCK->fs_bsize;
                size -= off;
                if (size > len)
                        size = len;

                if ((dblk = sbmap(lblk)) <= 0) {
                        /* we are in a file hole, just zero the buf */
                        grub_memset(buf, 0, size);
                } else {
                        disk_read_func = disk_read_hook;
                        ok = devread(fsbtodb(SUPERBLOCK, dblk), off, size, buf);
                        disk_read_func = 0;
                        if (!ok)
                                return 0;
                }
                buf += size;
                len -= size;
                filepos += size;
                ret += size;
        }

        return (ret);
}

int
ufs_embed (unsigned long long *start_sector, int needed_sectors)
{
        if (needed_sectors > 14)
                return 0;

        *start_sector = 2;
        return 1;
}

/* read inode and place content in INODE */
static int
openi(grub_ino_t inode)
{
        grub_daddr32_t dblk;
        int off;

        /* get block and byte offset into the block */
        dblk = fsbtodb(SUPERBLOCK, itod(SUPERBLOCK, inode));
        off = itoo(SUPERBLOCK, inode) * sizeof (struct icommon);

        return (devread(dblk, off, sizeof (struct icommon), (char *)INODE));
}

/*
 * Performs fileblock mapping. Convert file block no. to disk block no.
 * Returns 0 when block doesn't exist and <0 when block isn't initialized
 * (i.e belongs to a hole in the file).
 */
grub_daddr32_t
sbmap(grub_daddr32_t bn)
{
        int level, bound, i, index;
        grub_daddr32_t nb, blkno;
        grub_daddr32_t *db = INODE->ic_db;

        /* blocks 0..UFS_NDADDR are direct blocks */
        if (bn < UFS_NDADDR) {
                return db[bn];
        }

        /* determine how many levels of indirection. */
        level = 0;
        bn -= UFS_NDADDR;
        bound = UFS_NINDIR(SUPERBLOCK);
        while (bn >= bound) {
                level++;
                bn -= bound;
                bound *= UFS_NINDIR(SUPERBLOCK);
        }
        if (level >= UFS_NIADDR)        /* bn too big */
                return ((grub_daddr32_t)0);

        /* fetch the first indirect block */
        nb = INODE->ic_ib[level];
        if (nb == 0) {
                return ((grub_daddr32_t)0);
        }
        if (indirblk0 != nb) {
                indirblk0 = 0;
                blkno = fsbtodb(SUPERBLOCK, nb);
                if (!devread(blkno, 0, SUPERBLOCK->fs_bsize,
                    (char *)INDIRBLK0))
                        return (0);
                indirblk0 = nb;
        }
        bound /= UFS_NINDIR(SUPERBLOCK);
        index = (bn / bound) % UFS_NINDIR(SUPERBLOCK);
        nb = INDIRBLK0[index];

        /* fetch through the indirect blocks */
        for (i = 1; i <= level; i++) {
                if (indirblk1 != nb) {
                        blkno = fsbtodb(SUPERBLOCK, nb);
                        if (!devread(blkno, 0, SUPERBLOCK->fs_bsize,
                            (char *)INDIRBLK1))
                                return (0);
                        indirblk1 = nb;
                }
                bound /= UFS_NINDIR(SUPERBLOCK);
                index = (bn / bound) % UFS_NINDIR(SUPERBLOCK);
                nb = INDIRBLK1[index];
                if (nb == 0)
                        return ((grub_daddr32_t)0);
        }

        return (nb);
}

/* search directory content for name, return inode number */
static grub_ino_t
dlook(grub_ino_t dir_ino, char *name)
{
        int loc, off;
        grub_daddr32_t lbn, dbn, dblk;
        struct direct *dp;

        if ((INODE->ic_smode & IFMT) != IFDIR)
                return 0;

        loc = 0;
        while (loc < INODE->ic_sizelo) {
                /* offset into block */
                off = blkoff(SUPERBLOCK, loc);
                if (off == 0) {         /* need to read in a new block */

                        /* get logical block number */
                        lbn = lblkno(SUPERBLOCK, loc);
                        /* resolve indrect blocks */
                        dbn = sbmap(lbn);
                        if (dbn == 0)
                                return (0);

                        dblk = fsbtodb(SUPERBLOCK, dbn);
                        if (!devread(dblk, 0, SUPERBLOCK->fs_bsize,
                            (char *)DIRENT)) {
                                return 0;
                        }
                }

                dp = (struct direct *)(DIRENT + off);
                if (dp->d_ino && substring(name, dp->d_name) == 0)
                        return (dp->d_ino);
                loc += dp->d_reclen;
        }
        return (0);
}

#endif /* FSYS_UFS */