From 44b216fed1f9ae144b2eac01c2ab29ea0eebf882 Mon Sep 17 00:00:00 2001 From: Alyssa Rosenzweig Date: Mon, 9 Sep 2024 09:30:20 -0400 Subject: [PATCH] Add krun_add_disk() method This generalizes over krun_set_{root,data}_disk(). Notably: * It allows arbitrarily many disk images. * It allows mounting disks read-only. To implement, we refactor a bit the existing handling while allowing both APIs to be used together. Signed-off-by: Alyssa Rosenzweig --- include/libkrun.h | 17 +++++++++ src/libkrun/src/lib.rs | 84 +++++++++++++++++++++++++++++++++--------- 2 files changed, 83 insertions(+), 18 deletions(-) diff --git a/include/libkrun.h b/include/libkrun.h index 9a933c6e..a5ca3b7d 100644 --- a/include/libkrun.h +++ b/include/libkrun.h @@ -90,6 +90,23 @@ int32_t krun_set_root_disk(uint32_t ctx_id, const char *disk_path); */ int32_t krun_set_data_disk(uint32_t ctx_id, const char *disk_path); +/** + * Adds a disk image to be used as a general partition for the microVM. + * + * Arguments: + * "ctx_id" - the configuration context ID. + * "block_id" - a null-terminated string representing the partition. + * Must not be "root" or "disk". + * "disk_path" - a null-terminated string representing the path leading to the disk image that + * contains the root file-system. + * "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_disk(uint32_t ctx_id, const char *block_id, const char *disk_path, bool read_only); + /** * NO LONGER SUPPORTED. DO NOT USE. * diff --git a/src/libkrun/src/lib.rs b/src/libkrun/src/lib.rs index 289c14b2..5bdb2118 100644 --- a/src/libkrun/src/lib.rs +++ b/src/libkrun/src/lib.rs @@ -86,6 +86,8 @@ struct ContextConfig { net_cfg: NetworkConfig, mac: Option<[u8; 6]>, #[cfg(feature = "blk")] + block_cfgs: Vec, + #[cfg(feature = "blk")] root_block_cfg: Option, #[cfg(feature = "blk")] data_block_cfg: Option, @@ -156,23 +158,36 @@ impl ContextConfig { } #[cfg(feature = "blk")] - fn set_root_block_cfg(&mut self, block_cfg: BlockDeviceConfig) { - self.root_block_cfg = Some(block_cfg); + fn add_block_cfg(&mut self, block_cfg: BlockDeviceConfig) { + self.block_cfgs.push(block_cfg); } #[cfg(feature = "blk")] - fn get_root_block_cfg(&self) -> Option { - self.root_block_cfg.clone() + fn set_root_block_cfg(&mut self, block_cfg: BlockDeviceConfig) { + self.root_block_cfg = block_cfg; } #[cfg(feature = "blk")] fn set_data_block_cfg(&mut self, block_cfg: BlockDeviceConfig) { - self.data_block_cfg = Some(block_cfg); + self.data_block_cfg = block_cfg; } #[cfg(feature = "blk")] - fn get_data_block_cfg(&self) -> Option { - self.data_block_cfg.clone() + fn get_block_cfg(&self) -> Vec { + // Blocks added with the new API + let cfgs = self.block_cfgs.clone(); + + // Blocks added with the old API. For backwards compat, when cfgs is empty (the new API is + // not used), this needs to be root and then data, in that order. Also for backwards + // compat, root/data are setters and need to discard redundant calls. So we have simple + // setters above and fix up here. + for cfg in [self.root_block_cfg, self.data_block_cfg] { + if let Some(cfg) = cfg { + cfgs.push(cfg); + } + } + + cfgs } fn set_net_cfg(&mut self, net_cfg: NetworkConfig) { @@ -484,12 +499,53 @@ pub unsafe extern "C" fn krun_set_mapped_volumes( #[allow(clippy::missing_safety_doc)] #[no_mangle] #[cfg(feature = "blk")] -pub unsafe extern "C" fn krun_set_root_disk(ctx_id: u32, c_disk_path: *const c_char) -> i32 { +pub unsafe extern "C" fn krun_add_disk( + ctx_id: u32, + c_block_id: *const c_char, + c_disk_path: *const c_char, + 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, + }; + + // These block IDs are special cased in the block_cfgs logic for legacy compatibility. Just ban + // them for the new API, force the user to avoid unclear/ambiguous API usage. + if block_id == "root" || block_id == "data" { + 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(), + 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_set_root_disk(ctx_id: u32, c_disk_path: *const c_char) -> i32 { + let disk_path = match CStr::from_ptr(c_disk_path).to_str() { + Ok(disk) => disk, + Err(_) => return -libc::EINVAL, + }; match CTX_MAP.lock().unwrap().entry(ctx_id) { Entry::Occupied(mut ctx_cfg) => { @@ -1009,17 +1065,9 @@ pub extern "C" fn krun_start_enter(ctx_id: u32) -> i32 { }; #[cfg(feature = "blk")] - if let Some(block_cfg) = ctx_cfg.get_root_block_cfg() { - if ctx_cfg.vmr.add_block_device(block_cfg).is_err() { - error!("Error configuring virtio-blk for root block"); - return -libc::EINVAL; - } - } - - #[cfg(feature = "blk")] - if let Some(block_cfg) = ctx_cfg.get_data_block_cfg() { + for block_cfg in ctx_cfg.get_block_cfg() { if ctx_cfg.vmr.add_block_device(block_cfg).is_err() { - error!("Error configuring virtio-blk for data block"); + error!("Error configuring virtio-blk for block"); return -libc::EINVAL; } }