aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp11
-rw-r--r--include/fuse_common.h13
-rw-r--r--include/fuse_kernel.h283
-rw-r--r--include/fuse_lowlevel.h46
-rw-r--r--lib/fuse_lowlevel.c104
5 files changed, 406 insertions, 51 deletions
diff --git a/Android.bp b/Android.bp
index c862300..5373206 100644
--- a/Android.bp
+++ b/Android.bp
@@ -41,7 +41,10 @@ license {
cc_defaults {
name: "libfuse_default_flags",
- local_include_dirs: ["include/", "lib/"],
+ local_include_dirs: [
+ "include/",
+ "lib/",
+ ],
cflags: [
"-D_FILE_OFFSET_BITS=64",
"-DFUSERMOUNT_DIR=\"/system/bin\"",
@@ -56,7 +59,6 @@ cc_defaults {
"-Wno-unused-variable",
],
- clang: true,
sdk_version: "current",
min_sdk_version: "30",
@@ -70,7 +72,10 @@ cc_library {
"libfuse_default_flags",
],
- export_include_dirs: ["include", "lib"],
+ export_include_dirs: [
+ "include",
+ "lib",
+ ],
version_script: "lib/fuse_versionscript",
diff --git a/include/fuse_common.h b/include/fuse_common.h
index 3f836d7..efaf754 100644
--- a/include/fuse_common.h
+++ b/include/fuse_common.h
@@ -400,6 +400,13 @@ struct fuse_loop_config {
#define FUSE_CAP_EXPLICIT_INVAL_DATA (1 << 25)
/**
+ * Indicates that an extended 'struct fuse_setxattr' is used by the kernel
+ * side - extra_flags are passed, which are used (as of now by acl) processing.
+ * For example FUSE_SETXATTR_ACL_KILL_SGID might be set.
+ */
+#define FUSE_CAP_SETXATTR_EXT (1 << 27)
+
+/**
* Indicates support for passthrough mode access for read/write operations.
*
* If this flag is set in the `capable` field of the `fuse_conn_info`
@@ -409,7 +416,7 @@ struct fuse_loop_config {
*
* This feature is disabled by default.
*/
-#define FUSE_CAP_PASSTHROUGH (1 << 31)
+#define FUSE_CAP_PASSTHROUGH (1LL << 63)
/**
* Ioctl flags
@@ -473,7 +480,7 @@ struct fuse_conn_info {
/**
* Capability flags that the kernel supports (read-only)
*/
- unsigned capable;
+ uint64_t capable;
/**
* Capability flags that the filesystem wants to enable.
@@ -481,7 +488,7 @@ struct fuse_conn_info {
* libfuse attempts to initialize this field with
* reasonable default values before calling the init() handler.
*/
- unsigned want;
+ uint64_t want;
/**
* Maximum number of pending "background" requests. A
diff --git a/include/fuse_kernel.h b/include/fuse_kernel.h
index 70278fa..7928a2f 100644
--- a/include/fuse_kernel.h
+++ b/include/fuse_kernel.h
@@ -38,6 +38,43 @@
*
* Protocol changelog:
*
+ * 7.1:
+ * - add the following messages:
+ * FUSE_SETATTR, FUSE_SYMLINK, FUSE_MKNOD, FUSE_MKDIR, FUSE_UNLINK,
+ * FUSE_RMDIR, FUSE_RENAME, FUSE_LINK, FUSE_OPEN, FUSE_READ, FUSE_WRITE,
+ * FUSE_RELEASE, FUSE_FSYNC, FUSE_FLUSH, FUSE_SETXATTR, FUSE_GETXATTR,
+ * FUSE_LISTXATTR, FUSE_REMOVEXATTR, FUSE_OPENDIR, FUSE_READDIR,
+ * FUSE_RELEASEDIR
+ * - add padding to messages to accommodate 32-bit servers on 64-bit kernels
+ *
+ * 7.2:
+ * - add FOPEN_DIRECT_IO and FOPEN_KEEP_CACHE flags
+ * - add FUSE_FSYNCDIR message
+ *
+ * 7.3:
+ * - add FUSE_ACCESS message
+ * - add FUSE_CREATE message
+ * - add filehandle to fuse_setattr_in
+ *
+ * 7.4:
+ * - add frsize to fuse_kstatfs
+ * - clean up request size limit checking
+ *
+ * 7.5:
+ * - add flags and max_write to fuse_init_out
+ *
+ * 7.6:
+ * - add max_readahead to fuse_init_in and fuse_init_out
+ *
+ * 7.7:
+ * - add FUSE_INTERRUPT message
+ * - add POSIX file lock support
+ *
+ * 7.8:
+ * - add lock_owner and flags fields to fuse_release_in
+ * - add FUSE_BMAP message
+ * - add FUSE_DESTROY message
+ *
* 7.9:
* - new fuse_getattr_in input argument of GETATTR
* - add lk_flags in fuse_lk_in
@@ -133,6 +170,30 @@
*
* 7.31
* - add FUSE_WRITE_KILL_PRIV flag
+ * - add FUSE_SETUPMAPPING and FUSE_REMOVEMAPPING
+ * - add map_alignment to fuse_init_out, add FUSE_MAP_ALIGNMENT flag
+ *
+ * 7.32
+ * - add flags to fuse_attr, add FUSE_ATTR_SUBMOUNT, add FUSE_SUBMOUNTS
+ *
+ * 7.33
+ * - add FUSE_HANDLE_KILLPRIV_V2, FUSE_WRITE_KILL_SUIDGID, FATTR_KILL_SUIDGID
+ * - add FUSE_OPEN_KILL_SUIDGID
+ * - extend fuse_setxattr_in, add FUSE_SETXATTR_EXT
+ * - add FUSE_SETXATTR_ACL_KILL_SGID
+ *
+ * 7.34
+ * - add FUSE_SYNCFS
+ *
+ * 7.35
+ * - add FOPEN_NOFLUSH
+ *
+ * 7.36
+ * - extend fuse_init_in with reserved fields, add FUSE_INIT_EXT init flag
+ * - add flags2 to fuse_init_in and fuse_init_out
+ * - add FUSE_SECURITY_CTX init flag
+ * - add security context to create, mkdir, symlink, and mknod requests
+ * - add FUSE_HAS_INODE_DAX, FUSE_ATTR_DAX
*/
#ifndef _LINUX_FUSE_H
@@ -168,7 +229,7 @@
#define FUSE_KERNEL_VERSION 7
/** Minor version number of this interface */
-#define FUSE_KERNEL_MINOR_VERSION 31
+#define FUSE_KERNEL_MINOR_VERSION 36
/** The node ID of the root inode */
#define FUSE_ROOT_ID 1
@@ -192,7 +253,7 @@ struct fuse_attr {
uint32_t gid;
uint32_t rdev;
uint32_t blksize;
- uint32_t padding;
+ uint32_t flags;
};
struct fuse_kstatfs {
@@ -229,6 +290,7 @@ struct fuse_file_lock {
#define FATTR_MTIME_NOW (1 << 8)
#define FATTR_LOCKOWNER (1 << 9)
#define FATTR_CTIME (1 << 10)
+#define FATTR_KILL_SUIDGID (1 << 11)
/**
* Flags returned by the OPEN request
@@ -238,12 +300,14 @@ struct fuse_file_lock {
* FOPEN_NONSEEKABLE: the file is not seekable
* FOPEN_CACHE_DIR: allow caching this directory
* FOPEN_STREAM: the file is stream-like (no file position at all)
+ * FOPEN_NOFLUSH: don't flush data cache on close (unless FUSE_WRITEBACK_CACHE)
*/
#define FOPEN_DIRECT_IO (1 << 0)
#define FOPEN_KEEP_CACHE (1 << 1)
#define FOPEN_NONSEEKABLE (1 << 2)
#define FOPEN_CACHE_DIR (1 << 3)
#define FOPEN_STREAM (1 << 4)
+#define FOPEN_NOFLUSH (1 << 5)
/**
* INIT request/reply flags
@@ -274,6 +338,21 @@ struct fuse_file_lock {
* FUSE_CACHE_SYMLINKS: cache READLINK responses
* FUSE_NO_OPENDIR_SUPPORT: kernel supports zero-message opendir
* FUSE_EXPLICIT_INVAL_DATA: only invalidate cached pages on explicit request
+ * FUSE_MAP_ALIGNMENT: init_out.map_alignment contains log2(byte alignment) for
+ * foffset and moffset fields in struct
+ * fuse_setupmapping_out and fuse_removemapping_one.
+ * FUSE_SUBMOUNTS: kernel supports auto-mounting directory submounts
+ * FUSE_HANDLE_KILLPRIV_V2: fs kills suid/sgid/cap on write/chown/trunc.
+ * Upon write/truncate suid/sgid is only killed if caller
+ * does not have CAP_FSETID. Additionally upon
+ * write/truncate sgid is killed only if file has group
+ * execute permission. (Same as Linux VFS behavior).
+ * FUSE_SETXATTR_EXT: Server supports extended struct fuse_setxattr_in
+ * FUSE_INIT_EXT: extended fuse_init_in request
+ * FUSE_INIT_RESERVED: reserved, do not use
+ * FUSE_SECURITY_CTX: add security context to create, mkdir, symlink, and
+ * mknod
+ * FUSE_HAS_INODE_DAX: use per inode DAX
*/
#define FUSE_ASYNC_READ (1 << 0)
#define FUSE_POSIX_LOCKS (1 << 1)
@@ -301,7 +380,27 @@ struct fuse_file_lock {
#define FUSE_CACHE_SYMLINKS (1 << 23)
#define FUSE_NO_OPENDIR_SUPPORT (1 << 24)
#define FUSE_EXPLICIT_INVAL_DATA (1 << 25)
-#define FUSE_PASSTHROUGH (1 << 31)
+#define FUSE_MAP_ALIGNMENT (1 << 26)
+#define FUSE_SUBMOUNTS (1 << 27)
+#define FUSE_HANDLE_KILLPRIV_V2 (1 << 28)
+#define FUSE_SETXATTR_EXT (1 << 29)
+#define FUSE_INIT_EXT (1 << 30)
+#define FUSE_INIT_RESERVED (1 << 31)
+/* bits 32..63 get shifted down 32 bits into the flags2 field */
+#define FUSE_SECURITY_CTX (1ULL << 32)
+#define FUSE_HAS_INODE_DAX (1ULL << 33)
+
+/*
+ * For FUSE < 7.36 FUSE_PASSTHROUGH has value (1 << 31).
+ * This condition check is not really required, but would prevent having a
+ * broken commit in the tree.
+ */
+#if FUSE_KERNEL_VERSION > 7 || \
+ (FUSE_KERNEL_VERSION == 7 && FUSE_KERNEL_MINOR_VERSION >= 36)
+#define FUSE_PASSTHROUGH (1ULL << 63)
+#else
+#define FUSE_PASSTHROUGH (1 << 31)
+#endif
/**
* CUSE INIT request/reply flags
@@ -331,11 +430,14 @@ struct fuse_file_lock {
*
* FUSE_WRITE_CACHE: delayed write from page cache, file handle is guessed
* FUSE_WRITE_LOCKOWNER: lock_owner field is valid
- * FUSE_WRITE_KILL_PRIV: kill suid and sgid bits
+ * FUSE_WRITE_KILL_SUIDGID: kill suid and sgid bits
*/
#define FUSE_WRITE_CACHE (1 << 0)
#define FUSE_WRITE_LOCKOWNER (1 << 1)
-#define FUSE_WRITE_KILL_PRIV (1 << 2)
+#define FUSE_WRITE_KILL_SUIDGID (1 << 2)
+
+/* Obsolete alias; this flag implies killing suid/sgid only. */
+#define FUSE_WRITE_KILL_PRIV FUSE_WRITE_KILL_SUIDGID
/**
* Read flags
@@ -377,6 +479,27 @@ struct fuse_file_lock {
*/
#define FUSE_FSYNC_FDATASYNC (1 << 0)
+/**
+ * fuse_attr flags
+ *
+ * FUSE_ATTR_SUBMOUNT: Object is a submount root
+ * FUSE_ATTR_DAX: Enable DAX for this file in per inode DAX mode
+ */
+#define FUSE_ATTR_SUBMOUNT (1 << 0)
+#define FUSE_ATTR_DAX (1 << 1)
+
+/**
+ * Open flags
+ * FUSE_OPEN_KILL_SUIDGID: Kill suid and sgid if executable
+ */
+#define FUSE_OPEN_KILL_SUIDGID (1 << 0)
+
+/**
+ * setxattr flags
+ * FUSE_SETXATTR_ACL_KILL_SGID: Clear SGID when system.posix_acl_access is set
+ */
+#define FUSE_SETXATTR_ACL_KILL_SGID (1 << 0)
+
enum fuse_opcode {
FUSE_LOOKUP = 1,
FUSE_FORGET = 2, /* no reply */
@@ -423,12 +546,19 @@ enum fuse_opcode {
FUSE_RENAME2 = 45,
FUSE_LSEEK = 46,
FUSE_COPY_FILE_RANGE = 47,
+ FUSE_SETUPMAPPING = 48,
+ FUSE_REMOVEMAPPING = 49,
+ FUSE_SYNCFS = 50,
/* Android specific operations */
FUSE_CANONICAL_PATH = 2016,
/* CUSE specific operations */
- CUSE_INIT = 4096
+ CUSE_INIT = 4096,
+
+ /* Reserved opcodes: helpful to detect structure endian-ness */
+ CUSE_INIT_BSWAP_RESERVED = 1048576, /* CUSE_INIT << 8 */
+ FUSE_INIT_BSWAP_RESERVED = 436207616, /* FUSE_INIT << 24 */
};
enum fuse_notify_code {
@@ -438,7 +568,7 @@ enum fuse_notify_code {
FUSE_NOTIFY_STORE = 4,
FUSE_NOTIFY_RETRIEVE = 5,
FUSE_NOTIFY_DELETE = 6,
- FUSE_NOTIFY_CODE_MAX
+ FUSE_NOTIFY_CODE_MAX,
};
/* The read buffer is required to be at least 8k, but may be much larger */
@@ -546,14 +676,14 @@ struct fuse_setattr_in {
struct fuse_open_in {
uint32_t flags;
- uint32_t unused;
+ uint32_t open_flags; /* FUSE_OPEN_... */
};
struct fuse_create_in {
uint32_t flags;
uint32_t mode;
uint32_t umask;
- uint32_t padding;
+ uint32_t open_flags; /* FUSE_OPEN_... */
};
struct fuse_open_out {
@@ -586,6 +716,12 @@ struct fuse_read_in {
uint32_t padding;
};
+struct fuse_read_out {
+ uint64_t offset;
+ uint32_t size;
+ uint32_t again;
+};
+
struct fuse_passthrough_out_v0 {
uint32_t fd;
/* For future implementation */
@@ -622,9 +758,13 @@ struct fuse_fsync_in {
uint32_t padding;
};
+#define FUSE_COMPAT_SETXATTR_IN_SIZE 8
+
struct fuse_setxattr_in {
uint32_t size;
uint32_t flags;
+ uint32_t setxattr_flags;
+ uint32_t padding;
};
struct fuse_getxattr_in {
@@ -659,6 +799,8 @@ struct fuse_init_in {
uint32_t minor;
uint32_t max_readahead;
uint32_t flags;
+ uint32_t flags2;
+ uint32_t unused[11];
};
#define FUSE_COMPAT_INIT_OUT_SIZE 8
@@ -674,8 +816,9 @@ struct fuse_init_out {
uint32_t max_write;
uint32_t time_gran;
uint16_t max_pages;
- uint16_t padding;
- uint32_t unused[8];
+ uint16_t map_alignment;
+ uint32_t flags2;
+ uint32_t unused[7];
};
#define CUSE_INIT_INFO_MAX 4096
@@ -766,7 +909,7 @@ struct fuse_in_header {
uint32_t uid;
uint32_t gid;
uint32_t pid;
- uint32_t padding;
+ uint32_t error_in;
};
struct fuse_out_header {
@@ -783,9 +926,12 @@ struct fuse_dirent {
char name[];
};
-#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name)
-#define FUSE_DIRENT_ALIGN(x) \
+/* Align variable length records to 64bit boundary */
+#define FUSE_REC_ALIGN(x) \
(((x) + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1))
+
+#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name)
+#define FUSE_DIRENT_ALIGN(x) FUSE_REC_ALIGN(x)
#define FUSE_DIRENT_SIZE(d) \
FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen)
@@ -844,7 +990,8 @@ struct fuse_notify_retrieve_in {
};
/* Device ioctls: */
-#define FUSE_DEV_IOC_CLONE _IOR(229, 0, uint32_t)
+#define FUSE_DEV_IOC_MAGIC 229
+#define FUSE_DEV_IOC_CLONE _IOR(FUSE_DEV_IOC_MAGIC, 0, uint32_t)
#define FUSE_DEV_IOC_PASSTHROUGH_OPEN_V0 _IOW(229, 1, struct fuse_passthrough_out_v0)
#define FUSE_DEV_IOC_PASSTHROUGH_OPEN_V1 _IOW(229, 127, struct fuse_passthrough_out_v0)
#define FUSE_DEV_IOC_PASSTHROUGH_OPEN_V2 _IOW(229, 126, uint32_t)
@@ -870,42 +1017,104 @@ struct fuse_copy_file_range_in {
uint64_t flags;
};
+#define FUSE_SETUPMAPPING_FLAG_WRITE (1ull << 0)
+#define FUSE_SETUPMAPPING_FLAG_READ (1ull << 1)
+struct fuse_setupmapping_in {
+ /* An already open handle */
+ uint64_t fh;
+ /* Offset into the file to start the mapping */
+ uint64_t foffset;
+ /* Length of mapping required */
+ uint64_t len;
+ /* Flags, FUSE_SETUPMAPPING_FLAG_* */
+ uint64_t flags;
+ /* Offset in Memory Window */
+ uint64_t moffset;
+};
+
+struct fuse_removemapping_in {
+ /* number of fuse_removemapping_one follows */
+ uint32_t count;
+};
+
+struct fuse_removemapping_one {
+ /* Offset into the dax window start the unmapping */
+ uint64_t moffset;
+ /* Length of mapping required */
+ uint64_t len;
+};
+
+#define FUSE_REMOVEMAPPING_MAX_ENTRY \
+ (PAGE_SIZE / sizeof(struct fuse_removemapping_one))
+
+struct fuse_syncfs_in {
+ uint64_t padding;
+};
+
+/*
+ * For each security context, send fuse_secctx with size of security context
+ * fuse_secctx will be followed by security context name and this in turn
+ * will be followed by actual context label.
+ * fuse_secctx, name, context
+ */
+struct fuse_secctx {
+ uint32_t size;
+ uint32_t padding;
+};
+
+/*
+ * Contains the information about how many fuse_secctx structures are being
+ * sent and what's the total size of all security contexts (including
+ * size of fuse_secctx_header).
+ *
+ */
+struct fuse_secctx_header {
+ uint32_t size;
+ uint32_t nr_secctx;
+};
+
/** Export fuse_args only for bpf */
#ifdef __KERNEL__
struct fuse_mount;
+/*
+ * Fuse BPF Args
+ *
+ * Used to communicate with bpf programs to allow checking or altering certain values.
+ * The end_offset allows the bpf verifier to check boundaries statically. This reflects
+ * the ends of the buffer. size shows the length that was actually used.
+ *
+ */
+
/** One input argument of a request */
-struct fuse_in_arg {
- unsigned size;
+struct fuse_bpf_in_arg {
+ uint32_t size;
const void *value;
+ const void *end_offset;
};
/** One output argument of a request */
-struct fuse_arg {
- unsigned size;
+struct fuse_bpf_arg {
+ uint32_t size;
void *value;
+ void *end_offset;
};
-struct fuse_args {
+#define FUSE_MAX_IN_ARGS 5
+#define FUSE_MAX_OUT_ARGS 3
+
+#define FUSE_BPF_FORCE (1 << 0)
+#define FUSE_BPF_OUT_ARGVAR (1 << 6)
+
+struct fuse_bpf_args {
uint64_t nodeid;
uint32_t opcode;
- unsigned short in_numargs;
- unsigned short out_numargs;
- int force:1;
- int noreply:1;
- int nocreds:1;
- int in_pages:1;
- int out_pages:1;
- int out_argvar:1;
- int page_zeroing:1;
- int page_replace:1;
- int may_block:1;
- struct fuse_in_arg in_args[5];
- struct fuse_arg out_args[3];
- void (*end)(struct fuse_mount *fm, struct fuse_args *args, int error);
-
- /* Path used for completing d_canonical_path */
- struct path *canonical_path;
+ uint32_t error_in;
+ uint32_t in_numargs;
+ uint32_t out_numargs;
+ uint32_t flags;
+ struct fuse_bpf_in_arg in_args[FUSE_MAX_IN_ARGS];
+ struct fuse_bpf_arg out_args[FUSE_MAX_OUT_ARGS];
};
#endif
diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h
index c591f71..92370ea 100644
--- a/include/fuse_lowlevel.h
+++ b/include/fuse_lowlevel.h
@@ -143,6 +143,12 @@ struct fuse_forget_data {
#define FUSE_SET_ATTR_CTIME (1 << 10)
/* ----------------------------------------------------------- *
+ * structs from fuse_kernel.h *
+ * ----------------------------------------------------------- */
+struct fuse_entry_out;
+struct fuse_entry_bpf_out;
+
+/* ----------------------------------------------------------- *
* Request methods and replies *
* ----------------------------------------------------------- */
@@ -219,6 +225,25 @@ struct fuse_lowlevel_ops {
void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
/**
+ * post filter a lookup
+ *
+ * Valid replies:
+ * fuse_reply_entry
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param parent inode number of the parent directory
+ * @param error_in the error, or 0, of the lookup
+ * @param name the name that was looked up
+ * @param feo the fuse entry out struct from the lookup
+ * @param febo the fuse entry bpf out struct from the lookup
+ */
+ void (*lookup_postfilter)(fuse_req_t req, fuse_ino_t parent,
+ uint32_t error_in, const char *name,
+ struct fuse_entry_out *feo,
+ struct fuse_entry_bpf_out *febo);
+
+ /**
* Forget about an inode
*
* This function is called when the kernel removes an inode
@@ -743,6 +768,27 @@ struct fuse_lowlevel_ops {
struct fuse_file_info *fi);
/**
+ * Read directory postfilter
+ *
+ * Valid replies:
+ * fuse_reply_buf
+ * fuse_reply_data
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param ino the inode number
+ * @param error_in the error from the readdir
+ * @param off_in offset to continue reading the directory stream before backing
+ * @param off_out offset to continue reading the directory stream after backing
+ * @param size_out length in bytes of dirents
+ * @param dirents array of dirents read by backing
+ * @param fi file information
+ */
+ void (*readdirpostfilter)(fuse_req_t req, fuse_ino_t ino, uint32_t error_in,
+ off_t off_in, off_t off_out, size_t size_out,
+ const void *dirents, struct fuse_file_info *fi);
+
+ /**
* Release an open directory
*
* For every opendir call there will be exactly one releasedir
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
index aee22b4..4c75e3b 100644
--- a/lib/fuse_lowlevel.c
+++ b/lib/fuse_lowlevel.c
@@ -421,6 +421,7 @@ int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param* e) {
if (!e->ino && req->se->conn.proto_minor < 4) return fuse_reply_err(req, ENOENT);
memset(&arg, 0, sizeof(arg));
+ fill_entry(&arg, e);
if (extended_args) {
memset(&bpf_arg, 0, sizeof(bpf_arg));
@@ -435,7 +436,6 @@ int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param* e) {
return send_reply_ok(req, &arg_ext, size);
} else {
- fill_entry(&arg, e);
return send_reply_ok(req, &arg, size);
}
}
@@ -1187,6 +1187,28 @@ static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
fuse_reply_err(req, ENOSYS);
}
+static void do_lookup_postfilter(fuse_req_t req, fuse_ino_t nodeid, uint32_t error_in,
+ const void *inarg, size_t size)
+{
+ if (req->se->op.lookup_postfilter) {
+ char *name = (char *) inarg;
+ size_t namelen = strlen(name);
+
+ if (size != namelen + 1 + sizeof(struct fuse_entry_out)
+ + sizeof(struct fuse_entry_bpf_out)) {
+ fuse_log(FUSE_LOG_ERR, "%s: Bad size", __func__);
+ fuse_reply_err(req, EIO);
+ } else {
+ struct fuse_entry_out *feo = (void *) (name + namelen + 1);
+ struct fuse_entry_bpf_out *febo = (char *) feo + sizeof(*feo);
+
+ req->se->op.lookup_postfilter(req, nodeid, error_in, name, feo,
+ febo);
+ }
+ } else
+ fuse_reply_err(req, ENOSYS);
+}
+
static void do_forget(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
{
struct fuse_forget_in *arg = (struct fuse_forget_in *) inarg;
@@ -1629,6 +1651,26 @@ static void do_readdirplus(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
fuse_reply_err(req, ENOSYS);
}
+static void do_readdir_postfilter(fuse_req_t req, fuse_ino_t nodeid,
+ uint32_t error_in, const void *inarg,
+ size_t size) {
+ struct fuse_read_in *fri = (struct fuse_read_in *) inarg;
+ struct fuse_read_out *fro = (struct fuse_read_out *) (fri + 1);
+ struct fuse_dirent *dirents = (struct fuse_dirent *) (fro + 1);
+ struct fuse_file_info fi;
+
+ memset(&fi, 0, sizeof(fi));
+ fi.fh = fri->fh;
+
+ if (req->se->op.readdirpostfilter)
+ req->se->op.readdirpostfilter(req, nodeid, error_in, fri->offset,
+ fro->offset,
+ size - sizeof(*fri) - sizeof(*fro),
+ dirents, &fi);
+ else
+ fuse_reply_err(req, ENOSYS);
+}
+
static void do_releasedir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
{
struct fuse_release_in *arg = (struct fuse_release_in *) inarg;
@@ -1677,10 +1719,14 @@ static void do_statfs(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
static void do_setxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
{
+ struct fuse_session *se = req->se;
+ unsigned int xattr_ext = !!(se->conn.want & FUSE_CAP_SETXATTR_EXT);
struct fuse_setxattr_in *arg = (struct fuse_setxattr_in *) inarg;
- char *name = PARAM(arg);
+ char *name = xattr_ext ? PARAM(arg) :
+ (char *)arg + FUSE_COMPAT_SETXATTR_IN_SIZE;
char *value = name + strlen(name) + 1;
+ /* XXX:The API should be extended to support extra_flags/setxattr_flags */
if (req->se->op.setxattr)
req->se->op.setxattr(req, nodeid, name, value, arg->size,
arg->flags);
@@ -2013,6 +2059,7 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
struct fuse_session *se = req->se;
size_t bufsize = se->bufsize;
size_t outargsize = sizeof(outarg);
+ int extended_flags;
(void) nodeid;
if (se->debug) {
@@ -2032,6 +2079,10 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
outarg.major = FUSE_KERNEL_VERSION;
outarg.minor = FUSE_KERNEL_MINOR_VERSION;
+ extended_flags = arg->major > 7 || (arg->major == 7 && arg->minor >= 36);
+ fuse_log(FUSE_LOG_DEBUG, "fuse: protocol version: %u.%u, extended flags: %d\n",
+ arg->major, arg->minor, extended_flags);
+
if (arg->major < 7) {
fuse_log(FUSE_LOG_ERR, "fuse: unsupported protocol version: %u.%u\n",
arg->major, arg->minor);
@@ -2084,6 +2135,8 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
se->conn.capable |= FUSE_CAP_NO_OPENDIR_SUPPORT;
if (arg->flags & FUSE_EXPLICIT_INVAL_DATA)
se->conn.capable |= FUSE_CAP_EXPLICIT_INVAL_DATA;
+ if (arg->flags & FUSE_SETXATTR_EXT)
+ se->conn.capable |= FUSE_CAP_SETXATTR_EXT;
if (!(arg->flags & FUSE_MAX_PAGES)) {
size_t max_bufsize =
FUSE_DEFAULT_MAX_PAGES_PER_REQ * getpagesize()
@@ -2092,8 +2145,13 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
bufsize = max_bufsize;
}
}
- if (arg->flags & FUSE_PASSTHROUGH)
- se->conn.capable |= FUSE_PASSTHROUGH;
+ if (extended_flags) {
+ if (arg->flags2 & (1 << 31))
+ se->conn.capable |= FUSE_CAP_PASSTHROUGH;
+ } else {
+ if (arg->flags & (1 << 31))
+ se->conn.capable |= FUSE_CAP_PASSTHROUGH;
+ }
} else {
se->conn.max_readahead = 0;
}
@@ -2206,12 +2264,18 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
outarg.flags |= FUSE_WRITEBACK_CACHE;
if (se->conn.want & FUSE_CAP_POSIX_ACL)
outarg.flags |= FUSE_POSIX_ACL;
- if (se->conn.want & FUSE_CAP_PASSTHROUGH)
- outarg.flags |= FUSE_PASSTHROUGH;
+ if (se->conn.want & FUSE_CAP_PASSTHROUGH) {
+ if (extended_flags)
+ outarg.flags2 |= (1 << 31);
+ else
+ outarg.flags |= (1 << 31);
+ }
if (se->conn.want & FUSE_CAP_CACHE_SYMLINKS)
outarg.flags |= FUSE_CACHE_SYMLINKS;
if (se->conn.want & FUSE_CAP_EXPLICIT_INVAL_DATA)
outarg.flags |= FUSE_EXPLICIT_INVAL_DATA;
+ if (se->conn.want & FUSE_CAP_SETXATTR_EXT)
+ outarg.flags |= FUSE_SETXATTR_EXT;
outarg.max_readahead = se->conn.max_readahead;
outarg.max_write = se->conn.max_write;
if (se->conn.proto_minor >= 13) {
@@ -2583,7 +2647,7 @@ static struct {
[FUSE_GETATTR] = { do_getattr, "GETATTR" },
[FUSE_SETATTR] = { do_setattr, "SETATTR" },
[FUSE_READLINK] = { do_readlink, "READLINK" },
- [FUSE_CANONICAL_PATH] = { do_canonical_path, "CANONICAL_PATH" },
+ [FUSE_CANONICAL_PATH] = { do_canonical_path, "CANONICAL_PATH" },
[FUSE_SYMLINK] = { do_symlink, "SYMLINK" },
[FUSE_MKNOD] = { do_mknod, "MKNOD" },
[FUSE_MKDIR] = { do_mkdir, "MKDIR" },
@@ -2627,6 +2691,18 @@ static struct {
[CUSE_INIT] = { cuse_lowlevel_init, "CUSE_INIT" },
};
+static struct {
+ void (*func)( fuse_req_t, fuse_ino_t, const void *);
+ const char *name;
+} fuse_ll_prefilter_ops[] = {};
+
+static struct {
+ void (*func)( fuse_req_t, fuse_ino_t, uint32_t, const void *, size_t size);
+} fuse_ll_postfilter_ops[] = {
+ [FUSE_LOOKUP] = {do_lookup_postfilter},
+ [FUSE_READDIR] = {do_readdir_postfilter},
+};
+
#define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0]))
static const char *opname(enum fuse_opcode opcode)
@@ -2804,8 +2880,20 @@ void fuse_session_process_buf_int(struct fuse_session *se,
do_write_buf(req, in->nodeid, inarg, buf);
else if (in->opcode == FUSE_NOTIFY_REPLY)
do_notify_reply(req, in->nodeid, inarg, buf);
- else
+ else if (!opcode_filter)
fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg);
+ else if (opcode_filter == FUSE_PREFILTER && fuse_ll_prefilter_ops[in->opcode].func)
+ fuse_ll_prefilter_ops[in->opcode].func(req, in->nodeid, inarg);
+ else if (opcode_filter == FUSE_POSTFILTER
+ && fuse_ll_postfilter_ops[in->opcode].func)
+ fuse_ll_postfilter_ops[in->opcode].func(
+ req, in->nodeid, in->error_in, inarg,
+ buf->size - sizeof(struct fuse_in_header));
+ else {
+ fuse_log(FUSE_LOG_ERR, "Bad opcode");
+ err = ENOSYS;
+ goto reply_err;
+ }
out_free:
free(mbuf);