1use super::ParseSchemaExt;
11use crate::Vtl2SettingsErrorCode;
12use crate::Vtl2SettingsErrorInfo;
13use crate::errors::ErrorContext;
14use crate::errors::ParseErrors;
15use crate::schema::ParseResultExt;
16use crate::schema::ParseSchema;
17use crate::schema::ParsingStopped;
18use guid::Guid;
19use physical_device::DeviceType;
20use std::error::Error as _;
21use std::fmt::Write;
22use storage_controller::StorageProtocol;
23use thiserror::Error;
24use vtl2_settings_proto::*;
25
26pub(crate) const NAMESPACE_BASE: &str = "Base";
27pub(crate) const NAMESPACE_NETWORK_DEVICE: &str = "NetworkDevice";
28pub(crate) const NAMESPACE_NETWORK_ACCELERATION: &str = "NetworkAcceleration";
29
30#[derive(Error, Debug)]
31pub(crate) enum Error<'a> {
32 #[error("unsupported schema version {0:#x}")]
33 UnsupportedSchemaVersion(u32),
34 #[error("unsupported schema namespace {0}")]
35 UnsupportedSchemaNamespace(&'a str),
36 #[error("empty namespace settings chunk {0}")]
37 EmptyNamespaceChunk(&'a str),
38 #[error("invalid instance ID '{0}'")]
39 InvalidInstanceId(&'a str, #[source] guid::ParseError),
40 #[error("invalid ntfs guid '{0}'")]
41 InvalidNtfsGuid(&'a str, #[source] guid::ParseError),
42 #[error("controller already exists")]
43 StorageControllerGuidAlreadyExists,
44 #[error("disk location exceeds limits {limits:?}")]
45 StorageLunLocationExceedsMaxLimits { limits: u32 },
46 #[error("exceeded 4 max SCSI controllers")]
47 StorageScsiControllerExceedsMaxLimits,
48 #[error("disk location is duplicated")]
49 StorageLunLocationDuplicated,
50 #[error("NVMe namespace id is invalid")]
51 StorageLunNvmeNsidInvalid,
52 #[error("NVMe namespace can't be a DVD drive")]
53 StorageLunNvmeDvdUnsupported,
54 #[error("build-to-build schema compat error: {0}")]
55 StorageSchemaVersionMismatch(&'a str),
56 #[error("invalid physical disk count: physical_disk_count = {physical_disk_count}")]
57 StorageInvalidPhysicalDiskCount { physical_disk_count: usize },
58 #[error("ide controller channel not provided")]
59 StorageIdeChannelNotProvided,
60 #[error("ide controller channel exceeds 2 max channels")]
61 StorageIdeChannelExceedsMaxLimits,
62 #[error("ide controller channel exceeds 1 max controller")]
63 StorageIdeControllerExceedsMaxLimits,
64 #[error("ide controller location exceeds 2 allowed drives per channel")]
65 StorageIdeLocationExceedsMaxLimits,
66 #[error("ide controller has invalid configuration")]
67 StorageIdeChannelInvalidConfiguration,
68 #[error("controller has unknown storage protocol")]
69 StorageProtocolUnknown,
70 #[error("invalid device type")]
71 StorageInvalidDeviceType,
72}
73
74impl Error<'_> {
75 fn code(&self) -> Vtl2SettingsErrorCode {
76 match self {
77 Error::UnsupportedSchemaVersion(_) => Vtl2SettingsErrorCode::UnsupportedSchemaVersion,
78 Error::UnsupportedSchemaNamespace(_) => {
79 Vtl2SettingsErrorCode::UnsupportedSchemaNamespace
80 }
81 Error::EmptyNamespaceChunk(_) => Vtl2SettingsErrorCode::EmptyNamespaceChunk,
82 Error::InvalidInstanceId { .. } => Vtl2SettingsErrorCode::InvalidInstanceId,
83 Error::InvalidNtfsGuid(_, _) => Vtl2SettingsErrorCode::StorageInvalidNtfsFormatGuid,
84 Error::StorageControllerGuidAlreadyExists => {
85 Vtl2SettingsErrorCode::StorageControllerGuidAlreadyExists
86 }
87 Error::StorageLunLocationExceedsMaxLimits { .. } => {
88 Vtl2SettingsErrorCode::StorageLunLocationExceedsMaxLimits
89 }
90 Error::StorageScsiControllerExceedsMaxLimits => {
91 Vtl2SettingsErrorCode::StorageScsiControllerExceedsMaxLimits
92 }
93 Error::StorageLunLocationDuplicated { .. } => {
94 Vtl2SettingsErrorCode::StorageLunLocationDuplicated
95 }
96 Error::StorageLunNvmeNsidInvalid { .. } => {
97 Vtl2SettingsErrorCode::StorageLunLocationExceedsMaxLimits
98 }
99 Error::StorageLunNvmeDvdUnsupported { .. } => {
100 Vtl2SettingsErrorCode::StorageUnsupportedDeviceType
101 }
102 Error::StorageSchemaVersionMismatch(_) => Vtl2SettingsErrorCode::JsonFormatError,
103 Error::StorageInvalidPhysicalDiskCount { .. } => {
104 Vtl2SettingsErrorCode::StorageInvalidPhysicalDiskCount
105 }
106 Error::StorageIdeChannelNotProvided => {
107 Vtl2SettingsErrorCode::StorageIdeChannelNotProvided
108 }
109 Error::StorageIdeChannelExceedsMaxLimits => {
110 Vtl2SettingsErrorCode::StorageIdeChannelExceedsMaxLimits
111 }
112 Error::StorageIdeControllerExceedsMaxLimits => {
113 Vtl2SettingsErrorCode::StorageIdeChannelExceedsMaxLimits
114 }
115 Error::StorageIdeLocationExceedsMaxLimits => {
116 Vtl2SettingsErrorCode::StorageIdeChannelExceedsMaxLimits
117 }
118 Error::StorageIdeChannelInvalidConfiguration => {
119 Vtl2SettingsErrorCode::StorageIdeChannelInvalidConfiguration
120 }
121 Error::StorageProtocolUnknown => Vtl2SettingsErrorCode::StorageInvalidControllerType,
122 Error::StorageInvalidDeviceType => Vtl2SettingsErrorCode::StorageUnsupportedDeviceType,
123 }
124 }
125}
126
127impl From<Error<'_>> for Vtl2SettingsErrorInfo {
128 #[track_caller]
129 fn from(e: Error<'_>) -> Vtl2SettingsErrorInfo {
130 let mut message = e.to_string();
133 let mut source = e.source();
134 while let Some(inner) = source {
135 write!(&mut message, ": {}", inner).unwrap();
136 source = inner.source();
137 }
138
139 Vtl2SettingsErrorInfo::new(e.code(), message)
140 }
141}
142
143pub(crate) fn validate_version(
144 version: i32,
145 errors: &mut ParseErrors<'_>,
146) -> Result<(), ParsingStopped> {
147 match vtl2_settings_base::Version::from_i32(version)
148 .unwrap_or(vtl2_settings_base::Version::Unknown)
149 {
150 vtl2_settings_base::Version::Unknown => {
151 errors.push(Error::UnsupportedSchemaVersion(version as u32));
152 }
153 vtl2_settings_base::Version::V1 => {}
154 }
155 Ok(())
156}
157
158fn parse_instance_id(instance_id: &str) -> Result<Guid, Error<'_>> {
159 instance_id
160 .parse()
161 .map_err(|err| Error::InvalidInstanceId(instance_id, err))
162}
163
164fn parse_ntfs_guid(ntfs_guid: Option<&str>) -> Result<Option<Guid>, Error<'_>> {
165 ntfs_guid
166 .map(|guid| {
167 guid.parse()
168 .map_err(|err| Error::InvalidNtfsGuid(guid, err))
169 })
170 .transpose()
171}
172
173fn check_dups(errors: &mut ParseErrors<'_>, iter: impl IntoIterator<Item = u32>) {
174 let mut v: Vec<_> = iter.into_iter().collect();
175 v.sort();
176 for (a, b) in v.iter().zip(v.iter().skip(1)) {
177 if a == b {
178 errors.push(Error::StorageLunLocationDuplicated);
179 }
180 }
181}
182
183impl ParseSchema<crate::DeviceType> for DeviceType {
184 fn parse_schema(
185 &self,
186 _errors: &mut ParseErrors<'_>,
187 ) -> Result<crate::DeviceType, ParsingStopped> {
188 match self {
189 DeviceType::Nvme => Ok(crate::DeviceType::NVMe),
190 DeviceType::Vscsi => Ok(crate::DeviceType::VScsi),
191 DeviceType::Unknown => Err(Error::StorageInvalidDeviceType.into()),
192 }
193 }
194}
195
196impl ParseSchema<crate::PhysicalDevice> for PhysicalDevice {
197 fn parse_schema(
198 &self,
199 errors: &mut ParseErrors<'_>,
200 ) -> Result<crate::PhysicalDevice, ParsingStopped> {
201 Ok(crate::PhysicalDevice {
202 device_type: self.device_type().parse(errors)?,
203 vmbus_instance_id: parse_instance_id(&self.device_path)?,
204 sub_device_path: self.sub_device_path,
205 })
206 }
207}
208
209impl ParseSchema<crate::DiskParameters> for Lun {
210 fn parse_schema(
211 &self,
212 _errors: &mut ParseErrors<'_>,
213 ) -> Result<crate::DiskParameters, ParsingStopped> {
214 Ok(crate::DiskParameters {
215 device_id: self.device_id.clone(),
216 vendor_id: self.vendor_id.clone(),
217 product_id: self.product_id.clone(),
218 product_revision_level: self.product_revision_level.clone(),
219 serial_number: self.serial_number.clone(),
220 model_number: self.model_number.clone(),
221 medium_rotation_rate: self.medium_rotation_rate.map(|x| x as u16).unwrap_or(0),
222 physical_sector_size: self.physical_sector_size,
223 fua: self.fua,
224 write_cache: self.write_cache,
225 scsi_disk_size_in_bytes: self.scsi_disk_size_in_bytes,
226 odx: self.odx,
227 unmap: self.disable_thin_provisioning.map(|disable| !disable),
228 max_transfer_length: self.max_transfer_length.map(|x| x as usize),
229 })
230 }
231}
232
233impl ParseSchema<crate::PhysicalDevices> for Lun {
234 fn parse_schema(
235 &self,
236 errors: &mut ParseErrors<'_>,
237 ) -> Result<crate::PhysicalDevices, ParsingStopped> {
238 #[expect(deprecated)]
239 if (self.is_dvd || self.physical_devices.is_some())
240 && (self.device_type.is_some()
241 || self.device_path.is_some()
242 || self.sub_device_path.is_some())
243 {
244 errors.push(Error::StorageSchemaVersionMismatch(
245 "cannot mix old/new physical device schema declarations",
246 ));
247 }
248
249 let v = if let Some(physical_devices) = &self.physical_devices {
250 let invalid_disk_count = || Error::StorageInvalidPhysicalDiskCount {
251 physical_disk_count: physical_devices.device.is_some() as usize
252 + physical_devices.devices.len(),
253 };
254
255 match physical_devices.r#type() {
256 physical_devices::BackingType::Single => {
257 let device = physical_devices
258 .device
259 .as_ref()
260 .ok_or_else(invalid_disk_count)?;
261 if !physical_devices.devices.is_empty() {
262 errors.push(invalid_disk_count());
263 }
264 crate::PhysicalDevices::Single {
265 device: device.parse(errors)?,
266 }
267 }
268 physical_devices::BackingType::Striped => {
269 if physical_devices.devices.len() < 2 || physical_devices.device.is_some() {
270 errors.push(invalid_disk_count());
271 }
272 crate::PhysicalDevices::Striped {
273 devices: physical_devices
274 .devices
275 .iter()
276 .flat_map(|v| v.parse(errors).collect_error(errors))
277 .collect(),
278 chunk_size_in_kb: self.chunk_size_in_kb,
279 }
280 }
281 physical_devices::BackingType::Unknown => {
282 return Err(Error::StorageInvalidDeviceType.into());
283 }
284 }
285 } else if self.is_dvd {
286 crate::PhysicalDevices::EmptyDrive
287 } else {
288 #[expect(deprecated)]
290 let (device_path, sub_device_path) =
291 self.device_path.as_ref().zip(self.sub_device_path).ok_or(
292 Error::StorageSchemaVersionMismatch("could not find any physical devices"),
293 )?;
294
295 let device_type = self.device_type().parse(errors)?;
296 let vmbus_instance_id = parse_instance_id(device_path)?;
297 crate::PhysicalDevices::Single {
298 device: crate::PhysicalDevice {
299 device_type,
300 vmbus_instance_id,
301 sub_device_path,
302 },
303 }
304 };
305 Ok(v)
306 }
307}
308
309impl ParseSchema<crate::IdeDisk> for Lun {
310 fn parse_schema(&self, errors: &mut ParseErrors<'_>) -> Result<crate::IdeDisk, ParsingStopped> {
311 let channel = self.channel.ok_or(Error::StorageIdeChannelNotProvided)?;
312 errors.with_context(ErrorContext::Ide(channel, self.location), |errors| {
313 if channel >= crate::IDE_NUM_CHANNELS.into() {
314 return Err(Error::StorageIdeChannelExceedsMaxLimits.into());
315 }
316
317 if self.location >= crate::IDE_MAX_DRIVES_PER_CHANNEL.into() {
318 return Err(Error::StorageIdeLocationExceedsMaxLimits.into());
319 }
320
321 Ok(crate::IdeDisk {
322 channel: channel as u8,
323 location: self.location as u8,
324 disk_params: self.parse(errors)?,
325 physical_devices: self.parse(errors)?,
326 ntfs_guid: parse_ntfs_guid(self.ntfs_guid.as_deref())?,
327 is_dvd: self.is_dvd,
328 })
329 })
330 }
331}
332
333impl ParseSchema<crate::ScsiDisk> for Lun {
334 fn parse_schema(
335 &self,
336 errors: &mut ParseErrors<'_>,
337 ) -> Result<crate::ScsiDisk, ParsingStopped> {
338 errors.with_context(ErrorContext::Scsi(self.location), |errors| {
339 if self.location as usize >= crate::SCSI_LUN_NUM {
340 errors.push(Error::StorageLunLocationExceedsMaxLimits {
341 limits: crate::SCSI_LUN_NUM as u32,
342 });
343 }
344 Ok(crate::ScsiDisk {
345 location: self.location as u8,
346 disk_params: self.parse(errors)?,
347 physical_devices: self.parse(errors)?,
348 ntfs_guid: parse_ntfs_guid(self.ntfs_guid.as_deref())?,
349 is_dvd: self.is_dvd,
350 })
351 })
352 }
353}
354
355impl ParseSchema<crate::NvmeNamespace> for Lun {
356 fn parse_schema(
357 &self,
358 errors: &mut ParseErrors<'_>,
359 ) -> Result<crate::NvmeNamespace, ParsingStopped> {
360 errors.with_context(ErrorContext::Nvme(self.location), |errors| {
361 if self.location == 0 || self.location == !0 {
362 errors.push(Error::StorageLunNvmeNsidInvalid);
363 }
364 if self.is_dvd {
365 errors.push(Error::StorageLunNvmeDvdUnsupported);
366 }
367
368 Ok(crate::NvmeNamespace {
369 nsid: self.location,
370 disk_params: self.parse(errors)?,
371 physical_devices: self.parse(errors)?,
372 })
373 })
374 }
375}
376
377impl ParseSchema<crate::IdeController> for StorageController {
378 fn parse_schema(
379 &self,
380 errors: &mut ParseErrors<'_>,
381 ) -> Result<crate::IdeController, ParsingStopped> {
382 let instance_id = parse_instance_id(&self.instance_id)?;
383
384 errors.with_context(ErrorContext::InstanceId(instance_id), |errors| {
385 let mut disks = self
386 .luns
387 .iter()
388 .flat_map(|lun| lun.parse(errors).collect_error(errors))
389 .collect::<Vec<crate::IdeDisk>>();
390
391 disks.sort_by(|a, b| (a.location).cmp(&b.location));
392
393 for (a, b) in disks.iter().zip(disks.iter().skip(1)) {
394 if a.channel == b.channel && a.location == b.location {
395 errors.push_with_context(
397 ErrorContext::Ide(a.channel.into(), a.location.into()),
398 Error::StorageIdeChannelInvalidConfiguration,
399 );
400 }
401 }
402
403 Ok(crate::IdeController {
404 instance_id,
405 disks,
406 io_queue_depth: self.io_queue_depth,
407 })
408 })
409 }
410}
411
412impl ParseSchema<crate::ScsiController> for StorageController {
413 fn parse_schema(
414 &self,
415 errors: &mut ParseErrors<'_>,
416 ) -> Result<crate::ScsiController, ParsingStopped> {
417 let instance_id = parse_instance_id(&self.instance_id)?;
418
419 errors.with_context(ErrorContext::InstanceId(instance_id), |errors| {
420 let disks = self
421 .luns
422 .iter()
423 .flat_map(|lun| lun.parse(errors).collect_error(errors))
424 .collect::<Vec<crate::ScsiDisk>>();
425
426 check_dups(errors, disks.iter().map(|disk| disk.location.into()));
427
428 Ok(crate::ScsiController {
429 instance_id,
430 disks,
431 io_queue_depth: self.io_queue_depth,
432 })
433 })
434 }
435}
436
437impl ParseSchema<crate::NvmeController> for StorageController {
438 fn parse_schema(
439 &self,
440 errors: &mut ParseErrors<'_>,
441 ) -> Result<crate::NvmeController, ParsingStopped> {
442 assert!(matches!(self.protocol(), StorageProtocol::Nvme));
443
444 let instance_id = parse_instance_id(&self.instance_id)?;
445
446 errors.with_context(ErrorContext::InstanceId(instance_id), |errors| {
447 let namespaces = self
448 .luns
449 .iter()
450 .flat_map(|lun| lun.parse(errors).collect_error(errors))
451 .collect::<Vec<crate::NvmeNamespace>>();
452
453 check_dups(errors, namespaces.iter().map(|ns| ns.nsid));
454
455 Ok(crate::NvmeController {
456 instance_id,
457 namespaces,
458 })
459 })
460 }
461}
462
463impl ParseSchema<crate::NicDevice> for NicDeviceLegacy {
464 fn parse_schema(
465 &self,
466 _errors: &mut ParseErrors<'_>,
467 ) -> Result<crate::NicDevice, ParsingStopped> {
468 let instance_id = parse_instance_id(&self.instance_id)?;
469
470 let mut subordinate_instance_id = self
471 .subordinate_instance_id
472 .as_ref()
473 .map(|id| parse_instance_id(id))
474 .transpose()?;
475
476 if subordinate_instance_id == Some(Guid::ZERO) {
477 subordinate_instance_id = None;
478 }
479
480 Ok(crate::NicDevice {
481 instance_id,
482 subordinate_instance_id,
483 max_sub_channels: self.max_sub_channels.map(|val| val as u16),
484 })
485 }
486}
487
488impl ParseSchema<crate::NicDevice> for NicDevice {
489 fn parse_schema(
490 &self,
491 _errors: &mut ParseErrors<'_>,
492 ) -> Result<crate::NicDevice, ParsingStopped> {
493 let instance_id = parse_instance_id(&self.instance_id)?;
494
495 Ok(crate::NicDevice {
496 instance_id,
497 subordinate_instance_id: None,
498 max_sub_channels: self.max_sub_channels.map(|val| val as u16),
499 })
500 }
501}
502
503impl ParseSchema<crate::NicDevice> for NicAcceleration {
504 fn parse_schema(
505 &self,
506 _errors: &mut ParseErrors<'_>,
507 ) -> Result<crate::NicDevice, ParsingStopped> {
508 let instance_id = parse_instance_id(&self.instance_id)?;
509
510 let subordinate_instance_id = parse_instance_id(&self.subordinate_instance_id)?;
511 let subordinate_instance_id =
512 (subordinate_instance_id != Guid::ZERO).then_some(subordinate_instance_id);
513
514 Ok(crate::NicDevice {
515 instance_id,
516 subordinate_instance_id,
517 max_sub_channels: None,
518 })
519 }
520}
521
522impl ParseSchema<crate::Vtl2SettingsFixed> for Vtl2SettingsFixed {
523 fn parse_schema(
524 &self,
525 _errors: &mut ParseErrors<'_>,
526 ) -> Result<crate::Vtl2SettingsFixed, ParsingStopped> {
527 Ok(crate::Vtl2SettingsFixed {
528 scsi_sub_channels: self.scsi_sub_channels.map_or(0, |x| x as u16),
529 io_ring_size: self.io_ring_size.unwrap_or(256),
530 max_bounce_buffer_pages: self.max_bounce_buffer_pages,
531 })
532 }
533}
534
535impl ParseSchema<crate::Vtl2SettingsDynamic> for Vtl2SettingsDynamic {
536 fn parse_schema(
537 &self,
538 errors: &mut ParseErrors<'_>,
539 ) -> Result<crate::Vtl2SettingsDynamic, ParsingStopped> {
540 let mut ide_controller = None;
541 let mut scsi_controllers = Vec::new();
542 let mut nvme_controllers = Vec::new();
543
544 for controller in &self.storage_controllers {
545 match controller.protocol() {
546 StorageProtocol::Ide => {
547 if let Some(c) = controller
548 .parse::<crate::IdeController>(errors)
549 .collect_error(errors)
550 {
551 if ide_controller.is_some() {
552 errors.push_with_context(
553 ErrorContext::InstanceId(c.instance_id),
554 Error::StorageIdeControllerExceedsMaxLimits,
555 );
556 }
557 ide_controller = Some(c);
558 }
559 }
560 StorageProtocol::Scsi => {
561 if let Some(c) = controller
562 .parse::<crate::ScsiController>(errors)
563 .collect_error(errors)
564 {
565 if scsi_controllers.len() >= crate::SCSI_CONTROLLER_NUM {
566 errors.push_with_context(
567 ErrorContext::InstanceId(c.instance_id),
568 Error::StorageScsiControllerExceedsMaxLimits,
569 );
570 }
571 scsi_controllers.push(c);
572 }
573 }
574 StorageProtocol::Nvme => {
575 if let Some(c) = controller
576 .parse::<crate::NvmeController>(errors)
577 .collect_error(errors)
578 {
579 nvme_controllers.push(c);
580 }
581 }
582 StorageProtocol::Unknown => {
583 let instance_id = parse_instance_id(&controller.instance_id)?;
584 errors.push_with_context(
585 ErrorContext::InstanceId(instance_id),
586 Error::StorageProtocolUnknown,
587 );
588 }
589 }
590 }
591
592 let mut instance_ids = scsi_controllers
593 .iter()
594 .map(|c| c.instance_id)
595 .chain(nvme_controllers.iter().map(|c| c.instance_id))
596 .chain(ide_controller.iter().map(|c| c.instance_id))
597 .collect::<Vec<_>>();
598
599 instance_ids.sort();
600 for (a, b) in instance_ids.iter().zip(instance_ids.iter().skip(1)) {
601 if a == b {
602 errors.push_with_context(
603 ErrorContext::InstanceId(*a),
604 Error::StorageControllerGuidAlreadyExists,
605 );
606 }
607 }
608
609 let nic_devices = self
610 .nic_devices
611 .iter()
612 .flat_map(|nic| nic.parse(errors).collect_error(errors))
613 .collect();
614
615 Ok(crate::Vtl2SettingsDynamic {
616 ide_controller,
617 scsi_controllers,
618 nvme_controllers,
619 nic_devices,
620 })
621 }
622}