From c51b4b57422d54a0f6b488fd837a2cfd5352b45b Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Tue, 9 Jul 2019 14:16:46 +0200 Subject: [PATCH] New VFS plugin for emulating POSIX pipes Add a new plugin for creating pipes between pairs of VFS handles. It is intended to replace the libc_pipe plugin, one of the last remaining libc plugins. In contrast to the libc_pipe plugin, this plugin defers cross-handle notification until I/O signal handling rather than block and unblock readers using a semaphore. This is a performance regression in the case of multiple threads blocking on a pipe, but shall be an intermediate mechanism pending renovations within the libc VFS and threading layers. As a side effect, threads blocked on a pipe might not be resumed until the main thread suspends and dispatches I/O signals. The "test-libc_pipe" test has been adjusted to use the VFS pipe plugin and tests both local pipes and pipes hosted remotely in the VFS server. Merge adaptations (such as EOF handling, adjustment to VFS/libc interface changes) by Norman Feske. Fix #2303 --- repos/gems/lib/mk/vfs_pipe.mk | 5 + repos/gems/recipes/src/vfs_pipe/content.mk | 9 + repos/gems/recipes/src/vfs_pipe/hash | 1 + repos/gems/recipes/src/vfs_pipe/used_apis | 4 + repos/gems/src/lib/vfs/pipe/README | 12 + repos/gems/src/lib/vfs/pipe/plugin.cc | 618 ++++++++++++++++++ repos/gems/src/lib/vfs/pipe/target.mk | 2 + .../recipes/pkg/test-libc_pipe/archives | 2 + .../recipes/pkg/test-libc_pipe/runtime | 51 +- .../recipes/src/test-libc_pipe/content.mk | 9 - .../src/lib/libc/internal/vfs_plugin.h | 14 +- repos/libports/src/lib/libc/vfs_plugin.cc | 66 ++ repos/libports/src/test/libc_pipe/main.cc | 3 +- repos/libports/src/test/libc_pipe/target.mk | 2 +- repos/os/include/vfs/types.h | 4 + 15 files changed, 780 insertions(+), 22 deletions(-) create mode 100644 repos/gems/lib/mk/vfs_pipe.mk create mode 100644 repos/gems/recipes/src/vfs_pipe/content.mk create mode 100644 repos/gems/recipes/src/vfs_pipe/hash create mode 100644 repos/gems/recipes/src/vfs_pipe/used_apis create mode 100644 repos/gems/src/lib/vfs/pipe/README create mode 100644 repos/gems/src/lib/vfs/pipe/plugin.cc create mode 100644 repos/gems/src/lib/vfs/pipe/target.mk diff --git a/repos/gems/lib/mk/vfs_pipe.mk b/repos/gems/lib/mk/vfs_pipe.mk new file mode 100644 index 000000000..9b3ef9a3d --- /dev/null +++ b/repos/gems/lib/mk/vfs_pipe.mk @@ -0,0 +1,5 @@ +SRC_CC = plugin.cc + +vpath %.cc $(REP_DIR)/src/lib/vfs/pipe + +SHARED_LIB = yes diff --git a/repos/gems/recipes/src/vfs_pipe/content.mk b/repos/gems/recipes/src/vfs_pipe/content.mk new file mode 100644 index 000000000..9e0246b49 --- /dev/null +++ b/repos/gems/recipes/src/vfs_pipe/content.mk @@ -0,0 +1,9 @@ +MIRROR_FROM_REP_DIR := lib/mk/vfs_pipe.mk src/lib/vfs/pipe + +content: $(MIRROR_FROM_REP_DIR) LICENSE + +$(MIRROR_FROM_REP_DIR): + $(mirror_from_rep_dir) + +LICENSE: + cp $(GENODE_DIR)/LICENSE $@ diff --git a/repos/gems/recipes/src/vfs_pipe/hash b/repos/gems/recipes/src/vfs_pipe/hash new file mode 100644 index 000000000..d8ea98806 --- /dev/null +++ b/repos/gems/recipes/src/vfs_pipe/hash @@ -0,0 +1 @@ +2019-06-04 ecacc703584c04e70085ad12628ee40885e9e50c diff --git a/repos/gems/recipes/src/vfs_pipe/used_apis b/repos/gems/recipes/src/vfs_pipe/used_apis new file mode 100644 index 000000000..6069d7955 --- /dev/null +++ b/repos/gems/recipes/src/vfs_pipe/used_apis @@ -0,0 +1,4 @@ +base +os +so +vfs diff --git a/repos/gems/src/lib/vfs/pipe/README b/repos/gems/src/lib/vfs/pipe/README new file mode 100644 index 000000000..95e40e6fc --- /dev/null +++ b/repos/gems/src/lib/vfs/pipe/README @@ -0,0 +1,12 @@ +The VFS pipe plugin exposes a control file for creating pipes and a set of pipe +directories. Opening and reading the "/new" returns a relative path to a +directory. That directory represents a pipe and contains an "in" and "out" file +for writing and reading respectively to the pipe. + +Reads and writes are non-blocking and will complete short operations without +error, with the exception of reads on an empty pipe, which return READ_QUEUED. +The read and write capacity of a pipe may be queried by stat'ing the size of +"out" and "in" files. + +When all "in" and "out" handles on a pipe as well as the initial handle on "new" +are closed, the pipe is destroyed. diff --git a/repos/gems/src/lib/vfs/pipe/plugin.cc b/repos/gems/src/lib/vfs/pipe/plugin.cc new file mode 100644 index 000000000..bf880f53a --- /dev/null +++ b/repos/gems/src/lib/vfs/pipe/plugin.cc @@ -0,0 +1,618 @@ +/* + * \brief VFS pipe plugin + * \author Emery Hemingway + * \date 2019-05-29 + */ + +/* + * Copyright (C) 2019 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#include +#include +#include +#include + +namespace Vfs_pipe { + using namespace Vfs; + typedef Vfs::Directory_service::Open_result Open_result; + typedef Vfs::File_io_service::Write_result Write_result; + typedef Vfs::File_io_service::Read_result Read_result; + typedef Genode::Path<32> Path; + + enum { PIPE_BUF_SIZE = 8192U }; + typedef Genode::Ring_buffer Pipe_buffer; + + struct Pipe_handle; + typedef Genode::Fifo_element Handle_element; + typedef Genode::Fifo Handle_fifo; + typedef Genode::Registry::Element Pipe_handle_registry_element; + typedef Genode::Registry Pipe_handle_registry; + + class Pipe; + typedef Genode::Id_space Pipe_space; + + struct New_pipe_handle; + + class File_system; +} + + +struct Vfs_pipe::Pipe_handle : Vfs::Vfs_handle, private Pipe_handle_registry_element +{ + Pipe &pipe; + + Handle_element io_progress_elem { *this }; + Handle_element read_ready_elem { *this }; + + bool const writer; + + Pipe_handle(Vfs::File_system &fs, + Genode::Allocator &alloc, + unsigned flags, + Pipe_handle_registry ®istry, + Pipe &p) + : + Vfs::Vfs_handle(fs, fs, alloc, flags), + Pipe_handle_registry_element(registry, *this), + pipe(p), + writer(flags == Directory_service::OPEN_MODE_WRONLY) + { } + + virtual ~Pipe_handle(); + + Write_result write(const char *buf, + file_size count, + file_size &out_count); + + Read_result read(char *buf, + file_size count, + file_size &out_count); + + bool read_ready(); + bool notify_read_ready(); +}; + + +struct Vfs_pipe::Pipe +{ + Genode::Allocator &alloc; + Pipe_space::Element space_elem; + Pipe_buffer buffer { }; + Pipe_handle_registry registry { }; + Handle_fifo io_progress_waiters { }; + Handle_fifo read_ready_waiters { }; + unsigned num_writers = 0; + + Genode::Signal_context_capability ¬ify_sigh; + + bool new_handle_active { true }; + + Pipe(Genode::Allocator &alloc, Pipe_space &space, + Genode::Signal_context_capability ¬ify_sigh) + : alloc(alloc), space_elem(*this, space), notify_sigh(notify_sigh) { } + + ~Pipe() { } + + typedef Genode::String<8> Name; + Name name() const + { + return Name(space_elem.id().value); + } + + /** + * Check if pipe is referenced, if not, destroy + */ + void cleanup() + { + bool alive = new_handle_active; + if (!alive) + registry.for_each([&alive] (Pipe_handle&) { + alive = true; }); + if (!alive) + destroy(alloc, this); + } + + /** + * Remove "/new" handle reference + */ + void remove_new_handle() { + new_handle_active = false; } + + /** + * Detach a handle + */ + void remove(Pipe_handle &handle) + { + if (handle.io_progress_elem.enqueued()) + io_progress_waiters.remove(handle.io_progress_elem); + if (handle.read_ready_elem.enqueued()) + read_ready_waiters.remove(handle.read_ready_elem); + } + + /** + * Open a write or read handle + */ + Open_result open(Vfs::File_system &fs, + Path const &filename, + Vfs::Vfs_handle **handle, + Genode::Allocator &alloc) + { + if (filename == "/in") { + *handle = new (alloc) + Pipe_handle(fs, alloc, Directory_service::OPEN_MODE_WRONLY, registry, *this); + num_writers++; + return Open_result::OPEN_OK; + } + + if (filename == "/out") { + *handle = new (alloc) + Pipe_handle(fs, alloc, Directory_service::OPEN_MODE_RDONLY, registry, *this); + return Open_result::OPEN_OK; + } + + return Open_result::OPEN_ERR_UNACCESSIBLE; + } + + /** + * Use a signal as a hack to defer notifications + * until the "io_progress_handler". + */ + void submit_signal() { + Genode::Signal_transmitter(notify_sigh).submit(); } + + /** + * Notify handles waiting for activity + */ + void notify() + { + io_progress_waiters.dequeue_all([] (Handle_element &elem) { + elem.object().io_progress_response(); }); + read_ready_waiters.dequeue_all([] (Handle_element &elem) { + elem.object().read_ready_response(); }); + } + + Write_result write(Pipe_handle &handle, + const char *buf, file_size count, + file_size &out_count) + { + file_size out = 0; + bool notify = buffer.empty(); + + while (out < count && 0 < buffer.avail_capacity()) { + buffer.add(*(buf++)); + ++out; + } + + out_count = out; + if (out < count) + io_progress_waiters.enqueue(handle.io_progress_elem); + + if (notify) + submit_signal(); + + return Write_result::WRITE_OK; + } + + Read_result read(Pipe_handle &handle, + char *buf, file_size count, + file_size &out_count) + { + bool notify = buffer.avail_capacity() == 0; + + file_size out = 0; + while (out < count && !buffer.empty()) { + *(buf++) = buffer.get(); + ++out; + } + + out_count = out; + if (!out) { + + if (num_writers == 0) + return Read_result::READ_OK; /* EOF */ + + io_progress_waiters.enqueue(handle.io_progress_elem); + return Read_result::READ_QUEUED; + } + + if (notify) + submit_signal(); + + return Read_result::READ_OK; + } +}; + + +Vfs_pipe::Pipe_handle::~Pipe_handle() { + pipe.remove(*this); } + + +Vfs_pipe::Write_result +Vfs_pipe::Pipe_handle::write(const char *buf, + file_size count, + file_size &out_count) { + return Pipe_handle::pipe.write(*this, buf, count, out_count); } + + +Vfs_pipe::Read_result +Vfs_pipe::Pipe_handle::read(char *buf, + file_size count, + file_size &out_count) { + return Pipe_handle::pipe.read(*this, buf, count, out_count); } + + +bool +Vfs_pipe::Pipe_handle::read_ready() { + return !pipe.buffer.empty(); } + + +bool +Vfs_pipe::Pipe_handle::notify_read_ready() +{ + if (!read_ready_elem.enqueued()) + pipe.read_ready_waiters.enqueue(read_ready_elem); + return true; +} + + +struct Vfs_pipe::New_pipe_handle : Vfs::Vfs_handle +{ + Pipe &pipe; + + New_pipe_handle(Vfs::File_system &fs, + Genode::Allocator &alloc, + unsigned flags, + Pipe_space &pipe_space, + Genode::Signal_context_capability ¬ify_sigh) + : Vfs::Vfs_handle(fs, fs, alloc, flags), + pipe(*(new (alloc) Pipe(alloc, pipe_space, notify_sigh))) + { } + + ~New_pipe_handle() + { + pipe.remove_new_handle(); + } + + Read_result read(char *buf, + file_size count, + file_size &out_count) + { + auto name = pipe.name(); + if (name.length() < count) { + memcpy(buf, name.string(), name.length()); + out_count = name.length(); + return Read_result::READ_OK; + } + return Read_result::READ_ERR_INVALID; + } +}; + + +class Vfs_pipe::File_system : public Vfs::File_system +{ + private: + + Pipe_space _pipe_space { }; + + /* + * XXX: a hack to defer cross-thread notifications at + * the libc until the io_progress handler + */ + Genode::Io_signal_handler _notify_handler; + Genode::Signal_context_capability _notify_cap { _notify_handler }; + + void _notify_any() + { + _pipe_space.for_each([] (Pipe &pipe) { + pipe.notify(); }); + } + + public: + + File_system(Vfs::Env &env) + : _notify_handler(env.env().ep(), *this, &File_system::_notify_any) { } + + const char* type() override { return "pipe"; } + + /*********************** + ** Directory service ** + ***********************/ + + Genode::Dataspace_capability dataspace(char const*) override { + return Genode::Dataspace_capability(); } + + void release(char const*, Dataspace_capability) override { } + + Open_result open(const char *cpath, + unsigned mode, + Vfs::Vfs_handle **handle, + Genode::Allocator &alloc) override + { + Path path(cpath); + + if (path == "/new") { + if ((Directory_service::OPEN_MODE_ACCMODE & mode) == Directory_service::OPEN_MODE_WRONLY) + return Open_result::OPEN_ERR_NO_PERM; + *handle = new (alloc) + New_pipe_handle(*this, alloc, mode, _pipe_space, _notify_cap); + return Open_result::OPEN_OK; + } + + path.strip_last_element(); + if (!path.has_single_element()) + return Open_result::OPEN_ERR_UNACCESSIBLE; + + Pipe_space::Id id { ~0UL }; + if (!ascii_to(path.last_element(), id.value)) + return Open_result::OPEN_ERR_UNACCESSIBLE; + + Open_result result = Open_result::OPEN_ERR_UNACCESSIBLE; + try { + _pipe_space.apply(id, [&] (Pipe &pipe) { + Path filename(cpath); + filename.keep_only_last_element(); + result = pipe.open(*this, filename, handle, alloc); + }); + } + catch (Pipe_space::Unknown_id) { } + + return result; + } + + Opendir_result opendir(char const *cpath, bool create, + Vfs_handle **handle, + Allocator &alloc) override + { + /* open dummy handles on directories */ + + if (create) return OPENDIR_ERR_PERMISSION_DENIED; + Path path(cpath); + + if (path == "/") { + *handle = new (alloc) + Vfs_handle(*this, *this, alloc, 0); + return OPENDIR_OK; + } + + Opendir_result result { OPENDIR_ERR_LOOKUP_FAILED }; + + if (path.has_single_element()) { + Pipe_space::Id id { ~0UL }; + if (ascii_to(path.last_element(), id.value)) try { + _pipe_space.apply(id, [&] (Pipe&) { + *handle = new (alloc) + Vfs_handle(*this, *this, alloc, 0); + result = OPENDIR_OK; + }); + } + catch (Pipe_space::Unknown_id) { } + } + + return result; + } + + void close(Vfs::Vfs_handle *vfs_handle) override + { + Pipe *pipe = nullptr; + if (Pipe_handle *handle = dynamic_cast(vfs_handle)) { + pipe = &handle->pipe; + if (handle->writer) + pipe->num_writers--; + } else + if (New_pipe_handle *handle = dynamic_cast(vfs_handle)) + pipe = &handle->pipe; + + destroy(vfs_handle->alloc(), vfs_handle); + + if (pipe) + pipe->cleanup(); + } + + Stat_result stat(const char *cpath, Stat &out) override + { + Stat_result result { STAT_ERR_NO_ENTRY }; + Path path(cpath); + + out = Stat { }; + + if (path == "/new") { + out = Stat { + .size = 1, + .type = Node_type::TRANSACTIONAL_FILE, + .rwx = Node_rwx::wo(), + .inode = Genode::addr_t(this), + .device = Genode::addr_t(this), + .modification_time = { } + }; + return STAT_OK; + } + + if (path.has_single_element()) { + Pipe_space::Id id { ~0UL }; + if (ascii_to(path.last_element(), id.value)) try { + _pipe_space.apply(id, [&] (Pipe &pipe) { + out = Stat { + .size = 2, + .type = Node_type::DIRECTORY, + .rwx = Node_rwx::rwx(), + .inode = Genode::addr_t(&pipe), + .device = Genode::addr_t(this), + .modification_time = { } + }; + result = STAT_OK; + }); + } + catch (Pipe_space::Unknown_id) { } + } else { + /* maybe this is /N/in or /N/out */ + path.strip_last_element(); + if (!path.has_single_element()) + /* too many directory levels */ + return result; + + Pipe_space::Id id { ~0UL }; + if (ascii_to(path.last_element(), id.value)) try { + _pipe_space.apply(id, [&] (Pipe &pipe) { + Path filename(cpath); + filename.keep_only_last_element(); + if (filename == "/in") { + out = Stat { + .size = file_size(pipe.buffer.avail_capacity()), + .type = Node_type::CONTINUOUS_FILE, + .rwx = Node_rwx::wo(), + .inode = Genode::addr_t(&pipe) + 1, + .device = Genode::addr_t(this), + .modification_time = { } + }; + result = STAT_OK; + } else + if (filename == "/out") { + out = Stat { + .size = file_size(PIPE_BUF_SIZE + - pipe.buffer.avail_capacity()), + .type = Node_type::CONTINUOUS_FILE, + .rwx = Node_rwx::ro(), + .inode = Genode::addr_t(&pipe) + 2, + .device = Genode::addr_t(this), + .modification_time = { } + }; + result = STAT_OK; + } + }); + } + catch (Pipe_space::Unknown_id) { } + } + + return result; + } + + Unlink_result unlink(const char*) override { + return UNLINK_ERR_NO_ENTRY; } + + Rename_result rename(const char*, const char*) override { + return RENAME_ERR_NO_ENTRY; } + + file_size num_dirent(char const *) override { + return ~0UL; } + + bool directory(char const *cpath) override + { + Path path(cpath); + if (path == "/") return true; + + if (!path.has_single_element()) + return Open_result::OPEN_ERR_UNACCESSIBLE; + + Pipe_space::Id id { ~0UL }; + if (!ascii_to(path.last_element(), id.value)) + return false; + + bool result = false; + try { + _pipe_space.apply(id, [&] (Pipe &) { + result = true; }); + } + catch (Pipe_space::Unknown_id) { } + + return result; + } + + const char* leaf_path(const char *cpath) override + { + Path path(cpath); + if (path == "/") return cpath; + if (path == "/new") return cpath; + + char const *result = nullptr; + if (!path.has_single_element()) { + /* maybe this is /N/in or /N/out */ + path.strip_last_element(); + if (!path.has_single_element()) + /* too many directory levels */ + return nullptr; + + Path filename(cpath); + filename.keep_only_last_element(); + if (filename != "/in" && filename != "/out") + /* not a pipe file */ + return nullptr; + } + + Pipe_space::Id id { ~0UL }; + if (ascii_to(path.last_element(), id.value)) try { + /* check if the pipe directory exists */ + _pipe_space.apply(id, [&] (Pipe &) { + result = cpath; }); + } + catch (Pipe_space::Unknown_id) { } + + return result; + } + + /********************** + ** File I/O service ** + **********************/ + + Write_result write(Vfs_handle *vfs_handle, + const char *src, file_size count, + file_size &out_count) override + { + if (Pipe_handle *handle = dynamic_cast(vfs_handle)) + return handle->write(src, count, out_count); + + return WRITE_ERR_INVALID; + } + + Read_result complete_read(Vfs_handle *vfs_handle, + char *dst, file_size count, + file_size &out_count) override + { + if (Pipe_handle *handle = dynamic_cast(vfs_handle)) + return handle->read(dst, count, out_count); + + if (New_pipe_handle *handle = dynamic_cast(vfs_handle)) + return handle->read(dst, count, out_count); + + return READ_ERR_INVALID; + } + + bool read_ready(Vfs_handle *vfs_handle) override + { + if (Pipe_handle *handle = dynamic_cast(vfs_handle)) + return handle->read_ready(); + return true; + } + + bool notify_read_ready(Vfs_handle *vfs_handle) override + { + if (Pipe_handle *handle = dynamic_cast(vfs_handle)) + return handle->notify_read_ready(); + return false; + } + + Ftruncate_result ftruncate(Vfs_handle*, file_size) override { + return FTRUNCATE_ERR_NO_PERM; } + + Sync_result complete_sync(Vfs_handle*) override { + return SYNC_OK; } +}; + + +extern "C" Vfs::File_system_factory *vfs_file_system_factory(void) +{ + struct Factory : Vfs::File_system_factory + { + Vfs::File_system *create(Vfs::Env &env, Genode::Xml_node) override + { + return new (env.alloc()) + Vfs_pipe::File_system(env); + } + }; + + static Factory f; + return &f; +} diff --git a/repos/gems/src/lib/vfs/pipe/target.mk b/repos/gems/src/lib/vfs/pipe/target.mk new file mode 100644 index 000000000..d2d34db6b --- /dev/null +++ b/repos/gems/src/lib/vfs/pipe/target.mk @@ -0,0 +1,2 @@ +TARGET = dummy-vfs_pipe +LIBS = vfs_pipe diff --git a/repos/libports/recipes/pkg/test-libc_pipe/archives b/repos/libports/recipes/pkg/test-libc_pipe/archives index b9781b6a5..2e4192267 100644 --- a/repos/libports/recipes/pkg/test-libc_pipe/archives +++ b/repos/libports/recipes/pkg/test-libc_pipe/archives @@ -2,4 +2,6 @@ _/src/init _/src/test-libc_pipe _/src/libc _/src/vfs +_/src/vfs_pipe _/src/posix +_/src/sequence diff --git a/repos/libports/recipes/pkg/test-libc_pipe/runtime b/repos/libports/recipes/pkg/test-libc_pipe/runtime index a23f40ffd..ed0eb8f17 100644 --- a/repos/libports/recipes/pkg/test-libc_pipe/runtime +++ b/repos/libports/recipes/pkg/test-libc_pipe/runtime @@ -2,40 +2,71 @@ - child "test-libc_pipe" exited with exit value 0 + child "sequence" exited with exit value 0 Error: - - + + + + - - - + - - + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/repos/libports/recipes/src/test-libc_pipe/content.mk b/repos/libports/recipes/src/test-libc_pipe/content.mk index 7af7881c3..85d6481dc 100644 --- a/repos/libports/recipes/src/test-libc_pipe/content.mk +++ b/repos/libports/recipes/src/test-libc_pipe/content.mk @@ -1,11 +1,2 @@ SRC_DIR = src/test/libc_pipe include $(GENODE_DIR)/repos/base/recipes/src/content.inc - -MIRROR_FROM_REP_DIR := include/libc-plugin \ - lib/mk/libc_pipe.mk \ - src/lib/libc_pipe - -content: $(MIRROR_FROM_REP_DIR) - -$(MIRROR_FROM_REP_DIR): - $(mirror_from_rep_dir) diff --git a/repos/libports/src/lib/libc/internal/vfs_plugin.h b/repos/libports/src/lib/libc/internal/vfs_plugin.h index d2016e852..81e8960a3 100644 --- a/repos/libports/src/lib/libc/internal/vfs_plugin.h +++ b/repos/libports/src/lib/libc/internal/vfs_plugin.h @@ -78,6 +78,7 @@ class Libc::Vfs_plugin : public Plugin Constructible _root_dir { }; Vfs::Io_response_handler &_response_handler; Update_mtime const _update_mtime; + bool const _pipe_configured; /** * Sync a handle and propagate errors @@ -102,6 +103,14 @@ class Libc::Vfs_plugin : public Plugin template void _with_info(File_descriptor &fd, FN const &fn); + static bool _init_pipe_configured(Xml_node config) + { + bool result = false; + config.with_sub_node("libc", [&] (Xml_node libc_node) { + result = libc_node.has_attribute("pipe"); }); + return result; + } + public: Vfs_plugin(Libc::Env &env, @@ -114,7 +123,8 @@ class Libc::Vfs_plugin : public Plugin _alloc(alloc), _root_fs(env.vfs()), _response_handler(handler), - _update_mtime(update_mtime) + _update_mtime(update_mtime), + _pipe_configured(_init_pipe_configured(config)) { if (config.has_sub_node("libc")) _root_dir.construct(vfs_env); @@ -134,6 +144,7 @@ class Libc::Vfs_plugin : public Plugin bool supports_access(const char *, int) override { return true; } bool supports_mkdir(const char *, mode_t) override { return true; } bool supports_open(const char *, int) override { return true; } + bool supports_pipe() override { return _pipe_configured; } bool supports_poll() override { return true; } bool supports_readlink(const char *, char *, ::size_t) override { return true; } bool supports_rename(const char *, const char *) override { return true; } @@ -167,6 +178,7 @@ class Libc::Vfs_plugin : public Plugin int ioctl(File_descriptor *, int , char *) override; ::off_t lseek(File_descriptor *fd, ::off_t offset, int whence) override; int mkdir(const char *, mode_t) override; + int pipe(File_descriptor *pipefdo[2]) override; bool poll(File_descriptor &fdo, struct pollfd &pfd) override; ssize_t read(File_descriptor *, void *, ::size_t) override; ssize_t readlink(const char *, char *, ::size_t) override; diff --git a/repos/libports/src/lib/libc/vfs_plugin.cc b/repos/libports/src/lib/libc/vfs_plugin.cc index 3cb9dbbb5..ef8a9c72c 100644 --- a/repos/libports/src/lib/libc/vfs_plugin.cc +++ b/repos/libports/src/lib/libc/vfs_plugin.cc @@ -166,6 +166,13 @@ namespace Libc { char const *string() const { return _value.string(); } }; + char const *config_pipe() __attribute__((weak)); + char const *config_pipe() + { + static Config_attr attr("pipe", ""); + return attr.string(); + } + char const *config_rng() __attribute__((weak)); char const *config_rng() { @@ -1599,6 +1606,65 @@ int Libc::Vfs_plugin::munmap(void *addr, ::size_t) } +int Libc::Vfs_plugin::pipe(Libc::File_descriptor *pipefdo[2]) +{ + Absolute_path base_path(Libc::config_pipe()); + if (base_path == "") { + error(__func__, ": pipe fs not mounted"); + return Errno(EACCES); + } + + Libc::File_descriptor *meta_fd { nullptr }; + + { + Absolute_path new_path = base_path; + new_path.append("/new"); + + meta_fd = open(new_path.base(), O_RDONLY, Libc::ANY_FD); + if (!meta_fd) { + Genode::error("failed to create pipe at ", new_path); + return Errno(EACCES); + } + meta_fd->path(new_path.string()); + + char buf[32] { }; + int const n = read(meta_fd, buf, sizeof(buf)-1); + if (n < 1) { + error("failed to read pipe at ", new_path); + close(meta_fd); + return Errno(EACCES); + } + buf[n] = '\0'; + base_path.append("/"); + base_path.append(buf); + } + + auto open_pipe_fd = [&] (auto path_suffix, auto flags) + { + Absolute_path path = base_path; + path.append(path_suffix); + + File_descriptor *fd = open(path.base(), flags, Libc::ANY_FD); + if (!fd) + error("failed to open pipe end at ", path); + else + fd->path(path.string()); + + return fd; + }; + + pipefdo[0] = open_pipe_fd("/out", O_RDONLY); + pipefdo[1] = open_pipe_fd("/in", O_WRONLY); + + close(meta_fd); + + if (!pipefdo[0] || !pipefdo[1]) + return Errno(EACCES); + + return 0; +} + + bool Libc::Vfs_plugin::poll(File_descriptor &fd, struct pollfd &pfd) { if (fd.plugin != this) return false; diff --git a/repos/libports/src/test/libc_pipe/main.cc b/repos/libports/src/test/libc_pipe/main.cc index 816dc99bf..f4f691325 100644 --- a/repos/libports/src/test/libc_pipe/main.cc +++ b/repos/libports/src/test/libc_pipe/main.cc @@ -76,7 +76,8 @@ int main(int argc, char *argv[]) ssize_t bytes_written = write(pipefd[1], buf, BUF_SIZE); if (bytes_written != BUF_SIZE) { - fprintf(stderr, "Error writing to pipe\n"); + fprintf(stderr, "Error writing to pipe (bytes_written=%zd, BUF_SIZE=%zd)\n", + bytes_written, (size_t)BUF_SIZE); exit(1); } diff --git a/repos/libports/src/test/libc_pipe/target.mk b/repos/libports/src/test/libc_pipe/target.mk index fb24f5958..00bd7025e 100644 --- a/repos/libports/src/test/libc_pipe/target.mk +++ b/repos/libports/src/test/libc_pipe/target.mk @@ -1,5 +1,5 @@ TARGET = test-libc_pipe -LIBS = base posix libc_pipe +LIBS = base posix SRC_CC = main.cc CC_CXX_WARN_STRICT = diff --git a/repos/os/include/vfs/types.h b/repos/os/include/vfs/types.h index f2733966b..917f795cb 100644 --- a/repos/os/include/vfs/types.h +++ b/repos/os/include/vfs/types.h @@ -72,6 +72,10 @@ namespace Vfs { .writeable = false, .executable = false }; } + static Node_rwx wo() { return { .readable = false, + .writeable = true, + .executable = false }; } + static Node_rwx rw() { return { .readable = true, .writeable = true, .executable = false }; }