|
|
@@ -6,14 +6,13 @@
|
|
|
|
//! block bitmap handling and allocation policies
|
|
|
|
//! block bitmap handling and allocation policies
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "Debug.h"
|
|
|
|
|
|
|
|
#include "BlockAllocator.h"
|
|
|
|
#include "BlockAllocator.h"
|
|
|
|
#include "Volume.h"
|
|
|
|
|
|
|
|
#include "Inode.h"
|
|
|
|
|
|
|
|
#include "BPlusTree.h"
|
|
|
|
|
|
|
|
#include "bfs_control.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "system_dependencies.h"
|
|
|
|
#include "bfs_control.h"
|
|
|
|
|
|
|
|
#include "BPlusTree.h"
|
|
|
|
|
|
|
|
#include "Debug.h"
|
|
|
|
|
|
|
|
#include "Inode.h"
|
|
|
|
|
|
|
|
#include "Volume.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Things the BlockAllocator should do:
|
|
|
|
// Things the BlockAllocator should do:
|
|
|
@@ -150,7 +149,8 @@ class AllocationBlock : public CachedBlock {
|
|
|
|
inline bool IsUsed(uint16 block);
|
|
|
|
inline bool IsUsed(uint16 block);
|
|
|
|
|
|
|
|
|
|
|
|
status_t SetTo(AllocationGroup& group, uint16 block);
|
|
|
|
status_t SetTo(AllocationGroup& group, uint16 block);
|
|
|
|
status_t SetToWritable(Transaction &transaction, AllocationGroup &group, uint16 block);
|
|
|
|
status_t SetToWritable(Transaction& transaction, AllocationGroup& group,
|
|
|
|
|
|
|
|
uint16 block);
|
|
|
|
|
|
|
|
|
|
|
|
uint32 NumBlockBits() const { return fNumBits; }
|
|
|
|
uint32 NumBlockBits() const { return fNumBits; }
|
|
|
|
uint32& Block(int32 index) { return ((uint32*)fBlock)[index]; }
|
|
|
|
uint32& Block(int32 index) { return ((uint32*)fBlock)[index]; }
|
|
|
@@ -185,7 +185,8 @@ class AllocationGroup {
|
|
|
|
uint32 fNumBlocks;
|
|
|
|
uint32 fNumBlocks;
|
|
|
|
int32 fStart;
|
|
|
|
int32 fStart;
|
|
|
|
int32 fFirstFree, fLargest, fLargestFirst;
|
|
|
|
int32 fFirstFree, fLargest, fLargestFirst;
|
|
|
|
// ToDo: fLargest & fLargestFirst are not maintained (and therefore used) yet!
|
|
|
|
// TODO: fLargest & fLargestFirst are not maintained
|
|
|
|
|
|
|
|
// (and therefore used) yet!
|
|
|
|
int32 fFreeBits;
|
|
|
|
int32 fFreeBits;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
@@ -354,7 +355,7 @@ AllocationGroup::Allocate(Transaction &transaction, uint16 start, int32 length)
|
|
|
|
return B_ERROR;
|
|
|
|
return B_ERROR;
|
|
|
|
|
|
|
|
|
|
|
|
// Update the allocation group info
|
|
|
|
// Update the allocation group info
|
|
|
|
// ToDo: this info will be incorrect if something goes wrong later
|
|
|
|
// TODO: this info will be incorrect if something goes wrong later
|
|
|
|
// Note, the fFirstFree block doesn't have to be really free
|
|
|
|
// Note, the fFirstFree block doesn't have to be really free
|
|
|
|
if (start == fFirstFree)
|
|
|
|
if (start == fFirstFree)
|
|
|
|
fFirstFree = start + length;
|
|
|
|
fFirstFree = start + length;
|
|
|
@@ -401,7 +402,7 @@ AllocationGroup::Free(Transaction &transaction, uint16 start, int32 length)
|
|
|
|
return B_ERROR;
|
|
|
|
return B_ERROR;
|
|
|
|
|
|
|
|
|
|
|
|
// Update the allocation group info
|
|
|
|
// Update the allocation group info
|
|
|
|
// ToDo: this info will be incorrect if something goes wrong later
|
|
|
|
// TODO: this info will be incorrect if something goes wrong later
|
|
|
|
if (fFirstFree > start)
|
|
|
|
if (fFirstFree > start)
|
|
|
|
fFirstFree = start;
|
|
|
|
fFirstFree = start;
|
|
|
|
fFreeBits += length;
|
|
|
|
fFreeBits += length;
|
|
|
@@ -471,10 +472,10 @@ BlockAllocator::Initialize(bool full)
|
|
|
|
// the lock will be released by the _Initialize() method
|
|
|
|
// the lock will be released by the _Initialize() method
|
|
|
|
|
|
|
|
|
|
|
|
thread_id id = spawn_kernel_thread((thread_func)BlockAllocator::_Initialize,
|
|
|
|
thread_id id = spawn_kernel_thread((thread_func)BlockAllocator::_Initialize,
|
|
|
|
"bfs block allocator", B_LOW_PRIORITY, (void *)this);
|
|
|
|
"bfs block allocator", B_LOW_PRIORITY, this);
|
|
|
|
if (id < B_OK)
|
|
|
|
if (id < B_OK)
|
|
|
|
return _Initialize(this);
|
|
|
|
return _Initialize(this);
|
|
|
|
else
|
|
|
|
|
|
|
|
mutex_transfer_lock(&fLock, id);
|
|
|
|
mutex_transfer_lock(&fLock, id);
|
|
|
|
|
|
|
|
|
|
|
|
return resume_thread(id);
|
|
|
|
return resume_thread(id);
|
|
|
@@ -503,13 +504,15 @@ BlockAllocator::InitializeAndClearBitmap(Transaction &transaction)
|
|
|
|
// initialize the AllocationGroup objects and clear the on-disk bitmap
|
|
|
|
// initialize the AllocationGroup objects and clear the on-disk bitmap
|
|
|
|
|
|
|
|
|
|
|
|
for (int32 i = 0; i < fNumGroups; i++) {
|
|
|
|
for (int32 i = 0; i < fNumGroups; i++) {
|
|
|
|
if (write_pos(fVolume->Device(), offset << blockShift, buffer, blocks << blockShift) < B_OK)
|
|
|
|
if (write_pos(fVolume->Device(), offset << blockShift, buffer,
|
|
|
|
|
|
|
|
blocks << blockShift) < B_OK)
|
|
|
|
return B_ERROR;
|
|
|
|
return B_ERROR;
|
|
|
|
|
|
|
|
|
|
|
|
// the last allocation group may contain less blocks than the others
|
|
|
|
// the last allocation group may contain less blocks than the others
|
|
|
|
if (i == fNumGroups - 1) {
|
|
|
|
if (i == fNumGroups - 1) {
|
|
|
|
fGroups[i].fNumBits = fVolume->NumBlocks() - i * numBits;
|
|
|
|
fGroups[i].fNumBits = fVolume->NumBlocks() - i * numBits;
|
|
|
|
fGroups[i].fNumBlocks = 1 + ((fGroups[i].NumBits() - 1) >> (blockShift + 3));
|
|
|
|
fGroups[i].fNumBlocks = 1 + ((fGroups[i].NumBits() - 1)
|
|
|
|
|
|
|
|
>> (blockShift + 3));
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
fGroups[i].fNumBits = numBits;
|
|
|
|
fGroups[i].fNumBits = numBits;
|
|
|
|
fGroups[i].fNumBlocks = blocks;
|
|
|
|
fGroups[i].fNumBlocks = blocks;
|
|
|
@@ -529,7 +532,8 @@ BlockAllocator::InitializeAndClearBitmap(Transaction &transaction)
|
|
|
|
FATAL(("could not allocate reserved space for block bitmap/log!\n"));
|
|
|
|
FATAL(("could not allocate reserved space for block bitmap/log!\n"));
|
|
|
|
return B_ERROR;
|
|
|
|
return B_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fVolume->SuperBlock().used_blocks = HOST_ENDIAN_TO_BFS_INT64(reservedBlocks);
|
|
|
|
fVolume->SuperBlock().used_blocks
|
|
|
|
|
|
|
|
= HOST_ENDIAN_TO_BFS_INT64(reservedBlocks);
|
|
|
|
|
|
|
|
|
|
|
|
return B_OK;
|
|
|
|
return B_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
@@ -564,7 +568,8 @@ BlockAllocator::_Initialize(BlockAllocator *allocator)
|
|
|
|
// the last allocation group may contain less blocks than the others
|
|
|
|
// the last allocation group may contain less blocks than the others
|
|
|
|
if (i == numGroups - 1) {
|
|
|
|
if (i == numGroups - 1) {
|
|
|
|
groups[i].fNumBits = volume->NumBlocks() - i * bitsPerGroup;
|
|
|
|
groups[i].fNumBits = volume->NumBlocks() - i * bitsPerGroup;
|
|
|
|
groups[i].fNumBlocks = 1 + ((groups[i].NumBits() - 1) >> (blockShift + 3));
|
|
|
|
groups[i].fNumBlocks = 1 + ((groups[i].NumBits() - 1)
|
|
|
|
|
|
|
|
>> (blockShift + 3));
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
groups[i].fNumBits = bitsPerGroup;
|
|
|
|
groups[i].fNumBits = bitsPerGroup;
|
|
|
|
groups[i].fNumBlocks = blocks;
|
|
|
|
groups[i].fNumBlocks = blocks;
|
|
|
@@ -670,7 +675,8 @@ BlockAllocator::AllocateBlocks(Transaction &transaction, int32 group,
|
|
|
|
|
|
|
|
|
|
|
|
// The wanted maximum is smaller than the largest free block in the
|
|
|
|
// The wanted maximum is smaller than the largest free block in the
|
|
|
|
// group or already smaller than the minimum
|
|
|
|
// group or already smaller than the minimum
|
|
|
|
// ToDo: disabled because it's currently not maintained after the first allocation
|
|
|
|
// TODO: disabled because it's currently not maintained after the first
|
|
|
|
|
|
|
|
// allocation
|
|
|
|
//if (numBlocks > fGroups[group].fLargest)
|
|
|
|
//if (numBlocks > fGroups[group].fLargest)
|
|
|
|
// continue;
|
|
|
|
// continue;
|
|
|
|
|
|
|
|
|
|
|
@@ -688,7 +694,8 @@ BlockAllocator::AllocateBlocks(Transaction &transaction, int32 group,
|
|
|
|
if (cached.SetTo(fGroups[group], block) < B_OK)
|
|
|
|
if (cached.SetTo(fGroups[group], block) < B_OK)
|
|
|
|
RETURN_ERROR(B_ERROR);
|
|
|
|
RETURN_ERROR(B_ERROR);
|
|
|
|
|
|
|
|
|
|
|
|
T(Block("alloc-in", fGroups[group].Start() + block, cached.Block(), fVolume->BlockSize(), group, rangeStart));
|
|
|
|
T(Block("alloc-in", fGroups[group].Start() + block, cached.Block(),
|
|
|
|
|
|
|
|
fVolume->BlockSize(), group, rangeStart));
|
|
|
|
|
|
|
|
|
|
|
|
// find a block large enough to hold the allocation
|
|
|
|
// find a block large enough to hold the allocation
|
|
|
|
for (uint32 bit = start % bitsPerFullBlock;
|
|
|
|
for (uint32 bit = start % bitsPerFullBlock;
|
|
|
@@ -703,7 +710,8 @@ BlockAllocator::AllocateBlocks(Transaction &transaction, int32 group,
|
|
|
|
if (++range >= maximum)
|
|
|
|
if (++range >= maximum)
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
} else if (i >= fNumGroups && range >= minimum) {
|
|
|
|
} else if (i >= fNumGroups && range >= minimum) {
|
|
|
|
// we have found a block larger than the required minimum (second pass)
|
|
|
|
// we have found a block larger than the required minimum
|
|
|
|
|
|
|
|
// (second pass)
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
// end of a range
|
|
|
|
// end of a range
|
|
|
@@ -711,18 +719,19 @@ BlockAllocator::AllocateBlocks(Transaction &transaction, int32 group,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ToDo: we could also remember a "largest free block that fits the minimal
|
|
|
|
// TODO: we could also remember a "largest free block that fits the
|
|
|
|
// requirement" in the group, and use that - this would avoid the need
|
|
|
|
// minimal requirement" in the group, and use that - this would
|
|
|
|
// for a second run
|
|
|
|
// avoid the need for a second run
|
|
|
|
|
|
|
|
|
|
|
|
// if we found a suitable block, mark the blocks as in use, and write
|
|
|
|
// if we found a suitable block, mark the blocks as in use, and
|
|
|
|
// the updated block bitmap back to disk
|
|
|
|
// write the updated block bitmap back to disk
|
|
|
|
if (range >= numBlocks) {
|
|
|
|
if (range >= numBlocks) {
|
|
|
|
// adjust allocation size
|
|
|
|
// adjust allocation size
|
|
|
|
if (numBlocks < maximum)
|
|
|
|
if (numBlocks < maximum)
|
|
|
|
numBlocks = range;
|
|
|
|
numBlocks = range;
|
|
|
|
|
|
|
|
|
|
|
|
if (fGroups[group].Allocate(transaction, rangeStart, numBlocks) < B_OK)
|
|
|
|
if (fGroups[group].Allocate(transaction, rangeStart, numBlocks)
|
|
|
|
|
|
|
|
< B_OK)
|
|
|
|
RETURN_ERROR(B_IO_ERROR);
|
|
|
|
RETURN_ERROR(B_IO_ERROR);
|
|
|
|
|
|
|
|
|
|
|
|
run.allocation_group = HOST_ENDIAN_TO_BFS_INT32(group);
|
|
|
|
run.allocation_group = HOST_ENDIAN_TO_BFS_INT32(group);
|
|
|
@@ -755,12 +764,13 @@ status_t
|
|
|
|
BlockAllocator::AllocateForInode(Transaction& transaction,
|
|
|
|
BlockAllocator::AllocateForInode(Transaction& transaction,
|
|
|
|
const block_run* parent, mode_t type, block_run& run)
|
|
|
|
const block_run* parent, mode_t type, block_run& run)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// apply some allocation policies here (AllocateBlocks() will break them
|
|
|
|
// Apply some allocation policies here (AllocateBlocks() will break them
|
|
|
|
// if necessary) - we will start with those described in Dominic Giampaolo's
|
|
|
|
// if necessary) - we will start with those described in Dominic Giampaolo's
|
|
|
|
// "Practical File System Design", and see how good they work
|
|
|
|
// "Practical File System Design", and see how good they work
|
|
|
|
|
|
|
|
|
|
|
|
// files are going in the same allocation group as its parent, sub-directories
|
|
|
|
// Files are going in the same allocation group as its parent,
|
|
|
|
// will be inserted 8 allocation groups after the one of the parent
|
|
|
|
// sub-directories will be inserted 8 allocation groups after
|
|
|
|
|
|
|
|
// the one of the parent
|
|
|
|
uint16 group = parent->AllocationGroup();
|
|
|
|
uint16 group = parent->AllocationGroup();
|
|
|
|
if ((type & (S_DIRECTORY | S_INDEX_DIR | S_ATTR_DIR)) == S_DIRECTORY)
|
|
|
|
if ((type & (S_DIRECTORY | S_INDEX_DIR | S_ATTR_DIR)) == S_DIRECTORY)
|
|
|
|
group += 8;
|
|
|
|
group += 8;
|
|
|
@@ -770,8 +780,8 @@ BlockAllocator::AllocateForInode(Transaction &transaction,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
status_t
|
|
|
|
status_t
|
|
|
|
BlockAllocator::Allocate(Transaction &transaction, Inode *inode, off_t numBlocks,
|
|
|
|
BlockAllocator::Allocate(Transaction& transaction, Inode* inode,
|
|
|
|
block_run &run, uint16 minimum)
|
|
|
|
off_t numBlocks, block_run& run, uint16 minimum)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (numBlocks <= 0)
|
|
|
|
if (numBlocks <= 0)
|
|
|
|
return B_ERROR;
|
|
|
|
return B_ERROR;
|
|
|
@@ -782,26 +792,27 @@ BlockAllocator::Allocate(Transaction &transaction, Inode *inode, off_t numBlocks
|
|
|
|
|
|
|
|
|
|
|
|
// since block_run.length is uint16, the largest number of blocks that
|
|
|
|
// since block_run.length is uint16, the largest number of blocks that
|
|
|
|
// can be covered by a block_run is 65535
|
|
|
|
// can be covered by a block_run is 65535
|
|
|
|
// ToDo: if we drop compatibility, couldn't we do this any better?
|
|
|
|
// TODO: if we drop compatibility, couldn't we do this any better?
|
|
|
|
// There are basically two possibilities:
|
|
|
|
// There are basically two possibilities:
|
|
|
|
// a) since a length of zero doesn't have any sense, take that for 65536 -
|
|
|
|
// a) since a length of zero doesn't have any sense, take that for 65536 -
|
|
|
|
// but that could cause many problems (bugs) in other areas
|
|
|
|
// but that could cause many problems (bugs) in other areas
|
|
|
|
// b) reduce the maximum amount of blocks per block_run, so that the remaining
|
|
|
|
// b) reduce the maximum amount of blocks per block_run, so that the
|
|
|
|
// number of free blocks can be used in a useful manner (like 4 blocks) -
|
|
|
|
// remaining number of free blocks can be used in a useful manner
|
|
|
|
// but that would also reduce the maximum file size
|
|
|
|
// (like 4 blocks) - but that would also reduce the maximum file size
|
|
|
|
// c) have BlockRun::Length() return (length + 1).
|
|
|
|
// c) have BlockRun::Length() return (length + 1).
|
|
|
|
if (numBlocks > MAX_BLOCK_RUN_LENGTH)
|
|
|
|
if (numBlocks > MAX_BLOCK_RUN_LENGTH)
|
|
|
|
numBlocks = MAX_BLOCK_RUN_LENGTH;
|
|
|
|
numBlocks = MAX_BLOCK_RUN_LENGTH;
|
|
|
|
|
|
|
|
|
|
|
|
// apply some allocation policies here (AllocateBlocks() will break them
|
|
|
|
// Apply some allocation policies here (AllocateBlocks() will break them
|
|
|
|
// if necessary)
|
|
|
|
// if necessary)
|
|
|
|
uint16 group = inode->BlockRun().AllocationGroup();
|
|
|
|
uint16 group = inode->BlockRun().AllocationGroup();
|
|
|
|
uint16 start = 0;
|
|
|
|
uint16 start = 0;
|
|
|
|
|
|
|
|
|
|
|
|
// are there already allocated blocks? (then just try to allocate near the last one)
|
|
|
|
// Are there already allocated blocks? (then just try to allocate near the
|
|
|
|
|
|
|
|
// last one)
|
|
|
|
if (inode->Size() > 0) {
|
|
|
|
if (inode->Size() > 0) {
|
|
|
|
const data_stream& data = inode->Node().data;
|
|
|
|
const data_stream& data = inode->Node().data;
|
|
|
|
// ToDo: we currently don't care for when the data stream
|
|
|
|
// TODO: we currently don't care for when the data stream
|
|
|
|
// is already grown into the indirect ranges
|
|
|
|
// is already grown into the indirect ranges
|
|
|
|
if (data.max_double_indirect_range == 0
|
|
|
|
if (data.max_double_indirect_range == 0
|
|
|
|
&& data.max_indirect_range == 0) {
|
|
|
|
&& data.max_indirect_range == 0) {
|
|
|
@@ -836,7 +847,8 @@ BlockAllocator::Free(Transaction &transaction, block_run run)
|
|
|
|
uint16 start = run.Start();
|
|
|
|
uint16 start = run.Start();
|
|
|
|
uint16 length = run.Length();
|
|
|
|
uint16 length = run.Length();
|
|
|
|
|
|
|
|
|
|
|
|
FUNCTION_START(("group = %ld, start = %u, length = %u\n", group, start, length));
|
|
|
|
FUNCTION_START(("group = %ld, start = %u, length = %u\n", group, start,
|
|
|
|
|
|
|
|
length));
|
|
|
|
T(Free(run));
|
|
|
|
T(Free(run));
|
|
|
|
|
|
|
|
|
|
|
|
// doesn't use Volume::IsValidBlockRun() here because it can check better
|
|
|
|
// doesn't use Volume::IsValidBlockRun() here because it can check better
|
|
|
@@ -845,13 +857,17 @@ BlockAllocator::Free(Transaction &transaction, block_run run)
|
|
|
|
|| start > fGroups[group].NumBits()
|
|
|
|
|| start > fGroups[group].NumBits()
|
|
|
|
|| uint32(start + length) > fGroups[group].NumBits()
|
|
|
|
|| uint32(start + length) > fGroups[group].NumBits()
|
|
|
|
|| length == 0) {
|
|
|
|
|| length == 0) {
|
|
|
|
FATAL(("tried to free an invalid block_run (%d, %u, %u)\n", (int)group, start, length));
|
|
|
|
FATAL(("tried to free an invalid block_run (%d, %u, %u)\n", (int)group,
|
|
|
|
|
|
|
|
start, length));
|
|
|
|
DEBUGGER(("tried to free invalid block_run"));
|
|
|
|
DEBUGGER(("tried to free invalid block_run"));
|
|
|
|
return B_BAD_VALUE;
|
|
|
|
return B_BAD_VALUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// check if someone tries to free reserved areas at the beginning of the drive
|
|
|
|
// check if someone tries to free reserved areas at the beginning of the
|
|
|
|
if (group == 0 && start < uint32(fVolume->Log().Start() + fVolume->Log().Length())) {
|
|
|
|
// drive
|
|
|
|
FATAL(("tried to free a reserved block_run (%d, %u, %u)\n", (int)group, start, length));
|
|
|
|
if (group == 0
|
|
|
|
|
|
|
|
&& start < uint32(fVolume->Log().Start() + fVolume->Log().Length())) {
|
|
|
|
|
|
|
|
FATAL(("tried to free a reserved block_run (%d, %u, %u)\n", (int)group,
|
|
|
|
|
|
|
|
start, length));
|
|
|
|
DEBUGGER(("tried to free reserved block"));
|
|
|
|
DEBUGGER(("tried to free reserved block"));
|
|
|
|
return B_BAD_VALUE;
|
|
|
|
return B_BAD_VALUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
@@ -864,8 +880,10 @@ BlockAllocator::Free(Transaction &transaction, block_run run)
|
|
|
|
RETURN_ERROR(B_IO_ERROR);
|
|
|
|
RETURN_ERROR(B_IO_ERROR);
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (CheckBlockRun(run, NULL, NULL, false) < B_OK)
|
|
|
|
if (CheckBlockRun(run, NULL, NULL, false) < B_OK) {
|
|
|
|
DEBUGGER(("CheckBlockRun() reports allocated blocks (which were just freed)\n"));
|
|
|
|
DEBUGGER(("CheckBlockRun() reports allocated blocks (which were just "
|
|
|
|
|
|
|
|
"freed)\n"));
|
|
|
|
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
fVolume->SuperBlock().used_blocks =
|
|
|
|
fVolume->SuperBlock().used_blocks =
|
|
|
@@ -881,9 +899,12 @@ BlockAllocator::BitmapSize() const
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
// #pragma mark - Bitmap validity checking
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: implement new FS checking API
|
|
|
|
// Functions to check the validity of the bitmap - they are used from
|
|
|
|
// Functions to check the validity of the bitmap - they are used from
|
|
|
|
// the "chkbfs" command
|
|
|
|
// the "chkbfs" command (since this does even a bit more, maybe we should
|
|
|
|
|
|
|
|
// move this some place else?)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
bool
|
|
|
@@ -940,7 +961,7 @@ BlockAllocator::StartChecking(check_control *control)
|
|
|
|
fCheckCookie = cookie;
|
|
|
|
fCheckCookie = cookie;
|
|
|
|
// to be able to restore nicely if "chkbfs" exited abnormally
|
|
|
|
// to be able to restore nicely if "chkbfs" exited abnormally
|
|
|
|
|
|
|
|
|
|
|
|
// ToDo: check reserved area in bitmap!
|
|
|
|
// TODO: check reserved area in bitmap!
|
|
|
|
|
|
|
|
|
|
|
|
return B_OK;
|
|
|
|
return B_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
@@ -973,7 +994,7 @@ BlockAllocator::StopChecking(check_control *control)
|
|
|
|
size_t size = fVolume->BlockSize() * fNumGroups * fBlocksPerGroup;
|
|
|
|
size_t size = fVolume->BlockSize() * fNumGroups * fBlocksPerGroup;
|
|
|
|
off_t usedBlocks = 0LL;
|
|
|
|
off_t usedBlocks = 0LL;
|
|
|
|
|
|
|
|
|
|
|
|
// ToDo: update the allocation groups used blocks info
|
|
|
|
// TODO: update the allocation groups used blocks info
|
|
|
|
for (uint32 i = size >> 2; i-- > 0;) {
|
|
|
|
for (uint32 i = size >> 2; i-- > 0;) {
|
|
|
|
uint32 compare = 1;
|
|
|
|
uint32 compare = 1;
|
|
|
|
for (int16 j = 0; j < 32; j++, compare <<= 1) {
|
|
|
|
for (int16 j = 0; j < 32; j++, compare <<= 1) {
|
|
|
@@ -988,13 +1009,14 @@ BlockAllocator::StopChecking(check_control *control)
|
|
|
|
control->stats.freed = 0;
|
|
|
|
control->stats.freed = 0;
|
|
|
|
|
|
|
|
|
|
|
|
// Should we fix errors? Were there any errors we can fix?
|
|
|
|
// Should we fix errors? Were there any errors we can fix?
|
|
|
|
if (control->flags & BFS_FIX_BITMAP_ERRORS
|
|
|
|
if ((control->flags & BFS_FIX_BITMAP_ERRORS) != 0
|
|
|
|
&& (control->stats.freed != 0 || control->stats.missing != 0)) {
|
|
|
|
&& (control->stats.freed != 0 || control->stats.missing != 0)) {
|
|
|
|
// if so, write the check bitmap back over the original one,
|
|
|
|
// if so, write the check bitmap back over the original one,
|
|
|
|
// and use transactions here to play safe - we even use several
|
|
|
|
// and use transactions here to play safe - we even use several
|
|
|
|
// transactions, so that we don't blow the maximum log size
|
|
|
|
// transactions, so that we don't blow the maximum log size
|
|
|
|
// on large disks; since we don't need to make this atomic
|
|
|
|
// on large disks; since we don't need to make this atomic
|
|
|
|
fVolume->SuperBlock().used_blocks = HOST_ENDIAN_TO_BFS_INT64(usedBlocks);
|
|
|
|
fVolume->SuperBlock().used_blocks
|
|
|
|
|
|
|
|
= HOST_ENDIAN_TO_BFS_INT64(usedBlocks);
|
|
|
|
|
|
|
|
|
|
|
|
int32 blocksInBitmap = fNumGroups * fBlocksPerGroup;
|
|
|
|
int32 blocksInBitmap = fNumGroups * fBlocksPerGroup;
|
|
|
|
int32 blockSize = fVolume->BlockSize();
|
|
|
|
int32 blockSize = fVolume->BlockSize();
|
|
|
@@ -1049,18 +1071,21 @@ BlockAllocator::CheckNextNode(check_control *control)
|
|
|
|
Vnode vnode(fVolume, cookie->current);
|
|
|
|
Vnode vnode(fVolume, cookie->current);
|
|
|
|
Inode* inode;
|
|
|
|
Inode* inode;
|
|
|
|
if (vnode.Get(&inode) < B_OK) {
|
|
|
|
if (vnode.Get(&inode) < B_OK) {
|
|
|
|
FATAL(("check: Could not open inode at %Ld\n", fVolume->ToBlock(cookie->current)));
|
|
|
|
FATAL(("check: Could not open inode at %Ld\n",
|
|
|
|
|
|
|
|
fVolume->ToBlock(cookie->current)));
|
|
|
|
continue;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!inode->IsContainer()) {
|
|
|
|
if (!inode->IsContainer()) {
|
|
|
|
FATAL(("check: inode at %Ld should have been a directory\n", fVolume->ToBlock(cookie->current)));
|
|
|
|
FATAL(("check: inode at %Ld should have been a directory\n",
|
|
|
|
|
|
|
|
fVolume->ToBlock(cookie->current)));
|
|
|
|
continue;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
BPlusTree* tree;
|
|
|
|
BPlusTree* tree;
|
|
|
|
if (inode->GetTree(&tree) != B_OK) {
|
|
|
|
if (inode->GetTree(&tree) != B_OK) {
|
|
|
|
FATAL(("check: could not open b+tree from inode at %Ld\n", fVolume->ToBlock(cookie->current)));
|
|
|
|
FATAL(("check: could not open b+tree from inode at %Ld\n",
|
|
|
|
|
|
|
|
fVolume->ToBlock(cookie->current)));
|
|
|
|
continue;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@@ -1091,7 +1116,8 @@ BlockAllocator::CheckNextNode(check_control *control)
|
|
|
|
uint16 length;
|
|
|
|
uint16 length;
|
|
|
|
ino_t id;
|
|
|
|
ino_t id;
|
|
|
|
|
|
|
|
|
|
|
|
status_t status = cookie->iterator->GetNextEntry(name, &length, B_FILE_NAME_LENGTH, &id);
|
|
|
|
status_t status = cookie->iterator->GetNextEntry(name, &length,
|
|
|
|
|
|
|
|
B_FILE_NAME_LENGTH, &id);
|
|
|
|
if (status == B_ENTRY_NOT_FOUND) {
|
|
|
|
if (status == B_ENTRY_NOT_FOUND) {
|
|
|
|
// there are no more entries in this iterator, free it and go on
|
|
|
|
// there are no more entries in this iterator, free it and go on
|
|
|
|
delete cookie->iterator;
|
|
|
|
delete cookie->iterator;
|
|
|
@@ -1128,7 +1154,8 @@ BlockAllocator::CheckNextNode(check_control *control)
|
|
|
|
const char* localName = inode->Name(node.Node());
|
|
|
|
const char* localName = inode->Name(node.Node());
|
|
|
|
if (localName == NULL || strcmp(localName, name)) {
|
|
|
|
if (localName == NULL || strcmp(localName, name)) {
|
|
|
|
control->errors |= BFS_NAMES_DONT_MATCH;
|
|
|
|
control->errors |= BFS_NAMES_DONT_MATCH;
|
|
|
|
FATAL(("Names differ: tree \"%s\", inode \"%s\"\n", name, localName));
|
|
|
|
FATAL(("Names differ: tree \"%s\", inode \"%s\"\n", name,
|
|
|
|
|
|
|
|
localName));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@@ -1136,25 +1163,30 @@ BlockAllocator::CheckNextNode(check_control *control)
|
|
|
|
|
|
|
|
|
|
|
|
// Check for the correct mode of the node (if the mode of the
|
|
|
|
// Check for the correct mode of the node (if the mode of the
|
|
|
|
// file don't fit to its parent, there is a serious problem)
|
|
|
|
// file don't fit to its parent, there is a serious problem)
|
|
|
|
if (((cookie->parent_mode & S_ATTR_DIR) != 0 && !inode->IsAttribute())
|
|
|
|
if (((cookie->parent_mode & S_ATTR_DIR) != 0
|
|
|
|
|| ((cookie->parent_mode & S_INDEX_DIR) != 0 && !inode->IsIndex())
|
|
|
|
&& !inode->IsAttribute())
|
|
|
|
|| ((cookie->parent_mode & (S_DIRECTORY | S_ATTR_DIR | S_INDEX_DIR))
|
|
|
|
|| ((cookie->parent_mode & S_INDEX_DIR) != 0
|
|
|
|
== S_DIRECTORY
|
|
|
|
&& !inode->IsIndex())
|
|
|
|
&& (inode->Mode() & (S_ATTR | S_ATTR_DIR | S_INDEX_DIR)) != 0)) {
|
|
|
|
|| (is_directory(cookie->parent_mode)
|
|
|
|
FATAL(("inode at %Ld is of wrong type: %o (parent %o at %Ld)!\n",
|
|
|
|
&& !inode->IsRegularNode())) {
|
|
|
|
inode->BlockNumber(), inode->Mode(), cookie->parent_mode, cookie->parent->BlockNumber()));
|
|
|
|
FATAL(("inode at %Ld is of wrong type: %o (parent %o at %Ld)!"
|
|
|
|
|
|
|
|
"\n", inode->BlockNumber(), inode->Mode(),
|
|
|
|
|
|
|
|
cookie->parent_mode, cookie->parent->BlockNumber()));
|
|
|
|
|
|
|
|
|
|
|
|
// if we are allowed to fix errors, we should remove the file
|
|
|
|
// if we are allowed to fix errors, we should remove the file
|
|
|
|
if (control->flags & BFS_REMOVE_WRONG_TYPES
|
|
|
|
if ((control->flags & BFS_REMOVE_WRONG_TYPES) != 0
|
|
|
|
&& control->flags & BFS_FIX_BITMAP_ERRORS) {
|
|
|
|
&& (control->flags & BFS_FIX_BITMAP_ERRORS) != 0) {
|
|
|
|
// it's safe to start a transaction, because Inode::Remove()
|
|
|
|
// it's safe to start a transaction, because Inode::Remove()
|
|
|
|
// won't touch the block bitmap (which we hold the lock for)
|
|
|
|
// won't touch the block bitmap (which we hold the lock for)
|
|
|
|
// if we set the INODE_DONT_FREE_SPACE flag - since we fix
|
|
|
|
// if we set the INODE_DONT_FREE_SPACE flag - since we fix
|
|
|
|
// the bitmap anyway
|
|
|
|
// the bitmap anyway
|
|
|
|
Transaction transaction(fVolume, cookie->parent->BlockNumber());
|
|
|
|
Transaction transaction(fVolume,
|
|
|
|
|
|
|
|
cookie->parent->BlockNumber());
|
|
|
|
|
|
|
|
|
|
|
|
inode->Node().flags |= HOST_ENDIAN_TO_BFS_INT32(INODE_DONT_FREE_SPACE);
|
|
|
|
inode->Node().flags
|
|
|
|
status = cookie->parent->Remove(transaction, name, NULL, inode->IsContainer());
|
|
|
|
|= HOST_ENDIAN_TO_BFS_INT32(INODE_DONT_FREE_SPACE);
|
|
|
|
|
|
|
|
status = cookie->parent->Remove(transaction, name, NULL,
|
|
|
|
|
|
|
|
inode->IsContainer());
|
|
|
|
if (status == B_OK)
|
|
|
|
if (status == B_OK)
|
|
|
|
transaction.Done();
|
|
|
|
transaction.Done();
|
|
|
|
} else
|
|
|
|
} else
|
|
|
@@ -1192,7 +1224,8 @@ BlockAllocator::_CheckBitmapIsUsedAt(off_t block) const
|
|
|
|
if (index > size / 4)
|
|
|
|
if (index > size / 4)
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
return BFS_ENDIAN_TO_HOST_INT32(fCheckBitmap[index]) & (1UL << (block & 0x1f));
|
|
|
|
return BFS_ENDIAN_TO_HOST_INT32(fCheckBitmap[index])
|
|
|
|
|
|
|
|
& (1UL << (block & 0x1f));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1209,13 +1242,16 @@ BlockAllocator::_SetCheckBitmapAt(off_t block)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
status_t
|
|
|
|
status_t
|
|
|
|
BlockAllocator::CheckBlockRun(block_run run, const char *type, check_control *control, bool allocated)
|
|
|
|
BlockAllocator::CheckBlockRun(block_run run, const char* type,
|
|
|
|
|
|
|
|
check_control* control, bool allocated)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (run.AllocationGroup() < 0 || run.AllocationGroup() >= fNumGroups
|
|
|
|
if (run.AllocationGroup() < 0 || run.AllocationGroup() >= fNumGroups
|
|
|
|
|| run.Start() > fGroups[run.AllocationGroup()].fNumBits
|
|
|
|
|| run.Start() > fGroups[run.AllocationGroup()].fNumBits
|
|
|
|
|| uint32(run.Start() + run.Length()) > fGroups[run.AllocationGroup()].fNumBits
|
|
|
|
|| uint32(run.Start() + run.Length())
|
|
|
|
|
|
|
|
> fGroups[run.AllocationGroup()].fNumBits
|
|
|
|
|| run.length == 0) {
|
|
|
|
|| run.length == 0) {
|
|
|
|
PRINT(("%s: block_run(%ld, %u, %u) is invalid!\n", type, run.AllocationGroup(), run.Start(), run.Length()));
|
|
|
|
PRINT(("%s: block_run(%ld, %u, %u) is invalid!\n", type,
|
|
|
|
|
|
|
|
run.AllocationGroup(), run.Start(), run.Length()));
|
|
|
|
if (control == NULL)
|
|
|
|
if (control == NULL)
|
|
|
|
return B_BAD_DATA;
|
|
|
|
return B_BAD_DATA;
|
|
|
|
|
|
|
|
|
|
|
@@ -1228,7 +1264,8 @@ BlockAllocator::CheckBlockRun(block_run run, const char *type, check_control *co
|
|
|
|
uint32 pos = run.Start() % bitsPerBlock;
|
|
|
|
uint32 pos = run.Start() % bitsPerBlock;
|
|
|
|
int32 length = 0;
|
|
|
|
int32 length = 0;
|
|
|
|
off_t firstMissing = -1, firstSet = -1;
|
|
|
|
off_t firstMissing = -1, firstSet = -1;
|
|
|
|
off_t firstGroupBlock = (off_t)run.AllocationGroup() << fVolume->AllocationGroupShift();
|
|
|
|
off_t firstGroupBlock
|
|
|
|
|
|
|
|
= (off_t)run.AllocationGroup() << fVolume->AllocationGroupShift();
|
|
|
|
|
|
|
|
|
|
|
|
AllocationBlock cached(fVolume);
|
|
|
|
AllocationBlock cached(fVolume);
|
|
|
|
|
|
|
|
|
|
|
@@ -1244,8 +1281,10 @@ BlockAllocator::CheckBlockRun(block_run run, const char *type, check_control *co
|
|
|
|
while (length < run.Length() && pos < cached.NumBlockBits()) {
|
|
|
|
while (length < run.Length() && pos < cached.NumBlockBits()) {
|
|
|
|
if (cached.IsUsed(pos) != allocated) {
|
|
|
|
if (cached.IsUsed(pos) != allocated) {
|
|
|
|
if (control == NULL) {
|
|
|
|
if (control == NULL) {
|
|
|
|
PRINT(("%s: block_run(%ld, %u, %u) is only partially allocated (pos = %ld, length = %ld)!\n",
|
|
|
|
PRINT(("%s: block_run(%ld, %u, %u) is only partially "
|
|
|
|
type, run.AllocationGroup(), run.Start(), run.Length(), pos, length));
|
|
|
|
"allocated (pos = %ld, length = %ld)!\n", type,
|
|
|
|
|
|
|
|
run.AllocationGroup(), run.Start(), run.Length(),
|
|
|
|
|
|
|
|
pos, length));
|
|
|
|
return B_BAD_DATA;
|
|
|
|
return B_BAD_DATA;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (firstMissing == -1) {
|
|
|
|
if (firstMissing == -1) {
|
|
|
@@ -1254,15 +1293,17 @@ BlockAllocator::CheckBlockRun(block_run run, const char *type, check_control *co
|
|
|
|
}
|
|
|
|
}
|
|
|
|
control->stats.missing++;
|
|
|
|
control->stats.missing++;
|
|
|
|
} else if (firstMissing != -1) {
|
|
|
|
} else if (firstMissing != -1) {
|
|
|
|
PRINT(("%s: block_run(%ld, %u, %u): blocks %Ld - %Ld are %sallocated!\n",
|
|
|
|
PRINT(("%s: block_run(%ld, %u, %u): blocks %Ld - %Ld are "
|
|
|
|
type, run.allocation_group, run.start, run.length, firstMissing,
|
|
|
|
"%sallocated!\n", type, run.AllocationGroup(), run.Start(),
|
|
|
|
firstGroupBlock + pos + block * bitsPerBlock - 1, allocated ? "not " : ""));
|
|
|
|
run.Length(), firstMissing,
|
|
|
|
|
|
|
|
firstGroupBlock + pos + block * bitsPerBlock - 1,
|
|
|
|
|
|
|
|
allocated ? "not " : ""));
|
|
|
|
firstMissing = -1;
|
|
|
|
firstMissing = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (fCheckBitmap != NULL) {
|
|
|
|
if (fCheckBitmap != NULL) {
|
|
|
|
// Set the block in the check bitmap as well, but have a look if it
|
|
|
|
// Set the block in the check bitmap as well, but have a look
|
|
|
|
// is already allocated first
|
|
|
|
// if it is already allocated first
|
|
|
|
uint32 offset = pos + block * bitsPerBlock;
|
|
|
|
uint32 offset = pos + block * bitsPerBlock;
|
|
|
|
if (_CheckBitmapIsUsedAt(firstGroupBlock + offset)) {
|
|
|
|
if (_CheckBitmapIsUsedAt(firstGroupBlock + offset)) {
|
|
|
|
if (firstSet == -1) {
|
|
|
|
if (firstSet == -1) {
|
|
|
@@ -1272,8 +1313,11 @@ BlockAllocator::CheckBlockRun(block_run run, const char *type, check_control *co
|
|
|
|
control->stats.already_set++;
|
|
|
|
control->stats.already_set++;
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
if (firstSet != -1) {
|
|
|
|
if (firstSet != -1) {
|
|
|
|
FATAL(("%s: block_run(%d, %u, %u): blocks %Ld - %Ld are already set!\n",
|
|
|
|
FATAL(("%s: block_run(%d, %u, %u): blocks %Ld - %Ld "
|
|
|
|
type, (int)run.AllocationGroup(), run.Start(), run.Length(), firstSet, firstGroupBlock + offset - 1));
|
|
|
|
"are already set!\n", type,
|
|
|
|
|
|
|
|
(int)run.AllocationGroup(), run.Start(),
|
|
|
|
|
|
|
|
run.Length(), firstSet,
|
|
|
|
|
|
|
|
firstGroupBlock + offset - 1));
|
|
|
|
firstSet = -1;
|
|
|
|
firstSet = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_SetCheckBitmapAt(firstGroupBlock + offset);
|
|
|
|
_SetCheckBitmapAt(firstGroupBlock + offset);
|
|
|
@@ -1284,10 +1328,18 @@ BlockAllocator::CheckBlockRun(block_run run, const char *type, check_control *co
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (block + 1 >= fBlocksPerGroup || length >= run.Length()) {
|
|
|
|
if (block + 1 >= fBlocksPerGroup || length >= run.Length()) {
|
|
|
|
if (firstMissing != -1)
|
|
|
|
if (firstMissing != -1) {
|
|
|
|
PRINT(("%s: block_run(%ld, %u, %u): blocks %Ld - %Ld are not allocated!\n", type, run.AllocationGroup(), run.Start(), run.Length(), firstMissing, firstGroupBlock + pos + block * bitsPerBlock - 1));
|
|
|
|
PRINT(("%s: block_run(%ld, %u, %u): blocks %Ld - %Ld are not "
|
|
|
|
if (firstSet != -1)
|
|
|
|
"allocated!\n", type, run.AllocationGroup(), run.Start(),
|
|
|
|
FATAL(("%s: block_run(%d, %u, %u): blocks %Ld - %Ld are already set!\n", type, (int)run.AllocationGroup(), run.Start(), run.Length(), firstSet, firstGroupBlock + pos + block * bitsPerBlock - 1));
|
|
|
|
run.Length(), firstMissing,
|
|
|
|
|
|
|
|
firstGroupBlock + pos + block * bitsPerBlock - 1));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (firstSet != -1) {
|
|
|
|
|
|
|
|
FATAL(("%s: block_run(%d, %u, %u): blocks %Ld - %Ld are "
|
|
|
|
|
|
|
|
"already set!\n", type, (int)run.AllocationGroup(),
|
|
|
|
|
|
|
|
run.Start(), run.Length(), firstSet,
|
|
|
|
|
|
|
|
firstGroupBlock + pos + block * bitsPerBlock - 1));
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@@ -1364,7 +1416,8 @@ BlockAllocator::CheckInode(Inode *inode, check_control *control)
|
|
|
|
// check the double indirect range
|
|
|
|
// check the double indirect range
|
|
|
|
|
|
|
|
|
|
|
|
if (data->max_double_indirect_range) {
|
|
|
|
if (data->max_double_indirect_range) {
|
|
|
|
status = CheckBlockRun(data->double_indirect, "double indirect", control);
|
|
|
|
status = CheckBlockRun(data->double_indirect, "double indirect",
|
|
|
|
|
|
|
|
control);
|
|
|
|
if (status < B_OK)
|
|
|
|
if (status < B_OK)
|
|
|
|
return status;
|
|
|
|
return status;
|
|
|
|
|
|
|
|
|
|
|
@@ -1372,12 +1425,14 @@ BlockAllocator::CheckInode(Inode *inode, check_control *control)
|
|
|
|
int32 runsPerArray = runsPerBlock << ARRAY_BLOCKS_SHIFT;
|
|
|
|
int32 runsPerArray = runsPerBlock << ARRAY_BLOCKS_SHIFT;
|
|
|
|
|
|
|
|
|
|
|
|
CachedBlock cachedDirect(fVolume);
|
|
|
|
CachedBlock cachedDirect(fVolume);
|
|
|
|
int32 maxIndirectIndex = (data->double_indirect.Length() << fVolume->BlockShift())
|
|
|
|
int32 maxIndirectIndex = (data->double_indirect.Length()
|
|
|
|
/ sizeof(block_run);
|
|
|
|
<< fVolume->BlockShift()) / sizeof(block_run);
|
|
|
|
|
|
|
|
|
|
|
|
for (int32 indirectIndex = 0; indirectIndex < maxIndirectIndex; indirectIndex++) {
|
|
|
|
for (int32 indirectIndex = 0; indirectIndex < maxIndirectIndex;
|
|
|
|
|
|
|
|
indirectIndex++) {
|
|
|
|
// get the indirect array block
|
|
|
|
// get the indirect array block
|
|
|
|
block_run *array = (block_run *)cached.SetTo(fVolume->ToBlock(data->double_indirect)
|
|
|
|
block_run* array = (block_run*)cached.SetTo(
|
|
|
|
|
|
|
|
fVolume->ToBlock(data->double_indirect)
|
|
|
|
+ indirectIndex / runsPerBlock);
|
|
|
|
+ indirectIndex / runsPerBlock);
|
|
|
|
if (array == NULL)
|
|
|
|
if (array == NULL)
|
|
|
|
return B_IO_ERROR;
|
|
|
|
return B_IO_ERROR;
|
|
|
@@ -1391,11 +1446,12 @@ BlockAllocator::CheckInode(Inode *inode, check_control *control)
|
|
|
|
if (status < B_OK)
|
|
|
|
if (status < B_OK)
|
|
|
|
return status;
|
|
|
|
return status;
|
|
|
|
|
|
|
|
|
|
|
|
int32 maxIndex = (indirect.Length() << fVolume->BlockShift()) / sizeof(block_run);
|
|
|
|
int32 maxIndex = (indirect.Length() << fVolume->BlockShift())
|
|
|
|
|
|
|
|
/ sizeof(block_run);
|
|
|
|
|
|
|
|
|
|
|
|
for (int32 index = 0; index < maxIndex; ) {
|
|
|
|
for (int32 index = 0; index < maxIndex; ) {
|
|
|
|
block_run *runs = (block_run *)cachedDirect.SetTo(fVolume->ToBlock(indirect)
|
|
|
|
block_run* runs = (block_run*)cachedDirect.SetTo(
|
|
|
|
+ index / runsPerBlock);
|
|
|
|
fVolume->ToBlock(indirect) + index / runsPerBlock);
|
|
|
|
if (runs == NULL)
|
|
|
|
if (runs == NULL)
|
|
|
|
return B_IO_ERROR;
|
|
|
|
return B_IO_ERROR;
|
|
|
|
|
|
|
|
|
|
|
@@ -1404,7 +1460,8 @@ BlockAllocator::CheckInode(Inode *inode, check_control *control)
|
|
|
|
if (runs[index % runsPerBlock].IsZero())
|
|
|
|
if (runs[index % runsPerBlock].IsZero())
|
|
|
|
return B_OK;
|
|
|
|
return B_OK;
|
|
|
|
|
|
|
|
|
|
|
|
status = CheckBlockRun(runs[index % runsPerBlock], "double indirect->runs->run", control);
|
|
|
|
status = CheckBlockRun(runs[index % runsPerBlock],
|
|
|
|
|
|
|
|
"double indirect->runs->run", control);
|
|
|
|
if (status < B_OK)
|
|
|
|
if (status < B_OK)
|
|
|
|
return status;
|
|
|
|
return status;
|
|
|
|
} while ((++index % runsPerArray) != 0);
|
|
|
|
} while ((++index % runsPerArray) != 0);
|
|
|
|