From 647cff2e5925f2a1483b94aaff5620647ddf11bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Mon, 26 Mar 2012 22:41:58 +0200 Subject: [PATCH] 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. --- build/jam/HaikuImage | 2 +- src/bin/Jamfile | 3 +- src/bin/bfs_tools/Jamfile | 18 + src/bin/bfs_tools/TODO | 32 + src/bin/bfs_tools/bfsinfo.cpp | 307 ++++++ src/bin/bfs_tools/bfswhich.cpp | 319 ++++++ src/bin/bfs_tools/chkindex.cpp | 523 ++++++++++ src/bin/bfs_tools/lib/BPlusTree.cpp | 1185 +++++++++++++++++++++++ src/bin/bfs_tools/lib/BPlusTree.h | 291 ++++++ src/bin/bfs_tools/lib/Bitmap.cpp | 266 +++++ src/bin/bfs_tools/lib/Bitmap.h | 46 + src/bin/bfs_tools/lib/Cache.h | 192 ++++ src/bin/bfs_tools/lib/Disk.cpp | 751 ++++++++++++++ src/bin/bfs_tools/lib/Disk.h | 108 +++ src/bin/bfs_tools/lib/Hashtable.cpp | 284 ++++++ src/bin/bfs_tools/lib/Hashtable.h | 62 ++ src/bin/bfs_tools/lib/Inode.cpp | 1395 +++++++++++++++++++++++++++ src/bin/bfs_tools/lib/Inode.h | 199 ++++ src/bin/bfs_tools/lib/Jamfile | 13 + src/bin/bfs_tools/lib/Stack.h | 57 ++ src/bin/bfs_tools/lib/bfs.cpp | 51 + src/bin/bfs_tools/lib/bfs.h | 258 +++++ src/bin/bfs_tools/lib/dump.cpp | 322 +++++++ src/bin/bfs_tools/lib/dump.h | 32 + src/bin/bfs_tools/lib/makefile | 112 +++ src/bin/bfs_tools/recover.cpp | 816 ++++++++++++++++ 26 files changed, 7642 insertions(+), 2 deletions(-) create mode 100644 src/bin/bfs_tools/Jamfile create mode 100644 src/bin/bfs_tools/TODO create mode 100644 src/bin/bfs_tools/bfsinfo.cpp create mode 100644 src/bin/bfs_tools/bfswhich.cpp create mode 100644 src/bin/bfs_tools/chkindex.cpp create mode 100644 src/bin/bfs_tools/lib/BPlusTree.cpp create mode 100644 src/bin/bfs_tools/lib/BPlusTree.h create mode 100644 src/bin/bfs_tools/lib/Bitmap.cpp create mode 100644 src/bin/bfs_tools/lib/Bitmap.h create mode 100644 src/bin/bfs_tools/lib/Cache.h create mode 100644 src/bin/bfs_tools/lib/Disk.cpp create mode 100644 src/bin/bfs_tools/lib/Disk.h create mode 100644 src/bin/bfs_tools/lib/Hashtable.cpp create mode 100644 src/bin/bfs_tools/lib/Hashtable.h create mode 100644 src/bin/bfs_tools/lib/Inode.cpp create mode 100644 src/bin/bfs_tools/lib/Inode.h create mode 100644 src/bin/bfs_tools/lib/Jamfile create mode 100644 src/bin/bfs_tools/lib/Stack.h create mode 100644 src/bin/bfs_tools/lib/bfs.cpp create mode 100644 src/bin/bfs_tools/lib/bfs.h create mode 100644 src/bin/bfs_tools/lib/dump.cpp create mode 100644 src/bin/bfs_tools/lib/dump.h create mode 100644 src/bin/bfs_tools/lib/makefile create mode 100644 src/bin/bfs_tools/recover.cpp diff --git a/build/jam/HaikuImage b/build/jam/HaikuImage index a9497ee942..8647168c73 100644 --- a/build/jam/HaikuImage +++ b/build/jam/HaikuImage @@ -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 diff --git a/src/bin/Jamfile b/src/bin/Jamfile index ede13c8f8a..3fb534a375 100644 --- a/src/bin/Jamfile +++ b/src/bin/Jamfile @@ -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 ; diff --git a/src/bin/bfs_tools/Jamfile b/src/bin/bfs_tools/Jamfile new file mode 100644 index 0000000000..b286992667 --- /dev/null +++ b/src/bin/bfs_tools/Jamfile @@ -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 ; diff --git a/src/bin/bfs_tools/TODO b/src/bin/bfs_tools/TODO new file mode 100644 index 0000000000..e2e8335c63 --- /dev/null +++ b/src/bin/bfs_tools/TODO @@ -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 + diff --git a/src/bin/bfs_tools/bfsinfo.cpp b/src/bin/bfs_tools/bfsinfo.cpp new file mode 100644 index 0000000000..6b7438b8f6 --- /dev/null +++ b/src/bin/bfs_tools/bfsinfo.cpp @@ -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 +#include +#include +#include + + +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] [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; +} + diff --git a/src/bin/bfs_tools/bfswhich.cpp b/src/bin/bfs_tools/bfswhich.cpp new file mode 100644 index 0000000000..973cdce6eb --- /dev/null +++ b/src/bin/bfs_tools/bfswhich.cpp @@ -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 + +#include +#include +#include +#include +#include +#include + + +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(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(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(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 \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; +} + diff --git a/src/bin/bfs_tools/chkindex.cpp b/src/bin/bfs_tools/chkindex.cpp new file mode 100644 index 0000000000..e31a827fda --- /dev/null +++ b/src/bin/bfs_tools/chkindex.cpp @@ -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 + +#include +#include +#include +#include +#include +#include + + +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(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; +} + diff --git a/src/bin/bfs_tools/lib/BPlusTree.cpp b/src/bin/bfs_tools/lib/BPlusTree.cpp new file mode 100644 index 0000000000..9493593332 --- /dev/null +++ b/src/bin/bfs_tools/lib/BPlusTree.cpp @@ -0,0 +1,1185 @@ +/* BPlusTree - BFS B+Tree implementation +** +** Copyright 2001-2002 pinc Software. All Rights Reserved. +*/ + + +#include "BPlusTree.h" +#include "Inode.h" +#include "Stack.h" +#include "dump.h" + +#include + +#include +#include +#include + + +#define MAX_NODES_IN_CACHE 20 + +class CacheableNode : public NodeCache::Cacheable +{ + public: + CacheableNode(off_t offset,bplustree_node *node) + : + fOffset(offset), + fNode(node) + { + } + + virtual ~CacheableNode() + { + if (fNode) + free(fNode); + } + + virtual bool Equals(off_t offset) + { + return offset == fOffset; + } + + off_t fOffset; + bplustree_node *fNode; +}; + + +NodeCache::NodeCache(BPlusTree *tree) + : Cache(), + fTree(tree) +{ +} + + +Cache::Cacheable * +NodeCache::NewCacheable(off_t offset) +{ + return new CacheableNode(offset,fTree->Node(offset,false)); +} + + +bplustree_node * +NodeCache::Get(off_t offset) +{ + CacheableNode *node = (CacheableNode *)Cache::Get(offset); + return node->fNode; +} + + +// #pragma mark - + + +BPlusTree::BPlusTree(int32 keyType,int32 nodeSize,bool allowDuplicates) + : + fStream(NULL), + fHeader(NULL), + fCache(this), + fCurrentNodeOffset(BPLUSTREE_NULL) +{ + SetTo(keyType,nodeSize,allowDuplicates); +} + + +BPlusTree::BPlusTree(BPositionIO *stream,bool allowDuplicates) + : + fStream(NULL), + fHeader(NULL), + fCache(this), + fCurrentNodeOffset(BPLUSTREE_NULL) +{ + SetTo(stream,allowDuplicates); +} + + +BPlusTree::BPlusTree() + : + fStream(NULL), + fHeader(NULL), + fNodeSize(BPLUSTREE_NODE_SIZE), + fAllowDuplicates(true), + fStatus(B_NO_INIT), + fCache(this), + fCurrentNodeOffset(BPLUSTREE_NULL) +{ +} + + +BPlusTree::~BPlusTree() +{ + fCache.Clear(); +} + + +void BPlusTree::Initialize(int32 nodeSize) +{ + // free old data + fCache.Clear(0,true); + + if (fHeader) + free(fHeader); + + fStream = NULL; + + fNodeSize = nodeSize; + fHeader = (bplustree_header *)malloc(fNodeSize); + memset(fHeader,0,fNodeSize); + + fCurrentNodeOffset = BPLUSTREE_NULL; +} + + +status_t BPlusTree::SetTo(int32 keyType,int32 nodeSize,bool allowDuplicates) +{ + // initializes in-memory B+Tree + + Initialize(nodeSize); + + fAllowDuplicates = allowDuplicates; + fCache.SetHoldChanges(true); + + // initialize b+tree header + fHeader->magic = BPLUSTREE_MAGIC; + fHeader->node_size = fNodeSize; + fHeader->max_number_of_levels = 1; + fHeader->data_type = keyType; + fHeader->root_node_pointer = fNodeSize; + fHeader->free_node_pointer = BPLUSTREE_NULL; + fHeader->maximum_size = fNodeSize * 2; + + return fStatus = B_OK; +} + + +status_t BPlusTree::SetTo(BPositionIO *stream,bool allowDuplicates) +{ + // initializes on-disk B+Tree + + bplustree_header header; + ssize_t read = stream->ReadAt(0,&header,sizeof(bplustree_header)); + if (read < 0) + return fStatus = read; + + // is header valid? + + stream->Seek(0,SEEK_END); + off_t size = stream->Position(); + stream->Seek(0,SEEK_SET); + + //dump_bplustree_header(&header); + + if (header.magic != BPLUSTREE_MAGIC + || header.maximum_size != size + || (header.root_node_pointer % header.node_size) != 0 + || !header.IsValidLink(header.root_node_pointer) + || !header.IsValidLink(header.free_node_pointer)) + return fStatus = B_BAD_DATA; + + fAllowDuplicates = allowDuplicates; + + if (DataStream *dataStream = dynamic_cast(stream)) + { + uint32 toMode[] = {S_STR_INDEX, S_INT_INDEX, S_UINT_INDEX, S_LONG_LONG_INDEX, + S_ULONG_LONG_INDEX, S_FLOAT_INDEX, S_DOUBLE_INDEX}; + uint32 mode = dataStream->Mode() & (S_STR_INDEX | S_INT_INDEX | S_UINT_INDEX | S_LONG_LONG_INDEX + | S_ULONG_LONG_INDEX | S_FLOAT_INDEX | S_DOUBLE_INDEX); + + if (header.data_type > BPLUSTREE_DOUBLE_TYPE + || (dataStream->Mode() & S_INDEX_DIR) && toMode[header.data_type] != mode + || !dataStream->IsDirectory()) + return fStatus = B_BAD_TYPE; + + // although it's in stat.h, the S_ALLOW_DUPS flag is obviously unused + fAllowDuplicates = (dataStream->Mode() & (S_INDEX_DIR | 0777)) == S_INDEX_DIR; + + //printf("allows duplicates? %s\n",fAllowDuplicates ? "yes" : "no"); + } + + Initialize(header.node_size); + + memcpy(fHeader,&header,sizeof(bplustree_header)); + + fStream = stream; + + bplustree_node *node = fCache.Get(header.root_node_pointer); + //if (node) + // dump_bplustree_node(node); + + return fStatus = node && CheckNode(node) ? B_OK : B_BAD_DATA; +} + + +status_t BPlusTree::InitCheck() +{ + return fStatus; +} + + +struct validate_info { + off_t offset; + off_t from; + bool free; +}; + + +status_t +BPlusTree::Validate(bool verbose) +{ + // validates the on-disk b+tree + // (for now only the node integrity is checked) + + if (!fStream) + return B_OK; + + struct validate_info info; + + Stack stack; + info.offset = fHeader->root_node_pointer; + info.from = BPLUSTREE_NULL; + info.free = false; + stack.Push(info); + + if (fHeader->free_node_pointer != BPLUSTREE_NULL) { + info.offset = fHeader->free_node_pointer; + info.free = true; + stack.Push(info); + } + + char escape[3] = {0x1b, '[', 0}; + int32 count = 0,freeCount = 0; + + while (true) { + if (!stack.Pop(&info)) { + if (verbose) { + printf(" %ld B+tree node(s) processed (%ld free node(s)).\n", + count, freeCount); + } + return B_OK; + } + + if (!info.free && verbose && ++count % 10 == 0) + printf(" %ld%s1A\n", count, escape); + else if (info.free) + freeCount++; + + bplustree_node *node; + if ((node = fCache.Get(info.offset)) == NULL + || !info.free && !CheckNode(node)) { + if (verbose) { + fprintf(stderr," B+Tree: Could not get data at position %Ld (referenced by node at %Ld)\n",info.offset,info.from); + if ((node = fCache.Get(info.from)) != NULL) + dump_bplustree_node(node,fHeader); + } + return B_BAD_DATA; + } + + info.from = info.offset; + + if (node->overflow_link != BPLUSTREE_NULL && !info.free) { + info.offset = node->overflow_link; + stack.Push(info); + + //dump_bplustree_node(node,fHeader); + off_t *values = node->Values(); + for (int32 i = 0;i < node->all_key_count;i++) { + info.offset = values[i]; + stack.Push(info); + } + } else if (node->left_link != BPLUSTREE_NULL && info.free) { + info.offset = node->left_link; + stack.Push(info); + } + } +} + + +status_t +BPlusTree::WriteTo(BPositionIO *stream) +{ + ssize_t written = stream->WriteAt(0,fHeader,fNodeSize); + if (written < fNodeSize) + return written < B_OK ? written : B_IO_ERROR; + + for (off_t offset = fNodeSize;offset < fHeader->maximum_size;offset += fNodeSize) + { + bplustree_node *node = fCache.Get(offset); + if (node == NULL) + return B_ERROR; + + written = stream->WriteAt(offset,node,fNodeSize); + if (written < fNodeSize) + return written < B_OK ? written : B_IO_ERROR; + } + return stream->SetSize(fHeader->maximum_size); +} + + +// #pragma mark - + + +void BPlusTree::SetCurrentNode(bplustree_node *node,off_t offset,int8 to) +{ + fCurrentNodeOffset = offset; + fCurrentKey = to == BPLUSTREE_BEGIN ? -1 : node->all_key_count; + fDuplicateNode = BPLUSTREE_NULL; +} + + +status_t BPlusTree::Goto(int8 to) +{ + if (fHeader == NULL) + return B_BAD_VALUE; + + Stack stack; + if (stack.Push(fHeader->root_node_pointer) < B_OK) + return B_NO_MEMORY; + + bplustree_node *node; + off_t pos; + while (stack.Pop(&pos) && (node = fCache.Get(pos)) != NULL && CheckNode(node)) + { + // is the node a leaf node? + if (node->overflow_link == BPLUSTREE_NULL) + { + SetCurrentNode(node,pos,to); + + return B_OK; + } + + if (to == BPLUSTREE_END || node->all_key_count == 0) + pos = node->overflow_link; + else + { + if (node->all_key_length > fNodeSize + || (uint32)node->Values() > (uint32)node + fNodeSize - 8 * node->all_key_count) + return B_ERROR; + + pos = *node->Values(); + } + + if (stack.Push(pos) < B_OK) + break; + } + return B_ERROR; +} + + +status_t BPlusTree::Traverse(int8 direction,void *key,uint16 *keyLength,uint16 maxLength,off_t *value) +{ + if (fCurrentNodeOffset == BPLUSTREE_NULL + && Goto(direction == BPLUSTREE_FORWARD ? BPLUSTREE_BEGIN : BPLUSTREE_END) < B_OK) + return B_ERROR; + + bplustree_node *node; + + if (fDuplicateNode != BPLUSTREE_NULL) + { + // regardless of traverse direction the duplicates are always presented in + // the same order; since they are all considered as equal, this shouldn't + // cause any problems + + if (!fIsFragment || fDuplicate < fNumDuplicates) + node = fCache.Get(bplustree_node::FragmentOffset(fDuplicateNode)); + else + node = NULL; + + if (node != NULL) + { + if (!fIsFragment && fDuplicate >= fNumDuplicates) + { + // if the node is out of duplicates, we go directly to the next one + fDuplicateNode = node->right_link; + if (fDuplicateNode != BPLUSTREE_NULL + && (node = fCache.Get(fDuplicateNode)) != NULL) + { + fNumDuplicates = node->CountDuplicates(fDuplicateNode,false); + fDuplicate = 0; + } + } + if (fDuplicate < fNumDuplicates) + { + *value = node->DuplicateAt(fDuplicateNode,fIsFragment,fDuplicate++); + return B_OK; + } + } + fDuplicateNode = BPLUSTREE_NULL; + } + + off_t savedNodeOffset = fCurrentNodeOffset; + if ((node = fCache.Get(fCurrentNodeOffset)) == NULL || !CheckNode(node)) + return B_ERROR; + + fCurrentKey += direction; + + // is the current key in the current node? + while ((direction == BPLUSTREE_FORWARD && fCurrentKey >= node->all_key_count) + || (direction == BPLUSTREE_BACKWARD && fCurrentKey < 0)) + { + fCurrentNodeOffset = direction == BPLUSTREE_FORWARD ? node->right_link : node->left_link; + + // are there any more nodes? + if (fCurrentNodeOffset != BPLUSTREE_NULL) + { + node = fCache.Get(fCurrentNodeOffset); + if (!node || !CheckNode(node)) + return B_ERROR; + + // reset current key + fCurrentKey = direction == BPLUSTREE_FORWARD ? 0 : node->all_key_count; + } + else + { + // there are no nodes left, so turn back to the last key + fCurrentNodeOffset = savedNodeOffset; + fCurrentKey = direction == BPLUSTREE_FORWARD ? node->all_key_count : -1; + + return B_ENTRY_NOT_FOUND; + } + } + + if (node->all_key_count == 0) + return B_ERROR; //B_ENTRY_NOT_FOUND; + + uint16 length; + uint8 *keyStart = node->KeyAt(fCurrentKey,&length); + + length = min_c(length,maxLength); + memcpy(key,keyStart,length); + + if (fHeader->data_type == BPLUSTREE_STRING_TYPE) // terminate string type + { + if (length == maxLength) + length--; + ((char *)key)[length] = '\0'; + } + *keyLength = length; + + off_t offset = node->Values()[fCurrentKey]; + + // duplicate fragments? + uint8 type = bplustree_node::LinkType(offset); + if (type == BPLUSTREE_DUPLICATE_FRAGMENT || type == BPLUSTREE_DUPLICATE_NODE) + { + fDuplicateNode = offset; + + node = fCache.Get(bplustree_node::FragmentOffset(fDuplicateNode)); + if (node == NULL) + return B_ERROR; + + fIsFragment = type == BPLUSTREE_DUPLICATE_FRAGMENT; + + fNumDuplicates = node->CountDuplicates(offset,fIsFragment); + if (fNumDuplicates) + { + // give back first duplicate + fDuplicate = 1; + offset = node->DuplicateAt(offset,fIsFragment,0); + } + else + { + // shouldn't happen, but we're dealing here with corrupt disks... + fDuplicateNode = BPLUSTREE_NULL; + offset = 0; + } + } + *value = offset; + + return B_OK; +} + + +int32 BPlusTree::CompareKeys(const void *key1, int keyLength1, const void *key2, int keyLength2) +{ + switch (fHeader->data_type) + { + case BPLUSTREE_STRING_TYPE: + { + int len = min_c(keyLength1,keyLength2); + int result = strncmp((const char *)key1,(const char *)key2,len); + + if (result == 0) + result = keyLength1 - keyLength2; + + return result; + } + + case BPLUSTREE_INT32_TYPE: + return *(int32 *)key1 - *(int32 *)key2; + + case BPLUSTREE_UINT32_TYPE: + { + if (*(uint32 *)key1 == *(uint32 *)key2) + return 0; + else if (*(uint32 *)key1 > *(uint32 *)key2) + return 1; + + return -1; + } + + case BPLUSTREE_INT64_TYPE: + { + if (*(int64 *)key1 == *(int64 *)key2) + return 0; + else if (*(int64 *)key1 > *(int64 *)key2) + return 1; + + return -1; + } + + case BPLUSTREE_UINT64_TYPE: + { + if (*(uint64 *)key1 == *(uint64 *)key2) + return 0; + else if (*(uint64 *)key1 > *(uint64 *)key2) + return 1; + + return -1; + } + + case BPLUSTREE_FLOAT_TYPE: + { + float result = *(float *)key1 - *(float *)key2; + if (result == 0.0f) + return 0; + + return (result < 0.0f) ? -1 : 1; + } + + case BPLUSTREE_DOUBLE_TYPE: + { + double result = *(double *)key1 - *(double *)key2; + if (result == 0.0) + return 0; + + return (result < 0.0) ? -1 : 1; + } + } + return 0; +} + + +status_t BPlusTree::FindKey(bplustree_node *node,uint8 *key,uint16 keyLength,uint16 *index,off_t *next) +{ + if (node->all_key_count == 0) + { + if (index) + *index = 0; + if (next) + *next = node->overflow_link; + return B_ENTRY_NOT_FOUND; + } + + off_t *values = node->Values(); + int16 saveIndex = 0; + + // binary search in the key array + for (int16 first = 0,last = node->all_key_count - 1;first <= last;) + { + uint16 i = (first + last) >> 1; + + uint16 searchLength; + uint8 *searchKey = node->KeyAt(i,&searchLength); + + int32 cmp = CompareKeys(key,keyLength,searchKey,searchLength); + if (cmp < 0) + { + last = i - 1; + saveIndex = i; + } + else if (cmp > 0) + { + saveIndex = first = i + 1; + } + else + { + if (index) + *index = i; + if (next) + *next = values[i]; + return B_OK; + } + } + + if (index) + *index = saveIndex; + if (next) + { + if (saveIndex == node->all_key_count) + *next = node->overflow_link; + else + *next = values[saveIndex]; + } + return B_ENTRY_NOT_FOUND; +} + + +status_t BPlusTree::SeekDown(Stack &stack,uint8 *key,uint16 keyLength) +{ + node_and_key nodeAndKey; + nodeAndKey.nodeOffset = fHeader->root_node_pointer; + nodeAndKey.keyIndex = 0; + + bplustree_node *node; + while ((node = fCache.Get(nodeAndKey.nodeOffset)) != NULL && CheckNode(node)) { + // if we are already on leaf level, we're done + if (node->overflow_link == BPLUSTREE_NULL) { + // put the node on the stack + // node that the keyIndex is not properly set here! + nodeAndKey.keyIndex = 0; + stack.Push(nodeAndKey); + return B_OK; + } + + off_t nextOffset; + status_t status = FindKey(node,key,keyLength,&nodeAndKey.keyIndex,&nextOffset); + + if (status == B_ENTRY_NOT_FOUND && nextOffset == nodeAndKey.nodeOffset) + return B_ERROR; + + // put the node & the correct keyIndex on the stack + stack.Push(nodeAndKey); + + nodeAndKey.nodeOffset = nextOffset; + } + return B_ERROR; +} + + +void BPlusTree::InsertKey(bplustree_node *node,uint8 *key,uint16 keyLength,off_t value,uint16 index) +{ + // should never happen, but who knows? + if (index > node->all_key_count) + return; + + off_t *values = node->Values(); + uint16 *keyLengths = node->KeyLengths(); + uint8 *keys = node->Keys(); + + node->all_key_count++; + node->all_key_length += keyLength; + + off_t *newValues = node->Values(); + uint16 *newKeyLengths = node->KeyLengths(); + + // move values and copy new value into them + memmove(newValues + index + 1,values + index,sizeof(off_t) * (node->all_key_count - 1 - index)); + memmove(newValues,values,sizeof(off_t) * index); + + newValues[index] = value; + + // move and update key length index + for (uint16 i = node->all_key_count;i-- > index + 1;) + newKeyLengths[i] = keyLengths[i - 1] + keyLength; + memmove(newKeyLengths,keyLengths,sizeof(uint16) * index); + + int32 keyStart; + newKeyLengths[index] = keyLength + (keyStart = index > 0 ? newKeyLengths[index - 1] : 0); + + // move keys and copy new key into them + int32 size = node->all_key_length - newKeyLengths[index]; + if (size > 0) + memmove(keys + newKeyLengths[index],keys + newKeyLengths[index] - keyLength,size); + + memcpy(keys + keyStart,key,keyLength); +} + + +status_t BPlusTree::InsertDuplicate(bplustree_node */*node*/,uint16 /*index*/) +{ + printf("DUPLICATE ENTRY!!\n"); + +// /* handle dup keys */ +// if (dupflg == 0) +// { +// bt_errno(b) = BT_DUPKEY; +// goto bombout; +// } +// else +// { +// /* paste new data ptr in page */ +// /* and write it out again. */ +// off_t *p; +// p = (off_t *)KEYCLD(op->p); +// *(p + keyat) = rrn; +// op->flags = BT_CHE_DIRTY; +// if(bt_wpage(b,op) == BT_ERR || +// bt_wpage(b,kp) == BT_ERR) +// return(BT_ERR); +// +// /* mark all as well with tree */ +// bt_clearerr(b); +// return(BT_OK); +// } + + return B_OK; +} + + +status_t BPlusTree::SplitNode(bplustree_node *node,off_t nodeOffset,uint16 *_keyIndex,uint8 *key,uint16 *_keyLength,off_t *_value) +{ + if (*_keyIndex > node->all_key_count + 1) + return B_BAD_VALUE; + + //printf("before (insert \"%s\" (%d bytes) at %d):\n\n",key,*_keyLength,*_keyIndex); + //dump_bplustree_node(node,fHeader); + //hexdump(node,fNodeSize); + + off_t otherOffset; + bplustree_node *other = fCache.Get(otherOffset = fHeader->maximum_size); //Node(otherOffset = fHeader->maximum_size/*,false*/); + if (other == NULL) + return B_NO_MEMORY; + + uint16 *inKeyLengths = node->KeyLengths(); + off_t *inKeyValues = node->Values(); + uint8 *inKeys = node->Keys(); + uint8 *outKeys = other->Keys(); + uint16 keyIndex = *_keyIndex; + + // how many keys will fit in one (half) page? + + // "bytes" is the number of bytes written for the new key, + // "bytesBefore" are the bytes before that key + // "bytesAfter" are the bytes after the new key, if any + int32 bytes = 0,bytesBefore = 0,bytesAfter = 0; + + size_t size = fNodeSize >> 1; + int32 out,in; + for (in = out = 0;in < node->all_key_count + 1;) { + if (!bytes) + bytesBefore = in > 0 ? inKeyLengths[in - 1] : 0; + if (in == keyIndex && !bytes) { + bytes = *_keyLength; + } else { + if (keyIndex < out) { + bytesAfter = inKeyLengths[in] - bytesBefore; + // fix the key lengths for the new node + inKeyLengths[in] = bytesAfter + bytesBefore + bytes; + } //else + in++; + } + out++; + + if (round_up(sizeof(bplustree_node) + bytesBefore + bytesAfter + bytes) + + out * (sizeof(uint16) + sizeof(off_t)) >= size) { + // we have found the number of keys in the new node! + break; + } + } + + // if the new key was not inserted, set the length of the keys + // that can be copied directly + if (keyIndex >= out && in > 0) + bytesBefore = inKeyLengths[in - 1]; + + if (bytesBefore < 0 || bytesAfter < 0) + return B_BAD_DATA; + + //printf("put %ld keys in the other node (%ld, %ld, %ld) (in = %ld)\n",out,bytesBefore,bytes,bytesAfter,in); + + other->left_link = node->left_link; + other->right_link = nodeOffset; + other->all_key_length = bytes + bytesBefore + bytesAfter; + other->all_key_count = out; + //printf("-> used = %ld (bplustree_node = %ld bytes)\n",other->Used(),sizeof(bplustree_node)); + + uint16 *outKeyLengths = other->KeyLengths(); + off_t *outKeyValues = other->Values(); + int32 keys = out > keyIndex ? keyIndex : out; + + if (bytesBefore) { + // copy the keys + memcpy(outKeys,inKeys,bytesBefore); + memcpy(outKeyLengths,inKeyLengths,keys * sizeof(uint16)); + memcpy(outKeyValues,inKeyValues,keys * sizeof(off_t)); + } + if (bytes) { + // copy the newly inserted key + memcpy(outKeys + bytesBefore,key,bytes); + outKeyLengths[keyIndex] = bytes + bytesBefore; + outKeyValues[keyIndex] = *_value; + + if (bytesAfter) { + // copy the keys after the new key + memcpy(outKeys + bytesBefore + bytes,inKeys + bytesBefore,bytesAfter); + keys = out - keyIndex - 1; + memcpy(outKeyLengths + keyIndex + 1,inKeyLengths + keyIndex,keys * sizeof(uint16)); + memcpy(outKeyValues + keyIndex + 1,inKeyValues + keyIndex,keys * sizeof(off_t)); + } + } + + // if the new key was already inserted, we shouldn't use it again + if (in != out) + keyIndex--; + + int32 total = bytesBefore + bytesAfter; + + // if we have split an index node, we have to drop the first key + // of the next node (which can also be the new key to insert) + if (node->overflow_link != BPLUSTREE_NULL) { + if (in == keyIndex) { + other->overflow_link = *_value; + keyIndex--; + } else { + other->overflow_link = inKeyValues[in]; + total = inKeyLengths[in++]; + } + } + + // and now the same game for the other page and the rest of the keys + // (but with memmove() instead of memcpy(), because they may overlap) + + bytesBefore = bytesAfter = bytes = 0; + out = 0; + int32 skip = in; + while (in < node->all_key_count + 1) { + //printf("in = %ld, keyIndex = %d, bytes = %ld\n",in,keyIndex,bytes); + if (in == keyIndex && !bytes) { + bytesBefore = in > skip ? inKeyLengths[in - 1] : 0; + //printf("bytesBefore = %ld\n",bytesBefore); + bytes = *_keyLength; + } else if (in < node->all_key_count) { + //printf("1.inKeyLength[%ld] = %u\n",in,inKeyLengths[in]); + inKeyLengths[in] -= total; + if (bytes) { + inKeyLengths[in] += bytes; + bytesAfter = inKeyLengths[in] - bytesBefore - bytes; + //printf("2.inKeyLength[%ld] = %u, bytesAfter = %ld, bytesBefore = %ld\n",in,inKeyLengths[in],bytesAfter,bytesBefore); + } + + in++; + } else + in++; + + out++; + + //printf("-> out = %ld, keylen = %ld, %ld bytes total\n",out,bytesBefore,round_up(sizeof(bplustree_node) + bytesBefore + bytesAfter + bytes) + + // out * (sizeof(uint16) + sizeof(off_t))); + if (in > node->all_key_count && keyIndex < in) + break; + } + + //printf("bytes = (%ld, %ld, %ld), in = %ld, total = %ld\n",bytesBefore,bytes,bytesAfter,in,total); + + if (keyIndex >= in && keyIndex - skip < out) { + bytesAfter = inKeyLengths[in] - bytesBefore - total; + } else if (keyIndex < skip) + bytesBefore = node->all_key_length - total; + + //printf("bytes = (%ld, %ld, %ld), in = %ld, total = %ld\n",bytesBefore,bytes,bytesAfter,in,total); + + if (bytesBefore < 0 || bytesAfter < 0) + return B_BAD_DATA; + + node->left_link = otherOffset; + // right link, and overflow link can stay the same + node->all_key_length = bytes + bytesBefore + bytesAfter; + node->all_key_count = out - 1; + + // array positions have changed + outKeyLengths = node->KeyLengths(); + outKeyValues = node->Values(); + + //printf("put %ld keys in the first node (%ld, %ld, %ld) (in = %ld)\n",out,bytesBefore,bytes,bytesAfter,in); + + // move the keys in the old node: the order is important here, + // because we don't want to overwrite any contents + + keys = keyIndex <= skip ? out : keyIndex - skip; + keyIndex -= skip; + + if (bytesBefore) + memmove(inKeys,inKeys + total,bytesBefore); + if (bytesAfter) + memmove(inKeys + bytesBefore + bytes,inKeys + total + bytesBefore,bytesAfter); + + if (bytesBefore) + memmove(outKeyLengths,inKeyLengths + skip,keys * sizeof(uint16)); + in = out - keyIndex - 1; + if (bytesAfter) + memmove(outKeyLengths + keyIndex + 1,inKeyLengths + skip + keyIndex,in * sizeof(uint16)); + + if (bytesBefore) + memmove(outKeyValues,inKeyValues + skip,keys * sizeof(off_t)); + if (bytesAfter) + memmove(outKeyValues + keyIndex + 1,inKeyValues + skip + keyIndex,in * sizeof(off_t)); + + if (bytes) { + // finally, copy the newly inserted key (don't overwrite anything) + memcpy(inKeys + bytesBefore,key,bytes); + outKeyLengths[keyIndex] = bytes + bytesBefore; + outKeyValues[keyIndex] = *_value; + } + + //puts("\n!!!!!!!!!!!!!!! after: !!!!!!!!!!!!!!!\n"); + //dump_bplustree_node(other,fHeader); + //hexdump(other,fNodeSize); + //puts("\n"); + //dump_bplustree_node(node,fHeader); + //hexdump(node,fNodeSize); + + // write the updated nodes back + + fCache.SetDirty(otherOffset,true); + fCache.SetDirty(nodeOffset,true); + + // update the right link of the node in the left of the new node + if (other->left_link != BPLUSTREE_NULL) { + bplustree_node *left = fCache.Get(other->left_link); + if (left != NULL) { + left->right_link = otherOffset; + fCache.SetDirty(other->left_link,true); + } + } + + // prepare key to insert in the parent node + + //printf("skip: %ld, count-1: %u\n",skip,other->all_key_count - 1); + uint16 length; + uint8 *lastKey = other->KeyAt(other->all_key_count - 1,&length); + memcpy(key,lastKey,length); + *_keyLength = length; + *_value = otherOffset; + + return B_OK; +} + + +status_t BPlusTree::Insert(uint8 *key,uint16 keyLength,off_t value) +{ + if (keyLength < BPLUSTREE_MIN_KEY_LENGTH || keyLength > BPLUSTREE_MAX_KEY_LENGTH) + return B_BAD_VALUE; + + Stack stack; + if (SeekDown(stack,key,keyLength) != B_OK) + return B_ERROR; + + uint8 keyBuffer[BPLUSTREE_MAX_KEY_LENGTH + 1]; + + memcpy(keyBuffer,key,keyLength); + keyBuffer[keyLength] = 0; + + off_t valueToInsert = value; + + fCurrentNodeOffset = BPLUSTREE_NULL; + + node_and_key nodeAndKey; + bplustree_node *node; + uint32 count = 0; + + while (stack.Pop(&nodeAndKey) && (node = fCache.Get(nodeAndKey.nodeOffset)) != NULL && CheckNode(node)) + { + if (count++ == 0) // first round, check for duplicate entries + { + status_t status = FindKey(node,key,keyLength,&nodeAndKey.keyIndex); + if (status == B_ERROR) + return B_ERROR; + + // is this a duplicate entry? + if (status == B_OK && node->overflow_link == BPLUSTREE_NULL) + { + if (fAllowDuplicates) + return InsertDuplicate(node,nodeAndKey.keyIndex); + else + return B_NAME_IN_USE; + } + } + + // is the node big enough to hold the pair? + if (int32(round_up(sizeof(bplustree_node) + node->all_key_length + keyLength) + + (node->all_key_count + 1) * (sizeof(uint16) + sizeof(off_t))) < fNodeSize) + { + InsertKey(node,keyBuffer,keyLength,valueToInsert,nodeAndKey.keyIndex); + fCache.SetDirty(nodeAndKey.nodeOffset,true); + + return B_OK; + } + else + { + // do we need to allocate a new root node? if so, then do + // it now + bplustree_node *rootNode = NULL; + off_t newRoot = BPLUSTREE_NULL; + if (nodeAndKey.nodeOffset == fHeader->root_node_pointer) { + rootNode = fCache.Get(newRoot = fHeader->maximum_size); + if (rootNode == NULL) { + // the tree is most likely dead + return B_NO_MEMORY; + } + } + + if (SplitNode(node,nodeAndKey.nodeOffset,&nodeAndKey.keyIndex,keyBuffer,&keyLength,&valueToInsert) < B_OK) { + if (newRoot != BPLUSTREE_NULL) { + // free root node + } + return B_ERROR; + } + + if (newRoot != BPLUSTREE_NULL) { + rootNode->overflow_link = nodeAndKey.nodeOffset; + + InsertKey(rootNode,keyBuffer,keyLength,node->left_link,0); + + fHeader->root_node_pointer = newRoot; + fHeader->max_number_of_levels++; + // write header + + fCache.SetDirty(newRoot,true); + + return B_OK; + } + } + } + + return B_ERROR; +} + + +status_t BPlusTree::Find(uint8 *key,uint16 keyLength,off_t *value) +{ + if (keyLength < BPLUSTREE_MIN_KEY_LENGTH || keyLength > BPLUSTREE_MAX_KEY_LENGTH) + return B_BAD_VALUE; + + Stack stack; + if (SeekDown(stack,key,keyLength) != B_OK) + return B_ERROR; + + fCurrentNodeOffset = BPLUSTREE_NULL; + + node_and_key nodeAndKey; + bplustree_node *node; + + if (stack.Pop(&nodeAndKey) && (node = fCache.Get(nodeAndKey.nodeOffset)) != NULL && CheckNode(node)) + { + status_t status = FindKey(node,key,keyLength,&nodeAndKey.keyIndex); + if (status == B_ERROR) + return B_ERROR; + + SetCurrentNode(node,nodeAndKey.nodeOffset); + + if (status == B_OK && node->overflow_link == BPLUSTREE_NULL) + { + *value = node->Values()[nodeAndKey.keyIndex]; + return B_OK; + } + } + return B_ENTRY_NOT_FOUND; +} + + +// #pragma mark - + + +bool +BPlusTree::CheckNode(bplustree_node *node) +{ + if (!fHeader->IsValidLink(node->left_link) + || !fHeader->IsValidLink(node->right_link) + || !fHeader->IsValidLink(node->overflow_link) + || (int8 *)node->Values() + node->all_key_count * sizeof(off_t) > (int8 *)node + fNodeSize) + return false; + + return true; +} + + +bplustree_node *BPlusTree::Node(off_t nodeOffset,bool check) +{ +/*printf("1: %d,%d,%d\n", + nodeOffset > fHeader->maximum_size - fNodeSize, + nodeOffset < 0 && nodeOffset != BPLUSTREE_NULL, + (nodeOffset % fNodeSize) != 0); +*/ + // the super node is always in memory, and shouldn't + // never be taken out of the cache + if (nodeOffset > fHeader->maximum_size /*- fNodeSize*/ + || nodeOffset <= 0 && nodeOffset != BPLUSTREE_NULL + || (nodeOffset % fNodeSize) != 0) + return NULL; + + bplustree_node *node = (bplustree_node *)malloc(fNodeSize); + if (node == NULL) + return NULL; + + if (nodeOffset == BPLUSTREE_NULL || !fStream) + { + node->left_link = BPLUSTREE_NULL; + node->right_link = BPLUSTREE_NULL; + node->overflow_link = BPLUSTREE_NULL; + node->all_key_count = 0; + node->all_key_length = 0; + } + else if (fStream && fStream->ReadAt(nodeOffset,node,fNodeSize) < fNodeSize) + { + free(node); + return NULL; + } + + if (check && node != NULL) + { + // sanity checks (links, all_key_count) + if (!fHeader->IsValidLink(node->left_link) + || !fHeader->IsValidLink(node->right_link) + || !fHeader->IsValidLink(node->overflow_link) + || (int8 *)node->Values() + node->all_key_count * sizeof(off_t) > (int8 *)node + fNodeSize) + return NULL; + } + + if (!fStream && nodeOffset > fHeader->maximum_size - fNodeSize) { + // do some hacks here + fHeader->maximum_size += fNodeSize; + } + + return node; +} + + +void BPlusTree::SetHoldChanges(bool hold) +{ + fCache.SetHoldChanges(hold); +} + + +// #pragma mark - + + +uint8 *bplustree_node::KeyAt(int32 index,uint16 *keyLength) const +{ + if (index < 0 || index > all_key_count) + return NULL; + + uint8 *keyStart = Keys(); + uint16 *keyLengths = KeyLengths(); + + *keyLength = keyLengths[index] - (index != 0 ? keyLengths[index - 1] : 0); + if (index > 0) + keyStart += keyLengths[index - 1]; + + return keyStart; +} + + +uint8 bplustree_node::CountDuplicates(off_t offset,bool isFragment) const +{ + // the duplicate fragment handling is currently hard-coded to a node size + // of 1024 bytes - with future versions of BFS, this may be a problem + + if (isFragment) + { + uint32 fragment = 8 * ((uint64)offset & 0x3ff); + + return ((off_t *)this)[fragment]; + } + return overflow_link; +} + + +off_t bplustree_node::DuplicateAt(off_t offset,bool isFragment,int8 index) const +{ + uint32 start; + if (isFragment) + start = 8 * ((uint64)offset & 0x3ff); + else + start = 2; + + return ((off_t *)this)[start + 1 + index]; +} + diff --git a/src/bin/bfs_tools/lib/BPlusTree.h b/src/bin/bfs_tools/lib/BPlusTree.h new file mode 100644 index 0000000000..917d710efc --- /dev/null +++ b/src/bin/bfs_tools/lib/BPlusTree.h @@ -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 + +#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 Stack; +class BPlusTree; + +class NodeCache : public Cache { + 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 &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 */ diff --git a/src/bin/bfs_tools/lib/Bitmap.cpp b/src/bin/bfs_tools/lib/Bitmap.cpp new file mode 100644 index 0000000000..53567dadca --- /dev/null +++ b/src/bin/bfs_tools/lib/Bitmap.cpp @@ -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 +#include +#include + + +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(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; +} + diff --git a/src/bin/bfs_tools/lib/Bitmap.h b/src/bin/bfs_tools/lib/Bitmap.h new file mode 100644 index 0000000000..493a3abf4e --- /dev/null +++ b/src/bin/bfs_tools/lib/Bitmap.h @@ -0,0 +1,46 @@ +/* + * Copyright 2001-2008, pinc Software. All Rights Reserved. + */ +#ifndef BITMAP_H +#define BITMAP_H + + +#include + +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 */ diff --git a/src/bin/bfs_tools/lib/Cache.h b/src/bin/bfs_tools/lib/Cache.h new file mode 100644 index 0000000000..98c2b0e04c --- /dev/null +++ b/src/bin/bfs_tools/lib/Cache.h @@ -0,0 +1,192 @@ +#ifndef CACHE_H +#define CACHE_H +/* Cache - a template cache class +** +** Copyright 2001 pinc Software. All Rights Reserved. +*/ + + +#include + + +template 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 */ diff --git a/src/bin/bfs_tools/lib/Disk.cpp b/src/bin/bfs_tools/lib/Disk.cpp new file mode 100644 index 0000000000..d03d83ddac --- /dev/null +++ b/src/bin/bfs_tools/lib/Disk.cpp @@ -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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +#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(), + fDisk(disk) +{ +} + + +Cache::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; +} + diff --git a/src/bin/bfs_tools/lib/Disk.h b/src/bin/bfs_tools/lib/Disk.h new file mode 100644 index 0000000000..774d44fbfe --- /dev/null +++ b/src/bin/bfs_tools/lib/Disk.h @@ -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 +#include + +#include "Bitmap.h" +#include "bfs.h" +#include "Cache.h" + + +class Disk; + +class BlockRunCache : public Cache +{ + 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 */ diff --git a/src/bin/bfs_tools/lib/Hashtable.cpp b/src/bin/bfs_tools/lib/Hashtable.cpp new file mode 100644 index 0000000000..1cdab31f85 --- /dev/null +++ b/src/bin/bfs_tools/lib/Hashtable.cpp @@ -0,0 +1,284 @@ +/* Hashtable - a general purpose hash table +** +** Copyright 2001 pinc Software. All Rights Reserved. +*/ + + +#include +#include +#include + +#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; +} + diff --git a/src/bin/bfs_tools/lib/Hashtable.h b/src/bin/bfs_tools/lib/Hashtable.h new file mode 100644 index 0000000000..30c0b61795 --- /dev/null +++ b/src/bin/bfs_tools/lib/Hashtable.h @@ -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 diff --git a/src/bin/bfs_tools/lib/Inode.cpp b/src/bin/bfs_tools/lib/Inode.cpp new file mode 100644 index 0000000000..7b23916a31 --- /dev/null +++ b/src/bin/bfs_tools/lib/Inode.cpp @@ -0,0 +1,1395 @@ +/* + * Copyright 2001-2008 pinc Software. All Rights Reserved. + */ + +//! BFS Inode classes + + +#include "Inode.h" +#include "BPlusTree.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +class NodeGetter { + public: + NodeGetter(Inode* inode) + : + fInode(inode) + { + fInode->AcquireBuffer(); + } + + ~NodeGetter() + { + fInode->ReleaseBuffer(); + } + + private: + Inode* fInode; +}; + + +// #pragma mark - + + +Inode::Inode(Disk* disk, bfs_inode* inode, bool ownBuffer) + : + fDisk(disk), + fInode(inode), + fOwnBuffer(ownBuffer), + fPath(NULL), + fRefCount(1), + fCurrentSmallData(NULL), + fAttributes(NULL), + fAttributeBuffer(NULL) +{ + if (inode != NULL) + fBlockRun = inode->inode_num; +} + + +Inode::Inode(const Inode& inode) + : + fDisk(inode.fDisk), + fInode(inode.fInode), + fOwnBuffer(false), + fPath(NULL), + fBlockRun(inode.fBlockRun), + fRefCount(1), + fCurrentSmallData(NULL), + fAttributes(NULL), + fAttributeBuffer(NULL) +{ +} + + +Inode::~Inode() +{ + _Unset(); +} + + +void +Inode::_Unset() +{ + if (fOwnBuffer) + free(fInode); + + fInode = NULL; + fBlockRun.SetTo(0, 0, 0); + + free(fPath); + fPath = NULL; + + delete fAttributes; + fAttributes = NULL; +} + + +status_t +Inode::SetTo(bfs_inode *inode) +{ + _Unset(); + + fInode = inode; + fBlockRun = inode->inode_num; + return B_OK; +} + + +status_t +Inode::InitCheck() +{ + if (!fInode) + return B_ERROR; + + // test inode magic and flags + if (fInode->magic1 != INODE_MAGIC1 + || !(fInode->flags & INODE_IN_USE) + || fInode->inode_num.length != 1) + return B_ERROR; + + if (fDisk->BlockSize()) { + // matches known block size? + if (fInode->inode_size != fDisk->SuperBlock()->inode_size + // parent resides on disk? + || fInode->parent.allocation_group > fDisk->SuperBlock()->num_ags + || fInode->parent.allocation_group < 0 + || fInode->parent.start > (1L << fDisk->SuperBlock()->ag_shift) + || fInode->parent.length != 1 + // attributes, too? + || fInode->attributes.allocation_group > fDisk->SuperBlock()->num_ags + || fInode->attributes.allocation_group < 0 + || fInode->attributes.start > (1L << fDisk->SuperBlock()->ag_shift)) + return B_ERROR; + } else { + // is inode size one of the valid values? + switch (fInode->inode_size) { + case 1024: + case 2048: + case 4096: + case 8192: + break; + default: + return B_ERROR; + } + } + return B_OK; + // is inode on a boundary matching it's size? + //return (Offset() % fInode->inode_size) == 0 ? B_OK : B_ERROR; +} + + +status_t +Inode::CopyBuffer() +{ + if (!fInode) + return B_ERROR; + + bfs_inode *buffer = (bfs_inode *)malloc(fInode->inode_size); + if (!buffer) + return B_NO_MEMORY; + + memcpy(buffer, fInode, fInode->inode_size); + fInode = buffer; + fOwnBuffer = true; + BufferClobbered(); + // this must not be deleted anymore + + return B_OK; +} + + +/*static*/ bool +Inode::_LowMemory() +{ + static bigtime_t lastChecked; + static int32 percentUsed; + + if (system_time() > lastChecked + 1000000LL) { + system_info info; + get_system_info(&info); + percentUsed = 100 * info.used_pages / info.max_pages; + } + + return percentUsed > 75; +} + + +void +Inode::ReleaseBuffer() +{ + if (atomic_add(&fRefCount, -1) != 1) + return; + + if (fOwnBuffer) { + if (!_LowMemory()) + return; + + free(fInode); + fInode = NULL; + } +} + + +status_t +Inode::AcquireBuffer() +{ + if (atomic_add(&fRefCount, 1) != 0) + return B_OK; + + if (!fOwnBuffer || fInode != NULL) + return B_OK; + + fInode = (bfs_inode*)malloc(fDisk->BlockSize()); + if (fInode == NULL) + return B_NO_MEMORY; + + ssize_t bytesRead = fDisk->ReadAt(Offset(), fInode, fDisk->BlockSize()); + if (bytesRead < B_OK) + return bytesRead; + + return B_OK; +} + + +void +Inode::BufferClobbered() +{ + AcquireBuffer(); +} + + +void +Inode::SetParent(const block_run& run) +{ + fInode->parent = run; + BufferClobbered(); +} + + +void +Inode::SetBlockRun(const block_run& run) +{ + fInode->inode_num = run; + fBlockRun = run; + BufferClobbered(); +} + + +void +Inode::SetMode(uint32 mode) +{ + fInode->mode = mode; + BufferClobbered(); +} + + +status_t +Inode::SetName(const char *name) +{ + if (name == NULL || *name == '\0') + return B_BAD_VALUE; + + small_data *data = fInode->small_data_start, *nameData = NULL; + BufferClobbered(); + + while (!data->IsLast(fInode)) { + if (data->type == FILE_NAME_TYPE + && data->name_size == FILE_NAME_NAME_LENGTH + && *data->Name() == FILE_NAME_NAME) + nameData = data; + + data = data->Next(); + } + + int32 oldLength = nameData == NULL ? 0 : nameData->data_size; + int32 newLength = strlen(name) + (nameData == NULL ? sizeof(small_data) + 5 : 0); + + if (int32(data) + newLength - oldLength >= (int32)(fInode + fDisk->BlockSize())) + return B_NO_MEMORY; + + if (nameData == NULL) { + memmove(newLength + (uint8 *)fInode->small_data_start, + fInode->small_data_start, + int32(data) - int32(fInode->small_data_start)); + nameData = fInode->small_data_start; + } else { + memmove(newLength + (uint8 *)nameData, nameData, + int32(data) - int32(fInode->small_data_start)); + } + + memset(nameData, 0, sizeof(small_data) + 5 + strlen(name)); + nameData->type = FILE_NAME_TYPE; + nameData->name_size = FILE_NAME_NAME_LENGTH; + nameData->data_size = strlen(name); + *nameData->Name() = FILE_NAME_NAME; + strcpy((char *)nameData->Data(),name); + + return B_OK; +} + + +const char * +Inode::Name() const +{ + small_data *data = fInode->small_data_start; + while (!data->IsLast(fInode)) { + if (data->type == FILE_NAME_TYPE + && data->name_size == FILE_NAME_NAME_LENGTH + && *data->Name() == FILE_NAME_NAME) + return (const char *)data->Data(); + + data = data->Next(); + } + return NULL; +} + + +status_t +Inode::GetNextSmallData(small_data **smallData) +{ + if (!fInode) + return B_ERROR; + + small_data *data = *smallData; + + // begin from the start? + if (data == NULL) + data = fInode->small_data_start; + else + data = data->Next(); + + // is already last item? + if (data->IsLast(fInode)) + return B_ENTRY_NOT_FOUND; + + *smallData = data; + return B_OK; +} + + +status_t +Inode::RewindAttributes() +{ + fCurrentSmallData = NULL; + + if (fAttributes != NULL) + fAttributes->Rewind(); + + return B_OK; +} + + +status_t +Inode::GetNextAttribute(char *name, uint32 *type, void **data, size_t *length) +{ + // read attributes out of the small data section + + if (fCurrentSmallData == NULL || !fCurrentSmallData->IsLast(fInode)) { + if (fCurrentSmallData == NULL) + fCurrentSmallData = fInode->small_data_start; + else + fCurrentSmallData = fCurrentSmallData->Next(); + + // skip name attribute + if (!fCurrentSmallData->IsLast(fInode) + && fCurrentSmallData->name_size == FILE_NAME_NAME_LENGTH + && *fCurrentSmallData->Name() == FILE_NAME_NAME) + fCurrentSmallData = fCurrentSmallData->Next(); + + if (!fCurrentSmallData->IsLast(fInode)) { + strncpy(name,fCurrentSmallData->Name(), B_FILE_NAME_LENGTH); + *type = fCurrentSmallData->type; + *data = fCurrentSmallData->Data(); + *length = fCurrentSmallData->data_size; + + return B_OK; + } + } + + // read attributes out of the attribute directory + + if (Attributes().IsZero()) + return B_ENTRY_NOT_FOUND; + + if (fAttributes == NULL) + fAttributes = (Directory *)Inode::Factory(fDisk, Attributes()); + + status_t status = fAttributes ? fAttributes->InitCheck() : B_ERROR; + if (status < B_OK) + return status; + + block_run run; + status = fAttributes->GetNextEntry(name, &run); + if (status < B_OK) { + free(fAttributeBuffer); + fAttributeBuffer = NULL; + return status; + } + + Attribute *attribute = (Attribute *)Inode::Factory(fDisk, run); + if (attribute == NULL || attribute->InitCheck() < B_OK) + return B_IO_ERROR; + + *type = attribute->Type(); + + void *buffer = realloc(fAttributeBuffer, attribute->Size()); + if (buffer == NULL) { + free(fAttributeBuffer); + fAttributeBuffer = NULL; + delete attribute; + return B_NO_MEMORY; + } + fAttributeBuffer = buffer; + + ssize_t size = attribute->Read(fAttributeBuffer, attribute->Size()); + delete attribute; + + *length = size; + *data = fAttributeBuffer; + + return size < B_OK ? size : B_OK; +} + + +status_t +Inode::_FindPath(Inode::Source *source) +{ + BString path; + + block_run parent = Parent(); + while (!parent.IsZero() && parent != fDisk->Root()) { + Inode *inode; + if (source) + inode = source->InodeAt(parent); + else + inode = Inode::Factory(fDisk, parent); + + if (inode == NULL + || inode->InitCheck() < B_OK + || inode->Name() == NULL + || !*inode->Name()) { + BString sub; + sub << "__recovered " << parent.allocation_group << ":" + << (int32)parent.start << "/"; + path.Prepend(sub); + + delete inode; + break; + } + parent = inode->Parent(); + path.Prepend("/"); + path.Prepend(inode->Name()); + + delete inode; + } + fPath = strdup(path.String()); + + return B_OK; +} + + +const char * +Inode::Path(Inode::Source *source) +{ + if (fPath == NULL) + _FindPath(source); + + return fPath; +} + + +status_t +Inode::CopyTo(const char *root, bool fullPath, Inode::Source *source) +{ + if (root == NULL) + return B_ENTRY_NOT_FOUND; + + BString path; + + if (fullPath) + path.Append(Path(source)); + + if (*(root + strlen(root) - 1) != '/') + path.Prepend("/"); + path.Prepend(root); + + return create_directory(path.String(), 0777); +} + + +status_t +Inode::CopyAttributesTo(BNode *node) +{ + // copy attributes + + RewindAttributes(); + + char name[B_FILE_NAME_LENGTH]; + const uint32 kMaxBrokenAttributes = 64; + // sanity max value + uint32 count = 0; + uint32 type; + void *data; + size_t size; + + status_t status; + while ((status = GetNextAttribute(name, &type, &data, &size)) + != B_ENTRY_NOT_FOUND) { + if (status != B_OK) { + printf("could not open attribute (possibly: %s): %s!\n", + name, strerror(status)); + if (count++ > kMaxBrokenAttributes) + break; + + continue; + } + + ssize_t written = node->WriteAttr(name, type, 0, data, size); + if (written < B_OK) { + printf("could not write attribute \"%s\": %s\n", name, + strerror(written)); + } else if ((size_t)written < size) { + printf("could only write %ld bytes (from %ld) at attribute \"%s\"\n", + written, size, name); + } + } + + // copy stats + + node->SetPermissions(fInode->mode); + node->SetOwner(fInode->uid); + node->SetGroup(fInode->gid); + node->SetModificationTime(fInode->last_modified_time >> 16); + node->SetCreationTime(fInode->create_time >> 16); + + return B_OK; +} + + +Inode * +Inode::Factory(Disk *disk, bfs_inode *inode, bool ownBuffer) +{ + // attributes (of a file) + if ((inode->mode & (S_ATTR | S_ATTR_DIR)) == S_ATTR) + return new Attribute(disk, inode, ownBuffer); + + // directories, attribute directories, indices + if (S_ISDIR(inode->mode) || inode->mode & S_ATTR_DIR) + return new Directory(disk, inode, ownBuffer); + + // regular files + if (S_ISREG(inode->mode)) + return new File(disk, inode, ownBuffer); + + // symlinks (short and link in data-stream) + if (S_ISLNK(inode->mode)) + return new Symlink(disk, inode, ownBuffer); + + return NULL; +} + + +Inode * +Inode::Factory(Disk *disk, block_run run) +{ + bfs_inode *inode = (bfs_inode *)malloc(disk->BlockSize()); + if (!inode) + return NULL; + + if (disk->ReadAt(disk->ToOffset(run), inode, disk->BlockSize()) <= 0) + return NULL; + + Inode *object = Factory(disk, inode); + if (object == NULL) + free(inode); + + return object; +} + + +Inode * +Inode::Factory(Disk *disk, Inode *inode, bool copyBuffer) +{ + bfs_inode *inodeBuffer = inode->fInode; + + if (copyBuffer) { + bfs_inode *inodeCopy = (bfs_inode *)malloc(inodeBuffer->inode_size); + if (!inodeCopy) + return NULL; + + memcpy(inodeCopy, inodeBuffer, inodeBuffer->inode_size); + inodeBuffer = inodeCopy; + } + return Factory(disk, inodeBuffer, copyBuffer); +} + + +Inode * +Inode::EmptyInode(Disk *disk, const char *name, int32 mode) +{ + bfs_inode *inode = (bfs_inode *)malloc(disk->BlockSize()); + if (!inode) + return NULL; + + memset(inode, 0, sizeof(bfs_inode)); + + inode->magic1 = INODE_MAGIC1; + inode->inode_size = disk->BlockSize(); + inode->mode = mode; + inode->flags = INODE_IN_USE | (mode & S_IFDIR ? INODE_LOGGED : 0); + + if (name) { + small_data *data = inode->small_data_start; + data->type = FILE_NAME_TYPE; + data->name_size = FILE_NAME_NAME_LENGTH; + *data->Name() = FILE_NAME_NAME; + data->data_size = strlen(name); + strcpy((char *)data->Data(), name); + } + + Inode *object = new (std::nothrow) Inode(disk, inode); + if (object == NULL) + free(inode); + + object->AcquireBuffer(); + // this must not be deleted anymore! + return object; +} + + +// #pragma mark - + + +DataStream::DataStream(Disk *disk, bfs_inode *inode, bool ownBuffer) + : Inode(disk,inode,ownBuffer), + fCurrent(-1), + fPosition(0LL) +{ +} + + +DataStream::DataStream(const Inode &inode) + : Inode(inode), + fCurrent(-1), + fPosition(0LL) +{ +} + + +DataStream::~DataStream() +{ +} + + +status_t +DataStream::FindBlockRun(off_t pos) +{ + NodeGetter _(this); + + if (pos > fInode->data.size) + return B_ENTRY_NOT_FOUND; + + if (fCurrent < 0) + fLevel = 0; + + fRunBlockEnd = fCurrent >= 0 + ? fRunFileOffset + (fRun.length << fDisk->BlockShift()) : 0LL; + + // access in current block run? + + if (fCurrent >= 0 && pos >= fRunFileOffset && pos < fRunBlockEnd) + return B_OK; + + // find matching block run + + if (fInode->data.max_direct_range > 0 + && pos >= fInode->data.max_direct_range) { + if (fInode->data.max_double_indirect_range > 0 + && pos >= fInode->data.max_indirect_range) { + // read from double indirect blocks + + //printf("find double indirect block: %ld,%d!\n",fInode->data.double_indirect.allocation_group,fInode->data.double_indirect.start); + block_run *indirect = (block_run *)fDisk->ReadBlockRun(fInode->data.double_indirect); + if (indirect == NULL) + return B_ERROR; + + off_t start = pos - fInode->data.max_indirect_range; + int32 indirectSize = fDisk->BlockSize() * 16 * (fDisk->BlockSize() / sizeof(block_run)); + int32 directSize = fDisk->BlockSize() * 4; + int32 index = start / indirectSize; + + //printf("\tstart = %Ld, indirectSize = %ld, directSize = %ld, index = %ld\n",start,indirectSize,directSize,index); + //printf("\tlook for indirect block at %ld,%d\n",indirect[index].allocation_group,indirect[index].start); + indirect = (block_run *)fDisk->ReadBlockRun(indirect[index]); + if (indirect == NULL) + return B_ERROR; + + fCurrent = (start % indirectSize) / directSize; + fRunFileOffset = fInode->data.max_indirect_range + (index * indirectSize) + (fCurrent * directSize); + fRunBlockEnd = fRunFileOffset + directSize; + fRun = indirect[fCurrent]; + //printf("\tfCurrent = %ld, fRunFileOffset = %Ld, fRunBlockEnd = %Ld, fRun = %ld,%d\n",fCurrent,fRunFileOffset,fRunBlockEnd,fRun.allocation_group,fRun.start); + } else { + // access from indirect blocks + + block_run *indirect = (block_run *)fDisk->ReadBlockRun(fInode->data.indirect); + if (!indirect) + return B_ERROR; + + int32 indirectRuns = (fInode->data.indirect.length << fDisk->BlockShift()) / sizeof(block_run); + + if (fLevel != 1 || pos < fRunFileOffset) { + fRunBlockEnd = fInode->data.max_direct_range; + fCurrent = -1; + fLevel = 1; + } + + while (++fCurrent < indirectRuns) { + if (indirect[fCurrent].IsZero()) + break; + + fRunFileOffset = fRunBlockEnd; + fRunBlockEnd += indirect[fCurrent].length << fDisk->BlockShift(); + if (fRunBlockEnd > pos) + break; + } + if (fCurrent == indirectRuns || indirect[fCurrent].IsZero()) + return B_ERROR; + + fRun = indirect[fCurrent]; + //printf("reading from indirect block: %ld,%d\n",fRun.allocation_group,fRun.start); + //printf("### indirect-run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.length,fRunFileOffset); + } + } else { + // access from direct blocks + if (fRunFileOffset > pos) { + fRunBlockEnd = 0LL; + fCurrent = -1; + } + fLevel = 0; + + while (++fCurrent < NUM_DIRECT_BLOCKS) { + if (fInode->data.direct[fCurrent].IsZero()) + break; + + fRunFileOffset = fRunBlockEnd; + fRunBlockEnd += fInode->data.direct[fCurrent].length << fDisk->BlockShift(); + if (fRunBlockEnd > pos) + break; + } + if (fCurrent == NUM_DIRECT_BLOCKS || fInode->data.direct[fCurrent].IsZero()) + return B_ERROR; + + fRun = fInode->data.direct[fCurrent]; + //printf("### run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.length,fRunFileOffset); + } + return B_OK; +} + + +ssize_t +DataStream::ReadAt(off_t pos, void *buffer, size_t size) +{ + NodeGetter _(this); + + //printf("DataStream::ReadAt(pos = %Ld,buffer = %p,size = %ld);\n",pos,buffer,size); + // truncate size to read + if (pos + size > fInode->data.size) { + if (pos > fInode->data.size) // reading outside the file + return B_ERROR; + + size = fInode->data.size - pos; + if (!size) // there is nothing left to read + return 0; + } + ssize_t read = 0; + + //printf("### read %ld bytes at %Ld\n",size,pos); + while (size > 0) { + status_t status = FindBlockRun(pos); + if (status < B_OK) + return status; + + ssize_t bytes = min_c(size, fRunBlockEnd - pos); + + //printf("### read %ld bytes from %Ld\n### --\n",bytes,fDisk->ToOffset(fRun) + pos - fRunFileOffset); + bytes = fDisk->ReadAt(fDisk->ToOffset(fRun) + pos - fRunFileOffset, + buffer, bytes); + if (bytes <= 0) { + if (bytes == 0) { + printf("could not read bytes at: %ld,%d\n", + fRun.allocation_group, fRun.start); + } + return bytes < 0 ? bytes : B_BAD_DATA; + } + + buffer = (void *)((uint8 *)buffer + bytes); + size -= bytes; + pos += bytes; + read += bytes; + } + if (read >= 0) + return read; + + return B_IO_ERROR; +} + + +ssize_t +DataStream::WriteAt(off_t pos, const void *buffer, size_t size) +{ + NodeGetter _(this); + + // FIXME: truncate size -> should enlargen the file + if (pos + size > fInode->data.size) { + if (pos > fInode->data.size) // writing outside the file + return B_ERROR; + + size = fInode->data.size - pos; + if (!size) // there is nothing left to write + return 0; + } + ssize_t written = 0; + + //printf("### write %ld bytes at %Ld\n",size,pos); + while (size > 0) { + status_t status = FindBlockRun(pos); + if (status < B_OK) + return status; + + ssize_t bytes = min_c(size,fRunBlockEnd - pos); + + //printf("### write %ld bytes to %Ld\n### --\n",bytes,fDisk->ToOffset(fRun) + pos - fRunFileOffset); + bytes = fDisk->WriteAt(fDisk->ToOffset(fRun) + pos - fRunFileOffset,buffer,bytes); + if (bytes < 0) + return bytes; + + buffer = (void *)((uint8 *)buffer + bytes); + size -= bytes; + pos += bytes; + written += bytes; + } + if (written >= 0) + return written; + + return B_IO_ERROR; +} + + +off_t +DataStream::Seek(off_t position, uint32 seekMode) +{ + NodeGetter _(this); + + if (seekMode == SEEK_SET) + fPosition = position; + else if (seekMode == SEEK_END) + fPosition = fInode->data.size + position; + else + fPosition += position; + + return fPosition; +} + + +off_t +DataStream::Position() const +{ + return fPosition; +} + + +status_t +DataStream::SetSize(off_t size) +{ + NodeGetter _(this); + + // FIXME: not yet supported + if (size > fInode->data.size || size > fInode->data.max_direct_range) + return B_ERROR; + + if (size == fInode->data.size) + return B_OK; + + BufferClobbered(); + + fInode->data.size = size; + fInode->data.max_direct_range = size; + fInode->data.max_indirect_range = 0; + fInode->data.max_double_indirect_range = 0; + + for (int32 i = 0;i < NUM_DIRECT_BLOCKS;i++) { + if (size <= 0) + fInode->data.direct[i].SetTo(0, 0, 0); + else if ((fInode->data.direct[i].length << fDisk->BlockShift()) >= size) { + off_t blocks = (size + fDisk->BlockSize() - 1) / fDisk->BlockSize(); + fInode->data.direct[i].length = blocks; + size = 0; + } else + size -= fInode->data.direct[i].length << fDisk->BlockShift(); + } + + return B_OK; +} + + +// #pragma mark - + + +File::File(Disk *disk, bfs_inode *inode,bool ownBuffer) + : DataStream(disk,inode,ownBuffer) +{ +} + + +File::File(const Inode &inode) + : DataStream(inode) +{ +} + + +File::~File() +{ +} + + +status_t +File::InitCheck() +{ + status_t status = DataStream::InitCheck(); + if (status == B_OK) + return IsFile() ? B_OK : B_ERROR; + + return status; +} + + +status_t +File::CopyTo(const char *root, bool fullPath, Inode::Source *source) +{ + status_t status = Inode::CopyTo(root, fullPath, source); + if (status < B_OK) + return status; + + BPath path(root); + if (fullPath && Path(source)) + path.Append(Path(source)); + + char *name = (char *)Name(); + if (name != NULL) { + // changes the filename in the inode buffer (for deleted entries) + if (!*name) + *name = '_'; + path.Append(name); + } else { + BString sub; + sub << "__untitled " << BlockRun().allocation_group << ":" + << (int32)BlockRun().start; + path.Append(sub.String()); + } + printf("%ld,%d -> %s\n", BlockRun().allocation_group, BlockRun().start, + path.Path()); + + BFile file; + status = file.SetTo(path.Path(), + B_WRITE_ONLY | B_CREATE_FILE | B_FAIL_IF_EXISTS); + if (status < B_OK) + return status; + + char buffer[fDisk->BlockSize()]; + ssize_t size; + Seek(0, SEEK_SET); + + while ((size = Read(buffer, sizeof(buffer))) > B_OK) { + ssize_t written = file.Write(buffer, size); + if (written < B_OK) + return written; + } + + return CopyAttributesTo(&file); +} + + +// #pragma mark - + + +Attribute::Attribute(Disk *disk, bfs_inode *inode, bool ownBuffer) + : File(disk, inode, ownBuffer) +{ +} + + +Attribute::Attribute(const Inode &inode) + : File(inode) +{ +} + + +Attribute::~Attribute() +{ +} + + +status_t +Attribute::InitCheck() +{ + status_t status = DataStream::InitCheck(); + if (status == B_OK) + return IsAttribute() ? B_OK : B_ERROR; + + return status; +} + + +status_t +Attribute::CopyTo(const char */*path*/, bool /*fullPath*/, + Inode::Source */*source*/) +{ + // files and directories already copy all attributes + + // eventually, this method should be implemented to recover lost + // attributes on the disk + + return B_OK; +} + + +// #pragma mark - + + +Directory::Directory(Disk *disk, bfs_inode *inode, bool ownBuffer) + : DataStream(disk, inode, ownBuffer), + fTree(NULL) +{ +} + + +Directory::Directory(const Inode &inode) + : DataStream(inode), + fTree(NULL) +{ +} + + +Directory::~Directory() +{ + delete fTree; +} + + +status_t +Directory::InitCheck() +{ + status_t status = DataStream::InitCheck(); + if (status == B_OK) + return (IsDirectory() || IsAttributeDirectory()) ? B_OK : B_ERROR; + + return status; +} + + +status_t +Directory::CopyTo(const char *root, bool fullPath, Inode::Source *source) +{ + // don't copy attributes or indices + // the recovery program should make empty files to recover lost attributes + if (IsAttributeDirectory() || IsIndex()) + return B_OK; + + status_t status = Inode::CopyTo(root, fullPath, source); + if (status < B_OK) + return status; + + BPath path(root); + if (fullPath && Path(source)) + path.Append(Path(source)); + + char *name = (char *)Name(); + if (name != NULL) { + // changes the filename in the inode buffer (for deleted entries) + if (!*name) + *name = '_'; + path.Append(name); + } else { + // create unique name + BString sub; + sub << "__untitled " << BlockRun().allocation_group << ":" + << (int32)BlockRun().start; + path.Append(sub.String()); + } + + BEntry entry(path.Path()); + BDirectory directory; + if ((status = entry.GetParent(&directory)) < B_OK) + return status; + + status = directory.CreateDirectory(path.Leaf(), NULL); + if (status < B_OK && status != B_FILE_EXISTS) + return status; + + if ((status = directory.SetTo(&entry)) < B_OK) + return status; + + return CopyAttributesTo(&directory); +} + + +status_t +Directory::Rewind() +{ + if (!fTree) { + status_t status = CreateTree(); + if (status < B_OK) + return status; + } + return fTree->Rewind(); +} + + +status_t +Directory::GetNextEntry(char *name, block_run *run) +{ + status_t status; + + if (!fTree) { + if ((status = Rewind()) < B_OK) + return status; + } + uint16 length; + off_t offset; + + if ((status = fTree->GetNextEntry(name, &length, B_FILE_NAME_LENGTH - 1, + &offset)) < B_OK) + return status; + + *run = fDisk->ToBlockRun(offset); + + return B_OK; +} + + +status_t +Directory::GetNextEntry(block_run *run) +{ + char name[B_FILE_NAME_LENGTH]; + + return GetNextEntry(name, run); +} + + +status_t +Directory::Contains(const block_run *run) +{ + status_t status; + + if (!fTree) { + if ((status = Rewind()) < B_OK) + return status; + } + + block_run searchRun; + while (GetNextEntry(&searchRun) == B_OK) { + if (searchRun == *run) + return B_OK; + } + + return B_ENTRY_NOT_FOUND; +} + + +status_t +Directory::Contains(const Inode *inode) +{ + status_t status; + + if (!fTree) { + if ((status = CreateTree()) < B_OK) + return status; + } + + off_t value; + const char *name = inode->Name(); + status = B_ENTRY_NOT_FOUND; + + if (name && (status = fTree->Find((uint8 *)name, (uint16)strlen(name), + &value)) == B_OK) { + if (fDisk->ToBlockRun(value) == inode->InodeBuffer()->inode_num) + return B_OK; + + printf("inode address do not match (%s)!\n", inode->Name()); + } + + if (status != B_OK && status != B_ENTRY_NOT_FOUND) + return status; + + return Contains(&inode->InodeBuffer()->inode_num); +} + + +status_t +Directory::FindEntry(const char *name, block_run *run) +{ + status_t status; + + if (!name) + return B_BAD_VALUE; + + if (!fTree) { + if ((status = CreateTree()) < B_OK) + return status; + } + + off_t value; + + if ((status = fTree->Find((uint8 *)name, (uint16)strlen(name), + &value)) >= B_OK) { + if (run) + *run = fDisk->ToBlockRun(value); + return B_OK; + } + return status; +} + + +status_t +Directory::AddEntry(Inode *inode) +{ + status_t status; + bool created = false; + + if (!fTree) { + status = CreateTree(); + if (status == B_OK) + status = fTree->Validate(); + + if (status == B_BAD_DATA) { + //puts("bplustree corrupted!"); + fTree = new BPlusTree(BPLUSTREE_STRING_TYPE, BPLUSTREE_NODE_SIZE, + false); + if ((status = fTree->InitCheck()) < B_OK) { + delete fTree; + fTree = NULL; + } else + created = true; + } + + if (status < B_OK) + return status; + } + // keep all changes in memory + fTree->SetHoldChanges(true); + + if (created) { + // add . and .. + fTree->Insert(".", Block()); + fTree->Insert("..", fDisk->ToBlock(Parent())); + } + + if (inode->Flags() & INODE_DELETED) + return B_ENTRY_NOT_FOUND; + + BString name = inode->Name(); + if (name == "") { + name << "__file " << inode->BlockRun().allocation_group << ":" + << (int32)inode->BlockRun().start; + } + + return fTree->Insert(name.String(), inode->Block()); +} + + +status_t +Directory::CreateTree() +{ + fTree = new BPlusTree(this); + + status_t status = fTree->InitCheck(); + if (status < B_OK) { + delete fTree; + fTree = NULL; + return status; + } + return B_OK; +} + + +status_t +Directory::GetTree(BPlusTree **tree) +{ + if (!fTree) { + status_t status = CreateTree(); + if (status < B_OK) + return status; + } + *tree = fTree; + return B_OK; +} + + +// #pragma mark - + + +Symlink::Symlink(Disk *disk, bfs_inode *inode,bool ownBuffer) + : Inode(disk,inode,ownBuffer) +{ +} + + +Symlink::Symlink(const Inode &inode) + : Inode(inode) +{ +} + + +Symlink::~Symlink() +{ +} + + +status_t +Symlink::InitCheck() +{ + status_t status = Inode::InitCheck(); + if (status == B_OK) + return IsSymlink() ? B_OK : B_ERROR; + + return status; +} + + +status_t +Symlink::CopyTo(const char *root, bool fullPath,Inode::Source *source) +{ + status_t status = Inode::CopyTo(root,fullPath,source); + if (status < B_OK) + return status; + + BPath path(root); + if (fullPath && Path(source)) + path.Append(Path(source)); + + char *name = (char *)Name(); + if (name != NULL) { + // changes the filename in the inode buffer (for deleted entries) + if (!*name) + *name = '_'; + path.Append(name); + } else { + // create unique name + BString sub; + sub << "__symlink " << BlockRun().allocation_group << ":" + << (int32)BlockRun().start; + path.Append(sub.String()); + } + + BEntry entry(path.Path()); + BDirectory directory; + if ((status = entry.GetParent(&directory)) < B_OK) + return status; + + char to[2048]; + if (LinksTo(to,sizeof(to)) < B_OK) + return B_ERROR; + + BSymLink link; + status = directory.CreateSymLink(path.Leaf(),to,&link); + if (status < B_OK && status != B_FILE_EXISTS) + return status; + + if ((status = link.SetTo(&entry)) < B_OK) + return status; + + return CopyAttributesTo(&link); +} + + +status_t +Symlink::LinksTo(char *to,size_t maxLength) +{ + if ((fInode->flags & INODE_LONG_SYMLINK) == 0) { + strcpy(to,fInode->short_symlink); + return B_OK; + } + + DataStream stream(*this); + status_t status = stream.InitCheck(); + if (status < B_OK) + return status; + + status = stream.Read(to,maxLength); + + return status < B_OK ? status : B_OK; +} + diff --git a/src/bin/bfs_tools/lib/Inode.h b/src/bin/bfs_tools/lib/Inode.h new file mode 100644 index 0000000000..3a24a400b4 --- /dev/null +++ b/src/bin/bfs_tools/lib/Inode.h @@ -0,0 +1,199 @@ +/* + * Copyright 2001-2008, pinc Software. All Rights Reserved. + */ +#ifndef INODE_H +#define INODE_H + + +#include + +#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 */ diff --git a/src/bin/bfs_tools/lib/Jamfile b/src/bin/bfs_tools/lib/Jamfile new file mode 100644 index 0000000000..d9b8de5bb8 --- /dev/null +++ b/src/bin/bfs_tools/lib/Jamfile @@ -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 +; diff --git a/src/bin/bfs_tools/lib/Stack.h b/src/bin/bfs_tools/lib/Stack.h new file mode 100644 index 0000000000..d42908f56b --- /dev/null +++ b/src/bin/bfs_tools/lib/Stack.h @@ -0,0 +1,57 @@ +#ifndef STACK_H +#define STACK_H +/* Stack - a template stack class +** +** Copyright 2001 pinc Software. All Rights Reserved. +*/ + + +#include + + +template 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 */ diff --git a/src/bin/bfs_tools/lib/bfs.cpp b/src/bin/bfs_tools/lib/bfs.cpp new file mode 100644 index 0000000000..45dd96d8f4 --- /dev/null +++ b/src/bin/bfs_tools/lib/bfs.cpp @@ -0,0 +1,51 @@ +/* bfs - BFS definitions and helper functions +** +** Copyright 2001, pinc Software. All Rights Reserved. +*/ + + +#include + +#include + +#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 */ + + diff --git a/src/bin/bfs_tools/lib/bfs.h b/src/bin/bfs_tools/lib/bfs.h new file mode 100644 index 0000000000..f2ac852b4b --- /dev/null +++ b/src/bin/bfs_tools/lib/bfs.h @@ -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 + +#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 */ diff --git a/src/bin/bfs_tools/lib/dump.cpp b/src/bin/bfs_tools/lib/dump.cpp new file mode 100644 index 0000000000..651eeda5fa --- /dev/null +++ b/src/bin/bfs_tools/lib/dump.cpp @@ -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 +#include + +#include +#include + +#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"); + } +} + diff --git a/src/bin/bfs_tools/lib/dump.h b/src/bin/bfs_tools/lib/dump.h new file mode 100644 index 0000000000..4942b20f2b --- /dev/null +++ b/src/bin/bfs_tools/lib/dump.h @@ -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 */ diff --git a/src/bin/bfs_tools/lib/makefile b/src/bin/bfs_tools/lib/makefile new file mode 100644 index 0000000000..af1f21b6fc --- /dev/null +++ b/src/bin/bfs_tools/lib/makefile @@ -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
+# 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 diff --git a/src/bin/bfs_tools/recover.cpp b/src/bin/bfs_tools/recover.cpp new file mode 100644 index 0000000000..7a114ad6f6 --- /dev/null +++ b/src/bin/bfs_tools/recover.cpp @@ -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 +#include + +#include +#include +#include +#include +#include + + +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(getter.Node()); + + block_run run; + if (indices && indices->FindEntry("name", &run) == B_OK) + return dynamic_cast(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(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(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(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((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(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]] [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; +} +