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

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

Streamline TMPFS and fix its design so that it properly supports hardlinks.
libfs operation unlink() is now passed also the name of the component being
unlinked.

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