#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
#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;
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:
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
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: