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 ** 2001-03-11: added multi-session support, axeld. 9 */ 10 11 #include <stdlib.h> 12 #include <unistd.h> 13 #include <fcntl.h> 14 #include <errno.h> 15 #include <string.h> 16 #include <ctype.h> 17 #include <stdio.h> 18 #include <dirent.h> 19 #include <sys/stat.h> 20 #include <time.h> 21 #include <malloc.h> 22 23 #include <KernelExport.h> 24 #include <NodeMonitor.h> 25 #include <fs_interface.h> 26 #include <fs_cache.h> 27 28 #include <fs_attr.h> 29 #include <fs_info.h> 30 #include <fs_index.h> 31 #include <fs_query.h> 32 #include <fs_volume.h> 33 34 #include <util/kernel_cpp.h> 35 36 #include "iso.h" 37 #include "iso9660.h" 38 39 //#define TRACE_ISO9660 1 40 #if TRACE_ISO9660 41 # define TRACE(x) dprintf x 42 #else 43 # define TRACE(x) ; 44 #endif 45 46 47 /* Start of fundamental (read-only) required functions */ 48 static status_t fs_mount(mount_id mountID, const char *device, uint32 flags, 49 const char *args, void **_data, vnode_id *_rootID); 50 static status_t fs_unmount(void *ns); 51 52 static status_t fs_get_vnode_name(void *_ns, void *_node, 53 char *buffer, size_t bufferSize); 54 static status_t fs_walk(void *_ns, void *_base, const char *file, 55 vnode_id *_vnodeID, int *_type); 56 57 static status_t fs_read_vnode(void *_ns, vnode_id vnid, void **node, bool reenter); 58 static status_t fs_release_vnode(void *_ns, void *_node, bool reenter); 59 static status_t fs_get_file_map(fs_volume _fs, fs_vnode _node, off_t offset, size_t size, 60 struct file_io_vec *vecs, size_t *_count); 61 static status_t fs_read_stat(void *_ns, void *_node, struct stat *st); 62 static status_t fs_open(void *_ns, void *_node, int omode, void **cookie); 63 static status_t fs_read(void *_ns, void *_node, void *cookie, off_t pos, 64 void *buf, size_t *len); 65 /// fs_free_cookie - free cookie for file created in open. 66 static status_t fs_free_cookie(void *ns, void *node, void *cookie); 67 static status_t fs_close(void *ns, void *node, void *cookie); 68 69 // fs_access - checks permissions for access. 70 static status_t fs_access(void *_ns, void *_node, int mode); 71 72 // fs_opendir - creates fs-specific "cookie" struct that can tell where 73 // we are at in the directory list. 74 static status_t fs_open_dir(void* _ns, void* _node, void** cookie); 75 // fs_readdir - read 1 or more dirents, keep state in cookie, return 76 // 0 when no more entries. 77 static status_t fs_read_dir(void *_ns, void *_node, void *cookie, struct dirent *buf, 78 size_t bufsize, uint32 *_num); 79 // fs_rewinddir - set cookie to represent beginning of directory, so 80 // later fs_readdir calls start at beginning. 81 static status_t fs_rewind_dir(void *_ns, void *_node, void *cookie); 82 // fs_closedir - Do whatever you need to to close a directory (sometimes 83 // nothing), but DON'T free the cookie! 84 static status_t fs_close_dir(void *_ns, void *_node, void *cookie); 85 // fs_free_dircookie - Free the fs-specific cookie struct 86 static status_t fs_free_dir_cookie(void *_ns, void *_node, void *cookie); 87 88 // fs_rfsstat - Fill in fs_info struct for device. 89 static status_t fs_read_fs_stat(void *_ns, struct fs_info *); 90 91 // fs_readlink - Read in the name of a symbolic link. 92 static status_t fs_read_link(void *_ns, void *_node, char *buf, size_t *bufsize); 93 94 95 // #pragma mark - Scanning 96 97 struct identify_cookie { 98 iso9660_info info; 99 }; 100 101 102 static float 103 fs_identify_partition(int fd, partition_data *partition, void **_cookie) 104 { 105 iso9660_info info; 106 status_t status = iso9660_fs_identify(fd, &info); 107 if (status != B_OK) 108 return status; 109 110 identify_cookie *cookie = new identify_cookie; 111 memcpy(&cookie->info, &info, sizeof(info)); 112 113 *_cookie = cookie; 114 return 0.8f; 115 } 116 117 118 static status_t 119 fs_scan_partition(int fd, partition_data *partition, void *_cookie) 120 { 121 identify_cookie *cookie = (identify_cookie *)_cookie; 122 123 partition->status = B_PARTITION_VALID; 124 partition->flags |= B_PARTITION_FILE_SYSTEM | B_PARTITION_READ_ONLY ; 125 partition->block_size = ISO_PVD_SIZE; 126 partition->content_size = ISO_PVD_SIZE * cookie->info.maxBlocks; 127 partition->content_name = strdup(cookie->info.get_preferred_volume_name()); 128 if (partition->content_name == NULL) 129 return B_NO_MEMORY; 130 131 return B_OK; 132 } 133 134 135 static void 136 fs_free_identify_partition_cookie(partition_data *partition, void *_cookie) 137 { 138 identify_cookie *cookie = (identify_cookie *)_cookie; 139 140 delete cookie; 141 } 142 143 144 static status_t 145 fs_mount(mount_id mountID, const char *device, uint32 flags, 146 const char *args, void **_data, vnode_id *_rootID) 147 { 148 /* 149 Kernel passes in nspace_id, (representing a disk or partition?) 150 and a string representing the device (eg, "/dev/scsi/disk/030/raw) 151 Flags will be used for things like specifying read-only mounting. 152 parms is parameters passed in as switches from the mount command, 153 and len is the length of the otions. data is a pointer to a 154 driver-specific struct that should be allocated in this routine. 155 It will then be passed back in by the kernel to a number of the other 156 fs driver functions. vnid should also be passed back to the kernel, 157 representing the vnode id of the root vnode. 158 */ 159 status_t result = EINVAL; 160 // return EINVAL if it's not a device compatible with the driver. 161 bool allow_joliet = true; 162 nspace *vol; 163 164 (void)flags; 165 166 /* Create semaphore if it's not already created. When do we need to 167 use semaphores? */ 168 169 // Check for a 'nojoliet' parm 170 // all we check for is the existance of 'nojoliet' in the parms. 171 if (args != NULL) { 172 uint32 i; 173 char *spot; 174 char *buf = strdup(args); 175 176 uint32 len = strlen(buf); 177 // lower case the parms data 178 for (i = 0; i < len + 1; i++) 179 buf[i] = tolower(buf[i]); 180 181 // look for nojoliet 182 spot = strstr(buf, "nojoliet"); 183 if (spot != NULL) 184 allow_joliet = false; 185 186 free(buf); 187 } 188 189 // Try and mount volume as an ISO volume. 190 result = ISOMount(device, O_RDONLY, &vol, allow_joliet); 191 192 // If it is ISO … 193 if (result == B_NO_ERROR) { 194 //vnode_id rootID = vol->rootDirRec.startLBN[FS_DATA_FORMAT]; 195 //*vnid = rootID; 196 *_rootID = ISO_ROOTNODE_ID; 197 *_data = (void*)vol; 198 199 vol->id = mountID; 200 201 // You MUST do this. Create the vnode for the root. 202 result = publish_vnode(mountID, *_rootID, (void*)&(vol->rootDirRec)); 203 if (result != B_NO_ERROR) { 204 block_cache_delete(vol->fBlockCache, false); 205 free(vol); 206 result = EINVAL; 207 } else 208 result = B_NO_ERROR; 209 } 210 return result; 211 } 212 213 214 static status_t 215 fs_unmount(void *_ns) 216 { 217 status_t result = B_NO_ERROR; 218 nspace *ns = (nspace *)_ns; 219 220 TRACE(("fs_unmount - ENTER\n")); 221 222 // Unlike in BeOS, we need to put the reference to our root node ourselves 223 put_vnode(ns->id, ISO_ROOTNODE_ID); 224 225 block_cache_delete(ns->fBlockCache, false); 226 close(ns->fdOfSession); 227 result = close(ns->fd); 228 229 free(ns); 230 231 TRACE(("fs_unmount - EXIT, result is %s\n", strerror(result))); 232 return result; 233 } 234 235 236 static status_t 237 fs_read_fs_stat(void *_ns, struct fs_info *fss) 238 { 239 // Fill in fs_info struct for device. 240 nspace *ns = (nspace *)_ns; 241 int i; 242 243 TRACE(("fs_read_fs_stat - ENTER\n")); 244 245 // Fill in device id. 246 //fss->dev = ns->fd; 247 248 // Root vnode ID 249 //fss->root = ISO_ROOTNODE_ID; 250 251 // File system flags. 252 fss->flags = B_FS_IS_PERSISTENT | B_FS_IS_READONLY; 253 254 // FS block size. 255 fss->block_size = ns->logicalBlkSize[FS_DATA_FORMAT]; 256 257 // IO size - specifies buffer size for file copying 258 fss->io_size = 65536; 259 260 // Total blocks? 261 fss->total_blocks = ns->volSpaceSize[FS_DATA_FORMAT]; 262 263 // Free blocks = 0, read only 264 fss->free_blocks = 0; 265 266 // Device name. 267 strncpy(fss->device_name, ns->devicePath, sizeof(fss->device_name)); 268 269 strncpy(fss->volume_name, ns->volIDString, sizeof(fss->volume_name)); 270 for (i = strlen(fss->volume_name)-1; i >=0 ; i--) 271 if (fss->volume_name[i] != ' ') 272 break; 273 274 if (i < 0) 275 strcpy(fss->volume_name, "UNKNOWN"); 276 else 277 fss->volume_name[i + 1] = 0; 278 279 // File system name 280 strcpy(fss->fsh_name, "iso9660"); 281 282 TRACE(("fs_read_fs_stat - EXIT\n")); 283 return 0; 284 } 285 286 287 static status_t 288 fs_get_vnode_name(void *ns, void *_node, char *buffer, size_t bufferSize) 289 { 290 vnode *node = (vnode*)_node; 291 292 strlcpy(buffer, node->fileIDString, bufferSize); 293 return B_OK; 294 } 295 296 297 /* fs_walk - the walk function just "walks" through a directory looking for 298 the specified file. When you find it, call get_vnode on its vnid to init 299 it for the kernel. 300 */ 301 static status_t 302 fs_walk(void *_ns, void *base, const char *file, vnode_id *_vnodeID, int *_type) 303 { 304 /* Starting at the base, find file in the subdir, and return path 305 string and vnode id of file. */ 306 nspace *ns = (nspace *)_ns; 307 vnode *baseNode = (vnode*)base; 308 uint32 dataLen = baseNode->dataLen[FS_DATA_FORMAT]; 309 vnode *newNode = NULL; 310 status_t result = ENOENT; 311 bool done = FALSE; 312 uint32 totalRead = 0; 313 off_t block = baseNode->startLBN[FS_DATA_FORMAT]; 314 315 TRACE(("fs_walk - looking for %s in dir file of length %d\n", file, 316 baseNode->dataLen[FS_DATA_FORMAT])); 317 318 if (strcmp(file, ".") == 0) { 319 // base directory 320 TRACE(("fs_walk - found \".\" file.\n")); 321 *_vnodeID = baseNode->id; 322 *_type = S_IFDIR; 323 if (get_vnode(ns->id, *_vnodeID, (void **)&newNode) != 0) 324 result = EINVAL; 325 else 326 result = B_NO_ERROR; 327 } else if (strcmp(file, "..") == 0) { 328 // parent directory 329 TRACE(("fs_walk - found \"..\" file.\n")); 330 *_vnodeID = baseNode->parID; 331 *_type = S_IFDIR; 332 if (get_vnode(ns->id, *_vnodeID, (void **)&newNode) != 0) 333 result = EINVAL; 334 else 335 result = B_NO_ERROR; 336 } else { 337 // look up file in the directory 338 char *blockData; 339 340 while ((totalRead < dataLen) && !done) { 341 off_t cachedBlock = block; 342 343 blockData = (char *)block_cache_get_etc(ns->fBlockCache, block, 0, ns->logicalBlkSize[FS_DATA_FORMAT]); 344 if (blockData != NULL) { 345 int bytesRead = 0; 346 off_t blockBytesRead = 0; 347 vnode node; 348 int initResult; 349 350 TRACE(("fs_walk - read buffer from disk at LBN %Ld into buffer 0x%x.\n", 351 block, blockData)); 352 353 // Move to the next 2-block set if necessary 354 // Don't go over end of buffer, if dir record sits on boundary. 355 356 node.fileIDString = NULL; 357 node.attr.slName = NULL; 358 359 while (blockBytesRead < 2*ns->logicalBlkSize[FS_DATA_FORMAT] 360 && totalRead + blockBytesRead < dataLen 361 && blockData[0] != 0 362 && !done) 363 { 364 initResult = InitNode(&node, blockData, &bytesRead, ns->joliet_level); 365 TRACE(("fs_walk - InitNode returned %s, filename %s, %d bytes read\n", strerror(initResult), node.fileIDString, bytesRead)); 366 367 if (initResult == B_NO_ERROR) { 368 if (strlen(node.fileIDString) == strlen(file) 369 && !strncmp(node.fileIDString, file, strlen(file))) 370 { 371 TRACE(("fs_walk - success, found vnode at block %Ld, pos %Ld\n", block, blockBytesRead)); 372 *_vnodeID = (block << 30) + (blockBytesRead & 0xFFFFFFFF); 373 TRACE(("fs_walk - New vnode id is %Ld\n", *_vnodeID)); 374 375 if (get_vnode(ns->id, *_vnodeID, (void **)&newNode) != 0) 376 result = EINVAL; 377 else { 378 newNode->parID = baseNode->id; 379 done = TRUE; 380 result = B_NO_ERROR; 381 } 382 } else { 383 if (node.fileIDString != NULL) { 384 free(node.fileIDString); 385 node.fileIDString = NULL; 386 } 387 if (node.attr.slName != NULL) { 388 free(node.attr.slName); 389 node.attr.slName = NULL; 390 } 391 } 392 } else { 393 result = initResult; 394 if (bytesRead == 0) 395 done = TRUE; 396 } 397 blockData += bytesRead; 398 blockBytesRead += bytesRead; 399 400 TRACE(("fs_walk - Adding %d bytes to blockBytes read (total %Ld/%Ld).\n", 401 bytesRead, blockBytesRead, baseNode->dataLen[FS_DATA_FORMAT])); 402 } 403 totalRead += ns->logicalBlkSize[FS_DATA_FORMAT]; 404 block++; 405 406 TRACE(("fs_walk - moving to next block %Ld, total read %Ld\n", block, totalRead)); 407 block_cache_put(ns->fBlockCache, cachedBlock); 408 409 } else 410 done = TRUE; 411 } 412 413 if (newNode) 414 *_type = newNode->attr.stat[FS_DATA_FORMAT].st_mode; 415 416 } 417 TRACE(("fs_walk - EXIT, result is %s, vnid is %Lu\n", strerror(result), *_vnodeID)); 418 return result; 419 } 420 421 422 static status_t 423 fs_read_vnode(void *_ns, vnode_id vnid, void **node, bool reenter) 424 { 425 uint32 block, pos; 426 nspace *ns = (nspace*)_ns; 427 status_t result = B_NO_ERROR; 428 vnode *newNode = (vnode*)calloc(sizeof(vnode), 1); 429 430 (void)reenter; 431 432 pos = (vnid & 0x3FFFFFFF); 433 block = (vnid >> 30); 434 435 TRACE(("fs_read_vnode - ENTER, block = %ld, pos = %ld, raw = %Lu node 0x%x\n", 436 block, pos, vnid, newNode)); 437 438 if (newNode != NULL) { 439 if (vnid == ISO_ROOTNODE_ID) { 440 TRACE(("fs_read_vnode - root node requested.\n")); 441 memcpy(newNode, &(ns->rootDirRec), sizeof(vnode)); 442 *node = (void*)newNode; 443 } else { 444 char *blockData = (char *)block_cache_get_etc(ns->fBlockCache, block, 0, ns->logicalBlkSize[FS_DATA_FORMAT]); 445 446 if (pos > ns->logicalBlkSize[FS_DATA_FORMAT]) { 447 if (blockData != NULL) 448 block_cache_put(ns->fBlockCache, block); 449 450 result = EINVAL; 451 } else if (blockData != NULL) { 452 result = InitNode(newNode, blockData + pos, NULL, ns->joliet_level); 453 block_cache_put(ns->fBlockCache, block); 454 newNode->id = vnid; 455 456 TRACE(("fs_read_vnode - init result is %s\n", strerror(result))); 457 *node = (void *)newNode; 458 TRACE(("fs_read_vnode - new file %s, size %ld\n", newNode->fileIDString, newNode->dataLen[FS_DATA_FORMAT])); 459 } 460 } 461 } else 462 result = ENOMEM; 463 464 if (result == B_OK && !(newNode->flags & ISO_ISDIR)) { 465 newNode->cache = file_cache_create(ns->id, vnid, newNode->dataLen[FS_DATA_FORMAT], ns->fdOfSession); 466 } 467 468 TRACE(("fs_read_vnode - EXIT, result is %s\n", strerror(result))); 469 return result; 470 } 471 472 473 static status_t 474 fs_release_vnode(void *ns, void *_node, bool reenter) 475 { 476 status_t result = B_NO_ERROR; 477 vnode *node = (vnode*)_node; 478 479 (void)ns; 480 (void)reenter; 481 482 TRACE(("fs_release_vnode - ENTER (0x%x)\n", node)); 483 484 if (node != NULL) { 485 if (node->id != ISO_ROOTNODE_ID) { 486 if (node->fileIDString != NULL) 487 free (node->fileIDString); 488 if (node->attr.slName != NULL) 489 free (node->attr.slName); 490 if (node->cache != NULL) 491 file_cache_delete(node->cache); 492 493 free(node); 494 } 495 } 496 497 TRACE(("fs_release_vnode - EXIT\n")); 498 return result; 499 } 500 501 502 static status_t 503 fs_get_file_map(fs_volume _fs, fs_vnode _node, off_t pos, size_t reqLen, 504 struct file_io_vec *vecs, size_t *_count) 505 { 506 nspace *ns = (nspace *)_fs; // global stuff 507 vnode *node = (vnode *)_node; // The read file vnode. 508 uint16 blockSize = ns->logicalBlkSize[FS_DATA_FORMAT]; 509 uint32 startBlock = node->startLBN[FS_DATA_FORMAT] + (pos / blockSize); 510 off_t blockPos = pos % blockSize; 511 off_t numBlocks = 0; 512 uint32 dataLen = node->dataLen[FS_DATA_FORMAT]; 513 size_t endLen = 0; 514 size_t startLen = 0; 515 size_t index = 0, max = *_count; 516 517 TRACE(("fs_get_file_map - ENTER (0x%x)\n", node)); 518 519 // Allow an open to work on a dir, but no reads 520 if (node->flags & ISO_ISDIR) 521 return EISDIR; 522 523 if (pos < 0) 524 pos = 0; 525 *_count = 0; 526 527 // If passed-in requested length is bigger than file size, change it to 528 // file size. 529 if (reqLen + pos > dataLen) 530 reqLen = dataLen - pos; 531 532 // Compute the length of the partial start-block read, if any. 533 if (reqLen + blockPos <= blockSize) 534 startLen = reqLen; 535 else if (blockPos > 0) 536 startLen = blockSize - blockPos; 537 538 if (blockPos == 0 && reqLen >= blockSize) { 539 TRACE(("Setting startLen to 0\n")); 540 startLen = 0; 541 } 542 543 // Compute the length of the partial end-block read, if any. 544 if (reqLen + blockPos > blockSize) 545 endLen = (reqLen + blockPos) % blockSize; 546 547 // Compute the number of middle blocks to read. 548 numBlocks = ((reqLen - endLen - startLen) / blockSize); 549 550 if (pos >= dataLen) { 551 // If pos >= file length, return 552 return B_OK; 553 } 554 555 // Read in the first, potentially partial, block. 556 if (startLen > 0) { 557 vecs[index].offset = startBlock * blockSize + blockPos; 558 vecs[index].length = startLen; 559 startBlock++; 560 index++; 561 if (index >= max) { 562 // we're out of file_io_vecs; let's bail out 563 *_count = index; 564 return B_BUFFER_OVERFLOW; 565 } 566 } 567 568 // Read in the middle blocks. 569 if (numBlocks > 0) { 570 for (int32 i=startBlock; i<startBlock+numBlocks; i++) { 571 vecs[index].offset = i * blockSize; 572 vecs[index].length = blockSize; 573 index++; 574 if (index >= max) { 575 // we're out of file_io_vecs; let's bail out 576 *_count = index; 577 return B_BUFFER_OVERFLOW; 578 } 579 } 580 } 581 582 // Read in the last partial block. 583 if (endLen > 0) { 584 off_t endBlock = startBlock + numBlocks; 585 vecs[index].offset = endBlock * blockSize; 586 vecs[index].length = endLen; 587 index++; 588 } 589 590 *_count = index; 591 592 TRACE(("fs_get_file_map - EXIT\n")); 593 return B_OK; 594 } 595 596 597 static status_t 598 fs_read_stat(void *_ns, void *_node, struct stat *st) 599 { 600 nspace *ns = (nspace*)_ns; 601 vnode *node = (vnode*)_node; 602 status_t result = B_NO_ERROR; 603 time_t time; 604 605 TRACE(("fs_read_stat - ENTER\n")); 606 607 st->st_dev = ns->id; 608 st->st_ino = node->id; 609 st->st_nlink = node->attr.stat[FS_DATA_FORMAT].st_nlink; 610 st->st_uid = node->attr.stat[FS_DATA_FORMAT].st_uid; 611 st->st_gid = node->attr.stat[FS_DATA_FORMAT].st_gid; 612 st->st_blksize = 65536; 613 st->st_mode = node->attr.stat[FS_DATA_FORMAT].st_mode; 614 615 // Same for file/dir in ISO9660 616 st->st_size = node->dataLen[FS_DATA_FORMAT]; 617 if (ConvertRecDate(&(node->recordDate), &time) == B_NO_ERROR) 618 st->st_ctime = st->st_mtime = st->st_atime = time; 619 620 TRACE(("fs_read_stat - EXIT, result is %s\n", strerror(result))); 621 622 return result; 623 } 624 625 626 static status_t 627 fs_open(void *_ns, void *_node, int omode, void **cookie) 628 { 629 status_t result = B_NO_ERROR; 630 631 (void)_ns; 632 (void)cookie; 633 634 // Do not allow any of the write-like open modes to get by 635 if ((omode == O_WRONLY) || (omode == O_RDWR)) 636 result = EROFS; 637 else if((omode & O_TRUNC) || (omode & O_CREAT)) 638 result = EROFS; 639 640 return result; 641 } 642 643 644 static status_t 645 fs_read(void *_ns, void *_node, void *cookie, off_t pos, void *buf, size_t *len) 646 { 647 #if 0 648 nspace *ns = (nspace *)_ns; // global stuff 649 vnode *node = (vnode *)_node; // The read file vnode. 650 uint16 blockSize = ns->logicalBlkSize[FS_DATA_FORMAT]; 651 uint32 startBlock = node->startLBN[FS_DATA_FORMAT] + (pos / blockSize); 652 off_t blockPos = pos % blockSize; 653 off_t numBlocks = 0; 654 uint32 dataLen = node->dataLen[FS_DATA_FORMAT]; 655 status_t result = B_NO_ERROR; 656 size_t endLen = 0; 657 size_t reqLen = *len; 658 size_t startLen = 0; 659 660 (void)cookie; 661 662 // Allow an open to work on a dir, but no reads 663 if (node->flags & ISO_ISDIR) 664 return EISDIR; 665 666 if (pos < 0) 667 pos = 0; 668 *len = 0; 669 670 // If passed-in requested length is bigger than file size, change it to 671 // file size. 672 if (reqLen + pos > dataLen) 673 reqLen = dataLen - pos; 674 675 // Compute the length of the partial start-block read, if any. 676 677 if (reqLen + blockPos <= blockSize) 678 startLen = reqLen; 679 else if (blockPos > 0) 680 startLen = blockSize - blockPos; 681 682 if (blockPos == 0 && reqLen >= blockSize) { 683 TRACE(("Setting startLen to 0, even block read\n")); 684 startLen = 0; 685 } 686 687 // Compute the length of the partial end-block read, if any. 688 if (reqLen + blockPos > blockSize) 689 endLen = (reqLen + blockPos) % blockSize; 690 691 // Compute the number of middle blocks to read. 692 numBlocks = ((reqLen - endLen - startLen) / blockSize); 693 694 //dprintf("fs_read - ENTER, pos is %Ld, len is %lu\n", pos, reqLen); 695 //dprintf("fs_read - filename is %s\n", node->fileIDString); 696 //dprintf("fs_read - total file length is %lu\n", dataLen); 697 //dprintf("fs_read - start block of file is %lu\n", node->startLBN[FS_DATA_FORMAT]); 698 //dprintf("fs_read - block pos is %Lu\n", blockPos); 699 //dprintf("fs_read - read block will be %lu\n", startBlock); 700 //dprintf("fs_read - startLen is %lu\n", startLen); 701 //dprintf("fs_read - endLen is %lu\n", endLen); 702 //dprintf("fs_read - num blocks to read is %Ld\n", numBlocks); 703 704 if (pos >= dataLen) { 705 // If pos >= file length, return length of 0. 706 *len = 0; 707 return B_OK; 708 } 709 710 // Read in the first, potentially partial, block. 711 if (startLen > 0) { 712 off_t cachedBlock = startBlock; 713 char *blockData = (char *)block_cache_get_etc(ns->fBlockCache, startBlock, 0, blockSize); 714 if (blockData != NULL) { 715 //dprintf("fs_read - copying first block, len is %d.\n", startLen); 716 memcpy(buf, blockData+blockPos, startLen); 717 *len += startLen; 718 block_cache_put(ns->fBlockCache, cachedBlock); 719 startBlock++; 720 } else 721 result = EIO; 722 } 723 724 // Read in the middle blocks. 725 if (numBlocks > 0 && result == B_NO_ERROR) { 726 TRACE(("fs_read - getting middle blocks\n")); 727 char *endBuf = ((char *)buf) + startLen; 728 for (int32 i=startBlock; i<startBlock+numBlocks; i++) { 729 char *blockData = (char *)block_cache_get_etc(ns->fBlockCache, i, 0, blockSize); 730 memcpy(endBuf, blockData, blockSize); 731 *len += blockSize; 732 endBuf += blockSize; 733 block_cache_put(ns->fBlockCache, i); 734 } 735 } 736 737 // Read in the last partial block. 738 if (result == B_NO_ERROR && endLen > 0) { 739 off_t endBlock = startBlock + numBlocks; 740 char *endBlockData = (char*)block_cache_get_etc(ns->fBlockCache, endBlock, 0, blockSize); 741 if (endBlockData != NULL) { 742 char *endBuf = ((char *)buf) + (reqLen - endLen); 743 744 memcpy(endBuf, endBlockData, endLen); 745 block_cache_put(ns->fBlockCache, endBlock); 746 *len += endLen; 747 } else 748 result = EIO; 749 } 750 751 TRACE(("fs_read - EXIT, result is %s\n", strerror(result))); 752 return result; 753 #else 754 vnode *node = (vnode *)_node; // The read file vnode. 755 uint32 dataLen = node->dataLen[FS_DATA_FORMAT]; 756 757 // set/check boundaries for pos/length 758 if (pos < 0) { 759 return B_BAD_VALUE; 760 } 761 if (pos >= dataLen) { 762 // If pos >= file length, return length of 0. 763 *len = 0; 764 return B_OK; 765 } 766 return file_cache_read(node->cache, pos, buf, len); 767 #endif 768 } 769 770 771 static status_t 772 fs_close(void *ns, void *node, void *cookie) 773 { 774 (void)ns; 775 (void)node; 776 (void)cookie; 777 778 //dprintf("fs_close - ENTER\n"); 779 //dprintf("fs_close - EXIT\n"); 780 return B_OK; 781 } 782 783 static status_t 784 fs_free_cookie(void *ns, void *node, void *cookie) 785 { 786 (void)ns; 787 (void)node; 788 (void)cookie; 789 790 // We don't allocate file cookies, so we do nothing here. 791 //dprintf("fs_free_cookie - ENTER\n"); 792 //if (cookie != NULL) free (cookie); 793 //dprintf("fs_free_cookie - EXIT\n"); 794 return B_OK; 795 } 796 797 // fs_access - checks permissions for access. 798 static status_t 799 fs_access(void *ns, void *node, int mode) 800 { 801 (void)ns; 802 (void)node; 803 (void)mode; 804 805 // ns - global, fs-specific struct for device 806 // node - node to check permissions for 807 // mode - requested permissions on node. 808 //dprintf("fs_access - ENTER\n"); 809 //dprintf("fs_access - EXIT\n"); 810 return B_OK; 811 } 812 813 static status_t 814 fs_read_link(void *_ns, void *_node, char *buffer, size_t *_bufferSize) 815 { 816 vnode *node = (vnode *)_node; 817 status_t result = EINVAL; 818 819 (void)_ns; 820 821 if (S_ISLNK(node->attr.stat[FS_DATA_FORMAT].st_mode)) { 822 size_t length = strlen(node->attr.slName); 823 if (length > *_bufferSize) 824 memcpy(buffer, node->attr.slName, *_bufferSize); 825 else { 826 memcpy(buffer, node->attr.slName, length); 827 *_bufferSize = length; 828 } 829 830 result = B_NO_ERROR; 831 } 832 833 return result; 834 } 835 836 837 static status_t 838 fs_open_dir(void *_ns, void *_node, void **cookie) 839 { 840 vnode *node = (vnode *)_node; 841 status_t result = B_NO_ERROR; 842 dircookie *dirCookie = (dircookie *)malloc(sizeof(dircookie)); 843 844 (void)_ns; 845 846 TRACE(("fs_open_dir - ENTER, node is 0x%x\n", _node)); 847 848 if (!(node->flags & ISO_ISDIR)) 849 result = EMFILE; 850 851 if (dirCookie != NULL) { 852 dirCookie->startBlock = node->startLBN[FS_DATA_FORMAT]; 853 dirCookie->block = node->startLBN[FS_DATA_FORMAT]; 854 dirCookie->totalSize = node->dataLen[FS_DATA_FORMAT]; 855 dirCookie->pos = 0; 856 dirCookie->id = node->id; 857 *cookie = (void *)dirCookie; 858 } else 859 result = ENOMEM; 860 861 TRACE(("fs_open_dir - EXIT\n")); 862 return result; 863 } 864 865 866 static status_t 867 fs_read_dir(void *_ns, void *_node, void *_cookie, struct dirent *buffer, 868 size_t bufferSize, uint32 *num) 869 { 870 status_t result = B_NO_ERROR; 871 nspace *ns = (nspace *)_ns; 872 dircookie *dirCookie = (dircookie *)_cookie; 873 874 (void)_node; 875 876 TRACE(("fs_read_dir - ENTER\n")); 877 878 result = ISOReadDirEnt(ns, dirCookie, buffer, bufferSize); 879 880 // If we succeeded, return 1, the number of dirents we read. 881 if (result == B_NO_ERROR) 882 *num = 1; 883 else 884 *num = 0; 885 886 // When you get to the end, don't return an error, just return 887 // a zero in *num. 888 889 if (result == ENOENT) 890 result = B_NO_ERROR; 891 892 TRACE(("fs_read_dir - EXIT, result is %s\n", strerror(result))); 893 return result; 894 } 895 896 897 static status_t 898 fs_rewind_dir(void *ns, void *node, void* _cookie) 899 { 900 status_t result = EINVAL; 901 dircookie *cookie = (dircookie*)_cookie; 902 903 (void)ns; 904 (void)node; 905 906 //dprintf("fs_rewind_dir - ENTER\n"); 907 if (cookie != NULL) { 908 cookie->block = cookie->startBlock; 909 cookie->pos = 0; 910 result = B_NO_ERROR; 911 } 912 //dprintf("fs_rewind_dir - EXIT, result is %s\n", strerror(result)); 913 return result; 914 } 915 916 917 static status_t 918 fs_close_dir(void *ns, void *node, void *cookie) 919 { 920 (void)ns; 921 (void)node; 922 (void)cookie; 923 924 // ns - global, fs-specific struct for device 925 // node - directory to close 926 // cookie - current cookie for directory. 927 //dprintf("fs_close_dir - ENTER\n"); 928 //dprintf("fs_close_dir - EXIT\n"); 929 return B_OK; 930 } 931 932 933 static status_t 934 fs_free_dir_cookie(void *ns, void *node, void *cookie) 935 { 936 (void)ns; 937 (void)node; 938 939 // ns - global, fs-specific struct for device 940 // node - directory related to cookie 941 // cookie - current cookie for directory, to free. 942 //dprintf("fs_free_dir_cookie - ENTER\n"); 943 if (cookie != NULL) 944 free(cookie); 945 946 //dprintf("fs_free_dir_cookie - EXIT\n"); 947 return B_OK; 948 } 949 950 // #pragma mark - 951 952 953 static status_t 954 iso_std_ops(int32 op, ...) 955 { 956 switch (op) { 957 case B_MODULE_INIT: 958 return B_OK; 959 case B_MODULE_UNINIT: 960 return B_OK; 961 default: 962 return B_ERROR; 963 } 964 } 965 966 967 static file_system_module_info sISO660FileSystem = { 968 { 969 "file_systems/iso9660" B_CURRENT_FS_API_VERSION, 970 0, 971 iso_std_ops, 972 }, 973 974 "ISO9660 File System", 975 976 // scanning 977 fs_identify_partition, 978 fs_scan_partition, 979 fs_free_identify_partition_cookie, 980 NULL, // free_partition_content_cookie() 981 982 &fs_mount, 983 &fs_unmount, 984 &fs_read_fs_stat, 985 NULL, 986 NULL, 987 988 /* vnode operations */ 989 &fs_walk, 990 &fs_get_vnode_name, 991 &fs_read_vnode, 992 &fs_release_vnode, 993 NULL, // &fs_remove_vnode() 994 995 /* VM file access */ 996 NULL, // &fs_can_page 997 NULL, // &fs_read_pages 998 NULL, // &fs_write_pages 999 1000 &fs_get_file_map, 1001 1002 NULL, // &fs_ioctl 1003 NULL, // &fs_set_flags 1004 NULL, // &fs_select 1005 NULL, // &fs_deselect 1006 NULL, // &fs_fsync 1007 1008 &fs_read_link, 1009 NULL, // &fs_create_symlink, 1010 1011 NULL, // &fs_link, 1012 NULL, // &fs_unlink 1013 NULL, // &fs_rename 1014 1015 &fs_access, 1016 &fs_read_stat, 1017 NULL, // &fs_write_stat 1018 1019 /* file operations */ 1020 NULL, // &fs_create 1021 &fs_open, 1022 &fs_close, 1023 &fs_free_cookie, 1024 &fs_read, 1025 NULL, // &fs_write 1026 1027 /* directory operations */ 1028 NULL, // &fs_create_dir 1029 NULL, // &fs_remove_dir 1030 &fs_open_dir, 1031 &fs_close_dir, 1032 &fs_free_dir_cookie, 1033 &fs_read_dir, 1034 &fs_rewind_dir, 1035 1036 /* attribute directory operations */ 1037 NULL, // &fs_open_attr_dir 1038 NULL, // &fs_close_attr_dir 1039 NULL, // &fs_free_attr_dir_cookie 1040 NULL, // &fs_read_attr_dir 1041 NULL, // &fs_rewind_attr_dir 1042 1043 /* attribute operations */ 1044 NULL, // &fs_create_attr 1045 NULL, // &fs_open_attr 1046 NULL, // &fs_close_attr 1047 NULL, // &fs_free_attr_cookie 1048 NULL, // &fs_read_attr 1049 NULL, // &fs_write_attr 1050 1051 NULL, // &fs_read_attr_stat 1052 NULL, // &fs_write_attr_stat 1053 NULL, // &fs_rename_attr 1054 NULL, // &fs_remove_attr 1055 1056 /* index directory & index operations */ 1057 NULL, // &fs_open_index_dir 1058 NULL, // &fs_close_index_dir 1059 NULL, // &fs_free_index_dir_cookie 1060 NULL, // &fs_read_index_dir 1061 NULL, // &fs_rewind_index_dir 1062 1063 NULL, // &fs_create_index 1064 NULL, // &fs_remove_index 1065 NULL, // &fs_stat_index 1066 1067 /* query operations */ 1068 NULL, // &fs_open_query 1069 NULL, // &fs_close_query 1070 NULL, // &fs_free_query_cookie 1071 NULL, // &fs_read_query 1072 NULL, // &fs_rewind_query 1073 }; 1074 1075 module_info *modules[] = { 1076 (module_info *)&sISO660FileSystem, 1077 NULL, 1078 }; 1079 1080