source: mainline/uspace/srv/vfs/vfs_node.c@ 95658c9

ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 95658c9 was 5e801dc, checked in by GitHub <noreply@…>, 6 years ago

Indicate and enforce constness of hash table key in certain functions (#158)

The assumption here is that modifying key in the hash/equal functions in something completely unexpected, and not something you would ever want to do intentionally, so it makes sense to disallow it entirely to get that extra level of checking.

  • Property mode set to 100644
File size: 8.4 KB
Line 
1/*
2 * Copyright (c) 2008 Jakub Jermar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup vfs
30 * @{
31 */
32
33/**
34 * @file vfs_node.c
35 * @brief Various operations on VFS nodes have their home in this file.
36 */
37
38#include "vfs.h"
39#include <stdlib.h>
40#include <str.h>
41#include <fibril_synch.h>
42#include <adt/hash_table.h>
43#include <adt/hash.h>
44#include <assert.h>
45#include <async.h>
46#include <errno.h>
47#include <macros.h>
48
49/** Mutex protecting the VFS node hash table. */
50FIBRIL_MUTEX_INITIALIZE(nodes_mutex);
51
52#define NODES_BUCKETS_LOG 8
53#define NODES_BUCKETS (1 << NODES_BUCKETS_LOG)
54
55/** VFS node hash table containing all active, in-memory VFS nodes. */
56hash_table_t nodes;
57
58#define KEY_FS_HANDLE 0
59#define KEY_DEV_HANDLE 1
60#define KEY_INDEX 2
61
62static size_t nodes_key_hash(const void *);
63static size_t nodes_hash(const ht_link_t *);
64static bool nodes_key_equal(const void *, const ht_link_t *);
65static vfs_triplet_t node_triplet(vfs_node_t *node);
66
67/** VFS node hash table operations. */
68hash_table_ops_t nodes_ops = {
69 .hash = nodes_hash,
70 .key_hash = nodes_key_hash,
71 .key_equal = nodes_key_equal,
72 .equal = NULL,
73 .remove_callback = NULL,
74};
75
76/** Initialize the VFS node hash table.
77 *
78 * @return Return true on success, false on failure.
79 */
80bool vfs_nodes_init(void)
81{
82 return hash_table_create(&nodes, 0, 0, &nodes_ops);
83}
84
85static inline void _vfs_node_addref(vfs_node_t *node)
86{
87 node->refcnt++;
88}
89
90/** Increment reference count of a VFS node.
91 *
92 * @param node VFS node that will have its refcnt incremented.
93 */
94void vfs_node_addref(vfs_node_t *node)
95{
96 fibril_mutex_lock(&nodes_mutex);
97 _vfs_node_addref(node);
98 fibril_mutex_unlock(&nodes_mutex);
99}
100
101/** Decrement reference count of a VFS node.
102 *
103 * This function handles the case when the reference count drops to zero.
104 *
105 * @param node VFS node that will have its refcnt decremented.
106 */
107void vfs_node_delref(vfs_node_t *node)
108{
109 bool free_node = false;
110
111 fibril_mutex_lock(&nodes_mutex);
112
113 node->refcnt--;
114 if (node->refcnt == 0) {
115 /*
116 * We are dropping the last reference to this node.
117 * Remove it from the VFS node hash table.
118 */
119
120 hash_table_remove_item(&nodes, &node->nh_link);
121 free_node = true;
122 }
123
124 fibril_mutex_unlock(&nodes_mutex);
125
126 if (free_node) {
127 /*
128 * VFS_OUT_DESTROY will free up the file's resources if there
129 * are no more hard links.
130 */
131
132 async_exch_t *exch = vfs_exchange_grab(node->fs_handle);
133 async_msg_2(exch, VFS_OUT_DESTROY, (sysarg_t) node->service_id,
134 (sysarg_t)node->index);
135 vfs_exchange_release(exch);
136
137 free(node);
138 }
139}
140
141/** Forget node.
142 *
143 * This function will remove the node from the node hash table and deallocate
144 * its memory, regardless of the node's reference count.
145 *
146 * @param node Node to be forgotten.
147 */
148void vfs_node_forget(vfs_node_t *node)
149{
150 fibril_mutex_lock(&nodes_mutex);
151 hash_table_remove_item(&nodes, &node->nh_link);
152 fibril_mutex_unlock(&nodes_mutex);
153 free(node);
154}
155
156/** Find VFS node.
157 *
158 * This function will try to lookup the given triplet in the VFS node hash
159 * table. In case the triplet is not found there, a new VFS node is created.
160 * In any case, the VFS node will have its reference count incremented. Every
161 * node returned by this call should be eventually put back by calling
162 * vfs_node_put() on it.
163 *
164 * @param result Populated lookup result structure.
165 *
166 * @return VFS node corresponding to the given triplet.
167 */
168vfs_node_t *vfs_node_get(vfs_lookup_res_t *result)
169{
170 vfs_node_t *node;
171
172 fibril_mutex_lock(&nodes_mutex);
173 ht_link_t *tmp = hash_table_find(&nodes, &result->triplet);
174 if (!tmp) {
175 node = (vfs_node_t *) malloc(sizeof(vfs_node_t));
176 if (!node) {
177 fibril_mutex_unlock(&nodes_mutex);
178 return NULL;
179 }
180 memset(node, 0, sizeof(vfs_node_t));
181 node->fs_handle = result->triplet.fs_handle;
182 node->service_id = result->triplet.service_id;
183 node->index = result->triplet.index;
184 node->size = result->size;
185 node->type = result->type;
186 fibril_rwlock_initialize(&node->contents_rwlock);
187 hash_table_insert(&nodes, &node->nh_link);
188 } else {
189 node = hash_table_get_inst(tmp, vfs_node_t, nh_link);
190 }
191
192 _vfs_node_addref(node);
193 fibril_mutex_unlock(&nodes_mutex);
194
195 return node;
196}
197
198vfs_node_t *vfs_node_peek(vfs_lookup_res_t *result)
199{
200 vfs_node_t *node = NULL;
201
202 fibril_mutex_lock(&nodes_mutex);
203 ht_link_t *tmp = hash_table_find(&nodes, &result->triplet);
204 if (tmp) {
205 node = hash_table_get_inst(tmp, vfs_node_t, nh_link);
206 _vfs_node_addref(node);
207 }
208 fibril_mutex_unlock(&nodes_mutex);
209
210 return node;
211}
212
213/** Return VFS node when no longer needed by the caller.
214 *
215 * This function will remove the reference on the VFS node created by
216 * vfs_node_get(). This function can only be called as a closing bracket to the
217 * preceding vfs_node_get() call.
218 *
219 * @param node VFS node being released.
220 */
221void vfs_node_put(vfs_node_t *node)
222{
223 vfs_node_delref(node);
224}
225
226struct refcnt_data {
227 /** Sum of all reference counts for this file system instance. */
228 unsigned refcnt;
229 fs_handle_t fs_handle;
230 service_id_t service_id;
231};
232
233static bool refcnt_visitor(ht_link_t *item, void *arg)
234{
235 vfs_node_t *node = hash_table_get_inst(item, vfs_node_t, nh_link);
236 struct refcnt_data *rd = (void *) arg;
237
238 if ((node->fs_handle == rd->fs_handle) &&
239 (node->service_id == rd->service_id))
240 rd->refcnt += node->refcnt;
241
242 return true;
243}
244
245unsigned
246vfs_nodes_refcount_sum_get(fs_handle_t fs_handle, service_id_t service_id)
247{
248 struct refcnt_data rd = {
249 .refcnt = 0,
250 .fs_handle = fs_handle,
251 .service_id = service_id
252 };
253
254 fibril_mutex_lock(&nodes_mutex);
255 hash_table_apply(&nodes, refcnt_visitor, &rd);
256 fibril_mutex_unlock(&nodes_mutex);
257
258 return rd.refcnt;
259}
260
261/** Perform a remote node open operation.
262 *
263 * @return EOK on success or an error code from errno.h.
264 *
265 */
266errno_t vfs_open_node_remote(vfs_node_t *node)
267{
268 async_exch_t *exch = vfs_exchange_grab(node->fs_handle);
269
270 ipc_call_t answer;
271 aid_t req = async_send_2(exch, VFS_OUT_OPEN_NODE,
272 (sysarg_t) node->service_id, (sysarg_t) node->index, &answer);
273
274 vfs_exchange_release(exch);
275
276 errno_t rc;
277 async_wait_for(req, &rc);
278
279 return rc;
280}
281
282static size_t nodes_key_hash(const void *key)
283{
284 const vfs_triplet_t *tri = key;
285 size_t hash = hash_combine(tri->fs_handle, tri->index);
286 return hash_combine(hash, tri->service_id);
287}
288
289static size_t nodes_hash(const ht_link_t *item)
290{
291 vfs_node_t *node = hash_table_get_inst(item, vfs_node_t, nh_link);
292 vfs_triplet_t tri = node_triplet(node);
293 return nodes_key_hash(&tri);
294}
295
296static bool nodes_key_equal(const void *key, const ht_link_t *item)
297{
298 const vfs_triplet_t *tri = key;
299 vfs_node_t *node = hash_table_get_inst(item, vfs_node_t, nh_link);
300 return node->fs_handle == tri->fs_handle &&
301 node->service_id == tri->service_id && node->index == tri->index;
302}
303
304static inline vfs_triplet_t node_triplet(vfs_node_t *node)
305{
306 vfs_triplet_t tri = {
307 .fs_handle = node->fs_handle,
308 .service_id = node->service_id,
309 .index = node->index
310 };
311
312 return tri;
313}
314
315bool vfs_node_has_children(vfs_node_t *node)
316{
317 async_exch_t *exch = vfs_exchange_grab(node->fs_handle);
318 errno_t rc = async_req_2_0(exch, VFS_OUT_IS_EMPTY, node->service_id,
319 node->index);
320 vfs_exchange_release(exch);
321 return rc == ENOTEMPTY;
322}
323
324/**
325 * @}
326 */
Note: See TracBrowser for help on using the repository browser.