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 return status; 376 377 // TODO: Only recover if asked to 378 TRACE("Volume::Mount(): Asking journal to recover\n"); 379 status = fJournal->Recover(); 380 if (status != B_OK) 381 return status; 382 383 TRACE("Volume::Mount(): Restart journal log\n"); 384 status = fJournal->StartLog(); 385 if (status != B_OK) 386 return status; 387 388 // Initialize allocators 389 TRACE("Volume::Mount(): Initialize block allocator\n"); 390 status = fBlockAllocator.Initialize(); 391 if (status != B_OK) 392 return status; 393 394 // ready 395 status = get_vnode(fFSVolume, EXT2_ROOT_NODE, (void**)&fRootNode); 396 if (status != B_OK) { 397 TRACE("could not create root node: get_vnode() failed!\n"); 398 return status; 399 } 400 401 // all went fine 402 opener.Keep(); 403 404 if (!fSuperBlock.name[0]) { 405 // generate a more or less descriptive volume name 406 uint32 divisor = 1UL << 30; 407 char unit = 'G'; 408 if (diskSize < divisor) { 409 divisor = 1UL << 20; 410 unit = 'M'; 411 } 412 413 double size = double((10 * diskSize + divisor - 1) / divisor); 414 // %g in the kernel does not support precision... 415 416 snprintf(fName, sizeof(fName), "%g %cB Ext2 Volume", 417 size / 10, unit); 418 } 419 420 return B_OK; 421 } 422 423 424 status_t 425 Volume::Unmount() 426 { 427 TRACE("Volume::Unmount()\n"); 428 429 status_t status = fJournal->Uninit(); 430 431 delete fJournal; 432 delete fJournalInode; 433 434 TRACE("Volume::Unmount(): Putting root node\n"); 435 put_vnode(fFSVolume, RootNode()->ID()); 436 TRACE("Volume::Unmount(): Deleting the block cache\n"); 437 block_cache_delete(fBlockCache, !IsReadOnly()); 438 TRACE("Volume::Unmount(): Closing device\n"); 439 close(fDevice); 440 441 TRACE("Volume::Unmount(): Done\n"); 442 return status; 443 } 444 445 446 status_t 447 Volume::GetInodeBlock(ino_t id, uint32& block) 448 { 449 ext2_block_group* group; 450 status_t status = GetBlockGroup((id - 1) / fSuperBlock.InodesPerGroup(), 451 &group); 452 if (status != B_OK) 453 return status; 454 455 block = group->InodeTable() 456 + ((id - 1) % fSuperBlock.InodesPerGroup()) / fInodesPerBlock; 457 return B_OK; 458 } 459 460 461 uint32 462 Volume::InodeBlockIndex(ino_t id) const 463 { 464 return ((id - 1) % fSuperBlock.InodesPerGroup()) % fInodesPerBlock; 465 } 466 467 468 /*static*/ uint32 469 Volume::_UnsupportedIncompatibleFeatures(ext2_super_block& superBlock) 470 { 471 uint32 supportedIncompatible = EXT2_INCOMPATIBLE_FEATURE_FILE_TYPE 472 | EXT2_INCOMPATIBLE_FEATURE_RECOVER 473 | EXT2_INCOMPATIBLE_FEATURE_JOURNAL 474 /*| EXT2_INCOMPATIBLE_FEATURE_META_GROUP*/; 475 uint32 unsupported = superBlock.IncompatibleFeatures() 476 & ~supportedIncompatible; 477 478 if (unsupported != 0) { 479 FATAL("ext2: incompatible features not supported: %lx (extents %x)\n", 480 unsupported, EXT2_INCOMPATIBLE_FEATURE_EXTENTS); 481 } 482 483 return unsupported; 484 } 485 486 487 /*static*/ uint32 488 Volume::_UnsupportedReadOnlyFeatures(ext2_super_block& superBlock) 489 { 490 uint32 supportedReadOnly = EXT2_READ_ONLY_FEATURE_SPARSE_SUPER 491 | EXT2_READ_ONLY_FEATURE_HUGE_FILE; 492 // TODO actually implement EXT2_READ_ONLY_FEATURE_SPARSE_SUPER when 493 // implementing superblock backup copies 494 495 uint32 unsupported = superBlock.ReadOnlyFeatures() & ~supportedReadOnly; 496 497 if (unsupported != 0) 498 FATAL("ext2: readonly features not supported: %lx\n", unsupported); 499 500 return unsupported; 501 } 502 503 504 uint32 505 Volume::_GroupDescriptorBlock(uint32 blockIndex) 506 { 507 if ((fSuperBlock.IncompatibleFeatures() 508 & EXT2_INCOMPATIBLE_FEATURE_META_GROUP) == 0 509 || blockIndex < fSuperBlock.FirstMetaBlockGroup()) 510 return fFirstDataBlock + blockIndex + 1; 511 512 panic("meta block"); 513 return 0; 514 } 515 516 517 /*! Makes the requested block group available. 518 The block groups are loaded on demand, but are kept in memory until the 519 volume is unmounted; therefore we don't use the block cache. 520 */ 521 status_t 522 Volume::GetBlockGroup(int32 index, ext2_block_group** _group) 523 { 524 if (index < 0 || (uint32)index > fNumGroups) 525 return B_BAD_VALUE; 526 527 int32 blockIndex = index / fGroupsPerBlock; 528 529 MutexLocker _(fLock); 530 531 if (fGroupBlocks[blockIndex] == NULL) { 532 CachedBlock cached(this); 533 const uint8* block = cached.SetTo(_GroupDescriptorBlock(blockIndex)); 534 if (block == NULL) 535 return B_IO_ERROR; 536 537 ext2_block_group* groupBlock = (ext2_block_group*)malloc(fBlockSize); 538 if (groupBlock == NULL) 539 return B_NO_MEMORY; 540 541 memcpy((uint8*)groupBlock, block, fBlockSize); 542 543 fGroupBlocks[blockIndex] = groupBlock; 544 545 TRACE("group [%ld]: inode table %ld\n", index, 546 (fGroupBlocks[blockIndex] + index % fGroupsPerBlock)->InodeTable()); 547 } 548 549 *_group = fGroupBlocks[blockIndex] + index % fGroupsPerBlock; 550 return B_OK; 551 } 552 553 554 status_t 555 Volume::WriteBlockGroup(Transaction& transaction, int32 index) 556 { 557 if (index < 0 || (uint32)index > fNumGroups) 558 return B_BAD_VALUE; 559 560 TRACE("Volume::WriteBlockGroup()\n"); 561 562 int32 blockIndex = index / fGroupsPerBlock; 563 564 MutexLocker _(fLock); 565 566 if (fGroupBlocks[blockIndex] == NULL) 567 return B_BAD_VALUE; 568 569 CachedBlock cached(this); 570 uint8* block = cached.SetToWritable(transaction, 571 _GroupDescriptorBlock(blockIndex)); 572 if (block == NULL) 573 return B_IO_ERROR; 574 575 memcpy(block, (const uint8*)fGroupBlocks[blockIndex], fBlockSize); 576 577 // TODO: Write copies 578 579 return B_OK; 580 } 581 582 583 status_t 584 Volume::SaveOrphan(Transaction& transaction, ino_t newID, ino_t& oldID) 585 { 586 oldID = fSuperBlock.LastOrphan(); 587 TRACE("Volume::SaveOrphan(): Old: %d, New: %d\n", (int)oldID, (int)newID); 588 fSuperBlock.SetLastOrphan(newID); 589 590 return WriteSuperBlock(transaction); 591 } 592 593 594 status_t 595 Volume::RemoveOrphan(Transaction& transaction, ino_t id) 596 { 597 ino_t currentID = fSuperBlock.LastOrphan(); 598 TRACE("Volume::RemoveOrphan(): ID: %d\n", (int)id); 599 if (currentID == 0) 600 return B_OK; 601 602 CachedBlock cached(this); 603 604 uint32 blockNum; 605 status_t status = GetInodeBlock(currentID, blockNum); 606 if (status != B_OK) 607 return status; 608 609 uint8* block = cached.SetToWritable(transaction, blockNum); 610 if (block == NULL) 611 return B_IO_ERROR; 612 613 ext2_inode* inode = (ext2_inode*)(block 614 + InodeBlockIndex(currentID) * InodeSize()); 615 616 if (currentID == id) { 617 TRACE("Volume::RemoveOrphan(): First entry. Updating head to: %d\n", 618 (int)inode->NextOrphan()); 619 fSuperBlock.SetLastOrphan(inode->NextOrphan()); 620 621 return WriteSuperBlock(transaction); 622 } 623 624 currentID = inode->NextOrphan(); 625 if (currentID == 0) 626 return B_OK; 627 628 do { 629 uint32 lastBlockNum = blockNum; 630 status = GetInodeBlock(currentID, blockNum); 631 if (status != B_OK) 632 return status; 633 634 if (blockNum != lastBlockNum) { 635 block = cached.SetToWritable(transaction, blockNum); 636 if (block == NULL) 637 return B_IO_ERROR; 638 } 639 640 ext2_inode* inode = (ext2_inode*)(block 641 + InodeBlockIndex(currentID) * InodeSize()); 642 643 currentID = inode->NextOrphan(); 644 if (currentID == 0) 645 return B_OK; 646 } while(currentID != id); 647 648 CachedBlock cachedRemoved(this); 649 650 status = GetInodeBlock(id, blockNum); 651 if (status != B_OK) 652 return status; 653 654 uint8* removedBlock = cachedRemoved.SetToWritable(transaction, blockNum); 655 if (removedBlock == NULL) 656 return B_IO_ERROR; 657 658 ext2_inode* removedInode = (ext2_inode*)(removedBlock 659 + InodeBlockIndex(id) * InodeSize()); 660 661 // Next orphan is stored inside deletion time 662 inode->deletion_time = removedInode->deletion_time; 663 TRACE("Volume::RemoveOrphan(): Updated pointer to %d\n", 664 (int)inode->NextOrphan()); 665 666 return status; 667 } 668 669 670 status_t 671 Volume::AllocateInode(Transaction& transaction, Inode* parent, int32 mode, 672 ino_t& id) 673 { 674 status_t status = fInodeAllocator.New(transaction, parent, mode, id); 675 if (status != B_OK) 676 return status; 677 678 --fFreeInodes; 679 680 return WriteSuperBlock(transaction); 681 } 682 683 684 status_t 685 Volume::FreeInode(Transaction& transaction, ino_t id, bool isDirectory) 686 { 687 status_t status = fInodeAllocator.Free(transaction, id, isDirectory); 688 if (status != B_OK) 689 return status; 690 691 ++fFreeInodes; 692 693 return WriteSuperBlock(transaction); 694 } 695 696 697 status_t 698 Volume::AllocateBlocks(Transaction& transaction, uint32 minimum, uint32 maximum, 699 uint32& blockGroup, uint32& start, uint32& length) 700 { 701 TRACE("Volume::AllocateBlocks()\n"); 702 if (IsReadOnly()) 703 return B_READ_ONLY_DEVICE; 704 705 TRACE("Volume::AllocateBlocks(): Calling the block allocator\n"); 706 707 status_t status = fBlockAllocator.AllocateBlocks(transaction, minimum, 708 maximum, blockGroup, start, length); 709 if (status != B_OK) 710 return status; 711 712 TRACE("Volume::AllocateBlocks(): Allocated %lu blocks\n", length); 713 714 fFreeBlocks -= length; 715 716 return WriteSuperBlock(transaction); 717 } 718 719 720 status_t 721 Volume::FreeBlocks(Transaction& transaction, uint32 start, uint32 length) 722 { 723 TRACE("Volume::FreeBlocks(%lu, %lu)\n", start, length); 724 if (IsReadOnly()) 725 return B_READ_ONLY_DEVICE; 726 727 status_t status = fBlockAllocator.Free(transaction, start, length); 728 if (status != B_OK) 729 return status; 730 731 TRACE("Volume::FreeBlocks(): number of free blocks (before): %lu\n", 732 fFreeBlocks); 733 fFreeBlocks += length; 734 TRACE("Volume::FreeBlocks(): number of free blocks (after): %lu\n", 735 fFreeBlocks); 736 737 return WriteSuperBlock(transaction); 738 } 739 740 741 status_t 742 Volume::LoadSuperBlock() 743 { 744 CachedBlock cached(this); 745 const uint8* block = cached.SetTo(fFirstDataBlock); 746 747 if (block == NULL) 748 return B_IO_ERROR; 749 750 if (fFirstDataBlock == 0) 751 memcpy(&fSuperBlock, block + 1024, sizeof(fSuperBlock)); 752 else 753 memcpy(&fSuperBlock, block, sizeof(fSuperBlock)); 754 755 fFreeBlocks = fSuperBlock.FreeBlocks(); 756 fFreeInodes = fSuperBlock.FreeInodes(); 757 758 return B_OK; 759 } 760 761 762 status_t 763 Volume::WriteSuperBlock(Transaction& transaction) 764 { 765 TRACE("Volume::WriteSuperBlock()\n"); 766 fSuperBlock.SetFreeBlocks(fFreeBlocks); 767 fSuperBlock.SetFreeInodes(fFreeInodes); 768 // TODO: Rest of fields that can be modified 769 770 TRACE("Volume::WriteSuperBlock(): free blocks: %lu, free inodes: %lu\n", 771 fSuperBlock.FreeBlocks(), fSuperBlock.FreeInodes()); 772 773 CachedBlock cached(this); 774 uint8* block = cached.SetToWritable(transaction, fFirstDataBlock); 775 776 if (block == NULL) 777 return B_IO_ERROR; 778 779 TRACE("Volume::WriteSuperBlock(): first data block: %lu, block: %p, " 780 "superblock: %p\n", fFirstDataBlock, block, &fSuperBlock); 781 782 if (fFirstDataBlock == 0) 783 memcpy(block + 1024, &fSuperBlock, sizeof(fSuperBlock)); 784 else 785 memcpy(block, &fSuperBlock, sizeof(fSuperBlock)); 786 787 TRACE("Volume::WriteSuperBlock(): Done\n"); 788 789 return B_OK; 790 } 791 792 793 status_t 794 Volume::FlushDevice() 795 { 796 TRACE("Volume::FlushDevice(): %p, %p\n", this, fBlockCache); 797 return block_cache_sync(fBlockCache); 798 } 799 800 801 status_t 802 Volume::Sync() 803 { 804 TRACE("Volume::Sync()\n"); 805 return fJournal->FlushLogAndBlocks(); 806 } 807 808 809 // #pragma mark - Disk scanning and initialization 810 811 812 /*static*/ status_t 813 Volume::Identify(int fd, ext2_super_block* superBlock) 814 { 815 if (read_pos(fd, EXT2_SUPER_BLOCK_OFFSET, superBlock, 816 sizeof(ext2_super_block)) != sizeof(ext2_super_block)) 817 return B_IO_ERROR; 818 819 if (!superBlock->IsValid()) { 820 FATAL("invalid super block!\n"); 821 return B_BAD_VALUE; 822 } 823 824 return _UnsupportedIncompatibleFeatures(*superBlock) == 0 825 ? B_OK : B_NOT_SUPPORTED; 826 } 827 828 829 void 830 Volume::TransactionDone(bool success) 831 { 832 if (!success) { 833 status_t status = LoadSuperBlock(); 834 if (status != B_OK) 835 panic("Failed to reload ext2 superblock.\n"); 836 } 837 } 838 839 840 void 841 Volume::RemovedFromTransaction() 842 { 843 // TODO: Does it make a difference? 844 } 845