/*
 * Copyright (c) 2001-2008 pinc Software. All Rights Reserved.
 * Released under the terms of the MIT license.
 */
 
//!	recovers corrupt BFS disks
 
 
#include <set>
 
#include "Disk.h"
#include "Inode.h"
#include "Hashtable.h"
#include "BPlusTree.h"
#include "dump.h"
 
#include <String.h>
#include <fs_info.h>
 
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
 
 
extern const char *__progname;
static const char *kProgramName = __progname;
 
bool gCreateIndices = false;
bool gDumpMissingInodes = false;
bool gRawMode = false;
bool gVerbose = false;
 
 
// TODO: add a cache for all inodes
typedef std::set<block_run> RunSet;
 
 
class InodeHashtable {
	public:
		InodeHashtable(int capacity)
			:
			fHashtable(capacity),
			fLastChecked(0)
		{
			fHashtable.SetHashFunction((uint32 (*)(const void *))BlockRunHash);
			fHashtable.SetCompareFunction((bool (*)(const void *, const void *))
				BlockRunCompare);
		}
 
		Inode* Acquire(Inode* inode)
		{
			if (inode == NULL)
				return NULL;
 
			status_t status = inode->AcquireBuffer();
			if (status != B_OK) {
				fprintf(stderr, "Could not retrieve buffer for inode %"
					B_PRIdOFF ": %s\n", inode->Offset(), strerror(status));
				return NULL;
			}
			return inode;
		}
 
		void Release(Inode* inode)
		{
			inode->ReleaseBuffer();
		}
 
		Inode* Get(block_run run)
		{
			return Acquire((Inode *)fHashtable.GetValue(&run));
		}
 
		bool Insert(Inode* inode)
		{
			bool success = fHashtable.Put(&inode->BlockRun(), inode);
			if (success)
				inode->ReleaseBuffer();
 
			return success;
		}
 
		bool Contains(block_run *key)
		{
			return fHashtable.ContainsKey(key);
		}
 
		Inode* Remove(block_run *key)
		{
			return Acquire((Inode*)fHashtable.Remove(key));
		}
 
		status_t GetNextEntry(Inode **_inode)
		{
			status_t status = fHashtable.GetNextEntry((void**)_inode);
			if (status == B_OK) {
				if (Acquire(*_inode) == NULL)
					return B_NO_MEMORY;
			}
 
			return status;
		}
 
		void Rewind()
		{
			fHashtable.Rewind();
		}
 
		bool IsEmpty() const
		{
			return fHashtable.IsEmpty();
		}
 
		void MakeEmpty()
		{
			fHashtable.MakeEmpty(HASH_EMPTY_NONE, HASH_EMPTY_DELETE);
		}
 
		static uint32 BlockRunHash(const block_run *run)
		{
			return (run->allocation_group << 16) | run->start;
		}
 
		static bool BlockRunCompare(const block_run *runA, const block_run *runB)
		{
			return *runA == *runB;
		}
 
	private:
		Hashtable	fHashtable;
		bigtime_t	fLastChecked;
		uint32		fPercentUsed;
};
 
 
class InodeGetter {
	public:
		InodeGetter(Disk& disk, block_run run)
		{
			fInode = Inode::Factory(&disk, run);
			if (fInode != NULL)
				fInode->AcquireBuffer();
		}
 
		~InodeGetter()
		{
			if (fInode != NULL)
				fInode->ReleaseBuffer();
		}
 
		Inode* Node() const
		{
			return fInode;
		}
 
		void Detach()
		{
			fInode = NULL;
		}
 
	private:
		Inode*	fInode;
};
 
 
RunSet gMainInodes;
	// contains all inodes found on disk in the general data area
InodeHashtable gLogged(50);
	// contains all inodes found in the log area
InodeHashtable gMissing(50);
InodeHashtable gMissingEmpty(25);
 
 
class HashtableInodeSource : public Inode::Source {
public:
	HashtableInodeSource(Disk& disk)
		:
		fDisk(disk)
	{
	}
 
