mirror of
https://github.com/SerenityOS/serenity.git
synced 2025-01-23 09:51:57 -05: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,21 +1,37 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020, the SerenityOS developers.
|
* Copyright (c) 2020-2021, the SerenityOS developers.
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/EnumBits.h>
|
||||||
#include <AK/Types.h>
|
#include <AK/Types.h>
|
||||||
|
|
||||||
|
#ifdef KERNEL
|
||||||
|
# include <LibC/limits.h>
|
||||||
|
#else
|
||||||
|
# include <limits.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
struct [[gnu::packed]] InodeWatcherEvent {
|
struct [[gnu::packed]] InodeWatcherEvent {
|
||||||
enum class Type {
|
enum class Type : u32 {
|
||||||
Invalid = 0,
|
Invalid = 0,
|
||||||
Modified,
|
MetadataModified = 1 << 0,
|
||||||
ChildAdded,
|
ContentModified = 1 << 1,
|
||||||
ChildRemoved,
|
Deleted = 1 << 2,
|
||||||
|
ChildCreated = 1 << 3,
|
||||||
|
ChildDeleted = 1 << 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
int watch_descriptor { 0 };
|
||||||
Type type { Type::Invalid };
|
Type type { Type::Invalid };
|
||||||
unsigned inode_index { 0 };
|
size_t name_length { 0 };
|
||||||
|
// This is a VLA which is written during the read() from the descriptor.
|
||||||
|
const char name[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
AK_ENUM_BITWISE_OPERATORS(InodeWatcherEvent::Type);
|
||||||
|
|
||||||
|
constexpr unsigned MAXIMUM_EVENT_SIZE = sizeof(InodeWatcherEvent) + NAME_MAX + 1;
|
||||||
|
|
18
Kernel/API/InodeWatcherFlags.h
Normal file
18
Kernel/API/InodeWatcherFlags.h
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021, sin-ack <sin-ack@protonmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/EnumBits.h>
|
||||||
|
#include <AK/Types.h>
|
||||||
|
|
||||||
|
enum class InodeWatcherFlags : u32 {
|
||||||
|
None = 0,
|
||||||
|
Nonblock = 1 << 0,
|
||||||
|
CloseOnExec = 1 << 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
AK_ENUM_BITWISE_OPERATORS(InodeWatcherFlags);
|
|
@ -27,154 +27,156 @@ typedef u32 socklen_t;
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
#define ENUMERATE_SYSCALLS(S) \
|
#define ENUMERATE_SYSCALLS(S) \
|
||||||
S(yield) \
|
S(yield) \
|
||||||
S(open) \
|
S(open) \
|
||||||
S(close) \
|
S(close) \
|
||||||
S(read) \
|
S(read) \
|
||||||
S(lseek) \
|
S(lseek) \
|
||||||
S(kill) \
|
S(kill) \
|
||||||
S(getuid) \
|
S(getuid) \
|
||||||
S(exit) \
|
S(exit) \
|
||||||
S(geteuid) \
|
S(geteuid) \
|
||||||
S(getegid) \
|
S(getegid) \
|
||||||
S(getgid) \
|
S(getgid) \
|
||||||
S(getpid) \
|
S(getpid) \
|
||||||
S(getppid) \
|
S(getppid) \
|
||||||
S(getresuid) \
|
S(getresuid) \
|
||||||
S(getresgid) \
|
S(getresgid) \
|
||||||
S(waitid) \
|
S(waitid) \
|
||||||
S(mmap) \
|
S(mmap) \
|
||||||
S(munmap) \
|
S(munmap) \
|
||||||
S(get_dir_entries) \
|
S(get_dir_entries) \
|
||||||
S(getcwd) \
|
S(getcwd) \
|
||||||
S(gettimeofday) \
|
S(gettimeofday) \
|
||||||
S(gethostname) \
|
S(gethostname) \
|
||||||
S(sethostname) \
|
S(sethostname) \
|
||||||
S(chdir) \
|
S(chdir) \
|
||||||
S(uname) \
|
S(uname) \
|
||||||
S(set_mmap_name) \
|
S(set_mmap_name) \
|
||||||
S(readlink) \
|
S(readlink) \
|
||||||
S(write) \
|
S(write) \
|
||||||
S(ttyname) \
|
S(ttyname) \
|
||||||
S(stat) \
|
S(stat) \
|
||||||
S(getsid) \
|
S(getsid) \
|
||||||
S(setsid) \
|
S(setsid) \
|
||||||
S(getpgid) \
|
S(getpgid) \
|
||||||
S(setpgid) \
|
S(setpgid) \
|
||||||
S(getpgrp) \
|
S(getpgrp) \
|
||||||
S(fork) \
|
S(fork) \
|
||||||
S(execve) \
|
S(execve) \
|
||||||
S(dup2) \
|
S(dup2) \
|
||||||
S(sigaction) \
|
S(sigaction) \
|
||||||
S(umask) \
|
S(umask) \
|
||||||
S(getgroups) \
|
S(getgroups) \
|
||||||
S(setgroups) \
|
S(setgroups) \
|
||||||
S(sigreturn) \
|
S(sigreturn) \
|
||||||
S(sigprocmask) \
|
S(sigprocmask) \
|
||||||
S(sigpending) \
|
S(sigpending) \
|
||||||
S(pipe) \
|
S(pipe) \
|
||||||
S(killpg) \
|
S(killpg) \
|
||||||
S(seteuid) \
|
S(seteuid) \
|
||||||
S(setegid) \
|
S(setegid) \
|
||||||
S(setuid) \
|
S(setuid) \
|
||||||
S(setgid) \
|
S(setgid) \
|
||||||
S(setreuid) \
|
S(setreuid) \
|
||||||
S(setresuid) \
|
S(setresuid) \
|
||||||
S(setresgid) \
|
S(setresgid) \
|
||||||
S(alarm) \
|
S(alarm) \
|
||||||
S(fstat) \
|
S(fstat) \
|
||||||
S(access) \
|
S(access) \
|
||||||
S(fcntl) \
|
S(fcntl) \
|
||||||
S(ioctl) \
|
S(ioctl) \
|
||||||
S(mkdir) \
|
S(mkdir) \
|
||||||
S(times) \
|
S(times) \
|
||||||
S(utime) \
|
S(utime) \
|
||||||
S(sync) \
|
S(sync) \
|
||||||
S(ptsname) \
|
S(ptsname) \
|
||||||
S(select) \
|
S(select) \
|
||||||
S(unlink) \
|
S(unlink) \
|
||||||
S(poll) \
|
S(poll) \
|
||||||
S(rmdir) \
|
S(rmdir) \
|
||||||
S(chmod) \
|
S(chmod) \
|
||||||
S(socket) \
|
S(socket) \
|
||||||
S(bind) \
|
S(bind) \
|
||||||
S(accept) \
|
S(accept) \
|
||||||
S(listen) \
|
S(listen) \
|
||||||
S(connect) \
|
S(connect) \
|
||||||
S(link) \
|
S(link) \
|
||||||
S(chown) \
|
S(chown) \
|
||||||
S(fchmod) \
|
S(fchmod) \
|
||||||
S(symlink) \
|
S(symlink) \
|
||||||
S(sendmsg) \
|
S(sendmsg) \
|
||||||
S(recvmsg) \
|
S(recvmsg) \
|
||||||
S(getsockopt) \
|
S(getsockopt) \
|
||||||
S(setsockopt) \
|
S(setsockopt) \
|
||||||
S(create_thread) \
|
S(create_thread) \
|
||||||
S(gettid) \
|
S(gettid) \
|
||||||
S(donate) \
|
S(donate) \
|
||||||
S(rename) \
|
S(rename) \
|
||||||
S(ftruncate) \
|
S(ftruncate) \
|
||||||
S(exit_thread) \
|
S(exit_thread) \
|
||||||
S(mknod) \
|
S(mknod) \
|
||||||
S(writev) \
|
S(writev) \
|
||||||
S(beep) \
|
S(beep) \
|
||||||
S(getsockname) \
|
S(getsockname) \
|
||||||
S(getpeername) \
|
S(getpeername) \
|
||||||
S(socketpair) \
|
S(socketpair) \
|
||||||
S(sched_setparam) \
|
S(sched_setparam) \
|
||||||
S(sched_getparam) \
|
S(sched_getparam) \
|
||||||
S(fchown) \
|
S(fchown) \
|
||||||
S(halt) \
|
S(halt) \
|
||||||
S(reboot) \
|
S(reboot) \
|
||||||
S(mount) \
|
S(mount) \
|
||||||
S(umount) \
|
S(umount) \
|
||||||
S(dump_backtrace) \
|
S(dump_backtrace) \
|
||||||
S(dbgputch) \
|
S(dbgputch) \
|
||||||
S(dbgputstr) \
|
S(dbgputstr) \
|
||||||
S(watch_file) \
|
S(create_inode_watcher) \
|
||||||
S(mprotect) \
|
S(inode_watcher_add_watch) \
|
||||||
S(realpath) \
|
S(inode_watcher_remove_watch) \
|
||||||
S(get_process_name) \
|
S(mprotect) \
|
||||||
S(fchdir) \
|
S(realpath) \
|
||||||
S(getrandom) \
|
S(get_process_name) \
|
||||||
S(getkeymap) \
|
S(fchdir) \
|
||||||
S(setkeymap) \
|
S(getrandom) \
|
||||||
S(clock_gettime) \
|
S(getkeymap) \
|
||||||
S(clock_settime) \
|
S(setkeymap) \
|
||||||
S(clock_nanosleep) \
|
S(clock_gettime) \
|
||||||
S(join_thread) \
|
S(clock_settime) \
|
||||||
S(module_load) \
|
S(clock_nanosleep) \
|
||||||
S(module_unload) \
|
S(join_thread) \
|
||||||
S(detach_thread) \
|
S(module_load) \
|
||||||
S(set_thread_name) \
|
S(module_unload) \
|
||||||
S(get_thread_name) \
|
S(detach_thread) \
|
||||||
S(madvise) \
|
S(set_thread_name) \
|
||||||
S(purge) \
|
S(get_thread_name) \
|
||||||
S(profiling_enable) \
|
S(madvise) \
|
||||||
S(profiling_disable) \
|
S(purge) \
|
||||||
S(profiling_free_buffer) \
|
S(profiling_enable) \
|
||||||
S(futex) \
|
S(profiling_disable) \
|
||||||
S(chroot) \
|
S(profiling_free_buffer) \
|
||||||
S(pledge) \
|
S(futex) \
|
||||||
S(unveil) \
|
S(chroot) \
|
||||||
S(perf_event) \
|
S(pledge) \
|
||||||
S(shutdown) \
|
S(unveil) \
|
||||||
S(get_stack_bounds) \
|
S(perf_event) \
|
||||||
S(ptrace) \
|
S(shutdown) \
|
||||||
S(sendfd) \
|
S(get_stack_bounds) \
|
||||||
S(recvfd) \
|
S(ptrace) \
|
||||||
S(sysconf) \
|
S(sendfd) \
|
||||||
S(set_process_name) \
|
S(recvfd) \
|
||||||
S(disown) \
|
S(sysconf) \
|
||||||
S(adjtime) \
|
S(set_process_name) \
|
||||||
S(allocate_tls) \
|
S(disown) \
|
||||||
S(prctl) \
|
S(adjtime) \
|
||||||
S(mremap) \
|
S(allocate_tls) \
|
||||||
S(set_coredump_metadata) \
|
S(prctl) \
|
||||||
S(anon_create) \
|
S(mremap) \
|
||||||
S(msyscall) \
|
S(set_coredump_metadata) \
|
||||||
S(readv) \
|
S(anon_create) \
|
||||||
|
S(msyscall) \
|
||||||
|
S(readv) \
|
||||||
S(emuctl)
|
S(emuctl)
|
||||||
|
|
||||||
namespace Syscall {
|
namespace Syscall {
|
||||||
|
@ -442,6 +444,12 @@ struct SC_set_coredump_metadata_params {
|
||||||
StringArgument value;
|
StringArgument value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct SC_inode_watcher_add_watch_params {
|
||||||
|
int fd;
|
||||||
|
StringArgument user_path;
|
||||||
|
u32 event_mask;
|
||||||
|
};
|
||||||
|
|
||||||
void initialize();
|
void initialize();
|
||||||
int sync();
|
int sync();
|
||||||
|
|
||||||
|
|
|
@ -198,7 +198,7 @@ set(KERNEL_SOURCES
|
||||||
Syscalls/unveil.cpp
|
Syscalls/unveil.cpp
|
||||||
Syscalls/utime.cpp
|
Syscalls/utime.cpp
|
||||||
Syscalls/waitid.cpp
|
Syscalls/waitid.cpp
|
||||||
Syscalls/watch_file.cpp
|
Syscalls/inode_watcher.cpp
|
||||||
Syscalls/write.cpp
|
Syscalls/write.cpp
|
||||||
TTY/MasterPTY.cpp
|
TTY/MasterPTY.cpp
|
||||||
TTY/PTYMultiplexer.cpp
|
TTY/PTYMultiplexer.cpp
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||||
|
* Copyright (c) 2021, sin-ack <sin-ack@protonmail.com>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -1033,6 +1034,8 @@ KResultOr<ssize_t> Ext2FSInode::write_bytes(off_t offset, ssize_t count, const U
|
||||||
nwritten += num_bytes_to_copy;
|
nwritten += num_bytes_to_copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
did_modify_contents();
|
||||||
|
|
||||||
dbgln_if(EXT2_VERY_DEBUG, "Ext2FSInode[{}]::write_bytes(): After write, i_size={}, i_blocks={} ({} blocks in list)", identifier(), size(), m_raw_inode.i_blocks, m_block_list.size());
|
dbgln_if(EXT2_VERY_DEBUG, "Ext2FSInode[{}]::write_bytes(): After write, i_size={}, i_blocks={} ({} blocks in list)", identifier(), size(), m_raw_inode.i_blocks, m_block_list.size());
|
||||||
return nwritten;
|
return nwritten;
|
||||||
}
|
}
|
||||||
|
@ -1208,7 +1211,7 @@ KResult Ext2FSInode::add_child(Inode& child, const StringView& name, mode_t mode
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
m_lookup_cache.set(name, child.index());
|
m_lookup_cache.set(name, child.index());
|
||||||
did_add_child(child.identifier());
|
did_add_child(child.identifier(), name);
|
||||||
return KSuccess;
|
return KSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1245,7 +1248,7 @@ KResult Ext2FSInode::remove_child(const StringView& name)
|
||||||
if (result.is_error())
|
if (result.is_error())
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
did_remove_child(child_id);
|
did_remove_child(child_id, name);
|
||||||
return KSuccess;
|
return KSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1674,10 +1677,15 @@ KResult Ext2FSInode::decrement_link_count()
|
||||||
if (fs().is_readonly())
|
if (fs().is_readonly())
|
||||||
return EROFS;
|
return EROFS;
|
||||||
VERIFY(m_raw_inode.i_links_count);
|
VERIFY(m_raw_inode.i_links_count);
|
||||||
|
|
||||||
--m_raw_inode.i_links_count;
|
--m_raw_inode.i_links_count;
|
||||||
|
set_metadata_dirty(true);
|
||||||
|
if (m_raw_inode.i_links_count == 0)
|
||||||
|
did_delete_self();
|
||||||
|
|
||||||
if (ref_count() == 1 && m_raw_inode.i_links_count == 0)
|
if (ref_count() == 1 && m_raw_inode.i_links_count == 0)
|
||||||
fs().uncache_inode(index());
|
fs().uncache_inode(index());
|
||||||
set_metadata_dirty(true);
|
|
||||||
return KSuccess;
|
return KSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -109,6 +109,7 @@ public:
|
||||||
virtual bool is_block_device() const { return false; }
|
virtual bool is_block_device() const { return false; }
|
||||||
virtual bool is_character_device() const { return false; }
|
virtual bool is_character_device() const { return false; }
|
||||||
virtual bool is_socket() const { return false; }
|
virtual bool is_socket() const { return false; }
|
||||||
|
virtual bool is_inode_watcher() const { return false; }
|
||||||
|
|
||||||
virtual FileBlockCondition& block_condition() { return m_block_condition; }
|
virtual FileBlockCondition& block_condition() { return m_block_condition; }
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||||
|
* Copyright (c) 2021, sin-ack <sin-ack@protonmail.com>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -13,6 +14,7 @@
|
||||||
#include <Kernel/FileSystem/FileDescription.h>
|
#include <Kernel/FileSystem/FileDescription.h>
|
||||||
#include <Kernel/FileSystem/FileSystem.h>
|
#include <Kernel/FileSystem/FileSystem.h>
|
||||||
#include <Kernel/FileSystem/InodeFile.h>
|
#include <Kernel/FileSystem/InodeFile.h>
|
||||||
|
#include <Kernel/FileSystem/InodeWatcher.h>
|
||||||
#include <Kernel/Net/Socket.h>
|
#include <Kernel/Net/Socket.h>
|
||||||
#include <Kernel/Process.h>
|
#include <Kernel/Process.h>
|
||||||
#include <Kernel/TTY/MasterPTY.h>
|
#include <Kernel/TTY/MasterPTY.h>
|
||||||
|
@ -295,6 +297,25 @@ TTY* FileDescription::tty()
|
||||||
return static_cast<TTY*>(m_file.ptr());
|
return static_cast<TTY*>(m_file.ptr());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FileDescription::is_inode_watcher() const
|
||||||
|
{
|
||||||
|
return m_file->is_inode_watcher();
|
||||||
|
}
|
||||||
|
|
||||||
|
const InodeWatcher* FileDescription::inode_watcher() const
|
||||||
|
{
|
||||||
|
if (!is_inode_watcher())
|
||||||
|
return nullptr;
|
||||||
|
return static_cast<const InodeWatcher*>(m_file.ptr());
|
||||||
|
}
|
||||||
|
|
||||||
|
InodeWatcher* FileDescription::inode_watcher()
|
||||||
|
{
|
||||||
|
if (!is_inode_watcher())
|
||||||
|
return nullptr;
|
||||||
|
return static_cast<InodeWatcher*>(m_file.ptr());
|
||||||
|
}
|
||||||
|
|
||||||
bool FileDescription::is_master_pty() const
|
bool FileDescription::is_master_pty() const
|
||||||
{
|
{
|
||||||
return m_file->is_master_pty();
|
return m_file->is_master_pty();
|
||||||
|
|
|
@ -77,6 +77,10 @@ public:
|
||||||
const TTY* tty() const;
|
const TTY* tty() const;
|
||||||
TTY* tty();
|
TTY* tty();
|
||||||
|
|
||||||
|
bool is_inode_watcher() const;
|
||||||
|
const InodeWatcher* inode_watcher() const;
|
||||||
|
InodeWatcher* inode_watcher();
|
||||||
|
|
||||||
bool is_master_pty() const;
|
bool is_master_pty() const;
|
||||||
const MasterPTY* master_pty() const;
|
const MasterPTY* master_pty() const;
|
||||||
MasterPTY* master_pty();
|
MasterPTY* master_pty();
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||||
|
* Copyright (c) 2021, sin-ack <sin-ack@protonmail.com>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -109,6 +110,10 @@ Inode::~Inode()
|
||||||
{
|
{
|
||||||
ScopedSpinLock all_inodes_lock(s_all_inodes_lock);
|
ScopedSpinLock all_inodes_lock(s_all_inodes_lock);
|
||||||
all_with_lock().remove(this);
|
all_with_lock().remove(this);
|
||||||
|
|
||||||
|
for (auto& watcher : m_watchers) {
|
||||||
|
watcher->unregister_by_inode({}, identifier());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Inode::will_be_destroyed()
|
void Inode::will_be_destroyed()
|
||||||
|
@ -211,24 +216,47 @@ void Inode::set_metadata_dirty(bool metadata_dirty)
|
||||||
// FIXME: Maybe we should hook into modification events somewhere else, I'm not sure where.
|
// 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.
|
// We don't always end up on this particular code path, for instance when writing to an ext2fs file.
|
||||||
for (auto& watcher : m_watchers) {
|
for (auto& watcher : m_watchers) {
|
||||||
watcher->notify_inode_event({}, InodeWatcherEvent::Type::Modified);
|
watcher->notify_inode_event({}, identifier(), InodeWatcherEvent::Type::MetadataModified);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Inode::did_add_child(const InodeIdentifier& child_id)
|
void Inode::did_add_child(InodeIdentifier const&, String const& name)
|
||||||
{
|
{
|
||||||
Locker locker(m_lock);
|
Locker locker(m_lock);
|
||||||
|
|
||||||
for (auto& watcher : m_watchers) {
|
for (auto& watcher : m_watchers) {
|
||||||
watcher->notify_child_added({}, child_id);
|
watcher->notify_inode_event({}, identifier(), InodeWatcherEvent::Type::ChildCreated, name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Inode::did_remove_child(const InodeIdentifier& child_id)
|
void Inode::did_remove_child(InodeIdentifier const&, String const& name)
|
||||||
|
{
|
||||||
|
Locker locker(m_lock);
|
||||||
|
|
||||||
|
if (name == "." || name == "..") {
|
||||||
|
// These are just aliases and are not interesting to userspace.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& watcher : m_watchers) {
|
||||||
|
watcher->notify_inode_event({}, identifier(), InodeWatcherEvent::Type::ChildDeleted, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Inode::did_modify_contents()
|
||||||
{
|
{
|
||||||
Locker locker(m_lock);
|
Locker locker(m_lock);
|
||||||
for (auto& watcher : m_watchers) {
|
for (auto& watcher : m_watchers) {
|
||||||
watcher->notify_child_removed({}, child_id);
|
watcher->notify_inode_event({}, identifier(), InodeWatcherEvent::Type::ContentModified);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Inode::did_delete_self()
|
||||||
|
{
|
||||||
|
Locker locker(m_lock);
|
||||||
|
for (auto& watcher : m_watchers) {
|
||||||
|
watcher->notify_inode_event({}, identifier(), InodeWatcherEvent::Type::Deleted);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||||
|
* Copyright (c) 2021, sin-ack <sin-ack@protonmail.com>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -111,8 +112,10 @@ protected:
|
||||||
void set_metadata_dirty(bool);
|
void set_metadata_dirty(bool);
|
||||||
KResult prepare_to_write_data();
|
KResult prepare_to_write_data();
|
||||||
|
|
||||||
void did_add_child(const InodeIdentifier&);
|
void did_add_child(InodeIdentifier const& child_id, String const& name);
|
||||||
void did_remove_child(const InodeIdentifier&);
|
void did_remove_child(InodeIdentifier const& child_id, String const& name);
|
||||||
|
void did_modify_contents();
|
||||||
|
void did_delete_self();
|
||||||
|
|
||||||
mutable Lock m_lock { "Inode" };
|
mutable Lock m_lock { "Inode" };
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||||
|
* Copyright (c) 2021, sin-ack <sin-ack@protonmail.com>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -7,53 +8,59 @@
|
||||||
#include <AK/Memory.h>
|
#include <AK/Memory.h>
|
||||||
#include <Kernel/FileSystem/Inode.h>
|
#include <Kernel/FileSystem/Inode.h>
|
||||||
#include <Kernel/FileSystem/InodeWatcher.h>
|
#include <Kernel/FileSystem/InodeWatcher.h>
|
||||||
|
#include <Kernel/Process.h>
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
NonnullRefPtr<InodeWatcher> InodeWatcher::create(Inode& inode)
|
NonnullRefPtr<InodeWatcher> InodeWatcher::create()
|
||||||
{
|
{
|
||||||
return adopt_ref(*new InodeWatcher(inode));
|
return adopt_ref(*new InodeWatcher);
|
||||||
}
|
|
||||||
|
|
||||||
InodeWatcher::InodeWatcher(Inode& inode)
|
|
||||||
: m_inode(inode)
|
|
||||||
{
|
|
||||||
inode.register_watcher({}, *this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
InodeWatcher::~InodeWatcher()
|
InodeWatcher::~InodeWatcher()
|
||||||
{
|
{
|
||||||
if (auto inode = m_inode.strong_ref())
|
(void)close();
|
||||||
inode->unregister_watcher({}, *this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InodeWatcher::can_read(const FileDescription&, size_t) const
|
bool InodeWatcher::can_read(const FileDescription&, size_t) const
|
||||||
{
|
{
|
||||||
return !m_queue.is_empty() || !m_inode;
|
Locker locker(m_lock);
|
||||||
}
|
return !m_queue.is_empty();
|
||||||
|
|
||||||
bool InodeWatcher::can_write(const FileDescription&, size_t) const
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
KResultOr<size_t> InodeWatcher::read(FileDescription&, u64, UserOrKernelBuffer& buffer, size_t buffer_size)
|
KResultOr<size_t> InodeWatcher::read(FileDescription&, u64, UserOrKernelBuffer& buffer, size_t buffer_size)
|
||||||
{
|
{
|
||||||
Locker locker(m_lock);
|
Locker locker(m_lock);
|
||||||
VERIFY(!m_queue.is_empty() || !m_inode);
|
if (m_queue.is_empty())
|
||||||
|
// can_read will catch the blocking case.
|
||||||
if (!m_inode)
|
return EAGAIN;
|
||||||
return 0;
|
|
||||||
|
|
||||||
auto event = m_queue.dequeue();
|
auto event = m_queue.dequeue();
|
||||||
|
|
||||||
if (buffer_size < sizeof(InodeWatcherEvent))
|
size_t name_length = event.path.length() + 1;
|
||||||
return buffer_size;
|
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;
|
return (ssize_t)data_bytes;
|
||||||
});
|
});
|
||||||
if (nwritten < 0)
|
if (nwritten < 0)
|
||||||
|
@ -62,37 +69,94 @@ KResultOr<size_t> InodeWatcher::read(FileDescription&, u64, UserOrKernelBuffer&
|
||||||
return bytes_to_write;
|
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
|
String InodeWatcher::absolute_path(const FileDescription&) const
|
||||||
{
|
{
|
||||||
if (auto inode = m_inode.strong_ref())
|
return String::formatted("InodeWatcher:({})", m_wd_to_watches.size());
|
||||||
return String::formatted("InodeWatcher:{}", inode->identifier().to_string());
|
|
||||||
return "InodeWatcher:(gone)";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
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();
|
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);
|
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);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||||
|
* Copyright (c) 2021, sin-ack <sin-ack@protonmail.com>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -7,38 +8,76 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <AK/Badge.h>
|
#include <AK/Badge.h>
|
||||||
|
#include <AK/Checked.h>
|
||||||
#include <AK/CircularQueue.h>
|
#include <AK/CircularQueue.h>
|
||||||
#include <AK/WeakPtr.h>
|
#include <AK/HashMap.h>
|
||||||
|
#include <AK/NonnullOwnPtr.h>
|
||||||
#include <Kernel/API/InodeWatcherEvent.h>
|
#include <Kernel/API/InodeWatcherEvent.h>
|
||||||
#include <Kernel/FileSystem/File.h>
|
#include <Kernel/FileSystem/File.h>
|
||||||
#include <Kernel/Lock.h>
|
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
class Inode;
|
class Inode;
|
||||||
|
|
||||||
|
// A specific description of a watch.
|
||||||
|
struct WatchDescription {
|
||||||
|
int wd;
|
||||||
|
Inode& inode;
|
||||||
|
unsigned event_mask;
|
||||||
|
|
||||||
|
static NonnullOwnPtr<WatchDescription> create(int wd, Inode& inode, unsigned event_mask)
|
||||||
|
{
|
||||||
|
return adopt_own(*new WatchDescription(wd, inode, event_mask));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
WatchDescription(int wd, Inode& inode, unsigned event_mask)
|
||||||
|
: wd(wd)
|
||||||
|
, inode(inode)
|
||||||
|
, event_mask(event_mask)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class InodeWatcher final : public File {
|
class InodeWatcher final : public File {
|
||||||
public:
|
public:
|
||||||
static NonnullRefPtr<InodeWatcher> create(Inode&);
|
static NonnullRefPtr<InodeWatcher> create();
|
||||||
virtual ~InodeWatcher() override;
|
virtual ~InodeWatcher() override;
|
||||||
|
|
||||||
virtual bool can_read(const FileDescription&, size_t) const override;
|
virtual bool can_read(const FileDescription&, size_t) const override;
|
||||||
virtual bool can_write(const FileDescription&, size_t) const override;
|
|
||||||
virtual KResultOr<size_t> read(FileDescription&, u64, UserOrKernelBuffer&, size_t) override;
|
virtual KResultOr<size_t> read(FileDescription&, u64, UserOrKernelBuffer&, size_t) override;
|
||||||
virtual KResultOr<size_t> write(FileDescription&, u64, const UserOrKernelBuffer&, size_t) override;
|
// Can't write to an inode watcher.
|
||||||
|
virtual bool can_write(const FileDescription&, size_t) const override { return true; }
|
||||||
|
virtual KResultOr<size_t> write(FileDescription&, u64, const UserOrKernelBuffer&, size_t) override { return EIO; }
|
||||||
|
virtual KResult close() override;
|
||||||
|
|
||||||
virtual String absolute_path(const FileDescription&) const override;
|
virtual String absolute_path(const FileDescription&) const override;
|
||||||
virtual const char* class_name() const override { return "InodeWatcher"; };
|
virtual const char* class_name() const override { return "InodeWatcher"; };
|
||||||
|
virtual bool is_inode_watcher() const override { return true; }
|
||||||
|
|
||||||
void notify_inode_event(Badge<Inode>, InodeWatcherEvent::Type);
|
void notify_inode_event(Badge<Inode>, InodeIdentifier, InodeWatcherEvent::Type, String const& name = {});
|
||||||
void notify_child_added(Badge<Inode>, const InodeIdentifier& child_id);
|
|
||||||
void notify_child_removed(Badge<Inode>, const InodeIdentifier& child_id);
|
KResultOr<int> register_inode(Inode&, unsigned event_mask);
|
||||||
|
KResult unregister_by_wd(int);
|
||||||
|
void unregister_by_inode(Badge<Inode>, InodeIdentifier);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit InodeWatcher(Inode&);
|
explicit InodeWatcher() { }
|
||||||
|
|
||||||
Lock m_lock;
|
mutable Lock m_lock;
|
||||||
WeakPtr<Inode> m_inode;
|
|
||||||
CircularQueue<InodeWatcherEvent, 32> m_queue;
|
struct Event {
|
||||||
|
int wd { 0 };
|
||||||
|
InodeWatcherEvent::Type type { InodeWatcherEvent::Type::Invalid };
|
||||||
|
String path;
|
||||||
|
};
|
||||||
|
CircularQueue<Event, 32> m_queue;
|
||||||
|
Checked<int> m_wd_counter { 1 };
|
||||||
|
|
||||||
|
// NOTE: These two hashmaps provide two different ways of reaching the same
|
||||||
|
// watch description, so they will overlap.
|
||||||
|
HashMap<int, NonnullOwnPtr<WatchDescription>> m_wd_to_watches;
|
||||||
|
HashMap<InodeIdentifier, WatchDescription*> m_inode_to_watches;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -188,6 +188,8 @@ KResultOr<ssize_t> TmpFSInode::write_bytes(off_t offset, ssize_t size, const Use
|
||||||
|
|
||||||
if (!buffer.read(m_content->data() + offset, size)) // TODO: partial reads?
|
if (!buffer.read(m_content->data() + offset, size)) // TODO: partial reads?
|
||||||
return EFAULT;
|
return EFAULT;
|
||||||
|
|
||||||
|
did_modify_contents();
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,7 +286,7 @@ KResult TmpFSInode::add_child(Inode& child, const StringView& name, mode_t)
|
||||||
return ENAMETOOLONG;
|
return ENAMETOOLONG;
|
||||||
|
|
||||||
m_children.set(name, { name, static_cast<TmpFSInode&>(child) });
|
m_children.set(name, { name, static_cast<TmpFSInode&>(child) });
|
||||||
did_add_child(child.identifier());
|
did_add_child(child.identifier(), name);
|
||||||
return KSuccess;
|
return KSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -300,8 +302,9 @@ KResult TmpFSInode::remove_child(const StringView& name)
|
||||||
if (it == m_children.end())
|
if (it == m_children.end())
|
||||||
return ENOENT;
|
return ENOENT;
|
||||||
auto child_id = it->value.inode->identifier();
|
auto child_id = it->value.inode->identifier();
|
||||||
|
it->value.inode->did_delete_self();
|
||||||
m_children.remove(it);
|
m_children.remove(it);
|
||||||
did_remove_child(child_id);
|
did_remove_child(child_id, name);
|
||||||
return KSuccess;
|
return KSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -251,7 +251,9 @@ public:
|
||||||
KResultOr<int> sys$beep();
|
KResultOr<int> sys$beep();
|
||||||
KResultOr<int> sys$get_process_name(Userspace<char*> buffer, size_t buffer_size);
|
KResultOr<int> sys$get_process_name(Userspace<char*> buffer, size_t buffer_size);
|
||||||
KResultOr<int> sys$set_process_name(Userspace<const char*> user_name, size_t user_name_length);
|
KResultOr<int> sys$set_process_name(Userspace<const char*> user_name, size_t user_name_length);
|
||||||
KResultOr<int> sys$watch_file(Userspace<const char*> path, size_t path_length);
|
KResultOr<int> sys$create_inode_watcher(u32 flags);
|
||||||
|
KResultOr<int> sys$inode_watcher_add_watch(Userspace<const Syscall::SC_inode_watcher_add_watch_params*> user_params);
|
||||||
|
KResultOr<int> sys$inode_watcher_remove_watch(int fd, int wd);
|
||||||
KResultOr<int> sys$dbgputch(u8);
|
KResultOr<int> sys$dbgputch(u8);
|
||||||
KResultOr<int> sys$dbgputstr(Userspace<const u8*>, int length);
|
KResultOr<int> sys$dbgputstr(Userspace<const u8*>, int length);
|
||||||
KResultOr<int> sys$dump_backtrace();
|
KResultOr<int> sys$dump_backtrace();
|
||||||
|
|
88
Kernel/Syscalls/inode_watcher.cpp
Normal file
88
Kernel/Syscalls/inode_watcher.cpp
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||||
|
* Copyright (c) 2021, sin-ack <sin-ack@protonmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Kernel/API/InodeWatcherFlags.h>
|
||||||
|
#include <Kernel/FileSystem/Custody.h>
|
||||||
|
#include <Kernel/FileSystem/FileDescription.h>
|
||||||
|
#include <Kernel/FileSystem/InodeWatcher.h>
|
||||||
|
#include <Kernel/Process.h>
|
||||||
|
|
||||||
|
namespace Kernel {
|
||||||
|
|
||||||
|
KResultOr<int> Process::sys$create_inode_watcher(u32 flags)
|
||||||
|
{
|
||||||
|
REQUIRE_PROMISE(rpath);
|
||||||
|
|
||||||
|
int fd = alloc_fd();
|
||||||
|
if (fd < 0)
|
||||||
|
return fd;
|
||||||
|
|
||||||
|
auto description_or_error = FileDescription::create(*InodeWatcher::create());
|
||||||
|
if (description_or_error.is_error())
|
||||||
|
return description_or_error.error();
|
||||||
|
|
||||||
|
m_fds[fd].set(description_or_error.release_value());
|
||||||
|
m_fds[fd].description()->set_readable(true);
|
||||||
|
|
||||||
|
if (flags & static_cast<unsigned>(InodeWatcherFlags::Nonblock))
|
||||||
|
m_fds[fd].description()->set_blocking(false);
|
||||||
|
if (flags & static_cast<unsigned>(InodeWatcherFlags::CloseOnExec))
|
||||||
|
m_fds[fd].set_flags(m_fds[fd].flags() | FD_CLOEXEC);
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
KResultOr<int> Process::sys$inode_watcher_add_watch(Userspace<const Syscall::SC_inode_watcher_add_watch_params*> user_params)
|
||||||
|
{
|
||||||
|
REQUIRE_PROMISE(rpath);
|
||||||
|
|
||||||
|
Syscall::SC_inode_watcher_add_watch_params params;
|
||||||
|
if (!copy_from_user(¶ms, user_params))
|
||||||
|
return EFAULT;
|
||||||
|
|
||||||
|
auto description = file_description(params.fd);
|
||||||
|
if (!description)
|
||||||
|
return EBADF;
|
||||||
|
if (!description->is_inode_watcher())
|
||||||
|
return EBADF;
|
||||||
|
auto inode_watcher = description->inode_watcher();
|
||||||
|
|
||||||
|
auto path = get_syscall_path_argument(params.user_path.characters, params.user_path.length);
|
||||||
|
if (path.is_error())
|
||||||
|
return path.error();
|
||||||
|
|
||||||
|
auto custody_or_error = VFS::the().resolve_path(path.value(), current_directory());
|
||||||
|
if (custody_or_error.is_error())
|
||||||
|
return custody_or_error.error();
|
||||||
|
|
||||||
|
auto& custody = custody_or_error.value();
|
||||||
|
if (!custody->inode().fs().supports_watchers())
|
||||||
|
return ENOTSUP;
|
||||||
|
|
||||||
|
auto wd_or_error = inode_watcher->register_inode(custody->inode(), params.event_mask);
|
||||||
|
if (wd_or_error.is_error())
|
||||||
|
return wd_or_error.error();
|
||||||
|
return wd_or_error.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
KResultOr<int> Process::sys$inode_watcher_remove_watch(int fd, int wd)
|
||||||
|
{
|
||||||
|
auto description = file_description(fd);
|
||||||
|
if (!description)
|
||||||
|
return EBADF;
|
||||||
|
if (!description->is_inode_watcher())
|
||||||
|
return EBADF;
|
||||||
|
auto inode_watcher = description->inode_watcher();
|
||||||
|
|
||||||
|
auto result = inode_watcher->unregister_by_wd(wd);
|
||||||
|
if (result.is_error())
|
||||||
|
return result;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,44 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <Kernel/FileSystem/Custody.h>
|
|
||||||
#include <Kernel/FileSystem/FileDescription.h>
|
|
||||||
#include <Kernel/FileSystem/InodeWatcher.h>
|
|
||||||
#include <Kernel/Process.h>
|
|
||||||
|
|
||||||
namespace Kernel {
|
|
||||||
|
|
||||||
KResultOr<int> Process::sys$watch_file(Userspace<const char*> user_path, size_t path_length)
|
|
||||||
{
|
|
||||||
REQUIRE_PROMISE(rpath);
|
|
||||||
auto path = get_syscall_path_argument(user_path, path_length);
|
|
||||||
if (path.is_error())
|
|
||||||
return path.error();
|
|
||||||
|
|
||||||
auto custody_or_error = VFS::the().resolve_path(path.value(), current_directory());
|
|
||||||
if (custody_or_error.is_error())
|
|
||||||
return custody_or_error.error();
|
|
||||||
|
|
||||||
auto& custody = custody_or_error.value();
|
|
||||||
auto& inode = custody->inode();
|
|
||||||
|
|
||||||
if (!inode.fs().supports_watchers())
|
|
||||||
return ENOTSUP;
|
|
||||||
|
|
||||||
int fd = alloc_fd();
|
|
||||||
if (fd < 0)
|
|
||||||
return fd;
|
|
||||||
|
|
||||||
auto description = FileDescription::create(*InodeWatcher::create(inode));
|
|
||||||
if (description.is_error())
|
|
||||||
return description.error();
|
|
||||||
|
|
||||||
m_fds[fd].set(description.release_value());
|
|
||||||
m_fds[fd].description()->set_readable(true);
|
|
||||||
return fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Loading…
Reference in a new issue