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:
Axel Dörfler
2012-03-31 10:36:35 +02:00
parent ab2948538e
commit ce17af69dc
7 changed files with 406 additions and 149 deletions
+36 -27
View File
@@ -247,6 +247,7 @@ BFSPartitionHandle::Repair(bool checkOnly)
uint64 attributeDirectories = 0, attributes = 0;
uint64 files = 0, directories = 0, indices = 0;
uint64 counter = 0;
uint32 previousPass = result.pass;
// check all files and report errors
while (ioctl(fd, BFS_IOCTL_CHECK_NEXT_NODE, &result,
@@ -254,34 +255,42 @@ BFSPartitionHandle::Repair(bool checkOnly)
if (++counter % 50 == 0)
printf("%9Ld nodes processed\x1b[1A\n", counter);
if (result.errors) {
printf("%s (inode = %lld)", result.name, result.inode);
if ((result.errors & BFS_MISSING_BLOCKS) != 0)
printf(", some blocks weren't allocated");
if ((result.errors & BFS_BLOCKS_ALREADY_SET) != 0)
printf(", has blocks already set");
if ((result.errors & BFS_INVALID_BLOCK_RUN) != 0)
printf(", has invalid block run(s)");
if ((result.errors & BFS_COULD_NOT_OPEN) != 0)
printf(", could not be opened");
if ((result.errors & BFS_WRONG_TYPE) != 0)
printf(", has wrong type");
if ((result.errors & BFS_NAMES_DONT_MATCH) != 0)
printf(", names don't match");
if ((result.errors & BFS_INVALID_BPLUSTREE) != 0)
printf(", invalid b+tree");
putchar('\n');
if (result.pass == BFS_CHECK_PASS_BITMAP) {
if (result.errors) {
printf("%s (inode = %lld)", result.name, result.inode);
if ((result.errors & BFS_MISSING_BLOCKS) != 0)
printf(", some blocks weren't allocated");
if ((result.errors & BFS_BLOCKS_ALREADY_SET) != 0)
printf(", has blocks already set");
if ((result.errors & BFS_INVALID_BLOCK_RUN) != 0)
printf(", has invalid block run(s)");
if ((result.errors & BFS_COULD_NOT_OPEN) != 0)
printf(", could not be opened");
if ((result.errors & BFS_WRONG_TYPE) != 0)
printf(", has wrong type");
if ((result.errors & BFS_NAMES_DONT_MATCH) != 0)
printf(", names don't match");
if ((result.errors & BFS_INVALID_BPLUSTREE) != 0)
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
@@ -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
BPlusTree::TypeCodeToKeyType(type_code code)
{
@@ -217,6 +217,7 @@ public:
Inode* Stream() const { return fStream; }
status_t Validate(bool repair, bool& _errorsFound);
status_t MakeEmpty();
status_t Remove(Transaction& transaction,
const uint8* key, uint16 keyLength,
@@ -166,15 +166,32 @@ private:
#endif
struct check_cookie {
check_cookie() {}
struct check_index {
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;
Inode* parent;
mode_t parent_mode;
Stack<block_run> stack;
TreeIterator* iterator;
check_control control;
Stack<check_index*> indices;
};
@@ -1230,6 +1247,7 @@ BlockAllocator::StartChecking(const check_control* control)
_SetCheckBitmapAt(block);
}
fCheckCookie->pass = BFS_CHECK_PASS_BITMAP;
fCheckCookie->stack.Push(fVolume->Root());
fCheckCookie->stack.Push(fVolume->Indices());
fCheckCookie->iterator = NULL;
@@ -1264,81 +1282,25 @@ BlockAllocator::StopChecking(check_control* control)
if (fVolume->IsReadOnly()) {
// 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
// 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
if (fCheckCookie->control.status != B_ENTRY_NOT_FOUND)
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);
if (control != NULL)
@@ -1387,7 +1349,19 @@ BlockAllocator::CheckNextNode(check_control* control)
while (true) {
if (fCheckCookie->iterator == NULL) {
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;
return B_ENTRY_NOT_FOUND;
}
@@ -1406,7 +1380,7 @@ BlockAllocator::CheckNextNode(check_control* control)
if (!inode->IsContainer()) {
// Check file
fCheckCookie->control.errors = 0;
fCheckCookie->control.status = CheckInode(inode);
fCheckCookie->control.status = CheckInode(inode, NULL);
if (inode->GetName(fCheckCookie->control.name) < B_OK)
strcpy(fCheckCookie->control.name, "(node has no name)");
@@ -1435,9 +1409,9 @@ BlockAllocator::CheckNextNode(check_control* control)
// check the inode of the directory
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)");
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
if (inode->IsRegularNode()) {
if (fCheckCookie->pass == BFS_CHECK_PASS_BITMAP
&& inode->IsRegularNode()) {
RecursiveLocker locker(inode->SmallDataLock());
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
// file don't fit to its parent, there is a serious problem)
if (((fCheckCookie->parent_mode & S_ATTR_DIR) != 0
&& !inode->IsAttribute())
|| ((fCheckCookie->parent_mode & S_INDEX_DIR) != 0
&& !inode->IsIndex())
|| (is_directory(fCheckCookie->parent_mode)
&& !inode->IsRegularNode())) {
if (fCheckCookie->pass == BFS_CHECK_PASS_BITMAP
&& (((fCheckCookie->parent_mode & S_ATTR_DIR) != 0
&& !inode->IsAttribute())
|| ((fCheckCookie->parent_mode & S_INDEX_DIR) != 0
&& !inode->IsIndex())
|| (is_directory(fCheckCookie->parent_mode)
&& !inode->IsRegularNode()))) {
FATAL(("inode at %" B_PRIdOFF " is of wrong type: %o (parent "
"%o at %" B_PRIdOFF ")!\n", inode->BlockNumber(),
inode->Mode(), fCheckCookie->parent_mode,
@@ -1561,7 +1537,7 @@ BlockAllocator::CheckNextNode(check_control* control)
fCheckCookie->stack.Push(inode->BlockRun());
else {
// check it now
fCheckCookie->control.status = CheckInode(inode);
fCheckCookie->control.status = CheckInode(inode, name);
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,
depending on the \a allocated argument.
*/
@@ -1775,13 +1828,55 @@ BlockAllocator::CheckBlockRun(block_run run, const char* type, bool allocated)
status_t
BlockAllocator::CheckInode(Inode* inode)
BlockAllocator::CheckInode(Inode* inode, const char* name)
{
if (fCheckCookie != NULL && fCheckBitmap == NULL)
return B_NO_INIT;
if (inode == NULL)
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");
if (status != B_OK)
return status;
@@ -1913,19 +2008,100 @@ BlockAllocator::CheckInode(Inode* inode)
}
}
if (inode->IsContainer()) {
bool errorsFound = false;
status_t status = inode->Tree()->Validate(
(fCheckCookie->control.flags & BFS_FIX_BPLUSTREES) != 0,
errorsFound);
if (errorsFound)
fCheckCookie->control.errors |= BFS_INVALID_BPLUSTREE;
return B_OK;
}
status_t
BlockAllocator::_PrepareIndices()
{
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)
return status;
}
return B_OK;
return transaction.Done();
}
@@ -55,7 +55,7 @@ public:
status_t CheckBlockRun(block_run run,
const char* type = NULL,
bool allocated = true);
status_t CheckInode(Inode* inode);
status_t CheckInode(Inode* inode, const char* name);
size_t BitmapSize() const;
@@ -75,6 +75,12 @@ private:
bool _IsValidCheckControl(const check_control* control);
bool _CheckBitmapIsUsedAt(off_t block) const;
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);
@@ -36,11 +36,16 @@ struct update_boot_block {
#define BFS_IOCTL_STOP_CHECKING 14202
#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
* BFS_IOCTL_START_CHECKING is called, and magic must be set.
*/
struct check_control {
uint32 magic;
uint32 pass;
uint32 flags;
char name[B_FILE_NAME_LENGTH];
ino_t inode;
+36 -27
View File
@@ -49,6 +49,7 @@ command_checkfs(int argc, const char* const* argv)
uint64 attributeDirectories = 0, attributes = 0;
uint64 files = 0, directories = 0, indices = 0;
uint64 counter = 0;
uint32 previousPass = result.pass;
// check all files and report errors
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)
fssh_dprintf("%9Ld nodes processed\x1b[1A\n", counter);
if (result.errors) {
fssh_dprintf("%s (inode = %lld)", result.name, result.inode);
if ((result.errors & BFS_MISSING_BLOCKS) != 0)
fssh_dprintf(", some blocks weren't allocated");
if ((result.errors & BFS_BLOCKS_ALREADY_SET) != 0)
fssh_dprintf(", has blocks already set");
if ((result.errors & BFS_INVALID_BLOCK_RUN) != 0)
fssh_dprintf(", has invalid block run(s)");
if ((result.errors & BFS_COULD_NOT_OPEN) != 0)
fssh_dprintf(", could not be opened");
if ((result.errors & BFS_WRONG_TYPE) != 0)
fssh_dprintf(", has wrong type");
if ((result.errors & BFS_NAMES_DONT_MATCH) != 0)
fssh_dprintf(", names don't match");
if ((result.errors & BFS_INVALID_BPLUSTREE) != 0)
fssh_dprintf(", invalid b+tree");
fssh_dprintf("\n");
if (result.pass == BFS_CHECK_PASS_BITMAP) {
if (result.errors) {
fssh_dprintf("%s (inode = %lld)", result.name, result.inode);
if ((result.errors & BFS_MISSING_BLOCKS) != 0)
fssh_dprintf(", some blocks weren't allocated");
if ((result.errors & BFS_BLOCKS_ALREADY_SET) != 0)
fssh_dprintf(", has blocks already set");
if ((result.errors & BFS_INVALID_BLOCK_RUN) != 0)
fssh_dprintf(", has invalid block run(s)");
if ((result.errors & BFS_COULD_NOT_OPEN) != 0)
fssh_dprintf(", could not be opened");
if ((result.errors & BFS_WRONG_TYPE) != 0)
fssh_dprintf(", has wrong type");
if ((result.errors & BFS_NAMES_DONT_MATCH) != 0)
fssh_dprintf(", names don't match");
if ((result.errors & BFS_INVALID_BPLUSTREE) != 0)
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