Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/changes/12074.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- Use copy-on-write (COW) when copying files on filesystems that support it
(Btrfs, ZFS, XFS, etc), under Linux. (#12074, fixes #12071, @nojb)
49 changes: 44 additions & 5 deletions otherlibs/stdune/src/copyfile_stubs.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,10 @@ CAMLprim value stdune_sendfile(value v_in, value v_out, value v_size) {
#include <caml/unixsupport.h>

#include <sys/sendfile.h>
#include <unistd.h>
#include <sys/utsname.h>
#include <linux/version.h>
#include <dlfcn.h>
#include <stdio.h>

#define FD_val(value) Int_val(value)

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

static int dune_sendfile(int in, int out, size_t length) {
static ssize_t dune_sendfile(int in, int out, size_t length) {
ssize_t ret;
while (length > 0) {
ret = sendfile(out, in, NULL, length);
Expand All @@ -85,12 +88,48 @@ static int dune_sendfile(int in, int out, size_t length) {
return length;
}

typedef ssize_t (*copy_file_range_t)(int, loff_t*, int, loff_t*, size_t, unsigned int);

static copy_file_range_t copy_file_range_fn = NULL;

static ssize_t dune_copy_file_range(int in, int out, size_t length) {
ssize_t ret;
while (length > 0) {
ret = copy_file_range_fn(in, NULL, out, NULL, length, 0);
if (ret < 0) {
return dune_sendfile(in, out, length);
}
length = length - ret;
}
return length;
}

static int kernel_version(void) {
struct utsname uts;
int major, minor, patch;

if (uname(&uts) < 0)
return -1;

if (sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) != 3)
return -1;

return KERNEL_VERSION(major, minor, patch);
}

CAMLprim value stdune_sendfile(value v_in, value v_out, value v_size) {
static ssize_t (*dune_copyfile)(int, int, size_t) = NULL;
if (dune_copyfile == NULL) {
if (kernel_version() < KERNEL_VERSION(5, 19, 0) ||
(copy_file_range_fn = (copy_file_range_t)dlsym(NULL, "copy_file_range")) == NULL) {
dune_copyfile = &dune_sendfile;
} else {
dune_copyfile = &dune_copy_file_range;
}
}
CAMLparam3(v_in, v_out, v_size);
caml_release_runtime_system();
/* TODO Use copy_file_range once we have a good mechanism to test for its
* existence */
int ret = dune_sendfile(FD_val(v_in), FD_val(v_out), Long_val(v_size));
ssize_t ret = dune_copyfile(FD_val(v_in), FD_val(v_out), Long_val(v_size));
caml_acquire_runtime_system();
if (ret < 0) {
uerror("sendfile", Nothing);
Expand Down
Loading