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.
This commit is contained in:
Josef Söntgen 2013-11-11 15:58:59 +01:00 committed by Norman Feske
parent 2891653f8a
commit 3a0922001b
8 changed files with 1200 additions and 0 deletions

View File

@ -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 <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/types.h>
#include <fcntl.h>
#include <utime.h>
#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_ */

View File

@ -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_ */

View File

@ -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_ */

View File

@ -0,0 +1 @@
REP_INC_DIR += include/fuse

View File

@ -0,0 +1,5 @@
LIBS = libc libfuse
SRC_CC = plugin.cc
vpath %.cc $(REP_DIR)/src/lib/libc_fuse

View File

@ -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

View File

@ -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 <base/printf.h>
#include <util/string.h>
/* libc includes */
#include <stdlib.h>
#include <errno.h>
#include <dirent.h>
#include <fuse.h>
#include <fuse_private.h>
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<struct fuse*>(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" */

View File

@ -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 <base/allocator_avl.h>
#include <base/env.h>
#include <base/printf.h>
#include <util/string.h>
#include <os/path.h>
/* libc plugin includes */
#include <libc-plugin/plugin.h>
#include <libc-plugin/fd_alloc.h>
/* fuse */
#include <fuse_private.h>
/* libc includes */
#include <sys/statvfs.h>
#include <sys/dirent.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
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<Plugin_context *>(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<char*>(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<const char*>(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;
}