Skip to main content

scv_client/
fs.rs

1//! Private instance files: SCV's state, records, and credentials are
2//! written whole, readable only by the user, and survive a crash.
3
4use std::io::{self, Write as _};
5use std::path::Path;
6
7/// Replace `path` with `bytes` atomically, as a file only the user can read.
8///
9/// The bytes go to a temporary file in the same directory, which is synced
10/// and renamed over `path`; the directory is then synced so the rename
11/// itself survives a crash. A reader sees the old file or the new one, never
12/// a partial write. The directory must exist; callers create it with the
13/// privacy their data needs.
14pub fn replace_private(path: &Path, bytes: &[u8]) -> io::Result<()> {
15    let parent = path
16        .parent()
17        .ok_or_else(|| io::Error::other(format!("{} has no parent", path.display())))?;
18    // Named temporary files are created with mode 0600.
19    let mut temporary = tempfile::NamedTempFile::new_in(parent)?;
20    temporary.write_all(bytes)?;
21    temporary.as_file().sync_all()?;
22    temporary.persist(path).map_err(|error| error.error)?;
23    sync_directory(parent)
24}
25
26/// Flush a directory's entries, so files created, renamed, or removed in it
27/// persist. A no-op where directories cannot be opened (not Unix).
28pub fn sync_directory(directory: &Path) -> io::Result<()> {
29    #[cfg(unix)]
30    std::fs::File::open(directory)?.sync_all()?;
31    #[cfg(not(unix))]
32    let _ = directory;
33    Ok(())
34}
35
36#[cfg(test)]
37mod tests;