/* * Copyright (c) 2008 Jakub Jermar * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @addtogroup vfs * @{ */ /** * @file vfs_node.c * @brief Various operations on VFS nodes have their home in this file. */ #include "vfs.h" #include #include #include #include #include #include #include #include #include /** Mutex protecting the VFS node hash table. */ FIBRIL_MUTEX_INITIALIZE(nodes_mutex); #define NODES_BUCKETS_LOG 8 #define NODES_BUCKETS (1 << NODES_BUCKETS_LOG) /** VFS node hash table containing all active, in-memory VFS nodes. */ hash_table_t nodes; #define KEY_FS_HANDLE 0 #define KEY_DEV_HANDLE 1 #define KEY_INDEX 2 static size_t nodes_key_hash(const void *); static size_t nodes_hash(const ht_link_t *); static bool nodes_key_equal(const void *, size_t, const ht_link_t *); static vfs_triplet_t node_triplet(vfs_node_t *node); /** VFS node hash table operations. */ const hash_table_ops_t nodes_ops = { .hash = nodes_hash, .key_hash = nodes_key_hash, .key_equal = nodes_key_equal, .equal = NULL, .remove_callback = NULL, }; /** Initialize the VFS node hash table. * * @return Return true on success, false on failure. */ bool vfs_nodes_init(void) { return hash_table_create(&nodes, 0, 0, &nodes_ops); } static inline void _vfs_node_addref(vfs_node_t *node) { node->refcnt++; } /** Increment reference count of a VFS node. * * @param node VFS node that will have its refcnt incremented. */ void vfs_node_addref(vfs_node_t *node) { fibril_mutex_lock(&nodes_mutex); _vfs_node_addref(node); fibril_mutex_unlock(&nodes_mutex); } /** Decrement reference count of a VFS node. * * This function handles the case when the reference count drops to zero. * * @param node VFS node that will have its refcnt decremented. */ void vfs_node_delref(vfs_node_t *node) { bool free_node = false; fibril_mutex_lock(&nodes_mutex); node->refcnt--; if (node->refcnt == 0) { /* * We are dropping the last reference to this node. * Remove it from the VFS node hash table. */ hash_table_remove_item(&nodes, &node->nh_link); free_node = true; } fibril_mutex_unlock(&nodes_mutex); if (free_node) { /* * VFS_OUT_DESTROY will free up the file's resources if there * are no more hard links. */ async_exch_t *exch = vfs_exchange_grab(node->fs_handle); async_msg_2(exch, VFS_OUT_DESTROY, (sysarg_t) node->service_id, (sysarg_t)node->index); vfs_exchange_release(exch); free(node); } } /** Forget node. * * This function will remove the node from the node hash table and deallocate * its memory, regardless of the node's reference count. * * @param node Node to be forgotten. */ void vfs_node_forget(vfs_node_t *node) { fibril_mutex_lock(&nodes_mutex); hash_table_remove_item(&nodes, &node->nh_link); fibril_mutex_unlock(&nodes_mutex); free(node); } /** Find VFS node. * * This function will try to lookup the given triplet in the VFS node hash * table. In case the triplet is not found there, a new VFS node is created. * In any case, the VFS node will have its reference count incremented. Every * node returned by this call should be eventually put back by calling * vfs_node_put() on it. * * @param result Populated lookup result structure. * * @return VFS node corresponding to the given triplet. */ vfs_node_t *vfs_node_get(vfs_lookup_res_t *result) { vfs_node_t *node; fibril_mutex_lock(&nodes_mutex); ht_link_t *tmp = hash_table_find(&nodes, &result->triplet); if (!tmp) { node = (vfs_node_t *) malloc(sizeof(vfs_node_t)); if (!node) { fibril_mutex_unlock(&nodes_mutex); return NULL; } memset(node, 0, sizeof(vfs_node_t)); node->fs_handle = result->triplet.fs_handle; node->service_id = result->triplet.service_id; node->index = result->triplet.index; node->size = result->size; node->type = result->type; fibril_rwlock_initialize(&node->contents_rwlock); hash_table_insert(&nodes, &node->nh_link); } else { node = hash_table_get_inst(tmp, vfs_node_t, nh_link); } _vfs_node_addref(node); fibril_mutex_unlock(&nodes_mutex); return node; } vfs_node_t *vfs_node_peek(vfs_lookup_res_t *result) { vfs_node_t *node = NULL; fibril_mutex_lock(&nodes_mutex); ht_link_t *tmp = hash_table_find(&nodes, &result->triplet); if (tmp) { node = hash_table_get_inst(tmp, vfs_node_t, nh_link); _vfs_node_addref(node); } fibril_mutex_unlock(&nodes_mutex); return node; } /** Return VFS node when no longer needed by the caller. * * This function will remove the reference on the VFS node created by * vfs_node_get(). This function can only be called as a closing bracket to the * preceding vfs_node_get() call. * * @param node VFS node being released. */ void vfs_node_put(vfs_node_t *node) { vfs_node_delref(node); } struct refcnt_data { /** Sum of all reference counts for this file system instance. */ unsigned refcnt; fs_handle_t fs_handle; service_id_t service_id; }; static bool refcnt_visitor(ht_link_t *item, void *arg) { vfs_node_t *node = hash_table_get_inst(item, vfs_node_t, nh_link); struct refcnt_data *rd = (void *) arg; if ((node->fs_handle == rd->fs_handle) && (node->service_id == rd->service_id)) rd->refcnt += node->refcnt; return true; } unsigned vfs_nodes_refcount_sum_get(fs_handle_t fs_handle, service_id_t service_id) { struct refcnt_data rd = { .refcnt = 0, .fs_handle = fs_handle, .service_id = service_id }; fibril_mutex_lock(&nodes_mutex); hash_table_apply(&nodes, refcnt_visitor, &rd); fibril_mutex_unlock(&nodes_mutex); return rd.refcnt; } /** Perform a remote node open operation. * * @return EOK on success or an error code from errno.h. * */ errno_t vfs_open_node_remote(vfs_node_t *node) { async_exch_t *exch = vfs_exchange_grab(node->fs_handle); ipc_call_t answer; aid_t req = async_send_2(exch, VFS_OUT_OPEN_NODE, (sysarg_t) node->service_id, (sysarg_t) node->index, &answer); vfs_exchange_release(exch); errno_t rc; async_wait_for(req, &rc); return rc; } static size_t nodes_key_hash(const void *key) { const vfs_triplet_t *tri = key; size_t hash = hash_combine(tri->fs_handle, tri->index); return hash_combine(hash, tri->service_id); } static size_t nodes_hash(const ht_link_t *item) { vfs_node_t *node = hash_table_get_inst(item, vfs_node_t, nh_link); vfs_triplet_t tri = node_triplet(node); return nodes_key_hash(&tri); } static bool nodes_key_equal(const void *key, size_t hash, const ht_link_t *item) { const vfs_triplet_t *tri = key; vfs_node_t *node = hash_table_get_inst(item, vfs_node_t, nh_link); return node->fs_handle == tri->fs_handle && node->service_id == tri->service_id && node->index == tri->index; } static inline vfs_triplet_t node_triplet(vfs_node_t *node) { vfs_triplet_t tri = { .fs_handle = node->fs_handle, .service_id = node->service_id, .index = node->index }; return tri; } bool vfs_node_has_children(vfs_node_t *node) { async_exch_t *exch = vfs_exchange_grab(node->fs_handle); errno_t rc = async_req_2_0(exch, VFS_OUT_IS_EMPTY, node->service_id, node->index); vfs_exchange_release(exch); return rc == ENOTEMPTY; } /** * @} */