diff options
author | Waldemar Brodkorb <wbx@openadk.org> | 2017-12-31 18:47:16 +0100 |
---|---|---|
committer | Waldemar Brodkorb <wbx@openadk.org> | 2017-12-31 18:47:25 +0100 |
commit | 3a96085b999220c4da0c5ef7d1f7ba26b9ddfb98 (patch) | |
tree | 77f1445aae2e6be5135594e95986b3278bbc061c /package/aboot/src/fs | |
parent | cc28479164b8dc8afd4310716da32f16022f5974 (diff) |
dec-multia: make netboot possible, add aboot bootloader
Diffstat (limited to 'package/aboot/src/fs')
-rw-r--r-- | package/aboot/src/fs/dummy.c | 80 | ||||
-rw-r--r-- | package/aboot/src/fs/ext2.c | 623 | ||||
-rw-r--r-- | package/aboot/src/fs/iso.c | 59 | ||||
-rw-r--r-- | package/aboot/src/fs/ufs.c | 485 |
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 +}; |