#ifndef JOURNAL_H
#define JOURNAL_H
#define JOURNAL_MAGIC 0xc03b3998U
#define JOURNAL_DESCRIPTOR_BLOCK 1
#define JOURNAL_COMMIT_BLOCK 2
#define JOURNAL_SUPERBLOCK_V1 3
#define JOURNAL_SUPERBLOCK_V2 4
#define JOURNAL_REVOKE_BLOCK 5
#define JOURNAL_FLAG_ESCAPED 1
#define JOURNAL_FLAG_SAME_UUID 2
#define JOURNAL_FLAG_DELETED 4
#define JOURNAL_FLAG_LAST_TAG 8
#define JOURNAL_FEATURE_COMPATIBLE_CHECKSUM 0x1
#define JOURNAL_FEATURE_INCOMPATIBLE_REVOKE 0x1
#define JOURNAL_FEATURE_INCOMPATIBLE_64BIT 0x2
#define JOURNAL_FEATURE_INCOMPATIBLE_ASYNC_COMMIT 0x4
#define JOURNAL_FEATURE_INCOMPATIBLE_CSUM_V2 0x8
#define JOURNAL_FEATURE_INCOMPATIBLE_CSUM_V3 0x10
#define JOURNAL_KNOWN_READ_ONLY_COMPATIBLE_FEATURES 0
#define JOURNAL_KNOWN_INCOMPATIBLE_FEATURES \
(JOURNAL_FEATURE_INCOMPATIBLE_REVOKE | JOURNAL_FEATURE_INCOMPATIBLE_64BIT \
| JOURNAL_FEATURE_INCOMPATIBLE_CSUM_V3)
#define JOURNAL_CHECKSUM_TYPE_CRC32 0x1
#define JOURNAL_CHECKSUM_TYPE_MD5 0x2
#define JOURNAL_CHECKSUM_TYPE_SHA1 0x3
#define JOURNAL_CHECKSUM_TYPE_CRC32C 0x4
#include "Volume.h"
#include <AutoDeleter.h>
#include <util/DoublyLinkedList.h>
#include "Transaction.h"
class RevokeManager;
struct JournalHeader {
uint32 magic;
uint32 block_type;
uint32 sequence;
char data[0];
uint32 Magic() const
{ return B_BENDIAN_TO_HOST_INT32(magic); }
uint32 BlockType() const
{ return B_BENDIAN_TO_HOST_INT32(block_type); }
uint32 Sequence() const
{ return B_BENDIAN_TO_HOST_INT32(sequence); }
bool CheckMagic() const
{ return Magic() == JOURNAL_MAGIC; }
void IncrementSequence()
{ sequence = B_HOST_TO_BENDIAN_INT32(Sequence() + 1); }
void DecrementSequence()
{ sequence = B_HOST_TO_BENDIAN_INT32(Sequence() - 1); }
void MakeDescriptor(uint32 sequence);
void MakeCommit(uint32 sequence);
} _PACKED;
struct JournalBlockTag {
uint32 block_number;
uint16 checksum;
uint16 flags;
uint32 BlockNumber() const
{ return B_BENDIAN_TO_HOST_INT32(block_number); }
uint16 Flags() const
{ return B_BENDIAN_TO_HOST_INT16(flags); }
void SetBlockNumber(uint32 block)
{ block_number = B_HOST_TO_BENDIAN_INT32(block); }
void SetFlags(uint16 new_flags)
{ flags = B_HOST_TO_BENDIAN_INT16(new_flags); }
void SetLastTagFlag()
{ flags |= B_HOST_TO_BENDIAN_INT16(JOURNAL_FLAG_LAST_TAG); }
void SetEscapedFlag()
{ flags |= B_HOST_TO_BENDIAN_INT16(JOURNAL_FLAG_ESCAPED); }
} _PACKED;
struct JournalBlockTagV3 {
uint32 block_number;
uint32 flags;
uint32 block_number_high;
uint32 checksum;
uint64 BlockNumber(bool has64bits) const
{
uint64 num = B_BENDIAN_TO_HOST_INT32(block_number);
if (has64bits)
num |= ((uint64)B_BENDIAN_TO_HOST_INT32(block_number_high) << 32);
return num;
}
uint32 Flags() const
{ return B_BENDIAN_TO_HOST_INT32(flags); }
void SetBlockNumber(uint64 block, bool has64bits)
{
block_number = B_HOST_TO_BENDIAN_INT32(block & 0xffffffff);
if (has64bits)
block_number_high = B_HOST_TO_BENDIAN_INT32(block >> 32);
}
void SetFlags(uint32 new_flags)
{ flags = B_HOST_TO_BENDIAN_INT32(new_flags); }
void SetLastTagFlag()
{ flags |= B_HOST_TO_BENDIAN_INT32(JOURNAL_FLAG_LAST_TAG); }
void SetEscapedFlag()
{ flags |= B_HOST_TO_BENDIAN_INT32(JOURNAL_FLAG_ESCAPED); }
} _PACKED;
struct JournalBlockTail {
uint32 checksum;
uint32 Checksum() const
{ return B_BENDIAN_TO_HOST_INT32(checksum); }
void SetChecksum(uint32 new_checksum)
{ checksum = B_HOST_TO_BENDIAN_INT32(new_checksum); }
} _PACKED;
struct JournalRevokeHeader {
JournalHeader header;
uint32 num_bytes;
uint32 revoke_blocks[0];
uint32 NumBytes() const
{ return B_BENDIAN_TO_HOST_INT32(num_bytes); }
uint32 RevokeBlock(int offset) const
{ return B_BENDIAN_TO_HOST_INT32(revoke_blocks[offset]); }
} _PACKED;
struct JournalSuperBlock {
JournalHeader header;
uint32 block_size;
uint32 num_blocks;
uint32 first_log_block;
uint32 first_commit_id;
uint32 log_start;
uint32 error;
uint32 compatible_features;
uint32 incompatible_features;
uint32 read_only_compatible_features;
uint8 uuid[16];
uint32 num_users;
uint32 dynamic_superblock;
uint32 max_transaction_blocks;
uint32 max_transaction_data;
uint8 checksum_type;
uint8 padding2[3];
uint32 padding[42];
uint32 checksum;
uint8 user_ids[16*48];
uint32 BlockSize() const
{ return B_BENDIAN_TO_HOST_INT32(block_size); }
uint32 NumBlocks() const
{ return B_BENDIAN_TO_HOST_INT32(num_blocks); }
uint32 FirstLogBlock() const
{ return B_BENDIAN_TO_HOST_INT32(first_log_block); }
uint32 FirstCommitID() const
{ return B_BENDIAN_TO_HOST_INT32(first_commit_id); }
uint32 LogStart() const
{ return B_BENDIAN_TO_HOST_INT32(log_start); }
uint32 IncompatibleFeatures() const
{ return B_BENDIAN_TO_HOST_INT32(incompatible_features); }
uint32 ReadOnlyCompatibleFeatures() const
{ return B_BENDIAN_TO_HOST_INT32(read_only_compatible_features); }
uint32 MaxTransactionBlocks() const
{ return B_BENDIAN_TO_HOST_INT32(max_transaction_blocks); }
uint32 MaxTransactionData() const
{ return B_BENDIAN_TO_HOST_INT32(max_transaction_data); }
uint32 Checksum() const
{ return B_BENDIAN_TO_HOST_INT32(checksum); }
void SetLogStart(uint32 logStart)
{ log_start = B_HOST_TO_BENDIAN_INT32(logStart); }
void SetFirstCommitID(uint32 firstCommitID)
{ first_commit_id = B_HOST_TO_BENDIAN_INT32(firstCommitID); }
void SetChecksum(uint32 checksum)
{ log_start = B_HOST_TO_BENDIAN_INT32(checksum); }
} _PACKED;
class LogEntry;
class Transaction;
typedef DoublyLinkedList<LogEntry> LogEntryList;
class Journal {
public:
Journal(Volume *fsVolume, Volume *jVolume);
virtual ~Journal();
virtual status_t InitCheck();
virtual status_t Uninit();
virtual status_t Recover();
virtual status_t StartLog();
status_t RestartLog();
virtual status_t Lock(Transaction* owner,
bool separateSubTransactions);
virtual status_t Unlock(Transaction* owner, bool success);
virtual status_t MapBlock(off_t logical, fsblock_t& physical);
inline uint32 FreeLogBlocks() const;
status_t FlushLogAndBlocks();
int32 TransactionID() const;
Volume* GetFilesystemVolume()
{ return fFilesystemVolume; }
protected:
Journal();
status_t _WritePartialTransactionToLog(
JournalHeader* descriptorBlock,
bool detached, uint8** escapedBlock,
uint32& logBlock, off_t& blockNumber,
long& cookie,
ArrayDeleter<uint8>& escapedDataDeleter,
uint32& blockCount, bool& finished);
virtual status_t _WriteTransactionToLog();
status_t _SaveSuperBlock();
status_t _LoadSuperBlock();
Volume* fJournalVolume;
void* fJournalBlockCache;
Volume* fFilesystemVolume;
void* fFilesystemBlockCache;
recursive_lock fLock;
Transaction* fOwner;
RevokeManager* fRevokeManager;
status_t fInitStatus;
uint32 fBlockSize;
uint32 fFirstCommitID;
uint32 fFirstCacheCommitID;
uint32 fFirstLogBlock;
uint32 fLogSize;
uint32 fVersion;
bool fIsStarted;
uint32 fLogStart;
uint32 fLogEnd;
uint32 fFreeBlocks;
uint32 fMaxTransactionSize;
uint32 fCurrentCommitID;
LogEntryList fLogEntries;
mutex fLogEntriesLock;
bool fHasSubTransaction;
bool fSeparateSubTransactions;
int32 fUnwrittenTransactions;
int32 fTransactionID;
bool fChecksumEnabled;
bool fChecksumV3Enabled;
bool fFeature64bits;
uint32 fChecksumSeed;
private:
status_t _CheckFeatures(JournalSuperBlock* superblock);
uint32 _Checksum(JournalSuperBlock* superblock);
bool _Checksum(uint8 *block, bool set = false);
uint32 _CountTags(JournalHeader *descriptorBlock);
size_t _TagSize();
status_t _RecoverPassScan(uint32& lastCommitID);
status_t _RecoverPassRevoke(uint32 lastCommitID);
status_t _RecoverPassReplay(uint32 lastCommitID);
status_t _FlushLog(bool canWait, bool flushBlocks);
inline uint32 _WrapAroundLog(uint32 block);
size_t _CurrentTransactionSize() const;
size_t _FullTransactionSize() const;
size_t _MainTransactionSize() const;
virtual status_t _TransactionDone(bool success);
static void _TransactionWritten(int32 transactionID,
int32 event, void* _logEntry);
static void _TransactionIdle(int32 transactionID,
int32 event, void* _journal);
};
#endif