* 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.
*/
#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();
}
\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;
char treeName[B_FILE_NAME_LENGTH];
Inode* inode;
Vnode vnode;
if (fIterator == NULL) {
if (!fStack.Pop(&fCurrent)) {
return B_ENTRY_NOT_FOUND;
}
vnode.SetTo(fVolume, fCurrent);
status = vnode.Get(&inode);
put_vnode(fVolume->FSVolume(), fVolume->ToVnode(fCurrent));
if (status != B_OK) {
if (inode != NULL && inode->IsDeleted())
continue;
status = OpenInodeFailed(status, fVolume->ToBlock(fCurrent),
NULL, NULL, NULL);
if (status == B_OK)
continue;
return status;
}
if (inode->IsContainer()) {
BPlusTree* tree = inode->Tree();
if (tree == NULL) {
status = OpenBPlusTreeFailed(inode);
if (status == B_OK)
continue;
return status;
}
fParent = inode;
fIterator = new(std::nothrow) TreeIterator(tree);
if (fIterator == NULL)
RETURN_ERROR(B_NO_MEMORY);
vnode.Keep();
}
} else {
uint16 length;
ino_t id;
status_t status = fIterator->GetNextEntry(treeName, &length,
B_FILE_NAME_LENGTH, &id);
if (status != B_OK) {
delete fIterator;
fIterator = NULL;
put_vnode(fVolume->FSVolume(),
fVolume->ToVnode(fCurrent));
if (status == B_ENTRY_NOT_FOUND) {
continue;
}
status = TreeIterationFailed(status, fParent);
if (status == B_OK)
continue;
return status;
}
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()) {
fStack.Push(inode->BlockRun());
vnode.Keep();
continue;
}
name = treeName;
}
if ((fFlags & VISIT_ATTRIBUTE_DIRECTORIES)
&& !inode->Attributes().IsZero()) {
fStack.Push(inode->Attributes());
Vnode attrNode(fVolume, inode->Attributes());
attrNode.Keep();
}
bool visitingCurrentDirectory = inode->BlockRun() == fCurrent;
status = VisitInode(inode, name);
if (visitingCurrentDirectory)
fCurrent = inode->BlockRun();
return status;
}
}
- \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)
{
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) {
InodeList::Iterator iterator = fVolume->RemovedInodes().GetIterator();
while (Inode* inode = iterator.Next()) {
Vnode vnode(fVolume, inode->ID());
vnode.Keep();
fStack.Push(inode->BlockRun());
}
}
}
*/
void
FileSystemVisitor::Stop()
{
if (fIterator != NULL) {
delete fIterator;
fIterator = NULL;
put_vnode(fVolume->FSVolume(), fVolume->ToVnode(fCurrent));
}
block_run run;
while (fStack.Pop(&run))
put_vnode(fVolume->FSVolume(), fVolume->ToVnode(run));
}
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;
}
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;
}
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;
}
\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;
}
\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;
}