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
26extern const char *__progname;
27static const char *kProgramName = __progname;
28
29bool gCreateIndices = false;
30bool gDumpMissingInodes = false;
31bool gRawMode = false;
32bool gVerbose = false;
33
34
35// TODO: add a cache for all inodes
36typedef std::set<block_run> RunSet;
37
38
39class 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
137class 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
167RunSet gMainInodes;
168	// contains all inodes found on disk in the general data area
169InodeHashtable gLogged(50);
170	// contains all inodes found in the log area
171InodeHashtable gMissing(50);
172InodeHashtable gMissingEmpty(25);
173
174
175class HashtableInodeSource : public Inode::Source {
176public:
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
198private:
199	Disk&	fDisk;
200};
201
202
203bool
204operator<(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
211void
212collectInodes(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
287void
288collectLogInodes(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
300void
301collectRealInodes(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
314Directory *
315getNameIndex(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
345void
346checkDirectoryContents(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
417void
418checkStructure(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
672void
673copyInodes(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
705void
706usage(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
719int
720main(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