bfs: split checkfs code into FileSystemVisitor and CheckVisitor

Another FileSystemVisitor implementation will be used for resizing

Change-Id: I2d5f47dcbefb4c76834487b194a99c6f5ae7de50
Reviewed-on: https://review.haiku-os.org/c/923
Reviewed-by: Jérôme Duval <jerome.duval@gmail.com>
This commit is contained in:
ahenriksson 2012-07-05 10:40:08 +02:00 committed by waddlesplash
parent 8497a2cc28
commit aa7750382a
11 changed files with 1353 additions and 964 deletions

File diff suppressed because it is too large Load Diff

View File

@ -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
@ -49,18 +46,15 @@ public:
status_t Trim(uint64 offset, uint64 size,
uint64& trimmedSize);
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);
@ -70,20 +64,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();
status_t _AddTrim(fs_trim_data& trimData, uint32 maxRanges,
uint64 offset, uint64 size);
status_t _TrimNext(fs_trim_data& trimData, uint32 maxRanges,
@ -99,9 +82,6 @@ private:
int32 fNumGroups;
uint32 fBlocksPerGroup;
uint32 fNumBlocks;
uint32* fCheckBitmap;
check_cookie* fCheckCookie;
};
#ifdef BFS_DEBUGGER_COMMANDS

View File

@ -0,0 +1,762 @@
/*
* Copyright 2002-2012, Axel Dörfler, axeld@pinc-software.de.
* Copyright 2012, Andreas Henriksson, sausageboy@gmail.com
* This file may be used under the terms of the MIT License.
*/
//! File system error checking
#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()
{
// if we don't have indices to rebuild, this pass is done
if (indices.IsEmpty())
return B_ENTRY_NOT_FOUND;
Control().pass = BFS_CHECK_PASS_INDEX;
status_t status = _PrepareIndices();
if (status != B_OK) {
Control().status = status;
return status;
}
Start(VISIT_REGULAR);
return Next();
}
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(("CheckVisitor 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)
{
Control().inode = inode->ID();
Control().mode = inode->Mode();
if (Pass() != BFS_CHECK_PASS_BITMAP)
return B_OK;
// check if the inode's name is the same as in the b+tree
if (inode->IsRegularNode()) {
RecursiveLocker locker(inode->SmallDataLock());
NodeGetter node(GetVolume(), inode);
if (node.Node() == NULL) {
Control().errors |= BFS_COULD_NOT_OPEN;
Control().status = B_IO_ERROR;
return B_OK;
}
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;
}
}
}
}
// 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 (((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)
{
Control().inode = inode->ID();
Control().mode = inode->Mode();
// (we might have set these in VisitDirectoryEntry already)
// 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 = B_OK;
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);
}
}
}
break;
}
case BFS_CHECK_PASS_INDEX:
status = _AddInodeToIndex(inode);
break;
}
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
= ((uint32)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 % runsPerBlock) != 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));
Control().stats.missing += afterLastMissing - firstMissing;
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[MAX_INDEX_KEY_LENGTH];
size_t keyLength = sizeof(key);
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();
}

View File

@ -0,0 +1,76 @@
/*
* Copyright 2002-2012, Axel Dörfler, axeld@pinc-software.de.
* Copyright 2012, Andreas Henriksson, sausageboy@gmail.com
* This file may be used under the terms of the MIT License.
*/
#ifndef CHECK_VISITOR_H
#define CHECK_VISITOR_H
#include "system_dependencies.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();
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

View File

@ -0,0 +1,329 @@
/*
* Copyright 2002-2012, Axel Dörfler, axeld@pinc-software.de.
* Copyright 2012, Andreas Henriksson, sausageboy@gmail.com
* This file may be used under the terms of the MIT License.
*/
//! Traversing the file system
#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;
Vnode vnode;
if (fIterator == NULL) {
if (!fStack.Pop(&fCurrent)) {
// we're done
return B_ENTRY_NOT_FOUND;
}
// open inode
vnode.SetTo(fVolume, fCurrent);
status = vnode.Get(&inode);
// release the reference we acquired when pushing the node to
// the stack
put_vnode(fVolume->FSVolume(), fVolume->ToVnode(fCurrent));
if (status != B_OK) {
if (inode->IsDeleted())
continue;
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.SetTo(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());
// the inode may be deleted behind our back, we keep a
// reference so we can check for this
vnode.Keep();
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());
}
bool visitingCurrentDirectory = inode->BlockRun() == fCurrent;
status = VisitInode(inode, name);
// the inode id is allowed to change in the VisitInode() call, so we
// may need to change the inode reference
if (visitingCurrentDirectory)
fCurrent = inode->BlockRun();
return status;
}
// 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) {
Vnode vnode(fVolume, fVolume->Root());
vnode.Keep();
fStack.Push(fVolume->Root());
}
if (fFlags & VISIT_INDICES) {
Vnode vnode(fVolume, fVolume->Indices());
vnode.Keep();
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()) {
Vnode vnode(fVolume, inode->ID());
vnode.Keep();
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));
}
// release the references to the vnodes on the stack
block_run run;
while (fStack.Pop(&run))
put_vnode(fVolume->FSVolume(), fVolume->ToVnode(run));
}
// #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.
This call may change the inode ID.
*/
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;
}

