Imported BFS-Tools (without docs), added bfsinfo to the image.

* The sources are unchanged, and most of it doesn't follow Haiku's coding
  style -- stuff that I updated over the years might also have seen a coding
  style update.
* This was also the basis on which Haiku's BFS has been written.
* In the long run, the stuff in 'lib' that is already part of BFS should
  be removed, and used directly from BFS itself.
* I haven't yet added any other of the tools to the image. Not sure if they
  are ready for inclusion yet; 'recover' could also be renamed to reflect that
  it is BFS specific.
This commit is contained in:
Axel Dörfler 2012-03-26 22:41:58 +02:00
parent 8a7256ae14
commit 647cff2e59
26 changed files with 7642 additions and 2 deletions

View File

@ -27,7 +27,7 @@ if $(HAIKU_ATA_STACK) = 1 {
IDE_ONLY = "" ;
}
SYSTEM_BIN = "[" addattr alert arp base64 basename bash bc beep
SYSTEM_BIN = "[" addattr alert arp base64 basename bash bc beep bfsinfo
cal cat catattr checkfs checkitout chgrp chmod chop chown chroot cksum clear
clockconfig cmp collectcatkeys comm compress copyattr CortexAddOnHost cp
csplit cut date dc dd desklink df diff diff3 dircolors dirname

View File

@ -241,7 +241,7 @@ DoCatalogs filepanel
SubInclude HAIKU_TOP src bin addattr ;
SubInclude HAIKU_TOP src bin bash ;
SubInclude HAIKU_TOP src bin bc ;
SubInclude HAIKU_TOP src bin mail_utils ;
SubInclude HAIKU_TOP src bin bfs_tools ;
SubInclude HAIKU_TOP src bin compress ;
SubInclude HAIKU_TOP src bin consoled ;
SubInclude HAIKU_TOP src bin coreutils ;
@ -261,6 +261,7 @@ SubInclude HAIKU_TOP src bin listdev ;
SubInclude HAIKU_TOP src bin locale ;
SubInclude HAIKU_TOP src bin makebootable ;
#SubInclude HAIKU_TOP src bin makeudfimage ;
SubInclude HAIKU_TOP src bin mail_utils ;
SubInclude HAIKU_TOP src bin mkdos ;
SubInclude HAIKU_TOP src bin mkfs ;
SubInclude HAIKU_TOP src bin multiuser ;

18
src/bin/bfs_tools/Jamfile Normal file
View File

@ -0,0 +1,18 @@
SubDir HAIKU_TOP src bin bfs_tools ;
SetSubDirSupportedPlatformsBeOSCompatible ;
SubDirHdrs $(HAIKU_TOP) src bin bfs_tools lib ;
local haiku-utils_rsrc = [ FGristFiles haiku-utils.rsrc ] ;
StdBinCommands
bfsinfo.cpp
chkindex.cpp
bfswhich.cpp
recover.cpp
: libbfs_tools.a be : $(haiku-utils_rsrc
;
SubInclude HAIKU_TOP src bin bfs_tools lib ;

32
src/bin/bfs_tools/TODO Normal file
View File

@ -0,0 +1,32 @@
BFS-Tools ToDo
--
0.7 development release
- done!
inbetween (0.8 release?) and more:
- try to find a way to perform special actions on a cache miss (like a sanity check
for b+tree nodes)
- Have an option in chkindex which let it try if there is an inode at that point
- if files will be saved to the same disk, make sure you reserve their space on the disk's
bitmap first (in case someone run chkbfs) [you currently can't recover to the same
partition (and it makes sense until you can select the files to recover)]
- have an option that recovers only certain files (like those with a specific
path, or file extension)
- try to make the Cache class easier to use -> like the one in BFS!
- incorporate changes from the BFS BPlusTree (BPlusTree::Find(), TreeIterator), duplicates, etc.
- test the B+tree code with hand-corrupted trees
- try to unify the BFS BPlusTree code to only maintain *one* file
- recreate indices on target disk (all) -> chkindex should get that functionality
1.0 final release
- GUI
- the name index scanner could use its own tree iterator functions, which directly
visit every single node and use some heuristics to determine the node type
- better cache buffer management in the Disk class? Reading from an image is much
faster than reading from a real disk currently...
- reduce memory overhead: use just the block_run references instead of real Inodes
in the hashtables (not in the missing table, of course)
- sort hashtables before copying after block_run

View File

@ -0,0 +1,307 @@
/*
* Copyright 2001-2010 pinc Software. All Rights Reserved.
*/
//! Dumps various information about BFS volumes.
#include "Disk.h"
#include "BPlusTree.h"
#include "Inode.h"
#include "dump.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
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 ((int32)node < (int32)buffer + size) {
printf("\n\n-------------------\n"
"** node at offset: %ld\n** used: %ld bytes\n",
int32(node) - int32(buffer), node->Used());
dump_bplustree_node(node, header, &disk);
if (hexDump) {
putchar('\n');
dump_block((char *)node, header->node_size, 0);
}
node = (bplustree_node *)(int32(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[%02ld] = ", i);
char buffer[256];
if (showOffsets)
snprintf(buffer, sizeof(buffer), " %16lld", offset);
else
buffer[0] = '\0';
dump_block_run("", runs[i], buffer);
offset += runs[i].length * disk.BlockSize();
}
}
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)
{
puts("Copyright (c) 2001-2010 pinc Software.");
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 super block\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-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 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 '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) {
printf(" Name:\t\t\t\"%s\"\n", disk.SuperBlock()->name);
printf(" (disk is %s)\n\n",
disk.ValidateSuperBlock() == B_OK ? "valid" : "invalid!!");
printf(" Block Size:\t\t%ld bytes\n", disk.BlockSize());
printf(" Number of Blocks:\t%12Lu\t%10g MB\n", disk.NumBlocks(),
disk.NumBlocks() * disk.BlockSize() / (1024.0*1024));
if (disk.BlockBitmap() != NULL) {
printf(" Used Blocks:\t\t%12Lu\t%10g MB\n",
disk.BlockBitmap()->UsedBlocks(),
disk.BlockBitmap()->UsedBlocks() * disk.BlockSize()
/ (1024.0*1024));
printf(" Free Blocks:\t\t%12Lu\t%10g MB\n",
disk.BlockBitmap()->FreeBlocks(),
disk.BlockBitmap()->FreeBlocks() * disk.BlockSize()
/ (1024.0*1024));
}
int32 size
= (disk.AllocationGroups() * disk.SuperBlock()->blocks_per_ag);
printf(" Bitmap Size:\t\t%ld bytes (%ld blocks, %ld per allocation "
"group)\n", disk.BlockSize() * size, size,
disk.SuperBlock()->blocks_per_ag);
printf(" Allocation Groups:\t%lu\n\n", disk.AllocationGroups());
dump_block_run(" Log:\t\t\t", disk.Log());
printf(" (was %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 super block 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()];
block_run run;
Inode *inode = NULL;
if (dumpInode || dumpBTree || dumpHex || validateBTree) {
// Set the block_run to the right value (as specified on the command
// line)
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, (bfs_inode *)buffer, 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 %Ld:\n-----------------------------------------"
"\n", disk.ToBlock(run));
dump_inode(inode, (bfs_inode *)buffer, showOffsets);
dump_indirect_stream(disk, (bfs_inode *)buffer, showOffsets);
dump_small_data(inode);
putchar('\n');
}
if (dumpBTree && inode) {
printf("B+Tree at block %Ld:\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 (validateBTree && inode) {
printf("Validating B+Tree at block %Ld:\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 %Ld:\n-----------------------------"
"------------\n", disk.ToBlock(run));
dump_block(buffer, disk.BlockSize());
putchar('\n');
}
delete inode;
return 0;
}

View File

@ -0,0 +1,319 @@
/*
* Copyright (c) 2002-2009 pinc Software. All Rights Reserved.
*/
//! Finds out to which file(s) a particular block belongs
#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)
{
// check the inode space itself
if (checkForBlockRunIntersection(inode->BlockRun(), checkForRun))
return true;
// the data stream of symlinks is no real data stream
if (inode->IsSymlink() && (inode->Flags() & INODE_LONG_SYMLINK) == 0)
return false;
// direct blocks
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;
}
// indirect blocks
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;
// load indirect blocks
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);
// double indirect blocks
if (data->max_double_indirect_range == 0 || data->double_indirect.IsZero())
return false;
if (checkForBlockRunIntersection(data->double_indirect, checkForRun))
return true;
// TODO: to be implemented...
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 %Ld 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 super block is corrupt!\n");
return -1;
}
// Set the block_run to the right value (as specified on the command line)
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("Super Block 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;
}

View File

