summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPádraig Brady <P@draigBrady.com>2023-03-23 13:19:04 +0000
committerPádraig Brady <P@draigBrady.com>2023-03-24 13:12:51 +0000
commit093a8b4bfaba60005f14493ce7ef11ed665a0176 (patch)
tree11afbec5f8b202edc98c622340acd5bbd58e9ae5
parent55456b95d8209ebc09916d40aa28e34cdc16c929 (diff)
downloadcoreutils-093a8b4bfaba60005f14493ce7ef11ed665a0176.tar.gz
copy: fix --reflink=auto to fallback in more cases
On restricted systems like android or some containers, FICLONE could return EPERM, EACCES, or ENOTTY, which would have induced the command to fail to copy rather than falling back to a more standard copy. * src/copy.c (is_terminal_failure): A new function refactored from handle_clone_fail(). (is_CLONENOTSUP): Merge in the handling of EACCES, ENOTTY, EPERM as they also pertain to determination of whether cloning is supported if we ever use this function in that context. (handle_clone_fail): Use is_terminal_failure() in all cases, so that we assume a terminal failure in less errno cases. * NEWS: Mention the bug fix. Addresses https://bugs.gnu.org/62404
-rw-r--r--NEWS8
-rw-r--r--src/copy.c62
2 files changed, 43 insertions, 27 deletions
diff --git a/NEWS b/NEWS
index 4a95d3b5e..3b0524c22 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,14 @@ GNU coreutils NEWS -*- outline -*-
* Noteworthy changes in release ?.? (????-??-??) [?]
+** Bug fixes
+
+ cp --relink=auto (the default), mv, and install
+ will again fall back to a standard copy in more cases.
+ Previously copies could fail with permission errors on
+ more restricted systems like android or containers etc.
+ [bug introduced in coreutils-9.2]
+
md5sum --check again correctly prints the status of each file checked.
Previously the status for files was printed as 'OK' once any file had passed.
This also applies to cksum, sha*sum, and b2sum.
diff --git a/src/copy.c b/src/copy.c
index 39197872c..f8ba058d6 100644
--- a/src/copy.c
+++ b/src/copy.c
@@ -278,15 +278,27 @@ create_hole (int fd, char const *name, bool punch_holes, off_t size)
}
-/* Whether the errno from FICLONE, or copy_file_range
- indicates operation is not supported for this file or file system. */
+/* Whether the errno indicates the operation is a transient failure.
+ I.e., a failure that would indicate the operation _is_ supported,
+ but has failed in a terminal way. */
+
+static bool
+is_terminal_error (int err)
+{
+ return err == EIO || err == ENOMEM || err == ENOSPC || err == EDQUOT;
+}
+
+
+/* Whether the errno from FICLONE, or copy_file_range indicates
+ the operation is not supported/allowed for this file or process. */
static bool
is_CLONENOTSUP (int err)
{
- return err == ENOSYS || is_ENOTSUP (err)
+ return err == ENOSYS || err == ENOTTY || is_ENOTSUP (err)
|| err == EINVAL || err == EBADF
- || err == EXDEV || err == ETXTBSY;
+ || err == EXDEV || err == ETXTBSY
+ || err == EPERM || err == EACCES;
}
@@ -339,20 +351,18 @@ sparse_copy (int src_fd, int dest_fd, char **abuf, size_t buf_size,
{
copy_debug.offload = COPY_DEBUG_UNSUPPORTED;
- if (is_CLONENOTSUP (errno))
- break;
-
- /* copy_file_range might not be enabled in seccomp filters,
- so retry with a standard copy. EPERM can also occur
- for immutable files, but that would only be in the edge case
- where the file is made immutable after creating/truncating,
+ /* Consider operation unsupported only if no data copied.
+ For example, EPERM could occur if copy_file_range not enabled
+ in seccomp filters, so retry with a standard copy. EPERM can
+ also occur for immutable files, but that would only be in the
+ edge case where the file is made immutable after creating,
in which case the (more accurate) error is still shown. */
- if (errno == EPERM && *total_n_read == 0)
+ if (*total_n_read == 0 && is_CLONENOTSUP (errno))
break;
/* ENOENT was seen sometimes across CIFS shares, resulting in
no data being copied, but subsequent standard copies succeed. */
- if (errno == ENOENT && *total_n_read == 0)
+ if (*total_n_read == 0 && errno == ENOENT)
break;
if (errno == EINTR)
@@ -1172,17 +1182,15 @@ handle_clone_fail (int dst_dirfd, char const* dst_relname,
char const* src_name, char const* dst_name,
int dest_desc, bool new_dst, enum Reflink_type reflink_mode)
{
- /* If the clone operation is creating the destination,
- then don't try and cater for all non transient file system errors,
- and instead only cater for specific transient errors. */
- bool transient_failure;
- if (dest_desc < 0) /* currently for fclonefileat(). */
- transient_failure = errno == EIO || errno == ENOMEM
- || errno == ENOSPC || errno == EDQUOT;
- else /* currently for FICLONE. */
- transient_failure = ! is_CLONENOTSUP (errno);
-
- if (reflink_mode == REFLINK_ALWAYS || transient_failure)
+ /* When the clone operation fails, report failure only with errno values
+ known to mean trouble when the clone is supported and called properly.
+ Do not report failure merely because !is_CLONENOTSUP (errno),
+ as systems may yield oddball errno values here with FICLONE.
+ Also is_CLONENOTSUP() is not appropriate for the range of errnos
+ possible from fclonefileat(), so it's more consistent to avoid. */
+ bool report_failure = is_terminal_error (errno);
+
+ if (reflink_mode == REFLINK_ALWAYS || report_failure)
error (0, errno, _("failed to clone %s from %s"),
quoteaf_n (0, dst_name), quoteaf_n (1, src_name));
@@ -1190,14 +1198,14 @@ handle_clone_fail (int dst_dirfd, char const* dst_relname,
but cloned no data. */
if (new_dst /* currently not for fclonefileat(). */
&& reflink_mode == REFLINK_ALWAYS
- && ((! transient_failure) || lseek (dest_desc, 0, SEEK_END) == 0)
+ && ((! report_failure) || lseek (dest_desc, 0, SEEK_END) == 0)
&& unlinkat (dst_dirfd, dst_relname, 0) != 0 && errno != ENOENT)
error (0, errno, _("cannot remove %s"), quoteaf (dst_name));
- if (! transient_failure)
+ if (! report_failure)
copy_debug.reflink = COPY_DEBUG_UNSUPPORTED;
- if (reflink_mode == REFLINK_ALWAYS || transient_failure)
+ if (reflink_mode == REFLINK_ALWAYS || report_failure)
return false;
return true;