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 %Ld: %s\n", 59 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: %Ld \"%s\"\n", position, node->Name()); 247 248 if (set != NULL) 249 set->insert(node->BlockRun()); 250 else 251 hashTable->Insert(node); 252 253 if (node->IsDirectory()) { 254 directories++; 255 directorySize += node->Size(); 256 } else if (node->IsFile()) { 257 files++; 258 fileSize += node->Size(); 259 } else if (node->IsSymlink()) { 260 symlinks++; 261 } 262 count++; 263 } else if (gVerbose) { 264 printf("\nunrecognized inode:"); 265 dump_inode(&inode, inode.InodeBuffer()); 266 } 267 } 268 position += disk.BlockSize(); 269 } 270 if (system_time() - lastUpdate > 500000) { 271 printf(" block %Ld (%Ld%%), %Ld inodes\33[1A\n", offset, 272 100 * (offset - start) / (end - start), count); 273 lastUpdate = system_time(); 274 } 275 } 276 printf("\n%Ld inodes found.\n", count); 277 278 printf("\n%20Ld directories found (total of %Ld bytes)\n" 279 "%20Ld files found (total of %Ld bytes)\n" 280 "%20Ld symlinks found\n" 281 "--------------------\n" 282 "%20Ld inodes total found.\n", 283 directories, directorySize, files, fileSize, symlinks, count); 284 } 285 286 287 void 288 collectLogInodes(Disk &disk) 289 { 290 // scan log area 291 off_t offset = disk.ToOffset(disk.Log()); 292 off_t end = offset + (disk.Log().length << disk.BlockShift()); 293 294 printf("\nsearching from %Ld to %Ld (log area)\n",offset,end); 295 296 collectInodes(disk, NULL, &gLogged, offset, end); 297 } 298 299 300 void 301 collectRealInodes(Disk &disk) 302 { 303 // first block after bootblock, bitmap, and log 304 off_t offset = disk.ToOffset(disk.Log()) + (disk.Log().length 305 << disk.BlockShift()); 306 off_t end = (off_t)disk.NumBlocks() << disk.BlockShift(); 307 308 printf("\nsearching from %Ld to %Ld (main area)\n", offset, end); 309 310 collectInodes(disk, &gMainInodes, NULL, offset, end); 311 } 312 313 314 Directory * 315 getNameIndex(Disk &disk) 316 { 317 InodeGetter getter(disk, disk.Indices()); 318 Directory *indices = dynamic_cast<Directory *>(getter.Node()); 319 320 block_run run; 321 if (indices != NULL && indices->FindEntry("name", &run) == B_OK) { 322 InodeGetter getter(disk, run); 323 Inode* node = getter.Node(); 324 getter.Detach(); 325 return dynamic_cast<Directory *>(node); 326 } 327 328 // search name index 329 330 RunSet::iterator iterator = gMainInodes.begin(); 331 for (; iterator != gMainInodes.end(); iterator++) { 332 InodeGetter getter(disk, *iterator); 333 Inode* node = getter.Node(); 334 335 if (!node->IsIndex() || node->Name() == NULL) 336 continue; 337 if (!strcmp(node->Name(), "name") && node->Mode() & S_STR_INDEX) 338 return dynamic_cast<Directory *>(node); 339 } 340 341 return NULL; 342 } 343 344 345 void 346 checkDirectoryContents(Disk& disk, Directory *dir) 347 { 348 dir->Rewind(); 349 350 char name[B_FILE_NAME_LENGTH]; 351 block_run run; 352 353 while (dir->GetNextEntry(name, &run) == B_OK) { 354 if (run == dir->BlockRun() || run == dir->Parent() 355 || gMainInodes.find(run) != gMainInodes.end()) 356 continue; 357 358 Inode *missing = gMissing.Get(run); 359 if (missing != NULL) { 360 if (missing->SetName(name) < B_OK) { 361 fprintf(stderr, "setting name of missing node to " 362 "\"%s\" failed!", name); 363 } 364 if (gVerbose) { 365 printf("Set name of missing node (%ld, %d) to \"%s\" (%s)\n", 366 run.allocation_group, run.start, missing->Name(), name); 367 } 368 369 missing->SetParent(dir->BlockRun()); 370 } 371 // if (node->Mode() & S_INDEX_DIR) 372 // { 373 // if (node->Mode() & S_STR_INDEX) 374 // 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); 375 // else 376 // 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); 377 // } 378 else { 379 // missing inode has not been found 380 if (gVerbose) { 381 printf("directory \"%s\" (%ld, %d): node \"%s\" is " 382 "missing (%ld, %d, %d)\n", dir->Name(), 383 dir->BlockRun().allocation_group, 384 dir->BlockRun().start, name, 385 run.allocation_group, run.start, run.length); 386 } 387 388 if ((missing = (Inode *)gLogged.Remove(&run)) != NULL) { 389 // missing inode is in the log 390 if (gVerbose) 391 printf("found missing entry in log!\n"); 392 if (missing->InodeBuffer()->parent != dir->BlockRun()) { 393 if (gVerbose) 394 puts("\tparent directories differ (may be an old version of it), reseting parent."); 395 missing->SetParent(dir->BlockRun()); 396 } 397 if (!gMissing.Insert(missing)) 398 delete missing; 399 } else if (!gMissingEmpty.Contains(&run)) { 400 // not known at all yet 401 Inode *empty = Inode::EmptyInode(&disk, name, 0); 402 if (empty) { 403 empty->SetBlockRun(run); 404 empty->SetParent(dir->BlockRun()); 405 if (gVerbose) 406 printf("\tname = %s\n", empty->Name()); 407 408 if (!gMissingEmpty.Insert(empty)) 409 delete empty; 410 } 411 } 412 } 413 } 414 } 415 416 417 void 418 checkStructure(Disk &disk) 419 { 420 Inode *node; 421 422 off_t count = 0; 423 424 RunSet::iterator iterator = gMainInodes.begin(); 425 for (; iterator != gMainInodes.end(); iterator++) { 426 InodeGetter getter(disk, *iterator); 427 Inode* node = getter.Node(); 428 429 count++; 430 if ((count % 50) == 0) 431 fprintf(stderr, "%Ld inodes processed...\33[1A\n", count); 432 433 if (node->IsDirectory() && !node->IsIndex()) { 434 // check if all entries are in the hashtable 435 checkDirectoryContents(disk, (Directory*)node); 436 } 437 438 // check for the parent directory 439 440 block_run run = node->Parent(); 441 InodeGetter parentGetter(disk, run); 442 Inode *parentNode = parentGetter.Node(); 443 444 Directory *dir = dynamic_cast<Directory *>(parentNode); 445 if (dir || parentNode && (node->Mode() & S_ATTR_DIR)) { 446 // entry has a valid parent directory, so it's assumed to be a valid entry 447 disk.BlockBitmap()->BackupSet(node, true); 448 } else if (node->Mode() & S_ATTR) { 449 if (gVerbose) { 450 printf("attribute \"%s\" at %ld,%d misses its parent.\n", 451 node->Name(), node->BlockRun().allocation_group, 452 node->BlockRun().start); 453 puts("\thandling not yet implemented..."); 454 } 455 } else /*if ((node->Flags() & INODE_DELETED) == 0)*/ { 456 Inode* missing = gMissing.Get(run); 457 dir = dynamic_cast<Directory *>(missing); 458 459 if (missing == NULL) { 460 if (gVerbose) { 461 printf("%s \"%s\" (%ld, %d, mode = %10lo): parent directory is " 462 "missing (%ld, %d, %d), may be deleted!\n", 463 node->IsFile() ? "file" : "node", node->Name(), 464 node->BlockRun().allocation_group, node->BlockRun().start, 465 node->Mode(),run.allocation_group, run.start, run.length); 466 } 467 468 if ((dir = dynamic_cast<Directory *>((Inode *)gLogged.Remove( 469 &run))) != NULL) { 470 if (gVerbose) { 471 printf("found directory \"%s\" in log:\n", dir->Name()); 472 if (dir->Size() > 0) 473 dump_inode(dir, dir->InodeBuffer()); 474 else 475 puts("\tempty inode."); 476 } 477 } else { 478 if (gVerbose) 479 puts("\tcreate parent missing entry"); 480 481 Inode *nameNode = (Inode *)gMissingEmpty.Remove(&run); 482 if (nameNode != NULL) { 483 nameNode->SetMode(S_IFDIR); 484 if (gVerbose) 485 printf("found missing name!\n"); 486 } else { 487 BString parentName; 488 parentName << "__directory " << run.allocation_group 489 << ":" << (int32)run.start; 490 491 nameNode = Inode::EmptyInode(&disk, parentName.String(), 492 S_IFDIR); 493 if (nameNode) { 494 nameNode->SetBlockRun(run); 495 nameNode->SetParent(disk.Root()); 496 } 497 } 498 499 if (nameNode) { 500 dir = new Directory(*nameNode); 501 if (dir->CopyBuffer() < B_OK) 502 puts("could not copy buffer!"); 503 else 504 delete nameNode; 505 } 506 } 507 if (dir) { 508 dir->AcquireBuffer(); 509 510 if (!gMissing.Insert(dir)) { 511 printf("could not add dir!!\n"); 512 delete dir; 513 dir = NULL; 514 } 515 } 516 } else if (missing != NULL && dir == NULL && gVerbose) { 517 printf("%s \"%s\" (%ld, %d, mode = %10lo): parent directory " 518 "found in missing list (%ld, %d, %d), but it's not a dir!\n", 519 node->IsFile() ? "file" : "node", node->Name(), 520 node->BlockRun().allocation_group, node->BlockRun().start, 521 node->Mode(),run.allocation_group, run.start, run.length); 522 } else if (gVerbose) { 523 printf("%s \"%s\" (%ld, %d, mode = %10lo): parent directory " 524 "found in missing list (%ld, %d, %d)!\n", 525 node->IsFile() ? "file" : "node", node->Name(), 526 node->BlockRun().allocation_group, node->BlockRun().start, 527 node->Mode(),run.allocation_group, run.start, run.length); 528 } 529 530 if (dir) { 531 dir->AddEntry(node); 532 dir->ReleaseBuffer(); 533 } 534 } 535 // else 536 // { 537 // printf("node %s\n",node->Name()); 538 // status_t status = dir->Contains(node); 539 // if (status == B_ENTRY_NOT_FOUND) 540 // printf("node \"%s\": parent directory \"%s\" contains no link to this node!\n",node->Name(),dir->Name()); 541 // else if (status != B_OK) 542 // printf("node \"%s\": parent directory \"%s\" error: %s\n",node->Name(),dir->Name(),strerror(status)); 543 // } 544 545 // check for attributes 546 547 run = node->Attributes(); 548 if (!run.IsZero()) { 549 //printf("node \"%s\" (%ld, %d, mode = %010lo): has attribute dir!\n",node->Name(),node->BlockRun().allocation_group,node->BlockRun().start,node->Mode()); 550 551 if (gMainInodes.find(run) == gMainInodes.end()) { 552 if (gVerbose) { 553 printf("node \"%s\": attributes are missing (%ld, %d, %d)\n", 554 node->Name(), run.allocation_group, run.start, run.length); 555 } 556 557 if ((dir = (Directory *)gMissing.Get(run)) != NULL) { 558 if (gVerbose) 559 puts("\tfound in missing"); 560 dir->SetMode(dir->Mode() | S_ATTR_DIR); 561 dir->SetParent(node->BlockRun()); 562 } else { 563 if (gVerbose) 564 puts("\tcreate new!"); 565 566 Inode *empty = Inode::EmptyInode(&disk, NULL, 567 S_IFDIR | S_ATTR_DIR); 568 if (empty) { 569 empty->SetBlockRun(run); 570 empty->SetParent(node->BlockRun()); 571 572 dir = new Directory(*empty); 573 if (dir->CopyBuffer() < B_OK) 574 puts("could not copy buffer!"); 575 else 576 delete empty; 577 578 if (!gMissing.Insert(dir)) { 579 puts("\tcould not add attribute dir"); 580 delete dir; 581 } 582 } 583 } 584 } 585 } 586 } 587 printf("%Ld inodes processed.\n", count); 588 589 Directory *directory = getNameIndex(disk); 590 if (directory != NULL) { 591 puts("\n*** Search names of missing inodes in the name index"); 592 593 BPlusTree *tree; 594 if (directory->GetTree(&tree) == B_OK && tree->Validate(gVerbose) == B_OK) { 595 char name[B_FILE_NAME_LENGTH]; 596 block_run run; 597 directory->Rewind(); 598 while (directory->GetNextEntry(name, &run) >= B_OK) { 599 if ((node = gMissing.Get(run)) == NULL) 600 continue; 601 602 if (gVerbose) { 603 printf(" Node found: %ld:%d\n", run.allocation_group, 604 run.start); 605 } 606 if (node->Name() == NULL || strcmp(node->Name(), name)) { 607 if (gVerbose) { 608 printf("\tnames differ: %s -> %s (indices)\n", 609 node->Name(), name); 610 } 611 node->SetName(name); 612 } 613 } 614 } else 615 printf("\tname index is corrupt!\n"); 616 617 directory->ReleaseBuffer(); 618 } else 619 printf("*** Name index corrupt or not existent!\n"); 620 621 if (!gVerbose) 622 return; 623 624 if (!gMissing.IsEmpty()) 625 puts("\n*** Missing inodes:"); 626 627 gMissing.Rewind(); 628 while (gMissing.GetNextEntry(&node) == B_OK) { 629 if (gDumpMissingInodes) 630 dump_inode(node, node->InodeBuffer()); 631 632 Directory *dir = dynamic_cast<Directory *>(node); 633 if (dir) { 634 printf("\ndirectory \"%s\" (%ld, %d) contents:\n", 635 node->Name(), node->BlockRun().allocation_group, 636 node->BlockRun().start); 637 638 dir->Rewind(); 639 640 char name[B_FILE_NAME_LENGTH]; 641 block_run run; 642 while (dir->GetNextEntry(name, &run) == B_OK) { 643 printf("\t\"%s\" (%ld, %d, %d)\n", name, 644 run.allocation_group, run.start, run.length); 645 } 646 647 BPlusTree *tree; 648 if (dir->GetTree(&tree) < B_OK) 649 continue; 650 651 uint16 length; 652 off_t offset; 653 654 while (tree->GetNextEntry(name, &length, B_FILE_NAME_LENGTH, 655 &offset) == B_OK) { 656 name[length] = 0; 657 658 run = disk.ToBlockRun(offset); 659 printf("%s: block_run == (%5ld,%5d,%5d), \"%s\"\n", dir->Name(), 660 run.allocation_group, run.start, run.length, name); 661 } 662 663 //tree->WriteTo(dir); 664 //disk.WriteAt(dir->Offset(),dir->InodeBuffer(),disk.BlockSize()); 665 } 666 667 gMissing.Release(node); 668 } 669 } 670 671 672 void 673 copyInodes(Disk& disk, const char* copyTo) 674 { 675 if (copyTo == NULL) 676 return; 677 678 HashtableInodeSource source(disk); 679 Inode *node; 680 681 int32 count = 0; 682 683 RunSet::iterator iterator = gMainInodes.begin(); 684 for (; iterator != gMainInodes.end(); iterator++) { 685 InodeGetter getter(disk, *iterator); 686 Inode* node = getter.Node(); 687 688 if (!node->IsIndex() && !node->IsAttributeDirectory()) 689 node->CopyTo(copyTo, &source); 690 691 if ((++count % 500) == 0) 692 fprintf(stderr, "copied %ld files...\n", count); 693 } 694 695 gMissing.Rewind(); 696 while (gMissing.GetNextEntry(&node) == B_OK) { 697 if (!node->IsIndex() && !node->IsAttributeDirectory()) 698 node->CopyTo(copyTo, &source); 699 700 gMissing.Release(node); 701 } 702 } 703 704 705 void 706 usage(char *name) 707 { 708 fprintf(stderr,"usage: %s [-idv] [-r [start-offset] [end-offset]] <device> [recover-to-path]\n" 709 "\t-i\trecreate indices on target disk\n" 710 "\t-d\tdump missing and recreated i-nodes\n" 711 "\t-r\tdisk access in raw mode (use only if the partition table is invalid)\n" 712 "\t-s\trecreate superblock and exit (for experts only, don't use this\n" 713 "\t\tif you don't know what you're doing)\n" 714 "\t-v\tverbose output\n", name); 715 exit(-1); 716 } 717 718 719 int 720 main(int argc, char **argv) 721 { 722 char *fileName = strrchr(argv[0], '/'); 723 fileName = fileName ? fileName + 1 : argv[0]; 724 bool recreateSuperBlockOnly = false; 725 726 off_t startOffset = 0, endOffset = -1; 727 728 puts("Copyright (c) 2001-2008 pinc Software."); 729 730 if (argc < 2 || !strcmp(argv[1], "--help")) 731 usage(fileName); 732 733 while (*++argv) { 734 char *arg = *argv; 735 if (*arg == '-') { 736 while (*++arg && isalpha(*arg)) { 737 switch (arg[0]) { 738 case 'r': 739 { 740 gRawMode = true; 741 742 if (argv[1] && isdigit((argv[1])[0])) { 743 argv++; 744 arg = *argv; 745 746 startOffset = atoll(arg); 747 } 748 if (argv[1] && isdigit((argv[1])[0])) { 749 argv++; 750 arg = *argv; 751 752 endOffset = atoll(arg); 753 } 754 if (endOffset != -1 && endOffset < startOffset) 755 usage(fileName); 756 break; 757 } 758 case 'v': 759 gVerbose = true; 760 break; 761 case 'i': 762 gCreateIndices = true; 763 break; 764 case 'd': 765 gDumpMissingInodes = true; 766 break; 767 case 's': 768 recreateSuperBlockOnly = true; 769 break; 770 } 771 } 772 } else 773 break; 774 } 775 776 Disk disk(argv[0], gRawMode, startOffset, endOffset); 777 if (disk.InitCheck() < B_OK) { 778 fprintf(stderr,"Could not open device or file: %s\n", 779 strerror(disk.InitCheck())); 780 return -1; 781 } 782 783 if (argv[1] != NULL) { 784 dev_t device = dev_for_path(argv[1]); 785 fs_info info; 786 if (fs_stat_dev(device, &info) == B_OK) { 787 if (!strcmp(info.device_name, disk.Path().Path())) { 788 fprintf(stderr,"The source and target device are identical, " 789 "you currently need\n" 790 "to have another disk to recover to, sorry!\n"); 791 return -1; 792 } 793 if ((info.flags & (B_FS_IS_PERSISTENT | B_FS_HAS_ATTR)) 794 != (B_FS_IS_PERSISTENT | B_FS_HAS_ATTR)) { 795 fprintf(stderr, "%s: The target file system (%s) does not have " 796 "the required\n" 797 "\tfunctionality in order to restore all information.\n", 798 kProgramName, info.fsh_name); 799 return -1; 800 } 801 } 802 } 803 804 bool recreatedSuperBlock = false; 805 806 if (disk.ValidateSuperBlock() < B_OK) { 807 fprintf(stderr, "The disk's superblock is corrupt!\n"); 808 if (disk.RecreateSuperBlock() < B_OK) { 809 fprintf(stderr, "Can't recreate the disk's superblock, sorry!\n"); 810 return -1; 811 } 812 recreatedSuperBlock = true; 813 } 814 if (gVerbose) { 815 puts("\n*** The superblock:\n"); 816 dump_super_block(disk.SuperBlock()); 817 } 818 819 if (recreateSuperBlockOnly) { 820 if (!recreatedSuperBlock) { 821 printf("Superblock was valid, no changes made.\n"); 822 return 0; 823 } 824 825 status_t status = disk.WriteAt(512, disk.SuperBlock(), 826 sizeof(disk_super_block)); 827 if (status < B_OK) { 828 fprintf(stderr, "Could not write superblock: %s!\n", 829 strerror(status)); 830 return 1; 831 } 832 return 0; 833 } 834 835 puts("\n*** Collecting inodes..."); 836 837 collectLogInodes(disk); 838 collectRealInodes(disk); 839 840 puts("\n*** Checking Disk Structure Integrity..."); 841 842 checkStructure(disk); 843 844 if (argv[1]) 845 copyInodes(disk, argv[1]); 846 847 //disk.WriteBootBlock(); 848 //disk.BlockBitmap()->CompareWithBackup(); 849 850 gMissing.MakeEmpty(); 851 gLogged.MakeEmpty(); 852 853 return 0; 854 } 855 856