use crate::bytes::{le_u32, le_u64, read_guid};
use crate::guid::{format_guid, VSS_IDENTIFIER};
pub const VSS_VOLUME_HEADER_OFFSET: u64 = 0x1E00;
pub const BLOCK_SIZE: usize = 16_384;
pub const CATALOG_BLOCK_HEADER_LEN: usize = 128;
pub const CATALOG_ENTRY_LEN: usize = 128;
pub const VOLUME_HEADER_RECORD_TYPE: u32 = 0x01;
pub const CATALOG_BLOCK_RECORD_TYPE: u32 = 0x02;
pub const MAX_CATALOG_BLOCKS: usize = 4096;
#[derive(Debug, Clone)]
pub struct VolumeHeader {
pub has_vss_identifier: bool,
pub version: u32,
pub record_type: u32,
pub current_offset: u64,
pub catalog_offset: u64,
pub maximum_size: u64,
pub volume_identifier: [u8; 16],
pub storage_volume_identifier: [u8; 16],
}
impl VolumeHeader {
#[must_use]
pub fn parse(buf: &[u8]) -> Self {
VolumeHeader {
has_vss_identifier: read_guid(buf, 0) == VSS_IDENTIFIER,
version: le_u32(buf, 16),
record_type: le_u32(buf, 20),
current_offset: le_u64(buf, 24),
catalog_offset: le_u64(buf, 48),
maximum_size: le_u64(buf, 56),
volume_identifier: read_guid(buf, 64),
storage_volume_identifier: read_guid(buf, 80),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StoreDescriptor {
pub store_id: [u8; 16],
pub volume_size: u64,
pub sequence: u64,
pub flags: u64,
pub creation_time: u64,
pub store_header_offset: Option<u64>,
}
impl StoreDescriptor {
#[must_use]
pub fn store_id_string(&self) -> String {
format_guid(&self.store_id)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum CatalogEntry {
Empty,
Snapshot(StoreDescriptor),
StorePointer {
store_id: [u8; 16],
store_header_offset: u64,
},
Other,
}
pub(crate) fn parse_catalog_entry(buf: &[u8]) -> CatalogEntry {
match le_u64(buf, 0) {
0x02 => CatalogEntry::Snapshot(StoreDescriptor {
store_id: read_guid(buf, 16),
volume_size: le_u64(buf, 8),
sequence: le_u64(buf, 32),
flags: le_u64(buf, 40),
creation_time: le_u64(buf, 48),
store_header_offset: None,
}),
0x03 => CatalogEntry::StorePointer {
store_id: read_guid(buf, 16),
store_header_offset: le_u64(buf, 32),
},
0x00 => CatalogEntry::Empty,
_ => CatalogEntry::Other,
}
}
pub(crate) fn catalog_next_block_offset(block: &[u8]) -> u64 {
le_u64(block, 40)
}
pub(crate) fn is_catalog_block(block: &[u8]) -> bool {
read_guid(block, 0) == VSS_IDENTIFIER && le_u32(block, 20) == CATALOG_BLOCK_RECORD_TYPE
}