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