all lists on lists.proxmox.com
 help / color / mirror / Atom feed
From: Hannes Laimer <h.laimer@proxmox.com>
To: pbs-devel@lists.proxmox.com
Subject: [pbs-devel] [PATCH proxmox-backup v2 05/12] backup_info: add generics and separate functions into impl blocks
Date: Mon, 26 May 2025 16:14:38 +0200	[thread overview]
Message-ID: <20250526141445.228717-6-h.laimer@proxmox.com> (raw)
In-Reply-To: <20250526141445.228717-1-h.laimer@proxmox.com>

Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
 pbs-datastore/src/backup_info.rs | 583 ++++++++++++++++---------------
 pbs-datastore/src/datastore.rs   |  26 +-
 2 files changed, 313 insertions(+), 296 deletions(-)

diff --git a/pbs-datastore/src/backup_info.rs b/pbs-datastore/src/backup_info.rs
index d4732fdd..f3ca283c 100644
--- a/pbs-datastore/src/backup_info.rs
+++ b/pbs-datastore/src/backup_info.rs
@@ -18,7 +18,10 @@ use pbs_api_types::{
 use pbs_config::{open_backup_lockfile, BackupLockGuard};
 
 use crate::manifest::{BackupManifest, MANIFEST_LOCK_NAME};
-use crate::{DataBlob, DataStore};
+use crate::{
+    chunk_store::{CanRead, CanWrite},
+    DataBlob, DataStore,
+};
 
 pub const DATASTORE_LOCKS_DIR: &str = "/run/proxmox-backup/locks";
 
@@ -34,14 +37,14 @@ pub(crate) static OLD_LOCKING: LazyLock<bool> = LazyLock::new(|| {
 
 /// BackupGroup is a directory containing a list of BackupDir
 #[derive(Clone)]
-pub struct BackupGroup {
-    store: Arc<DataStore>,
+pub struct BackupGroup<T> {
+    store: Arc<DataStore<T>>,
 
     ns: BackupNamespace,
     group: pbs_api_types::BackupGroup,
 }
 
-impl fmt::Debug for BackupGroup {
+impl<T> fmt::Debug for BackupGroup<T> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         f.debug_struct("BackupGroup")
             .field("store", &self.store.name())
@@ -51,45 +54,12 @@ impl fmt::Debug for BackupGroup {
     }
 }
 
-impl BackupGroup {
-    pub(crate) fn new(
-        store: Arc<DataStore>,
-        ns: BackupNamespace,
-        group: pbs_api_types::BackupGroup,
-    ) -> Self {
-        Self { store, ns, group }
-    }
-
-    /// Access the underlying [`BackupGroup`](pbs_api_types::BackupGroup).
-    #[inline]
-    pub fn group(&self) -> &pbs_api_types::BackupGroup {
-        &self.group
-    }
-
-    #[inline]
-    pub fn backup_ns(&self) -> &BackupNamespace {
-        &self.ns
-    }
-
-    #[inline]
-    pub fn backup_type(&self) -> BackupType {
-        self.group.ty
-    }
-
-    #[inline]
-    pub fn backup_id(&self) -> &str {
-        &self.group.id
-    }
-
-    pub fn full_group_path(&self) -> PathBuf {
-        self.store.group_path(&self.ns, &self.group)
-    }
-
-    pub fn relative_group_path(&self) -> PathBuf {
-        let mut path = self.ns.path();
-        path.push(self.group.ty.as_str());
-        path.push(&self.group.id);
-        path
+impl<T: CanRead> BackupGroup<T> {
+    /// Returns the backup owner.
+    ///
+    /// The backup owner is the entity who first created the backup group.
+    pub fn get_owner(&self) -> Result<Authid, Error> {
+        self.store.get_owner(&self.ns, self.as_ref())
     }
 
     /// Simple check whether a group exists. This does not check whether there are any snapshots,
@@ -98,7 +68,7 @@ impl BackupGroup {
         self.full_group_path().exists()
     }
 
-    pub fn list_backups(&self) -> Result<Vec<BackupInfo>, Error> {
+    pub fn list_backups(&self) -> Result<Vec<BackupInfo<T>>, Error> {
         let mut list = vec![];
 
         let path = self.full_group_path();
@@ -130,7 +100,7 @@ impl BackupGroup {
     }
 
     /// Finds the latest backup inside a backup group
-    pub fn last_backup(&self, only_finished: bool) -> Result<Option<BackupInfo>, Error> {
+    pub fn last_backup(&self, only_finished: bool) -> Result<Option<BackupInfo<T>>, Error> {
         let backups = self.list_backups()?;
         Ok(backups
             .into_iter()
@@ -190,24 +160,13 @@ impl BackupGroup {
 
         Ok(last)
     }
+}
 
-    pub fn matches(&self, filter: &GroupFilter) -> bool {
-        self.group.matches(filter)
-    }
-
-    pub fn backup_dir(&self, time: i64) -> Result<BackupDir, Error> {
-        BackupDir::with_group(self.clone(), time)
-    }
-
-    pub fn backup_dir_with_rfc3339<T: Into<String>>(
-        &self,
-        time_string: T,
-    ) -> Result<BackupDir, Error> {
-        BackupDir::with_rfc3339(self.clone(), time_string.into())
-    }
-
-    pub fn iter_snapshots(&self) -> Result<crate::ListSnapshots, Error> {
-        crate::ListSnapshots::new(self.clone())
+impl<T: CanWrite> BackupGroup<T> {
+    /// Set the backup owner.
+    pub fn set_owner(&self, auth_id: &Authid, force: bool) -> Result<(), Error> {
+        self.store
+            .set_owner(&self.ns, self.as_ref(), auth_id, force)
     }
 
     /// Destroy the group inclusive all its backup snapshots (BackupDir's)
@@ -260,32 +219,6 @@ impl BackupGroup {
         Ok(())
     }
 
-    /// Returns the backup owner.
-    ///
-    /// The backup owner is the entity who first created the backup group.
-    pub fn get_owner(&self) -> Result<Authid, Error> {
-        self.store.get_owner(&self.ns, self.as_ref())
-    }
-
-    /// Set the backup owner.
-    pub fn set_owner(&self, auth_id: &Authid, force: bool) -> Result<(), Error> {
-        self.store
-            .set_owner(&self.ns, self.as_ref(), auth_id, force)
-    }
-
-    /// Returns a file name for locking a group.
-    ///
-    /// The lock file will be located in:
-    /// `${DATASTORE_LOCKS_DIR}/${datastore name}/${lock_file_path_helper(rpath)}`
-    /// where `rpath` is the relative path of the group.
-    fn lock_path(&self) -> PathBuf {
-        let path = Path::new(DATASTORE_LOCKS_DIR).join(self.store.name());
-
-        let rpath = Path::new(self.group.ty.as_str()).join(&self.group.id);
-
-        path.join(lock_file_path_helper(&self.ns, rpath))
-    }
-
     /// Locks a group exclusively.
     pub fn lock(&self) -> Result<BackupLockGuard, Error> {
         if *OLD_LOCKING {
@@ -304,34 +237,108 @@ impl BackupGroup {
     }
 }
 
-impl AsRef<pbs_api_types::BackupNamespace> for BackupGroup {
+impl<T: Clone> BackupGroup<T> {
+    pub(crate) fn new(
+        store: Arc<DataStore<T>>,
+        ns: BackupNamespace,
+        group: pbs_api_types::BackupGroup,
+    ) -> Self {
+        Self { store, ns, group }
+    }
+
+    /// Access the underlying [`BackupGroup`](pbs_api_types::BackupGroup).
+    #[inline]
+    pub fn group(&self) -> &pbs_api_types::BackupGroup {
+        &self.group
+    }
+
+    #[inline]
+    pub fn backup_ns(&self) -> &BackupNamespace {
+        &self.ns
+    }
+
+    #[inline]
+    pub fn backup_type(&self) -> BackupType {
+        self.group.ty
+    }
+
+    #[inline]
+    pub fn backup_id(&self) -> &str {
+        &self.group.id
+    }
+
+    pub fn full_group_path(&self) -> PathBuf {
+        self.store.group_path(&self.ns, &self.group)
+    }
+
+    pub fn relative_group_path(&self) -> PathBuf {
+        let mut path = self.ns.path();
+        path.push(self.group.ty.as_str());
+        path.push(&self.group.id);
+        path
+    }
+
+    pub fn matches(&self, filter: &GroupFilter) -> bool {
+        self.group.matches(filter)
+    }
+
+    pub fn backup_dir(&self, time: i64) -> Result<BackupDir<T>, Error> {
+        BackupDir::with_group(self.clone(), time)
+    }
+
+    pub fn backup_dir_with_rfc3339<D: Into<String>>(
+        &self,
+        time_string: D,
+    ) -> Result<BackupDir<T>, Error> {
+        BackupDir::with_rfc3339(self.clone(), time_string.into())
+    }
+
+    pub fn iter_snapshots(&self) -> Result<crate::ListSnapshots, Error> {
+        crate::ListSnapshots::new(self.clone())
+    }
+
+    /// Returns a file name for locking a group.
+    ///
+    /// The lock file will be located in:
+    /// `${DATASTORE_LOCKS_DIR}/${datastore name}/${lock_file_path_helper(rpath)}`
+    /// where `rpath` is the relative path of the group.
+    fn lock_path(&self) -> PathBuf {
+        let path = Path::new(DATASTORE_LOCKS_DIR).join(self.store.name());
+
+        let rpath = Path::new(self.group.ty.as_str()).join(&self.group.id);
+
+        path.join(lock_file_path_helper(&self.ns, rpath))
+    }
+}
+
+impl<T> AsRef<pbs_api_types::BackupNamespace> for BackupGroup<T> {
     #[inline]
     fn as_ref(&self) -> &pbs_api_types::BackupNamespace {
         &self.ns
     }
 }
 
-impl AsRef<pbs_api_types::BackupGroup> for BackupGroup {
+impl<T> AsRef<pbs_api_types::BackupGroup> for BackupGroup<T> {
     #[inline]
     fn as_ref(&self) -> &pbs_api_types::BackupGroup {
         &self.group
     }
 }
 
-impl From<&BackupGroup> for pbs_api_types::BackupGroup {
-    fn from(group: &BackupGroup) -> pbs_api_types::BackupGroup {
+impl<T> From<&BackupGroup<T>> for pbs_api_types::BackupGroup {
+    fn from(group: &BackupGroup<T>) -> pbs_api_types::BackupGroup {
         group.group.clone()
     }
 }
 
-impl From<BackupGroup> for pbs_api_types::BackupGroup {
-    fn from(group: BackupGroup) -> pbs_api_types::BackupGroup {
+impl<T> From<BackupGroup<T>> for pbs_api_types::BackupGroup {
+    fn from(group: BackupGroup<T>) -> pbs_api_types::BackupGroup {
         group.group
     }
 }
 
-impl From<BackupDir> for BackupGroup {
-    fn from(dir: BackupDir) -> BackupGroup {
+impl<T> From<BackupDir<T>> for BackupGroup<T> {
+    fn from(dir: BackupDir<T>) -> BackupGroup<T> {
         BackupGroup {
             store: dir.store,
             ns: dir.ns,
@@ -340,8 +347,8 @@ impl From<BackupDir> for BackupGroup {
     }
 }
 
-impl From<&BackupDir> for BackupGroup {
-    fn from(dir: &BackupDir) -> BackupGroup {
+impl<T> From<&BackupDir<T>> for BackupGroup<T> {
+    fn from(dir: &BackupDir<T>) -> BackupGroup<T> {
         BackupGroup {
             store: Arc::clone(&dir.store),
             ns: dir.ns.clone(),
@@ -354,15 +361,15 @@ impl From<&BackupDir> for BackupGroup {
 ///
 /// We also call this a backup snaphost.
 #[derive(Clone)]
-pub struct BackupDir {
-    store: Arc<DataStore>,
+pub struct BackupDir<T> {
+    store: Arc<DataStore<T>>,
     ns: BackupNamespace,
     dir: pbs_api_types::BackupDir,
     // backup_time as rfc3339
     backup_time_string: String,
 }
 
-impl fmt::Debug for BackupDir {
+impl<T> fmt::Debug for BackupDir<T> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         f.debug_struct("BackupDir")
             .field("store", &self.store.name())
@@ -373,102 +380,12 @@ impl fmt::Debug for BackupDir {
     }
 }
 
-impl BackupDir {
-    /// Temporarily used for tests.
-    #[doc(hidden)]
-    pub fn new_test(dir: pbs_api_types::BackupDir) -> Self {
-        Self {
-            store: unsafe { DataStore::new_test() },
-            backup_time_string: Self::backup_time_to_string(dir.time).unwrap(),
-            ns: BackupNamespace::root(),
-            dir,
-        }
-    }
-
-    pub(crate) fn with_group(group: BackupGroup, backup_time: i64) -> Result<Self, Error> {
-        let backup_time_string = Self::backup_time_to_string(backup_time)?;
-        Ok(Self {
-            store: group.store,
-            ns: group.ns,
-            dir: (group.group, backup_time).into(),
-            backup_time_string,
-        })
-    }
-
-    pub(crate) fn with_rfc3339(
-        group: BackupGroup,
-        backup_time_string: String,
-    ) -> Result<Self, Error> {
-        let backup_time = proxmox_time::parse_rfc3339(&backup_time_string)?;
-        Ok(Self {
-            store: group.store,
-            ns: group.ns,
-            dir: (group.group, backup_time).into(),
-            backup_time_string,
-        })
-    }
-
-    #[inline]
-    pub fn backup_ns(&self) -> &BackupNamespace {
-        &self.ns
-    }
-
-    #[inline]
-    pub fn backup_type(&self) -> BackupType {
-        self.dir.group.ty
-    }
-
-    #[inline]
-    pub fn backup_id(&self) -> &str {
-        &self.dir.group.id
-    }
-
-    #[inline]
-    pub fn backup_time(&self) -> i64 {
-        self.dir.time
-    }
-
-    pub fn backup_time_string(&self) -> &str {
-        &self.backup_time_string
-    }
-
-    pub fn dir(&self) -> &pbs_api_types::BackupDir {
-        &self.dir
-    }
-
-    pub fn group(&self) -> &pbs_api_types::BackupGroup {
-        &self.dir.group
-    }
-
-    pub fn relative_path(&self) -> PathBuf {
-        let mut path = self.ns.path();
-        path.push(self.dir.group.ty.as_str());
-        path.push(&self.dir.group.id);
-        path.push(&self.backup_time_string);
-        path
-    }
-
-    /// Returns the absolute path for backup_dir, using the cached formatted time string.
-    pub fn full_path(&self) -> PathBuf {
-        let mut path = self.store.base_path();
-        path.push(self.relative_path());
-        path
-    }
-
-    pub fn protected_file(&self) -> PathBuf {
-        let mut path = self.full_path();
-        path.push(".protected");
-        path
-    }
-
-    pub fn is_protected(&self) -> bool {
-        let path = self.protected_file();
-        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)
+impl<T: CanRead> BackupDir<T> {
+    /// Returns the backup owner.
+    ///
+    /// The backup owner is the entity who first created the backup group.
+    pub fn get_owner(&self) -> Result<Authid, Error> {
+        self.store.get_owner(&self.ns, self.as_ref())
     }
 
     /// load a `DataBlob` from this snapshot's backup dir.
@@ -483,22 +400,38 @@ impl BackupDir {
         .map_err(|err| format_err!("unable to load blob '{:?}' - {}", path, err))
     }
 
-    /// Returns the filename to lock a manifest
-    ///
-    /// Also creates the basedir. The lockfile is located in
-    /// `${DATASTORE_LOCKS_DIR}/${datastore name}/${lock_file_path_helper(rpath)}.index.json.lck`
-    /// where rpath is the relative path of the snapshot.
-    fn manifest_lock_path(&self) -> PathBuf {
-        let path = Path::new(DATASTORE_LOCKS_DIR).join(self.store.name());
+    /// Acquires a shared lock on a snapshot.
+    pub fn lock_shared(&self) -> Result<BackupLockGuard, Error> {
+        if *OLD_LOCKING {
+            lock_dir_noblock_shared(
+                &self.full_path(),
+                "snapshot",
+                "backup is running or snapshot is in use, could not acquire shared lock",
+            )
+            .map(BackupLockGuard::from)
+        } else {
+            lock_helper(self.store.name(), &self.lock_path(), |p| {
+                open_backup_lockfile(p, Some(Duration::from_secs(0)), false)
+                    .with_context(|| format!("unable to acquire shared snapshot lock {p:?}"))
+            })
+        }
+    }
 
-        let rpath = Path::new(self.dir.group.ty.as_str())
-            .join(&self.dir.group.id)
-            .join(&self.backup_time_string)
-            .join(MANIFEST_LOCK_NAME);
+    /// Load the manifest without a lock. Must not be written back.
+    pub fn load_manifest(&self) -> Result<(BackupManifest, u64), Error> {
+        let blob = self.load_blob(MANIFEST_BLOB_NAME.as_ref())?;
+        let raw_size = blob.raw_size();
+        let manifest = BackupManifest::try_from(blob)?;
+        Ok((manifest, raw_size))
+    }
 
-        path.join(lock_file_path_helper(&self.ns, rpath))
+    /// Load the verify state from the manifest.
+    pub fn verify_state(&self) -> Result<Option<VerifyState>, anyhow::Error> {
+        Ok(self.load_manifest()?.0.verify_state()?.map(|svs| svs.state))
     }
+}
 
+impl<T: CanWrite> BackupDir<T> {
     /// Locks the manifest of a snapshot, for example, to update or delete it.
     pub(crate) fn lock_manifest(&self) -> Result<BackupLockGuard, Error> {
         let path = if *OLD_LOCKING {
@@ -523,21 +456,6 @@ impl BackupDir {
         })
     }
 
-    /// Returns a file name for locking a snapshot.
-    ///
-    /// The lock file will be located in:
-    /// `${DATASTORE_LOCKS_DIR}/${datastore name}/${lock_file_path_helper(rpath)}`
-    /// where `rpath` is the relative path of the snapshot.
-    fn lock_path(&self) -> PathBuf {
-        let path = Path::new(DATASTORE_LOCKS_DIR).join(self.store.name());
-
-        let rpath = Path::new(self.dir.group.ty.as_str())
-            .join(&self.dir.group.id)
-            .join(&self.backup_time_string);
-
-        path.join(lock_file_path_helper(&self.ns, rpath))
-    }
-
     /// Locks a snapshot exclusively.
     pub fn lock(&self) -> Result<BackupLockGuard, Error> {
         if *OLD_LOCKING {
@@ -555,23 +473,6 @@ impl BackupDir {
         }
     }
 
-    /// Acquires a shared lock on a snapshot.
-    pub fn lock_shared(&self) -> Result<BackupLockGuard, Error> {
-        if *OLD_LOCKING {
-            lock_dir_noblock_shared(
-                &self.full_path(),
-                "snapshot",
-                "backup is running or snapshot is in use, could not acquire shared lock",
-            )
-            .map(BackupLockGuard::from)
-        } else {
-            lock_helper(self.store.name(), &self.lock_path(), |p| {
-                open_backup_lockfile(p, Some(Duration::from_secs(0)), false)
-                    .with_context(|| format!("unable to acquire shared snapshot lock {p:?}"))
-            })
-        }
-    }
-
     /// Destroy the whole snapshot, bails if it's protected
     ///
     /// Setting `force` to true skips locking and thus ignores if the backup is currently in use.
@@ -624,31 +525,6 @@ impl BackupDir {
         Ok(())
     }
 
-    /// Get the datastore.
-    pub fn datastore(&self) -> &Arc<DataStore> {
-        &self.store
-    }
-
-    /// Returns the backup owner.
-    ///
-    /// The backup owner is the entity who first created the backup group.
-    pub fn get_owner(&self) -> Result<Authid, Error> {
-        self.store.get_owner(&self.ns, self.as_ref())
-    }
-
-    /// Lock the snapshot and open a reader.
-    pub fn locked_reader(&self) -> Result<crate::SnapshotReader, Error> {
-        crate::SnapshotReader::new_do(self.clone())
-    }
-
-    /// Load the manifest without a lock. Must not be written back.
-    pub fn load_manifest(&self) -> Result<(BackupManifest, u64), Error> {
-        let blob = self.load_blob(MANIFEST_BLOB_NAME.as_ref())?;
-        let raw_size = blob.raw_size();
-        let manifest = BackupManifest::try_from(blob)?;
-        Ok((manifest, raw_size))
-    }
-
     /// Update the manifest of the specified snapshot. Never write a manifest directly,
     /// only use this method - anything else may break locking guarantees.
     pub fn update_manifest(
@@ -706,68 +582,203 @@ impl BackupDir {
 
         Ok(())
     }
+}
+
+impl<T> BackupDir<T> {
+    /// Temporarily used for tests.
+    #[doc(hidden)]
+    pub fn new_test(dir: pbs_api_types::BackupDir) -> Self {
+        Self {
+            store: DataStore::new_test(),
+            backup_time_string: Self::backup_time_to_string(dir.time).unwrap(),
+            ns: BackupNamespace::root(),
+            dir,
+        }
+    }
 
-    /// Load the verify state from the manifest.
-    pub fn verify_state(&self) -> Result<Option<VerifyState>, anyhow::Error> {
-        Ok(self.load_manifest()?.0.verify_state()?.map(|svs| svs.state))
+    pub(crate) fn with_group(group: BackupGroup<T>, backup_time: i64) -> Result<Self, Error> {
+        let backup_time_string = Self::backup_time_to_string(backup_time)?;
+        Ok(Self {
+            store: group.store,
+            ns: group.ns,
+            dir: (group.group, backup_time).into(),
+            backup_time_string,
+        })
+    }
+
+    pub(crate) fn with_rfc3339(
+        group: BackupGroup<T>,
+        backup_time_string: String,
+    ) -> Result<Self, Error> {
+        let backup_time = proxmox_time::parse_rfc3339(&backup_time_string)?;
+        Ok(Self {
+            store: group.store,
+            ns: group.ns,
+            dir: (group.group, backup_time).into(),
+            backup_time_string,
+        })
+    }
+
+    #[inline]
+    pub fn backup_ns(&self) -> &BackupNamespace {
+        &self.ns
+    }
+
+    #[inline]
+    pub fn backup_type(&self) -> BackupType {
+        self.dir.group.ty
+    }
+
+    #[inline]
+    pub fn backup_id(&self) -> &str {
+        &self.dir.group.id
+    }
+
+    #[inline]
+    pub fn backup_time(&self) -> i64 {
+        self.dir.time
+    }
+
+    pub fn backup_time_string(&self) -> &str {
+        &self.backup_time_string
+    }
+
+    pub fn dir(&self) -> &pbs_api_types::BackupDir {
+        &self.dir
+    }
+
+    pub fn group(&self) -> &pbs_api_types::BackupGroup {
+        &self.dir.group
+    }
+
+    pub fn relative_path(&self) -> PathBuf {
+        let mut path = self.ns.path();
+        path.push(self.dir.group.ty.as_str());
+        path.push(&self.dir.group.id);
+        path.push(&self.backup_time_string);
+        path
+    }
+
+    /// Returns the absolute path for backup_dir, using the cached formatted time string.
+    pub fn full_path(&self) -> PathBuf {
+        let mut path = self.store.base_path();
+        path.push(self.relative_path());
+        path
+    }
+
+    pub fn protected_file(&self) -> PathBuf {
+        let mut path = self.full_path();
+        path.push(".protected");
+        path
+    }
+
+    pub fn is_protected(&self) -> bool {
+        let path = self.protected_file();
+        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)
+    }
+
+    /// Returns the filename to lock a manifest
+    ///
+    /// Also creates the basedir. The lockfile is located in
+    /// `${DATASTORE_LOCKS_DIR}/${datastore name}/${lock_file_path_helper(rpath)}.index.json.lck`
+    /// where rpath is the relative path of the snapshot.
+    fn manifest_lock_path(&self) -> PathBuf {
+        let path = Path::new(DATASTORE_LOCKS_DIR).join(self.store.name());
+
+        let rpath = Path::new(self.dir.group.ty.as_str())
+            .join(&self.dir.group.id)
+            .join(&self.backup_time_string)
+            .join(MANIFEST_LOCK_NAME);
+
+        path.join(lock_file_path_helper(&self.ns, rpath))
+    }
+
+    /// Returns a file name for locking a snapshot.
+    ///
+    /// The lock file will be located in:
+    /// `${DATASTORE_LOCKS_DIR}/${datastore name}/${lock_file_path_helper(rpath)}`
+    /// where `rpath` is the relative path of the snapshot.
+    fn lock_path(&self) -> PathBuf {
+        let path = Path::new(DATASTORE_LOCKS_DIR).join(self.store.name());
+
+        let rpath = Path::new(self.dir.group.ty.as_str())
+            .join(&self.dir.group.id)
+            .join(&self.backup_time_string);
+
+        path.join(lock_file_path_helper(&self.ns, rpath))
+    }
+
+    /// Get the datastore.
+    pub fn datastore(&self) -> &Arc<DataStore<T>> {
+        &self.store
+    }
+
+    /// Lock the snapshot and open a reader.
+    pub fn locked_reader(&self) -> Result<crate::SnapshotReader, Error> {
+        crate::SnapshotReader::new_do(self.clone())
     }
 }
 
-impl AsRef<pbs_api_types::BackupNamespace> for BackupDir {
+impl<T> AsRef<pbs_api_types::BackupNamespace> for BackupDir<T> {
     fn as_ref(&self) -> &pbs_api_types::BackupNamespace {
         &self.ns
     }
 }
 
-impl AsRef<pbs_api_types::BackupDir> for BackupDir {
+impl<T> AsRef<pbs_api_types::BackupDir> for BackupDir<T> {
     fn as_ref(&self) -> &pbs_api_types::BackupDir {
         &self.dir
     }
 }
 
-impl AsRef<pbs_api_types::BackupGroup> for BackupDir {
+impl<T> AsRef<pbs_api_types::BackupGroup> for BackupDir<T> {
     fn as_ref(&self) -> &pbs_api_types::BackupGroup {
         &self.dir.group
     }
 }
 
-impl From<&BackupDir> for pbs_api_types::BackupGroup {
-    fn from(dir: &BackupDir) -> pbs_api_types::BackupGroup {
+impl<T> From<&BackupDir<T>> for pbs_api_types::BackupGroup {
+    fn from(dir: &BackupDir<T>) -> pbs_api_types::BackupGroup {
         dir.dir.group.clone()
     }
 }
 
-impl From<BackupDir> for pbs_api_types::BackupGroup {
-    fn from(dir: BackupDir) -> pbs_api_types::BackupGroup {
+impl<T> From<BackupDir<T>> for pbs_api_types::BackupGroup {
+    fn from(dir: BackupDir<T>) -> pbs_api_types::BackupGroup {
         dir.dir.group
     }
 }
 
-impl From<&BackupDir> for pbs_api_types::BackupDir {
-    fn from(dir: &BackupDir) -> pbs_api_types::BackupDir {
+impl<T> From<&BackupDir<T>> for pbs_api_types::BackupDir {
+    fn from(dir: &BackupDir<T>) -> pbs_api_types::BackupDir {
         dir.dir.clone()
     }
 }
 
-impl From<BackupDir> for pbs_api_types::BackupDir {
-    fn from(dir: BackupDir) -> pbs_api_types::BackupDir {
+impl<T> From<BackupDir<T>> for pbs_api_types::BackupDir {
+    fn from(dir: BackupDir<T>) -> pbs_api_types::BackupDir {
         dir.dir
     }
 }
 
 /// Detailed Backup Information, lists files inside a BackupDir
 #[derive(Clone, Debug)]
-pub struct BackupInfo {
+pub struct BackupInfo<T> {
     /// the backup directory
-    pub backup_dir: BackupDir,
+    pub backup_dir: BackupDir<T>,
     /// List of data files
     pub files: Vec<String>,
     /// Protection Status
     pub protected: bool,
 }
 
-impl BackupInfo {
-    pub fn new(backup_dir: BackupDir) -> Result<BackupInfo, Error> {
+impl<T: CanRead> BackupInfo<T> {
+    pub fn new(backup_dir: BackupDir<T>) -> Result<BackupInfo<T>, Error> {
         let path = backup_dir.full_path();
 
         let files = list_backup_files(libc::AT_FDCWD, &path)?;
@@ -779,8 +790,10 @@ impl BackupInfo {
             protected,
         })
     }
+}
 
-    pub fn sort_list(list: &mut [BackupInfo], ascendending: bool) {
+impl<T> BackupInfo<T> {
+    pub fn sort_list(list: &mut [BackupInfo<T>], ascendending: bool) {
         if ascendending {
             // oldest first
             list.sort_unstable_by(|a, b| a.backup_dir.dir.time.cmp(&b.backup_dir.dir.time));
diff --git a/pbs-datastore/src/datastore.rs b/pbs-datastore/src/datastore.rs
index 66a2e209..9356750b 100644
--- a/pbs-datastore/src/datastore.rs
+++ b/pbs-datastore/src/datastore.rs
@@ -303,7 +303,7 @@ impl<T: CanRead + 'static> DataStore<T> {
         self: &Arc<DataStore<T>>,
         ns: BackupNamespace,
         ty: BackupType,
-    ) -> Result<impl Iterator<Item = BackupGroup> + 'static, Error> {
+    ) -> Result<impl Iterator<Item = BackupGroup<T>> + 'static, Error> {
         Ok(self.iter_backup_type(ns, ty)?.ok())
     }
 
@@ -314,7 +314,7 @@ impl<T: CanRead + 'static> DataStore<T> {
     pub fn iter_backup_groups_ok(
         self: &Arc<DataStore<T>>,
         ns: BackupNamespace,
-    ) -> Result<impl Iterator<Item = BackupGroup> + 'static, Error> {
+    ) -> Result<impl Iterator<Item = BackupGroup<T>> + 'static, Error> {
         Ok(self.iter_backup_groups(ns)?.ok())
     }
 }
@@ -644,7 +644,7 @@ impl<T: CanRead> DataStore<T> {
     pub fn list_backup_groups(
         self: &Arc<DataStore<T>>,
         ns: BackupNamespace,
-    ) -> Result<Vec<BackupGroup>, Error> {
+    ) -> Result<Vec<BackupGroup<T>>, Error> {
         ListGroups::new(Arc::clone(self), ns)?.collect()
     }
 
@@ -837,7 +837,7 @@ impl<T: CanRead> DataStore<T> {
         ty: BackupType,
         id: D,
         time: i64,
-    ) -> Result<BackupDir, Error>
+    ) -> Result<BackupDir<T>, Error>
     where
         D: Into<String>,
     {
@@ -847,10 +847,10 @@ impl<T: CanRead> DataStore<T> {
     /// Open a snapshot (backup directory) from this datastore with a cached rfc3339 time string.
     pub fn backup_dir_with_rfc3339<D: Into<String>>(
         self: &Arc<Self>,
-        group: BackupGroup,
+        group: BackupGroup<T>,
         time_string: D,
-    ) -> Result<BackupDir, Error> {
-        BackupDir::with_rfc3339(group, time_string.into())
+    ) -> Result<BackupDir<T>, Error> {
+        BackupDir::<T>::with_rfc3339(group, time_string.into())
     }
 
     /// Open a backup group from this datastore.
@@ -859,7 +859,7 @@ impl<T: CanRead> DataStore<T> {
         ns: BackupNamespace,
         ty: BackupType,
         id: D,
-    ) -> BackupGroup
+    ) -> BackupGroup<T>
     where
         D: Into<String>,
     {
@@ -889,7 +889,7 @@ impl<T: CanRead> DataStore<T> {
         self: &Arc<Self>,
         ns: BackupNamespace,
         dir: pbs_api_types::BackupDir,
-    ) -> Result<BackupDir, Error> {
+    ) -> Result<BackupDir<T>, Error> {
         BackupDir::with_group(self.backup_group(ns, dir.group), dir.time)
     }
 }
@@ -1258,7 +1258,7 @@ impl<T: CanWrite> DataStore<T> {
                         _ => bail!("exhausted retries and unexpected counter overrun"),
                     };
 
-                    let mut snapshots = match group.list_backups() {
+                    let mut snapshots: Vec<BackupInfo<T>> = match group.list_backups() {
                         Ok(snapshots) => snapshots,
                         Err(err) => {
                             if group.exists() {
@@ -1526,7 +1526,11 @@ impl<T: CanWrite> DataStore<T> {
     }
 
     /// Updates the protection status of the specified snapshot.
-    pub fn update_protection(&self, backup_dir: &BackupDir, protection: bool) -> Result<(), Error> {
+    pub fn update_protection(
+        &self,
+        backup_dir: &BackupDir<T>,
+        protection: bool,
+    ) -> Result<(), Error> {
         let full_path = backup_dir.full_path();
 
         if !full_path.exists() {
-- 
2.39.5



_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel


  parent reply	other threads:[~2025-05-26 14:14 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-05-26 14:14 [pbs-devel] [PATCH proxmox-backup v2 00/12] introduce typestate for datastore/chunkstore Hannes Laimer
2025-05-26 14:14 ` [pbs-devel] [PATCH proxmox-backup v2 01/12] chunkstore: add CanRead and CanWrite trait Hannes Laimer
2025-05-26 14:14 ` [pbs-devel] [PATCH proxmox-backup v2 02/12] chunkstore: separate functions into impl block Hannes Laimer
2025-05-26 14:14 ` [pbs-devel] [PATCH proxmox-backup v2 03/12] datastore: add generics and new lookup functions Hannes Laimer
2025-05-26 14:14 ` [pbs-devel] [PATCH proxmox-backup v2 04/12] datastore: separate functions into impl block Hannes Laimer
2025-05-26 14:14 ` Hannes Laimer [this message]
2025-05-26 14:14 ` [pbs-devel] [PATCH proxmox-backup v2 06/12] pbs-datastore: add generics and separate functions into impl blocks Hannes Laimer
2025-05-26 14:14 ` [pbs-devel] [PATCH proxmox-backup v2 07/12] api: backup: env: add generics and separate functions into impl block Hannes Laimer
2025-05-26 14:14 ` [pbs-devel] [PATCH proxmox-backup v2 08/12] api/backup/bin/server/tape: add missing generics Hannes Laimer
2025-05-26 14:14 ` [pbs-devel] [PATCH proxmox-backup v2 09/12] examples/tests: " Hannes Laimer
2025-05-26 14:14 ` [pbs-devel] [PATCH proxmox-backup v2 10/12] api: admin: pull datastore loading out of check_privs helper Hannes Laimer
2025-05-26 14:14 ` [pbs-devel] [PATCH proxmox-backup v2 11/12] datastore: move `fn gc_running` out of DataStoreImpl Hannes Laimer
2025-05-26 14:14 ` [pbs-devel] [PATCH proxmox-backup v2 12/12] api/server: replace datastore_lookup with new, state-typed datastore returning functions Hannes Laimer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250526141445.228717-6-h.laimer@proxmox.com \
    --to=h.laimer@proxmox.com \
    --cc=pbs-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal