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