about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVika <vika@fireburn.ru>2023-07-16 01:35:24 +0300
committerVika <vika@fireburn.ru>2023-07-17 01:53:42 +0300
commit2b59a731eb5099fda4a31922e7b9c44fd9f9ab9d (patch)
tree319bfb399323dda8d3eebc6a5ea9637c0cf240bf
parentb63d1204be8b9e264a645352c594e136f248ea3d (diff)
downloadkittybox-2b59a731eb5099fda4a31922e7b9c44fd9f9ab9d.tar.zst
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)
-rw-r--r--kittybox-rs/src/database/file/mod.rs30
1 files 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(())
     }
 }