From 9a8c60ade639ab3f53bfa76c4945531ad4885477 Mon Sep 17 00:00:00 2001 From: sbosse Date: Mon, 14 Oct 2024 23:07:10 +0200 Subject: [PATCH] Mon 14 Oct 23:06:38 CEST 2024 --- kernel/fs.c | 501 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 501 insertions(+) create mode 100644 kernel/fs.c diff --git a/kernel/fs.c b/kernel/fs.c new file mode 100644 index 0000000..576bf26 --- /dev/null +++ b/kernel/fs.c @@ -0,0 +1,501 @@ +/* +Copyright (C) 2016-2019 The University of Notre Dame +This software is distributed under the GNU General Public License. +See the file LICENSE for details. +*/ + +#include "fs.h" +#include "fs_internal.h" +#include "kmalloc.h" +#include "string.h" +#include "page.h" +#include "process.h" +#include "bcache.h" + +static struct fs *fs_list = 0; + +static struct kobject * find_kobject_by_tag( const char *tag ) +{ + int i; + + // Check if tag is index-specified. + if(tag[0] == '#') { + str2int(&tag[1], &i); + return current->ktable[i]; + } else { + // Find an tag matching the tag. + int max = process_object_max(current); + for(i=0;iktable[i]; + if(k && !strcmp(k->tag,tag)) { + return k; + } + } + } + + return 0; +} + +struct fs_dirent * fs_getroot( struct process *p ) +{ + struct kobject *k = p->ktable[KNO_STDDIR]; + if( k && k->type==KOBJECT_DIR ) { + return k->data.dir; + } else { + return 0; + } +} + +struct fs_dirent * fs_getcurrent( struct process *p ) +{ + struct kobject *k = p->ktable[KNO_STDDIR]; + if( k && k->type==KOBJECT_DIR ) { + return k->data.dir; + } else { + return 0; + } +} + +struct fs_dirent *fs_resolve(const char *path) +{ + // If the path begins with a slash, navigate from the root directory. + if(path[0] == '/') { + return fs_dirent_traverse(fs_getroot(current), &path[1]); + } + + // If the path contains a colon, we are dealing with a tag. + const char *colon = strchr(path,':'); + if(colon) { + // Length of tag is distance from colon to beginning. + int length = colon - path; + + // Rest of path starts after the colon. + const char *rest = colon+1; + + // Make a temporary string with the tag. + char *tagstr = strdup(path); + tagstr[length] = 0; + + // Look up the object associated with that tag + struct kobject *tagobj = find_kobject_by_tag(tagstr); + kfree(tagstr); + if(!tagobj) return 0; + // XXX KERROR_NOT_FOUND; + + // Make sure it is really a directory. + if(kobject_get_type(tagobj)!=KOBJECT_DIR) return 0; + // XXX KERROR_NOT_A_DIRECTORY; + + // If there is no remaining path, just return that object. + if(!*rest) return fs_dirent_addref(tagobj->data.dir); + + // Otherwise, navigate from that object. + return fs_dirent_traverse(tagobj->data.dir,path); + } + + // If there was no tag, then navigate from the current working directory. + return fs_dirent_traverse(fs_getcurrent(current), path); +} + +void fs_register(struct fs *f) +{ + f->next = fs_list; + fs_list = f; +} + +struct fs *fs_lookup(const char *name) +{ + struct fs *f; + + for(f = fs_list; f; f = f->next) { + if(!strcmp(name, f->name)) { + return f; + } + } + return 0; +} + +int fs_volume_format(struct fs *f, struct device *d ) +{ + const struct fs_ops *ops = f->ops; + if(!ops->volume_format) + return KERROR_NOT_IMPLEMENTED; + return f->ops->volume_format(d); +} + +struct fs_volume *fs_volume_open(struct fs *f, struct device *d ) +{ + const struct fs_ops *ops = f->ops; + + if(!ops->volume_open) + return 0; + + struct fs_volume *v = f->ops->volume_open(d); + if(v) { + v->fs = f; + v->device = device_addref(d); + } + return v; +} + +struct fs_volume *fs_volume_addref(struct fs_volume *v) +{ + v->refcount++; + return v; +} + +int fs_volume_close(struct fs_volume *v) +{ + const struct fs_ops *ops = v->fs->ops; + if(!ops->volume_close) + return KERROR_NOT_IMPLEMENTED; + + v->refcount--; + if(v->refcount==0) { + v->fs->ops->volume_close(v); + bcache_flush_device(v->device); + device_close(v->device); + kfree(v); + } + + return 0; +} + +struct fs_dirent *fs_volume_root(struct fs_volume *v) +{ + const struct fs_ops *ops = v->fs->ops; + if(!ops->volume_root) + return 0; + + struct fs_dirent *d = v->fs->ops->volume_root(v); + d->volume = fs_volume_addref(v); + return d; +} + +int fs_dirent_list(struct fs_dirent *d, char *buffer, int buffer_length) +{ + const struct fs_ops *ops = d->volume->fs->ops; + if(!ops->list) + return KERROR_NOT_IMPLEMENTED; + return ops->list(d, buffer, buffer_length); +} + +static struct fs_dirent *fs_dirent_lookup(struct fs_dirent *d, const char *name) +{ + const struct fs_ops *ops = d->volume->fs->ops; + + if(!ops->lookup) + return 0; + + if(!strcmp(name,".")) { + // Special case: . refers to the containing directory. + return fs_dirent_addref(d); + } else { + struct fs_dirent *r = ops->lookup(d, name); + if(r) r->volume = fs_volume_addref(d->volume); + return r; + } +} + +struct fs_dirent *fs_dirent_traverse(struct fs_dirent *parent, const char *path) +{ + if(!parent || !path) + return 0; + + char *lpath = kmalloc(strlen(path) + 1); + strcpy(lpath, path); + + struct fs_dirent *d = parent; + + char *part = strtok(lpath, "/"); + while(part) { + struct fs_dirent *n = fs_dirent_lookup(d, part); + + if(d!=parent) fs_dirent_close(d); + + if(!n) { + // KERROR_NOT_FOUND + kfree(lpath); + return 0; + } + d = n; + part = strtok(0, "/"); + } + kfree(lpath); + return d; +} + +struct fs_dirent *fs_dirent_addref(struct fs_dirent *d) +{ + d->refcount++; + return d; +} + +int fs_dirent_close(struct fs_dirent *d) +{ + const struct fs_ops *ops = d->volume->fs->ops; + if(!ops->close) + return KERROR_NOT_IMPLEMENTED; + + d->refcount--; + if(d->refcount==0) { + ops->close(d); + // This close is paired with the addref in fs_dirent_lookup + fs_volume_close(d->volume); + kfree(d); + } + + return 0; +} + +int fs_dirent_read(struct fs_dirent *d, char *buffer, uint32_t length, uint32_t offset) +{ + int total = 0; + int bs = d->volume->block_size; + + const struct fs_ops *ops = d->volume->fs->ops; + if(!ops->read_block) + return KERROR_INVALID_REQUEST; + + if(offset > d->size) { + return 0; + } + + if(offset + length > d->size) { + length = d->size - offset; + } + + char *temp = page_alloc(0); + if(!temp) + return -1; + + while(length > 0) { + + int blocknum = offset / bs; + int actual = 0; + + if(offset % bs) { + actual = ops->read_block(d, temp, blocknum); + if(actual != bs) + goto failure; + actual = MIN(bs - offset % bs, length); + memcpy(buffer, &temp[offset % bs], actual); + } else if(length >= bs) { + actual = ops->read_block(d, buffer, blocknum); + if(actual != bs) + goto failure; + } else { + actual = ops->read_block(d, temp, blocknum); + if(actual != bs) + goto failure; + actual = length; + memcpy(buffer, temp, actual); + } + + buffer += actual; + length -= actual; + offset += actual; + total += actual; + } + + page_free(temp); + return total; + + failure: + page_free(temp); + if(total == 0) + return -1; + return total; +} + +struct fs_dirent * fs_dirent_mkdir(struct fs_dirent *d, const char *name) +{ + const struct fs_ops *ops = d->volume->fs->ops; + if(!ops->mkdir) return 0; + + struct fs_dirent *n = ops->mkdir(d, name); + if(n) { + n->volume = fs_volume_addref(d->volume); + return n; + } + + return 0; +} + +struct fs_dirent * fs_dirent_mkfile(struct fs_dirent *d, const char *name) +{ + const struct fs_ops *ops = d->volume->fs->ops; + if(!ops->mkfile) return 0; + + struct fs_dirent *n = ops->mkfile(d, name); + if(n) { + n->volume = fs_volume_addref(d->volume); + return n; + } + + return 0; +} + +int fs_dirent_remove(struct fs_dirent *d, const char *name) +{ + const struct fs_ops *ops = d->volume->fs->ops; + if(!ops->remove) + return 0; + return ops->remove(d, name); +} + +int fs_dirent_write(struct fs_dirent *d, const char *buffer, uint32_t length, uint32_t offset) +{ + int total = 0; + int bs = d->volume->block_size; + + const struct fs_ops *ops = d->volume->fs->ops; + if(!ops->write_block || !ops->read_block) + return KERROR_INVALID_REQUEST; + + char *temp = page_alloc(0); + + // if writing past the (current) end of the file, resize the file first + if (offset + length > d->size) { + ops->resize(d, offset+length); + } + + while(length > 0) { + + int blocknum = offset / bs; + int actual = 0; + + if(offset % bs) { + actual = ops->read_block(d, temp, blocknum); + if(actual != bs) + goto failure; + + actual = MIN(bs - offset % bs, length); + memcpy(&temp[offset % bs], buffer, actual); + + int wactual = ops->write_block(d, temp, blocknum); + if(wactual != bs) + goto failure; + + } else if(length >= bs) { + actual = ops->write_block(d, buffer, blocknum); + if(actual != bs) + goto failure; + } else { + actual = ops->read_block(d, temp, blocknum); + if(actual != bs) + goto failure; + + actual = length; + memcpy(temp, buffer, actual); + + int wactual = ops->write_block(d, temp, blocknum); + if(wactual != bs) + goto failure; + } + + buffer += actual; + length -= actual; + offset += actual; + total += actual; + } + + page_free(temp); + return total; + + failure: + page_free(temp); + if(total == 0) + return -1; + return total; +} + +int fs_dirent_size(struct fs_dirent *d) +{ + return d->size; +} + +int fs_dirent_isdir( struct fs_dirent *d ) +{ + return d->isdir; +} + +int fs_dirent_copy(struct fs_dirent *src, struct fs_dirent *dst, int depth ) +{ + char *buffer = page_alloc(1); + + int length = fs_dirent_list(src, buffer, PAGE_SIZE); + if (length <= 0) goto failure; + + char *name = buffer; + while (name && (name - buffer) < length) { + + // Skip relative directory entries. + if (strcmp(name,".") == 0 || (strcmp(name, "..") == 0)) { + goto next_entry; + } + + struct fs_dirent *new_src = fs_dirent_lookup(src, name); + if(!new_src) { + printf("couldn't lookup %s in directory!\n",name); + goto next_entry; + } + + int i; + for(i=0;i"); + + if(fs_dirent_isdir(new_src)) { + printf("%s (dir)\n", name); + struct fs_dirent *new_dst = fs_dirent_mkdir(dst,name); + if(!new_dst) { + printf("couldn't create %s!\n",name); + fs_dirent_close(new_src); + goto next_entry; + } + int res = fs_dirent_copy(new_src, new_dst,depth+1); + fs_dirent_close(new_dst); + if(res<0) goto failure; + } else { + printf("%s (%d bytes)\n", name,fs_dirent_size(new_src)); + struct fs_dirent *new_dst = fs_dirent_mkfile(dst, name); + if(!new_dst) { + printf("couldn't create %s!\n",name); + fs_dirent_close(new_src); + goto next_entry; + } + + char * filebuf = page_alloc(0); + if (!filebuf) { + fs_dirent_close(new_src); + fs_dirent_close(new_dst); + goto failure; + } + + uint32_t file_size = fs_dirent_size(new_src); + uint32_t offset = 0; + + while(offset