Skip to content

Commit

Permalink
feat(hyclone_server): Directory watch
Browse files Browse the repository at this point in the history
HyClone now supports watching directories.

Events attached to an emulated `IoContext` will be forwarded to
`VfsService`, which is then forwarded to `inotify` on Linux.

Currently, only directory-related events are processed. However,
this is enough for `package_daemon` to receive `B_ENTRY_CREATED`
and `B_ENTRY_REMOVED` for the `packages` folder and
install/uninstall packages on the fly.

HaikuPorter, 今は嘘が完全ですか?
  • Loading branch information
trungnt2910 committed Apr 26, 2023
1 parent 87fec10 commit 3241db0
Show file tree
Hide file tree
Showing 10 changed files with 474 additions and 16 deletions.
22 changes: 22 additions & 0 deletions hyclone_server/fs/hostfs.cpp
Expand Up @@ -208,4 +208,26 @@ status_t HostfsDevice::TransformDirent(const std::filesystem::path& path, haiku_
}

return dirent.d_reclen;
}

status_t HostfsDevice::AddMonitor(haiku_ino_t node)
{
auto& vfsService = System::GetInstance().GetVfsService();
std::string pathStr;

if (!vfsService.GetEntryRef(EntryRef(_info.dev, node), pathStr))
{
return B_ENTRY_NOT_FOUND;
}

std::filesystem::path path(pathStr);
auto relativePath = path.lexically_relative(_root);
auto hostPath = _hostRoot / relativePath;

return server_add_native_monitor(hostPath, _info.dev, node);
}

status_t HostfsDevice::RemoveMonitor(haiku_ino_t node)
{
return server_remove_native_monitor(_info.dev, node);
}
3 changes: 3 additions & 0 deletions hyclone_server/fs/hostfs.h
Expand Up @@ -24,6 +24,9 @@ class HostfsDevice : public VfsDevice, private std::enable_shared_from_this<Host
int statMask, bool& isSymlink) override;
virtual status_t TransformDirent(const std::filesystem::path& path, haiku_dirent& dirent) override;

virtual status_t AddMonitor(haiku_ino_t node) override;
virtual status_t RemoveMonitor(haiku_ino_t node) override;

// TODO: Override extended attributes functions to read from the host filesystem.

const std::filesystem::path& GetHostRoot() const { return _hostRoot; }
Expand Down
30 changes: 30 additions & 0 deletions hyclone_server/io_context.cpp
@@ -1,5 +1,7 @@
#include "io_context.h"
#include "server_nodemonitor.h"
#include "server_vfs.h"
#include "system.h"

IoContext::IoContext(const IoContext& other)
{
Expand All @@ -8,6 +10,20 @@ IoContext::IoContext(const IoContext& other)
// Monitors are not copied.
}

IoContext::~IoContext()
{
auto& vfsService = System::GetInstance().GetVfsService();
auto lock = vfsService.Lock();
for (auto& listener : _monitors)
{
if (listener && listener->monitor
&& listener->monitor->node != (haiku_ino_t)-1)
{
vfsService.RemoveMonitor(listener->monitor->device, listener->monitor->node);
}
}
}

