int cmd_cat(FILE * f, int argc, char ** argv) { struct fs_dirent entry; struct fs_file * fp; int i; if (argc < 2) return SHELL_ERR_ARG_MISSING; if (argc > 2) return SHELL_ERR_EXTRA_ARGS; if (!fs_dirent_lookup(argv[1], &entry)) { fprintf(f, "invalid file: \"%s\"\r\n", argv[1]); return SHELL_ERR_ARG_INVALID; } fp = entry.fp; for (i = 0; i < fp->size; ++i) { int c = fp->data[i]; fputc(c, f); } return 0; }
/* This is the main workhorse for the virtual filesystem. Given a start point and * a path string, it resolves the path into a directory entry. This is simply a * matter of looping through the path (seperated at '/', of course), and reading * the successive directory entries and inodes (see vfs_dirent_lookup and * vfs_dirent_readinode). The one thing it needs to pay attention to is the case * of traversing backwards up through a mount point. */ struct dirent *fs_do_path_resolve(struct inode *start, const char *path, int sym_start_level, int *result) { vfs_inode_get(start); ASSERT(start); if(!*path) path = "."; if(result) *result = 0; struct inode *node = start; struct dirent *dir = 0; struct inode *nextnode = 0; while(node && *path) { if(dir) { vfs_dirent_release(dir); dir = 0; } char *delim = strchr(path, '/'); if(delim != path) { nextnode = 0; const char *name = path; size_t namelen = delim ? (size_t)(delim - name) : strlen(name); dir = fs_dirent_lookup(node, name, namelen); if(!dir) { if(result) *result = -ENOENT; vfs_icache_put(node); return 0; } if(delim) { nextnode = fs_dirent_readinode(dir, true); if(namelen == 2 && !strncmp("..", name, 2) && node->id == node->filesystem->root_inode_id) { // Traverse back up through a mount. vfs_inode_get(nextnode->filesystem->point); struct inode *tmp = nextnode; nextnode = nextnode->filesystem->point; vfs_icache_put(tmp); } else if(nextnode) { if((*result = fs_resolve_iter_symlink(&dir, &nextnode, sym_start_level))) { vfs_icache_put(node); return 0; } nextnode = fs_resolve_mount(nextnode); if(!nextnode) { vfs_icache_put(node); return 0; } } } vfs_icache_put(node); node = nextnode; } path = delim + 1; } if(nextnode) vfs_icache_put(nextnode); return dir; }
int cmd_rm(FILE * f, int argc, char ** argv) { struct fs_dirent entry; if (argc < 2) return SHELL_ERR_ARG_MISSING; if (argc > 2) return SHELL_ERR_EXTRA_ARGS; if (!fs_dirent_lookup(argv[1], &entry)) { fprintf(f, "invalid file: \"%s\"\r\n", argv[1]); return SHELL_ERR_ARG_INVALID; } fs_file_unlink(&entry); return 0; }
int cmd_js(FILE * f, int argc, char ** argv) { struct fs_dirent entry; char * script; int len; if (argc < 2) return SHELL_ERR_ARG_MISSING; if (argc > 2) return SHELL_ERR_EXTRA_ARGS; if (fs_dirent_lookup(argv[1], &entry)) { script = (char *)entry.fp->data; len = entry.fp->size; } else { script = argv[1]; len = strlen(argv[1]); } return js(f, script, len); }
/* note that this function doesn't actually delete the dirent. It just sets a flag that * says "hey, this was deleted". See vfs_dirent_release for more details. An important * aspect is that on unlinking a directory, it does unlink the . and .. entries, even * though the directory won't actually be deleted until vfs_dirent_release gets called * and the last reference is released. */ static int do_fs_unlink(struct inode *node, const char *name, size_t namelen, int rec) { if(!vfs_inode_check_permissions(node, MAY_WRITE, 0)) return -EACCES; struct dirent *dir = fs_dirent_lookup(node, name, namelen); if(!dir) return -ENOENT; struct inode *target = fs_dirent_readinode(dir, true); if(!target || (rec && S_ISDIR(target->mode) && !fs_inode_dirempty(target))) { if(target) vfs_icache_put(target); vfs_dirent_release(dir); return -ENOTEMPTY; } atomic_fetch_or_explicit(&dir->flags, DIRENT_UNLINK, memory_order_release); if(S_ISDIR(target->mode) && rec) { do_fs_unlink(target, "..", 2, 0); do_fs_unlink(target, ".", 1, 0); } vfs_icache_put(target); vfs_dirent_release(dir); return 0; }
/* This function returns the directory entry associated with the name 'name' under * the inode 'node'. It must be careful to lookup the entry in the cache first. */ struct dirent *fs_dirent_lookup(struct inode *node, const char *name, size_t namelen) { if(!vfs_inode_check_permissions(node, MAY_READ, 0)) return 0; if(!S_ISDIR(node->mode)) return 0; if(node == current_process->root && !strncmp(name, "..", 2) && namelen == 2) return fs_dirent_lookup(node, ".", 1); mutex_acquire(dirent_cache_lock); rwlock_acquire(&node->lock, RWL_WRITER); struct dirent *dir = vfs_inode_get_dirent(node, name, namelen); if(!dir) { dir = vfs_dirent_create(node); dir->count = 1; strncpy(dir->name, name, namelen); dir->namelen = namelen; int r = fs_callback_inode_lookup(node, name, namelen, dir); if(r) { dir->count = 0; vfs_dirent_destroy(dir); rwlock_release(&node->lock, RWL_WRITER); mutex_release(dirent_cache_lock); return 0; } vfs_inode_get(node); vfs_inode_add_dirent(node, dir); } else { if(atomic_fetch_add(&dir->count, 1) == 0) { fs_dirent_remove_lru(dir); vfs_inode_get(node); } } rwlock_release(&node->lock, RWL_WRITER); mutex_release(dirent_cache_lock); return dir; }
/* This is possibly the lengthiest function in the VFS (possibly in the entire kernel!). * It resolves a path until the last name in the path string, and then tries to create * a new file with that name. It requires a lot of in-sequence checks for permission, * existance, is-a-directory, and so forth. */ struct inode *fs_path_resolve_create_get(const char *path, int flags, mode_t mode, int *result, struct dirent **dirent) { // Step 1: Split the path up into directory to create in, and name of new file. int len = strlen(path) + 1; char tmp[len]; memcpy(tmp, path, len); char *del = strrchr(tmp, '/'); if(del) *del = 0; char *dirpath = del ? tmp : "."; char *name = del ? del + 1 : tmp; if(dirpath[0] == 0) dirpath = "/"; if(dirent) *dirent = 0; if(result) *result = 0; // Step 2: Resolve the target directory. struct inode *dir = fs_path_resolve_inode(dirpath, flags, result); if(!dir) return 0; if(!S_ISDIR(dir->mode)) { if(result) *result = -ENOTDIR; return 0; } // Step 3: Try to look up the file that we're trying to create. struct dirent *test = fs_dirent_lookup(dir, name, strlen(name)); if(test) { // If it was found, return it and its inode. if(dirent) *dirent = test; struct inode *ret = fs_dirent_readinode(test, true); if(ret) ret = fs_resolve_mount(ret); if(!ret) { if(dirent) *dirent = 0; if(result) *result = -EIO; vfs_dirent_release(test); } else { if(!dirent) vfs_dirent_release(test); if(result) *result = 0; } vfs_icache_put(dir); return ret; } // Didn't find the entry. Step 4: Create one. if(!vfs_inode_check_permissions(dir, MAY_WRITE, 0)) { if(result) *result = -EACCES; vfs_icache_put(dir); return 0; } if(dir->nlink == 1) { if(result) *result = -ENOSPC; vfs_icache_put(dir); return 0; } uint32_t id; // Step 4a: Allocate an inode. int r = fs_callback_fs_alloc_inode(dir->filesystem, &id); if(r) { if(result) *result = r; vfs_icache_put(dir); return 0; } // Step 4b: Read in that inode, and set some initial values (like creation time). struct inode *node = vfs_icache_get(dir->filesystem, id); if(!node) { vfs_icache_put(dir); if(result) *result = -EIO; return 0; } node->mode = mode; node->length = 0; node->ctime = node->mtime = time_get_epoch(); vfs_inode_set_dirty(node); // If we're making a directory, create the . and .. entries. if(S_ISDIR(mode)) { // Create . and .. if(fs_link(node, node, ".", 1, true)) r = -EPERM; if(fs_link(node, dir, "..", 2, true)) r = -EMLINK; } // Step 4c: Create the link for the directory entry to the inode. r = fs_link(dir, node, name, strlen(name), false); if(result) *result = !r ? 1 : r; if(dirent && !r) { *dirent = fs_dirent_lookup(dir, name, strlen(name)); if(!*dirent && node) { vfs_icache_put(node); vfs_icache_put(dir); if(result) *result = -EIO; return 0; } } vfs_icache_put(dir); return r ? 0 : node; }