* Added Transaction class which wraps a block cache transaction and performs

all other operations required to roll back a transaction. Transactions are
  fully serialized -- due to limitations of our block cache and also to keep
  things simple.
* Use a transaction for all write operations.
* Implemented the directory entry management code (a simple tree algorithm).
* Finished/implemented the FS hooks for directory entry lookup, directory
  iteration, creation, and removal.
* Added non-persistent support for node access times.
* Set the user and group IDs on node creation.
* Added permission checks to several FS hooks.
* BlockAllocator::_Free(): The number of freed blocks was subtracted from
  fFreeBlocks instead of added.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@37478 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold 2010-07-12 13:21:42 +00:00
parent 56f0a9c0ec
commit a2e7c7417b
15 changed files with 2392 additions and 99 deletions

View File

@ -56,9 +56,17 @@ struct checksumfs_node {
} _PACKED;
static const uint32 kCheckSumFSMaxDirEntryTreeDepth = 24;
struct checksumfs_dir_entry_tree {
uint16 depth;
} _PACKED;
struct checksumfs_dir_entry_block {
uint16 entryCount;
uint16 nameEnds[0]; // end (in-block) offsets of the names,
uint16 nameEnds[0]; // end offsets of the names (relative to the
// start of the first name),
// e.g. nameEnds[0] == length of first name
// char names[]; // string of all (unterminated) names,
// directly follows the nameEnds array

View File

@ -8,6 +8,7 @@
#include <fs_cache.h>
#include "Transaction.h"
#include "Volume.h"
@ -25,28 +26,62 @@ public:
Put();
}
void TransferFrom(Block& other)
{
Put();
fVolume = other.fVolume;
fData = other.fData;
fIndex = other.fIndex;
fWritable = other.fWritable;
other.fVolume = NULL;
other.fData = NULL;
}
bool GetReadable(Volume* volume, uint64 blockIndex)
{
Put();
return _Init(volume, blockIndex,
block_cache_get(volume->BlockCache(), blockIndex));
block_cache_get(volume->BlockCache(), blockIndex), false);
}
bool GetWritable(Volume* volume, uint64 blockIndex)
bool GetWritable(Volume* volume, uint64 blockIndex,
Transaction& transaction)
{
Put();
return _Init(volume, blockIndex,
block_cache_get_writable(volume->BlockCache(), blockIndex, -1));
block_cache_get_writable(volume->BlockCache(), blockIndex,
transaction.ID()),
true);
}
bool GetZero(Volume* volume, uint64 blockIndex)
bool GetZero(Volume* volume, uint64 blockIndex, Transaction& transaction)
{
Put();
return _Init(volume, blockIndex,
block_cache_get_empty(volume->BlockCache(), blockIndex, -1));
block_cache_get_empty(volume->BlockCache(), blockIndex,
transaction.ID()),
true);
}
status_t MakeWritable(Transaction& transaction)
{
if (fVolume == NULL)
return B_BAD_VALUE;
if (fWritable)
return B_OK;
status_t error = block_cache_make_writable(fVolume->BlockCache(),
fIndex, transaction.ID());
if (error != B_OK)
return error;
fWritable = true;
return B_OK;
}
void Put()
@ -78,7 +113,8 @@ public:
}
private:
bool _Init(Volume* volume, uint64 blockIndex, const void* data)
bool _Init(Volume* volume, uint64 blockIndex, const void* data,
bool writable)
{
if (data == NULL)
return false;
@ -86,6 +122,7 @@ private:
fVolume = volume;
fData = const_cast<void*>(data);
fIndex = blockIndex;
fWritable = writable;
return true;
}
@ -95,6 +132,7 @@ private:
Volume* fVolume;
void* fData;
uint64 fIndex;
bool fWritable;
};

View File

