pool: gc: remove empty directories under link_dir

garbage collection currently is quite aggressive in removing all files
under the link_dir, which are not a hard-link to a checksum file.
removing directories that remain empty below the link_dir should thus
not too dangerous.

without this patch, removing a snapshot on a mirror, running gc there,
and syncing everything to a medium, leaves the medium with an
hierarchy of empty directories below the removed snapshot (the files
get cleaned up the directories remain).

using WalkDir::content_first() seems better than to check for
emptiness after each file-removal [0]

[0] https://docs.rs/walkdir/latest/walkdir/struct.WalkDir.html#method.contents_first

Signed-off-by: Stoiko Ivanov <s.ivanov@proxmox.com>
This commit is contained in:
Stoiko Ivanov 2024-07-09 12:47:04 +02:00 committed by Fabian Grünbichler
parent 2089e5d4b0
commit 24c697e8cd

View File

@ -451,6 +451,7 @@ impl PoolLockGuard<'_> {
/// Run a garbage collection, removing /// Run a garbage collection, removing
/// - any checksum files that have no links outside of `pool_dir` /// - any checksum files that have no links outside of `pool_dir`
/// - any files in `link_dir` that have no corresponding checksum files /// - any files in `link_dir` that have no corresponding checksum files
/// - any empty directories below `link_dir` remaining after the file removal
pub(crate) fn gc(&self) -> Result<(usize, u64), Error> { pub(crate) fn gc(&self) -> Result<(usize, u64), Error> {
let (inode_map, _link_count) = self.get_inode_csum_map()?; let (inode_map, _link_count) = self.get_inode_csum_map()?;
@ -459,7 +460,8 @@ impl PoolLockGuard<'_> {
let handle_entry = |entry: Result<walkdir::DirEntry, walkdir::Error>, let handle_entry = |entry: Result<walkdir::DirEntry, walkdir::Error>,
count: &mut usize, count: &mut usize,
size: &mut u64| size: &mut u64,
remove_empty_dir: bool|
-> Result<(), Error> { -> Result<(), Error> {
let path = entry?.into_path(); let path = entry?.into_path();
if path == self.lock_path() { if path == self.lock_path() {
@ -467,6 +469,10 @@ impl PoolLockGuard<'_> {
} }
let meta = path.metadata()?; let meta = path.metadata()?;
if remove_empty_dir && meta.is_dir() && path.read_dir()?.next().is_none() {
std::fs::remove_dir(path)?;
return Ok(());
}
if !meta.is_file() { if !meta.is_file() {
return Ok(()); return Ok(());
}; };
@ -507,11 +513,12 @@ impl PoolLockGuard<'_> {
}; };
WalkDir::new(&self.pool.link_dir) WalkDir::new(&self.pool.link_dir)
.contents_first(true)
.into_iter() .into_iter()
.try_for_each(|entry| handle_entry(entry, &mut count, &mut size))?; .try_for_each(|entry| handle_entry(entry, &mut count, &mut size, true))?;
WalkDir::new(&self.pool.pool_dir) WalkDir::new(&self.pool.pool_dir)
.into_iter() .into_iter()
.try_for_each(|entry| handle_entry(entry, &mut count, &mut size))?; .try_for_each(|entry| handle_entry(entry, &mut count, &mut size, false))?;
Ok((count, size)) Ok((count, size))
} }