X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?p=bootcensus;a=blobdiff_plain;f=src%2Ffsmem.c;fp=src%2Ffsmem.c;h=e4cbdd1ac274f17753043c02b00f95039c5234eb;hp=0000000000000000000000000000000000000000;hb=78e3e75fc7b5838d0261c876d00e1b8c3e0bcfe0;hpb=7b6f6de2124e28ae7da5599a7cdaf2c171c4f15e diff --git a/src/fsmem.c b/src/fsmem.c new file mode 100644 index 0000000..e4cbdd1 --- /dev/null +++ b/src/fsmem.c @@ -0,0 +1,520 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2019 John Tsiombikas + +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 . +*/ +#include +#include +#include +#include +#include +#include +#include "fs.h" +#include "panic.h" + +#define MAX_NAME 120 + +struct memfs_node; +struct memfs_file; +struct memfs_dir; + +struct memfs { + struct memfs_node *rootdir; +}; + +struct memfs_dir { + struct memfs_node *clist, *ctail; + struct memfs_node *cur; +}; + +struct odir { + struct memfs_dir *dir; + struct memfs_node *cur; + struct fs_dirent dent; +}; + +struct memfs_file { + char *data; + long size, max_size; +}; + +struct ofile { + struct memfs_file *file; + long cur_pos; +}; + +struct memfs_node { + union { + struct memfs_file file; + struct memfs_dir dir; + }; + int type; + char name[MAX_NAME + 4]; + struct memfs_node *parent; + struct memfs_node *next; + struct fs_node *fsnode; /* we need it for crossing mounts in fs_open */ +}; + + +static void destroy(struct filesys *fs); + +static struct fs_node *open(struct filesys *fs, const char *path, unsigned int flags); +static void close(struct fs_node *node); +static long fsize(struct fs_node *node); +static int seek(struct fs_node *node, int offs, int whence); +static long tell(struct fs_node *node); +static int read(struct fs_node *node, void *buf, int sz); +static int write(struct fs_node *node, void *buf, int sz); +static int rewinddir(struct fs_node *node); +static struct fs_dirent *readdir(struct fs_node *node); +static int rename(struct fs_node *node, const char *name); +static int remove(struct fs_node *node); + +static struct fs_node *create_fsnode(struct filesys *fs, struct memfs_node *n); + +static struct memfs_node *alloc_node(int type); +static void free_node(struct memfs_node *node); + +static struct memfs_node *find_entry(struct memfs_node *dir, const char *name); +static void add_child(struct memfs_node *dir, struct memfs_node *n); + + +static struct fs_operations fs_mem_ops = { + destroy, + open, close, + + fsize, + seek, tell, + read, write, + + rewinddir, readdir, + + rename, remove +}; + + +struct filesys *fsmem_create(int dev, uint64_t start, uint64_t size) +{ + struct filesys *fs; + struct memfs *memfs; + + if(dev != DEV_MEMDISK) { + return 0; + } + + if(!(memfs = malloc(sizeof *memfs))) { + panic("MEMFS: create failed to allocate memory\n"); + } + if(!(memfs->rootdir = alloc_node(FSNODE_DIR))) { + panic("MEMFS: failed to allocate root dir\n"); + } + + if(!(fs = malloc(sizeof *fs))) { + panic("MEMFS: failed to allocate memory for the filesystem structure\n"); + } + fs->type = FSTYPE_MEM; + fs->name = 0; + fs->fsop = &fs_mem_ops; + fs->data = memfs; + + return fs; +} + +static void destroy(struct filesys *fs) +{ + struct memfs *memfs = fs->data; + free_node((struct memfs_node*)memfs->rootdir); + free(memfs); + free(fs); +} + +static struct fs_node *open_mount(struct filesys *fs, const char *path, unsigned int flags) +{ + char *newpath; + + newpath = alloca(strlen(path) + 2); + newpath[0] = '/'; + strcpy(newpath + 1, path); + + return fs->fsop->open(fs, newpath, flags); +} + +#define NODE_IS_MNTPT(n) ((n)->fsnode && (n)->fsnode->mnt) + +static struct fs_node *open(struct filesys *fs, const char *path, unsigned int flags) +{ + struct memfs_node *node, *parent; + struct memfs *memfs = fs->data; + char name[MAX_NAME + 1]; + + if(path[0] == '/') { + node = memfs->rootdir; + path = fs_path_skipsep((char*)path); + } else { + if(cwdnode->fs->type != FSTYPE_MEM) { + return 0; + } + node = (struct memfs_node*)((struct odir*)cwdnode->data)->dir; + } + assert(node->type == FSNODE_DIR); + + while(*path) { + if(node->type != FSNODE_DIR) { + /* we have more path components, yet the last one wasn't a dir */ + errno = ENOTDIR; + return 0; + } + /* check if it's another filesystem hanging off this directory, and if + * so, recursively call that open function to complete the operation + */ + if(NODE_IS_MNTPT(node)) { + return open_mount(node->fsnode->mnt, path, flags); + } + + path = fs_path_next((char*)path, name, sizeof name); + parent = node; + + if(!(node = find_entry(node, name))) { + if(*path || !(flags & FSO_CREATE)) { + errno = ENOENT; + return 0; + } + /* create and add */ + if(!(node = alloc_node((flags & FSO_DIR) ? FSNODE_DIR : FSNODE_FILE))) { + errno = ENOMEM; + return 0; + } + strcpy(node->name, name); + add_child(parent, node); + return create_fsnode(fs, node); + } + } + + /* we need to check for mount points here too, because the check in the loop + * above is not going to be reached when the mount point is the last part of + * the path string (for instance opendir("/mnt/foo")) + */ + if(NODE_IS_MNTPT(node)) { + return open_mount(node->fsnode->mnt, path, flags); + } + + if(flags & FSO_EXCL) { + errno = EEXIST; + return 0; + } + return create_fsnode(fs, node); +} + +static struct fs_node *create_fsnode(struct filesys *fs, struct memfs_node *n) +{ + struct fs_node *fsn; + struct ofile *of; + struct odir *od; + + if(!(fsn = calloc(1, sizeof *fsn))) { + errno = ENOMEM; + return 0; + } + + if(n->type == FSNODE_FILE) { + if(!(of = malloc(sizeof *of))) { + errno = ENOMEM; + free(fsn); + return 0; + } + of->file = &n->file; + of->cur_pos = 0; + fsn->data = of; + } else { + if(!(od = malloc(sizeof *od))) { + errno = ENOMEM; + free(fsn); + return 0; + } + od->dir = &n->dir; + od->cur = n->dir.clist; + fsn->data = od; + } + + if(!n->fsnode) { + n->fsnode = fsn; + } + + fsn->fs = fs; + fsn->type = n->type; + return fsn; +} + +static void close(struct fs_node *node) +{ + if(!node) return; + + free(node->data); /* free the copy of memfs_node allocated by create_fsnode */ + free(node); +} + +static long fsize(struct fs_node *node) +{ + struct ofile *of; + if(!node || node->type != FSNODE_FILE) { + return -1; + } + of = node->data; + return of->file->size; +} + +static int seek(struct fs_node *node, int offs, int whence) +{ + struct ofile *of; + long new_pos; + + if(!node || node->type != FSNODE_FILE) { + return -1; + } + + of = node->data; + + switch(whence) { + case FSSEEK_SET: + new_pos = offs; + break; + + case FSSEEK_CUR: + new_pos = of->cur_pos + offs; + break; + + case FSSEEK_END: + new_pos = of->file->size + offs; + break; + + default: + return -1; + } + + if(new_pos < 0) new_pos = 0; + + of->cur_pos = new_pos; + return 0; +} + +static long tell(struct fs_node *node) +{ + struct ofile *of; + + if(!node || node->type != FSNODE_FILE) { + return -1; + } + of = node->data; + return of->cur_pos; +} + +static int read(struct fs_node *node, void *buf, int sz) +{ + struct ofile *of; + + if(!node || !buf || sz < 0 || node->type != FSNODE_FILE) { + return -1; + } + + of = node->data; + + if(sz > of->file->size - of->cur_pos) { + sz = of->file->size - of->cur_pos; + } + memcpy(buf, of->file->data + of->cur_pos, sz); + of->cur_pos += sz; + return sz; +} + +static int write(struct fs_node *node, void *buf, int sz) +{ + struct ofile *of; + int total_sz, new_max_sz; + void *tmp; + + if(!node || !buf || sz < 0 || node->type != FSNODE_FILE) { + return -1; + } + + of = node->data; + total_sz = of->cur_pos + sz; + if(total_sz > of->file->max_size) { + if(total_sz < of->file->max_size * 2) { + new_max_sz = of->file->max_size * 2; + } else { + new_max_sz = total_sz; + } + if(!(tmp = realloc(of->file->data, new_max_sz))) { + errno = ENOSPC; + return -1; + } + of->file->data = tmp; + of->file->max_size = new_max_sz; + } + + memcpy(of->file->data + of->cur_pos, buf, sz); + of->cur_pos += sz; + if(of->cur_pos > of->file->size) of->file->size = of->cur_pos; + return sz; +} + +static int rewinddir(struct fs_node *node) +{ + struct odir *od; + + if(!node || node->type != FSNODE_DIR) { + return -1; + } + + od = node->data; + od->cur = od->dir->clist; + return 0; +} + +static struct fs_dirent *readdir(struct fs_node *node) +{ + struct odir *od; + struct memfs_node *n; + struct fs_dirent *fsd; + + if(!node || node->type != FSNODE_DIR) { + return 0; + } + + od = node->data; + fsd = &od->dent; + + n = od->cur; + if(!n) return 0; + + od->cur = od->cur->next; + + fsd->name = n->name; + fsd->data = 0; + fsd->type = n->type; + fsd->fsize = n->file.size; + + return fsd; +} + +static int rename(struct fs_node *node, const char *name) +{ + struct memfs_node *n = (struct memfs_node*)((struct odir*)node->data)->dir; + strncpy(n->name, name, MAX_NAME); + n->name[MAX_NAME] = 0; + return 0; +} + +static int remove(struct fs_node *node) +{ + int res = -1; + struct odir *od = 0; + struct ofile *of = 0; + struct memfs_node *n, *par, *prev, dummy; + + if(node->type == FSNODE_DIR) { + od = node->data; + n = (struct memfs_node*)od->dir; + + if(n->dir.clist) { + errno = EEXIST; + return -1; + } + } else { + of = node->data; + n = (struct memfs_node*)of->file; + } + par = n->parent; + + if(!par) { + errno = EBUSY; + return -1; + } + + dummy.next = par->dir.clist; + prev = &dummy; + while(prev->next) { + if(prev->next == n) { + if(par->dir.ctail == n) { + par->dir.ctail = prev; + } + prev->next = n->next; + free_node(n); + res = 0; + break; + } + prev = prev->next; + } + par->dir.clist = dummy.next; + return res; +} + +static struct memfs_node *alloc_node(int type) +{ + struct memfs_node *node; + + if(!(node = calloc(1, sizeof *node))) { + return 0; + } + node->type = type; + return node; +} + +static void free_node(struct memfs_node *node) +{ + if(!node) return; + + switch(node->type) { + case FSNODE_FILE: + free(node->file.data); + break; + + case FSNODE_DIR: + while(node->dir.clist) { + struct memfs_node *n = node->dir.clist; + node->dir.clist = n->next; + free_node(n); + } + break; + } +} + +static struct memfs_node *find_entry(struct memfs_node *dnode, const char *name) +{ + struct memfs_node *n; + + if(strcmp(name, ".") == 0) return dnode; + if(strcmp(name, "..") == 0) return dnode->parent; + + n = dnode->dir.clist; + while(n) { + if(strcasecmp(n->name, name) == 0) { + return n; + } + n = n->next; + } + return 0; +} + +static void add_child(struct memfs_node *dnode, struct memfs_node *n) +{ + if(dnode->dir.clist) { + dnode->dir.ctail->next = n; + dnode->dir.ctail = n; + } else { + dnode->dir.clist = dnode->dir.ctail = n; + } + n->parent = dnode; +}