2020-04-17 14:11:25 +02:00
use anyhow ::{ bail , format_err , Error } ;
2019-10-12 17:58:08 +02:00
use std ::convert ::TryFrom ;
2019-12-31 15:23:41 +01:00
use std ::path ::Path ;
2019-10-12 17:58:08 +02:00
use serde_json ::{ json , Value } ;
2020-07-07 15:20:20 +02:00
use crate ::backup ::{ BackupDir , CryptMode } ;
2019-10-12 17:58:08 +02:00
pub const MANIFEST_BLOB_NAME : & str = " index.json.blob " ;
2020-05-30 14:04:15 +02:00
pub const CLIENT_LOG_BLOB_NAME : & str = " client.log.blob " ;
2019-10-12 17:58:08 +02:00
2019-12-31 15:23:41 +01:00
pub struct FileInfo {
pub filename : String ,
2020-07-07 15:20:20 +02:00
pub crypt_mode : CryptMode ,
2019-12-31 15:23:41 +01:00
pub size : u64 ,
pub csum : [ u8 ; 32 ] ,
2019-10-12 17:58:08 +02:00
}
pub struct BackupManifest {
snapshot : BackupDir ,
files : Vec < FileInfo > ,
}
2019-12-31 15:23:41 +01:00
#[ derive(PartialEq) ]
pub enum ArchiveType {
FixedIndex ,
DynamicIndex ,
Blob ,
}
pub fn archive_type < P : AsRef < Path > > (
archive_name : P ,
) -> Result < ArchiveType , Error > {
let archive_name = archive_name . as_ref ( ) ;
let archive_type = match archive_name . extension ( ) . and_then ( | ext | ext . to_str ( ) ) {
Some ( " didx " ) = > ArchiveType ::DynamicIndex ,
Some ( " fidx " ) = > ArchiveType ::FixedIndex ,
Some ( " blob " ) = > ArchiveType ::Blob ,
_ = > bail! ( " unknown archive type: {:?} " , archive_name ) ,
} ;
Ok ( archive_type )
}
2019-10-12 17:58:08 +02:00
impl BackupManifest {
pub fn new ( snapshot : BackupDir ) -> Self {
Self { files : Vec ::new ( ) , snapshot }
}
2020-07-07 15:20:20 +02:00
pub fn add_file ( & mut self , filename : String , size : u64 , csum : [ u8 ; 32 ] , crypt_mode : CryptMode ) -> Result < ( ) , Error > {
2019-12-31 15:23:41 +01:00
let _archive_type = archive_type ( & filename ) ? ; // check type
2020-07-07 15:20:20 +02:00
self . files . push ( FileInfo { filename , size , csum , crypt_mode } ) ;
2019-12-31 15:23:41 +01:00
Ok ( ( ) )
}
pub fn files ( & self ) -> & [ FileInfo ] {
& self . files [ .. ]
2019-10-12 17:58:08 +02:00
}
2019-10-13 10:09:12 +02:00
fn lookup_file_info ( & self , name : & str ) -> Result < & FileInfo , Error > {
let info = self . files . iter ( ) . find ( | item | item . filename = = name ) ;
match info {
None = > bail! ( " manifest does not contain file '{}' " , name ) ,
Some ( info ) = > Ok ( info ) ,
}
}
pub fn verify_file ( & self , name : & str , csum : & [ u8 ; 32 ] , size : u64 ) -> Result < ( ) , Error > {
let info = self . lookup_file_info ( name ) ? ;
if size ! = info . size {
2020-06-03 19:09:58 +02:00
bail! ( " wrong size for file '{}' ({} != {}) " , name , info . size , size ) ;
2019-10-13 10:09:12 +02:00
}
if csum ! = & info . csum {
bail! ( " wrong checksum for file '{}' " , name ) ;
}
Ok ( ( ) )
}
2019-10-12 17:58:08 +02:00
pub fn into_json ( self ) -> Value {
json! ( {
" backup-type " : self . snapshot . group ( ) . backup_type ( ) ,
" backup-id " : self . snapshot . group ( ) . backup_id ( ) ,
" backup-time " : self . snapshot . backup_time ( ) . timestamp ( ) ,
" files " : self . files . iter ( )
. fold ( Vec ::new ( ) , | mut acc , info | {
2020-07-07 15:20:20 +02:00
acc . push ( json! ( {
2019-10-12 17:58:08 +02:00
" filename " : info . filename ,
2020-07-07 15:20:20 +02:00
" crypt-mode " : info . crypt_mode ,
2019-10-12 17:58:08 +02:00
" size " : info . size ,
" csum " : proxmox ::tools ::digest_to_hex ( & info . csum ) ,
2020-07-07 15:20:20 +02:00
} ) ) ;
2019-10-12 17:58:08 +02:00
acc
} )
} )
}
}
2020-01-05 16:20:26 +01:00
impl TryFrom < super ::DataBlob > for BackupManifest {
type Error = Error ;
fn try_from ( blob : super ::DataBlob ) -> Result < Self , Error > {
let data = blob . decode ( None )
. map_err ( | err | format_err! ( " decode backup manifest blob failed - {} " , err ) ) ? ;
let json : Value = serde_json ::from_slice ( & data [ .. ] )
. map_err ( | err | format_err! ( " unable to parse backup manifest json - {} " , err ) ) ? ;
BackupManifest ::try_from ( json )
}
}
2019-10-12 17:58:08 +02:00
impl TryFrom < Value > for BackupManifest {
type Error = Error ;
fn try_from ( data : Value ) -> Result < Self , Error > {
2019-10-13 09:39:21 +02:00
use crate ::tools ::{ required_string_property , required_integer_property , required_array_property } ;
2020-01-21 12:28:01 +01:00
proxmox ::try_block! ( {
2019-10-14 12:28:27 +02:00
let backup_type = required_string_property ( & data , " backup-type " ) ? ;
let backup_id = required_string_property ( & data , " backup-id " ) ? ;
let backup_time = required_integer_property ( & data , " backup-time " ) ? ;
2019-10-12 17:58:08 +02:00
2019-10-13 10:09:12 +02:00
let snapshot = BackupDir ::new ( backup_type , backup_id , backup_time ) ;
2019-10-12 17:58:08 +02:00
2019-12-31 15:23:41 +01:00
let mut manifest = BackupManifest ::new ( snapshot ) ;
2019-10-13 10:09:12 +02:00
for item in required_array_property ( & data , " files " ) ? . iter ( ) {
let filename = required_string_property ( item , " filename " ) ? . to_owned ( ) ;
let csum = required_string_property ( item , " csum " ) ? ;
let csum = proxmox ::tools ::hex_to_digest ( csum ) ? ;
let size = required_integer_property ( item , " size " ) ? as u64 ;
2020-07-07 15:20:20 +02:00
let crypt_mode : CryptMode = serde_json ::from_value ( item [ " crypt-mode " ] . clone ( ) ) ? ;
manifest . add_file ( filename , size , csum , crypt_mode ) ? ;
2019-10-13 10:09:12 +02:00
}
2020-01-23 11:16:12 +01:00
if manifest . files ( ) . is_empty ( ) {
bail! ( " manifest does not list any files. " ) ;
}
2019-12-31 15:23:41 +01:00
Ok ( manifest )
2019-10-13 10:09:12 +02:00
} ) . map_err ( | err : Error | format_err! ( " unable to parse backup manifest - {} " , err ) )
2019-10-12 17:58:08 +02:00
}
}