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 <[email protected]>
This commit is contained in:
committed by
waddlesplash
parent
8497a2cc28
commit
aa7750382a
File diff suppressed because it is too large
Load Diff
@@ -10,14 +10,11 @@
|
|||||||
|
|
||||||
|
|
||||||
class AllocationGroup;
|
class AllocationGroup;
|
||||||
class BPlusTree;
|
|
||||||
class Inode;
|
class Inode;
|
||||||
class Transaction;
|
class Transaction;
|
||||||
class Volume;
|
class Volume;
|
||||||
struct disk_super_block;
|
struct disk_super_block;
|
||||||
struct block_run;
|
struct block_run;
|
||||||
struct check_control;
|
|
||||||
struct check_cookie;
|
|
||||||
|
|
||||||
|
|
||||||
//#define DEBUG_ALLOCATION_GROUPS
|
//#define DEBUG_ALLOCATION_GROUPS
|
||||||
@@ -49,18 +46,15 @@ public:
|
|||||||
status_t Trim(uint64 offset, uint64 size,
|
status_t Trim(uint64 offset, uint64 size,
|
||||||
uint64& trimmedSize);
|
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,
|
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,
|
status_t CheckBlockRun(block_run run,
|
||||||
const char* type = NULL,
|
const char* type = NULL,
|
||||||
bool allocated = true);
|
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
|
#ifdef BFS_DEBUGGER_COMMANDS
|
||||||
void Dump(int32 index);
|
void Dump(int32 index);
|
||||||
@@ -70,20 +64,9 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
status_t _RemoveInvalidNode(Inode* parent, BPlusTree* tree,
|
|
||||||
Inode* inode, const char* name);
|
|
||||||
#ifdef DEBUG_ALLOCATION_GROUPS
|
#ifdef DEBUG_ALLOCATION_GROUPS
|
||||||
void _CheckGroup(int32 group) const;
|
void _CheckGroup(int32 group) const;
|
||||||
#endif
|
#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,
|
status_t _AddTrim(fs_trim_data& trimData, uint32 maxRanges,
|
||||||
uint64 offset, uint64 size);
|
uint64 offset, uint64 size);
|
||||||
status_t _TrimNext(fs_trim_data& trimData, uint32 maxRanges,
|
status_t _TrimNext(fs_trim_data& trimData, uint32 maxRanges,
|
||||||
@@ -99,9 +82,6 @@ private:
|
|||||||
int32 fNumGroups;
|
int32 fNumGroups;
|
||||||
uint32 fBlocksPerGroup;
|
uint32 fBlocksPerGroup;
|
||||||
uint32 fNumBlocks;
|
uint32 fNumBlocks;
|
||||||
|
|
||||||
uint32* fCheckBitmap;
|
|
||||||
check_cookie* fCheckCookie;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef BFS_DEBUGGER_COMMANDS
|
#ifdef BFS_DEBUGGER_COMMANDS
|
||||||
|
|||||||
@@ -0,0 +1,762 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2012, Axel Dörfler, [email protected].
|
||||||
|
* Copyright 2012, Andreas Henriksson, [email protected]
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2012, Axel Dörfler, [email protected].
|
||||||
|
* Copyright 2012, Andreas Henriksson, [email protected]
|
||||||
|
* 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
|
||||||
@@ -0,0 +1,329 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2012, Axel Dörfler, [email protected].
|
||||||
|
* Copyright 2012, Andreas Henriksson, [email protected]
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2012, Axel Dörfler, [email protected].
|
||||||
|
* Copyright 2012, Andreas Henriksson, [email protected]
|
||||||
|
* 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
|
||||||
@@ -27,7 +27,9 @@ KernelAddon bfs :
|
|||||||
BPlusTree.cpp
|
BPlusTree.cpp
|
||||||
kernel_cpp.cpp
|
kernel_cpp.cpp
|
||||||
Attribute.cpp
|
Attribute.cpp
|
||||||
|
CheckVisitor.cpp
|
||||||
Debug.cpp
|
Debug.cpp
|
||||||
|
FileSystemVisitor.cpp
|
||||||
Index.cpp
|
Index.cpp
|
||||||
Inode.cpp
|
Inode.cpp
|
||||||
Journal.cpp
|
Journal.cpp
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
|
|
||||||
#include "Attribute.h"
|
#include "Attribute.h"
|
||||||
|
#include "CheckVisitor.h"
|
||||||
#include "Debug.h"
|
#include "Debug.h"
|
||||||
#include "Inode.h"
|
#include "Inode.h"
|
||||||
#include "Journal.h"
|
#include "Journal.h"
|
||||||
@@ -282,7 +283,8 @@ Volume::Volume(fs_volume* volume)
|
|||||||
fIndicesNode(NULL),
|
fIndicesNode(NULL),
|
||||||
fDirtyCachedBlocks(0),
|
fDirtyCachedBlocks(0),
|
||||||
fFlags(0),
|
fFlags(0),
|
||||||
fCheckingThread(-1)
|
fCheckingThread(-1),
|
||||||
|
fCheckVisitor(NULL)
|
||||||
{
|
{
|
||||||
mutex_init(&fLock, "bfs volume");
|
mutex_init(&fLock, "bfs volume");
|
||||||
mutex_init(&fQueryLock, "bfs queries");
|
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
|
// #pragma mark - Disk scanning and initialization
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "BlockAllocator.h"
|
#include "BlockAllocator.h"
|
||||||
|
|
||||||
|
|
||||||
|
class CheckVisitor;
|
||||||
class Journal;
|
class Journal;
|
||||||
class Inode;
|
class Inode;
|
||||||
class Query;
|
class Query;
|
||||||
@@ -65,6 +66,9 @@ public:
|
|||||||
{ return fSuperBlock.UsedBlocks(); }
|
{ return fSuperBlock.UsedBlocks(); }
|
||||||
off_t FreeBlocks() const
|
off_t FreeBlocks() const
|
||||||
{ return NumBlocks() - UsedBlocks(); }
|
{ return NumBlocks() - UsedBlocks(); }
|
||||||
|
off_t NumBitmapBlocks() const
|
||||||
|
{ return (NumBlocks() + fBlockSize * 8 - 1)
|
||||||
|
/ (fBlockSize * 8); }
|
||||||
|
|
||||||
uint32 DeviceBlockSize() const { return fDeviceBlockSize; }
|
uint32 DeviceBlockSize() const { return fDeviceBlockSize; }
|
||||||
uint32 BlockSize() const { return fBlockSize; }
|
uint32 BlockSize() const { return fBlockSize; }
|
||||||
@@ -114,6 +118,9 @@ public:
|
|||||||
{ fCheckingThread = thread; }
|
{ fCheckingThread = thread; }
|
||||||
bool IsCheckingThread() const
|
bool IsCheckingThread() const
|
||||||
{ return find_thread(NULL) == fCheckingThread; }
|
{ return find_thread(NULL) == fCheckingThread; }
|
||||||
|
status_t CreateCheckVisitor();
|
||||||
|
void DeleteCheckVisitor();
|
||||||
|
::CheckVisitor* CheckVisitor() { return fCheckVisitor; }
|
||||||
|
|
||||||
// cache access
|
// cache access
|
||||||
status_t WriteSuperBlock();
|
status_t WriteSuperBlock();
|
||||||
@@ -172,6 +179,7 @@ protected:
|
|||||||
|
|
||||||
void* fBlockCache;
|
void* fBlockCache;
|
||||||
thread_id fCheckingThread;
|
thread_id fCheckingThread;
|
||||||
|
::CheckVisitor* fCheckVisitor;
|
||||||
|
|
||||||
InodeList fRemovedInodes;
|
InodeList fRemovedInodes;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
//! file system interface to Haiku's vnode layer
|
//! file system interface to Haiku's vnode layer
|
||||||
|
|
||||||
|
|
||||||
|
#include "CheckVisitor.h"
|
||||||
#include "Debug.h"
|
#include "Debug.h"
|
||||||
#include "Volume.h"
|
#include "Volume.h"
|
||||||
#include "Inode.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:
|
case BFS_IOCTL_START_CHECKING:
|
||||||
{
|
{
|
||||||
// start checking
|
// start checking
|
||||||
BlockAllocator& allocator = volume->Allocator();
|
status_t status = volume->CreateCheckVisitor();
|
||||||
check_control control;
|
if (status != B_OK)
|
||||||
if (user_memcpy(&control, buffer, sizeof(check_control)) != B_OK)
|
return status;
|
||||||
return B_BAD_ADDRESS;
|
|
||||||
|
|
||||||
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) {
|
if (status == B_OK) {
|
||||||
file_cookie* cookie = (file_cookie*)_cookie;
|
file_cookie* cookie = (file_cookie*)_cookie;
|
||||||
cookie->open_mode |= BFS_OPEN_MODE_CHECKING;
|
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:
|
case BFS_IOCTL_STOP_CHECKING:
|
||||||
{
|
{
|
||||||
// stop checking
|
// stop checking
|
||||||
BlockAllocator& allocator = volume->Allocator();
|
CheckVisitor* checker = volume->CheckVisitor();
|
||||||
check_control control;
|
if (checker == NULL)
|
||||||
|
return B_NO_INIT;
|
||||||
|
|
||||||
|
status_t status = checker->StopChecking();
|
||||||
|
|
||||||
status_t status = allocator.StopChecking(&control);
|
|
||||||
if (status == B_OK) {
|
if (status == B_OK) {
|
||||||
file_cookie* cookie = (file_cookie*)_cookie;
|
file_cookie* cookie = (file_cookie*)_cookie;
|
||||||
cookie->open_mode &= ~BFS_OPEN_MODE_CHECKING;
|
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;
|
return status;
|
||||||
}
|
}
|
||||||
case BFS_IOCTL_CHECK_NEXT_NODE:
|
case BFS_IOCTL_CHECK_NEXT_NODE:
|
||||||
{
|
{
|
||||||
// check next
|
// check next
|
||||||
BlockAllocator& allocator = volume->Allocator();
|
CheckVisitor* checker = volume->CheckVisitor();
|
||||||
check_control control;
|
if (checker == NULL)
|
||||||
|
return B_NO_INIT;
|
||||||
|
|
||||||
status_t status = allocator.CheckNextNode(&control);
|
volume->SetCheckingThread(find_thread(NULL));
|
||||||
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) {
|
||||||
|
if (checker->WriteBackCheckBitmap() == B_OK)
|
||||||
|
status = checker->StartIndexPass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status == B_OK) {
|
||||||
|
status = user_memcpy(buffer, &checker->Control(),
|
||||||
|
sizeof(check_control));
|
||||||
|
}
|
||||||
|
|
||||||
return status;
|
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) {
|
if ((cookie->open_mode & BFS_OPEN_MODE_CHECKING) != 0) {
|
||||||
// "chkbfs" exited abnormally, so we have to stop it here...
|
// "chkbfs" exited abnormally, so we have to stop it here...
|
||||||
FATAL(("check process was aborted!\n"));
|
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)
|
if ((cookie->open_mode & O_NOCACHE) != 0 && inode->FileCache() != NULL)
|
||||||
|
|||||||
@@ -45,7 +45,9 @@ local bfsSource =
|
|||||||
BlockAllocator.cpp
|
BlockAllocator.cpp
|
||||||
BPlusTree.cpp
|
BPlusTree.cpp
|
||||||
Attribute.cpp
|
Attribute.cpp
|
||||||
|
CheckVisitor.cpp
|
||||||
Debug.cpp
|
Debug.cpp
|
||||||
|
FileSystemVisitor.cpp
|
||||||
Index.cpp
|
Index.cpp
|
||||||
Inode.cpp
|
Inode.cpp
|
||||||
Journal.cpp
|
Journal.cpp
|
||||||
|
|||||||
Reference in New Issue
Block a user