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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 781408e was 781408e, checked in by Manuele Conti <conti.ma@…>, 12 years ago

Implement free and total count operation for cdfs and udf filesystem.

  • Property mode set to 100644
File size: 22.4 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
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
197 ht_link_t nh_link; /**< Nodes hash table link */
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
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)
228{
229 ht_key_t *key = (ht_key_t*)k;
230 return hash_combine(key->service_id, key->index);
231}
232
233static size_t nodes_hash(const ht_link_t *item)
234{
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;
243
244 return key->service_id == node->service_id && key->index == node->index;
245}
246
247static void nodes_remove_callback(ht_link_t *item)
248{
249 cdfs_node_t *node = hash_table_get_inst(item, cdfs_node_t, nh_link);
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 */
265static hash_table_ops_t nodes_ops = {
266 .hash = nodes_hash,
267 .key_hash = nodes_key_hash,
268 .key_equal = nodes_key_equal,
269 .equal = NULL,
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{
276 ht_key_t key = {
277 .index = index,
278 .service_id = service_id
279 };
280
281 ht_link_t *link = hash_table_find(&nodes, &key);
282 if (link) {
283 cdfs_node_t *node =
284 hash_table_get_inst(link, cdfs_node_t, nh_link);
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. */
351 hash_table_insert(&nodes, &node->nh_link);
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{
501 ht_key_t key = {
502 .index = index,
503 .service_id = service_id
504 };
505
506 ht_link_t *link = hash_table_find(&nodes, &key);
507 if (link) {
508 cdfs_node_t *node =
509 hash_table_get_inst(link, cdfs_node_t, nh_link);
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
622static service_id_t cdfs_service_get(fs_node_t *fn)
623{
624 return 0;
625}
626
627static int cdfs_size_block(service_id_t service_id, uint32_t *size)
628{
629 *size = BLOCK_SIZE;
630
631 return EOK;
632}
633
634static int cdfs_total_block_count(service_id_t service_id, uint64_t *count)
635{
636 *count = 0;
637
638 return EOK;
639}
640
641static int cdfs_free_block_count(service_id_t service_id, uint64_t *count)
642{
643 *count = 0;
644
645 return EOK;
646}
647
648libfs_ops_t cdfs_libfs_ops = {
649 .root_get = cdfs_root_get,
650 .match = cdfs_match,
651 .node_get = cdfs_node_get,
652 .node_open = cdfs_node_open,
653 .node_put = cdfs_node_put,
654 .create = cdfs_create_node,
655 .destroy = cdfs_destroy_node,
656 .link = cdfs_link_node,
657 .unlink = cdfs_unlink_node,
658 .has_children = cdfs_has_children,
659 .index_get = cdfs_index_get,
660 .size_get = cdfs_size_get,
661 .lnkcnt_get = cdfs_lnkcnt_get,
662 .is_directory = cdfs_is_directory,
663 .is_file = cdfs_is_file,
664 .service_get = cdfs_service_get,
665 .size_block = cdfs_size_block,
666 .total_block_count = cdfs_total_block_count,
667 .free_block_count = cdfs_free_block_count
668};
669
670static bool iso_readfs(service_id_t service_id, fs_node_t *rfn,
671 cdfs_lba_t altroot)
672{
673 /* First 16 blocks of isofs are empty */
674 block_t *block;
675 int rc = block_get(&block, service_id, altroot + 16, BLOCK_FLAGS_NONE);
676 if (rc != EOK)
677 return false;
678
679 cdfs_vol_desc_t *vol_desc = (cdfs_vol_desc_t *) block->data;
680
681 /*
682 * Test for primary volume descriptor
683 * and standard compliance.
684 */
685 if ((vol_desc->type != VOL_DESC_PRIMARY) ||
686 (memcmp(vol_desc->standard_ident, CDFS_STANDARD_IDENT, 5) != 0) ||
687 (vol_desc->version != 1)) {
688 block_put(block);
689 return false;
690 }
691
692 uint16_t set_size = uint16_lb(vol_desc->data.primary.set_size);
693 if (set_size > 1) {
694 /*
695 * Technically, we don't support multi-disc sets.
696 * But one can encounter erroneously mastered
697 * images in the wild and it might actually work
698 * for the first disc in the set.
699 */
700 }
701
702 uint16_t sequence_nr = uint16_lb(vol_desc->data.primary.sequence_nr);
703 if (sequence_nr != 1) {
704 /*
705 * We only support the first disc
706 * in multi-disc sets.
707 */
708 block_put(block);
709 return false;
710 }
711
712 uint16_t block_size = uint16_lb(vol_desc->data.primary.block_size);
713 if (block_size != BLOCK_SIZE) {
714 block_put(block);
715 return false;
716 }
717
718 // TODO: implement path table support
719
720 cdfs_node_t *node = CDFS_NODE(rfn);
721 node->lba = uint32_lb(vol_desc->data.primary.root_dir.lba);
722 node->size = uint32_lb(vol_desc->data.primary.root_dir.size);
723
724 if (!cdfs_readdir(service_id, rfn)) {
725 block_put(block);
726 return false;
727 }
728
729 block_put(block);
730 return true;
731}
732
733/* Mount a session with session start offset
734 *
735 */
736static bool cdfs_instance_init(service_id_t service_id, cdfs_lba_t altroot)
737{
738 /* Create root node */
739 fs_node_t *rfn;
740 int rc = create_node(&rfn, service_id, L_DIRECTORY, cdfs_index++);
741
742 if ((rc != EOK) || (!rfn))
743 return false;
744
745 /* FS root is not linked */
746 CDFS_NODE(rfn)->lnkcnt = 0;
747 CDFS_NODE(rfn)->lba = 0;
748 CDFS_NODE(rfn)->processed = false;
749
750 /* Check if there is cdfs in given session */
751 if (!iso_readfs(service_id, rfn, altroot)) {
752 // XXX destroy node
753 return false;
754 }
755
756 return true;
757}
758
759static int cdfs_mounted(service_id_t service_id, const char *opts,
760 fs_index_t *index, aoff64_t *size, unsigned int *lnkcnt)
761{
762 /* Initialize the block layer */
763 int rc = block_init(EXCHANGE_SERIALIZE, service_id, BLOCK_SIZE);
764 if (rc != EOK)
765 return rc;
766
767 cdfs_lba_t altroot = 0;
768
769 if (str_lcmp(opts, "altroot=", 8) == 0) {
770 /* User-defined alternative root on a multi-session disk */
771 if (str_uint32_t(opts + 8, NULL, 0, false, &altroot) != EOK)
772 altroot = 0;
773 } else {
774 /* Read TOC and find the last session */
775 toc_block_t *toc = block_get_toc(service_id, 1);
776 if ((toc != NULL) && (uint16_t_be2host(toc->size) == 10)) {
777 altroot = uint32_t_be2host(toc->first_lba);
778 free(toc);
779 }
780 }
781
782 /* Initialize the block cache */
783 rc = block_cache_init(service_id, BLOCK_SIZE, 0, CACHE_MODE_WT);
784 if (rc != EOK) {
785 block_fini(service_id);
786 return rc;
787 }
788
789 /* Check if this device is not already mounted */
790 fs_node_t *rootfn;
791 rc = cdfs_root_get(&rootfn, service_id);
792 if ((rc == EOK) && (rootfn)) {
793 cdfs_node_put(rootfn);
794 block_cache_fini(service_id);
795 block_fini(service_id);
796
797 return EEXIST;
798 }
799
800 /* Initialize cdfs instance */
801 if (!cdfs_instance_init(service_id, altroot)) {
802 block_cache_fini(service_id);
803 block_fini(service_id);
804
805 return ENOMEM;
806 }
807
808 rc = cdfs_root_get(&rootfn, service_id);
809 assert(rc == EOK);
810
811 cdfs_node_t *root = CDFS_NODE(rootfn);
812 *index = root->index;
813 *size = root->size;
814 *lnkcnt = root->lnkcnt;
815
816 return EOK;
817}
818
819static bool rm_service_id_nodes(ht_link_t *item, void *arg)
820{
821 service_id_t service_id = *(service_id_t*)arg;
822 cdfs_node_t *node = hash_table_get_inst(item, cdfs_node_t, nh_link);
823
824 if (node->service_id == service_id) {
825 hash_table_remove_item(&nodes, &node->nh_link);
826 }
827
828 return true;
829}
830
831static void cdfs_instance_done(service_id_t service_id)
832{
833 hash_table_apply(&nodes, rm_service_id_nodes, &service_id);
834 block_cache_fini(service_id);
835 block_fini(service_id);
836}
837
838static int cdfs_unmounted(service_id_t service_id)
839{
840 cdfs_instance_done(service_id);
841 return EOK;
842}
843
844static int cdfs_read(service_id_t service_id, fs_index_t index, aoff64_t pos,
845 size_t *rbytes)
846{
847 ht_key_t key = {
848 .index = index,
849 .service_id = service_id
850 };
851
852 ht_link_t *link = hash_table_find(&nodes, &key);
853 if (link == NULL)
854 return ENOENT;
855
856 cdfs_node_t *node =
857 hash_table_get_inst(link, cdfs_node_t, nh_link);
858
859 if (!node->processed) {
860 int rc = cdfs_readdir(service_id, FS_NODE(node));
861 if (rc != EOK)
862 return rc;
863 }
864
865 ipc_callid_t callid;
866 size_t len;
867 if (!async_data_read_receive(&callid, &len)) {
868 async_answer_0(callid, EINVAL);
869 return EINVAL;
870 }
871
872 if (node->type == CDFS_FILE) {
873 if (pos >= node->size) {
874 *rbytes = 0;
875 async_data_read_finalize(callid, NULL, 0);
876 } else {
877 cdfs_lba_t lba = pos / BLOCK_SIZE;
878 size_t offset = pos % BLOCK_SIZE;
879
880 *rbytes = min(len, BLOCK_SIZE - offset);
881 *rbytes = min(*rbytes, node->size - pos);
882
883 block_t *block;
884 int rc = block_get(&block, service_id, node->lba + lba,
885 BLOCK_FLAGS_NONE);
886 if (rc != EOK) {
887 async_answer_0(callid, rc);
888 return rc;
889 }
890
891 async_data_read_finalize(callid, block->data + offset,
892 *rbytes);
893 rc = block_put(block);
894 if (rc != EOK)
895 return rc;
896 }
897 } else {
898 link_t *link = list_nth(&node->cs_list, pos);
899 if (link == NULL) {
900 async_answer_0(callid, ENOENT);
901 return ENOENT;
902 }
903
904 cdfs_dentry_t *dentry =
905 list_get_instance(link, cdfs_dentry_t, link);
906
907 *rbytes = 1;
908 async_data_read_finalize(callid, dentry->name,
909 str_size(dentry->name) + 1);
910 }
911
912 return EOK;
913}
914
915static int cdfs_write(service_id_t service_id, fs_index_t index, aoff64_t pos,
916 size_t *wbytes, aoff64_t *nsize)
917{
918 /*
919 * As cdfs is a read-only filesystem,
920 * the operation is not supported.
921 */
922
923 return ENOTSUP;
924}
925
926static int cdfs_truncate(service_id_t service_id, fs_index_t index,
927 aoff64_t size)
928{
929 /*
930 * As cdfs is a read-only filesystem,
931 * the operation is not supported.
932 */
933
934 return ENOTSUP;
935}
936
937static bool cache_remove_closed(ht_link_t *item, void *arg)
938{
939 size_t *premove_cnt = (size_t*)arg;
940
941 /* Some nodes were requested to be removed from the cache. */
942 if (0 < *premove_cnt) {
943 cdfs_node_t *node = hash_table_get_inst(item, cdfs_node_t, nh_link);
944
945 if (!node->opened) {
946 hash_table_remove_item(&nodes, item);
947
948 --nodes_cached;
949 --*premove_cnt;
950 }
951 }
952
953 /* Only continue if more nodes were requested to be removed. */
954 return 0 < *premove_cnt;
955}
956
957static void cleanup_cache(service_id_t service_id)
958{
959 if (nodes_cached > NODE_CACHE_SIZE) {
960 size_t remove_cnt = nodes_cached - NODE_CACHE_SIZE;
961
962 if (0 < remove_cnt)
963 hash_table_apply(&nodes, cache_remove_closed, &remove_cnt);
964 }
965}
966
967static int cdfs_close(service_id_t service_id, fs_index_t index)
968{
969 /* Root node is always in memory */
970 if (index == 0)
971 return EOK;
972
973 ht_key_t key = {
974 .index = index,
975 .service_id = service_id
976 };
977
978 ht_link_t *link = hash_table_find(&nodes, &key);
979 if (link == 0)
980 return ENOENT;
981
982 cdfs_node_t *node =
983 hash_table_get_inst(link, cdfs_node_t, nh_link);
984
985 assert(node->opened > 0);
986
987 node->opened--;
988 cleanup_cache(service_id);
989
990 return EOK;
991}
992
993static int cdfs_destroy(service_id_t service_id, fs_index_t index)
994{
995 /*
996 * As cdfs is a read-only filesystem,
997 * the operation is not supported.
998 */
999
1000 return ENOTSUP;
1001}
1002
1003static int cdfs_sync(service_id_t service_id, fs_index_t index)
1004{
1005 /*
1006 * As cdfs is a read-only filesystem,
1007 * the sync operation is a no-op.
1008 */
1009
1010 return EOK;
1011}
1012
1013vfs_out_ops_t cdfs_ops = {
1014 .mounted = cdfs_mounted,
1015 .unmounted = cdfs_unmounted,
1016 .read = cdfs_read,
1017 .write = cdfs_write,
1018 .truncate = cdfs_truncate,
1019 .close = cdfs_close,
1020 .destroy = cdfs_destroy,
1021 .sync = cdfs_sync
1022};
1023
1024/** Initialize the cdfs server
1025 *
1026 */
1027bool cdfs_init(void)
1028{
1029 if (!hash_table_create(&nodes, 0, 0, &nodes_ops))
1030 return false;
1031
1032 return true;
1033}
1034
1035/**
1036 * @}
1037 */
Note: See TracBrowser for help on using the repository browser.