#include <sys/uio.h>
#include <errno.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "debug.h"
#include "fuse_private.h"
enum {
KEY_HELP,
KEY_VERSION,
KEY_DEBUG
};
static const struct fuse_opt fuse_ll_opts[] = {
FUSE_OPT_KEY("debug", KEY_DEBUG),
FUSE_OPT_KEY("-d", KEY_DEBUG),
FUSE_OPT_KEY("-h", KEY_HELP),
FUSE_OPT_KEY("--help", KEY_HELP),
FUSE_OPT_KEY("-V", KEY_VERSION),
FUSE_OPT_KEY("--version", KEY_VERSION),
FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_DISCARD),
FUSE_OPT_END
};
static void
dump_version(void)
{
fprintf(stderr, "FUSE library version: %d.%d\n", FUSE_MAJOR_VERSION,
FUSE_MINOR_VERSION);
}
static void
dump_help(void)
{
fprintf(stderr, " -o max_write=N max buffer size for "
"write operations\n"
);
}
static int
ifuse_ll_opt_proc(void *data, const char *arg, int key,
struct fuse_args *outargs)
{
switch (key) {
case KEY_HELP:
dump_help();
break;
case KEY_VERSION:
dump_version();
break;
case KEY_DEBUG:
ifuse_debug_init();
return (1);
default:
fprintf(stderr, "fuse: unknown option -- %s\n", arg);
}
return -1;
}
struct fuse_session *
fuse_lowlevel_new(struct fuse_args *fargs,
const struct fuse_lowlevel_ops *llops, const size_t llops_len,
void *userdata)
{
struct fuse_session *se;
se = calloc(1, sizeof(*se));
if (se == NULL)
return (NULL);
if (fuse_opt_parse(fargs, NULL, fuse_ll_opts, ifuse_ll_opt_proc) == -1) {
free(se);
return (NULL);
}
if (llops->create && !llops->mknod)
DPRINTF("libfuse: WARNING: filesystem supports creating files "
"but does not implement mknod. No new files can be "
"created.\n");
if (sizeof(se->llops) == llops_len)
memcpy(&se->llops, llops, sizeof(se->llops));
else {
free(se);
return (NULL);
}
se->userdata = userdata;
return (se);
}
DEF(fuse_lowlevel_new);
static int
ifuse_reply(fuse_req_t req, const char *data, const size_t data_size, int err)
{
struct fusebuf *fbuf;
struct iovec iov[2];
size_t fbuf_size;
if (data == NULL && data_size > 0) {
DPRINTF("\nlibfuse: NULL data with size: %zu\n", data_size);
fuse_reply_err(req, EIO);
return (-EINVAL);
}
fbuf = req->fbuf;
fbuf_size = sizeof(fbuf->fb_hdr) + sizeof(fbuf->FD);
fbuf->fb_err = err;
fbuf->fb_len = data_size;
iov[0].iov_base = fbuf;
iov[0].iov_len = fbuf_size;
iov[1].iov_base = (void *)data;
iov[1].iov_len = data_size;
DPRINTF("errno: %d", fbuf->fb_err);
return fuse_chan_send(req->ch, iov, 2);
}
static int
ifuse_reply_ok(fuse_req_t req)
{
return ifuse_reply(req, NULL, 0, 0);
}
int
fuse_reply_err(fuse_req_t req, int err)
{
return ifuse_reply(req, NULL, 0, err);
}
DEF(fuse_reply_err);
int
fuse_reply_buf(fuse_req_t req, const char *buf, off_t size)
{
return ifuse_reply(req, buf, size, 0);
}
DEF(fuse_reply_buf);
int
fuse_reply_readlink(fuse_req_t req, char *path)
{
return ifuse_reply(req, path, strlen(path), 0);
}
DEF(fuse_reply_readlink);
int
fuse_reply_write(fuse_req_t req, size_t size)
{
struct fusebuf *fbuf;
fbuf = req->fbuf;
fbuf->fb_io_len = size;
return ifuse_reply_ok(req);
}
DEF(fuse_reply_write);
int
fuse_reply_attr(fuse_req_t req, const struct stat *stbuf, double attr_timeout)
{
struct fusebuf *fbuf;
fbuf = req->fbuf;
fbuf->fb_attr = *stbuf;
return ifuse_reply_ok(req);
}
DEF(fuse_reply_attr);
int
fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
{
struct fusebuf *fbuf;
fbuf = req->fbuf;
fbuf->fb_attr = e->attr;
fbuf->fb_ino = e->ino;
DPRINTF("inode: %llu\t", e->ino);
return ifuse_reply_ok(req);
}
DEF(fuse_reply_entry);
int
fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
{
struct fusebuf *fbuf;
fbuf = req->fbuf;
fbuf->fb_stat = *stbuf;
return ifuse_reply_ok(req);
}
DEF(fuse_reply_statfs);
int
fuse_reply_bmap(fuse_req_t req, uint64_t idx)
{
DPRINTF("%s: Unsupported", __func__);
return (-EOPNOTSUPP);
}
DEF(fuse_reply_bmap);
int
fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e,
const struct fuse_file_info *ffi)
{
DPRINTF("%s: Unsupported", __func__);
return (-EOPNOTSUPP);
}
DEF(fuse_reply_create);
int
fuse_reply_open(fuse_req_t req, const struct fuse_file_info *ffi)
{
struct fusebuf *fbuf;
fbuf = req->fbuf;
fbuf->fb_io_fd = ffi->fh;
return ifuse_reply_ok(req);
}
DEF(fuse_reply_open);
void
fuse_reply_none(fuse_req_t req)
{
}
DEF(fuse_reply_none);
#define GENERIC_DIRSIZ(NLEN) \
((sizeof (struct dirent) - (MAXNAMLEN+1)) + ((NLEN+1 + 7) &~ 7))
size_t
fuse_add_direntry(fuse_req_t req, char *buf, const size_t bufsize,
const char *name, const struct stat *stbuf, off_t off)
{
struct dirent *dir;
size_t namelen;
size_t len;
if (name == NULL)
return (0);
namelen = strnlen(name, MAXNAMLEN);
len = GENERIC_DIRSIZ(namelen);
if (buf == NULL || stbuf == NULL || req == NULL)
return (len);
if (bufsize < len)
return (len);
dir = (struct dirent *)buf;
dir->d_fileno = stbuf->st_ino;
dir->d_type = IFTODT(stbuf->st_mode);
dir->d_reclen = len;
dir->d_off = off;
strlcpy(dir->d_name, name, sizeof(dir->d_name));
dir->d_namlen = strlen(dir->d_name);
return (len);
}
DEF(fuse_add_direntry);
const struct fuse_ctx *
fuse_req_ctx(fuse_req_t req)
{
return (&req->ctx);
}
DEF(fuse_req_ctx);
void *
fuse_req_userdata(fuse_req_t req)
{
return (req->se->userdata);
}
DEF(fuse_req_userdata);