@ -77,7 +77,7 @@ BlockAllocator::Init(uint64 blockBitmap, uint64 freeBlocks)
status_t
BlockAllocator::Initialize()
BlockAllocator::Initialize(Transaction& transaction)
{
status_t error = Init(kCheckSumFSSuperBlockOffset / B_PAGE_SIZE + 1,
fTotalBlocks);
@ -93,7 +93,7 @@ dprintf("fBitmapBlockCount: %llu\n", fBitmapBlockCount);
// clear the block bitmap
for (uint64 i = 0; i < fBitmapBlockCount; i++) {
Block block;
if (!block.GetZero(fVolume, fBitmapBlock + i))
if (!block.GetZero(fVolume, fBitmapBlock + i, transaction))
return B_ERROR;
}
@ -102,8 +102,10 @@ dprintf("fBitmapBlockCount: %llu\n", fBitmapBlockCount);
uint32 partialBitmapBlock = fTotalBlocks % kBlocksPerBitmapBlock;
if (partialBitmapBlock != 0) {
Block block;
if (!block.GetZero(fVolume, fBitmapBlock + fBitmapBlockCount - 1))
if (!block.GetZero(fVolume, fBitmapBlock + fBitmapBlockCount - 1,
transaction)) {
return B_ERROR;
}
// set full uint32s
uint32* bits = (uint32*)block.Data();
@ -121,7 +123,7 @@ dprintf("fBitmapBlockCount: %llu\n", fBitmapBlockCount);
uint32 partialGroup = fTotalBlocks % kBlocksPerGroup;
for (uint64 i = 0; i < fAllocationGroupCount; i++) {
Block block;
if (!block.GetZero(fVolume, fAllocationGroupBlock + i))
if (!block.GetZero(fVolume, fAllocationGroupBlock + i, transaction))
return B_ERROR;
uint16* counts = (uint16*)block.Data();
@ -143,7 +145,7 @@ dprintf("fBitmapBlockCount: %llu\n", fBitmapBlockCount);
}
// mark all blocks we already use used
error = AllocateExactly(0, fBitmapBlock + fBitmapBlockCount);
error = AllocateExactly(0, fBitmapBlock + fBitmapBlockCount, transaction);
if (error != B_OK)
return error;
@ -159,8 +161,8 @@ dprintf("fBitmapBlockCount: %llu\n", fBitmapBlockCount);
status_t
BlockAllocator::Allocate(uint64 baseHint, uint64 count, uint64& _allocatedBase,
uint64& _allocatedCount)
BlockAllocator::Allocate(uint64 baseHint, uint64 count,
Transaction& transaction, uint64& _allocatedBase, uint64& _allocatedCount)
{
MutexLocker locker(fLock);
dprintf("BlockAllocator::Allocate(%llu, %llu)\n", baseHint, count);
@ -172,29 +174,32 @@ dprintf("BlockAllocator::Allocate(%llu, %llu)\n", baseHint, count);
baseHint = 0;
// search from base hint to end
status_t error = _Allocate(baseHint, fTotalBlocks, count, &_allocatedBase,
_allocatedCount);
status_t error = _Allocate(baseHint, fTotalBlocks, count, transaction,
&_allocatedBase, _allocatedCount);
if (error == B_OK || baseHint == 0)
return error;
// search from 0 to hint
return _Allocate(0, baseHint, count, &_allocatedBase, _allocatedCount);
return _Allocate(0, baseHint, count, transaction, &_allocatedBase,
_allocatedCount);
}
status_t
BlockAllocator::AllocateExactly(uint64 base, uint64 count)
BlockAllocator::AllocateExactly(uint64 base, uint64 count,
Transaction& transaction)
{
MutexLocker locker(fLock);
dprintf("BlockAllocator::AllocateExactly(%llu, %llu)\n", base, count);
uint64 allocated;
status_t error = _Allocate(base, fTotalBlocks, count, NULL, allocated);
status_t error = _Allocate(base, fTotalBlocks, count, transaction, NULL,
allocated);
if (error != B_OK)
return error;
if (allocated < count) {
_Free(base, allocated);
_Free(base, allocated, transaction);
return B_BUSY;
}
@ -203,11 +208,20 @@ dprintf("BlockAllocator::AllocateExactly(%llu, %llu)\n", base, count);
status_t
BlockAllocator::Free(uint64 base, uint64 count)
BlockAllocator::Free(uint64 base, uint64 count, Transaction& transaction)
{
MutexLocker locker(fLock);
return _Free(base, count);
return _Free(base, count, transaction);
}
void
BlockAllocator::ResetFreeBlocks(uint64 count)
{
MutexLocker locker(fLock);
fFreeBlocks = count;
}
@ -231,7 +245,7 @@ BlockAllocator::Free(uint64 base, uint64 count)
*/
status_t
BlockAllocator::_Allocate(uint64 base, uint64 searchEnd, uint64 count,
uint64* _allocatedBase, uint64& _allocatedCount)
Transaction& transaction, uint64* _allocatedBase, uint64& _allocatedCount)
{
ASSERT(base <= fTotalBlocks);
ASSERT(searchEnd <= fTotalBlocks);
@ -249,7 +263,7 @@ BlockAllocator::_Allocate(uint64 base, uint64 searchEnd, uint64 count,
uint32 allocated;
status_t error = _AllocateInGroup(base, searchEnd, toAllocate,
_allocatedBase, allocated);
transaction, _allocatedBase, allocated);
if (error == B_OK) {
fFreeBlocks -= toAllocate;
@ -281,8 +295,8 @@ BlockAllocator::_Allocate(uint64 base, uint64 searchEnd, uint64 count,
while (remaining > 0 && base < searchEnd) {
uint64 toAllocate = std::min(remaining, kBlocksPerGroup - groupOffset);
uint32 allocated;
status_t error = _AllocateInGroup(base, searchEnd, toAllocate, NULL,
allocated);
status_t error = _AllocateInGroup(base, searchEnd, toAllocate,
transaction, NULL, allocated);
if (error != B_OK)
break;
@ -327,7 +341,7 @@ BlockAllocator::_Allocate(uint64 base, uint64 searchEnd, uint64 count,
*/
status_t
BlockAllocator::_AllocateInGroup(uint64 base, uint64 searchEnd, uint32 count,
uint64* _allocatedBase, uint32& _allocatedCount)
Transaction& transaction, uint64* _allocatedBase, uint32& _allocatedCount)
{
dprintf("BlockAllocator::_AllocateInGroup(%llu, %lu)\n", base, count);
ASSERT(count <= kBlocksPerGroup);
@ -338,7 +352,7 @@ dprintf("BlockAllocator::_AllocateInGroup(%llu, %lu)\n", base, count);
Block block;
if (!block.GetWritable(fVolume,
fAllocationGroupBlock + base / kBlocksPerGroup)) {
fAllocationGroupBlock + base / kBlocksPerGroup, transaction)) {
return B_ERROR;
}
@ -354,8 +368,8 @@ dprintf("BlockAllocator::_AllocateInGroup(%llu, %lu)\n", base, count);
if (inBlockOffset != 0) {
if (counts[blockIndex] < kBlocksPerBitmapBlock) {
uint32 allocated;
if (_AllocateInBitmapBlock(base, count, _allocatedBase,
allocated) == B_OK) {
if (_AllocateInBitmapBlock(base, count, transaction,
_allocatedBase, allocated) == B_OK) {
if (inBlockOffset + allocated < kBlocksPerBitmapBlock
|| allocated == remaining) {
_allocatedCount = allocated;
@ -407,7 +421,7 @@ dprintf("BlockAllocator::_AllocateInGroup(%llu, %lu)\n", base, count);
kBlocksPerBitmapBlock - inBlockOffset);
uint32 allocated;
status_t error = _AllocateInBitmapBlock(base, toAllocate,
status_t error = _AllocateInBitmapBlock(base, toAllocate, transaction,
_allocatedBase, allocated);
if (error != B_OK)
break;
@ -453,7 +467,7 @@ dprintf("BlockAllocator::_AllocateInGroup(%llu, %lu)\n", base, count);
*/
status_t
BlockAllocator::_AllocateInBitmapBlock(uint64 base, uint32 count,
uint64* _allocatedBase, uint32& _allocatedCount)
Transaction& transaction, uint64* _allocatedBase, uint32& _allocatedCount)
{
dprintf("BlockAllocator::_AllocateInBitmapBlock(%llu, %lu)\n", base, count);
ASSERT(count <= kBlocksPerBitmapBlock);
@ -461,7 +475,7 @@ dprintf("BlockAllocator::_AllocateInBitmapBlock(%llu, %lu)\n", base, count);
Block block;
if (!block.GetWritable(fVolume,
fBitmapBlock + base / kBlocksPerBitmapBlock)) {
fBitmapBlock + base / kBlocksPerBitmapBlock, transaction)) {
return B_ERROR;
}
@ -546,7 +560,7 @@ dprintf("BlockAllocator::_AllocateInBitmapBlock(%llu, %lu)\n", base, count);
status_t
BlockAllocator::_Free(uint64 base, uint64 count)
BlockAllocator::_Free(uint64 base, uint64 count, Transaction& transaction)
{
if (count == 0)
return B_OK;
@ -561,11 +575,11 @@ dprintf("BlockAllocator::_Free(%llu, %llu)\n", base, count);
while (remaining > 0) {
uint64 toFree = std::min(remaining, kBlocksPerGroup - groupOffset);
status_t error = _FreeInGroup(base, toFree);
status_t error = _FreeInGroup(base, toFree, transaction);
if (error != B_OK)
return error;
fFreeBlocks -= toFree;
fFreeBlocks += toFree;
remaining -= toFree;
base += toFree;
groupOffset = 0;
@ -576,7 +590,8 @@ dprintf("BlockAllocator::_Free(%llu, %llu)\n", base, count);
status_t
BlockAllocator::_FreeInGroup(uint64 base, uint32 count)
BlockAllocator::_FreeInGroup(uint64 base, uint32 count,
Transaction& transaction)
{
if (count == 0)
return B_OK;
@ -587,7 +602,7 @@ dprintf("BlockAllocator::_FreeInGroup(%llu, %lu)\n", base, count);
Block block;
if (!block.GetWritable(fVolume,
fAllocationGroupBlock + base / kBlocksPerGroup)) {
fAllocationGroupBlock + base / kBlocksPerGroup, transaction)) {
return B_ERROR;
}
@ -604,7 +619,7 @@ dprintf("BlockAllocator::_FreeInGroup(%llu, %lu)\n", base, count);
if (counts[blockIndex] + toFree > kBlocksPerBitmapBlock)
return B_BAD_VALUE;
status_t error = _FreeInBitmapBlock(base, toFree);
status_t error = _FreeInBitmapBlock(base, toFree, transaction);
if (error != B_OK)
return error;
@ -620,7 +635,8 @@ dprintf("BlockAllocator::_FreeInGroup(%llu, %lu)\n", base, count);
status_t
BlockAllocator::_FreeInBitmapBlock(uint64 base, uint32 count)
BlockAllocator::_FreeInBitmapBlock(uint64 base, uint32 count,
Transaction& transaction)
{
dprintf("BlockAllocator::_FreeInBitmapBlock(%llu, %lu)\n", base, count);
ASSERT(count <= kBlocksPerBitmapBlock);
@ -628,7 +644,7 @@ dprintf("BlockAllocator::_FreeInBitmapBlock(%llu, %lu)\n", base, count);
Block block;
if (!block.GetWritable(fVolume,
fBitmapBlock + base / kBlocksPerBitmapBlock)) {
fBitmapBlock + base / kBlocksPerBitmapBlock, transaction)) {
return B_ERROR;
}

View File

@ -9,6 +9,7 @@
#include <lock.h>
struct Transaction;
struct Volume;
@ -22,29 +23,40 @@ public:
uint64 FreeBlocks() const { return fFreeBlocks; }
status_t Init(uint64 blockBitmap, uint64 freeBlocks);
status_t Initialize();
status_t Initialize(Transaction& transaction);
status_t Allocate(uint64 baseHint, uint64 count,
Transaction& transaction,
uint64& _allocatedBase,
uint64& _allocatedCount);
status_t AllocateExactly(uint64 base,
uint64 count);
status_t Free(uint64 base, uint64 count);
uint64 count, Transaction& transaction);
status_t Free(uint64 base, uint64 count,
Transaction& transaction);
void ResetFreeBlocks(uint64 count);
// interface for Transaction only
private:
status_t _Allocate(uint64 base, uint64 searchEnd,
uint64 count, uint64* _allocatedBase,
uint64 count, Transaction& transaction,
uint64* _allocatedBase,
uint64& _allocatedCount);
status_t _AllocateInGroup(uint64 base, uint64 searchEnd,
uint32 count, uint64* _allocatedBase,
uint32 count, Transaction& transaction,
uint64* _allocatedBase,
uint32& _allocatedCount);
status_t _AllocateInBitmapBlock(uint64 base,
uint32 count, uint64* _allocatedBase,
uint32 count, Transaction& transaction,
uint64* _allocatedBase,
uint32& _allocatedCount);
status_t _Free(uint64 base, uint64 count);
status_t _FreeInGroup(uint64 base, uint32 count);
status_t _FreeInBitmapBlock(uint64 base, uint32 count);
status_t _Free(uint64 base, uint64 count,
Transaction& transaction);
status_t _FreeInGroup(uint64 base, uint32 count,
Transaction& transaction);
status_t _FreeInBitmapBlock(uint64 base, uint32 count,
Transaction& transaction);
private:
mutex fLock;
@ -60,9 +72,10 @@ private:
class AllocatedBlock {
public:
AllocatedBlock(BlockAllocator* allocator)
AllocatedBlock(BlockAllocator* allocator, Transaction& transaction)
:
fAllocator(allocator),
fTransaction(transaction),
fIndex(0)
{
}
@ -70,7 +83,7 @@ public:
~AllocatedBlock()
{
if (fIndex > 0)
fAllocator->Free(fIndex, 1);
fAllocator->Free(fIndex, 1, fTransaction);
}
uint64 Index() const
@ -81,7 +94,8 @@ public:
status_t Allocate(uint64 baseHint = 0)
{
uint64 allocatedBlocks;
status_t error = fAllocator->Allocate(0, 1, fIndex, allocatedBlocks);
status_t error = fAllocator->Allocate(0, 1, fTransaction, fIndex,
allocatedBlocks);
if (error != B_OK)
fIndex = 0;
return error;
@ -96,6 +110,7 @@ public:
private:
BlockAllocator* fAllocator;
Transaction& fTransaction;
uint64 fIndex;
};

File diff suppressed because it is too large Load Diff

View File

@ -16,6 +16,17 @@ public:
Directory(Volume* volume, uint64 blockIndex,
mode_t mode);
virtual ~Directory();
status_t LookupEntry(const char* name,
uint64& _blockIndex);
status_t LookupNextEntry(const char* name,
char* foundName, size_t& _foundNameLength,
uint64& _blockIndex);
status_t InsertEntry(const char* name, uint64 blockIndex,
Transaction& transaction);
status_t RemoveEntry(const char* name,
Transaction& transaction);
};

View File

@ -13,6 +13,8 @@ UseHeaders [ FDirName $(HAIKU_TOP) src add-ons kernel file_systems ] ;
DEFINES += DEBUG_APP="\\\"checksumfs\\\"" ;
SubDirC++Flags -Werror ;
HAIKU_CHECKSUM_FS_SOURCES =
BlockAllocator.cpp
@ -20,6 +22,7 @@ HAIKU_CHECKSUM_FS_SOURCES =
Directory.cpp
Node.cpp
SuperBlock.cpp
Transaction.cpp
Volume.cpp
;

View File

@ -8,10 +8,21 @@
#include <string.h>
#include <sys/time.h>
#include <unistd.h>
#include "Block.h"
static inline uint64
current_time_nanos()
{
timeval time;
gettimeofday(&time, NULL);
return (uint64)time.tv_sec * 1000000000 + (uint64)time.tv_usec * 1000;
}
Node::Node(Volume* volume, uint64 blockIndex, const checksumfs_node& nodeData)
:
fVolume(volume),
@ -20,6 +31,8 @@ Node::Node(Volume* volume, uint64 blockIndex, const checksumfs_node& nodeData)
fNodeDataDirty(false)
{
_Init();
fAccessedTime = ModificationTime();
}
@ -35,14 +48,18 @@ Node::Node(Volume* volume, uint64 blockIndex, mode_t mode)
fNode.mode = mode;
// set user/group
fNode.uid = geteuid();
fNode.gid = getegid();
// set the times
timeval time;
gettimeofday(&time, NULL);
fNode.creationTime = (uint64)time.tv_sec * 1000000000
+ (uint64)time.tv_usec * 1000;
fNode.modificationTime = fNode.creationTime;
fNode.changeTime = fNode.creationTime;
fAccessedTime = current_time_nanos();
fNode.creationTime = fAccessedTime;
fNode.modificationTime = fAccessedTime;
fNode.changeTime = fAccessedTime;
}
@ -52,14 +69,62 @@ Node::~Node()
}
void
Node::SetParentDirectory(uint32 blockIndex)
{
fNode.parentDirectory = blockIndex;
fNodeDataDirty = true;
}
void
Node::SetHardLinks(uint32 value)
{
fNode.hardLinks = value;
fNodeDataDirty = true;
}
void
Node::SetSize(uint64 size)
{
fNode.size = size;
}
void
Node::Touched(int32 mode)
{
fAccessedTime = current_time_nanos();
switch (mode) {
default:
case NODE_MODIFIED:
fNode.modificationTime = fAccessedTime;
case NODE_STAT_CHANGED:
fNode.changeTime = fAccessedTime;
case NODE_ACCESSED:
break;
}
}
void
Node::RevertNodeData(const checksumfs_node& nodeData)
{
fNode = nodeData;
fNodeDataDirty = false;
}
status_t
Node::Flush()
Node::Flush(Transaction& transaction)
{
if (!fNodeDataDirty)
return B_OK;
Block block;
if (!block.GetWritable(fVolume, fBlockIndex))
if (!block.GetWritable(fVolume, fBlockIndex, transaction))
return B_ERROR;
memcpy(block.Data(), &fNode, sizeof(fNode));

View File

@ -8,14 +8,24 @@
#include <sys/types.h>
#include <AutoLocker.h>
#include <lock.h>
#include "checksumfs.h"
class Transaction;
class Volume;
enum {
NODE_ACCESSED,
NODE_STAT_CHANGED,
NODE_MODIFIED
};
class Node {
public:
Node(Volume* volume, uint64 blockIndex,
@ -24,6 +34,7 @@ public:
mode_t mode);
virtual ~Node();
inline const checksumfs_node& NodeData() const { return fNode; }
inline Volume* GetVolume() const { return fVolume; }
inline uint64 BlockIndex() const { return fBlockIndex; }
inline uint32 Mode() const { return fNode.mode; }
@ -32,16 +43,25 @@ public:
inline uint32 UID() const { return fNode.uid; }
inline uint32 GID() const { return fNode.gid; }
inline uint64 Size() const { return fNode.size; }
inline uint64 AccessedTime() const { return fAccessedTime; }
inline uint64 CreationTime() const;
inline uint64 ModificationTime() const;
inline uint64 ChangeTime() const;
void SetParentDirectory(uint32 blockIndex);
void SetHardLinks(uint32 value);
void SetSize(uint64 size);
void Touched(int32 mode);
inline bool ReadLock();
inline void ReadUnlock();
inline bool WriteLock();
inline void WriteUnlock();
status_t Flush();
void RevertNodeData(const checksumfs_node& nodeData);
status_t Flush(Transaction& transaction);
private:
void _Init();
@ -50,6 +70,7 @@ private:
rw_lock fLock;
Volume* fVolume;
uint64 fBlockIndex;
uint64 fAccessedTime;
checksumfs_node fNode;
bool fNodeDataDirty;
};
@ -111,4 +132,8 @@ Node::WriteUnlock()
}
typedef AutoLocker<Node, AutoLockerReadLocking<Node> > NodeReadLocker;
typedef AutoLocker<Node, AutoLockerWriteLocking<Node> > NodeWriteLocker;
#endif // NODE_H

View File

@ -0,0 +1,196 @@
/*
* Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include "Transaction.h"
#include <algorithm>
#include "BlockAllocator.h"
#include "Volume.h"
static inline bool
swap_if_greater(Node*& a, Node*& b)
{
if (a->BlockIndex() <= b->BlockIndex())
return false;
std::swap(a, b);
return true;
}
// #pragma mark - Transaction
Transaction::Transaction(Volume* volume)
:
fVolume(volume),
fID(-1)
{
}
Transaction::~Transaction()
{
Abort();
}
status_t
Transaction::Start()
{
ASSERT(fID < 0);
fVolume->TransactionStarted();
fID = cache_start_transaction(fVolume->BlockCache());
if (fID < 0) {
fVolume->TransactionFinished();
return fID;
}
fOldFreeBlockCount = fVolume->GetBlockAllocator()->FreeBlocks();
return B_OK;
}
status_t
Transaction::Commit()
{
ASSERT(fID >= 0);
// flush the nodes
for (NodeInfoList::Iterator it = fNodeInfos.GetIterator();
NodeInfo* info = it.Next();) {
status_t error = info->node->Flush(*this);
if (error != B_OK) {
Abort();
return error;
}
}
// commit the cache transaction
status_t error = cache_end_transaction(fVolume->BlockCache(), fID, NULL,
NULL);
if (error != B_OK) {
Abort();
return error;
}
// clean up
_DeleteNodeInfosAndUnlock(false);
fVolume->TransactionFinished();
fID = -1;
return B_OK;
}
void
Transaction::Abort()
{
if (fID < 0)
return;
// abort the cache transaction
cache_abort_transaction(fVolume->BlockCache(), fID);
// revert the nodes
for (NodeInfoList::Iterator it = fNodeInfos.GetIterator();
NodeInfo* info = it.Next();) {
info->node->RevertNodeData(info->oldNodeData);
}
// clean up
_DeleteNodeInfosAndUnlock(true);
fVolume->GetBlockAllocator()->ResetFreeBlocks(fOldFreeBlockCount);
fVolume->TransactionFinished();
fID = -1;
}
status_t
Transaction::AddNode(Node* node, uint32 flags)
{
NodeInfo* info = _GetNodeInfo(node);
if (info != NULL)
return B_OK;
info = new(std::nothrow) NodeInfo;
if (info == NULL)
return B_NO_MEMORY;
node->WriteLock();
info->node = node;
info->oldNodeData = node->NodeData();
info->flags = flags;
fNodeInfos.Add(info);
return B_OK;
}
status_t
Transaction::AddNodes(Node* node1, Node* node2, Node* node3)
{
// sort the nodes
swap_if_greater(node1, node2);
if (node3 != NULL && swap_if_greater(node2, node3))
swap_if_greater(node1, node2);
// add them
status_t error = AddNode(node1);
if (error == B_OK)
error = AddNode(node2);
if (error == B_OK && node3 != NULL)
AddNode(node3);
return error;
}
void
Transaction::KeepNode(Node* node)
{
NodeInfo* info = _GetNodeInfo(node);
if (info == NULL)
return;
info->flags &= ~(uint32)TRANSACTION_DELETE_NODE;
}
Transaction::NodeInfo*
Transaction::_GetNodeInfo(Node* node) const
{
for (NodeInfoList::ConstIterator it = fNodeInfos.GetIterator();
NodeInfo* info = it.Next();) {
if (node == info->node)
return info;
}
return NULL;
}
void
Transaction::_DeleteNodeInfosAndUnlock(bool failed)
{
while (NodeInfo* info = fNodeInfos.RemoveHead()) {
if ((info->flags & TRANSACTION_DELETE_NODE) != 0)
delete info->node;
else
info->node->WriteUnlock();
delete info;
}
}

View File

@ -0,0 +1,62 @@
/*
* Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#ifndef TRANSACTION_H
#define TRANSACTION_H
#include <fs_cache.h>
#include <util/DoublyLinkedList.h>
#include "Node.h"
class Volume;
enum {
TRANSACTION_DELETE_NODE = 0x1
};
class Transaction {
public:
explicit Transaction(Volume* volume);
~Transaction();
int32 ID() const { return fID; }
status_t Start();
status_t Commit();
void Abort();
status_t AddNode(Node* node, uint32 flags = 0);
status_t AddNodes(Node* node1, Node* node2,
Node* node3 = NULL);
void KeepNode(Node* node);
private:
struct NodeInfo : DoublyLinkedListLinkImpl<NodeInfo> {
Node* node;
checksumfs_node oldNodeData;
uint32 flags;
};
typedef DoublyLinkedList<NodeInfo> NodeInfoList;
private:
NodeInfo* _GetNodeInfo(Node* node) const;
void _DeleteNodeInfosAndUnlock(bool failed);
private:
Volume* fVolume;
int32 fID;
NodeInfoList fNodeInfos;
uint64 fOldFreeBlockCount;
};
#endif // TRANSACTION_H

View File

@ -38,6 +38,7 @@ Volume::Volume(uint32 flags)
fBlockAllocator(NULL),
fRootDirectory(NULL)
{
mutex_init(&fTransactionLock, "checksumfs transaction");
}
@ -53,6 +54,8 @@ Volume::~Volume()
close(fFD);
free(fName);
mutex_destroy(&fTransactionLock);
}
@ -170,30 +173,41 @@ Volume::Initialize(const char* name)
if (fName == NULL)
return B_NO_MEMORY;
status_t error = fBlockAllocator->Initialize();
Transaction transaction(this);
status_t error = transaction.Start();
if (error != B_OK)
return error;
error = fBlockAllocator->Initialize(transaction);
if (error != B_OK)
return error;
// create the root directory
error = CreateDirectory(S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH,
fRootDirectory);
transaction, fRootDirectory);
if (error != B_OK)
return error;
error = fRootDirectory->Flush();
if (error != B_OK)
return error;
transaction.KeepNode(fRootDirectory);
fRootDirectory->SetHardLinks(1);
// write the super block
Block block;
if (!block.GetZero(this, kCheckSumFSSuperBlockOffset / B_PAGE_SIZE))
if (!block.GetZero(this, kCheckSumFSSuperBlockOffset / B_PAGE_SIZE,
transaction)) {
return B_ERROR;
}
SuperBlock* superBlock = (SuperBlock*)block.Data();
superBlock->Initialize(this);
block.Put();
// commit the transaction and flush the block cache
error = transaction.Commit();
if (error != B_OK)
return error;
return block_cache_sync(fBlockCache);
}
@ -235,6 +249,13 @@ Volume::PutNode(Node* node)
}
status_t
Volume::RemoveNode(Node* node)
{
return remove_vnode(fFSVolume, node->BlockIndex());
}
status_t
Volume::ReadNode(uint64 blockIndex, Node*& _node)
{
@ -271,10 +292,11 @@ Volume::ReadNode(uint64 blockIndex, Node*& _node)
status_t
Volume::CreateDirectory(mode_t mode, Directory*& _directory)
Volume::CreateDirectory(mode_t mode, Transaction& transaction,
Directory*& _directory)
{
// allocate a free block
AllocatedBlock allocatedBlock(fBlockAllocator);
AllocatedBlock allocatedBlock(fBlockAllocator, transaction);
status_t error = allocatedBlock.Allocate();
if (error != B_OK)
return error;
@ -285,6 +307,13 @@ Volume::CreateDirectory(mode_t mode, Directory*& _directory)
if (directory == NULL)
return B_NO_MEMORY;
// attach the directory to the transaction
error = transaction.AddNode(directory, TRANSACTION_DELETE_NODE);
if (error != B_OK) {
delete directory;
return error;
}
allocatedBlock.Detach();
_directory = directory;
@ -292,6 +321,33 @@ Volume::CreateDirectory(mode_t mode, Directory*& _directory)
}
status_t
Volume::DeleteNode(Node* node)
{
Transaction transaction(this);
status_t error = transaction.Start();
if (error == B_OK) {
error = fBlockAllocator->Free(node->BlockIndex(), 1, transaction);
if (error == B_OK) {
error = transaction.Commit();
if (error != B_OK) {
ERROR("Failed to commit transaction for delete node at %"
B_PRIu64 "\n", node->BlockIndex());
}
} else {
ERROR("Failed to free block for node at %" B_PRIu64 "\n",
node->BlockIndex());
}
} else {
ERROR("Failed to start transaction for delete node at %" B_PRIu64 "\n",
node->BlockIndex());
}
delete node;
return error;
}
status_t
Volume::_Init(uint64 totalBlocks)
{

View File

@ -10,10 +10,13 @@
#include <fs_interface.h>
#include <fs_volume.h>
#include <lock.h>
class BlockAllocator;
class Directory;
class Node;
class Transaction;
class Volume {
@ -34,11 +37,17 @@ public:
status_t PublishNode(Node* node, uint32 flags);
status_t GetNode(uint64 blockIndex, Node*& _node);
status_t PutNode(Node* node);
status_t RemoveNode(Node* node);
status_t ReadNode(uint64 blockIndex, Node*& _node);
status_t CreateDirectory(mode_t mode,
Transaction& transaction,
Directory*& _directory);
status_t DeleteNode(Node* node);
inline void TransactionStarted();
inline void TransactionFinished();
inline dev_t ID() const { return fFSVolume->id; }
inline bool IsReadOnly() const;
@ -62,9 +71,24 @@ private:
char* fName;
BlockAllocator* fBlockAllocator;
Directory* fRootDirectory;
mutex fTransactionLock;
};
void
Volume::TransactionStarted()
{
mutex_lock(&fTransactionLock);
}
void
Volume::TransactionFinished()
{
mutex_unlock(&fTransactionLock);
}
bool
Volume::IsReadOnly() const
{

View File

@ -6,18 +6,24 @@
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <new>
#include <fs_interface.h>
#include <AutoDeleter.h>
#include <AutoLocker.h>
#include <debug.h>
#include "checksumfs.h"
#include "checksumfs_private.h"
#include "DebugSupport.h"
#include "Directory.h"
#include "SuperBlock.h"
#include "Transaction.h"
#include "Volume.h"
@ -34,6 +40,166 @@ set_timespec(timespec& time, uint64 nanos)
}
struct PutNode {
inline void operator()(Node* node)
{
if (node != NULL)
node->GetVolume()->PutNode(node);
}
};
typedef BPrivate::AutoDeleter<Node, PutNode> NodePutter;
static bool
is_user_in_group(gid_t gid)
{
gid_t groups[NGROUPS_MAX];
int groupCount = getgroups(NGROUPS_MAX, groups);
for (int i = 0; i < groupCount; i++) {
if (gid == groups[i])
return true;
}
return gid == getegid();
}
static status_t
check_access(Node* node, uint32 accessFlags)
{
// Note: we assume that the access flags are compatible with the permission
// bits.
STATIC_ASSERT(R_OK == S_IROTH && W_OK == S_IWOTH && X_OK == S_IXOTH);
// get node permissions
int userPermissions = (node->Mode() & S_IRWXU) >> 6;
int groupPermissions = (node->Mode() & S_IRWXG) >> 3;
int otherPermissions = node->Mode() & S_IRWXO;
// get the permissions for this uid/gid
int permissions = 0;
uid_t uid = geteuid();
if (uid == 0) {
// user is root
// root has always read/write permission, but at least one of the
// X bits must be set for execute permission
permissions = userPermissions | groupPermissions | otherPermissions
| R_OK | W_OK;
} else if (uid == node->UID()) {
// user is node owner
permissions = userPermissions;
} else if (is_user_in_group(node->GID())) {
// user is in owning group
permissions = groupPermissions;
} else {
// user is one of the others
permissions = otherPermissions;
}
return (accessFlags & ~permissions) == 0 ? B_OK : B_NOT_ALLOWED;
}
status_t
remove_entry(fs_volume* fsVolume, fs_vnode* parent, const char* name,
bool removeDirectory)
{
Volume* volume = (Volume*)fsVolume->private_volume;
Directory* directory
= dynamic_cast<Directory*>((Node*)parent->private_node);
if (directory == NULL)
return B_NOT_A_DIRECTORY;
if (volume->IsReadOnly())
return B_READ_ONLY_DEVICE;
// Since we need to lock both nodes (the directory and the entry's), this
// is a bit cumbersome. We first look up the entry while having the
// directory read-locked, then drop the read lock, write-lock both nodes
// and check whether anything has changed.
Transaction transaction(volume);
Node* childNode;
NodePutter childNodePutter;
while (true) {
// look up the entry
NodeReadLocker directoryLocker(directory);
uint64 blockIndex;
status_t error = directory->LookupEntry(name, blockIndex);
if (error != B_OK)
RETURN_ERROR(error);
directoryLocker.Unlock();
// get the entry's node
error = volume->GetNode(blockIndex, childNode);
if (error != B_OK)
RETURN_ERROR(error);
childNodePutter.SetTo(childNode);
// start the transaction
error = transaction.Start();
if (error != B_OK)
RETURN_ERROR(error);
// write-lock the nodes
error = transaction.AddNodes(directory, childNode);
if (error != B_OK)
RETURN_ERROR(error);
// check the situation again
error = directory->LookupEntry(name, blockIndex);
if (error != B_OK)
RETURN_ERROR(error);
if (blockIndex != childNode->BlockIndex()) {
transaction.Abort();
continue;
}
break;
}
// check permissions
status_t error = check_access(directory, W_OK);
if (error != B_OK)
return error;
// check whether the child node type agrees with our caller
if (removeDirectory) {
if (!S_ISDIR(childNode->Mode()))
RETURN_ERROR(B_NOT_A_DIRECTORY);
// directory must be empty
if (childNode->Size() > 0)
RETURN_ERROR(B_DIRECTORY_NOT_EMPTY);
} else if (S_ISDIR(childNode->Mode()))
RETURN_ERROR(B_IS_A_DIRECTORY);
// remove the entry
error = directory->RemoveEntry(name, transaction);
if (error != B_OK)
RETURN_ERROR(error);
// update stat data
childNode->SetHardLinks(childNode->HardLinks() - 1);
directory->Touched(NODE_MODIFIED);
// remove the child node, if no longer referenced
if (childNode->HardLinks() == 0) {
error = volume->RemoveNode(childNode);
if (error != B_OK)
return error;
}
// commit the transaction
return transaction.Commit();
}
// #pragma mark - FS operations
@ -211,6 +377,12 @@ checksumfs_lookup(fs_volume* fsVolume, fs_vnode* fsDir, const char* name,
if (directory == NULL)
return B_NOT_A_DIRECTORY;
status_t error = check_access(directory, X_OK);
if (error != B_OK)
return error;
NodeReadLocker nodeLocker(node);
uint64 blockIndex;
if (strcmp(name, ".") == 0) {
@ -218,13 +390,14 @@ checksumfs_lookup(fs_volume* fsVolume, fs_vnode* fsDir, const char* name,
} else if (strcmp(name, "..") == 0) {
blockIndex = directory->ParentDirectory();
} else {
// TODO: Implement!
return B_ENTRY_NOT_FOUND;
status_t error = directory->LookupEntry(name, blockIndex);
if (error != B_OK)
return error;
}
// get the node
Node* childNode;
status_t error = volume->GetNode(blockIndex, childNode);
error = volume->GetNode(blockIndex, childNode);
if (error != B_OK)
return error;
@ -242,6 +415,15 @@ checksumfs_put_vnode(fs_volume* fsVolume, fs_vnode* vnode, bool reenter)
}
static status_t
checksumfs_remove_vnode(fs_volume* fsVolume, fs_vnode* vnode, bool reenter)
{
Volume* volume = (Volume*)fsVolume->private_volume;
Node* node = (Node*)vnode->private_node;
return volume->DeleteNode(node);
}
// #pragma mark - common operations
@ -250,6 +432,8 @@ checksumfs_read_stat(fs_volume* fsVolume, fs_vnode* vnode, struct stat* st)
{
Node* node = (Node*)vnode->private_node;
NodeReadLocker nodeLocker(node);
st->st_mode = node->Mode();
st->st_nlink = node->HardLinks();
st->st_uid = node->UID();
@ -259,12 +443,11 @@ checksumfs_read_stat(fs_volume* fsVolume, fs_vnode* vnode, struct stat* st)
set_timespec(st->st_mtim, node->ModificationTime());
set_timespec(st->st_ctim, node->ChangeTime());
set_timespec(st->st_crtim, node->CreationTime());
st->st_atim = st->st_ctim;
// we don't support access time
set_timespec(st->st_atim, node->AccessedTime());
st->st_type = 0; /* attribute/index type */
st->st_blocks = 1 + (st->st_size + B_PAGE_SIZE - 1) / B_PAGE_SIZE;
// TODO: That does neither count management structures for the content
// nor attributes.
// (for files) nor attributes.
return B_OK;
}
@ -288,7 +471,35 @@ static status_t
checksumfs_open(fs_volume* fsVolume, fs_vnode* vnode, int openMode,
void** _cookie)
{
// TODO: Check permissions!
Volume* volume = (Volume*)fsVolume->private_volume;
Node* node = (Node*)vnode->private_node;
NodeReadLocker nodeLocker(node);
// check the open mode and permissions
uint32 accessFlags = 0;
switch (openMode & O_RWMASK) {
case O_RDONLY:
accessFlags = R_OK;
break;
case O_WRONLY:
accessFlags = W_OK;
break;
case O_RDWR:
accessFlags = R_OK | W_OK;
break;
}
if ((accessFlags & W_OK) != 0) {
if (S_ISDIR(node->Mode()))
return B_IS_A_DIRECTORY;
if (volume->IsReadOnly())
return B_READ_ONLY_DEVICE;
}
status_t error = check_access(node, accessFlags);
if (error != B_OK)
return error;
FileCookie* cookie = new(std::nothrow) FileCookie(openMode);
if (cookie == NULL)
@ -319,55 +530,65 @@ checksumfs_free_cookie(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie)
struct DirCookie {
enum {
DOT,
DOT_DOT,
OTHERS
};
Directory* directory;
int iterationState;
DirCookie(Directory* directory)
:
directory(directory),
iterationState(DOT)
fDirectory(directory)
{
Rewind();
}
Directory* GetDirectory() const
{
return fDirectory;
}
status_t ReadNextEntry(struct dirent* buffer, size_t size,
uint32& _countRead)
{
const char* name;
size_t nameLength;
uint64 blockIndex;
int nextIterationState = OTHERS;
switch (iterationState) {
switch (fIterationState) {
case DOT:
name = ".";
blockIndex = directory->BlockIndex();
nameLength = 1;
blockIndex = fDirectory->BlockIndex();
nextIterationState = DOT_DOT;
break;
case DOT_DOT:
name = "..";
blockIndex = directory->ParentDirectory();
nameLength = 2;
blockIndex = fDirectory->ParentDirectory();
break;
default:
// TODO: Implement!
_countRead = 0;
return B_OK;
{
status_t error = fDirectory->LookupNextEntry(fEntryName,
fEntryName, nameLength, blockIndex);
if (error != B_OK) {
if (error != B_ENTRY_NOT_FOUND)
return error;
_countRead = 0;
return B_OK;
}
name = fEntryName;
break;
}
}
size_t entrySize = sizeof(dirent) + strlen(name);
size_t entrySize = sizeof(dirent) + nameLength;
if (entrySize > size)
return B_BUFFER_OVERFLOW;
buffer->d_dev = directory->GetVolume()->ID();
buffer->d_dev = fDirectory->GetVolume()->ID();
buffer->d_ino = blockIndex;
buffer->d_reclen = entrySize;
strcpy(buffer->d_name, name);
iterationState = nextIterationState;
fIterationState = nextIterationState;
_countRead = 1;
return B_OK;
@ -375,11 +596,81 @@ struct DirCookie {
void Rewind()
{
iterationState = DOT;
fIterationState = DOT;
fEntryName[0] = '\0';
}
private:
enum {
DOT,
DOT_DOT,
OTHERS
};
Directory* fDirectory;
int fIterationState;
char fEntryName[kCheckSumFSNameLength + 1];
};
status_t
checksumfs_create_dir(fs_volume* fsVolume, fs_vnode* parent, const char* name,
int perms)
{
Volume* volume = (Volume*)fsVolume->private_volume;
Directory* directory
= dynamic_cast<Directory*>((Node*)parent->private_node);
if (directory == NULL)
return B_NOT_A_DIRECTORY;
if (volume->IsReadOnly())
return B_READ_ONLY_DEVICE;
status_t error = check_access(directory, W_OK);
if (error != B_OK)
return error;
// start a transaction
Transaction transaction(volume);
error = transaction.Start();
if (error != B_OK)
return error;
// attach the directory to the transaction (write locks it, too)
error = transaction.AddNode(directory);
if (error != B_OK)
return error;
// create a directory node
Directory* newDirectory;
error = volume->CreateDirectory(perms, transaction, newDirectory);
if (error != B_OK)
return error;
// insert the new directory
error = directory->InsertEntry(name, newDirectory->BlockIndex(),
transaction);
if (error != B_OK)
return error;
// update stat data
newDirectory->SetHardLinks(1);
newDirectory->SetParentDirectory(directory->BlockIndex());
directory->Touched(NODE_MODIFIED);
// commit the transaction
return transaction.Commit();
}
status_t
checksumfs_remove_dir(fs_volume* volume, fs_vnode* parent, const char* name)
{
return remove_entry(volume, parent, name, true);
}
static status_t
checksumfs_open_dir(fs_volume* fsVolume, fs_vnode* vnode, void** _cookie)
{
@ -387,6 +678,10 @@ checksumfs_open_dir(fs_volume* fsVolume, fs_vnode* vnode, void** _cookie)
if (directory == NULL)
return B_NOT_A_DIRECTORY;
status_t error = check_access(directory, R_OK);
if (error != B_OK)
return error;
DirCookie* cookie = new(std::nothrow) DirCookie(directory);
if (cookie == NULL)
return B_NO_MEMORY;
@ -420,6 +715,9 @@ checksumfs_read_dir(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie,
return B_OK;
DirCookie* cookie = (DirCookie*)_cookie;
NodeReadLocker nodeLocker(cookie->GetDirectory());
return cookie->ReadNextEntry(buffer, bufferSize, *_num);
}
@ -428,6 +726,9 @@ static status_t
checksumfs_rewind_dir(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie)
{
DirCookie* cookie = (DirCookie*)_cookie;
NodeReadLocker nodeLocker(cookie->GetDirectory());
cookie->Rewind();
return B_OK;
}
@ -547,7 +848,7 @@ fs_vnode_ops gCheckSumFSVnodeOps = {
NULL, // get_vnode_name
checksumfs_put_vnode,
NULL, // checksumfs_remove_vnode,
checksumfs_remove_vnode,
/* VM file access */
NULL, // can_page
@ -588,8 +889,8 @@ fs_vnode_ops gCheckSumFSVnodeOps = {
NULL, // checksumfs_write,
/* directory operations */
NULL, // checksumfs_create_dir,
NULL, // checksumfs_remove_dir,
checksumfs_create_dir,
checksumfs_remove_dir,
checksumfs_open_dir,
checksumfs_close_dir,
checksumfs_free_dir_cookie,

View File

@ -17,6 +17,9 @@ DEFINES += DEBUG_APP="\\\"checksumfs\\\"" ;
SEARCH_SOURCE += [ FDirName $(SUBDIR) $(DOTDOT) ] ;
SubDirC++Flags -Werror ;
Addon <userland>checksumfs :
$(HAIKU_CHECKSUM_FS_SOURCES)