* Inode::FindBlockRun() now checks the block run size of double indirect runs,
and stops processing if it finds something wrong. * The default indirect/double array size differs from BeOS' BFS. I've changed the size for double indirect arrays only, since the other size should work either way (not tested yet, this change has a negative effect on the maximum file size, but improves BeOS compatibility). * The read/write path of BFS is now double indirect block size agnostic, and should work with what it finds. * Merged all double indirect size computation into some utility inline functions. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@33551 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -1787,17 +1787,15 @@ BlockAllocator::CheckInode(Inode* inode, check_control* control)
|
||||
if (data->max_double_indirect_range) {
|
||||
status = CheckBlockRun(data->double_indirect, "double indirect",
|
||||
control);
|
||||
if (status < B_OK)
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
int32 runsPerBlock = fVolume->BlockSize() / sizeof(block_run);
|
||||
int32 runsPerArray = runsPerBlock << ARRAY_BLOCKS_SHIFT;
|
||||
int32 runsPerBlock = runs_per_block(fVolume);
|
||||
int32 runsPerArray = runsPerBlock * data->double_indirect.Length();
|
||||
|
||||
CachedBlock cachedDirect(fVolume);
|
||||
int32 maxIndirectIndex = (data->double_indirect.Length()
|
||||
<< fVolume->BlockShift()) / sizeof(block_run);
|
||||
|
||||
for (int32 indirectIndex = 0; indirectIndex < maxIndirectIndex;
|
||||
for (int32 indirectIndex = 0; indirectIndex < runsPerArray;
|
||||
indirectIndex++) {
|
||||
// get the indirect array block
|
||||
block_run* array = (block_run*)cached.SetTo(
|
||||
@@ -1812,7 +1810,7 @@ BlockAllocator::CheckInode(Inode* inode, check_control* control)
|
||||
return B_OK;
|
||||
|
||||
status = CheckBlockRun(indirect, "double indirect->runs", control);
|
||||
if (status < B_OK)
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
int32 maxIndex = (indirect.Length() << fVolume->BlockShift())
|
||||
@@ -1831,7 +1829,7 @@ BlockAllocator::CheckInode(Inode* inode, check_control* control)
|
||||
|
||||
status = CheckBlockRun(runs[index % runsPerBlock],
|
||||
"double indirect->runs->run", control);
|
||||
if (status < B_OK)
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
} while ((++index % runsPerArray) != 0);
|
||||
}
|
||||
|
||||
@@ -1327,13 +1327,14 @@ Inode::AllocatedSize() const
|
||||
if (data.MaxDoubleIndirectRange() != 0) {
|
||||
off_t doubleIndirectSize = data.MaxDoubleIndirectRange()
|
||||
- data.MaxIndirectRange();
|
||||
int32 indirectSize = (1L << (INDIRECT_BLOCKS_SHIFT
|
||||
+ fVolume->BlockShift())) * (blockSize / sizeof(block_run));
|
||||
int32 indirectSize = double_indirect_max_indirect_size(fVolume,
|
||||
data.double_indirect.Length());
|
||||
|
||||
size += (2 * NUM_ARRAY_BLOCKS + doubleIndirectSize / indirectSize)
|
||||
size += (2 * data.double_indirect.Length()
|
||||
+ doubleIndirectSize / indirectSize)
|
||||
* blockSize + data.MaxDoubleIndirectRange();
|
||||
} else if (data.MaxIndirectRange() != 0)
|
||||
size += NUM_ARRAY_BLOCKS + data.MaxIndirectRange();
|
||||
size += data.indirect.Length() + data.MaxIndirectRange();
|
||||
else
|
||||
size += data.MaxDirectRange();
|
||||
|
||||
@@ -1368,13 +1369,14 @@ Inode::FindBlockRun(off_t pos, block_run& run, off_t& offset)
|
||||
|
||||
CachedBlock cached(fVolume);
|
||||
|
||||
int32 runsPerBlock;
|
||||
int32 directSize;
|
||||
int32 indirectSize;
|
||||
get_double_indirect_sizes(fVolume, data->double_indirect.Length(),
|
||||
runsPerBlock, directSize, indirectSize);
|
||||
|
||||
off_t start = pos - data->MaxIndirectRange();
|
||||
int32 indirectSize = (1L << (INDIRECT_BLOCKS_SHIFT
|
||||
+ cached.BlockShift()))
|
||||
* (fVolume->BlockSize() / sizeof(block_run));
|
||||
int32 directSize = NUM_ARRAY_BLOCKS << cached.BlockShift();
|
||||
int32 index = start / indirectSize;
|
||||
int32 runsPerBlock = cached.BlockSize() / sizeof(block_run);
|
||||
|
||||
block_run* indirect = (block_run*)cached.SetTo(
|
||||
fVolume->ToBlock(data->double_indirect) + index / runsPerBlock);
|
||||
@@ -1390,6 +1392,9 @@ Inode::FindBlockRun(off_t pos, block_run& run, off_t& offset)
|
||||
RETURN_ERROR(B_ERROR);
|
||||
|
||||
run = indirect[current % runsPerBlock];
|
||||
if (run.Length() != data->double_indirect.Length())
|
||||
RETURN_ERROR(B_BAD_DATA);
|
||||
|
||||
offset = data->MaxIndirectRange() + (index * indirectSize)
|
||||
+ (current * directSize);
|
||||
} else {
|
||||
@@ -1572,19 +1577,19 @@ Inode::FillGapWithZeros(off_t pos, off_t newSize)
|
||||
}
|
||||
|
||||
|
||||
/*! Allocates NUM_ARRAY_BLOCKS blocks, and clears their contents. Growing
|
||||
/*! Allocates \a length blocks, and clears their contents. Growing
|
||||
the indirect and double indirect range uses this method.
|
||||
The allocated block_run is saved in "run"
|
||||
*/
|
||||
status_t
|
||||
Inode::_AllocateBlockArray(Transaction& transaction, block_run& run)
|
||||
Inode::_AllocateBlockArray(Transaction& transaction, block_run& run,
|
||||
size_t length)
|
||||
{
|
||||
if (!run.IsZero())
|
||||
return B_BAD_VALUE;
|
||||
|
||||
status_t status = fVolume->Allocate(transaction, this, NUM_ARRAY_BLOCKS,
|
||||
run, NUM_ARRAY_BLOCKS);
|
||||
if (status < B_OK)
|
||||
status_t status = fVolume->Allocate(transaction, this, length, run, length);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
// make sure those blocks are empty
|
||||
@@ -1624,9 +1629,9 @@ Inode::_GrowStream(Transaction& transaction, off_t size)
|
||||
off_t bytes;
|
||||
if (data->Size() < data->MaxDoubleIndirectRange()) {
|
||||
bytes = size - data->MaxDoubleIndirectRange();
|
||||
// The double indirect range can only handle multiple of
|
||||
// NUM_ARRAY_BLOCKS
|
||||
minimum = NUM_ARRAY_BLOCKS;
|
||||
// The double indirect range can only handle multiples of
|
||||
// its base length
|
||||
minimum = data->double_indirect.Length();
|
||||
} else if (data->Size() < data->MaxIndirectRange())
|
||||
bytes = size - data->MaxIndirectRange();
|
||||
else if (data->Size() < data->MaxDirectRange())
|
||||
@@ -1635,7 +1640,7 @@ Inode::_GrowStream(Transaction& transaction, off_t size)
|
||||
// no preallocation left to be used
|
||||
bytes = size - data->Size();
|
||||
if (data->MaxDoubleIndirectRange() > 0)
|
||||
minimum = NUM_ARRAY_BLOCKS;
|
||||
minimum = data->double_indirect.Length();
|
||||
}
|
||||
|
||||
// do we have enough free blocks on the disk?
|
||||
@@ -1748,7 +1753,8 @@ Inode::_GrowStream(Transaction& transaction, off_t size)
|
||||
|
||||
// if there is no indirect block yet, create one
|
||||
if (data->indirect.IsZero()) {
|
||||
status = _AllocateBlockArray(transaction, data->indirect);
|
||||
status = _AllocateBlockArray(transaction, data->indirect,
|
||||
NUM_ARRAY_BLOCKS);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
@@ -1803,14 +1809,20 @@ Inode::_GrowStream(Transaction& transaction, off_t size)
|
||||
|
||||
if (data->Size() <= data->MaxDoubleIndirectRange()
|
||||
|| !data->max_double_indirect_range) {
|
||||
if ((run.Length() % NUM_ARRAY_BLOCKS) != 0) {
|
||||
// The number of allocated blocks isn't a multiple of
|
||||
// NUM_ARRAY_BLOCKS, so we have to change this. This can happen
|
||||
// the first time the stream grows into the double
|
||||
// indirect range.
|
||||
// First, free the remaining blocks that don't fit into a
|
||||
// multiple of NUM_ARRAY_BLOCKS
|
||||
int32 rest = run.Length() % NUM_ARRAY_BLOCKS;
|
||||
// We make sure here that we have this minimum allocated, so if
|
||||
// the allocation succeeds, we don't run into an endless loop.
|
||||
if (!data->max_double_indirect_range)
|
||||
minimum = _DoubleIndirectBlockLength();
|
||||
else
|
||||
minimum = data->double_indirect.Length();
|
||||
|
||||
if ((run.Length() % minimum) != 0) {
|
||||
// The number of allocated blocks isn't a multiple of 'minimum',
|
||||
// so we have to change this. This can happen the first time the
|
||||
// stream grows into the double indirect range.
|
||||
// First, free the remaining blocks that don't fit into this
|
||||
// multiple.
|
||||
int32 rest = run.Length() % minimum;
|
||||
run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length() - rest);
|
||||
|
||||
status = fVolume->Free(transaction,
|
||||
@@ -1820,11 +1832,7 @@ Inode::_GrowStream(Transaction& transaction, off_t size)
|
||||
return status;
|
||||
|
||||
blocksNeeded += rest;
|
||||
blocksRequested = round_up(blocksNeeded, NUM_ARRAY_BLOCKS);
|
||||
minimum = NUM_ARRAY_BLOCKS;
|
||||
// we make sure here that we have at minimum
|
||||
// NUM_ARRAY_BLOCKS allocated, so if the allocation
|
||||
// succeeds, we don't run into an endless loop
|
||||
blocksRequested = round_up(blocksNeeded, minimum);
|
||||
|
||||
// Are there any blocks left in the run? If not, allocate
|
||||
// a new one
|
||||
@@ -1835,7 +1843,7 @@ Inode::_GrowStream(Transaction& transaction, off_t size)
|
||||
// if there is no double indirect block yet, create one
|
||||
if (data->double_indirect.IsZero()) {
|
||||
status = _AllocateBlockArray(transaction,
|
||||
data->double_indirect);
|
||||
data->double_indirect, _DoubleIndirectBlockLength());
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
@@ -1844,16 +1852,17 @@ Inode::_GrowStream(Transaction& transaction, off_t size)
|
||||
|
||||
// calculate the index where to insert the new blocks
|
||||
|
||||
int32 runsPerBlock = fVolume->BlockSize() / sizeof(block_run);
|
||||
int32 indirectSize = ((1L << INDIRECT_BLOCKS_SHIFT)
|
||||
<< fVolume->BlockShift()) * runsPerBlock;
|
||||
int32 directSize = NUM_ARRAY_BLOCKS << fVolume->BlockShift();
|
||||
int32 runsPerArray = runsPerBlock << ARRAY_BLOCKS_SHIFT;
|
||||
int32 runsPerBlock;
|
||||
int32 directSize;
|
||||
int32 indirectSize;
|
||||
get_double_indirect_sizes(fVolume, data->double_indirect.Length(),
|
||||
runsPerBlock, directSize, indirectSize);
|
||||
|
||||
off_t start = data->MaxDoubleIndirectRange()
|
||||
- data->MaxIndirectRange();
|
||||
int32 indirectIndex = start / indirectSize;
|
||||
int32 index = (start % indirectSize) / directSize;
|
||||
int32 runsPerArray = runsPerBlock * minimum;
|
||||
|
||||
// distribute the blocks to the array and allocate
|
||||
// new array blocks when needed
|
||||
@@ -1878,7 +1887,8 @@ Inode::_GrowStream(Transaction& transaction, off_t size)
|
||||
cached.MakeWritable(transaction);
|
||||
|
||||
status = _AllocateBlockArray(transaction,
|
||||
array[indirectIndex % runsPerBlock]);
|
||||
array[indirectIndex % runsPerBlock],
|
||||
data->double_indirect.Length());
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
}
|
||||
@@ -1929,16 +1939,28 @@ Inode::_GrowStream(Transaction& transaction, off_t size)
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
Inode::_DoubleIndirectBlockLength() const
|
||||
{
|
||||
if (fVolume->BlockSize() > DOUBLE_INDIRECT_ARRAY_SIZE)
|
||||
return 1;
|
||||
|
||||
return DOUBLE_INDIRECT_ARRAY_SIZE / fVolume->BlockSize();
|
||||
}
|
||||
|
||||
|
||||
/*! Frees the statically sized array of the double indirect part of a data
|
||||
stream.
|
||||
*/
|
||||
status_t
|
||||
Inode::_FreeStaticStreamArray(Transaction& transaction, int32 level,
|
||||
block_run run, off_t size, off_t offset, off_t& max)
|
||||
{
|
||||
int32 indirectSize = 0;
|
||||
int32 indirectSize;
|
||||
if (level == 0)
|
||||
indirectSize = (1L << (INDIRECT_BLOCKS_SHIFT + fVolume->BlockShift()))
|
||||
* (fVolume->BlockSize() / sizeof(block_run));
|
||||
else if (level == 1)
|
||||
indirectSize = 4 << fVolume->BlockShift();
|
||||
indirectSize = double_indirect_max_indirect_size(fVolume, run.Length());
|
||||
else
|
||||
indirectSize = double_indirect_max_direct_size(fVolume, run.Length());
|
||||
|
||||
off_t start;
|
||||
if (size > offset)
|
||||
@@ -2061,8 +2083,8 @@ Inode::_ShrinkStream(Transaction& transaction, off_t size)
|
||||
// gcc 4 work-around: "error: cannot bind packed field
|
||||
// 'data->data_stream::max_double_indirect_range' to 'off_t&'"
|
||||
status = _FreeStaticStreamArray(transaction, 0, data->double_indirect,
|
||||
size, data->MaxIndirectRange(),* maxDoubleIndirect);
|
||||
if (status < B_OK)
|
||||
size, data->MaxIndirectRange(), *maxDoubleIndirect);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
if (size <= data->MaxIndirectRange()) {
|
||||
|
||||
@@ -212,6 +212,7 @@ private:
|
||||
void _AddIterator(AttributeIterator* iterator);
|
||||
void _RemoveIterator(AttributeIterator* iterator);
|
||||
|
||||
size_t _DoubleIndirectBlockLength() const;
|
||||
status_t _FreeStaticStreamArray(Transaction& transaction,
|
||||
int32 level, block_run run, off_t size,
|
||||
off_t offset, off_t& max);
|
||||
@@ -219,7 +220,7 @@ private:
|
||||
block_run* array, uint32 arrayLength,
|
||||
off_t size, off_t& offset, off_t& max);
|
||||
status_t _AllocateBlockArray(Transaction& transaction,
|
||||
block_run& run);
|
||||
block_run& run, size_t length);
|
||||
status_t _GrowStream(Transaction& transaction, off_t size);
|
||||
status_t _ShrinkStream(Transaction& transaction, off_t size);
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#include "system_dependencies.h"
|
||||
|
||||
#include "Volume.h"
|
||||
|
||||
|
||||
enum inode_type {
|
||||
S_DIRECTORY = S_IFDIR,
|
||||
@@ -93,6 +95,38 @@ get_shift(uint64 i)
|
||||
}
|
||||
|
||||
|
||||
inline int32
|
||||
runs_per_block(Volume* volume)
|
||||
{
|
||||
return volume->BlockSize() / sizeof(block_run);
|
||||
}
|
||||
|
||||
|
||||
inline int32
|
||||
double_indirect_max_direct_size(Volume* volume, uint32 baseLength)
|
||||
{
|
||||
return baseLength << volume->BlockShift();
|
||||
}
|
||||
|
||||
|
||||
inline int32
|
||||
double_indirect_max_indirect_size(Volume* volume, uint32 baseLength)
|
||||
{
|
||||
return baseLength * double_indirect_max_direct_size(volume, baseLength)
|
||||
* runs_per_block(volume);
|
||||
}
|
||||
|
||||
|
||||
inline void
|
||||
get_double_indirect_sizes(Volume* volume, uint32 baseLength,
|
||||
int32& runsPerBlock, int32& directSize, int32& indirectSize)
|
||||
{
|
||||
runsPerBlock = runs_per_block(volume);
|
||||
directSize = double_indirect_max_direct_size(volume, baseLength);
|
||||
indirectSize = baseLength * directSize * runsPerBlock;
|
||||
}
|
||||
|
||||
|
||||
inline uint32
|
||||
key_align(uint32 data)
|
||||
{
|
||||
|
||||
@@ -135,11 +135,9 @@ struct data_stream {
|
||||
} _PACKED;
|
||||
|
||||
// This defines the size of the indirect and double indirect
|
||||
// blocks. Note: the code may not work correctly at some places
|
||||
// if this value is changed (it's not tested).
|
||||
#define NUM_ARRAY_BLOCKS 4
|
||||
#define ARRAY_BLOCKS_SHIFT 2
|
||||
#define INDIRECT_BLOCKS_SHIFT (ARRAY_BLOCKS_SHIFT + ARRAY_BLOCKS_SHIFT)
|
||||
// blocks.
|
||||
#define NUM_ARRAY_BLOCKS 4
|
||||
#define DOUBLE_INDIRECT_ARRAY_SIZE 4096
|
||||
|
||||
//**************************************
|
||||
|
||||
|
||||
Reference in New Issue
Block a user