Hi Yiyan, Thanks for the patch! Some comments below...
On Mon, 24 Jul 2023 13:35:27 +0800 Li Yiyan <[email protected]> wrote: > Add support for fuse2/3 lowlevel API in erofsfuse, > pass the make check test in experimental-test branch. > Conduct performance evaluation, providing higher > performance compared to highlevel API while > retaining compatibility with highlevel API of fuse 2. > > Signed-off-by: Li Yiyan <[email protected]> > --- > configure.ac | 26 ++- > fuse/Makefile.am | 6 +- > fuse/lowlevel.c | 553 +++++++++++++++++++++++++++++++++++++++++++++++ > fuse/main.c | 83 ++++++- > 4 files changed, 653 insertions(+), 15 deletions(-) > create mode 100644 fuse/lowlevel.c > > diff --git a/configure.ac b/configure.ac > index a8cecd0..d8d648e 100644 > --- a/configure.ac > +++ b/configure.ac > @@ -336,15 +336,27 @@ AS_IF([test "x$with_selinux" != "xno"], [ > > # Configure fuse > AS_IF([test "x$enable_fuse" != "xno"], [ > - PKG_CHECK_MODULES([libfuse], [fuse >= 2.6]) > - # Paranoia: don't trust the result reported by pkgconfig before trying out > saved_LIBS="$LIBS" > saved_CPPFLAGS=${CPPFLAGS} > - CPPFLAGS="${libfuse_CFLAGS} ${CPPFLAGS}" > - LIBS="${libfuse_LIBS} $LIBS" > - AC_CHECK_LIB(fuse, fuse_main, [ > - have_fuse="yes" ], [ > - AC_MSG_ERROR([libfuse (>= 2.6) doesn't work properly])]) > + PKG_CHECK_MODULES([libfuse3], [fuse3 >= 3.0], [ > + AC_DEFINE([FUSE_USE_VERSION], [30], [used FUSE API version]) > + CPPFLAGS="${libfuse3_CFLAGS} ${CPPFLAGS}" > + LIBS="${libfuse3_LIBS} $LIBS" > + AC_CHECK_LIB(fuse3, fuse_session_new, [ AC_DEFINE([USE_LOWLEVEL], [1], > [Define to 1 if libfuse lowlevel api available]) ], [ > + AC_MSG_ERROR([libfuse3 (>= 3.0) doesn't work properly for lowlevel > api])]) > + have_fuse="yes" > + ], [ > + PKG_CHECK_MODULES([libfuse2], [fuse >= 2.6], [ > + AC_DEFINE([FUSE_USE_VERSION], [26], [used FUSE API version]) > + CPPFLAGS="${libfuse2_CFLAGS} ${CPPFLAGS}" > + LIBS="${libfuse2_LIBS} $LIBS" > + AC_CHECK_LIB(fuse, fuse_lowlevel_new, [ AC_DEFINE([USE_LOWLEVEL], [1], > [Define to 1 if libfuse lowlevel api available]) ], [ > + AC_MSG_NOTICE([libfuse (>= 2.6) doesn't work properly for lowlevel > api])]) > + AC_CHECK_LIB(fuse, fuse_main, , [ > + AC_MSG_ERROR([libfuse (>= 2.6) doesn't work properly for highlevel > api and lowlevel api])]) > + have_fuse="yes" > + ], [have_fuse="no"]) > + ]) We may drop high level part if low level is working fine, so `USE_LOWLEVEL` is unneeded. > LIBS="${saved_LIBS}" > CPPFLAGS="${saved_CPPFLAGS}"], [have_fuse="no"]) > > diff --git a/fuse/Makefile.am b/fuse/Makefile.am > index 50be783..d54fc89 100644 > --- a/fuse/Makefile.am > +++ b/fuse/Makefile.am > @@ -3,8 +3,8 @@ > AUTOMAKE_OPTIONS = foreign > noinst_HEADERS = $(top_srcdir)/fuse/macosx.h > bin_PROGRAMS = erofsfuse > -erofsfuse_SOURCES = main.c > +erofsfuse_SOURCES = main.c lowlevel.c > erofsfuse_CFLAGS = -Wall -I$(top_srcdir)/include > -erofsfuse_CFLAGS += -DFUSE_USE_VERSION=26 ${libfuse_CFLAGS} > ${libselinux_CFLAGS} > -erofsfuse_LDADD = $(top_builddir)/lib/liberofs.la ${libfuse_LIBS} > ${liblz4_LIBS} \ > +erofsfuse_CFLAGS += ${libfuse2_CFLAGS} ${libfuse3_CFLAGS} > ${libselinux_CFLAGS} > +erofsfuse_LDADD = $(top_builddir)/lib/liberofs.la ${libfuse2_LIBS} > ${libfuse3_LIBS} ${liblz4_LIBS} \ > ${libselinux_LIBS} ${liblzma_LIBS} ${zlib_LIBS} ${libdeflate_LIBS} > diff --git a/fuse/lowlevel.c b/fuse/lowlevel.c > new file mode 100644 > index 0000000..89d3077 > --- /dev/null > +++ b/fuse/lowlevel.c > @@ -0,0 +1,553 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * Created by Li Yiyan <[email protected]> > + */ > +#include "erofs/config.h" > +#include "erofs/dir.h" > +#include "erofs/inode.h" > +#include "erofs/io.h" > +#include "erofs/print.h" > +#include "macosx.h" > +#include "config.h" > +#include <fuse_opt.h> > +#include <libgen.h> > +#include <signal.h> > +#include <stdlib.h> > +#include <string.h> > +#include <float.h> > + > +#if FUSE_USE_VERSION >= 30 > +#include <fuse3/fuse_lowlevel.h> > +#include <fuse3/fuse.h> > +#else > +#include <fuse.h> > +#include <fuse_lowlevel.h> > +#endif > + > +#define TMP_BUF_SIZE 4096 I'm not sure it's a good definition. > +static const double EROFS_TIMEOUT = DBL_MAX; > + > +struct erofsfuse_ll_dir_context { > + struct erofs_dir_context ctx; > + > + fuse_req_t req; > + void *buf; > + int is_plus; > + size_t offset; > + size_t buf_size; > + size_t start_off; > + struct fuse_file_info *fi; > +}; > + > +struct erofsfuse_ll_dir_search_context { > + struct erofs_dir_context ctx; > + > + const char *target_name; > + size_t target_name_len; > + struct fuse_entry_param *ent; > +}; > + > +static int erofsfuse_ll_fill_dentries(struct erofs_dir_context *ctx) > +{ > + size_t r = 0; > + struct stat st = { 0 }; > +#if FUSE_USE_VERSION >= 30 > + struct fuse_entry_param param; > +#endif > + char dname[EROFS_NAME_LEN + 1]; > + struct erofsfuse_ll_dir_context *fusectx = (void *)ctx; Why using void pointer cast? > + > + if (fusectx->offset < fusectx->start_off) { > + fusectx->offset += > + ctx->de_namelen + sizeof(struct erofs_dirent); > + return 0; > + } > + > + strncpy(dname, ctx->dname, ctx->de_namelen); > + dname[ctx->de_namelen] = '\0'; > + fusectx->offset += ctx->de_namelen + sizeof(struct erofs_dirent); > + > + if (!fusectx->is_plus) { Why need `is_plus`? Could we move this part under #else branch instead? > + st.st_mode = erofs_ftype_to_dtype(ctx->de_ftype); > + st.st_ino = ctx->de_nid; > + > + r = fuse_add_direntry(fusectx->req, fusectx->buf, > + fusectx->buf_size, dname, &st, > + fusectx->offset); > + } else { > +#if FUSE_USE_VERSION >= 30 > + param.ino = ctx->de_nid; > + param.generation = 0; > + param.attr.st_ino = ctx->de_nid; > + param.attr.st_mode = erofs_ftype_to_dtype(ctx->de_ftype); > + param.attr_timeout = EROFS_TIMEOUT; > + param.entry_timeout = EROFS_TIMEOUT; > + > + r = fuse_add_direntry_plus(fusectx->req, fusectx->buf, > + fusectx->buf_size, dname, ¶m, > + fusectx->offset); > +#endif > + } > + > + if (r > fusectx->buf_size) { > + fusectx->offset -= > + ctx->de_namelen + sizeof(struct erofs_dirent); > + return 1; > + } > + fusectx->buf += r; > + fusectx->buf_size -= r; > + return 0; > +} > + > +static void erofsfuse_ll_fill_stat(struct erofs_inode *vi, struct stat > *stbuf) > +{ > + stbuf->st_mode = vi->i_mode; > + stbuf->st_nlink = vi->i_nlink; > + > + if (!S_ISDIR(stbuf->st_mode)) > + stbuf->st_size = vi->i_size; > + if (S_ISBLK(vi->i_mode) || S_ISCHR(vi->i_mode)) > + stbuf->st_rdev = vi->u.i_rdev; > + > + stbuf->st_blocks = roundup(vi->i_size, erofs_blksiz()) >> 9; > + > + stbuf->st_uid = vi->i_uid; > + stbuf->st_gid = vi->i_gid; > + > + stbuf->st_ctime = vi->i_mtime; > + stbuf->st_mtime = stbuf->st_ctime; > + stbuf->st_atime = stbuf->st_ctime; > +} > + > +static int erofsfuse_ll_search_dentries(struct erofs_dir_context *ctx) > +{ > + int r = 0; > + struct erofs_inode vi; > + struct erofsfuse_ll_dir_search_context *search_ctx = (void *)ctx; Why using void pointer cast? > + > + if (search_ctx->ent->ino == 0 && > + search_ctx->target_name_len == ctx->de_namelen && > + strncmp(search_ctx->target_name, ctx->dname, ctx->de_namelen) == > + 0) { > + search_ctx->ent->ino = ctx->de_nid; > + search_ctx->ent->attr.st_ino = ctx->de_nid; > + vi.nid = (erofs_nid_t)ctx->de_nid; > + > + r = erofs_read_inode_from_disk(&vi); > + if (r < 0) > + return r; > + > + erofsfuse_ll_fill_stat(&vi, &(search_ctx->ent->attr)); > + } > + > + return 0; > +} > + > +void erofsfuse_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, > + off_t off, struct fuse_file_info *fi) > +{ > + int err = 0; > + char *buf = malloc(size); > + struct erofsfuse_ll_dir_context ctx; > + struct erofs_inode *vi = (struct erofs_inode *)fi->fh; > + > + erofs_dbg("readdir, ino: %lu, req: %p, fh: %lu, size: %lu, off: %lu\n", > + ino, req, fi->fh, size, off); > + if (!buf) { > + fuse_reply_err(req, ENOMEM); > + return; > + } > + ctx.ctx.dir = vi; > + ctx.ctx.cb = erofsfuse_ll_fill_dentries; > + > + ctx.fi = fi; > + ctx.buf = buf; > + ctx.buf_size = size; > + ctx.req = req; > + ctx.offset = 0; > + ctx.is_plus = 0; > + ctx.start_off = off; > + > +#ifdef NDEBUG > + err = erofs_iterate_dir(&ctx.ctx, false); > +#else > + err = erofs_iterate_dir(&ctx.ctx, true); > +#endif > + > + if (err < 0) /* if buffer insufficient, return 1 */ > + fuse_reply_err(req, EIO); > + else > + fuse_reply_buf(req, buf, size - ctx.buf_size); > + > + free(buf); > +} > + > +void erofsfuse_ll_init(void *userdata, struct fuse_conn_info *conn) > +{ > + erofs_inode_manager_init(); > +} > + > +void erofsfuse_ll_open(fuse_req_t req, fuse_ino_t ino, > + struct fuse_file_info *fi) > +{ > + int ret = 0; > + struct erofs_inode *vi; > + > + erofs_dbg("open, ino = %lu, req = %p\n", ino, req); > + if (fi->flags & (O_WRONLY | O_RDWR)) { > + fuse_reply_err(req, EROFS); > + return; > + } > + > + if (ino == FUSE_ROOT_ID) { > + fuse_reply_err(req, EISDIR); > + return; > + } > + > + vi = (struct erofs_inode *)malloc(sizeof(struct erofs_inode)); > + if (!vi) { > + fuse_reply_err(req, ENOMEM); > + return; > + } > + > + vi->nid = (erofs_nid_t)ino; > + ret = erofs_read_inode_from_disk(vi); > + if (ret < 0) { > + fuse_reply_err(req, EIO); > + goto out; > + } > + > + if (!S_ISREG(vi->i_mode)) { > + fuse_reply_err(req, EISDIR); > + goto out; goto is not needed. > + } else { > + fi->fh = (uint64_t)vi; > + fi->keep_cache = 1; > + fuse_reply_open(req, fi); > + return; > + } > + > +out: > + free(vi); > +} > + > +void erofsfuse_ll_getattr(fuse_req_t req, fuse_ino_t ino, > + struct fuse_file_info *fi) > +{ > + int ret; > + struct stat stbuf = { 0 }; > + struct erofs_inode vi; > + > + erofs_dbg("getattr triggered, ino: %lu, req: %p\n", ino, req); > + vi.nid = ino == FUSE_ROOT_ID ? sbi.root_nid : (erofs_nid_t)ino; We use this in a couple of places. > + ret = erofs_read_inode_from_disk(&vi); > + if (ret < 0) { > + erofs_dbg("read inode from disk failed, nid = %lu\n", vi.nid); > + fuse_reply_err(req, ENOENT); > + } > + > + erofsfuse_ll_fill_stat(&vi, &stbuf); > + stbuf.st_ino = ino; > + > + fuse_reply_attr(req, &stbuf, EROFS_TIMEOUT); > +} > + > +void erofsfuse_ll_opendir(fuse_req_t req, fuse_ino_t ino, > + struct fuse_file_info *fi) > +{ > + int ret; > + struct erofs_inode *vi; > + > + erofs_dbg("opendir, ino = %lu\n", ino); > + vi = (struct erofs_inode *)malloc(sizeof(struct erofs_inode)); > + if (!vi) { > + fuse_reply_err(req, ENOMEM); > + return; > + } > + > + vi->nid = ino == FUSE_ROOT_ID ? sbi.root_nid : (erofs_nid_t)ino; > + ret = erofs_read_inode_from_disk(vi); > + if (ret < 0) { > + fuse_reply_err(req, EIO); > + goto out; > + } > + > + if (!S_ISDIR(vi->i_mode)) { > + fuse_reply_err(req, ENOTDIR); > + goto out; > + } > + > + fi->fh = (uint64_t)vi; > + fuse_reply_open(req, fi); > + return; > + > +out: > + free(vi); > +} > + > +void erofsfuse_ll_releasedir(fuse_req_t req, fuse_ino_t ino, > + struct fuse_file_info *fi) > +{ > + free((struct erofs_inode *)fi->fh); > + fi->fh = 0; > + fuse_reply_err(req, 0); > +} > + > +void erofsfuse_ll_release(fuse_req_t req, fuse_ino_t ino, > + struct fuse_file_info *fi) > +{ > + free((struct erofs_inode *)fi->fh); > + fi->fh = 0; > + fuse_reply_err(req, 0); > +} duplicated function code. > + > +void erofsfuse_ll_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) > +{ > + int err, ret; > + struct erofs_inode *vi; > + struct fuse_entry_param fentry; > + struct erofsfuse_ll_dir_search_context ctx; > + > + vi = (struct erofs_inode *)malloc(sizeof(struct erofs_inode)); > + if (!vi) { > + fuse_reply_err(req, ENOMEM); > + return; > + } > + > + vi->nid = parent == FUSE_ROOT_ID ? sbi.root_nid : (erofs_nid_t)parent; > + ret = erofs_read_inode_from_disk(vi); > + if (ret < 0) { > + fuse_reply_err(req, EIO); > + goto out; > + } > + if (!S_ISDIR(vi->i_mode)) { > + fuse_reply_err(req, ENOTDIR); > + goto out; > + } > + > + memset(&fentry, 0, sizeof(fentry)); > + fentry.ino = 0; > + fentry.attr_timeout = fentry.entry_timeout = EROFS_TIMEOUT; > + ctx.ctx.dir = vi; > + ctx.ctx.cb = erofsfuse_ll_search_dentries; > + > + ctx.ent = &fentry; > + ctx.target_name = name; > + ctx.target_name_len = strlen(name); > + > +#ifdef NDEBUG > + err = erofs_iterate_dir(&ctx.ctx, false); > +#else > + err = erofs_iterate_dir(&ctx.ctx, true); > +#endif > + > + if (err < 0) { > + fuse_reply_err(req, EIO); > + goto out; > + } > + fuse_reply_entry(req, &fentry); > + > +out: > + free(vi); > +} > + > +void erofsfuse_ll_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t > off, > + struct fuse_file_info *fi) > +{ > + int ret; > + struct erofs_inode *vi = (struct erofs_inode *)fi->fh; > + char *buf = malloc(size); > + > + erofs_dbg("read, ino = %lu, size = %lu, off = %lu, fh = %lu\n", ino, > + size, off, fi->fh); > + if (!buf) { > + fuse_reply_err(req, ENOMEM); > + return; > + } > + > + if (!S_ISREG(vi->i_mode)) { > + fuse_reply_err(req, EIO); > + goto out; > + } > + > + ret = erofs_pread(vi, buf, size, off); > + if (ret == 0) { > + if (off >= vi->i_size) > + ret = 0; > + else if (off + size > vi->i_size) > + ret = vi->i_size - off; > + else > + ret = size; > + } else { > + fuse_reply_err(req, EIO); > + goto out; > + } if (ret) { fuse_reply_err(req, EIO); goto out; } if (off >= vi->i_size) ret = 0; else if (off + size > vi->i_size) ret = vi->i_size - off; else ret = size; > + > + fuse_reply_buf(req, buf, ret); > + > +out: > + free(buf); > +} > + > +void erofsfuse_ll_readlink(fuse_req_t req, fuse_ino_t ino) > +{ > + int ret; > + char *dst; > + struct erofs_inode vi; > + > + erofs_dbg("read_link, ino = %lu\n", ino); > + vi.nid = ino == FUSE_ROOT_ID ? sbi.root_nid : (erofs_nid_t)ino; > + ret = erofs_read_inode_from_disk(&vi); > + if (ret < 0) { > + fuse_reply_err(req, EIO); > + return; > + } > + > + if (!S_ISLNK(vi.i_mode)) { > + fuse_reply_err(req, EINVAL); > + return; > + } > + > + dst = malloc(vi.i_size + 1); > + if (!dst) { > + fuse_reply_err(req, ENOMEM); > + return; > + } > + > + ret = erofs_pread(&vi, dst, vi.i_size, 0); > + if (ret < 0) { > + fuse_reply_err(req, EIO); > + goto out; > + } > + > + dst[vi.i_size] = '\0'; > + fuse_reply_readlink(req, dst); > + > +out: > + free(dst); > +} > + > +void erofsfuse_ll_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, > + size_t size) > +{ > + int ret; > + char *buf = NULL; > + struct erofs_inode vi; > + size_t real = size == 0 ? TMP_BUF_SIZE : size; > + > + erofs_dbg("getxattr, ino = %lu, name = %s, size = %lu\n", ino, name, > + size); > + vi.nid = ino == FUSE_ROOT_ID ? sbi.root_nid : (erofs_nid_t)ino; > + ret = erofs_read_inode_from_disk(&vi); > + if (ret < 0) { > + fuse_reply_err(req, EIO); > + return; > + } > + > + buf = malloc(real); > + if (!buf) { > + fuse_reply_err(req, ENOMEM); > + return; > + } > + > + ret = erofs_getxattr(&vi, name, buf, real); > + if (ret < 0) > + fuse_reply_err(req, -ret); > + else if (size == 0) > + fuse_reply_xattr(req, ret); > + else > + fuse_reply_buf(req, buf, ret); > + > + free(buf); > +} > + > +void erofsfuse_ll_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) > +{ > + int ret; > + char *buf = NULL; > + struct erofs_inode vi; > + size_t real = size == 0 ? TMP_BUF_SIZE : size; > + > + erofs_dbg("listxattr, ino = %lu, size = %lu\n", ino, size); > + vi.nid = ino == FUSE_ROOT_ID ? sbi.root_nid : (erofs_nid_t)ino; > + ret = erofs_read_inode_from_disk(&vi); > + if (ret < 0) { > + fuse_reply_err(req, EIO); > + return; > + } > + > + buf = malloc(real); > + if (!buf) { > + fuse_reply_err(req, ENOMEM); > + return; > + } > + > + ret = erofs_listxattr(&vi, buf, real); > + if (ret < 0) > + fuse_reply_err(req, -ret); > + else if (size == 0) > + fuse_reply_xattr(req, ret); > + else > + fuse_reply_buf(req, buf, ret); > + > + free(buf); > +} > + > +#if FUSE_USE_VERSION >= 30 > +void erofsfuse_ll_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size, > + off_t off, struct fuse_file_info *fi) > +{ > + int err = 0; > + char *buf = malloc(size); > + struct erofsfuse_ll_dir_context ctx; > + struct erofs_inode *vi = (struct erofs_inode *)fi->fh; > + > + erofs_dbg("readdirplus, ino = %lu, size = %lu, off = %lu, fh = %lu\n", > + ino, size, off, fi->fh); > + if (!buf) { > + fuse_reply_err(req, ENOMEM); > + return; > + } > + ctx.ctx.dir = vi; > + ctx.ctx.cb = erofsfuse_ll_fill_dentries; > + > + ctx.fi = fi; > + ctx.buf = buf; > + ctx.buf_size = size; > + ctx.req = req; > + ctx.offset = 0; > + ctx.is_plus = 1; > + ctx.start_off = off; > + > +#ifdef NDEBUG > + err = erofs_iterate_dir(&ctx.ctx, false); > +#else > + err = erofs_iterate_dir(&ctx.ctx, true); > +#endif > + > + if (err < 0) /* if buffer insufficient, return 1 */ > + fuse_reply_err(req, EIO); > + else > + fuse_reply_buf(req, buf, size - ctx.buf_size); > + > + free(buf); > +} almost same as _readdir() except `is_plus`. > +#endif > + > +struct fuse_lowlevel_ops erofsfuse_lops = { > + .getxattr = erofsfuse_ll_getxattr, > + .opendir = erofsfuse_ll_opendir, > + .releasedir = erofsfuse_ll_releasedir, > + .release = erofsfuse_ll_release, > + .lookup = erofsfuse_ll_lookup, > + .listxattr = erofsfuse_ll_listxattr, > + .readlink = erofsfuse_ll_readlink, > + .getattr = erofsfuse_ll_getattr, > + .readdir = erofsfuse_ll_readdir, > +#if FUSE_USE_VERSION >= 30 > + .readdirplus = erofsfuse_ll_readdirplus, > +#endif > + .open = erofsfuse_ll_open, > + .read = erofsfuse_ll_read, > + .init = erofsfuse_ll_init, > +}; all functions above should be static. > diff --git a/fuse/main.c b/fuse/main.c > index b060e06..11726c1 100644 > --- a/fuse/main.c > +++ b/fuse/main.c > @@ -6,8 +6,6 @@ > #include <string.h> > #include <signal.h> > #include <libgen.h> > -#include <fuse.h> > -#include <fuse_opt.h> > #include "macosx.h" > #include "erofs/config.h" > #include "erofs/print.h" > @@ -15,6 +13,23 @@ > #include "erofs/dir.h" > #include "erofs/inode.h" > > +#if FUSE_USE_VERSION >= 30 > +#include <fuse3/fuse.h> > +#include <fuse3/fuse_lowlevel.h> > +#else > +#include <fuse.h> > +#include <fuse_lowlevel.h> > +#endif > + > +#if USE_LOWLEVEL no needed. > +#include <float.h> > + > +extern struct fuse_lowlevel_ops erofsfuse_lops; > + > +struct erofs_ll_ctx { > + unsigned int debug_lvl; any user? > +}; > +#else > struct erofsfuse_dir_context { > struct erofs_dir_context ctx; > fuse_fill_dir_t filler; > @@ -176,7 +191,7 @@ static int erofsfuse_listxattr(const char *path, char > *list, size_t size) > return erofs_listxattr(&vi, list, size); > } > > -static struct fuse_operations erofs_ops = { > +static struct fuse_operations erofsfuse_ops = { > .getxattr = erofsfuse_getxattr, > .listxattr = erofsfuse_listxattr, > .readlink = erofsfuse_readlink, > @@ -187,6 +202,8 @@ static struct fuse_operations erofs_ops = { > .init = erofsfuse_init, > }; > > +#endif > + > static struct options { > const char *disk; > const char *mountpoint; > @@ -207,7 +224,9 @@ static const struct fuse_opt option_spec[] = { > > static void usage(void) > { > +#if FUSE_MAJOR_VERSION < 3 > struct fuse_args args = FUSE_ARGS_INIT(0, NULL); > +#endif > > fputs("usage: [options] IMAGE MOUNTPOINT\n\n" > "Options:\n" > @@ -257,8 +276,12 @@ static int optional_opt_func(void *data, const char > *arg, int key, > fusecfg.disk = strdup(arg); > return 0; > } > - if (!fusecfg.mountpoint) > + if (!fusecfg.mountpoint) { > fusecfg.mountpoint = strdup(arg); > +#if USE_LOWLEVEL no needed. > + return 0; > +#endif > + } > case FUSE_OPT_KEY_OPT: > if (!strcmp(arg, "-d")) > fusecfg.odebug = true; > @@ -337,8 +360,58 @@ int main(int argc, char *argv[]) > goto err_dev_close; > } > > - ret = fuse_main(args.argc, args.argv, &erofs_ops, NULL); > +#if USE_LOWLEVEL no needed. > + struct erofs_ll_ctx *ll_ctx; > + struct fuse_session *se; > + > + ll_ctx = malloc(sizeof(struct erofs_ll_ctx)); > + if (!ll_ctx) { > + fprintf(stderr, "failed to alloc memory for ll_ctx\n"); > + goto err_sb_put; > + } > + > +#if FUSE_USE_VERSION >= 30 > + se = fuse_session_new(&args, &erofsfuse_lops, sizeof(erofsfuse_lops), > + ll_ctx); > + if (se != NULL) { > + fuse_set_signal_handlers(se); > + fuse_session_mount(se, fusecfg.mountpoint); > + > + ret = fuse_session_loop(se); > + > + fuse_remove_signal_handlers(se); > + fuse_session_unmount(se); > + fuse_session_destroy(se); > + } > +#else > + struct fuse_chan *ch; > + > + ch = fuse_mount(fusecfg.mountpoint, &args); > + if (ch != NULL) { > + se = fuse_lowlevel_new(&args, &erofsfuse_lops, > + sizeof(erofsfuse_lops), ll_ctx); > + if (se != NULL) { > + if (fuse_set_signal_handlers(se) != -1) { > + fuse_session_add_chan(se, ch); > + ret = fuse_session_loop(se); > + fuse_remove_signal_handlers(se); > + fuse_session_remove_chan(ch); > + } > + fuse_session_destroy(se); > + } > + fuse_unmount(fusecfg.mountpoint, ch); > + } > +#endif > + > + free(ll_ctx); > > +#else > + ret = fuse_main(args.argc, args.argv, &erofsfuse_ops, NULL); > +#endif > + > +#if USE_LOWLEVEL no needed. > +err_sb_put: > +#endif > erofs_put_super(); > err_dev_close: > blob_closeall();
