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

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

Use NULL instead of 0 as a hash_table_ops_t member initializer.

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