|
| 1 | +use crate::structures::common; |
| 2 | +use crate::structures::common::StructureError; |
| 3 | +use std::collections::HashMap; |
| 4 | + |
| 5 | +#[derive(Debug, Default, Clone)] |
| 6 | +pub struct QcowHeader { |
| 7 | + pub version: u8, |
| 8 | + pub storage_media_size: u64, |
| 9 | + pub cluster_block_bits: u8, |
| 10 | + pub encryption_method: String, |
| 11 | +} |
| 12 | + |
| 13 | +pub fn parse_qcow_header(qcow_data: &[u8]) -> Result<QcowHeader, StructureError> { |
| 14 | + let qcow_basehdr_structure = vec![("magic", "u32"), ("version", "u32")]; |
| 15 | + let qcow_header_v1_structure = vec![ |
| 16 | + ("backing_filename_offset", "u64"), |
| 17 | + ("backing_filename_size", "u32"), |
| 18 | + ("modification_timestamp", "u32"), |
| 19 | + ("storage_media_size", "u64"), |
| 20 | + ("cluster_block_bits", "u8"), |
| 21 | + ("level2_table_bits", "u8"), |
| 22 | + ("reserved1", "u16"), |
| 23 | + ("encryption_method", "u32"), |
| 24 | + ("level1_table_offset", "u64"), |
| 25 | + ]; |
| 26 | + let qcow_header_v2_structure = vec![ |
| 27 | + ("backing_filename_offset", "u64"), |
| 28 | + ("backing_filename_size", "u32"), |
| 29 | + ("cluster_block_bits", "u32"), |
| 30 | + ("storage_media_size", "u64"), |
| 31 | + ("encryption_method", "u32"), |
| 32 | + ("level1_table_refs", "u32"), |
| 33 | + ("level1_table_offset", "u64"), |
| 34 | + ("refcount_table_offset", "u64"), |
| 35 | + ("refcount_table_clusters", "u32"), |
| 36 | + ("snapshot_count", "u32"), |
| 37 | + ("snapshot_offset", "u64"), |
| 38 | + ]; |
| 39 | + let qcow_header_v3_structure = vec![ |
| 40 | + ("backing_filename_offset", "u64"), |
| 41 | + ("backing_filename_size", "u32"), |
| 42 | + ("cluster_block_bits", "u32"), |
| 43 | + ("storage_media_size", "u64"), |
| 44 | + ("encryption_method", "u32"), |
| 45 | + ("level1_table_refs", "u32"), |
| 46 | + ("level1_table_offset", "u64"), |
| 47 | + ("refcount_table_offset", "u64"), |
| 48 | + ("refcount_table_clusters", "u32"), |
| 49 | + ("snapshot_count", "u32"), |
| 50 | + ("snapshot_offset", "u64"), |
| 51 | + ("incompatible_feature_flags", "u64"), |
| 52 | + ("compatible_feature_flags", "u64"), |
| 53 | + ("autoclear_feature_flags", "u64"), |
| 54 | + ("refcount_order", "u32"), |
| 55 | + ("file_hdr_size", "u32"), // 104 or 112 |
| 56 | + ]; |
| 57 | + |
| 58 | + let encryption_methods = HashMap::from([(0, "None"), (1, "AES128-CBC"), (2, "LUKS")]); |
| 59 | + |
| 60 | + if let Ok(qcow_base_header) = common::parse(qcow_data, &qcow_basehdr_structure, "big") { |
| 61 | + let qcow_version = qcow_base_header["version"]; |
| 62 | + let qcow_data = qcow_data.get(8..).ok_or(StructureError)?; |
| 63 | + let qcow_header = match qcow_version { |
| 64 | + 1 => common::parse(qcow_data, &qcow_header_v1_structure, "big"), |
| 65 | + 2 => common::parse(qcow_data, &qcow_header_v2_structure, "big"), |
| 66 | + 3 => common::parse(qcow_data, &qcow_header_v3_structure, "big"), |
| 67 | + _ => Err(StructureError), |
| 68 | + }?; |
| 69 | + |
| 70 | + let encryption_method = encryption_methods |
| 71 | + .get(&qcow_header["encryption_method"]) |
| 72 | + .ok_or(StructureError)? |
| 73 | + .to_string(); |
| 74 | + |
| 75 | + let cluster_block_bits = *qcow_header |
| 76 | + .get("cluster_block_bits") |
| 77 | + .filter(|&&bits| (9..=21).contains(&bits)) |
| 78 | + .ok_or(StructureError)?; |
| 79 | + |
| 80 | + // sanity check: existing offsets need to be aligned to cluster boundary |
| 81 | + if let Some(offset) = qcow_header.get("level1_table_offset") { |
| 82 | + if offset % (1 << cluster_block_bits) != 0 { |
| 83 | + return Err(StructureError); |
| 84 | + } |
| 85 | + } |
| 86 | + if let Some(offset) = qcow_header.get("refcount_table_offset") { |
| 87 | + if offset % (1 << cluster_block_bits) != 0 { |
| 88 | + return Err(StructureError); |
| 89 | + } |
| 90 | + } |
| 91 | + if let Some(offset) = qcow_header.get("snapshot_offset") { |
| 92 | + if offset % (1 << cluster_block_bits) != 0 { |
| 93 | + return Err(StructureError); |
| 94 | + } |
| 95 | + } |
| 96 | + |
| 97 | + return Ok(QcowHeader { |
| 98 | + version: qcow_version as u8, |
| 99 | + storage_media_size: qcow_header["storage_media_size"] as u64, |
| 100 | + cluster_block_bits: cluster_block_bits as u8, |
| 101 | + encryption_method, |
| 102 | + }); |
| 103 | + } |
| 104 | + |
| 105 | + Err(StructureError) |
| 106 | +} |
0 commit comments