diff options
author | Bernd Schubert <bernd@bsbernd.com> | 2025-03-22 23:57:55 +0100 |
---|---|---|
committer | Bernd Schubert <bernd@bsbernd.com> | 2025-03-24 09:38:06 +0100 |
commit | f68970cd235a7e14026ca0f6240428bbebe8223b (patch) | |
tree | c0a9821396c2f4b4b8a4e3636bf57196a31e85f0 /test/test_want_conversion.c | |
parent | 5d1c49e54ca226f144b618a1623117e2a52e7e17 (diff) | |
download | libfuse-f68970cd235a7e14026ca0f6240428bbebe8223b.tar.gz |
fuse: Fix want flag conversion
32-bit conn->want flags been left to be ABI compatible to 3.10,
even though the so version was changed.
The more recent way is to use fuse_set_feature_flag(),
which will use conn->want_ext.
Given that we now have two flags (want and want_ext), we need
to convert and that brought several issues
- If the application sets conn->want, that needs to be set into
the lower 32 bit of conn->want_ext. As the application might
actually unset values, it really has to be a copy and not
just 'or' - fixed now.
- convert_to_conn_want_ext() actually needs to check for
_modified_ conn->want and conn->want_ext
- convert_to_conn_want_ext() must consider being called from
high and lowlevel interfact, with different want_ext_default
and want_default values. It is only a failure, if the application
changed both, conn->want and conn->want_ext. This function
was failing in issue #1171, because high level fuse_fs_init()
was changing values and then lowlevel do_init() was incorrectly
failing on that.
This also adds a new test (test_want_conversion) and sets
values into example/{hello.c,hello_ll.c}
Also some more internal users of conn->want are converted to
fuse_{set,unset}_feature_flag().
Closes: https://github.com/libfuse/libfuse/issues/1171
Signed-off-by: Bernd Schubert <bernd@bsbernd.com>
Diffstat (limited to 'test/test_want_conversion.c')
-rw-r--r-- | test/test_want_conversion.c | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/test/test_want_conversion.c b/test/test_want_conversion.c new file mode 100644 index 0000000..935b58d --- /dev/null +++ b/test/test_want_conversion.c @@ -0,0 +1,152 @@ +#include "util.h" +#include <string.h> +#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 17) + +#include "fuse_i.h" +#include <stdio.h> +#include <assert.h> +#include <inttypes.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); +} + +static void application_init(struct fuse_conn_info *conn) +{ + /* Simulate application init */ + conn->want |= FUSE_CAP_ASYNC_READ; + conn->want &= ~FUSE_CAP_SPLICE_READ; +} + +static void test_fuse_fs_init(struct fuse_conn_info *conn) +{ + 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; + + application_init(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) +{ + /* Initial setup */ + 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; + conn->capable = fuse_lower_32_bits(conn->capable_ext); + conn->want_ext = conn->capable_ext; + + 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); + + rc = convert_to_conn_want_ext(conn, want_ext_default, want_default); + 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); + /* 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)); + + print_conn_info("After init", conn); +} + +static void test_want_conversion_basic(void) +{ + struct fuse_conn_info conn = { 0 }; + + printf("\nTesting basic want conversion:\n"); + test_do_init(&conn); + print_conn_info("After init", &conn); +} + +static void test_want_conversion_conflict(void) +{ + struct fuse_conn_info conn = { 0 }; + int rc; + + printf("\nTesting want conversion conflict:\n"); + + /* Test conflicting values */ + /* Initialize like fuse_lowlevel.c does */ + conn.capable_ext = FUSE_CAP_SPLICE_READ | FUSE_CAP_SPLICE_WRITE | + FUSE_CAP_SPLICE_MOVE | FUSE_CAP_POSIX_LOCKS | + FUSE_CAP_FLOCK_LOCKS; + conn.capable = fuse_lower_32_bits(conn.capable_ext); + conn.want_ext = conn.capable_ext; + 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); + assert(rc == -EINVAL); + print_conn_info("Test conflict after", &conn); + + printf("Want conversion conflict test passed\n"); +} + +static void test_want_conversion_high_bits(void) +{ + struct fuse_conn_info conn = { 0 }; + int rc; + + printf("\nTesting want conversion high bits preservation:\n"); + + /* Test high bits preservation */ + conn.want_ext = (1ULL << 33) | FUSE_CAP_ASYNC_READ; + 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); + assert(rc == 0); + assert(conn.want_ext == ((1ULL << 33) | FUSE_CAP_ASYNC_READ)); + print_conn_info("Test high bits after", &conn); + + printf("Want conversion high bits test passed\n"); +} + +int main(void) +{ + test_want_conversion_basic(); + test_want_conversion_conflict(); + test_want_conversion_high_bits(); + return 0; +} |