conn->want conversion: Fix fuse_apply_conn_info_opts()

fuse_apply_conn_info_opts() was applying to 'want_ext',
which would cause conflicts with 'want' if the application
applied its own flags to 'conn->want'.

Solution is:
    - to move fuse_{set,unset,get}_feature_flag and
      convert_to_conn_want_ext() to fuse_lowlevel.c and
      to define them as part of the public API, although
      convert_to_conn_want_ext() should not be used - it is
      currently needed to be a public function due as it needs
      to be defined for the tests.

Related to https://github.com/libfuse/libfuse/issues/1171 and
https://github.com/libfuse/libfuse/pull/1172.

Closes: https://github.com/libfuse/libfuse/issues/1171
Signed-off-by: Bernd Schubert <bschubert@ddn.com>
This commit is contained in:
Bernd Schubert
2025-05-18 00:24:07 +02:00
committed by Bernd Schubert
parent eadd6a5454
commit baadab0492
9 changed files with 187 additions and 106 deletions
+57 -40
View File
@@ -1,16 +1,22 @@
#include "util.h"
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 17)
#include "util.h"
#include "fuse_i.h"
#include "fuse_lowlevel.h"
#include <stdio.h>
#include <assert.h>
#include <inttypes.h>
#include <stdbool.h>
#include <err.h>
static void print_conn_info(const char *prefix, struct fuse_conn_info *conn)
{
printf("%s: want=0x%" PRIx32 " want_ext=0x%" PRIx64 "\n", prefix,
conn->want, conn->want_ext);
struct fuse_session *se = container_of(conn, struct fuse_session, conn);
printf("%s: want=0x%" PRIx32 " want_ext=0x%" PRIx64
" want_default=0x%" PRIx32 " want_ext_default=0x%" PRIx64 "\n",
prefix, conn->want, conn->want_ext, se->conn_want,
se->conn_want_ext);
}
static void application_init_old_style(struct fuse_conn_info *conn)
@@ -18,33 +24,31 @@ static void application_init_old_style(struct fuse_conn_info *conn)
/* Simulate application init the old style */
conn->want |= FUSE_CAP_ASYNC_READ;
conn->want &= ~FUSE_CAP_SPLICE_READ;
/*
* Also use new style API, as that might happen through
* fuse_apply_conn_info_opts()
*/
fuse_set_feature_flag(conn, FUSE_CAP_IOCTL_DIR);
}
static void application_init_new_style(struct fuse_conn_info *conn)
{
/* Simulate application init the new style */
fuse_set_feature_flag(conn, FUSE_CAP_ASYNC_READ);
fuse_set_feature_flag(conn, FUSE_CAP_IOCTL_DIR);
fuse_unset_feature_flag(conn, FUSE_CAP_SPLICE_READ);
}
static void test_fuse_fs_init(struct fuse_conn_info *conn, bool new_style)
{
uint64_t want_ext_default = conn->want_ext;
uint32_t want_default = fuse_lower_32_bits(conn->want_ext);
int rc;
/* High-level init */
fuse_set_feature_flag(conn, FUSE_CAP_EXPORT_SUPPORT);
conn->want = want_default;
if (new_style)
application_init_new_style(conn);
else
application_init_old_style(conn);
rc = convert_to_conn_want_ext(conn, want_ext_default, want_default);
assert(rc == 0);
}
static void test_do_init(struct fuse_conn_info *conn, bool new_style)
@@ -53,49 +57,71 @@ static void test_do_init(struct fuse_conn_info *conn, bool new_style)
conn->capable_ext = FUSE_CAP_SPLICE_READ | FUSE_CAP_SPLICE_WRITE |
FUSE_CAP_SPLICE_MOVE | FUSE_CAP_POSIX_LOCKS |
FUSE_CAP_FLOCK_LOCKS | FUSE_CAP_EXPORT_SUPPORT |
FUSE_CAP_ASYNC_READ;
FUSE_CAP_ASYNC_READ | FUSE_CAP_IOCTL_DIR;
conn->capable = fuse_lower_32_bits(conn->capable_ext);
conn->want_ext = conn->capable_ext;
fuse_set_feature_flag(conn, FUSE_CAP_SPLICE_READ |
FUSE_CAP_SPLICE_WRITE |
FUSE_CAP_SPLICE_MOVE);
print_conn_info("Initial state", conn);
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;
conn->capable = fuse_lower_32_bits(conn->capable_ext);
test_fuse_fs_init(conn, new_style);
print_conn_info("After init", conn);
rc = convert_to_conn_want_ext(conn, want_ext_default, want_default);
rc = fuse_convert_to_conn_want_ext(conn);
assert(rc == 0);
/* Verify all expected flags are set */
assert(!(conn->want_ext & FUSE_CAP_SPLICE_READ));
assert(conn->want_ext & FUSE_CAP_SPLICE_WRITE);
assert(conn->want_ext & FUSE_CAP_SPLICE_MOVE);
assert(conn->want_ext & FUSE_CAP_POSIX_LOCKS);
assert(conn->want_ext & FUSE_CAP_FLOCK_LOCKS);
assert(conn->want_ext & FUSE_CAP_EXPORT_SUPPORT);
assert(conn->want_ext & FUSE_CAP_ASYNC_READ);
assert(conn->want_ext & FUSE_CAP_IOCTL_DIR);
/* Verify no other flags are set */
assert(conn->want_ext ==
(FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE |
FUSE_CAP_POSIX_LOCKS | FUSE_CAP_FLOCK_LOCKS |
FUSE_CAP_EXPORT_SUPPORT | FUSE_CAP_ASYNC_READ));
FUSE_CAP_EXPORT_SUPPORT | FUSE_CAP_ASYNC_READ |
FUSE_CAP_IOCTL_DIR));
print_conn_info("After init", conn);
}
static void test_want_conversion_basic(void)
{
struct fuse_conn_info conn = { 0 };
const struct fuse_lowlevel_ops ops = { 0 };
struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
struct fuse_session *se;
struct fuse_conn_info *conn;
/* Add the program name to arg[0] */
if (fuse_opt_add_arg(&args, "test_signals")) {
fprintf(stderr, "Failed to add argument\n");
errx(1, "Failed to add argument");
}
se = fuse_session_new(&args, &ops, sizeof(ops), NULL);
assert(se);
conn = &se->conn;
printf("\nTesting basic want conversion, old style:\n");
test_do_init(conn, false);
fuse_session_destroy(se);
se = fuse_session_new(&args, &ops, sizeof(ops), NULL);
assert(se);
conn = &se->conn;
printf("\nTesting basic want conversion, new style:\n");
test_do_init(conn, true);
print_conn_info("After init", conn);
fuse_session_destroy(se);
fuse_opt_free_args(&args);
printf("\nTesting basic want conversion:\n");
test_do_init(&conn, false);
test_do_init(&conn, true);
print_conn_info("After init", &conn);
}
static void test_want_conversion_conflict(void)
@@ -115,16 +141,11 @@ static void test_want_conversion_conflict(void)
conn.want = fuse_lower_32_bits(conn.want_ext);
print_conn_info("Test conflict initial", &conn);
/* Initialize default values like in basic test */
uint64_t want_ext_default_ll = conn.want_ext;
uint32_t want_default_ll = fuse_lower_32_bits(want_ext_default_ll);
/* Simulate application init modifying capabilities */
conn.want_ext |= FUSE_CAP_ATOMIC_O_TRUNC; /* Add new capability */
conn.want &= ~FUSE_CAP_SPLICE_READ; /* Remove a capability */
rc = convert_to_conn_want_ext(&conn, want_ext_default_ll,
want_default_ll);
rc = fuse_convert_to_conn_want_ext(&conn);
assert(rc == -EINVAL);
print_conn_info("Test conflict after", &conn);
@@ -143,11 +164,7 @@ static void test_want_conversion_high_bits(void)
conn.want = fuse_lower_32_bits(conn.want_ext);
print_conn_info("Test high bits initial", &conn);
uint64_t want_ext_default_ll = conn.want_ext;
uint32_t want_default_ll = fuse_lower_32_bits(want_ext_default_ll);
rc = convert_to_conn_want_ext(&conn, want_ext_default_ll,
want_default_ll);
rc = fuse_convert_to_conn_want_ext(&conn);
assert(rc == 0);
assert(conn.want_ext == ((1ULL << 33) | FUSE_CAP_ASYNC_READ));
print_conn_info("Test high bits after", &conn);