Atomically write `bytes` to `dst` via a `tmp` file with full durability. Order of operations (must not change): 1. Create / truncate `tmp` and write `bytes`. 2. `sync_data()` on `tmp` — forces file data pages to stable storage. 3. `rename(tmp, dst)` — atomic on POSIX filesystems. 4. `fsync_directory(parent)` — forces the directory entry durable so the new name survives power loss. `tmp` and `dst
(tmp: &Path, dst: &Path, bytes: &[u8])
| 49 | /// `tmp` and `dst` MUST be in the same directory; otherwise rename is not |
| 50 | /// atomic and the parent fsync won't cover both entries. |
| 51 | pub fn atomic_write_fsync(tmp: &Path, dst: &Path, bytes: &[u8]) -> Result<()> { |
| 52 | let parent = dst.parent().ok_or_else(|| { |
| 53 | WalError::Io(std::io::Error::new( |
| 54 | std::io::ErrorKind::InvalidInput, |
| 55 | "atomic_write_fsync: dst has no parent directory", |
| 56 | )) |
| 57 | })?; |
| 58 | |
| 59 | { |
| 60 | let mut f = fs::File::create(tmp).map_err(WalError::Io)?; |
| 61 | f.write_all(bytes).map_err(WalError::Io)?; |
| 62 | f.sync_data().map_err(WalError::Io)?; |
| 63 | } |
| 64 | |
| 65 | fs::rename(tmp, dst).map_err(WalError::Io)?; |
| 66 | fsync_directory(parent)?; |
| 67 | Ok(()) |
| 68 | } |
| 69 | |
| 70 | /// Atomically swap a directory: `rename(live, backup); rename(staged, live)`, |
| 71 | /// fsyncing the parent directory once both renames have completed. |