mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-18 18:56:29 +00:00
libc: support for ioctls via ioctl directory
This patch introduces a new scheme of handling ioctl operations that maps ioctls to pseudo-file accesses, similar to how the libc maps socket calls to socket-fs operations. A device file can be accompanied with a (hidden) directory that is named after the device file and hosts pseudo files for triggering the various device operations. For example, for accessing a terminal, the directory structure looks like this: /dev/terminal /dev/.terminal/info The 'info' file contains device information in XML format. The type of the XML node corresponds to the device type. E.g., If the libc receives a 'TIOCGWINSZ' ioctl for /dev/terminal, it reads the content of /dev/.terminal/info to obtain the terminal-size information. In this case, the 'info' file looks as follows: <terminal rows="25" columns="80/> Following this scheme, VFS plugins can support ioctl operations by providing an ioctl directory in addition to the actual device file. Internally, the mechanism uses the 'os/vfs.h' API to access pseudo files. Hence, we need to propagate the Vfs::Env to 'vfs_plugin.cc' to create an instance of a 'Directory' for the root for the VFS. Issue #3519
This commit is contained in:
parent
07a40d028a
commit
7ac32ea60c
@ -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 **
|
||||
*************************/
|
||||
|
@ -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);
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
/* Genode includes */
|
||||
#include <libc/component.h>
|
||||
#include <os/vfs.h>
|
||||
#include <vfs/file_system.h>
|
||||
|
||||
/* 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<Genode::Directory> _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 <typename FN>
|
||||
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 <typename FN>
|
||||
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; }
|
||||
|
@ -216,12 +216,34 @@ namespace Libc {
|
||||
|
||||
return VFS_THREAD_SAFE(handle->fs().read_ready(handle));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
template <typename FN>
|
||||
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;
|
||||
|
Loading…
Reference in New Issue
Block a user