@ -0,0 +1,523 @@
/*
* Copyright (c) 2001-2008 pinc Software. All Rights Reserved.
*/
//! sanity and completeness check for indices
#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>
char gEscape[3] = {0x1b, '[', 0};
off_t gCount = 1;
bool gDoNotCheckForFiles = false;
bool gDoNotCheckIndex = false;
bool gCheckAll = false;
// we just want to know which inodes exist, so don't remember anything
// along the position of the inode.
class BlockRunHashtable : public Hashtable
{
public:
BlockRunHashtable(int capacity)
: Hashtable(capacity)
{
SetHashFunction((uint32 (*)(const void *))BlockRunHash);
SetCompareFunction((bool (*)(const void *, const void *))BlockRunCompare);
}
bool Contains(block_run *run)
{
return ContainsKey((void *)run);
}
bool Put(block_run &run)
{
block_run *value = (block_run *)malloc(sizeof(block_run));
if (!value)
return false;
memcpy(value,&run,sizeof(block_run));
if (Hashtable::Put(value,value))
return true;
free(value);
return false;
}
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;
}
};
BlockRunHashtable gHashtable(1000);
int
compareBlockRuns(const block_run *a, const block_run *b)
{
int cmp = (int)a->allocation_group - (int)b->allocation_group;
if (cmp == 0)
cmp = (int)a->start - (int)b->start;
return cmp;
}
void
collectFiles(Disk &disk,Directory *directory)
{
if (directory == NULL)
return;
directory->Rewind();
char name[B_FILE_NAME_LENGTH];
block_run run;
while (directory->GetNextEntry(name,&run) >= B_OK)
{
if (!strcmp(name,".") || !strcmp(name,".."))
continue;
gHashtable.Put(run);
if (++gCount % 50 == 0)
printf(" %7Ld%s1A\n",gCount,gEscape);
Inode *inode = Inode::Factory(&disk,run);
if (inode != NULL)
{
if (inode->IsDirectory())
collectFiles(disk,static_cast<Directory *>(inode));
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
collectFiles(Disk &disk)
{
Directory *root = (Directory *)Inode::Factory(&disk,disk.Root());
puts("Collecting files (this will take some time)...");
if (root == NULL || root->InitCheck() < B_OK)
{
fprintf(stderr," Could not open root directory!\n");
return;
}
collectFiles(disk,root);
printf(" %7Ld files found.\n",gCount);
delete root;
}
void
checkIndexForNonExistingFiles(Disk &disk,BPlusTree &tree)
{
char name[B_FILE_NAME_LENGTH];
uint16 length;
off_t offset;
while (tree.GetNextEntry(name,&length,B_FILE_NAME_LENGTH,&offset) == B_OK)
{
name[length] = 0;
block_run run = disk.ToBlockRun(offset);
if (!gHashtable.Contains(&run))
{
printf(" inode at (%ld, %d), offset %Ld, doesn't exist!",run.allocation_group,run.start,offset);
switch (tree.Type())
{
case BPLUSTREE_STRING_TYPE:
printf(" (string = \"%s\")",name);
break;
case BPLUSTREE_INT32_TYPE:
printf(" (int32 = %ld)",*(int32 *)&name);
break;
case BPLUSTREE_UINT32_TYPE:
printf(" (uint32 = %lu)",*(uint32 *)&name);
break;
case BPLUSTREE_INT64_TYPE:
printf(" (int64 = %Ld)",*(int64 *)&name);
break;
case BPLUSTREE_UINT64_TYPE:
printf(" (uint64 = %Lu)",*(uint64 *)&name);
break;
case BPLUSTREE_FLOAT_TYPE:
printf(" (float = %g)",*(float *)&name);
break;
case BPLUSTREE_DOUBLE_TYPE:
printf(" (double = %g)",*(double *)&name);
break;
}
putchar('\n');
}
}
}
void
checkFiles(Disk &disk,BPlusTree &tree,char *attribute)
{
block_run *runs = (block_run *)malloc(gCount * sizeof(block_run));
if (runs == NULL)
{
fprintf(stderr," Not enough memory!\n");
return;
}
// copy hashtable to array
block_run *run = NULL;
int32 index = 0;
gHashtable.Rewind();
while (gHashtable.GetNextEntry((void **)&run) == B_OK)
{
runs[index++] = *run;
}
// sort array to speed up disk access
qsort(runs,index,sizeof(block_run),(int (*)(const void *,const void *))compareBlockRuns);
bool sizeIndex = !strcmp(attribute,"size");
bool nameIndex = !strcmp(attribute,"name");
bool modifiedIndex = !strcmp(attribute,"last_modified");
char key[B_FILE_NAME_LENGTH];
uint16 keyLength = 0;
Inode *inode = NULL;
for (int32 i = 0;i < index;i++)
{
if (i % 50 == 0)
printf(" %7ld%s1A\n",i,gEscape);
delete inode;
inode = Inode::Factory(&disk,runs[i]);
if (inode == NULL || inode->InitCheck() < B_OK)
{
fprintf(stderr," inode at (%ld, %d) is corrupt!\n",runs[i].allocation_group,runs[i].start);
delete inode;
continue;
}
// check indices not based on standard attributes
if (sizeIndex)
{
if (inode->IsDirectory())
continue;
memcpy(key,&inode->InodeBuffer()->data.size,sizeof(off_t));
keyLength = sizeof(off_t);
}
else if (nameIndex)
{
strcpy(key,inode->Name());
keyLength = strlen(key);
}
else if (modifiedIndex)
{
if (inode->IsDirectory())
continue;
memcpy(key,&inode->InodeBuffer()->last_modified_time,sizeof(off_t));
keyLength = sizeof(off_t);
}
else // iterate through all attributes to find the right one (damn slow, sorry...)
{
inode->RewindAttributes();
char name[B_FILE_NAME_LENGTH];
uint32 type;
void *data;
size_t length;
bool found = false;
while (inode->GetNextAttribute(name,&type,&data,&length) == B_OK)
{
if (!strcmp(name,attribute))
{
strncpy(key,(char *)data,B_FILE_NAME_LENGTH - 1);
key[B_FILE_NAME_LENGTH - 1] = '\0';
keyLength = length > B_FILE_NAME_LENGTH ? B_FILE_NAME_LENGTH : length;
found = true;
break;
}
}
if (!found)
continue;
}
off_t value;
if (tree.Find((uint8 *)key,keyLength,&value) < B_OK)
{
if (*inode->Name())
fprintf(stderr," inode at (%ld, %d) name \"%s\" is not in index!\n",runs[i].allocation_group,runs[i].start,inode->Name());
else
{
// inode is obviously deleted!
block_run parent = inode->Parent();
Directory *directory = (Directory *)Inode::Factory(&disk,parent);
if (directory != NULL && directory->InitCheck() == B_OK)
{
BPlusTree *parentTree;
if (directory->GetTree(&parentTree) == B_OK)
{
char name[B_FILE_NAME_LENGTH];
uint16 length;
off_t offset,searchOffset = disk.ToBlock(runs[i]);
bool found = false;
while (parentTree->GetNextEntry(name,&length,B_FILE_NAME_LENGTH,&offset) == B_OK)
{
if (offset == searchOffset)
{
fprintf(stderr," inode at (%ld, %d) name \"%s\" was obviously deleted, but the parent \"%s\" still contains it!\n",runs[i].allocation_group,runs[i].start,name,directory->Name());
found = true;
break;
}
}
if (!found)
fprintf(stderr," inode at (%ld, %d) was obviously deleted, and the parent \"%s\" obviously doesn't contain it anymore!\n",runs[i].allocation_group,runs[i].start,directory->Name());
}
else
fprintf(stderr," inode at (%ld, %d) was obviously deleted, but the parent \"%s\" is invalid and still contains it!\n",runs[i].allocation_group,runs[i].start,directory->Name());
}
else
{
// not that this would be really possible... - but who knows
fprintf(stderr," inode at (%ld, %d) is not in index and has invalid parent!\n",runs[i].allocation_group,runs[i].start);
}
delete directory;
}
}
else
{
if (bplustree_node::LinkType(value) == BPLUSTREE_NODE)
{
if (disk.ToBlockRun(value) != runs[i])
fprintf(stderr," offset in index and inode offset doesn't match for inode \"%s\" at (%ld, %d)\n",inode->Name(),runs[i].allocation_group,runs[i].start);
}
else
{
// search duplicates
char name[B_FILE_NAME_LENGTH];
uint16 length;
off_t offset;
bool found = false,duplicates = false;
//puts("++");
tree.Rewind();
while (tree.GetNextEntry(name,&length,B_FILE_NAME_LENGTH,&offset) == B_OK)
{
//printf("search for = %ld, key = %ld -> value = %Ld (%ld, %d)\n",*(int32 *)&key,*(int32 *)&name,offset,disk.ToBlockRun(offset).allocation_group,disk.ToBlockRun(offset).start);
if (keyLength == length && !memcmp(key,name,keyLength))
{
duplicates = true;
if (disk.ToBlockRun(offset) == runs[i])
{
found = true;
break;
}
}
//else if (duplicates)
// break;
}
if (!found)
{
printf(" inode \"%s\" at (%ld, %d) not found in duplicates!\n",inode->Name(),runs[i].allocation_group,runs[i].start);
// return;
}
}
}
}
delete inode;
printf(" %7Ld files processed.\n",gCount);
}
int
checkIndex(Disk &disk,char *attribute,block_run &run,bool collect)
{
Directory *index = (Directory *)Inode::Factory(&disk,run);
status_t status;
if (index == NULL || (status = index->InitCheck()) < B_OK)
{
fprintf(stderr," Could not get index directory for \"%s\": %s!\n",attribute,index ? strerror(status) : "not found/corrupted");
return -1;
}
printf("\nCheck \"%s\" index's on-disk structure...\n",attribute);
//dump_inode(index->InodeBuffer());
BPlusTree *tree;
if (index->GetTree(&tree) < B_OK || tree->Validate(true) < B_OK)
{
fprintf(stderr," B+Tree of index \"%s\" seems to be corrupt!\n",attribute);
//return -1;
}
if (collect && (!gDoNotCheckIndex || !gDoNotCheckForFiles))
collectFiles(disk);
if (!gDoNotCheckIndex)
{
printf("Check for non-existing files in index \"%s\"...\n",attribute);
checkIndexForNonExistingFiles(disk,*tree);
}
if (!gDoNotCheckForFiles)
{
printf("Check for files not in index \"%s\" (this may take even more time)...\n",attribute);
checkFiles(disk,*tree,attribute);
}
return 0;
}
void
printUsage(char *tool)
{
char *filename = strrchr(tool,'/');
fprintf(stderr,"usage: %s [-ifa] index-name\n"
"\t-i\tdo not check for non-existing files in the index\n"
"\t-f\tdo not check if all the files are in the index\n"
"\t-a\tcheck all indices (could take some weeks...)\n"
,filename ? filename + 1 : tool);
}
int
main(int argc,char **argv)
{
puts("Copyright (c) 2001-2008 pinc Software.");
char *toolName = argv[0];
if (argc < 2 || !strcmp(argv[1],"--help"))
{
printUsage(toolName);
return -1;
}
while (*++argv)
{
char *arg = *argv;
if (*arg == '-')
{
while (*++arg && isalpha(*arg))
{
switch (*arg)
{
case 'i':
gDoNotCheckIndex = true;
break;
case 'f':
gDoNotCheckForFiles = true;
break;
case 'a':
gCheckAll = true;
break;
}
}
}
else
break;
}
char *attribute = argv[0];
if (!gCheckAll && attribute == NULL)
{
printUsage(toolName);
return -1;
}
dev_t device = dev_for_path(".");
if (device < B_OK)
{
fprintf(stderr,"Could not find device for current location: %s\n",strerror(device));
return -1;
}
fs_info info;
if (fs_stat_dev(device,&info) < B_OK)
{
fprintf(stderr,"Could not get stats for device: %s\n",strerror(errno));
return -1;
}
Disk disk(info.device_name);
status_t status;
if ((status = disk.InitCheck()) < B_OK)
{
fprintf(stderr,"Could not open device or file \"%s\": %s\n",info.device_name,strerror(status));
return -1;
}
if (disk.ValidateSuperBlock() < B_OK)
{
fprintf(stderr,"The disk's super block is corrupt!\n");
return -1;
}
Directory *indices = (Directory *)Inode::Factory(&disk,disk.Indices());
if (indices == NULL || (status = indices->InitCheck()) < B_OK)
{
fprintf(stderr," Could not get indices directory: %s!\n",indices ? strerror(status) : "not found/corrupted");
delete indices;
return -1;
}
BPlusTree *tree;
if (indices->GetTree(&tree) < B_OK || tree->Validate() < B_OK)
{
fprintf(stderr," Indices B+Tree seems to be corrupt!\n");
delete indices;
return -1;
}
block_run run;
if (gCheckAll)
{
putchar('\n');
collectFiles(disk);
char name[B_FILE_NAME_LENGTH];
while (indices->GetNextEntry(name,&run) >= B_OK)
checkIndex(disk,name,run,false);
}
else if (indices->FindEntry(attribute,&run) == B_OK)
checkIndex(disk,attribute,run,true);
else
fprintf(stderr," Could not find index directory for \"%s\"!\n",attribute);
delete indices;
gHashtable.MakeEmpty(HASH_EMPTY_NONE,HASH_EMPTY_FREE);
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,291 @@
#ifndef B_PLUS_TREE_H
#define B_PLUS_TREE_H
/* BPlusTree - BFS B+Tree implementation
**
** Copyright 2001 pinc Software. All Rights Reserved.
*/
#include <List.h>
#include "Cache.h"
#include "bfs.h"
class BPositionIO;
//****************** on-disk structures ********************
#define BPLUSTREE_NULL -1LL
#define BPLUSTREE_FREE -2LL
struct bplustree_header
{
uint32 magic;
uint32 node_size;
uint32 max_number_of_levels;
uint32 data_type;
off_t root_node_pointer;
off_t free_node_pointer;
off_t maximum_size;
inline bool IsValidLink(off_t link);
};
#define BPLUSTREE_MAGIC 0x69f6c2e8
#define BPLUSTREE_NODE_SIZE 1024
#define BPLUSTREE_MAX_KEY_LENGTH 256
#define BPLUSTREE_MIN_KEY_LENGTH 1
enum bplustree_types {
BPLUSTREE_STRING_TYPE = 0,
BPLUSTREE_INT32_TYPE = 1,
BPLUSTREE_UINT32_TYPE = 2,
BPLUSTREE_INT64_TYPE = 3,
BPLUSTREE_UINT64_TYPE = 4,
BPLUSTREE_FLOAT_TYPE = 5,
BPLUSTREE_DOUBLE_TYPE = 6
};
struct bplustree_node {
off_t left_link;
off_t right_link;
off_t overflow_link;
uint16 all_key_count;
uint16 all_key_length;
inline uint16 *KeyLengths() const;
inline off_t *Values() const;
inline uint8 *Keys() const;
inline int32 Used() const;
uint8 *KeyAt(int32 index,uint16 *keyLength) const;
uint8 CountDuplicates(off_t offset,bool isFragment) const;
off_t DuplicateAt(off_t offset,bool isFragment,int8 index) const;
static inline uint8 LinkType(off_t link);
static inline off_t FragmentOffset(off_t link);
};
#define BPLUSTREE_NODE 0
#define BPLUSTREE_DUPLICATE_NODE 2
#define BPLUSTREE_DUPLICATE_FRAGMENT 3
//**************************************
enum bplustree_traversing {
BPLUSTREE_FORWARD = 1,
BPLUSTREE_BACKWARD = -1,
BPLUSTREE_BEGIN = 0,
BPLUSTREE_END = 1
};
template<class T> class Stack;
class BPlusTree;
class NodeCache : public Cache<off_t> {
public:
NodeCache(BPlusTree *);
virtual Cacheable *NewCacheable(off_t offset);
bplustree_node *Get(off_t offset);
// void SetOffset(bplustree_node *,off_t offset);
protected:
BPlusTree *fTree;
};
class BPlusTree {
public:
BPlusTree(int32 keyType, int32 nodeSize = BPLUSTREE_NODE_SIZE,
bool allowDuplicates = true);
BPlusTree(BPositionIO *stream, bool allowDuplicates = true);
BPlusTree();
~BPlusTree();
status_t SetTo(int32 keyType,int32 nodeSize = BPLUSTREE_NODE_SIZE,bool allowDuplicates = true);
status_t SetTo(BPositionIO *stream,bool allowDuplicates = true);
status_t InitCheck();
status_t Validate(bool verbose = false);
int32 Type() const { return fHeader->data_type; }
status_t Rewind();
status_t Goto(int8 to);
status_t GetNextEntry(void *key,uint16 *keyLength,uint16 maxLength,off_t *value);
status_t GetPreviousEntry(void *key,uint16 *keyLength,uint16 maxLength,off_t *value);
status_t Insert(uint8 *key, uint16 keyLength, off_t value);
status_t Insert(const char *key, off_t value);
status_t Insert(int32 key, off_t value);
status_t Insert(uint32 key, off_t value);
status_t Insert(int64 key, off_t value);
status_t Insert(uint64 key, off_t value);
status_t Insert(float key, off_t value);
status_t Insert(double key, off_t value);
status_t Find(uint8 *key, uint16 keyLength, off_t *value);
status_t WriteTo(BPositionIO *stream);
void SetHoldChanges(bool hold);
private:
// needed for searching
struct node_and_key {
off_t nodeOffset;
uint16 keyIndex;
};
void Initialize(int32 nodeSize);
void SetCurrentNode(bplustree_node *node,off_t offset,int8 to = BPLUSTREE_BEGIN);
status_t Traverse(int8 direction,void *key,uint16 *keyLength,uint16 maxLength,off_t *value);
int32 CompareKeys(const void *key1, int keylength1, const void *key2, int keylength2);
status_t FindKey(bplustree_node *node, uint8 *key, uint16 keyLength, uint16 *index = NULL, off_t *next = NULL);
status_t SeekDown(Stack<node_and_key> &stack, uint8 *key, uint16 keyLength);
status_t SplitNode(bplustree_node *node, off_t nodeOffset, uint16 *_keyIndex, uint8 *key, uint16 *_keyLength, off_t *_value);
void InsertKey(bplustree_node *node, uint8 *key, uint16 keyLength, off_t value, uint16 index);
status_t InsertDuplicate(bplustree_node *node,uint16 index);
bool CheckNode(bplustree_node *node);
private:
friend NodeCache;
bplustree_node *Node(off_t nodeoffset,bool check = true);
private:
BPositionIO *fStream;
bplustree_header *fHeader;
int32 fNodeSize;
bool fAllowDuplicates;
status_t fStatus;
NodeCache fCache;
off_t fCurrentNodeOffset; // traverse position
int32 fCurrentKey;
off_t fDuplicateNode;
uint16 fDuplicate, fNumDuplicates;
bool fIsFragment;
};
// inline functions
inline status_t BPlusTree::Rewind()
{
return Goto(BPLUSTREE_BEGIN);
}
inline status_t BPlusTree::GetNextEntry(void *key,uint16 *keyLength,uint16 maxLength,off_t *value)
{
return Traverse(BPLUSTREE_FORWARD,key,keyLength,maxLength,value);
}
inline status_t BPlusTree::GetPreviousEntry(void *key,uint16 *keyLength,uint16 maxLength,off_t *value)
{
return Traverse(BPLUSTREE_BACKWARD,key,keyLength,maxLength,value);
}
inline status_t BPlusTree::Insert(const char *key,off_t value)
{
if (fHeader->data_type != BPLUSTREE_STRING_TYPE)
return B_BAD_TYPE;
return Insert((uint8 *)key, strlen(key), value);
}
inline status_t BPlusTree::Insert(int32 key, off_t value)
{
if (fHeader->data_type != BPLUSTREE_INT32_TYPE)
return B_BAD_TYPE;
return Insert((uint8 *)&key, sizeof(key), value);
}
inline status_t BPlusTree::Insert(uint32 key, off_t value)
{
if (fHeader->data_type != BPLUSTREE_UINT32_TYPE)
return B_BAD_TYPE;
return Insert((uint8 *)&key, sizeof(key), value);
}
inline status_t BPlusTree::Insert(int64 key, off_t value)
{
if (fHeader->data_type != BPLUSTREE_INT64_TYPE)
return B_BAD_TYPE;
return Insert((uint8 *)&key, sizeof(key), value);
}
inline status_t BPlusTree::Insert(uint64 key, off_t value)
{
if (fHeader->data_type != BPLUSTREE_UINT64_TYPE)
return B_BAD_TYPE;
return Insert((uint8 *)&key, sizeof(key), value);
}
inline status_t BPlusTree::Insert(float key, off_t value)
{
if (fHeader->data_type != BPLUSTREE_FLOAT_TYPE)
return B_BAD_TYPE;
return Insert((uint8 *)&key, sizeof(key), value);
}
inline status_t BPlusTree::Insert(double key, off_t value)
{
if (fHeader->data_type != BPLUSTREE_DOUBLE_TYPE)
return B_BAD_TYPE;
return Insert((uint8 *)&key, sizeof(key), value);
}
/************************ bplustree_header inline functions ************************/
// #pragma mark -
inline bool bplustree_header::IsValidLink(off_t link)
{
return link == BPLUSTREE_NULL || (link > 0 && link <= maximum_size - node_size);
}
/************************ bplustree_node inline functions ************************/
// #pragma mark -
inline uint16 *bplustree_node::KeyLengths() const
{
return (uint16 *)(((char *)this) + round_up(sizeof(bplustree_node) + all_key_length));
}
inline off_t *bplustree_node::Values() const
{
return (off_t *)((char *)KeyLengths() + all_key_count * sizeof(uint16));
}
inline uint8 *bplustree_node::Keys() const
{
return (uint8 *)this + sizeof(bplustree_node);
}
inline int32 bplustree_node::Used() const
{
return round_up(sizeof(bplustree_node) + all_key_length) + all_key_count * (sizeof(uint16) + sizeof(off_t));
}
inline uint8 bplustree_node::LinkType(off_t link)
{
return *(uint64 *)&link >> 62;
}
inline off_t bplustree_node::FragmentOffset(off_t link)
{
return link & 0x3ffffffffffffc00LL;
}
#endif /* B_PLUS_TREE_H */

View File

@ -0,0 +1,266 @@
/*
* Copyright 2001-2008, pinc Software. All Rights Reserved.
*/
//! handles the BFS block bitmap
#include "Bitmap.h"
#include "Disk.h"
#include "Inode.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
Bitmap::Bitmap(Disk *disk)
:
fBitmap(NULL),
fBackupBitmap(NULL)
{
SetTo(disk);
}
Bitmap::Bitmap()
:
fDisk(NULL),
fBitmap(NULL),
fBackupBitmap(NULL),
fSize(0L),
fByteSize(0L),
fUsedBlocks(0LL)
{
}
Bitmap::~Bitmap()
{
free(fBitmap);
free(fBackupBitmap);
}
status_t
Bitmap::SetTo(Disk *disk)
{
free(fBitmap);
fDisk = disk;
fSize = divide_roundup(disk->NumBlocks(),disk->BlockSize() * 8);
fByteSize = disk->BlockSize() * disk->SuperBlock()->num_ags
* disk->SuperBlock()->blocks_per_ag;
fBitmap = (uint32 *)malloc(fByteSize);
if (!fBitmap)
return B_NO_MEMORY;
fBackupBitmap = (uint32 *)malloc(fByteSize);
if (fBackupBitmap == NULL)
return B_NO_MEMORY;
memset(fBackupBitmap, 0, fByteSize);
// set root block, block bitmap, log as used
off_t end = disk->ToBlock(disk->Log()) + disk->Log().length;
for (off_t block = 0; block < end; block++) {
if (!BackupSetAt(block, true))
break;
}
ssize_t size;
if ((size = disk->ReadAt(disk->BlockSize(), fBitmap, fByteSize)) < B_OK) {
free(fBitmap);
fBitmap = NULL;
free(fBackupBitmap);
fBackupBitmap = NULL;
return size;
}
// calculate used blocks
fUsedBlocks = 0LL;
for (uint32 i = fByteSize >> 2;i-- > 0;) {
uint32 compare = 1;
for (int16 j = 0;j < 32;j++,compare <<= 1) {
if (compare & fBitmap[i])
fUsedBlocks++;
}
}
return B_OK;
}
status_t
Bitmap::InitCheck()
{
return fBitmap ? B_OK : B_ERROR;
}
off_t
Bitmap::FreeBlocks() const
{
if (fDisk == NULL)
return 0;
return fDisk->NumBlocks() - fUsedBlocks;
}
bool
Bitmap::UsedAt(off_t block) const
{
uint32 index = block / 32; // 32bit resolution, (beginning with block 1?)
if (index > fByteSize / 4)
return false;
return fBitmap[index] & (1L << (block & 0x1f));
}
bool
Bitmap::BackupUsedAt(off_t block) const
{
uint32 index = block / 32; // 32bit resolution, (beginning with block 1?)
if (index > fByteSize / 4)
return false;
return fBackupBitmap[index] & (1L << (block & 0x1f));
}
bool
Bitmap::BackupSetAt(off_t block, bool used)
{
uint32 index = block / 32;
if (index > fByteSize / 4) {
fprintf(stderr, "Bitmap::BackupSetAt(): Block %Ld outside bitmap!\n",
block);
return false;
}
int32 mask = 1L << (block & 0x1f);
bool oldUsed = fBackupBitmap[index] & mask;
if (used)
fBackupBitmap[index] |= mask;
else
fBackupBitmap[index] &= ~mask;
return oldUsed;
}
void
Bitmap::BackupSet(Inode *inode, bool used)
{
// set inode and its data-stream
// the attributes are ignored for now, because they will
// be added anyway since all inodes from disk are collected.
// printf("a: %Ld\n",inode->Block());
BackupSetAt(inode->Block(),used);
// the data stream of symlinks is no real data stream
if (inode->IsSymlink() && (inode->Flags() & INODE_LONG_SYMLINK) == 0)
return;
// direct blocks
const bfs_inode *node = inode->InodeBuffer();
for (int32 i = 0; i < NUM_DIRECT_BLOCKS; i++) {
if (node->data.direct[i].IsZero())
break;
off_t start = fDisk->ToBlock(node->data.direct[i]);
off_t end = start + node->data.direct[i].length;
for (off_t block = start; block < end; block++) {
if (!BackupSetAt(block, used)) {
//dump_inode(node);
break;
}
}
}
// indirect blocks
if (node->data.max_indirect_range == 0 || node->data.indirect.IsZero())
return;
// printf("c: %Ld\n",fDisk->ToBlock(node->data.indirect));
BackupSetAt(fDisk->ToBlock(node->data.indirect), used);
DataStream *stream = dynamic_cast<DataStream *>(inode);
if (stream == NULL)
return;
// load indirect blocks
int32 bytes = node->data.indirect.length << fDisk->BlockShift();
block_run *indirect = (block_run *)malloc(bytes);
if (indirect == NULL)
return;
if (fDisk->ReadAt(fDisk->ToOffset(node->data.indirect), indirect,
bytes) <= 0)
return;
int32 runs = bytes / sizeof(block_run);
for (int32 i = 0; i < runs; i++) {
if (indirect[i].IsZero())
break;
off_t start = fDisk->ToBlock(indirect[i]);
off_t end = start + indirect[i].length;
for (off_t block = start; block < end; block++) {
// printf("d: %Ld\n", block);
if (!BackupSetAt(block, used))
break;
}
}
free(indirect);
// double indirect blocks
if (node->data.max_double_indirect_range == 0
|| node->data.double_indirect.IsZero())
return;
// printf("e: %Ld\n",fDisk->ToBlock(node->data.double_indirect));
BackupSetAt(fDisk->ToBlock(node->data.double_indirect), used);
// FIXME: to be implemented...
puts("double indirect blocks to block bitmap requested...");
}
status_t
Bitmap::Validate()
{
return B_OK;
}
status_t
Bitmap::CompareWithBackup()
{
for (int32 i = fByteSize / 4;i-- > 0;) {
if (fBitmap[i] != fBackupBitmap[i]) {
printf("differ at %ld (block %ld) -> bitmap = %08lx, backup = %08lx\n",i,i*32,fBitmap[i],fBackupBitmap[i]);
}
}
return B_OK;
}
bool
Bitmap::TrustBlockContaining(off_t /*block*/) const
{
return true;
}

View File

@ -0,0 +1,46 @@
/*
* Copyright 2001-2008, pinc Software. All Rights Reserved.
*/
#ifndef BITMAP_H
#define BITMAP_H
#include <SupportDefs.h>
class Disk;
class Inode;
class Bitmap {
public:
Bitmap(Disk *disk);
Bitmap();
~Bitmap();
status_t SetTo(Disk *disk);
status_t InitCheck();
off_t FreeBlocks() const;
off_t UsedBlocks() const { return fUsedBlocks; }
bool UsedAt(off_t block) const;
bool BackupUsedAt(off_t block) const;
bool BackupSetAt(off_t block,bool used);
void BackupSet(Inode *inode,bool used);
status_t Validate();
status_t CompareWithBackup();
bool TrustBlockContaining(off_t block) const;
size_t Size() const { return fSize; }
private:
Disk *fDisk;
uint32 *fBitmap;
uint32 *fBackupBitmap;
size_t fSize;
size_t fByteSize;
off_t fUsedBlocks;
};
#endif /* BITMAP_H */

View File

@ -0,0 +1,192 @@
#ifndef CACHE_H
#define CACHE_H
/* Cache - a template cache class
**
** Copyright 2001 pinc Software. All Rights Reserved.
*/
#include <SupportDefs.h>
template<class T> class Cache
{
public:
class Cacheable
{
public:
Cacheable()
:
prev(NULL),
next(NULL),
locked(0L),
isDirty(false)
{
}
virtual ~Cacheable()
{
// perform your "undirty" work here
}
virtual bool Equals(T) = 0;
Cacheable *prev,*next;
int32 locked;
bool isDirty;
};
public:
Cache(int32 max = 20)
:
fMaxInQueue(max),
fCount(0),
fHoldChanges(false),
fMostRecentlyUsed(NULL),
fLeastRecentlyUsed(NULL)
{
}
virtual ~Cache()
{
Clear(0,true);
}
void Clear(int32 targetCount = 0,bool force = false)
{
Cacheable *entry = fLeastRecentlyUsed;
while (entry)
{
Cacheable *prev = entry->prev;
if (entry->locked <= 0 || force)
{
if (entry->next)
entry->next->prev = prev;
if (prev)
prev->next = entry->next;
if (fLeastRecentlyUsed == entry)
fLeastRecentlyUsed = prev;
if (fMostRecentlyUsed == entry)
fMostRecentlyUsed = prev;
delete entry;
if (--fCount <= targetCount)
break;
}
entry = prev;
}
}
void SetHoldChanges(bool hold)
{
fHoldChanges = hold;
if (!hold)
Clear(fMaxInQueue);
}
status_t SetDirty(T data,bool dirty)
{
Cacheable *entry = Get(data);
if (!entry)
return B_ENTRY_NOT_FOUND;
entry->isDirty = dirty;
return B_OK;
}
status_t Lock(T data)
{
Cacheable *entry = Get(data);
if (!entry)
return B_ENTRY_NOT_FOUND;
entry->locked++;
return B_OK;
}
status_t Unlock(T data)
{
Cacheable *entry = Get(data);
if (!entry)
return B_ENTRY_NOT_FOUND;
entry->locked--;
return B_OK;
}
Cacheable *Get(T data)
{
Cacheable *entry = GetFromCache(data);
if (entry)
{
if (fMostRecentlyUsed == entry)
return entry;
// remove entry from cache (to insert it at top of the MRU list)
if (entry->prev)
entry->prev->next = entry->next;
if (!entry->next)
{
if (fLeastRecentlyUsed == entry)
fLeastRecentlyUsed = entry->prev;
else
fprintf(stderr,"Cache: fatal error, fLeastRecentlyUsed != entry\n");
}
else
entry->next->prev = entry->prev;
}
else
{
entry = NewCacheable(data);
if (entry)
fCount++;
}
if (entry)
{
// insert entry at the top of the MRU list
entry->next = fMostRecentlyUsed;
entry->prev = NULL;
if (fMostRecentlyUsed)
fMostRecentlyUsed->prev = entry;
else if (fLeastRecentlyUsed == NULL)
fLeastRecentlyUsed = entry;
else
fprintf(stderr,"Cache: fatal error, fLeastRecently != NULL!\n");
fMostRecentlyUsed = entry;
// remove old nodes from of the cache (if possible and necessary)
if (!fHoldChanges
&& fCount > fMaxInQueue
&& fLeastRecentlyUsed)
Clear(fMaxInQueue);
}
return entry;
}
protected:
Cacheable *GetFromCache(T data)
{
Cacheable *entry = fMostRecentlyUsed;
while (entry)
{
if (entry->Equals(data))
return entry;
entry = entry->next;
}
return NULL;
}
virtual Cacheable *NewCacheable(T data) = 0;
int32 fMaxInQueue, fCount;
bool fHoldChanges;
Cacheable *fMostRecentlyUsed;
Cacheable *fLeastRecentlyUsed;
};
#endif /* CACHE_H */

View File

@ -0,0 +1,751 @@
/*
* Copyright (c) 2001-2008 pinc Software. All Rights Reserved.
*/
//! Handles BFS super block, disk access etc.
#include "Disk.h"
#include "dump.h"
#include <Drivers.h>
#include <File.h>
#include <Entry.h>
#include <List.h>
#include <fs_info.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#define MIN_BLOCK_SIZE_INODES 50
#define MAX_BLOCK_SIZE_INODES 500
struct bfs_disk_info {
off_t offset;
disk_super_block super_block;
};
class CacheableBlockRun : public BlockRunCache::Cacheable {
public:
CacheableBlockRun(block_run run,uint8 *data)
:
fRun(run),
fData(data)
{
}
virtual ~CacheableBlockRun()
{
free(fData);
}
virtual bool Equals(block_run run)
{
return run == fRun;
}
void SetData(uint8 *data)
{
fData = data;
}
uint8 *Data()
{
return fData;
}
protected:
block_run fRun;
uint8 *fData;
};
BlockRunCache::BlockRunCache(Disk *disk)
: Cache<block_run>(),
fDisk(disk)
{
}
Cache<block_run>::Cacheable *BlockRunCache::NewCacheable(block_run run)
{
ssize_t length = (int32)run.length << fDisk->BlockShift();
void *buffer = malloc(length);
if (buffer == NULL)
return NULL;
ssize_t read = fDisk->ReadAt(fDisk->ToOffset(run),buffer,length);
if (read < length) {
free(buffer);
return NULL;
}
return new CacheableBlockRun(run,(uint8 *)buffer);
}
// #pragma mark -
Disk::Disk(const char *deviceName, bool rawMode, off_t start, off_t stop)
:
fRawDiskOffset(0),
fSize(0LL),
fCache(this),
fRawMode(rawMode)
{
BEntry entry(deviceName);
fPath.SetTo(deviceName);
if (entry.IsDirectory()) {
dev_t on = dev_for_path(deviceName);
fs_info info;
if (fs_stat_dev(on, &info) != B_OK)
return;
fPath.SetTo(info.device_name);
deviceName = fPath.Path();
}
if (!rawMode && !strncmp(deviceName, "/dev/", 5)
&& !strcmp(deviceName + strlen(deviceName) - 3, "raw"))
fprintf(stderr, "Raw mode not selected, but raw device specified.\n");
if (fFile.SetTo(deviceName, B_READ_WRITE) < B_OK) {
//fprintf(stderr,"Could not open file: %s\n",strerror(fFile.InitCheck()));
return;
}
int device = open(deviceName, O_RDONLY);
if (device < B_OK) {
//fprintf(stderr,"Could not open device\n");
return;
}
partition_info partitionInfo;
device_geometry geometry;
if (ioctl(device, B_GET_PARTITION_INFO, &partitionInfo,
sizeof(partition_info)) == 0) {
//if (gDumpPartitionInfo)
// dump_partition_info(&partitionInfo);
fSize = partitionInfo.size;
} else if (ioctl(device, B_GET_GEOMETRY, &geometry, sizeof(device_geometry))
== 0) {
fSize = (off_t)geometry.cylinder_count * geometry.sectors_per_track
* geometry.head_count * geometry.bytes_per_sector;
} else {
fprintf(stderr,"Disk: Could not get partition info.\n Use file size as partition size\n");
fFile.GetSize(&fSize);
}
close(device);
if (fSize == 0LL)
fprintf(stderr,"Disk: Invalid file size (%Ld bytes)!\n",fSize);
if (rawMode && ScanForSuperBlock(start, stop) < B_OK) {
fFile.Unset();
return;
}
if (fFile.ReadAt(512 + fRawDiskOffset, &fSuperBlock,
sizeof(disk_super_block)) < 1)
fprintf(stderr,"Disk: Could not read super block\n");
//dump_super_block(&fSuperBlock);
}
Disk::~Disk()
{
}
status_t Disk::InitCheck()
{
status_t status = fFile.InitCheck();
if (status == B_OK)
return fSize == 0LL ? B_ERROR : B_OK;
return status;
}
block_run Disk::ToBlockRun(off_t start, int16 length) const
{
block_run run;
run.allocation_group = start >> fSuperBlock.ag_shift;
run.start = start & ((1UL << fSuperBlock.ag_shift) - 1);
run.length = length;
return run;
}
off_t Disk::LogSize() const
{
if (fSuperBlock.num_blocks >= 4096)
return 2048;
return 512;
}
uint8 *Disk::ReadBlockRun(block_run run)
{
CacheableBlockRun *entry = (CacheableBlockRun *)fCache.Get(run);
if (entry)
return entry->Data();
return NULL;
}
status_t
Disk::DumpBootBlockToFile()
{
BFile file("/boot/home/bootblock.old", B_READ_WRITE | B_CREATE_FILE);
if (file.InitCheck() < B_OK)
return file.InitCheck();
char buffer[BlockSize()];
ssize_t bytes = ReadAt(0, buffer, BlockSize());
if (bytes < (int32)BlockSize())
return bytes < B_OK ? bytes : B_ERROR;
file.Write(buffer, BlockSize());
// initialize buffer
memset(buffer, 0, BlockSize());
memcpy(buffer + 512, &fSuperBlock, sizeof(disk_super_block));
// write buffer to disk
WriteAt(0, buffer, BlockSize());
return B_OK;
}
// #pragma mark - Superblock recovery methods
status_t
Disk::ScanForSuperBlock(off_t start = 0, off_t stop = -1)
{
printf("Disk size %Ld bytes, %.2f GB\n", fSize, 1.0 * fSize / (1024*1024*1024));
uint32 blockSize = 4096;
char buffer[blockSize + 1024];
if (stop == -1)
stop = fSize;
char escape[3] = {0x1b, '[', 0};
BList superBlocks;
printf("Scanning Disk from %Ld to %Ld\n", start, stop);
for (off_t offset = start; offset < stop; offset += blockSize)
{
if (((offset-start) % (blockSize * 100)) == 0)
printf(" %12Ld, %.2f GB %s1A\n",offset,1.0 * offset / (1024*1024*1024),escape);
ssize_t bytes = fFile.ReadAt(offset, buffer, blockSize + 1024);
if (bytes < B_OK)
{
fprintf(stderr,"Could not read from device: %s\n", strerror(bytes));
return -1;
}
for (uint32 i = 0;i < blockSize - 2;i++)
{
disk_super_block *super = (disk_super_block *)&buffer[i];
if (super->magic1 == (int32)SUPER_BLOCK_MAGIC1
&& super->magic2 == (int32)SUPER_BLOCK_MAGIC2
&& super->magic3 == (int32)SUPER_BLOCK_MAGIC3)
{
printf("\n(%ld) *** BFS superblock found at: %Ld\n",superBlocks.CountItems() + 1,offset);
dump_super_block(super);
// add a copy of the super block to the list
bfs_disk_info *info = (bfs_disk_info *)malloc(sizeof(bfs_disk_info));
if (info == NULL)
return B_NO_MEMORY;
memcpy(&info->super_block, super, sizeof(disk_super_block));
info->offset = offset + i - 512;
/* location off the BFS super block is 512 bytes after the partition start */
superBlocks.AddItem(info);
}
}
}
if (superBlocks.CountItems() == 0) {
puts("\nCouldn't find any BFS super blocks!");
return B_ENTRY_NOT_FOUND;
}
// Let the user decide which block to use
puts("\n\nThe following disks were found:");
for (int32 i = 0; i < superBlocks.CountItems(); i++) {
bfs_disk_info *info = (bfs_disk_info *)superBlocks.ItemAt(i);
printf("%ld) %s, offset %Ld, size %g GB (%svalid)\n", i + 1,
info->super_block.name, info->offset,
1.0 * info->super_block.num_blocks * info->super_block.block_size / (1024*1024*1024),
ValidateSuperBlock(info->super_block) < B_OK ? "in" : "");
}
char answer[16];
printf("\nChoose one (by number): ");
fflush(stdout);
fgets(answer, 15, stdin);
int32 index = atol(answer);
if (index > superBlocks.CountItems() || index < 1) {
puts("No disk there... exiting.");
return B_BAD_VALUE;
}
bfs_disk_info *info = (bfs_disk_info *)superBlocks.ItemAt(index - 1);
// ToDo: free the other disk infos
fRawDiskOffset = info->offset;
fFile.Seek(fRawDiskOffset, SEEK_SET);
if (ValidateSuperBlock(info->super_block))
fSize = info->super_block.block_size * info->super_block.block_size;
else {
// just make it open-end
fSize -= fRawDiskOffset;
}
return B_OK;
}
status_t
Disk::ValidateSuperBlock(disk_super_block &superBlock)
{
if (superBlock.magic1 != (int32)SUPER_BLOCK_MAGIC1
|| superBlock.magic2 != (int32)SUPER_BLOCK_MAGIC2
|| superBlock.magic3 != (int32)SUPER_BLOCK_MAGIC3
|| (int32)superBlock.block_size != superBlock.inode_size
|| superBlock.fs_byte_order != SUPER_BLOCK_FS_LENDIAN
|| (1UL << superBlock.block_shift) != superBlock.block_size
|| superBlock.num_ags < 1
|| superBlock.ag_shift < 1
|| superBlock.blocks_per_ag < 1
|| superBlock.num_blocks < 10
|| superBlock.used_blocks > superBlock.num_blocks
|| superBlock.num_ags != divide_roundup(superBlock.num_blocks,
1L << superBlock.ag_shift))
return B_ERROR;
return B_OK;
}
status_t
Disk::ValidateSuperBlock()
{
if (ValidateSuperBlock(fSuperBlock) < B_OK)
return B_ERROR;
fBitmap.SetTo(this);
return B_OK;
}
status_t
Disk::RecreateSuperBlock()
{
memset(&fSuperBlock,0,sizeof(disk_super_block));
puts("\n*** Determine block size");
status_t status = DetermineBlockSize();
if (status < B_OK)
return status;
printf("\tblock size = %ld\n",BlockSize());
strcpy(fSuperBlock.name,"recovered");
fSuperBlock.magic1 = SUPER_BLOCK_MAGIC1;
fSuperBlock.fs_byte_order = SUPER_BLOCK_FS_LENDIAN;
fSuperBlock.block_shift = get_shift(BlockSize());
fSuperBlock.num_blocks = fSize / BlockSize(); // only full blocks
fSuperBlock.inode_size = BlockSize();
fSuperBlock.magic2 = SUPER_BLOCK_MAGIC2;
fSuperBlock.flags = SUPER_BLOCK_CLEAN;
// size of the block bitmap + root block
fLogStart = 1 + divide_roundup(fSuperBlock.num_blocks,BlockSize() * 8);
// set it anywhere in the log
fSuperBlock.log_start = fLogStart + (LogSize() >> 1);
fSuperBlock.log_end = fSuperBlock.log_start;
//
// scan for indices and root inode
//
puts("\n*** Scanning for indices and root node...");
fValidOffset = 0LL;
bfs_inode indexDir;
bfs_inode rootDir;
if (ScanForIndexAndRoot(&indexDir,&rootDir) < B_OK) {
fprintf(stderr,"ERROR: Could not find root directory! Trying to recreate the super block will have no effect!\n\tSetting standard values for the root dir.\n");
rootDir.inode_num.allocation_group = 8;
rootDir.inode_num.start = 0;
rootDir.inode_num.length = 1;
//gErrors++;
}
if (fValidOffset == 0LL) {
printf("No valid inode found so far - perform search.\n");
off_t offset = 8LL * 65536 * BlockSize();
char buffer[1024];
GetNextSpecialInode(buffer,&offset,offset + 32LL * 65536 * BlockSize(),true);
if (fValidOffset == 0LL)
{
fprintf(stderr,"FATAL ERROR: Could not find valid inode!\n");
return B_ERROR;
}
}
// calculate allocation group size
int32 allocationGroupSize = (fValidOffset - fValidBlockRun.start
* BlockSize()
+ BlockSize() - 1) / (BlockSize() * fValidBlockRun.allocation_group);
fSuperBlock.blocks_per_ag = allocationGroupSize / (BlockSize() << 3);
fSuperBlock.ag_shift = get_shift(allocationGroupSize);
fSuperBlock.num_ags = divide_roundup(fSuperBlock.num_blocks,allocationGroupSize);
// calculate rest of log area
fSuperBlock.log_blocks.allocation_group = fLogStart / allocationGroupSize;
fSuperBlock.log_blocks.start = fLogStart - fSuperBlock.log_blocks.allocation_group * allocationGroupSize;
fSuperBlock.log_blocks.length = LogSize(); // assumed length of 2048 blocks
if (fLogStart != ((indexDir.inode_num.allocation_group
<< fSuperBlock.ag_shift) + indexDir.inode_num.start - LogSize())) {
fprintf(stderr,"ERROR: start of logging area doesn't fit assumed value "
"(%Ld blocks before indices)!\n", LogSize());
//gErrors++;
}
fSuperBlock.magic3 = SUPER_BLOCK_MAGIC3;
fSuperBlock.root_dir = rootDir.inode_num;
fSuperBlock.indices = indexDir.inode_num;
// calculate used blocks (from block bitmap)
fBitmap.SetTo(this);
if (fBitmap.UsedBlocks())
fSuperBlock.used_blocks = fBitmap.UsedBlocks();
else {
fprintf(stderr,"ERROR: couldn't calculate number of used blocks, marking all blocks as used!\n");
fSuperBlock.used_blocks = fSuperBlock.num_blocks;
//gErrors++;
}
return B_OK;
}
status_t
Disk::DetermineBlockSize()
{
// scan for about 500 inodes to determine the disk's block size
// min. block bitmap size (in bytes, rounded to a 1024 boundary)
// root_block_size + (num_blocks / bits_per_block) * block_size
off_t offset = 1024 + divide_roundup(fSize / 1024,8 * 1024) * 1024;
off_t inodesFound = 0;
char buffer[1024];
bfs_inode *inode = (bfs_inode *)buffer;
// valid block sizes from 1024 to 32768 bytes
int32 blockSizeCounter[6] = {0};
status_t status = B_OK;
// read a quarter of the drive at maximum
for (; offset < (fSize >> 2); offset += 1024) {
if (fFile.ReadAt(offset, buffer, sizeof(buffer)) < B_OK) {
fprintf(stderr, "could not read from device (offset = %Ld, "
"size = %ld)!\n", offset, sizeof(buffer));
status = B_IO_ERROR;
break;
}
if (inode->magic1 != INODE_MAGIC1
|| inode->inode_size != 1024
&& inode->inode_size != 2048
&& inode->inode_size != 4096
&& inode->inode_size != 8192)
continue;
inodesFound++;
// update block size counter
for (int i = 0;i < 6;i++)
if ((1 << (i + 10)) == inode->inode_size)
blockSizeCounter[i]++;
int32 count = 0;
for (int32 i = 0;i < 6;i++)
if (blockSizeCounter[i])
count++;
// do we have a clear winner at 100 inodes?
// if not, break at 500 inodes
if (inodesFound >= MAX_BLOCK_SIZE_INODES
|| (inodesFound >= MIN_BLOCK_SIZE_INODES && count == 1))
break;
}
// find the safest bet
int32 maxCounter = -1;
int32 maxIndex = 0;
for (int32 i = 0; i < 6; i++) {
if (maxCounter < blockSizeCounter[i]) {
maxIndex = i;
maxCounter = blockSizeCounter[i];
}
}
fSuperBlock.block_size = (1 << (maxIndex + 10));
return status;
}
status_t
Disk::GetNextSpecialInode(char *buffer, off_t *_offset, off_t end,
bool skipAfterValidInode = false)
{
off_t offset = *_offset;
if (end == offset)
end++;
bfs_inode *inode = (bfs_inode *)buffer;
for (; offset < end; offset += BlockSize()) {
if (fFile.ReadAt(offset, buffer, 1024) < B_OK) {
fprintf(stderr,"could not read from device (offset = %Ld, size = %d)!\n",offset,1024);
*_offset = offset;
return B_IO_ERROR;
}
if (inode->magic1 != INODE_MAGIC1
|| inode->inode_size != fSuperBlock.inode_size)
continue;
// can compute allocation group only for inodes which are
// a) not in the first allocation group
// b) not in the log area
if (inode->inode_num.allocation_group > 0
&& offset >= (BlockSize() * (fLogStart + LogSize()))) {
fValidBlockRun = inode->inode_num;
fValidOffset = offset;
if (skipAfterValidInode)
return B_OK;
}
// is the inode a special root inode (parent == self)?
if (!memcmp(&inode->parent, &inode->inode_num, sizeof(inode_addr))) {
printf("\t special inode found at %Ld\n", offset);
*_offset = offset;
return B_OK;
}
}
*_offset = offset;
return B_ENTRY_NOT_FOUND;
}
void
Disk::SaveInode(bfs_inode *inode, bool *indices, bfs_inode *indexDir,
bool *root, bfs_inode *rootDir)
{
if ((inode->mode & S_INDEX_DIR) == 0) {
*root = true;
printf("\troot node found!\n");
if (inode->inode_num.allocation_group != 8
|| inode->inode_num.start != 0
|| inode->inode_num.length != 1)
printf("WARNING: root node has unexpected position: (ag = %ld, "
"start = %d, length = %d)\n", inode->inode_num.allocation_group,
inode->inode_num.start, inode->inode_num.length);
if (rootDir)
memcpy(rootDir, inode, sizeof(bfs_inode));
} else if (inode->mode & S_INDEX_DIR) {
*indices = true;
printf("\tindices node found!\n");
if (indexDir)
memcpy(indexDir, inode, sizeof(bfs_inode));
}
// if (gDumpIndexRootNode)
// dump_inode(inode);
}
status_t
Disk::ScanForIndexAndRoot(bfs_inode *indexDir,bfs_inode *rootDir)
{
memset(rootDir,0,sizeof(bfs_inode));
memset(indexDir,0,sizeof(bfs_inode));
bool indices = false,root = false;
char buffer[1024];
bfs_inode *inode = (bfs_inode *)buffer;
// The problem here is that we don't want to find copies of the
// inodes in the log.
// The first offset where a root node can be is therefore the
// first allocation group with a block size of 1024, and 16384
// blocks per ag; that should be relatively save.
// search for the indices inode, start reading from log size + boot block size
off_t offset = (fLogStart + LogSize()) * BlockSize();
if (GetNextSpecialInode(buffer,&offset,offset + 65536LL * BlockSize()) == B_OK)
SaveInode(inode,&indices,indexDir,&root,rootDir);
if (!indices) {
fputs("ERROR: no indices node found!\n",stderr);
//gErrors++;
}
// search common places for root node, iterating from allocation group
// size from 1024 to 65536
for (off_t start = 8LL * 1024 * BlockSize();start <= 8LL * 65536 * BlockSize();start <<= 1) {
if (start < fLogStart)
continue;
off_t commonOffset = start;
if (GetNextSpecialInode(buffer, &commonOffset, commonOffset) == B_OK) {
SaveInode(inode, &indices, indexDir, &root, rootDir);
if (root)
break;
}
}
if (!root) {
printf("WARNING: Could not find root node at common places!\n");
printf("\tScanning log area for root node\n");
off_t logOffset = ToOffset(fSuperBlock.log_blocks);
if (GetNextSpecialInode(buffer,&logOffset,logOffset + LogSize() * BlockSize()) == B_OK)
{
SaveInode(inode,&indices,indexDir,&root,rootDir);
printf("root node at: 0x%Lx (DiskProbe)\n",logOffset / 512);
//fFile.ReadAt(logOffset + BlockSize(),buffer,1024);
//if (*(uint32 *)buffer == BPLUSTREE_MAGIC)
//{
// puts("\t\tnext block in log contains a bplustree!");
//}
}
}
/*if (!root)
{
char txt[64];
printf("Should I perform a deeper search (that will take some time) (Y/N) [N]? ");
gets(txt);
if (!strcasecmp("y",txt))
{
// search not so common places for the root node (all places)
if (indices)
offset += BlockSize(); // the block after the indices inode
else
offset = (fLogStart + 1) * BlockSize();
if (GetNextSpecialInode(buffer,&offset,65536LL * 16 * BlockSize()) == B_OK)
SaveInode(inode,&indices,indexDir,&root,rootDir);
}
}*/
if (root)
return B_OK;
return B_ERROR;
}
// #pragma mark - BPositionIO methods
ssize_t
Disk::Read(void *buffer, size_t size)
{
return fFile.Read(buffer, size);
}
ssize_t
Disk::Write(const void *buffer, size_t size)
{
return fFile.Write(buffer, size);
}
ssize_t
Disk::ReadAt(off_t pos, void *buffer, size_t size)
{
return fFile.ReadAt(pos + fRawDiskOffset, buffer, size);
}
ssize_t
Disk::WriteAt(off_t pos, const void *buffer, size_t size)
{
return fFile.WriteAt(pos + fRawDiskOffset, buffer, size);
}
off_t
Disk::Seek(off_t position, uint32 seekMode)
{
// ToDo: only correct for seekMode == SEEK_SET, right??
if (seekMode != SEEK_SET)
puts("OH NO, I AM BROKEN!");
return fFile.Seek(position + fRawDiskOffset, seekMode);
}
off_t
Disk::Position() const
{
return fFile.Position() - fRawDiskOffset;
}
status_t
Disk::SetSize(off_t /*size*/)
{
// SetSize() is not supported
return B_ERROR;
}

View File

@ -0,0 +1,108 @@
#ifndef DISK_H
#define DISK_H
/* Disk - handles BFS super block, disk access etc.
**
** Copyright (c) 2001-2003 pinc Software. All Rights Reserved.
*/
#include <File.h>
#include <Path.h>
#include "Bitmap.h"
#include "bfs.h"
#include "Cache.h"
class Disk;
class BlockRunCache : public Cache<block_run>
{
public:
BlockRunCache(Disk *disk);
// ~BlockRunCache();
virtual Cacheable *NewCacheable(block_run run);
protected:
Disk *fDisk;
};
class Disk : public BPositionIO
{
public:
Disk(const char *device, bool rawMode = false, off_t start = 0, off_t stop = -1);
virtual ~Disk();
status_t InitCheck();
const BPath &Path() const { return fPath; }
off_t Size() const { return fSize; }
off_t NumBlocks() const { return fSuperBlock.num_blocks; }
uint32 BlockSize() const { return fSuperBlock.block_size; }
uint32 BlockShift() const { return fSuperBlock.block_shift; }
uint32 AllocationGroups() const { return fSuperBlock.num_ags; }
uint32 AllocationGroupShift() const { return fSuperBlock.ag_shift; }
uint32 BitmapSize() const { return fBitmap.Size(); }
off_t LogSize() const;
disk_super_block *SuperBlock() { return &fSuperBlock; }
block_run Root() const { return fSuperBlock.root_dir; }
block_run Indices() const { return fSuperBlock.indices; }
block_run Log() const { return fSuperBlock.log_blocks; }
Bitmap *BlockBitmap() { return &fBitmap; }
const char *Name() const { return fSuperBlock.name; }
void SetName(const char *name) { strcpy(fSuperBlock.name,name); }
off_t ToOffset(block_run run) const { return ToBlock(run) << fSuperBlock.block_shift; }
off_t ToBlock(block_run run) const { return ((((off_t)run.allocation_group) << fSuperBlock.ag_shift) | (off_t)run.start); }
block_run ToBlockRun(off_t start,int16 length = 1) const;
uint8 *ReadBlockRun(block_run run);
status_t ScanForSuperBlock(off_t start = 0,off_t stop = -1);
status_t ValidateSuperBlock();
status_t RecreateSuperBlock();
status_t DumpBootBlockToFile();
// BPositionIO methods
virtual ssize_t Read(void *buffer, size_t size);
virtual ssize_t Write(const void *buffer, size_t size);
virtual ssize_t ReadAt(off_t pos, void *buffer, size_t size);
virtual ssize_t WriteAt(off_t pos, const void *buffer, size_t size);
virtual off_t Seek(off_t position, uint32 seek_mode);
virtual off_t Position() const;
virtual status_t SetSize(off_t size);
protected:
status_t GetNextSpecialInode(char *,off_t *,off_t,bool);
void SaveInode(bfs_inode *,bool *,bfs_inode *,bool *,bfs_inode *);
status_t ScanForIndexAndRoot(bfs_inode *,bfs_inode *);
status_t DetermineBlockSize();
status_t ValidateSuperBlock(disk_super_block &superBlock);
status_t LoadBootBlock();
BFile fFile;
BPath fPath;
off_t fRawDiskOffset;
off_t fSize;
disk_super_block fSuperBlock;
Bitmap fBitmap;
block_run fValidBlockRun;
off_t fValidOffset;
off_t fLogStart;
BlockRunCache fCache;
bool fRawMode;
};
#endif /* DISK_H */

View File

@ -0,0 +1,284 @@
/* Hashtable - a general purpose hash table
**
** Copyright 2001 pinc Software. All Rights Reserved.
*/
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include "Hashtable.h"
/************************** standard string hash functions **************************/
unsigned int stringHash(const char *c)
{
int len = strlen(c);
return(*(int *)(c + len - 4)); // erstmal zum Testen
}
bool stringCompare(const char *a, const char *b)
{
return(!strcmp(a, b));
}
// #pragma mark - Hashtable
Hashtable::Hashtable(int capacity, float loadFactor)
:
fIteratorIndex(-1)
{
if (capacity < 0 || loadFactor <= 0)
return;
if (!capacity)
capacity = 1;
if (!(fTable = (struct Entry **)malloc(capacity * sizeof(void *))))
return;
memset(fTable,0,capacity * sizeof(void *));
fThreshold = (int)(capacity * loadFactor);
fModCount = 0;
fLoadFactor = loadFactor;
fCapacity = capacity;
fHashFunc = (uint32 (*)(const void *))stringHash;
fCompareFunc = (bool (*)(const void *, const void *))stringCompare;
}
Hashtable::~Hashtable()
{
struct Entry **table = fTable;
for(int32 index = fCapacity;--index >= 0;)
{
struct Entry *entry,*next;
for(entry = table[index];entry;entry = next)
{
next = entry->next;
delete entry;
}
}
free(table);
}
void Hashtable::SetHashFunction(uint32 (*func)(const void *))
{
fHashFunc = func;
}
void Hashtable::SetCompareFunction(bool (*func)(const void *, const void *))
{
fCompareFunc = func;
}
bool Hashtable::IsEmpty() const
{
return fCount == 0;
}
bool Hashtable::ContainsKey(const void *key)
{
return GetHashEntry(key) ? true : false;
}
void *Hashtable::GetValue(const void *key)
{
Entry *entry = GetHashEntry(key);
return entry ? entry->value : NULL;
}
bool Hashtable::Put(const void *key, void *value)
{
Entry *entry = GetHashEntry(key);
int hash = fHashFunc(key);
int index;
if (entry)
return true;
fModCount++;
if (fCount >= fThreshold)
Rehash();
index = hash % fCapacity;
fTable[index] = new Entry(fTable[index], key, value);
fCount++;
return true;
}
void *Hashtable::Remove(const void *key)
{
Entry **table,*entry,*prev;
uint32 hash,(*func)(const void *);
int32 index;
table = fTable;
hash = (func = fHashFunc)(key);
index = hash % fCapacity;
for(entry = table[index],prev = NULL;entry;entry = entry->next)
{
if ((func(entry->key) == hash) && fCompareFunc(entry->key,key))
{
void *value;
fModCount++;
if (prev)
prev->next = entry->next;
else
table[index] = entry->next;
fCount--;
value = entry->value;
delete entry;
return value;
}
prev = entry;
}
return NULL;
}
status_t Hashtable::GetNextEntry(void **value)
{
if (fIteratorIndex == -1)
{
fIteratorIndex = 0;
fIteratorEntry = fTable[0];
}
else if (fIteratorEntry)
fIteratorEntry = fIteratorEntry->next;
// get next filled slot
while (!fIteratorEntry && fIteratorIndex + 1 < fCapacity)
fIteratorEntry = fTable[++fIteratorIndex];
if (fIteratorEntry)
{
*value = fIteratorEntry->value;
return B_OK;
}
return B_ENTRY_NOT_FOUND;
}
void Hashtable::Rewind()
{
fIteratorIndex = -1;
}
void
Hashtable::MakeEmpty(int8 keyMode = HASH_EMPTY_NONE,int8 valueMode = HASH_EMPTY_NONE)
{
fModCount++;
for (int32 index = fCapacity; --index >= 0;) {
Entry *entry, *next;
for (entry = fTable[index]; entry; entry = next) {
switch (keyMode) {
case HASH_EMPTY_DELETE:
// TODO: destructors are not called!
delete (void*)entry->key;
break;
case HASH_EMPTY_FREE:
free((void*)entry->key);
break;
}
switch (valueMode) {
case HASH_EMPTY_DELETE:
// TODO: destructors are not called!
delete entry->value;
break;
case HASH_EMPTY_FREE:
free(entry->value);
break;
}
next = entry->next;
delete entry;
}
fTable[index] = NULL;
}
fCount = 0;
}
/** The hash table will be doubled in size, and rebuild.
* @return true on success
*/
bool Hashtable::Rehash()
{
uint32 (*hashCode)(const void *) = fHashFunc;
struct Entry **oldTable = fTable,**newtable;
int oldCapacity = fCapacity;
int newCapacity,i;
newCapacity = oldCapacity * 2 + 1;
if (!(newtable = (struct Entry **)malloc(newCapacity * sizeof(struct Entry *))))
return false;
memset(newtable,0,newCapacity*sizeof(struct Entry *));
fModCount++;
fThreshold = (int)(newCapacity * fLoadFactor);
fTable = newtable;
fCapacity = newCapacity;
for (i = oldCapacity;i-- > 0;) {
Entry *oldEntry,*entry = NULL;
int index;
for (oldEntry = oldTable[i];oldEntry;) {
entry = oldEntry; oldEntry = oldEntry->next;
index = hashCode(entry->key) % newCapacity;
entry->next = newtable[index];
newtable[index] = entry;
}
}
free(oldTable);
return true;
}
Hashtable::Entry *Hashtable::GetHashEntry(const void *key)
{
Entry **table,*entry;
uint32 hash,(*func)(const void *);
table = fTable;
hash = (func = fHashFunc)(key);
for(entry = table[hash % fCapacity];entry;entry = entry->next)
{
if ((func(entry->key) == hash) && fCompareFunc(entry->key,key))
return entry;
}
return NULL;
}

View File

@ -0,0 +1,62 @@
#ifndef HASHTABLE_H
#define HASHTABLE_H
/* Hashtable - a general purpose hash table
**
** Copyright 2001 pinc Software. All Rights Reserved.
*/
#include "SupportDefs.h"
#define HASH_EMPTY_NONE 0
#define HASH_EMPTY_FREE 1
#define HASH_EMPTY_DELETE 2
class Hashtable
{
public:
Hashtable(int capacity = 100, float loadFactor = 0.75);
~Hashtable();
void SetHashFunction(uint32 (*func)(const void *));
void SetCompareFunction(bool (*func)(const void *, const void *));
bool IsEmpty() const;
bool ContainsKey(const void *key);
void *GetValue(const void *key);
bool Put(const void *key, void *value);
void *Remove(const void *key);
status_t GetNextEntry(void **value);
void Rewind();
void MakeEmpty(int8 keyMode = HASH_EMPTY_NONE,int8 valueMode = HASH_EMPTY_NONE);
protected:
class Entry
{
public:
Entry(Entry *_next, const void *_key, void *_value)
: next(_next), key(_key), value(_value) {}
Entry *next;
const void *key;
void *value;
};
bool Rehash();
Entry *GetHashEntry(const void *key);
int32 fCapacity,fCount,fThreshold,fModCount;
float fLoadFactor;
Entry **fTable;
uint32 (*fHashFunc)(const void *);
bool (*fCompareFunc)(const void *, const void *);
int32 fIteratorIndex;
Entry *fIteratorEntry;
};
#endif // HASHTABLE_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,199 @@
/*
* Copyright 2001-2008, pinc Software. All Rights Reserved.
*/
#ifndef INODE_H
#define INODE_H
#include <SupportDefs.h>
#include "Disk.h"
class BPlusTree;
class Directory;
class Inode {
public:
Inode(Disk* disk, bfs_inode* inode, bool ownBuffer = true);
Inode(const Inode &inode);
virtual ~Inode();
status_t SetTo(bfs_inode *inode);
virtual status_t InitCheck();
bool IsFile() const { return S_ISREG(fInode->mode); }
bool IsDirectory() const { return S_ISDIR(fInode->mode); }
bool IsSymlink() const { return S_ISLNK(fInode->mode); }
bool IsIndex() const { return S_ISINDEX(fInode->mode); }
bool IsAttribute() const
{ return (fInode->mode & S_ATTR) != 0; }
bool IsAttributeDirectory() const
{ return (fInode->mode & S_ATTR_DIR) != 0; }
bool IsRoot() const { return BlockRun() == fDisk->Root(); }
int32 Mode() const { return fInode->mode; }
int32 Flags() const { return fInode->flags; }
off_t Size() const { return fInode->data.size; }
off_t Offset() const
{ return fDisk->ToOffset(BlockRun()); }
off_t Block() const { return fDisk->ToBlock(BlockRun()); }
const block_run& BlockRun() const { return fBlockRun; }
block_run Parent() const { return fInode->parent; }
block_run Attributes() const { return fInode->attributes; }
const bfs_inode* InodeBuffer() const { return fInode; }
status_t CopyBuffer();
void ReleaseBuffer();
status_t AcquireBuffer();
void BufferClobbered();
void SetParent(const block_run& run);
void SetBlockRun(const block_run& run);
void SetMode(uint32 mode);
status_t SetName(const char* name);
const char* Name() const;
status_t GetNextSmallData(small_data** smallData);
status_t RewindAttributes();
status_t GetNextAttribute(char* name, uint32* type, void** data,
size_t* length);
class Source;
const char* Path(Inode::Source* source = NULL);
virtual status_t CopyTo(const char* path, bool fullPath = true,
Inode::Source* source = NULL);
status_t CopyAttributesTo(BNode* node);
static Inode* Factory(Disk* disk, bfs_inode* inode,
bool ownBuffer = true);
static Inode* Factory(Disk* disk, block_run run);
static Inode* Factory(Disk* disk, Inode* inode,
bool copyBuffer = true);
static Inode* EmptyInode(Disk* disk,const char* name, int32 mode);
class Source {
public:
virtual Inode *InodeAt(block_run run) = 0;
};
protected:
static bool _LowMemory();
void _Unset();
status_t _FindPath(Inode::Source *source = NULL);
Disk *fDisk;
bfs_inode *fInode;
bool fOwnBuffer;
char *fPath;
block_run fBlockRun;
int32 fRefCount;
small_data *fCurrentSmallData;
Directory *fAttributes;
void *fAttributeBuffer;
};
class DataStream : public Inode, public BPositionIO {
public:
DataStream(Disk *disk, bfs_inode *inode, bool ownBuffer = true);
DataStream(const Inode &inode);
~DataStream();
status_t FindBlockRun(off_t pos);
virtual ssize_t ReadAt(off_t pos, void *buffer, size_t size);
virtual ssize_t WriteAt(off_t pos, const void *buffer, size_t size);
virtual off_t Seek(off_t position, uint32 seek_mode);
virtual off_t Position() const;
virtual status_t SetSize(off_t size);
private:
int32 fCurrent;
int32 fLevel;
block_run fRun;
off_t fRunFileOffset;
off_t fRunBlockEnd;
off_t fPosition;
};
class File : public DataStream {
public:
File(Disk *disk, bfs_inode *inode, bool ownBuffer = true);
File(const Inode &inode);
~File();
virtual status_t InitCheck();
virtual status_t CopyTo(const char *path, bool fullPath = true,
Inode::Source *source = NULL);
};
class Attribute : public File {
public:
Attribute(Disk *disk, bfs_inode *inode, bool ownBuffer = true);
Attribute(const Inode &inode);
~Attribute();
virtual status_t InitCheck();
virtual status_t CopyTo(const char *path, bool fullPath = true,
Inode::Source *source = NULL);
uint32 Type() const { return fInode->type; }
};
class Directory : public DataStream {
public:
Directory(Disk *disk, bfs_inode *inode, bool ownBuffer = true);
Directory(const Inode &inode);
~Directory();
virtual status_t InitCheck();
virtual status_t CopyTo(const char *path, bool fullPath = true,
Inode::Source *source = NULL);
virtual status_t Rewind();
virtual status_t GetNextEntry(char *name, block_run *run);
virtual status_t GetNextEntry(block_run *run);
virtual status_t Contains(const block_run *run);
virtual status_t Contains(const Inode *inode);
virtual status_t FindEntry(const char *name, block_run *run);
virtual status_t AddEntry(Inode *inode);
status_t GetTree(BPlusTree **tree);
private:
virtual status_t CreateTree();
BPlusTree *fTree;
};
class Symlink : public Inode {
public:
Symlink(Disk *disk, bfs_inode *inode, bool ownBuffer = true);
Symlink(const Inode &inode);
~Symlink();
virtual status_t InitCheck();
virtual status_t CopyTo(const char *path, bool fullPath = true,
Inode::Source *source = NULL);
status_t LinksTo(char *to, size_t maxLength);
private:
char *fTo;
};
#endif /* INODE_H */

View File

@ -0,0 +1,13 @@
SubDir HAIKU_TOP src bin bfs_tools lib ;
SetSubDirSupportedPlatformsBeOSCompatible ;
StaticLibrary libbfs_tools.a :
bfs.cpp
Bitmap.cpp
BPlusTree.cpp
Disk.cpp
dump.cpp
Hashtable.cpp
Inode.cpp
;

View File

@ -0,0 +1,57 @@
#ifndef STACK_H
#define STACK_H
/* Stack - a template stack class
**
** Copyright 2001 pinc Software. All Rights Reserved.
*/
#include <SupportDefs.h>
template<class T> class Stack
{
public:
Stack()
:
fArray(NULL),
fUsed(0),
fMax(0)
{
}
~Stack()
{
if (fArray)
free(fArray);
}
status_t Push(T value)
{
if (fUsed >= fMax)
{
fMax += 16;
fArray = (T *)realloc(fArray,fMax * sizeof(T));
if (fArray == NULL)
return B_NO_MEMORY;
}
fArray[fUsed++] = value;
return B_OK;
}
bool Pop(T *value)
{
if (fUsed == 0)
return false;
*value = fArray[--fUsed];
return true;
}
private:
T *fArray;
int32 fUsed;
int32 fMax;
};
#endif /* STACK_H */

View File

@ -0,0 +1,51 @@
/* bfs - BFS definitions and helper functions
**
** Copyright 2001, pinc Software. All Rights Reserved.
*/
#include <File.h>
#include <stdio.h>
#include "bfs.h"
#ifdef BFS_BOOTSECTOR
const uint8 bfs_bootsector[512] = {
0xb8, 0x00, 0x90, 0x8e, 0xd0, 0x89, 0xc4, 0xfc, 0x68, 0xc0, 0x07, 0x1f, 0x1e, 0x07, 0x80, 0xfa,
0x02, 0x72, 0x0c, 0x80, 0xfa, 0x80, 0x72, 0x05, 0x80, 0xfa, 0x90, 0x72, 0x02, 0xb2, 0x80, 0x66,
0x31, 0xc0, 0x40, 0xbb, 0x00, 0x02, 0xe8, 0x7e, 0x00, 0x81, 0x7f, 0x20, 0x31, 0x53, 0x75, 0x75,
0x80, 0xbf, 0xff, 0x01, 0x20, 0x75, 0x6e, 0xbe, 0x74, 0x02, 0xe8, 0x82, 0x01, 0xe8, 0xa3, 0x02,
0xbf, 0x07, 0x03, 0xbb, 0x00, 0x06, 0x57, 0xe8, 0xf5, 0x00, 0x72, 0x59, 0x5f, 0x53, 0xbb, 0x00,
0x02, 0x59, 0x51, 0x81, 0xc3, 0x00, 0x04, 0x39, 0xcb, 0x73, 0xe8, 0x66, 0xff, 0x47, 0x10, 0x75,
0xf0, 0x66, 0xff, 0x47, 0x14, 0x75, 0xea, 0xb0, 0x2f, 0x89, 0xd9, 0x57, 0xf2, 0xae, 0x75, 0x35,
0x29, 0xd9, 0xf7, 0xd1, 0x5f, 0x60, 0xe8, 0x2b, 0x02, 0x61, 0x72, 0xd5, 0x8c, 0xd5, 0x01, 0xcf,
0x47, 0x38, 0x05, 0x75, 0xbe, 0xb8, 0x00, 0x10, 0x8e, 0xc0, 0x31, 0xdb, 0xc1, 0xeb, 0x04, 0x8c,
0xc0, 0x01, 0xd8, 0x8e, 0xc0, 0x31, 0xdb, 0xe8, 0xa5, 0x00, 0x73, 0xf0, 0x66, 0xa1, 0xab, 0x00,
0xea, 0x00, 0x02, 0x00, 0x10, 0xeb, 0x7f, 0x66, 0x60, 0x66, 0x05, 0x8a, 0xf5, 0x7f, 0x00, 0x06,
0x53, 0x52, 0x66, 0x50, 0x66, 0x31, 0xf6, 0x66, 0xc7, 0x04, 0x10, 0x00, 0x01, 0x00, 0x89, 0x5c,
0x04, 0x8c, 0x44, 0x06, 0x66, 0x89, 0x44, 0x08, 0x66, 0x89, 0x74, 0x0c, 0xb4, 0x08, 0xcd, 0x13,
0x88, 0xc8, 0x86, 0xe9, 0xc0, 0xc5, 0x02, 0x80, 0xe5, 0x03, 0x89, 0xcd, 0x25, 0x3f, 0x00, 0x89,
0xc3, 0xb2, 0x00, 0x86, 0xd6, 0x42, 0xf7, 0xe2, 0x91, 0x58, 0x5a, 0xe3, 0x39, 0x39, 0xca, 0x73,
0x2a, 0xf7, 0xf1, 0x39, 0xc5, 0x76, 0x24, 0x91, 0x92, 0xf6, 0xf3, 0x86, 0xcd, 0xc0, 0xc9, 0x02,
0x08, 0xe1, 0x41, 0x5a, 0x88, 0xc6, 0x5b, 0x07, 0xbf, 0x05, 0x00, 0xb8, 0x01, 0x02, 0xcd, 0x13,
0x73, 0x27, 0x31, 0xc0, 0xcd, 0x13, 0x4f, 0x75, 0xf2, 0xeb, 0x03, 0x5a, 0x5b, 0x07, 0x31, 0xf6,
0xb4, 0x42, 0xcd, 0x13, 0x73, 0x13, 0xbe, 0xd4, 0x01, 0x31, 0xdb, 0xb4, 0x0e, 0xac, 0xcd, 0x10,
0x3c, 0x2e, 0x75, 0xf9, 0x93, 0xcd, 0x16, 0xcd, 0x19, 0x66, 0x61, 0xc3, 0x01, 0x46, 0x00, 0x8b,
0x76, 0x00, 0xe8, 0x7a, 0x00, 0x83, 0x3c, 0x00, 0x75, 0x27, 0xb8, 0x08, 0x00, 0x01, 0x46, 0x00,
0xff, 0x4e, 0x02, 0x74, 0x08, 0x83, 0x4c, 0x08, 0x00, 0x75, 0xe4, 0xf9, 0xc3, 0xff, 0x46, 0x02,
0xfe, 0x0e, 0x10, 0x00, 0x7c, 0xf5, 0x81, 0xfd, 0x00, 0x90, 0x74, 0xd0, 0x83, 0xed, 0x04, 0xeb,
0xce, 0xff, 0x44, 0xfe, 0xff, 0x0c, 0x80, 0x3e, 0x10, 0x00, 0x02, 0x74, 0x24, 0x06, 0x53, 0x1e,
0x07, 0x8d, 0x5e, 0x08, 0xc1, 0xe3, 0x0b, 0xe8, 0x17, 0x00, 0x93, 0x5b, 0x07, 0x83, 0xc5, 0x04,
0x89, 0x7e, 0x00, 0x29, 0xf8, 0xc1, 0xe8, 0x03, 0x89, 0x46, 0x02, 0xfe, 0x06, 0x10, 0x00, 0xeb,
0x9e, 0xbe, 0x00, 0x02, 0x89, 0xdf, 0x8a, 0x4c, 0x2c, 0x80, 0xe9, 0x09, 0x66, 0xd3, 0xe0, 0x8b,
0x4c, 0x28, 0xc1, 0xe9, 0x09, 0xe8, 0xef, 0xfe, 0x01, 0xf3, 0x66, 0x40, 0xe2, 0xf7, 0xc3, 0x51,
0x66, 0xad, 0x8a, 0x0e, 0x4c, 0x02, 0x66, 0xd3, 0xe0, 0x66, 0x91, 0x66, 0x31, 0xc0, 0xad, 0x66,
0x01, 0xc8, 0x59, 0xc3, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x6e,
0x67, 0x20, 0x4f, 0x53, 0x3b, 0x20, 0x70, 0x72, 0x65, 0x73, 0x73, 0x20, 0x61, 0x6e, 0x79, 0x20,
0x6b, 0x65, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x72, 0x65, 0x62, 0x6f, 0x6f, 0x74, 0x2e, 0x55, 0xaa
};
#endif /* BFS_BOOTSECTOR */

258
src/bin/bfs_tools/lib/bfs.h Normal file
View File

@ -0,0 +1,258 @@
/* bfs - BFS definitions and helper functions
*
* Initial version by Axel Dörfler, axeld@pinc-software.de
* Parts of this code is based on work previously done by Marcus Overhagen
*
* Copyright 2001-2008 pinc Software. All Rights Reserved.
* This file may be used under the terms of the MIT License.
*/
#ifndef BFS_H
#define BFS_H
#include <SupportDefs.h>
#if !defined(BEOS_VERSION_DANO) && !defined(__HAIKU__)
# define B_BAD_DATA B_ERROR
#endif
struct block_run {
int32 allocation_group;
uint16 start;
uint16 length;
inline bool operator==(const block_run &run) const;
inline bool operator!=(const block_run &run) const;
inline bool IsZero() const;
inline void SetTo(int32 group, uint16 start, uint16 length = 1);
inline static block_run Run(int32 group, uint16 start, uint16 length = 1);
};
typedef block_run inode_addr;
//**************************************
#define BFS_DISK_NAME_LENGTH 32
struct disk_super_block
{
char name[BFS_DISK_NAME_LENGTH];
int32 magic1;
int32 fs_byte_order;
uint32 block_size;
uint32 block_shift;
off_t num_blocks;
off_t used_blocks;
int32 inode_size;
int32 magic2;
int32 blocks_per_ag;
int32 ag_shift;
int32 num_ags;
int32 flags;
block_run log_blocks;
off_t log_start;
off_t log_end;
int32 magic3;
inode_addr root_dir;
inode_addr indices;
int32 pad[8];
};
#define SUPER_BLOCK_FS_LENDIAN 'BIGE' /* BIGE */
#define SUPER_BLOCK_MAGIC1 'BFS1' /* BFS1 */
#define SUPER_BLOCK_MAGIC2 0xdd121031
#define SUPER_BLOCK_MAGIC3 0x15b6830e
#define SUPER_BLOCK_CLEAN 'CLEN' /* CLEN */
#define SUPER_BLOCK_DIRTY 'DIRT' /* DIRT */
//**************************************
#define NUM_DIRECT_BLOCKS 12
struct data_stream
{
block_run direct[NUM_DIRECT_BLOCKS];
off_t max_direct_range;
block_run indirect;
off_t max_indirect_range;
block_run double_indirect;
off_t max_double_indirect_range;
off_t size;
};
//**************************************
struct bfs_inode;
struct small_data
{
uint32 type;
uint16 name_size;
uint16 data_size;
char name[0]; // name_size long, followed by data
inline char *Name();
inline uint8 *Data();
inline small_data *Next();
inline bool IsLast(bfs_inode *inode);
};
// the file name is part of the small_data structure
#define FILE_NAME_TYPE 'CSTR'
#define FILE_NAME_NAME 0x13
#define FILE_NAME_NAME_LENGTH 1
//**************************************
#define SHORT_SYMLINK_NAME_LENGTH 144 // length incl. terminating '\0'
struct bfs_inode
{
int32 magic1;
inode_addr inode_num;
int32 uid;
int32 gid;
int32 mode; // see sys/stat.h
int32 flags;
bigtime_t create_time;
bigtime_t last_modified_time;
inode_addr parent;
inode_addr attributes;
uint32 type; // attribute type
int32 inode_size;
uint32 etc; // for in-memory structures (unused in OpenBeOS' fs)
union {
data_stream data;
char short_symlink[SHORT_SYMLINK_NAME_LENGTH];
};
int32 pad[4];
small_data small_data_start[0];
};
#define INODE_MAGIC1 0x3bbe0ad9
#define INODE_TIME_SHIFT 16
#define INODE_FILE_NAME_LENGTH 256
enum inode_flags
{
INODE_IN_USE = 0x00000001, // always set
INODE_ATTR_INODE = 0x00000004,
INODE_LOGGED = 0x00000008, // log changes to the data stream
INODE_DELETED = 0x00000010,
INODE_EMPTY = 0x00000020,
INODE_LONG_SYMLINK = 0x00000040, // symlink in data stream
INODE_PERMANENT_FLAGS = 0x0000ffff,
INODE_NO_CACHE = 0x00010000,
INODE_WAS_WRITTEN = 0x00020000,
INODE_NO_TRANSACTION = 0x00040000
};
//**************************************
inline int32
divide_roundup(int32 num,int32 divisor)
{
return (num + divisor - 1) / divisor;
}
inline int64
divide_roundup(int64 num,int32 divisor)
{
return (num + divisor - 1) / divisor;
}
inline int
get_shift(uint64 i)
{
int c;
c = 0;
while (i > 1) {
i >>= 1;
c++;
}
return c;
}
inline int32
round_up(uint32 data)
{
// rounds up to the next off_t boundary
return (data + sizeof(off_t) - 1) & ~(sizeof(off_t) - 1);
}
/************************ block_run inline functions ************************/
// #pragma mark -
inline bool block_run::operator==(const block_run &run) const
{
return allocation_group == run.allocation_group
&& start == run.start
&& length == run.length;
}
inline bool block_run::operator!=(const block_run &run) const
{
return allocation_group != run.allocation_group
|| start != run.start
|| length != run.length;
}
inline bool block_run::IsZero() const
{
return allocation_group == 0 && start == 0 && length == 0;
}
inline void block_run::SetTo(int32 _group,uint16 _start,uint16 _length)
{
allocation_group = _group;
start = _start;
length = _length;
}
inline block_run block_run::Run(int32 group, uint16 start, uint16 length)
{
block_run run;
run.allocation_group = group;
run.start = start;
run.length = length;
return run;
}
/************************ small_data inline functions ************************/
// #pragma mark -
inline char *small_data::Name()
{
return name;
}
inline uint8 *small_data::Data()
{
return (uint8 *)name + name_size + 3;
}
inline small_data *small_data::Next()
{
return (small_data *)((uint8 *)(this + 1) + name_size + 3 + data_size + 1);
}
inline bool small_data::IsLast(bfs_inode *inode)
{
return (uint32)this > (uint32)inode + inode->inode_size - sizeof(small_data)
|| name_size == 0;
}
#endif /* BFS_H */

View File

@ -0,0 +1,322 @@
/*
* Copyright 2001-2010, pinc Software. All Rights Reserved.
*/
//! BFS structure dump and helper functions
#include "BPlusTree.h"
#include "Inode.h"
#include "dump.h"
#include <File.h>
#include <Mime.h>
#include <stdio.h>
#include <string.h>
#define Print printf
char *
get_tupel(uint32 id)
{
static unsigned char tupel[5];
tupel[0] = 0xff & (id >> 24);
tupel[1] = 0xff & (id >> 16);
tupel[2] = 0xff & (id >> 8);
tupel[3] = 0xff & (id);
tupel[4] = 0;
for (int16 i = 0;i < 4;i++)
if (tupel[i] < ' ' || tupel[i] > 128)
tupel[i] = '.';
return (char *)tupel;
}
void
dump_block_run(const char *prefix, const block_run &run, const char *postfix)
{
Print("%s(%ld, %d, %d)%s\n", prefix, run.allocation_group,
run.start, run.length, postfix);
}
void
dump_super_block(const disk_super_block *superBlock)
{
Print("disk_super_block:\n");
Print(" name = %s\n", superBlock->name);
Print(" magic1 = %#08lx (%s) %s\n", superBlock->magic1,
get_tupel(superBlock->magic1),
superBlock->magic1 == SUPER_BLOCK_MAGIC1 ? "valid" : "INVALID");
Print(" fs_byte_order = %#08lx (%s, %s endian)\n",
superBlock->fs_byte_order, get_tupel(superBlock->fs_byte_order),
superBlock->fs_byte_order == SUPER_BLOCK_FS_LENDIAN ? "little" : "big");
Print(" block_size = %lu\n", superBlock->block_size);
Print(" block_shift = %lu\n", superBlock->block_shift);
Print(" num_blocks = %Lu\n", superBlock->num_blocks);
Print(" used_blocks = %Lu\n", superBlock->used_blocks);
Print(" inode_size = %lu\n", superBlock->inode_size);
Print(" magic2 = %#08lx (%s) %s\n", superBlock->magic2,
get_tupel(superBlock->magic2),
superBlock->magic2 == (int)SUPER_BLOCK_MAGIC2 ? "valid" : "INVALID");
Print(" blocks_per_ag = %lu\n", superBlock->blocks_per_ag);
Print(" ag_shift = %lu\n", superBlock->ag_shift);
Print(" num_ags = %lu\n", superBlock->num_ags);
Print(" flags = %#08lx (%s)\n", superBlock->flags,
get_tupel(superBlock->flags));
dump_block_run(" log_blocks = ", superBlock->log_blocks);
Print(" log_start = %Lu\n", superBlock->log_start);
Print(" log_end = %Lu\n", superBlock->log_end);
Print(" magic3 = %#08lx (%s) %s\n", superBlock->magic3,
get_tupel(superBlock->magic3),
superBlock->magic3 == SUPER_BLOCK_MAGIC3 ? "valid" : "INVALID");
dump_block_run(" root_dir = ", superBlock->root_dir);
dump_block_run(" indices = ", superBlock->indices);
}
void
dump_data_stream(const bfs_inode *inode, const data_stream *stream, bool showOffsets)
{
Print("data_stream:\n");
off_t offset = 0;
for (int i = 0; i < NUM_DIRECT_BLOCKS; i++) {
if (!stream->direct[i].IsZero()) {
Print(" direct[%02d] = ", i);
char buffer[256];
if (showOffsets)
snprintf(buffer, sizeof(buffer), " %16lld", offset);
else
buffer[0] = '\0';
dump_block_run("", stream->direct[i], buffer);
offset += stream->direct[i].length * inode->inode_size;
}
}
Print(" max_direct_range = %Lu\n", stream->max_direct_range);
if (!stream->indirect.IsZero())
dump_block_run(" indirect = ", stream->indirect);
Print(" max_indirect_range = %Lu\n", stream->max_indirect_range);
if (!stream->double_indirect.IsZero()) {
dump_block_run(" double_indirect = ",
stream->double_indirect);
}
Print(" max_double_indirect_range = %Lu\n",
stream->max_double_indirect_range);
Print(" size = %Lu\n", stream->size);
}
void
dump_inode(const Inode *nameNode, const bfs_inode *inode, bool showOffsets)
{
if (nameNode != NULL)
Print("inode \"%s\":\n", nameNode->Name());
else
Print("inode:\n");
Print(" magic1 = %08lx (%s) %s\n",inode->magic1,
get_tupel(inode->magic1), (inode->magic1 == INODE_MAGIC1 ? "valid" : "INVALID"));
dump_block_run( " inode_num = ",inode->inode_num);
Print(" uid = %lu\n",inode->uid);
Print(" gid = %lu\n",inode->gid);
Print(" mode = %10lo (octal)\n",inode->mode);
Print(" flags = %08lx\n",inode->flags);
time_t time;
time = (time_t)(inode->create_time >> 16);
Print(" create_time = %s",ctime(&time));
time = (time_t)(inode->last_modified_time >> 16);
Print(" last_modified_time = %s",ctime(&time));
dump_block_run( " parent = ",inode->parent);
dump_block_run( " attributes = ",inode->attributes);
Print(" type = %lu\n",inode->type);
Print(" inode_size = %lu\n",inode->inode_size);
Print(" etc = %#08lx\n",inode->etc);
Print(" short_symlink = %s\n",
S_ISLNK(inode->mode) && (inode->flags & INODE_LONG_SYMLINK) == 0
? inode->short_symlink : "-");
dump_data_stream(inode, &inode->data, showOffsets);
Print(" --\n");
#if 0
Print(" --\n pad[0] = %08lx\n", inode->pad[0]);
Print(" pad[1] = %08lx\n", inode->pad[1]);
Print(" pad[2] = %08lx\n", inode->pad[2]);
Print(" pad[3] = %08lx\n", inode->pad[3]);
#endif
}
void
dump_small_data(Inode *inode)
{
if (inode == NULL || inode->InodeBuffer() == NULL)
return;
small_data *item = NULL;
printf("small data section (max. %ld bytes):\n",
inode->InodeBuffer()->inode_size - sizeof(struct bfs_inode));
while (inode->GetNextSmallData(&item) == B_OK) {
printf("%#08lx (%s), name = \"%s\", ", item->type, get_tupel(item->type), item->Name());
if (item->type == FILE_NAME_TYPE
|| item->type == B_STRING_TYPE
|| item->type == B_MIME_STRING_TYPE)
printf("data = \"%s\", ", item->Data());
printf("%u bytes\n", item->data_size);
}
}
void
dump_bplustree_header(const bplustree_header* header)
{
printf("bplustree_header:\n");
printf(" magic = %#08lx (%s) %s\n", header->magic,
get_tupel(header->magic),
header->magic == BPLUSTREE_MAGIC ? "valid" : "INVALID");
printf(" node_size = %lu\n", header->node_size);
printf(" max_number_of_levels = %lu\n", header->max_number_of_levels);
printf(" data_type = %lu\n", header->data_type);
printf(" root_node_pointer = %Ld\n", header->root_node_pointer);
printf(" free_node_pointer = %Ld\n", header->free_node_pointer);
printf(" maximum_size = %Lu\n", header->maximum_size);
}
void
dump_bplustree_node(const bplustree_node* node, const bplustree_header* header,
Disk* disk)
{
Print("bplustree_node (%s node):\n",
node->overflow_link == BPLUSTREE_NULL ? "leaf" : "index");
Print(" left_link = %Ld\n", node->left_link);
Print(" right_link = %Ld\n", node->right_link);
Print(" overflow_link = %Ld\n", node->overflow_link);
Print(" all_key_count = %u\n", node->all_key_count);
Print(" all_key_length = %u\n", node->all_key_length);
if (header == NULL)
return;
if (node->all_key_count > node->all_key_length
|| uint32(node->all_key_count * 10) > (uint32)header->node_size) {
Print("\n");
dump_block((char *)node, header->node_size, sizeof(off_t));
return;
}
Print("\n");
for (int32 i = 0;i < node->all_key_count;i++) {
uint16 length;
char* key = (char *)node->KeyAt(i, &length);
if (length > 255) {
Print(" %2ld. Invalid length (%u)!!\n", i, length);
dump_block((char *)node, header->node_size, sizeof(off_t));
break;
}
char buffer[256];
memcpy(buffer, key, length);
buffer[length] = '\0';
off_t *value = node->Values() + i;
if ((uint32)value < (uint32)node || (uint32)value > (uint32)node + header->node_size)
Print(" %2ld. Invalid Offset!!\n",i);
else {
Print(" %2ld. ",i);
if (header->data_type == BPLUSTREE_STRING_TYPE)
Print("\"%s\"",buffer);
else if (header->data_type == BPLUSTREE_INT32_TYPE) {
Print("int32 = %ld (0x%lx)", *(int32 *)&buffer,
*(int32 *)&buffer);
} else if (header->data_type == BPLUSTREE_UINT32_TYPE) {
Print("uint32 = %lu (0x%lx)", *(uint32 *)&buffer,
*(uint32 *)&buffer);
} else if (header->data_type == BPLUSTREE_INT64_TYPE) {
Print("int64 = %Ld (0x%Lx)", *(int64 *)&buffer,
*(int64 *)&buffer);
} else
Print("???");
off_t offset = *value & 0x3fffffffffffffffLL;
Print(" (%d bytes) -> %Ld",length,offset);
if (disk != NULL) {
block_run run = disk->ToBlockRun(offset);
Print(" (%ld, %d)", run.allocation_group, run.start);
}
if (bplustree_node::LinkType(*value)
== BPLUSTREE_DUPLICATE_FRAGMENT) {
Print(" (duplicate fragment %Ld)\n", *value & 0x3ff);
} else if (bplustree_node::LinkType(*value)
== BPLUSTREE_DUPLICATE_NODE) {
Print(" (duplicate node)\n");
} else
Print("\n");
}
}
}
void
dump_block(const char *buffer, uint32 size, int8 valueSize)
{
const uint32 kBlockSize = 16;
for (uint32 i = 0; i < size;) {
uint32 start = i;
for (; i < start + kBlockSize; i++) {
if (!(i % 4))
Print(" ");
if (i >= size)
Print(" ");
else
Print("%02x", *(unsigned char *)(buffer + i));
}
Print(" ");
for (i = start; i < start + kBlockSize; i++) {
if (i < size) {
char c = *(buffer + i);
if (c < 30)
Print(".");
else
Print("%c",c);
}
else
break;
}
if (valueSize > 0) {
Print(" (");
for (uint32 offset = start; offset < start + kBlockSize;
offset += valueSize) {
if (valueSize == sizeof(off_t))
Print("%s%Ld", offset == start ? "" : ", ",
*(off_t *)(buffer + offset));
}
Print(")");
}
Print("\n");
}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright 2001-2010 pinc Software. All Rights Reserved.
*/
#ifndef DUMP_H
#define DUMP_H
#include "bfs.h"
struct bplustree_node;
struct bplustree_header;
class Inode;
extern void dump_block_run(const char* prefix, const block_run& run,
const char *postfix = NULL);
extern void dump_super_block(const disk_super_block* superBlock);
extern void dump_data_stream(const bfs_inode* inode, const data_stream* stream,
bool showOffsets = false);
extern void dump_small_data(Inode* inode);
extern void dump_inode(const Inode* node, const bfs_inode* inode,
bool showOffsets = false);
extern char* get_tupel(uint32 id);
extern void dump_bplustree_header(const bplustree_header* header);
extern void dump_bplustree_node(const bplustree_node* node,
const bplustree_header* header, Disk* disk = NULL);
extern void dump_block(const char *buffer, uint32 size, int8 valueSize = 0);
#endif /* DUMP_H */

View File

@ -0,0 +1,112 @@
## BeOS Generic Makefile v2.01 ##
## Fill in this file to specify the project being created, and the referenced
## makefile-engine will do all of the hard work for you. This handles both
## Intel and PowerPC builds of the BeOS.
## Application Specific Settings ---------------------------------------------
# specify the name of the binary
NAME = libbfs.a
# specify the type of binary
# APP: Application
# SHARED: Shared library or add-on
# STATIC: Static library archive
# DRIVER: Kernel Driver
TYPE = STATIC
# add support for new Pe and Eddie features
# to fill in generic makefile
#%{
# @src->@
# specify the source files to use
# full paths or paths relative to the makefile can be included
# all files, regardless of directory, will have their object
# files created in the common object directory.
# Note that this means this makefile will not work correctly
# if two source files with the same name (source.c or source.cpp)
# are included from different directories. Also note that spaces
# in folder names do not work well with this makefile.
SRCS = bfs.cpp Disk.cpp Inode.cpp Bitmap.cpp BPlusTree.cpp Hashtable.cpp dump.cpp
# specify the resource files to use
# full path or a relative path to the resource file can be used.
RSRCS =
# @<-src@
#%}
# end support for Pe and Eddie
# specify additional libraries to link against
# there are two acceptable forms of library specifications
# - if your library follows the naming pattern of:
# libXXX.so or libXXX.a you can simply specify XXX
# library: libbe.so entry: be
#
# - if your library does not follow the standard library
# naming scheme you need to specify the path to the library
# and it's name
# library: my_lib.a entry: my_lib.a or path/my_lib.a
LIBS = be
# specify additional paths to directories following the standard
# libXXX.so or libXXX.a naming scheme. You can specify full paths
# or paths relative to the makefile. The paths included may not
# be recursive, so include all of the paths where libraries can
# be found. Directories where source files are found are
# automatically included.
LIBPATHS=
# additional paths to look for system headers
# thes use the form: #include <header>
# source file directories are NOT auto-included here
SYSTEM_INCLUDE_PATHS =
# additional paths to look for local headers
# thes use the form: #include "header"
# source file directories are automatically included
LOCAL_INCLUDE_PATHS =
# specify the level of optimization that you desire
# NONE, SOME, FULL
OPTIMIZE= NONE
# specify any preprocessor symbols to be defined. The symbols
# will be set to a value of 1. For example specify DEBUG if you want
# DEBUG=1 to be set when compiling.
DEFINES=
# specify special warning levels
# if unspecified default warnings will be used
# NONE = supress all warnings
# ALL = enable all warnings
WARNINGS = ALL
# specify whether image symbols will be created
# so that stack crawls in the debugger are meaningful
# if TRUE symbols will be created
SYMBOLS = TRUE
# specify debug settings
# if TRUE will allow application to be run from
# a source-level debugger
DEBUGGER =
# specify additional compiler flags for all files
COMPILER_FLAGS =
# specify additional linker flags
LINKER_FLAGS =
TARGET_DIR=.
INSTALL_DIR=/boot/home/config/bin
## include the makefile-engine
include /boot/develop/etc/makefile-engine
zip backup tar:
@zip -y `basename $(NAME)`-`date +%Y-%m-%d`.zip *.[ch]* *.rsrc makefile History ToDo test/*.[ch]* test/makefile

View File

@ -0,0 +1,816 @@
/*
* Copyright (c) 2001-2008 pinc Software. All Rights Reserved.
*/
//! recovers corrupt BFS disks
#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;
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 %Ld: %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(InodeHashtable& hashtable, block_run run)
{
fInode = hashtable.Get(run);
}
~InodeGetter()
{
if (fInode != NULL)
fInode->ReleaseBuffer();
}
Inode* Node() { return fInode; }
private:
Inode* fInode;
};
InodeHashtable gHashtable(1000);
// 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:
virtual Inode *InodeAt(block_run run)
{
Inode *inode;
if ((inode = gHashtable.Get(run)) != NULL)
return inode;
if ((inode = gLogged.Get(run)) != NULL)
return inode;
if ((inode = gMissing.Get(run)) != NULL)
return inode;
return NULL;
}
};
void
collectInodes(Disk &disk, InodeHashtable &hashtable, off_t start, off_t end)
{
char buffer[8192];
Inode inode(&disk, (bfs_inode *)buffer, false);
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: %Ld \"%s\"\n", position, node->Name());
hashtable.Insert(node);
count++;
} else if (gVerbose) {
printf("\nunrecognized inode:");
dump_inode(&inode, inode.InodeBuffer());
}
}
position += disk.BlockSize();
}
if (system_time() - lastUpdate > 500000) {
printf(" block %Ld (%Ld%%), %Ld inodes\33[1A\n", offset, 100 * (offset - start) / (end - start), count);
lastUpdate = system_time();
}
}
printf("\n%Ld inodes found.\n", count);
Inode *node;
off_t directories = 0LL;
off_t directorySize = 0LL;
off_t files = 0LL;
off_t fileSize = 0LL;
off_t symlinks = 0LL;
count = 0LL;
hashtable.Rewind();
while (hashtable.GetNextEntry(&node) == B_OK) {
if (node->IsDirectory()) {
directories++;
directorySize += node->Size();
} else if (node->IsFile()) {
files++;
fileSize += node->Size();
} else if (node->IsSymlink()) {
symlinks++;
}
count++;
hashtable.Release(node);
}
printf("\n%20Ld directories found (total of %Ld bytes)\n"
"%20Ld files found (total of %Ld bytes)\n"
"%20Ld symlinks found\n"
"--------------------\n"
"%20Ld inodes total found in hashtable.\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 %Ld to %Ld (log area)\n",offset,end);
collectInodes(disk, 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 = /*(17LL << disk.SuperBlock()->ag_shift);
if (end > disk.NumBlocks())
end = */disk.NumBlocks();
end *= disk.BlockSize();
printf("\nsearching from %Ld to %Ld (main area)\n", offset, end);
collectInodes(disk, gHashtable, offset, end);
}
Directory *
getNameIndex(Disk &disk)
{
InodeGetter getter(gHashtable, disk.Indices());
Directory *indices = dynamic_cast<Directory *>(getter.Node());
block_run run;
if (indices && indices->FindEntry("name", &run) == B_OK)
return dynamic_cast<Directory *>(gHashtable.Get(run));
// search name index
Inode *node;
gHashtable.Rewind();
for (; gHashtable.GetNextEntry(&node) == B_OK; gHashtable.Release(node)) {
if (!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()
|| gHashtable.Contains(&run))
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 (%ld, %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\" (%ld, %d): node \"%s\" is "
"missing (%ld, %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)
{
Inode *node;
off_t count = 0;
gHashtable.Rewind();
while (gHashtable.GetNextEntry(&node) == B_OK) {
count++;
if ((count % 50) == 0)
fprintf(stderr, "%Ld inodes processed...\33[1A\n", count);
if (node->IsDirectory() && !node->IsIndex()) {
// check if all entries are in the hashtable
checkDirectoryContents(disk, (Directory*)node);
}
// check for the parent directory
block_run run = node->Parent();
InodeGetter parentGetter(gHashtable, 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 %ld,%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\" (%ld, %d, mode = %10lo): parent directory is "
"missing (%ld, %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\" (%ld, %d, mode = %10lo): parent directory "
"found in missing list (%ld, %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\" (%ld, %d, mode = %10lo): parent directory "
"found in missing list (%ld, %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 (!gHashtable.Contains(&run)) {
if (gVerbose) {
printf("node \"%s\": attributes are missing (%ld, %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("%Ld 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: %ld:%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\" (%ld, %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\" (%ld, %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 == (%5ld,%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(const char *copyTo)
{
if (!copyTo)
return;
Inode::Source *source = new HashtableInodeSource;
Inode *node;
int32 count = 0;
gHashtable.Rewind();
while (gHashtable.GetNextEntry(&node) == B_OK) {
if (!node->IsIndex() && !node->IsAttributeDirectory())
node->CopyTo(copyTo, source);
if ((++count % 500) == 0)
fprintf(stderr, "copied %ld files...\n", count);
gHashtable.Release(node);
}
gMissing.Rewind();
while (gMissing.GetNextEntry(&node) == B_OK) {
if (!node->IsIndex() && !node->IsAttributeDirectory())
node->CopyTo(copyTo, source);
gHashtable.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 super block 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 super block is corrupt!\n");
if (disk.RecreateSuperBlock() < B_OK) {
fprintf(stderr, "Can't recreate the disk's super block, sorry!\n");
return -1;
}
recreatedSuperBlock = true;
}
if (gVerbose) {
puts("\n*** The super block:\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 super block: %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(argv[1]);
//disk.WriteBootBlock();
//disk.BlockBitmap()->CompareWithBackup();
gHashtable.MakeEmpty();
gMissing.MakeEmpty();
gLogged.MakeEmpty();
return 0;
}