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