From 60f1fc90c6fe6da610c7ee442884022cc1abf75d Mon Sep 17 00:00:00 2001 From: Yuta Imazu Date: Sun, 23 Jun 2024 11:37:39 +0900 Subject: [PATCH] fs: refactor path resolution --- kernel/Makefile | 1 + kernel/console/psf.c | 82 +++++---- kernel/exec.c | 2 +- kernel/fs/fs.h | 25 ++- kernel/fs/initrd.c | 12 +- kernel/fs/path.c | 101 +++++++++++ kernel/fs/path.h | 35 ++++ kernel/fs/vfs.c | 380 ++++++++++++++------------------------- kernel/memory/kmalloc.c | 6 + kernel/process.c | 22 ++- kernel/process.h | 3 +- kernel/syscall/fs.c | 233 ++++++++++-------------- kernel/syscall/process.c | 49 +++-- 13 files changed, 473 insertions(+), 478 deletions(-) create mode 100644 kernel/fs/path.c create mode 100644 kernel/fs/path.h diff --git a/kernel/Makefile b/kernel/Makefile index 6f6d2b48..db97f807 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -34,6 +34,7 @@ OBJS := \ fs/fifo.o \ fs/fs.o \ fs/initrd.o \ + fs/path.o \ fs/procfs/pid.o \ fs/procfs/procfs.o \ fs/procfs/root.o \ diff --git a/kernel/console/psf.c b/kernel/console/psf.c index 43123cce..6aa1a913 100644 --- a/kernel/console/psf.c +++ b/kernel/console/psf.c @@ -2,31 +2,24 @@ #include #include #include +#include #include +#include #include #include -static struct font* load_psf1(const char* filename) { - file_description* desc = vfs_open(filename, O_RDONLY, 0); - if (IS_ERR(desc)) - return ERR_CAST(desc); - +static struct font* load_psf1(file_description* desc) { struct psf1_header header; if (file_description_read(desc, &header, sizeof(struct psf1_header)) != sizeof(struct psf1_header)) { - file_description_close(desc); return ERR_PTR(-EINVAL); } - if (header.magic[0] != PSF1_MAGIC0 || header.magic[1] != PSF1_MAGIC1) { - file_description_close(desc); + if (header.magic[0] != PSF1_MAGIC0 || header.magic[1] != PSF1_MAGIC1) return ERR_PTR(-EINVAL); - } struct font* font = kmalloc(sizeof(struct font)); - if (!font) { - file_description_close(desc); + if (!font) return ERR_PTR(-ENOMEM); - } font->glyph_width = 8; font->glyph_height = font->bytes_per_glyph = header.charsize; @@ -36,14 +29,12 @@ static struct font* load_psf1(const char* filename) { font->glyphs = kmalloc(buf_size); if (!font->glyphs) { kfree(font); - file_description_close(desc); return ERR_PTR(-ENOMEM); } if ((size_t)file_description_read(desc, font->glyphs, buf_size) != buf_size) { kfree(font->glyphs); kfree(font); - file_description_close(desc); return ERR_PTR(-EINVAL); } @@ -56,7 +47,6 @@ static struct font* load_psf1(const char* filename) { sizeof(uint16_t)) { kfree(font->glyphs); kfree(font); - file_description_close(desc); return ERR_PTR(-EINVAL); } if (uc == PSF1_SEPARATOR) @@ -70,32 +60,21 @@ static struct font* load_psf1(const char* filename) { font->ascii_to_glyph[i] = i; } - file_description_close(desc); return font; } -static struct font* load_psf2(const char* filename) { - file_description* desc = vfs_open(filename, O_RDONLY, 0); - if (IS_ERR(desc)) - return ERR_CAST(desc); - +static struct font* load_psf2(file_description* desc) { struct psf2_header header; if (file_description_read(desc, &header, sizeof(struct psf2_header)) != - sizeof(struct psf2_header)) { - file_description_close(desc); + sizeof(struct psf2_header)) return ERR_PTR(-EINVAL); - } if (header.magic != PSF2_MAGIC || header.version != 0 || - header.headersize != sizeof(struct psf2_header)) { - file_description_close(desc); + header.headersize != sizeof(struct psf2_header)) return ERR_PTR(-EINVAL); - } struct font* font = kmalloc(sizeof(struct font)); - if (!font) { - file_description_close(desc); + if (!font) return ERR_PTR(-ENOMEM); - } font->glyph_width = header.width; font->glyph_height = header.height; @@ -103,7 +82,6 @@ static struct font* load_psf2(const char* filename) { if (div_ceil(font->glyph_width, 8) * font->glyph_height != font->bytes_per_glyph) { kfree(font); - file_description_close(desc); return ERR_PTR(-EINVAL); } @@ -111,14 +89,12 @@ static struct font* load_psf2(const char* filename) { font->glyphs = kmalloc(buf_size); if (!font->glyphs) { kfree(font); - file_description_close(desc); return ERR_PTR(-ENOMEM); } if ((size_t)file_description_read(desc, font->glyphs, buf_size) != buf_size) { kfree(font->glyphs); kfree(font); - file_description_close(desc); return ERR_PTR(-EINVAL); } @@ -131,7 +107,6 @@ static struct font* load_psf2(const char* filename) { sizeof(uint8_t)) { kfree(font->glyphs); kfree(font); - file_description_close(desc); return ERR_PTR(-EINVAL); } if (uc == PSF2_SEPARATOR) @@ -145,18 +120,41 @@ static struct font* load_psf2(const char* filename) { font->ascii_to_glyph[i] = i; } - file_description_close(desc); return font; } struct font* load_psf(const char* filename) { - struct font* font = load_psf1(filename); - if (IS_OK(font)) - return font; + struct path* root = vfs_get_root(); + if (IS_ERR(root)) + return ERR_CAST(root); + + struct font* ret = NULL; - font = load_psf2(filename); - if (IS_OK(font)) - return font; + file_description* desc = vfs_open_at(root, filename, O_RDONLY, 0); + if (IS_ERR(desc)) { + ret = ERR_CAST(desc); + goto done; + } - return ERR_PTR(-EINVAL); + ret = load_psf1(desc); + if (IS_OK(ret)) + goto done; + + int rc = file_description_seek(desc, 0, SEEK_SET); + if (IS_ERR(rc)) { + ret = ERR_PTR(rc); + goto done; + } + + ret = load_psf2(desc); + if (IS_OK(ret)) + goto done; + + ret = ERR_PTR(-EINVAL); + +done: + if (desc) + file_description_close(desc); + path_destroy_recursive(root); + return ret; } diff --git a/kernel/exec.c b/kernel/exec.c index 939a1390..cd8ca763 100644 --- a/kernel/exec.c +++ b/kernel/exec.c @@ -158,7 +158,7 @@ NODISCARD static int push_ptrs(uintptr_t* sp, uintptr_t stack_base, static int execve(const char* pathname, string_vec* argv, string_vec* envp) { struct stat stat; - int rc = vfs_stat(pathname, &stat); + int rc = vfs_stat_at(current->cwd, pathname, &stat, 0); if (IS_ERR(rc)) return rc; if (!S_ISREG(stat.st_mode)) diff --git a/kernel/fs/fs.h b/kernel/fs/fs.h index 6dafb9a1..b8fc0e29 100644 --- a/kernel/fs/fs.h +++ b/kernel/fs/fs.h @@ -107,21 +107,34 @@ NODISCARD int file_description_block(file_description*, void vfs_init(void); void vfs_populate_root_fs(const multiboot_module_t* initrd_mod); -struct inode* vfs_get_root(void); -NODISCARD int vfs_mount(const char* path, struct inode* fs_root); +struct path* vfs_get_root(void); +NODISCARD int vfs_mount(const char* pathname, struct inode* fs_root); +NODISCARD int vfs_mount_at(const struct path* base, const char* pathname, + struct inode* fs_root); NODISCARD int vfs_register_device(struct inode* device); struct inode* vfs_get_device(dev_t); dev_t vfs_generate_unnamed_device_number(void); +// Return a path even if the last component of the path does not exist. +// The last component will have NULL inode in this case. +#define O_ALLOW_NOENT 0x4000 + NODISCARD file_description* vfs_open(const char* pathname, int flags, mode_t mode); -NODISCARD int vfs_stat(const char* pathname, struct stat* buf); +NODISCARD file_description* vfs_open_at(const struct path* base, + const char* pathname, int flags, + mode_t mode); +NODISCARD int vfs_stat(const char* pathname, struct stat* buf, int flags); +NODISCARD int vfs_stat_at(const struct path* base, const char* pathname, + struct stat* buf, int flags); NODISCARD struct inode* vfs_create(const char* pathname, mode_t mode); +NODISCARD struct inode* vfs_create_at(const struct path* base, + const char* pathname, mode_t mode); -char* vfs_canonicalize_path(const char* pathname); -struct inode* vfs_resolve_path(const char* pathname, struct inode** out_parent, - char** out_basename); +struct path* vfs_resolve_path(const char* pathname, int flags); +struct path* vfs_resolve_path_at(const struct path* base, const char* pathname, + int flags); uint8_t mode_to_dirent_type(mode_t); diff --git a/kernel/fs/initrd.c b/kernel/fs/initrd.c index 0cdc846f..4dd427fe 100644 --- a/kernel/fs/initrd.c +++ b/kernel/fs/initrd.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -43,6 +44,9 @@ void initrd_populate_root_fs(uintptr_t paddr, size_t size) { ASSERT_OK( paging_map_to_physical_range(vaddr, region_start, region_size, 0)); + struct path* root = vfs_get_root(); + ASSERT_OK(root); + uintptr_t cursor = vaddr + (paddr - region_start); for (;;) { const struct cpio_odc_header* header = @@ -59,10 +63,12 @@ void initrd_populate_root_fs(uintptr_t paddr, size_t size) { size_t file_size = PARSE(header->c_filesize); if (S_ISDIR(mode)) { - ASSERT_OK(vfs_create(filename, mode)); + struct inode* inode = vfs_create_at(root, filename, mode); + ASSERT_OK(inode); + inode_unref(inode); } else { file_description* desc = - vfs_open(filename, O_CREAT | O_EXCL | O_WRONLY, mode); + vfs_open_at(root, filename, O_CREAT | O_EXCL | O_WRONLY, mode); ASSERT_OK(desc); desc->inode->rdev = PARSE(header->c_rdev); @@ -83,6 +89,8 @@ void initrd_populate_root_fs(uintptr_t paddr, size_t size) { cursor += sizeof(struct cpio_odc_header) + name_size + file_size; } + path_destroy_recursive(root); + paging_kernel_unmap(vaddr, region_size); ASSERT_OK( range_allocator_free(&kernel_vaddr_allocator, vaddr, region_size)); diff --git a/kernel/fs/path.c b/kernel/fs/path.c new file mode 100644 index 00000000..4041d0cf --- /dev/null +++ b/kernel/fs/path.c @@ -0,0 +1,101 @@ +#include "path.h" +#include "fs.h" +#include +#include +#include + +char* path_to_string(const struct path* path) { + size_t len = 1; // For the null terminator + for (const struct path* it = path; it; it = it->parent) { + if (it->basename) + len += strlen(it->basename) + 1; // +1 for the '/' + else + ASSERT(it->parent == NULL); // Root directory + } + char* s = kmalloc(len); + if (!s) + return NULL; + char* p = s + len - 1; + *p = 0; + for (const struct path* it = path; it; it = it->parent) { + if (!it->basename) { + ASSERT(it->parent == NULL); // Root directory + break; + } + size_t basename_len = strlen(it->basename); + p -= basename_len; + memcpy(p, it->basename, basename_len); + --p; + *p = '/'; + } + ASSERT(p == s); + return s; +} + +struct path* path_dup(const struct path* path) { + if (!path) + return NULL; + struct path* new_path = kmalloc(sizeof(struct path)); + if (!new_path) + return ERR_PTR(-ENOMEM); + *new_path = (struct path){0}; + if (path->basename) { + new_path->basename = kstrdup(path->basename); + if (!new_path->basename) { + kfree(new_path); + return ERR_PTR(-ENOMEM); + } + } + new_path->parent = path_dup(path->parent); + if (IS_ERR(new_path->parent)) { + kfree(new_path->basename); + kfree(new_path); + return new_path->parent; + } + new_path->inode = path->inode; + inode_ref(new_path->inode); + return new_path; +} + +struct path* path_join(struct path* parent, struct inode* inode, + const char* basename) { + struct path* path = kmalloc(sizeof(struct path)); + if (!path) { + inode_unref(inode); + return ERR_PTR(-ENOMEM); + } + path->basename = kstrdup(basename); + if (!path->basename) { + kfree(path); + inode_unref(inode); + return ERR_PTR(-ENOMEM); + } + path->inode = inode; + path->parent = parent; + return path; +} + +struct inode* path_into_inode(struct path* path) { + if (!path) + return NULL; + struct inode* inode = path->inode; + path->inode = NULL; + path_destroy_recursive(path); + return inode; +} + +void path_destroy_last(struct path* path) { + if (!path) + return; + inode_unref(path->inode); + kfree(path->basename); + kfree(path); +} + +void path_destroy_recursive(struct path* path) { + while (path) { + struct path* parent = path->parent; + path_destroy_last(path); + path = parent; + } +} diff --git a/kernel/fs/path.h b/kernel/fs/path.h new file mode 100644 index 00000000..864162dc --- /dev/null +++ b/kernel/fs/path.h @@ -0,0 +1,35 @@ +#pragma once + +struct path { + // The inode associated with the path. + // NULL if vfs_resolve_path was called with O_ALLOW_NOENT and + // the last component of the path does not exist. + struct inode* inode; + + // The basename of the path. NULL for the root path. + char* basename; + + // The parent of the path. NULL for the root path. + struct path* parent; +}; + +// Returns a string representation of the path. +char* path_to_string(const struct path*); + +// Returns a clone of the path. +struct path* path_dup(const struct path*); + +// Joins a path with a basename associated with an inode. +// +// The parent is consumed by the function. +struct path* path_join(struct path* parent, struct inode* inode, + const char* basename); + +// Extracts the inode from the path and destroys the path. +struct inode* path_into_inode(struct path*); + +// Destroys the last component of the path, but not the parents. +void path_destroy_last(struct path*); + +// Destroys the path and all its parents. +void path_destroy_recursive(struct path*); diff --git a/kernel/fs/vfs.c b/kernel/fs/vfs.c index 2df2a234..086e6e40 100644 --- a/kernel/fs/vfs.c +++ b/kernel/fs/vfs.c @@ -1,4 +1,5 @@ #include "fs.h" +#include "path.h" #include #include #include @@ -16,7 +17,7 @@ static struct inode* root; void vfs_init(void) { kprintf("vfs: mounting root filesystem\n"); - ASSERT_OK(vfs_mount(ROOT_DIR, tmpfs_create_root())); + root = tmpfs_create_root(); } void vfs_populate_root_fs(const multiboot_module_t* initrd_mod) { @@ -26,10 +27,14 @@ void vfs_populate_root_fs(const multiboot_module_t* initrd_mod) { initrd_mod->mod_end - initrd_mod->mod_start); } -struct inode* vfs_get_root(void) { +struct path* vfs_get_root(void) { ASSERT(root); + struct path* path = kmalloc(sizeof(struct path)); + if (!path) + return ERR_PTR(-ENOMEM); + *path = (struct path){.inode = root}; inode_ref(root); - return root; + return path; } typedef struct mount_point { @@ -82,17 +87,17 @@ static struct inode* resolve_mounts(struct inode* host) { return needle; } -int vfs_mount(const char* path, struct inode* fs_root) { - if (path[0] == PATH_SEPARATOR && path[1] == '\0') { - root = fs_root; - return 0; - } +int vfs_mount(const char* pathname, struct inode* fs_root) { + return vfs_mount_at(current->cwd, pathname, fs_root); +} - struct inode* inode = vfs_resolve_path(path, NULL, NULL); - if (IS_ERR(inode)) - return PTR_ERR(inode); +int vfs_mount_at(const struct path* base, const char* pathname, + struct inode* fs_root) { + struct path* path = vfs_resolve_path_at(base, pathname, 0); + if (IS_ERR(path)) + return PTR_ERR(path); - return mount_at(inode, fs_root); + return mount_at(path_into_inode(path), fs_root); } typedef struct device { @@ -148,261 +153,85 @@ dev_t vfs_generate_unnamed_device_number(void) { return makedev(0, id); } -typedef struct list_node { - char* value; - struct list_node* next; -} list_node; - -static void list_destroy(list_node** list) { - list_node* it = *list; - while (it) { - list_node* next = it->next; - kfree(it->value); - kfree(it); - it = next; - } - *list = NULL; -} - -static int list_push(list_node** list, const char* value) { - list_node* node = kmalloc(sizeof(list_node)); - if (!node) - return -ENOMEM; - node->value = kstrdup(value); - if (!node->value) { - kfree(node); - return -ENOMEM; - } - - node->next = NULL; - if (*list) { - list_node* it = *list; - while (it->next) - it = it->next; - it->next = node; - } else { - *list = node; - } - return 0; -} - -static void list_pop(list_node** list) { - list_node* prev = NULL; - list_node* it = *list; - while (it) { - if (!it->next) - break; - prev = it; - it = it->next; - } - if (prev) - prev->next = NULL; - else - *list = NULL; - if (it) { - kfree(it->value); - kfree(it); - } -} - static bool is_absolute_path(const char* path) { return path[0] == PATH_SEPARATOR; } -static int create_path_component_list(const char* pathname, - list_node** out_list, - size_t* out_num_components) { - if (pathname[0] == PATH_SEPARATOR && pathname[1] == '\0') { - if (out_list) - *out_list = NULL; - if (out_num_components) - *out_num_components = 0; - return 0; - } +struct path* vfs_resolve_path(const char* pathname, int flags) { + return vfs_resolve_path_at(current->cwd, pathname, flags); +} - list_node* list = NULL; - size_t num_components = 0; - - if (!is_absolute_path(pathname)) { - char* dup_cwd = kstrdup(current->cwd_path); - if (!dup_cwd) - return -ENOMEM; - - char* saved_ptr; - for (const char* component = - strtok_r(dup_cwd, PATH_SEPARATOR_STR, &saved_ptr); - component; - component = strtok_r(NULL, PATH_SEPARATOR_STR, &saved_ptr)) { - int rc = list_push(&list, component); - if (IS_ERR(rc)) { - list_destroy(&list); - kfree(dup_cwd); - return rc; - } - ++num_components; - } +struct path* vfs_resolve_path_at(const struct path* base, const char* pathname, + int flags) { + struct path* path = + is_absolute_path(pathname) ? vfs_get_root() : path_dup(base); + if (IS_ERR(path)) + return path; - kfree(dup_cwd); + char* dup_pathname = kstrdup(pathname); + if (!dup_pathname) { + path_destroy_recursive(path); + return ERR_PTR(-ENOMEM); } - char* dup_path = kstrdup(pathname); - if (!dup_path) - return -ENOMEM; - char* saved_ptr; for (const char* component = - strtok_r(dup_path, PATH_SEPARATOR_STR, &saved_ptr); + strtok_r(dup_pathname, PATH_SEPARATOR_STR, &saved_ptr); component; component = strtok_r(NULL, PATH_SEPARATOR_STR, &saved_ptr)) { + if (component[0] == '\0') + continue; if (component[0] == '.' && component[1] == '\0') continue; if (!strcmp(component, "..")) { - if (num_components > 0) { // "/.." becomes "/" - list_pop(&list); - --num_components; + if (!path->parent) { + // "/.." becomes "/" + continue; } + struct path* parent = path->parent; + path_destroy_last(path); + path = parent; continue; } - int rc = list_push(&list, component); - if (IS_ERR(rc)) { - list_destroy(&list); - kfree(dup_path); - return rc; - } - ++num_components; - } - - kfree(dup_path); - if (out_list) - *out_list = list; - else - list_destroy(&list); - if (out_num_components) - *out_num_components = num_components; - - return 0; -} - -struct inode* vfs_resolve_path(const char* pathname, struct inode** out_parent, - char** out_basename) { - list_node* component_list = NULL; - size_t num_components = 0; - int rc = - create_path_component_list(pathname, &component_list, &num_components); - if (IS_ERR(rc)) - return ERR_PTR(rc); - - if (num_components == 0) - return vfs_get_root(); - - struct inode* parent = vfs_get_root(); - size_t i = 0; - for (list_node* node = component_list; node; node = node->next) { - const char* component = node->value; - if (i == num_components - 1) { // last component - if (out_basename) { - char* dup_basename = kstrdup(component); - if (!dup_basename) { - inode_unref(parent); - list_destroy(&component_list); - return ERR_PTR(-ENOMEM); - } - *out_basename = dup_basename; - } - if (out_parent) { - inode_ref(parent); - *out_parent = parent; + inode_ref(path->inode); + struct inode* inode = inode_lookup_child(path->inode, component); + + if ((flags & O_ALLOW_NOENT) && PTR_ERR(inode) == -ENOENT) { + const char* next_component = + strtok_r(NULL, PATH_SEPARATOR_STR, &saved_ptr); + if (next_component) { + // This is not the last component. + path_destroy_recursive(path); + kfree(dup_pathname); + return ERR_PTR(-ENOENT); } + struct path* joined = path_join(path, NULL, component); + kfree(dup_pathname); + if (IS_ERR(joined)) + path_destroy_recursive(path); + return joined; } - struct inode* child = inode_lookup_child(parent, component); - if (IS_ERR(child)) { - list_destroy(&component_list); - return child; + if (IS_ERR(inode)) { + path_destroy_recursive(path); + kfree(dup_pathname); + return ERR_CAST(inode); } - parent = resolve_mounts(child); - ++i; - } - - list_destroy(&component_list); - return parent; -} + inode = resolve_mounts(inode); -char* vfs_canonicalize_path(const char* pathname) { - list_node* component_list = NULL; - size_t num_components = 0; - int rc = - create_path_component_list(pathname, &component_list, &num_components); - if (IS_ERR(rc)) - return ERR_PTR(rc); - - if (num_components == 0) { - char* canonicalized = kstrdup(ROOT_DIR); - if (!canonicalized) - return ERR_PTR(-ENOMEM); - return canonicalized; - } - - size_t len = 0; - for (list_node* node = component_list; node; node = node->next) - len += strlen(node->value) + 1; - - char* canonicalized = kmalloc(len + 1); - if (!canonicalized) { - list_destroy(&component_list); - return ERR_PTR(-ENOMEM); - } - size_t idx = 0; - for (list_node* node = component_list; node; node = node->next) { - canonicalized[idx++] = PATH_SEPARATOR; - size_t len = strlen(node->value); - strncpy(canonicalized + idx, node->value, len); - idx += len; - } - canonicalized[idx] = '\0'; - - list_destroy(&component_list); - return canonicalized; -} - -static struct inode* create_inode(const char* pathname, mode_t mode, - bool exclusive) { - struct inode* parent = NULL; - char* basename = NULL; - struct inode* inode = vfs_resolve_path(pathname, &parent, &basename); - if (IS_OK(inode)) { - inode_unref(parent); - kfree(basename); - if (exclusive) { - inode_unref(inode); - return ERR_PTR(-EEXIST); + struct path* joined = path_join(path, inode, component); + if (IS_ERR(joined)) { + path_destroy_recursive(path); + kfree(dup_pathname); + return joined; } - return inode; - } - if (PTR_ERR(inode) != -ENOENT || !parent) { - inode_unref(parent); - kfree(basename); - return inode; + path = joined; } - // retry if another process is modifying the inode at the same time - for (;;) { - inode_ref(parent); - inode = inode_create_child(parent, basename, mode); - if (IS_OK(inode) || PTR_ERR(inode) != -EEXIST || exclusive) - break; - - inode_ref(parent); - inode = inode_lookup_child(parent, basename); - if (IS_OK(inode) || PTR_ERR(inode) != -ENOENT) - break; - } - - inode_unref(parent); - kfree(basename); - return inode; + kfree(dup_pathname); + return path; } static struct inode* resolve_special_file(struct inode* inode) { @@ -441,12 +270,50 @@ static struct inode* resolve_special_file(struct inode* inode) { return inode; } +static struct path* create_at(const struct path* base, const char* pathname, + mode_t mode, bool exclusive) { + struct path* path = vfs_resolve_path_at(base, pathname, O_ALLOW_NOENT); + if (IS_ERR(path)) + return ERR_CAST(path); + + if (exclusive && path->inode) { + path_destroy_recursive(path); + return ERR_PTR(-EEXIST); + } + + struct inode* inode = NULL; + for (;;) { + inode_ref(path->parent->inode); + inode = inode_create_child(path->parent->inode, path->basename, mode); + if (IS_OK(inode) || PTR_ERR(inode) != -EEXIST || exclusive) + break; + // Another process is creating the same file. Look up the created file. + + inode_ref(path->parent->inode); + inode = inode_lookup_child(path->parent->inode, path->basename); + if (IS_OK(inode) || PTR_ERR(inode) != -ENOENT) + break; + // The file was removed before we could look it up. Retry creating it. + } + + path->inode = inode; + return path; +} + file_description* vfs_open(const char* pathname, int flags, mode_t mode) { - struct inode* inode = (flags & O_CREAT) - ? create_inode(pathname, mode, flags & O_EXCL) - : vfs_resolve_path(pathname, NULL, NULL); - if (IS_ERR(inode)) - return ERR_CAST(inode); + return vfs_open_at(current->cwd, pathname, flags, mode); +} + +file_description* vfs_open_at(const struct path* base, const char* pathname, + int flags, mode_t mode) { + struct path* path = (flags & O_CREAT) + ? create_at(base, pathname, mode, flags & O_EXCL) + : vfs_resolve_path_at(base, pathname, flags); + if (IS_ERR(path)) + return ERR_CAST(path); + + struct inode* inode = path_into_inode(path); + ASSERT(inode); inode = resolve_special_file(inode); if (IS_ERR(inode)) @@ -455,13 +322,26 @@ file_description* vfs_open(const char* pathname, int flags, mode_t mode) { return inode_open(inode, flags, mode); } -int vfs_stat(const char* pathname, struct stat* buf) { - struct inode* inode = vfs_resolve_path(pathname, NULL, NULL); - if (IS_ERR(inode)) - return PTR_ERR(inode); - return inode_stat(inode, buf); +int vfs_stat(const char* pathname, struct stat* buf, int flags) { + return vfs_stat_at(current->cwd, pathname, buf, flags); +} + +int vfs_stat_at(const struct path* base, const char* pathname, struct stat* buf, + int flags) { + struct path* path = vfs_resolve_path_at(base, pathname, flags); + if (IS_ERR(path)) + return PTR_ERR(path); + return inode_stat(path_into_inode(path), buf); } struct inode* vfs_create(const char* pathname, mode_t mode) { - return create_inode(pathname, mode, true); + return vfs_create_at(current->cwd, pathname, mode); +} + +struct inode* vfs_create_at(const struct path* base, const char* pathname, + mode_t mode) { + struct path* path = create_at(base, pathname, mode, true); + if (IS_ERR(path)) + return ERR_CAST(path); + return path_into_inode(path); } diff --git a/kernel/memory/kmalloc.c b/kernel/memory/kmalloc.c index 52940665..4f606e8a 100644 --- a/kernel/memory/kmalloc.c +++ b/kernel/memory/kmalloc.c @@ -88,6 +88,9 @@ void kfree(void* ptr) { } char* kstrdup(const char* src) { + if (!src) + return NULL; + size_t len = strlen(src); char* buf = kmalloc((len + 1) * sizeof(char)); if (!buf) @@ -99,6 +102,9 @@ char* kstrdup(const char* src) { } char* kstrndup(const char* src, size_t n) { + if (!src) + return NULL; + size_t len = strnlen(src, n); char* buf = kmalloc((len + 1) * sizeof(char)); if (!buf) diff --git a/kernel/process.c b/kernel/process.c index f2549980..a8328a13 100644 --- a/kernel/process.c +++ b/kernel/process.c @@ -1,6 +1,7 @@ #include "process.h" #include "api/signum.h" #include "boot_defs.h" +#include "fs/path.h" #include "interrupts.h" #include "kprintf.h" #include "memory/memory.h" @@ -33,9 +34,8 @@ void process_init(void) { (page_directory*)((uintptr_t)kernel_page_directory + KERNEL_VADDR); current->stack_top = (uintptr_t)stack_top; - current->cwd_path = kstrdup(ROOT_DIR); - ASSERT(current->cwd_path); - current->cwd_inode = vfs_get_root(); + current->cwd = vfs_get_root(); + ASSERT_OK(current->cwd); ASSERT_OK(file_descriptor_table_init(¤t->fd_table)); @@ -55,16 +55,15 @@ struct process* process_create_kernel_process(const char* comm, process->state = PROCESS_STATE_RUNNABLE; strlcpy(process->comm, comm, sizeof(process->comm)); - process->cwd_path = kstrdup(ROOT_DIR); - if (!process->cwd_path) { + process->cwd = vfs_get_root(); + if (IS_ERR(process->cwd)) { kfree(process); - return ERR_PTR(-ENOMEM); + return ERR_CAST(process->cwd); } - process->cwd_inode = vfs_get_root(); int rc = file_descriptor_table_init(&process->fd_table); if (IS_ERR(rc)) { - kfree(process->cwd_path); + path_destroy_recursive(process->cwd); kfree(process); return ERR_PTR(rc); } @@ -72,7 +71,7 @@ struct process* process_create_kernel_process(const char* comm, void* stack = kmalloc(STACK_SIZE); if (!stack) { file_descriptor_table_destroy(&process->fd_table); - kfree(process->cwd_path); + path_destroy_recursive(process->cwd); kfree(process); return ERR_PTR(-ENOMEM); } @@ -83,7 +82,7 @@ struct process* process_create_kernel_process(const char* comm, if (IS_ERR(process->pd)) { kfree(stack); file_descriptor_table_destroy(&process->fd_table); - kfree(process->cwd_path); + path_destroy_recursive(process->cwd); kfree(process); return ERR_CAST(process->pd); } @@ -131,8 +130,7 @@ static noreturn void die(void) { sti(); paging_destroy_current_page_directory(); file_descriptor_table_destroy(¤t->fd_table); - kfree(current->cwd_path); - inode_unref(current->cwd_inode); + path_destroy_recursive(current->cwd); cli(); { diff --git a/kernel/process.h b/kernel/process.h index 9d267ac2..1b1442db 100644 --- a/kernel/process.h +++ b/kernel/process.h @@ -26,8 +26,7 @@ struct process { uintptr_t stack_top; range_allocator vaddr_allocator; - char* cwd_path; - struct inode* cwd_inode; + struct path* cwd; file_descriptor_table fd_table; bool (*should_unblock)(void*); diff --git a/kernel/syscall/fs.c b/kernel/syscall/fs.c index cf843484..5d3b99aa 100644 --- a/kernel/syscall/fs.c +++ b/kernel/syscall/fs.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -83,7 +84,7 @@ int sys_stat(const char* user_pathname, struct stat* user_buf) { if (IS_ERR(rc)) return rc; struct stat buf; - rc = vfs_stat(pathname, &buf); + rc = vfs_stat(pathname, &buf, 0); if (IS_ERR(rc)) return rc; if (!copy_to_user(user_buf, &buf, sizeof(struct stat))) @@ -167,49 +168,45 @@ int sys_mount(const mount_params* user_params) { } int sys_link(const char* user_oldpath, const char* user_newpath) { - char oldpath[PATH_MAX]; - int rc = copy_pathname_from_user(oldpath, user_oldpath); + char old_pathname[PATH_MAX]; + int rc = copy_pathname_from_user(old_pathname, user_oldpath); if (IS_ERR(rc)) return rc; - char newpath[PATH_MAX]; - rc = copy_pathname_from_user(newpath, user_newpath); + char new_pathname[PATH_MAX]; + rc = copy_pathname_from_user(new_pathname, user_newpath); if (IS_ERR(rc)) return rc; - struct inode* old_inode = vfs_resolve_path(oldpath, NULL, NULL); - if (IS_ERR(old_inode)) - return PTR_ERR(old_inode); - if (S_ISDIR(old_inode->mode)) { - inode_unref(old_inode); + struct path* old_path = vfs_resolve_path(old_pathname, 0); + if (IS_ERR(old_path)) + return PTR_ERR(old_path); + if (S_ISDIR(old_path->inode->mode)) { + path_destroy_recursive(old_path); return -EPERM; } - struct inode* new_parent = NULL; - char* new_basename = NULL; - struct inode* new_inode = - vfs_resolve_path(newpath, &new_parent, &new_basename); - if (IS_OK(new_inode)) { - inode_unref(old_inode); - inode_unref(new_parent); - inode_unref(new_inode); - kfree(new_basename); - return -EEXIST; + struct path* new_path = vfs_resolve_path(new_pathname, O_ALLOW_NOENT); + if (IS_ERR(new_path)) { + path_destroy_recursive(old_path); + return PTR_ERR(new_path); } - if (IS_ERR(new_inode) && PTR_ERR(new_inode) != -ENOENT) { - inode_unref(old_inode); - inode_unref(new_parent); - kfree(new_basename); - return PTR_ERR(new_inode); + if (new_path->inode) { + rc = -EEXIST; + goto done; } - if (!new_parent) { - inode_unref(old_inode); - kfree(new_basename); - return -EPERM; + if (!new_path->parent) { + rc = -EPERM; + goto done; } - ASSERT(new_basename); - rc = inode_link_child(new_parent, new_basename, old_inode); - kfree(new_basename); + inode_ref(new_path->parent->inode); + inode_ref(old_path->inode); + rc = inode_link_child(new_path->parent->inode, new_path->basename, + old_path->inode); + +done: + path_destroy_recursive(new_path); + path_destroy_recursive(old_path); return rc; } @@ -219,25 +216,17 @@ int sys_unlink(const char* user_pathname) { if (IS_ERR(rc)) return rc; - struct inode* parent = NULL; - char* basename = NULL; - struct inode* inode = vfs_resolve_path(pathname, &parent, &basename); - if (IS_ERR(inode)) { - inode_unref(parent); - kfree(basename); - return PTR_ERR(inode); - } - if (!parent || S_ISDIR(inode->mode)) { - inode_unref(parent); - inode_unref(inode); - kfree(basename); + struct path* path = vfs_resolve_path(pathname, 0); + if (IS_ERR(path)) + return PTR_ERR(path); + if (!path->parent || S_ISDIR(path->inode->mode)) { + path_destroy_recursive(path); return -EPERM; } - ASSERT(basename); - inode_unref(inode); - rc = inode_unlink_child(parent, basename); - kfree(basename); + inode_ref(path->parent->inode); + rc = inode_unlink_child(path->parent->inode, path->basename); + path_destroy_recursive(path); return rc; } @@ -265,90 +254,66 @@ static int ensure_empty_directory(struct inode* inode) { } int sys_rename(const char* user_oldpath, const char* user_newpath) { - char oldpath[PATH_MAX]; - int rc = copy_pathname_from_user(oldpath, user_oldpath); + char old_pathname[PATH_MAX]; + int rc = copy_pathname_from_user(old_pathname, user_oldpath); if (IS_ERR(rc)) return rc; - char newpath[PATH_MAX]; - rc = copy_pathname_from_user(newpath, user_newpath); + char new_pathname[PATH_MAX]; + rc = copy_pathname_from_user(new_pathname, user_newpath); if (IS_ERR(rc)) return rc; - struct inode* old_parent = NULL; - char* old_basename = NULL; - struct inode* old_inode = NULL; - struct inode* new_parent = NULL; - char* new_basename = NULL; - struct inode* new_inode = NULL; - - old_inode = vfs_resolve_path(oldpath, &old_parent, &old_basename); - if (IS_ERR(old_inode)) { - rc = PTR_ERR(old_inode); - old_inode = NULL; - goto fail; + struct path* old_path = vfs_resolve_path(old_pathname, 0); + if (IS_ERR(old_path)) + + return PTR_ERR(old_path); + if (!old_path->parent) { + path_destroy_recursive(old_path); + return -EPERM; + } + + struct path* new_path = vfs_resolve_path(new_pathname, O_ALLOW_NOENT); + if (IS_ERR(new_path)) { + path_destroy_recursive(old_path); + return PTR_ERR(new_path); } - if (!old_parent) { + + if (new_path->inode == old_path->inode) + goto done; + + if (!new_path->parent) { rc = -EPERM; - goto fail; + goto done; } - ASSERT(old_basename); - new_inode = vfs_resolve_path(newpath, &new_parent, &new_basename); - if (IS_OK(new_inode)) { - if (new_inode == old_inode) { - rc = 0; - goto do_nothing; + if (S_ISDIR(new_path->inode->mode)) { + if (!S_ISDIR(old_path->inode->mode)) { + rc = -EISDIR; + goto done; } - if (S_ISDIR(new_inode->mode)) { - if (!S_ISDIR(old_inode->mode)) { - rc = -EISDIR; - goto fail; - } - rc = ensure_empty_directory(new_inode); - if (IS_ERR(rc)) { - new_inode = NULL; - goto fail; - } - } - inode_ref(new_parent); - rc = inode_unlink_child(new_parent, new_basename); + inode_ref(new_path->inode); + rc = ensure_empty_directory(new_path->inode); if (IS_ERR(rc)) - goto fail; - } else { - if (PTR_ERR(new_inode) != -ENOENT) { - rc = PTR_ERR(new_inode); - new_inode = NULL; - goto fail; - } - new_inode = NULL; - if (!new_parent) { - rc = -EPERM; - goto fail; - } + goto done; } - ASSERT(new_basename); + inode_ref(new_path->parent->inode); + rc = inode_unlink_child(new_path->parent->inode, new_path->basename); + if (IS_ERR(rc)) + goto done; - rc = inode_link_child(new_parent, new_basename, old_inode); - if (IS_ERR(rc)) { - old_inode = NULL; - new_parent = NULL; - goto fail; - } - kfree(new_basename); + inode_ref(new_path->parent->inode); + inode_ref(old_path->inode); + rc = inode_link_child(new_path->parent->inode, new_path->basename, + old_path->inode); + if (IS_ERR(rc)) + goto done; - rc = inode_unlink_child(old_parent, old_basename); - kfree(old_basename); - return rc; + inode_ref(old_path->parent->inode); + rc = inode_unlink_child(old_path->parent->inode, old_path->basename); -fail: - ASSERT(IS_ERR(rc)); -do_nothing: - inode_unref(old_parent); - inode_unref(old_inode); - kfree(old_basename); - inode_unref(new_parent); - inode_unref(new_inode); - kfree(new_basename); +done: + path_destroy_recursive(new_path); + path_destroy_recursive(old_path); return rc; } @@ -358,34 +323,26 @@ int sys_rmdir(const char* user_pathname) { if (IS_ERR(rc)) return rc; - struct inode* parent = NULL; - char* basename = NULL; - struct inode* inode = vfs_resolve_path(pathname, &parent, &basename); - if (IS_ERR(inode)) { - inode_unref(parent); - kfree(basename); - return PTR_ERR(inode); - } - if (!parent) { - inode_unref(inode); - kfree(basename); + struct path* path = vfs_resolve_path(pathname, 0); + if (IS_ERR(path)) + return PTR_ERR(path); + if (!path->parent) { + path_destroy_recursive(path); return -EPERM; } - ASSERT(basename); - if (!S_ISDIR(inode->mode)) { - inode_unref(parent); - inode_unref(inode); - kfree(basename); + if (!S_ISDIR(path->inode->mode)) { + path_destroy_recursive(path); return -ENOTDIR; } - rc = ensure_empty_directory(inode); + inode_ref(path->inode); + rc = ensure_empty_directory(path->inode); if (IS_ERR(rc)) { - inode_unref(parent); - kfree(basename); + path_destroy_recursive(path); return rc; } - rc = inode_unlink_child(parent, basename); - kfree(basename); + inode_ref(path->parent->inode); + rc = inode_unlink_child(path->parent->inode, path->basename); + path_destroy_recursive(path); return rc; } diff --git a/kernel/syscall/process.c b/kernel/syscall/process.c index dbac006d..e020e1fd 100644 --- a/kernel/syscall/process.c +++ b/kernel/syscall/process.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -95,8 +96,8 @@ pid_t sys_fork(registers* regs) { *child_regs = *regs; child_regs->eax = 0; // fork() returns 0 in the child - process->cwd_path = kstrdup(current->cwd_path); - if (!process->cwd_path) { + process->cwd = path_dup(current->cwd); + if (!process->cwd) { kfree(stack); kfree(process); return -ENOMEM; @@ -105,7 +106,7 @@ pid_t sys_fork(registers* regs) { int rc = file_descriptor_table_clone_from(&process->fd_table, ¤t->fd_table); if (IS_ERR(rc)) { - kfree(process->cwd_path); + path_destroy_recursive(process->cwd); kfree(stack); kfree(process); return rc; @@ -115,15 +116,12 @@ pid_t sys_fork(registers* regs) { process->pd = paging_clone_current_page_directory(); if (IS_ERR(process->pd)) { file_descriptor_table_destroy(&process->fd_table); - kfree(process->cwd_path); + path_destroy_recursive(process->cwd); kfree(stack); kfree(process); return PTR_ERR(process->pd); } - process->cwd_inode = current->cwd_inode; - inode_ref(process->cwd_inode); - scheduler_register(process); return process->pid; } @@ -242,12 +240,21 @@ char* sys_getcwd(char* user_buf, size_t size) { if (!user_buf || size == 0) return ERR_PTR(-EINVAL); - size_t cwd_path_len = strlen(current->cwd_path); - if (size < cwd_path_len + 1) + char* cwd_str = path_to_string(current->cwd); + if (!cwd_str) + return ERR_PTR(-ENOMEM); + + size_t len = strlen(cwd_str) + 1; + if (size < len) { + kfree(cwd_str); return ERR_PTR(-ERANGE); - if (!copy_to_user(user_buf, current->cwd_path, size)) + } + if (!copy_to_user(user_buf, cwd_str, len)) { + kfree(cwd_str); return ERR_PTR(-EFAULT); + } + kfree(cwd_str); return user_buf; } @@ -259,25 +266,17 @@ int sys_chdir(const char* user_path) { if (path_len >= PATH_MAX) return -ENAMETOOLONG; - char* new_cwd_path = vfs_canonicalize_path(path); - if (IS_ERR(new_cwd_path)) - return PTR_ERR(new_cwd_path); + struct path* new_cwd = vfs_resolve_path_at(current->cwd, path, 0); + if (IS_ERR(new_cwd)) + return PTR_ERR(new_cwd); - struct inode* inode = vfs_resolve_path(path, NULL, NULL); - if (IS_ERR(inode)) { - kfree(new_cwd_path); - return PTR_ERR(inode); - } - if (!S_ISDIR(inode->mode)) { - kfree(new_cwd_path); - inode_unref(inode); + if (!S_ISDIR(new_cwd->inode->mode)) { + path_destroy_recursive(new_cwd); return -ENOTDIR; } - kfree(current->cwd_path); - inode_unref(current->cwd_inode); - current->cwd_path = new_cwd_path; - current->cwd_inode = inode; + path_destroy_recursive(current->cwd); + current->cwd = new_cwd; return 0; }