[pbs-devel] [RFC v2 proxmox-backup 03/21] datastore: allow filtering of backups by their trash status

Christian Ebner c.ebner at proxmox.com
Thu May 8 15:05:37 CEST 2025


Extends the BackupGroup::list_backups method by an enum parameter to
filter backup snapshots based on their trash status.

This allows to reuse the same logic for listing active, trashed or
all snapshots.

Signed-off-by: Christian Ebner <c.ebner at proxmox.com>
---
 pbs-datastore/src/backup_info.rs | 33 +++++++++++++++++++++++++++++---
 pbs-datastore/src/datastore.rs   |  4 ++--
 src/api2/admin/datastore.rs      | 10 +++++-----
 src/api2/tape/backup.rs          |  4 ++--
 src/backup/verify.rs             |  4 ++--
 src/server/prune_job.rs          |  3 ++-
 src/server/pull.rs               |  3 ++-
 7 files changed, 45 insertions(+), 16 deletions(-)

diff --git a/pbs-datastore/src/backup_info.rs b/pbs-datastore/src/backup_info.rs
index 9ce4cb0f8..a8c864ac8 100644
--- a/pbs-datastore/src/backup_info.rs
+++ b/pbs-datastore/src/backup_info.rs
@@ -52,6 +52,12 @@ impl fmt::Debug for BackupGroup {
     }
 }
 
