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("\33[34mbtrfs:\33[0m %s()\n",__PRETTY_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 FUNCTION(); 598 Inode* inode = (Inode*)_node->private_node; 599 600 if (!inode->IsFile()) { 601 *_length = 0; 602 return inode->IsDirectory() ? B_IS_A_DIRECTORY : B_BAD_VALUE; 603 } 604 605 return inode->ReadAt(pos, (uint8*)buffer, _length); 606 } 607 608 609 static status_t 610 btrfs_close(fs_volume* _volume, fs_vnode* _node, void* _cookie) 611 { 612 return B_OK; 613 } 614 615 616 static status_t 617 btrfs_free_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie) 618 { 619 file_cookie* cookie = (file_cookie*)_cookie; 620 Volume* volume = (Volume*)_volume->private_volume; 621 Inode* inode = (Inode*)_node->private_node; 622 623 if (inode->Size() != cookie->last_size) 624 notify_stat_changed(volume->ID(), -1, inode->ID(), B_STAT_SIZE); 625 626 delete cookie; 627 return B_OK; 628 } 629 630 631 static status_t 632 btrfs_access(fs_volume* _volume, fs_vnode* _node, int accessMode) 633 { 634 Inode* inode = (Inode*)_node->private_node; 635 return inode->CheckPermissions(accessMode); 636 } 637 638 639 static status_t 640 btrfs_read_link(fs_volume* _volume, fs_vnode* _node, char* buffer, 641 size_t* _bufferSize) 642 { 643 FUNCTION(); 644 Inode* inode = (Inode*)_node->private_node; 645 646 if (!inode->IsSymLink()) 647 return B_BAD_VALUE; 648 649 status_t result = inode->ReadAt(0, reinterpret_cast<uint8*>(buffer), 650 _bufferSize); 651 if (result != B_OK) 652 return result; 653 654 *_bufferSize = inode->Size(); 655 return B_OK; 656 } 657 658 659 status_t 660 btrfs_unlink(fs_volume* _volume, fs_vnode* _directory, const char* name) 661 { 662 if (!strcmp(name, "..") || !strcmp(name, ".")) 663 return B_NOT_ALLOWED; 664 665 Volume* volume = (Volume*)_volume->private_volume; 666 Inode* directory = (Inode*)_directory->private_node; 667 668 status_t status = directory->CheckPermissions(W_OK); 669 if (status < B_OK) 670 return status; 671 672 Transaction transaction(volume); 673 BTree::Path path(volume->FSTree()); 674 675 ino_t id; 676 status = DirectoryIterator(directory).Lookup(name, strlen(name), &id); 677 if (status != B_OK) 678 return status; 679 680 Inode inode(volume, id); 681 status = inode.InitCheck(); 682 if (status != B_OK) 683 return status; 684 685 status = inode.Remove(transaction, &path); 686 if (status != B_OK) 687 return status; 688 status = inode.Dereference(transaction, &path, directory->ID(), name); 689 if (status != B_OK) 690 return status; 691 692 entry_cache_remove(volume->ID(), directory->ID(), name); 693 694 status = transaction.Done(); 695 if (status == B_OK) 696 notify_entry_removed(volume->ID(), directory->ID(), name, id); 697 else 698 entry_cache_add(volume->ID(), directory->ID(), name, id); 699 700 return status; 701 } 702 703 704 // #pragma mark - Directory functions 705 706 707 static status_t 708 btrfs_create_dir(fs_volume* _volume, fs_vnode* _directory, const char* name, 709 int mode) 710 { 711 Volume* volume = (Volume*)_volume->private_volume; 712 Inode* directory = (Inode*)_directory->private_node; 713 BTree::Path path(volume->FSTree()); 714 715 if (volume->IsReadOnly()) 716 return B_READ_ONLY_DEVICE; 717 718 if (!directory->IsDirectory()) 719 return B_NOT_A_DIRECTORY; 720 721 status_t status = directory->CheckPermissions(W_OK); 722 if (status < B_OK) 723 return status; 724 725 Transaction transaction(volume); 726 ino_t id = volume->GetNextInodeID(); 727 mode = S_DIRECTORY | (mode & S_IUMSK); 728 Inode* inode = Inode::Create(transaction, id, directory, mode); 729 if (inode == NULL) 730 return B_NO_MEMORY; 731 732 status = inode->Insert(transaction, &path); 733 if (status != B_OK) 734 return status; 735 736 status = inode->MakeReference(transaction, &path, directory, name, mode); 737 if (status != B_OK) 738 return status; 739 740 put_vnode(volume->FSVolume(), inode->ID()); 741 entry_cache_add(volume->ID(), directory->ID(), name, inode->ID()); 742 743 status = transaction.Done(); 744 if (status == B_OK) 745 notify_entry_created(volume->ID(), directory->ID(), name, inode->ID()); 746 else 747 entry_cache_remove(volume->ID(), directory->ID(), name); 748 749 return status; 750 } 751 752 753 static status_t 754 btrfs_remove_dir(fs_volume* _volume, fs_vnode* _directory, const char* name) 755 { 756 Volume* volume = (Volume*)_volume->private_volume; 757 Inode* directory = (Inode*)_directory->private_node; 758 759 Transaction transaction(volume); 760 BTree::Path path(volume->FSTree()); 761 762 ino_t id; 763 status_t status = DirectoryIterator(directory).Lookup(name, strlen(name), 764 &id); 765 if (status != B_OK) 766 return status; 767 768 Inode inode(volume, id); 769 status = inode.InitCheck(); 770 if (status != B_OK) 771 return status; 772 773 status = inode.Remove(transaction, &path); 774 if (status != B_OK) 775 return status; 776 status = inode.Dereference(transaction, &path, directory->ID(), name); 777 if (status != B_OK) 778 return status; 779 780 entry_cache_remove(volume->ID(), directory->ID(), name); 781 entry_cache_remove(volume->ID(), id, ".."); 782 783 status = transaction.Done(); 784 if (status == B_OK) 785 notify_entry_removed(volume->ID(), directory->ID(), name, id); 786 else { 787 entry_cache_add(volume->ID(), directory->ID(), name, id); 788 entry_cache_add(volume->ID(), id, "..", id); 789 } 790 791 return status; 792 } 793 794 795 static status_t 796 btrfs_open_dir(fs_volume* /*_volume*/, fs_vnode* _node, void** _cookie) 797 { 798 Inode* inode = (Inode*)_node->private_node; 799 status_t status = inode->CheckPermissions(R_OK); 800 if (status < B_OK) 801 return status; 802 803 if (!inode->IsDirectory()) 804 return B_NOT_A_DIRECTORY; 805 806 DirectoryIterator* iterator = new(std::nothrow) DirectoryIterator(inode); 807 if (iterator == NULL || iterator->InitCheck() != B_OK) { 808 delete iterator; 809 return B_NO_MEMORY; 810 } 811 812 *_cookie = iterator; 813 return B_OK; 814 } 815 816 817 static status_t 818 btrfs_read_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie, 819 struct dirent* dirent, size_t bufferSize, uint32* _num) 820 { 821 DirectoryIterator* iterator = (DirectoryIterator*)_cookie; 822 Volume* volume = (Volume*)_volume->private_volume; 823 824 uint32 maxCount = *_num; 825 uint32 count = 0; 826 827 while (count < maxCount && bufferSize > sizeof(struct dirent)) { 828 ino_t id; 829 size_t length = bufferSize - offsetof(struct dirent, d_name); 830 831 status_t status = iterator->GetNext(dirent->d_name, &length, 832 &id); 833 834 if (status == B_ENTRY_NOT_FOUND) 835 break; 836 837 if (status == B_BUFFER_OVERFLOW) { 838 // the remaining name buffer length was too small 839 if (count == 0) 840 return B_BUFFER_OVERFLOW; 841 break; 842 } 843 844 if (status != B_OK) 845 return status; 846 847 dirent->d_dev = volume->ID(); 848 dirent->d_ino = id; 849 dirent->d_reclen = offsetof(struct dirent, d_name) + length + 1; 850 851 bufferSize -= dirent->d_reclen; 852 dirent = (struct dirent*)((uint8*)dirent + dirent->d_reclen); 853 count++; 854 } 855 856 *_num = count; 857 return B_OK; 858 } 859 860 861 static status_t 862 btrfs_rewind_dir(fs_volume* /*_volume*/, fs_vnode* /*node*/, void* _cookie) 863 { 864 DirectoryIterator* iterator = (DirectoryIterator*)_cookie; 865 866 return iterator->Rewind(); 867 } 868 869 870 static status_t 871 btrfs_close_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, 872 void * /*_cookie*/) 873 { 874 return B_OK; 875 } 876 877 878 static status_t 879 btrfs_free_dir_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie) 880 { 881 delete (DirectoryIterator*)_cookie; 882 return B_OK; 883 } 884 885 886 static status_t 887 btrfs_open_attr_dir(fs_volume* _volume, fs_vnode* _node, void** _cookie) 888 { 889 Inode* inode = (Inode*)_node->private_node; 890 TRACE("%s()\n", __FUNCTION__); 891 892 // on directories too ? 893 if (!inode->IsFile()) 894 return EINVAL; 895 896 AttributeIterator* iterator = new(std::nothrow) AttributeIterator(inode); 897 if (iterator == NULL || iterator->InitCheck() != B_OK) { 898 delete iterator; 899 return B_NO_MEMORY; 900 } 901 902 *_cookie = iterator; 903 return B_OK; 904 } 905 906 907 static status_t 908 btrfs_close_attr_dir(fs_volume* _volume, fs_vnode* _node, void* cookie) 909 { 910 TRACE("%s()\n", __FUNCTION__); 911 return B_OK; 912 } 913 914 915 static status_t 916 btrfs_free_attr_dir_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie) 917 { 918 TRACE("%s()\n", __FUNCTION__); 919 delete (AttributeIterator*)_cookie; 920 return B_OK; 921 } 922 923 924 static status_t 925 btrfs_read_attr_dir(fs_volume* _volume, fs_vnode* _node, 926 void* _cookie, struct dirent* dirent, size_t bufferSize, uint32* _num) 927 { 928 TRACE("%s()\n", __FUNCTION__); 929 AttributeIterator* iterator = (AttributeIterator*)_cookie; 930 931 size_t length = bufferSize; 932 status_t status = iterator->GetNext(dirent->d_name, &length); 933 if (status == B_ENTRY_NOT_FOUND) { 934 *_num = 0; 935 return B_OK; 936 } 937 938 if (status != B_OK) 939 return status; 940 941 Volume* volume = (Volume*)_volume->private_volume; 942 dirent->d_dev = volume->ID(); 943 dirent->d_reclen = offsetof(struct dirent, d_name) + length + 1; 944 *_num = 1; 945 946 return B_OK; 947 } 948 949 950 static status_t 951 btrfs_rewind_attr_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie) 952 { 953 AttributeIterator* iterator = (AttributeIterator*)_cookie; 954 return iterator->Rewind(); 955 } 956 957 958 /* attribute operations */ 959 static status_t 960 btrfs_create_attr(fs_volume* _volume, fs_vnode* _node, 961 const char* name, uint32 type, int openMode, void** _cookie) 962 { 963 return EROFS; 964 } 965 966 967 static status_t 968 btrfs_open_attr(fs_volume* _volume, fs_vnode* _node, const char* name, 969 int openMode, void** _cookie) 970 { 971 TRACE("%s()\n", __FUNCTION__); 972 973 Inode* inode = (Inode*)_node->private_node; 974 Attribute attribute(inode); 975 976 return attribute.Open(name, openMode, (attr_cookie**)_cookie); 977 } 978 979 980 static status_t 981 btrfs_close_attr(fs_volume* _volume, fs_vnode* _node, 982 void* cookie) 983 { 984 return B_OK; 985 } 986 987 988 static status_t 989 btrfs_free_attr_cookie(fs_volume* _volume, fs_vnode* _node, 990 void* cookie) 991 { 992 delete (attr_cookie*)cookie; 993 return B_OK; 994 } 995 996 997 static status_t 998 btrfs_read_attr(fs_volume* _volume, fs_vnode* _node, void* _cookie, 999 off_t pos, void* buffer, size_t* _length) 1000 { 1001 TRACE("%s()\n", __FUNCTION__); 1002 1003 attr_cookie* cookie = (attr_cookie*)_cookie; 1004 Inode* inode = (Inode*)_node->private_node; 1005 1006 Attribute attribute(inode, cookie); 1007 1008 return attribute.Read(cookie, pos, (uint8*)buffer, _length); 1009 } 1010 1011 1012 static status_t 1013 btrfs_write_attr(fs_volume* _volume, fs_vnode* _node, void* cookie, 1014 off_t pos, const void* buffer, size_t* length) 1015 { 1016 return EROFS; 1017 } 1018 1019 1020 static status_t 1021 btrfs_read_attr_stat(fs_volume* _volume, fs_vnode* _node, 1022 void* _cookie, struct stat* stat) 1023 { 1024 attr_cookie* cookie = (attr_cookie*)_cookie; 1025 Inode* inode = (Inode*)_node->private_node; 1026 1027 Attribute attribute(inode, cookie); 1028 1029 return attribute.Stat(*stat); 1030 } 1031 1032 1033 static status_t 1034 btrfs_write_attr_stat(fs_volume* _volume, fs_vnode* _node, 1035 void* cookie, const struct stat* stat, int statMask) 1036 { 1037 return EROFS; 1038 } 1039 1040 1041 static status_t 1042 btrfs_rename_attr(fs_volume* _volume, fs_vnode* fromVnode, 1043 const char* fromName, fs_vnode* toVnode, const char* toName) 1044 { 1045 return EROFS; 1046 } 1047 1048 1049 static status_t 1050 btrfs_remove_attr(fs_volume* _volume, fs_vnode* vnode, 1051 const char* name) 1052 { 1053 return EROFS; 1054 } 1055 1056 static uint32 1057 btrfs_get_supported_operations(partition_data* partition, uint32 mask) 1058 { 1059 // TODO: We should at least check the partition size. 1060 return B_DISK_SYSTEM_SUPPORTS_INITIALIZING 1061 | B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME 1062 // | B_DISK_SYSTEM_SUPPORTS_WRITING 1063 ; 1064 } 1065 1066 1067 static status_t 1068 btrfs_initialize(int fd, partition_id partitionID, const char* name, 1069 const char* parameterString, off_t partitionSize, disk_job_id job) 1070 { 1071 // check name 1072 status_t status = check_volume_name(name); 1073 if (status != B_OK) 1074 return status; 1075 1076 // parse parameters 1077 initialize_parameters parameters; 1078 status = parse_initialize_parameters(parameterString, parameters); 1079 if (status != B_OK) 1080 return status; 1081 1082 update_disk_device_job_progress(job, 0); 1083 1084 // initialize the volume 1085 Volume volume(NULL); 1086 status = volume.Initialize(fd, name, parameters.blockSize, 1087 parameters.sectorSize); 1088 if (status < B_OK) { 1089 INFORM("Initializing volume failed: %s\n", strerror(status)); 1090 return status; 1091 } 1092 1093 // rescan partition 1094 status = scan_partition(partitionID); 1095 if (status != B_OK) 1096 return status; 1097 1098 update_disk_device_job_progress(job, 1); 1099 1100 // print some info, if desired 1101 if (parameters.verbose) { 1102 btrfs_super_block super = volume.SuperBlock(); 1103 1104 INFORM("Disk was initialized successfully.\n"); 1105 INFORM("\tlabel: \"%s\"\n", super.label); 1106 INFORM("\tblock size: %u bytes\n", (unsigned)super.BlockSize()); 1107 INFORM("\tsector size: %u bytes\n", (unsigned)super.SectorSize()); 1108 } 1109 1110 return B_OK; 1111 } 1112 1113 1114 static status_t 1115 btrfs_uninitialize(int fd, partition_id partitionID, off_t partitionSize, 1116 uint32 blockSize, disk_job_id job) 1117 { 1118 if (blockSize == 0) 1119 return B_BAD_VALUE; 1120 1121 update_disk_device_job_progress(job, 0.0); 1122 1123 // just overwrite the superblock 1124 btrfs_super_block superBlock; 1125 memset(&superBlock, 0, sizeof(superBlock)); 1126 1127 if (write_pos(fd, BTRFS_SUPER_BLOCK_OFFSET, &superBlock, 1128 sizeof(superBlock)) < 0) 1129 return errno; 1130 1131 update_disk_device_job_progress(job, 1.0); 1132 1133 return B_OK; 1134 } 1135 1136 // #pragma mark - 1137 1138 1139 static status_t 1140 btrfs_std_ops(int32 op, ...) 1141 { 1142 switch (op) { 1143 case B_MODULE_INIT: 1144 init_debugging(); 1145 1146 return B_OK; 1147 case B_MODULE_UNINIT: 1148 exit_debugging(); 1149 1150 return B_OK; 1151 1152 default: 1153 return B_ERROR; 1154 } 1155 } 1156 1157 1158 fs_volume_ops gBtrfsVolumeOps = { 1159 &btrfs_unmount, 1160 &btrfs_read_fs_info, 1161 &btrfs_write_fs_info, 1162 NULL, // fs_sync, 1163 &btrfs_get_vnode, 1164 }; 1165 1166 1167 fs_vnode_ops gBtrfsVnodeOps = { 1168 /* vnode operations */ 1169 &btrfs_lookup, 1170 NULL, // btrfs_get_vnode_name - optional, and we can't do better than the 1171 // fallback implementation, so leave as NULL. 1172 &btrfs_put_vnode, 1173 NULL, // btrfs_remove_vnode, 1174 1175 /* VM file access */ 1176 &btrfs_can_page, 1177 &btrfs_read_pages, 1178 NULL, // btrfs_write_pages, 1179 1180 NULL, // io() 1181 NULL, // cancel_io() 1182 1183 &btrfs_get_file_map, 1184 1185 &btrfs_ioctl, 1186 NULL, 1187 NULL, // fs_select 1188 NULL, // fs_deselect 1189 NULL, // fs_fsync, 1190 1191 &btrfs_read_link, 1192 NULL, // fs_create_symlink, 1193 1194 NULL, // fs_link, 1195 &btrfs_unlink, 1196 NULL, // fs_rename, 1197 1198 &btrfs_access, 1199 &btrfs_read_stat, 1200 &btrfs_write_stat, 1201 NULL, // fs_preallocate 1202 1203 /* file operations */ 1204 NULL, // fs_create, 1205 &btrfs_open, 1206 &btrfs_close, 1207 &btrfs_free_cookie, 1208 &btrfs_read, 1209 &btrfs_write, 1210 1211 /* directory operations */ 1212 &btrfs_create_dir, 1213 &btrfs_remove_dir, 1214 &btrfs_open_dir, 1215 &btrfs_close_dir, 1216 &btrfs_free_dir_cookie, 1217 &btrfs_read_dir, 1218 &btrfs_rewind_dir, 1219 1220 /* attribute directory operations */ 1221 &btrfs_open_attr_dir, 1222 &btrfs_close_attr_dir, 1223 &btrfs_free_attr_dir_cookie, 1224 &btrfs_read_attr_dir, 1225 &btrfs_rewind_attr_dir, 1226 1227 /* attribute operations */ 1228 &btrfs_create_attr, 1229 &btrfs_open_attr, 1230 &btrfs_close_attr, 1231 &btrfs_free_attr_cookie, 1232 &btrfs_read_attr, 1233 &btrfs_write_attr, 1234 &btrfs_read_attr_stat, 1235 &btrfs_write_attr_stat, 1236 &btrfs_rename_attr, 1237 &btrfs_remove_attr, 1238 }; 1239 1240 1241 static file_system_module_info sBtrfsFileSystem = { 1242 { 1243 "file_systems/btrfs" B_CURRENT_FS_API_VERSION, 1244 0, 1245 btrfs_std_ops, 1246 }, 1247 1248 "btrfs", // short_name 1249 "Btrfs File System", // pretty_name 1250 1251 // DDM flags 1252 0 1253 | B_DISK_SYSTEM_SUPPORTS_INITIALIZING 1254 | B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME 1255 // | B_DISK_SYSTEM_SUPPORTS_WRITING 1256 , 1257 1258 // scanning 1259 btrfs_identify_partition, 1260 btrfs_scan_partition, 1261 btrfs_free_identify_partition_cookie, 1262 NULL, // free_partition_content_cookie() 1263 1264 &btrfs_mount, 1265 1266 1267 /* capability querying operations */ 1268 &btrfs_get_supported_operations, 1269 1270 NULL, // validate_resize 1271 NULL, // validate_move 1272 NULL, // validate_set_content_name 1273 NULL, // validate_set_content_parameters 1274 NULL, // validate_initialize, 1275 1276 /* shadow partition modification */ 1277 NULL, // shadow_changed 1278 1279 /* writing */ 1280 NULL, // defragment 1281 NULL, // repair 1282 NULL, // resize 1283 NULL, // move 1284 NULL, // set_content_name 1285 NULL, // set_content_parameters 1286 btrfs_initialize, 1287 btrfs_uninitialize 1288 }; 1289 1290 1291 module_info* modules[] = { 1292 (module_info*)&sBtrfsFileSystem, 1293 NULL, 1294 }; 1295