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 "CachedBlock.h" 25 #include "CRCTable.h" 26 #include "DeviceOpener.h" 27 #include "Inode.h" 28 #include "InodeJournal.h" 29 #include "NoJournal.h" 30 31 32 //#define TRACE_EXT2 33 #ifdef TRACE_EXT2 34 # define TRACE(x...) dprintf("\33[34mext2:\33[0m " x) 35 #else 36 # define TRACE(x...) ; 37 #endif 38 # define FATAL(x...) dprintf("\33[34mext2:\33[0m " x) 39 40 41 // #pragma mark - 42 43 44 bool 45 ext2_super_block::IsValid() 46 { 47 if (Magic() != (uint32)EXT2_SUPER_BLOCK_MAGIC 48 || BlockShift() > 16 49 || BlocksPerGroup() != (1UL << BlockShift()) * 8 50 || InodeSize() > (1UL << BlockShift()) 51 || RevisionLevel() > EXT2_MAX_REVISION 52 || ReservedGDTBlocks() > (1UL << BlockShift()) / 4 53 || NumInodes() == 0 54 || InodeSize() == 0 55 || FreeInodes() > NumInodes()) { 56 return false; 57 } 58 59 return true; 60 } 61 62 63 // #pragma mark - 64 65 66 Volume::Volume(fs_volume* volume) 67 : 68 fFSVolume(volume), 69 fBlockAllocator(NULL), 70 fInodeAllocator(this), 71 fJournalInode(NULL), 72 fFlags(0), 73 fGroupBlocks(NULL), 74 fRootNode(NULL) 75 { 76 mutex_init(&fLock, "ext2 volume"); 77 } 78 79 80 Volume::~Volume() 81 { 82 TRACE("Volume destructor.\n"); 83 delete fBlockAllocator; 84 if (fGroupBlocks != NULL) { 85 uint32 blockCount = (fNumGroups + fGroupsPerBlock - 1) 86 / fGroupsPerBlock; 87 for (uint32 i = 0; i < blockCount; i++) 88 free(fGroupBlocks[i]); 89 90 free(fGroupBlocks); 91 } 92 } 93 94 95 bool 96 Volume::IsValidSuperBlock() 97 { 98 return fSuperBlock.IsValid(); 99 } 100 101 102 bool 103 Volume::HasExtendedAttributes() const 104 { 105 return (fSuperBlock.CompatibleFeatures() & EXT2_FEATURE_EXT_ATTR) != 0; 106 } 107 108 109 const char* 110 Volume::Name() const 111 { 112 if (fSuperBlock.name[0]) 113 return fSuperBlock.name; 114 115 return fName; 116 } 117 118 119 void 120 Volume::SetName(const char* name) 121 { 122 strlcpy(fSuperBlock.name, name, sizeof(fSuperBlock.name)); 123 } 124 125 126 status_t 127 Volume::Mount(const char* deviceName, uint32 flags) 128 { 129 // flags |= B_MOUNT_READ_ONLY; 130 // we only support read-only for now 131 132 if ((flags & B_MOUNT_READ_ONLY) != 0) { 133 TRACE("Volume::Mount(): Read only\n"); 134 } else { 135 TRACE("Volume::Mount(): Read write\n"); 136 } 137 138 DeviceOpener opener(deviceName, (flags & B_MOUNT_READ_ONLY) != 0 139 ? O_RDONLY : O_RDWR); 140 fDevice = opener.Device(); 141 if (fDevice < B_OK) { 142 FATAL("Volume::Mount(): couldn't open device\n"); 143 return fDevice; 144 } 145 146 if (opener.IsReadOnly()) 147 fFlags |= VOLUME_READ_ONLY; 148 149 // read the superblock 150 status_t status = Identify(fDevice, &fSuperBlock); 151 if (status != B_OK) { 152 FATAL("Volume::Mount(): Identify() failed\n"); 153 return status; 154 } 155 156 TRACE("features %" B_PRIx32 ", incompatible features %" B_PRIx32 157 ", read-only features %" B_PRIx32 "\n", 158 fSuperBlock.CompatibleFeatures(), fSuperBlock.IncompatibleFeatures(), 159 fSuperBlock.ReadOnlyFeatures()); 160 161 // check read-only features if mounting read-write 162 if (!IsReadOnly() && _UnsupportedReadOnlyFeatures(fSuperBlock) != 0) 163 return B_UNSUPPORTED; 164 165 if (HasMetaGroupChecksumFeature() && HasChecksumFeature()) 166 return B_ERROR; 167 168 if (!_VerifySuperBlock()) 169 return B_ERROR; 170 171 if ((fSuperBlock.State() & EXT2_FS_STATE_VALID) == 0 172 || (fSuperBlock.State() & EXT2_FS_STATE_ERROR) != 0) { 173 if (!IsReadOnly()) { 174 FATAL("Volume::Mount(): can't mount R/W, volume not clean\n"); 175 return B_NOT_ALLOWED; 176 } else { 177 FATAL("Volume::Mount(): warning: volume not clean\n"); 178 } 179 } 180 181 // initialize short hands to the superblock (to save byte swapping) 182 fBlockShift = fSuperBlock.BlockShift(); 183 if (fBlockShift < 10 || fBlockShift > 16) 184 return B_ERROR; 185 if (fBlockShift > 12) { 186 FATAL("blocksize not supported!\n"); 187 return B_UNSUPPORTED; 188 } 189 fBlockSize = 1UL << fBlockShift; 190 fFirstDataBlock = fSuperBlock.FirstDataBlock(); 191 192 fFreeBlocks = fSuperBlock.FreeBlocks(Has64bitFeature()); 193 fFreeInodes = fSuperBlock.FreeInodes(); 194 195 if (fFirstDataBlock > fSuperBlock.NumBlocks(Has64bitFeature())) 196 return B_ERROR; 197 198 off_t numBlocks = fSuperBlock.NumBlocks(Has64bitFeature()) - fFirstDataBlock; 199 uint32 blocksPerGroup = fSuperBlock.BlocksPerGroup(); 200 fNumGroups = numBlocks / blocksPerGroup; 201 if (numBlocks % blocksPerGroup != 0) 202 fNumGroups++; 203 204 if (blocksPerGroup == 0 || fSuperBlock.FragmentsPerGroup() == 0) { 205 FATAL("zero blocksPerGroup or fragmentsPerGroup!\n"); 206 return B_UNSUPPORTED; 207 } 208 if (blocksPerGroup != fSuperBlock.FragmentsPerGroup()) { 209 FATAL("blocksPerGroup is not equal to fragmentsPerGroup!\n"); 210 return B_UNSUPPORTED; 211 } 212 213 if (Has64bitFeature()) { 214 TRACE("64bits\n"); 215 fGroupDescriptorSize = fSuperBlock.GroupDescriptorSize(); 216 if (fGroupDescriptorSize < sizeof(ext2_block_group)) 217 return B_ERROR; 218 if (fGroupDescriptorSize != sizeof(ext2_block_group)) 219 return B_UNSUPPORTED; 220 } else 221 fGroupDescriptorSize = EXT2_BLOCK_GROUP_NORMAL_SIZE; 222 fGroupsPerBlock = fBlockSize / fGroupDescriptorSize; 223 fNumInodes = fSuperBlock.NumInodes(); 224 225 TRACE("block size %" B_PRIu32 ", num groups %" B_PRIu32 ", groups per " 226 "block %" B_PRIu32 ", first %" B_PRIu32 ", group_descriptor_size %" 227 B_PRIu32 "\n", fBlockSize, fNumGroups, 228 fGroupsPerBlock, fFirstDataBlock, fGroupDescriptorSize); 229 230 uint32 blockCount = (fNumGroups + fGroupsPerBlock - 1) / fGroupsPerBlock; 231 232 fGroupBlocks = (uint8**)malloc(blockCount * sizeof(uint8*)); 233 if (fGroupBlocks == NULL) 234 return B_NO_MEMORY; 235 236 memset(fGroupBlocks, 0, blockCount * sizeof(uint8*)); 237 fInodesPerBlock = fBlockSize / InodeSize(); 238 239 _SuperBlockChecksumSeed(); 240 241 // check if the device size is large enough to hold the file system 242 off_t diskSize; 243 status = opener.GetSize(&diskSize); 244 if (status != B_OK) 245 return status; 246 if ((diskSize + fBlockSize) <= ((off_t)NumBlocks() << BlockShift())) { 247 FATAL("diskSize is too small for the number of blocks!\n"); 248 return B_BAD_VALUE; 249 } 250 251 fBlockCache = opener.InitCache(NumBlocks(), fBlockSize); 252 if (fBlockCache == NULL) 253 return B_ERROR; 254 255 TRACE("Volume::Mount(): Initialized block cache: %p\n", fBlockCache); 256 257 // initialize journal if mounted read-write 258 if (!IsReadOnly() && HasJournalFeature()) { 259 // TODO: There should be a mount option to ignore the existent journal 260 if (fSuperBlock.JournalInode() != 0) { 261 fJournalInode = new(std::nothrow) Inode(this, 262 fSuperBlock.JournalInode()); 263 264 if (fJournalInode == NULL) 265 return B_NO_MEMORY; 266 267 TRACE("Opening an on disk, inode mapped journal.\n"); 268 fJournal = new(std::nothrow) InodeJournal(fJournalInode); 269 } else { 270 // TODO: external journal 271 TRACE("Can not open an external journal.\n"); 272 return B_UNSUPPORTED; 273 } 274 } else { 275 TRACE("Opening a fake journal (NoJournal).\n"); 276 fJournal = new(std::nothrow) NoJournal(this); 277 } 278 279 if (fJournal == NULL) { 280 TRACE("No memory to create the journal\n"); 281 return B_NO_MEMORY; 282 } 283 284 TRACE("Volume::Mount(): Checking if journal was initialized\n"); 285 status = fJournal->InitCheck(); 286 if (status != B_OK) { 287 FATAL("could not initialize journal!\n"); 288 return status; 289 } 290 291 // TODO: Only recover if asked to 292 TRACE("Volume::Mount(): Asking journal to recover\n"); 293 status = fJournal->Recover(); 294 if (status != B_OK) { 295 FATAL("could not recover journal!\n"); 296 return status; 297 } 298 299 TRACE("Volume::Mount(): Restart journal log\n"); 300 status = fJournal->StartLog(); 301 if (status != B_OK) { 302 FATAL("could not initialize start journal!\n"); 303 return status; 304 } 305 306 if (!IsReadOnly()) { 307 // Initialize allocators 308 fBlockAllocator = new(std::nothrow) BlockAllocator(this); 309 if (fBlockAllocator != NULL) { 310 TRACE("Volume::Mount(): Initialize block allocator\n"); 311 status = fBlockAllocator->Initialize(); 312 } 313 if (fBlockAllocator == NULL || status != B_OK) { 314 delete fBlockAllocator; 315 fBlockAllocator = NULL; 316 FATAL("could not initialize block allocator, going read-only!\n"); 317 fFlags |= VOLUME_READ_ONLY; 318 fJournal->Uninit(); 319 delete fJournal; 320 delete fJournalInode; 321 fJournalInode = NULL; 322 fJournal = new(std::nothrow) NoJournal(this); 323 } 324 } 325 326 // ready 327 status = get_vnode(fFSVolume, EXT2_ROOT_NODE, (void**)&fRootNode); 328 if (status != B_OK) { 329 FATAL("could not create root node: get_vnode() failed!\n"); 330 return status; 331 } 332 333 // all went fine 334 opener.Keep(); 335 336 if (!fSuperBlock.name[0]) { 337 // generate a more or less descriptive volume name 338 off_t divisor = 1ULL << 40; 339 char unit = 'T'; 340 if (diskSize < divisor) { 341 divisor = 1UL << 30; 342 unit = 'G'; 343 if (diskSize < divisor) { 344 divisor = 1UL << 20; 345 unit = 'M'; 346 } 347 } 348 349 double size = double((10 * diskSize + divisor - 1) / divisor); 350 // %g in the kernel does not support precision... 351 352 snprintf(fName, sizeof(fName), "%g %cB Ext2 Volume", 353 size / 10, unit); 354 } 355 356 return B_OK; 357 } 358 359 360 status_t 361 Volume::Unmount() 362 { 363 TRACE("Volume::Unmount()\n"); 364 365 status_t status = fJournal->Uninit(); 366 367 // this will wait on the block notifier/writer thread 368 FlushDevice(); 369 delete fJournal; 370 delete fJournalInode; 371 372 TRACE("Volume::Unmount(): Putting root node\n"); 373 put_vnode(fFSVolume, RootNode()->ID()); 374 TRACE("Volume::Unmount(): Deleting the block cache\n"); 375 block_cache_delete(fBlockCache, !IsReadOnly()); 376 TRACE("Volume::Unmount(): Closing device\n"); 377 close(fDevice); 378 379 TRACE("Volume::Unmount(): Done\n"); 380 return status; 381 } 382 383 384 status_t 385 Volume::GetInodeBlock(ino_t id, off_t& block) 386 { 387 ext2_block_group* group; 388 status_t status = GetBlockGroup((id - 1) / fSuperBlock.InodesPerGroup(), 389 &group); 390 if (status != B_OK) 391 return status; 392 393 block = group->InodeTable(Has64bitFeature()) 394 + ((id - 1) % fSuperBlock.InodesPerGroup()) / fInodesPerBlock; 395 if (block < 0) 396 return B_BAD_DATA; 397 return B_OK; 398 } 399 400 401 uint32 402 Volume::InodeBlockIndex(ino_t id) const 403 { 404 return ((id - 1) % fSuperBlock.InodesPerGroup()) % fInodesPerBlock; 405 } 406 407 408 /*static*/ uint32 409 Volume::_UnsupportedIncompatibleFeatures(ext2_super_block& superBlock) 410 { 411 uint32 supportedIncompatible = EXT2_INCOMPATIBLE_FEATURE_FILE_TYPE 412 | EXT2_INCOMPATIBLE_FEATURE_RECOVER 413 | EXT2_INCOMPATIBLE_FEATURE_JOURNAL 414 | EXT2_INCOMPATIBLE_FEATURE_EXTENTS 415 | EXT2_INCOMPATIBLE_FEATURE_FLEX_GROUP 416 | EXT2_INCOMPATIBLE_FEATURE_64BIT; 417 /*| EXT2_INCOMPATIBLE_FEATURE_META_GROUP*/; 418 uint32 unsupported = superBlock.IncompatibleFeatures() 419 & ~supportedIncompatible; 420 421 if (unsupported != 0) { 422 FATAL("ext2: incompatible features not supported: %" B_PRIx32 423 " (extents %x)\n", unsupported, EXT2_INCOMPATIBLE_FEATURE_EXTENTS); 424 } 425 426 return unsupported; 427 } 428 429 430 /*static*/ uint32 431 Volume::_UnsupportedReadOnlyFeatures(ext2_super_block& superBlock) 432 { 433 uint32 supportedReadOnly = EXT2_READ_ONLY_FEATURE_SPARSE_SUPER 434 | EXT2_READ_ONLY_FEATURE_LARGE_FILE 435 | EXT2_READ_ONLY_FEATURE_HUGE_FILE 436 | EXT2_READ_ONLY_FEATURE_EXTRA_ISIZE 437 | EXT2_READ_ONLY_FEATURE_DIR_NLINK 438 | EXT2_READ_ONLY_FEATURE_GDT_CSUM 439 | EXT4_READ_ONLY_FEATURE_METADATA_CSUM; 440 // TODO actually implement EXT2_READ_ONLY_FEATURE_SPARSE_SUPER when 441 // implementing superblock backup copies 442 443 uint32 unsupported = superBlock.ReadOnlyFeatures() & ~supportedReadOnly; 444 445 if (unsupported != 0) { 446 FATAL("ext2: readonly features not supported: %" B_PRIx32 "\n", 447 unsupported); 448 } 449 450 return unsupported; 451 } 452 453 454 uint32 455 Volume::_GroupDescriptorBlock(uint32 blockIndex) 456 { 457 if ((fSuperBlock.IncompatibleFeatures() 458 & EXT2_INCOMPATIBLE_FEATURE_META_GROUP) == 0 459 || blockIndex < fSuperBlock.FirstMetaBlockGroup()) 460 return fFirstDataBlock + blockIndex + 1; 461 462 panic("meta block"); 463 return 0; 464 } 465 466 467 uint16 468 Volume::_GroupCheckSum(ext2_block_group *group, int32 index) 469 { 470 int32 number = B_HOST_TO_LENDIAN_INT32(index); 471 size_t offset = offsetof(ext2_block_group, checksum); 472 size_t offsetExt4 = offsetof(ext2_block_group, block_bitmap_high); 473 if (HasMetaGroupChecksumFeature()) { 474 uint16 dummy = 0; 475 uint32 checksum = calculate_crc32c(fChecksumSeed, (uint8*)&number, 476 sizeof(number)); 477 checksum = calculate_crc32c(checksum, (uint8*)group, offset); 478 checksum = calculate_crc32c(checksum, (uint8*)&dummy, sizeof(dummy)); 479 if (fGroupDescriptorSize > offset + sizeof(dummy)) { 480 checksum = calculate_crc32c(checksum, (uint8*)group + offsetExt4, 481 fGroupDescriptorSize - offsetExt4); 482 } 483 return checksum & 0xffff; 484 } else if (HasChecksumFeature()) { 485 uint16 checksum = calculate_crc(0xffff, fSuperBlock.uuid, 486 sizeof(fSuperBlock.uuid)); 487 checksum = calculate_crc(checksum, (uint8*)&number, sizeof(number)); 488 checksum = calculate_crc(checksum, (uint8*)group, offset); 489 if (Has64bitFeature() && fGroupDescriptorSize > offsetExt4) { 490 checksum = calculate_crc(checksum, (uint8*)group + offsetExt4, 491 fGroupDescriptorSize - offsetExt4); 492 } 493 return checksum; 494 } 495 return 0; 496 } 497 498 499 /*! Makes the requested block group available. 500 The block groups are loaded on demand, but are kept in memory until the 501 volume is unmounted; therefore we don't use the block cache. 502 */ 503 status_t 504 Volume::GetBlockGroup(int32 index, ext2_block_group** _group) 505 { 506 if (index < 0 || (uint32)index > fNumGroups) 507 return B_BAD_VALUE; 508 509 int32 blockIndex = index / fGroupsPerBlock; 510 int32 blockOffset = index % fGroupsPerBlock; 511 512 MutexLocker _(fLock); 513 514 if (fGroupBlocks[blockIndex] == NULL) { 515 CachedBlock cached(this); 516 const uint8* block = cached.SetTo(_GroupDescriptorBlock(blockIndex)); 517 if (block == NULL) 518 return B_IO_ERROR; 519 520 fGroupBlocks[blockIndex] = (uint8*)malloc(fBlockSize); 521 if (fGroupBlocks[blockIndex] == NULL) 522 return B_NO_MEMORY; 523 524 memcpy(fGroupBlocks[blockIndex], block, fBlockSize); 525 526 TRACE("group [%" B_PRId32 "]: inode table %" B_PRIu64 "\n", index, 527 ((ext2_block_group*)(fGroupBlocks[blockIndex] + blockOffset 528 * fGroupDescriptorSize))->InodeTable(Has64bitFeature())); 529 } 530 531 *_group = (ext2_block_group*)(fGroupBlocks[blockIndex] 532 + blockOffset * fGroupDescriptorSize); 533 if (HasChecksumFeature() 534 && (*_group)->checksum != _GroupCheckSum(*_group, index)) { 535 return B_BAD_DATA; 536 } 537 return B_OK; 538 } 539 540 541 status_t 542 Volume::WriteBlockGroup(Transaction& transaction, int32 index) 543 { 544 if (index < 0 || (uint32)index > fNumGroups) 545 return B_BAD_VALUE; 546 547 TRACE("Volume::WriteBlockGroup()\n"); 548 549 int32 blockIndex = index / fGroupsPerBlock; 550 int32 blockOffset = index % fGroupsPerBlock; 551 552 MutexLocker _(fLock); 553 554 if (fGroupBlocks[blockIndex] == NULL) 555 return B_BAD_VALUE; 556 557 ext2_block_group *group = (ext2_block_group*)(fGroupBlocks[blockIndex] 558 + blockOffset * fGroupDescriptorSize); 559 560 group->checksum = _GroupCheckSum(group, index); 561 TRACE("Volume::WriteBlockGroup() checksum 0x%x for group %" B_PRId32 " " 562 "(free inodes %" B_PRIu32 ", unused %" B_PRIu32 ")\n", group->checksum, 563 index, group->FreeInodes(Has64bitFeature()), 564 group->UnusedInodes(Has64bitFeature())); 565 566 CachedBlock cached(this); 567 uint8* block = cached.SetToWritable(transaction, 568 _GroupDescriptorBlock(blockIndex)); 569 if (block == NULL) 570 return B_IO_ERROR; 571 572 memcpy(block, (const uint8*)fGroupBlocks[blockIndex], fBlockSize); 573 574 // TODO: Write copies 575 576 return B_OK; 577 } 578 579 580 status_t 581 Volume::ActivateLargeFiles(Transaction& transaction) 582 { 583 if ((fSuperBlock.ReadOnlyFeatures() 584 & EXT2_READ_ONLY_FEATURE_LARGE_FILE) != 0) 585 return B_OK; 586 587 fSuperBlock.SetReadOnlyFeatures(fSuperBlock.ReadOnlyFeatures() 588 | EXT2_READ_ONLY_FEATURE_LARGE_FILE); 589 590 return WriteSuperBlock(transaction); 591 } 592 593 594 status_t 595 Volume::ActivateDirNLink(Transaction& transaction) 596 { 597 if ((fSuperBlock.ReadOnlyFeatures() 598 & EXT2_READ_ONLY_FEATURE_DIR_NLINK) != 0) 599 return B_OK; 600 601 fSuperBlock.SetReadOnlyFeatures(fSuperBlock.ReadOnlyFeatures() 602 | EXT2_READ_ONLY_FEATURE_DIR_NLINK); 603 604 return WriteSuperBlock(transaction); 605 } 606 607 608 status_t 609 Volume::SaveOrphan(Transaction& transaction, ino_t newID, ino_t& oldID) 610 { 611 oldID = fSuperBlock.LastOrphan(); 612 TRACE("Volume::SaveOrphan(): Old: %d, New: %d\n", (int)oldID, (int)newID); 613 fSuperBlock.SetLastOrphan(newID); 614 615 return WriteSuperBlock(transaction); 616 } 617 618 619 status_t 620 Volume::RemoveOrphan(Transaction& transaction, ino_t id) 621 { 622 ino_t currentID = fSuperBlock.LastOrphan(); 623 TRACE("Volume::RemoveOrphan(): ID: %d\n", (int)id); 624 if (currentID == 0) 625 return B_OK; 626 627 CachedBlock cached(this); 628 629 off_t blockNum; 630 status_t status = GetInodeBlock(currentID, blockNum); 631 if (status != B_OK) 632 return status; 633 634 uint8* block = cached.SetToWritable(transaction, blockNum); 635 if (block == NULL) 636 return B_IO_ERROR; 637 638 ext2_inode* inode = (ext2_inode*)(block 639 + InodeBlockIndex(currentID) * InodeSize()); 640 641 if (currentID == id) { 642 TRACE("Volume::RemoveOrphan(): First entry. Updating head to: %d\n", 643 (int)inode->NextOrphan()); 644 fSuperBlock.SetLastOrphan(inode->NextOrphan()); 645 646 return WriteSuperBlock(transaction); 647 } 648 649 currentID = inode->NextOrphan(); 650 if (currentID == 0) 651 return B_OK; 652 653 do { 654 off_t lastBlockNum = blockNum; 655 status = GetInodeBlock(currentID, blockNum); 656 if (status != B_OK) 657 return status; 658 659 if (blockNum != lastBlockNum) { 660 block = cached.SetToWritable(transaction, blockNum); 661 if (block == NULL) 662 return B_IO_ERROR; 663 } 664 665 ext2_inode* inode = (ext2_inode*)(block 666 + InodeBlockIndex(currentID) * InodeSize()); 667 668 currentID = inode->NextOrphan(); 669 if (currentID == 0) 670 return B_OK; 671 } while (currentID != id); 672 673 CachedBlock cachedRemoved(this); 674 675 status = GetInodeBlock(id, blockNum); 676 if (status != B_OK) 677 return status; 678 679 uint8* removedBlock = cachedRemoved.SetToWritable(transaction, blockNum); 680 if (removedBlock == NULL) 681 return B_IO_ERROR; 682 683 ext2_inode* removedInode = (ext2_inode*)(removedBlock 684 + InodeBlockIndex(id) * InodeSize()); 685 686 // Next orphan is stored inside deletion time 687 inode->deletion_time = removedInode->deletion_time; 688 TRACE("Volume::RemoveOrphan(): Updated pointer to %d\n", 689 (int)inode->NextOrphan()); 690 691 return status; 692 } 693 694 695 status_t 696 Volume::AllocateInode(Transaction& transaction, Inode* parent, int32 mode, 697 ino_t& id) 698 { 699 status_t status = fInodeAllocator.New(transaction, parent, mode, id); 700 if (status != B_OK) 701 return status; 702 703 --fFreeInodes; 704 705 return WriteSuperBlock(transaction); 706 } 707 708 709 status_t 710 Volume::FreeInode(Transaction& transaction, ino_t id, bool isDirectory) 711 { 712 status_t status = fInodeAllocator.Free(transaction, id, isDirectory); 713 if (status != B_OK) 714 return status; 715 716 ++fFreeInodes; 717 718 return WriteSuperBlock(transaction); 719 } 720 721 722 status_t 723 Volume::AllocateBlocks(Transaction& transaction, uint32 minimum, uint32 maximum, 724 uint32& blockGroup, fsblock_t& start, uint32& length) 725 { 726 TRACE("Volume::AllocateBlocks()\n"); 727 if (IsReadOnly()) 728 return B_READ_ONLY_DEVICE; 729 730 TRACE("Volume::AllocateBlocks(): Calling the block allocator\n"); 731 732 status_t status = fBlockAllocator->AllocateBlocks(transaction, minimum, 733 maximum, blockGroup, start, length); 734 if (status != B_OK) 735 return status; 736 737 TRACE("Volume::AllocateBlocks(): Allocated %" B_PRIu32 " blocks\n", 738 length); 739 740 fFreeBlocks -= length; 741 742 return WriteSuperBlock(transaction); 743 } 744 745 746 status_t 747 Volume::FreeBlocks(Transaction& transaction, fsblock_t start, uint32 length) 748 { 749 TRACE("Volume::FreeBlocks(%" B_PRIu64 ", %" B_PRIu32 ")\n", start, length); 750 if (IsReadOnly()) 751 return B_READ_ONLY_DEVICE; 752 753 status_t status = fBlockAllocator->Free(transaction, start, length); 754 if (status != B_OK) 755 return status; 756 757 TRACE("Volume::FreeBlocks(): number of free blocks (before): %" B_PRIdOFF 758 "\n", fFreeBlocks); 759 fFreeBlocks += length; 760 TRACE("Volume::FreeBlocks(): number of free blocks (after): %" B_PRIdOFF 761 "\n", fFreeBlocks); 762 763 return WriteSuperBlock(transaction); 764 } 765 766 767 status_t 768 Volume::LoadSuperBlock() 769 { 770 CachedBlock cached(this); 771 const uint8* block = cached.SetTo(fFirstDataBlock); 772 773 if (block == NULL) 774 return B_IO_ERROR; 775 776 if (fFirstDataBlock == 0) 777 memcpy(&fSuperBlock, block + 1024, sizeof(fSuperBlock)); 778 else 779 memcpy(&fSuperBlock, block, sizeof(fSuperBlock)); 780 781 fFreeBlocks = fSuperBlock.FreeBlocks(Has64bitFeature()); 782 fFreeInodes = fSuperBlock.FreeInodes(); 783 784 return B_OK; 785 } 786 787 788 status_t 789 Volume::WriteSuperBlock(Transaction& transaction) 790 { 791 TRACE("Volume::WriteSuperBlock()\n"); 792 fSuperBlock.SetFreeBlocks(fFreeBlocks, Has64bitFeature()); 793 fSuperBlock.SetFreeInodes(fFreeInodes); 794 // TODO: Rest of fields that can be modified 795 796 if (HasMetaGroupChecksumFeature()) { 797 fSuperBlock.checksum = calculate_crc32c(0xffffffff, (uint8*)&fSuperBlock, 798 offsetof(struct ext2_super_block, checksum)); 799 } 800 801 TRACE("Volume::WriteSuperBlock(): free blocks: %" B_PRIu64 ", free inodes:" 802 " %" B_PRIu32 "\n", fSuperBlock.FreeBlocks(Has64bitFeature()), 803 fSuperBlock.FreeInodes()); 804 805 CachedBlock cached(this); 806 uint8* block = cached.SetToWritable(transaction, fFirstDataBlock); 807 808 if (block == NULL) 809 return B_IO_ERROR; 810 811 TRACE("Volume::WriteSuperBlock(): first data block: %" B_PRIu32 ", block:" 812 " %p, superblock: %p\n", fFirstDataBlock, block, &fSuperBlock); 813 814 if (fFirstDataBlock == 0) 815 memcpy(block + 1024, &fSuperBlock, sizeof(fSuperBlock)); 816 else 817 memcpy(block, &fSuperBlock, sizeof(fSuperBlock)); 818 819 TRACE("Volume::WriteSuperBlock(): Done\n"); 820 821 return B_OK; 822 } 823 824 825 void 826 Volume::_SuperBlockChecksumSeed() 827 { 828 if (HasChecksumSeedFeature()) { 829 fChecksumSeed = fSuperBlock.checksum_seed; 830 } else if (HasMetaGroupChecksumFeature()) { 831 fChecksumSeed = calculate_crc32c(0xffffffff, (uint8*)fSuperBlock.uuid, 832 sizeof(fSuperBlock.uuid)); 833 } else 834 fChecksumSeed = 0; 835 } 836 837 838 bool 839 Volume::_VerifySuperBlock() 840 { 841 if (!HasMetaGroupChecksumFeature()) 842 return true; 843 844 uint32 checksum = calculate_crc32c(0xffffffff, (uint8*)&fSuperBlock, 845 offsetof(struct ext2_super_block, checksum)); 846 return checksum == fSuperBlock.checksum; 847 } 848 849 850 status_t 851 Volume::FlushDevice() 852 { 853 TRACE("Volume::FlushDevice(): %p, %p\n", this, fBlockCache); 854 return block_cache_sync(fBlockCache); 855 } 856 857 858 status_t 859 Volume::Sync() 860 { 861 TRACE("Volume::Sync()\n"); 862 return fJournal->Uninit(); 863 } 864 865 866 // #pragma mark - Disk scanning and initialization 867 868 869 /*static*/ status_t 870 Volume::Identify(int fd, ext2_super_block* superBlock) 871 { 872 if (read_pos(fd, EXT2_SUPER_BLOCK_OFFSET, superBlock, 873 sizeof(ext2_super_block)) != sizeof(ext2_super_block)) 874 return B_IO_ERROR; 875 876 if (!superBlock->IsValid()) { 877 FATAL("invalid superblock!\n"); 878 return B_BAD_VALUE; 879 } 880 881 if (_UnsupportedIncompatibleFeatures(*superBlock) != 0) 882 return B_UNSUPPORTED; 883 884 return B_OK; 885 } 886 887 888 void 889 Volume::TransactionDone(bool success) 890 { 891 if (!success) { 892 status_t status = LoadSuperBlock(); 893 if (status != B_OK) 894 panic("Failed to reload ext2 superblock.\n"); 895 } 896 } 897 898 899 void 900 Volume::RemovedFromTransaction() 901 { 902 // TODO: Does it make a difference? 903 } 904