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
Start() const40 uint32 Start() const { return fStart; }
CommitID() const41 uint32 CommitID() const { return fCommitID; }
42
GetJournal()43 Journal* GetJournal() { return fJournal; }
44
45 private:
46 Journal* fJournal;
47 uint32 fStart;
48 uint32 fCommitID;
49 };
50
51
LogEntry(Journal * journal,uint32 logStart,uint32 commitID)52 LogEntry::LogEntry(Journal* journal, uint32 logStart, uint32 commitID)
53 :
54 fJournal(journal),
55 fStart(logStart),
56 fCommitID(commitID)
57 {
58 }
59
60
~LogEntry()61 LogEntry::~LogEntry()
62 {
63 }
64
65
66 void
MakeDescriptor(uint32 sequence)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
MakeCommit(uint32 sequence)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
Journal(Volume * fsVolume,Volume * jVolume)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
Journal()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
~Journal()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
InitCheck()178 Journal::InitCheck()
179 {
180 return fInitStatus;
181 }
182
183
184 status_t
Uninit()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
StartLog()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
RestartLog()219 Journal::RestartLog()
220 {
221 fFirstCommitID = 1;
222
223 return B_OK;
224 }
225
226
227 /*virtual*/ status_t
Lock(Transaction * owner,bool separateSubTransactions)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
Unlock(Transaction * owner,bool success)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
MapBlock(off_t logical,fsblock_t & physical)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
FreeLogBlocks() const325 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
FlushLogAndBlocks()336 Journal::FlushLogAndBlocks()
337 {
338 return _FlushLog(true, true);
339 }
340
341
342 int32
TransactionID() const343 Journal::TransactionID() const
344 {
345 return fTransactionID;
346 }
347
348
349 status_t
_WritePartialTransactionToLog(JournalHeader * descriptorBlock,bool detached,uint8 ** _escapedData,uint32 & logBlock,off_t & blockNumber,long & cookie,ArrayDeleter<uint8> & escapedDataDeleter,uint32 & blockCount,bool & finished)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
_WriteTransactionToLog()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
_SaveSuperBlock()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
_LoadSuperBlock()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
_CheckFeatures(JournalSuperBlock * superblock)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
_Checksum(JournalSuperBlock * superblock)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
_Checksum(uint8 * block,bool set)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
_CountTags(JournalHeader * descriptorBlock)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
_TagSize()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
Recover()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
_RecoverPassScan(uint32 & lastCommitID)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
_RecoverPassRevoke(uint32 lastCommitID)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
_RecoverPassReplay(uint32 lastCommitID)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
_FlushLog(bool canWait,bool flushBlocks)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
_WrapAroundLog(uint32 block)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
_CurrentTransactionSize() const1232 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
_FullTransactionSize() const1256 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
_MainTransactionSize() const1272 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
_TransactionDone(bool success)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
_TransactionWritten(int32 transactionID,int32 event,void * _logEntry)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
_TransactionIdle(int32 transactionID,int32 event,void * _journal)1371 Journal::_TransactionIdle(int32 transactionID, int32 event, void* _journal)
1372 {
1373 Journal* journal = (Journal*)_journal;
1374 journal->_FlushLog(false, false);
1375 }
1376