Add FilesystemStoreV2Error for v1 data detection
What changed, and why it matters
This commit is a code-quality improvement, not a security fix. It changes how a new version of a file-storage component reports that it has found leftover data from the older version. Previously it returned a generic I/O error; now it returns a dedicated, clearly typed error so callers can tell the two situations apart. No vulnerability is patched and no exploit is prevented.
No security action required. Treat as a normal API change; update downstream callers that match on FilesystemStoreV2::new errors to handle FilesystemStoreV2Error instead of io::Error.
Security signals we found
No memory-safety, cryptographic, or authorization changes
Error-type refactoring only
No input validation, parsing, or serialization logic changed
No patch of a known vulnerability
Evidence from the diff
The patch introduces a new public error enum, FilesystemStoreV2Error, with variants V1DataDetected(PathBuf) and Io(io::Error). FilesystemStoreV2::new now returns Result
Changed components
lightning-persister/src/fs_store/v2.rsInspect captured patch +52 / −16
diff --git a/lightning-persister/src/fs_store/v2.rs b/lightning-persister/src/fs_store/v2.rs
index 773b22a..2f79cae 100644
--- a/lightning-persister/src/fs_store/v2.rs
+++ b/lightning-persister/src/fs_store/v2.rs
@@ -10,6 +10,7 @@ use lightning::util::persist::{
use std::fs;
use std::path::PathBuf;
use std::time::UNIX_EPOCH;
+use std::{error, fmt, io};
#[cfg(feature = "tokio")]
use core::future::Future;
@@ -17,6 +18,48 @@ use core::future::Future;
use lightning::util::persist::{KVStore, PaginatedKVStore};
use std::sync::Arc;
+/// An error returned when constructing a [`FilesystemStoreV2`].
+#[derive(Debug)]
+pub enum FilesystemStoreV2Error {
+ /// The data directory contains a file at the top level, indicating it was previously used
+ /// by [`FilesystemStore`] (v1). Contains the path of the offending file.
+ ///
+ /// [`FilesystemStore`]: crate::fs_store::v1::FilesystemStore
+ V1DataDetected(PathBuf),
+ /// An I/O error occurred while inspecting the data directory.
+ Io(io::Error),
+}
+
+impl fmt::Display for FilesystemStoreV2Error {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Self::V1DataDetected(path) => write!(
+ f,
+ "Found file `{}` in the top-level data directory. \
+ This indicates the directory was previously used by FilesystemStore (v1). \
+ Please migrate your data or use a different directory.",
+ path.display()
+ ),
+ Self::Io(err) => write!(f, "{}", err),
+ }
+ }
+}
+
+impl error::Error for FilesystemStoreV2Error {
+ fn source(&self) -> Option<&(dyn error::Error + 'static)> {
+ match self {
+ Self::V1DataDetected(_) => None,
+ Self::Io(err) => Some(err),
+ }
+ }
+}
+
+impl From<io::Error> for FilesystemStoreV2Error {
+ fn from(err: io::Error) -> Self {
+ Self::Io(err)
+ }
+}
+
/// A [`KVStore`] and [`KVStoreSync`] implementation that writes to and reads from the file system.
///
/// This is version 2 of the filesystem store which provides:
@@ -53,25 +96,18 @@ pub struct FilesystemStoreV2 {
impl FilesystemStoreV2 {
/// Constructs a new [`FilesystemStoreV2`].
///
- /// Returns an error if the data directory already exists and contains files at the top level,
- /// which would indicate it was previously used by a [`FilesystemStore`] (v1). The v2 store
- /// expects only directories (namespaces) at the top level.
+ /// Returns [`FilesystemStoreV2Error::V1DataDetected`] if the data directory already exists
+ /// and contains files at the top level, which would indicate it was previously used by a
+ /// [`FilesystemStore`] (v1). The v2 store expects only directories (namespaces) at the top
+ /// level.
///
/// [`FilesystemStore`]: crate::fs_store::v1::FilesystemStore
- pub fn new(data_dir: PathBuf) -> std::io::Result<Self> {
+ pub fn new(data_dir: PathBuf) -> Result<Self, FilesystemStoreV2Error> {
if data_dir.exists() {
for entry in fs::read_dir(&data_dir)? {
let entry = entry?;
if entry.file_type()?.is_file() {
- return Err(std::io::Error::new(
- std::io::ErrorKind::InvalidData,
- format!(
- "Found file `{}` in the top-level data directory. \
- This indicates the directory was previously used by FilesystemStore (v1). \
- Please migrate your data or use a different directory.",
- entry.path().display()
- ),
- ));
+ return Err(FilesystemStoreV2Error::V1DataDetected(entry.path()));
}
}
}
@@ -667,10 +703,10 @@ mod tests {
// V2 construction should fail
match FilesystemStoreV2::new(temp_path.clone()) {
- Err(err) => {
- assert_eq!(err.kind(), std::io::ErrorKind::InvalidData);
- assert!(err.to_string().contains("FilesystemStore (v1)"));
+ Err(FilesystemStoreV2Error::V1DataDetected(path)) => {
+ assert_eq!(path, temp_path.join("some_key"));
},
+ Err(err) => panic!("Expected V1DataDetected, got {:?}", err),
Ok(_) => panic!("Expected error for directory with top-level files"),
}
Why this scored 19/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.