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/ufsops.c | |
download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz |
OpenSolaris Launch
Diffstat (limited to 'usr/src/common/fs/ufsops.c')
-rw-r--r-- | usr/src/common/fs/ufsops.c | 737 |
1 files changed, 737 insertions, 0 deletions
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 +}; |