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 31
#include <fuse_lowlevel.h>
#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 <err.h>
#include <inttypes.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 nlookup;
};
struct lo_data {
int debug;
int writeback;
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 },
};
{
}
{
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)
fprintf(stderr, "lo_init: activating writeback\n");
}
}
{
int res;
struct stat buf;
(void) fi;
res = fstatat(lo_fd(req, ino), "", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
if (res == -1)
}
static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st)
{
struct lo_inode *p;
for (p = lo->root.next; p != &lo->root; p = p->next) {
if (p->ino == st->st_ino && p->dev == st->st_dev)
return p;
}
return NULL;
}
{
int newfd;
int res;
int saverr;
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 = &lo_data(req)->root;
struct lo_inode *next = prev->next;
saverr = ENOMEM;
inode = calloc(1, sizeof(struct lo_inode));
if (!inode)
goto out_err;
inode->fd = newfd;
inode->ino = e->
attr.st_ino;
inode->dev = e->
attr.st_dev;
next->prev = inode;
inode->next = next;
inode->prev = prev;
prev->next = inode;
}
inode->nlookup++;
e->
ino = (uintptr_t) inode;
if (lo_debug(req))
fprintf(stderr, " %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))
fprintf(stderr, "lo_lookup(parent=%" PRIu64 ", name=%s)\n",
parent, name);
err = lo_do_lookup(req, parent, name, &e);
if (err)
else
}
static void lo_free(struct lo_inode *inode)
{
struct lo_inode *prev = inode->prev;
struct lo_inode *next = inode->next;
next->prev = prev;
prev->next = next;
close(inode->fd);
free(inode);
}
{
struct lo_inode *inode = lo_inode(req, ino);
if (lo_debug(req)) {
fprintf(stderr, " forget %lli %lli -%lli\n",
(unsigned long long) ino, (unsigned long long) inode->nlookup,
(unsigned long long) nlookup);
}
assert(inode->nlookup >= nlookup);
inode->nlookup -= nlookup;
if (!inode->nlookup)
lo_free(inode);
}
{
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 {
int fd;
DIR *dp;
struct dirent *entry;
off_t offset;
};
{
return (
struct lo_dirp *) (uintptr_t) fi->
fh;
}
{
int error = ENOMEM;
struct lo_dirp *d = calloc(1, sizeof(struct lo_dirp));
if (d == NULL)
goto out_err;
d->fd = openat(lo_fd(req, ino), ".", O_RDONLY);
if (d->fd == -1)
goto out_errno;
d->dp = fdopendir(d->fd);
if (d->dp == NULL)
goto out_errno;
d->offset = 0;
d->entry = NULL;
return;
out_errno:
error = errno;
out_err:
if (d) {
if (d->fd != -1)
close(d->fd);
free(d);
}
}
{
struct lo_dirp *d = lo_dirp(fi);
char *buf;
char *p;
size_t rem;
int err;
(void) ino;
buf = calloc(1, size);
if (!buf)
if (offset != d->offset) {
seekdir(d->dp, offset);
d->entry = NULL;
d->offset = offset;
}
p = buf;
rem = size;
while (1) {
size_t entsize;
off_t nextoff;
if (!d->entry) {
errno = 0;
d->entry = readdir(d->dp);
if (!d->entry) {
if (errno && rem == size) {
err = errno;
goto error;
}
break;
}
}
nextoff = telldir(d->dp);
if (plus) {
err = lo_do_lookup(req, ino, d->entry->d_name, &e);
if (err)
goto error;
d->entry->d_name,
&e, nextoff);
} else {
struct stat st = {
.st_ino = d->entry->d_ino,
.st_mode = d->entry->d_type << 12,
};
d->entry->d_name,
&st, nextoff);
}
if (entsize > rem)
break;
p += entsize;
rem -= entsize;
d->entry = NULL;
d->offset = nextoff;
}
free(buf);
return;
error:
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;
int err;
if (lo_debug(req))
fprintf(stderr, "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)
err = lo_do_lookup(req, parent, name, &e);
if (err)
else
}
{
int fd;
char buf[64];
if (lo_debug(req))
fprintf(stderr, "lo_open(ino=%" PRIu64 ", flags=%d)\n",
if (lo_data(req)->writeback &&
(fi->
flags & O_ACCMODE) == O_WRONLY) {
}
if (lo_data(req)->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)
}
{
(void) ino;
}
{
if (lo_debug(req))
fprintf(stderr, "lo_read(ino=%" PRIu64 ", size=%zd, "
"off=%lu)\n", ino, size, (unsigned long) offset);
}
{
(void) ino;
ssize_t res;
if (lo_debug(req))
fprintf(stderr, "lo_write(ino=%" PRIu64 ", size=%zd, off=%lu)\n",
ino, out_buf.
buf[0].
size, (
unsigned long) off);
if(res < 0)
else
}
.lookup = lo_lookup,
.forget = lo_forget,
.getattr = lo_getattr,
.readlink = lo_readlink,
.opendir = lo_opendir,
.readdir = lo_readdir,
.readdirplus = lo_readdirplus,
.releasedir = lo_releasedir,
.create = lo_create,
.open = lo_open,
.release = lo_release,
.read = lo_read,
.write_buf = lo_write_buf
};
int main(int argc, char *argv[])
{
struct fuse_session *se;
struct fuse_cmdline_opts opts;
struct lo_data lo = { .debug = 0,
.writeback = 0 };
int ret = -1;
lo.root.next = lo.root.prev = &lo.root;
lo.root.fd = -1;
return 1;
if (opts.show_help) {
printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
ret = 0;
goto err_out1;
} else if (opts.show_version) {
ret = 0;
goto err_out1;
}
return 1;
lo.debug = opts.debug;
lo.root.fd = open("/", O_PATH);
lo.root.nlookup = 2;
if (lo.root.fd == -1)
err(1, "open(\"/\", O_PATH)");
if (se == NULL)
goto err_out1;
goto err_out2;
goto err_out3;
if (opts.singlethread)
else
ret = fuse_session_loop_mt(se, opts.clone_fd);
err_out3:
err_out2:
err_out1:
free(opts.mountpoint);
while (lo.root.next != &lo.root)
lo_free(lo.root.next);
if (lo.root.fd >= 0)
close(lo.root.fd);
return ret ? 1 : 0;
}