From 113c04bc6009881d2ce4ee5a366474e6d67fddf5 Mon Sep 17 00:00:00 2001 From: Christian Ebner Date: Mon, 12 Aug 2024 12:31:31 +0200 Subject: [PATCH] client: pxar: move catalog lookup helper to pxar tools The lookup helper used to generate catalog entries via the metadata archive for split archive backups is pxar specific, therefore move it to the appropriate pxar tools submodlue. --- pbs-client/src/pxar/tools.rs | 86 +++++++++++++++++++++++++++++++- pbs-client/src/tools/mod.rs | 84 ------------------------------- proxmox-file-restore/src/main.rs | 8 ++- src/api2/admin/datastore.rs | 2 +- 4 files changed, 91 insertions(+), 89 deletions(-) diff --git a/pbs-client/src/pxar/tools.rs b/pbs-client/src/pxar/tools.rs index 9d4ad6a4..c444a894 100644 --- a/pbs-client/src/pxar/tools.rs +++ b/pbs-client/src/pxar/tools.rs @@ -3,11 +3,17 @@ use std::ffi::OsStr; use std::os::unix::ffi::OsStrExt; use std::path::Path; +use std::path::PathBuf; -use anyhow::{bail, Context, Error}; +use anyhow::{bail, format_err, Context, Error}; use nix::sys::stat::Mode; -use pxar::{format::StatxTimestamp, mode, Entry, EntryKind, Metadata}; +use pxar::accessor::aio::Accessor; +use pxar::accessor::ReadAt; +use pxar::format::{SignedDuration, StatxTimestamp}; +use pxar::{mode, Entry, EntryKind, Metadata}; + +use pbs_datastore::catalog::{ArchiveEntry, DirEntryAttribute}; /// Get the file permissions as `nix::Mode` pub(crate) fn perms_from_metadata(meta: &Metadata) -> Result { @@ -257,3 +263,79 @@ pub fn format_multi_line_entry(entry: &Entry) -> String { ) } } + +/// Look up the directory entries of the given directory `path` in a pxar archive via it's given +/// `accessor` and return the entries formatted as [`ArchiveEntry`]'s, compatible with reading +/// entries from the catalog. +/// +/// If the optional `path_prefix` is given, all returned entry paths will be prefixed with it. +pub async fn pxar_metadata_catalog_lookup( + accessor: Accessor, + path: &OsStr, + path_prefix: Option<&str>, +) -> Result, Error> { + let root = accessor.open_root().await?; + let dir_entry = root + .lookup(&path) + .await + .map_err(|err| format_err!("lookup failed - {err}"))? + .ok_or_else(|| format_err!("lookup failed - error opening '{path:?}'"))?; + + let mut entries = Vec::new(); + if let EntryKind::Directory = dir_entry.kind() { + let dir_entry = dir_entry + .enter_directory() + .await + .map_err(|err| format_err!("failed to enter directory - {err}"))?; + + let mut entries_iter = dir_entry.read_dir(); + while let Some(entry) = entries_iter.next().await { + let entry = entry?.decode_entry().await?; + + let entry_attr = match entry.kind() { + EntryKind::Version(_) | EntryKind::Prelude(_) | EntryKind::GoodbyeTable => continue, + EntryKind::Directory => DirEntryAttribute::Directory { + start: entry.entry_range_info().entry_range.start, + }, + EntryKind::File { size, .. } => { + let mtime = match entry.metadata().mtime_as_duration() { + SignedDuration::Positive(val) => i64::try_from(val.as_secs())?, + SignedDuration::Negative(val) => -i64::try_from(val.as_secs())?, + }; + DirEntryAttribute::File { size: *size, mtime } + } + EntryKind::Device(_) => match entry.metadata().file_type() { + mode::IFBLK => DirEntryAttribute::BlockDevice, + mode::IFCHR => DirEntryAttribute::CharDevice, + _ => bail!("encountered unknown device type"), + }, + EntryKind::Symlink(_) => DirEntryAttribute::Symlink, + EntryKind::Hardlink(_) => DirEntryAttribute::Hardlink, + EntryKind::Fifo => DirEntryAttribute::Fifo, + EntryKind::Socket => DirEntryAttribute::Socket, + }; + + let entry_path = if let Some(prefix) = path_prefix { + let mut entry_path = PathBuf::from(prefix); + match entry.path().strip_prefix("/") { + Ok(path) => entry_path.push(path), + Err(_) => entry_path.push(entry.path()), + } + entry_path + } else { + PathBuf::from(entry.path()) + }; + entries.push(ArchiveEntry::new( + entry_path.as_os_str().as_bytes(), + Some(&entry_attr), + )); + } + } else { + bail!(format!( + "expected directory entry, got entry kind '{:?}'", + dir_entry.kind() + )); + } + + Ok(entries) +} diff --git a/pbs-client/src/tools/mod.rs b/pbs-client/src/tools/mod.rs index 772cc126..87e74de6 100644 --- a/pbs-client/src/tools/mod.rs +++ b/pbs-client/src/tools/mod.rs @@ -1,13 +1,10 @@ //! Shared tools useful for common CLI clients. use std::collections::HashMap; use std::env::VarError::{NotPresent, NotUnicode}; -use std::ffi::OsStr; use std::fs::File; use std::io::{BufRead, BufReader}; -use std::os::unix::ffi::OsStrExt; use std::os::unix::fs::OpenOptionsExt; use std::os::unix::io::FromRawFd; -use std::path::PathBuf; use std::process::Command; use std::sync::OnceLock; @@ -21,12 +18,7 @@ use proxmox_schema::*; use proxmox_sys::fs::file_get_json; use pbs_api_types::{Authid, BackupNamespace, RateLimitConfig, UserWithTokens, BACKUP_REPO_URL}; -use pbs_datastore::catalog::{ArchiveEntry, DirEntryAttribute}; use pbs_datastore::BackupManifest; -use pxar::accessor::aio::Accessor; -use pxar::accessor::ReadAt; -use pxar::format::SignedDuration; -use pxar::{mode, EntryKind}; use crate::{BackupRepository, HttpClient, HttpClientOptions}; @@ -663,82 +655,6 @@ pub fn raise_nofile_limit() -> Result { Ok(old) } -/// Look up the directory entries of the given directory `path` in a pxar archive via it's given -/// `accessor` and return the entries formatted as [`ArchiveEntry`]'s, compatible with reading -/// entries from the catalog. -/// -/// If the optional `path_prefix` is given, all returned entry paths will be prefixed with it. -pub async fn pxar_metadata_catalog_lookup( - accessor: Accessor, - path: &OsStr, - path_prefix: Option<&str>, -) -> Result, Error> { - let root = accessor.open_root().await?; - let dir_entry = root - .lookup(&path) - .await - .map_err(|err| format_err!("lookup failed - {err}"))? - .ok_or_else(|| format_err!("lookup failed - error opening '{path:?}'"))?; - - let mut entries = Vec::new(); - if let EntryKind::Directory = dir_entry.kind() { - let dir_entry = dir_entry - .enter_directory() - .await - .map_err(|err| format_err!("failed to enter directory - {err}"))?; - - let mut entries_iter = dir_entry.read_dir(); - while let Some(entry) = entries_iter.next().await { - let entry = entry?.decode_entry().await?; - - let entry_attr = match entry.kind() { - EntryKind::Version(_) | EntryKind::Prelude(_) | EntryKind::GoodbyeTable => continue, - EntryKind::Directory => DirEntryAttribute::Directory { - start: entry.entry_range_info().entry_range.start, - }, - EntryKind::File { size, .. } => { - let mtime = match entry.metadata().mtime_as_duration() { - SignedDuration::Positive(val) => i64::try_from(val.as_secs())?, - SignedDuration::Negative(val) => -i64::try_from(val.as_secs())?, - }; - DirEntryAttribute::File { size: *size, mtime } - } - EntryKind::Device(_) => match entry.metadata().file_type() { - mode::IFBLK => DirEntryAttribute::BlockDevice, - mode::IFCHR => DirEntryAttribute::CharDevice, - _ => bail!("encountered unknown device type"), - }, - EntryKind::Symlink(_) => DirEntryAttribute::Symlink, - EntryKind::Hardlink(_) => DirEntryAttribute::Hardlink, - EntryKind::Fifo => DirEntryAttribute::Fifo, - EntryKind::Socket => DirEntryAttribute::Socket, - }; - - let entry_path = if let Some(prefix) = path_prefix { - let mut entry_path = PathBuf::from(prefix); - match entry.path().strip_prefix("/") { - Ok(path) => entry_path.push(path), - Err(_) => entry_path.push(entry.path()), - } - entry_path - } else { - PathBuf::from(entry.path()) - }; - entries.push(ArchiveEntry::new( - entry_path.as_os_str().as_bytes(), - Some(&entry_attr), - )); - } - } else { - bail!(format!( - "expected directory entry, got entry kind '{:?}'", - dir_entry.kind() - )); - } - - Ok(entries) -} - /// Creates a temporary file (with `O_TMPFILE`) in `XDG_CACHE_HOME`. If we /// cannot create the file there it will be created in `/tmp` instead. pub fn create_tmp_file() -> std::io::Result { diff --git a/proxmox-file-restore/src/main.rs b/proxmox-file-restore/src/main.rs index cda4e911..3d93f171 100644 --- a/proxmox-file-restore/src/main.rs +++ b/proxmox-file-restore/src/main.rs @@ -187,8 +187,12 @@ async fn list_files( let accessor = Accessor::new(reader, archive_size).await?; let path = OsStr::from_bytes(&path); - pbs_client::tools::pxar_metadata_catalog_lookup(accessor, path, Some(&archive_name)) - .await + pbs_client::pxar::tools::pxar_metadata_catalog_lookup( + accessor, + path, + Some(&archive_name), + ) + .await } } ExtractPath::VM(file, path) => { diff --git a/src/api2/admin/datastore.rs b/src/api2/admin/datastore.rs index 7660dd7f..b73ad0ff 100644 --- a/src/api2/admin/datastore.rs +++ b/src/api2/admin/datastore.rs @@ -1737,7 +1737,7 @@ pub async fn catalog( let accessor = Accessor::new(reader, archive_size).await?; let file_path = decode_path(&filepath)?; - pbs_client::tools::pxar_metadata_catalog_lookup( + pbs_client::pxar::tools::pxar_metadata_catalog_lookup( accessor, OsStr::from_bytes(&file_path), None,