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