Don't bail out just because a node couldn't be read.
* Added a new CachedNode::SetTo() variant that actually returns an error code. * Only bail out if there was an actual I/O error, not already if the offset was invalid. * This should help fixing some corruption corner cases.
This commit is contained in:
@@ -268,10 +268,19 @@ CachedNode::Unset()
|
|||||||
const bplustree_node*
|
const bplustree_node*
|
||||||
CachedNode::SetTo(off_t offset, bool check)
|
CachedNode::SetTo(off_t offset, bool check)
|
||||||
{
|
{
|
||||||
if (fTree == NULL || fTree->fStream == NULL) {
|
const bplustree_node* node;
|
||||||
REPORT_ERROR(B_BAD_VALUE);
|
if (SetTo(offset, &node, check) == B_OK)
|
||||||
|
return node;
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
CachedNode::SetTo(off_t offset, const bplustree_node** _node, bool check)
|
||||||
|
{
|
||||||
|
if (fTree == NULL || fTree->fStream == NULL)
|
||||||
|
RETURN_ERROR(B_BAD_VALUE);
|
||||||
|
|
||||||
Unset();
|
Unset();
|
||||||
|
|
||||||
@@ -281,7 +290,7 @@ CachedNode::SetTo(off_t offset, bool check)
|
|||||||
if (offset > fTree->fHeader.MaximumSize() - fTree->fNodeSize
|
if (offset > fTree->fHeader.MaximumSize() - fTree->fNodeSize
|
||||||
|| offset <= 0
|
|| offset <= 0
|
||||||
|| (offset % fTree->fNodeSize) != 0)
|
|| (offset % fTree->fNodeSize) != 0)
|
||||||
return NULL;
|
RETURN_ERROR(B_BAD_VALUE);
|
||||||
|
|
||||||
if (InternalSetTo(NULL, offset) != NULL && check) {
|
if (InternalSetTo(NULL, offset) != NULL && check) {
|
||||||
// sanity checks (links, all_key_count)
|
// sanity checks (links, all_key_count)
|
||||||
@@ -289,10 +298,12 @@ CachedNode::SetTo(off_t offset, bool check)
|
|||||||
FATAL(("invalid node [%p] read from offset %" B_PRIdOFF " (block %"
|
FATAL(("invalid node [%p] read from offset %" B_PRIdOFF " (block %"
|
||||||
B_PRIdOFF "), inode at %" B_PRIdINO "\n", fNode, offset,
|
B_PRIdOFF "), inode at %" B_PRIdINO "\n", fNode, offset,
|
||||||
fBlockNumber, fTree->fStream->ID()));
|
fBlockNumber, fTree->fStream->ID()));
|
||||||
return NULL;
|
return B_BAD_DATA;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return fNode;
|
|
||||||
|
*_node = fNode;
|
||||||
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -702,10 +713,19 @@ BPlusTree::Validate(bool repair, bool& _errorsFound)
|
|||||||
CachedNode cached(this);
|
CachedNode cached(this);
|
||||||
off_t freeOffset = fHeader.FreeNode();
|
off_t freeOffset = fHeader.FreeNode();
|
||||||
while (freeOffset > 0) {
|
while (freeOffset > 0) {
|
||||||
const bplustree_node* node = cached.SetTo(freeOffset, false);
|
const bplustree_node* node;
|
||||||
if (node == NULL)
|
status_t status = cached.SetTo(freeOffset, &node, false);
|
||||||
|
if (status != B_OK) {
|
||||||
|
if (status == B_IO_ERROR)
|
||||||
return B_IO_ERROR;
|
return B_IO_ERROR;
|
||||||
|
|
||||||
|
dprintf("inode %" B_PRIdOFF ": free node at %" B_PRIdOFF " could "
|
||||||
|
"not be read: %s\n", fStream->ID(), freeOffset,
|
||||||
|
strerror(status));
|
||||||
|
check.FoundError();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (check.Visited(freeOffset)) {
|
if (check.Visited(freeOffset)) {
|
||||||
dprintf("inode %" B_PRIdOFF ": free node at %" B_PRIdOFF
|
dprintf("inode %" B_PRIdOFF ": free node at %" B_PRIdOFF
|
||||||
" circular!\n", fStream->ID(), freeOffset);
|
" circular!\n", fStream->ID(), freeOffset);
|
||||||
@@ -726,12 +746,12 @@ BPlusTree::Validate(bool repair, bool& _errorsFound)
|
|||||||
|
|
||||||
// Iterate over the complete tree recursively
|
// Iterate over the complete tree recursively
|
||||||
|
|
||||||
const bplustree_node* root = cached.SetTo(fHeader.RootNode(), true);
|
const bplustree_node* root;
|
||||||
if (root == NULL)
|
status_t status = cached.SetTo(fHeader.RootNode(), &root, true);
|
||||||
return B_IO_ERROR;
|
if (status != B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
status_t status = _ValidateChildren(check, 0, fHeader.RootNode(), NULL, 0,
|
status = _ValidateChildren(check, 0, fHeader.RootNode(), NULL, 0, root);
|
||||||
root);
|
|
||||||
|
|
||||||
if (check.ErrorsFound())
|
if (check.ErrorsFound())
|
||||||
_errorsFound = true;
|
_errorsFound = true;
|
||||||
@@ -2292,11 +2312,19 @@ BPlusTree::_ValidateChildren(TreeCheck& check, uint32 level, off_t offset,
|
|||||||
off_t lastDuplicateOffset = BPLUSTREE_NULL;
|
off_t lastDuplicateOffset = BPLUSTREE_NULL;
|
||||||
|
|
||||||
while (duplicateOffset != BPLUSTREE_NULL) {
|
while (duplicateOffset != BPLUSTREE_NULL) {
|
||||||
const bplustree_node* node
|
const bplustree_node* node;
|
||||||
= cached.SetTo(duplicateOffset, false);
|
status_t status = cached.SetTo(duplicateOffset, &node, false);
|
||||||
if (node == NULL)
|
if (status != B_OK) {
|
||||||
|
if (status == B_IO_ERROR)
|
||||||
return B_IO_ERROR;
|
return B_IO_ERROR;
|
||||||
|
|
||||||
|
dprintf("inode %" B_PRIdOFF ": duplicate node at %"
|
||||||
|
B_PRIdOFF " could not be read: %s\n", fStream->ID(),
|
||||||
|
duplicateOffset, strerror(status));
|
||||||
|
check.FoundError();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
bool isFragmentNode = bplustree_node::LinkType(childOffset)
|
bool isFragmentNode = bplustree_node::LinkType(childOffset)
|
||||||
== BPLUSTREE_DUPLICATE_FRAGMENT;
|
== BPLUSTREE_DUPLICATE_FRAGMENT;
|
||||||
bool isKnownFragment = isFragmentNode
|
bool isKnownFragment = isFragmentNode
|
||||||
@@ -2419,10 +2447,18 @@ BPlusTree::_ValidateChild(TreeCheck& check, CachedNode& cached, uint32 level,
|
|||||||
off_t offset, off_t lastOffset, off_t nextOffset,
|
off_t offset, off_t lastOffset, off_t nextOffset,
|
||||||
const uint8* key, uint16 keyLength)
|
const uint8* key, uint16 keyLength)
|
||||||
{
|
{
|
||||||
const bplustree_node* node = cached.SetTo(offset, true);
|
const bplustree_node* node;
|
||||||
if (node == NULL)
|
status_t status = cached.SetTo(offset, &node, true);
|
||||||
|
if (status != B_OK) {
|
||||||
|
if (status == B_IO_ERROR)
|
||||||
return B_IO_ERROR;
|
return B_IO_ERROR;
|
||||||
|
|
||||||
|
dprintf("inode %" B_PRIdOFF ": node at %" B_PRIdOFF " could not be "
|
||||||
|
"read: %s\n", fStream->ID(), offset, strerror(status));
|
||||||
|
check.FoundError();
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
if (node->LeftLink() != lastOffset) {
|
if (node->LeftLink() != lastOffset) {
|
||||||
dprintf("inode %" B_PRIdOFF ": node at %" B_PRIdOFF " has "
|
dprintf("inode %" B_PRIdOFF ": node at %" B_PRIdOFF " has "
|
||||||
"wrong left link %" B_PRIdOFF ", expected %" B_PRIdOFF
|
"wrong left link %" B_PRIdOFF ", expected %" B_PRIdOFF
|
||||||
|
|||||||
@@ -169,6 +169,9 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
const bplustree_node* SetTo(off_t offset, bool check = true);
|
const bplustree_node* SetTo(off_t offset, bool check = true);
|
||||||
|
status_t SetTo(off_t offset,
|
||||||
|
const bplustree_node** _node,
|
||||||
|
bool check = true);
|
||||||
bplustree_node* SetToWritable(Transaction& transaction,
|
bplustree_node* SetToWritable(Transaction& transaction,
|
||||||
off_t offset, bool check = true);
|
off_t offset, bool check = true);
|
||||||
bplustree_node* MakeWritable(Transaction& transaction);
|
bplustree_node* MakeWritable(Transaction& transaction);
|
||||||
|
|||||||
Reference in New Issue
Block a user