1 /* 2 * Copyright 2002-2012, Axel Dörfler, axeld@pinc-software.de. 3 * Copyright 2012, Andreas Henriksson, sausageboy@gmail.com 4 * This file may be used under the terms of the MIT License. 5 */ 6 7 8 //! File system error checking 9 10 11 #include "CheckVisitor.h" 12 13 #include "BlockAllocator.h" 14 #include "BPlusTree.h" 15 #include "Inode.h" 16 #include "Volume.h" 17 18 19 struct check_index { 20 check_index() 21 : 22 inode(NULL) 23 { 24 } 25 26 char name[B_FILE_NAME_LENGTH]; 27 block_run run; 28 Inode* inode; 29 }; 30 31 32 CheckVisitor::CheckVisitor(Volume* volume) 33 : 34 FileSystemVisitor(volume), 35 fCheckBitmap(NULL) 36 { 37 } 38 39 40 CheckVisitor::~CheckVisitor() 41 { 42 free(fCheckBitmap); 43 } 44 45 46 status_t 47 CheckVisitor::StartBitmapPass() 48 { 49 if (!_ControlValid()) 50 return B_BAD_VALUE; 51 52 // Lock the volume's journal and block allocator 53 GetVolume()->GetJournal(0)->Lock(NULL, true); 54 recursive_lock_lock(&GetVolume()->Allocator().Lock()); 55 56 size_t size = _BitmapSize(); 57 fCheckBitmap = (uint32*)malloc(size); 58 if (fCheckBitmap == NULL) { 59 recursive_lock_unlock(&GetVolume()->Allocator().Lock()); 60 GetVolume()->GetJournal(0)->Unlock(NULL, true); 61 return B_NO_MEMORY; 62 } 63 64 memset(&Control().stats, 0, sizeof(check_control::stats)); 65 66 // initialize bitmap 67 memset(fCheckBitmap, 0, size); 68 for (int32 block = GetVolume()->Log().Start() + GetVolume()->Log().Length(); 69 block-- > 0;) { 70 _SetCheckBitmapAt(block); 71 } 72 73 Control().pass = BFS_CHECK_PASS_BITMAP; 74 Control().stats.block_size = GetVolume()->BlockSize(); 75 76 // TODO: check reserved area in bitmap! 77 78 Start(VISIT_REGULAR | VISIT_INDICES | VISIT_REMOVED 79 | VISIT_ATTRIBUTE_DIRECTORIES); 80 81 return B_OK; 82 } 83 84 85 status_t 86 CheckVisitor::WriteBackCheckBitmap() 87 { 88 if (GetVolume()->IsReadOnly()) 89 return B_OK; 90 91 // calculate the number of used blocks in the check bitmap 92 size_t size = _BitmapSize(); 93 off_t usedBlocks = 0LL; 94 95 // TODO: update the allocation groups used blocks info 96 for (uint32 i = size >> 2; i-- > 0;) { 97 uint32 compare = 1; 98 // Count the number of bits set 99 for (int16 j = 0; j < 32; j++, compare <<= 1) { 100 if ((compare & fCheckBitmap[i]) != 0) 101 usedBlocks++; 102 } 103 } 104 105 Control().stats.freed = GetVolume()->UsedBlocks() - usedBlocks 106 + Control().stats.missing; 107 if (Control().stats.freed < 0) 108 Control().stats.freed = 0; 109 110 // Should we fix errors? Were there any errors we can fix? 111 if ((Control().flags & BFS_FIX_BITMAP_ERRORS) != 0 112 && (Control().stats.freed != 0 || Control().stats.missing != 0)) { 113 // If so, write the check bitmap back over the original one, 114 // and use transactions here to play safe - we even use several 115 // transactions, so that we don't blow the maximum log size 116 // on large disks, since we don't need to make this atomic. 117 #if 0 118 // prints the blocks that differ 119 off_t block = 0; 120 for (int32 i = 0; i < fNumGroups; i++) { 121 AllocationBlock cached(fVolume); 122 for (uint32 j = 0; j < fGroups[i].NumBlocks(); j++) { 123 cached.SetTo(fGroups[i], j); 124 for (uint32 k = 0; k < cached.NumBlockBits(); k++) { 125 if (cached.IsUsed(k) != _CheckBitmapIsUsedAt(block)) { 126 dprintf("differ block %lld (should be %d)\n", block, 127 _CheckBitmapIsUsedAt(block)); 128 } 129 block++; 130 } 131 } 132 } 133 #endif 134 135 GetVolume()->SuperBlock().used_blocks 136 = HOST_ENDIAN_TO_BFS_INT64(usedBlocks); 137 138 size_t blockSize = GetVolume()->BlockSize(); 139 off_t numBitmapBlocks = GetVolume()->NumBitmapBlocks(); 140 141 for (uint32 i = 0; i < numBitmapBlocks; i += 512) { 142 Transaction transaction(GetVolume(), 1 + i); 143 144 uint32 blocksToWrite = 512; 145 if (blocksToWrite + i > numBitmapBlocks) 146 blocksToWrite = numBitmapBlocks - i; 147 148 status_t status = transaction.WriteBlocks(1 + i, 149 (uint8*)fCheckBitmap + i * blockSize, blocksToWrite); 150 if (status < B_OK) { 151 FATAL(("error writing bitmap: %s\n", strerror(status))); 152 return status; 153 } 154 transaction.Done(); 155 } 156 } 157 158 return B_OK; 159 } 160 161 162 status_t 163 CheckVisitor::StartIndexPass() 164 { 165 // if we don't have indices to rebuild, this pass is done 166 if (indices.IsEmpty()) 167 return B_ENTRY_NOT_FOUND; 168 169 Control().pass = BFS_CHECK_PASS_INDEX; 170 171 status_t status = _PrepareIndices(); 172 if (status != B_OK) { 173 Control().status = status; 174 return status; 175 } 176 177 Start(VISIT_REGULAR); 178 return Next(); 179 } 180 181 182 status_t 183 CheckVisitor::StopChecking() 184 { 185 if (GetVolume()->IsReadOnly()) { 186 // We can't fix errors on this volume 187 Control().flags = 0; 188 } 189 190 if (Control().status != B_ENTRY_NOT_FOUND) 191 FATAL(("CheckVisitor didn't run through\n")); 192 193 _FreeIndices(); 194 195 recursive_lock_unlock(&GetVolume()->Allocator().Lock()); 196 GetVolume()->GetJournal(0)->Unlock(NULL, true); 197 return B_OK; 198 } 199 200 201 status_t 202 CheckVisitor::VisitDirectoryEntry(Inode* inode, Inode* parent, 203 const char* treeName) 204 { 205 Control().inode = inode->ID(); 206 Control().mode = inode->Mode(); 207 208 if (Pass() != BFS_CHECK_PASS_BITMAP) 209 return B_OK; 210 211 // check if the inode's name is the same as in the b+tree 212 if (inode->IsRegularNode()) { 213 RecursiveLocker locker(inode->SmallDataLock()); 214 NodeGetter node(GetVolume(), inode); 215 if (node.Node() == NULL) { 216 Control().errors |= BFS_COULD_NOT_OPEN; 217 Control().status = B_IO_ERROR; 218 return B_OK; 219 } 220 221 const char* localName = inode->Name(node.Node()); 222 if (localName == NULL || strcmp(localName, treeName)) { 223 Control().errors |= BFS_NAMES_DONT_MATCH; 224 FATAL(("Names differ: tree \"%s\", inode \"%s\"\n", treeName, 225 localName)); 226 227 if ((Control().flags & BFS_FIX_NAME_MISMATCHES) != 0) { 228 // Rename the inode 229 Transaction transaction(GetVolume(), inode->BlockNumber()); 230 231 // Note, this may need extra blocks, but the inode will 232 // only be checked afterwards, so that it won't be lost 233 status_t status = inode->SetName(transaction, treeName); 234 if (status == B_OK) 235 status = inode->WriteBack(transaction); 236 if (status == B_OK) 237 status = transaction.Done(); 238 if (status != B_OK) { 239 Control().status = status; 240 return B_OK; 241 } 242 } 243 } 244 } 245 246 // Check for the correct mode of the node (if the mode of the 247 // file don't fit to its parent, there is a serious problem) 248 if (((parent->Mode() & S_ATTR_DIR) != 0 249 && !inode->IsAttribute()) 250 || ((parent->Mode() & S_INDEX_DIR) != 0 251 && !inode->IsIndex()) 252 || (is_directory(parent->Mode()) 253 && !inode->IsRegularNode())) { 254 FATAL(("inode at %" B_PRIdOFF " is of wrong type: %o (parent " 255 "%o at %" B_PRIdOFF ")!\n", inode->BlockNumber(), 256 inode->Mode(), parent->Mode(), parent->BlockNumber())); 257 258 // if we are allowed to fix errors, we should remove the file 259 if ((Control().flags & BFS_REMOVE_WRONG_TYPES) != 0 260 && (Control().flags & BFS_FIX_BITMAP_ERRORS) != 0) { 261 Control().status = _RemoveInvalidNode(parent, NULL, inode, 262 treeName); 263 } else 264 Control().status = B_ERROR; 265 266 Control().errors |= BFS_WRONG_TYPE; 267 } 268 return B_OK; 269 } 270 271 272 status_t 273 CheckVisitor::VisitInode(Inode* inode, const char* treeName) 274 { 275 Control().inode = inode->ID(); 276 Control().mode = inode->Mode(); 277 // (we might have set these in VisitDirectoryEntry already) 278 279 // set name 280 if (treeName == NULL) { 281 if (inode->GetName(Control().name) < B_OK) { 282 if (inode->IsContainer()) 283 strcpy(Control().name, "(dir has no name)"); 284 else 285 strcpy(Control().name, "(node has no name)"); 286 } 287 } else 288 strcpy(Control().name, treeName); 289 290 status_t status = B_OK; 291 292 switch (Pass()) { 293 case BFS_CHECK_PASS_BITMAP: 294 { 295 status = _CheckInodeBlocks(inode, NULL); 296 if (status != B_OK) 297 return status; 298 299 // Check the B+tree as well 300 if (inode->IsContainer()) { 301 bool repairErrors = (Control().flags & BFS_FIX_BPLUSTREES) != 0; 302 bool errorsFound = false; 303 304 status = inode->Tree()->Validate(repairErrors, errorsFound); 305 306 if (errorsFound) { 307 Control().errors |= BFS_INVALID_BPLUSTREE; 308 if (inode->IsIndex() && treeName != NULL && repairErrors) { 309 // We completely rebuild corrupt indices 310 check_index* index = new(std::nothrow) check_index; 311 if (index == NULL) 312 return B_NO_MEMORY; 313 314 strlcpy(index->name, treeName, sizeof(index->name)); 315 index->run = inode->BlockRun(); 316 Indices().Push(index); 317 } 318 } 319 } 320 321 break; 322 } 323 324 case BFS_CHECK_PASS_INDEX: 325 status = _AddInodeToIndex(inode); 326 break; 327 } 328 329 Control().status = status; 330 return B_OK; 331 } 332 333 334 status_t 335 CheckVisitor::OpenInodeFailed(status_t reason, ino_t id, Inode* parent, 336 char* treeName, TreeIterator* iterator) 337 { 338 FATAL(("Could not open inode at %" B_PRIdOFF "\n", id)); 339 340 if (treeName != NULL) 341 strlcpy(Control().name, treeName, B_FILE_NAME_LENGTH); 342 else 343 strcpy(Control().name, "(node has no name)"); 344 345 Control().inode = id; 346 Control().errors = BFS_COULD_NOT_OPEN; 347 348 // remove inode from the tree if we can 349 if (parent != NULL && iterator != NULL 350 && (Control().flags & BFS_REMOVE_INVALID) != 0) { 351 Control().status = _RemoveInvalidNode(parent, iterator->Tree(), NULL, 352 treeName); 353 } else 354 Control().status = B_ERROR; 355 356 return B_OK; 357 } 358 359 360 status_t 361 CheckVisitor::OpenBPlusTreeFailed(Inode* inode) 362 { 363 FATAL(("Could not open b+tree from inode at %" B_PRIdOFF "\n", 364 inode->ID())); 365 return B_OK; 366 } 367 368 369 status_t 370 CheckVisitor::TreeIterationFailed(status_t reason, Inode* parent) 371 { 372 // Iterating over the B+tree failed - we let the checkfs run 373 // fail completely, as we would delete all files we cannot 374 // access. 375 // TODO: maybe have a force parameter that actually does that. 376 // TODO: we also need to be able to repair broken B+trees! 377 return reason; 378 } 379 380 381 status_t 382 CheckVisitor::_RemoveInvalidNode(Inode* parent, BPlusTree* tree, 383 Inode* inode, const char* name) 384 { 385 // It's safe to start a transaction, because Inode::Remove() 386 // won't touch the block bitmap (which we hold the lock for) 387 // if we set the INODE_DONT_FREE_SPACE flag - since we fix 388 // the bitmap anyway. 389 Transaction transaction(GetVolume(), parent->BlockNumber()); 390 status_t status; 391 392 if (inode != NULL) { 393 inode->Node().flags |= HOST_ENDIAN_TO_BFS_INT32(INODE_DONT_FREE_SPACE); 394 395 status = parent->Remove(transaction, name, NULL, false, true); 396 } else { 397 parent->WriteLockInTransaction(transaction); 398 399 // does the file even exist? 400 off_t id; 401 status = tree->Find((uint8*)name, (uint16)strlen(name), &id); 402 if (status == B_OK) 403 status = tree->Remove(transaction, name, id); 404 } 405 406 if (status == B_OK) { 407 entry_cache_remove(GetVolume()->ID(), parent->ID(), name); 408 transaction.Done(); 409 } 410 411 return status; 412 } 413 414 415 bool 416 CheckVisitor::_ControlValid() 417 { 418 if (Control().magic != BFS_IOCTL_CHECK_MAGIC) { 419 FATAL(("invalid check_control!\n")); 420 return false; 421 } 422 423 return true; 424 } 425 426 427 bool 428 CheckVisitor::_CheckBitmapIsUsedAt(off_t block) const 429 { 430 size_t size = _BitmapSize(); 431 uint32 index = block / 32; // 32bit resolution 432 if (index > size / 4) 433 return false; 434 435 return BFS_ENDIAN_TO_HOST_INT32(fCheckBitmap[index]) 436 & (1UL << (block & 0x1f)); 437 } 438 439 440 void 441 CheckVisitor::_SetCheckBitmapAt(off_t block) 442 { 443 size_t size = _BitmapSize(); 444 uint32 index = block / 32; // 32bit resolution 445 if (index > size / 4) 446 return; 447 448 fCheckBitmap[index] |= HOST_ENDIAN_TO_BFS_INT32(1UL << (block & 0x1f)); 449 } 450 451 452 size_t 453 CheckVisitor::_BitmapSize() const 454 { 455 return GetVolume()->BlockSize() * GetVolume()->NumBitmapBlocks(); 456 } 457 458 459 status_t 460 CheckVisitor::_CheckInodeBlocks(Inode* inode, const char* name) 461 { 462 status_t status = _CheckAllocated(inode->BlockRun(), "inode"); 463 if (status != B_OK) 464 return status; 465 466 if (inode->IsSymLink() && (inode->Flags() & INODE_LONG_SYMLINK) == 0) { 467 // symlinks may not have a valid data stream 468 if (strlen(inode->Node().short_symlink) >= SHORT_SYMLINK_NAME_LENGTH) 469 return B_BAD_DATA; 470 471 return B_OK; 472 } 473 474 data_stream* data = &inode->Node().data; 475 476 // check the direct range 477 478 if (data->max_direct_range) { 479 for (int32 i = 0; i < NUM_DIRECT_BLOCKS; i++) { 480 if (data->direct[i].IsZero()) 481 break; 482 483 status = _CheckAllocated(data->direct[i], "direct"); 484 if (status < B_OK) 485 return status; 486 487 Control().stats.direct_block_runs++; 488 Control().stats.blocks_in_direct 489 += data->direct[i].Length(); 490 } 491 } 492 493 CachedBlock cached(GetVolume()); 494 495 // check the indirect range 496 497 if (data->max_indirect_range) { 498 status = _CheckAllocated(data->indirect, "indirect"); 499 if (status < B_OK) 500 return status; 501 502 off_t block = GetVolume()->ToBlock(data->indirect); 503 504 for (int32 i = 0; i < data->indirect.Length(); i++) { 505 block_run* runs = (block_run*)cached.SetTo(block + i); 506 if (runs == NULL) 507 RETURN_ERROR(B_IO_ERROR); 508 509 int32 runsPerBlock = GetVolume()->BlockSize() / sizeof(block_run); 510 int32 index = 0; 511 for (; index < runsPerBlock; index++) { 512 if (runs[index].IsZero()) 513 break; 514 515 status = _CheckAllocated(runs[index], "indirect->run"); 516 if (status < B_OK) 517 return status; 518 519 Control().stats.indirect_block_runs++; 520 Control().stats.blocks_in_indirect 521 += runs[index].Length(); 522 } 523 Control().stats.indirect_array_blocks++; 524 525 if (index < runsPerBlock) 526 break; 527 } 528 } 529 530 // check the double indirect range 531 532 if (data->max_double_indirect_range) { 533 status = _CheckAllocated(data->double_indirect, "double indirect"); 534 if (status != B_OK) 535 return status; 536 537 int32 runsPerBlock = runs_per_block(GetVolume()->BlockSize()); 538 int32 runsPerArray = runsPerBlock * data->double_indirect.Length(); 539 540 CachedBlock cachedDirect(GetVolume()); 541 542 for (int32 indirectIndex = 0; indirectIndex < runsPerArray; 543 indirectIndex++) { 544 // get the indirect array block 545 block_run* array = (block_run*)cached.SetTo( 546 GetVolume()->ToBlock(data->double_indirect) 547 + indirectIndex / runsPerBlock); 548 if (array == NULL) 549 return B_IO_ERROR; 550 551 block_run indirect = array[indirectIndex % runsPerBlock]; 552 // are we finished yet? 553 if (indirect.IsZero()) 554 return B_OK; 555 556 status = _CheckAllocated(indirect, "double indirect->runs"); 557 if (status != B_OK) 558 return status; 559 560 int32 maxIndex 561 = ((uint32)indirect.Length() << GetVolume()->BlockShift()) 562 / sizeof(block_run); 563 564 for (int32 index = 0; index < maxIndex; ) { 565 block_run* runs = (block_run*)cachedDirect.SetTo( 566 GetVolume()->ToBlock(indirect) + index / runsPerBlock); 567 if (runs == NULL) 568 return B_IO_ERROR; 569 570 do { 571 // are we finished yet? 572 if (runs[index % runsPerBlock].IsZero()) 573 return B_OK; 574 575 status = _CheckAllocated(runs[index % runsPerBlock], 576 "double indirect->runs->run"); 577 if (status != B_OK) 578 return status; 579 580 Control().stats.double_indirect_block_runs++; 581 Control().stats.blocks_in_double_indirect 582 += runs[index % runsPerBlock].Length(); 583 } while ((++index % runsPerBlock) != 0); 584 } 585 586 Control().stats.double_indirect_array_blocks++; 587 } 588 } 589 590 return B_OK; 591 } 592 593 594 status_t 595 CheckVisitor::_CheckAllocated(block_run run, const char* type) 596 { 597 BlockAllocator& allocator = GetVolume()->Allocator(); 598 599 // make sure the block run is valid 600 if (!allocator.IsValidBlockRun(run)) { 601 Control().errors |= BFS_INVALID_BLOCK_RUN; 602 return B_OK; 603 } 604 605 status_t status; 606 607 off_t start = GetVolume()->ToBlock(run); 608 off_t end = start + run.Length(); 609 610 // check if the run is allocated in the block bitmap on disk 611 off_t block = start; 612 613 while (block < end) { 614 off_t firstMissing; 615 status = allocator.CheckBlocks(block, end - block, true, &firstMissing); 616 if (status == B_OK) 617 break; 618 else if (status != B_BAD_DATA) 619 return status; 620 621 off_t afterLastMissing; 622 status = allocator.CheckBlocks(firstMissing, end - firstMissing, false, 623 &afterLastMissing); 624 if (status == B_OK) 625 afterLastMissing = end; 626 else if (status != B_BAD_DATA) 627 return status; 628 629 PRINT(("%s: block_run(%ld, %u, %u): blocks %Ld - %Ld are " 630 "not allocated!\n", type, run.AllocationGroup(), run.Start(), 631 run.Length(), firstMissing, afterLastMissing - 1)); 632 633 Control().stats.missing += afterLastMissing - firstMissing; 634 635 block = afterLastMissing; 636 } 637 638 // set bits in check bitmap, while checking if they're already set 639 off_t firstSet = -1; 640 641 for (block = start; block < end; block++) { 642 if (_CheckBitmapIsUsedAt(block)) { 643 if (firstSet == -1) { 644 firstSet = block; 645 Control().errors |= BFS_BLOCKS_ALREADY_SET; 646 } 647 Control().stats.already_set++; 648 } else { 649 if (firstSet != -1) { 650 FATAL(("%s: block_run(%d, %u, %u): blocks %" B_PRIdOFF 651 " - %" B_PRIdOFF " are already set!\n", type, 652 (int)run.AllocationGroup(), run.Start(), run.Length(), 653 firstSet, block - 1)); 654 firstSet = -1; 655 } 656 _SetCheckBitmapAt(block); 657 } 658 } 659 660 return B_OK; 661 } 662 663 664 status_t 665 CheckVisitor::_PrepareIndices() 666 { 667 int32 count = 0; 668 669 for (int32 i = 0; i < Indices().CountItems(); i++) { 670 check_index* index = Indices().Array()[i]; 671 Vnode vnode(GetVolume(), index->run); 672 Inode* inode; 673 status_t status = vnode.Get(&inode); 674 if (status != B_OK) { 675 FATAL(("check: Could not open index at %" B_PRIdOFF "\n", 676 GetVolume()->ToBlock(index->run))); 677 return status; 678 } 679 680 BPlusTree* tree = inode->Tree(); 681 if (tree == NULL) { 682 // TODO: We can't yet repair those 683 continue; 684 } 685 686 status = tree->MakeEmpty(); 687 if (status != B_OK) 688 return status; 689 690 index->inode = inode; 691 vnode.Keep(); 692 count++; 693 } 694 695 return count == 0 ? B_ENTRY_NOT_FOUND : B_OK; 696 } 697 698 699 void 700 CheckVisitor::_FreeIndices() 701 { 702 for (int32 i = 0; i < Indices().CountItems(); i++) { 703 check_index* index = Indices().Array()[i]; 704 if (index->inode != NULL) { 705 put_vnode(GetVolume()->FSVolume(), 706 GetVolume()->ToVnode(index->inode->BlockRun())); 707 } 708 delete index; 709 } 710 Indices().MakeEmpty(); 711 } 712 713 714 status_t 715 CheckVisitor::_AddInodeToIndex(Inode* inode) 716 { 717 Transaction transaction(GetVolume(), inode->BlockNumber()); 718 719 for (int32 i = 0; i < Indices().CountItems(); i++) { 720 check_index* index = Indices().Array()[i]; 721 if (index->inode == NULL) 722 continue; 723 724 index->inode->WriteLockInTransaction(transaction); 725 726 BPlusTree* tree = index->inode->Tree(); 727 if (tree == NULL) 728 return B_ERROR; 729 730 status_t status = B_OK; 731 732 if (!strcmp(index->name, "name")) { 733 if (inode->InNameIndex()) { 734 char name[B_FILE_NAME_LENGTH]; 735 if (inode->GetName(name, B_FILE_NAME_LENGTH) != B_OK) 736 return B_ERROR; 737 738 status = tree->Insert(transaction, name, inode->ID()); 739 } 740 } else if (!strcmp(index->name, "last_modified")) { 741 if (inode->InLastModifiedIndex()) { 742 status = tree->Insert(transaction, inode->OldLastModified(), 743 inode->ID()); 744 } 745 } else if (!strcmp(index->name, "size")) { 746 if (inode->InSizeIndex()) 747 status = tree->Insert(transaction, inode->Size(), inode->ID()); 748 } else { 749 uint8 key[MAX_INDEX_KEY_LENGTH]; 750 size_t keyLength = sizeof(key); 751 if (inode->ReadAttribute(index->name, B_ANY_TYPE, 0, key, 752 &keyLength) == B_OK) { 753 status = tree->Insert(transaction, key, keyLength, inode->ID()); 754 } 755 } 756 757 if (status != B_OK) 758 return status; 759 } 760 761 return transaction.Done(); 762 } 763