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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 90782c36 was 758f8d5, checked in by Maurizio Lombardi <m.lombardi85@…>, 12 years ago

merge the df branch

  • Property mode set to 100644
File size: 22.3 KB
Line 
1/*
2 * Copyright (c) 2011 Martin Decky
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup fs
30 * @{
31 */
32
33/**
34 * @file cdfs_ops.c
35 * @brief Implementation of VFS operations for the cdfs file system
36 * server.
37 */
38
39#include "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, cdfs_dentry_t, dentry) {
368 if (str_cmp(dentry->name, name) == 0)
369 return EEXIST;
370 }
371
372 /* Allocate and initialize the dentry */
373 cdfs_dentry_t *dentry = malloc(sizeof(cdfs_dentry_t));
374 if (!dentry)
375 return ENOMEM;
376
377 /* Populate and link the new dentry */
378 dentry->name = str_dup(name);
379 if (dentry->name == NULL) {
380 free(dentry);
381 return ENOMEM;
382 }
383
384 link_initialize(&dentry->link);
385 dentry->index = node->index;
386
387 node->lnkcnt++;
388 list_append(&dentry->link, &parent->cs_list);
389
390 return EOK;
391}
392
393static bool cdfs_readdir(service_id_t service_id, fs_node_t *fs_node)
394{
395 cdfs_node_t *node = CDFS_NODE(fs_node);
396 assert(node);
397
398 if (node->processed)
399 return true;
400
401 uint32_t blocks = node->size / BLOCK_SIZE;
402 if ((node->size % BLOCK_SIZE) != 0)
403 blocks++;
404
405 for (uint32_t i = 0; i < blocks; i++) {
406 block_t *block;
407 int rc = block_get(&block, service_id, node->lba + i, BLOCK_FLAGS_NONE);
408 if (rc != EOK)
409 return false;
410
411 cdfs_dir_t *dir = (cdfs_dir_t *) block->data;
412
413 // FIXME: skip '.' and '..'
414
415 for (size_t offset = 0;
416 (dir->length != 0) && (offset < BLOCK_SIZE);
417 offset += dir->length) {
418 dir = (cdfs_dir_t *) (block->data + offset);
419
420 cdfs_dentry_type_t dentry_type;
421 if (dir->flags & DIR_FLAG_DIRECTORY)
422 dentry_type = CDFS_DIRECTORY;
423 else
424 dentry_type = CDFS_FILE;
425
426 // FIXME: hack - indexing by dentry byte offset on disc
427
428 fs_node_t *fn;
429 int rc = create_node(&fn, service_id, dentry_type,
430 (node->lba + i) * BLOCK_SIZE + offset);
431 if ((rc != EOK) || (fn == NULL))
432 return false;
433
434 cdfs_node_t *cur = CDFS_NODE(fn);
435 cur->lba = uint32_lb(dir->lba);
436 cur->size = uint32_lb(dir->size);
437
438 char *name = (char *) malloc(dir->name_length + 1);
439 if (name == NULL)
440 return false;
441
442 memcpy(name, dir->name, dir->name_length);
443 name[dir->name_length] = 0;
444
445 // FIXME: check return value
446
447 link_node(fs_node, fn, name);
448 free(name);
449
450 if (dentry_type == CDFS_FILE)
451 cur->processed = true;
452 }
453
454 block_put(block);
455 }
456
457 node->processed = true;
458 return true;
459}
460
461static fs_node_t *get_uncached_node(service_id_t service_id, fs_index_t index)
462{
463 cdfs_lba_t lba = index / BLOCK_SIZE;
464 size_t offset = index % BLOCK_SIZE;
465
466 block_t *block;
467 int rc = block_get(&block, service_id, lba, BLOCK_FLAGS_NONE);
468 if (rc != EOK)
469 return NULL;
470
471 cdfs_dir_t *dir = (cdfs_dir_t *) (block->data + offset);
472
473 cdfs_dentry_type_t dentry_type;
474 if (dir->flags & DIR_FLAG_DIRECTORY)
475 dentry_type = CDFS_DIRECTORY;
476 else
477 dentry_type = CDFS_FILE;
478
479 fs_node_t *fn;
480 rc = create_node(&fn, service_id, dentry_type, index);
481 if ((rc != EOK) || (fn == NULL))
482 return NULL;
483
484 cdfs_node_t *node = CDFS_NODE(fn);
485 node->lba = uint32_lb(dir->lba);
486 node->size = uint32_lb(dir->size);
487 node->lnkcnt = 1;
488
489 if (dentry_type == CDFS_FILE)
490 node->processed = true;
491
492 block_put(block);
493 return fn;
494}
495
496static fs_node_t *get_cached_node(service_id_t service_id, fs_index_t index)
497{
498 ht_key_t key = {
499 .index = index,
500 .service_id = service_id
501 };
502
503 ht_link_t *link = hash_table_find(&nodes, &key);
504 if (link) {
505 cdfs_node_t *node =
506 hash_table_get_inst(link, cdfs_node_t, nh_link);
507 return FS_NODE(node);
508 }
509
510 return get_uncached_node(service_id, index);
511}
512
513static int cdfs_match(fs_node_t **fn, fs_node_t *pfn, const char *component)
514{
515 cdfs_node_t *parent = CDFS_NODE(pfn);
516
517 if (!parent->processed) {
518 int rc = cdfs_readdir(parent->service_id, pfn);
519 if (rc != EOK)
520 return rc;
521 }
522
523 list_foreach(parent->cs_list, link, cdfs_dentry_t, dentry) {
524 if (str_cmp(dentry->name, component) == 0) {
525 *fn = get_cached_node(parent->service_id, dentry->index);
526 return EOK;
527 }
528 }
529
530 *fn = NULL;
531 return EOK;
532}
533
534static int cdfs_node_open(fs_node_t *fn)
535{
536 cdfs_node_t *node = CDFS_NODE(fn);
537
538 if (!node->processed)
539 cdfs_readdir(node->service_id, fn);
540
541 node->opened++;
542 return EOK;
543}
544
545static int cdfs_node_put(fs_node_t *fn)
546{
547 /* Nothing to do */
548 return EOK;
549}
550
551static int cdfs_create_node(fs_node_t **fn, service_id_t service_id, int lflag)
552{
553 /* Read-only */
554 return ENOTSUP;
555}
556
557static int cdfs_destroy_node(fs_node_t *fn)
558{
559 /* Read-only */
560 return ENOTSUP;
561}
562
563static int cdfs_link_node(fs_node_t *pfn, fs_node_t *cfn, const char *name)
564{
565 /* Read-only */
566 return ENOTSUP;
567}
568
569static int cdfs_unlink_node(fs_node_t *pfn, fs_node_t *cfn, const char *name)
570{
571 /* Read-only */
572 return ENOTSUP;
573}
574
575static int cdfs_has_children(bool *has_children, fs_node_t *fn)
576{
577 cdfs_node_t *node = CDFS_NODE(fn);
578
579 if ((node->type == CDFS_DIRECTORY) && (!node->processed))
580 cdfs_readdir(node->service_id, fn);
581
582 *has_children = !list_empty(&node->cs_list);
583 return EOK;
584}
585
586static fs_index_t cdfs_index_get(fs_node_t *fn)
587{
588 cdfs_node_t *node = CDFS_NODE(fn);
589 return node->index;
590}
591
592static aoff64_t cdfs_size_get(fs_node_t *fn)
593{
594 cdfs_node_t *node = CDFS_NODE(fn);
595 return node->size;
596}
597
598static unsigned int cdfs_lnkcnt_get(fs_node_t *fn)
599{
600 cdfs_node_t *node = CDFS_NODE(fn);
601 return node->lnkcnt;
602}
603
604static bool cdfs_is_directory(fs_node_t *fn)
605{
606 cdfs_node_t *node = CDFS_NODE(fn);
607 return (node->type == CDFS_DIRECTORY);
608}
609
610static bool cdfs_is_file(fs_node_t *fn)
611{
612 cdfs_node_t *node = CDFS_NODE(fn);
613 return (node->type == CDFS_FILE);
614}
615
616static service_id_t cdfs_service_get(fs_node_t *fn)
617{
618 return 0;
619}
620
621static int cdfs_size_block(service_id_t service_id, uint32_t *size)
622{
623 *size = BLOCK_SIZE;
624
625 return EOK;
626}
627
628static int cdfs_total_block_count(service_id_t service_id, uint64_t *count)
629{
630 *count = 0;
631
632 return EOK;
633}
634
635static int cdfs_free_block_count(service_id_t service_id, uint64_t *count)
636{
637 *count = 0;
638
639 return EOK;
640}
641
642libfs_ops_t cdfs_libfs_ops = {
643 .root_get = cdfs_root_get,
644 .match = cdfs_match,
645 .node_get = cdfs_node_get,
646 .node_open = cdfs_node_open,
647 .node_put = cdfs_node_put,
648 .create = cdfs_create_node,
649 .destroy = cdfs_destroy_node,
650 .link = cdfs_link_node,
651 .unlink = cdfs_unlink_node,
652 .has_children = cdfs_has_children,
653 .index_get = cdfs_index_get,
654 .size_get = cdfs_size_get,
655 .lnkcnt_get = cdfs_lnkcnt_get,
656 .is_directory = cdfs_is_directory,
657 .is_file = cdfs_is_file,
658 .service_get = cdfs_service_get,
659 .size_block = cdfs_size_block,
660 .total_block_count = cdfs_total_block_count,
661 .free_block_count = cdfs_free_block_count
662};
663
664static bool iso_readfs(service_id_t service_id, fs_node_t *rfn,
665 cdfs_lba_t altroot)
666{
667 /* First 16 blocks of isofs are empty */
668 block_t *block;
669 int rc = block_get(&block, service_id, altroot + 16, BLOCK_FLAGS_NONE);
670 if (rc != EOK)
671 return false;
672
673 cdfs_vol_desc_t *vol_desc = (cdfs_vol_desc_t *) block->data;
674
675 /*
676 * Test for primary volume descriptor
677 * and standard compliance.
678 */
679 if ((vol_desc->type != VOL_DESC_PRIMARY) ||
680 (memcmp(vol_desc->standard_ident, CDFS_STANDARD_IDENT, 5) != 0) ||
681 (vol_desc->version != 1)) {
682 block_put(block);
683 return false;
684 }
685
686 uint16_t set_size = uint16_lb(vol_desc->data.primary.set_size);
687 if (set_size > 1) {
688 /*
689 * Technically, we don't support multi-disc sets.
690 * But one can encounter erroneously mastered
691 * images in the wild and it might actually work
692 * for the first disc in the set.
693 */
694 }
695
696 uint16_t sequence_nr = uint16_lb(vol_desc->data.primary.sequence_nr);
697 if (sequence_nr != 1) {
698 /*
699 * We only support the first disc
700 * in multi-disc sets.
701 */
702 block_put(block);
703 return false;
704 }
705
706 uint16_t block_size = uint16_lb(vol_desc->data.primary.block_size);
707 if (block_size != BLOCK_SIZE) {
708 block_put(block);
709 return false;
710 }
711
712 // TODO: implement path table support
713
714 cdfs_node_t *node = CDFS_NODE(rfn);
715 node->lba = uint32_lb(vol_desc->data.primary.root_dir.lba);
716 node->size = uint32_lb(vol_desc->data.primary.root_dir.size);
717
718 if (!cdfs_readdir(service_id, rfn)) {
719 block_put(block);
720 return false;
721 }
722
723 block_put(block);
724 return true;
725}
726
727/* Mount a session with session start offset
728 *
729 */
730static bool cdfs_instance_init(service_id_t service_id, cdfs_lba_t altroot)
731{
732 /* Create root node */
733 fs_node_t *rfn;
734 int rc = create_node(&rfn, service_id, L_DIRECTORY, cdfs_index++);
735
736 if ((rc != EOK) || (!rfn))
737 return false;
738
739 /* FS root is not linked */
740 CDFS_NODE(rfn)->lnkcnt = 0;
741 CDFS_NODE(rfn)->lba = 0;
742 CDFS_NODE(rfn)->processed = false;
743
744 /* Check if there is cdfs in given session */
745 if (!iso_readfs(service_id, rfn, altroot)) {
746 // XXX destroy node
747 return false;
748 }
749
750 return true;
751}
752
753static int cdfs_mounted(service_id_t service_id, const char *opts,
754 fs_index_t *index, aoff64_t *size, unsigned int *lnkcnt)
755{
756 /* Initialize the block layer */
757 int rc = block_init(EXCHANGE_SERIALIZE, service_id, BLOCK_SIZE);
758 if (rc != EOK)
759 return rc;
760
761 cdfs_lba_t altroot = 0;
762
763 if (str_lcmp(opts, "altroot=", 8) == 0) {
764 /* User-defined alternative root on a multi-session disk */
765 if (str_uint32_t(opts + 8, NULL, 0, false, &altroot) != EOK)
766 altroot = 0;
767 } else {
768 /* Read TOC and find the last session */
769 toc_block_t *toc = block_get_toc(service_id, 1);
770 if ((toc != NULL) && (uint16_t_be2host(toc->size) == 10)) {
771 altroot = uint32_t_be2host(toc->first_lba);
772 free(toc);
773 }
774 }
775
776 /* Initialize the block cache */
777 rc = block_cache_init(service_id, BLOCK_SIZE, 0, CACHE_MODE_WT);
778 if (rc != EOK) {
779 block_fini(service_id);
780 return rc;
781 }
782
783 /* Check if this device is not already mounted */
784 fs_node_t *rootfn;
785 rc = cdfs_root_get(&rootfn, service_id);
786 if ((rc == EOK) && (rootfn)) {
787 cdfs_node_put(rootfn);
788 block_cache_fini(service_id);
789 block_fini(service_id);
790
791 return EEXIST;
792 }
793
794 /* Initialize cdfs instance */
795 if (!cdfs_instance_init(service_id, altroot)) {
796 block_cache_fini(service_id);
797 block_fini(service_id);
798
799 return ENOMEM;
800 }
801
802 rc = cdfs_root_get(&rootfn, service_id);
803 assert(rc == EOK);
804
805 cdfs_node_t *root = CDFS_NODE(rootfn);
806 *index = root->index;
807 *size = root->size;
808 *lnkcnt = root->lnkcnt;
809
810 return EOK;
811}
812
813static bool rm_service_id_nodes(ht_link_t *item, void *arg)
814{
815 service_id_t service_id = *(service_id_t*)arg;
816 cdfs_node_t *node = hash_table_get_inst(item, cdfs_node_t, nh_link);
817
818 if (node->service_id == service_id) {
819 hash_table_remove_item(&nodes, &node->nh_link);
820 }
821
822 return true;
823}
824
825static void cdfs_instance_done(service_id_t service_id)
826{
827 hash_table_apply(&nodes, rm_service_id_nodes, &service_id);
828 block_cache_fini(service_id);
829 block_fini(service_id);
830}
831
832static int cdfs_unmounted(service_id_t service_id)
833{
834 cdfs_instance_done(service_id);
835 return EOK;
836}
837
838static int cdfs_read(service_id_t service_id, fs_index_t index, aoff64_t pos,
839 size_t *rbytes)
840{
841 ht_key_t key = {
842 .index = index,
843 .service_id = service_id
844 };
845
846 ht_link_t *link = hash_table_find(&nodes, &key);
847 if (link == NULL)
848 return ENOENT;
849
850 cdfs_node_t *node =
851 hash_table_get_inst(link, cdfs_node_t, nh_link);
852
853 if (!node->processed) {
854 int rc = cdfs_readdir(service_id, FS_NODE(node));
855 if (rc != EOK)
856 return rc;
857 }
858
859 ipc_callid_t callid;
860 size_t len;
861 if (!async_data_read_receive(&callid, &len)) {
862 async_answer_0(callid, EINVAL);
863 return EINVAL;
864 }
865
866 if (node->type == CDFS_FILE) {
867 if (pos >= node->size) {
868 *rbytes = 0;
869 async_data_read_finalize(callid, NULL, 0);
870 } else {
871 cdfs_lba_t lba = pos / BLOCK_SIZE;
872 size_t offset = pos % BLOCK_SIZE;
873
874 *rbytes = min(len, BLOCK_SIZE - offset);
875 *rbytes = min(*rbytes, node->size - pos);
876
877 block_t *block;
878 int rc = block_get(&block, service_id, node->lba + lba,
879 BLOCK_FLAGS_NONE);
880 if (rc != EOK) {
881 async_answer_0(callid, rc);
882 return rc;
883 }
884
885 async_data_read_finalize(callid, block->data + offset,
886 *rbytes);
887 rc = block_put(block);
888 if (rc != EOK)
889 return rc;
890 }
891 } else {
892 link_t *link = list_nth(&node->cs_list, pos);
893 if (link == NULL) {
894 async_answer_0(callid, ENOENT);
895 return ENOENT;
896 }
897
898 cdfs_dentry_t *dentry =
899 list_get_instance(link, cdfs_dentry_t, link);
900
901 *rbytes = 1;
902 async_data_read_finalize(callid, dentry->name,
903 str_size(dentry->name) + 1);
904 }
905
906 return EOK;
907}
908
909static int cdfs_write(service_id_t service_id, fs_index_t index, aoff64_t pos,
910 size_t *wbytes, aoff64_t *nsize)
911{
912 /*
913 * As cdfs is a read-only filesystem,
914 * the operation is not supported.
915 */
916
917 return ENOTSUP;
918}
919
920static int cdfs_truncate(service_id_t service_id, fs_index_t index,
921 aoff64_t size)
922{
923 /*
924 * As cdfs is a read-only filesystem,
925 * the operation is not supported.
926 */
927
928 return ENOTSUP;
929}
930
931static bool cache_remove_closed(ht_link_t *item, void *arg)
932{
933 size_t *premove_cnt = (size_t*)arg;
934
935 /* Some nodes were requested to be removed from the cache. */
936 if (0 < *premove_cnt) {
937 cdfs_node_t *node = hash_table_get_inst(item, cdfs_node_t, nh_link);
938
939 if (!node->opened) {
940 hash_table_remove_item(&nodes, item);
941
942 --nodes_cached;
943 --*premove_cnt;
944 }
945 }
946
947 /* Only continue if more nodes were requested to be removed. */
948 return 0 < *premove_cnt;
949}
950
951static void cleanup_cache(service_id_t service_id)
952{
953 if (nodes_cached > NODE_CACHE_SIZE) {
954 size_t remove_cnt = nodes_cached - NODE_CACHE_SIZE;
955
956 if (0 < remove_cnt)
957 hash_table_apply(&nodes, cache_remove_closed, &remove_cnt);
958 }
959}
960
961static int cdfs_close(service_id_t service_id, fs_index_t index)
962{
963 /* Root node is always in memory */
964 if (index == 0)
965 return EOK;
966
967 ht_key_t key = {
968 .index = index,
969 .service_id = service_id
970 };
971
972 ht_link_t *link = hash_table_find(&nodes, &key);
973 if (link == 0)
974 return ENOENT;
975
976 cdfs_node_t *node =
977 hash_table_get_inst(link, cdfs_node_t, nh_link);
978
979 assert(node->opened > 0);
980
981 node->opened--;
982 cleanup_cache(service_id);
983
984 return EOK;
985}
986
987static int cdfs_destroy(service_id_t service_id, fs_index_t index)
988{
989 /*
990 * As cdfs is a read-only filesystem,
991 * the operation is not supported.
992 */
993
994 return ENOTSUP;
995}
996
997static int cdfs_sync(service_id_t service_id, fs_index_t index)
998{
999 /*
1000 * As cdfs is a read-only filesystem,
1001 * the sync operation is a no-op.
1002 */
1003
1004 return EOK;
1005}
1006
1007vfs_out_ops_t cdfs_ops = {
1008 .mounted = cdfs_mounted,
1009 .unmounted = cdfs_unmounted,
1010 .read = cdfs_read,
1011 .write = cdfs_write,
1012 .truncate = cdfs_truncate,
1013 .close = cdfs_close,
1014 .destroy = cdfs_destroy,
1015 .sync = cdfs_sync
1016};
1017
1018/** Initialize the cdfs server
1019 *
1020 */
1021bool cdfs_init(void)
1022{
1023 if (!hash_table_create(&nodes, 0, 0, &nodes_ops))
1024 return false;
1025
1026 return true;
1027}
1028
1029/**
1030 * @}
1031 */
Note: See TracBrowser for help on using the repository browser.