#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <Application.h>
#include <Autolock.h>
#include <Message.h>
#include <NodeMonitor.h>
#include <Path.h>
#include "StatCacheServer.h"
#include "StatCacheServerImpl.h"
#define DBG(x)
#define OUT(format...) {printf(format); fflush(stdout);}
static const int32 kMaxSymlinks = 32;
static const int32 kDefaultNodeMonitorLimit = 4096;
static const int32 kNodeMonitorLimitIncrement = 512;
extern "C" int _kset_mon_limit_(int num);
static inline bool
is_dot_or_dotdot(const char* name)
{
if (name && name[0] == '.')
return (name[1] == '\0' || name[1] == '.' && name[2] == '\0');
return false;
}
static inline
int32
get_dirent_size(const char *name)
{
dirent *dummy = NULL;
int32 entrySize = (dummy->d_name + strlen(name) + 1) - (char*)dummy;
return (entrySize + 3) & ~0x3;
}
static inline
uint32
node_ref_hash(dev_t device, ino_t node)
{
uint32 hash = device;
hash = hash * 17 + (uint32)node;
hash = hash * 17 + (uint32)(node >> 32);
return hash;
}
static inline
uint32
string_hash(const char *name)
{
uint32 h = 0;
if (name) {
for (; *name; name++) {
uint32 g = h & 0xf0000000;
if (g)
h ^= g >> 24;
h = (h << 4) + *name;
}
}
return h;
}
size_t
NodeRefHash::operator()(const node_ref &nodeRef) const
{
return node_ref_hash(nodeRef.device, nodeRef.node);
}
size_t
EntryRefHash::operator()(const entry_ref &entryRef) const
{
uint32 hash = node_ref_hash(entryRef.device, entryRef.directory);
hash = hash * 17 + string_hash(entryRef.name);
return hash;
}
Entry::Entry()
: Referencable(),
fParent(NULL),
fName(),
fNode(NULL),
fPrevious(NULL),
fNext(NULL)
{
}
Entry::~Entry()
{
SetNode(NULL);
}
status_t
Entry::SetTo(Directory *parent, const char *name)
{
fParent = parent;
fName = name;
return B_OK;
}
Directory *
Entry::GetParent() const
{
return fParent;
}
const char *
Entry::GetName() const
{
return fName.c_str();
}
void
Entry::SetNode(Node *node)
{
if (fNode != node) {
if (fNode)
fNode->RemoveReference();
fNode = node;
if (fNode) {
fNode->AddReference();
if (!fNode->GetEntry() && !is_dot_or_dotdot(fName.c_str()))
fNode->SetEntry(this);
}
}
}
Node *
Entry::GetNode() const
{
return fNode;
}
void
Entry::SetPrevious(Entry *entry)
{
fPrevious = entry;
}
Entry *
Entry::GetPrevious() const
{
return fPrevious;
}
void
Entry::SetNext(Entry *entry)
{
fNext = entry;
}
Entry *
Entry::GetNext() const
{
return fNext;
}
entry_ref
Entry::GetEntryRef() const
{
node_ref dirRef(fParent->GetNodeRef());
return entry_ref(dirRef.device, dirRef.node, fName.c_str());
}
status_t
Entry::GetPath(string& path)
{
if (!fParent)
return B_ERROR;
status_t error = fParent->GetPath(path);
if (error != B_OK)
return error;
if (path[path.length() - 1] != '/')
path += '/';
path += fName;
return B_OK;
}
void
Entry::Unreferenced()
{
NodeManager::GetDefault()->EntryUnreferenced(this);
}
Node::Node(const struct stat &st)
: Referencable(),
fEntry(NULL),
fStat(st),
fStatValid(false)
{
}
Node::~Node()
{
NodeManager::GetDefault()->StopWatching(this);
SetEntry(NULL);
}
status_t
Node::SetTo(Entry *entry)
{
status_t error = NodeManager::GetDefault()->StartWatching(this);
if (error != B_OK)
return error;
SetEntry(entry);
return UpdateStat();
}
status_t
Node::GetPath(string& path)
{
if (this == NodeManager::GetDefault()->GetRootDirectory()) {
path = "/";
return B_OK;
}
if (!fEntry)
return B_ERROR;
return fEntry->GetPath(path);
}
const struct stat &
Node::GetStat() const
{
return fStat;
}
status_t
Node::GetStat(struct stat *st)
{
if (!fStatValid) {
status_t error = UpdateStat();
if (error != B_OK)
return error;
}
*st = fStat;
return B_OK;
}
status_t
Node::UpdateStat()
{
string path;
status_t error = GetPath(path);
if (error != B_OK)
return error;
DBG(OUT("disk access: lstat(): %s\n", path.c_str()));
if (lstat(path.c_str(), &fStat) < 0)
return errno;
fStatValid = true;
return B_OK;
}
void
Node::MarkStatInvalid()
{
fStatValid = false;
}
void
Node::SetEntry(Entry *entry)
{
if (entry != fEntry) {
if (fEntry)
fEntry->RemoveReference();
fEntry = entry;
if (fEntry)
fEntry->AddReference();
}
}
Entry *
Node::GetEntry() const
{
return fEntry;
}
node_ref
Node::GetNodeRef() const
{
node_ref nodeRef;
nodeRef.device = fStat.st_dev;
nodeRef.node = fStat.st_ino;
return nodeRef;
}
void
Node::Unreferenced()
{
NodeManager::GetDefault()->NodeUnreferenced(this);
}
Directory::Directory(const struct stat &st)
: Node(st),
fFirstEntry(NULL),
fLastEntry(NULL),
fIsComplete(false)
{
}
Directory::~Directory()
{
while (Entry *entry = GetFirstEntry())
RemoveEntry(entry);
}
status_t
Directory::SetTo(Entry *entry)
{
return Node::SetTo(entry);
}
status_t
Directory::FindEntry(const char *name, Entry **entry)
{
entry_ref ref(fStat.st_dev, fStat.st_ino, name);
if (!fIsComplete)
return NodeManager::GetDefault()->CreateEntry(ref, NULL, entry);
*entry = NodeManager::GetDefault()->GetEntry(ref);
return (*entry ? B_OK : B_ENTRY_NOT_FOUND);
}
Entry *
Directory::GetFirstEntry() const
{
return fFirstEntry;
}
Entry *
Directory::GetNextEntry(Entry *entry) const
{
return (entry ? entry->GetNext() : NULL);
}
status_t
Directory::ReadAllEntries()
{
if (fIsComplete)
return B_OK;
string path;
status_t error = GetPath(path);
if (error != B_OK)
return error;
DBG(OUT("disk access: opendir(): %s\n", path.c_str()));
DIR *dir = opendir(path.c_str());
if (!dir)
return errno;
while (dirent *entry = readdir(dir)) {
Entry *dummy;
FindEntry(entry->d_name, &dummy);
}
closedir(dir);
fIsComplete = true;
return B_OK;
}
bool
Directory::IsComplete() const
{
return fIsComplete;
}
void
Directory::AddEntry(Entry *entry)
{
if (fLastEntry) {
entry->SetPrevious(fLastEntry);
entry->SetNext(NULL);
fLastEntry->SetNext(entry);
fLastEntry = entry;
} else {
entry->SetPrevious(NULL);
entry->SetNext(NULL);
fFirstEntry = fLastEntry = entry;
}
entry->AddReference();
if (strcmp(entry->GetName(), ".") == 0)
fReferenceBaseCount++;
}
void
Directory::RemoveEntry(Entry *entry)
{
if (entry->GetParent() != this)
return;
if (strcmp(entry->GetName(), ".") == 0)
fReferenceBaseCount--;
if (entry->GetPrevious())
entry->GetPrevious()->SetNext(entry->GetNext());
else
fFirstEntry = entry->GetNext();
if (entry->GetNext())
entry->GetNext()->SetPrevious(entry->GetPrevious());
else
fLastEntry = entry->GetPrevious();
entry->SetPrevious(NULL);
entry->SetNext(NULL);
entry->RemoveReference();
}
SymLink::SymLink(const struct stat &st)
: Node(st),
fTarget()
{
}
SymLink::~SymLink()
{
}
status_t
SymLink::SetTo(Entry *entry)
{
status_t error = Node::SetTo(entry);
if (error != B_OK)
return error;
string path;
error = entry->GetPath(path);
if (error != B_OK)
return error;
char target[B_PATH_NAME_LENGTH + 1];
ssize_t bytesRead = readlink(path.c_str(), target, B_PATH_NAME_LENGTH);
if (bytesRead < 0)
return errno;
target[bytesRead] = '\0';
fTarget = target;
return B_OK;
}
const char *
SymLink::GetTarget() const
{
return fTarget.c_str();
}
NodeMonitor::NodeMonitor()
: BLooper("node monitor", B_DISPLAY_PRIORITY, 1000),
fCurrentNodeMonitorLimit(kDefaultNodeMonitorLimit),
fMessageCountSem(-1)
{
}
NodeMonitor::~NodeMonitor()
{
delete_sem(fMessageCountSem);
}
status_t
NodeMonitor::Init()
{
fMessageCountSem = create_sem(0, "nm message count");
if (fMessageCountSem < 0)
return fMessageCountSem;
return B_OK;
}
void
NodeMonitor::MessageReceived(BMessage *message)
{
switch (message->what) {
case B_NODE_MONITOR:
DetachCurrentMessage();
fMessageQueue.AddMessage(message);
release_sem(fMessageCountSem);
break;
default:
BLooper::MessageReceived(message);
}
}
status_t
NodeMonitor::StartWatching(Node *node)
{
if (!node)
return B_BAD_VALUE;
uint32 flags = B_WATCH_STAT;
if (S_ISDIR(node->GetStat().st_mode))
flags |= B_WATCH_DIRECTORY;
node_ref ref = node->GetNodeRef();
status_t error = watch_node(&ref, flags, this);
if (error != B_OK) {
fCurrentNodeMonitorLimit += kNodeMonitorLimitIncrement;
error = _kset_mon_limit_(fCurrentNodeMonitorLimit);
if (error == B_OK)
error = watch_node(&ref, flags, this);
}
return error;
}
status_t
NodeMonitor::StopWatching(Node *node)
{
if (!node)
return B_BAD_VALUE;
node_ref ref = node->GetNodeRef();
return watch_node(&ref, B_STOP_WATCHING, this);
}
status_t
NodeMonitor::GetNextMonitoringMessage(BMessage **_message)
{
status_t error = B_OK;
do {
error = acquire_sem(fMessageCountSem);
} while (error == B_INTERRUPTED);
if (error != B_OK)
return error;
BMessage *message = fMessageQueue.NextMessage();
if (!message)
return B_ERROR;
*_message = message;
return B_OK;
}
PathResolver::PathResolver()
: fSymLinkCounter(0)
{
}
status_t
PathResolver::FindEntry(const char *path, bool traverse, Entry **_entry)
{
return FindEntry(NULL, path, traverse, _entry);
}
status_t
PathResolver::FindEntry(Entry *entry, const char *path, bool traverse,
Entry **_entry)
{
if (!path || (!entry && *path != '/'))
return B_BAD_VALUE;
if (*path == '/') {
entry = NodeManager::GetDefault()->GetRootDirectory()->GetEntry();
while (*path == '/')
path++;
}
while (*path != '\0') {
int componentLen;
if (char *nextSlash = strchr(path, '/'))
componentLen = nextSlash - path;
else
componentLen = strlen(path);
string component(path, componentLen);
path += componentLen;
Node *node = entry->GetNode();
if (SymLink *symlink = dynamic_cast<SymLink*>(node)) {
status_t error = ResolveSymlink(symlink, &node);
if (error != B_OK)
return error;
}
if (Directory *dir = dynamic_cast<Directory*>(node)) {
status_t error = dir->FindEntry(component.c_str(), &entry);
if (error != B_OK)
return error;
} else
return B_ENTRY_NOT_FOUND;
while (*path == '/')
path++;
}
if (traverse) {
status_t error = ResolveSymlink(entry, &entry);
if (error != B_OK)
return error;
}
*_entry = entry;
return B_OK;
}
status_t
PathResolver::FindNode(const char *path, bool traverse, Node **node)
{
Entry *entry;
status_t error = FindEntry(path, traverse, &entry);
if (error != B_OK)
return error;
if (!entry->GetNode())
return B_ENTRY_NOT_FOUND;
*node = entry->GetNode();
return B_OK;
}
status_t
PathResolver::ResolveSymlink(Node *node, Node **_node)
{
Entry *entry;
status_t error = ResolveSymlink(node, &entry);
if (error != B_OK)
return error;
if (!entry->GetNode())
return B_ENTRY_NOT_FOUND;
*_node = entry->GetNode();
return B_OK;
}
status_t
PathResolver::ResolveSymlink(Node *node, Entry **entry)
{
return ResolveSymlink(node->GetEntry(), entry);
}
status_t
PathResolver::ResolveSymlink(Entry *entry, Entry **_entry)
{
if (!entry->GetNode())
return B_ENTRY_NOT_FOUND;
SymLink *symlink = dynamic_cast<SymLink*>(entry->GetNode());
if (!symlink) {
*_entry = entry;
return B_OK;
}
if (fSymLinkCounter > kMaxSymlinks)
return B_LINK_LIMIT;
const char *target = symlink->GetTarget();
if (!target || !symlink->GetEntry() || !symlink->GetEntry()->GetParent()
|| !symlink->GetEntry()->GetParent()->GetEntry())
return B_ENTRY_NOT_FOUND;
fSymLinkCounter++;
status_t error = FindEntry(symlink->GetEntry()->GetParent()->GetEntry(),
target, true, _entry);
fSymLinkCounter--;
return error;
}
NodeManager::NodeManager()
: BLocker("node manager"),
fRootDirectory(NULL),
fNodeMonitor(NULL),
fNodeMonitoringProcessor(-1)
{
}
NodeManager::~NodeManager()
{
if (fNodeMonitor) {
fNodeMonitor->Lock();
fNodeMonitor->Quit();
}
if (fNodeMonitoringProcessor >= 0) {
status_t status;
wait_for_thread(fNodeMonitoringProcessor, &status);
}
}
NodeManager *
NodeManager::GetDefault()
{
return &sManager;
}
status_t
NodeManager::Init()
{
fNodeMonitor = new NodeMonitor;
status_t error = fNodeMonitor->Init();
if (error != B_OK)
return error;
fNodeMonitor->Run();
fNodeMonitoringProcessor = spawn_thread(&_NodeMonitoringProcessorEntry,
"node monitoring processor", B_NORMAL_PRIORITY, this);
if (fNodeMonitoringProcessor < 0)
return fNodeMonitoringProcessor;
resume_thread(fNodeMonitoringProcessor);
struct stat st;
if (lstat("/", &st) < 0)
return errno;
node_ref nodeRef;
nodeRef.device = st.st_dev;
nodeRef.node = st.st_ino;
fRootDirectory = new Directory(st);
fNodes[nodeRef] = fRootDirectory;
entry_ref entryRef(st.st_dev, st.st_ino, ".");
Entry *entry = new Entry;
error = entry->SetTo(fRootDirectory, ".");
if (error != B_OK)
return error;
entry->SetNode(fRootDirectory);
fEntries[entryRef] = entry;
error = fRootDirectory->SetTo(entry);
return error;
}
Directory *
NodeManager::GetRootDirectory() const
{
return fRootDirectory;
}
Node *
NodeManager::GetNode(const node_ref &nodeRef)
{
NodeMap::iterator it = fNodes.find(nodeRef);
if (it == fNodes.end())
return NULL;
return it->second;
}
Entry *
NodeManager::GetEntry(const entry_ref &entryRef)
{
EntryMap::iterator it = fEntries.find(entryRef);
if (it == fEntries.end())
return NULL;
return it->second;
}
status_t
NodeManager::CreateEntry(const entry_ref &entryRef, const node_ref *nodeRef,
Entry **_entry)
{
Entry *entry = GetEntry(entryRef);
if (nodeRef && entry && entry->GetNode()
&& entry->GetNode()->GetNodeRef() != *nodeRef) {
RemoveEntry(entry);
entry = NULL;
}
if (!entry) {
node_ref parentDirRef;
parentDirRef.device = entryRef.device;
parentDirRef.node = entryRef.directory;
Directory *dir;
status_t error = CreateDirectory(parentDirRef, &dir);
if (error != B_OK)
return error;
entry = GetEntry(entryRef);
if (!entry) {
entry = new Entry;
error = entry->SetTo(dir, entryRef.name);
if (error != B_OK) {
delete entry;
return error;
}
Node *node;
error = NodeManager::GetDefault()->_CreateNode(entry, &node);
if (error != B_OK) {
delete entry;
return error;
}
if (node->GetEntry() && node->GetEntry() != entry
&& !is_dot_or_dotdot(entry->GetName())) {
RemoveEntry(node->GetEntry());
StopWatching(node);
StartWatching(node);
}
entry->SetNode(node);
node->RemoveReference();
dir->AddEntry(entry);
fEntries[entryRef] = entry;
entry->RemoveReference();
DBG(
string path;
entry->GetPath(path);
OUT("entry created: `%s'\n", path.c_str());
)
}
}
*_entry = entry;
return B_OK;
}
status_t
NodeManager::CreateDirectory(const node_ref &nodeRef, Directory **_dir)
{
Node *node = GetNode(nodeRef);
if (!node) {
entry_ref entryRef(nodeRef.device, nodeRef.node, ".");
BPath path;
status_t error = path.SetTo(&entryRef);
if (error != B_OK)
return error;
error = PathResolver().FindNode(path.Path(), false, &node);
if (error != B_OK)
return error;
}
Directory *dir = dynamic_cast<Directory*>(node);
if (!dir)
return B_NOT_A_DIRECTORY;
*_dir = dir;
return B_OK;
}
void
NodeManager::RemoveEntry(Entry *entry)
{
if (!entry)
return;
DBG(
string path;
entry->GetPath(path);
OUT("entry removed: `%s'\n", path.c_str());
)
entry->AddReference();
if (entry->GetParent())
entry->GetParent()->RemoveEntry(entry);
Node *node = entry->GetNode();
if (node) {
if (node->GetEntry() == entry)
node->SetEntry(NULL);
entry->SetNode(NULL);
}
entry->RemoveReference();
}
void
NodeManager::MoveEntry(Entry *entry, const entry_ref &newRef,
const node_ref &nodeRef)
{
node_ref newDirRef;
newDirRef.device = newRef.device;
newDirRef.node = newRef.directory;
Directory *newDir = dynamic_cast<Directory*>(GetNode(newDirRef));
if (!newDir) {
RemoveEntry(entry);
return;
}
Node *node = entry->GetNode();
if (newDir != entry->GetParent()
|| strcmp(newRef.name, entry->GetName()) != 0
|| (node && node->GetNodeRef() != nodeRef)) {
if (node)
node->AddReference();
RemoveEntry(entry);
CreateEntry(newRef, &nodeRef, &entry);
if (node)
node->RemoveReference();
}
}
void
NodeManager::EntryUnreferenced(Entry *entry)
{
DBG(OUT("NodeManager::EntryUnreferenced(%p): (%p, `%s')\n", entry, entry->GetParent(), entry->GetName()));
if (fEntries.erase(entry->GetEntryRef()) > 0)
delete entry;
}
void
NodeManager::NodeUnreferenced(Node *node)
{
DBG(OUT("NodeManager::NodeUnreferenced(%p): entry: %p\n", node, node->GetEntry()));
if (fNodes.erase(node->GetNodeRef()) > 0)
delete node;
}
status_t
NodeManager::StartWatching(Node *node)
{
return fNodeMonitor->StartWatching(node);
}
status_t
NodeManager::StopWatching(Node *node)
{
return fNodeMonitor->StopWatching(node);
}
int32
NodeManager::_NodeMonitoringProcessorEntry(void *data)
{
return ((NodeManager*)data)->_NodeMonitoringProcessor();
}
int32
NodeManager::_NodeMonitoringProcessor()
{
BMessage *message;
while (fNodeMonitor->GetNextMonitoringMessage(&message) == B_OK) {
int32 opcode;
if (message->FindInt32("opcode", &opcode) == B_OK) {
BAutolock _(this);
switch (opcode) {
case B_ENTRY_CREATED:
_EntryCreated(message);
break;
case B_ENTRY_REMOVED:
_EntryRemoved(message);
break;
case B_ENTRY_MOVED:
_EntryMoved(message);
break;
case B_STAT_CHANGED:
_StatChanged(message);
break;
}
}
delete message;
}
}
status_t
NodeManager::_CreateNode(Entry *entry, Node **_node)
{
string path;
status_t error = entry->GetPath(path);
if (error != B_OK)
return error;
DBG(OUT("disk access: lstat(): %s\n", path.c_str()));
struct stat st;
if (lstat(path.c_str(), &st) < 0)
return errno;
node_ref nodeRef;
nodeRef.device = st.st_dev;
nodeRef.node = st.st_ino;
Node *node = GetNode(nodeRef);
if (node) {
node->AddReference();
} else {
if (S_ISLNK(st.st_mode))
node = new SymLink(st);
else if (S_ISDIR(st.st_mode))
node = new Directory(st);
else
node = new Node(st);
error = node->SetTo(entry);
if (error != B_OK) {
delete node;
return error;
}
fNodes[nodeRef] = node;
}
*_node = node;
return B_OK;
}
void
NodeManager::_EntryCreated(BMessage *message)
{
node_ref dirNodeRef;
node_ref nodeRef;
const char* name;
if (message->FindInt32("device", &dirNodeRef.device) != B_OK
|| message->FindInt64("directory", &dirNodeRef.node) != B_OK
|| message->FindInt64("node", &nodeRef.node) != B_OK
|| message->FindString("name", &name) != B_OK) {
return;
}
nodeRef.device = dirNodeRef.device;
Node *node = NodeManager::GetDefault()->GetNode(dirNodeRef);
Directory *dir = dynamic_cast<Directory*>(node);
if (!dir)
return;
if (dir->IsComplete()) {
Entry *entry;
if (dir->FindEntry(name, &entry) != B_OK) {
entry_ref ref(dirNodeRef.device, dirNodeRef.node, name);
NodeManager::GetDefault()->CreateEntry(ref, &nodeRef, &entry);
}
}
}
void
NodeManager::_EntryRemoved(BMessage *message)
{
node_ref nodeRef;
const char* name;
if (message->FindInt32("device", &nodeRef.device) != B_OK
|| message->FindInt64("node", &nodeRef.node) != B_OK) {
return;
}
Node *node = NodeManager::GetDefault()->GetNode(nodeRef);
if (!node)
return;
NodeManager::GetDefault()->RemoveEntry(node->GetEntry());
}
void
NodeManager::_EntryMoved(BMessage *message)
{
node_ref nodeRef;
ino_t newDirID;
const char* name;
if (message->FindInt32("device", &nodeRef.device) != B_OK
|| message->FindInt64("to directory", &newDirID) != B_OK
|| message->FindInt64("node", &nodeRef.node) != B_OK
|| message->FindString("name", &name) != B_OK) {
return;
}
Node *node = NodeManager::GetDefault()->GetNode(nodeRef);
if (!node) {
Entry *entry;
entry_ref newRef(nodeRef.device, newDirID, name);
NodeManager::GetDefault()->CreateEntry(newRef, &nodeRef, &entry);
return;
}
entry_ref newRef(nodeRef.device, newDirID, name);
NodeManager::GetDefault()->MoveEntry(node->GetEntry(), newRef, nodeRef);
}
void
NodeManager::_StatChanged(BMessage *message)
{
node_ref nodeRef;
if (message->FindInt32("device", &nodeRef.device) != B_OK
|| message->FindInt64("node", &nodeRef.node)) {
return;
}
Node *node = GetNode(nodeRef);
if (!node)
return;
node->MarkStatInvalid();
}
NodeManager NodeManager::sManager;
static
status_t
read_request(port_id port, stat_cache_request &request)
{
status_t error = B_OK;
bool done = false;
do {
int32 code;
ssize_t bytesRead = read_port(port, &code, &request,
sizeof(stat_cache_request));
if (bytesRead < 0) {
error = bytesRead;
done = (error != B_INTERRUPTED);
} else if (bytesRead
< ((stat_cache_request*)NULL)->path + 2 - (char*)NULL) {
DBG(OUT("request too short: %ld\n", bytesRead));
error = B_ERROR;
} else {
done = true;
error = B_OK;
}
} while (!done);
return error;
}
static
status_t
handle_stat_request(stat_cache_request &request)
{
DBG(OUT("handle_stat_request(): `%s'\n", request.path));
stat_cache_stat_reply reply;
PathResolver resolver;
Node *node;
reply.error = resolver.FindNode(request.path, true, &node);
if (reply.error == B_OK)
reply.error = node->GetStat(&reply.st);
DBG(OUT(" -> `%s'\n", strerror(reply.error)));
return write_port(request.replyPort, 0, &reply, sizeof(reply));
}
static
status_t
handle_read_dir_request(stat_cache_request &request)
{
DBG(OUT("handle_read_dir_request(): `%s'\n", request.path));
PathResolver resolver;
Node *node = NULL;
status_t error = resolver.FindNode(request.path, true, &node);
Directory *dir = dynamic_cast<Directory*>(node);
if (error == B_OK && !dir)
error = B_NOT_A_DIRECTORY;
if (error == B_OK)
error = dir->ReadAllEntries();
int32 replySize = sizeof(stat_cache_readdir_reply);
int32 entryCount = 0;
if (error == B_OK) {
for (Entry *entry = dir->GetFirstEntry();
entry;
entry = dir->GetNextEntry(entry)) {
replySize += get_dirent_size(entry->GetName());
entryCount++;
}
}
stat_cache_readdir_reply *reply
= (stat_cache_readdir_reply*)new uint8[replySize];
reply->error = error;
reply->entryCount = entryCount;
if (error == B_OK) {
uint8 *buffer = reply->buffer;
for (Entry *entry = dir->GetFirstEntry();
entry;
entry = dir->GetNextEntry(entry)) {
int32 entrySize = get_dirent_size(entry->GetName());
node_ref parentNodeRef(entry->GetParent()->GetNodeRef());
node_ref nodeRef(entry->GetNode()->GetNodeRef());
dirent *ent = (dirent*)buffer;
ent->d_pdev = parentNodeRef.device;
ent->d_pino = parentNodeRef.node;
ent->d_dev = nodeRef.device;
ent->d_ino = nodeRef.node;
ent->d_reclen = entrySize;
strcpy(ent->d_name, entry->GetName());
buffer += entrySize;
}
}
DBG(OUT(" -> entryCount: %ld, error: `%s'\n", reply->entryCount, strerror(reply->error)));
error = write_port(request.replyPort, 0, reply, replySize);
delete[] (uint8*)reply;
return error;
}
int32
request_loop(void *data)
{
port_id requestPort = *(port_id*)data;
status_t error = NodeManager::GetDefault()->Init();
if (error != B_OK) {
fprintf(stderr, "Failed to init node manager: %s\n", strerror(error));
return 1;
}
stat_cache_request request;
while (read_request(requestPort, request) == B_OK) {
BAutolock _(NodeManager::GetDefault());
switch (request.command) {
case STAT_CACHE_COMMAND_STAT:
handle_stat_request(request);
break;
case STAT_CACHE_COMMAND_READDIR:
handle_read_dir_request(request);
break;
default:
fprintf(stderr, "Unknown command: %ld\n", request.command);
break;
}
}
if (delete_port(requestPort) == B_OK)
be_app->PostMessage(B_QUIT_REQUESTED);
return 0;
}
int
main()
{
port_id requestPort = create_port(1, STAT_CACHE_SERVER_PORT_NAME);
if (requestPort < 0) {
fprintf(stderr, "Failed to create request port: %s\n",
strerror(requestPort));
return 1;
}
thread_id requestLoop = spawn_thread(request_loop, "request loop",
B_NORMAL_PRIORITY, &requestPort);
if (resume_thread(requestLoop) < B_OK)
return -1;
BApplication app("application/x-vnd.haiku.jam-cacheserver");
app.Run();
delete_port(requestPort);
status_t result;
wait_for_thread(requestLoop, &result);
return 0;
}