2 use std
::io
::{Read, Seek, SeekFrom, Write}
;
5 use anyhow
::{bail, format_err, Error}
;
9 use proxmox
::api
::cli
::{CliCommand, CliCommandMap, CommandLineInterface}
;
11 use pbs_datastore
::dynamic_index
::DynamicIndexReader
;
12 use pbs_datastore
::file_formats
::{DYNAMIC_SIZED_CHUNK_INDEX_1_0, FIXED_SIZED_CHUNK_INDEX_1_0}
;
13 use pbs_datastore
::fixed_index
::FixedIndexReader
;
14 use pbs_datastore
::index
::IndexFile
;
15 use pbs_datastore
::{load_and_decrypt_key, CryptConfig, DataBlob}
;
17 use pbs_client
::tools
::key_source
::get_encryption_key_password
;
19 use proxmox
::tools
::digest_to_hex
;
25 description
: "Path to the index file, either .fidx or .didx.",
29 description
: "Path to the directorty that contains the chunks, usually <datastore>/.chunks.",
33 description
: "Path to a keyfile, if the data was encrypted, a keyfile is needed for decryption.",
38 description
: "Skip the crc verification, increases the restore speed by lot.",
46 /// Restore the data from an index file, given the directory of where chunks
47 /// are saved, the index file and a keyfile, if needed for decryption.
51 keyfile
: Option
<String
>,
54 ) -> Result
<(), Error
> {
55 let file_path
= Path
::new(&file
);
56 let chunks_path
= Path
::new(&chunks
);
58 let key_file_path
= keyfile
.as_ref().map(Path
::new
);
60 let mut file
= File
::open(Path
::new(&file
))?
;
61 let mut magic
= [0; 8];
62 file
.read_exact(&mut magic
)?
;
63 file
.seek(SeekFrom
::Start(0))?
;
64 let index
: Box
<dyn IndexFile
> = match magic
{
65 FIXED_SIZED_CHUNK_INDEX_1_0
=> Box
::new(FixedIndexReader
::new(file
)?
) as Box
<dyn IndexFile
>,
66 DYNAMIC_SIZED_CHUNK_INDEX_1_0
=> {
67 Box
::new(DynamicIndexReader
::new(file
)?
) as Box
<dyn IndexFile
>
69 _
=> bail
!(format_err
!(
70 "index file must either be a .fidx or a .didx file"
74 let crypt_conf_opt
= if let Some(key_file_path
) = key_file_path
{
75 let (key
, _created
, _fingerprint
) =
76 load_and_decrypt_key(&key_file_path
, &get_encryption_key_password
)?
;
77 Some(CryptConfig
::new(key
)?
)
82 let output_filename
= file_path
.file_stem().unwrap().to_str().unwrap();
83 let output_path
= Path
::new(output_filename
);
84 let mut output_file
= File
::create(output_path
)
85 .map_err(|e
| format_err
!("could not create output file - {}", e
))?
;
87 let mut data
= Vec
::with_capacity(4 * 1024 * 1024);
88 for pos
in 0..index
.index_count() {
89 let chunk_digest
= index
.index_digest(pos
).unwrap();
90 let digest_str
= digest_to_hex(chunk_digest
);
91 let digest_prefix
= &digest_str
[0..4];
92 let chunk_path
= chunks_path
.join(digest_prefix
).join(digest_str
);
93 let mut chunk_file
= std
::fs
::File
::open(&chunk_path
)
94 .map_err(|e
| format_err
!("could not open chunk file - {}", e
))?
;
97 chunk_file
.read_to_end(&mut data
)?
;
98 let chunk_blob
= DataBlob
::from_raw(data
.clone())?
;
101 chunk_blob
.verify_crc()?
;
104 output_file
.write_all(
106 .decode(crypt_conf_opt
.as_ref(), Some(chunk_digest
))?
114 pub fn recover_commands() -> CommandLineInterface
{
115 let cmd_def
= CliCommandMap
::new().insert(
117 CliCommand
::new(&API_METHOD_RECOVER_INDEX
).arg_param(&["file", "chunks"]),