1 /* 2 * Copyright 2019, Les De Ridder, les@lesderid.net 3 * Copyright 2017, Chế Vũ Gia Hy, cvghy116@gmail.com. 4 * Copyright 2011, Jérôme Duval, korli@users.berlios.de. 5 * Copyright 2008, Axel Dörfler, axeld@pinc-software.de. 6 * 7 * This file may be used under the terms of the MIT License. 8 */ 9 10 11 #include "Attribute.h" 12 #include "AttributeIterator.h" 13 #include "btrfs.h" 14 #include "btrfs_disk_system.h" 15 #include "DirectoryIterator.h" 16 #include "Inode.h" 17 #include "system_dependencies.h" 18 #include "Utility.h" 19 20 21 #ifdef FS_SHELL 22 #define ERROR(x...) TRACE(x) 23 #define INFORM(x...) TRACE(x) 24 #define init_debugging() 25 #define exit_debugging() 26 #else 27 #include <DebugSupport.h> 28 #endif 29 30 31 //#define TRACE_BTRFS 32 #ifdef TRACE_BTRFS 33 # define TRACE(x...) dprintf("\33[34mbtrfs:\33[0m " x) 34 #else 35 # define TRACE(x...) ; 36 #endif 37 38 #define BTRFS_IO_SIZE 65536 39 40 41 struct identify_cookie { 42 btrfs_super_block super_block; 43 }; 44 45 46 //! btrfs_io() callback hook 47 static status_t 48 iterative_io_get_vecs_hook(void* cookie, io_request* request, off_t offset, 49 size_t size, struct file_io_vec* vecs, size_t* _count) 50 { 51 Inode* inode = (Inode*)cookie; 52 53 return file_map_translate(inode->Map(), offset, size, vecs, _count, 54 inode->GetVolume()->BlockSize()); 55 } 56 57 58 //! btrfs_io() callback hook 59 static status_t 60 iterative_io_finished_hook(void* cookie, io_request* request, status_t status, 61 bool partialTransfer, size_t bytesTransferred) 62 { 63 Inode* inode = (Inode*)cookie; 64 rw_lock_read_unlock(inode->Lock()); 65 return B_OK; 66 } 67 68 69 // #pragma mark - Scanning 70 71 72 static float 73 btrfs_identify_partition(int fd, partition_data* partition, void** _cookie) 74 { 75 btrfs_super_block superBlock; 76 status_t status = Volume::Identify(fd, &superBlock); 77 if (status != B_OK) 78 return -1; 79 80 identify_cookie* cookie = new identify_cookie; 81 memcpy(&cookie->super_block, &superBlock, sizeof(btrfs_super_block)); 82 83 *_cookie = cookie; 84 return 0.8f; 85 } 86 87 88 static status_t 89 btrfs_scan_partition(int fd, partition_data* partition, void* _cookie) 90 { 91 identify_cookie* cookie = (identify_cookie*)_cookie; 92 93 partition->status = B_PARTITION_VALID; 94 partition->flags |= B_PARTITION_FILE_SYSTEM; 95 partition->content_size = cookie->super_block.TotalSize(); 96 partition->block_size = cookie->super_block.BlockSize(); 97 partition->content_name = strdup(cookie->super_block.label); 98 if (partition->content_name == NULL) 99 return B_NO_MEMORY; 100 101 return B_OK; 102 } 103 104 105 static void 106 btrfs_free_identify_partition_cookie(partition_data* partition, void* _cookie) 107 { 108 delete (identify_cookie*)_cookie; 109 } 110 111 112 // #pragma mark - 113 114 115 static status_t 116 btrfs_mount(fs_volume* _volume, const char* device, uint32 flags, 117 const char* args, ino_t* _rootID) 118 { 119 Volume* volume = new(std::nothrow) Volume(_volume); 120 if (volume == NULL) 121 return B_NO_MEMORY; 122 123 // TODO: this is a bit hacky: we can't use publish_vnode() to publish 124 // the root node, or else its file cache cannot be created (we could 125 // create it later, though). Therefore we're using get_vnode() in Mount(), 126 // but that requires us to export our volume data before calling it. 127 _volume->private_volume = volume; 128 _volume->ops = &gBtrfsVolumeOps; 129 130 status_t status = volume->Mount(device, flags); 131 if (status != B_OK) { 132 ERROR("Failed mounting the volume. Error: %s\n", strerror(status)); 133 delete volume; 134 return status; 135 } 136 137 *_rootID = volume->RootNode()->ID(); 138 return B_OK; 139 } 140 141 142 static status_t 143 btrfs_unmount(fs_volume* _volume) 144 { 145 Volume* volume = (Volume*)_volume->private_volume; 146 147 status_t status = volume->Unmount(); 148 delete volume; 149 150 return status; 151 } 152 153 154 static status_t 155 btrfs_read_fs_info(fs_volume* _volume, struct fs_info* info) 156 { 157 Volume* volume = (Volume*)_volume->private_volume; 158 159 // File system flags 160 info->flags = B_FS_IS_PERSISTENT | B_FS_HAS_ATTR 161 | (volume->IsReadOnly() ? B_FS_IS_READONLY : 0); 162 info->io_size = BTRFS_IO_SIZE; 163 info->block_size = volume->BlockSize(); 164 info->total_blocks = volume->SuperBlock().TotalSize() / volume->BlockSize(); 165 info->free_blocks = 0; //volume->NumFreeBlocks(); 166 167 // Volume name 168 strlcpy(info->volume_name, volume->Name(), sizeof(info->volume_name)); 169 170 // File system name 171 strlcpy(info->fsh_name, "btrfs", sizeof(info->fsh_name)); 172 173 return B_OK; 174 } 175 176 177 // #pragma mark - 178 179 180 static status_t 181 btrfs_get_vnode(fs_volume* _volume, ino_t id, fs_vnode* _node, int* _type, 182 uint32* _flags, bool reenter) 183 { 184 Volume* volume = (Volume*)_volume->private_volume; 185 186 Inode* inode = new(std::nothrow) Inode(volume, id); 187 if (inode == NULL) 188 return B_NO_MEMORY; 189 190 status_t status = inode->InitCheck(); 191 if (status != B_OK) { 192 delete inode; 193 ERROR("get_vnode: InitCheck() failed. Error: %s\n", strerror(status)); 194 return status; 195 } 196 197 _node->private_node = inode; 198 _node->ops = &gBtrfsVnodeOps; 199 *_type = inode->Mode(); 200 *_flags = 0; 201 202 return B_OK; 203 } 204 205 206 static status_t 207 btrfs_put_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter) 208 { 209 delete (Inode*)_node->private_node; 210 return B_OK; 211 } 212 213 214 static bool 215 btrfs_can_page(fs_volume* _volume, fs_vnode* _node, void* _cookie) 216 { 217 return true; 218 } 219 220 221 static status_t 222 btrfs_read_pages(fs_volume* _volume, fs_vnode* _node, void* _cookie, 223 off_t pos, const iovec* vecs, size_t count, size_t* _numBytes) 224 { 225 Volume* volume = (Volume*)_volume->private_volume; 226 Inode* inode = (Inode*)_node->private_node; 227 228 if (inode->FileCache() == NULL) 229 return B_BAD_VALUE; 230 231 rw_lock_read_lock(inode->Lock()); 232 233 uint32 vecIndex = 0; 234 size_t vecOffset = 0; 235 size_t bytesLeft = *_numBytes; 236 status_t status; 237 238 while (true) { 239 file_io_vec fileVecs[8]; 240 size_t fileVecCount = 8; 241 242 status = file_map_translate(inode->Map(), pos, bytesLeft, fileVecs, 243 &fileVecCount, 0); 244 if (status != B_OK && status != B_BUFFER_OVERFLOW) 245 break; 246 247 bool bufferOverflow = status == B_BUFFER_OVERFLOW; 248 249 size_t bytes = bytesLeft; 250 status = read_file_io_vec_pages(volume->Device(), fileVecs, 251 fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes); 252 if (status != B_OK || !bufferOverflow) 253 break; 254 255 pos += bytes; 256 bytesLeft -= bytes; 257 } 258 259 rw_lock_read_unlock(inode->Lock()); 260 261 return status; 262 } 263 264 265 static status_t 266 btrfs_io(fs_volume* _volume, fs_vnode* _node, void* _cookie, 267 io_request* request) 268 { 269 Volume* volume = (Volume*)_volume->private_volume; 270 Inode* inode = (Inode*)_node->private_node; 271 272 #ifndef FS_SHELL 273 if (io_request_is_write(request) && volume->IsReadOnly()) { 274 notify_io_request(request, B_READ_ONLY_DEVICE); 275 return B_READ_ONLY_DEVICE; 276 } 277 #endif 278 279 if (inode->FileCache() == NULL) { 280 #ifndef FS_SHELL 281 notify_io_request(request, B_BAD_VALUE); 282 #endif 283 return B_BAD_VALUE; 284 } 285 286 // We lock the node here and will unlock it in the "finished" hook. 287 rw_lock_read_lock(inode->Lock()); 288 289 return do_iterative_fd_io(volume->Device(), request, 290 iterative_io_get_vecs_hook, iterative_io_finished_hook, inode); 291 } 292 293 294 static status_t 295 btrfs_get_file_map(fs_volume* _volume, fs_vnode* _node, off_t offset, 296 size_t size, struct file_io_vec* vecs, size_t* _count) 297 { 298 TRACE("btrfs_get_file_map()\n"); 299 Inode* inode = (Inode*)_node->private_node; 300 size_t index = 0, max = *_count; 301 302 while (true) { 303 off_t blockOffset; 304 off_t blockLength; 305 status_t status = inode->FindBlock(offset, blockOffset, &blockLength); 306 if (status != B_OK) 307 return status; 308 309 if (index > 0 && (vecs[index - 1].offset 310 == blockOffset - vecs[index - 1].length)) { 311 vecs[index - 1].length += blockLength; 312 } else { 313 if (index >= max) { 314 // we're out of file_io_vecs; let's bail out 315 *_count = index; 316 return B_BUFFER_OVERFLOW; 317 } 318 319 vecs[index].offset = blockOffset; 320 vecs[index].length = blockLength; 321 index++; 322 } 323 324 offset += blockLength; 325 size -= blockLength; 326 327 if ((off_t)size <= vecs[index - 1].length || offset >= inode->Size()) { 328 // We're done! 329 *_count = index; 330 TRACE("btrfs_get_file_map for inode %" B_PRIdINO "\n", inode->ID()); 331 return B_OK; 332 } 333 } 334 335 // can never get here 336 return B_ERROR; 337 } 338 339 340 // #pragma mark - 341 342 343 static status_t 344 btrfs_lookup(fs_volume* _volume, fs_vnode* _directory, const char* name, 345 ino_t* _vnodeID) 346 { 347 TRACE("btrfs_lookup: name address: %p (%s)\n", name, name); 348 Volume* volume = (Volume*)_volume->private_volume; 349 Inode* directory = (Inode*)_directory->private_node; 350 351 // check access permissions 352 status_t status = directory->CheckPermissions(X_OK); 353 if (status < B_OK) 354 return status; 355 356 status = DirectoryIterator(directory).Lookup(name, strlen(name), _vnodeID); 357 if (status != B_OK) 358 return status; 359 360 return get_vnode(volume->FSVolume(), *_vnodeID, NULL); 361 } 362 363 364 static status_t 365 btrfs_ioctl(fs_volume* _volume, fs_vnode* _node, void* _cookie, uint32 cmd, 366 void* buffer, size_t bufferLength) 367 { 368 TRACE("ioctl: %" B_PRIu32 "\n", cmd); 369 370 /*Volume* volume = (Volume*)_volume->private_volume;*/ 371 return B_DEV_INVALID_IOCTL; 372 } 373 374 375 static status_t 376 btrfs_read_stat(fs_volume* _volume, fs_vnode* _node, struct stat* stat) 377 { 378 Inode* inode = (Inode*)_node->private_node; 379 380 stat->st_dev = inode->GetVolume()->ID(); 381 stat->st_ino = inode->ID(); 382 stat->st_nlink = 1; 383 stat->st_blksize = BTRFS_IO_SIZE; 384 385 stat->st_uid = inode->UserID(); 386 stat->st_gid = inode->GroupID(); 387 stat->st_mode = inode->Mode(); 388 stat->st_type = 0; 389 390 inode->GetAccessTime(stat->st_atim); 391 inode->GetModificationTime(stat->st_mtim); 392 inode->GetChangeTime(stat->st_ctim); 393 inode->GetCreationTime(stat->st_crtim); 394 395 stat->st_size = inode->Size(); 396 stat->st_blocks = (inode->Size() + 511) / 512; 397 398 return B_OK; 399 } 400 401 402 static status_t 403 btrfs_open(fs_volume* /*_volume*/, fs_vnode* _node, int openMode, 404 void** _cookie) 405 { 406 Inode* inode = (Inode*)_node->private_node; 407 408 // opening a directory read-only is allowed, although you can't read 409 // any data from it. 410 if (inode->IsDirectory() && (openMode & O_RWMASK) != 0) 411 return B_IS_A_DIRECTORY; 412 413 status_t status = inode->CheckPermissions(open_mode_to_access(openMode) 414 | (openMode & O_TRUNC ? W_OK : 0)); 415 if (status != B_OK) 416 return status; 417 418 // Prepare the cookie 419 file_cookie* cookie = new(std::nothrow) file_cookie; 420 if (cookie == NULL) 421 return B_NO_MEMORY; 422 ObjectDeleter<file_cookie> cookieDeleter(cookie); 423 424 cookie->open_mode = openMode & BTRFS_OPEN_MODE_USER_MASK; 425 cookie->last_size = inode->Size(); 426 cookie->last_notification = system_time(); 427 428 if ((openMode & O_NOCACHE) != 0 && inode->FileCache() != NULL) { 429 // Disable the file cache, if requested? 430 status = file_cache_disable(inode->FileCache()); 431 if (status != B_OK) 432 return status; 433 } 434 435 cookieDeleter.Detach(); 436 *_cookie = cookie; 437 438 return B_OK; 439 } 440 441 442 static status_t 443 btrfs_read(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos, 444 void* buffer, size_t* _length) 445 { 446 Inode* inode = (Inode*)_node->private_node; 447 448 if (!inode->IsFile()) { 449 *_length = 0; 450 return inode->IsDirectory() ? B_IS_A_DIRECTORY : B_BAD_VALUE; 451 } 452 453 return inode->ReadAt(pos, (uint8*)buffer, _length); 454 } 455 456 457 static status_t 458 btrfs_close(fs_volume* _volume, fs_vnode* _node, void* _cookie) 459 { 460 return B_OK; 461 } 462 463 464 static status_t 465 btrfs_free_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie) 466 { 467 file_cookie* cookie = (file_cookie*)_cookie; 468 Volume* volume = (Volume*)_volume->private_volume; 469 Inode* inode = (Inode*)_node->private_node; 470 471 if (inode->Size() != cookie->last_size) 472 notify_stat_changed(volume->ID(), -1, inode->ID(), B_STAT_SIZE); 473 474 delete cookie; 475 return B_OK; 476 } 477 478 479 static status_t 480 btrfs_access(fs_volume* _volume, fs_vnode* _node, int accessMode) 481 { 482 Inode* inode = (Inode*)_node->private_node; 483 return inode->CheckPermissions(accessMode); 484 } 485 486 487 static status_t 488 btrfs_read_link(fs_volume* _volume, fs_vnode* _node, char* buffer, 489 size_t* _bufferSize) 490 { 491 Inode* inode = (Inode*)_node->private_node; 492 493 if (!inode->IsSymLink()) 494 return B_BAD_VALUE; 495 496 status_t result = inode->ReadAt(0, reinterpret_cast<uint8*>(buffer), 497 _bufferSize); 498 if (result != B_OK) 499 return result; 500 501 *_bufferSize = inode->Size(); 502 return B_OK; 503 } 504 505 506 status_t 507 btrfs_unlink(fs_volume* _volume, fs_vnode* _directory, const char* name) 508 { 509 if (!strcmp(name, "..") || !strcmp(name, ".")) 510 return B_NOT_ALLOWED; 511 512 Volume* volume = (Volume*)_volume->private_volume; 513 Inode* directory = (Inode*)_directory->private_node; 514 515 status_t status = directory->CheckPermissions(W_OK); 516 if (status < B_OK) 517 return status; 518 519 Transaction transaction(volume); 520 BTree::Path path(volume->FSTree()); 521 522 ino_t id; 523 status = DirectoryIterator(directory).Lookup(name, strlen(name), &id); 524 if (status != B_OK) 525 return status; 526 527 Inode inode(volume, id); 528 status = inode.InitCheck(); 529 if (status != B_OK) 530 return status; 531 532 status = inode.Remove(transaction, &path); 533 if (status != B_OK) 534 return status; 535 status = inode.Dereference(transaction, &path, directory->ID(), name); 536 if (status != B_OK) 537 return status; 538 539 entry_cache_remove(volume->ID(), directory->ID(), name); 540 541 status = transaction.Done(); 542 if (status == B_OK) 543 notify_entry_removed(volume->ID(), directory->ID(), name, id); 544 else 545 entry_cache_add(volume->ID(), directory->ID(), name, id); 546 547 return status; 548 } 549 550 551 // #pragma mark - Directory functions 552 553 554 static status_t 555 btrfs_create_dir(fs_volume* _volume, fs_vnode* _directory, const char* name, 556 int mode) 557 { 558 Volume* volume = (Volume*)_volume->private_volume; 559 Inode* directory = (Inode*)_directory->private_node; 560 BTree::Path path(volume->FSTree()); 561 562 if (volume->IsReadOnly()) 563 return B_READ_ONLY_DEVICE; 564 565 if (!directory->IsDirectory()) 566 return B_NOT_A_DIRECTORY; 567 568 status_t status = directory->CheckPermissions(W_OK); 569 if (status < B_OK) 570 return status; 571 572 Transaction transaction(volume); 573 ino_t id = volume->GetNextInodeID(); 574 mode = S_DIRECTORY | (mode & S_IUMSK); 575 Inode* inode = Inode::Create(transaction, id, directory, mode); 576 if (inode == NULL) 577 return B_NO_MEMORY; 578 579 status = inode->Insert(transaction, &path); 580 if (status != B_OK) 581 return status; 582 583 status = inode->MakeReference(transaction, &path, directory, name, mode); 584 if (status != B_OK) 585 return status; 586 587 put_vnode(volume->FSVolume(), inode->ID()); 588 entry_cache_add(volume->ID(), directory->ID(), name, inode->ID()); 589 590 status = transaction.Done(); 591 if (status == B_OK) 592 notify_entry_created(volume->ID(), directory->ID(), name, inode->ID()); 593 else 594 entry_cache_remove(volume->ID(), directory->ID(), name); 595 596 return status; 597 } 598 599 600 static status_t 601 btrfs_remove_dir(fs_volume* _volume, fs_vnode* _directory, const char* name) 602 { 603 Volume* volume = (Volume*)_volume->private_volume; 604 Inode* directory = (Inode*)_directory->private_node; 605 606 Transaction transaction(volume); 607 BTree::Path path(volume->FSTree()); 608 609 ino_t id; 610 status_t status = DirectoryIterator(directory).Lookup(name, strlen(name), 611 &id); 612 if (status != B_OK) 613 return status; 614 615 Inode inode(volume, id); 616 status = inode.InitCheck(); 617 if (status != B_OK) 618 return status; 619 620 status = inode.Remove(transaction, &path); 621 if (status != B_OK) 622 return status; 623 status = inode.Dereference(transaction, &path, directory->ID(), name); 624 if (status != B_OK) 625 return status; 626 627 entry_cache_remove(volume->ID(), directory->ID(), name); 628 entry_cache_remove(volume->ID(), id, ".."); 629 630 status = transaction.Done(); 631 if (status == B_OK) 632 notify_entry_removed(volume->ID(), directory->ID(), name, id); 633 else { 634 entry_cache_add(volume->ID(), directory->ID(), name, id); 635 entry_cache_add(volume->ID(), id, "..", id); 636 } 637 638 return status; 639 } 640 641 642 static status_t 643 btrfs_open_dir(fs_volume* /*_volume*/, fs_vnode* _node, void** _cookie) 644 { 645 Inode* inode = (Inode*)_node->private_node; 646 status_t status = inode->CheckPermissions(R_OK); 647 if (status < B_OK) 648 return status; 649 650 if (!inode->IsDirectory()) 651 return B_NOT_A_DIRECTORY; 652 653 DirectoryIterator* iterator = new(std::nothrow) DirectoryIterator(inode); 654 if (iterator == NULL || iterator->InitCheck() != B_OK) { 655 delete iterator; 656 return B_NO_MEMORY; 657 } 658 659 *_cookie = iterator; 660 return B_OK; 661 } 662 663 664 static status_t 665 btrfs_read_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie, 666 struct dirent* dirent, size_t bufferSize, uint32* _num) 667 { 668 DirectoryIterator* iterator = (DirectoryIterator*)_cookie; 669 Volume* volume = (Volume*)_volume->private_volume; 670 671 uint32 maxCount = *_num; 672 uint32 count = 0; 673 674 while (count < maxCount && bufferSize > sizeof(struct dirent)) { 675 ino_t id; 676 size_t length = bufferSize - sizeof(struct dirent) + 1; 677 678 status_t status = iterator->GetNext(dirent->d_name, &length, 679 &id); 680 681 if (status == B_ENTRY_NOT_FOUND) 682 break; 683 684 if (status == B_BUFFER_OVERFLOW) { 685 // the remaining name buffer length was too small 686 if (count == 0) 687 return B_BUFFER_OVERFLOW; 688 break; 689 } 690 691 if (status != B_OK) 692 return status; 693 694 dirent->d_dev = volume->ID(); 695 dirent->d_ino = id; 696 dirent->d_reclen = sizeof(struct dirent) + length; 697 698 bufferSize -= dirent->d_reclen; 699 dirent = (struct dirent*)((uint8*)dirent + dirent->d_reclen); 700 count++; 701 } 702 703 *_num = count; 704 return B_OK; 705 } 706 707 708 static status_t 709 btrfs_rewind_dir(fs_volume* /*_volume*/, fs_vnode* /*node*/, void* _cookie) 710 { 711 DirectoryIterator* iterator = (DirectoryIterator*)_cookie; 712 713 return iterator->Rewind(); 714 } 715 716 717 static status_t 718 btrfs_close_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, 719 void * /*_cookie*/) 720 { 721 return B_OK; 722 } 723 724 725 static status_t 726 btrfs_free_dir_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie) 727 { 728 delete (DirectoryIterator*)_cookie; 729 return B_OK; 730 } 731 732 733 static status_t 734 btrfs_open_attr_dir(fs_volume* _volume, fs_vnode* _node, void** _cookie) 735 { 736 Inode* inode = (Inode*)_node->private_node; 737 TRACE("%s()\n", __FUNCTION__); 738 739 // on directories too ? 740 if (!inode->IsFile()) 741 return EINVAL; 742 743 AttributeIterator* iterator = new(std::nothrow) AttributeIterator(inode); 744 if (iterator == NULL || iterator->InitCheck() != B_OK) { 745 delete iterator; 746 return B_NO_MEMORY; 747 } 748 749 *_cookie = iterator; 750 return B_OK; 751 } 752 753 754 static status_t 755 btrfs_close_attr_dir(fs_volume* _volume, fs_vnode* _node, void* cookie) 756 { 757 TRACE("%s()\n", __FUNCTION__); 758 return B_OK; 759 } 760 761 762 static status_t 763 btrfs_free_attr_dir_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie) 764 { 765 TRACE("%s()\n", __FUNCTION__); 766 delete (AttributeIterator*)_cookie; 767 return B_OK; 768 } 769 770 771 static status_t 772 btrfs_read_attr_dir(fs_volume* _volume, fs_vnode* _node, 773 void* _cookie, struct dirent* dirent, size_t bufferSize, uint32* _num) 774 { 775 TRACE("%s()\n", __FUNCTION__); 776 AttributeIterator* iterator = (AttributeIterator*)_cookie; 777 778 size_t length = bufferSize; 779 status_t status = iterator->GetNext(dirent->d_name, &length); 780 if (status == B_ENTRY_NOT_FOUND) { 781 *_num = 0; 782 return B_OK; 783 } 784 785 if (status != B_OK) 786 return status; 787 788 Volume* volume = (Volume*)_volume->private_volume; 789 dirent->d_dev = volume->ID(); 790 dirent->d_reclen = sizeof(struct dirent) + length; 791 *_num = 1; 792 793 return B_OK; 794 } 795 796 797 static status_t 798 btrfs_rewind_attr_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie) 799 { 800 AttributeIterator* iterator = (AttributeIterator*)_cookie; 801 return iterator->Rewind(); 802 } 803 804 805 /* attribute operations */ 806 static status_t 807 btrfs_create_attr(fs_volume* _volume, fs_vnode* _node, 808 const char* name, uint32 type, int openMode, void** _cookie) 809 { 810 return EROFS; 811 } 812 813 814 static status_t 815 btrfs_open_attr(fs_volume* _volume, fs_vnode* _node, const char* name, 816 int openMode, void** _cookie) 817 { 818 TRACE("%s()\n", __FUNCTION__); 819 820 Inode* inode = (Inode*)_node->private_node; 821 Attribute attribute(inode); 822 823 return attribute.Open(name, openMode, (attr_cookie**)_cookie); 824 } 825 826 827 static status_t 828 btrfs_close_attr(fs_volume* _volume, fs_vnode* _node, 829 void* cookie) 830 { 831 return B_OK; 832 } 833 834 835 static status_t 836 btrfs_free_attr_cookie(fs_volume* _volume, fs_vnode* _node, 837 void* cookie) 838 { 839 delete (attr_cookie*)cookie; 840 return B_OK; 841 } 842 843 844 static status_t 845 btrfs_read_attr(fs_volume* _volume, fs_vnode* _node, void* _cookie, 846 off_t pos, void* buffer, size_t* _length) 847 { 848 TRACE("%s()\n", __FUNCTION__); 849 850 attr_cookie* cookie = (attr_cookie*)_cookie; 851 Inode* inode = (Inode*)_node->private_node; 852 853 Attribute attribute(inode, cookie); 854 855 return attribute.Read(cookie, pos, (uint8*)buffer, _length); 856 } 857 858 859 static status_t 860 btrfs_write_attr(fs_volume* _volume, fs_vnode* _node, void* cookie, 861 off_t pos, const void* buffer, size_t* length) 862 { 863 return EROFS; 864 } 865 866 867 static status_t 868 btrfs_read_attr_stat(fs_volume* _volume, fs_vnode* _node, 869 void* _cookie, struct stat* stat) 870 { 871 attr_cookie* cookie = (attr_cookie*)_cookie; 872 Inode* inode = (Inode*)_node->private_node; 873 874 Attribute attribute(inode, cookie); 875 876 return attribute.Stat(*stat); 877 } 878 879 880 static status_t 881 btrfs_write_attr_stat(fs_volume* _volume, fs_vnode* _node, 882 void* cookie, const struct stat* stat, int statMask) 883 { 884 return EROFS; 885 } 886 887 888 static status_t 889 btrfs_rename_attr(fs_volume* _volume, fs_vnode* fromVnode, 890 const char* fromName, fs_vnode* toVnode, const char* toName) 891 { 892 return EROFS; 893 } 894 895 896 static status_t 897 btrfs_remove_attr(fs_volume* _volume, fs_vnode* vnode, 898 const char* name) 899 { 900 return EROFS; 901 } 902 903 static uint32 904 btrfs_get_supported_operations(partition_data* partition, uint32 mask) 905 { 906 // TODO: We should at least check the partition size. 907 return B_DISK_SYSTEM_SUPPORTS_INITIALIZING 908 | B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME 909 // | B_DISK_SYSTEM_SUPPORTS_WRITING 910 ; 911 } 912 913 914 static status_t 915 btrfs_initialize(int fd, partition_id partitionID, const char* name, 916 const char* parameterString, off_t partitionSize, disk_job_id job) 917 { 918 // check name 919 status_t status = check_volume_name(name); 920 if (status != B_OK) 921 return status; 922 923 // parse parameters 924 initialize_parameters parameters; 925 status = parse_initialize_parameters(parameterString, parameters); 926 if (status != B_OK) 927 return status; 928 929 update_disk_device_job_progress(job, 0); 930 931 // initialize the volume 932 Volume volume(NULL); 933 status = volume.Initialize(fd, name, parameters.blockSize, 934 parameters.sectorSize); 935 if (status < B_OK) { 936 INFORM("Initializing volume failed: %s\n", strerror(status)); 937 return status; 938 } 939 940 // rescan partition 941 status = scan_partition(partitionID); 942 if (status != B_OK) 943 return status; 944 945 update_disk_device_job_progress(job, 1); 946 947 // print some info, if desired 948 if (parameters.verbose) { 949 btrfs_super_block super = volume.SuperBlock(); 950 951 INFORM("Disk was initialized successfully.\n"); 952 INFORM("\tlabel: \"%s\"\n", super.label); 953 INFORM("\tblock size: %u bytes\n", (unsigned)super.BlockSize()); 954 INFORM("\tsector size: %u bytes\n", (unsigned)super.SectorSize()); 955 } 956 957 return B_OK; 958 } 959 960 961 static status_t 962 btrfs_uninitialize(int fd, partition_id partitionID, off_t partitionSize, 963 uint32 blockSize, disk_job_id job) 964 { 965 if (blockSize == 0) 966 return B_BAD_VALUE; 967 968 update_disk_device_job_progress(job, 0.0); 969 970 // just overwrite the superblock 971 btrfs_super_block superBlock; 972 memset(&superBlock, 0, sizeof(superBlock)); 973 974 if (write_pos(fd, BTRFS_SUPER_BLOCK_OFFSET, &superBlock, 975 sizeof(superBlock)) < 0) 976 return errno; 977 978 update_disk_device_job_progress(job, 1.0); 979 980 return B_OK; 981 } 982 983 // #pragma mark - 984 985 986 static status_t 987 btrfs_std_ops(int32 op, ...) 988 { 989 switch (op) { 990 case B_MODULE_INIT: 991 init_debugging(); 992 993 return B_OK; 994 case B_MODULE_UNINIT: 995 exit_debugging(); 996 997 return B_OK; 998 999 default: 1000 return B_ERROR; 1001 } 1002 } 1003 1004 1005 fs_volume_ops gBtrfsVolumeOps = { 1006 &btrfs_unmount, 1007 &btrfs_read_fs_info, 1008 NULL, // write_fs_info() 1009 NULL, // fs_sync, 1010 &btrfs_get_vnode, 1011 }; 1012 1013 1014 fs_vnode_ops gBtrfsVnodeOps = { 1015 /* vnode operations */ 1016 &btrfs_lookup, 1017 NULL, // btrfs_get_vnode_name - optional, and we can't do better than the 1018 // fallback implementation, so leave as NULL. 1019 &btrfs_put_vnode, 1020 NULL, // btrfs_remove_vnode, 1021 1022 /* VM file access */ 1023 &btrfs_can_page, 1024 &btrfs_read_pages, 1025 NULL, // btrfs_write_pages, 1026 1027 NULL, // io() 1028 NULL, // cancel_io() 1029 1030 &btrfs_get_file_map, 1031 1032 &btrfs_ioctl, 1033 NULL, 1034 NULL, // fs_select 1035 NULL, // fs_deselect 1036 NULL, // fs_fsync, 1037 1038 &btrfs_read_link, 1039 NULL, // fs_create_symlink, 1040 1041 NULL, // fs_link, 1042 &btrfs_unlink, 1043 NULL, // fs_rename, 1044 1045 &btrfs_access, 1046 &btrfs_read_stat, 1047 NULL, // fs_write_stat, 1048 NULL, // fs_preallocate 1049 1050 /* file operations */ 1051 NULL, // fs_create, 1052 &btrfs_open, 1053 &btrfs_close, 1054 &btrfs_free_cookie, 1055 &btrfs_read, 1056 NULL, // fs_write, 1057 1058 /* directory operations */ 1059 &btrfs_create_dir, 1060 &btrfs_remove_dir, 1061 &btrfs_open_dir, 1062 &btrfs_close_dir, 1063 &btrfs_free_dir_cookie, 1064 &btrfs_read_dir, 1065 &btrfs_rewind_dir, 1066 1067 /* attribute directory operations */ 1068 &btrfs_open_attr_dir, 1069 &btrfs_close_attr_dir, 1070 &btrfs_free_attr_dir_cookie, 1071 &btrfs_read_attr_dir, 1072 &btrfs_rewind_attr_dir, 1073 1074 /* attribute operations */ 1075 &btrfs_create_attr, 1076 &btrfs_open_attr, 1077 &btrfs_close_attr, 1078 &btrfs_free_attr_cookie, 1079 &btrfs_read_attr, 1080 &btrfs_write_attr, 1081 &btrfs_read_attr_stat, 1082 &btrfs_write_attr_stat, 1083 &btrfs_rename_attr, 1084 &btrfs_remove_attr, 1085 }; 1086 1087 1088 static file_system_module_info sBtrfsFileSystem = { 1089 { 1090 "file_systems/btrfs" B_CURRENT_FS_API_VERSION, 1091 0, 1092 btrfs_std_ops, 1093 }, 1094 1095 "btrfs", // short_name 1096 "Btrfs File System", // pretty_name 1097 1098 // DDM flags 1099 0 1100 | B_DISK_SYSTEM_SUPPORTS_INITIALIZING 1101 | B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME 1102 // | B_DISK_SYSTEM_SUPPORTS_WRITING 1103 , 1104 1105 // scanning 1106 btrfs_identify_partition, 1107 btrfs_scan_partition, 1108 btrfs_free_identify_partition_cookie, 1109 NULL, // free_partition_content_cookie() 1110 1111 &btrfs_mount, 1112 1113 1114 /* capability querying operations */ 1115 &btrfs_get_supported_operations, 1116 1117 NULL, // validate_resize 1118 NULL, // validate_move 1119 NULL, // validate_set_content_name 1120 NULL, // validate_set_content_parameters 1121 NULL, // validate_initialize, 1122 1123 /* shadow partition modification */ 1124 NULL, // shadow_changed 1125 1126 /* writing */ 1127 NULL, // defragment 1128 NULL, // repair 1129 NULL, // resize 1130 NULL, // move 1131 NULL, // set_content_name 1132 NULL, // set_content_parameters 1133 btrfs_initialize, 1134 btrfs_uninitialize 1135 }; 1136 1137 1138 module_info* modules[] = { 1139 (module_info*)&sBtrfsFileSystem, 1140 NULL, 1141 }; 1142