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}