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