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 size_t 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 ((off_t)size <= vecs[index - 1].length || offset >= inode->Size()) { 325 // We're done! 326 *_count = index; 327 TRACE("btrfs_get_file_map for inode %" B_PRIdINO "\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: %" B_PRIu32 "\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 Volume* volume = (Volume*)_volume->private_volume; 524 525 uint32 maxCount = *_num; 526 uint32 count = 0; 527 528 while (count < maxCount && bufferSize > sizeof(struct dirent)) { 529 ino_t id; 530 size_t length = bufferSize - sizeof(struct dirent) + 1; 531 532 status_t status = iterator->GetNext(dirent->d_name, &length, 533 &id); 534 535 if (status == B_ENTRY_NOT_FOUND) 536 break; 537 538 if (status == B_BUFFER_OVERFLOW) { 539 // the remaining name buffer length was too small 540 if (count == 0) 541 return B_BUFFER_OVERFLOW; 542 break; 543 } 544 545 if (status != B_OK) 546 return status; 547 548 dirent->d_dev = volume->ID(); 549 dirent->d_ino = id; 550 dirent->d_reclen = sizeof(struct dirent) + length; 551 552 bufferSize -= dirent->d_reclen; 553 dirent = (struct dirent*)((uint8*)dirent + dirent->d_reclen); 554 count++; 555 } 556 557 *_num = count; 558 return B_OK; 559 } 560 561 562 static status_t 563 btrfs_rewind_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void *_cookie) 564 { 565 DirectoryIterator* iterator = (DirectoryIterator*)_cookie; 566 567 return iterator->Rewind(); 568 } 569 570 571 static status_t 572 btrfs_close_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void * /*_cookie*/) 573 { 574 return B_OK; 575 } 576 577 578 static status_t 579 btrfs_free_dir_cookie(fs_volume *_volume, fs_vnode *_node, void *_cookie) 580 { 581 delete (DirectoryIterator*)_cookie; 582 return B_OK; 583 } 584 585 586 static status_t 587 btrfs_open_attr_dir(fs_volume *_volume, fs_vnode *_node, void **_cookie) 588 { 589 Inode* inode = (Inode*)_node->private_node; 590 TRACE("%s()\n", __FUNCTION__); 591 592 // on directories too ? 593 if (!inode->IsFile()) 594 return EINVAL; 595 596 AttributeIterator* iterator = new(std::nothrow) AttributeIterator(inode); 597 if (iterator == NULL || iterator->InitCheck() != B_OK) { 598 delete iterator; 599 return B_NO_MEMORY; 600 } 601 602 *_cookie = iterator; 603 return B_OK; 604 } 605 606 607 static status_t 608 btrfs_close_attr_dir(fs_volume* _volume, fs_vnode* _node, void* cookie) 609 { 610 TRACE("%s()\n", __FUNCTION__); 611 return B_OK; 612 } 613 614 615 static status_t 616 btrfs_free_attr_dir_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie) 617 { 618 TRACE("%s()\n", __FUNCTION__); 619 delete (AttributeIterator*)_cookie; 620 return B_OK; 621 } 622 623 624 static status_t 625 btrfs_read_attr_dir(fs_volume* _volume, fs_vnode* _node, 626 void* _cookie, struct dirent* dirent, size_t bufferSize, 627 uint32* _num) 628 { 629 TRACE("%s()\n", __FUNCTION__); 630 AttributeIterator* iterator = (AttributeIterator*)_cookie; 631 632 size_t length = bufferSize; 633 status_t status = iterator->GetNext(dirent->d_name, &length); 634 if (status == B_ENTRY_NOT_FOUND) { 635 *_num = 0; 636 return B_OK; 637 } else if (status != B_OK) 638 return status; 639 640 Volume* volume = (Volume*)_volume->private_volume; 641 dirent->d_dev = volume->ID(); 642 dirent->d_reclen = sizeof(struct dirent) + length; 643 *_num = 1; 644 645 return B_OK; 646 } 647 648 649 static status_t 650 btrfs_rewind_attr_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie) 651 { 652 AttributeIterator* iterator = (AttributeIterator*)_cookie; 653 return iterator->Rewind(); 654 } 655 656 657 /* attribute operations */ 658 static status_t 659 btrfs_create_attr(fs_volume* _volume, fs_vnode* _node, 660 const char* name, uint32 type, int openMode, void** _cookie) 661 { 662 return EROFS; 663 } 664 665 666 static status_t 667 btrfs_open_attr(fs_volume* _volume, fs_vnode* _node, const char* name, 668 int openMode, void** _cookie) 669 { 670 TRACE("%s()\n", __FUNCTION__); 671 672 Inode* inode = (Inode*)_node->private_node; 673 Attribute attribute(inode); 674 675 return attribute.Open(name, openMode, (attr_cookie**)_cookie); 676 } 677 678 679 static status_t 680 btrfs_close_attr(fs_volume* _volume, fs_vnode* _node, 681 void* cookie) 682 { 683 return B_OK; 684 } 685 686 687 static status_t 688 btrfs_free_attr_cookie(fs_volume* _volume, fs_vnode* _node, 689 void* cookie) 690 { 691 delete (attr_cookie*)cookie; 692 return B_OK; 693 } 694 695 696 static status_t 697 btrfs_read_attr(fs_volume* _volume, fs_vnode* _node, void* _cookie, 698 off_t pos, void* buffer, size_t* _length) 699 { 700 TRACE("%s()\n", __FUNCTION__); 701 702 attr_cookie* cookie = (attr_cookie*)_cookie; 703 Inode* inode = (Inode*)_node->private_node; 704 705 Attribute attribute(inode, cookie); 706 707 return attribute.Read(cookie, pos, (uint8*)buffer, _length); 708 } 709 710 711 static status_t 712 btrfs_write_attr(fs_volume* _volume, fs_vnode* _node, void* cookie, 713 off_t pos, const void* buffer, size_t* length) 714 { 715 return EROFS; 716 } 717 718 719 720 static status_t 721 btrfs_read_attr_stat(fs_volume* _volume, fs_vnode* _node, 722 void* _cookie, struct stat* stat) 723 { 724 attr_cookie* cookie = (attr_cookie*)_cookie; 725 Inode* inode = (Inode*)_node->private_node; 726 727 Attribute attribute(inode, cookie); 728 729 return attribute.Stat(*stat); 730 } 731 732 733 static status_t 734 btrfs_write_attr_stat(fs_volume* _volume, fs_vnode* _node, 735 void* cookie, const struct stat* stat, int statMask) 736 { 737 return EROFS; 738 } 739 740 741 static status_t 742 btrfs_rename_attr(fs_volume* _volume, fs_vnode* fromVnode, 743 const char* fromName, fs_vnode* toVnode, const char* toName) 744 { 745 return EROFS; 746 } 747 748 749 static status_t 750 btrfs_remove_attr(fs_volume* _volume, fs_vnode* vnode, 751 const char* name) 752 { 753 return EROFS; 754 } 755 756 757 fs_volume_ops gBtrfsVolumeOps = { 758 &btrfs_unmount, 759 &btrfs_read_fs_info, 760 NULL, // write_fs_info() 761 NULL, // fs_sync, 762 &btrfs_get_vnode, 763 }; 764 765 766 fs_vnode_ops gBtrfsVnodeOps = { 767 /* vnode operations */ 768 &btrfs_lookup, 769 NULL, 770 &btrfs_put_vnode, 771 NULL, // btrfs_remove_vnode, 772 773 /* VM file access */ 774 &btrfs_can_page, 775 &btrfs_read_pages, 776 NULL, // btrfs_write_pages, 777 778 NULL, // io() 779 NULL, // cancel_io() 780 781 &btrfs_get_file_map, 782 783 &btrfs_ioctl, 784 NULL, 785 NULL, // fs_select 786 NULL, // fs_deselect 787 NULL, // fs_fsync, 788 789 &btrfs_read_link, 790 NULL, // fs_create_symlink, 791 792 NULL, // fs_link, 793 NULL, // fs_unlink, 794 NULL, // fs_rename, 795 796 &btrfs_access, 797 &btrfs_read_stat, 798 NULL, // fs_write_stat, 799 NULL, // fs_preallocate 800 801 /* file operations */ 802 NULL, // fs_create, 803 &btrfs_open, 804 &btrfs_close, 805 &btrfs_free_cookie, 806 &btrfs_read, 807 NULL, // fs_write, 808 809 /* directory operations */ 810 NULL, // fs_create_dir, 811 NULL, // fs_remove_dir, 812 &btrfs_open_dir, 813 &btrfs_close_dir, 814 &btrfs_free_dir_cookie, 815 &btrfs_read_dir, 816 &btrfs_rewind_dir, 817 818 /* attribute directory operations */ 819 &btrfs_open_attr_dir, 820 &btrfs_close_attr_dir, 821 &btrfs_free_attr_dir_cookie, 822 &btrfs_read_attr_dir, 823 &btrfs_rewind_attr_dir, 824 825 /* attribute operations */ 826 &btrfs_create_attr, 827 &btrfs_open_attr, 828 &btrfs_close_attr, 829 &btrfs_free_attr_cookie, 830 &btrfs_read_attr, 831 &btrfs_write_attr, 832 &btrfs_read_attr_stat, 833 &btrfs_write_attr_stat, 834 &btrfs_rename_attr, 835 &btrfs_remove_attr, 836 }; 837 838 839 static file_system_module_info sBtrfsFileSystem = { 840 { 841 "file_systems/btrfs" B_CURRENT_FS_API_VERSION, 842 0, 843 NULL, 844 }, 845 846 "btrfs", // short_name 847 "btrfs File System", // pretty_name 848 0, // DDM flags 849 850 // scanning 851 btrfs_identify_partition, 852 btrfs_scan_partition, 853 btrfs_free_identify_partition_cookie, 854 NULL, // free_partition_content_cookie() 855 856 &btrfs_mount, 857 858 NULL, 859 }; 860 861 862 module_info *modules[] = { 863 (module_info *)&sBtrfsFileSystem, 864 NULL, 865 }; 866