1 /* 2 * Copyright 2008-2010, Axel Dörfler, axeld@pinc-software.de. 3 * Copyright 2011, Jérôme Duval, korli@users.berlios.de. 4 * Copyright 2014 Haiku, Inc. All rights reserved. 5 * 6 * Distributed under the terms of the MIT License. 7 * 8 * Authors: 9 * Axel Dörfler, axeld@pinc-software.de 10 * Jérôme Duval, korli@users.berlios.de 11 * John Scipione, jscipione@gmail.com 12 */ 13 14 15 #include <dirent.h> 16 #include <unistd.h> 17 #include <util/kernel_cpp.h> 18 #include <string.h> 19 20 #include <new> 21 22 #include <AutoDeleter.h> 23 #include <fs_cache.h> 24 #include <fs_info.h> 25 #include <io_requests.h> 26 #include <NodeMonitor.h> 27 #include <StorageDefs.h> 28 #include <util/AutoLock.h> 29 #include <file_systems/fs_ops_support.h> 30 31 #include "DirectoryIterator.h" 32 #include "exfat.h" 33 #include "Inode.h" 34 #include "Utility.h" 35 36 37 //#define TRACE_EXFAT 38 #ifdef TRACE_EXFAT 39 # define TRACE(x...) dprintf("\33[34mexfat:\33[0m " x) 40 #else 41 # define TRACE(x...) ; 42 #endif 43 #define ERROR(x...) dprintf("\33[34mexfat:\33[0m " x) 44 45 46 #define EXFAT_IO_SIZE 65536 47 48 49 struct identify_cookie { 50 exfat_super_block super_block; 51 char name[B_FILE_NAME_LENGTH]; 52 }; 53 54 55 //! exfat_io() callback hook 56 static status_t 57 iterative_io_get_vecs_hook(void* cookie, io_request* request, off_t offset, 58 size_t size, struct file_io_vec* vecs, size_t* _count) 59 { 60 Inode* inode = (Inode*)cookie; 61 62 return file_map_translate(inode->Map(), offset, size, vecs, _count, 63 inode->GetVolume()->BlockSize()); 64 } 65 66 67 //! exfat_io() callback hook 68 static status_t 69 iterative_io_finished_hook(void* cookie, io_request* request, status_t status, 70 bool partialTransfer, size_t bytesTransferred) 71 { 72 Inode* inode = (Inode*)cookie; 73 rw_lock_read_unlock(inode->Lock()); 74 return B_OK; 75 } 76 77 78 // #pragma mark - Scanning 79 80 81 static float 82 exfat_identify_partition(int fd, partition_data* partition, void** _cookie) 83 { 84 struct exfat_super_block superBlock; 85 status_t status = Volume::Identify(fd, &superBlock); 86 if (status != B_OK) 87 return -1; 88 89 identify_cookie* cookie = new (std::nothrow)identify_cookie; 90 if (cookie == NULL) 91 return -1; 92 93 memcpy(&cookie->super_block, &superBlock, sizeof(exfat_super_block)); 94 memset(cookie->name, 0, sizeof(cookie->name)); 95 // zero out volume name 96 97 uint32 rootDirCluster = superBlock.RootDirCluster(); 98 uint32 blockSize = 1 << superBlock.BlockShift(); 99 uint32 clusterSize = blockSize << superBlock.BlocksPerClusterShift(); 100 uint64 rootDirectoryOffset = EXFAT_SUPER_BLOCK_OFFSET 101 + (uint64)superBlock.FirstDataBlock() * blockSize 102 + (rootDirCluster - 2) * clusterSize; 103 struct exfat_entry entry; 104 size_t entrySize = sizeof(struct exfat_entry); 105 for (uint32 i = 0; read_pos(fd, rootDirectoryOffset + i * entrySize, 106 &entry, entrySize) == (ssize_t)entrySize; i++) { 107 if (entry.type == EXFAT_ENTRY_TYPE_NOT_IN_USE 108 || entry.type == EXFAT_ENTRY_TYPE_LABEL) { 109 if (get_volume_name(&entry, cookie->name, sizeof(cookie->name)) 110 != B_OK) { 111 delete cookie; 112 return -1; 113 } 114 break; 115 } 116 } 117 118 if (cookie->name[0] == '\0') { 119 off_t fileSystemSize = (off_t)superBlock.NumBlocks() 120 << superBlock.BlockShift(); 121 get_default_volume_name(fileSystemSize, cookie->name, 122 sizeof(cookie->name)); 123 } 124 125 *_cookie = cookie; 126 return 0.8f; 127 } 128 129 130 static status_t 131 exfat_scan_partition(int fd, partition_data* partition, void* _cookie) 132 { 133 identify_cookie* cookie = (identify_cookie*)_cookie; 134 135 partition->status = B_PARTITION_VALID; 136 partition->flags |= B_PARTITION_FILE_SYSTEM; 137 partition->content_size = cookie->super_block.NumBlocks() 138 << cookie->super_block.BlockShift(); 139 partition->block_size = 1 << cookie->super_block.BlockShift(); 140 partition->content_name = strdup(cookie->name); 141 142 return partition->content_name != NULL ? B_OK : B_NO_MEMORY; 143 } 144 145 146 static void 147 exfat_free_identify_partition_cookie(partition_data* partition, void* _cookie) 148 { 149 delete (identify_cookie*)_cookie; 150 } 151 152 153 // #pragma mark - 154 155 156 static status_t 157 exfat_mount(fs_volume* _volume, const char* device, uint32 flags, 158 const char* args, ino_t* _rootID) 159 { 160 Volume* volume = new(std::nothrow) Volume(_volume); 161 if (volume == NULL) 162 return B_NO_MEMORY; 163 164 // TODO: this is a bit hacky: we can't use publish_vnode() to publish 165 // the root node, or else its file cache cannot be created (we could 166 // create it later, though). Therefore we're using get_vnode() in Mount(), 167 // but that requires us to export our volume data before calling it. 168 _volume->private_volume = volume; 169 _volume->ops = &gExfatVolumeOps; 170 171 status_t status = volume->Mount(device, flags); 172 if (status != B_OK) { 173 ERROR("Failed mounting the volume. Error: %s\n", strerror(status)); 174 delete volume; 175 return status; 176 } 177 178 *_rootID = volume->RootNode()->ID(); 179 return B_OK; 180 } 181 182 183 static status_t 184 exfat_unmount(fs_volume *_volume) 185 { 186 Volume* volume = (Volume *)_volume->private_volume; 187 188 status_t status = volume->Unmount(); 189 delete volume; 190 191 return status; 192 } 193 194 195 static status_t 196 exfat_read_fs_info(fs_volume* _volume, struct fs_info* info) 197 { 198 Volume* volume = (Volume*)_volume->private_volume; 199 200 // File system flags 201 info->flags = B_FS_IS_PERSISTENT 202 | (volume->IsReadOnly() ? B_FS_IS_READONLY : 0); 203 info->io_size = EXFAT_IO_SIZE; 204 info->block_size = volume->BlockSize(); 205 info->total_blocks = volume->SuperBlock().NumBlocks(); 206 info->free_blocks = 0; //volume->NumFreeBlocks(); 207 208 // Volume name 209 strlcpy(info->volume_name, volume->Name(), sizeof(info->volume_name)); 210 211 // File system name 212 strlcpy(info->fsh_name, "exfat", sizeof(info->fsh_name)); 213 214 return B_OK; 215 } 216 217 218 // #pragma mark - 219 220 221 static status_t 222 exfat_get_vnode(fs_volume* _volume, ino_t id, fs_vnode* _node, int* _type, 223 uint32* _flags, bool reenter) 224 { 225 TRACE("get_vnode %" B_PRIdINO "\n", id); 226 Volume* volume = (Volume*)_volume->private_volume; 227 228 Inode* inode = new(std::nothrow) Inode(volume, id); 229 if (inode == NULL) 230 return B_NO_MEMORY; 231 232 status_t status = inode->InitCheck(); 233 if (status != B_OK) 234 delete inode; 235 236 if (status == B_OK) { 237 _node->private_node = inode; 238 _node->ops = &gExfatVnodeOps; 239 *_type = inode->Mode(); 240 *_flags = 0; 241 } else 242 ERROR("get_vnode: InitCheck() failed. Error: %s\n", strerror(status)); 243 244 return status; 245 } 246 247 248 static status_t 249 exfat_put_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter) 250 { 251 delete (Inode*)_node->private_node; 252 return B_OK; 253 } 254 255 256 static bool 257 exfat_can_page(fs_volume* _volume, fs_vnode* _node, void* _cookie) 258 { 259 return true; 260 } 261 262 263 static status_t 264 exfat_read_pages(fs_volume* _volume, fs_vnode* _node, void* _cookie, 265 off_t pos, const iovec* vecs, size_t count, size_t* _numBytes) 266 { 267 Volume* volume = (Volume*)_volume->private_volume; 268 Inode* inode = (Inode*)_node->private_node; 269 270 if (inode->FileCache() == NULL) 271 return B_BAD_VALUE; 272 273 rw_lock_read_lock(inode->Lock()); 274 275 uint32 vecIndex = 0; 276 size_t vecOffset = 0; 277 size_t bytesLeft = *_numBytes; 278 status_t status; 279 280 while (true) { 281 file_io_vec fileVecs[8]; 282 size_t fileVecCount = 8; 283 284 status = file_map_translate(inode->Map(), pos, bytesLeft, fileVecs, 285 &fileVecCount, 0); 286 if (status != B_OK && status != B_BUFFER_OVERFLOW) 287 break; 288 289 bool bufferOverflow = status == B_BUFFER_OVERFLOW; 290 291 size_t bytes = bytesLeft; 292 status = read_file_io_vec_pages(volume->Device(), fileVecs, 293 fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes); 294 if (status != B_OK || !bufferOverflow) 295 break; 296 297 pos += bytes; 298 bytesLeft -= bytes; 299 } 300 301 rw_lock_read_unlock(inode->Lock()); 302 303 return status; 304 } 305 306 307 static status_t 308 exfat_io(fs_volume* _volume, fs_vnode* _node, void* _cookie, 309 io_request* request) 310 { 311 Volume* volume = (Volume*)_volume->private_volume; 312 Inode* inode = (Inode*)_node->private_node; 313 314 #ifndef EXFAT_SHELL 315 if (io_request_is_write(request) && volume->IsReadOnly()) { 316 notify_io_request(request, B_READ_ONLY_DEVICE); 317 return B_READ_ONLY_DEVICE; 318 } 319 #endif 320 321 if (inode->FileCache() == NULL) { 322 #ifndef EXFAT_SHELL 323 notify_io_request(request, B_BAD_VALUE); 324 #endif 325 return B_BAD_VALUE; 326 } 327 328 // We lock the node here and will unlock it in the "finished" hook. 329 rw_lock_read_lock(inode->Lock()); 330 331 return do_iterative_fd_io(volume->Device(), request, 332 iterative_io_get_vecs_hook, iterative_io_finished_hook, inode); 333 } 334 335 336 static status_t 337 exfat_get_file_map(fs_volume* _volume, fs_vnode* _node, off_t offset, 338 size_t size, struct file_io_vec* vecs, size_t* _count) 339 { 340 TRACE("exfat_get_file_map()\n"); 341 Inode* inode = (Inode*)_node->private_node; 342 size_t index = 0, max = *_count; 343 344 while (true) { 345 off_t blockOffset; 346 off_t blockLength; 347 status_t status = inode->FindBlock(offset, blockOffset, &blockLength); 348 if (status != B_OK) 349 return status; 350 351 if (index > 0 && (vecs[index - 1].offset 352 == blockOffset - vecs[index - 1].length)) { 353 vecs[index - 1].length += blockLength; 354 } else { 355 if (index >= max) { 356 // we're out of file_io_vecs; let's bail out 357 *_count = index; 358 return B_BUFFER_OVERFLOW; 359 } 360 361 vecs[index].offset = blockOffset; 362 vecs[index].length = blockLength; 363 index++; 364 } 365 366 offset += blockLength; 367 size -= blockLength; 368 369 if ((off_t)size <= vecs[index - 1].length || offset >= inode->Size()) { 370 // We're done! 371 *_count = index; 372 TRACE("exfat_get_file_map for inode %" B_PRIdINO"\n", inode->ID()); 373 return B_OK; 374 } 375 } 376 377 // can never get here 378 return B_ERROR; 379 } 380 381 382 // #pragma mark - 383 384 385 static status_t 386 exfat_lookup(fs_volume* _volume, fs_vnode* _directory, const char* name, 387 ino_t* _vnodeID) 388 { 389 TRACE("exfat_lookup: name address: %p (%s)\n", name, name); 390 Volume* volume = (Volume*)_volume->private_volume; 391 Inode* directory = (Inode*)_directory->private_node; 392 393 // check access permissions 394 status_t status = directory->CheckPermissions(X_OK); 395 if (status < B_OK) 396 return status; 397 398 status = DirectoryIterator(directory).Lookup(name, strlen(name), _vnodeID); 399 if (status != B_OK) { 400 ERROR("exfat_lookup: name %s (%s)\n", name, strerror(status)); 401 if (status == B_ENTRY_NOT_FOUND) 402 entry_cache_add_missing(volume->ID(), directory->ID(), name); 403 return status; 404 } 405 406 TRACE("exfat_lookup: ID %" B_PRIdINO "\n", *_vnodeID); 407 entry_cache_add(volume->ID(), directory->ID(), name, *_vnodeID); 408 409 return get_vnode(volume->FSVolume(), *_vnodeID, NULL); 410 } 411 412 413 static status_t 414 exfat_ioctl(fs_volume* _volume, fs_vnode* _node, void* _cookie, uint32 cmd, 415 void* buffer, size_t bufferLength) 416 { 417 TRACE("ioctl: %" B_PRIu32 "\n", cmd); 418 419 /*Volume* volume = (Volume*)_volume->private_volume;*/ 420 return B_DEV_INVALID_IOCTL; 421 } 422 423 424 static status_t 425 exfat_read_stat(fs_volume* _volume, fs_vnode* _node, struct stat* stat) 426 { 427 Inode* inode = (Inode*)_node->private_node; 428 429 stat->st_dev = inode->GetVolume()->ID(); 430 stat->st_ino = inode->ID(); 431 stat->st_nlink = 1; 432 stat->st_blksize = EXFAT_IO_SIZE; 433 434 stat->st_uid = inode->UserID(); 435 stat->st_gid = inode->GroupID(); 436 stat->st_mode = inode->Mode(); 437 stat->st_type = 0; 438 439 inode->GetAccessTime(stat->st_atim); 440 inode->GetModificationTime(stat->st_mtim); 441 inode->GetChangeTime(stat->st_ctim); 442 inode->GetCreationTime(stat->st_crtim); 443 444 stat->st_size = inode->Size(); 445 stat->st_blocks = (inode->Size() + 511) / 512; 446 447 return B_OK; 448 } 449 450 451 static status_t 452 exfat_open(fs_volume* /*_volume*/, fs_vnode* _node, int openMode, 453 void** _cookie) 454 { 455 Inode* inode = (Inode*)_node->private_node; 456 457 // opening a directory read-only is allowed, although you can't read 458 // any data from it. 459 if (inode->IsDirectory() && (openMode & O_RWMASK) != 0) 460 return B_IS_A_DIRECTORY; 461 462 status_t status = inode->CheckPermissions(open_mode_to_access(openMode) 463 | (openMode & O_TRUNC ? W_OK : 0)); 464 if (status != B_OK) 465 return status; 466 467 // Prepare the cookie 468 file_cookie* cookie = new(std::nothrow) file_cookie; 469 if (cookie == NULL) 470 return B_NO_MEMORY; 471 ObjectDeleter<file_cookie> cookieDeleter(cookie); 472 473 cookie->open_mode = openMode & EXFAT_OPEN_MODE_USER_MASK; 474 cookie->last_size = inode->Size(); 475 cookie->last_notification = system_time(); 476 477 if ((openMode & O_NOCACHE) != 0 && inode->FileCache() != NULL) { 478 // Disable the file cache, if requested? 479 status = file_cache_disable(inode->FileCache()); 480 if (status != B_OK) 481 return status; 482 } 483 484 cookieDeleter.Detach(); 485 *_cookie = cookie; 486 487 return B_OK; 488 } 489 490 491 static status_t 492 exfat_read(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos, 493 void* buffer, size_t* _length) 494 { 495 Inode* inode = (Inode*)_node->private_node; 496 497 if (!inode->IsFile()) { 498 *_length = 0; 499 return inode->IsDirectory() ? B_IS_A_DIRECTORY : B_BAD_VALUE; 500 } 501 502 return inode->ReadAt(pos, (uint8*)buffer, _length); 503 } 504 505 506 static status_t 507 exfat_close(fs_volume *_volume, fs_vnode *_node, void *_cookie) 508 { 509 return B_OK; 510 } 511 512 513 static status_t 514 exfat_free_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie) 515 { 516 file_cookie* cookie = (file_cookie*)_cookie; 517 Volume* volume = (Volume*)_volume->private_volume; 518 Inode* inode = (Inode*)_node->private_node; 519 520 if (inode->Size() != cookie->last_size) 521 notify_stat_changed(volume->ID(), -1, inode->ID(), B_STAT_SIZE); 522 523 delete cookie; 524 return B_OK; 525 } 526 527 528 static status_t 529 exfat_access(fs_volume* _volume, fs_vnode* _node, int accessMode) 530 { 531 Inode* inode = (Inode*)_node->private_node; 532 return inode->CheckPermissions(accessMode); 533 } 534 535 536 static status_t 537 exfat_read_link(fs_volume *_volume, fs_vnode *_node, char *buffer, 538 size_t *_bufferSize) 539 { 540 Inode* inode = (Inode*)_node->private_node; 541 542 if (!inode->IsSymLink()) 543 return B_BAD_VALUE; 544 545 status_t result = inode->ReadAt(0, reinterpret_cast<uint8*>(buffer), 546 _bufferSize); 547 if (result != B_OK) 548 return result; 549 550 *_bufferSize = inode->Size(); 551 552 return B_OK; 553 } 554 555 556 // #pragma mark - Directory functions 557 558 559 static status_t 560 exfat_open_dir(fs_volume* /*_volume*/, fs_vnode* _node, void** _cookie) 561 { 562 Inode* inode = (Inode*)_node->private_node; 563 status_t status = inode->CheckPermissions(R_OK); 564 if (status < B_OK) 565 return status; 566 567 if (!inode->IsDirectory()) 568 return B_NOT_A_DIRECTORY; 569 570 DirectoryIterator* iterator = new(std::nothrow) DirectoryIterator(inode); 571 if (iterator == NULL || iterator->InitCheck() != B_OK) { 572 delete iterator; 573 return B_NO_MEMORY; 574 } 575 576 *_cookie = iterator; 577 return B_OK; 578 } 579 580 581 static status_t 582 exfat_read_dir(fs_volume *_volume, fs_vnode *_node, void *_cookie, 583 struct dirent *dirent, size_t bufferSize, uint32 *_num) 584 { 585 TRACE("exfat_read_dir\n"); 586 DirectoryIterator* iterator = (DirectoryIterator*)_cookie; 587 Volume* volume = (Volume*)_volume->private_volume; 588 589 uint32 maxCount = *_num; 590 uint32 count = 0; 591 592 while (count < maxCount && bufferSize > sizeof(struct dirent)) { 593 ino_t id; 594 size_t length = bufferSize - offsetof(struct dirent, d_name); 595 596 status_t status = iterator->GetNext(dirent->d_name, &length, &id); 597 if (status == B_ENTRY_NOT_FOUND) 598 break; 599 600 if (status == B_BUFFER_OVERFLOW) { 601 // the remaining name buffer length was too small 602 if (count == 0) 603 return B_BUFFER_OVERFLOW; 604 break; 605 } 606 607 if (status != B_OK) 608 return status; 609 610 dirent->d_dev = volume->ID(); 611 dirent->d_ino = id; 612 613 dirent = next_dirent(dirent, length, bufferSize); 614 count++; 615 } 616 617 *_num = count; 618 TRACE("exfat_read_dir end\n"); 619 return B_OK; 620 } 621 622 623 static status_t 624 exfat_rewind_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void *_cookie) 625 { 626 DirectoryIterator* iterator = (DirectoryIterator*)_cookie; 627 628 return iterator->Rewind(); 629 } 630 631 632 static status_t 633 exfat_close_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void * /*_cookie*/) 634 { 635 return B_OK; 636 } 637 638 639 static status_t 640 exfat_free_dir_cookie(fs_volume *_volume, fs_vnode *_node, void *_cookie) 641 { 642 delete (DirectoryIterator*)_cookie; 643 return B_OK; 644 } 645 646 647 fs_volume_ops gExfatVolumeOps = { 648 &exfat_unmount, 649 &exfat_read_fs_info, 650 NULL, // write_fs_info() 651 NULL, // fs_sync, 652 &exfat_get_vnode, 653 }; 654 655 656 fs_vnode_ops gExfatVnodeOps = { 657 /* vnode operations */ 658 &exfat_lookup, 659 NULL, 660 &exfat_put_vnode, 661 NULL, // exfat_remove_vnode, 662 663 /* VM file access */ 664 &exfat_can_page, 665 &exfat_read_pages, 666 NULL, // exfat_write_pages, 667 668 NULL, // io() 669 NULL, // cancel_io() 670 671 &exfat_get_file_map, 672 673 &exfat_ioctl, 674 NULL, 675 NULL, // fs_select 676 NULL, // fs_deselect 677 NULL, // fs_fsync, 678 679 &exfat_read_link, 680 NULL, // fs_create_symlink, 681 682 NULL, // fs_link, 683 NULL, // fs_unlink, 684 NULL, // fs_rename, 685 686 &exfat_access, 687 &exfat_read_stat, 688 NULL, // fs_write_stat, 689 NULL, // fs_preallocate 690 691 /* file operations */ 692 NULL, // fs_create, 693 &exfat_open, 694 &exfat_close, 695 &exfat_free_cookie, 696 &exfat_read, 697 NULL, // fs_write, 698 699 /* directory operations */ 700 NULL, // fs_create_dir, 701 NULL, // fs_remove_dir, 702 &exfat_open_dir, 703 &exfat_close_dir, 704 &exfat_free_dir_cookie, 705 &exfat_read_dir, 706 &exfat_rewind_dir, 707 708 /* attribute directory operations */ 709 NULL, // fs_open_attr_dir, 710 NULL, // fs_close_attr_dir, 711 NULL, // fs_free_attr_dir_cookie, 712 NULL, // fs_read_attr_dir, 713 NULL, // fs_rewind_attr_dir, 714 715 /* attribute operations */ 716 NULL, // fs_create_attr, 717 NULL, // fs_open_attr, 718 NULL, // fs_close_attr, 719 NULL, // fs_free_attr_cookie, 720 NULL, // fs_read_attr, 721 NULL, // fs_write_attr, 722 NULL, // fs_read_attr_stat, 723 NULL, // fs_write_attr_stat, 724 NULL, // fs_rename_attr, 725 NULL, // fs_remove_attr, 726 }; 727 728 729 static file_system_module_info sExfatFileSystem = { 730 { 731 "file_systems/exfat" B_CURRENT_FS_API_VERSION, 732 0, 733 NULL, 734 }, 735 736 "exfat", // short_name 737 "ExFAT File System", // pretty_name 738 0, // DDM flags 739 740 // scanning 741 exfat_identify_partition, 742 exfat_scan_partition, 743 exfat_free_identify_partition_cookie, 744 NULL, // free_partition_content_cookie() 745 746 &exfat_mount, 747 748 NULL, 749 }; 750 751 752 module_info *modules[] = { 753 (module_info *)&sExfatFileSystem, 754 NULL, 755 }; 756