+pub enum ListBackupFilter {
+    Active,
+    All,
+    Trashed,
+}
+
 impl BackupGroup {
     pub(crate) fn new(
         store: Arc<DataStore>,
@@ -99,7 +105,7 @@ impl BackupGroup {
         self.full_group_path().exists()
     }
 
-    pub fn list_backups(&self) -> Result<Vec<BackupInfo>, Error> {
+    pub fn list_backups(&self, filter: ListBackupFilter) -> Result<Vec<BackupInfo>, Error> {
         let mut list = vec![];
 
         let path = self.full_group_path();
@@ -117,6 +123,19 @@ impl BackupGroup {
                 let files = list_backup_files(l2_fd, backup_time)?;
 
                 let protected = backup_dir.is_protected();
+                match filter {
+                    ListBackupFilter::All => (),
+                    ListBackupFilter::Trashed => {
+                        if !backup_dir.is_trashed() {
+                            return Ok(());
+                        }
+                    }
+                    ListBackupFilter::Active => {
+                        if backup_dir.is_trashed() {
+                            return Ok(());
+                        }
+                    }
+                }
 
                 list.push(BackupInfo {
                     backup_dir,
@@ -132,7 +151,7 @@ impl BackupGroup {
 
     /// Finds the latest backup inside a backup group
     pub fn last_backup(&self, only_finished: bool) -> Result<Option<BackupInfo>, Error> {
-        let backups = self.list_backups()?;
+        let backups = self.list_backups(ListBackupFilter::Active)?;
         Ok(backups
             .into_iter()
             .filter(|item| !only_finished || item.is_finished())
@@ -480,6 +499,11 @@ impl BackupDir {
         path.exists()
     }
 
+    pub fn is_trashed(&self) -> bool {
+        let path = self.full_path().join(TRASH_MARKER_FILENAME);
+        path.exists()
+    }
+
     pub fn backup_time_to_string(backup_time: i64) -> Result<String, Error> {
         // fixme: can this fail? (avoid unwrap)
         proxmox_time::epoch_to_rfc3339_utc(backup_time)
@@ -637,7 +661,10 @@ impl BackupDir {
             //
             // Do not error out, as we have already removed the snapshot, there is nothing a user could
             // do to rectify the situation.
-            if guard.is_ok() && group.list_backups()?.is_empty() && !*OLD_LOCKING {
+            if guard.is_ok()
+                && group.list_backups(ListBackupFilter::All)?.is_empty()
+                && !*OLD_LOCKING
+            {
                 group.remove_group_dir()?;
             } else if let Err(err) = guard {
                 log::debug!("{err:#}");
diff --git a/pbs-datastore/src/datastore.rs b/pbs-datastore/src/datastore.rs
index e546bc532..867324380 100644
--- a/pbs-datastore/src/datastore.rs
+++ b/pbs-datastore/src/datastore.rs
@@ -28,7 +28,7 @@ use pbs_api_types::{
 };
 use pbs_config::BackupLockGuard;
 
-use crate::backup_info::{BackupDir, BackupGroup, BackupInfo, OLD_LOCKING};
+use crate::backup_info::{BackupDir, BackupGroup, BackupInfo, ListBackupFilter, OLD_LOCKING};
 use crate::chunk_store::ChunkStore;
 use crate::dynamic_index::{DynamicIndexReader, DynamicIndexWriter};
 use crate::fixed_index::{FixedIndexReader, FixedIndexWriter};
@@ -1158,7 +1158,7 @@ impl DataStore {
                         _ => bail!("exhausted retries and unexpected counter overrun"),
                     };
 
-                    let mut snapshots = match group.list_backups() {
+                    let mut snapshots = match group.list_backups(ListBackupFilter::All) {
                         Ok(snapshots) => snapshots,
                         Err(err) => {
                             if group.exists() {
diff --git a/src/api2/admin/datastore.rs b/src/api2/admin/datastore.rs
index aafd1bbd7..133a6d658 100644
--- a/src/api2/admin/datastore.rs
+++ b/src/api2/admin/datastore.rs
@@ -51,7 +51,7 @@ use pbs_api_types::{
 };
 use pbs_client::pxar::{create_tar, create_zip};
 use pbs_config::CachedUserInfo;
-use pbs_datastore::backup_info::BackupInfo;
+use pbs_datastore::backup_info::{BackupInfo, ListBackupFilter};
 use pbs_datastore::cached_chunk_reader::CachedChunkReader;
 use pbs_datastore::catalog::{ArchiveEntry, CatalogReader};
 use pbs_datastore::data_blob::DataBlob;
@@ -223,7 +223,7 @@ pub fn list_groups(
                 return Ok(group_info);
             }
 
-            let snapshots = match group.list_backups() {
+            let snapshots = match group.list_backups(ListBackupFilter::Active) {
                 Ok(snapshots) => snapshots,
                 Err(_) => return Ok(group_info),
             };
@@ -624,7 +624,7 @@ unsafe fn list_snapshots_blocking(
             return Ok(snapshots);
         }
 
-        let group_backups = group.list_backups()?;
+        let group_backups = group.list_backups(ListBackupFilter::Active)?;
 
         snapshots.extend(
             group_backups
@@ -657,7 +657,7 @@ async fn get_snapshots_count(
                 Ok(group) => group,
                 Err(_) => return Ok(counts), // TODO: add this as error counts?
             };
-            let snapshot_count = group.list_backups()?.len() as u64;
+            let snapshot_count = group.list_backups(ListBackupFilter::Active)?.len() as u64;
 
             // only include groups with snapshots, counting/displaying empty groups can confuse
             if snapshot_count > 0 {
@@ -1042,7 +1042,7 @@ pub fn prune(
     }
     let mut prune_result: Vec<PruneResult> = Vec::new();
 
-    let list = group.list_backups()?;
+    let list = group.list_backups(ListBackupFilter::Active)?;
 
     let mut prune_info = compute_prune_info(list, &keep_options)?;
 
diff --git a/src/api2/tape/backup.rs b/src/api2/tape/backup.rs
index 31293a9a9..923cb7834 100644
--- a/src/api2/tape/backup.rs
+++ b/src/api2/tape/backup.rs
@@ -17,7 +17,7 @@ use pbs_api_types::{
 };
 
 use pbs_config::CachedUserInfo;
-use pbs_datastore::backup_info::{BackupDir, BackupInfo};
+use pbs_datastore::backup_info::{BackupDir, BackupInfo, ListBackupFilter};
 use pbs_datastore::{DataStore, StoreProgress};
 
 use crate::tape::TapeNotificationMode;
@@ -433,7 +433,7 @@ fn backup_worker(
         progress.done_snapshots = 0;
         progress.group_snapshots = 0;
 
-        let snapshot_list = group.list_backups()?;
+        let snapshot_list = group.list_backups(ListBackupFilter::Active)?;
 
         // filter out unfinished backups
         let mut snapshot_list: Vec<_> = snapshot_list
diff --git a/src/backup/verify.rs b/src/backup/verify.rs
index 3d2cba8ac..1b5e8564b 100644
--- a/src/backup/verify.rs
+++ b/src/backup/verify.rs
@@ -14,7 +14,7 @@ use pbs_api_types::{
     CryptMode, SnapshotVerifyState, VerifyState, PRIV_DATASTORE_BACKUP, PRIV_DATASTORE_VERIFY,
     UPID,
 };
-use pbs_datastore::backup_info::{BackupDir, BackupGroup, BackupInfo};
+use pbs_datastore::backup_info::{BackupDir, BackupGroup, BackupInfo, ListBackupFilter};
 use pbs_datastore::index::IndexFile;
 use pbs_datastore::manifest::{BackupManifest, FileInfo};
 use pbs_datastore::{DataBlob, DataStore, StoreProgress};
@@ -411,7 +411,7 @@ pub fn verify_backup_group(
     filter: Option<&dyn Fn(&BackupManifest) -> bool>,
 ) -> Result<Vec<String>, Error> {
     let mut errors = Vec::new();
-    let mut list = match group.list_backups() {
+    let mut list = match group.list_backups(ListBackupFilter::Active) {
         Ok(list) => list,
         Err(err) => {
             info!(
diff --git a/src/server/prune_job.rs b/src/server/prune_job.rs
index 1c86647a0..596afe086 100644
--- a/src/server/prune_job.rs
+++ b/src/server/prune_job.rs
@@ -1,6 +1,7 @@
 use std::sync::Arc;
 
 use anyhow::Error;
+use pbs_datastore::backup_info::ListBackupFilter;
 use tracing::{info, warn};
 
 use pbs_api_types::{
@@ -54,7 +55,7 @@ pub fn prune_datastore(
     )? {
         let group = group?;
         let ns = group.backup_ns();
-        let list = group.list_backups()?;
+        let list = group.list_backups(ListBackupFilter::Active)?;
 
         let mut prune_info = compute_prune_info(list, &prune_options.keep)?;
         prune_info.reverse(); // delete older snapshots first
diff --git a/src/server/pull.rs b/src/server/pull.rs
index b1724c142..50d7b0806 100644
--- a/src/server/pull.rs
+++ b/src/server/pull.rs
@@ -7,6 +7,7 @@ use std::sync::{Arc, Mutex};
 use std::time::SystemTime;
 
 use anyhow::{bail, format_err, Error};
+use pbs_datastore::backup_info::ListBackupFilter;
 use proxmox_human_byte::HumanByte;
 use tracing::info;
 
@@ -660,7 +661,7 @@ async fn pull_group(
             .target
             .store
             .backup_group(target_ns.clone(), group.clone());
-        let local_list = group.list_backups()?;
+        let local_list = group.list_backups(ListBackupFilter::Active)?;
         for info in local_list {
             let snapshot = info.backup_dir;
             if source_snapshots.contains(&snapshot.backup_time()) {
-- 
2.39.5





More information about the pbs-devel mailing list