2414 lines
63 KiB
C++
2414 lines
63 KiB
C++
/*
|
|
* \brief Noux libc plugin
|
|
* \author Norman Feske
|
|
* \date 2011-02-14
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2011-2017 Genode Labs GmbH
|
|
*
|
|
* This file is part of the Genode OS framework, which is distributed
|
|
* under the terms of the GNU Affero General Public License version 3.
|
|
*/
|
|
|
|
/* Genode includes */
|
|
#include <util/construct_at.h>
|
|
#include <util/misc_math.h>
|
|
#include <util/arg_string.h>
|
|
#include <base/log.h>
|
|
#include <base/attached_rom_dataspace.h>
|
|
#include <base/sleep.h>
|
|
#include <dataspace/client.h>
|
|
#include <region_map/client.h>
|
|
|
|
/* noux includes */
|
|
#include <noux_session/connection.h>
|
|
#include <noux_session/sysio.h>
|
|
|
|
/* libc plugin includes */
|
|
#include <libc-plugin/plugin.h>
|
|
#include <libc-plugin/fd_alloc.h>
|
|
|
|
/* libc component includes */
|
|
#include <libc/component.h>
|
|
|
|
/* libc includes */
|
|
#include <errno.h>
|
|
#include <sys/disk.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/fcntl.h>
|
|
#include <sys/dirent.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/resource.h>
|
|
#include <unistd.h>
|
|
#include <termios.h>
|
|
#include <pwd.h>
|
|
#include <string.h>
|
|
#include <signal.h>
|
|
|
|
/**
|
|
* There is a off_t typedef clash between sys/socket.h
|
|
* and base/stdint.h. We define the macro here to circumvent
|
|
* this issue.
|
|
*/
|
|
#undef DIOCGMEDIASIZE
|
|
#define DIOCGMEDIASIZE _IOR('d', 129, int64_t)
|
|
|
|
|
|
/* libc-internal includes */
|
|
#include <internal/mem_alloc.h>
|
|
#include <internal/legacy.h>
|
|
|
|
|
|
using Genode::log;
|
|
using Genode::error;
|
|
using Genode::warning;
|
|
using Genode::Hex;
|
|
|
|
|
|
enum { verbose = false };
|
|
enum { verbose_signals = false };
|
|
|
|
|
|
/*
|
|
* Customize libc VFS
|
|
*/
|
|
namespace Libc {
|
|
|
|
/*
|
|
* Override the weak function interface of the libc and VFS plugin as Noux
|
|
* programs do not obtain such configuration via Genode's config mechanism.
|
|
*/
|
|
Genode::Xml_node config() { return Xml_node("<libc/>"); }
|
|
Genode::Xml_node vfs_config() { return Xml_node("<vfs/>"); }
|
|
|
|
/*
|
|
* Enhance main-thread stack
|
|
*
|
|
* This is done because we ran into a stack overflow while compiling
|
|
* Genodes core with GCC in Noux.
|
|
*/
|
|
enum { STACK_SIZE = 64UL * 1024 * sizeof(Genode::addr_t) };
|
|
Genode::size_t Component::stack_size() { return STACK_SIZE; }
|
|
}
|
|
|
|
|
|
|
|
class Noux_connection
|
|
{
|
|
private:
|
|
|
|
Genode::Env &_env;
|
|
|
|
Noux::Connection _connection;
|
|
|
|
Genode::Attached_dataspace _sysio_ds { _env.rm(), _connection.sysio_dataspace() };
|
|
|
|
public:
|
|
|
|
Noux_connection(Genode::Env &env) : _env(env), _connection(env) { }
|
|
|
|
/**
|
|
* Return the capability of the local stack-area region map
|
|
*
|
|
* \param ptr some address within the stack-area
|
|
*/
|
|
Genode::Capability<Genode::Region_map> stack_area_region_map(void * const ptr)
|
|
{
|
|
return _connection.lookup_region_map((Genode::addr_t)ptr);
|
|
}
|
|
|
|
Noux::Session *session() { return &_connection; }
|
|
Noux::Sysio *sysio() { return _sysio_ds.local_addr<Noux::Sysio>(); }
|
|
|
|
void reconnect()
|
|
{
|
|
using namespace Genode;
|
|
|
|
/*
|
|
* Release Id_space<Parent::Client>::Element of the local ID space.
|
|
*/
|
|
_connection.discard_session_id();
|
|
|
|
/*
|
|
* Obtain new noux connection. Note that we cannot reconstruct
|
|
* the connection via a 'Reconstructible' because this would
|
|
* result in an inconsistent referernce count when attempting
|
|
* to destruct the session capability in the just-cleared
|
|
* capability space.
|
|
*/
|
|
construct_at<Noux_connection>(this, _env);
|
|
}
|
|
};
|
|
|
|
|
|
/*
|
|
* Defined by Libc::Plugin::init
|
|
*/
|
|
static Genode::Env *_env_ptr = nullptr;
|
|
|
|
Noux_connection *noux_connection()
|
|
{
|
|
if (!_env_ptr) {
|
|
Genode::error("missing call of 'Libc::Plugin::init'");
|
|
return nullptr;
|
|
}
|
|
|
|
static Noux_connection inst(*_env_ptr);
|
|
return &inst;
|
|
}
|
|
|
|
|
|
Noux::Session *noux() { return noux_connection()->session(); }
|
|
Noux::Sysio *sysio() { return noux_connection()->sysio(); }
|
|
|
|
|
|
/*
|
|
* Array of signal handlers, initialized with 0 (SIG_DFL)
|
|
* TODO: preserve ignored signals across 'execve()'
|
|
*/
|
|
static struct sigaction signal_action[NSIG+1];
|
|
|
|
/*
|
|
* Signal mask functionality is not fully implemented yet.
|
|
* TODO: - actually block delivery of to be blocked signals
|
|
* - preserve signal mask across 'execve()'
|
|
*/
|
|
static sigset_t signal_mask;
|
|
|
|
|
|
static bool noux_syscall(Noux::Session::Syscall opcode)
|
|
{
|
|
/*
|
|
* Signal handlers might do syscalls themselves, so the 'sysio' object
|
|
* needs to be saved before and restored after calling the signal handler.
|
|
* There is only one global 'saved_sysio' buffer as signals are not checked
|
|
* in nested calls of 'noux_syscall' from signal handlers.
|
|
*/
|
|
static Noux::Sysio saved_sysio;
|
|
|
|
bool ret = noux()->syscall(opcode);
|
|
|
|
static bool in_sigh = false; /* true if called from signal handler */
|
|
|
|
if (in_sigh)
|
|
return ret;
|
|
|
|
/* handle signals */
|
|
while (!sysio()->pending_signals.empty()) {
|
|
in_sigh = true;
|
|
Noux::Sysio::Signal signal = sysio()->pending_signals.get();
|
|
if (verbose_signals)
|
|
log(__func__, ": received signal ", (int)signal);
|
|
if (signal_action[signal].sa_flags & SA_SIGINFO) {
|
|
memcpy(&saved_sysio, sysio(), sizeof(Noux::Sysio));
|
|
/* TODO: pass siginfo_t struct */
|
|
signal_action[signal].sa_sigaction(signal, 0, 0);
|
|
memcpy(sysio(), &saved_sysio, sizeof(Noux::Sysio));
|
|
} else {
|
|
if (signal_action[signal].sa_handler == SIG_DFL) {
|
|
switch (signal) {
|
|
case SIGCHLD:
|
|
case SIGWINCH:
|
|
/* ignore */
|
|
break;
|
|
default:
|
|
/* terminate the process */
|
|
exit((signal << 8) | EXIT_FAILURE);
|
|
}
|
|
} else if (signal_action[signal].sa_handler == SIG_IGN) {
|
|
/* do nothing */
|
|
} else {
|
|
memcpy(&saved_sysio, sysio(), sizeof(Noux::Sysio));
|
|
signal_action[signal].sa_handler(signal);
|
|
memcpy(sysio(), &saved_sysio, sizeof(Noux::Sysio));
|
|
}
|
|
}
|
|
}
|
|
in_sigh = false;
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
enum { FS_BLOCK_SIZE = 4096 };
|
|
|
|
|
|
/***********************************************
|
|
** Overrides of libc default implementations **
|
|
***********************************************/
|
|
|
|
/**
|
|
* User information related functions
|
|
*/
|
|
extern "C" struct passwd *getpwuid(uid_t uid)
|
|
{
|
|
static char name[Noux::Sysio::MAX_USERNAME_LEN];
|
|
static char shell[Noux::Sysio::MAX_SHELL_LEN];
|
|
static char home[Noux::Sysio::MAX_HOME_LEN];
|
|
|
|
static char *empty = strdup("");
|
|
|
|
static struct passwd pw = {
|
|
/* .pw_name = */ name,
|
|
/* .pw_passwd = */ empty,
|
|
/* .pw_uid = */ 0,
|
|
/* .pw_gid = */ 0,
|
|
/* .pw_change = */ 0,
|
|
/* .pw_class = */ empty,
|
|
/* .pw_gecos = */ empty,
|
|
/* .pw_dir = */ home,
|
|
/* .pw_shell = */ shell,
|
|
/* .pw_expire = */ 0,
|
|
/* .pw_fields = */ 0
|
|
};
|
|
|
|
sysio()->userinfo_in.uid = uid;
|
|
sysio()->userinfo_in.request = Noux::Sysio::USERINFO_GET_ALL;
|
|
|
|
if (!noux_syscall(Noux::Session::SYSCALL_USERINFO)) {
|
|
return (struct passwd *)0;
|
|
}
|
|
|
|
/* SYSCALL_USERINFO assures that strings are always '\0' terminated */
|
|
Genode::memcpy(name, sysio()->userinfo_out.name, sizeof (sysio()->userinfo_out.name));
|
|
Genode::memcpy(home, sysio()->userinfo_out.home, sizeof (sysio()->userinfo_out.home));
|
|
Genode::memcpy(shell, sysio()->userinfo_out.shell, sizeof (sysio()->userinfo_out.shell));
|
|
|
|
pw.pw_uid = sysio()->userinfo_out.uid;
|
|
pw.pw_gid = sysio()->userinfo_out.gid;
|
|
|
|
return &pw;
|
|
}
|
|
|
|
|
|
extern "C" int getdtablesize()
|
|
{
|
|
if (!noux_syscall(Noux::Session::SYSCALL_GETDTABLESIZE)) {
|
|
warning("getdtablesize syscall failed");
|
|
errno = ENOSYS;
|
|
return -1;
|
|
}
|
|
|
|
int n = sysio()->getdtablesize_out.n;
|
|
return n;
|
|
}
|
|
|
|
|
|
extern "C" uid_t getgid()
|
|
{
|
|
sysio()->userinfo_in.request = Noux::Sysio::USERINFO_GET_GID;
|
|
|
|
if (!noux_syscall(Noux::Session::SYSCALL_USERINFO))
|
|
return 0;
|
|
|
|
uid_t gid = sysio()->userinfo_out.gid;
|
|
return gid;
|
|
}
|
|
|
|
|
|
extern "C" uid_t getegid()
|
|
{
|
|
return getgid();
|
|
}
|
|
|
|
|
|
extern "C" uid_t getuid()
|
|
{
|
|
sysio()->userinfo_in.request = Noux::Sysio::USERINFO_GET_UID;
|
|
|
|
if (!noux_syscall(Noux::Session::SYSCALL_USERINFO))
|
|
return 0;
|
|
|
|
uid_t uid = sysio()->userinfo_out.uid;
|
|
|
|
return uid;
|
|
}
|
|
|
|
|
|
extern "C" uid_t geteuid()
|
|
{
|
|
return getuid();
|
|
}
|
|
|
|
|
|
void *sbrk(intptr_t increment)
|
|
{
|
|
if (verbose)
|
|
warning(__func__, " not implemented ", (long int)increment);
|
|
errno = ENOMEM;
|
|
return reinterpret_cast<void *>(-1);
|
|
}
|
|
|
|
|
|
extern "C" int getrlimit(int resource, struct rlimit *rlim)
|
|
{
|
|
switch (resource) {
|
|
case RLIMIT_STACK:
|
|
{
|
|
using namespace Genode;
|
|
|
|
Thread * me = Thread::myself();
|
|
|
|
if (!me)
|
|
break;
|
|
|
|
addr_t top = reinterpret_cast<addr_t>(me->stack_top());
|
|
addr_t cur = reinterpret_cast<addr_t>(me->stack_base());
|
|
|
|
rlim->rlim_cur = rlim->rlim_max = top - cur;
|
|
return 0;
|
|
}
|
|
case RLIMIT_AS:
|
|
#ifdef __x86_64__
|
|
rlim->rlim_cur = rlim->rlim_max = 0x800000000000UL;
|
|
#else
|
|
rlim->rlim_cur = rlim->rlim_max = 0xc0000000UL;
|
|
#endif
|
|
return 0;
|
|
case RLIMIT_RSS:
|
|
rlim->rlim_cur = rlim->rlim_max = _env_ptr->pd().ram_quota().value;
|
|
return 0;
|
|
case RLIMIT_NPROC:
|
|
case RLIMIT_NOFILE:
|
|
rlim->rlim_cur = rlim->rlim_max = RLIM_INFINITY;
|
|
return 0;
|
|
}
|
|
errno = ENOSYS;
|
|
return -1;
|
|
}
|
|
|
|
|
|
/**
|
|
* Utility to copy-out syscall results to buf struct
|
|
*
|
|
* Code shared between 'stat' and 'fstat'.
|
|
*/
|
|
static void _sysio_to_stat_struct(Noux::Sysio const *sysio, struct stat *buf)
|
|
{
|
|
unsigned const readable_bits = S_IRUSR,
|
|
writeable_bits = S_IWUSR,
|
|
executable_bits = S_IXUSR;
|
|
|
|
auto type = [] (Vfs::Node_type type)
|
|
{
|
|
switch (type) {
|
|
case Vfs::Node_type::DIRECTORY: return S_IFDIR;
|
|
case Vfs::Node_type::CONTINUOUS_FILE: return S_IFREG;
|
|
case Vfs::Node_type::TRANSACTIONAL_FILE: return S_IFSOCK;
|
|
case Vfs::Node_type::SYMLINK: return S_IFLNK;
|
|
}
|
|
return 0;
|
|
};
|
|
|
|
Vfs::Directory_service::Stat const &src = sysio->stat_out.st;
|
|
|
|
*buf = { };
|
|
|
|
buf->st_uid = 0;
|
|
buf->st_gid = 0;
|
|
buf->st_mode = (src.rwx.readable ? readable_bits : 0)
|
|
| (src.rwx.writeable ? writeable_bits : 0)
|
|
| (src.rwx.executable ? executable_bits : 0)
|
|
| type(src.type);
|
|
buf->st_size = src.size;
|
|
buf->st_blksize = FS_BLOCK_SIZE;
|
|
buf->st_blocks = (src.size + FS_BLOCK_SIZE - 1) / FS_BLOCK_SIZE;
|
|
buf->st_ino = src.inode;
|
|
buf->st_dev = src.device;
|
|
|
|
if (src.modification_time.value >= 0)
|
|
buf->st_mtime = src.modification_time.value;
|
|
}
|
|
|
|
|
|
static bool serialize_string_array(char const * const * array, char *dst, Genode::size_t dst_len)
|
|
{
|
|
for (unsigned i = 0; array[i]; i++)
|
|
{
|
|
Genode::size_t const curr_len = Genode::strlen(array[i]) + 1;
|
|
if (curr_len + 1 >= dst_len)
|
|
return false;
|
|
|
|
Genode::strncpy(dst, array[i], dst_len);
|
|
dst += curr_len;
|
|
dst_len -= curr_len;
|
|
}
|
|
|
|
dst[0] = 0;
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return number of marhalled file descriptors into select argument buffer
|
|
*
|
|
* \return number of marshalled file descriptors, this value is guaranteed to
|
|
* not exceed the 'dst_fds_len' argument
|
|
*/
|
|
static size_t marshal_fds(fd_set *src_fds, int nfds,
|
|
int *dst_fds, size_t dst_fds_len)
|
|
{
|
|
if (!src_fds) return 0;
|
|
|
|
size_t num_fds = 0;
|
|
|
|
for (int fd = 0; (fd < nfds) && (num_fds < dst_fds_len); fd++)
|
|
if (FD_ISSET(fd, src_fds))
|
|
dst_fds[num_fds++] = fd;
|
|
|
|
return num_fds;
|
|
}
|
|
|
|
|
|
/**
|
|
* Unmarshal result of select syscall into fd set
|
|
*/
|
|
static void unmarshal_fds(int nfds, int *src_fds, size_t src_fds_len, fd_set *dst_fds)
|
|
{
|
|
if (!dst_fds) return;
|
|
|
|
/**
|
|
* Calling FD_ZERO will not work because it will try to reset sizeof (fd_set)
|
|
* which is typically 128 bytes but dst_fds might by even less bytes large if
|
|
* it was allocated dynamically. So we will reset the fd_set manually which
|
|
* will work fine as long as we are using FreeBSDs libc - another libc however
|
|
* might use a different struct.
|
|
*
|
|
* Note: The fds are actually stored in a bit-array. So we need to calculate
|
|
* how many array entries we have to reset. sizeof (fd_mask) will return the
|
|
* size of one entry in bytes.
|
|
*/
|
|
int _ = nfds / (sizeof (fd_mask) * 8) + 1;
|
|
for (int i = 0; i < _; i++)
|
|
dst_fds->__fds_bits[i] = 0;
|
|
|
|
for (size_t i = 0; i < src_fds_len; i++)
|
|
FD_SET(src_fds[i], dst_fds);
|
|
}
|
|
|
|
|
|
extern "C" int select(int nfds, fd_set *readfds, fd_set *writefds,
|
|
fd_set *exceptfds, struct timeval *timeout)
|
|
{
|
|
|
|
/*
|
|
* Marshal file descriptor into sysio page
|
|
*/
|
|
Noux::Sysio::Select_fds &in_fds = sysio()->select_in.fds;
|
|
|
|
int *dst = in_fds.array;
|
|
size_t dst_len = Noux::Sysio::Select_fds::MAX_FDS;
|
|
|
|
/**
|
|
* These variables are used in max_fds_exceeded() calculation, so
|
|
* they need to be proper initialized.
|
|
*/
|
|
in_fds.num_rd = 0;
|
|
in_fds.num_wr = 0;
|
|
in_fds.num_ex = 0;
|
|
|
|
if (readfds != NULL) {
|
|
in_fds.num_rd = marshal_fds(readfds, nfds, dst, dst_len);
|
|
|
|
dst += in_fds.num_rd;
|
|
dst_len -= in_fds.num_rd;
|
|
}
|
|
|
|
if (writefds != NULL) {
|
|
in_fds.num_wr = marshal_fds(writefds, nfds, dst, dst_len);
|
|
|
|
dst += in_fds.num_wr;
|
|
dst_len -= in_fds.num_wr;
|
|
}
|
|
|
|
if (exceptfds != NULL) {
|
|
in_fds.num_ex = marshal_fds(exceptfds, nfds, dst, dst_len);
|
|
}
|
|
|
|
if (in_fds.max_fds_exceeded()) {
|
|
errno = ENOMEM;
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Marshal timeout
|
|
*/
|
|
if (timeout) {
|
|
sysio()->select_in.timeout.sec = timeout->tv_sec;
|
|
sysio()->select_in.timeout.usec = timeout->tv_usec;
|
|
} else {
|
|
sysio()->select_in.timeout.set_infinite();
|
|
}
|
|
|
|
/*
|
|
* Perform syscall
|
|
*/
|
|
if (!noux_syscall(Noux::Session::SYSCALL_SELECT)) {
|
|
switch (sysio()->error.select) {
|
|
case Noux::Sysio::SELECT_ERR_INTERRUPT: errno = EINTR; break;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Unmarshal file selectors reported by the select syscall
|
|
*/
|
|
Noux::Sysio::Select_fds &out_fds = sysio()->select_out.fds;
|
|
|
|
int *src = out_fds.array;
|
|
int total_fds = 0;
|
|
|
|
if (readfds != NULL) {
|
|
unmarshal_fds(nfds, src, out_fds.num_rd, readfds);
|
|
src += out_fds.num_rd;
|
|
total_fds += out_fds.num_rd;
|
|
}
|
|
|
|
if (writefds != NULL) {
|
|
unmarshal_fds(nfds, src, out_fds.num_wr, writefds);
|
|
src += out_fds.num_wr;
|
|
total_fds += out_fds.num_wr;
|
|
}
|
|
|
|
if (exceptfds != NULL) {
|
|
unmarshal_fds(nfds, src, out_fds.num_ex, exceptfds);
|
|
/* exceptfds are currently ignored */
|
|
}
|
|
|
|
return total_fds;
|
|
}
|
|
|
|
|
|
#include <setjmp.h>
|
|
|
|
|
|
static void * in_stack_area;
|
|
static jmp_buf fork_jmp_buf;
|
|
static Genode::Capability<Genode::Parent>::Raw new_parent;
|
|
|
|
extern "C" void stdout_reconnect(Genode::Parent &); /* provided by 'default_log.cc' */
|
|
|
|
|
|
/*
|
|
* The new process created via fork will start its execution here.
|
|
*/
|
|
extern "C" void fork_trampoline()
|
|
{
|
|
/* reinitialize environment */
|
|
using namespace Genode;
|
|
_env_ptr->reinit(new_parent);
|
|
|
|
/* reinitialize standard-output connection */
|
|
stdout_reconnect(_env_ptr->parent());
|
|
|
|
/* reinitialize noux connection */
|
|
noux_connection()->reconnect();
|
|
|
|
/* reinitialize main-thread object which implies reinit of stack area */
|
|
auto stack_area_rm = noux_connection()->stack_area_region_map(in_stack_area);
|
|
_env_ptr->reinit_main_thread(stack_area_rm);
|
|
|
|
/* apply processor state that the forker had when he did the fork */
|
|
longjmp(fork_jmp_buf, 1);
|
|
}
|
|
|
|
|
|
static pid_t fork_result;
|
|
|
|
|
|
/**
|
|
* Called once the component has left the entrypoint and exited the signal
|
|
* dispatch loop.
|
|
*
|
|
* This function is called from the context of the initial thread.
|
|
*/
|
|
static void suspended_callback()
|
|
{
|
|
/* stack used for executing 'fork_trampoline' */
|
|
enum { STACK_SIZE = 8 * 1024 };
|
|
static long stack[STACK_SIZE];
|
|
|
|
if (setjmp(fork_jmp_buf)) {
|
|
|
|
/*
|
|
* We got here via longjmp from 'fork_trampoline'.
|
|
*/
|
|
fork_result = 0;
|
|
|
|
} else {
|
|
|
|
/*
|
|
* save the current stack address used for re-initializing
|
|
* the stack area during process bootstrap
|
|
*/
|
|
int dummy;
|
|
in_stack_area = &dummy;
|
|
|
|
/* got here during the normal control flow of the fork call */
|
|
sysio()->fork_in.ip = (Genode::addr_t)(&fork_trampoline);
|
|
sysio()->fork_in.sp = Abi::stack_align((Genode::addr_t)&stack[STACK_SIZE]);
|
|
sysio()->fork_in.parent_cap_addr = (Genode::addr_t)(&new_parent);
|
|
|
|
if (!noux_syscall(Noux::Session::SYSCALL_FORK)) {
|
|
error("fork error ", (int)sysio()->error.general);
|
|
switch (sysio()->error.fork) {
|
|
case Noux::Sysio::FORK_NOMEM: errno = ENOMEM; break;
|
|
default: errno = EAGAIN;
|
|
}
|
|
fork_result = -1;
|
|
return;
|
|
}
|
|
|
|
fork_result = sysio()->fork_out.pid;
|
|
}
|
|
}
|
|
|
|
|
|
namespace Libc { void schedule_suspend(void (*suspended) ()); }
|
|
|
|
|
|
extern "C" pid_t fork(void)
|
|
{
|
|
Libc::schedule_suspend(suspended_callback);
|
|
|
|
return fork_result;
|
|
}
|
|
|
|
|
|
extern "C" pid_t vfork(void) { return fork(); }
|
|
|
|
|
|
extern "C" pid_t getpid(void)
|
|
{
|
|
noux_syscall(Noux::Session::SYSCALL_GETPID);
|
|
return sysio()->getpid_out.pid;
|
|
}
|
|
|
|
|
|
extern "C" pid_t getppid(void) { return getpid(); }
|
|
|
|
|
|
extern "C" int chmod(char const *path, mode_t mode)
|
|
{
|
|
if (verbose)
|
|
warning(__func__, ": chmod '", path, "' to ", Hex(mode), " not implemented");
|
|
return 0;
|
|
}
|
|
|
|
|
|
extern "C" pid_t wait4(pid_t pid, int *status, int options,
|
|
struct rusage *rusage)
|
|
{
|
|
sysio()->wait4_in.pid = pid;
|
|
sysio()->wait4_in.nohang = !!(options & WNOHANG);
|
|
if (!noux_syscall(Noux::Session::SYSCALL_WAIT4)) {
|
|
switch (sysio()->error.wait4) {
|
|
case Noux::Sysio::WAIT4_ERR_INTERRUPT: errno = EINTR; break;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* The libc expects status information in bits 0..6 and the exit value
|
|
* in bits 8..15 (according to 'wait.h').
|
|
*/
|
|
if (status)
|
|
*status = ((sysio()->wait4_out.status >> 8) & 0177) |
|
|
((sysio()->wait4_out.status & 0xff) << 8);
|
|
|
|
return sysio()->wait4_out.pid;
|
|
}
|
|
|
|
|
|
extern "C" __attribute__((alias("wait4")))
|
|
pid_t _wait4(pid_t, int *, int, struct rusage *);
|
|
|
|
|
|
extern "C" __attribute__((alias("wait4")))
|
|
pid_t __sys_wait4(pid_t, int *, int, struct rusage *);
|
|
|
|
|
|
extern "C" pid_t waitpid(pid_t pid, int *istat, int options)
|
|
{
|
|
return wait4(pid, istat, options, NULL);
|
|
}
|
|
|
|
|
|
extern "C" int execve(char const *filename, char *const argv[],
|
|
char *const envp[])
|
|
{
|
|
if (verbose) {
|
|
log(__func__, ": filename=", filename);
|
|
|
|
for (int i = 0; argv[i]; i++)
|
|
log(__func__, "argv[", i, "]='", Genode::Cstring(argv[i]), "'");
|
|
|
|
for (int i = 0; envp[i]; i++)
|
|
log(__func__, "envp[", i, "]='", Genode::Cstring(envp[i]), "'");
|
|
}
|
|
|
|
Libc::Absolute_path resolved_path;
|
|
try {
|
|
Libc::resolve_symlinks(filename, resolved_path);
|
|
} catch (Libc::Symlink_resolve_error) {
|
|
return -1;
|
|
}
|
|
|
|
Genode::strncpy(sysio()->execve_in.filename, resolved_path.string(),
|
|
sizeof(sysio()->execve_in.filename));
|
|
if (!serialize_string_array(argv, sysio()->execve_in.args,
|
|
sizeof(sysio()->execve_in.args))) {
|
|
error("execve: argument buffer exceeded");
|
|
errno = E2BIG;
|
|
return -1;
|
|
}
|
|
|
|
/* communicate the current working directory as environment variable */
|
|
|
|
size_t noux_cwd_len = Genode::snprintf(sysio()->execve_in.env,
|
|
sizeof(sysio()->execve_in.env),
|
|
"NOUX_CWD=");
|
|
|
|
if (!getcwd(&(sysio()->execve_in.env[noux_cwd_len]),
|
|
sizeof(sysio()->execve_in.env) - noux_cwd_len)) {
|
|
error("execve: environment buffer exceeded");
|
|
errno = E2BIG;
|
|
return -1;
|
|
}
|
|
|
|
noux_cwd_len = strlen(sysio()->execve_in.env) + 1;
|
|
|
|
if (!serialize_string_array(envp, &(sysio()->execve_in.env[noux_cwd_len]),
|
|
sizeof(sysio()->execve_in.env) - noux_cwd_len)) {
|
|
error("execve: environment buffer exceeded");
|
|
errno = E2BIG;
|
|
return -1;
|
|
}
|
|
|
|
if (!noux_syscall(Noux::Session::SYSCALL_EXECVE)) {
|
|
warning("exec syscall failed for path \"", filename, "\"");
|
|
switch (sysio()->error.execve) {
|
|
case Noux::Sysio::EXECVE_ERR_NO_ENTRY: errno = ENOENT; break;
|
|
case Noux::Sysio::EXECVE_ERR_NO_MEMORY: errno = ENOMEM; break;
|
|
case Noux::Sysio::EXECVE_ERR_NO_EXEC: errno = ENOEXEC; break;
|
|
case Noux::Sysio::EXECVE_ERR_ACCESS: errno = EACCES; break;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* In the success case, we never return from execve, the execution is
|
|
* resumed in the new program.
|
|
*/
|
|
Genode::sleep_forever();
|
|
return 0;
|
|
}
|
|
|
|
|
|
extern "C" int _execve(char const *, char *const[], char *const[]) __attribute__((alias("execve")));
|
|
|
|
|
|
int getrusage(int who, struct rusage *usage)
|
|
{
|
|
if (verbose)
|
|
warning(__func__, " not implemented");
|
|
|
|
errno = ENOSYS;
|
|
return -1;
|
|
}
|
|
|
|
|
|
void endpwent(void)
|
|
{
|
|
if (verbose)
|
|
warning(__func__, " not implemented");
|
|
}
|
|
|
|
|
|
extern "C" void sync(void)
|
|
{
|
|
noux_syscall(Noux::Session::SYSCALL_SYNC);
|
|
}
|
|
|
|
|
|
extern "C" int kill(__pid_t pid, int sig)
|
|
{
|
|
if (verbose_signals)
|
|
log(__func__, ": pid=", pid, ", sig=", sig);
|
|
|
|
sysio()->kill_in.pid = pid;
|
|
sysio()->kill_in.sig = Noux::Sysio::Signal(sig);
|
|
|
|
if (!noux_syscall(Noux::Session::SYSCALL_KILL)) {
|
|
switch (sysio()->error.kill) {
|
|
case Noux::Sysio::KILL_ERR_SRCH: errno = ESRCH; break;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
extern "C" int raise(int sig)
|
|
{
|
|
return kill(getpid(), sig);
|
|
}
|
|
|
|
|
|
extern "C" int nanosleep(const struct timespec *timeout,
|
|
struct timespec *remainder)
|
|
{
|
|
Noux::Sysio::Select_fds &in_fds = sysio()->select_in.fds;
|
|
|
|
in_fds.num_rd = 0;
|
|
in_fds.num_wr = 0;
|
|
in_fds.num_ex = 0;
|
|
|
|
sysio()->select_in.timeout.sec = timeout->tv_sec;
|
|
sysio()->select_in.timeout.usec = timeout->tv_nsec / 1000;
|
|
|
|
/*
|
|
* Perform syscall
|
|
*/
|
|
if (!noux_syscall(Noux::Session::SYSCALL_SELECT)) {
|
|
switch (sysio()->error.select) {
|
|
case Noux::Sysio::SELECT_ERR_INTERRUPT: errno = EINTR; break;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
if (remainder) {
|
|
remainder->tv_sec = 0;
|
|
remainder->tv_nsec = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
extern "C" unsigned int sleep(unsigned int seconds)
|
|
{
|
|
struct timespec dummy = { Genode::min<int>(seconds, __INT_MAX__), 0 };
|
|
|
|
/*
|
|
* Always return 0 because our nanosleep() cannot not be interrupted.
|
|
*/
|
|
nanosleep(&dummy, 0);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/********************
|
|
** Time functions **
|
|
********************/
|
|
|
|
/*
|
|
* The default implementations as provided by the libc rely on a dedicated
|
|
* thread. But on Noux, no thread other than the main thread is allowed. For
|
|
* this reason, we need to override the default implementations here.
|
|
*/
|
|
|
|
extern "C" int clock_gettime(clockid_t clk_id, struct timespec *tp)
|
|
{
|
|
/* we currently only support CLOCK_SECOND */
|
|
switch (clk_id) {
|
|
case CLOCK_SECOND:
|
|
sysio()->clock_gettime_in.clock_id = Noux::Sysio::CLOCK_ID_SECOND;
|
|
break;
|
|
default:
|
|
/* let's save the trip to noux and return directly */
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
if (!noux_syscall(Noux::Session::SYSCALL_CLOCK_GETTIME)) {
|
|
switch (sysio()->error.clock) {
|
|
case Noux::Sysio::CLOCK_ERR_INVALID: errno = EINVAL; break;
|
|
default: errno = 0; break;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
tp->tv_sec = sysio()->clock_gettime_out.sec;
|
|
tp->tv_nsec = sysio()->clock_gettime_out.nsec;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
extern "C" int gettimeofday(struct timeval *tv, struct timezone *tz)
|
|
{
|
|
if (!noux_syscall(Noux::Session::SYSCALL_GETTIMEOFDAY)) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
tv->tv_sec = sysio()->gettimeofday_out.sec;
|
|
tv->tv_usec = sysio()->gettimeofday_out.usec;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
extern "C" int utimes(const char* path, const struct timeval *times)
|
|
{
|
|
char * const dst = sysio()->utimes_in.path;
|
|
size_t const max_len = sizeof(sysio()->utimes_in.path);
|
|
Genode::strncpy(dst, path, max_len);
|
|
|
|
sysio()->utimes_in.sec = times ? times->tv_sec : 0;
|
|
sysio()->utimes_in.usec = times ? times->tv_usec : 0;
|
|
|
|
if (!noux_syscall(Noux::Session::SYSCALL_UTIMES)) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*********************
|
|
** Signal handling **
|
|
*********************/
|
|
|
|
extern "C" int sigprocmask(int how, const sigset_t *set, sigset_t *oldset)
|
|
{
|
|
if (oldset)
|
|
*oldset = signal_mask;
|
|
|
|
if (!set)
|
|
return 0;
|
|
|
|
switch (how) {
|
|
case SIG_BLOCK:
|
|
for (int sig = 1; sig < NSIG; sig++)
|
|
if (sigismember(set, sig)) {
|
|
if (verbose_signals)
|
|
log(__func__, ": signal ", sig, " requested to get blocked");
|
|
sigaddset(&signal_mask, sig);
|
|
}
|
|
break;
|
|
case SIG_UNBLOCK:
|
|
for (int sig = 1; sig < NSIG; sig++)
|
|
if (sigismember(set, sig)) {
|
|
if (verbose_signals)
|
|
log(__func__, ": signal ", sig, " requested to get unblocked");
|
|
sigdelset(&signal_mask, sig);
|
|
}
|
|
break;
|
|
case SIG_SETMASK:
|
|
if (verbose_signals)
|
|
for (int sig = 1; sig < NSIG; sig++)
|
|
if (sigismember(set, sig))
|
|
log(__func__, ": signal ", sig, " requested to get blocked");
|
|
signal_mask = *set;
|
|
break;
|
|
default:
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
extern "C" int _sigprocmask(int how, const sigset_t *set, sigset_t *oldset) __attribute__((alias("sigprocmask")));
|
|
|
|
|
|
extern "C" int _sigaction(int signum, const struct sigaction *act, struct sigaction *oldact)
|
|
{
|
|
if (verbose_signals)
|
|
log("signum=", signum, ", handler=", act ? act->sa_handler : nullptr);
|
|
|
|
if ((signum < 1) || (signum > NSIG)) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
if (oldact)
|
|
*oldact = signal_action[signum];
|
|
|
|
if (act)
|
|
signal_action[signum] = *act;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
extern "C" int sigaction(int, const struct sigaction *, struct sigaction *) __attribute__((alias("_sigaction")));
|
|
extern "C" int __libc_sigaction(int, const struct sigaction *, struct sigaction *) __attribute__((alias("_sigaction")));
|
|
|
|
|
|
/*********************
|
|
** File operations **
|
|
*********************/
|
|
|
|
static int noux_fd(Libc::Plugin_context *context)
|
|
{
|
|
/*
|
|
* We use the 'context' pointer only as container for an int value. It is
|
|
* never used as a pointer. To make 64-bit compilers happy, we need to keep
|
|
* the bit width of the cast intact. The upper 32 bit are discarded when
|
|
* leaving the function.
|
|
*/
|
|
return (long)context;
|
|
}
|
|
|
|
|
|
static Libc::Plugin_context *noux_context(int noux_fd)
|
|
{
|
|
return reinterpret_cast<Libc::Plugin_context*>(noux_fd);
|
|
}
|
|
|
|
|
|
namespace {
|
|
|
|
class Plugin : public Libc::Plugin
|
|
{
|
|
private:
|
|
|
|
/*
|
|
* Override the libc's default VFS plugin
|
|
*/
|
|
enum { PLUGIN_PRIORITY = 1 };
|
|
|
|
public:
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
Plugin() : Libc::Plugin(PLUGIN_PRIORITY) { }
|
|
|
|
void init(Genode::Env &);
|
|
|
|
bool supports_access(const char *, int) { return true; }
|
|
bool supports_open(char const *, int) { return true; }
|
|
bool supports_stat(char const *) { return true; }
|
|
bool supports_symlink(char const *, char const*) { return true; }
|
|
bool supports_pipe() { return true; }
|
|
bool supports_unlink(char const *) { return true; }
|
|
bool supports_readlink(const char *, char *, ::size_t) { return true; }
|
|
bool supports_rename(const char *, const char *) { return true; }
|
|
bool supports_rmdir(char const *) { return true; }
|
|
bool supports_mkdir(const char *, mode_t) { return true; }
|
|
bool supports_socket(int, int, int) { return true; }
|
|
bool supports_mmap() { return true; }
|
|
|
|
int access(char const *, int);
|
|
Libc::File_descriptor *open(char const *, int);
|
|
ssize_t write(Libc::File_descriptor *, const void *, ::size_t);
|
|
int close(Libc::File_descriptor *);
|
|
Libc::File_descriptor *dup(Libc::File_descriptor*);
|
|
int dup2(Libc::File_descriptor *, Libc::File_descriptor *);
|
|
int fstat(Libc::File_descriptor *, struct stat *);
|
|
int fsync(Libc::File_descriptor *);
|
|
int fstatfs(Libc::File_descriptor *, struct statfs *);
|
|
int ftruncate(Libc::File_descriptor *, ::off_t);
|
|
int fcntl(Libc::File_descriptor *, int, long);
|
|
ssize_t getdirentries(Libc::File_descriptor *, char *, ::size_t, ::off_t *);
|
|
::off_t lseek(Libc::File_descriptor *, ::off_t offset, int whence);
|
|
ssize_t read(Libc::File_descriptor *, void *, ::size_t);
|
|
ssize_t readlink(const char *path, char *buf, ::size_t bufsiz);
|
|
int rename(const char *oldpath, const char *newpath);
|
|
int rmdir(char const *path);
|
|
int stat(char const *, struct stat *);
|
|
int symlink(const char *, const char *);
|
|
int ioctl(Libc::File_descriptor *, int request, char *argp);
|
|
int pipe(Libc::File_descriptor *pipefd[2]);
|
|
int unlink(char const *path);
|
|
int mkdir(const char *path, mode_t mode);
|
|
void *mmap(void *addr, ::size_t length, int prot, int flags,
|
|
Libc::File_descriptor *, ::off_t offset);
|
|
int munmap(void *addr, ::size_t length);
|
|
|
|
/* Network related functions */
|
|
Libc::File_descriptor *socket(int, int, int);
|
|
Libc::File_descriptor *accept(Libc::File_descriptor *,
|
|
struct sockaddr *, socklen_t *);
|
|
int bind(Libc::File_descriptor *, const struct sockaddr *,
|
|
socklen_t);
|
|
int connect(Libc::File_descriptor *, const struct sockaddr *addr,
|
|
socklen_t addrlen);
|
|
int getpeername(Libc::File_descriptor *, struct sockaddr *,
|
|
socklen_t *);
|
|
int listen(Libc::File_descriptor *, int);
|
|
ssize_t send(Libc::File_descriptor *, const void *, ::size_t,
|
|
int flags);
|
|
ssize_t sendto(Libc::File_descriptor *, const void *, size_t, int,
|
|
const struct sockaddr *, socklen_t);
|
|
ssize_t recv(Libc::File_descriptor *, void *, ::size_t, int);
|
|
ssize_t recvfrom(Libc::File_descriptor *, void *, ::size_t, int,
|
|
struct sockaddr *, socklen_t*);
|
|
int getsockopt(Libc::File_descriptor *, int, int, void *,
|
|
socklen_t *);
|
|
int setsockopt(Libc::File_descriptor *, int , int , const void *,
|
|
socklen_t);
|
|
int shutdown(Libc::File_descriptor *, int how);
|
|
};
|
|
|
|
|
|
int Plugin::access(char const *pathname, int mode)
|
|
{
|
|
if (verbose)
|
|
log(__func__, ": access '", pathname, "' (mode=", Hex(mode), ") "
|
|
"called, not implemented");
|
|
|
|
struct stat stat;
|
|
if (::stat(pathname, &stat) == 0)
|
|
return 0;
|
|
|
|
errno = ENOENT;
|
|
return -1;
|
|
}
|
|
|
|
|
|
int Plugin::stat(char const *path, struct stat *buf)
|
|
{
|
|
if (verbose)
|
|
log(__func__, ": path=", path);
|
|
|
|
if ((path == NULL) or (buf == NULL)) {
|
|
errno = EFAULT;
|
|
return -1;
|
|
}
|
|
|
|
Genode::strncpy(sysio()->stat_in.path, path, sizeof(sysio()->stat_in.path));
|
|
|
|
if (!noux_syscall(Noux::Session::SYSCALL_STAT)) {
|
|
if (verbose)
|
|
warning("stat syscall failed for path \"", path, "\"");
|
|
switch (sysio()->error.stat) {
|
|
case Vfs::Directory_service::STAT_ERR_NO_ENTRY: errno = ENOENT; return -1;
|
|
case Vfs::Directory_service::STAT_ERR_NO_PERM: errno = EACCES; return -1;
|
|
case Vfs::Directory_service::STAT_OK: break; /* never reached */
|
|
}
|
|
}
|
|
|
|
_sysio_to_stat_struct(sysio(), buf);
|
|
return 0;
|
|
}
|
|
|
|
|
|
Libc::File_descriptor *Plugin::open(char const *pathname, int flags)
|
|
{
|
|
if (Genode::strlen(pathname) + 1 > sizeof(sysio()->open_in.path)) {
|
|
log(__func__, ": ENAMETOOLONG");
|
|
errno = ENAMETOOLONG;
|
|
return 0;
|
|
}
|
|
|
|
bool opened = false;
|
|
while (!opened) {
|
|
Genode::strncpy(sysio()->open_in.path, pathname, sizeof(sysio()->open_in.path));
|
|
sysio()->open_in.mode = flags;
|
|
if (noux_syscall(Noux::Session::SYSCALL_OPEN))
|
|
opened = true;
|
|
else
|
|
switch (sysio()->error.open) {
|
|
case Vfs::Directory_service::OPEN_ERR_UNACCESSIBLE:
|
|
if (!(flags & O_CREAT)) {
|
|
errno = ENOENT;
|
|
return 0;
|
|
}
|
|
/* O_CREAT is set, so try to create the file */
|
|
Genode::strncpy(sysio()->open_in.path, pathname, sizeof(sysio()->open_in.path));
|
|
sysio()->open_in.mode = flags | O_EXCL;
|
|
if (noux_syscall(Noux::Session::SYSCALL_OPEN))
|
|
opened = true;
|
|
else
|
|
switch (sysio()->error.open) {
|
|
case Vfs::Directory_service::OPEN_ERR_EXISTS:
|
|
/* file has been created by someone else in the meantime */
|
|
break;
|
|
case Vfs::Directory_service::OPEN_ERR_NO_PERM:
|
|
errno = EPERM; return 0;
|
|
default:
|
|
errno = ENOENT; return 0;
|
|
}
|
|
break;
|
|
case Vfs::Directory_service::OPEN_ERR_NO_PERM: errno = EPERM; return 0;
|
|
case Vfs::Directory_service::OPEN_ERR_EXISTS: errno = EEXIST; return 0;
|
|
default: errno = ENOENT; return 0;
|
|
}
|
|
}
|
|
|
|
Libc::Plugin_context *context = noux_context(sysio()->open_out.fd);
|
|
Libc::File_descriptor *fd =
|
|
Libc::file_descriptor_allocator()->alloc(this, context, sysio()->open_out.fd);
|
|
if ((flags & O_TRUNC) && (ftruncate(fd, 0) == -1))
|
|
return 0;
|
|
return fd;
|
|
}
|
|
|
|
|
|
int Plugin::symlink(const char *oldpath, const char *newpath)
|
|
{
|
|
if (verbose)
|
|
log(__func__, ": ", newpath, " -> ", oldpath);
|
|
|
|
if ((Genode::strlen(oldpath) + 1 > sizeof(sysio()->symlink_in.oldpath)) ||
|
|
(Genode::strlen(newpath) + 1 > sizeof(sysio()->symlink_in.newpath))) {
|
|
log(__func__, ": ENAMETOOLONG");
|
|
errno = ENAMETOOLONG;
|
|
return 0;
|
|
}
|
|
|
|
Genode::strncpy(sysio()->symlink_in.oldpath, oldpath, sizeof(sysio()->symlink_in.oldpath));
|
|
Genode::strncpy(sysio()->symlink_in.newpath, newpath, sizeof(sysio()->symlink_in.newpath));
|
|
if (!noux_syscall(Noux::Session::SYSCALL_SYMLINK)) {
|
|
warning("symlink syscall failed for path \"", newpath, "\"");
|
|
switch (sysio()->error.symlink) {
|
|
case Noux::Sysio::SYMLINK_ERR_NO_ENTRY: errno = ENOENT; return -1;
|
|
case Noux::Sysio::SYMLINK_ERR_EXISTS: errno = EEXIST; return -1;
|
|
case Noux::Sysio::SYMLINK_ERR_NO_SPACE: errno = ENOSPC; return -1;
|
|
case Noux::Sysio::SYMLINK_ERR_NO_PERM: errno = EPERM; return -1;
|
|
case Noux::Sysio::SYMLINK_ERR_NAME_TOO_LONG: errno = ENAMETOOLONG; return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Plugin::fstatfs(Libc::File_descriptor *, struct statfs *buf)
|
|
{
|
|
buf->f_flags = MNT_UNION;
|
|
return 0;
|
|
}
|
|
|
|
|
|
ssize_t Plugin::write(Libc::File_descriptor *fd, const void *buf,
|
|
::size_t count)
|
|
{
|
|
if (!buf) { errno = EFAULT; return -1; }
|
|
|
|
/* remember original len for the return value */
|
|
int const orig_count = count;
|
|
|
|
char *src = (char *)buf;
|
|
while (count > 0) {
|
|
|
|
Genode::size_t curr_count = Genode::min((::size_t)Noux::Sysio::CHUNK_SIZE, count);
|
|
|
|
sysio()->write_in.fd = noux_fd(fd->context);
|
|
sysio()->write_in.count = curr_count;
|
|
Genode::memcpy(sysio()->write_in.chunk, src, curr_count);
|
|
|
|
if (!noux_syscall(Noux::Session::SYSCALL_WRITE)) {
|
|
switch (sysio()->error.write) {
|
|
case Vfs::File_io_service::WRITE_ERR_AGAIN: errno = EAGAIN; break;
|
|
case Vfs::File_io_service::WRITE_ERR_WOULD_BLOCK: errno = EWOULDBLOCK; break;
|
|
case Vfs::File_io_service::WRITE_ERR_INVALID: errno = EINVAL; break;
|
|
case Vfs::File_io_service::WRITE_ERR_IO: errno = EIO; break;
|
|
case Vfs::File_io_service::WRITE_ERR_INTERRUPT: errno = EINTR; break;
|
|
default:
|
|
if (sysio()->error.general == Vfs::Directory_service::ERR_FD_INVALID)
|
|
errno = EBADF;
|
|
else
|
|
errno = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
count -= sysio()->write_out.count;
|
|
src += sysio()->write_out.count;
|
|
}
|
|
return orig_count;
|
|
}
|
|
|
|
|
|
ssize_t Plugin::read(Libc::File_descriptor *fd, void *buf, ::size_t count)
|
|
{
|
|
if (!buf) { errno = EFAULT; return -1; }
|
|
|
|
Genode::size_t sum_read_count = 0;
|
|
|
|
while (count > 0) {
|
|
|
|
Genode::size_t curr_count =
|
|
Genode::min(count, sizeof(sysio()->read_out.chunk));
|
|
|
|
sysio()->read_in.fd = noux_fd(fd->context);
|
|
sysio()->read_in.count = curr_count;
|
|
|
|
if (!noux_syscall(Noux::Session::SYSCALL_READ)) {
|
|
|
|
switch (sysio()->error.read) {
|
|
case Vfs::File_io_service::READ_ERR_AGAIN: errno = EAGAIN; break;
|
|
case Vfs::File_io_service::READ_ERR_WOULD_BLOCK: errno = EWOULDBLOCK; break;
|
|
case Vfs::File_io_service::READ_ERR_INVALID: errno = EINVAL; break;
|
|
case Vfs::File_io_service::READ_ERR_IO: errno = EIO; break;
|
|
case Vfs::File_io_service::READ_ERR_INTERRUPT: errno = EINTR; break;
|
|
default:
|
|
if (sysio()->error.general == Vfs::Directory_service::ERR_FD_INVALID)
|
|
errno = EBADF;
|
|
else
|
|
errno = 0;
|
|
break;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
Genode::memcpy((char*)buf + sum_read_count,
|
|
sysio()->read_out.chunk,
|
|
sysio()->read_out.count);
|
|
|
|
sum_read_count += sysio()->read_out.count;
|
|
|
|
if (sysio()->read_out.count < curr_count)
|
|
break; /* end of file */
|
|
|
|
if (sysio()->read_out.count <= count)
|
|
count -= sysio()->read_out.count;
|
|
else
|
|
break; /* should not happen */
|
|
}
|
|
|
|
return sum_read_count;
|
|
}
|
|
|
|
|
|
int Plugin::close(Libc::File_descriptor *fd)
|
|
{
|
|
sysio()->close_in.fd = noux_fd(fd->context);
|
|
if (!noux_syscall(Noux::Session::SYSCALL_CLOSE)) {
|
|
error("close error");
|
|
/* XXX set errno */
|
|
return -1;
|
|
}
|
|
Libc::file_descriptor_allocator()->free(fd);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Plugin::ioctl(Libc::File_descriptor *fd, int request, char *argp)
|
|
{
|
|
/*
|
|
* Marshal ioctl arguments
|
|
*/
|
|
sysio()->ioctl_in.fd = noux_fd(fd->context);
|
|
sysio()->ioctl_in.request = Vfs::File_io_service::IOCTL_OP_UNDEFINED;
|
|
|
|
switch (request) {
|
|
|
|
case TIOCGWINSZ:
|
|
sysio()->ioctl_in.request = Vfs::File_io_service::IOCTL_OP_TIOCGWINSZ;
|
|
break;
|
|
|
|
case TIOCGETA:
|
|
{
|
|
if (verbose)
|
|
log(__func__, ": TIOCGETA - argp=", (void *)argp);
|
|
::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:
|
|
{
|
|
sysio()->ioctl_in.request = Vfs::File_io_service::IOCTL_OP_TIOCSETAF;
|
|
|
|
::termios *termios = (::termios *)argp;
|
|
|
|
/**
|
|
* For now only enabling/disabling of ECHO is supported
|
|
*/
|
|
if (termios->c_lflag & (ECHO | ECHONL)) {
|
|
sysio()->ioctl_in.argp = (Vfs::File_io_service::IOCTL_VAL_ECHO |
|
|
Vfs::File_io_service::IOCTL_VAL_ECHONL);
|
|
}
|
|
else {
|
|
sysio()->ioctl_in.argp = Vfs::File_io_service::IOCTL_VAL_NULL;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case TIOCSETAW:
|
|
{
|
|
sysio()->ioctl_in.request = Vfs::File_io_service::IOCTL_OP_TIOCSETAW;
|
|
sysio()->ioctl_in.argp = argp ? *(int*)argp : 0;
|
|
|
|
break;
|
|
}
|
|
|
|
case FIONBIO:
|
|
{
|
|
if (verbose)
|
|
log(__func__, ": FIONBIO - *argp=", *argp);
|
|
|
|
sysio()->ioctl_in.request = Vfs::File_io_service::IOCTL_OP_FIONBIO;
|
|
sysio()->ioctl_in.argp = argp ? *(int*)argp : 0;
|
|
|
|
break;
|
|
}
|
|
|
|
case DIOCGMEDIASIZE:
|
|
{
|
|
sysio()->ioctl_in.request = Vfs::File_io_service::IOCTL_OP_DIOCGMEDIASIZE;
|
|
sysio()->ioctl_in.argp = 0;
|
|
|
|
break;
|
|
}
|
|
|
|
case TIOCSETA:
|
|
{
|
|
/* not implemented but used by e.g. vim */
|
|
break;
|
|
}
|
|
|
|
|
|
default:
|
|
|
|
warning("unsupported ioctl (request=", Hex(request), ")");
|
|
break;
|
|
}
|
|
|
|
if (sysio()->ioctl_in.request == Vfs::File_io_service::IOCTL_OP_UNDEFINED) {
|
|
errno = ENOTTY;
|
|
return -1;
|
|
}
|
|
|
|
/* perform syscall */
|
|
if (!noux_syscall(Noux::Session::SYSCALL_IOCTL)) {
|
|
switch (sysio()->error.ioctl) {
|
|
case Vfs::File_io_service::IOCTL_ERR_INVALID: errno = EINVAL; break;
|
|
case Vfs::File_io_service::IOCTL_ERR_NOTTY: errno = ENOTTY; break;
|
|
default: errno = 0; break;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Unmarshal ioctl results
|
|
*/
|
|
switch (request) {
|
|
|
|
case TIOCGWINSZ:
|
|
{
|
|
::winsize *winsize = (::winsize *)argp;
|
|
winsize->ws_row = sysio()->ioctl_out.tiocgwinsz.rows;
|
|
winsize->ws_col = sysio()->ioctl_out.tiocgwinsz.columns;
|
|
return 0;
|
|
}
|
|
case TIOCSETAF:
|
|
case TIOCSETAW:
|
|
return 0;
|
|
|
|
case FIONBIO:
|
|
return 0;
|
|
case DIOCGMEDIASIZE:
|
|
{
|
|
int64_t *disk_size = (int64_t*)argp;
|
|
*disk_size = sysio()->ioctl_out.diocgmediasize.size;
|
|
return 0;
|
|
}
|
|
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
|
|
int Plugin::pipe(Libc::File_descriptor *pipefd[2])
|
|
{
|
|
/* perform syscall */
|
|
if (!noux_syscall(Noux::Session::SYSCALL_PIPE)) {
|
|
error("pipe error");
|
|
/* XXX set errno */
|
|
return -1;
|
|
}
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
Libc::Plugin_context *context = noux_context(sysio()->pipe_out.fd[i]);
|
|
pipefd[i] = Libc::file_descriptor_allocator()->alloc(this, context, sysio()->pipe_out.fd[i]);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
Libc::File_descriptor *Plugin::dup(Libc::File_descriptor* fd)
|
|
{
|
|
sysio()->dup2_in.fd = noux_fd(fd->context);
|
|
sysio()->dup2_in.to_fd = -1;
|
|
|
|
if (!noux_syscall(Noux::Session::SYSCALL_DUP2)) {
|
|
error("dup error");
|
|
/* XXX set errno */
|
|
return 0;
|
|
}
|
|
|
|
Libc::Plugin_context *context = noux_context(sysio()->dup2_out.fd);
|
|
return Libc::file_descriptor_allocator()->alloc(this, context,
|
|
sysio()->dup2_out.fd);
|
|
}
|
|
|
|
|
|
int Plugin::dup2(Libc::File_descriptor *fd, Libc::File_descriptor *new_fd)
|
|
{
|
|
/*
|
|
* We use a one-to-one mapping of libc fds and Noux fds.
|
|
*/
|
|
new_fd->context = noux_context(new_fd->libc_fd);
|
|
|
|
sysio()->dup2_in.fd = noux_fd(fd->context);
|
|
sysio()->dup2_in.to_fd = noux_fd(new_fd->context);
|
|
|
|
/* perform syscall */
|
|
if (!noux_syscall(Noux::Session::SYSCALL_DUP2)) {
|
|
error("dup2 error");
|
|
/* XXX set errno */
|
|
return -1;
|
|
}
|
|
|
|
return noux_fd(new_fd->context);
|
|
}
|
|
|
|
|
|
int Plugin::fstat(Libc::File_descriptor *fd, struct stat *buf)
|
|
{
|
|
sysio()->fstat_in.fd = noux_fd(fd->context);
|
|
if (!noux_syscall(Noux::Session::SYSCALL_FSTAT)) {
|
|
error("fstat error");
|
|
/* XXX set errno */
|
|
return -1;
|
|
}
|
|
|
|
_sysio_to_stat_struct(sysio(), buf);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Plugin::fsync(Libc::File_descriptor *fd)
|
|
{
|
|
if (verbose)
|
|
warning(__func__, ": not implemented");
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Plugin::ftruncate(Libc::File_descriptor *fd, ::off_t length)
|
|
{
|
|
sysio()->ftruncate_in.fd = noux_fd(fd->context);
|
|
sysio()->ftruncate_in.length = length;
|
|
if (!noux_syscall(Noux::Session::SYSCALL_FTRUNCATE)) {
|
|
switch (sysio()->error.ftruncate) {
|
|
case Vfs::File_io_service::FTRUNCATE_OK: /* never reached */
|
|
case Vfs::File_io_service::FTRUNCATE_ERR_NO_PERM: errno = EPERM; break;
|
|
case Vfs::File_io_service::FTRUNCATE_ERR_INTERRUPT: errno = EINTR; break;
|
|
case Vfs::File_io_service::FTRUNCATE_ERR_NO_SPACE: errno = ENOSPC; break;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Plugin::fcntl(Libc::File_descriptor *fd, int cmd, long arg)
|
|
{
|
|
/* copy arguments to sysio */
|
|
sysio()->fcntl_in.fd = noux_fd(fd->context);
|
|
switch (cmd) {
|
|
|
|
case F_DUPFD_CLOEXEC:
|
|
case F_DUPFD:
|
|
{
|
|
/*
|
|
* Allocate free file descriptor locally. Noux FDs are expected
|
|
* to correspond one-to-one to libc FDs.
|
|
*/
|
|
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) {
|
|
error("Plugin::fcntl: dup2 unexpectedly failed");
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
return new_fd->libc_fd;
|
|
}
|
|
|
|
case F_GETFD:
|
|
sysio()->fcntl_in.cmd = Noux::Sysio::FCNTL_CMD_GET_FD_FLAGS;
|
|
break;
|
|
|
|
case F_SETFD:
|
|
sysio()->fcntl_in.cmd = Noux::Sysio::FCNTL_CMD_SET_FD_FLAGS;
|
|
sysio()->fcntl_in.long_arg = arg;
|
|
break;
|
|
|
|
case F_GETFL:
|
|
if (verbose)
|
|
log("fcntl: F_GETFL for libc_fd=", fd->libc_fd);
|
|
sysio()->fcntl_in.cmd = Noux::Sysio::FCNTL_CMD_GET_FILE_STATUS_FLAGS;
|
|
break;
|
|
|
|
case F_SETFL:
|
|
if (verbose)
|
|
log("fcntl: F_SETFL for libc_fd=", fd->libc_fd);
|
|
sysio()->fcntl_in.cmd = Noux::Sysio::FCNTL_CMD_SET_FILE_STATUS_FLAGS;
|
|
sysio()->fcntl_in.long_arg = arg;
|
|
break;
|
|
|
|
default:
|
|
error("fcntl: unsupported command ", cmd);
|
|
errno = EINVAL;
|
|
return -1;
|
|
};
|
|
|
|
/* invoke system call */
|
|
if (!noux_syscall(Noux::Session::SYSCALL_FCNTL)) {
|
|
warning("fcntl failed (libc_fd=", fd->libc_fd, ", cmd=", Hex(cmd), ")");
|
|
switch (sysio()->error.fcntl) {
|
|
case Noux::Sysio::FCNTL_ERR_CMD_INVALID: errno = EINVAL; break;
|
|
default:
|
|
switch (sysio()->error.general) {
|
|
case Vfs::Directory_service::ERR_FD_INVALID: errno = EINVAL; break;
|
|
case Vfs::Directory_service::NUM_GENERAL_ERRORS: break;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* read result from sysio */
|
|
return sysio()->fcntl_out.result;
|
|
}
|
|
|
|
|
|
ssize_t Plugin::getdirentries(Libc::File_descriptor *fd, char *buf,
|
|
::size_t nbytes, ::off_t *basep)
|
|
{
|
|
if (nbytes < sizeof(struct dirent)) {
|
|
error("buf too small");
|
|
return -1;
|
|
}
|
|
|
|
sysio()->dirent_in.fd = noux_fd(fd->context);
|
|
|
|
if (!noux_syscall(Noux::Session::SYSCALL_DIRENT)) {
|
|
switch (sysio()->error.general) {
|
|
|
|
case Vfs::Directory_service::ERR_FD_INVALID:
|
|
errno = EBADF;
|
|
error("dirent: ERR_FD_INVALID");
|
|
return -1;
|
|
|
|
case Vfs::Directory_service::NUM_GENERAL_ERRORS: return -1;
|
|
}
|
|
}
|
|
|
|
using Dirent_type = Vfs::Directory_service::Dirent_type;
|
|
|
|
if (sysio()->dirent_out.entry.type == Dirent_type::END)
|
|
return 0;
|
|
|
|
auto d_type = [] (Dirent_type const &type)
|
|
{
|
|
switch (sysio()->dirent_out.entry.type) {
|
|
case Dirent_type::DIRECTORY: return DT_DIR;
|
|
case Dirent_type::CONTINUOUS_FILE: return DT_REG;
|
|
case Dirent_type::TRANSACTIONAL_FILE: return DT_REG;
|
|
case Dirent_type::SYMLINK: return DT_LNK;
|
|
case Dirent_type::END: return 0;
|
|
}
|
|
return 0;
|
|
};
|
|
|
|
struct dirent &dirent = *(struct dirent *)buf;
|
|
|
|
dirent = { };
|
|
|
|
dirent.d_type = d_type(sysio()->dirent_out.entry.type);
|
|
dirent.d_fileno = sysio()->dirent_out.entry.fileno;
|
|
dirent.d_reclen = sizeof(struct dirent);
|
|
|
|
Genode::strncpy(dirent.d_name, sysio()->dirent_out.entry.name,
|
|
sizeof(dirent.d_name));
|
|
|
|
dirent.d_namlen = Genode::strlen(dirent.d_name);
|
|
|
|
*basep += sizeof(struct dirent);
|
|
return sizeof(struct dirent);
|
|
}
|
|
|
|
|
|
::off_t Plugin::lseek(Libc::File_descriptor *fd,
|
|
::off_t offset, int whence)
|
|
{
|
|
sysio()->lseek_in.fd = noux_fd(fd->context);
|
|
sysio()->lseek_in.offset = offset;
|
|
|
|
switch (whence) {
|
|
default:
|
|
case SEEK_SET: sysio()->lseek_in.whence = Noux::Sysio::LSEEK_SET; break;
|
|
case SEEK_CUR: sysio()->lseek_in.whence = Noux::Sysio::LSEEK_CUR; break;
|
|
case SEEK_END: sysio()->lseek_in.whence = Noux::Sysio::LSEEK_END; break;
|
|
}
|
|
|
|
if (!noux_syscall(Noux::Session::SYSCALL_LSEEK)) {
|
|
switch (sysio()->error.general) {
|
|
|
|
case Vfs::Directory_service::ERR_FD_INVALID:
|
|
errno = EBADF;
|
|
error("lseek: ERR_FD_INVALID");
|
|
return -1;
|
|
|
|
case Vfs::Directory_service::NUM_GENERAL_ERRORS: return -1;
|
|
}
|
|
}
|
|
|
|
return sysio()->lseek_out.offset;
|
|
}
|
|
|
|
|
|
int Plugin::unlink(char const *path)
|
|
{
|
|
Genode::strncpy(sysio()->unlink_in.path, path, sizeof(sysio()->unlink_in.path));
|
|
|
|
if (!noux_syscall(Noux::Session::SYSCALL_UNLINK)) {
|
|
warning("unlink syscall failed for path \"", path, "\"");
|
|
typedef Vfs::Directory_service::Unlink_result Result;
|
|
switch (sysio()->error.unlink) {
|
|
case Result::UNLINK_ERR_NO_ENTRY: errno = ENOENT; break;
|
|
case Result::UNLINK_ERR_NOT_EMPTY: errno = ENOTEMPTY; break;
|
|
case Result::UNLINK_ERR_NO_PERM: errno = EPERM; break;
|
|
case Result::UNLINK_OK: break; /* only here to complete the enumeration */
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Plugin::rmdir(char const *path)
|
|
{
|
|
return Plugin::unlink(path);
|
|
}
|
|
|
|
|
|
ssize_t Plugin::readlink(const char *path, char *buf, ::size_t bufsiz)
|
|
{
|
|
if (verbose)
|
|
log(__func__, ": path=", path, ", bufsiz=", bufsiz);
|
|
|
|
Genode::strncpy(sysio()->readlink_in.path, path, sizeof(sysio()->readlink_in.path));
|
|
sysio()->readlink_in.bufsiz = bufsiz;
|
|
|
|
if (!noux_syscall(Noux::Session::SYSCALL_READLINK)) {
|
|
warning("readlink syscall failed for path \"", path, "\"");
|
|
switch (sysio()->error.readlink) {
|
|
case Noux::Sysio::READLINK_ERR_NO_ENTRY: errno = ENOENT; return -1;
|
|
case Noux::Sysio::READLINK_ERR_NO_PERM: errno = EPERM; return -1;
|
|
}
|
|
}
|
|
|
|
ssize_t size = Genode::min((size_t)sysio()->readlink_out.count, bufsiz);
|
|
|
|
Genode::memcpy(buf, sysio()->readlink_out.chunk, size);
|
|
|
|
if (verbose)
|
|
log(__func__, ": result=", Genode::Cstring(buf));
|
|
|
|
return size;
|
|
}
|
|
|
|
|
|
int Plugin::rename(char const *from_path, char const *to_path)
|
|
{
|
|
Genode::strncpy(sysio()->rename_in.from_path, from_path, sizeof(sysio()->rename_in.from_path));
|
|
Genode::strncpy(sysio()->rename_in.to_path, to_path, sizeof(sysio()->rename_in.to_path));
|
|
|
|
if (!noux_syscall(Noux::Session::SYSCALL_RENAME)) {
|
|
warning("rename syscall failed for \"", from_path, "\" -> \"", to_path, "\"");
|
|
switch (sysio()->error.rename) {
|
|
case Vfs::Directory_service::RENAME_ERR_NO_ENTRY: errno = ENOENT; break;
|
|
case Vfs::Directory_service::RENAME_ERR_CROSS_FS: errno = EXDEV; break;
|
|
case Vfs::Directory_service::RENAME_ERR_NO_PERM: errno = EPERM; break;
|
|
default: errno = EPERM; break;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Plugin::mkdir(const char *path, mode_t mode)
|
|
{
|
|
Genode::strncpy(sysio()->mkdir_in.path, path, sizeof(sysio()->mkdir_in.path));
|
|
|
|
if (!noux_syscall(Noux::Session::SYSCALL_MKDIR)) {
|
|
warning("mkdir syscall failed for \"", path, "\" mode=", Hex(mode));
|
|
switch (sysio()->error.mkdir) {
|
|
case Noux::Sysio::MKDIR_ERR_EXISTS: errno = EEXIST; break;
|
|
case Noux::Sysio::MKDIR_ERR_NO_ENTRY: errno = ENOENT; break;
|
|
case Noux::Sysio::MKDIR_ERR_NO_SPACE: errno = ENOSPC; break;
|
|
case Noux::Sysio::MKDIR_ERR_NAME_TOO_LONG: errno = ENAMETOOLONG; break;
|
|
case Noux::Sysio::MKDIR_ERR_NO_PERM: errno = EPERM; break;
|
|
default: errno = EPERM; break;
|
|
}
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
void *Plugin::mmap(void *addr_in, ::size_t length, int prot, int flags,
|
|
Libc::File_descriptor *fd, ::off_t offset)
|
|
{
|
|
if (prot != PROT_READ) {
|
|
error("mmap for prot=", Hex(prot), " not supported");
|
|
errno = EACCES;
|
|
return (void *)-1;
|
|
}
|
|
|
|
if (addr_in != 0) {
|
|
error("mmap for predefined address not supported");
|
|
errno = EINVAL;
|
|
return (void *)-1;
|
|
}
|
|
|
|
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) {
|
|
error("mmap could not obtain file content");
|
|
::munmap(addr, length);
|
|
errno = EACCES;
|
|
return (void *)-1;
|
|
}
|
|
|
|
return addr;
|
|
}
|
|
|
|
|
|
int Plugin::munmap(void *addr, ::size_t)
|
|
{
|
|
Libc::mem_alloc()->free(addr);
|
|
return 0;
|
|
}
|
|
|
|
|
|
Libc::File_descriptor *Plugin::socket(int domain, int type, int protocol)
|
|
{
|
|
sysio()->socket_in.domain = domain;
|
|
sysio()->socket_in.type = type;
|
|
sysio()->socket_in.protocol = protocol;
|
|
|
|
if (!noux_syscall(Noux::Session::SYSCALL_SOCKET))
|
|
return 0;
|
|
|
|
Libc::Plugin_context *context = noux_context(sysio()->socket_out.fd);
|
|
return Libc::file_descriptor_allocator()->alloc(this, context,
|
|
sysio()->socket_out.fd);
|
|
}
|
|
|
|
|
|
int Plugin::getsockopt(Libc::File_descriptor *fd, int level, int optname,
|
|
void *optval, socklen_t *optlen)
|
|
{
|
|
sysio()->getsockopt_in.fd = noux_fd(fd->context);
|
|
sysio()->getsockopt_in.level = level;
|
|
sysio()->getsockopt_in.optname = optname;
|
|
|
|
/* wipe-old state */
|
|
sysio()->getsockopt_in.optlen = *optlen;
|
|
Genode::memset(sysio()->getsockopt_in.optval, 0,
|
|
sizeof (sysio()->getsockopt_in.optval));
|
|
|
|
if (!noux_syscall(Noux::Session::SYSCALL_GETSOCKOPT))
|
|
return -1;
|
|
|
|
Genode::memcpy(optval, sysio()->setsockopt_in.optval,
|
|
sysio()->getsockopt_in.optlen);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Plugin::setsockopt(Libc::File_descriptor *fd, int level, int optname,
|
|
const void *optval, socklen_t optlen)
|
|
{
|
|
if (optlen > sizeof(sysio()->setsockopt_in.optval)) {
|
|
/* XXX */
|
|
return -1;
|
|
}
|
|
|
|
sysio()->setsockopt_in.fd = noux_fd(fd->context);
|
|
sysio()->setsockopt_in.level = level;
|
|
sysio()->setsockopt_in.optname = optname;
|
|
sysio()->setsockopt_in.optlen = optlen;
|
|
|
|
Genode::memcpy(sysio()->setsockopt_in.optval, optval, optlen);
|
|
|
|
if (!noux_syscall(Noux::Session::SYSCALL_SETSOCKOPT)) {
|
|
/* XXX */
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
Libc::File_descriptor *Plugin::accept(Libc::File_descriptor *fd, struct sockaddr *addr,
|
|
socklen_t *addrlen)
|
|
{
|
|
sysio()->accept_in.fd = noux_fd(fd->context);
|
|
|
|
if (addr != NULL) {
|
|
Genode::memcpy(&sysio()->accept_in.addr, addr, sizeof (struct sockaddr));
|
|
sysio()->accept_in.addrlen = *addrlen;
|
|
}
|
|
else {
|
|
Genode::memset(&sysio()->accept_in.addr, 0, sizeof (struct sockaddr));
|
|
sysio()->accept_in.addrlen = 0;
|
|
}
|
|
|
|
if (!noux_syscall(Noux::Session::SYSCALL_ACCEPT)) {
|
|
switch (sysio()->error.accept) {
|
|
case Noux::Sysio::ACCEPT_ERR_AGAIN: errno = EAGAIN; break;
|
|
case Noux::Sysio::ACCEPT_ERR_NO_MEMORY: errno = ENOMEM; break;
|
|
case Noux::Sysio::ACCEPT_ERR_INVALID: errno = EINVAL; break;
|
|
case Noux::Sysio::ACCEPT_ERR_NOT_SUPPORTED: errno = EOPNOTSUPP; break;
|
|
case Noux::Sysio::ACCEPT_ERR_WOULD_BLOCK: errno = EWOULDBLOCK; break;
|
|
default: errno = 0; break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (addr != NULL)
|
|
*addrlen = sysio()->accept_in.addrlen;
|
|
|
|
Libc::Plugin_context *context = noux_context(sysio()->accept_out.fd);
|
|
return Libc::file_descriptor_allocator()->alloc(this, context,
|
|
sysio()->accept_out.fd);
|
|
}
|
|
|
|
|
|
int Plugin::bind(Libc::File_descriptor *fd, const struct sockaddr *addr,
|
|
socklen_t addrlen)
|
|
{
|
|
sysio()->bind_in.fd = noux_fd(fd->context);
|
|
|
|
Genode::memcpy(&sysio()->bind_in.addr, addr, sizeof (struct sockaddr));
|
|
sysio()->bind_in.addrlen = addrlen;
|
|
|
|
if (!noux_syscall(Noux::Session::SYSCALL_BIND)) {
|
|
switch (sysio()->error.bind) {
|
|
case Noux::Sysio::BIND_ERR_ACCESS: errno = EACCES; break;
|
|
case Noux::Sysio::BIND_ERR_ADDR_IN_USE: errno = EADDRINUSE; break;
|
|
case Noux::Sysio::BIND_ERR_INVALID: errno = EINVAL; break;
|
|
case Noux::Sysio::BIND_ERR_NO_MEMORY: errno = ENOMEM; break;
|
|
default: errno = 0; break;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Plugin::connect(Libc::File_descriptor *fd, const struct sockaddr *addr,
|
|
socklen_t addrlen)
|
|
{
|
|
sysio()->connect_in.fd = noux_fd(fd->context);
|
|
|
|
Genode::memcpy(&sysio()->connect_in.addr, addr, sizeof (struct sockaddr));
|
|
sysio()->connect_in.addrlen = addrlen;
|
|
|
|
if (!noux_syscall(Noux::Session::SYSCALL_CONNECT)) {
|
|
switch (sysio()->error.connect) {
|
|
case Noux::Sysio::CONNECT_ERR_AGAIN: errno = EAGAIN; break;
|
|
case Noux::Sysio::CONNECT_ERR_ALREADY: errno = EALREADY; break;
|
|
case Noux::Sysio::CONNECT_ERR_ADDR_IN_USE: errno = EADDRINUSE; break;
|
|
case Noux::Sysio::CONNECT_ERR_IN_PROGRESS: errno = EINPROGRESS; break;
|
|
case Noux::Sysio::CONNECT_ERR_IS_CONNECTED: errno = EISCONN; break;
|
|
case Noux::Sysio::CONNECT_ERR_RESET: errno = ECONNRESET; break;
|
|
case Noux::Sysio::CONNECT_ERR_ABORTED: errno = ECONNABORTED; break;
|
|
case Noux::Sysio::CONNECT_ERR_NO_ROUTE: errno = EHOSTUNREACH; break;
|
|
default: errno = 0; break;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Plugin::getpeername(Libc::File_descriptor *fd, struct sockaddr *addr,
|
|
socklen_t *addrlen)
|
|
{
|
|
sysio()->getpeername_in.fd = noux_fd(fd->context);
|
|
sysio()->getpeername_in.addrlen = *addrlen;
|
|
|
|
if (!noux_syscall(Noux::Session::SYSCALL_GETPEERNAME)) {
|
|
/* errno */
|
|
return -1;
|
|
}
|
|
|
|
Genode::memcpy(addr, &sysio()->getpeername_in.addr,
|
|
sizeof (struct sockaddr));
|
|
*addrlen = sysio()->getpeername_in.addrlen;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Plugin::listen(Libc::File_descriptor *fd, int backlog)
|
|
{
|
|
sysio()->listen_in.fd = noux_fd(fd->context);
|
|
sysio()->listen_in.backlog = backlog;
|
|
|
|
if (!noux_syscall(Noux::Session::SYSCALL_LISTEN)) {
|
|
switch (sysio()->error.listen) {
|
|
case Noux::Sysio::LISTEN_ERR_ADDR_IN_USE: errno = EADDRINUSE; break;
|
|
case Noux::Sysio::LISTEN_ERR_NOT_SUPPORTED: errno = EOPNOTSUPP; break;
|
|
default: errno = 0; break;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
ssize_t Plugin::recv(Libc::File_descriptor *fd, void *buf, ::size_t len, int flags)
|
|
{
|
|
if (!buf) { errno = EFAULT; return -1; }
|
|
|
|
Genode::size_t sum_recv_count = 0;
|
|
|
|
while (len > 0) {
|
|
Genode::size_t curr_len =
|
|
Genode::min(len, sizeof(sysio()->recv_in.buf));
|
|
|
|
sysio()->recv_in.fd = noux_fd(fd->context);
|
|
sysio()->recv_in.len = curr_len;
|
|
|
|
if (!noux_syscall(Noux::Session::SYSCALL_RECV)) {
|
|
switch (sysio()->error.recv) {
|
|
case Noux::Sysio::RECV_ERR_AGAIN: errno = EAGAIN; break;
|
|
case Noux::Sysio::RECV_ERR_WOULD_BLOCK: errno = EWOULDBLOCK; break;
|
|
case Noux::Sysio::RECV_ERR_INVALID: errno = EINVAL; break;
|
|
case Noux::Sysio::RECV_ERR_NOT_CONNECTED: errno = ENOTCONN; break;
|
|
default: errno = 0; break;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
Genode::memcpy((char *)buf + sum_recv_count,
|
|
sysio()->recv_in.buf, sysio()->recv_out.len);
|
|
|
|
sum_recv_count += sysio()->recv_out.len;
|
|
|
|
if (sysio()->recv_out.len < curr_len)
|
|
break;
|
|
|
|
if (sysio()->recv_out.len <= len)
|
|
len -= sysio()->recv_out.len;
|
|
else
|
|
break;
|
|
}
|
|
|
|
return sum_recv_count;
|
|
}
|
|
|
|
|
|
ssize_t Plugin::recvfrom(Libc::File_descriptor *fd, void *buf, ::size_t len, int flags,
|
|
struct sockaddr *src_addr, socklen_t *addrlen)
|
|
{
|
|
if (!buf) { errno = EFAULT; return -1; }
|
|
|
|
Genode::size_t sum_recvfrom_count = 0;
|
|
|
|
while (len > 0) {
|
|
Genode::size_t curr_len = Genode::min(len, sizeof(sysio()->recvfrom_in.buf));
|
|
|
|
sysio()->recv_in.fd = noux_fd(fd->context);
|
|
sysio()->recv_in.len = curr_len;
|
|
|
|
if (addrlen == NULL)
|
|
sysio()->recvfrom_in.addrlen = 0;
|
|
else
|
|
sysio()->recvfrom_in.addrlen = *addrlen;
|
|
|
|
if (!noux_syscall(Noux::Session::SYSCALL_RECVFROM)) {
|
|
switch (sysio()->error.recv) {
|
|
case Noux::Sysio::RECV_ERR_AGAIN: errno = EAGAIN; break;
|
|
case Noux::Sysio::RECV_ERR_WOULD_BLOCK: errno = EWOULDBLOCK; break;
|
|
case Noux::Sysio::RECV_ERR_INVALID: errno = EINVAL; break;
|
|
case Noux::Sysio::RECV_ERR_NOT_CONNECTED: errno = ENOTCONN; break;
|
|
default: errno = 0; break;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
if (src_addr != NULL && addrlen != NULL)
|
|
Genode::memcpy(src_addr, &sysio()->recvfrom_in.src_addr,
|
|
sysio()->recvfrom_in.addrlen);
|
|
|
|
|
|
Genode::memcpy((char *)buf + sum_recvfrom_count,
|
|
sysio()->recvfrom_in.buf, sysio()->recvfrom_out.len);
|
|
|
|
sum_recvfrom_count += sysio()->recvfrom_out.len;
|
|
|
|
if (sysio()->recvfrom_out.len < curr_len)
|
|
break;
|
|
|
|
if (sysio()->recvfrom_out.len <= len)
|
|
len -= sysio()->recvfrom_out.len;
|
|
else
|
|
break;
|
|
}
|
|
|
|
return sum_recvfrom_count;
|
|
}
|
|
|
|
|
|
ssize_t Plugin::send(Libc::File_descriptor *fd, const void *buf, ::size_t len, int flags)
|
|
{
|
|
if (!buf) { errno = EFAULT; return -1; }
|
|
|
|
/* remember original len for the return value */
|
|
int const orig_count = len;
|
|
char *src = (char *)buf;
|
|
|
|
while (len > 0) {
|
|
|
|
Genode::size_t curr_len = Genode::min(sizeof (sysio()->send_in.buf), len);
|
|
|
|
sysio()->send_in.fd = noux_fd(fd->context);
|
|
sysio()->send_in.len = curr_len;
|
|
Genode::memcpy(sysio()->send_in.buf, src, curr_len);
|
|
|
|
if (!noux_syscall(Noux::Session::SYSCALL_SEND)) {
|
|
error("write error ", (int)sysio()->error.general);
|
|
switch (sysio()->error.send) {
|
|
case Noux::Sysio::SEND_ERR_AGAIN: errno = EAGAIN; break;
|
|
case Noux::Sysio::SEND_ERR_WOULD_BLOCK: errno = EWOULDBLOCK; break;
|
|
case Noux::Sysio::SEND_ERR_CONNECTION_RESET: errno = ECONNRESET; break;
|
|
case Noux::Sysio::SEND_ERR_INVALID: errno = EINVAL; break;
|
|
case Noux::Sysio::SEND_ERR_IS_CONNECTED: errno = EISCONN; break;
|
|
case Noux::Sysio::SEND_ERR_NO_MEMORY: errno = ENOMEM; break;
|
|
default: errno = 0; break;
|
|
}
|
|
/* return foo */
|
|
}
|
|
|
|
len -= curr_len;
|
|
src += curr_len;
|
|
}
|
|
return orig_count;
|
|
}
|
|
|
|
|
|
ssize_t Plugin::sendto(Libc::File_descriptor *fd, const void *buf, size_t len, int flags,
|
|
const struct sockaddr *dest_addr, socklen_t addrlen)
|
|
{
|
|
if (!buf) { errno = EFAULT; return -1; }
|
|
|
|
int const orig_count = len;
|
|
|
|
if (addrlen > sizeof (sysio()->sendto_in.dest_addr)) {
|
|
errno = 0; /* XXX */
|
|
return -1;
|
|
}
|
|
|
|
/* wipe-out sendto buffer */
|
|
Genode::memset(sysio()->sendto_in.buf, 0, sizeof (sysio()->sendto_in.buf));
|
|
|
|
char *src = (char *)buf;
|
|
while (len > 0) {
|
|
size_t curr_len = Genode::min(sizeof (sysio()->sendto_in.buf), len);
|
|
|
|
sysio()->sendto_in.fd = noux_fd(fd->context);
|
|
sysio()->sendto_in.len = curr_len;
|
|
Genode::memcpy(sysio()->sendto_in.buf, src, curr_len);
|
|
|
|
if (addrlen == 0) {
|
|
sysio()->sendto_in.addrlen = 0;
|
|
Genode::memset(&sysio()->sendto_in.dest_addr, 0, sizeof (struct sockaddr));
|
|
}
|
|
else {
|
|
sysio()->sendto_in.addrlen = addrlen;
|
|
Genode::memcpy(&sysio()->sendto_in.dest_addr, dest_addr, addrlen);
|
|
}
|
|
|
|
if (!noux_syscall(Noux::Session::SYSCALL_SENDTO)) {
|
|
switch (sysio()->error.send) {
|
|
case Noux::Sysio::SEND_ERR_AGAIN: errno = EAGAIN; break;
|
|
case Noux::Sysio::SEND_ERR_WOULD_BLOCK: errno = EWOULDBLOCK; break;
|
|
case Noux::Sysio::SEND_ERR_CONNECTION_RESET: errno = ECONNRESET; break;
|
|
case Noux::Sysio::SEND_ERR_INVALID: errno = EINVAL; break;
|
|
case Noux::Sysio::SEND_ERR_IS_CONNECTED: errno = EISCONN; break;
|
|
case Noux::Sysio::SEND_ERR_NO_MEMORY: errno = ENOMEM; break;
|
|
default: errno = 0; break;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
len -= curr_len;
|
|
src += curr_len;
|
|
}
|
|
|
|
return orig_count;
|
|
}
|
|
|
|
|
|
int Plugin::shutdown(Libc::File_descriptor *fd, int how)
|
|
{
|
|
sysio()->shutdown_in.fd = noux_fd(fd->context);
|
|
sysio()->shutdown_in.how = how;
|
|
|
|
if (!noux_syscall(Noux::Session::SYSCALL_SHUTDOWN)) {
|
|
switch (sysio()->error.shutdown) {
|
|
case Noux::Sysio::SHUTDOWN_ERR_NOT_CONNECTED: errno = ENOTCONN; break;
|
|
default: errno = 0; break;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
} /* unnamed namespace */
|
|
|
|
|
|
/**************************************
|
|
** Obtaining command-line arguments **
|
|
**************************************/
|
|
|
|
/* external symbols provided by Genode's startup code */
|
|
extern char **genode_argv;
|
|
extern int genode_argc;
|
|
extern char **genode_envp;
|
|
|
|
/* pointer to environment, provided by libc */
|
|
extern char **environ;
|
|
|
|
|
|
/* register noux libc plugin at libc */
|
|
static Plugin noux_plugin;
|
|
|
|
|
|
void Plugin::init(Genode::Env &env)
|
|
{
|
|
_env_ptr = &env;
|
|
noux_connection();
|
|
|
|
/* register inherited open file descriptors */
|
|
int fd = 0;
|
|
while ((fd = noux()->next_open_fd(fd)) != -1) {
|
|
Libc::file_descriptor_allocator()->alloc(this, noux_context(fd), fd);
|
|
fd++;
|
|
}
|
|
|
|
sigemptyset(&signal_mask);
|
|
|
|
/* copy command-line arguments from 'args' ROM dataspace */
|
|
enum { MAX_ARGS = 256, ARG_BUF_SIZE = sizeof(Noux::Sysio::Args) };
|
|
static char *argv[MAX_ARGS];
|
|
static char arg_buf[ARG_BUF_SIZE];
|
|
{
|
|
Genode::Attached_rom_dataspace ds(env, "args");
|
|
Genode::memcpy(arg_buf, ds.local_addr<char>(),
|
|
Genode::min((size_t)ARG_BUF_SIZE, ds.size()));
|
|
}
|
|
|
|
int argc = 0;
|
|
for (unsigned i = 0; arg_buf[i] && (i < ARG_BUF_SIZE - 2); ) {
|
|
|
|
if (i >= ARG_BUF_SIZE - 2) {
|
|
warning("command-line argument buffer exceeded");
|
|
break;
|
|
}
|
|
|
|
if (argc >= MAX_ARGS - 1) {
|
|
warning("number of command-line arguments exceeded");
|
|
break;
|
|
}
|
|
|
|
argv[argc] = &arg_buf[i];
|
|
i += Genode::strlen(&arg_buf[i]) + 1; /* skip null-termination */
|
|
argc++;
|
|
}
|
|
|
|
/* register command-line arguments at Genode's startup code */
|
|
genode_argv = argv;
|
|
genode_argc = argc;
|
|
|
|
/*
|
|
* Make environment variables from 'env' ROM dataspace available to libc's
|
|
* 'environ'.
|
|
*/
|
|
enum { ENV_MAX_ENTRIES = 128, ENV_BUF_SIZE = 8*1024 };
|
|
static char *env_array[ENV_MAX_ENTRIES];
|
|
static char env_buf[ENV_BUF_SIZE];
|
|
{
|
|
Genode::Attached_rom_dataspace ds(env, "env");
|
|
Genode::memcpy(env_buf, ds.local_addr<char>(),
|
|
Genode::min((size_t)ENV_BUF_SIZE, ds.size()));
|
|
}
|
|
char *env_string = &env_buf[0];
|
|
|
|
unsigned num_entries = 0; /* index within 'env_array' */
|
|
|
|
static Libc::Absolute_path noux_cwd("/");
|
|
|
|
while (*env_string && (num_entries < ENV_MAX_ENTRIES - 1)) {
|
|
if ((strlen(env_string) >= strlen("NOUX_CWD=")) &&
|
|
(strncmp(env_string, "NOUX_CWD=", strlen("NOUX_CWD=")) == 0)) {
|
|
noux_cwd.import(&env_string[strlen("NOUX_CWD=")]);
|
|
} else {
|
|
env_array[num_entries++] = env_string;
|
|
}
|
|
env_string += (strlen(env_string) + 1);
|
|
}
|
|
env_array[num_entries] = 0;
|
|
|
|
/* register list of environment variables at libc 'environ' pointer */
|
|
environ = env_array;
|
|
|
|
/* define env pointer to be passed to main function (in '_main.cc') */
|
|
genode_envp = environ;
|
|
|
|
chdir(noux_cwd.base());
|
|
}
|
|
|
|
|