Changes in uspace/srv/fs/cdfs/cdfs_ops.c [feeac0d:3abf70c7] in mainline
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/srv/fs/cdfs/cdfs_ops.c
rfeeac0d r3abf70c7 39 39 #include "cdfs_ops.h" 40 40 #include <stdbool.h> 41 #include <adt/list.h> 41 42 #include <adt/hash_table.h> 42 43 #include <adt/hash.h> … … 47 48 #include <errno.h> 48 49 #include <block.h> 50 #include <scsi/mmc.h> 49 51 #include <str.h> 50 52 #include <byteorder.h> … … 65 67 66 68 #define CDFS_STANDARD_IDENT "CD001" 69 70 enum { 71 CDFS_NAME_CURDIR = '\x00', 72 CDFS_NAME_PARENTDIR = '\x01' 73 }; 67 74 68 75 typedef enum { … … 126 133 127 134 typedef struct { 128 uint8_t res0;135 uint8_t flags; /* reserved in primary */ 129 136 130 137 uint8_t system_ident[32]; … … 134 141 uint32_t_lb lba_size; 135 142 136 uint8_t res2[32];143 uint8_t esc_seq[32]; /* reserved in primary */ 137 144 uint16_t_lb set_size; 138 145 uint16_t_lb sequence_nr; … … 164 171 165 172 uint8_t fs_version; 166 } __attribute__((packed)) cdfs_vol_desc_pri mary_t;173 } __attribute__((packed)) cdfs_vol_desc_prisec_t; 167 174 168 175 typedef struct { … … 172 179 union { 173 180 cdfs_vol_desc_boot_t boot; 174 cdfs_vol_desc_pri mary_t primary;181 cdfs_vol_desc_prisec_t prisec; 175 182 } data; 176 183 } __attribute__((packed)) cdfs_vol_desc_t; 184 185 typedef enum { 186 /** ASCII character set / encoding (base ISO 9660) */ 187 enc_ascii, 188 /** UCS-2 character set / encoding (Joliet) */ 189 enc_ucs2 190 } cdfs_enc_t; 177 191 178 192 typedef enum { … … 191 205 192 206 typedef struct { 207 link_t link; /**< Link to list of all instances */ 208 service_id_t service_id; /**< Service ID of block device */ 209 cdfs_enc_t enc; /**< Filesystem string encoding */ 210 } cdfs_t; 211 212 typedef struct { 193 213 fs_node_t *fs_node; /**< FS node */ 194 214 fs_index_t index; /**< Node index */ 195 service_id_t service_id; /**< Service ID of block device*/215 cdfs_t *fs; /**< File system */ 196 216 197 217 ht_link_t nh_link; /**< Nodes hash table link */ … … 207 227 } cdfs_node_t; 208 228 229 /** String encoding */ 230 enum { 231 /** ASCII - standard ISO 9660 */ 232 ucs2_esc_seq_no = 3, 233 /** USC-2 - Joliet */ 234 ucs2_esc_seq_len = 3 235 }; 236 237 /** Joliet SVD UCS-2 escape sequences */ 238 static uint8_t ucs2_esc_seq[ucs2_esc_seq_no][ucs2_esc_seq_len] = { 239 { 0x25, 0x2f, 0x40 }, 240 { 0x25, 0x2f, 0x43 }, 241 { 0x25, 0x2f, 0x45 } 242 }; 243 244 /** List of all instances */ 245 static LIST_INITIALIZE(cdfs_instances); 246 209 247 /** Shared index of nodes */ 210 248 static fs_index_t cdfs_index = 1; … … 234 272 { 235 273 cdfs_node_t *node = hash_table_get_inst(item, cdfs_node_t, nh_link); 236 return hash_combine(node-> service_id, node->index);274 return hash_combine(node->fs->service_id, node->index); 237 275 } 238 276 … … 242 280 ht_key_t *key = (ht_key_t*)k; 243 281 244 return key->service_id == node-> service_id && key->index == node->index;282 return key->service_id == node->fs->service_id && key->index == node->index; 245 283 } 246 284 … … 249 287 cdfs_node_t *node = hash_table_get_inst(item, cdfs_node_t, nh_link); 250 288 251 assert(node->type == CDFS_DIRECTORY);252 253 link_t *link;254 while ((link = list_first(&node->cs_list)) != NULL) {255 cdfs_dentry_t *dentry = list_get_instance(link, cdfs_dentry_t,link);256 list_remove(&dentry->link);257 free(dentry);289 if (node->type == CDFS_DIRECTORY) { 290 link_t *link; 291 while ((link = list_first(&node->cs_list)) != NULL) { 292 cdfs_dentry_t *dentry = list_get_instance(link, cdfs_dentry_t, link); 293 list_remove(&dentry->link); 294 free(dentry); 295 } 258 296 } 259 297 … … 300 338 node->fs_node = NULL; 301 339 node->index = 0; 302 node-> service_id = 0;340 node->fs = NULL; 303 341 node->type = CDFS_NONE; 304 342 node->lnkcnt = 0; … … 311 349 } 312 350 313 static int create_node(fs_node_t **rfn, service_id_t service_id, int lflag,351 static int create_node(fs_node_t **rfn, cdfs_t *fs, int lflag, 314 352 fs_index_t index) 315 353 { … … 332 370 333 371 fs_node_t *rootfn; 334 int rc = cdfs_root_get(&rootfn, service_id);372 int rc = cdfs_root_get(&rootfn, fs->service_id); 335 373 336 374 assert(rc == EOK); … … 341 379 node->index = index; 342 380 343 node-> service_id = service_id;381 node->fs = fs; 344 382 345 383 if (lflag & L_DIRECTORY) … … 391 429 } 392 430 393 static bool cdfs_readdir(service_id_t service_id, fs_node_t *fs_node) 431 /** Decode CDFS string. 432 * 433 * @param data Pointer to string data 434 * @param dsize Size of data in bytes 435 * @param enc String encoding 436 * @return Decoded string 437 */ 438 static char *cdfs_decode_str(void *data, size_t dsize, cdfs_enc_t enc) 439 { 440 int rc; 441 char *str; 442 uint16_t *buf; 443 444 switch (enc) { 445 case enc_ascii: 446 str = malloc(dsize + 1); 447 if (str == NULL) 448 return NULL; 449 memcpy(str, data, dsize); 450 str[dsize] = '\0'; 451 break; 452 case enc_ucs2: 453 buf = calloc(dsize + 2, 1); 454 if (buf == NULL) 455 return NULL; 456 457 size_t i; 458 for (i = 0; i < dsize / sizeof(uint16_t); i++) { 459 buf[i] = uint16_t_be2host(((uint16_t *)data)[i]); 460 } 461 462 size_t dstr_size = dsize / sizeof(uint16_t) * 4 + 1; 463 str = malloc(dstr_size); 464 if (str == NULL) 465 return NULL; 466 467 rc = utf16_to_str(str, dstr_size, buf); 468 free(buf); 469 470 if (rc != EOK) 471 return NULL; 472 break; 473 default: 474 assert(false); 475 str = NULL; 476 } 477 478 return str; 479 } 480 481 /** Decode file name. 482 * 483 * @param data File name buffer 484 * @param dsize Fine name buffer size 485 * @param dtype Directory entry type 486 * @return Decoded file name (allocated string) 487 */ 488 static char *cdfs_decode_name(void *data, size_t dsize, cdfs_enc_t enc, 489 cdfs_dentry_type_t dtype) 490 { 491 char *name; 492 char *dot; 493 char *scolon; 494 495 name = cdfs_decode_str(data, dsize, enc); 496 if (name == NULL) 497 return NULL; 498 499 if (dtype == CDFS_DIRECTORY) 500 return name; 501 502 dot = str_chr(name, '.'); 503 504 if (dot != NULL) { 505 scolon = str_chr(dot, ';'); 506 if (scolon != NULL) { 507 /* Trim version part */ 508 *scolon = '\0'; 509 } 510 511 /* If the extension is an empty string, trim the dot separator. */ 512 if (dot[1] == '\0') 513 *dot = '\0'; 514 } 515 516 return name; 517 } 518 519 static bool cdfs_readdir(cdfs_t *fs, fs_node_t *fs_node) 394 520 { 395 521 cdfs_node_t *node = CDFS_NODE(fs_node); … … 405 531 for (uint32_t i = 0; i < blocks; i++) { 406 532 block_t *block; 407 int rc = block_get(&block, service_id, node->lba + i, BLOCK_FLAGS_NONE);533 int rc = block_get(&block, fs->service_id, node->lba + i, BLOCK_FLAGS_NONE); 408 534 if (rc != EOK) 409 535 return false; 410 536 411 cdfs_dir_t *dir = (cdfs_dir_t *) block->data; 412 413 // FIXME: skip '.' and '..' 414 415 for (size_t offset = 0; 416 (dir->length != 0) && (offset < BLOCK_SIZE); 537 cdfs_dir_t *dir; 538 539 for (size_t offset = 0; offset < BLOCK_SIZE; 417 540 offset += dir->length) { 418 541 dir = (cdfs_dir_t *) (block->data + offset); 542 if (dir->length == 0) 543 break; 544 if (offset + dir->length > BLOCK_SIZE) { 545 /* XXX Incorrect FS structure */ 546 break; 547 } 419 548 420 549 cdfs_dentry_type_t dentry_type; … … 424 553 dentry_type = CDFS_FILE; 425 554 555 /* Skip special entries */ 556 557 if (dir->name_length == 1 && 558 dir->name[0] == CDFS_NAME_CURDIR) 559 continue; 560 if (dir->name_length == 1 && 561 dir->name[0] == CDFS_NAME_PARENTDIR) 562 continue; 563 426 564 // FIXME: hack - indexing by dentry byte offset on disc 427 565 428 566 fs_node_t *fn; 429 int rc = create_node(&fn, service_id, dentry_type,567 int rc = create_node(&fn, fs, dentry_type, 430 568 (node->lba + i) * BLOCK_SIZE + offset); 431 569 if ((rc != EOK) || (fn == NULL)) … … 436 574 cur->size = uint32_lb(dir->size); 437 575 438 char *name = (char *) malloc(dir->name_length + 1); 576 char *name = cdfs_decode_name(dir->name, 577 dir->name_length, node->fs->enc, dentry_type); 439 578 if (name == NULL) 440 579 return false; 441 442 memcpy(name, dir->name, dir->name_length);443 name[dir->name_length] = 0;444 580 445 581 // FIXME: check return value … … 459 595 } 460 596 461 static fs_node_t *get_uncached_node( service_id_t service_id, fs_index_t index)597 static fs_node_t *get_uncached_node(cdfs_t *fs, fs_index_t index) 462 598 { 463 599 cdfs_lba_t lba = index / BLOCK_SIZE; … … 465 601 466 602 block_t *block; 467 int rc = block_get(&block, service_id, lba, BLOCK_FLAGS_NONE);603 int rc = block_get(&block, fs->service_id, lba, BLOCK_FLAGS_NONE); 468 604 if (rc != EOK) 469 605 return NULL; … … 478 614 479 615 fs_node_t *fn; 480 rc = create_node(&fn, service_id, dentry_type, index);616 rc = create_node(&fn, fs, dentry_type, index); 481 617 if ((rc != EOK) || (fn == NULL)) 482 618 return NULL; … … 494 630 } 495 631 496 static fs_node_t *get_cached_node( service_id_t service_id, fs_index_t index)632 static fs_node_t *get_cached_node(cdfs_t *fs, fs_index_t index) 497 633 { 498 634 ht_key_t key = { 499 635 .index = index, 500 .service_id = service_id636 .service_id = fs->service_id 501 637 }; 502 638 … … 508 644 } 509 645 510 return get_uncached_node( service_id, index);646 return get_uncached_node(fs, index); 511 647 } 512 648 … … 516 652 517 653 if (!parent->processed) { 518 int rc = cdfs_readdir(parent-> service_id, pfn);654 int rc = cdfs_readdir(parent->fs, pfn); 519 655 if (rc != EOK) 520 656 return rc; … … 523 659 list_foreach(parent->cs_list, link, cdfs_dentry_t, dentry) { 524 660 if (str_cmp(dentry->name, component) == 0) { 525 *fn = get_cached_node(parent-> service_id, dentry->index);661 *fn = get_cached_node(parent->fs, dentry->index); 526 662 return EOK; 527 663 } … … 537 673 538 674 if (!node->processed) 539 cdfs_readdir(node-> service_id, fn);675 cdfs_readdir(node->fs, fn); 540 676 541 677 node->opened++; … … 578 714 579 715 if ((node->type == CDFS_DIRECTORY) && (!node->processed)) 580 cdfs_readdir(node-> service_id, fn);716 cdfs_readdir(node->fs, fn); 581 717 582 718 *has_children = !list_empty(&node->cs_list); … … 617 753 { 618 754 return 0; 755 } 756 757 static int cdfs_size_block(service_id_t service_id, uint32_t *size) 758 { 759 *size = BLOCK_SIZE; 760 761 return EOK; 762 } 763 764 static int cdfs_total_block_count(service_id_t service_id, uint64_t *count) 765 { 766 *count = 0; 767 768 return EOK; 769 } 770 771 static int cdfs_free_block_count(service_id_t service_id, uint64_t *count) 772 { 773 *count = 0; 774 775 return EOK; 619 776 } 620 777 … … 635 792 .is_directory = cdfs_is_directory, 636 793 .is_file = cdfs_is_file, 637 .service_get = cdfs_service_get 794 .service_get = cdfs_service_get, 795 .size_block = cdfs_size_block, 796 .total_block_count = cdfs_total_block_count, 797 .free_block_count = cdfs_free_block_count 638 798 }; 639 799 640 static bool iso_readfs(service_id_t service_id, fs_node_t *rfn, 800 /** Verify that escape sequence corresonds to one of the allowed encoding 801 * escape sequences allowed for Joliet. */ 802 static int cdfs_verify_joliet_esc_seq(uint8_t *seq) 803 { 804 size_t i, j, k; 805 bool match; 806 807 i = 0; 808 while (i + ucs2_esc_seq_len <= 32) { 809 if (seq[i] == 0) 810 break; 811 812 for (j = 0; j < ucs2_esc_seq_no; j++) { 813 match = true; 814 for (k = 0; k < ucs2_esc_seq_len; k++) 815 if (seq[i + k] != ucs2_esc_seq[j][k]) 816 match = false; 817 if (match) { 818 break; 819 } 820 } 821 822 if (!match) 823 return EINVAL; 824 825 i += ucs2_esc_seq_len; 826 } 827 828 while (i < 32) { 829 if (seq[i] != 0) 830 return EINVAL; 831 ++i; 832 } 833 834 return EOK; 835 } 836 837 /** Find Joliet supplementary volume descriptor. 838 * 839 * @param sid Block device service ID 840 * @param altroot First filesystem block 841 * @param rlba Place to store LBA of root dir 842 * @param rsize Place to store size of root dir 843 * @return EOK if found, ENOENT if not 844 */ 845 static int cdfs_find_joliet_svd(service_id_t sid, cdfs_lba_t altroot, 846 uint32_t *rlba, uint32_t *rsize) 847 { 848 cdfs_lba_t bi; 849 850 for (bi = altroot + 17; ; bi++) { 851 block_t *block; 852 int rc = block_get(&block, sid, bi, BLOCK_FLAGS_NONE); 853 if (rc != EOK) 854 break; 855 856 cdfs_vol_desc_t *vol_desc = (cdfs_vol_desc_t *) block->data; 857 858 if (vol_desc->type == VOL_DESC_SET_TERMINATOR) { 859 block_put(block); 860 break; 861 } 862 863 if ((vol_desc->type != VOL_DESC_SUPPLEMENTARY) || 864 (memcmp(vol_desc->standard_ident, CDFS_STANDARD_IDENT, 5) != 0) || 865 (vol_desc->version != 1)) { 866 block_put(block); 867 continue; 868 } 869 870 uint16_t set_size = uint16_lb(vol_desc->data.prisec.set_size); 871 if (set_size > 1) { 872 /* 873 * Technically, we don't support multi-disc sets. 874 * But one can encounter erroneously mastered 875 * images in the wild and it might actually work 876 * for the first disc in the set. 877 */ 878 } 879 880 uint16_t sequence_nr = uint16_lb(vol_desc->data.prisec.sequence_nr); 881 if (sequence_nr != 1) { 882 /* 883 * We only support the first disc 884 * in multi-disc sets. 885 */ 886 block_put(block); 887 continue; 888 } 889 890 uint16_t block_size = uint16_lb(vol_desc->data.prisec.block_size); 891 if (block_size != BLOCK_SIZE) { 892 block_put(block); 893 continue; 894 } 895 896 rc = cdfs_verify_joliet_esc_seq(vol_desc->data.prisec.esc_seq); 897 if (rc != EOK) 898 continue; 899 *rlba = uint32_lb(vol_desc->data.prisec.root_dir.lba); 900 *rsize = uint32_lb(vol_desc->data.prisec.root_dir.size); 901 block_put(block); 902 return EOK; 903 } 904 905 return ENOENT; 906 } 907 908 static bool iso_readfs(cdfs_t *fs, fs_node_t *rfn, 641 909 cdfs_lba_t altroot) 642 910 { 643 911 /* First 16 blocks of isofs are empty */ 644 912 block_t *block; 645 int rc = block_get(&block, service_id, altroot + 16, BLOCK_FLAGS_NONE);913 int rc = block_get(&block, fs->service_id, altroot + 16, BLOCK_FLAGS_NONE); 646 914 if (rc != EOK) 647 915 return false; … … 660 928 } 661 929 662 uint16_t set_size = uint16_lb(vol_desc->data.pri mary.set_size);930 uint16_t set_size = uint16_lb(vol_desc->data.prisec.set_size); 663 931 if (set_size > 1) { 664 932 /* … … 670 938 } 671 939 672 uint16_t sequence_nr = uint16_lb(vol_desc->data.pri mary.sequence_nr);940 uint16_t sequence_nr = uint16_lb(vol_desc->data.prisec.sequence_nr); 673 941 if (sequence_nr != 1) { 674 942 /* … … 680 948 } 681 949 682 uint16_t block_size = uint16_lb(vol_desc->data.pri mary.block_size);950 uint16_t block_size = uint16_lb(vol_desc->data.prisec.block_size); 683 951 if (block_size != BLOCK_SIZE) { 684 952 block_put(block); … … 689 957 690 958 cdfs_node_t *node = CDFS_NODE(rfn); 691 node->lba = uint32_lb(vol_desc->data.primary.root_dir.lba); 692 node->size = uint32_lb(vol_desc->data.primary.root_dir.size); 693 694 if (!cdfs_readdir(service_id, rfn)) { 959 960 /* Search for Joliet SVD */ 961 962 uint32_t jrlba; 963 uint32_t jrsize; 964 965 rc = cdfs_find_joliet_svd(fs->service_id, altroot, &jrlba, &jrsize); 966 if (rc == EOK) { 967 /* Found */ 968 node->lba = jrlba; 969 node->size = jrsize; 970 fs->enc = enc_ucs2; 971 } else { 972 node->lba = uint32_lb(vol_desc->data.prisec.root_dir.lba); 973 node->size = uint32_lb(vol_desc->data.prisec.root_dir.size); 974 fs->enc = enc_ascii; 975 } 976 977 if (!cdfs_readdir(fs, rfn)) { 695 978 block_put(block); 696 979 return false; … … 704 987 * 705 988 */ 706 static bool cdfs_instance_init(service_id_t service_id, cdfs_lba_t altroot) 707 { 989 static cdfs_t *cdfs_fs_create(service_id_t sid, cdfs_lba_t altroot) 990 { 991 cdfs_t *fs = NULL; 992 fs_node_t *rfn = NULL; 993 994 fs = calloc(1, sizeof(cdfs_t)); 995 if (fs == NULL) 996 goto error; 997 998 fs->service_id = sid; 999 708 1000 /* Create root node */ 709 fs_node_t *rfn; 710 int rc = create_node(&rfn, service_id, L_DIRECTORY, cdfs_index++); 1001 int rc = create_node(&rfn, fs, L_DIRECTORY, cdfs_index++); 711 1002 712 1003 if ((rc != EOK) || (!rfn)) 713 return false;1004 goto error; 714 1005 715 1006 /* FS root is not linked */ … … 719 1010 720 1011 /* Check if there is cdfs in given session */ 721 if (!iso_readfs(service_id, rfn, altroot)) { 722 // XXX destroy node 723 return false; 724 } 725 726 return true; 1012 if (!iso_readfs(fs, rfn, altroot)) 1013 goto error; 1014 1015 list_append(&fs->link, &cdfs_instances); 1016 return fs; 1017 error: 1018 // XXX destroy node 1019 free(fs); 1020 return NULL; 727 1021 } 728 1022 … … 742 1036 altroot = 0; 743 1037 } else { 744 /* Read TOC and find the last session */ 745 toc_block_t *toc = block_get_toc(service_id, 1); 746 if ((toc != NULL) && (uint16_t_be2host(toc->size) == 10)) { 747 altroot = uint32_t_be2host(toc->first_lba); 748 free(toc); 749 } 1038 /* 1039 * Read TOC multisession information and get the start address 1040 * of the first track in the last session 1041 */ 1042 scsi_toc_multisess_data_t toc; 1043 1044 rc = block_read_toc(service_id, 1, &toc, sizeof(toc)); 1045 if (rc == EOK && (uint16_t_be2host(toc.toc_len) == 10)) 1046 altroot = uint32_t_be2host(toc.ftrack_lsess.start_addr); 750 1047 } 751 1048 … … 768 1065 } 769 1066 770 /* Initialize cdfs instance */771 if ( !cdfs_instance_init(service_id, altroot)) {1067 /* Create cdfs instance */ 1068 if (cdfs_fs_create(service_id, altroot) == NULL) { 772 1069 block_cache_fini(service_id); 773 1070 block_fini(service_id); … … 792 1089 cdfs_node_t *node = hash_table_get_inst(item, cdfs_node_t, nh_link); 793 1090 794 if (node-> service_id == service_id) {1091 if (node->fs->service_id == service_id) { 795 1092 hash_table_remove_item(&nodes, &node->nh_link); 796 1093 } … … 799 1096 } 800 1097 801 static void cdfs_instance_done(service_id_t service_id) 802 { 803 hash_table_apply(&nodes, rm_service_id_nodes, &service_id); 804 block_cache_fini(service_id); 805 block_fini(service_id); 1098 static void cdfs_fs_destroy(cdfs_t *fs) 1099 { 1100 list_remove(&fs->link); 1101 hash_table_apply(&nodes, rm_service_id_nodes, &fs->service_id); 1102 block_cache_fini(fs->service_id); 1103 block_fini(fs->service_id); 1104 free(fs); 1105 } 1106 1107 static cdfs_t *cdfs_find_by_sid(service_id_t service_id) 1108 { 1109 list_foreach(cdfs_instances, link, cdfs_t, fs) { 1110 if (fs->service_id == service_id) 1111 return fs; 1112 } 1113 1114 return NULL; 806 1115 } 807 1116 808 1117 static int cdfs_unmounted(service_id_t service_id) 809 1118 { 810 cdfs_instance_done(service_id); 1119 cdfs_t *fs; 1120 1121 fs = cdfs_find_by_sid(service_id); 1122 if (fs == NULL) 1123 return ENOENT; 1124 1125 cdfs_fs_destroy(fs); 811 1126 return EOK; 812 1127 } … … 828 1143 829 1144 if (!node->processed) { 830 int rc = cdfs_readdir( service_id, FS_NODE(node));1145 int rc = cdfs_readdir(node->fs, FS_NODE(node)); 831 1146 if (rc != EOK) 832 1147 return rc; … … 911 1226 /* Some nodes were requested to be removed from the cache. */ 912 1227 if (0 < *premove_cnt) { 913 cdfs_node_t *node = 1228 cdfs_node_t *node = hash_table_get_inst(item, cdfs_node_t, nh_link); 914 1229 915 1230 if (!node->opened) {
Note:
See TracChangeset
for help on using the changeset viewer.