source: mainline/uspace/srv/fs/tmpfs/tmpfs_ops.c@ 5e801dc

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