| /* |
| * CDDL HEADER START |
| * |
| * The contents of this file are subject to the terms of the |
| * Common Development and Distribution License (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 2007 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); |
| extern int cf_check_compressed(fileid_t *); |
| extern void cf_close(fileid_t *); |
| extern void cf_seek(fileid_t *, off_t, int); |
| extern int cf_read(fileid_t *, caddr_t, 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 int bhsfs_fstat(int fdesc, struct bootstat *stp); |
| |
| 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'; |
| n = dlook(path, filep); |
| *q = c; |
| path = q; |
| |
| if (n != 0) { |
| if (c == '\0') |
| break; |
| if (opendir(n, filep)) |
| return (0); |
| 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, ", fd); |
| dprintf("count 0x%lx\n", count); |
| filep = find_fp(fd); |
| if (filep == NULL) |
| return (-1); |
| |
| ip = filep->fi_inode; |
| n = buf; |
| if ((filep->fi_flags & FI_COMPRESSED) == 0 && |
| 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_flags & FI_COMPRESSED) { |
| if ((j = cf_read(filep, buf, count)) < 0) |
| return (0); /* encountered an error */ |
| if (j < i) |
| i = j; /* short read, must have hit EOF */ |
| } else { |
| 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); |
| } |
| filep->fi_memp += j; |
| filep->fi_offset += j; |
| filep->fi_count -= j; |
| buf += 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); |
| filep->fi_getblock = getblock; |
| filep->fi_flags = 0; |
| |
| 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; |
| |
| if (cf_check_compressed(filep) != 0) |
| return (-1); |
| 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 not allocated!\n", fd); |
| return (-1); |
| } |
| |
| cf_close(filep); |
| /* 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, ", fd); |
| dprintf("off = %lx\n", addr); |
| if (!(filep = find_fp(fd))) |
| return (-1); |
| |
| if (filep->fi_flags & FI_COMPRESSED) { |
| cf_seek(filep, addr, whence); |
| } else { |
| 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 |
| bhsfs_fstat(int fd, struct bootstat *stp) |
| { |
| fileid_t *filep; |
| struct inode *ip; |
| |
| if (!(filep = find_fp(fd))) |
| return (-1); |
| |
| ip = filep->fi_inode; |
| |
| stp->st_mode = 0; |
| stp->st_size = 0; |
| |
| if (ip == NULL) |
| return (0); |
| |
| switch (ip->i_smode & IFMT) { |
| case IFDIR: |
| stp->st_mode = S_IFDIR; |
| break; |
| case IFREG: |
| stp->st_mode = S_IFREG; |
| break; |
| default: |
| break; |
| } |
| /* |
| * NOTE: this size will be the compressed size for a compressed file |
| * This could confuse the caller since we decompress the file behind |
| * the scenes when the file is read. |
| */ |
| stp->st_size = ip->i_size; |
| |
| /* file times */ |
| stp->st_atim.tv_sec = ip->i_atime.tv_sec; |
| stp->st_atim.tv_nsec = ip->i_atime.tv_usec * 1000; |
| stp->st_mtim.tv_sec = ip->i_mtime.tv_sec; |
| stp->st_mtim.tv_nsec = ip->i_mtime.tv_usec * 1000; |
| stp->st_ctim.tv_sec = ip->i_ctime.tv_sec; |
| stp->st_ctim.tv_nsec = ip->i_ctime.tv_usec * 1000; |
| |
| 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, |
| bhsfs_fstat, |
| NULL |
| }; |