Compare commits

..

8 Commits

Author SHA1 Message Date
Markus Rudy
c97d373e2d test pr3
Signed-off-by: Markus Rudy <mr@edgeless.systems>
2026-04-09 19:55:51 +02:00
Fabiano Fidêncio
72fb41d33b kata-deploy: Symlink original config to per-shim runtime copy
Users were confused about which configuration file to edit because
kata-deploy copied the base config into a per-shim runtime directory
(runtimes/<shim>/) for config.d support, leaving the original file
in place untouched.  This made it look like the original was the
authoritative config, when in reality the runtime was loading the
copy from the per-shim directory.

Replace the original config file with a symlink pointing to the
per-shim runtime copy after the copy is made.  The runtime's
ResolvePath / EvalSymlinks follows the symlink and lands in the
per-shim directory, where it naturally finds config.d/ with all
drop-in fragments.  This makes it immediately obvious that the
real configuration lives in the per-shim directory and removes the
ambiguity about which file to inspect or modify.

During cleanup, the symlink at the original location is explicitly
removed before the runtime directory is deleted.

Signed-off-by: Fabiano Fidêncio <ffidencio@nvidia.com>
2026-04-09 17:16:40 +02:00
Steve Horsman
9e8069569e Merge pull request #12734 from Apokleos/rm-v9p-rs
runtime-rs: Remove virtio-9p Shared Filesystem Support
2026-04-09 16:15:55 +01:00
Alex Lyn
38382a59c4 kata-ctl: remove msize_9p from kata-ctl hypervisor info
Remove the msize_9p field from HypervisorInfo struct and
get_hypervisor_info() function in kata-ctl tool.

This aligns with the removal of 9p filesystem support from
the configuration and agent.

Signed-off-by: Alex Lyn <alex.lyn@antgroup.com>
2026-04-07 23:15:39 +02:00
Alex Lyn
2bac201364 agent: Remove virtio-9p storage handler
Remove the Virtio9pHandler implementation and its registration
from the storage handler manager:
(1) Remove Virtio9pHandler struct and StorageHandler implementation.
(2) Remove DRIVER_9P_TYPE and Virtio9pHandler from STORAGE_HANDLERS
  registration.
(3) Update watcher.rs comments to remove 9p references.
This completes the removal of virtio-9p support in the agent.

Signed-off-by: Alex Lyn <alex.lyn@antgroup.com>
2026-04-07 23:15:39 +02:00
Alex Lyn
10b24a19c8 kata-types: Remove virtio-9p shared filesystem support
Remove all virtio-9p related code and configurations:
(1) Remove DRIVER_9P_TYPE and VIRTIO_9P.
(2) Remove 9p validation and adjustment logic from SharedFsInfo.
(3) Remove KATA_ANNO_CFG_HYPERVISOR_MSIZE_9P annotation handling.
(4) Update test configurations to remove msize_9p settings.
(5) Update documentation and proto comments.

Signed-off-by: Alex Lyn <alex.lyn@antgroup.com>
2026-04-07 23:15:39 +02:00
Alex Lyn
f133b81579 docs: update shared filesystem documentation and tests
(1) Update annotations documentation to reflect new shared filesystem
    options (virtio-fs, inline-virtio-fs, virtio-fs-nydus, none).
(2) Replace virtio-9p references with inline-virtio-fs in config doc.
(3) Update drop-in configuration tests to use 'none' instead of 'virtio-9p'

Signed-off-by: Alex Lyn <alex.lyn@antgroup.com>
2026-04-07 23:15:39 +02:00
Alex Lyn
d6546f2a56 runtime-rs: Remove virtio-9p from configuration*.toml.in
As virtio-9p is never supported in runtime-rs, we have more choices to
replace it with blockfile snapshotter or erofs snapshotter(in future).

It's time to remove its documents and reduce misleading guidance.

Signed-off-by: Alex Lyn <alex.lyn@antgroup.com>
2026-04-07 23:15:39 +02:00
28 changed files with 394 additions and 123 deletions

View File

@@ -53,8 +53,6 @@ jobs:
vmm: qemu-tdx
- runner: sev-snp
vmm: qemu-snp
- runner: sev-snp
vmm: qemu-snp-runtime-rs
runs-on: ${{ matrix.runner }}
env:
DOCKER_REGISTRY: ${{ inputs.registry }}

View File

@@ -10,7 +10,7 @@ use std::sync::Arc;
use crate::storage::{common_storage_handler, new_device, StorageContext, StorageHandler};
use anyhow::{anyhow, Context, Result};
use kata_types::device::{DRIVER_9P_TYPE, DRIVER_OVERLAYFS_TYPE, DRIVER_VIRTIOFS_TYPE};
use kata_types::device::{DRIVER_OVERLAYFS_TYPE, DRIVER_VIRTIOFS_TYPE};
use kata_types::mount::{StorageDevice, KATA_VOLUME_OVERLAYFS_CREATE_DIR};
use protocols::agent::Storage;
use tracing::instrument;
@@ -69,27 +69,6 @@ impl StorageHandler for OverlayfsHandler {
}
}
#[derive(Debug)]
pub struct Virtio9pHandler {}
#[async_trait::async_trait]
impl StorageHandler for Virtio9pHandler {
#[instrument]
fn driver_types(&self) -> &[&str] {
&[DRIVER_9P_TYPE]
}
#[instrument]
async fn create_device(
&self,
storage: Storage,
ctx: &mut StorageContext,
) -> Result<Arc<dyn StorageDevice>> {
let path = common_storage_handler(ctx.logger, &storage)?;
new_device(path)
}
}
#[derive(Debug)]
pub struct VirtioFsHandler {}

View File

