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