[haiku-commits] BRANCH ahenriksson-github.production - src/add-ons/kernel/file_systems/bfs

  • From: ahenriksson-github.production <community@xxxxxxxxxxxx>
  • To: haiku-commits@xxxxxxxxxxxxx
  • Date: Tue, 10 Jul 2012 11:49:14 +0200 (CEST)

added 1 changeset to branch 'refs/remotes/ahenriksson-github/production'
old head: debc1307496dc87e1ec14c6bb0630e6d58f852b2
new head: 9b8ebc80c746198a0e965a9666f3328bf727139f

----------------------------------------------------------------------------

9b8ebc8: Refactored checkfs code into FileSystemVisitor and CheckVisitor

                                      [ ahenriksson <sausageboy@xxxxxxxxx> ]

----------------------------------------------------------------------------

Commit:      9b8ebc80c746198a0e965a9666f3328bf727139f

Author:      ahenriksson <sausageboy@xxxxxxxxx>
Date:        Thu Jul  5 08:40:08 2012 UTC

----------------------------------------------------------------------------

10 files changed, 1237 insertions(+), 958 deletions(-)
.../kernel/file_systems/bfs/BlockAllocator.cpp     |  952 +---------------
.../kernel/file_systems/bfs/BlockAllocator.h       |   29 +-
.../kernel/file_systems/bfs/CheckVisitor.cpp       |  733 ++++++++++++
src/add-ons/kernel/file_systems/bfs/CheckVisitor.h |   71 ++
.../kernel/file_systems/bfs/FileSystemVisitor.cpp  |  282 +++++
.../kernel/file_systems/bfs/FileSystemVisitor.h    |   60 +
src/add-ons/kernel/file_systems/bfs/Jamfile        |    2 +
src/add-ons/kernel/file_systems/bfs/Volume.h       |    6 +
.../kernel/file_systems/bfs/kernel_interface.cpp   |   58 +-
src/tools/bfs_shell/Jamfile                        |    2 +

----------------------------------------------------------------------------

diff --git a/src/add-ons/kernel/file_systems/bfs/BlockAllocator.cpp 
b/src/add-ons/kernel/file_systems/bfs/BlockAllocator.cpp
index 8573bbc..e0c08b6 100644
--- a/src/add-ons/kernel/file_systems/bfs/BlockAllocator.cpp
+++ b/src/add-ons/kernel/file_systems/bfs/BlockAllocator.cpp
@@ -9,8 +9,6 @@
 
 #include "BlockAllocator.h"
 
-#include "bfs_control.h"
-#include "BPlusTree.h"
 #include "Debug.h"
 #include "Inode.h"
 #include "Volume.h"
@@ -169,35 +167,6 @@ private:
 #endif
 
 
