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