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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since f73b291 was f73b291, checked in by Martin Decky <martin@…>, 13 years ago

libblock.{c|h} → block.{c|h}

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