1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-06-01 08:28:11 +00:00

Kernel: Add a mechanism for listening for changes to an inode.

The syscall is quite simple:

    int watch_file(const char* path, int path_length);

It returns a file descriptor referring to a "InodeWatcher" object in the
kernel. It becomes readable whenever something changes about the inode.

Currently this is implemented by hooking the "metadata dirty bit" in
Inode which isn't perfect, but it's a start. :^)
This commit is contained in:
Andreas Kling 2019-07-22 20:01:11 +02:00
parent a9adf4c95b
commit c8e2bb5605
12 changed files with 200 additions and 3 deletions

View file

@ -1,6 +1,7 @@
#include <AK/NonnullRefPtrVector.h>
#include <AK/StringBuilder.h>
#include <Kernel/FileSystem/Inode.h>
#include <Kernel/FileSystem/InodeWatcher.h>
#include <Kernel/Net/LocalSocket.h>
#include <Kernel/VM/VMObject.h>
@ -131,3 +132,33 @@ bool Inode::unbind_socket()
m_socket = nullptr;
return true;
}
void Inode::register_watcher(Badge<InodeWatcher>, InodeWatcher& watcher)
{
LOCKER(m_lock);
ASSERT(!m_watchers.contains(&watcher));
m_watchers.set(&watcher);
}
void Inode::unregister_watcher(Badge<InodeWatcher>, InodeWatcher& watcher)
{
LOCKER(m_lock);
ASSERT(m_watchers.contains(&watcher));
m_watchers.remove(&watcher);
}
void Inode::set_metadata_dirty(bool metadata_dirty)
{
if (m_metadata_dirty == metadata_dirty)
return;
m_metadata_dirty = metadata_dirty;
if (m_metadata_dirty) {
// FIXME: Maybe we should hook into modification events somewhere else, I'm not sure where.
// We don't always end up on this particular code path, for instance when writing to an ext2fs file.
LOCKER(m_lock);
for (auto& watcher : m_watchers) {
watcher->notify_inode_event({}, InodeWatcher::Event::Type::Modified);
}
}
}

View file

@ -11,10 +11,11 @@
#include <Kernel/Lock.h>
class FileDescription;
class InodeWatcher;
class LocalSocket;
class VMObject;
class Inode : public RefCounted<Inode> {
class Inode : public RefCounted<Inode>, public Weakable<Inode> {
friend class VFS;
friend class FS;
@ -73,9 +74,12 @@ public:
static void sync();
void register_watcher(Badge<InodeWatcher>, InodeWatcher&);
void unregister_watcher(Badge<InodeWatcher>, InodeWatcher&);
protected:
Inode(FS& fs, unsigned index);
void set_metadata_dirty(bool b) { m_metadata_dirty = b; }
void set_metadata_dirty(bool);
void inode_contents_changed(off_t, ssize_t, const u8*);
void inode_size_changed(size_t old_size, size_t new_size);
@ -86,5 +90,6 @@ private:
unsigned m_index { 0 };
WeakPtr<VMObject> m_vmo;
RefPtr<LocalSocket> m_socket;
HashTable<InodeWatcher*> m_watchers;
bool m_metadata_dirty { false };
};

View file

@ -0,0 +1,60 @@
#include <Kernel/FileSystem/Inode.h>
#include <Kernel/FileSystem/InodeWatcher.h>
NonnullRefPtr<InodeWatcher> InodeWatcher::create(Inode& inode)
{
return adopt(*new InodeWatcher(inode));
}
InodeWatcher::InodeWatcher(Inode& inode)
: m_inode(inode.make_weak_ptr())
{
inode.register_watcher({}, *this);
}
InodeWatcher::~InodeWatcher()
{
if (RefPtr<Inode> safe_inode = m_inode.ptr())
safe_inode->unregister_watcher({}, *this);
}
bool InodeWatcher::can_read(FileDescription&) const
{
return !m_queue.is_empty() || !m_inode;
}
bool InodeWatcher::can_write(FileDescription&) const
{
return true;
}
ssize_t InodeWatcher::read(FileDescription&, u8* buffer, ssize_t buffer_size)
{
ASSERT(!m_queue.is_empty() || !m_inode);
if (!m_inode)
return 0;
// FIXME: What should we do if the output buffer is too small?
ASSERT(buffer_size >= (int)sizeof(Event));
auto event = m_queue.dequeue();
memcpy(buffer, &event, sizeof(event));
return sizeof(event);
}
ssize_t InodeWatcher::write(FileDescription&, const u8*, ssize_t)
{
return -EIO;
}
String InodeWatcher::absolute_path(const FileDescription&) const
{
if (!m_inode)
return "InodeWatcher:(gone)";
return String::format("InodeWatcher:%s", m_inode->identifier().to_string().characters());
}
void InodeWatcher::notify_inode_event(Badge<Inode>, Event::Type event_type)
{
m_queue.enqueue({ event_type });
}

View file

@ -0,0 +1,38 @@
#pragma once
#include <AK/Badge.h>
#include <AK/CircularQueue.h>
#include <AK/WeakPtr.h>
#include <Kernel/FileSystem/File.h>
class Inode;
class InodeWatcher final : public File {
public:
static NonnullRefPtr<InodeWatcher> create(Inode&);
virtual ~InodeWatcher() override;
struct Event {
enum class Type {
Invalid = 0,
Modified,
};
Type type { Type::Invalid };
};
virtual bool can_read(FileDescription&) const override;
virtual bool can_write(FileDescription&) const override;
virtual ssize_t read(FileDescription&, u8*, ssize_t) override;
virtual ssize_t write(FileDescription&, const u8*, ssize_t) override;
virtual String absolute_path(const FileDescription&) const override;
virtual const char* class_name() const override { return "InodeWatcher"; };
void notify_inode_event(Badge<Inode>, Event::Type);
private:
explicit InodeWatcher(Inode&);
WeakPtr<Inode> m_inode;
CircularQueue<Event, 32> m_queue;
};