From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: <pbs-devel-bounces@lists.proxmox.com> Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id B79991FF15C for <inbox@lore.proxmox.com>; Fri, 16 May 2025 13:54:55 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 101B8C0BA; Fri, 16 May 2025 13:55:20 +0200 (CEST) From: Christian Ebner <c.ebner@proxmox.com> To: pbs-devel@lists.proxmox.com Date: Fri, 16 May 2025 13:54:26 +0200 Message-Id: <20250516115429.208563-3-c.ebner@proxmox.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250516115429.208563-1-c.ebner@proxmox.com> References: <20250516115429.208563-1-c.ebner@proxmox.com> MIME-Version: 1.0 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.029 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 Subject: [pbs-devel] [PATCH proxmox-backup 2/5] api: factor out helper converting backup info to snapshot list item X-BeenThere: pbs-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox Backup Server development discussion <pbs-devel.lists.proxmox.com> List-Unsubscribe: <https://lists.proxmox.com/cgi-bin/mailman/options/pbs-devel>, <mailto:pbs-devel-request@lists.proxmox.com?subject=unsubscribe> List-Archive: <http://lists.proxmox.com/pipermail/pbs-devel/> List-Post: <mailto:pbs-devel@lists.proxmox.com> List-Help: <mailto:pbs-devel-request@lists.proxmox.com?subject=help> List-Subscribe: <https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel>, <mailto:pbs-devel-request@lists.proxmox.com?subject=subscribe> Reply-To: Proxmox Backup Server development discussion <pbs-devel@lists.proxmox.com> Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: pbs-devel-bounces@lists.proxmox.com Sender: "pbs-devel" <pbs-devel-bounces@lists.proxmox.com> Move the logic currently provided by a closure to it's dedicated named helper function. Further, in order to reuse the same code for the generation of the snapshot list items to be used by the sync job source readers, move the helper and related helper functions to the tools module. No functional changes intended. Signed-off-by: Christian Ebner <c.ebner@proxmox.com> --- src/api2/admin/datastore.rs | 135 +++--------------------------------- src/tools/mod.rs | 129 ++++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+), 127 deletions(-) diff --git a/src/api2/admin/datastore.rs b/src/api2/admin/datastore.rs index 55cb3a0b3..9b3ffb8e0 100644 --- a/src/api2/admin/datastore.rs +++ b/src/api2/admin/datastore.rs @@ -1,6 +1,5 @@ //! Datastore Management -use std::collections::HashSet; use std::ffi::OsStr; use std::ops::Deref; use std::os::unix::ffi::OsStrExt; @@ -41,13 +40,12 @@ use pbs_api_types::{ BackupContent, BackupGroupDeleteStats, BackupNamespace, BackupType, Counts, CryptMode, DataStoreConfig, DataStoreListItem, DataStoreMountStatus, DataStoreStatus, GarbageCollectionJobStatus, GroupListItem, JobScheduleStatus, KeepOptions, MaintenanceMode, - MaintenanceType, Operation, PruneJobOptions, SnapshotListItem, SnapshotVerifyState, - BACKUP_ARCHIVE_NAME_SCHEMA, BACKUP_ID_SCHEMA, BACKUP_NAMESPACE_SCHEMA, BACKUP_TIME_SCHEMA, - BACKUP_TYPE_SCHEMA, CATALOG_NAME, CLIENT_LOG_BLOB_NAME, DATASTORE_SCHEMA, - IGNORE_VERIFIED_BACKUPS_SCHEMA, MANIFEST_BLOB_NAME, MAX_NAMESPACE_DEPTH, NS_MAX_DEPTH_SCHEMA, - PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_BACKUP, PRIV_DATASTORE_MODIFY, PRIV_DATASTORE_PRUNE, - PRIV_DATASTORE_READ, PRIV_DATASTORE_VERIFY, PRIV_SYS_MODIFY, UPID, UPID_SCHEMA, - VERIFICATION_OUTDATED_AFTER_SCHEMA, + MaintenanceType, Operation, PruneJobOptions, SnapshotListItem, BACKUP_ARCHIVE_NAME_SCHEMA, + BACKUP_ID_SCHEMA, BACKUP_NAMESPACE_SCHEMA, BACKUP_TIME_SCHEMA, BACKUP_TYPE_SCHEMA, + CATALOG_NAME, CLIENT_LOG_BLOB_NAME, DATASTORE_SCHEMA, IGNORE_VERIFIED_BACKUPS_SCHEMA, + MAX_NAMESPACE_DEPTH, NS_MAX_DEPTH_SCHEMA, PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_BACKUP, + PRIV_DATASTORE_MODIFY, PRIV_DATASTORE_PRUNE, PRIV_DATASTORE_READ, PRIV_DATASTORE_VERIFY, + PRIV_SYS_MODIFY, UPID, UPID_SCHEMA, VERIFICATION_OUTDATED_AFTER_SCHEMA, }; use pbs_client::pxar::{create_tar, create_zip}; use pbs_config::CachedUserInfo; @@ -74,8 +72,8 @@ use crate::backup::{ check_ns_privs_full, verify_all_backups, verify_backup_dir, verify_backup_group, verify_filter, ListAccessibleBackupGroups, NS_PRIVS_OK, }; - use crate::server::jobstate::{compute_schedule_status, Job, JobState}; +use crate::tools::{backup_info_to_snapshot_list_item, get_all_snapshot_files, read_backup_index}; const GROUP_NOTES_FILE_NAME: &str = "notes"; @@ -114,56 +112,6 @@ fn check_privs_and_load_store( Ok(datastore) } -fn read_backup_index( - backup_dir: &BackupDir, -) -> Result<(BackupManifest, Vec<BackupContent>), Error> { - let (manifest, index_size) = backup_dir.load_manifest()?; - - let mut result = Vec::new(); - for item in manifest.files() { - result.push(BackupContent { - filename: item.filename.clone(), - crypt_mode: Some(item.crypt_mode), - size: Some(item.size), - }); - } - - result.push(BackupContent { - filename: MANIFEST_BLOB_NAME.to_string(), - crypt_mode: match manifest.signature { - Some(_) => Some(CryptMode::SignOnly), - None => Some(CryptMode::None), - }, - size: Some(index_size), - }); - - Ok((manifest, result)) -} - -fn get_all_snapshot_files( - info: &BackupInfo, -) -> Result<(BackupManifest, Vec<BackupContent>), Error> { - let (manifest, mut files) = read_backup_index(&info.backup_dir)?; - - let file_set = files.iter().fold(HashSet::new(), |mut acc, item| { - acc.insert(item.filename.clone()); - acc - }); - - for file in &info.files { - if file_set.contains(file) { - continue; - } - files.push(BackupContent { - filename: file.to_string(), - size: None, - crypt_mode: None, - }); - } - - Ok((manifest, files)) -} - #[api( input: { properties: { @@ -529,73 +477,6 @@ unsafe fn list_snapshots_blocking( (None, None) => datastore.list_backup_groups(ns.clone())?, }; - let info_to_snapshot_list_item = |owner, info: BackupInfo| { - let backup = info.backup_dir.dir().to_owned(); - let protected = info.protected; - - match get_all_snapshot_files(&info) { - Ok((manifest, files)) => { - // extract the first line from notes - let comment: Option<String> = manifest.unprotected["notes"] - .as_str() - .and_then(|notes| notes.lines().next()) - .map(String::from); - - let fingerprint = match manifest.fingerprint() { - Ok(fp) => fp, - Err(err) => { - eprintln!("error parsing fingerprint: '{}'", err); - None - } - }; - - let verification: Option<SnapshotVerifyState> = match manifest.verify_state() { - Ok(verify) => verify, - Err(err) => { - eprintln!("error parsing verification state : '{}'", err); - None - } - }; - - let size = Some(files.iter().map(|x| x.size.unwrap_or(0)).sum()); - - SnapshotListItem { - backup, - comment, - verification, - fingerprint, - files, - size, - owner, - protected, - } - } - Err(err) => { - eprintln!("error during snapshot file listing: '{}'", err); - let files = info - .files - .into_iter() - .map(|filename| BackupContent { - filename, - size: None, - crypt_mode: None, - }) - .collect(); - - SnapshotListItem { - backup, - comment: None, - verification: None, - fingerprint: None, - files, - size: None, - owner, - protected, - } - } - } - }; - groups.iter().try_fold(Vec::new(), |mut snapshots, group| { let owner = match group.get_owner() { Ok(auth_id) => auth_id, @@ -619,7 +500,7 @@ unsafe fn list_snapshots_blocking( snapshots.extend( group_backups .into_iter() - .map(|info| info_to_snapshot_list_item(Some(owner.clone()), info)), + .map(|info| backup_info_to_snapshot_list_item(&info, &owner)), ); Ok(snapshots) diff --git a/src/tools/mod.rs b/src/tools/mod.rs index 322894dd7..6556effe3 100644 --- a/src/tools/mod.rs +++ b/src/tools/mod.rs @@ -3,9 +3,16 @@ //! This is a collection of small and useful tools. use anyhow::{bail, Error}; +use std::collections::HashSet; +use pbs_api_types::{ + Authid, BackupContent, CryptMode, SnapshotListItem, SnapshotVerifyState, MANIFEST_BLOB_NAME, +}; use proxmox_http::{client::Client, HttpOptions, ProxyConfig}; +use pbs_datastore::backup_info::{BackupDir, BackupInfo}; +use pbs_datastore::manifest::BackupManifest; + pub mod config; pub mod disks; pub mod fs; @@ -61,3 +68,125 @@ pub fn setup_safe_path_env() { std::env::remove_var(name); } } + +pub(crate) fn read_backup_index( + backup_dir: &BackupDir, +) -> Result<(BackupManifest, Vec<BackupContent>), Error> { + let (manifest, index_size) = backup_dir.load_manifest()?; + + let mut result = Vec::new(); + for item in manifest.files() { + result.push(BackupContent { + filename: item.filename.clone(), + crypt_mode: Some(item.crypt_mode), + size: Some(item.size), + }); + } + + result.push(BackupContent { + filename: MANIFEST_BLOB_NAME.to_string(), + crypt_mode: match manifest.signature { + Some(_) => Some(CryptMode::SignOnly), + None => Some(CryptMode::None), + }, + size: Some(index_size), + }); + + Ok((manifest, result)) +} + +pub(crate) fn get_all_snapshot_files( + info: &BackupInfo, +) -> Result<(BackupManifest, Vec<BackupContent>), Error> { + let (manifest, mut files) = read_backup_index(&info.backup_dir)?; + + let file_set = files.iter().fold(HashSet::new(), |mut acc, item| { + acc.insert(item.filename.clone()); + acc + }); + + for file in &info.files { + if file_set.contains(file) { + continue; + } + files.push(BackupContent { + filename: file.to_string(), + size: None, + crypt_mode: None, + }); + } + + Ok((manifest, files)) +} + +/// Helper to transform `BackupInfo` to `SnapshotListItem` with given owner. +pub(crate) fn backup_info_to_snapshot_list_item( + info: &BackupInfo, + owner: &Authid, +) -> SnapshotListItem { + let backup = info.backup_dir.dir().to_owned(); + let protected = info.protected; + let owner = Some(owner.to_owned()); + + match get_all_snapshot_files(info) { + Ok((manifest, files)) => { + // extract the first line from notes + let comment: Option<String> = manifest.unprotected["notes"] + .as_str() + .and_then(|notes| notes.lines().next()) + .map(String::from); + + let fingerprint = match manifest.fingerprint() { + Ok(fp) => fp, + Err(err) => { + eprintln!("error parsing fingerprint: '{}'", err); + None + } + }; + + let verification: Option<SnapshotVerifyState> = match manifest.verify_state() { + Ok(verify) => verify, + Err(err) => { + eprintln!("error parsing verification state : '{err}'"); + None + } + }; + + let size = Some(files.iter().map(|x| x.size.unwrap_or(0)).sum()); + + SnapshotListItem { + backup, + comment, + verification, + fingerprint, + files, + size, + owner, + protected, + } + } + Err(err) => { + eprintln!("error during snapshot file listing: '{err}'"); + let files = info + .files + .iter() + .map(|filename| BackupContent { + filename: filename.to_owned(), + size: None, + crypt_mode: None, + }) + .collect(); + + SnapshotListItem { + backup, + comment: None, + verification: None, + fingerprint: None, + files, + size: None, + owner, + protected, + } + } + } +} -- 2.39.5 _______________________________________________ pbs-devel mailing list pbs-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel