renameat2 works differently than on Linux

Bruno Haible bruno@clisp.org
Tue Apr 18 12:47:16 GMT 2023


Hi,

The renameat2 function is "Linux-specific", says the man page [1]; however,
Cygwin implements it as well.

In Cygwin 3.4.6, in a specific case, it operates differently than the
Linux function. Namely, if the old* arguments and the new* arguments
are the same and the flag RENAME_NOREPLACE is specified.

How to reproduce:
================================ foo.c ================================
#ifdef __CYGWIN__
 #include <cygwin/fs.h>
#else
 #define _GNU_SOURCE 1
 #ifdef __linux__
 # include <sys/syscall.h>
 #endif
#endif
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>

int
main (void)
{
  system ("rm -rf sub2");
  int dfd = open (".", O_RDONLY);
  if (dfd < 0) perror ("open");
  int ret = mkdir ("sub2", 0700);
  if (ret < 0) perror ("mkdir");
  close (creat ("sub2/file", 0600));
#if defined __CYGWIN__ || defined __GLIBC__
  ret = renameat2 (dfd, "sub2/file", dfd, "sub2/file", RENAME_NOREPLACE);
#else /* musl libc */
  ret = syscall (SYS_renameat2, dfd, "sub2/file", dfd, "sub2/file", 1);
#endif
  if (ret >= 0)
    printf ("ret=%d\n", ret);
  else
    printf ("ret=%d, errno=%d%s\n", ret, errno, errno == EEXIST ? "=EEXIST" : "");
}
===============================================================================
Output on Linux (glibc, musl libc):
ret=-1, errno=17=EEXIST

Output on Cygwin 3.4.6:
ret=0

Note that there is some ambiguity about this case in [1]: One one hand,
there is the general statement about rename():
  "If oldpath and newpath are existing hard links referring to the
   same file, then rename() does nothing, and returns a success status."
On the other hand, the text regarding RENAME_NOREPLACE says:
  "Return an error if newpath already exists."

Bruno

[1] https://man7.org/linux/man-pages/man2/rename.2.html





More information about the Cygwin mailing list