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