diff --git a/src/agent/src/storage/mod.rs b/src/agent/src/storage/mod.rs index 6b24bf1a9e..0b8c91cf17 100644 --- a/src/agent/src/storage/mod.rs +++ b/src/agent/src/storage/mod.rs @@ -22,6 +22,7 @@ use tracing::instrument; use self::bind_watcher_handler::BindWatcherHandler; use self::block_handler::{PmemHandler, ScsiHandler, VirtioBlkMmioHandler, VirtioBlkPciHandler}; +pub use self::ephemeral_handler::update_ephemeral_mounts; use self::ephemeral_handler::EphemeralHandler; use self::fs_handler::{OverlayfsHandler, VirtioFsHandler}; use self::image_pull_handler::ImagePullHandler; @@ -30,15 +31,13 @@ use self::multi_layer_erofs::{handle_multi_layer_erofs_group, is_multi_layer_sto use crate::mount::{baremount, is_mounted, remove_mounts}; use crate::sandbox::Sandbox; -pub use self::ephemeral_handler::update_ephemeral_mounts; - mod bind_watcher_handler; mod block_handler; mod ephemeral_handler; mod fs_handler; mod image_pull_handler; mod local_handler; -mod multi_layer_erofs; +pub mod multi_layer_erofs; const RW_MASK: u32 = 0o660; const RO_MASK: u32 = 0o440; @@ -168,6 +167,9 @@ struct MultiLayerProcessResult { /// Temporary mount points (upper/lower) backing the overlay, needed for /// container-scoped cleanup via `container_mounts`. temp_mount_points: Vec, + /// dm-verity device paths that need to be destroyed during cleanup + #[allow(dead_code)] + verity_devices: Vec, } /// Handle multi-layer storage by creating the overlay device. @@ -209,6 +211,7 @@ async fn handle_multi_layer_storage( device, processed_mount_points: result.processed_mount_points, temp_mount_points: result.temp_mount_points, + verity_devices: result.verity_devices, })) } @@ -303,6 +306,7 @@ pub async fn add_storages( } } mount_list.extend(result.temp_mount_points); + mount_list.extend(result.verity_devices); continue; } diff --git a/src/agent/src/storage/multi_layer_erofs.rs b/src/agent/src/storage/multi_layer_erofs.rs index 920141d5c7..7abc3df786 100644 --- a/src/agent/src/storage/multi_layer_erofs.rs +++ b/src/agent/src/storage/multi_layer_erofs.rs @@ -57,6 +57,8 @@ pub struct MultiLayerErofsResult { /// overlay. These must be tracked so they are unmounted *after* the /// overlay target during container teardown. pub temp_mount_points: Vec, + /// dm-verity device paths that need to be destroyed during cleanup + pub verity_devices: Vec, } #[derive(Debug)] @@ -64,6 +66,13 @@ struct MkdirDirective { raw_path: String, } +/// Helper struct to track layer mount information including dm-verity devices +#[derive(Debug)] +struct LayerMountInfo { + #[allow(dead_code)] + verity_device: Option, +} + #[async_trait::async_trait] impl StorageHandler for MultiLayerErofsHandler { fn driver_types(&self) -> &[&str] { @@ -176,7 +185,7 @@ pub async fn handle_multi_layer_erofs_group( let upper_mount = temp_base.join("upper"); fs::create_dir_all(&upper_mount).context("failed to create upper mount dir")?; - wait_and_mount_layer(ext4, &upper_mount, sandbox, &logger).await?; + wait_and_mount_layer(ext4, &upper_mount, sandbox, &logger, None).await?; for mkdir_dir in &mkdir_dirs { // As {{ mount 1 }} refers to the first lower layer, which is not available until we mount it. @@ -206,7 +215,7 @@ pub async fn handle_multi_layer_erofs_group( lower_mount.display() ))?; - wait_and_mount_layer(erofs, &lower_mount, sandbox, &logger).await?; + let _mount_info = wait_and_mount_layer(erofs, &lower_mount, sandbox, &logger, None).await?; lower_mounts.push(lower_mount); } @@ -316,6 +325,7 @@ pub async fn handle_multi_layer_erofs_group( mount_point: ext4.mount_point.clone(), processed_mount_points, temp_mount_points, + verity_devices: vec![], }) } @@ -464,7 +474,8 @@ async fn wait_and_mount_layer( layer_mount: &Path, sandbox: &Arc>, logger: &Logger, -) -> Result<()> { + base_dev_path: Option, +) -> Result { info!( logger, "Waiting for layer device"; @@ -472,22 +483,11 @@ async fn wait_and_mount_layer( "driver" => &layer.driver, "mount-point" => layer_mount.display(), ); - let dev_path = match layer.driver.as_str() { - DRIVER_SCSI_TYPE => { - // For SCSI devices, we need to wait for the device to appear and get its path before mounting. - get_scsi_device_name(sandbox, &layer.source).await? - } - DRIVER_BLK_PCI_TYPE => { - let (root_complex, pcipath) = pcipath_from_dev_tree_path(&layer.source)?; - get_virtio_blk_pci_device_name(sandbox, root_complex, &pcipath).await? - } - _ => { - // For non-SCSI devices, we can assume the source is directly mountable. - return Err(anyhow!( - "unsupported driver type '{}' for multi-layer erofs", - layer.driver - )); - } + + // Get the base device path + let dev_path = match base_dev_path { + Some(path) => path, + None => resolve_base_device_path(layer, sandbox).await?, }; info!( @@ -545,7 +545,34 @@ async fn wait_and_mount_layer( // After successfully mounting the layer, we track the mount point for cleanup. track_temporary_mount_for_cleanup(sandbox, layer_mount, logger).await?; - Ok(()) + Ok(LayerMountInfo { + verity_device: None, + }) +} + +async fn resolve_base_device_path( + layer: &Storage, + sandbox: &Arc>, +) -> Result { + let base_dev_path = match layer.driver.as_str() { + DRIVER_SCSI_TYPE => { + // For SCSI devices, we need to wait for the device to appear and get its path before mounting. + get_scsi_device_name(sandbox, &layer.source).await? + } + DRIVER_BLK_PCI_TYPE => { + let (root_complex, pcipath) = pcipath_from_dev_tree_path(&layer.source)?; + get_virtio_blk_pci_device_name(sandbox, root_complex, &pcipath).await? + } + _ => { + // For non-SCSI devices, we can assume the source is directly mountable. + return Err(anyhow!( + "unsupported driver type '{}' for multi-layer erofs", + layer.driver + )); + } + }; + + Ok(base_dev_path) } #[cfg(test)] @@ -600,25 +627,6 @@ mod tests { // --- parse_mkdir_directive --- - #[rstest] - #[case("some/path", true, "some/path")] - #[case("some/path:0755", true, "some/path")] - #[case("path:mode:extra", true, "path")] - #[case("", false, "")] - fn test_parse_mkdir_directive( - #[case] spec: &str, - #[case] should_pass: bool, - #[case] expected_path: &str, - ) { - let result = parse_mkdir_directive(spec); - if should_pass { - let d = result.expect("expected Ok"); - assert_eq!(d.raw_path, expected_path); - } else { - assert!(result.is_err(), "expected Err for spec {:?}", spec); - } - } - #[test] fn test_parse_mkdir_directive_rejects_null_bytes() { assert!(parse_mkdir_directive("foo\0bar").is_err());