[pbs-devel] [PATCH proxmox-backup 04/22] api2/admin/datastore: refactor list_dir_content in catalog_reader
Stefan Reiter
s.reiter at proxmox.com
Tue Feb 16 18:06:52 CET 2021
From: Dominik Csapak <d.csapak at proxmox.com>
we will reuse that later in the client, so we need it somewhere
we can use from there
Signed-off-by: Dominik Csapak <d.csapak at proxmox.com>
[add strongly typed ArchiveEntry and put api code into helpers.rs]
Signed-off-by: Stefan Reiter <s.reiter at proxmox.com>
---
src/api2/admin/datastore.rs | 53 ++++++-------------------------------
src/api2/helpers.rs | 31 ++++++++++++++++++++++
src/api2/types/mod.rs | 43 ++++++++++++++++++++++++++++++
src/backup/catalog.rs | 26 ++++++++++++++++++
4 files changed, 108 insertions(+), 45 deletions(-)
diff --git a/src/api2/admin/datastore.rs b/src/api2/admin/datastore.rs
index 6f02e460..ab88d172 100644
--- a/src/api2/admin/datastore.rs
+++ b/src/api2/admin/datastore.rs
@@ -27,6 +27,7 @@ use pxar::EntryKind;
use crate::api2::types::*;
use crate::api2::node::rrd::create_value_from_rrd;
+use crate::api2::helpers;
use crate::backup::*;
use crate::config::datastore;
use crate::config::cached_user_info::CachedUserInfo;
@@ -1294,7 +1295,7 @@ pub fn catalog(
backup_time: i64,
filepath: String,
rpcenv: &mut dyn RpcEnvironment,
-) -> Result<Value, Error> {
+) -> Result<Vec<ArchiveEntry>, Error> {
let datastore = DataStore::lookup_datastore(&store)?;
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
@@ -1326,52 +1327,14 @@ pub fn catalog(
let reader = BufferedDynamicReader::new(index, chunk_reader);
let mut catalog_reader = CatalogReader::new(reader);
- let mut current = catalog_reader.root()?;
- let mut components = vec![];
+ let path = if filepath != "root" {
+ base64::decode(filepath)?
+ } else {
+ vec![b'/']
+ };
- if filepath != "root" {
- components = base64::decode(filepath)?;
- if !components.is_empty() && components[0] == b'/' {
- components.remove(0);
- }
- for component in components.split(|c| *c == b'/') {
- if let Some(entry) = catalog_reader.lookup(¤t, component)? {
- current = entry;
- } else {
- bail!("path {:?} not found in catalog", &String::from_utf8_lossy(&components));
- }
- }
- }
-
- let mut res = Vec::new();
-
- for direntry in catalog_reader.read_dir(¤t)? {
- let mut components = components.clone();
- components.push(b'/');
- components.extend(&direntry.name);
- let path = base64::encode(components);
- let text = String::from_utf8_lossy(&direntry.name);
- let mut entry = json!({
- "filepath": path,
- "text": text,
- "type": CatalogEntryType::from(&direntry.attr).to_string(),
- "leaf": true,
- });
- match direntry.attr {
- DirEntryAttribute::Directory { start: _ } => {
- entry["leaf"] = false.into();
- },
- DirEntryAttribute::File { size, mtime } => {
- entry["size"] = size.into();
- entry["mtime"] = mtime.into();
- },
- _ => {},
- }
- res.push(entry);
- }
-
- Ok(res.into())
+ helpers::list_dir_content(&mut catalog_reader, &path)
}
fn recurse_files<'a, T, W>(
diff --git a/src/api2/helpers.rs b/src/api2/helpers.rs
index 2a822654..41391b77 100644
--- a/src/api2/helpers.rs
+++ b/src/api2/helpers.rs
@@ -1,3 +1,4 @@
+use std::io::{Read, Seek};
use std::path::PathBuf;
use anyhow::Error;
@@ -6,6 +7,9 @@ use hyper::{Body, Response, StatusCode, header};
use proxmox::http_bail;
+use crate::api2::types::ArchiveEntry;
+use crate::backup::{CatalogReader, DirEntryAttribute};
+
pub async fn create_download_response(path: PathBuf) -> Result<Response<Body>, Error> {
let file = match tokio::fs::File::open(path.clone()).await {
Ok(file) => file,
@@ -27,3 +31,30 @@ pub async fn create_download_response(path: PathBuf) -> Result<Response<Body>, E
.body(body)
.unwrap())
}
+
+/// Returns the list of content of the given path
+pub fn list_dir_content<R: Read + Seek>(
+ reader: &mut CatalogReader<R>,
+ path: &[u8],
+) -> Result<Vec<ArchiveEntry>, Error> {
+ let dir = reader.lookup_recursive(path)?;
+ let mut res = vec![];
+ let mut path = path.to_vec();
+ if !path.is_empty() && path[0] == b'/' {
+ path.remove(0);
+ }
+
+ for direntry in reader.read_dir(&dir)? {
+ let mut components = path.clone();
+ components.push(b'/');
+ components.extend(&direntry.name);
+ let mut entry = ArchiveEntry::new(&components, &direntry.attr);
+ if let DirEntryAttribute::File { size, mtime } = direntry.attr {
+ entry.size = size.into();
+ entry.mtime = mtime.into();
+ }
+ res.push(entry);
+ }
+
+ Ok(res)
+}
diff --git a/src/api2/types/mod.rs b/src/api2/types/mod.rs
index d9394586..4c663335 100644
--- a/src/api2/types/mod.rs
+++ b/src/api2/types/mod.rs
@@ -12,6 +12,8 @@ use crate::{
CryptMode,
Fingerprint,
BACKUP_ID_REGEX,
+ DirEntryAttribute,
+ CatalogEntryType,
},
server::UPID,
config::acl::Role,
@@ -1303,6 +1305,47 @@ pub struct DatastoreNotify {
pub sync: Option<Notify>,
}
+/// An entry in a hierarchy of files for restore and listing.
+#[api()]
+#[derive(Serialize, Deserialize)]
+pub struct ArchiveEntry {
+ /// Base64-encoded full path to the file, including the filename
+ pub filepath: String,
+ /// Displayable filename text for UIs
+ pub text: String,
+ /// File or directory type of this entry
+ #[serde(rename = "type")]
+ pub entry_type: String,
+ /// Is this entry a leaf node, or does it have children (i.e. a directory)?
+ pub leaf: bool,
+ /// The file size, if entry_type is 'f' (file)
+ #[serde(skip_serializing_if="Option::is_none")]
+ pub size: Option<u64>,
+ /// The file "last modified" time stamp, if entry_type is 'f' (file)
+ #[serde(skip_serializing_if="Option::is_none")]
+ pub mtime: Option<i64>,
+}
+
+impl ArchiveEntry {
+ pub fn new(filepath: &[u8], entry_type: &DirEntryAttribute) -> Self {
+ Self {
+ filepath: base64::encode(filepath),
+ text: String::from_utf8_lossy(filepath.split(|x| *x == b'/').last().unwrap())
+ .to_string(),
+ entry_type: CatalogEntryType::from(entry_type).to_string(),
+ leaf: matches!(entry_type, DirEntryAttribute::Directory { .. }),
+ size: match entry_type {
+ DirEntryAttribute::File { size, .. } => Some(*size),
+ _ => None
+ },
+ mtime: match entry_type {
+ DirEntryAttribute::File { mtime, .. } => Some(*mtime),
+ _ => None
+ },
+ }
+ }
+}
+
pub const DATASTORE_NOTIFY_STRING_SCHEMA: Schema = StringSchema::new(
"Datastore notification setting")
.format(&ApiStringFormat::PropertyString(&DatastoreNotify::API_SCHEMA))
diff --git a/src/backup/catalog.rs b/src/backup/catalog.rs
index 224e6bf7..a307f9d8 100644
--- a/src/backup/catalog.rs
+++ b/src/backup/catalog.rs
@@ -468,6 +468,32 @@ impl <R: Read + Seek> CatalogReader<R> {
Ok(entry_list)
}
+ /// Lookup a DirEntry from an absolute path
+ pub fn lookup_recursive(
+ &mut self,
+ path: &[u8],
+ ) -> Result<DirEntry, Error> {
+ let mut current = self.root()?;
+ if path == b"/" {
+ return Ok(current);
+ }
+
+ let components = if !path.is_empty() && path[0] == b'/' {
+ &path[1..]
+ } else {
+ path
+ }.split(|c| *c == b'/');
+
+ for comp in components {
+ if let Some(entry) = self.lookup(¤t, comp)? {
+ current = entry;
+ } else {
+ bail!("path {:?} not found in catalog", String::from_utf8_lossy(&path));
+ }
+ }
+ Ok(current)
+ }
+
/// Lockup a DirEntry inside a parent directory
pub fn lookup(
&mut self,
--
2.20.1
More information about the pbs-devel
mailing list