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