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

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

Decode CDFS file names.

  • Property mode set to 100644
File size: 23.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 "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
398/** Decode file name.
399 *
400 * @param data File name buffer
401 * @param dsize Fine name buffer size
402 * @param dtype Directory entry type
403 * @return Decoded file name (allocated string)
404 */
405static char *cdfs_decode_name(void *data, size_t dsize,
406 cdfs_dentry_type_t dtype)
407{
408 char *name;
409 char *dot;
410 char *scolon;
411
412 name = malloc(dsize + 1);
413 if (name == NULL)
414 return NULL;
415 memcpy(name, data, dsize);
416 name[dsize] = '\0';
417
418 if (dtype == CDFS_DIRECTORY)
419 return name;
420
421 dot = str_chr(name, '.');
422 if (dot == NULL)
423 return NULL;
424 scolon = str_chr(dot, ';');
425 if (scolon == NULL)
426 return NULL;
427
428 /* Trim version part */
429 *scolon = '\0';
430
431 /* If the extension is an empty string, trim the dot separator. */
432 if (dot[1] == '\0')
433 *dot = '\0';
434
435 return name;
436}
437
438static bool cdfs_readdir(service_id_t service_id, fs_node_t *fs_node)
439{
440 cdfs_node_t *node = CDFS_NODE(fs_node);
441 assert(node);
442
443 if (node->processed)
444 return true;
445
446 uint32_t blocks = node->size / BLOCK_SIZE;
447 if ((node->size % BLOCK_SIZE) != 0)
448 blocks++;
449
450 for (uint32_t i = 0; i < blocks; i++) {
451 block_t *block;
452 int rc = block_get(&block, service_id, node->lba + i, BLOCK_FLAGS_NONE);
453 if (rc != EOK)
454 return false;
455
456 cdfs_dir_t *dir;
457
458 for (size_t offset = 0; offset < BLOCK_SIZE;
459 offset += dir->length) {
460 dir = (cdfs_dir_t *) (block->data + offset);
461 if (dir->length == 0)
462 break;
463 if (offset + dir->length > BLOCK_SIZE) {
464 /* XXX Incorrect FS structure */
465 break;
466 }
467
468 cdfs_dentry_type_t dentry_type;
469 if (dir->flags & DIR_FLAG_DIRECTORY)
470 dentry_type = CDFS_DIRECTORY;
471 else
472 dentry_type = CDFS_FILE;
473
474 /* Skip special entries */
475
476 if (dir->name_length == 1 &&
477 dir->name[0] == CDFS_NAME_CURDIR)
478 continue;
479 if (dir->name_length == 1 &&
480 dir->name[0] == CDFS_NAME_PARENTDIR)
481 continue;
482
483 // FIXME: hack - indexing by dentry byte offset on disc
484
485 fs_node_t *fn;
486 int rc = create_node(&fn, service_id, dentry_type,
487 (node->lba + i) * BLOCK_SIZE + offset);
488 if ((rc != EOK) || (fn == NULL))
489 return false;
490
491 cdfs_node_t *cur = CDFS_NODE(fn);
492 cur->lba = uint32_lb(dir->lba);
493 cur->size = uint32_lb(dir->size);
494
495 char *name = cdfs_decode_name(dir->name,
496 dir->name_length, dentry_type);
497 if (name == NULL)
498 return false;
499
500 // FIXME: check return value
501
502 link_node(fs_node, fn, name);
503 free(name);
504
505 if (dentry_type == CDFS_FILE)
506 cur->processed = true;
507 }
508
509 block_put(block);
510 }
511
512 node->processed = true;
513 return true;
514}
515
516static fs_node_t *get_uncached_node(service_id_t service_id, fs_index_t index)
517{
518 cdfs_lba_t lba = index / BLOCK_SIZE;
519 size_t offset = index % BLOCK_SIZE;
520
521 block_t *block;
522 int rc = block_get(&block, service_id, lba, BLOCK_FLAGS_NONE);
523 if (rc != EOK)
524 return NULL;
525
526 cdfs_dir_t *dir = (cdfs_dir_t *) (block->data + offset);
527
528 cdfs_dentry_type_t dentry_type;
529 if (dir->flags & DIR_FLAG_DIRECTORY)
530 dentry_type = CDFS_DIRECTORY;
531 else
532 dentry_type = CDFS_FILE;
533
534 fs_node_t *fn;
535 rc = create_node(&fn, service_id, dentry_type, index);
536 if ((rc != EOK) || (fn == NULL))
537 return NULL;
538
539 cdfs_node_t *node = CDFS_NODE(fn);
540 node->lba = uint32_lb(dir->lba);
541 node->size = uint32_lb(dir->size);
542 node->lnkcnt = 1;
543
544 if (dentry_type == CDFS_FILE)
545 node->processed = true;
546
547 block_put(block);
548 return fn;
549}
550
551static fs_node_t *get_cached_node(service_id_t service_id, fs_index_t index)
552{
553 ht_key_t key = {
554 .index = index,
555 .service_id = service_id
556 };
557
558 ht_link_t *link = hash_table_find(&nodes, &key);
559 if (link) {
560 cdfs_node_t *node =
561 hash_table_get_inst(link, cdfs_node_t, nh_link);
562 return FS_NODE(node);
563 }
564
565 return get_uncached_node(service_id, index);
566}
567
568static int cdfs_match(fs_node_t **fn, fs_node_t *pfn, const char *component)
569{
570 cdfs_node_t *parent = CDFS_NODE(pfn);
571
572 if (!parent->processed) {
573 int rc = cdfs_readdir(parent->service_id, pfn);
574 if (rc != EOK)
575 return rc;
576 }
577
578 list_foreach(parent->cs_list, link, cdfs_dentry_t, dentry) {
579 if (str_cmp(dentry->name, component) == 0) {
580 *fn = get_cached_node(parent->service_id, dentry->index);
581 return EOK;
582 }
583 }
584
585 *fn = NULL;
586 return EOK;
587}
588
589static int cdfs_node_open(fs_node_t *fn)
590{
591 cdfs_node_t *node = CDFS_NODE(fn);
592
593 if (!node->processed)
594 cdfs_readdir(node->service_id, fn);
595
596 node->opened++;
597 return EOK;
598}
599
600static int cdfs_node_put(fs_node_t *fn)
601{
602 /* Nothing to do */
603 return EOK;
604}
605
606static int cdfs_create_node(fs_node_t **fn, service_id_t service_id, int lflag)
607{
608 /* Read-only */
609 return ENOTSUP;
610}
611
612static int cdfs_destroy_node(fs_node_t *fn)
613{
614 /* Read-only */
615 return ENOTSUP;
616}
617
618static int cdfs_link_node(fs_node_t *pfn, fs_node_t *cfn, const char *name)
619{
620 /* Read-only */
621 return ENOTSUP;
622}
623
624static int cdfs_unlink_node(fs_node_t *pfn, fs_node_t *cfn, const char *name)
625{
626 /* Read-only */
627 return ENOTSUP;
628}
629
630static int cdfs_has_children(bool *has_children, fs_node_t *fn)
631{
632 cdfs_node_t *node = CDFS_NODE(fn);
633
634 if ((node->type == CDFS_DIRECTORY) && (!node->processed))
635 cdfs_readdir(node->service_id, fn);
636
637 *has_children = !list_empty(&node->cs_list);
638 return EOK;
639}
640
641static fs_index_t cdfs_index_get(fs_node_t *fn)
642{
643 cdfs_node_t *node = CDFS_NODE(fn);
644 return node->index;
645}
646
647static aoff64_t cdfs_size_get(fs_node_t *fn)
648{
649 cdfs_node_t *node = CDFS_NODE(fn);
650 return node->size;
651}
652
653static unsigned int cdfs_lnkcnt_get(fs_node_t *fn)
654{
655 cdfs_node_t *node = CDFS_NODE(fn);
656 return node->lnkcnt;
657}
658
659static bool cdfs_is_directory(fs_node_t *fn)
660{
661 cdfs_node_t *node = CDFS_NODE(fn);
662 return (node->type == CDFS_DIRECTORY);
663}
664
665static bool cdfs_is_file(fs_node_t *fn)
666{
667 cdfs_node_t *node = CDFS_NODE(fn);
668 return (node->type == CDFS_FILE);
669}
670
671static service_id_t cdfs_service_get(fs_node_t *fn)
672{
673 return 0;
674}
675
676static int cdfs_size_block(service_id_t service_id, uint32_t *size)
677{
678 *size = BLOCK_SIZE;
679
680 return EOK;
681}
682
683static int cdfs_total_block_count(service_id_t service_id, uint64_t *count)
684{
685 *count = 0;
686
687 return EOK;
688}
689
690static int cdfs_free_block_count(service_id_t service_id, uint64_t *count)
691{
692 *count = 0;
693
694 return EOK;
695}
696
697libfs_ops_t cdfs_libfs_ops = {
698 .root_get = cdfs_root_get,
699 .match = cdfs_match,
700 .node_get = cdfs_node_get,
701 .node_open = cdfs_node_open,
702 .node_put = cdfs_node_put,
703 .create = cdfs_create_node,
704 .destroy = cdfs_destroy_node,
705 .link = cdfs_link_node,
706 .unlink = cdfs_unlink_node,
707 .has_children = cdfs_has_children,
708 .index_get = cdfs_index_get,
709 .size_get = cdfs_size_get,
710 .lnkcnt_get = cdfs_lnkcnt_get,
711 .is_directory = cdfs_is_directory,
712 .is_file = cdfs_is_file,
713 .service_get = cdfs_service_get,
714 .size_block = cdfs_size_block,
715 .total_block_count = cdfs_total_block_count,
716 .free_block_count = cdfs_free_block_count
717};
718
719static bool iso_readfs(service_id_t service_id, fs_node_t *rfn,
720 cdfs_lba_t altroot)
721{
722 /* First 16 blocks of isofs are empty */
723 block_t *block;
724 int rc = block_get(&block, service_id, altroot + 16, BLOCK_FLAGS_NONE);
725 if (rc != EOK)
726 return false;
727
728 cdfs_vol_desc_t *vol_desc = (cdfs_vol_desc_t *) block->data;
729
730 /*
731 * Test for primary volume descriptor
732 * and standard compliance.
733 */
734 if ((vol_desc->type != VOL_DESC_PRIMARY) ||
735 (memcmp(vol_desc->standard_ident, CDFS_STANDARD_IDENT, 5) != 0) ||
736 (vol_desc->version != 1)) {
737 block_put(block);
738 return false;
739 }
740
741 uint16_t set_size = uint16_lb(vol_desc->data.primary.set_size);
742 if (set_size > 1) {
743 /*
744 * Technically, we don't support multi-disc sets.
745 * But one can encounter erroneously mastered
746 * images in the wild and it might actually work
747 * for the first disc in the set.
748 */
749 }
750
751 uint16_t sequence_nr = uint16_lb(vol_desc->data.primary.sequence_nr);
752 if (sequence_nr != 1) {
753 /*
754 * We only support the first disc
755 * in multi-disc sets.
756 */
757 block_put(block);
758 return false;
759 }
760
761 uint16_t block_size = uint16_lb(vol_desc->data.primary.block_size);
762 if (block_size != BLOCK_SIZE) {
763 block_put(block);
764 return false;
765 }
766
767 // TODO: implement path table support
768
769 cdfs_node_t *node = CDFS_NODE(rfn);
770 node->lba = uint32_lb(vol_desc->data.primary.root_dir.lba);
771 node->size = uint32_lb(vol_desc->data.primary.root_dir.size);
772
773 if (!cdfs_readdir(service_id, rfn)) {
774 block_put(block);
775 return false;
776 }
777
778 block_put(block);
779 return true;
780}
781
782/* Mount a session with session start offset
783 *
784 */
785static bool cdfs_instance_init(service_id_t service_id, cdfs_lba_t altroot)
786{
787 /* Create root node */
788 fs_node_t *rfn;
789 int rc = create_node(&rfn, service_id, L_DIRECTORY, cdfs_index++);
790
791 if ((rc != EOK) || (!rfn))
792 return false;
793
794 /* FS root is not linked */
795 CDFS_NODE(rfn)->lnkcnt = 0;
796 CDFS_NODE(rfn)->lba = 0;
797 CDFS_NODE(rfn)->processed = false;
798
799 /* Check if there is cdfs in given session */
800 if (!iso_readfs(service_id, rfn, altroot)) {
801 // XXX destroy node
802 return false;
803 }
804
805 return true;
806}
807
808static int cdfs_mounted(service_id_t service_id, const char *opts,
809 fs_index_t *index, aoff64_t *size, unsigned int *lnkcnt)
810{
811 /* Initialize the block layer */
812 int rc = block_init(EXCHANGE_SERIALIZE, service_id, BLOCK_SIZE);
813 if (rc != EOK)
814 return rc;
815
816 cdfs_lba_t altroot = 0;
817
818 if (str_lcmp(opts, "altroot=", 8) == 0) {
819 /* User-defined alternative root on a multi-session disk */
820 if (str_uint32_t(opts + 8, NULL, 0, false, &altroot) != EOK)
821 altroot = 0;
822 } else {
823 /* Read TOC and find the last session */
824 toc_block_t *toc = block_get_toc(service_id, 1);
825 if ((toc != NULL) && (uint16_t_be2host(toc->size) == 10)) {
826 altroot = uint32_t_be2host(toc->first_lba);
827 free(toc);
828 }
829 }
830
831 /* Initialize the block cache */
832 rc = block_cache_init(service_id, BLOCK_SIZE, 0, CACHE_MODE_WT);
833 if (rc != EOK) {
834 block_fini(service_id);
835 return rc;
836 }
837
838 /* Check if this device is not already mounted */
839 fs_node_t *rootfn;
840 rc = cdfs_root_get(&rootfn, service_id);
841 if ((rc == EOK) && (rootfn)) {
842 cdfs_node_put(rootfn);
843 block_cache_fini(service_id);
844 block_fini(service_id);
845
846 return EEXIST;
847 }
848
849 /* Initialize cdfs instance */
850 if (!cdfs_instance_init(service_id, altroot)) {
851 block_cache_fini(service_id);
852 block_fini(service_id);
853
854 return ENOMEM;
855 }
856
857 rc = cdfs_root_get(&rootfn, service_id);
858 assert(rc == EOK);
859
860 cdfs_node_t *root = CDFS_NODE(rootfn);
861 *index = root->index;
862 *size = root->size;
863 *lnkcnt = root->lnkcnt;
864
865 return EOK;
866}
867
868static bool rm_service_id_nodes(ht_link_t *item, void *arg)
869{
870 service_id_t service_id = *(service_id_t*)arg;
871 cdfs_node_t *node = hash_table_get_inst(item, cdfs_node_t, nh_link);
872
873 if (node->service_id == service_id) {
874 hash_table_remove_item(&nodes, &node->nh_link);
875 }
876
877 return true;
878}
879
880static void cdfs_instance_done(service_id_t service_id)
881{
882 hash_table_apply(&nodes, rm_service_id_nodes, &service_id);
883 block_cache_fini(service_id);
884 block_fini(service_id);
885}
886
887static int cdfs_unmounted(service_id_t service_id)
888{
889 cdfs_instance_done(service_id);
890 return EOK;
891}
892
893static int cdfs_read(service_id_t service_id, fs_index_t index, aoff64_t pos,
894 size_t *rbytes)
895{
896 ht_key_t key = {
897 .index = index,
898 .service_id = service_id
899 };
900
901 ht_link_t *link = hash_table_find(&nodes, &key);
902 if (link == NULL)
903 return ENOENT;
904
905 cdfs_node_t *node =
906 hash_table_get_inst(link, cdfs_node_t, nh_link);
907
908 if (!node->processed) {
909 int rc = cdfs_readdir(service_id, FS_NODE(node));
910 if (rc != EOK)
911 return rc;
912 }
913
914 ipc_callid_t callid;
915 size_t len;
916 if (!async_data_read_receive(&callid, &len)) {
917 async_answer_0(callid, EINVAL);
918 return EINVAL;
919 }
920
921 if (node->type == CDFS_FILE) {
922 if (pos >= node->size) {
923 *rbytes = 0;
924 async_data_read_finalize(callid, NULL, 0);
925 } else {
926 cdfs_lba_t lba = pos / BLOCK_SIZE;
927 size_t offset = pos % BLOCK_SIZE;
928
929 *rbytes = min(len, BLOCK_SIZE - offset);
930 *rbytes = min(*rbytes, node->size - pos);
931
932 block_t *block;
933 int rc = block_get(&block, service_id, node->lba + lba,
934 BLOCK_FLAGS_NONE);
935 if (rc != EOK) {
936 async_answer_0(callid, rc);
937 return rc;
938 }
939
940 async_data_read_finalize(callid, block->data + offset,
941 *rbytes);
942 rc = block_put(block);
943 if (rc != EOK)
944 return rc;
945 }
946 } else {
947 link_t *link = list_nth(&node->cs_list, pos);
948 if (link == NULL) {
949 async_answer_0(callid, ENOENT);
950 return ENOENT;
951 }
952
953 cdfs_dentry_t *dentry =
954 list_get_instance(link, cdfs_dentry_t, link);
955
956 *rbytes = 1;
957 async_data_read_finalize(callid, dentry->name,
958 str_size(dentry->name) + 1);
959 }
960
961 return EOK;
962}
963
964static int cdfs_write(service_id_t service_id, fs_index_t index, aoff64_t pos,
965 size_t *wbytes, aoff64_t *nsize)
966{
967 /*
968 * As cdfs is a read-only filesystem,
969 * the operation is not supported.
970 */
971
972 return ENOTSUP;
973}
974
975static int cdfs_truncate(service_id_t service_id, fs_index_t index,
976 aoff64_t size)
977{
978 /*
979 * As cdfs is a read-only filesystem,
980 * the operation is not supported.
981 */
982
983 return ENOTSUP;
984}
985
986static bool cache_remove_closed(ht_link_t *item, void *arg)
987{
988 size_t *premove_cnt = (size_t*)arg;
989
990 /* Some nodes were requested to be removed from the cache. */
991 if (0 < *premove_cnt) {
992 cdfs_node_t *node = hash_table_get_inst(item, cdfs_node_t, nh_link);
993
994 if (!node->opened) {
995 hash_table_remove_item(&nodes, item);
996
997 --nodes_cached;
998 --*premove_cnt;
999 }
1000 }
1001
1002 /* Only continue if more nodes were requested to be removed. */
1003 return 0 < *premove_cnt;
1004}
1005
1006static void cleanup_cache(service_id_t service_id)
1007{
1008 if (nodes_cached > NODE_CACHE_SIZE) {
1009 size_t remove_cnt = nodes_cached - NODE_CACHE_SIZE;
1010
1011 if (0 < remove_cnt)
1012 hash_table_apply(&nodes, cache_remove_closed, &remove_cnt);
1013 }
1014}
1015
1016static int cdfs_close(service_id_t service_id, fs_index_t index)
1017{
1018 /* Root node is always in memory */
1019 if (index == 0)
1020 return EOK;
1021
1022 ht_key_t key = {
1023 .index = index,
1024 .service_id = service_id
1025 };
1026
1027 ht_link_t *link = hash_table_find(&nodes, &key);
1028 if (link == 0)
1029 return ENOENT;
1030
1031 cdfs_node_t *node =
1032 hash_table_get_inst(link, cdfs_node_t, nh_link);
1033
1034 assert(node->opened > 0);
1035
1036 node->opened--;
1037 cleanup_cache(service_id);
1038
1039 return EOK;
1040}
1041
1042static int cdfs_destroy(service_id_t service_id, fs_index_t index)
1043{
1044 /*
1045 * As cdfs is a read-only filesystem,
1046 * the operation is not supported.
1047 */
1048
1049 return ENOTSUP;
1050}
1051
1052static int cdfs_sync(service_id_t service_id, fs_index_t index)
1053{
1054 /*
1055 * As cdfs is a read-only filesystem,
1056 * the sync operation is a no-op.
1057 */
1058
1059 return EOK;
1060}
1061
1062vfs_out_ops_t cdfs_ops = {
1063 .mounted = cdfs_mounted,
1064 .unmounted = cdfs_unmounted,
1065 .read = cdfs_read,
1066 .write = cdfs_write,
1067 .truncate = cdfs_truncate,
1068 .close = cdfs_close,
1069 .destroy = cdfs_destroy,
1070 .sync = cdfs_sync
1071};
1072
1073/** Initialize the cdfs server
1074 *
1075 */
1076bool cdfs_init(void)
1077{
1078 if (!hash_table_create(&nodes, 0, 0, &nodes_ops))
1079 return false;
1080
1081 return true;
1082}
1083
1084/**
1085 * @}
1086 */
Note: See TracBrowser for help on using the repository browser.