xref: /haiku/src/add-ons/kernel/file_systems/ext2/Journal.cpp (revision ce4e12ca3e341178e3df50ef67cf7c1d724e9559)
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