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