linuxatomicsymlinkmvln

Atomically "moving" a directory in Linux, when it is not a symlink


Let's say I have two directories in Linux, /A and /B, a program uses /A, and I want now point /A to /B so that the program now sees /B when reading /A.

This is one way I could do that:

rm -rf /A
mv /B /A

Alternatively, if we do not want to lose /B and are okay with having a symlink in /A:

rm -rf /A
ln -sT /B /A # creating at /A a symlink that points to /B

However, the program could see for a short moment that /A is missing, and crash (which one could argue is the program's fault but hopefully should not invalidate the question).

The question is how one can do this entire process of making /A point to /B's content, atomically, if possible.

A best-voted answer to a similar question suggests using mv -T /B /A, which is atomic, if /A and /B are both symlinks. In this question, I assume /A is originally not a symlink.

While it is easy to create a symlink to /B using ln -sT /B link_to_B. Following with mv -T link_to_B /A would still not work, since /A is still not a symlink.

Creating a link to A with ln -sT /A link_to_A and following with mv -T link_to_B link_to_A does not help, since the program still points to /A not to link_to_A.

Is there any known solution? Thanks.


Solution

  • This isn't possible with regular mv, because it's based on the rename(2) system call, which can't atomically replace a nonempty directory. However Linux offers an extended version, renameat2(2), which has a RENAME_EXCHANGE option that can atomically exchange two pathnames of any types.

    There's a command-line wrapper for it on github, so you can do

    rename-exchange /A /B
    rm -rf /B              # the former /A
    

    Or if you'd rather end up with a symlink:

    ln -s /B foo
    rename-exchange /A foo
    rm -rf foo