summaryrefslogtreecommitdiff
path: root/usr/src/common/fs
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/common/fs
downloadillumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/common/fs')
-rw-r--r--usr/src/common/fs/hsfs.c760
-rw-r--r--usr/src/common/fs/pcfilep.h231
-rw-r--r--usr/src/common/fs/pcfs.c623
-rw-r--r--usr/src/common/fs/ufsops.c737
4 files changed, 2351 insertions, 0 deletions
diff --git a/usr/src/common/fs/hsfs.c b/usr/src/common/fs/hsfs.c
new file mode 100644
index 0000000000..2ebb7290e8
--- /dev/null
+++ b/usr/src/common/fs/hsfs.c
@@ -0,0 +1,760 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Basic file system reading code for standalone I/O system.
+ * Simulates a primitive UNIX I/O system (read(), write(), open(), etc).
+ * Does not support writes.
+ */
+
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <sys/vnode.h>
+#include <sys/fs/ufs_fsdir.h>
+#include <sys/fs/ufs_fs.h>
+#include <sys/fs/ufs_inode.h>
+
+#include <sys/fs/hsfs_spec.h>
+#include <sys/fs/hsfs_isospec.h>
+#include <sys/fs/hsfs_node.h>
+#include <sys/fs/hsfs_susp.h>
+#include <sys/fs/hsfs_rrip.h>
+#include <sys/bootvfs.h>
+#include <sys/filep.h>
+
+#ifdef _BOOT
+#include "../common/util.h"
+#else
+#include <sys/sunddi.h>
+#endif
+
+#define hdbtodb(n) ((ISO_SECTOR_SIZE / DEV_BSIZE) * (n))
+
+#define HSFS_NUM_SIG 14
+
+#define SUSP_SP_IX 0
+#define SUSP_CE_IX 1
+#define SUSP_PD_IX 2
+#define SUSP_ST_IX 3
+#define SUSP_ER_IX 4
+#define RRIP_PX_IX 5
+#define RRIP_PN_IX 6
+#define RRIP_SL_IX 7
+#define RRIP_CL_IX 8
+#define RRIP_PL_IX 9
+#define RRIP_RE_IX 10
+#define RRIP_RF_IX 11
+#define RRIP_RR_IX 12
+#define RRIP_NM_IX 13
+
+#ifdef _BOOT
+#define dprintf if (bootrd_debug) printf
+#else
+#define printf kobj_printf
+#define dprintf if (bootrd_debug) kobj_printf
+
+/* PRINTFLIKE1 */
+extern void kobj_printf(char *, ...);
+#endif
+
+extern int bootrd_debug;
+extern void *bkmem_alloc(size_t);
+extern void bkmem_free(void *, size_t);
+
+struct dirstuff {
+ int loc;
+ fileid_t *filep;
+};
+
+struct hs_direct {
+ struct direct hs_ufs_dir;
+ struct hs_direntry hs_dir;
+};
+
+static uint_t root_ino = 0;
+static struct hs_volume *hsfsp;
+static fileid_t *head;
+
+static char *hsfs_sig_tab[] = {
+ SUSP_SP,
+ SUSP_CE,
+ SUSP_PD,
+ SUSP_ST,
+ SUSP_ER,
+ RRIP_PX,
+ RRIP_PN,
+ RRIP_SL,
+ RRIP_CL,
+ RRIP_PL,
+ RRIP_RE,
+ RRIP_TF,
+ RRIP_RR,
+ RRIP_NM
+};
+
+static int hsfs_num_sig = sizeof (hsfs_sig_tab) / sizeof (hsfs_sig_tab[0]);
+
+/*
+ * Local prototypes
+ */
+static struct hs_direct *readdir(struct dirstuff *);
+static uint_t parse_dir(fileid_t *, int, struct hs_direct *);
+static uint_t parse_susp(char *, uint_t *, struct hs_direct *);
+static ino_t dlook(char *, fileid_t *);
+static int opendir(ino_t, fileid_t *);
+static ino_t find(char *, fileid_t *);
+
+static int bhsfs_mountroot(char *str);
+static int bhsfs_unmountroot(void);
+static int bhsfs_open(char *str, int flags);
+static int bhsfs_close(int fd);
+static void bhsfs_closeall(void);
+static ssize_t bhsfs_read(int fdesc, char *buf, size_t count);
+static off_t bhsfs_lseek(int fdesc, off_t addr, int whence);
+
+static fileid_t *
+find_fp(int fd)
+{
+ fileid_t *filep = head;
+
+ if (fd >= 0) {
+ while ((filep = filep->fi_forw) != head)
+ if (fd == filep->fi_filedes)
+ return (filep->fi_taken ? filep : 0);
+ }
+
+ return (0);
+}
+
+static int
+opendir(ino_t inode, fileid_t *filep)
+{
+ struct hs_direct hsdep;
+
+ dprintf("opendir: inode = %ld\n", inode);
+ /* Set up the IO request */
+ filep->fi_offset = 0;
+ filep->fi_blocknum = hdbtodb(inode);
+ filep->fi_count = ISO_SECTOR_SIZE;
+ filep->fi_memp = 0;
+
+ if (diskread(filep))
+ return (0);
+
+ filep->fi_offset = 0;
+ filep->fi_blocknum = hdbtodb(inode);
+
+ if (inode != root_ino)
+ return (0);
+
+ if (parse_dir(filep, 0, &hsdep) > 0) {
+ struct inode *ip;
+
+ ip = filep->fi_inode;
+ if (ip == NULL)
+ ip = filep->fi_inode = bkmem_alloc(sizeof (*ip));
+
+ ip->i_size = hsdep.hs_dir.ext_size;
+ ip->i_smode = hsdep.hs_dir.mode;
+ ip->i_number = inode;
+ return (0);
+ }
+ return (1);
+}
+
+static ino_t
+find(char *path, fileid_t *filep)
+{
+ char *q;
+ char c;
+ ino_t n;
+
+ dprintf("find: %s\n", path);
+ if (path == NULL || *path == '\0')
+ return (0);
+
+ if (opendir(root_ino, filep))
+ return (0);
+
+ while (*path) {
+ while (*path == '/')
+ path++;
+ q = path;
+ while (*q != '/' && *q != '\0')
+ q++;
+ c = *q;
+ *q = '\0';
+
+ if ((n = dlook(path, filep)) != 0) {
+ if (c == '\0')
+ break;
+ if (opendir(n, filep))
+ return (0);
+ *q = c;
+ path = q;
+ continue;
+ } else {
+ return (0);
+ }
+ }
+ return ((ino_t)n);
+}
+
+static ino_t
+dlook(char *s, fileid_t *filep)
+{
+ struct hs_direct *hsdep;
+ struct direct *udp;
+ struct inode *ip;
+ struct dirstuff dirp;
+ int len;
+
+ dprintf("dlook: %s\n", s);
+ ip = filep->fi_inode;
+ if (s == NULL || *s == '\0')
+ return (0);
+ if ((ip->i_smode & IFMT) != IFDIR) {
+ return (0);
+ }
+ if (ip->i_size == 0) {
+ return (0);
+ }
+ len = strlen(s);
+ dirp.loc = 0;
+ dirp.filep = filep;
+ for (hsdep = readdir(&dirp); hsdep != NULL; hsdep = readdir(&dirp)) {
+ udp = &hsdep->hs_ufs_dir;
+ if (udp->d_namlen == 1 &&
+ udp->d_name[0] == '.' &&
+ udp->d_name[1] == '\0')
+ continue;
+ if (udp->d_namlen == 2 &&
+ udp->d_name[0] == '.' &&
+ udp->d_name[1] == '.' &&
+ udp->d_name[2] == '\0')
+ continue;
+ if (udp->d_namlen == len && (strcmp(s, udp->d_name)) == 0) {
+ struct inode *ip = filep->fi_inode;
+
+ filep->fi_offset = 0;
+ filep->fi_blocknum = hdbtodb(udp->d_ino);
+
+ bzero(filep->fi_inode, sizeof (struct inode));
+ ip->i_size = hsdep->hs_dir.ext_size;
+ ip->i_smode = hsdep->hs_dir.mode;
+ ip->i_number = udp->d_ino;
+ return (udp->d_ino);
+ }
+ }
+ return (0);
+}
+
+/*
+ * get next entry in a directory.
+ */
+static struct hs_direct *
+readdir(struct dirstuff *dirp)
+{
+ static struct hs_direct hsdep;
+ struct direct *udp = &hsdep.hs_ufs_dir;
+ struct inode *ip;
+ fileid_t *filep;
+ daddr_t lbn;
+ int off;
+
+ dprintf("readdir: start\n");
+ filep = dirp->filep;
+ ip = filep->fi_inode;
+ for (;;) {
+ if (dirp->loc >= ip->i_size) {
+ return (NULL);
+ }
+ off = dirp->loc & ((1 << ISO_SECTOR_SHIFT) - 1);
+ if (off == 0) {
+ lbn = hdbtodb(dirp->loc >> ISO_SECTOR_SHIFT);
+ filep->fi_blocknum = lbn + hdbtodb(ip->i_number);
+ filep->fi_count = ISO_SECTOR_SIZE;
+ filep->fi_memp = 0;
+ if (diskread(filep)) {
+ dprintf("readdir: diskread failed\n");
+ return (NULL);
+ }
+ }
+ dirp->loc += parse_dir(filep, off, &hsdep);
+ if (udp->d_reclen == 0 && dirp->loc <= ip->i_size) {
+ dirp->loc = roundup(dirp->loc, ISO_SECTOR_SIZE);
+ continue;
+ }
+ return (&hsdep);
+ }
+}
+
+static int
+getblock(fileid_t *filep)
+{
+ struct inode *ip = filep->fi_inode;
+ int off, size, diff;
+ daddr_t lbn;
+
+ dprintf("getblock: start\n");
+ diff = ip->i_size - filep->fi_offset;
+ if (diff <= 0)
+ return (-1);
+
+ /* which block (or frag) in the file do we read? */
+ lbn = hdbtodb(filep->fi_offset >> ISO_SECTOR_SHIFT);
+ filep->fi_blocknum = lbn + hdbtodb(ip->i_number);
+
+ off = filep->fi_offset & ((1 << ISO_SECTOR_SHIFT) - 1);
+ size = filep->fi_count = ISO_SECTOR_SIZE;
+ filep->fi_memp = 0;
+ if (diskread(filep)) /* Trap errors */
+ return (-1);
+
+ if (filep->fi_offset - off + size >= ip->i_size)
+ filep->fi_count = diff + off;
+ filep->fi_count -= off;
+ filep->fi_memp += off;
+ dprintf("getblock: end\n");
+ return (0);
+}
+
+static ssize_t
+bhsfs_read(int fd, caddr_t buf, size_t count)
+{
+ int i, j;
+ fileid_t *filep;
+ struct inode *ip;
+ caddr_t n;
+
+ dprintf("bhsfs_read %d, count 0x%lx\n", fd, count);
+ filep = find_fp(fd);
+ if (filep == NULL)
+ return (-1);
+
+ ip = filep->fi_inode;
+ n = buf;
+ if (filep->fi_offset + count > ip->i_size)
+ count = ip->i_size - filep->fi_offset;
+
+ if ((i = count) <= 0)
+ return (0);
+
+ while (i > 0) {
+ if (filep->fi_count <= 0) {
+ if (getblock(filep) == -1)
+ return (0);
+ }
+ j = MIN(i, filep->fi_count);
+ bcopy(filep->fi_memp, buf, (uint_t)j);
+ buf += j;
+ filep->fi_memp += j;
+ filep->fi_offset += j;
+ filep->fi_count -= j;
+ i -= j;
+ }
+
+ dprintf("bhsfs_read: read 0x%x\n", (int)(buf - n));
+ return (buf - n);
+}
+
+/*ARGSUSED*/
+static int
+bhsfs_mountroot(char *str)
+{
+ char *bufp;
+
+ if (hsfsp != NULL)
+ return (0); /* already mounted */
+
+ dprintf("mounting ramdisk as hsfs\n");
+
+ hsfsp = bkmem_alloc(sizeof (*hsfsp));
+ bzero(hsfsp, sizeof (*hsfsp));
+ head = bkmem_alloc(sizeof (*head));
+ bzero(head, sizeof (*head));
+ head->fi_back = head->fi_forw = head;
+
+ /* now read the superblock. */
+ head->fi_blocknum = hdbtodb(ISO_VOLDESC_SEC);
+ head->fi_offset = 0;
+ head->fi_count = ISO_SECTOR_SIZE;
+ head->fi_memp = head->fi_buf;
+ if (diskread(head)) {
+ printf("failed to read superblock\n");
+ bhsfs_closeall();
+ return (-1);
+ }
+
+ /* Since RRIP is based on ISO9660, that's where we start */
+ bufp = head->fi_buf;
+ if ((ISO_DESC_TYPE(bufp) != ISO_VD_PVD) ||
+ (strncmp((const char *)ISO_std_id(bufp), ISO_ID_STRING,
+ ISO_ID_STRLEN) != 0) || (ISO_STD_VER(bufp) != ISO_ID_VER)) {
+ dprintf("volume type does not match\n");
+ bhsfs_closeall();
+ return (-1);
+ }
+
+ /* Now we fill in the volume descriptor */
+ hsfsp->vol_size = ISO_VOL_SIZE(bufp);
+ hsfsp->lbn_size = ISO_BLK_SIZE(bufp);
+ hsfsp->lbn_shift = ISO_SECTOR_SHIFT;
+ hsfsp->lbn_secshift = ISO_SECTOR_SHIFT;
+ hsfsp->vol_set_size = (ushort_t)ISO_SET_SIZE(bufp);
+ hsfsp->vol_set_seq = (ushort_t)ISO_SET_SEQ(bufp);
+
+ /* Make sure we have a valid logical block size */
+ if (hsfsp->lbn_size & ~(1 << hsfsp->lbn_shift)) {
+ printf("%d invalid logical block size\n", hsfsp->lbn_size);
+ bhsfs_closeall();
+ return (-1);
+ }
+
+ /* Since an HSFS root could be located anywhere on the media! */
+ root_ino = IDE_EXT_LBN(ISO_root_dir(bufp));
+ return (0);
+}
+
+static int
+bhsfs_unmountroot(void)
+{
+ if (hsfsp == NULL)
+ return (-1);
+
+ bhsfs_closeall();
+
+ return (0);
+}
+
+/*
+ * Open a file.
+ */
+/*ARGSUSED*/
+int
+bhsfs_open(char *str, int flags)
+{
+ static int filedes = 1;
+
+ fileid_t *filep;
+ ino_t ino;
+
+ dprintf("open %s\n", str);
+ filep = (fileid_t *)bkmem_alloc(sizeof (fileid_t));
+ filep->fi_back = head->fi_back;
+ filep->fi_forw = head;
+ head->fi_back->fi_forw = filep;
+ head->fi_back = filep;
+ filep->fi_filedes = filedes++;
+ filep->fi_taken = 1;
+ filep->fi_path = (char *)bkmem_alloc(strlen(str) + 1);
+ (void) strcpy(filep->fi_path, str);
+ filep->fi_inode = NULL;
+ bzero(filep->fi_buf, MAXBSIZE);
+
+ ino = find(str, filep);
+ if (ino == 0) {
+ (void) bhsfs_close(filep->fi_filedes);
+ return (-1);
+ }
+
+ filep->fi_blocknum = hdbtodb(ino);
+ filep->fi_offset = 0;
+ filep->fi_count = 0;
+ filep->fi_memp = 0;
+
+ dprintf("open done\n");
+ return (filep->fi_filedes);
+}
+
+int
+bhsfs_close(int fd)
+{
+ fileid_t *filep;
+
+ dprintf("close %d\n", fd);
+ if (!(filep = find_fp(fd)))
+ return (-1);
+
+ if (filep->fi_taken == 0 || filep == head) {
+ printf("File descripter %d no allocated!\n", fd);
+ return (-1);
+ }
+
+ /* unlink and deallocate node */
+ filep->fi_forw->fi_back = filep->fi_back;
+ filep->fi_back->fi_forw = filep->fi_forw;
+ if (filep->fi_inode)
+ bkmem_free(filep->fi_inode, sizeof (struct inode));
+ bkmem_free(filep->fi_path, strlen(filep->fi_path) + 1);
+ bkmem_free((char *)filep, sizeof (fileid_t));
+ dprintf("close done\n");
+ return (0);
+}
+
+static void
+bhsfs_closeall(void)
+{
+ fileid_t *filep;
+
+ while ((filep = head->fi_forw) != head)
+ if (filep->fi_taken && bhsfs_close(filep->fi_filedes))
+ printf("Filesystem may be inconsistent.\n");
+
+ bkmem_free(hsfsp, sizeof (*hsfsp));
+ bkmem_free(head, sizeof (fileid_t));
+ hsfsp = NULL;
+ head = NULL;
+}
+
+/*
+ * This version of seek() only performs absolute seeks (whence == 0).
+ */
+static off_t
+bhsfs_lseek(int fd, off_t addr, int whence)
+{
+ fileid_t *filep;
+
+ dprintf("lseek %d, off = %lx\n", fd, addr);
+ if (!(filep = find_fp(fd)))
+ return (-1);
+
+ switch (whence) {
+ case SEEK_CUR:
+ filep->fi_offset += addr;
+ break;
+ case SEEK_SET:
+ filep->fi_offset = addr;
+ break;
+ default:
+ case SEEK_END:
+ printf("lseek(): invalid whence value %d\n", whence);
+ break;
+ }
+
+ filep->fi_blocknum = addr / DEV_BSIZE;
+ filep->fi_count = 0;
+ return (0);
+}
+
+/*
+ * Parse a directory entry.
+ *
+ */
+static uint_t
+parse_dir(fileid_t *filep, int offset, struct hs_direct *hsdep)
+{
+ char *bufp = (char *)(filep->fi_memp + offset);
+ struct direct *udp = &hsdep->hs_ufs_dir; /* ufs-style dir info */
+ struct hs_direntry *hdp = &hsdep->hs_dir; /* hsfs-style dir info */
+ uint_t ce_lbn;
+ uint_t ce_len;
+ uint_t nmlen;
+ uint_t i;
+ uchar_t c;
+
+ dprintf("parse_dir: offset = %d\n", offset);
+ /* a zero length dir entry terminates the dir block */
+ udp->d_reclen = IDE_DIR_LEN(bufp);
+ if (udp->d_reclen == 0)
+ return (0);
+
+ /* fill in some basic hsfs info */
+ hdp->ext_lbn = IDE_EXT_LBN(bufp);
+ hdp->ext_size = IDE_EXT_SIZE(bufp);
+ hdp->xar_len = IDE_XAR_LEN(bufp);
+ hdp->intlf_sz = IDE_INTRLV_SIZE(bufp);
+ hdp->intlf_sk = IDE_INTRLV_SKIP(bufp);
+ hdp->sym_link = NULL;
+
+ /* we use lbn of data extent as an inode # equivalent */
+ udp->d_ino = hdp->ext_lbn;
+
+ c = IDE_FLAGS(bufp);
+ if (IDE_REGULAR_FILE(c)) {
+ hdp->type = VREG;
+ hdp->mode = IFREG;
+ hdp->nlink = 1;
+ } else if (IDE_REGULAR_DIR(c)) {
+ hdp->type = VDIR;
+ hdp->mode = IFDIR;
+ hdp->nlink = 2;
+ } else {
+ printf("pd(): file type=0x%x unknown.\n", c);
+ }
+
+ /*
+ * Massage hsfs name, recognizing special entries for . and ..
+ * else lopping off version junk.
+ */
+
+ /* Some initial conditions */
+ nmlen = IDE_NAME_LEN(bufp);
+ c = *IDE_NAME(bufp);
+ /* Special Case: Current Directory */
+ if (nmlen == 1 && c == '\0') {
+ udp->d_name[0] = '.';
+ udp->d_name[1] = '\0';
+ udp->d_namlen = 1;
+ /* Special Case: Parent Directory */
+ } else if (nmlen == 1 && c == '\001') {
+ udp->d_name[0] = '.';
+ udp->d_name[1] = '.';
+ udp->d_name[2] = '\0';
+ udp->d_namlen = 2;
+ /* Other file name */
+ } else {
+ udp->d_namlen = 0;
+ for (i = 0; i < nmlen; i++) {
+ c = *(IDE_name(bufp)+i);
+ if (c == ';')
+ break;
+ else if (c == ' ')
+ continue;
+ else
+ udp->d_name[udp->d_namlen++] = c;
+ }
+ udp->d_name[udp->d_namlen] = '\0';
+ }
+
+ /* System Use Fields */
+ ce_len = IDE_SUA_LEN(bufp);
+
+ if (ce_len <= 0)
+ return (udp->d_reclen);
+
+ /* there is an SUA for this dir entry; go parse it */
+ ce_lbn = parse_susp((char *)IDE_sys_use_area(bufp), &ce_len, hsdep);
+
+ if (ce_lbn) {
+ /*
+ * store away current position in dir,
+ * as we will be using the iobuf to reading SUA.
+ */
+ daddr_t save_bn = filep->fi_blocknum;
+ daddr_t save_offset = filep->fi_offset;
+ caddr_t save_ma = filep->fi_memp;
+ int save_cc = filep->fi_count;
+ do {
+ filep->fi_count = ISO_SECTOR_SIZE;
+ filep->fi_offset = 0;
+ filep->fi_blocknum = hdbtodb(ce_lbn);
+ filep->fi_memp = 0;
+ if (diskread(filep)) {
+ printf("failed to read cont. area\n");
+ ce_len = 0;
+ ce_lbn = 0;
+ break;
+ }
+ ce_lbn = parse_susp(filep->fi_memp, &ce_len,
+ hsdep);
+ } while (ce_lbn);
+ filep->fi_count = save_cc;
+ filep->fi_offset = save_offset;
+ filep->fi_blocknum = save_bn;
+ filep->fi_memp = save_ma;
+ }
+ return (udp->d_reclen);
+}
+
+/*
+ * Parse the System Use Fields in this System Use Area.
+ * Return blk number of continuation/SUA, or 0 if no continuation/not a SUA.
+ */
+static uint_t
+parse_susp(char *bufp, uint_t *len, struct hs_direct *hsdep)
+{
+ struct direct *udp = &hsdep->hs_ufs_dir; /* ufs-style info */
+ char *susp;
+ uint_t cur_off = 0;
+ uint_t blk_len = *len;
+ uint_t susp_len = 0;
+ uint_t ce_lbn = 0;
+ uint_t i;
+
+ dprintf("parse_susp: len = %d\n", *len);
+ while (cur_off < blk_len) {
+ susp = (char *)(bufp + cur_off);
+
+ /*
+ * A null entry, or an entry with zero length
+ * terminates the SUSP.
+ */
+ if (susp[0] == '\0' || susp[1] == '\0' ||
+ (susp_len = SUF_LEN(susp)) == 0)
+ break;
+
+ /*
+ * Compare current entry to all known signatures.
+ */
+ for (i = 0; i < hsfs_num_sig; i++)
+ if (strncmp(hsfs_sig_tab[i], susp, SUF_SIG_LEN) == 0)
+ break;
+ switch (i) {
+ case SUSP_CE_IX:
+ /*
+ * CE signature: continuation of SUSP.
+ * will want to return new lbn, len.
+ */
+ ce_lbn = CE_BLK_LOC(susp);
+ *len = CE_CONT_LEN(susp);
+ break;
+ case RRIP_NM_IX:
+ /* NM signature: POSIX-style file name */
+ if (!RRIP_NAME_FLAGS(susp)) {
+ udp->d_namlen = RRIP_NAME_LEN(susp);
+ bcopy((char *)RRIP_name(susp),
+ udp->d_name, udp->d_namlen);
+ udp->d_name[udp->d_namlen] = '\0';
+ }
+ break;
+ case HSFS_NUM_SIG:
+ /* couldn't find a legit susp, terminate loop */
+ case SUSP_ST_IX:
+ /* ST signature: terminates SUSP */
+ return (ce_lbn);
+ case SUSP_SP_IX:
+ case RRIP_RR_IX:
+ default:
+ break;
+ }
+ cur_off += susp_len;
+ }
+ return (ce_lbn);
+}
+
+struct boot_fs_ops bhsfs_ops = {
+ "boot_hsfs",
+ bhsfs_mountroot,
+ bhsfs_unmountroot,
+ bhsfs_open,
+ bhsfs_close,
+ bhsfs_read,
+ bhsfs_lseek,
+ NULL
+};
diff --git a/usr/src/common/fs/pcfilep.h b/usr/src/common/fs/pcfilep.h
new file mode 100644
index 0000000000..ba1024ccb7
--- /dev/null
+++ b/usr/src/common/fs/pcfilep.h
@@ -0,0 +1,231 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _PCFILEP_H
+#define _PCFILEP_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MAX_DOSMOUNT_RETRIES 3
+
+#define TICKS_PER_SEC 18 /* It's really 18.2! */
+#define SECSIZ 512
+#define fat_bpc(i) (pi[(i)]->f_bpb.bs_spc * SECSIZ)
+
+/*
+ * Access permissions for dosAccess(), dosOpen()
+ * NOTE: These permission need to match those for the DOS compiler.
+ */
+#define FILE_EXISTS 1
+#define FILE_READ 0x0000
+#define FILE_WRITE 0x0001
+#define FILE_RDWR 0x0002
+#define FILE_APPEND 0x0008
+#define FILE_CREATE 0x0100
+#define FILE_TRUNC 0x0200
+
+#define TYPE_EMPTY 0x00 /* undefined partition */
+#define TYPE_DOS 0x13 /* causes fatInit() to search for */
+ /* active partition */
+#define TYPE_DOS_12 0x01 /* partition with FAT12 filesys */
+#define TYPE_DOS_16 0x04 /* partition with FAT16 filesys */
+#define TYPE_DOS_EXT 0x05 /* not bootable, ignore */
+#define TYPE_HUGH 0x06 /* HUGH partition */
+#define TYPE_COMPAQ 0x12 /* Compaq's diag partition */
+#define TYPE_SOLARIS 0x82
+#define TYPE_SOLARIS_BOOT 0xBE /* For "boot hill" project */
+
+#define FDISK_START 0x1be /* location in first sector where */
+ /* the fdisk starts. */
+
+#define FDISK_PARTS 4 /* Number of partitions in a fdisk */
+#define FDISK_ACTIVE 0x80 /* indicates partition is active */
+#define FDISK_INACTIVE 0x00 /* " partition inactive */
+
+#pragma pack(1)
+struct _fdisk_partition_ {
+ uchar_t fd_active;
+ uchar_t fd_b_head;
+ uchar_t fd_b_sec;
+ uchar_t fd_b_cyl;
+ uchar_t fd_type;
+ uchar_t fd_e_head;
+ uchar_t fd_e_sec;
+ uchar_t fd_e_cyl;
+ union {
+ long fd_start_sec_long;
+ struct {
+ ushort_t low;
+ ushort_t high;
+ } s;
+ } u;
+ long fd_part_len;
+};
+#define fd_start_sec u.fd_start_sec_long
+#define fd_partition fd_type
+typedef struct _fdisk_partition_ _fdisk_t, *_fdisk_p;
+#pragma pack()
+
+#pragma pack(1)
+struct _boot_sector_ {
+ uchar_t bs_jump_code[3];
+ uchar_t bs_oem_name[8];
+ uchar_t bs_bytes_sector[2];
+ uchar_t bs_spc; /* ... sectors per cluster */
+ uchar_t bs_resv_sectors[2];
+ uchar_t bs_num_fats;
+ uchar_t bs_num_root_entries[2];
+ uchar_t bs_siv[2]; /* ... sectors in volume */
+ uchar_t bs_media;
+ uchar_t bs_spf[2]; /* ... sectors per fat */
+ uchar_t bs_sectors_per_track[2];
+ uchar_t bs_heads[2];
+ /*
+ * Byte offset at this point is 28 so we can declare the next
+ * variable with the correct type and not worry about alignment.
+ */
+ long bs_hidden_sectors;
+ long bs_lsiv; /* ... logical sectors in volume */
+ uchar_t bs_phys_drive_num;
+ uchar_t bs_reserved;
+ uchar_t bs_ext_signature;
+ char bs_volume_id[4];
+ char bs_volume_label[11];
+ char bs_type[8];
+
+ /* ---- ADDED BY SUNSOFT FOR MDBOOT ---- */
+ ushort_t bs_offset_high;
+ ushort_t bs_offset_low;
+};
+#pragma pack()
+typedef struct _boot_sector_ _boot_sector_t, *_boot_sector_p;
+
+/*
+ * Cluster types
+ */
+#define CLUSTER_AVAIL 0x00
+#define CLUSTER_RES_12_0 0x0ff0 /* 12bit fat, first reserved */
+#define CLUSTER_RES_12_6 0x0ff6 /* 12bit fat, last reserved */
+#define CLUSTER_RES_16_0 0xfff0 /* 16bit fat, first reserved */
+#define CLUSTER_RES_16_6 0xfff6 /* 16bit fat, last reserved */
+#define CLUSTER_BAD_12 0x0ff7 /* 12bit fat, bad entry */
+#define CLUSTER_BAD_16 0xfff7 /* 16bit fat, bad entry */
+#define CLUSTER_EOF CLUSTER_EOF_16_0
+#define CLUSTER_MAX_12 0x0ff7 /* max clusters for 12bit fat */
+#define CLUSTER_EOF_12_0 0x0ff8 /* 12bit fat, EOF first entry */
+#define CLUSTER_EOF_12_8 0x0fff /* 12bit fat, EOF last entry */
+#define CLUSTER_EOF_16_0 0xfff8 /* 16bit fat, EOF first entry */
+#define CLUSTER_EOF_16_8 0xffff /* 16bit fat, EOF last entry */
+
+/*
+ * Cluster operations for allocation
+ */
+#define CLUSTER_NOOP 0x0001 /* ... just allocate cluster */
+#define CLUSTER_ZEROFILL 0x0002 /* ... zero fill the alloc'd cluster */
+
+#define CLUSTER_FIRST 0x0002 /* ... first cluster number to search */
+#define CLUSTER_ROOTDIR 0x0000 /* ... root dir's cluster number */
+
+/*
+ * This structure is filled in by initFAT()
+ */
+struct _fat_controller_ {
+ union {
+ _boot_sector_t fu_bpb; /* boot parameter block */
+ uchar_t fu_sector[SECSIZ];
+ } fu;
+ long f_adjust; /* starting sec for part. */
+ long f_rootsec; /* root dir starting sec. */
+ long f_rootlen; /* length of root in sectors */
+ long f_filesec; /* adjustment for clusters */
+ long f_dclust; /* cur dir cluster */
+ int f_nxtfree; /* next free cluster */
+ int f_ncluster; /* number of cluster in part */
+ char f_16bit:1, /* 1 if 16bit fat entries */
+ f_flush:1; /* flush the fat */
+};
+typedef struct _fat_controller_ _fat_controller_t, *_fat_controller_p;
+
+#define f_bpb fu.fu_bpb
+#define f_sector fu.fu_sector
+
+#define NAMESIZ 8
+#define EXTSIZ 3
+#pragma pack(1)
+struct _dir_entry_ {
+ char d_name[NAMESIZ];
+ char d_ext[EXTSIZ];
+ uchar_t d_attr;
+ char d_res[10];
+ short d_time;
+ short d_date;
+ ushort_t d_cluster;
+ long d_size;
+};
+#pragma pack()
+typedef struct _dir_entry_ _dir_entry_t, *_dir_entry_p;
+
+/*
+ * Number of entries in one sector
+ */
+#define DIRENTS (SECSIZ / sizeof (_dir_entry_t))
+
+/*
+ * Directory entry attributes
+ */
+#define DE_READONLY 0x01
+#define DE_HIDDEN 0x02
+#define DE_SYSTEM 0x04
+#define DE_LABEL 0x08
+#define DE_DIRECTORY 0x10
+#define DE_ARCHIVE 0x20
+#define DE_RESERVED1 0x40
+#define DE_RESERVED2 0x80
+
+#define DE_IS_LFN (DE_READONLY | DE_HIDDEN | DE_SYSTEM | DE_LABEL)
+
+struct _file_descriptor_ {
+ struct _file_descriptor_ *f_forw; /* link to next file descriptor */
+ int f_desc; /* descriptor number */
+ long f_startclust; /* starting cluster number */
+ long f_off; /* current offset */
+ long f_len; /* size of file */
+ long f_index; /* index into directory block */
+ uchar_t f_attr; /* attributes */
+ int f_volidx; /* Volume device index */
+ char *f_volname; /* Name of volume */
+};
+typedef struct _file_descriptor_ _file_desc_t, *_file_desc_p;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PCFILEP_H */
diff --git a/usr/src/common/fs/pcfs.c b/usr/src/common/fs/pcfs.c
new file mode 100644
index 0000000000..63c1ba2963
--- /dev/null
+++ b/usr/src/common/fs/pcfs.c
@@ -0,0 +1,623 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Basic file system reading code for standalone I/O system.
+ * Simulates a primitive UNIX I/O system (read(), write(), open(), etc).
+ * Does not support writes.
+ */
+
+/*
+ * WARNING:
+ * This is currently used by installgrub for creating bootable floppy.
+ * The special part is diskread_callback/fileread_callback for gathering
+ * fileblock list.
+ */
+
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <sys/vnode.h>
+#include <sys/fs/pc_label.h>
+#include <sys/bootvfs.h>
+#include <sys/filep.h>
+#include "pcfilep.h"
+
+#if defined(_BOOT)
+#include "../common/util.h"
+#elif defined(_KERNEL)
+#include <sys/sunddi.h>
+#else
+#include <stdio.h>
+#include <strings.h>
+#include <ctype.h>
+#endif
+
+#if defined(_BOOT)
+#define dprintf if (bootrd_debug) printf
+#elif defined(_KERNEL)
+#define printf kobj_printf
+#define dprintf if (bootrd_debug) kobj_printf
+
+/* PRINTLIKE */
+extern void kobj_printf(char *, ...);
+#else
+#define dprintf if (bootrd_debug) printf
+#endif
+
+#define FI_STARTCLUST(fp) (*(ushort_t *)(fp)->fi_buf)
+#define FI_LENGTH(fp) (*(long *)((fp)->fi_buf + 4))
+
+extern int bootrd_debug;
+extern void *bkmem_alloc(size_t);
+extern void bkmem_free(void *, size_t);
+
+/*
+ * NOTE: The fileread_callback is set by the calling program
+ * during a file read. diskread_callback is set to fileread_callback
+ * only if reading a file block. It needs to be NULL while reading
+ * cluster blocks.
+ */
+extern int (*diskread_callback)(int, int);
+extern int (*fileread_callback)(int, int);
+
+/*
+ * Local prototypes
+ */
+static int lookuppn(char *, _dir_entry_p);
+static fileid_t *find_fp(int);
+static void *readblock(int, int);
+static int fat_map(int, int);
+static int cluster_valid(long, int);
+static int fat_ctodb(int, int);
+
+static int bpcfs_mountroot(char *str);
+static int bpcfs_unmountroot(void);
+static int bpcfs_open(char *str, int flags);
+static int bpcfs_close(int fd);
+static void bpcfs_closeall(void);
+static ssize_t bpcfs_read(int fdesc, char *buf, size_t count);
+static off_t bpcfs_lseek(int fdesc, off_t addr, int whence);
+
+static fileid_t *head;
+static _fat_controller_p pcfsp;
+
+/* cache the cluster */
+static int nsec_cache;
+static int nsec_start;
+static char *cluster_cache;
+
+/*ARGSUSED*/
+static int
+bpcfs_mountroot(char *str)
+{
+ int ncluster;
+ if (pcfsp != NULL)
+ return (0); /* already mounted */
+
+ pcfsp = bkmem_alloc(sizeof (_fat_controller_t));
+ head = (fileid_t *)bkmem_alloc(sizeof (fileid_t));
+ head->fi_back = head->fi_forw = head;
+ head->fi_filedes = 0;
+ head->fi_taken = 0;
+
+ /* read of first floppy sector */
+ head->fi_blocknum = 0;
+ head->fi_count = SECSIZ;
+ head->fi_memp = (caddr_t)pcfsp->f_sector;
+ if (diskread(head)) {
+ printf("failed to read first sector\n");
+ bkmem_free(pcfsp, sizeof (*pcfsp));
+ pcfsp = NULL;
+ return (-1);
+ }
+
+ if (pcfsp->f_bpb.bs_spc == 0) {
+ printf("invalid bios paramet block\n");
+ return (-1);
+ }
+
+ pcfsp->f_rootsec =
+ (pcfsp->f_bpb.bs_num_fats * ltohs(pcfsp->f_bpb.bs_spf)) +
+ ltohs(pcfsp->f_bpb.bs_resv_sectors);
+ pcfsp->f_rootlen =
+ ltohs(pcfsp->f_bpb.bs_num_root_entries) *
+ sizeof (_dir_entry_t) / SECSIZ;
+ pcfsp->f_adjust = 0;
+ pcfsp->f_dclust = CLUSTER_ROOTDIR;
+ pcfsp->f_filesec = pcfsp->f_rootsec + pcfsp->f_rootlen;
+ pcfsp->f_nxtfree = CLUSTER_FIRST;
+
+ /* figure out the number of clusters in this partition */
+ ncluster = (((ulong_t)ltohs(pcfsp->f_bpb.bs_siv) ?
+ (ulong_t)ltohs(pcfsp->f_bpb.bs_siv) :
+ (ulong_t)ltohi(pcfsp->f_bpb.bs_siv)) -
+ pcfsp->f_filesec) / (ulong_t)pcfsp->f_bpb.bs_spc;
+ pcfsp->f_16bit = ncluster >= CLUSTER_MAX_12;
+ pcfsp->f_ncluster = ncluster;
+
+ /* cache the cluster */
+ if (pcfsp->f_16bit)
+ nsec_cache = (((ncluster << 1) + 511) >> 9);
+ else
+ nsec_cache = (ncluster + ((ncluster + 1) >> 1) + 511) >> 9;
+ cluster_cache = bkmem_alloc(nsec_cache * SECSIZ);
+ if (cluster_cache == NULL) {
+ printf("bpcfs_mountroot: out of memory\n");
+ bkmem_free(pcfsp, sizeof (*pcfsp));
+ pcfsp = NULL;
+ return (-1);
+ }
+
+ head->fi_blocknum = nsec_start =
+ ltohs(pcfsp->f_bpb.bs_resv_sectors) + pcfsp->f_adjust;
+ head->fi_count = nsec_cache * SECSIZ;
+ head->fi_memp = cluster_cache;
+ if (diskread(head)) {
+ printf("bpcfs_mountroot: failed to read cluster\n");
+ bkmem_free(pcfsp, sizeof (*pcfsp));
+ pcfsp = NULL;
+ return (-1);
+ }
+ dprintf("read cluster sectors %d starting at %d\n",
+ nsec_cache, nsec_start);
+ return (0);
+}
+
+static int
+bpcfs_unmountroot(void)
+{
+ if (pcfsp == NULL)
+ return (-1);
+
+ (void) bpcfs_closeall();
+
+ return (0);
+}
+
+/*
+ * Open a file.
+ */
+/*ARGSUSED*/
+int
+bpcfs_open(char *str, int flags)
+{
+ static int filedes = 1;
+
+ fileid_t *filep;
+ _dir_entry_t d;
+
+ dprintf("open %s\n", str);
+ filep = (fileid_t *)bkmem_alloc(sizeof (fileid_t));
+ filep->fi_back = head->fi_back;
+ filep->fi_forw = head;
+ head->fi_back->fi_forw = filep;
+ head->fi_back = filep;
+ filep->fi_filedes = filedes++;
+ filep->fi_taken = 1;
+ filep->fi_path = (char *)bkmem_alloc(strlen(str) + 1);
+ (void) strcpy(filep->fi_path, str);
+
+ if (lookuppn(str, &d)) {
+ (void) bpcfs_close(filep->fi_filedes);
+ return (-1);
+ }
+
+ filep->fi_offset = 0;
+ FI_STARTCLUST(filep) = d.d_cluster;
+ FI_LENGTH(filep) = d.d_size;
+ dprintf("file %s size = %ld\n", str, d.d_size);
+ return (filep->fi_filedes);
+}
+
+int
+bpcfs_close(int fd)
+{
+ fileid_t *filep;
+
+ dprintf("close %d\n", fd);
+ if (!(filep = find_fp(fd)))
+ return (-1);
+
+ if (filep->fi_taken == 0 || filep == head) {
+ printf("File descripter %d no allocated!\n", fd);
+ return (-1);
+ }
+
+ /* unlink and deallocate node */
+ filep->fi_forw->fi_back = filep->fi_back;
+ filep->fi_back->fi_forw = filep->fi_forw;
+ bkmem_free(filep->fi_path, strlen(filep->fi_path) + 1);
+ bkmem_free((char *)filep, sizeof (fileid_t));
+ dprintf("close done\n");
+ return (0);
+}
+
+static void
+bpcfs_closeall(void)
+{
+ fileid_t *filep;
+
+ while ((filep = head->fi_forw) != head)
+ if (filep->fi_taken && bpcfs_close(filep->fi_filedes))
+ printf("Filesystem may be inconsistent.\n");
+
+ bkmem_free(pcfsp, sizeof (*pcfsp));
+ bkmem_free(head, sizeof (fileid_t));
+ pcfsp = NULL;
+ head = NULL;
+}
+
+static ssize_t
+bpcfs_read(int fd, caddr_t b, size_t c)
+{
+ ulong_t sector;
+ uint_t count = 0, xfer, i;
+ char *block;
+ ulong_t off, blk;
+ int rd, spc;
+ fileid_t *fp;
+
+ dprintf("bpcfs_read: fd = %d, buf = %p, size = %d\n",
+ fd, (void *)b, c);
+ fp = find_fp(fd);
+ if (fp == NULL) {
+ printf("invalid file descriptor %d\n", fd);
+ return (-1);
+ }
+
+ spc = pcfsp->f_bpb.bs_spc;
+ off = fp->fi_offset;
+ blk = FI_STARTCLUST(fp);
+ rd = blk == CLUSTER_ROOTDIR ? 1 : 0;
+
+ spc = pcfsp->f_bpb.bs_spc;
+ off = fp->fi_offset;
+ blk = FI_STARTCLUST(fp);
+ rd = (blk == CLUSTER_ROOTDIR) ? 1 : 0;
+
+ if ((c = MIN(FI_LENGTH(fp) - off, c)) == 0)
+ return (0);
+
+ while (off >= pcfsp->f_bpb.bs_spc * SECSIZ) {
+ blk = fat_map(blk, rd);
+ off -= pcfsp->f_bpb.bs_spc * SECSIZ;
+
+ if (!cluster_valid(blk, rd)) {
+ printf("bpcfs_read: invalid cluster: %ld, %d\n",
+ blk, rd);
+ return (-1);
+ }
+ }
+
+ while (count < c) {
+ sector = fat_ctodb(blk, rd);
+ diskread_callback = fileread_callback;
+ for (i = ((off / SECSIZ) % pcfsp->f_bpb.bs_spc); i < spc; i++) {
+ xfer = MIN(SECSIZ - (off % SECSIZ), c - count);
+ if (xfer == 0)
+ break; /* last sector done */
+
+ block = (char *)readblock(sector + i, 1);
+ if (block == NULL) {
+ return (-1);
+ }
+ dprintf("bpcfs_read: read %d bytes\n", xfer);
+ if (diskread_callback == NULL)
+ (void) bcopy(&block[off % SECSIZ], b, xfer);
+ count += xfer;
+ off += xfer;
+ b += xfer;
+ }
+
+ diskread_callback = NULL;
+ if (count < c) {
+ blk = fat_map(blk, rd);
+ if (!cluster_valid(blk, rd)) {
+ printf("bpcfs_read: invalid cluster: %ld, %d\n",
+ blk, rd);
+ break;
+ }
+ }
+ }
+
+ fp->fi_offset += count;
+ return (count);
+}
+
+/*
+ * This version of seek() only performs absolute seeks (whence == 0).
+ */
+static off_t
+bpcfs_lseek(int fd, off_t addr, int whence)
+{
+ fileid_t *filep;
+
+ dprintf("lseek %d, off = %lx\n", fd, addr);
+ if (!(filep = find_fp(fd)))
+ return (-1);
+
+ switch (whence) {
+ case SEEK_CUR:
+ filep->fi_offset += addr;
+ break;
+ case SEEK_SET:
+ filep->fi_offset = addr;
+ break;
+ default:
+ case SEEK_END:
+ printf("lseek(): invalid whence value %d\n", whence);
+ break;
+ }
+
+ filep->fi_blocknum = addr / DEV_BSIZE;
+ filep->fi_count = 0;
+ return (0);
+}
+
+static fileid_t *
+find_fp(int fd)
+{
+ fileid_t *filep = head;
+
+ if (fd >= 0) {
+ while ((filep = filep->fi_forw) != head)
+ if (fd == filep->fi_filedes)
+ return (filep->fi_taken ? filep : 0);
+ }
+
+ return (0);
+}
+
+static int
+cluster_valid(long c, int rd)
+{
+ return ((rd && (c == 0)) ? 1 : (c >= CLUSTER_RES_16_0 ? 0 : c));
+}
+
+static int
+fat_ctodb(int blk, int r)
+{
+ uint_t s;
+
+ s = r ? blk + pcfsp->f_rootsec + pcfsp->f_adjust :
+ ((blk - 2) * pcfsp->f_bpb.bs_spc) +
+ pcfsp->f_filesec + pcfsp->f_adjust;
+
+ return (s);
+}
+
+static int
+fat_map(int blk, int rootdir)
+{
+ ulong_t sectn, fat_index;
+ uchar_t *fp;
+
+ if (rootdir) {
+ return (blk > pcfsp->f_rootlen ? CLUSTER_EOF : blk + 1);
+ }
+
+ /* ---- Find out what sector this cluster is in ---- */
+ fat_index = (pcfsp->f_16bit) ? ((ulong_t)blk << 1) :
+ ((ulong_t)blk + ((uint_t)blk >> 1));
+
+ sectn = (fat_index / SECSIZ) + ltohs(pcfsp->f_bpb.bs_resv_sectors)
+ + pcfsp->f_adjust;
+
+ /*
+ * Read two sectors so that if our fat_index points at the last byte
+ * byte we'll have the data needed. This is only a problem for fat12
+ * entries.
+ */
+ if (!(fp = (uchar_t *)readblock(sectn, 2))) {
+ printf("fat_map: bad cluster\n");
+ return (CLUSTER_BAD_16);
+ }
+
+ fp += (fat_index % SECSIZ);
+
+ if (pcfsp->f_16bit)
+ blk = fp[0] | (fp[1] << 8);
+ else {
+ if (blk & 1)
+ blk = ((fp[0] >> 4) & 0xf) | (fp[1] << 4);
+ else
+ blk = ((fp[1] & 0xf) << 8) | fp[0];
+
+ /*
+ * This makes compares easier because we can just compare
+ * against one value instead of two.
+ */
+ if (blk >= CLUSTER_RES_12_0)
+ blk |= CLUSTER_RES_16_0;
+ }
+ return (blk);
+}
+
+static int
+namecmp(char *pn, char *dn, int cs)
+{
+ dprintf("namecmp %s, %s, len = %d\n", pn, dn, cs);
+
+ /* starting char must match */
+ while (*pn && *dn) {
+ --cs;
+ if (toupper(*pn++) != toupper(*dn++))
+ return (1);
+ }
+
+ dprintf("namecmp: cs = %d\n", cs);
+ /* remainder should be either ~# or all spaces */
+ if (cs > 0 && *dn == '~')
+ return (0);
+ while (cs > 0) {
+ if (*dn++ != ' ')
+ return (1);
+ --cs;
+ }
+ return (0);
+}
+
+static int
+dircmp(char *name, char *d_name, char *d_ext)
+{
+ int ret;
+ char *sep, *ext;
+
+ sep = (char *)strchr(name, '.');
+
+ if (sep) {
+ *sep = '\0';
+ ext = sep + 1;
+ } else
+ ext = " ";
+
+ if (namecmp(name, d_name, NAMESIZ) || namecmp(ext, d_ext, EXTSIZ))
+ ret = 1;
+ else
+ ret = 0;
+ if (sep)
+ *sep = '.';
+ return (ret);
+}
+
+static int
+lookup(char *n, _dir_entry_p dp, ulong_t dir_blk)
+{
+ int spc = pcfsp->f_bpb.bs_spc;
+ int rd = (dir_blk == CLUSTER_ROOTDIR ? 1 : 0);
+ _dir_entry_p dxp;
+ int j, sector;
+
+ dprintf("lookup: name = %s\n", n);
+
+ while (cluster_valid(dir_blk, rd)) {
+ sector = fat_ctodb(dir_blk, rd);
+ dxp = readblock(sector, 1); /* read one sector */
+ if (dxp == NULL)
+ return (0);
+ for (j = 0; j < DIRENTS * spc; j++, dxp++) {
+ dprintf("lookup: dir entry %s.%s;\n",
+ dxp->d_name, dxp->d_ext);
+ if (dxp->d_name[0] == 0)
+ return (0);
+ if ((uchar_t)dxp->d_name[0] != 0xE5 &&
+ (dxp->d_attr & (DE_LABEL|DE_HIDDEN)) == 0 &&
+ dircmp(n, dxp->d_name, dxp->d_ext) == 0) {
+ dprintf("lookup: match found\n");
+ (void) bcopy(dxp, dp, sizeof (*dp));
+ return (1);
+ }
+ }
+ /* next cluster */
+ dir_blk = fat_map(dir_blk, rd);
+ }
+
+ return (0);
+}
+
+static int
+lookuppn(char *n, _dir_entry_p dp)
+{
+ long dir_blk;
+ char name[8 + 1 + 3 + 1]; /* <8>.<3>'\0' */
+ char *p, *ep;
+ _dir_entry_t dd;
+
+ dprintf("lookuppn: path = %s\n", n);
+ dir_blk = pcfsp->f_dclust;
+ if ((*n == '\\') || (*n == '/')) {
+ dir_blk = CLUSTER_ROOTDIR;
+ while ((*n == '\\') || (*n == '/'))
+ n++;
+ if (*n == '\0') {
+ (void) bzero(dp, sizeof (*dp));
+ dp->d_cluster = CLUSTER_ROOTDIR;
+ dp->d_attr = DE_DIRECTORY;
+ return (0);
+ }
+ }
+
+ ep = &name[0] + sizeof (name);
+ while (*n) {
+ (void) bzero(name, sizeof (name));
+ p = &name[0];
+ while (*n && (*n != '\\') && (*n != '/'))
+ if (p != ep)
+ *p++ = *n++;
+ else {
+ dprintf("return, name %s is too long\n", name);
+ return (-1); /* name is too long */
+ }
+ while ((*n == '\\') || (*n == '/'))
+ n++;
+ if (lookup(name, &dd, dir_blk) == 0) {
+ dprintf("return, name %s not found\n", name);
+ return (-1);
+ }
+ dprintf("dd = %x:%x:%x attr = %x\n",
+ *(int *)&dd, *(((int *)&dd) + 1),
+ *(((int *)&dd) + 2), dd.d_attr);
+ if (*n && ((dd.d_attr & DE_DIRECTORY) == 0)) {
+ dprintf("return, not a directory\n");
+ return (-1);
+ }
+
+ dir_blk = dd.d_cluster;
+ }
+ (void) bcopy(&dd, dp, sizeof (dd));
+ return (0);
+}
+
+static void *
+readblock(int sector, int nsec)
+{
+ if (sector >= nsec_start && sector + nsec <= nsec_start + nsec_cache)
+ return (cluster_cache + (sector - nsec_start) * SECSIZ);
+
+ /* read disk sectors */
+ head->fi_blocknum = sector;
+ head->fi_count = nsec * SECSIZ;
+ head->fi_memp = head->fi_buf;
+ if (diskread(head)) {
+ printf("failed to %d sectors at %d\n", nsec, sector);
+ return (NULL);
+ }
+
+ return (head->fi_buf);
+}
+
+struct boot_fs_ops bpcfs_ops = {
+ "boot_pcfs",
+ bpcfs_mountroot,
+ bpcfs_unmountroot,
+ bpcfs_open,
+ bpcfs_close,
+ bpcfs_read,
+ bpcfs_lseek,
+ NULL
+};
diff --git a/usr/src/common/fs/ufsops.c b/usr/src/common/fs/ufsops.c
new file mode 100644
index 0000000000..1d7136a4a2
--- /dev/null
+++ b/usr/src/common/fs/ufsops.c
@@ -0,0 +1,737 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/vnode.h>
+#include <sys/fs/ufs_fsdir.h>
+#include <sys/fs/ufs_fs.h>
+#include <sys/fs/ufs_inode.h>
+#include <sys/sysmacros.h>
+#include <sys/bootvfs.h>
+#include <sys/filep.h>
+
+#ifdef _BOOT
+#include "../common/util.h"
+#else
+#include <sys/sunddi.h>
+#endif
+
+extern void *bkmem_alloc(size_t);
+extern void bkmem_free(void *, size_t);
+
+int bootrd_debug;
+#ifdef _BOOT
+#define dprintf if (bootrd_debug) printf
+#else
+#define printf kobj_printf
+#define dprintf if (bootrd_debug) kobj_printf
+
+/* PRINTLIKE */
+extern void kobj_printf(char *, ...);
+#endif
+
+/*
+ * This fd is used when talking to the device file itself.
+ */
+static fileid_t *head;
+
+/* Only got one of these...ergo, only 1 fs open at once */
+/* static */
+devid_t *ufs_devp;
+
+struct dirinfo {
+ int loc;
+ fileid_t *fi;
+};
+
+static int bufs_close(int);
+static void bufs_closeall(int);
+static ino_t find(fileid_t *filep, char *path);
+static ino_t dlook(fileid_t *filep, char *path);
+static daddr32_t sbmap(fileid_t *filep, daddr32_t bn);
+static struct direct *readdir(struct dirinfo *dstuff);
+static void set_cache(int, void *, uint_t);
+static void *get_cache(int);
+static void free_cache();
+
+/* These are the pools of buffers, etc. */
+#define NBUFS (NIADDR+1)
+/* Compilers like to play with alignment, so force the issue here */
+static union {
+ char *blk[NBUFS];
+ daddr32_t *dummy;
+} b;
+daddr32_t blknos[NBUFS];
+
+/*
+ * There is only 1 open (mounted) device at any given time.
+ * So we can keep a single, global devp file descriptor to
+ * use to index into the di[] array. This is not true for the
+ * fi[] array. We can have more than one file open at once,
+ * so there is no global fd for the fi[].
+ * The user program must save the fd passed back from open()
+ * and use it to do subsequent read()'s.
+ */
+
+static int
+openi(fileid_t *filep, ino_t inode)
+{
+ struct dinode *dp;
+ devid_t *devp = filep->fi_devp;
+
+ filep->fi_inode = get_cache((int)inode);
+ if (filep->fi_inode != 0)
+ return (0);
+
+ filep->fi_offset = 0;
+ filep->fi_blocknum = fsbtodb(&devp->un_fs.di_fs,
+ itod(&devp->un_fs.di_fs, inode));
+
+ /* never more than 1 disk block */
+ filep->fi_count = devp->un_fs.di_fs.fs_bsize;
+ filep->fi_memp = 0; /* cached read */
+ if (diskread(filep) != 0) {
+ return (0);
+ }
+
+ dp = (struct dinode *)filep->fi_memp;
+ filep->fi_inode = (struct inode *)
+ bkmem_alloc(sizeof (struct inode));
+ bzero((char *)filep->fi_inode, sizeof (struct inode));
+ filep->fi_inode->i_ic =
+ dp[itoo(&devp->un_fs.di_fs, inode)].di_un.di_icom;
+ filep->fi_inode->i_number = inode;
+ set_cache((int)inode, (void *)filep->fi_inode, sizeof (struct inode));
+ return (0);
+}
+
+static fileid_t *
+find_fp(int fd)
+{
+ fileid_t *filep = head;
+
+ if (fd >= 0) {
+ while ((filep = filep->fi_forw) != head)
+ if (fd == filep->fi_filedes)
+ return (filep->fi_taken ? filep : 0);
+ }
+
+ return (0);
+}
+
+static ino_t
+find(fileid_t *filep, char *path)
+{
+ char *q;
+ char c;
+ ino_t inode;
+ char lpath[MAXPATHLEN];
+ char *lpathp = lpath;
+ int len, r;
+ devid_t *devp;
+
+ if (path == NULL || *path == '\0') {
+ printf("null path\n");
+ return ((ino_t)0);
+ }
+
+ dprintf("openi: %s\n", path);
+
+ bzero(lpath, sizeof (lpath));
+ bcopy(path, lpath, strlen(path));
+ devp = filep->fi_devp;
+ while (*lpathp) {
+ /* if at the beginning of pathname get root inode */
+ r = (lpathp == lpath);
+ if (r && openi(filep, (ino_t)UFSROOTINO))
+ return ((ino_t)0);
+ while (*lpathp == '/')
+ lpathp++; /* skip leading slashes */
+ q = lpathp;
+ while (*q != '/' && *q != '\0')
+ q++; /* find end of component */
+ c = *q;
+ *q = '\0'; /* terminate component */
+
+ /* Bail out early if opening root */
+ if (r && (*lpathp == '\0'))
+ return ((ino_t)UFSROOTINO);
+ if ((inode = dlook(filep, lpathp)) != 0) {
+ if (openi(filep, inode))
+ return ((ino_t)0);
+ if ((filep->fi_inode->i_smode & IFMT) == IFLNK) {
+ filep->fi_blocknum =
+ fsbtodb(&devp->un_fs.di_fs,
+ filep->fi_inode->i_db[0]);
+ filep->fi_count = DEV_BSIZE;
+ filep->fi_memp = 0;
+ if (diskread(filep) != 0)
+ return ((ino_t)0);
+ len = strlen(filep->fi_memp);
+ if (filep->fi_memp[0] == '/')
+ /* absolute link */
+ lpathp = lpath;
+ /* copy rest of unprocessed path up */
+ bcopy(q, lpathp + len, strlen(q + 1) + 2);
+ /* point to unprocessed path */
+ *(lpathp + len) = c;
+ /* prepend link in before unprocessed path */
+ bcopy(filep->fi_memp, lpathp, len);
+ lpathp = lpath;
+ continue;
+ } else
+ *q = c;
+ if (c == '\0')
+ break;
+ lpathp = q;
+ continue;
+ } else {
+ return ((ino_t)0);
+ }
+ }
+ return (inode);
+}
+
+static daddr32_t
+sbmap(fileid_t *filep, daddr32_t bn)
+{
+ struct inode *inodep;
+ int i, j, sh;
+ daddr32_t nb, *bap;
+ daddr32_t *db;
+ devid_t *devp;
+
+ devp = filep->fi_devp;
+ inodep = filep->fi_inode;
+ db = inodep->i_db;
+
+ /*
+ * blocks 0..NDADDR are direct blocks
+ */
+ if (bn < NDADDR) {
+ nb = db[bn];
+ return (nb);
+ }
+
+ /*
+ * addresses NIADDR have single and double indirect blocks.
+ * the first step is to determine how many levels of indirection.
+ */
+ sh = 1;
+ bn -= NDADDR;
+ for (j = NIADDR; j > 0; j--) {
+ sh *= NINDIR(&devp->un_fs.di_fs);
+ if (bn < sh)
+ break;
+ bn -= sh;
+ }
+ if (j == 0) {
+ return ((daddr32_t)0);
+ }
+
+ /*
+ * fetch the first indirect block address from the inode
+ */
+ nb = inodep->i_ib[NIADDR - j];
+ if (nb == 0) {
+ return ((daddr32_t)0);
+ }
+
+ /*
+ * fetch through the indirect blocks
+ */
+ for (; j <= NIADDR; j++) {
+ if (blknos[j] != nb) {
+ filep->fi_blocknum = fsbtodb(&devp->un_fs.di_fs, nb);
+ filep->fi_count = devp->un_fs.di_fs.fs_bsize;
+ filep->fi_memp = 0;
+ if (diskread(filep) != 0)
+ return (0);
+ b.blk[j] = filep->fi_memp;
+ blknos[j] = nb;
+ }
+ bap = (daddr32_t *)b.blk[j];
+ sh /= NINDIR(&devp->un_fs.di_fs);
+ i = (bn / sh) % NINDIR(&devp->un_fs.di_fs);
+ nb = bap[i];
+ if (nb == 0) {
+ return ((daddr32_t)0);
+ }
+ }
+ return (nb);
+}
+
+static ino_t
+dlook(fileid_t *filep, char *path)
+{
+ struct direct *dp;
+ struct inode *ip;
+ struct dirinfo dirp;
+ int len;
+
+ ip = filep->fi_inode;
+ if (path == NULL || *path == '\0')
+ return (0);
+
+ dprintf("dlook: %s\n", path);
+
+ if ((ip->i_smode & IFMT) != IFDIR) {
+ return (0);
+ }
+ if (ip->i_size == 0) {
+ return (0);
+ }
+ len = strlen(path);
+ dirp.loc = 0;
+ dirp.fi = filep;
+ for (dp = readdir(&dirp); dp != NULL; dp = readdir(&dirp)) {
+ if (dp->d_ino == 0)
+ continue;
+ if (dp->d_namlen == len && strcmp(path, dp->d_name) == 0) {
+ return (dp->d_ino);
+ }
+ /* Allow "*" to print all names at that level, w/out match */
+ if (strcmp(path, "*") == 0)
+ dprintf("%s\n", dp->d_name);
+ }
+ return (0);
+}
+
+/*
+ * get next entry in a directory.
+ */
+struct direct *
+readdir(struct dirinfo *dstuff)
+{
+ struct direct *dp;
+ fileid_t *filep;
+ daddr32_t lbn, d;
+ int off;
+ devid_t *devp;
+
+ filep = dstuff->fi;
+ devp = filep->fi_devp;
+ for (;;) {
+ if (dstuff->loc >= filep->fi_inode->i_size) {
+ return (NULL);
+ }
+ off = blkoff(&devp->un_fs.di_fs, dstuff->loc);
+ dprintf("readdir: off = 0x%x\n", off);
+ if (off == 0) {
+ lbn = lblkno(&devp->un_fs.di_fs, dstuff->loc);
+ d = sbmap(filep, lbn);
+
+ if (d == 0)
+ return (NULL);
+
+ filep->fi_blocknum = fsbtodb(&devp->un_fs.di_fs, d);
+ filep->fi_count =
+ blksize(&devp->un_fs.di_fs, filep->fi_inode, lbn);
+ filep->fi_memp = 0;
+ if (diskread(filep) != 0) {
+ return (NULL);
+ }
+ }
+ dp = (struct direct *)(filep->fi_memp + off);
+ dstuff->loc += dp->d_reclen;
+ if (dp->d_ino == 0)
+ continue;
+ dprintf("readdir: name = %s\n", dp->d_name);
+ return (dp);
+ }
+}
+
+/*
+ * Get the next block of data from the file. If possible, dma right into
+ * user's buffer
+ */
+static int
+getblock(fileid_t *filep, caddr_t buf, int count, int *rcount)
+{
+ struct fs *fs;
+ caddr_t p;
+ int off, size, diff;
+ daddr32_t lbn;
+ devid_t *devp;
+
+ dprintf("getblock: buf 0x%p, count 0x%x\n", (void *)buf, count);
+
+ devp = filep->fi_devp;
+ p = filep->fi_memp;
+ if ((signed)filep->fi_count <= 0) {
+
+ /* find the amt left to be read in the file */
+ diff = filep->fi_inode->i_size - filep->fi_offset;
+ if (diff <= 0) {
+ printf("Short read\n");
+ return (-1);
+ }
+
+ fs = &devp->un_fs.di_fs;
+ /* which block (or frag) in the file do we read? */
+ lbn = lblkno(fs, filep->fi_offset);
+
+ /* which physical block on the device do we read? */
+ filep->fi_blocknum = fsbtodb(fs, sbmap(filep, lbn));
+
+ off = blkoff(fs, filep->fi_offset);
+
+ /* either blksize or fragsize */
+ size = blksize(fs, filep->fi_inode, lbn);
+ filep->fi_count = size;
+ filep->fi_memp = filep->fi_buf;
+
+ /*
+ * optimization if we are reading large blocks of data then
+ * we can go directly to user's buffer
+ */
+ *rcount = 0;
+ if (off == 0 && count >= size) {
+ filep->fi_memp = buf;
+ if (diskread(filep)) {
+ return (-1);
+ }
+ *rcount = size;
+ filep->fi_count = 0;
+ return (0);
+ } else if (diskread(filep))
+ return (-1);
+
+ if (filep->fi_offset - off + size >= filep->fi_inode->i_size)
+ filep->fi_count = diff + off;
+ filep->fi_count -= off;
+ p = &filep->fi_memp[off];
+ }
+ filep->fi_memp = p;
+ return (0);
+}
+
+
+/*
+ * This is the high-level read function. It works like this.
+ * We assume that our IO device buffers up some amount of
+ * data and that we can get a ptr to it. Thus we need
+ * to actually call the device func about filesize/blocksize times
+ * and this greatly increases our IO speed. When we already
+ * have data in the buffer, we just return that data (with bcopy() ).
+ */
+
+static ssize_t
+bufs_read(int fd, caddr_t buf, size_t count)
+{
+ size_t i, j;
+ caddr_t n;
+ int rcount;
+ fileid_t *filep;
+
+ if (!(filep = find_fp(fd))) {
+ return (-1);
+ }
+
+ if (filep->fi_offset + count > filep->fi_inode->i_size)
+ count = filep->fi_inode->i_size - filep->fi_offset;
+
+ /* that was easy */
+ if ((i = count) == 0)
+ return (0);
+
+ n = buf;
+ while (i > 0) {
+ /* If we need to reload the buffer, do so */
+ if ((j = filep->fi_count) == 0) {
+ (void) getblock(filep, buf, i, &rcount);
+ i -= rcount;
+ buf += rcount;
+ filep->fi_offset += rcount;
+ } else {
+ /* else just bcopy from our buffer */
+ j = MIN(i, j);
+ bcopy(filep->fi_memp, buf, (unsigned)j);
+ buf += j;
+ filep->fi_memp += j;
+ filep->fi_offset += j;
+ filep->fi_count -= j;
+ i -= j;
+ }
+ }
+ return (buf - n);
+}
+
+/*
+ * This routine will open a device as it is known by the V2 OBP.
+ * Interface Defn:
+ * err = mountroot(string);
+ * err = 0 on success
+ * err = -1 on failure
+ * string: char string describing the properties of the device.
+ * We must not dork with any fi[]'s here. Save that for later.
+ */
+
+static int
+bufs_mountroot(char *str)
+{
+ if (ufs_devp) /* already mounted */
+ return (0);
+
+ ufs_devp = (devid_t *)bkmem_alloc(sizeof (devid_t));
+ ufs_devp->di_taken = 1;
+ ufs_devp->di_dcookie = 0;
+ ufs_devp->di_desc = (char *)bkmem_alloc(strlen(str) + 1);
+ (void) strcpy(ufs_devp->di_desc, str);
+ bzero(ufs_devp->un_fs.dummy, SBSIZE);
+ head = (fileid_t *)bkmem_alloc(sizeof (fileid_t));
+ head->fi_back = head->fi_forw = head;
+ head->fi_filedes = 0;
+ head->fi_taken = 0;
+
+ /* Setup read of the superblock */
+ head->fi_devp = ufs_devp;
+ head->fi_blocknum = SBLOCK;
+ head->fi_count = (uint_t)SBSIZE;
+ head->fi_memp = (caddr_t)&(ufs_devp->un_fs.di_fs);
+ head->fi_offset = 0;
+
+ if (diskread(head)) {
+ printf("failed to read superblock\n");
+ (void) bufs_closeall(1);
+ return (-1);
+ }
+
+ if (ufs_devp->un_fs.di_fs.fs_magic != FS_MAGIC) {
+ dprintf("fs magic = 0x%x\n", ufs_devp->un_fs.di_fs.fs_magic);
+ (void) bufs_closeall(1);
+ return (-1);
+ }
+ dprintf("mountroot succeeded\n");
+ return (0);
+}
+
+/*
+ * Unmount the currently mounted root fs. In practice, this means
+ * closing all open files and releasing resources. All of this
+ * is done by closeall().
+ */
+
+static int
+bufs_unmountroot(void)
+{
+ if (ufs_devp == NULL)
+ return (-1);
+
+ (void) bufs_closeall(1);
+
+ return (0);
+}
+
+/*
+ * We allocate an fd here for use when talking
+ * to the file itself.
+ */
+
+/*ARGSUSED*/
+static int
+bufs_open(char *filename, int flags)
+{
+ fileid_t *filep;
+ ino_t inode;
+ static int filedes = 1;
+
+ dprintf("open: %s\n", filename);
+
+ /* build and link a new file descriptor */
+ filep = (fileid_t *)bkmem_alloc(sizeof (fileid_t));
+ filep->fi_back = head->fi_back;
+ filep->fi_forw = head;
+ head->fi_back->fi_forw = filep;
+ head->fi_back = filep;
+ filep->fi_filedes = filedes++;
+ filep->fi_taken = 1;
+ filep->fi_path = (char *)bkmem_alloc(strlen(filename) + 1);
+ (void) strcpy(filep->fi_path, filename);
+ filep->fi_devp = ufs_devp; /* dev is already "mounted" */
+ filep->fi_inode = NULL;
+ bzero(filep->fi_buf, MAXBSIZE);
+
+ inode = find(filep, (char *)filename);
+ if (inode == (ino_t)0) {
+ dprintf("open: cannot find %s\n", filename);
+ (void) bufs_close(filep->fi_filedes);
+ return (-1);
+ }
+ if (openi(filep, inode)) {
+ printf("open: cannot open %s\n", filename);
+ (void) bufs_close(filep->fi_filedes);
+ return (-1);
+ }
+
+ filep->fi_offset = filep->fi_count = 0;
+
+ return (filep->fi_filedes);
+}
+
+/*
+ * We don't do any IO here.
+ * We just play games with the device pointers.
+ */
+
+static off_t
+bufs_lseek(int fd, off_t addr, int whence)
+{
+ fileid_t *filep;
+
+ /* Make sure user knows what file he is talking to */
+ if (!(filep = find_fp(fd)))
+ return (-1);
+
+ switch (whence) {
+ case SEEK_CUR:
+ filep->fi_offset += addr;
+ break;
+ case SEEK_SET:
+ filep->fi_offset = addr;
+ break;
+ default:
+ case SEEK_END:
+ printf("lseek(): invalid whence value %d\n", whence);
+ break;
+ }
+
+ filep->fi_blocknum = addr / DEV_BSIZE;
+ filep->fi_count = 0;
+
+ return (0);
+}
+
+static int
+bufs_close(int fd)
+{
+ fileid_t *filep;
+
+ /* Make sure user knows what file he is talking to */
+ if (!(filep = find_fp(fd)))
+ return (-1);
+
+ if (filep->fi_taken && (filep != head)) {
+ /* Clear the ranks */
+ bkmem_free(filep->fi_path, strlen(filep->fi_path)+1);
+ filep->fi_blocknum = filep->fi_count = filep->fi_offset = 0;
+ filep->fi_memp = (caddr_t)0;
+ filep->fi_devp = 0;
+ filep->fi_taken = 0;
+
+ /* unlink and deallocate node */
+ filep->fi_forw->fi_back = filep->fi_back;
+ filep->fi_back->fi_forw = filep->fi_forw;
+ bkmem_free((char *)filep, sizeof (fileid_t));
+
+ return (0);
+ } else {
+ /* Big problem */
+ printf("\nFile descrip %d not allocated!", fd);
+ return (-1);
+ }
+}
+
+/*ARGSUSED*/
+static void
+bufs_closeall(int flag)
+{
+ fileid_t *filep = head;
+
+ while ((filep = filep->fi_forw) != head)
+ if (filep->fi_taken)
+ if (bufs_close(filep->fi_filedes))
+ printf("Filesystem may be inconsistent.\n");
+
+ ufs_devp->di_taken = 0;
+ bkmem_free((char *)ufs_devp, sizeof (devid_t));
+ bkmem_free((char *)head, sizeof (fileid_t));
+ ufs_devp = (devid_t *)NULL;
+ head = (fileid_t *)NULL;
+ free_cache();
+}
+
+static struct cache {
+ struct cache *next;
+ void *data;
+ int key;
+ uint_t size;
+} *icache;
+
+void
+set_cache(int key, void *data, uint_t size)
+{
+ struct cache *entry = bkmem_alloc(sizeof (*entry));
+ entry->key = key;
+ entry->data = data;
+ entry->size = size;
+ if (icache) {
+ entry->next = icache;
+ icache = entry;
+ } else {
+ icache = entry;
+ entry->next = 0;
+ }
+}
+
+void *
+get_cache(int key)
+{
+ struct cache *entry = icache;
+ while (entry) {
+ if (entry->key == key)
+ return (entry->data);
+ entry = entry->next;
+ }
+ return (NULL);
+}
+
+void
+free_cache()
+{
+ struct cache *next, *entry = icache;
+ while (entry) {
+ next = entry->next;
+ bkmem_free(entry->data, entry->size);
+ bkmem_free(entry, sizeof (*entry));
+ entry = next;
+ }
+ icache = 0;
+}
+
+struct boot_fs_ops bufs_ops = {
+ "boot_ufs",
+ bufs_mountroot,
+ bufs_unmountroot,
+ bufs_open,
+ bufs_close,
+ bufs_read,
+ bufs_lseek,
+ NULL
+};