Implemented repairing index b+trees.
* There are now two passes in case a corrupted index tree has been found. * The second pass will clear the affected indices at first, and will then walk over all inodes again to fill them. * As a side effect, this will also defragment the indices; ie. the same mechanism could be used for this some day.
This commit is contained in:
@@ -247,6 +247,7 @@ BFSPartitionHandle::Repair(bool checkOnly)
|
|||||||
uint64 attributeDirectories = 0, attributes = 0;
|
uint64 attributeDirectories = 0, attributes = 0;
|
||||||
uint64 files = 0, directories = 0, indices = 0;
|
uint64 files = 0, directories = 0, indices = 0;
|
||||||
uint64 counter = 0;
|
uint64 counter = 0;
|
||||||
|
uint32 previousPass = result.pass;
|
||||||
|
|
||||||
// check all files and report errors
|
// check all files and report errors
|
||||||
while (ioctl(fd, BFS_IOCTL_CHECK_NEXT_NODE, &result,
|
while (ioctl(fd, BFS_IOCTL_CHECK_NEXT_NODE, &result,
|
||||||
@@ -254,34 +255,42 @@ BFSPartitionHandle::Repair(bool checkOnly)
|
|||||||
if (++counter % 50 == 0)
|
if (++counter % 50 == 0)
|
||||||
printf("%9Ld nodes processed\x1b[1A\n", counter);
|
printf("%9Ld nodes processed\x1b[1A\n", counter);
|
||||||
|
|
||||||
if (result.errors) {
|
if (result.pass == BFS_CHECK_PASS_BITMAP) {
|
||||||
printf("%s (inode = %lld)", result.name, result.inode);
|
if (result.errors) {
|
||||||
if ((result.errors & BFS_MISSING_BLOCKS) != 0)
|
printf("%s (inode = %lld)", result.name, result.inode);
|
||||||
printf(", some blocks weren't allocated");
|
if ((result.errors & BFS_MISSING_BLOCKS) != 0)
|
||||||
if ((result.errors & BFS_BLOCKS_ALREADY_SET) != 0)
|
printf(", some blocks weren't allocated");
|
||||||
printf(", has blocks already set");
|
if ((result.errors & BFS_BLOCKS_ALREADY_SET) != 0)
|
||||||
if ((result.errors & BFS_INVALID_BLOCK_RUN) != 0)
|
printf(", has blocks already set");
|
||||||
printf(", has invalid block run(s)");
|
if ((result.errors & BFS_INVALID_BLOCK_RUN) != 0)
|
||||||
if ((result.errors & BFS_COULD_NOT_OPEN) != 0)
|
printf(", has invalid block run(s)");
|
||||||
printf(", could not be opened");
|
if ((result.errors & BFS_COULD_NOT_OPEN) != 0)
|
||||||
if ((result.errors & BFS_WRONG_TYPE) != 0)
|
printf(", could not be opened");
|
||||||
printf(", has wrong type");
|
if ((result.errors & BFS_WRONG_TYPE) != 0)
|
||||||
if ((result.errors & BFS_NAMES_DONT_MATCH) != 0)
|
printf(", has wrong type");
|
||||||
printf(", names don't match");
|
if ((result.errors & BFS_NAMES_DONT_MATCH) != 0)
|
||||||
if ((result.errors & BFS_INVALID_BPLUSTREE) != 0)
|
printf(", names don't match");
|
||||||
printf(", invalid b+tree");
|
if ((result.errors & BFS_INVALID_BPLUSTREE) != 0)
|
||||||
putchar('\n');
|
printf(", invalid b+tree");
|
||||||
|
putchar('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((result.mode & (S_INDEX_DIR | 0777)) == S_INDEX_DIR)
|
||||||
|
indices++;
|
||||||
|
else if (result.mode & S_ATTR_DIR)
|
||||||
|
attributeDirectories++;
|
||||||
|
else if (result.mode & S_ATTR)
|
||||||
|
attributes++;
|
||||||
|
else if (S_ISDIR(result.mode))
|
||||||
|
directories++;
|
||||||
|
else
|
||||||
|
files++;
|
||||||
|
} else if (result.pass == BFS_CHECK_PASS_INDEX) {
|
||||||
|
if (previousPass != result.pass) {
|
||||||
|
printf("Recreating broken index b+trees...\n");
|
||||||
|
previousPass = result.pass;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ((result.mode & (S_INDEX_DIR | 0777)) == S_INDEX_DIR)
|
|
||||||
indices++;
|
|
||||||
else if (result.mode & S_ATTR_DIR)
|
|
||||||
attributeDirectories++;
|
|
||||||
else if (result.mode & S_ATTR)
|
|
||||||
attributes++;
|
|
||||||
else if (S_ISDIR(result.mode))
|
|
||||||
directories++;
|
|
||||||
else
|
|
||||||
files++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// stop checking
|
// stop checking
|
||||||
|
|||||||
@@ -752,6 +752,57 @@ BPlusTree::Validate(bool repair, bool& _errorsFound)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
BPlusTree::MakeEmpty()
|
||||||
|
{
|
||||||
|
// Put all nodes into the free list in order
|
||||||
|
Transaction transaction(fStream->GetVolume(), fStream->BlockNumber());
|
||||||
|
|
||||||
|
// Reset the header, and root node
|
||||||
|
CachedNode cached(this);
|
||||||
|
bplustree_header* header = cached.SetToWritableHeader(transaction);
|
||||||
|
if (header == NULL)
|
||||||
|
return B_IO_ERROR;
|
||||||
|
|
||||||
|
header->max_number_of_levels = HOST_ENDIAN_TO_BFS_INT32(1);
|
||||||
|
header->root_node_pointer = HOST_ENDIAN_TO_BFS_INT64(NodeSize());
|
||||||
|
if (fStream->Size() > NodeSize() * 2)
|
||||||
|
header->free_node_pointer = HOST_ENDIAN_TO_BFS_INT64(2 * NodeSize());
|
||||||
|
else {
|
||||||
|
header->free_node_pointer
|
||||||
|
= HOST_ENDIAN_TO_BFS_INT64((uint64)BPLUSTREE_NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
bplustree_node* node = cached.SetToWritable(transaction, NodeSize());
|
||||||
|
node->left_link = HOST_ENDIAN_TO_BFS_INT64((uint64)BPLUSTREE_NULL);
|
||||||
|
node->right_link = HOST_ENDIAN_TO_BFS_INT64((uint64)BPLUSTREE_NULL);
|
||||||
|
node->overflow_link = HOST_ENDIAN_TO_BFS_INT64((uint64)BPLUSTREE_NULL);
|
||||||
|
node->all_key_count = 0;
|
||||||
|
node->all_key_length = 0;
|
||||||
|
|
||||||
|
for (off_t offset = 2 * NodeSize(); offset < fStream->Size();
|
||||||
|
offset += NodeSize()) {
|
||||||
|
bplustree_node* node = cached.SetToWritable(transaction, offset, false);
|
||||||
|
if (node == NULL) {
|
||||||
|
dprintf("--> could not open %lld\n", offset);
|
||||||
|
return B_IO_ERROR;
|
||||||
|
}
|
||||||
|
if (offset < fStream->Size() - NodeSize())
|
||||||
|
node->left_link = HOST_ENDIAN_TO_BFS_INT64(offset + NodeSize());
|
||||||
|
else
|
||||||
|
node->left_link = HOST_ENDIAN_TO_BFS_INT64((uint64)BPLUSTREE_NULL);
|
||||||
|
|
||||||
|
node->overflow_link = HOST_ENDIAN_TO_BFS_INT64((uint64)BPLUSTREE_FREE);
|
||||||
|
|
||||||
|
// It's not important to write it out in a single transaction
|
||||||
|
if (transaction.IsTooLarge())
|
||||||
|
transaction.Split();
|
||||||
|
}
|
||||||
|
|
||||||
|
return transaction.Done();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int32
|
int32
|
||||||
BPlusTree::TypeCodeToKeyType(type_code code)
|
BPlusTree::TypeCodeToKeyType(type_code code)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -217,6 +217,7 @@ public:
|
|||||||
Inode* Stream() const { return fStream; }
|
Inode* Stream() const { return fStream; }
|
||||||
|
|
||||||
status_t Validate(bool repair, bool& _errorsFound);
|
status_t Validate(bool repair, bool& _errorsFound);
|
||||||
|
status_t MakeEmpty();
|
||||||
|
|
||||||
status_t Remove(Transaction& transaction,
|
status_t Remove(Transaction& transaction,
|
||||||
const uint8* key, uint16 keyLength,
|
const uint8* key, uint16 keyLength,
|
||||||
|
|||||||
@@ -166,15 +166,32 @@ private:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
struct check_cookie {
|
struct check_index {
|
||||||
check_cookie() {}
|
check_index()
|
||||||
|
:
|
||||||
|
inode(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
char name[B_FILE_NAME_LENGTH];
|
||||||
|
block_run run;
|
||||||
|
Inode* inode;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct check_cookie {
|
||||||
|
check_cookie()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 pass;
|
||||||
block_run current;
|
block_run current;
|
||||||
Inode* parent;
|
Inode* parent;
|
||||||
mode_t parent_mode;
|
mode_t parent_mode;
|
||||||
Stack<block_run> stack;
|
Stack<block_run> stack;
|
||||||
TreeIterator* iterator;
|
TreeIterator* iterator;
|
||||||
check_control control;
|
check_control control;
|
||||||
|
Stack<check_index*> indices;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -1230,6 +1247,7 @@ BlockAllocator::StartChecking(const check_control* control)
|
|||||||
_SetCheckBitmapAt(block);
|
_SetCheckBitmapAt(block);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fCheckCookie->pass = BFS_CHECK_PASS_BITMAP;
|
||||||
fCheckCookie->stack.Push(fVolume->Root());
|
fCheckCookie->stack.Push(fVolume->Root());
|
||||||
fCheckCookie->stack.Push(fVolume->Indices());
|
fCheckCookie->stack.Push(fVolume->Indices());
|
||||||
fCheckCookie->iterator = NULL;
|
fCheckCookie->iterator = NULL;
|
||||||
@@ -1264,81 +1282,25 @@ BlockAllocator::StopChecking(check_control* control)
|
|||||||
|
|
||||||
if (fVolume->IsReadOnly()) {
|
if (fVolume->IsReadOnly()) {
|
||||||
// We can't fix errors on this volume
|
// We can't fix errors on this volume
|
||||||
fCheckCookie->control.flags &= ~BFS_FIX_BITMAP_ERRORS;
|
fCheckCookie->control.flags = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if CheckNextNode() could completely work through, we can
|
if (fCheckCookie->control.status != B_ENTRY_NOT_FOUND)
|
||||||
// fix any damages of the bitmap
|
|
||||||
if (fCheckCookie->control.status == B_ENTRY_NOT_FOUND) {
|
|
||||||
// calculate the number of used blocks in the check bitmap
|
|
||||||
size_t size = BitmapSize();
|
|
||||||
off_t usedBlocks = 0LL;
|
|
||||||
|
|
||||||
// TODO: update the allocation groups used blocks info
|
|
||||||
for (uint32 i = size >> 2; i-- > 0;) {
|
|
||||||
uint32 compare = 1;
|
|
||||||
// Count the number of bits set
|
|
||||||
for (int16 j = 0; j < 32; j++, compare <<= 1) {
|
|
||||||
if ((compare & fCheckBitmap[i]) != 0)
|
|
||||||
usedBlocks++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fCheckCookie->control.stats.freed = fVolume->UsedBlocks() - usedBlocks
|
|
||||||
+ fCheckCookie->control.stats.missing;
|
|
||||||
if (fCheckCookie->control.stats.freed < 0)
|
|
||||||
fCheckCookie->control.stats.freed = 0;
|
|
||||||
|
|
||||||
// Should we fix errors? Were there any errors we can fix?
|
|
||||||
if ((fCheckCookie->control.flags & BFS_FIX_BITMAP_ERRORS) != 0
|
|
||||||
&& (fCheckCookie->control.stats.freed != 0
|
|
||||||
|| fCheckCookie->control.stats.missing != 0)) {
|
|
||||||
// If so, write the check bitmap back over the original one,
|
|
||||||
// and use transactions here to play safe - we even use several
|
|
||||||
// transactions, so that we don't blow the maximum log size
|
|
||||||
// on large disks, since we don't need to make this atomic.
|
|
||||||
#if 0
|
|
||||||
// prints the blocks that differ
|
|
||||||
off_t block = 0;
|
|
||||||
for (int32 i = 0; i < fNumGroups; i++) {
|
|
||||||
AllocationBlock cached(fVolume);
|
|
||||||
for (uint32 j = 0; j < fGroups[i].NumBlocks(); j++) {
|
|
||||||
cached.SetTo(fGroups[i], j);
|
|
||||||
for (uint32 k = 0; k < cached.NumBlockBits(); k++) {
|
|
||||||
if (cached.IsUsed(k) != _CheckBitmapIsUsedAt(block)) {
|
|
||||||
dprintf("differ block %lld (should be %d)\n", block,
|
|
||||||
_CheckBitmapIsUsedAt(block));
|
|
||||||
}
|
|
||||||
block++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
fVolume->SuperBlock().used_blocks
|
|
||||||
= HOST_ENDIAN_TO_BFS_INT64(usedBlocks);
|
|
||||||
|
|
||||||
size_t blockSize = fVolume->BlockSize();
|
|
||||||
|
|
||||||
for (uint32 i = 0; i < fNumBlocks; i += 512) {
|
|
||||||
Transaction transaction(fVolume, 1 + i);
|
|
||||||
|
|
||||||
uint32 blocksToWrite = 512;
|
|
||||||
if (blocksToWrite + i > fNumBlocks)
|
|
||||||
blocksToWrite = fNumBlocks - i;
|
|
||||||
|
|
||||||
status_t status = transaction.WriteBlocks(1 + i,
|
|
||||||
(uint8*)fCheckBitmap + i * blockSize, blocksToWrite);
|
|
||||||
if (status < B_OK) {
|
|
||||||
FATAL(("error writing bitmap: %s\n", strerror(status)));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
transaction.Done();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
FATAL(("BlockAllocator::CheckNextNode() didn't run through\n"));
|
FATAL(("BlockAllocator::CheckNextNode() didn't run through\n"));
|
||||||
|
|
||||||
|
switch (fCheckCookie->pass) {
|
||||||
|
case BFS_CHECK_PASS_BITMAP:
|
||||||
|
// if CheckNextNode() could completely work through, we can
|
||||||
|
// fix any damages of the bitmap
|
||||||
|
if (fCheckCookie->control.status == B_ENTRY_NOT_FOUND)
|
||||||
|
_WriteBackCheckBitmap();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BFS_CHECK_PASS_INDEX:
|
||||||
|
_FreeIndices();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
fVolume->SetCheckingThread(-1);
|
fVolume->SetCheckingThread(-1);
|
||||||
|
|
||||||
if (control != NULL)
|
if (control != NULL)
|
||||||
@@ -1387,7 +1349,19 @@ BlockAllocator::CheckNextNode(check_control* control)
|
|||||||
while (true) {
|
while (true) {
|
||||||
if (fCheckCookie->iterator == NULL) {
|
if (fCheckCookie->iterator == NULL) {
|
||||||
if (!fCheckCookie->stack.Pop(&fCheckCookie->current)) {
|
if (!fCheckCookie->stack.Pop(&fCheckCookie->current)) {
|
||||||
// no more runs on the stack, we are obviously finished!
|
// No more runs on the stack, we might be finished!
|
||||||
|
if (fCheckCookie->pass == BFS_CHECK_PASS_BITMAP
|
||||||
|
&& !fCheckCookie->indices.IsEmpty()) {
|
||||||
|
// Start second pass to repair indices
|
||||||
|
_WriteBackCheckBitmap();
|
||||||
|
|
||||||
|
fCheckCookie->pass = BFS_CHECK_PASS_INDEX;
|
||||||
|
fCheckCookie->control.pass = BFS_CHECK_PASS_INDEX;
|
||||||
|
fCheckCookie->stack.Push(fVolume->Root());
|
||||||
|
_PrepareIndices();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
fCheckCookie->control.status = B_ENTRY_NOT_FOUND;
|
fCheckCookie->control.status = B_ENTRY_NOT_FOUND;
|
||||||
return B_ENTRY_NOT_FOUND;
|
return B_ENTRY_NOT_FOUND;
|
||||||
}
|
}
|
||||||
@@ -1406,7 +1380,7 @@ BlockAllocator::CheckNextNode(check_control* control)
|
|||||||
if (!inode->IsContainer()) {
|
if (!inode->IsContainer()) {
|
||||||
// Check file
|
// Check file
|
||||||
fCheckCookie->control.errors = 0;
|
fCheckCookie->control.errors = 0;
|
||||||
fCheckCookie->control.status = CheckInode(inode);
|
fCheckCookie->control.status = CheckInode(inode, NULL);
|
||||||
|
|
||||||
if (inode->GetName(fCheckCookie->control.name) < B_OK)
|
if (inode->GetName(fCheckCookie->control.name) < B_OK)
|
||||||
strcpy(fCheckCookie->control.name, "(node has no name)");
|
strcpy(fCheckCookie->control.name, "(node has no name)");
|
||||||
@@ -1435,9 +1409,9 @@ BlockAllocator::CheckNextNode(check_control* control)
|
|||||||
|
|
||||||
// check the inode of the directory
|
// check the inode of the directory
|
||||||
fCheckCookie->control.errors = 0;
|
fCheckCookie->control.errors = 0;
|
||||||
fCheckCookie->control.status = CheckInode(inode);
|
fCheckCookie->control.status = CheckInode(inode, NULL);
|
||||||
|
|
||||||
if (inode->GetName(fCheckCookie->control.name) < B_OK)
|
if (inode->GetName(fCheckCookie->control.name) != B_OK)
|
||||||
strcpy(fCheckCookie->control.name, "(dir has no name)");
|
strcpy(fCheckCookie->control.name, "(dir has no name)");
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
@@ -1497,7 +1471,8 @@ BlockAllocator::CheckNextNode(check_control* control)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check if the inode's name is the same as in the b+tree
|
// check if the inode's name is the same as in the b+tree
|
||||||
if (inode->IsRegularNode()) {
|
if (fCheckCookie->pass == BFS_CHECK_PASS_BITMAP
|
||||||
|
&& inode->IsRegularNode()) {
|
||||||
RecursiveLocker locker(inode->SmallDataLock());
|
RecursiveLocker locker(inode->SmallDataLock());
|
||||||
NodeGetter node(fVolume, inode);
|
NodeGetter node(fVolume, inode);
|
||||||
|
|
||||||
@@ -1531,12 +1506,13 @@ 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 (((fCheckCookie->parent_mode & S_ATTR_DIR) != 0
|
if (fCheckCookie->pass == BFS_CHECK_PASS_BITMAP
|
||||||
&& !inode->IsAttribute())
|
&& (((fCheckCookie->parent_mode & S_ATTR_DIR) != 0
|
||||||
|| ((fCheckCookie->parent_mode & S_INDEX_DIR) != 0
|
&& !inode->IsAttribute())
|
||||||
&& !inode->IsIndex())
|
|| ((fCheckCookie->parent_mode & S_INDEX_DIR) != 0
|
||||||
|| (is_directory(fCheckCookie->parent_mode)
|
&& !inode->IsIndex())
|
||||||
&& !inode->IsRegularNode())) {
|
|| (is_directory(fCheckCookie->parent_mode)
|
||||||
|
&& !inode->IsRegularNode()))) {
|
||||||
FATAL(("inode at %" B_PRIdOFF " is of wrong type: %o (parent "
|
FATAL(("inode at %" B_PRIdOFF " is of wrong type: %o (parent "
|
||||||
"%o at %" B_PRIdOFF ")!\n", inode->BlockNumber(),
|
"%o at %" B_PRIdOFF ")!\n", inode->BlockNumber(),
|
||||||
inode->Mode(), fCheckCookie->parent_mode,
|
inode->Mode(), fCheckCookie->parent_mode,
|
||||||
@@ -1561,7 +1537,7 @@ BlockAllocator::CheckNextNode(check_control* control)
|
|||||||
fCheckCookie->stack.Push(inode->BlockRun());
|
fCheckCookie->stack.Push(inode->BlockRun());
|
||||||
else {
|
else {
|
||||||
// check it now
|
// check it now
|
||||||
fCheckCookie->control.status = CheckInode(inode);
|
fCheckCookie->control.status = CheckInode(inode, name);
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1628,6 +1604,83 @@ BlockAllocator::_SetCheckBitmapAt(off_t block)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
BlockAllocator::_WriteBackCheckBitmap()
|
||||||
|
{
|
||||||
|
if (fVolume->IsReadOnly())
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
// calculate the number of used blocks in the check bitmap
|
||||||
|
size_t size = BitmapSize();
|
||||||
|
off_t usedBlocks = 0LL;
|
||||||
|
|
||||||
|
// TODO: update the allocation groups used blocks info
|
||||||
|
for (uint32 i = size >> 2; i-- > 0;) {
|
||||||
|
uint32 compare = 1;
|
||||||
|
// Count the number of bits set
|
||||||
|
for (int16 j = 0; j < 32; j++, compare <<= 1) {
|
||||||
|
if ((compare & fCheckBitmap[i]) != 0)
|
||||||
|
usedBlocks++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fCheckCookie->control.stats.freed = fVolume->UsedBlocks() - usedBlocks
|
||||||
|
+ fCheckCookie->control.stats.missing;
|
||||||
|
if (fCheckCookie->control.stats.freed < 0)
|
||||||
|
fCheckCookie->control.stats.freed = 0;
|
||||||
|
|
||||||
|
// Should we fix errors? Were there any errors we can fix?
|
||||||
|
if ((fCheckCookie->control.flags & BFS_FIX_BITMAP_ERRORS) != 0
|
||||||
|
&& (fCheckCookie->control.stats.freed != 0
|
||||||
|
|| fCheckCookie->control.stats.missing != 0)) {
|
||||||
|
// If so, write the check bitmap back over the original one,
|
||||||
|
// and use transactions here to play safe - we even use several
|
||||||
|
// transactions, so that we don't blow the maximum log size
|
||||||
|
// on large disks, since we don't need to make this atomic.
|
||||||
|
#if 0
|
||||||
|
// prints the blocks that differ
|
||||||
|
off_t block = 0;
|
||||||
|
for (int32 i = 0; i < fNumGroups; i++) {
|
||||||
|
AllocationBlock cached(fVolume);
|
||||||
|
for (uint32 j = 0; j < fGroups[i].NumBlocks(); j++) {
|
||||||
|
cached.SetTo(fGroups[i], j);
|
||||||
|
for (uint32 k = 0; k < cached.NumBlockBits(); k++) {
|
||||||
|
if (cached.IsUsed(k) != _CheckBitmapIsUsedAt(block)) {
|
||||||
|
dprintf("differ block %lld (should be %d)\n", block,
|
||||||
|
_CheckBitmapIsUsedAt(block));
|
||||||
|
}
|
||||||
|
block++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
fVolume->SuperBlock().used_blocks
|
||||||
|
= HOST_ENDIAN_TO_BFS_INT64(usedBlocks);
|
||||||
|
|
||||||
|
size_t blockSize = fVolume->BlockSize();
|
||||||
|
|
||||||
|
for (uint32 i = 0; i < fNumBlocks; i += 512) {
|
||||||
|
Transaction transaction(fVolume, 1 + i);
|
||||||
|
|
||||||
|
uint32 blocksToWrite = 512;
|
||||||
|
if (blocksToWrite + i > fNumBlocks)
|
||||||
|
blocksToWrite = fNumBlocks - i;
|
||||||
|
|
||||||
|
status_t status = transaction.WriteBlocks(1 + i,
|
||||||
|
(uint8*)fCheckBitmap + i * blockSize, blocksToWrite);
|
||||||
|
if (status < B_OK) {
|
||||||
|
FATAL(("error writing bitmap: %s\n", strerror(status)));
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
transaction.Done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Checks whether or not the specified block range is allocated or not,
|
/*! Checks whether or not the specified block range is allocated or not,
|
||||||
depending on the \a allocated argument.
|
depending on the \a allocated argument.
|
||||||
*/
|
*/
|
||||||
@@ -1775,13 +1828,55 @@ BlockAllocator::CheckBlockRun(block_run run, const char* type, bool allocated)
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
BlockAllocator::CheckInode(Inode* inode)
|
BlockAllocator::CheckInode(Inode* inode, const char* name)
|
||||||
{
|
{
|
||||||
if (fCheckCookie != NULL && fCheckBitmap == NULL)
|
if (fCheckCookie != NULL && fCheckBitmap == NULL)
|
||||||
return B_NO_INIT;
|
return B_NO_INIT;
|
||||||
if (inode == NULL)
|
if (inode == NULL)
|
||||||
return B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
switch (fCheckCookie->pass) {
|
||||||
|
case BFS_CHECK_PASS_BITMAP:
|
||||||
|
{
|
||||||
|
status_t status = _CheckInodeBlocks(inode, name);
|
||||||
|
if (status != B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
// Check the B+tree as well
|
||||||
|
if (inode->IsContainer()) {
|
||||||
|
bool repairErrors
|
||||||
|
= (fCheckCookie->control.flags & BFS_FIX_BPLUSTREES) != 0;
|
||||||
|
bool errorsFound = false;
|
||||||
|
status = inode->Tree()->Validate(repairErrors, errorsFound);
|
||||||
|
if (errorsFound) {
|
||||||
|
fCheckCookie->control.errors |= BFS_INVALID_BPLUSTREE;
|
||||||
|
if (inode->IsIndex() && name != NULL && repairErrors) {
|
||||||
|
// We completely rebuild corrupt indices
|
||||||
|
check_index* index = new(std::nothrow) check_index;
|
||||||
|
if (index == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
strlcpy(index->name, name, sizeof(index->name));
|
||||||
|
index->run = inode->BlockRun();
|
||||||
|
fCheckCookie->indices.Push(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
case BFS_CHECK_PASS_INDEX:
|
||||||
|
return _AddInodeToIndex(inode);
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
BlockAllocator::_CheckInodeBlocks(Inode* inode, const char* name)
|
||||||
|
{
|
||||||
status_t status = CheckBlockRun(inode->BlockRun(), "inode");
|
status_t status = CheckBlockRun(inode->BlockRun(), "inode");
|
||||||
if (status != B_OK)
|
if (status != B_OK)
|
||||||
return status;
|
return status;
|
||||||
@@ -1913,19 +2008,100 @@ BlockAllocator::CheckInode(Inode* inode)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inode->IsContainer()) {
|
return B_OK;
|
||||||
bool errorsFound = false;
|
}
|
||||||
status_t status = inode->Tree()->Validate(
|
|
||||||
(fCheckCookie->control.flags & BFS_FIX_BPLUSTREES) != 0,
|
|
||||||
errorsFound);
|
status_t
|
||||||
if (errorsFound)
|
BlockAllocator::_PrepareIndices()
|
||||||
fCheckCookie->control.errors |= BFS_INVALID_BPLUSTREE;
|
{
|
||||||
|
for (int32 i = 0; i < fCheckCookie->indices.CountItems(); i++) {
|
||||||
|
check_index* index = fCheckCookie->indices.Array()[i];
|
||||||
|
Vnode vnode(fVolume, index->run);
|
||||||
|
Inode* inode;
|
||||||
|
status_t status = vnode.Get(&inode);
|
||||||
|
if (status != B_OK) {
|
||||||
|
FATAL(("check: Could not open index at %" B_PRIdOFF "\n",
|
||||||
|
fVolume->ToBlock(index->run)));
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
BPlusTree* tree = inode->Tree();
|
||||||
|
if (tree == NULL) {
|
||||||
|
// TODO: We can't yet repair those
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = tree->MakeEmpty();
|
||||||
|
if (status != B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
index->inode = inode;
|
||||||
|
vnode.Keep();
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
BlockAllocator::_FreeIndices()
|
||||||
|
{
|
||||||
|
for (int32 i = 0; i < fCheckCookie->indices.CountItems(); i++) {
|
||||||
|
check_index* index = fCheckCookie->indices.Array()[i];
|
||||||
|
put_vnode(fVolume->FSVolume(),
|
||||||
|
fVolume->ToVnode(index->inode->BlockRun()));
|
||||||
|
}
|
||||||
|
fCheckCookie->indices.MakeEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
BlockAllocator::_AddInodeToIndex(Inode* inode)
|
||||||
|
{
|
||||||
|
Transaction transaction(fVolume, inode->BlockNumber());
|
||||||
|
|
||||||
|
for (int32 i = 0; i < fCheckCookie->indices.CountItems(); i++) {
|
||||||
|
check_index* index = fCheckCookie->indices.Array()[i];
|
||||||
|
if (index->inode == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
BPlusTree* tree = index->inode->Tree();
|
||||||
|
if (tree == NULL)
|
||||||
|
return B_ERROR;
|
||||||
|
|
||||||
|
status_t status = B_OK;
|
||||||
|
|
||||||
|
if (!strcmp(index->name, "name")) {
|
||||||
|
if (inode->InNameIndex()) {
|
||||||
|
char name[B_FILE_NAME_LENGTH];
|
||||||
|
if (inode->GetName(name, B_FILE_NAME_LENGTH) != B_OK)
|
||||||
|
return B_ERROR;
|
||||||
|
|
||||||
|
status = tree->Insert(transaction, name, inode->ID());
|
||||||
|
}
|
||||||
|
} else if (!strcmp(index->name, "last_modified")) {
|
||||||
|
if (inode->InLastModifiedIndex()) {
|
||||||
|
status = tree->Insert(transaction, inode->OldLastModified(),
|
||||||
|
inode->ID());
|
||||||
|
}
|
||||||
|
} else if (!strcmp(index->name, "size")) {
|
||||||
|
if (inode->InSizeIndex())
|
||||||
|
status = tree->Insert(transaction, inode->Size(), inode->ID());
|
||||||
|
} else {
|
||||||
|
uint8 key[BPLUSTREE_MAX_KEY_LENGTH];
|
||||||
|
size_t keyLength = BPLUSTREE_MAX_KEY_LENGTH;
|
||||||
|
if (inode->ReadAttribute(index->name, B_ANY_TYPE, 0, key,
|
||||||
|
&keyLength) == B_OK) {
|
||||||
|
status = tree->Insert(transaction, key, keyLength, inode->ID());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (status != B_OK)
|
if (status != B_OK)
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
return B_OK;
|
return transaction.Done();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ public:
|
|||||||
status_t CheckBlockRun(block_run run,
|
status_t CheckBlockRun(block_run run,
|
||||||
const char* type = NULL,
|
const char* type = NULL,
|
||||||
bool allocated = true);
|
bool allocated = true);
|
||||||
status_t CheckInode(Inode* inode);
|
status_t CheckInode(Inode* inode, const char* name);
|
||||||
|
|
||||||
size_t BitmapSize() const;
|
size_t BitmapSize() const;
|
||||||
|
|
||||||
@@ -75,6 +75,12 @@ private:
|
|||||||
bool _IsValidCheckControl(const check_control* control);
|
bool _IsValidCheckControl(const check_control* control);
|
||||||
bool _CheckBitmapIsUsedAt(off_t block) const;
|
bool _CheckBitmapIsUsedAt(off_t block) const;
|
||||||
void _SetCheckBitmapAt(off_t block);
|
void _SetCheckBitmapAt(off_t block);
|
||||||
|
status_t _CheckInodeBlocks(Inode* inode, const char* name);
|
||||||
|
status_t _FinishBitmapPass();
|
||||||
|
status_t _PrepareIndices();
|
||||||
|
void _FreeIndices();
|
||||||
|
status_t _AddInodeToIndex(Inode* inode);
|
||||||
|
status_t _WriteBackCheckBitmap();
|
||||||
|
|
||||||
static status_t _Initialize(BlockAllocator* self);
|
static status_t _Initialize(BlockAllocator* self);
|
||||||
|
|
||||||
|
|||||||
@@ -36,11 +36,16 @@ struct update_boot_block {
|
|||||||
#define BFS_IOCTL_STOP_CHECKING 14202
|
#define BFS_IOCTL_STOP_CHECKING 14202
|
||||||
#define BFS_IOCTL_CHECK_NEXT_NODE 14203
|
#define BFS_IOCTL_CHECK_NEXT_NODE 14203
|
||||||
|
|
||||||
|
/* The "pass" field constants */
|
||||||
|
#define BFS_CHECK_PASS_BITMAP 0
|
||||||
|
#define BFS_CHECK_PASS_INDEX 1
|
||||||
|
|
||||||
/* All fields except "flags", and "name" must be set to zero before
|
/* All fields except "flags", and "name" must be set to zero before
|
||||||
* BFS_IOCTL_START_CHECKING is called, and magic must be set.
|
* BFS_IOCTL_START_CHECKING is called, and magic must be set.
|
||||||
*/
|
*/
|
||||||
struct check_control {
|
struct check_control {
|
||||||
uint32 magic;
|
uint32 magic;
|
||||||
|
uint32 pass;
|
||||||
uint32 flags;
|
uint32 flags;
|
||||||
char name[B_FILE_NAME_LENGTH];
|
char name[B_FILE_NAME_LENGTH];
|
||||||
ino_t inode;
|
ino_t inode;
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ command_checkfs(int argc, const char* const* argv)
|
|||||||
uint64 attributeDirectories = 0, attributes = 0;
|
uint64 attributeDirectories = 0, attributes = 0;
|
||||||
uint64 files = 0, directories = 0, indices = 0;
|
uint64 files = 0, directories = 0, indices = 0;
|
||||||
uint64 counter = 0;
|
uint64 counter = 0;
|
||||||
|
uint32 previousPass = result.pass;
|
||||||
|
|
||||||
// check all files and report errors
|
// check all files and report errors
|
||||||
while (_kern_ioctl(rootDir, BFS_IOCTL_CHECK_NEXT_NODE, &result,
|
while (_kern_ioctl(rootDir, BFS_IOCTL_CHECK_NEXT_NODE, &result,
|
||||||
@@ -56,34 +57,42 @@ command_checkfs(int argc, const char* const* argv)
|
|||||||
if (++counter % 50 == 0)
|
if (++counter % 50 == 0)
|
||||||
fssh_dprintf("%9Ld nodes processed\x1b[1A\n", counter);
|
fssh_dprintf("%9Ld nodes processed\x1b[1A\n", counter);
|
||||||
|
|
||||||
if (result.errors) {
|
if (result.pass == BFS_CHECK_PASS_BITMAP) {
|
||||||
fssh_dprintf("%s (inode = %lld)", result.name, result.inode);
|
if (result.errors) {
|
||||||
if ((result.errors & BFS_MISSING_BLOCKS) != 0)
|
fssh_dprintf("%s (inode = %lld)", result.name, result.inode);
|
||||||
fssh_dprintf(", some blocks weren't allocated");
|
if ((result.errors & BFS_MISSING_BLOCKS) != 0)
|
||||||
if ((result.errors & BFS_BLOCKS_ALREADY_SET) != 0)
|
fssh_dprintf(", some blocks weren't allocated");
|
||||||
fssh_dprintf(", has blocks already set");
|
if ((result.errors & BFS_BLOCKS_ALREADY_SET) != 0)
|
||||||
if ((result.errors & BFS_INVALID_BLOCK_RUN) != 0)
|
fssh_dprintf(", has blocks already set");
|
||||||
fssh_dprintf(", has invalid block run(s)");
|
if ((result.errors & BFS_INVALID_BLOCK_RUN) != 0)
|
||||||
if ((result.errors & BFS_COULD_NOT_OPEN) != 0)
|
fssh_dprintf(", has invalid block run(s)");
|
||||||
fssh_dprintf(", could not be opened");
|
if ((result.errors & BFS_COULD_NOT_OPEN) != 0)
|
||||||
if ((result.errors & BFS_WRONG_TYPE) != 0)
|
fssh_dprintf(", could not be opened");
|
||||||
fssh_dprintf(", has wrong type");
|
if ((result.errors & BFS_WRONG_TYPE) != 0)
|
||||||
if ((result.errors & BFS_NAMES_DONT_MATCH) != 0)
|
fssh_dprintf(", has wrong type");
|
||||||
fssh_dprintf(", names don't match");
|
if ((result.errors & BFS_NAMES_DONT_MATCH) != 0)
|
||||||
if ((result.errors & BFS_INVALID_BPLUSTREE) != 0)
|
fssh_dprintf(", names don't match");
|
||||||
fssh_dprintf(", invalid b+tree");
|
if ((result.errors & BFS_INVALID_BPLUSTREE) != 0)
|
||||||
fssh_dprintf("\n");
|
fssh_dprintf(", invalid b+tree");
|
||||||
|
fssh_dprintf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((result.mode & (S_INDEX_DIR | 0777)) == S_INDEX_DIR)
|
||||||
|
indices++;
|
||||||
|
else if (result.mode & S_ATTR_DIR)
|
||||||
|
attributeDirectories++;
|
||||||
|
else if (result.mode & S_ATTR)
|
||||||
|
attributes++;
|
||||||
|
else if (S_ISDIR(result.mode))
|
||||||
|
directories++;
|
||||||
|
else
|
||||||
|
files++;
|
||||||
|
} else if (result.pass == BFS_CHECK_PASS_INDEX) {
|
||||||
|
if (previousPass != result.pass) {
|
||||||
|
fssh_dprintf("Recreating broken index b+trees...\n");
|
||||||
|
previousPass = result.pass;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ((result.mode & (S_INDEX_DIR | 0777)) == S_INDEX_DIR)
|
|
||||||
indices++;
|
|
||||||
else if (result.mode & S_ATTR_DIR)
|
|
||||||
attributeDirectories++;
|
|
||||||
else if (result.mode & S_ATTR)
|
|
||||||
attributes++;
|
|
||||||
else if (S_ISDIR(result.mode))
|
|
||||||
directories++;
|
|
||||||
else
|
|
||||||
files++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// stop checking
|
// stop checking
|
||||||
|
|||||||
Reference in New Issue
Block a user