diff options
Diffstat (limited to 'package/aboot/src/fs/ufs.c')
-rw-r--r-- | package/aboot/src/fs/ufs.c | 485 |
1 files changed, 485 insertions, 0 deletions
diff --git a/package/aboot/src/fs/ufs.c b/package/aboot/src/fs/ufs.c new file mode 100644 index 000000000..4cda3ceee --- /dev/null +++ b/package/aboot/src/fs/ufs.c @@ -0,0 +1,485 @@ +/* + * Mach Operating System + * Copyright (c) 1993 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * HISTORY + * $Log: ufs.c,v $ + * Revision 1.1.1.2 2007/08/16 07:04:23 vorlon + * Import upstream "release" 1.0pre20040408 to CVS to facilitate rebasing + * against the current upstream work and avoid copying patches one-by-one. + * + * Revision 1.2 2003/11/08 00:03:36 wgwoods + * Reverted changes from 0.10, merged doc changes + * + * Revision 1.1.1.1.2.1 2003/03/21 23:32:23 wgwoods + * Warning cleanups + * + * Revision 1.1.1.1 2001/10/08 23:03:52 wgwoods + * initial import of CVS source from alphalinux.org, plus a couple bugfixes + * + * Revision 1.1.1.1 2000/05/03 03:58:22 dhd + * Initial import (from 0.7 release) + * + * Revision 2.2 93/02/05 08:01:36 danner + * Adapted for alpha. + * [93/02/04 af] + * + */ +/* + * File: ufs.c + * Author: David Golub, Carnegie Mellon University + * Date: 12/90 + * + * Stand-alone file reading package. + * + * Modified for use by Linux/Alpha by David Mosberger + * (davidm@cs.arizona.edu) + */ +#include <linux/kernel.h> +#include <asm/stat.h> + +#include "aboot.h" +#include "bootfs.h" +#include "cons.h" +#include "disklabel.h" +#include "ufs.h" +#include "utils.h" +#include "string.h" + +#define MAX_OPEN_FILES 1 + +extern struct bootfs ufs; + +static long dev; +static long partition_offset; +static struct fs *fs; +static struct file { + int inuse; + struct icommon i_ic; /* copy of on-disk inode */ + int f_nindir[NIADDR+1]; + /* number of blocks mapped by + indirect block at level i */ + void *f_blk[NIADDR]; /* buffer for indir block at level i */ + long f_blksize[NIADDR]; + /* size of buffer */ + __kernel_daddr_t f_blkno[NIADDR]; + /* disk address of block in buffer */ + void *f_buf; /* buffer for data block */ + long f_buf_size; /* size of data block */ + __kernel_daddr_t f_buf_blkno; /* block number of data block */ +} inode_table[MAX_OPEN_FILES]; + + +static int read_inode(__kernel_ino_t inumber, struct file *fp) +{ + __kernel_daddr_t disk_block; + long offset; + struct dinode *dp; + int level; + + disk_block = itod(fs, inumber); + + offset = fsbtodb(fs, disk_block) * DEV_BSIZE + partition_offset; + if (cons_read(dev, fp->f_buf, fs->fs_bsize, offset) != fs->fs_bsize) { + printf("ufs_read_inode: read error\n"); + return 1; + } + dp = (struct dinode *)fp->f_buf; + dp += itoo(fs, inumber); + fp->i_ic = dp->di_ic; + /* + * Clear out the old buffers + */ + for (level = 0; level < NIADDR; level++) { + if (fp->f_blk[level]) { + free(fp->f_blk[level]); + fp->f_blk[level] = 0; + } + fp->f_blkno[level] = -1; + } + return 0; +} + + +/* + * Given an offset in a file, find the disk block number that + * contains that block. + */ +static __kernel_daddr_t block_map(struct file *fp, __kernel_daddr_t file_block) +{ + __kernel_daddr_t ind_block_num, *ind_p; + int level, idx; + long offset; + /* + * Index structure of an inode: + * + * i_db[0..NDADDR-1] hold block numbers for blocks + * 0..NDADDR-1 + * + * i_ib[0] index block 0 is the single indirect + * block + * holds block numbers for blocks + * NDADDR .. NDADDR + NINDIR(fs)-1 + * + * i_ib[1] index block 1 is the double indirect + * block + * holds block numbers for INDEX blocks + * for blocks + * NDADDR + NINDIR(fs) .. + * NDADDR + NINDIR(fs) + NINDIR(fs)**2 - 1 + * + * i_ib[2] index block 2 is the triple indirect + * block + * holds block numbers for double-indirect + * blocks for blocks + * NDADDR + NINDIR(fs) + NINDIR(fs)**2 .. + * NDADDR + NINDIR(fs) + NINDIR(fs)**2 + * + NINDIR(fs)**3 - 1 + */ + if (file_block < NDADDR) { + /* Direct block. */ + return fp->i_db[file_block]; + } + + file_block -= NDADDR; + + /* + * nindir[0] = NINDIR + * nindir[1] = NINDIR**2 + * nindir[2] = NINDIR**3 + * etc + */ + for (level = 0; level < NIADDR; level++) { + if (file_block < fp->f_nindir[level]) + break; + file_block -= fp->f_nindir[level]; + } + if (level == NIADDR) { + printf("ufs_block_map: block number too high\n"); + return -1; + } + + ind_block_num = fp->i_ib[level]; + + for (; level >= 0; level--) { + if (ind_block_num == 0) { + return 0; + } + + if (fp->f_blkno[level] != ind_block_num) { + if (fp->f_blk[level]) { + free(fp->f_blk[level]); + } + + offset = fsbtodb(fs, ind_block_num) * DEV_BSIZE + + partition_offset; + fp->f_blk[level] = malloc(fs->fs_bsize); + if (cons_read(dev, fp->f_blk[level], fs->fs_bsize, + offset) + != fs->fs_bsize) + { + printf("ufs_block_map: read error\n"); + return -1; + } + fp->f_blkno[level] = ind_block_num; + } + + ind_p = (__kernel_daddr_t *)fp->f_blk[level]; + + if (level > 0) { + idx = file_block / fp->f_nindir[level-1]; + file_block %= fp->f_nindir[level-1]; + } else { + idx = file_block; + } + ind_block_num = ind_p[idx]; + } + return ind_block_num; +} + + +static int breadi(struct file *fp, long blkno, long nblks, char *buffer) +{ + long block_size, offset, tot_bytes, nbytes, ncontig; + __kernel_daddr_t disk_block; + + tot_bytes = 0; + while (nblks) { + /* + * Contiguous reads are a lot faster, so we try to group + * as many blocks as possible: + */ + ncontig = 0; /* # of *fragments* that are contiguous */ + nbytes = 0; + disk_block = block_map(fp, blkno); + do { + block_size = blksize(fs, fp, blkno); + nbytes += block_size; + ncontig += numfrags(fs, block_size); + ++blkno; --nblks; + } while (nblks && + block_map(fp, blkno) == disk_block + ncontig); + + if (!disk_block) { + /* it's a hole... */ + memset(buffer, 0, nbytes); + } else { + offset = fsbtodb(fs, disk_block) * DEV_BSIZE + + partition_offset; + if (cons_read(dev, buffer, nbytes, offset) != nbytes) { + printf("ufs_breadi: read error\n"); + return -1; + } + } + buffer += nbytes; + tot_bytes += nbytes; + } + return tot_bytes; +} + + +/* + * Search a directory for a name and return its + * i_number. + */ +static int search_dir(const char *name, struct file *fp, __kernel_ino_t *inumber_p) +{ + long offset, blockoffset; + struct direct *dp; + int len; + + len = strlen(name); + + offset = 0; + while (offset < fp->i_size) { + blockoffset = 0; + if (breadi(fp, offset / fs->fs_bsize, 1, fp->f_buf) < 0) { + return -1; + } + while (blockoffset < fs->fs_bsize) { + dp = (struct direct *)((char*)fp->f_buf + blockoffset); + if (dp->d_ino) { + if (dp->d_namlen == len + && strcmp(name, dp->d_name) == 0) + { + /* found entry */ + *inumber_p = dp->d_ino; + return 0; + } + } + blockoffset += dp->d_reclen; + } + offset += fs->fs_bsize; + } + return -1; +} + + +/* + * Initialize a BSD FFS partition starting at offset P_OFFSET; this is + * sort-of the same idea as "mounting" it. Read in the relevant + * control structures and make them available to the user. Returns 0 + * if successful, -1 on failure. + */ +static int ufs_mount(long cons_dev, long p_offset, long quiet) +{ + static char buf[SBSIZE]; /* minimize frame size */ + long rc; + + memset(&inode_table, 0, sizeof(inode_table)); + + dev = cons_dev; + partition_offset = p_offset; + + rc = cons_read(dev, buf, SBSIZE, SBLOCK*DEV_BSIZE + partition_offset); + if (rc != SBSIZE) + { + printf("ufs_mount: superblock read failed (retval=%ld)\n", rc); + return -1; + } + + fs = (struct fs *)buf; + if (fs->fs_magic != FS_MAGIC || + fs->fs_bsize > MAXBSIZE || + fs->fs_bsize < (int) sizeof(struct fs)) + { + if (!quiet) { + printf("ufs_mount: invalid superblock " + "(magic=%x, bsize=%d)\n", + fs->fs_magic, fs->fs_bsize); + } + return -1; + } + ufs.blocksize = fs->fs_bsize; + + /* don't read cylinder groups - we aren't modifying anything */ + return 0; +} + + +static int ufs_open(const char *path) +{ + char *cp = 0, *component; + int fd; + __kernel_ino_t inumber, parent_inumber; + int nlinks = 0; + struct file *fp; + static char namebuf[MAXPATHLEN+1]; + + if (!path || !*path) { + return -1; + } + + for (fd = 0; inode_table[fd].inuse; ++fd) { + if (fd >= MAX_OPEN_FILES) { + return -1; + } + } + fp = &inode_table[fd]; + fp->f_buf_size = fs->fs_bsize; + fp->f_buf = malloc(fp->f_buf_size); + + /* copy name into buffer to allow modifying it: */ + memcpy(namebuf, path, (unsigned)(strlen(path) + 1)); + + inumber = (__kernel_ino_t) ROOTINO; + if (read_inode(inumber, fp) < 0) { + return -1; + } + + component = strtok(namebuf, "/"); + while (component) { + /* verify that current node is a directory: */ + if ((fp->i_mode & IFMT) != IFDIR) { + return -1; + } + /* + * Look up component in current directory. + * Save directory inumber in case we find a + * symbolic link. + */ + parent_inumber = inumber; + if (search_dir(component, fp, &inumber)) + return -1; + + /* open next component: */ + if (read_inode(inumber, fp)) + return -1; + + /* check for symbolic link: */ + if ((fp->i_mode & IFMT) == IFLNK) { + int link_len = fp->i_size; + int len; + + len = strlen(cp) + 1; + + if (link_len + len >= MAXPATHLEN - 1) { + return -1; + } + + if (++nlinks > MAXSYMLINKS) { + return FS_SYMLINK_LOOP; + } + memcpy(&namebuf[link_len], cp, len); +#ifdef IC_FASTLINK + if ((fp->i_flags & IC_FASTLINK) != 0) { + memcpy(namebuf, fp->i_symlink, link_len); + } else +#endif /* IC_FASTLINK */ + { + /* read file for symbolic link: */ + long rc, offset; + __kernel_daddr_t disk_block; + + disk_block = block_map(fp, (__kernel_daddr_t)0); + offset = fsbtodb(fs, disk_block) * DEV_BSIZE + + partition_offset; + rc = cons_read(dev, namebuf, sizeof(namebuf), + offset); + if (rc != sizeof(namebuf)) { + return -1; + } + } + /* + * If relative pathname, restart at parent directory. + * If absolute pathname, restart at root. + */ + cp = namebuf; + if (*cp != '/') { + inumber = parent_inumber; + } else + inumber = (__kernel_ino_t)ROOTINO; + + if (read_inode(inumber, fp)) + return -1; + } + component = strtok(NULL, "/"); + } + /* calculate indirect block levels: */ + { + register int mult; + register int level; + + mult = 1; + for (level = 0; level < NIADDR; level++) { + mult *= NINDIR(fs); + fp->f_nindir[level] = mult; + } + } + return fd; +} + + +static int ufs_bread(int fd, long blkno, long nblks, char *buffer) +{ + struct file *fp; + + fp = &inode_table[fd]; + return breadi(fp, blkno, nblks, buffer); +} + + +static void ufs_close(int fd) +{ + inode_table[fd].inuse = 0; +} + +static const char * +ufs_readdir(int fd, int rewind) +{ + return NULL; +} + +static int +ufs_fstat(int fd, struct stat* buf) +{ + return -1; +} + +struct bootfs ufs = { + FS_BSDFFS, 0, + ufs_mount, + ufs_open, ufs_bread, ufs_close, ufs_readdir, ufs_fstat +}; |