use crate::types::Guid;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SignaturePosition {
FileTypeIdentifier,
Header,
RegionTable,
MetadataTable,
LogEntry,
Descriptor,
DataSector,
}
pub(crate) fn pad_signature_4to8(sig: [u8; 4]) -> [u8; 8] {
let mut out = [0u8; 8];
out[..4].copy_from_slice(&sig);
out
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
#[error("IO error")]
Io(#[from] std::io::Error),
#[error("invalid VHDX file: {0}")]
InvalidFile(String),
#[error("signature mismatch at {position:?}: expected {expected:?}, found {found:?}")]
InvalidSignature {
position: SignaturePosition,
expected: [u8; 8],
found: [u8; 8],
},
#[error("corrupted header: {0}")]
CorruptedHeader(String),
#[error("header LogGuid mismatch: header1={header1_log_guid}, header2={header2_log_guid}")]
HeaderLogGuidMismatch {
header1_log_guid: Guid,
header2_log_guid: Guid,
},
#[error("header sequence number invalid: seq1={sequence_number_1}, seq2={sequence_number_2}")]
HeaderSequenceNumberInvalid {
sequence_number_1: u64,
sequence_number_2: u64,
},
#[error("unsupported VHDX version: {version}")]
UnsupportedVersion { version: u16 },
#[error("unsupported log version: {version}")]
UnsupportedLogVersion { version: u16 },
#[error("header log field not 1MB-aligned: {field}={value}")]
HeaderLogNotAligned {
field: String,
value: u64,
},
#[error("checksum mismatch: expected {expected:#010x}, actual {actual:#010x}")]
InvalidChecksum { expected: u32, actual: u32 },
#[error("invalid BAT block state: {0:#04x}")]
InvalidBlockState(u8),
#[error("invalid sector bitmap block state: {0:#04x}")]
InvalidSectorBitmapState(u8),
#[error("BAT state mismatch: state={state:#04x}, {description}")]
StateMismatch {
state: u8,
description: String,
},
#[error("BAT entry file offset not aligned: offset_mb={offset_mb}, block_size={block_size}")]
BatFileOffsetUnaligned {
offset_mb: u64,
block_size: u32,
},
#[error("BAT entry count insufficient: actual={actual}, expected={expected}")]
BatEntryCountInsufficient { actual: u64, expected: u64 },
#[error("BAT file offset duplicate: offset_mb={offset_mb}")]
BatFileOffsetDuplicate { offset_mb: u64 },
#[error("invalid region table: {0}")]
InvalidRegionTable(String),
#[error("unknown required region: {guid}")]
RegionRequiredUnknown { guid: Guid },
#[error("unknown optional region: {guid}")]
RegionOptionalUnknown { guid: Guid },
#[error("invalid metadata: {0}")]
InvalidMetadata(String),
#[error("unknown metadata GUID: {guid}")]
MetadataGuidUnknown { guid: Guid },
#[error("required metadata item missing: {guid}")]
MetadataRequiredMissing { guid: Guid },
#[error("unknown required metadata item: {guid}")]
MetadataRequiredUnknown { guid: Guid },
#[error("unknown optional metadata item: {guid}")]
MetadataOptionalUnknown { guid: Guid },
#[error("metadata reserved flags set: {flags:#010x}")]
MetadataReservedFlagsSet { flags: u32 },
#[error("metadata entry reserved field non-zero: {reserved:#010x}")]
MetadataEntryReservedNonzero { reserved: u32 },
#[error("FileParameters reserved flags set: {flags:#010x}")]
FileParametersReservedFlags { flags: u32 },
#[error("invalid parent locator: {0}")]
InvalidParentLocator(String),
#[error("metadata item not found: {guid}")]
MetadataNotFound { guid: Guid },
#[error("log replay is required")]
LogReplayRequired,
#[error("corrupted log entry: {0}")]
LogEntryCorrupted(String),
#[error("log sequence gap: expected={expected}, found={found}")]
LogSequenceGap { expected: u64, found: u64 },
#[error("log sequence GUID mismatch: entry={entry_log_guid}, header={header_log_guid}")]
LogSequenceGuidMismatch {
entry_log_guid: Guid,
header_log_guid: Guid,
},
#[error("log active sequence empty")]
LogActiveSequenceEmpty,
#[error("BAT entry not found: index {index}")]
BatEntryNotFound { index: u64 },
#[error("block not present: block {block_idx}, state={state}")]
BlockNotPresent { block_idx: u64, state: String },
#[error("sector out of bounds: {sector} (max={max})")]
SectorOutOfBounds { sector: u64, max: u64 },
#[error("parent disk not found")]
ParentNotFound,
#[error("parent resolver required")]
ParentResolverRequired,
#[error("parent logical sector size mismatch: child={child}, parent={parent}")]
ParentSectorSizeMismatch { child: u32, parent: u32 },
#[error("parent GUID mismatch: expected {expected}, found {actual}")]
ParentMismatch { expected: Guid, actual: Guid },
#[error("parent locator GUID mismatch: expected {expected}, found {actual}")]
ParentLocatorGuidMismatch { expected: Guid, actual: Guid },
#[error("parent linkage key missing")]
ParentLocatorMissingLinkage,
#[error("parent_linkage2 conflict (merge transition)")]
ParentLocatorLinkage2Conflict,
#[error("invalid parameter: {0}")]
InvalidParameter(String),
#[error("read-only mode")]
ReadOnly,
}
pub type Result<T> = std::result::Result<T, Error>;
impl From<Error> for std::io::Error {
fn from(e: Error) -> Self {
match e {
Error::Io(io_err) => io_err,
Error::InvalidParameter(msg) => Self::new(std::io::ErrorKind::InvalidData, msg),
Error::InvalidFile(..)
| Error::InvalidSignature { .. }
| Error::CorruptedHeader(..)
| Error::HeaderLogGuidMismatch { .. }
| Error::HeaderSequenceNumberInvalid { .. }
| Error::UnsupportedVersion { .. }
| Error::UnsupportedLogVersion { .. }
| Error::HeaderLogNotAligned { .. }
| Error::InvalidChecksum { .. }
| Error::InvalidBlockState(..)
| Error::InvalidSectorBitmapState(..)
| Error::StateMismatch { .. }
| Error::InvalidRegionTable(..)
| Error::RegionRequiredUnknown { .. }
| Error::RegionOptionalUnknown { .. }
| Error::InvalidMetadata(..)
| Error::MetadataGuidUnknown { .. }
| Error::MetadataRequiredMissing { .. }
| Error::MetadataRequiredUnknown { .. }
| Error::MetadataOptionalUnknown { .. }
| Error::MetadataReservedFlagsSet { .. }
| Error::MetadataEntryReservedNonzero { .. }
| Error::FileParametersReservedFlags { .. }
| Error::InvalidParentLocator(..)
| Error::BatFileOffsetUnaligned { .. }
| Error::BatEntryCountInsufficient { .. }
| Error::BatFileOffsetDuplicate { .. }
| Error::LogEntryCorrupted(..)
| Error::LogSequenceGap { .. }
| Error::LogSequenceGuidMismatch { .. }
| Error::LogActiveSequenceEmpty
| Error::ParentLocatorMissingLinkage
| Error::ParentLocatorLinkage2Conflict
| Error::ParentSectorSizeMismatch { .. }
| Error::ParentLocatorGuidMismatch { .. }
| Error::ParentMismatch { .. } => {
Self::new(std::io::ErrorKind::InvalidData, e.to_string())
}
Error::MetadataNotFound { .. }
| Error::BatEntryNotFound { .. }
| Error::BlockNotPresent { .. }
| Error::ParentResolverRequired
| Error::ParentNotFound => Self::new(std::io::ErrorKind::NotFound, e.to_string()),
Error::ReadOnly | Error::LogReplayRequired => {
Self::new(std::io::ErrorKind::PermissionDenied, e.to_string())
}
Error::SectorOutOfBounds { .. } => {
Self::new(std::io::ErrorKind::UnexpectedEof, e.to_string())
}
}
}
}