Subject: Re: bug#52193: mv broken on non-APFS filesystems on macOS on coreutils >= 9.0
From: Paul Eggert <[email-masked]>
To: Sudhip Nashi <[email-masked]>
Cc: Cameron Katri <[email-masked]>, [email-masked]
Date: Tue, 14 Dec 2021 12:48:17 -0800
List-Id: <debbugs-submit.debbugs.gnu.org>

On 12/12/21 09:06, Sudhip Nashi wrote:
> 
>> Thanks, I think I see the problem now. Please try the attached patch; I haven't tested myself as I lack access to macOS. Thanks.
>> <0001-renameatu-port-to-macOS-tmpfs.patch>
> 
> It looks like this patch solves the problem.
> 

Thanks, I installed the attached to implement this fix, the first patch 
into Gnulib, the second into coreutils.

From dd474e50930ea00910631eb1b77ff4270d7b02c0 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email-masked]>
Date: Tue, 14 Dec 2021 12:32:30 -0800
Subject: [PATCH] renameatu: port to macOS tmpfs
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Problem reported by Sudhip Nashi (Bug#52193).
* lib/renameatu.c (renameat2ish) [HAVE_RENAMEAT]: New function.
(renameatu): Use the new function, to avoid a bug when
renameatx_np fails with errno == ENOTSUP.  Don’t try to support
RENAME_EXCHANGE; the old code didn’t work and nobody using using
RENAME_EXCHANGE anyway.
---
 ChangeLog       | 10 +++++++
 lib/renameatu.c | 69 +++++++++++++++++++++++++------------------------
 2 files changed, 45 insertions(+), 34 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 0e20dcb58..370cd9839 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2021-12-14  Paul Eggert  <[email-masked]>
+
+	renameatu: port to macOS tmpfs
+	Problem reported by Sudhip Nashi (Bug#52193).
+	* lib/renameatu.c (renameat2ish) [HAVE_RENAMEAT]: New function.
+	(renameatu): Use the new function, to avoid a bug when
+	renameatx_np fails with errno == ENOTSUP.  Don’t try to support
+	RENAME_EXCHANGE; the old code didn’t work and nobody using using
+	RENAME_EXCHANGE anyway.
+
 2021-12-12  Bruno Haible  <[email-masked]>
 
 	gnulib-tool: Support non-recursive-gnulib-prefix-hack with tests.
diff --git a/lib/renameatu.c b/lib/renameatu.c
index 38438a4ef..b75f95269 100644
--- a/lib/renameatu.c
+++ b/lib/renameatu.c
@@ -61,6 +61,29 @@ rename_noreplace (char const *src, char const *dst)
 
 #undef renameat
 
+#if HAVE_RENAMEAT
+
+/* Act like renameat (FD1, SRC, FD2, DST), except fail with EEXIST if
+   FLAGS is nonzero and it is easy to fail atomically if DST already exists.
+   This lets renameatu be atomic when it can be implemented in terms
+   of renameatx_np.  */
+static int
+renameat2ish (int fd1, char const *src, int fd2, char const *dst,
+              unsigned int flags)
+{
+# ifdef RENAME_EXCL
+  if (flags)
+    {
+      int r = renameatx_np (fd1, src, fd2, dst, RENAME_EXCL);
+      if (r == 0 || errno != ENOTSUP)
+        return r;
+    }
+# endif
+
+  return renameat (fd1, src, fd2, dst);
+}
+#endif
+
 /* Rename FILE1, in the directory open on descriptor FD1, to FILE2, in
    the directory open on descriptor FD2.  If possible, do it without
    changing the working directory.  Otherwise, resort to using
@@ -93,9 +116,6 @@ renameatu (int fd1, char const *src, int fd2, char const *dst,
 
 #if HAVE_RENAMEAT
   {
-# if defined RENAME_EXCL                /* macOS */
-  unsigned int uflags;
-# endif
   size_t src_len;
   size_t dst_len;
   char *src_temp = (char *) src;
@@ -107,23 +127,12 @@ renameatu (int fd1, char const *src, int fd2, char const *dst,
   struct stat dst_st;
   bool dst_found_nonexistent = false;
 
-  /* Check the flags.  */
-# if defined RENAME_EXCL
-  /* We can support RENAME_EXCHANGE and RENAME_NOREPLACE.  */
-  if (flags & ~(RENAME_EXCHANGE | RENAME_NOREPLACE))
-# else
-  /* RENAME_NOREPLACE is the only flag currently supported.  */
-  if (flags & ~RENAME_NOREPLACE)
-# endif
-    return errno_fail (ENOTSUP);
-
-# if defined RENAME_EXCL
-  uflags = ((flags & RENAME_EXCHANGE ? RENAME_SWAP : 0)
-            | (flags & RENAME_NOREPLACE ? RENAME_EXCL : 0));
-# endif
-
-  if ((flags & RENAME_NOREPLACE) != 0)
+  switch (flags)
     {
+    case 0:
+      break;
+
+    case RENAME_NOREPLACE:
       /* This has a race between the call to lstatat and the calls to
          renameat below.  This lstatat is needed even if RENAME_EXCL
          is defined, because RENAME_EXCL is buggy on macOS 11.2:
@@ -134,26 +143,22 @@ renameatu (int fd1, char const *src, int fd2, char const *dst,
       if (errno != ENOENT)
         return -1;
       dst_found_nonexistent = true;
+      break;
+
+    default:
+      return errno_fail (ENOTSUP);
     }
 
   /* Let strace see any ENOENT failure.  */
   src_len = strlen (src);
   dst_len = strlen (dst);
   if (!src_len || !dst_len)
-# if defined RENAME_EXCL
-    return renameatx_np (fd1, src, fd2, dst, uflags);
-# else
-    return renameat (fd1, src, fd2, dst);
-# endif
+    return renameat2ish (fd1, src, fd2, dst, flags);
 
   src_slash = src[src_len - 1] == '/';
   dst_slash = dst[dst_len - 1] == '/';
   if (!src_slash && !dst_slash)
-# if defined RENAME_EXCL
-    return renameatx_np (fd1, src, fd2, dst, uflags);
-# else
-    return renameat (fd1, src, fd2, dst);
-# endif
+    return renameat2ish (fd1, src, fd2, dst, flags);
 
   /* Presence of a trailing slash requires directory semantics.  If
      the source does not exist, or if the destination cannot be turned
@@ -226,11 +231,7 @@ renameatu (int fd1, char const *src, int fd2, char const *dst,
      on Solaris, since all other systems either lack renameat or honor
      trailing slash correctly.  */
 
-# if defined RENAME_EXCL
-  ret_val = renameatx_np (fd1, src_temp, fd2, dst_temp, uflags);
-# else
-  ret_val = renameat (fd1, src_temp, fd2, dst_temp);
-# endif
+  ret_val = renameat2ish (fd1, src_temp, fd2, dst_temp, flags);
   rename_errno = errno;
   goto out;
  out:
-- 
2.32.0

From 55f92b8167b8065c039c4202cc5ce03fcaacdbda Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email-masked]>
Date: Tue, 14 Dec 2021 12:40:25 -0800
Subject: [PATCH] build: update gnulib submodule to latest

* NEWS: Mention the bugfix.
---
 NEWS   | 4 ++++
 gnulib | 2 +-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/NEWS b/NEWS
index c2ecdedfa..c8e8bdc16 100644
--- a/NEWS
+++ b/NEWS
@@ -21,6 +21,10 @@ GNU coreutils NEWS                                    -*- outline -*-
   and B is in some other file system.
   [bug introduced in coreutils-9.0]
 
+  On macOS, 'mv A B' no longer fails with "Operation not supported"
+  when A and B are in the same tmpfs file system.
+  [bug introduced in coreutils-9.0]
+
   'mv -T --backup=numbered A B/' no longer miscalculates the backup number
   for B when A is a directory, possibly inflooping.
   [bug introduced in coreutils-6.3]
diff --git a/gnulib b/gnulib
index 4d1e184fd..dd474e509 160000
--- a/gnulib
+++ b/gnulib
@@ -1 +1 @@
-Subproject commit 4d1e184fd546db46ca11bb4974e47984366875d9
+Subproject commit dd474e50930ea00910631eb1b77ff4270d7b02c0
-- 
2.32.0