1 /* 2 * Copyright 2008, Salvatore Benedetto, salvatore.benedetto@gmail.com. 3 * Copyright 2003, Tyler Dauwalder, tyler@dauwalder.net. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 /*! \file kernel_interface.cpp */ 8 9 #include <Drivers.h> 10 #include <ctype.h> 11 #include <errno.h> 12 #include <stdio.h> 13 #include <stdlib.h> 14 #include <string.h> 15 16 #include <KernelExport.h> 17 #include <util/kernel_cpp.h> 18 19 #include "Icb.h" 20 #include "Recognition.h" 21 #include "Utils.h" 22 #include "Volume.h" 23 24 25 #undef TRACE 26 #undef TRACE_ERROR 27 #define UDF_KERNEL_INTERFACE_DEBUG 28 #ifdef UDF_KERNEL_INTERFACE_DEBUG 29 # define TRACE(x) dprintf x 30 # define TRACE_ERROR(x) dprintf x 31 #else 32 # define TRACE(x) /* nothing */ 33 # define TRACE_ERROR(x) dprintf x 34 #endif 35 36 extern fs_volume_ops gUDFVolumeOps; 37 extern fs_vnode_ops gUDFVnodeOps; 38 39 40 // #pragma mark - fs_volume_ops fuctions 41 42 43 static float 44 udf_identify_partition(int fd, partition_data *partition, void **_cookie) 45 { 46 TRACE(("udf_identify_partition: fd = %d, id = %d, offset = %d, size = %d " 47 "content_size = %d, block_size = %d\n", fd, partition->id, 48 partition->offset, partition->size, partition->content_size, 49 partition->block_size)); 50 51 logical_volume_descriptor logicalVolumeDescriptor; 52 partition_descriptor partitionDescriptors[kMaxPartitionDescriptors]; 53 uint8 descriptorCount = kMaxPartitionDescriptors; 54 uint32 blockShift; 55 status_t error = udf_recognize(fd, partition->offset, partition->size, 56 partition->block_size, blockShift, logicalVolumeDescriptor, 57 partitionDescriptors, descriptorCount); 58 if (error != B_OK) 59 return -1; 60 61 return 0.8f; 62 } 63 64 65 static status_t 66 udf_scan_partition(int fd, partition_data *partition, void *_cookie) 67 { 68 TRACE(("udf_scan_partition: fd = %d\n", fd)); 69 70 #if 0 71 UdfString name(logicalVolumeDescriptor.logical_volume_identifier()); 72 partition->content_name = strdup(name.Utf8()); 73 #endif 74 return B_ERROR; 75 } 76 77 78 static status_t 79 udf_unmount(fs_volume *_volume) 80 { 81 TRACE(("udb_unmount: _volume = %p\n", _volume)); 82 Volume *volume = (Volume *)_volume->private_volume; 83 delete volume; 84 return B_OK; 85 } 86 87 88 static status_t 89 udf_read_fs_stat(fs_volume *_volume, struct fs_info *info) 90 { 91 TRACE(("udf_read_fs_stat: _volume = %p, info = %p\n", _volume, info)); 92 93 Volume *volume = (Volume *)_volume->private_volume; 94 95 // File system flags. 96 info->flags = B_FS_IS_PERSISTENT | B_FS_IS_READONLY; 97 98 info->io_size = 65536; 99 // whatever is appropriate here? Just use the same value as BFS (and iso9660) for now 100 101 info->block_size = volume->BlockSize(); 102 info->total_blocks = volume->Length(); 103 info->free_blocks = 0; 104 105 // Volume name 106 sprintf(info->volume_name, "%s", volume->Name()); 107 108 // File system name 109 strcpy(info->fsh_name, "udf"); 110 111 return B_OK; 112 } 113 114 115 static status_t 116 udf_get_vnode(fs_volume *_volume, ino_t id, fs_vnode *_node, int *_type, 117 uint32 *_flags, bool reenter) 118 { 119 TRACE(("udf_get_vnode: _volume = %p, _node = %p, reenter = %s\n", 120 _volume, _node, (reenter ? "true" : "false"))); 121 122 Volume *volume = (Volume *)_volume->private_volume; 123 124 // Convert the given vnode id to an address, and create 125 // and return a corresponding Icb object for it. 126 TRACE(("udf_get_vnode: id = %d, blockSize = %d\n", id, volume->BlockSize())); 127 Icb *icb = new(std::nothrow) Icb(volume, 128 to_long_address(id, volume->BlockSize())); 129 if (icb) { 130 if(icb->InitCheck() == B_OK) { 131 if (_node) 132 _node->private_node = icb; 133 _node->ops = &gUDFVnodeOps; 134 _flags = 0; 135 } else { 136 TRACE_ERROR(("udf_get_vnode: InitCheck failed\n")); 137 delete icb; 138 return B_ERROR; 139 } 140 } else 141 return B_NO_MEMORY; 142 143 return B_OK; 144 } 145 146 147 // #pragma mark - fs_vnode_ops functions 148 149 150 static status_t 151 udf_lookup(fs_volume *_volume, fs_vnode *_directory, const char *file, 152 ino_t *vnodeID) 153 { 154 TRACE(("udf_lookup: _directory = %p, filename = %s\n", _directory, file)); 155 156 Volume *volume = (Volume *)_volume->private_volume; 157 Icb *dir = (Icb *)_directory->private_node; 158 Icb *node = NULL; 159 160 status_t status = B_OK; 161 162 if (strcmp(file, ".") == 0) { 163 TRACE(("udf_lookup: file = ./\n")); 164 *vnodeID = dir->Id(); 165 status = get_vnode(volume->FSVolume(), *vnodeID, (void **)&node); 166 if (status != B_OK) 167 return B_ENTRY_NOT_FOUND; 168 } else { 169 status = dir->Find(file, vnodeID); 170 if (status != B_OK) 171 return status; 172 173 Icb *icb; 174 status = get_vnode(volume->FSVolume(), *vnodeID, (void **)&icb); 175 if (status != B_OK) 176 return B_ENTRY_NOT_FOUND; 177 } 178 TRACE(("udf_lookup: vnodeId = %Ld found!\n", *vnodeID)); 179 180 return B_OK; 181 } 182 183 184 static status_t 185 udf_put_vnode(fs_volume *volume, fs_vnode *node, bool reenter) 186 { 187 TRACE(("udf_put_vnode: volume = %p, node = %p\n", volume, node)); 188 // No debug-to-file in release_vnode; can cause a deadlock in 189 // rare circumstances. 190 #if !DEBUG_TO_FILE 191 DEBUG_INIT_ETC(NULL, ("node: %p", node)); 192 #endif 193 Icb *icb = (Icb *)node->private_node; 194 delete icb; 195 #if !DEBUG_TO_FILE 196 RETURN(B_OK); 197 #else 198 return B_OK; 199 #endif 200 } 201 202 203 static status_t 204 udf_remove_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter) 205 { 206 TRACE(("udf_remove_vnode: _volume = %p, _node = %p\n", _volume, _node)); 207 return B_ERROR; 208 } 209 210 211 static status_t 212 udf_read_stat(fs_volume *_volume, fs_vnode *node, struct stat *stat) 213 { 214 TRACE(("udf_read_stat: _volume = %p, node = %p\n", _volume, node)); 215 216 if (!_volume || !node || !stat) 217 return B_BAD_VALUE; 218 219 Volume *volume = (Volume *)_volume->private_volume; 220 Icb *icb = (Icb *)node->private_node; 221 222 stat->st_dev = volume->ID(); 223 stat->st_ino = icb->Id(); 224 stat->st_nlink = icb->FileLinkCount(); 225 stat->st_blksize = volume->BlockSize(); 226 227 TRACE(("udf_read_stat: st_dev = %d, st_ino = %d, st_blksize = %d\n", 228 stat->st_dev, stat->st_ino, stat->st_blksize)); 229 230 stat->st_uid = icb->Uid(); 231 stat->st_gid = icb->Gid(); 232 233 stat->st_mode = icb->Mode(); 234 stat->st_size = icb->Length(); 235 stat->st_blocks = (stat->st_size + 511) / 512; 236 237 // File times. For now, treat the modification time as creation 238 // time as well, since true creation time is an optional extended 239 // attribute, and supporting EAs is going to be a PITA. ;-) 240 stat->st_atime = icb->AccessTime(); 241 stat->st_mtime = stat->st_ctime = stat->st_crtime = icb->ModificationTime(); 242 243 TRACE(("udf_read_stat: mode = 0x%lx, st_ino: %Ld\n", stat->st_mode, 244 stat->st_ino)); 245 246 return B_OK; 247 } 248 249 250 static status_t 251 udf_open(fs_volume* _volume, fs_vnode* _node, int openMode, void** _cookie) 252 { 253 TRACE(("udf_open: _volume = %p, _node = %p\n", _volume, _node)); 254 return B_OK; 255 } 256 257 258 static status_t 259 udf_close(fs_volume* _volume, fs_vnode* _node, void* _cookie) 260 { 261 TRACE(("udf_close: _volume = %p, _node = %p\n", _volume, _node)); 262 return B_OK; 263 } 264 265 266 static status_t 267 udf_free_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie) 268 { 269 TRACE(("udf_free_cookie: _volume = %p, _node = %p\n", _volume, _node)); 270 return B_OK; 271 } 272 273 274 static status_t 275 udf_access(fs_volume* _volume, fs_vnode* _node, int accessMode) 276 { 277 TRACE(("udf_access: _volume = %p, _node = %p\n", _volume, _node)); 278 return B_OK; 279 } 280 281 282 static status_t 283 udf_read(fs_volume *volume, fs_vnode *vnode, void *cookie, off_t pos, 284 void *buffer, size_t *length) 285 { 286 TRACE(("udf_read: ID = %ld, pos = %lld, length = %lu\n", 287 ((Volume *)volume->private_volume)->ID(), pos, *length)); 288 289 Icb *icb = (Icb *)vnode->private_node; 290 291 // if (!inode->HasUserAccessableStream()) { 292 // *_length = 0; 293 // RETURN_ERROR(B_BAD_VALUE); 294 // } 295 296 RETURN(icb->Read(pos, buffer, length)); 297 } 298 299 300 static status_t 301 udf_open_dir(fs_volume *volume, fs_vnode *vnode, void **cookie) 302 { 303 TRACE(("udf_open_dir: volume = %p, vnode = %p\n", volume, vnode)); 304 305 if (!volume || !vnode || !cookie) 306 RETURN(B_BAD_VALUE); 307 308 Icb *dir = (Icb *)vnode->private_node; 309 310 if (!dir->IsDirectory()) { 311 TRACE_ERROR(("udf_open_dir: given Icb is not a directory (type: %d)\n", 312 dir->Type())); 313 return B_BAD_VALUE; 314 } 315 316 DirectoryIterator *iterator = NULL; 317 status_t status = dir->GetDirectoryIterator(&iterator); 318 if (status != B_OK) { 319 TRACE_ERROR(("udf_open_dir: error getting directory iterator: 0x%lx, " 320 "`%s'\n", status, strerror(status))); 321 return status; 322 } 323 *cookie = (void *)iterator; 324 TRACE(("udf_open_dir: *cookie = %p\n", *cookie)); 325 326 return B_OK; 327 } 328 329 330 static status_t 331 udf_close_dir(fs_volume *_volume, fs_vnode *node, void *_cookie) 332 { 333 TRACE(("udf_close_dir: _volume = %p, node = %p\n", _volume, node)); 334 return B_OK; 335 } 336 337 338 static status_t 339 udf_free_dir_cookie(fs_volume *_volume, fs_vnode *node, void *_cookie) 340 { 341 TRACE(("udf_free_dir_cookie: _volume = %p, node = %p\n", _volume, node)); 342 return B_OK; 343 } 344 345 346 static status_t 347 udf_read_dir(fs_volume *_volume, fs_vnode *vnode, void *cookie, 348 struct dirent *dirent, size_t bufferSize, uint32 *_num) 349 { 350 TRACE(("udf_read_dir: _volume = %p, vnode = %p, bufferSize = %ld\n", 351 _volume, vnode, bufferSize)); 352 353 if (!_volume || !vnode || !cookie || !_num || bufferSize < sizeof(dirent)) 354 return B_BAD_VALUE; 355 356 Volume *volume = (Volume *)_volume->private_volume; 357 Icb *dir = (Icb *)vnode->private_node; 358 DirectoryIterator *iterator = (DirectoryIterator *)cookie; 359 360 if (dir != iterator->Parent()) { 361 TRACE_ERROR(("udf_read_dir: Icb does not match parent Icb of given " 362 "DirectoryIterator! (iterator->Parent = %p)\n", iterator->Parent())); 363 return B_BAD_VALUE; 364 } 365 366 uint32 nameLength = bufferSize - sizeof(struct dirent) + 1; 367 ino_t id; 368 status_t status = iterator->GetNextEntry(dirent->d_name, &nameLength, &id); 369 TRACE(("udf_read_dir: dirent->d_name = %s, length = %ld\n", dirent->d_name, nameLength)); 370 if (!status) { 371 *_num = 1; 372 dirent->d_dev = volume->ID(); 373 dirent->d_ino = id; 374 dirent->d_reclen = sizeof(struct dirent) + nameLength - 1; 375 } else { 376 *_num = 0; 377 // Clear the status for end of directory 378 if (status == B_ENTRY_NOT_FOUND) 379 status = B_OK; 380 } 381 382 RETURN(status); 383 } 384 385 386 status_t 387 udf_rewind_dir(fs_volume *volume, fs_vnode *vnode, void *cookie) 388 { 389 TRACE(("udf_rewind_dir: volume = %p, vnode = %p, cookie = %p\n", 390 volume, vnode, cookie)); 391 392 if (!volume || !vnode || !cookie) 393 RETURN(B_BAD_VALUE); 394 395 Icb *dir = (Icb *)vnode->private_node; 396 DirectoryIterator *iterator = (DirectoryIterator *)cookie; 397 398 if (dir != iterator->Parent()) { 399 PRINT(("udf_rewind_dir: icb does not match parent Icb of given " 400 "DirectoryIterator! (iterator->Parent = %p)\n", iterator->Parent())); 401 return B_BAD_VALUE; 402 } 403 404 iterator->Rewind(); 405 406 return B_OK; 407 } 408 409 410 // #pragma mark - 411 412 413 /*! \brief mount 414 415 \todo I'm using the B_GET_GEOMETRY ioctl() to find out where the end of the 416 partition is. This won't work for handling multi-session semantics correctly. 417 To support them correctly in R5 I need either: 418 - A way to get the proper info (best) 419 - To ignore trying to find anchor volume descriptor pointers at 420 locations N-256 and N. (acceptable, perhaps, but not really correct) 421 Either way we should address this problem properly for OBOS::R1. 422 \todo Looks like B_GET_GEOMETRY doesn't work on non-device files (i.e. 423 disk images), so I need to use stat or something else for those 424 instances. 425 */ 426 static status_t 427 udf_mount(fs_volume *_volume, const char *_device, uint32 flags, 428 const char *args, ino_t *_rootVnodeID) 429 { 430 TRACE(("udf_mount: device = %s\n", _device)); 431 status_t status = B_OK; 432 Volume *volume = NULL; 433 off_t deviceOffset = 0; 434 off_t numBlock = 0; 435 partition_info info; 436 device_geometry geometry; 437 438 // Here we need to figure out the length of the device, and if we're 439 // attempting to open a multisession volume, we need to figure out the 440 // offset into the raw disk at which the volume begins, then open 441 // the raw volume itself instead of the fake partition device the 442 // kernel gives us, since multisession UDF volumes are allowed to access 443 // the data in their own partition, as well as the data in any partitions 444 // that precede them physically on the disc. 445 int device = open(_device, O_RDONLY); 446 status = device < B_OK ? device : B_OK; 447 if (!status) { 448 // First try to treat the device like a special partition device. If that's 449 // what we have, then we can use the partition_info data to figure out the 450 // name of the raw device (which we'll open instead), the offset into the 451 // raw device at which the volume of interest will begin, and the total 452 // length from the beginning of the raw device that we're allowed to access. 453 // 454 // If that fails, then we try to treat the device as an actual raw device, 455 // and see if we can get the device size with B_GET_GEOMETRY syscall, since 456 // stat()ing a raw device appears to not work. 457 // 458 // Finally, if that also fails, we're probably stuck with trying to mount 459 // a regular file, so we just stat() it to get the device size. 460 // 461 // If that fails, you're just SOL. 462 463 if (ioctl(device, B_GET_PARTITION_INFO, &info) == 0) { 464 TRACE(("partition_info:\n")); 465 TRACE(("\toffset: %Ld\n", info.offset)); 466 TRACE(("\tsize: %Ld\n", info.size)); 467 TRACE(("\tlogical_block_size: %ld\n", info.logical_block_size)); 468 TRACE(("\tsession: %ld\n", info.session)); 469 TRACE(("\tpartition: %ld\n", info.partition)); 470 TRACE(("\tdevice: `%s'\n", info.device)); 471 _device = info.device; 472 deviceOffset = info.offset / info.logical_block_size; 473 numBlock = deviceOffset + info.size / info.logical_block_size; 474 } else if (ioctl(device, B_GET_GEOMETRY, &geometry) == 0) { 475 TRACE(("geometry_info:\n")); 476 TRACE(("\tsectors_per_track: %ld\n", geometry.sectors_per_track)); 477 TRACE(("\tcylinder_count: %ld\n", geometry.cylinder_count)); 478 TRACE(("\thead_count: %ld\n", geometry.head_count)); 479 deviceOffset = 0; 480 numBlock = (off_t)geometry.sectors_per_track 481 * geometry.cylinder_count * geometry.head_count; 482 } else { 483 struct stat stat; 484 status = fstat(device, &stat) < 0 ? B_ERROR : B_OK; 485 if (!status) { 486 TRACE(("stat_info:\n")); 487 TRACE(("\tst_size: %Ld\n", stat.st_size)); 488 deviceOffset = 0; 489 numBlock = stat.st_size / 2048; 490 } 491 } 492 // Close the device 493 close(device); 494 } 495 496 // Create and mount the volume 497 volume = new(std::nothrow) Volume(_volume); 498 status = volume->Mount(_device, deviceOffset, numBlock, 2048, flags); 499 if (status != B_OK) { 500 delete volume; 501 return status; 502 } 503 504 _volume->private_volume = volume; 505 _volume->ops = &gUDFVolumeOps; 506 *_rootVnodeID = volume->RootIcb()->Id(); 507 508 TRACE(("udf_mount: succefully mounted the partition\n")); 509 return B_OK; 510 } 511 512 513 // #pragma mark - 514 515 516 static status_t 517 udf_std_ops(int32 op, ...) 518 { 519 switch (op) { 520 case B_MODULE_INIT: 521 case B_MODULE_UNINIT: 522 return B_OK; 523 default: 524 return B_ERROR; 525 } 526 } 527 528 fs_volume_ops gUDFVolumeOps = { 529 &udf_unmount, 530 &udf_read_fs_stat, 531 NULL, // write_fs_stat 532 NULL, // sync 533 &udf_get_vnode, 534 535 /* index directory & index operations */ 536 NULL, // open_index_dir 537 NULL, // close_index_dir 538 NULL, // free_index_dir_cookie 539 NULL, // read_index_dir 540 NULL, // rewind_index_dir 541 NULL, // create_index 542 NULL, // remove_index 543 NULL, // read_index_stat 544 545 /* query operations */ 546 NULL, // open_query 547 NULL, // close_query 548 NULL, // free_query_cookie 549 NULL, // read_query 550 NULL, // rewind_query 551 552 /* support for FS layers */ 553 NULL, // create_sub_vnode 554 NULL, // delete_sub_vnode 555 }; 556 557 fs_vnode_ops gUDFVnodeOps = { 558 /* vnode operatoins */ 559 &udf_lookup, 560 NULL, // get_vnode_name 561 &udf_put_vnode, 562 &udf_remove_vnode, 563 564 /* VM file access */ 565 NULL, // can_page 566 NULL, // read_pages 567 NULL, // write_pages 568 569 /* asynchronous I/O */ 570 NULL, // io() 571 NULL, // cancel_io() 572 573 /* cache file access */ 574 NULL, // &udf_get_file_map, 575 576 /* common operations */ 577 NULL, // ioctl 578 NULL, // set_flags 579 NULL, // select 580 NULL, // deselect 581 NULL, // fsync 582 NULL, // read_symlink 583 NULL, // create_symlnk 584 NULL, // link 585 NULL, // unlink 586 NULL, // rename 587 &udf_access, 588 &udf_read_stat, 589 NULL, // write_stat 590 591 /* file operations */ 592 NULL, // create 593 &udf_open, 594 &udf_close, 595 &udf_free_cookie, 596 &udf_read, 597 NULL, // write 598 599 /* directory operations */ 600 NULL, // create_dir 601 NULL, // remove_dir 602 &udf_open_dir, 603 &udf_close_dir, 604 &udf_free_dir_cookie, 605 &udf_read_dir, 606 &udf_rewind_dir, 607 608 /* attribue directory operations */ 609 NULL, // open_attr_dir 610 NULL, // close_attr_dir 611 NULL, // free_attr_dir_cookie 612 NULL, // read_attr_dir 613 NULL, // rewind_attr_dir 614 615 /* attribute operations */ 616 NULL, // create_attr 617 NULL, // open_attr 618 NULL, // close_attr 619 NULL, // free_attr_cookie 620 NULL, // read_attr 621 NULL, // write_attr 622 NULL, // read_attr_stat 623 NULL, // write_attr_stat 624 NULL, // rename_attr 625 NULL, // remove_attr 626 627 /* support for node and FS layers */ 628 NULL, // create_special_node 629 NULL // get_super_vnode 630 631 }; 632 633 static file_system_module_info sUDFFileSystem = { 634 { 635 "file_systems/udf" B_CURRENT_FS_API_VERSION, 636 0, 637 udf_std_ops, 638 }, 639 640 "udf", // short_name 641 "UDF File System", // pretty_name 642 0, // DDM flags 643 644 &udf_identify_partition, 645 &udf_scan_partition, 646 NULL, // &udf_free_identify_patition_cookie, 647 NULL, // free_partition_content_cookie() 648 649 &udf_mount, 650 651 NULL, 652 }; 653 654 module_info *modules[] = { 655 (module_info *)&sUDFFileSystem, 656 NULL, 657 }; 658