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
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 bool tmpfs_match(void *, void *, const char *);
73static void *tmpfs_node_get(int, int, unsigned long);
74static void *tmpfs_create_node(int);
75static bool tmpfs_link_node(void *, void *, const char *);
76static int tmpfs_unlink_node(void *, void *);
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{
92 return ((tmpfs_dentry_t *) nodep)->lnkcnt;
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,
128 .node_get = tmpfs_node_get,
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};
143
144/** Hash table of all directory entries. */
145hash_table_t dentries;
146
147/* Implementation of hash table interface for the dentries hash table. */
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
172unsigned tmpfs_next_index = 1;
173
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)
218{
219 dentry->index = 0;
220 dentry->sibling = NULL;
221 dentry->child = NULL;
222 dentry->type = TMPFS_NONE;
223 dentry->lnkcnt = 0;
224 dentry->size = 0;
225 dentry->data = NULL;
226 link_initialize(&dentry->dh_link);
227 return (bool)hash_table_create(&dentry->names, NAMES_BUCKETS, 1,
228 &names_ops);
229}
230
231static bool tmpfs_init(void)
232{
233 if (!hash_table_create(&dentries, DENTRIES_BUCKETS, 1, &dentries_ops))
234 return false;
235 root = (tmpfs_dentry_t *) tmpfs_create_node(L_DIRECTORY);
236 if (!root) {
237 hash_table_destroy(&dentries);
238 return false;
239 }
240 root->lnkcnt = 1;
241 return true;
242}
243
244/** Compare one component of path to a directory entry.
245 *
246 * @param prnt Node from which we descended.
247 * @param chld Node to compare the path component with.
248 * @param component Array of characters holding component name.
249 *
250 * @return True on match, false otherwise.
251 */
252bool tmpfs_match(void *prnt, void *chld, const char *component)
253{
254 tmpfs_dentry_t *parentp = (tmpfs_dentry_t *) prnt;
255 tmpfs_dentry_t *childp = (tmpfs_dentry_t *) chld;
256
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);
263}
264
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
273void *tmpfs_create_node(int lflag)
274{
275 assert((lflag & L_FILE) ^ (lflag & L_DIRECTORY));
276
277 tmpfs_dentry_t *node = malloc(sizeof(tmpfs_dentry_t));
278 if (!node)
279 return NULL;
280
281 if (!tmpfs_dentry_initialize(node)) {
282 free(node);
283 return NULL;
284 }
285 node->index = tmpfs_next_index++;
286 if (lflag & L_DIRECTORY)
287 node->type = TMPFS_DIRECTORY;
288 else
289 node->type = TMPFS_FILE;
290
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
296bool tmpfs_link_node(void *prnt, void *chld, const char *nm)
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
303 tmpfs_name_t *namep = malloc(sizeof(tmpfs_name_t));
304 if (!namep)
305 return false;
306 tmpfs_name_initialize(namep);
307 size_t len = strlen(nm);
308 namep->name = malloc(len + 1);
309 if (!namep->name) {
310 free(namep);
311 return false;
312 }
313 strcpy(namep->name, nm);
314 namep->parent = parentp;
315
316 childp->lnkcnt++;
317
318 unsigned long key = (unsigned long) parentp;
319 hash_table_insert(&childp->names, &key, &namep->link);
320
321 /* Insert the new node into the namespace. */
322 if (parentp->child) {
323 tmpfs_dentry_t *tmp = parentp->child;
324 while (tmp->sibling)
325 tmp = tmp->sibling;
326 tmp->sibling = childp;
327 } else {
328 parentp->child = childp;
329 }
330
331 return true;
332}
333
334int tmpfs_unlink_node(void *prnt, void *chld)
335{
336 tmpfs_dentry_t *parentp = (tmpfs_dentry_t *)prnt;
337 tmpfs_dentry_t *childp = (tmpfs_dentry_t *)chld;
338
339 if (!parentp)
340 return EBUSY;
341
342 if (childp->child)
343 return ENOTEMPTY;
344
345 if (parentp->child == childp) {
346 parentp->child = childp->sibling;
347 } else {
348 /* TODO: consider doubly linked list for organizing siblings. */
349 tmpfs_dentry_t *tmp = parentp->child;
350 while (tmp->sibling != childp)
351 tmp = tmp->sibling;
352 tmp->sibling = childp->sibling;
353 }
354 childp->sibling = NULL;
355
356 unsigned long key = (unsigned long) parentp;
357 hash_table_remove(&childp->names, &key, 1);
358
359 childp->lnkcnt--;
360
361 return EOK;
362}
363
364void tmpfs_destroy_node(void *nodep)
365{
366 tmpfs_dentry_t *dentry = (tmpfs_dentry_t *) nodep;
367
368 assert(!dentry->lnkcnt);
369 assert(!dentry->child);
370 assert(!dentry->sibling);
371
372 unsigned long index = dentry->index;
373 hash_table_remove(&dentries, &index, 1);
374
375 hash_table_destroy(&dentry->names);
376
377 if (dentry->type == TMPFS_FILE)
378 free(dentry->data);
379 free(dentry);
380}
381
382void tmpfs_lookup(ipc_callid_t rid, ipc_call_t *request)
383{
384 /* Initialize TMPFS. */
385 if (!root && !tmpfs_init()) {
386 ipc_answer_0(rid, ENOMEM);
387 return;
388 }
389 libfs_lookup(&tmpfs_libfs_ops, tmpfs_reg.fs_handle, rid, request);
390}
391
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 /*
411 * Receive the read request.
412 */
413 ipc_callid_t callid;
414 size_t len;
415 if (!ipc_data_read_receive(&callid, &len)) {
416 ipc_answer_0(callid, EINVAL);
417 ipc_answer_0(rid, EINVAL);
418 return;
419 }
420
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;
428 tmpfs_dentry_t *cur;
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
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);
455 bytes = 1;
456 }
457
458 /*
459 * Answer the VFS_READ call.
460 */
461 ipc_answer_1(rid, EOK, bytes);
462}
463
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;
486 size_t len;
487 if (!ipc_data_write_receive(&callid, &len)) {
488 ipc_answer_0(callid, EINVAL);
489 ipc_answer_0(rid, EINVAL);
490 return;
491 }
492
493 /*
494 * Check whether the file needs to grow.
495 */
496 if (pos + len <= dentry->size) {
497 /* The file size is not changing. */
498 (void) ipc_data_write_finalize(callid, dentry->data + pos, len);
499 ipc_answer_2(rid, EOK, len, dentry->size);
500 return;
501 }
502 size_t delta = (pos + len) - dentry->size;
503 /*
504 * At this point, we are deliberately extremely straightforward and
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.
509 */
510 void *newdata = realloc(dentry->data, dentry->size + delta);
511 if (!newdata) {
512 ipc_answer_0(callid, ENOMEM);
513 ipc_answer_2(rid, EOK, 0, dentry->size);
514 return;
515 }
516 /* Clear any newly allocated memory in order to emulate gaps. */
517 memset(newdata + dentry->size, 0, delta);
518 dentry->size += delta;
519 dentry->data = newdata;
520 (void) ipc_data_write_finalize(callid, dentry->data + pos, len);
521 ipc_answer_2(rid, EOK, len, dentry->size);
522}
523
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
561void tmpfs_destroy(ipc_callid_t rid, ipc_call_t *request)
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);
574 tmpfs_destroy_node(dentry);
575 ipc_answer_0(rid, EOK);
576}
577
578/**
579 * @}
580 */
Note: See TracBrowser for help on using the repository browser.