aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/fuse.c33
-rw-r--r--lib/fuse_i.h34
-rw-r--r--lib/fuse_lowlevel.c30
-rw-r--r--lib/helper.c4
-rw-r--r--lib/util.h27
5 files changed, 99 insertions, 29 deletions
diff --git a/lib/fuse.c b/lib/fuse.c
index 9335429..136f0c2 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -10,6 +10,8 @@
*/
#define _GNU_SOURCE
+#include "fuse.h"
+#include <pthread.h>
#include "fuse_config.h"
#include "fuse_i.h"
@@ -17,7 +19,9 @@
#include "fuse_opt.h"
#include "fuse_misc.h"
#include "fuse_kernel.h"
+#include "util.h"
+#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
@@ -2606,13 +2610,34 @@ void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn,
{
fuse_get_context()->private_data = fs->user_data;
if (!fs->op.write_buf)
- conn->want &= ~FUSE_CAP_SPLICE_READ;
+ fuse_unset_feature_flag(conn, FUSE_CAP_SPLICE_READ);
if (!fs->op.lock)
- conn->want &= ~FUSE_CAP_POSIX_LOCKS;
+ fuse_unset_feature_flag(conn, FUSE_CAP_POSIX_LOCKS);
if (!fs->op.flock)
- conn->want &= ~FUSE_CAP_FLOCK_LOCKS;
- if (fs->op.init)
+ fuse_unset_feature_flag(conn, FUSE_CAP_FLOCK_LOCKS);
+ if (fs->op.init) {
+ uint64_t want_ext_default = conn->want_ext;
+ uint32_t want_default = fuse_lower_32_bits(conn->want_ext);
+ int rc;
+
+ conn->want = want_default;
fs->user_data = fs->op.init(conn, cfg);
+
+ rc = convert_to_conn_want_ext(conn, want_ext_default,
+ want_default);
+
+ if (rc != 0) {
+ /*
+ * This is a grave developer error, but
+ * we cannot return an error here, as the function
+ * signature does not allow it.
+ */
+ fuse_log(
+ FUSE_LOG_ERR,
+ "fuse: Aborting due to invalid conn want flags.\n");
+ _exit(EXIT_FAILURE);
+ }
+ }
}
static int fuse_init_intr_signal(int signum, int *installed);
diff --git a/lib/fuse_i.h b/lib/fuse_i.h
index ea04c34..6fbfc2d 100644
--- a/lib/fuse_i.h
+++ b/lib/fuse_i.h
@@ -8,8 +8,11 @@
#include "fuse.h"
#include "fuse_lowlevel.h"
+#include "util.h"
+#include <stdint.h>
#include <stdbool.h>
+#include <errno.h>
#define MIN(a, b) \
({ \
@@ -222,3 +225,34 @@ int fuse_loop_cfg_verify(struct fuse_loop_config *config);
/* room needed in buffer to accommodate header */
#define FUSE_BUFFER_HEADER_SIZE 0x1000
+/**
+ * Get the wanted capability flags, converting from old format if necessary
+ */
+static inline int convert_to_conn_want_ext(struct fuse_conn_info *conn,
+ uint64_t want_ext_default,
+ uint32_t want_default)
+{
+ /*
+ * Convert want to want_ext if necessary.
+ * For the high level interface this function might be called
+ * twice, once from the high level interface and once from the
+ * low level interface. Both, with different want_ext_default and
+ * want_default values. In order to suppress a failure for the
+ * second call, we check if the lower 32 bits of want_ext are
+ * already set to the value of want.
+ */
+ if (conn->want != want_default &&
+ fuse_lower_32_bits(conn->want_ext) != conn->want) {
+ if (conn->want_ext != want_ext_default) {
+ fuse_log(FUSE_LOG_ERR,
+ "fuse: both 'want' and 'want_ext' are set\n");
+ return -EINVAL;
+ }
+
+ /* high bits from want_ext, low bits from want */
+ conn->want_ext = fuse_higher_32_bits(conn->want_ext) |
+ conn->want;
+ }
+
+ return 0;
+}
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
index d650944..c22b4a2 100644
--- a/lib/fuse_lowlevel.c
+++ b/lib/fuse_lowlevel.c
@@ -9,7 +9,6 @@
See the file COPYING.LIB
*/
-#include <stdbool.h>
#define _GNU_SOURCE
#include "fuse_config.h"
@@ -20,6 +19,8 @@
#include "mount_util.h"
#include "util.h"
+#include <stdint.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
@@ -1997,25 +1998,6 @@ static bool want_flags_valid(uint64_t capable, uint64_t want)
return true;
}
-/**
- * Get the wanted capability flags, converting from old format if necessary
- */
-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")))
@@ -2177,11 +2159,12 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
se->got_init = 1;
if (se->op.init) {
uint64_t want_ext_default = se->conn.want_ext;
+ uint32_t want_default = fuse_lower_32_bits(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->conn.capable = fuse_lower_32_bits(se->conn.capable_ext);
+ se->conn.want = want_default;
se->op.init(se->userdata, &se->conn);
@@ -2190,7 +2173,8 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
* 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);
+ rc = convert_to_conn_want_ext(&se->conn, want_ext_default,
+ want_default);
if (rc != 0) {
fuse_reply_err(req, EPROTO);
se->error = -EPROTO;
diff --git a/lib/helper.c b/lib/helper.c
index a7b2fe0..59dd488 100644
--- a/lib/helper.c
+++ b/lib/helper.c
@@ -424,9 +424,9 @@ void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts,
conn->max_readahead = opts->max_readahead;
#define LL_ENABLE(cond,cap) \
- if (cond) conn->want |= (cap)
+ if (cond) conn->want_ext |= (cap)
#define LL_DISABLE(cond,cap) \
- if (cond) conn->want &= ~(cap)
+ if (cond) conn->want_ext &= ~(cap)
LL_ENABLE(opts->splice_read, FUSE_CAP_SPLICE_READ);
LL_DISABLE(opts->no_splice_read, FUSE_CAP_SPLICE_READ);
diff --git a/lib/util.h b/lib/util.h
index 74ce748..0c4c258 100644
--- a/lib/util.h
+++ b/lib/util.h
@@ -1,3 +1,30 @@
+#ifndef FUSE_UTIL_H_
+#define FUSE_UTIL_H_
+
+#include <stdint.h>
+
#define ROUND_UP(val, round_to) (((val) + (round_to - 1)) & ~(round_to - 1))
int libfuse_strtol(const char *str, long *res);
+
+/**
+ * Return the low bits of a number
+ */
+static inline uint32_t fuse_lower_32_bits(uint64_t nr)
+{
+ return (uint32_t)(nr & 0xffffffff);
+}
+
+/**
+ * Return the high bits of a number
+ */
+static inline uint64_t fuse_higher_32_bits(uint64_t nr)
+{
+ return nr & ~0xffffffffULL;
+}
+
+#ifndef FUSE_VAR_UNUSED
+#define FUSE_VAR_UNUSED(var) (__attribute__((unused)) var)
+#endif
+
+#endif