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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since eadaeae8 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • Property mode set to 100644
File size: 15.2 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/** 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(void *k)
150{
151 node_key_t *key = (node_key_t *)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(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 node_key_t *key = (node_key_t *)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 tmpfs_node_initialize(nodep);
311 nodep->bp = malloc(sizeof(fs_node_t));
312 if (!nodep->bp) {
313 free(nodep);
314 return ENOMEM;
315 }
316 fs_node_initialize(nodep->bp);
317 nodep->bp->data = nodep; /* link the FS and TMPFS nodes */
318
319 rc = tmpfs_root_get(&rootfn, service_id);
320 assert(rc == EOK);
321 if (!rootfn)
322 nodep->index = TMPFS_SOME_ROOT;
323 else
324 nodep->index = tmpfs_next_index++;
325 nodep->service_id = service_id;
326 if (lflag & L_DIRECTORY)
327 nodep->type = TMPFS_DIRECTORY;
328 else
329 nodep->type = TMPFS_FILE;
330
331 /* Insert the new node into the nodes hash table. */
332 hash_table_insert(&nodes, &nodep->nh_link);
333 *rfn = FS_NODE(nodep);
334 return EOK;
335}
336
337errno_t tmpfs_destroy_node(fs_node_t *fn)
338{
339 tmpfs_node_t *nodep = TMPFS_NODE(fn);
340
341 assert(!nodep->lnkcnt);
342 assert(list_empty(&nodep->cs_list));
343
344 hash_table_remove_item(&nodes, &nodep->nh_link);
345
346 /*
347 * The nodes_remove_callback() function takes care of the actual
348 * resource deallocation.
349 */
350 return EOK;
351}
352
353errno_t tmpfs_link_node(fs_node_t *pfn, fs_node_t *cfn, const char *nm)
354{
355 tmpfs_node_t *parentp = TMPFS_NODE(pfn);
356 tmpfs_node_t *childp = TMPFS_NODE(cfn);
357 tmpfs_dentry_t *dentryp;
358
359 assert(parentp->type == TMPFS_DIRECTORY);
360
361 /* Check for duplicit entries. */
362 list_foreach(parentp->cs_list, link, tmpfs_dentry_t, dp) {
363 if (!str_cmp(dp->name, nm))
364 return EEXIST;
365 }
366
367 /* Allocate and initialize the dentry. */
368 dentryp = malloc(sizeof(tmpfs_dentry_t));
369 if (!dentryp)
370 return ENOMEM;
371 tmpfs_dentry_initialize(dentryp);
372
373 /* Populate and link the new dentry. */
374 size_t size = str_size(nm);
375 dentryp->name = malloc(size + 1);
376 if (!dentryp->name) {
377 free(dentryp);
378 return ENOMEM;
379 }
380 str_cpy(dentryp->name, size + 1, nm);
381 dentryp->node = childp;
382 childp->lnkcnt++;
383 list_append(&dentryp->link, &parentp->cs_list);
384
385 return EOK;
386}
387
388errno_t tmpfs_unlink_node(fs_node_t *pfn, fs_node_t *cfn, const char *nm)
389{
390 tmpfs_node_t *parentp = TMPFS_NODE(pfn);
391 tmpfs_node_t *childp = NULL;
392 tmpfs_dentry_t *dentryp;
393
394 if (!parentp)
395 return EBUSY;
396
397 list_foreach(parentp->cs_list, link, tmpfs_dentry_t, dp) {
398 if (!str_cmp(dp->name, nm)) {
399 dentryp = dp;
400 childp = dentryp->node;
401 assert(FS_NODE(childp) == cfn);
402 break;
403 }
404 }
405
406 if (!childp)
407 return ENOENT;
408
409 if ((childp->lnkcnt == 1) && !list_empty(&childp->cs_list))
410 return ENOTEMPTY;
411
412 list_remove(&dentryp->link);
413 free(dentryp);
414 childp->lnkcnt--;
415
416 return EOK;
417}
418
419/*
420 * Implementation of the VFS_OUT interface.
421 */
422
423static errno_t tmpfs_fsprobe(service_id_t service_id, vfs_fs_probe_info_t *info)
424{
425 return ENOTSUP;
426}
427
428static errno_t
429tmpfs_mounted(service_id_t service_id, const char *opts, fs_index_t *index,
430 aoff64_t *size)
431{
432 fs_node_t *rootfn;
433 errno_t rc;
434
435 /* Check if this device is not already mounted. */
436 rc = tmpfs_root_get(&rootfn, service_id);
437 if ((rc == EOK) && (rootfn)) {
438 (void) tmpfs_node_put(rootfn);
439 return EEXIST;
440 }
441
442 /* Initialize TMPFS instance. */
443 if (!tmpfs_instance_init(service_id))
444 return ENOMEM;
445
446 rc = tmpfs_root_get(&rootfn, service_id);
447 assert(rc == EOK);
448 tmpfs_node_t *rootp = TMPFS_NODE(rootfn);
449 if (str_cmp(opts, "restore") == 0) {
450 if (!tmpfs_restore(service_id))
451 return ELIMIT;
452 }
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_callid_t callid;
487 size_t size;
488 if (!async_data_read_receive(&callid, &size)) {
489 async_answer_0(callid, 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(callid, 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(callid, ENOENT);
513 return ENOENT;
514 }
515
516 dentryp = list_get_instance(lnk, tmpfs_dentry_t, link);
517
518 (void) async_data_read_finalize(callid, 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_callid_t callid;
550 size_t size;
551 if (!async_data_write_receive(&callid, &size)) {
552 async_answer_0(callid, 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(callid, 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(callid, 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(callid, 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
604 if (!hlp)
605 return ENOENT;
606 tmpfs_node_t *nodep = hash_table_get_inst(hlp, tmpfs_node_t, nh_link);
607
608 if (size == nodep->size)
609 return EOK;
610
611 if (size > SIZE_MAX)
612 return ENOMEM;
613
614 void *newdata = realloc(nodep->data, size);
615 if (!newdata)
616 return ENOMEM;
617
618 if (size > nodep->size) {
619 size_t delta = size - nodep->size;
620 memset(newdata + nodep->size, 0, delta);
621 }
622
623 nodep->size = size;
624 nodep->data = newdata;
625 return EOK;
626}
627
628static errno_t tmpfs_close(service_id_t service_id, fs_index_t index)
629{
630 return EOK;
631}
632
633static errno_t tmpfs_destroy(service_id_t service_id, fs_index_t index)
634{
635 node_key_t key = {
636 .service_id = service_id,
637 .index = index
638 };
639
640 ht_link_t *hlp = hash_table_find(&nodes, &key);
641 if (!hlp)
642 return ENOENT;
643 tmpfs_node_t *nodep = hash_table_get_inst(hlp, tmpfs_node_t,
644 nh_link);
645 return tmpfs_destroy_node(FS_NODE(nodep));
646}
647
648static errno_t tmpfs_sync(service_id_t service_id, fs_index_t index)
649{
650 /*
651 * TMPFS keeps its data structures always consistent,
652 * thus the sync operation is a no-op.
653 */
654 return EOK;
655}
656
657vfs_out_ops_t tmpfs_ops = {
658 .fsprobe = tmpfs_fsprobe,
659 .mounted = tmpfs_mounted,
660 .unmounted = tmpfs_unmounted,
661 .read = tmpfs_read,
662 .write = tmpfs_write,
663 .truncate = tmpfs_truncate,
664 .close = tmpfs_close,
665 .destroy = tmpfs_destroy,
666 .sync = tmpfs_sync,
667};
668
669/**
670 * @}
671 */
672
Note: See TracBrowser for help on using the repository browser.