agent: format fresh block emptyDirs

Teach the agent to create an ext4 filesystem for fresh block volumes
when the storage request carries the block create-filesystem driver
option.

The create-filesystem option is the freshness and format signal, while
storage.fstype remains the filesystem type source. Plain block emptyDirs
are formatted directly by the agent. Encrypted block emptyDirs must carry
the same create-filesystem signal together with ephemeral encryption and
continue through CDH secure_mount.

Reject ephemeral encryption without the create-filesystem signal so
existing direct block storage cannot accidentally enter the fresh emptyDir
path.

Signed-off-by: Manuel Huber <manuelh@nvidia.com>
Assisted-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
Manuel Huber
2026-06-03 18:06:52 +00:00
parent b2b17d4b64
commit 16404f1cd5

View File

@@ -16,7 +16,7 @@ use kata_types::device::DRIVER_BLK_CCW_TYPE;
use kata_types::device::{
DRIVER_BLK_MMIO_TYPE, DRIVER_BLK_PCI_TYPE, DRIVER_NVDIMM_TYPE, DRIVER_SCSI_TYPE,
};
use kata_types::mount::StorageDevice;
use kata_types::mount::{StorageDevice, KATA_BLOCK_VOLUME_CREATE_FS};
use nix::sys::stat::{major, minor};
use protocols::agent::Storage;
use tracing::instrument;
@@ -37,6 +37,17 @@ use slog::Logger;
#[cfg(target_arch = "s390x")]
use std::str::FromStr;
const EPHEMERAL_ENCRYPTION_DRIVER_OPTION: &str = "encryption_key=ephemeral";
const MKFS_EXT4: &str = "mkfs.ext4";
const BLOCK_EMPTYDIR_EXT4_MKFS_OPTS: [&str; 8] =
["-O", "^has_journal", "-m", "0", "-i", "163840", "-I", "128"];
#[derive(Debug, Eq, PartialEq)]
struct BlockStorageDriverOptions {
has_ephemeral_encryption: bool,
should_create_filesystem: bool,
}
fn get_device_number(dev_path: &str, metadata: Option<&fs::Metadata>) -> Result<String> {
let dev_id = match metadata {
Some(m) => m.rdev(),
@@ -54,27 +65,172 @@ async fn handle_block_storage(
storage: &Storage,
dev_num: &str,
) -> Result<Arc<dyn StorageDevice>> {
let has_ephemeral_encryption = storage
.driver_options
.contains(&"encryption_key=ephemeral".to_string());
let options = block_storage_driver_options(storage)?;
if has_ephemeral_encryption {
if options.has_ephemeral_encryption {
let mkfs_opts = BLOCK_EMPTYDIR_EXT4_MKFS_OPTS.join(" ");
crate::rpc::cdh_secure_mount(
"block-device",
dev_num,
"luks2",
&storage.mount_point,
"-O ^has_journal -m 0 -i 163840 -I 128",
&mkfs_opts,
)
.await?;
set_ownership(logger, storage)?;
new_device(storage.mount_point.clone())
} else {
if options.should_create_filesystem {
ensure_block_filesystem(logger, storage).await?;
}
let path = common_storage_handler(logger, storage)?;
new_device(path)
}
}
fn block_storage_driver_options(storage: &Storage) -> Result<BlockStorageDriverOptions> {
let has_ephemeral_encryption = storage
.driver_options
.iter()
.any(|opt| opt == EPHEMERAL_ENCRYPTION_DRIVER_OPTION);
let should_create_filesystem = should_create_block_filesystem(storage);
if has_ephemeral_encryption && !should_create_filesystem {
return Err(anyhow!(
"{} requires {} for block storage",
EPHEMERAL_ENCRYPTION_DRIVER_OPTION,
KATA_BLOCK_VOLUME_CREATE_FS
));
}
Ok(BlockStorageDriverOptions {
has_ephemeral_encryption,
should_create_filesystem,
})
}
fn should_create_block_filesystem(storage: &Storage) -> bool {
storage
.driver_options
.iter()
.any(|opt| opt == KATA_BLOCK_VOLUME_CREATE_FS)
}
async fn ensure_block_filesystem(logger: &Logger, storage: &Storage) -> Result<()> {
match storage.fstype.as_str() {
"ext4" => ensure_ext4_filesystem(logger, &storage.source).await,
_ => Err(anyhow!(
"creating filesystem {} for block storage is unsupported",
storage.fstype
)),
}
}
async fn ensure_ext4_filesystem(logger: &Logger, source: &str) -> Result<()> {
// This option is emitted for block emptyDir volumes, whose backing device
// is ephemeral and freshly allocated for the pod.
info!(logger, "creating ext4 filesystem"; "source" => source);
let output = {
// Keep the agent SIGCHLD handler from reaping this child before
// tokio::process observes it.
let _locker = rustjail::container::WAIT_PID_LOCKER.lock().await;
// BLOCK_EMPTYDIR_EXT4_MKFS_OPTS mirrors CDH's EXT4_INTEGRITY_MKFS_OPTS
// from confidential-data-hub/hub/src/storage/volume_type/blockdevice/mod.rs.
// CDH's FsFormatter adds "-F" and its mapped device path separately in
// confidential-data-hub/hub/src/storage/drivers/filesystem.rs; here the
// agent invokes mkfs.ext4 directly, so add "-F" and source below.
tokio::process::Command::new(MKFS_EXT4)
.arg("-F")
.args(BLOCK_EMPTYDIR_EXT4_MKFS_OPTS)
.arg(source)
.output()
.await
.with_context(|| format!("run {MKFS_EXT4} for {source}"))?
};
if output.status.success() {
return Ok(());
}
Err(anyhow!(
"{} failed for {}: status={}, stdout={}, stderr={}",
MKFS_EXT4,
source,
output.status,
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
))
}
#[cfg(test)]
mod tests {
use super::*;
fn storage_with_driver_options(options: &[&str]) -> Storage {
Storage {
driver_options: options.iter().map(|opt| opt.to_string()).collect(),
..Default::default()
}
}
#[test]
fn block_storage_options_allow_normal_existing_storage() {
let storage = storage_with_driver_options(&[]);
let options = block_storage_driver_options(&storage).unwrap();
assert_eq!(
options,
BlockStorageDriverOptions {
has_ephemeral_encryption: false,
should_create_filesystem: false,
}
);
}
#[test]
fn block_storage_options_allow_plain_fresh_storage() {
let storage = storage_with_driver_options(&[KATA_BLOCK_VOLUME_CREATE_FS]);
let options = block_storage_driver_options(&storage).unwrap();
assert_eq!(
options,
BlockStorageDriverOptions {
has_ephemeral_encryption: false,
should_create_filesystem: true,
}
);
}
#[test]
fn block_storage_options_allow_encrypted_fresh_storage() {
let storage = storage_with_driver_options(&[
EPHEMERAL_ENCRYPTION_DRIVER_OPTION,
KATA_BLOCK_VOLUME_CREATE_FS,
]);
let options = block_storage_driver_options(&storage).unwrap();
assert_eq!(
options,
BlockStorageDriverOptions {
has_ephemeral_encryption: true,
should_create_filesystem: true,
}
);
}
#[test]
fn block_storage_options_reject_encryption_without_filesystem_creation() {
let storage = storage_with_driver_options(&[EPHEMERAL_ENCRYPTION_DRIVER_OPTION]);
let err = block_storage_driver_options(&storage).unwrap_err();
assert!(err.to_string().contains(KATA_BLOCK_VOLUME_CREATE_FS));
}
}
#[derive(Debug)]
pub struct VirtioBlkMmioHandler {}
@@ -96,8 +252,8 @@ impl StorageHandler for VirtioBlkMmioHandler {
.await
.context("failed to get mmio device name")?;
}
let path = common_storage_handler(ctx.logger, &storage)?;
new_device(path)
let dev_num = get_device_number(&storage.source, None)?;
handle_block_storage(ctx.logger, &storage, &dev_num).await
}
}