mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
block: Detect and open flat VMDK images
Adds support to detect and open flat vmdk images. In particular, it updates the `detect_image_type` method to account for unaligned & small sized descriptor file (which describes the VMDK disk) reads using AlignedFile::read_at instead of read_file_at to loop over for small reads. Signed-off-by: Sumedh Alok Sharma <sumsharma@microsoft.com>
This commit is contained in:
committed by
Wei Liu
parent
e6fd5fefc4
commit
82b65c3352
@@ -24,6 +24,7 @@ use crate::formats::qcow::QcowDisk;
|
||||
use crate::formats::raw::{RawBackend, RawDisk};
|
||||
use crate::formats::vhd::VhdDisk;
|
||||
use crate::formats::vhdx::VhdxDisk;
|
||||
use crate::formats::vmdk::VmdkDisk;
|
||||
use crate::{
|
||||
ImageType, block_aio_is_supported, detect_image_type, open_disk_image, preallocate_disk,
|
||||
};
|
||||
@@ -98,6 +99,7 @@ pub fn open_disk(options: &DiskOpenOptions<'_>) -> BlockResult<OpenedDisk> {
|
||||
ImageType::Raw => open_raw(file, options)?,
|
||||
ImageType::Qcow2 => open_qcow2(file, options)?,
|
||||
ImageType::Vhdx => open_vhdx(file, options)?,
|
||||
ImageType::FlatVmdk => open_flat_vmdk(file, options)?,
|
||||
ImageType::Unknown => {
|
||||
return Err(
|
||||
BlockError::from_kind(BlockErrorKind::UnsupportedFeature).with_path(options.path)
|
||||
@@ -215,6 +217,16 @@ fn open_qcow2(
|
||||
))
|
||||
}
|
||||
|
||||
fn open_flat_vmdk(
|
||||
file: fs::File,
|
||||
options: &DiskOpenOptions<'_>,
|
||||
) -> BlockResult<Box<dyn AsyncFullDiskFile>> {
|
||||
info!("Opening VMDK disk file with synchronous backend");
|
||||
Ok(Box::new(
|
||||
VmdkDisk::new(file, options.path, options.direct).map_err(|e| e.with_path(options.path))?,
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod unit_tests {
|
||||
use std::path::Path;
|
||||
|
||||
@@ -119,7 +119,11 @@ impl VhdFooter {
|
||||
|
||||
/// Determine image type through file parsing.
|
||||
pub fn is_fixed_vhd(f: &mut File) -> io::Result<bool> {
|
||||
let footer = VhdFooter::new(f)?;
|
||||
let footer = match VhdFooter::new(f) {
|
||||
Ok(footer) => footer,
|
||||
Err(e) if e.kind() == io::ErrorKind::InvalidInput => return Ok(false),
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
// "conectix" => 0x636f6e6563746978
|
||||
Ok(footer.cookie() == 0x636f6e6563746978
|
||||
@@ -229,4 +233,11 @@ mod unit_tests {
|
||||
assert!(!(is_fixed_vhd(&mut file).unwrap()));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_fixed_vhd_short_file_is_not_vhd() {
|
||||
let mut file: File = TempFile::new().unwrap().into_file();
|
||||
file.write_all(b"# Disk DescriptorFile\n").unwrap();
|
||||
assert!(!is_fixed_vhd(&mut file).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -484,6 +484,7 @@ pub fn preallocate_disk<P: AsRef<Path>>(file: &File, path: P) {
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq, Default)]
|
||||
pub enum ImageType {
|
||||
FlatVmdk,
|
||||
FixedVhd,
|
||||
Qcow2,
|
||||
Raw,
|
||||
@@ -495,6 +496,7 @@ pub enum ImageType {
|
||||
impl fmt::Display for ImageType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
ImageType::FlatVmdk => write!(f, "vmdk"),
|
||||
ImageType::FixedVhd => write!(f, "vhd"),
|
||||
ImageType::Qcow2 => write!(f, "qcow2"),
|
||||
ImageType::Raw => write!(f, "raw"),
|
||||
@@ -513,6 +515,7 @@ impl FromStr for ImageType {
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s.to_lowercase().as_str() {
|
||||
"vmdk" => Ok(ImageType::FlatVmdk),
|
||||
"vhd" => Ok(ImageType::FixedVhd),
|
||||
"qcow2" => Ok(ImageType::Qcow2),
|
||||
"raw" => Ok(ImageType::Raw),
|
||||
@@ -538,10 +541,23 @@ pub fn open_disk_image(path: &Path, options: &OpenOptions) -> BlockResult<File>
|
||||
/// Determine image type through file parsing.
|
||||
pub fn detect_image_type(f: &mut File) -> BlockResult<ImageType> {
|
||||
let aligned = AlignedFile::new(f.try_clone()?, true);
|
||||
// A VMDK descriptor file is small few hundred bytes of text. Read
|
||||
// best-effort so a short file yields a zero-padded partial block instead
|
||||
// of an UnexpectedEof, and let the per-format probes below decide.
|
||||
let mut block = vec![0u8; aligned.alignment()];
|
||||
aligned
|
||||
.read_exact_at(&mut block, 0)
|
||||
.map_err(|e| BlockError::new(BlockErrorKind::Io, e).with_op(ErrorOp::DetectImageType))?;
|
||||
let mut filled = 0;
|
||||
while filled < block.len() {
|
||||
match aligned.read_at(&mut block[filled..], filled as u64) {
|
||||
Ok(0) => break,
|
||||
Ok(n) => filled += n,
|
||||
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
|
||||
Err(e) => {
|
||||
return Err(
|
||||
BlockError::new(BlockErrorKind::Io, e).with_op(ErrorOp::DetectImageType)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check 4 first bytes to get the header value and determine the image type
|
||||
let image_type = if u32::from_be_bytes(block[0..4].try_into().unwrap()) == QCOW_MAGIC {
|
||||
@@ -552,6 +568,11 @@ pub fn detect_image_type(f: &mut File) -> BlockResult<ImageType> {
|
||||
ImageType::FixedVhd
|
||||
} else if u64::from_le_bytes(block[0..8].try_into().unwrap()) == VHDX_SIGN {
|
||||
ImageType::Vhdx
|
||||
} else if formats::vmdk::has_descriptor_header(&block)
|
||||
&& formats::vmdk::is_flat_vmdk(f)
|
||||
.map_err(|e| BlockError::new(BlockErrorKind::Io, e).with_op(ErrorOp::DetectImageType))?
|
||||
{
|
||||
ImageType::FlatVmdk
|
||||
} else {
|
||||
ImageType::Raw
|
||||
};
|
||||
@@ -682,6 +703,18 @@ mod unit_tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn detect_short_file_is_not_eof_error() {
|
||||
let tmp = TempFile::new().unwrap();
|
||||
let mut f = tmp.into_file();
|
||||
f.write_all(b"not-a-disk-magic-just-some-short-text\n")
|
||||
.unwrap();
|
||||
f.sync_all().unwrap();
|
||||
|
||||
let image_type = detect_image_type(&mut f).unwrap();
|
||||
assert_eq!(image_type, ImageType::Raw);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_probe_regular_file_returns_valid_alignment() {
|
||||
let temp_file = TempFile::new().unwrap();
|
||||
|
||||
@@ -1597,6 +1597,7 @@ mod common_parallel {
|
||||
ImageType::Qcow2 => ("qcow2", &[]),
|
||||
ImageType::FixedVhd => ("vpc", &["-o", "subformat=fixed"]),
|
||||
ImageType::Vhdx => ("vhdx", &[]),
|
||||
ImageType::FlatVmdk => panic!("unsupported image_type {image_type}"),
|
||||
ImageType::Unknown => panic!("unsupported image_type {image_type}"),
|
||||
};
|
||||
let image_type_str = image_type.to_string();
|
||||
|
||||
Reference in New Issue
Block a user