2

Is there a non-platform-specific way of doing an atomic "rename File1 to File2 if File2 does not exist, otherwise give an error"? I know I could just check whether File2 exists, then rename the file if it doesn't, but this introduces a potential race condition when some other process creates File2 between the check and the rename().

Under Linux there is the renameat2() function which does exactly this with the RENAME_NOREPLACE flag set. Unfortunately, the manpage says

renameat2() is Linux-specific.

I don't even know whether all libc implementations support this call, or only glibc.

For my usecase, renaming inside the same directory is enough, I don't need to have support for moving to a whole different path.

This is potentially related to https://stackoverflow.com/a/230581/5562035 and Atomically swap contents of two files on Linux

Community
  • 1
  • 1
  • Atomic in what sense? Note in particular that the Linux docs for `rename()` and `renameat()` specifically remark that "there will probably be a window in which both `oldpath` and `newpath` refer to the file being renamed." – John Bollinger Jan 08 '17 at 18:29
  • Use common sense. – Karoly Horvath Jan 08 '17 at 18:30
  • There should be no time window between checking for whether the target file exists and the renaming in which another process could create the target file. Without this condition you could just do `if(!file_exists(path_2)){rename(path_1, path_2)}` –  Jan 08 '17 at 18:33

1 Answers1

2

Is there a non-platform-specific way of doing an atomic "rename File1 to File2 if File2 does not exist, otherwise give an error"?

The C standard library provides only rename() for changing file names, and the standard says this about that function: "If a file named by the string pointed to by new exists prior to the call to the rename function, the behavior is implementation-defined." Therefore, if "non-platform-specific" is interpreted as "specified by the C standard" then no, there is no such mechanism.

If we expand the scope of "non-platform-specific" to allow facilities specified by POSIX, however, then you can rely on link(), which

shall atomically create a new link for the existing file and the link count of the file shall be incremented by one. [and] shall fail if [... the second argument] resolves to an existing directory entry or refers to a symbolic link.

Thus, you can create a new (hard) link to the file first, failing if the specified pathname designates an existing file or symbolic link, and then remove the original link upon success.

The most prominent non-POSIX platform in which you might be interested would be Windows. I'm uncertain whether there is any way at all to do what you ask on Windows, but if there is, you could consider writing a wrapper function that uses conditional compilation to select either the POSIX or Windows implementation.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157