diff --git a/ports/include/noux_session/noux_session.h b/ports/include/noux_session/noux_session.h index f51439d5db..caa46564ff 100644 --- a/ports/include/noux_session/noux_session.h +++ b/ports/include/noux_session/noux_session.h @@ -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; diff --git a/ports/include/noux_session/sysio.h b/ports/include/noux_session/sysio.h index da4be46513..543a6794c2 100644 --- a/ports/include/noux_session/sysio.h +++ b/ports/include/noux_session/sysio.h @@ -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; }, { }); }; }; }; diff --git a/ports/run/noux.run b/ports/run/noux.run index e99fcf3acb..8a9e411ba5 100644 --- a/ports/run/noux.run +++ b/ports/run/noux.run @@ -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> diff --git a/ports/run/noux_bash.run b/ports/run/noux_bash.run index d026e82cba..96dda1e06a 100644 --- a/ports/run/noux_bash.run +++ b/ports/run/noux_bash.run @@ -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 diff --git a/ports/run/noux_fork.run b/ports/run/noux_fork.run index 58f1e126cf..d783866944 100644 --- a/ports/run/noux_fork.run +++ b/ports/run/noux_fork.run @@ -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> diff --git a/ports/run/noux_vim.run b/ports/run/noux_vim.run index 351d72ceba..9c9eefbf44 100644 --- a/ports/run/noux_vim.run +++ b/ports/run/noux_vim.run @@ -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" /> diff --git a/ports/src/lib/libc_noux/plugin.cc b/ports/src/lib/libc_noux/plugin.cc index 910ea7479f..5b6cbdab39 100644 --- a/ports/src/lib/libc_noux/plugin.cc +++ b/ports/src/lib/libc_noux/plugin.cc @@ -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 */ diff --git a/ports/src/noux/child.h b/ports/src/noux/child.h index bf53a822e0..f3946d2ea5 100644 --- a/ports/src/noux/child.h +++ b/ports/src/noux/child.h @@ -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(); } diff --git a/ports/src/noux/dir_file_system.h b/ports/src/noux/dir_file_system.h new file mode 100644 index 0000000000..59eee3ca93 --- /dev/null +++ b/ports/src/noux/dir_file_system.h @@ -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_ */ diff --git a/ports/src/noux/directory_service.h b/ports/src/noux/directory_service.h index 64b808f810..62771340e4 100644 --- a/ports/src/noux/directory_service.h +++ b/ports/src/noux/directory_service.h @@ -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; }; } diff --git a/ports/src/noux/file_system.h b/ports/src/noux/file_system.h index 7ae9adb8f5..376a26bbfd 100644 --- a/ports/src/noux/file_system.h +++ b/ports/src/noux/file_system.h @@ -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; }; } diff --git a/ports/src/noux/fs_file_system.h b/ports/src/noux/fs_file_system.h new file mode 100644 index 0000000000..350ae769c6 --- /dev/null +++ b/ports/src/noux/fs_file_system.h @@ -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_ */ diff --git a/ports/src/noux/io_channel.h b/ports/src/noux/io_channel.h index 2ecec4cbe9..9f5b3e7bf2 100644 --- a/ports/src/noux/io_channel.h +++ b/ports/src/noux/io_channel.h @@ -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 diff --git a/ports/src/noux/main.cc b/ports/src/noux/main.cc index 9a387201f8..10b0888567 100644 --- a/ports/src/noux/main.cc +++ b/ports/src/noux/main.cc @@ -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(), "/", diff --git a/ports/src/noux/path.h b/ports/src/noux/path.h index af483ebcf0..dcd99d8b71 100644 --- a/ports/src/noux/path.h +++ b/ports/src/noux/path.h @@ -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(); } }; diff --git a/ports/src/noux/root_file_system.h b/ports/src/noux/root_file_system.h deleted file mode 100644 index 063f654fd9..0000000000 --- a/ports/src/noux/root_file_system.h +++ /dev/null @@ -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_ */ diff --git a/ports/src/noux/tar_file_system.h b/ports/src/noux/tar_file_system.h index 813dbac248..e50cc7e337 100644 --- a/ports/src/noux/tar_file_system.h +++ b/ports/src/noux/tar_file_system.h @@ -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"; } /******************************** diff --git a/ports/src/noux/vfs.h b/ports/src/noux/vfs.h deleted file mode 100644 index 0993805a7f..0000000000 --- a/ports/src/noux/vfs.h +++ /dev/null @@ -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_ */ diff --git a/ports/src/noux/vfs_handle.h b/ports/src/noux/vfs_handle.h index 3fab73ae92..3a3ec50a02 100644 --- a/ports/src/noux/vfs_handle.h +++ b/ports/src/noux/vfs_handle.h @@ -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; diff --git a/ports/src/noux/vfs_io_channel.h b/ports/src/noux/vfs_io_channel.h index 2204468336..2b8619fa49 100644 --- a/ports/src/noux/vfs_io_channel.h +++ b/ports/src/noux/vfs_io_channel.h @@ -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