The Linux kernel implements the POSIX renameat() system call that atomically renames a file, moving it between directories if required. If the newpath already exists, it is atomically replaced by the oldpath.
This system call function signature is as follows:
int renameat(int olddirfd, const char *oldpath,
int newdirfd, const char *newpath);
While atomically renaming a file is useful, the system call was not designed for future extensibility and so a follow-up renameat2() system call was added. This new system call has a flags parameter that can be used to modify its behaviour:
int renameat2(int olddirfd, const char *oldpath,
int newdirfd, const char *newpath, unsigned int flags);
The flags argument is a bit mask that if set to zero, it makes renameat2() to behave just like the old renameat() system call.
RENAME_NOREPLACE is a flag that prevents the newpath to be overwritten by oldpath. If newpath already exists, an error is returned instead.
RENAME_EXCHANGE is a flag that atomically exchanges oldpath and newpath.
Before Linux v6.0, the vfat filesystem implementation only had support for the renameat2() RENAME_NOREPLACE flag. This means that files couldn’t be atomically exchanged in a vfat filesystem using the RENAME_EXCHANGE flag.
But this feature is quite convenient to implement for example A/B update mechanisms, since two files can be swapped atomically using a single system call. This is particularly important for EFI platforms, because the EFI System Partition always is formatted using vfat.
If someone wants to implement an update mechanism for EFI binaries stored in the ESP, and provide a fallback, there was no way to do it safely in Linux. This is even more important for vfat, due the filesystem not having a journal and so the EFI firmware not being able to do a replay of pending changes to make the filesystem consistent again. For this reason, the less operations done while swapping EFI binaries, the better.
Another example is OSTree, that uses symbolic links renames to atomically commit its deployment transactions. Having atomic exchange support in vfat, could allow to use RENAME_EXCHANGE instead in this filesystem that doesn’t support symlinks.
With that use case in mind, we implemented [1,2] RENAME_EXCHANGE support to the Linux vfat filesystem driver. An example of how this can be used, is in a Linux kernel rename_exchange selftest that was added along with the implementation.
Although this feature has been in the Linux kernel since 2022, I’ve had to mention it a few times recently, so I thought a post might help raise awareness in case anyone finds it useful too.
Happy hacking!