From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id 3CFE11FF163 for ; Thu, 24 Oct 2024 10:01:40 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 7276F9885; Thu, 24 Oct 2024 10:02:19 +0200 (CEST) From: Christian Ebner To: pbs-devel@lists.proxmox.com Date: Thu, 24 Oct 2024 10:01:48 +0200 Message-Id: <20241024080150.30200-4-c.ebner@proxmox.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20241024080150.30200-1-c.ebner@proxmox.com> References: <20241024080150.30200-1-c.ebner@proxmox.com> MIME-Version: 1.0 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.028 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 v3 proxmox-backup 3/5] client/server: use dedicated api type for all archive names 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: , Reply-To: Proxmox Backup Server development discussion Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: pbs-devel-bounces@lists.proxmox.com Sender: "pbs-devel" Instead of using the plain String or slices of it, use the dedicated api type and its methods to parse and check for archive type based on archive filename extension. Thereby, keeping the checks and mappings in the api type and resticting function parameters by the narrower wrapper type to reduce potential misuse. A positive ergonomic side effect of this is that commands now also accept the archive type extension optionally, when passing the archive name. E.g. ``` proxmox-backup-client restore .pxar.didx ``` is equal to ``` proxmox-backup-client restore .pxar ``` Signed-off-by: Christian Ebner --- changes since version 2: - rebased onto current master - reworded commit message pbs-client/src/backup_reader.rs | 14 ++-- pbs-client/src/backup_writer.rs | 39 +++++----- pbs-client/src/pxar/tools.rs | 3 +- pbs-client/src/tools/mod.rs | 24 +++--- pbs-datastore/src/manifest.rs | 30 +++++--- proxmox-backup-client/src/catalog.rs | 31 ++++---- proxmox-backup-client/src/helper.rs | 7 +- proxmox-backup-client/src/main.rs | 111 ++++++++++++++++----------- proxmox-backup-client/src/mount.rs | 33 ++++---- proxmox-file-restore/src/main.rs | 12 ++- src/api2/admin/datastore.rs | 60 +++++++-------- src/bin/proxmox_backup_debug/diff.rs | 16 ++-- src/server/pull.rs | 15 ++-- 13 files changed, 214 insertions(+), 181 deletions(-) diff --git a/pbs-client/src/backup_reader.rs b/pbs-client/src/backup_reader.rs index 4706abc78..533b9dc45 100644 --- a/pbs-client/src/backup_reader.rs +++ b/pbs-client/src/backup_reader.rs @@ -6,7 +6,7 @@ use std::sync::Arc; use futures::future::AbortHandle; use serde_json::{json, Value}; -use pbs_api_types::{BackupDir, BackupNamespace}; +use pbs_api_types::{BackupArchiveName, BackupDir, BackupNamespace}; use pbs_datastore::data_blob::DataBlob; use pbs_datastore::data_blob_reader::DataBlobReader; use pbs_datastore::dynamic_index::DynamicIndexReader; @@ -145,11 +145,11 @@ impl BackupReader { pub async fn download_blob( &self, manifest: &BackupManifest, - name: &str, + name: &BackupArchiveName, ) -> Result, Error> { let mut tmpfile = crate::tools::create_tmp_file()?; - self.download(name, &mut tmpfile).await?; + self.download(name.as_ref(), &mut tmpfile).await?; tmpfile.seek(SeekFrom::Start(0))?; let (csum, size) = sha256(&mut tmpfile)?; @@ -167,11 +167,11 @@ impl BackupReader { pub async fn download_dynamic_index( &self, manifest: &BackupManifest, - name: &str, + name: &BackupArchiveName, ) -> Result { let mut tmpfile = crate::tools::create_tmp_file()?; - self.download(name, &mut tmpfile).await?; + self.download(name.as_ref(), &mut tmpfile).await?; let index = DynamicIndexReader::new(tmpfile) .map_err(|err| format_err!("unable to read dynamic index '{}' - {}", name, err))?; @@ -190,11 +190,11 @@ impl BackupReader { pub async fn download_fixed_index( &self, manifest: &BackupManifest, - name: &str, + name: &BackupArchiveName, ) -> Result { let mut tmpfile = crate::tools::create_tmp_file()?; - self.download(name, &mut tmpfile).await?; + self.download(name.as_ref(), &mut tmpfile).await?; let index = FixedIndexReader::new(tmpfile) .map_err(|err| format_err!("unable to read fixed index '{}' - {}", name, err))?; diff --git a/pbs-client/src/backup_writer.rs b/pbs-client/src/backup_writer.rs index 8adaf9ef2..6eca0092b 100644 --- a/pbs-client/src/backup_writer.rs +++ b/pbs-client/src/backup_writer.rs @@ -11,7 +11,7 @@ use tokio::io::AsyncReadExt; use tokio::sync::{mpsc, oneshot}; use tokio_stream::wrappers::ReceiverStream; -use pbs_api_types::{ArchiveType, BackupDir, BackupNamespace}; +use pbs_api_types::{ArchiveType, BackupArchiveName, BackupDir, BackupNamespace}; use pbs_datastore::data_blob::{ChunkInfo, DataBlob, DataChunkBuilder}; use pbs_datastore::dynamic_index::DynamicIndexReader; use pbs_datastore::fixed_index::FixedIndexReader; @@ -270,7 +270,7 @@ impl BackupWriter { pub async fn upload_stream( &self, - archive_name: &str, + archive_name: &BackupArchiveName, stream: impl Stream>, options: UploadOptions, injections: Option>, @@ -296,13 +296,13 @@ impl BackupWriter { if !manifest .files() .iter() - .any(|file| file.filename == archive_name) + .any(|file| file.filename == archive_name.as_ref()) { log::info!("Previous manifest does not contain an archive called '{archive_name}', skipping download.."); } else { // try, but ignore errors - match ArchiveType::from_path(archive_name) { - Ok(ArchiveType::FixedIndex) => { + match archive_name.archive_type() { + ArchiveType::FixedIndex => { if let Err(err) = self .download_previous_fixed_index( archive_name, @@ -314,7 +314,7 @@ impl BackupWriter { log::warn!("Error downloading .fidx from previous manifest: {}", err); } } - Ok(ArchiveType::DynamicIndex) => { + ArchiveType::DynamicIndex => { if let Err(err) = self .download_previous_dynamic_index( archive_name, @@ -338,12 +338,6 @@ impl BackupWriter { .as_u64() .unwrap(); - let archive = if log::log_enabled!(log::Level::Debug) { - archive_name - } else { - pbs_tools::format::strip_server_file_extension(archive_name) - }; - let upload_stats = Self::upload_chunk_info_stream( self.h2.clone(), wid, @@ -357,12 +351,17 @@ impl BackupWriter { }, options.compress, injections, - archive, + archive_name, ) .await?; let size_dirty = upload_stats.size - upload_stats.size_reused; let size: HumanByte = upload_stats.size.into(); + let archive = if log::log_enabled!(log::Level::Debug) { + archive_name.to_string() + } else { + archive_name.without_type_extension() + }; if upload_stats.chunk_injected > 0 { log::info!( @@ -372,7 +371,7 @@ impl BackupWriter { ); } - if archive_name != CATALOG_NAME { + if archive_name.as_ref() != CATALOG_NAME { let speed: HumanByte = ((size_dirty * 1_000_000) / (upload_stats.duration.as_micros() as usize)).into(); let size_dirty: HumanByte = size_dirty.into(); @@ -541,7 +540,7 @@ impl BackupWriter { pub async fn download_previous_fixed_index( &self, - archive_name: &str, + archive_name: &BackupArchiveName, manifest: &BackupManifest, known_chunks: Arc>>, ) -> Result { @@ -576,7 +575,7 @@ impl BackupWriter { pub async fn download_previous_dynamic_index( &self, - archive_name: &str, + archive_name: &BackupArchiveName, manifest: &BackupManifest, known_chunks: Arc>>, ) -> Result { @@ -651,7 +650,7 @@ impl BackupWriter { crypt_config: Option>, compress: bool, injections: Option>, - archive: &str, + archive: &BackupArchiveName, ) -> impl Future> { let total_chunks = Arc::new(AtomicUsize::new(0)); let total_chunks2 = total_chunks.clone(); @@ -683,9 +682,9 @@ impl BackupWriter { let index_csum = Arc::new(Mutex::new(Some(openssl::sha::Sha256::new()))); let index_csum_2 = index_csum.clone(); - let progress_handle = if archive.ends_with(".img") - || archive.ends_with(".pxar") - || archive.ends_with(".ppxar") + let progress_handle = if archive.ends_with(".img.fidx") + || archive.ends_with(".pxar.didx") + || archive.ends_with(".ppxar.didx") { Some(tokio::spawn(async move { loop { diff --git a/pbs-client/src/pxar/tools.rs b/pbs-client/src/pxar/tools.rs index 7a4d75522..2deee5056 100644 --- a/pbs-client/src/pxar/tools.rs +++ b/pbs-client/src/pxar/tools.rs @@ -14,6 +14,7 @@ use pxar::accessor::ReadAt; use pxar::format::StatxTimestamp; use pxar::{mode, Entry, EntryKind, Metadata}; +use pbs_api_types::BackupArchiveName; use pbs_datastore::catalog::{ArchiveEntry, CatalogEntryType, DirEntryAttribute}; use pbs_datastore::dynamic_index::{BufferedDynamicReader, LocalDynamicReadAt}; @@ -358,7 +359,7 @@ pub fn handle_root_with_optional_format_version_prelude, manifest: &BackupManifest, crypt_config: Option>, diff --git a/pbs-client/src/tools/mod.rs b/pbs-client/src/tools/mod.rs index 28db6f348..4b1d7a910 100644 --- a/pbs-client/src/tools/mod.rs +++ b/pbs-client/src/tools/mod.rs @@ -17,7 +17,10 @@ use proxmox_router::cli::{complete_file_name, shellword_split}; use proxmox_schema::*; use proxmox_sys::fs::file_get_json; -use pbs_api_types::{Authid, BackupNamespace, RateLimitConfig, UserWithTokens, BACKUP_REPO_URL}; +use pbs_api_types::{ + ArchiveType, Authid, BackupArchiveName, BackupNamespace, RateLimitConfig, UserWithTokens, + BACKUP_REPO_URL, +}; use pbs_datastore::BackupManifest; use crate::{BackupRepository, HttpClient, HttpClientOptions}; @@ -548,12 +551,12 @@ pub fn place_xdg_file( } pub fn get_pxar_archive_names( - archive_name: &str, + archive_name: &BackupArchiveName, manifest: &BackupManifest, -) -> Result<(String, Option), Error> { - let (filename, ext) = match archive_name.strip_suffix(".didx") { - Some(filename) => (filename, ".didx"), - None => (archive_name, ""), +) -> Result<(BackupArchiveName, Option), Error> { + let (filename, ext) = match archive_name.archive_type() { + ArchiveType::DynamicIndex => (archive_name.without_type_extension(), ".didx"), + _ => (archive_name.to_string(), ""), }; // Check if archive with given extension is present @@ -568,8 +571,8 @@ pub fn get_pxar_archive_names( .or_else(|| filename.strip_suffix(".ppxar")) { return Ok(( - format!("{base}.mpxar{ext}"), - Some(format!("{base}.ppxar{ext}")), + format!("{base}.mpxar{ext}").as_str().try_into()?, + Some(format!("{base}.ppxar{ext}").as_str().try_into()?), )); } return Ok((archive_name.to_owned(), None)); @@ -577,7 +580,10 @@ pub fn get_pxar_archive_names( // if not, try fallback from regular to split archive if let Some(base) = filename.strip_suffix(".pxar") { - return get_pxar_archive_names(&format!("{base}.mpxar{ext}"), manifest); + return get_pxar_archive_names( + &format!("{base}.mpxar{ext}").as_str().try_into()?, + manifest, + ); } bail!("archive not found in manifest"); diff --git a/pbs-datastore/src/manifest.rs b/pbs-datastore/src/manifest.rs index 823c85003..647f213d7 100644 --- a/pbs-datastore/src/manifest.rs +++ b/pbs-datastore/src/manifest.rs @@ -3,7 +3,7 @@ use anyhow::{bail, format_err, Error}; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; -use pbs_api_types::{ArchiveType, BackupType, CryptMode, Fingerprint}; +use pbs_api_types::{BackupArchiveName, BackupType, CryptMode, Fingerprint}; use pbs_tools::crypt_config::CryptConfig; pub const MANIFEST_BLOB_NAME: &str = "index.json.blob"; @@ -68,14 +68,13 @@ impl BackupManifest { pub fn add_file( &mut self, - filename: String, + filename: &BackupArchiveName, size: u64, csum: [u8; 32], crypt_mode: CryptMode, ) -> Result<(), Error> { - let _archive_type = ArchiveType::from_path(&filename)?; // check type self.files.push(FileInfo { - filename, + filename: filename.to_string(), size, csum, crypt_mode, @@ -87,8 +86,11 @@ impl BackupManifest { &self.files[..] } - pub fn lookup_file_info(&self, name: &str) -> Result<&FileInfo, Error> { - let info = self.files.iter().find(|item| item.filename == name); + pub fn lookup_file_info(&self, name: &BackupArchiveName) -> Result<&FileInfo, Error> { + let info = self + .files + .iter() + .find(|item| item.filename == name.as_ref()); match info { None => bail!("manifest does not contain file '{}'", name), @@ -96,7 +98,12 @@ impl BackupManifest { } } - pub fn verify_file(&self, name: &str, csum: &[u8; 32], size: u64) -> Result<(), Error> { + pub fn verify_file( + &self, + name: &BackupArchiveName, + csum: &[u8; 32], + size: u64, + ) -> Result<(), Error> { let info = self.lookup_file_info(name)?; if size != info.size { @@ -256,8 +263,13 @@ fn test_manifest_signature() -> Result<(), Error> { let mut manifest = BackupManifest::new("host/elsa/2020-06-26T13:56:05Z".parse()?); - manifest.add_file("test1.img.fidx".into(), 200, [1u8; 32], CryptMode::Encrypt)?; - manifest.add_file("abc.blob".into(), 200, [2u8; 32], CryptMode::None)?; + manifest.add_file( + &"test1.img.fidx".try_into()?, + 200, + [1u8; 32], + CryptMode::Encrypt, + )?; + manifest.add_file(&"abc.blob".try_into()?, 200, [2u8; 32], CryptMode::None)?; manifest.unprotected["note"] = "This is not protected by the signature.".into(); diff --git a/proxmox-backup-client/src/catalog.rs b/proxmox-backup-client/src/catalog.rs index a55c9effe..6e2421d1a 100644 --- a/proxmox-backup-client/src/catalog.rs +++ b/proxmox-backup-client/src/catalog.rs @@ -7,9 +7,8 @@ use serde_json::Value; use proxmox_router::cli::*; use proxmox_schema::api; -use pbs_api_types::BackupNamespace; +use pbs_api_types::{BackupArchiveName, BackupNamespace}; use pbs_client::pxar::tools::get_remote_pxar_reader; -use pbs_client::tools::has_pxar_filename_extension; use pbs_client::tools::key_source::get_encryption_key_password; use pbs_client::{BackupReader, RemoteChunkReader}; use pbs_tools::crypt_config::CryptConfig; @@ -90,7 +89,8 @@ async fn dump_catalog(param: Value) -> Result { let (manifest, _) = client.download_manifest().await?; manifest.check_fingerprint(crypt_config.as_ref().map(Arc::as_ref))?; - let file_info = match manifest.lookup_file_info(CATALOG_NAME) { + let catalog_name: BackupArchiveName = CATALOG_NAME.try_into()?; + let file_info = match manifest.lookup_file_info(&catalog_name) { Ok(file_info) => file_info, Err(err) => { let mut metadata_archives = Vec::new(); @@ -104,7 +104,7 @@ async fn dump_catalog(param: Value) -> Result { for archive in &metadata_archives { let (reader, archive_size) = get_remote_pxar_reader( - &archive, + &archive.as_str().try_into()?, client.clone(), &manifest, crypt_config.clone(), @@ -128,7 +128,7 @@ async fn dump_catalog(param: Value) -> Result { }; let index = client - .download_dynamic_index(&manifest, CATALOG_NAME) + .download_dynamic_index(&manifest, &catalog_name) .await?; let most_used = index.find_most_used_chunks(8); @@ -170,8 +170,7 @@ async fn dump_catalog(param: Value) -> Result { description: "Group/Snapshot path.", }, "archive-name": { - type: String, - description: "Backup archive name.", + type: BackupArchiveName, }, "repository": { optional: true, @@ -195,7 +194,8 @@ async fn catalog_shell(param: Value) -> Result<(), Error> { let client = connect(&repo)?; let backup_ns = optional_ns_param(¶m)?; let path = required_string_param(¶m, "snapshot")?; - let archive_name = required_string_param(¶m, "archive-name")?; + let server_archive_name: BackupArchiveName = + required_string_param(¶m, "archive-name")?.try_into()?; let backup_dir = dir_or_last_from_group(&client, &repo, &backup_ns, path).await?; @@ -214,9 +214,7 @@ async fn catalog_shell(param: Value) -> Result<(), Error> { } }; - let server_archive_name = if has_pxar_filename_extension(archive_name, false) { - format!("{}.didx", archive_name) - } else { + if !server_archive_name.has_pxar_filename_extension() { bail!("Can only mount pxar archives."); }; @@ -233,7 +231,8 @@ async fn catalog_shell(param: Value) -> Result<(), Error> { let (manifest, _) = client.download_manifest().await?; manifest.check_fingerprint(crypt_config.as_ref().map(Arc::as_ref))?; - if let Err(_err) = manifest.lookup_file_info(CATALOG_NAME) { + let catalog_name: BackupArchiveName = CATALOG_NAME.try_into().unwrap(); + if let Err(_err) = manifest.lookup_file_info(&catalog_name) { // No catalog, fallback to pxar archive accessor if present let accessor = helper::get_pxar_fuse_accessor( &server_archive_name, @@ -243,7 +242,7 @@ async fn catalog_shell(param: Value) -> Result<(), Error> { ) .await?; - let state = Shell::new(None, &server_archive_name, accessor).await?; + let state = Shell::new(None, &server_archive_name.as_ref(), accessor).await?; log::info!("Starting interactive shell"); state.shell().await?; record_repository(&repo); @@ -267,11 +266,11 @@ async fn catalog_shell(param: Value) -> Result<(), Error> { // Note: do not use values stored in index (not trusted) - instead, computed them again let (csum, size) = index.compute_csum(); - manifest.verify_file(CATALOG_NAME, &csum, size)?; + manifest.verify_file(&catalog_name, &csum, size)?; let most_used = index.find_most_used_chunks(8); - let file_info = manifest.lookup_file_info(CATALOG_NAME)?; + let file_info = manifest.lookup_file_info(&catalog_name)?; let chunk_reader = RemoteChunkReader::new( client.clone(), crypt_config, @@ -286,7 +285,7 @@ async fn catalog_shell(param: Value) -> Result<(), Error> { catalogfile.seek(SeekFrom::Start(0))?; let catalog_reader = CatalogReader::new(catalogfile); - let state = Shell::new(Some(catalog_reader), &server_archive_name, decoder).await?; + let state = Shell::new(Some(catalog_reader), &server_archive_name.as_ref(), decoder).await?; log::info!("Starting interactive shell"); state.shell().await?; diff --git a/proxmox-backup-client/src/helper.rs b/proxmox-backup-client/src/helper.rs index 60355d7d0..642d66a7b 100644 --- a/proxmox-backup-client/src/helper.rs +++ b/proxmox-backup-client/src/helper.rs @@ -1,6 +1,7 @@ use std::sync::Arc; use anyhow::Error; +use pbs_api_types::BackupArchiveName; use pbs_client::{BackupReader, RemoteChunkReader}; use pbs_datastore::BackupManifest; use pbs_tools::crypt_config::CryptConfig; @@ -8,7 +9,7 @@ use pbs_tools::crypt_config::CryptConfig; use crate::{BufferedDynamicReadAt, BufferedDynamicReader, IndexFile}; pub(crate) async fn get_pxar_fuse_accessor( - archive_name: &str, + archive_name: &BackupArchiveName, client: Arc, manifest: &BackupManifest, crypt_config: Option>, @@ -44,7 +45,7 @@ pub(crate) async fn get_pxar_fuse_accessor( } pub(crate) async fn get_pxar_fuse_reader( - archive_name: &str, + archive_name: &BackupArchiveName, client: Arc, manifest: &BackupManifest, crypt_config: Option>, @@ -57,7 +58,7 @@ pub(crate) async fn get_pxar_fuse_reader( } pub(crate) async fn get_buffered_pxar_reader( - archive_name: &str, + archive_name: &BackupArchiveName, client: Arc, manifest: &BackupManifest, crypt_config: Option>, diff --git a/proxmox-backup-client/src/main.rs b/proxmox-backup-client/src/main.rs index f6fb3555e..c699e7687 100644 --- a/proxmox-backup-client/src/main.rs +++ b/proxmox-backup-client/src/main.rs @@ -25,10 +25,10 @@ use pxar::accessor::aio::Accessor; use pxar::accessor::{MaybeReady, ReadAt, ReadAtOperation}; use pbs_api_types::{ - ArchiveType, Authid, BackupDir, BackupGroup, BackupNamespace, BackupPart, BackupType, - ClientRateLimitConfig, CryptMode, Fingerprint, GroupListItem, PruneJobOptions, PruneListItem, - RateLimitConfig, SnapshotListItem, StorageStatus, BACKUP_ID_SCHEMA, BACKUP_NAMESPACE_SCHEMA, - BACKUP_TIME_SCHEMA, BACKUP_TYPE_SCHEMA, + ArchiveType, Authid, BackupArchiveName, BackupDir, BackupGroup, BackupNamespace, BackupPart, + BackupType, ClientRateLimitConfig, CryptMode, Fingerprint, GroupListItem, PruneJobOptions, + PruneListItem, RateLimitConfig, SnapshotListItem, StorageStatus, BACKUP_ID_SCHEMA, + BACKUP_NAMESPACE_SCHEMA, BACKUP_TIME_SCHEMA, BACKUP_TYPE_SCHEMA, }; use pbs_client::catalog_shell::Shell; use pbs_client::pxar::{ErrorHandler as PxarErrorHandler, MetadataArchiveReader, PxarPrevRef}; @@ -36,7 +36,7 @@ use pbs_client::tools::{ complete_archive_name, complete_auth_id, complete_backup_group, complete_backup_snapshot, complete_backup_source, complete_chunk_size, complete_group_or_snapshot, complete_img_archive_name, complete_namespace, complete_pxar_archive_name, complete_repository, - connect, connect_rate_limited, extract_repository_from_value, has_pxar_filename_extension, + connect, connect_rate_limited, extract_repository_from_value, key_source::{ crypto_parameters, format_key_source, get_encryption_key_password, KEYFD_SCHEMA, KEYFILE_SCHEMA, MASTER_PUBKEY_FD_SCHEMA, MASTER_PUBKEY_FILE_SCHEMA, @@ -196,8 +196,8 @@ pub async fn dir_or_last_from_group( async fn backup_directory>( client: &BackupWriter, dir_path: P, - archive_name: &str, - payload_target: Option<&str>, + archive_name: &BackupArchiveName, + payload_target: Option<&BackupArchiveName>, chunk_size: Option, catalog: Option>>>>>, pxar_create_options: pbs_client::pxar::PxarCreateOptions, @@ -276,7 +276,7 @@ async fn backup_directory>( async fn backup_image>( client: &BackupWriter, image_path: P, - archive_name: &str, + archive_name: &BackupArchiveName, chunk_size: Option, upload_options: UploadOptions, ) -> Result { @@ -605,8 +605,9 @@ fn spawn_catalog_upload( }; tokio::spawn(async move { + let catalog_name: BackupArchiveName = CATALOG_NAME.try_into().unwrap(); let catalog_upload_result = client - .upload_stream(CATALOG_NAME, catalog_chunk_stream, upload_options, None) + .upload_stream(&catalog_name, catalog_chunk_stream, upload_options, None) .await; if let Err(ref err) = catalog_upload_result { @@ -1005,13 +1006,21 @@ async fn create_backup( }; for (backup_type, filename, target_base, extension, size) in upload_list { - let target = format!("{target_base}.{extension}"); + let target: BackupArchiveName = format!("{target_base}.{extension}").as_str().try_into()?; match (backup_type, dry_run) { // dry-run - (BackupSpecificationType::CONFIG, true) => log_file("config file", &filename, &target), - (BackupSpecificationType::LOGFILE, true) => log_file("log file", &filename, &target), - (BackupSpecificationType::PXAR, true) => log_file("directory", &filename, &target), - (BackupSpecificationType::IMAGE, true) => log_file("image", &filename, &target), + (BackupSpecificationType::CONFIG, true) => { + log_file("config file", &filename, target.as_ref()) + } + (BackupSpecificationType::LOGFILE, true) => { + log_file("log file", &filename, target.as_ref()) + } + (BackupSpecificationType::PXAR, true) => { + log_file("directory", &filename, target.as_ref()) + } + (BackupSpecificationType::IMAGE, true) => { + log_file("image", &filename, &target.as_ref()) + } // no dry-run (BackupSpecificationType::CONFIG, false) => { let upload_options = UploadOptions { @@ -1020,11 +1029,11 @@ async fn create_backup( ..UploadOptions::default() }; - log_file("config file", &filename, &target); + log_file("config file", &filename, target.as_ref()); let stats = client - .upload_blob_from_file(&filename, &target, upload_options) + .upload_blob_from_file(&filename, target.as_ref(), upload_options) .await?; - manifest.add_file(target, stats.size, stats.csum, crypto.mode)?; + manifest.add_file(&target, stats.size, stats.csum, crypto.mode)?; } (BackupSpecificationType::LOGFILE, false) => { // fixme: remove - not needed anymore ? @@ -1034,11 +1043,11 @@ async fn create_backup( ..UploadOptions::default() }; - log_file("log file", &filename, &target); + log_file("log file", &filename, target.as_ref()); let stats = client - .upload_blob_from_file(&filename, &target, upload_options) + .upload_blob_from_file(&filename, target.as_ref(), upload_options) .await?; - manifest.add_file(target, stats.size, stats.csum, crypto.mode)?; + manifest.add_file(&target, stats.size, stats.csum, crypto.mode)?; } (BackupSpecificationType::PXAR, false) => { let target_base = if let Some(base) = target_base.strip_suffix(".pxar") { @@ -1050,8 +1059,14 @@ async fn create_backup( let (target, payload_target) = if detection_mode.is_metadata() || detection_mode.is_data() { ( - format!("{target_base}.mpxar.{extension}"), - Some(format!("{target_base}.ppxar.{extension}")), + format!("{target_base}.mpxar.{extension}") + .as_str() + .try_into()?, + Some( + format!("{target_base}.ppxar.{extension}") + .as_str() + .try_into()?, + ), ) } else { (target, None) @@ -1065,12 +1080,12 @@ async fn create_backup( catalog_result_rx = Some(catalog_upload_res.result); } - log_file("directory", &filename, &target); + log_file("directory", &filename, target.as_ref()); if let Some(catalog) = catalog.as_ref() { catalog .lock() .unwrap() - .start_directory(std::ffi::CString::new(target.as_str())?.as_c_str())?; + .start_directory(std::ffi::CString::new(target.as_ref())?.as_c_str())?; } let mut previous_ref = None; @@ -1137,7 +1152,7 @@ async fn create_backup( &client, &filename, &target, - payload_target.as_deref(), + payload_target.as_ref().as_deref(), chunk_size_opt, catalog.as_ref().cloned(), pxar_options, @@ -1147,20 +1162,20 @@ async fn create_backup( if let Some(payload_stats) = payload_stats { manifest.add_file( - payload_target + &payload_target .ok_or_else(|| format_err!("missing payload target archive"))?, payload_stats.size, payload_stats.csum, crypto.mode, )?; } - manifest.add_file(target, stats.size, stats.csum, crypto.mode)?; + manifest.add_file(&target, stats.size, stats.csum, crypto.mode)?; if let Some(catalog) = catalog.as_ref() { catalog.lock().unwrap().end_directory()?; } } (BackupSpecificationType::IMAGE, false) => { - log_file("image", &filename, &target); + log_file("image", &filename, target.as_ref()); let upload_options = UploadOptions { previous_manifest: previous_manifest.clone(), @@ -1172,7 +1187,7 @@ async fn create_backup( let stats = backup_image(&client, &filename, &target, chunk_size_opt, upload_options) .await?; - manifest.add_file(target, stats.size, stats.csum, crypto.mode)?; + manifest.add_file(&target, stats.size, stats.csum, crypto.mode)?; } } } @@ -1194,12 +1209,17 @@ async fn create_backup( if let Some(catalog_result_rx) = catalog_result_rx { let stats = catalog_result_rx.await??; - manifest.add_file(CATALOG_NAME.to_owned(), stats.size, stats.csum, crypto.mode)?; + manifest.add_file( + &CATALOG_NAME.try_into()?, + stats.size, + stats.csum, + crypto.mode, + )?; } } if let Some(rsa_encrypted_key) = rsa_encrypted_key { - let target = ENCRYPTED_KEY_BLOB_NAME; + let target: BackupArchiveName = ENCRYPTED_KEY_BLOB_NAME.try_into()?; log::info!("Upload RSA encoded key to '{}' as {}", repo, target); let options = UploadOptions { compress: false, @@ -1207,9 +1227,9 @@ async fn create_backup( ..UploadOptions::default() }; let stats = client - .upload_blob_from_data(rsa_encrypted_key, target, options) + .upload_blob_from_data(rsa_encrypted_key, target.as_ref(), options) .await?; - manifest.add_file(target.to_string(), stats.size, stats.csum, crypto.mode)?; + manifest.add_file(&target, stats.size, stats.csum, crypto.mode)?; } // create manifest (index.json) // manifests are never encrypted, but include a signature @@ -1238,7 +1258,7 @@ async fn create_backup( } async fn prepare_reference( - target: &str, + target: &BackupArchiveName, manifest: Arc, backup_writer: &BackupWriter, backup_reader: Arc, @@ -1250,7 +1270,11 @@ async fn prepare_reference( Ok((target, payload_target)) => (target, payload_target), Err(_) => return Ok(None), }; - let payload_target = payload_target.unwrap_or_default(); + let payload_target = if let Some(payload_target) = payload_target { + payload_target + } else { + return Ok(None); + }; let metadata_ref_index = if let Ok(index) = backup_reader .download_dynamic_index(&manifest, &target) @@ -1299,7 +1323,7 @@ async fn prepare_reference( Ok(Some(pbs_client::pxar::PxarPrevRef { accessor, payload_index: payload_ref_index, - archive_name: target, + archive_name: target.to_string(), })) } @@ -1486,7 +1510,8 @@ async fn restore( ) -> Result { let repo = extract_repository_from_value(¶m)?; - let archive_name = json::required_string_param(¶m, "archive-name")?; + let archive_name: BackupArchiveName = + json::required_string_param(¶m, "archive-name")?.try_into()?; let rate_limit = RateLimitConfig::from_client_config(limit); @@ -1525,11 +1550,9 @@ async fn restore( ) .await?; - let (archive_name, archive_type) = parse_archive_type(archive_name); - let (manifest, backup_index_data) = client.download_manifest().await?; - if archive_name == ENCRYPTED_KEY_BLOB_NAME && crypt_config.is_none() { + if archive_name.as_ref() == ENCRYPTED_KEY_BLOB_NAME && crypt_config.is_none() { log::info!("Restoring encrypted key blob without original key - skipping manifest fingerprint check!") } else { if manifest.signature.is_some() { @@ -1543,7 +1566,7 @@ async fn restore( manifest.check_fingerprint(crypt_config.as_ref().map(Arc::as_ref))?; } - if archive_name == MANIFEST_BLOB_NAME { + if archive_name.as_ref() == MANIFEST_BLOB_NAME { if let Some(target) = target { replace_file(target, &backup_index_data, CreateOptions::new(), false)?; } else { @@ -1557,7 +1580,7 @@ async fn restore( return Ok(Value::Null); } - if archive_type == ArchiveType::Blob { + if archive_name.archive_type() == ArchiveType::Blob { let mut reader = client.download_blob(&manifest, &archive_name).await?; if let Some(target) = target { @@ -1576,7 +1599,7 @@ async fn restore( std::io::copy(&mut reader, &mut writer) .map_err(|err| format_err!("unable to pipe data - {}", err))?; } - } else if archive_type == ArchiveType::DynamicIndex { + } else if archive_name.archive_type() == ArchiveType::DynamicIndex { let (archive_name, payload_archive_name) = pbs_client::tools::get_pxar_archive_names(&archive_name, &manifest)?; @@ -1680,7 +1703,7 @@ async fn restore( std::io::copy(&mut reader, &mut writer) .map_err(|err| format_err!("unable to pipe data - {}", err))?; } - } else if archive_type == ArchiveType::FixedIndex { + } else if archive_name.archive_type() == ArchiveType::FixedIndex { let file_info = manifest.lookup_file_info(&archive_name)?; let index = client .download_fixed_index(&manifest, &archive_name) diff --git a/proxmox-backup-client/src/mount.rs b/proxmox-backup-client/src/mount.rs index c15e030f5..0048a8ad4 100644 --- a/proxmox-backup-client/src/mount.rs +++ b/proxmox-backup-client/src/mount.rs @@ -18,8 +18,7 @@ use proxmox_schema::*; use proxmox_sortable_macro::sortable; use proxmox_systemd; -use pbs_api_types::BackupNamespace; -use pbs_client::tools::has_pxar_filename_extension; +use pbs_api_types::{ArchiveType, BackupArchiveName, BackupNamespace}; use pbs_client::tools::key_source::get_encryption_key_password; use pbs_client::{BackupReader, RemoteChunkReader}; use pbs_datastore::cached_chunk_reader::CachedChunkReader; @@ -47,11 +46,7 @@ const API_METHOD_MOUNT: ApiMethod = ApiMethod::new( false, &StringSchema::new("Group/Snapshot path.").schema() ), - ( - "archive-name", - false, - &StringSchema::new("Backup archive name.").schema() - ), + ("archive-name", false, &BackupArchiveName::API_SCHEMA), ( "target", false, @@ -87,11 +82,7 @@ WARNING: Only do this with *trusted* backups!", false, &StringSchema::new("Group/Snapshot path.").schema() ), - ( - "archive-name", - false, - &StringSchema::new("Backup archive name.").schema() - ), + ("archive-name", false, &BackupArchiveName::API_SCHEMA), ("repository", true, &REPO_URL_SCHEMA), ( "keyfile", @@ -208,7 +199,8 @@ fn mount( async fn mount_do(param: Value, pipe: Option) -> Result { let repo = extract_repository_from_value(¶m)?; - let archive_name = required_string_param(¶m, "archive-name")?; + let server_archive_name: BackupArchiveName = + required_string_param(¶m, "archive-name")?.try_into()?; let client = connect(&repo)?; let target = param["target"].as_str(); @@ -230,16 +222,14 @@ async fn mount_do(param: Value, pipe: Option) -> Result { } }; - let server_archive_name = if has_pxar_filename_extension(archive_name, false) { + if server_archive_name.has_pxar_filename_extension() { if target.is_none() { bail!("use the 'mount' command to mount pxar archives"); } - format!("{}.didx", archive_name) - } else if archive_name.ends_with(".img") { + } else if server_archive_name.ends_with(".img.fidx") { if target.is_some() { bail!("use the 'map' command to map drive images"); } - format!("{}.fidx", archive_name) } else { bail!("Can only mount/map pxar archives and drive images."); }; @@ -291,7 +281,7 @@ async fn mount_do(param: Value, pipe: Option) -> Result { let mut interrupt = futures::future::select(interrupt_int.recv().boxed(), interrupt_term.recv().boxed()); - if server_archive_name.ends_with(".didx") { + if server_archive_name.archive_type() == ArchiveType::DynamicIndex { let decoder = helper::get_pxar_fuse_accessor( &server_archive_name, client.clone(), @@ -312,7 +302,7 @@ async fn mount_do(param: Value, pipe: Option) -> Result { // exit on interrupted } } - } else if server_archive_name.ends_with(".fidx") { + } else if server_archive_name.archive_type() == ArchiveType::FixedIndex { let file_info = manifest.lookup_file_info(&server_archive_name)?; let index = client .download_fixed_index(&manifest, &server_archive_name) @@ -326,7 +316,10 @@ async fn mount_do(param: Value, pipe: Option) -> Result { ); let reader = CachedChunkReader::new(chunk_reader, index, 8).seekable(); - let name = &format!("{}:{}/{}", repo, path, archive_name); + let name = &format!( + "{repo}:{path}/{}", + server_archive_name.without_type_extension(), + ); let name_escaped = proxmox_systemd::escape_unit(name, false); let mut session = diff --git a/proxmox-file-restore/src/main.rs b/proxmox-file-restore/src/main.rs index 08354b454..c4bde0699 100644 --- a/proxmox-file-restore/src/main.rs +++ b/proxmox-file-restore/src/main.rs @@ -5,6 +5,7 @@ use std::sync::Arc; use anyhow::{bail, format_err, Error}; use futures::StreamExt; +use pbs_api_types::BackupArchiveName; use serde_json::{json, Value}; use tokio::io::AsyncWriteExt; @@ -149,9 +150,10 @@ async fn list_files( Ok(entries) } ExtractPath::Pxar(file, mut path) => { - if let Ok(file_info) = manifest.lookup_file_info(CATALOG_NAME) { + let catalog_name: BackupArchiveName = CATALOG_NAME.try_into()?; + if let Ok(file_info) = manifest.lookup_file_info(&catalog_name) { let index = client - .download_dynamic_index(&manifest, CATALOG_NAME) + .download_dynamic_index(&manifest, &catalog_name) .await?; let most_used = index.find_most_used_chunks(8); let chunk_reader = RemoteChunkReader::new( @@ -172,6 +174,7 @@ async fn list_files( path = vec![b'/']; } + let file: BackupArchiveName = file.as_str().try_into()?; let (archive_name, _payload_archive_name) = pbs_client::tools::get_pxar_archive_names(&file, &manifest)?; @@ -191,7 +194,7 @@ async fn list_files( pbs_client::pxar::tools::pxar_metadata_catalog_lookup( accessor, path, - Some(&archive_name), + Some(archive_name.as_ref()), ) .await } @@ -476,10 +479,11 @@ async fn extract( match path { ExtractPath::Pxar(archive_name, path) => { + let archive_name: BackupArchiveName = archive_name.as_str().try_into()?; let (archive_name, payload_archive_name) = pbs_client::tools::get_pxar_archive_names(&archive_name, &manifest)?; let (reader, archive_size) = get_remote_pxar_reader( - &archive_name, + &archive_name.try_into()?, client.clone(), &manifest, crypt_config.clone(), diff --git a/src/api2/admin/datastore.rs b/src/api2/admin/datastore.rs index b73ad0ff0..f8856e855 100644 --- a/src/api2/admin/datastore.rs +++ b/src/api2/admin/datastore.rs @@ -34,15 +34,15 @@ use pxar::accessor::aio::Accessor; use pxar::EntryKind; use pbs_api_types::{ - print_ns_and_snapshot, print_store_and_ns, Authid, BackupContent, BackupNamespace, BackupType, - Counts, CryptMode, DataStoreConfig, DataStoreListItem, DataStoreStatus, - GarbageCollectionJobStatus, GroupListItem, JobScheduleStatus, KeepOptions, Operation, - PruneJobOptions, SnapshotListItem, SnapshotVerifyState, BACKUP_ARCHIVE_NAME_SCHEMA, - BACKUP_ID_SCHEMA, BACKUP_NAMESPACE_SCHEMA, BACKUP_TIME_SCHEMA, BACKUP_TYPE_SCHEMA, - 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, UPID, UPID_SCHEMA, - VERIFICATION_OUTDATED_AFTER_SCHEMA, + print_ns_and_snapshot, print_store_and_ns, ArchiveType, Authid, BackupArchiveName, + BackupContent, BackupNamespace, BackupType, Counts, CryptMode, DataStoreConfig, + DataStoreListItem, DataStoreStatus, GarbageCollectionJobStatus, GroupListItem, + JobScheduleStatus, KeepOptions, Operation, PruneJobOptions, SnapshotListItem, + SnapshotVerifyState, BACKUP_ARCHIVE_NAME_SCHEMA, BACKUP_ID_SCHEMA, BACKUP_NAMESPACE_SCHEMA, + BACKUP_TIME_SCHEMA, BACKUP_TYPE_SCHEMA, 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, UPID, + UPID_SCHEMA, VERIFICATION_OUTDATED_AFTER_SCHEMA, }; use pbs_client::pxar::{create_tar, create_zip}; use pbs_config::CachedUserInfo; @@ -1468,12 +1468,13 @@ pub fn download_file_decoded( &backup_dir_api.group, )?; - let file_name = required_string_param(¶m, "file-name")?.to_owned(); + let file_name: BackupArchiveName = + required_string_param(¶m, "file-name")?.try_into()?; let backup_dir = datastore.backup_dir(backup_ns.clone(), backup_dir_api.clone())?; let (manifest, files) = read_backup_index(&backup_dir)?; for file in files { - if file.filename == file_name && file.crypt_mode == Some(CryptMode::Encrypt) { + if file.filename == file_name.as_ref() && file.crypt_mode == Some(CryptMode::Encrypt) { bail!("cannot decode '{}' - is encrypted", file_name); } } @@ -1488,12 +1489,10 @@ pub fn download_file_decoded( let mut path = datastore.base_path(); path.push(backup_dir.relative_path()); - path.push(&file_name); + path.push(file_name.as_ref()); - let (_, extension) = file_name.rsplit_once('.').unwrap(); - - let body = match extension { - "didx" => { + let body = match file_name.archive_type() { + ArchiveType::DynamicIndex => { let index = DynamicIndexReader::open(&path).map_err(|err| { format_err!("unable to read dynamic index '{:?}' - {}", &path, err) })?; @@ -1507,7 +1506,7 @@ pub fn download_file_decoded( err })) } - "fidx" => { + ArchiveType::FixedIndex => { let index = FixedIndexReader::open(&path).map_err(|err| { format_err!("unable to read fixed index '{:?}' - {}", &path, err) })?; @@ -1526,7 +1525,7 @@ pub fn download_file_decoded( ), ) } - "blob" => { + ArchiveType::Blob => { let file = std::fs::File::open(&path) .map_err(|err| http_err!(BAD_REQUEST, "File open failed: {}", err))?; @@ -1541,9 +1540,6 @@ pub fn download_file_decoded( ), ) } - extension => { - bail!("cannot download '{}' files", extension); - } }; // fixme: set other headers ? @@ -1658,7 +1654,7 @@ fn decode_path(path: &str) -> Result, Error> { type: String, }, "archive-name": { - schema: BACKUP_ARCHIVE_NAME_SCHEMA, + type: BackupArchiveName, optional: true, }, }, @@ -1675,12 +1671,12 @@ pub async fn catalog( ns: Option, backup_dir: pbs_api_types::BackupDir, filepath: String, - archive_name: Option, + archive_name: Option, rpcenv: &mut dyn RpcEnvironment, ) -> Result, Error> { let file_name = archive_name .clone() - .unwrap_or_else(|| CATALOG_NAME.to_string()); + .unwrap_or_else(|| CATALOG_NAME.try_into().unwrap()); let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; @@ -1700,7 +1696,7 @@ pub async fn catalog( let (manifest, files) = read_backup_index(&backup_dir)?; for file in files { - if file.filename == file_name && file.crypt_mode == Some(CryptMode::Encrypt) { + if file.filename == file_name.as_ref() && file.crypt_mode == Some(CryptMode::Encrypt) { bail!("cannot decode '{file_name}' - is encrypted"); } } @@ -1709,7 +1705,7 @@ pub async fn catalog( tokio::task::spawn_blocking(move || { let mut path = datastore.base_path(); path.push(backup_dir.relative_path()); - path.push(&file_name); + path.push(file_name.as_ref()); let index = DynamicIndexReader::open(&path) .map_err(|err| format_err!("unable to read dynamic index '{path:?}' - {err}"))?; @@ -1759,7 +1755,7 @@ pub const API_METHOD_PXAR_FILE_DOWNLOAD: ApiMethod = ApiMethod::new( ("backup-time", false, &BACKUP_TIME_SCHEMA), ("filepath", false, &StringSchema::new("Base64 encoded path").schema()), ("tar", true, &BooleanSchema::new("Download as .tar.zst").schema()), - ("archive-name", true, &BACKUP_ARCHIVE_NAME_SCHEMA), + ("archive-name", true, &BackupArchiveName::API_SCHEMA), ]), ) ).access( @@ -1774,11 +1770,11 @@ fn get_local_pxar_reader( datastore: Arc, manifest: &BackupManifest, backup_dir: &BackupDir, - pxar_name: &str, + pxar_name: &BackupArchiveName, ) -> Result<(LocalDynamicReadAt, u64), Error> { let mut path = datastore.base_path(); path.push(backup_dir.relative_path()); - path.push(pxar_name); + path.push(pxar_name.as_ref()); let index = DynamicIndexReader::open(&path) .map_err(|err| format_err!("unable to read dynamic index '{:?}' - {}", &path, err))?; @@ -1836,16 +1832,16 @@ pub fn pxar_file_download( let file_path = split.next().unwrap_or(b"/"); (pxar_name.to_owned(), file_path.to_owned()) }; - let pxar_name = std::str::from_utf8(&pxar_name)?; + let pxar_name: BackupArchiveName = std::str::from_utf8(&pxar_name)?.try_into()?; let (manifest, files) = read_backup_index(&backup_dir)?; for file in files { - if file.filename == pxar_name && file.crypt_mode == Some(CryptMode::Encrypt) { + if file.filename == pxar_name.as_ref() && file.crypt_mode == Some(CryptMode::Encrypt) { bail!("cannot decode '{}' - is encrypted", pxar_name); } } let (pxar_name, payload_archive_name) = - pbs_client::tools::get_pxar_archive_names(pxar_name, &manifest)?; + pbs_client::tools::get_pxar_archive_names(&pxar_name, &manifest)?; let (reader, archive_size) = get_local_pxar_reader(datastore.clone(), &manifest, &backup_dir, &pxar_name)?; diff --git a/src/bin/proxmox_backup_debug/diff.rs b/src/bin/proxmox_backup_debug/diff.rs index b0436d048..dcd351d93 100644 --- a/src/bin/proxmox_backup_debug/diff.rs +++ b/src/bin/proxmox_backup_debug/diff.rs @@ -13,7 +13,7 @@ use proxmox_human_byte::HumanByte; use proxmox_router::cli::{CliCommand, CliCommandMap, CommandLineInterface}; use proxmox_schema::api; -use pbs_api_types::{BackupNamespace, BackupPart}; +use pbs_api_types::{BackupArchiveName, BackupNamespace, BackupPart}; use pbs_client::tools::key_source::{ crypto_parameters, format_key_source, get_encryption_key_password, KEYFD_SCHEMA, }; @@ -70,8 +70,7 @@ pub fn diff_commands() -> CommandLineInterface { type: String, }, "archive-name": { - description: "Name of the .pxar archive", - type: String, + type: BackupArchiveName, }, "repository": { optional: true, @@ -106,7 +105,7 @@ pub fn diff_commands() -> CommandLineInterface { async fn diff_archive_cmd( prev_snapshot: String, snapshot: String, - archive_name: String, + archive_name: BackupArchiveName, compare_content: bool, color: Option, ns: Option, @@ -140,12 +139,11 @@ async fn diff_archive_cmd( let output_params = OutputParams { color }; - if archive_name.ends_with(".pxar") { - let file_name = format!("{}.didx", archive_name); + if archive_name.ends_with(".pxar.didx") { diff_archive( &prev_snapshot, &snapshot, - &file_name, + &archive_name, &repo_params, compare_content, &output_params, @@ -161,7 +159,7 @@ async fn diff_archive_cmd( async fn diff_archive( snapshot_a: &str, snapshot_b: &str, - file_name: &str, + file_name: &BackupArchiveName, repo_params: &RepoParams, compare_contents: bool, output_params: &OutputParams, @@ -249,7 +247,7 @@ struct OutputParams { async fn open_dynamic_index( snapshot: &str, - archive_name: &str, + archive_name: &BackupArchiveName, params: &RepoParams, ) -> Result<(DynamicIndexReader, Accessor), Error> { let backup_reader = create_backup_reader(snapshot, params).await?; diff --git a/src/server/pull.rs b/src/server/pull.rs index 8cbfdb399..baaedbffe 100644 --- a/src/server/pull.rs +++ b/src/server/pull.rs @@ -11,9 +11,9 @@ use proxmox_human_byte::HumanByte; use tracing::info; use pbs_api_types::{ - print_store_and_ns, ArchiveType, Authid, BackupDir, BackupGroup, BackupNamespace, GroupFilter, - Operation, RateLimitConfig, Remote, MAX_NAMESPACE_DEPTH, PRIV_DATASTORE_AUDIT, - PRIV_DATASTORE_BACKUP, + print_store_and_ns, ArchiveType, Authid, BackupArchiveName, BackupDir, BackupGroup, + BackupNamespace, GroupFilter, Operation, RateLimitConfig, Remote, MAX_NAMESPACE_DEPTH, + PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_BACKUP, }; use pbs_client::BackupRepository; use pbs_config::CachedUserInfo; @@ -381,11 +381,12 @@ async fn pull_snapshot<'a>( path.push(&item.filename); if path.exists() { - match ArchiveType::from_path(&item.filename)? { + let filename: BackupArchiveName = item.filename.as_str().try_into()?; + match filename.archive_type() { ArchiveType::DynamicIndex => { let index = DynamicIndexReader::open(&path)?; let (csum, size) = index.compute_csum(); - match manifest.verify_file(&item.filename, &csum, size) { + match manifest.verify_file(&filename, &csum, size) { Ok(_) => continue, Err(err) => { info!("detected changed file {path:?} - {err}"); @@ -395,7 +396,7 @@ async fn pull_snapshot<'a>( ArchiveType::FixedIndex => { let index = FixedIndexReader::open(&path)?; let (csum, size) = index.compute_csum(); - match manifest.verify_file(&item.filename, &csum, size) { + match manifest.verify_file(&filename, &csum, size) { Ok(_) => continue, Err(err) => { info!("detected changed file {path:?} - {err}"); @@ -405,7 +406,7 @@ async fn pull_snapshot<'a>( ArchiveType::Blob => { let mut tmpfile = std::fs::File::open(&path)?; let (csum, size) = sha256(&mut tmpfile)?; - match manifest.verify_file(&item.filename, &csum, size) { + match manifest.verify_file(&filename, &csum, size) { Ok(_) => continue, Err(err) => { info!("detected changed file {path:?} - {err}"); -- 2.39.5 _______________________________________________ pbs-devel mailing list pbs-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel