]> git.proxmox.com Git - proxmox-backup.git/blame - src/client/backup_reader.rs
add tools::http for generic HTTP GET and move HttpsConnector there
[proxmox-backup.git] / src / client / backup_reader.rs
CommitLineData
b53f6379 1use anyhow::{format_err, Error};
1bc1d81a 2use std::io::{Write, Seek, SeekFrom};
bb19af73 3use std::fs::File;
9e490a74 4use std::sync::Arc;
c3d84a22 5use std::os::unix::fs::OpenOptionsExt;
9e490a74 6
dc089345 7use futures::future::AbortHandle;
9e490a74
DM
8use serde_json::{json, Value};
9
10use proxmox::tools::digest_to_hex;
11
1bc1d81a
DM
12use crate::{
13 tools::compute_file_csum,
14 backup::*,
15};
9e490a74
DM
16
17use super::{HttpClient, H2Client};
18
913acb41 19/// Backup Reader
9e490a74
DM
20pub struct BackupReader {
21 h2: H2Client,
dc089345 22 abort: AbortHandle,
296c50ba 23 crypt_config: Option<Arc<CryptConfig>>,
9e490a74
DM
24}
25
26impl Drop for BackupReader {
27
28 fn drop(&mut self) {
dc089345 29 self.abort.abort();
9e490a74
DM
30 }
31}
32
33impl BackupReader {
34
dc089345
WB
35 fn new(h2: H2Client, abort: AbortHandle, crypt_config: Option<Arc<CryptConfig>>) -> Arc<Self> {
36 Arc::new(Self { h2, abort, crypt_config})
9e490a74
DM
37 }
38
913acb41 39 /// Create a new instance by upgrading the connection at '/api2/json/reader'
9e490a74
DM
40 pub async fn start(
41 client: HttpClient,
296c50ba 42 crypt_config: Option<Arc<CryptConfig>>,
9e490a74
DM
43 datastore: &str,
44 backup_type: &str,
45 backup_id: &str,
6a7be83e 46 backup_time: i64,
9e490a74
DM
47 debug: bool,
48 ) -> Result<Arc<BackupReader>, Error> {
49
50 let param = json!({
51 "backup-type": backup_type,
52 "backup-id": backup_id,
6a7be83e 53 "backup-time": backup_time,
9e490a74
DM
54 "store": datastore,
55 "debug": debug,
56 });
ba20987a 57 let req = HttpClient::request_builder(client.server(), client.port(), "GET", "/api2/json/reader", Some(param)).unwrap();
9e490a74 58
dc089345 59 let (h2, abort) = client.start_h2_connection(req, String::from(PROXMOX_BACKUP_READER_PROTOCOL_ID_V1!())).await?;
9e490a74 60
dc089345 61 Ok(BackupReader::new(h2, abort, crypt_config))
9e490a74
DM
62 }
63
913acb41 64 /// Execute a GET request
9e490a74
DM
65 pub async fn get(
66 &self,
67 path: &str,
68 param: Option<Value>,
69 ) -> Result<Value, Error> {
70 self.h2.get(path, param).await
71 }
72
913acb41 73 /// Execute a PUT request
9e490a74
DM
74 pub async fn put(
75 &self,
76 path: &str,
77 param: Option<Value>,
78 ) -> Result<Value, Error> {
79 self.h2.put(path, param).await
80 }
81
913acb41 82 /// Execute a POST request
9e490a74
DM
83 pub async fn post(
84 &self,
85 path: &str,
86 param: Option<Value>,
87 ) -> Result<Value, Error> {
88 self.h2.post(path, param).await
89 }
90
913acb41 91 /// Execute a GET request and send output to a writer
9e490a74
DM
92 pub async fn download<W: Write + Send>(
93 &self,
94 file_name: &str,
95 output: W,
3d571d55 96 ) -> Result<(), Error> {
9e490a74
DM
97 let path = "download";
98 let param = json!({ "file-name": file_name });
99 self.h2.download(path, Some(param), output).await
100 }
101
913acb41
DM
102 /// Execute a special GET request and send output to a writer
103 ///
104 /// This writes random data, and is only useful to test download speed.
9e490a74
DM
105 pub async fn speedtest<W: Write + Send>(
106 &self,
107 output: W,
3d571d55 108 ) -> Result<(), Error> {
9e490a74
DM
109 self.h2.download("speedtest", None, output).await
110 }
111
913acb41 112 /// Download a specific chunk
9e490a74
DM
113 pub async fn download_chunk<W: Write + Send>(
114 &self,
115 digest: &[u8; 32],
116 output: W,
3d571d55 117 ) -> Result<(), Error> {
9e490a74
DM
118 let path = "chunk";
119 let param = json!({ "digest": digest_to_hex(digest) });
120 self.h2.download(path, Some(param), output).await
121 }
122
123 pub fn force_close(self) {
dc089345 124 self.abort.abort();
9e490a74 125 }
296c50ba
DM
126
127 /// Download backup manifest (index.json)
2107a5ae
DM
128 ///
129 /// The manifest signature is verified if we have a crypt_config.
130 pub async fn download_manifest(&self) -> Result<(BackupManifest, Vec<u8>), Error> {
f06b820a 131
3d571d55
WB
132 let mut raw_data = Vec::with_capacity(64 * 1024);
133 self.download(MANIFEST_BLOB_NAME, &mut raw_data).await?;
39f18b30 134 let blob = DataBlob::load_from_reader(&mut &raw_data[..])?;
8819d1f2
FG
135 // no expected digest available
136 let data = blob.decode(None, None)?;
b53f6379
DM
137
138 let manifest = BackupManifest::from_data(&data[..], self.crypt_config.as_ref().map(Arc::as_ref))?;
2107a5ae
DM
139
140 Ok((manifest, data))
296c50ba 141 }
c3d84a22 142
bb19af73
DM
143 /// Download a .blob file
144 ///
add5861e 145 /// This creates a temporary file in /tmp (using O_TMPFILE). The data is verified using
bb19af73
DM
146 /// the provided manifest.
147 pub async fn download_blob(
148 &self,
149 manifest: &BackupManifest,
150 name: &str,
151 ) -> Result<DataBlobReader<File>, Error> {
152
3d571d55 153 let mut tmpfile = std::fs::OpenOptions::new()
bb19af73
DM
154 .write(true)
155 .read(true)
156 .custom_flags(libc::O_TMPFILE)
157 .open("/tmp")?;
158
3d571d55 159 self.download(name, &mut tmpfile).await?;
bb19af73
DM
160
161 let (csum, size) = compute_file_csum(&mut tmpfile)?;
162 manifest.verify_file(name, &csum, size)?;
163
164 tmpfile.seek(SeekFrom::Start(0))?;
165
166 DataBlobReader::new(tmpfile, self.crypt_config.clone())
167 }
168
c3d84a22
DM
169 /// Download dynamic index file
170 ///
add5861e 171 /// This creates a temporary file in /tmp (using O_TMPFILE). The index is verified using
c3d84a22
DM
172 /// the provided manifest.
173 pub async fn download_dynamic_index(
174 &self,
175 manifest: &BackupManifest,
176 name: &str,
177 ) -> Result<DynamicIndexReader, Error> {
178
3d571d55 179 let mut tmpfile = std::fs::OpenOptions::new()
c3d84a22
DM
180 .write(true)
181 .read(true)
182 .custom_flags(libc::O_TMPFILE)
183 .open("/tmp")?;
184
3d571d55 185 self.download(name, &mut tmpfile).await?;
c3d84a22
DM
186
187 let index = DynamicIndexReader::new(tmpfile)
188 .map_err(|err| format_err!("unable to read dynamic index '{}' - {}", name, err))?;
189
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)?;
193
194 Ok(index)
195 }
72050500
DM
196
197 /// Download fixed index file
198 ///
add5861e 199 /// This creates a temporary file in /tmp (using O_TMPFILE). The index is verified using
72050500
DM
200 /// the provided manifest.
201 pub async fn download_fixed_index(
202 &self,
203 manifest: &BackupManifest,
204 name: &str,
205 ) -> Result<FixedIndexReader, Error> {
206
3d571d55 207 let mut tmpfile = std::fs::OpenOptions::new()
72050500
DM
208 .write(true)
209 .read(true)
210 .custom_flags(libc::O_TMPFILE)
211 .open("/tmp")?;
212
3d571d55 213 self.download(name, &mut tmpfile).await?;
72050500
DM
214
215 let index = FixedIndexReader::new(tmpfile)
216 .map_err(|err| format_err!("unable to read fixed index '{}' - {}", name, err))?;
217
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)?;
221
222 Ok(index)
223 }
9e490a74 224}