* 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:
@@ -56,9 +56,17 @@ struct checksumfs_node {
|
|||||||
} _PACKED;
|
} _PACKED;
|
||||||
|
|
||||||
|
|
||||||
|
static const uint32 kCheckSumFSMaxDirEntryTreeDepth = 24;
|
||||||
|
|
||||||
|
struct checksumfs_dir_entry_tree {
|
||||||
|
uint16 depth;
|
||||||
|
} _PACKED;
|
||||||
|
|
||||||
|
|
||||||
struct checksumfs_dir_entry_block {
|
struct checksumfs_dir_entry_block {
|
||||||
uint16 entryCount;
|
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
|
// e.g. nameEnds[0] == length of first name
|
||||||
// char names[]; // string of all (unterminated) names,
|
// char names[]; // string of all (unterminated) names,
|
||||||
// directly follows the nameEnds array
|
// directly follows the nameEnds array
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include <fs_cache.h>
|
#include <fs_cache.h>
|
||||||
|
|
||||||
|
#include "Transaction.h"
|
||||||
#include "Volume.h"
|
#include "Volume.h"
|
||||||
|
|
||||||
|
|
||||||
@@ -25,28 +26,62 @@ public:
|
|||||||
Put();
|
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)
|
bool GetReadable(Volume* volume, uint64 blockIndex)
|
||||||
{
|
{
|
||||||
Put();
|
Put();
|
||||||
|
|
||||||
return _Init(volume, blockIndex,
|
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();
|
Put();
|
||||||
|
|
||||||
return _Init(volume, blockIndex,
|
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();
|
Put();
|
||||||
|
|
||||||
return _Init(volume, blockIndex,
|
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()
|
void Put()
|
||||||
@@ -78,7 +113,8 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _Init(Volume* volume, uint64 blockIndex, const void* data)
|
bool _Init(Volume* volume, uint64 blockIndex, const void* data,
|
||||||
|
bool writable)
|
||||||
{
|
{
|
||||||
if (data == NULL)
|
if (data == NULL)
|
||||||
return false;
|
return false;
|
||||||
@@ -86,6 +122,7 @@ private:
|
|||||||
fVolume = volume;
|
fVolume = volume;
|
||||||
fData = const_cast<void*>(data);
|
fData = const_cast<void*>(data);
|
||||||
fIndex = blockIndex;
|
fIndex = blockIndex;
|
||||||
|
fWritable = writable;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -95,6 +132,7 @@ private:
|
|||||||
Volume* fVolume;
|
Volume* fVolume;
|
||||||
void* fData;
|
void* fData;
|
||||||
uint64 fIndex;
|
uint64 fIndex;
|
||||||
|
bool fWritable;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ BlockAllocator::Init(uint64 blockBitmap, uint64 freeBlocks)
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
BlockAllocator::Initialize()
|
BlockAllocator::Initialize(Transaction& transaction)
|
||||||
{
|
{
|
||||||
status_t error = Init(kCheckSumFSSuperBlockOffset / B_PAGE_SIZE + 1,
|
status_t error = Init(kCheckSumFSSuperBlockOffset / B_PAGE_SIZE + 1,
|
||||||
fTotalBlocks);
|
fTotalBlocks);
|
||||||
@@ -93,7 +93,7 @@ dprintf("fBitmapBlockCount: %llu\n", fBitmapBlockCount);
|
|||||||
// clear the block bitmap
|
// clear the block bitmap
|
||||||
for (uint64 i = 0; i < fBitmapBlockCount; i++) {
|
for (uint64 i = 0; i < fBitmapBlockCount; i++) {
|
||||||
Block block;
|
Block block;
|
||||||
if (!block.GetZero(fVolume, fBitmapBlock + i))
|
if (!block.GetZero(fVolume, fBitmapBlock + i, transaction))
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,8 +102,10 @@ dprintf("fBitmapBlockCount: %llu\n", fBitmapBlockCount);
|
|||||||
uint32 partialBitmapBlock = fTotalBlocks % kBlocksPerBitmapBlock;
|
uint32 partialBitmapBlock = fTotalBlocks % kBlocksPerBitmapBlock;
|
||||||
if (partialBitmapBlock != 0) {
|
if (partialBitmapBlock != 0) {
|
||||||
Block block;
|
Block block;
|
||||||
if (!block.GetZero(fVolume, fBitmapBlock + fBitmapBlockCount - 1))
|
if (!block.GetZero(fVolume, fBitmapBlock + fBitmapBlockCount - 1,
|
||||||
|
transaction)) {
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
// set full uint32s
|
// set full uint32s
|
||||||
uint32* bits = (uint32*)block.Data();
|
uint32* bits = (uint32*)block.Data();
|
||||||
@@ -121,7 +123,7 @@ dprintf("fBitmapBlockCount: %llu\n", fBitmapBlockCount);
|
|||||||
uint32 partialGroup = fTotalBlocks % kBlocksPerGroup;
|
uint32 partialGroup = fTotalBlocks % kBlocksPerGroup;
|
||||||
for (uint64 i = 0; i < fAllocationGroupCount; i++) {
|
for (uint64 i = 0; i < fAllocationGroupCount; i++) {
|
||||||
Block block;
|
Block block;
|
||||||
if (!block.GetZero(fVolume, fAllocationGroupBlock + i))
|
if (!block.GetZero(fVolume, fAllocationGroupBlock + i, transaction))
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
|
|
||||||
uint16* counts = (uint16*)block.Data();
|
uint16* counts = (uint16*)block.Data();
|
||||||
@@ -143,7 +145,7 @@ dprintf("fBitmapBlockCount: %llu\n", fBitmapBlockCount);
|
|||||||
}
|
}
|
||||||
|
|
||||||
// mark all blocks we already use used
|
// mark all blocks we already use used
|
||||||
error = AllocateExactly(0, fBitmapBlock + fBitmapBlockCount);
|
error = AllocateExactly(0, fBitmapBlock + fBitmapBlockCount, transaction);
|
||||||
if (error != B_OK)
|
if (error != B_OK)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
@@ -159,8 +161,8 @@ dprintf("fBitmapBlockCount: %llu\n", fBitmapBlockCount);
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
BlockAllocator::Allocate(uint64 baseHint, uint64 count, uint64& _allocatedBase,
|
BlockAllocator::Allocate(uint64 baseHint, uint64 count,
|
||||||
uint64& _allocatedCount)
|
Transaction& transaction, uint64& _allocatedBase, uint64& _allocatedCount)
|
||||||
{
|
{
|
||||||
MutexLocker locker(fLock);
|
MutexLocker locker(fLock);
|
||||||
dprintf("BlockAllocator::Allocate(%llu, %llu)\n", baseHint, count);
|
dprintf("BlockAllocator::Allocate(%llu, %llu)\n", baseHint, count);
|
||||||
@@ -172,29 +174,32 @@ dprintf("BlockAllocator::Allocate(%llu, %llu)\n", baseHint, count);
|
|||||||
baseHint = 0;
|
baseHint = 0;
|
||||||
|
|
||||||
// search from base hint to end
|
// search from base hint to end
|
||||||
status_t error = _Allocate(baseHint, fTotalBlocks, count, &_allocatedBase,
|
status_t error = _Allocate(baseHint, fTotalBlocks, count, transaction,
|
||||||
_allocatedCount);
|
&_allocatedBase, _allocatedCount);
|
||||||
if (error == B_OK || baseHint == 0)
|
if (error == B_OK || baseHint == 0)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
// search from 0 to hint
|
// search from 0 to hint
|
||||||
return _Allocate(0, baseHint, count, &_allocatedBase, _allocatedCount);
|
return _Allocate(0, baseHint, count, transaction, &_allocatedBase,
|
||||||
|
_allocatedCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
BlockAllocator::AllocateExactly(uint64 base, uint64 count)
|
BlockAllocator::AllocateExactly(uint64 base, uint64 count,
|
||||||
|
Transaction& transaction)
|
||||||
{
|
{
|
||||||
MutexLocker locker(fLock);
|
MutexLocker locker(fLock);
|
||||||
dprintf("BlockAllocator::AllocateExactly(%llu, %llu)\n", base, count);
|
dprintf("BlockAllocator::AllocateExactly(%llu, %llu)\n", base, count);
|
||||||
|
|
||||||
uint64 allocated;
|
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)
|
if (error != B_OK)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
if (allocated < count) {
|
if (allocated < count) {
|
||||||
_Free(base, allocated);
|
_Free(base, allocated, transaction);
|
||||||
return B_BUSY;
|
return B_BUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,11 +208,20 @@ dprintf("BlockAllocator::AllocateExactly(%llu, %llu)\n", base, count);
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
BlockAllocator::Free(uint64 base, uint64 count)
|
BlockAllocator::Free(uint64 base, uint64 count, Transaction& transaction)
|
||||||
{
|
{
|
||||||
MutexLocker locker(fLock);
|
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
|
status_t
|
||||||
BlockAllocator::_Allocate(uint64 base, uint64 searchEnd, uint64 count,
|
BlockAllocator::_Allocate(uint64 base, uint64 searchEnd, uint64 count,
|
||||||
uint64* _allocatedBase, uint64& _allocatedCount)
|
Transaction& transaction, uint64* _allocatedBase, uint64& _allocatedCount)
|
||||||
{
|
{
|
||||||
ASSERT(base <= fTotalBlocks);
|
ASSERT(base <= fTotalBlocks);
|
||||||
ASSERT(searchEnd <= fTotalBlocks);
|
ASSERT(searchEnd <= fTotalBlocks);
|
||||||
@@ -249,7 +263,7 @@ BlockAllocator::_Allocate(uint64 base, uint64 searchEnd, uint64 count,
|
|||||||
|
|
||||||
uint32 allocated;
|
uint32 allocated;
|
||||||
status_t error = _AllocateInGroup(base, searchEnd, toAllocate,
|
status_t error = _AllocateInGroup(base, searchEnd, toAllocate,
|
||||||
_allocatedBase, allocated);
|
transaction, _allocatedBase, allocated);
|
||||||
if (error == B_OK) {
|
if (error == B_OK) {
|
||||||
fFreeBlocks -= toAllocate;
|
fFreeBlocks -= toAllocate;
|
||||||
|
|
||||||
@@ -281,8 +295,8 @@ BlockAllocator::_Allocate(uint64 base, uint64 searchEnd, uint64 count,
|
|||||||
while (remaining > 0 && base < searchEnd) {
|
while (remaining > 0 && base < searchEnd) {
|
||||||
uint64 toAllocate = std::min(remaining, kBlocksPerGroup - groupOffset);
|
uint64 toAllocate = std::min(remaining, kBlocksPerGroup - groupOffset);
|
||||||
uint32 allocated;
|
uint32 allocated;
|
||||||
status_t error = _AllocateInGroup(base, searchEnd, toAllocate, NULL,
|
status_t error = _AllocateInGroup(base, searchEnd, toAllocate,
|
||||||
allocated);
|
transaction, NULL, allocated);
|
||||||
if (error != B_OK)
|
if (error != B_OK)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -327,7 +341,7 @@ BlockAllocator::_Allocate(uint64 base, uint64 searchEnd, uint64 count,
|
|||||||
*/
|
*/
|
||||||
status_t
|
status_t
|
||||||
BlockAllocator::_AllocateInGroup(uint64 base, uint64 searchEnd, uint32 count,
|
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);
|
dprintf("BlockAllocator::_AllocateInGroup(%llu, %lu)\n", base, count);
|
||||||
ASSERT(count <= kBlocksPerGroup);
|
ASSERT(count <= kBlocksPerGroup);
|
||||||
@@ -338,7 +352,7 @@ dprintf("BlockAllocator::_AllocateInGroup(%llu, %lu)\n", base, count);
|
|||||||
|
|
||||||
Block block;
|
Block block;
|
||||||
if (!block.GetWritable(fVolume,
|
if (!block.GetWritable(fVolume,
|
||||||
fAllocationGroupBlock + base / kBlocksPerGroup)) {
|
fAllocationGroupBlock + base / kBlocksPerGroup, transaction)) {
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -354,8 +368,8 @@ dprintf("BlockAllocator::_AllocateInGroup(%llu, %lu)\n", base, count);
|
|||||||
if (inBlockOffset != 0) {
|
if (inBlockOffset != 0) {
|
||||||
if (counts[blockIndex] < kBlocksPerBitmapBlock) {
|
if (counts[blockIndex] < kBlocksPerBitmapBlock) {
|
||||||
uint32 allocated;
|
uint32 allocated;
|
||||||
if (_AllocateInBitmapBlock(base, count, _allocatedBase,
|
if (_AllocateInBitmapBlock(base, count, transaction,
|
||||||
allocated) == B_OK) {
|
_allocatedBase, allocated) == B_OK) {
|
||||||
if (inBlockOffset + allocated < kBlocksPerBitmapBlock
|
if (inBlockOffset + allocated < kBlocksPerBitmapBlock
|
||||||
|| allocated == remaining) {
|
|| allocated == remaining) {
|
||||||
_allocatedCount = allocated;
|
_allocatedCount = allocated;
|
||||||
@@ -407,7 +421,7 @@ dprintf("BlockAllocator::_AllocateInGroup(%llu, %lu)\n", base, count);
|
|||||||
kBlocksPerBitmapBlock - inBlockOffset);
|
kBlocksPerBitmapBlock - inBlockOffset);
|
||||||
|
|
||||||
uint32 allocated;
|
uint32 allocated;
|
||||||
status_t error = _AllocateInBitmapBlock(base, toAllocate,
|
status_t error = _AllocateInBitmapBlock(base, toAllocate, transaction,
|
||||||
_allocatedBase, allocated);
|
_allocatedBase, allocated);
|
||||||
if (error != B_OK)
|
if (error != B_OK)
|
||||||
break;
|
break;
|
||||||
@@ -453,7 +467,7 @@ dprintf("BlockAllocator::_AllocateInGroup(%llu, %lu)\n", base, count);
|
|||||||
*/
|
*/
|
||||||
status_t
|
status_t
|
||||||
BlockAllocator::_AllocateInBitmapBlock(uint64 base, uint32 count,
|
BlockAllocator::_AllocateInBitmapBlock(uint64 base, uint32 count,
|
||||||
uint64* _allocatedBase, uint32& _allocatedCount)
|
Transaction& transaction, uint64* _allocatedBase, uint32& _allocatedCount)
|
||||||
{
|
{
|
||||||
dprintf("BlockAllocator::_AllocateInBitmapBlock(%llu, %lu)\n", base, count);
|
dprintf("BlockAllocator::_AllocateInBitmapBlock(%llu, %lu)\n", base, count);
|
||||||
ASSERT(count <= kBlocksPerBitmapBlock);
|
ASSERT(count <= kBlocksPerBitmapBlock);
|
||||||
@@ -461,7 +475,7 @@ dprintf("BlockAllocator::_AllocateInBitmapBlock(%llu, %lu)\n", base, count);
|
|||||||
|
|
||||||
Block block;
|
Block block;
|
||||||
if (!block.GetWritable(fVolume,
|
if (!block.GetWritable(fVolume,
|
||||||
fBitmapBlock + base / kBlocksPerBitmapBlock)) {
|
fBitmapBlock + base / kBlocksPerBitmapBlock, transaction)) {
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -546,7 +560,7 @@ dprintf("BlockAllocator::_AllocateInBitmapBlock(%llu, %lu)\n", base, count);
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
BlockAllocator::_Free(uint64 base, uint64 count)
|
BlockAllocator::_Free(uint64 base, uint64 count, Transaction& transaction)
|
||||||
{
|
{
|
||||||
if (count == 0)
|
if (count == 0)
|
||||||
return B_OK;
|
return B_OK;
|
||||||
@@ -561,11 +575,11 @@ dprintf("BlockAllocator::_Free(%llu, %llu)\n", base, count);
|
|||||||
|
|
||||||
while (remaining > 0) {
|
while (remaining > 0) {
|
||||||
uint64 toFree = std::min(remaining, kBlocksPerGroup - groupOffset);
|
uint64 toFree = std::min(remaining, kBlocksPerGroup - groupOffset);
|
||||||
status_t error = _FreeInGroup(base, toFree);
|
status_t error = _FreeInGroup(base, toFree, transaction);
|
||||||
if (error != B_OK)
|
if (error != B_OK)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
fFreeBlocks -= toFree;
|
fFreeBlocks += toFree;
|
||||||
remaining -= toFree;
|
remaining -= toFree;
|
||||||
base += toFree;
|
base += toFree;
|
||||||
groupOffset = 0;
|
groupOffset = 0;
|
||||||
@@ -576,7 +590,8 @@ dprintf("BlockAllocator::_Free(%llu, %llu)\n", base, count);
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
BlockAllocator::_FreeInGroup(uint64 base, uint32 count)
|
BlockAllocator::_FreeInGroup(uint64 base, uint32 count,
|
||||||
|
Transaction& transaction)
|
||||||
{
|
{
|
||||||
if (count == 0)
|
if (count == 0)
|
||||||
return B_OK;
|
return B_OK;
|
||||||
@@ -587,7 +602,7 @@ dprintf("BlockAllocator::_FreeInGroup(%llu, %lu)\n", base, count);
|
|||||||
|
|
||||||
Block block;
|
Block block;
|
||||||
if (!block.GetWritable(fVolume,
|
if (!block.GetWritable(fVolume,
|
||||||
fAllocationGroupBlock + base / kBlocksPerGroup)) {
|
fAllocationGroupBlock + base / kBlocksPerGroup, transaction)) {
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -604,7 +619,7 @@ dprintf("BlockAllocator::_FreeInGroup(%llu, %lu)\n", base, count);
|
|||||||
if (counts[blockIndex] + toFree > kBlocksPerBitmapBlock)
|
if (counts[blockIndex] + toFree > kBlocksPerBitmapBlock)
|
||||||
return B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
status_t error = _FreeInBitmapBlock(base, toFree);
|
status_t error = _FreeInBitmapBlock(base, toFree, transaction);
|
||||||
if (error != B_OK)
|
if (error != B_OK)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
@@ -620,7 +635,8 @@ dprintf("BlockAllocator::_FreeInGroup(%llu, %lu)\n", base, count);
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
BlockAllocator::_FreeInBitmapBlock(uint64 base, uint32 count)
|
BlockAllocator::_FreeInBitmapBlock(uint64 base, uint32 count,
|
||||||
|
Transaction& transaction)
|
||||||
{
|
{
|
||||||
dprintf("BlockAllocator::_FreeInBitmapBlock(%llu, %lu)\n", base, count);
|
dprintf("BlockAllocator::_FreeInBitmapBlock(%llu, %lu)\n", base, count);
|
||||||
ASSERT(count <= kBlocksPerBitmapBlock);
|
ASSERT(count <= kBlocksPerBitmapBlock);
|
||||||
@@ -628,7 +644,7 @@ dprintf("BlockAllocator::_FreeInBitmapBlock(%llu, %lu)\n", base, count);
|
|||||||
|
|
||||||
Block block;
|
Block block;
|
||||||
if (!block.GetWritable(fVolume,
|
if (!block.GetWritable(fVolume,
|
||||||
fBitmapBlock + base / kBlocksPerBitmapBlock)) {
|
fBitmapBlock + base / kBlocksPerBitmapBlock, transaction)) {
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <lock.h>
|
#include <lock.h>
|
||||||
|
|
||||||
|
|
||||||
|
struct Transaction;
|
||||||
struct Volume;
|
struct Volume;
|
||||||
|
|
||||||
|
|
||||||
@@ -22,29 +23,40 @@ public:
|
|||||||
uint64 FreeBlocks() const { return fFreeBlocks; }
|
uint64 FreeBlocks() const { return fFreeBlocks; }
|
||||||
|
|
||||||
status_t Init(uint64 blockBitmap, uint64 freeBlocks);
|
status_t Init(uint64 blockBitmap, uint64 freeBlocks);
|
||||||
status_t Initialize();
|
status_t Initialize(Transaction& transaction);
|
||||||
|
|
||||||
status_t Allocate(uint64 baseHint, uint64 count,
|
status_t Allocate(uint64 baseHint, uint64 count,
|
||||||
|
Transaction& transaction,
|
||||||
uint64& _allocatedBase,
|
uint64& _allocatedBase,
|
||||||
uint64& _allocatedCount);
|
uint64& _allocatedCount);
|
||||||
status_t AllocateExactly(uint64 base,
|
status_t AllocateExactly(uint64 base,
|
||||||
uint64 count);
|
uint64 count, Transaction& transaction);
|
||||||
status_t Free(uint64 base, uint64 count);
|
status_t Free(uint64 base, uint64 count,
|
||||||
|
Transaction& transaction);
|
||||||
|
|
||||||
|
void ResetFreeBlocks(uint64 count);
|
||||||
|
// interface for Transaction only
|
||||||
|
|
||||||
private:
|
private:
|
||||||
status_t _Allocate(uint64 base, uint64 searchEnd,
|
status_t _Allocate(uint64 base, uint64 searchEnd,
|
||||||
uint64 count, uint64* _allocatedBase,
|
uint64 count, Transaction& transaction,
|
||||||
|
uint64* _allocatedBase,
|
||||||
uint64& _allocatedCount);
|
uint64& _allocatedCount);
|
||||||
status_t _AllocateInGroup(uint64 base, uint64 searchEnd,
|
status_t _AllocateInGroup(uint64 base, uint64 searchEnd,
|
||||||
uint32 count, uint64* _allocatedBase,
|
uint32 count, Transaction& transaction,
|
||||||
|
uint64* _allocatedBase,
|
||||||
uint32& _allocatedCount);
|
uint32& _allocatedCount);
|
||||||
status_t _AllocateInBitmapBlock(uint64 base,
|
status_t _AllocateInBitmapBlock(uint64 base,
|
||||||
uint32 count, uint64* _allocatedBase,
|
uint32 count, Transaction& transaction,
|
||||||
|
uint64* _allocatedBase,
|
||||||
uint32& _allocatedCount);
|
uint32& _allocatedCount);
|
||||||
|
|
||||||
status_t _Free(uint64 base, uint64 count);
|
status_t _Free(uint64 base, uint64 count,
|
||||||
status_t _FreeInGroup(uint64 base, uint32 count);
|
Transaction& transaction);
|
||||||
status_t _FreeInBitmapBlock(uint64 base, uint32 count);
|
status_t _FreeInGroup(uint64 base, uint32 count,
|
||||||
|
Transaction& transaction);
|
||||||
|
status_t _FreeInBitmapBlock(uint64 base, uint32 count,
|
||||||
|
Transaction& transaction);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
mutex fLock;
|
mutex fLock;
|
||||||
@@ -60,9 +72,10 @@ private:
|
|||||||
|
|
||||||
class AllocatedBlock {
|
class AllocatedBlock {
|
||||||
public:
|
public:
|
||||||
AllocatedBlock(BlockAllocator* allocator)
|
AllocatedBlock(BlockAllocator* allocator, Transaction& transaction)
|
||||||
:
|
:
|
||||||
fAllocator(allocator),
|
fAllocator(allocator),
|
||||||
|
fTransaction(transaction),
|
||||||
fIndex(0)
|
fIndex(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -70,7 +83,7 @@ public:
|
|||||||
~AllocatedBlock()
|
~AllocatedBlock()
|
||||||
{
|
{
|
||||||
if (fIndex > 0)
|
if (fIndex > 0)
|
||||||
fAllocator->Free(fIndex, 1);
|
fAllocator->Free(fIndex, 1, fTransaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64 Index() const
|
uint64 Index() const
|
||||||
@@ -81,7 +94,8 @@ public:
|
|||||||
status_t Allocate(uint64 baseHint = 0)
|
status_t Allocate(uint64 baseHint = 0)
|
||||||
{
|
{
|
||||||
uint64 allocatedBlocks;
|
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)
|
if (error != B_OK)
|
||||||
fIndex = 0;
|
fIndex = 0;
|
||||||
return error;
|
return error;
|
||||||
@@ -96,6 +110,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
BlockAllocator* fAllocator;
|
BlockAllocator* fAllocator;
|
||||||
|
Transaction& fTransaction;
|
||||||
uint64 fIndex;
|
uint64 fIndex;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -16,6 +16,17 @@ public:
|
|||||||
Directory(Volume* volume, uint64 blockIndex,
|
Directory(Volume* volume, uint64 blockIndex,
|
||||||
mode_t mode);
|
mode_t mode);
|
||||||
virtual ~Directory();
|
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\\\"" ;
|
DEFINES += DEBUG_APP="\\\"checksumfs\\\"" ;
|
||||||
|
|
||||||
|
SubDirC++Flags -Werror ;
|
||||||
|
|
||||||
|
|
||||||
HAIKU_CHECKSUM_FS_SOURCES =
|
HAIKU_CHECKSUM_FS_SOURCES =
|
||||||
BlockAllocator.cpp
|
BlockAllocator.cpp
|
||||||
@@ -20,6 +22,7 @@ HAIKU_CHECKSUM_FS_SOURCES =
|
|||||||
Directory.cpp
|
Directory.cpp
|
||||||
Node.cpp
|
Node.cpp
|
||||||
SuperBlock.cpp
|
SuperBlock.cpp
|
||||||
|
Transaction.cpp
|
||||||
Volume.cpp
|
Volume.cpp
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,21 @@
|
|||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "Block.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)
|
Node::Node(Volume* volume, uint64 blockIndex, const checksumfs_node& nodeData)
|
||||||
:
|
:
|
||||||
fVolume(volume),
|
fVolume(volume),
|
||||||
@@ -20,6 +31,8 @@ Node::Node(Volume* volume, uint64 blockIndex, const checksumfs_node& nodeData)
|
|||||||
fNodeDataDirty(false)
|
fNodeDataDirty(false)
|
||||||
{
|
{
|
||||||
_Init();
|
_Init();
|
||||||
|
|
||||||
|
fAccessedTime = ModificationTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -35,14 +48,18 @@ Node::Node(Volume* volume, uint64 blockIndex, mode_t mode)
|
|||||||
|
|
||||||
fNode.mode = mode;
|
fNode.mode = mode;
|
||||||
|
|
||||||
|
// set user/group
|
||||||
|
fNode.uid = geteuid();
|
||||||
|
fNode.gid = getegid();
|
||||||
|
|
||||||
// set the times
|
// set the times
|
||||||
timeval time;
|
timeval time;
|
||||||
gettimeofday(&time, NULL);
|
gettimeofday(&time, NULL);
|
||||||
|
|
||||||
fNode.creationTime = (uint64)time.tv_sec * 1000000000
|
fAccessedTime = current_time_nanos();
|
||||||
+ (uint64)time.tv_usec * 1000;
|
fNode.creationTime = fAccessedTime;
|
||||||
fNode.modificationTime = fNode.creationTime;
|
fNode.modificationTime = fAccessedTime;
|
||||||
fNode.changeTime = fNode.creationTime;
|
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
|
status_t
|
||||||
Node::Flush()
|
Node::Flush(Transaction& transaction)
|
||||||
{
|
{
|
||||||
if (!fNodeDataDirty)
|
if (!fNodeDataDirty)
|
||||||
return B_OK;
|
return B_OK;
|
||||||
|
|
||||||
Block block;
|
Block block;
|
||||||
if (!block.GetWritable(fVolume, fBlockIndex))
|
if (!block.GetWritable(fVolume, fBlockIndex, transaction))
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
|
|
||||||
memcpy(block.Data(), &fNode, sizeof(fNode));
|
memcpy(block.Data(), &fNode, sizeof(fNode));
|
||||||
|
|||||||
@@ -8,14 +8,24 @@
|
|||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <AutoLocker.h>
|
||||||
|
|
||||||
#include <lock.h>
|
#include <lock.h>
|
||||||
|
|
||||||
#include "checksumfs.h"
|
#include "checksumfs.h"
|
||||||
|
|
||||||
|
|
||||||
|
class Transaction;
|
||||||
class Volume;
|
class Volume;
|
||||||
|
|
||||||
|
|
||||||
|
enum {
|
||||||
|
NODE_ACCESSED,
|
||||||
|
NODE_STAT_CHANGED,
|
||||||
|
NODE_MODIFIED
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class Node {
|
class Node {
|
||||||
public:
|
public:
|
||||||
Node(Volume* volume, uint64 blockIndex,
|
Node(Volume* volume, uint64 blockIndex,
|
||||||
@@ -24,6 +34,7 @@ public:
|
|||||||
mode_t mode);
|
mode_t mode);
|
||||||
virtual ~Node();
|
virtual ~Node();
|
||||||
|
|
||||||
|
inline const checksumfs_node& NodeData() const { return fNode; }
|
||||||
inline Volume* GetVolume() const { return fVolume; }
|
inline Volume* GetVolume() const { return fVolume; }
|
||||||
inline uint64 BlockIndex() const { return fBlockIndex; }
|
inline uint64 BlockIndex() const { return fBlockIndex; }
|
||||||
inline uint32 Mode() const { return fNode.mode; }
|
inline uint32 Mode() const { return fNode.mode; }
|
||||||
@@ -32,16 +43,25 @@ public:
|
|||||||
inline uint32 UID() const { return fNode.uid; }
|
inline uint32 UID() const { return fNode.uid; }
|
||||||
inline uint32 GID() const { return fNode.gid; }
|
inline uint32 GID() const { return fNode.gid; }
|
||||||
inline uint64 Size() const { return fNode.size; }
|
inline uint64 Size() const { return fNode.size; }
|
||||||
|
inline uint64 AccessedTime() const { return fAccessedTime; }
|
||||||
inline uint64 CreationTime() const;
|
inline uint64 CreationTime() const;
|
||||||
inline uint64 ModificationTime() const;
|
inline uint64 ModificationTime() const;
|
||||||
inline uint64 ChangeTime() 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 bool ReadLock();
|
||||||
inline void ReadUnlock();
|
inline void ReadUnlock();
|
||||||
inline bool WriteLock();
|
inline bool WriteLock();
|
||||||
inline void WriteUnlock();
|
inline void WriteUnlock();
|
||||||
|
|
||||||
status_t Flush();
|
void RevertNodeData(const checksumfs_node& nodeData);
|
||||||
|
|
||||||
|
status_t Flush(Transaction& transaction);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void _Init();
|
void _Init();
|
||||||
@@ -50,6 +70,7 @@ private:
|
|||||||
rw_lock fLock;
|
rw_lock fLock;
|
||||||
Volume* fVolume;
|
Volume* fVolume;
|
||||||
uint64 fBlockIndex;
|
uint64 fBlockIndex;
|
||||||
|
uint64 fAccessedTime;
|
||||||
checksumfs_node fNode;
|
checksumfs_node fNode;
|
||||||
bool fNodeDataDirty;
|
bool fNodeDataDirty;
|
||||||
};
|
};
|
||||||
@@ -111,4 +132,8 @@ Node::WriteUnlock()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
typedef AutoLocker<Node, AutoLockerReadLocking<Node> > NodeReadLocker;
|
||||||
|
typedef AutoLocker<Node, AutoLockerWriteLocking<Node> > NodeWriteLocker;
|
||||||
|
|
||||||
|
|
||||||
#endif // NODE_H
|
#endif // NODE_H
|
||||||
|
|||||||
@@ -0,0 +1,196 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010, Ingo Weinhold, [email protected].
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010, Ingo Weinhold, [email protected].
|
||||||
|
* 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),
|
fBlockAllocator(NULL),
|
||||||
fRootDirectory(NULL)
|
fRootDirectory(NULL)
|
||||||
{
|
{
|
||||||
|
mutex_init(&fTransactionLock, "checksumfs transaction");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -53,6 +54,8 @@ Volume::~Volume()
|
|||||||
close(fFD);
|
close(fFD);
|
||||||
|
|
||||||
free(fName);
|
free(fName);
|
||||||
|
|
||||||
|
mutex_destroy(&fTransactionLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -170,30 +173,41 @@ Volume::Initialize(const char* name)
|
|||||||
if (fName == NULL)
|
if (fName == NULL)
|
||||||
return B_NO_MEMORY;
|
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)
|
if (error != B_OK)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
// create the root directory
|
// create the root directory
|
||||||
error = CreateDirectory(S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH,
|
error = CreateDirectory(S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH,
|
||||||
fRootDirectory);
|
transaction, fRootDirectory);
|
||||||
if (error != B_OK)
|
if (error != B_OK)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
error = fRootDirectory->Flush();
|
transaction.KeepNode(fRootDirectory);
|
||||||
if (error != B_OK)
|
fRootDirectory->SetHardLinks(1);
|
||||||
return error;
|
|
||||||
|
|
||||||
// write the super block
|
// write the super block
|
||||||
Block block;
|
Block block;
|
||||||
if (!block.GetZero(this, kCheckSumFSSuperBlockOffset / B_PAGE_SIZE))
|
if (!block.GetZero(this, kCheckSumFSSuperBlockOffset / B_PAGE_SIZE,
|
||||||
|
transaction)) {
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
SuperBlock* superBlock = (SuperBlock*)block.Data();
|
SuperBlock* superBlock = (SuperBlock*)block.Data();
|
||||||
superBlock->Initialize(this);
|
superBlock->Initialize(this);
|
||||||
|
|
||||||
block.Put();
|
block.Put();
|
||||||
|
|
||||||
|
// commit the transaction and flush the block cache
|
||||||
|
error = transaction.Commit();
|
||||||
|
if (error != B_OK)
|
||||||
|
return error;
|
||||||
|
|
||||||
return block_cache_sync(fBlockCache);
|
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
|
status_t
|
||||||
Volume::ReadNode(uint64 blockIndex, Node*& _node)
|
Volume::ReadNode(uint64 blockIndex, Node*& _node)
|
||||||
{
|
{
|
||||||
@@ -271,10 +292,11 @@ Volume::ReadNode(uint64 blockIndex, Node*& _node)
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
Volume::CreateDirectory(mode_t mode, Directory*& _directory)
|
Volume::CreateDirectory(mode_t mode, Transaction& transaction,
|
||||||
|
Directory*& _directory)
|
||||||
{
|
{
|
||||||
// allocate a free block
|
// allocate a free block
|
||||||
AllocatedBlock allocatedBlock(fBlockAllocator);
|
AllocatedBlock allocatedBlock(fBlockAllocator, transaction);
|
||||||
status_t error = allocatedBlock.Allocate();
|
status_t error = allocatedBlock.Allocate();
|
||||||
if (error != B_OK)
|
if (error != B_OK)
|
||||||
return error;
|
return error;
|
||||||
@@ -285,6 +307,13 @@ Volume::CreateDirectory(mode_t mode, Directory*& _directory)
|
|||||||
if (directory == NULL)
|
if (directory == NULL)
|
||||||
return B_NO_MEMORY;
|
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();
|
allocatedBlock.Detach();
|
||||||
_directory = directory;
|
_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
|
status_t
|
||||||
Volume::_Init(uint64 totalBlocks)
|
Volume::_Init(uint64 totalBlocks)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,10 +10,13 @@
|
|||||||
#include <fs_interface.h>
|
#include <fs_interface.h>
|
||||||
#include <fs_volume.h>
|
#include <fs_volume.h>
|
||||||
|
|
||||||
|
#include <lock.h>
|
||||||
|
|
||||||
|
|
||||||
class BlockAllocator;
|
class BlockAllocator;
|
||||||
class Directory;
|
class Directory;
|
||||||
class Node;
|
class Node;
|
||||||
|
class Transaction;
|
||||||
|
|
||||||
|
|
||||||
class Volume {
|
class Volume {
|
||||||
@@ -34,11 +37,17 @@ public:
|
|||||||
status_t PublishNode(Node* node, uint32 flags);
|
status_t PublishNode(Node* node, uint32 flags);
|
||||||
status_t GetNode(uint64 blockIndex, Node*& _node);
|
status_t GetNode(uint64 blockIndex, Node*& _node);
|
||||||
status_t PutNode(Node* node);
|
status_t PutNode(Node* node);
|
||||||
|
status_t RemoveNode(Node* node);
|
||||||
|
|
||||||
status_t ReadNode(uint64 blockIndex, Node*& _node);
|
status_t ReadNode(uint64 blockIndex, Node*& _node);
|
||||||
|
|
||||||
status_t CreateDirectory(mode_t mode,
|
status_t CreateDirectory(mode_t mode,
|
||||||
|
Transaction& transaction,
|
||||||
Directory*& _directory);
|
Directory*& _directory);
|
||||||
|
status_t DeleteNode(Node* node);
|
||||||
|
|
||||||
|
inline void TransactionStarted();
|
||||||
|
inline void TransactionFinished();
|
||||||
|
|
||||||
inline dev_t ID() const { return fFSVolume->id; }
|
inline dev_t ID() const { return fFSVolume->id; }
|
||||||
inline bool IsReadOnly() const;
|
inline bool IsReadOnly() const;
|
||||||
@@ -62,9 +71,24 @@ private:
|
|||||||
char* fName;
|
char* fName;
|
||||||
BlockAllocator* fBlockAllocator;
|
BlockAllocator* fBlockAllocator;
|
||||||
Directory* fRootDirectory;
|
Directory* fRootDirectory;
|
||||||
|
mutex fTransactionLock;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Volume::TransactionStarted()
|
||||||
|
{
|
||||||
|
mutex_lock(&fTransactionLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Volume::TransactionFinished()
|
||||||
|
{
|
||||||
|
mutex_unlock(&fTransactionLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Volume::IsReadOnly() const
|
Volume::IsReadOnly() const
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,18 +6,24 @@
|
|||||||
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <new>
|
#include <new>
|
||||||
|
|
||||||
#include <fs_interface.h>
|
#include <fs_interface.h>
|
||||||
|
|
||||||
#include <AutoDeleter.h>
|
#include <AutoDeleter.h>
|
||||||
|
#include <AutoLocker.h>
|
||||||
|
|
||||||
|
#include <debug.h>
|
||||||
|
|
||||||
#include "checksumfs.h"
|
#include "checksumfs.h"
|
||||||
#include "checksumfs_private.h"
|
#include "checksumfs_private.h"
|
||||||
#include "DebugSupport.h"
|
#include "DebugSupport.h"
|
||||||
#include "Directory.h"
|
#include "Directory.h"
|
||||||
#include "SuperBlock.h"
|
#include "SuperBlock.h"
|
||||||
|
#include "Transaction.h"
|
||||||
#include "Volume.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
|
// #pragma mark - FS operations
|
||||||
|
|
||||||
|
|
||||||
@@ -211,6 +377,12 @@ checksumfs_lookup(fs_volume* fsVolume, fs_vnode* fsDir, const char* name,
|
|||||||
if (directory == NULL)
|
if (directory == NULL)
|
||||||
return B_NOT_A_DIRECTORY;
|
return B_NOT_A_DIRECTORY;
|
||||||
|
|
||||||
|
status_t error = check_access(directory, X_OK);
|
||||||
|
if (error != B_OK)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
NodeReadLocker nodeLocker(node);
|
||||||
|
|
||||||
uint64 blockIndex;
|
uint64 blockIndex;
|
||||||
|
|
||||||
if (strcmp(name, ".") == 0) {
|
if (strcmp(name, ".") == 0) {
|
||||||
@@ -218,13 +390,14 @@ checksumfs_lookup(fs_volume* fsVolume, fs_vnode* fsDir, const char* name,
|
|||||||
} else if (strcmp(name, "..") == 0) {
|
} else if (strcmp(name, "..") == 0) {
|
||||||
blockIndex = directory->ParentDirectory();
|
blockIndex = directory->ParentDirectory();
|
||||||
} else {
|
} else {
|
||||||
// TODO: Implement!
|
status_t error = directory->LookupEntry(name, blockIndex);
|
||||||
return B_ENTRY_NOT_FOUND;
|
if (error != B_OK)
|
||||||
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the node
|
// get the node
|
||||||
Node* childNode;
|
Node* childNode;
|
||||||
status_t error = volume->GetNode(blockIndex, childNode);
|
error = volume->GetNode(blockIndex, childNode);
|
||||||
if (error != B_OK)
|
if (error != B_OK)
|
||||||
return error;
|
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
|
// #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;
|
Node* node = (Node*)vnode->private_node;
|
||||||
|
|
||||||
|
NodeReadLocker nodeLocker(node);
|
||||||
|
|
||||||
st->st_mode = node->Mode();
|
st->st_mode = node->Mode();
|
||||||
st->st_nlink = node->HardLinks();
|
st->st_nlink = node->HardLinks();
|
||||||
st->st_uid = node->UID();
|
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_mtim, node->ModificationTime());
|
||||||
set_timespec(st->st_ctim, node->ChangeTime());
|
set_timespec(st->st_ctim, node->ChangeTime());
|
||||||
set_timespec(st->st_crtim, node->CreationTime());
|
set_timespec(st->st_crtim, node->CreationTime());
|
||||||
st->st_atim = st->st_ctim;
|
set_timespec(st->st_atim, node->AccessedTime());
|
||||||
// we don't support access time
|
|
||||||
st->st_type = 0; /* attribute/index type */
|
st->st_type = 0; /* attribute/index type */
|
||||||
st->st_blocks = 1 + (st->st_size + B_PAGE_SIZE - 1) / B_PAGE_SIZE;
|
st->st_blocks = 1 + (st->st_size + B_PAGE_SIZE - 1) / B_PAGE_SIZE;
|
||||||
// TODO: That does neither count management structures for the content
|
// TODO: That does neither count management structures for the content
|
||||||
// nor attributes.
|
// (for files) nor attributes.
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
@@ -288,7 +471,35 @@ static status_t
|
|||||||
checksumfs_open(fs_volume* fsVolume, fs_vnode* vnode, int openMode,
|
checksumfs_open(fs_volume* fsVolume, fs_vnode* vnode, int openMode,
|
||||||
void** _cookie)
|
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);
|
FileCookie* cookie = new(std::nothrow) FileCookie(openMode);
|
||||||
if (cookie == NULL)
|
if (cookie == NULL)
|
||||||
@@ -319,55 +530,65 @@ checksumfs_free_cookie(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie)
|
|||||||
|
|
||||||
|
|
||||||
struct DirCookie {
|
struct DirCookie {
|
||||||
enum {
|
|
||||||
DOT,
|
|
||||||
DOT_DOT,
|
|
||||||
OTHERS
|
|
||||||
};
|
|
||||||
|
|
||||||
Directory* directory;
|
|
||||||
int iterationState;
|
|
||||||
|
|
||||||
DirCookie(Directory* directory)
|
DirCookie(Directory* directory)
|
||||||
:
|
:
|
||||||
directory(directory),
|
fDirectory(directory)
|
||||||
iterationState(DOT)
|
|
||||||
{
|
{
|
||||||
|
Rewind();
|
||||||
|
}
|
||||||
|
|
||||||
|
Directory* GetDirectory() const
|
||||||
|
{
|
||||||
|
return fDirectory;
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t ReadNextEntry(struct dirent* buffer, size_t size,
|
status_t ReadNextEntry(struct dirent* buffer, size_t size,
|
||||||
uint32& _countRead)
|
uint32& _countRead)
|
||||||
{
|
{
|
||||||
const char* name;
|
const char* name;
|
||||||
|
size_t nameLength;
|
||||||
uint64 blockIndex;
|
uint64 blockIndex;
|
||||||
|
|
||||||
int nextIterationState = OTHERS;
|
int nextIterationState = OTHERS;
|
||||||
switch (iterationState) {
|
switch (fIterationState) {
|
||||||
case DOT:
|
case DOT:
|
||||||
name = ".";
|
name = ".";
|
||||||
blockIndex = directory->BlockIndex();
|
nameLength = 1;
|
||||||
|
blockIndex = fDirectory->BlockIndex();
|
||||||
nextIterationState = DOT_DOT;
|
nextIterationState = DOT_DOT;
|
||||||
break;
|
break;
|
||||||
case DOT_DOT:
|
case DOT_DOT:
|
||||||
name = "..";
|
name = "..";
|
||||||
blockIndex = directory->ParentDirectory();
|
nameLength = 2;
|
||||||
|
blockIndex = fDirectory->ParentDirectory();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// TODO: Implement!
|
{
|
||||||
_countRead = 0;
|
status_t error = fDirectory->LookupNextEntry(fEntryName,
|
||||||
return B_OK;
|
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)
|
if (entrySize > size)
|
||||||
return B_BUFFER_OVERFLOW;
|
return B_BUFFER_OVERFLOW;
|
||||||
|
|
||||||
buffer->d_dev = directory->GetVolume()->ID();
|
buffer->d_dev = fDirectory->GetVolume()->ID();
|
||||||
buffer->d_ino = blockIndex;
|
buffer->d_ino = blockIndex;
|
||||||
buffer->d_reclen = entrySize;
|
buffer->d_reclen = entrySize;
|
||||||
strcpy(buffer->d_name, name);
|
strcpy(buffer->d_name, name);
|
||||||
|
|
||||||
iterationState = nextIterationState;
|
fIterationState = nextIterationState;
|
||||||
|
|
||||||
_countRead = 1;
|
_countRead = 1;
|
||||||
return B_OK;
|
return B_OK;
|
||||||
@@ -375,11 +596,81 @@ struct DirCookie {
|
|||||||
|
|
||||||
void Rewind()
|
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
|
static status_t
|
||||||
checksumfs_open_dir(fs_volume* fsVolume, fs_vnode* vnode, void** _cookie)
|
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)
|
if (directory == NULL)
|
||||||
return B_NOT_A_DIRECTORY;
|
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);
|
DirCookie* cookie = new(std::nothrow) DirCookie(directory);
|
||||||
if (cookie == NULL)
|
if (cookie == NULL)
|
||||||
return B_NO_MEMORY;
|
return B_NO_MEMORY;
|
||||||
@@ -420,6 +715,9 @@ checksumfs_read_dir(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie,
|
|||||||
return B_OK;
|
return B_OK;
|
||||||
|
|
||||||
DirCookie* cookie = (DirCookie*)_cookie;
|
DirCookie* cookie = (DirCookie*)_cookie;
|
||||||
|
|
||||||
|
NodeReadLocker nodeLocker(cookie->GetDirectory());
|
||||||
|
|
||||||
return cookie->ReadNextEntry(buffer, bufferSize, *_num);
|
return cookie->ReadNextEntry(buffer, bufferSize, *_num);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -428,6 +726,9 @@ static status_t
|
|||||||
checksumfs_rewind_dir(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie)
|
checksumfs_rewind_dir(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie)
|
||||||
{
|
{
|
||||||
DirCookie* cookie = (DirCookie*)_cookie;
|
DirCookie* cookie = (DirCookie*)_cookie;
|
||||||
|
|
||||||
|
NodeReadLocker nodeLocker(cookie->GetDirectory());
|
||||||
|
|
||||||
cookie->Rewind();
|
cookie->Rewind();
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
@@ -547,7 +848,7 @@ fs_vnode_ops gCheckSumFSVnodeOps = {
|
|||||||
NULL, // get_vnode_name
|
NULL, // get_vnode_name
|
||||||
|
|
||||||
checksumfs_put_vnode,
|
checksumfs_put_vnode,
|
||||||
NULL, // checksumfs_remove_vnode,
|
checksumfs_remove_vnode,
|
||||||
|
|
||||||
/* VM file access */
|
/* VM file access */
|
||||||
NULL, // can_page
|
NULL, // can_page
|
||||||
@@ -588,8 +889,8 @@ fs_vnode_ops gCheckSumFSVnodeOps = {
|
|||||||
NULL, // checksumfs_write,
|
NULL, // checksumfs_write,
|
||||||
|
|
||||||
/* directory operations */
|
/* directory operations */
|
||||||
NULL, // checksumfs_create_dir,
|
checksumfs_create_dir,
|
||||||
NULL, // checksumfs_remove_dir,
|
checksumfs_remove_dir,
|
||||||
checksumfs_open_dir,
|
checksumfs_open_dir,
|
||||||
checksumfs_close_dir,
|
checksumfs_close_dir,
|
||||||
checksumfs_free_dir_cookie,
|
checksumfs_free_dir_cookie,
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ DEFINES += DEBUG_APP="\\\"checksumfs\\\"" ;
|
|||||||
SEARCH_SOURCE += [ FDirName $(SUBDIR) $(DOTDOT) ] ;
|
SEARCH_SOURCE += [ FDirName $(SUBDIR) $(DOTDOT) ] ;
|
||||||
|
|
||||||
|
|
||||||
|
SubDirC++Flags -Werror ;
|
||||||
|
|
||||||
|
|
||||||
Addon <userland>checksumfs :
|
Addon <userland>checksumfs :
|
||||||
$(HAIKU_CHECKSUM_FS_SOURCES)
|
$(HAIKU_CHECKSUM_FS_SOURCES)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user