1 /* 2 * Copyright 2008, Axel Dörfler, axeld@pinc-software.de. 3 * This file may be used under the terms of the MIT License. 4 */ 5 6 7 #include <dirent.h> 8 #include <util/kernel_cpp.h> 9 #include <string.h> 10 11 #include <AutoDeleter.h> 12 #include <fs_cache.h> 13 #include <fs_info.h> 14 15 #include "AttributeIterator.h" 16 #include "DirectoryIterator.h" 17 #include "ext2.h" 18 #include "HTree.h" 19 #include "Inode.h" 20 21 22 //#define TRACE_EXT2 23 #ifdef TRACE_EXT2 24 # define TRACE(x...) dprintf("\33[34mext2:\33[0m " x) 25 #else 26 # define TRACE(x...) ; 27 #endif 28 29 30 #define EXT2_IO_SIZE 65536 31 32 33 struct identify_cookie { 34 ext2_super_block super_block; 35 }; 36 37 38 /*! Converts the open mode, the open flags given to bfs_open(), into 39 access modes, e.g. since O_RDONLY requires read access to the 40 file, it will be converted to R_OK. 41 */ 42 int 43 open_mode_to_access(int openMode) 44 { 45 openMode &= O_RWMASK; 46 if (openMode == O_RDONLY) 47 return R_OK; 48 else if (openMode == O_WRONLY) 49 return W_OK; 50 51 return R_OK | W_OK; 52 } 53 54 55 // #pragma mark - Scanning 56 57 58 static float 59 ext2_identify_partition(int fd, partition_data *partition, void **_cookie) 60 { 61 ext2_super_block superBlock; 62 status_t status = Volume::Identify(fd, &superBlock); 63 if (status != B_OK) 64 return -1; 65 66 identify_cookie *cookie = new identify_cookie; 67 memcpy(&cookie->super_block, &superBlock, sizeof(ext2_super_block)); 68 69 *_cookie = cookie; 70 return 0.8f; 71 } 72 73 74 static status_t 75 ext2_scan_partition(int fd, partition_data *partition, void *_cookie) 76 { 77 identify_cookie *cookie = (identify_cookie *)_cookie; 78 79 partition->status = B_PARTITION_VALID; 80 partition->flags |= B_PARTITION_FILE_SYSTEM; 81 partition->content_size = cookie->super_block.NumBlocks() 82 << cookie->super_block.BlockShift(); 83 partition->block_size = 1UL << cookie->super_block.BlockShift(); 84 partition->content_name = strdup(cookie->super_block.name); 85 if (partition->content_name == NULL) 86 return B_NO_MEMORY; 87 88 return B_OK; 89 } 90 91 92 static void 93 ext2_free_identify_partition_cookie(partition_data* partition, void* _cookie) 94 { 95 delete (identify_cookie*)_cookie; 96 } 97 98 99 // #pragma mark - 100 101 102 static status_t 103 ext2_mount(fs_volume* _volume, const char* device, uint32 flags, 104 const char* args, ino_t* _rootID) 105 { 106 Volume* volume = new Volume(_volume); 107 if (volume == NULL) 108 return B_NO_MEMORY; 109 110 // TODO: this is a bit hacky: we can't use publish_vnode() to publish 111 // the root node, or else its file cache cannot be created (we could 112 // create it later, though). Therefore we're using get_vnode() in Mount(), 113 // but that requires us to export our volume data before calling it. 114 _volume->private_volume = volume; 115 _volume->ops = &gExt2VolumeOps; 116 117 status_t status = volume->Mount(device, flags); 118 if (status != B_OK) { 119 delete volume; 120 return status; 121 } 122 123 *_rootID = volume->RootNode()->ID(); 124 return B_OK; 125 } 126 127 128 static status_t 129 ext2_unmount(fs_volume *_volume) 130 { 131 Volume* volume = (Volume *)_volume->private_volume; 132 133 status_t status = volume->Unmount(); 134 delete volume; 135 136 return status; 137 } 138 139 140 static status_t 141 ext2_read_fs_info(fs_volume* _volume, struct fs_info* info) 142 { 143 Volume* volume = (Volume*)_volume->private_volume; 144 145 // File system flags 146 info->flags = B_FS_IS_PERSISTENT 147 | (volume->IsReadOnly() ? B_FS_IS_READONLY : 0); 148 info->io_size = EXT2_IO_SIZE; 149 info->block_size = volume->BlockSize(); 150 info->total_blocks = volume->NumBlocks(); 151 info->free_blocks = volume->FreeBlocks(); 152 153 // Volume name 154 strlcpy(info->volume_name, volume->Name(), sizeof(info->volume_name)); 155 156 // File system name 157 strlcpy(info->fsh_name, "ext2", sizeof(info->fsh_name)); 158 159 return B_OK; 160 } 161 162 163 // #pragma mark - 164 165 166 static status_t 167 ext2_get_vnode(fs_volume* _volume, ino_t id, fs_vnode* _node, int* _type, 168 uint32* _flags, bool reenter) 169 { 170 Volume* volume = (Volume*)_volume->private_volume; 171 172 if (id < 2 || id > volume->NumInodes()) { 173 dprintf("ext2: inode at %Ld requested!\n", id); 174 return B_ERROR; 175 } 176 177 Inode* inode = new Inode(volume, id); 178 if (inode == NULL) 179 return B_NO_MEMORY; 180 181 status_t status = inode->InitCheck(); 182 if (status < B_OK) 183 delete inode; 184 185 if (status == B_OK) { 186 _node->private_node = inode; 187 _node->ops = &gExt2VnodeOps; 188 *_type = inode->Mode(); 189 *_flags = 0; 190 } 191 192 return status; 193 } 194 195 196 static status_t 197 ext2_put_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter) 198 { 199 delete (Inode*)_node->private_node; 200 return B_OK; 201 } 202 203 204 static bool 205 ext2_can_page(fs_volume* _volume, fs_vnode* _node, void* _cookie) 206 { 207 return true; 208 } 209 210 211 static status_t 212 ext2_read_pages(fs_volume* _volume, fs_vnode* _node, void* _cookie, 213 off_t pos, const iovec* vecs, size_t count, size_t* _numBytes) 214 { 215 Volume* volume = (Volume*)_volume->private_volume; 216 Inode* inode = (Inode*)_node->private_node; 217 218 if (inode->FileCache() == NULL) 219 return B_BAD_VALUE; 220 221 rw_lock_read_lock(inode->Lock()); 222 223 uint32 vecIndex = 0; 224 size_t vecOffset = 0; 225 size_t bytesLeft = *_numBytes; 226 status_t status; 227 228 while (true) { 229 file_io_vec fileVecs[8]; 230 uint32 fileVecCount = 8; 231 232 status = file_map_translate(inode->Map(), pos, bytesLeft, fileVecs, 233 &fileVecCount, 0); 234 if (status != B_OK && status != B_BUFFER_OVERFLOW) 235 break; 236 237 bool bufferOverflow = status == B_BUFFER_OVERFLOW; 238 239 size_t bytes = bytesLeft; 240 status = read_file_io_vec_pages(volume->Device(), fileVecs, 241 fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes); 242 if (status != B_OK || !bufferOverflow) 243 break; 244 245 pos += bytes; 246 bytesLeft -= bytes; 247 } 248 249 rw_lock_read_unlock(inode->Lock()); 250 251 return status; 252 } 253 254 255 static status_t 256 ext2_get_file_map(fs_volume* _volume, fs_vnode* _node, off_t offset, 257 size_t size, struct file_io_vec* vecs, size_t* _count) 258 { 259 Volume* volume = (Volume*)_volume->private_volume; 260 Inode* inode = (Inode*)_node->private_node; 261 size_t index = 0, max = *_count; 262 263 while (true) { 264 uint32 block; 265 status_t status = inode->FindBlock(offset, block); 266 if (status != B_OK) 267 return status; 268 269 off_t blockOffset = (off_t)block << volume->BlockShift(); 270 uint32 blockLength = volume->BlockSize(); 271 272 if (index > 0 && (vecs[index - 1].offset == blockOffset - blockLength 273 || (vecs[index - 1].offset == -1 && block == 0))) { 274 vecs[index - 1].length += blockLength; 275 } else { 276 if (index >= max) { 277 // we're out of file_io_vecs; let's bail out 278 *_count = index; 279 return B_BUFFER_OVERFLOW; 280 } 281 282 // 'block' is 0 for sparse blocks 283 if (block != 0) 284 vecs[index].offset = blockOffset; 285 else 286 vecs[index].offset = -1; 287 288 vecs[index].length = blockLength; 289 index++; 290 } 291 292 offset += blockLength; 293 294 if (size <= vecs[index - 1].length || offset >= inode->Size()) { 295 // We're done! 296 *_count = index; 297 TRACE("ext2_get_file_map for inode %ld\n", inode->ID()); 298 return B_OK; 299 } 300 } 301 302 // can never get here 303 return B_ERROR; 304 } 305 306 307 // #pragma mark - 308 309 310 static status_t 311 ext2_lookup(fs_volume* _volume, fs_vnode* _directory, const char* name, 312 ino_t* _vnodeID) 313 { 314 Volume* volume = (Volume*)_volume->private_volume; 315 Inode* directory = (Inode*)_directory->private_node; 316 317 // check access permissions 318 status_t status = directory->CheckPermissions(X_OK); 319 if (status < B_OK) 320 return status; 321 322 HTree htree(volume, directory); 323 DirectoryIterator* iterator; 324 325 status = htree.Lookup(name, &iterator); 326 if (status != B_OK) 327 return status; 328 329 ObjectDeleter<DirectoryIterator> iteratorDeleter(iterator); 330 331 while (true) { 332 char buffer[B_FILE_NAME_LENGTH]; 333 size_t length = sizeof(buffer); 334 status = iterator->GetNext(buffer, &length, _vnodeID); 335 if (status != B_OK) 336 return status; 337 TRACE("ext2_lookup() %s\n", buffer); 338 339 if (!strcmp(buffer, name)) 340 break; 341 } 342 343 return get_vnode(volume->FSVolume(), *_vnodeID, NULL); 344 } 345 346 347 static status_t 348 ext2_read_stat(fs_volume* _volume, fs_vnode* _node, struct stat* stat) 349 { 350 Inode* inode = (Inode*)_node->private_node; 351 const ext2_inode& node = inode->Node(); 352 353 stat->st_dev = inode->GetVolume()->ID(); 354 stat->st_ino = inode->ID(); 355 stat->st_nlink = node.NumLinks(); 356 stat->st_blksize = EXT2_IO_SIZE; 357 358 stat->st_uid = node.UserID(); 359 stat->st_gid = node.GroupID(); 360 stat->st_mode = node.Mode(); 361 stat->st_type = 0; 362 363 stat->st_atime = node.AccessTime(); 364 stat->st_mtime = stat->st_ctime = node.ModificationTime(); 365 stat->st_crtime = node.CreationTime(); 366 367 stat->st_size = inode->Size(); 368 stat->st_blocks = (inode->Size() + 511) / 512; 369 370 return B_OK; 371 } 372 373 374 static status_t 375 ext2_open(fs_volume* _volume, fs_vnode* _node, int openMode, void** _cookie) 376 { 377 Inode* inode = (Inode*)_node->private_node; 378 379 // opening a directory read-only is allowed, although you can't read 380 // any data from it. 381 if (inode->IsDirectory() && (openMode & O_RWMASK) != 0) 382 return B_IS_A_DIRECTORY; 383 384 if ((openMode & O_TRUNC) != 0) 385 return B_READ_ONLY_DEVICE; 386 387 return inode->CheckPermissions(open_mode_to_access(openMode) 388 | (openMode & O_TRUNC ? W_OK : 0)); 389 } 390 391 392 static status_t 393 ext2_read(fs_volume *_volume, fs_vnode *_node, void *_cookie, off_t pos, 394 void *buffer, size_t *_length) 395 { 396 Inode *inode = (Inode *)_node->private_node; 397 398 if (!inode->IsFile()) { 399 *_length = 0; 400 return inode->IsDirectory() ? B_IS_A_DIRECTORY : B_BAD_VALUE; 401 } 402 403 return inode->ReadAt(pos, (uint8 *)buffer, _length); 404 } 405 406 407 static status_t 408 ext2_close(fs_volume *_volume, fs_vnode *_node, void *_cookie) 409 { 410 return B_OK; 411 } 412 413 414 static status_t 415 ext2_free_cookie(fs_volume* _volume, fs_vnode* _node, void* cookie) 416 { 417 return B_OK; 418 } 419 420 421 static status_t 422 ext2_access(fs_volume* _volume, fs_vnode* _node, int accessMode) 423 { 424 Inode* inode = (Inode*)_node->private_node; 425 return inode->CheckPermissions(accessMode); 426 } 427 428 429 static status_t 430 ext2_read_link(fs_volume *_volume, fs_vnode *_node, char *buffer, 431 size_t *_bufferSize) 432 { 433 Inode* inode = (Inode*)_node->private_node; 434 435 if (!inode->IsSymLink()) 436 return B_BAD_VALUE; 437 438 if (inode->Size() < *_bufferSize) 439 *_bufferSize = inode->Size(); 440 441 if (inode->Size() > EXT2_SHORT_SYMLINK_LENGTH) 442 return inode->ReadAt(0, (uint8 *)buffer, _bufferSize); 443 444 memcpy(buffer, inode->Node().symlink, *_bufferSize); 445 return B_OK; 446 } 447 448 449 // #pragma mark - Directory functions 450 451 452 static status_t 453 ext2_open_dir(fs_volume *_volume, fs_vnode *_node, void **_cookie) 454 { 455 Inode *inode = (Inode *)_node->private_node; 456 status_t status = inode->CheckPermissions(R_OK); 457 if (status < B_OK) 458 return status; 459 460 if (!inode->IsDirectory()) 461 return B_NOT_A_DIRECTORY; 462 463 DirectoryIterator* iterator = new(std::nothrow) DirectoryIterator(inode); 464 if (iterator == NULL) 465 return B_NO_MEMORY; 466 467 *_cookie = iterator; 468 return B_OK; 469 } 470 471 472 static status_t 473 ext2_read_dir(fs_volume *_volume, fs_vnode *_node, void *_cookie, 474 struct dirent *dirent, size_t bufferSize, uint32 *_num) 475 { 476 DirectoryIterator *iterator = (DirectoryIterator *)_cookie; 477 478 size_t length = bufferSize; 479 ino_t id; 480 status_t status = iterator->GetNext(dirent->d_name, &length, &id); 481 if (status == B_ENTRY_NOT_FOUND) { 482 *_num = 0; 483 return B_OK; 484 } else if (status != B_OK) 485 return status; 486 487 Volume* volume = (Volume*)_volume->private_volume; 488 489 dirent->d_dev = volume->ID(); 490 dirent->d_ino = id; 491 dirent->d_reclen = sizeof(struct dirent) + length; 492 493 *_num = 1; 494 return B_OK; 495 } 496 497 498 static status_t 499 ext2_rewind_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void *_cookie) 500 { 501 DirectoryIterator *iterator = (DirectoryIterator *)_cookie; 502 return iterator->Rewind(); 503 } 504 505 506 static status_t 507 ext2_close_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void * /*_cookie*/) 508 { 509 return B_OK; 510 } 511 512 513 static status_t 514 ext2_free_dir_cookie(fs_volume *_volume, fs_vnode *_node, void *_cookie) 515 { 516 delete (DirectoryIterator *)_cookie; 517 return B_OK; 518 } 519 520 521 static status_t 522 ext2_open_attr_dir(fs_volume *_volume, fs_vnode *_node, void **_cookie) 523 { 524 Inode* inode = (Inode*)_node->private_node; 525 Volume* volume = (Volume*)_volume->private_volume; 526 TRACE("%s()\n", __FUNCTION__); 527 528 if (!(volume->SuperBlock().CompatibleFeatures() & EXT2_FEATURE_EXT_ATTR)) 529 return ENOSYS; 530 531 // on directories too ? 532 if (!inode->IsFile()) 533 return EINVAL; 534 535 AttributeIterator* iterator = new(std::nothrow) AttributeIterator(inode); 536 if (iterator == NULL) 537 return B_NO_MEMORY; 538 539 *_cookie = iterator; 540 return B_OK; 541 } 542 543 static status_t 544 ext2_close_attr_dir(fs_volume* _volume, fs_vnode* _node, void* cookie) 545 { 546 TRACE("%s()\n", __FUNCTION__); 547 return B_OK; 548 } 549 550 551 static status_t 552 ext2_free_attr_dir_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie) 553 { 554 TRACE("%s()\n", __FUNCTION__); 555 delete (AttributeIterator *)_cookie; 556 return B_OK; 557 } 558 559 560 static status_t 561 ext2_read_attr_dir(fs_volume* _volume, fs_vnode* _node, 562 void* _cookie, struct dirent* dirent, size_t bufferSize, 563 uint32* _num) 564 { 565 Inode* inode = (Inode*)_node->private_node; 566 AttributeIterator *iterator = (AttributeIterator *)_cookie; 567 TRACE("%s()\n", __FUNCTION__); 568 569 size_t length = bufferSize; 570 status_t status = iterator->GetNext(dirent->d_name, &length); 571 if (status == B_ENTRY_NOT_FOUND) { 572 *_num = 0; 573 return B_OK; 574 } else if (status != B_OK) 575 return status; 576 577 Volume* volume = (Volume*)_volume->private_volume; 578 579 dirent->d_dev = volume->ID(); 580 dirent->d_ino = inode->ID(); 581 dirent->d_reclen = sizeof(struct dirent) + length; 582 583 *_num = 1; 584 return B_OK; 585 } 586 587 588 static status_t 589 ext2_rewind_attr_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie) 590 { 591 AttributeIterator *iterator = (AttributeIterator *)_cookie; 592 TRACE("%s()\n", __FUNCTION__); 593 return iterator->Rewind(); 594 } 595 596 597 /* attribute operations */ 598 static status_t 599 ext2_create_attr(fs_volume* _volume, fs_vnode* _node, 600 const char* name, uint32 type, int openMode, void** _cookie) 601 { 602 return EROFS; 603 } 604 605 606 static status_t 607 ext2_open_attr(fs_volume* _volume, fs_vnode* _node, const char* name, 608 int openMode, void** _cookie) 609 { 610 TRACE("%s()\n", __FUNCTION__); 611 if ((openMode & O_RWMASK) != O_RDONLY) 612 return EROFS; 613 614 Inode* inode = (Inode*)_node->private_node; 615 Volume* volume = (Volume*)_volume->private_volume; 616 617 if (!(volume->SuperBlock().CompatibleFeatures() & EXT2_FEATURE_EXT_ATTR)) 618 return ENOSYS; 619 620 // on directories too ? 621 if (!inode->IsFile()) 622 return EINVAL; 623 624 ext2_xattr_entry *entry = new ext2_xattr_entry; 625 626 AttributeIterator i(inode); 627 status_t status = i.Find(name, entry); 628 if (status == B_OK) { 629 //entry->Dump(); 630 *_cookie = entry; 631 return B_OK; 632 } 633 634 delete entry; 635 return status; 636 } 637 638 639 static status_t 640 ext2_close_attr(fs_volume* _volume, fs_vnode* _node, 641 void* cookie) 642 { 643 return B_OK; 644 } 645 646 647 static status_t 648 ext2_free_attr_cookie(fs_volume* _volume, fs_vnode* _node, 649 void* cookie) 650 { 651 ext2_xattr_entry *entry = (ext2_xattr_entry *)cookie; 652 653 delete entry; 654 return B_OK; 655 } 656 657 658 static status_t 659 ext2_read_attr(fs_volume* _volume, fs_vnode* _node, void* cookie, 660 off_t pos, void* buffer, size_t* length) 661 { 662 TRACE("%s()\n", __FUNCTION__); 663 664 Inode* inode = (Inode*)_node->private_node; 665 Volume* volume = (Volume*)_volume->private_volume; 666 ext2_xattr_entry *entry = (ext2_xattr_entry *)cookie; 667 668 if (!entry->IsValid()) 669 return EINVAL; 670 671 if (pos < 0 || (pos + *length) > entry->ValueSize()) 672 return ERANGE; 673 674 return inode->AttributeBlockReadAt(entry->ValueOffset() + pos, 675 (uint8 *)buffer, length); 676 } 677 678 679 static status_t 680 ext2_write_attr(fs_volume* _volume, fs_vnode* _node, void* cookie, 681 off_t pos, const void* buffer, size_t* length) 682 { 683 return EROFS; 684 } 685 686 687 688 static status_t 689 ext2_read_attr_stat(fs_volume* _volume, fs_vnode* _node, 690 void* cookie, struct stat* stat) 691 { 692 ext2_xattr_entry *entry = (ext2_xattr_entry *)cookie; 693 694 stat->st_type = B_RAW_TYPE; 695 stat->st_size = entry->ValueSize(); 696 TRACE("%s: st_size %d\n", __FUNCTION__, stat->st_size); 697 698 return B_OK; 699 } 700 701 702 static status_t 703 ext2_write_attr_stat(fs_volume* _volume, fs_vnode* _node, 704 void* cookie, const struct stat* stat, int statMask) 705 { 706 return EROFS; 707 } 708 709 710 static status_t 711 ext2_rename_attr(fs_volume* _volume, fs_vnode* fromVnode, 712 const char* fromName, fs_vnode* toVnode, const char* toName) 713 { 714 return ENOSYS; 715 } 716 717 718 static status_t 719 ext2_remove_attr(fs_volume* _volume, fs_vnode* vnode, 720 const char* name) 721 { 722 return ENOSYS; 723 } 724 725 726 fs_volume_ops gExt2VolumeOps = { 727 &ext2_unmount, 728 &ext2_read_fs_info, 729 NULL, // write_fs_info() 730 NULL, // sync() 731 &ext2_get_vnode, 732 }; 733 734 fs_vnode_ops gExt2VnodeOps = { 735 /* vnode operations */ 736 &ext2_lookup, 737 NULL, 738 &ext2_put_vnode, 739 NULL, 740 741 /* VM file access */ 742 &ext2_can_page, 743 &ext2_read_pages, 744 NULL, 745 746 NULL, // io() 747 NULL, // cancel_io() 748 749 &ext2_get_file_map, 750 751 NULL, 752 NULL, 753 NULL, // fs_select 754 NULL, // fs_deselect 755 NULL, 756 757 &ext2_read_link, 758 NULL, 759 760 NULL, 761 NULL, 762 NULL, 763 764 &ext2_access, 765 &ext2_read_stat, 766 NULL, 767 768 /* file operations */ 769 NULL, 770 &ext2_open, 771 &ext2_close, 772 &ext2_free_cookie, 773 &ext2_read, 774 NULL, 775 776 /* directory operations */ 777 NULL, 778 NULL, 779 &ext2_open_dir, 780 &ext2_close_dir, 781 &ext2_free_dir_cookie, 782 &ext2_read_dir, 783 &ext2_rewind_dir, 784 785 /* attribute directory operations */ 786 &ext2_open_attr_dir, 787 &ext2_close_attr_dir, 788 &ext2_free_attr_dir_cookie, 789 &ext2_read_attr_dir, 790 &ext2_rewind_attr_dir, 791 792 /* attribute operations */ 793 NULL, //&ext2_create_attr, 794 &ext2_open_attr, 795 &ext2_close_attr, 796 &ext2_free_attr_cookie, 797 &ext2_read_attr, 798 NULL, //&ext2_write_attr, 799 &ext2_read_attr_stat, 800 NULL, //&ext2_write_attr_stat, 801 NULL, //&ext2_rename_attr, 802 NULL, //&ext2_remove_attr, 803 804 }; 805 806 static file_system_module_info sExt2FileSystem = { 807 { 808 "file_systems/ext2" B_CURRENT_FS_API_VERSION, 809 0, 810 NULL, 811 }, 812 813 "ext2", // short_name 814 "Ext2 File System", // pretty_name 815 0, // DDM flags 816 817 // scanning 818 ext2_identify_partition, 819 ext2_scan_partition, 820 ext2_free_identify_partition_cookie, 821 NULL, // free_partition_content_cookie() 822 823 &ext2_mount, 824 825 NULL, 826 }; 827 828 module_info *modules[] = { 829 (module_info *)&sExt2FileSystem, 830 NULL, 831 }; 832