mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 10:18:11 +00:00
Kernel: Implement multi-watch InodeWatcher :^)
This patch modifies InodeWatcher to switch to a one watcher, multiple watches architecture. The following changes have been made: - The watch_file syscall is removed, and in its place the create_iwatcher, iwatcher_add_watch and iwatcher_remove_watch calls have been added. - InodeWatcher now holds multiple WatchDescriptions for each file that is being watched. - The InodeWatcher file descriptor can be read from to receive events on all watched files. Co-authored-by: Gunnar Beutner <gunnar@beutner.name>
This commit is contained in:
parent
2de11b0dc8
commit
fe5ca6ca27
16 changed files with 521 additions and 262 deletions
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* Copyright (c) 2021, sin-ack <sin-ack@protonmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
@ -7,53 +8,59 @@
|
|||
#include <AK/Memory.h>
|
||||
#include <Kernel/FileSystem/Inode.h>
|
||||
#include <Kernel/FileSystem/InodeWatcher.h>
|
||||
#include <Kernel/Process.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
NonnullRefPtr<InodeWatcher> InodeWatcher::create(Inode& inode)
|
||||
NonnullRefPtr<InodeWatcher> InodeWatcher::create()
|
||||
{
|
||||
return adopt_ref(*new InodeWatcher(inode));
|
||||
}
|
||||
|
||||
InodeWatcher::InodeWatcher(Inode& inode)
|
||||
: m_inode(inode)
|
||||
{
|
||||
inode.register_watcher({}, *this);
|
||||
return adopt_ref(*new InodeWatcher);
|
||||
}
|
||||
|
||||
InodeWatcher::~InodeWatcher()
|
||||
{
|
||||
if (auto inode = m_inode.strong_ref())
|
||||
inode->unregister_watcher({}, *this);
|
||||
(void)close();
|
||||
}
|
||||
|
||||
bool InodeWatcher::can_read(const FileDescription&, size_t) const
|
||||
{
|
||||
return !m_queue.is_empty() || !m_inode;
|
||||
}
|
||||
|
||||
bool InodeWatcher::can_write(const FileDescription&, size_t) const
|
||||
{
|
||||
return true;
|
||||
Locker locker(m_lock);
|
||||
return !m_queue.is_empty();
|
||||
}
|
||||
|
||||
KResultOr<size_t> InodeWatcher::read(FileDescription&, u64, UserOrKernelBuffer& buffer, size_t buffer_size)
|
||||
{
|
||||
Locker locker(m_lock);
|
||||
VERIFY(!m_queue.is_empty() || !m_inode);
|
||||
|
||||
if (!m_inode)
|
||||
return 0;
|
||||
if (m_queue.is_empty())
|
||||
// can_read will catch the blocking case.
|
||||
return EAGAIN;
|
||||
|
||||
auto event = m_queue.dequeue();
|
||||
|
||||
if (buffer_size < sizeof(InodeWatcherEvent))
|
||||
return buffer_size;
|
||||
size_t name_length = event.path.length() + 1;
|
||||
size_t bytes_to_write = sizeof(InodeWatcherEvent);
|
||||
if (!event.path.is_null())
|
||||
bytes_to_write += name_length;
|
||||
|
||||
size_t bytes_to_write = min(buffer_size, sizeof(event));
|
||||
if (buffer_size < bytes_to_write)
|
||||
return EINVAL;
|
||||
|
||||
ssize_t nwritten = buffer.write_buffered<MAXIMUM_EVENT_SIZE>(bytes_to_write, [&](u8* data, size_t data_bytes) {
|
||||
size_t offset = 0;
|
||||
|
||||
memcpy(data + offset, &event.wd, sizeof(InodeWatcherEvent::watch_descriptor));
|
||||
offset += sizeof(InodeWatcherEvent::watch_descriptor);
|
||||
memcpy(data + offset, &event.type, sizeof(InodeWatcherEvent::type));
|
||||
offset += sizeof(InodeWatcherEvent::type);
|
||||
|
||||
if (!event.path.is_null()) {
|
||||
memcpy(data + offset, &name_length, sizeof(InodeWatcherEvent::name_length));
|
||||
offset += sizeof(InodeWatcherEvent::name_length);
|
||||
memcpy(data + offset, event.path.characters(), name_length);
|
||||
} else {
|
||||
memset(data + offset, 0, sizeof(InodeWatcherEvent::name_length));
|
||||
}
|
||||
|
||||
ssize_t nwritten = buffer.write_buffered<sizeof(event)>(bytes_to_write, [&](u8* data, size_t data_bytes) {
|
||||
memcpy(data, &event, bytes_to_write);
|
||||
return (ssize_t)data_bytes;
|
||||
});
|
||||
if (nwritten < 0)
|
||||
|
@ -62,37 +69,94 @@ KResultOr<size_t> InodeWatcher::read(FileDescription&, u64, UserOrKernelBuffer&
|
|||
return bytes_to_write;
|
||||
}
|
||||
|
||||
KResultOr<size_t> InodeWatcher::write(FileDescription&, u64, const UserOrKernelBuffer&, size_t)
|
||||
KResult InodeWatcher::close()
|
||||
{
|
||||
return EIO;
|
||||
Locker locker(m_lock);
|
||||
|
||||
for (auto& entry : m_wd_to_watches) {
|
||||
auto& inode = const_cast<Inode&>(entry.value->inode);
|
||||
inode.unregister_watcher({}, *this);
|
||||
}
|
||||
|
||||
m_wd_to_watches.clear();
|
||||
m_inode_to_watches.clear();
|
||||
return KSuccess;
|
||||
}
|
||||
|
||||
String InodeWatcher::absolute_path(const FileDescription&) const
|
||||
{
|
||||
if (auto inode = m_inode.strong_ref())
|
||||
return String::formatted("InodeWatcher:{}", inode->identifier().to_string());
|
||||
return "InodeWatcher:(gone)";
|
||||
return String::formatted("InodeWatcher:({})", m_wd_to_watches.size());
|
||||
}
|
||||
|
||||
void InodeWatcher::notify_inode_event(Badge<Inode>, InodeWatcherEvent::Type event_type)
|
||||
void InodeWatcher::notify_inode_event(Badge<Inode>, InodeIdentifier inode_id, InodeWatcherEvent::Type event_type, String const& name)
|
||||
{
|
||||
Locker locker(m_lock);
|
||||
m_queue.enqueue({ event_type });
|
||||
|
||||
auto it = m_inode_to_watches.find(inode_id);
|
||||
if (it == m_inode_to_watches.end())
|
||||
return;
|
||||
|
||||
auto& watcher = *it->value;
|
||||
if (!(watcher.event_mask & static_cast<unsigned>(event_type)))
|
||||
return;
|
||||
|
||||
m_queue.enqueue({ watcher.wd, event_type, name });
|
||||
evaluate_block_conditions();
|
||||
}
|
||||
|
||||
void InodeWatcher::notify_child_added(Badge<Inode>, const InodeIdentifier& child_id)
|
||||
KResultOr<int> InodeWatcher::register_inode(Inode& inode, unsigned event_mask)
|
||||
{
|
||||
Locker locker(m_lock);
|
||||
m_queue.enqueue({ InodeWatcherEvent::Type::ChildAdded, child_id.index().value() });
|
||||
evaluate_block_conditions();
|
||||
|
||||
if (m_inode_to_watches.find(inode.identifier()) != m_inode_to_watches.end())
|
||||
return EEXIST;
|
||||
|
||||
int wd;
|
||||
do {
|
||||
wd = m_wd_counter.value();
|
||||
|
||||
m_wd_counter++;
|
||||
if (m_wd_counter.has_overflow())
|
||||
m_wd_counter = 1;
|
||||
} while (m_wd_to_watches.find(wd) != m_wd_to_watches.end());
|
||||
|
||||
auto description = WatchDescription::create(wd, inode, event_mask);
|
||||
m_inode_to_watches.set(inode.identifier(), description.ptr());
|
||||
m_wd_to_watches.set(wd, move(description));
|
||||
|
||||
inode.register_watcher({}, *this);
|
||||
return wd;
|
||||
}
|
||||
|
||||
void InodeWatcher::notify_child_removed(Badge<Inode>, const InodeIdentifier& child_id)
|
||||
KResult InodeWatcher::unregister_by_wd(int wd)
|
||||
{
|
||||
Locker locker(m_lock);
|
||||
m_queue.enqueue({ InodeWatcherEvent::Type::ChildRemoved, child_id.index().value() });
|
||||
evaluate_block_conditions();
|
||||
|
||||
auto it = m_wd_to_watches.find(wd);
|
||||
if (it == m_wd_to_watches.end())
|
||||
return ENOENT;
|
||||
|
||||
auto& inode = it->value->inode;
|
||||
inode.unregister_watcher({}, *this);
|
||||
|
||||
m_inode_to_watches.remove(inode.identifier());
|
||||
m_wd_to_watches.remove(it);
|
||||
|
||||
return KSuccess;
|
||||
}
|
||||
|
||||
void InodeWatcher::unregister_by_inode(Badge<Inode>, InodeIdentifier identifier)
|
||||
{
|
||||
Locker locker(m_lock);
|
||||
|
||||
auto it = m_inode_to_watches.find(identifier);
|
||||
if (it == m_inode_to_watches.end())
|
||||
return;
|
||||
|
||||
// NOTE: no need to call unregister_watcher here, the Inode calls us.
|
||||
|
||||
m_inode_to_watches.remove(identifier);
|
||||
m_wd_to_watches.remove(it->value->wd);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue