atomic_write/
lib.rs

1use nix::{
2    fcntl::{open, OFlag},
3    sys::stat::Mode,
4    unistd::{close, fsync, unlink, write},
5};
6use std::path::Path;
7
8mod error;
9pub use self::error::Error;
10
11pub fn atomic_write(path: impl AsRef<str>, contents: impl AsRef<[u8]>) -> Result<(), Error> {
12    let original_path = path.as_ref();
13    let mut tmp_path = original_path.to_string();
14    tmp_path.push_str(".tmp~");
15
16    let tmp_path = Path::new(&tmp_path);
17
18    match unlink(tmp_path) {
19        Ok(()) | Err(nix::Error::Sys(nix::errno::Errno::ENOENT)) => {}
20        Err(e) => return Err(e.into()),
21    }
22
23    let fd = open(
24        tmp_path,
25        OFlag::O_RDWR | OFlag::O_CREAT | OFlag::O_TRUNC,
26        Mode::S_IRUSR
27            | Mode::S_IWUSR
28            | Mode::S_IRGRP
29            | Mode::S_IWGRP
30            | Mode::S_IROTH
31            | Mode::S_IWOTH,
32    )?;
33
34    write(fd, contents.as_ref())?;
35
36    fsync(fd)?;
37    close(fd)?;
38    std::fs::rename(tmp_path, Path::new(&original_path))?;
39
40    Ok(())
41}