diff --git a/repos/libports/src/lib/libc/internal/env.h b/repos/libports/src/lib/libc/internal/env.h index d73ca1641..0cd249c9b 100644 --- a/repos/libports/src/lib/libc/internal/env.h +++ b/repos/libports/src/lib/libc/internal/env.h @@ -68,6 +68,9 @@ class Libc::Env_implementation : public Libc::Env, public Config_accessor : _env(env), _vfs_env(_env, alloc, _vfs_config()) { } + Vfs::Env &vfs_env() { return _vfs_env; } + + /************************* ** Libc::Env interface ** *************************/ diff --git a/repos/libports/src/lib/libc/internal/kernel.h b/repos/libports/src/lib/libc/internal/kernel.h index 705874136..3d873ab99 100644 --- a/repos/libports/src/lib/libc/internal/kernel.h +++ b/repos/libports/src/lib/libc/internal/kernel.h @@ -98,9 +98,10 @@ struct Libc::Kernel final : Vfs::Io_response_handler, bool const _update_mtime = _libc_env.libc_config().attribute_value("update_mtime", true); - Vfs_plugin _vfs { _libc_env, _heap, *this, + Vfs_plugin _vfs { _libc_env, _libc_env.vfs_env(), _heap, *this, _update_mtime ? Vfs_plugin::Update_mtime::YES - : Vfs_plugin::Update_mtime::NO }; + : Vfs_plugin::Update_mtime::NO, + _libc_env.config() }; bool const _cloned = _libc_env.libc_config().attribute_value("cloned", false); pid_t const _pid = _libc_env.libc_config().attribute_value("pid", 0U); diff --git a/repos/libports/src/lib/libc/internal/vfs_plugin.h b/repos/libports/src/lib/libc/internal/vfs_plugin.h index d766eea8e..886cec13c 100644 --- a/repos/libports/src/lib/libc/internal/vfs_plugin.h +++ b/repos/libports/src/lib/libc/internal/vfs_plugin.h @@ -18,6 +18,7 @@ /* Genode includes */ #include +#include #include /* libc includes */ @@ -41,12 +42,32 @@ class Libc::Vfs_plugin : public Plugin enum class Update_mtime { NO, YES }; + /** + * Return path to pseudo files used for ioctl operations of a given FD + */ + static Absolute_path ioctl_dir(File_descriptor const &fd) + { + Absolute_path path(fd.fd_path); + + /* + * The pseudo files used for ioctl operations reside in a (hidden) + * directory named after the device path and prefixed with '.'. + */ + String<64> const ioctl_dir_name(".", path.last_element()); + + path.strip_last_element(); + path.append_element(ioctl_dir_name.string()); + + return path; + } + private: - Genode::Allocator &_alloc; - Vfs::File_system &_root_dir; - Vfs::Io_response_handler &_response_handler; - Update_mtime const _update_mtime; + Genode::Allocator &_alloc; + Vfs::File_system &_root_fs; + Constructible _root_dir { }; + Vfs::Io_response_handler &_response_handler; + Update_mtime const _update_mtime; /** * Sync a handle and propagate errors @@ -58,21 +79,47 @@ class Libc::Vfs_plugin : public Plugin */ void _vfs_write_mtime(Vfs::Vfs_handle&); + int _legacy_ioctl(File_descriptor *, int , char *); + + /** + * Call functor 'fn' with ioctl info for the given file descriptor 'fd' + * + * The functor is called with an 'Xml_node' of the ioctl information + * as argument. + * + * If no ioctl info is available, 'fn' is not called. + */ + template + void _with_info(File_descriptor &fd, FN const &fn); + public: Vfs_plugin(Libc::Env &env, + Vfs::Env &vfs_env, Genode::Allocator &alloc, Vfs::Io_response_handler &handler, - Update_mtime update_mtime) + Update_mtime update_mtime, + Xml_node config) : - _alloc(alloc), _root_dir(env.vfs()), + _alloc(alloc), + _root_fs(env.vfs()), _response_handler(handler), _update_mtime(update_mtime) - { } + { + if (config.has_sub_node("libc")) + _root_dir.construct(vfs_env); + } ~Vfs_plugin() final { } - bool root_dir_has_dirents() const { return _root_dir.num_dirent("/") > 0; } + template + void with_root_dir(FN const &fn) + { + if (_root_dir.constructed()) + fn(*_root_dir); + } + + bool root_dir_has_dirents() const { return _root_fs.num_dirent("/") > 0; } bool supports_access(const char *, int) override { return true; } bool supports_mkdir(const char *, mode_t) override { return true; } diff --git a/repos/libports/src/lib/libc/vfs_plugin.cc b/repos/libports/src/lib/libc/vfs_plugin.cc index 3e5e8e5c7..530172f9e 100644 --- a/repos/libports/src/lib/libc/vfs_plugin.cc +++ b/repos/libports/src/lib/libc/vfs_plugin.cc @@ -216,12 +216,34 @@ namespace Libc { return VFS_THREAD_SAFE(handle->fs().read_ready(handle)); } - } + +template +void Libc::Vfs_plugin::_with_info(File_descriptor &fd, FN const &fn) +{ + if (!_root_dir.constructed()) + return; + + Absolute_path path = ioctl_dir(fd); + path.append_element("info"); + + try { + Lock::Guard g(vfs_lock()); + + File_content const content(_alloc, *_root_dir, path.string(), + File_content::Limit{4096U}); + + content.xml([&] (Xml_node node) { + fn(node); }); + + } catch (...) { } +} + + int Libc::Vfs_plugin::access(const char *path, int amode) { - if (VFS_THREAD_SAFE(_root_dir.leaf_path(path))) + if (VFS_THREAD_SAFE(_root_fs.leaf_path(path))) return 0; errno = ENOENT; @@ -232,7 +254,7 @@ int Libc::Vfs_plugin::access(const char *path, int amode) Libc::File_descriptor *Libc::Vfs_plugin::open(char const *path, int flags, int libc_fd) { - if (VFS_THREAD_SAFE(_root_dir.directory(path))) { + if (VFS_THREAD_SAFE(_root_fs.directory(path))) { if (((flags & O_ACCMODE) != O_RDONLY)) { errno = EISDIR; @@ -245,7 +267,7 @@ Libc::File_descriptor *Libc::Vfs_plugin::open(char const *path, int flags, typedef Vfs::Directory_service::Opendir_result Opendir_result; - switch (VFS_THREAD_SAFE(_root_dir.opendir(path, false, &handle, _alloc))) { + switch (VFS_THREAD_SAFE(_root_fs.opendir(path, false, &handle, _alloc))) { case Opendir_result::OPENDIR_OK: break; case Opendir_result::OPENDIR_ERR_LOOKUP_FAILED: errno = ENOENT; return nullptr; case Opendir_result::OPENDIR_ERR_NAME_TOO_LONG: errno = ENAMETOOLONG; return nullptr; @@ -286,7 +308,7 @@ Libc::File_descriptor *Libc::Vfs_plugin::open(char const *path, int flags, while (handle == nullptr) { - switch (VFS_THREAD_SAFE(_root_dir.open(path, flags, &handle, _alloc))) { + switch (VFS_THREAD_SAFE(_root_fs.open(path, flags, &handle, _alloc))) { case Result::OPEN_OK: break; @@ -303,7 +325,7 @@ Libc::File_descriptor *Libc::Vfs_plugin::open(char const *path, int flags, } /* O_CREAT is set, so try to create the file */ - switch (VFS_THREAD_SAFE(_root_dir.open(path, flags | O_EXCL, &handle, _alloc))) { + switch (VFS_THREAD_SAFE(_root_fs.open(path, flags | O_EXCL, &handle, _alloc))) { case Result::OPEN_OK: break; @@ -491,7 +513,7 @@ int Libc::Vfs_plugin::dup2(File_descriptor *fd, typedef Vfs::Directory_service::Open_result Result; - if (VFS_THREAD_SAFE(_root_dir.open(fd->fd_path, fd->flags, &handle, _alloc)) + if (VFS_THREAD_SAFE(_root_fs.open(fd->fd_path, fd->flags, &handle, _alloc)) != Result::OPEN_OK) { warning("dup2 failed for path ", fd->fd_path); @@ -515,7 +537,7 @@ Libc::File_descriptor *Libc::Vfs_plugin::dup(File_descriptor *fd) typedef Vfs::Directory_service::Open_result Result; - if (VFS_THREAD_SAFE(_root_dir.open(fd->fd_path, fd->flags, &handle, _alloc)) + if (VFS_THREAD_SAFE(_root_fs.open(fd->fd_path, fd->flags, &handle, _alloc)) != Result::OPEN_OK) { warning("dup failed for path ", fd->fd_path); @@ -576,7 +598,7 @@ int Libc::Vfs_plugin::mkdir(const char *path, mode_t mode) typedef Vfs::Directory_service::Opendir_result Opendir_result; - switch (VFS_THREAD_SAFE(_root_dir.opendir(path, true, &dir_handle, _alloc))) { + switch (VFS_THREAD_SAFE(_root_fs.opendir(path, true, &dir_handle, _alloc))) { case Opendir_result::OPENDIR_OK: VFS_THREAD_SAFE(dir_handle->close()); break; @@ -609,7 +631,7 @@ int Libc::Vfs_plugin::stat(char const *path, struct stat *buf) Vfs::Directory_service::Stat stat; - switch (VFS_THREAD_SAFE(_root_dir.stat(path, stat))) { + switch (VFS_THREAD_SAFE(_root_fs.stat(path, stat))) { case Result::STAT_ERR_NO_ENTRY: errno = ENOENT; return -1; case Result::STAT_ERR_NO_PERM: errno = EACCES; return -1; case Result::STAT_OK: break; @@ -925,6 +947,55 @@ ssize_t Libc::Vfs_plugin::getdirentries(File_descriptor *fd, char *buf, int Libc::Vfs_plugin::ioctl(File_descriptor *fd, int request, char *argp) +{ + bool handled = false; + + if (request == TIOCGWINSZ) { + + if (!argp) + return Errno(EINVAL); + + _with_info(*fd, [&] (Xml_node info) { + if (info.type() == "terminal") { + ::winsize *winsize = (::winsize *)argp; + winsize->ws_row = info.attribute_value("rows", 25U); + winsize->ws_col = info.attribute_value("columns", 80U); + handled = true; + } + }); + + } else if (request == TIOCGETA) { + + ::termios *termios = (::termios *)argp; + + termios->c_iflag = 0; + termios->c_oflag = 0; + termios->c_cflag = 0; + /* + * Set 'ECHO' flag, needed by libreadline. Otherwise, echoing + * user input doesn't work in bash. + */ + termios->c_lflag = ECHO; + ::memset(termios->c_cc, _POSIX_VDISABLE, sizeof(termios->c_cc)); + termios->c_ispeed = 0; + termios->c_ospeed = 0; + handled = true; + } + + if (handled) + return 0; + + return _legacy_ioctl(fd, request, argp); +} + + +/** + * Fallback for ioctl operations targeting the deprecated VFS ioctl interface + * + * XXX Remove this method once all ioctl operations are supported via + * regular VFS file accesses. + */ +int Libc::Vfs_plugin::_legacy_ioctl(File_descriptor *fd, int request, char *argp) { using ::off_t; @@ -951,27 +1022,6 @@ int Libc::Vfs_plugin::ioctl(File_descriptor *fd, int request, char *argp) break; } - case TIOCGETA: - { - ::termios *termios = (::termios *)argp; - - termios->c_iflag = 0; - termios->c_oflag = 0; - termios->c_cflag = 0; - /* - * Set 'ECHO' flag, needed by libreadline. Otherwise, echoing - * user input doesn't work in bash. - */ - termios->c_lflag = ECHO; - ::memset(termios->c_cc, _POSIX_VDISABLE, sizeof(termios->c_cc)); - termios->c_ispeed = 0; - termios->c_ospeed = 0; - - return 0; - } - - break; - case TIOCSETAF: { opcode = Opcode::IOCTL_OP_TIOCSETAF; @@ -1183,7 +1233,7 @@ int Libc::Vfs_plugin::symlink(const char *oldpath, const char *newpath) Vfs::Vfs_handle *handle { 0 }; Openlink_result openlink_result = - VFS_THREAD_SAFE(_root_dir.openlink(newpath, true, &handle, _alloc)); + VFS_THREAD_SAFE(_root_fs.openlink(newpath, true, &handle, _alloc)); switch (openlink_result) { case Openlink_result::OPENLINK_OK: @@ -1258,7 +1308,7 @@ ssize_t Libc::Vfs_plugin::readlink(const char *path, char *buf, ::size_t buf_siz Vfs::Vfs_handle *symlink_handle { 0 }; Vfs::Directory_service::Openlink_result openlink_result = - VFS_THREAD_SAFE(_root_dir.openlink(path, false, &symlink_handle, _alloc)); + VFS_THREAD_SAFE(_root_fs.openlink(path, false, &symlink_handle, _alloc)); switch (openlink_result) { case Vfs::Directory_service::OPENLINK_OK: @@ -1373,7 +1423,7 @@ int Libc::Vfs_plugin::unlink(char const *path) { typedef Vfs::Directory_service::Unlink_result Result; - switch (VFS_THREAD_SAFE(_root_dir.unlink(path))) { + switch (VFS_THREAD_SAFE(_root_fs.unlink(path))) { case Result::UNLINK_ERR_NO_ENTRY: errno = ENOENT; return -1; case Result::UNLINK_ERR_NO_PERM: errno = EPERM; return -1; case Result::UNLINK_ERR_NOT_EMPTY: errno = ENOTEMPTY; return -1; @@ -1387,25 +1437,25 @@ int Libc::Vfs_plugin::rename(char const *from_path, char const *to_path) { typedef Vfs::Directory_service::Rename_result Result; - if (VFS_THREAD_SAFE(_root_dir.leaf_path(to_path))) { + if (VFS_THREAD_SAFE(_root_fs.leaf_path(to_path))) { - if (VFS_THREAD_SAFE(_root_dir.directory(to_path))) { - if (!VFS_THREAD_SAFE(_root_dir.directory(from_path))) { + if (VFS_THREAD_SAFE(_root_fs.directory(to_path))) { + if (!VFS_THREAD_SAFE(_root_fs.directory(from_path))) { errno = EISDIR; return -1; } - if (VFS_THREAD_SAFE(_root_dir.num_dirent(to_path))) { + if (VFS_THREAD_SAFE(_root_fs.num_dirent(to_path))) { errno = ENOTEMPTY; return -1; } } else { - if (VFS_THREAD_SAFE(_root_dir.directory(from_path))) { + if (VFS_THREAD_SAFE(_root_fs.directory(from_path))) { errno = ENOTDIR; return -1; } } } - switch (VFS_THREAD_SAFE(_root_dir.rename(from_path, to_path))) { + switch (VFS_THREAD_SAFE(_root_fs.rename(from_path, to_path))) { case Result::RENAME_ERR_NO_ENTRY: errno = ENOENT; return -1; case Result::RENAME_ERR_CROSS_FS: errno = EXDEV; return -1; case Result::RENAME_ERR_NO_PERM: errno = EPERM; return -1;