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