feat(ec): remove the .ecsum sidecars when destroying an EC volume

remove_ec_volume_files now clears <base>.ecsum (and any versioned .ecsum.v<N>)
from the data and idx dirs, so a vid reuse can't load a stale sidecar. Mirrors
Go's removeBitrotSidecars.

Claude-Session: https://claude.ai/code/session_015EE9Sc9EvNp8BCVva4RKdo
This commit is contained in:
Chris Lu
2026-06-30 19:36:38 -07:00
parent 11c277765c
commit 4d6fc8047e
@@ -395,6 +395,12 @@ impl DiskLocation {
for i in 0..MAX_SHARD_COUNT {
rm_if_present(format!("{}.ec{:02}", base, i))?;
}
// Remove the bitrot checksum sidecars from both the data and idx dirs.
remove_bitrot_sidecars(&base)?;
if self.idx_directory != self.directory {
remove_bitrot_sidecars(&idx_base)?;
}
Ok(())
}
@@ -1081,6 +1087,49 @@ fn rm_if_present(path: String) -> io::Result<()> {
}
}
/// Remove the bitrot checksum sidecars for a base file name: the legacy
/// `<base>.ecsum` (generation 0) and any versioned `<base>.ecsum.v<N>`.
/// Already-gone is success; returns the first real removal failure (and surfaces
/// a directory-scan error) so a stale sidecar left behind is not reported as
/// cleaned. Mirrors Go's removeBitrotSidecars.
fn remove_bitrot_sidecars(base: &str) -> io::Result<()> {
use crate::storage::erasure_coding::ec_bitrot::BITROT_SIDECAR_EXT;
let rm = |path: std::path::PathBuf| -> io::Result<()> {
match fs::remove_file(&path) {
Err(e) if e.kind() != io::ErrorKind::NotFound => Err(e),
_ => Ok(()),
}
};
let mut first_err: Option<io::Error> = None;
let mut record = |res: io::Result<()>| {
if let Err(e) = res {
if first_err.is_none() {
first_err = Some(e);
}
}
};
record(rm(format!("{}{}", base, BITROT_SIDECAR_EXT).into()));
let path = std::path::Path::new(base);
if let (Some(parent), Some(fname)) = (path.parent(), path.file_name()) {
let prefix = format!("{}{}.v", fname.to_string_lossy(), BITROT_SIDECAR_EXT);
match fs::read_dir(parent) {
Ok(entries) => {
for entry in entries.flatten() {
if entry.file_name().to_string_lossy().starts_with(&prefix) {
record(rm(entry.path()));
}
}
}
Err(e) if e.kind() != io::ErrorKind::NotFound => record(Err(e)),
Err(_) => {}
}
}
match first_err {
Some(e) => Err(e),
None => Ok(()),
}
}
fn ec_data_shards_from_vif(directory: &str, idx_directory: &str, collection: &str, vid: VolumeId) -> usize {
for dir in [directory, idx_directory] {
let vif = format!("{}.vif", volume_file_name(dir, collection, vid));