qcow: Turn QCOW support into a feature

This change allows to turn off support for QCOW. This is useful for
projects that do not need/want it.

A follow-up change (if this one is merged) will do the same for Android
sparse disk images.

BUG=none
TEST=./tools/presubmit

Change-Id: I69083c4c2e21d504db89eff47f4c86c6f61d67e9
Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/3864926
Tested-by: Christian Blichmann <cblichmann@google.com>
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
Commit-Queue: Christian Blichmann <cblichmann@google.com>
This commit is contained in:
Christian Blichmann 2022-08-31 15:30:11 +02:00 committed by crosvm LUCI
parent 9dc9210d3d
commit 8847de2725
7 changed files with 56 additions and 28 deletions

View file

@ -125,7 +125,7 @@ balloon = ["devices/balloon", "vm_control/balloon"]
chromeos = ["base/chromeos", "audio_cras", "devices/chromeos"]
composite-disk = ["protos/composite-disk", "protobuf", "disk/composite-disk"]
crash-report = []
default = ["audio", "balloon", "gpu", "usb"]
default = ["audio", "balloon", "gpu", "qcow", "usb"]
default-no-sandbox = []
direct = ["balloon", "devices/direct", "arch/direct", "x86_64/direct"]
ffmpeg = ["devices/ffmpeg"]
@ -147,6 +147,7 @@ linux-aarch64 = ["all-linux"]
plugin = ["protos/plugin", "crosvm_plugin", "kvm", "kvm_sys", "protobuf"]
plugin-render-server = []
power-monitor-powerd = ["arch/power-monitor-powerd"]
qcow = ["disk/qcow"]
slirp = ["devices/slirp"]
tpm = ["devices/tpm"]
usb = ["devices/usb"]

View file

@ -9,6 +9,7 @@ path = "src/disk.rs"
[features]
composite-disk = ["crc32fast", "protos", "protobuf", "uuid"]
qcow = []
[dependencies]
async-trait = "*"

View file

