source: mainline/uspace/srv/fs/cdfs/cdfs_ops.c@ 0ca7286

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 0ca7286 was 0ca7286, checked in by Adam Hraska <adam.hraska+hos@…>, 13 years ago

Added resizing to user space (single-threaded) hash_table. Resizes in a way to mitigate effects of bad hash functions. Change of interface affected many files.

  • Property mode set to 100644
File size: 22.3 KB
Line 
1/*
2 * Copyright (c) 2011 Martin Decky
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 cdfs_ops.c
35 * @brief Implementation of VFS operations for the cdfs file system
36 * server.
37 */
38
39#include <bool.h>
40#include <adt/hash_table.h>
41#include <malloc.h>
42#include <mem.h>
43#include <loc.h>
44#include <libfs.h>
45#include <errno.h>
46#include <libblock.h>
47#include <str.h>
48#include <byteorder.h>
49#include <macros.h>
50#include "cdfs.h"
51#include "cdfs_endian.h"
52#include "cdfs_ops.h"
53
54/** Standard CD-ROM block size */
55#define BLOCK_SIZE 2048
56
57/** Implicit node cache size
58 *
59 * More nodes can be actually cached if the files remain
60 * opended.
61 *
62 */
63#define NODE_CACHE_SIZE 200
64
65#define NODES_KEY_SRVC 0
66#define NODES_KEY_INDEX 1
67
68/** All root nodes have index 0 */
69#define CDFS_SOME_ROOT 0
70
71#define CDFS_NODE(node) ((node) ? (cdfs_node_t *)(node)->data : NULL)
72#define FS_NODE(node) ((node) ? (node)->fs_node : NULL)
73
74#define CDFS_STANDARD_IDENT "CD001"
75
76typedef enum {
77 VOL_DESC_BOOT = 0,
78 VOL_DESC_PRIMARY = 1,
79 VOL_DESC_SUPPLEMENTARY = 2,
80 VOL_DESC_VOL_PARTITION = 3,
81 VOL_DESC_SET_TERMINATOR = 255
82} vol_desc_type_t;
83
84typedef struct {
85 uint8_t system_ident[32];
86 uint8_t ident[32];
87} __attribute__((packed)) cdfs_vol_desc_boot_t;
88
89typedef struct {
90 uint8_t year[4];
91 uint8_t mon[2];
92 uint8_t day[2];
93
94 uint8_t hour[2];
95 uint8_t min[2];
96 uint8_t sec[2];
97 uint8_t msec[2];
98
99 uint8_t offset;
100} __attribute__((packed)) cdfs_datetime_t;
101
102typedef struct {
103 uint8_t year; /**< Since 1900 */
104 uint8_t mon;
105 uint8_t day;
106
107 uint8_t hour;
108 uint8_t min;
109 uint8_t sec;
110
111 uint8_t offset;
112} __attribute__((packed)) cdfs_timestamp_t;
113
114typedef enum {
115 DIR_FLAG_DIRECTORY = 2
116} cdfs_dir_flag_t;
117
118typedef struct {
119 uint8_t length;
120 uint8_t ea_length;
121
122 uint32_t_lb lba;
123 uint32_t_lb size;
124
125 cdfs_timestamp_t timestamp;
126 uint8_t flags;
127 uint8_t unit_size;
128 uint8_t gap_size;
129 uint16_t_lb sequence_nr;
130
131 uint8_t name_length;
132 uint8_t name[];
133} __attribute__((packed)) cdfs_dir_t;
134
135typedef struct {
136 uint8_t res0;
137
138 uint8_t system_ident[32];
139 uint8_t ident[32];
140
141 uint64_t res1;
142 uint32_t_lb lba_size;
143
144 uint8_t res2[32];
145 uint16_t_lb set_size;
146 uint16_t_lb sequence_nr;
147
148 uint16_t_lb block_size;
149 uint32_t_lb path_table_size;
150
151 uint32_t path_table_lsb;
152 uint32_t opt_path_table_lsb;
153 uint32_t path_table_msb;
154 uint32_t opt_path_table_msb;
155
156 cdfs_dir_t root_dir;
157 uint8_t pad0;
158
159 uint8_t set_ident[128];
160 uint8_t publisher_ident[128];
161 uint8_t preparer_ident[128];
162 uint8_t app_ident[128];
163
164 uint8_t copyright_file_ident[37];
165 uint8_t abstract_file_ident[37];
166 uint8_t biblio_file_ident[37];
167
168 cdfs_datetime_t creation;
169 cdfs_datetime_t modification;
170 cdfs_datetime_t expiration;
171 cdfs_datetime_t effective;
172
173 uint8_t fs_version;
174} __attribute__((packed)) cdfs_vol_desc_primary_t;
175
176typedef struct {
177 uint8_t type;
178 uint8_t standard_ident[5];
179 uint8_t version;
180 union {
181 cdfs_vol_desc_boot_t boot;
182 cdfs_vol_desc_primary_t primary;
183 } data;
184} __attribute__((packed)) cdfs_vol_desc_t;
185
186typedef enum {
187 CDFS_NONE,
188 CDFS_FILE,
189 CDFS_DIRECTORY
190} cdfs_dentry_type_t;
191
192typedef struct {
193 link_t link; /**< Siblings list link */
194 fs_index_t index; /**< Node index */
195 char *name; /**< Dentry name */
196} cdfs_dentry_t;
197
198typedef uint32_t cdfs_lba_t;
199
200typedef struct {
201 fs_node_t *fs_node; /**< FS node */
202 fs_index_t index; /**< Node index */
203 service_id_t service_id; /**< Service ID of block device */
204
205 link_t nh_link; /**< Nodes hash table link */
206 cdfs_dentry_type_t type; /**< Dentry type */
207
208 unsigned int lnkcnt; /**< Link count */
209 uint32_t size; /**< File size if type is CDFS_FILE */
210
211 list_t cs_list; /**< Child's siblings list */
212 cdfs_lba_t lba; /**< LBA of data on disk */
213 bool processed; /**< If all children have been read */
214 unsigned int opened; /**< Opened count */
215} cdfs_node_t;
216
217/** Shared index of nodes */
218static fs_index_t cdfs_index = 1;
219
220/** Number of currently cached nodes */
221static size_t nodes_cached = 0;
222
223/** Hash table of all cdfs nodes */
224static hash_table_t nodes;
225
226static size_t nodes_key_hash(unsigned long key[])
227{
228 return key[NODES_KEY_INDEX];
229}
230
231static size_t nodes_hash(const link_t *item)
232{
233 cdfs_node_t *node = hash_table_get_instance(item, cdfs_node_t, nh_link);
234
235 unsigned long key[] = {
236 [NODES_KEY_INDEX] = node->index
237 };
238
239 return nodes_key_hash(key);
240}
241
242static bool nodes_match(unsigned long key[], size_t keys, const link_t *item)
243{
244 cdfs_node_t *node = hash_table_get_instance(item, cdfs_node_t, nh_link);
245
246 if (keys == 1) {
247 return (node->service_id == key[NODES_KEY_SRVC]);
248 } else {
249 assert(keys == 2);
250 return ((node->service_id == key[NODES_KEY_SRVC]) &&
251 (node->index == key[NODES_KEY_INDEX]));
252 }
253}
254
255static bool nodes_equal(const link_t *item1, const link_t *item2)
256{
257 cdfs_node_t *node1 = hash_table_get_instance(item1, cdfs_node_t, nh_link);
258 cdfs_node_t *node2 = hash_table_get_instance(item2, cdfs_node_t, nh_link);
259
260 return node1->service_id == node2->service_id
261 && node1->index == node2->index;
262}
263
264static void nodes_remove_callback(link_t *item)
265{
266 cdfs_node_t *node = hash_table_get_instance(item, cdfs_node_t, nh_link);
267
268 assert(node->type == CDFS_DIRECTORY);
269
270 link_t *link;
271 while ((link = list_first(&node->cs_list)) != NULL) {
272 cdfs_dentry_t *dentry = list_get_instance(link, cdfs_dentry_t, link);
273 list_remove(&dentry->link);
274 free(dentry);
275 }
276
277 free(node->fs_node);
278 free(node);
279}
280
281/** Nodes hash table operations */
282static hash_table_ops_t nodes_ops = {
283 .hash = nodes_hash,
284 .key_hash = nodes_key_hash,
285 .match = nodes_match,
286 .equal = nodes_equal,
287 .remove_callback = nodes_remove_callback
288};
289
290static int cdfs_node_get(fs_node_t **rfn, service_id_t service_id,
291 fs_index_t index)
292{
293 unsigned long key[] = {
294 [NODES_KEY_SRVC] = service_id,
295 [NODES_KEY_INDEX] = index
296 };
297
298 link_t *link = hash_table_find(&nodes, key);
299 if (link) {
300 cdfs_node_t *node =
301 hash_table_get_instance(link, cdfs_node_t, nh_link);
302
303 *rfn = FS_NODE(node);
304 } else
305 *rfn = NULL;
306
307 return EOK;
308}
309
310static int cdfs_root_get(fs_node_t **rfn, service_id_t service_id)
311{
312 return cdfs_node_get(rfn, service_id, CDFS_SOME_ROOT);
313}
314
315static void cdfs_node_initialize(cdfs_node_t *node)
316{
317 node->fs_node = NULL;
318 node->index = 0;
319 node->service_id = 0;
320 node->type = CDFS_NONE;
321 node->lnkcnt = 0;
322 node->size = 0;
323 node->lba = 0;
324 node->processed = false;
325 node->opened = 0;
326
327 link_initialize(&node->nh_link);
328 list_initialize(&node->cs_list);
329}
330
331static int create_node(fs_node_t **rfn, service_id_t service_id, int lflag,
332 fs_index_t index)
333{
334 assert((lflag & L_FILE) ^ (lflag & L_DIRECTORY));
335
336 cdfs_node_t *node = malloc(sizeof(cdfs_node_t));
337 if (!node)
338 return ENOMEM;
339
340 cdfs_node_initialize(node);
341
342 node->fs_node = malloc(sizeof(fs_node_t));
343 if (!node->fs_node) {
344 free(node);
345 return ENOMEM;
346 }
347
348 fs_node_initialize(node->fs_node);
349 node->fs_node->data = node;
350
351 fs_node_t *rootfn;
352 int rc = cdfs_root_get(&rootfn, service_id);
353
354 assert(rc == EOK);
355
356 if (!rootfn)
357 node->index = CDFS_SOME_ROOT;
358 else
359 node->index = index;
360
361 node->service_id = service_id;
362
363 if (lflag & L_DIRECTORY)
364 node->type = CDFS_DIRECTORY;
365 else
366 node->type = CDFS_FILE;
367
368 /* Insert the new node into the nodes hash table. */
369 hash_table_insert(&nodes, &node->nh_link);
370
371 *rfn = FS_NODE(node);
372 nodes_cached++;
373
374 return EOK;
375}
376
377static int link_node(fs_node_t *pfn, fs_node_t *fn, const char *name)
378{
379 cdfs_node_t *parent = CDFS_NODE(pfn);
380 cdfs_node_t *node = CDFS_NODE(fn);
381
382 assert(parent->type == CDFS_DIRECTORY);
383
384 /* Check for duplicate entries */
385 list_foreach(parent->cs_list, link) {
386 cdfs_dentry_t *dentry =
387 list_get_instance(link, cdfs_dentry_t, link);
388
389 if (str_cmp(dentry->name, name) == 0)
390 return EEXIST;
391 }
392
393 /* Allocate and initialize the dentry */
394 cdfs_dentry_t *dentry = malloc(sizeof(cdfs_dentry_t));
395 if (!dentry)
396 return ENOMEM;
397
398 /* Populate and link the new dentry */
399 dentry->name = str_dup(name);
400 if (dentry->name == NULL) {
401 free(dentry);
402 return ENOMEM;
403 }
404
405 link_initialize(&dentry->link);
406 dentry->index = node->index;
407
408 node->lnkcnt++;
409 list_append(&dentry->link, &parent->cs_list);
410
411 return EOK;
412}
413
414static bool cdfs_readdir(service_id_t service_id, fs_node_t *fs_node)
415{
416 cdfs_node_t *node = CDFS_NODE(fs_node);
417 assert(node);
418
419 if (node->processed)
420 return true;
421
422 uint32_t blocks = node->size / BLOCK_SIZE;
423 if ((node->size % BLOCK_SIZE) != 0)
424 blocks++;
425
426 for (uint32_t i = 0; i < blocks; i++) {
427 block_t *block;
428 int rc = block_get(&block, service_id, node->lba + i, BLOCK_FLAGS_NONE);
429 if (rc != EOK)
430 return false;
431
432 cdfs_dir_t *dir = (cdfs_dir_t *) block->data;
433
434 // FIXME: skip '.' and '..'
435
436 for (size_t offset = 0;
437 (dir->length != 0) && (offset < BLOCK_SIZE);
438 offset += dir->length) {
439 dir = (cdfs_dir_t *) (block->data + offset);
440
441 cdfs_dentry_type_t dentry_type;
442 if (dir->flags & DIR_FLAG_DIRECTORY)
443 dentry_type = CDFS_DIRECTORY;
444 else
445 dentry_type = CDFS_FILE;
446
447 // FIXME: hack - indexing by dentry byte offset on disc
448
449 fs_node_t *fn;
450 int rc = create_node(&fn, service_id, dentry_type,
451 (node->lba + i) * BLOCK_SIZE + offset);
452 if ((rc != EOK) || (fn == NULL))
453 return false;
454
455 cdfs_node_t *cur = CDFS_NODE(fn);
456 cur->lba = uint32_lb(dir->lba);
457 cur->size = uint32_lb(dir->size);
458
459 char *name = (char *) malloc(dir->name_length + 1);
460 if (name == NULL)
461 return false;
462
463 memcpy(name, dir->name, dir->name_length);
464 name[dir->name_length] = 0;
465
466 // FIXME: check return value
467
468 link_node(fs_node, fn, name);
469 free(name);
470
471 if (dentry_type == CDFS_FILE)
472 cur->processed = true;
473 }
474
475 block_put(block);
476 }
477
478 node->processed = true;
479 return true;
480}
481
482static fs_node_t *get_uncached_node(service_id_t service_id, fs_index_t index)
483{
484 cdfs_lba_t lba = index / BLOCK_SIZE;
485 size_t offset = index % BLOCK_SIZE;
486
487 block_t *block;
488 int rc = block_get(&block, service_id, lba, BLOCK_FLAGS_NONE);
489 if (rc != EOK)
490 return NULL;
491
492 cdfs_dir_t *dir = (cdfs_dir_t *) (block->data + offset);
493
494 cdfs_dentry_type_t dentry_type;
495 if (dir->flags & DIR_FLAG_DIRECTORY)
496 dentry_type = CDFS_DIRECTORY;
497 else
498 dentry_type = CDFS_FILE;
499
500 fs_node_t *fn;
501 rc = create_node(&fn, service_id, dentry_type, index);
502 if ((rc != EOK) || (fn == NULL))
503 return NULL;
504
505 cdfs_node_t *node = CDFS_NODE(fn);
506 node->lba = uint32_lb(dir->lba);
507 node->size = uint32_lb(dir->size);
508 node->lnkcnt = 1;
509
510 if (dentry_type == CDFS_FILE)
511 node->processed = true;
512
513 block_put(block);
514 return fn;
515}
516
517static fs_node_t *get_cached_node(service_id_t service_id, fs_index_t index)
518{
519 unsigned long key[] = {
520 [NODES_KEY_SRVC] = service_id,
521 [NODES_KEY_INDEX] = index
522 };
523
524 link_t *link = hash_table_find(&nodes, key);
525 if (link) {
526 cdfs_node_t *node =
527 hash_table_get_instance(link, cdfs_node_t, nh_link);
528 return FS_NODE(node);
529 }
530
531 return get_uncached_node(service_id, index);
532}
533
534static int cdfs_match(fs_node_t **fn, fs_node_t *pfn, const char *component)
535{
536 cdfs_node_t *parent = CDFS_NODE(pfn);
537
538 if (!parent->processed) {
539 int rc = cdfs_readdir(parent->service_id, pfn);
540 if (rc != EOK)
541 return rc;
542 }
543
544 list_foreach(parent->cs_list, link) {
545 cdfs_dentry_t *dentry =
546 list_get_instance(link, cdfs_dentry_t, link);
547
548 if (str_cmp(dentry->name, component) == 0) {
549 *fn = get_cached_node(parent->service_id, dentry->index);
550 return EOK;
551 }
552 }
553
554 *fn = NULL;
555 return EOK;
556}
557
558static int cdfs_node_open(fs_node_t *fn)
559{
560 cdfs_node_t *node = CDFS_NODE(fn);
561
562 if (!node->processed)
563 cdfs_readdir(node->service_id, fn);
564
565 node->opened++;
566 return EOK;
567}
568
569static int cdfs_node_put(fs_node_t *fn)
570{
571 /* Nothing to do */
572 return EOK;
573}
574
575static int cdfs_create_node(fs_node_t **fn, service_id_t service_id, int lflag)
576{
577 /* Read-only */
578 return ENOTSUP;
579}
580
581static int cdfs_destroy_node(fs_node_t *fn)
582{
583 /* Read-only */
584 return ENOTSUP;
585}
586
587static int cdfs_link_node(fs_node_t *pfn, fs_node_t *cfn, const char *name)
588{
589 /* Read-only */
590 return ENOTSUP;
591}
592
593static int cdfs_unlink_node(fs_node_t *pfn, fs_node_t *cfn, const char *name)
594{
595 /* Read-only */
596 return ENOTSUP;
597}
598
599static int cdfs_has_children(bool *has_children, fs_node_t *fn)
600{
601 cdfs_node_t *node = CDFS_NODE(fn);
602
603 if ((node->type == CDFS_DIRECTORY) && (!node->processed))
604 cdfs_readdir(node->service_id, fn);
605
606 *has_children = !list_empty(&node->cs_list);
607 return EOK;
608}
609
610static fs_index_t cdfs_index_get(fs_node_t *fn)
611{
612 cdfs_node_t *node = CDFS_NODE(fn);
613 return node->index;
614}
615
616static aoff64_t cdfs_size_get(fs_node_t *fn)
617{
618 cdfs_node_t *node = CDFS_NODE(fn);
619 return node->size;
620}
621
622static unsigned int cdfs_lnkcnt_get(fs_node_t *fn)
623{
624 cdfs_node_t *node = CDFS_NODE(fn);
625 return node->lnkcnt;
626}
627
628static bool cdfs_is_directory(fs_node_t *fn)
629{
630 cdfs_node_t *node = CDFS_NODE(fn);
631 return (node->type == CDFS_DIRECTORY);
632}
633
634static bool cdfs_is_file(fs_node_t *fn)
635{
636 cdfs_node_t *node = CDFS_NODE(fn);
637 return (node->type == CDFS_FILE);
638}
639
640static service_id_t cdfs_service_get(fs_node_t *fn)
641{
642 return 0;
643}
644
645libfs_ops_t cdfs_libfs_ops = {
646 .root_get = cdfs_root_get,
647 .match = cdfs_match,
648 .node_get = cdfs_node_get,
649 .node_open = cdfs_node_open,
650 .node_put = cdfs_node_put,
651 .create = cdfs_create_node,
652 .destroy = cdfs_destroy_node,
653 .link = cdfs_link_node,
654 .unlink = cdfs_unlink_node,
655 .has_children = cdfs_has_children,
656 .index_get = cdfs_index_get,
657 .size_get = cdfs_size_get,
658 .lnkcnt_get = cdfs_lnkcnt_get,
659 .is_directory = cdfs_is_directory,
660 .is_file = cdfs_is_file,
661 .service_get = cdfs_service_get
662};
663
664static bool iso_readfs(service_id_t service_id, fs_node_t *rfn,
665 cdfs_lba_t altroot)
666{
667 /* First 16 blocks of isofs are empty */
668 block_t *block;
669 int rc = block_get(&block, service_id, altroot + 16, BLOCK_FLAGS_NONE);
670 if (rc != EOK)
671 return false;
672
673 cdfs_vol_desc_t *vol_desc = (cdfs_vol_desc_t *) block->data;
674
675 /*
676 * Test for primary volume descriptor
677 * and standard compliance.
678 */
679 if ((vol_desc->type != VOL_DESC_PRIMARY) ||
680 (bcmp(vol_desc->standard_ident, CDFS_STANDARD_IDENT, 5) != 0) ||
681 (vol_desc->version != 1)) {
682 block_put(block);
683 return false;
684 }
685
686 uint16_t set_size = uint16_lb(vol_desc->data.primary.set_size);
687 if (set_size > 1) {
688 /*
689 * Technically, we don't support multi-disc sets.
690 * But one can encounter erroneously mastered
691 * images in the wild and it might actually work
692 * for the first disc in the set.
693 */
694 }
695
696 uint16_t sequence_nr = uint16_lb(vol_desc->data.primary.sequence_nr);
697 if (sequence_nr != 1) {
698 /*
699 * We only support the first disc
700 * in multi-disc sets.
701 */
702 block_put(block);
703 return false;
704 }
705
706 uint16_t block_size = uint16_lb(vol_desc->data.primary.block_size);
707 if (block_size != BLOCK_SIZE) {
708 block_put(block);
709 return false;
710 }
711
712 // TODO: implement path table support
713
714 cdfs_node_t *node = CDFS_NODE(rfn);
715 node->lba = uint32_lb(vol_desc->data.primary.root_dir.lba);
716 node->size = uint32_lb(vol_desc->data.primary.root_dir.size);
717
718 if (!cdfs_readdir(service_id, rfn)) {
719 block_put(block);
720 return false;
721 }
722
723 block_put(block);
724 return true;
725}
726
727/* Mount a session with session start offset
728 *
729 */
730static bool cdfs_instance_init(service_id_t service_id, cdfs_lba_t altroot)
731{
732 /* Create root node */
733 fs_node_t *rfn;
734 int rc = create_node(&rfn, service_id, L_DIRECTORY, cdfs_index++);
735
736 if ((rc != EOK) || (!rfn))
737 return false;
738
739 /* FS root is not linked */
740 CDFS_NODE(rfn)->lnkcnt = 0;
741 CDFS_NODE(rfn)->lba = 0;
742 CDFS_NODE(rfn)->processed = false;
743
744 /* Check if there is cdfs in given session */
745 if (!iso_readfs(service_id, rfn, altroot)) {
746 // XXX destroy node
747 return false;
748 }
749
750 return true;
751}
752
753static int cdfs_mounted(service_id_t service_id, const char *opts,
754 fs_index_t *index, aoff64_t *size, unsigned int *lnkcnt)
755{
756 /* Initialize the block layer */
757 int rc = block_init(EXCHANGE_SERIALIZE, service_id, BLOCK_SIZE);
758 if (rc != EOK)
759 return rc;
760
761 cdfs_lba_t altroot = 0;
762
763 if (str_lcmp(opts, "altroot=", 8) == 0) {
764 /* User-defined alternative root on a multi-session disk */
765 if (str_uint32_t(opts + 8, NULL, 0, false, &altroot) != EOK)
766 altroot = 0;
767 } else {
768 /* Read TOC and find the last session */
769 toc_block_t *toc = block_get_toc(service_id, 1);
770 if ((toc != NULL) && (uint16_t_be2host(toc->size) == 10)) {
771 altroot = uint32_t_be2host(toc->first_lba);
772 free(toc);
773 }
774 }
775
776 /* Initialize the block cache */
777 rc = block_cache_init(service_id, BLOCK_SIZE, 0, CACHE_MODE_WT);
778 if (rc != EOK) {
779 block_fini(service_id);
780 return rc;
781 }
782
783 /* Check if this device is not already mounted */
784 fs_node_t *rootfn;
785 rc = cdfs_root_get(&rootfn, service_id);
786 if ((rc == EOK) && (rootfn)) {
787 cdfs_node_put(rootfn);
788 block_cache_fini(service_id);
789 block_fini(service_id);
790
791 return EEXIST;
792 }
793
794 /* Initialize cdfs instance */
795 if (!cdfs_instance_init(service_id, altroot)) {
796 block_cache_fini(service_id);
797 block_fini(service_id);
798
799 return ENOMEM;
800 }
801
802 rc = cdfs_root_get(&rootfn, service_id);
803 assert(rc == EOK);
804
805 cdfs_node_t *root = CDFS_NODE(rootfn);
806 *index = root->index;
807 *size = root->size;
808 *lnkcnt = root->lnkcnt;
809
810 return EOK;
811}
812
813static void cdfs_instance_done(service_id_t service_id)
814{
815 unsigned long key[] = {
816 [NODES_KEY_SRVC] = service_id
817 };
818
819 hash_table_remove(&nodes, key, 1);
820 block_cache_fini(service_id);
821 block_fini(service_id);
822}
823
824static int cdfs_unmounted(service_id_t service_id)
825{
826 cdfs_instance_done(service_id);
827 return EOK;
828}
829
830static int cdfs_read(service_id_t service_id, fs_index_t index, aoff64_t pos,
831 size_t *rbytes)
832{
833 unsigned long key[] = {
834 [NODES_KEY_SRVC] = service_id,
835 [NODES_KEY_INDEX] = index
836 };
837
838 link_t *link = hash_table_find(&nodes, key);
839 if (link == NULL)
840 return ENOENT;
841
842 cdfs_node_t *node =
843 hash_table_get_instance(link, cdfs_node_t, nh_link);
844
845 if (!node->processed) {
846 int rc = cdfs_readdir(service_id, FS_NODE(node));
847 if (rc != EOK)
848 return rc;
849 }
850
851 ipc_callid_t callid;
852 size_t len;
853 if (!async_data_read_receive(&callid, &len)) {
854 async_answer_0(callid, EINVAL);
855 return EINVAL;
856 }
857
858 if (node->type == CDFS_FILE) {
859 if (pos >= node->size) {
860 *rbytes = 0;
861 async_data_read_finalize(callid, NULL, 0);
862 } else {
863 cdfs_lba_t lba = pos / BLOCK_SIZE;
864 size_t offset = pos % BLOCK_SIZE;
865
866 *rbytes = min(len, BLOCK_SIZE - offset);
867 *rbytes = min(*rbytes, node->size - pos);
868
869 block_t *block;
870 int rc = block_get(&block, service_id, node->lba + lba,
871 BLOCK_FLAGS_NONE);
872 if (rc != EOK) {
873 async_answer_0(callid, rc);
874 return rc;
875 }
876
877 async_data_read_finalize(callid, block->data + offset,
878 *rbytes);
879 rc = block_put(block);
880 if (rc != EOK)
881 return rc;
882 }
883 } else {
884 link_t *link = list_nth(&node->cs_list, pos);
885 if (link == NULL) {
886 async_answer_0(callid, ENOENT);
887 return ENOENT;
888 }
889
890 cdfs_dentry_t *dentry =
891 list_get_instance(link, cdfs_dentry_t, link);
892
893 *rbytes = 1;
894 async_data_read_finalize(callid, dentry->name,
895 str_size(dentry->name) + 1);
896 }
897
898 return EOK;
899}
900
901static int cdfs_write(service_id_t service_id, fs_index_t index, aoff64_t pos,
902 size_t *wbytes, aoff64_t *nsize)
903{
904 /*
905 * As cdfs is a read-only filesystem,
906 * the operation is not supported.
907 */
908
909 return ENOTSUP;
910}
911
912static int cdfs_truncate(service_id_t service_id, fs_index_t index,
913 aoff64_t size)
914{
915 /*
916 * As cdfs is a read-only filesystem,
917 * the operation is not supported.
918 */
919
920 return ENOTSUP;
921}
922
923static bool cache_remove_closed(link_t *item, void *arg)
924{
925 size_t *premove_cnt = (size_t*)arg;
926
927 /* Some nodes were requested to be removed from the cache. */
928 if (0 < *premove_cnt) {
929 cdfs_node_t *node = hash_table_get_instance(item, cdfs_node_t, nh_link);
930
931 if (!node->opened) {
932 hash_table_remove_item(&nodes, item);
933
934 --nodes_cached;
935 --*premove_cnt;
936 }
937 }
938
939 /* Only continue if more nodes were requested to be removed. */
940 return 0 < *premove_cnt;
941}
942
943static void cleanup_cache(service_id_t service_id)
944{
945 if (nodes_cached > NODE_CACHE_SIZE) {
946 size_t remove_cnt = nodes_cached - NODE_CACHE_SIZE;
947
948 if (0 < remove_cnt)
949 hash_table_apply(&nodes, cache_remove_closed, &remove_cnt);
950 }
951}
952
953static int cdfs_close(service_id_t service_id, fs_index_t index)
954{
955 /* Root node is always in memory */
956 if (index == 0)
957 return EOK;
958
959 unsigned long key[] = {
960 [NODES_KEY_SRVC] = service_id,
961 [NODES_KEY_INDEX] = index
962 };
963
964 link_t *link = hash_table_find(&nodes, key);
965 if (link == 0)
966 return ENOENT;
967
968 cdfs_node_t *node =
969 hash_table_get_instance(link, cdfs_node_t, nh_link);
970
971 assert(node->opened > 0);
972
973 node->opened--;
974 cleanup_cache(service_id);
975
976 return EOK;
977}
978
979static int cdfs_destroy(service_id_t service_id, fs_index_t index)
980{
981 /*
982 * As cdfs is a read-only filesystem,
983 * the operation is not supported.
984 */
985
986 return ENOTSUP;
987}
988
989static int cdfs_sync(service_id_t service_id, fs_index_t index)
990{
991 /*
992 * As cdfs is a read-only filesystem,
993 * the sync operation is a no-op.
994 */
995
996 return EOK;
997}
998
999vfs_out_ops_t cdfs_ops = {
1000 .mounted = cdfs_mounted,
1001 .unmounted = cdfs_unmounted,
1002 .read = cdfs_read,
1003 .write = cdfs_write,
1004 .truncate = cdfs_truncate,
1005 .close = cdfs_close,
1006 .destroy = cdfs_destroy,
1007 .sync = cdfs_sync
1008};
1009
1010/** Initialize the cdfs server
1011 *
1012 */
1013bool cdfs_init(void)
1014{
1015 if (!hash_table_create(&nodes, 0, 2, &nodes_ops))
1016 return false;
1017
1018 return true;
1019}
1020
1021/**
1022 * @}
1023 */
Note: See TracBrowser for help on using the repository browser.