root/src/add-ons/kernel/file_systems/netfs/server/Directory.cpp
// Directory.cpp

#include <new>

#include <dirent.h>
#include <errno.h>

#include <AutoDeleter.h>

#include "Directory.h"
#include "FDManager.h"
#include "Path.h"
#include "VolumeManager.h"

// CachedDirIterator
class CachedDirIterator : public DirIterator {
public:
                                                                CachedDirIterator();
                                                                ~CachedDirIterator();

        virtual status_t                        SetDirectory(Directory* directory);

        virtual Entry*                          NextEntry();
        virtual void                            Rewind();

        virtual Entry*                          GetCurrentEntry() const;

        virtual status_t                        GetStat(struct stat* st);

private:
                        Entry*                          fCurrentEntry;
};

// UncachedDirIterator
class UncachedDirIterator : public DirIterator {
public:
                                                                UncachedDirIterator();
                                                                ~UncachedDirIterator();

        virtual status_t                        SetDirectory(Directory* directory);

        virtual Entry*                          NextEntry();
        virtual void                            Rewind();

        virtual Entry*                          GetCurrentEntry() const;

protected:
        virtual int                                     GetFD() const;

private:
                        DIR*                            fDirHandle;
};


// #pragma mark -

// constructor
CachedDirIterator::CachedDirIterator()
        : DirIterator(),
          fCurrentEntry(NULL)
{
}

// destructor
CachedDirIterator::~CachedDirIterator()
{
}

// SetDirectory
status_t
CachedDirIterator::SetDirectory(Directory* directory)
{
        // set the new directory
        fDirectory = directory;
        fCurrentEntry = (fDirectory ? fDirectory->GetFirstEntry() : NULL);

        if (directory)
                fNodeRef = directory->GetNodeRef();

        return B_OK;
}

// NextEntry
Entry*
CachedDirIterator::NextEntry()
{
        if (!IsValid() || !fCurrentEntry)
                return NULL;

        Entry* entry = fCurrentEntry;
        fCurrentEntry = fDirectory->GetNextEntry(fCurrentEntry);
        return entry;
}

// Rewind
void
CachedDirIterator::Rewind()
{
        fCurrentEntry = (IsValid() ? fDirectory->GetFirstEntry() : NULL);
}

// GetCurrentEntry
Entry*
CachedDirIterator::GetCurrentEntry() const
{
        return (IsValid() ? fCurrentEntry : NULL);
}

// GetStat
status_t
CachedDirIterator::GetStat(struct stat* st)
{
        if (!fDirectory || !st)
                return B_BAD_VALUE;

        *st = fDirectory->GetStat();
        return B_OK;
}


// #pragma mark -

// constructor
UncachedDirIterator::UncachedDirIterator()
        : DirIterator(),
          fDirHandle(NULL)
{
}

// destructor
UncachedDirIterator::~UncachedDirIterator()
{
        // close
        if (fDirHandle) {
                closedir(fDirHandle);
                fDirHandle = NULL;
        }
}

// SetDirectory
status_t
UncachedDirIterator::SetDirectory(Directory* directory)
{
        // unset old
        if (fDirHandle) {
                closedir(fDirHandle);
                fDirHandle = NULL;
        }
        fDirectory = NULL;

        // set new
        if (directory) {
                // get the directory path
                Path path;
                status_t error = directory->GetPath(&path);
                if (error != B_OK)
                        return error;

                // open the directory
                error = FDManager::OpenDir(path.GetPath(), fDirHandle);
                if (error != B_OK)
                        return error;

                fDirectory = directory;
                fNodeRef = directory->GetNodeRef();
        }

        return B_OK;
}

// NextEntry
Entry*
UncachedDirIterator::NextEntry()
{
        if (!IsValid() && fDirHandle)
                return NULL;

        while (struct dirent* dirEntry = readdir(fDirHandle)) {
                Entry* entry;
                if (VolumeManager::GetDefault()->LoadEntry(dirEntry->d_pdev,
                                dirEntry->d_pino, dirEntry->d_name, false, &entry) == B_OK) {
                        return entry;
                }
        }

        // we're through: set the directory to "complete"
        fDirectory->SetComplete(true);

        return NULL;
}

// Rewind
void
UncachedDirIterator::Rewind()
{
        if (IsValid() && fDirHandle)
                rewinddir(fDirHandle);
}

// GetCurrentEntry
Entry*
UncachedDirIterator::GetCurrentEntry() const
{
        return NULL;
}

// GetFD
int
UncachedDirIterator::GetFD() const
{
        return dirfd(fDirHandle);
}


// #pragma mark -

// constructor
Directory::Directory(Volume* volume, const struct stat& st)
        : Node(volume, st),
          fEntries(),
          fIterators(),
          fIsComplete(false)
{
}

// destructor
Directory::~Directory()
{
        // remove all directory iterators
        while (DirIterator* iterator = fIterators.First())
                iterator->SetDirectory(NULL);
}

// GetActualReferringEntry
Entry*
Directory::GetActualReferringEntry() const
{
        // any entry other than "." and ".." is fine
        for (Entry* entry = GetFirstReferringEntry();
                 entry;
                 entry = GetNextReferringEntry(entry)) {
                if (entry->IsActualEntry())
                        return entry;
        }
        return NULL;
}

// AddEntry
void
Directory::AddEntry(Entry* entry)
{
        if (entry)
                fEntries.Insert(entry);
}

// RemoveEntry
void
Directory::RemoveEntry(Entry* entry)
{
        if (entry) {
                // update the directory iterators pointing to the removed entry
                for (DirIterator* iterator = fIterators.First();
                         iterator;
                         iterator = fIterators.GetNext(iterator)) {
                        if (iterator->GetCurrentEntry() == entry)
                                iterator->NextEntry();
                }

                fEntries.Remove(entry);
        }
}

// GetFirstEntry
Entry*
Directory::GetFirstEntry() const
{
        return fEntries.First();
}

// GetNextEntry
Entry*
Directory::GetNextEntry(Entry* entry) const
{
        return (entry ? fEntries.GetNext(entry) : NULL);
}

// CountEntries
int32
Directory::CountEntries() const
{
        int32 count = 0;
        Entry* entry = GetFirstEntry();
        while (entry) {
                count++;
                entry = GetNextEntry(entry);
        }
        return count;
}

// OpenDir
status_t
Directory::OpenDir(DirIterator** _iterator)
{
        if (!_iterator)
                return B_BAD_VALUE;

        // create the iterator
        DirIterator* iterator;
        if (fIsComplete)
                iterator = new(std::nothrow) CachedDirIterator;
        else
                iterator = new(std::nothrow) UncachedDirIterator;
        if (!iterator)
                return B_NO_MEMORY;
        ObjectDeleter<DirIterator> iteratorDeleter(iterator);

        // initialize it
        status_t error = iterator->SetDirectory(this);
        if (error != B_OK)
                return error;

        // check, if it really belongs to this node
        error = _CheckNodeHandle(iterator);
        if (error != B_OK)
                return error;

        // add it
        fIterators.Insert(iterator);

        iteratorDeleter.Detach();
        *_iterator = iterator;
        return B_OK;
}

// HasDirIterators
bool
Directory::HasDirIterators() const
{
        return fIterators.First();
}

// RemoveDirIterator
void
Directory::RemoveDirIterator(DirIterator* iterator)
{
        if (iterator)
                fIterators.Remove(iterator);
}

// SetComplete
void
Directory::SetComplete(bool complete)
{
        fIsComplete = complete;
}

// IsComplete
bool
Directory::IsComplete() const
{
        return fIsComplete;
}