$RodHat_
Syscall reference · Filesystem

renameat2(2)

Rename or atomically swap two filesystem entries, with optional no-overwrite and exchange flags.

Since
Linux 3.15check your kernel →
glibc
2.28
System
Linux
Man page
renameat2(2)

Synopsis

#include <fcntl.h>
#include <sys/syscall.h>
#include <unistd.h>

/* glibc wrapper since 2.28; use syscall(2) on older systems */
int renameat2(int olddirfd, const char *oldpath,
              int newdirfd, const char *newpath,
              unsigned int flags);

Rod's take

renameat2(2) adds two flags rename(2) never had. RENAME_NOREPLACE fails with EEXIST if the destination exists, eliminating the check-then-rename race. RENAME_EXCHANGE atomically swaps two paths: both keep existing inodes, both remain visible to readers throughout, and the old file lands in the new name after the call. ext4, XFS, btrfs, tmpfs, and overlayfs support both flags; FAT and NFSv3 return EINVAL or ENOSYS. glibc wrapper arrived in 2.28; call syscall(2) directly on older systems.

Flags and arguments that matter

NameWhat it does
RENAME_NOREPLACEFail with EEXIST if newpath already exists instead of replacing it.
RENAME_EXCHANGEAtomically swap oldpath and newpath. Both must exist. No intermediate state where either path is absent.
RENAME_WHITEOUTLeave a whiteout inode at oldpath for union/overlay mount semantics. Rarely needed outside container runtimes.

Errors you will actually hit

errnoMeaning
EEXISTRENAME_NOREPLACE was set and newpath already exists.
ENOENTRENAME_EXCHANGE was set and one or both paths do not exist.
EINVALUnsupported flags combination, or the filesystem does not support the requested flag.
ENOSYSKernel older than 3.15, or filesystem (FAT, NFSv3) does not implement renameat2.

Example

#include <fcntl.h>
#include <sys/syscall.h>
#include <unistd.h>

/* write new config fully, then swap atomically */
int r = syscall(SYS_renameat2,
                AT_FDCWD, "etc/app.conf.new",
                AT_FDCWD, "etc/app.conf",
                2 /* RENAME_EXCHANGE */);
/* etc/app.conf.new now holds the old config */
if (r == 0) unlink("etc/app.conf.new");

Trimmed from the full walkthrough in renameat2(2): atomic file swap without a race window.

BSD equivalent

No equivalent in FreeBSD or OpenBSD. Both support atomic rename(2) for same-directory replace, but neither has an EXCHANGE primitive at the VFS layer.

The long version, from the console

Sources

  1. renameat2(2), man7.org
  2. Atomic file renaming, LWN.net

Version numbers are read off the cited man pages, not memory. Spot one that's wrong? Tell Rod.