From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id 00B1D6739D for ; Thu, 30 Jul 2020 11:31:34 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id E99EF152EC for ; Thu, 30 Jul 2020 11:31:34 +0200 (CEST) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [212.186.127.180]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS id 7F6F0152DD for ; Thu, 30 Jul 2020 11:31:33 +0200 (CEST) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id 4083B433F6 for ; Thu, 30 Jul 2020 11:31:33 +0200 (CEST) To: Proxmox Backup Server development discussion , Dietmar Maurer References: <20200730085031.30790-1-dietmar@proxmox.com> From: Stefan Reiter Message-ID: <89116eff-a7ca-a428-08d2-52432a3173e0@proxmox.com> Date: Thu, 30 Jul 2020 11:31:32 +0200 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.9.0 MIME-Version: 1.0 In-Reply-To: <20200730085031.30790-1-dietmar@proxmox.com> Content-Type: text/plain; charset=utf-8; format=flowed Content-Language: en-US Content-Transfer-Encoding: 7bit X-SPAM-LEVEL: Spam detection results: 0 AWL 0.351 Adjusted score from AWL reputation of From: address KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment NICE_REPLY_A -0.812 Looks like a legit reply (A) RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Subject: Re: [pbs-devel] [PATCH backup] src/api2/backup.rs: aquire backup lock earlier in create_locked_backup_group() X-BeenThere: pbs-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox Backup Server development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 30 Jul 2020 09:31:35 -0000 Looking again, I don't think the race would have mattered (since we checked the owner after create_backup_group() anyway), but locking early is still superior and also works nicely for pull.rs. Reviewed-By: Stefan Reiter On 7/30/20 10:50 AM, Dietmar Maurer wrote: > --- > src/api2/backup.rs | 8 ++++---- > src/backup/datastore.rs | 14 +++++++++----- > src/client/pull.rs | 2 +- > 3 files changed, 14 insertions(+), 10 deletions(-) > > diff --git a/src/api2/backup.rs b/src/api2/backup.rs > index b9dff1f..ad13faa 100644 > --- a/src/api2/backup.rs > +++ b/src/api2/backup.rs > @@ -88,7 +88,10 @@ async move { > let env_type = rpcenv.env_type(); > > let backup_group = BackupGroup::new(backup_type, backup_id); > - let owner = datastore.create_backup_group(&backup_group, &username)?; > + > + // lock backup group to only allow one backup per group at a time > + let (owner, _group_guard) = datastore.create_locked_backup_group(&backup_group, &username)?; > + > // permission check > if owner != username { // only the owner is allowed to create additional snapshots > bail!("backup owner check failed ({} != {})", username, owner); > @@ -103,9 +106,6 @@ async move { > } > } > > - // lock backup group to only allow one backup per group at a time > - let _group_guard = backup_group.lock(&datastore.base_path())?; > - > let (path, is_new) = datastore.create_backup_dir(&backup_dir)?; > if !is_new { bail!("backup directory already exists."); } > > diff --git a/src/backup/datastore.rs b/src/backup/datastore.rs > index 9c2f285..af964f6 100644 > --- a/src/backup/datastore.rs > +++ b/src/backup/datastore.rs > @@ -8,7 +8,7 @@ use anyhow::{bail, format_err, Error}; > use lazy_static::lazy_static; > use chrono::{DateTime, Utc}; > > -use super::backup_info::{BackupGroup, BackupDir, BackupInfo}; > +use super::backup_info::{BackupGroup, BackupGroupGuard, BackupDir, BackupInfo}; > use super::chunk_store::ChunkStore; > use super::dynamic_index::{DynamicIndexReader, DynamicIndexWriter}; > use super::fixed_index::{FixedIndexReader, FixedIndexWriter}; > @@ -318,11 +318,13 @@ impl DataStore { > Ok(()) > } > > - /// Create a backup group if it does not already exists. > + /// Create (if it does not already exists) and lock a backup group > /// > /// And set the owner to 'userid'. If the group already exists, it returns the > /// current owner (instead of setting the owner). > - pub fn create_backup_group(&self, backup_group: &BackupGroup, userid: &str) -> Result { > + /// > + /// This also aquires an exclusive lock on the directory and returns the lock guard. > + pub fn create_locked_backup_group(&self, backup_group: &BackupGroup, userid: &str) -> Result<(String, BackupGroupGuard), Error> { > > // create intermediate path first: > let base_path = self.base_path(); > @@ -336,13 +338,15 @@ impl DataStore { > // create the last component now > match std::fs::create_dir(&full_path) { > Ok(_) => { > + let guard = backup_group.lock(&base_path)?; > self.set_owner(backup_group, userid, false)?; > let owner = self.get_owner(backup_group)?; // just to be sure > - Ok(owner) > + Ok((owner, guard)) > } > Err(ref err) if err.kind() == io::ErrorKind::AlreadyExists => { > + let guard = backup_group.lock(&base_path)?; > let owner = self.get_owner(backup_group)?; // just to be sure > - Ok(owner) > + Ok((owner, guard)) > } > Err(err) => bail!("unable to create backup group {:?} - {}", full_path, err), > } > diff --git a/src/client/pull.rs b/src/client/pull.rs > index c44cb9f..fc193a8 100644 > --- a/src/client/pull.rs > +++ b/src/client/pull.rs > @@ -406,7 +406,7 @@ pub async fn pull_store( > for item in list { > let group = BackupGroup::new(&item.backup_type, &item.backup_id); > > - let owner = tgt_store.create_backup_group(&group, &username)?; > + let (owner, _lock_guard) = tgt_store.create_locked_backup_group(&group, &username)?; > // permission check > if owner != username { // only the owner is allowed to create additional snapshots > worker.log(format!("sync group {}/{} failed - owner check failed ({} != {})", >