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;