1 /* 2 * Copyright (c) 2001-2008 pinc Software. All Rights Reserved. 3 */ 4 5 //! recovers corrupt BFS disks 6 7 8 #include <set> 9 10 #include "Disk.h" 11 #include "Inode.h" 12 #include "Hashtable.h" 13 #include "BPlusTree.h" 14 #include "dump.h" 15 16 #include <String.h> 17 #include <fs_info.h> 18 19 #include <stdlib.h> 20 #include <stdio.h> 21 #include <string.h> 22 #include <unistd.h> 23 #include <ctype.h> 24 25 26 extern const char *__progname; 27 static const char *kProgramName = __progname; 28 29 bool gCreateIndices = false; 30 bool gDumpMissingInodes = false; 31 bool gRawMode = false; 32 bool gVerbose = false; 33 34 35 // TODO: add a cache for all inodes 36 typedef std::set<block_run> RunSet; 37 38 39 class InodeHashtable { 40 public: 41 InodeHashtable(int capacity) 42 : 43 fHashtable(capacity), 44 fLastChecked(0) 45 { 46 fHashtable.SetHashFunction((uint32 (*)(const void *))BlockRunHash); 47 fHashtable.SetCompareFunction((bool (*)(const void *, const void *)) 48 BlockRunCompare); 49 } 50 51 Inode* Acquire(Inode* inode) 52 { 53 if (inode == NULL) 54 return NULL; 55 56 status_t status = inode->AcquireBuffer(); 57 if (status != B_OK) { 58 fprintf(stderr, "Could not retrieve buffer for inode %" 59 B_PRIdOFF ": %s\n", inode->Offset(), strerror(status)); 60 return NULL; 61 } 62 return inode; 63 } 64 65 void Release(Inode* inode) 66 { 67 inode->ReleaseBuffer(); 68 } 69 70 Inode* Get(block_run run) 71 { 72 return Acquire((Inode *)fHashtable.GetValue(&run)); 73 } 74 75 bool Insert(Inode* inode) 76 { 77 bool success = fHashtable.Put(&inode->BlockRun(), inode); 78 if (success) 79 inode->ReleaseBuffer(); 80 81 return success; 82 } 83 84 bool Contains(block_run *key) 85 { 86 return fHashtable.ContainsKey(key); 87 } 88 89 Inode* Remove(block_run *key) 90 { 91 return Acquire((Inode*)fHashtable.Remove(key)); 92 } 93 94 status_t GetNextEntry(Inode **_inode) 95 { 96 status_t status = fHashtable.GetNextEntry((void**)_inode); 97 if (status == B_OK) { 98 if (Acquire(*_inode) == NULL) 99 return B_NO_MEMORY; 100 } 101 102 return status; 103 } 104 105 void Rewind() 106 { 107 fHashtable.Rewind(); 108 } 109 110 bool IsEmpty() const 111 { 112 return fHashtable.IsEmpty(); 113 } 114 115 void MakeEmpty() 116 { 117 fHashtable.MakeEmpty(HASH_EMPTY_NONE, HASH_EMPTY_DELETE); 118 } 119 120 static uint32 BlockRunHash(const block_run *run) 121 { 122 return (run->allocation_group << 16) | run->start; 123 } 124 125 static bool BlockRunCompare(const block_run *runA, const block_run *runB) 126 { 127 return *runA == *runB; 128 } 129 130 private: 131 Hashtable fHashtable; 132 bigtime_t fLastChecked; 133 uint32 fPercentUsed; 134 }; 135 136 137 class InodeGetter { 138 public: 139 InodeGetter(Disk& disk, block_run run) 140 { 141 fInode = Inode::Factory(&disk, run); 142 if (fInode != NULL) 143 fInode->AcquireBuffer(); 144 } 145 146 ~InodeGetter() 147 { 148 if (fInode != NULL) 149 fInode->ReleaseBuffer(); 150 } 151 152 Inode* Node() const 153 { 154 return fInode; 155 } 156 157 void Detach() 158 { 159 fInode = NULL; 160 } 161 162 private: 163 Inode* fInode; 164 }; 165 166 167 RunSet gMainInodes; 168 // contains all inodes found on disk in the general data area 169 InodeHashtable gLogged(50); 170 // contains all inodes found in the log area 171 InodeHashtable gMissing(50); 172 InodeHashtable gMissingEmpty(25); 173 174 175 class HashtableInodeSource : public Inode::Source { 176 public: 177 HashtableInodeSource(Disk& disk) 178 : 179 fDisk(disk) 180 { 181 } 182 183 virtual Inode *InodeAt(block_run run) 184 { 185 Inode *inode; 186 if ((inode = gLogged.Get(run)) != NULL) 187 return inode; 188 189 if ((inode = gMissing.Get(run)) != NULL) 190 return inode; 191 192 if (gMainInodes.find(run) == gMainInodes.end()) 193 return NULL; 194 195 return Inode::Factory(&fDisk, run); 196 } 197 198 private: 199 Disk& fDisk; 200 }; 201 202 203 bool 204 operator<(const block_run& a, const block_run& b) 205 { 206 return a.allocation_group < b.allocation_group 207 || (a.allocation_group == b.allocation_group && a.start < b.start); 208 } 209 210 211 void 212 collectInodes(Disk& disk, RunSet* set, InodeHashtable* hashTable, off_t start, 213 off_t end) 214 { 215 char buffer[8192]; 216 Inode inode(&disk, (bfs_inode *)buffer, false); 217 218 off_t directories = 0LL; 219 off_t directorySize = 0LL; 220 off_t files = 0LL; 221 off_t fileSize = 0LL; 222 off_t symlinks = 0LL; 223 off_t count = 0LL; 224 225 off_t position = start; 226 bigtime_t lastUpdate = system_time(); 227 228 for (off_t offset = start; offset < end; offset += sizeof(buffer)) { 229 if (disk.ReadAt(offset, buffer, sizeof(buffer)) < B_OK) { 230 fprintf(stderr, "could not read from device!\n"); 231 break; 232 } 233 234 //if ((offset % (disk.BlockSize() << disk.SuperBlock()->ag_shift)) == 0) 235 // printf("reading block %Ld, allocation group %Ld, %Ld inodes...\33[1A\n", offset / disk.BlockSize(),offset / (disk.BlockSize() << disk.SuperBlock()->ag_shift), count); 236 237 for (uint32 i = 0; i < sizeof(buffer); i += disk.BlockSize()) { 238 inode.SetTo((bfs_inode *)(buffer + i)); 239 if (inode.InitCheck() == B_OK) { 240 if (inode.Flags() & INODE_DELETED) 241 continue; 242 243 Inode *node = Inode::Factory(&disk, &inode); 244 if (node != NULL) { 245 if (gVerbose) 246 printf(" node: %" B_PRIdOFF " \"%s\"\n", position, 247 node->Name()); 248 249 if (set != NULL) 250 set->insert(node->BlockRun()); 251 else 252 hashTable->Insert(node); 253 254 if (node->IsDirectory()) { 255 directories++; 256 directorySize += node->Size(); 257 } else if (node->IsFile()) { 258 files++; 259 fileSize += node->Size(); 260 } else if (node->IsSymlink()) { 261 symlinks++; 262 } 263 count++; 264 } else if (gVerbose) { 265 printf("\nunrecognized inode:"); 266 dump_inode(&inode, inode.InodeBuffer()); 267 } 268 } 269 position += disk.BlockSize(); 270 } 271 if (system_time() - lastUpdate > 500000) { 272 printf(" block %" B_PRIdOFF " (%" B_PRIdOFF "%%), %" B_PRIdOFF 273 " inodes\33[1A\n", offset, 274 100 * (offset - start) / (end - start), count); 275 lastUpdate = system_time(); 276 } 277 } 278 printf("\n%" B_PRIdOFF " inodes found.\n", count); 279 280 printf("\n%20" B_PRIdOFF " directories found (total of %" B_PRIdOFF 281 " bytes)\n%20" B_PRIdOFF " files found (total of %" B_PRIdOFF 282 " bytes)\n%20" B_PRIdOFF " symlinks found\n" 283 "--------------------\n" 284 "%20" B_PRIdOFF " inodes total found.\n", 285 directories, directorySize, files, fileSize, symlinks, count); 286 } 287 288 289 void 290 collectLogInodes(Disk &disk) 291 { 292 // scan log area 293 off_t offset = disk.ToOffset(disk.Log()); 294 off_t end = offset + (disk.Log().length << disk.BlockShift()); 295 296 printf("\nsearching from %" B_PRIdOFF " to %" B_PRIdOFF " (log area)\n", 297 offset, end); 298 299 collectInodes(disk, NULL, &gLogged, offset, end); 300 } 301 302 303 void 304 collectRealInodes(Disk &disk) 305 { 306 // first block after bootblock, bitmap, and log 307 off_t offset = disk.ToOffset(disk.Log()) + (disk.Log().length 308 << disk.BlockShift()); 309 off_t end = (off_t)disk.NumBlocks() << disk.BlockShift(); 310 311 printf("\nsearching from %" B_PRIdOFF " to %" B_PRIdOFF " (main area)\n", 312 offset, end); 313 314 collectInodes(disk, &gMainInodes, NULL, offset, end); 315 } 316 317 318 Directory * 319 getNameIndex(Disk &disk) 320 { 321 InodeGetter getter(disk, disk.Indices()); 322 Directory *indices = dynamic_cast<Directory *>(getter.Node()); 323 324 block_run run; 325 if (indices != NULL && indices->FindEntry("name", &run) == B_OK) { 326 InodeGetter getter(disk, run); 327 Inode* node = getter.Node(); 328 getter.Detach(); 329 return dynamic_cast<Directory *>(node); 330 } 331 332 // search name index 333 334 RunSet::iterator iterator = gMainInodes.begin(); 335 for (; iterator != gMainInodes.end(); iterator++) { 336 InodeGetter getter(disk, *iterator); 337 Inode* node = getter.Node(); 338 339 if (!node || !node->IsIndex() || node->Name() == NULL) 340 continue; 341 if (!strcmp(node->Name(), "name") && node->Mode() & S_STR_INDEX) 342 return dynamic_cast<Directory *>(node); 343 } 344 345 return NULL; 346 } 347 348 349 void 350 checkDirectoryContents(Disk& disk, Directory *dir) 351 { 352 dir->Rewind(); 353 354 char name[B_FILE_NAME_LENGTH]; 355 block_run run; 356 357 while (dir->GetNextEntry(name, &run) == B_OK) { 358 if (run == dir->BlockRun() || run == dir->Parent() 359 || gMainInodes.find(run) != gMainInodes.end()) 360 continue; 361 362 Inode *missing = gMissing.Get(run); 363 if (missing != NULL) { 364 if (missing->SetName(name) < B_OK) { 365 fprintf(stderr, "setting name of missing node to " 366 "\"%s\" failed!", name); 367 } 368 if (gVerbose) { 369 printf("Set name of missing node (%" B_PRId32 370 ", %d) to \"%s\" (%s)\n", 371 run.allocation_group, run.start, missing->Name(), name); 372 } 373 374 missing->SetParent(dir->BlockRun()); 375 } 376 // if (node->Mode() & S_INDEX_DIR) 377 // { 378 // if (node->Mode() & S_STR_INDEX) 379 // printf("index directory (%ld, %d): \"%s\" is missing (%ld, %d, %d)\n",node->BlockRun().allocation_group,node->BlockRun().start,name,run.allocation_group,run.start,run.length); 380 // else 381 // printf("index directory (%ld, %d): key is missing (%ld, %d, %d)\n",node->BlockRun().allocation_group,node->BlockRun().start,run.allocation_group,run.start,run.length); 382 // } 383 else { 384 // missing inode has not been found 385 if (gVerbose) { 386 printf("directory \"%s\" (%" B_PRId32 ", %d): node \"%s\" is " 387 "missing (%" B_PRId32 ", %d, %d)\n", dir->Name(), 388 dir->BlockRun().allocation_group, 389 dir->BlockRun().start, name, 390 run.allocation_group, run.start, run.length); 391 } 392 393 if ((missing = (Inode *)gLogged.Remove(&run)) != NULL) { 394 // missing inode is in the log 395 if (gVerbose) 396 printf("found missing entry in log!\n"); 397 if (missing->InodeBuffer()->parent != dir->BlockRun()) { 398 if (gVerbose) 399 puts("\tparent directories differ (may be an old " 400 "version of it), reseting parent."); 401 missing->SetParent(dir->BlockRun()); 402 } 403 if (!gMissing.Insert(missing)) 404 delete missing; 405 } else if (!gMissingEmpty.Contains(&run)) { 406 // not known at all yet 407 Inode *empty = Inode::EmptyInode(&disk, name, 0); 408 if (empty) { 409 empty->SetBlockRun(run); 410 empty->SetParent(dir->BlockRun()); 411 if (gVerbose) 412 printf("\tname = %s\n", empty->Name()); 413 414 if (!gMissingEmpty.Insert(empty)) 415 delete empty; 416 } 417 } 418 } 419 } 420 } 421 422 423 void 424 checkStructure(Disk &disk) 425 { 426 off_t count = 0; 427 Inode* node; 428 429 RunSet::iterator iterator = gMainInodes.begin(); 430 for (; iterator != gMainInodes.end(); iterator++) { 431 InodeGetter getter(disk, *iterator); 432 node = getter.Node(); 433 434 count++; 435 if ((count % 50) == 0) 436 fprintf(stderr, "%" B_PRIdOFF " inodes processed...\33[1A\n", count); 437 438 if (node == NULL) 439 continue; 440 441 if (node->IsDirectory() && !node->IsIndex()) { 442 // check if all entries are in the hashtable 443 Directory* directory = dynamic_cast<Directory*>(node); 444 if (directory != NULL) 445 checkDirectoryContents(disk, directory); 446 else { 447 printf("Node \"%s\" at %" B_PRId32 448 ",%d looks like a directory, but isn't.\n", 449 node->Name(), node->BlockRun().allocation_group, 450 node->BlockRun().start); 451 } 452 } 453 454 // check for the parent directory 455 456 block_run run = node->Parent(); 457 InodeGetter parentGetter(disk, run); 458 Inode *parentNode = parentGetter.Node(); 459 460 Directory *dir = dynamic_cast<Directory *>(parentNode); 461 if (dir || (parentNode && (node->Mode() & S_ATTR_DIR))) { 462 // entry has a valid parent directory, so it's assumed to be a valid entry 463 disk.BlockBitmap()->BackupSet(node, true); 464 } else if (node->Mode() & S_ATTR) { 465 if (gVerbose) { 466 printf("attribute \"%s\" at %" B_PRId32 ",%d misses its parent.\n", 467 node->Name(), node->BlockRun().allocation_group, 468 node->BlockRun().start); 469 puts("\thandling not yet implemented..."); 470 } 471 } else /*if ((node->Flags() & INODE_DELETED) == 0)*/ { 472 Inode* missing = gMissing.Get(run); 473 dir = dynamic_cast<Directory *>(missing); 474 475 if (missing == NULL) { 476 if (gVerbose) { 477 printf("%s \"%s\" (%" B_PRId32 ", %d, mode = %10" B_PRIo32 478 "): parent directory is missing (%" B_PRId32 479 ", %d, %d), may be deleted!\n", 480 node->IsFile() ? "file" : "node", node->Name(), 481 node->BlockRun().allocation_group, 482 node->BlockRun().start, 483 node->Mode(), run.allocation_group, run.start, 484 run.length); 485 } 486 487 if ((dir = dynamic_cast<Directory *>((Inode *)gLogged.Remove( 488 &run))) != NULL) { 489 if (gVerbose) { 490 printf("found directory \"%s\" in log:\n", dir->Name()); 491 if (dir->Size() > 0) 492 dump_inode(dir, dir->InodeBuffer()); 493 else 494 puts("\tempty inode."); 495 } 496 } else { 497 if (gVerbose) 498 puts("\tcreate parent missing entry"); 499 500 Inode *nameNode = (Inode *)gMissingEmpty.Remove(&run); 501 if (nameNode != NULL) { 502 nameNode->SetMode(S_IFDIR); 503 if (gVerbose) 504 printf("found missing name!\n"); 505 } else { 506 BString parentName; 507 parentName << "__directory " << run.allocation_group 508 << ":" << (int32)run.start; 509 510 nameNode = Inode::EmptyInode(&disk, parentName.String(), 511 S_IFDIR); 512 if (nameNode) { 513 nameNode->SetBlockRun(run); 514 nameNode->SetParent(disk.Root()); 515 } 516 } 517 518 if (nameNode) { 519 dir = new Directory(*nameNode); 520 if (dir->CopyBuffer() < B_OK) 521 puts("could not copy buffer!"); 522 else 523 delete nameNode; 524 } 525 } 526 if (dir) { 527 dir->AcquireBuffer(); 528 529 if (!gMissing.Insert(dir)) { 530 printf("could not add dir!!\n"); 531 delete dir; 532 dir = NULL; 533 } 534 } 535 } else if (missing != NULL && dir == NULL && gVerbose) { 536 printf("%s \"%s\" (%" B_PRId32 ", %d, mode = %10" B_PRIo32 537 "): parent directory found in missing list (%" B_PRId32 538 ", %d, %d), but it's not a dir!\n", 539 node->IsFile() ? "file" : "node", node->Name(), 540 node->BlockRun().allocation_group, node->BlockRun().start, 541 node->Mode(), run.allocation_group, run.start, run.length); 542 } else if (gVerbose) { 543 printf("%s \"%s\" (%" B_PRId32 ", %d, mode = %10" B_PRIo32 544 "): parent directory found in missing list (%" B_PRId32 545 ", %d, %d)!\n", 546 node->IsFile() ? "file" : "node", node->Name(), 547 node->BlockRun().allocation_group, node->BlockRun().start, 548 node->Mode(), run.allocation_group, run.start, run.length); 549 } 550 551 if (dir) { 552 dir->AddEntry(node); 553 dir->ReleaseBuffer(); 554 } 555 } 556 // else 557 // { 558 // printf("node %s\n", node->Name()); 559 // status_t status = dir->Contains(node); 560 // if (status == B_ENTRY_NOT_FOUND) 561 // printf("node \"%s\": parent directory \"%s\" contains no link to this node!\n",node->Name(),dir->Name()); 562 // else if (status != B_OK) 563 // printf("node \"%s\": parent directory \"%s\" error: %s\n",node->Name(),dir->Name(),strerror(status)); 564 // } 565 566 // check for attributes 567 568 run = node->Attributes(); 569 if (!run.IsZero()) { 570 //printf("node \"%s\" (%ld, %d, mode = %010lo): has attribute dir!\n",node->Name(),node->BlockRun().allocation_group,node->BlockRun().start,node->Mode()); 571 572 if (gMainInodes.find(run) == gMainInodes.end()) { 573 if (gVerbose) { 574 printf("node \"%s\": attributes are missing (%" B_PRId32 575 ", %d, %d)\n", node->Name(), run.allocation_group, 576 run.start, run.length); 577 } 578 579 if ((dir = (Directory *)gMissing.Get(run)) != NULL) { 580 if (gVerbose) 581 puts("\tfound in missing"); 582 dir->SetMode(dir->Mode() | S_ATTR_DIR); 583 dir->SetParent(node->BlockRun()); 584 } else { 585 if (gVerbose) 586 puts("\tcreate new!"); 587 588 Inode *empty = Inode::EmptyInode(&disk, NULL, 589 S_IFDIR | S_ATTR_DIR); 590 if (empty) { 591 empty->SetBlockRun(run); 592 empty->SetParent(node->BlockRun()); 593 594 dir = new Directory(*empty); 595 if (dir->CopyBuffer() < B_OK) 596 puts("could not copy buffer!"); 597 else 598 delete empty; 599 600 if (!gMissing.Insert(dir)) { 601 puts("\tcould not add attribute dir"); 602 delete dir; 603 } 604 } 605 } 606 } 607 } 608 } 609 printf("%" B_PRIdOFF " inodes processed.\n", count); 610 611 Directory *directory = getNameIndex(disk); 612 if (directory != NULL) { 613 puts("\n*** Search names of missing inodes in the name index"); 614 615 BPlusTree *tree; 616 if (directory->GetTree(&tree) == B_OK && tree->Validate(gVerbose) == B_OK) { 617 char name[B_FILE_NAME_LENGTH]; 618 block_run run; 619 directory->Rewind(); 620 while (directory->GetNextEntry(name, &run) >= B_OK) { 621 if ((node = gMissing.Get(run)) == NULL) 622 continue; 623 624 if (gVerbose) { 625 printf(" Node found: %" B_PRId32 ":%d\n", 626 run.allocation_group, run.start); 627 } 628 if (node->Name() == NULL || strcmp(node->Name(), name)) { 629 if (gVerbose) { 630 printf("\tnames differ: %s -> %s (indices)\n", 631 node->Name(), name); 632 } 633 node->SetName(name); 634 } 635 } 636 } else 637 printf("\tname index is corrupt!\n"); 638 639 directory->ReleaseBuffer(); 640 } else 641 printf("*** Name index corrupt or not existent!\n"); 642 643 if (!gVerbose) 644 return; 645 646 if (!gMissing.IsEmpty()) 647 puts("\n*** Missing inodes:"); 648 649 gMissing.Rewind(); 650 while (gMissing.GetNextEntry(&node) == B_OK) { 651 if (gDumpMissingInodes) 652 dump_inode(node, node->InodeBuffer()); 653 654 Directory *dir = dynamic_cast<Directory *>(node); 655 if (dir) { 656 printf("\ndirectory \"%s\" (%" B_PRId32 ", %d) contents:\n", 657 node->Name(), node->BlockRun().allocation_group, 658 node->BlockRun().start); 659 660 dir->Rewind(); 661 662 char name[B_FILE_NAME_LENGTH]; 663 block_run run; 664 while (dir->GetNextEntry(name, &run) == B_OK) { 665 printf("\t\"%s\" (%" B_PRId32 ", %d, %d)\n", name, 666 run.allocation_group, run.start, run.length); 667 } 668 669 BPlusTree *tree; 670 if (dir->GetTree(&tree) < B_OK) 671 continue; 672 673 uint16 length; 674 off_t offset; 675 676 while (tree->GetNextEntry(name, &length, B_FILE_NAME_LENGTH, 677 &offset) == B_OK) { 678 name[length] = 0; 679 680 run = disk.ToBlockRun(offset); 681 printf("%s: block_run == (%5" B_PRId32 ",%5d,%5d), \"%s\"\n", 682 dir->Name(), run.allocation_group, run.start, run.length, 683 name); 684 } 685 686 //tree->WriteTo(dir); 687 //disk.WriteAt(dir->Offset(),dir->InodeBuffer(),disk.BlockSize()); 688 } 689 690 gMissing.Release(node); 691 } 692 } 693 694 695 void 696 copyInodes(Disk& disk, const char* copyTo) 697 { 698 if (copyTo == NULL) 699 return; 700 701 HashtableInodeSource source(disk); 702 Inode *node; 703 704 int32 count = 0; 705 706 RunSet::iterator iterator = gMainInodes.begin(); 707 for (; iterator != gMainInodes.end(); iterator++) { 708 InodeGetter getter(disk, *iterator); 709 Inode* node = getter.Node(); 710 711 if (node && !node->IsIndex() && !node->IsAttributeDirectory()) 712 node->CopyTo(copyTo, true, &source); 713 714 if ((++count % 500) == 0) 715 fprintf(stderr, "copied %" B_PRId32 " files...\n", count); 716 } 717 718 gMissing.Rewind(); 719 while (gMissing.GetNextEntry(&node) == B_OK) { 720 if (!node->IsIndex() && !node->IsAttributeDirectory()) 721 node->CopyTo(copyTo, true, &source); 722 723 gMissing.Release(node); 724 } 725 } 726 727 728 void 729 usage(char *name) 730 { 731 fprintf(stderr,"usage: %s [-idv] [-r [start-offset] [end-offset]] <device> [recover-to-path]\n" 732 "\t-i\trecreate indices on target disk\n" 733 "\t-d\tdump missing and recreated i-nodes\n" 734 "\t-r\tdisk access in raw mode (use only if the partition table is invalid)\n" 735 "\t-s\trecreate superblock and exit (for experts only, don't use this\n" 736 "\t\tif you don't know what you're doing)\n" 737 "\t-v\tverbose output\n", name); 738 exit(-1); 739 } 740 741 742 int 743 main(int argc, char **argv) 744 { 745 char *fileName = strrchr(argv[0], '/'); 746 fileName = fileName ? fileName + 1 : argv[0]; 747 bool recreateSuperBlockOnly = false; 748 749 off_t startOffset = 0, endOffset = -1; 750 751 puts("Copyright (c) 2001-2008 pinc Software."); 752 753 if (argc < 2 || !strcmp(argv[1], "--help")) 754 usage(fileName); 755 756 while (*++argv) { 757 char *arg = *argv; 758 if (*arg == '-') { 759 while (*++arg && isalpha(*arg)) { 760 switch (arg[0]) { 761 case 'r': 762 { 763 gRawMode = true; 764 765 if (argv[1] && isdigit((argv[1])[0])) { 766 argv++; 767 arg = *argv; 768 769 startOffset = atoll(arg); 770 } 771 if (argv[1] && isdigit((argv[1])[0])) { 772 argv++; 773 arg = *argv; 774 775 endOffset = atoll(arg); 776 } 777 if (endOffset != -1 && endOffset < startOffset) 778 usage(fileName); 779 break; 780 } 781 case 'v': 782 gVerbose = true; 783 break; 784 case 'i': 785 gCreateIndices = true; 786 break; 787 case 'd': 788 gDumpMissingInodes = true; 789 break; 790 case 's': 791 recreateSuperBlockOnly = true; 792 break; 793 } 794 } 795 } else 796 break; 797 } 798 799 Disk disk(argv[0], gRawMode, startOffset, endOffset); 800 if (disk.InitCheck() < B_OK) { 801 fprintf(stderr,"Could not open device or file: %s\n", 802 strerror(disk.InitCheck())); 803 return -1; 804 } 805 806 if (argv[1] != NULL) { 807 dev_t device = dev_for_path(argv[1]); 808 fs_info info; 809 if (fs_stat_dev(device, &info) == B_OK) { 810 if (!strcmp(info.device_name, disk.Path().Path())) { 811 fprintf(stderr,"The source and target device are identical, " 812 "you currently need\n" 813 "to have another disk to recover to, sorry!\n"); 814 return -1; 815 } 816 if ((info.flags & (B_FS_IS_PERSISTENT | B_FS_HAS_ATTR)) 817 != (B_FS_IS_PERSISTENT | B_FS_HAS_ATTR)) { 818 fprintf(stderr, "%s: The target file system (%s) does not have " 819 "the required\n" 820 "\tfunctionality in order to restore all information.\n", 821 kProgramName, info.fsh_name); 822 return -1; 823 } 824 } 825 } 826 827 bool recreatedSuperBlock = false; 828 829 if (disk.ValidateSuperBlock() < B_OK) { 830 fprintf(stderr, "The disk's superblock is corrupt!\n"); 831 if (disk.RecreateSuperBlock() < B_OK) { 832 fprintf(stderr, "Can't recreate the disk's superblock, sorry!\n"); 833 return -1; 834 } 835 recreatedSuperBlock = true; 836 } 837 if (gVerbose) { 838 puts("\n*** The superblock:\n"); 839 dump_super_block(disk.SuperBlock()); 840 } 841 842 if (recreateSuperBlockOnly) { 843 if (!recreatedSuperBlock) { 844 printf("Superblock was valid, no changes made.\n"); 845 return 0; 846 } 847 848 status_t status = disk.WriteAt(512, disk.SuperBlock(), 849 sizeof(disk_super_block)); 850 if (status < B_OK) { 851 fprintf(stderr, "Could not write superblock: %s!\n", 852 strerror(status)); 853 return 1; 854 } 855 return 0; 856 } 857 858 puts("\n*** Collecting inodes..."); 859 860 collectLogInodes(disk); 861 collectRealInodes(disk); 862 863 puts("\n*** Checking Disk Structure Integrity..."); 864 865 checkStructure(disk); 866 867 if (argv[1]) 868 copyInodes(disk, argv[1]); 869 870 //disk.WriteBootBlock(); 871 //disk.BlockBitmap()->CompareWithBackup(); 872 873 gMissing.MakeEmpty(); 874 gLogged.MakeEmpty(); 875 876 return 0; 877 } 878 879