From 65f75589e98b0188c310af7d59eb7e3c59977a44 Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Fri, 16 Aug 2019 19:04:04 +0200 Subject: [PATCH] libc: configurable initial FDs The libc already supports the configuration of 'stdin', 'stdout', and 'stderr' using '' config attributes. This patch equips the libc with the additional ability to pre-initialize any other file descriptor. A file descriptor is configured as follows: ... ... Furthermore, this patch moves the FD initialization code from the VFS plugin to the libc kernel initialization because opening the FDs depends on 'malloc' ('strdup'), which should not be used at early 'Libc::Kernel' initialization time. Issue #3478 --- repos/libports/include/libc-plugin/fd_alloc.h | 5 ++ repos/libports/src/lib/libc/fd_alloc.cc | 7 ++ repos/libports/src/lib/libc/task.cc | 79 +++++++++++++++++++ repos/libports/src/lib/libc/vfs_plugin.h | 57 +------------ 4 files changed, 94 insertions(+), 54 deletions(-) diff --git a/repos/libports/include/libc-plugin/fd_alloc.h b/repos/libports/include/libc-plugin/fd_alloc.h index 3f955e5a1..c6304c7f5 100644 --- a/repos/libports/include/libc-plugin/fd_alloc.h +++ b/repos/libports/include/libc-plugin/fd_alloc.h @@ -109,6 +109,11 @@ namespace Libc { */ void free(File_descriptor *fdo); + /** + * Prevent the use of the specified file descriptor + */ + void preserve(int libc_fd); + File_descriptor *find_by_libc_fd(int libc_fd); }; diff --git a/repos/libports/src/lib/libc/fd_alloc.cc b/repos/libports/src/lib/libc/fd_alloc.cc index db27f5c75..a24f6288e 100644 --- a/repos/libports/src/lib/libc/fd_alloc.cc +++ b/repos/libports/src/lib/libc/fd_alloc.cc @@ -70,6 +70,13 @@ void File_descriptor_allocator::free(File_descriptor *fdo) } +void File_descriptor_allocator::preserve(int fd) +{ + if (!find_by_libc_fd(fd)) + alloc(nullptr, nullptr, fd); +} + + File_descriptor *File_descriptor_allocator::find_by_libc_fd(int libc_fd) { Lock::Guard guard(_lock); diff --git a/repos/libports/src/lib/libc/task.cc b/repos/libports/src/lib/libc/task.cc index 6ac560efb..d366b44ab 100644 --- a/repos/libports/src/lib/libc/task.cc +++ b/repos/libports/src/lib/libc/task.cc @@ -558,12 +558,16 @@ struct Libc::Kernel final : Vfs::Io_response_handler, return timeout_ms > 0 ? _main_timeout.duration_left() : 0; } + void _init_file_descriptors(); + public: Kernel(Genode::Env &env, Genode::Allocator &heap) : _env(env), _heap(heap) { _env.ep().register_io_progress_handler(*this); + + _init_file_descriptors(); } ~Kernel() { Genode::error(__PRETTY_FUNCTION__, " should not be executed!"); } @@ -818,6 +822,81 @@ struct Libc::Kernel final : Vfs::Io_response_handler, }; +void Libc::Kernel::_init_file_descriptors() +{ + auto init_fd = [&] (Genode::Xml_node const &node, char const *attr, + int libc_fd, unsigned flags) + { + if (!node.has_attribute(attr)) + return; + + typedef Genode::String Path; + Path const path = node.attribute_value(attr, Path()); + + struct stat out_stat { }; + if (stat(path.string(), &out_stat) != 0) + return; + + Libc::File_descriptor *fd = _vfs.open(path.string(), flags, libc_fd); + if (fd->libc_fd != libc_fd) { + Genode::error("could not allocate fd ",libc_fd," for ",path,", " + "got fd ",fd->libc_fd); + _vfs.close(fd); + return; + } + + /* + * We need to manually register the path. Normally this is done + * by '_open'. But we call the local 'open' function directly + * because we want to explicitly specify the libc fd ID. + * + * We have to allocate the path from the libc (done via 'strdup') + * such that the path can be freed when an stdio fd is closed. + */ + if (fd->fd_path) { Genode::warning("may leak former FD path memory"); } + fd->fd_path = strdup(path.string()); + + ::off_t const seek = node.attribute_value("seek", 0ULL); + if (seek) + _vfs.lseek(fd, seek, SEEK_SET); + }; + + if (_vfs.root_dir_has_dirents()) { + + Xml_node const node = _libc_env.libc_config(); + + typedef Genode::String Path; + + if (node.has_attribute("cwd")) + chdir(node.attribute_value("cwd", Path()).string()); + + init_fd(node, "stdin", 0, O_RDONLY); + init_fd(node, "stdout", 1, O_WRONLY); + init_fd(node, "stderr", 2, O_WRONLY); + + node.for_each_sub_node("fd", [&] (Xml_node fd) { + + unsigned const id = fd.attribute_value("id", 0U); + + bool const rd = fd.attribute_value("readable", false); + bool const wr = fd.attribute_value("writeable", false); + + unsigned const flags = rd ? (wr ? O_RDWR : O_RDONLY) + : (wr ? O_WRONLY : 0); + + if (!fd.has_attribute("path")) + warning("Invalid node, 'path' attribute is missing"); + + init_fd(fd, "path", id, flags); + }); + + /* prevent use of IDs of stdin, stdout, and stderr for other files */ + for (unsigned fd = 0; fd <= 2; fd++) + Libc::file_descriptor_allocator()->preserve(fd); + } +} + + /** * Libc kernel singleton * diff --git a/repos/libports/src/lib/libc/vfs_plugin.h b/repos/libports/src/lib/libc/vfs_plugin.h index 402739eb1..d175b4591 100644 --- a/repos/libports/src/lib/libc/vfs_plugin.h +++ b/repos/libports/src/lib/libc/vfs_plugin.h @@ -43,41 +43,6 @@ class Libc::Vfs_plugin : public Libc::Plugin Vfs::File_system &_root_dir; Vfs::Io_response_handler &_response_handler; - void _open_stdio(Genode::Xml_node const &node, char const *attr, - int libc_fd, unsigned flags) - { - if (!node.has_attribute(attr)) { - Libc::file_descriptor_allocator()->alloc(nullptr, nullptr, libc_fd); - return; - } - - typedef Genode::String Path; - Path const path = node.attribute_value(attr, Path()); - - struct stat out_stat { }; - if (stat(path.string(), &out_stat) != 0) - return; - - Libc::File_descriptor *fd = open(path.string(), flags, libc_fd); - if (fd->libc_fd != libc_fd) { - Genode::error("could not allocate fd ",libc_fd," for ",path,", " - "got fd ",fd->libc_fd); - close(fd); - return; - } - - /* - * We need to manually register the path. Normally this is done - * by '_open'. But we call the local 'open' function directly - * because we want to explicitly specify the libc fd ID. - * - * We have to allocate the path from the libc (done via 'strdup') - * such that the path can be freed when an stdio fd is closed. - */ - if (fd->fd_path) { Genode::warning("may leak former FD path memory"); } - fd->fd_path = strdup(path.string()); - } - /** * Sync a handle and propagate errors */ @@ -90,28 +55,12 @@ class Libc::Vfs_plugin : public Libc::Plugin Vfs::Io_response_handler &handler) : _alloc(alloc), _root_dir(env.vfs()), _response_handler(handler) - { - using Genode::Xml_node; - - if (_root_dir.num_dirent("/")) - env.config([&] (Xml_node const &top) { - - top.with_sub_node("libc", [&] (Xml_node node) { - - typedef Genode::String Path; - - if (node.has_attribute("cwd")) - chdir(node.attribute_value("cwd", Path()).string()); - - _open_stdio(node, "stdin", 0, O_RDONLY); - _open_stdio(node, "stdout", 1, O_WRONLY); - _open_stdio(node, "stderr", 2, O_WRONLY); - }); - }); - } + { } ~Vfs_plugin() final { } + bool root_dir_has_dirents() const { return _root_dir.num_dirent("/") > 0; } + 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; }