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