diff --git a/lightning-background-processor/src/lib.rs b/lightning-background-processor/src/lib.rs index e1fa8144ac4..b95eb04ae44 100644 --- a/lightning-background-processor/src/lib.rs +++ b/lightning-background-processor/src/lib.rs @@ -717,6 +717,14 @@ mod tests { self.filesystem_persister.persist(key, object) } + + fn unpersist(&self, key: &str) -> std::io::Result { + if key == "manager" || key == "network_graph" || key == "scorer" { + return Err(std::io::Error::new(std::io::ErrorKind::Other, "Refusing to unpersist this key")); + } + + self.filesystem_persister.unpersist(key) + } } fn get_full_filepath(filepath: String, filename: String) -> String { diff --git a/lightning-persister/src/lib.rs b/lightning-persister/src/lib.rs index b277c30ca8c..a183d5ebac6 100644 --- a/lightning-persister/src/lib.rs +++ b/lightning-persister/src/lib.rs @@ -28,8 +28,8 @@ use std::io::Cursor; use std::ops::Deref; use std::path::{Path, PathBuf}; -/// FilesystemPersister persists channel data on disk, where each channel's -/// data is stored in a file named after its funding outpoint. +/// `FilesystemPersister` persists a given `Writeable` object on disk. In the case of channel data, +/// it is stored in a file named after its funding outpoint. /// /// Warning: this module does the best it can with calls to persist data, but it /// can only guarantee that the data is passed to the drive. It is up to the @@ -39,23 +39,22 @@ use std::path::{Path, PathBuf}; /// persistent. /// Corollary: especially when dealing with larger amounts of money, it is best /// practice to have multiple channel data backups and not rely only on one -/// FilesystemPersister. +/// `FilesystemPersister`. pub struct FilesystemPersister { - path_to_channel_data: String, + path_to_data: String, } impl FilesystemPersister { - /// Initialize a new FilesystemPersister and set the path to the individual channels' - /// files. - pub fn new(path_to_channel_data: String) -> Self { + /// Initialize a new FilesystemPersister and set the parent path of the individual files. + pub fn new(path_to_data: String) -> Self { return Self { - path_to_channel_data, + path_to_data, } } /// Get the directory which was provided when this persister was initialized. pub fn get_data_dir(&self) -> String { - self.path_to_channel_data.clone() + self.path_to_data.clone() } /// Read `ChannelMonitor`s from disk. @@ -64,7 +63,7 @@ impl FilesystemPersister { ) -> Result)>, std::io::Error> where K::Target: KeysInterface + Sized, { - let mut path = PathBuf::from(&self.path_to_channel_data); + let mut path = PathBuf::from(&self.path_to_data); path.push("monitors"); if !Path::new(&path).exists() { return Ok(Vec::new()); @@ -124,10 +123,16 @@ impl FilesystemPersister { impl KVStorePersister for FilesystemPersister { fn persist(&self, key: &str, object: &W) -> std::io::Result<()> { - let mut dest_file = PathBuf::from(self.path_to_channel_data.clone()); + let mut dest_file = PathBuf::from(self.path_to_data.clone()); dest_file.push(key); util::write_to_file(dest_file, object) } + + fn unpersist(&self, key: &str) -> std::io::Result { + let mut dest_file = PathBuf::from(self.path_to_data.clone()); + dest_file.push(key); + util::delete_file(dest_file) + } } #[cfg(test)] @@ -158,7 +163,7 @@ mod tests { fn drop(&mut self) { // We test for invalid directory names, so it's OK if directory removal // fails. - match fs::remove_dir_all(&self.path_to_channel_data) { + match fs::remove_dir_all(&self.path_to_data) { Err(e) => println!("Failed to remove test persister directory: {}", e), _ => {} } @@ -242,7 +247,7 @@ mod tests { #[test] fn test_readonly_dir_perm_failure() { let persister = FilesystemPersister::new("test_readonly_dir_perm_failure".to_string()); - fs::create_dir_all(&persister.path_to_channel_data).unwrap(); + fs::create_dir_all(&persister.path_to_data).unwrap(); // Set up a dummy channel and force close. This will produce a monitor // that we can then use to test persistence. @@ -260,7 +265,7 @@ mod tests { // Set the persister's directory to read-only, which should result in // returning a permanent failure when we then attempt to persist a // channel update. - let path = &persister.path_to_channel_data; + let path = &persister.path_to_data; let mut perms = fs::metadata(path).unwrap().permissions(); perms.set_readonly(true); fs::set_permissions(path, perms).unwrap(); diff --git a/lightning-persister/src/util.rs b/lightning-persister/src/util.rs index 4adbb33e5b2..f1a109d6b9d 100644 --- a/lightning-persister/src/util.rs +++ b/lightning-persister/src/util.rs @@ -77,6 +77,26 @@ pub(crate) fn write_to_file(dest_file: PathBuf, data: &W) -> std:: Ok(()) } +pub(crate) fn delete_file(dest_file: PathBuf) -> std::io::Result { + if !dest_file.is_file() { + return Ok(false) + } + + fs::remove_file(&dest_file)?; + let parent_directory = dest_file.parent().unwrap(); + let dir_file = fs::OpenOptions::new().read(true).open(parent_directory)?; + #[cfg(not(target_os = "windows"))] + { + unsafe { libc::fsync(dir_file.as_raw_fd()); } + } + + if dest_file.is_file() { + return Err(std::io::Error::new(std::io::ErrorKind::Other, "Unpersisting key failed")); + } + + return Ok(true) +} + #[cfg(test)] mod tests { use lightning::util::ser::{Writer, Writeable}; diff --git a/lightning/src/util/persist.rs b/lightning/src/util/persist.rs index 522c1c92ac3..5224938877d 100644 --- a/lightning/src/util/persist.rs +++ b/lightning/src/util/persist.rs @@ -21,8 +21,12 @@ use super::{logger::Logger, ser::Writeable}; /// and [`Persist`] traits. It uses "manager", "network_graph", /// and "monitors/{funding_txo_id}_{funding_txo_index}" for keys. pub trait KVStorePersister { - /// Persist the given writeable using the provided key + /// Persist the given writeable using the provided key. fn persist(&self, key: &str, object: &W) -> io::Result<()>; + + /// Unpersist (i.e., remove) the writeable previously persisted under the provided key. + /// Returns `true` if the key was present, and `false` otherwise. + fn unpersist(&self, key: &str) -> io::Result; } /// Trait that handles persisting a [`ChannelManager`], [`NetworkGraph`], and [`WriteableScore`] to disk.