diff options
author | Michael Neumann <mneumann@ntecs.de> | 2021-04-14 19:07:03 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-04-14 19:07:03 +0200 |
commit | 58a3a1cf087ba06d1df79ca1169174a345b7e23a (patch) | |
tree | 12b8a431315f8767e8ce697148b5f84755e458dc /tedge_config | |
parent | db1497261309254b5cc7dc5b6178e59cc231f7d7 (diff) |
Replace crate `tempfile` with custom code - Fixes #169 (#181)
- Write our own `atomically_write_file` function instead of using
`tempfile`.
- Save data to `tedge.toml.tmp` and then atomically renaming it to the
final destination `tedge.toml`.
- By storing the temporary file in the same directory as the destination
file we get around issues with cross-device link errors (EXDEV), which
occur when the tempfile and the destination file would be on different
file systems (`/tmp` on FreeBSD is a separate file system from `/home` or
`/`).
- Only use POSIX functionality explosed by Rust's `std`, so it should
really be cross-platform.
- Tested on FreeBSD 12.2
Fixes bug: #169
Diffstat (limited to 'tedge_config')
-rw-r--r-- | tedge_config/Cargo.toml | 2 | ||||
-rw-r--r-- | tedge_config/src/tedge_config_location.rs | 5 | ||||
-rw-r--r-- | tedge_config/src/tedge_config_repository.rs | 43 |
3 files changed, 37 insertions, 13 deletions
diff --git a/tedge_config/Cargo.toml b/tedge_config/Cargo.toml index 311c1f09..601d0d27 100644 --- a/tedge_config/Cargo.toml +++ b/tedge_config/Cargo.toml @@ -8,7 +8,7 @@ edition = "2018" toml = "0.5" thiserror = "1.0" serde = { version = "1.0", features = ["derive"] } -tempfile = "3.2" [dev-dependencies] assert_matches = "1.4" +tempfile = "3.2" diff --git a/tedge_config/src/tedge_config_location.rs b/tedge_config/src/tedge_config_location.rs index 1ca2ff28..a6f9a844 100644 --- a/tedge_config/src/tedge_config_location.rs +++ b/tedge_config/src/tedge_config_location.rs @@ -2,6 +2,7 @@ use std::path::{Path, PathBuf}; const DEFAULT_ETC_PATH: &str = "/etc"; const TEDGE_CONFIG_FILE: &str = "tedge.toml"; +const TEDGE_CONFIG_FILE_TMP: &str = "tedge.toml.tmp"; /// Information about where `tedge.toml` is located. /// @@ -48,6 +49,10 @@ impl TEdgeConfigLocation { pub fn tedge_config_file_path(&self) -> &Path { &self.tedge_config_file_path } + + pub fn temporary_tedge_config_file_path(&self) -> impl AsRef<Path> { + self.tedge_config_root_path.join(TEDGE_CONFIG_FILE_TMP) + } } #[test] diff --git a/tedge_config/src/tedge_config_repository.rs b/tedge_config/src/tedge_config_repository.rs index c7fdd2b2..59df9a7a 100644 --- a/tedge_config/src/tedge_config_repository.rs +++ b/tedge_config/src/tedge_config_repository.rs @@ -1,7 +1,7 @@ use crate::*; +use std::fs; use std::io::Write; -use std::path::PathBuf; -use tempfile::NamedTempFile; +use std::path::{Path, PathBuf}; /// TEdgeConfigRepository is resposible for loading and storing TEdgeConfig entities. /// @@ -25,23 +25,42 @@ impl ConfigRepository<TEdgeConfig> for TEdgeConfigRepository { Ok(config) } - // XXX: Explicitly set the file permissions in this function and file ownership! + // TODO: Explicitly set the file permissions in this function and file ownership! fn store(&self, config: TEdgeConfig) -> Result<(), TEdgeConfigError> { let toml = toml::to_string_pretty(&config.data)?; - let mut file = NamedTempFile::new()?; - file.write_all(toml.as_bytes())?; - // Create $HOME/.tedge or /etc/tedge directory if it does not exist + // Create `$HOME/.tedge` or `/etc/tedge` directory in case it does not exist yet if !self.config_location.tedge_config_root_path.exists() { - let () = std::fs::create_dir(self.config_location.tedge_config_root_path())?; - // XXX: Correctly assign permissions + let () = fs::create_dir(self.config_location.tedge_config_root_path())?; } - match file.persist(self.config_location.tedge_config_file_path()) { - Ok(_) => Ok(()), - Err(err) => Err(err.error.into()), - } + let () = atomically_write_file( + self.config_location.temporary_tedge_config_file_path(), + self.config_location.tedge_config_file_path(), + toml.as_bytes(), + )?; + Ok(()) + } +} + +fn atomically_write_file( + tempfile: impl AsRef<Path>, + dest: impl AsRef<Path>, + content: &[u8], +) -> std::io::Result<()> { + let mut file = fs::OpenOptions::new() + .write(true) + .create_new(true) + .open(tempfile.as_ref())?; + if let Err(err) = file.write_all(content) { + let _ = fs::remove_file(tempfile); + return Err(err); + } + if let Err(err) = fs::rename(tempfile.as_ref(), dest) { + let _ = fs::remove_file(tempfile); + return Err(err); } + Ok(()) } impl TEdgeConfigRepository { |