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