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 = (uint64)(EXFAT_SUPER_BLOCK_OFFSET 100 + 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 return status; 401 } 402 403 TRACE("exfat_lookup: ID %" B_PRIdINO "\n", *_vnodeID); 404 405 return get_vnode(volume->FSVolume(), *_vnodeID, NULL); 406 } 407 408 409 static status_t 410 exfat_ioctl(fs_volume* _volume, fs_vnode* _node, void* _cookie, uint32 cmd, 411 void* buffer, size_t bufferLength) 412 { 413 TRACE("ioctl: %" B_PRIu32 "\n", cmd); 414 415 /*Volume* volume = (Volume*)_volume->private_volume;*/ 416 return B_DEV_INVALID_IOCTL; 417 } 418 419 420 static status_t 421 exfat_read_stat(fs_volume* _volume, fs_vnode* _node, struct stat* stat) 422 { 423 Inode* inode = (Inode*)_node->private_node; 424 425 stat->st_dev = inode->GetVolume()->ID(); 426 stat->st_ino = inode->ID(); 427 stat->st_nlink = 1; 428 stat->st_blksize = EXFAT_IO_SIZE; 429 430 stat->st_uid = inode->UserID(); 431 stat->st_gid = inode->GroupID(); 432 stat->st_mode = inode->Mode(); 433 stat->st_type = 0; 434 435 inode->GetAccessTime(stat->st_atim); 436 inode->GetModificationTime(stat->st_mtim); 437 inode->GetChangeTime(stat->st_ctim); 438 inode->GetCreationTime(stat->st_crtim); 439 440 stat->st_size = inode->Size(); 441 stat->st_blocks = (inode->Size() + 511) / 512; 442 443 return B_OK; 444 } 445 446 447 static status_t 448 exfat_open(fs_volume* /*_volume*/, fs_vnode* _node, int openMode, 449 void** _cookie) 450 { 451 Inode* inode = (Inode*)_node->private_node; 452 453 // opening a directory read-only is allowed, although you can't read 454 // any data from it. 455 if (inode->IsDirectory() && (openMode & O_RWMASK) != 0) 456 return B_IS_A_DIRECTORY; 457 458 status_t status = inode->CheckPermissions(open_mode_to_access(openMode) 459 | (openMode & O_TRUNC ? W_OK : 0)); 460 if (status != B_OK) 461 return status; 462 463 // Prepare the cookie 464 file_cookie* cookie = new(std::nothrow) file_cookie; 465 if (cookie == NULL) 466 return B_NO_MEMORY; 467 ObjectDeleter<file_cookie> cookieDeleter(cookie); 468 469 cookie->open_mode = openMode & EXFAT_OPEN_MODE_USER_MASK; 470 cookie->last_size = inode->Size(); 471 cookie->last_notification = system_time(); 472 473 if ((openMode & O_NOCACHE) != 0 && inode->FileCache() != NULL) { 474 // Disable the file cache, if requested? 475 status = file_cache_disable(inode->FileCache()); 476 if (status != B_OK) 477 return status; 478 } 479 480 cookieDeleter.Detach(); 481 *_cookie = cookie; 482 483 return B_OK; 484 } 485 486 487 static status_t 488 exfat_read(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos, 489 void* buffer, size_t* _length) 490 { 491 Inode* inode = (Inode*)_node->private_node; 492 493 if (!inode->IsFile()) { 494 *_length = 0; 495 return inode->IsDirectory() ? B_IS_A_DIRECTORY : B_BAD_VALUE; 496 } 497 498 return inode->ReadAt(pos, (uint8*)buffer, _length); 499 } 500 501 502 static status_t 503 exfat_close(fs_volume *_volume, fs_vnode *_node, void *_cookie) 504 { 505 return B_OK; 506 } 507 508 509 static status_t 510 exfat_free_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie) 511 { 512 file_cookie* cookie = (file_cookie*)_cookie; 513 Volume* volume = (Volume*)_volume->private_volume; 514 Inode* inode = (Inode*)_node->private_node; 515 516 if (inode->Size() != cookie->last_size) 517 notify_stat_changed(volume->ID(), inode->ID(), B_STAT_SIZE); 518 519 delete cookie; 520 return B_OK; 521 } 522 523 524 static status_t 525 exfat_access(fs_volume* _volume, fs_vnode* _node, int accessMode) 526 { 527 Inode* inode = (Inode*)_node->private_node; 528 return inode->CheckPermissions(accessMode); 529 } 530 531 532 static status_t 533 exfat_read_link(fs_volume *_volume, fs_vnode *_node, char *buffer, 534 size_t *_bufferSize) 535 { 536 Inode* inode = (Inode*)_node->private_node; 537 return inode->ReadAt(0, (uint8*)buffer, _bufferSize); 538 } 539 540 541 // #pragma mark - Directory functions 542 543 544 static status_t 545 exfat_open_dir(fs_volume* /*_volume*/, fs_vnode* _node, void** _cookie) 546 { 547 Inode* inode = (Inode*)_node->private_node; 548 status_t status = inode->CheckPermissions(R_OK); 549 if (status < B_OK) 550 return status; 551 552 if (!inode->IsDirectory()) 553 return B_NOT_A_DIRECTORY; 554 555 DirectoryIterator* iterator = new(std::nothrow) DirectoryIterator(inode); 556 if (iterator == NULL || iterator->InitCheck() != B_OK) { 557 delete iterator; 558 return B_NO_MEMORY; 559 } 560 561 *_cookie = iterator; 562 return B_OK; 563 } 564 565 566 static status_t 567 exfat_read_dir(fs_volume *_volume, fs_vnode *_node, void *_cookie, 568 struct dirent *dirent, size_t bufferSize, uint32 *_num) 569 { 570 TRACE("exfat_read_dir\n"); 571 DirectoryIterator* iterator = (DirectoryIterator*)_cookie; 572 Volume* volume = (Volume*)_volume->private_volume; 573 574 uint32 maxCount = *_num; 575 uint32 count = 0; 576 577 while (count < maxCount && bufferSize > sizeof(struct dirent)) { 578 ino_t id; 579 size_t length = bufferSize - sizeof(struct dirent) + 1; 580 581 status_t status = iterator->GetNext(dirent->d_name, &length, &id); 582 if (status == B_ENTRY_NOT_FOUND) 583 break; 584 585 if (status == B_BUFFER_OVERFLOW) { 586 // the remaining name buffer length was too small 587 if (count == 0) 588 return B_BUFFER_OVERFLOW; 589 break; 590 } 591 592 if (status != B_OK) 593 return status; 594 595 dirent->d_dev = volume->ID(); 596 dirent->d_ino = id; 597 dirent->d_reclen = sizeof(struct dirent) + length; 598 599 bufferSize -= dirent->d_reclen; 600 dirent = (struct dirent*)((uint8*)dirent + dirent->d_reclen); 601 count++; 602 } 603 604 *_num = count; 605 TRACE("exfat_read_dir end\n"); 606 return B_OK; 607 } 608 609 610 static status_t 611 exfat_rewind_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void *_cookie) 612 { 613 DirectoryIterator* iterator = (DirectoryIterator*)_cookie; 614 615 return iterator->Rewind(); 616 } 617 618 619 static status_t 620 exfat_close_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void * /*_cookie*/) 621 { 622 return B_OK; 623 } 624 625 626 static status_t 627 exfat_free_dir_cookie(fs_volume *_volume, fs_vnode *_node, void *_cookie) 628 { 629 delete (DirectoryIterator*)_cookie; 630 return B_OK; 631 } 632 633 634 fs_volume_ops gExfatVolumeOps = { 635 &exfat_unmount, 636 &exfat_read_fs_info, 637 NULL, // write_fs_info() 638 NULL, // fs_sync, 639 &exfat_get_vnode, 640 }; 641 642 643 fs_vnode_ops gExfatVnodeOps = { 644 /* vnode operations */ 645 &exfat_lookup, 646 NULL, 647 &exfat_put_vnode, 648 NULL, // exfat_remove_vnode, 649 650 /* VM file access */ 651 &exfat_can_page, 652 &exfat_read_pages, 653 NULL, // exfat_write_pages, 654 655 NULL, // io() 656 NULL, // cancel_io() 657 658 &exfat_get_file_map, 659 660 &exfat_ioctl, 661 NULL, 662 NULL, // fs_select 663 NULL, // fs_deselect 664 NULL, // fs_fsync, 665 666 &exfat_read_link, 667 NULL, // fs_create_symlink, 668 669 NULL, // fs_link, 670 NULL, // fs_unlink, 671 NULL, // fs_rename, 672 673 &exfat_access, 674 &exfat_read_stat, 675 NULL, // fs_write_stat, 676 NULL, // fs_preallocate 677 678 /* file operations */ 679 NULL, // fs_create, 680 &exfat_open, 681 &exfat_close, 682 &exfat_free_cookie, 683 &exfat_read, 684 NULL, // fs_write, 685 686 /* directory operations */ 687 NULL, // fs_create_dir, 688 NULL, // fs_remove_dir, 689 &exfat_open_dir, 690 &exfat_close_dir, 691 &exfat_free_dir_cookie, 692 &exfat_read_dir, 693 &exfat_rewind_dir, 694 695 /* attribute directory operations */ 696 NULL, // fs_open_attr_dir, 697 NULL, // fs_close_attr_dir, 698 NULL, // fs_free_attr_dir_cookie, 699 NULL, // fs_read_attr_dir, 700 NULL, // fs_rewind_attr_dir, 701 702 /* attribute operations */ 703 NULL, // fs_create_attr, 704 NULL, // fs_open_attr, 705 NULL, // fs_close_attr, 706 NULL, // fs_free_attr_cookie, 707 NULL, // fs_read_attr, 708 NULL, // fs_write_attr, 709 NULL, // fs_read_attr_stat, 710 NULL, // fs_write_attr_stat, 711 NULL, // fs_rename_attr, 712 NULL, // fs_remove_attr, 713 }; 714 715 716 static file_system_module_info sExfatFileSystem = { 717 { 718 "file_systems/exfat" B_CURRENT_FS_API_VERSION, 719 0, 720 NULL, 721 }, 722 723 "exfat", // short_name 724 "ExFAT File System", // pretty_name 725 0, // DDM flags 726 727 // scanning 728 exfat_identify_partition, 729 exfat_scan_partition, 730 exfat_free_identify_partition_cookie, 731 NULL, // free_partition_content_cookie() 732 733 &exfat_mount, 734 735 NULL, 736 }; 737 738 739 module_info *modules[] = { 740 (module_info *)&sExfatFileSystem, 741 NULL, 742 }; 743