@@ -23,7 +23,7 @@ use tracing::instrument;
use self::bind_watcher_handler::BindWatcherHandler;
use self::block_handler::{PmemHandler, ScsiHandler, VirtioBlkMmioHandler, VirtioBlkPciHandler};
use self::ephemeral_handler::EphemeralHandler;
use self::fs_handler::{OverlayfsHandler, Virtio9pHandler, VirtioFsHandler};
use self::fs_handler::{OverlayfsHandler, VirtioFsHandler};
use self::image_pull_handler::ImagePullHandler;
use self::local_handler::LocalHandler;
use crate::mount::{baremount, is_mounted, remove_mounts};
@@ -134,7 +134,6 @@ lazy_static! {
pub static ref STORAGE_HANDLERS: StorageHandlerManager<Arc<dyn StorageHandler>> = {
let mut manager: StorageHandlerManager<Arc<dyn StorageHandler>> = StorageHandlerManager::new();
let handlers: Vec<Arc<dyn StorageHandler>> = vec![
Arc::new(Virtio9pHandler {}),
Arc::new(VirtioBlkMmioHandler {}),
Arc::new(VirtioBlkPciHandler {}),
Arc::new(EphemeralHandler {}),

View File

@@ -425,7 +425,7 @@ impl SandboxStorages {
/// or updated file to a target mount point, or remove the removed file from the target mount point. All WatchableStorage
/// target mount points are expected to reside within a single tmpfs, whose root is created by the BindWatcher.
///
/// This is a temporary workaround to handle config map updates until we get inotify on 9p/virtio-fs.
/// This is a temporary workaround to handle config map updates until we get inotify on virtio-fs.
/// More context on this:
/// - https://github.com/kata-containers/runtime/issues/1505
/// - https://github.com/kata-containers/kata-containers/issues/1879

View File

@@ -257,7 +257,7 @@ pub const KATA_ANNO_CFG_HYPERVISOR_ENABLE_ROOTLESS_HYPERVISOR: &str =
"io.katacontainers.config.hypervisor.rootless";
// Hypervisor Shared File System related annotations
/// A sandbox annotation to specify the shared file system type, either inline-virtio-fs (default), virtio-9p, virtio-fs or virtio-fs-nydus.
/// A sandbox annotation to specify the shared file system type, either virtio-fs(default), inline-virtio-fs, virtio-fs-nydus or none.
pub const KATA_ANNO_CFG_HYPERVISOR_SHARED_FS: &str =
"io.katacontainers.config.hypervisor.shared_fs";
/// A sandbox annotations to specify virtio-fs vhost-user daemon path.
@@ -272,8 +272,6 @@ pub const KATA_ANNO_CFG_HYPERVISOR_VIRTIO_FS_CACHE_SIZE: &str =
/// A sandbox annotation to pass options to virtiofsd daemon.
pub const KATA_ANNO_CFG_HYPERVISOR_VIRTIO_FS_EXTRA_ARGS: &str =
"io.katacontainers.config.hypervisor.virtio_fs_extra_args";
/// A sandbox annotation to specify as the msize for 9p shares.
pub const KATA_ANNO_CFG_HYPERVISOR_MSIZE_9P: &str = "io.katacontainers.config.hypervisor.msize_9p";
/// The initdata annotation passed in when CVM launchs
pub const KATA_ANNO_CFG_HYPERVISOR_INIT_DATA: &str =
"io.katacontainers.config.hypervisor.cc_init_data";
@@ -975,14 +973,6 @@ impl Annotation {
hv.shared_fs.virtio_fs_extra_args.push(arg.to_string());
}
}
KATA_ANNO_CFG_HYPERVISOR_MSIZE_9P => match self.get_value::<u32>(key) {
Ok(v) => {
hv.shared_fs.msize_9p = v.unwrap_or_default();
}
Err(_e) => {
return Err(u32_err);
}
},
KATA_ANNO_CFG_HYPERVISOR_BLOCK_DEV_NUM_QUEUES => {
match self.get_value::<usize>(key) {
Ok(v) => {

View File

@@ -47,9 +47,6 @@ pub const DEFAULT_BLOCK_DEVICE_QUEUE_SIZE: u32 = 128;
pub const DEFAULT_SHARED_FS_TYPE: &str = "virtio-fs";
pub const DEFAULT_VIRTIO_FS_CACHE_MODE: &str = "never";
pub const DEFAULT_VIRTIO_FS_DAX_SIZE_MB: u32 = 1024;
pub const DEFAULT_SHARED_9PFS_SIZE_MB: u32 = 8 * 1024;
pub const MIN_SHARED_9PFS_SIZE_MB: u32 = 4 * 1024;
pub const MAX_SHARED_9PFS_SIZE_MB: u32 = 8 * 1024 * 1024;
pub const DEFAULT_GUEST_HOOK_PATH: &str = "/opt/kata/hooks";
pub const DEFAULT_GUEST_DNS_FILE: &str = "/etc/resolv.conf";

View File

@@ -346,7 +346,7 @@ mod drop_in_directory_handling {
let dropin_override_data = r#"
[hypervisor.qemu]
shared_fs = "virtio-9p"
shared_fs = "none"
[runtime]
vfio_mode="vfio"
"#;
@@ -372,7 +372,7 @@ mod drop_in_directory_handling {
assert_eq!(config.hypervisor["qemu"].device_info.default_bridges, 4);
assert_eq!(
config.hypervisor["qemu"].shared_fs.shared_fs.as_deref(),
Some("virtio-9p")
Some("none")
);
assert!(config.runtime.debug);
assert!(config.runtime.sandbox_cgroup_only);

View File

@@ -68,7 +68,6 @@ mod firecracker;
pub use self::firecracker::{FirecrackerConfig, HYPERVISOR_NAME_FIRECRACKER};
const NO_VIRTIO_FS: &str = "none";
const VIRTIO_9P: &str = "virtio-9p";
const VIRTIO_FS: &str = "virtio-fs";
const VIRTIO_FS_INLINE: &str = "inline-virtio-fs";
const MAX_BRIDGE_SIZE: u32 = 5;
@@ -1419,12 +1418,13 @@ impl SecurityInfo {
}
}
/// Configuration information for shared filesystems, such as virtio-9p and virtio-fs.
/// Configuration information for shared filesystems, such as virtio-fs-nydus and virtio-fs.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct SharedFsInfo {
/// Type of shared file system to use:
/// - `virtio-fs` (default)
/// - `virtio-9p`
/// - `inline-virtio-fs`
/// - `virtio-fs-nydus`
/// - `none` (disables shared filesystem)
pub shared_fs: Option<String>,
@@ -1466,18 +1466,13 @@ pub struct SharedFsInfo {
/// Enables `virtio-fs` DAX (Direct Access) window if `true`.
#[serde(default)]
pub virtio_fs_is_dax: bool,
/// This is the `msize` used for 9p shares. It represents the number of bytes
/// used for the 9p packet payload.
#[serde(default)]
pub msize_9p: u32,
}
impl SharedFsInfo {
/// Adjusts the shared filesystem configuration after loading from a configuration file.
///
/// Handles default values for `shared_fs` type, `virtio-fs` specific settings
/// (daemon path, cache mode, DAX), and `virtio-9p` msize.
/// (daemon path, cache mode, DAX) or `inline-virtio-fs` settings.
pub fn adjust_config(&mut self) -> Result<()> {
if self.shared_fs.as_deref() == Some(NO_VIRTIO_FS) {
self.shared_fs = None;
@@ -1490,11 +1485,6 @@ impl SharedFsInfo {
match self.shared_fs.as_deref() {
Some(VIRTIO_FS) => self.adjust_virtio_fs(false)?,
Some(VIRTIO_FS_INLINE) => self.adjust_virtio_fs(true)?,
Some(VIRTIO_9P) => {
if self.msize_9p == 0 {
self.msize_9p = default::DEFAULT_SHARED_9PFS_SIZE_MB;
}
}
_ => {}
}
@@ -1504,23 +1494,12 @@ impl SharedFsInfo {
/// Validates the shared filesystem configuration.
///
/// Checks the validity of the selected `shared_fs` type and
/// performs specific validations for `virtio-fs` and `virtio-9p` settings.
/// performs specific validations for `virtio-fs` and `inline-virtio-fs` settings.
pub fn validate(&self) -> Result<()> {
match self.shared_fs.as_deref() {
None => Ok(()),
Some(VIRTIO_FS) => self.validate_virtio_fs(false),
Some(VIRTIO_FS_INLINE) => self.validate_virtio_fs(true),
Some(VIRTIO_9P) => {
if self.msize_9p < default::MIN_SHARED_9PFS_SIZE_MB
|| self.msize_9p > default::MAX_SHARED_9PFS_SIZE_MB
{
return Err(std::io::Error::other(format!(
"Invalid 9p configuration msize 0x{:x}, min value is 0x{:x}, max value is 0x{:x}",
self.msize_9p,default::MIN_SHARED_9PFS_SIZE_MB, default::MAX_SHARED_9PFS_SIZE_MB
)));
}
Ok(())
}
Some(v) => Err(std::io::Error::other(format!("Invalid shared_fs type {v}"))),
}
}

View File

@@ -27,8 +27,6 @@ pub const DRIVER_VFIO_AP_TYPE: &str = "vfio-ap";
/// DRIVER_VFIO_AP_COLD_TYPE is the device driver for vfio-ap coldplug.
pub const DRIVER_VFIO_AP_COLD_TYPE: &str = "vfio-ap-cold";
/// DRIVER_9P_TYPE is the driver for 9pfs volume.
pub const DRIVER_9P_TYPE: &str = "9p";
/// DRIVER_EPHEMERAL_TYPE is the driver for ephemeral volume.
pub const DRIVER_EPHEMERAL_TYPE: &str = "ephemeral";
/// DRIVER_LOCAL_TYPE is the driver for local volume.

View File

@@ -48,7 +48,6 @@ file_mem_backend = "/dev/shm"
valid_file_mem_backends = ["/dev/shm","/dev/snd","./test_file_backend_mem_root"]
pflashes = ["/proc/mounts"]
enable_debug = true
msize_9p = 16384
disable_image_nvdimm = true
hotplug_vfio_on_root_bus = true
pcie_root_port = 2

View File

@@ -47,7 +47,6 @@ file_mem_backend = "/dev/shm"
valid_file_mem_backends = ["/dev/shm"]
pflashes = ["/proc/mounts"]
enable_debug = true
msize_9p = 16384
disable_image_nvdimm = true
hotplug_vfio_on_root_bus = true
pcie_root_port = 2

View File

@@ -493,7 +493,7 @@ message SharedMount {
// could have been defined through the Mount list of the OCI specification.
message Storage {
// Driver is used to define the way the storage is passed through the
// virtual machine. It can be "9p", "blk", or something else, but for
// virtual machine. It can be "blk", or something else, but for
// all cases, this will define if some extra steps are required before
// this storage gets mounted into the container.
string driver = 1;
@@ -509,7 +509,7 @@ message Storage {
string source = 3;
// Fstype represents the filesystem that needs to be used to mount the
// storage inside the VM. For instance, it could be "xfs" for block
// device, "9p" for shared filesystem, or "tmpfs" for shared /dev/shm.
// device, or "tmpfs" for shared /dev/shm.
string fstype = 4;
// Options describes the additional options that might be needed to
// mount properly the storage filesystem.

View File

@@ -174,7 +174,6 @@ guest_hook_path = ""
# Shared file system type:
# - inline-virtio-fs (default)
# - virtio-fs
# - virtio-9p
# - virtio-fs-nydus
# "inline-virtio-fs" is the same as "virtio-fs", but it is running in the same process
# of shim, does not need an external virtiofsd process.

View File

@@ -179,7 +179,6 @@ disable_block_device_use = @DEFDISABLEBLOCK@
# Shared file system type:
# - virtio-fs (default)
# - virtio-9p
# - virtio-fs-nydus
# - none
shared_fs = "@DEFSHAREDFS_QEMU_COCO_DEV_VIRTIOFS@"

View File

@@ -163,7 +163,6 @@ disable_block_device_use = @DEFDISABLEBLOCK@
# Shared file system type:
# - virtio-fs (default)
# - virtio-9p
# - virtio-fs-nydus
# - none
shared_fs = "@DEFSHAREDFS_QEMU_VIRTIOFS@"

View File

@@ -162,7 +162,6 @@ disable_block_device_use = @DEFDISABLEBLOCK@
# Shared file system type:
# - virtio-fs (default)
# - virtio-9p
# - virtio-fs-nydus
# - none
shared_fs = "@DEFSHAREDFS_QEMU_SEL_VIRTIOFS@"

View File

@@ -1519,6 +1519,8 @@ CopyFileRequest if {
check_directory_traversal(input.path)
allow_copy_file(input.path, input.file_mode, input.data)
some regex1 in policy_data.request_defaults.CopyFileRequest
regex2 := replace(regex1, "$(sfprefix)", policy_data.common.sfprefix)
regex3 := replace(regex2, "$(cpath)", policy_data.common.cpath)
@@ -1530,6 +1532,39 @@ CopyFileRequest if {
print("CopyFileRequest: true")
}
allow_copy_file(path, mode, data) if {
print("allow_copy_file regular")
bits.and(mode, 61440) == 32768
print("allow_copy_file regular: true")
}
allow_copy_file(path, mode, data) if {
print("allow_copy_file dir")
bits.and(mode, 61440) == 16384
print("allow_copy_file dir: true")
}
allow_copy_file(path, mode, data) if {
print("allow_copy_file symlink")
bits.and(mode, 61440) == 40960
target := concat("", [sprintf("%c", [c]) | c := data[_]])
check_directory_traversal(target)
not startswith(target, "/")
regex1 := concat("", [policy_data.common.sfprefix, ".*/.+"])
regex2 := replace(regex1, "$(cpath)", policy_data.common.cpath)
regex3 := replace(regex2, "$(bundle-id)", "[a-z0-9]{64}")
regex.match(regex3, path)
print("allow_copy_file symlink: true")
}
CreateSandboxRequest if {
print("CreateSandboxRequest: input.guest_hook_path =", input.guest_hook_path)
count(input.guest_hook_path) == 0

View File

@@ -4,6 +4,7 @@
"description": "copy initiated by k8s mount",
"kind": "CopyFileRequest",
"request": {
"file_mode": 33206,
"path": "/run/kata-containers/shared/containers/81e5f43bc8599c5661e66f959ac28df5bfb30da23c5d583f2dcc6f9e0c5186dc-ce23cfeb91e75aaa-resolv.conf"
}
},
@@ -12,6 +13,7 @@
"description": "a dirname can have trailing dots",
"kind": "CopyFileRequest",
"request": {
"file_mode": 33206,
"path": "/run/kata-containers/shared/containers/81e5f43bc8599c5661e66f959ac28df5bfb30da23c5d583f2dcc6f9e0c5186dc-ce23cfeb91e75aaa-foo../bar"
}
},
@@ -20,6 +22,7 @@
"description": "attempt to copy outside of container root",
"kind": "CopyFileRequest",
"request": {
"file_mode": 33206,
"path": "/etc/ssl/cert.pem"
}
},
@@ -28,6 +31,7 @@
"description": "attempt to write into container root",
"kind": "CopyFileRequest",
"request": {
"file_mode": 33206,
"path": "/run/kata-containers/shared/containers/81e5f43bc8599c5661e66f959ac28df5bfb30da23c5d583f2dcc6f9e0c5186dc/rootfs/bin/sh"
}
},
@@ -36,6 +40,7 @@
"description": "attempt to write into container root - guest pull",
"kind": "CopyFileRequest",
"request": {
"file_mode": 33206,
"path": "/run/kata-containers/81e5f43bc8599c5661e66f959ac28df5bfb30da23c5d583f2dcc6f9e0c5186dc/rootfs/bin/sh"
}
},
@@ -44,6 +49,7 @@
"description": "attempted directory traversal",
"kind": "CopyFileRequest",
"request": {
"file_mode": 33206,
"path": "/run/kata-containers/shared/containers/81e5f43bc8599c5661e66f959ac28df5bfb30da23c5d583f2dcc6f9e0c5186dc-ce23cfeb91e75aaa-foo/../../../../../etc/ssl/cert.pem"
}
},
@@ -52,6 +58,7 @@
"description": "attempted directory traversal - parent directory",
"kind": "CopyFileRequest",
"request": {
"file_mode": 16895,
"path": "/run/kata-containers/shared/containers/81e5f43bc8599c5661e66f959ac28df5bfb30da23c5d583f2dcc6f9e0c5186dc-ce23cfeb91e75aaa-foo/.."
}
},
@@ -60,6 +67,7 @@
"description": "relative path",
"kind": "CopyFileRequest",
"request": {
"file_mode": 33206,
"path": "etc/ssl/cert.pem"
}
},
@@ -68,7 +76,122 @@
"description": "relative path - parent directory",
"kind": "CopyFileRequest",
"request": {
"file_mode": 16895,
"path": ".."
}
},
{
"allowed": false,
"description": "unsupported S_IFBLK",
"kind": "CopyFileRequest",
"request": {
"file_mode": 24576,
"path": "/run/kata-containers/shared/containers/81e5f43bc8599c5661e66f959ac28df5bfb30da23c5d583f2dcc6f9e0c5186dc-ce23cfeb91e75aaa-foo/bar"
}
},
{
"allowed": false,
"description": "unsupported S_IFSOCK",
"kind": "CopyFileRequest",
"request": {
"file_mode": 49152,
"path": "/run/kata-containers/shared/containers/81e5f43bc8599c5661e66f959ac28df5bfb30da23c5d583f2dcc6f9e0c5186dc-ce23cfeb91e75aaa-foo/bar"
}
},
{
"allowed": false,
"description": "unsupported S_IFIFO",
"kind": "CopyFileRequest",
"request": {
"file_mode": 4096,
"path": "/run/kata-containers/shared/containers/81e5f43bc8599c5661e66f959ac28df5bfb30da23c5d583f2dcc6f9e0c5186dc-ce23cfeb91e75aaa-foo/bar"
}
},
{
"allowed": false,
"description": "unsupported mixed mode (S_IFREG | S_IFLNK)",
"kind": "CopyFileRequest",
"request": {
"file_mode": 73728,
"path": "/run/kata-containers/shared/containers/81e5f43bc8599c5661e66f959ac28df5bfb30da23c5d583f2dcc6f9e0c5186dc-ce23cfeb91e75aaa-foo/bar"
}
},
{
"allowed": false,
"description": "unsupported no mode",
"kind": "CopyFileRequest",
"request": {
"file_mode": 511,
"path": "/run/kata-containers/shared/containers/81e5f43bc8599c5661e66f959ac28df5bfb30da23c5d583f2dcc6f9e0c5186dc-ce23cfeb91e75aaa-foo/bar"
}
},
{
"allowed": true,
"description": "directory in top-level shared directory",
"kind": "CopyFileRequest",
"request": {
"file_mode": 16895,
"path": "/run/kata-containers/shared/containers/81e5f43bc8599c5661e66f959ac28df5bfb30da23c5d583f2dcc6f9e0c5186dc-ce23cfeb91e75aaa-foo"
}
},
{
"allowed": false,
"description": "symlink in top-level shared directory",
"kind": "CopyFileRequest",
"request": {
"data": [97, 98, 99],
"file_mode": 41471,
"path": "/run/kata-containers/shared/containers/81e5f43bc8599c5661e66f959ac28df5bfb30da23c5d583f2dcc6f9e0c5186dc-ce23cfeb91e75aaa-foo"
}
},
{
"allowed": true,
"description": "symlink beneath top-level shared directory",
"kind": "CopyFileRequest",
"request": {
"data": [97, 98, 99],
"file_mode": 41471,
"path": "/run/kata-containers/shared/containers/81e5f43bc8599c5661e66f959ac28df5bfb30da23c5d583f2dcc6f9e0c5186dc-ce23cfeb91e75aaa-foo/lnk"
}
},
{
"allowed": false,
"description": "symlink pointing up - leading (../abc)",
"kind": "CopyFileRequest",
"request": {
"data": [46, 46, 47, 97, 98, 99],
"file_mode": 41471,
"path": "/run/kata-containers/shared/containers/81e5f43bc8599c5661e66f959ac28df5bfb30da23c5d583f2dcc6f9e0c5186dc-ce23cfeb91e75aaa-foo/lnk"
}
},
{
"allowed": false,
"description": "symlink pointing up - middle (a/../../b)",
"kind": "CopyFileRequest",
"request": {
"data": [97, 47, 46, 46, 47, 46, 46, 47, 98],
"file_mode": 41471,
"path": "/run/kata-containers/shared/containers/81e5f43bc8599c5661e66f959ac28df5bfb30da23c5d583f2dcc6f9e0c5186dc-ce23cfeb91e75aaa-foo/lnk"
}
},
{
"allowed": false,
"description": "symlink with 0-byte in target (a\\x00/b)",
"kind": "CopyFileRequest",
"request": {
"data": [97, 0, 47, 98],
"file_mode": 41471,
"path": "/run/kata-containers/shared/containers/81e5f43bc8599c5661e66f959ac28df5bfb30da23c5d583f2dcc6f9e0c5186dc-ce23cfeb91e75aaa-foo/lnk"
}
},
{
"allowed": false,
"description": "symlink with absolute target (/abc)",
"kind": "CopyFileRequest",
"request": {
"data": [47, 97, 98, 99],
"file_mode": 41471,
"path": "/run/kata-containers/shared/containers/81e5f43bc8599c5661e66f959ac28df5bfb30da23c5d583f2dcc6f9e0c5186dc-ce23cfeb91e75aaa-foo/lnk"
}
}
]

View File

@@ -184,8 +184,6 @@ pub struct HypervisorInfo {
#[serde(default)]
virtio_fs_daemon: String,
#[serde(default)]
msize_9p: u32,
#[serde(default)]
memory_slots: u32,
#[serde(default)]
pcie_root_port: u32,
@@ -417,7 +415,6 @@ pub fn get_hypervisor_info(
.clone()
.unwrap_or_else(|| String::from("none")),
virtio_fs_daemon: hypervisor_config.shared_fs.virtio_fs_daemon.to_string(),
msize_9p: hypervisor_config.shared_fs.msize_9p,
memory_slots: hypervisor_config.memory_info.memory_slots,
pcie_root_port: hypervisor_config.device_info.pcie_root_port,
hotplug_vfio_on_rootbus: hypervisor_config.device_info.hotplug_vfio_on_root_bus,

View File

@@ -635,7 +635,7 @@ function helm_helper() {
base_values_file="${helm_chart_dir}/try-kata-nvidia-gpu.values.yaml"
fi
;;
qemu-snp|qemu-snp-runtime-rs|qemu-tdx|qemu-se|qemu-se-runtime-rs|qemu-cca|qemu-coco-dev|qemu-coco-dev-runtime-rs)
qemu-snp|qemu-tdx|qemu-se|qemu-se-runtime-rs|qemu-cca|qemu-coco-dev|qemu-coco-dev-runtime-rs)
# Use TEE example file
if [[ -f "${helm_chart_dir}/try-kata-tee.values.yaml" ]]; then
base_values_file="${helm_chart_dir}/try-kata-tee.values.yaml"

View File

@@ -11,7 +11,7 @@ source "${BATS_TEST_DIRNAME}/../../common.bash"
load "${BATS_TEST_DIRNAME}/confidential_kbs.sh"
SUPPORTED_GPU_TEE_HYPERVISORS=("qemu-nvidia-gpu-snp" "qemu-nvidia-gpu-tdx")
SUPPORTED_TEE_HYPERVISORS=("qemu-snp" "qemu-snp-runtime-rs" "qemu-tdx" "qemu-se" "qemu-se-runtime-rs" "${SUPPORTED_GPU_TEE_HYPERVISORS[@]}")
SUPPORTED_TEE_HYPERVISORS=("qemu-snp" "qemu-tdx" "qemu-se" "qemu-se-runtime-rs" "${SUPPORTED_GPU_TEE_HYPERVISORS[@]}")
SUPPORTED_NON_TEE_HYPERVISORS=("qemu-coco-dev" "qemu-coco-dev-runtime-rs")
function setup_unencrypted_confidential_pod() {
@@ -36,7 +36,7 @@ function get_remote_command_per_hypervisor() {
qemu-se*)
echo "cd /sys/firmware/uv; cat prot_virt_guest | grep 1"
;;
qemu-snp|qemu-snp-runtime-rs)
qemu-snp)
echo "dmesg | grep \"Memory Encryption Features active:.*SEV-SNP\""
;;
qemu-tdx)

View File

@@ -187,7 +187,7 @@ function deploy_kata() {
# Workaround to avoid modifying the workflow yaml files
case "${KATA_HYPERVISOR}" in
qemu-tdx|qemu-snp|qemu-snp-runtime-rs|qemu-nvidia-gpu-*)
qemu-tdx|qemu-snp|qemu-nvidia-gpu-*)
USE_EXPERIMENTAL_SETUP_SNAPSHOTTER=true
SNAPSHOTTER="nydus"
EXPERIMENTAL_FORCE_GUEST_PULL=false
@@ -447,7 +447,7 @@ function cleanup() {
}
function deploy_snapshotter() {
if [[ "${KATA_HYPERVISOR}" == "qemu-tdx" || "${KATA_HYPERVISOR}" == "qemu-snp" || "${KATA_HYPERVISOR}" == "qemu-snp-runtime-rs" ]]; then
if [[ "${KATA_HYPERVISOR}" == "qemu-tdx" || "${KATA_HYPERVISOR}" == "qemu-snp" ]]; then
echo "[Skip] ${SNAPSHOTTER} is pre-installed in the TEE machine"
return
fi
@@ -461,7 +461,7 @@ function deploy_snapshotter() {
}
function cleanup_snapshotter() {
if [[ "${KATA_HYPERVISOR}" == "qemu-tdx" || "${KATA_HYPERVISOR}" == "qemu-snp" || "${KATA_HYPERVISOR}" == "qemu-snp-runtime-rs" ]]; then
if [[ "${KATA_HYPERVISOR}" == "qemu-tdx" || "${KATA_HYPERVISOR}" == "qemu-snp" ]]; then
echo "[Skip] ${SNAPSHOTTER} is pre-installed in the TEE machine"
return
fi

View File

@@ -146,22 +146,15 @@ setup() {
kbs_set_cpu0_resource_policy
# get measured artifacts from qemu command line of previous test
# Go runtime logs: "launching <path> with: [<args>]"
# runtime-rs logs: "qemu args: <args>"
log_line=$(sudo journalctl -r -x -t kata | grep -m 1 'launching.*qemu.*with:' || true)
if [[ -n "$log_line" ]]; then
qemu_cmd=$(echo "$log_line" | sed 's/.*with: \[\(.*\)\]".*/\1/')
else
log_line=$(sudo journalctl -r -x -t kata | grep -m 1 'qemu args:' || true)
qemu_cmd=$(echo "$log_line" | sed 's/.*qemu args: //')
fi
qemu_cmd=$(echo "$log_line" | sed 's/.*with: \[\(.*\)\]".*/\1/')
[[ -n "$qemu_cmd" ]] || { echo "Could not find QEMU command line"; return 1; }
kernel_path=$(echo "$qemu_cmd" | grep -oP -- '-kernel \K[^ ]+')
initrd_path=$(echo "$qemu_cmd" | grep -oP -- '-initrd \K[^ ]+' || true)
firmware_path=$(echo "$qemu_cmd" | grep -oP -- '-bios \K[^ ]+')
vcpu_count=$(echo "$qemu_cmd" | grep -oP -- '-smp \K\d+')
append=$(echo "$qemu_cmd" | grep -oP -- '-append \K.*?(?= -(smp|bios) )')
append=$(echo "$qemu_cmd" | sed -n 's/.*-append \(.*\) -bios.*/\1/p')
# Remove escape backslashes for quotes from output for dm-mod.create parameters
append="${append//\\\"/\"}"

View File

@@ -15,7 +15,7 @@ setup() {
[ "${KATA_HYPERVISOR}" == "qemu-se-runtime-rs" ] && skip "Requires CPU hotplug which isn't supported on ${KATA_HYPERVISOR} yet"
[[ "${KATA_HYPERVISOR}" == qemu-coco-dev* ]] && skip "Requires CPU hotplug which disabled by static_sandbox_resource_mgmt"
( [ "${KATA_HYPERVISOR}" == "qemu-tdx" ] || [ "${KATA_HYPERVISOR}" == "qemu-snp" ] || \
[ "${KATA_HYPERVISOR}" == "qemu-snp-runtime-rs" ] || [ "${KATA_HYPERVISOR}" == "qemu-se" ] ) \
[ "${KATA_HYPERVISOR}" == "qemu-se" ] ) \
&& skip "TEEs do not support memory / CPU hotplug"
pod_name="constraints-cpu-test"
@@ -121,7 +121,7 @@ teardown() {
[ "${KATA_HYPERVISOR}" == "qemu-se-runtime-rs" ] && skip "Requires CPU hotplug which isn't supported on ${KATA_HYPERVISOR} yet"
[[ "${KATA_HYPERVISOR}" == qemu-coco-dev* ]] && skip "Requires CPU hotplug which disabled by static_sandbox_resource_mgmt"
( [ "${KATA_HYPERVISOR}" == "qemu-tdx" ] || [ "${KATA_HYPERVISOR}" == "qemu-snp" ] || \
[ "${KATA_HYPERVISOR}" == "qemu-snp-runtime-rs" ] || [ "${KATA_HYPERVISOR}" == "qemu-se" ] ) \
[ "${KATA_HYPERVISOR}" == "qemu-se" ] ) \
&& skip "TEEs do not support memory / CPU hotplug"
# Debugging information

View File

@@ -9,18 +9,14 @@ load "${BATS_TEST_DIRNAME}/../../common.bash"
load "${BATS_TEST_DIRNAME}/lib.sh"
load "${BATS_TEST_DIRNAME}/tests_common.sh"
case "${KATA_HYPERVISOR}" in
*-runtime-rs)
shim_config_file="/opt/kata/share/defaults/kata-containers/runtime-rs/runtimes/${KATA_HYPERVISOR}/configuration-${KATA_HYPERVISOR}.toml"
;;
*)
shim_config_file="/opt/kata/share/defaults/kata-containers/runtimes/${KATA_HYPERVISOR}/configuration-${KATA_HYPERVISOR}.toml"
;;
esac
# Currently only the Go runtime provides the config path used here.
# If a Rust hypervisor runs this test, mirror the enabling_hypervisor
# pattern in tests/common.bash to select the correct runtime-rs config.
shim_config_file="/opt/kata/share/defaults/kata-containers/configuration-${KATA_HYPERVISOR}.toml"
check_and_skip() {
case "${KATA_HYPERVISOR}" in
qemu-tdx|qemu-coco-dev|qemu-snp|qemu-snp-runtime-rs)
qemu-tdx|qemu-coco-dev|qemu-snp)
if [ "$(uname -m)" == "s390x" ]; then
skip "measured rootfs tests not implemented for s390x"
fi

View File

@@ -138,7 +138,7 @@ add_runtime_handler_annotations() {
fi
case "${KATA_HYPERVISOR}" in
qemu-coco-dev | qemu-snp | qemu-snp-runtime-rs | qemu-tdx | qemu-coco-dev-runtime-rs)
qemu-coco-dev | qemu-snp | qemu-tdx | qemu-coco-dev-runtime-rs)
info "Add runtime handler annotations for ${KATA_HYPERVISOR}"
local handler_value="kata-${KATA_HYPERVISOR}"
for K8S_TEST_YAML in runtimeclass_workloads_work/*.yaml

View File

@@ -82,7 +82,7 @@ auto_generate_policy_enabled() {
is_coco_platform() {
case "${KATA_HYPERVISOR}" in
"qemu-tdx"|"qemu-snp"|"qemu-snp-runtime-rs"|"qemu-coco-dev"|"qemu-coco-dev-runtime-rs"|"qemu-nvidia-gpu-tdx"|"qemu-nvidia-gpu-snp")
"qemu-tdx"|"qemu-snp"|"qemu-coco-dev"|"qemu-coco-dev-runtime-rs"|"qemu-nvidia-gpu-tdx"|"qemu-nvidia-gpu-snp")
return 0
;;
*)
@@ -148,7 +148,7 @@ install_genpolicy_drop_ins() {
# 20-* OCI version overlay
if [[ "${KATA_HOST_OS:-}" == "cbl-mariner" ]]; then
cp "${examples_dir}/20-oci-1.2.0-drop-in.json" "${settings_d}/"
elif is_k3s_or_rke2 || is_nvidia_gpu_platform || [[ "${KATA_HYPERVISOR}" == "qemu-snp" ]] || [[ "${KATA_HYPERVISOR}" == "qemu-snp-runtime-rs" ]] || [[ "${KATA_HYPERVISOR}" == "qemu-tdx" ]] || [[ -n "${CONTAINER_ENGINE_VERSION:-}" ]]; then
elif is_k3s_or_rke2 || is_nvidia_gpu_platform || [[ "${KATA_HYPERVISOR}" == "qemu-snp" ]] || [[ "${KATA_HYPERVISOR}" == "qemu-tdx" ]] || [[ -n "${CONTAINER_ENGINE_VERSION:-}" ]]; then
cp "${examples_dir}/20-oci-1.3.0-drop-in.json" "${settings_d}/"
fi
@@ -340,7 +340,7 @@ hard_coded_policy_tests_enabled() {
# CI is testing hard-coded policies just on a the platforms listed here. Outside of CI,
# users can enable testing of the same policies (plus the auto-generated policies) by
# specifying AUTO_GENERATE_POLICY=yes.
local -r enabled_hypervisors=("qemu-coco-dev" "qemu-snp" "qemu-snp-runtime-rs" "qemu-tdx" "qemu-coco-dev-runtime-rs")
local -r enabled_hypervisors=("qemu-coco-dev" "qemu-snp" "qemu-tdx" "qemu-coco-dev-runtime-rs")
for enabled_hypervisor in "${enabled_hypervisors[@]}"
do
if [[ "${enabled_hypervisor}" == "${KATA_HYPERVISOR}" ]]; then

View File

@@ -440,14 +440,46 @@ fn add_kata_deploy_warning(config_file: &Path) -> Result<()> {
Ok(())
}
/// Atomically replace a file with a symlink.
///
/// Creates the symlink at a temporary path first, then renames it over the
/// original so the original is preserved if symlink creation fails.
fn atomic_symlink_replace(file_path: &str, symlink_target: &str) -> Result<()> {
let temp_symlink = format!("{}.tmp-link", file_path);
// Clean up any stale temp symlink from a previous interrupted run
if Path::new(&temp_symlink).exists() || Path::new(&temp_symlink).is_symlink() {
let _ = fs::remove_file(&temp_symlink);
}
std::os::unix::fs::symlink(symlink_target, &temp_symlink).with_context(|| {
format!(
"Failed to create temporary symlink {} -> {}",
temp_symlink, symlink_target
)
})?;
fs::rename(&temp_symlink, file_path).map_err(|err| {
let _ = fs::remove_file(&temp_symlink);
anyhow::anyhow!(
"Failed to atomically replace {} with symlink to {}: {}",
file_path,
symlink_target,
err
)
})?;
Ok(())
}
/// Set up the runtime directory structure for a shim.
/// Creates: {config_path}/runtimes/{shim}/
/// {config_path}/runtimes/{shim}/config.d/
/// {config_path}/runtimes/{shim}/configuration-{shim}.toml (copy of original)
///
/// Note: We copy the config file instead of symlinking because kata-containers'
/// ResolvePath uses filepath.EvalSymlinks, which would resolve to the original
/// location and look for config.d there instead of in our per-shim directory.
/// After copying, the original config file is replaced with a symlink pointing
/// to the runtime copy. This way the runtime's ResolvePath / EvalSymlinks resolves
/// the symlink and finds config.d next to the real file in the per-shim directory.
fn setup_runtime_directory(config: &Config, shim: &str) -> Result<()> {
let original_config_dir = format!(
"/host{}",
@@ -466,9 +498,9 @@ fn setup_runtime_directory(config: &Config, shim: &str) -> Result<()> {
fs::create_dir_all(&config_d_dir)
.with_context(|| format!("Failed to create config.d directory: {}", config_d_dir))?;
// Copy the original config file to the runtime directory
let original_config_file = format!("{}/configuration-{}.toml", original_config_dir, shim);
let dest_config_file = format!("{}/configuration-{}.toml", runtime_config_dir, shim);
let config_filename = format!("configuration-{}.toml", shim);
let original_config_file = format!("{}/{}", original_config_dir, config_filename);
let dest_config_file = format!("{}/{}", runtime_config_dir, config_filename);
// Only copy if original exists
if Path::new(&original_config_file).exists() {
@@ -481,7 +513,7 @@ fn setup_runtime_directory(config: &Config, shim: &str) -> Result<()> {
})?;
}
// Copy the base config file
// Copy the base config file to the runtime directory
fs::copy(&original_config_file, &dest_config_file).with_context(|| {
format!(
"Failed to copy config: {} -> {}",
@@ -493,13 +525,37 @@ fn setup_runtime_directory(config: &Config, shim: &str) -> Result<()> {
add_kata_deploy_warning(Path::new(&dest_config_file))?;
info!(" Copied base config: {}", dest_config_file);
let symlink_target = format!("runtimes/{}/{}", shim, config_filename);
atomic_symlink_replace(&original_config_file, &symlink_target)?;
info!(
" Symlinked original config: {} -> {}",
original_config_file, symlink_target
);
}
Ok(())
}
/// Remove the runtime directory for a shim during cleanup
/// Remove the runtime directory for a shim during cleanup.
/// Also removes the symlink at the original config location that was created
/// by setup_runtime_directory.
fn remove_runtime_directory(config: &Config, shim: &str) -> Result<()> {
// Remove the symlink at the original config location (if present)
let original_config_dir = format!(
"/host{}",
utils::get_kata_containers_original_config_path(shim, &config.dest_dir)
);
let original_config_file = format!("{}/configuration-{}.toml", original_config_dir, shim);
let original_path = Path::new(&original_config_file);
if original_path.is_symlink() {
fs::remove_file(&original_config_file).with_context(|| {
format!("Failed to remove config symlink: {}", original_config_file)
})?;
log::debug!("Removed config symlink: {}", original_config_file);
}
let runtime_config_dir = format!(
"/host{}",
utils::get_kata_containers_config_path(shim, &config.dest_dir)
@@ -528,7 +584,7 @@ fn remove_runtime_directory(config: &Config, shim: &str) -> Result<()> {
}
async fn configure_shim_config(config: &Config, shim: &str, container_runtime: &str) -> Result<()> {
// Set up the runtime directory structure with symlink to original config
// Set up the runtime directory: copy config to per-shim dir and replace original with symlink
setup_runtime_directory(config, shim)?;
let runtime_config_dir = format!(
@@ -540,11 +596,11 @@ async fn configure_shim_config(config: &Config, shim: &str, container_runtime: &
let kata_config_file =
Path::new(&runtime_config_dir).join(format!("configuration-{shim}.toml"));
// The configuration file (symlink) should exist after setup_runtime_directory()
// The configuration file should exist after setup_runtime_directory()
if !kata_config_file.exists() {
return Err(anyhow::anyhow!(
"Configuration file not found: {kata_config_file:?}. This file should have been \
symlinked from the original config. Check that the shim '{}' has a valid configuration \
copied from the original config. Check that the shim '{}' has a valid configuration \
file in the artifacts.",
shim
));
@@ -1144,4 +1200,141 @@ mod tests {
"following the symlink should yield the real content"
);
}
#[test]
fn test_atomic_symlink_replace_creates_symlink() {
let tmpdir = tempfile::tempdir().unwrap();
// Create the original file and the target it will point to
let target_dir = tmpdir.path().join("runtimes/qemu");
fs::create_dir_all(&target_dir).unwrap();
let target_file = target_dir.join("configuration-qemu.toml");
fs::write(&target_file, "real config content").unwrap();
let original = tmpdir.path().join("configuration-qemu.toml");
fs::write(&original, "original content").unwrap();
let symlink_target = "runtimes/qemu/configuration-qemu.toml";
atomic_symlink_replace(original.to_str().unwrap(), symlink_target).unwrap();
assert!(original.is_symlink(), "original should now be a symlink");
assert_eq!(
fs::read_link(&original).unwrap().to_str().unwrap(),
symlink_target
);
assert_eq!(
fs::read_to_string(&original).unwrap(),
"real config content",
"reading through the symlink should yield the target's content"
);
}
#[test]
fn test_atomic_symlink_replace_is_idempotent() {
let tmpdir = tempfile::tempdir().unwrap();
let target_dir = tmpdir.path().join("runtimes/qemu");
fs::create_dir_all(&target_dir).unwrap();
let target_file = target_dir.join("configuration-qemu.toml");
fs::write(&target_file, "config content").unwrap();
let original = tmpdir.path().join("configuration-qemu.toml");
fs::write(&original, "original").unwrap();
let symlink_target = "runtimes/qemu/configuration-qemu.toml";
// First call
atomic_symlink_replace(original.to_str().unwrap(), symlink_target).unwrap();
assert!(original.is_symlink());
// Second call (e.g. re-install) should succeed and still be a valid symlink
atomic_symlink_replace(original.to_str().unwrap(), symlink_target).unwrap();
assert!(original.is_symlink());
assert_eq!(
fs::read_link(&original).unwrap().to_str().unwrap(),
symlink_target
);
}
#[test]
fn test_atomic_symlink_replace_cleans_stale_temp() {
let tmpdir = tempfile::tempdir().unwrap();
let original = tmpdir.path().join("configuration-qemu.toml");
fs::write(&original, "original").unwrap();
// Simulate a stale temp symlink from an interrupted previous run
let stale_temp = tmpdir.path().join("configuration-qemu.toml.tmp-link");
std::os::unix::fs::symlink("stale-target", &stale_temp).unwrap();
assert!(stale_temp.is_symlink());
let target_dir = tmpdir.path().join("runtimes/qemu");
fs::create_dir_all(&target_dir).unwrap();
fs::write(target_dir.join("configuration-qemu.toml"), "content").unwrap();
let symlink_target = "runtimes/qemu/configuration-qemu.toml";
atomic_symlink_replace(original.to_str().unwrap(), symlink_target).unwrap();
assert!(original.is_symlink());
assert_eq!(
fs::read_link(&original).unwrap().to_str().unwrap(),
symlink_target
);
// Temp file should not linger
assert!(!stale_temp.exists() && !stale_temp.is_symlink());
}
#[test]
fn test_setup_and_remove_runtime_directory_symlink() {
let tmpdir = tempfile::tempdir().unwrap();
// Simulate the directory layout that setup_runtime_directory expects
// (after copy_artifacts has run), using a Go shim as example.
let defaults_dir = tmpdir.path().join("share/defaults/kata-containers");
fs::create_dir_all(&defaults_dir).unwrap();
let config_filename = "configuration-qemu.toml";
let original_config = defaults_dir.join(config_filename);
fs::write(
&original_config,
"[hypervisor.qemu]\npath = \"/usr/bin/qemu\"",
)
.unwrap();
// Create the runtime directory and copy the config (mimics setup_runtime_directory)
let runtime_dir = defaults_dir.join("runtimes/qemu");
let config_d_dir = runtime_dir.join("config.d");
fs::create_dir_all(&config_d_dir).unwrap();
let dest_config = runtime_dir.join(config_filename);
fs::copy(&original_config, &dest_config).unwrap();
// Atomically replace the original with a symlink
let symlink_target = format!("runtimes/qemu/{}", config_filename);
atomic_symlink_replace(original_config.to_str().unwrap(), &symlink_target).unwrap();
// Verify: original is now a symlink
assert!(original_config.is_symlink());
assert_eq!(
fs::read_link(&original_config).unwrap().to_str().unwrap(),
symlink_target
);
// Verify: reading through the symlink yields the real file content
assert_eq!(
fs::read_to_string(&original_config).unwrap(),
fs::read_to_string(&dest_config).unwrap()
);
// Verify: config.d is next to the real file (the resolved path)
assert!(dest_config.parent().unwrap().join("config.d").is_dir());
// Simulate remove_runtime_directory: remove symlink then runtime dir
assert!(original_config.is_symlink());
fs::remove_file(&original_config).unwrap();
assert!(!original_config.exists() && !original_config.is_symlink());
fs::remove_dir_all(&runtime_dir).unwrap();
assert!(!runtime_dir.exists());
}
}