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