Also, added more comments for the same purpose.
libfuse_libs=$LIBS
LIBS=
AC_CHECK_LIB(ulockmgr, ulockmgr_op)
-fusexmp_fh_libs=$LIBS
-AC_SUBST(fusexmp_fh_libs)
+passthrough_fh_libs=$LIBS
+AC_SUBST(passthrough_fh_libs)
LIBS=
AC_ARG_WITH([libiconv-prefix],
-/fusexmp
-/fusexmp_fh
+/passthrough
+/passthrough_fh
/hello
/hello_ll
/fioc
/fsel
/fselclient
/cusexmp
-/fuse_lo-plus
+/passthrough_ll
/timefs1
/timefs2
AM_CPPFLAGS = -I$(top_srcdir)/include -D_REENTRANT
noinst_HEADERS = fioc.h
-noinst_PROGRAMS = fusexmp fusexmp_fh hello hello_ll fioc fioclient \
- fsel fselclient cusexmp fuse_lo-plus timefs1 timefs2 \
+noinst_PROGRAMS = passthrough passthrough_fh hello hello_ll fioc fioclient \
+ fsel fselclient cusexmp passthrough_ll timefs1 timefs2 \
timefs3
LDADD = ../lib/libfuse3.la
-fusexmp_fh_LDADD = ../lib/libfuse3.la @fusexmp_fh_libs@
+passthrough_fh_LDADD = ../lib/libfuse3.la @passthrough_fh_libs@
fioclient_CPPFLAGS =
fioclient_LDFLAGS =
+++ /dev/null
-/*
- FUSE: Filesystem in Userspace
- Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
-
- This program can be distributed under the terms of the GNU GPL.
- See the file COPYING.
-*/
-
-/*
- * gcc -Wall fuse_lo-plus.c `pkg-config fuse3 --cflags --libs` -o fuse_lo-plus
- */
-
-#define _GNU_SOURCE
-#define FUSE_USE_VERSION 30
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <fuse_lowlevel.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <stddef.h>
-#include <stdbool.h>
-#include <string.h>
-#include <dirent.h>
-#include <assert.h>
-#include <errno.h>
-#include <err.h>
-
-/* Compat stuff. Doesn't make it work, just makes it compile. */
-#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;
-};
-
-static struct lo_data *lo_data(fuse_req_t req)
-{
- return (struct lo_data *) fuse_req_userdata(req);
-}
-
-static struct lo_inode *lo_inode(fuse_req_t req, fuse_ino_t ino)
-{
- if (ino == FUSE_ROOT_ID)
- return &lo_data(req)->root;
- else
- return (struct lo_inode *) (uintptr_t) ino;
-}
-
-static int lo_fd(fuse_req_t req, fuse_ino_t ino)
-{
- return lo_inode(req, ino)->fd;
-}
-
-static bool lo_debug(fuse_req_t req)
-{
- return lo_data(req)->debug != 0;
-}
-
-static void lo_getattr(fuse_req_t req, fuse_ino_t ino,
- struct fuse_file_info *fi)
-{
- int res;
- struct stat buf;
- (void) fi;
-
- res = fstatat(lo_fd(req, ino), "", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
- if (res == -1)
- return (void) fuse_reply_err(req, errno);
-
- fuse_reply_attr(req, &buf, 1.0);
-}
-
-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;
-}
-
-static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name,
- struct fuse_entry_param *e)
-{
- int newfd;
- int res;
- int saverr;
- struct lo_inode *inode;
-
- memset(e, 0, sizeof(*e));
- e->attr_timeout = 1.0;
- e->entry_timeout = 1.0;
-
- 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;
-}
-
-static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
-{
- struct fuse_entry_param e;
- int err;
-
- err = lo_do_lookup(req, parent, name, &e);
- if (err)
- fuse_reply_err(req, err);
- else
- fuse_reply_entry(req, &e);
-}
-
-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);
-}
-
-static void lo_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
-{
- 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);
-
- fuse_reply_none(req);
-}
-
-static void lo_readlink(fuse_req_t req, fuse_ino_t ino)
-{
- char buf[PATH_MAX + 1];
- int res;
-
- res = readlinkat(lo_fd(req, ino), "", buf, sizeof(buf));
- if (res == -1)
- return (void) fuse_reply_err(req, errno);
-
- if (res == sizeof(buf))
- return (void) fuse_reply_err(req, ENAMETOOLONG);
-
- buf[res] = '\0';
-
- fuse_reply_readlink(req, buf);
-}
-
-struct lo_dirp {
- int fd;
- DIR *dp;
- struct dirent *entry;
- off_t offset;
-};
-
-static struct lo_dirp *lo_dirp(struct fuse_file_info *fi)
-{
- return (struct lo_dirp *) (uintptr_t) fi->fh;
-}
-
-static void lo_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
-{
- 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;
-
- fi->fh = (uintptr_t) d;
- fuse_reply_open(req, fi);
- return;
-
-out_errno:
- error = errno;
-out_err:
- if (d) {
- if (d->fd != -1)
- close(d->fd);
- free(d);
- }
- fuse_reply_err(req, error);
-}
-
-static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
- off_t offset, struct fuse_file_info *fi, int plus)
-{
- struct lo_dirp *d = lo_dirp(fi);
- char *buf;
- char *p;
- size_t rem;
- int err;
-
- (void) ino;
-
- buf = calloc(size, 1);
- if (!buf)
- return (void) fuse_reply_err(req, ENOMEM);
-
- 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) {
- struct fuse_entry_param e;
-
- err = lo_do_lookup(req, ino, d->entry->d_name, &e);
- if (err)
- goto error;
-
- entsize = fuse_add_direntry_plus(req, p, rem,
- d->entry->d_name,
- &e, nextoff);
- } else {
- struct stat st = {
- .st_ino = d->entry->d_ino,
- .st_mode = d->entry->d_type << 12,
- };
- entsize = fuse_add_direntry(req, p, rem,
- d->entry->d_name,
- &st, nextoff);
- }
- if (entsize > rem)
- break;
-
- p += entsize;
- rem -= entsize;
-
- d->entry = NULL;
- d->offset = nextoff;
- }
-
- fuse_reply_buf(req, buf, size - rem);
- free(buf);
- return;
-
-error:
- free(buf);
- fuse_reply_err(req, err);
-}
-
-static void lo_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
- off_t offset, struct fuse_file_info *fi)
-{
- lo_do_readdir(req, ino, size, offset, fi, 0);
-}
-
-static void lo_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size,
- off_t offset, struct fuse_file_info *fi)
-{
- lo_do_readdir(req, ino, size, offset, fi, 1);
-}
-
-static void lo_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
-{
- struct lo_dirp *d = lo_dirp(fi);
- (void) ino;
- closedir(d->dp);
- free(d);
- fuse_reply_err(req, 0);
-}
-
-static void lo_open(fuse_req_t req, fuse_ino_t ino,
- struct fuse_file_info *fi)
-{
- 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)
- return (void) fuse_reply_err(req, errno);
-
- fi->fh = fd;
- fuse_reply_open(req, fi);
-}
-
-static void lo_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
-{
- (void) ino;
-
- close(fi->fh);
- fuse_reply_err(req, 0);
-}
-
-static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size,
- off_t offset, struct fuse_file_info *fi)
-{
- struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size);
-
- (void) ino;
-
- buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
- buf.buf[0].fd = fi->fh;
- buf.buf[0].pos = offset;
-
- fuse_reply_data(req, &buf, FUSE_BUF_SPLICE_MOVE);
-}
-
-static struct fuse_lowlevel_ops lo_oper = {
- .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_args args = FUSE_ARGS_INIT(argc, argv);
- struct fuse_session *se;
- struct fuse_cmdline_opts opts;
- struct lo_data lo = { .debug = 0 };
- int ret = -1;
-
- if (fuse_parse_cmdline(&args, &opts) != 0)
- return 1;
- if (opts.show_help) {
- printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
- fuse_cmdline_help();
- fuse_lowlevel_help();
- fuse_mount_help();
- ret = 0;
- goto err_out1;
- } else if (opts.show_version) {
- printf("FUSE library version %s\n", fuse_pkgversion());
- fuse_lowlevel_version();
- fuse_mount_version();
- ret = 0;
- goto err_out1;
- }
-
- lo.debug = opts.debug;
- lo.root.next = lo.root.prev = &lo.root;
- lo.root.fd = open("/", O_PATH);
- lo.root.nlookup = 2;
- if (lo.root.fd == -1)
- err(1, "open(\"/\", O_PATH)");
-
- se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo);
- if (se == NULL)
- goto err_out1;
-
- if (fuse_set_signal_handlers(se) != 0)
- goto err_out2;
-
- if (fuse_session_mount(se, opts.mountpoint) != 0)
- goto err_out3;
-
- fuse_daemonize(opts.foreground);
-
- /* Block until ctrl+c or fusermount -u */
- if (opts.singlethread)
- ret = fuse_session_loop(se);
- else
- ret = fuse_session_loop_mt(se);
-
- fuse_session_unmount(se);
-err_out3:
- fuse_remove_signal_handlers(se);
-err_out2:
- fuse_session_destroy(se);
-err_out1:
- free(opts.mountpoint);
- fuse_opt_free_args(&args);
-
- while (lo.root.next != &lo.root)
- lo_free(lo.root.next);
-
- return ret ? 1 : 0;
-}
+++ /dev/null
-/*
- FUSE: Filesystem in Userspace
- Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
- Copyright (C) 2011 Sebastian Pipping <sebastian@pipping.org>
-
- This program can be distributed under the terms of the GNU GPL.
- See the file COPYING.
-*/
-
-/** @file
- * @tableofcontents
- *
- * fusexmp.c - FUSE: Filesystem in Userspace
- *
- * \section section_compile compiling this example
- *
- * gcc -Wall fusexmp.c `pkg-config fuse3 --cflags --libs` -o fusexmp
- *
- * \section section_source the complete source
- * \include fusexmp.c
- */
-
-
-#define FUSE_USE_VERSION 30
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#ifdef linux
-/* For pread()/pwrite()/utimensat() */
-#define _XOPEN_SOURCE 700
-#endif
-
-#include <fuse.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <dirent.h>
-#include <errno.h>
-#include <sys/time.h>
-#ifdef HAVE_SETXATTR
-#include <sys/xattr.h>
-#endif
-
-static int xmp_getattr(const char *path, struct stat *stbuf)
-{
- int res;
-
- res = lstat(path, stbuf);
- if (res == -1)
- return -errno;
-
- return 0;
-}
-
-static int xmp_access(const char *path, int mask)
-{
- int res;
-
- res = access(path, mask);
- if (res == -1)
- return -errno;
-
- return 0;
-}
-
-static int xmp_readlink(const char *path, char *buf, size_t size)
-{
- int res;
-
- res = readlink(path, buf, size - 1);
- if (res == -1)
- return -errno;
-
- buf[res] = '\0';
- return 0;
-}
-
-
-static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
- off_t offset, struct fuse_file_info *fi,
- enum fuse_readdir_flags flags)
-{
- DIR *dp;
- struct dirent *de;
-
- (void) offset;
- (void) fi;
- (void) flags;
-
- dp = opendir(path);
- if (dp == NULL)
- return -errno;
-
- while ((de = readdir(dp)) != NULL) {
- struct stat st;
- memset(&st, 0, sizeof(st));
- st.st_ino = de->d_ino;
- st.st_mode = de->d_type << 12;
- if (filler(buf, de->d_name, &st, 0, 0))
- break;
- }
-
- closedir(dp);
- return 0;
-}
-
-static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
-{
- int res;
-
- /* On Linux this could just be 'mknod(path, mode, rdev)' but this
- is more portable */
- if (S_ISREG(mode)) {
- res = open(path, O_CREAT | O_EXCL | O_WRONLY, mode);
- if (res >= 0)
- res = close(res);
- } else if (S_ISFIFO(mode))
- res = mkfifo(path, mode);
- else
- res = mknod(path, mode, rdev);
- if (res == -1)
- return -errno;
-
- return 0;
-}
-
-static int xmp_mkdir(const char *path, mode_t mode)
-{
- int res;
-
- res = mkdir(path, mode);
- if (res == -1)
- return -errno;
-
- return 0;
-}
-
-static int xmp_unlink(const char *path)
-{
- int res;
-
- res = unlink(path);
- if (res == -1)
- return -errno;
-
- return 0;
-}
-
-static int xmp_rmdir(const char *path)
-{
- int res;
-
- res = rmdir(path);
- if (res == -1)
- return -errno;
-
- return 0;
-}
-
-static int xmp_symlink(const char *from, const char *to)
-{
- int res;
-
- res = symlink(from, to);
- if (res == -1)
- return -errno;
-
- return 0;
-}
-
-static int xmp_rename(const char *from, const char *to, unsigned int flags)
-{
- int res;
-
- if (flags)
- return -EINVAL;
-
- res = rename(from, to);
- if (res == -1)
- return -errno;
-
- return 0;
-}
-
-static int xmp_link(const char *from, const char *to)
-{
- int res;
-
- res = link(from, to);
- if (res == -1)
- return -errno;
-
- return 0;
-}
-
-static int xmp_chmod(const char *path, mode_t mode)
-{
- int res;
-
- res = chmod(path, mode);
- if (res == -1)
- return -errno;
-
- return 0;
-}
-
-static int xmp_chown(const char *path, uid_t uid, gid_t gid)
-{
- int res;
-
- res = lchown(path, uid, gid);
- if (res == -1)
- return -errno;
-
- return 0;
-}
-
-static int xmp_truncate(const char *path, off_t size)
-{
- int res;
-
- res = truncate(path, size);
- if (res == -1)
- return -errno;
-
- return 0;
-}
-
-#ifdef HAVE_UTIMENSAT
-static int xmp_utimens(const char *path, const struct timespec ts[2])
-{
- int res;
-
- /* don't use utime/utimes since they follow symlinks */
- res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW);
- if (res == -1)
- return -errno;
-
- return 0;
-}
-#endif
-
-static int xmp_open(const char *path, struct fuse_file_info *fi)
-{
- int res;
-
- res = open(path, fi->flags);
- if (res == -1)
- return -errno;
-
- close(res);
- return 0;
-}
-
-static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
- struct fuse_file_info *fi)
-{
- int fd;
- int res;
-
- (void) fi;
- fd = open(path, O_RDONLY);
- if (fd == -1)
- return -errno;
-
- res = pread(fd, buf, size, offset);
- if (res == -1)
- res = -errno;
-
- close(fd);
- return res;
-}
-
-static int xmp_write(const char *path, const char *buf, size_t size,
- off_t offset, struct fuse_file_info *fi)
-{
- int fd;
- int res;
-
- (void) fi;
- fd = open(path, O_WRONLY);
- if (fd == -1)
- return -errno;
-
- res = pwrite(fd, buf, size, offset);
- if (res == -1)
- res = -errno;
-
- close(fd);
- return res;
-}
-
-static int xmp_statfs(const char *path, struct statvfs *stbuf)
-{
- int res;
-
- res = statvfs(path, stbuf);
- if (res == -1)
- return -errno;
-
- return 0;
-}
-
-static int xmp_release(const char *path, struct fuse_file_info *fi)
-{
- /* Just a stub. This method is optional and can safely be left
- unimplemented */
-
- (void) path;
- (void) fi;
- return 0;
-}
-
-static int xmp_fsync(const char *path, int isdatasync,
- struct fuse_file_info *fi)
-{
- /* Just a stub. This method is optional and can safely be left
- unimplemented */
-
- (void) path;
- (void) isdatasync;
- (void) fi;
- return 0;
-}
-
-#ifdef HAVE_POSIX_FALLOCATE
-static int xmp_fallocate(const char *path, int mode,
- off_t offset, off_t length, struct fuse_file_info *fi)
-{
- int fd;
- int res;
-
- (void) fi;
-
- if (mode)
- return -EOPNOTSUPP;
-
- fd = open(path, O_WRONLY);
- if (fd == -1)
- return -errno;
-
- res = -posix_fallocate(fd, offset, length);
-
- close(fd);
- return res;
-}
-#endif
-
-#ifdef HAVE_SETXATTR
-/* xattr operations are optional and can safely be left unimplemented */
-static int xmp_setxattr(const char *path, const char *name, const char *value,
- size_t size, int flags)
-{
- int res = lsetxattr(path, name, value, size, flags);
- if (res == -1)
- return -errno;
- return 0;
-}
-
-static int xmp_getxattr(const char *path, const char *name, char *value,
- size_t size)
-{
- int res = lgetxattr(path, name, value, size);
- if (res == -1)
- return -errno;
- return res;
-}
-
-static int xmp_listxattr(const char *path, char *list, size_t size)
-{
- int res = llistxattr(path, list, size);
- if (res == -1)
- return -errno;
- return res;
-}
-
-static int xmp_removexattr(const char *path, const char *name)
-{
- int res = lremovexattr(path, name);
- if (res == -1)
- return -errno;
- return 0;
-}
-#endif /* HAVE_SETXATTR */
-
-static struct fuse_operations xmp_oper = {
- .getattr = xmp_getattr,
- .access = xmp_access,
- .readlink = xmp_readlink,
- .readdir = xmp_readdir,
- .mknod = xmp_mknod,
- .mkdir = xmp_mkdir,
- .symlink = xmp_symlink,
- .unlink = xmp_unlink,
- .rmdir = xmp_rmdir,
- .rename = xmp_rename,
- .link = xmp_link,
- .chmod = xmp_chmod,
- .chown = xmp_chown,
- .truncate = xmp_truncate,
-#ifdef HAVE_UTIMENSAT
- .utimens = xmp_utimens,
-#endif
- .open = xmp_open,
- .read = xmp_read,
- .write = xmp_write,
- .statfs = xmp_statfs,
- .release = xmp_release,
- .fsync = xmp_fsync,
-#ifdef HAVE_POSIX_FALLOCATE
- .fallocate = xmp_fallocate,
-#endif
-#ifdef HAVE_SETXATTR
- .setxattr = xmp_setxattr,
- .getxattr = xmp_getxattr,
- .listxattr = xmp_listxattr,
- .removexattr = xmp_removexattr,
-#endif
-};
-
-int main(int argc, char *argv[])
-{
- umask(0);
- return fuse_main(argc, argv, &xmp_oper, NULL);
-}
+++ /dev/null
-/*
- FUSE: Filesystem in Userspace
- Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
- Copyright (C) 2011 Sebastian Pipping <sebastian@pipping.org>
-
- This program can be distributed under the terms of the GNU GPL.
- See the file COPYING.
-*/
-
-/** @file
- * @tableofcontents
- *
- * fusexmp_fh.c - FUSE: Filesystem in Userspace
- *
- * \section section_compile compiling this example
- *
- * gcc -Wall fusexmp_fh.c `pkg-config fuse3 --cflags --libs` -lulockmgr -o fusexmp_fh
- *
- * \section section_source the complete source
- * \include fusexmp_fh.c
- */
-
-#define FUSE_USE_VERSION 30
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#define _GNU_SOURCE
-
-#include <fuse.h>
-
-#ifdef HAVE_LIBULOCKMGR
-#include <ulockmgr.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <dirent.h>
-#include <errno.h>
-#include <sys/time.h>
-#ifdef HAVE_SETXATTR
-#include <sys/xattr.h>
-#endif
-#include <sys/file.h> /* flock(2) */
-
-static int xmp_getattr(const char *path, struct stat *stbuf)
-{
- int res;
-
- res = lstat(path, stbuf);
- if (res == -1)
- return -errno;
-
- return 0;
-}
-
-static int xmp_fgetattr(const char *path, struct stat *stbuf,
- struct fuse_file_info *fi)
-{
- int res;
-
- (void) path;
-
- res = fstat(fi->fh, stbuf);
- if (res == -1)
- return -errno;
-
- return 0;
-}
-
-static int xmp_access(const char *path, int mask)
-{
- int res;
-
- res = access(path, mask);
- if (res == -1)
- return -errno;
-
- return 0;
-}
-
-static int xmp_readlink(const char *path, char *buf, size_t size)
-{
- int res;
-
- res = readlink(path, buf, size - 1);
- if (res == -1)
- return -errno;
-
- buf[res] = '\0';
- return 0;
-}
-
-struct xmp_dirp {
- DIR *dp;
- struct dirent *entry;
- off_t offset;
-};
-
-static int xmp_opendir(const char *path, struct fuse_file_info *fi)
-{
- int res;
- struct xmp_dirp *d = malloc(sizeof(struct xmp_dirp));
- if (d == NULL)
- return -ENOMEM;
-
- d->dp = opendir(path);
- if (d->dp == NULL) {
- res = -errno;
- free(d);
- return res;
- }
- d->offset = 0;
- d->entry = NULL;
-
- fi->fh = (unsigned long) d;
- return 0;
-}
-
-static inline struct xmp_dirp *get_dirp(struct fuse_file_info *fi)
-{
- return (struct xmp_dirp *) (uintptr_t) fi->fh;
-}
-
-static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
- off_t offset, struct fuse_file_info *fi,
- enum fuse_readdir_flags flags)
-{
- struct xmp_dirp *d = get_dirp(fi);
-
- (void) path;
- if (offset != d->offset) {
- seekdir(d->dp, offset);
- d->entry = NULL;
- d->offset = offset;
- }
- while (1) {
- struct stat st;
- off_t nextoff;
- enum fuse_fill_dir_flags fill_flags = 0;
-
- if (!d->entry) {
- d->entry = readdir(d->dp);
- if (!d->entry)
- break;
- }
-#ifdef HAVE_FSTATAT
- if (flags & FUSE_READDIR_PLUS) {
- int res;
-
- res = fstatat(dirfd(d->dp), d->entry->d_name, &st,
- AT_SYMLINK_NOFOLLOW);
- if (res != -1)
- fill_flags |= FUSE_FILL_DIR_PLUS;
- }
-#endif
- if (!(fill_flags & FUSE_FILL_DIR_PLUS)) {
- memset(&st, 0, sizeof(st));
- st.st_ino = d->entry->d_ino;
- st.st_mode = d->entry->d_type << 12;
- }
- nextoff = telldir(d->dp);
- if (filler(buf, d->entry->d_name, &st, nextoff, fill_flags))
- break;
-
- d->entry = NULL;
- d->offset = nextoff;
- }
-
- return 0;
-}
-
-static int xmp_releasedir(const char *path, struct fuse_file_info *fi)
-{
- struct xmp_dirp *d = get_dirp(fi);
- (void) path;
- closedir(d->dp);
- free(d);
- return 0;
-}
-
-static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
-{
- int res;
-
- if (S_ISFIFO(mode))
- res = mkfifo(path, mode);
- else
- res = mknod(path, mode, rdev);
- if (res == -1)
- return -errno;
-
- return 0;
-}
-
-static int xmp_mkdir(const char *path, mode_t mode)
-{
- int res;
-
- res = mkdir(path, mode);
- if (res == -1)
- return -errno;
-
- return 0;
-}
-
-static int xmp_unlink(const char *path)
-{
- int res;
-
- res = unlink(path);
- if (res == -1)
- return -errno;
-
- return 0;
-}
-
-static int xmp_rmdir(const char *path)
-{
- int res;
-
- res = rmdir(path);
- if (res == -1)
- return -errno;
-
- return 0;
-}
-
-static int xmp_symlink(const char *from, const char *to)
-{
- int res;
-
- res = symlink(from, to);
- if (res == -1)
- return -errno;
-
- return 0;
-}
-
-static int xmp_rename(const char *from, const char *to, unsigned int flags)
-{
- int res;
-
- /* When we have renameat2() in libc, then we can implement flags */
- if (flags)
- return -EINVAL;
-
- res = rename(from, to);
- if (res == -1)
- return -errno;
-
- return 0;
-}
-
-static int xmp_link(const char *from, const char *to)
-{
- int res;
-
- res = link(from, to);
- if (res == -1)
- return -errno;
-
- return 0;
-}
-
-static int xmp_chmod(const char *path, mode_t mode)
-{
- int res;
-
- res = chmod(path, mode);
- if (res == -1)
- return -errno;
-
- return 0;
-}
-
-static int xmp_chown(const char *path, uid_t uid, gid_t gid)
-{
- int res;
-
- res = lchown(path, uid, gid);
- if (res == -1)
- return -errno;
-
- return 0;
-}
-
-static int xmp_truncate(const char *path, off_t size)
-{
- int res;
-
- res = truncate(path, size);
- if (res == -1)
- return -errno;
-
- return 0;
-}
-
-static int xmp_ftruncate(const char *path, off_t size,
- struct fuse_file_info *fi)
-{
- int res;
-
- (void) path;
-
- res = ftruncate(fi->fh, size);
- if (res == -1)
- return -errno;
-
- return 0;
-}
-
-#ifdef HAVE_UTIMENSAT
-static int xmp_utimens(const char *path, const struct timespec ts[2])
-{
- int res;
-
- /* don't use utime/utimes since they follow symlinks */
- res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW);
- if (res == -1)
- return -errno;
-
- return 0;
-}
-#endif
-
-static int xmp_create(const char *path, mode_t mode, struct fuse_file_info *fi)
-{
- int fd;
-
- fd = open(path, fi->flags, mode);
- if (fd == -1)
- return -errno;
-
- fi->fh = fd;
- return 0;
-}
-
-static int xmp_open(const char *path, struct fuse_file_info *fi)
-{
- int fd;
-
- fd = open(path, fi->flags);
- if (fd == -1)
- return -errno;
-
- fi->fh = fd;
- return 0;
-}
-
-static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
- struct fuse_file_info *fi)
-{
- int res;
-
- (void) path;
- res = pread(fi->fh, buf, size, offset);
- if (res == -1)
- res = -errno;
-
- return res;
-}
-
-static int xmp_read_buf(const char *path, struct fuse_bufvec **bufp,
- size_t size, off_t offset, struct fuse_file_info *fi)
-{
- struct fuse_bufvec *src;
-
- (void) path;
-
- src = malloc(sizeof(struct fuse_bufvec));
- if (src == NULL)
- return -ENOMEM;
-
- *src = FUSE_BUFVEC_INIT(size);
-
- src->buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
- src->buf[0].fd = fi->fh;
- src->buf[0].pos = offset;
-
- *bufp = src;
-
- return 0;
-}
-
-static int xmp_write(const char *path, const char *buf, size_t size,
- off_t offset, struct fuse_file_info *fi)
-{
- int res;
-
- (void) path;
- res = pwrite(fi->fh, buf, size, offset);
- if (res == -1)
- res = -errno;
-
- return res;
-}
-
-static int xmp_write_buf(const char *path, struct fuse_bufvec *buf,
- off_t offset, struct fuse_file_info *fi)
-{
- struct fuse_bufvec dst = FUSE_BUFVEC_INIT(fuse_buf_size(buf));
-
- (void) path;
-
- dst.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
- dst.buf[0].fd = fi->fh;
- dst.buf[0].pos = offset;
-
- return fuse_buf_copy(&dst, buf, FUSE_BUF_SPLICE_NONBLOCK);
-}
-
-static int xmp_statfs(const char *path, struct statvfs *stbuf)
-{
- int res;
-
- res = statvfs(path, stbuf);
- if (res == -1)
- return -errno;
-
- return 0;
-}
-
-static int xmp_flush(const char *path, struct fuse_file_info *fi)
-{
- int res;
-
- (void) path;
- /* This is called from every close on an open file, so call the
- close on the underlying filesystem. But since flush may be
- called multiple times for an open file, this must not really
- close the file. This is important if used on a network
- filesystem like NFS which flush the data/metadata on close() */
- res = close(dup(fi->fh));
- if (res == -1)
- return -errno;
-
- return 0;
-}
-
-static int xmp_release(const char *path, struct fuse_file_info *fi)
-{
- (void) path;
- close(fi->fh);
-
- return 0;
-}
-
-static int xmp_fsync(const char *path, int isdatasync,
- struct fuse_file_info *fi)
-{
- int res;
- (void) path;
-
-#ifndef HAVE_FDATASYNC
- (void) isdatasync;
-#else
- if (isdatasync)
- res = fdatasync(fi->fh);
- else
-#endif
- res = fsync(fi->fh);
- if (res == -1)
- return -errno;
-
- return 0;
-}
-
-#ifdef HAVE_POSIX_FALLOCATE
-static int xmp_fallocate(const char *path, int mode,
- off_t offset, off_t length, struct fuse_file_info *fi)
-{
- (void) path;
-
- if (mode)
- return -EOPNOTSUPP;
-
- return -posix_fallocate(fi->fh, offset, length);
-}
-#endif
-
-#ifdef HAVE_SETXATTR
-/* xattr operations are optional and can safely be left unimplemented */
-static int xmp_setxattr(const char *path, const char *name, const char *value,
- size_t size, int flags)
-{
- int res = lsetxattr(path, name, value, size, flags);
- if (res == -1)
- return -errno;
- return 0;
-}
-
-static int xmp_getxattr(const char *path, const char *name, char *value,
- size_t size)
-{
- int res = lgetxattr(path, name, value, size);
- if (res == -1)
- return -errno;
- return res;
-}
-
-static int xmp_listxattr(const char *path, char *list, size_t size)
-{
- int res = llistxattr(path, list, size);
- if (res == -1)
- return -errno;
- return res;
-}
-
-static int xmp_removexattr(const char *path, const char *name)
-{
- int res = lremovexattr(path, name);
- if (res == -1)
- return -errno;
- return 0;
-}
-#endif /* HAVE_SETXATTR */
-
-#ifdef HAVE_LIBULOCKMGR
-static int xmp_lock(const char *path, struct fuse_file_info *fi, int cmd,
- struct flock *lock)
-{
- (void) path;
-
- return ulockmgr_op(fi->fh, cmd, lock, &fi->lock_owner,
- sizeof(fi->lock_owner));
-}
-#endif
-
-static int xmp_flock(const char *path, struct fuse_file_info *fi, int op)
-{
- int res;
- (void) path;
-
- res = flock(fi->fh, op);
- if (res == -1)
- return -errno;
-
- return 0;
-}
-
-static struct fuse_operations xmp_oper = {
- .getattr = xmp_getattr,
- .fgetattr = xmp_fgetattr,
- .access = xmp_access,
- .readlink = xmp_readlink,
- .opendir = xmp_opendir,
- .readdir = xmp_readdir,
- .releasedir = xmp_releasedir,
- .mknod = xmp_mknod,
- .mkdir = xmp_mkdir,
- .symlink = xmp_symlink,
- .unlink = xmp_unlink,
- .rmdir = xmp_rmdir,
- .rename = xmp_rename,
- .link = xmp_link,
- .chmod = xmp_chmod,
- .chown = xmp_chown,
- .truncate = xmp_truncate,
- .ftruncate = xmp_ftruncate,
-#ifdef HAVE_UTIMENSAT
- .utimens = xmp_utimens,
-#endif
- .create = xmp_create,
- .open = xmp_open,
- .read = xmp_read,
- .read_buf = xmp_read_buf,
- .write = xmp_write,
- .write_buf = xmp_write_buf,
- .statfs = xmp_statfs,
- .flush = xmp_flush,
- .release = xmp_release,
- .fsync = xmp_fsync,
-#ifdef HAVE_POSIX_FALLOCATE
- .fallocate = xmp_fallocate,
-#endif
-#ifdef HAVE_SETXATTR
- .setxattr = xmp_setxattr,
- .getxattr = xmp_getxattr,
- .listxattr = xmp_listxattr,
- .removexattr = xmp_removexattr,
-#endif
-#ifdef HAVE_LIBULOCKMGR
- .lock = xmp_lock,
-#endif
- .flock = xmp_flock,
-};
-
-int main(int argc, char *argv[])
-{
- umask(0);
- return fuse_main(argc, argv, &xmp_oper, NULL);
-}
--- /dev/null
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+ Copyright (C) 2011 Sebastian Pipping <sebastian@pipping.org>
+
+ This program can be distributed under the terms of the GNU GPL.
+ See the file COPYING.
+*/
+
+/** @file
+ * @tableofcontents
+ *
+ * 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. It's performance is terrible.
+ *
+ * \section section_compile compiling this example
+ *
+ * gcc -Wall passthrough.c `pkg-config fuse3 --cflags --libs` -o passthrough
+ *
+ * \section section_source the complete source
+ * \include passthrough.c
+ */
+
+
+#define FUSE_USE_VERSION 30
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef linux
+/* For pread()/pwrite()/utimensat() */
+#define _XOPEN_SOURCE 700
+#endif
+
+#include <fuse.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <errno.h>
+#include <sys/time.h>
+#ifdef HAVE_SETXATTR
+#include <sys/xattr.h>
+#endif
+
+static int xmp_getattr(const char *path, struct stat *stbuf)
+{
+ int res;
+
+ res = lstat(path, stbuf);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_access(const char *path, int mask)
+{
+ int res;
+
+ res = access(path, mask);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_readlink(const char *path, char *buf, size_t size)
+{
+ int res;
+
+ res = readlink(path, buf, size - 1);
+ if (res == -1)
+ return -errno;
+
+ buf[res] = '\0';
+ return 0;
+}
+
+
+static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
+ off_t offset, struct fuse_file_info *fi,
+ enum fuse_readdir_flags flags)
+{
+ DIR *dp;
+ struct dirent *de;
+
+ (void) offset;
+ (void) fi;
+ (void) flags;
+
+ dp = opendir(path);
+ if (dp == NULL)
+ return -errno;
+
+ while ((de = readdir(dp)) != NULL) {
+ struct stat st;
+ memset(&st, 0, sizeof(st));
+ st.st_ino = de->d_ino;
+ st.st_mode = de->d_type << 12;
+ if (filler(buf, de->d_name, &st, 0, 0))
+ break;
+ }
+
+ closedir(dp);
+ return 0;
+}
+
+static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
+{
+ int res;
+
+ /* On Linux this could just be 'mknod(path, mode, rdev)' but this
+ is more portable */
+ if (S_ISREG(mode)) {
+ res = open(path, O_CREAT | O_EXCL | O_WRONLY, mode);
+ if (res >= 0)
+ res = close(res);
+ } else if (S_ISFIFO(mode))
+ res = mkfifo(path, mode);
+ else
+ res = mknod(path, mode, rdev);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_mkdir(const char *path, mode_t mode)
+{
+ int res;
+
+ res = mkdir(path, mode);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_unlink(const char *path)
+{
+ int res;
+
+ res = unlink(path);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_rmdir(const char *path)
+{
+ int res;
+
+ res = rmdir(path);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_symlink(const char *from, const char *to)
+{
+ int res;
+
+ res = symlink(from, to);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_rename(const char *from, const char *to, unsigned int flags)
+{
+ int res;
+
+ if (flags)
+ return -EINVAL;
+
+ res = rename(from, to);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_link(const char *from, const char *to)
+{
+ int res;
+
+ res = link(from, to);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_chmod(const char *path, mode_t mode)
+{
+ int res;
+
+ res = chmod(path, mode);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_chown(const char *path, uid_t uid, gid_t gid)
+{
+ int res;
+
+ res = lchown(path, uid, gid);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_truncate(const char *path, off_t size)
+{
+ int res;
+
+ res = truncate(path, size);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+#ifdef HAVE_UTIMENSAT
+static int xmp_utimens(const char *path, const struct timespec ts[2])
+{
+ int res;
+
+ /* don't use utime/utimes since they follow symlinks */
+ res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+#endif
+
+static int xmp_open(const char *path, struct fuse_file_info *fi)
+{
+ int res;
+
+ res = open(path, fi->flags);
+ if (res == -1)
+ return -errno;
+
+ close(res);
+ return 0;
+}
+
+static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
+ struct fuse_file_info *fi)
+{
+ int fd;
+ int res;
+
+ (void) fi;
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
+ return -errno;
+
+ res = pread(fd, buf, size, offset);
+ if (res == -1)
+ res = -errno;
+
+ close(fd);
+ return res;
+}
+
+static int xmp_write(const char *path, const char *buf, size_t size,
+ off_t offset, struct fuse_file_info *fi)
+{
+ int fd;
+ int res;
+
+ (void) fi;
+ fd = open(path, O_WRONLY);
+ if (fd == -1)
+ return -errno;
+
+ res = pwrite(fd, buf, size, offset);
+ if (res == -1)
+ res = -errno;
+
+ close(fd);
+ return res;
+}
+
+static int xmp_statfs(const char *path, struct statvfs *stbuf)
+{
+ int res;
+
+ res = statvfs(path, stbuf);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_release(const char *path, struct fuse_file_info *fi)
+{
+ /* Just a stub. This method is optional and can safely be left
+ unimplemented */
+
+ (void) path;
+ (void) fi;
+ return 0;
+}
+
+static int xmp_fsync(const char *path, int isdatasync,
+ struct fuse_file_info *fi)
+{
+ /* Just a stub. This method is optional and can safely be left
+ unimplemented */
+
+ (void) path;
+ (void) isdatasync;
+ (void) fi;
+ return 0;
+}
+
+#ifdef HAVE_POSIX_FALLOCATE
+static int xmp_fallocate(const char *path, int mode,
+ off_t offset, off_t length, struct fuse_file_info *fi)
+{
+ int fd;
+ int res;
+
+ (void) fi;
+
+ if (mode)
+ return -EOPNOTSUPP;
+
+ fd = open(path, O_WRONLY);
+ if (fd == -1)
+ return -errno;
+
+ res = -posix_fallocate(fd, offset, length);
+
+ close(fd);
+ return res;
+}
+#endif
+
+#ifdef HAVE_SETXATTR
+/* xattr operations are optional and can safely be left unimplemented */
+static int xmp_setxattr(const char *path, const char *name, const char *value,
+ size_t size, int flags)
+{
+ int res = lsetxattr(path, name, value, size, flags);
+ if (res == -1)
+ return -errno;
+ return 0;
+}
+
+static int xmp_getxattr(const char *path, const char *name, char *value,
+ size_t size)
+{
+ int res = lgetxattr(path, name, value, size);
+ if (res == -1)
+ return -errno;
+ return res;
+}
+
+static int xmp_listxattr(const char *path, char *list, size_t size)
+{
+ int res = llistxattr(path, list, size);
+ if (res == -1)
+ return -errno;
+ return res;
+}
+
+static int xmp_removexattr(const char *path, const char *name)
+{
+ int res = lremovexattr(path, name);
+ if (res == -1)
+ return -errno;
+ return 0;
+}
+#endif /* HAVE_SETXATTR */
+
+static struct fuse_operations xmp_oper = {
+ .getattr = xmp_getattr,
+ .access = xmp_access,
+ .readlink = xmp_readlink,
+ .readdir = xmp_readdir,
+ .mknod = xmp_mknod,
+ .mkdir = xmp_mkdir,
+ .symlink = xmp_symlink,
+ .unlink = xmp_unlink,
+ .rmdir = xmp_rmdir,
+ .rename = xmp_rename,
+ .link = xmp_link,
+ .chmod = xmp_chmod,
+ .chown = xmp_chown,
+ .truncate = xmp_truncate,
+#ifdef HAVE_UTIMENSAT
+ .utimens = xmp_utimens,
+#endif
+ .open = xmp_open,
+ .read = xmp_read,
+ .write = xmp_write,
+ .statfs = xmp_statfs,
+ .release = xmp_release,
+ .fsync = xmp_fsync,
+#ifdef HAVE_POSIX_FALLOCATE
+ .fallocate = xmp_fallocate,
+#endif
+#ifdef HAVE_SETXATTR
+ .setxattr = xmp_setxattr,
+ .getxattr = xmp_getxattr,
+ .listxattr = xmp_listxattr,
+ .removexattr = xmp_removexattr,
+#endif
+};
+
+int main(int argc, char *argv[])
+{
+ umask(0);
+ return fuse_main(argc, argv, &xmp_oper, NULL);
+}
--- /dev/null
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+ Copyright (C) 2011 Sebastian Pipping <sebastian@pipping.org>
+
+ This program can be distributed under the terms of the GNU GPL.
+ See the file COPYING.
+*/
+
+/** @file
+ * @tableofcontents
+ *
+ * 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. This implementation is a little more sophisticated
+ * than the one in passthrough.c, so performance is not quite as bad.
+ *
+ * \section section_compile compiling this example
+ *
+ * gcc -Wall passthrough_fh.c `pkg-config fuse3 --cflags --libs` -lulockmgr -o passthrough_fh
+ *
+ * \section section_source the complete source
+ * \include passthrough_fh.c
+ */
+
+#define FUSE_USE_VERSION 30
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+
+#include <fuse.h>
+
+#ifdef HAVE_LIBULOCKMGR
+#include <ulockmgr.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <errno.h>
+#include <sys/time.h>
+#ifdef HAVE_SETXATTR
+#include <sys/xattr.h>
+#endif
+#include <sys/file.h> /* flock(2) */
+
+static int xmp_getattr(const char *path, struct stat *stbuf)
+{
+ int res;
+
+ res = lstat(path, stbuf);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_fgetattr(const char *path, struct stat *stbuf,
+ struct fuse_file_info *fi)
+{
+ int res;
+
+ (void) path;
+
+ res = fstat(fi->fh, stbuf);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_access(const char *path, int mask)
+{
+ int res;
+
+ res = access(path, mask);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_readlink(const char *path, char *buf, size_t size)
+{
+ int res;
+
+ res = readlink(path, buf, size - 1);
+ if (res == -1)
+ return -errno;
+
+ buf[res] = '\0';
+ return 0;
+}
+
+struct xmp_dirp {
+ DIR *dp;
+ struct dirent *entry;
+ off_t offset;
+};
+
+static int xmp_opendir(const char *path, struct fuse_file_info *fi)
+{
+ int res;
+ struct xmp_dirp *d = malloc(sizeof(struct xmp_dirp));
+ if (d == NULL)
+ return -ENOMEM;
+
+ d->dp = opendir(path);
+ if (d->dp == NULL) {
+ res = -errno;
+ free(d);
+ return res;
+ }
+ d->offset = 0;
+ d->entry = NULL;
+
+ fi->fh = (unsigned long) d;
+ return 0;
+}
+
+static inline struct xmp_dirp *get_dirp(struct fuse_file_info *fi)
+{
+ return (struct xmp_dirp *) (uintptr_t) fi->fh;
+}
+
+static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
+ off_t offset, struct fuse_file_info *fi,
+ enum fuse_readdir_flags flags)
+{
+ struct xmp_dirp *d = get_dirp(fi);
+
+ (void) path;
+ if (offset != d->offset) {
+ seekdir(d->dp, offset);
+ d->entry = NULL;
+ d->offset = offset;
+ }
+ while (1) {
+ struct stat st;
+ off_t nextoff;
+ enum fuse_fill_dir_flags fill_flags = 0;
+
+ if (!d->entry) {
+ d->entry = readdir(d->dp);
+ if (!d->entry)
+ break;
+ }
+#ifdef HAVE_FSTATAT
+ if (flags & FUSE_READDIR_PLUS) {
+ int res;
+
+ res = fstatat(dirfd(d->dp), d->entry->d_name, &st,
+ AT_SYMLINK_NOFOLLOW);
+ if (res != -1)
+ fill_flags |= FUSE_FILL_DIR_PLUS;
+ }
+#endif
+ if (!(fill_flags & FUSE_FILL_DIR_PLUS)) {
+ memset(&st, 0, sizeof(st));
+ st.st_ino = d->entry->d_ino;
+ st.st_mode = d->entry->d_type << 12;
+ }
+ nextoff = telldir(d->dp);
+ if (filler(buf, d->entry->d_name, &st, nextoff, fill_flags))
+ break;
+
+ d->entry = NULL;
+ d->offset = nextoff;
+ }
+
+ return 0;
+}
+
+static int xmp_releasedir(const char *path, struct fuse_file_info *fi)
+{
+ struct xmp_dirp *d = get_dirp(fi);
+ (void) path;
+ closedir(d->dp);
+ free(d);
+ return 0;
+}
+
+static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
+{
+ int res;
+
+ if (S_ISFIFO(mode))
+ res = mkfifo(path, mode);
+ else
+ res = mknod(path, mode, rdev);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_mkdir(const char *path, mode_t mode)
+{
+ int res;
+
+ res = mkdir(path, mode);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_unlink(const char *path)
+{
+ int res;
+
+ res = unlink(path);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_rmdir(const char *path)
+{
+ int res;
+
+ res = rmdir(path);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_symlink(const char *from, const char *to)
+{
+ int res;
+
+ res = symlink(from, to);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_rename(const char *from, const char *to, unsigned int flags)
+{
+ int res;
+
+ /* When we have renameat2() in libc, then we can implement flags */
+ if (flags)
+ return -EINVAL;
+
+ res = rename(from, to);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_link(const char *from, const char *to)
+{
+ int res;
+
+ res = link(from, to);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_chmod(const char *path, mode_t mode)
+{
+ int res;
+
+ res = chmod(path, mode);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_chown(const char *path, uid_t uid, gid_t gid)
+{
+ int res;
+
+ res = lchown(path, uid, gid);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_truncate(const char *path, off_t size)
+{
+ int res;
+
+ res = truncate(path, size);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_ftruncate(const char *path, off_t size,
+ struct fuse_file_info *fi)
+{
+ int res;
+
+ (void) path;
+
+ res = ftruncate(fi->fh, size);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+#ifdef HAVE_UTIMENSAT
+static int xmp_utimens(const char *path, const struct timespec ts[2])
+{
+ int res;
+
+ /* don't use utime/utimes since they follow symlinks */
+ res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+#endif
+
+static int xmp_create(const char *path, mode_t mode, struct fuse_file_info *fi)
+{
+ int fd;
+
+ fd = open(path, fi->flags, mode);
+ if (fd == -1)
+ return -errno;
+
+ fi->fh = fd;
+ return 0;
+}
+
+static int xmp_open(const char *path, struct fuse_file_info *fi)
+{
+ int fd;
+
+ fd = open(path, fi->flags);
+ if (fd == -1)
+ return -errno;
+
+ fi->fh = fd;
+ return 0;
+}
+
+static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
+ struct fuse_file_info *fi)
+{
+ int res;
+
+ (void) path;
+ res = pread(fi->fh, buf, size, offset);
+ if (res == -1)
+ res = -errno;
+
+ return res;
+}
+
+static int xmp_read_buf(const char *path, struct fuse_bufvec **bufp,
+ size_t size, off_t offset, struct fuse_file_info *fi)
+{
+ struct fuse_bufvec *src;
+
+ (void) path;
+
+ src = malloc(sizeof(struct fuse_bufvec));
+ if (src == NULL)
+ return -ENOMEM;
+
+ *src = FUSE_BUFVEC_INIT(size);
+
+ src->buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
+ src->buf[0].fd = fi->fh;
+ src->buf[0].pos = offset;
+
+ *bufp = src;
+
+ return 0;
+}
+
+static int xmp_write(const char *path, const char *buf, size_t size,
+ off_t offset, struct fuse_file_info *fi)
+{
+ int res;
+
+ (void) path;
+ res = pwrite(fi->fh, buf, size, offset);
+ if (res == -1)
+ res = -errno;
+
+ return res;
+}
+
+static int xmp_write_buf(const char *path, struct fuse_bufvec *buf,
+ off_t offset, struct fuse_file_info *fi)
+{
+ struct fuse_bufvec dst = FUSE_BUFVEC_INIT(fuse_buf_size(buf));
+
+ (void) path;
+
+ dst.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
+ dst.buf[0].fd = fi->fh;
+ dst.buf[0].pos = offset;
+
+ return fuse_buf_copy(&dst, buf, FUSE_BUF_SPLICE_NONBLOCK);
+}
+
+static int xmp_statfs(const char *path, struct statvfs *stbuf)
+{
+ int res;
+
+ res = statvfs(path, stbuf);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_flush(const char *path, struct fuse_file_info *fi)
+{
+ int res;
+
+ (void) path;
+ /* This is called from every close on an open file, so call the
+ close on the underlying filesystem. But since flush may be
+ called multiple times for an open file, this must not really
+ close the file. This is important if used on a network
+ filesystem like NFS which flush the data/metadata on close() */
+ res = close(dup(fi->fh));
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int xmp_release(const char *path, struct fuse_file_info *fi)
+{
+ (void) path;
+ close(fi->fh);
+
+ return 0;
+}
+
+static int xmp_fsync(const char *path, int isdatasync,
+ struct fuse_file_info *fi)
+{
+ int res;
+ (void) path;
+
+#ifndef HAVE_FDATASYNC
+ (void) isdatasync;
+#else
+ if (isdatasync)
+ res = fdatasync(fi->fh);
+ else
+#endif
+ res = fsync(fi->fh);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+#ifdef HAVE_POSIX_FALLOCATE
+static int xmp_fallocate(const char *path, int mode,
+ off_t offset, off_t length, struct fuse_file_info *fi)
+{
+ (void) path;
+
+ if (mode)
+ return -EOPNOTSUPP;
+
+ return -posix_fallocate(fi->fh, offset, length);
+}
+#endif
+
+#ifdef HAVE_SETXATTR
+/* xattr operations are optional and can safely be left unimplemented */
+static int xmp_setxattr(const char *path, const char *name, const char *value,
+ size_t size, int flags)
+{
+ int res = lsetxattr(path, name, value, size, flags);
+ if (res == -1)
+ return -errno;
+ return 0;
+}
+
+static int xmp_getxattr(const char *path, const char *name, char *value,
+ size_t size)
+{
+ int res = lgetxattr(path, name, value, size);
+ if (res == -1)
+ return -errno;
+ return res;
+}
+
+static int xmp_listxattr(const char *path, char *list, size_t size)
+{
+ int res = llistxattr(path, list, size);
+ if (res == -1)
+ return -errno;
+ return res;
+}
+
+static int xmp_removexattr(const char *path, const char *name)
+{
+ int res = lremovexattr(path, name);
+ if (res == -1)
+ return -errno;
+ return 0;
+}
+#endif /* HAVE_SETXATTR */
+
+#ifdef HAVE_LIBULOCKMGR
+static int xmp_lock(const char *path, struct fuse_file_info *fi, int cmd,
+ struct flock *lock)
+{
+ (void) path;
+
+ return ulockmgr_op(fi->fh, cmd, lock, &fi->lock_owner,
+ sizeof(fi->lock_owner));
+}
+#endif
+
+static int xmp_flock(const char *path, struct fuse_file_info *fi, int op)
+{
+ int res;
+ (void) path;
+
+ res = flock(fi->fh, op);
+ if (res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static struct fuse_operations xmp_oper = {
+ .getattr = xmp_getattr,
+ .fgetattr = xmp_fgetattr,
+ .access = xmp_access,
+ .readlink = xmp_readlink,
+ .opendir = xmp_opendir,
+ .readdir = xmp_readdir,
+ .releasedir = xmp_releasedir,
+ .mknod = xmp_mknod,
+ .mkdir = xmp_mkdir,
+ .symlink = xmp_symlink,
+ .unlink = xmp_unlink,
+ .rmdir = xmp_rmdir,
+ .rename = xmp_rename,
+ .link = xmp_link,
+ .chmod = xmp_chmod,
+ .chown = xmp_chown,
+ .truncate = xmp_truncate,
+ .ftruncate = xmp_ftruncate,
+#ifdef HAVE_UTIMENSAT
+ .utimens = xmp_utimens,
+#endif
+ .create = xmp_create,
+ .open = xmp_open,
+ .read = xmp_read,
+ .read_buf = xmp_read_buf,
+ .write = xmp_write,
+ .write_buf = xmp_write_buf,
+ .statfs = xmp_statfs,
+ .flush = xmp_flush,
+ .release = xmp_release,
+ .fsync = xmp_fsync,
+#ifdef HAVE_POSIX_FALLOCATE
+ .fallocate = xmp_fallocate,
+#endif
+#ifdef HAVE_SETXATTR
+ .setxattr = xmp_setxattr,
+ .getxattr = xmp_getxattr,
+ .listxattr = xmp_listxattr,
+ .removexattr = xmp_removexattr,
+#endif
+#ifdef HAVE_LIBULOCKMGR
+ .lock = xmp_lock,
+#endif
+ .flock = xmp_flock,
+};
+
+int main(int argc, char *argv[])
+{
+ umask(0);
+ return fuse_main(argc, argv, &xmp_oper, NULL);
+}
--- /dev/null
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU GPL.
+ See the file COPYING.
+*/
+
+/** @file
+ * @tableofcontents
+ *
+ * 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.
+ *
+ * \section section_compile compiling this example
+ *
+ * gcc -Wall passthrough_ll.c `pkg-config fuse3 --cflags --libs` -o passthrough_ll
+ *
+ * \section section_source the complete source
+ * \include passthrough_ll.c
+ */
+
+#define _GNU_SOURCE
+#define FUSE_USE_VERSION 30
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <fuse_lowlevel.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <string.h>
+#include <dirent.h>
+#include <assert.h>
+#include <errno.h>
+#include <err.h>
+
+/* Compat stuff. Doesn't make it work, just makes it compile. */
+#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;
+};
+
+static struct lo_data *lo_data(fuse_req_t req)
+{
+ return (struct lo_data *) fuse_req_userdata(req);
+}
+
+static struct lo_inode *lo_inode(fuse_req_t req, fuse_ino_t ino)
+{
+ if (ino == FUSE_ROOT_ID)
+ return &lo_data(req)->root;
+ else
+ return (struct lo_inode *) (uintptr_t) ino;
+}
+
+static int lo_fd(fuse_req_t req, fuse_ino_t ino)
+{
+ return lo_inode(req, ino)->fd;
+}
+
+static bool lo_debug(fuse_req_t req)
+{
+ return lo_data(req)->debug != 0;
+}
+
+static void lo_getattr(fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi)
+{
+ int res;
+ struct stat buf;
+ (void) fi;
+
+ res = fstatat(lo_fd(req, ino), "", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
+ if (res == -1)
+ return (void) fuse_reply_err(req, errno);
+
+ fuse_reply_attr(req, &buf, 1.0);
+}
+
+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;
+}
+
+static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name,
+ struct fuse_entry_param *e)
+{
+ int newfd;
+ int res;
+ int saverr;
+ struct lo_inode *inode;
+
+ memset(e, 0, sizeof(*e));
+ e->attr_timeout = 1.0;
+ e->entry_timeout = 1.0;
+
+ 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;
+}
+
+static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
+{
+ struct fuse_entry_param e;
+ int err;
+
+ err = lo_do_lookup(req, parent, name, &e);
+ if (err)
+ fuse_reply_err(req, err);
+ else
+ fuse_reply_entry(req, &e);
+}
+
+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);
+}
+
+static void lo_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
+{
+ 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);
+
+ fuse_reply_none(req);
+}
+
+static void lo_readlink(fuse_req_t req, fuse_ino_t ino)
+{
+ char buf[PATH_MAX + 1];
+ int res;
+
+ res = readlinkat(lo_fd(req, ino), "", buf, sizeof(buf));
+ if (res == -1)
+ return (void) fuse_reply_err(req, errno);
+
+ if (res == sizeof(buf))
+ return (void) fuse_reply_err(req, ENAMETOOLONG);
+
+ buf[res] = '\0';
+
+ fuse_reply_readlink(req, buf);
+}
+
+struct lo_dirp {
+ int fd;
+ DIR *dp;
+ struct dirent *entry;
+ off_t offset;
+};
+
+static struct lo_dirp *lo_dirp(struct fuse_file_info *fi)
+{
+ return (struct lo_dirp *) (uintptr_t) fi->fh;
+}
+
+static void lo_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
+{
+ 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;
+
+ fi->fh = (uintptr_t) d;
+ fuse_reply_open(req, fi);
+ return;
+
+out_errno:
+ error = errno;
+out_err:
+ if (d) {
+ if (d->fd != -1)
+ close(d->fd);
+ free(d);
+ }
+ fuse_reply_err(req, error);
+}
+
+static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
+ off_t offset, struct fuse_file_info *fi, int plus)
+{
+ struct lo_dirp *d = lo_dirp(fi);
+ char *buf;
+ char *p;
+ size_t rem;
+ int err;
+
+ (void) ino;
+
+ buf = calloc(size, 1);
+ if (!buf)
+ return (void) fuse_reply_err(req, ENOMEM);
+
+ 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) {
+ struct fuse_entry_param e;
+
+ err = lo_do_lookup(req, ino, d->entry->d_name, &e);
+ if (err)
+ goto error;
+
+ entsize = fuse_add_direntry_plus(req, p, rem,
+ d->entry->d_name,
+ &e, nextoff);
+ } else {
+ struct stat st = {
+ .st_ino = d->entry->d_ino,
+ .st_mode = d->entry->d_type << 12,
+ };
+ entsize = fuse_add_direntry(req, p, rem,
+ d->entry->d_name,
+ &st, nextoff);
+ }
+ if (entsize > rem)
+ break;
+
+ p += entsize;
+ rem -= entsize;
+
+ d->entry = NULL;
+ d->offset = nextoff;
+ }
+
+ fuse_reply_buf(req, buf, size - rem);
+ free(buf);
+ return;
+
+error:
+ free(buf);
+ fuse_reply_err(req, err);
+}
+
+static void lo_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
+ off_t offset, struct fuse_file_info *fi)
+{
+ lo_do_readdir(req, ino, size, offset, fi, 0);
+}
+
+static void lo_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size,
+ off_t offset, struct fuse_file_info *fi)
+{
+ lo_do_readdir(req, ino, size, offset, fi, 1);
+}
+
+static void lo_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
+{
+ struct lo_dirp *d = lo_dirp(fi);
+ (void) ino;
+ closedir(d->dp);
+ free(d);
+ fuse_reply_err(req, 0);
+}
+
+static void lo_open(fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi)
+{
+ 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)
+ return (void) fuse_reply_err(req, errno);
+
+ fi->fh = fd;
+ fuse_reply_open(req, fi);
+}
+
+static void lo_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
+{
+ (void) ino;
+
+ close(fi->fh);
+ fuse_reply_err(req, 0);
+}
+
+static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size,
+ off_t offset, struct fuse_file_info *fi)
+{
+ struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size);
+
+ (void) ino;
+
+ buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
+ buf.buf[0].fd = fi->fh;
+ buf.buf[0].pos = offset;
+
+ fuse_reply_data(req, &buf, FUSE_BUF_SPLICE_MOVE);
+}
+
+static struct fuse_lowlevel_ops lo_oper = {
+ .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_args args = FUSE_ARGS_INIT(argc, argv);
+ struct fuse_session *se;
+ struct fuse_cmdline_opts opts;
+ struct lo_data lo = { .debug = 0 };
+ int ret = -1;
+
+ if (fuse_parse_cmdline(&args, &opts) != 0)
+ return 1;
+ if (opts.show_help) {
+ printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
+ fuse_cmdline_help();
+ fuse_lowlevel_help();
+ fuse_mount_help();
+ ret = 0;
+ goto err_out1;
+ } else if (opts.show_version) {
+ printf("FUSE library version %s\n", fuse_pkgversion());
+ fuse_lowlevel_version();
+ fuse_mount_version();
+ ret = 0;
+ goto err_out1;
+ }
+
+ lo.debug = opts.debug;
+ lo.root.next = lo.root.prev = &lo.root;
+ lo.root.fd = open("/", O_PATH);
+ lo.root.nlookup = 2;
+ if (lo.root.fd == -1)
+ err(1, "open(\"/\", O_PATH)");
+
+ se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo);
+ if (se == NULL)
+ goto err_out1;
+
+ if (fuse_set_signal_handlers(se) != 0)
+ goto err_out2;
+
+ if (fuse_session_mount(se, opts.mountpoint) != 0)
+ goto err_out3;
+
+ fuse_daemonize(opts.foreground);
+
+ /* Block until ctrl+c or fusermount -u */
+ if (opts.singlethread)
+ ret = fuse_session_loop(se);
+ else
+ ret = fuse_session_loop_mt(se);
+
+ fuse_session_unmount(se);
+err_out3:
+ fuse_remove_signal_handlers(se);
+err_out2:
+ fuse_session_destroy(se);
+err_out1:
+ free(opts.mountpoint);
+ fuse_opt_free_args(&args);
+
+ while (lo.root.next != &lo.root)
+ lo_free(lo.root.next);
+
+ return ret ? 1 : 0;
+}
else:
umount(mount_process, mnt_dir)
+@pytest.mark.parametrize("name", ('passthrough', 'passthrough_fh',
+ 'passthrough_ll'))
@pytest.mark.parametrize("options", LL_OPTIONS)
-def test_fuse_lo_plus(tmpdir, options):
+def test_passthrough(tmpdir, name, options):
mnt_dir = str(tmpdir.mkdir('mnt'))
src_dir = str(tmpdir.mkdir('src'))
cmdline = base_cmdline + \
- [ pjoin(basename, 'example', 'fuse_lo-plus'),
- '-f', '-s', mnt_dir ] + options
+ [ pjoin(basename, 'example', name),
+ '-f', mnt_dir ] + options
+ if not name.endswith('_ll'):
+ cmdline += [ '-o', 'use_ino,readdir_ino,kernel_cache' ]
mount_process = subprocess.Popen(cmdline)
try:
wait_for_mount(mount_process, mnt_dir)
work_dir = pjoin(mnt_dir, src_dir)
- tst_write(work_dir)
- tst_mkdir(work_dir)
- tst_symlink(work_dir)
- tst_mknod(work_dir)
- if os.getuid() == 0:
- tst_chown(work_dir)
- # Underlying fs may not have full nanosecond resolution
- tst_utimens(work_dir, ns_tol=1000)
- tst_link(work_dir)
- tst_readdir(work_dir)
- tst_statvfs(work_dir)
- tst_truncate_path(work_dir)
- tst_truncate_fd(work_dir)
- tst_unlink(work_dir)
- tst_passthrough(src_dir, work_dir)
- except:
- cleanup(mnt_dir)
- raise
- else:
- umount(mount_process, mnt_dir)
-@pytest.mark.parametrize("name", ('fusexmp', 'fusexmp_fh'))
-@pytest.mark.parametrize("options", LL_OPTIONS)
-def test_fusexmp_fh(tmpdir, name, options):
- mnt_dir = str(tmpdir.mkdir('mnt'))
- src_dir = str(tmpdir.mkdir('src'))
+ subprocess.check_call([ os.path.join(basename, 'test', 'test'),
+ work_dir, ':' + src_dir ])
- cmdline = base_cmdline + \
- [ pjoin(basename, 'example', name),
- '-f', '-o', 'use_ino,readdir_ino,kernel_cache',
- mnt_dir ] + options
- mount_process = subprocess.Popen(cmdline)
- try:
- wait_for_mount(mount_process, mnt_dir)
- work_dir = pjoin(mnt_dir, src_dir)
tst_write(work_dir)
tst_mkdir(work_dir)
tst_symlink(work_dir)
+++ /dev/null
-#!/usr/bin/env python3
-import pytest
-import sys
-
-if __name__ == '__main__':
- sys.exit(pytest.main([__file__] + sys.argv[1:]))
-
-import subprocess
-import os
-from util import wait_for_mount, umount, cleanup, base_cmdline
-
-basename = os.path.join(os.path.dirname(__file__), '..')
-
-def test_fuse(tmpdir):
- mnt_dir = str(tmpdir.mkdir('mnt'))
- src_dir = str(tmpdir.mkdir('src'))
-
- cmdline = base_cmdline + \
- [ os.path.join(basename, 'example', 'fusexmp_fh'),
- '-f', '-o' , 'use_ino,readdir_ino,kernel_cache',
- mnt_dir ]
- mount_process = subprocess.Popen(cmdline)
- try:
- wait_for_mount(mount_process, mnt_dir)
- cmdline = [ os.path.join(basename, 'test', 'test'),
- os.path.join(mnt_dir, src_dir),
- ':' + src_dir ]
- subprocess.check_call(cmdline)
- except:
- cleanup(mnt_dir)
- raise
- else:
- umount(mount_process, mnt_dir)