source: mainline/uspace/srv/fs/cdfs/cdfs_ops.c@ 8e77b12f

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 8e77b12f was 8e77b12f, checked in by Jiri Svoboda <jiri@…>, 11 years ago

Skip special CDFS directory entries. Correctly detect the last one.

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