diff --git a/src/tests/system/kernel/file_corruption/fs/Block.cpp b/src/tests/system/kernel/file_corruption/fs/Block.cpp index 6504d74dcb..2c2455639d 100644 --- a/src/tests/system/kernel/file_corruption/fs/Block.cpp +++ b/src/tests/system/kernel/file_corruption/fs/Block.cpp @@ -20,7 +20,7 @@ Block::TransferFrom(Block& other) fVolume = other.fVolume; fData = other.fData; fIndex = other.fIndex; - fWritable = other.fWritable; + fTransaction = other.fTransaction; other.fVolume = NULL; other.fData = NULL; @@ -33,7 +33,7 @@ Block::GetReadable(Volume* volume, uint64 blockIndex) Put(); return _Init(volume, blockIndex, - block_cache_get(volume->BlockCache(), blockIndex), false); + block_cache_get(volume->BlockCache(), blockIndex), NULL); } @@ -42,10 +42,14 @@ Block::GetWritable(Volume* volume, uint64 blockIndex, Transaction& transaction) { Put(); + status_t error = transaction.RegisterBlock(blockIndex); + if (error != B_OK) + return error; + return _Init(volume, blockIndex, block_cache_get_writable(volume->BlockCache(), blockIndex, transaction.ID()), - true); + &transaction); } @@ -54,10 +58,14 @@ Block::GetZero(Volume* volume, uint64 blockIndex, Transaction& transaction) { Put(); + status_t error = transaction.RegisterBlock(blockIndex); + if (error != B_OK) + return error; + return _Init(volume, blockIndex, block_cache_get_empty(volume->BlockCache(), blockIndex, transaction.ID()), - true); + &transaction); } @@ -66,15 +74,21 @@ Block::MakeWritable(Transaction& transaction) { if (fVolume == NULL) return B_BAD_VALUE; - if (fWritable) + if (fTransaction != NULL) return B_OK; - status_t error = block_cache_make_writable(fVolume->BlockCache(), - fIndex, transaction.ID()); + status_t error = transaction.RegisterBlock(fIndex); if (error != B_OK) return error; - fWritable = true; + error = block_cache_make_writable(fVolume->BlockCache(), fIndex, + transaction.ID()); + if (error != B_OK) { + transaction.PutBlock(fIndex, NULL); + return error; + } + + fTransaction = &transaction; return B_OK; } @@ -83,6 +97,9 @@ void Block::Put() { if (fVolume != NULL) { + if (fTransaction != NULL) + fTransaction->PutBlock(fIndex, fData); + block_cache_put(fVolume->BlockCache(), fIndex); fVolume = NULL; fData = NULL; @@ -91,7 +108,8 @@ Block::Put() bool -Block::_Init(Volume* volume, uint64 blockIndex, const void* data, bool writable) +Block::_Init(Volume* volume, uint64 blockIndex, const void* data, + Transaction* transaction) { if (data == NULL) return false; @@ -99,7 +117,7 @@ Block::_Init(Volume* volume, uint64 blockIndex, const void* data, bool writable) fVolume = volume; fData = const_cast(data); fIndex = blockIndex; - fWritable = writable; + fTransaction = transaction; return true; } diff --git a/src/tests/system/kernel/file_corruption/fs/Block.h b/src/tests/system/kernel/file_corruption/fs/Block.h index a56efe64a1..eaafab198a 100644 --- a/src/tests/system/kernel/file_corruption/fs/Block.h +++ b/src/tests/system/kernel/file_corruption/fs/Block.h @@ -35,13 +35,13 @@ public: private: bool _Init(Volume* volume, uint64 blockIndex, - const void* data, bool writable); + const void* data, Transaction* transaction); private: Volume* fVolume; void* fData; uint64 fIndex; - bool fWritable; + Transaction* fTransaction; }; diff --git a/src/tests/system/kernel/file_corruption/fs/Transaction.cpp b/src/tests/system/kernel/file_corruption/fs/Transaction.cpp index 43db8110b6..9a533d39ec 100644 --- a/src/tests/system/kernel/file_corruption/fs/Transaction.cpp +++ b/src/tests/system/kernel/file_corruption/fs/Transaction.cpp @@ -6,8 +6,12 @@ #include "Transaction.h" +#include + #include +#include + #include "BlockAllocator.h" #include "Volume.h" @@ -29,6 +33,8 @@ swap_if_greater(Node*& a, Node*& b) Transaction::Transaction(Volume* volume) : fVolume(volume), + fSHA256(NULL), + fCheckSum(NULL), fID(-1) { } @@ -37,6 +43,9 @@ Transaction::Transaction(Volume* volume) Transaction::~Transaction() { Abort(); + + delete fCheckSum; + delete fSHA256; } @@ -45,6 +54,22 @@ Transaction::Start() { ASSERT(fID < 0); + status_t error = fBlockInfos.Init(); + if (error != B_OK) + return error; + + if (fSHA256 == NULL) { + fSHA256 = new(std::nothrow) SHA256; + if (fSHA256 == NULL) + return B_NO_MEMORY; + } + + if (fCheckSum == NULL) { + fCheckSum = new(std::nothrow) checksum_device_ioctl_check_sum; + if (fCheckSum == NULL) + return B_NO_MEMORY; + } + fVolume->TransactionStarted(); fID = cache_start_transaction(fVolume->BlockCache()); @@ -87,9 +112,25 @@ Transaction::Commit(const PostCommitNotification* notification1, } } + // Make sure the previous transaction is on disk. This is not particularly + // performance friendly, but prevents race conditions between us setting + // the new block check sums and the block writer deciding to write back the + // old block data. + status_t error = block_cache_sync(fVolume->BlockCache()); + if (error != B_OK) { + Abort(); + return error; + } + + // compute the new block check sums + error = _UpdateBlockCheckSums(); + if (error != B_OK) { + Abort(); + return error; + } + // commit the cache transaction - status_t error = cache_end_transaction(fVolume->BlockCache(), fID, NULL, - NULL); + error = cache_end_transaction(fVolume->BlockCache(), fID, NULL, NULL); if (error != B_OK) { Abort(); return error; @@ -128,9 +169,24 @@ Transaction::Abort() info->node->RevertNodeData(info->oldNodeData); } + // revert the block check sums + _RevertBlockCheckSums(); + // clean up + + // delete the node infos _DeleteNodeInfosAndUnlock(true); + // delete the block infos + BlockInfo* blockInfo = fBlockInfos.Clear(true); + while (blockInfo != NULL) { + BlockInfo* nextInfo = blockInfo->hashNext; + block_cache_put(fVolume->BlockCache(), + blockInfo->indexAndCheckSum.blockIndex); + delete nextInfo; + blockInfo = nextInfo; + } + fVolume->GetBlockAllocator()->ResetFreeBlocks(fOldFreeBlockCount); fVolume->TransactionFinished(); @@ -228,6 +284,77 @@ Transaction::KeepNode(Node* node) } +status_t +Transaction::RegisterBlock(uint64 blockIndex) +{ + ASSERT(fID >= 0); + + // look it up -- maybe it's already registered + BlockInfo* info = fBlockInfos.Lookup(blockIndex); + if (info != NULL) { + info->refCount++; + return B_OK; + } + + // nope, create a new one + info = new(std::nothrow) BlockInfo; + if (info == NULL) + return B_NO_MEMORY; + ObjectDeleter infoDeleter(info); + + info->indexAndCheckSum.blockIndex = blockIndex; + info->refCount = 1; + info->dirty = false; + + // get the old check sum + if (ioctl(fVolume->FD(), CHECKSUM_DEVICE_IOCTL_GET_CHECK_SUM, + &info->indexAndCheckSum, sizeof(info->indexAndCheckSum)) < 0) { + return errno; + } + + // get the data (we're fine with read-only) + info->data = block_cache_get(fVolume->BlockCache(), blockIndex); + if (info->data == NULL) { + delete info; + return B_ERROR; + } + + fBlockInfos.Insert(infoDeleter.Detach()); + + return B_OK; +} + + +void +Transaction::PutBlock(uint64 blockIndex, const void* data) +{ + ASSERT(fID >= 0); + + BlockInfo* info = fBlockInfos.Lookup(blockIndex); + if (info == NULL) { + panic("checksumfs: Transaction::PutBlock(): unknown block %" B_PRIu64, + blockIndex); + return; + } + + if (info->refCount == 0) { + panic("checksumfs: Unbalanced Transaction::PutBlock(): for block %" + B_PRIu64, blockIndex); + return; + } + + info->dirty |= data != NULL; + + if (--info->refCount == 0 && !info->dirty) { + // block wasn't got successfully -- remove the info + fBlockInfos.Remove(info); + block_cache_put(fVolume->BlockCache(), + info->indexAndCheckSum.blockIndex); + delete info; + } +} + + Transaction::NodeInfo* Transaction::_GetNodeInfo(Node* node) const { @@ -267,6 +394,54 @@ Transaction::_DeleteNodeInfoAndUnlock(NodeInfo* info, bool failed) } +status_t +Transaction::_UpdateBlockCheckSums() +{ + for (BlockInfoTable::Iterator it = fBlockInfos.GetIterator(); + BlockInfo* info = it.Next();) { + if (info->refCount > 0) { + panic("checksumfs: Transaction::Commit(): block %" B_PRIu64 + " still referenced", info->indexAndCheckSum.blockIndex); + } + + if (!info->dirty) + continue; + + // compute the check sum + fSHA256->Init(); + fSHA256->Update(info->data, B_PAGE_SIZE); + fCheckSum->blockIndex = info->indexAndCheckSum.blockIndex; + fCheckSum->checkSum = fSHA256->Digest(); + + // set it + if (ioctl(fVolume->FD(), CHECKSUM_DEVICE_IOCTL_SET_CHECK_SUM, fCheckSum, + sizeof(*fCheckSum)) < 0) { + return errno; + } + } + + return B_OK; +} + + +status_t +Transaction::_RevertBlockCheckSums() +{ + for (BlockInfoTable::Iterator it = fBlockInfos.GetIterator(); + BlockInfo* info = it.Next();) { + if (!info->dirty) + continue; + + // set the old check sum + if (ioctl(fVolume->FD(), CHECKSUM_DEVICE_IOCTL_SET_CHECK_SUM, + &info->indexAndCheckSum, sizeof(info->indexAndCheckSum)) < 0) { + return errno; + } + } + + return B_OK; +} + // #pragma mark - PostCommitNotification diff --git a/src/tests/system/kernel/file_corruption/fs/Transaction.h b/src/tests/system/kernel/file_corruption/fs/Transaction.h index 6e66fc0da0..07432f4ba5 100644 --- a/src/tests/system/kernel/file_corruption/fs/Transaction.h +++ b/src/tests/system/kernel/file_corruption/fs/Transaction.h @@ -9,7 +9,10 @@ #include #include +#include +#include "CheckSum.h" +#include "driver/checksum_device.h" #include "Node.h" @@ -59,6 +62,9 @@ public: bool IsNodeLocked(Node* node) const { return _GetNodeInfo(node) != NULL; } + status_t RegisterBlock(uint64 blockIndex); + void PutBlock(uint64 blockIndex, const void* data); + private: struct NodeInfo : DoublyLinkedListLinkImpl { Node* node; @@ -68,16 +74,57 @@ private: typedef DoublyLinkedList NodeInfoList; + struct BlockInfo { + checksum_device_ioctl_check_sum indexAndCheckSum; + BlockInfo* hashNext; + const void* data; + int32 refCount; + bool dirty; + }; + + struct BlockInfoHashDefinition { + typedef uint64 KeyType; + typedef BlockInfo ValueType; + + size_t HashKey(uint64 key) const + { + return (size_t)key; + } + + size_t Hash(const BlockInfo* value) const + { + return HashKey(value->indexAndCheckSum.blockIndex); + } + + bool Compare(uint64 key, const BlockInfo* value) const + { + return value->indexAndCheckSum.blockIndex == key; + } + + BlockInfo*& GetLink(BlockInfo* value) const + { + return value->hashNext; + } + }; + + typedef BOpenHashTable BlockInfoTable; + private: NodeInfo* _GetNodeInfo(Node* node) const; void _DeleteNodeInfosAndUnlock(bool failed); void _DeleteNodeInfoAndUnlock(NodeInfo* info, bool failed); + status_t _UpdateBlockCheckSums(); + status_t _RevertBlockCheckSums(); + private: Volume* fVolume; + SHA256* fSHA256; + checksum_device_ioctl_check_sum* fCheckSum; int32 fID; NodeInfoList fNodeInfos; + BlockInfoTable fBlockInfos; uint64 fOldFreeBlockCount; };