* 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:
Jérôme Duval
2010-12-18 16:26:39 +00:00
parent 699dc0e367
commit 45af882d06
26 changed files with 784 additions and 81 deletions
@@ -39,7 +39,7 @@ public:
status_t ScanFreeRanges(); status_t ScanFreeRanges();
bool IsFull() const; bool IsFull() const;
status_t Allocate(Transaction& transaction, uint32 start, status_t Allocate(Transaction& transaction, fsblock_t start,
uint32 length); uint32 length);
status_t Free(Transaction& transaction, uint32 start, status_t Free(Transaction& transaction, uint32 start,
uint32 length); uint32 length);
@@ -48,9 +48,9 @@ public:
uint32 NumBits() const; uint32 NumBits() const;
uint32 FreeBits() const; uint32 FreeBits() const;
uint32 Start() const; fsblock_t Start() const;
uint32 LargestStart() const; fsblock_t LargestStart() const;
uint32 LargestLength() const; uint32 LargestLength() const;
// TransactionListener implementation // TransactionListener implementation
@@ -70,9 +70,9 @@ private:
mutex fTransactionLock; mutex fTransactionLock;
int32 fCurrentTransaction; int32 fCurrentTransaction;
uint32 fStart; fsblock_t fStart;
uint32 fNumBits; uint32 fNumBits;
off_t fBitmapBlock; fsblock_t fBitmapBlock;
uint32 fFreeBits; uint32 fFreeBits;
uint32 fFirstFree; uint32 fFirstFree;
@@ -122,6 +122,7 @@ AllocationBlockGroup::Initialize(Volume* volume, uint32 blockGroup,
fVolume = volume; fVolume = volume;
fBlockGroup = blockGroup; fBlockGroup = blockGroup;
fNumBits = numBits; fNumBits = numBits;
fStart = blockGroup * numBits + fVolume->FirstDataBlock();
status_t status = fVolume->GetBlockGroup(blockGroup, &fGroupDescriptor); status_t status = fVolume->GetBlockGroup(blockGroup, &fGroupDescriptor);
if (status != B_OK) if (status != B_OK)
@@ -187,13 +188,17 @@ AllocationBlockGroup::IsFull() const
status_t status_t
AllocationBlockGroup::Allocate(Transaction& transaction, uint32 start, AllocationBlockGroup::Allocate(Transaction& transaction, fsblock_t _start,
uint32 length) uint32 length)
{ {
uint32 start = _start - fStart;
TRACE("AllocationBlockGroup::Allocate(%ld,%ld)\n", start, length); TRACE("AllocationBlockGroup::Allocate(%ld,%ld)\n", start, length);
if (length == 0)
return B_OK;
uint32 end = start + length; uint32 end = start + length;
if (end > fNumBits) if (end > fNumBits)
return B_BAD_DATA; return B_BAD_VALUE;
_LockInTransaction(transaction); _LockInTransaction(transaction);
@@ -263,9 +268,8 @@ AllocationBlockGroup::Free(Transaction& transaction, uint32 start,
return B_OK; return B_OK;
uint32 end = start + length; uint32 end = start + length;
if (end > fNumBits) if (end > fNumBits)
return B_BAD_DATA; return B_BAD_VALUE;
_LockInTransaction(transaction); _LockInTransaction(transaction);
@@ -342,17 +346,17 @@ AllocationBlockGroup::FreeBits() const
} }
uint32 fsblock_t
AllocationBlockGroup::Start() const AllocationBlockGroup::Start() const
{ {
return fStart; return fStart;
} }
uint32 fsblock_t
AllocationBlockGroup::LargestStart() const AllocationBlockGroup::LargestStart() const
{ {
return fLargestStart; return fStart + fLargestStart;
} }
@@ -457,7 +461,7 @@ BlockAllocator::Initialize()
fNumBlocks = fVolume->NumBlocks(); fNumBlocks = fVolume->NumBlocks();
TRACE("BlockAllocator::Initialize(): blocks per group: %lu, block groups: " 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); fNumGroups, fFirstBlock, fNumBlocks);
fGroups = new(std::nothrow) AllocationBlockGroup[fNumGroups]; fGroups = new(std::nothrow) AllocationBlockGroup[fNumGroups];
@@ -486,7 +490,7 @@ BlockAllocator::Initialize()
status_t status_t
BlockAllocator::AllocateBlocks(Transaction& transaction, uint32 minimum, 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"); TRACE("BlockAllocator::AllocateBlocks()\n");
MutexLocker lock(fLock); MutexLocker lock(fLock);
@@ -496,7 +500,7 @@ BlockAllocator::AllocateBlocks(Transaction& transaction, uint32 minimum,
"max: %lu, block group: %lu, start: %llu, num groups: %lu\n", "max: %lu, block group: %lu, start: %llu, num groups: %lu\n",
transaction.ID(), minimum, maximum, blockGroup, start, fNumGroups); transaction.ID(), minimum, maximum, blockGroup, start, fNumGroups);
off_t bestStart = 0; fsblock_t bestStart = 0;
uint32 bestLength = 0; uint32 bestLength = 0;
uint32 bestGroup = 0; uint32 bestGroup = 0;
@@ -567,10 +571,12 @@ BlockAllocator::AllocateBlocks(Transaction& transaction, uint32 minimum,
status_t status_t
BlockAllocator::Allocate(Transaction& transaction, Inode* inode, 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) if (numBlocks <= 0)
return B_ERROR; return B_ERROR;
if (start > fNumBlocks)
return B_BAD_VALUE;
uint32 group = inode->ID() / fVolume->InodesPerGroup(); uint32 group = inode->ID() / fVolume->InodesPerGroup();
uint32 preferred = 0; uint32 preferred = 0;
@@ -644,28 +650,32 @@ BlockAllocator::Allocate(Transaction& transaction, Inode* inode,
status_t 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); TRACE("BlockAllocator::Free(%llu, %lu)\n", start, length);
MutexLocker lock(fLock); MutexLocker lock(fLock);
if (start <= fFirstBlock) { if (start <= fFirstBlock) {
panic("Trying to free superblock!\n"); panic("Trying to free superblock!\n");
return B_BAD_DATA; return B_BAD_VALUE;
} }
if (length == 0) if (length == 0)
return B_OK; 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); fFirstBlock, fBlocksPerGroup);
start -= fFirstBlock; start -= fFirstBlock;
off_t end = start + length - 1; off_t end = start + length - 1;
uint32 group = start / fBlocksPerGroup; uint32 group = start / fBlocksPerGroup;
if (group >= fNumGroups) if (group >= fNumGroups) {
panic("BlockAllocator::Free() group %ld too big (fNumGroups %ld)\n", group, fNumGroups); panic("BlockAllocator::Free() group %ld too big (fNumGroups %ld)\n",
group, fNumGroups);
}
uint32 lastGroup = end / fBlocksPerGroup; uint32 lastGroup = end / fBlocksPerGroup;
start = start % fBlocksPerGroup; start = start % fBlocksPerGroup;
@@ -8,8 +8,10 @@
#ifndef BLOCKALLOCATOR_H #ifndef BLOCKALLOCATOR_H
#define BLOCKALLOCATOR_H #define BLOCKALLOCATOR_H
#include <lock.h> #include <lock.h>
#include "ext2.h"
#include "Transaction.h" #include "Transaction.h"
@@ -27,11 +29,11 @@ public:
status_t AllocateBlocks(Transaction& transaction, status_t AllocateBlocks(Transaction& transaction,
uint32 minimum, uint32 maximum, uint32& blockGroup, uint32 minimum, uint32 maximum, uint32& blockGroup,
off_t& start, uint32& length); fsblock_t& start, uint32& length);
status_t Allocate(Transaction& transaction, Inode* inode, 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); uint32& length);
status_t Free(Transaction& transaction, off_t start, status_t Free(Transaction& transaction, fsblock_t start,
uint32 length); uint32 length);
uint32 FreeBlocks(); uint32 FreeBlocks();
@@ -45,9 +47,9 @@ protected:
AllocationBlockGroup* fGroups; AllocationBlockGroup* fGroups;
uint32 fBlocksPerGroup; uint32 fBlocksPerGroup;
uint32 fNumBlocks; fsblock_t fNumBlocks;
uint32 fNumGroups; uint32 fNumGroups;
uint32 fFirstBlock; fsblock_t fFirstBlock;
}; };
#endif // BLOCKALLOCATOR_H #endif // BLOCKALLOCATOR_H
@@ -53,7 +53,7 @@ DataStream::~DataStream()
status_t 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(); uint32 index = offset >> fVolume->BlockShift();
@@ -377,8 +377,7 @@ DataStream::_GetBlock(Transaction& transaction, uint32& blockNum)
if (fAllocated == 0) { if (fAllocated == 0) {
uint32 blockGroup = (fAllocatedPos - fFirstBlock) uint32 blockGroup = (fAllocatedPos - fFirstBlock)
/ fVolume->BlocksPerGroup(); / fVolume->BlocksPerGroup();
fAllocatedPos %= fVolume->BlocksPerGroup();
status_t status = fVolume->AllocateBlocks(transaction, 1, fWaiting, status_t status = fVolume->AllocateBlocks(transaction, 1, fWaiting,
blockGroup, fAllocatedPos, fAllocated); blockGroup, fAllocatedPos, fAllocated);
if (status != B_OK) { if (status != B_OK) {
@@ -387,7 +386,6 @@ DataStream::_GetBlock(Transaction& transaction, uint32& blockNum)
} }
fWaiting -= fAllocated; fWaiting -= fAllocated;
fAllocatedPos += fVolume->BlocksPerGroup() * blockGroup + fFirstBlock;
TRACE("DataStream::_GetBlock(): newAllocated: %lu, newpos: %llu," TRACE("DataStream::_GetBlock(): newAllocated: %lu, newpos: %llu,"
"newwaiting: %lu\n", fAllocated, fAllocatedPos, fWaiting); "newwaiting: %lu\n", fAllocated, fAllocatedPos, fWaiting);
@@ -8,6 +8,7 @@
#ifndef DATASTREAM_H #ifndef DATASTREAM_H
#define DATASTREAM_H #define DATASTREAM_H
#include "ext2.h" #include "ext2.h"
#include "Transaction.h" #include "Transaction.h"
@@ -22,7 +23,7 @@ public:
off_t size); off_t size);
~DataStream(); ~DataStream();
status_t FindBlock(off_t offset, off_t& block, status_t FindBlock(off_t offset, fsblock_t& block,
uint32 *_count = NULL); uint32 *_count = NULL);
status_t Enlarge(Transaction& transaction, off_t& numBlocks); status_t Enlarge(Transaction& transaction, off_t& numBlocks);
status_t Shrink(Transaction& transaction, off_t& numBlocks); status_t Shrink(Transaction& transaction, off_t& numBlocks);
@@ -82,7 +83,7 @@ private:
uint32 fFirstBlock; uint32 fFirstBlock;
uint32 fAllocated; uint32 fAllocated;
off_t fAllocatedPos; fsblock_t fAllocatedPos;
uint32 fWaiting; uint32 fWaiting;
uint32 fFreeStart; uint32 fFreeStart;
@@ -517,7 +517,7 @@ DirectoryIterator::_SplitIndexedBlock(Transaction& transaction,
return B_NO_MEMORY; return B_NO_MEMORY;
ArrayDeleter<uint8> bufferDeleter(buffer); 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 // Prepare block to hold the first half of the entries and fill the buffer
CachedBlock cachedFirst(fVolume); CachedBlock cachedFirst(fVolume);
@@ -6,6 +6,8 @@
#define DIRECTORY_ITERATOR_H #define DIRECTORY_ITERATOR_H
#include "ext2.h"
#include <SupportDefs.h> #include <SupportDefs.h>
#include "Transaction.h" #include "Transaction.h"
@@ -69,7 +71,7 @@ protected:
uint32 fNumBlocks; uint32 fNumBlocks;
uint32 fLogicalBlock; uint32 fLogicalBlock;
off_t fPhysicalBlock; fsblock_t fPhysicalBlock;
uint32 fDisplacement; uint32 fDisplacement;
uint32 fPreviousDisplacement; 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 status_t
HTree::PrepareForHash() HTree::PrepareForHash()
{ {
off_t blockNum; fsblock_t blockNum;
status_t status = fDirectory->FindBlock(0, blockNum); status_t status = fDirectory->FindBlock(0, blockNum);
if (status != B_OK) if (status != B_OK)
return status; return status;
@@ -115,7 +115,7 @@ HTree::Lookup(const char* name, DirectoryIterator** iterator)
return _FallbackToLinearIteration(iterator); return _FallbackToLinearIteration(iterator);
} }
off_t blockNum; fsblock_t blockNum;
status_t status = fDirectory->FindBlock(0, blockNum); status_t status = fDirectory->FindBlock(0, blockNum);
if (status != B_OK) if (status != B_OK)
return _FallbackToLinearIteration(iterator); return _FallbackToLinearIteration(iterator);
@@ -189,7 +189,7 @@ HTreeEntryIterator::Lookup(uint32 hash, int indirections,
TRACE("HTreeEntryIterator::Lookup(): Creating a HTree entry iterator " TRACE("HTreeEntryIterator::Lookup(): Creating a HTree entry iterator "
"starting at block: %lu, hash: 0x%lX\n", start->Block(), start->Hash()); "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, status_t status = fDirectory->FindBlock(start->Block() * fBlockSize,
blockNum); blockNum);
if (status != B_OK) 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 " panic("Splitting a HTree node required, but isn't yet fully "
"supported\n"); "supported\n");
off_t physicalBlock; fsblock_t physicalBlock;
status_t status = fDirectory->FindBlock(newBlocksPos, physicalBlock); status_t status = fDirectory->FindBlock(newBlocksPos, physicalBlock);
if (status != B_OK) if (status != B_OK)
return status; return status;
@@ -53,7 +53,7 @@ private:
uint16 fCurrentEntry; uint16 fCurrentEntry;
uint32 fBlockSize; uint32 fBlockSize;
off_t fBlockNum; fsblock_t fBlockNum;
HTreeEntryIterator* fParent; HTreeEntryIterator* fParent;
HTreeEntryIterator* fChild; HTreeEntryIterator* fChild;
+38 -9
View File
@@ -13,6 +13,7 @@
#include "CachedBlock.h" #include "CachedBlock.h"
#include "DataStream.h" #include "DataStream.h"
#include "DirectoryIterator.h" #include "DirectoryIterator.h"
#include "ExtentStream.h"
#include "HTree.h" #include "HTree.h"
#include "Utility.h" #include "Utility.h"
@@ -224,8 +225,12 @@ Inode::CheckPermissions(int accessMode) const
status_t 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()); DataStream stream(fVolume, &fNode.stream, Size());
return stream.FindBlock(offset, block, _count); return stream.FindBlock(offset, block, _count);
} }
@@ -407,9 +412,14 @@ Inode::InitDirectory(Transaction& transaction, Inode* parent)
if (status != B_OK) if (status != B_OK)
return status; return status;
off_t blockNum; fsblock_t blockNum;
DataStream stream(fVolume, &fNode.stream, Size()); if (Flags() & EXT2_INODE_EXTENTS) {
status = stream.FindBlock(0, blockNum); 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) if (status != B_OK)
return status; return status;
@@ -613,6 +623,14 @@ Inode::Create(Transaction& transaction, Inode* parent, const char* name,
inode->SetAccessTime(&timespec); inode->SetAccessTime(&timespec);
inode->SetCreationTime(&timespec); inode->SetCreationTime(&timespec);
inode->SetModificationTime(&timespec); inode->SetModificationTime(&timespec);
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()) if (sizeof(ext2_inode) < volume->InodeSize())
node.SetExtraInodeSize(sizeof(ext2_inode) - EXT2_INODE_NORMAL_SIZE); 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; off_t end = size == 0 ? 0 : (size - 1) / fVolume->BlockSize() + 1;
DataStream stream(fVolume, &fNode.stream, oldSize); if (Flags() & EXT2_INODE_EXTENTS) {
stream.Enlarge(transaction, end); 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); TRACE("Inode::_EnlargeDataStream(): Setting size to %lld\n", size);
fNode.SetSize(size); fNode.SetSize(size);
TRACE("Inode::_EnlargeDataStream(): Setting allocated block count to %llu\n", 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; off_t end = size == 0 ? 0 : (size - 1) / fVolume->BlockSize() + 1;
DataStream stream(fVolume, &fNode.stream, oldSize); if (Flags() & EXT2_INODE_EXTENTS) {
stream.Shrink(transaction, end); 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); fNode.SetSize(size);
return _SetNumBlocks(_NumBlocks() - end * (fVolume->BlockSize() / 512)); return _SetNumBlocks(_NumBlocks() - end * (fVolume->BlockSize() / 512));
+1 -1
View File
@@ -79,7 +79,7 @@ public:
//::Volume* _Volume() const { return fVolume; } //::Volume* _Volume() const { return fVolume; }
Volume* GetVolume() 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); uint32 *_count = NULL);
status_t ReadAt(off_t pos, uint8 *buffer, size_t *length); status_t ReadAt(off_t pos, uint8 *buffer, size_t *length);
status_t WriteAt(Transaction& transaction, off_t pos, status_t WriteAt(Transaction& transaction, off_t pos,
@@ -156,7 +156,7 @@ InodeAllocator::_Allocate(Transaction& transaction, uint32 preferredBlockGroup,
status_t status_t
InodeAllocator::_MarkInBitmap(Transaction& transaction, off_t bitmapBlock, InodeAllocator::_MarkInBitmap(Transaction& transaction, fsblock_t bitmapBlock,
uint32 blockGroup, uint32 numInodes, ino_t& id) uint32 blockGroup, uint32 numInodes, ino_t& id)
{ {
BitmapBlock inodeBitmap(fVolume, numInodes); BitmapBlock inodeBitmap(fVolume, numInodes);
@@ -190,7 +190,7 @@ InodeAllocator::_MarkInBitmap(Transaction& transaction, off_t bitmapBlock,
status_t status_t
InodeAllocator::_UnmarkInBitmap(Transaction& transaction, off_t bitmapBlock, InodeAllocator::_UnmarkInBitmap(Transaction& transaction, fsblock_t bitmapBlock,
uint32 numInodes, ino_t id) uint32 numInodes, ino_t id)
{ {
BitmapBlock inodeBitmap(fVolume, numInodes); BitmapBlock inodeBitmap(fVolume, numInodes);
@@ -10,6 +10,7 @@
#include <lock.h> #include <lock.h>
#include "ext2.h"
#include "Transaction.h" #include "Transaction.h"
@@ -29,13 +30,13 @@ public:
private: private:
status_t _Allocate(Transaction& transaction, status_t _Allocate(Transaction& transaction,
uint32 prefferedBlockGroup, bool isDirectory, uint32 preferredBlockGroup, bool isDirectory,
ino_t& id); ino_t& id);
status_t _MarkInBitmap(Transaction& transaction, status_t _MarkInBitmap(Transaction& transaction,
off_t bitmapBlock, uint32 blockGroup, fsblock_t bitmapBlock, uint32 blockGroup,
uint32 numInodes, ino_t& id); uint32 numInodes, ino_t& id);
status_t _UnmarkInBitmap(Transaction& transaction, status_t _UnmarkInBitmap(Transaction& transaction,
off_t bitmapBlock, uint32 numInodes, ino_t id); fsblock_t bitmapBlock, uint32 numInodes, ino_t id);
Volume* fVolume; Volume* fVolume;
@@ -84,7 +84,7 @@ InodeJournal::InitCheck()
status_t status_t
InodeJournal::MapBlock(off_t logical, off_t& physical) InodeJournal::MapBlock(off_t logical, fsblock_t& physical)
{ {
TRACE("InodeJournal::MapBlock()\n"); TRACE("InodeJournal::MapBlock()\n");
return fInode->FindBlock(logical * fBlockSize, physical); return fInode->FindBlock(logical * fBlockSize, physical);
@@ -20,7 +20,7 @@ public:
status_t InitCheck(); status_t InitCheck();
status_t MapBlock(off_t logical, off_t& physical); status_t MapBlock(off_t logical, fsblock_t& physical);
private: private:
Inode* fInode; Inode* fInode;
status_t fInitStatus; status_t fInitStatus;
@@ -11,6 +11,7 @@ KernelAddon ext2 :
Inode.cpp Inode.cpp
Attribute.cpp Attribute.cpp
DirectoryIterator.cpp DirectoryIterator.cpp
ExtentStream.cpp
HTree.cpp HTree.cpp
HTreeEntryIterator.cpp HTreeEntryIterator.cpp
RevokeManager.cpp RevokeManager.cpp
@@ -304,7 +304,7 @@ Journal::Unlock(Transaction* owner, bool success)
status_t status_t
Journal::MapBlock(off_t logical, off_t& physical) Journal::MapBlock(off_t logical, fsblock_t& physical)
{ {
TRACE("Journal::MapBlock()\n"); TRACE("Journal::MapBlock()\n");
physical = logical; physical = logical;
@@ -402,7 +402,7 @@ Journal::_WritePartialTransactionToLog(JournalHeader* descriptorBlock,
logBlock = _WrapAroundLog(logBlock + 1); logBlock = _WrapAroundLog(logBlock + 1);
off_t physicalBlock; fsblock_t physicalBlock;
status = MapBlock(logBlock, physicalBlock); status = MapBlock(logBlock, physicalBlock);
if (status != B_OK) if (status != B_OK)
return status; return status;
@@ -434,7 +434,7 @@ Journal::_WritePartialTransactionToLog(JournalHeader* descriptorBlock,
--tag; --tag;
tag->SetLastTagFlag(); tag->SetLastTagFlag();
off_t physicalBlock; fsblock_t physicalBlock;
status = MapBlock(descriptorBlockPos, physicalBlock); status = MapBlock(descriptorBlockPos, physicalBlock);
if (status != B_OK) if (status != B_OK)
return status; return status;
@@ -579,7 +579,7 @@ Journal::_WriteTransactionToLog()
// written. When it recovery reaches where the first commit should be // 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. // and doesn't find it, it considers it found the end of the log.
off_t physicalBlock; fsblock_t physicalBlock;
status = MapBlock(logBlock, physicalBlock); status = MapBlock(logBlock, physicalBlock);
if (status != B_OK) if (status != B_OK)
return status; return status;
@@ -602,7 +602,7 @@ Journal::_WriteTransactionToLog()
} }
// Transaction will enter the Commit state // Transaction will enter the Commit state
off_t physicalBlock; fsblock_t physicalBlock;
status = MapBlock(commitBlockPos, physicalBlock); status = MapBlock(commitBlockPos, physicalBlock);
if (status != B_OK) if (status != B_OK)
return status; return status;
@@ -661,7 +661,7 @@ status_t
Journal::_SaveSuperBlock() Journal::_SaveSuperBlock()
{ {
TRACE("Journal::_SaveSuperBlock()\n"); TRACE("Journal::_SaveSuperBlock()\n");
off_t physicalBlock; fsblock_t physicalBlock;
status_t status = MapBlock(0, physicalBlock); status_t status = MapBlock(0, physicalBlock);
if (status != B_OK) if (status != B_OK)
return status; return status;
@@ -695,7 +695,7 @@ status_t
Journal::_LoadSuperBlock() Journal::_LoadSuperBlock()
{ {
TRACE("Journal::_LoadSuperBlock()\n"); TRACE("Journal::_LoadSuperBlock()\n");
off_t superblockPos; fsblock_t superblockPos;
status_t status = MapBlock(0, superblockPos); status_t status = MapBlock(0, superblockPos);
if (status != B_OK) if (status != B_OK)
@@ -842,7 +842,7 @@ Journal::_RecoverPassScan(uint32& lastCommitID)
JournalHeader* header; JournalHeader* header;
uint32 nextCommitID = fFirstCommitID; uint32 nextCommitID = fFirstCommitID;
uint32 nextBlock = fLogStart; uint32 nextBlock = fLogStart;
off_t nextBlockPos; fsblock_t nextBlockPos;
status_t status = MapBlock(nextBlock, nextBlockPos); status_t status = MapBlock(nextBlock, nextBlockPos);
if (status != B_OK) if (status != B_OK)
@@ -898,7 +898,7 @@ Journal::_RecoverPassRevoke(uint32 lastCommitID)
JournalHeader* header; JournalHeader* header;
uint32 nextCommitID = fFirstCommitID; uint32 nextCommitID = fFirstCommitID;
uint32 nextBlock = fLogStart; uint32 nextBlock = fLogStart;
off_t nextBlockPos; fsblock_t nextBlockPos;
status_t status = MapBlock(nextBlock, nextBlockPos); status_t status = MapBlock(nextBlock, nextBlockPos);
if (status != B_OK) if (status != B_OK)
@@ -960,7 +960,7 @@ Journal::_RecoverPassReplay(uint32 lastCommitID)
uint32 nextCommitID = fFirstCommitID; uint32 nextCommitID = fFirstCommitID;
uint32 nextBlock = fLogStart; uint32 nextBlock = fLogStart;
off_t nextBlockPos; fsblock_t nextBlockPos;
status_t status = MapBlock(nextBlock, nextBlockPos); status_t status = MapBlock(nextBlock, nextBlockPos);
if (status != B_OK) if (status != B_OK)
@@ -173,7 +173,7 @@ public:
bool separateSubTransactions); bool separateSubTransactions);
virtual status_t Unlock(Transaction* owner, bool success); 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; inline uint32 FreeLogBlocks() const;
status_t FlushLogAndBlocks(); status_t FlushLogAndBlocks();
@@ -279,8 +279,10 @@ Volume::Mount(const char* deviceName, uint32 flags)
DeviceOpener opener(deviceName, (flags & B_MOUNT_READ_ONLY) != 0 DeviceOpener opener(deviceName, (flags & B_MOUNT_READ_ONLY) != 0
? O_RDONLY : O_RDWR); ? O_RDONLY : O_RDWR);
fDevice = opener.Device(); fDevice = opener.Device();
if (fDevice < B_OK) if (fDevice < B_OK) {
FATAL("Volume::Mount(): couldn't open device\n");
return fDevice; return fDevice;
}
if (opener.IsReadOnly()) if (opener.IsReadOnly())
fFlags |= VOLUME_READ_ONLY; fFlags |= VOLUME_READ_ONLY;
@@ -291,8 +293,10 @@ Volume::Mount(const char* deviceName, uint32 flags)
// read the super block // read the super block
status_t status = Identify(fDevice, &fSuperBlock); status_t status = Identify(fDevice, &fSuperBlock);
if (status != B_OK) if (status != B_OK) {
FATAL("Volume::Mount(): Identify() failed\n");
return status; return status;
}
// check read-only features if mounting read-write // check read-only features if mounting read-write
if (!IsReadOnly() && _UnsupportedReadOnlyFeatures(fSuperBlock) != 0) 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) // initialize short hands to the super block (to save byte swapping)
fBlockShift = fSuperBlock.BlockShift(); fBlockShift = fSuperBlock.BlockShift();
fBlockSize = 1UL << fSuperBlock.BlockShift(); if (fBlockShift < 10 || fBlockShift > 16)
return B_ERROR;
fBlockSize = 1UL << fBlockShift;
fFirstDataBlock = fSuperBlock.FirstDataBlock(); fFirstDataBlock = fSuperBlock.FirstDataBlock();
fFreeBlocks = fSuperBlock.FreeBlocks(Has64bitFeature()); fFreeBlocks = fSuperBlock.FreeBlocks(Has64bitFeature());
@@ -338,7 +344,7 @@ Volume::Mount(const char* deviceName, uint32 flags)
status = opener.GetSize(&diskSize); status = opener.GetSize(&diskSize);
if (status != B_OK) if (status != B_OK)
return status; return status;
if (diskSize < (NumBlocks() << BlockShift())) if (diskSize < ((off_t)NumBlocks() << BlockShift()))
return B_BAD_VALUE; return B_BAD_VALUE;
fBlockCache = opener.InitCache(NumBlocks(), fBlockSize); fBlockCache = opener.InitCache(NumBlocks(), fBlockSize);
@@ -501,6 +507,7 @@ Volume::_UnsupportedIncompatibleFeatures(ext2_super_block& superBlock)
uint32 supportedIncompatible = EXT2_INCOMPATIBLE_FEATURE_FILE_TYPE uint32 supportedIncompatible = EXT2_INCOMPATIBLE_FEATURE_FILE_TYPE
| EXT2_INCOMPATIBLE_FEATURE_RECOVER | EXT2_INCOMPATIBLE_FEATURE_RECOVER
| EXT2_INCOMPATIBLE_FEATURE_JOURNAL | EXT2_INCOMPATIBLE_FEATURE_JOURNAL
| EXT2_INCOMPATIBLE_FEATURE_EXTENTS;
/*| EXT2_INCOMPATIBLE_FEATURE_META_GROUP*/; /*| EXT2_INCOMPATIBLE_FEATURE_META_GROUP*/;
uint32 unsupported = superBlock.IncompatibleFeatures() uint32 unsupported = superBlock.IncompatibleFeatures()
& ~supportedIncompatible; & ~supportedIncompatible;
@@ -742,7 +749,7 @@ Volume::FreeInode(Transaction& transaction, ino_t id, bool isDirectory)
status_t status_t
Volume::AllocateBlocks(Transaction& transaction, uint32 minimum, uint32 maximum, 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"); TRACE("Volume::AllocateBlocks()\n");
if (IsReadOnly()) if (IsReadOnly())
@@ -764,7 +771,7 @@ Volume::AllocateBlocks(Transaction& transaction, uint32 minimum, uint32 maximum,
status_t 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); TRACE("Volume::FreeBlocks(%llu, %lu)\n", start, length);
if (IsReadOnly()) if (IsReadOnly())
@@ -48,7 +48,7 @@ public:
{ return fNumInodes; } { return fNumInodes; }
uint32 NumGroups() const uint32 NumGroups() const
{ return fNumGroups; } { return fNumGroups; }
off_t NumBlocks() const fsblock_t NumBlocks() const
{ return fSuperBlock.NumBlocks( { return fSuperBlock.NumBlocks(
Has64bitFeature()); } Has64bitFeature()); }
off_t NumFreeBlocks() const off_t NumFreeBlocks() const
@@ -81,6 +81,9 @@ public:
bool Has64bitFeature() const bool Has64bitFeature() const
{ return (fSuperBlock.CompatibleFeatures() { return (fSuperBlock.CompatibleFeatures()
& EXT2_INCOMPATIBLE_FEATURE_64BIT) != 0; } & EXT2_INCOMPATIBLE_FEATURE_64BIT) != 0; }
bool HasExtentsFeature() const
{ return (fSuperBlock.IncompatibleFeatures()
& EXT2_INCOMPATIBLE_FEATURE_EXTENTS) != 0; }
uint8 DefaultHashVersion() const uint8 DefaultHashVersion() const
{ return fSuperBlock.default_hash_version; } { return fSuperBlock.default_hash_version; }
bool HugeFiles() const bool HugeFiles() const
@@ -100,10 +103,10 @@ public:
status_t AllocateBlocks(Transaction& transaction, status_t AllocateBlocks(Transaction& transaction,
uint32 minimum, uint32 maximum, uint32 minimum, uint32 maximum,
uint32& blockGroup, off_t& start, uint32& blockGroup, fsblock_t& start,
uint32& length); uint32& length);
status_t FreeBlocks(Transaction& transaction, status_t FreeBlocks(Transaction& transaction,
off_t start, uint32 length); fsblock_t start, uint32 length);
status_t LoadSuperBlock(); status_t LoadSuperBlock();
status_t WriteSuperBlock(Transaction& transaction); status_t WriteSuperBlock(Transaction& transaction);
+88 -2
View File
@@ -15,6 +15,11 @@
//#define TRACE_EXT2 //#define TRACE_EXT2
typedef uint64 fileblock_t; // file block number
typedef uint64 fsblock_t; // filesystem block number
#define EXT2_SUPER_BLOCK_OFFSET 1024 #define EXT2_SUPER_BLOCK_OFFSET 1024
struct ext2_super_block { struct ext2_super_block {
@@ -213,7 +218,7 @@ struct ext2_block_group {
uint16 unused_inodes_high; uint16 unused_inodes_high;
uint32 _reserved2[3]; uint32 _reserved2[3];
uint64 BlockBitmap(bool has64bits) const fsblock_t BlockBitmap(bool has64bits) const
{ {
uint64 block = B_LENDIAN_TO_HOST_INT32(block_bitmap); uint64 block = B_LENDIAN_TO_HOST_INT32(block_bitmap);
if (has64bits) if (has64bits)
@@ -221,7 +226,7 @@ struct ext2_block_group {
((uint64)B_LENDIAN_TO_HOST_INT32(block_bitmap_high) << 32); ((uint64)B_LENDIAN_TO_HOST_INT32(block_bitmap_high) << 32);
return block; return block;
} }
uint64 InodeBitmap(bool has64bits) const fsblock_t InodeBitmap(bool has64bits) const
{ {
uint64 bitmap = B_LENDIAN_TO_HOST_INT32(inode_bitmap); uint64 bitmap = B_LENDIAN_TO_HOST_INT32(inode_bitmap);
if (has64bits) if (has64bits)
@@ -296,6 +301,76 @@ struct ext2_data_stream {
uint32 triple_indirect; uint32 triple_indirect;
} _PACKED; } _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 #define EXT2_INODE_NORMAL_SIZE 128
struct ext2_inode { struct ext2_inode {
@@ -314,6 +389,7 @@ struct ext2_inode {
union { union {
ext2_data_stream stream; ext2_data_stream stream;
char symlink[EXT2_SHORT_SYMLINK_LENGTH]; char symlink[EXT2_SHORT_SYMLINK_LENGTH];
ext2_extent_stream extent_stream;
}; };
uint32 generation; uint32 generation;
uint32 file_access_control; uint32 file_access_control;
@@ -546,9 +622,18 @@ struct ext2_inode {
#define EXT2_INODE_COMPRESSION_ERROR 0x00000800 #define EXT2_INODE_COMPRESSION_ERROR 0x00000800
#define EXT2_INODE_BTREE 0x00001000 #define EXT2_INODE_BTREE 0x00001000
#define EXT2_INODE_INDEXED 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_HUGE_FILE 0x00040000
#define EXT2_INODE_EXTENTS 0x00080000 #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 #define EXT2_NAME_LENGTH 255
struct ext2_dir_entry { struct ext2_dir_entry {
@@ -680,6 +765,7 @@ struct file_cookie {
int open_mode; int open_mode;
}; };
#define EXT2_OPEN_MODE_USER_MASK 0x7fffffff #define EXT2_OPEN_MODE_USER_MASK 0x7fffffff
#define INODE_NOTIFICATION_INTERVAL 10000000LL #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; size_t index = 0, max = *_count;
while (true) { while (true) {
off_t block; fsblock_t block;
uint32 count = 1; uint32 count = 1;
status_t status = inode->FindBlock(offset, block, &count); status_t status = inode->FindBlock(offset, block, &count);
if (status != B_OK) if (status != B_OK)
return status; 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(); off_t blockOffset = block << volume->BlockShift();
uint32 blockLength = volume->BlockSize() * count; 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 blocksPerGroup = volume->BlocksPerGroup();
uint32 blockSize = volume->BlockSize(); uint32 blockSize = volume->BlockSize();
uint32 firstBlock = volume->FirstDataBlock(); uint32 firstBlock = volume->FirstDataBlock();
off_t start = 0; fsblock_t start = 0;
uint32 group = 0; uint32 group = 0;
uint32 length; uint32 length;
@@ -955,7 +960,7 @@ ext2_rename(fs_volume* _volume, fs_vnode* _oldDir, const char* oldName,
if (status != B_OK) if (status != B_OK)
return B_IO_ERROR; return B_IO_ERROR;
off_t blockNum; fsblock_t blockNum;
status = parent->FindBlock(0, blockNum); status = parent->FindBlock(0, blockNum);
if (status != B_OK) if (status != B_OK)
return status; return status;