diff options
author | Sebastian Schmelzer | 2010-10-25 16:53:54 +0200 |
---|---|---|
committer | Sebastian Schmelzer | 2010-10-25 16:53:54 +0200 |
commit | 3050a9253437f4a4b5ad4bf3b3efdc3c660a5137 (patch) | |
tree | 91ac22153e416aac7ca20916b314b5e2ffa871b1 /contrib/syslinux-4.02/core/fs/fs.c | |
download | preboot-3050a9253437f4a4b5ad4bf3b3efdc3c660a5137.tar.gz preboot-3050a9253437f4a4b5ad4bf3b3efdc3c660a5137.tar.xz preboot-3050a9253437f4a4b5ad4bf3b3efdc3c660a5137.zip |
Diffstat (limited to 'contrib/syslinux-4.02/core/fs/fs.c')
-rw-r--r-- | contrib/syslinux-4.02/core/fs/fs.c | 475 |
1 files changed, 475 insertions, 0 deletions
diff --git a/contrib/syslinux-4.02/core/fs/fs.c b/contrib/syslinux-4.02/core/fs/fs.c new file mode 100644 index 0000000..48856c9 --- /dev/null +++ b/contrib/syslinux-4.02/core/fs/fs.c @@ -0,0 +1,475 @@ +#include <stdio.h> +#include <stdbool.h> +#include <string.h> +#include <dprintf.h> +#include "fs.h" +#include "cache.h" + +/* The currently mounted filesystem */ +struct fs_info *this_fs = NULL; /* Root filesystem */ + +/* Actual file structures (we don't have malloc yet...) */ +struct file files[MAX_OPEN]; + +/* Symlink hard limits */ +#define MAX_SYMLINK_CNT 20 +#define MAX_SYMLINK_BUF 4096 + +/* + * Get a new inode structure + */ +struct inode *alloc_inode(struct fs_info *fs, uint32_t ino, size_t data) +{ + struct inode *inode = zalloc(sizeof(struct inode) + data); + if (inode) { + inode->fs = fs; + inode->ino = ino; + inode->refcnt = 1; + } + return inode; +} + +/* + * Free a refcounted inode + */ +void put_inode(struct inode *inode) +{ + while (inode && --inode->refcnt == 0) { + struct inode *dead = inode; + inode = inode->parent; + free(dead); + } +} + +/* + * Get an empty file structure + */ +static struct file *alloc_file(void) +{ + int i; + struct file *file = files; + + for (i = 0; i < MAX_OPEN; i++) { + if (!file->fs) + return file; + file++; + } + + return NULL; +} + +/* + * Close and free a file structure + */ +static inline void free_file(struct file *file) +{ + memset(file, 0, sizeof *file); +} + +void _close_file(struct file *file) +{ + if (file->fs) + file->fs->fs_ops->close_file(file); + free_file(file); +} + +/* + * Convert between a 16-bit file handle and a file structure + */ + +void load_config(void) +{ + int err; + + err = this_fs->fs_ops->load_config(); + + if (err) + printf("ERROR: No configuration file found\n"); +} + +void pm_mangle_name(com32sys_t *regs) +{ + const char *src = MK_PTR(regs->ds, regs->esi.w[0]); + char *dst = MK_PTR(regs->es, regs->edi.w[0]); + + mangle_name(dst, src); +} + +void mangle_name(char *dst, const char *src) +{ + this_fs->fs_ops->mangle_name(dst, src); +} + +void getfssec(com32sys_t *regs) +{ + int sectors; + bool have_more; + uint32_t bytes_read; + char *buf; + struct file *file; + uint16_t handle; + + sectors = regs->ecx.w[0]; + + handle = regs->esi.w[0]; + file = handle_to_file(handle); + + buf = MK_PTR(regs->es, regs->ebx.w[0]); + bytes_read = file->fs->fs_ops->getfssec(file, buf, sectors, &have_more); + + /* + * If we reach EOF, the filesystem driver will have already closed + * the underlying file... this really should be cleaner. + */ + if (!have_more) { + _close_file(file); + regs->esi.w[0] = 0; + } + + regs->ecx.l = bytes_read; +} + +void getfsbytes(com32sys_t *regs) +{ + int sectors; + bool have_more; + uint32_t bytes_read; + char *buf; + struct file *file; + uint16_t handle; + + handle = regs->esi.w[0]; + file = handle_to_file(handle); + + sectors = regs->ecx.w[0] >> SECTOR_SHIFT(file->fs); + + buf = MK_PTR(regs->es, regs->ebx.w[0]); + bytes_read = file->fs->fs_ops->getfssec(file, buf, sectors, &have_more); + + /* + * If we reach EOF, the filesystem driver will have already closed + * the underlying file... this really should be cleaner. + */ + if (!have_more) { + _close_file(file); + regs->esi.w[0] = 0; + } + + regs->ecx.l = bytes_read; +} + +size_t pmapi_read_file(uint16_t *handle, void *buf, size_t sectors) +{ + bool have_more; + size_t bytes_read; + struct file *file; + + file = handle_to_file(*handle); + bytes_read = file->fs->fs_ops->getfssec(file, buf, sectors, &have_more); + + /* + * If we reach EOF, the filesystem driver will have already closed + * the underlying file... this really should be cleaner. + */ + if (!have_more) { + _close_file(file); + *handle = 0; + } + + return bytes_read; +} + +void pm_searchdir(com32sys_t *regs) +{ + char *name = MK_PTR(regs->ds, regs->edi.w[0]); + int rv; + + rv = searchdir(name); + if (rv < 0) { + regs->esi.w[0] = 0; + regs->eax.l = 0; + regs->eflags.l |= EFLAGS_ZF; + } else { + regs->esi.w[0] = rv; + regs->eax.l = handle_to_file(rv)->inode->size; + regs->eflags.l &= ~EFLAGS_ZF; + } +} + +int searchdir(const char *name) +{ + struct inode *inode = NULL; + struct inode *parent = NULL; + struct file *file; + char *pathbuf = NULL; + char *part, *p, echar; + int symlink_count = MAX_SYMLINK_CNT; + + if (!(file = alloc_file())) + goto err_no_close; + file->fs = this_fs; + + /* if we have ->searchdir method, call it */ + if (file->fs->fs_ops->searchdir) { + file->fs->fs_ops->searchdir(name, file); + + if (file->inode) + return file_to_handle(file); + else + goto err; + } + + /* else, try the generic-path-lookup method */ + + parent = get_inode(this_fs->cwd); + p = pathbuf = strdup(name); + if (!pathbuf) + goto err; + + do { + got_link: + if (*p == '/') { + put_inode(parent); + parent = get_inode(this_fs->root); + } + + do { + inode = get_inode(parent); + + while (*p == '/') + p++; + + if (!*p) + break; + + part = p; + while ((echar = *p) && echar != '/') + p++; + *p++ = '\0'; + + if (part[0] == '.' && part[1] == '.' && part[2] == '\0') { + if (inode->parent) { + put_inode(parent); + parent = get_inode(inode->parent); + put_inode(inode); + inode = NULL; + if (!echar) { + /* Terminal double dots */ + inode = parent; + parent = inode->parent ? + get_inode(inode->parent) : NULL; + } + } + } else if (part[0] != '.' || part[1] != '\0') { + inode = this_fs->fs_ops->iget(part, parent); + if (!inode) + goto err; + if (inode->mode == DT_LNK) { + char *linkbuf, *q; + int name_len = echar ? strlen(p) : 0; + int total_len = inode->size + name_len + 2; + int link_len; + + if (!this_fs->fs_ops->readlink || + --symlink_count == 0 || /* limit check */ + total_len > MAX_SYMLINK_BUF) + goto err; + + linkbuf = malloc(total_len); + if (!linkbuf) + goto err; + + link_len = this_fs->fs_ops->readlink(inode, linkbuf); + if (link_len <= 0) { + free(linkbuf); + goto err; + } + + q = linkbuf + link_len; + + if (echar) { + if (link_len > 0 && q[-1] != '/') + *q++ = '/'; + + memcpy(q, p, name_len+1); + } else { + *q = '\0'; + } + + free(pathbuf); + p = pathbuf = linkbuf; + put_inode(inode); + inode = NULL; + goto got_link; + } + + inode->parent = parent; + parent = NULL; + + if (!echar) + break; + + if (inode->mode != DT_DIR) + goto err; + + parent = inode; + inode = NULL; + } + } while (echar); + } while (0); + + free(pathbuf); + pathbuf = NULL; + put_inode(parent); + parent = NULL; + + if (!inode) + goto err; + + file->inode = inode; + file->offset = 0; + + return file_to_handle(file); + +err: + put_inode(inode); + put_inode(parent); + if (pathbuf) + free(pathbuf); + _close_file(file); +err_no_close: + return -1; +} + +int open_file(const char *name, struct com32_filedata *filedata) +{ + int rv; + struct file *file; + char mangled_name[FILENAME_MAX]; + + mangle_name(mangled_name, name); + rv = searchdir(mangled_name); + + if (rv < 0) + return rv; + + file = handle_to_file(rv); + + if (file->inode->mode != DT_REG) { + _close_file(file); + return -1; + } + + filedata->size = file->inode->size; + filedata->blocklg2 = SECTOR_SHIFT(file->fs); + filedata->handle = rv; + + return rv; +} + +void pm_open_file(com32sys_t *regs) +{ + int rv; + struct file *file; + const char *name = MK_PTR(regs->es, regs->esi.w[0]); + char mangled_name[FILENAME_MAX]; + + mangle_name(mangled_name, name); + rv = searchdir(mangled_name); + if (rv < 0) { + regs->eflags.l |= EFLAGS_CF; + } else { + file = handle_to_file(rv); + regs->eflags.l &= ~EFLAGS_CF; + regs->eax.l = file->inode->size; + regs->ecx.w[0] = SECTOR_SIZE(file->fs); + regs->esi.w[0] = rv; + } +} + +void close_file(uint16_t handle) +{ + struct file *file; + + if (handle) { + file = handle_to_file(handle); + _close_file(file); + } +} + +void pm_close_file(com32sys_t *regs) +{ + close_file(regs->esi.w[0]); +} + +/* + * it will do: + * initialize the memory management function; + * set up the vfs fs structure; + * initialize the device structure; + * invoke the fs-specific init function; + * initialize the cache if we need one; + * finally, get the current inode for relative path looking. + */ +__bss16 uint16_t SectorSize, SectorShift; + +void fs_init(com32sys_t *regs) +{ + static struct fs_info fs; /* The actual filesystem buffer */ + uint8_t disk_devno = regs->edx.b[0]; + uint8_t disk_cdrom = regs->edx.b[1]; + sector_t disk_offset = regs->ecx.l | ((sector_t)regs->ebx.l << 32); + uint16_t disk_heads = regs->esi.w[0]; + uint16_t disk_sectors = regs->edi.w[0]; + uint32_t maxtransfer = regs->ebp.l; + int blk_shift = -1; + struct device *dev = NULL; + /* ops is a ptr list for several fs_ops */ + const struct fs_ops **ops = (const struct fs_ops **)regs->eax.l; + + /* Initialize malloc() */ + mem_init(); + + /* Default name for the root directory */ + fs.cwd_name[0] = '/'; + + while ((blk_shift < 0) && *ops) { + /* set up the fs stucture */ + fs.fs_ops = *ops; + + /* + * This boldly assumes that we don't mix FS_NODEV filesystems + * with FS_DEV filesystems... + */ + if (fs.fs_ops->fs_flags & FS_NODEV) { + fs.fs_dev = NULL; + } else { + if (!dev) + dev = device_init(disk_devno, disk_cdrom, disk_offset, + disk_heads, disk_sectors, maxtransfer); + fs.fs_dev = dev; + } + /* invoke the fs-specific init code */ + blk_shift = fs.fs_ops->fs_init(&fs); + ops++; + } + if (blk_shift < 0) { + printf("No valid file system found!\n"); + while (1) + ; + } + this_fs = &fs; + + /* initialize the cache */ + if (fs.fs_dev && fs.fs_dev->cache_data) + cache_init(fs.fs_dev, blk_shift); + + /* start out in the root directory */ + if (fs.fs_ops->iget_root) { + fs.root = fs.fs_ops->iget_root(&fs); + fs.cwd = get_inode(fs.root); + } + + SectorShift = fs.sector_shift; + SectorSize = fs.sector_size; +} |