safemesh_crdt/
persistence.rs1use std::{
7 fs::{self, File},
8 io::{self, Write},
9 path::Path,
10};
11
12pub(crate) fn replace(path: &Path, bytes: &[u8]) -> io::Result<()> {
13 let parent = File::open(path.parent().ok_or_else(|| io::Error::other("no parent"))?)?;
14 checkpoint(1)?;
15 let temporary = path.with_extension("tmp");
16 let mut file = File::create(&temporary)?;
17 #[cfg(test)]
20 if FAULT.with(|fault| fault.get() == (2, false)) {
21 file.write_all(&bytes[..bytes.len() / 2])?;
22 }
23 checkpoint(2)?;
24 file.write_all(bytes)?;
25 checkpoint(3)?;
26 file.sync_all()?;
27 checkpoint(4)?;
28 fs::rename(&temporary, path)?;
29 checkpoint(5)?;
30 parent.sync_all()?;
31 checkpoint(6)?;
32 Ok(())
33}
34
35pub(crate) fn checkpoint(_boundary: u8) -> io::Result<()> {
36 #[cfg(test)]
37 FAULT.with(|fault| {
38 let (boundary, crash) = fault.get();
39 if boundary == _boundary {
40 if crash {
41 std::process::exit(77);
42 }
43 return Err(io::Error::other("injected uncertain I/O"));
44 }
45 Ok(())
46 })?;
47 Ok(())
48}
49
50#[cfg(test)]
51std::thread_local! {
52 pub(crate) static FAULT: std::cell::Cell<(u8, bool)> = const { std::cell::Cell::new((0, false)) };
53}