1 /* 2 * Copyright 2010, Haiku Inc. All rights reserved. 3 * Copyright 2001-2010, Axel Dörfler, axeld@pinc-software.de. 4 * This file may be used under the terms of the MIT License. 5 * 6 * Authors: 7 * Janito V. Ferreira Filho 8 */ 9 10 11 #include "Journal.h" 12 13 #include <new> 14 #include <string.h> 15 #include <unistd.h> 16 17 #include <fs_cache.h> 18 19 #include "CachedBlock.h" 20 #include "CRCTable.h" 21 #include "HashRevokeManager.h" 22 23 24 //#define TRACE_EXT2 25 #ifdef TRACE_EXT2 26 # define TRACE(x...) dprintf("\33[34mext2:\33[0m " x) 27 #else 28 # define TRACE(x...) ; 29 #endif 30 #define ERROR(x...) dprintf("\33[34mext2:\33[0m " x) 31 #define WARN(x...) dprintf("\33[34mext2:\33[0m " x) 32 33 34 class LogEntry : public DoublyLinkedListLinkImpl<LogEntry> { 35 public: 36 LogEntry(Journal* journal, uint32 logStart, 37 uint32 length); 38 ~LogEntry(); 39 40 uint32 Start() const { return fStart; } 41 uint32 CommitID() const { return fCommitID; } 42 43 Journal* GetJournal() { return fJournal; } 44 45 private: 46 Journal* fJournal; 47 uint32 fStart; 48 uint32 fCommitID; 49 }; 50 51 52 LogEntry::LogEntry(Journal* journal, uint32 logStart, uint32 commitID) 53 : 54 fJournal(journal), 55 fStart(logStart), 56 fCommitID(commitID) 57 { 58 } 59 60 61 LogEntry::~LogEntry() 62 { 63 } 64 65 66 void 67 JournalHeader::MakeDescriptor(uint32 sequence) 68 { 69 this->magic = B_HOST_TO_BENDIAN_INT32(JOURNAL_MAGIC); 70 this->sequence = B_HOST_TO_BENDIAN_INT32(sequence); 71 this->block_type = B_HOST_TO_BENDIAN_INT32(JOURNAL_DESCRIPTOR_BLOCK); 72 } 73 74 75 void 76 JournalHeader::MakeCommit(uint32 sequence) 77 { 78 this->magic = B_HOST_TO_BENDIAN_INT32(JOURNAL_MAGIC); 79 this->sequence = B_HOST_TO_BENDIAN_INT32(sequence); 80 this->block_type = B_HOST_TO_BENDIAN_INT32(JOURNAL_COMMIT_BLOCK); 81 } 82 83 84 Journal::Journal(Volume* fsVolume, Volume* jVolume) 85 : 86 fJournalVolume(jVolume), 87 fJournalBlockCache(jVolume->BlockCache()), 88 fFilesystemVolume(fsVolume), 89 fFilesystemBlockCache(fsVolume->BlockCache()), 90 fRevokeManager(NULL), 91 fInitStatus(B_OK), 92 fBlockSize(sizeof(JournalSuperBlock)), 93 fFirstCommitID(0), 94 fFirstCacheCommitID(0), 95 fFirstLogBlock(1), 96 fLogSize(0), 97 fVersion(0), 98 fLogStart(0), 99 fLogEnd(0), 100 fFreeBlocks(0), 101 fMaxTransactionSize(0), 102 fCurrentCommitID(0), 103 fHasSubTransaction(false), 104 fSeparateSubTransactions(false), 105 fUnwrittenTransactions(0), 106 fTransactionID(0) 107 { 108 recursive_lock_init(&fLock, "ext2 journal"); 109 mutex_init(&fLogEntriesLock, "ext2 journal log entries"); 110 111 HashRevokeManager* revokeManager = new(std::nothrow) HashRevokeManager( 112 fsVolume->Has64bitFeature()); 113 TRACE("Journal::Journal(): Allocated a hash revoke manager at %p\n", 114 revokeManager); 115 116 if (revokeManager == NULL) 117 fInitStatus = B_NO_MEMORY; 118 else { 119 fInitStatus = revokeManager->Init(); 120 121 if (fInitStatus == B_OK) { 122 fRevokeManager = revokeManager; 123 fInitStatus = _LoadSuperBlock(); 124 } else 125 delete revokeManager; 126 } 127 } 128 129 130 Journal::Journal() 131 : 132 fJournalVolume(NULL), 133 fJournalBlockCache(NULL), 134 fFilesystemVolume(NULL), 135 fFilesystemBlockCache(NULL), 136 fOwner(NULL), 137 fRevokeManager(NULL), 138 fInitStatus(B_OK), 139 fBlockSize(sizeof(JournalSuperBlock)), 140 fFirstCommitID(0), 141 fFirstCacheCommitID(0), 142 fFirstLogBlock(1), 143 fLogSize(0), 144 fVersion(0), 145 fIsStarted(false), 146 fLogStart(0), 147 fLogEnd(0), 148 fFreeBlocks(0), 149 fMaxTransactionSize(0), 150 fCurrentCommitID(0), 151 fHasSubTransaction(false), 152 fSeparateSubTransactions(false), 153 fUnwrittenTransactions(0), 154 fTransactionID(0), 155 fChecksumEnabled(false), 156 fChecksumV3Enabled(false), 157 fFeature64bits(false) 158 { 159 recursive_lock_init(&fLock, "ext2 journal"); 160 mutex_init(&fLogEntriesLock, "ext2 journal log entries"); 161 } 162 163 164 Journal::~Journal() 165 { 166 TRACE("Journal destructor.\n"); 167 168 TRACE("Journal::~Journal(): Attempting to delete revoke manager at %p\n", 169 fRevokeManager); 170 delete fRevokeManager; 171 172 recursive_lock_destroy(&fLock); 173 mutex_destroy(&fLogEntriesLock); 174 } 175 176 177 status_t 178 Journal::InitCheck() 179 { 180 return fInitStatus; 181 } 182 183 184 status_t 185 Journal::Uninit() 186 { 187 if (!fIsStarted) 188 return B_OK; 189 190 status_t status = FlushLogAndBlocks(); 191 192 if (status == B_OK) { 193 // Mark journal as clean 194 fLogStart = 0; 195 status = _SaveSuperBlock(); 196 } 197 198 fIsStarted = false; 199 200 return status; 201 } 202 203 204 /*virtual*/ status_t 205 Journal::StartLog() 206 { 207 fLogStart = fFirstLogBlock; 208 fLogEnd = fFirstLogBlock; 209 fFreeBlocks = 0; 210 fIsStarted = true; 211 212 fCurrentCommitID = fFirstCommitID; 213 214 return _SaveSuperBlock(); 215 } 216 217 218 status_t 219 Journal::RestartLog() 220 { 221 fFirstCommitID = 1; 222 223 return B_OK; 224 } 225 226 227 /*virtual*/ status_t 228 Journal::Lock(Transaction* owner, bool separateSubTransactions) 229 { 230 TRACE("Journal::Lock()\n"); 231 status_t status = recursive_lock_lock(&fLock); 232 if (status != B_OK) 233 return status; 234 235 TRACE("Journal::Lock(): Aquired lock\n"); 236 237 if (!fSeparateSubTransactions && recursive_lock_get_recursion(&fLock) > 1) { 238 // reuse current transaction 239 TRACE("Journal::Lock(): Reusing current transaction\n"); 240 return B_OK; 241 } 242 243 if (separateSubTransactions) 244 fSeparateSubTransactions = true; 245 246 if (owner != NULL) 247 owner->SetParent(fOwner); 248 249 fOwner = owner; 250 251 if (fOwner != NULL) { 252 if (fUnwrittenTransactions > 0) { 253 // start a sub transaction 254 TRACE("Journal::Lock(): Starting sub transaction\n"); 255 cache_start_sub_transaction(fFilesystemBlockCache, fTransactionID); 256 fHasSubTransaction = true; 257 } else { 258 TRACE("Journal::Lock(): Starting new transaction\n"); 259 fTransactionID = cache_start_transaction(fFilesystemBlockCache); 260 } 261 262 if (fTransactionID < B_OK) { 263 recursive_lock_unlock(&fLock); 264 return fTransactionID; 265 } 266 267 cache_add_transaction_listener(fFilesystemBlockCache, fTransactionID, 268 TRANSACTION_IDLE, _TransactionIdle, this); 269 } 270 271 return B_OK; 272 } 273 274 275 /*virtual*/ status_t 276 Journal::Unlock(Transaction* owner, bool success) 277 { 278 TRACE("Journal::Unlock(): Lock recursion: %" B_PRId32 "\n", 279 recursive_lock_get_recursion(&fLock)); 280 if (fSeparateSubTransactions 281 || recursive_lock_get_recursion(&fLock) == 1) { 282 // we only end the transaction if we unlock it 283 if (owner != NULL) { 284 TRACE("Journal::Unlock(): Calling _TransactionDone\n"); 285 status_t status = _TransactionDone(success); 286 if (status != B_OK) 287 return status; 288 289 TRACE("Journal::Unlock(): Returned from _TransactionDone\n"); 290 bool separateSubTransactions = fSeparateSubTransactions; 291 fSeparateSubTransactions = true; 292 TRACE("Journal::Unlock(): Notifying listeners for: %p\n", owner); 293 owner->NotifyListeners(success); 294 TRACE("Journal::Unlock(): Done notifying listeners\n"); 295 fSeparateSubTransactions = separateSubTransactions; 296 297 fOwner = owner->Parent(); 298 } else 299 fOwner = NULL; 300 301 if (fSeparateSubTransactions 302 && recursive_lock_get_recursion(&fLock) == 1) 303 fSeparateSubTransactions = false; 304 } else 305 owner->MoveListenersTo(fOwner); 306 307 TRACE("Journal::Unlock(): Unlocking the lock\n"); 308 309 recursive_lock_unlock(&fLock); 310 return B_OK; 311 } 312 313 314 status_t 315 Journal::MapBlock(off_t logical, fsblock_t& physical) 316 { 317 TRACE("Journal::MapBlock()\n"); 318 physical = logical; 319 320 return B_OK; 321 } 322 323 324 inline uint32 325 Journal::FreeLogBlocks() const 326 { 327 TRACE("Journal::FreeLogBlocks(): start: %" B_PRIu32 ", end: %" B_PRIu32 328 ", size: %" B_PRIu32 "\n", fLogStart, fLogEnd, fLogSize); 329 return fLogStart <= fLogEnd 330 ? fLogSize - fLogEnd + fLogStart - 1 331 : fLogStart - fLogEnd; 332 } 333 334 335 status_t 336 Journal::FlushLogAndBlocks() 337 { 338 return _FlushLog(true, true); 339 } 340 341 342 int32 343 Journal::TransactionID() const 344 { 345 return fTransactionID; 346 } 347 348 349 status_t 350 Journal::_WritePartialTransactionToLog(JournalHeader* descriptorBlock, 351 bool detached, uint8** _escapedData, uint32 &logBlock, off_t& blockNumber, 352 long& cookie, ArrayDeleter<uint8>& escapedDataDeleter, uint32& blockCount, 353 bool& finished) 354 { 355 TRACE("Journal::_WritePartialTransactionToLog()\n"); 356 357 uint32 descriptorBlockPos = logBlock; 358 uint8* escapedData = *_escapedData; 359 360 JournalBlockTag* tag = (JournalBlockTag*)descriptorBlock->data; 361 JournalBlockTag* lastTag = (JournalBlockTag*)((uint8*)descriptorBlock 362 + fBlockSize - sizeof(JournalHeader)); 363 364 finished = false; 365 status_t status = B_OK; 366 367 while (tag < lastTag && status == B_OK) { 368 tag->SetBlockNumber(blockNumber); 369 tag->SetFlags(0); 370 371 CachedBlock data(fFilesystemVolume); 372 const JournalHeader* blockData = (JournalHeader*)data.SetTo( 373 blockNumber); 374 if (blockData == NULL) { 375 panic("Got a NULL pointer while iterating through transaction " 376 "blocks.\n"); 377 return B_ERROR; 378 } 379 380 void* finalData; 381 382 if (blockData->CheckMagic()) { 383 // The journaled block starts with the magic value 384 // We must remove it to prevent confusion 385 TRACE("Journal::_WritePartialTransactionToLog(): Block starts with " 386 "magic number. Escaping it\n"); 387 tag->SetEscapedFlag(); 388 389 if (escapedData == NULL) { 390 TRACE("Journal::_WritePartialTransactionToLog(): Allocating " 391 "space for escaped block (%" B_PRIu32 ")\n", fBlockSize); 392 escapedData = new(std::nothrow) uint8[fBlockSize]; 393 if (escapedData == NULL) { 394 TRACE("Journal::_WritePartialTransactionToLof(): Failed to " 395 "allocate buffer for escaped data block\n"); 396 return B_NO_MEMORY; 397 } 398 escapedDataDeleter.SetTo(escapedData); 399 *_escapedData = escapedData; 400 401 ((int32*)escapedData)[0] = 0; // Remove magic 402 } 403 404 memcpy(escapedData + 4, blockData->data, fBlockSize - 4); 405 finalData = escapedData; 406 } else 407 finalData = (void*)blockData; 408 409 // TODO: use iovecs? 410 411 logBlock = _WrapAroundLog(logBlock + 1); 412 413 fsblock_t physicalBlock; 414 status = MapBlock(logBlock, physicalBlock); 415 if (status != B_OK) 416 return status; 417 418 off_t logOffset = physicalBlock * fBlockSize; 419 420 TRACE("Journal::_WritePartialTransactionToLog(): Writing from memory: " 421 "%p, to disk: %" B_PRIdOFF "\n", finalData, logOffset); 422 size_t written = write_pos(fJournalVolume->Device(), logOffset, 423 finalData, fBlockSize); 424 if (written != fBlockSize) { 425 TRACE("Failed to write journal block.\n"); 426 return B_IO_ERROR; 427 } 428 429 TRACE("Journal::_WritePartialTransactionToLog(): Wrote a journal block " 430 "at: %" B_PRIu32 "\n", logBlock); 431 432 blockCount++; 433 tag++; 434 435 status = cache_next_block_in_transaction(fFilesystemBlockCache, 436 fTransactionID, detached, &cookie, &blockNumber, NULL, NULL); 437 } 438 439 finished = status != B_OK; 440 441 // Write descriptor block 442 --tag; 443 tag->SetLastTagFlag(); 444 445 fsblock_t physicalBlock; 446 status = MapBlock(descriptorBlockPos, physicalBlock); 447 if (status != B_OK) 448 return status; 449 450 off_t descriptorBlockOffset = physicalBlock * fBlockSize; 451 452 TRACE("Journal::_WritePartialTransactionToLog(): Writing to: %" B_PRIdOFF 453 "\n", descriptorBlockOffset); 454 size_t written = write_pos(fJournalVolume->Device(), 455 descriptorBlockOffset, descriptorBlock, fBlockSize); 456 if (written != fBlockSize) { 457 TRACE("Failed to write journal descriptor block.\n"); 458 return B_IO_ERROR; 459 } 460 461 blockCount++; 462 logBlock = _WrapAroundLog(logBlock + 1); 463 464 return B_OK; 465 } 466 467 468 status_t 469 Journal::_WriteTransactionToLog() 470 { 471 TRACE("Journal::_WriteTransactionToLog()\n"); 472 // Transaction enters the Flush state 473 bool detached = false; 474 TRACE("Journal::_WriteTransactionToLog(): Attempting to get transaction " 475 "size\n"); 476 size_t size = _FullTransactionSize(); 477 TRACE("Journal::_WriteTransactionToLog(): transaction size: %" B_PRIuSIZE 478 "\n", size); 479 480 if (size > fMaxTransactionSize) { 481 TRACE("Journal::_WriteTransactionToLog(): not enough free space " 482 "for the transaction. Attempting to free some space.\n"); 483 size = _MainTransactionSize(); 484 TRACE("Journal::_WriteTransactionToLog(): main transaction size: %" 485 B_PRIuSIZE "\n", size); 486 487 if (fHasSubTransaction && size < fMaxTransactionSize) { 488 TRACE("Journal::_WriteTransactionToLog(): transaction doesn't fit, " 489 "but it can be separated\n"); 490 detached = true; 491 } else { 492 // Error: transaction can't fit in log 493 panic("transaction too large (size: %" B_PRIuSIZE ", max size: %" 494 B_PRIu32 ", log size: %" B_PRIu32 ")\n", size, 495 fMaxTransactionSize, fLogSize); 496 return B_BUFFER_OVERFLOW; 497 } 498 } 499 500 TRACE("Journal::_WriteTransactionToLog(): free log blocks: %" B_PRIu32 501 "\n", FreeLogBlocks()); 502 if (size > FreeLogBlocks()) { 503 TRACE("Journal::_WriteTransactionToLog(): Syncing block cache\n"); 504 cache_sync_transaction(fFilesystemBlockCache, fTransactionID); 505 506 if (size > FreeLogBlocks()) { 507 panic("Transaction fits, but sync didn't result in enough" 508 "free space.\n\tGot %" B_PRIu32 " when at least %" B_PRIuSIZE 509 " was expected.", FreeLogBlocks(), size); 510 } 511 } 512 513 TRACE("Journal::_WriteTransactionToLog(): finished managing space for " 514 "the transaction\n"); 515 516 fHasSubTransaction = false; 517 if (!fIsStarted) 518 StartLog(); 519 520 // Prepare Descriptor block 521 TRACE("Journal::_WriteTransactionToLog(): attempting to allocate space for " 522 "the descriptor block, block size %" B_PRIu32 "\n", fBlockSize); 523 JournalHeader* descriptorBlock = 524 (JournalHeader*)new(std::nothrow) uint8[fBlockSize]; 525 if (descriptorBlock == NULL) { 526 TRACE("Journal::_WriteTransactionToLog(): Failed to allocate a buffer " 527 "for the descriptor block\n"); 528 return B_NO_MEMORY; 529 } 530 ArrayDeleter<uint8> descriptorBlockDeleter((uint8*)descriptorBlock); 531 532 descriptorBlock->MakeDescriptor(fCurrentCommitID); 533 534 // Prepare Commit block 535 TRACE("Journal::_WriteTransactionToLog(): attempting to allocate space for " 536 "the commit block, block size %" B_PRIu32 "\n", fBlockSize); 537 JournalHeader* commitBlock = 538 (JournalHeader*)new(std::nothrow) uint8[fBlockSize]; 539 if (commitBlock == NULL) { 540 TRACE("Journal::_WriteTransactionToLog(): Failed to allocate a buffer " 541 "for the commit block\n"); 542 return B_NO_MEMORY; 543 } 544 ArrayDeleter<uint8> commitBlockDeleter((uint8*)commitBlock); 545 546 commitBlock->MakeCommit(fCurrentCommitID + 1); 547 memset(commitBlock->data, 0, fBlockSize - sizeof(JournalHeader)); 548 // TODO: This probably isn't necessary 549 550 uint8* escapedData = NULL; 551 ArrayDeleter<uint8> escapedDataDeleter; 552 553 off_t blockNumber; 554 long cookie = 0; 555 556 status_t status = cache_next_block_in_transaction(fFilesystemBlockCache, 557 fTransactionID, detached, &cookie, &blockNumber, NULL, NULL); 558 if (status != B_OK) { 559 TRACE("Journal::_WriteTransactionToLog(): Transaction has no blocks to " 560 "write\n"); 561 return B_OK; 562 } 563 564 uint32 blockCount = 0; 565 566 uint32 logBlock = _WrapAroundLog(fLogEnd); 567 568 bool finished = false; 569 570 status = _WritePartialTransactionToLog(descriptorBlock, detached, 571 &escapedData, logBlock, blockNumber, cookie, escapedDataDeleter, 572 blockCount, finished); 573 if (!finished && status != B_OK) 574 return status; 575 576 uint32 commitBlockPos = logBlock; 577 578 while (!finished) { 579 descriptorBlock->IncrementSequence(); 580 581 status = _WritePartialTransactionToLog(descriptorBlock, detached, 582 &escapedData, logBlock, blockNumber, cookie, escapedDataDeleter, 583 blockCount, finished); 584 if (!finished && status != B_OK) 585 return status; 586 587 // It is okay to write the commit blocks of the partial transactions 588 // as long as the commit block of the first partial transaction isn't 589 // written. When it recovery reaches where the first commit should be 590 // and doesn't find it, it considers it found the end of the log. 591 592 fsblock_t physicalBlock; 593 status = MapBlock(logBlock, physicalBlock); 594 if (status != B_OK) 595 return status; 596 597 off_t logOffset = physicalBlock * fBlockSize; 598 599 TRACE("Journal::_WriteTransactionToLog(): Writting commit block to " 600 "%" B_PRIdOFF "\n", logOffset); 601 off_t written = write_pos(fJournalVolume->Device(), logOffset, 602 commitBlock, fBlockSize); 603 if (written != fBlockSize) { 604 TRACE("Failed to write journal commit block.\n"); 605 return B_IO_ERROR; 606 } 607 608 commitBlock->IncrementSequence(); 609 blockCount++; 610 611 logBlock = _WrapAroundLog(logBlock + 1); 612 } 613 614 // Transaction will enter the Commit state 615 fsblock_t physicalBlock; 616 status = MapBlock(commitBlockPos, physicalBlock); 617 if (status != B_OK) 618 return status; 619 620 off_t logOffset = physicalBlock * fBlockSize; 621 622 TRACE("Journal::_WriteTransactionToLog(): Writing to: %" B_PRIdOFF "\n", 623 logOffset); 624 off_t written = write_pos(fJournalVolume->Device(), logOffset, commitBlock, 625 fBlockSize); 626 if (written != fBlockSize) { 627 TRACE("Failed to write journal commit block.\n"); 628 return B_IO_ERROR; 629 } 630 631 blockCount++; 632 fLogEnd = _WrapAroundLog(fLogEnd + blockCount); 633 634 status = _SaveSuperBlock(); 635 636 // Transaction will enter Finished state 637 LogEntry *logEntry = new LogEntry(this, fLogEnd, fCurrentCommitID++); 638 TRACE("Journal::_WriteTransactionToLog(): Allocating log entry at %p\n", 639 logEntry); 640 if (logEntry == NULL) { 641 panic("no memory to allocate log entries!"); 642 return B_NO_MEMORY; 643 } 644 645 mutex_lock(&fLogEntriesLock); 646 fLogEntries.Add(logEntry); 647 mutex_unlock(&fLogEntriesLock); 648 649 if (detached) { 650 fTransactionID = cache_detach_sub_transaction(fFilesystemBlockCache, 651 fTransactionID, _TransactionWritten, logEntry); 652 fUnwrittenTransactions = 1; 653 654 if (status == B_OK && _FullTransactionSize() > fLogSize) { 655 // If the transaction is too large after writing, there is no way to 656 // recover, so let this transaction fail. 657 ERROR("transaction too large (%" B_PRIuSIZE " blocks, log size %" 658 B_PRIu32 ")!\n", _FullTransactionSize(), fLogSize); 659 return B_BUFFER_OVERFLOW; 660 } 661 } else { 662 cache_end_transaction(fFilesystemBlockCache, fTransactionID, 663 _TransactionWritten, logEntry); 664 fUnwrittenTransactions = 0; 665 } 666 667 return B_OK; 668 } 669 670 671 status_t 672 Journal::_SaveSuperBlock() 673 { 674 TRACE("Journal::_SaveSuperBlock()\n"); 675 fsblock_t physicalBlock; 676 status_t status = MapBlock(0, physicalBlock); 677 if (status != B_OK) 678 return status; 679 680 off_t superblockPos = physicalBlock * fBlockSize; 681 682 JournalSuperBlock superblock; 683 size_t bytesRead = read_pos(fJournalVolume->Device(), superblockPos, 684 &superblock, sizeof(superblock)); 685 686 if (bytesRead != sizeof(superblock)) 687 return B_IO_ERROR; 688 689 superblock.SetFirstCommitID(fFirstCommitID); 690 superblock.SetLogStart(fLogStart); 691 692 if (fChecksumEnabled) 693 superblock.SetChecksum(_Checksum(&superblock)); 694 695 TRACE("Journal::SaveSuperBlock(): Write to %" B_PRIdOFF "\n", 696 superblockPos); 697 size_t bytesWritten = write_pos(fJournalVolume->Device(), superblockPos, 698 &superblock, sizeof(superblock)); 699 700 if (bytesWritten != sizeof(superblock)) 701 return B_IO_ERROR; 702 703 TRACE("Journal::_SaveSuperBlock(): Done\n"); 704 705 return B_OK; 706 } 707 708 709 status_t 710 Journal::_LoadSuperBlock() 711 { 712 STATIC_ASSERT(sizeof(struct JournalHeader) == 12); 713 STATIC_ASSERT(sizeof(struct JournalSuperBlock) == 1024); 714 715 TRACE("Journal::_LoadSuperBlock()\n"); 716 fsblock_t superblockPos; 717 718 status_t status = MapBlock(0, superblockPos); 719 if (status != B_OK) 720 return status; 721 722 TRACE("Journal::_LoadSuperBlock(): superblock physical block: %" B_PRIu64 723 "\n", superblockPos); 724 725 JournalSuperBlock superblock; 726 size_t bytesRead = read_pos(fJournalVolume->Device(), superblockPos 727 * fJournalVolume->BlockSize(), &superblock, sizeof(superblock)); 728 729 if (bytesRead != sizeof(superblock)) { 730 ERROR("Journal::_LoadSuperBlock(): failed to read superblock\n"); 731 return B_IO_ERROR; 732 } 733 734 if (!superblock.header.CheckMagic()) { 735 ERROR("Journal::_LoadSuperBlock(): Invalid superblock magic %" B_PRIx32 736 "\n", superblock.header.Magic()); 737 return B_BAD_VALUE; 738 } 739 740 if (superblock.header.BlockType() == JOURNAL_SUPERBLOCK_V1) { 741 TRACE("Journal::_LoadSuperBlock(): Journal superblock version 1\n"); 742 fVersion = 1; 743 } else if (superblock.header.BlockType() == JOURNAL_SUPERBLOCK_V2) { 744 TRACE("Journal::_LoadSuperBlock(): Journal superblock version 2\n"); 745 fVersion = 2; 746 } else { 747 ERROR("Journal::_LoadSuperBlock(): Invalid superblock version\n"); 748 return B_BAD_VALUE; 749 } 750 751 if (fVersion >= 2) { 752 TRACE("Journal::_LoadSuperBlock(): incompatible features %" B_PRIx32 753 ", read-only features %" B_PRIx32 "\n", 754 superblock.IncompatibleFeatures(), 755 superblock.ReadOnlyCompatibleFeatures()); 756 757 status = _CheckFeatures(&superblock); 758 759 if (status != B_OK) 760 return status; 761 762 if (fChecksumEnabled) { 763 if (superblock.Checksum() != _Checksum(&superblock)) { 764 ERROR("Journal::_LoadSuperBlock(): Invalid checksum\n"); 765 return B_BAD_DATA; 766 } 767 fChecksumSeed = calculate_crc32c(0xffffffff, (uint8*)superblock.uuid, 768 sizeof(superblock.uuid)); 769 } 770 } 771 772 fBlockSize = superblock.BlockSize(); 773 fFirstCommitID = superblock.FirstCommitID(); 774 fFirstLogBlock = superblock.FirstLogBlock(); 775 fLogStart = superblock.LogStart(); 776 fLogSize = superblock.NumBlocks(); 777 778 uint32 descriptorTags = (fBlockSize - sizeof(JournalHeader)) 779 / sizeof(JournalBlockTag); 780 // Maximum tags per descriptor block 781 uint32 maxDescriptors = (fLogSize - 1) / (descriptorTags + 2); 782 // Maximum number of full journal transactions 783 fMaxTransactionSize = maxDescriptors * descriptorTags; 784 fMaxTransactionSize += (fLogSize - 1) - fMaxTransactionSize - 2; 785 // Maximum size of a "logical" transaction 786 // TODO: Why is "superblock.MaxTransactionBlocks();" zero? 787 //fFirstCacheCommitID = fFirstCommitID - fTransactionID /*+ 1*/; 788 789 TRACE("Journal::_LoadSuperBlock(): block size: %" B_PRIu32 ", first commit" 790 " id: %" B_PRIu32 ", first log block: %" B_PRIu32 ", log start: %" 791 B_PRIu32 ", log size: %" B_PRIu32 ", max transaction size: %" B_PRIu32 792 "\n", fBlockSize, fFirstCommitID, fFirstLogBlock, fLogStart, 793 fLogSize, fMaxTransactionSize); 794 795 return B_OK; 796 } 797 798 799 status_t 800 Journal::_CheckFeatures(JournalSuperBlock* superblock) 801 { 802 uint32 readonly = superblock->ReadOnlyCompatibleFeatures(); 803 uint32 incompatible = superblock->IncompatibleFeatures(); 804 bool hasReadonly = (readonly & ~JOURNAL_KNOWN_READ_ONLY_COMPATIBLE_FEATURES) 805 != 0; 806 bool hasIncompatible = (incompatible 807 & ~JOURNAL_KNOWN_INCOMPATIBLE_FEATURES) != 0; 808 if (hasReadonly || hasIncompatible ) { 809 ERROR("Journal::_CheckFeatures(): Unsupported features: %" B_PRIx32 810 " %" B_PRIx32 "\n", readonly, incompatible); 811 return B_UNSUPPORTED; 812 } 813 814 bool hasCsumV2 = 815 (superblock->IncompatibleFeatures() & JOURNAL_FEATURE_INCOMPATIBLE_CSUM_V2) != 0; 816 bool hasCsumV3 = 817 (superblock->IncompatibleFeatures() & JOURNAL_FEATURE_INCOMPATIBLE_CSUM_V3) != 0; 818 if (hasCsumV2 && hasCsumV3) { 819 return B_BAD_VALUE; 820 } 821 822 fChecksumEnabled = hasCsumV2 && hasCsumV3; 823 fChecksumV3Enabled = hasCsumV3; 824 fFeature64bits = 825 (superblock->IncompatibleFeatures() & JOURNAL_FEATURE_INCOMPATIBLE_64BIT) != 0; 826 return B_OK; 827 } 828 829 830 uint32 831 Journal::_Checksum(JournalSuperBlock* superblock) 832 { 833 uint32 oldChecksum = superblock->checksum; 834 superblock->checksum = 0; 835 uint32 checksum = calculate_crc32c(0xffffffff, (uint8*)superblock, 836 sizeof(JournalSuperBlock)); 837 superblock->checksum = oldChecksum; 838 return checksum; 839 } 840 841 842 bool 843 Journal::_Checksum(uint8* block, bool set) 844 { 845 JournalBlockTail *tail = (JournalBlockTail*)(block + fBlockSize 846 - sizeof(JournalBlockTail)); 847 uint32 oldChecksum = tail->checksum; 848 tail->checksum = 0; 849 uint32 checksum = calculate_crc32c(0xffffffff, block, fBlockSize); 850 if (set) { 851 tail->checksum = checksum; 852 } else { 853 tail->checksum = oldChecksum; 854 } 855 return checksum == oldChecksum; 856 } 857 858 859 uint32 860 Journal::_CountTags(JournalHeader* descriptorBlock) 861 { 862 uint32 count = 0; 863 size_t tagSize = _TagSize(); 864 size_t size = fBlockSize; 865 866 if (fChecksumEnabled) 867 size -= sizeof(JournalBlockTail); 868 869 JournalBlockTag* tags = (JournalBlockTag*)descriptorBlock->data; 870 // Skip the header 871 JournalBlockTag* lastTag = (JournalBlockTag*) 872 (descriptorBlock + size - tagSize); 873 874 while (tags < lastTag && (tags->Flags() & JOURNAL_FLAG_LAST_TAG) == 0) { 875 if ((tags->Flags() & JOURNAL_FLAG_SAME_UUID) == 0) 876 tags = (JournalBlockTag*)((uint8*)tags + 16); // Skip new UUID 877 878 TRACE("Journal::_CountTags(): Tag block: %" B_PRIu32 "\n", 879 tags->BlockNumber()); 880 881 tags = (JournalBlockTag*)((uint8*)tags + tagSize); // Go to next tag 882 count++; 883 } 884 885 if ((tags->Flags() & JOURNAL_FLAG_LAST_TAG) != 0) 886 count++; 887 888 TRACE("Journal::_CountTags(): counted tags: %" B_PRIu32 "\n", count); 889 890 return count; 891 } 892 893 894 size_t 895 Journal::_TagSize() 896 { 897 if (fChecksumV3Enabled) 898 return sizeof(JournalBlockTagV3); 899 900 size_t size = sizeof(JournalBlockTag); 901 if (fChecksumEnabled) 902 size += sizeof(uint16); 903 if (!fFeature64bits) 904 size -= sizeof(uint32); 905 return size; 906 } 907 908 909 /*virtual*/ status_t 910 Journal::Recover() 911 { 912 TRACE("Journal::Recover()\n"); 913 if (fLogStart == 0) // Journal was cleanly unmounted 914 return B_OK; 915 916 TRACE("Journal::Recover(): Journal needs recovery\n"); 917 918 uint32 lastCommitID; 919 920 status_t status = _RecoverPassScan(lastCommitID); 921 if (status != B_OK) 922 return status; 923 924 status = _RecoverPassRevoke(lastCommitID); 925 if (status != B_OK) 926 return status; 927 928 return _RecoverPassReplay(lastCommitID); 929 } 930 931 932 // First pass: Find the end of the log 933 status_t 934 Journal::_RecoverPassScan(uint32& lastCommitID) 935 { 936 TRACE("Journal Recover: 1st Pass: Scan\n"); 937 938 CachedBlock cached(fJournalVolume); 939 JournalHeader* header; 940 uint32 nextCommitID = fFirstCommitID; 941 uint32 nextBlock = fLogStart; 942 fsblock_t nextBlockPos; 943 944 status_t status = MapBlock(nextBlock, nextBlockPos); 945 if (status != B_OK) 946 return status; 947 948 header = (JournalHeader*)cached.SetTo(nextBlockPos); 949 950 while (header->CheckMagic() && header->Sequence() == nextCommitID) { 951 uint32 blockType = header->BlockType(); 952 953 if (blockType == JOURNAL_DESCRIPTOR_BLOCK) { 954 if (fChecksumEnabled && !_Checksum((uint8*)header, false)) { 955 ERROR("Journal::_RecoverPassScan(): Invalid checksum\n"); 956 return B_BAD_DATA; 957 } 958 uint32 tags = _CountTags(header); 959 nextBlock += tags; 960 TRACE("Journal recover pass scan: Found a descriptor block with " 961 "%" B_PRIu32 " tags\n", tags); 962 } else if (blockType == JOURNAL_COMMIT_BLOCK) { 963 nextCommitID++; 964 TRACE("Journal recover pass scan: Found a commit block. Next " 965 "commit ID: %" B_PRIu32 "\n", nextCommitID); 966 } else if (blockType != JOURNAL_REVOKE_BLOCK) { 967 TRACE("Journal recover pass scan: Reached an unrecognized block, " 968 "assuming as log's end.\n"); 969 break; 970 } else { 971 TRACE("Journal recover pass scan: Found a revoke block, " 972 "skipping it\n"); 973 } 974 975 nextBlock = _WrapAroundLog(nextBlock + 1); 976 977 status = MapBlock(nextBlock, nextBlockPos); 978 if (status != B_OK) 979 return status; 980 981 header = (JournalHeader*)cached.SetTo(nextBlockPos); 982 } 983 984 TRACE("Journal Recovery pass scan: Last detected transaction ID: %" 985 B_PRIu32 "\n", nextCommitID); 986 987 lastCommitID = nextCommitID; 988 return B_OK; 989 } 990 991 992 // Second pass: Collect all revoked blocks 993 status_t 994 Journal::_RecoverPassRevoke(uint32 lastCommitID) 995 { 996 TRACE("Journal Recover: 2nd Pass: Revoke\n"); 997 998 CachedBlock cached(fJournalVolume); 999 JournalHeader* header; 1000 uint32 nextCommitID = fFirstCommitID; 1001 uint32 nextBlock = fLogStart; 1002 fsblock_t nextBlockPos; 1003 1004 status_t status = MapBlock(nextBlock, nextBlockPos); 1005 if (status != B_OK) 1006 return status; 1007 1008 header = (JournalHeader*)cached.SetTo(nextBlockPos); 1009 1010 while (nextCommitID < lastCommitID) { 1011 if (!header->CheckMagic() || header->Sequence() != nextCommitID) { 1012 // Somehow the log is different than the expexted 1013 return B_ERROR; 1014 } 1015 1016 uint32 blockType = header->BlockType(); 1017 1018 if (blockType == JOURNAL_DESCRIPTOR_BLOCK) 1019 nextBlock += _CountTags(header); 1020 else if (blockType == JOURNAL_COMMIT_BLOCK) 1021 nextCommitID++; 1022 else if (blockType == JOURNAL_REVOKE_BLOCK) { 1023 TRACE("Journal::_RecoverPassRevoke(): Found a revoke block\n"); 1024 status = fRevokeManager->ScanRevokeBlock( 1025 (JournalRevokeHeader*)header, nextCommitID); 1026 1027 if (status != B_OK) 1028 return status; 1029 } else { 1030 WARN("Journal::_RecoverPassRevoke(): Found an unrecognized block\n"); 1031 break; 1032 } 1033 1034 nextBlock = _WrapAroundLog(nextBlock + 1); 1035 1036 status = MapBlock(nextBlock, nextBlockPos); 1037 if (status != B_OK) 1038 return status; 1039 1040 header = (JournalHeader*)cached.SetTo(nextBlockPos); 1041 } 1042 1043 if (nextCommitID != lastCommitID) { 1044 // Possibly because of some sort of IO error 1045 TRACE("Journal::_RecoverPassRevoke(): Incompatible commit IDs\n"); 1046 return B_ERROR; 1047 } 1048 1049 TRACE("Journal recovery pass revoke: Revoked blocks: %" B_PRIu32 "\n", 1050 fRevokeManager->NumRevokes()); 1051 1052 return B_OK; 1053 } 1054 1055 1056 // Third pass: Replay log 1057 status_t 1058 Journal::_RecoverPassReplay(uint32 lastCommitID) 1059 { 1060 TRACE("Journal Recover: 3rd Pass: Replay\n"); 1061 1062 uint32 nextCommitID = fFirstCommitID; 1063 uint32 nextBlock = fLogStart; 1064 fsblock_t nextBlockPos; 1065 1066 status_t status = MapBlock(nextBlock, nextBlockPos); 1067 if (status != B_OK) 1068 return status; 1069 1070 CachedBlock cached(fJournalVolume); 1071 JournalHeader* header = (JournalHeader*)cached.SetTo(nextBlockPos); 1072 1073 int count = 0; 1074 1075 uint8* data = new(std::nothrow) uint8[fBlockSize]; 1076 if (data == NULL) { 1077 TRACE("Journal::_RecoverPassReplay(): Failed to allocate memory for " 1078 "data\n"); 1079 return B_NO_MEMORY; 1080 } 1081 1082 ArrayDeleter<uint8> dataDeleter(data); 1083 1084 while (nextCommitID < lastCommitID) { 1085 if (!header->CheckMagic() || header->Sequence() != nextCommitID) { 1086 // Somehow the log is different than the expected 1087 ERROR("Journal::_RecoverPassReplay(): Weird problem with block\n"); 1088 return B_ERROR; 1089 } 1090 1091 uint32 blockType = header->BlockType(); 1092 1093 if (blockType == JOURNAL_DESCRIPTOR_BLOCK) { 1094 JournalBlockTag* last_tag = (JournalBlockTag*)((uint8*)header 1095 + fBlockSize - sizeof(JournalBlockTag)); 1096 1097 for (JournalBlockTag* tag = (JournalBlockTag*)header->data; 1098 tag <= last_tag; ++tag) { 1099 nextBlock = _WrapAroundLog(nextBlock + 1); 1100 1101 status = MapBlock(nextBlock, nextBlockPos); 1102 if (status != B_OK) 1103 return status; 1104 1105 if (!fRevokeManager->Lookup(tag->BlockNumber(), 1106 nextCommitID)) { 1107 // Block isn't revoked 1108 size_t read = read_pos(fJournalVolume->Device(), 1109 nextBlockPos * fBlockSize, data, fBlockSize); 1110 if (read != fBlockSize) 1111 return B_IO_ERROR; 1112 1113 if ((tag->Flags() & JOURNAL_FLAG_ESCAPED) != 0) { 1114 // Block is escaped 1115 ((int32*)data)[0] 1116 = B_HOST_TO_BENDIAN_INT32(JOURNAL_MAGIC); 1117 } 1118 1119 TRACE("Journal::_RevoverPassReplay(): Write to %" B_PRIu32 1120 "\n", tag->BlockNumber() * fBlockSize); 1121 size_t written = write_pos(fFilesystemVolume->Device(), 1122 tag->BlockNumber() * fBlockSize, data, fBlockSize); 1123 1124 if (written != fBlockSize) 1125 return B_IO_ERROR; 1126 1127 ++count; 1128 } 1129 1130 if ((tag->Flags() & JOURNAL_FLAG_LAST_TAG) != 0) 1131 break; 1132 if ((tag->Flags() & JOURNAL_FLAG_SAME_UUID) == 0) { 1133 // TODO: Check new UUID with file system UUID 1134 tag += 2; 1135 // sizeof(JournalBlockTag) = 8 1136 // sizeof(UUID) = 16 1137 } 1138 } 1139 } else if (blockType == JOURNAL_COMMIT_BLOCK) 1140 nextCommitID++; 1141 else if (blockType != JOURNAL_REVOKE_BLOCK) { 1142 WARN("Journal::_RecoverPassReplay(): Found an unrecognized block\n"); 1143 break; 1144 } // If blockType == JOURNAL_REVOKE_BLOCK we just skip it 1145 1146 nextBlock = _WrapAroundLog(nextBlock + 1); 1147 1148 status = MapBlock(nextBlock, nextBlockPos); 1149 if (status != B_OK) 1150 return status; 1151 1152 header = (JournalHeader*)cached.SetTo(nextBlockPos); 1153 } 1154 1155 if (nextCommitID != lastCommitID) { 1156 // Possibly because of some sort of IO error 1157 return B_ERROR; 1158 } 1159 1160 TRACE("Journal recovery pass replay: Replayed blocks: %u\n", count); 1161 1162 return B_OK; 1163 } 1164 1165 1166 status_t 1167 Journal::_FlushLog(bool canWait, bool flushBlocks) 1168 { 1169 TRACE("Journal::_FlushLog()\n"); 1170 status_t status = canWait ? recursive_lock_lock(&fLock) 1171 : recursive_lock_trylock(&fLock); 1172 1173 TRACE("Journal::_FlushLog(): Acquired fLock, recursion: %" B_PRId32 "\n", 1174 recursive_lock_get_recursion(&fLock)); 1175 if (status != B_OK) 1176 return status; 1177 1178 if (recursive_lock_get_recursion(&fLock) > 1) { 1179 // Called from inside a transaction 1180 recursive_lock_unlock(&fLock); 1181 TRACE("Journal::_FlushLog(): Called from a transaction. Leaving...\n"); 1182 return B_OK; 1183 } 1184 1185 if (fUnwrittenTransactions != 0 && _FullTransactionSize() != 0) { 1186 status = _WriteTransactionToLog(); 1187 if (status < B_OK) 1188 panic("Failed flushing transaction: %s\n", strerror(status)); 1189 } 1190 1191 TRACE("Journal::_FlushLog(): Attempting to flush journal volume at %p\n", 1192 fJournalVolume); 1193 1194 // TODO: Not sure this is correct. Need to review... 1195 // NOTE: Not correct. Causes double lock of a block cache mutex 1196 // TODO: Need some other way to synchronize the journal... 1197 /*status = fJournalVolume->FlushDevice(); 1198 if (status != B_OK) 1199 return status;*/ 1200 1201 TRACE("Journal::_FlushLog(): Flushed journal volume\n"); 1202 1203 if (flushBlocks) { 1204 TRACE("Journal::_FlushLog(): Attempting to flush file system volume " 1205 "at %p\n", fFilesystemVolume); 1206 status = fFilesystemVolume->FlushDevice(); 1207 if (status == B_OK) 1208 TRACE("Journal::_FlushLog(): Flushed file system volume\n"); 1209 } 1210 1211 TRACE("Journal::_FlushLog(): Finished. Releasing lock\n"); 1212 1213 recursive_lock_unlock(&fLock); 1214 1215 TRACE("Journal::_FlushLog(): Done, final status: %s\n", strerror(status)); 1216 return status; 1217 } 1218 1219 1220 inline uint32 1221 Journal::_WrapAroundLog(uint32 block) 1222 { 1223 TRACE("Journal::_WrapAroundLog()\n"); 1224 if (block >= fLogSize) 1225 return block - fLogSize + fFirstLogBlock; 1226 else 1227 return block; 1228 } 1229 1230 1231 size_t 1232 Journal::_CurrentTransactionSize() const 1233 { 1234 TRACE("Journal::_CurrentTransactionSize(): transaction %" B_PRIu32 "\n", 1235 fTransactionID); 1236 1237 size_t count; 1238 1239 if (fHasSubTransaction) { 1240 count = cache_blocks_in_sub_transaction(fFilesystemBlockCache, 1241 fTransactionID); 1242 1243 TRACE("\tSub transaction size: %" B_PRIuSIZE "\n", count); 1244 } else { 1245 count = cache_blocks_in_transaction(fFilesystemBlockCache, 1246 fTransactionID); 1247 1248 TRACE("\tTransaction size: %" B_PRIuSIZE "\n", count); 1249 } 1250 1251 return count; 1252 } 1253 1254 1255 size_t 1256 Journal::_FullTransactionSize() const 1257 { 1258 TRACE("Journal::_FullTransactionSize(): transaction %" B_PRIu32 "\n", 1259 fTransactionID); 1260 TRACE("\tFile sytem block cache: %p\n", fFilesystemBlockCache); 1261 1262 size_t count = cache_blocks_in_transaction(fFilesystemBlockCache, 1263 fTransactionID); 1264 1265 TRACE("\tFull transaction size: %" B_PRIuSIZE "\n", count); 1266 1267 return count; 1268 } 1269 1270 1271 size_t 1272 Journal::_MainTransactionSize() const 1273 { 1274 TRACE("Journal::_MainTransactionSize(): transaction %" B_PRIu32 "\n", 1275 fTransactionID); 1276 1277 size_t count = cache_blocks_in_main_transaction(fFilesystemBlockCache, 1278 fTransactionID); 1279 1280 TRACE("\tMain transaction size: %" B_PRIuSIZE "\n", count); 1281 1282 return count; 1283 } 1284 1285 1286 status_t 1287 Journal::_TransactionDone(bool success) 1288 { 1289 if (!success) { 1290 if (fHasSubTransaction) { 1291 TRACE("Journal::_TransactionDone(): transaction %" B_PRIu32 1292 " failed, aborting subtransaction\n", fTransactionID); 1293 cache_abort_sub_transaction(fFilesystemBlockCache, fTransactionID); 1294 // parent is unaffected 1295 } else { 1296 TRACE("Journal::_TransactionDone(): transaction %" B_PRIu32 1297 " failed, aborting\n", fTransactionID); 1298 cache_abort_transaction(fFilesystemBlockCache, fTransactionID); 1299 fUnwrittenTransactions = 0; 1300 } 1301 1302 TRACE("Journal::_TransactionDone(): returning B_OK\n"); 1303 return B_OK; 1304 } 1305 1306 // If possible, delay flushing the transaction 1307 uint32 size = _FullTransactionSize(); 1308 TRACE("Journal::_TransactionDone(): full transaction size: %" B_PRIu32 1309 ", max transaction size: %" B_PRIu32 ", free log blocks: %" B_PRIu32 1310 "\n", size, fMaxTransactionSize, FreeLogBlocks()); 1311 if (fMaxTransactionSize > 0 && size < fMaxTransactionSize) { 1312 TRACE("Journal::_TransactionDone(): delaying flush of transaction " 1313 "%" B_PRIu32 "\n", fTransactionID); 1314 1315 // Make sure the transaction fits in the log 1316 if (size < FreeLogBlocks()) 1317 cache_sync_transaction(fFilesystemBlockCache, fTransactionID); 1318 1319 fUnwrittenTransactions++; 1320 TRACE("Journal::_TransactionDone(): returning B_OK\n"); 1321 return B_OK; 1322 } 1323 1324 return _WriteTransactionToLog(); 1325 } 1326 1327 1328 /*static*/ void 1329 Journal::_TransactionWritten(int32 transactionID, int32 event, void* _logEntry) 1330 { 1331 LogEntry* logEntry = (LogEntry*)_logEntry; 1332 1333 TRACE("Journal::_TransactionWritten(): Transaction %" B_PRIu32 1334 " checkpointed\n", transactionID); 1335 1336 Journal* journal = logEntry->GetJournal(); 1337 1338 TRACE("Journal::_TransactionWritten(): log entry: %p, journal: %p\n", 1339 logEntry, journal); 1340 TRACE("Journal::_TransactionWritten(): log entries: %p\n", 1341 &journal->fLogEntries); 1342 1343 mutex_lock(&journal->fLogEntriesLock); 1344 1345 TRACE("Journal::_TransactionWritten(): first log entry: %p\n", 1346 journal->fLogEntries.First()); 1347 if (logEntry == journal->fLogEntries.First()) { 1348 TRACE("Journal::_TransactionWritten(): Moving start of log to %" 1349 B_PRIu32 "\n", logEntry->Start()); 1350 journal->fLogStart = logEntry->Start(); 1351 journal->fFirstCommitID = logEntry->CommitID(); 1352 TRACE("Journal::_TransactionWritten(): Setting commit ID to %" B_PRIu32 1353 "\n", logEntry->CommitID()); 1354 1355 if (journal->_SaveSuperBlock() != B_OK) 1356 panic("ext2: Failed to write journal superblock\n"); 1357 } 1358 1359 TRACE("Journal::_TransactionWritten(): Removing log entry\n"); 1360 journal->fLogEntries.Remove(logEntry); 1361 1362 TRACE("Journal::_TransactionWritten(): Unlocking entries list\n"); 1363 mutex_unlock(&journal->fLogEntriesLock); 1364 1365 TRACE("Journal::_TransactionWritten(): Deleting log entry at %p\n", logEntry); 1366 delete logEntry; 1367 } 1368 1369 1370 /*static*/ void 1371 Journal::_TransactionIdle(int32 transactionID, int32 event, void* _journal) 1372 { 1373 Journal* journal = (Journal*)_journal; 1374 journal->_FlushLog(false, false); 1375 } 1376