ext2: ExtentStream::FindBlock(): Restructure/fix

* The binary search was somewhat broken.
* Restructure a bit so that only the binary/linear search itself is
  handled separately. The handling afterwards is common again.
* Fix sparse block handling:
  - There can be sparse blocks before the first extend.
  - Set the return parameter _count in this case as well.
  - Set the return parameter block to 0 instead of 0xffffffff. That's
    what ext2_get_file_map() expects.

Fixes an infinite loop in ext2_get_file_map() when sparse blocks are
involved, due to previously returning a wrong block and a 0 count.
Ticket #9274 may be related.
This commit is contained in:
Ingo Weinhold
2013-11-11 22:27:54 +01:00
parent 24d0e21f51
commit fd91d2cbe1
@@ -77,60 +77,71 @@ ExtentStream::FindBlock(off_t offset, fsblock_t& block, uint32 *_count)
panic("ExtentStream::FindBlock() invalid header\n"); panic("ExtentStream::FindBlock() invalid header\n");
} }
// find the extend following the one that should contain the logical block
int32 extentIndex;
if (stream->extent_header.NumEntries() > 7) { if (stream->extent_header.NumEntries() > 7) {
// binary search when enough entries // binary search when enough entries
int32 low = 0; int32 low = 0;
int32 high = stream->extent_header.NumEntries() - 1; int32 high = stream->extent_header.NumEntries() - 1;
int32 middle = 0; while (low < high) {
while (low <= high) { int32 middle = (high + low + 1) / 2;
middle = (high + low) >> 1; if (stream->extent_entries[middle].LogicalBlock() > index)
if (stream->extent_entries[middle].LogicalBlock() == index)
break;
if (stream->extent_entries[middle].LogicalBlock() < index)
low = middle + 1;
else
high = middle - 1; high = middle - 1;
} else
if (stream->extent_entries[middle].LogicalBlock() > index) low = middle;
middle--;
fileblock_t diff = index
- stream->extent_entries[middle].LogicalBlock();
if (diff > stream->extent_entries[middle].Length()) {
// sparse block
TRACE("FindBlock() sparse block index %" B_PRIu64 " at %" B_PRIu32
"\n", index, stream->extent_entries[middle].LogicalBlock());
block = 0xffffffff;
return B_OK;
} }
block = stream->extent_entries[middle].PhysicalBlock() + diff; extentIndex = low + 1;
if (_count) } else {
*_count = stream->extent_entries[middle].Length() - diff; extentIndex = stream->extent_header.NumEntries();
TRACE("FindBlock(offset %" B_PRIdOFF "): %" B_PRIu64 " %" B_PRIu32 for (int32 i = 0; i < stream->extent_header.NumEntries(); i++) {
"\n", offset, block, _count != NULL ? *_count : 1); if (stream->extent_entries[i].LogicalBlock() > index) {
extentIndex = i;
break;
}
}
}
fileblock_t logicalEndIndex
= (fSize + fVolume->BlockSize() - 1) >> fVolume->BlockShift();
if (extentIndex == 0) {
// sparse block at the beginning of the stream
block = 0;
if (_count != NULL) {
*_count = stream->extent_header.NumEntries() == 0
? logicalEndIndex - index
: stream->extent_entries[0].LogicalBlock() - index;
}
TRACE("FindBlock() sparse block index %" B_PRIu64 " at beginning of "
"stream\n", index);
return B_OK; return B_OK;
} }
for (int32 i = 0; i < stream->extent_header.NumEntries(); i++) { const ext2_extent_entry& extent = stream->extent_entries[extentIndex - 1];
if (stream->extent_entries[i].LogicalBlock() > index) { // the extent supposedly containing the offset
// sparse block fileblock_t diff = index - extent.LogicalBlock();
TRACE("FindBlock() sparse block index %" B_PRIu64 " at %" B_PRIu32 if (diff >= extent.Length()) {
"\n", index, stream->extent_entries[i].LogicalBlock()); // sparse block between extends or at the end of the stream
block = 0xffffffff; TRACE("FindBlock() sparse block index %" B_PRIu64 " at %" B_PRIu32
return B_OK; "\n", index, extent.LogicalBlock());
} block = 0;
fileblock_t diff = index - stream->extent_entries[i].LogicalBlock(); if (_count != NULL) {
if (diff < stream->extent_entries[i].Length()) { *_count = stream->extent_header.NumEntries() == extentIndex
block = stream->extent_entries[i].PhysicalBlock() + diff; ? logicalEndIndex - index
if (_count) : stream->extent_entries[extentIndex].LogicalBlock() - index;
*_count = stream->extent_entries[i].Length() - diff;
TRACE("FindBlock(offset %" B_PRIdOFF "): %" B_PRIu64 " %" B_PRIu32
"\n", offset, block, _count != NULL ? *_count : 1);
return B_OK;
} }
return B_OK;
} }
return B_ERROR; block = extent.PhysicalBlock() + diff;
if (_count != NULL)
*_count = extent.Length() - diff;
TRACE("FindBlock(offset %" B_PRIdOFF "): %" B_PRIu64 " %" B_PRIu32
"\n", offset, block, _count != NULL ? *_count : 1);
return B_OK;
} }