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:
InodeHashtable(int capacity)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
Acquire(Inode * inode)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
Release(Inode * inode)66 void Release(Inode* inode)
67 {
68 inode->ReleaseBuffer();
69 }
70
Get(block_run run)71 Inode* Get(block_run run)
72 {
73 return Acquire((Inode *)fHashtable.GetValue(&run));
74 }
75
Insert(Inode * inode)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
Contains(block_run * key)85 bool Contains(block_run *key)
86 {
87 return fHashtable.ContainsKey(key);
88 }
89
Remove(block_run * key)90 Inode* Remove(block_run *key)
91 {
92 return Acquire((Inode*)fHashtable.Remove(key));
93 }
94
GetNextEntry(Inode ** _inode)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
Rewind()106 void Rewind()
107 {
108 fHashtable.Rewind();
109 }
110
IsEmpty() const111 bool IsEmpty() const
112 {
113 return fHashtable.IsEmpty();
114 }
115
MakeEmpty()116 void MakeEmpty()
117 {
118 fHashtable.MakeEmpty(HASH_EMPTY_NONE, HASH_EMPTY_DELETE);
119 }
120
BlockRunHash(const block_run * run)121 static uint32 BlockRunHash(const block_run *run)
122 {
123 return (run->allocation_group << 16) | run->start;
124 }
125
BlockRunCompare(const block_run * runA,const block_run * runB)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:
InodeGetter(Disk & disk,block_run run)140 InodeGetter(Disk& disk, block_run run)
141 {
142 fInode = Inode::Factory(&disk, run);
143 if (fInode != NULL)
144 fInode->AcquireBuffer();
145 }
146
~InodeGetter()147 ~InodeGetter()
148 {
149 if (fInode != NULL)
150 fInode->ReleaseBuffer();
151 }
152
Node() const153 Inode* Node() const
154 {
155 return fInode;
156 }
157
Detach()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:
HashtableInodeSource(Disk & disk)178 HashtableInodeSource(Disk& disk)
179 :
180 fDisk(disk)
181 {
182 }
183
InodeAt(block_run run)184 virtual Inode *InodeAt(block_run run)
185 {
186 Inode *inode;
187 if ((inode = gLogged.Get(run)) != NULL)
188 return Inode::Factory(&fDisk, inode, false);
189
190 if ((inode = gMissing.Get(run)) != NULL)
191 return Inode::Factory(&fDisk, inode, false);
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
operator <(const block_run & a,const block_run & b)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
collectInodes(Disk & disk,RunSet * set,InodeHashtable * hashTable,off_t start,off_t end)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 %lld, allocation group %lld, %lld 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
collectLogInodes(Disk & disk)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
collectRealInodes(Disk & disk)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 *
getNameIndex(Disk & disk)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
checkDirectoryContents(Disk & disk,Directory * dir)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
checkStructure(Disk & disk)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
copyInodes(Disk & disk,const char * copyTo)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
usage(char * name)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
main(int argc,char ** argv)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