From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) by lore.proxmox.com (Postfix) with ESMTPS id 997181FF141 for ; Tue, 05 May 2026 10:12:14 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 6692D19961; Tue, 5 May 2026 10:12:08 +0200 (CEST) From: Christian Ebner To: pbs-devel@lists.proxmox.com Subject: [PATCH proxmox-backup 3/4] datastore: move file lock helper to centralized place Date: Tue, 5 May 2026 10:11:35 +0200 Message-ID: <20260505081137.227901-4-c.ebner@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260505081137.227901-1-c.ebner@proxmox.com> References: <20260505081137.227901-1-c.ebner@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1777968613911 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.070 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record URIBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [lib.rs] Message-ID-Hash: ZAQYYJ6D2PHKEEVG3DCETRKSPJZYZTHW X-Message-ID-Hash: ZAQYYJ6D2PHKEEVG3DCETRKSPJZYZTHW X-MailFrom: c.ebner@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox Backup Server development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: Moves the implementation to a more central location, as it is not only used by the datastore contents, but also by the chunk store. No functional changes. Signed-off-by: Christian Ebner --- pbs-datastore/src/backup_info.rs | 35 ++------------------------------ pbs-datastore/src/chunk_store.rs | 2 +- pbs-datastore/src/lib.rs | 34 +++++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 34 deletions(-) diff --git a/pbs-datastore/src/backup_info.rs b/pbs-datastore/src/backup_info.rs index 9919b908a..bae136d3c 100644 --- a/pbs-datastore/src/backup_info.rs +++ b/pbs-datastore/src/backup_info.rs @@ -1,5 +1,5 @@ use std::fmt; -use std::os::unix::io::{AsRawFd, RawFd}; +use std::os::unix::io::RawFd; use std::os::unix::prelude::OsStrExt; use std::path::Path; use std::path::PathBuf; @@ -24,7 +24,7 @@ use crate::datastore::{GROUP_NOTES_FILE_NAME, GROUP_OWNER_FILE_NAME}; use crate::manifest::{BackupManifest, MANIFEST_LOCK_NAME}; use crate::move_journal; use crate::s3::S3_CONTENT_PREFIX; -use crate::{DataBlob, DataStore, DatastoreBackend, DATASTORE_LOCKS_DIR}; +use crate::{lock_helper, DataBlob, DataStore, DatastoreBackend, DATASTORE_LOCKS_DIR}; pub const PROTECTED_MARKER_FILENAME: &str = ".protected"; @@ -1210,34 +1210,3 @@ fn lock_file_path_helper(ns: &BackupNamespace, path: PathBuf) -> PathBuf { to_return.join(format!("{first_eigthy}...{last_eighty}-{hash}")) } - -/// Helps implement the double stat'ing procedure. It avoids certain race conditions upon lock -/// deletion. -/// -/// It also creates the base directory for lock files. -pub(crate) fn lock_helper( - store_name: &str, - path: &std::path::Path, - lock_fn: F, -) -> Result -where - F: Fn(&std::path::Path) -> Result, -{ - let mut lock_dir = Path::new(DATASTORE_LOCKS_DIR).join(store_name); - - if let Some(parent) = path.parent() { - lock_dir = lock_dir.join(parent); - }; - - std::fs::create_dir_all(&lock_dir)?; - - let lock = lock_fn(path)?; - - let inode = nix::sys::stat::fstat(lock.as_raw_fd())?.st_ino; - - if nix::sys::stat::stat(path).map_or(true, |st| inode != st.st_ino) { - bail!("could not acquire lock, another thread modified the lock file"); - } - - Ok(lock) -} diff --git a/pbs-datastore/src/chunk_store.rs b/pbs-datastore/src/chunk_store.rs index 5b07ebdaa..778897974 100644 --- a/pbs-datastore/src/chunk_store.rs +++ b/pbs-datastore/src/chunk_store.rs @@ -944,7 +944,7 @@ impl ChunkStore { timeout: Duration, ) -> Result { let lock_path = self.chunk_lock_path(digest); - let guard = crate::backup_info::lock_helper(self.name(), &lock_path, |path| { + let guard = crate::lock_helper(self.name(), &lock_path, |path| { pbs_config::open_backup_lockfile(path, Some(timeout), true) })?; Ok(guard) diff --git a/pbs-datastore/src/lib.rs b/pbs-datastore/src/lib.rs index 29d203f87..48acba4c8 100644 --- a/pbs-datastore/src/lib.rs +++ b/pbs-datastore/src/lib.rs @@ -157,6 +157,13 @@ #![deny(unsafe_op_in_unsafe_fn)] +use std::os::unix::io::AsRawFd; +use std::path::Path; + +use anyhow::{bail, Error}; + +use pbs_config::BackupLockGuard; + /// Directory path where active operations counters are saved. pub const ACTIVE_OPERATIONS_DIR: &str = concat!( pbs_buildcfg::PROXMOX_BACKUP_RUN_DIR_M!(), @@ -237,3 +244,30 @@ pub use local_chunk_reader::LocalChunkReader; mod local_datastore_lru_cache; pub use local_datastore_lru_cache::LocalDatastoreLruCache; + +/// Helps implement the double stat'ing procedure. It avoids certain race conditions upon lock +/// deletion. +/// +/// It also creates the base directory for lock files. +pub fn lock_helper(store_name: &str, path: &Path, lock_fn: F) -> Result +where + F: Fn(&Path) -> Result, +{ + let mut lock_dir = Path::new(DATASTORE_LOCKS_DIR).join(store_name); + + if let Some(parent) = path.parent() { + lock_dir = lock_dir.join(parent); + }; + + std::fs::create_dir_all(&lock_dir)?; + + let lock = lock_fn(path)?; + + let inode = nix::sys::stat::fstat(lock.as_raw_fd())?.st_ino; + + if nix::sys::stat::stat(path).map_or(true, |st| inode != st.st_ino) { + bail!("could not acquire lock, another thread modified the lock file"); + } + + Ok(lock) +} -- 2.47.3