1use super::{
2 Error, Guid, Header, HeaderStructure, MIB, Result, SignaturePosition, SpecValidator,
3 ValidationIssue,
4};
5
6impl SpecValidator {
7 pub fn validate_header(&self) -> Result<Vec<ValidationIssue>> {
24 let mut issues = Vec::new();
25 let header = self.parse_header()?;
26 Self::validate_file_type_identifier(&header, &mut issues)?;
27 Self::validate_header_pair(&header, &mut issues)?;
28 Self::validate_log_alignment(&header, &mut issues)?;
29
30 Ok(issues)
31 }
32
33 fn validate_file_type_identifier(
34 header: &Header<'_>, issues: &mut Vec<ValidationIssue>,
35 ) -> Result<()> {
36 let ft = header.file_type();
37 if ft.signature() == b"vhdxfile" {
38 return Ok(());
39 }
40 Self::push_issue(
41 issues,
42 ValidationIssue::new(
43 "header",
44 "HEADER_FILE_TYPE_ID_INVALID",
45 format!(
46 "invalid signature at offset 0: expected \"vhdxfile\", found {:?}",
47 std::str::from_utf8(ft.signature()).unwrap_or("<binary>")
48 ),
49 "MS-VHDX/2.2.1",
50 ),
51 );
52 Err(Error::InvalidSignature {
53 position: SignaturePosition::FileTypeIdentifier,
54 expected: *b"vhdxfile",
55 found: *ft.signature(),
56 })
57 }
58
59 fn validate_header_pair(header: &Header<'_>, issues: &mut Vec<ValidationIssue>) -> Result<()> {
60 let v1 = header
61 .header(1)
62 .and_then(|h| Self::validate_single_header(Ok(h)));
63 let v2 = header
64 .header(2)
65 .and_then(|h| Self::validate_single_header(Ok(h)));
66 let h1_valid = v1.is_ok();
67 let h2_valid = v2.is_ok();
68 if !h1_valid && !h2_valid {
69 Self::push_header_issue(issues, 1, v1.as_ref().err().unwrap());
70 Self::push_header_issue(issues, 2, v2.as_ref().err().unwrap());
71 return Err(Error::CorruptedHeader("both headers are invalid".into()));
72 }
73 if !h1_valid {
74 Self::push_header_issue(issues, 1, v1.as_ref().err().unwrap());
75 }
76 if !h2_valid {
77 Self::push_header_issue(issues, 2, v2.as_ref().err().unwrap());
78 }
79 if h1_valid && h2_valid {
80 Self::validate_header_pair_consistency(header, issues, &v1.unwrap(), &v2.unwrap())?;
81 }
82 Ok(())
83 }
84
85 fn validate_header_pair_consistency(
86 header: &Header<'_>, issues: &mut Vec<ValidationIssue>, v1: &HeaderStructure<'_>,
87 v2: &HeaderStructure<'_>,
88 ) -> Result<()> {
89 if v1.sequence_number() == v2.sequence_number() {
90 Self::push_issue(
91 issues,
92 ValidationIssue::new(
93 "header",
94 "HEADER_SEQUENCE_NUMBER_INVALID",
95 "both headers have same sequence number",
96 "MS-VHDX/2.2.2",
97 ),
98 );
99 return Err(Error::HeaderSequenceNumberInvalid {
100 sequence_number_1: v1.sequence_number(),
101 sequence_number_2: v2.sequence_number(),
102 });
103 }
104 let log_guid = Self::current_log_guid(header)?;
105 if log_guid == v1.log_guid() && log_guid == v2.log_guid() {
106 return Ok(());
107 }
108 Self::push_issue(
109 issues,
110 ValidationIssue::new(
111 "header",
112 "HEADER_LOG_GUID_MISMATCH",
113 "LogGuid differs between headers",
114 "MS-VHDX/2.2.2",
115 ),
116 );
117 Err(Error::HeaderLogGuidMismatch {
118 header1_log_guid: v1.log_guid(),
119 header2_log_guid: v2.log_guid(),
120 })
121 }
122
123 fn validate_log_alignment(
124 header: &Header<'_>, issues: &mut Vec<ValidationIssue>,
125 ) -> Result<()> {
126 let current = header.header(0)?;
127 let log_offset = current.log_offset();
128 let log_length = current.log_length();
129 if log_length > 0 && u64::from(log_length) % u64::from(MIB) != 0 {
130 Self::push_issue(
131 issues,
132 ValidationIssue::new(
133 "header",
134 "HEADER_LOG_LENGTH_NOT_ALIGNED",
135 format!("log_length {log_length} is not a multiple of 1MB"),
136 "MS-VHDX/2.2.2",
137 ),
138 );
139 return Err(Error::HeaderLogNotAligned {
140 field: "log_length".to_string(),
141 value: u64::from(log_length),
142 });
143 }
144 if log_offset > 0 && log_offset % u64::from(MIB) != 0 {
145 Self::push_issue(
146 issues,
147 ValidationIssue::new(
148 "header",
149 "HEADER_LOG_OFFSET_NOT_ALIGNED",
150 format!("log_offset {log_offset} is not a multiple of 1MB"),
151 "MS-VHDX/2.2.2",
152 ),
153 );
154 return Err(Error::HeaderLogNotAligned {
155 field: "log_offset".to_string(),
156 value: log_offset,
157 });
158 }
159 Ok(())
160 }
161
162 fn validate_single_header(result: Result<HeaderStructure<'_>>) -> Result<HeaderStructure<'_>> {
164 let mut issues = Vec::new();
165 let h = result?;
166
167 if h.version() != 1 {
173 Self::push_issue(
174 &mut issues,
175 ValidationIssue::new(
176 "header",
177 "HEADER_VERSION_UNSUPPORTED",
178 format!("version {} is not supported (expected 1)", h.version()),
179 "MS-VHDX/2.2.2",
180 ),
181 );
182 return Err(Error::UnsupportedVersion {
183 version: h.version(),
184 });
185 }
186
187 if h.log_version() != 0 && h.log_guid() != Guid::zero() {
189 Self::push_issue(
190 &mut issues,
191 ValidationIssue::new(
192 "header",
193 "HEADER_LOG_VERSION_UNSUPPORTED",
194 format!(
195 "log version {} is not supported (expected 0)",
196 h.log_version()
197 ),
198 "MS-VHDX/2.2.2",
199 ),
200 );
201 return Err(Error::UnsupportedLogVersion {
202 version: h.log_version(),
203 });
204 }
205
206 Ok(h)
207 }
208}