2264 lines
58 KiB
C
2264 lines
58 KiB
C
|
/* Copyright (C) 2005,2006 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 <sos/syscall.h> /* For the FCNTL commands */
|
||
|
#include "chardev.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_t
|
||
|
fs_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_t
|
||
|
fs_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_t
|
||
|
fs_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_t
|
||
|
fs_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_t
|
||
|
fs_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_t
|
||
|
fs_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_t
|
||
|
fs_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_t
|
||
|
fs_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)
|
||
|
{
|
||
|
if (SOS_FS_NODE_DEVICE_CHAR == node->type)
|
||
|
sos_chardev_helper_release_fsnode(node);
|
||
|
else if (SOS_FS_NODE_DEVICE_BLOCK == node->type)
|
||
|
sos_blockdev_helper_release_fsnode(node);
|
||
|
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;
|
||
|
|
||
|
/* Special case for device nodes */
|
||
|
if (SOS_FS_NODE_DEVICE_CHAR == (*result_fsnode)->type)
|
||
|
SOS_ASSERT_FATAL(SOS_OK
|
||
|
== sos_chardev_helper_ref_new_fsnode(*result_fsnode));
|
||
|
else if (SOS_FS_NODE_DEVICE_BLOCK == (*result_fsnode)->type)
|
||
|
SOS_ASSERT_FATAL(SOS_OK
|
||
|
== sos_blockdev_helper_ref_new_fsnode(*result_fsnode));
|
||
|
|
||
|
sos_hash_insert(fs->nodecache, *result_fsnode);
|
||
|
return SOS_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Helper function to allocate a new on-disk node
|
||
|
*/
|
||
|
static sos_ret_t
|
||
|
fs_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;
|
||
|
|
||
|
/* Special case for device nodes */
|
||
|
if (SOS_FS_NODE_DEVICE_CHAR == (*result_fsnode)->type)
|
||
|
{
|
||
|
SOS_ASSERT_FATAL(SOS_OK
|
||
|
== sos_chardev_helper_ref_new_fsnode(*result_fsnode));
|
||
|
}
|
||
|
else if (SOS_FS_NODE_DEVICE_BLOCK == (*result_fsnode)->type)
|
||
|
{
|
||
|
SOS_ASSERT_FATAL(SOS_OK
|
||
|
== sos_blockdev_helper_ref_new_fsnode(*result_fsnode));
|
||
|
}
|
||
|
|
||
|
|
||
|
/* 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))
|
||
|
return sos_fs_sync_node(fsnode);
|
||
|
|
||
|
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;
|
||
|
|
||
|
/* Commit the FS changes to the device */
|
||
|
if (fsnode->fs->device)
|
||
|
{
|
||
|
retval = sos_blockdev_helper_sync_fsnode(fsnode->fs->device);
|
||
|
if (SOS_OK != retval)
|
||
|
{
|
||
|
/* We got a problem ! Mark the node dirty again */
|
||
|
list_add_tail_named(fsnode->fs->dirty_nodes, fsnode,
|
||
|
prev_dirty, next_dirty);
|
||
|
fsnode->dirty = TRUE;
|
||
|
return retval;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
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_t
|
||
|
fs_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_t
|
||
|
fs_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
|
||
|
* disk...
|
||
|
*/
|
||
|
current_fsnode = sos_fs_nscache_get_fs_node(current_nsnode);
|
||
|
|
||
|
retval = current_fsnode->ops_dir
|
||
|
->lookup(current_fsnode,
|
||
|
current_component.contents,
|
||
|
current_component.length,
|
||
|
& storage_location);
|
||
|
if (SOS_OK != retval)
|
||
|
{
|
||
|
/* Well, we cannot go further, stop here */
|
||
|
*result_nsnode = current_nsnode;
|
||
|
return SOS_OK;
|
||
|
}
|
||
|
|
||
|
/* Now retrieve this node from disk or from the cache into
|
||
|
memory */
|
||
|
retval = fs_fetch_node(current_fsnode->fs,
|
||
|
storage_location, & next_fsnode);
|
||
|
if (SOS_OK != retval)
|
||
|
{
|
||
|
sos_fs_nscache_unref_node(current_nsnode);
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
/* Integrate it in the nscache */
|
||
|
retval = sos_fs_nscache_add_new_child_node(current_nsnode,
|
||
|
& current_component,
|
||
|
next_fsnode,
|
||
|
& next_nsnode);
|
||
|
sos_fs_nscache_unref_node(current_nsnode);
|
||
|
if (SOS_OK != retval)
|
||
|
return retval;
|
||
|
}
|
||
|
else
|
||
|
sos_fs_nscache_unref_node(current_nsnode);
|
||
|
|
||
|
/* Reaching a symlink ? */
|
||
|
if (sos_fs_nscache_get_fs_node(next_nsnode)->type
|
||
|
== SOS_FS_NODE_SYMLINK)
|
||
|
{
|
||
|
/* Expand the link only for non-terminal nodes, or for the
|
||
|
terminal node only if follow_symlinks is TRUE */
|
||
|
if ( (remaining.length != 0)
|
||
|
|| follow_symlinks )
|
||
|
{
|
||
|
struct sos_fs_nscache_node * symlink_target;
|
||
|
|
||
|
retval = fs_resolve_symlink(root_nsnode, next_nsnode,
|
||
|
& symlink_target,
|
||
|
lookup_recursion_level);
|
||
|
sos_fs_nscache_unref_node(next_nsnode);
|
||
|
if (SOS_OK != retval)
|
||
|
return retval; /* Dangling symlink */
|
||
|
|
||
|
next_nsnode = symlink_target;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Make sure there was no slash after this component, unless
|
||
|
this component is a directory */
|
||
|
if (slashes_after_first_component
|
||
|
&&
|
||
|
( sos_fs_nscache_get_fs_node(next_nsnode)->type
|
||
|
!= SOS_FS_NODE_DIRECTORY) )
|
||
|
{
|
||
|
sos_fs_nscache_unref_node(next_nsnode);
|
||
|
return -SOS_ENOTDIR;
|
||
|
}
|
||
|
|
||
|
/* Ok, fine, we got it, update the path we still have to explore */
|
||
|
memcpy(result_remaining_path, & remaining, sizeof(remaining));
|
||
|
current_nsnode = next_nsnode;
|
||
|
}
|
||
|
|
||
|
sos_display_fatal_error("Should not get there");
|
||
|
return -SOS_EFATAL;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* It is assumed that parent does not already have a child with the
|
||
|
* given name. We make sure that the "path" is a single entity (ie
|
||
|
* not "a/b")
|
||
|
* @return Error if fsnode is not on the same FS as parent_nsnode
|
||
|
*/
|
||
|
static sos_ret_t
|
||
|
fs_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)
|
||
|
{
|
||
|
sos_ret_t retval;
|
||
|
struct sos_fs_node * parent_fsnode;
|
||
|
struct sos_fs_pathname first_component, remaining;
|
||
|
sos_bool_t slashes_after_first_component = FALSE;
|
||
|
|
||
|
*result_nsnode = NULL;
|
||
|
|
||
|
parent_fsnode = sos_fs_nscache_get_fs_node(parent_nsnode);
|
||
|
if (parent_fsnode->type != SOS_FS_NODE_DIRECTORY)
|
||
|
return -SOS_ENOTDIR;
|
||
|
|
||
|
if (name->length <= 0)
|
||
|
return -SOS_EINVAL;
|
||
|
|
||
|
slashes_after_first_component
|
||
|
= sos_fs_pathname_split_path(name, & first_component, & remaining);
|
||
|
|
||
|
if (fsnode->type != SOS_FS_NODE_DIRECTORY)
|
||
|
{
|
||
|
/* Make sure the given name is exactly a single path component
|
||
|
(ie no '/') */
|
||
|
if (slashes_after_first_component)
|
||
|
return -SOS_EINVAL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* Make sure there aren't any other component behind the '/'s, if
|
||
|
any */
|
||
|
if (remaining.length > 0)
|
||
|
return -SOS_EINVAL;
|
||
|
}
|
||
|
|
||
|
/* Make sure the parent directory is writeable */
|
||
|
if (! (parent_fsnode->access_rights & SOS_FS_WRITABLE) )
|
||
|
return -SOS_EACCES;
|
||
|
|
||
|
/* Make sure that the entries are located on the same FS */
|
||
|
if (fsnode->fs != parent_fsnode->fs)
|
||
|
return -SOS_EXDEV;
|
||
|
|
||
|
/* Make sure that the nsnode won't be destroyed */
|
||
|
sos_fs_nscache_ref_node(parent_nsnode);
|
||
|
|
||
|
/* Allocate the node in directory */
|
||
|
retval = parent_fsnode->ops_dir->link(parent_fsnode,
|
||
|
creator,
|
||
|
first_component.contents,
|
||
|
first_component.length,
|
||
|
fsnode);
|
||
|
if (SOS_OK != retval)
|
||
|
{
|
||
|
sos_fs_nscache_unref_node(parent_nsnode);
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
/* Success: Consider the directory as dirty */
|
||
|
mark_dirty_fsnode(parent_fsnode, FALSE);
|
||
|
|
||
|
/* Allocate the node in nscache cache */
|
||
|
retval = sos_fs_nscache_add_new_child_node(parent_nsnode, & first_component,
|
||
|
fsnode, result_nsnode);
|
||
|
|
||
|
sos_fs_nscache_unref_node(parent_nsnode);
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
|
||
|
/** It is assumed that parent does not already have a child with the
|
||
|
given name. We make sure that the "path" is a single entity (ie
|
||
|
not "a/b"). Return a NEW reference to the newly-created NS node */
|
||
|
static sos_ret_t
|
||
|
fs_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)
|
||
|
{
|
||
|
sos_ret_t retval;
|
||
|
struct sos_fs_node * fsnode, * parent_fsnode;
|
||
|
|
||
|
parent_fsnode = sos_fs_nscache_get_fs_node(parent_nsnode);
|
||
|
if (parent_fsnode->type != SOS_FS_NODE_DIRECTORY)
|
||
|
return -SOS_ENOTDIR;
|
||
|
|
||
|
/* Make sure that the nsnode won't be destroyed */
|
||
|
sos_fs_nscache_ref_node(parent_nsnode);
|
||
|
|
||
|
retval = fs_allocate_node(parent_fsnode->fs, type, flags, creator,
|
||
|
access_rights, & fsnode);
|
||
|
if (SOS_OK != retval)
|
||
|
{
|
||
|
sos_fs_nscache_unref_node(parent_nsnode);
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
retval = fs_register_child_node(creator,
|
||
|
parent_nsnode, name, fsnode, flags,
|
||
|
result_nsnode);
|
||
|
sos_fs_nscache_unref_node(parent_nsnode);
|
||
|
|
||
|
/* The function does not need it anymore */
|
||
|
sos_fs_unref_fsnode(fsnode);
|
||
|
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* It is assumed that parent does not already have a child with the
|
||
|
* given name, and that the new child does not have a parent yet. We
|
||
|
* make sure that the "path" is a single entity (ie not "a/b") @return
|
||
|
* Error if fsnode is not on the same FS as parent_nsnode
|
||
|
*/
|
||
|
static sos_ret_t
|
||
|
fs_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)
|
||
|
{
|
||
|
sos_ret_t retval;
|
||
|
struct sos_fs_node * parent_fsnode, * fsnode;
|
||
|
struct sos_fs_pathname first_component, remaining;
|
||
|
sos_bool_t slashes_after_first_component = FALSE;
|
||
|
|
||
|
fsnode = sos_fs_nscache_get_fs_node(nsnode);
|
||
|
parent_fsnode = sos_fs_nscache_get_fs_node(parent_nsnode);
|
||
|
if (parent_fsnode->type != SOS_FS_NODE_DIRECTORY)
|
||
|
return -SOS_ENOTDIR;
|
||
|
|
||
|
if (name->length <= 0)
|
||
|
return -SOS_EINVAL;
|
||
|
|
||
|
slashes_after_first_component
|
||
|
= sos_fs_pathname_split_path(name, & first_component, & remaining);
|
||
|
|
||
|
if (fsnode->type != SOS_FS_NODE_DIRECTORY)
|
||
|
{
|
||
|
/* Make sure the given name is exactly a single path component
|
||
|
(ie no '/') */
|
||
|
if (slashes_after_first_component)
|
||
|
return -SOS_EINVAL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* Make sure there aren't any other component behind the '/'s, if
|
||
|
any */
|
||
|
if (remaining.length > 0)
|
||
|
return -SOS_EINVAL;
|
||
|
}
|
||
|
|
||
|
/* Make sure the parent directory is writeable */
|
||
|
if (! (parent_fsnode->access_rights & SOS_FS_WRITABLE) )
|
||
|
return -SOS_EACCES;
|
||
|
|
||
|
/* Make sure that the entries are located on the same FS */
|
||
|
if (fsnode->fs != parent_fsnode->fs)
|
||
|
return -SOS_EXDEV;
|
||
|
|
||
|
/* Make sure that the nsnode won't be destroyed */
|
||
|
sos_fs_nscache_ref_node(parent_nsnode);
|
||
|
|
||
|
/* Allocate the node in directory */
|
||
|
retval = parent_fsnode->ops_dir->link(parent_fsnode,
|
||
|
creator,
|
||
|
first_component.contents,
|
||
|
first_component.length,
|
||
|
fsnode);
|
||
|
if (SOS_OK != retval)
|
||
|
{
|
||
|
sos_fs_nscache_unref_node(parent_nsnode);
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
/* Success: Consider the directory as dirty */
|
||
|
mark_dirty_fsnode(parent_fsnode, FALSE);
|
||
|
|
||
|
/* Allocate the node in nscache cache */
|
||
|
retval = sos_fs_nscache_add_existing_child_node(parent_nsnode,
|
||
|
& first_component,
|
||
|
nsnode);
|
||
|
sos_fs_nscache_unref_node(parent_nsnode);
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
|
||
|
/** Return a new reference to the new node inserted unless
|
||
|
result_nsnode is NULL */
|
||
|
static sos_ret_t
|
||
|
fs_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)
|
||
|
{
|
||
|
sos_ret_t retval;
|
||
|
struct sos_fs_pathname path;
|
||
|
struct sos_fs_nscache_node *nsnode, *new_nsnode;
|
||
|
|
||
|
path.contents = _path->contents;
|
||
|
path.length = _path->length;
|
||
|
|
||
|
if (path.length <= 0)
|
||
|
return -SOS_ENOENT;
|
||
|
|
||
|
if (path.contents[0] == '/')
|
||
|
nsnode = sos_process_get_root(creator)->direntry;
|
||
|
else
|
||
|
nsnode = sos_process_get_cwd(creator)->direntry;
|
||
|
|
||
|
retval = fs_lookup_node(& path,
|
||
|
TRUE,
|
||
|
sos_process_get_root(creator)->direntry,
|
||
|
nsnode,
|
||
|
& nsnode,
|
||
|
& path,
|
||
|
0);
|
||
|
if (SOS_OK != retval)
|
||
|
return retval;
|
||
|
|
||
|
if (path.length <= 0)
|
||
|
{
|
||
|
/* Found the exact match ! */
|
||
|
sos_fs_nscache_unref_node(nsnode);
|
||
|
return -SOS_EEXIST;
|
||
|
}
|
||
|
|
||
|
/* Create a new entry in the file system */
|
||
|
retval = fs_create_child_node(nsnode,
|
||
|
& path,
|
||
|
type,
|
||
|
/* flags */0,
|
||
|
creator, access_rights,
|
||
|
& new_nsnode);
|
||
|
sos_fs_nscache_unref_node(nsnode);
|
||
|
|
||
|
/* node not needed by this function ? */
|
||
|
if (NULL == result_nsnode)
|
||
|
sos_fs_nscache_unref_node(new_nsnode);
|
||
|
else
|
||
|
*result_nsnode = new_nsnode;
|
||
|
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
|
||
|
static sos_ret_t
|
||
|
fs_remove_node(const struct sos_process * actor,
|
||
|
struct sos_fs_nscache_node * nsnode)
|
||
|
{
|
||
|
sos_ret_t retval;
|
||
|
struct sos_fs_nscache_node * parent_nsnode;
|
||
|
struct sos_fs_node * parent_fsnode;
|
||
|
struct sos_fs_pathname childname;
|
||
|
|
||
|
/* Refuse to do anything if this is the root of a mounted FS */
|
||
|
if (nsnode == sos_fs_nscache_get_fs_node(nsnode)->fs->root)
|
||
|
return -SOS_EBUSY;
|
||
|
|
||
|
retval = sos_fs_nscache_get_parent(nsnode, & parent_nsnode);
|
||
|
if (SOS_OK != retval)
|
||
|
return retval;
|
||
|
parent_fsnode = sos_fs_nscache_get_fs_node(parent_nsnode);
|
||
|
|
||
|
/* Make sure FS is writable ! */
|
||
|
if (parent_fsnode->fs->flags & SOS_FS_MOUNT_READONLY)
|
||
|
return -SOS_EPERM;
|
||
|
|
||
|
/* Make sure the parent directory is writeable */
|
||
|
if (! (parent_fsnode->access_rights & SOS_FS_WRITABLE) )
|
||
|
return -SOS_EACCES;
|
||
|
|
||
|
sos_fs_nscache_ref_node(parent_nsnode);
|
||
|
|
||
|
sos_fs_nscache_get_name(nsnode, & childname);
|
||
|
retval = parent_fsnode->ops_dir->unlink(parent_fsnode, actor,
|
||
|
childname.contents,
|
||
|
childname.length);
|
||
|
if (SOS_OK == retval)
|
||
|
sos_fs_nscache_disconnect_node(nsnode);
|
||
|
|
||
|
/* Unallocate the node */
|
||
|
if (SOS_OK == retval)
|
||
|
mark_dirty_fsnode(parent_fsnode, FALSE);
|
||
|
|
||
|
sos_fs_nscache_unref_node(parent_nsnode);
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* **********************************************************
|
||
|
* Exported functions
|
||
|
*/
|
||
|
|
||
|
sos_ret_t sos_fs_new_opened_file(const struct sos_process * owner,
|
||
|
struct sos_fs_nscache_node * nsnode,
|
||
|
sos_ui32_t open_flags,
|
||
|
struct sos_fs_opened_file ** result_of)
|
||
|
{
|
||
|
sos_ret_t retval;
|
||
|
struct sos_fs_node * fsnode = sos_fs_nscache_get_fs_node(nsnode);
|
||
|
|
||
|
retval = fsnode->new_opened_file(fsnode, owner, open_flags, result_of);
|
||
|
if (SOS_OK != retval)
|
||
|
return retval;
|
||
|
|
||
|
(*result_of)->ref_cnt = 1;
|
||
|
(*result_of)->generation = 1;
|
||
|
|
||
|
retval = sos_fs_nscache_register_opened_file(nsnode, *result_of);
|
||
|
if (SOS_OK != retval)
|
||
|
{
|
||
|
fsnode->close_opened_file(fsnode, *result_of);
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
(*result_of)->open_flags = open_flags;
|
||
|
return SOS_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
sos_ret_t
|
||
|
sos_fs_duplicate_opened_file(struct sos_fs_opened_file * src_of,
|
||
|
const struct sos_process * dst_proc,
|
||
|
struct sos_fs_opened_file ** result_of)
|
||
|
{
|
||
|
sos_ret_t retval = src_of->duplicate(src_of, dst_proc, result_of);
|
||
|
if (SOS_OK != retval)
|
||
|
return retval;
|
||
|
|
||
|
SOS_ASSERT_FATAL((*result_of)->owner == dst_proc);
|
||
|
(*result_of)->ref_cnt = 1;
|
||
|
(*result_of)->generation = 1;
|
||
|
retval = sos_fs_nscache_register_opened_file(src_of->direntry, *result_of);
|
||
|
if (SOS_OK != retval)
|
||
|
{
|
||
|
struct sos_fs_node * fsnode = sos_fs_nscache_get_fs_node(src_of->direntry);
|
||
|
fsnode->close_opened_file(fsnode, *result_of);
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
|
||
|
sos_ret_t sos_fs_open(const struct sos_process *owner,
|
||
|
const char *_path,
|
||
|
sos_size_t _pathlen,
|
||
|
sos_ui32_t open_flags,
|
||
|
sos_ui32_t creat_access_rights,
|
||
|
struct sos_fs_opened_file ** of)
|
||
|
{
|
||
|
sos_ret_t retval;
|
||
|
struct sos_fs_nscache_node *nsnode;
|
||
|
struct sos_fs_node * fsnode;
|
||
|
struct sos_fs_pathname path;
|
||
|
|
||
|
/* O_DIR | O_CREAT combination not supported */
|
||
|
if ( (open_flags & SOS_FS_OPEN_DIRECTORY)
|
||
|
&& ( (open_flags & (SOS_FS_OPEN_CREAT
|
||
|
| SOS_FS_OPEN_TRUNC)) ) )
|
||
|
return -SOS_EINVAL;
|
||
|
|
||
|
if (_pathlen <= 0)
|
||
|
return -SOS_ENOENT;
|
||
|
|
||
|
path.contents = _path;
|
||
|
path.length = _pathlen;
|
||
|
|
||
|
if (path.contents[0] == '/')
|
||
|
nsnode = sos_process_get_root(owner)->direntry;
|
||
|
else
|
||
|
nsnode = sos_process_get_cwd(owner)->direntry;
|
||
|
|
||
|
retval = fs_lookup_node(& path,
|
||
|
! (open_flags & SOS_FS_OPEN_NOFOLLOW),
|
||
|
sos_process_get_root(owner)->direntry,
|
||
|
nsnode,
|
||
|
& nsnode,
|
||
|
& path,
|
||
|
0);
|
||
|
if (SOS_OK != retval)
|
||
|
return retval;
|
||
|
|
||
|
if (path.length <= 0)
|
||
|
{
|
||
|
/* Found the exact match ! */
|
||
|
|
||
|
/* Handle O_EXCL flag */
|
||
|
if (open_flags & SOS_FS_OPEN_EXCL)
|
||
|
{
|
||
|
sos_fs_nscache_unref_node(nsnode);
|
||
|
return -SOS_EEXIST;
|
||
|
}
|
||
|
|
||
|
fsnode = sos_fs_nscache_get_fs_node(nsnode);
|
||
|
if ((open_flags & SOS_FS_OPEN_DIRECTORY)
|
||
|
&& (fsnode->type != SOS_FS_NODE_DIRECTORY))
|
||
|
{
|
||
|
sos_fs_nscache_unref_node(nsnode);
|
||
|
return -SOS_ENOTDIR;
|
||
|
}
|
||
|
|
||
|
/* Handle O_TRUNC flag */
|
||
|
if ((open_flags & SOS_FS_OPEN_TRUNC)
|
||
|
&& fsnode->ops_file->truncate)
|
||
|
{
|
||
|
retval = fsnode->ops_file->truncate(fsnode, 0);
|
||
|
if (SOS_OK != retval)
|
||
|
{
|
||
|
sos_fs_nscache_unref_node(nsnode);
|
||
|
return retval;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
struct sos_fs_nscache_node * parent_nsnode = nsnode;
|
||
|
|
||
|
/* Did not find an exact match. Should create the node ! */
|
||
|
if (! (open_flags & SOS_FS_OPEN_CREAT))
|
||
|
{
|
||
|
sos_fs_nscache_unref_node(parent_nsnode);
|
||
|
return -SOS_ENOENT;
|
||
|
}
|
||
|
|
||
|
/* Create a new entry in the file system */
|
||
|
retval = fs_create_child_node(parent_nsnode,
|
||
|
& path,
|
||
|
SOS_FS_NODE_REGULAR_FILE,
|
||
|
open_flags,
|
||
|
owner,
|
||
|
creat_access_rights,
|
||
|
& nsnode);
|
||
|
sos_fs_nscache_unref_node(parent_nsnode);
|
||
|
if (SOS_OK != retval)
|
||
|
{
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
fsnode = sos_fs_nscache_get_fs_node(nsnode);
|
||
|
}
|
||
|
|
||
|
/* Recompute access rights */
|
||
|
open_flags &= ~(SOS_FS_OPEN_CREAT
|
||
|
| SOS_FS_OPEN_EXCL
|
||
|
| SOS_FS_OPEN_NOFOLLOW
|
||
|
| SOS_FS_OPEN_DIRECTORY);
|
||
|
if (! (fsnode->access_rights & SOS_FS_WRITABLE))
|
||
|
open_flags &= ~(SOS_FS_OPEN_WRITE);
|
||
|
if (! (fsnode->access_rights & SOS_FS_READABLE))
|
||
|
open_flags &= ~(SOS_FS_OPEN_READ);
|
||
|
if (fsnode->fs->flags & SOS_FS_MOUNT_READONLY)
|
||
|
open_flags &= ~(SOS_FS_OPEN_READ);
|
||
|
|
||
|
/*
|
||
|
* Ok, open it right now !
|
||
|
*/
|
||
|
retval = sos_fs_new_opened_file(owner, nsnode, open_flags, of);
|
||
|
|
||
|
sos_fs_nscache_unref_node(nsnode);
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
|
||
|
sos_ret_t sos_fs_close(struct sos_fs_opened_file * of)
|
||
|
{
|
||
|
return sos_fs_unref_opened_file(of);
|
||
|
}
|
||
|
|
||
|
|
||
|
sos_ret_t sos_fs_mark_dirty(struct sos_fs_opened_file * of)
|
||
|
{
|
||
|
struct sos_fs_node * fsnode = sos_fs_nscache_get_fs_node(of->direntry);
|
||
|
|
||
|
/* This function should never get called if the FS is read-only */
|
||
|
SOS_ASSERT_FATAL(! (fsnode->fs->flags & SOS_FS_MOUNT_READONLY));
|
||
|
|
||
|
return mark_dirty_fsnode(fsnode, of->open_flags & SOS_FS_OPEN_SYNC);
|
||
|
}
|
||
|
|
||
|
|
||
|
sos_ret_t sos_fs_read(struct sos_fs_opened_file * of,
|
||
|
sos_uaddr_t dest_buf,
|
||
|
sos_size_t * /* in/ou */len)
|
||
|
{
|
||
|
if (! (of->open_flags & SOS_FS_OPEN_READ))
|
||
|
return -SOS_EPERM;
|
||
|
|
||
|
if (! of->ops_file->read)
|
||
|
return -SOS_ENOSYS;
|
||
|
|
||
|
return of->ops_file->read(of, dest_buf, len);
|
||
|
}
|
||
|
|
||
|
|
||
|
sos_ret_t sos_fs_write(struct sos_fs_opened_file * of,
|
||
|
sos_uaddr_t src_buf,
|
||
|
sos_size_t * /* in/out */len)
|
||
|
{
|
||
|
if (! (of->open_flags & SOS_FS_OPEN_WRITE))
|
||
|
return -SOS_EPERM;
|
||
|
|
||
|
if (! of->ops_file->write)
|
||
|
return -SOS_ENOSYS;
|
||
|
|
||
|
return of->ops_file->write(of, src_buf, len);
|
||
|
}
|
||
|
|
||
|
|
||
|
sos_ret_t sos_fs_seek(struct sos_fs_opened_file *of,
|
||
|
sos_lsoffset_t offset,
|
||
|
sos_seek_whence_t whence,
|
||
|
sos_lsoffset_t * result_position)
|
||
|
{
|
||
|
if (! of->ops_file->seek)
|
||
|
return -SOS_ENOSYS;
|
||
|
|
||
|
return of->ops_file->seek(of, offset, whence, result_position);
|
||
|
}
|
||
|
|
||
|
|
||
|
sos_ret_t sos_fs_ftruncate(struct sos_fs_opened_file *of,
|
||
|
sos_lsoffset_t length)
|
||
|
{
|
||
|
sos_ret_t retval;
|
||
|
struct sos_fs_node * fsnode = sos_fs_nscache_get_fs_node(of->direntry);
|
||
|
|
||
|
if (! (of->open_flags & SOS_FS_OPEN_WRITE))
|
||
|
return -SOS_EPERM;
|
||
|
|
||
|
if (! fsnode->ops_file->truncate)
|
||
|
return -SOS_ENOSYS;
|
||
|
|
||
|
retval = fsnode->ops_file->truncate(fsnode, length);
|
||
|
if (SOS_OK == retval)
|
||
|
mark_dirty_fsnode(fsnode, FALSE);
|
||
|
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
|
||
|
sos_ret_t sos_fs_mmap(struct sos_fs_opened_file *of,
|
||
|
sos_uaddr_t *uaddr, sos_size_t size,
|
||
|
sos_ui32_t access_rights,
|
||
|
sos_ui32_t flags,
|
||
|
sos_luoffset_t offset)
|
||
|
{
|
||
|
sos_ui32_t required_access = 0;
|
||
|
struct sos_fs_node * fsnode = sos_fs_nscache_get_fs_node(of->direntry);
|
||
|
|
||
|
if (! of->ops_file->mmap)
|
||
|
return -SOS_ENOSYS;
|
||
|
|
||
|
/* Translate VM requested rights into FS equivalent */
|
||
|
if (access_rights & SOS_VM_MAP_PROT_READ)
|
||
|
required_access |= SOS_FS_OPEN_READ;
|
||
|
if ( (access_rights & SOS_VM_MAP_PROT_WRITE) && (flags & SOS_VR_MAP_SHARED) )
|
||
|
required_access |= SOS_FS_OPEN_WRITE;
|
||
|
if (access_rights & SOS_VM_MAP_PROT_EXEC)
|
||
|
required_access |= SOS_FS_OPEN_READ;
|
||
|
|
||
|
/* Make sure that the opened file allowed this access */
|
||
|
if ((of->open_flags & required_access) != required_access)
|
||
|
return -SOS_EPERM;
|
||
|
|
||
|
if ( (access_rights & SOS_VM_MAP_PROT_EXEC)
|
||
|
&& (fsnode->fs->flags & SOS_FS_MOUNT_NOEXEC) )
|
||
|
return -SOS_EPERM;
|
||
|
|
||
|
return of->ops_file->mmap(of, uaddr, size, access_rights, flags, offset);
|
||
|
}
|
||
|
|
||
|
|
||
|
sos_ret_t sos_fs_fcntl(struct sos_fs_opened_file *of,
|
||
|
int req_id,
|
||
|
sos_ui32_t req_arg /* Usually: sos_uaddr_t */)
|
||
|
{
|
||
|
if (! of->ops_file->fcntl)
|
||
|
return sos_fs_basic_fcntl_helper(of, req_id, req_arg);
|
||
|
|
||
|
return of->ops_file->fcntl(of, req_id, req_arg);
|
||
|
}
|
||
|
|
||
|
|
||
|
sos_ret_t sos_fs_ioctl(struct sos_fs_opened_file *of,
|
||
|
int req_id,
|
||
|
sos_ui32_t req_arg /* Usually: sos_uaddr_t */)
|
||
|
{
|
||
|
struct sos_fs_node * fsnode = sos_fs_nscache_get_fs_node(of->direntry);
|
||
|
|
||
|
if (fsnode->type == SOS_FS_NODE_DEVICE_CHAR)
|
||
|
{
|
||
|
if (! of->ops_chardev->ioctl)
|
||
|
return -SOS_ENOSYS;
|
||
|
|
||
|
return of->ops_chardev->ioctl(of, req_id, req_arg);
|
||
|
}
|
||
|
else if (fsnode->type == SOS_FS_NODE_DEVICE_BLOCK)
|
||
|
{
|
||
|
if (! of->ops_blockdev->ioctl)
|
||
|
return -SOS_ENOSYS;
|
||
|
|
||
|
return of->ops_blockdev->ioctl(of, req_id, req_arg);
|
||
|
}
|
||
|
|
||
|
return -SOS_ENOSYS;
|
||
|
}
|
||
|
|
||
|
|
||
|
sos_ret_t sos_fs_readdir(struct sos_fs_opened_file * of,
|
||
|
struct sos_fs_dirent * result)
|
||
|
{
|
||
|
struct sos_fs_node * fsnode = sos_fs_nscache_get_fs_node(of->direntry);
|
||
|
|
||
|
if (fsnode->type != SOS_FS_NODE_DIRECTORY)
|
||
|
return -SOS_ENOTDIR;
|
||
|
|
||
|
return of->ops_dir->readdir(of, result);
|
||
|
}
|
||
|
|
||
|
|
||
|
sos_ret_t sos_fs_creat(const struct sos_process * creator,
|
||
|
const char * _path,
|
||
|
sos_size_t _pathlen,
|
||
|
sos_ui32_t access_rights)
|
||
|
{
|
||
|
struct sos_fs_pathname path;
|
||
|
|
||
|
path.contents = _path;
|
||
|
path.length = _pathlen;
|
||
|
|
||
|
return fs_create_node(& path, creator, access_rights,
|
||
|
SOS_FS_NODE_REGULAR_FILE, NULL);
|
||
|
}
|
||
|
|
||
|
|
||
|
sos_ret_t sos_fs_link(const struct sos_process * creator,
|
||
|
const char * _old_path,
|
||
|
sos_size_t _old_pathlen,
|
||
|
const char * _new_path,
|
||
|
sos_size_t _new_pathlen)
|
||
|
{
|
||
|
sos_ret_t retval;
|
||
|
struct sos_fs_nscache_node *old_nsnode, *dest_parent_nsnode, *new_nsnode;
|
||
|
struct sos_fs_node * fsnode;
|
||
|
struct sos_fs_pathname old_path, new_path;
|
||
|
|
||
|
if (_old_pathlen <= 0)
|
||
|
return -SOS_ENOENT;
|
||
|
if (_new_pathlen <= 0)
|
||
|
return -SOS_ENOENT;
|
||
|
|
||
|
/* Resolve target FS node using "old_path" */
|
||
|
old_path.contents = _old_path;
|
||
|
old_path.length = _old_pathlen;
|
||
|
|
||
|
if (old_path.contents[0] == '/')
|
||
|
old_nsnode = sos_process_get_root(creator)->direntry;
|
||
|
else
|
||
|
old_nsnode = sos_process_get_cwd(creator)->direntry;
|
||
|
|
||
|
retval = fs_lookup_node(& old_path,
|
||
|
FALSE /* don't follow symlink */,
|
||
|
sos_process_get_root(creator)->direntry,
|
||
|
old_nsnode,
|
||
|
& old_nsnode,
|
||
|
& old_path,
|
||
|
0);
|
||
|
if (SOS_OK != retval)
|
||
|
return retval;
|
||
|
|
||
|
if (old_path.length > 0)
|
||
|
{
|
||
|
/* Could not resolve full path ! */
|
||
|
sos_fs_nscache_unref_node(old_nsnode);
|
||
|
return -SOS_ENOENT;
|
||
|
}
|
||
|
|
||
|
fsnode = sos_fs_nscache_get_fs_node(old_nsnode);
|
||
|
|
||
|
/* Not allowed to link directories ! */
|
||
|
if (fsnode->type == SOS_FS_NODE_DIRECTORY)
|
||
|
{
|
||
|
sos_fs_nscache_unref_node(old_nsnode);
|
||
|
return -SOS_ENOENT;
|
||
|
}
|
||
|
|
||
|
/* Resolve destination path */
|
||
|
new_path.contents = _new_path;
|
||
|
new_path.length = _new_pathlen;
|
||
|
|
||
|
if (new_path.contents[0] == '/')
|
||
|
dest_parent_nsnode = sos_process_get_root(creator)->direntry;
|
||
|
else
|
||
|
dest_parent_nsnode = sos_process_get_cwd(creator)->direntry;
|
||
|
|
||
|
retval = fs_lookup_node(& new_path,
|
||
|
TRUE /* Follow symlink */,
|
||
|
sos_process_get_root(creator)->direntry,
|
||
|
dest_parent_nsnode,
|
||
|
& dest_parent_nsnode,
|
||
|
& new_path,
|
||
|
0);
|
||
|
if (SOS_OK != retval)
|
||
|
{
|
||
|
sos_fs_nscache_unref_node(old_nsnode);
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
if (new_path.length == 0)
|
||
|
{
|
||
|
/* Found the exact match ! Not allowed to overwrite it ! */
|
||
|
sos_fs_nscache_unref_node(dest_parent_nsnode);
|
||
|
sos_fs_nscache_unref_node(old_nsnode);
|
||
|
return -SOS_EEXIST;
|
||
|
}
|
||
|
|
||
|
/* Create a new entry in the file system */
|
||
|
retval = fs_register_child_node(creator, dest_parent_nsnode, & new_path,
|
||
|
fsnode, 0,
|
||
|
& new_nsnode);
|
||
|
|
||
|
sos_fs_nscache_unref_node(dest_parent_nsnode);
|
||
|
sos_fs_nscache_unref_node(old_nsnode);
|
||
|
sos_fs_nscache_unref_node(new_nsnode);
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
|
||
|
sos_ret_t sos_fs_rename(const struct sos_process * actor,
|
||
|
const char * _old_path,
|
||
|
sos_size_t _old_pathlen,
|
||
|
const char * _new_path,
|
||
|
sos_size_t _new_pathlen)
|
||
|
{
|
||
|
sos_ret_t retval;
|
||
|
struct sos_fs_nscache_node *old_nsnode, *old_parent_nsnode,
|
||
|
*dest_parent_nsnode, *replaced_nsnode;
|
||
|
struct sos_fs_pathname old_name, replaced_name;
|
||
|
struct sos_fs_pathname old_path, new_path;
|
||
|
|
||
|
old_nsnode = old_parent_nsnode = dest_parent_nsnode = replaced_nsnode = NULL;
|
||
|
|
||
|
if (_old_pathlen <= 0)
|
||
|
return -SOS_ENOENT;
|
||
|
if (_new_pathlen <= 0)
|
||
|
return -SOS_ENOENT;
|
||
|
|
||
|
/* Resolve target FS node using "old_path" */
|
||
|
old_path.contents = _old_path;
|
||
|
old_path.length = _old_pathlen;
|
||
|
|
||
|
if (old_path.contents[0] == '/')
|
||
|
old_nsnode = sos_process_get_root(actor)->direntry;
|
||
|
else
|
||
|
old_nsnode = sos_process_get_cwd(actor)->direntry;
|
||
|
|
||
|
retval = fs_lookup_node(& old_path,
|
||
|
FALSE /* don't follow symlink */,
|
||
|
sos_process_get_root(actor)->direntry,
|
||
|
old_nsnode,
|
||
|
& old_nsnode,
|
||
|
& old_path,
|
||
|
0);
|
||
|
if (SOS_OK != retval)
|
||
|
return retval;
|
||
|
|
||
|
if (old_path.length > 0)
|
||
|
{
|
||
|
/* Could not resolve full path ! */
|
||
|
sos_fs_nscache_unref_node(old_nsnode);
|
||
|
return -SOS_ENOENT;
|
||
|
}
|
||
|
|
||
|
/* Not allowed to rename mountpoints ! */
|
||
|
if (sos_fs_nscache_is_mountnode(old_nsnode))
|
||
|
{
|
||
|
sos_fs_nscache_unref_node(old_nsnode);
|
||
|
return -SOS_ENOENT;
|
||
|
}
|
||
|
|
||
|
/* Keep a reference on this node's parent, in case we must undo the
|
||
|
rename */
|
||
|
retval = sos_fs_nscache_get_parent(old_nsnode, & old_parent_nsnode);
|
||
|
if (SOS_OK != retval)
|
||
|
{
|
||
|
sos_fs_nscache_unref_node(old_nsnode);
|
||
|
return retval;
|
||
|
}
|
||
|
sos_fs_nscache_ref_node(old_parent_nsnode);
|
||
|
|
||
|
/* Resolve destination path */
|
||
|
replaced_nsnode = NULL;
|
||
|
new_path.contents = _new_path;
|
||
|
new_path.length = _new_pathlen;
|
||
|
|
||
|
if (new_path.contents[0] == '/')
|
||
|
dest_parent_nsnode = sos_process_get_root(actor)->direntry;
|
||
|
else
|
||
|
dest_parent_nsnode = sos_process_get_cwd(actor)->direntry;
|
||
|
|
||
|
retval = fs_lookup_node(& new_path,
|
||
|
TRUE /* Follow symlink */,
|
||
|
sos_process_get_root(actor)->direntry,
|
||
|
dest_parent_nsnode,
|
||
|
& dest_parent_nsnode,
|
||
|
& new_path,
|
||
|
0);
|
||
|
if (SOS_OK != retval)
|
||
|
{
|
||
|
goto undo_rename;
|
||
|
}
|
||
|
|
||
|
/* Nothing to do ? */
|
||
|
if (old_nsnode == dest_parent_nsnode)
|
||
|
{
|
||
|
sos_fs_nscache_unref_node(old_nsnode);
|
||
|
sos_fs_nscache_unref_node(old_parent_nsnode);
|
||
|
sos_fs_nscache_unref_node(dest_parent_nsnode);
|
||
|
return SOS_OK;
|
||
|
}
|
||
|
|
||
|
/* Remove old nsnode from file ns cache */
|
||
|
sos_fs_nscache_get_name(old_nsnode, & old_name);
|
||
|
retval = fs_remove_node(actor, old_nsnode);
|
||
|
if (SOS_OK != retval)
|
||
|
{
|
||
|
sos_fs_nscache_unref_node(old_nsnode);
|
||
|
sos_fs_nscache_unref_node(old_parent_nsnode);
|
||
|
sos_fs_nscache_unref_node(dest_parent_nsnode);
|
||
|
return -SOS_ENOENT;
|
||
|
}
|
||
|
|
||
|
if (new_path.length == 0)
|
||
|
{
|
||
|
/* Found the exact match ! We disconnect it from the namespace,
|
||
|
but keep it aside */
|
||
|
|
||
|
/* Not allowed to replace directories */
|
||
|
if (sos_fs_nscache_get_fs_node(dest_parent_nsnode)->type
|
||
|
== SOS_FS_NODE_DIRECTORY)
|
||
|
{
|
||
|
retval = -SOS_EBUSY;
|
||
|
goto undo_rename;
|
||
|
}
|
||
|
|
||
|
replaced_nsnode = dest_parent_nsnode;
|
||
|
dest_parent_nsnode = NULL;
|
||
|
|
||
|
/* Retrieve the parent of the node to replace */
|
||
|
retval = sos_fs_nscache_get_parent(replaced_nsnode,
|
||
|
& dest_parent_nsnode);
|
||
|
if (SOS_OK != retval)
|
||
|
{
|
||
|
dest_parent_nsnode = replaced_nsnode;
|
||
|
goto undo_rename;
|
||
|
}
|
||
|
|
||
|
sos_fs_nscache_ref_node(dest_parent_nsnode);
|
||
|
|
||
|
/* Disconnect this node from its parent */
|
||
|
sos_fs_nscache_get_name(replaced_nsnode, & replaced_name);
|
||
|
retval = fs_remove_node(actor, replaced_nsnode);
|
||
|
if (SOS_OK != retval)
|
||
|
goto undo_rename;
|
||
|
}
|
||
|
|
||
|
/* Create a new entry in the file system */
|
||
|
retval = fs_connect_existing_child_node(actor, dest_parent_nsnode,
|
||
|
& new_path,
|
||
|
old_nsnode);
|
||
|
if (SOS_OK != retval)
|
||
|
goto undo_rename;
|
||
|
|
||
|
sos_fs_nscache_unref_node(old_nsnode);
|
||
|
sos_fs_nscache_unref_node(old_parent_nsnode);
|
||
|
sos_fs_nscache_unref_node(dest_parent_nsnode);
|
||
|
if (NULL != replaced_nsnode)
|
||
|
sos_fs_nscache_unref_node(replaced_nsnode);
|
||
|
|
||
|
return retval;
|
||
|
|
||
|
undo_rename:
|
||
|
|
||
|
/* Handle special case: the node replaced something. In case the
|
||
|
previous operation failed, we try to reinsert the replaced
|
||
|
node */
|
||
|
if (NULL != replaced_nsnode)
|
||
|
{
|
||
|
fs_connect_existing_child_node(actor, dest_parent_nsnode,
|
||
|
& replaced_name,
|
||
|
replaced_nsnode);
|
||
|
sos_fs_nscache_unref_node(replaced_nsnode);
|
||
|
}
|
||
|
|
||
|
fs_connect_existing_child_node(actor, old_parent_nsnode,
|
||
|
& old_name,
|
||
|
old_nsnode);
|
||
|
sos_fs_nscache_unref_node(old_nsnode);
|
||
|
sos_fs_nscache_unref_node(old_parent_nsnode);
|
||
|
|
||
|
if (NULL != dest_parent_nsnode)
|
||
|
sos_fs_nscache_unref_node(dest_parent_nsnode);
|
||
|
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
|
||
|
sos_ret_t sos_fs_unlink(const struct sos_process * actor,
|
||
|
const char * _path,
|
||
|
sos_size_t _pathlen)
|
||
|
{
|
||
|
sos_ret_t retval;
|
||
|
struct sos_fs_pathname path;
|
||
|
struct sos_fs_nscache_node * nsnode;
|
||
|
|
||
|
path.contents = _path;
|
||
|
path.length = _pathlen;
|
||
|
|
||
|
if (path.length <= 0)
|
||
|
return -SOS_ENOENT;
|
||
|
|
||
|
if (path.contents[0] == '/')
|
||
|
nsnode = sos_process_get_root(actor)->direntry;
|
||
|
else
|
||
|
nsnode = sos_process_get_cwd(actor)->direntry;
|
||
|
|
||
|
retval = fs_lookup_node(& path, FALSE,
|
||
|
sos_process_get_root(actor)->direntry,
|
||
|
nsnode,
|
||
|
& nsnode, & path, 0);
|
||
|
if (SOS_OK != retval)
|
||
|
return retval;
|
||
|
|
||
|
/* Make sure the whole path has been resolved */
|
||
|
if (path.length > 0)
|
||
|
{
|
||
|
sos_fs_nscache_unref_node(nsnode);
|
||
|
return -SOS_ENOENT;
|
||
|
}
|
||
|
|
||
|
if (sos_fs_nscache_get_fs_node(nsnode)->type == SOS_FS_NODE_DIRECTORY)
|
||
|
retval = -SOS_EISDIR;
|
||
|
else
|
||
|
retval = fs_remove_node(actor, nsnode);
|
||
|
|
||
|
sos_fs_nscache_unref_node(nsnode);
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
|
||
|
sos_ret_t sos_fs_symlink(const struct sos_process * creator,
|
||
|
const char * _path,
|
||
|
sos_size_t _pathlen,
|
||
|
sos_uaddr_t symlink_target,
|
||
|
sos_size_t symlink_target_len)
|
||
|
{
|
||
|
sos_ret_t retval;
|
||
|
struct sos_fs_pathname path;
|
||
|
struct sos_fs_node * fsnode;
|
||
|
struct sos_fs_nscache_node * symlink;
|
||
|
struct sos_fs_opened_file * tmp_of;
|
||
|
sos_size_t len;
|
||
|
|
||
|
path.contents = _path;
|
||
|
path.length = _pathlen;
|
||
|
|
||
|
retval = fs_create_node(& path, creator, SOS_FS_S_IRWXALL,
|
||
|
SOS_FS_NODE_SYMLINK, & symlink);
|
||
|
if (SOS_OK != retval)
|
||
|
return retval;
|
||
|
|
||
|
/* Artificially open the symlink to change its contents */
|
||
|
fsnode = sos_fs_nscache_get_fs_node(symlink);
|
||
|
retval = fsnode->new_opened_file(fsnode, creator,
|
||
|
SOS_FS_OPEN_WRITE, & tmp_of);
|
||
|
if (SOS_OK != retval)
|
||
|
{
|
||
|
fs_remove_node(creator, symlink);
|
||
|
sos_fs_nscache_unref_node(symlink);
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
tmp_of->ref_cnt = 1;
|
||
|
retval = sos_fs_nscache_register_opened_file(symlink, tmp_of);
|
||
|
if (SOS_OK != retval)
|
||
|
{
|
||
|
fsnode->close_opened_file(fsnode, tmp_of);
|
||
|
fs_remove_node(creator, symlink);
|
||
|
sos_fs_nscache_unref_node(symlink);
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
len = symlink_target_len;
|
||
|
retval = sos_fs_write(tmp_of, symlink_target, & len);
|
||
|
mark_dirty_fsnode(fsnode, FALSE);
|
||
|
fsnode->close_opened_file(fsnode, tmp_of);
|
||
|
|
||
|
if ((SOS_OK != retval) || (len != symlink_target_len))
|
||
|
{
|
||
|
fs_remove_node(creator, symlink);
|
||
|
if (SOS_OK == retval)
|
||
|
retval = -SOS_ENAMETOOLONG;
|
||
|
}
|
||
|
|
||
|
sos_fs_nscache_unref_node(symlink);
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
|
||
|
sos_ret_t sos_fs_mkdir(const struct sos_process * creator,
|
||
|
const char * _path,
|
||
|
sos_size_t _pathlen,
|
||
|
sos_ui32_t access_rights)
|
||
|
{
|
||
|
struct sos_fs_pathname path;
|
||
|
|
||
|
path.contents = _path;
|
||
|
path.length = _pathlen;
|
||
|
|
||
|
return fs_create_node(& path, creator, access_rights,
|
||
|
SOS_FS_NODE_DIRECTORY, NULL);
|
||
|
}
|
||
|
|
||
|
|
||
|
sos_ret_t sos_fs_rmdir(const struct sos_process * actor,
|
||
|
const char * _path,
|
||
|
sos_size_t _pathlen)
|
||
|
{
|
||
|
sos_ret_t retval;
|
||
|
struct sos_fs_pathname path;
|
||
|
struct sos_fs_nscache_node * nsnode;
|
||
|
|
||
|
path.contents = _path;
|
||
|
path.length = _pathlen;
|
||
|
|
||
|
if (path.length <= 0)
|
||
|
return -SOS_ENOENT;
|
||
|
|
||
|
if (path.contents[0] == '/')
|
||
|
nsnode = sos_process_get_root(actor)->direntry;
|
||
|
else
|
||
|
nsnode = sos_process_get_cwd(actor)->direntry;
|
||
|
|
||
|
retval = fs_lookup_node(& path, FALSE,
|
||
|
sos_process_get_root(actor)->direntry,
|
||
|
nsnode,
|
||
|
& nsnode, & path, 0);
|
||
|
if (SOS_OK != retval)
|
||
|
return retval;
|
||
|
|
||
|
/* Make sure the whole path has been resolved */
|
||
|
if (path.length > 0)
|
||
|
{
|
||
|
sos_fs_nscache_unref_node(nsnode);
|
||
|
return -SOS_ENOENT;
|
||
|
}
|
||
|
|
||
|
/* Cannot rmdir non-dir nodes */
|
||
|
if (sos_fs_nscache_get_fs_node(nsnode)->type != SOS_FS_NODE_DIRECTORY)
|
||
|
retval = -SOS_ENOTDIR;
|
||
|
|
||
|
/* Cannot remove directory if it is still used by somebody else */
|
||
|
else if (sos_fs_nscache_get_ref_cnt(nsnode) > 1)
|
||
|
retval = -SOS_EBUSY;
|
||
|
|
||
|
/* Cannot remove directory if it is still has children stored on
|
||
|
disk */
|
||
|
else if (sos_fs_nscache_get_fs_node(nsnode)->ondisk_lnk_cnt > 1)
|
||
|
retval = -SOS_ENOTEMPTY;
|
||
|
|
||
|
/* Otherwise, yes : suppress the node on disk */
|
||
|
else
|
||
|
retval = fs_remove_node(actor, nsnode);
|
||
|
|
||
|
sos_fs_nscache_unref_node(nsnode);
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
|
||
|
sos_ret_t sos_fs_mknod(const struct sos_process * creator,
|
||
|
const char * _path,
|
||
|
sos_size_t _pathlen,
|
||
|
sos_fs_node_type_t type /* only block/char allowed */,
|
||
|
sos_ui32_t access_rights,
|
||
|
const struct sos_fs_dev_id_t * devid)
|
||
|
{
|
||
|
sos_ret_t retval;
|
||
|
struct sos_fs_pathname path;
|
||
|
struct sos_fs_nscache_node * nsnode;
|
||
|
struct sos_fs_node * fsnode;
|
||
|
|
||
|
if ((type != SOS_FS_NODE_DEVICE_BLOCK)
|
||
|
&& (type != SOS_FS_NODE_DEVICE_CHAR))
|
||
|
return -SOS_EINVAL;
|
||
|
|
||
|
path.contents = _path;
|
||
|
path.length = _pathlen;
|
||
|
|
||
|
retval = fs_create_node(& path, creator, access_rights,
|
||
|
type, & nsnode);
|
||
|
if (SOS_OK != retval)
|
||
|
return retval;
|
||
|
|
||
|
fsnode = sos_fs_nscache_get_fs_node(nsnode);
|
||
|
fsnode->dev_id.device_class = devid->device_class;
|
||
|
fsnode->dev_id.device_instance = devid->device_instance;
|
||
|
|
||
|
sos_fs_nscache_unref_node(nsnode);
|
||
|
return SOS_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
sos_ret_t sos_fs_stat(const struct sos_process * actor,
|
||
|
const char * _path,
|
||
|
sos_size_t _pathlen,
|
||
|
int nofollow,
|
||
|
struct sos_fs_stat * result)
|
||
|
{
|
||
|
sos_ret_t retval;
|
||
|
struct sos_fs_pathname path;
|
||
|
struct sos_fs_nscache_node * nsnode;
|
||
|
struct sos_fs_node * fsnode;
|
||
|
|
||
|
path.contents = _path;
|
||
|
path.length = _pathlen;
|
||
|
|
||
|
if (path.length <= 0)
|
||
|
return -SOS_ENOENT;
|
||
|
|
||
|
if (path.contents[0] == '/')
|
||
|
nsnode = sos_process_get_root(actor)->direntry;
|
||
|
else
|
||
|
nsnode = sos_process_get_cwd(actor)->direntry;
|
||
|
|
||
|
retval = fs_lookup_node(& path, (nofollow != 0),
|
||
|
sos_process_get_root(actor)->direntry,
|
||
|
nsnode,
|
||
|
& nsnode, & path, 0);
|
||
|
if (SOS_OK != retval)
|
||
|
return retval;
|
||
|
|
||
|
/* Make sure the whole path has been resolved */
|
||
|
if (path.length > 0)
|
||
|
{
|
||
|
sos_fs_nscache_unref_node(nsnode);
|
||
|
return -SOS_ENOENT;
|
||
|
}
|
||
|
|
||
|
fsnode = sos_fs_nscache_get_fs_node(nsnode);
|
||
|
retval = fsnode->ops_file->stat(fsnode, result);
|
||
|
|
||
|
sos_fs_nscache_unref_node(nsnode);
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
|
||
|
sos_ret_t sos_fs_fsync(struct sos_fs_opened_file * of)
|
||
|
{
|
||
|
struct sos_fs_node * fsnode = sos_fs_nscache_get_fs_node(of->direntry);
|
||
|
return sos_fs_sync_node(fsnode);
|
||
|
}
|
||
|
|
||
|
|
||
|
sos_ret_t sos_fs_chmod(const struct sos_process * actor,
|
||
|
const char * _path,
|
||
|
sos_size_t _pathlen,
|
||
|
sos_ui32_t access_rights)
|
||
|
{
|
||
|
sos_ret_t retval;
|
||
|
struct sos_fs_pathname path;
|
||
|
struct sos_fs_nscache_node * nsnode;
|
||
|
struct sos_fs_node * fsnode;
|
||
|
|
||
|
path.contents = _path;
|
||
|
path.length = _pathlen;
|
||
|
|
||
|
if (path.length <= 0)
|
||
|
return -SOS_ENOENT;
|
||
|
|
||
|
if (path.contents[0] == '/')
|
||
|
nsnode = sos_process_get_root(actor)->direntry;
|
||
|
else
|
||
|
nsnode = sos_process_get_cwd(actor)->direntry;
|
||
|
|
||
|
retval = fs_lookup_node(& path, TRUE,
|
||
|
sos_process_get_root(actor)->direntry,
|
||
|
nsnode,
|
||
|
& nsnode, & path, 0);
|
||
|
if (SOS_OK != retval)
|
||
|
return retval;
|
||
|
|
||
|
/* Make sure the whole path has been resolved */
|
||
|
if (path.length > 0)
|
||
|
{
|
||
|
sos_fs_nscache_unref_node(nsnode);
|
||
|
return -SOS_ENOENT;
|
||
|
}
|
||
|
|
||
|
fsnode = sos_fs_nscache_get_fs_node(nsnode);
|
||
|
retval = fsnode->ops_file->chmod(fsnode, access_rights);
|
||
|
if (SOS_OK == retval)
|
||
|
mark_dirty_fsnode(fsnode, FALSE);
|
||
|
|
||
|
sos_fs_nscache_unref_node(nsnode);
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
|
||
|
sos_ret_t sos_fs_vfstat(const struct sos_process * actor,
|
||
|
const char * _path,
|
||
|
sos_size_t _pathlen,
|
||
|
struct sos_fs_statfs * result)
|
||
|
{
|
||
|
sos_ret_t retval;
|
||
|
struct sos_fs_pathname path;
|
||
|
struct sos_fs_nscache_node * nsnode;
|
||
|
struct sos_fs_node * fsnode;
|
||
|
|
||
|
path.contents = _path;
|
||
|
path.length = _pathlen;
|
||
|
|
||
|
if (path.length <= 0)
|
||
|
return -SOS_ENOENT;
|
||
|
|
||
|
if (path.contents[0] == '/')
|
||
|
nsnode = sos_process_get_root(actor)->direntry;
|
||
|
else
|
||
|
nsnode = sos_process_get_cwd(actor)->direntry;
|
||
|
|
||
|
retval = fs_lookup_node(& path, FALSE,
|
||
|
sos_process_get_root(actor)->direntry,
|
||
|
nsnode,
|
||
|
& nsnode, & path, 0);
|
||
|
if (SOS_OK != retval)
|
||
|
return retval;
|
||
|
|
||
|
/* Make sure the whole path has been resolved */
|
||
|
if (path.length > 0)
|
||
|
{
|
||
|
sos_fs_nscache_unref_node(nsnode);
|
||
|
return -SOS_ENOENT;
|
||
|
}
|
||
|
|
||
|
fsnode = sos_fs_nscache_get_fs_node(nsnode);
|
||
|
if (fsnode->fs->statfs)
|
||
|
retval = fsnode->fs->statfs(fsnode->fs, result);
|
||
|
else
|
||
|
retval = -SOS_ENOSYS;
|
||
|
|
||
|
sos_fs_nscache_unref_node(nsnode);
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
|
||
|
/** This function is resilient against mounting/unmounting of other FS */
|
||
|
sos_ret_t sos_fs_sync_all_fs()
|
||
|
{
|
||
|
int dummy = 0;
|
||
|
sos_ui64_t uid = 0;
|
||
|
|
||
|
while (1)
|
||
|
{
|
||
|
/* Iterate over the FS types */
|
||
|
struct sos_fs_manager_type * fstype;
|
||
|
int ntype;
|
||
|
list_foreach(fs_list, fstype, ntype)
|
||
|
{
|
||
|
/* Iterate over the FS instances */
|
||
|
struct sos_fs_manager_instance * fs;
|
||
|
int ninst;
|
||
|
|
||
|
/* This scan will be exhaustive and resilient to
|
||
|
addition/removal of file systems as long as new FS are
|
||
|
added with list_add_tail (because the scan is "forward",
|
||
|
ie in order head -> tail) */
|
||
|
|
||
|
/* As long as we don't block, we can safely access the
|
||
|
prev/next fields of the FS instance */
|
||
|
list_foreach_forward(fstype->instances, fs, ninst)
|
||
|
{
|
||
|
if (fs->uid <= uid)
|
||
|
continue;
|
||
|
|
||
|
uid = fs->uid;
|
||
|
sos_fs_sync_fs(fs);
|
||
|
|
||
|
/* We must NOT continue the loops because the
|
||
|
prev/next/current fs types/instances might have been
|
||
|
removed or added (sync blocks, by definition) ! */
|
||
|
goto lookup_next_fs;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Reached the end of the list */
|
||
|
break;
|
||
|
|
||
|
lookup_next_fs:
|
||
|
/* Loop over */
|
||
|
dummy ++;
|
||
|
}
|
||
|
|
||
|
/* Now flush all the block devices to disk */
|
||
|
return sos_blockdev_sync_all_devices();
|
||
|
}
|
||
|
|
||
|
|
||
|
/* *************************************************************
|
||
|
* client FS helper functions
|
||
|
*/
|
||
|
sos_ret_t sos_fs_basic_fcntl_helper(struct sos_fs_opened_file * of,
|
||
|
int req_id, sos_ui32_t req_arg)
|
||
|
{
|
||
|
sos_ret_t result = -SOS_ENOSUP;
|
||
|
|
||
|
switch(req_id)
|
||
|
{
|
||
|
case SOS_FCNTL_DUPFD:
|
||
|
{
|
||
|
result = sos_fs_ref_opened_file(of);
|
||
|
if (SOS_OK != result)
|
||
|
break;
|
||
|
|
||
|
result = sos_process_register_opened_file((struct sos_process*)of->owner,
|
||
|
of, req_arg);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case SOS_FCNTL_GETFD:
|
||
|
{
|
||
|
result = of->open_flags & SOS_FS_OPEN_CLOSEONEXEC;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case SOS_FCNTL_SETFD:
|
||
|
{
|
||
|
of->open_flags &= ~SOS_FS_OPEN_CLOSEONEXEC;
|
||
|
of->open_flags |= (req_arg & SOS_FS_OPEN_CLOSEONEXEC);
|
||
|
result = SOS_OK;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case SOS_FCNTL_GETFL:
|
||
|
{
|
||
|
result = of->open_flags;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case SOS_FCNTL_SETFL:
|
||
|
{
|
||
|
/* Not supported */
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* *************************************************************
|
||
|
* mount/umount stuff
|
||
|
*/
|
||
|
|
||
|
|
||
|
sos_ret_t sos_fs_register_fs_instance(struct sos_fs_manager_instance * fs,
|
||
|
struct sos_fs_node * root_fsnode)
|
||
|
{
|
||
|
sos_ret_t retval;
|
||
|
struct sos_fs_nscache_node * nsnode_root;
|
||
|
|
||
|
retval = sos_fs_nscache_create_mounted_root(root_fsnode, & nsnode_root);
|
||
|
if (SOS_OK != retval)
|
||
|
return retval;
|
||
|
|
||
|
fs->uid = ++last_fs_instance_uid;
|
||
|
fs->root = nsnode_root;
|
||
|
sos_hash_insert(fs->nodecache, root_fsnode);
|
||
|
|
||
|
list_add_tail(fs->fs_type->instances, fs);
|
||
|
return SOS_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
sos_ret_t sos_fs_unregister_fs_instance(struct sos_fs_manager_instance * fs)
|
||
|
{
|
||
|
fs->uid = 0;
|
||
|
list_delete(fs->fs_type->instances, fs);
|
||
|
return SOS_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
sos_ret_t sos_fs_register_fs_type(struct sos_fs_manager_type * fstype)
|
||
|
{
|
||
|
struct sos_fs_manager_type * iterator;
|
||
|
int nbtypes;
|
||
|
|
||
|
list_foreach_forward(fs_list, iterator, nbtypes)
|
||
|
{
|
||
|
if (! strncmp(fstype->name, iterator->name, SOS_FS_MANAGER_NAME_MAXLEN))
|
||
|
return -SOS_EEXIST;
|
||
|
}
|
||
|
|
||
|
list_add_tail(fs_list, fstype);
|
||
|
return SOS_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
sos_ret_t sos_fs_unregister_fs_type(struct sos_fs_manager_type * fstype)
|
||
|
{
|
||
|
struct sos_fs_manager_type * iterator;
|
||
|
int nbtypes;
|
||
|
|
||
|
if (! list_is_empty(fstype->instances))
|
||
|
return -SOS_EBUSY;
|
||
|
|
||
|
list_foreach_forward(fs_list, iterator, nbtypes)
|
||
|
{
|
||
|
if (! strncmp(fstype->name, iterator->name, SOS_FS_MANAGER_NAME_MAXLEN))
|
||
|
{
|
||
|
list_delete(fs_list, fstype);
|
||
|
return SOS_OK;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return -SOS_EINVAL;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* _src_path may be empty
|
||
|
*/
|
||
|
sos_ret_t sos_fs_mount(struct sos_process * actor,
|
||
|
const char * _src_path,
|
||
|
sos_size_t _src_pathlen,
|
||
|
const char * _dst_path,
|
||
|
sos_size_t _dst_pathlen,
|
||
|
const char * fsname,
|
||
|
sos_ui32_t mountflags,
|
||
|
const char * args,
|
||
|
struct sos_fs_manager_instance ** result_fs)
|
||
|
{
|
||
|
sos_ret_t retval;
|
||
|
struct sos_fs_pathname src_path, dst_path;
|
||
|
struct sos_fs_nscache_node * src_nsnode, * dst_nsnode;
|
||
|
struct sos_fs_manager_type * fs_type;
|
||
|
struct sos_fs_manager_instance * new_fs;
|
||
|
int nb_fstypes;
|
||
|
|
||
|
if (_dst_pathlen <= 0)
|
||
|
return -SOS_ENOENT;
|
||
|
|
||
|
/* 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;
|
||
|
|
||
|
src_path.contents = _src_path;
|
||
|
src_path.length = _src_pathlen;
|
||
|
|
||
|
/* Compute the start_nsnode for the source */
|
||
|
if (src_path.length <= 0)
|
||
|
src_nsnode = NULL;
|
||
|
else if (src_path.contents[0] == '/')
|
||
|
src_nsnode = sos_process_get_root(actor)->direntry;
|
||
|
else
|
||
|
src_nsnode = sos_process_get_cwd(actor)->direntry;
|
||
|
|
||
|
/* Lookup the source node */
|
||
|
if (src_nsnode)
|
||
|
{
|
||
|
retval = fs_lookup_node(& src_path, TRUE,
|
||
|
sos_process_get_root(actor)->direntry,
|
||
|
src_nsnode,
|
||
|
& src_nsnode, & src_path, 0);
|
||
|
if (SOS_OK != retval)
|
||
|
return retval;
|
||
|
|
||
|
/* Make sure the whole path has been resolved */
|
||
|
if (src_path.length > 0)
|
||
|
{
|
||
|
sos_fs_nscache_unref_node(src_nsnode);
|
||
|
return -SOS_ENOENT;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
dst_path.contents = _dst_path;
|
||
|
dst_path.length = _dst_pathlen;
|
||
|
|
||
|
/* Compute the start_nsnode for the destination */
|
||
|
if (dst_path.contents[0] == '/')
|
||
|
dst_nsnode = sos_process_get_root(actor)->direntry;
|
||
|
else
|
||
|
dst_nsnode = sos_process_get_cwd(actor)->direntry;
|
||
|
|
||
|
/* Lookup the destination node */
|
||
|
retval = fs_lookup_node(& dst_path, TRUE,
|
||
|
sos_process_get_root(actor)->direntry,
|
||
|
dst_nsnode,
|
||
|
& dst_nsnode, & dst_path, 0);
|
||
|
if ((SOS_OK != retval) || (dst_path.length > 0))
|
||
|
{
|
||
|
if (src_nsnode)
|
||
|
sos_fs_nscache_unref_node(src_nsnode);
|
||
|
if (dst_path.length > 0)
|
||
|
retval = -SOS_ENOENT;
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
/* Actually call the mount callback of the FS */
|
||
|
retval
|
||
|
= fs_type->mount(fs_type,
|
||
|
(src_nsnode)?sos_fs_nscache_get_fs_node(src_nsnode):NULL,
|
||
|
args, & new_fs);
|
||
|
if (SOS_OK != retval)
|
||
|
{
|
||
|
if (src_nsnode)
|
||
|
sos_fs_nscache_unref_node(src_nsnode);
|
||
|
sos_fs_nscache_unref_node(dst_nsnode);
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
/* Make sure the nodecache was created */
|
||
|
SOS_ASSERT_FATAL(NULL != new_fs->nodecache);
|
||
|
SOS_ASSERT_FATAL(NULL != new_fs->root);
|
||
|
|
||
|
/* Update some reserved fields */
|
||
|
sos_fs_nscache_get_fs_node(new_fs->root)->fs = new_fs;
|
||
|
|
||
|
/* Register the mountpoint in the nscache */
|
||
|
retval = sos_fs_nscache_mount(dst_nsnode, new_fs->root);
|
||
|
SOS_ASSERT_FATAL(SOS_OK == retval);
|
||
|
|
||
|
/* Un-reference the temporary nsnodes */
|
||
|
if (src_nsnode)
|
||
|
sos_fs_nscache_unref_node(src_nsnode);
|
||
|
sos_fs_nscache_unref_node(dst_nsnode);
|
||
|
|
||
|
if (result_fs)
|
||
|
*result_fs = new_fs;
|
||
|
|
||
|
return SOS_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
sos_ret_t sos_fs_umount(struct sos_process * actor,
|
||
|
const char * _mounted_root_path,
|
||
|
sos_size_t _mounted_root_pathlen)
|
||
|
{
|
||
|
sos_ret_t retval;
|
||
|
struct sos_fs_pathname mounted_root_path;
|
||
|
struct sos_fs_nscache_node * mounted_root_nsnode;
|
||
|
struct sos_fs_manager_instance * fs;
|
||
|
|
||
|
if (_mounted_root_pathlen <= 0)
|
||
|
return -SOS_ENOENT;
|
||
|
|
||
|
mounted_root_path.contents = _mounted_root_path;
|
||
|
mounted_root_path.length = _mounted_root_pathlen;
|
||
|
|
||
|
/* Compute the start_nsnode for the mounted_root */
|
||
|
if (mounted_root_path.contents[0] == '/')
|
||
|
mounted_root_nsnode = sos_process_get_root(actor)->direntry;
|
||
|
else
|
||
|
mounted_root_nsnode = sos_process_get_cwd(actor)->direntry;
|
||
|
|
||
|
/* Lookup the mounted_root node */
|
||
|
retval = fs_lookup_node(& mounted_root_path, TRUE,
|
||
|
sos_process_get_root(actor)->direntry,
|
||
|
mounted_root_nsnode,
|
||
|
& mounted_root_nsnode, & mounted_root_path, 0);
|
||
|
if (SOS_OK != retval)
|
||
|
return retval;
|
||
|
|
||
|
/* Make sure the whole path has been resolved */
|
||
|
if (mounted_root_path.length > 0)
|
||
|
{
|
||
|
sos_fs_nscache_unref_node(mounted_root_nsnode);
|
||
|
return -SOS_ENOENT;
|
||
|
}
|
||
|
|
||
|
/* Make sure this node is the real root of the FS */
|
||
|
fs = sos_fs_nscache_get_fs_node(mounted_root_nsnode)->fs;
|
||
|
if (fs->root != mounted_root_nsnode)
|
||
|
{
|
||
|
sos_fs_nscache_unref_node(mounted_root_nsnode);
|
||
|
return -SOS_ENOENT;
|
||
|
}
|
||
|
|
||
|
/* Disconnect this FS mounted_root from namespace cache */
|
||
|
retval = sos_fs_nscache_umount(mounted_root_nsnode);
|
||
|
|
||
|
/* Mounted_Root not needed anymore */
|
||
|
sos_fs_nscache_unref_node(mounted_root_nsnode);
|
||
|
if (SOS_OK != retval)
|
||
|
return retval;
|
||
|
|
||
|
fs->root = NULL;
|
||
|
|
||
|
/* Flush any changes to disk */
|
||
|
retval = sos_fs_sync_fs(fs);
|
||
|
if (SOS_OK != retval)
|
||
|
{
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
retval = fs->fs_type->umount(fs->fs_type, fs);
|
||
|
return retval;
|
||
|
}
|