aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--example/passthrough_ll.c22
-rw-r--r--example/printcap.c113
-rw-r--r--include/fuse.h85
-rw-r--r--include/fuse_common.h761
-rw-r--r--lib/cuse_lowlevel.c6
-rw-r--r--lib/fuse.c3
-rw-r--r--lib/fuse_lowlevel.c179
-rw-r--r--lib/meson.build2
-rw-r--r--test/test_write_cache.c2
9 files changed, 645 insertions, 528 deletions
diff --git a/example/passthrough_ll.c b/example/passthrough_ll.c
index 309d8dd..5f1fde9 100644
--- a/example/passthrough_ll.c
+++ b/example/passthrough_ll.c
@@ -168,18 +168,20 @@ static bool lo_debug(fuse_req_t req)
static void lo_init(void *userdata,
struct fuse_conn_info *conn)
{
- struct lo_data *lo = (struct lo_data*) userdata;
-
- if (lo->writeback &&
- conn->capable & FUSE_CAP_WRITEBACK_CACHE) {
- if (lo->debug)
- fuse_log(FUSE_LOG_DEBUG, "lo_init: activating writeback\n");
- conn->want |= FUSE_CAP_WRITEBACK_CACHE;
+ struct lo_data *lo = (struct lo_data *)userdata;
+ bool has_flag;
+
+ if (lo->writeback) {
+ has_flag = fuse_set_feature_flag(conn, FUSE_CAP_WRITEBACK_CACHE);
+ if (lo->debug && has_flag)
+ fuse_log(FUSE_LOG_DEBUG,
+ "lo_init: activating writeback\n");
}
if (lo->flock && conn->capable & FUSE_CAP_FLOCK_LOCKS) {
- if (lo->debug)
- fuse_log(FUSE_LOG_DEBUG, "lo_init: activating flock locks\n");
- conn->want |= FUSE_CAP_FLOCK_LOCKS;
+ has_flag = fuse_set_feature_flag(conn, FUSE_CAP_FLOCK_LOCKS);
+ if (lo->debug && has_flag)
+ fuse_log(FUSE_LOG_DEBUG,
+ "lo_init: activating flock locks\n");
}
/* Disable the receiving and processing of FUSE_INTERRUPT requests */
diff --git a/example/printcap.c b/example/printcap.c
index 01b4d3f..82a7598 100644
--- a/example/printcap.c
+++ b/example/printcap.c
@@ -29,68 +29,63 @@
struct fuse_session *se;
-static void pc_init(void *userdata,
- struct fuse_conn_info *conn)
+// Define a structure to hold capability information
+struct cap_info {
+ uint64_t flag;
+ const char *name;
+};
+
+// Define an array of all capabilities
+static const struct cap_info capabilities[] = {
+ {FUSE_CAP_ASYNC_READ, "FUSE_CAP_ASYNC_READ"},
+ {FUSE_CAP_POSIX_LOCKS, "FUSE_CAP_POSIX_LOCKS"},
+ {FUSE_CAP_ATOMIC_O_TRUNC, "FUSE_CAP_ATOMIC_O_TRUNC"},
+ {FUSE_CAP_EXPORT_SUPPORT, "FUSE_CAP_EXPORT_SUPPORT"},
+ {FUSE_CAP_DONT_MASK, "FUSE_CAP_DONT_MASK"},
+ {FUSE_CAP_SPLICE_MOVE, "FUSE_CAP_SPLICE_MOVE"},
+ {FUSE_CAP_SPLICE_READ, "FUSE_CAP_SPLICE_READ"},
+ {FUSE_CAP_SPLICE_WRITE, "FUSE_CAP_SPLICE_WRITE"},
+ {FUSE_CAP_FLOCK_LOCKS, "FUSE_CAP_FLOCK_LOCKS"},
+ {FUSE_CAP_IOCTL_DIR, "FUSE_CAP_IOCTL_DIR"},
+ {FUSE_CAP_AUTO_INVAL_DATA, "FUSE_CAP_AUTO_INVAL_DATA"},
+ {FUSE_CAP_READDIRPLUS, "FUSE_CAP_READDIRPLUS"},
+ {FUSE_CAP_READDIRPLUS_AUTO, "FUSE_CAP_READDIRPLUS_AUTO"},
+ {FUSE_CAP_ASYNC_DIO, "FUSE_CAP_ASYNC_DIO"},
+ {FUSE_CAP_WRITEBACK_CACHE, "FUSE_CAP_WRITEBACK_CACHE"},
+ {FUSE_CAP_NO_OPEN_SUPPORT, "FUSE_CAP_NO_OPEN_SUPPORT"},
+ {FUSE_CAP_PARALLEL_DIROPS, "FUSE_CAP_PARALLEL_DIROPS"},
+ {FUSE_CAP_POSIX_ACL, "FUSE_CAP_POSIX_ACL"},
+ {FUSE_CAP_CACHE_SYMLINKS, "FUSE_CAP_CACHE_SYMLINKS"},
+ {FUSE_CAP_NO_OPENDIR_SUPPORT, "FUSE_CAP_NO_OPENDIR_SUPPORT"},
+ {FUSE_CAP_EXPLICIT_INVAL_DATA, "FUSE_CAP_EXPLICIT_INVAL_DATA"},
+ {FUSE_CAP_EXPIRE_ONLY, "FUSE_CAP_EXPIRE_ONLY"},
+ {FUSE_CAP_SETXATTR_EXT, "FUSE_CAP_SETXATTR_EXT"},
+ {FUSE_CAP_HANDLE_KILLPRIV, "FUSE_CAP_HANDLE_KILLPRIV"},
+ {FUSE_CAP_HANDLE_KILLPRIV_V2, "FUSE_CAP_HANDLE_KILLPRIV_V2"},
+ {FUSE_CAP_DIRECT_IO_ALLOW_MMAP, "FUSE_CAP_DIRECT_IO_ALLOW_MMAP"},
+ {FUSE_CAP_NO_EXPORT_SUPPORT, "FUSE_CAP_NO_EXPORT_SUPPORT"},
+ {FUSE_CAP_PASSTHROUGH, "FUSE_CAP_PASSTHROUGH"},
+ // Add any new capabilities here
+ {0, NULL} // Sentinel to mark the end of the array
+};
+
+static void print_capabilities(struct fuse_conn_info *conn)
+{
+ printf("Capabilities:\n");
+ for (const struct cap_info *cap = capabilities; cap->name != NULL; cap++) {
+ if (fuse_get_feature_flag(conn, cap->flag)) {
+ printf("\t%s\n", cap->name);
+ }
+ }
+}
+
+static void pc_init(void *userdata, struct fuse_conn_info *conn)
{
(void) userdata;
-
+
printf("Protocol version: %d.%d\n", conn->proto_major,
conn->proto_minor);
- printf("Capabilities:\n");
- if(conn->capable & FUSE_CAP_ASYNC_READ)
- printf("\tFUSE_CAP_ASYNC_READ\n");
- if(conn->capable & FUSE_CAP_POSIX_LOCKS)
- printf("\tFUSE_CAP_POSIX_LOCKS\n");
- if(conn->capable & FUSE_CAP_ATOMIC_O_TRUNC)
- printf("\tFUSE_CAP_ATOMIC_O_TRUNC\n");
- if(conn->capable & FUSE_CAP_EXPORT_SUPPORT)
- printf("\tFUSE_CAP_EXPORT_SUPPORT\n");
- if(conn->capable & FUSE_CAP_DONT_MASK)
- printf("\tFUSE_CAP_DONT_MASK\n");
- if(conn->capable & FUSE_CAP_SPLICE_MOVE)
- printf("\tFUSE_CAP_SPLICE_MOVE\n");
- if(conn->capable & FUSE_CAP_SPLICE_READ)
- printf("\tFUSE_CAP_SPLICE_READ\n");
- if(conn->capable & FUSE_CAP_SPLICE_WRITE)
- printf("\tFUSE_CAP_SPLICE_WRITE\n");
- if(conn->capable & FUSE_CAP_FLOCK_LOCKS)
- printf("\tFUSE_CAP_FLOCK_LOCKS\n");
- if(conn->capable & FUSE_CAP_IOCTL_DIR)
- printf("\tFUSE_CAP_IOCTL_DIR\n");
- if(conn->capable & FUSE_CAP_AUTO_INVAL_DATA)
- printf("\tFUSE_CAP_AUTO_INVAL_DATA\n");
- if(conn->capable & FUSE_CAP_READDIRPLUS)
- printf("\tFUSE_CAP_READDIRPLUS\n");
- if(conn->capable & FUSE_CAP_READDIRPLUS_AUTO)
- printf("\tFUSE_CAP_READDIRPLUS_AUTO\n");
- if(conn->capable & FUSE_CAP_ASYNC_DIO)
- printf("\tFUSE_CAP_ASYNC_DIO\n");
- if(conn->capable & FUSE_CAP_WRITEBACK_CACHE)
- printf("\tFUSE_CAP_WRITEBACK_CACHE\n");
- if(conn->capable & FUSE_CAP_NO_OPEN_SUPPORT)
- printf("\tFUSE_CAP_NO_OPEN_SUPPORT\n");
- if(conn->capable & FUSE_CAP_PARALLEL_DIROPS)
- printf("\tFUSE_CAP_PARALLEL_DIROPS\n");
- if(conn->capable & FUSE_CAP_POSIX_ACL)
- printf("\tFUSE_CAP_POSIX_ACL\n");
- if(conn->capable & FUSE_CAP_CACHE_SYMLINKS)
- printf("\tFUSE_CAP_CACHE_SYMLINKS\n");
- if(conn->capable & FUSE_CAP_NO_OPENDIR_SUPPORT)
- printf("\tFUSE_CAP_NO_OPENDIR_SUPPORT\n");
- if(conn->capable & FUSE_CAP_EXPLICIT_INVAL_DATA)
- printf("\tFUSE_CAP_EXPLICIT_INVAL_DATA\n");
- if(conn->capable & FUSE_CAP_EXPIRE_ONLY)
- printf("\tFUSE_CAP_EXPIRE_ONLY\n");
- if(conn->capable & FUSE_CAP_SETXATTR_EXT)
- printf("\tFUSE_CAP_SETXATTR_EXT\n");
- if(conn->capable & FUSE_CAP_HANDLE_KILLPRIV)
- printf("\tFUSE_CAP_HANDLE_KILLPRIV\n");
- if(conn->capable & FUSE_CAP_HANDLE_KILLPRIV_V2)
- printf("\tFUSE_CAP_HANDLE_KILLPRIV_V2\n");
- if(conn->capable & FUSE_CAP_DIRECT_IO_ALLOW_MMAP)
- printf("\tFUSE_CAP_DIRECT_IO_ALLOW_MMAP\n");
- if (conn->capable & FUSE_CAP_NO_EXPORT_SUPPORT)
- printf("\tFUSE_CAP_NO_EXPORT_SUPPORT\n");
+ print_capabilities(conn);
fuse_session_exit(se);
}
@@ -110,7 +105,7 @@ int main(int argc, char **argv)
perror("mkdtemp");
return 1;
}
-
+
printf("FUSE library version %s\n", fuse_pkgversion());
fuse_lowlevel_version();
diff --git a/include/fuse.h b/include/fuse.h
index 396358d..2fc1b6f 100644
--- a/include/fuse.h
+++ b/include/fuse.h
@@ -103,22 +103,22 @@ struct fuse_config {
* If `set_gid` is non-zero, the st_gid attribute of each file
* is overwritten with the value of `gid`.
*/
- int set_gid;
- unsigned int gid;
+ int32_t set_gid;
+ uint32_t gid;
/**
* If `set_uid` is non-zero, the st_uid attribute of each file
* is overwritten with the value of `uid`.
*/
- int set_uid;
- unsigned int uid;
+ int32_t set_uid;
+ uint32_t uid;
/**
* If `set_mode` is non-zero, the any permissions bits set in
* `umask` are unset in the st_mode attribute of each file.
*/
- int set_mode;
- unsigned int umask;
+ int32_t set_mode;
+ uint32_t umask;
/**
* The timeout in seconds for which name lookups will be
@@ -145,14 +145,14 @@ struct fuse_config {
/**
* Allow requests to be interrupted
*/
- int intr;
+ int32_t intr;
/**
* Specify which signal number to send to the filesystem when
* a request is interrupted. The default is hardcoded to
* USR1.
*/
- int intr_signal;
+ int32_t intr_signal;
/**
* Normally, FUSE assigns inodes to paths only for as long as
@@ -164,7 +164,7 @@ struct fuse_config {
* A number of -1 means that inodes will be remembered for the
* entire life-time of the file-system process.
*/
- int remember;
+ int32_t remember;
/**
* The default behavior is that if an open file is deleted,
@@ -182,7 +182,7 @@ struct fuse_config {
* ENOENT): read(2), write(2), fsync(2), close(2), f*xattr(2),
* ftruncate(2), fstat(2), fchmod(2), fchown(2)
*/
- int hard_remove;
+ int32_t hard_remove;
/**
* Honor the st_ino field in the functions getattr() and
@@ -195,7 +195,7 @@ struct fuse_config {
* Note that this does *not* affect the inode that libfuse
* and the kernel use internally (also called the "nodeid").
*/
- int use_ino;
+ int32_t use_ino;
/**
* If use_ino option is not given, still try to fill in the
@@ -204,7 +204,7 @@ struct fuse_config {
* found there will be used. Otherwise it will be set to -1.
* If use_ino option is given, this option is ignored.
*/
- int readdir_ino;
+ int32_t readdir_ino;
/**
* This option disables the use of page cache (file content cache)
@@ -223,7 +223,7 @@ struct fuse_config {
* `direct_io` field of `struct fuse_file_info` - overwriting
* any value that was put there by the file system.
*/
- int direct_io;
+ int32_t direct_io;
/**
* This option disables flushing the cache of the file
@@ -242,7 +242,7 @@ struct fuse_config {
* `keep_cache` field of `struct fuse_file_info` - overwriting
* any value that was put there by the file system.
*/
- int kernel_cache;
+ int32_t kernel_cache;
/**
* This option is an alternative to `kernel_cache`. Instead of
@@ -250,22 +250,14 @@ struct fuse_config {
* invalidated on open(2) if if the modification time or the
* size of the file has changed since it was last opened.
*/
- int auto_cache;
+ int32_t auto_cache;
- /**
- * By default, fuse waits for all pending writes to complete
- * and calls the FLUSH operation on close(2) of every fuse fd.
- * With this option, wait and FLUSH are not done for read-only
- * fuse fd, similar to the behavior of NFS/SMB clients.
- */
- int no_rofd_flush;
-
- /**
+ /*
* The timeout in seconds for which file attributes are cached
* for the purpose of checking if auto_cache should flush the
* file data on open.
*/
- int ac_attr_timeout_set;
+ int32_t ac_attr_timeout_set;
double ac_attr_timeout;
/**
@@ -278,7 +270,31 @@ struct fuse_config {
* operations the path will be provided only if the struct
* fuse_file_info argument is NULL.
*/
- int nullpath_ok;
+ int32_t nullpath_ok;
+
+ /**
+ * These 3 options are used by libfuse internally and
+ * should not be touched.
+ */
+ int32_t show_help;
+ char *modules;
+ int32_t debug;
+
+ /**
+ * `fmask` and `dmask` function the same way as `umask`, but apply
+ * to files and directories separately. If non-zero, `fmask` and
+ * `dmask` take precedence over the `umask` setting.
+ */
+ uint32_t fmask;
+ uint32_t dmask;
+
+ /**
+ * By default, fuse waits for all pending writes to complete
+ * and calls the FLUSH operation on close(2) of every fuse fd.
+ * With this option, wait and FLUSH are not done for read-only
+ * fuse fd, similar to the behavior of NFS/SMB clients.
+ */
+ int32_t no_rofd_flush;
/**
* Allow parallel direct-io writes to operate on the same file.
@@ -293,23 +309,18 @@ struct fuse_config {
* enabling this setting, all direct writes on the same file are
* serialized, resulting in huge data bandwidth loss).
*/
- int parallel_direct_writes;
+ int32_t parallel_direct_writes;
+
/**
- * These 3 options are used by libfuse internally and
- * should not be touched.
+ * Reserved for future use.
*/
- int show_help;
- char *modules;
- int debug;
+ uint32_t flags;
/**
- * `fmask` and `dmask` function the same way as `umask`, but apply
- * to files and directories separately. If non-zero, `fmask` and
- * `dmask` take precedence over the `umask` setting.
+ * Reserved for future use.
*/
- unsigned int fmask;
- unsigned int dmask;
+ uint64_t reserved[48];
};
diff --git a/include/fuse_common.h b/include/fuse_common.h
index e6c3b5d..aba1c15 100644
--- a/include/fuse_common.h
+++ b/include/fuse_common.h
@@ -25,10 +25,19 @@
#include "fuse_log.h"
#include <stdint.h>
#include <sys/types.h>
+#include <assert.h>
#define FUSE_MAKE_VERSION(maj, min) ((maj) * 100 + (min))
#define FUSE_VERSION FUSE_MAKE_VERSION(FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION)
+#if (defined(__cplusplus) && __cplusplus >= 201103L) || \
+ (!defined(__cplusplus) && defined(__STDC_VERSION__) && \
+ __STDC_VERSION__ >= 201112L)
+#define fuse_static_assert(condition, message) static_assert(condition, message)
+#else
+#define fuse_static_assert(condition, message)
+#endif
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -48,7 +57,7 @@ extern "C" {
*/
struct fuse_file_info {
/** Open flags. Available in open(), release() and create() */
- int flags;
+ int32_t flags;
/** In case of a write operation indicates if this was caused
by a delayed write from the page cache. If so, then the
@@ -56,48 +65,49 @@ struct fuse_file_info {
the *fh* value may not match the *fh* value that would
have been sent with the corresponding individual write
requests if write caching had been disabled. */
- unsigned int writepage : 1;
+ uint32_t writepage : 1;
/** Can be filled in by open/create, to use direct I/O on this file. */
- unsigned int direct_io : 1;
+ uint32_t direct_io : 1;
/** Can be filled in by open and opendir. It signals the kernel that any
currently cached data (ie., data that the filesystem provided the
last time the file/directory was open) need not be invalidated when
the file/directory is closed. */
- unsigned int keep_cache : 1;
-
- /** Can be filled by open/create, to allow parallel direct writes on this
- file */
- unsigned int parallel_direct_writes : 1;
+ uint32_t keep_cache : 1;
/** Indicates a flush operation. Set in flush operation, also
maybe set in highlevel lock operation and lowlevel release
operation. */
- unsigned int flush : 1;
+ uint32_t flush : 1;
/** Can be filled in by open, to indicate that the file is not
seekable. */
- unsigned int nonseekable : 1;
+ uint32_t nonseekable : 1;
/* Indicates that flock locks for this file should be
released. If set, lock_owner shall contain a valid value.
May only be set in ->release(). */
- unsigned int flock_release : 1;
+ uint32_t flock_release : 1;
/** Can be filled in by opendir. It signals the kernel to
enable caching of entries returned by readdir(). Has no
effect when set in other contexts (in particular it does
nothing when set by open()). */
- unsigned int cache_readdir : 1;
+ uint32_t cache_readdir : 1;
/** Can be filled in by open, to indicate that flush is not needed
on close. */
- unsigned int noflush : 1;
+ uint32_t noflush : 1;
+
+ /** Can be filled by open/create, to allow parallel direct writes on this
+ file */
+ uint32_t parallel_direct_writes : 1;
/** Padding. Reserved for future use*/
- unsigned int padding : 23;
- unsigned int padding2 : 32;
+ uint32_t padding : 23;
+ uint32_t padding2 : 32;
+ uint32_t padding3 : 32;
/** File handle id. May be filled in by filesystem in create,
* open, and opendir(). Available in most other file operations on the
@@ -115,9 +125,14 @@ struct fuse_file_info {
* create and open. It is used to create a passthrough connection
* between FUSE file and backing file. */
int32_t backing_id;
-};
+ /** struct fuse_file_info api and abi flags */
+ uint64_t compat_flags;
+ uint64_t reserved[2];
+};
+fuse_static_assert(sizeof(struct fuse_file_info) == 64,
+ "fuse_file_info size mismatch");
/**
* Configuration parameters passed to fuse_session_loop_mt() and
@@ -159,348 +174,355 @@ struct fuse_loop_config_v1 {
* Capability bits for 'fuse_conn_info.capable' and 'fuse_conn_info.want' *
**************************************************************************/
-/**
- * Indicates that the filesystem supports asynchronous read requests.
- *
- * If this capability is not requested/available, the kernel will
- * ensure that there is at most one pending read request per
- * file-handle at any time, and will attempt to order read requests by
- * increasing offset.
- *
- * This feature is enabled by default when supported by the kernel.
- */
-#define FUSE_CAP_ASYNC_READ (1 << 0)
+enum fuse_capability {
+ /**
+ * Indicates that the filesystem supports asynchronous read requests.
+ *
+ * If this capability is not requested/available, the kernel will
+ * ensure that there is at most one pending read request per
+ * file-handle at any time, and will attempt to order read requests by
+ * increasing offset.
+ *
+ * This feature is enabled by default when supported by the kernel.
+ */
+ FUSE_CAP_ASYNC_READ = (1 << 0),
-/**
- * Indicates that the filesystem supports "remote" locking.
- *
- * This feature is enabled by default when supported by the kernel,
- * and if getlk() and setlk() handlers are implemented.
- */
-#define FUSE_CAP_POSIX_LOCKS (1 << 1)
+ /**
+ * Indicates that the filesystem supports "remote" locking.
+ *
+ * This feature is enabled by default when supported by the kernel,
+ * and if getlk() and setlk() handlers are implemented.
+ */
+ FUSE_CAP_POSIX_LOCKS = (1 << 1),
-/**
- * Indicates that the filesystem supports the O_TRUNC open flag. If
- * disabled, and an application specifies O_TRUNC, fuse first calls
- * truncate() and then open() with O_TRUNC filtered out.
- *
- * This feature is enabled by default when supported by the kernel.
- */
-#define FUSE_CAP_ATOMIC_O_TRUNC (1 << 3)
+ /**
+ * Indicates that the filesystem supports the O_TRUNC open flag. If
+ * disabled, and an application specifies O_TRUNC, fuse first calls
+ * truncate() and then open() with O_TRUNC filtered out.
+ *
+ * This feature is enabled by default when supported by the kernel.
+ */
+ FUSE_CAP_ATOMIC_O_TRUNC = (1 << 3),
-/**
- * Indicates that the filesystem supports lookups of "." and "..".
- *
- * When this flag is set, the filesystem must be prepared to receive requests
- * for invalid inodes (i.e., for which a FORGET request was received or
- * which have been used in a previous instance of the filesystem daemon) and
- * must not reuse node-ids (even when setting generation numbers).
- *
- * This feature is disabled by default.
- */
-#define FUSE_CAP_EXPORT_SUPPORT (1 << 4)
+ /**
+ * Indicates that the filesystem supports lookups of "." and "..".
+ *
+ * When this flag is set, the filesystem must be prepared to receive requests
+ * for invalid inodes (i.e., for which a FORGET request was received or
+ * which have been used in a previous instance of the filesystem daemon) and
+ * must not reuse node-ids (even when setting generation numbers).
+ *
+ * This feature is disabled by default.
+ */
+ FUSE_CAP_EXPORT_SUPPORT = (1 << 4),
-/**
- * Indicates that the kernel should not apply the umask to the
- * file mode on create operations.
- *
- * This feature is disabled by default.
- */
-#define FUSE_CAP_DONT_MASK (1 << 6)
+ /**
+ * Indicates that the kernel should not apply the umask to the
+ * file mode on create operations.
+ *
+ * This feature is disabled by default.
+ */
+ FUSE_CAP_DONT_MASK = (1 << 6),
-/**
- * Indicates that libfuse should try to use splice() when writing to
- * the fuse device. This may improve performance.
- *
- * This feature is disabled by default.
- */
-#define FUSE_CAP_SPLICE_WRITE (1 << 7)
+ /**
+ * Indicates that libfuse should try to use splice() when writing to
+ * the fuse device. This may improve performance.
+ *
+ * This feature is disabled by default.
+ */
+ FUSE_CAP_SPLICE_WRITE = (1 << 7),
-/**
- * Indicates that libfuse should try to move pages instead of copying when
- * writing to / reading from the fuse device. This may improve performance.
- *
- * This feature is disabled by default.
- */
-#define FUSE_CAP_SPLICE_MOVE (1 << 8)
+ /**
+ * Indicates that libfuse should try to move pages instead of copying when
+ * writing to / reading from the fuse device. This may improve performance.
+ *
+ * This feature is disabled by default.
+ */
+ FUSE_CAP_SPLICE_MOVE = (1 << 8),
-/**
- * Indicates that libfuse should try to use splice() when reading from
- * the fuse device. This may improve performance.
- *
- * This feature is enabled by default when supported by the kernel and
- * if the filesystem implements a write_buf() handler.
- */
-#define FUSE_CAP_SPLICE_READ (1 << 9)
+ /**
+ * Indicates that libfuse should try to use splice() when reading from
+ * the fuse device. This may improve performance.
+ *
+ * This feature is enabled by default when supported by the kernel and
+ * if the filesystem implements a write_buf() handler.
+ */
+ FUSE_CAP_SPLICE_READ = (1 << 9),
-/**
- * If set, the calls to flock(2) will be emulated using POSIX locks and must
- * then be handled by the filesystem's setlock() handler.
- *
- * If not set, flock(2) calls will be handled by the FUSE kernel module
- * internally (so any access that does not go through the kernel cannot be taken
- * into account).
- *
- * This feature is enabled by default when supported by the kernel and
- * if the filesystem implements a flock() handler.
- */
-#define FUSE_CAP_FLOCK_LOCKS (1 << 10)
+ /**
+ * If set, the calls to flock(2) will be emulated using POSIX locks and must
+ * then be handled by the filesystem's setlock() handler.
+ *
+ * If not set, flock(2) calls will be handled by the FUSE kernel module
+ * internally (so any access that does not go through the kernel cannot be taken
+ * into account).
+ *
+ * This feature is enabled by default when supported by the kernel and
+ * if the filesystem implements a flock() handler.
+ */
+ FUSE_CAP_FLOCK_LOCKS = (1 << 10),
-/**
- * Indicates that the filesystem supports ioctl's on directories.
- *
- * This feature is enabled by default when supported by the kernel.
- */
-#define FUSE_CAP_IOCTL_DIR (1 << 11)
+ /**
+ * Indicates that the filesystem supports ioctl's on directories.
+ *
+ * This feature is enabled by default when supported by the kernel.
+ */
+ FUSE_CAP_IOCTL_DIR = (1 << 11),
-/**
- * Traditionally, while a file is open the FUSE kernel module only
- * asks the filesystem for an update of the file's attributes when a
- * client attempts to read beyond EOF. This is unsuitable for
- * e.g. network filesystems, where the file contents may change
- * without the kernel knowing about it.
- *
- * If this flag is set, FUSE will check the validity of the attributes
- * on every read. If the attributes are no longer valid (i.e., if the
- * *attr_timeout* passed to fuse_reply_attr() or set in `struct
- * fuse_entry_param` has passed), it will first issue a `getattr`
- * request. If the new mtime differs from the previous value, any
- * cached file *contents* will be invalidated as well.
- *
- * This flag should always be set when available. If all file changes
- * go through the kernel, *attr_timeout* should be set to a very large
- * number to avoid unnecessary getattr() calls.
- *
- * This feature is enabled by default when supported by the kernel.
- */
-#define FUSE_CAP_AUTO_INVAL_DATA (1 << 12)
+ /**
+ * Traditionally, while a file is open the FUSE kernel module only
+ * asks the filesystem for an update of the file's attributes when a
+ * client attempts to read beyond EOF. This is unsuitable for
+ * e.g. network filesystems, where the file contents may change
+ * without the kernel knowing about it.
+ *
+ * If this flag is set, FUSE will check the validity of the attributes
+ * on every read. If the attributes are no longer valid (i.e., if the
+ * *attr_timeout* passed to fuse_reply_attr() or set in `struct
+ * fuse_entry_param` has passed), it will first issue a `getattr`
+ * request. If the new mtime differs from the previous value, any
+ * cached file *contents* will be invalidated as well.
+ *
+ * This flag should always be set when available. If all file changes
+ * go through the kernel, *attr_timeout* should be set to a very large
+ * number to avoid unnecessary getattr() calls.
+ *
+ * This feature is enabled by default when supported by the kernel.
+ */
+ FUSE_CAP_AUTO_INVAL_DATA = (1 << 12),
-/**
- * Indicates that the filesystem supports readdirplus.
- *
- * This feature is enabled by default when supported by the kernel and if the
- * filesystem implements a readdirplus() handler.
- */
-#define FUSE_CAP_READDIRPLUS (1 << 13)
+ /**
+ * Indicates that the filesystem supports readdirplus.
+ *
+ * This feature is enabled by default when supported by the kernel and if the
+ * filesystem implements a readdirplus() handler.
+ */
+ FUSE_CAP_READDIRPLUS = (1 << 13),
-/**
- * Indicates that the filesystem supports adaptive readdirplus.
- *
- * If FUSE_CAP_READDIRPLUS is not set, this flag has no effect.
- *
- * If FUSE_CAP_READDIRPLUS is set and this flag is not set, the kernel
- * will always issue readdirplus() requests to retrieve directory
- * contents.
- *
- * If FUSE_CAP_READDIRPLUS is set and this flag is set, the kernel
- * will issue both readdir() and readdirplus() requests, depending on
- * how much information is expected to be required.
- *
- * As of Linux 4.20, the algorithm is as follows: when userspace
- * starts to read directory entries, issue a READDIRPLUS request to
- * the filesystem. If any entry attributes have been looked up by the
- * time userspace requests the next batch of entries continue with
- * READDIRPLUS, otherwise switch to plain READDIR. This will reasult
- * in eg plain "ls" triggering READDIRPLUS first then READDIR after
- * that because it doesn't do lookups. "ls -l" should result in all
- * READDIRPLUS, except if dentries are already cached.
- *
- * This feature is enabled by default when supported by the kernel and
- * if the filesystem implements both a readdirplus() and a readdir()
- * handler.
- */
-#define FUSE_CAP_READDIRPLUS_AUTO (1 << 14)
+ /**
+ * Indicates that the filesystem supports adaptive readdirplus.
+ *
+ * If FUSE_CAP_READDIRPLUS is not set, this flag has no effect.
+ *
+ * If FUSE_CAP_READDIRPLUS is set and this flag is not set, the kernel
+ * will always issue readdirplus() requests to retrieve directory
+ * contents.
+ *
+ * If FUSE_CAP_READDIRPLUS is set and this flag is set, the kernel
+ * will issue both readdir() and readdirplus() requests, depending on
+ * how much information is expected to be required.
+ *
+ * As of Linux 4.20, the algorithm is as follows: when userspace
+ * starts to read directory entries, issue a READDIRPLUS request to
+ * the filesystem. If any entry attributes have been looked up by the
+ * time userspace requests the next batch of entries continue with
+ * READDIRPLUS, otherwise switch to plain READDIR. This will reasult
+ * in eg plain "ls" triggering READDIRPLUS first then READDIR after
+ * that because it doesn't do lookups. "ls -l" should result in all
+ * READDIRPLUS, except if dentries are already cached.
+ *
+ * This feature is enabled by default when supported by the kernel and
+ * if the filesystem implements both a readdirplus() and a readdir()
+ * handler.
+ */
+ FUSE_CAP_READDIRPLUS_AUTO = (1 << 14),
-/**
- * Indicates that the filesystem supports asynchronous direct I/O submission.
- *
- * If this capability is not requested/available, the kernel will ensure that
- * there is at most one pending read and one pending write request per direct
- * I/O file-handle at any time.
- *
- * This feature is enabled by default when supported by the kernel.
- */
-#define FUSE_CAP_ASYNC_DIO (1 << 15)
+ /**
+ * Indicates that the filesystem supports asynchronous direct I/O submission.
+ *
+ * If this capability is not requested/available, the kernel will ensure that
+ * there is at most one pending read and one pending write request per direct
+ * I/O file-handle at any time.
+ *
+ * This feature is enabled by default when supported by the kernel.
+ */
+ FUSE_CAP_ASYNC_DIO = (1 << 15),
-/**
- * Indicates that writeback caching should be enabled. This means that
- * individual write request may be buffered and merged in the kernel
- * before they are send to the filesystem.
- *
- * This feature is disabled by default.
- */
-#define FUSE_CAP_WRITEBACK_CACHE (1 << 16)
+ /**
+ * Indicates that writeback caching should be enabled. This means that
+ * individual write request may be buffered and merged in the kernel
+ * before they are send to the filesystem.
+ *
+ * This feature is disabled by default.
+ */
+ FUSE_CAP_WRITEBACK_CACHE = (1 << 16),
-/**
- * Indicates support for zero-message opens. If this flag is set in
- * the `capable` field of the `fuse_conn_info` structure, then the
- * filesystem may return `ENOSYS` from the open() handler to indicate
- * success. Further attempts to open files will be handled in the
- * kernel. (If this flag is not set, returning ENOSYS will be treated
- * as an error and signaled to the caller).
- *
- * Setting this flag in the `want` field enables this behavior automatically
- * within libfuse for low level API users. If non-low level users wish to have
- * this behavior you must return `ENOSYS` from the open() handler on supporting
- * kernels.
- */
-#define FUSE_CAP_NO_OPEN_SUPPORT (1 << 17)
+ /**
+ * Indicates support for zero-message opens. If this flag is set in
+ * the `capable` field of the `fuse_conn_info` structure, then the
+ * filesystem may return `ENOSYS` from the open() handler to indicate
+ * success. Further attempts to open files will be handled in the
+ * kernel. (If this flag is not set, returning ENOSYS will be treated
+ * as an error and signaled to the caller).
+ *
+ * Setting this flag in the `want` field enables this behavior automatically
+ * within libfuse for low level API users. If non-low level users wish to have
+ * this behavior you must return `ENOSYS` from the open() handler on supporting
+ * kernels.
+ */
+ FUSE_CAP_NO_OPEN_SUPPORT = (1 << 17),
-/**
- * Indicates support for parallel directory operations. If this flag
- * is unset, the FUSE kernel module will ensure that lookup() and
- * readdir() requests are never issued concurrently for the same
- * directory.
- */
-#define FUSE_CAP_PARALLEL_DIROPS (1 << 18)
+ /**
+ * Indicates support for parallel directory operations. If this flag
+ * is unset, the FUSE kernel module will ensure that lookup() and
+ * readdir() requests are never issued concurrently for the same
+ * directory.
+ */
+ FUSE_CAP_PARALLEL_DIROPS = (1 << 18),
-/**
- * Indicates support for POSIX ACLs.
- *
- * If this feature is enabled, the kernel will cache and have
- * responsibility for enforcing ACLs. ACL will be stored as xattrs and
- * passed to userspace, which is responsible for updating the ACLs in
- * the filesystem, keeping the file mode in sync with the ACL, and
- * ensuring inheritance of default ACLs when new filesystem nodes are
- * created. Note that this requires that the file system is able to
- * parse and interpret the xattr representation of ACLs.
- *
- * Enabling this feature implicitly turns on the
- * ``default_permissions`` mount option (even if it was not passed to
- * mount(2)).
- *
- * This feature is disabled by default.
- */
-#define FUSE_CAP_POSIX_ACL (1 << 19)
+ /**
+ * Indicates support for POSIX ACLs.
+ *
+ * If this feature is enabled, the kernel will cache and have
+ * responsibility for enforcing ACLs. ACL will be stored as xattrs and
+ * passed to userspace, which is responsible for updating the ACLs in
+ * the filesystem, keeping the file mode in sync with the ACL, and
+ * ensuring inheritance of default ACLs when new filesystem nodes are
+ * created. Note that this requires that the file system is able to
+ * parse and interpret the xattr representation of ACLs.
+ *
+ * Enabling this feature implicitly turns on the
+ * ``default_permissions`` mount option (even if it was not passed to
+ * mount(2)).
+ *
+ * This feature is disabled by default.
+ */
+ FUSE_CAP_POSIX_ACL = (1 << 19),
-/**
- * Indicates that the filesystem is responsible for unsetting
- * setuid and setgid bits when a file is written, truncated, or
- * its owner is changed.
- *
- * This feature is disabled by default.
- */
-#define FUSE_CAP_HANDLE_KILLPRIV (1 << 20)
+ /**
+ * Indicates that the filesystem is responsible for unsetting
+ * setuid and setgid bits when a file is written, truncated, or
+ * its owner is changed.
+ *
+ * This feature is disabled by default.
+ */
+ FUSE_CAP_HANDLE_KILLPRIV = (1 << 20),
-/**
- * Indicates that the filesystem is responsible for unsetting
- * setuid and setgid bit and additionally cap (stored as xattr) when a
- * file is written, truncated, or its owner is changed.
- * 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).
- * KILLPRIV_V2 requires handling of
- * - FUSE_OPEN_KILL_SUIDGID (set in struct fuse_create_in::open_flags)
- * - FATTR_KILL_SUIDGID (set in struct fuse_setattr_in::valid)
- * - FUSE_WRITE_KILL_SUIDGID (set in struct fuse_write_in::write_flags)
- *
- * This feature is disabled by default.
- */
-#define FUSE_CAP_HANDLE_KILLPRIV_V2 (1 << 21)
+ /**
+ * Indicates that the filesystem is responsible for unsetting
+ * setuid and setgid bit and additionally cap (stored as xattr) when a
+ * file is written, truncated, or its owner is changed.
+ * 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).
+ * KILLPRIV_V2 requires handling of
+ * - FUSE_OPEN_KILL_SUIDGID (set in struct fuse_create_in::open_flags)
+ * - FATTR_KILL_SUIDGID (set in struct fuse_setattr_in::valid)
+ * - FUSE_WRITE_KILL_SUIDGID (set in struct fuse_write_in::write_flags)
+ *
+ * This feature is disabled by default.
+ */
+ FUSE_CAP_HANDLE_KILLPRIV_V2 = (1 << 21),
-/**
- * Indicates that the kernel supports caching symlinks in its page cache.
- *
- * When this feature is enabled, symlink targets are saved in the page cache.
- * You can invalidate a cached link by calling:
- * `fuse_lowlevel_notify_inval_inode(se, ino, 0, 0);`
- *
- * This feature is disabled by default.
- * If the kernel supports it (>= 4.20), you can enable this feature by
- * setting this flag in the `want` field of the `fuse_conn_info` structure.
- */
-#define FUSE_CAP_CACHE_SYMLINKS (1 << 23)
+ /**
+ * Indicates that the kernel supports caching symlinks in its page cache.
+ *
+ * When this feature is enabled, symlink targets are saved in the page cache.
+ * You can invalidate a cached link by calling:
+ * `fuse_lowlevel_notify_inval_inode(se, ino, 0, 0);`
+ *
+ * This feature is disabled by default.
+ * If the kernel supports it (>= 4.20), you can enable this feature by
+ * setting this flag in the `want` field of the `fuse_conn_info` structure.
+ */
+ FUSE_CAP_CACHE_SYMLINKS = (1 << 23),
-/**
- * Indicates support for zero-message opendirs. If this flag is set in
- * the `capable` field of the `fuse_conn_info` structure, then the filesystem
- * may return `ENOSYS` from the opendir() handler to indicate success. Further
- * opendir and releasedir messages will be handled in the kernel. (If this
- * flag is not set, returning ENOSYS will be treated as an error and signalled
- * to the caller.)
- *
- * Setting this flag in the `want` field enables this behavior automatically
- * within libfuse for low level API users. If non-low level users with to have
- * this behavior you must return `ENOSYS` from the opendir() handler on
- * supporting kernels.
- */
-#define FUSE_CAP_NO_OPENDIR_SUPPORT (1 << 24)
+ /**
+ * Indicates support for zero-message opendirs. If this flag is set in
+ * the `capable` field of the `fuse_conn_info` structure, then the filesystem
+ * may return `ENOSYS` from the opendir() handler to indicate success. Further
+ * opendir and releasedir messages will be handled in the kernel. (If this
+ * flag is not set, returning ENOSYS will be treated as an error and signalled
+ * to the caller.)
+ *
+ * Setting this flag in the `want` field enables this behavior automatically
+ * within libfuse for low level API users. If non-low level users with to have
+ * this behavior you must return `ENOSYS` from the opendir() handler on
+ * supporting kernels.
+ */
+ FUSE_CAP_NO_OPENDIR_SUPPORT = (1 << 24),
-/**
- * Indicates support for invalidating cached pages only on explicit request.
- *
- * If this flag is set in the `capable` field of the `fuse_conn_info` structure,
- * then the FUSE kernel module supports invalidating cached pages only on
- * explicit request by the filesystem through fuse_lowlevel_notify_inval_inode()
- * or fuse_invalidate_path().
- *
- * By setting this flag in the `want` field of the `fuse_conn_info` structure,
- * the filesystem is responsible for invalidating cached pages through explicit
- * requests to the kernel.
- *
- * Note that setting this flag does not prevent the cached pages from being
- * flushed by OS itself and/or through user actions.
- *
- * Note that if both FUSE_CAP_EXPLICIT_INVAL_DATA and FUSE_CAP_AUTO_INVAL_DATA
- * are set in the `capable` field of the `fuse_conn_info` structure then
- * FUSE_CAP_AUTO_INVAL_DATA takes precedence.
- *
- * This feature is disabled by default.
- */
-#define FUSE_CAP_EXPLICIT_INVAL_DATA (1 << 25)
+ /**
+ * Indicates support for invalidating cached pages only on explicit request.
+ *
+ * If this flag is set in the `capable` field of the `fuse_conn_info` structure,
+ * then the FUSE kernel module supports invalidating cached pages only on
+ * explicit request by the filesystem through fuse_lowlevel_notify_inval_inode()
+ * or fuse_invalidate_path().
+ *
+ * By setting this flag in the `want` field of the `fuse_conn_info` structure,
+ * the filesystem is responsible for invalidating cached pages through explicit
+ * requests to the kernel.
+ *
+ * Note that setting this flag does not prevent the cached pages from being
+ * flushed by OS itself and/or through user actions.
+ *
+ * Note that if both FUSE_CAP_EXPLICIT_INVAL_DATA and FUSE_CAP_AUTO_INVAL_DATA
+ * are set in the `capable` field of the `fuse_conn_info` structure then
+ * FUSE_CAP_AUTO_INVAL_DATA takes precedence.
+ *
+ * This feature is disabled by default.
+ */
+ FUSE_CAP_EXPLICIT_INVAL_DATA = (1 << 25),
-/**
- * Indicates support that dentries can be expired.
- *
- * Expiring dentries, instead of invalidating them, makes a difference for
- * overmounted dentries, where plain invalidation would detach all submounts
- * before dropping the dentry from the cache. If only expiry is set on the
- * dentry, then any overmounts are left alone and until ->d_revalidate()
- * is called.
- *
- * Note: ->d_revalidate() is not called for the case of following a submount,
- * so invalidation will only be triggered for the non-overmounted case.
- * The dentry could also be mounted in a different mount instance, in which case
- * any submounts will still be detached.
-*/
-#define FUSE_CAP_EXPIRE_ONLY (1 << 26)
+ /**
+ * Indicates support that dentries can be expired.
+ *
+ * Expiring dentries, instead of invalidating them, makes a difference for
+ * overmounted dentries, where plain invalidation would detach all submounts
+ * before dropping the dentry from the cache. If only expiry is set on the
+ * dentry, then any overmounts are left alone and until ->d_revalidate()
+ * is called.
+ *
+ * Note: ->d_revalidate() is not called for the case of following a submount,
+ * so invalidation will only be triggered for the non-overmounted case.
+ * The dentry could also be mounted in a different mount instance, in which case
+ * any submounts will still be detached.
+ */
+ FUSE_CAP_EXPIRE_ONLY = (1 << 26),
-/**
- * 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 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.
+ */
+ FUSE_CAP_SETXATTR_EXT = (1 << 27),
-/**
- * Files opened with FUSE_DIRECT_IO do not support MAP_SHARED mmap. This restriction
- * is relaxed through FUSE_CAP_DIRECT_IO_RELAX (kernel flag: FUSE_DIRECT_IO_RELAX).
- * MAP_SHARED is disabled by default for FUSE_DIRECT_IO, as this flag can be used to
- * ensure coherency between mount points (or network clients) and with kernel page
- * cache as enforced by mmap that cannot be guaranteed anymore.
- */
-#define FUSE_CAP_DIRECT_IO_ALLOW_MMAP (1 << 28)
+ /**
+ * Files opened with FUSE_DIRECT_IO do not support MAP_SHARED mmap. This restriction
+ * is relaxed through FUSE_CAP_DIRECT_IO_RELAX (kernel flag: FUSE_DIRECT_IO_RELAX).
+ * MAP_SHARED is disabled by default for FUSE_DIRECT_IO, as this flag can be used to
+ * ensure coherency between mount points (or network clients) and with kernel page
+ * cache as enforced by mmap that cannot be guaranteed anymore.
+ */
+ FUSE_CAP_DIRECT_IO_ALLOW_MMAP = (1 << 28),
-/**
- * Indicates support for passthrough mode access for read/write operations.
- *
- * If this flag is set in the `capable` field of the `fuse_conn_info`
- * structure, then the FUSE kernel module supports redirecting read/write
- * operations to the backing file instead of letting them to be handled
- * by the FUSE daemon.
- *
- * This feature is disabled by default.
- */
-#define FUSE_CAP_PASSTHROUGH (1 << 29)
+ /**
+ * Indicates support for passthrough mode access for read/write operations.
+ *
+ * If this flag is set in the `capable` field of the `fuse_conn_info`
+ * structure, then the FUSE kernel module supports redirecting read/write
+ * operations to the backing file instead of letting them to be handled
+ * by the FUSE daemon.
+ *
+ * This feature is disabled by default.
+ */
+ FUSE_CAP_PASSTHROUGH = (1 << 29),
-/**
- * Indicates that the file system cannot handle NFS export
- *
- * If this flag is set NFS export and name_to_handle_at
- * is not going to work at all and will fail with EOPNOTSUPP.
- */
-#define FUSE_CAP_NO_EXPORT_SUPPORT (1 << 30)
+ /**
+ * Indicates that the file system cannot handle NFS export
+ *
+ * If this flag is set NFS export and name_to_handle_at
+ * is not going to work at all and will fail with EOPNOTSUPP.
+ */
+ FUSE_CAP_NO_EXPORT_SUPPORT = (1 << 30),
+
+ /**
+ * Current maximum capability value.
+ */
+ FUSE_CAP_CURRENT_MAX
+};
/**
* Ioctl flags
@@ -525,22 +547,26 @@ struct fuse_loop_config_v1 {
* Some of the elements are read-write, these can be changed to
* indicate the value requested by the filesystem. The requested
* value must usually be smaller than the indicated value.
+ *
+ * Note: The `capable` and `want` fields are limited to 32 bits for
+ * ABI compatibility. For full 64-bit capability support, use the
+ * `capable_ext` and `want_ext` fields instead.
*/
struct fuse_conn_info {
/**
* Major version of the protocol (read-only)
*/
- unsigned proto_major;
+ uint32_t proto_major;
/**
* Minor version of the protocol (read-only)
*/
- unsigned proto_minor;
+ uint32_t proto_minor;
/**
* Maximum size of the write buffer
*/
- unsigned max_write;
+ uint32_t max_write;
/**
* Maximum size of read requests. A value of zero indicates no
@@ -554,25 +580,31 @@ struct fuse_conn_info {
* in the future, specifying the mount option will no longer
* be necessary.
*/
- unsigned max_read;
+ uint32_t max_read;
/**
* Maximum readahead
*/
- unsigned max_readahead;
+ uint32_t max_readahead;
/**
* Capability flags that the kernel supports (read-only)
+ *
+ * Deprecated left over for ABI compatibility, use capable_ext
*/
- unsigned capable;
+ uint32_t capable;
/**
* Capability flags that the filesystem wants to enable.
*
* libfuse attempts to initialize this field with
* reasonable default values before calling the init() handler.
+ *
+ * Deprecated left over for ABI compatibility.
+ * Use want_ext with the helper functions
+ * fuse_set_feature_flag() / fuse_unset_feature_flag()
*/
- unsigned want;
+ uint32_t want;
/**
* Maximum number of pending "background" requests. A
@@ -602,7 +634,7 @@ struct fuse_conn_info {
* call actually blocks, so these are also limited to one per
* thread).
*/
- unsigned max_background;
+ uint32_t max_background;
/**
* Kernel congestion threshold parameter. If the number of pending
@@ -612,7 +644,7 @@ struct fuse_conn_info {
* adjust its algorithms accordingly (e.g. by putting a waiting thread
* to sleep instead of using a busy-loop).
*/
- unsigned congestion_threshold;
+ uint32_t congestion_threshold;
/**
* When FUSE_CAP_WRITEBACK_CACHE is enabled, the kernel is responsible
@@ -629,7 +661,7 @@ struct fuse_conn_info {
* nano-second resolution. Filesystems supporting only second resolution
* should set this to 1000000000.
*/
- unsigned time_gran;
+ uint32_t time_gran;
/**
* When FUSE_CAP_PASSTHROUGH is enabled, this is the maximum allowed
@@ -649,7 +681,7 @@ struct fuse_conn_info {
*/
#define FUSE_BACKING_STACKED_UNDER (0)
#define FUSE_BACKING_STACKED_OVER (1)
- unsigned max_backing_stack_depth;
+ uint32_t max_backing_stack_depth;
/**
* Disable FUSE_INTERRUPT requests.
@@ -659,13 +691,34 @@ struct fuse_conn_info {
* 2) Return ENOSYS for the reply of FUSE_INTERRUPT request to
* inform the kernel not to send the FUSE_INTERRUPT request.
*/
- unsigned no_interrupt;
+ uint32_t no_interrupt : 1;
+
+ /* reserved bits for future use */
+ uint32_t padding : 31;
+
+ /**
+ * Extended capability flags that the kernel supports (read-only)
+ * This field provides full 64-bit capability support.
+ */
+ uint64_t capable_ext;
+
+ /**
+ * Extended capability flags that the filesystem wants to enable.
+ * This field provides full 64-bit capability support.
+ *
+ * Don't set this field directly, but use the helper functions
+ * fuse_set_feature_flag() / fuse_unset_feature_flag()
+ *
+ */
+ uint64_t want_ext;
/**
* For future use.
*/
- unsigned reserved[20];
+ uint32_t reserved[16];
};
+fuse_static_assert(sizeof(struct fuse_conn_info) == 128,
+ "Size of struct fuse_conn_info must be 128 bytes");
struct fuse_session;
struct fuse_pollhandle;
@@ -914,10 +967,10 @@ struct fuse_bufvec {
*/
struct libfuse_version
{
- int major;
- int minor;
- int hotfix;
- int padding;
+ uint32_t major;
+ uint32_t minor;
+ uint32_t hotfix;
+ uint32_t padding;
};
/* Initialize bufvec with a single buffer of given size */
@@ -1056,8 +1109,8 @@ void fuse_loop_cfg_convert(struct fuse_loop_config *config,
static inline bool fuse_set_feature_flag(struct fuse_conn_info *conn,
uint64_t flag)
{
- if (conn->capable & flag) {
- conn->want |= flag;
+ if (conn->capable_ext & flag) {
+ conn->want_ext |= flag;
return true;
}
return false;
@@ -1066,7 +1119,13 @@ static inline bool fuse_set_feature_flag(struct fuse_conn_info *conn,
static inline void fuse_unset_feature_flag(struct fuse_conn_info *conn,
uint64_t flag)
{
- conn->want &= ~flag;
+ conn->want_ext &= ~flag;
+}
+
+static inline bool fuse_get_feature_flag(struct fuse_conn_info *conn,
+ uint64_t flag)
+{
+ return conn->capable_ext & flag ? true : false;
}
/* ----------------------------------------------------------- *
diff --git a/lib/cuse_lowlevel.c b/lib/cuse_lowlevel.c
index cd68787..5387f84 100644
--- a/lib/cuse_lowlevel.c
+++ b/lib/cuse_lowlevel.c
@@ -208,8 +208,10 @@ void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
}
se->conn.proto_major = arg->major;
se->conn.proto_minor = arg->minor;
- se->conn.capable = 0;
- se->conn.want = 0;
+
+ /* XXX This is not right.*/
+ se->conn.capable_ext = 0;
+ se->conn.want_ext = 0;
if (arg->major < 7) {
fuse_log(FUSE_LOG_ERR, "cuse: unsupported protocol version: %u.%u\n",
diff --git a/lib/fuse.c b/lib/fuse.c
index b327bab..a1537af 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -2619,8 +2619,7 @@ static void fuse_lib_init(void *data, struct fuse_conn_info *conn)
struct fuse *f = (struct fuse *) data;
fuse_create_context(f);
- if(conn->capable & FUSE_CAP_EXPORT_SUPPORT)
- conn->want |= FUSE_CAP_EXPORT_SUPPORT;
+ fuse_set_feature_flag(conn, FUSE_CAP_EXPORT_SUPPORT);
fuse_fs_init(f->fs, conn, &f->conf);
if (f->conf.intr) {
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
index e48efa5..d84c678 100644
--- a/lib/fuse_lowlevel.c
+++ b/lib/fuse_lowlevel.c
@@ -728,7 +728,7 @@ static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch,
goto fallback;
if (se->conn.proto_minor < 14 ||
- !(se->conn.want & FUSE_CAP_SPLICE_WRITE))
+ !(se->conn.want_ext & FUSE_CAP_SPLICE_WRITE))
goto fallback;
llp = fuse_ll_get_pipe(se);
@@ -869,7 +869,7 @@ static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch,
splice_flags = 0;
if ((flags & FUSE_BUF_SPLICE_MOVE) &&
- (se->conn.want & FUSE_CAP_SPLICE_MOVE))
+ (se->conn.want_ext & FUSE_CAP_SPLICE_MOVE))
splice_flags |= SPLICE_F_MOVE;
if (se->io != NULL && se->io->splice_send != NULL) {
@@ -1410,7 +1410,7 @@ static void do_open(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
if (req->se->op.open)
req->se->op.open(req, nodeid, &fi);
- else if (req->se->conn.want & FUSE_CAP_NO_OPEN_SUPPORT)
+ else if (req->se->conn.want_ext & FUSE_CAP_NO_OPEN_SUPPORT)
fuse_reply_err(req, ENOSYS);
else
fuse_reply_open(req, &fi);
@@ -1568,7 +1568,7 @@ static void do_opendir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
if (req->se->op.opendir)
req->se->op.opendir(req, nodeid, &fi);
- else if (req->se->conn.want & FUSE_CAP_NO_OPENDIR_SUPPORT)
+ else if (req->se->conn.want_ext & FUSE_CAP_NO_OPENDIR_SUPPORT)
fuse_reply_err(req, ENOSYS);
else
fuse_reply_open(req, &fi);
@@ -1651,7 +1651,7 @@ 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);
+ unsigned int xattr_ext = !!(se->conn.want_ext & FUSE_CAP_SETXATTR_EXT);
struct fuse_setxattr_in *arg = (struct fuse_setxattr_in *) inarg;
char *name = xattr_ext ? PARAM(arg) :
(char *)arg + FUSE_COMPAT_SETXATTR_IN_SIZE;
@@ -1882,7 +1882,7 @@ static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
struct fuse_file_info fi;
if (flags & FUSE_IOCTL_DIR &&
- !(req->se->conn.want & FUSE_CAP_IOCTL_DIR)) {
+ !(req->se->conn.want_ext & FUSE_CAP_IOCTL_DIR)) {
fuse_reply_err(req, ENOTTY);
return;
}
@@ -1997,6 +1997,27 @@ static bool want_flags_valid(uint64_t capable, uint64_t want)
return true;
}
+/**
+ * Get the wanted capability flags, converting from old format if necessary
+ * Also applies the first 32 bits of capable_ext to capable
+ *
+ */
+static inline int convert_to_conn_want_ext(struct fuse_conn_info *conn,
+ uint64_t want_ext_default)
+{
+ /* Convert want to want_ext if necessary */
+ if (conn->want != 0) {
+ if (conn->want_ext != want_ext_default) {
+ fuse_log(FUSE_LOG_ERR,
+ "fuse: both 'want' and 'want_ext' are set\n");
+ return -EINVAL;
+ }
+ conn->want_ext |= conn->want;
+ }
+
+ return 0;
+}
+
/* Prevent bogus data races (bogus since "init" is called before
* multi-threading becomes relevant */
static __attribute__((no_sanitize("thread")))
@@ -2021,8 +2042,8 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
}
se->conn.proto_major = arg->major;
se->conn.proto_minor = arg->minor;
- se->conn.capable = 0;
- se->conn.want = 0;
+ se->conn.capable_ext = 0;
+ se->conn.want_ext = 0;
memset(&outarg, 0, sizeof(outarg));
outarg.major = FUSE_KERNEL_VERSION;
@@ -2048,45 +2069,45 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
if (inargflags & FUSE_INIT_EXT)
inargflags = inargflags | (uint64_t) arg->flags2 << 32;
if (inargflags & FUSE_ASYNC_READ)
- se->conn.capable |= FUSE_CAP_ASYNC_READ;
+ se->conn.capable_ext |= FUSE_CAP_ASYNC_READ;
if (inargflags & FUSE_POSIX_LOCKS)
- se->conn.capable |= FUSE_CAP_POSIX_LOCKS;
+ se->conn.capable_ext |= FUSE_CAP_POSIX_LOCKS;
if (inargflags & FUSE_ATOMIC_O_TRUNC)
- se->conn.capable |= FUSE_CAP_ATOMIC_O_TRUNC;
+ se->conn.capable_ext |= FUSE_CAP_ATOMIC_O_TRUNC;
if (inargflags & FUSE_EXPORT_SUPPORT)
- se->conn.capable |= FUSE_CAP_EXPORT_SUPPORT;
+ se->conn.capable_ext |= FUSE_CAP_EXPORT_SUPPORT;
if (inargflags & FUSE_DONT_MASK)
- se->conn.capable |= FUSE_CAP_DONT_MASK;
+ se->conn.capable_ext |= FUSE_CAP_DONT_MASK;
if (inargflags & FUSE_FLOCK_LOCKS)
- se->conn.capable |= FUSE_CAP_FLOCK_LOCKS;
+ se->conn.capable_ext |= FUSE_CAP_FLOCK_LOCKS;
if (inargflags & FUSE_AUTO_INVAL_DATA)
- se->conn.capable |= FUSE_CAP_AUTO_INVAL_DATA;
+ se->conn.capable_ext |= FUSE_CAP_AUTO_INVAL_DATA;
if (inargflags & FUSE_DO_READDIRPLUS)
- se->conn.capable |= FUSE_CAP_READDIRPLUS;
+ se->conn.capable_ext |= FUSE_CAP_READDIRPLUS;
if (inargflags & FUSE_READDIRPLUS_AUTO)
- se->conn.capable |= FUSE_CAP_READDIRPLUS_AUTO;
+ se->conn.capable_ext |= FUSE_CAP_READDIRPLUS_AUTO;
if (inargflags & FUSE_ASYNC_DIO)
- se->conn.capable |= FUSE_CAP_ASYNC_DIO;
+ se->conn.capable_ext |= FUSE_CAP_ASYNC_DIO;
if (inargflags & FUSE_WRITEBACK_CACHE)
- se->conn.capable |= FUSE_CAP_WRITEBACK_CACHE;
+ se->conn.capable_ext |= FUSE_CAP_WRITEBACK_CACHE;
if (inargflags & FUSE_NO_OPEN_SUPPORT)
- se->conn.capable |= FUSE_CAP_NO_OPEN_SUPPORT;
+ se->conn.capable_ext |= FUSE_CAP_NO_OPEN_SUPPORT;
if (inargflags & FUSE_PARALLEL_DIROPS)
- se->conn.capable |= FUSE_CAP_PARALLEL_DIROPS;
+ se->conn.capable_ext |= FUSE_CAP_PARALLEL_DIROPS;
if (inargflags & FUSE_POSIX_ACL)
- se->conn.capable |= FUSE_CAP_POSIX_ACL;
+ se->conn.capable_ext |= FUSE_CAP_POSIX_ACL;
if (inargflags & FUSE_HANDLE_KILLPRIV)
- se->conn.capable |= FUSE_CAP_HANDLE_KILLPRIV;
+ se->conn.capable_ext |= FUSE_CAP_HANDLE_KILLPRIV;
if (inargflags & FUSE_HANDLE_KILLPRIV_V2)
- se->conn.capable |= FUSE_CAP_HANDLE_KILLPRIV_V2;
+ se->conn.capable_ext |= FUSE_CAP_HANDLE_KILLPRIV_V2;
if (inargflags & FUSE_CACHE_SYMLINKS)
- se->conn.capable |= FUSE_CAP_CACHE_SYMLINKS;
+ se->conn.capable_ext |= FUSE_CAP_CACHE_SYMLINKS;
if (inargflags & FUSE_NO_OPENDIR_SUPPORT)
- se->conn.capable |= FUSE_CAP_NO_OPENDIR_SUPPORT;
+ se->conn.capable_ext |= FUSE_CAP_NO_OPENDIR_SUPPORT;
if (inargflags & FUSE_EXPLICIT_INVAL_DATA)
- se->conn.capable |= FUSE_CAP_EXPLICIT_INVAL_DATA;
+ se->conn.capable_ext |= FUSE_CAP_EXPLICIT_INVAL_DATA;
if (inargflags & FUSE_SETXATTR_EXT)
- se->conn.capable |= FUSE_CAP_SETXATTR_EXT;
+ se->conn.capable_ext |= FUSE_CAP_SETXATTR_EXT;
if (!(inargflags & FUSE_MAX_PAGES)) {
size_t max_bufsize =
FUSE_DEFAULT_MAX_PAGES_PER_REQ * getpagesize()
@@ -2097,13 +2118,13 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
buf_reallocable = false;
}
if (inargflags & FUSE_DIRECT_IO_ALLOW_MMAP)
- se->conn.capable |= FUSE_CAP_DIRECT_IO_ALLOW_MMAP;
+ se->conn.capable_ext |= FUSE_CAP_DIRECT_IO_ALLOW_MMAP;
if (arg->minor >= 38 || (inargflags & FUSE_HAS_EXPIRE_ONLY))
- se->conn.capable |= FUSE_CAP_EXPIRE_ONLY;
+ se->conn.capable_ext |= FUSE_CAP_EXPIRE_ONLY;
if (inargflags & FUSE_PASSTHROUGH)
- se->conn.capable |= FUSE_CAP_PASSTHROUGH;
+ se->conn.capable_ext |= FUSE_CAP_PASSTHROUGH;
if (inargflags & FUSE_NO_EXPORT_SUPPORT)
- se->conn.capable |= FUSE_CAP_NO_EXPORT_SUPPORT;
+ se->conn.capable_ext |= FUSE_CAP_NO_EXPORT_SUPPORT;
} else {
se->conn.max_readahead = 0;
}
@@ -2112,16 +2133,17 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
#ifdef HAVE_SPLICE
#ifdef HAVE_VMSPLICE
if ((se->io == NULL) || (se->io->splice_send != NULL)) {
- se->conn.capable |= FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE;
+ se->conn.capable_ext |= FUSE_CAP_SPLICE_WRITE |
+ FUSE_CAP_SPLICE_MOVE;
}
#endif
if ((se->io == NULL) || (se->io->splice_receive != NULL)) {
- se->conn.capable |= FUSE_CAP_SPLICE_READ;
+ se->conn.capable_ext |= FUSE_CAP_SPLICE_READ;
}
#endif
}
if (se->conn.proto_minor >= 18)
- se->conn.capable |= FUSE_CAP_IOCTL_DIR;
+ se->conn.capable_ext |= FUSE_CAP_IOCTL_DIR;
/* Default settings for modern filesystems.
*
@@ -2130,9 +2152,10 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
* we can finally enable them by default (as long as they're
* supported by the kernel).
*/
-#define LL_SET_DEFAULT(cond, cap) \
- if ((cond) && (se->conn.capable & (cap))) \
- se->conn.want |= (cap)
+#define LL_SET_DEFAULT(cond, cap) \
+ if ((cond)) \
+ fuse_set_feature_flag(&se->conn, cap)
+
LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_READ);
LL_SET_DEFAULT(1, FUSE_CAP_AUTO_INVAL_DATA);
LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_DIO);
@@ -2154,10 +2177,31 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
se->conn.time_gran = 1;
se->got_init = 1;
- if (se->op.init)
+ if (se->op.init) {
+ uint32_t want_ext_default = se->conn.want_ext;
+ int rc;
+
+ // Apply the first 32 bits of capable_ext to capable
+ se->conn.capable =
+ (uint32_t)(se->conn.capable_ext & 0xFFFFFFFF);
+
se->op.init(se->userdata, &se->conn);
- if (!want_flags_valid(se->conn.capable, se->conn.want)) {
+ /*
+ * se->conn.want is 32-bit value and deprecated in favour of
+ * se->conn.want_ext
+ * Userspace might still use conn.want - we need to convert it
+ */
+ rc = convert_to_conn_want_ext(&se->conn, want_ext_default);
+ if (rc != 0) {
+ fuse_reply_err(req, EPROTO);
+ se->error = -EPROTO;
+ fuse_session_exit(se);
+ return;
+ }
+ }
+
+ if (!want_flags_valid(se->conn.capable_ext, se->conn.want_ext)) {
fuse_reply_err(req, EPROTO);
se->error = -EPROTO;
fuse_session_exit(se);
@@ -2196,45 +2240,45 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
by the max_write option */
outargflags |= FUSE_BIG_WRITES;
- if (se->conn.want & FUSE_CAP_ASYNC_READ)
+ if (se->conn.want_ext & FUSE_CAP_ASYNC_READ)
outargflags |= FUSE_ASYNC_READ;
- if (se->conn.want & FUSE_CAP_POSIX_LOCKS)
+ if (se->conn.want_ext & FUSE_CAP_POSIX_LOCKS)
outargflags |= FUSE_POSIX_LOCKS;
- if (se->conn.want & FUSE_CAP_ATOMIC_O_TRUNC)
+ if (se->conn.want_ext & FUSE_CAP_ATOMIC_O_TRUNC)
outargflags |= FUSE_ATOMIC_O_TRUNC;
- if (se->conn.want & FUSE_CAP_EXPORT_SUPPORT)
+ if (se->conn.want_ext & FUSE_CAP_EXPORT_SUPPORT)
outargflags |= FUSE_EXPORT_SUPPORT;
- if (se->conn.want & FUSE_CAP_DONT_MASK)
+ if (se->conn.want_ext & FUSE_CAP_DONT_MASK)
outargflags |= FUSE_DONT_MASK;
- if (se->conn.want & FUSE_CAP_FLOCK_LOCKS)
+ if (se->conn.want_ext & FUSE_CAP_FLOCK_LOCKS)
outargflags |= FUSE_FLOCK_LOCKS;
- if (se->conn.want & FUSE_CAP_AUTO_INVAL_DATA)
+ if (se->conn.want_ext & FUSE_CAP_AUTO_INVAL_DATA)
outargflags |= FUSE_AUTO_INVAL_DATA;
- if (se->conn.want & FUSE_CAP_READDIRPLUS)
+ if (se->conn.want_ext & FUSE_CAP_READDIRPLUS)
outargflags |= FUSE_DO_READDIRPLUS;
- if (se->conn.want & FUSE_CAP_READDIRPLUS_AUTO)
+ if (se->conn.want_ext & FUSE_CAP_READDIRPLUS_AUTO)
outargflags |= FUSE_READDIRPLUS_AUTO;
- if (se->conn.want & FUSE_CAP_ASYNC_DIO)
+ if (se->conn.want_ext & FUSE_CAP_ASYNC_DIO)
outargflags |= FUSE_ASYNC_DIO;
- if (se->conn.want & FUSE_CAP_WRITEBACK_CACHE)
+ if (se->conn.want_ext & FUSE_CAP_WRITEBACK_CACHE)
outargflags |= FUSE_WRITEBACK_CACHE;
- if (se->conn.want & FUSE_CAP_PARALLEL_DIROPS)
+ if (se->conn.want_ext & FUSE_CAP_PARALLEL_DIROPS)
outargflags |= FUSE_PARALLEL_DIROPS;
- if (se->conn.want & FUSE_CAP_POSIX_ACL)
+ if (se->conn.want_ext & FUSE_CAP_POSIX_ACL)
outargflags |= FUSE_POSIX_ACL;
- if (se->conn.want & FUSE_CAP_HANDLE_KILLPRIV)
+ if (se->conn.want_ext & FUSE_CAP_HANDLE_KILLPRIV)
outargflags |= FUSE_HANDLE_KILLPRIV;
- if (se->conn.want & FUSE_CAP_HANDLE_KILLPRIV_V2)
+ if (se->conn.want_ext & FUSE_CAP_HANDLE_KILLPRIV_V2)
outargflags |= FUSE_HANDLE_KILLPRIV_V2;
- if (se->conn.want & FUSE_CAP_CACHE_SYMLINKS)
+ if (se->conn.want_ext & FUSE_CAP_CACHE_SYMLINKS)
outargflags |= FUSE_CACHE_SYMLINKS;
- if (se->conn.want & FUSE_CAP_EXPLICIT_INVAL_DATA)
+ if (se->conn.want_ext & FUSE_CAP_EXPLICIT_INVAL_DATA)
outargflags |= FUSE_EXPLICIT_INVAL_DATA;
- if (se->conn.want & FUSE_CAP_SETXATTR_EXT)
+ if (se->conn.want_ext & FUSE_CAP_SETXATTR_EXT)
outargflags |= FUSE_SETXATTR_EXT;
- if (se->conn.want & FUSE_CAP_DIRECT_IO_ALLOW_MMAP)
+ if (se->conn.want_ext & FUSE_CAP_DIRECT_IO_ALLOW_MMAP)
outargflags |= FUSE_DIRECT_IO_ALLOW_MMAP;
- if (se->conn.want & FUSE_CAP_PASSTHROUGH) {
+ if (se->conn.want_ext & FUSE_CAP_PASSTHROUGH) {
outargflags |= FUSE_PASSTHROUGH;
/*
* outarg.max_stack_depth includes the fuse stack layer,
@@ -2242,7 +2286,7 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
*/
outarg.max_stack_depth = se->conn.max_backing_stack_depth + 1;
}
- if (se->conn.want & FUSE_CAP_NO_EXPORT_SUPPORT)
+ if (se->conn.want_ext & FUSE_CAP_NO_EXPORT_SUPPORT)
outargflags |= FUSE_NO_EXPORT_SUPPORT;
if (inargflags & FUSE_INIT_EXT) {
@@ -2282,7 +2326,7 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
outarg.congestion_threshold);
fuse_log(FUSE_LOG_DEBUG, " time_gran=%u\n",
outarg.time_gran);
- if (se->conn.want & FUSE_CAP_PASSTHROUGH)
+ if (se->conn.want_ext & FUSE_CAP_PASSTHROUGH)
fuse_log(FUSE_LOG_DEBUG, " max_stack_depth=%u\n",
outarg.max_stack_depth);
}
@@ -2467,7 +2511,7 @@ int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent
if (!se)
return -EINVAL;
- if (!(se->conn.capable & FUSE_CAP_EXPIRE_ONLY))
+ if (!(se->conn.capable_ext & FUSE_CAP_EXPIRE_ONLY))
return -ENOSYS;
return fuse_lowlevel_notify_entry(se, parent, name, namelen, FUSE_LL_EXPIRE_ONLY);
@@ -2712,7 +2756,12 @@ static struct {
[CUSE_INIT] = { cuse_lowlevel_init, "CUSE_INIT" },
};
-#define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0]))
+/*
+ * For ABI compatibility we cannot allow higher values than CUSE_INIT.
+ * Without ABI compatibility we could use the size of the array.
+ * #define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0]))
+ */
+#define FUSE_MAXOP (CUSE_INIT + 1)
static const char *opname(enum fuse_opcode opcode)
{
@@ -3001,7 +3050,7 @@ static int _fuse_session_receive_buf(struct fuse_session *se,
struct fuse_buf tmpbuf;
if (se->conn.proto_minor < 14 ||
- !(se->conn.want & FUSE_CAP_SPLICE_READ))
+ !(se->conn.want_ext & FUSE_CAP_SPLICE_READ))
goto fallback;
llp = fuse_ll_get_pipe(se);
diff --git a/lib/meson.build b/lib/meson.build
index 34dd607..e201a5a 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -34,7 +34,7 @@ endif
fusermount_path = join_paths(get_option('prefix'), get_option('bindir'))
libfuse = library('fuse3', libfuse_sources, version: meson.project_version(),
- soversion: '3', include_directories: include_dirs,
+ soversion: '4', include_directories: include_dirs,
dependencies: deps, install: true,
link_depends: 'fuse_versionscript',
c_args: [ '-DFUSE_USE_VERSION=317',
diff --git a/test/test_write_cache.c b/test/test_write_cache.c
index cc827c7..d3c7af0 100644
--- a/test/test_write_cache.c
+++ b/test/test_write_cache.c
@@ -65,7 +65,7 @@ static void tfs_init (void *userdata, struct fuse_conn_info *conn)
(void) userdata;
if(options.writeback) {
- assert(conn->capable & FUSE_CAP_WRITEBACK_CACHE);
+ assert(fuse_get_feature_flag(conn, FUSE_CAP_WRITEBACK_CACHE));
conn->want |= FUSE_CAP_WRITEBACK_CACHE;
}
}