#include <errno.h>
#include <fcntl.h>
#include <new>
#include <unistd.h>
#include <sys/stat.h>
#include <AutoDeleter.h>
#include <fs_attr.h>
#include "DebugSupport.h"
#include "Entry.h"
#include "FDManager.h"
#include "Node.h"
#include "NodeHandle.h"
#include "Path.h"
#include "VolumeManager.h"
Node::Node(Volume* volume, const struct stat& st)
: AttributeDirectory(),
fVolume(volume),
fStat(st),
fReferringEntries()
{
}
Node::~Node()
{
}
Volume*
Node::GetVolume() const
{
return fVolume;
}
node_ref
Node::GetNodeRef() const
{
node_ref ref;
ref.device = fStat.st_dev;
ref.node = fStat.st_ino;
return ref;
}
dev_t
Node::GetVolumeID() const
{
return fStat.st_dev;
}
ino_t
Node::GetID() const
{
return fStat.st_ino;
}
void
Node::AddReferringEntry(Entry* entry)
{
if (!entry)
return;
fReferringEntries.Insert(entry);
}
void
Node::RemoveReferringEntry(Entry* entry)
{
if (entry)
fReferringEntries.Remove(entry);
}
Entry*
Node::GetFirstReferringEntry() const
{
return fReferringEntries.First();
}
Entry*
Node::GetNextReferringEntry(Entry* entry) const
{
return (entry ? fReferringEntries.GetNext(entry) : NULL);
}
Entry*
Node::FindReferringEntry(dev_t volumeID, ino_t directoryID, const char* name)
{
if (!name)
return NULL;
NoAllocEntryRef ref(volumeID, directoryID, name);
return FindReferringEntry(ref);
}
Entry*
Node::FindReferringEntry(const entry_ref& entryRef)
{
for (Entry* entry = GetFirstReferringEntry();
entry;
entry = GetNextReferringEntry(entry)) {
NoAllocEntryRef ref(entry->GetEntryRef());
if (ref == entryRef)
return entry;
}
return NULL;
}
Entry*
Node::GetActualReferringEntry() const
{
return GetFirstReferringEntry();
}
const struct stat&
Node::GetStat() const
{
return fStat;
}
status_t
Node::UpdateStat()
{
Path path;
status_t error = GetPath(&path);
if (error != B_OK)
return error;
struct stat st;
if (lstat(path.GetPath(), &st) < 0)
return errno;
if (st.st_dev != fStat.st_dev || st.st_ino != fStat.st_ino) {
ERROR("Node::UpdateStat(): ERROR: GetPath() returned path that "
"doesn't point to this node: node: (%" B_PRIdDEV ", %" B_PRIdINO "), "
"path: `%s'\n",
GetVolumeID(), GetID(), path.GetPath());
return B_ENTRY_NOT_FOUND;
}
fStat = st;
return B_OK;
}
bool
Node::IsDirectory() const
{
return S_ISDIR(fStat.st_mode);
}
bool
Node::IsFile() const
{
return S_ISREG(fStat.st_mode);
}
bool
Node::IsSymlink() const
{
return S_ISLNK(fStat.st_mode);
}
status_t
Node::GetPath(Path* path)
{
return VolumeManager::GetDefault()->GetPath(this, path);
}
status_t
Node::Open(int openMode, FileHandle** _fileHandle)
{
if (!_fileHandle)
return B_BAD_VALUE;
FileHandle* fileHandle = new(std::nothrow) FileHandle;
if (!fileHandle)
return B_NO_MEMORY;
ObjectDeleter<FileHandle> fileHandleDeleter(fileHandle);
status_t error = fileHandle->Open(this, openMode);
if (error != B_OK)
return error;
error = _CheckNodeHandle(fileHandle);
if (error != B_OK)
return error;
fileHandleDeleter.Detach();
*_fileHandle = fileHandle;
return B_OK;
}
status_t
Node::OpenAttrDir(AttrDirIterator** _iterator)
{
if (!_iterator)
return B_BAD_VALUE;
AttrDirIterator* iterator = new(std::nothrow) AttrDirIterator;
if (!iterator)
return B_NO_MEMORY;
ObjectDeleter<AttrDirIterator> iteratorDeleter(iterator);
status_t error = iterator->Open(this);
if (error != B_OK)
return error;
error = _CheckNodeHandle(iterator);
if (error != B_OK)
return error;
iteratorDeleter.Detach();
*_iterator = iterator;
return B_OK;
}
status_t
Node::OpenNode(BNode& node)
{
Entry* entry = GetActualReferringEntry();
if (!entry)
return B_ENTRY_NOT_FOUND;
NoAllocEntryRef entryRef(entry->GetEntryRef());
return FDManager::SetNode(&node, &entryRef);
}
status_t
Node::ReadSymlink(char* buffer, int32 bufferSize, int32* _bytesRead)
{
if (!buffer || bufferSize < 1)
return B_BAD_VALUE;
Path path;
status_t error = GetPath(&path);
if (error != B_OK)
return error;
ssize_t bytesRead = readlink(path.GetPath(), buffer, bufferSize);
if (bytesRead < 0)
return bytesRead;
if (bytesRead < bufferSize)
buffer[bytesRead] = '\0';
else
buffer[bufferSize - 1] = '\0';
if (_bytesRead)
*_bytesRead = bytesRead;
return B_OK;
}
status_t
Node::_CheckNodeHandle(NodeHandle* nodeHandle)
{
if (!nodeHandle)
return B_BAD_VALUE;
struct stat st;
status_t error = nodeHandle->GetStat(&st);
if (error != B_OK)
return error;
if (st.st_dev != fStat.st_dev || st.st_ino != fStat.st_ino)
return B_ENTRY_NOT_FOUND;
return B_OK;
}