⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 fs.c

📁 Simple Operating Systems (简称SOS)是一个可以运行在X86平台上(包括QEMU
💻 C
📖 第 1 页 / 共 4 页
字号:
/* 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 + -