📄 fs.c
字号:
/* Copyright (C) 2005 David Decotigny Copyright (C) 2000-2005 The KOS Team (Thomas Petazzoni, David Decotigny, Julien Munier) 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 2 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, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */#include <sos/assert.h>#include <sos/list.h>#include <sos/kmem_slab.h>#include <sos/kmalloc.h>#include "fs.h"/** List of available filesystems registered in the system */static struct sos_fs_manager_type * fs_list = NULL;/** Last UID delivered for the FS instances */static sos_ui64_t last_fs_instance_uid;/* ********************************************************** * Forward declarations */static sos_ret_t fs_fetch_node(struct sos_fs_manager_instance *fs, sos_ui64_t storage_location, struct sos_fs_node ** result_fsnode);static sos_ret_tfs_allocate_node(struct sos_fs_manager_instance * fs, sos_fs_node_type_t type, sos_ui32_t flags, const struct sos_process * creator, sos_ui32_t access_rights, struct sos_fs_node ** result_fsnode);static sos_ret_t mark_dirty_fsnode(struct sos_fs_node * fsnode, sos_bool_t force_sync);static sos_ret_t sos_fs_sync_node(struct sos_fs_node * fsnode);static sos_ret_t sos_fs_sync_fs(struct sos_fs_manager_instance * fs);static sos_ret_tfs_lookup_node(const struct sos_fs_pathname * path, sos_bool_t follow_symlinks, const struct sos_fs_nscache_node * root_nsnode, const struct sos_fs_nscache_node * start_nsnode, struct sos_fs_nscache_node ** result_nsnode, struct sos_fs_pathname * result_remaining_path, int lookup_recursion_level);static sos_ret_tfs_resolve_symlink(const struct sos_fs_nscache_node * root_nsnode, const struct sos_fs_nscache_node * symlink_nsnode, struct sos_fs_nscache_node ** target_nsnode, int lookup_recursion_level);static sos_ret_tfs_register_child_node(const struct sos_process * creator, struct sos_fs_nscache_node * parent_nsnode, const struct sos_fs_pathname * name, struct sos_fs_node * fsnode, sos_ui32_t flags, struct sos_fs_nscache_node ** result_nsnode);static sos_ret_tfs_create_child_node(struct sos_fs_nscache_node * parent_nsnode, const struct sos_fs_pathname * name, sos_fs_node_type_t type, sos_ui32_t flags, const struct sos_process * creator, sos_ui32_t access_rights, struct sos_fs_nscache_node ** result_nsnode);static sos_ret_tfs_connect_existing_child_node(const struct sos_process * creator, struct sos_fs_nscache_node * parent_nsnode, const struct sos_fs_pathname * name, struct sos_fs_nscache_node * nsnode);static sos_ret_tfs_create_node(const struct sos_fs_pathname * _path, const struct sos_process * creator, sos_ui32_t access_rights, sos_fs_node_type_t type, struct sos_fs_nscache_node ** result_nsnode);static sos_ret_tfs_remove_node(const struct sos_process * actor, struct sos_fs_nscache_node * nsnode);/* ********************************************************** * Basic low-level memory & co related functions */sos_ret_t sos_fs_subsystem_setup(const char * root_device, const char * fsname, const char * mount_args, struct sos_fs_manager_instance ** result_rootfs){ sos_ret_t retval; struct sos_fs_manager_type * fs_type; struct sos_fs_manager_instance * new_fs; struct sos_fs_node * rdev_fsnode; int nb_fstypes; /* root_device is ignored for now */ rdev_fsnode = NULL; last_fs_instance_uid = 0; *result_rootfs = NULL; retval = sos_fs_nscache_subsystem_setup(); if (SOS_OK != retval) return retval; /* Look for the FS manager type */ list_foreach(fs_list, fs_type, nb_fstypes) { if (! strcmp(fsname, fs_type->name)) break; } if (! list_foreach_early_break(fs_list, fs_type, nb_fstypes)) return -SOS_ENODEV; retval = fs_type->mount(fs_type, rdev_fsnode, mount_args, & new_fs); if (SOS_OK != retval) { if (rdev_fsnode) sos_fs_unref_fsnode(rdev_fsnode); return retval; } /* Update some reserved fields */ sos_fs_nscache_get_fs_node(new_fs->root)->fs = new_fs; *result_rootfs = new_fs; return SOS_OK;}sos_ret_t sos_fs_ref_fsnode(struct sos_fs_node * fsnode){ fsnode->inmem_ref_cnt ++; return SOS_OK;}sos_ret_t _sos_fs_unref_fsnode(struct sos_fs_node * node){ SOS_ASSERT_FATAL(node->inmem_ref_cnt > 0); /* Commit the changes the the FS when the last reference is being removed */ if ((node->inmem_ref_cnt == 1) && (node->dirty)) { SOS_ASSERT_FATAL(SOS_OK == sos_fs_sync_node(node)); } node->inmem_ref_cnt --; if (node->inmem_ref_cnt == 0) { sos_hash_remove(node->fs->nodecache, node); node->destructor(node); } return SOS_OK;}sos_ret_t sos_fs_ref_opened_file(struct sos_fs_opened_file * of){ of->ref_cnt ++; return SOS_OK;}sos_ret_t _sos_fs_unref_opened_file(struct sos_fs_opened_file ** _of){ struct sos_fs_opened_file * of = *_of; *_of = NULL; SOS_ASSERT_FATAL(of->ref_cnt > 0); of->ref_cnt --; if (0 == of->ref_cnt) { sos_ret_t retval; struct sos_fs_nscache_node * nsnode = of->direntry; struct sos_fs_node * fsnode = sos_fs_nscache_get_fs_node(nsnode); retval = fsnode->close_opened_file(fsnode, of); if (SOS_OK != retval) return retval; return sos_fs_nscache_unref_node(nsnode); } return SOS_OK;}/* ********************************************************** * Some helper functions *//** Fetch the given fsnode from hash first, or from disk when not in hash */static sos_ret_t fs_fetch_node(struct sos_fs_manager_instance *fs, sos_ui64_t storage_location, struct sos_fs_node ** result_fsnode){ sos_ret_t retval; /* If it is already loaded in memory, no need to look further */ *result_fsnode = (struct sos_fs_node*) sos_hash_lookup(fs->nodecache, & storage_location); if (*result_fsnode) return SOS_OK; /* Otherwise, call the appropriate method of the FS */ retval = fs->fetch_node_from_disk(fs, storage_location, result_fsnode); if (SOS_OK != retval) return retval; (*result_fsnode)->generation = 0; sos_hash_insert(fs->nodecache, *result_fsnode); return SOS_OK;}/** * Helper function to allocate a new on-disk node */static sos_ret_tfs_allocate_node(struct sos_fs_manager_instance * fs, sos_fs_node_type_t type, sos_ui32_t flags, const struct sos_process * creator, sos_ui32_t access_rights, struct sos_fs_node ** result_fsnode){ sos_ret_t retval; /* Make sure FS is writable ! */ if (fs->flags & SOS_FS_MOUNT_READONLY) return -SOS_EPERM; /* Allocate the node on disk */ retval = fs->allocate_new_node(fs, type, creator, access_rights, flags, result_fsnode); if (SOS_OK != retval) return retval; /* Update some resrved fields */ (*result_fsnode)->fs = fs; /* insert it in the node cache */ retval = sos_hash_insert(fs->nodecache, *result_fsnode); if (SOS_OK != retval) { sos_fs_unref_fsnode(*result_fsnode); return retval; } /* Success: Consider the node as dirty */ mark_dirty_fsnode(*result_fsnode, FALSE); return retval;}/** Helper function to add the given node in the dirty list, or to write it directly to the disk if the system is mounted in SYNC mode */static sos_ret_t mark_dirty_fsnode(struct sos_fs_node * fsnode, sos_bool_t force_sync){ sos_ret_t retval; sos_bool_t was_dirty = fsnode->dirty; fsnode->dirty = TRUE; fsnode->generation ++; retval = SOS_OK; /* If the fsnode is newly dirty, add it to the dirty list of the FS */ if (!was_dirty && fsnode->dirty) list_add_tail_named(fsnode->fs->dirty_nodes, fsnode, prev_dirty, next_dirty); if (force_sync || (fsnode->fs->flags & SOS_FS_MOUNT_SYNC)) { /* Commit the node changes to the FS */ if (SOS_OK == sos_fs_sync_node(fsnode)) { /* Commit the FS changes to the device */ if (SOS_OK == fsnode->fs->device->ops_file->sync(fsnode->fs->device)) return SOS_OK; /* We got a problem: FORCE re-add the node to the dirty list */ was_dirty = FALSE; fsnode->dirty = TRUE; retval = -SOS_EBUSY; } } return retval;}/** Remove the given node from the dirty list of the FS */static sos_ret_t sos_fs_sync_node(struct sos_fs_node * fsnode){ sos_ret_t retval; if (! fsnode->dirty) return SOS_OK; retval = fsnode->ops_file->sync(fsnode); if (SOS_OK != retval) return retval; /* Remove it from the dirty list */ list_delete_named(fsnode->fs->dirty_nodes, fsnode, prev_dirty, next_dirty); fsnode->dirty = FALSE; return SOS_OK;}/** Collapse the whole dirty list of the FS and commit the changes to the underlying device */static sos_ret_t sos_fs_sync_fs(struct sos_fs_manager_instance * fs){ struct sos_fs_node * fsnode; while (NULL != (fsnode = list_get_head_named(fs->dirty_nodes, prev_dirty, next_dirty))) { sos_ret_t retval = sos_fs_sync_node(fsnode); if (SOS_OK != retval) return retval; } if (NULL != fs->device) return fs->device->ops_file->sync(fs->device); return SOS_OK;}/** * Resolve the given symlink: return the nsnode for the destination * of the symlink, or error status for dangling symlinks * * @note result_nsnode is a NEW reference to the node. It should be * unreferenced when unused */static sos_ret_tfs_resolve_symlink(const struct sos_fs_nscache_node * root_nsnode, const struct sos_fs_nscache_node * symlink_nsnode, struct sos_fs_nscache_node ** target_nsnode, int lookup_recursion_level){ sos_ret_t retval; const struct sos_fs_nscache_node * start_nsnode; struct sos_fs_node * symlink_fsnode; struct sos_fs_pathname path; struct sos_fs_pathname remaining; symlink_fsnode = sos_fs_nscache_get_fs_node(symlink_nsnode); retval = symlink_fsnode->ops_symlink->expand(symlink_fsnode, & path.contents, & path.length); if (SOS_OK != retval) return retval; if (path.length <= 0) return -SOS_ENOENT; /* Absolute path for target ? */ if (path.contents[0] == '/') start_nsnode = root_nsnode; else { retval = sos_fs_nscache_get_parent(symlink_nsnode, (struct sos_fs_nscache_node**)& start_nsnode); if (SOS_OK != retval) return retval; } retval = fs_lookup_node(& path, TRUE, root_nsnode, start_nsnode, target_nsnode, & remaining, lookup_recursion_level); if (SOS_OK != retval) return retval; /* The target of the symlink could not be completely opened ! */ if (remaining.length != 0) { sos_fs_nscache_unref_node(*target_nsnode); return -SOS_ENOENT; } return SOS_OK;}#define MAX_LOOKUP_RECURSION_LEVEL 5/** * @return OK in any case, except if 1/ a symlink could not be * resolved, or 2/ a path "a/b" is given where "a" is not a directory, * or 3/ a fsnode that should exist could not be retrieved from disk. * * @param result_remaining_path contains the path that could not be resolved * * @param result_nsnode a NEW reference to the farthest node that * could be resolved. It should be unreferenced when unused */static sos_ret_tfs_lookup_node(const struct sos_fs_pathname * path, sos_bool_t follow_symlinks, const struct sos_fs_nscache_node * root_nsnode, const struct sos_fs_nscache_node * start_nsnode, struct sos_fs_nscache_node ** result_nsnode, struct sos_fs_pathname * result_remaining_path, int lookup_recursion_level){ sos_ret_t retval; struct sos_fs_nscache_node * current_nsnode; /* Make sure we did not go too deep while resolving symlinks */ lookup_recursion_level ++; if (lookup_recursion_level > MAX_LOOKUP_RECURSION_LEVEL) { return -SOS_ELOOP; } if (path->length <= 0) return -SOS_ENOENT; *result_nsnode = NULL; memcpy(result_remaining_path, path, sizeof(*path)); current_nsnode = (struct sos_fs_nscache_node *)start_nsnode; sos_fs_nscache_ref_node(current_nsnode); while (1) { struct sos_fs_pathname current_component, remaining; struct sos_fs_nscache_node * next_nsnode = NULL; sos_bool_t slashes_after_first_component;/* dbg_dump_pathname("Before", result_remaining_path); */ /* Extract the next component of the path */ slashes_after_first_component = sos_fs_pathname_split_path(result_remaining_path, & current_component, & remaining);/* dbg_dump_pathname("After", result_remaining_path); *//* dbg_dump_pathname("Cur", & current_component); *//* dbg_dump_pathname("Rem", & remaining); *//* sos_bochs_printf("Slash after=%d\n", slashes_after_first_component); */ /* Could resolve the whole path ? */ if (current_component.length == 0) { /* Ok, fine, we got it ! */ memcpy(result_remaining_path, & remaining, sizeof(remaining)); *result_nsnode = current_nsnode; return SOS_OK; } /* Otherwise: make sure we reached a DIR node */ if (sos_fs_nscache_get_fs_node(current_nsnode)->type != SOS_FS_NODE_DIRECTORY) { sos_fs_nscache_unref_node(current_nsnode); return -SOS_ENOENT; } /* Make sure this directory is "executable" */ if (! (sos_fs_nscache_get_fs_node(current_nsnode)->access_rights & SOS_FS_EXECUTABLE) ) { sos_fs_nscache_unref_node(current_nsnode); return -SOS_EACCES; } /* If we can find the entry in the namespace cache, it is really fine ! */ retval = sos_fs_nscache_lookup(current_nsnode, & current_component, root_nsnode, & next_nsnode); if (SOS_OK != retval) { struct sos_fs_node * current_fsnode, * next_fsnode; sos_ui64_t storage_location; /* * Not found in the namespace cache. Must read from the
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -