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