Stacked file systems for Noux

This patch introduces support for stacked file systems alongside new
glue for accessing file-system implementations provided via Genode's
new file-system-session interface.

Using stacked file systems, an arbitrary number of file systems (such
as tar archives or file systems implemented as separate Genode
components) can be composed to form one merged virtual file system.

An example is given via the 'ports/run/noux_bash.run' script. This run
script creates a virtual file system out of multiple tar archives each
containing the content of a particular GNU package. In addition, one
'ram_fs' is mounted, which enables Noux to perform write operations.
This way, the shell output can be redirected to a file, or files can
be saved in VIM.

Fixes #103.
This commit is contained in:
Norman Feske 2012-04-25 23:52:49 +02:00
parent ae1d0c04ae
commit 66290ea46d
20 changed files with 1476 additions and 382 deletions

View File

@ -44,6 +44,7 @@ namespace Noux {
SYSCALL_OPEN,
SYSCALL_CLOSE,
SYSCALL_IOCTL,
SYSCALL_LSEEK,
SYSCALL_DIRENT,
SYSCALL_FCHDIR,
SYSCALL_EXECVE,
@ -53,6 +54,9 @@ namespace Noux {
SYSCALL_WAIT4,
SYSCALL_PIPE,
SYSCALL_DUP2,
SYSCALL_UNLINK,
SYSCALL_RENAME,
SYSCALL_MKDIR,
SYSCALL_INVALID = -1
};
@ -69,6 +73,7 @@ namespace Noux {
NOUX_DECL_SYSCALL_NAME(OPEN)
NOUX_DECL_SYSCALL_NAME(CLOSE)
NOUX_DECL_SYSCALL_NAME(IOCTL)
NOUX_DECL_SYSCALL_NAME(LSEEK)
NOUX_DECL_SYSCALL_NAME(DIRENT)
NOUX_DECL_SYSCALL_NAME(FCHDIR)
NOUX_DECL_SYSCALL_NAME(EXECVE)
@ -78,6 +83,9 @@ namespace Noux {
NOUX_DECL_SYSCALL_NAME(WAIT4)
NOUX_DECL_SYSCALL_NAME(PIPE)
NOUX_DECL_SYSCALL_NAME(DUP2)
NOUX_DECL_SYSCALL_NAME(UNLINK)
NOUX_DECL_SYSCALL_NAME(RENAME)
NOUX_DECL_SYSCALL_NAME(MKDIR)
case SYSCALL_INVALID: return 0;
}
return 0;

View File

@ -58,6 +58,17 @@ namespace Noux {
typedef __SIZE_TYPE__ size_t;
/**
* Flags of 'mode' argument of open syscall
*/
enum {
OPEN_MODE_RDONLY = 0,
OPEN_MODE_WRONLY = 1,
OPEN_MODE_RDWR = 2,
OPEN_MODE_ACCMODE = 3,
OPEN_MODE_CREATE = 0x0200,
};
enum {
STAT_MODE_SYMLINK = 0120000,
STAT_MODE_FILE = 0100000,
@ -100,6 +111,8 @@ namespace Noux {
};
};
enum Lseek_whence { LSEEK_SET, LSEEK_CUR, LSEEK_END };
enum { DIRENT_MAX_NAME_LEN = 128 };
enum Dirent_type {
@ -208,8 +221,14 @@ namespace Noux {
enum General_error { ERR_FD_INVALID, NUM_GENERAL_ERRORS };
enum Stat_error { STAT_ERR_NO_ENTRY = NUM_GENERAL_ERRORS };
enum Fcntl_error { FCNTL_ERR_CMD_INVALID = NUM_GENERAL_ERRORS };
enum Open_error { OPEN_ERR_UNACCESSIBLE = NUM_GENERAL_ERRORS };
enum Open_error { OPEN_ERR_UNACCESSIBLE, OPEN_ERR_NO_PERM };
enum Execve_error { EXECVE_NONEXISTENT = NUM_GENERAL_ERRORS };
enum Unlink_error { UNLINK_ERR_NO_ENTRY, UNLINK_ERR_NO_PERM };
enum Rename_error { RENAME_ERR_NO_ENTRY, RENAME_ERR_CROSS_FS,
RENAME_ERR_NO_PERM };
enum Mkdir_error { MKDIR_ERR_EXISTS, MKDIR_ERR_NO_ENTRY,
MKDIR_ERR_NO_SPACE, MKDIR_ERR_NO_PERM,
MKDIR_ERR_NAME_TOO_LONG};
union {
General_error general;
@ -217,13 +236,17 @@ namespace Noux {
Fcntl_error fcntl;
Open_error open;
Execve_error execve;
Unlink_error unlink;
Rename_error rename;
Mkdir_error mkdir;
} error;
union {
SYSIO_DECL(getcwd, { }, { Path path; });
SYSIO_DECL(write, { int fd; size_t count; Chunk chunk; }, { });
SYSIO_DECL(write, { int fd; size_t count; Chunk chunk; },
{ size_t count; });
SYSIO_DECL(stat, { Path path; }, { Stat st; });
@ -238,7 +261,10 @@ namespace Noux {
SYSIO_DECL(ioctl, : Ioctl_in { int fd; }, : Ioctl_out { });
SYSIO_DECL(dirent, { int fd; int index; }, { Dirent entry; });
SYSIO_DECL(lseek, { int fd; off_t offset; Lseek_whence whence; },
{ off_t offset; });
SYSIO_DECL(dirent, { int fd; }, { Dirent entry; });
SYSIO_DECL(fchdir, { int fd; }, { });
@ -261,6 +287,12 @@ namespace Noux {
SYSIO_DECL(pipe, { }, { int fd[2]; });
SYSIO_DECL(dup2, { int fd; int to_fd; }, { });
SYSIO_DECL(unlink, { Path path; }, { });
SYSIO_DECL(rename, { Path from_path; Path to_path; }, { });
SYSIO_DECL(mkdir, { Path path; int mode; }, { });
};
};
};

View File

@ -47,7 +47,7 @@ install_config {
<start name="noux">
<resource name="RAM" quantum="1G"/>
<config>
<fstab> <tar name="coreutils.tar" at="/"/> </fstab>
<fstab> <tar name="coreutils.tar" /> </fstab>
<start name="/bin/ls"> <arg value="-Rla"/> </start>
</config>
</start>

View File

@ -12,7 +12,7 @@ if {![have_spec x86]} {
set build_components {
core init drivers/timer noux lib/libc_noux
drivers/framebuffer drivers/pci drivers/input
server/terminal
server/terminal server/ram_fs
test/libports/ncurses
}
@ -38,9 +38,9 @@ close $vimrc_fd
# strip all binaries prior archiving
exec sh -c "find bin/bash/ bin/vim/ bin/coreutils/ -type f | (xargs strip || true) 2>/dev/null"
exec tar cfv bin/bash.tar -h -C bin/bash .
exec tar rfv bin/bash.tar -h -C bin/coreutils .
exec tar rfv bin/bash.tar -h -C bin/vim .
exec tar cfv bin/bash.tar -h -C bin/bash .
exec tar cfv bin/coreutils.tar -h -C bin/coreutils .
exec tar cfv bin/vim.tar -h -C bin/vim .
create_boot_directory
@ -101,23 +101,62 @@ append_if [have_spec ps2] config {
</start> }
append config {
<start name="terminal">
<resource name="RAM" quantum="2M"/>
<provides><service name="Terminal"/></provides>
<config>
<keyboard layout="de"/>
</config>
</start>
<start name="noux">
<resource name="RAM" quantum="1G"/>
<config>
<fstab> <tar name="bash.tar" at="/"/> </fstab>
<start name="/bin/bash">
<env name="TERM" value="linux" />
</start>
</config>
</start>
</config>
<start name="terminal">
<resource name="RAM" quantum="2M"/>
<provides><service name="Terminal"/></provides>
<config>
<keyboard layout="de"/>
</config>
</start>
<start name="ram_fs">
<resource name="RAM" quantum="10M"/>
<provides><service name="File_system"/></provides>
<config>
<!-- preload RAM file system with some ROM images -->
<content>
<dir name="tmp">
<rom name="init" as="blubb" />
</dir>
<dir name="home">
<dir name="user">
<!-- just a place holder -->
<rom name="timer" />
</dir>
</dir>
</content>
<!-- constrain sessions according to their labels -->
<policy label="noux -> root" root="/" />
<policy label="noux -> home" root="/home/user" writeable="yes" />
<policy label="noux -> tmp" root="/tmp" writeable="yes" />
</config>
</start>
<start name="noux">
<resource name="RAM" quantum="1G" />
<config>
<fstab>
<tar name="coreutils.tar" />
<tar name="vim.tar" />
<tar name="bash.tar" />
<!-- Example of how to impose policy onto a file system
session. The label attached to the 'fs' node is used
as a key to select the policy of 'ram_fs' -->
<dir name="home"> <fs label="home" /> </dir>
<!-- The entirety of ram_fs is mounted within the '/ram'
directory. -->
<dir name="ram"> <fs label="root" /> </dir>
<dir name="tmp"> <fs label="tmp" /> </dir>
</fstab>
<start name="/bin/bash">
<env name="TERM" value="linux" />
</start>
</config>
</start>
</config>
}
install_config $config
@ -129,9 +168,9 @@ install_config $config
# generic modules
set boot_modules {
core init timer ld.lib.so noux terminal
core init timer ld.lib.so noux terminal ram_fs
libc.lib.so libm.lib.so libc_noux.lib.so ncurses.lib.so
bash.tar
bash.tar coreutils.tar vim.tar
}
# platform-specific modules

View File

@ -46,7 +46,7 @@ install_config {
<start name="noux">
<resource name="RAM" quantum="1G"/>
<config>
<fstab> <tar name="noux_fork.tar" at="/"/> </fstab>
<fstab> <tar name="noux_fork.tar" /> </fstab>
<start name="test-noux_fork"> </start>
</config>
</start>

View File

@ -90,7 +90,7 @@ append config {
<start name="noux">
<resource name="RAM" quantum="1G"/>
<config>
<fstab> <tar name="vim.tar" at="/"/> </fstab>
<fstab> <tar name="vim.tar" /> </fstab>
<start name="/bin/vim">
<env name="TERM" value="linux" />

View File

@ -122,7 +122,7 @@ static void _sysio_to_stat_struct(Noux::Sysio const *sysio, struct stat *buf)
}
static int _stat(const char *path, struct stat *buf, bool lstat = false)
static int _stat(char const *path, struct stat *buf, bool lstat = false)
{
if ((path == NULL) or (buf == NULL)) {
errno = EFAULT;
@ -144,7 +144,7 @@ static int _stat(const char *path, struct stat *buf, bool lstat = false)
}
extern "C" int lstat(const char *path, struct stat *buf) { return _stat(path, buf, true); }
extern "C" int lstat(char const *path, struct stat *buf) { return _stat(path, buf, true); }
static bool serialize_string_array(char const * const * array, char *dst, Genode::size_t dst_len)
@ -166,7 +166,7 @@ static bool serialize_string_array(char const * const * array, char *dst, Genode
}
extern "C" int execve(const char *filename, char *const argv[],
extern "C" int execve(char const *filename, char *const argv[],
char *const envp[])
{
if (verbose) {
@ -383,6 +383,20 @@ extern "C" pid_t getpid(void)
}
extern "C" int access(char const *pathname, int mode)
{
PDBG("access '%s' (mode=%x) called, not implemented", pathname, mode);
return 0;
}
extern "C" int chmod(char const *path, mode_t mode)
{
PDBG("chmod '%s' to 0x%x not implemented", path, mode);
return 0;
}
extern "C" pid_t _wait4(pid_t pid, int *status, int options,
struct rusage *rusage)
{
@ -496,35 +510,42 @@ namespace {
_stderr(Libc::file_descriptor_allocator()->alloc(this, noux_context(2), 2))
{ }
bool supports_chdir(const char *) { return true; }
bool supports_open(const char *, int) { return true; }
bool supports_stat(const char *) { return true; }
bool supports_pipe() { return true; }
bool supports_chdir(char const *) { return true; }
bool supports_open(char const *, int) { return true; }
bool supports_stat(char const *) { return true; }
bool supports_pipe() { return true; }
bool supports_unlink(char const *) { return true; }
bool supports_rename(const char *, const char *) { return true; }
bool supports_mkdir(const char *, mode_t) { return true; }
Libc::File_descriptor *open(const char *, int);
Libc::File_descriptor *open(char const *, int);
ssize_t write(Libc::File_descriptor *, const void *, ::size_t);
int close(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 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);
int fchdir(Libc::File_descriptor *);
ssize_t read(Libc::File_descriptor *, void *, ::size_t);
int stat(const char *, struct stat *);
int stat(char const *, struct stat *);
int ioctl(Libc::File_descriptor *, int request, char *argp);
int pipe(Libc::File_descriptor *pipefd[2]);
int unlink(char const *path);
int rename(const char *oldpath, const char *newpath);
int mkdir(const char *path, mode_t mode);
};
int Plugin::stat(const char *path, struct stat *buf)
int Plugin::stat(char const *path, struct stat *buf)
{
return _stat(path, buf, false);
}
Libc::File_descriptor *Plugin::open(const char *pathname, int flags)
Libc::File_descriptor *Plugin::open(char const *pathname, int flags)
{
if (Genode::strlen(pathname) + 1 > sizeof(sysio()->open_in.path)) {
errno = ENAMETOOLONG;
@ -549,6 +570,7 @@ namespace {
int Plugin::fstatfs(Libc::File_descriptor *, struct statfs *buf)
{
buf->f_flags = MNT_UNION;
return 0;
}
@ -556,11 +578,6 @@ namespace {
ssize_t Plugin::write(Libc::File_descriptor *fd, const void *buf,
::size_t count)
{
if (fd != _stdout && fd != _stderr) {
errno = EBADF;
return -1;
}
/* remember original len for the return value */
int const orig_count = count;
@ -574,7 +591,7 @@ namespace {
Genode::memcpy(sysio()->write_in.chunk, src, curr_count);
if (!noux()->syscall(Noux::Session::SYSCALL_WRITE)) {
PERR("write error %d", sysio()->error.general);
PERR("write error %d (fd %d)", sysio()->error.general, noux_fd(fd->context));
}
count -= curr_count;
@ -750,12 +767,52 @@ namespace {
}
int Plugin::fsync(Libc::File_descriptor *fd)
{
PDBG("not implemented");
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:
{
/*
* 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->context = noux_context(new_fd->libc_fd);
/*
* Use new allocated number as name of file descriptor
* duplicate.
*/
if (dup2(fd, new_fd)) {
PERR("Plugin::fcntl: dup2 unexpectedly failed");
errno = EINVAL;
return -1;
}
return new_fd->libc_fd;
}
case F_GETFD:
/*
* Normally, we would return the file-descriptor flags.
*
* XXX: FD_CLOEXEC not yet supported
*/
PWRN("fcntl(F_GETFD) not implemented, returning 0");
return 0;
case F_SETFD:
sysio()->fcntl_in.cmd = Noux::Sysio::FCNTL_CMD_SET_FD_FLAGS;
sysio()->fcntl_in.long_arg = arg;
@ -793,10 +850,7 @@ namespace {
return -1;
}
unsigned const curr_offset = *basep;
sysio()->dirent_in.fd = noux_fd(fd->context);
sysio()->dirent_in.index = curr_offset / sizeof(struct dirent);
struct dirent *dirent = (struct dirent *)buf;
Genode::memset(dirent, 0, sizeof(struct dirent));
@ -837,13 +891,29 @@ namespace {
::off_t Plugin::lseek(Libc::File_descriptor *fd,
::off_t offset, int whence)
{
PWRN("lseek - not implemented: fd=%d, offset=%ld, whence=%d",
noux_fd(fd->context), (long)offset, whence);
sysio()->lseek_in.fd = noux_fd(fd->context);
sysio()->lseek_in.offset = offset;
if (whence == SEEK_SET)
return 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;
}
return 0;
if (!noux()->syscall(Noux::Session::SYSCALL_LSEEK)) {
switch (sysio()->error.general) {
case Noux::Sysio::ERR_FD_INVALID:
errno = EBADF;
PERR("dirent: ERR_FD_INVALID");
return -1;
case Noux::Sysio::NUM_GENERAL_ERRORS: return -1;
}
}
return sysio()->lseek_out.offset;
}
@ -859,6 +929,62 @@ namespace {
return 0;
}
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)) {
PWRN("unlink syscall failed for path \"%s\"", path);
switch (sysio()->error.unlink) {
case Noux::Sysio::UNLINK_ERR_NO_ENTRY: errno = ENOENT; break;
default: errno = EPERM; break;
}
return -1;
}
return 0;
}
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)) {
PWRN("rename syscall failed for \"%s\" -> \"%s\"", from_path, to_path);
switch (sysio()->error.rename) {
case Noux::Sysio::RENAME_ERR_NO_ENTRY: errno = ENOENT; break;
case Noux::Sysio::RENAME_ERR_CROSS_FS: errno = EXDEV; break;
case Noux::Sysio::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)) {
PWRN("mkdir syscall failed for \"%s\" mode=0x%x", path, (int)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;
}
} /* unnamed namespace */

View File

@ -22,7 +22,7 @@
/* Noux includes */
#include <file_descriptor_registry.h>
#include <vfs.h>
#include <dir_file_system.h>
#include <signal_dispatcher.h>
#include <noux_session/capability.h>
#include <args.h>
@ -195,7 +195,7 @@ namespace Noux {
*/
Environment _env;
Vfs * const _vfs;
Dir_file_system * const _root_dir;
/**
* ELF binary
@ -266,7 +266,7 @@ namespace Noux {
Family_member *parent,
int pid,
Signal_receiver *sig_rec,
Vfs *vfs,
Dir_file_system *root_dir,
Args const &args,
char const *env,
char const *pwd,
@ -286,9 +286,9 @@ namespace Noux {
_resources(name, resources_ep, false),
_args(ARGS_DS_SIZE, args),
_env(env),
_vfs(vfs),
_root_dir(root_dir),
_binary_ds(forked ? Dataspace_capability()
: vfs->dataspace_from_file(name)),
: root_dir->dataspace(name)),
_sysio_ds(Genode::env()->ram_session(), SYSIO_DS_SIZE),
_sysio(_sysio_ds.local_addr<Sysio>()),
_noux_session_cap(Session_capability(_entrypoint.manage(this))),
@ -313,7 +313,7 @@ namespace Noux {
_entrypoint.dissolve(this);
_vfs->release_dataspace_for_file(_child_policy.name(), _binary_ds);
_root_dir->release(_child_policy.name(), _binary_ds);
}
void start() { _entrypoint.activate(); }

View File

@ -0,0 +1,518 @@
/*
* \brief Directory file system
* \author Norman Feske
* \date 2012-04-23
*/
/*
* Copyright (C) 2011-2012 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.
*/
#ifndef _NOUX__DIR_FILE_SYSTEM_H_
#define _NOUX__DIR_FILE_SYSTEM_H_
/* Genode includes */
#include <base/printf.h>
#include <base/lock.h>
/* Noux includes */
#include <noux_session/sysio.h>
#include <tar_file_system.h>
#include <fs_file_system.h>
namespace Noux {
class Dir_file_system : public File_system
{
public:
enum { MAX_NAME_LEN = 128 };
private:
Lock _lock;
/* pointer to first child file system */
File_system *_first_file_system;
/* add new file system to the list of children */
void _append_file_system(File_system *fs)
{
if (!_first_file_system) {
_first_file_system = fs;
return;
}
File_system *curr = _first_file_system;
while (curr->next)
curr = curr->next;
curr->next = fs;
}
/**
* Directory name
*/
char _name[MAX_NAME_LEN];
bool _is_root() const { return _name[0] == 0; }
public:
Dir_file_system(Xml_node node) : _first_file_system(0)
{
/* remember directory name */
if (node.has_type("fstab"))
_name[0] = 0;
else
node.attribute("name").value(_name, sizeof(_name));
for (unsigned i = 0; i < node.num_sub_nodes(); i++) {
Xml_node sub_node = node.sub_node(i);
if (sub_node.has_type("tar")) {
_append_file_system(new Tar_file_system(sub_node));
continue;
}
if (sub_node.has_type("fs")) {
_append_file_system(new Fs_file_system(sub_node));
continue;
}
/* traverse into <dir> nodes */
if (sub_node.has_type("dir")) {
_append_file_system(new Dir_file_system(sub_node));
continue;
}
{
char type_name[64];
sub_node.type_name(type_name, sizeof(type_name));
PWRN("unknown fstab node type <%s>", type_name);
}
}
}
/**
* Return portion of the path after the element corresponding to
* the current directory.
*/
char const *_sub_path(char const *path) const
{
/* do not strip anything from the path when we are root */
if (_is_root())
return path;
/* skip heading slash in path if present */
if (path[0] == '/')
path++;
size_t const name_len = strlen(_name);
if (strcmp(path, _name, name_len) != 0)
return 0;
path += name_len;
/*
* The first characters of the first path element are equal to
* the current directory name. Let's check if the length of the
* first path element matches the name length.
*/
if (*path != 0 && *path != '/')
return 0;
return path;
}
/*********************************
** Directory-service interface **
*********************************/
Dataspace_capability dataspace(char const *path)
{
path = _sub_path(path);
if (!path)
return Dataspace_capability();
/*
* Query sub file systems for dataspace using the path local to
* the respective file system
*/
File_system *fs = _first_file_system;
for (; fs; fs = fs->next) {
Dataspace_capability ds = fs->dataspace(path);
if (ds.valid())
return ds;
}
return Dataspace_capability();
}
void release(char const *path, Dataspace_capability ds_cap)
{
path = _sub_path(path);
if (!path)
return;
for (File_system *fs = _first_file_system; fs; fs = fs->next)
fs->release(path, ds_cap);
}
bool stat(Sysio *sysio, char const *path)
{
path = _sub_path(path);
/* path does not match directory name */
if (!path) {
sysio->error.stat = Sysio::STAT_ERR_NO_ENTRY;
return false;
}
/*
* If path equals directory name, return information about the
* current directory.
*/
if (strlen(path) == 0) {
sysio->stat_out.st.size = 0;
sysio->stat_out.st.mode = Sysio::STAT_MODE_DIRECTORY | 0755;
sysio->stat_out.st.uid = 0;
sysio->stat_out.st.gid = 0;
return true;
}
/*
* The given path refers to one of our sub directories.
* Propagate the request into our file systems.
*/
for (File_system *fs = _first_file_system; fs; fs = fs->next)
if (fs->stat(sysio, path))
return true;
/* none of our file systems felt responsible for the path */
sysio->error.stat = Sysio::STAT_ERR_NO_ENTRY;
return false;
}
/**
* The 'path' is relative to the child file systems.
*/
bool _dirent_of_file_systems(Sysio *sysio, char const *path, off_t index)
{
int base = 0;
for (File_system *fs = _first_file_system; fs; fs = fs->next) {
/*
* Determine number of matching directory entries within
* the current file system.
*/
int const fs_num_dirent = fs->num_dirent(path);
/*
* Query directory entry if index lies with the file
* system.
*/
if (index - base < fs_num_dirent) {
index = index - base;
bool const res = fs->dirent(sysio, path, index);
sysio->dirent_out.entry.fileno += base;
return res;
}
/* adjust base index for next file system */
base += fs_num_dirent;
}
sysio->dirent_out.entry.type = Sysio::DIRENT_TYPE_END;
return true;
}
bool _dirent_of_this_dir_node(Sysio *sysio, off_t index)
{
if (index == 0) {
strncpy(sysio->dirent_out.entry.name, _name,
sizeof(sysio->dirent_out.entry.name));
sysio->dirent_out.entry.type = Sysio::DIRENT_TYPE_DIRECTORY;
sysio->dirent_out.entry.fileno = (unsigned)this;
} else
sysio->dirent_out.entry.type = Sysio::DIRENT_TYPE_END;
return true;
}
bool dirent(Sysio *sysio, char const *path, off_t index)
{
Lock::Guard guard(_lock);
if (_is_root()) {
return _dirent_of_file_systems(sysio, path, index);
} else {
if (strcmp(path, "/") == 0)
return _dirent_of_this_dir_node(sysio, index);
/* path contains at least one element */
/* remove current element from path */
path = _sub_path(path);
if (path) {
return _dirent_of_file_systems(sysio, path, index);
} else {
/* path does not lie within our tree */
return false;
}
}
}
/*
* Accumulate number of directory entries that match in any of
* our sub file systems.
*/
size_t _sum_dirents_of_file_systems(char const *path)
{
size_t cnt = 0;
for (File_system *fs = _first_file_system; fs; fs = fs->next) {
cnt += fs->num_dirent(path);
}
return cnt;
}
size_t num_dirent(char const *path)
{
Lock::Guard guard(_lock);
if (_is_root()) {
return _sum_dirents_of_file_systems(path);
} else {
if (strcmp(path, "/") == 0)
return 1;
/*
* The path contains at least one element. Remove current
* element from path.
*/
path = _sub_path(path);
/*
* If the resulting 'path' is non-NULL, the path lies
* within our tree. In this case, determine the sum of
* matching dirents of all our file systems. Otherwise,
* the specified path lies outside our directory node.
*/
return path ? _sum_dirents_of_file_systems(path) : 0;
}
}
bool is_directory(char const *path)
{
Lock::Guard guard(_lock);
path = _sub_path(path);
if (!path)
return false;
if (strlen(path) == 0)
return true;
for (File_system *fs = _first_file_system; fs; fs = fs->next)
if (fs->is_directory(path))
return true;
return false;
}
char const *leaf_path(char const *path)
{
Lock::Guard guard(_lock);
path = _sub_path(path);
if (!path)
return 0;
if (strlen(path) == 0)
return path;
for (File_system *fs = _first_file_system; fs; fs = fs->next) {
char const *leaf_path = fs->leaf_path(path);
if (leaf_path)
return leaf_path;
}
return 0;
}
Vfs_handle *open(Sysio *sysio, char const *path)
{
/*
* If 'path' is a directory, we create a 'Vfs_handle'
* for the root directory so that subsequent 'dirent' calls
* are subjected to the stacked file-system layout.
*/
if (is_directory(path))
return new (env()->heap()) Vfs_handle(this, this, 0);
/*
* If 'path' refers to a non-directory node, create a
* 'Vfs_handle' local to the file system that provides the
* file.
*/
Lock::Guard guard(_lock);
path = _sub_path(path);
/* check if path does not match directory name */
if (!path)
return 0;
/* path equals directory name */
if (strlen(path) == 0)
return new (env()->heap()) Vfs_handle(this, this, 0);
/* path refers to any of our sub file systems */
for (File_system *fs = _first_file_system; fs; fs = fs->next) {
Vfs_handle *vfs_handle = fs->open(sysio, path);
if (vfs_handle)
return vfs_handle;
}
/* path does not match any existing file or directory */
return 0;
}
bool unlink(Sysio *sysio, char const *path)
{
path = _sub_path(path);
/* path does not match directory name */
if (!path) {
sysio->error.unlink = Sysio::UNLINK_ERR_NO_ENTRY;
return false;
}
/*
* Prevent unlinking if path equals directory name defined
* via the static fstab configuration.
*/
if (strlen(path) == 0) {
sysio->error.unlink = Sysio::UNLINK_ERR_NO_PERM;
return false;
}
/*
* The given path refers to at least one of our sub
* directories. Propagate the request into all of our file
* systems. If at least one unlink operation succeeded, we
* return success.
*/
bool unlink_ret = false;
Sysio::Unlink_error error = Sysio::UNLINK_ERR_NO_ENTRY;
for (File_system *fs = _first_file_system; fs; fs = fs->next)
if (fs->unlink(sysio, path)) {
unlink_ret = true;
} else {
/*
* Keep the most meaningful error code. When using
* stacked file systems, most child file systems will
* eventually return 'UNLINK_ERR_NO_ENTRY'. If any of
* those file systems has anything more interesting to
* tell (in particular 'UNLINK_ERR_NO_PERM'), return
* this information.
*/
if (sysio->error.unlink != Sysio::UNLINK_ERR_NO_ENTRY)
error = sysio->error.unlink;
}
sysio->error.unlink = error;
return unlink_ret;
}
bool rename(Sysio *sysio, char const *from_path, char const *to_path)
{
from_path = _sub_path(from_path);
/* path does not match directory name */
if (!from_path) {
sysio->error.rename = Sysio::RENAME_ERR_NO_ENTRY;
return false;
}
/*
* Prevent renaming if path equals directory name defined
* via the static fstab configuration.
*/
if (strlen(from_path) == 0) {
sysio->error.rename = Sysio::RENAME_ERR_NO_PERM;
return false;
}
/*
* Check if destination path resides within the same file
* system instance as the source path.
*/
to_path = _sub_path(to_path);
if (!to_path) {
sysio->error.rename = Sysio::RENAME_ERR_CROSS_FS;
return false;
}
/* path refers to any of our sub file systems */
for (File_system *fs = _first_file_system; fs; fs = fs->next)
if (fs->rename(sysio, from_path, to_path))
return true;
/* none of our file systems could successfully rename the path */
return false;
}
bool mkdir(Sysio *sysio, char const *path)
{
path = _sub_path(path);
/* path does not match directory name */
if (!path) {
sysio->error.mkdir = Sysio::MKDIR_ERR_NO_ENTRY;
return false;
}
/*
* Prevent mkdir of path that equals directory name defined
* via the static fstab configuration.
*/
if (strlen(path) == 0) {
sysio->error.mkdir = Sysio::MKDIR_ERR_EXISTS;
return false;
}
/* path refers to any of our sub file systems */
for (File_system *fs = _first_file_system; fs; fs = fs->next)
if (fs->mkdir(sysio, path))
return true;
/* none of our file systems could create the directory */
return false;
}
/***************************
** File_system interface **
***************************/
char const *name() const { return "dir"; }
/********************************
** File I/O service interface **
********************************/
bool write(Sysio *sysio, Vfs_handle *handle) { return false; }
bool read(Sysio *sysio, Vfs_handle *vfs_handle) { return false; }
};
}
#endif /* _NOUX__DIR_FILE_SYSTEM_H_ */

View File

@ -30,14 +30,25 @@ namespace Noux {
struct Directory_service
{
virtual Dataspace_capability dataspace(char const *path) = 0;
virtual void release(Dataspace_capability) = 0;
virtual void release(char const *path, Dataspace_capability) = 0;
virtual Vfs_handle *open(Sysio *sysio, char const *path) = 0;
virtual bool stat(Sysio *sysio, char const *path) = 0;
virtual bool dirent(Sysio *sysio, char const *path) = 0;
virtual bool dirent(Sysio *sysio, char const *path, off_t index) = 0;
virtual bool unlink(Sysio *sysio, char const *path) = 0;
virtual bool rename(Sysio *sysio, char const *from_path,
char const *to_path) = 0;
virtual bool mkdir(Sysio *sysio, char const *path) = 0;
virtual void close(Vfs_handle *handle) = 0;
/**
* Return number of directory entries located at given path
*/
virtual size_t num_dirent(char const *path) = 0;
virtual bool is_directory(char const *path) = 0;
virtual char const *leaf_path(char const *path) = 0;
};
}

View File

@ -5,7 +5,7 @@
*/
/*
* Copyright (C) 2011-2012 gENODe Labs GmbH
* Copyright (C) 2011-2012 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.
@ -22,71 +22,26 @@
/* Noux includes */
#include <directory_service.h>
#include <file_io_service.h>
#include <path.h>
#include <noux_session/sysio.h>
namespace Noux {
class File_system : public Directory_service, public File_io_service,
public List<File_system>::Element
struct File_system : Directory_service, File_io_service
{
private:
/**
* Our next sibling within the same 'Dir_file_system'
*/
struct File_system *next;
char _mount_point[Sysio::MAX_PATH_LEN];
File_system() : next(0) { }
/**
* Strip any number of trailing slashes
*/
void _strip_trailing_slashes_from_mount_point()
{
size_t len;
while ((len = strlen(_mount_point)) && (_mount_point[len - 1] == '/'))
_mount_point[len - 1] = 0;
}
public:
File_system(Xml_node config)
{
enum { TYPE_MAX_LEN = 64 };
char type[TYPE_MAX_LEN];
config.type_name(type, sizeof(type));
config.attribute("at").value(_mount_point, sizeof(_mount_point));
_strip_trailing_slashes_from_mount_point();
PINF("created %s file system at \"%s\"", type, _mount_point);
}
File_system(char const *mount_point)
{
strncpy(_mount_point, mount_point, sizeof(_mount_point));
_strip_trailing_slashes_from_mount_point();
}
/**
* Return file-system-local path for the given global path
*
* This function checks if the global path lies within the mount
* point of the file system. If yes, it returns the sub string of
* the global path that corresponds to the path relative to the
* file system.
*
* \return pointer to file-system relative path name within
* the 'global_path' string, or
* 0 if global path lies outside the file system
*/
char const *local_path(char const *global_path)
{
size_t const mount_point_len = strlen(_mount_point);
if (strlen(global_path) < mount_point_len)
return 0;
if (strcmp(global_path, _mount_point, mount_point_len))
return 0;
return global_path + mount_point_len;
}
/**
* Return name of file system
*
* This function is used for debugging only.
*/
virtual char const *name() const = 0;
};
}

View File

@ -0,0 +1,451 @@
/*
* \brief Adapter from Genode 'File_system' session to Noux file system
* \author Norman Feske
* \date 2011-02-17
*/
/*
* Copyright (C) 2012 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.
*/
#ifndef _NOUX__FS_FILE_SYSTEM_H_
#define _NOUX__FS_FILE_SYSTEM_H_
/* Genode includes */
#include <base/allocator_avl.h>
#include <base/env.h>
/* Noux includes */
#include <noux_session/sysio.h>
#include <file_system_session/connection.h>
namespace Noux {
class Fs_file_system : public File_system
{
private:
Lock _lock;
Allocator_avl _fs_packet_alloc;
struct Label
{
enum { LABEL_MAX_LEN = 64 };
char string[LABEL_MAX_LEN];
Label(Xml_node config)
{
string[0] = 0;
try { config.attribute("label").value(string, sizeof(string)); }
catch (...) { }
}
} _label;
::File_system::Connection _fs;
class Fs_vfs_handle : public Vfs_handle
{
private:
::File_system::File_handle const _handle;
public:
Fs_vfs_handle(File_system *fs, int status_flags,
::File_system::File_handle handle)
: Vfs_handle(fs, fs, status_flags), _handle(handle)
{ }
~Fs_vfs_handle()
{
Fs_file_system *fs = static_cast<Fs_file_system *>(ds());
fs->_fs.close(_handle);
}
::File_system::File_handle file_handle() const { return _handle; }
};
/**
* Helper for managing the lifetime of temporary open node handles
*/
struct Fs_handle_guard
{
::File_system::Session &_fs;
::File_system::Node_handle _handle;
Fs_handle_guard(::File_system::Session &fs,
::File_system::Node_handle handle)
: _fs(fs), _handle(handle) { }
~Fs_handle_guard() { _fs.close(_handle); }
};
public:
/*
* XXX read label from config
*/
Fs_file_system(Xml_node config)
:
_fs_packet_alloc(env()->heap()),
_label(config),
_fs(_fs_packet_alloc, 128*1024, _label.string)
{ }
/*********************************
** Directory-service interface **
*********************************/
Dataspace_capability dataspace(char const *path)
{
return Dataspace_capability();
}
void release(char const *path, Dataspace_capability ds_cap)
{
}
bool stat(Sysio *sysio, char const *path)
{
::File_system::Status status;
try {
::File_system::Node_handle node = _fs.node(path);
Fs_handle_guard node_guard(_fs, node);
status = _fs.status(node);
} catch (...) {
PWRN("stat failed for path '%s'", path);
return false;
}
sysio->stat_out.st.size = status.size;
sysio->stat_out.st.mode = Sysio::STAT_MODE_FILE | 0777;
if (status.is_symlink())
sysio->stat_out.st.mode = Sysio::STAT_MODE_SYMLINK | 0777;
if (status.is_directory())
sysio->stat_out.st.mode = Sysio::STAT_MODE_DIRECTORY | 0777;
sysio->stat_out.st.uid = 0;
sysio->stat_out.st.gid = 0;
return true;
}
bool dirent(Sysio *sysio, char const *path, off_t index)
{
Lock::Guard guard(_lock);
::File_system::Session::Tx::Source &source = *_fs.tx();
if (strcmp(path, "") == 0)
path = "/";
::File_system::Dir_handle dir_handle = _fs.dir(path, false);
Fs_handle_guard dir_guard(_fs, dir_handle);
enum { DIRENT_SIZE = sizeof(::File_system::Directory_entry) };
::File_system::Packet_descriptor
packet(source.alloc_packet(DIRENT_SIZE),
0,
dir_handle,
::File_system::Packet_descriptor::READ,
DIRENT_SIZE,
index*DIRENT_SIZE);
/* pass packet to server side */
source.submit_packet(packet);
source.get_acked_packet();
/*
* XXX check if acked packet belongs to request,
* needed for thread safety
*/
typedef ::File_system::Directory_entry Directory_entry;
/* copy-out payload into destination buffer */
Directory_entry const *entry =
(Directory_entry *)source.packet_content(packet);
Sysio::Dirent_type type;
switch (entry->type) {
case Directory_entry::TYPE_DIRECTORY: type = Sysio::DIRENT_TYPE_DIRECTORY; break;
case Directory_entry::TYPE_FILE: type = Sysio::DIRENT_TYPE_FILE; break;
case Directory_entry::TYPE_SYMLINK: type = Sysio::DIRENT_TYPE_SYMLINK; break;
}
sysio->dirent_out.entry.type = type;
sysio->dirent_out.entry.fileno = index + 1;
strncpy(sysio->dirent_out.entry.name, entry->name,
sizeof(sysio->dirent_out.entry.name));
source.release_packet(packet);
return true;
}
bool unlink(Sysio *sysio, char const *path)
{
Lock::Guard guard(_lock);
Absolute_path dir_path(path);
dir_path.strip_last_element();
Absolute_path file_name(path);
file_name.keep_only_last_element();
try {
::File_system::Dir_handle dir = _fs.dir(dir_path.base(), false);
Fs_handle_guard dir_guard(_fs, dir);
_fs.unlink(dir, file_name.base() + 1);
} catch (...) {
sysio->error.unlink = Sysio::UNLINK_ERR_NO_ENTRY;
return false;
}
return true;
}
bool rename(Sysio *sysio, char const *from_path, char const *to_path)
{
Absolute_path from_dir_path(from_path);
from_dir_path.strip_last_element();
Absolute_path from_file_name(from_path);
from_file_name.keep_only_last_element();
Absolute_path to_dir_path(to_path);
to_dir_path.strip_last_element();
Absolute_path to_file_name(to_path);
to_file_name.keep_only_last_element();
try {
::File_system::Dir_handle from_dir = _fs.dir(from_dir_path.base(), false);
Fs_handle_guard from_dir_guard(_fs, from_dir);
::File_system::Dir_handle to_dir = _fs.dir(to_dir_path.base(), false);
Fs_handle_guard to_dir_guard(_fs, to_dir);
_fs.move(from_dir, from_file_name.base() + 1,
to_dir, to_file_name.base() + 1);
} catch (...) {
sysio->error.unlink = Sysio::UNLINK_ERR_NO_ENTRY;
return false;
}
return true;
}
bool mkdir(Sysio *sysio, char const *path)
{
/*
* Canonicalize path (i.e., path must start with '/')
*/
Absolute_path abs_path(path);
Sysio::Mkdir_error error = Sysio::MKDIR_ERR_NO_PERM;
try {
_fs.dir(abs_path.base(), true);
return true;
}
catch (::File_system::Permission_denied) { error = Sysio::MKDIR_ERR_NO_PERM; }
catch (::File_system::Node_already_exists) { error = Sysio::MKDIR_ERR_EXISTS; }
catch (::File_system::Lookup_failed) { error = Sysio::MKDIR_ERR_NO_ENTRY; }
catch (::File_system::Name_too_long) { error = Sysio::MKDIR_ERR_NAME_TOO_LONG; }
catch (::File_system::No_space) { error = Sysio::MKDIR_ERR_NO_SPACE; }
sysio->error.mkdir = error;
return false;
}
size_t num_dirent(char const *path)
{
if (strcmp(path, "") == 0)
path = "/";
/*
* XXX handle exceptions
*/
::File_system::Node_handle node = _fs.node(path);
Fs_handle_guard node_guard(_fs, node);
::File_system::Status status = _fs.status(node);
return status.size / sizeof(::File_system::Directory_entry);
}
bool is_directory(char const *path)
{
try {
::File_system::Node_handle node = _fs.node(path);
Fs_handle_guard node_guard(_fs, node);
::File_system::Status status = _fs.status(node);
return status.is_directory();
}
catch (...) { return false; }
}
char const *leaf_path(char const *path)
{
/* check if node at path exists within file system */
try {
::File_system::Node_handle node = _fs.node(path);
_fs.close(node);
}
catch (...) {
return 0; }
return path;
}
Vfs_handle *open(Sysio *sysio, char const *path)
{
Lock::Guard guard(_lock);
Absolute_path dir_path(path);
dir_path.strip_last_element();
Absolute_path file_name(path);
file_name.keep_only_last_element();
::File_system::Mode mode;
switch (sysio->open_in.mode & Sysio::OPEN_MODE_ACCMODE) {
default: mode = ::File_system::STAT_ONLY; break;
case Sysio::OPEN_MODE_RDONLY: mode = ::File_system::READ_ONLY; break;
case Sysio::OPEN_MODE_WRONLY: mode = ::File_system::WRITE_ONLY; break;
case Sysio::OPEN_MODE_RDWR: mode = ::File_system::READ_WRITE; break;
}
bool const create = sysio->open_in.mode & Sysio::OPEN_MODE_CREATE;
if (create)
PDBG("creation of file %s requested", file_name.base());
::File_system::Dir_handle dir = _fs.dir(dir_path.base(), false);
Fs_handle_guard dir_guard(_fs, dir);
Sysio::Open_error error = Sysio::OPEN_ERR_UNACCESSIBLE;
try {
::File_system::File_handle file = _fs.file(dir, file_name.base() + 1,
mode, create);
return new (env()->heap()) Fs_vfs_handle(this, 0, file);
}
catch (::File_system::Permission_denied) {
error = Sysio::OPEN_ERR_NO_PERM; }
catch (::File_system::Invalid_handle) {
error = Sysio::OPEN_ERR_NO_PERM; }
sysio->error.open = error;
return 0;
}
/***************************
** File_system interface **
***************************/
char const *name() const { return "fs"; }
/********************************
** File I/O service interface **
********************************/
bool write(Sysio *sysio, Vfs_handle *vfs_handle)
{
Fs_vfs_handle const *handle = static_cast<Fs_vfs_handle *>(vfs_handle);
::File_system::Session::Tx::Source &source = *_fs.tx();
size_t const max_packet_size = source.bulk_buffer_size() / 2;
size_t const count = min(max_packet_size,
min(sizeof(sysio->write_in.chunk),
sysio->write_in.count));
::File_system::Packet_descriptor
packet(source.alloc_packet(count),
0,
handle->file_handle(),
::File_system::Packet_descriptor::WRITE,
count,
handle->seek());
memcpy(source.packet_content(packet), sysio->write_in.chunk, count);
/* pass packet to server side */
source.submit_packet(packet);
source.get_acked_packet();
sysio->write_out.count = count;
/*
* XXX check if acked packet belongs to request,
* needed for thread safety
*/
source.release_packet(packet);
return true;
}
bool read(Sysio *sysio, Vfs_handle *vfs_handle)
{
Fs_vfs_handle const *handle = static_cast<Fs_vfs_handle *>(vfs_handle);
::File_system::Status status = _fs.status(handle->file_handle());
size_t const file_size = status.size;
size_t const file_bytes_left = file_size >= handle->seek()
? file_size - handle->seek() : 0;
::File_system::Session::Tx::Source &source = *_fs.tx();
size_t const max_packet_size = source.bulk_buffer_size() / 2;
size_t const count = min(max_packet_size,
min(file_bytes_left,
min(sizeof(sysio->read_out.chunk),
sysio->read_in.count)));
::File_system::Packet_descriptor
packet(source.alloc_packet(count),
0,
handle->file_handle(),
::File_system::Packet_descriptor::READ,
count,
handle->seek());
/* pass packet to server side */
source.submit_packet(packet);
source.get_acked_packet();
memcpy(sysio->read_out.chunk, source.packet_content(packet), count);
sysio->read_out.count = count;
/*
* XXX check if acked packet belongs to request,
* needed for thread safety
*/
source.release_packet(packet);
return true;
}
};
}
#endif /* _NOUX__FS_FILE_SYSTEM_H_ */

View File

@ -44,6 +44,10 @@ namespace Noux {
public:
bool close_on_execve;
Io_channel() : close_on_execve(false) { }
virtual ~Io_channel() { }
virtual bool write(Sysio *sysio, size_t &count) { return false; }
@ -53,6 +57,7 @@ namespace Noux {
virtual bool fchdir(Sysio *sysio, Pwd *pwd) { return false; }
virtual bool dirent(Sysio *sysio) { return false; }
virtual bool ioctl(Sysio *sysio) { return false; }
virtual bool lseek(Sysio *sysio) { return false; }
/**
* Return true if an unblocking condition of the channel is satisfied

View File

@ -34,8 +34,8 @@
* ;- import env into child (execve and fork)
* ;- shell
* - debug 'find'
* - stacked file system infrastructure
* - TMP file system
* ;- stacked file system infrastructure
* ;- TMP file system
* ;- RAM service using a common quota pool
*/
@ -49,8 +49,7 @@
#include <terminal_io_channel.h>
#include <dummy_input_io_channel.h>
#include <pipe_io_channel.h>
#include <root_file_system.h>
#include <tar_file_system.h>
#include <dir_file_system.h>
enum { verbose_syscall = false };
@ -121,8 +120,8 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
case SYSCALL_STAT:
case SYSCALL_LSTAT: /* XXX implement difference between 'lstat' and 'stat' */
return _vfs->stat(_sysio, Absolute_path(_sysio->stat_in.path,
_env.pwd()).base());
return _root_dir->stat(_sysio, Absolute_path(_sysio->stat_in.path,
_env.pwd()).base());
case SYSCALL_FSTAT:
@ -130,28 +129,41 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
case SYSCALL_FCNTL:
if (_sysio->fcntl_in.cmd == Sysio::FCNTL_CMD_SET_FD_FLAGS) {
/* we assume that there is only the close-on-execve flag */
_lookup_channel(_sysio->fcntl_in.fd)->close_on_execve =
!!_sysio->fcntl_in.long_arg;
return true;
}
return _lookup_channel(_sysio->fcntl_in.fd)->fcntl(_sysio);
case SYSCALL_OPEN:
{
Absolute_path absolute_path(_sysio->open_in.path, _env.pwd());
PINF("open pwd=%s path=%s", _env.pwd(), _sysio->open_in.path);
/* remember mode only for debug output */
int const mode = _sysio->open_in.mode;
Vfs_handle *vfs_handle = _vfs->open(_sysio, absolute_path.base());
Vfs_handle *vfs_handle = _root_dir->open(_sysio, absolute_path.base());
if (!vfs_handle)
return false;
Shared_pointer<Io_channel> channel(new Vfs_io_channel(absolute_path.base(), _vfs, vfs_handle),
Genode::env()->heap());
char const *leaf_path = _root_dir->leaf_path(absolute_path.base());
/*
* File descriptors of opened directories are handled by
* '_root_dir'. In this case, we use the absolute path as leaf
* path because path operations always refer to the global
* root.
*/
if (vfs_handle->ds() == _root_dir)
leaf_path = absolute_path.base();
Shared_pointer<Io_channel>
channel(new Vfs_io_channel(absolute_path.base(),
leaf_path, _root_dir, vfs_handle),
Genode::env()->heap());
_sysio->open_out.fd = add_io_channel(channel);
PINF("open fd %d for \"%s\" with mode %o",
_sysio->open_out.fd, absolute_path.base(), mode);
return true;
}
@ -165,6 +177,10 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
return _lookup_channel(_sysio->ioctl_in.fd)->ioctl(_sysio);
case SYSCALL_LSEEK:
return _lookup_channel(_sysio->lseek_in.fd)->lseek(_sysio);
case SYSCALL_DIRENT:
return _lookup_channel(_sysio->dirent_in.fd)->dirent(_sysio);
@ -175,7 +191,7 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
case SYSCALL_EXECVE:
{
char const *filename = _sysio->execve_in.filename;
Absolute_path absolute_path(_sysio->execve_in.filename, _env.pwd());
/*
* Deserialize environment variable buffer into a
@ -208,11 +224,11 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
j += strlen(src);
}
Child *child = new Child(filename,
Child *child = new Child(absolute_path.base(),
parent(),
pid(),
_sig_rec,
_vfs,
_root_dir,
Args(_sysio->execve_in.args,
sizeof(_sysio->execve_in.args)),
env,
@ -350,7 +366,7 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
this,
new_pid,
_sig_rec,
_vfs,
_root_dir,
_args,
_env.env(),
_env.pwd(),
@ -425,6 +441,23 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
return true;
}
case SYSCALL_UNLINK:
return _root_dir->unlink(_sysio, Absolute_path(_sysio->unlink_in.path,
_env.pwd()).base());
case SYSCALL_RENAME:
return _root_dir->rename(_sysio, Absolute_path(_sysio->rename_in.from_path,
_env.pwd()).base(),
Absolute_path(_sysio->rename_in.to_path,
_env.pwd()).base());
case SYSCALL_MKDIR:
return _root_dir->mkdir(_sysio, Absolute_path(_sysio->mkdir_in.path,
_env.pwd()).base());
case SYSCALL_INVALID: break;
}
}
@ -568,15 +601,8 @@ int main(int argc, char **argv)
static Genode::Cap_connection cap;
/* initialize virtual file system */
static Vfs vfs;
try {
Genode::Xml_node fs = Genode::config()->xml_node().sub_node("fstab").sub_node();
for (; ; fs = fs.next()) {
if (fs.has_type("tar"))
vfs.add_file_system(new Tar_file_system(fs));
}
} catch (Genode::Xml_node::Nonexistent_sub_node) { }
static Dir_file_system
root_dir(config()->xml_node().sub_node("fstab"));
/*
* Entrypoint used to virtualize child resources such as RAM, RM
@ -591,7 +617,7 @@ int main(int argc, char **argv)
0,
pid_allocator()->alloc(),
&sig_rec,
&vfs,
&root_dir,
args_of_init_process(),
env_string_of_init_process(),
"/",

View File

@ -59,9 +59,10 @@ namespace Noux {
path[i] = 0;
}
static char const *last_element(char const *path)
template <typename T>
static T *last_element(T *path)
{
char const *result = path;
T *result = path;
for (; *path; path++)
if (path[0] == '/' && path[1] != 0)
result = path;
@ -199,10 +200,8 @@ namespace Noux {
_append_slash_if_needed();
_append(relative_path);
}
// PDBG("path: '%s'", _path);
}
_canonicalize();
// PDBG("canonical path: '%s'", _path);
}
public:
@ -231,6 +230,11 @@ namespace Noux {
*dst = 0;
}
void strip_last_element()
{
last_element(_path)[1] = 0;
}
bool equals(Path_base const &ref) const { return strcmp(ref._path, _path) == 0; }
bool equals(char const *str) const { return strcmp(str, _path) == 0; }
@ -269,7 +273,7 @@ namespace Noux {
return (num_slashes == 1) && !equals("/");
}
void append(char const *str) { _append(str); }
void append(char const *str) { _append(str); _canonicalize(); }
};

View File

@ -1,93 +0,0 @@
/*
* \brief Root file system
* \author Norman Feske
* \date 2011-02-17
*/
/*
* Copyright (C) 2011-2012 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.
*/
#ifndef _NOUX__ROOT_FILE_SYSTEM_H_
#define _NOUX__ROOT_FILE_SYSTEM_H_
/* Genode includes */
#include <base/printf.h>
#include <base/lock.h>
/* Noux includes */
#include <noux_session/sysio.h>
namespace Noux {
class Root_file_system : public File_system
{
private:
Lock _lock;
public:
Root_file_system() : File_system("/") { }
/*********************************
** Directory-service interface **
*********************************/
bool stat(Sysio *sysio, char const *path)
{
sysio->stat_out.st.size = 1234;
sysio->stat_out.st.mode = Sysio::STAT_MODE_DIRECTORY | 0755;
sysio->stat_out.st.uid = 13;
sysio->stat_out.st.gid = 14;
return true;
}
bool dirent(Sysio *sysio, char const *path)
{
Lock::Guard guard(_lock);
int const index = sysio->dirent_in.index;
if (index) {
sysio->dirent_out.entry.type = Sysio::DIRENT_TYPE_END;
return true;
}
sysio->dirent_out.entry.fileno = 13;
strncpy(sysio->dirent_out.entry.name, "test",
sizeof(sysio->dirent_out.entry.name));
sysio->dirent_out.entry.type = Sysio::DIRENT_TYPE_DIRECTORY;
return true;
}
Vfs_handle *open(Sysio *sysio, char const *path)
{
Lock::Guard guard(_lock);
if (strcmp(path, "/") == 0)
return new (env()->heap()) Vfs_handle(this, this, 0);
return 0;
}
void close(Vfs_handle *handle)
{
Lock::Guard guard(_lock);
destroy(env()->heap(), handle);
}
/********************************
** File I/O service interface **
********************************/
bool write(Sysio *sysio, Vfs_handle *handle) { return false; }
};
}
#endif /* _NOUX__ROOT_FILE_SYSTEM_H_ */

View File

@ -22,6 +22,8 @@
/* Noux includes */
#include <noux_session/sysio.h>
#include <file_system.h>
#include <vfs_handle.h>
namespace Noux {
@ -227,13 +229,43 @@ namespace Noux {
}
struct Num_dirent_cache
{
Lock lock;
Tar_file_system &tar_fs;
bool valid; /* true after first lookup */
char key[256]; /* key used for lookup */
size_t cached_num_dirent; /* cached value */
Num_dirent_cache(Tar_file_system &tar_fs)
: tar_fs(tar_fs), valid(false), cached_num_dirent(0) { }
size_t num_dirent(char const *path)
{
Lock::Guard guard(lock);
/* check for cache miss */
if (!valid || strcmp(path, key) != 0) {
Lookup_member_of_path lookup_criterion(path, ~0);
tar_fs._lookup(&lookup_criterion);
strncpy(key, path, sizeof(key));
cached_num_dirent = lookup_criterion.cnt;
valid = true;
}
return cached_num_dirent;
}
} _cached_num_dirent;
public:
Tar_file_system(Xml_node config)
:
File_system(config), _rom_name(config), _rom(_rom_name.name),
_rom_name(config), _rom(_rom_name.name),
_tar_base(env()->rm_session()->attach(_rom.dataspace())),
_tar_size(Dataspace_client(_rom.dataspace()).size())
_tar_size(Dataspace_client(_rom.dataspace()).size()),
_cached_num_dirent(*this)
{
PINF("tar archive '%s' local at %p, size is %zd",
_rom_name.name, _tar_base, _tar_size);
@ -284,7 +316,7 @@ namespace Noux {
return Dataspace_capability();
}
void release(Dataspace_capability ds_cap)
void release(char const *, Dataspace_capability ds_cap)
{
env()->ram_session()->free(static_cap_cast<Ram_dataspace>(ds_cap));
}
@ -315,12 +347,10 @@ namespace Noux {
return true;
}
bool dirent(Sysio *sysio, char const *path)
bool dirent(Sysio *sysio, char const *path, off_t index)
{
Lock::Guard guard(_lock);
int const index = sysio->dirent_in.index;
Lookup_member_of_path lookup_criterion(path, index);
Record *record = _lookup(&lookup_criterion);
if (!record) {
@ -345,16 +375,43 @@ namespace Noux {
absolute_path.base() + 1,
sizeof(sysio->dirent_out.entry.name));
PWRN("direntry in %s: %s", path, absolute_path.base() + 1);
return true;
}
bool unlink(Sysio *, char const *) { return false; }
bool rename(Sysio *, char const *, char const *) { return false; }
bool mkdir(Sysio *, char const *) { return false; }
size_t num_dirent(char const *path)
{
return _cached_num_dirent.num_dirent(path);
}
bool is_directory(char const *path)
{
Lookup_exact lookup_criterion(path);
Record const * record = _lookup(&lookup_criterion);
return record && (record->type() == Record::TYPE_DIR);
}
char const *leaf_path(char const *path)
{
/*
* Check if path exists within the file system. If this is the
* case, return the whole path, which is relative to the root
* of this file system.
*/
Lookup_exact lookup_criterion(path);
Record const * record = _lookup(&lookup_criterion);
return record ? path : 0;
}
Vfs_handle *open(Sysio *sysio, char const *path)
{
Lock::Guard guard(_lock);
PDBG("open %s", path);
Lookup_exact lookup_criterion(path);
Record *record = 0;
if ((record = _lookup(&lookup_criterion)))
@ -365,11 +422,12 @@ namespace Noux {
return 0;
}
void close(Vfs_handle *handle)
{
Lock::Guard guard(_lock);
destroy(env()->heap(), handle);
}
/***************************
** File_system interface **
***************************/
char const *name() const { return "tar"; }
/********************************

View File

@ -1,97 +0,0 @@
/*
* \brief Management of file systems within virtual directory tree
* \author Norman Feske
* \date 2011-02-17
*/
/*
* Copyright (C) 2011-2012 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.
*/
#ifndef _NOUX__VFS_H_
#define _NOUX__VFS_H_
/* Genode includes */
#include <util/list.h>
#include <dataspace/capability.h>
/* Noux includes */
#include <path.h>
#include <pwd.h>
#include <vfs_handle.h>
#include <file_system.h>
#include <noux_session/sysio.h>
namespace Noux {
class Vfs
{
private:
List<File_system> _file_systems;
public:
Dataspace_capability dataspace_from_file(char const *filename)
{
for (File_system *fs = _file_systems.first(); fs; fs = fs->next()) {
char const *fs_local_path = fs->local_path(filename);
Dataspace_capability ds_cap;
if (fs_local_path && (ds_cap = fs->dataspace(fs_local_path)).valid())
return ds_cap;
}
return Dataspace_capability();
}
void release_dataspace_for_file(char const *filename,
Dataspace_capability ds_cap)
{
for (File_system *fs = _file_systems.first(); fs; fs = fs->next()) {
char const *fs_local_path = fs->local_path(filename);
if (fs_local_path)
fs->release(ds_cap);
}
}
void add_file_system(File_system *file_system)
{
_file_systems.insert(file_system);
}
bool stat(Sysio *sysio, char const *path)
{
for (File_system *fs = _file_systems.first(); fs; fs = fs->next()) {
char const *fs_local_path = fs->local_path(path);
if (fs_local_path && fs->stat(sysio, fs_local_path))
return true;
}
sysio->error.stat = Sysio::STAT_ERR_NO_ENTRY;
return false;
}
Vfs_handle *open(Sysio *sysio, char const *path)
{
Vfs_handle *handle;
for (File_system *fs = _file_systems.first(); fs; fs = fs->next()) {
char const *fs_local_path = fs->local_path(path);
if (fs_local_path && (handle = fs->open(sysio, fs_local_path)))
return handle;
}
PWRN("no file system for \"%s\"", path);
sysio->error.open = Sysio::OPEN_ERR_UNACCESSIBLE;
return 0;
}
void close(Vfs_handle *handle)
{
handle->ds()->close(handle);
}
};
}
#endif /* _NOUX__VIRTUAL_DIRECTORY_SERVICE_H_ */

View File

@ -51,12 +51,17 @@ namespace Noux {
return Dataspace_capability();
}
void release(Dataspace_capability) { }
void release(char const *, Dataspace_capability) { }
bool stat(Sysio *, char const *) { return _msg("stat"); }
Vfs_handle *open(Sysio *, char const *) { _msg("open"); return 0; }
void close(Vfs_handle *) { _msg("close"); }
bool dirent(Sysio *, char const *) { return _msg("dirent"); }
bool stat(Sysio *, char const *) { return _msg("stat"); }
Vfs_handle *open(Sysio *, char const *) { _msg("open"); return 0; }
bool dirent(Sysio *, char const *, off_t) { return _msg("dirent"); }
bool unlink(Sysio *, char const *) { return _msg("unlink"); }
bool rename(Sysio *, char const *, char const *) { return _msg("rename"); }
bool mkdir(Sysio *, char const *) { return _msg("mkdir"); }
size_t num_dirent(char const *) { return 0; }
bool is_directory(char const *) { return false; }
char const *leaf_path(char const *path) { return 0; }
};
static Pseudo_directory_service ds;
return &ds;

View File

@ -17,23 +17,33 @@
/* Noux includes */
#include <io_channel.h>
#include <file_system.h>
#include <dir_file_system.h>
#include <pwd.h>
namespace Noux {
struct Vfs_io_channel : public Io_channel
{
Vfs *_vfs;
Vfs_handle *_fh;
Absolute_path _path;
Absolute_path _leaf_path;
Vfs_io_channel(char const *path, Vfs *vfs, Vfs_handle *vfs_handle)
: _vfs(vfs), _fh(vfs_handle), _path(path) { }
Vfs_io_channel(char const *path, char const *leaf_path,
Dir_file_system *root_dir, Vfs_handle *vfs_handle)
: _fh(vfs_handle), _path(path), _leaf_path(leaf_path) { }
~Vfs_io_channel() { _vfs->close(_fh); }
~Vfs_io_channel() { destroy(env()->heap(), _fh); }
bool write(Sysio *sysio) { return _fh->fs()->write(sysio, _fh); }
bool write(Sysio *sysio, size_t &count)
{
if (!_fh->fs()->write(sysio, _fh))
return false;
count = sysio->write_out.count;
_fh->_seek += count;
return true;
}
bool read(Sysio *sysio)
{
@ -44,16 +54,15 @@ namespace Noux {
return true;
}
bool fstat(Sysio *sysio) { return _fh->ds()->stat(sysio, _path.base()); }
bool fstat(Sysio *sysio)
{
return _fh->ds()->stat(sysio, _leaf_path.base());
}
bool fcntl(Sysio *sysio)
{
switch (sysio->fcntl_in.cmd) {
case Sysio::FCNTL_CMD_SET_FD_FLAGS:
return true;
case Sysio::FCNTL_CMD_GET_FILE_STATUS_FLAGS:
sysio->fcntl_out.result = _fh->status_flags();
@ -73,12 +82,18 @@ namespace Noux {
return true;
}
/*
* The 'dirent' function for the root directory only (the
* 'Dir_file_system::open()' function handles all requests referring
* to directories). Hence, '_path' is the absolute path of the
* directory to inspect.
*/
bool dirent(Sysio *sysio)
{
/*
* Return artificial dir entries for "." and ".."
*/
unsigned const index = sysio->dirent_in.index;
unsigned const index = _fh->seek() / sizeof(Sysio::Dirent);
if (index < 2) {
sysio->dirent_out.entry.type = Sysio::DIRENT_TYPE_DIRECTORY;
strncpy(sysio->dirent_out.entry.name,
@ -86,15 +101,46 @@ namespace Noux {
sizeof(sysio->dirent_out.entry.name));
sysio->dirent_out.entry.fileno = 1;
_fh->_seek += sizeof(Sysio::Dirent);
return true;
}
/*
* Delegate remaining dir-entry request to the actual file
* system.
* Delegate remaining dir-entry request to the actual file system.
* Align index range to zero when calling the directory service.
*/
sysio->dirent_in.index -= 2;
return _fh->ds()->dirent(sysio, _path.base());
if (!_fh->ds()->dirent(sysio, _path.base(), index - 2))
return false;
_fh->_seek += sizeof(Sysio::Dirent);
return true;
}
/**
* Return size of file that the I/O channel refers to
*
* Note that this function overwrites the 'sysio' argument. Do not
* call it prior saving all input arguments from the original sysio
* structure.
*/
size_t size(Sysio *sysio)
{
if (fstat(sysio))
return sysio->stat_out.st.size;
return 0;
}
bool lseek(Sysio *sysio)
{
switch (sysio->lseek_in.whence) {
case Sysio::LSEEK_SET: _fh->_seek = sysio->lseek_in.offset; break;
case Sysio::LSEEK_CUR: break;
case Sysio::LSEEK_END: _fh->_seek = size(sysio); break;
}
sysio->lseek_out.offset = _fh->_seek;
return true;
}
bool check_unblock(bool rd, bool wr, bool ex) const