1 /* 2 * Copyright 2019, Les De Ridder, les@lesderid.net 3 * Copyright 2017, Chế Vũ Gia Hy, cvghy116@gmail.com. 4 * Copyright 2011, Jérôme Duval, korli@users.berlios.de. 5 * Copyright 2008-2010, Axel Dörfler, axeld@pinc-software.de. 6 * 7 * This file may be used under the terms of the MIT License. 8 */ 9 10 11 //! Superblock, mounting, etc. 12 13 14 #include "Volume.h" 15 #include "BTree.h" 16 #include "CachedBlock.h" 17 #include "Chunk.h" 18 #include "DebugSupport.h" 19 #include "ExtentAllocator.h" 20 #include "Inode.h" 21 #include "Journal.h" 22 #include "Utility.h" 23 24 25 //#define TRACE_BTRFS 26 #ifdef TRACE_BTRFS 27 # define TRACE(x...) dprintf("\33[34mbtrfs:\33[0m " x) 28 #else 29 # define TRACE(x...) ; 30 #endif 31 # define ERROR(x...) dprintf("\33[34mbtrfs:\33[0m " x) 32 33 34 class DeviceOpener { 35 public: 36 DeviceOpener(int fd, int mode); 37 DeviceOpener(const char* device, int mode); 38 ~DeviceOpener(); 39 40 int Open(const char* device, int mode); 41 int Open(int fd, int mode); 42 void* InitCache(off_t numBlocks, uint32 blockSize); 43 void RemoveCache(bool allowWrites); 44 45 void Keep(); 46 47 int Device() const { return fDevice; } 48 int Mode() const { return fMode; } 49 bool IsReadOnly() const 50 { return _IsReadOnly(fMode); } 51 52 status_t GetSize(off_t* _size, uint32* _blockSize = NULL); 53 54 private: 55 static bool _IsReadOnly(int mode) 56 { return (mode & O_RWMASK) == O_RDONLY;} 57 static bool _IsReadWrite(int mode) 58 { return (mode & O_RWMASK) == O_RDWR;} 59 60 int fDevice; 61 int fMode; 62 void* fBlockCache; 63 }; 64 65 66 DeviceOpener::DeviceOpener(const char* device, int mode) 67 : 68 fBlockCache(NULL) 69 { 70 Open(device, mode); 71 } 72 73 74 DeviceOpener::DeviceOpener(int fd, int mode) 75 : 76 fBlockCache(NULL) 77 { 78 Open(fd, mode); 79 } 80 81 82 DeviceOpener::~DeviceOpener() 83 { 84 if (fDevice >= 0) { 85 RemoveCache(false); 86 close(fDevice); 87 } 88 } 89 90 91 int 92 DeviceOpener::Open(const char* device, int mode) 93 { 94 fDevice = open(device, mode | O_NOCACHE); 95 if (fDevice < 0) 96 fDevice = errno; 97 98 if (fDevice < 0 && _IsReadWrite(mode)) { 99 // try again to open read-only (don't rely on a specific error code) 100 return Open(device, O_RDONLY | O_NOCACHE); 101 } 102 103 if (fDevice >= 0) { 104 // opening succeeded 105 fMode = mode; 106 if (_IsReadWrite(mode)) { 107 // check out if the device really allows for read/write access 108 device_geometry geometry; 109 if (!ioctl(fDevice, B_GET_GEOMETRY, &geometry)) { 110 if (geometry.read_only) { 111 // reopen device read-only 112 close(fDevice); 113 return Open(device, O_RDONLY | O_NOCACHE); 114 } 115 } 116 } 117 } 118 119 return fDevice; 120 } 121 122 123 int 124 DeviceOpener::Open(int fd, int mode) 125 { 126 fDevice = dup(fd); 127 if (fDevice < 0) 128 return errno; 129 130 fMode = mode; 131 132 return fDevice; 133 } 134 135 136 void* 137 DeviceOpener::InitCache(off_t numBlocks, uint32 blockSize) 138 { 139 return fBlockCache = block_cache_create(fDevice, numBlocks, blockSize, 140 IsReadOnly()); 141 } 142 143 144 void 145 DeviceOpener::RemoveCache(bool allowWrites) 146 { 147 if (fBlockCache == NULL) 148 return; 149 150 block_cache_delete(fBlockCache, allowWrites); 151 fBlockCache = NULL; 152 } 153 154 155 void 156 DeviceOpener::Keep() 157 { 158 fDevice = -1; 159 } 160 161 162 /*! Returns the size of the device in bytes. It uses B_GET_GEOMETRY 163 to compute the size, or fstat() if that failed. 164 */ 165 status_t 166 DeviceOpener::GetSize(off_t* _size, uint32* _blockSize) 167 { 168 device_geometry geometry; 169 if (ioctl(fDevice, B_GET_GEOMETRY, &geometry) < 0) { 170 // maybe it's just a file 171 struct stat stat; 172 if (fstat(fDevice, &stat) < 0) 173 return B_ERROR; 174 175 if (_size) 176 *_size = stat.st_size; 177 if (_blockSize) // that shouldn't cause us any problems 178 *_blockSize = 512; 179 180 return B_OK; 181 } 182 183 if (_size) { 184 *_size = 1ULL * geometry.head_count * geometry.cylinder_count 185 * geometry.sectors_per_track * geometry.bytes_per_sector; 186 } 187 if (_blockSize) 188 *_blockSize = geometry.bytes_per_sector; 189 190 return B_OK; 191 } 192 193 194 // #pragma mark - 195 196 197 bool 198 btrfs_super_block::IsValid() const 199 { 200 // TODO: check some more values! 201 if (strncmp(magic, BTRFS_SUPER_BLOCK_MAGIC, sizeof(magic)) != 0) 202 return false; 203 204 return true; 205 } 206 207 208 void 209 btrfs_super_block::Initialize(const char* name, off_t numBlocks, 210 uint32 blockSize, uint32 sectorSize) 211 { 212 memset(this, 0, sizeof(btrfs_super_block)); 213 214 uuid_generate(fsid); 215 blocknum = B_HOST_TO_LENDIAN_INT64(BTRFS_SUPER_BLOCK_OFFSET); 216 num_devices = B_HOST_TO_LENDIAN_INT64(1); 217 strncpy(magic, BTRFS_SUPER_BLOCK_MAGIC_TEMPORARY, sizeof(magic)); 218 generation = B_HOST_TO_LENDIAN_INT64(1); 219 root = B_HOST_TO_LENDIAN_INT64(BTRFS_RESERVED_SPACE_OFFSET + blockSize); 220 chunk_root = B_HOST_TO_LENDIAN_INT64(Root() + blockSize); 221 total_size = B_HOST_TO_LENDIAN_INT64(numBlocks * blockSize); 222 used_size = B_HOST_TO_LENDIAN_INT64(6 * blockSize); 223 sector_size = B_HOST_TO_LENDIAN_INT32(sectorSize); 224 leaf_size = B_HOST_TO_LENDIAN_INT32(blockSize); 225 node_size = B_HOST_TO_LENDIAN_INT32(blockSize); 226 stripe_size = B_HOST_TO_LENDIAN_INT32(blockSize); 227 checksum_type = B_HOST_TO_LENDIAN_INT32(BTRFS_CSUM_TYPE_CRC32); 228 chunk_root_generation = B_HOST_TO_LENDIAN_INT64(1); 229 // TODO(lesderid): Support configurable filesystem features 230 incompat_flags = B_HOST_TO_LENDIAN_INT64(0); 231 strlcpy(label, name, BTRFS_LABEL_SIZE); 232 } 233 234 235 // #pragma mark - 236 237 238 Volume::Volume(fs_volume* volume) 239 : 240 fFSVolume(volume), 241 fFlags(0), 242 fChunk(NULL), 243 fChunkTree(NULL) 244 { 245 mutex_init(&fLock, "btrfs volume"); 246 } 247 248 249 Volume::~Volume() 250 { 251 TRACE("Volume destructor.\n"); 252 } 253 254 255 bool 256 Volume::IsValidSuperBlock() 257 { 258 return fSuperBlock.IsValid(); 259 } 260 261 262 const char* 263 Volume::Name() const 264 { 265 if (fSuperBlock.label[0]) 266 return fSuperBlock.label; 267 268 return fName; 269 } 270 271 272 status_t 273 Volume::Mount(const char* deviceName, uint32 flags) 274 { 275 flags |= B_MOUNT_READ_ONLY; 276 // we only support read-only for now 277 278 if ((flags & B_MOUNT_READ_ONLY) != 0) { 279 TRACE("Volume::Mount(): Read only\n"); 280 } else { 281 TRACE("Volume::Mount(): Read write\n"); 282 } 283 284 DeviceOpener opener(deviceName, (flags & B_MOUNT_READ_ONLY) != 0 285 ? O_RDONLY : O_RDWR); 286 fDevice = opener.Device(); 287 if (fDevice < B_OK) { 288 ERROR("Volume::Mount(): couldn't open device\n"); 289 return fDevice; 290 } 291 292 if (opener.IsReadOnly()) 293 fFlags |= VOLUME_READ_ONLY; 294 295 // read the superblock 296 status_t status = Identify(fDevice, &fSuperBlock); 297 if (status != B_OK) { 298 ERROR("Volume::Mount(): Identify() failed\n"); 299 return status; 300 } 301 302 fBlockSize = fSuperBlock.BlockSize(); 303 fSectorSize = fSuperBlock.SectorSize(); 304 TRACE("block size %" B_PRIu32 "\n", fBlockSize); 305 TRACE("sector size %" B_PRIu32 "\n", fSectorSize); 306 307 uint8* start = (uint8*)&fSuperBlock.system_chunk_array[0]; 308 uint8* end = (uint8*)&fSuperBlock.system_chunk_array[2048]; 309 while (start < end) { 310 btrfs_key* key = (btrfs_key*)start; 311 TRACE("system_chunk_array object_id 0x%" B_PRIx64 " offset 0x%" 312 B_PRIx64 " type 0x%x\n", key->ObjectID(), key->Offset(), 313 key->Type()); 314 if (key->Type() != BTRFS_KEY_TYPE_CHUNK_ITEM) { 315 break; 316 } 317 318 btrfs_chunk* chunk = (btrfs_chunk*)(key + 1); 319 fChunk = new(std::nothrow) Chunk(chunk, key->Offset()); 320 if (fChunk == NULL) 321 return B_ERROR; 322 start += sizeof(btrfs_key) + fChunk->Size(); 323 } 324 325 // check if the device size is large enough to hold the file system 326 off_t diskSize; 327 status = opener.GetSize(&diskSize); 328 if (status != B_OK) 329 return status; 330 if (diskSize < (off_t)fSuperBlock.TotalSize()) 331 return B_BAD_VALUE; 332 333 fBlockCache = opener.InitCache(fSuperBlock.TotalSize() / fBlockSize, 334 fBlockSize); 335 if (fBlockCache == NULL) 336 return B_ERROR; 337 338 TRACE("Volume::Mount(): Initialized block cache: %p\n", fBlockCache); 339 340 fChunkTree = new(std::nothrow) BTree(this); 341 if (fChunkTree == NULL) 342 return B_NO_MEMORY; 343 fChunkTree->SetRoot(fSuperBlock.ChunkRoot(), NULL); 344 TRACE("Volume::Mount() chunk_root: %" B_PRIu64 " (physical block %" B_PRIu64 345 ")\n", fSuperBlock.ChunkRoot(), fChunkTree->RootBlock()); 346 347 fRootTree = new(std::nothrow) BTree(this); 348 if (fRootTree == NULL) 349 return B_NO_MEMORY; 350 fRootTree->SetRoot(fSuperBlock.Root(), NULL); 351 TRACE("Volume::Mount() root: %" B_PRIu64 " (physical block %" B_PRIu64 ")\n", 352 fSuperBlock.Root(), fRootTree->RootBlock()); 353 354 BTree::Path path(fRootTree); 355 356 TRACE("Volume::Mount(): Searching extent root\n"); 357 btrfs_key search_key; 358 search_key.SetOffset(0); 359 search_key.SetType(BTRFS_KEY_TYPE_ROOT_ITEM); 360 search_key.SetObjectID(BTRFS_OBJECT_ID_EXTENT_TREE); 361 btrfs_root* root; 362 status = fRootTree->FindExact(&path, search_key, (void**)&root); 363 if (status != B_OK) { 364 ERROR("Volume::Mount(): Couldn't find extent root\n"); 365 return status; 366 } 367 TRACE("Volume::Mount(): Found extent root: %" B_PRIu64 "\n", 368 root->LogicalAddress()); 369 fExtentTree = new(std::nothrow) BTree(this); 370 if (fExtentTree == NULL) 371 return B_NO_MEMORY; 372 fExtentTree->SetRoot(root->LogicalAddress(), NULL); 373 free(root); 374 375 TRACE("Volume::Mount(): Searching fs root\n"); 376 search_key.SetOffset(0); 377 search_key.SetObjectID(BTRFS_OBJECT_ID_FS_TREE); 378 status = fRootTree->FindExact(&path, search_key, (void**)&root); 379 if (status != B_OK) { 380 ERROR("Volume::Mount(): Couldn't find fs root\n"); 381 return status; 382 } 383 TRACE("Volume::Mount(): Found fs root: %" B_PRIu64 "\n", 384 root->LogicalAddress()); 385 fFSTree = new(std::nothrow) BTree(this); 386 if (fFSTree == NULL) 387 return B_NO_MEMORY; 388 fFSTree->SetRoot(root->LogicalAddress(), NULL); 389 free(root); 390 391 TRACE("Volume::Mount(): Searching dev root\n"); 392 search_key.SetOffset(0); 393 search_key.SetObjectID(BTRFS_OBJECT_ID_DEV_TREE); 394 status = fRootTree->FindExact(&path, search_key, (void**)&root); 395 if (status != B_OK) { 396 ERROR("Volume::Mount(): Couldn't find dev root\n"); 397 return status; 398 } 399 TRACE("Volume::Mount(): Found dev root: %" B_PRIu64 "\n", 400 root->LogicalAddress()); 401 fDevTree = new(std::nothrow) BTree(this); 402 if (fDevTree == NULL) 403 return B_NO_MEMORY; 404 fDevTree->SetRoot(root->LogicalAddress(), NULL); 405 free(root); 406 407 TRACE("Volume::Mount(): Searching checksum root\n"); 408 search_key.SetOffset(0); 409 search_key.SetObjectID(BTRFS_OBJECT_ID_CHECKSUM_TREE); 410 status = fRootTree->FindExact(&path, search_key, (void**)&root); 411 if (status != B_OK) { 412 ERROR("Volume::Mount(): Couldn't find checksum root\n"); 413 return status; 414 } 415 TRACE("Volume::Mount(): Found checksum root: %" B_PRIu64 "\n", 416 root->LogicalAddress()); 417 fChecksumTree = new(std::nothrow) BTree(this); 418 if (fChecksumTree == NULL) 419 return B_NO_MEMORY; 420 fChecksumTree->SetRoot(root->LogicalAddress(), NULL); 421 free(root); 422 423 search_key.SetObjectID(-1); 424 search_key.SetType(0); 425 status = fFSTree->FindPrevious(&path, search_key, NULL); 426 if (status != B_OK) { 427 ERROR("Volume::Mount() Couldn't find any inode!!\n"); 428 return status; 429 } 430 fLargestInodeID = search_key.ObjectID(); 431 TRACE("Volume::Mount() Find larget inode id % " B_PRIu64 "\n", 432 fLargestInodeID); 433 434 // Initialize Journal 435 fJournal = new(std::nothrow) Journal(this); 436 if (fJournal == NULL) 437 return B_NO_MEMORY; 438 439 // Initialize ExtentAllocator; 440 fExtentAllocator = new(std::nothrow) ExtentAllocator(this); 441 if (fExtentAllocator == NULL) 442 return B_NO_MEMORY; 443 status = fExtentAllocator->Initialize(); 444 if (status != B_OK) { 445 ERROR("could not initalize extent allocator!\n"); 446 return status; 447 } 448 449 // ready 450 status = get_vnode(fFSVolume, BTRFS_FIRST_SUBVOLUME, 451 (void**)&fRootNode); 452 if (status != B_OK) { 453 ERROR("could not create root node: get_vnode() failed!\n"); 454 return status; 455 } 456 457 TRACE("Volume::Mount(): Found root node: %" B_PRIu64 " (%s)\n", 458 fRootNode->ID(), strerror(fRootNode->InitCheck())); 459 460 // all went fine 461 opener.Keep(); 462 463 if (!fSuperBlock.label[0]) { 464 // generate a more or less descriptive volume name 465 off_t divisor = 1ULL << 40; 466 char unit = 'T'; 467 if (diskSize < divisor) { 468 divisor = 1UL << 30; 469 unit = 'G'; 470 if (diskSize < divisor) { 471 divisor = 1UL << 20; 472 unit = 'M'; 473 } 474 } 475 476 double size = double((10 * diskSize + divisor - 1) / divisor); 477 // %g in the kernel does not support precision... 478 479 snprintf(fName, sizeof(fName), "%g %cB Btrfs Volume", 480 size / 10, unit); 481 } 482 483 return B_OK; 484 } 485 486 487 status_t 488 Volume::Initialize(int fd, const char* label, uint32 blockSize, 489 uint32 sectorSize) 490 { 491 TRACE("Volume::Initialize()\n"); 492 493 // label must != NULL and may not contain '/' or '\\' 494 if (label == NULL 495 || strchr(label, '/') != NULL || strchr(label, '\\') != NULL) { 496 return B_BAD_VALUE; 497 } 498 499 if ((blockSize != 1024 && blockSize != 2048 && blockSize != 4096 500 && blockSize != 8192 && blockSize != 16384) 501 || blockSize % sectorSize != 0) { 502 return B_BAD_VALUE; 503 } 504 505 DeviceOpener opener(fd, O_RDWR); 506 if (opener.Device() < B_OK) 507 return B_BAD_VALUE; 508 509 if (opener.IsReadOnly()) 510 return B_READ_ONLY_DEVICE; 511 512 fDevice = opener.Device(); 513 514 uint32 deviceBlockSize; 515 off_t deviceSize; 516 if (opener.GetSize(&deviceSize, &deviceBlockSize) < B_OK) 517 return B_ERROR; 518 off_t numBlocks = deviceSize / sectorSize; 519 520 // create valid superblock 521 522 fSuperBlock.Initialize(label, numBlocks, blockSize, sectorSize); 523 524 fBlockSize = fSuperBlock.BlockSize(); 525 fSectorSize = fSuperBlock.SectorSize(); 526 527 // TODO(lesderid): Initialize remaining core structures 528 // (extent tree, chunk tree, fs tree, etc.) 529 530 status_t status = WriteSuperBlock(); 531 if (status < B_OK) 532 return status; 533 534 fBlockCache = opener.InitCache(fSuperBlock.TotalSize() / fBlockSize, 535 fBlockSize); 536 if (fBlockCache == NULL) 537 return B_ERROR; 538 539 fJournal = new(std::nothrow) Journal(this); 540 if (fJournal == NULL) 541 RETURN_ERROR(B_ERROR); 542 543 // TODO(lesderid): Perform secondary initialization (in transactions) 544 // (add block groups to extent tree, create root dir, etc.) 545 Transaction transaction(this); 546 547 // TODO(lesderid): Write all superblocks when transactions are committed 548 status = transaction.Done(); 549 if (status < B_OK) 550 return status; 551 552 opener.RemoveCache(true); 553 554 TRACE("Volume::Initialize(): Done\n"); 555 return B_OK; 556 } 557 558 559 status_t 560 Volume::Unmount() 561 { 562 TRACE("Volume::Unmount()\n"); 563 delete fRootTree; 564 delete fExtentTree; 565 delete fChunkTree; 566 delete fChecksumTree; 567 delete fFSTree; 568 delete fDevTree; 569 delete fJournal; 570 delete fExtentAllocator; 571 fRootTree = NULL; 572 fExtentTree = NULL; 573 fChunkTree = NULL; 574 fChecksumTree = NULL; 575 fFSTree = NULL; 576 fDevTree = NULL; 577 fJournal = NULL; 578 fExtentAllocator = NULL; 579 580 TRACE("Volume::Unmount(): Putting root node\n"); 581 put_vnode(fFSVolume, RootNode()->ID()); 582 TRACE("Volume::Unmount(): Deleting the block cache\n"); 583 block_cache_delete(fBlockCache, !IsReadOnly()); 584 TRACE("Volume::Unmount(): Closing device\n"); 585 close(fDevice); 586 587 TRACE("Volume::Unmount(): Done\n"); 588 return B_OK; 589 } 590 591 592 status_t 593 Volume::LoadSuperBlock() 594 { 595 CachedBlock cached(this); 596 const uint8* block = cached.SetTo(BTRFS_SUPER_BLOCK_OFFSET / fBlockSize); 597 598 if (block == NULL) 599 return B_IO_ERROR; 600 601 memcpy(&fSuperBlock, block + BTRFS_SUPER_BLOCK_OFFSET % fBlockSize, 602 sizeof(fSuperBlock)); 603 604 return B_OK; 605 } 606 607 608 status_t 609 Volume::FindBlock(off_t logical, fsblock_t& physicalBlock) 610 { 611 off_t physical; 612 status_t status = FindBlock(logical, physical); 613 if (status != B_OK) 614 return status; 615 physicalBlock = physical / fBlockSize; 616 return B_OK; 617 } 618 619 620 status_t 621 Volume::FindBlock(off_t logical, off_t& physical) 622 { 623 if (fChunkTree == NULL 624 || (logical >= (off_t)fChunk->Offset() 625 && logical < (off_t)fChunk->End())) { 626 // try with fChunk 627 return fChunk->FindBlock(logical, physical); 628 } 629 630 btrfs_key search_key; 631 search_key.SetOffset(logical); 632 search_key.SetType(BTRFS_KEY_TYPE_CHUNK_ITEM); 633 search_key.SetObjectID(BTRFS_OBJECT_ID_FIRST_CHUNK_TREE); 634 btrfs_chunk* chunk; 635 BTree::Path path(fChunkTree); 636 status_t status = fChunkTree->FindPrevious(&path, search_key, 637 (void**)&chunk); 638 if (status != B_OK) 639 return status; 640 641 Chunk _chunk(chunk, search_key.Offset()); 642 free(chunk); 643 status = _chunk.FindBlock(logical, physical); 644 if (status != B_OK) 645 return status; 646 TRACE("Volume::FindBlock(): logical: %" B_PRIdOFF ", physical: %" B_PRIdOFF 647 "\n", logical, physical); 648 return B_OK; 649 } 650 651 652 status_t 653 Volume::WriteSuperBlock() 654 { 655 // TODO(lesderid): Calculate checksum 656 657 if (write_pos(fDevice, BTRFS_SUPER_BLOCK_OFFSET, &fSuperBlock, 658 sizeof(btrfs_super_block)) 659 != sizeof(btrfs_super_block)) 660 return B_IO_ERROR; 661 662 return B_OK; 663 } 664 665 666 /* Wrapper function for allocating new block 667 */ 668 status_t 669 Volume::GetNewBlock(uint64& logical, fsblock_t& physical, uint64 start, 670 uint64 flags) 671 { 672 status_t status = fExtentAllocator->AllocateTreeBlock(logical, start, flags); 673 if (status != B_OK) 674 return status; 675 676 return FindBlock(logical, physical); 677 } 678 679 680 // #pragma mark - Disk scanning and initialization 681 682 683 /*static*/ status_t 684 Volume::Identify(int fd, btrfs_super_block* superBlock) 685 { 686 if (read_pos(fd, BTRFS_SUPER_BLOCK_OFFSET, superBlock, 687 sizeof(btrfs_super_block)) != sizeof(btrfs_super_block)) 688 return B_IO_ERROR; 689 690 if (!superBlock->IsValid()) { 691 ERROR("invalid superblock!\n"); 692 return B_BAD_VALUE; 693 } 694 695 return B_OK; 696 } 697 698