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 236 // File times. For now, treat the modification time as creation 237 // time as well, since true creation time is an optional extended 238 // attribute, and supporting EAs is going to be a PITA. ;-) 239 stat->st_atime = icb->AccessTime(); 240 stat->st_mtime = stat->st_ctime = stat->st_crtime = icb->ModificationTime(); 241 242 TRACE(("udf_read_stat: mode = 0x%lx, st_ino: %Ld\n", stat->st_mode, 243 stat->st_ino)); 244 245 return B_OK; 246 } 247 248 249 static status_t 250 udf_open(fs_volume* _volume, fs_vnode* _node, int openMode, void** _cookie) 251 { 252 TRACE(("udf_open: _volume = %p, _node = %p\n", _volume, _node)); 253 return B_OK; 254 } 255 256 257 static status_t 258 udf_close(fs_volume* _volume, fs_vnode* _node, void* _cookie) 259 { 260 TRACE(("udf_close: _volume = %p, _node = %p\n", _volume, _node)); 261 return B_OK; 262 } 263 264 265 static status_t 266 udf_free_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie) 267 { 268 TRACE(("udf_free_cookie: _volume = %p, _node = %p\n", _volume, _node)); 269 return B_OK; 270 } 271 272 273 static status_t 274 udf_access(fs_volume* _volume, fs_vnode* _node, int accessMode) 275 { 276 TRACE(("udf_access: _volume = %p, _node = %p\n", _volume, _node)); 277 return B_OK; 278 } 279 280 281 static status_t 282 udf_read(fs_volume *volume, fs_vnode *vnode, void *cookie, off_t pos, 283 void *buffer, size_t *length) 284 { 285 TRACE(("udf_read: ID = %ld, pos = %lld, length = %lu\n", 286 ((Volume *)volume->private_volume)->ID(), pos, *length)); 287 288 Icb *icb = (Icb *)vnode->private_node; 289 290 // if (!inode->HasUserAccessableStream()) { 291 // *_length = 0; 292 // RETURN_ERROR(B_BAD_VALUE); 293 // } 294 295 RETURN(icb->Read(pos, buffer, length)); 296 } 297 298 299 static status_t 300 udf_open_dir(fs_volume *volume, fs_vnode *vnode, void **cookie) 301 { 302 TRACE(("udf_open_dir: volume = %p, vnode = %p\n", volume, vnode)); 303 304 if (!volume || !vnode || !cookie) 305 RETURN(B_BAD_VALUE); 306 307 Icb *dir = (Icb *)vnode->private_node; 308 309 if (!dir->IsDirectory()) { 310 TRACE_ERROR(("udf_open_dir: given Icb is not a directory (type: %d)\n", 311 dir->Type())); 312 return B_BAD_VALUE; 313 } 314 315 DirectoryIterator *iterator = NULL; 316 status_t status = dir->GetDirectoryIterator(&iterator); 317 if (status != B_OK) { 318 TRACE_ERROR(("udf_open_dir: error getting directory iterator: 0x%lx, " 319 "`%s'\n", status, strerror(status))); 320 return status; 321 } 322 *cookie = (void *)iterator; 323 TRACE(("udf_open_dir: *cookie = %p\n", *cookie)); 324 325 return B_OK; 326 } 327 328 329 static status_t 330 udf_close_dir(fs_volume *_volume, fs_vnode *node, void *_cookie) 331 { 332 TRACE(("udf_close_dir: _volume = %p, node = %p\n", _volume, node)); 333 return B_OK; 334 } 335 336 337 static status_t 338 udf_free_dir_cookie(fs_volume *_volume, fs_vnode *node, void *_cookie) 339 { 340 TRACE(("udf_free_dir_cookie: _volume = %p, node = %p\n", _volume, node)); 341 return B_OK; 342 } 343 344 345 static status_t 346 udf_read_dir(fs_volume *_volume, fs_vnode *vnode, void *cookie, 347 struct dirent *dirent, size_t bufferSize, uint32 *_num) 348 { 349 TRACE(("udf_read_dir: _volume = %p, vnode = %p, bufferSize = %ld\n", 350 _volume, vnode, bufferSize)); 351 352 if (!_volume || !vnode || !cookie || !_num || bufferSize < sizeof(dirent)) 353 return B_BAD_VALUE; 354 355 Volume *volume = (Volume *)_volume->private_volume; 356 Icb *dir = (Icb *)vnode->private_node; 357 DirectoryIterator *iterator = (DirectoryIterator *)cookie; 358 359 if (dir != iterator->Parent()) { 360 TRACE_ERROR(("udf_read_dir: Icb does not match parent Icb of given " 361 "DirectoryIterator! (iterator->Parent = %p)\n", iterator->Parent())); 362 return B_BAD_VALUE; 363 } 364 365 uint32 nameLength = bufferSize - sizeof(struct dirent) + 1; 366 ino_t id; 367 status_t status = iterator->GetNextEntry(dirent->d_name, &nameLength, &id); 368 TRACE(("udf_read_dir: dirent->d_name = %s, length = %ld\n", dirent->d_name, nameLength)); 369 if (!status) { 370 *_num = 1; 371 dirent->d_dev = volume->ID(); 372 dirent->d_ino = id; 373 dirent->d_reclen = sizeof(struct dirent) + nameLength - 1; 374 } else { 375 *_num = 0; 376 // Clear the status for end of directory 377 if (status == B_ENTRY_NOT_FOUND) 378 status = B_OK; 379 } 380 381 RETURN(status); 382 } 383 384 385 status_t 386 udf_rewind_dir(fs_volume *volume, fs_vnode *vnode, void *cookie) 387 { 388 TRACE(("udf_rewind_dir: volume = %p, vnode = %p, cookie = %p\n", 389 volume, vnode, cookie)); 390 391 if (!volume || !vnode || !cookie) 392 RETURN(B_BAD_VALUE); 393 394 Icb *dir = (Icb *)vnode->private_node; 395 DirectoryIterator *iterator = (DirectoryIterator *)cookie; 396 397 if (dir != iterator->Parent()) { 398 PRINT(("udf_rewind_dir: icb does not match parent Icb of given " 399 "DirectoryIterator! (iterator->Parent = %p)\n", iterator->Parent())); 400 return B_BAD_VALUE; 401 } 402 403 iterator->Rewind(); 404 405 return B_OK; 406 } 407 408 409 // #pragma mark - 410 411 412 /*! \brief mount 413 414 \todo I'm using the B_GET_GEOMETRY ioctl() to find out where the end of the 415 partition is. This won't work for handling multi-session semantics correctly. 416 To support them correctly in R5 I need either: 417 - A way to get the proper info (best) 418 - To ignore trying to find anchor volume descriptor pointers at 419 locations N-256 and N. (acceptable, perhaps, but not really correct) 420 Either way we should address this problem properly for OBOS::R1. 421 \todo Looks like B_GET_GEOMETRY doesn't work on non-device files (i.e. 422 disk images), so I need to use stat or something else for those 423 instances. 424 */ 425 static status_t 426 udf_mount(fs_volume *_volume, const char *_device, uint32 flags, 427 const char *args, ino_t *_rootVnodeID) 428 { 429 TRACE(("udf_mount: device = %s\n", _device)); 430 status_t status = B_OK; 431 Volume *volume = NULL; 432 off_t deviceOffset = 0; 433 off_t numBlock = 0; 434 partition_info info; 435 device_geometry geometry; 436 437 // Here we need to figure out the length of the device, and if we're 438 // attempting to open a multisession volume, we need to figure out the 439 // offset into the raw disk at which the volume begins, then open 440 // the raw volume itself instead of the fake partition device the 441 // kernel gives us, since multisession UDF volumes are allowed to access 442 // the data in their own partition, as well as the data in any partitions 443 // that precede them physically on the disc. 444 int device = open(_device, O_RDONLY); 445 status = device < B_OK ? device : B_OK; 446 if (!status) { 447 // First try to treat the device like a special partition device. If that's 448 // what we have, then we can use the partition_info data to figure out the 449 // name of the raw device (which we'll open instead), the offset into the 450 // raw device at which the volume of interest will begin, and the total 451 // length from the beginning of the raw device that we're allowed to access. 452 // 453 // If that fails, then we try to treat the device as an actual raw device, 454 // and see if we can get the device size with B_GET_GEOMETRY syscall, since 455 // stat()ing a raw device appears to not work. 456 // 457 // Finally, if that also fails, we're probably stuck with trying to mount 458 // a regular file, so we just stat() it to get the device size. 459 // 460 // If that fails, you're just SOL. 461 462 if (ioctl(device, B_GET_PARTITION_INFO, &info) == 0) { 463 TRACE(("partition_info:\n")); 464 TRACE(("\toffset: %Ld\n", info.offset)); 465 TRACE(("\tsize: %Ld\n", info.size)); 466 TRACE(("\tlogical_block_size: %ld\n", info.logical_block_size)); 467 TRACE(("\tsession: %ld\n", info.session)); 468 TRACE(("\tpartition: %ld\n", info.partition)); 469 TRACE(("\tdevice: `%s'\n", info.device)); 470 _device = info.device; 471 deviceOffset = info.offset / info.logical_block_size; 472 numBlock = deviceOffset + info.size / info.logical_block_size; 473 } else if (ioctl(device, B_GET_GEOMETRY, &geometry) == 0) { 474 TRACE(("geometry_info:\n")); 475 TRACE(("\tsectors_per_track: %ld\n", geometry.sectors_per_track)); 476 TRACE(("\tcylinder_count: %ld\n", geometry.cylinder_count)); 477 TRACE(("\thead_count: %ld\n", geometry.head_count)); 478 deviceOffset = 0; 479 numBlock = (off_t)geometry.sectors_per_track 480 * geometry.cylinder_count * geometry.head_count; 481 } else { 482 struct stat stat; 483 status = fstat(device, &stat) < 0 ? B_ERROR : B_OK; 484 if (!status) { 485 TRACE(("stat_info:\n")); 486 TRACE(("\tst_size: %Ld\n", stat.st_size)); 487 deviceOffset = 0; 488 numBlock = stat.st_size / 2048; 489 } 490 } 491 // Close the device 492 close(device); 493 } 494 495 // Create and mount the volume 496 volume = new(std::nothrow) Volume(_volume); 497 status = volume->Mount(_device, deviceOffset, numBlock, 2048, flags); 498 if (status != B_OK) { 499 delete volume; 500 return status; 501 } 502 503 _volume->private_volume = volume; 504 _volume->ops = &gUDFVolumeOps; 505 *_rootVnodeID = volume->RootIcb()->Id(); 506 507 TRACE(("udf_mount: succefully mounted the partition\n")); 508 return B_OK; 509 } 510 511 512 // #pragma mark - 513 514 515 static status_t 516 udf_std_ops(int32 op, ...) 517 { 518 switch (op) { 519 case B_MODULE_INIT: 520 case B_MODULE_UNINIT: 521 return B_OK; 522 default: 523 return B_ERROR; 524 } 525 } 526 527 fs_volume_ops gUDFVolumeOps = { 528 &udf_unmount, 529 &udf_read_fs_stat, 530 NULL, // write_fs_stat 531 NULL, // sync 532 &udf_get_vnode, 533 534 /* index directory & index operations */ 535 NULL, // open_index_dir 536 NULL, // close_index_dir 537 NULL, // free_index_dir_cookie 538 NULL, // read_index_dir 539 NULL, // rewind_index_dir 540 NULL, // create_index 541 NULL, // remove_index 542 NULL, // read_index_stat 543 544 /* query operations */ 545 NULL, // open_query 546 NULL, // close_query 547 NULL, // free_query_cookie 548 NULL, // read_query 549 NULL, // rewind_query 550 551 /* support for FS layers */ 552 NULL, // create_sub_vnode 553 NULL, // delete_sub_vnode 554 }; 555 556 fs_vnode_ops gUDFVnodeOps = { 557 /* vnode operatoins */ 558 &udf_lookup, 559 NULL, // get_vnode_name 560 &udf_put_vnode, 561 &udf_remove_vnode, 562 563 /* VM file access */ 564 NULL, // can_page 565 NULL, // read_pages 566 NULL, // write_pages 567 568 /* asynchronous I/O */ 569 NULL, // io() 570 NULL, // cancel_io() 571 572 /* cache file access */ 573 NULL, // &udf_get_file_map, 574 575 /* common operations */ 576 NULL, // ioctl 577 NULL, // set_flags 578 NULL, // select 579 NULL, // deselect 580 NULL, // fsync 581 NULL, // read_symlink 582 NULL, // create_symlnk 583 NULL, // link 584 NULL, // unlink 585 NULL, // rename 586 &udf_access, 587 &udf_read_stat, 588 NULL, // write_stat 589 590 /* file operations */ 591 NULL, // create 592 &udf_open, 593 &udf_close, 594 &udf_free_cookie, 595 &udf_read, 596 NULL, // write 597 598 /* directory operations */ 599 NULL, // create_dir 600 NULL, // remove_dir 601 &udf_open_dir, 602 &udf_close_dir, 603 &udf_free_dir_cookie, 604 &udf_read_dir, 605 &udf_rewind_dir, 606 607 /* attribue directory operations */ 608 NULL, // open_attr_dir 609 NULL, // close_attr_dir 610 NULL, // free_attr_dir_cookie 611 NULL, // read_attr_dir 612 NULL, // rewind_attr_dir 613 614 /* attribute operations */ 615 NULL, // create_attr 616 NULL, // open_attr 617 NULL, // close_attr 618 NULL, // free_attr_cookie 619 NULL, // read_attr 620 NULL, // write_attr 621 NULL, // read_attr_stat 622 NULL, // write_attr_stat 623 NULL, // rename_attr 624 NULL, // remove_attr 625 626 /* support for node and FS layers */ 627 NULL, // create_special_node 628 NULL // get_super_vnode 629 630 }; 631 632 static file_system_module_info sUDFFileSystem = { 633 { 634 "file_systems/udf" B_CURRENT_FS_API_VERSION, 635 0, 636 udf_std_ops, 637 }, 638 639 "udf", // short_name 640 "UDF File System", // pretty_name 641 0, // DDM flags 642 643 &udf_identify_partition, 644 &udf_scan_partition, 645 NULL, // &udf_free_identify_patition_cookie, 646 NULL, // free_partition_content_cookie() 647 648 &udf_mount, 649 650 NULL, 651 }; 652 653 module_info *modules[] = { 654 (module_info *)&sUDFFileSystem, 655 NULL, 656 }; 657