Subject: Re: bug#27986: 26.0.50; 'rename-file' can rename files without confirmation

From: Paul Eggert <[email-masked]>
Date: Sun, 13 Aug 2017 16:48:59 -0700

Getting back to Philipp's original bug report, Apple documentation says macOS 
has a facility like the Linux renameat2 system call (i.e., it's like 'renameat' 
except it can be told to fail if the destination already exists). Attached is a 
proposed patch to use this facility, which means that the case-insensitivity 
test would no longer need to be done in macOS. If there's some way to implement 
renameat_noreplace on MS-Windows we could get rid of the case-insensitivity test 
there too.

I don't have easy access to macOS so I have not installed this patch. It'd be 
nice, Philipp, if you could try it out.

This patch is independent of the destination-directory patch that I recently 
proposed in:

https://bugs.gnu.org/27986#14
From 62605066c4d5a7b91c7800fcc300dbe5082426dc Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email-masked]>
Date: Sun, 13 Aug 2017 16:31:08 -0700
Subject: [PATCH] Improve rename-file behavior on macOS

Problem reported by Philipp Stephani (Bug#27986).
* src/fileio.c (Frename_file):
Worry about file name case sensitivity only if WINDOWSNT.
* src/sysdep.c (renameat_noreplace): Use renameatx_np on macOS,
since this provides the necessary atomicity guarantees.
---
 src/fileio.c | 2 ++
 src/sysdep.c | 2 ++
 2 files changed, 4 insertions(+)

diff --git a/src/fileio.c b/src/fileio.c
index 69079c6ae4..ee70dc6e69 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -2259,12 +2259,14 @@ This is what happens in interactive use with M-x.  */)
      not worry whether NEWNAME exists or whether it is a directory, as
      it is already another name for FILE.  */
   bool case_only_rename = false;
+#ifdef WINDOWSNT
   if (!NILP (Ffile_name_case_insensitive_p (file)))
     {
       newname = Fexpand_file_name (newname, Qnil);
       case_only_rename = !NILP (Fstring_equal (Fdowncase (file),
 					       Fdowncase (newname)));
     }
+#endif
 
   if (!case_only_rename)
     newname = expand_cp_target (Fdirectory_file_name (file), newname);
diff --git a/src/sysdep.c b/src/sysdep.c
index 9eb733221e..4e98f1db24 100644
--- a/src/sysdep.c
+++ b/src/sysdep.c
@@ -2693,6 +2693,8 @@ renameat_noreplace (int srcfd, char const *src, int dstfd, char const *dst)
 {
 #if defined SYS_renameat2 && defined RENAME_NOREPLACE
   return syscall (SYS_renameat2, srcfd, src, dstfd, dst, RENAME_NOREPLACE);
+#elif defined RENAME_EXCL
+  return renameatx_np (srcfd, src, dstfd, dst, RENAME_EXCL);
 #else
   errno = ENOSYS;
   return -1;
-- 
2.13.4