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

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

Support for rename().

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