backported more fixes from 256boss
[bootcensus] / src / fs.c
diff --git a/src/fs.c b/src/fs.c
new file mode 100644 (file)
index 0000000..2e9cbaf
--- /dev/null
+++ b/src/fs.c
@@ -0,0 +1,278 @@
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2019  John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+*/
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <alloca.h>
+#include "fs.h"
+#include "mtab.h"
+#include "panic.h"
+
+struct filesys *fsfat_create(int dev, uint64_t start, uint64_t size);
+struct filesys *fsmem_create(int dev, uint64_t start, uint64_t size);
+
+static struct filesys *(*createfs[])(int, uint64_t, uint64_t) = {
+       fsmem_create,
+       fsfat_create
+};
+
+struct filesys *fs_mount(int dev, uint64_t start, uint64_t size, struct fs_node *parent)
+{
+       int i;
+       struct filesys *fs;
+
+       if(!parent && rootfs) {
+               printf("fs_mount error: root filesystem already mounted!\n");
+               return 0;
+       }
+
+       for(i=0; i<NUM_FSTYPES; i++) {
+               if((fs = createfs[i](dev, start, size))) {
+                       if(!parent) {
+                               rootfs = fs;
+
+                               fs_chdir("/");
+                       } else {
+                               parent->mnt = fs;
+                               mtab_add(parent, fs);
+                       }
+                       return fs;
+               }
+       }
+
+       printf("failed to mount filesystem dev: %d, start %llu\n", dev, (unsigned long long)start);
+       return 0;
+}
+
+static char cwdpath[1024];
+static char *cwdpath_end = cwdpath;
+
+int fs_chdir(const char *path)
+{
+       struct fs_node *node;
+       char *uppath = 0;
+       int uplen;
+
+       if(!path || !*path) {
+               return -1;
+       }
+
+       if(strcmp(path, ".") == 0) {
+               return 0;
+       }
+       if(strcmp(path, "..") == 0) {
+               char *endptr;
+
+               if(cwdpath_end <= cwdpath + 1) {
+                       return -1;
+               }
+
+               endptr = cwdpath + (cwdpath_end - cwdpath);
+               while(endptr > cwdpath && *--endptr != '/');
+               if(endptr == cwdpath) endptr++;
+
+               uplen = endptr - cwdpath;
+               uppath = alloca(uplen + 1);
+               memcpy(uppath, cwdpath, uplen);
+               uppath[uplen] = 0;
+
+               path = uppath;
+       }
+
+       if(!(node = fs_open(path, 0))) {
+               return -1;
+       }
+       if(node->type != FSNODE_DIR) {
+               fs_close(node);
+               return -1;
+       }
+
+       if(uppath) {
+               memcpy(cwdpath, uppath, uplen + 1);
+               cwdpath_end = cwdpath + uplen;
+
+       } else {
+               int len = strlen(path);
+               if(cwdpath_end - cwdpath + len > sizeof cwdpath) {
+                       panic("fs_chdir: path too long: %s\n", path);
+               }
+               if(path[0] == '/') {
+                       memcpy(cwdpath, path, len + 1);
+                       cwdpath_end = cwdpath + len;
+               } else {
+                       if(cwdpath_end > cwdpath + 1) {
+                               *cwdpath_end++ = '/';
+                       }
+                       memcpy(cwdpath_end, path, len + 1);
+                       cwdpath_end += len;
+               }
+       }
+
+       fs_close(cwdnode);
+       cwdnode = node;
+       return 0;
+}
+
+char *fs_getcwd(void)
+{
+       return cwdpath;
+}
+
+/* TODO normalize path */
+struct fs_node *fs_open(const char *path, unsigned int flags)
+{
+       struct filesys *fs;
+       struct fs_node *node;
+
+       if(!path || !*path) {
+               return 0;
+       }
+
+       if(*path == '/') {
+               fs = rootfs;
+       } else {
+               if(!cwdnode) return 0;
+               fs = cwdnode->fs;
+       }
+
+       if(!(node = fs->fsop->open(fs, path, flags))) {
+               return 0;
+       }
+       return node;
+}
+
+int fs_close(struct fs_node *node)
+{
+       struct fs_operations *fsop;
+
+       if(!node) return -1;
+
+       fsop = node->fs->fsop;
+       fsop->close(node);
+       return 0;
+}
+
+int fs_rename(struct fs_node *node, const char *name)
+{
+       struct fs_operations *fsop = node->fs->fsop;
+       return fsop->rename(node, name);
+}
+
+int fs_remove(struct fs_node *node)
+{
+       struct fs_operations *fsop = node->fs->fsop;
+       return fsop->remove(node);
+}
+
+long fs_filesize(struct fs_node *node)
+{
+       struct fs_operations *fsop = node->fs->fsop;
+
+       if(node->type != FSNODE_FILE) {
+               return -1;
+       }
+       return fsop->fsize(node);
+}
+
+int fs_seek(struct fs_node *node, int offs, int whence)
+{
+       struct fs_operations *fsop = node->fs->fsop;
+
+       if(node->type != FSNODE_FILE) {
+               return -1;
+       }
+       return fsop->seek(node, offs, whence);
+}
+
+long fs_tell(struct fs_node *node)
+{
+       struct fs_operations *fsop = node->fs->fsop;
+
+       if(node->type != FSNODE_FILE) {
+               return -1;
+       }
+       return fsop->tell(node);
+}
+
+int fs_read(struct fs_node *node, void *buf, int sz)
+{
+       struct fs_operations *fsop = node->fs->fsop;
+
+       if(node->type != FSNODE_FILE) {
+               return -1;
+       }
+       return fsop->read(node, buf, sz);
+}
+
+int fs_write(struct fs_node *node, void *buf, int sz)
+{
+       struct fs_operations *fsop = node->fs->fsop;
+
+       if(node->type != FSNODE_FILE) {
+               return -1;
+       }
+       return fsop->write(node, buf, sz);
+}
+
+int fs_rewinddir(struct fs_node *node)
+{
+       struct fs_operations *fsop = node->fs->fsop;
+
+       if(node->type != FSNODE_DIR) {
+               return -1;
+       }
+       return fsop->rewinddir(node);
+}
+
+struct fs_dirent *fs_readdir(struct fs_node *node)
+{
+       struct fs_operations *fsop = node->fs->fsop;
+
+       if(node->type != FSNODE_DIR) {
+               return 0;
+       }
+       return fsop->readdir(node);
+}
+
+/* fs utility functions */
+char *fs_path_skipsep(char *s)
+{
+       while(*s == '/') s++;
+       return s;
+}
+
+char *fs_path_next(char *s, char *namebuf, int bufsz)
+{
+       int len;
+       char *ptr;
+
+       ptr = s = fs_path_skipsep(s);
+
+       while(*ptr && *ptr != '/') ptr++;
+
+       if(namebuf) {
+               len = ptr - s;
+               if(len >= bufsz) len = bufsz - 1;
+
+               memcpy(namebuf, s, len);
+               namebuf[len] = 0;
+       }
+
+       return fs_path_skipsep(ptr);
+}