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
27extern const char *__progname;
28static const char *kProgramName = __progname;
29
30bool gCreateIndices = false;
31bool gDumpMissingInodes = false;
32bool gRawMode = false;
33bool gVerbose = false;
34
35
36// TODO: add a cache for all inodes
37typedef std::set<block_run> RunSet;
38
39
40class InodeHashtable {
41	public:
42		InodeHashtable(int capacity)
43			:
44			fHashtable(capacity),
45			fLastChecked(0)
46		{
47			fHashtable.SetHashFunction((uint32 (*)(const void *))BlockRunHash);
48			fHashtable.SetCompareFunction((bool (*)(const void *, const void *))
49				BlockRunCompare);
50		}
51
52		Inode* Acquire(Inode* inode)
53		{
54			if (inode == NULL)
55				return NULL;
56
57			status_t status = inode->AcquireBuffer();
58			if (status != B_OK) {
59				fprintf(stderr, "Could not retrieve buffer for inode %"
60					B_PRIdOFF ": %s\n", inode->Offset(), strerror(status));
61				return NULL;
62			}
63			return inode;
64		}
65
66		void Release(Inode* inode)
67		{
68			inode->ReleaseBuffer();
69		}
70
71		Inode* Get(block_run run)
72		{
73			return Acquire((Inode *)fHashtable.GetValue(&run));
74		}
75
76		bool Insert(Inode* inode)
77		{
78			bool success = fHashtable.Put(&inode->BlockRun(), inode);
79			if (success)
80				inode->ReleaseBuffer();
81
82			return success;
83		}
84
85		bool Contains(block_run *key)
86		{
87			return fHashtable.ContainsKey(key);
88		}
89
90		Inode* Remove(block_run *key)
91		{
92			return Acquire((Inode*)fHashtable.Remove(key));
93		}
94
95		status_t GetNextEntry(Inode **_inode)
96		{
97			status_t status = fHashtable.GetNextEntry((void**)_inode);
98			if (status == B_OK) {
99				if (Acquire(*_inode) == NULL)
100					return B_NO_MEMORY;
101			}
102
103			return status;
104		}
105
106		void Rewind()
107		{
108			fHashtable.Rewind();
109		}
110
111		bool IsEmpty() const
112		{
113			return fHashtable.IsEmpty();
114		}
115
116		void MakeEmpty()
117		{
118			fHashtable.MakeEmpty(HASH_EMPTY_NONE, HASH_EMPTY_DELETE);
119		}
120
121		static uint32 BlockRunHash(const block_run *run)
122		{
123			return (run->allocation_group << 16) | run->start;
124		}
125
126		static bool BlockRunCompare(const block_run *runA, const block_run *runB)
127		{
128			return *runA == *runB;
129		}
130
131	private:
132		Hashtable	fHashtable;
133		bigtime_t	fLastChecked;
134		uint32		fPercentUsed;
135};
136
137
138class InodeGetter {
139	public:
140		InodeGetter(Disk& disk, block_run run)
141		{
142			fInode = Inode::Factory(&disk, run);
143			if (fInode != NULL)
144				fInode->AcquireBuffer();
145		}
146
147		~InodeGetter()
148		{
149			if (fInode != NULL)
150				fInode->ReleaseBuffer();
151		}
152
153		Inode* Node() const
154		{
155			return fInode;
156		}
157
158		void Detach()
159		{
160			fInode = NULL;
161		}
162
163	private:
164		Inode*	fInode;
165};
166
167
168RunSet gMainInodes;
169	// contains all inodes found on disk in the general data area
170InodeHashtable gLogged(50);
171	// contains all inodes found in the log area
172InodeHashtable gMissing(50);
173InodeHashtable gMissingEmpty(25);
174
175
176class HashtableInodeSource : public Inode::Source {
177public:
178	HashtableInodeSource(Disk& disk)
179		:
180		fDisk(disk)
181	{
182	}
183
184	virtual Inode *InodeAt(block_run run)
185	{
186		Inode *inode;
187		if ((inode = gLogged.Get(run)) != NULL)
188			return Inode::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
199private:
200	Disk&	fDisk;
201};
202
203
204bool
205operator<(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
212void
213collectInodes(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
290void
291collectLogInodes(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
304void
305collectRealInodes(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
319Directory *
320getNameIndex(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
350void
351checkDirectoryContents(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
424void
425checkStructure(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
696void
697copyInodes(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
729void
730usage(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
743int
744main(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