source: mainline/uspace/srv/fs/tmpfs/tmpfs_ops.c@ 0ca7286

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 0ca7286 was 0ca7286, checked in by Adam Hraska <adam.hraska+hos@…>, 13 years ago

Added resizing to user space (single-threaded) hash_table. Resizes in a way to mitigate effects of bad hash functions. Change of interface affected many files.

  • Property mode set to 100644
File size: 15.8 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 fs
30 * @{
31 */
32
33/**
34 * @file tmpfs_ops.c
35 * @brief Implementation of VFS operations for the TMPFS file system
36 * server.
37 */
38
39#include "tmpfs.h"
40#include "../../vfs/vfs.h"
41#include <macros.h>
42#include <stdint.h>
43#include <async.h>
44#include <errno.h>
45#include <atomic.h>
46#include <stdlib.h>
47#include <str.h>
48#include <stdio.h>
49#include <assert.h>
50#include <sys/types.h>
51#include <adt/hash_table.h>
52#include <as.h>
53#include <libfs.h>
54
55#define min(a, b) ((a) < (b) ? (a) : (b))
56#define max(a, b) ((a) > (b) ? (a) : (b))
57
58/** All root nodes have index 0. */
59#define TMPFS_SOME_ROOT 0
60/** Global counter for assigning node indices. Shared by all instances. */
61fs_index_t tmpfs_next_index = 1;
62
63/*
64 * Implementation of the libfs interface.
65 */
66
67/* Forward declarations of static functions. */
68static int tmpfs_match(fs_node_t **, fs_node_t *, const char *);
69static int tmpfs_node_get(fs_node_t **, service_id_t, fs_index_t);
70static int tmpfs_node_open(fs_node_t *);
71static int tmpfs_node_put(fs_node_t *);
72static int tmpfs_create_node(fs_node_t **, service_id_t, int);
73static int tmpfs_destroy_node(fs_node_t *);
74static int tmpfs_link_node(fs_node_t *, fs_node_t *, const char *);
75static int tmpfs_unlink_node(fs_node_t *, fs_node_t *, const char *);
76
77/* Implementation of helper functions. */
78static int tmpfs_root_get(fs_node_t **rfn, service_id_t service_id)
79{
80 return tmpfs_node_get(rfn, service_id, TMPFS_SOME_ROOT);
81}
82
83static int tmpfs_has_children(bool *has_children, fs_node_t *fn)
84{
85 *has_children = !list_empty(&TMPFS_NODE(fn)->cs_list);
86 return EOK;
87}
88
89static fs_index_t tmpfs_index_get(fs_node_t *fn)
90{
91 return TMPFS_NODE(fn)->index;
92}
93
94static aoff64_t tmpfs_size_get(fs_node_t *fn)
95{
96 return TMPFS_NODE(fn)->size;
97}
98
99static unsigned tmpfs_lnkcnt_get(fs_node_t *fn)
100{
101 return TMPFS_NODE(fn)->lnkcnt;
102}
103
104static bool tmpfs_is_directory(fs_node_t *fn)
105{
106 return TMPFS_NODE(fn)->type == TMPFS_DIRECTORY;
107}
108
109static bool tmpfs_is_file(fs_node_t *fn)
110{
111 return TMPFS_NODE(fn)->type == TMPFS_FILE;
112}
113
114static service_id_t tmpfs_service_get(fs_node_t *fn)
115{
116 return 0;
117}
118
119/** libfs operations */
120libfs_ops_t tmpfs_libfs_ops = {
121 .root_get = tmpfs_root_get,
122 .match = tmpfs_match,
123 .node_get = tmpfs_node_get,
124 .node_open = tmpfs_node_open,
125 .node_put = tmpfs_node_put,
126 .create = tmpfs_create_node,
127 .destroy = tmpfs_destroy_node,
128 .link = tmpfs_link_node,
129 .unlink = tmpfs_unlink_node,
130 .has_children = tmpfs_has_children,
131 .index_get = tmpfs_index_get,
132 .size_get = tmpfs_size_get,
133 .lnkcnt_get = tmpfs_lnkcnt_get,
134 .is_directory = tmpfs_is_directory,
135 .is_file = tmpfs_is_file,
136 .service_get = tmpfs_service_get
137};
138
139/** Hash table of all TMPFS nodes. */
140hash_table_t nodes;
141
142#define NODES_KEY_DEV 0
143#define NODES_KEY_INDEX 1
144
145/* Implementation of hash table interface for the nodes hash table. */
146static size_t nodes_key_hash(unsigned long key[])
147{
148 /* Based on Effective Java, 2nd Edition. */
149 size_t hash = 17;
150 hash = 37 * hash + key[NODES_KEY_DEV];
151 hash = 37 * hash + key[NODES_KEY_INDEX];
152 return hash;
153}
154
155static size_t nodes_hash(const link_t *item)
156{
157 tmpfs_node_t *nodep = hash_table_get_instance(item, tmpfs_node_t, nh_link);
158
159 unsigned long key[] = {
160 [NODES_KEY_DEV] = nodep->service_id,
161 [NODES_KEY_INDEX] = nodep->index
162 };
163
164 return nodes_key_hash(key);
165}
166
167static bool nodes_match(unsigned long key[], size_t keys, const link_t *item)
168{
169 tmpfs_node_t *nodep = hash_table_get_instance(item, tmpfs_node_t,
170 nh_link);
171
172 switch (keys) {
173 case 1:
174 return (nodep->service_id == key[NODES_KEY_DEV]);
175 case 2:
176 return ((nodep->service_id == key[NODES_KEY_DEV]) &&
177 (nodep->index == key[NODES_KEY_INDEX]));
178 default:
179 assert((keys == 1) || (keys == 2));
180 }
181
182 return 0;
183}
184
185static void nodes_remove_callback(link_t *item)
186{
187 tmpfs_node_t *nodep = hash_table_get_instance(item, tmpfs_node_t,
188 nh_link);
189
190 while (!list_empty(&nodep->cs_list)) {
191 tmpfs_dentry_t *dentryp = list_get_instance(
192 list_first(&nodep->cs_list), tmpfs_dentry_t, link);
193
194 assert(nodep->type == TMPFS_DIRECTORY);
195 list_remove(&dentryp->link);
196 free(dentryp);
197 }
198
199 if (nodep->data) {
200 assert(nodep->type == TMPFS_FILE);
201 free(nodep->data);
202 }
203 free(nodep->bp);
204 free(nodep);
205}
206
207/** TMPFS nodes hash table operations. */
208hash_table_ops_t nodes_ops = {
209 .hash = nodes_hash,
210 .key_hash = nodes_key_hash,
211 .match = nodes_match,
212 .equal = 0,
213 .remove_callback = nodes_remove_callback
214};
215
216static void tmpfs_node_initialize(tmpfs_node_t *nodep)
217{
218 nodep->bp = NULL;
219 nodep->index = 0;
220 nodep->service_id = 0;
221 nodep->type = TMPFS_NONE;
222 nodep->lnkcnt = 0;
223 nodep->size = 0;
224 nodep->data = NULL;
225 link_initialize(&nodep->nh_link);
226 list_initialize(&nodep->cs_list);
227}
228
229static void tmpfs_dentry_initialize(tmpfs_dentry_t *dentryp)
230{
231 link_initialize(&dentryp->link);
232 dentryp->name = NULL;
233 dentryp->node = NULL;
234}
235
236bool tmpfs_init(void)
237{
238 if (!hash_table_create(&nodes, 0, 2, &nodes_ops))
239 return false;
240
241 return true;
242}
243
244static bool tmpfs_instance_init(service_id_t service_id)
245{
246 fs_node_t *rfn;
247 int rc;
248
249 rc = tmpfs_create_node(&rfn, service_id, L_DIRECTORY);
250 if (rc != EOK || !rfn)
251 return false;
252 TMPFS_NODE(rfn)->lnkcnt = 0; /* FS root is not linked */
253 return true;
254}
255
256static void tmpfs_instance_done(service_id_t service_id)
257{
258 unsigned long key[] = {
259 [NODES_KEY_DEV] = service_id
260 };
261 /*
262 * Here we are making use of one special feature of our hash table
263 * implementation, which allows to remove more items based on a partial
264 * key match. In the following, we are going to remove all nodes
265 * matching our device handle. The nodes_remove_callback() function will
266 * take care of resource deallocation.
267 */
268 hash_table_remove(&nodes, key, 1);
269}
270
271int tmpfs_match(fs_node_t **rfn, fs_node_t *pfn, const char *component)
272{
273 tmpfs_node_t *parentp = TMPFS_NODE(pfn);
274
275 list_foreach(parentp->cs_list, lnk) {
276 tmpfs_dentry_t *dentryp;
277 dentryp = list_get_instance(lnk, tmpfs_dentry_t, link);
278 if (!str_cmp(dentryp->name, component)) {
279 *rfn = FS_NODE(dentryp->node);
280 return EOK;
281 }
282 }
283
284 *rfn = NULL;
285 return EOK;
286}
287
288int tmpfs_node_get(fs_node_t **rfn, service_id_t service_id, fs_index_t index)
289{
290 unsigned long key[] = {
291 [NODES_KEY_DEV] = service_id,
292 [NODES_KEY_INDEX] = index
293 };
294 link_t *lnk = hash_table_find(&nodes, key);
295 if (lnk) {
296 tmpfs_node_t *nodep;
297 nodep = hash_table_get_instance(lnk, tmpfs_node_t, nh_link);
298 *rfn = FS_NODE(nodep);
299 } else {
300 *rfn = NULL;
301 }
302 return EOK;
303}
304
305int tmpfs_node_open(fs_node_t *fn)
306{
307 /* nothing to do */
308 return EOK;
309}
310
311int tmpfs_node_put(fs_node_t *fn)
312{
313 /* nothing to do */
314 return EOK;
315}
316
317int tmpfs_create_node(fs_node_t **rfn, service_id_t service_id, int lflag)
318{
319 fs_node_t *rootfn;
320 int rc;
321
322 assert((lflag & L_FILE) ^ (lflag & L_DIRECTORY));
323
324 tmpfs_node_t *nodep = malloc(sizeof(tmpfs_node_t));
325 if (!nodep)
326 return ENOMEM;
327 tmpfs_node_initialize(nodep);
328 nodep->bp = malloc(sizeof(fs_node_t));
329 if (!nodep->bp) {
330 free(nodep);
331 return ENOMEM;
332 }
333 fs_node_initialize(nodep->bp);
334 nodep->bp->data = nodep; /* link the FS and TMPFS nodes */
335
336 rc = tmpfs_root_get(&rootfn, service_id);
337 assert(rc == EOK);
338 if (!rootfn)
339 nodep->index = TMPFS_SOME_ROOT;
340 else
341 nodep->index = tmpfs_next_index++;
342 nodep->service_id = service_id;
343 if (lflag & L_DIRECTORY)
344 nodep->type = TMPFS_DIRECTORY;
345 else
346 nodep->type = TMPFS_FILE;
347
348 /* Insert the new node into the nodes hash table. */
349 hash_table_insert(&nodes, &nodep->nh_link);
350 *rfn = FS_NODE(nodep);
351 return EOK;
352}
353
354int tmpfs_destroy_node(fs_node_t *fn)
355{
356 tmpfs_node_t *nodep = TMPFS_NODE(fn);
357
358 assert(!nodep->lnkcnt);
359 assert(list_empty(&nodep->cs_list));
360
361 unsigned long key[] = {
362 [NODES_KEY_DEV] = nodep->service_id,
363 [NODES_KEY_INDEX] = nodep->index
364 };
365 hash_table_remove(&nodes, key, 2);
366
367 /*
368 * The nodes_remove_callback() function takes care of the actual
369 * resource deallocation.
370 */
371 return EOK;
372}
373
374int tmpfs_link_node(fs_node_t *pfn, fs_node_t *cfn, const char *nm)
375{
376 tmpfs_node_t *parentp = TMPFS_NODE(pfn);
377 tmpfs_node_t *childp = TMPFS_NODE(cfn);
378 tmpfs_dentry_t *dentryp;
379
380 assert(parentp->type == TMPFS_DIRECTORY);
381
382 /* Check for duplicit entries. */
383 list_foreach(parentp->cs_list, lnk) {
384 dentryp = list_get_instance(lnk, tmpfs_dentry_t, link);
385 if (!str_cmp(dentryp->name, nm))
386 return EEXIST;
387 }
388
389 /* Allocate and initialize the dentry. */
390 dentryp = malloc(sizeof(tmpfs_dentry_t));
391 if (!dentryp)
392 return ENOMEM;
393 tmpfs_dentry_initialize(dentryp);
394
395 /* Populate and link the new dentry. */
396 size_t size = str_size(nm);
397 dentryp->name = malloc(size + 1);
398 if (!dentryp->name) {
399 free(dentryp);
400 return ENOMEM;
401 }
402 str_cpy(dentryp->name, size + 1, nm);
403 dentryp->node = childp;
404 childp->lnkcnt++;
405 list_append(&dentryp->link, &parentp->cs_list);
406
407 return EOK;
408}
409
410int tmpfs_unlink_node(fs_node_t *pfn, fs_node_t *cfn, const char *nm)
411{
412 tmpfs_node_t *parentp = TMPFS_NODE(pfn);
413 tmpfs_node_t *childp = NULL;
414 tmpfs_dentry_t *dentryp;
415
416 if (!parentp)
417 return EBUSY;
418
419 list_foreach(parentp->cs_list, lnk) {
420 dentryp = list_get_instance(lnk, tmpfs_dentry_t, link);
421 if (!str_cmp(dentryp->name, nm)) {
422 childp = dentryp->node;
423 assert(FS_NODE(childp) == cfn);
424 break;
425 }
426 }
427
428 if (!childp)
429 return ENOENT;
430
431 if ((childp->lnkcnt == 1) && !list_empty(&childp->cs_list))
432 return ENOTEMPTY;
433
434 list_remove(&dentryp->link);
435 free(dentryp);
436 childp->lnkcnt--;
437
438 return EOK;
439}
440
441/*
442 * Implementation of the VFS_OUT interface.
443 */
444
445static int
446tmpfs_mounted(service_id_t service_id, const char *opts,
447 fs_index_t *index, aoff64_t *size, unsigned *lnkcnt)
448{
449 fs_node_t *rootfn;
450 int rc;
451
452 /* Check if this device is not already mounted. */
453 rc = tmpfs_root_get(&rootfn, service_id);
454 if ((rc == EOK) && (rootfn)) {
455 (void) tmpfs_node_put(rootfn);
456 return EEXIST;
457 }
458
459 /* Initialize TMPFS instance. */
460 if (!tmpfs_instance_init(service_id))
461 return ENOMEM;
462
463 rc = tmpfs_root_get(&rootfn, service_id);
464 assert(rc == EOK);
465 tmpfs_node_t *rootp = TMPFS_NODE(rootfn);
466 if (str_cmp(opts, "restore") == 0) {
467 if (!tmpfs_restore(service_id))
468 return ELIMIT;
469 }
470
471 *index = rootp->index;
472 *size = rootp->size;
473 *lnkcnt = rootp->lnkcnt;
474
475 return EOK;
476}
477
478static int tmpfs_unmounted(service_id_t service_id)
479{
480 tmpfs_instance_done(service_id);
481 return EOK;
482}
483
484static int tmpfs_read(service_id_t service_id, fs_index_t index, aoff64_t pos,
485 size_t *rbytes)
486{
487 /*
488 * Lookup the respective TMPFS node.
489 */
490 link_t *hlp;
491 unsigned long key[] = {
492 [NODES_KEY_DEV] = service_id,
493 [NODES_KEY_INDEX] = index
494 };
495 hlp = hash_table_find(&nodes, key);
496 if (!hlp)
497 return ENOENT;
498 tmpfs_node_t *nodep = hash_table_get_instance(hlp, tmpfs_node_t,
499 nh_link);
500
501 /*
502 * Receive the read request.
503 */
504 ipc_callid_t callid;
505 size_t size;
506 if (!async_data_read_receive(&callid, &size)) {
507 async_answer_0(callid, EINVAL);
508 return EINVAL;
509 }
510
511 size_t bytes;
512 if (nodep->type == TMPFS_FILE) {
513 bytes = min(nodep->size - pos, size);
514 (void) async_data_read_finalize(callid, nodep->data + pos,
515 bytes);
516 } else {
517 tmpfs_dentry_t *dentryp;
518 link_t *lnk;
519
520 assert(nodep->type == TMPFS_DIRECTORY);
521
522 /*
523 * Yes, we really use O(n) algorithm here.
524 * If it bothers someone, it could be fixed by introducing a
525 * hash table.
526 */
527 lnk = list_nth(&nodep->cs_list, pos);
528
529 if (lnk == NULL) {
530 async_answer_0(callid, ENOENT);
531 return ENOENT;
532 }
533
534 dentryp = list_get_instance(lnk, tmpfs_dentry_t, link);
535
536 (void) async_data_read_finalize(callid, dentryp->name,
537 str_size(dentryp->name) + 1);
538 bytes = 1;
539 }
540
541 *rbytes = bytes;
542 return EOK;
543}
544
545static int
546tmpfs_write(service_id_t service_id, fs_index_t index, aoff64_t pos,
547 size_t *wbytes, aoff64_t *nsize)
548{
549 /*
550 * Lookup the respective TMPFS node.
551 */
552 link_t *hlp;
553 unsigned long key[] = {
554 [NODES_KEY_DEV] = service_id,
555 [NODES_KEY_INDEX] = index
556 };
557 hlp = hash_table_find(&nodes, key);
558 if (!hlp)
559 return ENOENT;
560 tmpfs_node_t *nodep = hash_table_get_instance(hlp, tmpfs_node_t,
561 nh_link);
562
563 /*
564 * Receive the write request.
565 */
566 ipc_callid_t callid;
567 size_t size;
568 if (!async_data_write_receive(&callid, &size)) {
569 async_answer_0(callid, EINVAL);
570 return EINVAL;
571 }
572
573 /*
574 * Check whether the file needs to grow.
575 */
576 if (pos + size <= nodep->size) {
577 /* The file size is not changing. */
578 (void) async_data_write_finalize(callid, nodep->data + pos,
579 size);
580 goto out;
581 }
582 size_t delta = (pos + size) - nodep->size;
583 /*
584 * At this point, we are deliberately extremely straightforward and
585 * simply realloc the contents of the file on every write that grows the
586 * file. In the end, the situation might not be as bad as it may look:
587 * our heap allocator can save us and just grow the block whenever
588 * possible.
589 */
590 void *newdata = realloc(nodep->data, nodep->size + delta);
591 if (!newdata) {
592 async_answer_0(callid, ENOMEM);
593 size = 0;
594 goto out;
595 }
596 /* Clear any newly allocated memory in order to emulate gaps. */
597 memset(newdata + nodep->size, 0, delta);
598 nodep->size += delta;
599 nodep->data = newdata;
600 (void) async_data_write_finalize(callid, nodep->data + pos, size);
601
602out:
603 *wbytes = size;
604 *nsize = nodep->size;
605 return EOK;
606}
607
608static int tmpfs_truncate(service_id_t service_id, fs_index_t index,
609 aoff64_t size)
610{
611 /*
612 * Lookup the respective TMPFS node.
613 */
614 unsigned long key[] = {
615 [NODES_KEY_DEV] = service_id,
616 [NODES_KEY_INDEX] = index
617 };
618 link_t *hlp = hash_table_find(&nodes, key);
619 if (!hlp)
620 return ENOENT;
621 tmpfs_node_t *nodep = hash_table_get_instance(hlp, tmpfs_node_t, nh_link);
622
623 if (size == nodep->size)
624 return EOK;
625
626 if (size > SIZE_MAX)
627 return ENOMEM;
628
629 void *newdata = realloc(nodep->data, size);
630 if (!newdata)
631 return ENOMEM;
632
633 if (size > nodep->size) {
634 size_t delta = size - nodep->size;
635 memset(newdata + nodep->size, 0, delta);
636 }
637
638 nodep->size = size;
639 nodep->data = newdata;
640 return EOK;
641}
642
643static int tmpfs_close(service_id_t service_id, fs_index_t index)
644{
645 return EOK;
646}
647
648static int tmpfs_destroy(service_id_t service_id, fs_index_t index)
649{
650 link_t *hlp;
651 unsigned long key[] = {
652 [NODES_KEY_DEV] = service_id,
653 [NODES_KEY_INDEX] = index
654 };
655 hlp = hash_table_find(&nodes, key);
656 if (!hlp)
657 return ENOENT;
658 tmpfs_node_t *nodep = hash_table_get_instance(hlp, tmpfs_node_t,
659 nh_link);
660 return tmpfs_destroy_node(FS_NODE(nodep));
661}
662
663static int tmpfs_sync(service_id_t service_id, fs_index_t index)
664{
665 /*
666 * TMPFS keeps its data structures always consistent,
667 * thus the sync operation is a no-op.
668 */
669 return EOK;
670}
671
672vfs_out_ops_t tmpfs_ops = {
673 .mounted = tmpfs_mounted,
674 .unmounted = tmpfs_unmounted,
675 .read = tmpfs_read,
676 .write = tmpfs_write,
677 .truncate = tmpfs_truncate,
678 .close = tmpfs_close,
679 .destroy = tmpfs_destroy,
680 .sync = tmpfs_sync,
681};
682
683/**
684 * @}
685 */
686
Note: See TracBrowser for help on using the repository browser.