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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 36c3139 was 36c3139, checked in by jzr <zarevucky.jiri@…>, 8 years ago

CDFS has structure fields after a field of variable length.

This is unused and probably wouldn't work as intended anyway, so just removing it for now.

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