[PATCH 3/3] Linux: test sizes larger than UINT_MAX for copy_file_range
Adhemerval Zanella Netto
adhemerval.zanella@linaro.org
Thu Jan 8 16:04:33 GMT 2026
On 08/01/26 04:27, Xi Ruoyao wrote:
> If the kernel supports the COPY_FILE_RANGE_64 FUSE interface, we can
> safely tests the large size values.
>
> Signed-off-by: Xi Ruoyao <xry111@xry111.site>
LGTM, and it fixes the tst-copy_file_range-large on recent kernel.
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Tested-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
> ---
> support/fuse.h | 1 +
> .../sysv/linux/tst-copy_file_range-large.c | 64 ++++++++++++++-----
> 2 files changed, 50 insertions(+), 15 deletions(-)
>
> 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 <fcntl.h>
> #include <limits.h>
> #include <stdio.h>
> +#include <stdatomic.h>
> #include <string.h>
> #include <support/check.h>
> #include <support/fuse.h>
> @@ -35,16 +36,32 @@
> #include <sys/stat.h>
> #include <unistd.h>
>
> +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:
> - <https://github.com/torvalds/linux/commit/1e08938c3694f707bb165535df352ac97a8c75c9>.
> + <https://git.kernel.org/torvalds/c/1e08938c3694>.
> 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 *
More information about the Libc-alpha
mailing list