diff options
author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
---|---|---|
committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/common/fs | |
download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz |
OpenSolaris Launch
Diffstat (limited to 'usr/src/common/fs')
-rw-r--r-- | usr/src/common/fs/hsfs.c | 760 | ||||
-rw-r--r-- | usr/src/common/fs/pcfilep.h | 231 | ||||
-rw-r--r-- | usr/src/common/fs/pcfs.c | 623 | ||||
-rw-r--r-- | usr/src/common/fs/ufsops.c | 737 |
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 +}; |