aboutsummaryrefslogtreecommitdiffstats
path: root/tools/libfsimage/ufs/fsys_ufs.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/libfsimage/ufs/fsys_ufs.c')
-rw-r--r--tools/libfsimage/ufs/fsys_ufs.c276
1 files changed, 276 insertions, 0 deletions
diff --git a/tools/libfsimage/ufs/fsys_ufs.c b/tools/libfsimage/ufs/fsys_ufs.c
new file mode 100644
index 0000000000..f1cb917c8c
--- /dev/null
+++ b/tools/libfsimage/ufs/fsys_ufs.c
@@ -0,0 +1,276 @@
+/*
+ * 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 */
+
+#include <fsimage_grub.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 */
+
+#define indirblk0 (*fsig_int1(ffi))
+#define indirblk1 (*fsig_int2(ffi))
+
+static int openi(fsi_file_t *, grub_ino_t);
+static grub_ino_t dlook(fsi_file_t *, grub_ino_t, char *);
+static grub_daddr32_t sbmap(fsi_file_t *, grub_daddr32_t);
+
+/* read superblock and check fs magic */
+int
+ufs_mount(fsi_file_t *ffi)
+{
+ if (/*! IS_PC_SLICE_TYPE_SOLARIS(current_slice) || */
+ !devread(ffi, 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(fsi_file_t *ffi, 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(ffi, 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(ffi, inode, fname);
+ *dirname = ch;
+ while (*dirname == '/')
+ dirname++;
+ }
+
+ /* return 1 only if inode exists and is a regular file */
+ if (! openi(ffi, 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(fsi_file_t *ffi, 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(ffi, 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(ffi, 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 (int *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(fsi_file_t *ffi, 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(ffi, 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(fsi_file_t *ffi, 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(ffi, 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(ffi, 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(fsi_file_t *ffi, 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(ffi, lbn);
+ if (dbn == 0)
+ return (0);
+
+ dblk = fsbtodb(SUPERBLOCK, dbn);
+ if (!devread(ffi, 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);
+}
+
+fsi_plugin_ops_t *
+fsi_init_plugin(int version, fsi_plugin_t *fp, const char **name)
+{
+ static fsig_plugin_ops_t ops = {
+ FSIMAGE_PLUGIN_VERSION,
+ .fpo_mount = ufs_mount,
+ .fpo_dir = ufs_dir,
+ .fpo_read = ufs_read
+ };
+
+ *name = "ufs";
+ return (fsig_init(fp, &ops));
+}