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:
Axel Dörfler
2012-03-31 21:57:55 +02:00
parent bae8d9ab83
commit e109dcf97b
2 changed files with 61 additions and 22 deletions
@@ -268,11 +268,20 @@ 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();
// You can only ask for nodes at valid positions - you can't // You can only ask for nodes at valid positions - you can't
@@ -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);