xref: /haiku/src/bin/bfs_tools/recover.cpp (revision 7d6915b4d08ffe728cd38af02843d5e98ddfe0db)
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