This patch enables using the macOS 10.12+ clonefile(2) system call for reflinking on supported filesystems. The system BSD cp(1) only supports -c (--reflink=always), which is quite a pain.
Tested on a hackintosh machine with macOS 10.15. And yes, this is a resubmission. --- configure.ac | 14 ++++++++++++++ src/copy.c | 45 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 69d4e7d..07ad5c1 100644 --- a/configure.ac +++ b/configure.ac @@ -362,6 +362,20 @@ case $utils_cv_func_setpriority,$ac_cv_func_nice in gl_ADD_PROG([optional_bin_progs], [nice]) esac +AC_CACHE_CHECK([for clonefile], + [utils_cv_func_clonefile], + [AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[#include <sys/attr.h> + #include <sys/clonefile.h> + ]], + [[clonefile("", "", CLONE_NOFOLLOW);]])], + [utils_cv_func_clonefile=yes], + [utils_cv_func_clonefile=no])]) +if test $utils_cv_func_clonefile = yes; then + AC_DEFINE([HAVE_CLONEFILE], [1], [Define if clonefile exists.]) +fi + if test "$cross_compiling" = yes || test -c /dev/stdin <.; then AC_DEFINE([DEV_FD_MIGHT_BE_CHR], [1], [Define to 1 if /dev/std{in,out,err} and /dev/fd/N, if they exist, might be diff --git a/src/copy.c b/src/copy.c index 4050f69..fad8189 100644 --- a/src/copy.c +++ b/src/copy.c @@ -73,6 +73,11 @@ # include "verror.h" #endif +#if HAVE_CLONEFILE +# include <sys/attr.h> +# include <sys/clonefile.h> +#endif + #if HAVE_LINUX_FALLOC_H # include <linux/falloc.h> #endif @@ -1311,12 +1316,48 @@ copy_reg (char const *src_name, char const *dst_name, goto close_src_desc; } +#if HAVE_CLONEFILE + /* The macOS clonefile() syscall is like link in that it takes + pathnames. Do it first. */ + if (data_copy_required && x->reflink_mode) + { + bool clone_ok = false; + if (! *new_dst) + { + if (unlink(dst_name) != 0) + { + error (0, errno, _("cannot remove %s"), quoteaf (dst_name)); + goto noclone; + } + if (x->verbose) + printf (_("removed %s\n"), quoteaf (dst_name)); + } + uint32_t cloneflags = 0; + if (x->dereference == DEREF_NEVER) cloneflags |= CLONE_NOFOLLOW; + /* CLONE_NOOWNERCOPY (0x0002) is recently added with no specific + * date. Let's not feed ourselves to Apple-versioning. */ + clone_ok = clonefile (src_name, dst_name, cloneflags) == 0; + noclone:; + if (clone_ok || x->reflink_mode == REFLINK_ALWAYS) + { + if (!clone_ok) + { + error (0, errno, _("failed to clone %s from %s"), + quoteaf_n (0, dst_name), quoteaf_n (1, src_name)); + return false; + } + data_copy_required = false; + *new_dst = false; + } + } +#endif + /* The semantics of the following open calls are mandated by the specs for both cp and mv. */ if (! *new_dst) { int open_flags = - O_WRONLY | O_BINARY | (x->data_copy_required ? O_TRUNC : 0); + O_WRONLY | O_BINARY | (data_copy_required ? O_TRUNC : 0); dest_desc = open (dst_name, open_flags); dest_errno = errno; @@ -1453,6 +1494,7 @@ copy_reg (char const *src_name, char const *dst_name, goto close_src_and_dst_desc; } +#if ! HAVE_CLONEFILE /* --attributes-only overrides --reflink. */ if (data_copy_required && x->reflink_mode) { @@ -1469,6 +1511,7 @@ copy_reg (char const *src_name, char const *dst_name, data_copy_required = false; } } +#endif if (data_copy_required) { -- 2.20.1.windows.1