summaryrefslogtreecommitdiff
path: root/package/aboot/src/fs
diff options
context:
space:
mode:
Diffstat (limited to 'package/aboot/src/fs')
-rw-r--r--package/aboot/src/fs/dummy.c80
-rw-r--r--package/aboot/src/fs/ext2.c623
-rw-r--r--package/aboot/src/fs/iso.c59
-rw-r--r--package/aboot/src/fs/ufs.c485
4 files changed, 1247 insertions, 0 deletions
diff --git a/package/aboot/src/fs/dummy.c b/package/aboot/src/fs/dummy.c
new file mode 100644
index 000000000..82d73d441
--- /dev/null
+++ b/package/aboot/src/fs/dummy.c
@@ -0,0 +1,80 @@
+/*
+ * This is a set of functions that provides access to a Linux kernel
+ * starting at sector BOOT_SECT+aboot_size/SECT_SIZE
+ *
+ * Michael Schwingen (rincewind@discworld.oche.de).
+ */
+#include "system.h"
+
+#include <config.h>
+#include <aboot.h>
+#include <bootfs.h>
+#include <cons.h>
+#include <utils.h>
+
+#define BLOCKSIZE (16*SECT_SIZE)
+
+static int dummy_mount(long cons_dev, long p_offset, long quiet);
+static int dummy_bread(int fd, long blkno, long nblks, char *buffer);
+static int dummy_open(const char *filename);
+static void dummy_close(int fd);
+
+struct bootfs dummyfs = {
+ 0, BLOCKSIZE,
+ dummy_mount,
+ dummy_open, dummy_bread, dummy_close
+};
+
+static long dev = -1;
+
+
+/*
+ * Initialize 'filesystem'
+ * Returns 0 if successful, -1 on failure.
+ */
+static int
+dummy_mount(long cons_dev, long p_offset, long quiet)
+{
+ dev = cons_dev;
+ return 0;
+}
+
+
+/*
+ * Read block number "blkno".
+ */
+static int
+dummy_bread(int fd, long blkno, long nblks, char *buffer)
+{
+ extern char _end;
+ static long aboot_size = 0;
+
+ if (!aboot_size) {
+ aboot_size = &_end - (char *) BOOT_ADDR + SECT_SIZE - 1;
+ aboot_size &= ~(SECT_SIZE - 1);
+ }
+
+ if (cons_read(dev, buffer, nblks*BLOCKSIZE,
+ BOOT_SECTOR*SECT_SIZE + blkno*BLOCKSIZE + aboot_size)
+ != nblks*BLOCKSIZE)
+ {
+ printf("dummy_bread: read error\n");
+ return -1;
+ }
+ return nblks*BLOCKSIZE;
+}
+
+
+/*
+ * Unix-like open routine. Returns a small integer
+ * (does not care what file, we say it's OK)
+ */
+static int dummy_open(const char *filename)
+{
+ return 1;
+}
+
+
+static void dummy_close(int fd)
+{
+}
diff --git a/package/aboot/src/fs/ext2.c b/package/aboot/src/fs/ext2.c
new file mode 100644
index 000000000..b8cdefd13
--- /dev/null
+++ b/package/aboot/src/fs/ext2.c
@@ -0,0 +1,623 @@
+/*
+ * This is a set of functions that provides minimal filesystem
+ * functionality to the Linux bootstrapper. All we can do is
+ * open and read files... but that's all we need 8-)
+ *
+ * This file has been ported from the DEC 32-bit Linux version
+ * by David Mosberger (davidm@cs.arizona.edu).
+ */
+#include <linux/stat.h>
+#include <linux/types.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
+
+# undef __KERNEL__
+# include <linux/ext2_fs.h>
+# define __KERNEL__
+# include <linux/fs.h>
+
+#else /* Linux 2.4.0 or later */
+
+typedef unsigned short umode_t;
+# undef __KERNEL__
+# include <linux/ext2_fs.h>
+# include <linux/fs.h>
+# define __KERNEL__
+
+#endif
+
+#include "bootfs.h"
+#include "cons.h"
+#include "disklabel.h"
+#include "utils.h"
+#include "string.h"
+
+#define MAX_OPEN_FILES 5
+
+extern struct bootfs ext2fs;
+
+static struct ext2_super_block sb;
+static struct ext2_group_desc *gds;
+static struct ext2_inode *root_inode = NULL;
+static int ngroups = 0;
+static int directlim; /* Maximum direct blkno */
+static int ind1lim; /* Maximum single-indir blkno */
+static int ind2lim; /* Maximum double-indir blkno */
+static int ptrs_per_blk; /* ptrs/indirect block */
+static char blkbuf[EXT2_MAX_BLOCK_SIZE];
+static int cached_iblkno = -1;
+static char iblkbuf[EXT2_MAX_BLOCK_SIZE];
+static int cached_diblkno = -1;
+static char diblkbuf[EXT2_MAX_BLOCK_SIZE];
+static long dev = -1;
+static long partition_offset;
+
+static struct inode_table_entry {
+ struct ext2_inode inode;
+ int inumber;
+ int free;
+ unsigned short old_mode;
+} inode_table[MAX_OPEN_FILES];
+
+
+/*
+ * Initialize an ext2 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 ext2_mount(long cons_dev, long p_offset, long quiet)
+{
+ long sb_block = 1;
+ long sb_offset;
+ int i;
+
+ dev = cons_dev;
+ partition_offset = p_offset;
+
+ /* initialize the inode table */
+ for (i = 0; i < MAX_OPEN_FILES; i++) {
+ inode_table[i].free = 1;
+ inode_table[i].inumber = 0;
+ }
+ /* clear the root inode pointer (very important!) */
+ root_inode = NULL;
+
+ /* read in the first superblock */
+ sb_offset = sb_block * EXT2_MIN_BLOCK_SIZE;
+ if (cons_read(dev, &sb, sizeof(sb), partition_offset + sb_offset)
+ != sizeof(sb))
+ {
+ printf("ext2 sb read failed\n");
+ return -1;
+ }
+
+ if (sb.s_magic != EXT2_SUPER_MAGIC) {
+ if (!quiet) {
+ printf("ext2_init: bad magic 0x%x\n", sb.s_magic);
+ }
+ return -1;
+ }
+
+ ngroups = (sb.s_blocks_count -
+ sb.s_first_data_block +
+ EXT2_BLOCKS_PER_GROUP(&sb) - 1)
+ / EXT2_BLOCKS_PER_GROUP(&sb);
+
+ gds = (struct ext2_group_desc *)
+ malloc((size_t)(ngroups * sizeof(struct ext2_group_desc)));
+
+ ext2fs.blocksize = EXT2_BLOCK_SIZE(&sb);
+
+ /* read in the group descriptors (immediately follows superblock) */
+ cons_read(dev, gds, ngroups * sizeof(struct ext2_group_desc),
+ partition_offset +
+ ext2fs.blocksize * (EXT2_MIN_BLOCK_SIZE/ext2fs.blocksize + 1));
+ /*
+ * Calculate direct/indirect block limits for this file system
+ * (blocksize dependent):
+ */
+ ext2fs.blocksize = EXT2_BLOCK_SIZE(&sb);
+ directlim = EXT2_NDIR_BLOCKS - 1;
+ ptrs_per_blk = ext2fs.blocksize/sizeof(unsigned int);
+ ind1lim = ptrs_per_blk + directlim;
+ ind2lim = (ptrs_per_blk * ptrs_per_blk) + directlim;
+
+ return 0;
+}
+
+
+/*
+ * Read the specified inode from the disk and return it to the user.
+ * Returns NULL if the inode can't be read...
+ */
+static struct ext2_inode *ext2_iget(int ino)
+{
+ int i;
+ struct ext2_inode *ip;
+ struct inode_table_entry *itp = 0;
+ int group;
+ long offset;
+
+ ip = 0;
+ for (i = 0; i < MAX_OPEN_FILES; i++) {
+#ifdef DEBUG_EXT2
+ printf("ext2_iget: looping, entry %d inode %d free %d\n",
+ i, inode_table[i].inumber, inode_table[i].free);
+#endif
+ if (inode_table[i].free) {
+ itp = &inode_table[i];
+ ip = &itp->inode;
+ break;
+ }
+ }
+ if (!ip) {
+ printf("ext2_iget: no free inodes\n");
+ return NULL;
+ }
+
+ group = (ino-1) / sb.s_inodes_per_group;
+#ifdef DEBUG_EXT2
+ printf("group is %d\n", group);
+#endif
+ offset = partition_offset
+ + ((long) gds[group].bg_inode_table * (long)ext2fs.blocksize)
+ + (((ino - 1) % EXT2_INODES_PER_GROUP(&sb))
+ * EXT2_INODE_SIZE(&sb));
+#ifdef DEBUG_EXT2
+ printf("ext2_iget: reading %ld bytes at offset %ld "
+ "(%ld + (%d * %d) + ((%d) %% %d) * %d) "
+ "(inode %d -> table %d)\n",
+ sizeof(struct ext2_inode), offset, partition_offset,
+ gds[group].bg_inode_table, ext2fs.blocksize,
+ ino - 1, EXT2_INODES_PER_GROUP(&sb), EXT2_INODE_SIZE(&sb),
+ ino, (int) (itp - inode_table));
+#endif
+ if (cons_read(dev, ip, sizeof(struct ext2_inode), offset)
+ != sizeof(struct ext2_inode))
+ {
+ printf("ext2_iget: read error\n");
+ return NULL;
+ }
+
+ itp->free = 0;
+ itp->inumber = ino;
+ itp->old_mode = ip->i_mode;
+
+ return ip;
+}
+
+
+/*
+ * Release our hold on an inode. Since this is a read-only application,
+ * don't worry about putting back any changes...
+ */
+static void ext2_iput(struct ext2_inode *ip)
+{
+ struct inode_table_entry *itp;
+
+ /* Find and free the inode table slot we used... */
+ itp = (struct inode_table_entry *)ip;
+
+#ifdef DEBUG_EXT2
+ printf("ext2_iput: inode %d table %d\n", itp->inumber,
+ (int) (itp - inode_table));
+#endif
+ itp->inumber = 0;
+ itp->free = 1;
+}
+
+
+/*
+ * Map a block offset into a file into an absolute block number.
+ * (traverse the indirect blocks if necessary). Note: Double-indirect
+ * blocks allow us to map over 64Mb on a 1k file system. Therefore, for
+ * our purposes, we will NOT bother with triple indirect blocks.
+ *
+ * The "allocate" argument is set if we want to *allocate* a block
+ * and we don't already have one allocated.
+ */
+static int ext2_blkno(struct ext2_inode *ip, int blkoff)
+{
+ unsigned int *lp;
+ unsigned int *ilp;
+ unsigned int *dlp;
+ int blkno;
+ int iblkno;
+ int diblkno;
+ unsigned long offset;
+
+ ilp = (unsigned int *)iblkbuf;
+ dlp = (unsigned int *)diblkbuf;
+ lp = (unsigned int *)blkbuf;
+
+ /* If it's a direct block, it's easy! */
+ if (blkoff <= directlim) {
+ return ip->i_block[blkoff];
+ }
+
+ /* Is it a single-indirect? */
+ if (blkoff <= ind1lim) {
+ iblkno = ip->i_block[EXT2_IND_BLOCK];
+
+ if (iblkno == 0) {
+ return 0;
+ }
+
+ /* Read the indirect block */
+ if (cached_iblkno != iblkno) {
+ offset = partition_offset + (long)iblkno * (long)ext2fs.blocksize;
+ if (cons_read(dev, iblkbuf, ext2fs.blocksize, offset)
+ != ext2fs.blocksize)
+ {
+ printf("ext2_blkno: error on iblk read\n");
+ return 0;
+ }
+ cached_iblkno = iblkno;
+ }
+
+ blkno = ilp[blkoff-(directlim+1)];
+ return blkno;
+ }
+
+ /* Is it a double-indirect? */
+ if (blkoff <= ind2lim) {
+ /* Find the double-indirect block */
+ diblkno = ip->i_block[EXT2_DIND_BLOCK];
+
+ if (diblkno == 0) {
+ return 0;
+ }
+
+ /* Read in the double-indirect block */
+ if (cached_diblkno != diblkno) {
+ offset = partition_offset + (long) diblkno * (long) ext2fs.blocksize;
+ if (cons_read(dev, diblkbuf, ext2fs.blocksize, offset)
+ != ext2fs.blocksize)
+ {
+ printf("ext2_blkno: err reading dindr blk\n");
+ return 0;
+ }
+ cached_diblkno = diblkno;
+ }
+
+ /* Find the single-indirect block pointer ... */
+ iblkno = dlp[(blkoff - (ind1lim+1)) / ptrs_per_blk];
+
+ if (iblkno == 0) {
+ return 0;
+ }
+
+ /* Read the indirect block */
+
+ if (cached_iblkno != iblkno) {
+ offset = partition_offset + (long) iblkno * (long) ext2fs.blocksize;
+ if (cons_read(dev, iblkbuf, ext2fs.blocksize, offset)
+ != ext2fs.blocksize)
+ {
+ printf("ext2_blkno: err on iblk read\n");
+ return 0;
+ }
+ cached_iblkno = iblkno;
+ }
+
+ /* Find the block itself. */
+ blkno = ilp[(blkoff-(ind1lim+1)) % ptrs_per_blk];
+ return blkno;
+ }
+
+ if (blkoff > ind2lim) {
+ printf("ext2_blkno: block number too large: %d\n", blkoff);
+ return 0;
+ }
+ return -1;
+}
+
+
+static int ext2_breadi(struct ext2_inode *ip, long blkno, long nblks,
+ char *buffer)
+{
+ long dev_blkno, ncontig, offset, nbytes, tot_bytes;
+
+ tot_bytes = 0;
+ if ((blkno+nblks)*ext2fs.blocksize > ip->i_size)
+ nblks = (ip->i_size + ext2fs.blocksize) / ext2fs.blocksize - blkno;
+
+ while (nblks) {
+ /*
+ * Contiguous reads are a lot faster, so we try to group
+ * as many blocks as possible:
+ */
+ ncontig = 0; nbytes = 0;
+ dev_blkno = ext2_blkno(ip, blkno);
+ do {
+ ++blkno; ++ncontig; --nblks;
+ nbytes += ext2fs.blocksize;
+ } while (nblks &&
+ ext2_blkno(ip, blkno) == dev_blkno + ncontig);
+
+ if (dev_blkno == 0) {
+ /* This is a "hole" */
+ memset(buffer, 0, nbytes);
+ } else {
+ /* Read it for real */
+ offset = partition_offset + (long) dev_blkno* (long) ext2fs.blocksize;
+#ifdef DEBUG_EXT2
+ printf("ext2_bread: reading %ld bytes at offset %ld\n",
+ nbytes, offset);
+#endif
+ if (cons_read(dev, buffer, nbytes, offset)
+ != nbytes)
+ {
+ printf("ext2_bread: read error\n");
+ return -1;
+ }
+ }
+ buffer += nbytes;
+ tot_bytes += nbytes;
+ }
+ return tot_bytes;
+}
+
+static struct ext2_dir_entry_2 *ext2_readdiri(struct ext2_inode *dir_inode,
+ int rewind)
+{
+ struct ext2_dir_entry_2 *dp;
+ static int diroffset = 0, blockoffset = 0;
+
+ /* Reading a different directory, invalidate previous state */
+ if (rewind) {
+ diroffset = 0;
+ blockoffset = 0;
+ /* read first block */
+ if (ext2_breadi(dir_inode, 0, 1, blkbuf) < 0)
+ return NULL;
+ }
+
+#ifdef DEBUG_EXT2
+ printf("ext2_readdiri: blkoffset %d diroffset %d len %d\n",
+ blockoffset, diroffset, dir_inode->i_size);
+#endif
+ if (blockoffset >= ext2fs.blocksize) {
+ diroffset += ext2fs.blocksize;
+ if (diroffset >= dir_inode->i_size)
+ return NULL;
+#ifdef DEBUG_EXT2
+ printf("ext2_readdiri: reading block at %d\n",
+ diroffset);
+#endif
+ /* assume that this will read the whole block */
+ if (ext2_breadi(dir_inode,
+ diroffset / ext2fs.blocksize,
+ 1, blkbuf) < 0)
+ return NULL;
+ blockoffset = 0;
+ }
+
+ dp = (struct ext2_dir_entry_2 *) (blkbuf + blockoffset);
+ blockoffset += dp->rec_len;
+#ifdef DEBUG_EXT2
+ printf("ext2_readdiri: returning %p = %.*s\n", dp, dp->name_len, dp->name);
+#endif
+ return dp;
+}
+
+static struct ext2_inode *ext2_namei(const char *name)
+{
+ char namebuf[256];
+ char *component;
+ struct ext2_inode *dir_inode;
+ struct ext2_dir_entry_2 *dp;
+ int next_ino;
+
+ /* squirrel away a copy of "namebuf" that we can modify: */
+ strcpy(namebuf, name);
+
+ /* start at the root: */
+ if (!root_inode)
+ root_inode = ext2_iget(EXT2_ROOT_INO);
+ dir_inode = root_inode;
+ if (!dir_inode)
+ return NULL;
+
+ component = strtok(namebuf, "/");
+ while (component) {
+ int component_length;
+ int rewind = 0;
+ /*
+ * Search for the specified component in the current
+ * directory inode.
+ */
+ next_ino = -1;
+ component_length = strlen(component);
+
+ /* rewind the first time through */
+ while ((dp = ext2_readdiri(dir_inode, !rewind++))) {
+ if ((dp->name_len == component_length) &&
+ (strncmp(component, dp->name,
+ component_length) == 0))
+ {
+ /* Found it! */
+#ifdef DEBUG_EXT2
+ printf("ext2_namei: found entry %s\n",
+ component);
+#endif
+ next_ino = dp->inode;
+ break;
+ }
+#ifdef DEBUG_EXT2
+ printf("ext2_namei: looping\n");
+#endif
+ }
+
+#ifdef DEBUG_EXT2
+ printf("ext2_namei: next_ino = %d\n", next_ino);
+#endif
+
+ /*
+ * At this point, we're done with this directory whether
+ * we've succeeded or failed...
+ */
+ if (dir_inode != root_inode)
+ ext2_iput(dir_inode);
+
+ /*
+ * If next_ino is negative, then we've failed (gone
+ * all the way through without finding anything)
+ */
+ if (next_ino < 0) {
+ return NULL;
+ }
+
+ /*
+ * Otherwise, we can get this inode and find the next
+ * component string...
+ */
+ dir_inode = ext2_iget(next_ino);
+ if (!dir_inode)
+ return NULL;
+
+ component = strtok(NULL, "/");
+ }
+
+ /*
+ * If we get here, then we got through all the components.
+ * Whatever we got must match up with the last one.
+ */
+ return dir_inode;
+}
+
+
+/*
+ * Read block number "blkno" from the specified file.
+ */
+static int ext2_bread(int fd, long blkno, long nblks, char *buffer)
+{
+ struct ext2_inode * ip;
+ ip = &inode_table[fd].inode;
+ return ext2_breadi(ip, blkno, nblks, buffer);
+}
+
+/*
+ * Note: don't mix any kind of file lookup or other I/O with this or
+ * you will lose horribly (as it reuses blkbuf)
+ */
+static const char * ext2_readdir(int fd, int rewind)
+{
+ struct ext2_inode * ip = &inode_table[fd].inode;
+ struct ext2_dir_entry_2 * ent;
+ if (!S_ISDIR(ip->i_mode)) {
+ printf("fd %d (inode %d) is not a directory (mode %x)\n",
+ fd, inode_table[fd].inumber, ip->i_mode);
+ return NULL;
+ }
+ ent = ext2_readdiri(ip, rewind);
+ if (ent) {
+ ent->name[ent->name_len] = '\0';
+ return ent->name;
+ } else {
+ return NULL;
+ }
+}
+
+static int ext2_fstat(int fd, struct stat* buf)
+{
+ struct ext2_inode * ip = &inode_table[fd].inode;
+
+ if (fd >= MAX_OPEN_FILES)
+ return -1;
+ memset(buf, 0, sizeof(struct stat));
+ /* fill in relevant fields */
+ buf->st_ino = inode_table[fd].inumber;
+ buf->st_mode = ip->i_mode;
+ buf->st_flags = ip->i_flags;
+ buf->st_nlink = ip->i_links_count;
+ buf->st_uid = ip->i_uid;
+ buf->st_gid = ip->i_gid;
+ buf->st_size = ip->i_size;
+ buf->st_blocks = ip->i_blocks;
+ buf->st_atime = ip->i_atime;
+ buf->st_mtime = ip->i_mtime;
+ buf->st_ctime = ip->i_ctime;
+
+ return 0; /* NOTHING CAN GO WROGN! */
+}
+
+static struct ext2_inode * ext2_follow_link(struct ext2_inode * from,
+ const char * base)
+{
+ char *linkto;
+
+ if (from->i_blocks) {
+ linkto = blkbuf;
+ if (ext2_breadi(from, 0, 1, blkbuf) == -1)
+ return NULL;
+#ifdef DEBUG_EXT2
+ printf("long link!\n");
+#endif
+ } else {
+ linkto = (char*)from->i_block;
+ }
+#ifdef DEBUG_EXT2
+ printf("symlink to %s\n", linkto);
+#endif
+
+ /* Resolve relative links */
+ if (linkto[0] != '/') {
+ char *end = strrchr(base, '/');
+ if (end) {
+ char fullname[(end - base + 1) + strlen(linkto) + 1];
+ strncpy(fullname, base, end - base + 1);
+ fullname[end - base + 1] = '\0';
+ strcat(fullname, linkto);
+#ifdef DEBUG_EXT2
+ printf("resolved to %s\n", fullname);
+#endif
+ return ext2_namei(fullname);
+ } else {
+ /* Assume it's in the root */
+ return ext2_namei(linkto);
+ }
+ } else {
+ return ext2_namei(linkto);
+ }
+}
+
+static int ext2_open(const char *filename)
+{
+ /*
+ * Unix-like open routine. Returns a small integer (actually
+ * an index into the inode table...
+ */
+ struct ext2_inode * ip;
+
+ ip = ext2_namei(filename);
+ if (ip) {
+ struct inode_table_entry *itp;
+
+ while (S_ISLNK(ip->i_mode)) {
+ ip = ext2_follow_link(ip, filename);
+ if (!ip) return -1;
+ }
+ itp = (struct inode_table_entry *)ip;
+ return itp - inode_table;
+ } else
+ return -1;
+}
+
+
+static void ext2_close(int fd)
+{
+ /* blah, hack, don't close the root inode ever */
+ if (&inode_table[fd].inode != root_inode)
+ ext2_iput(&inode_table[fd].inode);
+}
+
+
+struct bootfs ext2fs = {
+ FS_EXT2, 0,
+ ext2_mount,
+ ext2_open, ext2_bread, ext2_close,
+ ext2_readdir, ext2_fstat
+};
diff --git a/package/aboot/src/fs/iso.c b/package/aboot/src/fs/iso.c
new file mode 100644
index 000000000..764993c05
--- /dev/null
+++ b/package/aboot/src/fs/iso.c
@@ -0,0 +1,59 @@
+/*
+ * This code is based on the ISO filesystem support in MILO (by
+ * Dave Rusling).
+ *
+ * This is a set of functions that provides minimal filesystem
+ * functionality to the Linux bootstrapper. All we can do is
+ * open and read files... but that's all we need 8-)
+ */
+#include <stddef.h>
+#include <cons.h>
+#include <bootfs.h>
+#include <isolib.h>
+
+#ifdef DEBUG_ISO
+#include <utils.h>
+#endif
+
+extern const struct bootfs isofs;
+
+static long cd_device = -1;
+
+
+long
+iso_dev_read (void * buf, long offset, long size)
+{
+ return cons_read(cd_device, buf, size, offset);
+}
+
+
+static int
+iso_mount (long cons_dev, long p_offset, long quiet)
+{
+#ifdef DEBUG_ISO
+ printf("iso_mount() called\n");
+#endif
+ cd_device = cons_dev;
+ /*
+ * Read the super block (this determines the file system type
+ * and other important information)
+ */
+ return iso_read_super(NULL, quiet);
+}
+
+static const char *
+iso_readdir(int fd, int rewind)
+{
+ return iso_readdir_i(fd,rewind);
+}
+
+const struct bootfs iso = {
+ -1, /* isofs is not partitioned */
+ 1024, /* block size */
+ iso_mount,
+ iso_open,
+ iso_bread,
+ iso_close,
+ iso_readdir,
+ iso_fstat
+};
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
+};