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