diff --git a/include/libkrun.h b/include/libkrun.h index 16619aac..ea6a6e38 100644 --- a/include/libkrun.h +++ b/include/libkrun.h @@ -100,6 +100,12 @@ int32_t krun_set_data_disk(uint32_t ctx_id, const char *disk_path); * This API is mutually exclusive with the deprecated krun_set_root_disk and * krun_set_data_disk methods and must not be used together. * + * By all possible means, one should avoid probing the disk image file that will be given to libkrun. + * If a raw file is probed, and a QCOW2 header is written into the raw file's header, the next time + * libkrun launches the VM it will boot as a QCOW2 image. QCOW2 files can, and will, open other files. + * The guest will most likely have access to the data in those files, essentially giving the guest + * access to any file on the host system the disk emulation has access to. + * * Arguments: * "ctx_id" - the configuration context ID. * "block_id" - a null-terminated string representing the partition. @@ -113,6 +119,35 @@ int32_t krun_set_data_disk(uint32_t ctx_id, const char *disk_path); */ int32_t krun_add_disk(uint32_t ctx_id, const char *block_id, const char *disk_path, bool read_only); +/* Formatted disk image that are supported */ +#define KRUN_DISK_FORMAT_QCOW2 0 +/** + * Adds a formatted disk image to be used as a general partition for the microVM. The supported + * image formats are: "qcow2". + * + * This API is mutually exclusive with the deprecated krun_set_root_disk and + * krun_set_data_disk methods and must not be used together. + * + * By all possible means, one should avoid probing the disk image file that will be given to libkrun. + * + * Arguments: + * "ctx_id" - the configuration context ID. + * "block_id" - a null-terminated string representing the partition. + * "disk_path" - a null-terminated string representing the path leading to the disk image that + * contains the root file-system. + * "disk_format" - the formatted disk image format + * "read_only" - whether the mount should be read-only. Required if the caller does not have + * write permissions (for disk images in /usr/share). + * + * Returns: + * Zero on success or a negative error number on failure. + */ +int32_t krun_add_formatted_disk(uint32_t ctx_id, + const char *block_id, + const char *disk_path, + uint32_t disk_format, + bool read_only); + /** * NO LONGER SUPPORTED. DO NOT USE. * diff --git a/src/devices/src/virtio/block/device.rs b/src/devices/src/virtio/block/device.rs index 60350aff..5bd8aea5 100644 --- a/src/devices/src/virtio/block/device.rs +++ b/src/devices/src/virtio/block/device.rs @@ -8,7 +8,7 @@ use std::cmp; use std::convert::From; use std::fs::{File, OpenOptions}; -use std::io::{self, Write}; +use std::io::{self, ErrorKind, Write}; #[cfg(target_os = "linux")] use std::os::linux::fs::MetadataExt; #[cfg(target_os = "macos")] @@ -204,6 +204,7 @@ impl Block { partuuid: Option, cache_type: CacheType, disk_image_path: String, + disk_image_format: ImageType, is_disk_read_only: bool, ) -> io::Result { let disk_image = OpenOptions::new() @@ -215,6 +216,13 @@ impl Block { let image_type = disk::detect_image_type(&disk_image, false).unwrap(); + if image_type != disk_image_format { + return Err(io::Error::new( + ErrorKind::InvalidInput, + "image type reported does not match image header", + )); + } + let disk_image = match image_type { ImageType::Qcow2 => { let mut qcow_disk_image = diff --git a/src/devices/src/virtio/block/disk/mod.rs b/src/devices/src/virtio/block/disk/mod.rs index f1abc62f..59af8814 100644 --- a/src/devices/src/virtio/block/disk/mod.rs +++ b/src/devices/src/virtio/block/disk/mod.rs @@ -17,7 +17,7 @@ pub enum Error { type Result = std::result::Result; /// The variants of image files on the host that can be used as virtual disks. -#[derive(Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum ImageType { Raw, Qcow2, diff --git a/src/libkrun/src/lib.rs b/src/libkrun/src/lib.rs index 77c84d5d..1b4a88c4 100644 --- a/src/libkrun/src/lib.rs +++ b/src/libkrun/src/lib.rs @@ -18,6 +18,8 @@ use std::sync::Mutex; #[cfg(target_os = "macos")] use crossbeam_channel::unbounded; +#[cfg(feature = "blk")] +use devices::virtio::block::disk::ImageType; #[cfg(feature = "net")] use devices::virtio::net::device::VirtioNetBackend; #[cfg(feature = "blk")] @@ -521,6 +523,54 @@ pub unsafe extern "C" fn krun_add_disk( block_id: block_id.to_string(), cache_type: CacheType::Writeback, disk_image_path: disk_path.to_string(), + disk_image_format: ImageType::Raw, + is_disk_read_only: read_only, + }; + cfg.add_block_cfg(block_device_config); + } + Entry::Vacant(_) => return -libc::ENOENT, + } + + KRUN_SUCCESS +} + +#[allow(clippy::missing_safety_doc)] +#[no_mangle] +#[cfg(feature = "blk")] +pub unsafe extern "C" fn krun_add_formatted_disk( + ctx_id: u32, + c_block_id: *const c_char, + c_disk_path: *const c_char, + disk_format: u32, + read_only: bool, +) -> i32 { + let disk_path = match CStr::from_ptr(c_disk_path).to_str() { + Ok(disk) => disk, + Err(_) => return -libc::EINVAL, + }; + + let block_id = match CStr::from_ptr(c_block_id).to_str() { + Ok(block_id) => block_id, + Err(_) => return -libc::EINVAL, + }; + + let format = match disk_format { + 0 => ImageType::Qcow2, + _ => { + // Qcow2 is the only supported formatted disk image format. Any other value specified + // is invalid. + return -libc::EINVAL; + } + }; + + match CTX_MAP.lock().unwrap().entry(ctx_id) { + Entry::Occupied(mut ctx_cfg) => { + let cfg = ctx_cfg.get_mut(); + let block_device_config = BlockDeviceConfig { + block_id: block_id.to_string(), + cache_type: CacheType::Writeback, + disk_image_path: disk_path.to_string(), + disk_image_format: format, is_disk_read_only: read_only, }; cfg.add_block_cfg(block_device_config); @@ -547,6 +597,7 @@ pub unsafe extern "C" fn krun_set_root_disk(ctx_id: u32, c_disk_path: *const c_c block_id: "root".to_string(), cache_type: CacheType::Writeback, disk_image_path: disk_path.to_string(), + disk_image_format: ImageType::Raw, is_disk_read_only: false, }; cfg.set_root_block_cfg(block_device_config); @@ -573,6 +624,7 @@ pub unsafe extern "C" fn krun_set_data_disk(ctx_id: u32, c_disk_path: *const c_c block_id: "data".to_string(), cache_type: CacheType::Writeback, disk_image_path: disk_path.to_string(), + disk_image_format: ImageType::Raw, is_disk_read_only: false, }; cfg.set_data_block_cfg(block_device_config); diff --git a/src/vmm/src/vmm_config/block.rs b/src/vmm/src/vmm_config/block.rs index 81f31184..421f2dbe 100644 --- a/src/vmm/src/vmm_config/block.rs +++ b/src/vmm/src/vmm_config/block.rs @@ -2,7 +2,7 @@ use std::collections::VecDeque; use std::fmt; use std::sync::{Arc, Mutex}; -use devices::virtio::{Block, CacheType}; +use devices::virtio::{block::disk::ImageType, Block, CacheType}; #[derive(Debug)] pub enum BlockConfigError { @@ -26,6 +26,7 @@ pub struct BlockDeviceConfig { pub block_id: String, pub cache_type: CacheType, pub disk_image_path: String, + pub disk_image_format: ImageType, pub is_disk_read_only: bool, } @@ -53,6 +54,7 @@ impl BlockBuilder { None, config.cache_type, config.disk_image_path, + config.disk_image_format, config.is_disk_read_only, ) .map_err(BlockConfigError::CreateBlockDevice)