diff options
author | H. Peter Anvin <hpa@zytor.com> | 2010-02-16 10:35:34 -0800 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2010-02-16 10:35:34 -0800 |
commit | 51933ae32e742a5a81b4b880c1e96a6faa1b3380 (patch) | |
tree | b36c783c3fcec7577d038f764dd234cb825a930e /core/fs/fs.c | |
parent | a531c952eb0f377878f5bc46ab930bcbbcbdf9e3 (diff) | |
download | syslinux-51933ae32e742a5a81b4b880c1e96a6faa1b3380.tar.gz |
core: move fs-related C files into fs/
Move filesystem-related C files into the fs/ directory.
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Diffstat (limited to 'core/fs/fs.c')
-rw-r--r-- | core/fs/fs.c | 366 |
1 files changed, 366 insertions, 0 deletions
diff --git a/core/fs/fs.c b/core/fs/fs.c new file mode 100644 index 00000000..d67a87ca --- /dev/null +++ b/core/fs/fs.c @@ -0,0 +1,366 @@ +#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; +} + +/* + * 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); +} + +/* + * XXX: current unmangle_name() is stpcpy() on all filesystems; consider + * eliminating it as a method. + */ +void pm_unmangle_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]); + + dst = unmangle_name(dst, src); + + /* Update the di register to point to the last null char */ + regs->edi.w[0] = OFFS_WRT(dst, regs->es); +} + +char *unmangle_name(char *dst, const char *src) +{ + return this_fs->fs_ops->unmangle_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 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)->file_len; + 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->open_file) + 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 { + while (*p == '/') + p++; + + if (!*p) + break; + + part = p; + while ((echar = *p) && echar != '/') + p++; + *p++ = '\0'; + + if (part[0] != '.' || part[1] != '\0') { + inode = this_fs->fs_ops->iget(part, parent); + if (!inode) + goto err; + if (inode->mode == I_SYMLINK) { + char *linkbuf, *q; + int name_len = echar ? strlen(p) : 0; + int total_len = inode->size + name_len + (echar ? 2 : 1); + 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; + } + + put_inode(parent); + parent = NULL; + + if (!echar) + break; + + if (inode->mode != I_DIR) + goto err; + + parent = inode; + inode = NULL; + } + } while (echar); + } while (0); + + free(pathbuf); + put_inode(parent); + parent = NULL; + + if (!inode) + goto err; + + file->inode = inode; + file->offset = 0; + file->file_len = inode->size; + + return file_to_handle(file); + +err: + if (inode) + put_inode(inode); + if (parent) + put_inode(parent); + if (pathbuf) + free(pathbuf); + _close_file(file); +err_no_close: + return -1; +} + +void close_file(com32sys_t *regs) +{ + uint16_t handle = regs->esi.w[0]; + struct file *file; + + if (handle) { + file = handle_to_file(handle); + _close_file(file); + } +} + +/* + * 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. + * + */ +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]; + 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); + 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); + } +} |