Filesystems & Storage

rename-for-atomic-replace

Say you want to overwrite an important config file with new contents. If you open it and write the new bytes over the old ones and the power fails halfway, you are left with a file that is neither the old version nor the new one - a corrupted mixture, possibly truncated, possibly half-written. There is a standard trick to make replacing a file all-or-nothing, so a reader always sees either the complete old file or the complete new file and never a torn in-between. It is the write-to-a-temp-file-then-rename pattern, the most important atomicity tool an ordinary application has.

The pattern has a precise recipe. (1) Write the entire new contents to a brand-new temporary file in the same directory (same directory matters, because rename is only atomic within one filesystem). (2) fsync() that temp file so its data is durably on disk - skip this and a crash can leave the renamed file present but empty or partial. (3) rename() the temp file onto the final name. The POSIX rename() call is atomic: at every instant the target name refers to either the old inode or the new one, never to a half-written file, and it also unlinks the old target in the same atomic step. (4) For full durability of the name change itself, fsync() the parent directory afterward, so the new directory entry survives a crash. A reader who opens the file at any moment gets a coherent version; there is no window where the name points at garbage.

Why it matters: this is how editors, package managers, and config tools safely replace files, and why you so often see a stray temp file appear and vanish during a save. The honesty points are sharp. rename() is atomic only within a single filesystem - rename across a mount boundary is not a rename at all but a copy-and-delete, which is not atomic. Skipping the fsync on the temp file before the rename is a real and common bug: the rename can be made durable before the file's data is, so a crash leaves a correctly-named but empty file. And atomic replace gives the new file a new inode, which breaks any hard links to the old one and changes the inode number - sometimes a surprise to programs watching the file.

int fd = open("config.tmp", O_WRONLY|O_CREAT|O_TRUNC, 0644); write(fd, new_data, n); fsync(fd); // 1. temp file's DATA is durable close(fd); rename("config.tmp", "config"); // 2. atomic swap: readers see old OR new, never torn fsync(dirfd); // 3. make the name change itself durable

fsync the temp file, then rename atomically over the target, then fsync the directory - a complete-or-old replace.

rename() is atomic only WITHIN one filesystem; across a mount point it degrades to a non-atomic copy-and-delete. The classic bug is renaming before fsync()ing the temp file's data, which can leave a correctly-named but empty file after a crash. Note also that the replaced file gets a new inode, breaking hard links to the old one.

Also called
atomic file replacewrite-temp-then-renameatomic save pattern原子檔案替換寫暫存再更名