Fixed a number of issues with double indirect blocks:
* The "index" was not correctly updated when the next indirect block was about to be used, which lead the code to write the runs to a wrong block, causing slight corruption, as well as the known invalid block_run(0,0,0) problem. * blocksRequested is now always a multiple of "minimum" as well. * When the file size grew beyond the max_double_indirect_range, the minimum was not adjusted, leading to too many extra allocations that had to be reverted afterwards again. * If an allocation was not a multiple of NUM_ARRAY_BLOCKS, but also smaller than this limit (could happen due to the bug above), an endless loop could be entered. This was actually a regression introduced in r974. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@31767 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -1633,8 +1633,12 @@ Inode::_GrowStream(Transaction& transaction, off_t size)
|
|||||||
bytes = size - data->MaxIndirectRange();
|
bytes = size - data->MaxIndirectRange();
|
||||||
else if (data->Size() < data->MaxDirectRange())
|
else if (data->Size() < data->MaxDirectRange())
|
||||||
bytes = size - data->MaxDirectRange();
|
bytes = size - data->MaxDirectRange();
|
||||||
else
|
else {
|
||||||
|
// no preallocation left to be used
|
||||||
bytes = size - data->Size();
|
bytes = size - data->Size();
|
||||||
|
if (data->MaxDoubleIndirectRange() > 0)
|
||||||
|
minimum = NUM_ARRAY_BLOCKS;
|
||||||
|
}
|
||||||
|
|
||||||
// do we have enough free blocks on the disk?
|
// do we have enough free blocks on the disk?
|
||||||
off_t blocksNeeded = (bytes + fVolume->BlockSize() - 1)
|
off_t blocksNeeded = (bytes + fVolume->BlockSize() - 1)
|
||||||
@@ -1687,10 +1691,15 @@ Inode::_GrowStream(Transaction& transaction, off_t size)
|
|||||||
// the requested blocks do not need to be returned with a
|
// the requested blocks do not need to be returned with a
|
||||||
// single allocation, so we need to iterate until we have
|
// single allocation, so we need to iterate until we have
|
||||||
// enough blocks allocated
|
// enough blocks allocated
|
||||||
|
if (minimum > 1) {
|
||||||
|
// make sure that "blocks" is a multiple of minimum
|
||||||
|
blocksRequested = round_up(blocksRequested, minimum);
|
||||||
|
}
|
||||||
|
|
||||||
block_run run;
|
block_run run;
|
||||||
status_t status = fVolume->Allocate(transaction, this, blocksRequested,
|
status_t status = fVolume->Allocate(transaction, this, blocksRequested,
|
||||||
run, minimum);
|
run, minimum);
|
||||||
if (status < B_OK)
|
if (status != B_OK)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
// okay, we have the needed blocks, so just distribute them to the
|
// okay, we have the needed blocks, so just distribute them to the
|
||||||
@@ -1702,10 +1711,6 @@ Inode::_GrowStream(Transaction& transaction, off_t size)
|
|||||||
blocksNeeded -= run.Length();
|
blocksNeeded -= run.Length();
|
||||||
// don't preallocate if the first allocation was already too small
|
// don't preallocate if the first allocation was already too small
|
||||||
blocksRequested = blocksNeeded;
|
blocksRequested = blocksNeeded;
|
||||||
if (minimum > 1) {
|
|
||||||
// make sure that "blocks" is a multiple of minimum
|
|
||||||
blocksRequested = round_up(blocksRequested, minimum);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Direct block range
|
// Direct block range
|
||||||
|
|
||||||
@@ -1746,7 +1751,7 @@ Inode::_GrowStream(Transaction& transaction, off_t size)
|
|||||||
// if there is no indirect block yet, create one
|
// if there is no indirect block yet, create one
|
||||||
if (data->indirect.IsZero()) {
|
if (data->indirect.IsZero()) {
|
||||||
status = _AllocateBlockArray(transaction, data->indirect);
|
status = _AllocateBlockArray(transaction, data->indirect);
|
||||||
if (status < B_OK)
|
if (status != B_OK)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
data->max_indirect_range = HOST_ENDIAN_TO_BFS_INT64(
|
data->max_indirect_range = HOST_ENDIAN_TO_BFS_INT64(
|
||||||
@@ -1800,7 +1805,7 @@ Inode::_GrowStream(Transaction& transaction, off_t size)
|
|||||||
|
|
||||||
if (data->Size() <= data->MaxDoubleIndirectRange()
|
if (data->Size() <= data->MaxDoubleIndirectRange()
|
||||||
|| !data->max_double_indirect_range) {
|
|| !data->max_double_indirect_range) {
|
||||||
while ((run.Length() % NUM_ARRAY_BLOCKS) != 0) {
|
if ((run.Length() % NUM_ARRAY_BLOCKS) != 0) {
|
||||||
// The number of allocated blocks isn't a multiple of
|
// The number of allocated blocks isn't a multiple of
|
||||||
// NUM_ARRAY_BLOCKS, so we have to change this. This can happen
|
// NUM_ARRAY_BLOCKS, so we have to change this. This can happen
|
||||||
// the first time the stream grows into the double
|
// the first time the stream grows into the double
|
||||||
@@ -1813,7 +1818,7 @@ Inode::_GrowStream(Transaction& transaction, off_t size)
|
|||||||
status = fVolume->Free(transaction,
|
status = fVolume->Free(transaction,
|
||||||
block_run::Run(run.AllocationGroup(),
|
block_run::Run(run.AllocationGroup(),
|
||||||
run.Start() + run.Length(), rest));
|
run.Start() + run.Length(), rest));
|
||||||
if (status < B_OK)
|
if (status != B_OK)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
blocksNeeded += rest;
|
blocksNeeded += rest;
|
||||||
@@ -1833,7 +1838,7 @@ Inode::_GrowStream(Transaction& transaction, off_t size)
|
|||||||
if (data->double_indirect.IsZero()) {
|
if (data->double_indirect.IsZero()) {
|
||||||
status = _AllocateBlockArray(transaction,
|
status = _AllocateBlockArray(transaction,
|
||||||
data->double_indirect);
|
data->double_indirect);
|
||||||
if (status < B_OK)
|
if (status != B_OK)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
data->max_double_indirect_range = data->max_indirect_range;
|
data->max_double_indirect_range = data->max_indirect_range;
|
||||||
@@ -1876,7 +1881,7 @@ Inode::_GrowStream(Transaction& transaction, off_t size)
|
|||||||
|
|
||||||
status = _AllocateBlockArray(transaction,
|
status = _AllocateBlockArray(transaction,
|
||||||
array[indirectIndex % runsPerBlock]);
|
array[indirectIndex % runsPerBlock]);
|
||||||
if (status < B_OK)
|
if (status != B_OK)
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1900,6 +1905,8 @@ Inode::_GrowStream(Transaction& transaction, off_t size)
|
|||||||
} while ((++index % runsPerBlock) != 0 && run.length);
|
} while ((++index % runsPerBlock) != 0 && run.length);
|
||||||
} while ((index % runsPerArray) != 0 && run.length);
|
} while ((index % runsPerArray) != 0 && run.length);
|
||||||
|
|
||||||
|
if (index == runsPerArray)
|
||||||
|
index = 0;
|
||||||
if (++indirectIndex % runsPerBlock == 0) {
|
if (++indirectIndex % runsPerBlock == 0) {
|
||||||
array = NULL;
|
array = NULL;
|
||||||
index = 0;
|
index = 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user