//! Writing files so a crash leaves the old content or the new, never half: //! a temporary file in the same directory, flushed, then renamed over. use std::io::Write; use std::path::Path; fn write(path: &Path, bytes: &[u8], private: bool) -> std::io::Result<()> { let dir = path.parent().filter(|d| !d.as_os_str().is_empty()).unwrap_or(Path::new(".")); std::fs::create_dir_all(dir)?; let name = path.file_name().and_then(|n| n.to_str()).unwrap_or("file"); let tmp = dir.join(format!(".{name}.{}.tmp", std::process::id())); let mut opts = std::fs::OpenOptions::new(); opts.write(true).create(true).truncate(true); #[cfg(unix)] { use std::os::unix::fs::OpenOptionsExt; opts.mode(if private { 0o600 } else { 0o644 }); } #[cfg(not(unix))] let _ = private; let mut f = opts.open(&tmp)?; f.write_all(bytes)?; f.sync_all()?; drop(f); std::fs::rename(&tmp, path)?; if let Ok(d) = std::fs::File::open(dir) { let _ = d.sync_all(); } Ok(()) } pub fn write_atomic(path: &Path, bytes: &[u8]) -> std::io::Result<()> { write(path, bytes, false) } /// As [`write_atomic`], readable by its owner only. pub fn write_atomic_private(path: &Path, bytes: &[u8]) -> std::io::Result<()> { write(path, bytes, true) }