9

I have two files, A and B, each with its own content.

I would like to swap these two files, so A would become B, and B would become A. But I would like to do with a guaranty that no other process will find these two files in an inconsistent state, nor any process will find any of those files missing, even for a short while. So, as a side operation, I would also like to have a guaranty that if anything would go wrong during the operation, nothing will be changed (kind of like a transaction I guess).

On OS X there is a exchangedata() function, so I guess I'm looking for a Linux equivalent of it, or at least an equivalent method for doing atomic file swap.

antonone
  • 2,045
  • 1
  • 25
  • 35
  • @Frédéric Hamidi - the answer to the question you've posted doesn't meet my criteria at all, so I guess it's not a duplicate. – antonone Jan 09 '15 at 14:00
  • I thought the answers there would suit you, but I guess you don't want to leave a temporary file around if something goes wrong? – Frédéric Hamidi Jan 09 '15 at 14:03
  • 2
    Yes, as well as I would like to have a guaranty that I won't remove the original file. If first move will succeed, I will effectively remove the file. Then, if second move will fail, I will have to rollback. If rollback also fails, I will be in trouble for destroying the systems of the customers ;) – antonone Jan 09 '15 at 14:05

2 Answers2

18

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.

tux3
  • 7,171
  • 6
  • 39
  • 51
  • The kernel documentation document that describes this syscall can be found [here](https://man7.org/linux/man-pages/man2/rename.2.html). – senden9 Nov 08 '21 at 09:21
  • By the way, `RENAME_EXCHANGE` was added to the Linux kernel in v3.15 according to [SourceDigger](https://sourcedigger.io/linux?q=RENAME_EXCHANGE) – Wazzaps Oct 10 '22 at 17:07
2

I depends on what you mean by "inconsistent state". If it is acceptable for there to be a period of time during which the two files are identical, then you can simply do:

ln A C
ln B D
ln -f D A  
# now, A and B have the same content
ln -f C B

It also depends on the behavior you want for processes that already have the file opened. Remember that paths are not files, but merely links to a file, so if process 1 opens a file via the path 'A', and then you swap the names A and B, process 1 will still have the file opened that was referred to by the name A.

William Pursell
  • 204,365
  • 48
  • 270
  • 300
  • 1
    Explanations for the downvotes are welcome. This solves the question as asked, with the minor caveat of the ambiguity in the question which is addressed. – William Pursell Jan 09 '15 at 19:50
  • 5
    I did some experiments, and it seems that using the `-f` flag fails to meet with the 'atomicity' constraint, as the `ln` tool uses `unlink` to remove the destination path before creating a link with `link` function. So `ln -f` is not really atomic, there is a (very brief, but still) time window when the file doesn't exist. Having this in mind, simply swapping by renaming using a temporary file name seems to do the same thing similarly. – antonone Jan 14 '15 at 12:15