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 return inode->ReadAt(0, (uint8*)buffer, _bufferSize); 493 } 494 495 496 status_t 497 btrfs_unlink(fs_volume* _volume, fs_vnode* _directory, const char* name) 498 { 499 if (!strcmp(name, "..") || !strcmp(name, ".")) 500 return B_NOT_ALLOWED; 501 502 Volume* volume = (Volume*)_volume->private_volume; 503 Inode* directory = (Inode*)_directory->private_node; 504 505 status_t status = directory->CheckPermissions(W_OK); 506 if (status < B_OK) 507 return status; 508 509 Transaction transaction(volume); 510 BTree::Path path(volume->FSTree()); 511 512 ino_t id; 513 status = DirectoryIterator(directory).Lookup(name, strlen(name), &id); 514 if (status != B_OK) 515 return status; 516 517 Inode inode(volume, id); 518 status = inode.InitCheck(); 519 if (status != B_OK) 520 return status; 521 522 status = inode.Remove(transaction, &path); 523 if (status != B_OK) 524 return status; 525 status = inode.Dereference(transaction, &path, directory->ID(), name); 526 if (status != B_OK) 527 return status; 528 529 entry_cache_remove(volume->ID(), directory->ID(), name); 530 531 status = transaction.Done(); 532 if (status == B_OK) 533 notify_entry_removed(volume->ID(), directory->ID(), name, id); 534 else 535 entry_cache_add(volume->ID(), directory->ID(), name, id); 536 537 return status; 538 } 539 540 541 // #pragma mark - Directory functions 542 543 544 static status_t 545 btrfs_create_dir(fs_volume* _volume, fs_vnode* _directory, const char* name, 546 int mode) 547 { 548 Volume* volume = (Volume*)_volume->private_volume; 549 Inode* directory = (Inode*)_directory->private_node; 550 BTree::Path path(volume->FSTree()); 551 552 if (volume->IsReadOnly()) 553 return B_READ_ONLY_DEVICE; 554 555 if (!directory->IsDirectory()) 556 return B_NOT_A_DIRECTORY; 557 558 status_t status = directory->CheckPermissions(W_OK); 559 if (status < B_OK) 560 return status; 561 562 Transaction transaction(volume); 563 ino_t id = volume->GetNextInodeID(); 564 mode = S_DIRECTORY | (mode & S_IUMSK); 565 Inode* inode = Inode::Create(transaction, id, directory, mode); 566 if (inode == NULL) 567 return B_NO_MEMORY; 568 569 status = inode->Insert(transaction, &path); 570 if (status != B_OK) 571 return status; 572 573 status = inode->MakeReference(transaction, &path, directory, name, mode); 574 if (status != B_OK) 575 return status; 576 577 put_vnode(volume->FSVolume(), inode->ID()); 578 entry_cache_add(volume->ID(), directory->ID(), name, inode->ID()); 579 580 status = transaction.Done(); 581 if (status == B_OK) 582 notify_entry_created(volume->ID(), directory->ID(), name, inode->ID()); 583 else 584 entry_cache_remove(volume->ID(), directory->ID(), name); 585 586 return status; 587 } 588 589 590 static status_t 591 btrfs_remove_dir(fs_volume* _volume, fs_vnode* _directory, const char* name) 592 { 593 Volume* volume = (Volume*)_volume->private_volume; 594 Inode* directory = (Inode*)_directory->private_node; 595 596 Transaction transaction(volume); 597 BTree::Path path(volume->FSTree()); 598 599 ino_t id; 600 status_t status = DirectoryIterator(directory).Lookup(name, strlen(name), 601 &id); 602 if (status != B_OK) 603 return status; 604 605 Inode inode(volume, id); 606 status = inode.InitCheck(); 607 if (status != B_OK) 608 return status; 609 610 status = inode.Remove(transaction, &path); 611 if (status != B_OK) 612 return status; 613 status = inode.Dereference(transaction, &path, directory->ID(), name); 614 if (status != B_OK) 615 return status; 616 617 entry_cache_remove(volume->ID(), directory->ID(), name); 618 entry_cache_remove(volume->ID(), id, ".."); 619 620 status = transaction.Done(); 621 if (status == B_OK) 622 notify_entry_removed(volume->ID(), directory->ID(), name, id); 623 else { 624 entry_cache_add(volume->ID(), directory->ID(), name, id); 625 entry_cache_add(volume->ID(), id, "..", id); 626 } 627 628 return status; 629 } 630 631 632 static status_t 633 btrfs_open_dir(fs_volume* /*_volume*/, fs_vnode* _node, void** _cookie) 634 { 635 Inode* inode = (Inode*)_node->private_node; 636 status_t status = inode->CheckPermissions(R_OK); 637 if (status < B_OK) 638 return status; 639 640 if (!inode->IsDirectory()) 641 return B_NOT_A_DIRECTORY; 642 643 DirectoryIterator* iterator = new(std::nothrow) DirectoryIterator(inode); 644 if (iterator == NULL || iterator->InitCheck() != B_OK) { 645 delete iterator; 646 return B_NO_MEMORY; 647 } 648 649 *_cookie = iterator; 650 return B_OK; 651 } 652 653 654 static status_t 655 btrfs_read_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie, 656 struct dirent* dirent, size_t bufferSize, uint32* _num) 657 { 658 DirectoryIterator* iterator = (DirectoryIterator*)_cookie; 659 Volume* volume = (Volume*)_volume->private_volume; 660 661 uint32 maxCount = *_num; 662 uint32 count = 0; 663 664 while (count < maxCount && bufferSize > sizeof(struct dirent)) { 665 ino_t id; 666 size_t length = bufferSize - sizeof(struct dirent) + 1; 667 668 status_t status = iterator->GetNext(dirent->d_name, &length, 669 &id); 670 671 if (status == B_ENTRY_NOT_FOUND) 672 break; 673 674 if (status == B_BUFFER_OVERFLOW) { 675 // the remaining name buffer length was too small 676 if (count == 0) 677 return B_BUFFER_OVERFLOW; 678 break; 679 } 680 681 if (status != B_OK) 682 return status; 683 684 dirent->d_dev = volume->ID(); 685 dirent->d_ino = id; 686 dirent->d_reclen = sizeof(struct dirent) + length; 687 688 bufferSize -= dirent->d_reclen; 689 dirent = (struct dirent*)((uint8*)dirent + dirent->d_reclen); 690 count++; 691 } 692 693 *_num = count; 694 return B_OK; 695 } 696 697 698 static status_t 699 btrfs_rewind_dir(fs_volume* /*_volume*/, fs_vnode* /*node*/, void* _cookie) 700 { 701 DirectoryIterator* iterator = (DirectoryIterator*)_cookie; 702 703 return iterator->Rewind(); 704 } 705 706 707 static status_t 708 btrfs_close_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, 709 void * /*_cookie*/) 710 { 711 return B_OK; 712 } 713 714 715 static status_t 716 btrfs_free_dir_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie) 717 { 718 delete (DirectoryIterator*)_cookie; 719 return B_OK; 720 } 721 722 723 static status_t 724 btrfs_open_attr_dir(fs_volume* _volume, fs_vnode* _node, void** _cookie) 725 { 726 Inode* inode = (Inode*)_node->private_node; 727 TRACE("%s()\n", __FUNCTION__); 728 729 // on directories too ? 730 if (!inode->IsFile()) 731 return EINVAL; 732 733 AttributeIterator* iterator = new(std::nothrow) AttributeIterator(inode); 734 if (iterator == NULL || iterator->InitCheck() != B_OK) { 735 delete iterator; 736 return B_NO_MEMORY; 737 } 738 739 *_cookie = iterator; 740 return B_OK; 741 } 742 743 744 static status_t 745 btrfs_close_attr_dir(fs_volume* _volume, fs_vnode* _node, void* cookie) 746 { 747 TRACE("%s()\n", __FUNCTION__); 748 return B_OK; 749 } 750 751 752 static status_t 753 btrfs_free_attr_dir_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie) 754 { 755 TRACE("%s()\n", __FUNCTION__); 756 delete (AttributeIterator*)_cookie; 757 return B_OK; 758 } 759 760 761 static status_t 762 btrfs_read_attr_dir(fs_volume* _volume, fs_vnode* _node, 763 void* _cookie, struct dirent* dirent, size_t bufferSize, uint32* _num) 764 { 765 TRACE("%s()\n", __FUNCTION__); 766 AttributeIterator* iterator = (AttributeIterator*)_cookie; 767 768 size_t length = bufferSize; 769 status_t status = iterator->GetNext(dirent->d_name, &length); 770 if (status == B_ENTRY_NOT_FOUND) { 771 *_num = 0; 772 return B_OK; 773 } 774 775 if (status != B_OK) 776 return status; 777 778 Volume* volume = (Volume*)_volume->private_volume; 779 dirent->d_dev = volume->ID(); 780 dirent->d_reclen = sizeof(struct dirent) + length; 781 *_num = 1; 782 783 return B_OK; 784 } 785 786 787 static status_t 788 btrfs_rewind_attr_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie) 789 { 790 AttributeIterator* iterator = (AttributeIterator*)_cookie; 791 return iterator->Rewind(); 792 } 793 794 795 /* attribute operations */ 796 static status_t 797 btrfs_create_attr(fs_volume* _volume, fs_vnode* _node, 798 const char* name, uint32 type, int openMode, void** _cookie) 799 { 800 return EROFS; 801 } 802 803 804 static status_t 805 btrfs_open_attr(fs_volume* _volume, fs_vnode* _node, const char* name, 806 int openMode, void** _cookie) 807 { 808 TRACE("%s()\n", __FUNCTION__); 809 810 Inode* inode = (Inode*)_node->private_node; 811 Attribute attribute(inode); 812 813 return attribute.Open(name, openMode, (attr_cookie**)_cookie); 814 } 815 816 817 static status_t 818 btrfs_close_attr(fs_volume* _volume, fs_vnode* _node, 819 void* cookie) 820 { 821 return B_OK; 822 } 823 824 825 static status_t 826 btrfs_free_attr_cookie(fs_volume* _volume, fs_vnode* _node, 827 void* cookie) 828 { 829 delete (attr_cookie*)cookie; 830 return B_OK; 831 } 832 833 834 static status_t 835 btrfs_read_attr(fs_volume* _volume, fs_vnode* _node, void* _cookie, 836 off_t pos, void* buffer, size_t* _length) 837 { 838 TRACE("%s()\n", __FUNCTION__); 839 840 attr_cookie* cookie = (attr_cookie*)_cookie; 841 Inode* inode = (Inode*)_node->private_node; 842 843 Attribute attribute(inode, cookie); 844 845 return attribute.Read(cookie, pos, (uint8*)buffer, _length); 846 } 847 848 849 static status_t 850 btrfs_write_attr(fs_volume* _volume, fs_vnode* _node, void* cookie, 851 off_t pos, const void* buffer, size_t* length) 852 { 853 return EROFS; 854 } 855 856 857 static status_t 858 btrfs_read_attr_stat(fs_volume* _volume, fs_vnode* _node, 859 void* _cookie, struct stat* stat) 860 { 861 attr_cookie* cookie = (attr_cookie*)_cookie; 862 Inode* inode = (Inode*)_node->private_node; 863 864 Attribute attribute(inode, cookie); 865 866 return attribute.Stat(*stat); 867 } 868 869 870 static status_t 871 btrfs_write_attr_stat(fs_volume* _volume, fs_vnode* _node, 872 void* cookie, const struct stat* stat, int statMask) 873 { 874 return EROFS; 875 } 876 877 878 static status_t 879 btrfs_rename_attr(fs_volume* _volume, fs_vnode* fromVnode, 880 const char* fromName, fs_vnode* toVnode, const char* toName) 881 { 882 return EROFS; 883 } 884 885 886 static status_t 887 btrfs_remove_attr(fs_volume* _volume, fs_vnode* vnode, 888 const char* name) 889 { 890 return EROFS; 891 } 892 893 static uint32 894 btrfs_get_supported_operations(partition_data* partition, uint32 mask) 895 { 896 // TODO: We should at least check the partition size. 897 return B_DISK_SYSTEM_SUPPORTS_INITIALIZING 898 | B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME 899 // | B_DISK_SYSTEM_SUPPORTS_WRITING 900 ; 901 } 902 903 904 static status_t 905 btrfs_initialize(int fd, partition_id partitionID, const char* name, 906 const char* parameterString, off_t partitionSize, disk_job_id job) 907 { 908 // check name 909 status_t status = check_volume_name(name); 910 if (status != B_OK) 911 return status; 912 913 // parse parameters 914 initialize_parameters parameters; 915 status = parse_initialize_parameters(parameterString, parameters); 916 if (status != B_OK) 917 return status; 918 919 update_disk_device_job_progress(job, 0); 920 921 // initialize the volume 922 Volume volume(NULL); 923 status = volume.Initialize(fd, name, parameters.blockSize, 924 parameters.sectorSize); 925 if (status < B_OK) { 926 INFORM("Initializing volume failed: %s\n", strerror(status)); 927 return status; 928 } 929 930 // rescan partition 931 status = scan_partition(partitionID); 932 if (status != B_OK) 933 return status; 934 935 update_disk_device_job_progress(job, 1); 936 937 // print some info, if desired 938 if (parameters.verbose) { 939 btrfs_super_block super = volume.SuperBlock(); 940 941 INFORM("Disk was initialized successfully.\n"); 942 INFORM("\tlabel: \"%s\"\n", super.label); 943 INFORM("\tblock size: %u bytes\n", (unsigned)super.BlockSize()); 944 INFORM("\tsector size: %u bytes\n", (unsigned)super.SectorSize()); 945 } 946 947 return B_OK; 948 } 949 950 951 static status_t 952 btrfs_uninitialize(int fd, partition_id partitionID, off_t partitionSize, 953 uint32 blockSize, disk_job_id job) 954 { 955 if (blockSize == 0) 956 return B_BAD_VALUE; 957 958 update_disk_device_job_progress(job, 0.0); 959 960 // just overwrite the superblock 961 btrfs_super_block superBlock; 962 memset(&superBlock, 0, sizeof(superBlock)); 963 964 if (write_pos(fd, BTRFS_SUPER_BLOCK_OFFSET, &superBlock, 965 sizeof(superBlock)) < 0) 966 return errno; 967 968 update_disk_device_job_progress(job, 1.0); 969 970 return B_OK; 971 } 972 973 // #pragma mark - 974 975 976 static status_t 977 btrfs_std_ops(int32 op, ...) 978 { 979 switch (op) { 980 case B_MODULE_INIT: 981 init_debugging(); 982 983 return B_OK; 984 case B_MODULE_UNINIT: 985 exit_debugging(); 986 987 return B_OK; 988 989 default: 990 return B_ERROR; 991 } 992 } 993 994 995 fs_volume_ops gBtrfsVolumeOps = { 996 &btrfs_unmount, 997 &btrfs_read_fs_info, 998 NULL, // write_fs_info() 999 NULL, // fs_sync, 1000 &btrfs_get_vnode, 1001 }; 1002 1003 1004 fs_vnode_ops gBtrfsVnodeOps = { 1005 /* vnode operations */ 1006 &btrfs_lookup, 1007 NULL, // btrfs_get_vnode_name - optional, and we can't do better than the 1008 // fallback implementation, so leave as NULL. 1009 &btrfs_put_vnode, 1010 NULL, // btrfs_remove_vnode, 1011 1012 /* VM file access */ 1013 &btrfs_can_page, 1014 &btrfs_read_pages, 1015 NULL, // btrfs_write_pages, 1016 1017 NULL, // io() 1018 NULL, // cancel_io() 1019 1020 &btrfs_get_file_map, 1021 1022 &btrfs_ioctl, 1023 NULL, 1024 NULL, // fs_select 1025 NULL, // fs_deselect 1026 NULL, // fs_fsync, 1027 1028 &btrfs_read_link, 1029 NULL, // fs_create_symlink, 1030 1031 NULL, // fs_link, 1032 &btrfs_unlink, 1033 NULL, // fs_rename, 1034 1035 &btrfs_access, 1036 &btrfs_read_stat, 1037 NULL, // fs_write_stat, 1038 NULL, // fs_preallocate 1039 1040 /* file operations */ 1041 NULL, // fs_create, 1042 &btrfs_open, 1043 &btrfs_close, 1044 &btrfs_free_cookie, 1045 &btrfs_read, 1046 NULL, // fs_write, 1047 1048 /* directory operations */ 1049 &btrfs_create_dir, 1050 &btrfs_remove_dir, 1051 &btrfs_open_dir, 1052 &btrfs_close_dir, 1053 &btrfs_free_dir_cookie, 1054 &btrfs_read_dir, 1055 &btrfs_rewind_dir, 1056 1057 /* attribute directory operations */ 1058 &btrfs_open_attr_dir, 1059 &btrfs_close_attr_dir, 1060 &btrfs_free_attr_dir_cookie, 1061 &btrfs_read_attr_dir, 1062 &btrfs_rewind_attr_dir, 1063 1064 /* attribute operations */ 1065 &btrfs_create_attr, 1066 &btrfs_open_attr, 1067 &btrfs_close_attr, 1068 &btrfs_free_attr_cookie, 1069 &btrfs_read_attr, 1070 &btrfs_write_attr, 1071 &btrfs_read_attr_stat, 1072 &btrfs_write_attr_stat, 1073 &btrfs_rename_attr, 1074 &btrfs_remove_attr, 1075 }; 1076 1077 1078 static file_system_module_info sBtrfsFileSystem = { 1079 { 1080 "file_systems/btrfs" B_CURRENT_FS_API_VERSION, 1081 0, 1082 btrfs_std_ops, 1083 }, 1084 1085 "btrfs", // short_name 1086 "Btrfs File System", // pretty_name 1087 1088 // DDM flags 1089 0 1090 | B_DISK_SYSTEM_SUPPORTS_INITIALIZING 1091 | B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME 1092 // | B_DISK_SYSTEM_SUPPORTS_WRITING 1093 , 1094 1095 // scanning 1096 btrfs_identify_partition, 1097 btrfs_scan_partition, 1098 btrfs_free_identify_partition_cookie, 1099 NULL, // free_partition_content_cookie() 1100 1101 &btrfs_mount, 1102 1103 1104 /* capability querying operations */ 1105 &btrfs_get_supported_operations, 1106 1107 NULL, // validate_resize 1108 NULL, // validate_move 1109 NULL, // validate_set_content_name 1110 NULL, // validate_set_content_parameters 1111 NULL, // validate_initialize, 1112 1113 /* shadow partition modification */ 1114 NULL, // shadow_changed 1115 1116 /* writing */ 1117 NULL, // defragment 1118 NULL, // repair 1119 NULL, // resize 1120 NULL, // move 1121 NULL, // set_content_name 1122 NULL, // set_content_parameters 1123 btrfs_initialize, 1124 btrfs_uninitialize 1125 }; 1126 1127 1128 module_info* modules[] = { 1129 (module_info*)&sBtrfsFileSystem, 1130 NULL, 1131 }; 1132