OXYGENHUB
◆ v0.1.0 spectra / crates / spectra-core / src / durable.rs
1.3 KiB · Rust · last changed Spectra stands alone: its own identities, signatures and CBOR; projects of any kind of work (#26, #27) 1 day ago source blame history raw
98a0fb9f Andy McCall ↶ 1
//! Writing files so a crash leaves the old content or the new, never half:
2
//! a temporary file in the same directory, flushed, then renamed over.
3
4
use std::io::Write;
5
use std::path::Path;
6
7
fn write(path: &Path, bytes: &[u8], private: bool) -> std::io::Result<()> {
8
    let dir = path.parent().filter(|d| !d.as_os_str().is_empty()).unwrap_or(Path::new("."));
9
    std::fs::create_dir_all(dir)?;
10
    let name = path.file_name().and_then(|n| n.to_str()).unwrap_or("file");
11
    let tmp = dir.join(format!(".{name}.{}.tmp", std::process::id()));
12
    let mut opts = std::fs::OpenOptions::new();
13
    opts.write(true).create(true).truncate(true);
14
    #[cfg(unix)]
15
    {
16
        use std::os::unix::fs::OpenOptionsExt;
17
        opts.mode(if private { 0o600 } else { 0o644 });
18
    }
19
    #[cfg(not(unix))]
20
    let _ = private;
21
    let mut f = opts.open(&tmp)?;
22
    f.write_all(bytes)?;
23
    f.sync_all()?;
24
    drop(f);
25
    std::fs::rename(&tmp, path)?;
26
    if let Ok(d) = std::fs::File::open(dir) {
27
        let _ = d.sync_all();
28
    }
29
    Ok(())
30
}
31
32
pub fn write_atomic(path: &Path, bytes: &[u8]) -> std::io::Result<()> {
33
    write(path, bytes, false)
34
}
35
36
/// As [`write_atomic`], readable by its owner only.
37
pub fn write_atomic_private(path: &Path, bytes: &[u8]) -> std::io::Result<()> {
38
    write(path, bytes, true)
39
}