2014-04-11 19:14:10 +00:00
|
|
|
/*
|
2017-02-01 10:28:15 +00:00
|
|
|
* \brief Libc plugin for using a process-local virtual file system
|
|
|
|
* \author Norman Feske
|
|
|
|
* \author Christian Helmuth
|
|
|
|
* \author Emery Hemingway
|
|
|
|
* \date 2014-04-09
|
2014-04-11 19:14:10 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2017-02-01 10:28:15 +00:00
|
|
|
* Copyright (C) 2014-2017 Genode Labs GmbH
|
2014-04-11 19:14:10 +00:00
|
|
|
*
|
|
|
|
* This file is part of the Genode OS framework, which is distributed
|
2017-02-20 12:23:52 +00:00
|
|
|
* under the terms of the GNU Affero General Public License version 3.
|
2014-04-11 19:14:10 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/* Genode includes */
|
|
|
|
#include <base/env.h>
|
base: avoid use of deprecated base/printf.h
Besides adapting the components to the use of base/log.h, the patch
cleans up a few base headers, i.e., it removes unused includes from
root/component.h, specifically base/heap.h and
ram_session/ram_session.h. Hence, components that relied on the implicit
inclusion of those headers have to manually include those headers now.
While adjusting the log messages, I repeatedly stumbled over the problem
that printing char * arguments is ambiguous. It is unclear whether to
print the argument as pointer or null-terminated string. To overcome
this problem, the patch introduces a new type 'Cstring' that allows the
caller to express that the argument should be handled as null-terminated
string. As a nice side effect, with this type in place, the optional len
argument of the 'String' class could be removed. Instead of supplying a
pair of (char const *, size_t), the constructor accepts a 'Cstring'.
This, in turn, clears the way let the 'String' constructor use the new
output mechanism to assemble a string from multiple arguments (and
thereby getting rid of snprintf within Genode in the near future).
To enforce the explicit resolution of the char * ambiguity, the 'char *'
overload of the 'print' function is marked as deleted.
Issue #1987
2016-07-13 17:07:09 +00:00
|
|
|
#include <base/log.h>
|
2014-04-11 19:14:10 +00:00
|
|
|
#include <vfs/dir_file_system.h>
|
|
|
|
|
|
|
|
/* libc includes */
|
|
|
|
#include <errno.h>
|
|
|
|
#include <dirent.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <termios.h>
|
|
|
|
#include <sys/mman.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/disk.h>
|
2014-08-19 15:21:10 +00:00
|
|
|
#include <dlfcn.h>
|
2014-04-11 19:14:10 +00:00
|
|
|
|
|
|
|
/* libc plugin interface */
|
|
|
|
#include <libc-plugin/plugin.h>
|
2016-05-25 13:47:22 +00:00
|
|
|
#include <vfs_plugin.h>
|
2014-04-11 19:14:10 +00:00
|
|
|
|
|
|
|
/* libc-internal includes */
|
2017-02-01 10:28:15 +00:00
|
|
|
#include "libc_mem_alloc.h"
|
2016-06-15 16:39:11 +00:00
|
|
|
#include "libc_errno.h"
|
2017-01-31 15:38:23 +00:00
|
|
|
#include "task.h"
|
2014-04-11 19:14:10 +00:00
|
|
|
|
|
|
|
|
|
|
|
static Vfs::Vfs_handle *vfs_handle(Libc::File_descriptor *fd)
|
|
|
|
{
|
|
|
|
return reinterpret_cast<Vfs::Vfs_handle *>(fd->context);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static Libc::Plugin_context *vfs_context(Vfs::Vfs_handle *vfs_handle)
|
|
|
|
{
|
|
|
|
return reinterpret_cast<Libc::Plugin_context *>(vfs_handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Utility to convert VFS stat struct to the libc stat struct
|
|
|
|
*
|
|
|
|
* Code shared between 'stat' and 'fstat'.
|
|
|
|
*/
|
|
|
|
static void vfs_stat_to_libc_stat_struct(Vfs::Directory_service::Stat const &src,
|
|
|
|
struct stat *dst)
|
|
|
|
{
|
|
|
|
enum { FS_BLOCK_SIZE = 1024 };
|
|
|
|
|
|
|
|
Genode::memset(dst, 0, sizeof(*dst));
|
|
|
|
|
|
|
|
dst->st_uid = src.uid;
|
|
|
|
dst->st_gid = src.gid;
|
|
|
|
dst->st_mode = src.mode;
|
|
|
|
dst->st_size = src.size;
|
|
|
|
dst->st_blksize = FS_BLOCK_SIZE;
|
|
|
|
dst->st_blocks = (dst->st_size + FS_BLOCK_SIZE - 1) / FS_BLOCK_SIZE;
|
|
|
|
dst->st_ino = src.inode;
|
|
|
|
dst->st_dev = src.device;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-02-22 13:42:20 +00:00
|
|
|
static Genode::Xml_node *_config_node;
|
|
|
|
|
|
|
|
|
2014-04-24 10:40:18 +00:00
|
|
|
namespace Libc {
|
2014-04-11 19:14:10 +00:00
|
|
|
|
2017-02-22 13:42:20 +00:00
|
|
|
void libc_config_init(Genode::Xml_node node)
|
|
|
|
{
|
|
|
|
static Genode::Xml_node config = node;
|
|
|
|
_config_node = &config;
|
|
|
|
}
|
|
|
|
|
2014-05-06 09:49:56 +00:00
|
|
|
Genode::Xml_node config() __attribute__((weak));
|
|
|
|
Genode::Xml_node config()
|
2014-04-24 10:40:18 +00:00
|
|
|
{
|
2017-03-14 12:19:02 +00:00
|
|
|
if (!_config_node) {
|
|
|
|
error("libc config not initialized - aborting");
|
|
|
|
exit(1);
|
|
|
|
}
|
2017-03-03 09:56:05 +00:00
|
|
|
return *_config_node;
|
2014-04-24 10:40:18 +00:00
|
|
|
}
|
2014-04-11 19:14:10 +00:00
|
|
|
|
2014-04-24 10:40:18 +00:00
|
|
|
class Config_attr
|
|
|
|
{
|
|
|
|
private:
|
2014-04-11 19:14:10 +00:00
|
|
|
|
2014-04-24 10:40:18 +00:00
|
|
|
char _buf[Vfs::MAX_PATH_LEN];
|
2014-04-11 19:14:10 +00:00
|
|
|
|
2014-04-24 10:40:18 +00:00
|
|
|
public:
|
2014-04-11 19:14:10 +00:00
|
|
|
|
2014-04-24 10:40:18 +00:00
|
|
|
Config_attr(char const *attr_name, char const *default_value)
|
|
|
|
{
|
|
|
|
Genode::strncpy(_buf, default_value, sizeof(_buf));
|
|
|
|
try {
|
|
|
|
Libc::config().attribute(attr_name).value(_buf, sizeof(_buf));
|
|
|
|
} catch (...) { }
|
|
|
|
}
|
2014-04-11 19:14:10 +00:00
|
|
|
|
2014-04-24 10:40:18 +00:00
|
|
|
char const *string() const { return _buf; }
|
|
|
|
};
|
2014-04-11 19:14:10 +00:00
|
|
|
|
2014-08-20 14:48:12 +00:00
|
|
|
char const *config_rtc() __attribute__((weak));
|
|
|
|
char const *config_rtc()
|
|
|
|
{
|
|
|
|
static Config_attr rtc("rtc", "");
|
|
|
|
return rtc.string();
|
|
|
|
}
|
2017-02-12 09:59:37 +00:00
|
|
|
|
|
|
|
char const *config_socket() __attribute__((weak));
|
|
|
|
char const *config_socket()
|
|
|
|
{
|
|
|
|
static Config_attr socket("socket", "");
|
|
|
|
return socket.string();
|
|
|
|
}
|
2017-04-07 12:02:40 +00:00
|
|
|
|
|
|
|
void notify_read_ready(Vfs::Vfs_handle *handle)
|
|
|
|
{
|
|
|
|
struct Check : Libc::Suspend_functor
|
|
|
|
{
|
|
|
|
Vfs::Vfs_handle *handle;
|
|
|
|
Check(Vfs::Vfs_handle *handle) : handle(handle) { }
|
|
|
|
bool suspend() override { return !handle->fs().notify_read_ready(handle); }
|
|
|
|
} check(handle);
|
|
|
|
|
|
|
|
while (!handle->fs().notify_read_ready(handle))
|
|
|
|
Libc::suspend(check);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool read_ready(Libc::File_descriptor *fd)
|
|
|
|
{
|
|
|
|
Vfs::Vfs_handle *handle = vfs_handle(fd);
|
|
|
|
if (!handle) return false;
|
|
|
|
|
|
|
|
notify_read_ready(handle);
|
|
|
|
|
|
|
|
return handle->fs().read_ready(handle);
|
|
|
|
}
|
|
|
|
|
2014-04-11 19:14:10 +00:00
|
|
|
}
|
|
|
|
|
2015-09-20 09:47:17 +00:00
|
|
|
int Libc::Vfs_plugin::access(const char *path, int amode)
|
|
|
|
{
|
|
|
|
if (_root_dir.leaf_path(path))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
errno = ENOENT;
|
|
|
|
return -1;
|
|
|
|
}
|
2015-09-18 09:10:29 +00:00
|
|
|
|
|
|
|
|
2014-04-11 19:14:10 +00:00
|
|
|
Libc::File_descriptor *Libc::Vfs_plugin::open(char const *path, int flags,
|
|
|
|
int libc_fd)
|
|
|
|
{
|
2017-08-15 18:51:53 +00:00
|
|
|
if (_root_dir.directory(path)) {
|
|
|
|
|
|
|
|
if (((flags & O_ACCMODE) != O_RDONLY)) {
|
|
|
|
errno = EINVAL;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
Vfs::Vfs_handle *handle = 0;
|
|
|
|
|
|
|
|
typedef Vfs::Directory_service::Opendir_result Opendir_result;
|
|
|
|
|
|
|
|
switch (_root_dir.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;
|
|
|
|
case Opendir_result::OPENDIR_ERR_NODE_ALREADY_EXISTS: errno = EEXIST; return nullptr;
|
|
|
|
case Opendir_result::OPENDIR_ERR_NO_SPACE: errno = ENOSPC; return nullptr;
|
|
|
|
case Opendir_result::OPENDIR_ERR_OUT_OF_RAM:
|
|
|
|
case Opendir_result::OPENDIR_ERR_OUT_OF_CAPS:
|
|
|
|
case Opendir_result::OPENDIR_ERR_PERMISSION_DENIED: errno = EPERM; return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* the directory was successfully opened */
|
|
|
|
|
|
|
|
Libc::File_descriptor *fd =
|
|
|
|
Libc::file_descriptor_allocator()->alloc(this, vfs_context(handle), libc_fd);
|
|
|
|
|
|
|
|
/* FIXME error cleanup code leaks resources! */
|
|
|
|
|
|
|
|
if (!fd) {
|
|
|
|
errno = EMFILE;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
fd->flags = flags & O_ACCMODE;
|
|
|
|
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
2014-04-11 19:14:10 +00:00
|
|
|
typedef Vfs::Directory_service::Open_result Result;
|
|
|
|
|
|
|
|
Vfs::Vfs_handle *handle = 0;
|
|
|
|
|
|
|
|
while (handle == 0) {
|
|
|
|
|
2016-05-25 13:47:22 +00:00
|
|
|
switch (_root_dir.open(path, flags, &handle, _alloc)) {
|
2014-04-11 19:14:10 +00:00
|
|
|
|
|
|
|
case Result::OPEN_OK:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Result::OPEN_ERR_UNACCESSIBLE:
|
|
|
|
{
|
|
|
|
if (!(flags & O_CREAT)) {
|
2017-07-04 19:11:57 +00:00
|
|
|
if (flags & O_NOFOLLOW) {
|
|
|
|
errno = ELOOP;
|
|
|
|
return 0;
|
|
|
|
}
|
2014-04-11 19:14:10 +00:00
|
|
|
errno = ENOENT;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* O_CREAT is set, so try to create the file */
|
2016-05-25 13:47:22 +00:00
|
|
|
switch (_root_dir.open(path, flags | O_EXCL, &handle, _alloc)) {
|
2014-04-11 19:14:10 +00:00
|
|
|
|
|
|
|
case Result::OPEN_OK:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Result::OPEN_ERR_EXISTS:
|
|
|
|
|
|
|
|
/* file has been created by someone else in the meantime */
|
2017-07-02 22:31:24 +00:00
|
|
|
if (flags & O_NOFOLLOW) {
|
|
|
|
errno = ELOOP;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
errno = EEXIST;
|
|
|
|
return 0;
|
2014-04-11 19:14:10 +00:00
|
|
|
|
2015-10-01 09:53:00 +00:00
|
|
|
case Result::OPEN_ERR_NO_PERM: errno = EPERM; return 0;
|
|
|
|
case Result::OPEN_ERR_UNACCESSIBLE: errno = ENOENT; return 0;
|
|
|
|
case Result::OPEN_ERR_NAME_TOO_LONG: errno = ENAMETOOLONG; return 0;
|
|
|
|
case Result::OPEN_ERR_NO_SPACE: errno = ENOSPC; return 0;
|
2017-08-15 18:51:53 +00:00
|
|
|
case Result::OPEN_ERR_OUT_OF_RAM: errno = ENOSPC; return 0;
|
|
|
|
case Result::OPEN_ERR_OUT_OF_CAPS: errno = ENOSPC; return 0;
|
2014-04-11 19:14:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2015-10-01 09:53:00 +00:00
|
|
|
case Result::OPEN_ERR_NO_PERM: errno = EPERM; return 0;
|
|
|
|
case Result::OPEN_ERR_EXISTS: errno = EEXIST; return 0;
|
|
|
|
case Result::OPEN_ERR_NAME_TOO_LONG: errno = ENAMETOOLONG; return 0;
|
|
|
|
case Result::OPEN_ERR_NO_SPACE: errno = ENOSPC; return 0;
|
2017-08-15 18:51:53 +00:00
|
|
|
case Result::OPEN_ERR_OUT_OF_RAM: errno = ENOSPC; return 0;
|
|
|
|
case Result::OPEN_ERR_OUT_OF_CAPS: errno = ENOSPC; return 0;
|
2014-04-11 19:14:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* the file was successfully opened */
|
|
|
|
|
|
|
|
Libc::File_descriptor *fd =
|
|
|
|
Libc::file_descriptor_allocator()->alloc(this, vfs_context(handle), libc_fd);
|
|
|
|
|
2017-06-09 14:02:57 +00:00
|
|
|
/* FIXME error cleanup code leaks resources! */
|
|
|
|
|
|
|
|
if (!fd) {
|
|
|
|
errno = EMFILE;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2017-06-24 21:42:30 +00:00
|
|
|
fd->flags = flags & (O_ACCMODE|O_NONBLOCK|O_APPEND);
|
2014-04-11 19:14:10 +00:00
|
|
|
|
|
|
|
if ((flags & O_TRUNC) && (ftruncate(fd, 0) == -1)) {
|
2017-06-09 14:02:57 +00:00
|
|
|
errno = EINVAL; /* XXX which error code fits best ? */
|
|
|
|
return nullptr;
|
2014-04-11 19:14:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int Libc::Vfs_plugin::close(Libc::File_descriptor *fd)
|
|
|
|
{
|
2016-03-30 13:24:19 +00:00
|
|
|
Vfs::Vfs_handle *handle = vfs_handle(fd);
|
2017-08-15 18:51:53 +00:00
|
|
|
_vfs_sync(handle);
|
2016-03-30 13:24:19 +00:00
|
|
|
handle->ds().close(handle);
|
2014-04-11 19:14:10 +00:00
|
|
|
Libc::file_descriptor_allocator()->free(fd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int Libc::Vfs_plugin::dup2(Libc::File_descriptor *fd,
|
|
|
|
Libc::File_descriptor *new_fd)
|
|
|
|
{
|
|
|
|
new_fd->context = fd->context;
|
|
|
|
return new_fd->libc_fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int Libc::Vfs_plugin::fstat(Libc::File_descriptor *fd, struct stat *buf)
|
|
|
|
{
|
|
|
|
return stat(fd->fd_path, buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-06-15 16:39:11 +00:00
|
|
|
int Libc::Vfs_plugin::fstatfs(Libc::File_descriptor *fd, struct statfs *buf)
|
2014-04-11 19:14:10 +00:00
|
|
|
{
|
2016-06-15 16:39:11 +00:00
|
|
|
if (!fd || !buf)
|
|
|
|
return Libc::Errno(EFAULT);
|
|
|
|
|
|
|
|
Genode::memset(buf, 0, sizeof(*buf));
|
|
|
|
|
2014-04-11 19:14:10 +00:00
|
|
|
buf->f_flags = MNT_UNION;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int Libc::Vfs_plugin::mkdir(const char *path, mode_t mode)
|
|
|
|
{
|
2017-08-15 18:51:53 +00:00
|
|
|
Vfs::Vfs_handle *dir_handle { 0 };
|
2014-04-11 19:14:10 +00:00
|
|
|
|
2017-08-15 18:51:53 +00:00
|
|
|
typedef Vfs::Directory_service::Opendir_result Opendir_result;
|
|
|
|
|
|
|
|
switch (_root_dir.opendir(path, true, &dir_handle, _alloc)) {
|
|
|
|
case Opendir_result::OPENDIR_OK:
|
|
|
|
dir_handle->ds().close(dir_handle);
|
|
|
|
break;
|
|
|
|
case Opendir_result::OPENDIR_ERR_LOOKUP_FAILED:
|
|
|
|
return Errno(ENOENT);
|
|
|
|
case Opendir_result::OPENDIR_ERR_NAME_TOO_LONG:
|
|
|
|
return Errno(ENAMETOOLONG);
|
|
|
|
case Opendir_result::OPENDIR_ERR_NODE_ALREADY_EXISTS:
|
|
|
|
return Errno(EEXIST);
|
|
|
|
case Opendir_result::OPENDIR_ERR_NO_SPACE:
|
|
|
|
return Errno(ENOSPC);
|
|
|
|
case Opendir_result::OPENDIR_ERR_OUT_OF_RAM:
|
|
|
|
case Opendir_result::OPENDIR_ERR_OUT_OF_CAPS:
|
|
|
|
case Opendir_result::OPENDIR_ERR_PERMISSION_DENIED:
|
|
|
|
return Errno(EPERM);
|
2014-04-11 19:14:10 +00:00
|
|
|
}
|
2017-08-15 18:51:53 +00:00
|
|
|
|
2014-04-11 19:14:10 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int Libc::Vfs_plugin::stat(char const *path, struct stat *buf)
|
|
|
|
{
|
|
|
|
if (!path or !buf) {
|
|
|
|
errno = EFAULT;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef Vfs::Directory_service::Stat_result Result;
|
|
|
|
|
|
|
|
Vfs::Directory_service::Stat stat;
|
|
|
|
|
|
|
|
switch (_root_dir.stat(path, stat)) {
|
|
|
|
case Result::STAT_ERR_NO_ENTRY: errno = ENOENT; return -1;
|
2016-04-01 09:42:31 +00:00
|
|
|
case Result::STAT_ERR_NO_PERM: errno = EACCES; return -1;
|
2014-10-15 14:09:45 +00:00
|
|
|
case Result::STAT_OK: break;
|
2014-04-11 19:14:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
vfs_stat_to_libc_stat_struct(stat, buf);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ssize_t Libc::Vfs_plugin::write(Libc::File_descriptor *fd, const void *buf,
|
|
|
|
::size_t count)
|
|
|
|
{
|
|
|
|
typedef Vfs::File_io_service::Write_result Result;
|
|
|
|
|
|
|
|
Vfs::Vfs_handle *handle = vfs_handle(fd);
|
|
|
|
|
2017-08-15 18:51:53 +00:00
|
|
|
Vfs::file_size out_count = 0;
|
|
|
|
Result out_result = Result::WRITE_OK;
|
2014-04-11 19:14:10 +00:00
|
|
|
|
2017-08-15 18:51:53 +00:00
|
|
|
if (fd->flags & O_NONBLOCK) {
|
|
|
|
|
|
|
|
try {
|
|
|
|
out_result = handle->fs().write(handle, (char const *)buf, count, out_count);
|
|
|
|
} catch (Vfs::File_io_service::Insufficient_buffer) { }
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
struct Check : Libc::Suspend_functor
|
|
|
|
{
|
|
|
|
bool retry { false };
|
|
|
|
|
|
|
|
Vfs::Vfs_handle *handle;
|
|
|
|
void const *buf;
|
|
|
|
::size_t count;
|
|
|
|
Vfs::file_size &out_count;
|
|
|
|
Result &out_result;
|
|
|
|
|
|
|
|
Check(Vfs::Vfs_handle *handle, void const *buf,
|
|
|
|
::size_t count, Vfs::file_size &out_count,
|
|
|
|
Result &out_result)
|
|
|
|
: handle(handle), buf(buf), count(count), out_count(out_count),
|
|
|
|
out_result(out_result)
|
|
|
|
{ }
|
|
|
|
|
|
|
|
bool suspend() override
|
|
|
|
{
|
|
|
|
try {
|
|
|
|
out_result = handle->fs().write(handle, (char const *)buf,
|
|
|
|
count, out_count);
|
|
|
|
retry = false;
|
|
|
|
} catch (Vfs::File_io_service::Insufficient_buffer) {
|
|
|
|
retry = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return retry;
|
|
|
|
}
|
|
|
|
} check(handle, buf, count, out_count, out_result);
|
|
|
|
|
|
|
|
do {
|
|
|
|
Libc::suspend(check);
|
|
|
|
} while (check.retry);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (out_result) {
|
|
|
|
case Result::WRITE_ERR_AGAIN: return Errno(EAGAIN);
|
|
|
|
case Result::WRITE_ERR_WOULD_BLOCK: return Errno(EWOULDBLOCK);
|
|
|
|
case Result::WRITE_ERR_INVALID: return Errno(EINVAL);
|
|
|
|
case Result::WRITE_ERR_IO: return Errno(EIO);
|
|
|
|
case Result::WRITE_ERR_INTERRUPT: return Errno(EINTR);
|
|
|
|
case Result::WRITE_OK: break;
|
2014-04-11 19:14:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
handle->advance_seek(out_count);
|
|
|
|
|
|
|
|
return out_count;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ssize_t Libc::Vfs_plugin::read(Libc::File_descriptor *fd, void *buf,
|
|
|
|
::size_t count)
|
|
|
|
{
|
2017-05-18 14:04:21 +00:00
|
|
|
Libc::dispatch_pending_io_signals();
|
|
|
|
|
2017-08-15 18:51:53 +00:00
|
|
|
typedef Vfs::File_io_service::Read_result Result;
|
2014-04-11 19:14:10 +00:00
|
|
|
|
2017-08-15 18:51:53 +00:00
|
|
|
Vfs::Vfs_handle *handle = vfs_handle(fd);
|
2017-01-31 15:38:23 +00:00
|
|
|
|
2017-04-07 12:02:40 +00:00
|
|
|
if (fd->flags & O_NONBLOCK && !Libc::read_ready(fd))
|
|
|
|
return Errno(EAGAIN);
|
|
|
|
|
2017-08-15 18:51:53 +00:00
|
|
|
{
|
|
|
|
struct Check : Libc::Suspend_functor
|
|
|
|
{
|
|
|
|
bool retry { false };
|
|
|
|
|
|
|
|
Vfs::Vfs_handle *handle;
|
|
|
|
::size_t count;
|
2017-02-20 12:30:36 +00:00
|
|
|
|
2017-08-15 18:51:53 +00:00
|
|
|
Check(Vfs::Vfs_handle *handle, ::size_t count)
|
|
|
|
: handle(handle), count(count) { }
|
2017-02-20 12:30:36 +00:00
|
|
|
|
2017-08-15 18:51:53 +00:00
|
|
|
bool suspend() override
|
|
|
|
{
|
|
|
|
retry = !handle->fs().queue_read(handle, count);
|
|
|
|
return retry;
|
|
|
|
}
|
|
|
|
} check ( handle, count);
|
|
|
|
|
|
|
|
do {
|
|
|
|
Libc::suspend(check);
|
|
|
|
} while (check.retry);
|
2017-02-20 12:30:36 +00:00
|
|
|
}
|
2017-01-31 15:38:23 +00:00
|
|
|
|
2017-08-15 18:51:53 +00:00
|
|
|
Vfs::file_size out_count = 0;
|
|
|
|
Result out_result;
|
2017-02-20 12:30:36 +00:00
|
|
|
|
2017-08-15 18:51:53 +00:00
|
|
|
{
|
|
|
|
struct Check : Libc::Suspend_functor
|
|
|
|
{
|
|
|
|
bool retry { false };
|
2017-02-20 12:30:36 +00:00
|
|
|
|
2017-08-15 18:51:53 +00:00
|
|
|
Vfs::Vfs_handle *handle;
|
|
|
|
void *buf;
|
|
|
|
::size_t count;
|
|
|
|
Vfs::file_size &out_count;
|
|
|
|
Result &out_result;
|
|
|
|
|
|
|
|
Check(Vfs::Vfs_handle *handle, void *buf, ::size_t count,
|
|
|
|
Vfs::file_size &out_count, Result &out_result)
|
|
|
|
: handle(handle), buf(buf), count(count), out_count(out_count),
|
|
|
|
out_result(out_result)
|
|
|
|
{ }
|
|
|
|
|
|
|
|
bool suspend() override
|
|
|
|
{
|
|
|
|
out_result = handle->fs().complete_read(handle, (char *)buf,
|
|
|
|
count, out_count);
|
2017-02-20 12:30:36 +00:00
|
|
|
/* suspend me if read is still queued */
|
2017-08-15 18:51:53 +00:00
|
|
|
|
|
|
|
retry = (out_result == Result::READ_QUEUED);
|
|
|
|
|
|
|
|
return retry;
|
2017-02-20 12:30:36 +00:00
|
|
|
}
|
2017-08-15 18:51:53 +00:00
|
|
|
} check ( handle, buf, count, out_count, out_result);
|
2017-02-20 12:30:36 +00:00
|
|
|
|
2017-08-15 18:51:53 +00:00
|
|
|
do {
|
|
|
|
Libc::suspend(check);
|
|
|
|
} while (check.retry);
|
2017-01-31 15:38:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (out_result) {
|
|
|
|
case Result::READ_ERR_AGAIN: return Errno(EAGAIN);
|
|
|
|
case Result::READ_ERR_WOULD_BLOCK: return Errno(EWOULDBLOCK);
|
|
|
|
case Result::READ_ERR_INVALID: return Errno(EINVAL);
|
|
|
|
case Result::READ_ERR_IO: return Errno(EIO);
|
|
|
|
case Result::READ_ERR_INTERRUPT: return Errno(EINTR);
|
|
|
|
case Result::READ_OK: break;
|
2014-04-11 19:14:10 +00:00
|
|
|
|
2017-01-31 15:38:23 +00:00
|
|
|
case Result::READ_QUEUED: /* handled above, so never reached */ break;
|
2014-04-11 19:14:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
handle->advance_seek(out_count);
|
|
|
|
|
|
|
|
return out_count;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ssize_t Libc::Vfs_plugin::getdirentries(Libc::File_descriptor *fd, char *buf,
|
|
|
|
::size_t nbytes, ::off_t *basep)
|
|
|
|
{
|
|
|
|
if (nbytes < sizeof(struct dirent)) {
|
base: avoid use of deprecated base/printf.h
Besides adapting the components to the use of base/log.h, the patch
cleans up a few base headers, i.e., it removes unused includes from
root/component.h, specifically base/heap.h and
ram_session/ram_session.h. Hence, components that relied on the implicit
inclusion of those headers have to manually include those headers now.
While adjusting the log messages, I repeatedly stumbled over the problem
that printing char * arguments is ambiguous. It is unclear whether to
print the argument as pointer or null-terminated string. To overcome
this problem, the patch introduces a new type 'Cstring' that allows the
caller to express that the argument should be handled as null-terminated
string. As a nice side effect, with this type in place, the optional len
argument of the 'String' class could be removed. Instead of supplying a
pair of (char const *, size_t), the constructor accepts a 'Cstring'.
This, in turn, clears the way let the 'String' constructor use the new
output mechanism to assemble a string from multiple arguments (and
thereby getting rid of snprintf within Genode in the near future).
To enforce the explicit resolution of the char * ambiguity, the 'char *'
overload of the 'print' function is marked as deleted.
Issue #1987
2016-07-13 17:07:09 +00:00
|
|
|
Genode::error("getdirentries: buffer too small");
|
2014-04-11 19:14:10 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2017-08-15 18:51:53 +00:00
|
|
|
typedef Vfs::File_io_service::Read_result Result;
|
2014-04-11 19:14:10 +00:00
|
|
|
|
|
|
|
Vfs::Vfs_handle *handle = vfs_handle(fd);
|
|
|
|
|
2017-08-15 18:51:53 +00:00
|
|
|
typedef Vfs::Directory_service::Dirent Dirent;
|
|
|
|
|
|
|
|
Dirent dirent_out;
|
|
|
|
|
|
|
|
{
|
|
|
|
struct Check : Libc::Suspend_functor
|
|
|
|
{
|
|
|
|
bool retry { false };
|
|
|
|
|
|
|
|
Vfs::Vfs_handle *handle;
|
2014-04-11 19:14:10 +00:00
|
|
|
|
2017-08-15 18:51:53 +00:00
|
|
|
Check(Vfs::Vfs_handle *handle)
|
|
|
|
: handle(handle) { }
|
2014-04-11 19:14:10 +00:00
|
|
|
|
2017-08-15 18:51:53 +00:00
|
|
|
bool suspend() override
|
|
|
|
{
|
|
|
|
retry = !handle->fs().queue_read(handle, sizeof(Dirent));
|
|
|
|
return retry;
|
|
|
|
}
|
|
|
|
} check(handle);
|
|
|
|
|
|
|
|
do {
|
|
|
|
Libc::suspend(check);
|
|
|
|
} while (check.retry);
|
|
|
|
}
|
|
|
|
|
|
|
|
Result out_result;
|
|
|
|
Vfs::file_size out_count;
|
|
|
|
|
|
|
|
{
|
|
|
|
struct Check : Libc::Suspend_functor
|
|
|
|
{
|
|
|
|
bool retry { false };
|
|
|
|
|
|
|
|
Vfs::Vfs_handle *handle;
|
|
|
|
Dirent &dirent_out;
|
|
|
|
Vfs::file_size &out_count;
|
|
|
|
Result &out_result;
|
|
|
|
|
|
|
|
Check(Vfs::Vfs_handle *handle, Dirent &dirent_out,
|
|
|
|
Vfs::file_size &out_count, Result &out_result)
|
|
|
|
: handle(handle), dirent_out(dirent_out), out_count(out_count),
|
|
|
|
out_result(out_result) { }
|
|
|
|
|
|
|
|
bool suspend() override
|
|
|
|
{
|
|
|
|
out_result = handle->fs().complete_read(handle,
|
|
|
|
(char*)&dirent_out,
|
|
|
|
sizeof(Dirent),
|
|
|
|
out_count);
|
|
|
|
|
|
|
|
/* suspend me if read is still queued */
|
|
|
|
|
|
|
|
retry = (out_result == Result::READ_QUEUED);
|
|
|
|
|
|
|
|
return retry;
|
|
|
|
}
|
|
|
|
} check(handle, dirent_out, out_count, out_result);
|
|
|
|
|
|
|
|
do {
|
|
|
|
Libc::suspend(check);
|
|
|
|
} while (check.retry);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((out_result != Result::READ_OK) ||
|
|
|
|
(out_count < sizeof(Dirent))) {
|
|
|
|
return 0;
|
2014-04-11 19:14:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Convert dirent structure from VFS to libc
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct dirent *dirent = (struct dirent *)buf;
|
|
|
|
Genode::memset(dirent, 0, sizeof(struct dirent));
|
|
|
|
|
|
|
|
switch (dirent_out.type) {
|
|
|
|
case Vfs::Directory_service::DIRENT_TYPE_DIRECTORY: dirent->d_type = DT_DIR; break;
|
|
|
|
case Vfs::Directory_service::DIRENT_TYPE_FILE: dirent->d_type = DT_REG; break;
|
|
|
|
case Vfs::Directory_service::DIRENT_TYPE_SYMLINK: dirent->d_type = DT_LNK; break;
|
|
|
|
case Vfs::Directory_service::DIRENT_TYPE_FIFO: dirent->d_type = DT_FIFO; break;
|
2014-04-14 09:57:22 +00:00
|
|
|
case Vfs::Directory_service::DIRENT_TYPE_CHARDEV: dirent->d_type = DT_CHR; break;
|
|
|
|
case Vfs::Directory_service::DIRENT_TYPE_BLOCKDEV: dirent->d_type = DT_BLK; break;
|
|
|
|
case Vfs::Directory_service::DIRENT_TYPE_END: return 0;
|
2014-04-11 19:14:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
dirent->d_fileno = dirent_out.fileno;
|
|
|
|
dirent->d_reclen = sizeof(struct dirent);
|
|
|
|
|
|
|
|
Genode::strncpy(dirent->d_name, dirent_out.name, sizeof(dirent->d_name));
|
|
|
|
|
|
|
|
dirent->d_namlen = Genode::strlen(dirent->d_name);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Keep track of VFS seek pointer and user-supplied basep.
|
|
|
|
*/
|
|
|
|
handle->advance_seek(sizeof(Vfs::Directory_service::Dirent));
|
|
|
|
|
|
|
|
*basep += sizeof(struct dirent);
|
|
|
|
|
|
|
|
return sizeof(struct dirent);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int Libc::Vfs_plugin::ioctl(Libc::File_descriptor *fd, int request, char *argp)
|
|
|
|
{
|
|
|
|
using ::off_t;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Marshal ioctl arguments
|
|
|
|
*/
|
|
|
|
|
|
|
|
typedef Vfs::File_io_service::Ioctl_opcode Opcode;
|
|
|
|
|
|
|
|
Opcode opcode = Opcode::IOCTL_OP_UNDEFINED;
|
|
|
|
|
|
|
|
Vfs::File_io_service::Ioctl_arg arg = 0;
|
|
|
|
|
|
|
|
switch (request) {
|
|
|
|
|
|
|
|
case TIOCGWINSZ:
|
|
|
|
opcode = Opcode::IOCTL_OP_TIOCGWINSZ;
|
|
|
|
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;
|
|
|
|
|
|
|
|
::termios *termios = (::termios *)argp;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For now only enabling/disabling of ECHO is supported
|
|
|
|
*/
|
|
|
|
if (termios->c_lflag & (ECHO | ECHONL)) {
|
|
|
|
arg = (Vfs::File_io_service::IOCTL_VAL_ECHO |
|
|
|
|
Vfs::File_io_service::IOCTL_VAL_ECHONL);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
arg = Vfs::File_io_service::IOCTL_VAL_NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case TIOCSETAW:
|
|
|
|
{
|
|
|
|
opcode = Opcode::IOCTL_OP_TIOCSETAW;
|
|
|
|
arg = argp ? *(int*)argp : 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case FIONBIO:
|
|
|
|
{
|
|
|
|
opcode = Opcode::IOCTL_OP_FIONBIO;
|
|
|
|
arg = argp ? *(int*)argp : 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DIOCGMEDIASIZE:
|
|
|
|
{
|
2017-10-26 12:57:47 +00:00
|
|
|
if (!argp) {
|
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-04-11 19:14:10 +00:00
|
|
|
opcode = Opcode::IOCTL_OP_DIOCGMEDIASIZE;
|
|
|
|
arg = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
base: avoid use of deprecated base/printf.h
Besides adapting the components to the use of base/log.h, the patch
cleans up a few base headers, i.e., it removes unused includes from
root/component.h, specifically base/heap.h and
ram_session/ram_session.h. Hence, components that relied on the implicit
inclusion of those headers have to manually include those headers now.
While adjusting the log messages, I repeatedly stumbled over the problem
that printing char * arguments is ambiguous. It is unclear whether to
print the argument as pointer or null-terminated string. To overcome
this problem, the patch introduces a new type 'Cstring' that allows the
caller to express that the argument should be handled as null-terminated
string. As a nice side effect, with this type in place, the optional len
argument of the 'String' class could be removed. Instead of supplying a
pair of (char const *, size_t), the constructor accepts a 'Cstring'.
This, in turn, clears the way let the 'String' constructor use the new
output mechanism to assemble a string from multiple arguments (and
thereby getting rid of snprintf within Genode in the near future).
To enforce the explicit resolution of the char * ambiguity, the 'char *'
overload of the 'print' function is marked as deleted.
Issue #1987
2016-07-13 17:07:09 +00:00
|
|
|
Genode::warning("unsupported ioctl (request=", Genode::Hex(request), ")");
|
2014-04-11 19:14:10 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (opcode == Opcode::IOCTL_OP_UNDEFINED) {
|
|
|
|
errno = ENOTTY;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef Vfs::File_io_service::Ioctl_result Result;
|
2015-05-13 16:20:39 +00:00
|
|
|
|
2014-04-11 19:14:10 +00:00
|
|
|
Vfs::File_io_service::Ioctl_out out;
|
|
|
|
Genode::memset(&out, 0, sizeof(out));
|
|
|
|
|
|
|
|
Vfs::Vfs_handle *handle = vfs_handle(fd);
|
|
|
|
|
|
|
|
switch (handle->fs().ioctl(handle, opcode, arg, out)) {
|
2015-05-13 16:20:39 +00:00
|
|
|
case Result::IOCTL_ERR_INVALID: errno = EINVAL; return -1;
|
|
|
|
case Result::IOCTL_ERR_NOTTY: errno = ENOTTY; return -1;
|
|
|
|
case Result::IOCTL_OK: break;
|
2014-04-11 19:14:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Unmarshal ioctl results
|
|
|
|
*/
|
|
|
|
switch (request) {
|
|
|
|
|
|
|
|
case TIOCGWINSZ:
|
|
|
|
{
|
|
|
|
::winsize *winsize = (::winsize *)arg;
|
|
|
|
winsize->ws_row = out.tiocgwinsz.rows;
|
|
|
|
winsize->ws_col = out.tiocgwinsz.columns;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
case TIOCSETAF:
|
|
|
|
case TIOCSETAW:
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case FIONBIO:
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case DIOCGMEDIASIZE:
|
|
|
|
{
|
|
|
|
/* resolve ambiguity with libc type */
|
|
|
|
using Genode::int64_t;
|
|
|
|
|
2017-10-26 12:57:47 +00:00
|
|
|
int64_t *disk_size = (int64_t*)argp;
|
2014-04-11 19:14:10 +00:00
|
|
|
*disk_size = out.diocgmediasize.size;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
::off_t Libc::Vfs_plugin::lseek(Libc::File_descriptor *fd, ::off_t offset, int whence)
|
|
|
|
{
|
|
|
|
Vfs::Vfs_handle *handle = vfs_handle(fd);
|
|
|
|
|
|
|
|
switch (whence) {
|
|
|
|
case SEEK_SET: handle->seek(offset); break;
|
|
|
|
case SEEK_CUR: handle->advance_seek(offset); break;
|
|
|
|
case SEEK_END:
|
|
|
|
{
|
|
|
|
struct stat stat;
|
|
|
|
::memset(&stat, 0, sizeof(stat));
|
|
|
|
fstat(fd, &stat);
|
|
|
|
handle->seek(stat.st_size + offset);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return handle->seek();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int Libc::Vfs_plugin::ftruncate(Libc::File_descriptor *fd, ::off_t length)
|
|
|
|
{
|
|
|
|
Vfs::Vfs_handle *handle = vfs_handle(fd);
|
|
|
|
|
|
|
|
typedef Vfs::File_io_service::Ftruncate_result Result;
|
|
|
|
|
|
|
|
switch (handle->fs().ftruncate(handle, length)) {
|
2015-10-01 09:53:00 +00:00
|
|
|
case Result::FTRUNCATE_ERR_NO_PERM: errno = EPERM; return -1;
|
|
|
|
case Result::FTRUNCATE_ERR_INTERRUPT: errno = EINTR; return -1;
|
|
|
|
case Result::FTRUNCATE_ERR_NO_SPACE: errno = ENOSPC; return -1;
|
|
|
|
case Result::FTRUNCATE_OK: break;
|
2014-04-11 19:14:10 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int Libc::Vfs_plugin::fcntl(Libc::File_descriptor *fd, int cmd, long arg)
|
|
|
|
{
|
|
|
|
switch (cmd) {
|
|
|
|
case F_DUPFD:
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Allocate free file descriptor locally.
|
|
|
|
*/
|
|
|
|
Libc::File_descriptor *new_fd =
|
|
|
|
Libc::file_descriptor_allocator()->alloc(this, 0);
|
2017-06-09 14:02:57 +00:00
|
|
|
if (!new_fd) return Errno(EMFILE);
|
|
|
|
|
2014-04-11 19:14:10 +00:00
|
|
|
new_fd->path(fd->fd_path);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Use new allocated number as name of file descriptor
|
|
|
|
* duplicate.
|
|
|
|
*/
|
|
|
|
if (dup2(fd, new_fd) == -1) {
|
base: avoid use of deprecated base/printf.h
Besides adapting the components to the use of base/log.h, the patch
cleans up a few base headers, i.e., it removes unused includes from
root/component.h, specifically base/heap.h and
ram_session/ram_session.h. Hence, components that relied on the implicit
inclusion of those headers have to manually include those headers now.
While adjusting the log messages, I repeatedly stumbled over the problem
that printing char * arguments is ambiguous. It is unclear whether to
print the argument as pointer or null-terminated string. To overcome
this problem, the patch introduces a new type 'Cstring' that allows the
caller to express that the argument should be handled as null-terminated
string. As a nice side effect, with this type in place, the optional len
argument of the 'String' class could be removed. Instead of supplying a
pair of (char const *, size_t), the constructor accepts a 'Cstring'.
This, in turn, clears the way let the 'String' constructor use the new
output mechanism to assemble a string from multiple arguments (and
thereby getting rid of snprintf within Genode in the near future).
To enforce the explicit resolution of the char * ambiguity, the 'char *'
overload of the 'print' function is marked as deleted.
Issue #1987
2016-07-13 17:07:09 +00:00
|
|
|
Genode::error("Plugin::fcntl: dup2 unexpectedly failed");
|
2017-06-09 14:02:57 +00:00
|
|
|
return Errno(EINVAL);
|
2014-04-11 19:14:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return new_fd->libc_fd;
|
|
|
|
}
|
2017-03-12 17:05:38 +00:00
|
|
|
case F_GETFD: return fd->cloexec ? FD_CLOEXEC : 0;
|
|
|
|
case F_SETFD: fd->cloexec = arg == FD_CLOEXEC; return 0;
|
|
|
|
|
|
|
|
case F_GETFL: return fd->flags;
|
|
|
|
case F_SETFL: fd->flags = arg; return 0;
|
2014-04-11 19:14:10 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
base: avoid use of deprecated base/printf.h
Besides adapting the components to the use of base/log.h, the patch
cleans up a few base headers, i.e., it removes unused includes from
root/component.h, specifically base/heap.h and
ram_session/ram_session.h. Hence, components that relied on the implicit
inclusion of those headers have to manually include those headers now.
While adjusting the log messages, I repeatedly stumbled over the problem
that printing char * arguments is ambiguous. It is unclear whether to
print the argument as pointer or null-terminated string. To overcome
this problem, the patch introduces a new type 'Cstring' that allows the
caller to express that the argument should be handled as null-terminated
string. As a nice side effect, with this type in place, the optional len
argument of the 'String' class could be removed. Instead of supplying a
pair of (char const *, size_t), the constructor accepts a 'Cstring'.
This, in turn, clears the way let the 'String' constructor use the new
output mechanism to assemble a string from multiple arguments (and
thereby getting rid of snprintf within Genode in the near future).
To enforce the explicit resolution of the char * ambiguity, the 'char *'
overload of the 'print' function is marked as deleted.
Issue #1987
2016-07-13 17:07:09 +00:00
|
|
|
Genode::error("fcntl(): command ", cmd, " not supported - vfs");
|
2017-06-09 14:02:57 +00:00
|
|
|
return Errno(EINVAL);
|
2014-04-11 19:14:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int Libc::Vfs_plugin::fsync(Libc::File_descriptor *fd)
|
|
|
|
{
|
2017-08-15 18:51:53 +00:00
|
|
|
Vfs::Vfs_handle *handle = vfs_handle(fd);
|
|
|
|
_vfs_sync(handle);
|
2014-04-11 19:14:10 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int Libc::Vfs_plugin::symlink(const char *oldpath, const char *newpath)
|
|
|
|
{
|
2017-08-15 18:51:53 +00:00
|
|
|
typedef Vfs::Directory_service::Openlink_result Openlink_result;
|
|
|
|
|
|
|
|
Vfs::Vfs_handle *handle { 0 };
|
|
|
|
|
|
|
|
Openlink_result openlink_result =
|
|
|
|
_root_dir.openlink(newpath, true, &handle, _alloc);
|
2014-04-11 19:14:10 +00:00
|
|
|
|
2017-08-15 18:51:53 +00:00
|
|
|
switch (openlink_result) {
|
|
|
|
case Openlink_result::OPENLINK_OK:
|
|
|
|
break;
|
|
|
|
case Openlink_result::OPENLINK_ERR_LOOKUP_FAILED:
|
|
|
|
return Errno(ENOENT);
|
|
|
|
case Openlink_result::OPENLINK_ERR_NAME_TOO_LONG:
|
|
|
|
return Errno(ENAMETOOLONG);
|
|
|
|
case Openlink_result::OPENLINK_ERR_NODE_ALREADY_EXISTS:
|
|
|
|
return Errno(EEXIST);
|
|
|
|
case Openlink_result::OPENLINK_ERR_NO_SPACE:
|
|
|
|
return Errno(ENOSPC);
|
|
|
|
case Openlink_result::OPENLINK_ERR_OUT_OF_RAM:
|
|
|
|
return Errno(ENOSPC);
|
|
|
|
case Openlink_result::OPENLINK_ERR_OUT_OF_CAPS:
|
|
|
|
return Errno(ENOSPC);
|
|
|
|
case Vfs::Directory_service::OPENLINK_ERR_PERMISSION_DENIED:
|
|
|
|
return Errno(EPERM);
|
2014-04-11 19:14:10 +00:00
|
|
|
}
|
2017-08-15 18:51:53 +00:00
|
|
|
|
|
|
|
Vfs::file_size count = ::strlen(oldpath) + 1;
|
|
|
|
Vfs::file_size out_count = 0;
|
|
|
|
|
|
|
|
struct Check : Libc::Suspend_functor
|
|
|
|
{
|
|
|
|
bool retry { false };
|
|
|
|
|
|
|
|
Vfs::Vfs_handle *handle;
|
|
|
|
void const *buf;
|
|
|
|
::size_t count;
|
|
|
|
Vfs::file_size &out_count;
|
|
|
|
|
|
|
|
Check(Vfs::Vfs_handle *handle, void const *buf,
|
|
|
|
::size_t count, Vfs::file_size &out_count)
|
|
|
|
: handle(handle), buf(buf), count(count), out_count(out_count)
|
|
|
|
{ }
|
|
|
|
|
|
|
|
bool suspend() override
|
|
|
|
{
|
|
|
|
try {
|
|
|
|
handle->fs().write(handle, (char const *)buf,
|
|
|
|
count, out_count);
|
|
|
|
retry = false;
|
|
|
|
} catch (Vfs::File_io_service::Insufficient_buffer) {
|
|
|
|
retry = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return retry;
|
|
|
|
}
|
|
|
|
} check ( handle, oldpath, count, out_count);
|
|
|
|
|
|
|
|
do {
|
|
|
|
Libc::suspend(check);
|
|
|
|
} while (check.retry);
|
|
|
|
|
|
|
|
_vfs_sync(handle);
|
|
|
|
handle->ds().close(handle);
|
|
|
|
|
|
|
|
if (out_count != count)
|
|
|
|
return Errno(ENAMETOOLONG);
|
|
|
|
|
2014-04-11 19:14:10 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-09-15 12:40:37 +00:00
|
|
|
ssize_t Libc::Vfs_plugin::readlink(const char *path, char *buf, ::size_t buf_size)
|
2014-04-11 19:14:10 +00:00
|
|
|
{
|
2017-08-15 18:51:53 +00:00
|
|
|
Vfs::Vfs_handle *symlink_handle { 0 };
|
|
|
|
|
|
|
|
Vfs::Directory_service::Openlink_result openlink_result =
|
|
|
|
_root_dir.openlink(path, false, &symlink_handle, _alloc);
|
2014-04-11 19:14:10 +00:00
|
|
|
|
2017-08-15 18:51:53 +00:00
|
|
|
switch (openlink_result) {
|
|
|
|
case Vfs::Directory_service::OPENLINK_OK:
|
|
|
|
break;
|
|
|
|
case Vfs::Directory_service::OPENLINK_ERR_LOOKUP_FAILED:
|
|
|
|
return Errno(ENOENT);
|
|
|
|
case Vfs::Directory_service::OPENLINK_ERR_NAME_TOO_LONG:
|
|
|
|
/* should not happen */
|
|
|
|
return Errno(ENAMETOOLONG);
|
|
|
|
case Vfs::Directory_service::OPENLINK_ERR_NODE_ALREADY_EXISTS:
|
|
|
|
case Vfs::Directory_service::OPENLINK_ERR_NO_SPACE:
|
|
|
|
case Vfs::Directory_service::OPENLINK_ERR_OUT_OF_RAM:
|
|
|
|
case Vfs::Directory_service::OPENLINK_ERR_OUT_OF_CAPS:
|
|
|
|
case Vfs::Directory_service::OPENLINK_ERR_PERMISSION_DENIED:
|
|
|
|
return Errno(EACCES);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
struct Check : Libc::Suspend_functor
|
|
|
|
{
|
|
|
|
bool retry { false };
|
|
|
|
|
|
|
|
Vfs::Vfs_handle *symlink_handle;
|
|
|
|
::size_t const buf_size;
|
|
|
|
|
|
|
|
Check(Vfs::Vfs_handle *symlink_handle,
|
|
|
|
::size_t const buf_size)
|
|
|
|
: symlink_handle(symlink_handle), buf_size(buf_size) { }
|
|
|
|
|
|
|
|
bool suspend() override
|
|
|
|
{
|
|
|
|
retry =
|
|
|
|
!symlink_handle->fs().queue_read(symlink_handle, buf_size);
|
|
|
|
return retry;
|
|
|
|
}
|
|
|
|
} check(symlink_handle, buf_size);
|
|
|
|
|
|
|
|
do {
|
|
|
|
Libc::suspend(check);
|
|
|
|
} while (check.retry);
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef Vfs::File_io_service::Read_result Result;
|
|
|
|
|
|
|
|
Result out_result;
|
2014-09-09 12:32:31 +00:00
|
|
|
Vfs::file_size out_len = 0;
|
2014-04-11 19:14:10 +00:00
|
|
|
|
2017-08-15 18:51:53 +00:00
|
|
|
{
|
|
|
|
struct Check : Libc::Suspend_functor
|
|
|
|
{
|
|
|
|
bool retry { false };
|
|
|
|
|
|
|
|
Vfs::Vfs_handle *symlink_handle;
|
|
|
|
char *buf;
|
|
|
|
::size_t const buf_size;
|
|
|
|
Vfs::file_size &out_len;
|
|
|
|
Result &out_result;
|
|
|
|
|
|
|
|
Check(Vfs::Vfs_handle *symlink_handle,
|
|
|
|
char *buf,
|
|
|
|
::size_t const buf_size,
|
|
|
|
Vfs::file_size &out_len,
|
|
|
|
Result &out_result)
|
|
|
|
: symlink_handle(symlink_handle), buf(buf), buf_size(buf_size),
|
|
|
|
out_len(out_len), out_result(out_result) { }
|
|
|
|
|
|
|
|
bool suspend() override
|
|
|
|
{
|
|
|
|
out_result = symlink_handle->fs().complete_read(symlink_handle, buf, buf_size, out_len);
|
|
|
|
|
|
|
|
/* suspend me if read is still queued */
|
|
|
|
|
|
|
|
retry = (out_result == Result::READ_QUEUED);
|
|
|
|
|
|
|
|
return retry;
|
|
|
|
}
|
|
|
|
} check(symlink_handle, buf, buf_size, out_len, out_result);
|
|
|
|
|
|
|
|
do {
|
|
|
|
Libc::suspend(check);
|
|
|
|
} while (check.retry);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (out_result) {
|
|
|
|
case Result::READ_ERR_AGAIN: return Errno(EAGAIN);
|
|
|
|
case Result::READ_ERR_WOULD_BLOCK: return Errno(EWOULDBLOCK);
|
|
|
|
case Result::READ_ERR_INVALID: return Errno(EINVAL);
|
|
|
|
case Result::READ_ERR_IO: return Errno(EIO);
|
|
|
|
case Result::READ_ERR_INTERRUPT: return Errno(EINTR);
|
|
|
|
case Result::READ_OK: break;
|
|
|
|
|
|
|
|
case Result::READ_QUEUED: /* handled above, so never reached */ break;
|
2014-04-11 19:14:10 +00:00
|
|
|
};
|
|
|
|
|
2017-08-15 18:51:53 +00:00
|
|
|
symlink_handle->ds().close(symlink_handle);
|
|
|
|
|
2014-04-11 19:14:10 +00:00
|
|
|
return out_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int Libc::Vfs_plugin::rmdir(char const *path)
|
|
|
|
{
|
|
|
|
return unlink(path);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int Libc::Vfs_plugin::unlink(char const *path)
|
|
|
|
{
|
|
|
|
typedef Vfs::Directory_service::Unlink_result Result;
|
|
|
|
|
|
|
|
switch (_root_dir.unlink(path)) {
|
2015-10-30 11:33:26 +00:00
|
|
|
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;
|
|
|
|
case Result::UNLINK_OK: break;
|
2014-04-11 19:14:10 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int Libc::Vfs_plugin::rename(char const *from_path, char const *to_path)
|
|
|
|
{
|
|
|
|
typedef Vfs::Directory_service::Rename_result Result;
|
|
|
|
|
2016-03-02 13:32:43 +00:00
|
|
|
if (_root_dir.leaf_path(to_path)) {
|
|
|
|
|
2016-05-11 16:21:47 +00:00
|
|
|
if (_root_dir.directory(to_path)) {
|
|
|
|
if (!_root_dir.directory(from_path)) {
|
2016-03-02 13:32:43 +00:00
|
|
|
errno = EISDIR; return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_root_dir.num_dirent(to_path)) {
|
|
|
|
errno = ENOTEMPTY; return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
2016-05-11 16:21:47 +00:00
|
|
|
if (_root_dir.directory(from_path)) {
|
2016-03-02 13:32:43 +00:00
|
|
|
errno = ENOTDIR; return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-11 19:14:10 +00:00
|
|
|
switch (_root_dir.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;
|
2014-10-15 14:09:45 +00:00
|
|
|
case Result::RENAME_OK: break;
|
2014-04-11 19:14:10 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void *Libc::Vfs_plugin::mmap(void *addr_in, ::size_t length, int prot, int flags,
|
|
|
|
Libc::File_descriptor *fd, ::off_t offset)
|
|
|
|
{
|
|
|
|
if (prot != PROT_READ) {
|
base: avoid use of deprecated base/printf.h
Besides adapting the components to the use of base/log.h, the patch
cleans up a few base headers, i.e., it removes unused includes from
root/component.h, specifically base/heap.h and
ram_session/ram_session.h. Hence, components that relied on the implicit
inclusion of those headers have to manually include those headers now.
While adjusting the log messages, I repeatedly stumbled over the problem
that printing char * arguments is ambiguous. It is unclear whether to
print the argument as pointer or null-terminated string. To overcome
this problem, the patch introduces a new type 'Cstring' that allows the
caller to express that the argument should be handled as null-terminated
string. As a nice side effect, with this type in place, the optional len
argument of the 'String' class could be removed. Instead of supplying a
pair of (char const *, size_t), the constructor accepts a 'Cstring'.
This, in turn, clears the way let the 'String' constructor use the new
output mechanism to assemble a string from multiple arguments (and
thereby getting rid of snprintf within Genode in the near future).
To enforce the explicit resolution of the char * ambiguity, the 'char *'
overload of the 'print' function is marked as deleted.
Issue #1987
2016-07-13 17:07:09 +00:00
|
|
|
Genode::error("mmap for prot=", Genode::Hex(prot), " not supported");
|
2014-04-11 19:14:10 +00:00
|
|
|
errno = EACCES;
|
|
|
|
return (void *)-1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (addr_in != 0) {
|
base: avoid use of deprecated base/printf.h
Besides adapting the components to the use of base/log.h, the patch
cleans up a few base headers, i.e., it removes unused includes from
root/component.h, specifically base/heap.h and
ram_session/ram_session.h. Hence, components that relied on the implicit
inclusion of those headers have to manually include those headers now.
While adjusting the log messages, I repeatedly stumbled over the problem
that printing char * arguments is ambiguous. It is unclear whether to
print the argument as pointer or null-terminated string. To overcome
this problem, the patch introduces a new type 'Cstring' that allows the
caller to express that the argument should be handled as null-terminated
string. As a nice side effect, with this type in place, the optional len
argument of the 'String' class could be removed. Instead of supplying a
pair of (char const *, size_t), the constructor accepts a 'Cstring'.
This, in turn, clears the way let the 'String' constructor use the new
output mechanism to assemble a string from multiple arguments (and
thereby getting rid of snprintf within Genode in the near future).
To enforce the explicit resolution of the char * ambiguity, the 'char *'
overload of the 'print' function is marked as deleted.
Issue #1987
2016-07-13 17:07:09 +00:00
|
|
|
Genode::error("mmap for predefined address not supported");
|
2014-04-11 19:14:10 +00:00
|
|
|
errno = EINVAL;
|
|
|
|
return (void *)-1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX attempt to obtain memory mapping via
|
|
|
|
* 'Vfs::Directory_service::dataspace'.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void *addr = Libc::mem_alloc()->alloc(length, PAGE_SHIFT);
|
|
|
|
if (addr == (void *)-1) {
|
|
|
|
errno = ENOMEM;
|
|
|
|
return (void *)-1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (::pread(fd->libc_fd, addr, length, offset) < 0) {
|
base: avoid use of deprecated base/printf.h
Besides adapting the components to the use of base/log.h, the patch
cleans up a few base headers, i.e., it removes unused includes from
root/component.h, specifically base/heap.h and
ram_session/ram_session.h. Hence, components that relied on the implicit
inclusion of those headers have to manually include those headers now.
While adjusting the log messages, I repeatedly stumbled over the problem
that printing char * arguments is ambiguous. It is unclear whether to
print the argument as pointer or null-terminated string. To overcome
this problem, the patch introduces a new type 'Cstring' that allows the
caller to express that the argument should be handled as null-terminated
string. As a nice side effect, with this type in place, the optional len
argument of the 'String' class could be removed. Instead of supplying a
pair of (char const *, size_t), the constructor accepts a 'Cstring'.
This, in turn, clears the way let the 'String' constructor use the new
output mechanism to assemble a string from multiple arguments (and
thereby getting rid of snprintf within Genode in the near future).
To enforce the explicit resolution of the char * ambiguity, the 'char *'
overload of the 'print' function is marked as deleted.
Issue #1987
2016-07-13 17:07:09 +00:00
|
|
|
Genode::error("mmap could not obtain file content");
|
2014-04-11 19:14:10 +00:00
|
|
|
::munmap(addr, length);
|
|
|
|
errno = EACCES;
|
|
|
|
return (void *)-1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int Libc::Vfs_plugin::munmap(void *addr, ::size_t)
|
|
|
|
{
|
|
|
|
Libc::mem_alloc()->free(addr);
|
|
|
|
return 0;
|
|
|
|
}
|
2017-02-01 10:28:15 +00:00
|
|
|
|
|
|
|
|
|
|
|
bool Libc::Vfs_plugin::supports_select(int nfds,
|
|
|
|
fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
|
|
|
|
struct timeval *timeout)
|
|
|
|
{
|
|
|
|
/* return true if any file descriptor (which is set) belongs to the VFS */
|
|
|
|
for (int fd = 0; fd < nfds; ++fd) {
|
|
|
|
|
|
|
|
if (FD_ISSET(fd, readfds) || FD_ISSET(fd, writefds) || FD_ISSET(fd, exceptfds)) {
|
|
|
|
|
|
|
|
Libc::File_descriptor *fdo =
|
|
|
|
Libc::file_descriptor_allocator()->find_by_libc_fd(fd);
|
|
|
|
|
|
|
|
if (fdo && (fdo->plugin == this))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int Libc::Vfs_plugin::select(int nfds,
|
|
|
|
fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
|
|
|
|
struct timeval *timeout)
|
|
|
|
{
|
|
|
|
int nready = 0;
|
|
|
|
|
|
|
|
fd_set const in_readfds = *readfds;
|
|
|
|
fd_set const in_writefds = *writefds;
|
|
|
|
/* XXX exceptfds not supported */
|
|
|
|
|
|
|
|
/* clear fd sets */
|
|
|
|
FD_ZERO(readfds);
|
|
|
|
FD_ZERO(writefds);
|
|
|
|
FD_ZERO(exceptfds);
|
|
|
|
|
|
|
|
for (int fd = 0; fd < nfds; ++fd) {
|
|
|
|
|
|
|
|
Libc::File_descriptor *fdo =
|
|
|
|
Libc::file_descriptor_allocator()->find_by_libc_fd(fd);
|
|
|
|
|
|
|
|
/* handle only fds that belong to this plugin */
|
|
|
|
if (!fdo || (fdo->plugin != this))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
Vfs::Vfs_handle *handle = vfs_handle(fdo);
|
|
|
|
if (!handle) continue;
|
|
|
|
|
|
|
|
if (FD_ISSET(fd, &in_readfds)) {
|
|
|
|
if (handle->fs().read_ready(handle)) {
|
|
|
|
FD_SET(fd, readfds);
|
|
|
|
++nready;
|
|
|
|
} else {
|
2017-04-07 12:02:40 +00:00
|
|
|
Libc::notify_read_ready(handle);
|
2017-02-01 10:28:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (FD_ISSET(fd, &in_writefds)) {
|
|
|
|
if (true /* XXX always writeable */) {
|
|
|
|
FD_SET(fd, writefds);
|
|
|
|
++nready;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* XXX exceptfds not supported */
|
|
|
|
}
|
|
|
|
return nready;
|
|
|
|
}
|