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