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