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 F036F1FF141 for ; Fri, 13 Feb 2026 11:43:11 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id A5E1F361D8; Fri, 13 Feb 2026 11:43:58 +0100 (CET) From: Christian Ebner To: pbs-devel@lists.proxmox.com Subject: [PATCH proxmox-backup] fix #7305: client: restore: filter out last snapshot if not finished Date: Fri, 13 Feb 2026 11:43:04 +0100 Message-ID: <20260213104304.375430-1-c.ebner@proxmox.com> X-Mailer: git-send-email 2.47.3 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1770979399740 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.047 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 RCVD_IN_VALIDITY_CERTIFIED_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. RCVD_IN_VALIDITY_RPBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. RCVD_IN_VALIDITY_SAFE_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: FGKUAY7FAZ4JHB4HZ2UF5D5RDJP32SR5 X-Message-ID-Hash: FGKUAY7FAZ4JHB4HZ2UF5D5RDJP32SR5 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: When invoking the client CLI for restore via: ``` proxmox-backup-client restore ``` but providing a backup group only, the client falls back to restore the last snapshot of that group for convenience. The snapshot listing used to identify the last snapshot returns however all snapshots, including unfinished ones, a restore on an unfinished one will therefore fail. Fix this by filtering the snapshot to be restored by checking the presence of the manifest file and are therefore considered finished. If the file is not present, skip the snapshot and try for the next one, if any. Also adapt the helper function name to reflect this change. Ideally this would happen on the server side, guarded by some flag, but that will not work with older server versions. Fixes: https://bugzilla.proxmox.com/show_bug.cgi?id=7305 Signed-off-by: Christian Ebner --- changes since version 1: - Skip any considered snapshot without manifest - Refactor based on Fabian's suggestions proxmox-backup-client/src/main.rs | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/proxmox-backup-client/src/main.rs b/proxmox-backup-client/src/main.rs index 999e50205..10a864a0f 100644 --- a/proxmox-backup-client/src/main.rs +++ b/proxmox-backup-client/src/main.rs @@ -160,7 +160,7 @@ async fn api_datastore_list_snapshots( Ok(result["data"].take()) } -pub async fn api_datastore_latest_snapshot( +pub async fn api_datastore_latest_finished_snapshot( client: &HttpClient, store: &str, ns: &BackupNamespace, @@ -175,7 +175,30 @@ pub async fn api_datastore_latest_snapshot( list.sort_unstable_by(|a, b| b.backup.time.cmp(&a.backup.time)); - Ok((group, list[0].backup.time).into()) + let last_finished_time = list + .iter() + .find_map(|snapshot_item| { + // only once a snapshots contains the manifest it is considered finished + snapshot_item + .files + .iter() + .find_map(|content| { + if content.filename == MANIFEST_BLOB_NAME.as_ref() { + Some(snapshot_item.backup.time) + } else { + None + } + }) + .or_else(|| { + log::info!("skipped not finished snapshot {}", snapshot_item.backup); + None + }) + }) + .ok_or_else(|| { + format_err!("backup group {group} does not contain any finished snapshots.") + })?; + + Ok((group, last_finished_time).into()) } pub async fn dir_or_last_from_group( @@ -187,7 +210,7 @@ pub async fn dir_or_last_from_group( match path.parse::()? { BackupPart::Dir(dir) => Ok(dir), BackupPart::Group(group) => { - api_datastore_latest_snapshot(client, repo.store(), ns, group).await + api_datastore_latest_finished_snapshot(client, repo.store(), ns, group).await } } } -- 2.47.3