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

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

use a TAR as the archive format for populating TMPFS root file system

Also remove the logic of populating a TMPFS file system from the TMPFS
file system driver. A more elegant separation of concerns is to populate
the file system from the client. This is now done by the init task (if
required) and should work universally for any file system.

  • 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/** 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
450 *index = rootp->index;
451 *size = rootp->size;
452
453 return EOK;
454}
455
456static errno_t tmpfs_unmounted(service_id_t service_id)
457{
458 tmpfs_instance_done(service_id);
459 return EOK;
460}
461
462static errno_t tmpfs_read(service_id_t service_id, fs_index_t index, aoff64_t pos,
463 size_t *rbytes)
464{
465 /*
466 * Lookup the respective TMPFS node.
467 */
468 node_key_t key = {
469 .service_id = service_id,
470 .index = index
471 };
472
473 ht_link_t *hlp = hash_table_find(&nodes, &key);
474 if (!hlp)
475 return ENOENT;
476
477 tmpfs_node_t *nodep = hash_table_get_inst(hlp, tmpfs_node_t, nh_link);
478
479 /*
480 * Receive the read request.
481 */
482 cap_call_handle_t chandle;
483 size_t size;
484 if (!async_data_read_receive(&chandle, &size)) {
485 async_answer_0(chandle, EINVAL);
486 return EINVAL;
487 }
488
489 size_t bytes;
490 if (nodep->type == TMPFS_FILE) {
491 bytes = min(nodep->size - pos, size);
492 (void) async_data_read_finalize(chandle, nodep->data + pos,
493 bytes);
494 } else {
495 tmpfs_dentry_t *dentryp;
496 link_t *lnk;
497
498 assert(nodep->type == TMPFS_DIRECTORY);
499
500 /*
501 * Yes, we really use O(n) algorithm here.
502 * If it bothers someone, it could be fixed by introducing a
503 * hash table.
504 */
505 lnk = list_nth(&nodep->cs_list, pos);
506
507 if (lnk == NULL) {
508 async_answer_0(chandle, ENOENT);
509 return ENOENT;
510 }
511
512 dentryp = list_get_instance(lnk, tmpfs_dentry_t, link);
513
514 (void) async_data_read_finalize(chandle, dentryp->name,
515 str_size(dentryp->name) + 1);
516 bytes = 1;
517 }
518
519 *rbytes = bytes;
520 return EOK;
521}
522
523static errno_t
524tmpfs_write(service_id_t service_id, fs_index_t index, aoff64_t pos,
525 size_t *wbytes, aoff64_t *nsize)
526{
527 /*
528 * Lookup the respective TMPFS node.
529 */
530 node_key_t key = {
531 .service_id = service_id,
532 .index = index
533 };
534
535 ht_link_t *hlp = hash_table_find(&nodes, &key);
536
537 if (!hlp)
538 return ENOENT;
539
540 tmpfs_node_t *nodep = hash_table_get_inst(hlp, tmpfs_node_t, nh_link);
541
542 /*
543 * Receive the write request.
544 */
545 cap_call_handle_t chandle;
546 size_t size;
547 if (!async_data_write_receive(&chandle, &size)) {
548 async_answer_0(chandle, EINVAL);
549 return EINVAL;
550 }
551
552 /*
553 * Check whether the file needs to grow.
554 */
555 if (pos + size <= nodep->size) {
556 /* The file size is not changing. */
557 (void) async_data_write_finalize(chandle, nodep->data + pos,
558 size);
559 goto out;
560 }
561 size_t delta = (pos + size) - nodep->size;
562 /*
563 * At this point, we are deliberately extremely straightforward and
564 * simply realloc the contents of the file on every write that grows the
565 * file. In the end, the situation might not be as bad as it may look:
566 * our heap allocator can save us and just grow the block whenever
567 * possible.
568 */
569 void *newdata = realloc(nodep->data, nodep->size + delta);
570 if (!newdata) {
571 async_answer_0(chandle, ENOMEM);
572 size = 0;
573 goto out;
574 }
575 /* Clear any newly allocated memory in order to emulate gaps. */
576 memset(newdata + nodep->size, 0, delta);
577 nodep->size += delta;
578 nodep->data = newdata;
579 (void) async_data_write_finalize(chandle, nodep->data + pos, size);
580
581out:
582 *wbytes = size;
583 *nsize = nodep->size;
584 return EOK;
585}
586
587static errno_t tmpfs_truncate(service_id_t service_id, fs_index_t index,
588 aoff64_t size)
589{
590 /*
591 * Lookup the respective TMPFS node.
592 */
593 node_key_t key = {
594 .service_id = service_id,
595 .index = index
596 };
597
598 ht_link_t *hlp = hash_table_find(&nodes, &key);
599
600 if (!hlp)
601 return ENOENT;
602 tmpfs_node_t *nodep = hash_table_get_inst(hlp, tmpfs_node_t, nh_link);
603
604 if (size == nodep->size)
605 return EOK;
606
607 if (size > SIZE_MAX)
608 return ENOMEM;
609
610 void *newdata = realloc(nodep->data, size);
611 if (!newdata)
612 return ENOMEM;
613
614 if (size > nodep->size) {
615 size_t delta = size - nodep->size;
616 memset(newdata + nodep->size, 0, delta);
617 }
618
619 nodep->size = size;
620 nodep->data = newdata;
621 return EOK;
622}
623
624static errno_t tmpfs_close(service_id_t service_id, fs_index_t index)
625{
626 return EOK;
627}
628
629static errno_t tmpfs_destroy(service_id_t service_id, fs_index_t index)
630{
631 node_key_t key = {
632 .service_id = service_id,
633 .index = index
634 };
635
636 ht_link_t *hlp = hash_table_find(&nodes, &key);
637 if (!hlp)
638 return ENOENT;
639 tmpfs_node_t *nodep = hash_table_get_inst(hlp, tmpfs_node_t,
640 nh_link);
641 return tmpfs_destroy_node(FS_NODE(nodep));
642}
643
644static errno_t tmpfs_sync(service_id_t service_id, fs_index_t index)
645{
646 /*
647 * TMPFS keeps its data structures always consistent,
648 * thus the sync operation is a no-op.
649 */
650 return EOK;
651}
652
653vfs_out_ops_t tmpfs_ops = {
654 .fsprobe = tmpfs_fsprobe,
655 .mounted = tmpfs_mounted,
656 .unmounted = tmpfs_unmounted,
657 .read = tmpfs_read,
658 .write = tmpfs_write,
659 .truncate = tmpfs_truncate,
660 .close = tmpfs_close,
661 .destroy = tmpfs_destroy,
662 .sync = tmpfs_sync,
663};
664
665/**
666 * @}
667 */
668
Note: See TracBrowser for help on using the repository browser.