Skip to content

Commit 13da9c7

Browse files
authored
Use copy_file_range when supported (ocaml#12074)
Signed-off-by: Nicolás Ojeda Bär <[email protected]>
1 parent 988af36 commit 13da9c7

File tree

2 files changed

+46
-5
lines changed

2 files changed

+46
-5
lines changed

doc/changes/12074.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
- Use copy-on-write (COW) when copying files on filesystems that support it
2+
(Btrfs, ZFS, XFS, etc), under Linux. (#12074, fixes #12071, @nojb)

otherlibs/stdune/src/copyfile_stubs.c

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,10 @@ CAMLprim value stdune_sendfile(value v_in, value v_out, value v_size) {
6363
#include <caml/unixsupport.h>
6464

6565
#include <sys/sendfile.h>
66-
#include <unistd.h>
66+
#include <sys/utsname.h>
67+
#include <linux/version.h>
68+
#include <dlfcn.h>
69+
#include <stdio.h>
6770

6871
#define FD_val(value) Int_val(value)
6972

@@ -73,7 +76,7 @@ CAMLprim value stdune_copyfile(value v_from, value v_to) {
7376
caml_failwith("copyfile: only on macos");
7477
}
7578

76-
static int dune_sendfile(int in, int out, size_t length) {
79+
static ssize_t dune_sendfile(int in, int out, size_t length) {
7780
ssize_t ret;
7881
while (length > 0) {
7982
ret = sendfile(out, in, NULL, length);
@@ -85,12 +88,48 @@ static int dune_sendfile(int in, int out, size_t length) {
8588
return length;
8689
}
8790

91+
typedef ssize_t (*copy_file_range_t)(int, loff_t*, int, loff_t*, size_t, unsigned int);
92+
93+
static copy_file_range_t copy_file_range_fn = NULL;
94+
95+
static ssize_t dune_copy_file_range(int in, int out, size_t length) {
96+
ssize_t ret;
97+
while (length > 0) {
98+
ret = copy_file_range_fn(in, NULL, out, NULL, length, 0);
99+
if (ret < 0) {
100+
return dune_sendfile(in, out, length);
101+
}
102+
length = length - ret;
103+
}
104+
return length;
105+
}
106+
107+
static int kernel_version(void) {
108+
struct utsname uts;
109+
int major, minor, patch;
110+
111+
if (uname(&uts) < 0)
112+
return -1;
113+
114+
if (sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) != 3)
115+
return -1;
116+
117+
return KERNEL_VERSION(major, minor, patch);
118+
}
119+
88120
CAMLprim value stdune_sendfile(value v_in, value v_out, value v_size) {
121+
static ssize_t (*dune_copyfile)(int, int, size_t) = NULL;
122+
if (dune_copyfile == NULL) {
123+
if (kernel_version() < KERNEL_VERSION(5, 19, 0) ||
124+
(copy_file_range_fn = (copy_file_range_t)dlsym(NULL, "copy_file_range")) == NULL) {
125+
dune_copyfile = &dune_sendfile;
126+
} else {
127+
dune_copyfile = &dune_copy_file_range;
128+
}
129+
}
89130
CAMLparam3(v_in, v_out, v_size);
90131
caml_release_runtime_system();
91-
/* TODO Use copy_file_range once we have a good mechanism to test for its
92-
* existence */
93-
int ret = dune_sendfile(FD_val(v_in), FD_val(v_out), Long_val(v_size));
132+
ssize_t ret = dune_copyfile(FD_val(v_in), FD_val(v_out), Long_val(v_size));
94133
caml_acquire_runtime_system();
95134
if (ret < 0) {
96135
uerror("sendfile", Nothing);

0 commit comments

Comments
 (0)