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