IoContext& IoContext::operator=(const IoContext& other)
{
if (this == &other)
Expand All @@ -27,11 +43,25 @@ size_t IoContext::AddMonitor(const std::shared_ptr<monitor_listener>& listener)
{
_monitors.push_back(listener);
listener->context_link = --_monitors.end();
if (listener && listener->monitor
&& listener->monitor->node != (haiku_ino_t)-1)
{
auto& vfsService = System::GetInstance().GetVfsService();
auto lock = vfsService.Lock();
vfsService.AddMonitor(listener->monitor->device, listener->monitor->node);
}
return _monitors.size();
}

size_t IoContext::RemoveMonitor(const std::shared_ptr<monitor_listener>& listener)
{
_monitors.erase(listener->context_link);
if (listener && listener->monitor
&& listener->monitor->node != (haiku_ino_t)-1)
{
auto& vfsService = System::GetInstance().GetVfsService();
auto lock = vfsService.Lock();
vfsService.RemoveMonitor(listener->monitor->device, listener->monitor->node);
}
return _monitors.size();
}
2 changes: 1 addition & 1 deletion hyclone_server/io_context.h
Expand Up @@ -15,9 +15,9 @@ class IoContext
std::list<std::shared_ptr<monitor_listener>> _monitors;
public:
IoContext() = default;
~IoContext() = default;
IoContext(const IoContext& other);
IoContext& operator=(const IoContext& other);
~IoContext();

unsigned int MaxMonitors() const { return _maxMonitors; }
unsigned int NumMonitors() const;
Expand Down
4 changes: 4 additions & 0 deletions hyclone_server/server_native.h
Expand Up @@ -5,6 +5,7 @@
#include <filesystem>
#include <string>

#include "BeDefs.h"
#include "haiku_team.h"
#include "haiku_thread.h"

Expand All @@ -28,6 +29,9 @@ void server_fill_fs_info(const std::filesystem::path& path, haiku_fs_info* info)
status_t server_read_stat(const std::filesystem::path& path, haiku_stat& st);
status_t server_write_stat(const std::filesystem::path& path, const haiku_stat& stat, int statMask);

status_t server_add_native_monitor(const std::filesystem::path& hostPath, haiku_dev_t device, haiku_ino_t node);
status_t server_remove_native_monitor(haiku_dev_t device, haiku_ino_t node);

void server_exit_thread();

#endif // __SERVER_NATIVE_H__
135 changes: 124 additions & 11 deletions hyclone_server/server_nodemonitor.cpp
Expand Up @@ -6,16 +6,6 @@

const std::string NodeMonitorService::kNodeMonitorServiceName = "node monitor";

typedef std::list<std::shared_ptr<monitor_listener>> MonitorListenerList;

struct node_monitor
{
// node_monitor* hash_link;
haiku_dev_t device;
haiku_ino_t node;
MonitorListenerList listeners;
};

struct interested_monitor_listener_list
{
MonitorListenerList::iterator iterator;
Expand Down Expand Up @@ -374,6 +364,129 @@ status_t NodeMonitorService::_SendNotificationMessage(KMessage& message,
return B_OK;
}

/*! \brief Notifies all interested listeners that an entry has been created
or removed.
\param opcode \c B_ENTRY_CREATED or \c B_ENTRY_REMOVED.
\param device The ID of the mounted FS, the entry lives/lived in.
\param directory The entry's parent directory ID.
\param name The entry's name.
\param node The ID of the node the entry refers/referred to.
\return
- \c B_OK, if everything went fine,
- another error code otherwise.
*/
status_t NodeMonitorService::NotifyEntryCreatedOrRemoved(
int32 opcode, haiku_dev_t device,
haiku_ino_t directory, const char* name, haiku_ino_t node)
{
if (!name)
return B_BAD_VALUE;

std::unique_lock<std::recursive_mutex> locker(_recursiveLock);

// get the lists of all interested listeners
interested_monitor_listener_list interestedListeners[3];
int32 interestedListenerCount = 0;
// ... for the volume
_GetInterestedVolumeListeners(device, B_WATCH_NAME,
interestedListeners, interestedListenerCount);
// ... for the node
if (opcode != B_ENTRY_CREATED)
{
_GetInterestedMonitorListeners(device, node, B_WATCH_NAME,
interestedListeners, interestedListenerCount);
}
// ... for the directory
_GetInterestedMonitorListeners(device, directory, B_WATCH_DIRECTORY,
interestedListeners, interestedListenerCount);

if (interestedListenerCount == 0)
return B_OK;

// there are interested listeners: construct the message and send it
char messageBuffer[1024];
KMessage message;
message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR);
message.AddInt32("opcode", opcode);
message.AddInt32("device", device);
message.AddInt64("directory", directory);
message.AddInt64("node", node);
message.AddString("name", name); // for "removed" Haiku only

return _SendNotificationMessage(message, interestedListeners,
interestedListenerCount);
}

inline status_t
NodeMonitorService::NotifyEntryMoved(
haiku_dev_t device, haiku_ino_t fromDirectory,
const char* fromName, haiku_ino_t toDirectory, const char* toName,
haiku_ino_t node)
{
if (!fromName || !toName)
return B_BAD_VALUE;

// If node is a mount point, we need to resolve it to the mounted
// volume's root node.
haiku_dev_t nodeDevice = device;
{
auto& vfsService = System::GetInstance().GetVfsService();
auto lock = vfsService.Lock();
std::string path;
if (vfsService.GetEntryRef(EntryRef(device, node), path))
{
auto nodeVfsDevice = vfsService.GetDevice(path).lock();

if (nodeVfsDevice)
{
nodeDevice = nodeVfsDevice->GetId();
node = nodeVfsDevice->GetInfo().root;
}
}
}
// vfs_resolve_vnode_to_covering_vnode(device, node, &nodeDevice, &node);

std::unique_lock<std::recursive_mutex> locker(_recursiveLock);

// get the lists of all interested listeners
interested_monitor_listener_list interestedListeners[4];
int32 interestedListenerCount = 0;
// ... for the volume
_GetInterestedVolumeListeners(device, B_WATCH_NAME,
interestedListeners, interestedListenerCount);
// ... for the node
_GetInterestedMonitorListeners(nodeDevice, node, B_WATCH_NAME,
interestedListeners, interestedListenerCount);
// ... for the source directory
_GetInterestedMonitorListeners(device, fromDirectory, B_WATCH_DIRECTORY,
interestedListeners, interestedListenerCount);
// ... for the target directory
if (toDirectory != fromDirectory)
{
_GetInterestedMonitorListeners(device, toDirectory, B_WATCH_DIRECTORY,
interestedListeners, interestedListenerCount);
}

if (interestedListenerCount == 0)
return B_OK;

// there are interested listeners: construct the message and send it
char messageBuffer[1024];
KMessage message;
message.SetTo(messageBuffer, sizeof(messageBuffer), B_NODE_MONITOR);
message.AddInt32("opcode", B_ENTRY_MOVED);
message.AddInt32("device", device);
message.AddInt64("from directory", fromDirectory);
message.AddInt64("to directory", toDirectory);
message.AddInt32("node device", nodeDevice); // Haiku only
message.AddInt64("node", node);
message.AddString("from name", fromName); // Haiku only
message.AddString("name", toName);

return _SendNotificationMessage(message, interestedListeners,
interestedListenerCount);
}

status_t NodeMonitorService::NotifyUnmount(haiku_dev_t device)
{
std::unique_lock<std::recursive_mutex> locker(_recursiveLock);
Expand Down Expand Up @@ -529,7 +642,7 @@ status_t NodeMonitorService::UpdateUserListener(const std::shared_ptr<IoContext>

std::shared_ptr<node_monitor> monitor;
status_t status = _GetMonitor(context, device, node, true, &monitor,
(flags & B_WATCH_VOLUME) != 0);
(flags & B_WATCH_VOLUME) != 0);
if (status < B_OK)
return status;

Expand Down
17 changes: 13 additions & 4 deletions hyclone_server/server_nodemonitor.h
Expand Up @@ -10,7 +10,7 @@
#include "BeDefs.h"
#include "server_notifications.h"

struct node_monitor;
struct monitor_listener;
struct interested_monitor_listener_list;

class KMessage;
Expand All @@ -19,6 +19,15 @@ class IoContext;

class UserNodeListener;

typedef std::list<std::shared_ptr<monitor_listener>> MonitorListenerList;

struct node_monitor
{
haiku_dev_t device;
haiku_ino_t node;
MonitorListenerList listeners;
};

struct monitor_listener
{
std::list<std::shared_ptr<monitor_listener>>::iterator context_link;
Expand Down Expand Up @@ -101,14 +110,14 @@ class NodeMonitorService : public NotificationService
status_t InitCheck();

status_t NotifyEntryCreatedOrRemoved(int32 opcode, haiku_dev_t device,
haiku_ino_t directory, const std::string& name, haiku_ino_t node);
haiku_ino_t directory, const char* name, haiku_ino_t node);
status_t NotifyEntryMoved(haiku_dev_t device, haiku_ino_t fromDirectory,
const std::string& fromName, haiku_ino_t toDirectory, const std::string& toName,
const char* fromName, haiku_ino_t toDirectory, const char* toName,
haiku_ino_t node);
status_t NotifyStatChanged(haiku_dev_t device, haiku_ino_t directory, haiku_ino_t node,
uint32 statFields);
status_t NotifyAttributeChanged(haiku_dev_t device, haiku_ino_t directory,
haiku_ino_t node, const std::string& attribute, int32 cause);
haiku_ino_t node, const char* attribute, int32 cause);
status_t NotifyUnmount(haiku_dev_t device);
status_t NotifyMount(haiku_dev_t device, haiku_dev_t parentDevice,
haiku_ino_t parentDirectory);
Expand Down

0 comments on commit 3241db0

Please sign in to comment.