1 /* 2 * Copyright 2008-2010, Axel Dörfler, axeld@pinc-software.de. 3 * This file may be used under the terms of the MIT License. 4 */ 5 6 7 //! Super block, mounting, etc. 8 9 10 #include "Volume.h" 11 12 #include <errno.h> 13 #include <new> 14 #include <stdio.h> 15 #include <stdlib.h> 16 #include <string.h> 17 18 #include <fs_cache.h> 19 #include <fs_volume.h> 20 21 #include <util/AutoLock.h> 22 23 #include "CachedBlock.h" 24 #include "Inode.h" 25 #include "InodeJournal.h" 26 #include "NoJournal.h" 27 28 29 //#define TRACE_EXT2 30 #ifdef TRACE_EXT2 31 # define TRACE(x...) dprintf("\33[34mext2:\33[0m " x) 32 #else 33 # define TRACE(x...) ; 34 #endif 35 # define FATAL(x...) dprintf("\33[34mext2:\33[0m " x) 36 37 38 class DeviceOpener { 39 public: 40 DeviceOpener(int fd, int mode); 41 DeviceOpener(const char* device, int mode); 42 ~DeviceOpener(); 43 44 int Open(const char* device, int mode); 45 int Open(int fd, int mode); 46 void* InitCache(off_t numBlocks, uint32 blockSize); 47 void RemoveCache(bool allowWrites); 48 49 void Keep(); 50 51 int Device() const { return fDevice; } 52 int Mode() const { return fMode; } 53 bool IsReadOnly() const { return _IsReadOnly(fMode); } 54 55 status_t GetSize(off_t* _size, uint32* _blockSize = NULL); 56 57 private: 58 static bool _IsReadOnly(int mode) 59 { return (mode & O_RWMASK) == O_RDONLY;} 60 static bool _IsReadWrite(int mode) 61 { return (mode & O_RWMASK) == O_RDWR;} 62 63 int fDevice; 64 int fMode; 65 void* fBlockCache; 66 }; 67 68 69 DeviceOpener::DeviceOpener(const char* device, int mode) 70 : 71 fBlockCache(NULL) 72 { 73 Open(device, mode); 74 } 75 76 77 DeviceOpener::DeviceOpener(int fd, int mode) 78 : 79 fBlockCache(NULL) 80 { 81 Open(fd, mode); 82 } 83 84 85 DeviceOpener::~DeviceOpener() 86 { 87 if (fDevice >= 0) { 88 RemoveCache(false); 89 close(fDevice); 90 } 91 } 92 93 94 int 95 DeviceOpener::Open(const char* device, int mode) 96 { 97 fDevice = open(device, mode | O_NOCACHE); 98 if (fDevice < 0) 99 fDevice = errno; 100 101 if (fDevice < 0 && _IsReadWrite(mode)) { 102 // try again to open read-only (don't rely on a specific error code) 103 return Open(device, O_RDONLY | O_NOCACHE); 104 } 105 106 if (fDevice >= 0) { 107 // opening succeeded 108 fMode = mode; 109 if (_IsReadWrite(mode)) { 110 // check out if the device really allows for read/write access 111 device_geometry geometry; 112 if (!ioctl(fDevice, B_GET_GEOMETRY, &geometry)) { 113 if (geometry.read_only) { 114 // reopen device read-only 115 close(fDevice); 116 return Open(device, O_RDONLY | O_NOCACHE); 117 } 118 } 119 } 120 } 121 122 return fDevice; 123 } 124 125 126 int 127 DeviceOpener::Open(int fd, int mode) 128 { 129 fDevice = dup(fd); 130 if (fDevice < 0) 131 return errno; 132 133 fMode = mode; 134 135 return fDevice; 136 } 137 138 139 void* 140 DeviceOpener::InitCache(off_t numBlocks, uint32 blockSize) 141 { 142 return fBlockCache = block_cache_create(fDevice, numBlocks, blockSize, 143 IsReadOnly()); 144 } 145 146 147 void 148 DeviceOpener::RemoveCache(bool allowWrites) 149 { 150 if (fBlockCache == NULL) 151 return; 152 153 block_cache_delete(fBlockCache, allowWrites); 154 fBlockCache = NULL; 155 } 156 157 158 void 159 DeviceOpener::Keep() 160 { 161 fDevice = -1; 162 } 163 164 165 /*! Returns the size of the device in bytes. It uses B_GET_GEOMETRY 166 to compute the size, or fstat() if that failed. 167 */ 168 status_t 169 DeviceOpener::GetSize(off_t* _size, uint32* _blockSize) 170 { 171 device_geometry geometry; 172 if (ioctl(fDevice, B_GET_GEOMETRY, &geometry) < 0) { 173 // maybe it's just a file 174 struct stat stat; 175 if (fstat(fDevice, &stat) < 0) 176 return B_ERROR; 177 178 if (_size) 179 *_size = stat.st_size; 180 if (_blockSize) // that shouldn't cause us any problems 181 *_blockSize = 512; 182 183 return B_OK; 184 } 185 186 if (_size) { 187 *_size = 1LL * geometry.head_count * geometry.cylinder_count 188 * geometry.sectors_per_track * geometry.bytes_per_sector; 189 } 190 if (_blockSize) 191 *_blockSize = geometry.bytes_per_sector; 192 193 return B_OK; 194 } 195 196 197 // #pragma mark - 198 199 200 bool 201 ext2_super_block::IsValid() 202 { 203 // TODO: check some more values! 204 if (Magic() != (uint32)EXT2_SUPER_BLOCK_MAGIC) 205 return false; 206 207 return true; 208 } 209 210 211 // #pragma mark - 212 213 214 Volume::Volume(fs_volume* volume) 215 : 216 fFSVolume(volume), 217 fBlockAllocator(this), 218 fInodeAllocator(this), 219 fJournalInode(NULL), 220 fFlags(0), 221 fGroupBlocks(NULL), 222 fRootNode(NULL) 223 { 224 mutex_init(&fLock, "ext2 volume"); 225 } 226 227 228 Volume::~Volume() 229 { 230 TRACE("Volume destructor.\n"); 231 if (fGroupBlocks != NULL) { 232 uint32 blockCount = (fNumGroups + fGroupsPerBlock - 1) 233 / fGroupsPerBlock; 234 for (uint32 i = 0; i < blockCount; i++) { 235 free(fGroupBlocks[i]); 236 } 237 238 free(fGroupBlocks); 239 } 240 } 241 242 243 bool 244 Volume::IsValidSuperBlock() 245 { 246 return fSuperBlock.IsValid(); 247 } 248 249 250 bool 251 Volume::HasExtendedAttributes() const 252 { 253 return (fSuperBlock.CompatibleFeatures() & EXT2_FEATURE_EXT_ATTR) != 0; 254 } 255 256 257 const char* 258 Volume::Name() const 259 { 260 if (fSuperBlock.name[0]) 261 return fSuperBlock.name; 262 263 return fName; 264 } 265 266 267 status_t 268 Volume::Mount(const char* deviceName, uint32 flags) 269 { 270 // flags |= B_MOUNT_READ_ONLY; 271 // we only support read-only for now 272 273 if ((flags & B_MOUNT_READ_ONLY) != 0) { 274 TRACE("Volume::Mount(): Read only\n"); 275 } else { 276 TRACE("Volume::Mount(): Read write\n"); 277 } 278 279 DeviceOpener opener(deviceName, (flags & B_MOUNT_READ_ONLY) != 0 280 ? O_RDONLY : O_RDWR); 281 fDevice = opener.Device(); 282 if (fDevice < B_OK) 283 return fDevice; 284 285 if (opener.IsReadOnly()) 286 fFlags |= VOLUME_READ_ONLY; 287 288 TRACE("features %lx, incompatible features %lx, read-only features %lx\n", 289 fSuperBlock.CompatibleFeatures(), fSuperBlock.IncompatibleFeatures(), 290 fSuperBlock.ReadOnlyFeatures()); 291 292 // read the super block 293 status_t status = Identify(fDevice, &fSuperBlock); 294 if (status != B_OK) 295 return status; 296 297 // check read-only features if mounting read-write 298 if (!IsReadOnly() && _UnsupportedReadOnlyFeatures(fSuperBlock) != 0) 299 return B_NOT_SUPPORTED; 300 301 // initialize short hands to the super block (to save byte swapping) 302 fBlockShift = fSuperBlock.BlockShift(); 303 fBlockSize = 1UL << fSuperBlock.BlockShift(); 304 fFirstDataBlock = fSuperBlock.FirstDataBlock(); 305 306 fFreeBlocks = fSuperBlock.FreeBlocks(); 307 fFreeInodes = fSuperBlock.FreeInodes(); 308 309 uint32 numBlocks = fSuperBlock.NumBlocks() - fFirstDataBlock; 310 uint32 blocksPerGroup = fSuperBlock.BlocksPerGroup(); 311 fNumGroups = numBlocks / blocksPerGroup; 312 if (numBlocks % blocksPerGroup != 0) 313 fNumGroups++; 314 315 fGroupsPerBlock = fBlockSize / sizeof(ext2_block_group); 316 fNumInodes = fSuperBlock.NumInodes(); 317 318 TRACE("block size %ld, num groups %ld, groups per block %ld, first %lu\n", 319 fBlockSize, fNumGroups, fGroupsPerBlock, fFirstDataBlock); 320 321 uint32 blockCount = (fNumGroups + fGroupsPerBlock - 1) / fGroupsPerBlock; 322 323 fGroupBlocks = (ext2_block_group**)malloc(blockCount * sizeof(void*)); 324 if (fGroupBlocks == NULL) 325 return B_NO_MEMORY; 326 327 memset(fGroupBlocks, 0, blockCount * sizeof(void*)); 328 fInodesPerBlock = fBlockSize / InodeSize(); 329 330 // check if the device size is large enough to hold the file system 331 off_t diskSize; 332 status = opener.GetSize(&diskSize); 333 if (status != B_OK) 334 return status; 335 if (diskSize < (NumBlocks() << BlockShift())) 336 return B_BAD_VALUE; 337 338 fBlockCache = opener.InitCache(NumBlocks(), fBlockSize); 339 if (fBlockCache == NULL) 340 return B_ERROR; 341 342 TRACE("Volume::Mount(): Initialized block cache: %p\n", fBlockCache); 343 344 // initialize journal if mounted read-write 345 if (!IsReadOnly() && 346 (fSuperBlock.CompatibleFeatures() & EXT2_FEATURE_HAS_JOURNAL) != 0) { 347 // TODO: There should be a mount option to ignore the existent journal 348 if (fSuperBlock.JournalInode() != 0) { 349 fJournalInode = new(std::nothrow) Inode(this, 350 fSuperBlock.JournalInode()); 351 352 if (fJournalInode == NULL) 353 return B_NO_MEMORY; 354 355 TRACE("Opening an on disk, inode mapped journal.\n"); 356 fJournal = new(std::nothrow) InodeJournal(fJournalInode); 357 } else { 358 // TODO: external journal 359 TRACE("Can not open an external journal.\n"); 360 return B_NOT_SUPPORTED; 361 } 362 } else { 363 TRACE("Opening a fake journal (NoJournal).\n"); 364 fJournal = new(std::nothrow) NoJournal(this); 365 } 366 367 if (fJournal == NULL) { 368 TRACE("No memory to create the journal\n"); 369 return B_NO_MEMORY; 370 } 371 372 TRACE("Volume::Mount(): Checking if journal was initialized\n"); 373 status = fJournal->InitCheck(); 374 if (status != B_OK) { 375 FATAL("could not initialize journal!\n"); 376 return status; 377 } 378 379 // TODO: Only recover if asked to 380 TRACE("Volume::Mount(): Asking journal to recover\n"); 381 status = fJournal->Recover(); 382 if (status != B_OK) { 383 FATAL("could not recover journal!\n"); 384 return status; 385 } 386 387 TRACE("Volume::Mount(): Restart journal log\n"); 388 status = fJournal->StartLog(); 389 if (status != B_OK) { 390 FATAL("could not initialize start journal!\n"); 391 return status; 392 } 393 394 // Initialize allocators 395 TRACE("Volume::Mount(): Initialize block allocator\n"); 396 status = fBlockAllocator.Initialize(); 397 if (status != B_OK) { 398 FATAL("could not initialize block allocator!\n"); 399 return status; 400 } 401 402 // ready 403 status = get_vnode(fFSVolume, EXT2_ROOT_NODE, (void**)&fRootNode); 404 if (status != B_OK) { 405 FATAL("could not create root node: get_vnode() failed!\n"); 406 return status; 407 } 408 409 // all went fine 410 opener.Keep(); 411 412 if (!fSuperBlock.name[0]) { 413 // generate a more or less descriptive volume name 414 uint32 divisor = 1UL << 30; 415 char unit = 'G'; 416 if (diskSize < divisor) { 417 divisor = 1UL << 20; 418 unit = 'M'; 419 } 420 421 double size = double((10 * diskSize + divisor - 1) / divisor); 422 // %g in the kernel does not support precision... 423 424 snprintf(fName, sizeof(fName), "%g %cB Ext2 Volume", 425 size / 10, unit); 426 } 427 428 return B_OK; 429 } 430 431 432 status_t 433 Volume::Unmount() 434 { 435 TRACE("Volume::Unmount()\n"); 436 437 status_t status = fJournal->Uninit(); 438 439 delete fJournal; 440 delete fJournalInode; 441 442 TRACE("Volume::Unmount(): Putting root node\n"); 443 put_vnode(fFSVolume, RootNode()->ID()); 444 TRACE("Volume::Unmount(): Deleting the block cache\n"); 445 block_cache_delete(fBlockCache, !IsReadOnly()); 446 TRACE("Volume::Unmount(): Closing device\n"); 447 close(fDevice); 448 449 TRACE("Volume::Unmount(): Done\n"); 450 return status; 451 } 452 453 454 status_t 455 Volume::GetInodeBlock(ino_t id, uint32& block) 456 { 457 ext2_block_group* group; 458 status_t status = GetBlockGroup((id - 1) / fSuperBlock.InodesPerGroup(), 459 &group); 460 if (status != B_OK) 461 return status; 462 463 block = group->InodeTable() 464 + ((id - 1) % fSuperBlock.InodesPerGroup()) / fInodesPerBlock; 465 return B_OK; 466 } 467 468 469 uint32 470 Volume::InodeBlockIndex(ino_t id) const 471 { 472 return ((id - 1) % fSuperBlock.InodesPerGroup()) % fInodesPerBlock; 473 } 474 475 476 /*static*/ uint32 477 Volume::_UnsupportedIncompatibleFeatures(ext2_super_block& superBlock) 478 { 479 uint32 supportedIncompatible = EXT2_INCOMPATIBLE_FEATURE_FILE_TYPE 480 | EXT2_INCOMPATIBLE_FEATURE_RECOVER 481 | EXT2_INCOMPATIBLE_FEATURE_JOURNAL 482 /*| EXT2_INCOMPATIBLE_FEATURE_META_GROUP*/; 483 uint32 unsupported = superBlock.IncompatibleFeatures() 484 & ~supportedIncompatible; 485 486 if (unsupported != 0) { 487 FATAL("ext2: incompatible features not supported: %lx (extents %x)\n", 488 unsupported, EXT2_INCOMPATIBLE_FEATURE_EXTENTS); 489 } 490 491 return unsupported; 492 } 493 494 495 /*static*/ uint32 496 Volume::_UnsupportedReadOnlyFeatures(ext2_super_block& superBlock) 497 { 498 uint32 supportedReadOnly = EXT2_READ_ONLY_FEATURE_SPARSE_SUPER 499 | EXT2_READ_ONLY_FEATURE_LARGE_FILE 500 | EXT2_READ_ONLY_FEATURE_HUGE_FILE 501 | EXT2_READ_ONLY_FEATURE_EXTRA_ISIZE; 502 // TODO actually implement EXT2_READ_ONLY_FEATURE_SPARSE_SUPER when 503 // implementing superblock backup copies 504 505 uint32 unsupported = superBlock.ReadOnlyFeatures() & ~supportedReadOnly; 506 507 if (unsupported != 0) 508 FATAL("ext2: readonly features not supported: %lx\n", unsupported); 509 510 return unsupported; 511 } 512 513 514 uint32 515 Volume::_GroupDescriptorBlock(uint32 blockIndex) 516 { 517 if ((fSuperBlock.IncompatibleFeatures() 518 & EXT2_INCOMPATIBLE_FEATURE_META_GROUP) == 0 519 || blockIndex < fSuperBlock.FirstMetaBlockGroup()) 520 return fFirstDataBlock + blockIndex + 1; 521 522 panic("meta block"); 523 return 0; 524 } 525 526 527 /*! Makes the requested block group available. 528 The block groups are loaded on demand, but are kept in memory until the 529 volume is unmounted; therefore we don't use the block cache. 530 */ 531 status_t 532 Volume::GetBlockGroup(int32 index, ext2_block_group** _group) 533 { 534 if (index < 0 || (uint32)index > fNumGroups) 535 return B_BAD_VALUE; 536 537 int32 blockIndex = index / fGroupsPerBlock; 538 539 MutexLocker _(fLock); 540 541 if (fGroupBlocks[blockIndex] == NULL) { 542 CachedBlock cached(this); 543 const uint8* block = cached.SetTo(_GroupDescriptorBlock(blockIndex)); 544 if (block == NULL) 545 return B_IO_ERROR; 546 547 ext2_block_group* groupBlock = (ext2_block_group*)malloc(fBlockSize); 548 if (groupBlock == NULL) 549 return B_NO_MEMORY; 550 551 memcpy((uint8*)groupBlock, block, fBlockSize); 552 553 fGroupBlocks[blockIndex] = groupBlock; 554 555 TRACE("group [%ld]: inode table %ld\n", index, 556 (fGroupBlocks[blockIndex] + index % fGroupsPerBlock)->InodeTable()); 557 } 558 559 *_group = fGroupBlocks[blockIndex] + index % fGroupsPerBlock; 560 return B_OK; 561 } 562 563 564 status_t 565 Volume::WriteBlockGroup(Transaction& transaction, int32 index) 566 { 567 if (index < 0 || (uint32)index > fNumGroups) 568 return B_BAD_VALUE; 569 570 TRACE("Volume::WriteBlockGroup()\n"); 571 572 int32 blockIndex = index / fGroupsPerBlock; 573 574 MutexLocker _(fLock); 575 576 if (fGroupBlocks[blockIndex] == NULL) 577 return B_BAD_VALUE; 578 579 CachedBlock cached(this); 580 uint8* block = cached.SetToWritable(transaction, 581 _GroupDescriptorBlock(blockIndex)); 582 if (block == NULL) 583 return B_IO_ERROR; 584 585 memcpy(block, (const uint8*)fGroupBlocks[blockIndex], fBlockSize); 586 587 // TODO: Write copies 588 589 return B_OK; 590 } 591 592 593 status_t 594 Volume::ActivateLargeFiles(Transaction& transaction) 595 { 596 if ((fSuperBlock.ReadOnlyFeatures() 597 & EXT2_READ_ONLY_FEATURE_LARGE_FILE) != 0) 598 return B_OK; 599 600 fSuperBlock.SetReadOnlyFeatures(fSuperBlock.ReadOnlyFeatures() 601 | EXT2_READ_ONLY_FEATURE_LARGE_FILE); 602 603 return WriteSuperBlock(transaction); 604 } 605 606 607 status_t 608 Volume::SaveOrphan(Transaction& transaction, ino_t newID, ino_t& oldID) 609 { 610 oldID = fSuperBlock.LastOrphan(); 611 TRACE("Volume::SaveOrphan(): Old: %d, New: %d\n", (int)oldID, (int)newID); 612 fSuperBlock.SetLastOrphan(newID); 613 614 return WriteSuperBlock(transaction); 615 } 616 617 618 status_t 619 Volume::RemoveOrphan(Transaction& transaction, ino_t id) 620 { 621 ino_t currentID = fSuperBlock.LastOrphan(); 622 TRACE("Volume::RemoveOrphan(): ID: %d\n", (int)id); 623 if (currentID == 0) 624 return B_OK; 625 626 CachedBlock cached(this); 627 628 uint32 blockNum; 629 status_t status = GetInodeBlock(currentID, blockNum); 630 if (status != B_OK) 631 return status; 632 633 uint8* block = cached.SetToWritable(transaction, blockNum); 634 if (block == NULL) 635 return B_IO_ERROR; 636 637 ext2_inode* inode = (ext2_inode*)(block 638 + InodeBlockIndex(currentID) * InodeSize()); 639 640 if (currentID == id) { 641 TRACE("Volume::RemoveOrphan(): First entry. Updating head to: %d\n", 642 (int)inode->NextOrphan()); 643 fSuperBlock.SetLastOrphan(inode->NextOrphan()); 644 645 return WriteSuperBlock(transaction); 646 } 647 648 currentID = inode->NextOrphan(); 649 if (currentID == 0) 650 return B_OK; 651 652 do { 653 uint32 lastBlockNum = blockNum; 654 status = GetInodeBlock(currentID, blockNum); 655 if (status != B_OK) 656 return status; 657 658 if (blockNum != lastBlockNum) { 659 block = cached.SetToWritable(transaction, blockNum); 660 if (block == NULL) 661 return B_IO_ERROR; 662 } 663 664 ext2_inode* inode = (ext2_inode*)(block 665 + InodeBlockIndex(currentID) * InodeSize()); 666 667 currentID = inode->NextOrphan(); 668 if (currentID == 0) 669 return B_OK; 670 } while(currentID != id); 671 672 CachedBlock cachedRemoved(this); 673 674 status = GetInodeBlock(id, blockNum); 675 if (status != B_OK) 676 return status; 677 678 uint8* removedBlock = cachedRemoved.SetToWritable(transaction, blockNum); 679 if (removedBlock == NULL) 680 return B_IO_ERROR; 681 682 ext2_inode* removedInode = (ext2_inode*)(removedBlock 683 + InodeBlockIndex(id) * InodeSize()); 684 685 // Next orphan is stored inside deletion time 686 inode->deletion_time = removedInode->deletion_time; 687 TRACE("Volume::RemoveOrphan(): Updated pointer to %d\n", 688 (int)inode->NextOrphan()); 689 690 return status; 691 } 692 693 694 status_t 695 Volume::AllocateInode(Transaction& transaction, Inode* parent, int32 mode, 696 ino_t& id) 697 { 698 status_t status = fInodeAllocator.New(transaction, parent, mode, id); 699 if (status != B_OK) 700 return status; 701 702 --fFreeInodes; 703 704 return WriteSuperBlock(transaction); 705 } 706 707 708 status_t 709 Volume::FreeInode(Transaction& transaction, ino_t id, bool isDirectory) 710 { 711 status_t status = fInodeAllocator.Free(transaction, id, isDirectory); 712 if (status != B_OK) 713 return status; 714 715 ++fFreeInodes; 716 717 return WriteSuperBlock(transaction); 718 } 719 720 721 status_t 722 Volume::AllocateBlocks(Transaction& transaction, uint32 minimum, uint32 maximum, 723 uint32& blockGroup, uint32& start, uint32& length) 724 { 725 TRACE("Volume::AllocateBlocks()\n"); 726 if (IsReadOnly()) 727 return B_READ_ONLY_DEVICE; 728 729 TRACE("Volume::AllocateBlocks(): Calling the block allocator\n"); 730 731 status_t status = fBlockAllocator.AllocateBlocks(transaction, minimum, 732 maximum, blockGroup, start, length); 733 if (status != B_OK) 734 return status; 735 736 TRACE("Volume::AllocateBlocks(): Allocated %lu blocks\n", length); 737 738 fFreeBlocks -= length; 739 740 return WriteSuperBlock(transaction); 741 } 742 743 744 status_t 745 Volume::FreeBlocks(Transaction& transaction, uint32 start, uint32 length) 746 { 747 TRACE("Volume::FreeBlocks(%lu, %lu)\n", start, length); 748 if (IsReadOnly()) 749 return B_READ_ONLY_DEVICE; 750 751 status_t status = fBlockAllocator.Free(transaction, start, length); 752 if (status != B_OK) 753 return status; 754 755 TRACE("Volume::FreeBlocks(): number of free blocks (before): %lu\n", 756 fFreeBlocks); 757 fFreeBlocks += length; 758 TRACE("Volume::FreeBlocks(): number of free blocks (after): %lu\n", 759 fFreeBlocks); 760 761 return WriteSuperBlock(transaction); 762 } 763 764 765 status_t 766 Volume::LoadSuperBlock() 767 { 768 CachedBlock cached(this); 769 const uint8* block = cached.SetTo(fFirstDataBlock); 770 771 if (block == NULL) 772 return B_IO_ERROR; 773 774 if (fFirstDataBlock == 0) 775 memcpy(&fSuperBlock, block + 1024, sizeof(fSuperBlock)); 776 else 777 memcpy(&fSuperBlock, block, sizeof(fSuperBlock)); 778 779 fFreeBlocks = fSuperBlock.FreeBlocks(); 780 fFreeInodes = fSuperBlock.FreeInodes(); 781 782 return B_OK; 783 } 784 785 786 status_t 787 Volume::WriteSuperBlock(Transaction& transaction) 788 { 789 TRACE("Volume::WriteSuperBlock()\n"); 790 fSuperBlock.SetFreeBlocks(fFreeBlocks); 791 fSuperBlock.SetFreeInodes(fFreeInodes); 792 // TODO: Rest of fields that can be modified 793 794 TRACE("Volume::WriteSuperBlock(): free blocks: %lu, free inodes: %lu\n", 795 fSuperBlock.FreeBlocks(), fSuperBlock.FreeInodes()); 796 797 CachedBlock cached(this); 798 uint8* block = cached.SetToWritable(transaction, fFirstDataBlock); 799 800 if (block == NULL) 801 return B_IO_ERROR; 802 803 TRACE("Volume::WriteSuperBlock(): first data block: %lu, block: %p, " 804 "superblock: %p\n", fFirstDataBlock, block, &fSuperBlock); 805 806 if (fFirstDataBlock == 0) 807 memcpy(block + 1024, &fSuperBlock, sizeof(fSuperBlock)); 808 else 809 memcpy(block, &fSuperBlock, sizeof(fSuperBlock)); 810 811 TRACE("Volume::WriteSuperBlock(): Done\n"); 812 813 return B_OK; 814 } 815 816 817 status_t 818 Volume::FlushDevice() 819 { 820 TRACE("Volume::FlushDevice(): %p, %p\n", this, fBlockCache); 821 return block_cache_sync(fBlockCache); 822 } 823 824 825 status_t 826 Volume::Sync() 827 { 828 TRACE("Volume::Sync()\n"); 829 return fJournal->FlushLogAndBlocks(); 830 } 831 832 833 // #pragma mark - Disk scanning and initialization 834 835 836 /*static*/ status_t 837 Volume::Identify(int fd, ext2_super_block* superBlock) 838 { 839 if (read_pos(fd, EXT2_SUPER_BLOCK_OFFSET, superBlock, 840 sizeof(ext2_super_block)) != sizeof(ext2_super_block)) 841 return B_IO_ERROR; 842 843 if (!superBlock->IsValid()) { 844 FATAL("invalid super block!\n"); 845 return B_BAD_VALUE; 846 } 847 848 return _UnsupportedIncompatibleFeatures(*superBlock) == 0 849 ? B_OK : B_NOT_SUPPORTED; 850 } 851 852 853 void 854 Volume::TransactionDone(bool success) 855 { 856 if (!success) { 857 status_t status = LoadSuperBlock(); 858 if (status != B_OK) 859 panic("Failed to reload ext2 superblock.\n"); 860 } 861 } 862 863 864 void 865 Volume::RemovedFromTransaction() 866 { 867 // TODO: Does it make a difference? 868 } 869