/* 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