* Copyright (c) 2002-2009 pinc Software. All Rights Reserved.
* Released under the terms of the MIT license.
*/
#include "Bitmap.h"
#include "Disk.h"
#include "Inode.h"
#include "Hashtable.h"
#include "BPlusTree.h"
#include "dump.h"
#include <fs_info.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <errno.h>
void scanNodes(Disk& disk, Directory* directory, const char* name,
const block_run& checkForRun);
char gEscape[3] = {0x1b, '[', 0};
off_t gCount = 1;
bool
checkForBlockRunIntersection(const block_run& check, const block_run& against)
{
if (check.allocation_group == against.allocation_group
&& check.start + check.length > against.start
&& check.start < against.start + against.length)
return true;
return false;
}
bool
checkNode(Disk &disk, Inode *inode, block_run checkForRun)
{
if (checkForBlockRunIntersection(inode->BlockRun(), checkForRun))
return true;
if (inode->IsSymlink() && (inode->Flags() & INODE_LONG_SYMLINK) == 0)
return false;
const data_stream* data = &inode->InodeBuffer()->data;
if (data->max_direct_range == 0)
return false;
for (int32 i = 0; i < NUM_DIRECT_BLOCKS; i++) {
if (data->direct[i].IsZero())
break;
if (checkForBlockRunIntersection(data->direct[i], checkForRun))
return true;
}
if (data->max_indirect_range == 0 || data->indirect.IsZero())
return false;
if (checkForBlockRunIntersection(data->indirect, checkForRun))
return true;
DataStream *stream = dynamic_cast<DataStream *>(inode);
if (stream == NULL)
return false;
int32 bytes = data->indirect.length << disk.BlockShift();
block_run* indirect = (block_run*)malloc(bytes);
if (indirect == NULL)
return false;
if (disk.ReadAt(disk.ToOffset(data->indirect), indirect, bytes) <= 0)
return false;
int32 runs = bytes / sizeof(block_run);
for (int32 i = 0; i < runs; i++) {
if (indirect[i].IsZero())
break;
if (checkForBlockRunIntersection(indirect[i], checkForRun))
return true;
}
free(indirect);
if (data->max_double_indirect_range == 0 || data->double_indirect.IsZero())
return false;
if (checkForBlockRunIntersection(data->double_indirect, checkForRun))
return true;
return false;
}
void
scanNode(Disk& disk, Inode* inode, const char* name,
const block_run& checkForRun)
{
if (checkNode(disk, inode, checkForRun)) {
printf("Inode at (%ld, %u, %u) \"%s\" intersects\n",
inode->BlockRun().allocation_group, inode->BlockRun().start,
inode->BlockRun().length, name);
}
if (!inode->Attributes().IsZero()) {
Inode *attributeDirectory = Inode::Factory(&disk,
inode->Attributes());
if (attributeDirectory != NULL) {
scanNodes(disk,
dynamic_cast<Directory *>(attributeDirectory), "(attr-dir)",
checkForRun);
}
delete attributeDirectory;
}
}
void
scanNodes(Disk& disk, Directory* directory, const char* directoryName,
const block_run& checkForRun)
{
if (directory == NULL)
return;
scanNode(disk, directory, directoryName, checkForRun);
directory->Rewind();
char name[B_FILE_NAME_LENGTH];
block_run run;
while (directory->GetNextEntry(name, &run) == B_OK) {
if (!strcmp(name, ".") || !strcmp(name, ".."))
continue;
if (++gCount % 50 == 0)
printf(" %7Ld%s1A\n", gCount, gEscape);
Inode *inode = Inode::Factory(&disk, run);
if (inode != NULL) {
if (inode->IsDirectory()) {
scanNodes(disk, static_cast<Directory *>(inode), name,
checkForRun);
} else
scanNode(disk, inode, name, checkForRun);
delete inode;
} else {
printf(" Directory \"%s\" (%ld, %d) points to corrupt inode \"%s\" "
"(%ld, %d)\n", directory->Name(),
directory->BlockRun().allocation_group,directory
->BlockRun().start,
name, run.allocation_group, run.start);
}
}
}
void
scanNodes(Disk& disk, const block_run& checkForRun, bool scanAll)
{
Directory* root = (Directory*)Inode::Factory(&disk, disk.Root());
puts("Scanning nodes (this will take some time)...");
if (root == NULL || root->InitCheck() != B_OK) {
fprintf(stderr," Could not open root directory!\n");
return;
}
scanNodes(disk, root, "(root)", checkForRun);
delete root;
if (scanAll) {
Directory* indices = (Directory*)Inode::Factory(&disk, disk.Indices());
puts("Scanning index nodes (this will take some time)...");
scanNodes(disk, indices, "(indices)", checkForRun);
delete indices;
}
printf(" %7Ld nodes found.\n", gCount);
}
void
testBitmap(Disk& disk, const block_run& run)
{
Bitmap bitmap;
status_t status = bitmap.SetTo(&disk);
if (status != B_OK) {
fprintf(stderr, "Bitmap initialization failed: %s\n", strerror(status));
return;
}
printf("Block bitmap sees block %lld as %s.\n", disk.ToBlock(run),
bitmap.UsedAt(disk.ToBlock(run)) ? "used" : "free");
}
block_run
parseBlockRun(Disk& disk, char* first, char* last)
{
char* comma;
if (last != NULL)
return block_run::Run(atol(first), atol(last), 1);
if ((comma = strchr(first, ',')) != NULL) {
*comma++ = '\0';
return block_run::Run(atol(first), atol(comma));
}
return disk.ToBlockRun(atoll(first));
}
void
printUsage(char* tool)
{
char* filename = strrchr(tool, '/');
fprintf(stderr, "usage: %s <device> <allocation_group> <start>\n",
filename ? filename + 1 : tool);
}
int
main(int argc, char** argv)
{
puts("Copyright (c) 2001-2009 pinc Software.");
char* toolName = argv[0];
if (argc < 2 || !strcmp(argv[1], "--help")) {
printUsage(toolName);
return -1;
}
bool scanAll = false;
while (*++argv) {
char *arg = *argv;
if (*arg == '-') {
while (*++arg && isalpha(*arg)) {
switch (*arg) {
case 'a':
scanAll = true;
break;
default:
printUsage(toolName);
return -1;
}
}
} else
break;
}
Disk disk(argv[0]);
status_t status = disk.InitCheck();
if (status < B_OK) {
fprintf(stderr, "Could not open device or file \"%s\": %s\n",
argv[0], strerror(status));
return -1;
}
if (disk.ValidateSuperBlock() != B_OK) {
fprintf(stderr, "The disk's superblock is corrupt!\n");
return -1;
}
if (!argv[1]) {
fprintf(stderr, "Need the allocation group and starting offset (or the "
"block number) of the block to search for!\n");
return -1;
}
block_run run = parseBlockRun(disk, argv[1], argv[2]);
off_t block = disk.ToBlock(run);
putchar('\n');
testBitmap(disk, run);
if (checkForBlockRunIntersection(disk.Log(), run)) {
printf("Log area (%lu.%u.%u) intersects\n",
disk.Log().allocation_group, disk.Log().start,
disk.Log().length);
} else if (block < 1) {
printf("Superblock intersects\n");
} else if (block < 1 + disk.BitmapSize()) {
printf("Block bitmap intersects (start %d, end %lu)\n", 1,
disk.BitmapSize());
} else
scanNodes(disk, run, scanAll);
return 0;
}