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:
parent
8a7256ae14
commit
647cff2e59
|
@ -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
|
||||
|
|
|
@ -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 ;
|
||||
|
|
|
@ -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 ;
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
@ -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 */
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
@ -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 */
|
|
@ -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
|
||||
;
|
|
@ -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 */
|
|
@ -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 */
|
||||
|
||||
|
|
@ -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 */
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
@ -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 */
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue