diff --git a/src/config/datastore.rs b/src/config/datastore.rs index 6bde349a..264f0899 100644 --- a/src/config/datastore.rs +++ b/src/config/datastore.rs @@ -4,6 +4,7 @@ use std::fs::{OpenOptions}; use std::io::{Read, Write}; //use std::sync::Arc; +use crate::tools; use crate::api::schema::*; use crate::section_config::*; @@ -71,9 +72,7 @@ pub fn save_config(config: &SectionConfigData) -> Result<(), Error> { //fixme: compute and compare digest - //fixme: impl file_set_contents() (atomic write) - file.set_len(0)?; - file.write_all(raw.as_bytes())?; + tools::file_set_contents(DATASTORE_CFG_FILENAME, raw.as_bytes(), None)?; Ok(()) } diff --git a/src/lib.rs b/src/lib.rs index c37a5015..4731de06 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,6 +10,8 @@ pub mod static_map; /// hierarchy of API entries, and provides ways to find an API /// definition by path. +pub mod tools; + #[macro_use] pub mod api { diff --git a/src/tools.rs b/src/tools.rs new file mode 100644 index 00000000..d5637384 --- /dev/null +++ b/src/tools.rs @@ -0,0 +1,50 @@ +use failure::*; +use nix::unistd; +use nix::sys::stat; + +use std::fs::File; +use std::io::Write; +use std::path::Path; + +pub fn file_set_contents>( + path: P, + data: &[u8], + perm: Option, +) -> Result<(), Error> { + + let path = path.as_ref(); + + let mut template = path.to_owned(); + template.set_extension("tmp_XXXXXX"); + let (fd, tmp_path) = match unistd::mkstemp(&template) { + Ok((fd, path)) => (fd, path), + Err(err) => bail!("mkstemp {:?} failed: {}", template, err), + }; + + let tmp_path = tmp_path.as_path(); + + let mode : stat::Mode = stat::Mode::from( + stat::Mode::S_IRUSR | stat::Mode::S_IWUSR | + stat::Mode::S_IRGRP | stat::Mode::S_IROTH + ); + + if let Err(err) = stat::fchmod(fd, mode) { + let _ = unistd::unlink(tmp_path); + bail!("fchmod {:?} failed: {}", tmp_path, err); + } + + use std::os::unix::io::FromRawFd; + let mut file = unsafe { File::from_raw_fd(fd) }; + + if let Err(err) = file.write_all(data) { + let _ = unistd::unlink(tmp_path); + bail!("write failed: {}", err); + } + + if let Err(err) = std::fs::rename(tmp_path, path) { + let _ = unistd::unlink(tmp_path); + bail!("Atomic rename failed for file {:?} - {}", path, err); + } + + Ok(()) +}