summaryrefslogtreecommitdiffstats
path: root/tedge_config
diff options
context:
space:
mode:
authorMichael Neumann <mneumann@ntecs.de>2021-04-14 19:07:03 +0200
committerGitHub <noreply@github.com>2021-04-14 19:07:03 +0200
commit58a3a1cf087ba06d1df79ca1169174a345b7e23a (patch)
tree12b8a431315f8767e8ce697148b5f84755e458dc /tedge_config
parentdb1497261309254b5cc7dc5b6178e59cc231f7d7 (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.toml2
-rw-r--r--tedge_config/src/tedge_config_location.rs5
-rw-r--r--tedge_config/src/tedge_config_repository.rs43
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 {