@ -32,8 +32,11 @@ use thiserror::Error as ThisError;
mod asynchronous;
pub(crate) use asynchronous::AsyncDiskFileWrapper;
#[cfg(feature = "qcow")]
mod qcow;
#[cfg(feature = "qcow")]
pub use qcow::QcowFile;
#[cfg(feature = "qcow")]
pub use qcow::QCOW_MAGIC;
mod sys;
@ -92,6 +95,7 @@ pub enum Error {
MaxNestingDepthExceeded,
#[error("failure to punch hole: {0}")]
PunchHole(io::Error),
#[cfg(feature = "qcow")]
#[error("failure in qcow: {0}")]
QcowError(qcow::Error),
#[error("failed to read data: {0}")]
@ -237,9 +241,11 @@ pub fn detect_image_type(file: &File) -> Result<ImageType> {
}
if let Some(magic4) = magic.data.get(0..4) {
#[cfg(feature = "qcow")]
if magic4 == QCOW_MAGIC.to_be_bytes() {
return Ok(ImageType::Qcow2);
} else if magic4 == SPARSE_HEADER_MAGIC.to_le_bytes() {
}
if magic4 == SPARSE_HEADER_MAGIC.to_le_bytes() {
return Ok(ImageType::AndroidSparse);
}
}
@ -271,14 +277,18 @@ pub fn create_async_disk_file(raw_image: File) -> Result<Box<dyn ToAsyncDisk>> {
pub fn create_disk_file(
raw_image: File,
is_sparse_file: bool,
mut max_nesting_depth: u32,
// max_nesting_depth is only used if the composite-disk or qcow features are enabled.
#[allow(unused_variables)] mut max_nesting_depth: u32,
// image_path is only used if the composite-disk feature is enabled.
#[allow(unused_variables)] image_path: &Path,
) -> Result<Box<dyn DiskFile>> {
if max_nesting_depth == 0 {
return Err(Error::MaxNestingDepthExceeded);
}
max_nesting_depth -= 1;
#[allow(unused_assignments)]
{
max_nesting_depth -= 1;
}
let image_type = detect_image_type(&raw_image)?;
Ok(match image_type {
@ -286,6 +296,7 @@ pub fn create_disk_file(
sys::apply_raw_disk_file_options(&raw_image, is_sparse_file)?;
Box::new(raw_image) as Box<dyn DiskFile>
}
#[cfg(feature = "qcow")]
ImageType::Qcow2 => {
Box::new(QcowFile::from(raw_image, max_nesting_depth).map_err(Error::QcowError)?)
as Box<dyn DiskFile>
@ -303,12 +314,12 @@ pub fn create_disk_file(
.map_err(Error::CreateCompositeDisk)?,
) as Box<dyn DiskFile>
}
#[cfg(not(feature = "composite-disk"))]
ImageType::CompositeDisk => return Err(Error::UnknownType),
ImageType::AndroidSparse => {
Box::new(AndroidSparse::from_file(raw_image).map_err(Error::CreateAndroidSparseDisk)?)
as Box<dyn DiskFile>
}
#[allow(unreachable_patterns)]
_ => return Err(Error::UnknownType),
})
}

View file

@ -67,6 +67,7 @@ mod tests {
}
#[test]
#[cfg(feature = "qcow")]
fn detect_image_type_qcow2() {
let mut t = tempfile::tempfile().unwrap();
// Write the qcow2 magic signature. The rest of the header is not filled in, so if

View file

@ -13,38 +13,45 @@ use base::flock;
use base::FlockOperation;
use disk::DiskFile;
use disk::ImageType;
#[cfg(feature = "qcow")]
use disk::QcowFile;
use libc::EINVAL;
use libc::EIO;
use libc::ENOSYS;
#[no_mangle]
#[allow(unused_variables)] // If the qcow feature is disabled, this function becomes an empty stub.
pub unsafe extern "C" fn create_qcow_with_size(path: *const c_char, virtual_size: u64) -> c_int {
// NULL pointers are checked, but this will access any other invalid pointer passed from C
// code. It's the caller's responsibility to pass a valid pointer.
if path.is_null() {
return -EINVAL;
}
let c_str = CStr::from_ptr(path);
let file_path = match c_str.to_str() {
Ok(s) => s,
Err(_) => return -EINVAL,
};
let file = match OpenOptions::new()
.create(true)
.read(true)
.write(true)
.open(file_path)
#[cfg(feature = "qcow")]
{
Ok(f) => f,
Err(_) => return -1,
};
// NULL pointers are checked, but this will access any other invalid pointer passed from C
// code. It's the caller's responsibility to pass a valid pointer.
if path.is_null() {
return -EINVAL;
}
let c_str = CStr::from_ptr(path);
let file_path = match c_str.to_str() {
Ok(s) => s,
Err(_) => return -EINVAL,
};
match QcowFile::new(file, virtual_size) {
Ok(_) => 0,
Err(_) => -1,
let file = match OpenOptions::new()
.create(true)
.read(true)
.write(true)
.open(file_path)
{
Ok(f) => f,
Err(_) => return -1,
};
match QcowFile::new(file, virtual_size) {
Ok(_) => 0,
Err(_) => -1,
}
}
#[cfg(not(feature = "qcow"))]
-ENOSYS // Not implemented
}
#[no_mangle]
@ -77,6 +84,7 @@ pub unsafe extern "C" fn expand_disk_image(path: *const c_char, virtual_size: u6
let disk_image: Box<dyn DiskFile> = match image_type {
ImageType::Raw => Box::new(raw_image),
#[cfg(feature = "qcow")]
ImageType::Qcow2 => match QcowFile::from(raw_image, disk::MAX_NESTING_DEPTH) {
Ok(f) => Box::new(f),
Err(_) => return -EINVAL,

View file

@ -121,6 +121,7 @@ pub enum CrossPlatformCommands {
Battery(BatteryCommand),
#[cfg(feature = "composite-disk")]
CreateComposite(CreateCompositeCommand),
#[cfg(feature = "qcow")]
CreateQcow2(CreateQcow2Command),
Device(DeviceCommand),
Disk(DiskCommand),
@ -198,6 +199,7 @@ pub struct CreateCompositeCommand {
pub partitions: Vec<String>,
}
#[cfg(feature = "qcow")]
#[derive(FromArgs)]
#[argh(subcommand, name = "create_qcow2")]
/// Create Qcow2 image given path and size

View file

@ -4,6 +4,7 @@
//! Runs a virtual machine
#[cfg(any(feature = "composite-disk", feature = "qcow"))]
use std::fs::OpenOptions;
use std::path::Path;
@ -34,6 +35,7 @@ use disk::create_zero_filler;
use disk::ImagePartitionType;
#[cfg(feature = "composite-disk")]
use disk::PartitionInfo;
#[cfg(feature = "qcow")]
use disk::QcowFile;
mod sys;
use crosvm::cmdline::Command;
@ -338,6 +340,7 @@ fn create_composite(cmd: cmdline::CreateCompositeCommand) -> std::result::Result
Ok(())
}
#[cfg(feature = "qcow")]
fn create_qcow2(cmd: cmdline::CreateQcow2Command) -> std::result::Result<(), ()> {
if !(cmd.size.is_some() ^ cmd.backing_file.is_some()) {
println!(
@ -563,6 +566,7 @@ fn crosvm_main() -> Result<CommandStatus> {
#[cfg(feature = "composite-disk")]
CrossPlatformCommands::CreateComposite(cmd) => create_composite(cmd)
.map_err(|_| anyhow!("create_composite subcommand failed")),
#[cfg(feature = "qcow")]
CrossPlatformCommands::CreateQcow2(cmd) => {
create_qcow2(cmd).map_err(|_| anyhow!("create_qcow2 subcommand failed"))
}