Atomically Swap Contents of Two Files on Linux

Atomically swap contents of two files on Linux

You can use the (fairly recent) linux syscall renameat2

Here is the definition :

int renameat2(int olddir, const char *oldname, 
int newdir, const char *newname, unsigned int flags);

You can find its source code on the kernel's Git repo if needed.

It's basically the same as renameat, but if you pass the flag RENAME_EXCHANGE it will swap the two files instead of renaming one into the other.

The operation is atomic.

Shortest way to swap two files in bash

Add this to your .bashrc:

function swap()         
{
local TMPFILE=tmp.$$
mv "$1" $TMPFILE
mv "$2" "$1"
mv $TMPFILE "$2"
}

If you want to handle potential failure of intermediate mv operations, check Can Bal's answer.

Please note that neither this, nor other answers provide an atomic solution, because it's impossible to implement such using Linux syscalls and/or popular Linux filesystems. For Darwin kernel, check exchangedata syscall.

How to swap filenames in Unix?

ok, stupid question, but why can't you simply do something like (in a shell script):

mv $fileA $fileA.$$
mv $fileB $fileA
mv $fileA.$$ $fileB

and yes of course it uses a temporary file, but its more concise then the other answers.

Moving a directory atomically

You can do this if you use symlinks:

Let's say alpha is a symlink to directory alpha_1, and you want to switch the symlink to point to alpha_2. Here's what that looks like before the switch:

$ ls -l
lrwxrwxrwx alpha -> alpha_1
drwxr-xr-x alpha_1
drwxr-xr-x alpha_2

To make alpha refer to alpha_2, use ln -nsf:

$ ln -nsf alpha_2 alpha
$ ls -l
lrwxrwxrwx alpha -> alpha_2
drwxr-xr-x alpha_1
drwxr-xr-x alpha_2

Now you can remove the old directory:

$ rm -rf alpha_1

Note that this is NOT actually a fully atomic operation, but it does happen very quickly since the "ln" command both unlinks and then immediately recreates the symlink. You can verify this behaviour with strace:

$ strace ln -nsf alpha_2 alpha
...
symlink("alpha_2", "alpha") = -1 EEXIST (File exists)
unlink("alpha") = 0
symlink("alpha_2", "alpha") = 0
...

You can repeat this procedure as desired: e.g. when you have a new version, alpha_3:

$ ln -nsf alpha_3 alpha
$ rm -rf alpha_2

How does one atomically replace a directory with another one in Java?

I am afraid you can't. Not at the SO level at least. So even if you manage "atomicity" in the context of your java application, you have no guarantee about some other "rogue" process interfering at the actual filesystem level.

If I were you, I'd read this article (quite old, but should give you some ideas) and then see if you can port the suggested approach to a more modern version .

Oh, wait, someone did this already!

And apparently your aren't the first one to ask here, either

Best of luck...

Is file append atomic in UNIX?

A write that's under the size of 'PIPE_BUF' is supposed to be atomic. That should be at least 512 bytes, though it could easily be larger (linux seems to have it set to 4096).

This assume that you're talking all fully POSIX-compliant components. For instance, this isn't true on NFS.

But assuming you write to a log file you opened in 'O_APPEND' mode and keep your lines (including newline) under 'PIPE_BUF' bytes long, you should be able to have multiple writers to a log file without any corruption issues. Any interrupts will arrive before or after the write, not in the middle. If you want file integrity to survive a reboot you'll also need to call fsync(2) after every write, but that's terrible for performance.

Clarification: read the comments and Oz Solomon's answer. I'm not sure that O_APPEND is supposed to have that PIPE_BUF size atomicity. It's entirely possible that it's just how Linux implemented write(), or it may be due to the underlying filesystem's block sizes.



Related Topics



Leave a reply



Submit