source: mainline/uspace/srv/fs/tmpfs/tmpfs_ops.c@ 63d46341

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 63d46341 was 984a9ba, checked in by Martin Decky <martin@…>, 7 years ago

do not expose the call capability handler from the async framework

Keep the call capability handler encapsulated within the async framework
and do not expose it explicitly via its API. Use the pointer to
ipc_call_t as the sole object identifying an IPC call in the code that
uses the async framework.

This plugs a major leak in the abstraction and also simplifies both the
async framework (slightly) and all IPC servers.

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