2020-07-09 07:20:49 +00:00
|
|
|
use anyhow::{format_err, Error};
|
2019-11-08 12:13:39 +00:00
|
|
|
use std::fs::File;
|
2022-04-14 12:08:48 +00:00
|
|
|
use std::io::{Seek, SeekFrom, Write};
|
2019-11-08 11:40:10 +00:00
|
|
|
use std::os::unix::fs::OpenOptionsExt;
|
2022-04-14 12:08:48 +00:00
|
|
|
use std::sync::Arc;
|
2019-10-12 10:57:08 +00:00
|
|
|
|
2019-12-17 09:52:07 +00:00
|
|
|
use futures::future::AbortHandle;
|
2019-10-12 10:57:08 +00:00
|
|
|
use serde_json::{json, Value};
|
|
|
|
|
2022-04-14 13:05:58 +00:00
|
|
|
use pbs_api_types::BackupType;
|
2021-07-09 09:31:53 +00:00
|
|
|
use pbs_datastore::data_blob::DataBlob;
|
|
|
|
use pbs_datastore::data_blob_reader::DataBlobReader;
|
|
|
|
use pbs_datastore::dynamic_index::DynamicIndexReader;
|
|
|
|
use pbs_datastore::fixed_index::FixedIndexReader;
|
|
|
|
use pbs_datastore::index::IndexFile;
|
|
|
|
use pbs_datastore::manifest::MANIFEST_BLOB_NAME;
|
2022-04-14 12:08:48 +00:00
|
|
|
use pbs_datastore::{BackupManifest, PROXMOX_BACKUP_READER_PROTOCOL_ID_V1};
|
|
|
|
use pbs_tools::crypt_config::CryptConfig;
|
|
|
|
use pbs_tools::sha::sha256;
|
2019-10-12 10:57:08 +00:00
|
|
|
|
2022-04-14 12:08:48 +00:00
|
|
|
use super::{H2Client, HttpClient};
|
2019-10-12 10:57:08 +00:00
|
|
|
|
2019-10-12 11:26:47 +00:00
|
|
|
/// Backup Reader
|
2019-10-12 10:57:08 +00:00
|
|
|
pub struct BackupReader {
|
|
|
|
h2: H2Client,
|
2019-12-17 09:52:07 +00:00
|
|
|
abort: AbortHandle,
|
2019-10-12 13:50:26 +00:00
|
|
|
crypt_config: Option<Arc<CryptConfig>>,
|
2019-10-12 10:57:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for BackupReader {
|
|
|
|
fn drop(&mut self) {
|
2019-12-17 09:52:07 +00:00
|
|
|
self.abort.abort();
|
2019-10-12 10:57:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl BackupReader {
|
2019-12-17 09:52:07 +00:00
|
|
|
fn new(h2: H2Client, abort: AbortHandle, crypt_config: Option<Arc<CryptConfig>>) -> Arc<Self> {
|
2022-04-14 12:08:48 +00:00
|
|
|
Arc::new(Self {
|
|
|
|
h2,
|
|
|
|
abort,
|
|
|
|
crypt_config,
|
|
|
|
})
|
2019-10-12 10:57:08 +00:00
|
|
|
}
|
|
|
|
|
2019-10-12 11:26:47 +00:00
|
|
|
/// Create a new instance by upgrading the connection at '/api2/json/reader'
|
2019-10-12 10:57:08 +00:00
|
|
|
pub async fn start(
|
|
|
|
client: HttpClient,
|
2019-10-12 13:50:26 +00:00
|
|
|
crypt_config: Option<Arc<CryptConfig>>,
|
2019-10-12 10:57:08 +00:00
|
|
|
datastore: &str,
|
2022-04-14 13:05:58 +00:00
|
|
|
backup_type: BackupType,
|
2019-10-12 10:57:08 +00:00
|
|
|
backup_id: &str,
|
2020-09-12 13:10:47 +00:00
|
|
|
backup_time: i64,
|
2019-10-12 10:57:08 +00:00
|
|
|
debug: bool,
|
|
|
|
) -> Result<Arc<BackupReader>, Error> {
|
|
|
|
let param = json!({
|
|
|
|
"backup-type": backup_type,
|
|
|
|
"backup-id": backup_id,
|
2020-09-12 13:10:47 +00:00
|
|
|
"backup-time": backup_time,
|
2019-10-12 10:57:08 +00:00
|
|
|
"store": datastore,
|
|
|
|
"debug": debug,
|
|
|
|
});
|
2022-04-14 12:08:48 +00:00
|
|
|
let req = HttpClient::request_builder(
|
|
|
|
client.server(),
|
|
|
|
client.port(),
|
|
|
|
"GET",
|
|
|
|
"/api2/json/reader",
|
|
|
|
Some(param),
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let (h2, abort) = client
|
|
|
|
.start_h2_connection(req, String::from(PROXMOX_BACKUP_READER_PROTOCOL_ID_V1!()))
|
|
|
|
.await?;
|
2019-10-12 10:57:08 +00:00
|
|
|
|
2019-12-17 09:52:07 +00:00
|
|
|
Ok(BackupReader::new(h2, abort, crypt_config))
|
2019-10-12 10:57:08 +00:00
|
|
|
}
|
|
|
|
|
2019-10-12 11:26:47 +00:00
|
|
|
/// Execute a GET request
|
2022-04-14 12:08:48 +00:00
|
|
|
pub async fn get(&self, path: &str, param: Option<Value>) -> Result<Value, Error> {
|
2019-10-12 10:57:08 +00:00
|
|
|
self.h2.get(path, param).await
|
|
|
|
}
|
|
|
|
|
2019-10-12 11:26:47 +00:00
|
|
|
/// Execute a PUT request
|
2022-04-14 12:08:48 +00:00
|
|
|
pub async fn put(&self, path: &str, param: Option<Value>) -> Result<Value, Error> {
|
2019-10-12 10:57:08 +00:00
|
|
|
self.h2.put(path, param).await
|
|
|
|
}
|
|
|
|
|
2019-10-12 11:26:47 +00:00
|
|
|
/// Execute a POST request
|
2022-04-14 12:08:48 +00:00
|
|
|
pub async fn post(&self, path: &str, param: Option<Value>) -> Result<Value, Error> {
|
2019-10-12 10:57:08 +00:00
|
|
|
self.h2.post(path, param).await
|
|
|
|
}
|
|
|
|
|
2019-10-12 11:26:47 +00:00
|
|
|
/// Execute a GET request and send output to a writer
|
2022-04-14 12:08:48 +00:00
|
|
|
pub async fn download<W: Write + Send>(&self, file_name: &str, output: W) -> Result<(), Error> {
|
2019-10-12 10:57:08 +00:00
|
|
|
let path = "download";
|
|
|
|
let param = json!({ "file-name": file_name });
|
|
|
|
self.h2.download(path, Some(param), output).await
|
|
|
|
}
|
|
|
|
|
2019-10-12 11:26:47 +00:00
|
|
|
/// Execute a special GET request and send output to a writer
|
|
|
|
///
|
|
|
|
/// This writes random data, and is only useful to test download speed.
|
2022-04-14 12:08:48 +00:00
|
|
|
pub async fn speedtest<W: Write + Send>(&self, output: W) -> Result<(), Error> {
|
2019-10-12 10:57:08 +00:00
|
|
|
self.h2.download("speedtest", None, output).await
|
|
|
|
}
|
|
|
|
|
2019-10-12 11:26:47 +00:00
|
|
|
/// Download a specific chunk
|
2019-10-12 10:57:08 +00:00
|
|
|
pub async fn download_chunk<W: Write + Send>(
|
|
|
|
&self,
|
|
|
|
digest: &[u8; 32],
|
|
|
|
output: W,
|
2020-06-12 09:46:42 +00:00
|
|
|
) -> Result<(), Error> {
|
2019-10-12 10:57:08 +00:00
|
|
|
let path = "chunk";
|
2021-11-23 16:57:00 +00:00
|
|
|
let param = json!({ "digest": hex::encode(digest) });
|
2019-10-12 10:57:08 +00:00
|
|
|
self.h2.download(path, Some(param), output).await
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn force_close(self) {
|
2019-12-17 09:52:07 +00:00
|
|
|
self.abort.abort();
|
2019-10-12 10:57:08 +00:00
|
|
|
}
|
2019-10-12 13:50:26 +00:00
|
|
|
|
|
|
|
/// Download backup manifest (index.json)
|
2020-07-08 14:07:14 +00:00
|
|
|
///
|
|
|
|
/// The manifest signature is verified if we have a crypt_config.
|
|
|
|
pub async fn download_manifest(&self) -> Result<(BackupManifest, Vec<u8>), Error> {
|
2020-06-12 09:46:42 +00:00
|
|
|
let mut raw_data = Vec::with_capacity(64 * 1024);
|
|
|
|
self.download(MANIFEST_BLOB_NAME, &mut raw_data).await?;
|
2020-07-28 08:23:16 +00:00
|
|
|
let blob = DataBlob::load_from_reader(&mut &raw_data[..])?;
|
2020-08-03 12:10:43 +00:00
|
|
|
// no expected digest available
|
|
|
|
let data = blob.decode(None, None)?;
|
2020-07-09 07:20:49 +00:00
|
|
|
|
2022-04-14 12:08:48 +00:00
|
|
|
let manifest =
|
|
|
|
BackupManifest::from_data(&data[..], self.crypt_config.as_ref().map(Arc::as_ref))?;
|
2020-07-08 14:07:14 +00:00
|
|
|
|
|
|
|
Ok((manifest, data))
|
2019-10-12 13:50:26 +00:00
|
|
|
}
|
2019-11-08 11:40:10 +00:00
|
|
|
|
2019-11-08 12:13:39 +00:00
|
|
|
/// Download a .blob file
|
|
|
|
///
|
2020-05-30 14:37:33 +00:00
|
|
|
/// This creates a temporary file in /tmp (using O_TMPFILE). The data is verified using
|
2019-11-08 12:13:39 +00:00
|
|
|
/// the provided manifest.
|
|
|
|
pub async fn download_blob(
|
|
|
|
&self,
|
|
|
|
manifest: &BackupManifest,
|
|
|
|
name: &str,
|
2021-06-02 10:14:37 +00:00
|
|
|
) -> Result<DataBlobReader<'_, File>, Error> {
|
2020-06-12 09:46:42 +00:00
|
|
|
let mut tmpfile = std::fs::OpenOptions::new()
|
2019-11-08 12:13:39 +00:00
|
|
|
.write(true)
|
|
|
|
.read(true)
|
|
|
|
.custom_flags(libc::O_TMPFILE)
|
|
|
|
.open("/tmp")?;
|
|
|
|
|
2020-06-12 09:46:42 +00:00
|
|
|
self.download(name, &mut tmpfile).await?;
|
2019-11-08 12:13:39 +00:00
|
|
|
|
2021-07-09 12:10:15 +00:00
|
|
|
tmpfile.seek(SeekFrom::Start(0))?;
|
|
|
|
let (csum, size) = sha256(&mut tmpfile)?;
|
2019-11-08 12:13:39 +00:00
|
|
|
manifest.verify_file(name, &csum, size)?;
|
|
|
|
|
|
|
|
tmpfile.seek(SeekFrom::Start(0))?;
|
|
|
|
|
|
|
|
DataBlobReader::new(tmpfile, self.crypt_config.clone())
|
|
|
|
}
|
|
|
|
|
2019-11-08 11:40:10 +00:00
|
|
|
/// Download dynamic index file
|
|
|
|
///
|
2020-05-30 14:37:33 +00:00
|
|
|
/// This creates a temporary file in /tmp (using O_TMPFILE). The index is verified using
|
2019-11-08 11:40:10 +00:00
|
|
|
/// the provided manifest.
|
|
|
|
pub async fn download_dynamic_index(
|
|
|
|
&self,
|
|
|
|
manifest: &BackupManifest,
|
|
|
|
name: &str,
|
|
|
|
) -> Result<DynamicIndexReader, Error> {
|
2020-06-12 09:46:42 +00:00
|
|
|
let mut tmpfile = std::fs::OpenOptions::new()
|
2019-11-08 11:40:10 +00:00
|
|
|
.write(true)
|
|
|
|
.read(true)
|
|
|
|
.custom_flags(libc::O_TMPFILE)
|
|
|
|
.open("/tmp")?;
|
|
|
|
|
2020-06-12 09:46:42 +00:00
|
|
|
self.download(name, &mut tmpfile).await?;
|
2019-11-08 11:40:10 +00:00
|
|
|
|
|
|
|
let index = DynamicIndexReader::new(tmpfile)
|
|
|
|
.map_err(|err| format_err!("unable to read dynamic index '{}' - {}", name, err))?;
|
|
|
|
|
|
|
|
// Note: do not use values stored in index (not trusted) - instead, computed them again
|
|
|
|
let (csum, size) = index.compute_csum();
|
|
|
|
manifest.verify_file(name, &csum, size)?;
|
|
|
|
|
|
|
|
Ok(index)
|
|
|
|
}
|
2019-11-08 11:43:56 +00:00
|
|
|
|
|
|
|
/// Download fixed index file
|
|
|
|
///
|
2020-05-30 14:37:33 +00:00
|
|
|
/// This creates a temporary file in /tmp (using O_TMPFILE). The index is verified using
|
2019-11-08 11:43:56 +00:00
|
|
|
/// the provided manifest.
|
|
|
|
pub async fn download_fixed_index(
|
|
|
|
&self,
|
|
|
|
manifest: &BackupManifest,
|
|
|
|
name: &str,
|
|
|
|
) -> Result<FixedIndexReader, Error> {
|
2020-06-12 09:46:42 +00:00
|
|
|
let mut tmpfile = std::fs::OpenOptions::new()
|
2019-11-08 11:43:56 +00:00
|
|
|
.write(true)
|
|
|
|
.read(true)
|
|
|
|
.custom_flags(libc::O_TMPFILE)
|
|
|
|
.open("/tmp")?;
|
|
|
|
|
2020-06-12 09:46:42 +00:00
|
|
|
self.download(name, &mut tmpfile).await?;
|
2019-11-08 11:43:56 +00:00
|
|
|
|
|
|
|
let index = FixedIndexReader::new(tmpfile)
|
|
|
|
.map_err(|err| format_err!("unable to read fixed index '{}' - {}", name, err))?;
|
|
|
|
|
|
|
|
// Note: do not use values stored in index (not trusted) - instead, computed them again
|
|
|
|
let (csum, size) = index.compute_csum();
|
|
|
|
manifest.verify_file(name, &csum, size)?;
|
|
|
|
|
|
|
|
Ok(index)
|
|
|
|
}
|
2019-10-12 10:57:08 +00:00
|
|
|
}
|