diff --git a/support/fuse.h b/support/fuse.h index 581c83b08d..9e35105a57 100644 --- a/support/fuse.h +++ b/support/fuse.h @@ -97,6 +97,7 @@ void *support_fuse_cast_name_internal (struct fuse_in_header *, uint32_t, #define support_fuse_payload_type_SETATTR struct fuse_setattr_in #define support_fuse_payload_type_WRITE struct fuse_write_in #define support_fuse_payload_type_COPY_FILE_RANGE struct fuse_copy_file_range_in +#define support_fuse_payload_type_COPY_FILE_RANGE_64 struct fuse_copy_file_range_in #define support_fuse_cast(typ, inh) \ ((support_fuse_payload_type_##typ *) \ support_fuse_cast_internal ((inh), FUSE_##typ)) diff --git a/sysdeps/unix/sysv/linux/tst-copy_file_range-large.c b/sysdeps/unix/sysv/linux/tst-copy_file_range-large.c index e92f7d4557..4a323df539 100644 --- a/sysdeps/unix/sysv/linux/tst-copy_file_range-large.c +++ b/sysdeps/unix/sysv/linux/tst-copy_file_range-large.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -35,16 +36,32 @@ #include #include +static _Atomic bool fuse_has_copy_file_range_64 = false; +static const uint64_t file_size = 1LLU << 61; + +/* Node IDs for our test files. */ +enum { NODE_SOURCE = 2, NODE_DEST = 3 }; + +/* Verify this is a copy from source to dest, starting at + offset 0. */ +static void +verify_fuse_request (struct fuse_copy_file_range_in *p) +{ + TEST_COMPARE (p->fh_in, NODE_SOURCE); + TEST_COMPARE (p->nodeid_out, NODE_DEST); + TEST_COMPARE (p->off_in, 0); + TEST_COMPARE (p->off_out, 0); + TEST_VERIFY (p->len > 0); + TEST_VERIFY (p->len <= file_size); +} + static void fuse_thread (struct support_fuse *f, void *closure) { - /* Node IDs for our test files. */ - enum { NODE_SOURCE = 2, NODE_DEST = 3 }; /* A large size, so that the kernel does not fail the copy_file_range attempt before performing the FUSE callback. Only the source file size matters to the kernel, but both files use the same size for simplicity. */ - const uint64_t file_size = 1LLU << 61; struct fuse_in_header *inh; while ((inh = support_fuse_next (f)) != NULL) @@ -108,14 +125,7 @@ fuse_thread (struct support_fuse *f, void *closure) struct fuse_copy_file_range_in *p = support_fuse_cast (COPY_FILE_RANGE, inh); - /* Verify this is a copy from source to dest, starting at - offset 0. */ - TEST_COMPARE (p->fh_in, NODE_SOURCE); - TEST_COMPARE (p->nodeid_out, NODE_DEST); - TEST_COMPARE (p->off_in, 0); - TEST_COMPARE (p->off_out, 0); - TEST_VERIFY (p->len > 0); - TEST_VERIFY (p->len <= file_size); + verify_fuse_request (p); /* Pretend the copy succeeded. */ struct fuse_write_out out = { .size = p->len }; @@ -123,6 +133,23 @@ fuse_thread (struct support_fuse *f, void *closure) } break; + case FUSE_COPY_FILE_RANGE_64: + { + atomic_store (&fuse_has_copy_file_range_64, true); + + struct fuse_copy_file_range_in *p + = support_fuse_cast (COPY_FILE_RANGE_64, inh); + + verify_fuse_request (p); + + /* Pretend the copy succeeded. */ + struct fuse_copy_file_range_out out = { + .bytes_copied = p->len, + }; + support_fuse_reply (f, &out, sizeof (out)); + } + break; + case FUSE_FLUSH: support_fuse_reply_empty (f); break; @@ -170,10 +197,14 @@ test_size (struct support_fuse *f, off64_t size) FAIL_UNSUPPORTED ("copy_file_range not supported"); } + if (atomic_load (&fuse_has_copy_file_range_64)) + TEST_COMPARE (copied, size); + /* To avoid the negative return value in Linux versions 6.18 the size is - silently clamped to UINT_MAX & PAGE_MASK. Accept that return value + silently clamped to UINT_MAX & PAGE_MASK and the change has been + backported to stable kernel release series. Accept that return value too. See: - . + . We must AND the expression with SSIZE_MAX for 32-bit platforms where SSIZE_MAX is less than UINT_MAX. */ @@ -201,8 +232,11 @@ test_all_sizes (struct support_fuse *f) test_size (f, UINT_MAX + i); /* We would like to test larger values than UINT_MAX here, but they - do not work because the FUSE protocol uses uint32_t for the - copy_file_range result in struct fuse_write_out. */ + do not work if the FUSE protocol still uses uint32_t for the + copy_file_range result in struct fuse_write_out (on Linux < 6.18). */ + if (atomic_load (&fuse_has_copy_file_range_64)) + for (int i = -10; i <= 0; ++i) + test_size (f, file_size + i); } static void *