[PATCH v10] erofs-utils: add support for fuse 2/3 lowlevel API
Li Yiyan
lyy0627 at sjtu.edu.cn
Mon Sep 18 19:03:06 AEST 2023
Support FUSE low-level APIs for erofsfuse. Lowlevel APIs offer improved
performance compared to the previous high-level APIs, while maintaining
compatibility with libfuse version 2(>=2.6) and 3 (>=3.0).
Dataset: linux 5.15
Compression algorithm: lz4hc,12
Additional options: -T0 -C16384
Test options: --warmup 3 -p "echo 3 > /proc/sys/vm/drop_caches; sleep 1"
Evaluation result (highlevel->lowlevel avg time):
- Sequence metadata: 777.3 ms->180.9 ms
- Sequence data: 3.282 s->818.1 ms
- Random metadata: 1.571 s->928.3 ms
- Random data: 2.461 s->597.8 ms
Signed-off-by: Li Yiyan <lyy0627 at sjtu.edu.cn>
---
Changes since v9:
- rename confusing off/start_off in readdir
- avoid potential uninitialization
- fix known bugs and memory leaks
Changes since v8:
- correctly set umode of readdir
- fill full stat for readdirplus
Changes since v7:
- optimize errno
- optimize mapping between nid and ino
- remove redundant workaround
Changes since v6:
- remove redundant code
- optimize naming
- add eval data
Changes since v5:
- name retval to `ret` from `err`
Changes since v4:
- support fuse option
- default run in daemon
- remove redundant log
Changes since v3:
- remove ll identifier
- optimize naming
- remove redundant log
- add fixme label for confusing xattr_buf
Changes since v2:
- merge lowlevel.c into main.c
- fix typo in autoconf
- optimize naming
- remove redundant code
- avoid global sbi dependencies
Changes since v1:
- remove highlevel fallback path
- remove redundant code
- add static for erofsfuse_ll_func
configure.ac | 23 +-
fuse/Makefile.am | 4 +-
fuse/main.c | 621 ++++++++++++++++++++++++++++++++++--------
include/erofs/inode.h | 1 +
lib/inode.c | 19 ++
5 files changed, 542 insertions(+), 126 deletions(-)
diff --git a/configure.ac b/configure.ac
index 51ace67..cb3e097 100644
--- a/configure.ac
+++ b/configure.ac
@@ -338,15 +338,26 @@ 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_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_MSG_ERROR([libfuse (>= 2.6) doesn't work properly for lowlevel api])])
+ have_fuse="yes"
+ ], [have_fuse="no"])
+ ])
LIBS="${saved_LIBS}"
CPPFLAGS="${saved_CPPFLAGS}"], [have_fuse="no"])
diff --git a/fuse/Makefile.am b/fuse/Makefile.am
index 50be783..c63efcd 100644
--- a/fuse/Makefile.am
+++ b/fuse/Makefile.am
@@ -5,6 +5,6 @@ noinst_HEADERS = $(top_srcdir)/fuse/macosx.h
bin_PROGRAMS = erofsfuse
erofsfuse_SOURCES = main.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/main.c b/fuse/main.c
index 821d98c..1587538 100644
--- a/fuse/main.c
+++ b/fuse/main.c
@@ -1,13 +1,12 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Created by Li Guifu <blucerlee at gmail.com>
+ * Lowlevel added by Li Yiyan <lyy0627 at sjtu.edu.cn>
*/
#include <stdlib.h>
#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,177 +14,499 @@
#include "erofs/dir.h"
#include "erofs/inode.h"
-struct erofsfuse_dir_context {
+#include <float.h>
+#include <fuse.h>
+#include <fuse_lowlevel.h>
+
+#define EROFSFUSE_TIMEOUT DBL_MAX
+
+struct erofsfuse_readdir_context {
struct erofs_dir_context ctx;
- fuse_fill_dir_t filler;
- struct fuse_file_info *fi;
+
+ fuse_req_t req;
void *buf;
+ int is_plus;
+ size_t index;
+ size_t buf_rem;
+ size_t offset;
+ struct fuse_file_info *fi;
+};
+
+struct erofsfuse_lookupdir_context {
+ struct erofs_dir_context ctx;
+
+ const char *target_name;
+ struct fuse_entry_param *ent;
};
-static int erofsfuse_fill_dentries(struct erofs_dir_context *ctx)
+static inline erofs_nid_t erofsfuse_to_nid(fuse_ino_t ino)
+{
+ if (ino == FUSE_ROOT_ID)
+ return sbi.root_nid;
+ return (erofs_nid_t)(ino - FUSE_ROOT_ID);
+}
+
+static inline fuse_ino_t erofsfuse_to_ino(erofs_nid_t nid)
+{
+ if (nid == sbi.root_nid)
+ return FUSE_ROOT_ID;
+ return (nid + FUSE_ROOT_ID);
+}
+
+static void erofsfuse_fill_stat(struct erofs_inode *vi, struct stat *stbuf)
+{
+ if (S_ISBLK(vi->i_mode) || S_ISCHR(vi->i_mode))
+ stbuf->st_rdev = vi->u.i_rdev;
+
+ stbuf->st_mode = vi->i_mode;
+ stbuf->st_nlink = vi->i_nlink;
+ stbuf->st_size = vi->i_size;
+ stbuf->st_blocks = roundup(vi->i_size, erofs_blksiz(&sbi)) >> 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_add_dentry(struct erofs_dir_context *ctx)
{
- struct erofsfuse_dir_context *fusectx = (void *)ctx;
- struct stat st = {0};
+ size_t entsize = 0;
char dname[EROFS_NAME_LEN + 1];
+ struct erofsfuse_readdir_context *readdir_ctx = (void *)ctx;
+
+ if (readdir_ctx->index < readdir_ctx->offset) {
+ readdir_ctx->index++;
+ return 0;
+ }
strncpy(dname, ctx->dname, ctx->de_namelen);
dname[ctx->de_namelen] = '\0';
- st.st_mode = erofs_ftype_to_dtype(ctx->de_ftype) << 12;
- fusectx->filler(fusectx->buf, dname, &st, 0);
+
+ if (!readdir_ctx->is_plus) { /* fuse 3 still use non-plus readdir */
+ struct stat st = { 0 };
+
+ st.st_mode = erofs_ftype_to_mode(ctx->de_ftype, 0);
+ st.st_ino = erofsfuse_to_ino(ctx->de_nid);
+ entsize = fuse_add_direntry(readdir_ctx->req, readdir_ctx->buf,
+ readdir_ctx->buf_rem, dname, &st,
+ readdir_ctx->index + 1);
+ } else {
+#if FUSE_MAJOR_VERSION >= 3
+ int ret;
+ struct erofs_inode vi = {
+ .sbi = &sbi,
+ .nid = ctx->de_nid
+ };
+
+ ret = erofs_read_inode_from_disk(&vi);
+ if (ret < 0)
+ return ret;
+
+ struct fuse_entry_param param = {
+ .ino = erofsfuse_to_ino(ctx->de_nid),
+ .attr.st_ino = erofsfuse_to_ino(ctx->de_nid),
+ .generation = 0,
+
+ .attr_timeout = EROFSFUSE_TIMEOUT,
+ .entry_timeout = EROFSFUSE_TIMEOUT,
+ };
+ erofsfuse_fill_stat(&vi, &(param.attr));
+
+ entsize = fuse_add_direntry_plus(readdir_ctx->req,
+ readdir_ctx->buf,
+ readdir_ctx->buf_rem, dname,
+ ¶m, readdir_ctx->index + 1);
+#else
+ return -EOPNOTSUPP;
+#endif
+ }
+
+ if (entsize > readdir_ctx->buf_rem)
+ return 1;
+ readdir_ctx->index++;
+ readdir_ctx->buf += entsize;
+ readdir_ctx->buf_rem -= entsize;
return 0;
}
-int erofsfuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
- off_t offset, struct fuse_file_info *fi)
+static int erofsfuse_lookup_dentry(struct erofs_dir_context *ctx)
{
- int ret;
- struct erofs_inode dir;
- struct erofsfuse_dir_context ctx = {
- .ctx.dir = &dir,
- .ctx.cb = erofsfuse_fill_dentries,
- .filler = filler,
- .fi = fi,
- .buf = buf,
- };
- erofs_dbg("readdir:%s offset=%llu", path, (long long)offset);
-
- dir.sbi = &sbi;
- ret = erofs_ilookup(path, &dir);
- if (ret)
- return ret;
-
- erofs_dbg("path=%s nid = %llu", path, dir.nid | 0ULL);
- if (!S_ISDIR(dir.i_mode))
- return -ENOTDIR;
+ struct erofsfuse_lookupdir_context *lookup_ctx = (void *)ctx;
- if (!dir.i_size)
+ if (lookup_ctx->ent->ino != 0 ||
+ strlen(lookup_ctx->target_name) != ctx->de_namelen)
return 0;
+
+ if (!strncmp(lookup_ctx->target_name, ctx->dname, ctx->de_namelen)) {
+ int ret;
+ struct erofs_inode vi = {
+ .sbi = &sbi,
+ .nid = (erofs_nid_t)ctx->de_nid,
+ };
+
+ ret = erofs_read_inode_from_disk(&vi);
+ if (ret < 0)
+ return ret;
+
+ lookup_ctx->ent->ino = erofsfuse_to_ino(ctx->de_nid);
+ lookup_ctx->ent->attr.st_ino = erofsfuse_to_ino(ctx->de_nid);
+
+ erofsfuse_fill_stat(&vi, &(lookup_ctx->ent->attr));
+ }
+ return 0;
+}
+
+static inline void erofsfuse_readdir_general(fuse_req_t req, fuse_ino_t ino,
+ size_t size, off_t off,
+ struct fuse_file_info *fi,
+ int plus)
+{
+ int ret = 0;
+ char *buf = NULL;
+ struct erofsfuse_readdir_context ctx = { 0 };
+ struct erofs_inode *vi = (struct erofs_inode *)fi->fh;
+
+ erofs_dbg("readdir(%llu): size: %zu, off: %lu, plus: %d", ino | 0ULL,
+ size, off, plus);
+
+ buf = malloc(size);
+ if (!buf) {
+ fuse_reply_err(req, ENOMEM);
+ return;
+ }
+ ctx.ctx.dir = vi;
+ ctx.ctx.cb = erofsfuse_add_dentry;
+
+ ctx.fi = fi;
+ ctx.buf = buf;
+ ctx.buf_rem = size;
+ ctx.req = req;
+ ctx.index = 0;
+ ctx.offset = off;
+ ctx.is_plus = plus;
+
#ifdef NDEBUG
- return erofs_iterate_dir(&ctx.ctx, false);
+ ret = erofs_iterate_dir(&ctx.ctx, false);
#else
- return erofs_iterate_dir(&ctx.ctx, true);
+ ret = erofs_iterate_dir(&ctx.ctx, true);
#endif
+
+ if (ret < 0) /* if buffer insufficient, return 1 */
+ fuse_reply_err(req, -ret);
+ else
+ fuse_reply_buf(req, buf, size - ctx.buf_rem);
+
+ free(buf);
+}
+
+static void erofsfuse_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
+ off_t off, struct fuse_file_info *fi)
+{
+ erofsfuse_readdir_general(req, ino, size, off, fi, 0);
+}
+
+#if FUSE_MAJOR_VERSION >= 3
+static void erofsfuse_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size,
+ off_t off, struct fuse_file_info *fi)
+{
+ erofsfuse_readdir_general(req, ino, size, off, fi, 1);
}
+#endif
-static void *erofsfuse_init(struct fuse_conn_info *info)
+static void erofsfuse_init(void *userdata, struct fuse_conn_info *conn)
{
- erofs_info("Using FUSE protocol %d.%d", info->proto_major, info->proto_minor);
- return NULL;
+ erofs_info("Using FUSE protocol %d.%d", conn->proto_major,
+ conn->proto_minor);
}
-static int erofsfuse_open(const char *path, struct fuse_file_info *fi)
+static void erofsfuse_open(fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi)
{
- erofs_dbg("open path=%s", path);
+ int ret = 0;
+ struct erofs_inode *vi;
- if ((fi->flags & O_ACCMODE) != O_RDONLY)
- return -EACCES;
+ if (fi->flags & (O_WRONLY | O_RDWR)) {
+ fuse_reply_err(req, EROFS);
+ return;
+ }
- return 0;
+ vi = (struct erofs_inode *)malloc(sizeof(struct erofs_inode));
+ if (!vi) {
+ fuse_reply_err(req, ENOMEM);
+ return;
+ }
+
+ vi->sbi = &sbi;
+ vi->nid = erofsfuse_to_nid(ino);
+ ret = erofs_read_inode_from_disk(vi);
+ if (ret < 0) {
+ fuse_reply_err(req, -ret);
+ goto out;
+ }
+
+ if (!S_ISREG(vi->i_mode)) {
+ fuse_reply_err(req, EISDIR);
+ } else {
+ fi->fh = (uint64_t)vi;
+ fi->keep_cache = 1;
+ fuse_reply_open(req, fi);
+ return;
+ }
+
+out:
+ free(vi);
}
-static int erofsfuse_getattr(const char *path, struct stat *stbuf)
+static void erofsfuse_getattr(fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi)
{
- struct erofs_inode vi = { .sbi = &sbi };
int ret;
+ struct stat stbuf = { 0 };
+ struct erofs_inode vi = { .sbi = &sbi, .nid = erofsfuse_to_nid(ino) };
- erofs_dbg("getattr(%s)", path);
- ret = erofs_ilookup(path, &vi);
- if (ret)
- return -ENOENT;
-
- stbuf->st_mode = vi.i_mode;
- stbuf->st_nlink = vi.i_nlink;
- stbuf->st_size = vi.i_size;
- stbuf->st_blocks = roundup(vi.i_size, erofs_blksiz(vi.sbi)) >> 9;
- stbuf->st_uid = vi.i_uid;
- stbuf->st_gid = vi.i_gid;
- if (S_ISBLK(vi.i_mode) || S_ISCHR(vi.i_mode))
- stbuf->st_rdev = vi.u.i_rdev;
- stbuf->st_ctime = vi.i_mtime;
- stbuf->st_mtime = stbuf->st_ctime;
- stbuf->st_atime = stbuf->st_ctime;
- return 0;
+ ret = erofs_read_inode_from_disk(&vi);
+ if (ret < 0)
+ fuse_reply_err(req, -ret);
+
+ erofsfuse_fill_stat(&vi, &stbuf);
+ stbuf.st_ino = ino;
+
+ fuse_reply_attr(req, &stbuf, EROFSFUSE_TIMEOUT);
}
-static int erofsfuse_read(const char *path, char *buffer,
- size_t size, off_t offset,
- struct fuse_file_info *fi)
+static void erofsfuse_opendir(fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi)
{
int ret;
- struct erofs_inode vi;
+ struct erofs_inode *vi;
- erofs_dbg("path:%s size=%zd offset=%llu", path, size, (long long)offset);
+ vi = (struct erofs_inode *)malloc(sizeof(struct erofs_inode));
+ if (!vi) {
+ fuse_reply_err(req, ENOMEM);
+ return;
+ }
- vi.sbi = &sbi;
- ret = erofs_ilookup(path, &vi);
- if (ret)
- return ret;
+ vi->sbi = &sbi;
+ vi->nid = erofsfuse_to_nid(ino);
+ ret = erofs_read_inode_from_disk(vi);
+ if (ret < 0) {
+ fuse_reply_err(req, -ret);
+ goto out;
+ }
- ret = erofs_pread(&vi, buffer, size, offset);
- if (ret)
- return ret;
- if (offset >= vi.i_size)
- return 0;
- if (offset + size > vi.i_size)
- return vi.i_size - offset;
- return size;
+ 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);
}
-static int erofsfuse_readlink(const char *path, char *buffer, size_t size)
+static void erofsfuse_release(fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi)
{
- int ret = erofsfuse_read(path, buffer, size, 0, NULL);
-
- if (ret < 0)
- return ret;
- DBG_BUGON(ret > size);
- if (ret == size)
- buffer[size - 1] = '\0';
- erofs_dbg("readlink(%s): %s", path, buffer);
- return 0;
+ free((struct erofs_inode *)fi->fh);
+ fi->fh = 0;
+ fuse_reply_err(req, 0);
}
-static int erofsfuse_getxattr(const char *path, const char *name, char *value,
- size_t size
-#ifdef __APPLE__
- , uint32_t position)
+static void erofsfuse_lookup(fuse_req_t req, fuse_ino_t parent,
+ const char *name)
+{
+ int ret;
+ struct erofs_inode *vi;
+ struct fuse_entry_param fentry = { 0 };
+ struct erofsfuse_lookupdir_context ctx = { 0 };
+
+ vi = (struct erofs_inode *)malloc(sizeof(struct erofs_inode));
+ if (!vi) {
+ fuse_reply_err(req, ENOMEM);
+ return;
+ }
+
+ vi->sbi = &sbi;
+ vi->nid = erofsfuse_to_nid(parent);
+ ret = erofs_read_inode_from_disk(vi);
+ if (ret < 0) {
+ fuse_reply_err(req, -ret);
+ goto out;
+ }
+
+ memset(&fentry, 0, sizeof(fentry));
+ fentry.ino = 0;
+ fentry.attr_timeout = fentry.entry_timeout = EROFSFUSE_TIMEOUT;
+ ctx.ctx.dir = vi;
+ ctx.ctx.cb = erofsfuse_lookup_dentry;
+
+ ctx.ent = &fentry;
+ ctx.target_name = name;
+
+#ifdef NDEBUG
+ ret = erofs_iterate_dir(&ctx.ctx, false);
#else
- )
+ ret = erofs_iterate_dir(&ctx.ctx, true);
#endif
+
+ if (ret < 0) {
+ fuse_reply_err(req, -ret);
+ goto out;
+ }
+ fuse_reply_entry(req, &fentry);
+
+out:
+ free(vi);
+}
+
+static void erofsfuse_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;
+ char *buf = NULL;
+ struct erofs_inode *vi = (struct erofs_inode *)fi->fh;
- erofs_dbg("getxattr(%s): name=%s size=%llu", path, name, size);
+ erofs_dbg("read(%llu): size = %zu, off = %lu", ino | 0ULL, size, off);
- vi.sbi = &sbi;
- ret = erofs_ilookup(path, &vi);
- if (ret)
- return ret;
+ buf = malloc(size);
+ if (!buf) {
+ fuse_reply_err(req, ENOMEM);
+ return;
+ }
- return erofs_getxattr(&vi, name, value, size);
+ ret = erofs_pread(vi, buf, size, off);
+ if (ret) {
+ fuse_reply_err(req, -ret);
+ 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);
}
-static int erofsfuse_listxattr(const char *path, char *list, size_t size)
+static void erofsfuse_readlink(fuse_req_t req, fuse_ino_t ino)
{
int ret;
- struct erofs_inode vi;
+ char *buf = NULL;
+ struct erofs_inode vi = { .sbi = &sbi, .nid = erofsfuse_to_nid(ino) };
+
+ ret = erofs_read_inode_from_disk(&vi);
+ if (ret < 0) {
+ fuse_reply_err(req, -ret);
+ return;
+ }
- erofs_dbg("listxattr(%s): size=%llu", path, size);
+ buf = malloc(vi.i_size + 1);
+ if (!buf) {
+ fuse_reply_err(req, ENOMEM);
+ return;
+ }
- vi.sbi = &sbi;
- ret = erofs_ilookup(path, &vi);
- if (ret)
- return ret;
+ ret = erofs_pread(&vi, buf, vi.i_size, 0);
+ if (ret < 0) {
+ fuse_reply_err(req, -ret);
+ goto out;
+ }
+
+ buf[vi.i_size] = '\0';
+ fuse_reply_readlink(req, buf);
+
+out:
+ free(buf);
+}
+
+static void erofsfuse_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
+ size_t size)
+{
+ int ret;
+ char *buf = NULL;
+ struct erofs_inode vi = { .sbi = &sbi, .nid = erofsfuse_to_nid(ino) };
+
+ erofs_dbg("getattr(%llu): name = %s, size = %zu", ino | 0ULL, name, size);
+
+ ret = erofs_read_inode_from_disk(&vi);
+ if (ret < 0) {
+ fuse_reply_err(req, -ret);
+ return;
+ }
+
+ if (size != 0) {
+ buf = malloc(size);
+ if (!buf) {
+ fuse_reply_err(req, ENOMEM);
+ return;
+ }
+ }
+
+ ret = erofs_getxattr(&vi, name, buf, size);
+ 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);
+}
+
+static void erofsfuse_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
+{
+ int ret;
+ char *buf = NULL;
+ struct erofs_inode vi = { .sbi = &sbi, .nid = erofsfuse_to_nid(ino) };
+
+ erofs_dbg("listxattr(%llu): size = %zu", ino | 0ULL, size);
- return erofs_listxattr(&vi, list, size);
+ ret = erofs_read_inode_from_disk(&vi);
+ if (ret < 0) {
+ fuse_reply_err(req, -ret);
+ return;
+ }
+
+ if (size != 0) {
+ buf = malloc(size);
+ if (!buf) {
+ fuse_reply_err(req, ENOMEM);
+ return;
+ }
+ }
+
+ ret = erofs_listxattr(&vi, buf, size);
+ 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);
}
-static struct fuse_operations erofs_ops = {
+static struct fuse_lowlevel_ops erofsfuse_lops = {
.getxattr = erofsfuse_getxattr,
+ .opendir = erofsfuse_opendir,
+ .releasedir = erofsfuse_release,
+ .release = erofsfuse_release,
+ .lookup = erofsfuse_lookup,
.listxattr = erofsfuse_listxattr,
.readlink = erofsfuse_readlink,
.getattr = erofsfuse_getattr,
.readdir = erofsfuse_readdir,
+#if FUSE_MAJOR_VERSION >= 3
+ .readdirplus = erofsfuse_readdirplus,
+#endif
.open = erofsfuse_open,
.read = erofsfuse_read,
.init = erofsfuse_init,
@@ -197,6 +518,7 @@ static struct options {
u64 offset;
unsigned int debug_lvl;
bool show_help;
+ bool show_version;
bool odebug;
} fusecfg;
@@ -205,13 +527,17 @@ static const struct fuse_opt option_spec[] = {
OPTION("--offset=%lu", offset),
OPTION("--dbglevel=%u", debug_lvl),
OPTION("--help", show_help),
+ OPTION("--version", show_version),
FUSE_OPT_KEY("--device=", 1),
FUSE_OPT_END
};
static void usage(void)
{
- struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
+#if FUSE_MAJOR_VERSION >= 3
+ fuse_lowlevel_version();
+#endif
+ fprintf(stderr, "erofsfuse version: %s\n\n", cfg.c_version);
fputs("usage: [options] IMAGE MOUNTPOINT\n\n"
"Options:\n"
@@ -220,12 +546,15 @@ static void usage(void)
" --device=# specify an extra device to be used together\n"
#if FUSE_MAJOR_VERSION < 3
" --help display this help and exit\n"
+ " --version display erofsfuse version\n"
#endif
"\n", stderr);
#if FUSE_MAJOR_VERSION >= 3
+ fputs("\nFUSE options:\n", stderr);
fuse_cmdline_help();
#else
+ struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
fuse_opt_add_arg(&args, ""); /* progname */
fuse_opt_add_arg(&args, "-ho"); /* progname */
fuse_parse_cmdline(&args, NULL, NULL, NULL);
@@ -266,12 +595,12 @@ static int optional_opt_func(void *data, const char *arg, int key,
case FUSE_OPT_KEY_OPT:
if (!strcmp(arg, "-d"))
fusecfg.odebug = true;
- break;
- default:
- DBG_BUGON(1);
- break;
+ if (!strcmp(arg, "-h"))
+ fusecfg.show_help = true;
+ if (!strcmp(arg, "-V"))
+ fusecfg.show_version = true;
}
- return 1;
+ return 1; // keep arg
}
#if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE)
@@ -301,10 +630,20 @@ static void signal_handle_sigsegv(int signal)
int main(int argc, char *argv[])
{
int ret;
+ struct fuse_session *se;
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
+#if FUSE_MAJOR_VERSION >= 3
+ struct fuse_cmdline_opts opts;
+#else
+ struct fuse_chan *ch;
+ struct {
+ char *mountpoint;
+ int mt, foreground;
+ } opts;
+#endif
erofs_init_configure();
- printf("%s %s\n", basename(argv[0]), cfg.c_version);
+ erofs_dbg("%s %s", basename(argv[0]), cfg.c_version);
#if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE)
if (signal(SIGSEGV, signal_handle_sigsegv) == SIG_ERR) {
@@ -319,7 +658,16 @@ int main(int argc, char *argv[])
if (ret)
goto err;
- if (fusecfg.show_help || !fusecfg.mountpoint)
+#if FUSE_MAJOR_VERSION >= 3
+ ret = fuse_parse_cmdline(&args, &opts);
+#else
+ ret = (fuse_parse_cmdline(&args, &opts.mountpoint, &opts.mt,
+ &opts.foreground) == -1);
+#endif
+ if (ret)
+ goto err_fuse_free_args;
+
+ if (fusecfg.show_help || fusecfg.show_version || !fusecfg.mountpoint)
usage();
cfg.c_dbg_lvl = fusecfg.debug_lvl;
@@ -341,13 +689,50 @@ int main(int argc, char *argv[])
goto err_dev_close;
}
- ret = fuse_main(args.argc, args.argv, &erofs_ops, NULL);
+#if FUSE_MAJOR_VERSION >= 3
+ se = fuse_session_new(&args, &erofsfuse_lops, sizeof(erofsfuse_lops),
+ NULL);
+ if (!se)
+ goto err_super_put;
+
+ if (fuse_session_mount(se, fusecfg.mountpoint) != -1) {
+ if (fuse_daemonize(opts.foreground) != -1) {
+ if (fuse_set_signal_handlers(se) != -1) {
+ ret = fuse_session_loop(se);
+ fuse_remove_signal_handlers(se);
+ }
+ fuse_session_unmount(se);
+ fuse_session_destroy(se);
+ }
+ }
+#else
+ ch = fuse_mount(fusecfg.mountpoint, &args);
+ if (!ch)
+ goto err_super_put;
+
+ se = fuse_lowlevel_new(&args, &erofsfuse_lops, sizeof(erofsfuse_lops),
+ NULL);
+ if (se) {
+ if (fuse_daemonize(opts.foreground) != -1) {
+ 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
+err_super_put:
erofs_put_super(&sbi);
err_dev_close:
blob_closeall(&sbi);
dev_close(&sbi);
err_fuse_free_args:
+ free(opts.mountpoint);
fuse_opt_free_args(&args);
err:
erofs_exit_configure();
diff --git a/include/erofs/inode.h b/include/erofs/inode.h
index fe9dda2..fc06acd 100644
--- a/include/erofs/inode.h
+++ b/include/erofs/inode.h
@@ -23,6 +23,7 @@ static inline struct erofs_inode *erofs_igrab(struct erofs_inode *inode)
u32 erofs_new_encode_dev(dev_t dev);
unsigned char erofs_mode_to_ftype(umode_t mode);
+umode_t erofs_ftype_to_mode(unsigned int ftype, unsigned int perm);
unsigned char erofs_ftype_to_dtype(unsigned int filetype);
void erofs_inode_manager_init(void);
void erofs_insert_ihash(struct erofs_inode *inode, dev_t dev, ino_t ino);
diff --git a/lib/inode.c b/lib/inode.c
index 4dc8260..8fceee9 100644
--- a/lib/inode.c
+++ b/lib/inode.c
@@ -56,6 +56,25 @@ static const unsigned char erofs_dtype_by_ftype[EROFS_FT_MAX] = {
[EROFS_FT_SYMLINK] = DT_LNK
};
+static const umode_t erofs_dtype_by_umode[EROFS_FT_MAX] = {
+ [EROFS_FT_UNKNOWN] = S_IFMT,
+ [EROFS_FT_REG_FILE] = S_IFREG,
+ [EROFS_FT_DIR] = S_IFDIR,
+ [EROFS_FT_CHRDEV] = S_IFCHR,
+ [EROFS_FT_BLKDEV] = S_IFBLK,
+ [EROFS_FT_FIFO] = S_IFIFO,
+ [EROFS_FT_SOCK] = S_IFSOCK,
+ [EROFS_FT_SYMLINK] = S_IFLNK
+};
+
+umode_t erofs_ftype_to_mode(unsigned int ftype, unsigned int perm)
+{
+ if (ftype >= EROFS_FT_MAX)
+ ftype = EROFS_FT_UNKNOWN;
+
+ return erofs_dtype_by_umode[ftype] | perm;
+}
+
unsigned char erofs_ftype_to_dtype(unsigned int filetype)
{
if (filetype >= EROFS_FT_MAX)
--
2.34.1
More information about the Linux-erofs
mailing list