1 /* 2 * Copyright 1999, Be Incorporated. All Rights Reserved. 3 * This file may be used under the terms of the Be Sample Code License. 4 * 5 * Copyright 2001, pinc Software. All Rights Reserved. 6 * 7 * iso9960/multi-session, 1.0.0 8 */ 9 10 11 #include <ctype.h> 12 #include <dirent.h> 13 #include <errno.h> 14 #include <fcntl.h> 15 #include <stdio.h> 16 #include <stdlib.h> 17 #include <string.h> 18 #include <sys/stat.h> 19 #include <time.h> 20 #include <unistd.h> 21 22 #include <KernelExport.h> 23 #include <NodeMonitor.h> 24 #include <fs_interface.h> 25 #include <fs_cache.h> 26 27 #include <fs_attr.h> 28 #include <fs_info.h> 29 #include <fs_index.h> 30 #include <fs_query.h> 31 #include <fs_volume.h> 32 33 #include <util/kernel_cpp.h> 34 35 #include "iso9660.h" 36 #include "iso9660_identify.h" 37 38 39 //#define TRACE_ISO9660 40 #ifdef TRACE_ISO9660 41 # define TRACE(x) dprintf x 42 #else 43 # define TRACE(x) ; 44 #endif 45 46 47 struct identify_cookie { 48 iso9660_info info; 49 }; 50 51 52 // #pragma mark - Scanning 53 54 55 static float 56 fs_identify_partition(int fd, partition_data *partition, void **_cookie) 57 { 58 iso9660_info *info = new iso9660_info; 59 60 status_t status = iso9660_fs_identify(fd, info); 61 if (status != B_OK) { 62 delete info; 63 return -1; 64 } 65 66 *_cookie = info; 67 return 0.6f; 68 } 69 70 71 static status_t 72 fs_scan_partition(int fd, partition_data *partition, void *_cookie) 73 { 74 iso9660_info *info = (iso9660_info *)_cookie; 75 76 partition->status = B_PARTITION_VALID; 77 partition->flags |= B_PARTITION_FILE_SYSTEM | B_PARTITION_READ_ONLY ; 78 partition->block_size = ISO_PVD_SIZE; 79 partition->content_size = ISO_PVD_SIZE * info->max_blocks; 80 partition->content_name = strdup(info->PreferredName()); 81 82 if (partition->content_name == NULL) 83 return B_NO_MEMORY; 84 85 return B_OK; 86 } 87 88 89 static void 90 fs_free_identify_partition_cookie(partition_data *partition, void *_cookie) 91 { 92 delete (iso9660_info *)_cookie; 93 } 94 95 96 // #pragma mark - FS hooks 97 98 99 static status_t 100 fs_mount(dev_t mountID, const char *device, uint32 flags, 101 const char *args, void **_volume, ino_t *_rootID) 102 { 103 bool allowJoliet = true; 104 nspace *volume; 105 106 // Check for a 'nojoliet' parm 107 // all we check for is the existance of 'nojoliet' in the parms. 108 if (args != NULL) { 109 uint32 i; 110 char *spot; 111 char *buf = strdup(args); 112 113 uint32 len = strlen(buf); 114 // lower case the parms data 115 for (i = 0; i < len + 1; i++) 116 buf[i] = tolower(buf[i]); 117 118 // look for nojoliet 119 spot = strstr(buf, "nojoliet"); 120 if (spot != NULL) 121 allowJoliet = false; 122 123 free(buf); 124 } 125 126 // Try and mount volume as an ISO volume. 127 status_t result = ISOMount(device, O_RDONLY, &volume, allowJoliet); 128 if (result == B_OK) { 129 *_rootID = ISO_ROOTNODE_ID; 130 *_volume = volume; 131 132 volume->id = mountID; 133 134 result = publish_vnode(mountID, *_rootID, &volume->rootDirRec); 135 if (result != B_OK) { 136 block_cache_delete(volume->fBlockCache, false); 137 free(volume); 138 result = B_ERROR; 139 } 140 } 141 return result; 142 } 143 144 145 static status_t 146 fs_unmount(void *_ns) 147 { 148 status_t result = B_NO_ERROR; 149 nspace *ns = (nspace *)_ns; 150 151 TRACE(("fs_unmount - ENTER\n")); 152 153 // Unlike in BeOS, we need to put the reference to our root node ourselves 154 put_vnode(ns->id, ISO_ROOTNODE_ID); 155 156 block_cache_delete(ns->fBlockCache, false); 157 close(ns->fdOfSession); 158 result = close(ns->fd); 159 160 free(ns); 161 162 TRACE(("fs_unmount - EXIT, result is %s\n", strerror(result))); 163 return result; 164 } 165 166 167 static status_t 168 fs_read_fs_stat(void *_ns, struct fs_info *fss) 169 { 170 nspace *ns = (nspace *)_ns; 171 int i; 172 173 fss->flags = B_FS_IS_PERSISTENT | B_FS_IS_READONLY; 174 fss->block_size = ns->logicalBlkSize[FS_DATA_FORMAT]; 175 fss->io_size = 65536; 176 fss->total_blocks = ns->volSpaceSize[FS_DATA_FORMAT]; 177 fss->free_blocks = 0; 178 179 strncpy(fss->device_name, ns->devicePath, sizeof(fss->device_name)); 180 181 strncpy(fss->volume_name, ns->volIDString, sizeof(fss->volume_name)); 182 for (i = strlen(fss->volume_name) - 1; i >=0 ; i--) { 183 if (fss->volume_name[i] != ' ') 184 break; 185 } 186 187 if (i < 0) 188 strcpy(fss->volume_name, "UNKNOWN"); 189 else 190 fss->volume_name[i + 1] = 0; 191 192 strcpy(fss->fsh_name, "iso9660"); 193 return B_OK; 194 } 195 196 197 static status_t 198 fs_get_vnode_name(void *ns, void *_node, char *buffer, size_t bufferSize) 199 { 200 vnode *node = (vnode*)_node; 201 202 strlcpy(buffer, node->fileIDString, bufferSize); 203 return B_OK; 204 } 205 206 207 static status_t 208 fs_walk(void *_ns, void *base, const char *file, ino_t *_vnodeID, int *_type) 209 { 210 nspace *ns = (nspace *)_ns; 211 vnode *baseNode = (vnode*)base; 212 vnode *newNode = NULL; 213 214 TRACE(("fs_walk - looking for %s in dir file of length %d\n", file, 215 baseNode->dataLen[FS_DATA_FORMAT])); 216 217 if (strcmp(file, ".") == 0) { 218 // base directory 219 TRACE(("fs_walk - found \".\" file.\n")); 220 *_vnodeID = baseNode->id; 221 *_type = S_IFDIR; 222 return get_vnode(ns->id, *_vnodeID, (void **)&newNode); 223 } else if (strcmp(file, "..") == 0) { 224 // parent directory 225 TRACE(("fs_walk - found \"..\" file.\n")); 226 *_vnodeID = baseNode->parID; 227 *_type = S_IFDIR; 228 return get_vnode(ns->id, *_vnodeID, (void **)&newNode); 229 } 230 231 // look up file in the directory 232 uint32 dataLength = baseNode->dataLen[FS_DATA_FORMAT]; 233 status_t result = ENOENT; 234 uint32 totalRead = 0; 235 off_t block = baseNode->startLBN[FS_DATA_FORMAT]; 236 bool done = false; 237 238 while (totalRead < dataLength && !done) { 239 off_t cachedBlock = block; 240 char *blockData = (char *)block_cache_get_etc(ns->fBlockCache, block, 0, 241 ns->logicalBlkSize[FS_DATA_FORMAT]); 242 if (blockData != NULL) { 243 int bytesRead = 0; 244 off_t blockBytesRead = 0; 245 vnode node; 246 int initResult; 247 248 TRACE(("fs_walk - read buffer from disk at LBN %Ld into buffer 0x%x.\n", 249 block, blockData)); 250 251 // Move to the next 2-block set if necessary 252 // Don't go over end of buffer, if dir record sits on boundary. 253 254 node.fileIDString = NULL; 255 node.attr.slName = NULL; 256 257 while (blockBytesRead < 2 * ns->logicalBlkSize[FS_DATA_FORMAT] 258 && totalRead + blockBytesRead < dataLength 259 && blockData[0] != 0 260 && !done) { 261 initResult = InitNode(&node, blockData, &bytesRead, 262 ns->joliet_level); 263 TRACE(("fs_walk - InitNode returned %s, filename %s, %d bytes read\n", strerror(initResult), node.fileIDString, bytesRead)); 264 265 if (initResult == B_OK) { 266 if (!strcmp(node.fileIDString, file)) { 267 TRACE(("fs_walk - success, found vnode at block %Ld, pos %Ld\n", block, blockBytesRead)); 268 *_vnodeID = (block << 30) 269 + (blockBytesRead & 0xffffffff); 270 TRACE(("fs_walk - New vnode id is %Ld\n", *_vnodeID)); 271 272 result = get_vnode(ns->id, *_vnodeID, 273 (void **)&newNode); 274 if (result == B_OK) { 275 newNode->parID = baseNode->id; 276 done = true; 277 } 278 } else { 279 free(node.fileIDString); 280 node.fileIDString = NULL; 281 free(node.attr.slName); 282 node.attr.slName = NULL; 283 } 284 } else { 285 result = initResult; 286 if (bytesRead == 0) 287 done = TRUE; 288 } 289 blockData += bytesRead; 290 blockBytesRead += bytesRead; 291 292 TRACE(("fs_walk - Adding %d bytes to blockBytes read (total %Ld/%Ld).\n", 293 bytesRead, blockBytesRead, baseNode->dataLen[FS_DATA_FORMAT])); 294 } 295 totalRead += ns->logicalBlkSize[FS_DATA_FORMAT]; 296 block++; 297 298 TRACE(("fs_walk - moving to next block %Ld, total read %Ld\n", block, totalRead)); 299 block_cache_put(ns->fBlockCache, cachedBlock); 300 301 } else 302 done = TRUE; 303 } 304 305 if (newNode) 306 *_type = newNode->attr.stat[FS_DATA_FORMAT].st_mode; 307 308 TRACE(("fs_walk - EXIT, result is %s, vnid is %Lu\n", 309 strerror(result), *_vnodeID)); 310 return result; 311 } 312 313 314 static status_t 315 fs_read_vnode(void *_ns, ino_t vnodeID, void **_node, bool reenter) 316 { 317 nspace *ns = (nspace*)_ns; 318 319 vnode *newNode = (vnode*)calloc(sizeof(vnode), 1); 320 if (newNode == NULL) 321 return B_NO_MEMORY; 322 323 uint32 pos = vnodeID & 0x3fffffff; 324 uint32 block = vnodeID >> 30; 325 326 TRACE(("fs_read_vnode - block = %ld, pos = %ld, raw = %Lu node 0x%x\n", 327 block, pos, vnodeID, newNode)); 328 329 if (pos > ns->logicalBlkSize[FS_DATA_FORMAT]) 330 return B_BAD_VALUE; 331 332 char *data = (char *)block_cache_get_etc(ns->fBlockCache, 333 block, 0, ns->logicalBlkSize[FS_DATA_FORMAT]); 334 if (data == NULL) { 335 free(newNode); 336 return B_IO_ERROR; 337 } 338 339 status_t result = InitNode(newNode, data + pos, NULL, ns->joliet_level); 340 block_cache_put(ns->fBlockCache, block); 341 342 if (result < B_OK) { 343 free(newNode); 344 return result; 345 } 346 347 newNode->id = vnodeID; 348 *_node = (void *)newNode; 349 350 if ((newNode->flags & ISO_ISDIR) == 0) { 351 newNode->cache = file_cache_create(ns->id, vnodeID, 352 newNode->dataLen[FS_DATA_FORMAT]); 353 } 354 355 return B_OK; 356 } 357 358 359 static status_t 360 fs_release_vnode(void *ns, void *_node, bool reenter) 361 { 362 status_t result = B_NO_ERROR; 363 vnode *node = (vnode*)_node; 364 365 (void)ns; 366 (void)reenter; 367 368 TRACE(("fs_release_vnode - ENTER (0x%x)\n", node)); 369 370 if (node != NULL) { 371 if (node->id != ISO_ROOTNODE_ID) { 372 if (node->fileIDString != NULL) 373 free (node->fileIDString); 374 if (node->attr.slName != NULL) 375 free (node->attr.slName); 376 if (node->cache != NULL) 377 file_cache_delete(node->cache); 378 379 free(node); 380 } 381 } 382 383 TRACE(("fs_release_vnode - EXIT\n")); 384 return result; 385 } 386 387 388 static status_t 389 fs_read_pages(fs_volume _fs, fs_vnode _node, fs_cookie _cookie, off_t pos, 390 const iovec *vecs, size_t count, size_t *_numBytes, bool reenter) 391 { 392 nspace *ns = (nspace *)_fs; 393 vnode *node = (vnode *)_node; 394 395 uint32 fileSize = node->dataLen[FS_DATA_FORMAT]; 396 size_t bytesLeft = *_numBytes; 397 398 if (pos >= fileSize) { 399 *_numBytes = 0; 400 return B_OK; 401 } 402 if (pos + bytesLeft > fileSize) { 403 bytesLeft = fileSize - pos; 404 *_numBytes = bytesLeft; 405 } 406 407 file_io_vec fileVec; 408 fileVec.offset = pos + node->startLBN[FS_DATA_FORMAT] 409 * ns->logicalBlkSize[FS_DATA_FORMAT]; 410 fileVec.length = bytesLeft; 411 412 uint32 vecIndex = 0; 413 size_t vecOffset = 0; 414 return read_file_io_vec_pages(ns->fd, &fileVec, 1, vecs, count, 415 &vecIndex, &vecOffset, &bytesLeft); 416 } 417 418 419 static status_t 420 fs_read_stat(void *_ns, void *_node, struct stat *st) 421 { 422 nspace *ns = (nspace*)_ns; 423 vnode *node = (vnode*)_node; 424 status_t result = B_NO_ERROR; 425 time_t time; 426 427 TRACE(("fs_read_stat - ENTER\n")); 428 429 st->st_dev = ns->id; 430 st->st_ino = node->id; 431 st->st_nlink = node->attr.stat[FS_DATA_FORMAT].st_nlink; 432 st->st_uid = node->attr.stat[FS_DATA_FORMAT].st_uid; 433 st->st_gid = node->attr.stat[FS_DATA_FORMAT].st_gid; 434 st->st_blksize = 65536; 435 st->st_mode = node->attr.stat[FS_DATA_FORMAT].st_mode; 436 437 // Same for file/dir in ISO9660 438 st->st_size = node->dataLen[FS_DATA_FORMAT]; 439 if (ConvertRecDate(&(node->recordDate), &time) == B_NO_ERROR) 440 st->st_ctime = st->st_mtime = st->st_atime = time; 441 442 TRACE(("fs_read_stat - EXIT, result is %s\n", strerror(result))); 443 444 return result; 445 } 446 447 448 static status_t 449 fs_open(void *_ns, void *_node, int omode, void **cookie) 450 { 451 status_t result = B_NO_ERROR; 452 453 (void)_ns; 454 (void)cookie; 455 456 // Do not allow any of the write-like open modes to get by 457 if ((omode == O_WRONLY) || (omode == O_RDWR)) 458 result = EROFS; 459 else if((omode & O_TRUNC) || (omode & O_CREAT)) 460 result = EROFS; 461 462 return result; 463 } 464 465 466 static status_t 467 fs_read(void *_ns, void *_node, void *cookie, off_t pos, void *buffer, 468 size_t *_length) 469 { 470 vnode *node = (vnode *)_node; 471 472 if (node->flags & ISO_ISDIR) 473 return EISDIR; 474 475 uint32 fileSize = node->dataLen[FS_DATA_FORMAT]; 476 477 // set/check boundaries for pos/length 478 if (pos < 0) 479 return B_BAD_VALUE; 480 if (pos >= fileSize) { 481 *_length = 0; 482 return B_OK; 483 } 484 485 return file_cache_read(node->cache, NULL, pos, buffer, _length); 486 } 487 488 489 static status_t 490 fs_close(void *ns, void *node, void *cookie) 491 { 492 (void)ns; 493 (void)node; 494 (void)cookie; 495 496 return B_OK; 497 } 498 499 500 static status_t 501 fs_free_cookie(void *ns, void *node, void *cookie) 502 { 503 (void)ns; 504 (void)node; 505 (void)cookie; 506 507 return B_OK; 508 } 509 510 511 static status_t 512 fs_access(void *ns, void *node, int mode) 513 { 514 (void)ns; 515 (void)node; 516 (void)mode; 517 518 return B_OK; 519 } 520 521 522 static status_t 523 fs_read_link(void */*_volume*/, void *_node, char *buffer, size_t *_bufferSize) 524 { 525 vnode *node = (vnode *)_node; 526 527 if (!S_ISLNK(node->attr.stat[FS_DATA_FORMAT].st_mode)) 528 return B_BAD_VALUE; 529 530 size_t length = strlen(node->attr.slName); 531 if (length > *_bufferSize) 532 memcpy(buffer, node->attr.slName, *_bufferSize); 533 else { 534 memcpy(buffer, node->attr.slName, length); 535 *_bufferSize = length; 536 } 537 538 return B_OK; 539 } 540 541 542 static status_t 543 fs_open_dir(void *_ns, void *_node, void **cookie) 544 { 545 vnode *node = (vnode *)_node; 546 547 TRACE(("fs_open_dir - node is 0x%x\n", _node)); 548 549 if (!(node->flags & ISO_ISDIR)) 550 return B_NOT_A_DIRECTORY; 551 552 dircookie *dirCookie = (dircookie *)malloc(sizeof(dircookie)); 553 if (dirCookie == NULL) 554 return B_NO_MEMORY; 555 556 dirCookie->startBlock = node->startLBN[FS_DATA_FORMAT]; 557 dirCookie->block = node->startLBN[FS_DATA_FORMAT]; 558 dirCookie->totalSize = node->dataLen[FS_DATA_FORMAT]; 559 dirCookie->pos = 0; 560 dirCookie->id = node->id; 561 *cookie = (void *)dirCookie; 562 563 return B_OK; 564 } 565 566 567 static status_t 568 fs_read_dir(void *_ns, void *_node, void *_cookie, struct dirent *buffer, 569 size_t bufferSize, uint32 *num) 570 { 571 nspace *ns = (nspace *)_ns; 572 dircookie *dirCookie = (dircookie *)_cookie; 573 574 TRACE(("fs_read_dir - ENTER\n")); 575 576 status_t result = ISOReadDirEnt(ns, dirCookie, buffer, bufferSize); 577 578 // If we succeeded, return 1, the number of dirents we read. 579 if (result == B_OK) 580 *num = 1; 581 else 582 *num = 0; 583 584 // When you get to the end, don't return an error, just return 585 // a zero in *num. 586 587 if (result == ENOENT) 588 result = B_OK; 589 590 TRACE(("fs_read_dir - EXIT, result is %s\n", strerror(result))); 591 return result; 592 } 593 594 595 static status_t 596 fs_rewind_dir(void *ns, void *node, void* _cookie) 597 { 598 dircookie *cookie = (dircookie*)_cookie; 599 600 cookie->block = cookie->startBlock; 601 cookie->pos = 0; 602 return B_OK; 603 } 604 605 606 static status_t 607 fs_close_dir(void *ns, void *node, void *cookie) 608 { 609 return B_OK; 610 } 611 612 613 static status_t 614 fs_free_dir_cookie(void *ns, void *node, void *cookie) 615 { 616 free(cookie); 617 return B_OK; 618 } 619 620 621 // #pragma mark - 622 623 624 static status_t 625 iso_std_ops(int32 op, ...) 626 { 627 switch (op) { 628 case B_MODULE_INIT: 629 case B_MODULE_UNINIT: 630 return B_OK; 631 default: 632 return B_ERROR; 633 } 634 } 635 636 637 static file_system_module_info sISO660FileSystem = { 638 { 639 "file_systems/iso9660" B_CURRENT_FS_API_VERSION, 640 0, 641 iso_std_ops, 642 }, 643 644 "ISO9660 File System", 645 0, // DDM flags 646 647 // scanning 648 fs_identify_partition, 649 fs_scan_partition, 650 fs_free_identify_partition_cookie, 651 NULL, // free_partition_content_cookie() 652 653 &fs_mount, 654 &fs_unmount, 655 &fs_read_fs_stat, 656 NULL, 657 NULL, 658 659 /* vnode operations */ 660 &fs_walk, 661 &fs_get_vnode_name, 662 &fs_read_vnode, 663 &fs_release_vnode, 664 NULL, // fs_remove_vnode() 665 666 /* VM file access */ 667 NULL, // fs_can_page 668 &fs_read_pages, 669 NULL, // fs_write_pages 670 671 NULL, // fs_get_file_map 672 673 NULL, // fs_ioctl 674 NULL, // fs_set_flags 675 NULL, // fs_select 676 NULL, // fs_deselect 677 NULL, // fs_fsync 678 679 &fs_read_link, 680 NULL, // fs_create_symlink 681 682 NULL, // fs_link, 683 NULL, // fs_unlink 684 NULL, // fs_rename 685 686 &fs_access, 687 &fs_read_stat, 688 NULL, // fs_write_stat 689 690 /* file operations */ 691 NULL, // fs_create 692 &fs_open, 693 &fs_close, 694 &fs_free_cookie, 695 &fs_read, 696 NULL, // fs_write 697 698 /* directory operations */ 699 NULL, // fs_create_dir 700 NULL, // fs_remove_dir 701 &fs_open_dir, 702 &fs_close_dir, 703 &fs_free_dir_cookie, 704 &fs_read_dir, 705 &fs_rewind_dir, 706 707 /* attribute directory operations */ 708 NULL, // fs_open_attr_dir 709 NULL, // fs_close_attr_dir 710 NULL, // fs_free_attr_dir_cookie 711 NULL, // fs_read_attr_dir 712 NULL, // fs_rewind_attr_dir 713 714 /* attribute operations */ 715 NULL, // fs_create_attr 716 NULL, // fs_open_attr 717 NULL, // fs_close_attr 718 NULL, // fs_free_attr_cookie 719 NULL, // fs_read_attr 720 NULL, // fs_write_attr 721 722 NULL, // fs_read_attr_stat 723 NULL, // fs_write_attr_stat 724 NULL, // fs_rename_attr 725 NULL, // fs_remove_attr 726 727 /* index directory & index operations */ 728 NULL, // fs_open_index_dir 729 NULL, // fs_close_index_dir 730 NULL, // fs_free_index_dir_cookie 731 NULL, // fs_read_index_dir 732 NULL, // fs_rewind_index_dir 733 734 NULL, // fs_create_index 735 NULL, // fs_remove_index 736 NULL, // fs_stat_index 737 738 /* query operations */ 739 NULL, // fs_open_query 740 NULL, // fs_close_query 741 NULL, // fs_free_query_cookie 742 NULL, // fs_read_query 743 NULL, // fs_rewind_query 744 }; 745 746 module_info *modules[] = { 747 (module_info *)&sISO660FileSystem, 748 NULL, 749 }; 750 751