From 0a590853b159d4eda90b1d3a83fc1859356727ac Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Fri, 10 Feb 2023 13:34:54 -0800 Subject: [PATCH 1/3] copy: improve use of fclonefileat * src/copy.c (copy_reg): Use CLONE_ACL if available and working. If the only problem with fclonefileat is that it would create the file with the wrong timestamp, or with too few permissions, do that but fix the timestamp and permissions afterwards, rather than falling back on a traditional copy. (CLONE_ACL) [HAVE_FCLONEFILEAT && !USE_XATTR]: Default to 0. --- NEWS | 3 +++ src/copy.c | 53 +++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/NEWS b/NEWS index 3d0ede150..cbc614c2e 100644 --- a/NEWS +++ b/NEWS @@ -124,6 +124,9 @@ GNU coreutils NEWS -*- outline -*- and possibly employing copy offloading or reflinking, for the non sparse portion of such sparse files. + On macOS, cp creates a copy-on-write clone in more cases. + Previously cp would only do this when preserving mode and timestamps. + date --debug now diagnoses if multiple --date or --set options are specified, as only the last specified is significant in that case. diff --git a/src/copy.c b/src/copy.c index dfbb557de..8370f55bd 100644 --- a/src/copy.c +++ b/src/copy.c @@ -1146,6 +1146,7 @@ copy_reg (char const *src_name, char const *dst_name, union scan_inference scan_inference; bool return_val = true; bool data_copy_required = x->data_copy_required; + bool mode_already_preserved = false; bool preserve_xattr = USE_XATTR & x->preserve_xattr; source_desc = open (src_name, @@ -1244,17 +1245,48 @@ copy_reg (char const *src_name, char const *dst_name, if (*new_dst) { #if HAVE_FCLONEFILEAT && !USE_XATTR -/* CLONE_NOOWNERCOPY only available on macos >= 10.13. */ +# ifndef CLONE_ACL +# define CLONE_ACL 0 /* Added in macOS 12.6. */ +# endif # ifndef CLONE_NOOWNERCOPY -# define CLONE_NOOWNERCOPY 0 +# define CLONE_NOOWNERCOPY 0 /* Added in macOS 10.13. */ # endif - int fc_flags = x->preserve_ownership ? 0 : CLONE_NOOWNERCOPY; + mode_t cloned_mode_bits = S_ISVTX | S_IRWXUGO; + mode_t cloned_mode = src_mode & cloned_mode_bits; + int fc_flags = ((x->preserve_mode ? CLONE_ACL : 0) + | (x->preserve_ownership ? 0 : CLONE_NOOWNERCOPY)); if (data_copy_required && x->reflink_mode - && x->preserve_mode && x->preserve_timestamps + && (x->preserve_mode || ! (cloned_mode & ~dst_mode)) && (x->preserve_ownership || CLONE_NOOWNERCOPY)) { - if (fclonefileat (source_desc, dst_dirfd, dst_relname, fc_flags) == 0) - goto close_src_desc; + int s = fclonefileat (source_desc, dst_dirfd, dst_relname, fc_flags); + if (s != 0 && (fc_flags & CLONE_ACL) != 0 && errno == EINVAL) + { + fc_flags &= ~CLONE_ACL; + s = fclonefileat (source_desc, dst_dirfd, dst_relname, fc_flags); + } + if (s == 0) + { + if (!x->preserve_timestamps) + { + struct timespec timespec[2]; + timespec[0].tv_nsec = timespec[1].tv_nsec = UTIME_NOW; + if (utimensat (dst_dirfd, dst_relname, timespec, + AT_SYMLINK_NOFOLLOW) + != 0) + { + error (0, errno, _("updating times for %s"), + quoteaf (dst_name)); + return_val = false; + } + } + mode_already_preserved = (fc_flags & CLONE_ACL) != 0 + && cloned_mode == src_mode; + dest_desc = -1; + omitted_permissions = dst_mode & ~cloned_mode; + extra_permissions = dst_mode & ~ cached_umask (); + goto set_dest_mode; + } else if (! handle_clone_fail (dst_dirfd, dst_relname, src_name, dst_name, -1, false /* We didn't create dst */, @@ -1485,9 +1517,14 @@ copy_reg (char const *src_name, char const *dst_name, set_author (dst_name, dest_desc, src_sb); +#if HAVE_FCLONEFILEAT && !USE_XATTR +set_dest_mode: +#endif if (x->preserve_mode || x->move_mode) { - if (copy_acl (src_name, source_desc, dst_name, dest_desc, src_mode) != 0 + if (!mode_already_preserved + && (copy_acl (src_name, source_desc, dst_name, dest_desc, src_mode) + != 0) && x->require_preserve) return_val = false; } @@ -1517,7 +1554,7 @@ copy_reg (char const *src_name, char const *dst_name, } close_src_and_dst_desc: - if (close (dest_desc) < 0) + if (0 <= dest_desc && close (dest_desc) < 0) { error (0, errno, _("failed to close %s"), quoteaf (dst_name)); return_val = false; -- 2.26.2