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 "CRCTable.h" 19 #include "DebugSupport.h" 20 #include "ExtentAllocator.h" 21 #include "Inode.h" 22 #include "Journal.h" 23 #include "Utility.h" 24 25 26 //#define TRACE_BTRFS 27 #ifdef TRACE_BTRFS 28 # define TRACE(x...) dprintf("\33[34mbtrfs:\33[0m " x) 29 #else 30 # define TRACE(x...) ; 31 #endif 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, sizeof(device_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, sizeof(device_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 if ((flags & B_MOUNT_READ_ONLY) != 0) { 435 fJournal = NULL; 436 fExtentAllocator = NULL; 437 } else { 438 // Initialize Journal 439 fJournal = new(std::nothrow) Journal(this); 440 if (fJournal == NULL) 441 return B_NO_MEMORY; 442 443 // Initialize ExtentAllocator; 444 fExtentAllocator = new(std::nothrow) ExtentAllocator(this); 445 if (fExtentAllocator == NULL) 446 return B_NO_MEMORY; 447 status = fExtentAllocator->Initialize(); 448 if (status != B_OK) { 449 ERROR("could not initalize extent allocator!\n"); 450 return status; 451 } 452 } 453 454 // ready 455 status = get_vnode(fFSVolume, BTRFS_FIRST_SUBVOLUME, 456 (void**)&fRootNode); 457 if (status != B_OK) { 458 ERROR("could not create root node: get_vnode() failed!\n"); 459 return status; 460 } 461 462 TRACE("Volume::Mount(): Found root node: %" B_PRIu64 " (%s)\n", 463 fRootNode->ID(), strerror(fRootNode->InitCheck())); 464 465 // all went fine 466 opener.Keep(); 467 468 if (!fSuperBlock.label[0]) { 469 // generate a more or less descriptive volume name 470 off_t divisor = 1ULL << 40; 471 char unit = 'T'; 472 if (diskSize < divisor) { 473 divisor = 1UL << 30; 474 unit = 'G'; 475 if (diskSize < divisor) { 476 divisor = 1UL << 20; 477 unit = 'M'; 478 } 479 } 480 481 double size = double((10 * diskSize + divisor - 1) / divisor); 482 // %g in the kernel does not support precision... 483 484 snprintf(fName, sizeof(fName), "%g %cB Btrfs Volume", 485 size / 10, unit); 486 } 487 488 return B_OK; 489 } 490 491 492 status_t 493 Volume::Initialize(int fd, const char* label, uint32 blockSize, 494 uint32 sectorSize) 495 { 496 TRACE("Volume::Initialize()\n"); 497 498 // label must != NULL and may not contain '/' or '\\' 499 if (label == NULL 500 || strchr(label, '/') != NULL || strchr(label, '\\') != NULL) { 501 return B_BAD_VALUE; 502 } 503 504 if ((blockSize != 1024 && blockSize != 2048 && blockSize != 4096 505 && blockSize != 8192 && blockSize != 16384) 506 || blockSize % sectorSize != 0) { 507 return B_BAD_VALUE; 508 } 509 510 DeviceOpener opener(fd, O_RDWR); 511 if (opener.Device() < B_OK) 512 return B_BAD_VALUE; 513 514 if (opener.IsReadOnly()) 515 return B_READ_ONLY_DEVICE; 516 517 fDevice = opener.Device(); 518 519 uint32 deviceBlockSize; 520 off_t deviceSize; 521 if (opener.GetSize(&deviceSize, &deviceBlockSize) < B_OK) 522 return B_ERROR; 523 off_t numBlocks = deviceSize / sectorSize; 524 525 // create valid superblock 526 527 fSuperBlock.Initialize(label, numBlocks, blockSize, sectorSize); 528 529 fBlockSize = fSuperBlock.BlockSize(); 530 fSectorSize = fSuperBlock.SectorSize(); 531 532 // TODO(lesderid): Initialize remaining core structures 533 // (extent tree, chunk tree, fs tree, etc.) 534 535 status_t status = WriteSuperBlock(); 536 if (status < B_OK) 537 return status; 538 539 fBlockCache = opener.InitCache(fSuperBlock.TotalSize() / fBlockSize, 540 fBlockSize); 541 if (fBlockCache == NULL) 542 return B_ERROR; 543 544 fJournal = new(std::nothrow) Journal(this); 545 if (fJournal == NULL) 546 RETURN_ERROR(B_ERROR); 547 548 // TODO(lesderid): Perform secondary initialization (in transactions) 549 // (add block groups to extent tree, create root dir, etc.) 550 Transaction transaction(this); 551 552 // TODO(lesderid): Write all superblocks when transactions are committed 553 status = transaction.Done(); 554 if (status < B_OK) 555 return status; 556 557 opener.RemoveCache(true); 558 559 TRACE("Volume::Initialize(): Done\n"); 560 return B_OK; 561 } 562 563 564 status_t 565 Volume::Unmount() 566 { 567 TRACE("Volume::Unmount()\n"); 568 delete fRootTree; 569 delete fExtentTree; 570 delete fChunkTree; 571 delete fChecksumTree; 572 delete fFSTree; 573 delete fDevTree; 574 delete fJournal; 575 delete fExtentAllocator; 576 fRootTree = NULL; 577 fExtentTree = NULL; 578 fChunkTree = NULL; 579 fChecksumTree = NULL; 580 fFSTree = NULL; 581 fDevTree = NULL; 582 fJournal = NULL; 583 fExtentAllocator = NULL; 584 585 TRACE("Volume::Unmount(): Putting root node\n"); 586 put_vnode(fFSVolume, RootNode()->ID()); 587 TRACE("Volume::Unmount(): Deleting the block cache\n"); 588 block_cache_delete(fBlockCache, !IsReadOnly()); 589 TRACE("Volume::Unmount(): Closing device\n"); 590 close(fDevice); 591 592 TRACE("Volume::Unmount(): Done\n"); 593 return B_OK; 594 } 595 596 597 status_t 598 Volume::LoadSuperBlock() 599 { 600 CachedBlock cached(this); 601 const uint8* block = cached.SetTo(BTRFS_SUPER_BLOCK_OFFSET / fBlockSize); 602 603 if (block == NULL) 604 return B_IO_ERROR; 605 606 memcpy(&fSuperBlock, block + BTRFS_SUPER_BLOCK_OFFSET % fBlockSize, 607 sizeof(fSuperBlock)); 608 609 return B_OK; 610 } 611 612 613 status_t 614 Volume::FindBlock(off_t logical, fsblock_t& physicalBlock) 615 { 616 off_t physical; 617 status_t status = FindBlock(logical, physical); 618 if (status != B_OK) 619 return status; 620 physicalBlock = physical / fBlockSize; 621 return B_OK; 622 } 623 624 625 status_t 626 Volume::FindBlock(off_t logical, off_t& physical) 627 { 628 if (fChunkTree == NULL 629 || (logical >= (off_t)fChunk->Offset() 630 && logical < (off_t)fChunk->End())) { 631 // try with fChunk 632 return fChunk->FindBlock(logical, physical); 633 } 634 635 btrfs_key search_key; 636 search_key.SetOffset(logical); 637 search_key.SetType(BTRFS_KEY_TYPE_CHUNK_ITEM); 638 search_key.SetObjectID(BTRFS_OBJECT_ID_FIRST_CHUNK_TREE); 639 btrfs_chunk* chunk; 640 BTree::Path path(fChunkTree); 641 status_t status = fChunkTree->FindPrevious(&path, search_key, 642 (void**)&chunk); 643 if (status != B_OK) 644 return status; 645 646 Chunk _chunk(chunk, search_key.Offset()); 647 free(chunk); 648 status = _chunk.FindBlock(logical, physical); 649 if (status != B_OK) 650 return status; 651 TRACE("Volume::FindBlock(): logical: %" B_PRIdOFF ", physical: %" B_PRIdOFF 652 "\n", logical, physical); 653 return B_OK; 654 } 655 656 657 status_t 658 Volume::WriteSuperBlock() 659 { 660 uint32 checksum = calculate_crc((uint32)~1, 661 (uint8 *)(&fSuperBlock + sizeof(fSuperBlock.checksum)), 662 sizeof(fSuperBlock) - sizeof(fSuperBlock.checksum)); 663 664 fSuperBlock.checksum[0] = (checksum >> 0) & 0xFF; 665 fSuperBlock.checksum[1] = (checksum >> 8) & 0xFF; 666 fSuperBlock.checksum[2] = (checksum >> 16) & 0xFF; 667 fSuperBlock.checksum[3] = (checksum >> 24) & 0xFF; 668 669 if (write_pos(fDevice, BTRFS_SUPER_BLOCK_OFFSET, &fSuperBlock, 670 sizeof(btrfs_super_block)) 671 != sizeof(btrfs_super_block)) 672 return B_IO_ERROR; 673 674 return B_OK; 675 } 676 677 678 /* Wrapper function for allocating new block 679 */ 680 status_t 681 Volume::GetNewBlock(uint64& logical, fsblock_t& physical, uint64 start, 682 uint64 flags) 683 { 684 status_t status = fExtentAllocator->AllocateTreeBlock(logical, start, flags); 685 if (status != B_OK) 686 return status; 687 688 return FindBlock(logical, physical); 689 } 690 691 692 // #pragma mark - Disk scanning and initialization 693 694 695 /*static*/ status_t 696 Volume::Identify(int fd, btrfs_super_block* superBlock) 697 { 698 if (read_pos(fd, BTRFS_SUPER_BLOCK_OFFSET, superBlock, 699 sizeof(btrfs_super_block)) != sizeof(btrfs_super_block)) 700 return B_IO_ERROR; 701 702 if (!superBlock->IsValid()) { 703 ERROR("invalid superblock!\n"); 704 return B_BAD_VALUE; 705 } 706 707 return B_OK; 708 } 709 710