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 if (status == B_ENTRY_NOT_FOUND) 387 entry_cache_add_missing(volume->ID(), directory->ID(), name); 388 return status; 389 } 390 entry_cache_add(volume->ID(), directory->ID(), name, *_vnodeID); 391 392 return get_vnode(volume->FSVolume(), *_vnodeID, NULL); 393 } 394 395 396 static status_t 397 btrfs_ioctl(fs_volume* _volume, fs_vnode* _node, void* _cookie, uint32 cmd, 398 void* buffer, size_t bufferLength) 399 { 400 TRACE("ioctl: %" B_PRIu32 "\n", cmd); 401 402 /*Volume* volume = (Volume*)_volume->private_volume;*/ 403 return B_DEV_INVALID_IOCTL; 404 } 405 406 407 static status_t 408 btrfs_read_stat(fs_volume* _volume, fs_vnode* _node, struct stat* stat) 409 { 410 Inode* inode = (Inode*)_node->private_node; 411 412 stat->st_dev = inode->GetVolume()->ID(); 413 stat->st_ino = inode->ID(); 414 stat->st_nlink = 1; 415 stat->st_blksize = BTRFS_IO_SIZE; 416 417 stat->st_uid = inode->UserID(); 418 stat->st_gid = inode->GroupID(); 419 stat->st_mode = inode->Mode(); 420 stat->st_type = 0; 421 422 inode->GetAccessTime(stat->st_atim); 423 inode->GetModificationTime(stat->st_mtim); 424 inode->GetChangeTime(stat->st_ctim); 425 inode->GetCreationTime(stat->st_crtim); 426 427 stat->st_size = inode->Size(); 428 stat->st_blocks = (inode->Size() + 511) / 512; 429 430 return B_OK; 431 } 432 433 434 static status_t 435 btrfs_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat* stat, 436 uint32 mask) 437 { 438 FUNCTION(); 439 440 Volume* volume = (Volume*)_volume->private_volume; 441 Inode* inode = (Inode*)_node->private_node; 442 443 if (volume->IsReadOnly()) 444 return B_READ_ONLY_DEVICE; 445 446 btrfs_inode& node = inode->Node(); 447 bool updateTime = false; 448 uid_t uid = geteuid(); 449 450 bool isOwnerOrRoot = uid == 0 || uid == (uid_t)node.UserID(); 451 bool hasWriteAccess = inode->CheckPermissions(W_OK) == B_OK; 452 453 Transaction transaction(volume); 454 455 if ((mask & B_STAT_SIZE) != 0 && inode->Size() != stat->st_size) { 456 if (inode->IsDirectory()) 457 return B_IS_A_DIRECTORY; 458 if (!inode->IsFile()) 459 return B_BAD_VALUE; 460 if (!hasWriteAccess) 461 RETURN_ERROR(B_NOT_ALLOWED); 462 463 //TODO: implement file shrinking/growing 464 return B_NOT_SUPPORTED; 465 } 466 467 if ((mask & B_STAT_UID) != 0) { 468 if (uid != 0) 469 RETURN_ERROR(B_NOT_ALLOWED); 470 node.uid = B_HOST_TO_LENDIAN_INT32(stat->st_uid); 471 updateTime = true; 472 } 473 474 if ((mask & B_STAT_GID) != 0) { 475 if (!isOwnerOrRoot) 476 RETURN_ERROR(B_NOT_ALLOWED); 477 node.gid = B_HOST_TO_LENDIAN_INT32(stat->st_gid); 478 updateTime = true; 479 } 480 481 if ((mask & B_STAT_MODE) != 0) { 482 if (!isOwnerOrRoot) 483 RETURN_ERROR(B_NOT_ALLOWED); 484 PRINT(("original mode = %ld, stat->st_mode = %d\n", node.Mode(), 485 stat->st_mode)); 486 node.mode = B_HOST_TO_LENDIAN_INT32((node.Mode() & ~S_IUMSK) 487 | (stat->st_mode & S_IUMSK)); 488 updateTime = true; 489 } 490 491 if ((mask & B_STAT_CREATION_TIME) != 0) { 492 if (!isOwnerOrRoot && !hasWriteAccess) 493 RETURN_ERROR(B_NOT_ALLOWED); 494 btrfs_inode::SetTime(node.change_time, stat->st_crtim); 495 } 496 497 if ((mask & B_STAT_MODIFICATION_TIME) != 0) { 498 if (!isOwnerOrRoot && !hasWriteAccess) 499 RETURN_ERROR(B_NOT_ALLOWED); 500 btrfs_inode::SetTime(node.change_time, stat->st_mtim); 501 } 502 503 if ((mask & B_STAT_CHANGE_TIME) != 0 || updateTime) { 504 if (!isOwnerOrRoot && !hasWriteAccess) 505 RETURN_ERROR(B_NOT_ALLOWED); 506 if ((mask & B_STAT_CHANGE_TIME) == 0) { 507 uint64_t microseconds = real_time_clock_usecs(); 508 struct timespec t; 509 t.tv_sec = microseconds / 1000000; 510 t.tv_nsec = microseconds % 1000000; 511 btrfs_inode::SetTime(node.change_time, t); 512 } else 513 btrfs_inode::SetTime(node.change_time, stat->st_ctim); 514 } 515 516 status_t status = transaction.Done(); 517 if (status == B_OK) { 518 ino_t pid; 519 inode->FindParent(&pid); 520 notify_stat_changed(volume->ID(), pid, inode->ID(), mask); 521 } 522 523 return status; 524 } 525 526 527 static status_t 528 btrfs_open(fs_volume* /*_volume*/, fs_vnode* _node, int openMode, 529 void** _cookie) 530 { 531 Inode* inode = (Inode*)_node->private_node; 532 533 // opening a directory read-only is allowed, although you can't read 534 // any data from it. 535 if (inode->IsDirectory() && (openMode & O_RWMASK) != 0) 536 return B_IS_A_DIRECTORY; 537 538 status_t status = inode->CheckPermissions(open_mode_to_access(openMode) 539 | (openMode & O_TRUNC ? W_OK : 0)); 540 if (status != B_OK) 541 return status; 542 543 // Prepare the cookie 544 file_cookie* cookie = new(std::nothrow) file_cookie; 545 if (cookie == NULL) 546 return B_NO_MEMORY; 547 ObjectDeleter<file_cookie> cookieDeleter(cookie); 548 549 cookie->open_mode = openMode & BTRFS_OPEN_MODE_USER_MASK; 550 cookie->last_size = inode->Size(); 551 cookie->last_notification = system_time(); 552 553 if ((openMode & O_NOCACHE) != 0 && inode->FileCache() != NULL) { 554 // Disable the file cache, if requested? 555 status = file_cache_disable(inode->FileCache()); 556 if (status != B_OK) 557 return status; 558 } 559 560 cookieDeleter.Detach(); 561 *_cookie = cookie; 562 563 return B_OK; 564 } 565 566 567 status_t 568 btrfs_write(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos, 569 const void* buffer, size_t* _length) 570 { 571 Volume* volume = (Volume*)_volume->private_volume; 572 Inode* inode = (Inode*)_node->private_node; 573 574 if (volume->IsReadOnly()) 575 return B_READ_ONLY_DEVICE; 576 577 if (pos < 0) 578 return B_BAD_VALUE; 579 580 if (!inode->IsFile()) 581 return B_BAD_VALUE; 582 583 return B_NOT_SUPPORTED; 584 } 585 586 587 static status_t 588 btrfs_read(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos, 589 void* buffer, size_t* _length) 590 { 591 Inode* inode = (Inode*)_node->private_node; 592 593 if (!inode->IsFile()) { 594 *_length = 0; 595 return inode->IsDirectory() ? B_IS_A_DIRECTORY : B_BAD_VALUE; 596 } 597 598 return inode->ReadAt(pos, (uint8*)buffer, _length); 599 } 600 601 602 static status_t 603 btrfs_close(fs_volume* _volume, fs_vnode* _node, void* _cookie) 604 { 605 return B_OK; 606 } 607 608 609 static status_t 610 btrfs_free_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie) 611 { 612 file_cookie* cookie = (file_cookie*)_cookie; 613 Volume* volume = (Volume*)_volume->private_volume; 614 Inode* inode = (Inode*)_node->private_node; 615 616 if (inode->Size() != cookie->last_size) 617 notify_stat_changed(volume->ID(), -1, inode->ID(), B_STAT_SIZE); 618 619 delete cookie; 620 return B_OK; 621 } 622 623 624 static status_t 625 btrfs_access(fs_volume* _volume, fs_vnode* _node, int accessMode) 626 { 627 Inode* inode = (Inode*)_node->private_node; 628 return inode->CheckPermissions(accessMode); 629 } 630 631 632 static status_t 633 btrfs_read_link(fs_volume* _volume, fs_vnode* _node, char* buffer, 634 size_t* _bufferSize) 635 { 636 Inode* inode = (Inode*)_node->private_node; 637 638 if (!inode->IsSymLink()) 639 return B_BAD_VALUE; 640 641 status_t result = inode->ReadAt(0, reinterpret_cast<uint8*>(buffer), 642 _bufferSize); 643 if (result != B_OK) 644 return result; 645 646 *_bufferSize = inode->Size(); 647 return B_OK; 648 } 649 650 651 status_t 652 btrfs_unlink(fs_volume* _volume, fs_vnode* _directory, const char* name) 653 { 654 if (!strcmp(name, "..") || !strcmp(name, ".")) 655 return B_NOT_ALLOWED; 656 657 Volume* volume = (Volume*)_volume->private_volume; 658 Inode* directory = (Inode*)_directory->private_node; 659 660 status_t status = directory->CheckPermissions(W_OK); 661 if (status < B_OK) 662 return status; 663 664 Transaction transaction(volume); 665 BTree::Path path(volume->FSTree()); 666 667 ino_t id; 668 status = DirectoryIterator(directory).Lookup(name, strlen(name), &id); 669 if (status != B_OK) 670 return status; 671 672 Inode inode(volume, id); 673 status = inode.InitCheck(); 674 if (status != B_OK) 675 return status; 676 677 status = inode.Remove(transaction, &path); 678 if (status != B_OK) 679 return status; 680 status = inode.Dereference(transaction, &path, directory->ID(), name); 681 if (status != B_OK) 682 return status; 683 684 entry_cache_remove(volume->ID(), directory->ID(), name); 685 686 status = transaction.Done(); 687 if (status == B_OK) 688 notify_entry_removed(volume->ID(), directory->ID(), name, id); 689 else 690 entry_cache_add(volume->ID(), directory->ID(), name, id); 691 692 return status; 693 } 694 695 696 // #pragma mark - Directory functions 697 698 699 static status_t 700 btrfs_create_dir(fs_volume* _volume, fs_vnode* _directory, const char* name, 701 int mode) 702 { 703 Volume* volume = (Volume*)_volume->private_volume; 704 Inode* directory = (Inode*)_directory->private_node; 705 BTree::Path path(volume->FSTree()); 706 707 if (volume->IsReadOnly()) 708 return B_READ_ONLY_DEVICE; 709 710 if (!directory->IsDirectory()) 711 return B_NOT_A_DIRECTORY; 712 713 status_t status = directory->CheckPermissions(W_OK); 714 if (status < B_OK) 715 return status; 716 717 Transaction transaction(volume); 718 ino_t id = volume->GetNextInodeID(); 719 mode = S_DIRECTORY | (mode & S_IUMSK); 720 Inode* inode = Inode::Create(transaction, id, directory, mode); 721 if (inode == NULL) 722 return B_NO_MEMORY; 723 724 status = inode->Insert(transaction, &path); 725 if (status != B_OK) 726 return status; 727 728 status = inode->MakeReference(transaction, &path, directory, name, mode); 729 if (status != B_OK) 730 return status; 731 732 put_vnode(volume->FSVolume(), inode->ID()); 733 entry_cache_add(volume->ID(), directory->ID(), name, inode->ID()); 734 735 status = transaction.Done(); 736 if (status == B_OK) 737 notify_entry_created(volume->ID(), directory->ID(), name, inode->ID()); 738 else 739 entry_cache_remove(volume->ID(), directory->ID(), name); 740 741 return status; 742 } 743 744 745 static status_t 746 btrfs_remove_dir(fs_volume* _volume, fs_vnode* _directory, const char* name) 747 { 748 Volume* volume = (Volume*)_volume->private_volume; 749 Inode* directory = (Inode*)_directory->private_node; 750 751 Transaction transaction(volume); 752 BTree::Path path(volume->FSTree()); 753 754 ino_t id; 755 status_t status = DirectoryIterator(directory).Lookup(name, strlen(name), 756 &id); 757 if (status != B_OK) 758 return status; 759 760 Inode inode(volume, id); 761 status = inode.InitCheck(); 762 if (status != B_OK) 763 return status; 764 765 status = inode.Remove(transaction, &path); 766 if (status != B_OK) 767 return status; 768 status = inode.Dereference(transaction, &path, directory->ID(), name); 769 if (status != B_OK) 770 return status; 771 772 entry_cache_remove(volume->ID(), directory->ID(), name); 773 entry_cache_remove(volume->ID(), id, ".."); 774 775 status = transaction.Done(); 776 if (status == B_OK) 777 notify_entry_removed(volume->ID(), directory->ID(), name, id); 778 else { 779 entry_cache_add(volume->ID(), directory->ID(), name, id); 780 entry_cache_add(volume->ID(), id, "..", id); 781 } 782 783 return status; 784 } 785 786 787 static status_t 788 btrfs_open_dir(fs_volume* /*_volume*/, fs_vnode* _node, void** _cookie) 789 { 790 Inode* inode = (Inode*)_node->private_node; 791 status_t status = inode->CheckPermissions(R_OK); 792 if (status < B_OK) 793 return status; 794 795 if (!inode->IsDirectory()) 796 return B_NOT_A_DIRECTORY; 797 798 DirectoryIterator* iterator = new(std::nothrow) DirectoryIterator(inode); 799 if (iterator == NULL || iterator->InitCheck() != B_OK) { 800 delete iterator; 801 return B_NO_MEMORY; 802 } 803 804 *_cookie = iterator; 805 return B_OK; 806 } 807 808 809 static status_t 810 btrfs_read_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie, 811 struct dirent* dirent, size_t bufferSize, uint32* _num) 812 { 813 DirectoryIterator* iterator = (DirectoryIterator*)_cookie; 814 Volume* volume = (Volume*)_volume->private_volume; 815 816 uint32 maxCount = *_num; 817 uint32 count = 0; 818 819 while (count < maxCount && bufferSize > sizeof(struct dirent)) { 820 ino_t id; 821 size_t length = bufferSize - offsetof(struct dirent, d_name); 822 823 status_t status = iterator->GetNext(dirent->d_name, &length, 824 &id); 825 826 if (status == B_ENTRY_NOT_FOUND) 827 break; 828 829 if (status == B_BUFFER_OVERFLOW) { 830 // the remaining name buffer length was too small 831 if (count == 0) 832 return B_BUFFER_OVERFLOW; 833 break; 834 } 835 836 if (status != B_OK) 837 return status; 838 839 dirent->d_dev = volume->ID(); 840 dirent->d_ino = id; 841 dirent->d_reclen = offsetof(struct dirent, d_name) + length + 1; 842 843 bufferSize -= dirent->d_reclen; 844 dirent = (struct dirent*)((uint8*)dirent + dirent->d_reclen); 845 count++; 846 } 847 848 *_num = count; 849 return B_OK; 850 } 851 852 853 static status_t 854 btrfs_rewind_dir(fs_volume* /*_volume*/, fs_vnode* /*node*/, void* _cookie) 855 { 856 DirectoryIterator* iterator = (DirectoryIterator*)_cookie; 857 858 return iterator->Rewind(); 859 } 860 861 862 static status_t 863 btrfs_close_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, 864 void * /*_cookie*/) 865 { 866 return B_OK; 867 } 868 869 870 static status_t 871 btrfs_free_dir_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie) 872 { 873 delete (DirectoryIterator*)_cookie; 874 return B_OK; 875 } 876 877 878 static status_t 879 btrfs_open_attr_dir(fs_volume* _volume, fs_vnode* _node, void** _cookie) 880 { 881 Inode* inode = (Inode*)_node->private_node; 882 TRACE("%s()\n", __FUNCTION__); 883 884 // on directories too ? 885 if (!inode->IsFile()) 886 return EINVAL; 887 888 AttributeIterator* iterator = new(std::nothrow) AttributeIterator(inode); 889 if (iterator == NULL || iterator->InitCheck() != B_OK) { 890 delete iterator; 891 return B_NO_MEMORY; 892 } 893 894 *_cookie = iterator; 895 return B_OK; 896 } 897 898 899 static status_t 900 btrfs_close_attr_dir(fs_volume* _volume, fs_vnode* _node, void* cookie) 901 { 902 TRACE("%s()\n", __FUNCTION__); 903 return B_OK; 904 } 905 906 907 static status_t 908 btrfs_free_attr_dir_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie) 909 { 910 TRACE("%s()\n", __FUNCTION__); 911 delete (AttributeIterator*)_cookie; 912 return B_OK; 913 } 914 915 916 static status_t 917 btrfs_read_attr_dir(fs_volume* _volume, fs_vnode* _node, 918 void* _cookie, struct dirent* dirent, size_t bufferSize, uint32* _num) 919 { 920 TRACE("%s()\n", __FUNCTION__); 921 AttributeIterator* iterator = (AttributeIterator*)_cookie; 922 923 size_t length = bufferSize; 924 status_t status = iterator->GetNext(dirent->d_name, &length); 925 if (status == B_ENTRY_NOT_FOUND) { 926 *_num = 0; 927 return B_OK; 928 } 929 930 if (status != B_OK) 931 return status; 932 933 Volume* volume = (Volume*)_volume->private_volume; 934 dirent->d_dev = volume->ID(); 935 dirent->d_reclen = offsetof(struct dirent, d_name) + length + 1; 936 *_num = 1; 937 938 return B_OK; 939 } 940 941 942 static status_t 943 btrfs_rewind_attr_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie) 944 { 945 AttributeIterator* iterator = (AttributeIterator*)_cookie; 946 return iterator->Rewind(); 947 } 948 949 950 /* attribute operations */ 951 static status_t 952 btrfs_create_attr(fs_volume* _volume, fs_vnode* _node, 953 const char* name, uint32 type, int openMode, void** _cookie) 954 { 955 return EROFS; 956 } 957 958 959 static status_t 960 btrfs_open_attr(fs_volume* _volume, fs_vnode* _node, const char* name, 961 int openMode, void** _cookie) 962 { 963 TRACE("%s()\n", __FUNCTION__); 964 965 Inode* inode = (Inode*)_node->private_node; 966 Attribute attribute(inode); 967 968 return attribute.Open(name, openMode, (attr_cookie**)_cookie); 969 } 970 971 972 static status_t 973 btrfs_close_attr(fs_volume* _volume, fs_vnode* _node, 974 void* cookie) 975 { 976 return B_OK; 977 } 978 979 980 static status_t 981 btrfs_free_attr_cookie(fs_volume* _volume, fs_vnode* _node, 982 void* cookie) 983 { 984 delete (attr_cookie*)cookie; 985 return B_OK; 986 } 987 988 989 static status_t 990 btrfs_read_attr(fs_volume* _volume, fs_vnode* _node, void* _cookie, 991 off_t pos, void* buffer, size_t* _length) 992 { 993 TRACE("%s()\n", __FUNCTION__); 994 995 attr_cookie* cookie = (attr_cookie*)_cookie; 996 Inode* inode = (Inode*)_node->private_node; 997 998 Attribute attribute(inode, cookie); 999 1000 return attribute.Read(cookie, pos, (uint8*)buffer, _length); 1001 } 1002 1003 1004 static status_t 1005 btrfs_write_attr(fs_volume* _volume, fs_vnode* _node, void* cookie, 1006 off_t pos, const void* buffer, size_t* length) 1007 { 1008 return EROFS; 1009 } 1010 1011 1012 static status_t 1013 btrfs_read_attr_stat(fs_volume* _volume, fs_vnode* _node, 1014 void* _cookie, struct stat* stat) 1015 { 1016 attr_cookie* cookie = (attr_cookie*)_cookie; 1017 Inode* inode = (Inode*)_node->private_node; 1018 1019 Attribute attribute(inode, cookie); 1020 1021 return attribute.Stat(*stat); 1022 } 1023 1024 1025 static status_t 1026 btrfs_write_attr_stat(fs_volume* _volume, fs_vnode* _node, 1027 void* cookie, const struct stat* stat, int statMask) 1028 { 1029 return EROFS; 1030 } 1031 1032 1033 static status_t 1034 btrfs_rename_attr(fs_volume* _volume, fs_vnode* fromVnode, 1035 const char* fromName, fs_vnode* toVnode, const char* toName) 1036 { 1037 return EROFS; 1038 } 1039 1040 1041 static status_t 1042 btrfs_remove_attr(fs_volume* _volume, fs_vnode* vnode, 1043 const char* name) 1044 { 1045 return EROFS; 1046 } 1047 1048 static uint32 1049 btrfs_get_supported_operations(partition_data* partition, uint32 mask) 1050 { 1051 // TODO: We should at least check the partition size. 1052 return B_DISK_SYSTEM_SUPPORTS_INITIALIZING 1053 | B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME 1054 // | B_DISK_SYSTEM_SUPPORTS_WRITING 1055 ; 1056 } 1057 1058 1059 static status_t 1060 btrfs_initialize(int fd, partition_id partitionID, const char* name, 1061 const char* parameterString, off_t partitionSize, disk_job_id job) 1062 { 1063 // check name 1064 status_t status = check_volume_name(name); 1065 if (status != B_OK) 1066 return status; 1067 1068 // parse parameters 1069 initialize_parameters parameters; 1070 status = parse_initialize_parameters(parameterString, parameters); 1071 if (status != B_OK) 1072 return status; 1073 1074 update_disk_device_job_progress(job, 0); 1075 1076 // initialize the volume 1077 Volume volume(NULL); 1078 status = volume.Initialize(fd, name, parameters.blockSize, 1079 parameters.sectorSize); 1080 if (status < B_OK) { 1081 INFORM("Initializing volume failed: %s\n", strerror(status)); 1082 return status; 1083 } 1084 1085 // rescan partition 1086 status = scan_partition(partitionID); 1087 if (status != B_OK) 1088 return status; 1089 1090 update_disk_device_job_progress(job, 1); 1091 1092 // print some info, if desired 1093 if (parameters.verbose) { 1094 btrfs_super_block super = volume.SuperBlock(); 1095 1096 INFORM("Disk was initialized successfully.\n"); 1097 INFORM("\tlabel: \"%s\"\n", super.label); 1098 INFORM("\tblock size: %u bytes\n", (unsigned)super.BlockSize()); 1099 INFORM("\tsector size: %u bytes\n", (unsigned)super.SectorSize()); 1100 } 1101 1102 return B_OK; 1103 } 1104 1105 1106 static status_t 1107 btrfs_uninitialize(int fd, partition_id partitionID, off_t partitionSize, 1108 uint32 blockSize, disk_job_id job) 1109 { 1110 if (blockSize == 0) 1111 return B_BAD_VALUE; 1112 1113 update_disk_device_job_progress(job, 0.0); 1114 1115 // just overwrite the superblock 1116 btrfs_super_block superBlock; 1117 memset(&superBlock, 0, sizeof(superBlock)); 1118 1119 if (write_pos(fd, BTRFS_SUPER_BLOCK_OFFSET, &superBlock, 1120 sizeof(superBlock)) < 0) 1121 return errno; 1122 1123 update_disk_device_job_progress(job, 1.0); 1124 1125 return B_OK; 1126 } 1127 1128 // #pragma mark - 1129 1130 1131 static status_t 1132 btrfs_std_ops(int32 op, ...) 1133 { 1134 switch (op) { 1135 case B_MODULE_INIT: 1136 init_debugging(); 1137 1138 return B_OK; 1139 case B_MODULE_UNINIT: 1140 exit_debugging(); 1141 1142 return B_OK; 1143 1144 default: 1145 return B_ERROR; 1146 } 1147 } 1148 1149 1150 fs_volume_ops gBtrfsVolumeOps = { 1151 &btrfs_unmount, 1152 &btrfs_read_fs_info, 1153 &btrfs_write_fs_info, 1154 NULL, // fs_sync, 1155 &btrfs_get_vnode, 1156 }; 1157 1158 1159 fs_vnode_ops gBtrfsVnodeOps = { 1160 /* vnode operations */ 1161 &btrfs_lookup, 1162 NULL, // btrfs_get_vnode_name - optional, and we can't do better than the 1163 // fallback implementation, so leave as NULL. 1164 &btrfs_put_vnode, 1165 NULL, // btrfs_remove_vnode, 1166 1167 /* VM file access */ 1168 &btrfs_can_page, 1169 &btrfs_read_pages, 1170 NULL, // btrfs_write_pages, 1171 1172 NULL, // io() 1173 NULL, // cancel_io() 1174 1175 &btrfs_get_file_map, 1176 1177 &btrfs_ioctl, 1178 NULL, 1179 NULL, // fs_select 1180 NULL, // fs_deselect 1181 NULL, // fs_fsync, 1182 1183 &btrfs_read_link, 1184 NULL, // fs_create_symlink, 1185 1186 NULL, // fs_link, 1187 &btrfs_unlink, 1188 NULL, // fs_rename, 1189 1190 &btrfs_access, 1191 &btrfs_read_stat, 1192 &btrfs_write_stat, 1193 NULL, // fs_preallocate 1194 1195 /* file operations */ 1196 NULL, // fs_create, 1197 &btrfs_open, 1198 &btrfs_close, 1199 &btrfs_free_cookie, 1200 &btrfs_read, 1201 &btrfs_write, 1202 1203 /* directory operations */ 1204 &btrfs_create_dir, 1205 &btrfs_remove_dir, 1206 &btrfs_open_dir, 1207 &btrfs_close_dir, 1208 &btrfs_free_dir_cookie, 1209 &btrfs_read_dir, 1210 &btrfs_rewind_dir, 1211 1212 /* attribute directory operations */ 1213 &btrfs_open_attr_dir, 1214 &btrfs_close_attr_dir, 1215 &btrfs_free_attr_dir_cookie, 1216 &btrfs_read_attr_dir, 1217 &btrfs_rewind_attr_dir, 1218 1219 /* attribute operations */ 1220 &btrfs_create_attr, 1221 &btrfs_open_attr, 1222 &btrfs_close_attr, 1223 &btrfs_free_attr_cookie, 1224 &btrfs_read_attr, 1225 &btrfs_write_attr, 1226 &btrfs_read_attr_stat, 1227 &btrfs_write_attr_stat, 1228 &btrfs_rename_attr, 1229 &btrfs_remove_attr, 1230 }; 1231 1232 1233 static file_system_module_info sBtrfsFileSystem = { 1234 { 1235 "file_systems/btrfs" B_CURRENT_FS_API_VERSION, 1236 0, 1237 btrfs_std_ops, 1238 }, 1239 1240 "btrfs", // short_name 1241 "Btrfs File System", // pretty_name 1242 1243 // DDM flags 1244 0 1245 | B_DISK_SYSTEM_SUPPORTS_INITIALIZING 1246 | B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME 1247 // | B_DISK_SYSTEM_SUPPORTS_WRITING 1248 , 1249 1250 // scanning 1251 btrfs_identify_partition, 1252 btrfs_scan_partition, 1253 btrfs_free_identify_partition_cookie, 1254 NULL, // free_partition_content_cookie() 1255 1256 &btrfs_mount, 1257 1258 1259 /* capability querying operations */ 1260 &btrfs_get_supported_operations, 1261 1262 NULL, // validate_resize 1263 NULL, // validate_move 1264 NULL, // validate_set_content_name 1265 NULL, // validate_set_content_parameters 1266 NULL, // validate_initialize, 1267 1268 /* shadow partition modification */ 1269 NULL, // shadow_changed 1270 1271 /* writing */ 1272 NULL, // defragment 1273 NULL, // repair 1274 NULL, // resize 1275 NULL, // move 1276 NULL, // set_content_name 1277 NULL, // set_content_parameters 1278 btrfs_initialize, 1279 btrfs_uninitialize 1280 }; 1281 1282 1283 module_info* modules[] = { 1284 (module_info*)&sBtrfsFileSystem, 1285 NULL, 1286 }; 1287