1 use anyhow
::{format_err, Error}
;
2 use std
::io
::{Write, Seek, SeekFrom}
;
5 use std
::os
::unix
::fs
::OpenOptionsExt
;
7 use futures
::future
::AbortHandle
;
8 use serde_json
::{json, Value}
;
10 use proxmox
::tools
::digest_to_hex
;
13 tools
::compute_file_csum
,
17 use super::{HttpClient, H2Client}
;
20 pub struct BackupReader
{
23 crypt_config
: Option
<Arc
<CryptConfig
>>,
26 impl Drop
for BackupReader
{
35 fn new(h2
: H2Client
, abort
: AbortHandle
, crypt_config
: Option
<Arc
<CryptConfig
>>) -> Arc
<Self> {
36 Arc
::new(Self { h2, abort, crypt_config}
)
39 /// Create a new instance by upgrading the connection at '/api2/json/reader'
42 crypt_config
: Option
<Arc
<CryptConfig
>>,
48 ) -> Result
<Arc
<BackupReader
>, Error
> {
51 "backup-type": backup_type
,
52 "backup-id": backup_id
,
53 "backup-time": backup_time
,
57 let req
= HttpClient
::request_builder(client
.server(), client
.port(), "GET", "/api2/json/reader", Some(param
)).unwrap();
59 let (h2
, abort
) = client
.start_h2_connection(req
, String
::from(PROXMOX_BACKUP_READER_PROTOCOL_ID_V1
!())).await?
;
61 Ok(BackupReader
::new(h2
, abort
, crypt_config
))
64 /// Execute a GET request
69 ) -> Result
<Value
, Error
> {
70 self.h2
.get(path
, param
).await
73 /// Execute a PUT request
78 ) -> Result
<Value
, Error
> {
79 self.h2
.put(path
, param
).await
82 /// Execute a POST request
87 ) -> Result
<Value
, Error
> {
88 self.h2
.post(path
, param
).await
91 /// Execute a GET request and send output to a writer
92 pub async
fn download
<W
: Write
+ Send
>(
96 ) -> Result
<(), Error
> {
97 let path
= "download";
98 let param
= json
!({ "file-name": file_name }
);
99 self.h2
.download(path
, Some(param
), output
).await
102 /// Execute a special GET request and send output to a writer
104 /// This writes random data, and is only useful to test download speed.
105 pub async
fn speedtest
<W
: Write
+ Send
>(
108 ) -> Result
<(), Error
> {
109 self.h2
.download("speedtest", None
, output
).await
112 /// Download a specific chunk
113 pub async
fn download_chunk
<W
: Write
+ Send
>(
117 ) -> Result
<(), Error
> {
119 let param
= json
!({ "digest": digest_to_hex(digest) }
);
120 self.h2
.download(path
, Some(param
), output
).await
123 pub fn force_close(self) {
127 /// Download backup manifest (index.json)
129 /// The manifest signature is verified if we have a crypt_config.
130 pub async
fn download_manifest(&self) -> Result
<(BackupManifest
, Vec
<u8>), Error
> {
132 let mut raw_data
= Vec
::with_capacity(64 * 1024);
133 self.download(MANIFEST_BLOB_NAME
, &mut raw_data
).await?
;
134 let blob
= DataBlob
::load_from_reader(&mut &raw_data
[..])?
;
135 // no expected digest available
136 let data
= blob
.decode(None
, None
)?
;
138 let manifest
= BackupManifest
::from_data(&data
[..], self.crypt_config
.as_ref().map(Arc
::as_ref
))?
;
143 /// Download a .blob file
145 /// This creates a temporary file in /tmp (using O_TMPFILE). The data is verified using
146 /// the provided manifest.
147 pub async
fn download_blob(
149 manifest
: &BackupManifest
,
151 ) -> Result
<DataBlobReader
<File
>, Error
> {
153 let mut tmpfile
= std
::fs
::OpenOptions
::new()
156 .custom_flags(libc
::O_TMPFILE
)
159 self.download(name
, &mut tmpfile
).await?
;
161 let (csum
, size
) = compute_file_csum(&mut tmpfile
)?
;
162 manifest
.verify_file(name
, &csum
, size
)?
;
164 tmpfile
.seek(SeekFrom
::Start(0))?
;
166 DataBlobReader
::new(tmpfile
, self.crypt_config
.clone())
169 /// Download dynamic index file
171 /// This creates a temporary file in /tmp (using O_TMPFILE). The index is verified using
172 /// the provided manifest.
173 pub async
fn download_dynamic_index(
175 manifest
: &BackupManifest
,
177 ) -> Result
<DynamicIndexReader
, Error
> {
179 let mut tmpfile
= std
::fs
::OpenOptions
::new()
182 .custom_flags(libc
::O_TMPFILE
)
185 self.download(name
, &mut tmpfile
).await?
;
187 let index
= DynamicIndexReader
::new(tmpfile
)
188 .map_err(|err
| format_err
!("unable to read dynamic index '{}' - {}", name
, err
))?
;
190 // Note: do not use values stored in index (not trusted) - instead, computed them again
191 let (csum
, size
) = index
.compute_csum();
192 manifest
.verify_file(name
, &csum
, size
)?
;
197 /// Download fixed index file
199 /// This creates a temporary file in /tmp (using O_TMPFILE). The index is verified using
200 /// the provided manifest.
201 pub async
fn download_fixed_index(
203 manifest
: &BackupManifest
,
205 ) -> Result
<FixedIndexReader
, Error
> {
207 let mut tmpfile
= std
::fs
::OpenOptions
::new()
210 .custom_flags(libc
::O_TMPFILE
)
213 self.download(name
, &mut tmpfile
).await?
;
215 let index
= FixedIndexReader
::new(tmpfile
)
216 .map_err(|err
| format_err
!("unable to read fixed index '{}' - {}", name
, err
))?
;
218 // Note: do not use values stored in index (not trusted) - instead, computed them again
219 let (csum
, size
) = index
.compute_csum();
220 manifest
.verify_file(name
, &csum
, size
)?
;