/tmp/sos-code-article7.5/drivers/fs_virtfs.c (1970-01-01 01:00:00.000000000 +0100
) |
|
../sos-code-article8/drivers/fs_virtfs.c (2005-07-01 16:39:47.000000000 +0200
) |
|
|
|
| /* Copyright (C) 2005 David Decotigny |
| |
| 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/kmalloc.h> |
| #include <sos/klibc.h> |
| #include <sos/assert.h> |
| #include <sos/list.h> |
| #include <sos/ksynch.h> |
| #include <sos/uaccess.h> |
| #include <sos/physmem.h> |
| |
| #include <sos/fs.h> |
| #include <sos/fs_nscache.h> |
| |
| #include "fs_virtfs.h" |
| |
| /** |
| * @file fs_virtfs.c |
| * |
| * All the sos_fs_nodes and their contents are stored in kernel |
| * memory. Thus, with "virtfs", the "disk" is actually the kernel |
| * memory. In a sos_fs_node, the storage_location field corresponds to |
| * the kernel address of the corresponding struct sos_fs_node. |
| * |
| * Basic kernel-memory virtual FS. Highly UNDER-optimized filesystem: |
| * each time we resize a file, a complete reallocation is done. This |
| * somehow complicates the file mapping, as we cannot do this anyhow |
| * as long as the file is mapped because the kernel address of the |
| * file contents must NOT change. For that reason, we forbid to resize |
| * a file as soon as it is mapped and the file's contents are aligned |
| * on a page boundary in kernel memory (use of sos_kmem_vmm_alloc |
| * instead of sos_kmalloc). |
| */ |
| |
| |
| /** |
| * A directory entry, used in virtfs_node of type "directory". This is |
| * as simple as a sos_fs_node with a name. |
| */ |
| struct virtfs_direntry |
| { |
| char * name; |
| struct sos_fs_node * fsnode; |
| |
| /** |
| * Used by readdir to be resilient against creat/mkdir/unlink/rmdir |
| * between 2 successive readdirs. Each time a new child to a node is |
| * allocated, a new "creation_order" is uniquely attributed. 64bits |
| * should be large enough. |
| */ |
| sos_lcount_t creation_order; |
| |
| struct virtfs_direntry * sibling_prev, * sibling_next; |
| }; |
| |
| |
| /** |
| * Structure of a FS file or dir for virtfs. Virtfs only supports |
| * regular files, directories or symbolic links: |
| * - regular files correspond to an area in kernel memory |
| * - directories correspond to a list of virtfs_direntry |
| * |
| * Structural inheritance of sos_fs_node: "super" is the ancestor (in |
| * "OOP" terms). The "super" field might not be the first field: from |
| * a sos_fs_node, the corresponding virtfs_node is given by the |
| * sos_fs_node::custom_data field |
| */ |
| struct virtfs_node |
| { |
| /** The ancestor */ |
| struct sos_fs_node super; |
| |
| union |
| { |
| /* A file */ |
| struct |
| { |
| /* Contents of the file */ |
| sos_size_t size; |
| void * data; |
| |
| /* Yes, the file can be mapped ! */ |
| struct sos_umem_vmm_mapped_resource mapres; |
| sos_count_t num_mappings; /**< Forbids the region to be resized |
| while it's being mapped, because this |
| woould cause the underlying data area |
| to be moved */ |
| } file; |
| |
| /* A directory */ |
| struct |
| { |
| /** the children nodes are inserted in FIFO order. This is |
| important for the readdir function to be resilient against |
| mkdir/rmdir/creat/unlink */ |
| struct virtfs_direntry * list_entries; |
| |
| /** Used by readdir to remember the last unique "creation order" |
| attributed */ |
| sos_lcount_t top_creation_order; |
| } dir; |
| }; /* Anonymous union (gcc extension) */ |
| |
| /* The virtfs nodes are chained */ |
| struct virtfs_node * prev, * next; |
| }; |
| |
| |
| /** |
| * A virtfs FS instance |
| * |
| * Structural inheritance of sos_fs_manager_instance: "super" is the |
| * ancestor (in "OOP" terms). The "super" field might not be the first |
| * field: from a sos_fs_manager_instance, the corresponding |
| * virtfs_instance is given by the |
| * sos_fs_manager_instance::custom_data field |
| */ |
| struct virtfs_instance |
| { |
| /** Ancestor */ |
| struct sos_fs_manager_instance super; |
| |
| /** For the serialization of virtfs "disk" (ie kernel memory) |
| accesses */ |
| struct sos_kmutex lock; |
| |
| /** The list of virtfs nodes "on disk" (ie in kernel memory) */ |
| struct virtfs_node * list_fsnodes; |
| }; |
| |
| |
| /** The description of the "Virtual FS" */ |
| static struct sos_fs_manager_type virtfs_type; |
| |
| |
| /* ********** Forward declarations */ |
| static sos_ret_t virtfs_mount(struct sos_fs_manager_type * this, |
| struct sos_fs_node * device, |
| const char * args, |
| struct sos_fs_manager_instance ** mounted_fs); |
| |
| |
| static sos_ret_t virtfs_umount(struct sos_fs_manager_type * this, |
| struct sos_fs_manager_instance * mounted_fs); |
| |
| |
| static sos_ret_t virtfs_new_mapping(struct sos_umem_vmm_vr *vr); |
| |
| |
| sos_ret_t sos_fs_virtfs_subsystem_setup() |
| { |
| strzcpy(virtfs_type.name, "virtfs", SOS_FS_MANAGER_NAME_MAXLEN); |
| virtfs_type.mount = virtfs_mount; |
| virtfs_type.umount = virtfs_umount; |
| |
| return sos_fs_register_fs_type(& virtfs_type); |
| } |
| |
| |
| /* ******************************************************** |
| * Helper functions |
| */ |
| |
| |
| /* Helper function to serialize "disk" accesses */ |
| inline static void virtfs_lock(struct virtfs_node *a_node) |
| { |
| struct virtfs_instance * fs = (struct virtfs_instance*)a_node->super.fs->custom_data; |
| sos_kmutex_lock(& fs->lock, NULL); |
| } |
| |
| |
| /* Helper function to serialize "disk" accesses */ |
| inline static void virtfs_unlock(struct virtfs_node *a_node) |
| { |
| struct virtfs_instance * fs = (struct virtfs_instance*)a_node->super.fs->custom_data; |
| sos_kmutex_unlock(& fs->lock); |
| } |
| |
| |
| /* Helper function to resize the given virts node (ie kernel memory |
| reallocation) */ |
| static sos_ret_t virtfs_resize(struct virtfs_node *this, |
| sos_size_t new_size) |
| { |
| void * new_data = NULL; |
| |
| if (this->file.size == new_size) |
| return SOS_OK; |
| |
| /* Don't allow to resize the region when the file is being mapped */ |
| if (this->file.num_mappings > 0) |
| return -SOS_EBUSY; |
| |
| if (new_size > 0) |
| { |
| /* Use kmem_vmm_alloc instead of kmalloc to make sure the data |
| WILL be page-aligned (needed by file mapping stuff) */ |
| sos_ui32_t npages = SOS_PAGE_ALIGN_SUP(new_size) / SOS_PAGE_SIZE; |
| new_data = (void*)sos_kmem_vmm_alloc(npages, |
| SOS_KMEM_VMM_MAP); |
| if (! new_data) |
| return -SOS_OK; |
| } |
| |
| /* Copy the data to its new location */ |
| if (this->file.size < new_size) |
| { |
| if (this->file.size > 0) |
| memcpy(new_data, this->file.data, this->file.size); |
| if (new_size > this->file.size) |
| memset(new_data + this->file.size, 0x0, |
| new_size - this->file.size); |
| } |
| else if (new_size > 0) |
| memcpy(new_data, this->file.data, new_size); |
| |
| if (this->file.data) |
| sos_kfree((sos_vaddr_t)this->file.data); |
| this->file.data = new_data; |
| this->file.size = new_size; |
| |
| return SOS_OK; |
| } |
| |
| |
| /* ******************************************************** |
| * Opened file operations |
| */ |
| |
| |
| static sos_ret_t |
| virtfs_duplicate_opened_file(struct sos_fs_opened_file *this, |
| const struct sos_process * for_owner, |
| struct sos_fs_opened_file **result) |
| { |
| *result = (struct sos_fs_opened_file*) |
| sos_kmalloc(sizeof(struct sos_fs_opened_file), 0); |
| if (! *result) |
| return -SOS_ENOMEM; |
| |
| memcpy(*result, this, sizeof(*this)); |
| (*result)->owner = for_owner; |
| return SOS_OK; |
| } |
| |
| |
| static sos_ret_t |
| virtfs_seek(struct sos_fs_opened_file *this, |
| sos_lsoffset_t offset, |
| sos_seek_whence_t whence, |
| /* out */ sos_lsoffset_t * result_position) |
| { |
| sos_lsoffset_t ref_offs; |
| struct virtfs_node * virtfsnode; |
| |
| virtfsnode = (struct virtfs_node*) |
| sos_fs_nscache_get_fs_node(this->direntry)->custom_data; |
| |
| if ( (virtfsnode->super.type != SOS_FS_NODE_REGULAR_FILE) |
| && (virtfsnode->super.type != SOS_FS_NODE_SYMLINK)) |
| return -SOS_ENOSUP; |
| |
| *result_position = this->position; |
| switch (whence) |
| { |
| case SOS_SEEK_SET: |
| ref_offs = 0; |
| break; |
| |
| case SOS_SEEK_CUR: |
| ref_offs = this->position; |
| break; |
| |
| case SOS_SEEK_END: |
| ref_offs = virtfsnode->file.size; |
| break; |
| |
| default: |
| return -SOS_EINVAL; |
| } |
| |
| if (offset < -ref_offs) |
| return -SOS_EINVAL; |
| |
| this->position = ref_offs + offset; |
| *result_position = this->position; |
| return SOS_OK; |
| } |
| |
| |
| static sos_ret_t virtfs_read(struct sos_fs_opened_file *this, |
| sos_uaddr_t dest_buf, |
| sos_size_t * /* in/out */len) |
| { |
| sos_ret_t retval; |
| struct virtfs_node * virtfsnode; |
| |
| virtfsnode = (struct virtfs_node*) |
| sos_fs_nscache_get_fs_node(this->direntry)->custom_data; |
| |
| if ( (virtfsnode->super.type != SOS_FS_NODE_REGULAR_FILE) |
| && (virtfsnode->super.type != SOS_FS_NODE_SYMLINK)) |
| return -SOS_ENOSUP; |
| |
| if (this->position >= virtfsnode->file.size) |
| { |
| *len = 0; |
| return SOS_OK; |
| } |
| |
| virtfs_lock(virtfsnode); |
| |
| if (this->position + *len >= virtfsnode->file.size) |
| *len = virtfsnode->file.size - this->position; |
| |
| retval = sos_memcpy_to_user(dest_buf, |
| ((sos_vaddr_t)virtfsnode->file.data) |
| + this->position, |
| *len); |
| if (retval < 0) |
| { |
| virtfs_unlock(virtfsnode); |
| return retval; |
| } |
| |
| this->position += retval; |
| *len = retval; |
| |
| virtfs_unlock(virtfsnode); |
| |
| return SOS_OK; |
| } |
| |
| |
| static sos_ret_t virtfs_write(struct sos_fs_opened_file *this, |
| sos_uaddr_t src_buf, |
| sos_size_t * /* in/out */len) |
| { |
| sos_ret_t retval; |
| struct virtfs_node * virtfsnode; |
| |
| virtfsnode = (struct virtfs_node*) |
| sos_fs_nscache_get_fs_node(this->direntry)->custom_data; |
| |
| if ( (virtfsnode->super.type != SOS_FS_NODE_REGULAR_FILE) |
| && (virtfsnode->super.type != SOS_FS_NODE_SYMLINK)) |
| return -SOS_ENOSUP; |
| |
| virtfs_lock(virtfsnode); |
| if (this->position + *len >= virtfsnode->file.size) |
| { |
| /* Try to resize if needed */ |
| if (SOS_OK != virtfs_resize(virtfsnode, this->position + *len)) |
| *len = virtfsnode->file.size - this->position; |
| } |
| |
| retval = sos_memcpy_from_user(((sos_vaddr_t)virtfsnode->file.data) |
| + this->position, |
| src_buf, |
| *len); |
| if (retval < 0) |
| { |
| virtfs_unlock(virtfsnode); |
| return retval; |
| } |
| |
| this->position += retval; |
| *len = retval; |
| |
| virtfs_unlock(virtfsnode); |
| |
| return SOS_OK; |
| } |
| |
| |
| static sos_ret_t virtfs_mmap(struct sos_fs_opened_file *this, |
| sos_uaddr_t *uaddr, sos_size_t size, |
| sos_ui32_t access_rights, |
| sos_ui32_t flags, |
| sos_luoffset_t offset) |
| { |
| struct virtfs_node * virtfsnode; |
| |
| virtfsnode = (struct virtfs_node*) |
| sos_fs_nscache_get_fs_node(this->direntry)->custom_data; |
| |
| if (virtfsnode->super.type != SOS_FS_NODE_REGULAR_FILE) |
| return -SOS_ENOSUP; |
| |
| return sos_umem_vmm_map(sos_process_get_address_space(this->owner), |
| uaddr, size, access_rights, |
| flags, & virtfsnode->file.mapres, offset); |
| } |
| |
| |
| static sos_ret_t virtfs_readdir(struct sos_fs_opened_file *this, |
| struct sos_fs_dirent * result) |
| { |
| /* For directories, "position" indicates the "creation_order" of the |
| last readdir operation, and "custom_data" indicates the address |
| of the last directory entry */ |
| |
| struct virtfs_direntry * direntry, * next_direntry; |
| struct virtfs_node * virtfsnode; |
| int nb; |
| |
| virtfsnode = (struct virtfs_node*) |
| sos_fs_nscache_get_fs_node(this->direntry)->custom_data; |
| next_direntry = NULL; |
| |
| /* If the "generation" of the node did not change, the next direntry |
| is the next in the list. We can safely do this because the list |
| of direntries is sorted in increasing creation_order. */ |
| if ((this->generation == virtfsnode->super.generation) |
| && (this->custom_data != NULL)) |
| { |
| direntry = (struct virtfs_direntry*)this->custom_data; |
| next_direntry = direntry->sibling_next; |
| |
| /* Did we go past the end of the list ? */ |
| if (next_direntry == list_get_head_named(virtfsnode->dir.list_entries, |
| sibling_prev, sibling_next)) |
| next_direntry = NULL; |
| } |
| else |
| /* Otherwise we have to lookup the next entry manually */ |
| { |
| /* Lookup the entry that has next creation_order */ |
| next_direntry = NULL; |
| list_foreach_forward_named(virtfsnode->dir.list_entries, |
| direntry, nb, |
| sibling_prev, sibling_next) |
| { |
| if (direntry->creation_order <= this->position) |
| continue; |
| |
| if (! next_direntry) |
| { |
| next_direntry = direntry; |
| continue; |
| } |
| |
| if (direntry->creation_order < next_direntry->creation_order) |
| next_direntry = direntry; |
| } |
| } |
| |
| if (! next_direntry) |
| { |
| this->custom_data = NULL; |
| this->position = 0; |
| return -SOS_ENOENT; |
| } |
| |
| /* Update the result */ |
| result->storage_location = ((sos_vaddr_t)next_direntry->fsnode); |
| result->offset_in_dirfile = next_direntry->creation_order; |
| result->type = next_direntry->fsnode->type; |
| result->namelen = strnlen(next_direntry->name, |
| SOS_FS_DIRENT_NAME_MAXLEN); |
| strzcpy(result->name, next_direntry->name, SOS_FS_DIRENT_NAME_MAXLEN); |
| |
| /* Update the custom data */ |
| this->position = next_direntry->creation_order; |
| this->custom_data = next_direntry; |
| |
| return SOS_OK; |
| } |
| |
| |
| static struct sos_fs_ops_opened_file virtfs_ops_opened_file = |
| (struct sos_fs_ops_opened_file){ |
| .seek = virtfs_seek, |
| .read = virtfs_read, |
| .write = virtfs_write, |
| .mmap = virtfs_mmap |
| }; |
| |
| |
| static struct sos_fs_ops_opened_dir virtfs_ops_opened_dir = |
| (struct sos_fs_ops_opened_dir){ |
| .readdir = virtfs_readdir |
| }; |
| |
| |
| /* ******************************************************** |
| * FS node operations |
| */ |
| |
| static sos_ret_t virtfs_stat_node(struct sos_fs_node * this, |
| struct sos_fs_stat * result) |
| { |
| struct virtfs_node * virtfsnode = (struct virtfs_node*)this->custom_data; |
| result->st_type = this->type; |
| result->st_storage_location = this->storage_location; |
| result->st_access_rights = this->access_rights; |
| result->st_nlink = this->ondisk_lnk_cnt; |
| if (this->type == SOS_FS_NODE_REGULAR_FILE) |
| result->st_size = virtfsnode->file.size; |
| else |
| result->st_size = 0; |
| return SOS_OK; |
| } |
| |
| |
| static sos_ret_t virtfs_truncate(struct sos_fs_node *this, |
| sos_lsoffset_t length) |
| { |
| sos_ret_t retval; |
| struct virtfs_node * virtfsnode; |
| |
| virtfsnode = (struct virtfs_node*) this->custom_data; |
| |
| if ( (virtfsnode->super.type != SOS_FS_NODE_REGULAR_FILE) |
| && (virtfsnode->super.type != SOS_FS_NODE_SYMLINK)) |
| return -SOS_ENOSUP; |
| |
| virtfs_lock(virtfsnode); |
| retval = virtfs_resize(virtfsnode, length); |
| virtfs_unlock(virtfsnode); |
| |
| return retval; |
| } |
| |
| |
| static sos_ret_t virtfs_sync_node(struct sos_fs_node *this) |
| { |
| /* No backing store for virtfs */ |
| return SOS_OK; |
| } |
| |
| |
| static sos_ret_t virtfs_chmod_node(struct sos_fs_node * this, |
| sos_ui32_t access_rights) |
| { |
| this->access_rights = access_rights; |
| return SOS_OK; |
| } |
| |
| |
| /** Callback when nothing (in particular no sos_fs_nscache_node) make |
| reference to the node */ |
| static sos_ret_t virtfs_node_destructor(struct sos_fs_node * this) |
| { |
| /* This callback is called only when the fsnode is not needed |
| anymore by any process or by the kernel. But the node must remain |
| stored "on disk" as long as the FS is mounted AND the node is |
| still "on disk" */ |
| if (this->ondisk_lnk_cnt <= 0) |
| { |
| struct virtfs_instance * virtfs = (struct virtfs_instance*)this->fs->custom_data; |
| struct virtfs_node * virtfsnode = (struct virtfs_node*)this->custom_data; |
| |
| list_delete(virtfs->list_fsnodes, virtfsnode); |
| sos_kfree((sos_vaddr_t) this->custom_data); |
| } |
| |
| return SOS_OK; |
| } |
| |
| |
| static struct sos_fs_node_ops_file virtfs_ops_file = |
| (struct sos_fs_node_ops_file){ |
| .truncate = virtfs_truncate, |
| .stat = virtfs_stat_node, |
| .sync = virtfs_sync_node, |
| .chmod = virtfs_chmod_node |
| }; |
| |
| |
| static sos_ret_t virtfs_new_opened_file(struct sos_fs_node * this, |
| const struct sos_process * owner, |
| sos_ui32_t open_flags, |
| struct sos_fs_opened_file ** result_of) |
| { |
| struct sos_fs_opened_file * of |
| = (struct sos_fs_opened_file*)sos_kmalloc(sizeof(*of), 0); |
| if (! of) |
| return -SOS_ENOMEM; |
| |
| memset(of, 0x0, sizeof(*of)); |
| of->owner = owner; |
| of->duplicate = virtfs_duplicate_opened_file; |
| of->open_flags = open_flags; |
| of->ops_file = & virtfs_ops_opened_file; |
| if (this->type == SOS_FS_NODE_DIRECTORY) |
| of->ops_dir = & virtfs_ops_opened_dir; |
| |
| *result_of = of; |
| return SOS_OK; |
| } |
| |
| |
| static sos_ret_t virtfs_close_opened_file(struct sos_fs_node * this, |
| struct sos_fs_opened_file * of) |
| { |
| sos_kfree((sos_vaddr_t)of); |
| return SOS_OK; |
| } |
| |
| |
| static sos_ret_t virtfs_symlink_expand(struct sos_fs_node *this, |
| char const ** target, |
| sos_size_t * target_len) |
| { |
| struct virtfs_node * virtfsnode = (struct virtfs_node*)this->custom_data; |
| |
| *target = virtfsnode->file.data; |
| *target_len = virtfsnode->file.size; |
| |
| return SOS_OK; |
| } |
| |
| |
| static struct sos_fs_node_ops_symlink virtfs_ops_symlink |
| = (struct sos_fs_node_ops_symlink){ |
| .expand = virtfs_symlink_expand |
| }; |
| |
| |
| static sos_ret_t virtfs_dir_lookup(struct sos_fs_node *this, |
| const char * name, sos_ui16_t namelen, |
| sos_ui64_t * result_storage_location) |
| { |
| struct virtfs_node * virtfsnode = (struct virtfs_node*)this->custom_data; |
| struct virtfs_direntry * direntry; |
| int nbentries; |
| |
| list_foreach_forward_named(virtfsnode->dir.list_entries, |
| direntry, nbentries, |
| sibling_prev, sibling_next) |
| { |
| if (!memcmp(name, direntry->name, namelen) && !direntry->name[namelen]) |
| { |
| *result_storage_location = direntry->fsnode->storage_location; |
| return SOS_OK; |
| } |
| } |
| |
| return -SOS_ENOENT; |
| } |
| |
| |
| static sos_ret_t virtfs_link(struct sos_fs_node *this, |
| const struct sos_process *actor, |
| const char * entry_name, sos_ui16_t entry_namelen, |
| struct sos_fs_node * node) |
| { |
| struct virtfs_node * parent = (struct virtfs_node*)this->custom_data; |
| struct virtfs_direntry * direntry; |
| |
| direntry = (struct virtfs_direntry*)sos_kmalloc(sizeof(*direntry), 0); |
| if (! direntry) |
| return -SOS_ENOMEM; |
| |
| direntry->name = (char*)sos_kmalloc(entry_namelen + 1, 0); |
| if (! direntry->name) |
| { |
| sos_kfree((sos_vaddr_t)direntry->name); |
| return -SOS_ENOMEM; |
| } |
| |
| memcpy(direntry->name, entry_name, entry_namelen); |
| direntry->name[entry_namelen] = '\0'; |
| |
| direntry->fsnode = node; |
| node->ondisk_lnk_cnt ++; |
| this->ondisk_lnk_cnt ++; |
| list_add_tail_named(parent->dir.list_entries, direntry, |
| sibling_prev, sibling_next); |
| |
| /* Update the index of the new entry in order for the next readdirs |
| to fetch this new entry */ |
| parent->dir.top_creation_order ++; |
| direntry->creation_order = parent->dir.top_creation_order; |
| |
| return SOS_OK; |
| } |
| |
| |
| static sos_ret_t |
| virtfs_unlink(struct sos_fs_node *this, |
| const struct sos_process *actor, |
| const char * entry_name, sos_ui16_t entry_namelen) |
| { |
| struct virtfs_node * parent = (struct virtfs_node*)this->custom_data; |
| struct virtfs_direntry * direntry; |
| int nbentries; |
| |
| list_foreach_forward_named(parent->dir.list_entries, |
| direntry, nbentries, |
| sibling_prev, sibling_next) |
| { |
| if (!memcmp(entry_name, direntry->name, entry_namelen) |
| && !direntry->name[entry_namelen]) |
| { |
| list_delete_named(parent->dir.list_entries, direntry, |
| sibling_prev, sibling_next); |
| direntry->fsnode->ondisk_lnk_cnt --; |
| this->ondisk_lnk_cnt --; |
| sos_kfree((sos_vaddr_t)direntry); |
| return SOS_OK; |
| } |
| } |
| |
| return -SOS_ENOENT; |
| } |
| |
| |
| static struct sos_fs_node_ops_dir virtfs_ops_dir |
| = (struct sos_fs_node_ops_dir){ |
| .lookup = virtfs_dir_lookup, |
| .link = virtfs_link, |
| .unlink = virtfs_unlink |
| }; |
| |
| |
| /* ******************************************************** |
| * FS instance operations |
| */ |
| |
| |
| /** Simulate the access to a node located on disk. In virtfs, the |
| "disk" is directly the kernel memory, so the |
| "sos_fs_node::storage_location field corresponds to a kernel |
| address */ |
| static sos_ret_t |
| virtfs_fetch_node_from_disk(struct sos_fs_manager_instance * this, |
| sos_ui64_t storage_location, |
| struct sos_fs_node ** result) |
| { |
| /* The "disk" is simply the ram */ |
| struct virtfs_node * virtfsnode; |
| |
| virtfsnode = (struct virtfs_node *)((sos_vaddr_t)storage_location); |
| *result = & virtfsnode->super; |
| |
| return SOS_OK; |
| } |
| |
| |
| static sos_ret_t |
| virtfs_allocate_new_node(struct sos_fs_manager_instance * this, |
| sos_fs_node_type_t type, |
| const struct sos_process * creator, |
| sos_ui32_t access_rights, |
| sos_ui32_t flags, |
| struct sos_fs_node ** result) |
| { |
| struct virtfs_node * virtfsnode; |
| |
| /* Allow only DIRs or FILEs */ |
| if ((type != SOS_FS_NODE_REGULAR_FILE) |
| && (type != SOS_FS_NODE_SYMLINK) |
| && (type != SOS_FS_NODE_DIRECTORY)) |
| return -SOS_ENOSUP; |
| |
| virtfsnode = (struct virtfs_node*) sos_kmalloc(sizeof(*virtfsnode), 0); |
| if (! virtfsnode) |
| return -SOS_ENOMEM; |
| |
| memset(virtfsnode, 0x0, sizeof(*virtfsnode)); |
| *result = & virtfsnode->super; |
| |
| /* Initialize "file" */ |
| (*result)->inmem_ref_cnt = 1; |
| (*result)->custom_data = virtfsnode; |
| (*result)->storage_location = (sos_ui64_t)((sos_vaddr_t)virtfsnode); |
| (*result)->type = type; |
| (*result)->access_rights = access_rights; |
| (*result)->destructor = virtfs_node_destructor; |
| (*result)->new_opened_file = virtfs_new_opened_file; |
| (*result)->close_opened_file = virtfs_close_opened_file; |
| (*result)->ops_file = & virtfs_ops_file; |
| if (type == SOS_FS_NODE_SYMLINK) |
| (*result)->ops_symlink = & virtfs_ops_symlink; |
| else if (type == SOS_FS_NODE_DIRECTORY) |
| (*result)->ops_dir = & virtfs_ops_dir; |
| |
| /* Initialize mapping structure */ |
| if (type == SOS_FS_NODE_REGULAR_FILE) |
| { |
| virtfsnode->file.mapres.allowed_access_rights |
| = SOS_VM_MAP_PROT_READ |
| | SOS_VM_MAP_PROT_WRITE |
| | SOS_VM_MAP_PROT_EXEC; |
| virtfsnode->file.mapres.custom_data = virtfsnode; |
| virtfsnode->file.mapres.mmap = virtfs_new_mapping; |
| } |
| |
| list_add_tail(((struct virtfs_instance*)this->custom_data)->list_fsnodes, |
| virtfsnode); |
| |
| return SOS_OK; |
| } |
| |
| |
| /* ******************************************************** |
| * FS type (mount/umount) operations |
| */ |
| |
| static sos_ret_t virtfs_mount(struct sos_fs_manager_type * this, |
| struct sos_fs_node * device, |
| const char * args, |
| struct sos_fs_manager_instance ** mounted_fs) |
| { |
| sos_ret_t retval; |
| struct virtfs_instance * fs; |
| struct sos_fs_node * fsnode_root; |
| struct sos_hash_table * hash; |
| |
| *mounted_fs = (struct sos_fs_manager_instance*)NULL; |
| |
| /* Create FS node hash table */ |
| hash = sos_hash_create("virtfs H", struct sos_fs_node, |
| sos_hash_ui64, |
| sos_hash_key_eq_ui64, 17, |
| storage_location, hlink_nodecache); |
| if (! hash) |
| return -SOS_ENOMEM; |
| |
| fs = (struct virtfs_instance*) |
| sos_kmalloc(sizeof(struct virtfs_instance), 0); |
| if (! fs) |
| { |
| sos_hash_dispose(hash); |
| return -SOS_ENOMEM; |
| } |
| |
| memset(fs, 0x0, sizeof(struct virtfs_instance)); |
| retval = sos_kmutex_init(& fs->lock, "virtfs", SOS_KWQ_ORDER_FIFO); |
| if (SOS_OK != retval) |
| { |
| sos_hash_dispose(hash); |
| sos_kfree((sos_vaddr_t) fs); |
| return retval; |
| } |
| fs->super.custom_data = fs; |
| fs->super.fs_type = this; |
| fs->super.allocate_new_node = virtfs_allocate_new_node; |
| fs->super.fetch_node_from_disk = virtfs_fetch_node_from_disk; |
| fs->super.nodecache = hash; |
| |
| retval = virtfs_allocate_new_node(& fs->super, SOS_FS_NODE_DIRECTORY, |
| NULL, |
| SOS_FS_READABLE | SOS_FS_WRITABLE |
| | SOS_FS_EXECUTABLE, |
| 0, |
| & fsnode_root); |
| if (SOS_OK != retval) |
| { |
| sos_hash_dispose(hash); |
| sos_kmutex_dispose(& fs->lock); |
| sos_kfree((sos_vaddr_t) fs); |
| return retval; |
| } |
| |
| retval = sos_fs_register_fs_instance(& fs->super, fsnode_root); |
| sos_fs_unref_fsnode(fsnode_root); |
| if (SOS_OK != retval) |
| { |
| sos_hash_dispose(hash); |
| sos_kmutex_dispose(& fs->lock); |
| sos_kfree((sos_vaddr_t) fs); |
| return retval; |
| } |
| |
| *mounted_fs = & fs->super; |
| return SOS_OK; |
| } |
| |
| |
| static sos_ret_t virtfs_umount(struct sos_fs_manager_type * this, |
| struct sos_fs_manager_instance * mounted_fs) |
| { |
| struct virtfs_instance * virtfs = (struct virtfs_instance*)mounted_fs->custom_data; |
| |
| sos_hash_dispose(virtfs->super.nodecache); |
| while (! list_is_empty(virtfs->list_fsnodes)) |
| { |
| struct virtfs_node * virtfsnode = list_pop_head(virtfs->list_fsnodes); |
| |
| if (virtfsnode->super.type == SOS_FS_NODE_REGULAR_FILE) |
| { |
| if (virtfsnode->file.size > 0) |
| sos_kfree((sos_vaddr_t) virtfsnode->file.data); |
| } |
| else if (virtfsnode->super.type == SOS_FS_NODE_DIRECTORY) |
| { |
| while (! list_is_empty_named(virtfsnode->dir.list_entries, |
| sibling_prev, sibling_next)) |
| { |
| struct virtfs_direntry * direntry |
| = list_pop_head_named(virtfsnode->dir.list_entries, |
| sibling_prev, sibling_next); |
| |
| sos_kfree((sos_vaddr_t)direntry->name); |
| sos_kfree((sos_vaddr_t)direntry); |
| } |
| } |
| |
| sos_kfree((sos_vaddr_t)virtfsnode); |
| } |
| |
| sos_fs_unregister_fs_instance(& virtfs->super); |
| sos_kmutex_dispose(& virtfs->lock); |
| sos_kfree((sos_vaddr_t)virtfs); |
| return SOS_OK; |
| } |
| |
| |
| /* ******************************************************** |
| * File mapping stuff |
| */ |
| inline static struct virtfs_node * |
| get_virtfsnode_of_vr(struct sos_umem_vmm_vr * vr) |
| { |
| struct sos_umem_vmm_mapped_resource *mr |
| = sos_umem_vmm_get_mapped_resource_of_vr(vr); |
| |
| return (struct virtfs_node *)mr->custom_data; |
| } |
| |
| |
| static void virtfs_map_ref(struct sos_umem_vmm_vr * vr) |
| { |
| struct virtfs_node * virtfsnode = get_virtfsnode_of_vr(vr); |
| sos_fs_ref_fsnode(& virtfsnode->super); |
| virtfsnode->file.num_mappings ++; |
| } |
| |
| |
| static void virtfs_map_unref(struct sos_umem_vmm_vr * vr) |
| { |
| struct virtfs_node * virtfsnode = get_virtfsnode_of_vr(vr); |
| |
| SOS_ASSERT_FATAL(virtfsnode->file.num_mappings > 0); |
| virtfsnode->file.num_mappings --; |
| |
| _sos_fs_unref_fsnode(& virtfsnode->super); |
| } |
| |
| |
| static sos_ret_t virtfs_map_page_in(struct sos_umem_vmm_vr * vr, |
| sos_uaddr_t uaddr, |
| sos_bool_t write_access) |
| { |
| struct virtfs_node * virtfsnode = get_virtfsnode_of_vr(vr); |
| sos_luoffset_t offset = uaddr - sos_umem_vmm_get_start_of_vr(vr); |
| sos_ret_t retval = SOS_OK; |
| sos_paddr_t ppage_paddr; |
| |
| /* The region is not allowed to be resized */ |
| if (SOS_PAGE_ALIGN_SUP(offset) > virtfsnode->file.size) |
| return -SOS_EFAULT; |
| |
| /* Lookup physical kernel page */ |
| ppage_paddr = sos_paging_get_paddr(SOS_PAGE_ALIGN_INF(virtfsnode->file.data |
| + offset)); |
| |
| /* Cannot access unmapped kernel pages */ |
| if (! ppage_paddr) |
| return -SOS_EFAULT; |
| |
| /* Remap it in user space */ |
| retval = sos_paging_map(ppage_paddr, |
| SOS_PAGE_ALIGN_INF(uaddr), |
| TRUE, |
| sos_umem_vmm_get_prot_of_vr(vr)); |
| |
| return retval; |
| } |
| |
| |
| static struct sos_umem_vmm_vr_ops virtfs_map_ops |
| = (struct sos_umem_vmm_vr_ops){ |
| .ref = virtfs_map_ref, |
| .unref = virtfs_map_unref, |
| .page_in = virtfs_map_page_in |
| }; |
| |
| static sos_ret_t virtfs_new_mapping(struct sos_umem_vmm_vr *vr) |
| { |
| struct virtfs_node * virtfsnode = get_virtfsnode_of_vr(vr); |
| sos_size_t reqsize; |
| sos_ret_t retval; |
| |
| reqsize = sos_umem_vmm_get_offset_in_resource(vr); |
| reqsize += sos_umem_vmm_get_size_of_vr(vr); |
| |
| /* Resize the region NOW */ |
| if (reqsize > virtfsnode->file.size) |
| { |
| retval = virtfs_resize(virtfsnode, |
| SOS_PAGE_ALIGN_SUP(reqsize)); |
| if (SOS_OK != retval) |
| return retval; |
| } |
| |
| return sos_umem_vmm_set_ops_of_vr(vr, &virtfs_map_ops); |
| } |
| |
/tmp/sos-code-article7.5/sos/fs.c (1970-01-01 01:00:00.000000000 +0100
) |
|
../sos-code-article8/sos/fs.c (2005-07-01 16:39:49.000000000 +0200
) |
|
|
|
| /* 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_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) |
| { |
| 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_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; |
| |
| /* 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_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; |
| |
| 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) |
| { |
| sos_fs_nscache_unref_node(nsnode); |
| 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; |
| |
| (*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)) |
| 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 ! */ |
| 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; |
| } |
| } |
| 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_ENOSUP; |
| |
| 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_ENOSUP; |
| |
| 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_ENOSUP; |
| |
| 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_ENOSUP; |
| |
| 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_ENOSUP; |
| |
| /* 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_ENOSUP; |
| |
| return of->ops_file->fcntl(of, req_id, req_arg); |
| } |
| |
| |
| 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_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 fsnode->ops_file->sync(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_ENOSUP; |
| |
| 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; |
| |
| list_foreach(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; |
| } |
| } |
| |
| lookup_next_fs: |
| /* Loop over */ |
| dummy ++; |
| } |
| |
| return SOS_OK; |
| } |
| |
| |
| |
| /* ************************************************************* |
| * 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; |
| } |
| |
/tmp/sos-code-article7.5/sos/fs.h (1970-01-01 01:00:00.000000000 +0100
) |
|
../sos-code-article8/sos/fs.h (2005-07-01 16:39:49.000000000 +0200
) |
|
|
|
| /* 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. |
| */ |
| #ifndef _SOS_FS_H_ |
| #define _SOS_FS_H_ |
| |
| |
| /** |
| * @file fs.h |
| * |
| * (Virtual) Filesystem management. |
| * |
| * SOS provides a complete Unix-like file system service. Supported |
| * features of this service are: |
| * - mountpoints |
| * - generic file system support (FS) through object-oriented |
| * specialization (so-called VFS) |
| * - hard & symbolic links |
| * - regular files and directories |
| * - block and character device special files (from article 9 onward) |
| * - file mapping |
| * - basic permission management ("rwx" only, no notion of owner) |
| * - chroot |
| * - separation of disk node and namespace notions allowing hard links |
| * and to move/rename/remove files or directories that are in use |
| * - basic FS interface (open/read/seek/creat/mkdir/rename/link |
| * / symlink/chmod/mount/fcntl/ioctl...) |
| * - deferred writes (ie file caching). @see sync(3) |
| * |
| * Among the unsupported features: |
| * - no user-based permission (uid/gid, ACLS) |
| * - no modification / access time accounting |
| * - no Fifo/socket special files (yet) |
| * - no generic support library for common fcntl commands |
| * (F_SETOWN/GETLEASE/NOTIFY, etc.) |
| * - no flock-style functions |
| * |
| * Rationale |
| * ========= |
| * The VFS layer is based on 3 central concepts: |
| * |
| * - The meta-information for each file stored on disk (struct |
| * sos_fs_node for SOS, inode for Unix) |
| * |
| * It is sufficient to know where this meta-information is located |
| * on disk (a simple sector number most of the time) to build the |
| * corresponding struct sos_fs_node into memory and to retrieve the |
| * data of the file from the disk |
| * |
| * For example, consider that we know a sector that holds the meta |
| * information (ie size, permissions) is located at sector 64589 on |
| * disk. By retrieving this meta information directly from disk, we |
| * can build the struct sos_fs_node, which would (for example) tell |
| * that the corresponding file spans (for example) over sectors |
| * 4345, 5645, 4539 and 6575, is 1.7kB long |
| * |
| * Everything is stored this way on the disk, even the |
| * directories. From the disk contents' point of view, a directory |
| * is simply a file whose contents represent a list of mappings |
| * "name" -> meta-information location |
| * |
| * - One or many nodes in the file hierarchy pointing to this data |
| * (struct sos_fs_nscache_node for SOS, struct dentry for Linux). This |
| * tells that the entry "toto" in directory "/home/zorglub" |
| * corresponds to the given struct sos_fs_node |
| * |
| * Actually, with the struct sos_fs_node above, we can reach any |
| * file in the system. However, dealing with mountpoints requires |
| * an intermediary data structure because a directory on a disk |
| * cannot make reference to children struct sos_fs_node on other |
| * disk. This is one of the reasons why there is this struct |
| * sos_fs_nscache_node. Another reason is that we kind-of "cache" the |
| * most used struct sos_fs_node: those that lead from the global |
| * root ("/") to the files and directories currently being used |
| * (hence the name "nscache" for "namespace cache"). This speeds-up |
| * the path-resolving process (aka "lookup"), as the most-used path |
| * are already in-memory and the struct sos_fs_node are already |
| * in-memory too. |
| * |
| * A struct sos_fs_nscache_node can have at most 1 parent (the ".." |
| * entry). It can also have 0 parent in case the node is being used |
| * by a process (file is opened or mapped), but the file is |
| * actually "removed", ie un-reachable from any directory. |
| * |
| * Many such structures can reference the same underlying struct |
| * sos_fs_node, which enables the support of "hard links". |
| * |
| * - The "opened file" strucures. They store the information |
| * pertaining to a particular usage of a file. The most important |
| * thing they store is the "file pointer", which holds the |
| * location in the file where the next read/write operation should |
| * start |
| * |
| * Each process has at least 2 such opened files: its "current |
| * working directory" (RTFM chdir) and its "process root" (RTFM |
| * chroot). Those are heritated across fork() and can be changed by |
| * appropriate syscalls (resp. chdir/chroot). The main "root" of |
| * the system is the process root of the "init" process. The usual |
| * opened files (think of open() and opendir()) are stored in the |
| * file descriptor array (fds[]). This is the index in this array |
| * that is commonly called a "file descriptor". |
| * |
| * |
| * The whole VFS layer comprises a series of objects that can be |
| * specialized to implement different FS support (fat, ext2, ffs, ...): |
| * |
| * - The notion of "file system manager", which basically is a |
| * container to a FS name (eg "FAT", "EXT2", etc...) and a series of |
| * functions responsible for initializing a particular "mounting" of |
| * a FS (the "mount" method). This is SOS's struct sos_fs_manager_type |
| * |
| * - The notion of "file system instance" which contains the data |
| * proper to a particular mounting of an FS. Its most important job |
| * is to allocate new struct sos_fs_node on disk, or to retrieve the |
| * meta-information (ie struct sos_fs_node) located at the given |
| * location on disk. This is roughly THE primary physical interface |
| * between the VFS and the disks. This is SOS's struct |
| * sos_fs_manager_instance, aka the Linux's superblock |
| * |
| * For each struct sos_fs_node that it allocates, or that is loads |
| * from disk into memory, this "instance manager" is responsible |
| * for inidicating the functions that implement the FS-dedicated |
| * routine such as read/write/mmap/ioctl/... for this precise node. |
| * |
| * The nodes (struct sos_fs_node) of a struct |
| * sos_fs_manager_instance that are currently loaded in memory are |
| * stored in a hash-table. The key of this map is the location of the |
| * meta-information on disk. That way, it is very fast to look for |
| * the given meta-information whose location on disk is knows: if |
| * it has already been loaded into memory, its memory address is |
| * quickly resolved thanks to this hash table. |
| */ |
| |
| #include <sos/types.h> |
| #include <sos/errno.h> |
| #include <sos/hash.h> |
| #include <sos/umem_vmm.h> |
| |
| /* Forward declarations (structures defined in this file) */ |
| struct sos_fs_manager_type; |
| struct sos_fs_manager_instance; |
| struct sos_fs_statfs; |
| struct sos_fs_node; |
| struct sos_fs_opened_file; |
| struct sos_fs_stat; |
| |
| #include "fs_nscache.h" |
| |
| /** |
| * The type of filesystem object. |
| * |
| * Each struct sos_fs_node has a type. Here are the supported types. |
| */ |
| typedef enum { |
| SOS_FS_NODE_REGULAR_FILE = 0x42, |
| SOS_FS_NODE_DIRECTORY = 0x24, |
| SOS_FS_NODE_SYMLINK = 0x84, |
| } sos_fs_node_type_t; |
| |
| |
| #define SOS_FS_MANAGER_NAME_MAXLEN 32 |
| /** |
| * Description of a supported Filesystem type. |
| * |
| * These descriptions are listed in an internal list (see |
| * fs.c:fs_list), and each time we want to mount a FS, we precise a |
| * name (eg "FAT", "EXT2", ...). The VFS will look for this name into |
| * the list of supported filesystem types, and, when found, call its |
| * sos_fs_manager_type::mount() method. |
| * |
| * New filesystem types are registered using sos_fs_register_fs_type() |
| */ |
| struct sos_fs_manager_type |
| { |
| char name[SOS_FS_MANAGER_NAME_MAXLEN]; |
| |
| /** |
| * Responsible for making sure the underlying device (if any) really |
| * stores the correct filesystem format, for creating the hash of fs |
| * nodes and for calling sos_fs_register_fs_instance |
| * |
| * @param device May be NULL |
| * |
| * @note mandatory, may block |
| */ |
| sos_ret_t (*mount)(struct sos_fs_manager_type * this, |
| struct sos_fs_node * device, |
| const char * args, |
| struct sos_fs_manager_instance ** mounted_fs); |
| |
| /** |
| * Responsible for de-allocating the hash of fs nodes and for |
| * calling sos_fs_unregister_fs_instance |
| * |
| * @note mandatory, may block |
| */ |
| sos_ret_t (*umount)(struct sos_fs_manager_type * this, |
| struct sos_fs_manager_instance * mounted_fs); |
| |
| /** Free of use */ |
| void * custom_data; |
| |
| /** List of filesystem instances of this type currently mounted |
| somewhere in the system */ |
| struct sos_fs_manager_instance * instances; |
| |
| /** Linkage for the list of filesystem types registered in the |
| system */ |
| struct sos_fs_manager_type *prev, *next; |
| }; |
| |
| |
| /** |
| * Data related to a particular "mounting" of a file system. A so-called "superblock" under Linux |
| * |
| * This holds the FUNDAMENTAL functions responsible for loading struct |
| * sos_fs_node from disk, or for allocating thom on disk. It also |
| * holds the hash-table of struct sos_fs_node already loaded into |
| * memory. |
| */ |
| struct sos_fs_manager_instance |
| { |
| /** |
| * @note Publicly readable. Written only by sos_fs_manager_type::mount() |
| */ |
| struct sos_fs_manager_type * fs_type; |
| |
| /** |
| * Usually, a filesystem relies on a device (disk, network, ram, |
| * ...) to fetch its data. This is the location of the device. |
| * |
| * @note Publicly readable. Written only by fs.c |
| */ |
| struct sos_fs_node * device; |
| |
| #define SOS_FS_MOUNT_SYNC (1 << 0) |
| #define SOS_FS_MOUNT_READONLY (1 << 1) |
| #define SOS_FS_MOUNT_NOEXEC (1 << 2) |
| /** |
| * Is this FS read-only, without EXEC file permission, write-through |
| * ? Or-red combination of the SOS_FS_MOUNT_ flags |
| * |
| * @note Publicly readable. Written only by fs.c |
| */ |
| sos_ui32_t flags; |
| |
| /** |
| * The namespace node that is the root of THIS file system mounting |
| * |
| * @note Publicly readable. Written only by fs.c |
| */ |
| struct sos_fs_nscache_node * root; |
| |
| /** |
| * List of dirty nodes. These are the nodes that need to be written |
| * back to disk. With FS supporting deferred-writes, the |
| * sos_fs_sync() function will use this list to flush the dirty |
| * nodes back to disk. |
| * |
| * @note Reserved to fs.c |
| */ |
| struct sos_fs_node * dirty_nodes; |
| |
| /** |
| * Build a fresh new FS node at the given location. This implies |
| * the allocation of a new sos_fs_node structure in memory |
| * |
| * @note Mandatory, may block. Appropriate locking MUST be implemented |
| */ |
| sos_ret_t (*fetch_node_from_disk)(struct sos_fs_manager_instance * this, |
| sos_ui64_t storage_location, |
| struct sos_fs_node ** result); |
| |
| /** |
| * Build a fresh new FS node ON THE DISK of the given type (dir, |
| * plain file, symlink, ...), completely empty ; return a newly |
| * allocated IN-MEMORY node structure representing it |
| * |
| * @param open_creat_flags is the open_flags parameter passed to |
| * sos_fs_open() when O_CREAT is set. 0 when allocated trough |
| * creat/mkdir/mknod/symlink |
| * |
| * @note Mandatory, may block. Appropriate locking MUST be implemented |
| */ |
| sos_ret_t (*allocate_new_node)(struct sos_fs_manager_instance * this, |
| sos_fs_node_type_t type, |
| const struct sos_process * creator, |
| sos_ui32_t access_rights, |
| sos_ui32_t open_creat_flags, |
| struct sos_fs_node ** result); |
| |
| /** |
| * Return filesystem status (RTFM df) |
| * |
| * @note Optional, may block. Appropriate locking MUST be implemented |
| */ |
| sos_ret_t (*statfs)(struct sos_fs_manager_instance * this, |
| struct sos_fs_statfs * result); |
| |
| /** |
| * Comparison callback called when looking for file/dirs in the |
| * namespace cache. Normally, a usual lexicographical comparison is |
| * done (when this function points to NULL). But for some FS, it |
| * might be useful to use another comparison function (eg for |
| * case-insensitive FS) |
| * |
| * @note Optional (may be NULL), must NOT block |
| */ |
| sos_bool_t (*nsnode_same_name)(const char * name1, sos_ui16_t namelen1, |
| const char * name2, sos_ui16_t namelen2); |
| |
| /** |
| * Hash table of the struct sos_fs_node of this filesystem instance |
| * loaded in memory: key=storage_location, element=sos_fs_node |
| */ |
| struct sos_hash_table * nodecache; |
| |
| /** |
| * Unique identifier of this FS (used in sync method, updated by |
| * fs.c). This enables sync_all_fs to be resilient to mount/umount |
| * and (un)register_fs_type/instance |
| */ |
| sos_ui64_t uid; |
| |
| void * custom_data; |
| |
| /** Linkage for the list of instances for the underlying fs type */ |
| struct sos_fs_manager_instance * prev, * next; |
| }; |
| |
| |
| /** |
| * The CENTRAL data structure of the whole thing. A so-called "inode" |
| * |
| * This represents the meta-information related to a file on disk: its |
| * permission, where its data is located. Actually, in SOS, these |
| * information are not stored in this structure. Instead, we define a |
| * series of methods in this structure that MUST be implemented by the |
| * FS and that realize the higher level operations needed by the |
| * OS. These operations will rely on the meta-information that the FS |
| * code MUST define and manage by itself (hence the |
| * sos_fs_node::custom_data field). |
| */ |
| struct sos_fs_node |
| { |
| /** |
| * An struct sos_fs_node always belong to exactly ONE file system |
| */ |
| struct sos_fs_manager_instance * fs; |
| |
| /** |
| * The so-called "inode": location of this node inside the FS |
| * instance. Updated by struct |
| * sos_fs_manager_instance::fetch_node_from_disk() |
| */ |
| sos_ui64_t storage_location; |
| |
| /** |
| * Number of ON-DISK links to this node. |
| * |
| * - For everything but directories: the number of hard links to the file |
| * - For directories: 1 + the number of children nodes |
| * |
| * @note Publicly readable. Written only by |
| * sos_fs_node_ops_dir::link() and sos_fs_node_ops_dir::unlink() |
| */ |
| sos_count_t ondisk_lnk_cnt; |
| |
| /** |
| * Number of IN-MEMORY nscache_nodes referencing this FS node. |
| * |
| * Corresponds to the number of struct sos_fs_nscache_node pointing |
| * to this node. This could be as much as ondisk_lnk_cnt + 1, but is |
| * usually less |
| * |
| * @note Reserved to fs.c |
| */ |
| sos_count_t inmem_ref_cnt; |
| |
| /** |
| * Directory, symlink, ... |
| * |
| * @see sos_fs_node_type_t |
| * |
| * @note Publicly readable. Written only by fs.c |
| */ |
| sos_fs_node_type_t type; |
| |
| #define SOS_FS_READABLE 00400 |
| #define SOS_FS_WRITABLE 00200 |
| #define SOS_FS_EXECUTABLE 00100 |
| /** |
| * read/write, ... @see the SOS_FS_*ABLE flags |
| * @note Publicly readable. Written only by fs.c |
| */ |
| sos_ui32_t access_rights; |
| |
| /** |
| * @note Reserved to fs.c |
| */ |
| sos_bool_t dirty; |
| |
| /** |
| * Incremented each time one of the opened files for this node is |
| * modified |
| * @note Publicly readable. Written only by fs.c |
| */ |
| sos_lcount_t generation; |
| |
| /** Operations common to all node types */ |
| struct sos_fs_node_ops_file *ops_file; |
| |
| /** Operations specific to some node types */ |
| union |
| { |
| /** when type == SOS_FS_NODE_DIRECTORY */ |
| struct sos_fs_node_ops_dir *ops_dir; |
| |
| /** when type == SOS_FS_NODE_SYMLINK */ |
| struct sos_fs_node_ops_symlink *ops_symlink; |
| }; /* Anonymous union (gcc extension) */ |
| |
| |
| /** |
| * Simply free this FS node from the kernel memory: it does NOT |
| * mean that the corresponding on-disk node is free ! Actually, the |
| * corresponding ON-DISK node is free iff ondisk_lnk_cnt == 0. No |
| * need to sync anything to disk, as the VFS will sync the node |
| * before calling this method |
| * |
| * @note Mandatory, may block, no special locking needed |
| */ |
| sos_ret_t (*destructor)(struct sos_fs_node * this); |
| |
| /** |
| * Called when a process opens the node |
| * |
| * @note Mandatory, may block. Appropriate locking MUST be implemented |
| */ |
| sos_ret_t (*new_opened_file)(struct sos_fs_node * this, |
| const struct sos_process * owner, |
| sos_ui32_t open_flags, |
| struct sos_fs_opened_file ** result_of); |
| |
| /** |
| * Called when a process opens the node |
| * |
| * @note Mandatory, may block. Appropriate locking MUST be implemented |
| */ |
| sos_ret_t (*close_opened_file)(struct sos_fs_node * this, |
| struct sos_fs_opened_file * of); |
| |
| /** |
| * This should hold the meta information for this node as needed by |
| * the FS instance. |
| */ |
| void * custom_data; |
| |
| /** Hash linkage entry for this FS node in the nodecache |
| dictionary */ |
| struct sos_hash_linkage hlink_nodecache; |
| |
| /** Linkage to list the dirty nodes of the given FS */ |
| struct sos_fs_node *prev_dirty, *next_dirty; |
| }; |
| |
| |
| |
| /** |
| * The list of methods implementing the basic VFS operations on the |
| * given struct sos_fs_node |
| * |
| * @see sos_fs_node::ops_file |
| */ |
| struct sos_fs_node_ops_file |
| { |
| /** |
| * Change size of file |
| * |
| * @note Optional, may block. Appropriate locking MUST be implemented |
| */ |
| sos_ret_t (*truncate)(struct sos_fs_node *this, |
| sos_lsoffset_t length); |
| |
| /** |
| * Retrieve the status (eg size) of the file |
| * |
| * @note Mandatory, may block. Appropriate locking MUST be implemented |
| */ |
| sos_ret_t (*stat)(struct sos_fs_node * this, |
| struct sos_fs_stat * result); |
| |
| /** |
| * Flush any change to disk |
| * |
| * @note Mandatory, may block. Appropriate locking MUST be implemented |
| */ |
| sos_ret_t (*sync)(struct sos_fs_node *this); |
| |
| /** |
| * Change the sos_fs_node::access_rights attribute |
| * |
| * @note Mandatory, may block. Appropriate locking MUST be implemented |
| */ |
| sos_ret_t (*chmod)(struct sos_fs_node * this, |
| sos_ui32_t new_access_rights); |
| }; |
| |
| |
| /** |
| * The list of methods implementing the basic VFS symlink operations |
| * |
| * @see sos_fs_node::ops_symlink |
| */ |
| struct sos_fs_node_ops_symlink |
| { |
| /** |
| * Used by the _kernel_ to resolve the symlinks. To change/create a |
| * symlink target, it is needed only from userland: the read/write |
| * methods are made for this |
| * |
| * @param target Pointer to the string representing the target's |
| * path, allocated for the fs_node's lifetime ! |
| * |
| * @note Mandatory, may block. Appropriate locking MUST be implemented |
| */ |
| sos_ret_t (*expand)(struct sos_fs_node *this, |
| char const ** target, |
| sos_size_t * target_len); |
| }; |
| |
| |
| /** |
| * The list of methods implementing the basic VFS directory operations |
| * |
| * @see sos_fs_node::ops_dir |
| */ |
| struct sos_fs_node_ops_dir |
| { |
| /** |
| * Look for the on-disk location of the sos_fs_node having the given |
| * name |
| * |
| * @note Mandatory, may block. Appropriate locking MUST be implemented |
| */ |
| sos_ret_t (*lookup)(struct sos_fs_node *this, |
| const char * name, sos_ui16_t namelen, |
| sos_ui64_t * result_storage_location); |
| |
| /** |
| * Add a new reference in the current sos_fs_node to the on-disk |
| * location of the given sos_fs_node |
| * |
| * @note Responsible for updating this->ondisk_lnk_cnt |
| * @note Mandatory for writable directories, may block. Appropriate |
| * locking MUST be implemented |
| */ |
| sos_ret_t (*link)(struct sos_fs_node *this, |
| const struct sos_process *actor, |
| const char * entry_name, sos_ui16_t entry_namelen, |
| struct sos_fs_node * node); |
| |
| /** |
| * Remove the entry in the current sos_fs_node for the on-disk |
| * location with the given name |
| * |
| * @note Responsible for updating this->ondisk_lnk_cnt |
| * @note Mandatory for writable directories, may block. Appropriate |
| * locking MUST be implemented |
| */ |
| sos_ret_t (*unlink)(struct sos_fs_node *this, |
| const struct sos_process *actor, |
| const char * entry_name, sos_ui16_t entry_namelen); |
| }; |
| |
| |
| /** |
| * The data structure holding information and method related to a |
| * particular usage of a file. A so-called "struct file" |
| * |
| * This represents the kernel structure behind a "file descriptor" or |
| * behind a chdir/chroot. Among other things, it holds the methods |
| * responsible for reading/writing into the file, and for moving the |
| * file pointer (see @sos_fs_opened_file::position) inside it. |
| */ |
| struct sos_fs_opened_file |
| { |
| /** The process that opened the file/dir */ |
| const struct sos_process * owner; |
| |
| /** |
| * The reference to the sos_fs_nscache_node and, hence, to the underlying sos_fs_node. |
| * |
| * Used to cache the in-memory fs nodes |
| */ |
| struct sos_fs_nscache_node * direntry; |
| |
| /** Use for memory-management */ |
| sos_count_t ref_cnt; |
| |
| /** |
| * Always > 0 (ie max size = 2^63-1 = 9.2 10^18). We make it |
| * "signed" here to limit its range. Because we must be able to |
| * seek to the begining of the file with SEEK_END and a negative |
| * offset, so the max size of the file must be reachable by a lseek |
| * offset |
| * |
| * @note reserved to filesystem instance code. Not modified nor used |
| * by fs.c |
| */ |
| sos_lsoffset_t position; |
| |
| /** |
| * Incremented each time this opened file is modified |
| * |
| * Used to implement a readdir method resilient to |
| * creat/mkdir/rmdir/unlink |
| */ |
| sos_lcount_t generation; |
| |
| /** |
| * @see SOS_FS_OPEN_* flags |
| */ |
| sos_ui32_t open_flags; |
| |
| /** Operations common to all node types */ |
| struct sos_fs_ops_opened_file * ops_file; |
| |
| /** Operations specific to some node types */ |
| union |
| { |
| /** when direntry->fs_node->type == SOS_FS_NODE_DIRECTORY */ |
| struct sos_fs_ops_opened_dir * ops_dir; |
| }; /* Anonymous union (gcc extension) */ |
| |
| /** |
| * Called upon fork() to duplicate all the opened files |
| */ |
| sos_ret_t (*duplicate)(struct sos_fs_opened_file *this, |
| const struct sos_process * for_owner, |
| struct sos_fs_opened_file **result); |
| |
| void * custom_data; |
| }; |
| |
| |
| /** |
| * Reference position for sos_fs_seek |
| */ |
| typedef enum { SOS_SEEK_SET=42, |
| SOS_SEEK_CUR=24, |
| SOS_SEEK_END=84 } sos_seek_whence_t; |
| /** |
| * The list of methods implementing the basic VFS opened file |
| * operations |
| * |
| * See the Unix manual pages, they basically form the interfaces to to |
| * these functions |
| * |
| * @see sos_fs_opened_file::ops_file |
| */ |
| struct sos_fs_ops_opened_file |
| { |
| /** |
| * @note Mandatory, may block. Appropriate locking MUST be implemented |
| * @note Please call sos_fs_mark_dirty() if disk contents is changed |
| */ |
| sos_ret_t (*seek)(struct sos_fs_opened_file *this, |
| sos_lsoffset_t offset, |
| sos_seek_whence_t whence, |
| /* out */ sos_lsoffset_t * result_position); |
| |
| /** |
| * @note Mandatory, may block. Appropriate locking MUST be implemented |
| * @note Please call sos_fs_mark_dirty() if disk contents is changed |
| */ |
| sos_ret_t (*read)(struct sos_fs_opened_file *this, |
| sos_uaddr_t dest_buf, |
| sos_size_t * /* in/out */len); |
| |
| /** |
| * @note Optional (might be NULL), may block. Appropriate locking |
| * MUST be implemented |
| * @note Please call sos_fs_mark_dirty() if disk contents is changed |
| */ |
| sos_ret_t (*write)(struct sos_fs_opened_file *this, |
| sos_uaddr_t src_buf, |
| sos_size_t * /* in/out */len); |
| |
| /** |
| * @note Optional (might be NULL), may block. Appropriate locking |
| * MUST be implemented |
| * @note Please call sos_fs_mark_dirty() if disk contents is changed |
| */ |
| sos_ret_t (*mmap)(struct sos_fs_opened_file *this, |
| sos_uaddr_t *uaddr, sos_size_t size, |
| sos_ui32_t access_rights, |
| sos_ui32_t flags, |
| sos_luoffset_t offset); |
| |
| /** |
| * @note Optional (might be NULL), may block. Appropriate locking |
| * MUST be implemented |
| * @note Please call sos_fs_mark_dirty() if disk contents is changed |
| */ |
| sos_ret_t (*fcntl)(struct sos_fs_opened_file *this, |
| int req_id, |
| sos_ui32_t req_arg /* Usually: sos_uaddr_t */); |
| }; |
| |
| |
| /** Data structure that is to be filled by readdir */ |
| struct sos_fs_dirent |
| { |
| sos_ui64_t storage_location; |
| sos_si64_t offset_in_dirfile; |
| sos_ui32_t type; |
| sos_ui16_t namelen; |
| |
| #define SOS_FS_DIRENT_NAME_MAXLEN 128 |
| char name[SOS_FS_DIRENT_NAME_MAXLEN]; |
| }; |
| |
| |
| /** |
| * The list of methods implementing the basic VFS opened directory |
| * operations |
| * |
| * @see sos_fs_opened_file::ops_file |
| */ |
| struct sos_fs_ops_opened_dir |
| { |
| /** |
| * Each time it is called, responsible for filling the sos_fs_dirent |
| * structure, return -SOS_ENOENT when done. |
| * |
| * @note Mandatory, may block. Appropriate locking MUST be implemented |
| * @note Please call sos_fs_mark_dirty() if disk contents is changed |
| */ |
| sos_ret_t (*readdir)(struct sos_fs_opened_file *this, |
| struct sos_fs_dirent * result); |
| }; |
| |
| |
| |
| /** |
| * Used by the stat calls |
| * |
| * @see sos_fs_node_ops_file::stat |
| */ |
| struct sos_fs_stat |
| { |
| sos_fs_node_type_t st_type; |
| sos_ui64_t st_storage_location; |
| sos_ui32_t st_access_rights; |
| sos_count_t st_nlink; |
| sos_si64_t st_size; |
| }; |
| |
| |
| /** |
| * Used by the statvfs calls |
| * |
| * @see sos_fs_manager_instance::statfs |
| */ |
| struct sos_fs_statfs |
| { |
| sos_size_t f_sz_total; /**< Total size */ |
| sos_size_t f_sz_free; /**< Size left on device */ |
| sos_count_t f_node_total;/**< Total allocatable FS nodes */ |
| sos_count_t f_node_avail;/**< Number of available free FS nodes */ |
| sos_ui32_t f_flags; |
| }; |
| |
| |
| /** |
| * Must be called AFTER the FS manager types needed to mount the root |
| * filesystem have been registered |
| */ |
| sos_ret_t sos_fs_subsystem_setup(const char * root_device, |
| const char * fs_type, |
| const char * mount_args, |
| struct sos_fs_manager_instance ** result_rootfs); |
| |
| |
| /* *************************************************************** |
| * The Following functions are relatively standard |
| * |
| * @see Unix manual pages for details |
| */ |
| |
| |
| /** |
| * mount a file system |
| * |
| * @param actor process calling mount |
| * @param _src_path(len) may be NULL (as for virtfs or /proc) |
| * @fsname the name of the filesystem type to mount |
| * @args any args passed to the sos_fs_manager_type::mount method |
| * @result_fs the resulting filesystem instance |
| */ |
| 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 ** /*out*/result_fs); |
| |
| /** |
| * unmount the filesystem at the given location |
| */ |
| sos_ret_t sos_fs_umount(struct sos_process * actor, |
| const char * _mountpoint_path, |
| sos_size_t _mountpoint_pathlen); |
| |
| /** |
| * Flush all the dirty nodes of all the FS to disk |
| */ |
| sos_ret_t sos_fs_sync_all_fs(); |
| |
| /** |
| * Retrieve filesystem status, or return -SOS_ENOSUP if filesystem |
| * cannot report this |
| */ |
| sos_ret_t sos_fs_vfstat(const struct sos_process * actor, |
| const char * _path, |
| sos_size_t _pathlen, |
| struct sos_fs_statfs * result); |
| |
| /** |
| * Open flags |
| */ |
| #define SOS_FS_OPEN_EXCL (1 << 0) |
| #define SOS_FS_OPEN_CREAT (1 << 1) |
| #define SOS_FS_OPEN_NOFOLLOW (1 << 2) |
| #define SOS_FS_OPEN_DIRECTORY (1 << 3) /* Incompatible with CREAT */ |
| #define SOS_FS_OPEN_SYNC (1 << 4) |
| #define SOS_FS_OPEN_KEEPONEXEC (1 << 5) /* By default, files are closed |
| upon an exec() */ |
| |
| #define SOS_FS_OPEN_READ (1 << 16) |
| #define SOS_FS_OPEN_WRITE (1 << 17) |
| |
| |
| /** |
| * FS access rights |
| */ |
| #define SOS_FS_S_IRUSR 00400 |
| #define SOS_FS_S_IWUSR 00200 |
| #define SOS_FS_S_IXUSR 00100 |
| |
| #define SOS_FS_S_IRWXALL 07777 /* For symlinks */ |
| |
| 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 sos_fs_close(struct sos_fs_opened_file * of); |
| |
| sos_ret_t sos_fs_read(struct sos_fs_opened_file * of, |
| sos_uaddr_t dest_buf, |
| sos_size_t * /* in/ou */len); |
| |
| sos_ret_t sos_fs_readdir(struct sos_fs_opened_file * of, |
| struct sos_fs_dirent * result); |
| |
| sos_ret_t sos_fs_write(struct sos_fs_opened_file * of, |
| sos_uaddr_t src_buf, |
| sos_size_t * /* in/out */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); |
| |
| sos_ret_t sos_fs_ftruncate(struct sos_fs_opened_file *of, |
| sos_lsoffset_t length); |
| |
| 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_ret_t sos_fs_fsync(struct sos_fs_opened_file * of); |
| |
| sos_ret_t sos_fs_fcntl(struct sos_fs_opened_file *of, |
| int req_id, |
| sos_ui32_t req_arg /* Usually: sos_uaddr_t */); |
| |
| sos_ret_t sos_fs_creat(const struct sos_process * creator, |
| const char * _path, |
| sos_size_t _pathlen, |
| sos_ui32_t access_rights); |
| |
| sos_ret_t sos_fs_link(const struct sos_process * creator, |
| const char * _old_path, |
| sos_size_t _old_pathlen, |
| const char * _dest_path, |
| sos_size_t _dest_pathlen); |
| |
| sos_ret_t sos_fs_rename(const struct sos_process * creator, |
| const char * _old_path, |
| sos_size_t _old_pathlen, |
| const char * _dest_path, |
| sos_size_t _dest_pathlen); |
| |
| sos_ret_t sos_fs_unlink(const struct sos_process * actor, |
| const char * _path, |
| sos_size_t _pathlen); |
| |
| 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 sos_fs_mkdir(const struct sos_process * creator, |
| const char * _path, |
| sos_size_t _pathlen, |
| sos_ui32_t access_rights); |
| |
| sos_ret_t sos_fs_rmdir(const struct sos_process * actor, |
| const char * _path, |
| sos_size_t _pathlen); |
| |
| 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 sos_fs_stat(const struct sos_process * actor, |
| const char * _path, |
| sos_size_t _pathlen, |
| int nofollow, |
| struct sos_fs_stat * result); |
| |
| |
| /* *************************************************************** |
| * Restricted functions reserved to FS code and block/char devices |
| */ |
| |
| /** |
| * Function to be called when proposing a new File system type |
| */ |
| sos_ret_t sos_fs_register_fs_type(struct sos_fs_manager_type * fstype); |
| sos_ret_t sos_fs_unregister_fs_type(struct sos_fs_manager_type * fstype); |
| |
| /** |
| * Marthe given file as dirty, for FS supporting deferred write access |
| * mode |
| */ |
| sos_ret_t sos_fs_mark_dirty(struct sos_fs_opened_file * of); |
| |
| /** |
| * Helper function to be called from the mount() method of the FS |
| * instance code. Responsible for creating and updating the "root" |
| * field of the FS instance structure and for connecting this FS in |
| * the nscache |
| * @param root_fsnode The root of the FS being mounted |
| */ |
| sos_ret_t sos_fs_register_fs_instance(struct sos_fs_manager_instance * fs, |
| struct sos_fs_node * root_fsnode); |
| |
| /** |
| * Helper function to be called from the umount() method of the FS |
| * instance code. Responsible for unregistering the instance from the |
| * FS type's instances list and for disconnecting this mountpoint in |
| * the nscache. |
| */ |
| sos_ret_t sos_fs_unregister_fs_instance(struct sos_fs_manager_instance * fs); |
| |
| |
| /* *************************************************************** |
| * Restricted functions reserved to syscall.c |
| */ |
| sos_ret_t sos_fs_ref_opened_file(struct sos_fs_opened_file * of); |
| sos_ret_t _sos_fs_unref_opened_file(struct sos_fs_opened_file ** of); |
| #define sos_fs_unref_opened_file(f) _sos_fs_unref_opened_file(&(f)) |
| |
| |
| /* *************************************************************** |
| * Restricted functions to be used only by fs_nscache.c |
| */ |
| |
| sos_ret_t sos_fs_ref_fsnode(struct sos_fs_node * fsnode); |
| |
| sos_ret_t _sos_fs_unref_fsnode(struct sos_fs_node * fsnode); |
| #define sos_fs_unref_fsnode(n) \ |
| ({ sos_ret_t __retval = _sos_fs_unref_fsnode(n); (n)=NULL; __retval; }) |
| |
| |
| /* *************************************************************** |
| * Restricted functions reserved to process.c and main.c:start_init() |
| */ |
| sos_ret_t sos_fs_new_opened_file(const struct sos_process * proc, |
| struct sos_fs_nscache_node * nsnode, |
| sos_ui32_t open_flags, |
| struct sos_fs_opened_file ** result_of); |
| |
| |
| 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); |
| |
| |
| |
| #endif /* _SOS_FS_H_ */ |
| |
/tmp/sos-code-article7.5/sos/fs_nscache.c (1970-01-01 01:00:00.000000000 +0100
) |
|
../sos-code-article8/sos/fs_nscache.c (2005-07-01 16:39:49.000000000 +0200
) |
|
|
|
| /* 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_nscache.h" |
| |
| |
| /** |
| * A so-called "dentry" / "nsnode" structure. Used to make the |
| * "in-memory" representation of the file system files/dir/devices |
| * that have been used up to now |
| */ |
| struct sos_fs_nscache_node |
| { |
| /** The reference to the associated sos_fs_node */ |
| struct sos_fs_node *fs_node; |
| |
| struct sos_fs_pathname name; |
| |
| /** Number of references to that node, reference from parent (if |
| any) is EXCLUDED */ |
| sos_count_t ref_cnt; |
| |
| /* |
| * Ued to chain the mounted filesystem |
| */ |
| |
| /** If this node is a mountpoint (a file system is mounted on it): |
| reference to the filesystem mounted on it */ |
| struct sos_fs_nscache_node *mounted_root; |
| /** If this node is the root of a mounted filesystem: reference to |
| the mountpoint where it is mounted on */ |
| struct sos_fs_nscache_node *mountpoint; |
| |
| /** ".." */ |
| struct sos_fs_nscache_node *parent; |
| |
| /** List of the already known children */ |
| struct sos_fs_nscache_node *children; |
| |
| /** Other children for the same parent */ |
| struct sos_fs_nscache_node *siblings_prev, *siblings_next; |
| }; |
| |
| |
| /** The cache of nscache_node objects */ |
| static struct sos_kslab_cache * cache_of_nscache_nodes; |
| |
| |
| sos_ret_t sos_fs_nscache_subsystem_setup() |
| { |
| cache_of_nscache_nodes |
| = sos_kmem_cache_create("fs_nscache", |
| sizeof(struct sos_fs_nscache_node), |
| 3, 0, |
| SOS_KSLAB_CREATE_MAP | SOS_KSLAB_CREATE_ZERO); |
| if (! cache_of_nscache_nodes) |
| return -SOS_ENOMEM; |
| |
| return SOS_OK; |
| }; |
| |
| |
| sos_bool_t |
| sos_fs_pathname_eat_slashes(const struct sos_fs_pathname * path, |
| struct sos_fs_pathname * result) |
| { |
| sos_bool_t retval = FALSE; |
| |
| result->contents = path->contents; |
| result->length = path->length; |
| while (result->length > 0) |
| { |
| if (*result->contents != '/') |
| break; |
| |
| result->contents ++; |
| result->length --; |
| retval = TRUE; |
| } |
| |
| if(result->length <= 0) |
| result->contents = NULL; |
| |
| return retval; |
| } |
| |
| |
| static sos_bool_t |
| sos_fs_pathname_eat_non_slashes(const struct sos_fs_pathname * path, |
| struct sos_fs_pathname * result) |
| { |
| sos_bool_t retval = FALSE; |
| |
| result->contents = path->contents; |
| result->length = path->length; |
| while (result->length > 0) |
| { |
| if (*result->contents == '/') |
| break; |
| |
| result->contents ++; |
| result->length --; |
| retval = TRUE; |
| } |
| |
| if(result->length <= 0) |
| result->contents = NULL; |
| |
| return retval; |
| } |
| |
| |
| sos_bool_t |
| sos_fs_pathname_split_path(const struct sos_fs_pathname * path, |
| struct sos_fs_pathname * result_first_component, |
| struct sos_fs_pathname * result_remaining_path) |
| { |
| result_first_component->contents = path->contents; |
| result_first_component->length = path->length; |
| |
| /* Skip any leading slash */ |
| sos_fs_pathname_eat_slashes(result_first_component, |
| result_first_component); |
| |
| /* Extract the first component */ |
| sos_fs_pathname_eat_non_slashes(result_first_component, |
| result_remaining_path); |
| SOS_ASSERT_FATAL(result_remaining_path->length >= 0); |
| result_first_component->length -= result_remaining_path->length; |
| |
| /* Return true if there is something left (at least one slash) */ |
| return (result_remaining_path->length > 0); |
| } |
| |
| |
| sos_bool_t fs_pathname_iseq(const struct sos_fs_pathname * p1, |
| const struct sos_fs_pathname * p2) |
| { |
| if (!p1->contents) |
| SOS_ASSERT_FATAL(p1->length == 0); |
| if (!p2->contents) |
| SOS_ASSERT_FATAL(p2->length == 0); |
| |
| if (p1->length != p2->length) |
| return FALSE; |
| |
| if (p1->length == 0) |
| return TRUE; |
| |
| return (0 == memcmp(p1->contents, p2->contents, p1->length)); |
| } |
| |
| |
| #define fs_pathname_isstr(str,path) \ |
| ({ struct sos_fs_pathname _s; _s.contents = str; _s.length = sizeof(str)-1; \ |
| fs_pathname_iseq(&_s, (path)); }) |
| |
| |
| struct sos_fs_node * |
| sos_fs_nscache_get_fs_node(const struct sos_fs_nscache_node * nsnode) |
| { |
| return nsnode->fs_node; |
| } |
| |
| |
| sos_ret_t |
| sos_fs_nscache_get_parent(const struct sos_fs_nscache_node * nsnode, |
| struct sos_fs_nscache_node ** result_parent) |
| { |
| *result_parent = nsnode->parent; |
| if (*result_parent) |
| return SOS_OK; |
| return -SOS_ENOENT; |
| } |
| |
| |
| sos_ret_t |
| sos_fs_nscache_get_name(const struct sos_fs_nscache_node * nsnode, |
| struct sos_fs_pathname * result_pathname) |
| { |
| result_pathname->contents = nsnode->name.contents; |
| result_pathname->length = nsnode->name.length; |
| return SOS_OK; |
| } |
| |
| |
| sos_ret_t |
| sos_fs_nscache_get_ref_cnt(const struct sos_fs_nscache_node * nsnode) |
| { |
| return nsnode->ref_cnt; |
| } |
| |
| |
| sos_ret_t |
| sos_fs_nscache_lookup(struct sos_fs_nscache_node * cur_nsnode, |
| const struct sos_fs_pathname * node_name, |
| const struct sos_fs_nscache_node * root_nsnode, |
| struct sos_fs_nscache_node ** result_nsnode) |
| { |
| if (fs_pathname_isstr(".", node_name)) |
| { |
| *result_nsnode = cur_nsnode; |
| } |
| else if (fs_pathname_isstr("..", node_name)) |
| { |
| /* Effectively go up only if we did not reach a root node */ |
| if (cur_nsnode == root_nsnode) /* did reach chroot */ |
| { |
| /* Simply stay here */ |
| *result_nsnode = cur_nsnode; |
| } |
| else |
| { |
| /* If current node is a mounted FS, rewind the mountpoint |
| chain */ |
| for ( ; cur_nsnode->mountpoint ; cur_nsnode = cur_nsnode->mountpoint) |
| /* nop */ ; |
| |
| /* Now go up to parent */ |
| SOS_ASSERT_FATAL(NULL != cur_nsnode->parent); |
| *result_nsnode = cur_nsnode->parent; |
| } |
| |
| /* Update the nscache_node result */ |
| sos_fs_nscache_ref_node(*result_nsnode); |
| return SOS_OK; |
| } |
| else |
| { |
| /* Normal lookup: we iterate over the list of children nscache |
| nodes */ |
| int nb_children; |
| struct sos_fs_nscache_node * child; |
| |
| /* Lookup the child node with the correct name, if any */ |
| list_foreach_named(cur_nsnode->children, |
| child, nb_children, |
| siblings_prev, siblings_next) |
| { |
| struct sos_fs_node * fs_node = cur_nsnode->fs_node; |
| |
| if (fs_node->fs->nsnode_same_name) |
| { |
| if (fs_node->fs-> |
| nsnode_same_name(child->name.contents, |
| child->name.length, |
| node_name->contents, |
| node_name->length)) |
| break; |
| } |
| else |
| if (fs_pathname_iseq(& child->name, |
| node_name)) |
| break; |
| } |
| |
| /* Did not find it ! */ |
| if (! list_foreach_early_break(cur_nsnode->children, |
| child, nb_children)) |
| return -SOS_ENOENT; |
| |
| /* Yes, found it ! */ |
| *result_nsnode = child; |
| } |
| |
| /* Found it. Now, Follow the mountpoint chain, if any */ |
| for ( ; (*result_nsnode)->mounted_root ; |
| *result_nsnode = (*result_nsnode)->mounted_root) |
| /* nop */ ; |
| |
| /* Update the nscache_node result */ |
| sos_fs_nscache_ref_node(*result_nsnode); |
| |
| return SOS_OK; |
| } |
| |
| |
| sos_ret_t sos_fs_nscache_ref_node(struct sos_fs_nscache_node * nsnode) |
| { |
| SOS_ASSERT_FATAL(nsnode->ref_cnt > 0); |
| nsnode->ref_cnt ++; |
| return SOS_OK; |
| } |
| |
| |
| /* Eventually collapses a whole list of nsnodes (non recursive) */ |
| sos_ret_t _sos_fs_nscache_unref_node(struct sos_fs_nscache_node ** nsnode) |
| { |
| struct sos_fs_nscache_node * to_delete = NULL, *node; |
| |
| node = *nsnode; |
| *nsnode = NULL; |
| |
| while (node) |
| { |
| /* Unreference this node */ |
| SOS_ASSERT_FATAL(node->ref_cnt > 0); |
| node->ref_cnt --; |
| |
| /* Is it a good candidate for deletion ? */ |
| if (node->ref_cnt > 0) |
| break; /* No */ |
| |
| if (node->parent) |
| { |
| struct sos_fs_nscache_node * parent = node->parent; |
| |
| SOS_ASSERT_FATAL(node->parent->ref_cnt >= 1); |
| |
| list_delete_named(parent->children, node, |
| siblings_prev, siblings_next); |
| /* The parent lost one child: next iteration will decrement |
| ths parent's ref cnt */ |
| } |
| |
| /* Add to the list of elements to suppress */ |
| list_add_tail_named(to_delete, node, siblings_prev, siblings_next); |
| |
| /* Now look if parent (if any) can be destroyed */ |
| node = node->parent; |
| } |
| |
| /* Now destroy all the elements gathered */ |
| while (! list_is_empty_named(to_delete, siblings_prev, siblings_next)) |
| { |
| node = list_pop_head_named(to_delete, siblings_prev, siblings_next); |
| sos_fs_unref_fsnode(node->fs_node); |
| sos_kfree((sos_vaddr_t)node); |
| } |
| |
| return SOS_OK; |
| } |
| |
| |
| sos_ret_t |
| sos_fs_nscache_add_new_child_node(struct sos_fs_nscache_node * parent, |
| const struct sos_fs_pathname * node_name, |
| struct sos_fs_node * fsnode, |
| struct sos_fs_nscache_node ** result_nsnode) |
| { |
| struct sos_fs_nscache_node * nsnode; |
| |
| /* Allocate a new nscache node from slab */ |
| nsnode = (struct sos_fs_nscache_node*) |
| sos_kmem_cache_alloc(cache_of_nscache_nodes, |
| SOS_KSLAB_ALLOC_ATOMIC); |
| if (! nsnode) |
| return -SOS_ENOMEM; |
| |
| /* Allocate a new memory chunk to hold the node's name */ |
| if (node_name && (node_name->length > 0)) |
| { |
| char * contents = (char*) sos_kmalloc(node_name->length, |
| SOS_KMALLOC_ATOMIC); |
| if (! contents) |
| { |
| sos_kfree((sos_vaddr_t)nsnode); |
| return -SOS_ENOMEM; |
| } |
| |
| memcpy(contents, node_name->contents, node_name->length); |
| nsnode->name.contents = contents; |
| nsnode->name.length = node_name->length; |
| } |
| |
| /* Now initialize the new node's fields */ |
| nsnode->ref_cnt = 1; |
| sos_fs_ref_fsnode(fsnode); |
| nsnode->fs_node = fsnode; |
| |
| /* Register this node as a child of its parent, if any */ |
| nsnode->parent = parent; |
| if (parent) |
| { |
| sos_fs_nscache_ref_node(parent); |
| list_add_head_named(parent->children, nsnode, |
| siblings_prev, siblings_next); |
| } |
| |
| *result_nsnode = nsnode; |
| return SOS_OK; |
| } |
| |
| |
| sos_ret_t |
| sos_fs_nscache_add_existing_child_node(struct sos_fs_nscache_node * parent, |
| const struct sos_fs_pathname * node_name, |
| struct sos_fs_nscache_node * nsnode) |
| { |
| SOS_ASSERT_FATAL(nsnode->parent == NULL); |
| |
| /* If the node already had a name, suppress it */ |
| if (NULL != nsnode->name.contents) |
| { |
| sos_kfree((sos_vaddr_t)nsnode->name.contents); |
| } |
| memset(& nsnode->name, 0x0, sizeof(struct sos_fs_pathname)); |
| |
| /* Allocate a new memory chunk to hold the node's name */ |
| if (node_name && (node_name->length > 0)) |
| { |
| char * contents = (char*) sos_kmalloc(node_name->length, |
| SOS_KMALLOC_ATOMIC); |
| if (! contents) |
| { |
| sos_kfree((sos_vaddr_t)nsnode); |
| return -SOS_ENOMEM; |
| } |
| |
| memcpy(contents, node_name->contents, node_name->length); |
| nsnode->name.contents = contents; |
| nsnode->name.length = node_name->length; |
| } |
| |
| |
| /* Register this node as a child of its parent, if any */ |
| nsnode->parent = parent; |
| if (parent) |
| { |
| sos_fs_nscache_ref_node(parent); |
| list_add_head_named(parent->children, nsnode, |
| siblings_prev, siblings_next); |
| } |
| return SOS_OK; |
| } |
| |
| |
| sos_ret_t |
| sos_fs_nscache_disconnect_node(struct sos_fs_nscache_node * nsnode) |
| { |
| if (! nsnode->parent) |
| return SOS_OK; |
| |
| list_delete_named(nsnode->parent->children, nsnode, |
| siblings_prev, siblings_next); |
| sos_fs_nscache_unref_node(nsnode->parent); |
| nsnode->parent = NULL; |
| |
| return SOS_OK; |
| } |
| |
| |
| sos_ret_t |
| sos_fs_nscache_register_opened_file(struct sos_fs_nscache_node * nsnode, |
| struct sos_fs_opened_file * of) |
| { |
| of->direntry = nsnode; |
| sos_fs_nscache_ref_node(nsnode); |
| return SOS_OK; |
| } |
| |
| |
| sos_ret_t |
| sos_fs_nscache_mount(struct sos_fs_nscache_node * mountpoint, |
| struct sos_fs_nscache_node * mounted_root) |
| { |
| SOS_ASSERT_FATAL(NULL == mountpoint->mounted_root); |
| SOS_ASSERT_FATAL(NULL == mounted_root->mountpoint); |
| mountpoint->mounted_root = mounted_root; |
| mounted_root->mountpoint = mountpoint; |
| sos_fs_nscache_ref_node(mountpoint); |
| sos_fs_nscache_ref_node(mounted_root); |
| |
| return SOS_OK; |
| } |
| |
| |
| sos_ret_t |
| sos_fs_nscache_umount(struct sos_fs_nscache_node * mounted_root) |
| { |
| struct sos_fs_manager_instance *fs; |
| |
| SOS_ASSERT_FATAL(NULL != mounted_root->mountpoint); |
| SOS_ASSERT_FATAL(mounted_root->mountpoint->mounted_root == mounted_root); |
| |
| /* No FS should be mounted on the mounted root to umount */ |
| SOS_ASSERT_FATAL(NULL == mounted_root->mounted_root); |
| |
| /* The mounted root should have its own reference, plus a reference |
| from the mountpoint and from the fs instance */ |
| SOS_ASSERT_FATAL(mounted_root->ref_cnt >= 3); |
| if (mounted_root->ref_cnt != 3) |
| return -SOS_EBUSY; |
| |
| fs = mounted_root->fs_node->fs; |
| SOS_ASSERT_FATAL(NULL != fs); |
| SOS_ASSERT_FATAL(fs->root == mounted_root); |
| |
| /* Undo the mountpoint <-> mounted_root mutual reference */ |
| mounted_root->mountpoint->mounted_root = NULL; |
| sos_fs_nscache_unref_node(mounted_root->mountpoint); |
| mounted_root->mountpoint = NULL; |
| sos_fs_nscache_unref_node(mounted_root); |
| |
| /* Undo the fs->root -> mounted_root reference */ |
| sos_fs_nscache_unref_node(fs->root); |
| |
| return SOS_OK; |
| } |
| |
| sos_bool_t |
| sos_fs_nscache_is_mountnode(const struct sos_fs_nscache_node * nsnode) |
| { |
| return ( (NULL != nsnode->mounted_root) || (NULL != nsnode->mountpoint) ); |
| } |
| |
/tmp/sos-code-article7.5/sos/syscall.c (2005-04-27 20:17:18.000000000 +0200
) |
|
../sos-code-article8/sos/syscall.c (2005-07-01 16:39:49.000000000 +0200
) |
|
|
|
case SOS_SYSCALL_ID_EXIT: | case SOS_SYSCALL_ID_EXIT: |
{ | { |
unsigned int status; | unsigned int status; |
| |
retval = sos_syscall_get1arg(user_ctxt, & status); | retval = sos_syscall_get1arg(user_ctxt, & status); |
if (SOS_OK != retval) | if (SOS_OK != retval) |
break; | break; |
|
|
case SOS_SYSCALL_ID_EXEC: | case SOS_SYSCALL_ID_EXEC: |
{ | { |
struct sos_thread *cur_thr, *new_thr; | struct sos_thread *cur_thr, *new_thr; |
| struct sos_process * proc; |
struct sos_umem_vmm_as *new_as; | struct sos_umem_vmm_as *new_as; |
sos_uaddr_t user_str, ustack, start_uaddr; | sos_uaddr_t user_str, ustack, start_uaddr; |
sos_size_t len; | sos_size_t len; |
char * str; | char * str; |
| |
cur_thr = sos_thread_get_current(); | cur_thr = sos_thread_get_current(); |
| proc = cur_thr->process; |
| |
/* Make sure the process has exactly 1 thread in it */ | /* Make sure the process has exactly 1 thread in it */ |
if (sos_process_get_nb_threads(cur_thr->process) != 1) | if (sos_process_get_nb_threads(proc) != 1) |
retval = -SOS_EBUSY; | retval = -SOS_EBUSY; |
break; | break; |
|
|
break; | break; |
| |
/* Copy the program name into kernel sppace */ | /* Copy the program name into kernel sppace */ |
str = (char*)sos_kmalloc(len + 1, 0); | retval = sos_strndup_from_user(& str, user_str, len + 1, 0); |
if (! str) | if (SOS_OK != retval) |
{ | |
retval = -SOS_ENOMEM; | |
break; | |
} | |
retval = sos_strzcpy_from_user(str, user_str, len + 1); | |
if (retval < SOS_OK) | |
sos_kfree((sos_vaddr_t)str); | |
} | } |
| |
/* Create a new empty address space to map the program */ | /* Create a new empty address space to map the program */ |
new_as = sos_umem_vmm_create_empty_as(cur_thr->process); | new_as = sos_umem_vmm_create_empty_as(proc); |
{ | { |
sos_kfree((sos_vaddr_t)str); | sos_kfree((sos_vaddr_t)str); |
|
|
| |
/* Now create the user thread */ | /* Now create the user thread */ |
new_thr = sos_create_user_thread(NULL, | new_thr = sos_create_user_thread(NULL, |
cur_thr->process, | proc, |
0, 0, | 0, 0, |
ustack + SOS_DEFAULT_USER_STACK_SIZE | ustack + SOS_DEFAULT_USER_STACK_SIZE |
|
|
break; | break; |
} | } |
| |
sos_process_set_name(cur_thr->process, str); | sos_process_set_name(proc, str); |
/* Switch to this address space */ | /* Switch to this address space */ |
retval = sos_process_set_address_space(cur_thr->process, | retval = sos_process_set_address_space(proc, |
if (SOS_OK != retval) | if (SOS_OK != retval) |
{ | { |
|
|
} | } |
break; | break; |
| |
case SOS_SYSCALL_ID_MMAP: | case SOS_SYSCALL_ID_FAKEMMAP: |
sos_uaddr_t ptr_hint_uaddr; | sos_uaddr_t ptr_hint_uaddr; |
sos_uaddr_t hint_uaddr; | sos_uaddr_t hint_uaddr; |
|
|
retval = -SOS_ENOMEM; | retval = -SOS_ENOMEM; |
break; | break; |
} | } |
| |
} | } |
break; | break; |
| |
|
|
} | } |
break; | break; |
| |
| |
| /** |
| * File system interface |
| */ |
| case SOS_SYSCALL_ID_MOUNT: |
| { |
| sos_uaddr_t user_src; |
| sos_size_t srclen; |
| const char * kernel_src = NULL; |
| sos_uaddr_t user_dst; |
| sos_size_t dstlen; |
| const char * kernel_dst; |
| sos_ui32_t mountflags; |
| sos_uaddr_t user_fstype; |
| char * kernel_fstype; |
| sos_uaddr_t user_args; |
| char * kernel_args = NULL; |
| struct sos_process * proc; |
| |
| proc = sos_thread_get_current()->process; |
| retval = sos_syscall_get7args(user_ctxt, |
| (unsigned int*)& user_src, |
| (unsigned int*)& srclen, |
| (unsigned int*)& user_dst, |
| (unsigned int*)& dstlen, |
| (unsigned int*)& user_fstype, |
| (unsigned int*)& mountflags, |
| (unsigned int*)& user_args); |
| if (SOS_OK != retval) |
| break; |
| |
| if (user_src != (sos_uaddr_t)NULL) |
| { |
| retval = sos_memdup_from_user((sos_vaddr_t*) &kernel_src, user_src, srclen, 0); |
| if (SOS_OK != retval) |
| break; |
| } |
| |
| retval = sos_memdup_from_user((sos_vaddr_t*) &kernel_dst, user_dst, dstlen, 0); |
| if (SOS_OK != retval) |
| { |
| if (kernel_src) |
| sos_kfree((sos_vaddr_t)kernel_src); |
| break; |
| } |
| |
| retval = sos_strndup_from_user(& kernel_fstype, user_fstype, 256, 0); |
| if (SOS_OK != retval) |
| { |
| if (kernel_src) |
| sos_kfree((sos_vaddr_t)kernel_src); |
| sos_kfree((sos_vaddr_t)kernel_dst); |
| break; |
| } |
| |
| if (user_args != (sos_uaddr_t)NULL) |
| { |
| retval = sos_strndup_from_user(& kernel_args, user_args, 1024, 0); |
| if (SOS_OK != retval) |
| { |
| if (kernel_src) |
| sos_kfree((sos_vaddr_t)kernel_src); |
| sos_kfree((sos_vaddr_t)kernel_dst); |
| sos_kfree((sos_vaddr_t)kernel_fstype); |
| break; |
| } |
| } |
| |
| retval = sos_fs_mount(proc, kernel_src, srclen, |
| kernel_dst, dstlen, |
| kernel_fstype, |
| mountflags, |
| kernel_args, |
| NULL); |
| if (kernel_src) |
| sos_kfree((sos_vaddr_t)kernel_src); |
| sos_kfree((sos_vaddr_t)kernel_dst); |
| sos_kfree((sos_vaddr_t)kernel_fstype); |
| if (kernel_args) |
| sos_kfree((sos_vaddr_t)kernel_args); |
| } |
| break; |
| |
| case SOS_SYSCALL_ID_UMOUNT: |
| { |
| sos_uaddr_t user_str; |
| sos_size_t len; |
| char * path; |
| struct sos_process * proc; |
| |
| proc = sos_thread_get_current()->process; |
| retval = sos_syscall_get2args(user_ctxt, |
| (unsigned int*)& user_str, |
| (unsigned int*)& len); |
| if (SOS_OK != retval) |
| break; |
| |
| retval = sos_memdup_from_user((sos_vaddr_t*) &path, user_str, len, 0); |
| if (SOS_OK != retval) |
| break; |
| |
| retval = sos_fs_umount(proc, |
| path, len); |
| sos_kfree((sos_vaddr_t)path); |
| } |
| break; |
| |
| case SOS_SYSCALL_ID_SYNC: |
| { |
| sos_fs_sync_all_fs(); |
| retval = SOS_OK; |
| } |
| break; |
| |
| case SOS_SYSCALL_ID_VFSTAT64: |
| { |
| sos_uaddr_t user_str; |
| sos_size_t len; |
| sos_uaddr_t user_vfstat_struct; |
| struct sos_fs_statfs kernel_vfstat_struct; |
| char * path; |
| struct sos_process * proc; |
| |
| proc = sos_thread_get_current()->process; |
| retval = sos_syscall_get3args(user_ctxt, |
| (unsigned int*)& user_str, |
| (unsigned int*)& len, |
| (unsigned int*)& user_vfstat_struct); |
| if (SOS_OK != retval) |
| break; |
| |
| retval = sos_memdup_from_user((sos_vaddr_t*) &path, user_str, len, 0); |
| if (SOS_OK != retval) |
| break; |
| |
| retval = sos_fs_vfstat(proc, path, len, & kernel_vfstat_struct); |
| sos_kfree((sos_vaddr_t)path); |
| if (SOS_OK != retval) |
| break; |
| |
| if (sizeof(kernel_vfstat_struct) |
| != sos_memcpy_to_user(user_vfstat_struct, |
| (sos_vaddr_t) & kernel_vfstat_struct, |
| sizeof(kernel_vfstat_struct))) |
| retval = -SOS_EFAULT; |
| } |
| break; |
| |
| case SOS_SYSCALL_ID_OPEN: |
| { |
| sos_uaddr_t user_str; |
| sos_size_t len; |
| sos_ui32_t open_flags; |
| sos_ui32_t access_rights; |
| char * path; |
| struct sos_fs_opened_file * of; |
| struct sos_process * proc; |
| |
| proc = sos_thread_get_current()->process; |
| retval = sos_syscall_get4args(user_ctxt, |
| (unsigned int*)& user_str, |
| (unsigned int*)& len, |
| (unsigned int*)& open_flags, |
| (unsigned int*)& access_rights); |
| if (SOS_OK != retval) |
| break; |
| |
| retval = sos_memdup_from_user((sos_vaddr_t*) &path, user_str, len, 0); |
| if (SOS_OK != retval) |
| break; |
| |
| retval = sos_fs_open(proc, |
| path, len, |
| open_flags, |
| access_rights, |
| & of); |
| sos_kfree((sos_vaddr_t)path); |
| if (SOS_OK != retval) |
| break; |
| |
| retval = sos_process_register_opened_file(proc, of); |
| if (retval < 0) |
| { |
| sos_fs_close(of); |
| break; |
| } |
| } |
| break; |
| |
| case SOS_SYSCALL_ID_CLOSE: |
| { |
| struct sos_fs_opened_file * of; |
| struct sos_process * proc; |
| int fd; |
| |
| proc = sos_thread_get_current()->process; |
| retval = sos_syscall_get1arg(user_ctxt, |
| (unsigned int*)& fd); |
| if (SOS_OK != retval) |
| break; |
| |
| of = sos_process_get_opened_file(proc, fd); |
| if (NULL == of) |
| { |
| retval = -SOS_EBADF; |
| break; |
| } |
| |
| retval = sos_process_unregister_opened_file(proc, fd); |
| if (SOS_OK != retval) |
| break; |
| |
| retval = sos_fs_close(of); |
| } |
| break; |
| |
| case SOS_SYSCALL_ID_READ: |
| { |
| struct sos_fs_opened_file * of; |
| struct sos_process * proc; |
| sos_uaddr_t uaddr_buf; |
| sos_uaddr_t uaddr_buflen; |
| sos_size_t kernel_buflen; |
| int fd; |
| |
| proc = sos_thread_get_current()->process; |
| retval = sos_syscall_get3args(user_ctxt, |
| (unsigned int*)& fd, |
| (unsigned int*)& uaddr_buf, |
| (unsigned int*)& uaddr_buflen); |
| if (SOS_OK != retval) |
| break; |
| |
| /* Retrieve the value for "buflen" */ |
| retval = sos_memcpy_from_user((sos_vaddr_t)& kernel_buflen, |
| uaddr_buflen, |
| sizeof(kernel_buflen)); |
| if (sizeof(kernel_buflen) != retval) |
| { |
| retval = -SOS_EFAULT; |
| break; |
| } |
| |
| of = sos_process_get_opened_file(proc, fd); |
| if (NULL == of) |
| { |
| retval = -SOS_EBADF; |
| break; |
| } |
| |
| /* Do the actual reading */ |
| retval = sos_fs_read(of, uaddr_buf, & kernel_buflen); |
| |
| /* Send successful number of bytes read to user */ |
| sos_memcpy_to_user(uaddr_buflen, |
| (sos_vaddr_t)& kernel_buflen, |
| sizeof(kernel_buflen)); |
| } |
| break; |
| |
| case SOS_SYSCALL_ID_READDIR: |
| { |
| struct sos_fs_opened_file * of; |
| struct sos_process * proc; |
| sos_uaddr_t uaddr_direntry; |
| struct sos_fs_dirent direntry; |
| int fd; |
| |
| proc = sos_thread_get_current()->process; |
| retval = sos_syscall_get2args(user_ctxt, |
| (unsigned int*)& fd, |
| (unsigned int*)& uaddr_direntry); |
| if (SOS_OK != retval) |
| break; |
| |
| of = sos_process_get_opened_file(proc, fd); |
| if (NULL == of) |
| { |
| retval = -SOS_EBADF; |
| break; |
| } |
| |
| /* Do the actual readdir */ |
| retval = sos_fs_readdir(of, & direntry); |
| if (SOS_OK != retval) |
| break; |
| |
| /* Send direntry structure to user */ |
| if (sizeof(direntry) != sos_memcpy_to_user(uaddr_direntry, |
| (sos_vaddr_t)& direntry, |
| sizeof(direntry))) |
| retval = -SOS_EFAULT; |
| } |
| break; |
| |
| case SOS_SYSCALL_ID_WRITE: |
| { |
| struct sos_fs_opened_file * of; |
| struct sos_process * proc; |
| sos_uaddr_t uaddr_buf; |
| sos_uaddr_t uaddr_buflen; |
| sos_size_t kernel_buflen; |
| int fd; |
| |
| proc = sos_thread_get_current()->process; |
| retval = sos_syscall_get3args(user_ctxt, |
| (unsigned int*)& fd, |
| (unsigned int*)& uaddr_buf, |
| (unsigned int*)& uaddr_buflen); |
| if (SOS_OK != retval) |
| break; |
| |
| /* Retrieve the value for "buflen" */ |
| retval = sos_memcpy_from_user((sos_vaddr_t)& kernel_buflen, |
| uaddr_buflen, |
| sizeof(kernel_buflen)); |
| if (sizeof(kernel_buflen) != retval) |
| { |
| retval = -SOS_EFAULT; |
| break; |
| } |
| |
| of = sos_process_get_opened_file(proc, fd); |
| if (NULL == of) |
| { |
| retval = -SOS_EBADF; |
| break; |
| } |
| |
| /* Do the actual writing */ |
| retval = sos_fs_write(of, uaddr_buf, & kernel_buflen); |
| |
| /* Send successful number of bytes written to user */ |
| sos_memcpy_to_user(uaddr_buflen, |
| (sos_vaddr_t)& kernel_buflen, |
| sizeof(kernel_buflen)); |
| } |
| break; |
| |
| case SOS_SYSCALL_ID_SEEK64: |
| { |
| struct sos_fs_opened_file * of; |
| struct sos_process * proc; |
| sos_uaddr_t uaddr_offset; |
| sos_seek_whence_t whence; |
| sos_lsoffset_t kernel_offset, result_position; |
| int fd; |
| |
| proc = sos_thread_get_current()->process; |
| retval = sos_syscall_get3args(user_ctxt, |
| (unsigned int*)& fd, |
| (unsigned int*)& uaddr_offset, |
| (unsigned int*)& whence); |
| if (SOS_OK != retval) |
| break; |
| |
| /* Retrieve the value for "buflen" */ |
| retval = sos_memcpy_from_user((sos_vaddr_t)& kernel_offset, |
| uaddr_offset, |
| sizeof(kernel_offset)); |
| if (sizeof(kernel_offset) != retval) |
| { |
| retval = -SOS_EFAULT; |
| break; |
| } |
| |
| of = sos_process_get_opened_file(proc, fd); |
| if (NULL == of) |
| { |
| retval = -SOS_EBADF; |
| break; |
| } |
| |
| /* Do the actual seek */ |
| retval = sos_fs_seek(of, kernel_offset, whence, & result_position); |
| |
| /* Send successful number of bytes written to user */ |
| sos_memcpy_to_user(uaddr_offset, |
| (sos_vaddr_t)& result_position, |
| sizeof(kernel_offset)); |
| } |
| break; |
| |
| case SOS_SYSCALL_ID_FTRUNCATE64: |
| { |
| struct sos_fs_opened_file * of; |
| struct sos_process * proc; |
| sos_lsoffset_t length; |
| int fd; |
| |
| proc = sos_thread_get_current()->process; |
| retval = sos_syscall_get2args(user_ctxt, |
| (unsigned int*)& fd, |
| (unsigned int*)& length); |
| if (SOS_OK != retval) |
| break; |
| |
| of = sos_process_get_opened_file(proc, fd); |
| if (NULL == of) |
| { |
| retval = -SOS_EBADF; |
| break; |
| } |
| |
| /* Do the actual trunc */ |
| retval = sos_fs_ftruncate(of, length); |
| } |
| break; |
| |
| case SOS_SYSCALL_ID_FSMMAP: |
| { |
| sos_uaddr_t ptr_hint_uaddr; |
| sos_uaddr_t hint_uaddr; |
| sos_size_t length; |
| sos_ui32_t prot; |
| sos_ui32_t flags; |
| int fd; |
| sos_ui32_t offs64_hi; |
| sos_ui32_t offs64_lo; |
| sos_luoffset_t offset_in_resource; |
| struct sos_fs_opened_file * of; |
| struct sos_process * proc; |
| |
| proc = sos_thread_get_current()->process; |
| retval = sos_syscall_get7args(user_ctxt, |
| (unsigned int*)& ptr_hint_uaddr, |
| (unsigned int*)& length, |
| (unsigned int*)& prot, |
| (unsigned int*)& flags, |
| (unsigned int*)& fd, |
| (unsigned int*)& offs64_hi, |
| (unsigned int*)& offs64_lo); |
| if (SOS_OK != retval) |
| break; |
| |
| of = sos_process_get_opened_file(proc, fd); |
| if (NULL == of) |
| { |
| retval = -SOS_EBADF; |
| break; |
| } |
| |
| /* Compute 64 bits offset value */ |
| offset_in_resource = offs64_hi; |
| offset_in_resource <<= 32; |
| offset_in_resource |= offs64_lo; |
| |
| retval = sos_memcpy_from_user((sos_vaddr_t)& hint_uaddr, |
| ptr_hint_uaddr, |
| sizeof(hint_uaddr)); |
| if (sizeof(hint_uaddr) != retval) |
| { |
| retval = -SOS_EFAULT; |
| break; |
| } |
| |
| retval = sos_fs_mmap(of, & hint_uaddr, length, prot, flags, |
| offset_in_resource); |
| if (SOS_OK == retval) |
| { |
| if (sizeof(hint_uaddr) |
| != sos_memcpy_to_user(ptr_hint_uaddr, |
| (sos_vaddr_t)& hint_uaddr, |
| sizeof(hint_uaddr))) |
| { |
| sos_umem_vmm_unmap(sos_process_get_address_space(proc), |
| hint_uaddr, length); |
| retval = -SOS_EFAULT; |
| } |
| } |
| |
| } |
| break; |
| |
| case SOS_SYSCALL_ID_FSYNC: |
| { |
| struct sos_fs_opened_file * of; |
| struct sos_process * proc; |
| int fd; |
| |
| proc = sos_thread_get_current()->process; |
| retval = sos_syscall_get1arg(user_ctxt, |
| (unsigned int*)& fd); |
| if (SOS_OK != retval) |
| break; |
| |
| of = sos_process_get_opened_file(proc, fd); |
| if (NULL == of) |
| { |
| retval = -SOS_EBADF; |
| break; |
| } |
| |
| /* Do the actual sync */ |
| retval = sos_fs_fsync(of); |
| } |
| break; |
| |
| case SOS_SYSCALL_ID_FCNTL: |
| { |
| struct sos_fs_opened_file * of; |
| struct sos_process * proc; |
| sos_ui32_t cmd, arg; |
| int fd; |
| |
| proc = sos_thread_get_current()->process; |
| retval = sos_syscall_get3args(user_ctxt, |
| (unsigned int*)& fd, |
| (unsigned int*)& cmd, |
| (unsigned int*)& arg); |
| if (SOS_OK != retval) |
| break; |
| |
| of = sos_process_get_opened_file(proc, fd); |
| if (NULL == of) |
| { |
| retval = -SOS_EBADF; |
| break; |
| } |
| |
| /* Do the actual fcntl */ |
| retval = sos_fs_fcntl(of, cmd, arg); |
| } |
| break; |
| |
| case SOS_SYSCALL_ID_CREAT: |
| { |
| sos_uaddr_t user_str; |
| sos_size_t len; |
| sos_ui32_t access_rights; |
| char * path; |
| struct sos_process * proc; |
| |
| proc = sos_thread_get_current()->process; |
| retval = sos_syscall_get3args(user_ctxt, |
| (unsigned int*)& user_str, |
| (unsigned int*)& len, |
| (unsigned int*)& access_rights); |
| if (SOS_OK != retval) |
| break; |
| |
| retval = sos_memdup_from_user((sos_vaddr_t*) &path, user_str, len, 0); |
| if (SOS_OK != retval) |
| break; |
| |
| retval = sos_fs_creat(proc, |
| path, len, |
| access_rights); |
| sos_kfree((sos_vaddr_t)path); |
| } |
| break; |
| |
| case SOS_SYSCALL_ID_LINK: |
| case SOS_SYSCALL_ID_RENAME: |
| { |
| sos_uaddr_t user_oldpath, user_newpath; |
| sos_size_t oldpathlen, newpathlen; |
| char * kernel_oldpath, * kernel_newpath; |
| struct sos_process * proc; |
| |
| proc = sos_thread_get_current()->process; |
| retval = sos_syscall_get4args(user_ctxt, |
| (unsigned int*)& user_oldpath, |
| (unsigned int*)& oldpathlen, |
| (unsigned int*)& user_newpath, |
| (unsigned int*)& newpathlen); |
| if (SOS_OK != retval) |
| break; |
| |
| retval = sos_memdup_from_user((sos_vaddr_t*) &kernel_oldpath, |
| user_oldpath, |
| oldpathlen, 0); |
| if (SOS_OK != retval) |
| break; |
| |
| retval = sos_memdup_from_user((sos_vaddr_t*) &kernel_newpath, |
| user_newpath, |
| newpathlen, 0); |
| if (SOS_OK != retval) |
| { |
| sos_kfree((sos_vaddr_t) kernel_oldpath); |
| break; |
| } |
| |
| if (syscall_id == SOS_SYSCALL_ID_LINK) |
| retval = sos_fs_link(proc, |
| kernel_oldpath, oldpathlen, |
| kernel_newpath, newpathlen); |
| else |
| retval = sos_fs_rename(proc, |
| kernel_oldpath, oldpathlen, |
| kernel_newpath, newpathlen); |
| sos_kfree((sos_vaddr_t)kernel_oldpath); |
| sos_kfree((sos_vaddr_t)kernel_newpath); |
| } |
| break; |
| |
| case SOS_SYSCALL_ID_UNLINK: |
| { |
| sos_uaddr_t user_str; |
| sos_size_t len; |
| char * path; |
| struct sos_process * proc; |
| |
| proc = sos_thread_get_current()->process; |
| retval = sos_syscall_get2args(user_ctxt, |
| (unsigned int*)& user_str, |
| (unsigned int*)& len); |
| if (SOS_OK != retval) |
| break; |
| |
| retval = sos_memdup_from_user((sos_vaddr_t*) &path, user_str, len, 0); |
| if (SOS_OK != retval) |
| break; |
| |
| retval = sos_fs_unlink(proc, |
| path, len); |
| sos_kfree((sos_vaddr_t)path); |
| } |
| break; |
| |
| case SOS_SYSCALL_ID_SYMLINK: |
| { |
| sos_uaddr_t user_path, user_targetpath; |
| sos_size_t pathlen, targetpathlen; |
| char * kernel_path; |
| struct sos_process * proc; |
| |
| proc = sos_thread_get_current()->process; |
| retval = sos_syscall_get4args(user_ctxt, |
| (unsigned int*)& user_path, |
| (unsigned int*)& pathlen, |
| (unsigned int*)& user_targetpath, |
| (unsigned int*)& targetpathlen); |
| if (SOS_OK != retval) |
| break; |
| |
| retval = sos_memdup_from_user((sos_vaddr_t*) &kernel_path, |
| user_path, |
| pathlen, 0); |
| if (SOS_OK != retval) |
| break; |
| |
| retval = sos_fs_symlink(proc, |
| kernel_path, pathlen, |
| user_targetpath, targetpathlen); |
| sos_kfree((sos_vaddr_t)kernel_path); |
| } |
| break; |
| |
| case SOS_SYSCALL_ID_MKDIR: |
| { |
| sos_uaddr_t user_str; |
| sos_size_t len; |
| sos_ui32_t access_rights; |
| char * path; |
| struct sos_process * proc; |
| |
| proc = sos_thread_get_current()->process; |
| retval = sos_syscall_get3args(user_ctxt, |
| (unsigned int*)& user_str, |
| (unsigned int*)& len, |
| (unsigned int*)& access_rights); |
| if (SOS_OK != retval) |
| break; |
| |
| retval = sos_memdup_from_user((sos_vaddr_t*) &path, user_str, len, 0); |
| if (SOS_OK != retval) |
| break; |
| |
| retval = sos_fs_mkdir(proc, |
| path, len, access_rights); |
| sos_kfree((sos_vaddr_t)path); |
| } |
| break; |
| |
| case SOS_SYSCALL_ID_RMDIR: |
| { |
| sos_uaddr_t user_str; |
| sos_size_t len; |
| char * path; |
| struct sos_process * proc; |
| |
| proc = sos_thread_get_current()->process; |
| retval = sos_syscall_get2args(user_ctxt, |
| (unsigned int*)& user_str, |
| (unsigned int*)& len); |
| if (SOS_OK != retval) |
| break; |
| |
| retval = sos_memdup_from_user((sos_vaddr_t*) &path, user_str, len, 0); |
| if (SOS_OK != retval) |
| break; |
| |
| retval = sos_fs_rmdir(proc, path, len); |
| sos_kfree((sos_vaddr_t)path); |
| } |
| break; |
| |
| case SOS_SYSCALL_ID_CHMOD: |
| { |
| sos_uaddr_t user_str; |
| sos_size_t len; |
| sos_ui32_t access_rights; |
| char * path; |
| struct sos_process * proc; |
| |
| proc = sos_thread_get_current()->process; |
| retval = sos_syscall_get3args(user_ctxt, |
| (unsigned int*)& user_str, |
| (unsigned int*)& len, |
| (unsigned int*)& access_rights); |
| if (SOS_OK != retval) |
| break; |
| |
| retval = sos_memdup_from_user((sos_vaddr_t*) &path, user_str, len, 0); |
| if (SOS_OK != retval) |
| break; |
| |
| retval = sos_fs_chmod(proc, path, len, access_rights); |
| sos_kfree((sos_vaddr_t)path); |
| } |
| break; |
| |
| case SOS_SYSCALL_ID_STAT64: |
| { |
| sos_uaddr_t user_str; |
| sos_size_t len; |
| sos_uaddr_t user_stat_struct; |
| struct sos_fs_stat kernel_stat_struct; |
| int nofollow; |
| char * path; |
| struct sos_process * proc; |
| |
| proc = sos_thread_get_current()->process; |
| retval = sos_syscall_get4args(user_ctxt, |
| (unsigned int*)& user_str, |
| (unsigned int*)& len, |
| (unsigned int*)& nofollow, |
| (unsigned int*)& user_stat_struct); |
| if (SOS_OK != retval) |
| break; |
| |
| retval = sos_memdup_from_user((sos_vaddr_t*) &path, user_str, len, 0); |
| if (SOS_OK != retval) |
| break; |
| |
| retval = sos_fs_stat(proc, path, len, nofollow, & kernel_stat_struct); |
| sos_kfree((sos_vaddr_t)path); |
| if (SOS_OK != retval) |
| break; |
| |
| if (sizeof(kernel_stat_struct) |
| != sos_memcpy_to_user(user_stat_struct, |
| (sos_vaddr_t) & kernel_stat_struct, |
| sizeof(kernel_stat_struct))) |
| retval = -SOS_EFAULT; |
| } |
| break; |
| |
| case SOS_SYSCALL_ID_CHROOT: |
| case SOS_SYSCALL_ID_CHDIR: |
| { |
| sos_uaddr_t user_str; |
| sos_size_t len; |
| char * path; |
| struct sos_fs_opened_file * of, * old_of; |
| struct sos_process * proc; |
| |
| proc = sos_thread_get_current()->process; |
| retval = sos_syscall_get2args(user_ctxt, |
| (unsigned int*)& user_str, |
| (unsigned int*)& len); |
| if (SOS_OK != retval) |
| break; |
| |
| retval = sos_memdup_from_user((sos_vaddr_t*) &path, user_str, len, 0); |
| if (SOS_OK != retval) |
| break; |
| |
| retval = sos_fs_open(proc, |
| path, len, |
| SOS_FS_OPEN_DIRECTORY, |
| SOS_FS_OPEN_READ, |
| & of); |
| sos_kfree((sos_vaddr_t)path); |
| if (SOS_OK != retval) |
| break; |
| |
| if (syscall_id == SOS_SYSCALL_ID_CHROOT) |
| retval = sos_process_chroot(proc, of, & old_of); |
| else |
| retval = sos_process_chdir(proc, of, & old_of); |
| |
| if (retval < 0) |
| { |
| sos_fs_close(of); |
| break; |
| } |
| |
| sos_fs_close(old_of); |
| } |
| break; |
| |
| case SOS_SYSCALL_ID_FCHDIR: |
| { |
| struct sos_fs_opened_file * of, * new_of, * old_of; |
| struct sos_process * proc; |
| int fd; |
| |
| proc = sos_thread_get_current()->process; |
| retval = sos_syscall_get1arg(user_ctxt, |
| (unsigned int*)& fd); |
| if (SOS_OK != retval) |
| break; |
| |
| of = sos_process_get_opened_file(proc, fd); |
| if (NULL == of) |
| { |
| retval = -SOS_EBADF; |
| break; |
| } |
| |
| /* Duplicate this FD */ |
| retval = sos_fs_duplicate_opened_file(of, proc, & new_of); |
| if (SOS_OK != retval) |
| break; |
| |
| /* Do the actual chdir */ |
| retval = sos_process_chdir(proc, new_of, & old_of); |
| if (retval < 0) |
| { |
| sos_fs_close(new_of); |
| break; |
| } |
| |
| sos_fs_close(old_of); |
| } |
| break; |
| |
case SOS_SYSCALL_ID_BOCHS_WRITE: | case SOS_SYSCALL_ID_BOCHS_WRITE: |
{ | { |
sos_uaddr_t user_str; | sos_uaddr_t user_str; |
|
|
if (SOS_OK != retval) | if (SOS_OK != retval) |
break; | break; |
| |
str = (char*)sos_kmalloc(len + 1, 0); | retval = sos_strndup_from_user(& str, user_str, len + 1, 0); |
if (str) | if (SOS_OK == retval) |
retval = sos_strzcpy_from_user(str, user_str, len+1); | sos_bochs_printf("THR 0x%x: ", |
str[len] = '\0'; | (unsigned)sos_thread_get_current()); |
if (SOS_OK == retval) | retval = sos_bochs_putstring(str); |
{ | retval = len; |
sos_bochs_printf("THR 0x%x: ", | |
(unsigned)sos_thread_get_current()); | |
retval = sos_bochs_putstring(str); | |
retval = len; | |
} | |
} | } |
else | |
retval = -SOS_ENOMEM; | |
break; | break; |
| |
|
|
if (SOS_OK != retval) | if (SOS_OK != retval) |
break; | break; |
| |
str = (char*)sos_kmalloc(len + 1, 0); | str = (char*)sos_kmalloc(len, 0); |
{ | { |
int i; | int i; |
| |
/tmp/sos-code-article7.5/userland/fstest.c (1970-01-01 01:00:00.000000000 +0100
) |
|
../sos-code-article8/userland/fstest.c (2005-07-01 16:39:50.000000000 +0200
) |
|
|
|
| /* Copyright (C) 2005 David Decotigny |
| |
| 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 <crt.h> |
| #include <libc.h> |
| #include <stdarg.h> |
| #include <string.h> |
| #include <debug.h> |
| |
| #include "fstest_utils.h" |
| |
| /** |
| * @file fstest.c |
| * |
| * File-system tests |
| */ |
| |
| int main() |
| { |
| int fd, len; |
| char buff[256]; |
| |
| bochs_printf("Hi from fstest\n"); |
| |
| ls("/", 1, 1); |
| ls(".", 1, 1); |
| ls("", 1, 1); |
| ls(0, 1, 1); |
| |
| TEST_EXPECT_CONDITION(fd = open("", 0), |
| RETVAL < 0); |
| |
| TEST_EXPECT_CONDITION(fd = open(0, 0), |
| RETVAL < 0); |
| |
| TEST_EXPECT_CONDITION(fd = open("/", O_RDWR), |
| RETVAL == 0); |
| |
| TEST_EXPECT_CONDITION(fd = open("/", O_RDONLY), |
| RETVAL == 1); |
| |
| TEST_EXPECT_CONDITION(fd = open("/", O_WRONLY), |
| RETVAL == 2); |
| |
| TEST_EXPECT_CONDITION(close(1), |
| RETVAL == 0); |
| |
| TEST_EXPECT_CONDITION(fd = open("/", O_WRONLY), |
| RETVAL == 1); |
| |
| TEST_EXPECT_CONDITION(fd = open("//", O_WRONLY), |
| RETVAL == 3); |
| |
| TEST_EXPECT_CONDITION(fd = open("////////", O_WRONLY), |
| RETVAL == 4); |
| |
| TEST_EXPECT_CONDITION(fd = open("/does not exist", O_WRONLY), |
| RETVAL < 0); |
| |
| TEST_EXPECT_CONDITION(fd = open("////does not exist", O_WRONLY), |
| RETVAL < 0); |
| |
| TEST_EXPECT_CONDITION(fd = open("/does not exist/", O_WRONLY), |
| RETVAL < 0); |
| |
| TEST_EXPECT_CONDITION(fd = open("////does not exist/", O_WRONLY), |
| RETVAL < 0); |
| |
| TEST_EXPECT_CONDITION(fd = open("/does not exist////", O_WRONLY), |
| RETVAL < 0); |
| |
| TEST_EXPECT_CONDITION(fd = open("////does not exist/////", O_WRONLY), |
| RETVAL < 0); |
| |
| TEST_EXPECT_CONDITION(fd = open("does not exist", O_WRONLY), |
| RETVAL < 0); |
| |
| TEST_EXPECT_CONDITION(fd = open("does not exist/", O_WRONLY), |
| RETVAL < 0); |
| |
| TEST_EXPECT_CONDITION(fd = open("does not exist////", O_WRONLY), |
| RETVAL); |
| |
| TEST_EXPECT_CONDITION(fd = open("/does not exist/ab c d", O_WRONLY), |
| RETVAL < 0); |
| |
| TEST_EXPECT_CONDITION(fd = open("////does not exist/ab c d", O_WRONLY), |
| RETVAL < 0); |
| |
| TEST_EXPECT_CONDITION(fd = open("/does not exist////ab c d", O_WRONLY), |
| RETVAL < 0); |
| |
| TEST_EXPECT_CONDITION(fd = open("////does not exist/////ab c d", O_WRONLY), |
| RETVAL < 0); |
| |
| TEST_EXPECT_CONDITION(fd = open("does not exist", O_WRONLY), |
| RETVAL < 0); |
| |
| TEST_EXPECT_CONDITION(fd = open("does not exist/ab c d", O_WRONLY), |
| RETVAL < 0); |
| |
| TEST_EXPECT_CONDITION(fd = open("does not exist////ab c d", O_WRONLY), |
| RETVAL < 0); |
| |
| TEST_EXPECT_CONDITION(fd = open("/", O_RDWR), |
| RETVAL == 5); |
| |
| TEST_EXPECT_CONDITION(fd = open("/tutu.txt", O_RDWR), |
| RETVAL < 0); |
| |
| ls("/", 1, 1); |
| |
| TEST_EXPECT_CONDITION(fd = open("/tutu.txt", O_RDWR | O_CREAT, |
| S_IRUSR | S_IWUSR), |
| RETVAL == 6); |
| |
| ls("/", 1, 1); |
| |
| TEST_EXPECT_CONDITION(fd = open("tutu.txt", O_RDWR | O_CREAT), |
| RETVAL == 7); |
| |
| /* O_EXCL with an already-existing file */ |
| TEST_EXPECT_CONDITION(fd = open("tutu.txt", O_RDWR | O_CREAT | O_EXCL), |
| RETVAL < 0); |
| |
| TEST_EXPECT_CONDITION(fd = open("/toto.txt", O_RDWR | O_CREAT | O_EXCL, |
| S_IRWXALL), |
| RETVAL == 8); |
| |
| /* O_EXCL with an already-existing file */ |
| TEST_EXPECT_CONDITION(fd = open("toto.txt", O_RDWR | O_CREAT | O_EXCL), |
| RETVAL < 0); |
| |
| TEST_EXPECT_CONDITION(fd = open("toto.txt", O_RDWR | O_CREAT, |
| S_IRUSR | S_IWUSR), |
| RETVAL == 9); |
| |
| /* Trailing slash on non-dir entries */ |
| TEST_EXPECT_CONDITION(fd = open("toto.txt/", O_RDWR), RETVAL < 0); |
| TEST_EXPECT_CONDITION(fd = open("notdir/", O_RDWR | O_CREAT, S_IRWXALL), |
| RETVAL < 0); |
| TEST_EXPECT_CONDITION(fd = open("notdir/", O_RDWR), RETVAL < 0); |
| |
| /* Substring match */ |
| TEST_EXPECT_CONDITION(fd = open("toto1.txt", O_RDWR), |
| RETVAL < 0); |
| TEST_EXPECT_CONDITION(fd = open("toto1.tx", O_RDWR | O_CREAT, S_IWUSR), |
| RETVAL == 10); |
| |
| /* Substring match */ |
| TEST_EXPECT_CONDITION(fd = open("toto.tx", O_RDWR), |
| RETVAL < 0); |
| TEST_EXPECT_CONDITION(fd = open("toto.tx", O_RDWR | O_CREAT, |
| S_IRUSR | S_IWUSR), |
| RETVAL == 11); |
| |
| /* |
| * read/write/seek |
| */ |
| |
| TEST_EXPECT_CONDITION(len = read(fd, buff, 256), |
| RETVAL == 0); |
| |
| TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_SET), |
| RETVAL == 0); |
| |
| strzcpy(buff, "Bonjour !", 256); |
| TEST_EXPECT_CONDITION(len = write(fd, buff, 10), |
| RETVAL == 10); |
| |
| ls("/", 1, 1); |
| |
| TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_SET), |
| RETVAL == 0); |
| |
| strzcpy(buff, "Garbage garbage garbage", 256); |
| TEST_EXPECT_CONDITION(len = read(fd, buff, 256), |
| RETVAL == 10); |
| bochs_printf("read s='%s'\n", buff); |
| TEST_EXPECT_CONDITION(strcmp("Bonjour !", buff), RETVAL ==0); |
| TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_CUR), RETVAL == 10); |
| |
| /* |
| * truncate |
| */ |
| |
| TEST_EXPECT_CONDITION(ftruncate(fd, 3), RETVAL == 0); |
| |
| /* The current position should not have changed */ |
| TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_CUR), RETVAL == 10); |
| |
| /* Make sure we cannot read anything because we get past the end of |
| the file */ |
| strzcpy(buff, "Garbage garbage garbage", 256); |
| TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 0); |
| |
| /* Now get back at the begining of the file */ |
| TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_SET), RETVAL == 0); |
| |
| /* Make sure that we can read something with the correct first 3 |
| characters */ |
| strzcpy(buff, "Garbage garbage garbage", 256); |
| TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 3); |
| bochs_printf("read s='%s'\n", buff); |
| TEST_EXPECT_CONDITION(strncmp("Bon", buff, len), RETVAL == 0); |
| |
| /* |
| * open mode |
| */ |
| |
| ls("/", 1, 1); |
| |
| |
| /* Open r/w, create read-only */ |
| |
| TEST_EXPECT_CONDITION(fd = open("toto2.txt", O_RDWR | O_CREAT, S_IRUSR), |
| RETVAL == 12); |
| |
| strzcpy(buff, "Garbage garbage garbage", 256); |
| TEST_EXPECT_CONDITION(len = read(fd, buff, 256), |
| RETVAL == 0); |
| |
| TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_SET), RETVAL == 0); |
| |
| strzcpy(buff, "Permission denied", 256); |
| TEST_EXPECT_CONDITION(len = write(fd, buff, 10), |
| RETVAL < 0); /* Permission denied ! */ |
| |
| TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_SET), RETVAL == 0); |
| |
| strzcpy(buff, "Garbage garbage garbage", 256); |
| TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 0); |
| |
| /* Open read-only, create r/w */ |
| |
| TEST_EXPECT_CONDITION(fd = open("toto3.txt", O_RDONLY | O_CREAT, |
| S_IRUSR | S_IWUSR), |
| RETVAL == 13); |
| |
| strzcpy(buff, "Garbage garbage garbage", 256); |
| TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 0); |
| |
| TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_SET), RETVAL == 0); |
| |
| strzcpy(buff, "Permission denied 2", 256); |
| TEST_EXPECT_CONDITION(len = write(fd, buff, 10), |
| RETVAL < 0); /* Permission denied ! */ |
| |
| TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_SET), RETVAL == 0); |
| |
| strzcpy(buff, "Garbage garbage garbage", 256); |
| TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 0); |
| |
| /* Create another process that chdirs in it */ |
| if (fork() == 0) |
| { |
| bochs_printf("Hello from child\n"); |
| TEST_EXPECT_CONDITION(fd = open("shrd.txt", O_RDWR | O_CREAT, S_IRWXALL), |
| RETVAL == 14); |
| strzcpy(buff, "Hello from child !", 256); |
| TEST_EXPECT_CONDITION(len = write(fd, buff, 19), |
| RETVAL == 19); |
| TEST_EXPECT_CONDITION(close(fd), RETVAL == 0); |
| bochs_printf("Bye from child\n"); |
| return 0; |
| } |
| |
| bochs_printf("Father sleeping\n"); |
| nanosleep(1, 0); |
| ls("/", 1, 1); |
| TEST_EXPECT_CONDITION(fd = open("shrd.txt", O_RDONLY), |
| RETVAL == 14); |
| strzcpy(buff, "Garbage garbage garbage", 256); |
| TEST_EXPECT_CONDITION(len = read(fd, buff, 256), |
| RETVAL == 19); |
| bochs_printf("read s='%s'\n", buff); |
| TEST_EXPECT_CONDITION(strncmp("Hello from child !", buff, len), |
| RETVAL == 0); |
| TEST_EXPECT_CONDITION(close(fd), RETVAL == 0); |
| TEST_EXPECT_CONDITION(unlink("shrd.txt"), RETVAL == 0); |
| ls("/", 1, 1); |
| |
| /* |
| * ioctl / fcntl |
| */ |
| |
| TEST_EXPECT_CONDITION(fcntl(fd, 2, 3), RETVAL < 0); /* Not supported |
| by virtfs */ |
| |
| ls("/", 1, 1); |
| |
| /* |
| * creat/link/unlink/symlink |
| */ |
| TEST_EXPECT_CONDITION(creat("toto4.txt", S_IRUSR), RETVAL == 0); |
| TEST_EXPECT_CONDITION(creat("toto4.txt", S_IRWXALL), RETVAL < 0); /*EEXIST*/ |
| TEST_EXPECT_CONDITION(link("toto4.txt", "toto5.txt"), RETVAL == 0); |
| TEST_EXPECT_CONDITION(link("toto4.txt", "toto5.txt"), RETVAL < 0); /*EEXIST*/ |
| TEST_EXPECT_CONDITION(link("toto4.txt", "toto.txt"), RETVAL < 0); /*EEXIST*/ |
| ls("/", 1, 1); |
| TEST_EXPECT_CONDITION(chmod("toto5.txt", S_IRUSR | S_IWUSR), RETVAL == 0); |
| ls("/", 1, 1); |
| |
| TEST_EXPECT_CONDITION(fd = open("toto5.txt", O_RDWR), RETVAL == 14); |
| strzcpy(buff, "Garbage garbage garbage", 256); |
| TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 0); |
| |
| TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_SET), RETVAL == 0); |
| |
| strzcpy(buff, "Hello world from toto5", 256); |
| TEST_EXPECT_CONDITION(len = write(fd, buff, 24), RETVAL == 24); |
| |
| TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_SET), RETVAL == 0); |
| |
| strzcpy(buff, "Garbage garbage garbage", 256); |
| TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 24); |
| bochs_printf("read s='%s'\n", buff); |
| |
| TEST_EXPECT_CONDITION(fd = open("toto4.txt", O_RDWR), RETVAL == 15); |
| strzcpy(buff, "Garbage garbage garbage", 256); |
| TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 24); |
| bochs_printf("read s='%s'\n", buff); |
| |
| ls("/", 1, 1); |
| |
| TEST_EXPECT_CONDITION(link("dangling", "toto42.txt"), RETVAL < 0); /*ENOENT*/ |
| TEST_EXPECT_CONDITION(unlink("toto4.txt"), RETVAL == 0); |
| TEST_EXPECT_CONDITION(unlink("toto42.txt"), RETVAL < 0); /* ENOENT */ |
| TEST_EXPECT_CONDITION(unlink("toto4.txt"), RETVAL < 0); /* ENOENT */ |
| TEST_EXPECT_CONDITION(creat("toto4.txt", S_IWUSR | S_IRUSR), RETVAL == 0); |
| TEST_EXPECT_CONDITION(unlink("toto5.txt/"), RETVAL < 0); /* EISDIR ? */ |
| TEST_EXPECT_CONDITION(rmdir("toto5.txt/"), RETVAL < 0); /* ENOTDIR ? */ |
| TEST_EXPECT_CONDITION(rmdir("toto5.txt"), RETVAL < 0); /* ENOTDIR ? */ |
| TEST_EXPECT_CONDITION(unlink("toto5.txt"), RETVAL == 0); |
| TEST_EXPECT_CONDITION(unlink("toto5.txt"), RETVAL < 0); /* ENOENT */ |
| TEST_EXPECT_CONDITION(creat("toto4.txt", S_IRWXALL), RETVAL < 0); /*EEXIST*/ |
| |
| ls("/", 1, 1); |
| |
| /* Change symlinks */ |
| TEST_EXPECT_CONDITION(symlink("toto4.txt", "toto5.txt"), RETVAL == 0); |
| TEST_EXPECT_CONDITION(symlink("toto4.txt", "toto5.txt"), RETVAL < 0); /*EEXIST*/ |
| TEST_EXPECT_CONDITION(symlink("toto4.txt", "toto.txt"), RETVAL < 0); /*EEXIST*/ |
| |
| ls("/", 1, 1); |
| |
| TEST_EXPECT_CONDITION(fd = open("toto5.txt", O_RDWR), RETVAL == 16); |
| strzcpy(buff, "Garbage garbage garbage", 256); |
| TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 0); |
| |
| TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_SET), RETVAL == 0); |
| |
| strzcpy(buff, "Hello world from toto5", 256); |
| TEST_EXPECT_CONDITION(len = write(fd, buff, 24), RETVAL == 24); |
| |
| TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_SET), RETVAL == 0); |
| |
| strzcpy(buff, "Garbage garbage garbage", 256); |
| TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 24); |
| bochs_printf("read s='%s'\n", buff); |
| TEST_EXPECT_CONDITION(strcmp(buff, "Hello world from toto5"), RETVAL == 0); |
| |
| TEST_EXPECT_CONDITION(fd = open("toto4.txt", O_RDWR), RETVAL == 17); |
| strzcpy(buff, "Garbage garbage garbage", 256); |
| TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 24); |
| bochs_printf("read s='%s'\n", buff); |
| TEST_EXPECT_CONDITION(strcmp(buff, "Hello world from toto5"), RETVAL == 0); |
| |
| |
| TEST_EXPECT_CONDITION(symlink("dangling", "toto6.txt"), RETVAL == 0); |
| TEST_EXPECT_CONDITION(symlink("dangling", "toto6.txt"), RETVAL < 0); /*EEXIST*/ |
| |
| TEST_EXPECT_CONDITION(fd = open("toto6.txt", O_RDWR), RETVAL < 0); |
| TEST_EXPECT_CONDITION(fd = open("toto6.txt", O_RDWR | O_NOFOLLOW), RETVAL == 18); |
| strzcpy(buff, "Garbage garbage garbage", 256); |
| TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 8); |
| bochs_printf("read s='%s'\n", buff); |
| TEST_EXPECT_CONDITION(strncmp(buff, "dangling", len), RETVAL == 0); |
| |
| ls("/", 1, 1); |
| |
| /* mkdir/rmdir */ |
| |
| TEST_EXPECT_CONDITION(mkdir("yo1", S_IRUSR | S_IXUSR), RETVAL == 0); |
| TEST_EXPECT_CONDITION(mkdir("yo1", S_IRWXALL), RETVAL < 0); /*EEXIST*/ |
| |
| ls("/", 1, 1); |
| |
| TEST_EXPECT_CONDITION(unlink("yo1"), RETVAL < 0); /*EISDIR*/ |
| TEST_EXPECT_CONDITION(unlink("yo1/"), RETVAL < 0); /*EISDIR*/ |
| TEST_EXPECT_CONDITION(rmdir("yo1"), RETVAL == 0); |
| TEST_EXPECT_CONDITION(rmdir("yo1"), RETVAL < 0); /*ENOENT*/ |
| TEST_EXPECT_CONDITION(rmdir("yoda"), RETVAL < 0); /*ENOENT*/ |
| |
| ls("/", 1, 1); |
| |
| TEST_EXPECT_CONDITION(mkdir("yoplait1", S_IRWXALL), RETVAL == 0); |
| TEST_EXPECT_CONDITION(rename("yoplait1", "mouf1"), RETVAL == 0); |
| TEST_EXPECT_CONDITION(fd = open("mouf1/a", O_RDWR | O_CREAT, S_IRWXALL), RETVAL == 19); |
| TEST_EXPECT_CONDITION(unlink("mouf1/a"), RETVAL == 0); |
| TEST_EXPECT_CONDITION(rmdir("mouf1"), RETVAL == 0); |
| |
| TEST_EXPECT_CONDITION(mkdir("yoplait2", S_IRWXALL), RETVAL == 0); |
| TEST_EXPECT_CONDITION(rename("yoplait2", "mouf2"), RETVAL == 0); |
| TEST_EXPECT_CONDITION(fd = open("mouf2/a", O_RDWR | O_CREAT, S_IRWXALL), RETVAL == 20); |
| TEST_EXPECT_CONDITION(rmdir("mouf2"), RETVAL < 0); |
| |
| TEST_EXPECT_CONDITION(mkdir("yoplait3", S_IRWXALL), RETVAL == 0); |
| TEST_EXPECT_CONDITION(rename("yoplait3", "mouf3"), RETVAL == 0); |
| TEST_EXPECT_CONDITION(creat("mouf3/a", S_IRWXALL), RETVAL == 0); |
| TEST_EXPECT_CONDITION(unlink("mouf3/a"), RETVAL == 0); |
| TEST_EXPECT_CONDITION(rmdir("mouf3"), RETVAL == 0); |
| |
| TEST_EXPECT_CONDITION(mkdir("yoplait4", S_IRWXALL), RETVAL == 0); |
| TEST_EXPECT_CONDITION(rename("yoplait4", "mouf4"), RETVAL == 0); |
| TEST_EXPECT_CONDITION(creat("mouf4/a", S_IRWXALL), RETVAL == 0); |
| TEST_EXPECT_CONDITION(rmdir("mouf4"), RETVAL < 0); |
| |
| ls("/", 1, 1); |
| |
| ls("/", 1, 1); |
| ls("..", 1, 1); |
| ls("../", 1, 1); |
| ls("/..", 1, 1); |
| |
| /* subdirs */ |
| TEST_EXPECT_CONDITION(fd = open("yo1/titi1.txt", O_RDONLY), RETVAL < 0); |
| TEST_EXPECT_CONDITION(fd = open("yo1/titi1.txt", O_RDONLY | O_CREAT, S_IRUSR), RETVAL < 0); |
| |
| ls("/", 1, 1); |
| ls("/yo1", 1, 1); |
| |
| TEST_EXPECT_CONDITION(mkdir("yo1", S_IRWXALL), RETVAL == 0); |
| TEST_EXPECT_CONDITION(fd = open("yo1/titi1.txt", O_RDONLY), RETVAL < 0); |
| TEST_EXPECT_CONDITION(fd = open("yo1/titi1.txt", O_RDONLY | O_CREAT, S_IRUSR), RETVAL == 21); |
| |
| ls("/", 1, 1); |
| ls("/yo1", 1, 1); |
| |
| TEST_EXPECT_CONDITION(mkdir("yo2", S_IRUSR | S_IXUSR), RETVAL == 0); |
| TEST_EXPECT_CONDITION(fd = open("yo2/titi1.txt", O_RDONLY), RETVAL < 0); |
| TEST_EXPECT_CONDITION(fd = open("yo2/titi1.txt", O_RDONLY | O_CREAT, S_IRUSR), RETVAL < 0); |
| |
| ls("/", 1, 1); |
| |
| TEST_EXPECT_CONDITION(mkdir("yo3", S_IWUSR | S_IXUSR), RETVAL == 0); |
| TEST_EXPECT_CONDITION(fd = open("yo3/titi1.txt", O_RDONLY), RETVAL < 0); |
| TEST_EXPECT_CONDITION(fd = open("yo3/titi1.txt", O_RDONLY | O_CREAT, S_IRUSR), RETVAL == 22); |
| |
| ls("/", 1, 1); |
| |
| TEST_EXPECT_CONDITION(mkdir("yo4", S_IWUSR), RETVAL == 0); |
| TEST_EXPECT_CONDITION(fd = open("yo4/titi1.txt", O_RDONLY), RETVAL < 0); |
| TEST_EXPECT_CONDITION(fd = open("yo4/titi1.txt", O_RDONLY | O_CREAT, S_IRUSR), RETVAL < 0); |
| |
| ls("/", 1, 1); |
| |
| TEST_EXPECT_CONDITION(chdir("nowhere"), RETVAL < 0); |
| TEST_EXPECT_CONDITION(chdir("yo1"), RETVAL == 0); |
| |
| ls(".", 1, 1); |
| ls("/", 1, 1); |
| ls("..", 1, 1); |
| ls("../../../../", 1, 1); |
| ls("/../../../../", 1, 1); |
| |
| /* Test chroot */ |
| |
| TEST_EXPECT_CONDITION(chroot("nowhere"), RETVAL < 0); |
| TEST_EXPECT_CONDITION(chroot("."), RETVAL == 0); |
| ls(".", 1, 1); |
| ls("/", 1, 1); |
| ls("..", 1, 1); |
| ls("../../../../", 1, 1); |
| ls("/../../../../", 1, 1); |
| |
| /* mount */ |
| TEST_EXPECT_CONDITION(mount(NULL, "nowhere", NULL, 0, NULL), RETVAL < 0); |
| TEST_EXPECT_CONDITION(mount(NULL, "nowhere", "yoplait", 0, NULL), RETVAL < 0); |
| TEST_EXPECT_CONDITION(mount(NULL, "nowhere", "virtfs", 0, NULL), RETVAL < 0); |
| |
| TEST_EXPECT_CONDITION(mkdir("mnt", S_IRWXALL), RETVAL == 0); |
| TEST_EXPECT_CONDITION(mkdir("mnt/subdir0", S_IRWXALL), RETVAL == 0); |
| ls("/", 1, 1); |
| TEST_EXPECT_CONDITION(mount(NULL, "mnt", "virtfs", 0, NULL), RETVAL == 0); |
| ls("/", 1, 1); |
| TEST_EXPECT_CONDITION(mkdir("mnt/subdir_mounted", S_IRWXALL), RETVAL == 0); |
| ls("/", 1, 1); |
| |
| /* Make sure we cannot umount if the FS is in use */ |
| TEST_EXPECT_CONDITION(fd = open("mnt/subdir_mounted", O_DIRECTORY), RETVAL == 23); |
| TEST_EXPECT_CONDITION(umount("mnt"), RETVAL < 0); |
| TEST_EXPECT_CONDITION(close(fd), RETVAL == 0); |
| |
| /* Make sure we cannot umount if the FS is in use */ |
| TEST_EXPECT_CONDITION(chdir("mnt"), RETVAL == 0); |
| TEST_EXPECT_CONDITION(umount("/mnt"), RETVAL < 0); |
| TEST_EXPECT_CONDITION(chdir(".."), RETVAL == 0); |
| ls(".", 1, 1); |
| |
| /* Create another process that chdirs in it */ |
| if (fork() == 0) |
| { |
| bochs_printf("Hello from child\n"); |
| TEST_EXPECT_CONDITION(chdir("mnt"), RETVAL == 0); |
| TEST_EXPECT_CONDITION(fd = open("subdir_mounted", O_DIRECTORY), RETVAL == 23); |
| bochs_printf("Child sleeping...\n"); |
| nanosleep(2, 0); |
| bochs_printf("Bye from child\n"); |
| return 0; |
| } |
| else |
| { |
| bochs_printf("Father sleeping\n"); |
| nanosleep(1, 0); |
| bochs_printf("Father trying to umount, should fail (a process chdir'ed in it)\n"); |
| TEST_EXPECT_CONDITION(umount("mnt"), RETVAL < 0); |
| bochs_printf("Father Resuming normal operation in 3s...\n"); |
| nanosleep(3, 0); |
| } |
| |
| TEST_EXPECT_CONDITION(umount(NULL), RETVAL < 0); |
| TEST_EXPECT_CONDITION(umount("nowhere"), RETVAL < 0); |
| ls("/", 1, 1); |
| TEST_EXPECT_CONDITION(umount("mnt"), RETVAL == 0); |
| ls("/", 1, 1); |
| TEST_EXPECT_CONDITION(umount("mnt"), RETVAL < 0); |
| |
| /* |
| * Mountchain exploration |
| */ |
| TEST_EXPECT_CONDITION(mkdir("/mnt2", S_IRWXALL), RETVAL == 0); |
| TEST_EXPECT_CONDITION(mkdir("/mnt2/nothing-mounted", S_IRWXALL), RETVAL == 0); |
| TEST_EXPECT_CONDITION(mount(NULL, "mnt2", "virtfs", 0, NULL), RETVAL == 0); |
| TEST_EXPECT_CONDITION(mkdir("/mnt2/mountpoint-1", S_IRWXALL), RETVAL == 0); |
| TEST_EXPECT_CONDITION(mount(NULL, "mnt2", "virtfs", 0, NULL), RETVAL == 0); |
| TEST_EXPECT_CONDITION(mkdir("/mnt2/mountpoint-2", S_IRWXALL), RETVAL == 0); |
| ls("/", 1, 1); |
| TEST_EXPECT_CONDITION(umount("mnt2"), RETVAL == 0); |
| ls("/", 1, 1); |
| TEST_EXPECT_CONDITION(umount("mnt2"), RETVAL == 0); |
| ls("/", 1, 1); |
| TEST_EXPECT_CONDITION(umount("mnt2"), RETVAL < 0); |
| |
| /* |
| * Erasing files while they are in use |
| */ |
| |
| TEST_EXPECT_CONDITION(fd = open("toto8.txt", O_RDWR | O_CREAT, |
| S_IRUSR | S_IWUSR), |
| RETVAL == 23); |
| ls("/", 1, 1); |
| TEST_EXPECT_CONDITION(unlink("toto8.txt"), RETVAL == 0); |
| ls("/", 1, 1); |
| |
| strzcpy(buff, "Garbage garbage garbage", 256); |
| TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 0); |
| |
| TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_SET), RETVAL == 0); |
| |
| strzcpy(buff, "Hello world from toto8", 256); |
| TEST_EXPECT_CONDITION(len = write(fd, buff, 24), RETVAL == 24); |
| |
| TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_SET), RETVAL == 0); |
| |
| strzcpy(buff, "Garbage garbage garbage", 256); |
| TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 24); |
| bochs_printf("read s='%s'\n", buff); |
| |
| /* |
| * rmdir on still used dirs |
| */ |
| TEST_EXPECT_CONDITION(mkdir("plotch", S_IRWXALL), RETVAL == 0); |
| TEST_EXPECT_CONDITION(chdir("plotch"), RETVAL == 0); |
| TEST_EXPECT_CONDITION(rmdir("../plotch"), RETVAL < 0); |
| TEST_EXPECT_CONDITION(chdir(".."), RETVAL == 0); |
| TEST_EXPECT_CONDITION(rmdir("plotch"), RETVAL == 0); |
| ls("/", 1, 1); |
| |
| TEST_EXPECT_CONDITION(mkdir("plotch", S_IRWXALL), RETVAL == 0); |
| TEST_EXPECT_CONDITION(creat("plotch/a", S_IRWXALL), RETVAL == 0); |
| TEST_EXPECT_CONDITION(rmdir("plotch"), RETVAL < 0); |
| TEST_EXPECT_CONDITION(unlink("plotch/a"), RETVAL == 0); |
| TEST_EXPECT_CONDITION(rmdir("plotch"), RETVAL == 0); |
| ls("/", 1, 1); |
| |
| TEST_EXPECT_CONDITION(mkdir("plotch", S_IRWXALL), RETVAL == 0); |
| TEST_EXPECT_CONDITION(fd = open("plotch/a", O_RDWR | O_CREAT, S_IRWXALL), |
| RETVAL == 24); |
| TEST_EXPECT_CONDITION(rmdir("plotch"), RETVAL < 0); |
| TEST_EXPECT_CONDITION(unlink("plotch/a"), RETVAL == 0); |
| TEST_EXPECT_CONDITION(rmdir("plotch"), RETVAL == 0); |
| TEST_EXPECT_CONDITION(close(fd), RETVAL == 0); |
| ls("/", 1, 1); |
| |
| TEST_EXPECT_CONDITION(mkdir("this is ", S_IRWXALL), RETVAL == 0); |
| TEST_EXPECT_CONDITION(mkdir("this is / a long path", S_IRWXALL), RETVAL == 0); |
| TEST_EXPECT_CONDITION(mkdir("this is / a long path/tothe", S_IRWXALL), RETVAL == 0); |
| TEST_EXPECT_CONDITION(mkdir("this is / a long path/tothe/destination ", S_IRWXALL), RETVAL == 0); |
| TEST_EXPECT_CONDITION(mkdir("this is / a long path/tothe/destination / directory", S_IRWXALL), RETVAL == 0); |
| TEST_EXPECT_CONDITION(fd = open("this is / a long path/tothe/destination / directory/a", O_RDWR | O_CREAT, S_IRWXALL), |
| RETVAL == 24); |
| TEST_EXPECT_CONDITION(rmdir("this is / a long path/tothe/destination / directory"), RETVAL < 0); |
| TEST_EXPECT_CONDITION(unlink("this is / a long path/tothe/destination / directory/a"), RETVAL == 0); |
| TEST_EXPECT_CONDITION(rmdir("this is / a long path/tothe/destination / directory"), RETVAL == 0); |
| TEST_EXPECT_CONDITION(rmdir("this is / a long path/tothe/destination / directory/"), RETVAL < 0); |
| TEST_EXPECT_CONDITION(rmdir("this is / a long path/tothe/destination "), RETVAL == 0); |
| TEST_EXPECT_CONDITION(rmdir("this is / a long path/tothe/"), RETVAL == 0); |
| TEST_EXPECT_CONDITION(rmdir("this is / a long path"), RETVAL == 0); |
| TEST_EXPECT_CONDITION(rmdir("this is "), RETVAL == 0); |
| TEST_EXPECT_CONDITION(close(fd), RETVAL == 0); |
| ls("/", 1, 1); |
| |
| /* |
| * Unlink/link files while they are in use |
| */ |
| |
| TEST_EXPECT_CONDITION(fd = open("toto8.txt", O_RDWR | O_CREAT, |
| S_IRUSR | S_IWUSR), |
| RETVAL == 24); |
| ls("/", 1, 1); |
| TEST_EXPECT_CONDITION(link("toto8.txt", "toto9.txt"), RETVAL == 0); |
| TEST_EXPECT_CONDITION(unlink("toto8.txt"), RETVAL == 0); |
| ls("/", 1, 1); |
| |
| strzcpy(buff, "Garbage garbage garbage", 256); |
| TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 0); |
| |
| TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_SET), RETVAL == 0); |
| |
| strzcpy(buff, "Hello world from toto8", 256); |
| TEST_EXPECT_CONDITION(len = write(fd, buff, 24), RETVAL == 24); |
| |
| TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_SET), RETVAL == 0); |
| |
| strzcpy(buff, "Garbage garbage garbage", 256); |
| TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 24); |
| bochs_printf("read s='%s'\n", buff); |
| |
| TEST_EXPECT_CONDITION(fd = open("toto8.txt", O_RDWR), RETVAL < 0); |
| TEST_EXPECT_CONDITION(fd = open("toto9.txt", O_RDWR), RETVAL == 25); |
| |
| strzcpy(buff, "Garbage garbage garbage", 256); |
| TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 24); |
| bochs_printf("read s='%s'\n", buff); |
| TEST_EXPECT_CONDITION(unlink("toto9.txt"), RETVAL == 0); |
| ls("/", 1, 1); |
| |
| /* |
| * Rename files while they are in use |
| */ |
| |
| TEST_EXPECT_CONDITION(fd = open("toto8.txt", O_RDWR | O_CREAT, |
| S_IRUSR | S_IWUSR), |
| RETVAL == 26); |
| ls("/", 1, 1); |
| TEST_EXPECT_CONDITION(rename("toto8.txt", "toto9.txt"), RETVAL == 0); |
| ls("/", 1, 1); |
| |
| strzcpy(buff, "Garbage garbage garbage", 256); |
| TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 0); |
| |
| TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_SET), RETVAL == 0); |
| |
| strzcpy(buff, "Hello world from toto8", 256); |
| TEST_EXPECT_CONDITION(len = write(fd, buff, 24), RETVAL == 24); |
| |
| TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_SET), RETVAL == 0); |
| |
| strzcpy(buff, "Garbage garbage garbage", 256); |
| TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 24); |
| bochs_printf("read s='%s'\n", buff); |
| TEST_EXPECT_CONDITION(close(fd), RETVAL == 0); |
| |
| TEST_EXPECT_CONDITION(fd = open("toto8.txt", O_RDWR), RETVAL < 0); |
| TEST_EXPECT_CONDITION(fd = open("toto9.txt", O_RDWR), RETVAL == 26); |
| |
| strzcpy(buff, "Garbage garbage garbage", 256); |
| TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 24); |
| bochs_printf("read s='%s'\n", buff); |
| TEST_EXPECT_CONDITION(unlink("toto9.txt"), RETVAL == 0); |
| TEST_EXPECT_CONDITION(close(fd), RETVAL == 0); |
| |
| /* Rename */ |
| ls("/", 1, 1); |
| TEST_EXPECT_CONDITION(rename("/mnt/subdir0", "subdir42"), RETVAL == 0); |
| ls("/", 1, 1); |
| TEST_EXPECT_CONDITION(rename("titi1.txt", "subdir42"), RETVAL < 0); |
| ls("/", 1, 1); |
| TEST_EXPECT_CONDITION(rename("titi1.txt", "subdir42/titi.txt"), RETVAL == 0); |
| |
| /* Rename a dir being used */ |
| TEST_EXPECT_CONDITION(chdir("subdir42"), RETVAL == 0); |
| ls(".", 1, 1); |
| ls("..", 1, 1); |
| ls("/", 1, 1); |
| TEST_EXPECT_CONDITION(rename("../subdir42", "../subdir000"), RETVAL == 0); |
| ls(".", 1, 1); |
| ls("/", 1, 1); |
| ls("..", 1, 1); |
| |
| /* |
| * test mmap |
| */ |
| |
| /* Common use: shared file suppressed as soon as possible */ |
| TEST_EXPECT_CONDITION(fd = open("mmap.txt", O_RDWR | O_CREAT, |
| S_IRUSR | S_IWUSR), |
| RETVAL == 26); |
| if (fork() == 0) |
| { |
| char *shrd; |
| TEST_EXPECT_CONDITION(shrd = mmap(NULL, 4096, PROT_READ | PROT_WRITE, |
| MAP_SHARED, fd, 4096), |
| shrd != NULL); |
| nanosleep(1, 0); |
| strzcpy(shrd, "Hello1 from the child (shared mapping) !", 4096); |
| return 0; |
| } |
| else |
| { |
| char *shrd; |
| TEST_EXPECT_CONDITION(shrd = mmap(NULL, 4096, PROT_READ | PROT_WRITE, |
| MAP_SHARED, fd, 4096), |
| shrd != NULL); |
| strzcpy(shrd, "Garbage garbage garbage", 256); |
| nanosleep(2, 0); |
| bochs_printf("Father woken up\n"); |
| bochs_printf("Read string from child: '%s'\n", shrd); |
| TEST_EXPECT_CONDITION(strcmp(shrd, "Hello1 from the child (shared mapping) !"), |
| RETVAL == 0); |
| munmap(shrd, 8192); |
| } |
| ls("/", 1, 1); |
| TEST_EXPECT_CONDITION(unlink("mmap.txt"), RETVAL == 0); |
| ls("/", 1, 1); |
| |
| /* Common use: shared file suppressed as soon as possible */ |
| TEST_EXPECT_CONDITION(fd = open("mmap.txt", O_RDWR | O_CREAT, |
| S_IRUSR | S_IWUSR), |
| RETVAL == 27); |
| TEST_EXPECT_CONDITION(unlink("mmap.txt"), RETVAL == 0); |
| if (fork() == 0) |
| { |
| char *shrd; |
| TEST_EXPECT_CONDITION(shrd = mmap(NULL, 4096, PROT_READ | PROT_WRITE, |
| MAP_SHARED, fd, 4096), |
| shrd != NULL); |
| nanosleep(1, 0); |
| strzcpy(shrd, "Hello2 from the child (shared mapping) !", 4096); |
| return 0; |
| } |
| else |
| { |
| char *shrd; |
| TEST_EXPECT_CONDITION(shrd = mmap(NULL, 4096, PROT_READ | PROT_WRITE, |
| MAP_SHARED, fd, 4096), |
| shrd != NULL); |
| strzcpy(shrd, "Garbage garbage garbage", 256); |
| nanosleep(2, 0); |
| bochs_printf("Father woken up\n"); |
| bochs_printf("Read string from child: '%s'\n", shrd); |
| TEST_EXPECT_CONDITION(strcmp(shrd, "Hello2 from the child (shared mapping) !"), |
| RETVAL == 0); |
| } |
| ls("/", 1, 1); |
| |
| /* Basic use */ |
| TEST_EXPECT_CONDITION(creat("mmap.txt", S_IRWXALL), RETVAL == 0); |
| if (fork() == 0) |
| { |
| char *shrd; |
| TEST_EXPECT_CONDITION(fd = open("mmap.txt", O_RDWR | O_CREAT, |
| S_IRUSR | S_IWUSR), |
| RETVAL == 28); |
| TEST_EXPECT_CONDITION(shrd = mmap(NULL, 4096, PROT_READ | PROT_WRITE, |
| MAP_SHARED, fd, 4096), |
| shrd != NULL); |
| nanosleep(1, 0); |
| strzcpy(shrd, "Hello3 from the child (shared mapping) !", 4096); |
| return 0; |
| } |
| else |
| { |
| char *shrd; |
| TEST_EXPECT_CONDITION(fd = open("mmap.txt", O_RDWR | O_CREAT, |
| S_IRUSR | S_IWUSR), |
| RETVAL == 28); |
| TEST_EXPECT_CONDITION(shrd = mmap(NULL, 4096, PROT_READ | PROT_WRITE, |
| MAP_SHARED, fd, 4096), |
| shrd != NULL); |
| strzcpy(shrd, "Garbage garbage garbage", 256); |
| nanosleep(2, 0); |
| bochs_printf("Father woken up\n"); |
| bochs_printf("Read string from child: '%s'\n", shrd); |
| TEST_EXPECT_CONDITION(strcmp(shrd, "Hello3 from the child (shared mapping) !"), |
| RETVAL == 0); |
| } |
| ls("/", 1, 1); |
| |
| bochs_printf("Bye from fstest\n"); |
| ls("/", 1, 1); |
| return 0; |
| } |
| |