[pbs-devel] [PATCH proxmox-backup v11 19/46] datastore: create/delete protected marker file on s3 storage backend

Christian Ebner c.ebner at proxmox.com
Tue Jul 22 12:10:39 CEST 2025


Commit 8292d3d2 ("api2/admin/datastore: add get/set_protection")
introduced the protected flag for backup snapshots, considering
snapshots as protected based on the presence/absence of the
`.protected` marker file in the corresponding snapshot directory.

To allow independent recovery of a datastore backed by an S3 bucket,
also create/delete the marker file on the object store backend. For
actual checks, still rely on the marker as encountered in the local
cache store.

Signed-off-by: Christian Ebner <c.ebner at proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner at proxmox.com>
Reviewed-by: Hannes Laimer <h.laimer at proxmox.com>
---
changes since version 10:
 - no changes

 pbs-datastore/src/backup_info.rs |  2 +-
 pbs-datastore/src/datastore.rs   | 42 +++++++++++++++++++++++++++-----
 2 files changed, 37 insertions(+), 7 deletions(-)

diff --git a/pbs-datastore/src/backup_info.rs b/pbs-datastore/src/backup_info.rs
index a4bf958cf..26f03a0ae 100644
--- a/pbs-datastore/src/backup_info.rs
+++ b/pbs-datastore/src/backup_info.rs
@@ -22,7 +22,7 @@ use crate::manifest::{BackupManifest, MANIFEST_LOCK_NAME};
 use crate::{DataBlob, DataStore, DatastoreBackend};
 
 pub const DATASTORE_LOCKS_DIR: &str = "/run/proxmox-backup/locks";
-const PROTECTED_MARKER_FILENAME: &str = ".protected";
+pub const PROTECTED_MARKER_FILENAME: &str = ".protected";
 
 proxmox_schema::const_regex! {
     pub BACKUP_FILES_AND_PROTECTED_REGEX = concatcp!(r"^(.*\.([fd]idx|blob)|\", PROTECTED_MARKER_FILENAME, ")$");
diff --git a/pbs-datastore/src/datastore.rs b/pbs-datastore/src/datastore.rs
index 820c039db..63dc91cb8 100644
--- a/pbs-datastore/src/datastore.rs
+++ b/pbs-datastore/src/datastore.rs
@@ -31,7 +31,9 @@ use pbs_api_types::{
 use pbs_config::s3::S3_CFG_TYPE_ID;
 use pbs_config::BackupLockGuard;
 
-use crate::backup_info::{BackupDir, BackupGroup, BackupInfo, OLD_LOCKING};
+use crate::backup_info::{
+    BackupDir, BackupGroup, BackupInfo, OLD_LOCKING, PROTECTED_MARKER_FILENAME,
+};
 use crate::chunk_store::ChunkStore;
 use crate::dynamic_index::{DynamicIndexReader, DynamicIndexWriter};
 use crate::fixed_index::{FixedIndexReader, FixedIndexWriter};
@@ -1572,12 +1574,40 @@ impl DataStore {
 
         let protected_path = backup_dir.protected_file();
         if protection {
-            std::fs::File::create(protected_path)
+            std::fs::File::create(&protected_path)
                 .map_err(|err| format_err!("could not create protection file: {}", err))?;
-        } else if let Err(err) = std::fs::remove_file(protected_path) {
-            // ignore error for non-existing file
-            if err.kind() != std::io::ErrorKind::NotFound {
-                bail!("could not remove protection file: {}", err);
+            if let DatastoreBackend::S3(s3_client) = self.backend()? {
+                let object_key = crate::s3::object_key_from_path(
+                    &backup_dir.relative_path(),
+                    PROTECTED_MARKER_FILENAME,
+                )
+                .context("invalid protected marker object key")?;
+                let _is_duplicate = proxmox_async::runtime::block_on(
+                    s3_client
+                        .upload_no_replace_with_retry(object_key, hyper::body::Bytes::from("")),
+                )
+                .context("failed to mark snapshot as protected on s3 backend")?;
+            }
+        } else {
+            if let Err(err) = std::fs::remove_file(&protected_path) {
+                // ignore error for non-existing file
+                if err.kind() != std::io::ErrorKind::NotFound {
+                    bail!("could not remove protection file: {err}");
+                }
+            }
+            if let DatastoreBackend::S3(s3_client) = self.backend()? {
+                let object_key = crate::s3::object_key_from_path(
+                    &backup_dir.relative_path(),
+                    PROTECTED_MARKER_FILENAME,
+                )
+                .context("invalid protected marker object key")?;
+                if let Err(err) =
+                    proxmox_async::runtime::block_on(s3_client.delete_object(object_key))
+                {
+                    std::fs::File::create(&protected_path)
+                        .map_err(|err| format_err!("could not re-create protection file: {err}"))?;
+                    return Err(err);
+                }
             }
         }
 
-- 
2.47.2





More information about the pbs-devel mailing list