aboutsummaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
authorBernd Schubert <bernd@bsbernd.com>2025-03-22 23:57:55 +0100
committerBernd Schubert <bernd@bsbernd.com>2025-03-24 10:29:15 +0100
commitf52f71df3373185c4d18414aad57c4e8995a4393 (patch)
treec29e5167670efc2d399e00cece46fc03e62b0c20 /test
parentcdad7b6001036f1a33bddab59558eec099bcb278 (diff)
downloadlibfuse-f52f71df3373185c4d18414aad57c4e8995a4393.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> (cherry picked from commit f68970cd235a7e14026ca0f6240428bbebe8223b)
Diffstat (limited to 'test')
-rw-r--r--test/meson.build3
-rw-r--r--test/test_want_conversion.c152
-rw-r--r--test/test_write_cache.c2
3 files changed, 156 insertions, 1 deletions
diff --git a/test/meson.build b/test/meson.build
index 3d74b9a..5997030 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -16,6 +16,9 @@ td += executable('readdir_inode', 'readdir_inode.c',
td += executable('release_unlink_race', 'release_unlink_race.c',
dependencies: [ libfuse_dep ],
install: false)
+td += executable('test_want_conversion', 'test_want_conversion.c',
+ dependencies: [ libfuse_dep ],
+ install: false)
test_scripts = [ 'conftest.py', 'pytest.ini', 'test_examples.py',
'util.py', 'test_ctests.py', 'test_custom_io.py' ]
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;
+}
diff --git a/test/test_write_cache.c b/test/test_write_cache.c
index 0c8fa7e..9f21f02 100644
--- a/test/test_write_cache.c
+++ b/test/test_write_cache.c
@@ -69,7 +69,7 @@ static void tfs_init (void *userdata, struct fuse_conn_info *conn)
if(options.writeback) {
assert(fuse_get_feature_flag(conn, FUSE_CAP_WRITEBACK_CACHE));
- conn->want |= FUSE_CAP_WRITEBACK_CACHE;
+ fuse_set_feature_flag(conn, FUSE_CAP_WRITEBACK_CACHE);
}
}