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

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

do not expose the call capability handler from the async framework

Keep the call capability handler encapsulated within the async framework
and do not expose it explicitly via its API. Use the pointer to
ipc_call_t as the sole object identifying an IPC call in the code that
uses the async framework.

This plugs a major leak in the abstraction and also simplifies both the
async framework (slightly) and all IPC servers.

  • Property mode set to 100644
File size: 30.8 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"
[3e6a98c5]40#include <stdbool.h>
[0003e0f5]41#include <adt/list.h>
[a26895d]42#include <adt/hash_table.h>
[062d900]43#include <adt/hash.h>
[a26895d]44#include <mem.h>
45#include <loc.h>
46#include <libfs.h>
47#include <errno.h>
[f73b291]48#include <block.h>
[3abf70c7]49#include <scsi/mmc.h>
[38d150e]50#include <stdlib.h>
[a26895d]51#include <str.h>
52#include <byteorder.h>
53#include <macros.h>
[1c88835]54#include <unaligned.h>
55
[a26895d]56#include "cdfs.h"
57#include "cdfs_endian.h"
58
59/** Standard CD-ROM block size */
60#define BLOCK_SIZE 2048
61
[062d900]62#define NODE_CACHE_SIZE 200
[a26895d]63
64/** All root nodes have index 0 */
65#define CDFS_SOME_ROOT 0
66
67#define CDFS_NODE(node) ((node) ? (cdfs_node_t *)(node)->data : NULL)
68#define FS_NODE(node) ((node) ? (node)->fs_node : NULL)
69
70#define CDFS_STANDARD_IDENT "CD001"
71
[8e77b12f]72enum {
73 CDFS_NAME_CURDIR = '\x00',
74 CDFS_NAME_PARENTDIR = '\x01'
75};
76
[a26895d]77typedef enum {
78 VOL_DESC_BOOT = 0,
79 VOL_DESC_PRIMARY = 1,
80 VOL_DESC_SUPPLEMENTARY = 2,
81 VOL_DESC_VOL_PARTITION = 3,
82 VOL_DESC_SET_TERMINATOR = 255
83} vol_desc_type_t;
84
85typedef struct {
86 uint8_t system_ident[32];
87 uint8_t ident[32];
88} __attribute__((packed)) cdfs_vol_desc_boot_t;
89
90typedef struct {
91 uint8_t year[4];
92 uint8_t mon[2];
93 uint8_t day[2];
[a35b458]94
[a26895d]95 uint8_t hour[2];
96 uint8_t min[2];
97 uint8_t sec[2];
98 uint8_t msec[2];
[a35b458]99
[a26895d]100 uint8_t offset;
101} __attribute__((packed)) cdfs_datetime_t;
102
103typedef struct {
104 uint8_t year; /**< Since 1900 */
105 uint8_t mon;
106 uint8_t day;
[a35b458]107
[a26895d]108 uint8_t hour;
109 uint8_t min;
110 uint8_t sec;
[a35b458]111
[a26895d]112 uint8_t offset;
113} __attribute__((packed)) cdfs_timestamp_t;
114
115typedef enum {
116 DIR_FLAG_DIRECTORY = 2
117} cdfs_dir_flag_t;
118
[0cebbac]119/** Directory record */
[a26895d]120typedef struct {
121 uint8_t length;
122 uint8_t ea_length;
[a35b458]123
[a26895d]124 uint32_t_lb lba;
125 uint32_t_lb size;
[a35b458]126
[a26895d]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;
[a35b458]132
[a26895d]133 uint8_t name_length;
134 uint8_t name[];
135} __attribute__((packed)) cdfs_dir_t;
136
[0cebbac]137/** Directory record for the root directory */
138typedef struct {
139 uint8_t length;
140 uint8_t ea_length;
[a35b458]141
[0cebbac]142 uint32_t_lb lba;
143 uint32_t_lb size;
[a35b458]144
[0cebbac]145 cdfs_timestamp_t timestamp;
146 uint8_t flags;
147 uint8_t unit_size;
148 uint8_t gap_size;
149 uint16_t_lb sequence_nr;
[a35b458]150
[0cebbac]151 uint8_t name_length;
152 uint8_t name[1];
153} __attribute__((packed)) cdfs_root_dir_t;
154
[a26895d]155typedef struct {
[993d608]156 uint8_t flags; /* reserved in primary */
[a35b458]157
[a26895d]158 uint8_t system_ident[32];
159 uint8_t ident[32];
[a35b458]160
[a26895d]161 uint64_t res1;
162 uint32_t_lb lba_size;
[a35b458]163
[993d608]164 uint8_t esc_seq[32]; /* reserved in primary */
[a26895d]165 uint16_t_lb set_size;
166 uint16_t_lb sequence_nr;
[a35b458]167
[a26895d]168 uint16_t_lb block_size;
169 uint32_t_lb path_table_size;
[a35b458]170
[a26895d]171 uint32_t path_table_lsb;
172 uint32_t opt_path_table_lsb;
173 uint32_t path_table_msb;
174 uint32_t opt_path_table_msb;
[a35b458]175
[0cebbac]176 cdfs_root_dir_t root_dir;
177 uint8_t pad0;
[a35b458]178
[0cebbac]179 uint8_t set_ident[128];
180 uint8_t publisher_ident[128];
181 uint8_t preparer_ident[128];
182 uint8_t app_ident[128];
[a35b458]183
[0cebbac]184 uint8_t copyright_file_ident[37];
185 uint8_t abstract_file_ident[37];
186 uint8_t biblio_file_ident[37];
[a35b458]187
[0cebbac]188 cdfs_datetime_t creation;
189 cdfs_datetime_t modification;
190 cdfs_datetime_t expiration;
191 cdfs_datetime_t effective;
[a35b458]192
[0cebbac]193 uint8_t fs_version;
[993d608]194} __attribute__((packed)) cdfs_vol_desc_prisec_t;
[a26895d]195
196typedef struct {
197 uint8_t type;
198 uint8_t standard_ident[5];
199 uint8_t version;
200 union {
201 cdfs_vol_desc_boot_t boot;
[993d608]202 cdfs_vol_desc_prisec_t prisec;
[a26895d]203 } data;
204} __attribute__((packed)) cdfs_vol_desc_t;
205
[993d608]206typedef enum {
207 /** ASCII character set / encoding (base ISO 9660) */
208 enc_ascii,
209 /** UCS-2 character set / encoding (Joliet) */
210 enc_ucs2
211} cdfs_enc_t;
212
[a26895d]213typedef enum {
214 CDFS_NONE,
215 CDFS_FILE,
216 CDFS_DIRECTORY
217} cdfs_dentry_type_t;
218
219typedef struct {
220 link_t link; /**< Siblings list link */
221 fs_index_t index; /**< Node index */
222 char *name; /**< Dentry name */
223} cdfs_dentry_t;
224
225typedef uint32_t cdfs_lba_t;
226
[0003e0f5]227typedef struct {
[993d608]228 link_t link; /**< Link to list of all instances */
[0003e0f5]229 service_id_t service_id; /**< Service ID of block device */
[993d608]230 cdfs_enc_t enc; /**< Filesystem string encoding */
[9c96634]231 char *vol_ident; /**< Volume identifier */
[0003e0f5]232} cdfs_t;
233
[a26895d]234typedef struct {
235 fs_node_t *fs_node; /**< FS node */
236 fs_index_t index; /**< Node index */
[0003e0f5]237 cdfs_t *fs; /**< File system */
[a35b458]238
[062d900]239 ht_link_t nh_link; /**< Nodes hash table link */
[a26895d]240 cdfs_dentry_type_t type; /**< Dentry type */
[a35b458]241
[a26895d]242 unsigned int lnkcnt; /**< Link count */
243 uint32_t size; /**< File size if type is CDFS_FILE */
[a35b458]244
[a26895d]245 list_t cs_list; /**< Child's siblings list */
246 cdfs_lba_t lba; /**< LBA of data on disk */
247 bool processed; /**< If all children have been read */
248 unsigned int opened; /**< Opened count */
249} cdfs_node_t;
250
[993d608]251/** String encoding */
252enum {
253 /** ASCII - standard ISO 9660 */
254 ucs2_esc_seq_no = 3,
255 /** USC-2 - Joliet */
256 ucs2_esc_seq_len = 3
257};
258
259/** Joliet SVD UCS-2 escape sequences */
260static uint8_t ucs2_esc_seq[ucs2_esc_seq_no][ucs2_esc_seq_len] = {
261 { 0x25, 0x2f, 0x40 },
262 { 0x25, 0x2f, 0x43 },
263 { 0x25, 0x2f, 0x45 }
264};
265
[0003e0f5]266/** List of all instances */
267static LIST_INITIALIZE(cdfs_instances);
268
[a26895d]269/** Shared index of nodes */
270static fs_index_t cdfs_index = 1;
271
272/** Number of currently cached nodes */
273static size_t nodes_cached = 0;
274
275/** Hash table of all cdfs nodes */
276static hash_table_t nodes;
277
[1b20da0]278/*
[062d900]279 * Hash table support functions.
280 */
281
282typedef struct {
283 service_id_t service_id;
[3bacee1]284 fs_index_t index;
[062d900]285} ht_key_t;
286
287static size_t nodes_key_hash(void *k)
[a26895d]288{
[3bacee1]289 ht_key_t *key = (ht_key_t *)k;
[062d900]290 return hash_combine(key->service_id, key->index);
[a26895d]291}
292
[062d900]293static size_t nodes_hash(const ht_link_t *item)
[a26895d]294{
[062d900]295 cdfs_node_t *node = hash_table_get_inst(item, cdfs_node_t, nh_link);
[0003e0f5]296 return hash_combine(node->fs->service_id, node->index);
[062d900]297}
298
299static bool nodes_key_equal(void *k, const ht_link_t *item)
300{
301 cdfs_node_t *node = hash_table_get_inst(item, cdfs_node_t, nh_link);
[3bacee1]302 ht_key_t *key = (ht_key_t *)k;
[a35b458]303
[0003e0f5]304 return key->service_id == node->fs->service_id && key->index == node->index;
[a26895d]305}
306
[062d900]307static void nodes_remove_callback(ht_link_t *item)
[a26895d]308{
[062d900]309 cdfs_node_t *node = hash_table_get_inst(item, cdfs_node_t, nh_link);
[a35b458]310
[251d4dd]311 if (node->type == CDFS_DIRECTORY) {
312 link_t *link;
313 while ((link = list_first(&node->cs_list)) != NULL) {
314 cdfs_dentry_t *dentry = list_get_instance(link, cdfs_dentry_t, link);
315 list_remove(&dentry->link);
316 free(dentry);
317 }
[a26895d]318 }
[a35b458]319
[a26895d]320 free(node->fs_node);
321 free(node);
322}
323
324/** Nodes hash table operations */
[062d900]325static hash_table_ops_t nodes_ops = {
[a26895d]326 .hash = nodes_hash,
[062d900]327 .key_hash = nodes_key_hash,
328 .key_equal = nodes_key_equal,
[4e00f87]329 .equal = NULL,
[a26895d]330 .remove_callback = nodes_remove_callback
331};
332
[b7fd2a0]333static errno_t cdfs_node_get(fs_node_t **rfn, service_id_t service_id,
[a26895d]334 fs_index_t index)
335{
[062d900]336 ht_key_t key = {
337 .index = index,
338 .service_id = service_id
[a26895d]339 };
[a35b458]340
[062d900]341 ht_link_t *link = hash_table_find(&nodes, &key);
[a26895d]342 if (link) {
343 cdfs_node_t *node =
[062d900]344 hash_table_get_inst(link, cdfs_node_t, nh_link);
[a35b458]345
[a26895d]346 *rfn = FS_NODE(node);
347 } else
348 *rfn = NULL;
[a35b458]349
[a26895d]350 return EOK;
351}
352
[b7fd2a0]353static errno_t cdfs_root_get(fs_node_t **rfn, service_id_t service_id)
[a26895d]354{
355 return cdfs_node_get(rfn, service_id, CDFS_SOME_ROOT);
356}
357
358static void cdfs_node_initialize(cdfs_node_t *node)
359{
360 node->fs_node = NULL;
361 node->index = 0;
[0003e0f5]362 node->fs = NULL;
[a26895d]363 node->type = CDFS_NONE;
364 node->lnkcnt = 0;
365 node->size = 0;
366 node->lba = 0;
367 node->processed = false;
368 node->opened = 0;
[a35b458]369
[a26895d]370 list_initialize(&node->cs_list);
371}
372
[b7fd2a0]373static errno_t create_node(fs_node_t **rfn, cdfs_t *fs, int lflag,
[a26895d]374 fs_index_t index)
375{
376 assert((lflag & L_FILE) ^ (lflag & L_DIRECTORY));
[a35b458]377
[a26895d]378 cdfs_node_t *node = malloc(sizeof(cdfs_node_t));
379 if (!node)
380 return ENOMEM;
[a35b458]381
[a26895d]382 cdfs_node_initialize(node);
[a35b458]383
[a26895d]384 node->fs_node = malloc(sizeof(fs_node_t));
385 if (!node->fs_node) {
386 free(node);
387 return ENOMEM;
388 }
[a35b458]389
[a26895d]390 fs_node_initialize(node->fs_node);
391 node->fs_node->data = node;
[a35b458]392
[a26895d]393 fs_node_t *rootfn;
[b7fd2a0]394 errno_t rc = cdfs_root_get(&rootfn, fs->service_id);
[a35b458]395
[a26895d]396 assert(rc == EOK);
[a35b458]397
[a26895d]398 if (!rootfn)
399 node->index = CDFS_SOME_ROOT;
400 else
401 node->index = index;
[a35b458]402
[0003e0f5]403 node->fs = fs;
[a35b458]404
[a26895d]405 if (lflag & L_DIRECTORY)
406 node->type = CDFS_DIRECTORY;
407 else
408 node->type = CDFS_FILE;
[a35b458]409
[a26895d]410 /* Insert the new node into the nodes hash table. */
[062d900]411 hash_table_insert(&nodes, &node->nh_link);
[a35b458]412
[a26895d]413 *rfn = FS_NODE(node);
414 nodes_cached++;
[a35b458]415
[a26895d]416 return EOK;
417}
418
[b7fd2a0]419static errno_t link_node(fs_node_t *pfn, fs_node_t *fn, const char *name)
[a26895d]420{
421 cdfs_node_t *parent = CDFS_NODE(pfn);
422 cdfs_node_t *node = CDFS_NODE(fn);
[a35b458]423
[a26895d]424 assert(parent->type == CDFS_DIRECTORY);
[a35b458]425
[a26895d]426 /* Check for duplicate entries */
[feeac0d]427 list_foreach(parent->cs_list, link, cdfs_dentry_t, dentry) {
[a26895d]428 if (str_cmp(dentry->name, name) == 0)
429 return EEXIST;
430 }
[a35b458]431
[a26895d]432 /* Allocate and initialize the dentry */
433 cdfs_dentry_t *dentry = malloc(sizeof(cdfs_dentry_t));
434 if (!dentry)
435 return ENOMEM;
[a35b458]436
[a26895d]437 /* Populate and link the new dentry */
438 dentry->name = str_dup(name);
439 if (dentry->name == NULL) {
440 free(dentry);
441 return ENOMEM;
442 }
[a35b458]443
[a26895d]444 link_initialize(&dentry->link);
445 dentry->index = node->index;
[a35b458]446
[a26895d]447 node->lnkcnt++;
448 list_append(&dentry->link, &parent->cs_list);
[a35b458]449
[a26895d]450 return EOK;
451}
452
[993d608]453/** Decode CDFS string.
454 *
455 * @param data Pointer to string data
456 * @param dsize Size of data in bytes
457 * @param enc String encoding
458 * @return Decoded string
459 */
460static char *cdfs_decode_str(void *data, size_t dsize, cdfs_enc_t enc)
461{
[b7fd2a0]462 errno_t rc;
[993d608]463 char *str;
464 uint16_t *buf;
[a35b458]465
[993d608]466 switch (enc) {
467 case enc_ascii:
468 str = malloc(dsize + 1);
469 if (str == NULL)
470 return NULL;
471 memcpy(str, data, dsize);
472 str[dsize] = '\0';
473 break;
474 case enc_ucs2:
475 buf = calloc(dsize + 2, 1);
476 if (buf == NULL)
477 return NULL;
[a35b458]478
[993d608]479 size_t i;
480 for (i = 0; i < dsize / sizeof(uint16_t); i++) {
[1c88835]481 buf[i] = uint16_t_be2host(
482 ((unaligned_uint16_t *)data)[i]);
[993d608]483 }
[a35b458]484
[993d608]485 size_t dstr_size = dsize / sizeof(uint16_t) * 4 + 1;
486 str = malloc(dstr_size);
487 if (str == NULL)
488 return NULL;
[a35b458]489
[993d608]490 rc = utf16_to_str(str, dstr_size, buf);
491 free(buf);
[a35b458]492
[993d608]493 if (rc != EOK)
494 return NULL;
495 break;
496 default:
497 assert(false);
498 str = NULL;
499 }
[a35b458]500
[993d608]501 return str;
502}
503
[d4b63fa]504/** Decode file name.
505 *
506 * @param data File name buffer
507 * @param dsize Fine name buffer size
[9c96634]508 * @param enc Encoding
[d4b63fa]509 * @param dtype Directory entry type
510 * @return Decoded file name (allocated string)
511 */
[993d608]512static char *cdfs_decode_name(void *data, size_t dsize, cdfs_enc_t enc,
[d4b63fa]513 cdfs_dentry_type_t dtype)
514{
515 char *name;
516 char *dot;
517 char *scolon;
[a35b458]518
[993d608]519 name = cdfs_decode_str(data, dsize, enc);
[d4b63fa]520 if (name == NULL)
521 return NULL;
[a35b458]522
[d4b63fa]523 if (dtype == CDFS_DIRECTORY)
524 return name;
[a35b458]525
[d4b63fa]526 dot = str_chr(name, '.');
[a35b458]527
[993d608]528 if (dot != NULL) {
529 scolon = str_chr(dot, ';');
530 if (scolon != NULL) {
531 /* Trim version part */
532 *scolon = '\0';
533 }
[a35b458]534
[993d608]535 /* If the extension is an empty string, trim the dot separator. */
536 if (dot[1] == '\0')
537 *dot = '\0';
538 }
[a35b458]539
[d4b63fa]540 return name;
541}
542
[9c96634]543/** Decode volume identifier.
544 *
545 * @param data Volume identifier buffer
546 * @param dsize Volume identifier buffer size
547 * @param enc Encoding
548 * @return Decoded volume identifier (allocated string)
549 */
550static char *cdfs_decode_vol_ident(void *data, size_t dsize, cdfs_enc_t enc)
551{
552 char *ident;
553 size_t i;
[a35b458]554
[9c96634]555 ident = cdfs_decode_str(data, dsize, enc);
556 if (ident == NULL)
557 return NULL;
[a35b458]558
[9c96634]559 /* Trim trailing spaces */
560 i = str_size(ident);
561 while (i > 0 && ident[i - 1] == ' ')
562 --i;
563 ident[i] = '\0';
[a35b458]564
[9c96634]565 return ident;
566}
567
[b7fd2a0]568static errno_t cdfs_readdir(cdfs_t *fs, fs_node_t *fs_node)
[a26895d]569{
570 cdfs_node_t *node = CDFS_NODE(fs_node);
571 assert(node);
[a35b458]572
[a26895d]573 if (node->processed)
[a8c7a6d]574 return EOK;
[a35b458]575
[a26895d]576 uint32_t blocks = node->size / BLOCK_SIZE;
577 if ((node->size % BLOCK_SIZE) != 0)
578 blocks++;
[a35b458]579
[a26895d]580 for (uint32_t i = 0; i < blocks; i++) {
581 block_t *block;
[b7fd2a0]582 errno_t rc = block_get(&block, fs->service_id, node->lba + i, BLOCK_FLAGS_NONE);
[a26895d]583 if (rc != EOK)
[a8c7a6d]584 return rc;
[a35b458]585
[8e77b12f]586 cdfs_dir_t *dir;
[a35b458]587
[8e77b12f]588 for (size_t offset = 0; offset < BLOCK_SIZE;
[a26895d]589 offset += dir->length) {
590 dir = (cdfs_dir_t *) (block->data + offset);
[8e77b12f]591 if (dir->length == 0)
592 break;
593 if (offset + dir->length > BLOCK_SIZE) {
594 /* XXX Incorrect FS structure */
595 break;
596 }
[a35b458]597
[a26895d]598 cdfs_dentry_type_t dentry_type;
599 if (dir->flags & DIR_FLAG_DIRECTORY)
600 dentry_type = CDFS_DIRECTORY;
601 else
602 dentry_type = CDFS_FILE;
[a35b458]603
[8e77b12f]604 /* Skip special entries */
[a35b458]605
[8e77b12f]606 if (dir->name_length == 1 &&
607 dir->name[0] == CDFS_NAME_CURDIR)
608 continue;
609 if (dir->name_length == 1 &&
610 dir->name[0] == CDFS_NAME_PARENTDIR)
611 continue;
[a35b458]612
[a26895d]613 // FIXME: hack - indexing by dentry byte offset on disc
[a35b458]614
[a26895d]615 fs_node_t *fn;
[b7fd2a0]616 errno_t rc = create_node(&fn, fs, dentry_type,
[a26895d]617 (node->lba + i) * BLOCK_SIZE + offset);
[a8c7a6d]618 if (rc != EOK)
619 return rc;
620
621 assert(fn != NULL);
[a35b458]622
[a26895d]623 cdfs_node_t *cur = CDFS_NODE(fn);
624 cur->lba = uint32_lb(dir->lba);
625 cur->size = uint32_lb(dir->size);
[a35b458]626
[d4b63fa]627 char *name = cdfs_decode_name(dir->name,
[993d608]628 dir->name_length, node->fs->enc, dentry_type);
[a26895d]629 if (name == NULL)
[a8c7a6d]630 return EIO;
[a35b458]631
[a26895d]632 // FIXME: check return value
[a35b458]633
[a26895d]634 link_node(fs_node, fn, name);
635 free(name);
[a35b458]636
[a26895d]637 if (dentry_type == CDFS_FILE)
638 cur->processed = true;
639 }
[a35b458]640
[a26895d]641 block_put(block);
642 }
[a35b458]643
[a26895d]644 node->processed = true;
[a8c7a6d]645 return EOK;
[a26895d]646}
647
[0003e0f5]648static fs_node_t *get_uncached_node(cdfs_t *fs, fs_index_t index)
[a26895d]649{
650 cdfs_lba_t lba = index / BLOCK_SIZE;
651 size_t offset = index % BLOCK_SIZE;
[a35b458]652
[a26895d]653 block_t *block;
[b7fd2a0]654 errno_t rc = block_get(&block, fs->service_id, lba, BLOCK_FLAGS_NONE);
[a26895d]655 if (rc != EOK)
656 return NULL;
[a35b458]657
[a26895d]658 cdfs_dir_t *dir = (cdfs_dir_t *) (block->data + offset);
[a35b458]659
[a26895d]660 cdfs_dentry_type_t dentry_type;
661 if (dir->flags & DIR_FLAG_DIRECTORY)
662 dentry_type = CDFS_DIRECTORY;
663 else
664 dentry_type = CDFS_FILE;
[a35b458]665
[a26895d]666 fs_node_t *fn;
[0003e0f5]667 rc = create_node(&fn, fs, dentry_type, index);
[a26895d]668 if ((rc != EOK) || (fn == NULL))
669 return NULL;
[a35b458]670
[a26895d]671 cdfs_node_t *node = CDFS_NODE(fn);
672 node->lba = uint32_lb(dir->lba);
673 node->size = uint32_lb(dir->size);
674 node->lnkcnt = 1;
[a35b458]675
[a26895d]676 if (dentry_type == CDFS_FILE)
677 node->processed = true;
[a35b458]678
[a26895d]679 block_put(block);
680 return fn;
681}
682
[0003e0f5]683static fs_node_t *get_cached_node(cdfs_t *fs, fs_index_t index)
[a26895d]684{
[062d900]685 ht_key_t key = {
686 .index = index,
[0003e0f5]687 .service_id = fs->service_id
[a26895d]688 };
[a35b458]689
[062d900]690 ht_link_t *link = hash_table_find(&nodes, &key);
[a26895d]691 if (link) {
692 cdfs_node_t *node =
[062d900]693 hash_table_get_inst(link, cdfs_node_t, nh_link);
[a26895d]694 return FS_NODE(node);
695 }
[a35b458]696
[0003e0f5]697 return get_uncached_node(fs, index);
[a26895d]698}
699
[b7fd2a0]700static errno_t cdfs_match(fs_node_t **fn, fs_node_t *pfn, const char *component)
[a26895d]701{
702 cdfs_node_t *parent = CDFS_NODE(pfn);
[a35b458]703
[a26895d]704 if (!parent->processed) {
[b7fd2a0]705 errno_t rc = cdfs_readdir(parent->fs, pfn);
[a26895d]706 if (rc != EOK)
707 return rc;
708 }
[a35b458]709
[feeac0d]710 list_foreach(parent->cs_list, link, cdfs_dentry_t, dentry) {
[a26895d]711 if (str_cmp(dentry->name, component) == 0) {
[0003e0f5]712 *fn = get_cached_node(parent->fs, dentry->index);
[a26895d]713 return EOK;
714 }
715 }
[a35b458]716
[a26895d]717 *fn = NULL;
718 return EOK;
719}
720
[b7fd2a0]721static errno_t cdfs_node_open(fs_node_t *fn)
[a26895d]722{
723 cdfs_node_t *node = CDFS_NODE(fn);
[a35b458]724
[a26895d]725 if (!node->processed)
[0003e0f5]726 cdfs_readdir(node->fs, fn);
[a35b458]727
[a26895d]728 node->opened++;
729 return EOK;
730}
731
[b7fd2a0]732static errno_t cdfs_node_put(fs_node_t *fn)
[a26895d]733{
734 /* Nothing to do */
735 return EOK;
736}
737
[b7fd2a0]738static errno_t cdfs_create_node(fs_node_t **fn, service_id_t service_id, int lflag)
[a26895d]739{
740 /* Read-only */
741 return ENOTSUP;
742}
743
[b7fd2a0]744static errno_t cdfs_destroy_node(fs_node_t *fn)
[a26895d]745{
746 /* Read-only */
747 return ENOTSUP;
748}
749
[b7fd2a0]750static errno_t cdfs_link_node(fs_node_t *pfn, fs_node_t *cfn, const char *name)
[a26895d]751{
752 /* Read-only */
753 return ENOTSUP;
754}
755
[b7fd2a0]756static errno_t cdfs_unlink_node(fs_node_t *pfn, fs_node_t *cfn, const char *name)
[a26895d]757{
758 /* Read-only */
759 return ENOTSUP;
760}
761
[b7fd2a0]762static errno_t cdfs_has_children(bool *has_children, fs_node_t *fn)
[a26895d]763{
764 cdfs_node_t *node = CDFS_NODE(fn);
[a35b458]765
[a26895d]766 if ((node->type == CDFS_DIRECTORY) && (!node->processed))
[0003e0f5]767 cdfs_readdir(node->fs, fn);
[a35b458]768
[a26895d]769 *has_children = !list_empty(&node->cs_list);
770 return EOK;
771}
772
773static fs_index_t cdfs_index_get(fs_node_t *fn)
774{
775 cdfs_node_t *node = CDFS_NODE(fn);
776 return node->index;
777}
778
779static aoff64_t cdfs_size_get(fs_node_t *fn)
780{
781 cdfs_node_t *node = CDFS_NODE(fn);
782 return node->size;
783}
784
785static unsigned int cdfs_lnkcnt_get(fs_node_t *fn)
786{
787 cdfs_node_t *node = CDFS_NODE(fn);
788 return node->lnkcnt;
789}
790
791static bool cdfs_is_directory(fs_node_t *fn)
792{
793 cdfs_node_t *node = CDFS_NODE(fn);
794 return (node->type == CDFS_DIRECTORY);
795}
796
797static bool cdfs_is_file(fs_node_t *fn)
798{
799 cdfs_node_t *node = CDFS_NODE(fn);
800 return (node->type == CDFS_FILE);
801}
802
[b33870b]803static service_id_t cdfs_service_get(fs_node_t *fn)
[a26895d]804{
805 return 0;
806}
807
[b7fd2a0]808static errno_t cdfs_size_block(service_id_t service_id, uint32_t *size)
[a727fb0]809{
[3dd148d]810 *size = BLOCK_SIZE;
[a35b458]811
[1b20da0]812 return EOK;
[a727fb0]813}
814
[b7fd2a0]815static errno_t cdfs_total_block_count(service_id_t service_id, uint64_t *count)
[781408e]816{
817 *count = 0;
[a35b458]818
[781408e]819 return EOK;
820}
821
[b7fd2a0]822static errno_t cdfs_free_block_count(service_id_t service_id, uint64_t *count)
[781408e]823{
824 *count = 0;
[a35b458]825
[781408e]826 return EOK;
827}
828
[a26895d]829libfs_ops_t cdfs_libfs_ops = {
830 .root_get = cdfs_root_get,
831 .match = cdfs_match,
832 .node_get = cdfs_node_get,
833 .node_open = cdfs_node_open,
834 .node_put = cdfs_node_put,
835 .create = cdfs_create_node,
836 .destroy = cdfs_destroy_node,
837 .link = cdfs_link_node,
838 .unlink = cdfs_unlink_node,
839 .has_children = cdfs_has_children,
840 .index_get = cdfs_index_get,
841 .size_get = cdfs_size_get,
842 .lnkcnt_get = cdfs_lnkcnt_get,
843 .is_directory = cdfs_is_directory,
844 .is_file = cdfs_is_file,
[a727fb0]845 .service_get = cdfs_service_get,
[781408e]846 .size_block = cdfs_size_block,
847 .total_block_count = cdfs_total_block_count,
848 .free_block_count = cdfs_free_block_count
[a26895d]849};
850
[993d608]851/** Verify that escape sequence corresonds to one of the allowed encoding
[7c3fb9b]852 * escape sequences allowed for Joliet.
853 */
[b7fd2a0]854static errno_t cdfs_verify_joliet_esc_seq(uint8_t *seq)
[993d608]855{
856 size_t i, j, k;
857 bool match;
[a35b458]858
[993d608]859 i = 0;
860 while (i + ucs2_esc_seq_len <= 32) {
861 if (seq[i] == 0)
862 break;
[a35b458]863
[993d608]864 for (j = 0; j < ucs2_esc_seq_no; j++) {
865 match = true;
866 for (k = 0; k < ucs2_esc_seq_len; k++)
867 if (seq[i + k] != ucs2_esc_seq[j][k])
868 match = false;
869 if (match) {
870 break;
871 }
872 }
[a35b458]873
[993d608]874 if (!match)
875 return EINVAL;
[a35b458]876
[993d608]877 i += ucs2_esc_seq_len;
878 }
[a35b458]879
[993d608]880 while (i < 32) {
881 if (seq[i] != 0)
882 return EINVAL;
883 ++i;
884 }
[a35b458]885
[993d608]886 return EOK;
887}
888
889/** Find Joliet supplementary volume descriptor.
890 *
891 * @param sid Block device service ID
892 * @param altroot First filesystem block
893 * @param rlba Place to store LBA of root dir
894 * @param rsize Place to store size of root dir
[9c96634]895 * @param vol_ident Place to store pointer to volume identifier
[993d608]896 * @return EOK if found, ENOENT if not
897 */
[b7fd2a0]898static errno_t cdfs_find_joliet_svd(service_id_t sid, cdfs_lba_t altroot,
[9c96634]899 uint32_t *rlba, uint32_t *rsize, char **vol_ident)
[993d608]900{
901 cdfs_lba_t bi;
902
903 for (bi = altroot + 17; ; bi++) {
904 block_t *block;
[b7fd2a0]905 errno_t rc = block_get(&block, sid, bi, BLOCK_FLAGS_NONE);
[993d608]906 if (rc != EOK)
907 break;
[a35b458]908
[993d608]909 cdfs_vol_desc_t *vol_desc = (cdfs_vol_desc_t *) block->data;
[a35b458]910
[993d608]911 if (vol_desc->type == VOL_DESC_SET_TERMINATOR) {
912 block_put(block);
913 break;
914 }
[a35b458]915
[993d608]916 if ((vol_desc->type != VOL_DESC_SUPPLEMENTARY) ||
917 (memcmp(vol_desc->standard_ident, CDFS_STANDARD_IDENT, 5) != 0) ||
918 (vol_desc->version != 1)) {
919 block_put(block);
920 continue;
921 }
[a35b458]922
[993d608]923 uint16_t set_size = uint16_lb(vol_desc->data.prisec.set_size);
924 if (set_size > 1) {
925 /*
926 * Technically, we don't support multi-disc sets.
927 * But one can encounter erroneously mastered
928 * images in the wild and it might actually work
929 * for the first disc in the set.
930 */
931 }
[a35b458]932
[993d608]933 uint16_t sequence_nr = uint16_lb(vol_desc->data.prisec.sequence_nr);
934 if (sequence_nr != 1) {
935 /*
[ae7d03c]936 * We only support the first disc
[993d608]937 * in multi-disc sets.
938 */
939 block_put(block);
940 continue;
941 }
[a35b458]942
[993d608]943 uint16_t block_size = uint16_lb(vol_desc->data.prisec.block_size);
944 if (block_size != BLOCK_SIZE) {
945 block_put(block);
946 continue;
947 }
[a35b458]948
[993d608]949 rc = cdfs_verify_joliet_esc_seq(vol_desc->data.prisec.esc_seq);
950 if (rc != EOK)
951 continue;
952 *rlba = uint32_lb(vol_desc->data.prisec.root_dir.lba);
953 *rsize = uint32_lb(vol_desc->data.prisec.root_dir.size);
[9c96634]954
955 *vol_ident = cdfs_decode_vol_ident(vol_desc->data.prisec.ident,
956 32, enc_ucs2);
957
[993d608]958 block_put(block);
959 return EOK;
960 }
[a35b458]961
[993d608]962 return ENOENT;
963}
964
[395df52]965/** Read the volume descriptors. */
[b7fd2a0]966static errno_t iso_read_vol_desc(service_id_t sid, cdfs_lba_t altroot,
[9c96634]967 uint32_t *rlba, uint32_t *rsize, cdfs_enc_t *enc, char **vol_ident)
[a26895d]968{
969 /* First 16 blocks of isofs are empty */
970 block_t *block;
[b7fd2a0]971 errno_t rc = block_get(&block, sid, altroot + 16, BLOCK_FLAGS_NONE);
[a26895d]972 if (rc != EOK)
[94e3a03]973 return rc;
[a35b458]974
[a26895d]975 cdfs_vol_desc_t *vol_desc = (cdfs_vol_desc_t *) block->data;
[a35b458]976
[a26895d]977 /*
978 * Test for primary volume descriptor
979 * and standard compliance.
980 */
981 if ((vol_desc->type != VOL_DESC_PRIMARY) ||
[44ecf89]982 (memcmp(vol_desc->standard_ident, CDFS_STANDARD_IDENT, 5) != 0) ||
[a26895d]983 (vol_desc->version != 1)) {
984 block_put(block);
[94e3a03]985 return ENOTSUP;
[a26895d]986 }
[a35b458]987
[993d608]988 uint16_t set_size = uint16_lb(vol_desc->data.prisec.set_size);
[a26895d]989 if (set_size > 1) {
990 /*
991 * Technically, we don't support multi-disc sets.
992 * But one can encounter erroneously mastered
993 * images in the wild and it might actually work
994 * for the first disc in the set.
995 */
996 }
[a35b458]997
[993d608]998 uint16_t sequence_nr = uint16_lb(vol_desc->data.prisec.sequence_nr);
[a26895d]999 if (sequence_nr != 1) {
1000 /*
1001 * We only support the first disc
1002 * in multi-disc sets.
1003 */
1004 block_put(block);
[94e3a03]1005 return ENOTSUP;
[a26895d]1006 }
[a35b458]1007
[993d608]1008 uint16_t block_size = uint16_lb(vol_desc->data.prisec.block_size);
[a26895d]1009 if (block_size != BLOCK_SIZE) {
1010 block_put(block);
[94e3a03]1011 return ENOTSUP;
[a26895d]1012 }
[a35b458]1013
[a26895d]1014 // TODO: implement path table support
[a35b458]1015
[993d608]1016 /* Search for Joliet SVD */
[a35b458]1017
[993d608]1018 uint32_t jrlba;
1019 uint32_t jrsize;
[a35b458]1020
[9c96634]1021 rc = cdfs_find_joliet_svd(sid, altroot, &jrlba, &jrsize, vol_ident);
[993d608]1022 if (rc == EOK) {
1023 /* Found */
[395df52]1024 *rlba = jrlba;
1025 *rsize = jrsize;
1026 *enc = enc_ucs2;
[993d608]1027 } else {
[395df52]1028 *rlba = uint32_lb(vol_desc->data.prisec.root_dir.lba);
1029 *rsize = uint32_lb(vol_desc->data.prisec.root_dir.size);
1030 *enc = enc_ascii;
[9c96634]1031 *vol_ident = cdfs_decode_vol_ident(vol_desc->data.prisec.ident,
1032 32, enc_ascii);
[a26895d]1033 }
[a35b458]1034
[a26895d]1035 block_put(block);
[94e3a03]1036 return EOK;
[a26895d]1037}
1038
[b7fd2a0]1039static errno_t iso_readfs(cdfs_t *fs, fs_node_t *rfn,
[395df52]1040 cdfs_lba_t altroot)
1041{
1042 cdfs_node_t *node = CDFS_NODE(rfn);
[a35b458]1043
[b7fd2a0]1044 errno_t rc = iso_read_vol_desc(fs->service_id, altroot, &node->lba,
[94e3a03]1045 &node->size, &fs->enc, &fs->vol_ident);
1046 if (rc != EOK)
1047 return rc;
[a35b458]1048
[395df52]1049 return cdfs_readdir(fs, rfn);
1050}
1051
[7c3fb9b]1052/*
1053 * Mount a session with session start offset
[a26895d]1054 *
1055 */
[0003e0f5]1056static cdfs_t *cdfs_fs_create(service_id_t sid, cdfs_lba_t altroot)
[a26895d]1057{
[0003e0f5]1058 cdfs_t *fs = NULL;
1059 fs_node_t *rfn = NULL;
1060
1061 fs = calloc(1, sizeof(cdfs_t));
1062 if (fs == NULL)
1063 goto error;
[a35b458]1064
[0003e0f5]1065 fs->service_id = sid;
[a35b458]1066
[a26895d]1067 /* Create root node */
[b7fd2a0]1068 errno_t rc = create_node(&rfn, fs, L_DIRECTORY, cdfs_index++);
[a35b458]1069
[a26895d]1070 if ((rc != EOK) || (!rfn))
[0003e0f5]1071 goto error;
[a35b458]1072
[a26895d]1073 /* FS root is not linked */
1074 CDFS_NODE(rfn)->lnkcnt = 0;
1075 CDFS_NODE(rfn)->lba = 0;
1076 CDFS_NODE(rfn)->processed = false;
[a35b458]1077
[a26895d]1078 /* Check if there is cdfs in given session */
[94e3a03]1079 if (iso_readfs(fs, rfn, altroot) != EOK)
[0003e0f5]1080 goto error;
[a35b458]1081
[0003e0f5]1082 list_append(&fs->link, &cdfs_instances);
1083 return fs;
1084error:
1085 // XXX destroy node
1086 free(fs);
1087 return NULL;
[a26895d]1088}
1089
[b7fd2a0]1090static errno_t cdfs_fsprobe(service_id_t service_id, vfs_fs_probe_info_t *info)
[d2c8533]1091{
[9c96634]1092 char *vol_ident;
1093
[395df52]1094 /* Initialize the block layer */
[b7fd2a0]1095 errno_t rc = block_init(service_id, BLOCK_SIZE);
[395df52]1096 if (rc != EOK)
1097 return rc;
[a35b458]1098
[395df52]1099 cdfs_lba_t altroot = 0;
[a35b458]1100
[395df52]1101 /*
1102 * Read TOC multisession information and get the start address
1103 * of the first track in the last session
1104 */
1105 scsi_toc_multisess_data_t toc;
1106
1107 rc = block_read_toc(service_id, 1, &toc, sizeof(toc));
1108 if (rc == EOK && (uint16_t_be2host(toc.toc_len) == 10))
1109 altroot = uint32_t_be2host(toc.ftrack_lsess.start_addr);
[a35b458]1110
[395df52]1111 /* Initialize the block cache */
1112 rc = block_cache_init(service_id, BLOCK_SIZE, 0, CACHE_MODE_WT);
1113 if (rc != EOK) {
1114 block_fini(service_id);
1115 return rc;
1116 }
[a35b458]1117
[395df52]1118 /* Check if this device is not already mounted */
1119 fs_node_t *rootfn;
1120 rc = cdfs_root_get(&rootfn, service_id);
1121 if ((rc == EOK) && (rootfn)) {
1122 cdfs_node_put(rootfn);
1123 block_cache_fini(service_id);
1124 block_fini(service_id);
1125 return EOK;
1126 }
[a35b458]1127
[395df52]1128 /* Read volume descriptors */
1129 uint32_t rlba;
1130 uint32_t rsize;
1131 cdfs_enc_t enc;
[94e3a03]1132 rc = iso_read_vol_desc(service_id, altroot, &rlba, &rsize, &enc,
1133 &vol_ident);
1134 if (rc != EOK) {
[395df52]1135 block_cache_fini(service_id);
1136 block_fini(service_id);
[94e3a03]1137 return rc;
[395df52]1138 }
[a35b458]1139
[9c96634]1140 str_cpy(info->label, FS_LABEL_MAXLEN + 1, vol_ident);
1141 free(vol_ident);
[a35b458]1142
[adc68de]1143 block_cache_fini(service_id);
1144 block_fini(service_id);
[395df52]1145 return EOK;
[d2c8533]1146}
1147
[b7fd2a0]1148static errno_t cdfs_mounted(service_id_t service_id, const char *opts,
[4f30222]1149 fs_index_t *index, aoff64_t *size)
[a26895d]1150{
1151 /* Initialize the block layer */
[b7fd2a0]1152 errno_t rc = block_init(service_id, BLOCK_SIZE);
[a26895d]1153 if (rc != EOK)
1154 return rc;
[a35b458]1155
[a26895d]1156 cdfs_lba_t altroot = 0;
[a35b458]1157
[a26895d]1158 if (str_lcmp(opts, "altroot=", 8) == 0) {
1159 /* User-defined alternative root on a multi-session disk */
1160 if (str_uint32_t(opts + 8, NULL, 0, false, &altroot) != EOK)
1161 altroot = 0;
1162 } else {
[3abf70c7]1163 /*
1164 * Read TOC multisession information and get the start address
1165 * of the first track in the last session
1166 */
1167 scsi_toc_multisess_data_t toc;
1168
1169 rc = block_read_toc(service_id, 1, &toc, sizeof(toc));
1170 if (rc == EOK && (uint16_t_be2host(toc.toc_len) == 10))
1171 altroot = uint32_t_be2host(toc.ftrack_lsess.start_addr);
[a26895d]1172 }
[a35b458]1173
[a26895d]1174 /* Initialize the block cache */
1175 rc = block_cache_init(service_id, BLOCK_SIZE, 0, CACHE_MODE_WT);
1176 if (rc != EOK) {
1177 block_fini(service_id);
1178 return rc;
1179 }
[a35b458]1180
[a26895d]1181 /* Check if this device is not already mounted */
1182 fs_node_t *rootfn;
1183 rc = cdfs_root_get(&rootfn, service_id);
1184 if ((rc == EOK) && (rootfn)) {
1185 cdfs_node_put(rootfn);
1186 block_cache_fini(service_id);
1187 block_fini(service_id);
[a35b458]1188
[a26895d]1189 return EEXIST;
1190 }
[a35b458]1191
[0003e0f5]1192 /* Create cdfs instance */
1193 if (cdfs_fs_create(service_id, altroot) == NULL) {
[a26895d]1194 block_cache_fini(service_id);
1195 block_fini(service_id);
[a35b458]1196
[a26895d]1197 return ENOMEM;
1198 }
[a35b458]1199
[a26895d]1200 rc = cdfs_root_get(&rootfn, service_id);
1201 assert(rc == EOK);
[a35b458]1202
[a26895d]1203 cdfs_node_t *root = CDFS_NODE(rootfn);
1204 *index = root->index;
1205 *size = root->size;
[a35b458]1206
[a26895d]1207 return EOK;
1208}
1209
[1b20da0]1210static bool rm_service_id_nodes(ht_link_t *item, void *arg)
[a26895d]1211{
[3bacee1]1212 service_id_t service_id = *(service_id_t *)arg;
[062d900]1213 cdfs_node_t *node = hash_table_get_inst(item, cdfs_node_t, nh_link);
[a35b458]1214
[0003e0f5]1215 if (node->fs->service_id == service_id) {
[062d900]1216 hash_table_remove_item(&nodes, &node->nh_link);
1217 }
[a35b458]1218
[062d900]1219 return true;
1220}
1221
[0003e0f5]1222static void cdfs_fs_destroy(cdfs_t *fs)
1223{
1224 list_remove(&fs->link);
1225 hash_table_apply(&nodes, rm_service_id_nodes, &fs->service_id);
1226 block_cache_fini(fs->service_id);
1227 block_fini(fs->service_id);
[9c96634]1228 free(fs->vol_ident);
[0003e0f5]1229 free(fs);
1230}
1231
1232static cdfs_t *cdfs_find_by_sid(service_id_t service_id)
[062d900]1233{
[0003e0f5]1234 list_foreach(cdfs_instances, link, cdfs_t, fs) {
1235 if (fs->service_id == service_id)
1236 return fs;
1237 }
[a35b458]1238
[0003e0f5]1239 return NULL;
[a26895d]1240}
1241
[b7fd2a0]1242static errno_t cdfs_unmounted(service_id_t service_id)
[a26895d]1243{
[0003e0f5]1244 cdfs_t *fs;
1245
1246 fs = cdfs_find_by_sid(service_id);
1247 if (fs == NULL)
1248 return ENOENT;
[a35b458]1249
[0003e0f5]1250 cdfs_fs_destroy(fs);
[a26895d]1251 return EOK;
1252}
1253
[b7fd2a0]1254static errno_t cdfs_read(service_id_t service_id, fs_index_t index, aoff64_t pos,
[a26895d]1255 size_t *rbytes)
1256{
[062d900]1257 ht_key_t key = {
1258 .index = index,
1259 .service_id = service_id
[a26895d]1260 };
[a35b458]1261
[062d900]1262 ht_link_t *link = hash_table_find(&nodes, &key);
[a26895d]1263 if (link == NULL)
1264 return ENOENT;
[a35b458]1265
[a26895d]1266 cdfs_node_t *node =
[062d900]1267 hash_table_get_inst(link, cdfs_node_t, nh_link);
[a35b458]1268
[a26895d]1269 if (!node->processed) {
[b7fd2a0]1270 errno_t rc = cdfs_readdir(node->fs, FS_NODE(node));
[a26895d]1271 if (rc != EOK)
1272 return rc;
1273 }
[a35b458]1274
[984a9ba]1275 ipc_call_t call;
[a26895d]1276 size_t len;
[984a9ba]1277 if (!async_data_read_receive(&call, &len)) {
1278 async_answer_0(&call, EINVAL);
[a26895d]1279 return EINVAL;
1280 }
[a35b458]1281
[a26895d]1282 if (node->type == CDFS_FILE) {
1283 if (pos >= node->size) {
1284 *rbytes = 0;
[984a9ba]1285 async_data_read_finalize(&call, NULL, 0);
[a26895d]1286 } else {
1287 cdfs_lba_t lba = pos / BLOCK_SIZE;
1288 size_t offset = pos % BLOCK_SIZE;
[a35b458]1289
[a26895d]1290 *rbytes = min(len, BLOCK_SIZE - offset);
1291 *rbytes = min(*rbytes, node->size - pos);
[a35b458]1292
[a26895d]1293 block_t *block;
[b7fd2a0]1294 errno_t rc = block_get(&block, service_id, node->lba + lba,
[a26895d]1295 BLOCK_FLAGS_NONE);
1296 if (rc != EOK) {
[984a9ba]1297 async_answer_0(&call, rc);
[a26895d]1298 return rc;
1299 }
[a35b458]1300
[984a9ba]1301 async_data_read_finalize(&call, block->data + offset,
[a26895d]1302 *rbytes);
1303 rc = block_put(block);
1304 if (rc != EOK)
1305 return rc;
1306 }
1307 } else {
1308 link_t *link = list_nth(&node->cs_list, pos);
1309 if (link == NULL) {
[984a9ba]1310 async_answer_0(&call, ENOENT);
[a26895d]1311 return ENOENT;
1312 }
[a35b458]1313
[a26895d]1314 cdfs_dentry_t *dentry =
1315 list_get_instance(link, cdfs_dentry_t, link);
[a35b458]1316
[a26895d]1317 *rbytes = 1;
[984a9ba]1318 async_data_read_finalize(&call, dentry->name,
[a26895d]1319 str_size(dentry->name) + 1);
1320 }
[a35b458]1321
[a26895d]1322 return EOK;
1323}
1324
[b7fd2a0]1325static errno_t cdfs_write(service_id_t service_id, fs_index_t index, aoff64_t pos,
[a26895d]1326 size_t *wbytes, aoff64_t *nsize)
1327{
1328 /*
1329 * As cdfs is a read-only filesystem,
1330 * the operation is not supported.
1331 */
[a35b458]1332
[a26895d]1333 return ENOTSUP;
1334}
1335
[b7fd2a0]1336static errno_t cdfs_truncate(service_id_t service_id, fs_index_t index,
[a26895d]1337 aoff64_t size)
1338{
1339 /*
1340 * As cdfs is a read-only filesystem,
1341 * the operation is not supported.
1342 */
[a35b458]1343
[a26895d]1344 return ENOTSUP;
1345}
1346
[062d900]1347static bool cache_remove_closed(ht_link_t *item, void *arg)
1348{
[3bacee1]1349 size_t *premove_cnt = (size_t *)arg;
[a35b458]1350
[062d900]1351 /* Some nodes were requested to be removed from the cache. */
1352 if (0 < *premove_cnt) {
[993d608]1353 cdfs_node_t *node = hash_table_get_inst(item, cdfs_node_t, nh_link);
[062d900]1354
1355 if (!node->opened) {
1356 hash_table_remove_item(&nodes, item);
[a35b458]1357
[062d900]1358 --nodes_cached;
1359 --*premove_cnt;
1360 }
1361 }
[a35b458]1362
[062d900]1363 /* Only continue if more nodes were requested to be removed. */
1364 return 0 < *premove_cnt;
1365}
1366
[a26895d]1367static void cleanup_cache(service_id_t service_id)
1368{
1369 if (nodes_cached > NODE_CACHE_SIZE) {
[062d900]1370 size_t remove_cnt = nodes_cached - NODE_CACHE_SIZE;
[a35b458]1371
[062d900]1372 if (0 < remove_cnt)
1373 hash_table_apply(&nodes, cache_remove_closed, &remove_cnt);
[a26895d]1374 }
1375}
1376
[b7fd2a0]1377static errno_t cdfs_close(service_id_t service_id, fs_index_t index)
[a26895d]1378{
1379 /* Root node is always in memory */
1380 if (index == 0)
1381 return EOK;
[a35b458]1382
[062d900]1383 ht_key_t key = {
1384 .index = index,
1385 .service_id = service_id
[a26895d]1386 };
[a35b458]1387
[062d900]1388 ht_link_t *link = hash_table_find(&nodes, &key);
[a26895d]1389 if (link == 0)
1390 return ENOENT;
[a35b458]1391
[a26895d]1392 cdfs_node_t *node =
[062d900]1393 hash_table_get_inst(link, cdfs_node_t, nh_link);
[a35b458]1394
[a26895d]1395 assert(node->opened > 0);
[a35b458]1396
[a26895d]1397 node->opened--;
1398 cleanup_cache(service_id);
[a35b458]1399
[a26895d]1400 return EOK;
1401}
1402
[b7fd2a0]1403static errno_t cdfs_destroy(service_id_t service_id, fs_index_t index)
[a26895d]1404{
1405 /*
1406 * As cdfs is a read-only filesystem,
1407 * the operation is not supported.
1408 */
[a35b458]1409
[a26895d]1410 return ENOTSUP;
1411}
1412
[b7fd2a0]1413static errno_t cdfs_sync(service_id_t service_id, fs_index_t index)
[a26895d]1414{
1415 /*
1416 * As cdfs is a read-only filesystem,
1417 * the sync operation is a no-op.
1418 */
[a35b458]1419
[a26895d]1420 return EOK;
1421}
1422
1423vfs_out_ops_t cdfs_ops = {
[d2c8533]1424 .fsprobe = cdfs_fsprobe,
[a26895d]1425 .mounted = cdfs_mounted,
1426 .unmounted = cdfs_unmounted,
1427 .read = cdfs_read,
1428 .write = cdfs_write,
1429 .truncate = cdfs_truncate,
1430 .close = cdfs_close,
1431 .destroy = cdfs_destroy,
1432 .sync = cdfs_sync
1433};
1434
1435/** Initialize the cdfs server
1436 *
1437 */
1438bool cdfs_init(void)
1439{
[062d900]1440 if (!hash_table_create(&nodes, 0, 0, &nodes_ops))
[a26895d]1441 return false;
[a35b458]1442
[a26895d]1443 return true;
1444}
1445
1446/**
1447 * @}
1448 */
Note: See TracBrowser for help on using the repository browser.