From 2b59a731eb5099fda4a31922e7b9c44fd9f9ab9d Mon Sep 17 00:00:00 2001 From: Vika Date: Sun, 16 Jul 2023 01:35:24 +0300 Subject: FileStorage: properly fsync() files and directories Total crash-safety. Yank the power cord all you want, your data is going to be safe and sound. (Unless your drive controller lies to you about flushing its caches) --- kittybox-rs/src/database/file/mod.rs | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/kittybox-rs/src/database/file/mod.rs b/kittybox-rs/src/database/file/mod.rs index 842a834..8514717 100644 --- a/kittybox-rs/src/database/file/mod.rs +++ b/kittybox-rs/src/database/file/mod.rs @@ -316,8 +316,8 @@ impl Storage for FileStorage { .parent() .expect("Parent for this directory should always exist") .to_owned(); - if !parent.is_dir() { - tokio::fs::create_dir_all(parent).await?; + if !(spawn_blocking(move || parent.is_dir()).await.unwrap()) { + tokio::fs::create_dir_all(path.parent().unwrap()).await?; } let mut file = tokio::fs::OpenOptions::new() @@ -328,8 +328,10 @@ impl Storage for FileStorage { file.write_all(post.to_string().as_bytes()).await?; file.flush().await?; + file.sync_all().await?; drop(file); tokio::fs::rename(&tempfile, &path).await?; + tokio::fs::File::open(path.parent().unwrap()).await?.sync_all().await?; if let Some(urls) = post["properties"]["url"].as_array() { for url in urls.iter().map(|i| i.as_str().unwrap()) { @@ -369,10 +371,13 @@ impl Storage for FileStorage { { tracing::debug!("Adding to channel list..."); // Add the h-feed to the channel list - let mut path = relative_path::RelativePathBuf::new(); - path.push(user); - path.push("channels"); - let path = path.to_path(&self.root_dir); + let path = { + let mut path = relative_path::RelativePathBuf::new(); + path.push(user); + path.push("channels"); + + path.to_path(&self.root_dir) + }; tracing::debug!("Channels file path: {}", path.display()); let tempfilename = path.with_extension("tmp"); let channel_name = post["properties"]["name"][0] @@ -426,8 +431,10 @@ impl Storage for FileStorage { .write_all(serde_json::to_string(&channels)?.as_bytes()) .await?; tempfile.flush().await?; + tempfile.sync_all().await?; drop(tempfile); - tokio::fs::rename(tempfilename, path).await?; + tokio::fs::rename(tempfilename, &path).await?; + tokio::fs::File::open(path.parent().unwrap()).await?.sync_all().await?; } Ok(()) } @@ -454,8 +461,10 @@ impl Storage for FileStorage { temp.write_all(new_json.to_string().as_bytes()).await?; temp.flush().await?; + temp.sync_all().await?; drop(temp); - tokio::fs::rename(tempfilename, path).await?; + tokio::fs::rename(tempfilename, &path).await?; + tokio::fs::File::open(path.parent().unwrap()).await?.sync_all().await?; (json, new_json) }; @@ -667,8 +676,11 @@ impl Storage for FileStorage { tempfile .write_all(serde_json::to_string(&settings)?.as_bytes()) .await?; + tempfile.flush().await?; + tempfile.sync_all().await?; drop(tempfile); - tokio::fs::rename(temppath, path).await?; + tokio::fs::rename(temppath, &path).await?; + tokio::fs::File::open(path.parent().unwrap()).await?.sync_all().await?; Ok(()) } } -- cgit 1.4.1