mirror of git://sourceware.org/git/glibc.git
Linux: test sizes larger than UINT_MAX for copy_file_range
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> Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org> Tested-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
This commit is contained in:
parent
ee77bb99b7
commit
bcf231ec71
|
|
@ -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_SETATTR struct fuse_setattr_in
|
||||||
#define support_fuse_payload_type_WRITE struct fuse_write_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 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) \
|
#define support_fuse_cast(typ, inh) \
|
||||||
((support_fuse_payload_type_##typ *) \
|
((support_fuse_payload_type_##typ *) \
|
||||||
support_fuse_cast_internal ((inh), FUSE_##typ))
|
support_fuse_cast_internal ((inh), FUSE_##typ))
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdatomic.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <support/check.h>
|
#include <support/check.h>
|
||||||
#include <support/fuse.h>
|
#include <support/fuse.h>
|
||||||
|
|
@ -35,16 +36,32 @@
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <unistd.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
|
static void
|
||||||
fuse_thread (struct support_fuse *f, void *closure)
|
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
|
/* A large size, so that the kernel does not fail the
|
||||||
copy_file_range attempt before performing the FUSE callback.
|
copy_file_range attempt before performing the FUSE callback.
|
||||||
Only the source file size matters to the kernel, but both files
|
Only the source file size matters to the kernel, but both files
|
||||||
use the same size for simplicity. */
|
use the same size for simplicity. */
|
||||||
const uint64_t file_size = 1LLU << 61;
|
|
||||||
|
|
||||||
struct fuse_in_header *inh;
|
struct fuse_in_header *inh;
|
||||||
while ((inh = support_fuse_next (f)) != NULL)
|
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
|
struct fuse_copy_file_range_in *p
|
||||||
= support_fuse_cast (COPY_FILE_RANGE, inh);
|
= support_fuse_cast (COPY_FILE_RANGE, inh);
|
||||||
|
|
||||||
/* Verify this is a copy from source to dest, starting at
|
verify_fuse_request (p);
|
||||||
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);
|
|
||||||
|
|
||||||
/* Pretend the copy succeeded. */
|
/* Pretend the copy succeeded. */
|
||||||
struct fuse_write_out out = { .size = p->len };
|
struct fuse_write_out out = { .size = p->len };
|
||||||
|
|
@ -123,6 +133,23 @@ fuse_thread (struct support_fuse *f, void *closure)
|
||||||
}
|
}
|
||||||
break;
|
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:
|
case FUSE_FLUSH:
|
||||||
support_fuse_reply_empty (f);
|
support_fuse_reply_empty (f);
|
||||||
break;
|
break;
|
||||||
|
|
@ -170,10 +197,14 @@ test_size (struct support_fuse *f, off64_t size)
|
||||||
FAIL_UNSUPPORTED ("copy_file_range not supported");
|
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
|
/* 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:
|
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
|
We must AND the expression with SSIZE_MAX for 32-bit platforms where
|
||||||
SSIZE_MAX is less than UINT_MAX.
|
SSIZE_MAX is less than UINT_MAX.
|
||||||
*/
|
*/
|
||||||
|
|
@ -201,8 +232,11 @@ test_all_sizes (struct support_fuse *f)
|
||||||
test_size (f, UINT_MAX + i);
|
test_size (f, UINT_MAX + i);
|
||||||
|
|
||||||
/* We would like to test larger values than UINT_MAX here, but they
|
/* 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
|
do not work if the FUSE protocol still uses uint32_t for the
|
||||||
copy_file_range result in struct fuse_write_out. */
|
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 *
|
static void *
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue