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