#include <new>
#include <dirent.h>
#include <errno.h>
#include <AutoDeleter.h>
#include "Directory.h"
#include "FDManager.h"
#include "Path.h"
#include "VolumeManager.h"
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;
};
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;
};
CachedDirIterator::CachedDirIterator()
: DirIterator(),
fCurrentEntry(NULL)
{
}
CachedDirIterator::~CachedDirIterator()
{
}
status_t
CachedDirIterator::SetDirectory(Directory* directory)
{
fDirectory = directory;
fCurrentEntry = (fDirectory ? fDirectory->GetFirstEntry() : NULL);
if (directory)
fNodeRef = directory->GetNodeRef();
return B_OK;
}
Entry*
CachedDirIterator::NextEntry()
{
if (!IsValid() || !fCurrentEntry)
return NULL;
Entry* entry = fCurrentEntry;
fCurrentEntry = fDirectory->GetNextEntry(fCurrentEntry);
return entry;
}
void
CachedDirIterator::Rewind()
{
fCurrentEntry = (IsValid() ? fDirectory->GetFirstEntry() : NULL);
}
Entry*
CachedDirIterator::GetCurrentEntry() const
{
return (IsValid() ? fCurrentEntry : NULL);
}
status_t
CachedDirIterator::GetStat(struct stat* st)
{
if (!fDirectory || !st)
return B_BAD_VALUE;
*st = fDirectory->GetStat();
return B_OK;
}
UncachedDirIterator::UncachedDirIterator()
: DirIterator(),
fDirHandle(NULL)
{
}
UncachedDirIterator::~UncachedDirIterator()
{
if (fDirHandle) {
closedir(fDirHandle);
fDirHandle = NULL;
}
}
status_t
UncachedDirIterator::SetDirectory(Directory* directory)
{
if (fDirHandle) {
closedir(fDirHandle);
fDirHandle = NULL;
}
fDirectory = NULL;
if (directory) {
Path path;
status_t error = directory->GetPath(&path);
if (error != B_OK)
return error;
error = FDManager::OpenDir(path.GetPath(), fDirHandle);
if (error != B_OK)
return error;
fDirectory = directory;
fNodeRef = directory->GetNodeRef();
}
return B_OK;
}
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;
}
}
fDirectory->SetComplete(true);
return NULL;
}
void
UncachedDirIterator::Rewind()
{
if (IsValid() && fDirHandle)
rewinddir(fDirHandle);
}
Entry*
UncachedDirIterator::GetCurrentEntry() const
{
return NULL;
}
int
UncachedDirIterator::GetFD() const
{
return dirfd(fDirHandle);
}
Directory::Directory(Volume* volume, const struct stat& st)
: Node(volume, st),
fEntries(),
fIterators(),
fIsComplete(false)
{
}
Directory::~Directory()
{
while (DirIterator* iterator = fIterators.First())
iterator->SetDirectory(NULL);
}
Entry*
Directory::GetActualReferringEntry() const
{
for (Entry* entry = GetFirstReferringEntry();
entry;
entry = GetNextReferringEntry(entry)) {
if (entry->IsActualEntry())
return entry;
}
return NULL;
}
void
Directory::AddEntry(Entry* entry)
{
if (entry)
fEntries.Insert(entry);
}
void
Directory::RemoveEntry(Entry* entry)
{
if (entry) {
for (DirIterator* iterator = fIterators.First();
iterator;
iterator = fIterators.GetNext(iterator)) {
if (iterator->GetCurrentEntry() == entry)
iterator->NextEntry();
}
fEntries.Remove(entry);
}
}
Entry*
Directory::GetFirstEntry() const
{
return fEntries.First();
}
Entry*
Directory::GetNextEntry(Entry* entry) const
{
return (entry ? fEntries.GetNext(entry) : NULL);
}
int32
Directory::CountEntries() const
{
int32 count = 0;
Entry* entry = GetFirstEntry();
while (entry) {
count++;
entry = GetNextEntry(entry);
}
return count;
}
status_t
Directory::OpenDir(DirIterator** _iterator)
{
if (!_iterator)
return B_BAD_VALUE;
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);
status_t error = iterator->SetDirectory(this);
if (error != B_OK)
return error;
error = _CheckNodeHandle(iterator);
if (error != B_OK)
return error;
fIterators.Insert(iterator);
iteratorDeleter.Detach();
*_iterator = iterator;
return B_OK;
}
bool
Directory::HasDirIterators() const
{
return fIterators.First();
}
void
Directory::RemoveDirIterator(DirIterator* iterator)
{
if (iterator)
fIterators.Remove(iterator);
}
void
Directory::SetComplete(bool complete)
{
fIsComplete = complete;
}
bool
Directory::IsComplete() const
{
return fIsComplete;
}