This file system mirrors the existing file system hierarchy of the system, starting at the root file system. This is implemented by just "passing through" all requests to the corresponding user-space libc functions. In contrast to passthrough.c and passthrough_fh.c, this implementation uses the low-level API. Its performance should be the least bad among the three, but many operations are not implemented. In particular, it is not possible to remove files (or directories) because the code necessary to defer actual removal until the file is not opened anymore would make the example much more complicated.
When writeback caching is enabled (-o writeback mount option), it is only possible to write to files for which the mounting user has read permissions. This is because the writeback cache requires the kernel to be able to issue read requests for all files (which the passthrough filesystem cannot satisfy if it can't read the file in the underlying filesystem).
#define _GNU_SOURCE
#define FUSE_USE_VERSION 34
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <limits.h>
#include <dirent.h>
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <pthread.h>
#include <sys/file.h>
#include <sys/xattr.h>
#include "passthrough_helpers.h"
#if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && !defined __cplusplus
_Static_assert(
sizeof(
fuse_ino_t) >=
sizeof(uintptr_t),
"fuse_ino_t too small to hold uintptr_t values!");
#else
struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct \
{ unsigned _uintptr_to_must_hold_fuse_ino_t:
((
sizeof(
fuse_ino_t) >=
sizeof(uintptr_t)) ? 1 : -1); };
#endif
struct lo_inode {
struct lo_inode *next;
struct lo_inode *prev;
int fd;
ino_t ino;
dev_t dev;
uint64_t refcount;
};
enum {
CACHE_NEVER,
CACHE_NORMAL,
CACHE_ALWAYS,
};
struct lo_data {
pthread_mutex_t mutex;
int debug;
int writeback;
int flock;
int xattr;
char *source;
double timeout;
int cache;
int timeout_set;
struct lo_inode root;
};
static const struct fuse_opt lo_opts[] = {
{ "writeback",
offsetof(struct lo_data, writeback), 1 },
{ "no_writeback",
offsetof(struct lo_data, writeback), 0 },
{ "source=%s",
offsetof(struct lo_data, source), 0 },
{ "flock",
offsetof(struct lo_data, flock), 1 },
{ "no_flock",
offsetof(struct lo_data, flock), 0 },
{ "xattr",
offsetof(struct lo_data, xattr), 1 },
{ "no_xattr",
offsetof(struct lo_data, xattr), 0 },
{ "timeout=%lf",
offsetof(struct lo_data, timeout), 0 },
{ "timeout=",
offsetof(struct lo_data, timeout_set), 1 },
{ "cache=never",
offsetof(struct lo_data, cache), CACHE_NEVER },
{ "cache=auto",
offsetof(struct lo_data, cache), CACHE_NORMAL },
{ "cache=always",
offsetof(struct lo_data, cache), CACHE_ALWAYS },
};
static void passthrough_ll_help(void)
{
printf(
" -o writeback Enable writeback\n"
" -o no_writeback Disable write back\n"
" -o source=/home/dir Source directory to be mounted\n"
" -o flock Enable flock\n"
" -o no_flock Disable flock\n"
" -o xattr Enable xattr\n"
" -o no_xattr Disable xattr\n"
" -o timeout=1.0 Caching timeout\n"
" -o timeout=0/1 Timeout is set\n"
" -o cache=never Disable cache\n"
" -o cache=auto Auto enable cache\n"
" -o cache=always Cache always\n");
}
{
}
{
return &lo_data(req)->root;
else
return (struct lo_inode *) (uintptr_t) ino;
}
{
return lo_inode(req, ino)->fd;
}
{
return lo_data(req)->debug != 0;
}
static void lo_init(void *userdata,
{
struct lo_data *lo = (struct lo_data*) userdata;
if (lo->writeback &&
if (lo->debug)
fuse_log(FUSE_LOG_DEBUG,
"lo_init: activating writeback\n");
}
if (lo->debug)
fuse_log(FUSE_LOG_DEBUG,
"lo_init: activating flock locks\n");
}
}
static void lo_destroy(void *userdata)
{
struct lo_data *lo = (struct lo_data*) userdata;
while (lo->root.next != &lo->root) {
struct lo_inode* next = lo->root.next;
lo->root.next = next->next;
free(next);
}
}
{
int res;
struct stat buf;
struct lo_data *lo = lo_data(req);
(void) fi;
res = fstatat(lo_fd(req, ino), "", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
if (res == -1)
}
{
int saverr;
char procname[64];
struct lo_inode *inode = lo_inode(req, ino);
int ifd = inode->fd;
int res;
if (valid & FUSE_SET_ATTR_MODE) {
if (fi) {
res = fchmod(fi->
fh, attr->st_mode);
} else {
sprintf(procname, "/proc/self/fd/%i", ifd);
res = chmod(procname, attr->st_mode);
}
if (res == -1)
goto out_err;
}
if (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)) {
uid_t uid = (valid & FUSE_SET_ATTR_UID) ?
attr->st_uid : (uid_t) -1;
gid_t gid = (valid & FUSE_SET_ATTR_GID) ?
attr->st_gid : (gid_t) -1;
res = fchownat(ifd, "", uid, gid,
AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
if (res == -1)
goto out_err;
}
if (valid & FUSE_SET_ATTR_SIZE) {
if (fi) {
res = ftruncate(fi->
fh, attr->st_size);
} else {
sprintf(procname, "/proc/self/fd/%i", ifd);
res = truncate(procname, attr->st_size);
}
if (res == -1)
goto out_err;
}
if (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) {
struct timespec tv[2];
tv[0].tv_sec = 0;
tv[1].tv_sec = 0;
tv[0].tv_nsec = UTIME_OMIT;
tv[1].tv_nsec = UTIME_OMIT;
if (valid & FUSE_SET_ATTR_ATIME_NOW)
tv[0].tv_nsec = UTIME_NOW;
else if (valid & FUSE_SET_ATTR_ATIME)
tv[0] = attr->st_atim;
if (valid & FUSE_SET_ATTR_MTIME_NOW)
tv[1].tv_nsec = UTIME_NOW;
else if (valid & FUSE_SET_ATTR_MTIME)
tv[1] = attr->st_mtim;
if (fi)
res = futimens(fi->
fh, tv);
else {
sprintf(procname, "/proc/self/fd/%i", ifd);
res = utimensat(AT_FDCWD, procname, tv, 0);
}
if (res == -1)
goto out_err;
}
return lo_getattr(req, ino, fi);
out_err:
saverr = errno;
}
static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st)
{
struct lo_inode *p;
struct lo_inode *ret = NULL;
pthread_mutex_lock(&lo->mutex);
for (p = lo->root.next; p != &lo->root; p = p->next) {
if (p->ino == st->st_ino && p->dev == st->st_dev) {
assert(p->refcount > 0);
ret = p;
ret->refcount++;
break;
}
}
pthread_mutex_unlock(&lo->mutex);
return ret;
}
{
int newfd;
int res;
int saverr;
struct lo_data *lo = lo_data(req);
struct lo_inode *inode;
memset(e, 0, sizeof(*e));
newfd = openat(lo_fd(req, parent), name, O_PATH | O_NOFOLLOW);
if (newfd == -1)
goto out_err;
res = fstatat(newfd,
"", &e->
attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
if (res == -1)
goto out_err;
inode = lo_find(lo_data(req), &e->
attr);
if (inode) {
close(newfd);
newfd = -1;
} else {
struct lo_inode *prev, *next;
saverr = ENOMEM;
inode = calloc(1, sizeof(struct lo_inode));
if (!inode)
goto out_err;
inode->refcount = 1;
inode->fd = newfd;
inode->ino = e->
attr.st_ino;
inode->dev = e->
attr.st_dev;
pthread_mutex_lock(&lo->mutex);
prev = &lo->root;
next = prev->next;
next->prev = inode;
inode->next = next;
inode->prev = prev;
prev->next = inode;
pthread_mutex_unlock(&lo->mutex);
}
e->
ino = (uintptr_t) inode;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG,
" %lli/%s -> %lli\n",
(
unsigned long long) parent, name, (
unsigned long long) e->
ino);
return 0;
out_err:
saverr = errno;
if (newfd != -1)
close(newfd);
return saverr;
}
{
int err;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG,
"lo_lookup(parent=%" PRIu64
", name=%s)\n",
parent, name);
err = lo_do_lookup(req, parent, name, &e);
if (err)
else
}
const char *name, mode_t mode, dev_t rdev,
const char *link)
{
int res;
int saverr;
struct lo_inode *dir = lo_inode(req, parent);
res = mknod_wrapper(dir->fd, name, link, mode, rdev);
saverr = errno;
if (res == -1)
goto out;
saverr = lo_do_lookup(req, parent, name, &e);
if (saverr)
goto out;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG,
" %lli/%s -> %lli\n",
(
unsigned long long) parent, name, (
unsigned long long) e.
ino);
return;
out:
}
const char *name, mode_t mode, dev_t rdev)
{
lo_mknod_symlink(req, parent, name, mode, rdev, NULL);
}
mode_t mode)
{
lo_mknod_symlink(req, parent, name, S_IFDIR | mode, 0, NULL);
}
static void lo_symlink(
fuse_req_t req,
const char *link,
{
lo_mknod_symlink(req, parent, name, S_IFLNK, 0, link);
}
const char *name)
{
int res;
struct lo_data *lo = lo_data(req);
struct lo_inode *inode = lo_inode(req, ino);
char procname[64];
int saverr;
sprintf(procname, "/proc/self/fd/%i", inode->fd);
res = linkat(AT_FDCWD, procname, lo_fd(req, parent), name,
AT_SYMLINK_FOLLOW);
if (res == -1)
goto out_err;
res = fstatat(inode->fd,
"", &e.
attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
if (res == -1)
goto out_err;
pthread_mutex_lock(&lo->mutex);
inode->refcount++;
pthread_mutex_unlock(&lo->mutex);
e.
ino = (uintptr_t) inode;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG,
" %lli/%s -> %lli\n",
(unsigned long long) parent, name,
(
unsigned long long) e.
ino);
return;
out_err:
saverr = errno;
}
{
int res;
res = unlinkat(lo_fd(req, parent), name, AT_REMOVEDIR);
}
unsigned int flags)
{
int res;
if (flags) {
return;
}
res = renameat(lo_fd(req, parent), name,
lo_fd(req, newparent), newname);
}
{
int res;
res = unlinkat(lo_fd(req, parent), name, 0);
}
static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n)
{
if (!inode)
return;
pthread_mutex_lock(&lo->mutex);
assert(inode->refcount >= n);
inode->refcount -= n;
if (!inode->refcount) {
struct lo_inode *prev, *next;
prev = inode->prev;
next = inode->next;
next->prev = prev;
prev->next = next;
pthread_mutex_unlock(&lo->mutex);
close(inode->fd);
free(inode);
} else {
pthread_mutex_unlock(&lo->mutex);
}
}
{
struct lo_data *lo = lo_data(req);
struct lo_inode *inode = lo_inode(req, ino);
if (lo_debug(req)) {
fuse_log(FUSE_LOG_DEBUG,
" forget %lli %lli -%lli\n",
(unsigned long long) ino,
(unsigned long long) inode->refcount,
(unsigned long long) nlookup);
}
unref_inode(lo, inode, nlookup);
}
{
lo_forget_one(req, ino, nlookup);
}
static void lo_forget_multi(
fuse_req_t req,
size_t count,
struct fuse_forget_data *forgets)
{
int i;
for (i = 0; i < count; i++)
lo_forget_one(req, forgets[i].ino, forgets[i].nlookup);
}
{
char buf[PATH_MAX + 1];
int res;
res = readlinkat(lo_fd(req, ino), "", buf, sizeof(buf));
if (res == -1)
if (res == sizeof(buf))
buf[res] = '\0';
}
struct lo_dirp {
DIR *dp;
struct dirent *entry;
off_t offset;
};
{
return (
struct lo_dirp *) (uintptr_t) fi->
fh;
}
{
int error = ENOMEM;
struct lo_data *lo = lo_data(req);
struct lo_dirp *d;
int fd;
d = calloc(1, sizeof(struct lo_dirp));
if (d == NULL)
goto out_err;
fd = openat(lo_fd(req, ino), ".", O_RDONLY);
if (fd == -1)
goto out_errno;
d->dp = fdopendir(fd);
if (d->dp == NULL)
goto out_errno;
d->offset = 0;
d->entry = NULL;
if (lo->cache == CACHE_ALWAYS)
return;
out_errno:
error = errno;
out_err:
if (d) {
if (fd != -1)
close(fd);
free(d);
}
}
static int is_dot_or_dotdot(const char *name)
{
return name[0] == '.' && (name[1] == '\0' ||
(name[1] == '.' && name[2] == '\0'));
}
{
struct lo_dirp *d = lo_dirp(fi);
char *buf;
char *p;
size_t rem = size;
int err;
(void) ino;
buf = calloc(1, size);
if (!buf) {
err = ENOMEM;
goto error;
}
p = buf;
if (offset != d->offset) {
seekdir(d->dp, offset);
d->entry = NULL;
d->offset = offset;
}
while (1) {
size_t entsize;
off_t nextoff;
const char *name;
if (!d->entry) {
errno = 0;
d->entry = readdir(d->dp);
if (!d->entry) {
if (errno) {
err = errno;
goto error;
} else {
break;
}
}
}
nextoff = d->entry->d_off;
name = d->entry->d_name;
if (plus) {
if (is_dot_or_dotdot(name)) {
.
attr.st_ino = d->entry->d_ino,
.attr.st_mode = d->entry->d_type << 12,
};
} else {
err = lo_do_lookup(req, ino, name, &e);
if (err)
goto error;
}
&e, nextoff);
} else {
struct stat st = {
.st_ino = d->entry->d_ino,
.st_mode = d->entry->d_type << 12,
};
&st, nextoff);
}
if (entsize > rem) {
if (entry_ino != 0)
lo_forget_one(req, entry_ino, 1);
break;
}
p += entsize;
rem -= entsize;
d->entry = NULL;
d->offset = nextoff;
}
err = 0;
error:
if (err && rem == size)
else
free(buf);
}
{
lo_do_readdir(req, ino, size, offset, fi, 0);
}
{
lo_do_readdir(req, ino, size, offset, fi, 1);
}
{
struct lo_dirp *d = lo_dirp(fi);
(void) ino;
closedir(d->dp);
free(d);
}
{
int fd;
struct lo_data *lo = lo_data(req);
int err;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG,
"lo_create(parent=%" PRIu64
", name=%s)\n",
parent, name);
fd = openat(lo_fd(req, parent), name,
(fi->
flags | O_CREAT) & ~O_NOFOLLOW, mode);
if (fd == -1)
if (lo->cache == CACHE_NEVER)
else if (lo->cache == CACHE_ALWAYS)
err = lo_do_lookup(req, parent, name, &e);
if (err)
else
}
{
int res;
int fd = dirfd(lo_dirp(fi)->dp);
if (datasync)
res = fdatasync(fd);
else
res = fsync(fd);
}
{
int fd;
char buf[64];
struct lo_data *lo = lo_data(req);
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG,
"lo_open(ino=%" PRIu64
", flags=%d)\n",
if (lo->writeback && (fi->
flags & O_ACCMODE) == O_WRONLY) {
}
if (lo->writeback && (fi->
flags & O_APPEND))
sprintf(buf, "/proc/self/fd/%i", lo_fd(req, ino));
fd = open(buf, fi->
flags & ~O_NOFOLLOW);
if (fd == -1)
if (lo->cache == CACHE_NEVER)
else if (lo->cache == CACHE_ALWAYS)
}
{
(void) ino;
}
{
int res;
(void) ino;
res = close(dup(fi->
fh));
}
{
int res;
(void) ino;
if (datasync)
else
}
{
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG,
"lo_read(ino=%" PRIu64
", size=%zd, "
"off=%lu)\n", ino, size, (unsigned long) offset);
}
{
(void) ino;
ssize_t res;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG,
"lo_write(ino=%" PRIu64
", size=%zd, off=%lu)\n",
if(res < 0)
else
}
{
int res;
struct statvfs stbuf;
res = fstatvfs(lo_fd(req, ino), &stbuf);
if (res == -1)
else
}
{
int err = EOPNOTSUPP;
(void) ino;
#ifdef HAVE_FALLOCATE
err = fallocate(fi->
fh, mode, offset, length);
if (err < 0)
err = errno;
#elif defined(HAVE_POSIX_FALLOCATE)
if (mode) {
return;
}
err = posix_fallocate(fi->
fh, offset, length);
#endif
}
int op)
{
int res;
(void) ino;
}
size_t size)
{
char *value = NULL;
char procname[64];
struct lo_inode *inode = lo_inode(req, ino);
ssize_t ret;
int saverr;
saverr = ENOSYS;
if (!lo_data(req)->xattr)
goto out;
if (lo_debug(req)) {
fuse_log(FUSE_LOG_DEBUG,
"lo_getxattr(ino=%" PRIu64
", name=%s size=%zd)\n",
ino, name, size);
}
sprintf(procname, "/proc/self/fd/%i", inode->fd);
if (size) {
value = malloc(size);
if (!value)
goto out_err;
ret = getxattr(procname, name, value, size);
if (ret == -1)
goto out_err;
saverr = 0;
if (ret == 0)
goto out;
} else {
ret = getxattr(procname, name, NULL, 0);
if (ret == -1)
goto out_err;
}
out_free:
free(value);
return;
out_err:
saverr = errno;
out:
goto out_free;
}
{
char *value = NULL;
char procname[64];
struct lo_inode *inode = lo_inode(req, ino);
ssize_t ret;
int saverr;
saverr = ENOSYS;
if (!lo_data(req)->xattr)
goto out;
if (lo_debug(req)) {
fuse_log(FUSE_LOG_DEBUG,
"lo_listxattr(ino=%" PRIu64
", size=%zd)\n",
ino, size);
}
sprintf(procname, "/proc/self/fd/%i", inode->fd);
if (size) {
value = malloc(size);
if (!value)
goto out_err;
ret = listxattr(procname, value, size);
if (ret == -1)
goto out_err;
saverr = 0;
if (ret == 0)
goto out;
} else {
ret = listxattr(procname, NULL, 0);
if (ret == -1)
goto out_err;
}
out_free:
free(value);
return;
out_err:
saverr = errno;
out:
goto out_free;
}
const char *value, size_t size, int flags)
{
char procname[64];
struct lo_inode *inode = lo_inode(req, ino);
ssize_t ret;
int saverr;
saverr = ENOSYS;
if (!lo_data(req)->xattr)
goto out;
if (lo_debug(req)) {
fuse_log(FUSE_LOG_DEBUG,
"lo_setxattr(ino=%" PRIu64
", name=%s value=%s size=%zd)\n",
ino, name, value, size);
}
sprintf(procname, "/proc/self/fd/%i", inode->fd);
ret = setxattr(procname, name, value, size, flags);
saverr = ret == -1 ? errno : 0;
out:
}
{
char procname[64];
struct lo_inode *inode = lo_inode(req, ino);
ssize_t ret;
int saverr;
saverr = ENOSYS;
if (!lo_data(req)->xattr)
goto out;
if (lo_debug(req)) {
fuse_log(FUSE_LOG_DEBUG,
"lo_removexattr(ino=%" PRIu64
", name=%s)\n",
ino, name);
}
sprintf(procname, "/proc/self/fd/%i", inode->fd);
ret = removexattr(procname, name);
saverr = ret == -1 ? errno : 0;
out:
}
#ifdef HAVE_COPY_FILE_RANGE
int flags)
{
ssize_t res;
if (lo_debug(req))
fuse_log(FUSE_LOG_DEBUG,
"lo_copy_file_range(ino=%" PRIu64
"/fd=%lu, "
"off=%lu, ino=%" PRIu64 "/fd=%lu, "
"off=%lu, size=%zd, flags=0x%x)\n",
ino_in, fi_in->
fh, off_in, ino_out, fi_out->
fh, off_out,
len, flags);
res = copy_file_range(fi_in->
fh, &off_in, fi_out->
fh, &off_out, len,
flags);
if (res < 0)
else
}
#endif
{
off_t res;
(void)ino;
res = lseek(fi->
fh, off, whence);
if (res != -1)
else
}
.destroy = lo_destroy,
.lookup = lo_lookup,
.mkdir = lo_mkdir,
.mknod = lo_mknod,
.symlink = lo_symlink,
.link = lo_link,
.unlink = lo_unlink,
.rmdir = lo_rmdir,
.rename = lo_rename,
.forget = lo_forget,
.forget_multi = lo_forget_multi,
.getattr = lo_getattr,
.setattr = lo_setattr,
.readlink = lo_readlink,
.opendir = lo_opendir,
.readdir = lo_readdir,
.readdirplus = lo_readdirplus,
.releasedir = lo_releasedir,
.fsyncdir = lo_fsyncdir,
.create = lo_create,
.open = lo_open,
.release = lo_release,
.flush = lo_flush,
.fsync = lo_fsync,
.read = lo_read,
.write_buf = lo_write_buf,
.statfs = lo_statfs,
.fallocate = lo_fallocate,
.flock = lo_flock,
.getxattr = lo_getxattr,
.listxattr = lo_listxattr,
.setxattr = lo_setxattr,
.removexattr = lo_removexattr,
#ifdef HAVE_COPY_FILE_RANGE
.copy_file_range = lo_copy_file_range,
#endif
.lseek = lo_lseek,
};
int main(int argc, char *argv[])
{
struct fuse_session *se;
struct lo_data lo = { .debug = 0,
.writeback = 0 };
int ret = -1;
umask(0);
pthread_mutex_init(&lo.mutex, NULL);
lo.root.next = lo.root.prev = &lo.root;
lo.root.fd = -1;
lo.cache = CACHE_NORMAL;
if (fuse_parse_cmdline(&args, &opts) != 0)
return 1;
if (opts.show_help) {
printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
passthrough_ll_help();
ret = 0;
goto err_out1;
} else if (opts.show_version) {
ret = 0;
goto err_out1;
}
if(opts.mountpoint == NULL) {
printf("usage: %s [options] <mountpoint>\n", argv[0]);
printf(" %s --help\n", argv[0]);
ret = 1;
goto err_out1;
}
return 1;
lo.debug = opts.debug;
lo.root.refcount = 2;
if (lo.source) {
struct stat stat;
int res;
res = lstat(lo.source, &stat);
if (res == -1) {
fuse_log(FUSE_LOG_ERR,
"failed to stat source (\"%s\"): %m\n",
lo.source);
exit(1);
}
if (!S_ISDIR(stat.st_mode)) {
fuse_log(FUSE_LOG_ERR,
"source is not a directory\n");
exit(1);
}
} else {
lo.source = strdup("/");
if(!lo.source) {
fuse_log(FUSE_LOG_ERR,
"fuse: memory allocation failed\n");
exit(1);
}
}
if (!lo.timeout_set) {
switch (lo.cache) {
case CACHE_NEVER:
lo.timeout = 0.0;
break;
case CACHE_NORMAL:
lo.timeout = 1.0;
break;
case CACHE_ALWAYS:
lo.timeout = 86400.0;
break;
}
} else if (lo.timeout < 0) {
fuse_log(FUSE_LOG_ERR,
"timeout is negative (%lf)\n",
lo.timeout);
exit(1);
}
lo.root.fd = open(lo.source, O_PATH);
if (lo.root.fd == -1) {
fuse_log(FUSE_LOG_ERR,
"open(\"%s\", O_PATH): %m\n",
lo.source);
exit(1);
}
if (se == NULL)
goto err_out1;
goto err_out2;
goto err_out3;
if (opts.singlethread)
else {
config.clone_fd = opts.clone_fd;
config.max_idle_threads = opts.max_idle_threads;
ret = fuse_session_loop_mt(se, &config);
}
err_out3:
err_out2:
err_out1:
free(opts.mountpoint);
if (lo.root.fd >= 0)
close(lo.root.fd);
free(lo.source);
return ret ? 1 : 0;
}
int fuse_set_signal_handlers(struct fuse_session *se)
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
#define FUSE_CAP_WRITEBACK_CACHE
#define FUSE_CAP_EXPORT_SUPPORT
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
const char * fuse_pkgversion(void)
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
#define FUSE_CAP_FLOCK_LOCKS
void fuse_log(enum fuse_log_level level, const char *fmt,...)
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_err(fuse_req_t req, int err)
void * fuse_req_userdata(fuse_req_t req)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct fuse_entry_param *e, off_t off)
int fuse_reply_readlink(fuse_req_t req, const char *link)
int fuse_session_loop(struct fuse_session *se)
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
void fuse_cmdline_help(void)
struct fuse_session * fuse_session_new(struct fuse_args *args, const struct fuse_lowlevel_ops *op, size_t op_size, void *userdata)
void fuse_reply_none(fuse_req_t req)
void fuse_lowlevel_help(void)
int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
int fuse_reply_write(fuse_req_t req, size_t count)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, const struct fuse_file_info *fi)
int fuse_reply_lseek(fuse_req_t req, off_t off)
void fuse_lowlevel_version(void)
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_reply_xattr(fuse_req_t req, size_t count)
void fuse_opt_free_args(struct fuse_args *args)
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
#define FUSE_ARGS_INIT(argc, argv)
enum fuse_buf_flags flags
unsigned int cache_readdir
void(* init)(void *userdata, struct fuse_conn_info *conn)