@@ -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+
88120CAMLprim 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