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