diff options
| author | David Lönnhager <david.l@mullvad.net> | 2023-03-27 15:34:46 +0200 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2023-03-29 15:50:34 +0200 |
| commit | d630325c55920cb4c294812e40221fea7e6d7510 (patch) | |
| tree | ed9460a6213e9402d7a719b8a73c43af3175dfab /mullvad-fs | |
| parent | 2616889e0ab64afb98bf67056b4e5c977608b660 (diff) | |
| download | mullvadvpn-d630325c55920cb4c294812e40221fea7e6d7510.tar.xz mullvadvpn-d630325c55920cb4c294812e40221fea7e6d7510.zip | |
Remove getters and setters from SettingsPersister
Diffstat (limited to 'mullvad-fs')
| -rw-r--r-- | mullvad-fs/Cargo.toml | 15 | ||||
| -rw-r--r-- | mullvad-fs/src/lib.rs | 73 |
2 files changed, 88 insertions, 0 deletions
diff --git a/mullvad-fs/Cargo.toml b/mullvad-fs/Cargo.toml new file mode 100644 index 0000000000..b2ae4176e0 --- /dev/null +++ b/mullvad-fs/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "mullvad-fs" +version = "0.0.0" +authors = ["Mullvad VPN"] +description = "File utility library" +license = "GPL-3.0" +edition = "2021" +publish = false + +[dependencies] +log = "0.4" +tokio = { version = "1.8", features = ["fs"] } +uuid = { version = "0.8", features = ["v4"] } + +talpid-types = { path = "../talpid-types" } diff --git a/mullvad-fs/src/lib.rs b/mullvad-fs/src/lib.rs new file mode 100644 index 0000000000..22c6a80588 --- /dev/null +++ b/mullvad-fs/src/lib.rs @@ -0,0 +1,73 @@ +use std::{ + ops::{Deref, DerefMut}, + path::{Path, PathBuf}, +}; +use talpid_types::ErrorExt; +use tokio::{fs, io}; + +/// Stores content in a temporary file before moving it to the +/// final destination, ensuring that consumers of the file never +/// end up with partial content. Must be moved with `finalize`. +pub struct AtomicFile { + file: Option<fs::File>, + temp_path: PathBuf, + target_path: PathBuf, +} + +impl AtomicFile { + pub async fn new<P: Into<PathBuf>>(target_path: P) -> io::Result<Self> { + let target_path = target_path.into(); + let temp_path = target_path.with_file_name(uuid::Uuid::new_v4().to_string()); + Ok(Self { + file: Some(fs::File::create(&temp_path).await?), + temp_path, + target_path, + }) + } + + /// Flushes and moves the file to `self.target_path`, replacing it if it exists. + pub async fn finalize(mut self) -> io::Result<()> { + let result = async { + let file = self.file.take().unwrap(); + file.sync_all().await?; + let std_file = file.into_std().await; + let _ = tokio::task::spawn_blocking(move || drop(std_file)).await; + fs::rename(&self.temp_path, &self.target_path).await + } + .await; + if result.is_err() { + let _ = tokio::task::spawn_blocking(move || try_remove_file(&self.temp_path)).await; + } + result + } +} + +impl Drop for AtomicFile { + fn drop(&mut self) { + if self.file.is_some() { + log::error!("{} was not finalized", self.target_path.display()); + try_remove_file(&self.temp_path); + } + } +} + +fn try_remove_file(temp_path: &Path) { + if let Err(error) = std::fs::remove_file(temp_path) { + let msg = format!("Failed to delete temp file: {}", temp_path.display()); + log::warn!("{}", error.display_chain_with_msg(&msg)); + } +} + +impl Deref for AtomicFile { + type Target = fs::File; + + fn deref(&self) -> &Self::Target { + self.file.as_ref().unwrap() + } +} + +impl DerefMut for AtomicFile { + fn deref_mut(&mut self) -> &mut Self::Target { + self.file.as_mut().unwrap() + } +} |
