1- /* fcopyfile on macOS is slower than a simple read/write loop? */
1+ /*
2+ * By default, use a read/write loop to copy files on POSIX systems.
3+ * On Linux, use sendfile by default as it's slightly faster. On
4+ * macOS, we avoid fcopyfile by default because it's slightly slower.
5+ */
26#undef USE_FCOPYFILE
7+ #define USE_SENDFILE 1
38
49#ifdef _WIN32
510
@@ -264,6 +269,10 @@ cl_fs_cleanup(void)
264269#include <sys/types.h>
265270#include <sys/stat.h>
266271
272+ #if defined(__linux__ )
273+ # include <sys/sendfile.h>
274+ #endif
275+
267276#if defined(__APPLE__ ) || defined(__FreeBSD__ )
268277# include <copyfile.h>
269278#endif
@@ -341,12 +350,12 @@ static char *joinpath(const char *dir, const char *base, int base_len)
341350}
342351
343352static void
344- fs_copydir_helper (const char * source , const char * dest , int dmode )
353+ fs_copydir_helper (const char * source , const char * dest , int dest_mode )
345354{
346355 DIR * source_dir ;
347356 struct dirent * d ;
348357
349- mkdir (dest , dmode );
358+ mkdir (dest , dest_mode );
350359
351360 cl_assert_ (source_dir = opendir (source ), "Could not open source dir" );
352361 while ((d = (errno = 0 , readdir (source_dir ))) != NULL ) {
@@ -366,20 +375,32 @@ fs_copydir_helper(const char *source, const char *dest, int dmode)
366375}
367376
368377static void
369- fs_copyfile_helper (const char * source , const char * dest , int dmode )
378+ fs_copyfile_helper (const char * source , size_t source_len , const char * dest , int dest_mode )
370379{
371380 int in , out ;
372381
373382 cl_must_pass ((in = open (source , O_RDONLY )));
374- cl_must_pass ((out = open (dest , O_WRONLY |O_CREAT |O_TRUNC , dmode )));
383+ cl_must_pass ((out = open (dest , O_WRONLY |O_CREAT |O_TRUNC , dest_mode )));
375384
376385#if USE_FCOPYFILE && (defined(__APPLE__ ) || defined(__FreeBSD__ ))
386+ ((void )(source_len )); /* unused */
377387 cl_must_pass (fcopyfile (in , out , 0 , COPYFILE_DATA ));
388+ #elif USE_SENDFILE && defined(__linux__ )
389+ {
390+ ssize_t ret = 0 ;
391+
392+ while (source_len && (ret = sendfile (out , in , NULL , source_len )) > 0 ) {
393+ source_len -= (size_t )ret ;
394+ }
395+ cl_assert (ret >= 0 );
396+ }
378397#else
379398 {
380399 char buf [131072 ];
381400 ssize_t ret ;
382401
402+ ((void )(source_len )); /* unused */
403+
383404 while ((ret = read (in , buf , sizeof (buf ))) > 0 ) {
384405 size_t len = (size_t )ret ;
385406
@@ -427,7 +448,7 @@ fs_copy(const char *source, const char *_dest)
427448 if (S_ISDIR (source_st .st_mode )) {
428449 fs_copydir_helper (source , dest , source_st .st_mode );
429450 } else {
430- fs_copyfile_helper (source , dest , source_st .st_mode );
451+ fs_copyfile_helper (source , source_st . st_size , dest , source_st .st_mode );
431452 }
432453
433454 free (dbuf );
0 commit comments