summaryrefslogtreecommitdiff
path: root/core/fs/fs.c
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2010-02-16 10:35:34 -0800
committerH. Peter Anvin <hpa@zytor.com>2010-02-16 10:35:34 -0800
commit51933ae32e742a5a81b4b880c1e96a6faa1b3380 (patch)
treeb36c783c3fcec7577d038f764dd234cb825a930e /core/fs/fs.c
parenta531c952eb0f377878f5bc46ab930bcbbcbdf9e3 (diff)
downloadsyslinux-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.c366
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);
+ }
+}