From 3a0922001be492a0dd0f1819bf6cd61a355525ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josef=20S=C3=B6ntgen?= Date: Mon, 11 Nov 2013 15:58:59 +0100 Subject: [PATCH] libports: add FUSE implementation This FUSE implementation consists of libfuse, which provides a subset of the FUSE 2.6 API and libc_fuse, which provides support for accessing FUSE based file system via the libc. Fixes #942. --- libports/include/fuse/fuse.h | 161 ++++++++ libports/include/fuse/fuse_opt.h | 56 +++ libports/include/fuse/fuse_private.h | 127 ++++++ libports/lib/import/import-libfuse.mk | 1 + libports/lib/mk/libc_fuse.mk | 5 + libports/lib/mk/libfuse.mk | 9 + libports/src/lib/fuse/fuse.cc | 293 ++++++++++++++ libports/src/lib/libc_fuse/plugin.cc | 548 ++++++++++++++++++++++++++ 8 files changed, 1200 insertions(+) create mode 100644 libports/include/fuse/fuse.h create mode 100644 libports/include/fuse/fuse_opt.h create mode 100644 libports/include/fuse/fuse_private.h create mode 100644 libports/lib/import/import-libfuse.mk create mode 100644 libports/lib/mk/libc_fuse.mk create mode 100644 libports/lib/mk/libfuse.mk create mode 100644 libports/src/lib/fuse/fuse.cc create mode 100644 libports/src/lib/libc_fuse/plugin.cc diff --git a/libports/include/fuse/fuse.h b/libports/include/fuse/fuse.h new file mode 100644 index 000000000..7164b8d44 --- /dev/null +++ b/libports/include/fuse/fuse.h @@ -0,0 +1,161 @@ +/* + * \brief libfuse re-implementation public API + * \author Josef Soentgen + * \date 2013-11-07 + */ + +/* + * Copyright (C) 2013 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 _INCLUDE__FUSE_H_ +#define _INCLUDE__FUSE_H_ + +/* libc includes */ +#include +#include +#include +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef FUSE_USE_VERSION +#define FUSE_USE_VERSION 26 +#endif + +#if FUSE_USE_VERSION >= 26 +#define FUSE_VERSION 26 +#else +#error "No support for FUSE_VERSION < 26" +#endif + +void fuse_genode(const char*); + +#include "fuse_opt.h" + +struct fuse_chan; +struct fuse_args; +struct fuse_session; + +struct fuse_file_info { + int32_t flags; /* open(2) flags */ + uint32_t fh_old; /* old file handle */ + int32_t writepage; + uint32_t direct_io:1; + uint32_t keep_cache:1; + uint32_t flush:1; + uint32_t nonseekable:1; + uint32_t __padd:27; + uint32_t flock_release : 1; + uint64_t fh; /* file handle */ + uint64_t lock_owner; +}; + +struct fuse_conn_info { + uint32_t proto_major; + uint32_t proto_minor; + uint32_t async_read; + uint32_t max_write; + uint32_t max_readahead; + uint32_t capable; + uint32_t want; + uint32_t max_background; + uint32_t congestion_threshold; + uint32_t reserved[23]; +}; + +struct fuse_context { + struct fuse *fuse; + uid_t uid; + gid_t gid; + pid_t pid; + void *private_data; + mode_t umask; +}; + +typedef ino_t fuse_ino_t; +typedef int (*fuse_fill_dir_t)(void *, const char *, const struct stat *, off_t); + + + +typedef int (*fuse_dirfil_t)(void *, const char *, int, ino_t); + +/* only operations available in 2.6 */ +struct fuse_operations { + int (*getattr)(const char *, struct stat *); + int (*readlink)(const char *, char *, size_t); + int (*getdir)(const char *, void *, fuse_dirfil_t); + int (*mknod)(const char *, mode_t, dev_t); + int (*mkdir)(const char *, mode_t); + int (*unlink)(const char *); + int (*rmdir)(const char *); + int (*symlink)(const char *, const char *); + int (*rename)(const char *, const char *); + int (*link)(const char *, const char *); + int (*chmod)(const char *, mode_t); + int (*chown)(const char *, uid_t, gid_t); + int (*truncate)(const char *, off_t); + int (*utime)(const char *, struct utimbuf *); + int (*open)(const char *, struct fuse_file_info *); + int (*read)(const char *, char *, size_t, off_t, + struct fuse_file_info *); + int (*write)(const char *, const char *, size_t, off_t, + struct fuse_file_info *); + int (*statfs)(const char *, struct statvfs *); + int (*flush)(const char *, struct fuse_file_info *); + int (*release)(const char *, struct fuse_file_info *); + int (*fsync)(const char *, int, struct fuse_file_info *); + int (*setxattr)(const char *, const char *, const char *, size_t, + int); + int (*getxattr)(const char *, const char *, char *, size_t); + int (*listxattr)(const char *, char *, size_t); + int (*removexattr)(const char *, const char *); + int (*opendir)(const char *, struct fuse_file_info *); + int (*readdir)(const char *, void *, fuse_fill_dir_t, off_t, + struct fuse_file_info *); + int (*releasedir)(const char *, struct fuse_file_info *); + int (*fsyncdir)(const char *, int, struct fuse_file_info *); + void *(*init)(struct fuse_conn_info *); + void (*destroy)(void *); + int (*access)(const char *, int); + int (*create)(const char *, mode_t, struct fuse_file_info *); + int (*ftruncate)(const char *, off_t, struct fuse_file_info *); + int (*fgetattr)(const char *, struct stat *, struct fuse_file_info *); + int (*lock)(const char *, struct fuse_file_info *, int, struct flock *); + int (*utimens)(const char *, const struct timespec *); + int (*bmap)(const char *, size_t , uint64_t *); +}; + +/* API */ +int fuse_version(void); +struct fuse *fuse_new(struct fuse_chan *, struct fuse_args *, + const struct fuse_operations *, size_t, void *); +void fuse_destroy(struct fuse *); +void fuse_exit(struct fuse *f); +struct fuse_session *fuse_get_session(struct fuse *); +struct fuse_context *fuse_get_context(void); +int fuse_loop(struct fuse *); +int fuse_loop_mt(struct fuse *); +int fuse_main(int, char **, const struct fuse_operations *, void *); +struct fuse_chan *fuse_mount(const char *, struct fuse_args *); +int fuse_parse_cmdline(struct fuse_args *, char **, int *, int *); +void fuse_remove_signal_handlers(struct fuse_session *); +int fuse_set_signal_handlers(struct fuse_session *); +int fuse_chan_fd(struct fuse_chan *); +int fuse_daemonize(int); +int fuse_is_lib_option(const char *); +void fuse_teardown(struct fuse *, char *); +void fuse_unmount(const char *, struct fuse_chan *); + +#ifdef __cplusplus +} +#endif + +#endif /* _INCLUDE__FUSE_H_ */ diff --git a/libports/include/fuse/fuse_opt.h b/libports/include/fuse/fuse_opt.h new file mode 100644 index 000000000..30fe20ca8 --- /dev/null +++ b/libports/include/fuse/fuse_opt.h @@ -0,0 +1,56 @@ +/* + * \brief libfuse public API + * \author Josef Soentgen + * \date 2013-11-07 + */ + +/* + * Copyright (C) 2013 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 _FUSE_OPT_H_ +#define _FUSE_OPT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +struct fuse_opt { + const char *templ; + unsigned long off; + int val; +}; + +struct fuse_args { + int argc; + char **argv; + int allocated; +}; + +typedef int (*fuse_opt_proc_t)(void *, const char *, int, struct fuse_args *); +int fuse_opt_add_arg(struct fuse_args *, const char *); +int fuse_opt_insert_arg(struct fuse_args *, int, const char *); +void fuse_opt_free_args(struct fuse_args *); +int fuse_opt_add_opt(char **, const char *); +int fuse_opt_add_opt_escaped(char **, const char *); +int fuse_opt_match(const struct fuse_opt *, const char *); +int fuse_opt_parse(struct fuse_args *, void *, struct fuse_opt *, + fuse_opt_proc_t); + +#define FUSE_ARGS_INIT(ac, av) { ac, av, 0 } + +#define FUSE_OPT_KEY(t, k) { t, -1, k } +#define FUSE_OPT_END { NULL, 0, 0 } +#define FUSE_OPT_KEY_OPT -1 +#define FUSE_OPT_KEY_NONOPT -2 +#define FUSE_OPT_KEY_KEEP -3 +#define FUSE_OPT_KEY_DISCARD -4 + +#ifdef __cplusplus +} +#endif + +#endif /* _FUSE_OPT_H_ */ diff --git a/libports/include/fuse/fuse_private.h b/libports/include/fuse/fuse_private.h new file mode 100644 index 000000000..ab30bdb36 --- /dev/null +++ b/libports/include/fuse/fuse_private.h @@ -0,0 +1,127 @@ +/* + * \brief libfuse private API + * \author Josef Soentgen + * \date 2013-11-07 + * */ + +/* + * Copyright (C) 2013 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 _INCLUDE__FUSE_PRIVATE_H_ +#define _INCLUDE__FUSE_PRIVATE_H_ + +#include "fuse.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct fuse_dirhandle; +struct fuse_args; + +struct fuse_session { + void *args; +}; + +struct fuse_chan { + char *dir; + struct fuse_args *args; +}; + +struct fuse_config { + uid_t uid; + gid_t gid; + pid_t pid; + mode_t umask; + int set_mode; + int set_uid; + int set_gid; +}; + +typedef struct fuse_dirhandle { + fuse_fill_dir_t filler; + void *buf; + size_t size; + off_t offset; +} *fuse_dirh_t; + + +struct fuse { + struct fuse_chan *fc; + struct fuse_operations op; + struct fuse_args *args; + + struct fuse_config conf; + struct fuse_session se; + + fuse_fill_dir_t filler; + + void *userdata; + + /* Block_session info */ + uint32_t block_size; + uint64_t block_count; +}; + +#ifdef __cplusplus +} +#endif + +namespace Fuse { + + struct fuse* fuse(); + + bool initialized(); + + /* Genode fuse filesystem init functions */ + void init_fs(); + void deinit_fs(); + + enum Fuse_operations { + FUSE_OP_GETATTR = 0, + FUSE_OP_READLINK = 1, + FUSE_OP_GETDIR = 2, + FUSE_OP_MKNOD = 3, + FUSE_OP_MKDIR = 4, + FUSE_OP_UNLINK = 5, + FUSE_OP_RMDIR = 6, + FUSE_OP_SYMLINK = 7, + FUSE_OP_RENAME = 8, + FUSE_OP_LINK = 9, + FUSE_OP_CHMOD = 10, + FUSE_OP_CHOWN = 11, + FUSE_OP_TRUNCATE = 12, + FUSE_OP_UTIME = 13, + FUSE_OP_OPEN = 14, + FUSE_OP_READ = 15, + FUSE_OP_WRITE = 16, + FUSE_OP_STATFS = 17, + FUSE_OP_FLUSH = 18, + FUSE_OP_RELEASE = 19, + FUSE_OP_FSYNC = 20, + FUSE_OP_SETXATTR = 21, + FUSE_OP_GETXATTR = 22, + FUSE_OP_LISTXATTR = 23, + FUSE_OP_REMOVEXATTR = 24, + FUSE_OP_OPENDIR = 25, + FUSE_OP_READDIR = 26, + FUSE_OP_RELEASEDIR = 27, + FUSE_OP_FSYNCDIR = 28, + FUSE_OP_INIT = 29, + FUSE_OP_DESTROY = 30, + FUSE_OP_ACCESS = 31, + FUSE_OP_CREATE = 32, + FUSE_OP_FTRUNCATE = 33, + FUSE_OP_FGETATTR = 34, + FUSE_OP_LOCK = 35, + FUSE_OP_UTIMENS = 36, + FUSE_OP_BMAP = 37, + FUSE_OP_MAX = 38, + }; +} + +#endif /* _INCLUDE__FUSE_PRIVATE_ */ diff --git a/libports/lib/import/import-libfuse.mk b/libports/lib/import/import-libfuse.mk new file mode 100644 index 000000000..132cf5bac --- /dev/null +++ b/libports/lib/import/import-libfuse.mk @@ -0,0 +1 @@ +REP_INC_DIR += include/fuse diff --git a/libports/lib/mk/libc_fuse.mk b/libports/lib/mk/libc_fuse.mk new file mode 100644 index 000000000..ad40bcf7f --- /dev/null +++ b/libports/lib/mk/libc_fuse.mk @@ -0,0 +1,5 @@ +LIBS = libc libfuse + +SRC_CC = plugin.cc + +vpath %.cc $(REP_DIR)/src/lib/libc_fuse diff --git a/libports/lib/mk/libfuse.mk b/libports/lib/mk/libfuse.mk new file mode 100644 index 000000000..ba176e682 --- /dev/null +++ b/libports/lib/mk/libfuse.mk @@ -0,0 +1,9 @@ +SRC_CC = fuse.cc + +INC_DIR += $(REP_DIR)/include/fuse + +LIBS = libc + +CC_OPT += -fpermissive + +vpath %.cc $(REP_DIR)/src/lib/fuse diff --git a/libports/src/lib/fuse/fuse.cc b/libports/src/lib/fuse/fuse.cc new file mode 100644 index 000000000..4127d176f --- /dev/null +++ b/libports/src/lib/fuse/fuse.cc @@ -0,0 +1,293 @@ +/* + * \brief libfuse re-implementation + * \author Josef Soentgen + * \date 2013-11-07 + */ + +/* + * Copyright (C) 2013 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. + */ + +/* Genodes includes */ +#include +#include + +/* libc includes */ +#include +#include +#include + +#include +#include + +static bool _initialized; +static struct fuse *_fuse; +static struct fuse_context _ctx; + +#if 1 +#define TRACE PDBG("") +#else +#define TRACE +#endif + +struct fuse* Fuse::fuse() +{ + if (_fuse == 0) { + PERR("libfuse: struct fuse is zero"); + abort(); + } + + return _fuse; +} + +bool Fuse::initialized() +{ + return _initialized; +} + +extern "C" { + +void fuse_genode(const char *s) +{ + PLOG("%s: %s", __func__, s); +} + +#define FIX_UP_OPERATION1(f, name) \ + if(f->op.name == 0) \ + f->op.name = dummy_op1; + +#define FIX_UP_OPERATION2(f, name) \ + if(f->op.name == 0) \ + f->op.name = dummy_op2; + +static int dummy_op1(void) +{ + TRACE; + return -ENOSYS; +} + +static int dummy_op2(void) +{ + TRACE; + return 0; +} + +static int fill_dir(void *dh, const char *name, const struct stat *sbuf, off_t offset) +{ + static uint32_t fileno = 1; + + struct fuse_dirhandle *dir = (struct fuse_dirhandle*)dh; + + if ((dir->offset + sizeof (struct dirent)) > dir->size) { + PWRN("fill_dir buffer full"); + return 1; + } + + struct dirent *entry = (struct dirent *)(((char*)dir->buf) + dir->offset); + Genode::memset(entry, 0, sizeof (struct dirent)); + + if (sbuf) { + entry->d_fileno = sbuf->st_ino; + entry->d_type = IFTODT(sbuf->st_mode); + } + else { + entry->d_fileno = fileno++; + entry->d_type = DT_UNKNOWN; + } + + /* even in a valid sbuf the inode might by 0 */ + if (entry->d_fileno == 0) + entry->d_fileno = fileno++; + + size_t namelen = Genode::strlen(name); + if (namelen > 255) + namelen = 255; + + Genode::strncpy(entry->d_name, name, namelen + 1); + + entry->d_reclen = sizeof (struct dirent); + + dir->offset += sizeof (struct dirent); + + return 0; +} + +/* + * FUSE API + */ + +int fuse_version(void) +{ + return (FUSE_VERSION); +} + +int fuse_main(int argc, char *argv[], + const struct fuse_operations *fsop, void *data) +{ + TRACE; + return -1; +} + +struct fuse* fuse_new(struct fuse_chan *chan, struct fuse_args *args, + const struct fuse_operations *fsop, size_t size, + void *userdata) +{ + TRACE; + + _fuse = reinterpret_cast(malloc(sizeof (struct fuse))); + if (_fuse == 0) { + PERR("could not create struct fuse"); + return 0; + } + + _fuse->fc = chan; + _fuse->args = args; + + Genode::memcpy(&_fuse->op, fsop, Genode::min(size, sizeof (_fuse->op))); + + FIX_UP_OPERATION1(_fuse, readlink); + FIX_UP_OPERATION1(_fuse, mknod); + FIX_UP_OPERATION1(_fuse, unlink); + FIX_UP_OPERATION1(_fuse, rmdir); + FIX_UP_OPERATION1(_fuse, symlink); + FIX_UP_OPERATION1(_fuse, rename); + FIX_UP_OPERATION1(_fuse, link); + FIX_UP_OPERATION1(_fuse, chmod); + FIX_UP_OPERATION1(_fuse, chown); + FIX_UP_OPERATION1(_fuse, truncate); + FIX_UP_OPERATION1(_fuse, utime); + FIX_UP_OPERATION2(_fuse, open); + FIX_UP_OPERATION1(_fuse, read); + FIX_UP_OPERATION1(_fuse, write); + FIX_UP_OPERATION1(_fuse, statfs); + FIX_UP_OPERATION1(_fuse, flush); + FIX_UP_OPERATION1(_fuse, release); + FIX_UP_OPERATION1(_fuse, fsync); + FIX_UP_OPERATION1(_fuse, fsyncdir); + FIX_UP_OPERATION1(_fuse, access); + FIX_UP_OPERATION1(_fuse, create); + FIX_UP_OPERATION1(_fuse, utimens); + FIX_UP_OPERATION1(_fuse, read); + FIX_UP_OPERATION1(_fuse, read); + + if (_fuse->op.opendir == 0) + _fuse->op.opendir = _fuse->op.open; + if (_fuse->op.releasedir == 0) + _fuse->op.releasedir = _fuse->op.release; + if (_fuse->op.fgetattr == 0) + _fuse->op.fgetattr = _fuse->op.getattr; + if (_fuse->op.ftruncate == 0) + _fuse->op.ftruncate = _fuse->op.truncate; + + _fuse->filler = fill_dir; + + _fuse->userdata = userdata; + + _ctx.fuse = _fuse; + _ctx.uid = 0; + _ctx.gid = 0; + _ctx.pid = 0; + _ctx.private_data = userdata; + _ctx.umask = 022; + + _initialized = true; + + return _fuse; +} + +int fuse_parse_cmdline(struct fuse_args *args, char **mp, int *mt, int *fg) +{ + TRACE; + return -1; +} + +struct fuse_chan* fuse_mount(const char *dir, struct fuse_args *args) +{ + TRACE; + return 0; +} + +void fuse_remove_signal_handlers(struct fuse_session *se) +{ + TRACE; +} + +int fuse_set_signal_handlers(struct fuse_session *se) +{ + TRACE; + return -1; +} + +struct fuse_session* fuse_get_session(struct fuse *f) +{ + TRACE; + return 0; +} + +struct fuse_context* fuse_get_context(void) +{ + return &_ctx; +} + +int fuse_is_lib_option(const char *opt) +{ + TRACE; + return -1; +} + +int fuse_loop(struct fuse *fuse) +{ + TRACE; + return -1; +} + +int fuse_loop_mt(struct fuse *fuse) +{ + TRACE; + return -1; +} + +int fuse_chan_fd(struct fuse_chan *ch) +{ + TRACE; + return -1; +} + +void fuse_unmount(const char *dir, struct fuse_chan *ch) +{ + TRACE; +} + +int fuse_daemonize(int foreground) +{ + TRACE; + return -1; +} + +void fuse_destroy(struct fuse *f) +{ + TRACE; + + free(f); +} + +void fuse_teardown(struct fuse *f, char *mp) +{ + TRACE; +} + +int fuse_opt_add_arg(struct fuse_args *, const char *) +{ + TRACE; + return -1; +} + +void fuse_opt_free_args(struct fuse_args *) +{ + TRACE; +} + +} /* extern "C" */ diff --git a/libports/src/lib/libc_fuse/plugin.cc b/libports/src/lib/libc_fuse/plugin.cc new file mode 100644 index 000000000..bb0a4a02c --- /dev/null +++ b/libports/src/lib/libc_fuse/plugin.cc @@ -0,0 +1,548 @@ +/* + * \brief Libc libfuse plugin + * \author Josef Soentgen + * \date 2013-11-07 + */ + +/* + * Copyright (C) 2013 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include + +/* libc plugin includes */ +#include +#include + +/* fuse */ +#include + +/* libc includes */ +#include +#include +#include +#include +#include + +using namespace Genode; + +void *operator new (size_t, void *ptr) { return ptr; } + +#define PDBGV(...) if (verbose) PDBG(__VA_ARGS__) +static bool const verbose = false; + +/* a little helper to prevent code duplication */ +static inline int check_result(int res) +{ + if (res < 0) { + /** + * FUSE file systems always return -errno as result + * if something went wrong. + */ + errno = -res; + return -1; + } + + return 0; +} + + +/*************************** + ** override libc defaults ** + ***************************/ + +/* +extern "C" int access(const char *pathname, int mode) +{ + PDBGV("pathname: %s", pathname); + return check_result(Fuse::fuse()->op.access(pathname, mode)); +} +*/ + +extern "C" int chmod(const char *path, mode_t mode) +{ + PDBGV("path: %s", path); + return check_result(Fuse::fuse()->op.chmod(path, mode)); +} + + +extern "C" int chown(const char *path, uid_t uid, gid_t gid) +{ + PDBGV("path: %s", path); + return check_result(Fuse::fuse()->op.chown(path, uid, gid)); +} + + +extern "C" int link(const char *oldpath, const char *newpath) +{ + PDBGV("oldpath: %s", oldpath); + return check_result(Fuse::fuse()->op.link(oldpath, newpath)); +} + + +/************ + ** Plugin ** + ************/ + +namespace { + + struct Plugin_context : Libc::Plugin_context + { + String<4096> path; + int flags; + int fd_flags; + struct fuse_file_info file_info; + + ::off_t offset; + + Plugin_context(const char *p, int f) + : + path(p), flags(f), offset(0) + { + Genode::memset(&file_info, 0, sizeof (struct fuse_file_info)); + } + + ~Plugin_context() { } + }; + + static inline Plugin_context *context(Libc::File_descriptor *fd) + { + return static_cast(fd->context); + } + + class Plugin : public Libc::Plugin + { + private: + + public: + + /** + * Constructor + */ + Plugin() + { + Fuse::init_fs(); + } + + ~Plugin() + { + Fuse::deinit_fs(); + } + + bool supports_mkdir(const char *path, mode_t mode) + { + PDBGV("path: %s", path); + if (Fuse::initialized() == 0) + return false; + + return true; + } + + bool supports_open(const char *pathname, int flags) + { + PDBGV("pathname: %s", pathname); + + if (Genode::strcmp(pathname, "/dev/blkdev") == 0) + return false; + + if (Fuse::initialized() == 0) + return false; + + return true; + } + + bool supports_readlink(const char *path, char *buf, ::size_t bufsiz) + { + PDBGV("path: %s", path); + if (Fuse::initialized() == 0) + return false; + + return true; + } + + bool supports_stat(const char *path) + { + PDBGV("path: %s", path); + if (Fuse::initialized() == 0) + return false; + + return true; + } + + bool supports_symlink(const char *oldpath, const char *newpath) + { + PDBGV("path: %s", oldpath); + if (Fuse::fuse() == 0) + return false; + return true; + } + + bool supports_unlink(const char *path) + { + PDBGV("path: %s", path); + if (Fuse::fuse() == 0) + return false; + return true; + } + + + int close(Libc::File_descriptor *fd) + { + Plugin_context *ctx = context(fd); + + PDBGV("path: %s", ctx->path.string()); + + Fuse::fuse()->op.release(ctx->path.string(), &ctx->file_info); + + destroy(env()->heap(), ctx); + Libc::file_descriptor_allocator()->free(fd); + + return 0; + } + + int fcntl(Libc::File_descriptor *fd, int cmd, long arg) + { + Plugin_context *ctx = context(fd); + + switch (cmd) { + case F_GETFD: + return ctx->fd_flags; + case F_GETFL: + return ctx->flags; + case F_SETFD: + ctx->fd_flags = arg; + return 0; + default: + PDBG("cmd %d not supported", cmd); + return -1; + } + + return -1; /* never reached */ + } + + int fstat(Libc::File_descriptor *fd, struct stat *buf) + { + Plugin_context *ctx = context(fd); + + PDBGV("path: %s", ctx->path.string()); + + Genode::memset(buf, 0, sizeof (struct stat)); + + int res = Fuse::fuse()->op.getattr(ctx->path.string(), buf); + if (res != 0) { + errno = -res; + return -1; + } + + return 0; + } + + int fstatfs(Libc::File_descriptor *fd, struct statfs *buf) + { + Plugin_context *ctx = context(fd); + + PDBGV("path: %s", ctx->path.string()); + + struct statvfs vfs; + + int res = Fuse::fuse()->op.statfs(ctx->path.string(), &vfs); + if (res != 0) { + errno = -res; + return -1; + } + + Genode::memset(buf, 0, sizeof (struct statfs)); + + buf->f_bsize = vfs.f_bsize; + //buf->f_frsize = vfs.f_frsize; + buf->f_blocks = vfs.f_blocks; + buf->f_bavail = vfs.f_bavail; + buf->f_bfree = vfs.f_bfree; + buf->f_namemax = vfs.f_namemax; + buf->f_files = vfs.f_files; + //buf->f_favail = vfs.f_favail; + buf->f_ffree = vfs.f_ffree; + + return 0; + } + + int ftruncate(Libc::File_descriptor *fd, ::off_t length) + { + Plugin_context *ctx = context(fd); + + PDBGV("path: %s", ctx->path.string()); + + int res = Fuse::fuse()->op.ftruncate(ctx->path.string(), length, + &ctx->file_info); + if (res != 0) { + errno = -res; + return -1; + } + + return 0; + } + + ::ssize_t getdirentries(Libc::File_descriptor *fd, char *buf, ::size_t nbytes, + ::off_t *basep) + { + Plugin_context *ctx = context(fd); + PDBGV("path: %s", ctx->path.string()); + + if (nbytes < sizeof (struct dirent)) { + PERR("buf too small"); + errno = ENOMEM; + return -1; + } + + struct dirent *de = (struct dirent *)buf; + ::memset(de, 0, sizeof (struct dirent)); + + struct fuse_dirhandle dh = { + .filler = Fuse::fuse()->filler, + .buf = buf, + .size = nbytes, + .offset = 0, + }; + + int res = Fuse::fuse()->op.readdir(ctx->path.string(), &dh, + Fuse::fuse()->filler, 0, + &ctx->file_info); + if (res != 0) { + errno = -res; + return -1; + } + + /** + * We have to stat(2) each entry because there are FUSE file + * systems which do not provide a valid struct stat entry in + * its readdir() implementation because only d_ino and d_name + * are specified by POSIX. + */ + ::off_t offset = 0; + while (offset < dh.offset) { + struct dirent *entry = (struct dirent*)(buf + offset); + + /* try to query the type of the file if the type is unknown */ + if (entry->d_type == DT_UNKNOWN) { + Genode::Path<4096> path(entry->d_name, ctx->path.string()); + struct stat sbuf; + res = Fuse::fuse()->op.getattr(path.base(), &sbuf); + if (res == 0) { + entry->d_type = IFTODT(sbuf.st_mode); + entry->d_fileno = sbuf.st_ino ? sbuf.st_ino : 1; + } + } + + offset += sizeof (struct dirent); + } + + /** + * To prevent the libc from being stuck in an endless loop we + * append an empty entry. This is a rather hacky solution but + * for now it suffice. + */ + dh.offset += sizeof (struct dirent); + ((struct dirent*)(buf + dh.offset))->d_reclen = 0; + + return dh.offset; + } + + ::off_t lseek(Libc::File_descriptor *fd, ::off_t offset, int whence) + { + Plugin_context *ctx = context(fd); + PDBGV("path: %s", ctx->path.string()); + + switch (whence) { + case SEEK_SET: + ctx->offset = offset; + return ctx->offset; + + case SEEK_CUR: + ctx->offset += offset; + return ctx->offset; + + case SEEK_END: + if (offset != 0) { + errno = EINVAL; + return -1; + } + ctx->offset = ~0L; + + return (Fuse::fuse()->block_size * Fuse::fuse()->block_count); + + default: + errno = EINVAL; + return -1; + } + } + + int mkdir(const char *pathname, mode_t mode) + { + PDBGV("pathname: %s", pathname); + int res = Fuse::fuse()->op.mkdir(pathname, mode); + + return check_result(res); + } + + Libc::File_descriptor *open(const char *pathname, int flags) + { + /* XXX evaluate flags */ + PDBGV("pathname: %s", pathname); + + Plugin_context *context = new (Genode::env()->heap()) + Plugin_context(pathname, flags); + + int res; + int tries = 0; + do { + /* first try to open pathname */ + res = Fuse::fuse()->op.open(pathname, &context->file_info); + if (res == 0) { + break; + } + + /* try to create pathname if open failed and O_CREAT flag was specified */ + if (flags & O_CREAT && !tries) { + mode_t mode = S_IFREG | 0644; + int res = Fuse::fuse()->op.mknod(pathname, mode, 0); + switch (res) { + case 0: + break; + default: + PERR("could not create '%s'", pathname); + destroy(env()->heap(), context); + return 0; + } + + tries++; + continue; + } + + if (res < 0) { + errno = -res; + destroy(env()->heap(), context); + return 0; + } + } + while (true); + + if (flags & O_TRUNC) { + res = Fuse::fuse()->op.ftruncate(pathname, 0, + &context->file_info); + if (res != 0) { + errno = -res; + Fuse::fuse()->op.release(context->path.string(), + &context->file_info); + destroy(env()->heap(), context); + return 0; + } + } + + context->file_info.flags = flags; + + return Libc::file_descriptor_allocator()->alloc(this, context); + } + + ssize_t read(Libc::File_descriptor *fd, void *buf, ::size_t count) + { + Plugin_context *ctx = context(fd); + PDBGV("path: %s", ctx->path.string()); + + int res = Fuse::fuse()->op.read(ctx->path.string(), + reinterpret_cast(buf), + count, ctx->offset, &ctx->file_info); + + if (check_result(res)) + return -1; + + ctx->offset += res; + + return res; + } + + ssize_t readlink(const char *path, char *buf, ::size_t bufsiz) + { + PDBGV("path: %s", path); + + int res = Fuse::fuse()->op.readlink(path, buf, bufsiz); + if (res < 0) { + errno = -res; + return -1; + } + + /** + * We have to trust each FUSE file system to append a + * null byte to buf, which is required according to + * FUSEs documentation. + */ + return Genode::strlen(buf); + } + + int rename(const char *oldpath, const char *newpath) + { + PDBGV("oldpath: %s newpath: %s", oldpath, newpath); + int res = Fuse::fuse()->op.rename(oldpath, newpath); + + return check_result(res); + } + + int stat(const char *path, struct stat *buf) + { + PDBGV("path: %s", path); + Genode::memset(buf, 0, sizeof (buf)); + + int res = Fuse::fuse()->op.getattr(path, buf); + + return check_result(res); + } + + int symlink(const char *oldpath, const char *newpath) + { + PDBGV("oldpath: %s newpath: %s", oldpath, newpath); + int res = Fuse::fuse()->op.symlink(oldpath, newpath); + + return check_result(res); + } + + int unlink(const char *path) + { + PDBGV("path: %s", path); + int res = Fuse::fuse()->op.unlink(path); + + return check_result(res); + } + + ssize_t write(Libc::File_descriptor *fd, const void *buf, ::size_t count) + { + Plugin_context *ctx = context(fd); + + PDBGV("path: %s", ctx->path.string()); + int res = Fuse::fuse()->op.write(ctx->path.string(), + reinterpret_cast(buf), + count, ctx->offset, &ctx->file_info); + + if (check_result(res)) + return -1; + + ctx->offset += res; + + return res; + } + + }; + +} /* unnamed namespace */ + +void __attribute__((constructor)) init_libc_fuse(void) +{ + PDBGV("using the libc_fuse plugin"); + static Plugin plugin; +}