From e9ac4b653bb71a574fe042df306d2b823347e6d0 Mon Sep 17 00:00:00 2001 From: Christian Prochaska Date: Mon, 8 Oct 2012 14:44:31 +0200 Subject: [PATCH] Add support for symbolic links This patch adds support for symbolic links in libc, libc plugins, file system servers and Noux. Fixes #322. --- libports/include/libc-plugin/fd_alloc.h | 23 ++ libports/include/libc-plugin/plugin.h | 14 +- .../include/libc-plugin/plugin_registry.h | 5 +- libports/lib/mk/libc.mk | 2 +- libports/run/libc_fs_tar_fs.run | 7 +- libports/src/lib/libc/dummies.cc | 5 - libports/src/lib/libc/fd_alloc.cc | 8 +- libports/src/lib/libc/file_operations.cc | 360 ++++++++++++++++-- libports/src/lib/libc/plugin.cc | 32 +- libports/src/lib/libc/plugin_registry.cc | 14 +- libports/src/lib/libc/readlink.cc | 33 -- libports/src/lib/libc_ffat/plugin.cc | 59 +-- libports/src/lib/libc_fs/plugin.cc | 200 +++++----- libports/src/lib/libc_rom/plugin.cc | 21 +- libports/src/lib/libc_terminal/plugin.cc | 12 +- libports/src/test/libc_ffat/main.cc | 39 +- libports/src/test/libc_fs_tar_fs/main.cc | 12 + os/include/os/path.h | 2 +- os/src/server/ram_fs/directory.h | 15 +- os/src/server/ram_fs/main.cc | 30 +- os/src/server/ram_fs/symlink.h | 16 +- os/src/server/tar_fs/main.cc | 42 +- os/src/server/tar_fs/symlink.h | 54 +++ ports/include/noux_session/client.h | 4 +- ports/include/noux_session/noux_session.h | 8 +- ports/include/noux_session/sysio.h | 16 +- ports/run/noux_gdb.run | 9 +- ports/src/lib/libc_noux/plugin.cc | 279 ++++++++------ ports/src/noux/child.h | 2 - ports/src/noux/dir_file_system.h | 75 ++++ ports/src/noux/directory_service.h | 12 +- ports/src/noux/environment.h | 24 +- ports/src/noux/fs_file_system.h | 207 +++++++--- ports/src/noux/io_channel.h | 2 - ports/src/noux/main.cc | 55 +-- ports/src/noux/net/net.cc | 4 +- ports/src/noux/net/socket_io_channel.h | 1 - ports/src/noux/null_file_system.h | 14 +- ports/src/noux/pwd.h | 42 -- ports/src/noux/shared_pointer.h | 1 - ports/src/noux/tar_file_system.h | 24 ++ ports/src/noux/terminal_file_system.h | 14 +- ports/src/noux/vfs_handle.h | 2 + ports/src/noux/vfs_io_channel.h | 17 - ports/src/noux/zero_file_system.h | 12 + 45 files changed, 1267 insertions(+), 562 deletions(-) delete mode 100644 libports/src/lib/libc/readlink.cc create mode 100644 os/src/server/tar_fs/symlink.h delete mode 100644 ports/src/noux/pwd.h diff --git a/libports/include/libc-plugin/fd_alloc.h b/libports/include/libc-plugin/fd_alloc.h index 008f3ec9ec..ac153ef3ef 100644 --- a/libports/include/libc-plugin/fd_alloc.h +++ b/libports/include/libc-plugin/fd_alloc.h @@ -16,6 +16,12 @@ #define _LIBC_PLUGIN__FD_ALLOC_H_ #include +#include +#include + +/* libc includes */ +#include +#include #include @@ -28,12 +34,29 @@ namespace Libc { */ class Plugin_context { }; + enum { ANY_FD = -1 }; struct File_descriptor { int libc_fd; + char *fd_path; /* for 'fchdir()' */ Plugin *plugin; Plugin_context *context; + + void path(char const *newpath) + { + if (newpath) { + size_t path_size = ::strlen(newpath) + 1; + fd_path = (char*)malloc(path_size); + if (!fd_path) { + PERR("could not allocate path buffer for libc_fd %d%s", + libc_fd, libc_fd == ANY_FD ? " (any)" : ""); + return; + } + ::memcpy(fd_path, newpath, path_size); + } else + fd_path = 0; + } }; diff --git a/libports/include/libc-plugin/plugin.h b/libports/include/libc-plugin/plugin.h index 476946a5d5..7663dc87c1 100644 --- a/libports/include/libc-plugin/plugin.h +++ b/libports/include/libc-plugin/plugin.h @@ -14,6 +14,7 @@ #ifndef _LIBC_PLUGIN__PLUGIN_H_ #define _LIBC_PLUGIN__PLUGIN_H_ +#include #include #include @@ -28,6 +29,8 @@ namespace Libc { class File_descriptor; + typedef Genode::Path Absolute_path; + class Plugin : public List::Element { protected: @@ -41,7 +44,8 @@ namespace Libc { virtual int priority(); - virtual bool supports_chdir(const char *path); + virtual bool supports_execve(char const *filename, char *const argv[], + char *const envp[]); virtual bool supports_mkdir(const char *path, mode_t mode); virtual bool supports_freeaddrinfo(struct ::addrinfo *res); virtual bool supports_getaddrinfo(const char *node, const char *service, @@ -49,6 +53,7 @@ namespace Libc { struct ::addrinfo **res); virtual bool supports_open(const char *pathname, int flags); virtual bool supports_pipe(); + virtual bool supports_readlink(const char *path, char *buf, size_t bufsiz); virtual bool supports_rename(const char *oldpath, const char *newpath); virtual bool supports_select(int nfds, fd_set *readfds, @@ -57,6 +62,7 @@ namespace Libc { struct timeval *timeout); virtual bool supports_socket(int domain, int type, int protocol); virtual bool supports_stat(const char *path); + virtual bool supports_symlink(const char *oldpath, const char *newpath); virtual bool supports_unlink(const char *path); virtual bool supports_mmap(); @@ -66,14 +72,14 @@ namespace Libc { virtual int bind(File_descriptor *, const struct ::sockaddr *addr, socklen_t addrlen); - virtual int chdir(const char *path); virtual int close(File_descriptor *fd); virtual int connect(File_descriptor *, const struct ::sockaddr *addr, socklen_t addrlen); virtual int dup2(File_descriptor *, File_descriptor *new_fd); + virtual int execve(char const *filename, char *const argv[], + char *const envp[]); virtual int fstatfs(File_descriptor *, struct statfs *buf); - virtual int fchdir(File_descriptor *); virtual int fcntl(File_descriptor *, int cmd, long arg); virtual void freeaddrinfo(struct ::addrinfo *res); virtual int fstat(File_descriptor *, struct stat *buf); @@ -103,6 +109,7 @@ namespace Libc { virtual File_descriptor *open(const char *pathname, int flags); virtual int pipe(File_descriptor *pipefd[2]); virtual ssize_t read(File_descriptor *, void *buf, ::size_t count); + virtual ssize_t readlink(const char *path, char *buf, ::size_t bufsiz); virtual ssize_t recv(File_descriptor *, void *buf, ::size_t len, int flags); virtual ssize_t recvfrom(File_descriptor *, void *buf, ::size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); @@ -121,6 +128,7 @@ namespace Libc { virtual int shutdown(File_descriptor *, int how); virtual File_descriptor *socket(int domain, int type, int protocol); virtual int stat(const char *path, struct stat *buf); + virtual int symlink(const char *oldpath, const char *newpath); virtual int unlink(const char *path); virtual ssize_t write(File_descriptor *, const void *buf, ::size_t count); }; diff --git a/libports/include/libc-plugin/plugin_registry.h b/libports/include/libc-plugin/plugin_registry.h index ba2fc7ba44..7096fd1817 100644 --- a/libports/include/libc-plugin/plugin_registry.h +++ b/libports/include/libc-plugin/plugin_registry.h @@ -25,7 +25,8 @@ namespace Libc { { public: - Plugin *get_plugin_for_chdir(const char *path); + Plugin *get_plugin_for_execve(char const *filename, char *const argv[], + char *const envp[]); Plugin *get_plugin_for_freeaddrinfo(struct addrinfo *res); Plugin *get_plugin_for_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, @@ -33,9 +34,11 @@ namespace Libc { Plugin *get_plugin_for_mkdir(const char *path, mode_t mode); Plugin *get_plugin_for_open(const char *pathname, int flags); Plugin *get_plugin_for_pipe(); + Plugin *get_plugin_for_readlink(const char *path, char *buf, size_t bufsiz); Plugin *get_plugin_for_rename(const char *oldpath, const char *newpath); Plugin *get_plugin_for_socket(int domain, int type, int protocol); Plugin *get_plugin_for_stat(const char *path, struct stat *); + Plugin *get_plugin_for_symlink(const char *oldpath, const char *newpath); Plugin *get_plugin_for_unlink(const char *path); }; diff --git a/libports/lib/mk/libc.mk b/libports/lib/mk/libc.mk index f357882bde..0e29dc0105 100644 --- a/libports/lib/mk/libc.mk +++ b/libports/lib/mk/libc.mk @@ -9,7 +9,7 @@ LIBS += timed_semaphore cxx # # Back end # -SRC_CC = atexit.cc dummies.cc rlimit.cc sysctl.cc readlink.cc \ +SRC_CC = atexit.cc dummies.cc rlimit.cc sysctl.cc \ issetugid.cc errno.cc gai_strerror.cc clock_gettime.cc \ gettimeofday.cc malloc.cc progname.cc fd_alloc.cc file_operations.cc \ plugin.cc plugin_registry.cc select.cc exit.cc environ.cc nanosleep.cc \ diff --git a/libports/run/libc_fs_tar_fs.run b/libports/run/libc_fs_tar_fs.run index 5b7498190d..2eb7a8cee7 100644 --- a/libports/run/libc_fs_tar_fs.run +++ b/libports/run/libc_fs_tar_fs.run @@ -68,7 +68,12 @@ install_config $config exec mkdir -p bin/libc_fs_tar_fs/testdir/testdir exec echo -n "a single line of text" > bin/libc_fs_tar_fs/testdir/testdir/test.tst -exec tar cfv bin/libc_fs_tar_fs.tar -h -C bin/libc_fs_tar_fs . +exec mkdir -p bin/libc_fs_tar_fs/testdir/a +exec mkdir -p bin/libc_fs_tar_fs/testdir/c +exec ln -sf /a bin/libc_fs_tar_fs/testdir/c/d +exec ln -sf /c bin/libc_fs_tar_fs/testdir/e +exec echo -n "a single line of text" > bin/libc_fs_tar_fs/testdir/a/b +exec tar cfv bin/libc_fs_tar_fs.tar -C bin/libc_fs_tar_fs . # # Boot modules diff --git a/libports/src/lib/libc/dummies.cc b/libports/src/lib/libc/dummies.cc index d9783cc05d..3abebfc9c8 100644 --- a/libports/src/lib/libc/dummies.cc +++ b/libports/src/lib/libc/dummies.cc @@ -37,8 +37,6 @@ DUMMY( 0, __default_hash) DUMMY(-1, _dup2) DUMMY(-1, dup2) DUMMY( 0, endpwent) -DUMMY(-1, _execve) -DUMMY(-1, execve) DUMMY( 0, fchmod) DUMMY(-1, fchown) DUMMY(-1, feholdexcept) @@ -52,7 +50,6 @@ DUMMY(-1, fpathconf) DUMMY(-1, freebsd7___semctl) DUMMY(-1, fstatat) DUMMY(-1, getcontext) -DUMMY( 0, __getcwd) DUMMY( 0, getdtablesize) DUMMY( 0, getegid) DUMMY( 0, geteuid) @@ -89,7 +86,6 @@ DUMMY(-1, ksem_trywait) DUMMY(-1, ksem_unlink) DUMMY(-1, ksem_wait) DUMMY(-1, link) -DUMMY(-1, lstat) DUMMY(-1, madvise) DUMMY(-1, mkfifo) DUMMY(-1, mknod) @@ -137,7 +133,6 @@ DUMMY(-1, sigsuspend) DUMMY(-1, socketpair) DUMMY(-1, stat) DUMMY(-1, statfs) -DUMMY(-1, symlink) DUMMY( 0, sync) DUMMY(-1, __test_sse) DUMMY(-1, truncate) diff --git a/libports/src/lib/libc/fd_alloc.cc b/libports/src/lib/libc/fd_alloc.cc index 6032566ca0..51d7aa88e3 100644 --- a/libports/src/lib/libc/fd_alloc.cc +++ b/libports/src/lib/libc/fd_alloc.cc @@ -40,10 +40,10 @@ File_descriptor_allocator::File_descriptor_allocator() } -File_descriptor *File_descriptor_allocator::alloc(Plugin *plugin, Plugin_context *context, int libc_fd) +File_descriptor *File_descriptor_allocator::alloc(Plugin *plugin, + Plugin_context *context, + int libc_fd) { - enum { ANY_FD = -1 }; - /* we use addresses returned by the allocator as file descriptors */ addr_t addr = (libc_fd == ANY_FD ? ANY_FD : libc_fd); @@ -62,6 +62,7 @@ File_descriptor *File_descriptor_allocator::alloc(Plugin *plugin, Plugin_context File_descriptor *fdo = metadata((void*)addr); fdo->libc_fd = (int)addr; + fdo->fd_path = 0; fdo->plugin = plugin; fdo->context = context; return fdo; @@ -70,6 +71,7 @@ File_descriptor *File_descriptor_allocator::alloc(Plugin *plugin, Plugin_context void File_descriptor_allocator::free(File_descriptor *fdo) { + ::free(fdo->fd_path); Allocator_avl_base::free(reinterpret_cast(fdo->libc_fd)); } diff --git a/libports/src/lib/libc/file_operations.cc b/libports/src/lib/libc/file_operations.cc index bece72decb..d72fa10c74 100644 --- a/libports/src/lib/libc/file_operations.cc +++ b/libports/src/lib/libc/file_operations.cc @@ -15,6 +15,8 @@ /* Genode includes */ #include #include +#include +#include /* Genode-specific libc interfaces */ #include @@ -23,8 +25,10 @@ /* libc includes */ #include +#include #include #include +#include #include #include @@ -40,6 +44,9 @@ using namespace Libc; #define PERR(...) #endif /* GENODE_RELEASE */ +static bool const verbose = false; +#define PDBGV(...) if (verbose) PDBG(__VA_ARGS__) + enum { INVALID_FD = -1 }; @@ -65,7 +72,6 @@ inline File_descriptor *libc_fd_to_fd(int libc_fd, const char *func_name) File_descriptor *fd = file_descriptor_allocator()->find_by_libc_fd(libc_fd); if (!fd) PERR("no plugin found for %s(%d)", func_name, libc_fd); - return fd; } @@ -74,11 +80,13 @@ inline File_descriptor *libc_fd_to_fd(int libc_fd, const char *func_name) * Generate body of wrapper function taking a file descriptor as first argument */ #define FD_FUNC_WRAPPER_GENERIC(result_stm, func_name, libc_fd, ...) \ - File_descriptor *fd = libc_fd_to_fd(libc_fd, #func_name); \ - if (!fd || !fd->plugin) \ - result_stm INVALID_FD; \ - else \ - result_stm fd->plugin->func_name(fd, ##__VA_ARGS__ ); +{ \ + File_descriptor *fd = libc_fd_to_fd(libc_fd, #func_name); \ + if (!fd || !fd->plugin) \ + result_stm INVALID_FD; \ + else \ + result_stm fd->plugin->func_name(fd, ##__VA_ARGS__ ); \ +} #define FD_FUNC_WRAPPER(func_name, libc_fd, ...) \ FD_FUNC_WRAPPER_GENERIC(return, func_name, libc_fd, ##__VA_ARGS__ ) @@ -86,13 +94,168 @@ inline File_descriptor *libc_fd_to_fd(int libc_fd, const char *func_name) /** * Generate body of wrapper function taking a path name as first argument */ +#define FNAME_FUNC_WRAPPER_GENERIC(result_stm, func_name, path, ...) \ +{ \ + Plugin *plugin = plugin_registry()->get_plugin_for_##func_name(path, ##__VA_ARGS__); \ + if (!plugin) { \ + PERR("no plugin found for %s(\"%s\")", #func_name, path); \ + errno = ENOSYS; \ + result_stm -1; \ + } else \ + result_stm plugin->func_name(path, ##__VA_ARGS__); \ +} + #define FNAME_FUNC_WRAPPER(func_name, path, ...) \ - Plugin *plugin = plugin_registry()->get_plugin_for_##func_name(path, ##__VA_ARGS__); \ - if (!plugin) { \ - PERR("no plugin found for %s(\"%s\")", #func_name, path); \ - return -1; \ - } \ - return plugin->func_name(path, ##__VA_ARGS__); + FNAME_FUNC_WRAPPER_GENERIC(return, func_name, path, ##__VA_ARGS__ ) + + +/** + * Current working directory + */ +static Absolute_path &cwd() +{ + static Absolute_path _cwd("/"); + return _cwd; +} + +/** + * path element token + */ + +struct Scanner_policy_path_element +{ + static bool identifier_char(char c, unsigned /* i */) + { + return (c != '/') && (c != 0); + } +}; + +typedef Genode::Token Path_element_token; + + +/** + * Resolve symbolic links in a given absolute path + */ + +/* exception */ +class Symlink_resolve_error { }; + +static void resolve_symlinks(char const *path, Absolute_path &resolved_path) +{ + PDBGV("path = %s", path); + + char path_element[PATH_MAX]; + char symlink_target[PATH_MAX]; + + Absolute_path current_iteration_working_path; + Absolute_path next_iteration_working_path(path, cwd().base()); + PDBGV("absolute_path = %s", next_iteration_working_path.base()); + + enum { FOLLOW_LIMIT = 10 }; + int follow_count = 0; + bool symlink_resolved_in_this_iteration; + do { + PDBGV("new iteration"); + if (follow_count++ == FOLLOW_LIMIT) { + errno = ELOOP; + throw Symlink_resolve_error(); + } + + current_iteration_working_path.import(next_iteration_working_path.base()); + PDBGV("current_iteration_working_path = %s", current_iteration_working_path.base()); + + next_iteration_working_path.import(""); + symlink_resolved_in_this_iteration = false; + + Path_element_token t(current_iteration_working_path.base()); + + while (t) { + if (t.type() != Path_element_token::IDENT) { + t = t.next(); + continue; + } + + t.string(path_element, sizeof(path_element)); + PDBGV("path_element = %s", path_element); + + try { + next_iteration_working_path.append("/"); + next_iteration_working_path.append(path_element); + } catch (Genode::Path_base::Path_too_long) { + errno = ENAMETOOLONG; + throw Symlink_resolve_error(); + } + + PDBGV("working_path_new = %s", next_iteration_working_path.base()); + + /* + * If a symlink has been resolved in this iteration, the remaining + * path elements get added and a new iteration starts. + */ + if (!symlink_resolved_in_this_iteration) { + struct stat stat_buf; + int res; + FNAME_FUNC_WRAPPER_GENERIC(res = , stat, next_iteration_working_path.base(), &stat_buf); + if (res == -1) { + PDBGV("stat() failed for %s", next_iteration_working_path.base()); + throw Symlink_resolve_error(); + } + if (S_ISLNK(stat_buf.st_mode)) { + PDBGV("found symlink: %s", next_iteration_working_path.base()); + FNAME_FUNC_WRAPPER_GENERIC(res = , readlink, + next_iteration_working_path.base(), + symlink_target, sizeof(symlink_target)); + if (res < 1) + throw Symlink_resolve_error(); + + if (symlink_target[0] == '/') + /* absolute target */ + next_iteration_working_path.import(symlink_target, cwd().base()); + else { + /* relative target */ + next_iteration_working_path.strip_last_element(); + try { + next_iteration_working_path.append(symlink_target); + } catch (Genode::Path_base::Path_too_long) { + errno = ENAMETOOLONG; + throw Symlink_resolve_error(); + } + } + PDBGV("resolved symlink to: %s", next_iteration_working_path.base()); + symlink_resolved_in_this_iteration = true; + } + } + + t = t.next(); + } + PDBGV("token end"); + + } while (symlink_resolved_in_this_iteration); + + resolved_path.import(next_iteration_working_path.base()); + PDBGV("resolved_path = %s", resolved_path.base()); +} + + +static void resolve_symlinks_except_last_element(char const *path, Absolute_path &resolved_path) +{ + PDBGV("path = %s", path); + + Absolute_path absolute_path_without_last_element(path, cwd().base()); + absolute_path_without_last_element.strip_last_element(); + + resolve_symlinks(absolute_path_without_last_element.base(), resolved_path); + + /* append last element to resolved path */ + Absolute_path absolute_path_last_element(path, cwd().base()); + absolute_path_last_element.keep_only_last_element(); + try { + resolved_path.append(absolute_path_last_element.base()); + } catch (Genode::Path_base::Path_too_long) { + errno = ENAMETOOLONG; + throw Symlink_resolve_error(); + } +} /******************** @@ -125,8 +288,17 @@ extern "C" int bind(int libc_fd, const struct sockaddr *addr, FD_FUNC_WRAPPER(bind, libc_fd, addr, addrlen); } -extern "C" int chdir(const char *path) { - FNAME_FUNC_WRAPPER(chdir, path) } +extern "C" int chdir(const char *path) +{ + struct stat stat_buf; + if ((stat(path, &stat_buf) == -1) || + (!S_ISDIR(stat_buf.st_mode))) { + errno = ENOTDIR; + return -1; + } + cwd().import(path, cwd().base()); + return 0; +} extern "C" int _close(int libc_fd) { @@ -150,7 +322,6 @@ extern "C" int _connect(int libc_fd, const struct sockaddr *addr, extern "C" int _dup2(int libc_fd, int new_libc_fd) { - File_descriptor *fd = libc_fd_to_fd(libc_fd, "dup2"); if (!fd || !fd->plugin) return INVALID_FD; @@ -163,7 +334,8 @@ extern "C" int _dup2(int libc_fd, int new_libc_fd) if (new_fd) close(new_libc_fd); - new_fd = file_descriptor_allocator()->alloc(fd->plugin, 0, new_libc_fd); + new_fd = file_descriptor_allocator()->alloc(fd->plugin, 0, new_libc_fd); + new_fd->path(fd->fd_path); /* new_fd->context must be assigned by the plugin implementing 'dup2' */ return fd->plugin->dup2(fd, new_fd); } @@ -175,8 +347,35 @@ extern "C" int dup2(int libc_fd, int new_libc_fd) } -extern "C" int fchdir(int libc_fd) { - FD_FUNC_WRAPPER(fchdir, libc_fd); } +extern "C" int _execve(char const *filename, char *const argv[], + char *const envp[]) +{ + try { + Absolute_path resolved_path; + resolve_symlinks(filename, resolved_path); + FNAME_FUNC_WRAPPER(execve, resolved_path.base(), argv, envp); + } catch (Symlink_resolve_error) { + return -1; + } +} + + +extern "C" int execve(char const *filename, char *const argv[], + char *const envp[]) +{ + return _execve(filename, argv, envp); +} + + +extern "C" int fchdir(int libc_fd) +{ + File_descriptor *fd = libc_fd_to_fd(libc_fd, "fchdir"); + + if (!fd) + return INVALID_FD; + + return chdir(fd->fd_path); +} extern "C" int fcntl(int libc_fd, int cmd, ...) @@ -295,8 +494,28 @@ extern "C" ::off_t lseek(int libc_fd, ::off_t offset, int whence) { FD_FUNC_WRAPPER(lseek, libc_fd, offset, whence); } -extern "C" int mkdir(const char *path, mode_t mode) { - FNAME_FUNC_WRAPPER(mkdir, path, mode) } +extern "C" int lstat(const char *path, struct stat *buf) +{ + try { + Absolute_path resolved_path; + resolve_symlinks_except_last_element(path, resolved_path); + FNAME_FUNC_WRAPPER(stat, resolved_path.base(), buf); + } catch (Symlink_resolve_error) { + return -1; + } +} + + +extern "C" int mkdir(const char *path, mode_t mode) +{ + try { + Absolute_path resolved_path; + resolve_symlinks_except_last_element(path, resolved_path); + FNAME_FUNC_WRAPPER(mkdir, resolved_path.base(), mode); + } catch(Symlink_resolve_error) { + return -1; + } +} extern "C" void *mmap(void *addr, ::size_t length, int prot, int flags, @@ -351,21 +570,47 @@ extern "C" int munmap(void *start, ::size_t length) extern "C" int _open(const char *pathname, int flags, ::mode_t mode) { + PDBGV("pathname = %s", pathname); + + Absolute_path resolved_path; + Plugin *plugin; File_descriptor *new_fdo; - plugin = plugin_registry()->get_plugin_for_open(pathname, flags); + try { + resolve_symlinks_except_last_element(pathname, resolved_path); + } catch (Symlink_resolve_error) { + return -1; + } + + if (!(flags & O_NOFOLLOW)) { + /* resolve last element */ + try { + resolve_symlinks(resolved_path.base(), resolved_path); + } catch (Symlink_resolve_error) { + if (errno == ENOENT) { + if (!(flags & O_CREAT)) + return -1; + } else + return -1; + } + } + + PDBGV("resolved path = %s", resolved_path.base()); + + plugin = plugin_registry()->get_plugin_for_open(resolved_path.base(), flags); if (!plugin) { PERR("no plugin found for open(\"%s\", int)", pathname, flags); return -1; } - new_fdo = plugin->open(pathname, flags); + new_fdo = plugin->open(resolved_path.base(), flags); if (!new_fdo) { PERR("plugin()->open(\"%s\") failed", pathname); return -1; } + new_fdo->path(resolved_path.base()); return new_fdo->libc_fd; } @@ -415,6 +660,18 @@ extern "C" ssize_t read(int libc_fd, void *buf, ::size_t count) } +extern "C" ssize_t readlink(const char *path, char *buf, size_t bufsiz) +{ + try { + Absolute_path resolved_path; + resolve_symlinks_except_last_element(path, resolved_path); + FNAME_FUNC_WRAPPER(readlink, resolved_path.base(), buf, bufsiz); + } catch(Symlink_resolve_error) { + return -1; + } +} + + extern "C" ssize_t recv(int libc_fd, void *buf, ::size_t len, int flags) { FD_FUNC_WRAPPER(recv, libc_fd, buf, len, flags); } @@ -435,8 +692,17 @@ extern "C" ssize_t recvmsg(int libc_fd, struct msghdr *msg, int flags) { FD_FUNC_WRAPPER(recvmsg, libc_fd, msg, flags); } -extern "C" int rename(const char *oldpath, const char *newpath) { - FNAME_FUNC_WRAPPER(rename, oldpath, newpath); } +extern "C" int rename(const char *oldpath, const char *newpath) +{ + try { + Absolute_path resolved_oldpath, resolved_newpath; + resolve_symlinks_except_last_element(oldpath, resolved_oldpath); + resolve_symlinks_except_last_element(newpath, resolved_newpath); + FNAME_FUNC_WRAPPER(rename, resolved_oldpath.base(), resolved_newpath.base()); + } catch(Symlink_resolve_error) { + return -1; + } +} extern "C" ssize_t send(int libc_fd, const void *buf, ::size_t len, int flags) { @@ -510,16 +776,54 @@ extern "C" int _socket(int domain, int type, int protocol) } -extern "C" int stat(const char *path, struct stat *buf) { - FNAME_FUNC_WRAPPER(stat, path, buf) } +extern "C" int stat(const char *path, struct stat *buf) +{ + PDBGV("path = %s", path); + try { + Absolute_path resolved_path; + resolve_symlinks(path, resolved_path); + FNAME_FUNC_WRAPPER(stat, resolved_path.base(), buf); + } catch(Symlink_resolve_error) { + return -1; + } +} -extern "C" int unlink(const char *path) { - FNAME_FUNC_WRAPPER(unlink, path) } +extern "C" int symlink(const char *oldpath, const char *newpath) +{ + try { + Absolute_path resolved_path; + resolve_symlinks_except_last_element(newpath, resolved_path); + FNAME_FUNC_WRAPPER(symlink, oldpath, resolved_path.base()); + } catch(Symlink_resolve_error) { + return -1; + } +} + + +extern "C" int unlink(const char *path) +{ + try { + Absolute_path resolved_path; + resolve_symlinks_except_last_element(path, resolved_path); + FNAME_FUNC_WRAPPER(unlink, resolved_path.base()); + } catch(Symlink_resolve_error) { + return -1; + } +} extern "C" ssize_t _write(int libc_fd, const void *buf, ::size_t count) { FD_FUNC_WRAPPER(write, libc_fd, buf, count); } + extern "C" ssize_t write(int libc_fd, const void *buf, ::size_t count) { return _write(libc_fd, buf, count); } + + +extern "C" int __getcwd(char *dst, ::size_t dst_size) +{ + Genode::strncpy(dst, cwd().base(), dst_size); + PDBGV("cwd = %s", dst); + return 0; +} diff --git a/libports/src/lib/libc/plugin.cc b/libports/src/lib/libc/plugin.cc index 5b99fbb79e..9642e5c6b8 100644 --- a/libports/src/lib/libc/plugin.cc +++ b/libports/src/lib/libc/plugin.cc @@ -42,7 +42,8 @@ int Plugin::priority() } -bool Plugin::supports_chdir(const char *path) +bool Plugin::supports_execve(char const *filename, char *const argv[], + char *const envp[]) { return false; } @@ -80,6 +81,12 @@ bool Plugin::supports_pipe() } +bool Plugin::supports_readlink(const char *path, char *buf, size_t bufsiz) +{ + return false; +} + + bool Plugin::supports_rename(const char *, const char *) { return false; @@ -105,6 +112,12 @@ bool Plugin::supports_stat(const char*) } +bool Plugin::supports_symlink(const char*, const char *) +{ + return false; +} + + bool Plugin::supports_unlink(const char*) { return false; @@ -117,19 +130,6 @@ bool Plugin::supports_mmap() } -/** - * Default implementations - */ - -int Plugin::chdir(const char *path) -{ - Libc::File_descriptor *fd = open(path, 0 /* no rights necessary */); - bool success = ((fd != NULL) - and (fchdir(fd) == 0) - and (close(fd) == 0)); - return success ? 0 : -1; -} - /** * Generate dummy member function of Plugin class */ @@ -157,7 +157,6 @@ DUMMY(int, -1, close, (File_descriptor *)); DUMMY(int, -1, connect, (File_descriptor *, const struct sockaddr *, socklen_t)); DUMMY(int, -1, dup2, (File_descriptor *, File_descriptor *new_fd)); DUMMY(int, -1, fstatfs, (File_descriptor *, struct statfs *)); -DUMMY(int, -1, fchdir, (File_descriptor *)); DUMMY(int, -1, fcntl, (File_descriptor *, int cmd, long arg)); DUMMY(int, -1, fstat, (File_descriptor *, struct stat *)); DUMMY(int, -1, fsync, (File_descriptor *)); @@ -183,6 +182,7 @@ DUMMY(ssize_t, -1, write, (File_descriptor *, const void *, ::size_t)); /* * Misc */ +DUMMY(int, -1, execve, (char const *, char *const[], char *const[])); DUMMY(void, , freeaddrinfo, (struct ::addrinfo *)); DUMMY(int, -1, getaddrinfo, (const char *, const char *, const struct ::addrinfo *, struct ::addrinfo **)); DUMMY(int, -1, mkdir, (const char*, mode_t)); @@ -190,7 +190,9 @@ DUMMY(void *, (void *)(-1), mmap, (void *addr, ::size_t length, int prot, int fl File_descriptor *, ::off_t offset)); DUMMY(int, -1, munmap, (void *, ::size_t)); DUMMY(int, -1, pipe, (File_descriptor*[2])); +DUMMY(ssize_t, -1, readlink, (const char *, char *, size_t)); DUMMY(int, -1, rename, (const char *, const char *)); DUMMY(int, -1, select, (int, fd_set *, fd_set *, fd_set *, struct timeval *)); DUMMY(int, -1, stat, (const char*, struct stat*)); +DUMMY(int, -1, symlink, (const char*, const char*)); DUMMY(int, -1, unlink, (const char*)); diff --git a/libports/src/lib/libc/plugin_registry.cc b/libports/src/lib/libc/plugin_registry.cc index 79a46e7b08..eea97e46e0 100644 --- a/libports/src/lib/libc/plugin_registry.cc +++ b/libports/src/lib/libc/plugin_registry.cc @@ -37,9 +37,9 @@ using namespace Libc; } \ return result; - -Plugin *Plugin_registry::get_plugin_for_chdir(const char *path) { - GET_PLUGIN_FOR(chdir, path) } +Plugin *Plugin_registry::get_plugin_for_execve(char const *filename, char *const argv[], + char *const envp[]) { + GET_PLUGIN_FOR(execve, filename, argv, envp) } Plugin *Plugin_registry::get_plugin_for_freeaddrinfo(struct addrinfo *res) { @@ -64,6 +64,10 @@ Plugin *Plugin_registry::get_plugin_for_pipe() { GET_PLUGIN_FOR(pipe) } +Plugin *Plugin_registry::get_plugin_for_readlink(const char *path, char *buf, size_t bufsiz) { + GET_PLUGIN_FOR(readlink, path, buf, bufsiz) } + + Plugin *Plugin_registry::get_plugin_for_rename(const char *oldpath, const char *newpath) { GET_PLUGIN_FOR(rename, oldpath, newpath) } @@ -76,5 +80,9 @@ Plugin *Plugin_registry::get_plugin_for_stat(const char *path, struct stat *) { GET_PLUGIN_FOR(stat, path) } +Plugin *Plugin_registry::get_plugin_for_symlink(const char *oldpath, const char *newpath) { + GET_PLUGIN_FOR(symlink, oldpath, newpath) } + + Plugin *Plugin_registry::get_plugin_for_unlink(const char *path) { GET_PLUGIN_FOR(unlink, path) } diff --git a/libports/src/lib/libc/readlink.cc b/libports/src/lib/libc/readlink.cc deleted file mode 100644 index e6ef9dd9db..0000000000 --- a/libports/src/lib/libc/readlink.cc +++ /dev/null @@ -1,33 +0,0 @@ -/* - * \brief C-library back end - * \author Norman Feske - * \date 2008-11-11 - */ - -/* - * Copyright (C) 2008-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. - */ - -#include -#include -#include - -#include "libc_debug.h" - -extern "C" ssize_t readlink(const char *path, char *buf, size_t bufsiz) -{ - /* - * During malloc, readlink is called with the path argument "/etc/malloc.conf" - */ - if (strcmp("/etc/malloc.conf", path) == 0) - return -1; - - raw_write_str("readlink called path=\n"); - raw_write_str(path); - raw_write_str("\n"); - return -1; -} - diff --git a/libports/src/lib/libc_ffat/plugin.cc b/libports/src/lib/libc_ffat/plugin.cc index 280f158514..e21fd0bca4 100644 --- a/libports/src/lib/libc_ffat/plugin.cc +++ b/libports/src/lib/libc_ffat/plugin.cc @@ -14,6 +14,7 @@ /* Genode includes */ #include #include +#include /* libc includes */ #include @@ -131,7 +132,6 @@ class Plugin : public Libc::Plugin File_plugin_context *file_plugin_context = dynamic_cast(context(fd)); if (!file_plugin_context) { - PERR("_get_ffat_file() called for a directory"); return 0; } return file_plugin_context->ffat_file(); @@ -142,7 +142,6 @@ class Plugin : public Libc::Plugin Directory_plugin_context *directory_plugin_context = dynamic_cast(context(fd)); if (!directory_plugin_context) { - PERR("_get_ffat_dir() called for a regular file"); return 0; } return directory_plugin_context->ffat_dir(); @@ -174,13 +173,6 @@ class Plugin : public Libc::Plugin * TODO: decide if the file named shall be handled by this plugin */ - bool supports_chdir(const char *path) - { - if (verbose) - PDBG("path = %s", path); - return true; - } - bool supports_mkdir(const char *path, mode_t) { if (verbose) @@ -216,39 +208,20 @@ class Plugin : public Libc::Plugin return true; } - int chdir(const char *path) - { - using namespace Ffat; - - FRESULT res = f_chdir(path); - - switch(res) { - case FR_OK: - return 0; - case FR_NO_PATH: - case FR_INVALID_NAME: - case FR_INVALID_DRIVE: - errno = ENOENT; - return -1; - case FR_NOT_READY: - case FR_DISK_ERR: - case FR_INT_ERR: - case FR_NOT_ENABLED: - case FR_NO_FILESYSTEM: - errno = EIO; - return -1; - default: - /* not supposed to occur according to the libffat documentation */ - PERR("f_chdir() returned an unexpected error code"); - return -1; - } - } - int close(Libc::File_descriptor *fd) { using namespace Ffat; - FRESULT res = f_close(_get_ffat_file(fd)); + FIL *ffat_file = _get_ffat_file(fd); + + if (!ffat_file){ + /* directory */ + Genode::destroy(Genode::env()->heap(), context(fd)); + Libc::file_descriptor_allocator()->free(fd); + return 0; + } + + FRESULT res = f_close(ffat_file); Genode::destroy(Genode::env()->heap(), context(fd)); Libc::file_descriptor_allocator()->free(fd); @@ -646,6 +619,14 @@ class Plugin : public Libc::Plugin file_info.lfname = 0; file_info.lfsize = 0; + ::memset(buf, 0, sizeof(struct stat)); + + /* 'f_stat()' does not work for the '/' directory */ + if (strcmp(path, "/") == 0) { + buf->st_mode |= S_IFDIR; + return 0; + } + FRESULT res = f_stat(path, &file_info); switch(res) { @@ -670,8 +651,6 @@ class Plugin : public Libc::Plugin return -1; } - ::memset(buf, 0, sizeof(struct stat)); - /* convert FILINFO to struct stat */ buf->st_size = file_info.fsize; diff --git a/libports/src/lib/libc_fs/plugin.cc b/libports/src/lib/libc_fs/plugin.cc index 9bfc2fe58f..488d130403 100644 --- a/libports/src/lib/libc_fs/plugin.cc +++ b/libports/src/lib/libc_fs/plugin.cc @@ -15,6 +15,7 @@ #include #include #include +#include /* libc includes */ #include @@ -43,44 +44,7 @@ namespace { enum { PATH_MAX_LEN = 256 }; - -/** - * Current working directory - */ -struct Cwd -{ - char path[PATH_MAX_LEN]; - - Cwd() { Genode::strncpy(path, "/", sizeof(path)); } -}; - - -static Cwd *cwd() -{ - static Cwd cwd_inst; - return &cwd_inst; -} - - -struct Canonical_path -{ - char str[PATH_MAX_LEN]; - - Canonical_path(char const *pathname) - { - /* - * If pathname is a relative path, prepend the current working - * directory. - * - * XXX we might consider using Noux' 'Path' class here - */ - if (pathname[0] != '/') { - snprintf(str, sizeof(str), "%s/%s", cwd()->path, pathname); - } else { - strncpy(str, pathname, sizeof(str)); - } - } -}; +typedef Genode::Path Canonical_path; static File_system::Session *file_system() @@ -252,13 +216,6 @@ class Plugin : public Libc::Plugin ~Plugin() { } - bool supports_chdir(const char *path) - { - if (verbose) - PDBG("path = %s", path); - return true; - } - bool supports_mkdir(const char *path, mode_t) { if (verbose) @@ -273,6 +230,13 @@ class Plugin : public Libc::Plugin return true; } + bool supports_readlink(const char *path, char *, size_t) + { + if (verbose) + PDBG("path = %s", path); + return true; + } + bool supports_rename(const char *oldpath, const char *newpath) { if (verbose) @@ -287,6 +251,13 @@ class Plugin : public Libc::Plugin return true; } + bool supports_symlink(const char *oldpath, const char *newpath) + { + if (verbose) + PDBG("oldpath = %s, newpath = %s", oldpath, newpath); + return true; + } + bool supports_unlink(const char *path) { if (verbose) @@ -296,25 +267,6 @@ class Plugin : public Libc::Plugin bool supports_mmap() { return true; } - int chdir(const char *path) - { - if (*path != '/') { - PERR("chdir: relative path names not yet supported"); - errno = ENOENT; - return -1; - } - - Genode::strncpy(cwd()->path, path, sizeof(cwd()->path)); - - /* strip trailing slash if needed */ - char *s = cwd()->path; - for (; s[0] && s[1]; s++); - if (s[0] == '/') - s[0] = 0; - - return 0; - } - int close(Libc::File_descriptor *fd) { /* wait for the completion of all operations of the context */ @@ -468,7 +420,7 @@ class Plugin : public Libc::Plugin try { File_system::Dir_handle const handle = - file_system()->dir(canonical_path.str, true); + file_system()->dir(canonical_path.base(), true); file_system()->close(handle); return 0; } @@ -498,9 +450,9 @@ class Plugin : public Libc::Plugin */ try { if (verbose) - PDBG("open dir '%s'", path.str); + PDBG("open dir '%s'", path.base()); File_system::Dir_handle const handle = - file_system()->dir(path.str, false); + file_system()->dir(path.base(), false); Plugin_context *context = new (Genode::env()->heap()) Plugin_context(handle); @@ -511,27 +463,18 @@ class Plugin : public Libc::Plugin /* * Determine directory path that contains the node to open */ - unsigned last_slash = 0; - for (unsigned i = 0; path.str[i]; i++) - if (path.str[i] == '/') - last_slash = i; + Canonical_path dir_path(pathname); + dir_path.strip_last_element(); - char dir_path[256] = "/"; - if (last_slash > 0) - Genode::strncpy(dir_path, path.str, - Genode::min(sizeof(dir_path), last_slash + 1)); - - /* - * Determine base name - */ - char const *basename = path.str + last_slash + 1; + Canonical_path basename(pathname); + basename.keep_only_last_element(); try { /* * Open directory that contains the file to be opened/created */ File_system::Dir_handle const dir_handle = - file_system()->dir(dir_path, false); + file_system()->dir(dir_path.base(), false); Node_handle_guard guard(dir_handle); @@ -545,14 +488,14 @@ class Plugin : public Libc::Plugin bool opened = false; while (!opened) { try { - handle = file_system()->file(dir_handle, basename, mode, create); + handle = file_system()->file(dir_handle, basename.base() + 1, mode, create); opened = true; } catch (File_system::Node_already_exists) { if (flags & O_EXCL) throw File_system::Node_already_exists(); /* try to open the existing file */ try { - handle = file_system()->file(dir_handle, basename, mode, false); + handle = file_system()->file(dir_handle, basename.base() + 1, mode, false); opened = true; } catch (File_system::Lookup_failed) { /* the file got deleted in the meantime */ @@ -649,27 +592,114 @@ class Plugin : public Libc::Plugin return count - remaining_count; } + ssize_t readlink(const char *path, char *buf, size_t bufsiz) + { + if (verbose) + PDBG("path = %s, bufsiz = %zu", path, bufsiz); + + Canonical_path abs_path(path); + abs_path.strip_last_element(); + + Canonical_path symlink_name(path); + symlink_name.keep_only_last_element(); + + try { + ::File_system::Dir_handle dir_handle = file_system()->dir(abs_path.base(), false); + + ::File_system::Symlink_handle symlink_handle = + file_system()->symlink(dir_handle, symlink_name.base() + 1, false); + + if (symlink_handle.value == -1) { + errno = ENOSYS; + return -1; + } + + Plugin_context *context = new (Genode::env()->heap()) + Plugin_context(symlink_handle); + + Libc::File_descriptor *fd = Libc::file_descriptor_allocator()->alloc(this, context); + + ssize_t result = read(fd, buf, bufsiz); + if (verbose) + PDBG("result = %zd, buf = %s", result, buf); + close(fd); + + return result; + } catch (...) { } + + errno = ENOENT; + return -1; + } + int stat(const char *pathname, struct stat *buf) { if (verbose) PDBG("stat %s", pathname); + Canonical_path path(pathname); try { File_system::Node_handle const node_handle = - file_system()->node(path.str); + file_system()->node(path.base()); Node_handle_guard guard(node_handle); obtain_stat_for_node(node_handle, buf); return 0; } catch (File_system::Lookup_failed) { - PERR("lookup failed"); + PERR("stat(%s): lookup failed", pathname); errno = ENOENT; } return -1; } + int symlink(const char *oldpath, const char *newpath) + { + Canonical_path abs_path(newpath); + abs_path.strip_last_element(); + + Canonical_path symlink_name(newpath); + symlink_name.keep_only_last_element(); + + try { + /* + * Open directory that contains the file to be opened/created + */ + File_system::Dir_handle const dir_handle = + file_system()->dir(abs_path.base(), false); + + Node_handle_guard guard(dir_handle); + + File_system::Symlink_handle symlink_handle = + file_system()->symlink(dir_handle, symlink_name.base() + 1, true); + + if (symlink_handle.value == -1) { + errno = ENOSYS; + return -1; + } + + Plugin_context *context = new (Genode::env()->heap()) + Plugin_context(symlink_handle); + + Libc::File_descriptor *fd = + Libc::file_descriptor_allocator()->alloc(this, context); + + if (write(fd, oldpath, strlen(oldpath) + 1) == -1) { + errno = EIO; + return -1; + } + + close(fd); + + return 0; + } + catch (File_system::Lookup_failed) { + PERR("symlink(%s) lookup failed", newpath); } + + errno = ENOENT; + return -1; + } + int unlink(const char *path) { return -1; diff --git a/libports/src/lib/libc_rom/plugin.cc b/libports/src/lib/libc_rom/plugin.cc index 6be47eeb23..14736d2408 100644 --- a/libports/src/lib/libc_rom/plugin.cc +++ b/libports/src/lib/libc_rom/plugin.cc @@ -77,13 +77,18 @@ namespace { bool supports_open(const char *path, int flags) { - return _probe_rom(path); + return _probe_rom(&path[1]); + } + + bool supports_stat(const char *path) + { + return _probe_rom(&path[1]); } Libc::File_descriptor *open(const char *pathname, int flags) { Plugin_context *context = new (Genode::env()->heap()) - Plugin_context(pathname); + Plugin_context(&pathname[1]); return Libc::file_descriptor_allocator()->alloc(this, context); } @@ -94,6 +99,16 @@ namespace { return 0; } + int stat(const char *path, struct stat *buf) + { + Genode::Rom_connection rom(&path[1]); + + memset(buf, 0, sizeof(struct stat)); + buf->st_mode = S_IFREG; + buf->st_size = Genode::Dataspace_client(rom.dataspace()).size(); + return 0; + } + ssize_t read(Libc::File_descriptor *fd, void *buf, ::size_t count) { Plugin_context *rom = context(fd); @@ -140,7 +155,7 @@ namespace { case SEEK_SET: - if (offset > rom->size()) { + if (offset > (::off_t)rom->size()) { errno = EINVAL; return (::off_t)(-1); } diff --git a/libports/src/lib/libc_terminal/plugin.cc b/libports/src/lib/libc_terminal/plugin.cc index 6811e86647..33dea5eef0 100644 --- a/libports/src/lib/libc_terminal/plugin.cc +++ b/libports/src/lib/libc_terminal/plugin.cc @@ -133,7 +133,8 @@ namespace { bool supports_stat(const char *path) { - return (Genode::strcmp(path, _dev_name()) == 0); + return (Genode::strcmp(path, "/dev") == 0) || + (Genode::strcmp(path, _dev_name()) == 0); } bool supports_open(const char *path, int flags) @@ -164,7 +165,14 @@ namespace { */ if (buf) { Genode::memset(buf, 0, sizeof(struct stat)); - buf->st_mode = S_IFCHR; + if (Genode::strcmp(path, "/dev") == 0) + buf->st_mode = S_IFDIR; + else if (Genode::strcmp(path, _dev_name()) == 0) + buf->st_mode = S_IFCHR; + else { + errno = ENOENT; + return -1; + } } return 0; } diff --git a/libports/src/test/libc_ffat/main.cc b/libports/src/test/libc_ffat/main.cc index 424ddd9cf1..56f71315c8 100644 --- a/libports/src/test/libc_ffat/main.cc +++ b/libports/src/test/libc_ffat/main.cc @@ -101,13 +101,13 @@ int main(int argc, char *argv[]) } /* test 'pread()' and 'pwrite()' */ - CALL_AND_CHECK(fd, open(file_name2, O_CREAT | O_WRONLY), fd >= 0, "file_name=%s", file_name); + CALL_AND_CHECK(fd, open(file_name2, O_CREAT | O_WRONLY), fd >= 0, "file_name=%s", file_name2); /* write "a single line of" */ CALL_AND_CHECK(count, pwrite(fd, pattern, (pattern_size - 6), 0), (size_t)count == (pattern_size - 6), ""); /* write "line of text" at offset 9 */ CALL_AND_CHECK(count, pwrite(fd, &pattern[9], (pattern_size - 9), 9), (size_t)count == (pattern_size - 9), ""); CALL_AND_CHECK(ret, close(fd), ret == 0, ""); - CALL_AND_CHECK(fd, open(file_name2, O_RDONLY), fd >= 0, "file_name=%s", file_name); + CALL_AND_CHECK(fd, open(file_name2, O_RDONLY), fd >= 0, "file_name=%s", file_name2); memset(buf, 0, sizeof(buf)); /* read "single line of text" from offset 2 */ CALL_AND_CHECK(count, pread(fd, buf, sizeof(buf), 2), (size_t)count == (pattern_size - 2), ""); @@ -121,7 +121,7 @@ int main(int argc, char *argv[]) } /* test 'readv()' and 'writev()' */ - CALL_AND_CHECK(fd, open(file_name3, O_CREAT | O_WRONLY), fd >= 0, "file_name=%s", file_name); + CALL_AND_CHECK(fd, open(file_name3, O_CREAT | O_WRONLY), fd >= 0, "file_name=%s", file_name3); struct iovec iov[2]; /* write "a single line" */ iov[0].iov_base = (void*)pattern; @@ -131,7 +131,7 @@ int main(int argc, char *argv[]) iov[1].iov_len = pattern_size - 8; CALL_AND_CHECK(count, writev(fd, iov, 2), (size_t)count == (pattern_size + 5), ""); CALL_AND_CHECK(ret, close(fd), ret == 0, ""); - CALL_AND_CHECK(fd, open(file_name3, O_RDONLY), fd >= 0, "file_name=%s", file_name); + CALL_AND_CHECK(fd, open(file_name3, O_RDONLY), fd >= 0, "file_name=%s", file_name3); memset(buf, 0, sizeof(buf)); /* read "a single line" */ iov[0].iov_base = buf; @@ -187,6 +187,37 @@ int main(int argc, char *argv[]) (ret == 0) && (stat_buf.st_size == 0), "file_name=%s", file_name4); + /* test 'fchdir()' */ + CALL_AND_CHECK(ret, chdir("/"), ret == 0, ""); + CALL_AND_CHECK(fd, open(dir_name, O_RDONLY), fd >= 0, "dir_name=%s", dir_name); + CALL_AND_CHECK(ret, fchdir(fd), ret == 0, ""); + CALL_AND_CHECK(ret, close(fd), ret == 0, ""); + CALL_AND_CHECK(ret, stat(file_name, &stat_buf), ret == 0, "file_name=%s", file_name); + + /* test symbolic links */ + if ((symlink("/", "/symlinks_supported") == 0) || (errno != ENOSYS)) { + CALL_AND_CHECK(ret, mkdir("/a", 0777), ((ret == 0) || (errno == EEXIST)), "dir_name=%s", "/a"); + CALL_AND_CHECK(ret, mkdir("/c", 0777), ((ret == 0) || (errno == EEXIST)), "dir_name=%s", "/c"); + CALL_AND_CHECK(ret, symlink("/a", "/c/d"), + ((ret == 0) || (errno == EEXIST)), "dir_name=%s", "/c/d"); + CALL_AND_CHECK(ret, symlink("/c", "/e"), ((ret == 0) || (errno == EEXIST)), "dir_name=%s", "/e"); + + CALL_AND_CHECK(fd, open("/a/b", O_CREAT | O_WRONLY), fd >= 0, "file_name=%s", "/a/b"); + CALL_AND_CHECK(count, write(fd, pattern, pattern_size), (size_t)count == pattern_size, ""); + CALL_AND_CHECK(ret, close(fd), ret == 0, ""); + + CALL_AND_CHECK(fd, open("/e/d/b", O_RDONLY), fd >= 0, "file_name=%s", "/e/d/b"); + CALL_AND_CHECK(count, read(fd, buf, sizeof(buf)), (size_t)count == pattern_size, ""); + CALL_AND_CHECK(ret, close(fd), ret == 0, ""); + printf("content of file: \"%s\"\n", buf); + if (strcmp(buf, pattern) != 0) { + printf("unexpected content of file\n"); + return -1; + } else { + printf("file content is correct\n"); + } + } + if (i < (iterations - 1)) sleep(2); } diff --git a/libports/src/test/libc_fs_tar_fs/main.cc b/libports/src/test/libc_fs_tar_fs/main.cc index 04716a4674..b5a083b6bb 100644 --- a/libports/src/test/libc_fs_tar_fs/main.cc +++ b/libports/src/test/libc_fs_tar_fs/main.cc @@ -98,6 +98,18 @@ int main(int argc, char *argv[]) } } + /* test symbolic links */ + CALL_AND_CHECK(fd, open("/e/d/b", O_RDONLY), fd >= 0, "file_name=%s", "/e/d/b"); + CALL_AND_CHECK(count, read(fd, buf, sizeof(buf)), (size_t)count == pattern_size, ""); + CALL_AND_CHECK(ret, close(fd), ret == 0, ""); + printf("content of file: \"%s\"\n", buf); + if (strcmp(buf, pattern) != 0) { + printf("unexpected content of file\n"); + return -1; + } else { + printf("file content is correct\n"); + } + if (i < (iterations - 1)) sleep(2); } diff --git a/os/include/os/path.h b/os/include/os/path.h index b9ba16392b..7fdc57bb40 100644 --- a/os/include/os/path.h +++ b/os/include/os/path.h @@ -227,7 +227,7 @@ namespace Genode { _import(path, pwd); } - void import(char const *path) { _import(path); } + void import(char const *path, char const *pwd = 0) { _import(path, pwd); } char *base() { return _path; } size_t max_len() { return _path_max_len; } diff --git a/os/src/server/ram_fs/directory.h b/os/src/server/ram_fs/directory.h index 592edcbfdd..1e9afea327 100644 --- a/os/src/server/ram_fs/directory.h +++ b/os/src/server/ram_fs/directory.h @@ -83,7 +83,8 @@ namespace File_system { /* try to find entry that matches the first path element */ Node *sub_node = _entries.first(); for (; sub_node; sub_node = sub_node->next()) - if (strcmp(sub_node->name(), path, i) == 0) + if ((strlen(sub_node->name()) == i) && + (strcmp(sub_node->name(), path, i) == 0)) break; if (!sub_node) @@ -141,6 +142,18 @@ namespace File_system { throw Lookup_failed(); } + Symlink *lookup_and_lock_symlink(char const *path) + { + Node *node = lookup_and_lock(path); + + Symlink *symlink = dynamic_cast(node); + if (symlink) + return symlink; + + node->unlock(); + throw Lookup_failed(); + } + /** * Lookup parent directory of the specified path * diff --git a/os/src/server/ram_fs/main.cc b/os/src/server/ram_fs/main.cc index 415ca32448..8162887e33 100644 --- a/os/src/server/ram_fs/main.cc +++ b/os/src/server/ram_fs/main.cc @@ -261,9 +261,34 @@ namespace File_system { return _handle_registry.alloc(file); } - Symlink_handle symlink(Dir_handle, Name const &name, bool create) + Symlink_handle symlink(Dir_handle dir_handle, Name const &name, bool create) { - return Symlink_handle(-1); + if (!valid_name(name.string())) + throw Invalid_name(); + + Directory *dir = _handle_registry.lookup_and_lock(dir_handle); + Node_lock_guard dir_guard(*dir); + + if (create) { + + if (!_writable) + throw Permission_denied(); + + if (dir->has_sub_node_unsynchronized(name.string())) + throw Node_already_exists(); + + try { + Symlink * const symlink = new (env()->heap()) + Symlink(name.string()); + + dir->adopt_unsynchronized(symlink); + } + catch (Allocator::Out_of_memory) { throw No_space(); } + } + + Symlink *symlink = dir->lookup_and_lock_symlink(name.string()); + Node_lock_guard file_guard(*symlink); + return _handle_registry.alloc(symlink); } Dir_handle dir(Path const &path, bool create) @@ -343,6 +368,7 @@ namespace File_system { } Symlink *symlink = dynamic_cast(node); if (symlink) { + s.size = symlink->length(); s.mode = File_system::Status::MODE_SYMLINK; return s; } diff --git a/os/src/server/ram_fs/symlink.h b/os/src/server/ram_fs/symlink.h index 07dacd3850..c74164e9f7 100644 --- a/os/src/server/ram_fs/symlink.h +++ b/os/src/server/ram_fs/symlink.h @@ -16,21 +16,27 @@ namespace File_system { { private: - Name _link_to; + char _link_to[MAX_PATH_LEN]; public: + Symlink(char const *name) { Node::name(name); } + size_t read(char *dst, size_t len, seek_off_t seek_offset) { - PDBG("not implemented"); - return 0; + size_t count = min(len, sizeof(_link_to) + 1); + Genode::strncpy(dst, _link_to, count); + return count; } size_t write(char const *src, size_t len, seek_off_t seek_offset) { - PDBG("not implemented"); - return 0; + size_t count = min(len, sizeof(_link_to) + 1); + Genode::strncpy(_link_to, src, count); + return count; } + + file_size_t length() const { return strlen(_link_to) + 1; } }; } diff --git a/os/src/server/tar_fs/main.cc b/os/src/server/tar_fs/main.cc index dbe5d26a34..3c48570797 100644 --- a/os/src/server/tar_fs/main.cc +++ b/os/src/server/tar_fs/main.cc @@ -25,6 +25,7 @@ #include #include #include +#include #include @@ -278,10 +279,45 @@ namespace File_system { return _handle_registry.alloc(file_node); } - Symlink_handle symlink(Dir_handle, Name const &name, bool create) + Symlink_handle symlink(Dir_handle dir_handle, Name const &name, bool create) { - /* not supported */ - return Symlink_handle(-1); + PDBGV("_root = %s, dir_name = %s, name = %s, create = %d", + _root.record()->name(), + _handle_registry.lookup(dir_handle)->record()->name(), + name.string(), + create); + + if (!valid_filename(name.string())) + throw Lookup_failed(); + + if (create) + throw Permission_denied(); + + Directory *dir = _handle_registry.lookup(dir_handle); + + Absolute_path abs_path(dir->record()->name()); + try { + abs_path.append("/"); + abs_path.append(name.base()); + } catch (Path_base::Path_too_long) { + throw Name_too_long(); + } + + PDBGV("abs_path = %s", abs_path.base()); + + Lookup_exact lookup_criterion(abs_path.base()); + Record *record = _lookup(&lookup_criterion); + + if (!record) { + PERR("Could not find record for %s", abs_path.base()); + throw Lookup_failed(); + } + + if (record->type() != Record::TYPE_SYMLINK) + throw Lookup_failed(); + + Symlink *symlink_node = new (env()->heap()) Symlink(record); + return _handle_registry.alloc(symlink_node); } Dir_handle dir(Path const &path, bool create) diff --git a/os/src/server/tar_fs/symlink.h b/os/src/server/tar_fs/symlink.h new file mode 100644 index 0000000000..78a6a4f8f8 --- /dev/null +++ b/os/src/server/tar_fs/symlink.h @@ -0,0 +1,54 @@ +/* + * \brief TAR file-system symlink node + * \author Christian Prochaska + * \date 2012-08-24 + */ + +/* + * 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 _SYMLINK_H_ +#define _SYMLINK_H_ + +/* local includes */ +#include + + +namespace File_system { + + class Symlink : public Node + { + public: + + Symlink(Record *record) : Node(record) { } + + size_t read(char *dst, size_t len, seek_off_t seek_offset) + { + bool verbose = false; + + if (verbose) + PDBG("len = %zu, seek_offset = %llu", len, seek_offset); + + size_t count = min(len, (size_t)100); + memcpy(dst, _record->linked_name(), count); + return count; + } + + size_t write(char const *src, size_t len, seek_off_t seek_offset) + { + bool verbose = false; + + if (verbose) + PDBG("len = %zu, seek_offset = %llu", len, seek_offset); + + return -1; + } + + }; +} + +#endif /* _SYMLINK_H_ */ diff --git a/ports/include/noux_session/client.h b/ports/include/noux_session/client.h index 7779799c93..5e87e3208a 100644 --- a/ports/include/noux_session/client.h +++ b/ports/include/noux_session/client.h @@ -34,9 +34,11 @@ namespace Noux { bool syscall(Syscall sc) { + static bool verbose = false; + bool result = call(sc); - if (result == false) + if ((result == false) && verbose) PERR("syscall %s failed", syscall_name(sc)); return result; diff --git a/ports/include/noux_session/noux_session.h b/ports/include/noux_session/noux_session.h index 31c96cfd57..b59e0455e0 100644 --- a/ports/include/noux_session/noux_session.h +++ b/ports/include/noux_session/noux_session.h @@ -34,7 +34,6 @@ namespace Noux { virtual Dataspace_capability sysio_dataspace() = 0; enum Syscall { - SYSCALL_GETCWD, SYSCALL_WRITE, SYSCALL_READ, SYSCALL_STAT, @@ -47,7 +46,6 @@ namespace Noux { SYSCALL_IOCTL, SYSCALL_LSEEK, SYSCALL_DIRENT, - SYSCALL_FCHDIR, SYSCALL_EXECVE, SYSCALL_SELECT, SYSCALL_FORK, @@ -56,8 +54,10 @@ namespace Noux { SYSCALL_PIPE, SYSCALL_DUP2, SYSCALL_UNLINK, + SYSCALL_READLINK, SYSCALL_RENAME, SYSCALL_MKDIR, + SYSCALL_SYMLINK, SYSCALL_SOCKET, SYSCALL_GETSOCKOPT, SYSCALL_SETSOCKOPT, @@ -78,7 +78,6 @@ namespace Noux { static char const *syscall_name(Syscall sc) { switch (sc) { - NOUX_DECL_SYSCALL_NAME(GETCWD) NOUX_DECL_SYSCALL_NAME(WRITE) NOUX_DECL_SYSCALL_NAME(READ) NOUX_DECL_SYSCALL_NAME(STAT) @@ -91,7 +90,6 @@ namespace Noux { 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) NOUX_DECL_SYSCALL_NAME(SELECT) NOUX_DECL_SYSCALL_NAME(FORK) @@ -100,8 +98,10 @@ namespace Noux { NOUX_DECL_SYSCALL_NAME(PIPE) NOUX_DECL_SYSCALL_NAME(DUP2) NOUX_DECL_SYSCALL_NAME(UNLINK) + NOUX_DECL_SYSCALL_NAME(READLINK) NOUX_DECL_SYSCALL_NAME(RENAME) NOUX_DECL_SYSCALL_NAME(MKDIR) + NOUX_DECL_SYSCALL_NAME(SYMLINK) NOUX_DECL_SYSCALL_NAME(SOCKET) NOUX_DECL_SYSCALL_NAME(GETSOCKOPT) NOUX_DECL_SYSCALL_NAME(SETSOCKOPT) diff --git a/ports/include/noux_session/sysio.h b/ports/include/noux_session/sysio.h index 1cdd65d5fb..82deebd345 100644 --- a/ports/include/noux_session/sysio.h +++ b/ports/include/noux_session/sysio.h @@ -286,18 +286,20 @@ namespace Noux { enum General_error { ERR_FD_INVALID, NUM_GENERAL_ERRORS }; enum Stat_error { STAT_ERR_NO_ENTRY = NUM_GENERAL_ERRORS }; - enum Fchdir_error { FCHDIR_ERR_NOT_DIR = NUM_GENERAL_ERRORS }; enum Fcntl_error { FCNTL_ERR_CMD_INVALID = NUM_GENERAL_ERRORS }; enum Ftruncate_error { FTRUNCATE_ERR_NO_PERM = NUM_GENERAL_ERRORS }; enum Open_error { OPEN_ERR_UNACCESSIBLE, OPEN_ERR_NO_PERM, OPEN_ERR_EXISTS }; enum Execve_error { EXECVE_NONEXISTENT = NUM_GENERAL_ERRORS }; enum Unlink_error { UNLINK_ERR_NO_ENTRY, UNLINK_ERR_NO_PERM }; + enum Readlink_error { READLINK_ERR_NO_ENTRY }; 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}; + enum Symlink_error { SYMLINK_ERR_EXISTS, SYMLINK_ERR_NO_ENTRY, + SYMLINK_ERR_NAME_TOO_LONG}; enum Read_error { READ_ERR_AGAIN, READ_ERR_WOULD_BLOCK, READ_ERR_INVALID, READ_ERR_IO }; @@ -338,14 +340,15 @@ namespace Noux { union { General_error general; Stat_error stat; - Fchdir_error fchdir; Fcntl_error fcntl; Ftruncate_error ftruncate; Open_error open; Execve_error execve; Unlink_error unlink; + Readlink_error readlink; Rename_error rename; Mkdir_error mkdir; + Symlink_error symlink; Read_error read; Write_error write; Accept_error accept; @@ -360,13 +363,13 @@ namespace Noux { union { - SYSIO_DECL(getcwd, { }, { Path path; }); - SYSIO_DECL(write, { int fd; size_t count; Chunk chunk; }, { size_t count; }); SYSIO_DECL(stat, { Path path; }, { Stat st; }); + SYSIO_DECL(symlink, { Path oldpath; Path newpath; }, { }); + SYSIO_DECL(fstat, { int fd; }, { Stat st; }); SYSIO_DECL(ftruncate, { int fd; off_t length; }, { }); @@ -385,11 +388,12 @@ namespace Noux { SYSIO_DECL(dirent, { int fd; }, { Dirent entry; }); - SYSIO_DECL(fchdir, { int fd; }, { }); - SYSIO_DECL(read, { int fd; size_t count; }, { Chunk chunk; size_t count; }); + SYSIO_DECL(readlink, { Path path; size_t bufsiz; }, + { Chunk chunk; ssize_t count; }); + SYSIO_DECL(execve, { Path filename; Args args; Env env; }, { }); SYSIO_DECL(select, { Select_fds fds; Select_timeout timeout; }, diff --git a/ports/run/noux_gdb.run b/ports/run/noux_gdb.run index dae672968e..9a0252c765 100644 --- a/ports/run/noux_gdb.run +++ b/ports/run/noux_gdb.run @@ -51,13 +51,20 @@ foreach test_binary $test_binaries { } # tar archive for the source code of the GDB monitor test +# currently, directories need to have their own tar records +exec mkdir -p bin/test-gdb_monitor-src foreach test_binary $test_binaries { set source_files [ exec [cross_dev_prefix]objdump -dl bin/$test_binary | grep "^/.*:.*" | sed -e "s/:.*//" | uniq ] foreach source_file $source_files { - if [file exists $source_file] { exec tar ufvhP bin/test-gdb_monitor-src.tar $source_file } + if [file exists $source_file] { + set dirname [ exec dirname $source_file] + exec mkdir -p bin/test-gdb_monitor-src$dirname + exec ln -sf $source_file bin/test-gdb_monitor-src$source_file + } } } +exec tar chf bin/test-gdb_monitor-src.tar -C bin/test-gdb_monitor-src . create_boot_directory diff --git a/ports/src/lib/libc_noux/plugin.cc b/ports/src/lib/libc_noux/plugin.cc index d7df5ee851..5c54dd30e1 100644 --- a/ports/src/lib/libc_noux/plugin.cc +++ b/ports/src/lib/libc_noux/plugin.cc @@ -176,19 +176,6 @@ extern "C" uid_t geteuid() } -extern "C" int __getcwd(char *dst, Genode::size_t dst_size) -{ - noux()->syscall(Noux::Session::SYSCALL_GETCWD); - Genode::size_t path_size = Genode::strlen(sysio()->getcwd_out.path); - - if (dst_size < path_size + 1) - return -ERANGE; - - Genode::strncpy(dst, sysio()->getcwd_out.path, dst_size); - return 0; -} - - /** * Utility to copy-out syscall results to buf struct * @@ -208,31 +195,6 @@ static void _sysio_to_stat_struct(Noux::Sysio const *sysio, struct stat *buf) } -static int _stat(char const *path, struct stat *buf, bool lstat = false) -{ - if ((path == NULL) or (buf == NULL)) { - errno = EFAULT; - return -1; - } - - Genode::strncpy(sysio()->stat_in.path, path, sizeof(sysio()->stat_in.path)); - - if (!noux()->syscall(Noux::Session::SYSCALL_STAT)) { - PWRN("stat syscall failed for path \"%s\"", path); - switch (sysio()->error.stat) { - case Noux::Sysio::STAT_ERR_NO_ENTRY: errno = ENOENT; break; - } - return -1; - } - - _sysio_to_stat_struct(sysio(), buf); - return 0; -} - - -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) { for (unsigned i = 0; array[i]; i++) @@ -251,61 +213,6 @@ static bool serialize_string_array(char const * const * array, char *dst, Genode } -extern "C" int execve(char const *filename, char *const argv[], - char *const envp[]) -{ - if (verbose) { - PDBG("filename=%s", filename); - - for (int i = 0; argv[i]; i++) - PDBG("argv[%d]='%s'", i, argv[i]); - - for (int i = 0; envp[i]; i++) - PDBG("envp[%d]='%s'", i, envp[i]); - } - - Genode::strncpy(sysio()->execve_in.filename, filename, sizeof(sysio()->execve_in.filename)); - if (!serialize_string_array(argv, sysio()->execve_in.args, - sizeof(sysio()->execve_in.args))) { - PERR("execve: argument buffer exceeded"); - errno = E2BIG; - return -1; - } - - if (!serialize_string_array(envp, sysio()->execve_in.env, - sizeof(sysio()->execve_in.env))) { - PERR("execve: environment buffer exceeded"); - errno = E2BIG; - return -1; - } - - if (!noux()->syscall(Noux::Session::SYSCALL_EXECVE)) { - PWRN("exec syscall failed for path \"%s\"", filename); - switch (sysio()->error.execve) { - case Noux::Sysio::EXECVE_NONEXISTENT: errno = ENOENT; break; - } - return -1; - } - - /* - * In the success case, we never return from execve, the execution is - * resumed in the new program. - */ - Genode::sleep_forever(); - return 0; -} - - -/** - * Called by execvp - */ -extern "C" int _execve(char const *filename, char *const argv[], - char *const envp[]) -{ - return execve(filename, argv, envp); -} - - /** * Return number of marhalled file descriptors into select argument buffer * @@ -640,20 +547,25 @@ namespace { _stderr(Libc::file_descriptor_allocator()->alloc(this, noux_context(2), 2)) { } - 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; } - bool supports_socket(int, int, int) { return true; } - bool supports_mmap() { return true; } + bool supports_execve(char const *, char *const[], + char *const[]) { return true; } + bool supports_open(char const *, int) { return true; } + bool supports_stat(char const *) { return true; } + bool supports_symlink(char const *, char const*) { return true; } + bool supports_pipe() { return true; } + bool supports_unlink(char const *) { return true; } + bool supports_readlink(const char *, char *, size_t) { return true; } + bool supports_rename(const char *, const char *) { return true; } + bool supports_mkdir(const char *, mode_t) { return true; } + bool supports_socket(int, int, int) { return true; } + bool supports_mmap() { return true; } 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 execve(char const *filename, char *const argv[], + char *const envp[]); int fstat(Libc::File_descriptor *, struct stat *); int fsync(Libc::File_descriptor *); int fstatfs(Libc::File_descriptor *, struct statfs *); @@ -661,9 +573,10 @@ namespace { 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); + ssize_t readlink(const char *path, char *buf, size_t bufsiz); int stat(char const *, struct stat *); + int symlink(const char *, const char *); int ioctl(Libc::File_descriptor *, int request, char *argp); int pipe(Libc::File_descriptor *pipefd[2]); int unlink(char const *path); @@ -699,9 +612,89 @@ namespace { }; + int Plugin::execve(char const *filename, char *const argv[], + char *const envp[]) + { + if (verbose) { + PDBG("filename=%s", filename); + + for (int i = 0; argv[i]; i++) + PDBG("argv[%d]='%s'", i, argv[i]); + + for (int i = 0; envp[i]; i++) + PDBG("envp[%d]='%s'", i, envp[i]); + } + + Genode::strncpy(sysio()->execve_in.filename, filename, sizeof(sysio()->execve_in.filename)); + if (!serialize_string_array(argv, sysio()->execve_in.args, + sizeof(sysio()->execve_in.args))) { + PERR("execve: argument buffer exceeded"); + errno = E2BIG; + return -1; + } + + /* communicate the current working directory as environment variable */ + + size_t noux_cwd_len = Genode::snprintf(sysio()->execve_in.env, + sizeof(sysio()->execve_in.env), + "NOUX_CWD="); + + if (!getcwd(&(sysio()->execve_in.env[noux_cwd_len]), + sizeof(sysio()->execve_in.env) - noux_cwd_len)) { + PERR("execve: environment buffer exceeded"); + errno = E2BIG; + return -1; + } + + noux_cwd_len = strlen(sysio()->execve_in.env) + 1; + + if (!serialize_string_array(envp, &(sysio()->execve_in.env[noux_cwd_len]), + sizeof(sysio()->execve_in.env) - noux_cwd_len)) { + PERR("execve: environment buffer exceeded"); + errno = E2BIG; + return -1; + } + + if (!noux()->syscall(Noux::Session::SYSCALL_EXECVE)) { + PWRN("exec syscall failed for path \"%s\"", filename); + switch (sysio()->error.execve) { + case Noux::Sysio::EXECVE_NONEXISTENT: errno = ENOENT; break; + } + return -1; + } + + /* + * In the success case, we never return from execve, the execution is + * resumed in the new program. + */ + Genode::sleep_forever(); + return 0; + } + + int Plugin::stat(char const *path, struct stat *buf) { - return _stat(path, buf, false); + if (verbose) + PDBG("path = %s", path); + + if ((path == NULL) or (buf == NULL)) { + errno = EFAULT; + return -1; + } + + Genode::strncpy(sysio()->stat_in.path, path, sizeof(sysio()->stat_in.path)); + + if (!noux()->syscall(Noux::Session::SYSCALL_STAT)) { + if (verbose) + PWRN("stat syscall failed for path \"%s\"", path); + switch (sysio()->error.stat) { + case Noux::Sysio::STAT_ERR_NO_ENTRY: errno = ENOENT; break; + } + return -1; + } + + _sysio_to_stat_struct(sysio(), buf); + return 0; } @@ -755,6 +748,30 @@ namespace { } + int Plugin::symlink(const char *oldpath, const char *newpath) + { + if (verbose) + PDBG("%s -> %s", newpath, oldpath); + + if ((Genode::strlen(oldpath) + 1 > sizeof(sysio()->symlink_in.oldpath)) || + (Genode::strlen(newpath) + 1 > sizeof(sysio()->symlink_in.newpath))) { + PDBG("ENAMETOOLONG"); + errno = ENAMETOOLONG; + return 0; + } + + Genode::strncpy(sysio()->symlink_in.oldpath, oldpath, sizeof(sysio()->symlink_in.oldpath)); + Genode::strncpy(sysio()->symlink_in.newpath, newpath, sizeof(sysio()->symlink_in.newpath)); + if (!noux()->syscall(Noux::Session::SYSCALL_SYMLINK)) { + PERR("symlink error"); + /* XXX set errno */ + return -1; + } + + return 0; + } + + int Plugin::fstatfs(Libc::File_descriptor *, struct statfs *buf) { buf->f_flags = MNT_UNION; @@ -1027,8 +1044,7 @@ namespace { */ Libc::File_descriptor *new_fd = Libc::file_descriptor_allocator()->alloc(this, 0); - - new_fd->context = noux_context(new_fd->libc_fd); + new_fd->path(fd->fd_path); /* * Use new allocated number as name of file descriptor @@ -1163,21 +1179,6 @@ namespace { } - int Plugin::fchdir(Libc::File_descriptor *fd) - { - sysio()->fchdir_in.fd = noux_fd(fd->context); - if (!noux()->syscall(Noux::Session::SYSCALL_FCHDIR)) { - switch (sysio()->error.fchdir) { - case Noux::Sysio::FCHDIR_ERR_NOT_DIR: errno = ENOTDIR; break; - default: errno = EPERM; break; - } - return -1; - } - - return 0; - } - - int Plugin::unlink(char const *path) { Genode::strncpy(sysio()->unlink_in.path, path, sizeof(sysio()->unlink_in.path)); @@ -1195,6 +1196,31 @@ namespace { } + ssize_t Plugin::readlink(const char *path, char *buf, size_t bufsiz) + { + if (verbose) + PDBG("path = %s, bufsiz = %zu", path, bufsiz); + + Genode::strncpy(sysio()->readlink_in.path, path, sizeof(sysio()->readlink_in.path)); + sysio()->readlink_in.bufsiz = bufsiz; + + if (!noux()->syscall(Noux::Session::SYSCALL_READLINK)) { + PWRN("readlink syscall failed for \"%s\"", path); + /* XXX set errno */ + return -1; + } + + ssize_t size = Genode::min((size_t)sysio()->readlink_out.count, bufsiz); + + Genode::memcpy(buf, sysio()->readlink_out.chunk, size); + + if (verbose) + PDBG("result = %s", buf); + + return size; + } + + int Plugin::rename(char const *from_path, char const *to_path) { Genode::strncpy(sysio()->rename_in.from_path, from_path, sizeof(sysio()->rename_in.from_path)); @@ -1706,8 +1732,15 @@ void init_libc_noux(void) unsigned num_entries = 0; /* index within 'env_array' */ + static Libc::Absolute_path noux_cwd("/"); + while (*env_string && (num_entries < ENV_MAX_ENTRIES - 1)) { - env_array[num_entries++] = env_string; + if ((strlen(env_string) >= strlen("NOUX_CWD=")) && + (strncmp(env_string, "NOUX_CWD=", strlen("NOUX_CWD=")) == 0)) { + noux_cwd.import(&env_string[strlen("NOUX_CWD=")]); + } else { + env_array[num_entries++] = env_string; + } env_string += (strlen(env_string) + 1); } env_array[num_entries] = 0; @@ -1720,6 +1753,8 @@ void init_libc_noux(void) /* initialize noux libc plugin */ static Plugin noux_plugin; + + chdir(noux_cwd.base()); } diff --git a/ports/src/noux/child.h b/ports/src/noux/child.h index 8932b72695..0c038b2500 100644 --- a/ports/src/noux/child.h +++ b/ports/src/noux/child.h @@ -296,7 +296,6 @@ namespace Noux { Dir_file_system *root_dir, Args const &args, Sysio::Env const &env, - char const *pwd, Cap_session *cap_session, Service_registry &parent_services, Rpc_entrypoint &resources_ep, @@ -329,7 +328,6 @@ namespace Noux { _child(_binary_ds, _resources.ram.cap(), _resources.cpu.cap(), _resources.rm.cap(), &_entrypoint, &_child_policy) { - _env.pwd(pwd); _args.dump(); if (!forked && !_binary_ds.valid()) { diff --git a/ports/src/noux/dir_file_system.h b/ports/src/noux/dir_file_system.h index a0e63e003d..72b665b906 100644 --- a/ports/src/noux/dir_file_system.h +++ b/ports/src/noux/dir_file_system.h @@ -466,6 +466,39 @@ namespace Noux { return false; } + bool readlink(Sysio *sysio, char const *path) + { + path = _sub_path(path); + + sysio->error.readlink = Sysio::READLINK_ERR_NO_ENTRY; + + /* path does not match directory name */ + if (!path) { + return false; + } + + /* path refers to any of our sub file systems */ + for (File_system *fs = _first_file_system; fs; fs = fs->next) { + if (fs->readlink(sysio, path)) { + return true; + } else { + /* + * Keep the most meaningful error code. When using + * stacked file systems, most child file systems will + * eventually return 'READLINK_ERR_NO_ENTRY' (or leave + * the error code unchanged). If any of those file + * systems has anything more interesting to tell, + * return this information. + */ + if (sysio->error.readlink != Sysio::READLINK_ERR_NO_ENTRY) + return false; + } + } + + /* none of our file systems could read the link */ + return false; + } + bool rename(Sysio *sysio, char const *from_path, char const *to_path) { from_path = _sub_path(from_path); @@ -518,6 +551,48 @@ namespace Noux { return false; } + bool symlink(Sysio *sysio, char const *path) + { + path = _sub_path(path); + + sysio->error.symlink = Sysio::SYMLINK_ERR_NO_ENTRY; + + /* path does not match directory name */ + if (!path) { + return false; + } + + /* + * Prevent symlink of path that equals directory name defined + * via the static fstab configuration. + */ + if (strlen(path) == 0) { + sysio->error.symlink = Sysio::SYMLINK_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->symlink(sysio, path)) { + return true; + } else { + /* + * Keep the most meaningful error code. When using + * stacked file systems, most child file systems will + * eventually return 'SYMLINK_ERR_NO_ENTRY' (or leave + * the error code unchanged). If any of those file + * systems has anything more interesting to tell, + * return this information. + */ + if (sysio->error.symlink != Sysio::SYMLINK_ERR_NO_ENTRY) + return false; + } + } + + /* none of our file systems could create the symlink */ + return false; + } + bool mkdir(Sysio *sysio, char const *path) { path = _sub_path(path); diff --git a/ports/src/noux/directory_service.h b/ports/src/noux/directory_service.h index 62771340e4..bfee517c5d 100644 --- a/ports/src/noux/directory_service.h +++ b/ports/src/noux/directory_service.h @@ -34,12 +34,14 @@ namespace Noux { 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, off_t index) = 0; - virtual bool unlink(Sysio *sysio, char const *path) = 0; - virtual bool rename(Sysio *sysio, char const *from_path, + virtual bool stat(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 readlink(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 bool mkdir(Sysio *sysio, char const *path) = 0; + virtual bool symlink(Sysio *sysio, char const *path) = 0; /** * Return number of directory entries located at given path diff --git a/ports/src/noux/environment.h b/ports/src/noux/environment.h index 7c06b984f5..25920ffee8 100644 --- a/ports/src/noux/environment.h +++ b/ports/src/noux/environment.h @@ -13,28 +13,20 @@ /* Genode includes */ #include -#include #include #include /* Noux includes */ #include -#include -#include namespace Noux { - /** - * Front-end for PWD environment variable - */ - class Environment : private Attached_ram_dataspace, public Pwd + class Environment : private Attached_ram_dataspace { private: Sysio::Env *_env; - Pwd::Path _pwd_path; - public: /** @@ -53,19 +45,5 @@ namespace Noux { * Return list of environment variables as zero-separated list */ Sysio::Env const &env() { return *_env; } - - - /******************* - ** Pwd interface ** - *******************/ - - char const *pwd() { return _pwd_path.base(); } - - void pwd(char const *pwd) - { - _pwd_path.import(pwd); - _pwd_path.remove_trailing('/'); - PINF("changed current work directory to %s", _pwd_path.base()); - } }; } diff --git a/ports/src/noux/fs_file_system.h b/ports/src/noux/fs_file_system.h index ef3cdf1fc8..b05007afde 100644 --- a/ports/src/noux/fs_file_system.h +++ b/ports/src/noux/fs_file_system.h @@ -26,6 +26,8 @@ namespace Noux { class Fs_file_system : public File_system { + enum { verbose = false }; + private: Lock _lock; @@ -84,6 +86,70 @@ namespace Noux { ~Fs_handle_guard() { _fs.close(_handle); } }; + size_t _read(::File_system::Node_handle node_handle, void *buf, + size_t count, size_t seek_offset) + { + ::File_system::Session::Tx::Source &source = *_fs.tx(); + + size_t const max_packet_size = source.bulk_buffer_size() / 2; + count = min(max_packet_size, count); + + ::File_system::Packet_descriptor + packet(source.alloc_packet(count), + 0, + node_handle, + ::File_system::Packet_descriptor::READ, + count, + seek_offset); + + /* pass packet to server side */ + source.submit_packet(packet); + source.get_acked_packet(); + + memcpy(buf, source.packet_content(packet), count); + + /* + * XXX check if acked packet belongs to request, + * needed for thread safety + */ + + source.release_packet(packet); + + return count; + } + + size_t _write(::File_system::Node_handle node_handle, + const char *buf, size_t count, size_t seek_offset) + { + ::File_system::Session::Tx::Source &source = *_fs.tx(); + + size_t const max_packet_size = source.bulk_buffer_size() / 2; + count = min(max_packet_size, count); + + ::File_system::Packet_descriptor + packet(source.alloc_packet(count), + 0, + node_handle, + ::File_system::Packet_descriptor::WRITE, + count, + seek_offset); + + memcpy(source.packet_content(packet), buf, count); + + /* 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 + */ + + source.release_packet(packet); + + return count; + } + public: /* @@ -185,7 +251,8 @@ namespace Noux { Fs_handle_guard node_guard(_fs, node); status = _fs.status(node); } catch (...) { - PWRN("stat failed for path '%s'", path); + if (verbose) + PDBG("stat failed for path '%s'", path); return false; } @@ -284,6 +351,40 @@ namespace Noux { return true; } + bool readlink(Sysio *sysio, char const *path) + { + /* + * Canonicalize path (i.e., path must start with '/') + */ + Absolute_path abs_path(path); + abs_path.strip_last_element(); + + Absolute_path symlink_name(path); + symlink_name.keep_only_last_element(); + + Sysio::Readlink_error error = Sysio::READLINK_ERR_NO_ENTRY; + try { + ::File_system::Dir_handle dir_handle = _fs.dir(abs_path.base(), false); + Fs_handle_guard from_dir_guard(_fs, dir_handle); + + ::File_system::Symlink_handle symlink_handle = + _fs.symlink(dir_handle, symlink_name.base() + 1, false); + Fs_handle_guard symlink_guard(_fs, symlink_handle); + + sysio->readlink_out.count = _read(symlink_handle, + sysio->readlink_out.chunk, + min(sysio->readlink_in.bufsiz, + sizeof(sysio->readlink_out.chunk)), + 0); + + return true; + } catch (...) { } + + sysio->error.readlink = error; + + return false; + } + bool rename(Sysio *sysio, char const *from_path, char const *to_path) { Absolute_path from_dir_path(from_path); @@ -337,6 +438,40 @@ namespace Noux { return false; } + bool symlink(Sysio *sysio, char const *path) + { + /* + * Canonicalize path (i.e., path must start with '/') + */ + Absolute_path abs_path(path); + abs_path.strip_last_element(); + + Absolute_path symlink_name(path); + symlink_name.keep_only_last_element(); + + Sysio::Symlink_error error = Sysio::SYMLINK_ERR_NO_ENTRY; + try { + ::File_system::Dir_handle dir_handle = _fs.dir(abs_path.base(), false); + Fs_handle_guard from_dir_guard(_fs, dir_handle); + + ::File_system::Symlink_handle symlink_handle = + _fs.symlink(dir_handle, symlink_name.base() + 1, true); + Fs_handle_guard symlink_guard(_fs, symlink_handle); + + _write(symlink_handle, sysio->symlink_in.oldpath, + strlen(sysio->symlink_in.oldpath) + 1, 0); + return true; + } + catch (::File_system::Invalid_handle) { error = Sysio::SYMLINK_ERR_NO_ENTRY; } + catch (::File_system::Node_already_exists) { error = Sysio::SYMLINK_ERR_EXISTS; } + catch (::File_system::Invalid_name) { error = Sysio::SYMLINK_ERR_NAME_TOO_LONG; } + catch (::File_system::Lookup_failed) { error = Sysio::SYMLINK_ERR_NO_ENTRY; } + + sysio->error.symlink = error; + + return false; + } + size_t num_dirent(char const *path) { if (strcmp(path, "") == 0) @@ -401,7 +536,8 @@ namespace Noux { bool const create = sysio->open_in.mode & Sysio::OPEN_MODE_CREATE; if (create) - PDBG("creation of file %s requested", file_name.base() + 1); + if (verbose) + PDBG("creation of file %s requested", file_name.base() + 1); Sysio::Open_error error = Sysio::OPEN_ERR_UNACCESSIBLE; @@ -443,35 +579,12 @@ namespace Noux { { Fs_vfs_handle const *handle = static_cast(vfs_handle); - ::File_system::Session::Tx::Source &source = *_fs.tx(); + size_t const count = min(sizeof(sysio->write_in.chunk), + sysio->write_in.count); - 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); + sysio->write_out.count = _write(handle->file_handle(), + sysio->write_in.chunk, count, + handle->seek()); return true; } @@ -486,36 +599,14 @@ namespace Noux { 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 count = min(file_bytes_left, + min(sizeof(sysio->read_out.chunk), + sysio->read_in.count)); - 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))); + sysio->read_out.count = _read(handle->file_handle(), + sysio->read_out.chunk, + count, handle->seek()); - ::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; } diff --git a/ports/src/noux/io_channel.h b/ports/src/noux/io_channel.h index c947eab449..db2820579c 100644 --- a/ports/src/noux/io_channel.h +++ b/ports/src/noux/io_channel.h @@ -22,7 +22,6 @@ /* Noux includes */ #include -#include #include #include @@ -55,7 +54,6 @@ namespace Noux { virtual bool fstat(Sysio *sysio) { return false; } virtual bool ftruncate(Sysio *sysio) { return false; } virtual bool fcntl(Sysio *sysio) { return false; } - 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; } diff --git a/ports/src/noux/main.cc b/ports/src/noux/main.cc index 2bf9fe0f35..37db82b2e1 100644 --- a/ports/src/noux/main.cc +++ b/ports/src/noux/main.cc @@ -127,12 +127,6 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) try { switch (sc) { - case SYSCALL_GETCWD: - - Genode::strncpy(_sysio->getcwd_out.path, _env.pwd(), - sizeof(_sysio->getcwd_out.path)); - return true; - case SYSCALL_WRITE: { size_t const count_in = _sysio->write_in.count; @@ -178,8 +172,7 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) case SYSCALL_STAT: case SYSCALL_LSTAT: /* XXX implement difference between 'lstat' and 'stat' */ { - bool result = _root_dir->stat(_sysio, Absolute_path(_sysio->stat_in.path, - _env.pwd()).base()); + bool result = _root_dir->stat(_sysio, _sysio->stat_in.path); /** * Instead of using the uid/gid given by the actual file system @@ -211,13 +204,11 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) case SYSCALL_OPEN: { - Absolute_path absolute_path(_sysio->open_in.path, _env.pwd()); - - Vfs_handle *vfs_handle = _root_dir->open(_sysio, absolute_path.base()); + Vfs_handle *vfs_handle = _root_dir->open(_sysio, _sysio->open_in.path); if (!vfs_handle) return false; - char const *leaf_path = _root_dir->leaf_path(absolute_path.base()); + char const *leaf_path = _root_dir->leaf_path(_sysio->open_in.path); /* * File descriptors of opened directories are handled by @@ -226,10 +217,10 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) * root. */ if (vfs_handle->ds() == _root_dir) - leaf_path = absolute_path.base(); + leaf_path = _sysio->open_in.path; Shared_pointer - channel(new Vfs_io_channel(absolute_path.base(), + channel(new Vfs_io_channel(_sysio->open_in.path, leaf_path, _root_dir, vfs_handle), Genode::env()->heap()); @@ -262,24 +253,18 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) return _lookup_channel(_sysio->dirent_in.fd)->dirent(_sysio); - case SYSCALL_FCHDIR: - - return _lookup_channel(_sysio->fchdir_in.fd)->fchdir(_sysio, &_env); - case SYSCALL_EXECVE: { - Absolute_path absolute_path(_sysio->execve_in.filename, _env.pwd()); - - Dataspace_capability binary_ds = _root_dir->dataspace(absolute_path.base()); + Dataspace_capability binary_ds = _root_dir->dataspace(_sysio->execve_in.filename); if (!binary_ds.valid()) throw Child::Binary_does_not_exist(); Child_envexecve_in.args)> child_env( - absolute_path.base(), binary_ds, _sysio->execve_in.args, + _sysio->execve_in.filename, binary_ds, _sysio->execve_in.args, _sysio->execve_in.env); - _root_dir->release(absolute_path.base(), binary_ds); + _root_dir->release(_sysio->execve_in.filename, binary_ds); try { Child *child = new Child(child_env.binary_name(), @@ -289,7 +274,6 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) _root_dir, child_env.args(), child_env.env(), - _env.pwd(), _cap_session, _parent_services, _resources.ep, @@ -490,7 +474,6 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) _root_dir, _args, _env.env(), - _env.pwd(), _cap_session, _parent_services, _resources.ep, @@ -564,20 +547,25 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) case SYSCALL_UNLINK: - return _root_dir->unlink(_sysio, Absolute_path(_sysio->unlink_in.path, - _env.pwd()).base()); + return _root_dir->unlink(_sysio, _sysio->unlink_in.path); + + case SYSCALL_READLINK: + + return _root_dir->readlink(_sysio, _sysio->readlink_in.path); + 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()); + return _root_dir->rename(_sysio, _sysio->rename_in.from_path, + _sysio->rename_in.to_path); case SYSCALL_MKDIR: - return _root_dir->mkdir(_sysio, Absolute_path(_sysio->mkdir_in.path, - _env.pwd()).base()); + return _root_dir->mkdir(_sysio, _sysio->mkdir_in.path); + + case SYSCALL_SYMLINK: + + return _root_dir->symlink(_sysio, _sysio->symlink_in.newpath); case SYSCALL_USERINFO: { @@ -793,7 +781,6 @@ int main(int argc, char **argv) &root_dir, args_of_init_process(), env_string_of_init_process(), - "/", &cap, parent_services, resources_ep, diff --git a/ports/src/noux/net/net.cc b/ports/src/noux/net/net.cc index d76786fc88..a2ca5c5e72 100644 --- a/ports/src/noux/net/net.cc +++ b/ports/src/noux/net/net.cc @@ -150,7 +150,6 @@ bool Noux::Child::_syscall_net(Noux::Session::Syscall sc) /** * Keep compiler from complaining */ - case SYSCALL_GETCWD: case SYSCALL_WRITE: case SYSCALL_READ: case SYSCALL_STAT: @@ -162,7 +161,6 @@ bool Noux::Child::_syscall_net(Noux::Session::Syscall sc) case SYSCALL_IOCTL: case SYSCALL_LSEEK: case SYSCALL_DIRENT: - case SYSCALL_FCHDIR: case SYSCALL_EXECVE: case SYSCALL_SELECT: case SYSCALL_FORK: @@ -175,6 +173,8 @@ bool Noux::Child::_syscall_net(Noux::Session::Syscall sc) case SYSCALL_RENAME: case SYSCALL_MKDIR: case SYSCALL_FTRUNCATE: + case SYSCALL_READLINK: + case SYSCALL_SYMLINK: case SYSCALL_USERINFO: break; case SYSCALL_SOCKET: diff --git a/ports/src/noux/net/socket_io_channel.h b/ports/src/noux/net/socket_io_channel.h index 6fcef63337..ba3acc15e4 100644 --- a/ports/src/noux/net/socket_io_channel.h +++ b/ports/src/noux/net/socket_io_channel.h @@ -103,7 +103,6 @@ namespace Noux { } } - bool fchdir(Sysio *sysio, Pwd *pwd) { return false; } bool dirent(Sysio *sysio) { return false; } bool check_unblock(bool rd, bool wr, bool ex) const diff --git a/ports/src/noux/null_file_system.h b/ports/src/noux/null_file_system.h index ef30b9b57b..c86dbdbd8c 100644 --- a/ports/src/noux/null_file_system.h +++ b/ports/src/noux/null_file_system.h @@ -134,6 +134,12 @@ namespace Noux { return false; } + bool readlink(Sysio *sysio, char const *path) + { + /* not supported */ + return false; + } + bool rename(Sysio *sysio, char const *from_path, char const *to_path) { /* not supported */ @@ -146,6 +152,12 @@ namespace Noux { return false; } + bool symlink(Sysio *sysio, char const *path) + { + /* not supported */ + return false; + } + /*************************** ** File_system interface ** ***************************/ @@ -171,7 +183,7 @@ namespace Noux { return true; } - bool ftruncate(Sysio *sysio, Vfs_handle *vfs_handle) { return false; } + bool ftruncate(Sysio *sysio, Vfs_handle *vfs_handle) { return true; } }; } diff --git a/ports/src/noux/pwd.h b/ports/src/noux/pwd.h deleted file mode 100644 index 9a161ca8be..0000000000 --- a/ports/src/noux/pwd.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * \brief Interface to 'PWD' environment variable - * \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__PWD_H_ -#define _NOUX__PWD_H_ - -/* Genode includes */ -#include - -/* Noux includes */ -#include -#include - -namespace Noux { - - struct Pwd - { - typedef Noux::Path Path; - - /** - * Lookup process work directory from environment - */ - virtual char const *pwd() = 0; - - /** - * Set current work directory - */ - virtual void pwd(char const *pwd) = 0; - }; -} - -#endif /* _NOUX__PWD_H_ */ diff --git a/ports/src/noux/shared_pointer.h b/ports/src/noux/shared_pointer.h index 85aedb6560..f86680b793 100644 --- a/ports/src/noux/shared_pointer.h +++ b/ports/src/noux/shared_pointer.h @@ -22,7 +22,6 @@ /* Noux includes */ #include -#include namespace Noux { diff --git a/ports/src/noux/tar_file_system.h b/ports/src/noux/tar_file_system.h index ac7833bfd2..52ed82f66d 100644 --- a/ports/src/noux/tar_file_system.h +++ b/ports/src/noux/tar_file_system.h @@ -389,10 +389,34 @@ namespace Noux { bool unlink(Sysio *, char const *) { return false; } + bool readlink(Sysio *sysio, char const *path) + { + Lookup_exact lookup_criterion(path); + + Record *record = _lookup(&lookup_criterion); + + if (!record || (record->type() != Record::TYPE_SYMLINK)) { + sysio->error.readlink = Sysio::READLINK_ERR_NO_ENTRY; + return false; + } + + size_t const count = min(sysio->readlink_in.bufsiz, + min(sizeof(sysio->readlink_out.chunk), + (size_t)100)); + + memcpy(sysio->readlink_out.chunk, record->linked_name(), count); + + sysio->readlink_out.count = count; + + return true; + } + bool rename(Sysio *, char const *, char const *) { return false; } bool mkdir(Sysio *, char const *) { return false; } + bool symlink(Sysio *, char const *) { return false; } + size_t num_dirent(char const *path) { return _cached_num_dirent.num_dirent(path); diff --git a/ports/src/noux/terminal_file_system.h b/ports/src/noux/terminal_file_system.h index 36b5008324..6b5f3e51da 100644 --- a/ports/src/noux/terminal_file_system.h +++ b/ports/src/noux/terminal_file_system.h @@ -169,6 +169,12 @@ namespace Noux { return false; } + bool readlink(Sysio *sysio, char const *path) + { + /* not supported */ + return false; + } + bool rename(Sysio *sysio, char const *from_path, char const *to_path) { /* not supported */ @@ -181,6 +187,12 @@ namespace Noux { return false; } + bool symlink(Sysio *sysio, char const *path) + { + /* not supported */ + return false; + } + /*************************** ** File_system interface ** ***************************/ @@ -205,7 +217,7 @@ namespace Noux { return true; } - bool ftruncate(Sysio *sysio, Vfs_handle *vfs_handle) { return false; } + bool ftruncate(Sysio *sysio, Vfs_handle *vfs_handle) { return true; } }; } diff --git a/ports/src/noux/vfs_handle.h b/ports/src/noux/vfs_handle.h index 5ac9dcae53..0c655291f0 100644 --- a/ports/src/noux/vfs_handle.h +++ b/ports/src/noux/vfs_handle.h @@ -57,8 +57,10 @@ namespace Noux { 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 readlink(Sysio *, char const *) { return _msg("readlink"); } bool rename(Sysio *, char const *, char const *) { return _msg("rename"); } bool mkdir(Sysio *, char const *) { return _msg("mkdir"); } + bool symlink(Sysio *, char const *) { return _msg("symlink"); } size_t num_dirent(char const *) { return 0; } bool is_directory(char const *) { return false; } char const *leaf_path(char const *path) { return 0; } diff --git a/ports/src/noux/vfs_io_channel.h b/ports/src/noux/vfs_io_channel.h index 46225b7f22..1161b1820c 100644 --- a/ports/src/noux/vfs_io_channel.h +++ b/ports/src/noux/vfs_io_channel.h @@ -18,7 +18,6 @@ #include #include #include -#include namespace Noux { @@ -87,22 +86,6 @@ namespace Noux { }; } - bool fchdir(Sysio *sysio, Pwd *pwd) - { - sysio->fstat_in.fd = sysio->fchdir_in.fd; - - fstat(sysio); - - if ((sysio->fstat_out.st.mode & Sysio::STAT_MODE_DIRECTORY) != - Sysio::STAT_MODE_DIRECTORY) { - sysio->error.fchdir = Sysio::FCHDIR_ERR_NOT_DIR; - return false; - } - - pwd->pwd(_path.base()); - return true; - } - /* * The 'dirent' function for the root directory only (the * 'Dir_file_system::open()' function handles all requests referring diff --git a/ports/src/noux/zero_file_system.h b/ports/src/noux/zero_file_system.h index 5979c10e4f..65ba21f65d 100644 --- a/ports/src/noux/zero_file_system.h +++ b/ports/src/noux/zero_file_system.h @@ -134,6 +134,12 @@ namespace Noux { return false; } + bool readlink(Sysio *sysio, char const *path) + { + /* not supported */ + return false; + } + bool rename(Sysio *sysio, char const *from_path, char const *to_path) { /* not supported */ @@ -146,6 +152,12 @@ namespace Noux { return false; } + bool symlink(Sysio *sysio, char const *path) + { + /* not supported */ + return false; + } + /*************************** ** File_system interface ** ***************************/