* 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:
parent
56f0a9c0ec
commit
a2e7c7417b
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
@ -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);
|
||||
};
|
||||
|
||||
|
||||
|
@ -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
|
||||
;
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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
|
||||
|
196
src/tests/system/kernel/file_corruption/fs/Transaction.cpp
Normal file
196
src/tests/system/kernel/file_corruption/fs/Transaction.cpp
Normal 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;
|
||||
}
|
||||
}
|
62
src/tests/system/kernel/file_corruption/fs/Transaction.h
Normal file
62
src/tests/system/kernel/file_corruption/fs/Transaction.h
Normal 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
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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,
|
||||
|
@ -17,6 +17,9 @@ DEFINES += DEBUG_APP="\\\"checksumfs\\\"" ;
|
||||
SEARCH_SOURCE += [ FDirName $(SUBDIR) $(DOTDOT) ] ;
|
||||
|
||||
|
||||
SubDirC++Flags -Werror ;
|
||||
|
||||
|
||||
Addon <userland>checksumfs :
|
||||
$(HAIKU_CHECKSUM_FS_SOURCES)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user