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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 9f429c0 was f49b0ea, checked in by Jakub Jermar <jakub@…>, 17 years ago

Split the 'mount another filesystem here' and 'you are being mounted and the
device is this' mount semantics. Add VFS_MOUNTED VFS operation that corresponds
to the latter and reserve VFS_MOUNT only for the former. Because of this
change, the VFS server does not maintain the mr_node VFS node for the name space
root anymore and the VFS_LOOKUP operation is now not meant to be used on
unmounted file system, not even for looking up the root node of unmounted file
systems. In the light of these changes, TMPFS is now initialized from
tmpfs_mounted() function.

  • 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 <ipc/ipc.h>
42#include <async.h>
43#include <errno.h>
44#include <atomic.h>
45#include <stdlib.h>
46#include <string.h>
47#include <stdio.h>
48#include <assert.h>
49#include <sys/types.h>
50#include <libadt/hash_table.h>
51#include <as.h>
52#include <libfs.h>
53
54#define min(a, b) ((a) < (b) ? (a) : (b))
55#define max(a, b) ((a) > (b) ? (a) : (b))
56
57#define DENTRIES_BUCKETS 256
58
59#define NAMES_BUCKETS 4
60
61/*
62 * For now, we don't distinguish between different dev_handles/instances. All
63 * requests resolve to the only instance, rooted in the following variable.
64 */
65static tmpfs_dentry_t *root;
66
67/*
68 * Implementation of the libfs interface.
69 */
70
71/* Forward declarations of static functions. */
72static void *tmpfs_match(void *, const char *);
73static void *tmpfs_node_get(dev_handle_t, fs_index_t);
74static void tmpfs_node_put(void *);
75static void *tmpfs_create_node(int);
76static bool tmpfs_link_node(void *, void *, const char *);
77static int tmpfs_unlink_node(void *, void *);
78static int tmpfs_destroy_node(void *);
79
80/* Implementation of helper functions. */
81static fs_index_t tmpfs_index_get(void *nodep)
82{
83 return ((tmpfs_dentry_t *) nodep)->index;
84}
85
86static size_t tmpfs_size_get(void *nodep)
87{
88 return ((tmpfs_dentry_t *) nodep)->size;
89}
90
91static unsigned tmpfs_lnkcnt_get(void *nodep)
92{
93 return ((tmpfs_dentry_t *) nodep)->lnkcnt;
94}
95
96static bool tmpfs_has_children(void *nodep)
97{
98 return ((tmpfs_dentry_t *) nodep)->child != NULL;
99}
100
101static void *tmpfs_root_get(dev_handle_t dev_handle)
102{
103 return root;
104}
105
106static char tmpfs_plb_get_char(unsigned pos)
107{
108 return tmpfs_reg.plb_ro[pos % PLB_SIZE];
109}
110
111static bool tmpfs_is_directory(void *nodep)
112{
113 return ((tmpfs_dentry_t *) nodep)->type == TMPFS_DIRECTORY;
114}
115
116static bool tmpfs_is_file(void *nodep)
117{
118 return ((tmpfs_dentry_t *) nodep)->type == TMPFS_FILE;
119}
120
121/** libfs operations */
122libfs_ops_t tmpfs_libfs_ops = {
123 .match = tmpfs_match,
124 .node_get = tmpfs_node_get,
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 .index_get = tmpfs_index_get,
131 .size_get = tmpfs_size_get,
132 .lnkcnt_get = tmpfs_lnkcnt_get,
133 .has_children = tmpfs_has_children,
134 .root_get = tmpfs_root_get,
135 .plb_get_char = tmpfs_plb_get_char,
136 .is_directory = tmpfs_is_directory,
137 .is_file = tmpfs_is_file
138};
139
140/** Hash table of all directory entries. */
141hash_table_t dentries;
142
143/* Implementation of hash table interface for the dentries hash table. */
144static hash_index_t dentries_hash(unsigned long *key)
145{
146 return *key % DENTRIES_BUCKETS;
147}
148
149static int dentries_compare(unsigned long *key, hash_count_t keys,
150 link_t *item)
151{
152 tmpfs_dentry_t *dentry = hash_table_get_instance(item, tmpfs_dentry_t,
153 dh_link);
154 return dentry->index == *key;
155}
156
157static void dentries_remove_callback(link_t *item)
158{
159}
160
161/** TMPFS dentries hash table operations. */
162hash_table_operations_t dentries_ops = {
163 .hash = dentries_hash,
164 .compare = dentries_compare,
165 .remove_callback = dentries_remove_callback
166};
167
168fs_index_t tmpfs_next_index = 1;
169
170typedef struct {
171 char *name;
172 tmpfs_dentry_t *parent;
173 link_t link;
174} tmpfs_name_t;
175
176/* Implementation of hash table interface for the names hash table. */
177static hash_index_t names_hash(unsigned long *key)
178{
179 tmpfs_dentry_t *dentry = (tmpfs_dentry_t *) *key;
180 return dentry->index % NAMES_BUCKETS;
181}
182
183static int names_compare(unsigned long *key, hash_count_t keys, link_t *item)
184{
185 tmpfs_dentry_t *dentry = (tmpfs_dentry_t *) *key;
186 tmpfs_name_t *namep = hash_table_get_instance(item, tmpfs_name_t,
187 link);
188 return dentry == namep->parent;
189}
190
191static void names_remove_callback(link_t *item)
192{
193 tmpfs_name_t *namep = hash_table_get_instance(item, tmpfs_name_t,
194 link);
195 free(namep->name);
196 free(namep);
197}
198
199/** TMPFS node names hash table operations. */
200static hash_table_operations_t names_ops = {
201 .hash = names_hash,
202 .compare = names_compare,
203 .remove_callback = names_remove_callback
204};
205
206static void tmpfs_name_initialize(tmpfs_name_t *namep)
207{
208 namep->name = NULL;
209 namep->parent = NULL;
210 link_initialize(&namep->link);
211}
212
213static bool tmpfs_dentry_initialize(tmpfs_dentry_t *dentry)
214{
215 dentry->index = 0;
216 dentry->sibling = NULL;
217 dentry->child = NULL;
218 dentry->type = TMPFS_NONE;
219 dentry->lnkcnt = 0;
220 dentry->size = 0;
221 dentry->data = NULL;
222 link_initialize(&dentry->dh_link);
223 return (bool)hash_table_create(&dentry->names, NAMES_BUCKETS, 1,
224 &names_ops);
225}
226
227static bool tmpfs_init(void)
228{
229 if (!hash_table_create(&dentries, DENTRIES_BUCKETS, 1, &dentries_ops))
230 return false;
231 root = (tmpfs_dentry_t *) tmpfs_create_node(L_DIRECTORY);
232 if (!root) {
233 hash_table_destroy(&dentries);
234 return false;
235 }
236 root->lnkcnt = 1;
237 return true;
238}
239
240/** Compare one component of path to a directory entry.
241 *
242 * @param parentp Pointer to node from which we descended.
243 * @param childp Pointer to node to compare the path component with.
244 * @param component Array of characters holding component name.
245 *
246 * @return True on match, false otherwise.
247 */
248static bool
249tmpfs_match_one(tmpfs_dentry_t *parentp, tmpfs_dentry_t *childp,
250 const char *component)
251{
252 unsigned long key = (unsigned long) parentp;
253 link_t *hlp = hash_table_find(&childp->names, &key);
254 assert(hlp);
255 tmpfs_name_t *namep = hash_table_get_instance(hlp, tmpfs_name_t, link);
256 return !strcmp(namep->name, component);
257}
258
259void *tmpfs_match(void *prnt, const char *component)
260{
261 tmpfs_dentry_t *parentp = (tmpfs_dentry_t *) prnt;
262 tmpfs_dentry_t *childp = parentp->child;
263
264 while (childp && !tmpfs_match_one(parentp, childp, component))
265 childp = childp->sibling;
266
267 return (void *) childp;
268}
269
270void *
271tmpfs_node_get(dev_handle_t dev_handle, fs_index_t index)
272{
273 unsigned long key = index;
274 link_t *lnk = hash_table_find(&dentries, &key);
275 if (!lnk)
276 return NULL;
277 return hash_table_get_instance(lnk, tmpfs_dentry_t, dh_link);
278}
279
280void tmpfs_node_put(void *node)
281{
282 /* nothing to do */
283}
284
285void *tmpfs_create_node(int lflag)
286{
287 assert((lflag & L_FILE) ^ (lflag & L_DIRECTORY));
288
289 tmpfs_dentry_t *node = malloc(sizeof(tmpfs_dentry_t));
290 if (!node)
291 return NULL;
292
293 if (!tmpfs_dentry_initialize(node)) {
294 free(node);
295 return NULL;
296 }
297 node->index = tmpfs_next_index++;
298 if (lflag & L_DIRECTORY)
299 node->type = TMPFS_DIRECTORY;
300 else
301 node->type = TMPFS_FILE;
302
303 /* Insert the new node into the dentry hash table. */
304 unsigned long key = node->index;
305 hash_table_insert(&dentries, &key, &node->dh_link);
306 return (void *) node;
307}
308
309bool tmpfs_link_node(void *prnt, void *chld, const char *nm)
310{
311 tmpfs_dentry_t *parentp = (tmpfs_dentry_t *) prnt;
312 tmpfs_dentry_t *childp = (tmpfs_dentry_t *) chld;
313
314 assert(parentp->type == TMPFS_DIRECTORY);
315
316 tmpfs_name_t *namep = malloc(sizeof(tmpfs_name_t));
317 if (!namep)
318 return false;
319 tmpfs_name_initialize(namep);
320 size_t len = strlen(nm);
321 namep->name = malloc(len + 1);
322 if (!namep->name) {
323 free(namep);
324 return false;
325 }
326 strcpy(namep->name, nm);
327 namep->parent = parentp;
328
329 childp->lnkcnt++;
330
331 unsigned long key = (unsigned long) parentp;
332 hash_table_insert(&childp->names, &key, &namep->link);
333
334 /* Insert the new node into the namespace. */
335 if (parentp->child) {
336 tmpfs_dentry_t *tmp = parentp->child;
337 while (tmp->sibling)
338 tmp = tmp->sibling;
339 tmp->sibling = childp;
340 } else {
341 parentp->child = childp;
342 }
343
344 return true;
345}
346
347int tmpfs_unlink_node(void *prnt, void *chld)
348{
349 tmpfs_dentry_t *parentp = (tmpfs_dentry_t *)prnt;
350 tmpfs_dentry_t *childp = (tmpfs_dentry_t *)chld;
351
352 if (!parentp)
353 return EBUSY;
354
355 if (childp->child)
356 return ENOTEMPTY;
357
358 if (parentp->child == childp) {
359 parentp->child = childp->sibling;
360 } else {
361 /* TODO: consider doubly linked list for organizing siblings. */
362 tmpfs_dentry_t *tmp = parentp->child;
363 while (tmp->sibling != childp)
364 tmp = tmp->sibling;
365 tmp->sibling = childp->sibling;
366 }
367 childp->sibling = NULL;
368
369 unsigned long key = (unsigned long) parentp;
370 hash_table_remove(&childp->names, &key, 1);
371
372 childp->lnkcnt--;
373
374 return EOK;
375}
376
377int tmpfs_destroy_node(void *nodep)
378{
379 tmpfs_dentry_t *dentry = (tmpfs_dentry_t *) nodep;
380
381 assert(!dentry->lnkcnt);
382 assert(!dentry->child);
383 assert(!dentry->sibling);
384
385 unsigned long key = dentry->index;
386 hash_table_remove(&dentries, &key, 1);
387
388 hash_table_destroy(&dentry->names);
389
390 if (dentry->type == TMPFS_FILE)
391 free(dentry->data);
392 free(dentry);
393 return EOK;
394}
395
396void tmpfs_mounted(ipc_callid_t rid, ipc_call_t *request)
397{
398 dev_handle_t dev_handle = (dev_handle_t) IPC_GET_ARG1(*request);
399
400 /* Initialize TMPFS. */
401 if (!root && !tmpfs_init()) {
402 ipc_answer_0(rid, ENOMEM);
403 return;
404 }
405
406 if (dev_handle >= 0) {
407 if (tmpfs_restore(dev_handle))
408 ipc_answer_0(rid, EOK);
409 else
410 ipc_answer_0(rid, ELIMIT);
411 } else {
412 ipc_answer_0(rid, EOK);
413 }
414}
415
416void tmpfs_mount(ipc_callid_t rid, ipc_call_t *request)
417{
418 dev_handle_t mp_dev_handle = (dev_handle_t) IPC_GET_ARG1(*request);
419 fs_index_t mp_index = (fs_index_t) IPC_GET_ARG2(*request);
420 fs_handle_t mr_fs_handle = (fs_handle_t) IPC_GET_ARG3(*request);
421 dev_handle_t mr_dev_handle = (dev_handle_t) IPC_GET_ARG4(*request);
422
423 ipc_answer_0(rid, ENOTSUP);
424}
425
426void tmpfs_lookup(ipc_callid_t rid, ipc_call_t *request)
427{
428 libfs_lookup(&tmpfs_libfs_ops, tmpfs_reg.fs_handle, rid, request);
429}
430
431void tmpfs_read(ipc_callid_t rid, ipc_call_t *request)
432{
433 dev_handle_t dev_handle = (dev_handle_t)IPC_GET_ARG1(*request);
434 fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
435 off_t pos = (off_t)IPC_GET_ARG3(*request);
436
437 /*
438 * Lookup the respective dentry.
439 */
440 link_t *hlp;
441 unsigned long key = index;
442 hlp = hash_table_find(&dentries, &key);
443 if (!hlp) {
444 ipc_answer_0(rid, ENOENT);
445 return;
446 }
447 tmpfs_dentry_t *dentry = hash_table_get_instance(hlp, tmpfs_dentry_t,
448 dh_link);
449
450 /*
451 * Receive the read request.
452 */
453 ipc_callid_t callid;
454 size_t len;
455 if (!ipc_data_read_receive(&callid, &len)) {
456 ipc_answer_0(callid, EINVAL);
457 ipc_answer_0(rid, EINVAL);
458 return;
459 }
460
461 size_t bytes;
462 if (dentry->type == TMPFS_FILE) {
463 bytes = max(0, min(dentry->size - pos, len));
464 (void) ipc_data_read_finalize(callid, dentry->data + pos,
465 bytes);
466 } else {
467 int i;
468 tmpfs_dentry_t *cur;
469
470 assert(dentry->type == TMPFS_DIRECTORY);
471
472 /*
473 * Yes, we really use O(n) algorithm here.
474 * If it bothers someone, it could be fixed by introducing a
475 * hash table.
476 */
477 for (i = 0, cur = dentry->child; i < pos && cur; i++,
478 cur = cur->sibling)
479 ;
480
481 if (!cur) {
482 ipc_answer_0(callid, ENOENT);
483 ipc_answer_1(rid, ENOENT, 0);
484 return;
485 }
486
487 unsigned long key = (unsigned long) dentry;
488 link_t *hlp = hash_table_find(&cur->names, &key);
489 assert(hlp);
490 tmpfs_name_t *namep = hash_table_get_instance(hlp, tmpfs_name_t,
491 link);
492
493 (void) ipc_data_read_finalize(callid, namep->name,
494 strlen(namep->name) + 1);
495 bytes = 1;
496 }
497
498 /*
499 * Answer the VFS_READ call.
500 */
501 ipc_answer_1(rid, EOK, bytes);
502}
503
504void tmpfs_write(ipc_callid_t rid, ipc_call_t *request)
505{
506 dev_handle_t dev_handle = (dev_handle_t)IPC_GET_ARG1(*request);
507 fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
508 off_t pos = (off_t)IPC_GET_ARG3(*request);
509
510 /*
511 * Lookup the respective dentry.
512 */
513 link_t *hlp;
514 unsigned long key = index;
515 hlp = hash_table_find(&dentries, &key);
516 if (!hlp) {
517 ipc_answer_0(rid, ENOENT);
518 return;
519 }
520 tmpfs_dentry_t *dentry = hash_table_get_instance(hlp, tmpfs_dentry_t,
521 dh_link);
522
523 /*
524 * Receive the write request.
525 */
526 ipc_callid_t callid;
527 size_t len;
528 if (!ipc_data_write_receive(&callid, &len)) {
529 ipc_answer_0(callid, EINVAL);
530 ipc_answer_0(rid, EINVAL);
531 return;
532 }
533
534 /*
535 * Check whether the file needs to grow.
536 */
537 if (pos + len <= dentry->size) {
538 /* The file size is not changing. */
539 (void) ipc_data_write_finalize(callid, dentry->data + pos, len);
540 ipc_answer_2(rid, EOK, len, dentry->size);
541 return;
542 }
543 size_t delta = (pos + len) - dentry->size;
544 /*
545 * At this point, we are deliberately extremely straightforward and
546 * simply realloc the contents of the file on every write that grows the
547 * file. In the end, the situation might not be as bad as it may look:
548 * our heap allocator can save us and just grow the block whenever
549 * possible.
550 */
551 void *newdata = realloc(dentry->data, dentry->size + delta);
552 if (!newdata) {
553 ipc_answer_0(callid, ENOMEM);
554 ipc_answer_2(rid, EOK, 0, dentry->size);
555 return;
556 }
557 /* Clear any newly allocated memory in order to emulate gaps. */
558 memset(newdata + dentry->size, 0, delta);
559 dentry->size += delta;
560 dentry->data = newdata;
561 (void) ipc_data_write_finalize(callid, dentry->data + pos, len);
562 ipc_answer_2(rid, EOK, len, dentry->size);
563}
564
565void tmpfs_truncate(ipc_callid_t rid, ipc_call_t *request)
566{
567 dev_handle_t dev_handle = (dev_handle_t)IPC_GET_ARG1(*request);
568 fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
569 size_t size = (off_t)IPC_GET_ARG3(*request);
570
571 /*
572 * Lookup the respective dentry.
573 */
574 link_t *hlp;
575 unsigned long key = index;
576 hlp = hash_table_find(&dentries, &key);
577 if (!hlp) {
578 ipc_answer_0(rid, ENOENT);
579 return;
580 }
581 tmpfs_dentry_t *dentry = hash_table_get_instance(hlp, tmpfs_dentry_t,
582 dh_link);
583
584 if (size == dentry->size) {
585 ipc_answer_0(rid, EOK);
586 return;
587 }
588
589 void *newdata = realloc(dentry->data, size);
590 if (!newdata) {
591 ipc_answer_0(rid, ENOMEM);
592 return;
593 }
594 if (size > dentry->size) {
595 size_t delta = size - dentry->size;
596 memset(newdata + dentry->size, 0, delta);
597 }
598 dentry->size = size;
599 dentry->data = newdata;
600 ipc_answer_0(rid, EOK);
601}
602
603void tmpfs_destroy(ipc_callid_t rid, ipc_call_t *request)
604{
605 dev_handle_t dev_handle = (dev_handle_t)IPC_GET_ARG1(*request);
606 fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
607 int rc;
608
609 link_t *hlp;
610 unsigned long key = index;
611 hlp = hash_table_find(&dentries, &key);
612 if (!hlp) {
613 ipc_answer_0(rid, ENOENT);
614 return;
615 }
616 tmpfs_dentry_t *dentry = hash_table_get_instance(hlp, tmpfs_dentry_t,
617 dh_link);
618 rc = tmpfs_destroy_node(dentry);
619 ipc_answer_0(rid, rc);
620}
621
622/**
623 * @}
624 */
Note: See TracBrowser for help on using the repository browser.