-struct check_index {
-       check_index()
-               :
-               inode(NULL)
-       {
-       }
-
-       char                            name[B_FILE_NAME_LENGTH];
-       block_run                       run;
-       Inode*                          inode;
-};
-
-
-struct check_cookie {
-       check_cookie()
-       {
-       }
-
-       uint32                          pass;
-       block_run                       current;
-       Inode*                          parent;
-       mode_t                          parent_mode;
-       Stack<block_run>        stack;
-       TreeIterator*           iterator;
-       check_control           control;
-       Stack<check_index*>     indices;
-};
-
-
 class AllocationBlock : public CachedBlock {
 public:
        AllocationBlock(Volume* volume);
@@ -536,9 +505,9 @@ AllocationGroup::Free(Transaction& transaction, uint16 
start, int32 length)
 BlockAllocator::BlockAllocator(Volume* volume)
        :
        fVolume(volume),
-       fGroups(NULL),
-       fCheckBitmap(NULL),
-       fCheckCookie(NULL)
+       fGroups(NULL)
+       //fCheckBitmap(NULL),
+       //fCheckCookie(NULL)
 {
        recursive_lock_init(&fLock, "bfs allocator");
 }
@@ -556,8 +525,9 @@ BlockAllocator::Initialize(bool full)
 {
        fNumGroups = fVolume->AllocationGroups();
        fBlocksPerGroup = fVolume->SuperBlock().BlocksPerAllocationGroup();
-       fNumBlocks = (fVolume->NumBlocks() + fVolume->BlockSize() * 8 - 1)
-               / (fVolume->BlockSize() * 8);
+       //fNumBlocks = (fVolume->NumBlocks() + fVolume->BlockSize() * 8 - 1)
+               /// (fVolume->BlockSize() * 8);
+       fNumBlocks = fVolume->NumBitmapBlocks();
 
        fGroups = new(std::nothrow) AllocationGroup[fNumGroups];
        if (fGroups == NULL)
@@ -1137,13 +1107,6 @@ BlockAllocator::Free(Transaction& transaction, block_run 
run)
 }
 
 
-size_t
-BlockAllocator::BitmapSize() const
-{
-       return fVolume->BlockSize() * fNumBlocks;
-}
-
-
 #ifdef DEBUG_FRAGMENTER
 void
 BlockAllocator::Fragment()
@@ -1249,516 +1212,22 @@ BlockAllocator::_CheckGroup(int32 groupIndex) const
 #endif // DEBUG_ALLOCATION_GROUPS
 
 
-//     #pragma mark - Bitmap validity checking
-
-// TODO: implement new FS checking API
-// Functions to check the validity of the bitmap - they are used from
-// the "checkfs" command (since this does even a bit more, maybe we should
-// move this some place else?)
-
-
-bool
-BlockAllocator::_IsValidCheckControl(const check_control* control)
-{
-       if (control == NULL
-               || control->magic != BFS_IOCTL_CHECK_MAGIC) {
-               FATAL(("invalid check_control (%p)!\n", control));
-               return false;
-       }
-
-       return true;
-}
-
-
-status_t
-BlockAllocator::StartChecking(const check_control* control)
-{
-       if (!_IsValidCheckControl(control))
-               return B_BAD_VALUE;
-
-       fVolume->GetJournal(0)->Lock(NULL, true);
-               // Lock the volume's journal
-
-       recursive_lock_lock(&fLock);
-
-       size_t size = BitmapSize();
-       fCheckBitmap = (uint32*)malloc(size);
-       if (fCheckBitmap == NULL) {
-               recursive_lock_unlock(&fLock);
-               fVolume->GetJournal(0)->Unlock(NULL, true);
-               return B_NO_MEMORY;
-       }
-
-       fCheckCookie = new(std::nothrow) check_cookie();
-       if (fCheckCookie == NULL) {
-               free(fCheckBitmap);
-               fCheckBitmap = NULL;
-               recursive_lock_unlock(&fLock);
-               fVolume->GetJournal(0)->Unlock(NULL, true);
-
-               return B_NO_MEMORY;
-       }
-
-       memcpy(&fCheckCookie->control, control, sizeof(check_control));
-       memset(&fCheckCookie->control.stats, 0, sizeof(control->stats));
-
-       // initialize bitmap
-       memset(fCheckBitmap, 0, size);
-       for (int32 block = fVolume->Log().Start() + fVolume->Log().Length();
-                       block-- > 0;) {
-               _SetCheckBitmapAt(block);
-       }
-
-       fCheckCookie->pass = BFS_CHECK_PASS_BITMAP;
-       fCheckCookie->stack.Push(fVolume->Root());
-       fCheckCookie->stack.Push(fVolume->Indices());
-       fCheckCookie->iterator = NULL;
-       fCheckCookie->control.stats.block_size = fVolume->BlockSize();
-
-       // Put removed vnodes to the stack -- they are not reachable by 
traversing
-       // the file system anymore.
-       InodeList::Iterator iterator = fVolume->RemovedInodes().GetIterator();
-       while (Inode* inode = iterator.Next()) {
-               fCheckCookie->stack.Push(inode->BlockRun());
-       }
-
-       // TODO: check reserved area in bitmap!
-
-       return B_OK;
-}
-
-
-status_t
-BlockAllocator::StopChecking(check_control* control)
-{
-       if (fCheckCookie == NULL)
-               return B_NO_INIT;
-
-       if (fCheckCookie->iterator != NULL) {
-               delete fCheckCookie->iterator;
-               fCheckCookie->iterator = NULL;
-
-               // the current directory inode is still locked in memory
-               put_vnode(fVolume->FSVolume(), 
fVolume->ToVnode(fCheckCookie->current));
-       }
-
-       if (fVolume->IsReadOnly()) {
-               // We can't fix errors on this volume
-               fCheckCookie->control.flags = 0;
-       }
-
-       if (fCheckCookie->control.status != B_ENTRY_NOT_FOUND)
-               FATAL(("BlockAllocator::CheckNextNode() didn't run through\n"));
-
-       switch (fCheckCookie->pass) {
-               case BFS_CHECK_PASS_BITMAP:
-                       // if CheckNextNode() could completely work through, we 
can
-                       // fix any damages of the bitmap
-                       if (fCheckCookie->control.status == B_ENTRY_NOT_FOUND)
-                               _WriteBackCheckBitmap();
-                       break;
-
-               case BFS_CHECK_PASS_INDEX:
-                       _FreeIndices();
-                       break;
-       }
-
-       fVolume->SetCheckingThread(-1);
-
-       if (control != NULL)
-               user_memcpy(control, &fCheckCookie->control, 
sizeof(check_control));
-
-       free(fCheckBitmap);
-       fCheckBitmap = NULL;
-       delete fCheckCookie;
-       fCheckCookie = NULL;
-       recursive_lock_unlock(&fLock);
-       fVolume->GetJournal(0)->Unlock(NULL, true);
-
-       return B_OK;
-}
-
-
-status_t
-BlockAllocator::CheckNextNode(check_control* control)
-{
-       if (fCheckCookie == NULL)
-               return B_NO_INIT;
-
-       fVolume->SetCheckingThread(find_thread(NULL));
-
-       // Make sure the user control is copied on exit
-       class CopyControlOnExit {
-       public:
-               CopyControlOnExit(check_control* source, check_control* 
userTarget)
-                       :
-                       fSource(source),
-                       fTarget(userTarget)
-               {
-               }
-
-               ~CopyControlOnExit()
-               {
-                       if (fTarget != NULL)
-                               user_memcpy(fTarget, fSource, 
sizeof(check_control));
-               }
-
-       private:
-               check_control*  fSource;
-               check_control*  fTarget;
-       } copyControl(&fCheckCookie->control, control);
-
-       while (true) {
-               if (fCheckCookie->iterator == NULL) {
-                       if (!fCheckCookie->stack.Pop(&fCheckCookie->current)) {
-                               // No more runs on the stack, we might be 
finished!
-                               if (fCheckCookie->pass == BFS_CHECK_PASS_BITMAP
-                                       && !fCheckCookie->indices.IsEmpty()) {
-                                       // Start second pass to repair indices
-                                       _WriteBackCheckBitmap();
-
-                                       fCheckCookie->pass = 
BFS_CHECK_PASS_INDEX;
-                                       fCheckCookie->control.pass = 
BFS_CHECK_PASS_INDEX;
-
-                                       status_t status = _PrepareIndices();
-                                       if (status != B_OK) {
-                                               fCheckCookie->control.status = 
status;
-                                               return status;
-                                       }
-
-                                       
fCheckCookie->stack.Push(fVolume->Root());
-                                       continue;
-                               }
-
-                               fCheckCookie->control.status = 
B_ENTRY_NOT_FOUND;
-                               return B_ENTRY_NOT_FOUND;
-                       }
-
-                       Vnode vnode(fVolume, fCheckCookie->current);
-                       Inode* inode;
-                       if (vnode.Get(&inode) != B_OK) {
-                               FATAL(("check: Could not open inode at %" 
B_PRIdOFF "\n",
-                                       
fVolume->ToBlock(fCheckCookie->current)));
-                               continue;
-                       }
-
-                       fCheckCookie->control.inode = inode->ID();
-                       fCheckCookie->control.mode = inode->Mode();
-
-                       if (!inode->IsContainer()) {
-                               // Check file
-                               fCheckCookie->control.errors = 0;
-                               fCheckCookie->control.status = 
CheckInode(inode, NULL);
-
-                               if (inode->GetName(fCheckCookie->control.name) 
< B_OK)
-                                       strcpy(fCheckCookie->control.name, 
"(node has no name)");
-
-                               return B_OK;
-                       }
-
-                       // Check directory
-                       BPlusTree* tree = inode->Tree();
-                       if (tree == NULL) {
-                               FATAL(("check: could not open b+tree from inode 
at %" B_PRIdOFF
-                                       "\n", 
fVolume->ToBlock(fCheckCookie->current)));
-                               continue;
-                       }
-
-                       fCheckCookie->parent = inode;
-                       fCheckCookie->parent_mode = inode->Mode();
-
-                       // get iterator for the next directory
-                       fCheckCookie->iterator = new(std::nothrow) 
TreeIterator(tree);
-                       if (fCheckCookie->iterator == NULL)
-                               RETURN_ERROR(B_NO_MEMORY);
-
-                       // the inode must stay locked in memory until the 
iterator is freed
-                       vnode.Keep();
-
-                       // check the inode of the directory
-                       fCheckCookie->control.errors = 0;
-                       fCheckCookie->control.status = CheckInode(inode, NULL);
-
-                       if (inode->GetName(fCheckCookie->control.name) != B_OK)
-                               strcpy(fCheckCookie->control.name, "(dir has no 
name)");
-
-                       return B_OK;
-               }
-
-               char name[B_FILE_NAME_LENGTH];
-               uint16 length;
-               ino_t id;
-
-               status_t status = fCheckCookie->iterator->GetNextEntry(name, 
&length,
-                       B_FILE_NAME_LENGTH, &id);
-               if (status != B_OK) {
-                       // we no longer need this iterator
-                       delete fCheckCookie->iterator;
-                       fCheckCookie->iterator = NULL;
-
-                       // unlock the directory's inode from memory
-                       put_vnode(fVolume->FSVolume(),
-                               fVolume->ToVnode(fCheckCookie->current));
-
-                       if (status == B_ENTRY_NOT_FOUND) {
-                               // We iterated over all entries already, just 
go on to the next
-                               continue;
-                       }
-
-                       // Iterating over the B+tree failed - we let the 
checkfs run
-                       // fail completely, as we would delete all files we 
cannot
-                       // access.
-                       // TODO: maybe have a force parameter that actually 
does that.
-                       // TODO: we also need to be able to repair broken 
B+trees!
-                       return status;
-               }
-
-               // ignore "." and ".." entries
-               if (!strcmp(name, ".") || !strcmp(name, ".."))
-                       continue;
-
-               // fill in the control data as soon as we have them
-               strlcpy(fCheckCookie->control.name, name, B_FILE_NAME_LENGTH);
-               fCheckCookie->control.inode = id;
-               fCheckCookie->control.errors = 0;
-
-               Vnode vnode(fVolume, id);
-               Inode* inode;
-               if (vnode.Get(&inode) != B_OK) {
-                       FATAL(("Could not open inode ID %" B_PRIdINO "!\n", 
id));
-                       fCheckCookie->control.errors |= BFS_COULD_NOT_OPEN;
-
-                       if ((fCheckCookie->control.flags & BFS_REMOVE_INVALID) 
!= 0) {
-                               status = 
_RemoveInvalidNode(fCheckCookie->parent,
-                                       fCheckCookie->iterator->Tree(), NULL, 
name);
-                       } else
-                               status = B_ERROR;
-
-                       fCheckCookie->control.status = status;
-                       return B_OK;
-               }
-
-               // check if the inode's name is the same as in the b+tree
-               if (fCheckCookie->pass == BFS_CHECK_PASS_BITMAP
-                       && inode->IsRegularNode()) {
-                       RecursiveLocker locker(inode->SmallDataLock());
-                       NodeGetter node(fVolume, inode);
-
-                       const char* localName = inode->Name(node.Node());
-                       if (localName == NULL || strcmp(localName, name)) {
-                               fCheckCookie->control.errors |= 
BFS_NAMES_DONT_MATCH;
-                               FATAL(("Names differ: tree \"%s\", inode 
\"%s\"\n", name,
-                                       localName));
-
-                               if ((fCheckCookie->control.flags & 
BFS_FIX_NAME_MISMATCHES)
-                                               != 0) {
-                                       // Rename the inode
-                                       Transaction transaction(fVolume, 
inode->BlockNumber());
-
-                                       // Note, this may need extra blocks, 
but the inode will
-                                       // only be checked afterwards, so that 
it won't be lost
-                                       status = inode->SetName(transaction, 
name);
-                                       if (status == B_OK)
-                                               status = 
inode->WriteBack(transaction);
-                                       if (status == B_OK)
-                                               status = transaction.Done();
-                                       if (status != B_OK) {
-                                               fCheckCookie->control.status = 
status;
-                                               return B_OK;
-                                       }
-                               }
-                       }
-               }
-
-               fCheckCookie->control.mode = inode->Mode();
-
-               // Check for the correct mode of the node (if the mode of the
-               // file don't fit to its parent, there is a serious problem)
-               if (fCheckCookie->pass == BFS_CHECK_PASS_BITMAP
-                       && (((fCheckCookie->parent_mode & S_ATTR_DIR) != 0
-                                       && !inode->IsAttribute())
-                               || ((fCheckCookie->parent_mode & S_INDEX_DIR) 
!= 0
-                                       && !inode->IsIndex())
-                               || (is_directory(fCheckCookie->parent_mode)
-                                       && !inode->IsRegularNode()))) {
-                       FATAL(("inode at %" B_PRIdOFF " is of wrong type: %o 
(parent "
-                               "%o at %" B_PRIdOFF ")!\n", 
inode->BlockNumber(),
-                               inode->Mode(), fCheckCookie->parent_mode,
-                               fCheckCookie->parent->BlockNumber()));
-
-                       // if we are allowed to fix errors, we should remove 
the file
-                       if ((fCheckCookie->control.flags & 
BFS_REMOVE_WRONG_TYPES) != 0
-                               && (fCheckCookie->control.flags & 
BFS_FIX_BITMAP_ERRORS)
-                                               != 0) {
-                               status = 
_RemoveInvalidNode(fCheckCookie->parent, NULL,
-                                       inode, name);
-                       } else
-                               status = B_ERROR;
-
-                       fCheckCookie->control.errors |= BFS_WRONG_TYPE;
-                       fCheckCookie->control.status = status;
-                       return B_OK;
-               }
-
-               // push the directory on the stack so that it will be scanned 
later
-               if (inode->IsContainer() && !inode->IsIndex())
-                       fCheckCookie->stack.Push(inode->BlockRun());
-               else {
-                       // check it now
-                       fCheckCookie->control.status = CheckInode(inode, name);
-                       return B_OK;
-               }
-       }
-       // is never reached
-}
-
-
-status_t
-BlockAllocator::_RemoveInvalidNode(Inode* parent, BPlusTree* tree, Inode* 
inode,
-       const char* name)
-{
-       // It's safe to start a transaction, because Inode::Remove()
-       // won't touch the block bitmap (which we hold the lock for)
-       // if we set the INODE_DONT_FREE_SPACE flag - since we fix
-       // the bitmap anyway.
-       Transaction transaction(fVolume, parent->BlockNumber());
-       status_t status;
-
-       if (inode != NULL) {
-               inode->Node().flags |= 
HOST_ENDIAN_TO_BFS_INT32(INODE_DONT_FREE_SPACE);
-
-               status = parent->Remove(transaction, name, NULL, false, true);
-       } else {
-               parent->WriteLockInTransaction(transaction);
-
-               // does the file even exist?
-               off_t id;
-               status = tree->Find((uint8*)name, (uint16)strlen(name), &id);
-               if (status == B_OK)
-                       status = tree->Remove(transaction, name, id);
-       }
-
-       if (status == B_OK) {
-               entry_cache_remove(fVolume->ID(), parent->ID(), name);
-               transaction.Done();
-       }
-
-       return status;
-}
-
-
-bool
-BlockAllocator::_CheckBitmapIsUsedAt(off_t block) const
-{
-       size_t size = BitmapSize();
-       uint32 index = block / 32;      // 32bit resolution
-       if (index > size / 4)
-               return false;
-
-       return BFS_ENDIAN_TO_HOST_INT32(fCheckBitmap[index])
-               & (1UL << (block & 0x1f));
-}
-
-
-void
-BlockAllocator::_SetCheckBitmapAt(off_t block)
-{
-       size_t size = BitmapSize();
-       uint32 index = block / 32;      // 32bit resolution
-       if (index > size / 4)
-               return;
-
-       fCheckBitmap[index] |= HOST_ENDIAN_TO_BFS_INT32(1UL << (block & 0x1f));
-}
-
-
-status_t
-BlockAllocator::_WriteBackCheckBitmap()
-{
-       if (fVolume->IsReadOnly())
-               return B_OK;
-
-       // calculate the number of used blocks in the check bitmap
-       size_t size = BitmapSize();
-       off_t usedBlocks = 0LL;
-
-       // TODO: update the allocation groups used blocks info
-       for (uint32 i = size >> 2; i-- > 0;) {
-               uint32 compare = 1;
-               // Count the number of bits set
-               for (int16 j = 0; j < 32; j++, compare <<= 1) {
-                       if ((compare & fCheckBitmap[i]) != 0)
-                               usedBlocks++;
-               }
-       }
-
-       fCheckCookie->control.stats.freed = fVolume->UsedBlocks() - usedBlocks
-               + fCheckCookie->control.stats.missing;
-       if (fCheckCookie->control.stats.freed < 0)
-               fCheckCookie->control.stats.freed = 0;
-
-       // Should we fix errors? Were there any errors we can fix?
-       if ((fCheckCookie->control.flags & BFS_FIX_BITMAP_ERRORS) != 0
-               && (fCheckCookie->control.stats.freed != 0
-                       || fCheckCookie->control.stats.missing != 0)) {
-               // If so, write the check bitmap back over the original one,
-               // and use transactions here to play safe - we even use several
-               // transactions, so that we don't blow the maximum log size
-               // on large disks, since we don't need to make this atomic.
-#if 0
-               // prints the blocks that differ
-               off_t block = 0;
-               for (int32 i = 0; i < fNumGroups; i++) {
-                       AllocationBlock cached(fVolume);
-                       for (uint32 j = 0; j < fGroups[i].NumBlocks(); j++) {
-                               cached.SetTo(fGroups[i], j);
-                               for (uint32 k = 0; k < cached.NumBlockBits(); 
k++) {
-                                       if (cached.IsUsed(k) != 
_CheckBitmapIsUsedAt(block)) {
-                                               dprintf("differ block %lld 
(should be %d)\n", block,
-                                                       
_CheckBitmapIsUsedAt(block));
-                                       }
-                                       block++;
-                               }
-                       }
-               }
-#endif
-
-               fVolume->SuperBlock().used_blocks
-                       = HOST_ENDIAN_TO_BFS_INT64(usedBlocks);
-
-               size_t blockSize = fVolume->BlockSize();
-
-               for (uint32 i = 0; i < fNumBlocks; i += 512) {
-                       Transaction transaction(fVolume, 1 + i);
-
-                       uint32 blocksToWrite = 512;
-                       if (blocksToWrite + i > fNumBlocks)
-                               blocksToWrite = fNumBlocks - i;
-
-                       status_t status = transaction.WriteBlocks(1 + i,
-                               (uint8*)fCheckBitmap + i * blockSize, 
blocksToWrite);
-                       if (status < B_OK) {
-                               FATAL(("error writing bitmap: %s\n", 
strerror(status)));
-                               return status;
-                       }
-                       transaction.Done();
-               }
-       }
-
-       return B_OK;
-}
+//     #pragma mark -
 
 
 /*!    Checks whether or not the specified block range is allocated or not,
        depending on the \a allocated argument.
 */
 status_t
-BlockAllocator::CheckBlocks(off_t start, off_t length, bool allocated)
+BlockAllocator::CheckBlocks(off_t start, off_t length, bool allocated,
+       off_t* firstError)
 {
        if (start < 0 || start + length > fVolume->NumBlocks())
                return B_BAD_VALUE;
 
-       uint32 group = start >> fVolume->AllocationGroupShift();
+       off_t block = start;
+
+       int32 group = start >> fVolume->AllocationGroupShift();
        uint32 groupBlock = start / (fVolume->BlockSize() << 3);
        uint32 blockOffset = start % fVolume->BlockSize();
 
@@ -1769,9 +1238,16 @@ BlockAllocator::CheckBlocks(off_t start, off_t length, 
bool allocated)
                        RETURN_ERROR(B_IO_ERROR);
 
                for (; blockOffset < cached.NumBlockBits() && length > 0;
-                               blockOffset++, length--) {
+                               blockOffset++, length--, block++) {
                        if (cached.IsUsed(blockOffset) != allocated) {
-                               RETURN_ERROR(B_BAD_DATA);
+                               PRINT(("CheckBlocks: Erroneous block (group = 
%ld, "
+                                       "groupBlock = %ld, blockOffset = 
%ld)!\n",
+                                       group, groupBlock, blockOffset));
+
+                               if (firstError)
+                                       *firstError = block;
+
+                               return B_BAD_DATA;
                        }
                }
 
@@ -1787,8 +1263,8 @@ BlockAllocator::CheckBlocks(off_t start, off_t length, 
bool allocated)
 }
 
 
-status_t
-BlockAllocator::CheckBlockRun(block_run run, const char* type, bool allocated)
+bool
+BlockAllocator::IsValidBlockRun(block_run run)
 {
        if (run.AllocationGroup() < 0 || run.AllocationGroup() >= fNumGroups
                || run.Start() > fGroups[run.AllocationGroup()].fNumBits
@@ -1797,386 +1273,26 @@ BlockAllocator::CheckBlockRun(block_run run, const 
char* type, bool allocated)
                || run.length == 0) {
                PRINT(("%s: block_run(%ld, %u, %u) is invalid!\n", type,
                        run.AllocationGroup(), run.Start(), run.Length()));
-               if (fCheckCookie == NULL)
-                       return B_BAD_DATA;
-
-               fCheckCookie->control.errors |= BFS_INVALID_BLOCK_RUN;
-               return B_OK;
-       }
-
-       uint32 bitsPerBlock = fVolume->BlockSize() << 3;
-       uint32 block = run.Start() / bitsPerBlock;
-       uint32 pos = run.Start() % bitsPerBlock;
-       int32 length = 0;
-       off_t firstMissing = -1, firstSet = -1;
-       off_t firstGroupBlock
-               = (off_t)run.AllocationGroup() << 
fVolume->AllocationGroupShift();
-
-       AllocationBlock cached(fVolume);
-
-       for (; block < fBlocksPerGroup && length < run.Length(); block++, pos = 
0) {
-               if (cached.SetTo(fGroups[run.AllocationGroup()], block) < B_OK)
-                       RETURN_ERROR(B_IO_ERROR);
-
-               if (pos >= cached.NumBlockBits()) {
-                       // something very strange has happened...
-                       RETURN_ERROR(B_ERROR);
-               }
-
-               while (length < run.Length() && pos < cached.NumBlockBits()) {
-                       if (cached.IsUsed(pos) != allocated) {
-                               if (fCheckCookie == NULL) {
-                                       PRINT(("%s: block_run(%ld, %u, %u) is 
only partially "
-                                               "allocated (pos = %ld, length = 
%ld)!\n", type,
-                                               run.AllocationGroup(), 
run.Start(), run.Length(),
-                                               pos, length));
-                                       return B_BAD_DATA;
-                               }
-                               if (firstMissing == -1) {
-                                       firstMissing = firstGroupBlock + pos + 
block * bitsPerBlock;
-                                       fCheckCookie->control.errors |= 
BFS_MISSING_BLOCKS;
-                               }
-                               fCheckCookie->control.stats.missing++;
-                       } else if (firstMissing != -1) {
-                               PRINT(("%s: block_run(%ld, %u, %u): blocks %Ld 
- %Ld are "
-                                       "%sallocated!\n", type, 
run.AllocationGroup(), run.Start(),
-                                       run.Length(), firstMissing,
-                                       firstGroupBlock + pos + block * 
bitsPerBlock - 1,
-                                       allocated ? "not " : ""));
-                               firstMissing = -1;
-                       }
-
-                       if (fCheckBitmap != NULL) {
-                               // Set the block in the check bitmap as well, 
but have a look
-                               // if it is already allocated first
-                               uint32 offset = pos + block * bitsPerBlock;
-                               if (_CheckBitmapIsUsedAt(firstGroupBlock + 
offset)) {
-                                       if (firstSet == -1) {
-                                               firstSet = firstGroupBlock + 
offset;
-                                               fCheckCookie->control.errors |= 
BFS_BLOCKS_ALREADY_SET;
-                                               dprintf("block %" B_PRIdOFF " 
is already set!!!\n",
-                                                       firstGroupBlock + 
offset);
-                                       }
-                                       
fCheckCookie->control.stats.already_set++;
-                               } else {
-                                       if (firstSet != -1) {
-                                               FATAL(("%s: block_run(%d, %u, 
%u): blocks %" B_PRIdOFF
-                                                       " - %" B_PRIdOFF " are 
already set!\n", type,
-                                                       
(int)run.AllocationGroup(), run.Start(),
-                                                       run.Length(), firstSet,
-                                                       firstGroupBlock + 
offset - 1));
-                                               firstSet = -1;
-                                       }
-                                       _SetCheckBitmapAt(firstGroupBlock + 
offset);
-                               }
-                       }
-                       length++;
-                       pos++;
-               }
-
-               if (block + 1 >= fBlocksPerGroup || length >= run.Length()) {
-                       if (firstMissing != -1) {
-                               PRINT(("%s: block_run(%ld, %u, %u): blocks %Ld 
- %Ld are not "
-                                       "allocated!\n", type, 
run.AllocationGroup(), run.Start(),
-                                       run.Length(), firstMissing,
-                                       firstGroupBlock + pos + block * 
bitsPerBlock - 1));
-                       }
-                       if (firstSet != -1) {
-                               FATAL(("%s: block_run(%d, %u, %u): blocks %" 
B_PRIdOFF " - %"
-                                       B_PRIdOFF " are already set!\n", type,
-                                       (int)run.AllocationGroup(), 
run.Start(), run.Length(),
-                                       firstSet,
-                                       firstGroupBlock + pos + block * 
bitsPerBlock - 1));
-                       }
-               }
-       }
-
-       return B_OK;
-}
-
-
-status_t
-BlockAllocator::CheckInode(Inode* inode, const char* name)
-{
-       if (fCheckCookie != NULL && fCheckBitmap == NULL)
-               return B_NO_INIT;
-       if (inode == NULL)
-               return B_BAD_VALUE;
-
-       switch (fCheckCookie->pass) {
-               case BFS_CHECK_PASS_BITMAP:
-               {
-                       status_t status = _CheckInodeBlocks(inode, name);
-                       if (status != B_OK)
-                               return status;
-
-                       // Check the B+tree as well
-                       if (inode->IsContainer()) {
-                               bool repairErrors
-                                       = (fCheckCookie->control.flags & 
BFS_FIX_BPLUSTREES) != 0;
-                               bool errorsFound = false;
-                               status = inode->Tree()->Validate(repairErrors, 
errorsFound);
-                               if (errorsFound) {
-                                       fCheckCookie->control.errors |= 
BFS_INVALID_BPLUSTREE;
-                                       if (inode->IsIndex() && name != NULL && 
repairErrors) {
-                                               // We completely rebuild 
corrupt indices
-                                               check_index* index = 
new(std::nothrow) check_index;
-                                               if (index == NULL)
-                                                       return B_NO_MEMORY;
-
-                                               strlcpy(index->name, name, 
sizeof(index->name));
-                                               index->run = inode->BlockRun();
-                                               
fCheckCookie->indices.Push(index);
-                                       }
-                               }
-                       }
-
-                       return status;
-               }
-
-               case BFS_CHECK_PASS_INDEX:
-                       return _AddInodeToIndex(inode);
-       }
-
-       return B_OK;
-}
-
-
-status_t
-BlockAllocator::_CheckInodeBlocks(Inode* inode, const char* name)
-{
-       status_t status = CheckBlockRun(inode->BlockRun(), "inode");
-       if (status != B_OK)
-               return status;
-
-       // If the inode has an attribute directory, push it on the stack
-       if (!inode->Attributes().IsZero())
-               fCheckCookie->stack.Push(inode->Attributes());
-
-       if (inode->IsSymLink() && (inode->Flags() & INODE_LONG_SYMLINK) == 0) {
-               // symlinks may not have a valid data stream
-               if (strlen(inode->Node().short_symlink) >= 
SHORT_SYMLINK_NAME_LENGTH)
-                       return B_BAD_DATA;
-
-               return B_OK;
-       }
-
-       data_stream* data = &inode->Node().data;
-
-       // check the direct range
-
-       if (data->max_direct_range) {
-               for (int32 i = 0; i < NUM_DIRECT_BLOCKS; i++) {
-                       if (data->direct[i].IsZero())
-                               break;
-
-                       status = CheckBlockRun(data->direct[i], "direct");
-                       if (status < B_OK)
-                               return status;
-
-                       fCheckCookie->control.stats.direct_block_runs++;
-                       fCheckCookie->control.stats.blocks_in_direct
-                               += data->direct[i].Length();
-               }
-       }
-
-       CachedBlock cached(fVolume);
-
-       // check the indirect range
-
-       if (data->max_indirect_range) {
-               status = CheckBlockRun(data->indirect, "indirect");
-               if (status < B_OK)
-                       return status;
-
-               off_t block = fVolume->ToBlock(data->indirect);
-
-               for (int32 i = 0; i < data->indirect.Length(); i++) {
-                       block_run* runs = (block_run*)cached.SetTo(block + i);
-                       if (runs == NULL)
-                               RETURN_ERROR(B_IO_ERROR);
-
-                       int32 runsPerBlock = fVolume->BlockSize() / 
sizeof(block_run);
-                       int32 index = 0;
-                       for (; index < runsPerBlock; index++) {
-                               if (runs[index].IsZero())
-                                       break;
-
-                               status = CheckBlockRun(runs[index], 
"indirect->run");
-                               if (status < B_OK)
-                                       return status;
-
-                               
fCheckCookie->control.stats.indirect_block_runs++;
-                               fCheckCookie->control.stats.blocks_in_indirect
-                                       += runs[index].Length();
-                       }
-                       fCheckCookie->control.stats.indirect_array_blocks++;
-
-                       if (index < runsPerBlock)
-                               break;
-               }
-       }
-
-       // check the double indirect range
-
-       if (data->max_double_indirect_range) {
-               status = CheckBlockRun(data->double_indirect, "double 
indirect");
-               if (status != B_OK)
-                       return status;
-
-               int32 runsPerBlock = runs_per_block(fVolume->BlockSize());
-               int32 runsPerArray = runsPerBlock * 
data->double_indirect.Length();
-
-               CachedBlock cachedDirect(fVolume);
-
-               for (int32 indirectIndex = 0; indirectIndex < runsPerArray;
-                               indirectIndex++) {
-                       // get the indirect array block
-                       block_run* array = (block_run*)cached.SetTo(
-                               fVolume->ToBlock(data->double_indirect)
-                                       + indirectIndex / runsPerBlock);
-                       if (array == NULL)
-                               return B_IO_ERROR;
-
-                       block_run indirect = array[indirectIndex % 
runsPerBlock];
-                       // are we finished yet?
-                       if (indirect.IsZero())
-                               return B_OK;
-
-                       status = CheckBlockRun(indirect, "double 
indirect->runs");
-                       if (status != B_OK)
-                               return status;
-
-                       int32 maxIndex = (indirect.Length() << 
fVolume->BlockShift())
-                               / sizeof(block_run);
-
-                       for (int32 index = 0; index < maxIndex; ) {
-                               block_run* runs = 
(block_run*)cachedDirect.SetTo(
-                                       fVolume->ToBlock(indirect) + index / 
runsPerBlock);
-                               if (runs == NULL)
-                                       return B_IO_ERROR;
-
-                               do {
-                                       // are we finished yet?
-                                       if (runs[index % runsPerBlock].IsZero())
-                                               return B_OK;
-
-                                       status = CheckBlockRun(runs[index % 
runsPerBlock],
-                                               "double indirect->runs->run");
-                                       if (status != B_OK)
-                                               return status;
-
-                                       
fCheckCookie->control.stats.double_indirect_block_runs++;
-                                       
fCheckCookie->control.stats.blocks_in_double_indirect
-                                               += runs[index % 
runsPerBlock].Length();
-                               } while ((++index % runsPerArray) != 0);
-                       }
-
-                       
fCheckCookie->control.stats.double_indirect_array_blocks++;
-               }
-       }
-
-       return B_OK;
-}
-
-
-status_t
-BlockAllocator::_PrepareIndices()
-{
-       int32 count = 0;
-
-       for (int32 i = 0; i < fCheckCookie->indices.CountItems(); i++) {
-               check_index* index = fCheckCookie->indices.Array()[i];
-               Vnode vnode(fVolume, index->run);
-               Inode* inode;
-               status_t status = vnode.Get(&inode);
-               if (status != B_OK) {
-                       FATAL(("check: Could not open index at %" B_PRIdOFF 
"\n",
-                               fVolume->ToBlock(index->run)));
-                       return status;
-               }
-
-               BPlusTree* tree = inode->Tree();
-               if (tree == NULL) {
-                       // TODO: We can't yet repair those
-                       continue;
-               }
-
-               status = tree->MakeEmpty();
-               if (status != B_OK)
-                       return status;
-
-               index->inode = inode;
-               vnode.Keep();
-               count++;
-       }
-
-       return count == 0 ? B_ENTRY_NOT_FOUND : B_OK;
-}
-
-
-void
-BlockAllocator::_FreeIndices()
-{
-       for (int32 i = 0; i < fCheckCookie->indices.CountItems(); i++) {
-               check_index* index = fCheckCookie->indices.Array()[i];
-               if (index->inode != NULL) {
-                       put_vnode(fVolume->FSVolume(),
-                               fVolume->ToVnode(index->inode->BlockRun()));
-               }
+               return false;
        }
-       fCheckCookie->indices.MakeEmpty();
+       return true;
 }
 
 
 status_t
-BlockAllocator::_AddInodeToIndex(Inode* inode)
+BlockAllocator::CheckBlockRun(block_run run, const char* type, bool allocated)
 {
-       Transaction transaction(fVolume, inode->BlockNumber());
-
-       for (int32 i = 0; i < fCheckCookie->indices.CountItems(); i++) {
-               check_index* index = fCheckCookie->indices.Array()[i];
-               if (index->inode == NULL)
-                       continue;
-
-               index->inode->WriteLockInTransaction(transaction);
-
-               BPlusTree* tree = index->inode->Tree();
-               if (tree == NULL)
-                       return B_ERROR;
-
-               status_t status = B_OK;
-
-               if (!strcmp(index->name, "name")) {
-                       if (inode->InNameIndex()) {
-                               char name[B_FILE_NAME_LENGTH];
-                               if (inode->GetName(name, B_FILE_NAME_LENGTH) != 
B_OK)
-                                       return B_ERROR;
-
-                               status = tree->Insert(transaction, name, 
inode->ID());
-                       }
-               } else if (!strcmp(index->name, "last_modified")) {
-                       if (inode->InLastModifiedIndex()) {
-                               status = tree->Insert(transaction, 
inode->OldLastModified(),
-                                       inode->ID());
-                       }
-               } else if (!strcmp(index->name, "size")) {
-                       if (inode->InSizeIndex())
-                               status = tree->Insert(transaction, 
inode->Size(), inode->ID());
-               } else {
-                       uint8 key[BPLUSTREE_MAX_KEY_LENGTH];
-                       size_t keyLength = BPLUSTREE_MAX_KEY_LENGTH;
-                       if (inode->ReadAttribute(index->name, B_ANY_TYPE, 0, 
key,
-                                       &keyLength) == B_OK) {
-                               status = tree->Insert(transaction, key, 
keyLength, inode->ID());
-                       }
-               }
+       if (!IsValidBlockRun(run))
+               return B_BAD_DATA;
 
-               if (status != B_OK)
-                       return status;
+       status_t status = CheckBlocks(fVolume->ToBlock(run), run.Length(),
+               allocated);
+       if (status != B_OK) {
+               PRINT(("%s: block_run(%ld, %u, %u) is only partially 
allocated!\n",
+                       type, run.AllocationGroup(), run.Start(), 
run.Length()));
        }
 
-       return transaction.Done();
+       return status;
 }
 
 
diff --git a/src/add-ons/kernel/file_systems/bfs/BlockAllocator.h 
b/src/add-ons/kernel/file_systems/bfs/BlockAllocator.h
index d3b575d..aa62881 100644
--- a/src/add-ons/kernel/file_systems/bfs/BlockAllocator.h
+++ b/src/add-ons/kernel/file_systems/bfs/BlockAllocator.h
@@ -10,14 +10,11 @@
 
 
 class AllocationGroup;
-class BPlusTree;
 class Inode;
 class Transaction;
 class Volume;
 struct disk_super_block;
 struct block_run;
-struct check_control;
-struct check_cookie;
 
 
 //#define DEBUG_ALLOCATION_GROUPS
@@ -48,18 +45,15 @@ public:
                                                                uint16 minimum, 
block_run& run,
                                                                off_t 
beginBlock = 0, off_t endBlock = 0);
 
-                       status_t                StartChecking(const 
check_control* control);
-                       status_t                StopChecking(check_control* 
control);
-                       status_t                CheckNextNode(check_control* 
control);
-
                        status_t                CheckBlocks(off_t start, off_t 
length,
-                                                               bool allocated 
= true);
+                                                               bool allocated 
= true,
+                                                               off_t* 
firstError = NULL);
                        status_t                CheckBlockRun(block_run run,
                                                                const char* 
type = NULL,
                                                                bool allocated 
= true);
-                       status_t                CheckInode(Inode* inode, const 
char* name);
+                       bool                    IsValidBlockRun(block_run run);
 
-                       size_t                  BitmapSize() const;
+                       recursive_lock& Lock() { return fLock; }
 
 #ifdef BFS_DEBUGGER_COMMANDS
                        void                    Dump(int32 index);
@@ -69,21 +63,9 @@ public:
 #endif
 
 private:
-                       status_t                _RemoveInvalidNode(Inode* 
parent, BPlusTree* tree,
-                                                               Inode* inode, 
const char* name);
 #ifdef DEBUG_ALLOCATION_GROUPS
                        void                    _CheckGroup(int32 group) const;
 #endif
-                       bool                    _IsValidCheckControl(const 
check_control* control);
-                       bool                    _CheckBitmapIsUsedAt(off_t 
block) const;
-                       void                    _SetCheckBitmapAt(off_t block);
-                       status_t                _CheckInodeBlocks(Inode* inode, 
const char* name);
-                       status_t                _FinishBitmapPass();
-                       status_t                _PrepareIndices();
-                       void                    _FreeIndices();
-                       status_t                _AddInodeToIndex(Inode* inode);
-                       status_t                _WriteBackCheckBitmap();
-
        static  status_t                _Initialize(BlockAllocator* self);
 
 private:
@@ -93,9 +75,6 @@ private:
                        int32                   fNumGroups;
                        uint32                  fBlocksPerGroup;
                        uint32                  fNumBlocks;
-
-                       uint32*                 fCheckBitmap;
-                       check_cookie*   fCheckCookie;
 };
 
 #ifdef BFS_DEBUGGER_COMMANDS
