mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-18 02:40:08 +00:00
parent
d65872a815
commit
8876dd057d
5
libports/lib/mk/libc_vfs.mk
Normal file
5
libports/lib/mk/libc_vfs.mk
Normal file
@ -0,0 +1,5 @@
|
||||
SRC_CC = plugin.cc
|
||||
LIBS += libc
|
||||
INC_DIR += $(REP_DIR)/src/lib/libc
|
||||
|
||||
vpath plugin.cc $(REP_DIR)/src/lib/libc_vfs
|
74
libports/run/libc_vfs.run
Normal file
74
libports/run/libc_vfs.run
Normal file
@ -0,0 +1,74 @@
|
||||
#
|
||||
# \brief Test for using the libc_vfs plugin
|
||||
# \author Norman Feske
|
||||
# \date 2014-04-10
|
||||
#
|
||||
|
||||
#
|
||||
# Build
|
||||
#
|
||||
|
||||
build { core init server/ram_fs test/libc_vfs }
|
||||
|
||||
create_boot_directory
|
||||
|
||||
#
|
||||
# Generate config
|
||||
#
|
||||
|
||||
install_config {
|
||||
<config>
|
||||
<parent-provides>
|
||||
<service name="ROM"/>
|
||||
<service name="RAM"/>
|
||||
<service name="CAP"/>
|
||||
<service name="PD"/>
|
||||
<service name="RM"/>
|
||||
<service name="CPU"/>
|
||||
<service name="LOG"/>
|
||||
<service name="SIGNAL"/>
|
||||
</parent-provides>
|
||||
<default-route>
|
||||
<any-service> <parent/> <any-child/> </any-service>
|
||||
</default-route>
|
||||
<start name="ram_fs">
|
||||
<resource name="RAM" quantum="4M"/>
|
||||
<provides> <service name="File_system"/> </provides>
|
||||
<config> <policy label="" root="/" writeable="yes" /> </config>
|
||||
</start>
|
||||
<start name="test-libc_vfs">
|
||||
<resource name="RAM" quantum="2M"/>
|
||||
<config>
|
||||
<iterations value="1"/>
|
||||
<libc cwd="/tmp" >
|
||||
<vfs>
|
||||
<dir name="tmp" >
|
||||
<fs/>
|
||||
</dir>
|
||||
</vfs>
|
||||
</libc>
|
||||
</config>
|
||||
</start>
|
||||
</config>
|
||||
}
|
||||
|
||||
#
|
||||
# Boot modules
|
||||
#
|
||||
|
||||
build_boot_image {
|
||||
core init
|
||||
ld.lib.so libc.lib.so libc_log.lib.so
|
||||
ram_fs test-libc_vfs
|
||||
}
|
||||
|
||||
#
|
||||
# Execute test case
|
||||
#
|
||||
|
||||
append qemu_args " -m 128 -nographic "
|
||||
run_genode_until {.*child exited with exit value 0.*} 60
|
||||
|
||||
puts "\ntest succeeded\n"
|
||||
|
||||
# vi: set ft=tcl :
|
892
libports/src/lib/libc_vfs/plugin.cc
Normal file
892
libports/src/lib/libc_vfs/plugin.cc
Normal file
@ -0,0 +1,892 @@
|
||||
/*
|
||||
* \brief Libc plugin for using a process-local virtual file system
|
||||
* \author Norman Feske
|
||||
* \date 2014-04-09
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2014 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/env.h>
|
||||
#include <base/printf.h>
|
||||
#include <vfs/dir_file_system.h>
|
||||
#include <os/config.h>
|
||||
|
||||
/* supported builtin file systems */
|
||||
#include <vfs/tar_file_system.h>
|
||||
#include <vfs/fs_file_system.h>
|
||||
#include <vfs/terminal_file_system.h>
|
||||
#include <vfs/null_file_system.h>
|
||||
#include <vfs/zero_file_system.h>
|
||||
#include <vfs/block_file_system.h>
|
||||
#include <vfs/log_file_system.h>
|
||||
#include <vfs/rom_file_system.h>
|
||||
#include <vfs/inline_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>
|
||||
|
||||
/* libc plugin interface */
|
||||
#include <libc-plugin/plugin.h>
|
||||
#include <libc-plugin/fd_alloc.h>
|
||||
|
||||
/* libc-internal includes */
|
||||
#include <libc_mem_alloc.h>
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
class Libc_file_system_factory : public Vfs::File_system_factory
|
||||
{
|
||||
private:
|
||||
|
||||
struct Entry_base : Genode::List<Entry_base>::Element
|
||||
{
|
||||
char const * const name;
|
||||
|
||||
Entry_base(char const *name) : name(name) { }
|
||||
|
||||
virtual Vfs::File_system *create(Genode::Xml_node node) = 0;
|
||||
|
||||
bool matches(Genode::Xml_node node) const
|
||||
{
|
||||
return node.has_type(name);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename FILE_SYSTEM>
|
||||
struct Builtin_entry : Entry_base
|
||||
{
|
||||
Builtin_entry() : Entry_base(FILE_SYSTEM::name()) { }
|
||||
|
||||
Vfs::File_system *create(Genode::Xml_node node) override
|
||||
{
|
||||
return new (Genode::env()->heap()) FILE_SYSTEM(node);
|
||||
}
|
||||
};
|
||||
|
||||
Genode::List<Entry_base> _list;
|
||||
|
||||
template <typename FILE_SYSTEM>
|
||||
void _add_builtin_fs()
|
||||
{
|
||||
_list.insert(new (Genode::env()->heap()) Builtin_entry<FILE_SYSTEM>());
|
||||
}
|
||||
|
||||
Vfs::File_system *_try_create(Genode::Xml_node node)
|
||||
{
|
||||
for (Entry_base *e = _list.first(); e; e = e->next())
|
||||
if (e->matches(node))
|
||||
return e->create(node);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Vfs::File_system *create(Genode::Xml_node node) override
|
||||
{
|
||||
/* try if type is handled by the currently registered fs types */
|
||||
if (Vfs::File_system *fs = _try_create(node))
|
||||
return fs;
|
||||
|
||||
/* XXX probe for file system implementation available as shared lib */
|
||||
|
||||
/* try again with the new file system type loaded */
|
||||
if (Vfs::File_system *fs = _try_create(node))
|
||||
return fs;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Libc_file_system_factory()
|
||||
{
|
||||
_add_builtin_fs<Vfs::Tar_file_system>();
|
||||
_add_builtin_fs<Vfs::Fs_file_system>();
|
||||
_add_builtin_fs<Vfs::Terminal_file_system>();
|
||||
_add_builtin_fs<Vfs::Null_file_system>();
|
||||
_add_builtin_fs<Vfs::Zero_file_system>();
|
||||
_add_builtin_fs<Vfs::Block_file_system>();
|
||||
_add_builtin_fs<Vfs::Log_file_system>();
|
||||
_add_builtin_fs<Vfs::Rom_file_system>();
|
||||
_add_builtin_fs<Vfs::Inline_file_system>();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static Genode::Xml_node libc_config()
|
||||
{
|
||||
return Genode::config()->xml_node().sub_node("libc");
|
||||
}
|
||||
|
||||
|
||||
static Genode::Xml_node libc_vfs_config()
|
||||
{
|
||||
return libc_config().sub_node("vfs");
|
||||
}
|
||||
|
||||
|
||||
class Libc_config_attr
|
||||
{
|
||||
private:
|
||||
|
||||
char _buf[Vfs::MAX_PATH_LEN];
|
||||
|
||||
public:
|
||||
|
||||
Libc_config_attr(char const *attr_name, char const *default_value)
|
||||
{
|
||||
strncpy(_buf, default_value, sizeof(_buf));
|
||||
try {
|
||||
libc_config().attribute(attr_name).value(_buf, sizeof(_buf));
|
||||
} catch (...) { }
|
||||
}
|
||||
|
||||
char const *string() const { return _buf; }
|
||||
};
|
||||
|
||||
|
||||
static char const *libc_initial_cwd()
|
||||
{
|
||||
static Libc_config_attr initial_cwd("cwd", "/");
|
||||
return initial_cwd.string();
|
||||
}
|
||||
|
||||
|
||||
static char const *libc_config_stdin()
|
||||
{
|
||||
static Libc_config_attr stdin("stdin", "");
|
||||
return stdin.string();
|
||||
}
|
||||
|
||||
|
||||
static char const *libc_config_stdout()
|
||||
{
|
||||
static Libc_config_attr stdout("stdout", "");
|
||||
return stdout.string();
|
||||
}
|
||||
|
||||
|
||||
static char const *libc_config_stderr()
|
||||
{
|
||||
static Libc_config_attr stderr("stderr", "");
|
||||
return stderr.string();
|
||||
}
|
||||
|
||||
|
||||
namespace Libc { class Vfs_plugin; }
|
||||
|
||||
|
||||
class Libc::Vfs_plugin : public Libc::Plugin
|
||||
{
|
||||
private:
|
||||
|
||||
Libc_file_system_factory _fs_factory;
|
||||
|
||||
Vfs::Dir_file_system _root_dir;
|
||||
|
||||
Genode::Xml_node _vfs_config()
|
||||
{
|
||||
try {
|
||||
return libc_vfs_config();
|
||||
} catch (...) {
|
||||
PINF("no VFS configured");
|
||||
return Genode::Xml_node("<vfs/>");
|
||||
}
|
||||
}
|
||||
|
||||
void _open_stdio(int libc_fd, char const *path, unsigned flags)
|
||||
{
|
||||
struct stat out_stat;
|
||||
if (::strlen(path) == 0 || stat(path, &out_stat) != 0)
|
||||
return;
|
||||
|
||||
Libc::File_descriptor *fd = open(path, flags, libc_fd);
|
||||
if (fd->libc_fd != libc_fd) {
|
||||
PERR("could not allocate fd %d for %s, got fd %d",
|
||||
libc_fd, path, 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.
|
||||
*/
|
||||
fd->fd_path = strdup(path);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Vfs_plugin() : _root_dir(_vfs_config(), _fs_factory)
|
||||
{
|
||||
chdir(libc_initial_cwd());
|
||||
|
||||
_open_stdio(0, libc_config_stdin(), O_RDONLY);
|
||||
_open_stdio(1, libc_config_stdout(), O_WRONLY);
|
||||
_open_stdio(2, libc_config_stderr(), O_WRONLY);
|
||||
}
|
||||
|
||||
~Vfs_plugin() { }
|
||||
|
||||
bool supports_mkdir(const char *, mode_t) override { return true; }
|
||||
bool supports_open(const char *, int) override { return true; }
|
||||
bool supports_readlink(const char *, char *, size_t) override { return true; }
|
||||
bool supports_rename(const char *, const char *) override { return true; }
|
||||
bool supports_rmdir(const char *) override { return true; }
|
||||
bool supports_stat(const char *) override { return true; }
|
||||
bool supports_symlink(const char *, const char *) override { return true; }
|
||||
bool supports_unlink(const char *) override { return true; }
|
||||
bool supports_mmap() override { return true; }
|
||||
|
||||
Libc::File_descriptor *open(const char *, int, int libc_fd);
|
||||
|
||||
Libc::File_descriptor *open(const char *path, int flags) override
|
||||
{
|
||||
return open(path, flags, Libc::ANY_FD);
|
||||
}
|
||||
|
||||
int close(Libc::File_descriptor *) override;
|
||||
int dup2(Libc::File_descriptor *, Libc::File_descriptor *) override;
|
||||
int fcntl(Libc::File_descriptor *, int, long) override;
|
||||
int fstat(Libc::File_descriptor *, struct stat *) override;
|
||||
int fstatfs(Libc::File_descriptor *, struct statfs *) override;
|
||||
int fsync(Libc::File_descriptor *fd) override;
|
||||
int ftruncate(Libc::File_descriptor *, ::off_t) override;
|
||||
ssize_t getdirentries(Libc::File_descriptor *, char *, ::size_t , ::off_t *) override;
|
||||
int ioctl(Libc::File_descriptor *, int , char *) override;
|
||||
::off_t lseek(Libc::File_descriptor *fd, ::off_t offset, int whence) override;
|
||||
int mkdir(const char *, mode_t) override;
|
||||
ssize_t read(Libc::File_descriptor *, void *, ::size_t) override;
|
||||
ssize_t readlink(const char *, char *, size_t) override;
|
||||
int rename(const char *, const char *) override;
|
||||
int rmdir(const char *) override;
|
||||
int stat(const char *, struct stat *) override;
|
||||
int symlink(const char *, const char *) override;
|
||||
int unlink(const char *) override;
|
||||
ssize_t write(Libc::File_descriptor *, const void *, ::size_t ) override;
|
||||
void *mmap(void *, ::size_t, int, int, Libc::File_descriptor *, ::off_t) override;
|
||||
int munmap(void *, ::size_t) override;
|
||||
};
|
||||
|
||||
|
||||
Libc::File_descriptor *Libc::Vfs_plugin::open(char const *path, int flags,
|
||||
int libc_fd)
|
||||
{
|
||||
typedef Vfs::Directory_service::Open_result Result;
|
||||
|
||||
Vfs::Vfs_handle *handle = 0;
|
||||
|
||||
while (handle == 0) {
|
||||
|
||||
switch (_root_dir.open(path, flags, &handle)) {
|
||||
|
||||
case Result::OPEN_OK:
|
||||
errno = 0;
|
||||
break;
|
||||
|
||||
case Result::OPEN_ERR_UNACCESSIBLE:
|
||||
{
|
||||
if (!(flags & O_CREAT)) {
|
||||
errno = ENOENT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* O_CREAT is set, so try to create the file */
|
||||
switch (_root_dir.open(path, flags | O_EXCL, &handle)) {
|
||||
|
||||
case Result::OPEN_OK:
|
||||
errno = 0;
|
||||
break;
|
||||
|
||||
case Result::OPEN_ERR_EXISTS:
|
||||
|
||||
/* file has been created by someone else in the meantime */
|
||||
break;
|
||||
|
||||
case Result::OPEN_ERR_NO_PERM: errno = EPERM; return 0;
|
||||
case Result::OPEN_ERR_UNACCESSIBLE: errno = ENOENT; return 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Result::OPEN_ERR_NO_PERM: errno = EPERM; return 0;
|
||||
case Result::OPEN_ERR_EXISTS: errno = EEXIST; return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* the file was successfully opened */
|
||||
|
||||
Libc::File_descriptor *fd =
|
||||
Libc::file_descriptor_allocator()->alloc(this, vfs_context(handle), libc_fd);
|
||||
|
||||
fd->status = flags;
|
||||
|
||||
if ((flags & O_TRUNC) && (ftruncate(fd, 0) == -1)) {
|
||||
/* XXX leaking fd, missing errno */
|
||||
return 0;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
||||
int Libc::Vfs_plugin::close(Libc::File_descriptor *fd)
|
||||
{
|
||||
Genode::destroy(Genode::env()->heap(), vfs_handle(fd));
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
int Libc::Vfs_plugin::fstatfs(Libc::File_descriptor *, struct statfs *buf)
|
||||
{
|
||||
buf->f_flags = MNT_UNION;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int Libc::Vfs_plugin::mkdir(const char *path, mode_t mode)
|
||||
{
|
||||
typedef Vfs::Directory_service::Mkdir_result Result;
|
||||
|
||||
switch (_root_dir.mkdir(path, mode)) {
|
||||
case Result::MKDIR_ERR_EXISTS: errno = EEXIST; return -1;
|
||||
case Result::MKDIR_ERR_NO_ENTRY: errno = ENOENT; return -1;
|
||||
case Result::MKDIR_ERR_NO_SPACE: errno = ENOSPC; return -1;
|
||||
case Result::MKDIR_ERR_NAME_TOO_LONG: errno = ENAMETOOLONG; return -1;
|
||||
case Result::MKDIR_ERR_NO_PERM: errno = EPERM; return -1;
|
||||
case Result::MKDIR_OK: errno = 0; break;
|
||||
}
|
||||
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;
|
||||
case Result::STAT_OK: errno = 0; break;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
size_t out_count = 0;
|
||||
|
||||
switch (handle->fs().write(handle, (char const *)buf, count, out_count)) {
|
||||
case Result::WRITE_ERR_AGAIN: errno = EAGAIN; return -1;
|
||||
case Result::WRITE_ERR_WOULD_BLOCK: errno = EWOULDBLOCK; return -1;
|
||||
case Result::WRITE_ERR_INVALID: errno = EINVAL; return -1;
|
||||
case Result::WRITE_ERR_IO: errno = EIO; return -1;
|
||||
case Result::WRITE_ERR_INTERRUPT: errno = EINTR; return -1;
|
||||
case Result::WRITE_OK: errno = 0; break;
|
||||
}
|
||||
|
||||
handle->advance_seek(out_count);
|
||||
|
||||
return out_count;
|
||||
}
|
||||
|
||||
|
||||
ssize_t Libc::Vfs_plugin::read(Libc::File_descriptor *fd, void *buf,
|
||||
::size_t count)
|
||||
{
|
||||
typedef Vfs::File_io_service::Read_result Result;
|
||||
|
||||
Vfs::Vfs_handle *handle = vfs_handle(fd);
|
||||
|
||||
Genode::size_t out_count = 0;
|
||||
|
||||
switch (handle->fs().read(handle, (char *)buf, count, out_count)) {
|
||||
case Result::READ_ERR_AGAIN: errno = EAGAIN; PERR("A1"); return -1;
|
||||
case Result::READ_ERR_WOULD_BLOCK: errno = EWOULDBLOCK; PERR("A2"); return -1;
|
||||
case Result::READ_ERR_INVALID: errno = EINVAL; PERR("A3"); return -1;
|
||||
case Result::READ_ERR_IO: errno = EIO; PERR("A4"); return -1;
|
||||
case Result::READ_ERR_INTERRUPT: errno = EINTR; PERR("A5"); return -1;
|
||||
case Result::READ_OK: errno = 0; break;
|
||||
}
|
||||
|
||||
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)) {
|
||||
PERR("getdirentries: buffer too small");
|
||||
return -1;
|
||||
}
|
||||
|
||||
typedef Vfs::Directory_service::Dirent_result Result;
|
||||
|
||||
Vfs::Vfs_handle *handle = vfs_handle(fd);
|
||||
|
||||
Vfs::Directory_service::Dirent dirent_out;
|
||||
Genode::memset(&dirent_out, 0, sizeof(dirent_out));
|
||||
|
||||
unsigned const index = handle->seek() / sizeof(Vfs::Directory_service::Dirent);
|
||||
|
||||
switch (handle->ds().dirent(fd->fd_path, index, dirent_out)) {
|
||||
case Result::DIRENT_ERR_INVALID_PATH: /* XXX errno */ return -1;
|
||||
case Result::DIRENT_OK: errno = 0; break;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
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;
|
||||
}
|
||||
|
||||
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:
|
||||
{
|
||||
opcode = Opcode::IOCTL_OP_DIOCGMEDIASIZE;
|
||||
arg = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
PWRN("unsupported ioctl (request=0x%x)", request);
|
||||
break;
|
||||
}
|
||||
|
||||
if (opcode == Opcode::IOCTL_OP_UNDEFINED) {
|
||||
errno = ENOTTY;
|
||||
return -1;
|
||||
}
|
||||
|
||||
typedef Vfs::File_io_service::Ioctl_result Result;
|
||||
|
||||
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)) {
|
||||
case Vfs::File_io_service::IOCTL_ERR_INVALID: errno = EINVAL; return -1;
|
||||
case Vfs::File_io_service::IOCTL_ERR_NOTTY: errno = ENOTTY; return -1;
|
||||
case Vfs::File_io_service::IOCTL_OK: errno = 0; break;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
int64_t *disk_size = (int64_t*)arg;
|
||||
*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)) {
|
||||
case Result::FTRUNCATE_ERR_NO_PERM: errno = EPERM; return -1;
|
||||
case Result::FTRUNCATE_ERR_INTERRUPT: errno = EINTR; return -1;
|
||||
case Result::FTRUNCATE_OK: errno = 0; break;
|
||||
}
|
||||
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);
|
||||
new_fd->path(fd->fd_path);
|
||||
|
||||
/*
|
||||
* Use new allocated number as name of file descriptor
|
||||
* duplicate.
|
||||
*/
|
||||
if (dup2(fd, new_fd) == -1) {
|
||||
PERR("Plugin::fcntl: dup2 unexpectedly failed");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return new_fd->libc_fd;
|
||||
}
|
||||
case F_GETFD: return fd->flags;
|
||||
case F_SETFD: fd->flags = arg; return 0;
|
||||
case F_GETFL: return fd->status;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
PERR("fcntl(): command %d not supported", cmd);
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int Libc::Vfs_plugin::fsync(Libc::File_descriptor *fd)
|
||||
{
|
||||
_root_dir.sync();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int Libc::Vfs_plugin::symlink(const char *oldpath, const char *newpath)
|
||||
{
|
||||
typedef Vfs::Directory_service::Symlink_result Result;
|
||||
|
||||
switch (_root_dir.symlink(oldpath, newpath)) {
|
||||
case Result::SYMLINK_ERR_EXISTS: errno = EEXIST; return -1;
|
||||
case Result::SYMLINK_ERR_NO_ENTRY: errno = ENOENT; return -1;
|
||||
case Result::SYMLINK_ERR_NAME_TOO_LONG: errno = ENAMETOOLONG; return -1;
|
||||
case Result::SYMLINK_ERR_NO_PERM: errno = ENOSYS; return -1;
|
||||
case Result::SYMLINK_OK: errno = 0; break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
ssize_t Libc::Vfs_plugin::readlink(const char *path, char *buf, size_t buf_size)
|
||||
{
|
||||
typedef Vfs::Directory_service::Readlink_result Result;
|
||||
|
||||
size_t out_len = 0;
|
||||
|
||||
switch (_root_dir.readlink(path, buf, buf_size, out_len)) {
|
||||
case Result::READLINK_ERR_NO_ENTRY: errno = ENOENT; return -1;
|
||||
case Result::READLINK_OK: errno = 0; break;
|
||||
};
|
||||
|
||||
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)) {
|
||||
case Result::UNLINK_ERR_NO_ENTRY: errno = ENOENT; return -1;
|
||||
case Result::UNLINK_ERR_NO_PERM: errno = EPERM; return -1;
|
||||
case Result::UNLINK_OK: errno = 0; break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int Libc::Vfs_plugin::rename(char const *from_path, char const *to_path)
|
||||
{
|
||||
typedef Vfs::Directory_service::Rename_result Result;
|
||||
|
||||
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;
|
||||
case Result::RENAME_OK: errno = 0; break;
|
||||
}
|
||||
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) {
|
||||
PERR("mmap for prot=%x not supported", prot);
|
||||
errno = EACCES;
|
||||
return (void *)-1;
|
||||
}
|
||||
|
||||
if (addr_in != 0) {
|
||||
PERR("mmap for predefined address not supported");
|
||||
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) {
|
||||
PERR("mmap could not obtain file content");
|
||||
::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;
|
||||
}
|
||||
|
||||
|
||||
void __attribute__((constructor)) init_libc_vfs(void)
|
||||
{
|
||||
static Libc::Vfs_plugin plugin;
|
||||
}
|
6
libports/src/test/libc_vfs/target.mk
Normal file
6
libports/src/test/libc_vfs/target.mk
Normal file
@ -0,0 +1,6 @@
|
||||
TARGET = test-libc_vfs
|
||||
LIBS = libc libc_log libc_vfs
|
||||
SRC_CC = main.cc
|
||||
|
||||
# we re-use the libc_ffat test
|
||||
vpath main.cc $(REP_DIR)/src/test/libc_ffat
|
@ -8,6 +8,7 @@ tar_rom
|
||||
noux
|
||||
noux_net_netcat
|
||||
libc_ffat
|
||||
libc_vfs
|
||||
timed_semaphore
|
||||
signal
|
||||
sub_rm
|
||||
|
Loading…
Reference in New Issue
Block a user