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:
parent
8497a2cc28
commit
aa7750382a
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
|
762
src/add-ons/kernel/file_systems/bfs/CheckVisitor.cpp
Normal file
762
src/add-ons/kernel/file_systems/bfs/CheckVisitor.cpp
Normal 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();
|
||||
}
|
76
src/add-ons/kernel/file_systems/bfs/CheckVisitor.h
Normal file
76
src/add-ons/kernel/file_systems/bfs/CheckVisitor.h
Normal 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
|
329
src/add-ons/kernel/file_systems/bfs/FileSystemVisitor.cpp
Normal file
329
src/add-ons/kernel/file_systems/bfs/FileSystemVisitor.cpp
Normal 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;
|
||||
}
|
65
src/add-ons/kernel/file_systems/bfs/FileSystemVisitor.h
Normal file
65
src/add-ons/kernel/file_systems/bfs/FileSystemVisitor.h
Normal 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
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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)
|
||||
|
@ -45,7 +45,9 @@ local bfsSource =
|
||||
BlockAllocator.cpp
|
||||
BPlusTree.cpp
|
||||
Attribute.cpp
|
||||
CheckVisitor.cpp
|
||||
Debug.cpp
|
||||
FileSystemVisitor.cpp
|
||||
Index.cpp
|
||||
Inode.cpp
|
||||
Journal.cpp
|
||||
|
Loading…
Reference in New Issue
Block a user