View File

@ -0,0 +1,65 @@
/*
* Copyright 2002-2012, Axel Dörfler, axeld@pinc-software.de.
* Copyright 2012, Andreas Henriksson, sausageboy@gmail.com
* This file may be used under the terms of the MIT License.
*/
#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

View File

@ -27,7 +27,9 @@ KernelAddon bfs :
BPlusTree.cpp
kernel_cpp.cpp
Attribute.cpp
CheckVisitor.cpp
Debug.cpp
FileSystemVisitor.cpp
Index.cpp
Inode.cpp
Journal.cpp

View File

@ -8,6 +8,7 @@
#include "Attribute.h"
#include "CheckVisitor.h"
#include "Debug.h"
#include "Inode.h"
#include "Journal.h"
@ -282,7 +283,8 @@ Volume::Volume(fs_volume* volume)
fIndicesNode(NULL),
fDirtyCachedBlocks(0),
fFlags(0),
fCheckingThread(-1)
fCheckingThread(-1),
fCheckVisitor(NULL)
{
mutex_init(&fLock, "bfs volume");
mutex_init(&fQueryLock, "bfs queries");
@ -622,6 +624,28 @@ Volume::RemoveQuery(Query* query)
}
status_t
Volume::CreateCheckVisitor()
{
if (fCheckVisitor != NULL)
return B_BUSY;
fCheckVisitor = new(std::nothrow) ::CheckVisitor(this);
if (fCheckVisitor == NULL)
return B_NO_MEMORY;
return B_OK;
}
void
Volume::DeleteCheckVisitor()
{
delete fCheckVisitor;
fCheckVisitor = NULL;
}
// #pragma mark - Disk scanning and initialization

View File

@ -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; }
@ -114,6 +118,9 @@ public:
{ fCheckingThread = thread; }
bool IsCheckingThread() const
{ return find_thread(NULL) == fCheckingThread; }
status_t CreateCheckVisitor();
void DeleteCheckVisitor();
::CheckVisitor* CheckVisitor() { return fCheckVisitor; }
// cache access
status_t WriteSuperBlock();
@ -172,6 +179,7 @@ protected:
void* fBlockCache;
thread_id fCheckingThread;
::CheckVisitor* fCheckVisitor;
InodeList fRemovedInodes;
};

View File

@ -7,6 +7,7 @@
//! file system interface to Haiku's vnode layer
#include "CheckVisitor.h"
#include "Debug.h"
#include "Volume.h"
#include "Inode.h"
@ -672,12 +673,18 @@ 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)
return B_BAD_ADDRESS;
status_t status = volume->CreateCheckVisitor();
if (status != B_OK)
return status;
status_t status = allocator.StartChecking(&control);
CheckVisitor* checker = volume->CheckVisitor();
if (user_memcpy(&checker->Control(), buffer,
sizeof(check_control)) != B_OK) {
return B_BAD_ADDRESS;
}
status = checker->StartBitmapPass();
if (status == B_OK) {
file_cookie* cookie = (file_cookie*)_cookie;
cookie->open_mode |= BFS_OPEN_MODE_CHECKING;
@ -688,28 +695,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->CheckVisitor();
if (checker == NULL)
return B_NO_INIT;
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));
volume->DeleteCheckVisitor();
volume->SetCheckingThread(-1);
return status;
}
case BFS_IOCTL_CHECK_NEXT_NODE:
{
// check next
BlockAllocator& allocator = volume->Allocator();
check_control control;
CheckVisitor* checker = volume->CheckVisitor();
if (checker == NULL)
return B_NO_INIT;
status_t status = allocator.CheckNextNode(&control);
if (status == B_OK)
status = user_memcpy(buffer, &control, sizeof(check_control));
volume->SetCheckingThread(find_thread(NULL));
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) {
if (checker->WriteBackCheckBitmap() == B_OK)
status = checker->StartIndexPass();
}
}
if (status == B_OK) {
status = user_memcpy(buffer, &checker->Control(),
sizeof(check_control));
}
return status;
}
@ -1508,7 +1538,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->CheckVisitor()->StopChecking();
volume->DeleteCheckVisitor();
}
if ((cookie->open_mode & O_NOCACHE) != 0 && inode->FileCache() != NULL)

View File

@ -45,7 +45,9 @@ local bfsSource =
BlockAllocator.cpp
BPlusTree.cpp
Attribute.cpp
CheckVisitor.cpp
Debug.cpp
FileSystemVisitor.cpp
Index.cpp
Inode.cpp
Journal.cpp