* Copyright 2001-2010 pinc Software. All Rights Reserved.
* Released under the terms of the MIT license.
*/
#include "Disk.h"
#include "BPlusTree.h"
#include "Inode.h"
#include "dump.h"
#include <StringForSize.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
using namespace BPrivate;
void
dump_bplustree(Disk &disk, BPositionIO *file, off_t size, bool hexDump)
{
uint8 *buffer = (uint8 *)malloc(size);
if (buffer == NULL) {
puts("no buffer");
return;
}
if (file->ReadAt(0, buffer, size) != size) {
puts("couldn't read whole file");
return;
}
bplustree_header *header = (bplustree_header *)buffer;
int32 nodeSize = header->node_size;
dump_bplustree_header(header);
bplustree_node *node = (bplustree_node *)(buffer + nodeSize);
while ((addr_t)node < (addr_t)buffer + size) {
printf("\n\n-------------------\n"
"** node at offset: %" B_PRIuADDR "\n** used: %" B_PRId32 " bytes"
"\n", (addr_t)node - (addr_t)buffer, node->Used());
dump_bplustree_node(node, header, &disk);
if (hexDump) {
putchar('\n');
dump_block((char *)node, header->node_size, 0);
}
node = (bplustree_node *)((addr_t)node + nodeSize);
}
}
void
dump_indirect_stream(Disk &disk, bfs_inode *node, bool showOffsets)
{
if (node->data.max_indirect_range == 0)
return;
int32 bytes = node->data.indirect.length * disk.BlockSize();
int32 count = bytes / sizeof(block_run);
block_run runs[count];
off_t offset = node->data.max_direct_range;
ssize_t bytesRead = disk.ReadAt(disk.ToOffset(node->data.indirect),
(uint8 *)runs, bytes);
if (bytesRead < bytes) {
fprintf(stderr, "couldn't read indirect runs: %s\n",
strerror(bytesRead));
return;
}
puts("indirect stream:");
for (int32 i = 0; i < count; i++) {
if (runs[i].IsZero())
return;
printf(" indirect[%04" B_PRId32 "] = ", i);
char buffer[256];
if (showOffsets)
snprintf(buffer, sizeof(buffer), " %16" B_PRIdOFF, offset);
else
buffer[0] = '\0';
dump_block_run("", runs[i], buffer);
offset += runs[i].length * disk.BlockSize();
}
}
void
dump_double_indirect_stream(Disk& disk, bfs_inode* node, bool showOffsets)
{
if (node->data.max_double_indirect_range == 0)
return;
int32 bytes = node->data.double_indirect.length * disk.BlockSize();
int32 count = bytes / sizeof(block_run);
block_run runs[count];
off_t offset = node->data.max_indirect_range;
ssize_t bytesRead = disk.ReadAt(disk.ToOffset(node->data.double_indirect),
(uint8*)runs, bytes);
if (bytesRead < bytes) {
fprintf(stderr, "couldn't read double indirect runs: %s\n",
strerror(bytesRead));
return;
}
puts("double indirect stream:");
for (int32 i = 0; i < count; i++) {
if (runs[i].IsZero())
return;
printf(" double_indirect[%02" B_PRId32 "] = ", i);
dump_block_run("", runs[i], "");
int32 indirectBytes = runs[i].length * disk.BlockSize();
int32 indirectCount = indirectBytes / sizeof(block_run);
block_run indirectRuns[indirectCount];
bytesRead = disk.ReadAt(disk.ToOffset(runs[i]), (uint8*)indirectRuns,
indirectBytes);
if (bytesRead < indirectBytes) {
fprintf(stderr, "couldn't read double indirect runs: %s\n",
strerror(bytesRead));
continue;
}
for (int32 j = 0; j < indirectCount; j++) {
if (indirectRuns[j].IsZero())
break;
printf(" [%04" B_PRId32 "] = ", j);
char buffer[256];
if (showOffsets)
snprintf(buffer, sizeof(buffer), " %16" B_PRIdOFF, offset);
else
buffer[0] = '\0';
dump_block_run("", indirectRuns[j], buffer);
offset += indirectRuns[j].length * disk.BlockSize();
}
}
}
void
list_bplustree(Disk& disk, Directory* directory, off_t size)
{
directory->Rewind();
char name[B_FILE_NAME_LENGTH];
char buffer[512];
uint64 count = 0;
block_run run;
while (directory->GetNextEntry(name, &run) == B_OK) {
snprintf(buffer, sizeof(buffer), " %s", name);
dump_block_run("", run, buffer);
count++;
}
printf("--\n%" B_PRId64 " items.\n", count);
}
void
count_bplustree(Disk& disk, Directory* directory, off_t size)
{
directory->Rewind();
char name[B_FILE_NAME_LENGTH];
uint64 count = 0;
block_run run;
while (directory->GetNextEntry(name, &run) == B_OK)
count++;
printf("%" B_PRId64 " items.\n", count);
}
block_run
parseBlockRun(Disk &disk, char *first, char *last)
{
char *comma;
if (last) {
return block_run::Run(atol(first), atol(last), 1);
} else if ((comma = strchr(first, ',')) != NULL) {
*comma++ = '\0';
return block_run::Run(atol(first), atol(comma));
}
return disk.ToBlockRun(atoll(first));
}
int
main(int argc, char **argv)
{
if (argc < 2 || !strcmp(argv[1], "--help")) {
char *filename = strrchr(argv[0],'/');
fprintf(stderr,"usage: %s [-srib] <device> [allocation_group start]\n"
"\t-s\tdump superblock\n"
"\t-r\tdump root node\n"
" the following options need the allocation_group/start "
"parameters:\n"
"\t-i\tdump inode\n"
"\t-b\tdump b+tree\n"
"\t-c\tlist b+tree leaves\n"
"\t-c\tcount b+tree leaves\n"
"\t-v\tvalidate b+tree\n"
"\t-h\thexdump\n"
"\t-o\tshow disk offsets\n",
filename ? filename + 1 : argv[0]);
return -1;
}
bool dumpRootNode = false;
bool dumpInode = false;
bool dumpSuperBlock = false;
bool dumpBTree = false;
bool listBTree = false;
bool countBTree = false;
bool validateBTree = false;
bool dumpHex = false;
bool showOffsets = false;
while (*++argv) {
char *arg = *argv;
if (*arg == '-') {
while (*++arg && isalpha(*arg)) {
switch (*arg) {
case 's':
dumpSuperBlock = true;
break;
case 'r':
dumpRootNode = true;
break;
case 'i':
dumpInode = true;
break;
case 'b':
dumpBTree = true;
break;
case 'l':
listBTree = true;
break;
case 'c':
countBTree = true;
break;
case 'v':
validateBTree = true;
break;
case 'h':
dumpHex = true;
break;
case 'o':
showOffsets = true;
break;
}
}
} else
break;
}
Disk disk(argv[0]);
if (disk.InitCheck() < B_OK)
{
fprintf(stderr, "Could not open device or file: %s\n", strerror(disk.InitCheck()));
return -1;
}
putchar('\n');
if (!dumpSuperBlock && !dumpRootNode && !dumpInode && !dumpBTree
&& !dumpHex && !listBTree && !countBTree) {
char buffer[16];
printf(" Name:\t\t\t\"%s\"\n", disk.SuperBlock()->name);
printf(" SuperBlock:\t\t%s\n\n",
disk.ValidateSuperBlock() == B_OK ? "valid" : "invalid!");
printf(" Block Size:%*" B_PRIu32 " bytes\n", 23, disk.BlockSize());
string_for_size(disk.NumBlocks() * disk.BlockSize(), buffer,
sizeof(buffer));
printf(" Number of Blocks:%*" B_PRIdOFF "\t%*s\n", 17, disk.NumBlocks(),
16, buffer);
if (disk.BlockBitmap() != NULL) {
string_for_size(disk.BlockBitmap()->UsedBlocks() * disk.BlockSize(),
buffer, sizeof(buffer));
printf(" Used Blocks:%*" B_PRIdOFF "\t%*s\n", 22,
disk.BlockBitmap()->UsedBlocks(), 16, buffer);
string_for_size(disk.BlockBitmap()->FreeBlocks() * disk.BlockSize(),
buffer, sizeof(buffer));
printf(" Free Blocks:%*" B_PRIdOFF "\t%*s\n", 22,
disk.BlockBitmap()->FreeBlocks(), 16, buffer);
}
int32 size
= (disk.AllocationGroups() * disk.SuperBlock()->blocks_per_ag);
string_for_size(disk.BlockSize() * size, buffer, sizeof(buffer));
printf(" Bitmap Blocks:%*" B_PRId32 "\t%*s\n", 20, size, 16, buffer);
printf(" Allocation Group Size:%*" B_PRId32 " blocks\n", 12,
disk.SuperBlock()->blocks_per_ag);
printf(" Allocation Groups:%*" B_PRIu32 "\n\n", 16,
disk.AllocationGroups());
dump_block_run(" Log:\t\t\t", disk.Log());
printf("\t\t\t%s\n\n", disk.SuperBlock()->flags == SUPER_BLOCK_CLEAN
? "cleanly unmounted" : "not unmounted cleanly!");
dump_block_run(" Root Directory:\t", disk.Root());
putchar('\n');
} else if (dumpSuperBlock) {
dump_super_block(disk.SuperBlock());
putchar('\n');
}
if (disk.ValidateSuperBlock() < B_OK) {
fprintf(stderr, "The disk's superblock is corrupt (or it's not a BFS "
"device)!\n");
return 0;
}
if (dumpRootNode) {
bfs_inode inode;
if (disk.ReadAt(disk.ToOffset(disk.Root()), (void *)&inode,
sizeof(bfs_inode)) < B_OK) {
fprintf(stderr,"Could not read root node from disk!\n");
} else {
puts("Root node:\n-----------------------------------------");
dump_inode(NULL, &inode, showOffsets);
dump_indirect_stream(disk, &inode, showOffsets);
putchar('\n');
}
}
char buffer[disk.BlockSize()];
bfs_inode* bfsInode = (bfs_inode*)buffer;
block_run run;
Inode *inode = NULL;
if (dumpInode || dumpBTree || dumpHex || validateBTree || listBTree
|| countBTree) {
if (!argv[1]) {
fprintf(stderr, "The -i/b/f options need the allocation group and "
"starting offset (or the block number) of the node to dump!\n");
return -1;
}
run = parseBlockRun(disk, argv[1], argv[2]);
if (disk.ReadAt(disk.ToOffset(run), buffer, disk.BlockSize()) <= 0) {
fprintf(stderr,"Could not read node from disk!\n");
return -1;
}
inode = Inode::Factory(&disk, bfsInode, false);
if (inode == NULL || inode->InitCheck() < B_OK) {
fprintf(stderr,"Not a valid inode!\n");
delete inode;
inode = NULL;
}
}
if (dumpInode) {
printf("Inode at block %" B_PRIdOFF ":\n------------------------------"
"-----------\n", disk.ToBlock(run));
dump_inode(inode, bfsInode, showOffsets);
dump_indirect_stream(disk, bfsInode, showOffsets);
dump_double_indirect_stream(disk, bfsInode, showOffsets);
dump_small_data(inode);
putchar('\n');
}
if (dumpBTree && inode != NULL) {
printf("B+Tree at block %" B_PRIdOFF ":\n-----------------------------"
"------------\n", disk.ToBlock(run));
if (inode->IsDirectory() || inode->IsAttributeDirectory()) {
dump_bplustree(disk, (Directory*)inode, inode->Size(), dumpHex);
putchar('\n');
} else
fprintf(stderr, "Inode is not a directory!\n");
}
if (listBTree && inode != NULL) {
printf("Directory contents: ------------------------------------------\n");
if (inode->IsDirectory() || inode->IsAttributeDirectory()) {
list_bplustree(disk, (Directory*)inode, inode->Size());
putchar('\n');
} else
fprintf(stderr, "Inode is not a directory!\n");
}
if (countBTree && inode != NULL) {
printf("Count contents: ------------------------------------------\n");
if (inode->IsDirectory() || inode->IsAttributeDirectory()) {
count_bplustree(disk, (Directory*)inode, inode->Size());
putchar('\n');
} else
fprintf(stderr, "Inode is not a directory!\n");
}
if (validateBTree && inode != NULL) {
printf("Validating B+Tree at block %" B_PRIdOFF ":\n------------------"
"-----------------------\n", disk.ToBlock(run));
if (inode->IsDirectory() || inode->IsAttributeDirectory()) {
BPlusTree *tree;
if (((Directory *)inode)->GetTree(&tree) == B_OK) {
if (tree->Validate(true) < B_OK)
puts("B+Tree is corrupt!");
else
puts("B+Tree seems to be okay.");
}
} else
fprintf(stderr, "Inode is not a directory!\n");
}
if (dumpHex) {
printf("Hexdump from inode at block %" B_PRIdOFF ":\n-----------------"
"------------------------\n", disk.ToBlock(run));
dump_block(buffer, disk.BlockSize());
putchar('\n');
}
delete inode;
return 0;
}