* added fsblock_t and fileblock_t definitions, used them when needed.
* validate fBlockShift in superblock. * Volume::AllocateBlocks() now uses an absolute fsblock instead of a blockgroup related fsblock. * AllocationBlockGroup now provides absolute fsblock values. * added support for extents feature: ExtentStream class is the equivalent for DataStream class for extent operations. The extent tree implementation is very basic, should work for normal growing/shrinking operations, but not for sparse files. When enlarging a file and extent tree is full, the root is moved in a new block and a new level is added on top. Extents can usually be extended when adjacent blocks are allocated. Shrinking happens by removing leafs one after another. * removed empty IndexedDirectoryIterator.* git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@39886 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -39,7 +39,7 @@ public:
|
||||
status_t ScanFreeRanges();
|
||||
bool IsFull() const;
|
||||
|
||||
status_t Allocate(Transaction& transaction, uint32 start,
|
||||
status_t Allocate(Transaction& transaction, fsblock_t start,
|
||||
uint32 length);
|
||||
status_t Free(Transaction& transaction, uint32 start,
|
||||
uint32 length);
|
||||
@@ -48,9 +48,9 @@ public:
|
||||
|
||||
uint32 NumBits() const;
|
||||
uint32 FreeBits() const;
|
||||
uint32 Start() const;
|
||||
fsblock_t Start() const;
|
||||
|
||||
uint32 LargestStart() const;
|
||||
fsblock_t LargestStart() const;
|
||||
uint32 LargestLength() const;
|
||||
|
||||
// TransactionListener implementation
|
||||
@@ -70,9 +70,9 @@ private:
|
||||
mutex fTransactionLock;
|
||||
int32 fCurrentTransaction;
|
||||
|
||||
uint32 fStart;
|
||||
fsblock_t fStart;
|
||||
uint32 fNumBits;
|
||||
off_t fBitmapBlock;
|
||||
fsblock_t fBitmapBlock;
|
||||
|
||||
uint32 fFreeBits;
|
||||
uint32 fFirstFree;
|
||||
@@ -122,6 +122,7 @@ AllocationBlockGroup::Initialize(Volume* volume, uint32 blockGroup,
|
||||
fVolume = volume;
|
||||
fBlockGroup = blockGroup;
|
||||
fNumBits = numBits;
|
||||
fStart = blockGroup * numBits + fVolume->FirstDataBlock();
|
||||
|
||||
status_t status = fVolume->GetBlockGroup(blockGroup, &fGroupDescriptor);
|
||||
if (status != B_OK)
|
||||
@@ -187,13 +188,17 @@ AllocationBlockGroup::IsFull() const
|
||||
|
||||
|
||||
status_t
|
||||
AllocationBlockGroup::Allocate(Transaction& transaction, uint32 start,
|
||||
AllocationBlockGroup::Allocate(Transaction& transaction, fsblock_t _start,
|
||||
uint32 length)
|
||||
{
|
||||
uint32 start = _start - fStart;
|
||||
TRACE("AllocationBlockGroup::Allocate(%ld,%ld)\n", start, length);
|
||||
if (length == 0)
|
||||
return B_OK;
|
||||
|
||||
uint32 end = start + length;
|
||||
if (end > fNumBits)
|
||||
return B_BAD_DATA;
|
||||
return B_BAD_VALUE;
|
||||
|
||||
_LockInTransaction(transaction);
|
||||
|
||||
@@ -263,9 +268,8 @@ AllocationBlockGroup::Free(Transaction& transaction, uint32 start,
|
||||
return B_OK;
|
||||
|
||||
uint32 end = start + length;
|
||||
|
||||
if (end > fNumBits)
|
||||
return B_BAD_DATA;
|
||||
return B_BAD_VALUE;
|
||||
|
||||
_LockInTransaction(transaction);
|
||||
|
||||
@@ -342,17 +346,17 @@ AllocationBlockGroup::FreeBits() const
|
||||
}
|
||||
|
||||
|
||||
uint32
|
||||
fsblock_t
|
||||
AllocationBlockGroup::Start() const
|
||||
{
|
||||
return fStart;
|
||||
}
|
||||
|
||||
|
||||
uint32
|
||||
fsblock_t
|
||||
AllocationBlockGroup::LargestStart() const
|
||||
{
|
||||
return fLargestStart;
|
||||
return fStart + fLargestStart;
|
||||
}
|
||||
|
||||
|
||||
@@ -457,7 +461,7 @@ BlockAllocator::Initialize()
|
||||
fNumBlocks = fVolume->NumBlocks();
|
||||
|
||||
TRACE("BlockAllocator::Initialize(): blocks per group: %lu, block groups: "
|
||||
"%lu, first block: %lu, num blocks: %lu\n", fBlocksPerGroup,
|
||||
"%lu, first block: %llu, num blocks: %llu\n", fBlocksPerGroup,
|
||||
fNumGroups, fFirstBlock, fNumBlocks);
|
||||
|
||||
fGroups = new(std::nothrow) AllocationBlockGroup[fNumGroups];
|
||||
@@ -486,7 +490,7 @@ BlockAllocator::Initialize()
|
||||
|
||||
status_t
|
||||
BlockAllocator::AllocateBlocks(Transaction& transaction, uint32 minimum,
|
||||
uint32 maximum, uint32& blockGroup, off_t& start, uint32& length)
|
||||
uint32 maximum, uint32& blockGroup, fsblock_t& start, uint32& length)
|
||||
{
|
||||
TRACE("BlockAllocator::AllocateBlocks()\n");
|
||||
MutexLocker lock(fLock);
|
||||
@@ -496,7 +500,7 @@ BlockAllocator::AllocateBlocks(Transaction& transaction, uint32 minimum,
|
||||
"max: %lu, block group: %lu, start: %llu, num groups: %lu\n",
|
||||
transaction.ID(), minimum, maximum, blockGroup, start, fNumGroups);
|
||||
|
||||
off_t bestStart = 0;
|
||||
fsblock_t bestStart = 0;
|
||||
uint32 bestLength = 0;
|
||||
uint32 bestGroup = 0;
|
||||
|
||||
@@ -567,10 +571,12 @@ BlockAllocator::AllocateBlocks(Transaction& transaction, uint32 minimum,
|
||||
|
||||
status_t
|
||||
BlockAllocator::Allocate(Transaction& transaction, Inode* inode,
|
||||
off_t numBlocks, uint32 minimum, off_t& start, uint32& allocated)
|
||||
off_t numBlocks, uint32 minimum, fsblock_t& start, uint32& allocated)
|
||||
{
|
||||
if (numBlocks <= 0)
|
||||
return B_ERROR;
|
||||
if (start > fNumBlocks)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
uint32 group = inode->ID() / fVolume->InodesPerGroup();
|
||||
uint32 preferred = 0;
|
||||
@@ -644,28 +650,32 @@ BlockAllocator::Allocate(Transaction& transaction, Inode* inode,
|
||||
|
||||
|
||||
status_t
|
||||
BlockAllocator::Free(Transaction& transaction, off_t start, uint32 length)
|
||||
BlockAllocator::Free(Transaction& transaction, fsblock_t start, uint32 length)
|
||||
{
|
||||
TRACE("BlockAllocator::Free(%llu, %lu)\n", start, length);
|
||||
MutexLocker lock(fLock);
|
||||
|
||||
if (start <= fFirstBlock) {
|
||||
panic("Trying to free superblock!\n");
|
||||
return B_BAD_DATA;
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
if (length == 0)
|
||||
return B_OK;
|
||||
if (start > fNumBlocks || length > fNumBlocks)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
TRACE("BlockAllocator::Free(): first block: %lu, blocks per group: %lu\n",
|
||||
TRACE("BlockAllocator::Free(): first block: %llu, blocks per group: %lu\n",
|
||||
fFirstBlock, fBlocksPerGroup);
|
||||
|
||||
start -= fFirstBlock;
|
||||
off_t end = start + length - 1;
|
||||
|
||||
uint32 group = start / fBlocksPerGroup;
|
||||
if (group >= fNumGroups)
|
||||
panic("BlockAllocator::Free() group %ld too big (fNumGroups %ld)\n", group, fNumGroups);
|
||||
if (group >= fNumGroups) {
|
||||
panic("BlockAllocator::Free() group %ld too big (fNumGroups %ld)\n",
|
||||
group, fNumGroups);
|
||||
}
|
||||
uint32 lastGroup = end / fBlocksPerGroup;
|
||||
start = start % fBlocksPerGroup;
|
||||
|
||||
|
||||
@@ -8,8 +8,10 @@
|
||||
#ifndef BLOCKALLOCATOR_H
|
||||
#define BLOCKALLOCATOR_H
|
||||
|
||||
|
||||
#include <lock.h>
|
||||
|
||||
#include "ext2.h"
|
||||
#include "Transaction.h"
|
||||
|
||||
|
||||
@@ -27,11 +29,11 @@ public:
|
||||
|
||||
status_t AllocateBlocks(Transaction& transaction,
|
||||
uint32 minimum, uint32 maximum, uint32& blockGroup,
|
||||
off_t& start, uint32& length);
|
||||
fsblock_t& start, uint32& length);
|
||||
status_t Allocate(Transaction& transaction, Inode* inode,
|
||||
off_t numBlocks, uint32 minimum, off_t& start,
|
||||
off_t numBlocks, uint32 minimum, fsblock_t& start,
|
||||
uint32& length);
|
||||
status_t Free(Transaction& transaction, off_t start,
|
||||
status_t Free(Transaction& transaction, fsblock_t start,
|
||||
uint32 length);
|
||||
|
||||
uint32 FreeBlocks();
|
||||
@@ -45,9 +47,9 @@ protected:
|
||||
|
||||
AllocationBlockGroup* fGroups;
|
||||
uint32 fBlocksPerGroup;
|
||||
uint32 fNumBlocks;
|
||||
fsblock_t fNumBlocks;
|
||||
uint32 fNumGroups;
|
||||
uint32 fFirstBlock;
|
||||
fsblock_t fFirstBlock;
|
||||
};
|
||||
|
||||
#endif // BLOCKALLOCATOR_H
|
||||
|
||||
@@ -53,7 +53,7 @@ DataStream::~DataStream()
|
||||
|
||||
|
||||
status_t
|
||||
DataStream::FindBlock(off_t offset, off_t& block, uint32 *_count)
|
||||
DataStream::FindBlock(off_t offset, fsblock_t& block, uint32 *_count)
|
||||
{
|
||||
uint32 index = offset >> fVolume->BlockShift();
|
||||
|
||||
@@ -377,8 +377,7 @@ DataStream::_GetBlock(Transaction& transaction, uint32& blockNum)
|
||||
if (fAllocated == 0) {
|
||||
uint32 blockGroup = (fAllocatedPos - fFirstBlock)
|
||||
/ fVolume->BlocksPerGroup();
|
||||
fAllocatedPos %= fVolume->BlocksPerGroup();
|
||||
|
||||
|
||||
status_t status = fVolume->AllocateBlocks(transaction, 1, fWaiting,
|
||||
blockGroup, fAllocatedPos, fAllocated);
|
||||
if (status != B_OK) {
|
||||
@@ -387,7 +386,6 @@ DataStream::_GetBlock(Transaction& transaction, uint32& blockNum)
|
||||
}
|
||||
|
||||
fWaiting -= fAllocated;
|
||||
fAllocatedPos += fVolume->BlocksPerGroup() * blockGroup + fFirstBlock;
|
||||
|
||||
TRACE("DataStream::_GetBlock(): newAllocated: %lu, newpos: %llu,"
|
||||
"newwaiting: %lu\n", fAllocated, fAllocatedPos, fWaiting);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#ifndef DATASTREAM_H
|
||||
#define DATASTREAM_H
|
||||
|
||||
|
||||
#include "ext2.h"
|
||||
#include "Transaction.h"
|
||||
|
||||
@@ -22,7 +23,7 @@ public:
|
||||
off_t size);
|
||||
~DataStream();
|
||||
|
||||
status_t FindBlock(off_t offset, off_t& block,
|
||||
status_t FindBlock(off_t offset, fsblock_t& block,
|
||||
uint32 *_count = NULL);
|
||||
status_t Enlarge(Transaction& transaction, off_t& numBlocks);
|
||||
status_t Shrink(Transaction& transaction, off_t& numBlocks);
|
||||
@@ -82,7 +83,7 @@ private:
|
||||
uint32 fFirstBlock;
|
||||
|
||||
uint32 fAllocated;
|
||||
off_t fAllocatedPos;
|
||||
fsblock_t fAllocatedPos;
|
||||
uint32 fWaiting;
|
||||
|
||||
uint32 fFreeStart;
|
||||
|
||||
@@ -517,7 +517,7 @@ DirectoryIterator::_SplitIndexedBlock(Transaction& transaction,
|
||||
return B_NO_MEMORY;
|
||||
ArrayDeleter<uint8> bufferDeleter(buffer);
|
||||
|
||||
off_t firstPhysicalBlock = 0;
|
||||
fsblock_t firstPhysicalBlock = 0;
|
||||
|
||||
// Prepare block to hold the first half of the entries and fill the buffer
|
||||
CachedBlock cachedFirst(fVolume);
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#define DIRECTORY_ITERATOR_H
|
||||
|
||||
|
||||
#include "ext2.h"
|
||||
|
||||
#include <SupportDefs.h>
|
||||
|
||||
#include "Transaction.h"
|
||||
@@ -69,7 +71,7 @@ protected:
|
||||
|
||||
uint32 fNumBlocks;
|
||||
uint32 fLogicalBlock;
|
||||
off_t fPhysicalBlock;
|
||||
fsblock_t fPhysicalBlock;
|
||||
uint32 fDisplacement;
|
||||
uint32 fPreviousDisplacement;
|
||||
|
||||
|
||||
@@ -0,0 +1,510 @@
|
||||
/*
|
||||
* Copyright 2001-2010, Haiku Inc. All rights reserved.
|
||||
* This file may be used under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Jérôme Duval
|
||||
*/
|
||||
|
||||
|
||||
#include "ExtentStream.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "CachedBlock.h"
|
||||
#include "Volume.h"
|
||||
|
||||
|
||||
#undef ASSERT
|
||||
//#define TRACE_EXT2
|
||||
#ifdef TRACE_EXT2
|
||||
# define TRACE(x...) dprintf("\33[34mext2:\33[0m ExtentStream::" x)
|
||||
# define ASSERT(x) { if (!(x)) kernel_debugger("ext2: assert failed: " #x "\n"); }
|
||||
#else
|
||||
# define TRACE(x...) ;
|
||||
# define ASSERT(x) ;
|
||||
#endif
|
||||
#define ERROR(x...) dprintf("\33[34mext2:\33[0m ExtentStream::" x)
|
||||
|
||||
|
||||
ExtentStream::ExtentStream(Volume* volume, ext2_extent_stream* stream,
|
||||
off_t size)
|
||||
:
|
||||
fVolume(volume),
|
||||
fStream(stream),
|
||||
fFirstBlock(volume->FirstDataBlock()),
|
||||
fAllocatedPos(fFirstBlock),
|
||||
fSize(size)
|
||||
{
|
||||
fNumBlocks = size == 0 ? 0 : ((size - 1) >> fVolume->BlockShift()) + 1;
|
||||
}
|
||||
|
||||
|
||||
ExtentStream::~ExtentStream()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ExtentStream::FindBlock(off_t offset, fsblock_t& block, uint32 *_count)
|
||||
{
|
||||
fileblock_t index = offset >> fVolume->BlockShift();
|
||||
TRACE("FindBlock(%lld, %lld)\n", offset, index);
|
||||
|
||||
if (offset >= fSize) {
|
||||
TRACE("FindBlock: offset larger than inode size\n");
|
||||
return B_ENTRY_NOT_FOUND;
|
||||
}
|
||||
|
||||
ext2_extent_stream *stream = fStream;
|
||||
if (!stream->extent_header.IsValid())
|
||||
panic("ExtentStream::FindBlock() invalid header\n");
|
||||
|
||||
CachedBlock cached(fVolume);
|
||||
while (stream->extent_header.Depth() != 0) {
|
||||
TRACE("FindBlock() depth %d\n",
|
||||
stream->extent_header.Depth());
|
||||
int32 i = 1;
|
||||
while (i < stream->extent_header.NumEntries()
|
||||
&& stream->extent_index[i].LogicalBlock() <= index) {
|
||||
i++;
|
||||
}
|
||||
TRACE("FindBlock() getting index %ld at %lld\n", i - 1,
|
||||
stream->extent_index[i - 1].PhysicalBlock());
|
||||
stream = (ext2_extent_stream *)cached.SetTo(
|
||||
stream->extent_index[i - 1].PhysicalBlock());
|
||||
if (!stream->extent_header.IsValid())
|
||||
panic("ExtentStream::FindBlock() invalid header\n");
|
||||
}
|
||||
|
||||
for (int32 i = 0; i < stream->extent_header.NumEntries(); i++) {
|
||||
if (stream->extent_entries[i].LogicalBlock() > index) {
|
||||
// sparse block
|
||||
TRACE("FindBlock() sparse block index %lld at %ld\n", index,
|
||||
stream->extent_entries[i].LogicalBlock());
|
||||
block = 0xffffffff;
|
||||
return B_OK;
|
||||
}
|
||||
fileblock_t diff = index - stream->extent_entries[i].LogicalBlock();
|
||||
if (diff < stream->extent_entries[i].Length()) {
|
||||
block = stream->extent_entries[i].PhysicalBlock() + diff;
|
||||
if (_count)
|
||||
*_count = stream->extent_entries[i].Length() - diff;
|
||||
TRACE("FindBlock(offset %lld): %lld %ld\n", offset,
|
||||
block, _count != NULL ? *_count : 1);
|
||||
return B_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ExtentStream::Enlarge(Transaction& transaction, off_t& numBlocks)
|
||||
{
|
||||
TRACE("Enlarge(): current size: %llu, target size: %llu\n",
|
||||
fNumBlocks, numBlocks);
|
||||
|
||||
off_t targetBlocks = numBlocks;
|
||||
numBlocks = targetBlocks - fNumBlocks;
|
||||
uint32 allocated = 0;
|
||||
|
||||
while (fNumBlocks < targetBlocks) {
|
||||
// allocate new blocks
|
||||
uint32 blockGroup = (fAllocatedPos - fFirstBlock)
|
||||
/ fVolume->BlocksPerGroup();
|
||||
|
||||
if (allocated == 0) {
|
||||
status_t status = fVolume->AllocateBlocks(transaction, 1,
|
||||
targetBlocks - fNumBlocks, blockGroup, fAllocatedPos,
|
||||
allocated);
|
||||
if (status != B_OK) {
|
||||
ERROR("Enlarge(): AllocateBlocks() failed()\n");
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT(_CheckBlock(fStream, fAllocatedPos) == B_OK);
|
||||
|
||||
ext2_extent_stream *stream = fStream;
|
||||
fsblock_t path[stream->extent_header.Depth()];
|
||||
|
||||
// search for the leaf
|
||||
CachedBlock cached(fVolume);
|
||||
int32 level = -1;
|
||||
for (; stream->extent_header.Depth() != 0;) {
|
||||
if (stream->extent_header.NumEntries() == 0)
|
||||
panic("stream->extent_header.NumEntries() == 0\n");
|
||||
int32 lastIndex = stream->extent_header.NumEntries() - 1;
|
||||
TRACE("Enlarge() depth %d\n", stream->extent_header.Depth());
|
||||
TRACE("Enlarge() getting index %ld at %lld\n", lastIndex,
|
||||
stream->extent_index[lastIndex].PhysicalBlock());
|
||||
path[++level] = stream->extent_index[lastIndex].PhysicalBlock();
|
||||
stream = (ext2_extent_stream *)cached.SetTo(path[level]);
|
||||
if (stream == NULL)
|
||||
return B_IO_ERROR;
|
||||
}
|
||||
|
||||
// check if the new extent is adjacent
|
||||
if (stream->extent_header.NumEntries() > 0) {
|
||||
ext2_extent_entry &last = stream->extent_entries[
|
||||
stream->extent_header.NumEntries() - 1];
|
||||
TRACE("Enlarge() last %lld allocatedPos %lld\n",
|
||||
last.PhysicalBlock() + last.Length(), fAllocatedPos);
|
||||
if (last.PhysicalBlock() + last.Length() == fAllocatedPos
|
||||
&& (last.Length() + allocated) <= 0xffff) {
|
||||
if (stream != fStream) {
|
||||
stream = (ext2_extent_stream *)cached.SetToWritable(
|
||||
transaction, cached.BlockNumber());
|
||||
if (stream == NULL)
|
||||
return B_IO_ERROR;
|
||||
}
|
||||
stream->extent_entries[stream->extent_header.NumEntries() - 1]
|
||||
.SetLength(last.Length() + allocated);
|
||||
fNumBlocks += allocated;
|
||||
allocated = 0;
|
||||
TRACE("Enlarge() entry extended\n");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (stream->extent_header.NumEntries()
|
||||
>= stream->extent_header.MaxEntries()) {
|
||||
TRACE("Enlarge() adding leaf and indexes at depth %d level %ld\n",
|
||||
stream->extent_header.Depth(), level);
|
||||
// try to add a leaf and indexes
|
||||
while (--level >= 0) {
|
||||
stream = (ext2_extent_stream *)cached.SetTo(path[level]);
|
||||
if (stream == NULL)
|
||||
return B_IO_ERROR;
|
||||
if (stream->extent_header.NumEntries()
|
||||
< stream->extent_header.MaxEntries()) {
|
||||
break;
|
||||
}
|
||||
TRACE("Enlarge() going up from level %ld\n", level);
|
||||
}
|
||||
|
||||
if (level < 0 && fStream->extent_header.NumEntries()
|
||||
>= fStream->extent_header.MaxEntries()) {
|
||||
TRACE("Enlarge() add a level, increment root depth\n");
|
||||
fsblock_t newBlock = fStream->extent_index[0].PhysicalBlock();
|
||||
uint32 _allocated;
|
||||
status_t status = fVolume->AllocateBlocks(transaction, 1, 1,
|
||||
blockGroup, newBlock, _allocated);
|
||||
if (status != B_OK) {
|
||||
ERROR("Enlarge() AllocateBlocks() failed()\n");
|
||||
return status;
|
||||
}
|
||||
ASSERT(_CheckBlock(fStream, newBlock) == B_OK);
|
||||
TRACE("Enlarge() move root to block %lld\n", newBlock);
|
||||
numBlocks++;
|
||||
stream = (ext2_extent_stream *)cached.SetToWritable(
|
||||
transaction, newBlock);
|
||||
if (stream == NULL)
|
||||
return B_IO_ERROR;
|
||||
ASSERT(fStream->extent_header.IsValid());
|
||||
memcpy(stream, fStream, sizeof(ext2_extent_stream));
|
||||
fStream->extent_header.SetDepth(stream->extent_header.Depth()
|
||||
+ 1);
|
||||
TRACE("Enlarge() new root depth %d\n",
|
||||
fStream->extent_header.Depth());
|
||||
fStream->extent_header.SetNumEntries(1);
|
||||
fStream->extent_index[0].SetLogicalBlock(0);
|
||||
fStream->extent_index[0].SetPhysicalBlock(newBlock);
|
||||
stream->extent_header.SetMaxEntries((fVolume->BlockSize()
|
||||
- sizeof(ext2_extent_header)) / sizeof(ext2_extent_index));
|
||||
ASSERT(stream->extent_header.IsValid());
|
||||
|
||||
ASSERT(Check());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (level < 0)
|
||||
stream = fStream;
|
||||
|
||||
uint16 depth = stream->extent_header.Depth();
|
||||
while (depth > 1) {
|
||||
TRACE("Enlarge() adding an index block at depth %d\n", depth);
|
||||
fsblock_t newBlock = path[level];
|
||||
uint32 _allocated;
|
||||
status_t status = fVolume->AllocateBlocks(transaction, 1, 1,
|
||||
blockGroup, newBlock, _allocated);
|
||||
if (status != B_OK) {
|
||||
ERROR("Enlarge(): AllocateBlocks() failed()\n");
|
||||
return status;
|
||||
}
|
||||
ASSERT(_CheckBlock(fStream, newBlock) == B_OK);
|
||||
numBlocks++;
|
||||
int32 index = stream->extent_header.NumEntries();
|
||||
stream->extent_index[index].SetLogicalBlock(fNumBlocks);
|
||||
stream->extent_index[index].SetPhysicalBlock(newBlock);
|
||||
stream->extent_header.SetNumEntries(index + 1);
|
||||
path[level++] = newBlock;
|
||||
|
||||
depth = stream->extent_header.Depth() - 1;
|
||||
TRACE("Enlarge() init index block %lld at depth %d\n",
|
||||
newBlock, depth);
|
||||
stream = (ext2_extent_stream *)cached.SetToWritable(
|
||||
transaction, newBlock);
|
||||
if (stream == NULL)
|
||||
return B_IO_ERROR;
|
||||
stream->extent_header.magic = EXT2_EXTENT_MAGIC;
|
||||
stream->extent_header.SetNumEntries(0);
|
||||
stream->extent_header.SetMaxEntries((fVolume->BlockSize()
|
||||
- sizeof(ext2_extent_header)) / sizeof(ext2_extent_index));
|
||||
stream->extent_header.SetDepth(depth);
|
||||
stream->extent_header.SetGeneration(0);
|
||||
|
||||
ASSERT(Check());
|
||||
}
|
||||
|
||||
TRACE("Enlarge() depth %d level %ld\n",
|
||||
stream->extent_header.Depth(), level);
|
||||
|
||||
if (stream->extent_header.Depth() == 1) {
|
||||
TRACE("Enlarge() adding an entry block at depth %d level %ld\n",
|
||||
depth, level);
|
||||
fsblock_t newBlock;
|
||||
if (level >= 0)
|
||||
newBlock = path[level];
|
||||
else
|
||||
newBlock = fStream->extent_index[0].PhysicalBlock();
|
||||
uint32 _allocated;
|
||||
status_t status = fVolume->AllocateBlocks(transaction, 1, 1,
|
||||
blockGroup, newBlock, _allocated);
|
||||
if (status != B_OK) {
|
||||
ERROR("Enlarge(): AllocateBlocks() failed()\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
ASSERT(_CheckBlock(fStream, newBlock) == B_OK);
|
||||
numBlocks++;
|
||||
int32 index = stream->extent_header.NumEntries();
|
||||
stream->extent_index[index].SetLogicalBlock(fNumBlocks);
|
||||
stream->extent_index[index].SetPhysicalBlock(newBlock);
|
||||
stream->extent_header.SetNumEntries(index + 1);
|
||||
|
||||
TRACE("Enlarge() init entry block %lld at depth %d\n",
|
||||
newBlock, depth);
|
||||
stream = (ext2_extent_stream *)cached.SetToWritable(
|
||||
transaction, newBlock);
|
||||
if (stream == NULL)
|
||||
return B_IO_ERROR;
|
||||
stream->extent_header.magic = EXT2_EXTENT_MAGIC;
|
||||
stream->extent_header.SetNumEntries(0);
|
||||
stream->extent_header.SetMaxEntries((fVolume->BlockSize()
|
||||
- sizeof(ext2_extent_header)) / sizeof(ext2_extent_entry));
|
||||
stream->extent_header.SetDepth(0);
|
||||
stream->extent_header.SetGeneration(0);
|
||||
ASSERT(Check());
|
||||
}
|
||||
}
|
||||
|
||||
// add a new entry
|
||||
TRACE("Enlarge() add entry %lld\n", fAllocatedPos);
|
||||
if (stream != fStream) {
|
||||
stream = (ext2_extent_stream *)cached.SetToWritable(
|
||||
transaction, cached.BlockNumber());
|
||||
if (stream == NULL)
|
||||
return B_IO_ERROR;
|
||||
}
|
||||
int32 index = stream->extent_header.NumEntries();
|
||||
stream->extent_entries[index].SetLogicalBlock(fNumBlocks);
|
||||
stream->extent_entries[index].SetLength(allocated);
|
||||
stream->extent_entries[index].SetPhysicalBlock(fAllocatedPos);
|
||||
stream->extent_header.SetNumEntries(index + 1);
|
||||
TRACE("Enlarge() entry added at index %ld\n", index);
|
||||
ASSERT(stream->extent_header.IsValid());
|
||||
|
||||
fNumBlocks += allocated;
|
||||
allocated = 0;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ExtentStream::Shrink(Transaction& transaction, off_t& numBlocks)
|
||||
{
|
||||
TRACE("DataStream::Shrink(): current size: %llu, target size: %llu\n",
|
||||
fNumBlocks, numBlocks);
|
||||
|
||||
off_t targetBlocks = numBlocks;
|
||||
numBlocks = fNumBlocks - targetBlocks;
|
||||
|
||||
while (targetBlocks < fNumBlocks) {
|
||||
ext2_extent_stream *stream = fStream;
|
||||
fsblock_t path[stream->extent_header.Depth()];
|
||||
|
||||
// search for the leaf
|
||||
CachedBlock cached(fVolume);
|
||||
int32 level = -1;
|
||||
for (; stream->extent_header.Depth() != 0;) {
|
||||
if (stream->extent_header.NumEntries() == 0)
|
||||
panic("stream->extent_header.NumEntries() == 0\n");
|
||||
int32 lastIndex = stream->extent_header.NumEntries() - 1;
|
||||
TRACE("Shrink() depth %d\n", stream->extent_header.Depth());
|
||||
TRACE("Shrink() getting index %ld at %lld\n", lastIndex,
|
||||
stream->extent_index[lastIndex].PhysicalBlock());
|
||||
path[++level] = stream->extent_index[lastIndex].PhysicalBlock();
|
||||
stream = (ext2_extent_stream *)cached.SetToWritable(transaction,
|
||||
path[level]);
|
||||
if (stream == NULL)
|
||||
return B_IO_ERROR;
|
||||
if (!stream->extent_header.IsValid())
|
||||
panic("Shrink() invalid header\n");
|
||||
}
|
||||
|
||||
int32 index = stream->extent_header.NumEntries() - 1;
|
||||
status_t status = B_OK;
|
||||
while (index >= 0 && targetBlocks < fNumBlocks) {
|
||||
ext2_extent_entry &last = stream->extent_entries[index];
|
||||
if (last.LogicalBlock() + last.Length() < targetBlocks) {
|
||||
status = B_BAD_DATA;
|
||||
break;
|
||||
}
|
||||
uint16 length = min_c(last.Length(), fNumBlocks - targetBlocks);
|
||||
fsblock_t block = last.PhysicalBlock() + last.Length() - length;
|
||||
TRACE("Shrink() free block %lld length %d\n", block, length);
|
||||
status = fVolume->FreeBlocks(transaction, block, length);
|
||||
if (status != B_OK)
|
||||
break;
|
||||
fNumBlocks -= length;
|
||||
stream->extent_entries[index].SetLength(last.Length() - length);
|
||||
TRACE("Shrink() new length for %ld: %d\n", index, last.Length());
|
||||
if (last.Length() != 0)
|
||||
break;
|
||||
index--;
|
||||
TRACE("Shrink() next index: %ld\n", index);
|
||||
}
|
||||
TRACE("Shrink() new entry count: %ld\n", index + 1);
|
||||
stream->extent_header.SetNumEntries(index + 1);
|
||||
ASSERT(Check());
|
||||
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
while (stream != fStream && stream->extent_header.NumEntries() == 0) {
|
||||
TRACE("Shrink() empty leaf at depth %d\n",
|
||||
stream->extent_header.Depth());
|
||||
level--;
|
||||
if (level >= 0) {
|
||||
stream = (ext2_extent_stream *)cached.SetToWritable(
|
||||
transaction, path[level]);
|
||||
if (stream == NULL)
|
||||
return B_IO_ERROR;
|
||||
} else
|
||||
stream = fStream;
|
||||
if (!stream->extent_header.IsValid())
|
||||
panic("Shrink() invalid header\n");
|
||||
uint16 index = stream->extent_header.NumEntries() - 1;
|
||||
ext2_extent_index &last = stream->extent_index[index];
|
||||
if (last.PhysicalBlock() != path[level + 1])
|
||||
panic("Shrink() last.PhysicalBlock() != path[level + 1]\n");
|
||||
status = fVolume->FreeBlocks(transaction, last.PhysicalBlock(), 1);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
numBlocks++;
|
||||
stream->extent_header.SetNumEntries(index);
|
||||
TRACE("Shrink() new entry count: %d\n", index);
|
||||
}
|
||||
if (stream == fStream && stream->extent_header.NumEntries() == 0)
|
||||
stream->extent_header.SetDepth(0);
|
||||
|
||||
ASSERT(Check());
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ExtentStream::Init()
|
||||
{
|
||||
fStream->extent_header.magic = EXT2_EXTENT_MAGIC;
|
||||
fStream->extent_header.SetNumEntries(0);
|
||||
fStream->extent_header.SetMaxEntries(4);
|
||||
fStream->extent_header.SetDepth(0);
|
||||
fStream->extent_header.SetGeneration(0);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ExtentStream::_Check(ext2_extent_stream *stream, fileblock_t &block)
|
||||
{
|
||||
if (!stream->extent_header.IsValid()) {
|
||||
panic("_Check() invalid header\n");
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
if (stream->extent_header.Depth() == 0) {
|
||||
for (int32 i = 0; i < stream->extent_header.NumEntries(); i++) {
|
||||
ext2_extent_entry &entry = stream->extent_entries[i];
|
||||
if (entry.LogicalBlock() < block) {
|
||||
panic("_Check() entry %ld %lld %ld\n", i, block,
|
||||
entry.LogicalBlock());
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
block = entry.LogicalBlock() + entry.Length();
|
||||
}
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
CachedBlock cached(fVolume);
|
||||
for (int32 i = 0; i < stream->extent_header.NumEntries(); i++) {
|
||||
ext2_extent_index &index = stream->extent_index[i];
|
||||
if (index.LogicalBlock() < block) {
|
||||
panic("_Check() index %ld %lld %ld\n", i, block,
|
||||
index.LogicalBlock());
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
ext2_extent_stream *child = (ext2_extent_stream *)cached.SetTo(
|
||||
index.PhysicalBlock());
|
||||
if (child->extent_header.Depth() != (stream->extent_header.Depth() - 1)
|
||||
|| _Check(child, block) != B_OK)
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
ExtentStream::Check()
|
||||
{
|
||||
fileblock_t block = 0;
|
||||
return _Check(fStream, block) == B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ExtentStream::_CheckBlock(ext2_extent_stream *stream, fsblock_t block)
|
||||
{
|
||||
if (stream->extent_header.Depth() == 0) {
|
||||
for (int32 i = 0; i < stream->extent_header.NumEntries(); i++) {
|
||||
ext2_extent_entry &entry = stream->extent_entries[i];
|
||||
if (entry.PhysicalBlock() <= block
|
||||
&& (entry.PhysicalBlock() + entry.Length()) > block) {
|
||||
panic("_CheckBlock() entry %ld %lld %lld %d\n", i, block,
|
||||
entry.PhysicalBlock(), entry.Length());
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
}
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
CachedBlock cached(fVolume);
|
||||
for (int32 i = 0; i < stream->extent_header.NumEntries(); i++) {
|
||||
ext2_extent_index &index = stream->extent_index[i];
|
||||
if (index.PhysicalBlock() == block) {
|
||||
panic("_CheckBlock() index %ld %lld\n", i, block);
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
ext2_extent_stream *child = (ext2_extent_stream *)cached.SetTo(
|
||||
index.PhysicalBlock());
|
||||
if (child->extent_header.Depth() != (stream->extent_header.Depth() - 1)
|
||||
|| _CheckBlock(child, block) != B_OK)
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
return B_OK;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2001-2010, Haiku Inc. All rights reserved.
|
||||
* This file may be used under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Jérôme Duval
|
||||
*/
|
||||
#ifndef EXTENTSTREAM_H
|
||||
#define EXTENTSTREAM_H
|
||||
|
||||
#include "ext2.h"
|
||||
#include "Transaction.h"
|
||||
|
||||
|
||||
class Volume;
|
||||
|
||||
|
||||
class ExtentStream
|
||||
{
|
||||
public:
|
||||
ExtentStream(Volume* volume, ext2_extent_stream* stream,
|
||||
off_t size);
|
||||
~ExtentStream();
|
||||
|
||||
status_t FindBlock(off_t offset, fsblock_t& block,
|
||||
uint32 *_count = NULL);
|
||||
status_t Enlarge(Transaction& transaction, off_t& numBlocks);
|
||||
status_t Shrink(Transaction& transaction, off_t& numBlocks);
|
||||
void Init();
|
||||
|
||||
bool Check();
|
||||
|
||||
private:
|
||||
status_t _Check(ext2_extent_stream *stream, fileblock_t &block);
|
||||
status_t _CheckBlock(ext2_extent_stream *stream, fsblock_t block);
|
||||
|
||||
Volume* fVolume;
|
||||
ext2_extent_stream* fStream;
|
||||
fsblock_t fFirstBlock;
|
||||
|
||||
fsblock_t fAllocatedPos;
|
||||
|
||||
off_t fNumBlocks;
|
||||
off_t fSize;
|
||||
};
|
||||
|
||||
#endif // EXTENTSTREAM_H
|
||||
|
||||
@@ -84,7 +84,7 @@ HTree::~HTree()
|
||||
status_t
|
||||
HTree::PrepareForHash()
|
||||
{
|
||||
off_t blockNum;
|
||||
fsblock_t blockNum;
|
||||
status_t status = fDirectory->FindBlock(0, blockNum);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
@@ -115,7 +115,7 @@ HTree::Lookup(const char* name, DirectoryIterator** iterator)
|
||||
return _FallbackToLinearIteration(iterator);
|
||||
}
|
||||
|
||||
off_t blockNum;
|
||||
fsblock_t blockNum;
|
||||
status_t status = fDirectory->FindBlock(0, blockNum);
|
||||
if (status != B_OK)
|
||||
return _FallbackToLinearIteration(iterator);
|
||||
|
||||
@@ -189,7 +189,7 @@ HTreeEntryIterator::Lookup(uint32 hash, int indirections,
|
||||
|
||||
TRACE("HTreeEntryIterator::Lookup(): Creating a HTree entry iterator "
|
||||
"starting at block: %lu, hash: 0x%lX\n", start->Block(), start->Hash());
|
||||
off_t blockNum;
|
||||
fsblock_t blockNum;
|
||||
status_t status = fDirectory->FindBlock(start->Block() * fBlockSize,
|
||||
blockNum);
|
||||
if (status != B_OK)
|
||||
@@ -322,7 +322,7 @@ HTreeEntryIterator::InsertEntry(Transaction& transaction, uint32 hash,
|
||||
panic("Splitting a HTree node required, but isn't yet fully "
|
||||
"supported\n");
|
||||
|
||||
off_t physicalBlock;
|
||||
fsblock_t physicalBlock;
|
||||
status_t status = fDirectory->FindBlock(newBlocksPos, physicalBlock);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
@@ -53,7 +53,7 @@ private:
|
||||
uint16 fCurrentEntry;
|
||||
|
||||
uint32 fBlockSize;
|
||||
off_t fBlockNum;
|
||||
fsblock_t fBlockNum;
|
||||
|
||||
HTreeEntryIterator* fParent;
|
||||
HTreeEntryIterator* fChild;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "CachedBlock.h"
|
||||
#include "DataStream.h"
|
||||
#include "DirectoryIterator.h"
|
||||
#include "ExtentStream.h"
|
||||
#include "HTree.h"
|
||||
#include "Utility.h"
|
||||
|
||||
@@ -224,8 +225,12 @@ Inode::CheckPermissions(int accessMode) const
|
||||
|
||||
|
||||
status_t
|
||||
Inode::FindBlock(off_t offset, off_t& block, uint32 *_count)
|
||||
Inode::FindBlock(off_t offset, fsblock_t& block, uint32 *_count)
|
||||
{
|
||||
if (Flags() & EXT2_INODE_EXTENTS) {
|
||||
ExtentStream stream(fVolume, &fNode.extent_stream, Size());
|
||||
return stream.FindBlock(offset, block, _count);
|
||||
}
|
||||
DataStream stream(fVolume, &fNode.stream, Size());
|
||||
return stream.FindBlock(offset, block, _count);
|
||||
}
|
||||
@@ -407,9 +412,14 @@ Inode::InitDirectory(Transaction& transaction, Inode* parent)
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
off_t blockNum;
|
||||
DataStream stream(fVolume, &fNode.stream, Size());
|
||||
status = stream.FindBlock(0, blockNum);
|
||||
fsblock_t blockNum;
|
||||
if (Flags() & EXT2_INODE_EXTENTS) {
|
||||
ExtentStream stream(fVolume, &fNode.extent_stream, Size());
|
||||
status = stream.FindBlock(0, blockNum);
|
||||
} else {
|
||||
DataStream stream(fVolume, &fNode.stream, Size());
|
||||
status = stream.FindBlock(0, blockNum);
|
||||
}
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
@@ -613,6 +623,14 @@ Inode::Create(Transaction& transaction, Inode* parent, const char* name,
|
||||
inode->SetAccessTime(×pec);
|
||||
inode->SetCreationTime(×pec);
|
||||
inode->SetModificationTime(×pec);
|
||||
node.SetFlags(parent->Flags() & EXT2_INODE_INHERITED);
|
||||
if (volume->HasExtentsFeature()
|
||||
&& (inode->IsDirectory() || inode->IsFile())) {
|
||||
node.SetFlag(EXT2_INODE_EXTENTS);
|
||||
ExtentStream stream(volume, &node.extent_stream, 0);
|
||||
stream.Init();
|
||||
ASSERT(stream.Check());
|
||||
}
|
||||
|
||||
if (sizeof(ext2_inode) < volume->InodeSize())
|
||||
node.SetExtraInodeSize(sizeof(ext2_inode) - EXT2_INODE_NORMAL_SIZE);
|
||||
@@ -803,9 +821,14 @@ Inode::_EnlargeDataStream(Transaction& transaction, off_t size)
|
||||
}
|
||||
|
||||
off_t end = size == 0 ? 0 : (size - 1) / fVolume->BlockSize() + 1;
|
||||
DataStream stream(fVolume, &fNode.stream, oldSize);
|
||||
stream.Enlarge(transaction, end);
|
||||
|
||||
if (Flags() & EXT2_INODE_EXTENTS) {
|
||||
ExtentStream stream(fVolume, &fNode.extent_stream, Size());
|
||||
stream.Enlarge(transaction, end);
|
||||
ASSERT(stream.Check());
|
||||
} else {
|
||||
DataStream stream(fVolume, &fNode.stream, oldSize);
|
||||
stream.Enlarge(transaction, end);
|
||||
}
|
||||
TRACE("Inode::_EnlargeDataStream(): Setting size to %lld\n", size);
|
||||
fNode.SetSize(size);
|
||||
TRACE("Inode::_EnlargeDataStream(): Setting allocated block count to %llu\n",
|
||||
@@ -837,8 +860,14 @@ Inode::_ShrinkDataStream(Transaction& transaction, off_t size)
|
||||
}
|
||||
|
||||
off_t end = size == 0 ? 0 : (size - 1) / fVolume->BlockSize() + 1;
|
||||
DataStream stream(fVolume, &fNode.stream, oldSize);
|
||||
stream.Shrink(transaction, end);
|
||||
if (Flags() & EXT2_INODE_EXTENTS) {
|
||||
ExtentStream stream(fVolume, &fNode.extent_stream, Size());
|
||||
stream.Shrink(transaction, end);
|
||||
ASSERT(stream.Check());
|
||||
} else {
|
||||
DataStream stream(fVolume, &fNode.stream, oldSize);
|
||||
stream.Shrink(transaction, end);
|
||||
}
|
||||
|
||||
fNode.SetSize(size);
|
||||
return _SetNumBlocks(_NumBlocks() - end * (fVolume->BlockSize() / 512));
|
||||
|
||||
@@ -79,7 +79,7 @@ public:
|
||||
//::Volume* _Volume() const { return fVolume; }
|
||||
Volume* GetVolume() const { return fVolume; }
|
||||
|
||||
status_t FindBlock(off_t offset, off_t& block,
|
||||
status_t FindBlock(off_t offset, fsblock_t& block,
|
||||
uint32 *_count = NULL);
|
||||
status_t ReadAt(off_t pos, uint8 *buffer, size_t *length);
|
||||
status_t WriteAt(Transaction& transaction, off_t pos,
|
||||
|
||||
@@ -156,7 +156,7 @@ InodeAllocator::_Allocate(Transaction& transaction, uint32 preferredBlockGroup,
|
||||
|
||||
|
||||
status_t
|
||||
InodeAllocator::_MarkInBitmap(Transaction& transaction, off_t bitmapBlock,
|
||||
InodeAllocator::_MarkInBitmap(Transaction& transaction, fsblock_t bitmapBlock,
|
||||
uint32 blockGroup, uint32 numInodes, ino_t& id)
|
||||
{
|
||||
BitmapBlock inodeBitmap(fVolume, numInodes);
|
||||
@@ -190,7 +190,7 @@ InodeAllocator::_MarkInBitmap(Transaction& transaction, off_t bitmapBlock,
|
||||
|
||||
|
||||
status_t
|
||||
InodeAllocator::_UnmarkInBitmap(Transaction& transaction, off_t bitmapBlock,
|
||||
InodeAllocator::_UnmarkInBitmap(Transaction& transaction, fsblock_t bitmapBlock,
|
||||
uint32 numInodes, ino_t id)
|
||||
{
|
||||
BitmapBlock inodeBitmap(fVolume, numInodes);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include <lock.h>
|
||||
|
||||
#include "ext2.h"
|
||||
#include "Transaction.h"
|
||||
|
||||
|
||||
@@ -29,13 +30,13 @@ public:
|
||||
|
||||
private:
|
||||
status_t _Allocate(Transaction& transaction,
|
||||
uint32 prefferedBlockGroup, bool isDirectory,
|
||||
uint32 preferredBlockGroup, bool isDirectory,
|
||||
ino_t& id);
|
||||
status_t _MarkInBitmap(Transaction& transaction,
|
||||
off_t bitmapBlock, uint32 blockGroup,
|
||||
fsblock_t bitmapBlock, uint32 blockGroup,
|
||||
uint32 numInodes, ino_t& id);
|
||||
status_t _UnmarkInBitmap(Transaction& transaction,
|
||||
off_t bitmapBlock, uint32 numInodes, ino_t id);
|
||||
fsblock_t bitmapBlock, uint32 numInodes, ino_t id);
|
||||
|
||||
|
||||
Volume* fVolume;
|
||||
|
||||
@@ -84,7 +84,7 @@ InodeJournal::InitCheck()
|
||||
|
||||
|
||||
status_t
|
||||
InodeJournal::MapBlock(off_t logical, off_t& physical)
|
||||
InodeJournal::MapBlock(off_t logical, fsblock_t& physical)
|
||||
{
|
||||
TRACE("InodeJournal::MapBlock()\n");
|
||||
return fInode->FindBlock(logical * fBlockSize, physical);
|
||||
|
||||
@@ -20,7 +20,7 @@ public:
|
||||
|
||||
status_t InitCheck();
|
||||
|
||||
status_t MapBlock(off_t logical, off_t& physical);
|
||||
status_t MapBlock(off_t logical, fsblock_t& physical);
|
||||
private:
|
||||
Inode* fInode;
|
||||
status_t fInitStatus;
|
||||
|
||||
@@ -11,6 +11,7 @@ KernelAddon ext2 :
|
||||
Inode.cpp
|
||||
Attribute.cpp
|
||||
DirectoryIterator.cpp
|
||||
ExtentStream.cpp
|
||||
HTree.cpp
|
||||
HTreeEntryIterator.cpp
|
||||
RevokeManager.cpp
|
||||
|
||||
@@ -304,7 +304,7 @@ Journal::Unlock(Transaction* owner, bool success)
|
||||
|
||||
|
||||
status_t
|
||||
Journal::MapBlock(off_t logical, off_t& physical)
|
||||
Journal::MapBlock(off_t logical, fsblock_t& physical)
|
||||
{
|
||||
TRACE("Journal::MapBlock()\n");
|
||||
physical = logical;
|
||||
@@ -402,7 +402,7 @@ Journal::_WritePartialTransactionToLog(JournalHeader* descriptorBlock,
|
||||
|
||||
logBlock = _WrapAroundLog(logBlock + 1);
|
||||
|
||||
off_t physicalBlock;
|
||||
fsblock_t physicalBlock;
|
||||
status = MapBlock(logBlock, physicalBlock);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
@@ -434,7 +434,7 @@ Journal::_WritePartialTransactionToLog(JournalHeader* descriptorBlock,
|
||||
--tag;
|
||||
tag->SetLastTagFlag();
|
||||
|
||||
off_t physicalBlock;
|
||||
fsblock_t physicalBlock;
|
||||
status = MapBlock(descriptorBlockPos, physicalBlock);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
@@ -579,7 +579,7 @@ Journal::_WriteTransactionToLog()
|
||||
// written. When it recovery reaches where the first commit should be
|
||||
// and doesn't find it, it considers it found the end of the log.
|
||||
|
||||
off_t physicalBlock;
|
||||
fsblock_t physicalBlock;
|
||||
status = MapBlock(logBlock, physicalBlock);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
@@ -602,7 +602,7 @@ Journal::_WriteTransactionToLog()
|
||||
}
|
||||
|
||||
// Transaction will enter the Commit state
|
||||
off_t physicalBlock;
|
||||
fsblock_t physicalBlock;
|
||||
status = MapBlock(commitBlockPos, physicalBlock);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
@@ -661,7 +661,7 @@ status_t
|
||||
Journal::_SaveSuperBlock()
|
||||
{
|
||||
TRACE("Journal::_SaveSuperBlock()\n");
|
||||
off_t physicalBlock;
|
||||
fsblock_t physicalBlock;
|
||||
status_t status = MapBlock(0, physicalBlock);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
@@ -695,7 +695,7 @@ status_t
|
||||
Journal::_LoadSuperBlock()
|
||||
{
|
||||
TRACE("Journal::_LoadSuperBlock()\n");
|
||||
off_t superblockPos;
|
||||
fsblock_t superblockPos;
|
||||
|
||||
status_t status = MapBlock(0, superblockPos);
|
||||
if (status != B_OK)
|
||||
@@ -842,7 +842,7 @@ Journal::_RecoverPassScan(uint32& lastCommitID)
|
||||
JournalHeader* header;
|
||||
uint32 nextCommitID = fFirstCommitID;
|
||||
uint32 nextBlock = fLogStart;
|
||||
off_t nextBlockPos;
|
||||
fsblock_t nextBlockPos;
|
||||
|
||||
status_t status = MapBlock(nextBlock, nextBlockPos);
|
||||
if (status != B_OK)
|
||||
@@ -898,7 +898,7 @@ Journal::_RecoverPassRevoke(uint32 lastCommitID)
|
||||
JournalHeader* header;
|
||||
uint32 nextCommitID = fFirstCommitID;
|
||||
uint32 nextBlock = fLogStart;
|
||||
off_t nextBlockPos;
|
||||
fsblock_t nextBlockPos;
|
||||
|
||||
status_t status = MapBlock(nextBlock, nextBlockPos);
|
||||
if (status != B_OK)
|
||||
@@ -960,7 +960,7 @@ Journal::_RecoverPassReplay(uint32 lastCommitID)
|
||||
|
||||
uint32 nextCommitID = fFirstCommitID;
|
||||
uint32 nextBlock = fLogStart;
|
||||
off_t nextBlockPos;
|
||||
fsblock_t nextBlockPos;
|
||||
|
||||
status_t status = MapBlock(nextBlock, nextBlockPos);
|
||||
if (status != B_OK)
|
||||
|
||||
@@ -173,7 +173,7 @@ public:
|
||||
bool separateSubTransactions);
|
||||
virtual status_t Unlock(Transaction* owner, bool success);
|
||||
|
||||
virtual status_t MapBlock(off_t logical, off_t& physical);
|
||||
virtual status_t MapBlock(off_t logical, fsblock_t& physical);
|
||||
inline uint32 FreeLogBlocks() const;
|
||||
|
||||
status_t FlushLogAndBlocks();
|
||||
|
||||
@@ -279,8 +279,10 @@ Volume::Mount(const char* deviceName, uint32 flags)
|
||||
DeviceOpener opener(deviceName, (flags & B_MOUNT_READ_ONLY) != 0
|
||||
? O_RDONLY : O_RDWR);
|
||||
fDevice = opener.Device();
|
||||
if (fDevice < B_OK)
|
||||
if (fDevice < B_OK) {
|
||||
FATAL("Volume::Mount(): couldn't open device\n");
|
||||
return fDevice;
|
||||
}
|
||||
|
||||
if (opener.IsReadOnly())
|
||||
fFlags |= VOLUME_READ_ONLY;
|
||||
@@ -291,8 +293,10 @@ Volume::Mount(const char* deviceName, uint32 flags)
|
||||
|
||||
// read the super block
|
||||
status_t status = Identify(fDevice, &fSuperBlock);
|
||||
if (status != B_OK)
|
||||
if (status != B_OK) {
|
||||
FATAL("Volume::Mount(): Identify() failed\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
// check read-only features if mounting read-write
|
||||
if (!IsReadOnly() && _UnsupportedReadOnlyFeatures(fSuperBlock) != 0)
|
||||
@@ -300,7 +304,9 @@ Volume::Mount(const char* deviceName, uint32 flags)
|
||||
|
||||
// initialize short hands to the super block (to save byte swapping)
|
||||
fBlockShift = fSuperBlock.BlockShift();
|
||||
fBlockSize = 1UL << fSuperBlock.BlockShift();
|
||||
if (fBlockShift < 10 || fBlockShift > 16)
|
||||
return B_ERROR;
|
||||
fBlockSize = 1UL << fBlockShift;
|
||||
fFirstDataBlock = fSuperBlock.FirstDataBlock();
|
||||
|
||||
fFreeBlocks = fSuperBlock.FreeBlocks(Has64bitFeature());
|
||||
@@ -338,7 +344,7 @@ Volume::Mount(const char* deviceName, uint32 flags)
|
||||
status = opener.GetSize(&diskSize);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
if (diskSize < (NumBlocks() << BlockShift()))
|
||||
if (diskSize < ((off_t)NumBlocks() << BlockShift()))
|
||||
return B_BAD_VALUE;
|
||||
|
||||
fBlockCache = opener.InitCache(NumBlocks(), fBlockSize);
|
||||
@@ -501,6 +507,7 @@ Volume::_UnsupportedIncompatibleFeatures(ext2_super_block& superBlock)
|
||||
uint32 supportedIncompatible = EXT2_INCOMPATIBLE_FEATURE_FILE_TYPE
|
||||
| EXT2_INCOMPATIBLE_FEATURE_RECOVER
|
||||
| EXT2_INCOMPATIBLE_FEATURE_JOURNAL
|
||||
| EXT2_INCOMPATIBLE_FEATURE_EXTENTS;
|
||||
/*| EXT2_INCOMPATIBLE_FEATURE_META_GROUP*/;
|
||||
uint32 unsupported = superBlock.IncompatibleFeatures()
|
||||
& ~supportedIncompatible;
|
||||
@@ -742,7 +749,7 @@ Volume::FreeInode(Transaction& transaction, ino_t id, bool isDirectory)
|
||||
|
||||
status_t
|
||||
Volume::AllocateBlocks(Transaction& transaction, uint32 minimum, uint32 maximum,
|
||||
uint32& blockGroup, off_t& start, uint32& length)
|
||||
uint32& blockGroup, fsblock_t& start, uint32& length)
|
||||
{
|
||||
TRACE("Volume::AllocateBlocks()\n");
|
||||
if (IsReadOnly())
|
||||
@@ -764,7 +771,7 @@ Volume::AllocateBlocks(Transaction& transaction, uint32 minimum, uint32 maximum,
|
||||
|
||||
|
||||
status_t
|
||||
Volume::FreeBlocks(Transaction& transaction, off_t start, uint32 length)
|
||||
Volume::FreeBlocks(Transaction& transaction, fsblock_t start, uint32 length)
|
||||
{
|
||||
TRACE("Volume::FreeBlocks(%llu, %lu)\n", start, length);
|
||||
if (IsReadOnly())
|
||||
|
||||
@@ -48,7 +48,7 @@ public:
|
||||
{ return fNumInodes; }
|
||||
uint32 NumGroups() const
|
||||
{ return fNumGroups; }
|
||||
off_t NumBlocks() const
|
||||
fsblock_t NumBlocks() const
|
||||
{ return fSuperBlock.NumBlocks(
|
||||
Has64bitFeature()); }
|
||||
off_t NumFreeBlocks() const
|
||||
@@ -81,6 +81,9 @@ public:
|
||||
bool Has64bitFeature() const
|
||||
{ return (fSuperBlock.CompatibleFeatures()
|
||||
& EXT2_INCOMPATIBLE_FEATURE_64BIT) != 0; }
|
||||
bool HasExtentsFeature() const
|
||||
{ return (fSuperBlock.IncompatibleFeatures()
|
||||
& EXT2_INCOMPATIBLE_FEATURE_EXTENTS) != 0; }
|
||||
uint8 DefaultHashVersion() const
|
||||
{ return fSuperBlock.default_hash_version; }
|
||||
bool HugeFiles() const
|
||||
@@ -100,10 +103,10 @@ public:
|
||||
|
||||
status_t AllocateBlocks(Transaction& transaction,
|
||||
uint32 minimum, uint32 maximum,
|
||||
uint32& blockGroup, off_t& start,
|
||||
uint32& blockGroup, fsblock_t& start,
|
||||
uint32& length);
|
||||
status_t FreeBlocks(Transaction& transaction,
|
||||
off_t start, uint32 length);
|
||||
fsblock_t start, uint32 length);
|
||||
|
||||
status_t LoadSuperBlock();
|
||||
status_t WriteSuperBlock(Transaction& transaction);
|
||||
|
||||
@@ -15,6 +15,11 @@
|
||||
|
||||
//#define TRACE_EXT2
|
||||
|
||||
|
||||
typedef uint64 fileblock_t; // file block number
|
||||
typedef uint64 fsblock_t; // filesystem block number
|
||||
|
||||
|
||||
#define EXT2_SUPER_BLOCK_OFFSET 1024
|
||||
|
||||
struct ext2_super_block {
|
||||
@@ -213,7 +218,7 @@ struct ext2_block_group {
|
||||
uint16 unused_inodes_high;
|
||||
uint32 _reserved2[3];
|
||||
|
||||
uint64 BlockBitmap(bool has64bits) const
|
||||
fsblock_t BlockBitmap(bool has64bits) const
|
||||
{
|
||||
uint64 block = B_LENDIAN_TO_HOST_INT32(block_bitmap);
|
||||
if (has64bits)
|
||||
@@ -221,7 +226,7 @@ struct ext2_block_group {
|
||||
((uint64)B_LENDIAN_TO_HOST_INT32(block_bitmap_high) << 32);
|
||||
return block;
|
||||
}
|
||||
uint64 InodeBitmap(bool has64bits) const
|
||||
fsblock_t InodeBitmap(bool has64bits) const
|
||||
{
|
||||
uint64 bitmap = B_LENDIAN_TO_HOST_INT32(inode_bitmap);
|
||||
if (has64bits)
|
||||
@@ -296,6 +301,76 @@ struct ext2_data_stream {
|
||||
uint32 triple_indirect;
|
||||
} _PACKED;
|
||||
|
||||
#define EXT2_EXTENT_MAGIC 0xf30a
|
||||
|
||||
struct ext2_extent_header {
|
||||
uint16 magic;
|
||||
uint16 num_entries;
|
||||
uint16 max_entries;
|
||||
uint16 depth;
|
||||
uint32 generation;
|
||||
bool IsValid() const
|
||||
{
|
||||
return B_LENDIAN_TO_HOST_INT16(magic) == EXT2_EXTENT_MAGIC;
|
||||
}
|
||||
uint16 NumEntries() const { return B_LENDIAN_TO_HOST_INT16(num_entries); }
|
||||
uint16 MaxEntries() const { return B_LENDIAN_TO_HOST_INT16(max_entries); }
|
||||
uint16 Depth() const { return B_LENDIAN_TO_HOST_INT16(depth); }
|
||||
uint32 Generation() const { return B_LENDIAN_TO_HOST_INT32(generation); }
|
||||
void SetNumEntries(uint16 num)
|
||||
{ num_entries = B_HOST_TO_LENDIAN_INT16(num); }
|
||||
void SetMaxEntries(uint16 max)
|
||||
{ max_entries = B_HOST_TO_LENDIAN_INT16(max); }
|
||||
void SetDepth(uint16 _depth)
|
||||
{ depth = B_HOST_TO_LENDIAN_INT16(_depth); }
|
||||
void SetGeneration(uint32 _generation)
|
||||
{ generation = B_HOST_TO_LENDIAN_INT32(_generation); }
|
||||
} _PACKED;
|
||||
|
||||
struct ext2_extent_index {
|
||||
uint32 logical_block;
|
||||
uint32 physical_block;
|
||||
uint16 physical_block_high;
|
||||
uint16 _reserved;
|
||||
uint32 LogicalBlock() const
|
||||
{ return B_LENDIAN_TO_HOST_INT32(logical_block); }
|
||||
uint64 PhysicalBlock() const { return B_LENDIAN_TO_HOST_INT32(physical_block)
|
||||
| ((uint64)B_LENDIAN_TO_HOST_INT16(physical_block_high) << 32); }
|
||||
void SetLogicalBlock(uint32 block) {
|
||||
logical_block = B_HOST_TO_LENDIAN_INT32(block); }
|
||||
void SetPhysicalBlock(uint64 block) {
|
||||
physical_block = B_HOST_TO_LENDIAN_INT32(block & 0xffffffff);
|
||||
physical_block_high = B_HOST_TO_LENDIAN_INT16((block >> 32) & 0xffff); }
|
||||
} _PACKED;
|
||||
|
||||
struct ext2_extent_entry {
|
||||
uint32 logical_block;
|
||||
uint16 length;
|
||||
uint16 physical_block_high;
|
||||
uint32 physical_block;
|
||||
uint32 LogicalBlock() const
|
||||
{ return B_LENDIAN_TO_HOST_INT32(logical_block); }
|
||||
uint16 Length() const { return B_LENDIAN_TO_HOST_INT16(length) == 0x8000
|
||||
? 0x8000 : B_LENDIAN_TO_HOST_INT16(length) & 0x7fff; }
|
||||
uint64 PhysicalBlock() const { return B_LENDIAN_TO_HOST_INT32(physical_block)
|
||||
| ((uint64)B_LENDIAN_TO_HOST_INT16(physical_block_high) << 32); }
|
||||
void SetLogicalBlock(uint32 block) {
|
||||
logical_block = B_HOST_TO_LENDIAN_INT32(block); }
|
||||
void SetLength(uint16 _length) {
|
||||
length = B_HOST_TO_LENDIAN_INT16(_length) & 0x7fff; }
|
||||
void SetPhysicalBlock(uint64 block) {
|
||||
physical_block = B_HOST_TO_LENDIAN_INT32(block & 0xffffffff);
|
||||
physical_block_high = B_HOST_TO_LENDIAN_INT16((block >> 32) & 0xffff); }
|
||||
} _PACKED;
|
||||
|
||||
struct ext2_extent_stream {
|
||||
ext2_extent_header extent_header;
|
||||
union {
|
||||
ext2_extent_entry extent_entries[4];
|
||||
ext2_extent_index extent_index[4];
|
||||
};
|
||||
} _PACKED;
|
||||
|
||||
#define EXT2_INODE_NORMAL_SIZE 128
|
||||
|
||||
struct ext2_inode {
|
||||
@@ -314,6 +389,7 @@ struct ext2_inode {
|
||||
union {
|
||||
ext2_data_stream stream;
|
||||
char symlink[EXT2_SHORT_SYMLINK_LENGTH];
|
||||
ext2_extent_stream extent_stream;
|
||||
};
|
||||
uint32 generation;
|
||||
uint32 file_access_control;
|
||||
@@ -546,9 +622,18 @@ struct ext2_inode {
|
||||
#define EXT2_INODE_COMPRESSION_ERROR 0x00000800
|
||||
#define EXT2_INODE_BTREE 0x00001000
|
||||
#define EXT2_INODE_INDEXED 0x00001000
|
||||
#define EXT2_INODE_JOURNAL_DATA 0x00004000
|
||||
#define EXT2_INODE_NO_MERGE_TAIL 0x00008000
|
||||
#define EXT2_INODE_DIR_SYNCH 0x00010000
|
||||
#define EXT2_INODE_HUGE_FILE 0x00040000
|
||||
#define EXT2_INODE_EXTENTS 0x00080000
|
||||
|
||||
#define EXT2_INODE_INHERITED (EXT2_INODE_SECURE_DELETION | EXT2_INODE_UNDELETE \
|
||||
| EXT2_INODE_COMPRESSED | EXT2_INODE_SYNCHRONOUS | EXT2_INODE_IMMUTABLE \
|
||||
| EXT2_INODE_APPEND_ONLY | EXT2_INODE_NO_DUMP | EXT2_INODE_NO_ACCESS_TIME \
|
||||
| EXT2_INODE_DO_NOT_COMPRESS | EXT2_INODE_JOURNAL_DATA \
|
||||
| EXT2_INODE_NO_MERGE_TAIL | EXT2_INODE_DIR_SYNCH)
|
||||
|
||||
#define EXT2_NAME_LENGTH 255
|
||||
|
||||
struct ext2_dir_entry {
|
||||
@@ -680,6 +765,7 @@ struct file_cookie {
|
||||
int open_mode;
|
||||
};
|
||||
|
||||
|
||||
#define EXT2_OPEN_MODE_USER_MASK 0x7fffffff
|
||||
|
||||
#define INODE_NOTIFICATION_INTERVAL 10000000LL
|
||||
|
||||
@@ -407,12 +407,17 @@ ext2_get_file_map(fs_volume* _volume, fs_vnode* _node, off_t offset,
|
||||
size_t index = 0, max = *_count;
|
||||
|
||||
while (true) {
|
||||
off_t block;
|
||||
fsblock_t block;
|
||||
uint32 count = 1;
|
||||
status_t status = inode->FindBlock(offset, block, &count);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
if (block > volume->NumBlocks()) {
|
||||
panic("ext2_get_file_map() found block %lld for offset %lld\n",
|
||||
block, offset);
|
||||
}
|
||||
|
||||
off_t blockOffset = block << volume->BlockShift();
|
||||
uint32 blockLength = volume->BlockSize() * count;
|
||||
|
||||
@@ -506,7 +511,7 @@ ext2_ioctl(fs_volume* _volume, fs_vnode* _node, void* _cookie, uint32 cmd,
|
||||
uint32 blocksPerGroup = volume->BlocksPerGroup();
|
||||
uint32 blockSize = volume->BlockSize();
|
||||
uint32 firstBlock = volume->FirstDataBlock();
|
||||
off_t start = 0;
|
||||
fsblock_t start = 0;
|
||||
uint32 group = 0;
|
||||
uint32 length;
|
||||
|
||||
@@ -955,7 +960,7 @@ ext2_rename(fs_volume* _volume, fs_vnode* _oldDir, const char* oldName,
|
||||
if (status != B_OK)
|
||||
return B_IO_ERROR;
|
||||
|
||||
off_t blockNum;
|
||||
fsblock_t blockNum;
|
||||
status = parent->FindBlock(0, blockNum);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
Reference in New Issue
Block a user