bfs_tools: recover is now using a set to reduce memory.

* The hashtable stored complete Inode objects (albeit without the actual block).
* Now we only store the block_run which should reduce the memory footprint
  considerably; before "recover" could easily run out of memory. In any case,
  a 64 bit version would still make sense to have :-)
* Saved an extra hash table traversal by counting the node types directly.
* This isn't that well tested yet, though.
This commit is contained in:
Axel Dörfler
2012-12-02 21:03:44 +01:00
parent fd919c2c1a
commit eb8a124325
+115 -76
View File
@@ -5,6 +5,8 @@
//! recovers corrupt BFS disks //! recovers corrupt BFS disks
#include <set>
#include "Disk.h" #include "Disk.h"
#include "Inode.h" #include "Inode.h"
#include "Hashtable.h" #include "Hashtable.h"
@@ -30,6 +32,10 @@ bool gRawMode = false;
bool gVerbose = false; bool gVerbose = false;
// TODO: add a cache for all inodes
typedef std::set<block_run> RunSet;
class InodeHashtable { class InodeHashtable {
public: public:
InodeHashtable(int capacity) InodeHashtable(int capacity)
@@ -113,7 +119,7 @@ class InodeHashtable {
static uint32 BlockRunHash(const block_run *run) static uint32 BlockRunHash(const block_run *run)
{ {
return run->allocation_group << 16 | run->start; return (run->allocation_group << 16) | run->start;
} }
static bool BlockRunCompare(const block_run *runA, const block_run *runB) static bool BlockRunCompare(const block_run *runA, const block_run *runB)
@@ -127,11 +133,14 @@ class InodeHashtable {
uint32 fPercentUsed; uint32 fPercentUsed;
}; };
class InodeGetter { class InodeGetter {
public: public:
InodeGetter(InodeHashtable& hashtable, block_run run) InodeGetter(Disk& disk, block_run run)
{ {
fInode = hashtable.Get(run); fInode = Inode::Factory(&disk, run);
if (fInode != NULL)
fInode->AcquireBuffer();
} }
~InodeGetter() ~InodeGetter()
@@ -140,14 +149,22 @@ class InodeGetter {
fInode->ReleaseBuffer(); fInode->ReleaseBuffer();
} }
Inode* Node() { return fInode; } Inode* Node() const
{
return fInode;
}
void Detach()
{
fInode = NULL;
}
private: private:
Inode* fInode; Inode* fInode;
}; };
InodeHashtable gHashtable(1000); RunSet gMainInodes;
// contains all inodes found on disk in the general data area // contains all inodes found on disk in the general data area
InodeHashtable gLogged(50); InodeHashtable gLogged(50);
// contains all inodes found in the log area // contains all inodes found in the log area
@@ -156,32 +173,56 @@ InodeHashtable gMissingEmpty(25);
class HashtableInodeSource : public Inode::Source { class HashtableInodeSource : public Inode::Source {
public: public:
virtual Inode *InodeAt(block_run run) HashtableInodeSource(Disk& disk)
{ :
Inode *inode; fDisk(disk)
if ((inode = gHashtable.Get(run)) != NULL) {
return inode; }
if ((inode = gLogged.Get(run)) != NULL) virtual Inode *InodeAt(block_run run)
return inode; {
Inode *inode;
if ((inode = gLogged.Get(run)) != NULL)
return inode;
if ((inode = gMissing.Get(run)) != NULL) if ((inode = gMissing.Get(run)) != NULL)
return inode; return inode;
if (gMainInodes.find(run) == gMainInodes.end())
return NULL; return NULL;
}
return Inode::Factory(&fDisk, run);
}
private:
Disk& fDisk;
}; };
bool
operator<(const block_run& a, const block_run& b)
{
return a.allocation_group < b.allocation_group
|| (a.allocation_group == b.allocation_group && a.start < b.start);
}
void void
collectInodes(Disk &disk, InodeHashtable &hashtable, off_t start, off_t end) collectInodes(Disk& disk, RunSet* set, InodeHashtable* hashTable, off_t start,
off_t end)
{ {
char buffer[8192]; char buffer[8192];
Inode inode(&disk, (bfs_inode *)buffer, false); Inode inode(&disk, (bfs_inode *)buffer, false);
off_t count = 0LL;
off_t position = start;
off_t directories = 0LL;
off_t directorySize = 0LL;
off_t files = 0LL;
off_t fileSize = 0LL;
off_t symlinks = 0LL;
off_t count = 0LL;
off_t position = start;
bigtime_t lastUpdate = system_time(); bigtime_t lastUpdate = system_time();
for (off_t offset = start; offset < end; offset += sizeof(buffer)) { for (off_t offset = start; offset < end; offset += sizeof(buffer)) {
@@ -203,7 +244,21 @@ collectInodes(Disk &disk, InodeHashtable &hashtable, off_t start, off_t end)
if (node != NULL) { if (node != NULL) {
if (gVerbose) if (gVerbose)
printf(" node: %Ld \"%s\"\n", position, node->Name()); printf(" node: %Ld \"%s\"\n", position, node->Name());
hashtable.Insert(node);
if (set != NULL)
set->insert(node->BlockRun());
else
hashTable->Insert(node);
if (node->IsDirectory()) {
directories++;
directorySize += node->Size();
} else if (node->IsFile()) {
files++;
fileSize += node->Size();
} else if (node->IsSymlink()) {
symlinks++;
}
count++; count++;
} else if (gVerbose) { } else if (gVerbose) {
printf("\nunrecognized inode:"); printf("\nunrecognized inode:");
@@ -213,40 +268,18 @@ collectInodes(Disk &disk, InodeHashtable &hashtable, off_t start, off_t end)
position += disk.BlockSize(); position += disk.BlockSize();
} }
if (system_time() - lastUpdate > 500000) { if (system_time() - lastUpdate > 500000) {
printf(" block %Ld (%Ld%%), %Ld inodes\33[1A\n", offset, 100 * (offset - start) / (end - start), count); printf(" block %Ld (%Ld%%), %Ld inodes\33[1A\n", offset,
100 * (offset - start) / (end - start), count);
lastUpdate = system_time(); lastUpdate = system_time();
} }
} }
printf("\n%Ld inodes found.\n", count); 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" printf("\n%20Ld directories found (total of %Ld bytes)\n"
"%20Ld files found (total of %Ld bytes)\n" "%20Ld files found (total of %Ld bytes)\n"
"%20Ld symlinks found\n" "%20Ld symlinks found\n"
"--------------------\n" "--------------------\n"
"%20Ld inodes total found in hashtable.\n", "%20Ld inodes total found.\n",
directories, directorySize, files, fileSize, symlinks, count); directories, directorySize, files, fileSize, symlinks, count);
} }
@@ -260,7 +293,7 @@ collectLogInodes(Disk &disk)
printf("\nsearching from %Ld to %Ld (log area)\n",offset,end); printf("\nsearching from %Ld to %Ld (log area)\n",offset,end);
collectInodes(disk, gLogged, offset, end); collectInodes(disk, NULL, &gLogged, offset, end);
} }
@@ -270,39 +303,41 @@ collectRealInodes(Disk &disk)
// first block after bootblock, bitmap, and log // first block after bootblock, bitmap, and log
off_t offset = disk.ToOffset(disk.Log()) + (disk.Log().length off_t offset = disk.ToOffset(disk.Log()) + (disk.Log().length
<< disk.BlockShift()); << disk.BlockShift());
off_t end = /*(17LL << disk.SuperBlock()->ag_shift); off_t end = (off_t)disk.NumBlocks() << disk.BlockShift();
if (end > disk.NumBlocks())
end = */disk.NumBlocks();
end *= disk.BlockSize();
printf("\nsearching from %Ld to %Ld (main area)\n", offset, end); printf("\nsearching from %Ld to %Ld (main area)\n", offset, end);
collectInodes(disk, gHashtable, offset, end); collectInodes(disk, &gMainInodes, NULL, offset, end);
} }
Directory * Directory *
getNameIndex(Disk &disk) getNameIndex(Disk &disk)
{ {
InodeGetter getter(gHashtable, disk.Indices()); InodeGetter getter(disk, disk.Indices());
Directory *indices = dynamic_cast<Directory *>(getter.Node()); Directory *indices = dynamic_cast<Directory *>(getter.Node());
block_run run; block_run run;
if (indices && indices->FindEntry("name", &run) == B_OK) if (indices != NULL && indices->FindEntry("name", &run) == B_OK) {
return dynamic_cast<Directory *>(gHashtable.Get(run)); InodeGetter getter(disk, run);
Inode* node = getter.Node();
getter.Detach();
return dynamic_cast<Directory *>(node);
}
// search name index // search name index
Inode *node; RunSet::iterator iterator = gMainInodes.begin();
for (; iterator != gMainInodes.end(); iterator++) {
InodeGetter getter(disk, *iterator);
Inode* node = getter.Node();
gHashtable.Rewind();
for (; gHashtable.GetNextEntry(&node) == B_OK; gHashtable.Release(node)) {
if (!node->IsIndex() || node->Name() == NULL) if (!node->IsIndex() || node->Name() == NULL)
continue; continue;
if (!strcmp(node->Name(), "name") && node->Mode() & S_STR_INDEX) { if (!strcmp(node->Name(), "name") && node->Mode() & S_STR_INDEX)
return dynamic_cast<Directory *>(node); return dynamic_cast<Directory *>(node);
}
} }
return NULL; return NULL;
} }
@@ -317,7 +352,7 @@ checkDirectoryContents(Disk& disk, Directory *dir)
while (dir->GetNextEntry(name, &run) == B_OK) { while (dir->GetNextEntry(name, &run) == B_OK) {
if (run == dir->BlockRun() || run == dir->Parent() if (run == dir->BlockRun() || run == dir->Parent()
|| gHashtable.Contains(&run)) || gMainInodes.find(run) != gMainInodes.end())
continue; continue;
Inode *missing = gMissing.Get(run); Inode *missing = gMissing.Get(run);
@@ -385,8 +420,12 @@ checkStructure(Disk &disk)
Inode *node; Inode *node;
off_t count = 0; off_t count = 0;
gHashtable.Rewind();
while (gHashtable.GetNextEntry(&node) == B_OK) { RunSet::iterator iterator = gMainInodes.begin();
for (; iterator != gMainInodes.end(); iterator++) {
InodeGetter getter(disk, *iterator);
Inode* node = getter.Node();
count++; count++;
if ((count % 50) == 0) if ((count % 50) == 0)
fprintf(stderr, "%Ld inodes processed...\33[1A\n", count); fprintf(stderr, "%Ld inodes processed...\33[1A\n", count);
@@ -399,7 +438,7 @@ checkStructure(Disk &disk)
// check for the parent directory // check for the parent directory
block_run run = node->Parent(); block_run run = node->Parent();
InodeGetter parentGetter(gHashtable, run); InodeGetter parentGetter(disk, run);
Inode *parentNode = parentGetter.Node(); Inode *parentNode = parentGetter.Node();
Directory *dir = dynamic_cast<Directory *>(parentNode); Directory *dir = dynamic_cast<Directory *>(parentNode);
@@ -509,7 +548,7 @@ checkStructure(Disk &disk)
if (!run.IsZero()) { 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()); //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 (gMainInodes.find(run) == gMainInodes.end()) {
if (gVerbose) { if (gVerbose) {
printf("node \"%s\": attributes are missing (%ld, %d, %d)\n", printf("node \"%s\": attributes are missing (%ld, %d, %d)\n",
node->Name(), run.allocation_group, run.start, run.length); node->Name(), run.allocation_group, run.start, run.length);
@@ -631,33 +670,34 @@ checkStructure(Disk &disk)
void void
copyInodes(const char *copyTo) copyInodes(Disk& disk, const char* copyTo)
{ {
if (!copyTo) if (copyTo == NULL)
return; return;
Inode::Source *source = new HashtableInodeSource; HashtableInodeSource source(disk);
Inode *node; Inode *node;
int32 count = 0; int32 count = 0;
gHashtable.Rewind(); RunSet::iterator iterator = gMainInodes.begin();
while (gHashtable.GetNextEntry(&node) == B_OK) { for (; iterator != gMainInodes.end(); iterator++) {
InodeGetter getter(disk, *iterator);
Inode* node = getter.Node();
if (!node->IsIndex() && !node->IsAttributeDirectory()) if (!node->IsIndex() && !node->IsAttributeDirectory())
node->CopyTo(copyTo, source); node->CopyTo(copyTo, &source);
if ((++count % 500) == 0) if ((++count % 500) == 0)
fprintf(stderr, "copied %ld files...\n", count); fprintf(stderr, "copied %ld files...\n", count);
gHashtable.Release(node);
} }
gMissing.Rewind(); gMissing.Rewind();
while (gMissing.GetNextEntry(&node) == B_OK) { while (gMissing.GetNextEntry(&node) == B_OK) {
if (!node->IsIndex() && !node->IsAttributeDirectory()) if (!node->IsIndex() && !node->IsAttributeDirectory())
node->CopyTo(copyTo, source); node->CopyTo(copyTo, &source);
gHashtable.Release(node); gMissing.Release(node);
} }
} }
@@ -802,12 +842,11 @@ main(int argc, char **argv)
checkStructure(disk); checkStructure(disk);
if (argv[1]) if (argv[1])
copyInodes(argv[1]); copyInodes(disk, argv[1]);
//disk.WriteBootBlock(); //disk.WriteBootBlock();
//disk.BlockBitmap()->CompareWithBackup(); //disk.BlockBitmap()->CompareWithBackup();
gHashtable.MakeEmpty();
gMissing.MakeEmpty(); gMissing.MakeEmpty();
gLogged.MakeEmpty(); gLogged.MakeEmpty();