Collin Funk <[email protected]> writes:

> Hopefully I am just missing something silly, or perhaps it is not even
> worth the effort and using "#ifdef __APPLE__" is enough.

Given that the issue exists on all non-EOL macOS versions, I think I am
okay with just using "#ifdef __APPLE__". I am leaning towards applying
the attached patch, but will sleep on it and give others a chance to
review.

Collin

>From 07a80c39c13ce19fbf229104fa9943681acf30c6 Mon Sep 17 00:00:00 2001
Message-ID: <07a80c39c13ce19fbf229104fa9943681acf30c6.1780719116.git.collin.fu...@gmail.com>
From: Collin Funk <[email protected]>
Date: Fri, 5 Jun 2026 21:01:26 -0700
Subject: [PATCH v2] install: workaround a macOS bug that prevents reading
 standard input

* src/copy.c (follow_fstatat): Prefer openat and fstat on macOS.
* tests/install/stdin.sh: Remove the skip condition added in commit
2632fbf08 (tests: install: avoid failure on FreeBSD/macOS, 2026-06-03).
Fixes https://bugs.gnu.org/70411
---
 src/copy.c             | 26 +++++++++++++++++++++++++-
 tests/install/stdin.sh |  5 -----
 2 files changed, 25 insertions(+), 6 deletions(-)

diff --git a/src/copy.c b/src/copy.c
index a53fb8f8c..252a9c4ab 100644
--- a/src/copy.c
+++ b/src/copy.c
@@ -186,7 +186,31 @@ emit_debug (const struct cp_options *x)
 static int
 follow_fstatat (int dirfd, char const *filename, struct stat *st, int flags)
 {
-  int result = fstatat (dirfd, filename, st, flags);
+  int result = -1;
+  bool try_fstatat = true;
+
+  /* Work around a macOS bug where stat and fstat give us a different dev/ino.
+     See the following threads:
+     <https://bugs.gnu.org/70411>
+     <https://lists.gnu.org/r/coreutils/2026-06/msg00004.html>.  */
+#ifdef __APPLE__
+  int open_flags = (O_RDONLY | O_NOCTTY | O_NONBLOCK
+                    | ((flags & AT_SYMLINK_NOFOLLOW) ? O_NOFOLLOW : 0));
+  int fd = openat (dirfd, filename, open_flags);
+
+  /* If openat fails, try to use fstatat unless we are certain it will also
+     fail.  E.g., if the given file is not readable openat will fail but
+     fstatat may succeed.  */
+  try_fstatat = fd < 0 && errno != ENOENT && errno != ENOTDIR;
+  if (0 <= fd)
+    {
+      result = fstat (fd, st);
+      close (fd);
+    }
+#endif
+
+  if (result < 0 && try_fstatat)
+    result = fstatat (dirfd, filename, st, flags);
 
   if (DEV_FD_MIGHT_BE_CHR && result == 0 && !(flags & AT_SYMLINK_NOFOLLOW)
       && S_ISCHR (st->st_mode))
diff --git a/tests/install/stdin.sh b/tests/install/stdin.sh
index 2e050059e..4ee8a85c9 100755
--- a/tests/install/stdin.sh
+++ b/tests/install/stdin.sh
@@ -26,11 +26,6 @@ tty=$(readlink -f /dev/stdin)
 test -r "$tty" 2>&1 \
   || skip_ '/dev/stdin is not readable'
 
-# work around FreeBSD / macOS issue as discussed at:
-# https://lists.gnu.org/r/coreutils/2026-06/msg00004.html
-test $(stat -L -c%i /dev/stdin) = $(stat -L -c%i - </dev/stdin) ||
-  skip_ '/dev/stdin inode correlation mismatch'
-
 echo a >a || framework_failure_
 echo b >b || framework_failure_
 
-- 
2.54.0

Reply via email to