	virtual Inode *InodeAt(block_run run)
	{
		Inode *inode;
		if ((inode = gLogged.Get(run)) != NULL)
			return Inode::Factory(&fDisk, inode, false);
 
		if ((inode = gMissing.Get(run)) != NULL)
			return Inode::Factory(&fDisk, inode, false);
 
		if (gMainInodes.find(run) == gMainInodes.end())
			return NULL;
 
		return Inode::Factory(&fDisk, run);
	}
 
private:
	Disk&	fDisk;
};
 
 
bool
operator<(const block_run& a, const block_run& b)
{
	return a.allocation_group < b.allocation_group
		|| (a.allocation_group == b.allocation_group && a.start < b.start);
}
 
 
void
collectInodes(Disk& disk, RunSet* set, InodeHashtable* hashTable, off_t start,
	off_t end)
{
	char buffer[8192];
	Inode inode(&disk, (bfs_inode *)buffer, false);
 
	off_t directories = 0LL;
	off_t directorySize = 0LL;
	off_t files = 0LL;
	off_t fileSize = 0LL;
	off_t symlinks = 0LL;
	off_t count = 0LL;
 
	off_t position = start;
	bigtime_t lastUpdate = system_time();
 
	for (off_t offset = start; offset < end; offset += sizeof(buffer)) {
		if (disk.ReadAt(offset, buffer, sizeof(buffer)) < B_OK) {
			fprintf(stderr, "could not read from device!\n");
			break;
		}
 
		//if ((offset % (disk.BlockSize() << disk.SuperBlock()->ag_shift)) == 0)
		//	printf("reading block %Ld, allocation group %Ld, %Ld inodes...\33[1A\n", offset / disk.BlockSize(),offset / (disk.BlockSize() << disk.SuperBlock()->ag_shift), count);
 
		for (uint32 i = 0; i < sizeof(buffer); i += disk.BlockSize()) {
			inode.SetTo((bfs_inode *)(buffer + i));
			if (inode.InitCheck() == B_OK) {
				if (inode.Flags() & INODE_DELETED)
					continue;
 
				Inode *node = Inode::Factory(&disk, &inode);
				if (node != NULL) {
					if (gVerbose)
						printf("  node: %" B_PRIdOFF " \"%s\"\n", position,
							node->Name());
 
					if (set != NULL)
						set->insert(node->BlockRun());
					else
						hashTable->Insert(node);
 
					if (node->IsDirectory()) {
						directories++;
						directorySize += node->Size();
					} else if (node->IsFile()) {
						files++;
						fileSize += node->Size();
					} else if (node->IsSymlink()) {
						symlinks++;
					}
					count++;
				} else if (gVerbose) {
					printf("\nunrecognized inode:");
					dump_inode(&inode, inode.InodeBuffer());
				}
			}
			position += disk.BlockSize();
		}
		if (system_time() - lastUpdate > 500000) {
			printf("  block %" B_PRIdOFF " (%" B_PRIdOFF "%%), %" B_PRIdOFF
				" inodes\33[1A\n", offset,
				100 * (offset - start) / (end - start), count);
			lastUpdate = system_time();
		}
	}
	printf("\n%" B_PRIdOFF " inodes found.\n", count);
 
	printf("\n%20" B_PRIdOFF " directories found (total of %" B_PRIdOFF
		" bytes)\n%20" B_PRIdOFF " files found (total of %" B_PRIdOFF
		" bytes)\n%20" B_PRIdOFF " symlinks found\n"
		"--------------------\n"
		"%20" B_PRIdOFF " inodes total found.\n",
		directories, directorySize, files, fileSize, symlinks, count);
}
 
 
void
collectLogInodes(Disk &disk)
{
	// scan log area
	off_t offset = disk.ToOffset(disk.Log());
	off_t end = offset + (disk.Log().length << disk.BlockShift());
 
	printf("\nsearching from %" B_PRIdOFF " to %" B_PRIdOFF " (log area)\n",
		offset, end);
 
	collectInodes(disk, NULL, &gLogged, offset, end);
}
 
 
void
collectRealInodes(Disk &disk)
{
	// first block after bootblock, bitmap, and log
	off_t offset = disk.ToOffset(disk.Log()) + (disk.Log().length
		<< disk.BlockShift());
	off_t end = (off_t)disk.NumBlocks() << disk.BlockShift();
 
	printf("\nsearching from %" B_PRIdOFF " to %" B_PRIdOFF " (main area)\n",
		offset, end);
 
	collectInodes(disk, &gMainInodes, NULL, offset, end);
}
 
 
Directory *
getNameIndex(Disk &disk)
{
	InodeGetter getter(disk, disk.Indices());
	Directory *indices = dynamic_cast<Directory *>(getter.Node());
 
	block_run run;
	if (indices != NULL && indices->FindEntry("name", &run) == B_OK) {
		InodeGetter getter(disk, run);
		Inode* node = getter.Node();
		getter.Detach();
		return dynamic_cast<Directory *>(node);
	}
 
	// search name index
 
	RunSet::iterator iterator = gMainInodes.begin();
	for (; iterator != gMainInodes.end(); iterator++) {
		InodeGetter getter(disk, *iterator);
		Inode* node = getter.Node();
 
		if (!node || !node->IsIndex() || node->Name() == NULL)
			continue;
		if (!strcmp(node->Name(), "name") && node->Mode() & S_STR_INDEX)
			return dynamic_cast<Directory *>(node);
	}
 
	return NULL;
}
 
 
void
checkDirectoryContents(Disk& disk, Directory *dir)
{
	dir->Rewind();
 
	char name[B_FILE_NAME_LENGTH];
	block_run run;
 
	while (dir->GetNextEntry(name, &run) == B_OK) {
		if (run == dir->BlockRun() || run == dir->Parent()
			|| gMainInodes.find(run) != gMainInodes.end())
			continue;
 
		Inode *missing = gMissing.Get(run);
		if (missing != NULL) {
			if (missing->SetName(name) < B_OK) {
				fprintf(stderr, "setting name of missing node to "
					"\"%s\" failed!", name);
			}
			if (gVerbose) {
				printf("Set name of missing node (%" B_PRId32
					", %d) to \"%s\" (%s)\n",
					run.allocation_group, run.start, missing->Name(), name);
			}
 
			missing->SetParent(dir->BlockRun());
		}
//		if (node->Mode() & S_INDEX_DIR)
//		{
//			if (node->Mode() & S_STR_INDEX)
//				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);
//			else
//				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);
//		}
		else {
			// missing inode has not been found
			if (gVerbose) {
				printf("directory \"%s\" (%" B_PRId32 ", %d): node \"%s\" is "
					"missing (%" B_PRId32 ", %d, %d)\n", dir->Name(),
					dir->BlockRun().allocation_group,
					dir->BlockRun().start, name,
					run.allocation_group, run.start, run.length);
			}
 
			if ((missing = (Inode *)gLogged.Remove(&run)) != NULL) {
				// missing inode is in the log
				if (gVerbose)
					printf("found missing entry in log!\n");
				if (missing->InodeBuffer()->parent != dir->BlockRun()) {
					if (gVerbose)
						puts("\tparent directories differ (may be an old "
							"version of it), reseting parent.");
					missing->SetParent(dir->BlockRun());
				}
				if (!gMissing.Insert(missing))
					delete missing;
			} else if (!gMissingEmpty.Contains(&run)) {
				// not known at all yet
				Inode *empty = Inode::EmptyInode(&disk, name, 0);
				if (empty) {
					empty->SetBlockRun(run);
					empty->SetParent(dir->BlockRun());
					if (gVerbose)
						printf("\tname = %s\n", empty->Name());
 
					if (!gMissingEmpty.Insert(empty))
						delete empty;
				}
			}
		}
	}
}
 
 
void
checkStructure(Disk &disk)
{
	off_t count = 0;
	Inode* node;
 
	RunSet::iterator iterator = gMainInodes.begin();
	for (; iterator != gMainInodes.end(); iterator++) {
		InodeGetter getter(disk, *iterator);
		node = getter.Node();
 
		count++;
		if ((count % 50) == 0)
			fprintf(stderr, "%" B_PRIdOFF " inodes processed...\33[1A\n", count);
 
		if (node == NULL)
			continue;
 
		if (node->IsDirectory() && !node->IsIndex()) {
			// check if all entries are in the hashtable
			Directory* directory = dynamic_cast<Directory*>(node);
			if (directory != NULL)
				checkDirectoryContents(disk, directory);
			else {
				printf("Node \"%s\" at %" B_PRId32
					",%d looks like a directory, but isn't.\n",
					node->Name(), node->BlockRun().allocation_group,
					node->BlockRun().start);
			}
		}
 
		// check for the parent directory
 
		block_run run = node->Parent();
		InodeGetter parentGetter(disk, run);
		Inode *parentNode = parentGetter.Node();
 
		Directory *dir = dynamic_cast<Directory *>(parentNode);
		if (dir || (parentNode && (node->Mode() & S_ATTR_DIR))) {
			// entry has a valid parent directory, so it's assumed to be a valid entry
			disk.BlockBitmap()->BackupSet(node, true);
		} else if (node->Mode() & S_ATTR) {
			if (gVerbose) {
				printf("attribute \"%s\" at %" B_PRId32 ",%d misses its parent.\n",
					node->Name(), node->BlockRun().allocation_group,
					node->BlockRun().start);
				puts("\thandling not yet implemented...");
			}
		} else /*if ((node->Flags() & INODE_DELETED) == 0)*/ {
			Inode* missing = gMissing.Get(run);
			dir = dynamic_cast<Directory *>(missing);
 
			if (missing == NULL) {
				if (gVerbose) {
					printf("%s \"%s\" (%" B_PRId32 ", %d, mode = %10" B_PRIo32
						"): parent directory is missing (%" B_PRId32
						", %d, %d), may be deleted!\n",
						node->IsFile() ? "file" : "node", node->Name(),
						node->BlockRun().allocation_group,
						node->BlockRun().start,
						node->Mode(), run.allocation_group, run.start,
						run.length);
				}
 
				if ((dir = dynamic_cast<Directory *>((Inode *)gLogged.Remove(
						&run))) != NULL) {
					if (gVerbose) {
						printf("found directory \"%s\" in log:\n", dir->Name());
						if (dir->Size() > 0)
							dump_inode(dir, dir->InodeBuffer());
						else
							puts("\tempty inode.");
					}
				} else {
					if (gVerbose)
						puts("\tcreate parent missing entry");
 
					Inode *nameNode = (Inode *)gMissingEmpty.Remove(&run);
					if (nameNode != NULL) {
						nameNode->SetMode(S_IFDIR);
						if (gVerbose)
							printf("found missing name!\n");
					} else {
						BString parentName;
						parentName << "__directory " << run.allocation_group
							<< ":" << (int32)run.start;
 
						nameNode = Inode::EmptyInode(&disk, parentName.String(),
							S_IFDIR);
						if (nameNode) {
							nameNode->SetBlockRun(run);
							nameNode->SetParent(disk.Root());
						}
					}
 
					if (nameNode) {
						dir = new Directory(*nameNode);
						if (dir->CopyBuffer() < B_OK)
							puts("could not copy buffer!");
						else
							delete nameNode;
					}
				}
				if (dir) {
					dir->AcquireBuffer();
 
					if (!gMissing.Insert(dir)) {
						printf("could not add dir!!\n");
						delete dir;
						dir = NULL;
					}
				}
			} else if (missing != NULL && dir == NULL && gVerbose) {
				printf("%s \"%s\" (%" B_PRId32 ", %d, mode = %10" B_PRIo32
					"): parent directory found in missing list (%" B_PRId32
					", %d, %d), but it's not a dir!\n",
					node->IsFile() ? "file" : "node", node->Name(),
					node->BlockRun().allocation_group, node->BlockRun().start,
					node->Mode(), run.allocation_group, run.start, run.length);
			} else if (gVerbose) {
				printf("%s \"%s\" (%" B_PRId32 ", %d, mode = %10" B_PRIo32
					"): parent directory found in missing list (%" B_PRId32
					", %d, %d)!\n",
					node->IsFile() ? "file" : "node", node->Name(),
					node->BlockRun().allocation_group, node->BlockRun().start,
					node->Mode(), run.allocation_group, run.start, run.length);
			}
 
			if (dir) {
				dir->AddEntry(node);
				dir->ReleaseBuffer();
			}
		}
//			else
//			{
//				printf("node %s\n", node->Name());
//				status_t status = dir->Contains(node);
//				if (status == B_ENTRY_NOT_FOUND)
//					printf("node \"%s\": parent directory \"%s\" contains no link to this node!\n",node->Name(),dir->Name());
//				else if (status != B_OK)
//					printf("node \"%s\": parent directory \"%s\" error: %s\n",node->Name(),dir->Name(),strerror(status));
//			}
 
		// check for attributes
 
		run = node->Attributes();
		if (!run.IsZero()) {
			//printf("node \"%s\" (%ld, %d, mode = %010lo): has attribute dir!\n",node->Name(),node->BlockRun().allocation_group,node->BlockRun().start,node->Mode());
 
			if (gMainInodes.find(run) == gMainInodes.end()) {
				if (gVerbose) {
					printf("node \"%s\": attributes are missing (%" B_PRId32
						", %d, %d)\n", node->Name(), run.allocation_group,
						run.start, run.length);
				}
 
				if ((dir = (Directory *)gMissing.Get(run)) != NULL) {
					if (gVerbose)
						puts("\tfound in missing");
					dir->SetMode(dir->Mode() | S_ATTR_DIR);
					dir->SetParent(node->BlockRun());
				} else {
					if (gVerbose)
						puts("\tcreate new!");
 
					Inode *empty = Inode::EmptyInode(&disk, NULL,
						S_IFDIR | S_ATTR_DIR);
					if (empty) {
						empty->SetBlockRun(run);
						empty->SetParent(node->BlockRun());
 
						dir = new Directory(*empty);
						if (dir->CopyBuffer() < B_OK)
							puts("could not copy buffer!");
						else
							delete empty;
 
						if (!gMissing.Insert(dir)) {
							puts("\tcould not add attribute dir");
							delete dir;
						}
					}
				}
			}
		}
	}
	printf("%" B_PRIdOFF " inodes processed.\n", count);
 
	Directory *directory = getNameIndex(disk);
	if (directory != NULL) {
		puts("\n*** Search names of missing inodes in the name index");
 
		BPlusTree *tree;
		if (directory->GetTree(&tree) == B_OK && tree->Validate(gVerbose) == B_OK) {
			char name[B_FILE_NAME_LENGTH];
			block_run run;
			directory->Rewind();
			while (directory->GetNextEntry(name, &run) >= B_OK) {
				if ((node = gMissing.Get(run)) == NULL)
					continue;
 
				if (gVerbose) {
					printf("  Node found: %" B_PRId32 ":%d\n",
						run.allocation_group, run.start);
				}
				if (node->Name() == NULL || strcmp(node->Name(), name)) {
					if (gVerbose) {
						printf("\tnames differ: %s -> %s (indices)\n",
							node->Name(), name);
					}
					node->SetName(name);
				}
			}
		} else
			printf("\tname index is corrupt!\n");
 
		directory->ReleaseBuffer();
	} else
		printf("*** Name index corrupt or not existent!\n");
 
	if (!gVerbose)
		return;
 
	if (!gMissing.IsEmpty())
		puts("\n*** Missing inodes:");
 
	gMissing.Rewind();
	while (gMissing.GetNextEntry(&node) == B_OK) {
		if (gDumpMissingInodes)
			dump_inode(node, node->InodeBuffer());
 
		Directory *dir = dynamic_cast<Directory *>(node);
		if (dir) {
			printf("\ndirectory \"%s\" (%" B_PRId32 ", %d) contents:\n",
				node->Name(), node->BlockRun().allocation_group,
				node->BlockRun().start);
 
			dir->Rewind();
 
			char name[B_FILE_NAME_LENGTH];
			block_run run;
			while (dir->GetNextEntry(name, &run) == B_OK) {
				printf("\t\"%s\" (%" B_PRId32 ", %d, %d)\n", name,
					run.allocation_group, run.start, run.length);
			}
 
			BPlusTree *tree;
			if (dir->GetTree(&tree) < B_OK)
				continue;
 
			uint16 length;
			off_t offset;
 
			while (tree->GetNextEntry(name, &length, B_FILE_NAME_LENGTH,
					&offset) == B_OK) {
				name[length] = 0;
 
				run = disk.ToBlockRun(offset);
				printf("%s: block_run == (%5" B_PRId32 ",%5d,%5d), \"%s\"\n",
					dir->Name(), run.allocation_group, run.start, run.length,
					name);
			}
 
			//tree->WriteTo(dir);
			//disk.WriteAt(dir->Offset(),dir->InodeBuffer(),disk.BlockSize());
		}
 
		gMissing.Release(node);
	}
}
 
 
void
copyInodes(Disk& disk, const char* copyTo)
{
	if (copyTo == NULL)
		return;
 
	HashtableInodeSource source(disk);
	Inode *node;
 
	int32 count = 0;
 
	RunSet::iterator iterator = gMainInodes.begin();
	for (; iterator != gMainInodes.end(); iterator++) {
		InodeGetter getter(disk, *iterator);
		Inode* node = getter.Node();
 
		if (node && !node->IsIndex() && !node->IsAttributeDirectory())
			node->CopyTo(copyTo, true, &source);
 
		if ((++count % 500) == 0)
			fprintf(stderr, "copied %" B_PRId32 " files...\n", count);
	}
 
	gMissing.Rewind();
	while (gMissing.GetNextEntry(&node) == B_OK) {
		if (!node->IsIndex() && !node->IsAttributeDirectory())
			node->CopyTo(copyTo, true, &source);
 
		gMissing.Release(node);
	}
}
 
 
void
usage(char *name)
{
	fprintf(stderr,"usage: %s [-idv] [-r [start-offset] [end-offset]] <device> [recover-to-path]\n"
		"\t-i\trecreate indices on target disk\n"
		"\t-d\tdump missing and recreated i-nodes\n"
		"\t-r\tdisk access in raw mode (use only if the partition table is invalid)\n"
		"\t-s\trecreate superblock and exit (for experts only, don't use this\n"
		"\t\tif you don't know what you're doing)\n"
		"\t-v\tverbose output\n", name);
	exit(-1);
}
 
 
int
main(int argc, char **argv)
{
	char *fileName = strrchr(argv[0], '/');
	fileName = fileName ? fileName + 1 : argv[0];
	bool recreateSuperBlockOnly = false;
 
	off_t startOffset = 0, endOffset = -1;
 
	puts("Copyright (c) 2001-2008 pinc Software.");
 
	if (argc < 2 || !strcmp(argv[1], "--help"))
		usage(fileName);
 
	while (*++argv) {
		char *arg = *argv;
		if (*arg == '-') {
			while (*++arg && isalpha(*arg)) {
				switch (arg[0]) {
					case 'r':
					{
						gRawMode = true;
 
						if (argv[1] && isdigit((argv[1])[0])) {
							argv++;
							arg = *argv;
 
							startOffset = atoll(arg);
						}
						if (argv[1] && isdigit((argv[1])[0])) {
							argv++;
							arg = *argv;
 
							endOffset = atoll(arg);
						}
						if (endOffset != -1 && endOffset < startOffset)
							usage(fileName);
						break;
					}
					case 'v':
						gVerbose = true;
						break;
					case 'i':
						gCreateIndices = true;
						break;
					case 'd':
						gDumpMissingInodes = true;
						break;
					case 's':
						recreateSuperBlockOnly = true;
						break;
				}
			}
		} else
			break;
	}
 
	Disk disk(argv[0], gRawMode, startOffset, endOffset);
	if (disk.InitCheck() < B_OK) {
		fprintf(stderr,"Could not open device or file: %s\n",
			strerror(disk.InitCheck()));
		return -1;
	}
 
	if (argv[1] != NULL) {
		dev_t device = dev_for_path(argv[1]);
		fs_info info;
		if (fs_stat_dev(device, &info) == B_OK) {
			if (!strcmp(info.device_name, disk.Path().Path())) {
				fprintf(stderr,"The source and target device are identical, "
					"you currently need\n"
					"to have another disk to recover to, sorry!\n");
				return -1;
			}
			if ((info.flags & (B_FS_IS_PERSISTENT | B_FS_HAS_ATTR))
					!= (B_FS_IS_PERSISTENT | B_FS_HAS_ATTR)) {
				fprintf(stderr, "%s: The target file system (%s) does not have "
					"the required\n"
					"\tfunctionality in order to restore all information.\n",
					kProgramName, info.fsh_name);
				return -1;
			}
		}
	}
 
	bool recreatedSuperBlock = false;
 
	if (disk.ValidateSuperBlock() < B_OK) {
		fprintf(stderr, "The disk's superblock is corrupt!\n");
		if (disk.RecreateSuperBlock() < B_OK) {
			fprintf(stderr, "Can't recreate the disk's superblock, sorry!\n");
			return -1;
		}
		recreatedSuperBlock = true;
	}
	if (gVerbose) {
		puts("\n*** The superblock:\n");
		dump_super_block(disk.SuperBlock());
	}
 
	if (recreateSuperBlockOnly) {
		if (!recreatedSuperBlock) {
			printf("Superblock was valid, no changes made.\n");
			return 0;
		}
 
		status_t status = disk.WriteAt(512, disk.SuperBlock(),
			sizeof(disk_super_block));
		if (status < B_OK) {
			fprintf(stderr, "Could not write superblock: %s!\n",
				strerror(status));
			return 1;
		}
		return 0;
	}
 
	puts("\n*** Collecting inodes...");
 
	collectLogInodes(disk);
	collectRealInodes(disk);
 
	puts("\n*** Checking Disk Structure Integrity...");
 
	checkStructure(disk);
 
	if (argv[1])
		copyInodes(disk, argv[1]);
 
	//disk.WriteBootBlock();
	//disk.BlockBitmap()->CompareWithBackup();
 
	gMissing.MakeEmpty();
	gLogged.MakeEmpty();
 
	return 0;
}
 

V629 Consider inspecting the 'disk.Log().length << disk.BlockShift()' expression. Bit shifting of the 32-bit value with a subsequent expansion to the 64-bit type.

V629 Consider inspecting the 'disk.Log().length << disk.BlockShift()' expression. Bit shifting of the 32-bit value with a subsequent expansion to the 64-bit type.

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fPercentUsed.

V757 It is possible that an incorrect variable is compared with nullptr after type conversion using 'dynamic_cast'. Check lines: 474, 476.