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 ises 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.
#define _GNU_SOURCE
#define FUSE_USE_VERSION 30
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#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>
#ifndef HAVE_FSTATAT
#warning fstatat(2) needed by this program
int fstatat(int dirfd, const char *pathname, struct stat *buf, int flags)
{
errno = ENOSYS;
return -1;
}
#endif
#ifndef HAVE_OPENAT
#warning openat(2) needed by this program
int openat(int dirfd, const char *pathname, int flags, ...)
{
errno = ENOSYS;
return -1;
}
#endif
#ifndef HAVE_READLINKAT
#warning readlinkat(2) needed by this program
ssize_t readlinkat(int dirfd, const char *pathname, char *buf, size_t bufsiz)
{
errno = ENOSYS;
return -1;
}
#endif
#ifndef AT_EMPTY_PATH
#warning AT_EMPTY_PATH needed by this program
#define AT_EMPTY_PATH 0
#endif
#ifndef AT_SYMLINK_NOFOLLOW
#warning AT_SYMLINK_NOFOLLOW needed by this program
#define AT_SYMLINK_NOFOLLOW 0
#endif
#ifndef O_PATH
#warning O_PATH needed by this program
#define O_PATH 0
#endif
#ifndef O_NOFOLLOW
#warning O_NOFOLLOW needed by this program
#define O_NOFOLLOW 0
#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;
struct lo_inode root;
};
{
}
{
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,
{
(void) userdata;
}
{
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;
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(size, 1);
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;
char buf[64];
sprintf(buf, "/proc/self/fd/%i", lo_fd(req, ino));
fd = open(buf, fi->
flags & ~O_NOFOLLOW);
if (fd == -1)
}
{
(void) ino;
}
{
(void) ino;
}
.lookup = lo_lookup,
.forget = lo_forget,
.getattr = lo_getattr,
.readlink = lo_readlink,
.opendir = lo_opendir,
.readdir = lo_readdir,
.readdirplus = lo_readdirplus,
.releasedir = lo_releasedir,
.open = lo_open,
.release = lo_release,
.read = lo_read,
};
int main(int argc, char *argv[])
{
struct fuse_session *se;
struct fuse_cmdline_opts opts;
struct lo_data lo = { .debug = 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;
}
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
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;
}