diff --git a/src/add-ons/kernel/file_systems/bfs/CheckVisitor.cpp 
b/src/add-ons/kernel/file_systems/bfs/CheckVisitor.cpp
new file mode 100644
index 0000000..feb9579
--- /dev/null
+++ b/src/add-ons/kernel/file_systems/bfs/CheckVisitor.cpp
@@ -0,0 +1,733 @@
+#include "CheckVisitor.h"
+
+#include "BlockAllocator.h"
+#include "BPlusTree.h"
+#include "Inode.h"
+#include "Volume.h"
+
+
+struct check_index {
+       check_index()
+               :
+               inode(NULL)
+       {
+       }
+
+       char                            name[B_FILE_NAME_LENGTH];
+       block_run                       run;
+       Inode*                          inode;
+};
+
+
+CheckVisitor::CheckVisitor(Volume* volume)
+       :
+       FileSystemVisitor(volume),
+       fCheckBitmap(NULL)
+{
+}
+
+
+CheckVisitor::~CheckVisitor()
+{
+       free(fCheckBitmap);
+}
+
+
+status_t
+CheckVisitor::StartBitmapPass()
+{
+       if (!_ControlValid())
+               return B_BAD_VALUE;
+
+       // Lock the volume's journal and block allocator
+       GetVolume()->GetJournal(0)->Lock(NULL, true);
+       recursive_lock_lock(&GetVolume()->Allocator().Lock());
+
+       size_t size = _BitmapSize();
+       fCheckBitmap = (uint32*)malloc(size);
+       if (fCheckBitmap == NULL) {
+               recursive_lock_unlock(&GetVolume()->Allocator().Lock());
+               GetVolume()->GetJournal(0)->Unlock(NULL, true);
+               return B_NO_MEMORY;
+       }
+
+       memset(&Control().stats, 0, sizeof(check_control::stats));
+
+       // initialize bitmap
+       memset(fCheckBitmap, 0, size);
+       for (int32 block = GetVolume()->Log().Start() + 
GetVolume()->Log().Length();
+                       block-- > 0;) {
+               _SetCheckBitmapAt(block);
+       }
+
+       Control().pass = BFS_CHECK_PASS_BITMAP;
+       Control().stats.block_size = GetVolume()->BlockSize();
+
+       // TODO: check reserved area in bitmap!
+
+       Start(VISIT_REGULAR | VISIT_INDICES | VISIT_REMOVED
+               | VISIT_ATTRIBUTE_DIRECTORIES);
+
+       return B_OK;
+}
+
+
+status_t
+CheckVisitor::WriteBackCheckBitmap()
+{
+       if (GetVolume()->IsReadOnly())
+               return B_OK;
+
+       // calculate the number of used blocks in the check bitmap
+       size_t size = _BitmapSize();
+       off_t usedBlocks = 0LL;
+
+       // TODO: update the allocation groups used blocks info
+       for (uint32 i = size >> 2; i-- > 0;) {
+               uint32 compare = 1;
+               // Count the number of bits set
+               for (int16 j = 0; j < 32; j++, compare <<= 1) {
+                       if ((compare & fCheckBitmap[i]) != 0)
+                               usedBlocks++;
+               }
+       }
+
+       Control().stats.freed = GetVolume()->UsedBlocks() - usedBlocks
+               + Control().stats.missing;
+       if (Control().stats.freed < 0)
+               Control().stats.freed = 0;
+
+       // Should we fix errors? Were there any errors we can fix?
+       if ((Control().flags & BFS_FIX_BITMAP_ERRORS) != 0
+               && (Control().stats.freed != 0 || Control().stats.missing != 
0)) {
+               // If so, write the check bitmap back over the original one,
+               // and use transactions here to play safe - we even use several
+               // transactions, so that we don't blow the maximum log size
+               // on large disks, since we don't need to make this atomic.
+#if 0
+               // prints the blocks that differ
+               off_t block = 0;
+               for (int32 i = 0; i < fNumGroups; i++) {
+                       AllocationBlock cached(fVolume);
+                       for (uint32 j = 0; j < fGroups[i].NumBlocks(); j++) {
+                               cached.SetTo(fGroups[i], j);
+                               for (uint32 k = 0; k < cached.NumBlockBits(); 
k++) {
+                                       if (cached.IsUsed(k) != 
_CheckBitmapIsUsedAt(block)) {
+                                               dprintf("differ block %lld 
(should be %d)\n", block,
+                                                       
_CheckBitmapIsUsedAt(block));
+                                       }
+                                       block++;
+                               }
+                       }
+               }
+#endif
+
+               GetVolume()->SuperBlock().used_blocks
+                       = HOST_ENDIAN_TO_BFS_INT64(usedBlocks);
+
+               size_t blockSize = GetVolume()->BlockSize();
+               off_t numBitmapBlocks = GetVolume()->NumBitmapBlocks();
+
+               for (uint32 i = 0; i < numBitmapBlocks; i += 512) {
+                       Transaction transaction(GetVolume(), 1 + i);
+
+                       uint32 blocksToWrite = 512;
+                       if (blocksToWrite + i > numBitmapBlocks)
+                               blocksToWrite = numBitmapBlocks - i;
+
+                       status_t status = transaction.WriteBlocks(1 + i,
+                               (uint8*)fCheckBitmap + i * blockSize, 
blocksToWrite);
+                       if (status < B_OK) {
+                               FATAL(("error writing bitmap: %s\n", 
strerror(status)));
+                               return status;
+                       }
+                       transaction.Done();
+               }
+       }
+
+       return B_OK;
+}
+
+
+status_t
+CheckVisitor::StartIndexPass()
+{
+       Control().pass = BFS_CHECK_PASS_INDEX;
+
+       status_t status = _PrepareIndices();
+       if (status != B_OK) {
+               Control().status = status;
+               return status;
+       }
+
+       Start(VISIT_REGULAR);
+
+       return B_OK;
+}
+
+
+status_t
+CheckVisitor::StopChecking()
+{
+       if (GetVolume()->IsReadOnly()) {
+               // We can't fix errors on this volume
+               Control().flags = 0;
+       }
+
+       if (Control().status != B_ENTRY_NOT_FOUND)
+               FATAL(("BlockAllocator::CheckNextNode() didn't run through\n"));
+
+       _FreeIndices();
+
+       recursive_lock_unlock(&GetVolume()->Allocator().Lock());
+       GetVolume()->GetJournal(0)->Unlock(NULL, true);
+       return B_OK;
+}
+
+
+status_t
+CheckVisitor::VisitDirectoryEntry(Inode* inode, Inode* parent,
+       const char* treeName)
+{
+       // check if the inode's name is the same as in the b+tree
+       if (Pass() == BFS_CHECK_PASS_BITMAP && inode->IsRegularNode()) {
+               RecursiveLocker locker(inode->SmallDataLock());
+               NodeGetter node(GetVolume(), inode);
+
+               const char* localName = inode->Name(node.Node());
+               if (localName == NULL || strcmp(localName, treeName)) {
+                       Control().errors |= BFS_NAMES_DONT_MATCH;
+                       FATAL(("Names differ: tree \"%s\", inode \"%s\"\n", 
treeName,
+                               localName));
+
+                       if ((Control().flags & BFS_FIX_NAME_MISMATCHES) != 0) {
+                               // Rename the inode
+                               Transaction transaction(GetVolume(), 
inode->BlockNumber());
+
+                               // Note, this may need extra blocks, but the 
inode will
+                               // only be checked afterwards, so that it won't 
be lost
+                               status_t status = inode->SetName(transaction, 
treeName);
+                               if (status == B_OK)
+                                       status = inode->WriteBack(transaction);
+                               if (status == B_OK)
+                                       status = transaction.Done();
+                               if (status != B_OK) {
+                                       Control().status = status;
+                                       return B_OK;
+                               }
+                       }
+               }
+       }
+
+       Control().mode = inode->Mode();
+
+       // Check for the correct mode of the node (if the mode of the
+       // file don't fit to its parent, there is a serious problem)
+       if (Pass() == BFS_CHECK_PASS_BITMAP
+               && (((parent->Mode() & S_ATTR_DIR) != 0
+                               && !inode->IsAttribute())
+                       || ((parent->Mode() & S_INDEX_DIR) != 0
+                               && !inode->IsIndex())
+                       || (is_directory(parent->Mode())
+                               && !inode->IsRegularNode()))) {
+               FATAL(("inode at %" B_PRIdOFF " is of wrong type: %o (parent "
+                       "%o at %" B_PRIdOFF ")!\n", inode->BlockNumber(),
+                       inode->Mode(), parent->Mode(), parent->BlockNumber()));
+
+               // if we are allowed to fix errors, we should remove the file
+               if ((Control().flags & BFS_REMOVE_WRONG_TYPES) != 0
+                       && (Control().flags & BFS_FIX_BITMAP_ERRORS) != 0) {
+                       Control().status = _RemoveInvalidNode(parent, NULL, 
inode,
+                               treeName);
+               } else
+                       Control().status = B_ERROR;
+
+               Control().errors |= BFS_WRONG_TYPE;
+       }
+       return B_OK;
+}
+
+
+status_t
+CheckVisitor::VisitInode(Inode* inode, const char* treeName)
+{
+       // set name
+       if (treeName == NULL) {
+               if (inode->GetName(Control().name) < B_OK) {
+                       if (inode->IsContainer())
+                               strcpy(Control().name, "(dir has no name)");
+                       else
+                               strcpy(Control().name, "(node has no name)");
+               }
+       } else
+               strcpy(Control().name, treeName);
+
+       status_t status;
+
+       switch (Pass()) {
+               case BFS_CHECK_PASS_BITMAP:
+               {
+                       status = _CheckInodeBlocks(inode, NULL);
+                       if (status != B_OK)
+                               return status;
+
+                       // Check the B+tree as well
+                       if (inode->IsContainer()) {
+                               bool repairErrors = (Control().flags & 
BFS_FIX_BPLUSTREES) != 0;
+                               bool errorsFound = false;
+
+                               status = inode->Tree()->Validate(repairErrors, 
errorsFound);
+
+                               if (errorsFound) {
+                                       Control().errors |= 
BFS_INVALID_BPLUSTREE;
+                                       if (inode->IsIndex() && treeName != 
NULL && repairErrors) {
+                                               // We completely rebuild 
corrupt indices
+                                               check_index* index = 
new(std::nothrow) check_index;
+                                               if (index == NULL)
+                                                       return B_NO_MEMORY;
+
+                                               strlcpy(index->name, treeName, 
sizeof(index->name));
+                                               index->run = inode->BlockRun();
+                                               Indices().Push(index);
+                                       }
+                               }
+                       }
+               }
+
+               case BFS_CHECK_PASS_INDEX:
+                       status = _AddInodeToIndex(inode);
+       }
+
+       Control().status = status;
+
+
+       return B_OK;
+}
+
+
+status_t
+CheckVisitor::OpenInodeFailed(status_t reason, ino_t id, Inode* parent,
+               char* treeName, TreeIterator* iterator)
+{
+       FATAL(("Could not open inode at %" B_PRIdOFF "\n", id));
+
+       if (treeName != NULL)
+               strlcpy(Control().name, treeName, B_FILE_NAME_LENGTH);
+       else
+               strcpy(Control().name, "(node has no name)");
+
+       Control().inode = id;
+       Control().errors = BFS_COULD_NOT_OPEN;
+
+       // remove inode from the tree if we can
+       if (parent != NULL && iterator != NULL
+               && (Control().flags & BFS_REMOVE_INVALID) != 0) {
+               Control().status = _RemoveInvalidNode(parent, iterator->Tree(), 
NULL,
+                       treeName);
+       } else
+               Control().status = B_ERROR;
+
+       return B_OK;
+}
+
+
+status_t
+CheckVisitor::OpenBPlusTreeFailed(Inode* inode)
+{
+       FATAL(("Could not open b+tree from inode at %" B_PRIdOFF "\n",
+               inode->ID()));
+       return B_OK;
+}
+
+
+status_t
+CheckVisitor::TreeIterationFailed(status_t reason, Inode* parent)
+{
+       // Iterating over the B+tree failed - we let the checkfs run
+       // fail completely, as we would delete all files we cannot
+       // access.
+       // TODO: maybe have a force parameter that actually does that.
+       // TODO: we also need to be able to repair broken B+trees!
+       return reason;
+}
+
+
+status_t
+CheckVisitor::_RemoveInvalidNode(Inode* parent, BPlusTree* tree,
+       Inode* inode, const char* name)
+{
+       // It's safe to start a transaction, because Inode::Remove()
+       // won't touch the block bitmap (which we hold the lock for)
+       // if we set the INODE_DONT_FREE_SPACE flag - since we fix
+       // the bitmap anyway.
+       Transaction transaction(GetVolume(), parent->BlockNumber());
+       status_t status;
+
+       if (inode != NULL) {
+               inode->Node().flags |= 
HOST_ENDIAN_TO_BFS_INT32(INODE_DONT_FREE_SPACE);
+
+               status = parent->Remove(transaction, name, NULL, false, true);
+       } else {
+               parent->WriteLockInTransaction(transaction);
+
+               // does the file even exist?
+               off_t id;
+               status = tree->Find((uint8*)name, (uint16)strlen(name), &id);
+               if (status == B_OK)
+                       status = tree->Remove(transaction, name, id);
+       }
+
+       if (status == B_OK) {
+               entry_cache_remove(GetVolume()->ID(), parent->ID(), name);
+               transaction.Done();
+       }
+
+       return status;
+}
+
+
+bool
+CheckVisitor::_ControlValid()
+{
+       if (Control().magic != BFS_IOCTL_CHECK_MAGIC) {
+               FATAL(("invalid check_control!\n"));
+               return false;
+       }
+
+       return true;
+}
+
+
+bool
+CheckVisitor::_CheckBitmapIsUsedAt(off_t block) const
+{
+       size_t size = _BitmapSize();
+       uint32 index = block / 32;      // 32bit resolution
+       if (index > size / 4)
+               return false;
+
+       return BFS_ENDIAN_TO_HOST_INT32(fCheckBitmap[index])
+               & (1UL << (block & 0x1f));
+}
+
+
+void
+CheckVisitor::_SetCheckBitmapAt(off_t block)
+{
+       size_t size = _BitmapSize();
+       uint32 index = block / 32;      // 32bit resolution
+       if (index > size / 4)
+               return;
+
+       fCheckBitmap[index] |= HOST_ENDIAN_TO_BFS_INT32(1UL << (block & 0x1f));
+}
+
+
+size_t
+CheckVisitor::_BitmapSize() const
+{
+       return GetVolume()->BlockSize() * GetVolume()->NumBitmapBlocks();
+}
+
+
+status_t
+CheckVisitor::_CheckInodeBlocks(Inode* inode, const char* name)
+{
+       status_t status = _CheckAllocated(inode->BlockRun(), "inode");
+       if (status != B_OK)
+               return status;
+
+       if (inode->IsSymLink() && (inode->Flags() & INODE_LONG_SYMLINK) == 0) {
+               // symlinks may not have a valid data stream
+               if (strlen(inode->Node().short_symlink) >= 
SHORT_SYMLINK_NAME_LENGTH)
+                       return B_BAD_DATA;
+
+               return B_OK;
+       }
+
+       data_stream* data = &inode->Node().data;
+
+       // check the direct range
+
+       if (data->max_direct_range) {
+               for (int32 i = 0; i < NUM_DIRECT_BLOCKS; i++) {
+                       if (data->direct[i].IsZero())
+                               break;
+
+                       status = _CheckAllocated(data->direct[i], "direct");
+                       if (status < B_OK)
+                               return status;
+
+                       Control().stats.direct_block_runs++;
+                       Control().stats.blocks_in_direct
+                               += data->direct[i].Length();
+               }
+       }
+
+       CachedBlock cached(GetVolume());
+
+       // check the indirect range
+
+       if (data->max_indirect_range) {
+               status = _CheckAllocated(data->indirect, "indirect");
+               if (status < B_OK)
+                       return status;
+
+               off_t block = GetVolume()->ToBlock(data->indirect);
+
+               for (int32 i = 0; i < data->indirect.Length(); i++) {
+                       block_run* runs = (block_run*)cached.SetTo(block + i);
+                       if (runs == NULL)
+                               RETURN_ERROR(B_IO_ERROR);
+
+                       int32 runsPerBlock = GetVolume()->BlockSize() / 
sizeof(block_run);
+                       int32 index = 0;
+                       for (; index < runsPerBlock; index++) {
+                               if (runs[index].IsZero())
+                                       break;
+
+                               status = _CheckAllocated(runs[index], 
"indirect->run");
+                               if (status < B_OK)
+                                       return status;
+
+                               Control().stats.indirect_block_runs++;
+                               Control().stats.blocks_in_indirect
+                                       += runs[index].Length();
+                       }
+                       Control().stats.indirect_array_blocks++;
+
+                       if (index < runsPerBlock)
+                               break;
+               }
+       }
+
+       // check the double indirect range
+
+       if (data->max_double_indirect_range) {
+               status = _CheckAllocated(data->double_indirect, "double 
indirect");
+               if (status != B_OK)
+                       return status;
+
+               int32 runsPerBlock = runs_per_block(GetVolume()->BlockSize());
+               int32 runsPerArray = runsPerBlock * 
data->double_indirect.Length();
+
+               CachedBlock cachedDirect(GetVolume());
+
+               for (int32 indirectIndex = 0; indirectIndex < runsPerArray;
+                               indirectIndex++) {
+                       // get the indirect array block
+                       block_run* array = (block_run*)cached.SetTo(
+                               GetVolume()->ToBlock(data->double_indirect)
+                                       + indirectIndex / runsPerBlock);
+                       if (array == NULL)
+                               return B_IO_ERROR;
+
+                       block_run indirect = array[indirectIndex % 
runsPerBlock];
+                       // are we finished yet?
+                       if (indirect.IsZero())
+                               return B_OK;
+
+                       status = _CheckAllocated(indirect, "double 
indirect->runs");
+                       if (status != B_OK)
+                               return status;
+
+                       int32 maxIndex = (indirect.Length() << 
GetVolume()->BlockShift())
+                               / sizeof(block_run);
+
+                       for (int32 index = 0; index < maxIndex; ) {
+                               block_run* runs = 
(block_run*)cachedDirect.SetTo(
+                                       GetVolume()->ToBlock(indirect) + index 
/ runsPerBlock);
+                               if (runs == NULL)
+                                       return B_IO_ERROR;
+
+                               do {
+                                       // are we finished yet?
+                                       if (runs[index % runsPerBlock].IsZero())
+                                               return B_OK;
+
+                                       status = _CheckAllocated(runs[index % 
runsPerBlock],
+                                               "double indirect->runs->run");
+                                       if (status != B_OK)
+                                               return status;
+
+                                       
Control().stats.double_indirect_block_runs++;
+                                       
Control().stats.blocks_in_double_indirect
+                                               += runs[index % 
runsPerBlock].Length();
+                               } while ((++index % runsPerArray) != 0);
+                       }
+
+                       Control().stats.double_indirect_array_blocks++;
+               }
+       }
+
+       return B_OK;
+}
+
+
+status_t
+CheckVisitor::_CheckAllocated(block_run run, const char* type)
+{
+       BlockAllocator& allocator = GetVolume()->Allocator();
+
+       // make sure the block run is valid
+       if (!allocator.IsValidBlockRun(run)) {
+               Control().errors |= BFS_INVALID_BLOCK_RUN;
+               return B_OK;
+       }
+
+       status_t status;
+
+       off_t start = GetVolume()->ToBlock(run);
+       off_t end = start + run.Length();
+
+       // check if the run is allocated in the block bitmap on disk
+       off_t block = start;
+
+       while (block < end) {
+               off_t firstMissing;
+               status = allocator.CheckBlocks(block, end - block, true, 
&firstMissing);
+               if (status == B_OK)
+                       break;
+               else if (status != B_BAD_DATA)
+                       return status;
+
+               off_t afterLastMissing;
+               status = allocator.CheckBlocks(firstMissing, end - 
firstMissing, false,
+                       &afterLastMissing);
+               if (status == B_OK)
+                       afterLastMissing = end;
+               else if (status != B_BAD_DATA)
+                       return status;
+
+               PRINT(("%s: block_run(%ld, %u, %u): blocks %Ld - %Ld are "
+                       "not allocated!\n", type, run.AllocationGroup(), 
run.Start(),
+                       run.Length(), firstMissing, afterLastMissing - 1));
+
+               block = afterLastMissing;
+       }
+
+       // set bits in check bitmap, while checking if they're already set
+       off_t firstSet = -1;
+
+       for (block = start; block < end; block++) {
+               if (_CheckBitmapIsUsedAt(block)) {
+                       if (firstSet == -1) {
+                               firstSet = block;
+                               Control().errors |= BFS_BLOCKS_ALREADY_SET;
+                       }
+                       Control().stats.already_set++;
+               } else {
+                       if (firstSet != -1) {
+                               FATAL(("%s: block_run(%d, %u, %u): blocks %" 
B_PRIdOFF
+                                       " - %" B_PRIdOFF " are already set!\n", 
type,
+                                       (int)run.AllocationGroup(), 
run.Start(), run.Length(),
+                                       firstSet, block - 1));
+                               firstSet = -1;
+                       }
+                       _SetCheckBitmapAt(block);
+               }
+       }
+
+       return B_OK;
+}
+
+
+status_t
+CheckVisitor::_PrepareIndices()
+{
+       int32 count = 0;
+
+       for (int32 i = 0; i < Indices().CountItems(); i++) {
+               check_index* index = Indices().Array()[i];
+               Vnode vnode(GetVolume(), index->run);
+               Inode* inode;
+               status_t status = vnode.Get(&inode);
+               if (status != B_OK) {
+                       FATAL(("check: Could not open index at %" B_PRIdOFF 
"\n",
+                               GetVolume()->ToBlock(index->run)));
+                       return status;
+               }
+
+               BPlusTree* tree = inode->Tree();
+               if (tree == NULL) {
+                       // TODO: We can't yet repair those
+                       continue;
+               }
+
+               status = tree->MakeEmpty();
+               if (status != B_OK)
+                       return status;
+
+               index->inode = inode;
+               vnode.Keep();
+               count++;
+       }
+
+       return count == 0 ? B_ENTRY_NOT_FOUND : B_OK;
+}
+
+
+void
+CheckVisitor::_FreeIndices()
+{
+       for (int32 i = 0; i < Indices().CountItems(); i++) {
+               check_index* index = Indices().Array()[i];
+               if (index->inode != NULL) {
+                       put_vnode(GetVolume()->FSVolume(),
+                               GetVolume()->ToVnode(index->inode->BlockRun()));
+               }
+               delete index;
+       }
+       Indices().MakeEmpty();
+}
+
+
+status_t
+CheckVisitor::_AddInodeToIndex(Inode* inode)
+{
+       Transaction transaction(GetVolume(), inode->BlockNumber());
+
+       for (int32 i = 0; i < Indices().CountItems(); i++) {
+               check_index* index = Indices().Array()[i];
+               if (index->inode == NULL)
+                       continue;
+
+               index->inode->WriteLockInTransaction(transaction);
+
+               BPlusTree* tree = index->inode->Tree();
+               if (tree == NULL)
+                       return B_ERROR;
+
+               status_t status = B_OK;
+
+               if (!strcmp(index->name, "name")) {
+                       if (inode->InNameIndex()) {
+                               char name[B_FILE_NAME_LENGTH];
+                               if (inode->GetName(name, B_FILE_NAME_LENGTH) != 
B_OK)
+                                       return B_ERROR;
+
+                               status = tree->Insert(transaction, name, 
inode->ID());
+                       }
+               } else if (!strcmp(index->name, "last_modified")) {
+                       if (inode->InLastModifiedIndex()) {
+                               status = tree->Insert(transaction, 
inode->OldLastModified(),
+                                       inode->ID());
+                       }
+               } else if (!strcmp(index->name, "size")) {
+                       if (inode->InSizeIndex())
+                               status = tree->Insert(transaction, 
inode->Size(), inode->ID());
+               } else {
+                       uint8 key[BPLUSTREE_MAX_KEY_LENGTH];
+                       size_t keyLength = BPLUSTREE_MAX_KEY_LENGTH;
+                       if (inode->ReadAttribute(index->name, B_ANY_TYPE, 0, 
key,
+                                       &keyLength) == B_OK) {
+                               status = tree->Insert(transaction, key, 
keyLength, inode->ID());
+                       }
+               }
+
+               if (status != B_OK)
+                       return status;
+       }
+
+       return transaction.Done();
+}
diff --git a/src/add-ons/kernel/file_systems/bfs/CheckVisitor.h 
b/src/add-ons/kernel/file_systems/bfs/CheckVisitor.h
new file mode 100644
index 0000000..b931d30
--- /dev/null
+++ b/src/add-ons/kernel/file_systems/bfs/CheckVisitor.h
@@ -0,0 +1,71 @@
+#ifndef CHECK_VISITOR_H
+#define CHECK_VISITOR_H
+
+
+#include "bfs_control.h"
+#include "FileSystemVisitor.h"
+
+
+class BlockAllocator;
+class BPlusTree;
+struct check_index;
+
+typedef Stack<check_index*> IndexStack;
+
+
+class CheckVisitor : public FileSystemVisitor {
+public:
+                                                               
CheckVisitor(Volume* volume);
+       virtual                                         ~CheckVisitor();
+
+                       check_control&          Control() { return control; }
+                       IndexStack&                     Indices() { return 
indices; }
+                       uint32                          Pass() { return 
control.pass; }
+
+                       status_t                        StartBitmapPass();
+                       status_t                        WriteBackCheckBitmap();
+                       status_t                        StartIndexPass();
+                       status_t                        StopChecking();
+
+                       bool                            HasIndicesToRebuild()
+                                                                       { 
return !indices.IsEmpty(); }
+
+       virtual status_t                        VisitDirectoryEntry(Inode* 
inode,
+                                                                       Inode* 
parent, const char* treeName);
+       virtual status_t                        VisitInode(Inode* inode, const 
char* treeName);
+
+       virtual status_t                        OpenInodeFailed(status_t 
reason, ino_t id,
+                                                                       Inode* 
parent, char* treeName,
+                                                                       
TreeIterator* iterator);
+       virtual status_t                        OpenBPlusTreeFailed(Inode* 
inode);
+       virtual status_t                        TreeIterationFailed(status_t 
reason,
+                                                                       Inode* 
parent);
+
+private:
+                       status_t                        
_RemoveInvalidNode(Inode* parent,
+                                                                       
BPlusTree* tree, Inode* inode,
+                                                                       const 
char* name);
+
+                       bool                            _ControlValid();
+                       bool                            
_CheckBitmapIsUsedAt(off_t block) const;
+                       void                            _SetCheckBitmapAt(off_t 
block);
+                       status_t                        
_CheckInodeBlocks(Inode* inode,
+                                                                       const 
char* name);
+                       status_t                        
_CheckAllocated(block_run run,
+                                                                       const 
char* type);
+
+                       size_t                          _BitmapSize() const;
+
+                       status_t                        _PrepareIndices();
+                       void                            _FreeIndices();
+                       status_t                        _AddInodeToIndex(Inode* 
inode);
+
+private:
+                       check_control           control;
+                       IndexStack                      indices;
+
+                       uint32*                         fCheckBitmap;
+};
+
+
+#endif // CHECK_VISITOR_H
diff --git a/src/add-ons/kernel/file_systems/bfs/FileSystemVisitor.cpp 
b/src/add-ons/kernel/file_systems/bfs/FileSystemVisitor.cpp
new file mode 100644
index 0000000..d09847d
--- /dev/null
+++ b/src/add-ons/kernel/file_systems/bfs/FileSystemVisitor.cpp
@@ -0,0 +1,282 @@
+#include "FileSystemVisitor.h"
+
+#include "BPlusTree.h"
+#include "Debug.h"
+#include "Inode.h"
+#include "Volume.h"
+
+
+FileSystemVisitor::FileSystemVisitor(Volume* volume)
+       :
+       fVolume(volume),
+       fIterator(NULL)
+{
+}
+
+
+FileSystemVisitor::~FileSystemVisitor()
+{
+       Stop();
+}
+
+
+//     #pragma mark - file system traversal
+
+
+/*! Visit the next inode.
+
+       \return
+       - \c B_ENTRY_NOT_FOUND : All nodes specified in Start() have been
+         traversed
+       - Any error code returned by the overridable functions
+*/
+status_t
+FileSystemVisitor::Next()
+{
+       status_t status;
+
+       while (true) {
+               const char* name = NULL;
+               Inode* inode;
+
+               if (fIterator == NULL) {
+                       if (!fStack.Pop(&fCurrent)) {
+                               // we're done
+                               return B_ENTRY_NOT_FOUND;
+                       }
+
+                       // open inode
+                       Vnode vnode(fVolume, fCurrent);
+                       status = vnode.Get(&inode);
+                       if (status != B_OK) {
+                               status = OpenInodeFailed(status, 
fVolume->ToBlock(fCurrent),
+                                       NULL, NULL, NULL);
+                               if (status == B_OK)
+                                       continue;
+                               return status;
+                       }
+
+                       if (inode->IsContainer()) {
+                               // open directory
+                               BPlusTree* tree = inode->Tree();
+                               if (tree == NULL) {
+                                       status = OpenBPlusTreeFailed(inode);
+                                       if (status == B_OK)
+                                               continue;
+                                       return status;
+                               }
+
+                               fParent = inode;
+
+                               // get iterator for the next directory
+                               fIterator = new(std::nothrow) 
TreeIterator(tree);
+                               if (fIterator == NULL)
+                                       RETURN_ERROR(B_NO_MEMORY);
+
+                               // the inode must stay locked in memory until 
the iterator
+                               // is freed
+                               vnode.Keep();
+                       }
+               } else {
+                       char treeName[B_FILE_NAME_LENGTH];
+                       uint16 length;
+                       ino_t id;
+
+                       status_t status = fIterator->GetNextEntry(treeName, 
&length,
+                               B_FILE_NAME_LENGTH, &id);
+                       if (status != B_OK) {
+                               // we no longer need this iterator
+                               delete fIterator;
+                               fIterator = NULL;
+
+                               // unlock the directory's inode from memory
+                               put_vnode(fVolume->FSVolume(),
+                                       fVolume->ToVnode(fCurrent));
+
+                               if (status == B_ENTRY_NOT_FOUND) {
+                                       // We iterated over all entries 
already, just go on
+                                       // to the next
+                                       continue;
+                               }
+
+                               // iterating over the B+tree failed
+                               //return TreeIterationFailed(status, fParent);
+                               status = TreeIterationFailed(status, fParent);
+                               if (status == B_OK)
+                                       continue;
+                               return status;
+                       }
+
+                       // ignore "." and ".." entries
+                       if (!strcmp(treeName, ".") || !strcmp(treeName, ".."))
+                               continue;
+
+                       Vnode vnode(fVolume, id);
+                       status = vnode.Get(&inode);
+                       if (status != B_OK) {
+                               status = OpenInodeFailed(status, id, fParent, 
treeName,
+                                       fIterator);
+                               if (status == B_OK)
+                                       continue;
+                               return status;
+                       }
+
+                       status = VisitDirectoryEntry(inode, fParent, treeName);
+                       if (status != B_OK)
+                               return status;
+
+                       if (inode->IsContainer() && !inode->IsIndex()) {
+                               // push the directory on the stack, it will be 
visited after
+                               // its children
+                               fStack.Push(inode->BlockRun());
+                               continue;
+                       }
+
+                       name = treeName;
+               }
+
+               // If the inode has an attribute directory that we want to 
visit,
+               // push it on the stack
+               if ((fFlags & VISIT_ATTRIBUTE_DIRECTORIES)
+                       && !inode->Attributes().IsZero()) {
+                       fStack.Push(inode->Attributes());
+               }
+
+               return VisitInode(inode, name);
+       }
+       // is never reached
+}
+
+
+/*! Start/restart traversal. \a flags is used to specify the nodes visited:
+       - \c VISIT_REGULAR :    Visit the nodes that are reachable by traversing
+                                                       the file system from 
the root directory.
+       - \c VISIT_INDICES :    Visit the index directory and indices
+       - \c VISIT_REMOVED :    Visit removed vnodes
+       - \c VISIT_ATTRIBUTE_DIRECTORIES :      Visit the attribute directory 
and
+                                                                               
attributes of files that have them.
+*/
+void
+FileSystemVisitor::Start(uint32 flags)
+{
+       // initialize state
+       Stop();
+       fCurrent.SetTo(0, 0, 0);
+       fParent = NULL;
+       fStack.MakeEmpty();
+
+       fFlags = flags;
+
+       if (fFlags & VISIT_REGULAR)
+               fStack.Push(fVolume->Root());
+
+       if (fFlags & VISIT_INDICES)
+               fStack.Push(fVolume->Indices());
+
+       if (fFlags & VISIT_REMOVED) {
+               // Put removed vnodes to the stack -- they are not reachable by
+               // traversing the file system anymore.
+               InodeList::Iterator iterator = 
fVolume->RemovedInodes().GetIterator();
+               while (Inode* inode = iterator.Next()) {
+                       fStack.Push(inode->BlockRun());
+               }
+       }
+}
+
+
+/*! Free aquired resources. This function is called from the destructor.
+*/
+void
+FileSystemVisitor::Stop()
+{
+       if (fIterator != NULL) {
+               delete fIterator;
+               fIterator = NULL;
+
+               // the current directory inode is still locked in memory
+               put_vnode(fVolume->FSVolume(), fVolume->ToVnode(fCurrent));
+       }
+}
+
+
+//     #pragma mark - overrideable actions
+
+
+/*! Called when an inode is opened while iterating through its parent
+       directory. Note that this function is not called for inodes which
+       don't have a proper parent directory, namely:
+       - The root directory
+       - The index directory
+       - Attribute directories
+       - Removed nodes
+
+       Return \c B_OK to continue traversing, any other error code to stop
+       traversal and propagate the error to the caller of Next(). In the
+       latter case, VisitInode() will never be called for this inode.
+*/
+status_t
+FileSystemVisitor::VisitDirectoryEntry(Inode* inode, Inode* parent,
+       const char* treeName)
+{
+       return B_OK;
+}
+
+
+/*! Called for every inode, some time after VisitDirectoryEntry(). For
+       directories, all subdirectories will be traversed before this
+       function is called.
+
+       Unless traversal has been stopped by an error handling function, all
+       calls to Next() end by invoking this function, and the return value
+       is passed along to the caller.
+*/
+status_t
+FileSystemVisitor::VisitInode(Inode* inode, const char* treeName)
+{
+       return B_OK;
+}
+
+
+/*! Called when opening an inode fails. If the failure happened while
+       iterating through a directory, \a parent, \a treeName and \a iterator
+       will be provided. Otherwise, they will be \c NULL.
+
+       \return
+       - \c B_OK : The traversal continues to the next inode
+       - Other : The traversal stops and the error code is propagated to the
+         caller of Next().
+*/
+status_t
+FileSystemVisitor::OpenInodeFailed(status_t reason, ino_t id, Inode* parent,
+       char* treeName, TreeIterator* iterator)
+{
+       return B_OK;
+}
+
+
+/*! Called when opening a b+tree fails.
+       
+       \return
+       - \c B_OK : The traversal continues to the next inode
+       - Other : The traversal stops and the error code is propagated to the
+         caller of Next().
+*/
+status_t
+FileSystemVisitor::OpenBPlusTreeFailed(Inode* inode)
+{
+       return B_OK;
+}
+
+
+/*! Called if we failed to get the next node while iterating a container.
+       
+       \return
+       - \c B_OK : The traversal continues to the next inode
+       - Other : The traversal stops and the error code is propagated to the
+         caller of Next().
+*/
+status_t
+FileSystemVisitor::TreeIterationFailed(status_t reason, Inode* parent)
+{
+       return B_OK;
+}
diff --git a/src/add-ons/kernel/file_systems/bfs/FileSystemVisitor.h 
b/src/add-ons/kernel/file_systems/bfs/FileSystemVisitor.h
new file mode 100644
index 0000000..cb953c1
--- /dev/null
+++ b/src/add-ons/kernel/file_systems/bfs/FileSystemVisitor.h
@@ -0,0 +1,60 @@
+#ifndef FILE_SYSTEM_VISITOR_H
+#define FILE_SYSTEM_VISITOR_H
+
+
+#include "system_dependencies.h"
+
+#include "bfs.h"
+
+
+class Inode;
+class TreeIterator;
+class Volume;
+
+
+enum visitor_flags {
+       VISIT_REGULAR                           = 0x0001,
+       VISIT_INDICES                           = 0x0002,
+       VISIT_REMOVED                           = 0x0004,
+       VISIT_ATTRIBUTE_DIRECTORIES     = 0x0008
+};
+
+
+class FileSystemVisitor {
+public:
+                                                               
FileSystemVisitor(Volume* volume);
+       virtual                                         ~FileSystemVisitor();
+
+                       Volume*                         GetVolume() const { 
return fVolume; }
+       
+                       // traversing the file system
+                       status_t                        Next();
+                       void                            Start(uint32 flags);
+                       void                            Stop();
+       
+
+       virtual status_t                        VisitDirectoryEntry(Inode* 
inode,
+                                                                       Inode* 
parent, const char* treeName);
+       virtual status_t                        VisitInode(Inode* inode, const 
char* treeName);
+
+       virtual status_t                        OpenInodeFailed(status_t 
reason, ino_t id,
+                                                                       Inode* 
parent, char* treeName,
+                                                                       
TreeIterator* iterator);
+       virtual status_t                        OpenBPlusTreeFailed(Inode* 
inode);
+       virtual status_t                        TreeIterationFailed(status_t 
reason,
+                                                                       Inode* 
parent);
+
+private:
+                       Volume*                         fVolume;
+
+                       // state needed when traversing
+                       block_run                       fCurrent;
+                       Inode*                          fParent;
+                       Stack<block_run>        fStack;
+                       TreeIterator*           fIterator;
+
+                       uint32                          fFlags;
+};
+
+
+#endif // FILE_SYSTEM_VISITOR_H
diff --git a/src/add-ons/kernel/file_systems/bfs/Jamfile 
b/src/add-ons/kernel/file_systems/bfs/Jamfile
index 5389516..c48b36e 100644
--- a/src/add-ons/kernel/file_systems/bfs/Jamfile
+++ b/src/add-ons/kernel/file_systems/bfs/Jamfile
@@ -34,7 +34,9 @@ KernelAddon bfs :
        BPlusTree.cpp
        kernel_cpp.cpp
        Attribute.cpp
+       CheckVisitor.cpp
        Debug.cpp
+       FileSystemVisitor.cpp
        Index.cpp
        Inode.cpp
        Journal.cpp
diff --git a/src/add-ons/kernel/file_systems/bfs/Volume.h 
b/src/add-ons/kernel/file_systems/bfs/Volume.h
index a9efe7b..f7f3c35 100644
--- a/src/add-ons/kernel/file_systems/bfs/Volume.h
+++ b/src/add-ons/kernel/file_systems/bfs/Volume.h
@@ -12,6 +12,7 @@
 #include "BlockAllocator.h"
 
 
+class CheckVisitor;
 class Journal;
 class Inode;
 class Query;
@@ -65,6 +66,9 @@ public:
                                                                { return 
fSuperBlock.UsedBlocks(); }
                        off_t                   FreeBlocks() const
                                                                { return 
NumBlocks() - UsedBlocks(); }
+                       off_t                   NumBitmapBlocks() const
+                                                               { return 
(NumBlocks() + fBlockSize * 8 - 1)
+                                                                       / 
(fBlockSize * 8); }
 
                        uint32                  DeviceBlockSize() const { 
return fDeviceBlockSize; }
                        uint32                  BlockSize() const { return 
fBlockSize; }
@@ -115,6 +119,7 @@ public:
                                                                { 
fCheckingThread = thread; }
                        bool                    IsCheckingThread() const
                                                                { return 
find_thread(NULL) == fCheckingThread; }
+                       CheckVisitor*&  GetCheckVisitor() { return 
fCheckVisitor; }
 
                        // cache access
                        status_t                WriteSuperBlock();
@@ -170,6 +175,7 @@ protected:
 
                        void*                   fBlockCache;
                        thread_id               fCheckingThread;
+                       CheckVisitor*   fCheckVisitor;
 
                        InodeList               fRemovedInodes;
 };
diff --git a/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp 
b/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp
index ecc18cb..c44b04e 100644
--- a/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp
+++ b/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp
@@ -7,6 +7,7 @@
 //!    file system interface to Haiku's vnode layer
 
 
+#include "CheckVisitor.h"
 #include "Debug.h"
 #include "Volume.h"
 #include "Inode.h"
@@ -772,12 +773,15 @@ bfs_ioctl(fs_volume* _volume, fs_vnode* _node, void* 
_cookie, uint32 cmd,
                case BFS_IOCTL_START_CHECKING:
                {
                        // start checking
-                       BlockAllocator& allocator = volume->Allocator();
-                       check_control control;
-                       if (user_memcpy(&control, buffer, 
sizeof(check_control)) != B_OK)
+                       CheckVisitor*& checker = volume->GetCheckVisitor();
+                       checker = new CheckVisitor(volume);
+
+                       if (user_memcpy(&checker->Control(), buffer,
+                                       sizeof(check_control)) != B_OK) {
                                return B_BAD_ADDRESS;
+                       }
 
-                       status_t status = allocator.StartChecking(&control);
+                       status_t status = checker->StartBitmapPass();
                        if (status == B_OK) {
                                file_cookie* cookie = (file_cookie*)_cookie;
                                cookie->open_mode |= BFS_OPEN_MODE_CHECKING;
@@ -788,28 +792,51 @@ bfs_ioctl(fs_volume* _volume, fs_vnode* _node, void* 
_cookie, uint32 cmd,
                case BFS_IOCTL_STOP_CHECKING:
                {
                        // stop checking
-                       BlockAllocator& allocator = volume->Allocator();
-                       check_control control;
+                       CheckVisitor*& checker = volume->GetCheckVisitor();
+                       status_t status = checker->StopChecking();
 
-                       status_t status = allocator.StopChecking(&control);
                        if (status == B_OK) {
                                file_cookie* cookie = (file_cookie*)_cookie;
                                cookie->open_mode &= ~BFS_OPEN_MODE_CHECKING;
+
+                               status = user_memcpy(buffer, 
&checker->Control(),
+                                       sizeof(check_control));
                        }
-                       if (status == B_OK)
-                               status = user_memcpy(buffer, &control, 
sizeof(check_control));
+
+                       delete checker;
+                       checker = NULL;
+                       volume->SetCheckingThread(-1);
 
                        return status;
                }
                case BFS_IOCTL_CHECK_NEXT_NODE:
                {
                        // check next
-                       BlockAllocator& allocator = volume->Allocator();
-                       check_control control;
+                       CheckVisitor*& checker = volume->GetCheckVisitor();
+                       volume->SetCheckingThread(find_thread(NULL));
 
-                       status_t status = allocator.CheckNextNode(&control);
-                       if (status == B_OK)
-                               status = user_memcpy(buffer, &control, 
sizeof(check_control));
+                       checker->Control().errors = 0;
+
+                       status_t status = checker->Next();
+                       if (status == B_ENTRY_NOT_FOUND) {
+                               checker->Control().status = B_ENTRY_NOT_FOUND;
+                                       // tells StopChecking() that we 
finished the pass
+
+                               if (checker->Pass() == BFS_CHECK_PASS_BITMAP) {
+                                       checker->WriteBackCheckBitmap();
+
+                                       if (status == B_OK && 
checker->HasIndicesToRebuild()) {
+                                               // enter rebuild index pass
+                                               checker->StartIndexPass();
+                                               status = checker->Next();
+                                       }
+                               }
+                       }
+                       
+                       if (status == B_OK) {
+                               status = user_memcpy(buffer, 
&checker->Control(),
+                                       sizeof(check_control));
+                       }
 
                        return status;
                }
@@ -1612,7 +1639,8 @@ bfs_free_cookie(fs_volume* _volume, fs_vnode* _node, 
void* _cookie)
        if ((cookie->open_mode & BFS_OPEN_MODE_CHECKING) != 0) {
                // "chkbfs" exited abnormally, so we have to stop it here...
                FATAL(("check process was aborted!\n"));
-               volume->Allocator().StopChecking(NULL);
+               volume->GetCheckVisitor()->StopChecking();
+               delete volume->GetCheckVisitor();
        }
 
        if ((cookie->open_mode & O_NOCACHE) != 0 && inode->FileCache() != NULL)
diff --git a/src/tools/bfs_shell/Jamfile b/src/tools/bfs_shell/Jamfile
index 264275e..1a297e9 100644
--- a/src/tools/bfs_shell/Jamfile
+++ b/src/tools/bfs_shell/Jamfile
@@ -49,7 +49,9 @@ local bfsSource =
        BlockAllocator.cpp
        BPlusTree.cpp
        Attribute.cpp
+       CheckVisitor.cpp
        Debug.cpp
+       FileSystemVisitor.cpp
        Index.cpp
        Inode.cpp
        Journal.cpp


Other related posts: