diff --git a/src/runtime-rs/Cargo.lock b/src/runtime-rs/Cargo.lock index 7cb064508e..f9e199efc8 100644 --- a/src/runtime-rs/Cargo.lock +++ b/src/runtime-rs/Cargo.lock @@ -293,7 +293,7 @@ checksum = "ec3245a0ca564e7f3c797d20d833a6870f57a728ac967d5225b3ffdef4465011" dependencies = [ "lazy_static", "log", - "rand", + "rand 0.8.5", ] [[package]] @@ -321,6 +321,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + [[package]] name = "futures" version = "0.1.31" @@ -1066,6 +1072,29 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" +dependencies = [ + "libc", + "rand 0.4.6", +] + +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +dependencies = [ + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi", +] + [[package]] name = "rand" version = "0.8.5" @@ -1074,7 +1103,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", - "rand_core", + "rand_core 0.6.3", ] [[package]] @@ -1084,9 +1113,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.3", ] +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + [[package]] name = "rand_core" version = "0.6.3" @@ -1096,6 +1140,15 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + [[package]] name = "redox_syscall" version = "0.2.11" @@ -1135,6 +1188,7 @@ dependencies = [ name = "resource" version = "0.1.0" dependencies = [ + "agent", "anyhow", "async-trait", "cgroups-rs", @@ -1148,7 +1202,9 @@ dependencies = [ "nix 0.16.1", "oci", "slog", + "slog-scope", "tokio", + "uuid", ] [[package]] @@ -1306,7 +1362,7 @@ dependencies = [ "nix 0.23.1", "oci", "protobuf", - "rand", + "rand 0.8.5", "serial_test", "service", "sha2", @@ -1472,7 +1528,7 @@ dependencies = [ name = "tests_utils" version = "0.1.0" dependencies = [ - "rand", + "rand 0.8.5", ] [[package]] @@ -1692,6 +1748,15 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "uuid" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cfec50b0842181ba6e713151b72f4ec84a6a7e2c9c8a8a3ffc37bb1cd16b231" +dependencies = [ + "rand 0.3.23", +] + [[package]] name = "vcpkg" version = "0.2.15" diff --git a/src/runtime-rs/crates/resource/Cargo.toml b/src/runtime-rs/crates/resource/Cargo.toml index 743575329b..115d4fc56b 100644 --- a/src/runtime-rs/crates/resource/Cargo.toml +++ b/src/runtime-rs/crates/resource/Cargo.toml @@ -13,8 +13,11 @@ libc = ">=0.2.39" log = "^0.4.0" nix = "0.16.0" slog = "2.5.2" -tokio = { version = "1.8.0", features = ["sync"] } +slog-scope = "4.4.0" +tokio = { version = "1.8.0", features = ["process"] } +uuid = { version = "0.4", features = ["v4"] } +agent = { path = "../agent" } hypervisor = { path = "../hypervisor" } kata-types = { path = "../../../libs/kata-types" } kata-sys-util = { path = "../../../libs/kata-sys-util" } diff --git a/src/runtime-rs/crates/resource/src/lib.rs b/src/runtime-rs/crates/resource/src/lib.rs index 8609c82214..86dc71fe78 100644 --- a/src/runtime-rs/crates/resource/src/lib.rs +++ b/src/runtime-rs/crates/resource/src/lib.rs @@ -4,4 +4,25 @@ // SPDX-License-Identifier: Apache-2.0 // +#[macro_use] +extern crate lazy_static; + +#[macro_use] +extern crate slog; + +logging::logger_with_subsystem!(sl, "resource"); + pub mod cgroups; +pub mod manager; +mod manager_inner; +pub mod rootfs; +pub mod share_fs; +pub mod volume; +pub use manager::ResourceManager; + +use kata_types::config::hypervisor::SharedFsInfo; + +#[derive(Debug)] +pub enum ResourceConfig { + ShareFs(SharedFsInfo), +} diff --git a/src/runtime-rs/crates/resource/src/manager.rs b/src/runtime-rs/crates/resource/src/manager.rs new file mode 100644 index 0000000000..f5db93a175 --- /dev/null +++ b/src/runtime-rs/crates/resource/src/manager.rs @@ -0,0 +1,92 @@ +// Copyright (c) 2019-2022 Alibaba Cloud +// Copyright (c) 2019-2022 Ant Group +// +// SPDX-License-Identifier: Apache-2.0 +// + +use std::sync::Arc; + +use agent::{Agent, Storage}; +use anyhow::Result; +use hypervisor::Hypervisor; +use kata_types::config::TomlConfig; +use kata_types::mount::Mount; +use oci::LinuxResources; +use tokio::sync::RwLock; + +use crate::{manager_inner::ResourceManagerInner, rootfs::Rootfs, volume::Volume, ResourceConfig}; + +pub struct ResourceManager { + inner: Arc>, +} + +impl ResourceManager { + pub fn new( + sid: &str, + agent: Arc, + hypervisor: Arc, + toml_config: &TomlConfig, + ) -> Result { + Ok(Self { + inner: Arc::new(RwLock::new(ResourceManagerInner::new( + sid, + agent, + hypervisor, + toml_config, + )?)), + }) + } + + pub async fn prepare_before_start_vm(&self, device_configs: Vec) -> Result<()> { + let mut inner = self.inner.write().await; + inner.prepare_before_start_vm(device_configs).await + } + + pub async fn setup_after_start_vm(&self) -> Result<()> { + let mut inner = self.inner.write().await; + inner.setup_after_start_vm().await + } + + pub async fn get_storage_for_sandbox(&self) -> Result> { + let inner = self.inner.read().await; + inner.get_storage_for_sandbox().await + } + + pub async fn handler_rootfs( + &self, + cid: &str, + bundle_path: &str, + rootfs_mounts: &[Mount], + ) -> Result> { + let inner = self.inner.read().await; + inner.handler_rootfs(cid, bundle_path, rootfs_mounts).await + } + + pub async fn handler_volumes( + &self, + cid: &str, + oci_mounts: &[oci::Mount], + ) -> Result>> { + let inner = self.inner.read().await; + inner.handler_volumes(cid, oci_mounts).await + } + + pub async fn dump(&self) { + let inner = self.inner.read().await; + inner.dump().await + } + + pub async fn update_cgroups( + &self, + cid: &str, + linux_resources: Option<&LinuxResources>, + ) -> Result<()> { + let inner = self.inner.read().await; + inner.update_cgroups(cid, linux_resources).await + } + + pub async fn delete_cgroups(&self) -> Result<()> { + let inner = self.inner.read().await; + inner.delete_cgroups().await + } +} diff --git a/src/runtime-rs/crates/resource/src/manager_inner.rs b/src/runtime-rs/crates/resource/src/manager_inner.rs new file mode 100644 index 0000000000..7b7cfea4fd --- /dev/null +++ b/src/runtime-rs/crates/resource/src/manager_inner.rs @@ -0,0 +1,134 @@ +// Copyright (c) 2019-2022 Alibaba Cloud +// Copyright (c) 2019-2022 Ant Group +// +// SPDX-License-Identifier: Apache-2.0 +// + +use std::sync::Arc; + +use agent::{Agent, Storage}; +use anyhow::{Context, Result}; +use hypervisor::Hypervisor; +use kata_types::config::TomlConfig; +use kata_types::mount::Mount; +use oci::LinuxResources; + +use crate::{ + cgroups::CgroupsResource, + rootfs::{RootFsResource, Rootfs}, + share_fs::{self, ShareFs}, + volume::{Volume, VolumeResource}, + ResourceConfig, +}; + +pub(crate) struct ResourceManagerInner { + sid: String, + // TODO: remove + #[allow(dead_code)] + agent: Arc, + hypervisor: Arc, + share_fs: Option>, + + pub rootfs_resource: RootFsResource, + pub volume_resource: VolumeResource, + pub cgroups_resource: CgroupsResource, +} + +impl ResourceManagerInner { + pub(crate) fn new( + sid: &str, + agent: Arc, + hypervisor: Arc, + toml_config: &TomlConfig, + ) -> Result { + Ok(Self { + sid: sid.to_string(), + agent, + hypervisor, + share_fs: None, + rootfs_resource: RootFsResource::new(), + volume_resource: VolumeResource::new(), + cgroups_resource: CgroupsResource::new(sid, toml_config)?, + }) + } + + pub async fn prepare_before_start_vm( + &mut self, + device_configs: Vec, + ) -> Result<()> { + for dc in device_configs { + match dc { + ResourceConfig::ShareFs(c) => { + let share_fs = share_fs::new(&self.sid, &c).context("new share fs")?; + share_fs + .setup_device_before_start_vm(self.hypervisor.as_ref()) + .await + .context("setup share fs device before start vm")?; + self.share_fs = Some(share_fs); + } + }; + } + + Ok(()) + } + + pub async fn setup_after_start_vm(&mut self) -> Result<()> { + if let Some(share_fs) = self.share_fs.as_ref() { + share_fs + .setup_device_after_start_vm(self.hypervisor.as_ref()) + .await + .context("setup share fs device after start vm")?; + } + + Ok(()) + } + + pub async fn get_storage_for_sandbox(&self) -> Result> { + let mut storages = vec![]; + if let Some(d) = self.share_fs.as_ref() { + let mut s = d.get_storages().await.context("get storage")?; + storages.append(&mut s); + } + Ok(storages) + } + + pub async fn handler_rootfs( + &self, + cid: &str, + bundle_path: &str, + rootfs_mounts: &[Mount], + ) -> Result> { + self.rootfs_resource + .handler_rootfs(&self.share_fs, cid, bundle_path, rootfs_mounts) + .await + } + + pub async fn handler_volumes( + &self, + cid: &str, + oci_mounts: &[oci::Mount], + ) -> Result>> { + self.volume_resource + .handler_volumes(&self.share_fs, cid, oci_mounts) + .await + } + + pub async fn update_cgroups( + &self, + cid: &str, + linux_resources: Option<&LinuxResources>, + ) -> Result<()> { + self.cgroups_resource + .update_cgroups(cid, linux_resources, self.hypervisor.as_ref()) + .await + } + + pub async fn delete_cgroups(&self) -> Result<()> { + self.cgroups_resource.delete().await + } + + pub async fn dump(&self) { + self.rootfs_resource.dump().await; + self.volume_resource.dump().await; + } +} diff --git a/src/runtime-rs/crates/resource/src/rootfs/mod.rs b/src/runtime-rs/crates/resource/src/rootfs/mod.rs new file mode 100644 index 0000000000..4063c8bf82 --- /dev/null +++ b/src/runtime-rs/crates/resource/src/rootfs/mod.rs @@ -0,0 +1,123 @@ +// Copyright (c) 2019-2022 Alibaba Cloud +// Copyright (c) 2019-2022 Ant Group +// +// SPDX-License-Identifier: Apache-2.0 +// + +mod share_fs_rootfs; + +use std::{sync::Arc, vec::Vec}; + +use anyhow::{anyhow, Context, Result}; +use async_trait::async_trait; +use kata_types::mount::Mount; +use log::{error, info}; +use nix::sys::stat::{self, SFlag}; +use tokio::sync::RwLock; + +use crate::share_fs::ShareFs; + +const ROOTFS: &str = "rootfs"; + +#[async_trait] +pub trait Rootfs: Send + Sync { + async fn get_guest_rootfs_path(&self) -> Result; + async fn get_rootfs_mount(&self) -> Result>; +} + +#[derive(Default)] +struct RootFsResourceInner { + rootfs: Vec>, +} + +pub struct RootFsResource { + inner: Arc>, +} + +impl Default for RootFsResource { + fn default() -> Self { + Self::new() + } +} + +impl RootFsResource { + pub fn new() -> Self { + Self { + inner: Arc::new(RwLock::new(RootFsResourceInner::default())), + } + } + + pub async fn handler_rootfs( + &self, + share_fs: &Option>, + cid: &str, + bundle_path: &str, + rootfs_mounts: &[Mount], + ) -> Result> { + match rootfs_mounts { + mounts_vec if is_single_layer_rootfs(mounts_vec) => { + // Safe as single_layer_rootfs must have one layer + let layer = &mounts_vec[0]; + + let rootfs = if let Some(_dev_id) = get_block_device(&layer.source) { + // block rootfs + unimplemented!() + } else if let Some(share_fs) = share_fs { + // share fs rootfs + let share_fs_mount = share_fs.get_share_fs_mount(); + share_fs_rootfs::ShareFsRootfs::new(&share_fs_mount, cid, bundle_path, layer) + .await + .context("new share fs rootfs")? + } else { + return Err(anyhow!("unsupported rootfs {:?}", &layer)); + }; + + let mut inner = self.inner.write().await; + let r = Arc::new(rootfs); + inner.rootfs.push(r.clone()); + Ok(r) + } + _ => { + return Err(anyhow!( + "unsupported rootfs mounts count {}", + rootfs_mounts.len() + )) + } + } + } + + pub async fn dump(&self) { + let inner = self.inner.read().await; + for r in &inner.rootfs { + info!( + "rootfs {:?}: count {}", + r.get_guest_rootfs_path().await, + Arc::strong_count(r) + ); + } + } +} + +fn is_single_layer_rootfs(rootfs_mounts: &[Mount]) -> bool { + rootfs_mounts.len() == 1 +} + +fn get_block_device(file_path: &str) -> Option { + if file_path.is_empty() { + return None; + } + + match stat::stat(file_path) { + Ok(fstat) => { + if SFlag::from_bits_truncate(fstat.st_mode) == SFlag::S_IFBLK { + return Some(fstat.st_rdev); + } + } + Err(err) => { + error!("failed to stat for {} {:?}", file_path, err); + return None; + } + }; + + None +} diff --git a/src/runtime-rs/crates/resource/src/rootfs/share_fs_rootfs.rs b/src/runtime-rs/crates/resource/src/rootfs/share_fs_rootfs.rs new file mode 100644 index 0000000000..643af13fed --- /dev/null +++ b/src/runtime-rs/crates/resource/src/rootfs/share_fs_rootfs.rs @@ -0,0 +1,59 @@ +// Copyright (c) 2019-2022 Alibaba Cloud +// Copyright (c) 2019-2022 Ant Group +// +// SPDX-License-Identifier: Apache-2.0 +// + +use std::sync::Arc; + +use anyhow::{Context, Result}; +use async_trait::async_trait; +use kata_sys_util::mount::Mounter; +use kata_types::mount::Mount; + +use super::{Rootfs, ROOTFS}; +use crate::share_fs::{ShareFsMount, ShareFsRootfsConfig}; + +pub(crate) struct ShareFsRootfs { + guest_path: String, +} + +impl ShareFsRootfs { + pub async fn new( + share_fs_mount: &Arc, + cid: &str, + bundle_path: &str, + rootfs: &Mount, + ) -> Result { + let bundle_rootfs = format!("{}/{}", bundle_path, ROOTFS); + rootfs.mount(&bundle_rootfs).context(format!( + "mount rootfs from {:?} to {}", + &rootfs, &bundle_rootfs + ))?; + + let mount_result = share_fs_mount + .share_rootfs(ShareFsRootfsConfig { + cid: cid.to_string(), + source: bundle_rootfs.to_string(), + target: ROOTFS.to_string(), + readonly: false, + }) + .await + .context("share rootfs")?; + + Ok(ShareFsRootfs { + guest_path: mount_result.guest_path, + }) + } +} + +#[async_trait] +impl Rootfs for ShareFsRootfs { + async fn get_guest_rootfs_path(&self) -> Result { + Ok(self.guest_path.clone()) + } + + async fn get_rootfs_mount(&self) -> Result> { + todo!() + } +} diff --git a/src/runtime-rs/crates/resource/src/share_fs/mod.rs b/src/runtime-rs/crates/resource/src/share_fs/mod.rs new file mode 100644 index 0000000000..36f4f1ec26 --- /dev/null +++ b/src/runtime-rs/crates/resource/src/share_fs/mod.rs @@ -0,0 +1,78 @@ +// Copyright (c) 2019-2022 Alibaba Cloud +// Copyright (c) 2019-2022 Ant Group +// +// SPDX-License-Identifier: Apache-2.0 +// + +mod share_virtio_fs; +mod share_virtio_fs_inline; +use share_virtio_fs_inline::ShareVirtioFsInline; +mod share_virtio_fs_standalone; +use share_virtio_fs_standalone::ShareVirtioFsStandalone; +mod utils; +mod virtio_fs_share_mount; +use virtio_fs_share_mount::VirtiofsShareMount; + +use std::sync::Arc; + +use agent::Storage; +use anyhow::{anyhow, Context, Result}; +use async_trait::async_trait; +use hypervisor::Hypervisor; +use kata_types::config::hypervisor::SharedFsInfo; + +const VIRTIO_FS: &str = "virtio-fs"; +const INLINE_VIRTIO_FS: &str = "inline-virtio-fs"; + +const KATA_HOST_SHARED_DIR: &str = "/run/kata-containers/shared/sandboxes/"; +const KATA_GUEST_SHARE_DIR: &str = "/run/kata-containers/shared/containers/"; +pub(crate) const DEFAULT_KATA_GUEST_SANDBOX_DIR: &str = "/run/kata-containers/sandbox/"; + +const PASSTHROUGH_FS_DIR: &str = "passthrough"; + +#[async_trait] +pub trait ShareFs: Send + Sync { + fn get_share_fs_mount(&self) -> Arc; + async fn setup_device_before_start_vm(&self, h: &dyn Hypervisor) -> Result<()>; + async fn setup_device_after_start_vm(&self, h: &dyn Hypervisor) -> Result<()>; + async fn get_storages(&self) -> Result>; +} + +pub struct ShareFsRootfsConfig { + // TODO: for nydus v5/v6 need to update ShareFsMount + pub cid: String, + pub source: String, + pub target: String, + pub readonly: bool, +} + +pub struct ShareFsVolumeConfig { + pub cid: String, + pub source: String, + pub target: String, + pub readonly: bool, +} + +pub struct ShareFsMountResult { + pub guest_path: String, +} + +#[async_trait] +pub trait ShareFsMount: Send + Sync { + async fn share_rootfs(&self, config: ShareFsRootfsConfig) -> Result; + async fn share_volume(&self, config: ShareFsVolumeConfig) -> Result; +} + +pub fn new(id: &str, config: &SharedFsInfo) -> Result> { + let shared_fs = config.shared_fs.clone(); + let shared_fs = shared_fs.unwrap_or_default(); + match shared_fs.as_str() { + INLINE_VIRTIO_FS => Ok(Arc::new( + ShareVirtioFsInline::new(id, config).context("new inline virtio fs")?, + )), + VIRTIO_FS => Ok(Arc::new( + ShareVirtioFsStandalone::new(id, config).context("new standalone virtio fs")?, + )), + _ => Err(anyhow!("unsupported shred fs {:?}", &shared_fs)), + } +} diff --git a/src/runtime-rs/crates/resource/src/share_fs/share_virtio_fs.rs b/src/runtime-rs/crates/resource/src/share_fs/share_virtio_fs.rs new file mode 100644 index 0000000000..364614dfd8 --- /dev/null +++ b/src/runtime-rs/crates/resource/src/share_fs/share_virtio_fs.rs @@ -0,0 +1,53 @@ +// Copyright (c) 2019-2022 Alibaba Cloud +// Copyright (c) 2019-2022 Ant Group +// +// SPDX-License-Identifier: Apache-2.0 +// + +use std::path::Path; + +use anyhow::{Context, Result}; +use hypervisor::{device, Hypervisor}; +use kata_sys_util::mount; + +use super::utils; + +pub(crate) const MOUNT_GUEST_TAG: &str = "kataShared"; +pub(crate) const PASSTHROUGH_FS_DIR: &str = "passthrough"; + +pub(crate) const FS_TYPE_VIRTIO_FS: &str = "virtio_fs"; +pub(crate) const KATA_VIRTIO_FS_DEV_TYPE: &str = "virtio-fs"; + +const VIRTIO_FS_SOCKET: &str = "virtiofsd.sock"; + +pub(crate) fn generate_sock_path(root: &str) -> String { + let socket_path = Path::new(root).join(VIRTIO_FS_SOCKET); + socket_path.to_str().unwrap().to_string() +} + +pub(crate) async fn prepare_virtiofs( + h: &dyn Hypervisor, + fs_type: &str, + id: &str, + root: &str, +) -> Result<()> { + let host_ro_dest = utils::get_host_ro_shared_path(id); + utils::ensure_dir_exist(&host_ro_dest)?; + + let host_rw_dest = utils::get_host_rw_shared_path(id); + utils::ensure_dir_exist(&host_rw_dest)?; + + mount::bind_mount_unchecked(&host_rw_dest, &host_ro_dest, true) + .context("bind mount shared_fs directory")?; + + let share_fs_device = device::Device::ShareFsDevice(device::ShareFsDeviceConfig { + sock_path: generate_sock_path(root), + mount_tag: String::from(MOUNT_GUEST_TAG), + host_path: String::from(host_ro_dest.to_str().unwrap()), + fs_type: fs_type.to_string(), + queue_size: 0, + queue_num: 0, + }); + h.add_device(share_fs_device).await.context("add device")?; + Ok(()) +} diff --git a/src/runtime-rs/crates/resource/src/share_fs/share_virtio_fs_inline.rs b/src/runtime-rs/crates/resource/src/share_fs/share_virtio_fs_inline.rs new file mode 100644 index 0000000000..146efc6094 --- /dev/null +++ b/src/runtime-rs/crates/resource/src/share_fs/share_virtio_fs_inline.rs @@ -0,0 +1,114 @@ +// Copyright (c) 2019-2022 Alibaba Cloud +// Copyright (c) 2019-2022 Ant Group +// +// SPDX-License-Identifier: Apache-2.0 +// + +use agent::Storage; +use anyhow::{Context, Result}; +use async_trait::async_trait; +use hypervisor::{ + device::{Device as HypervisorDevice, ShareFsMountConfig, ShareFsMountType, ShareFsOperation}, + Hypervisor, +}; +use kata_types::config::hypervisor::SharedFsInfo; + +use super::{ + share_virtio_fs::{ + prepare_virtiofs, FS_TYPE_VIRTIO_FS, KATA_VIRTIO_FS_DEV_TYPE, MOUNT_GUEST_TAG, + PASSTHROUGH_FS_DIR, + }, + utils, ShareFs, *, +}; + +lazy_static! { + pub(crate) static ref SHARED_DIR_VIRTIO_FS_OPTIONS: Vec:: = vec![ + String::from("default_permissions,allow_other,rootmode=040000,user_id=0,group_id=0"), + String::from("nodev"), + ]; +} + +#[derive(Debug, Clone)] +pub struct ShareVirtioFsInlineConfig { + pub id: String, +} + +pub struct ShareVirtioFsInline { + config: ShareVirtioFsInlineConfig, + share_fs_mount: Arc, +} + +impl ShareVirtioFsInline { + pub(crate) fn new(id: &str, _config: &SharedFsInfo) -> Result { + Ok(Self { + config: ShareVirtioFsInlineConfig { id: id.to_string() }, + share_fs_mount: Arc::new(VirtiofsShareMount::new(id)), + }) + } +} + +#[async_trait] +impl ShareFs for ShareVirtioFsInline { + fn get_share_fs_mount(&self) -> Arc { + self.share_fs_mount.clone() + } + + async fn setup_device_before_start_vm(&self, h: &dyn Hypervisor) -> Result<()> { + prepare_virtiofs(h, INLINE_VIRTIO_FS, &self.config.id, "") + .await + .context("prepare virtiofs")?; + Ok(()) + } + + async fn setup_device_after_start_vm(&self, h: &dyn Hypervisor) -> Result<()> { + setup_inline_virtiofs(&self.config.id, h) + .await + .context("setup inline virtiofs")?; + Ok(()) + } + async fn get_storages(&self) -> Result> { + // setup storage + let mut storages: Vec = Vec::new(); + + let mut shared_options = SHARED_DIR_VIRTIO_FS_OPTIONS.clone(); + shared_options.push(format!("tag={}", MOUNT_GUEST_TAG)); + + let shared_volume: Storage = Storage { + driver: String::from(KATA_VIRTIO_FS_DEV_TYPE), + driver_options: Vec::new(), + source: String::from(MOUNT_GUEST_TAG), + fs_type: String::from(FS_TYPE_VIRTIO_FS), + options: shared_options, + mount_point: String::from(KATA_GUEST_SHARE_DIR), + }; + + storages.push(shared_volume); + Ok(storages) + } +} + +async fn setup_inline_virtiofs(id: &str, h: &dyn Hypervisor) -> Result<()> { + // - source is the absolute path of PASSTHROUGH_FS_DIR on host, e.g. + // /run/kata-containers/shared/sandboxes//passthrough + // - mount point is the path relative to KATA_GUEST_SHARE_DIR in guest + let mnt = format!("/{}", PASSTHROUGH_FS_DIR); + + let rw_source = utils::get_host_rw_shared_path(id).join(PASSTHROUGH_FS_DIR); + utils::ensure_dir_exist(&rw_source)?; + + let ro_source = utils::get_host_ro_shared_path(id).join(PASSTHROUGH_FS_DIR); + let source = String::from(ro_source.to_str().unwrap()); + + let virtio_fs = HypervisorDevice::ShareFsMount(ShareFsMountConfig { + source: source.clone(), + fstype: ShareFsMountType::PASSTHROUGH, + mount_point: mnt, + config: None, + tag: String::from(MOUNT_GUEST_TAG), + op: ShareFsOperation::Mount, + prefetch_list_path: None, + }); + h.add_device(virtio_fs) + .await + .context(format!("fail to attach passthrough fs {:?}", source)) +} diff --git a/src/runtime-rs/crates/resource/src/share_fs/share_virtio_fs_standalone.rs b/src/runtime-rs/crates/resource/src/share_fs/share_virtio_fs_standalone.rs new file mode 100644 index 0000000000..b6f143dcd3 --- /dev/null +++ b/src/runtime-rs/crates/resource/src/share_fs/share_virtio_fs_standalone.rs @@ -0,0 +1,178 @@ +// Copyright (c) 2019-2022 Alibaba Cloud +// Copyright (c) 2019-2022 Ant Group +// +// SPDX-License-Identifier: Apache-2.0 +// + +use std::{process::Stdio, sync::Arc}; + +use agent::Storage; +use anyhow::{anyhow, Context, Result}; +use async_trait::async_trait; +use hypervisor::Hypervisor; +use kata_types::config::hypervisor::SharedFsInfo; +use tokio::{ + io::{AsyncBufReadExt, BufReader}, + process::{Child, Command}, + sync::{ + mpsc::{channel, Receiver, Sender}, + RwLock, + }, +}; + +use super::{ + share_virtio_fs::generate_sock_path, utils::get_host_ro_shared_path, + virtio_fs_share_mount::VirtiofsShareMount, ShareFs, ShareFsMount, +}; + +#[derive(Debug, Clone)] +pub struct ShareVirtioFsStandaloneConfig { + id: String, + jail_root: String, + + // virtio_fs_daemon is the virtio-fs vhost-user daemon path + pub virtio_fs_daemon: String, + // virtio_fs_cache cache mode for fs version cache or "none" + pub virtio_fs_cache: String, + // virtio_fs_extra_args passes options to virtiofsd daemon + pub virtio_fs_extra_args: Vec, +} + +#[derive(Default)] +struct ShareVirtioFsStandaloneInner { + pid: Option, +} +pub(crate) struct ShareVirtioFsStandalone { + inner: Arc>, + config: ShareVirtioFsStandaloneConfig, + share_fs_mount: Arc, +} + +impl ShareVirtioFsStandalone { + pub(crate) fn new(id: &str, _config: &SharedFsInfo) -> Result { + Ok(Self { + inner: Arc::new(RwLock::new(ShareVirtioFsStandaloneInner::default())), + // TODO: update with config + config: ShareVirtioFsStandaloneConfig { + id: id.to_string(), + jail_root: "".to_string(), + virtio_fs_daemon: "".to_string(), + virtio_fs_cache: "".to_string(), + virtio_fs_extra_args: vec![], + }, + share_fs_mount: Arc::new(VirtiofsShareMount::new(id)), + }) + } + + fn virtiofsd_args(&self, sock_path: &str) -> Result> { + let source_path = get_host_ro_shared_path(&self.config.id); + if !source_path.exists() { + return Err(anyhow!("The virtiofs shared path didn't exist")); + } + + let mut args: Vec = vec![ + String::from("-f"), + String::from("-o"), + format!("vhost_user_socket={}", sock_path), + String::from("-o"), + format!("source={}", source_path.to_str().unwrap()), + String::from("-o"), + format!("cache={}", self.config.virtio_fs_cache), + ]; + + if !self.config.virtio_fs_extra_args.is_empty() { + let mut extra_args: Vec = self.config.virtio_fs_extra_args.clone(); + args.append(&mut extra_args); + } + + Ok(args) + } + + async fn setup_virtiofsd(&self) -> Result<()> { + let sock_path = generate_sock_path(&self.config.jail_root); + let args = self.virtiofsd_args(&sock_path).context("virtiofsd args")?; + + let mut cmd = Command::new(&self.config.virtio_fs_daemon); + let child_cmd = cmd.args(&args).stderr(Stdio::piped()); + let child = child_cmd.spawn().context("spawn virtiofsd")?; + + // update virtiofsd pid{ + { + let mut inner = self.inner.write().await; + inner.pid = child.id(); + } + + let (tx, mut rx): (Sender>, Receiver>) = channel(100); + tokio::spawn(run_virtiofsd(child, tx)); + + // TODO: support timeout + match rx.recv().await.unwrap() { + Ok(_) => { + info!(sl!(), "start virtiofsd successfully"); + Ok(()) + } + Err(e) => { + error!(sl!(), "failed to start virtiofsd {}", e); + self.shutdown_virtiofsd() + .await + .context("shutdown_virtiofsd")?; + Err(anyhow!("failed to start virtiofsd")) + } + } + } + + async fn shutdown_virtiofsd(&self) -> Result<()> { + let mut inner = self.inner.write().await; + + if let Some(pid) = inner.pid.take() { + info!(sl!(), "shutdown virtiofsd pid {}", pid); + let pid = ::nix::unistd::Pid::from_raw(pid as i32); + if let Err(err) = ::nix::sys::signal::kill(pid, nix::sys::signal::SIGKILL) { + if err != ::nix::Error::Sys(nix::errno::Errno::ESRCH) { + return Err(anyhow!("failed to kill virtiofsd pid {} {}", pid, err)); + } + } + } + + Ok(()) + } +} + +async fn run_virtiofsd(mut child: Child, tx: Sender>) -> Result<()> { + let stderr = child.stderr.as_mut().unwrap(); + let stderr_reader = BufReader::new(stderr); + let mut lines = stderr_reader.lines(); + + while let Some(buffer) = lines.next_line().await.context("read next line")? { + let trim_buffer = buffer.trim_end(); + if !trim_buffer.is_empty() { + info!(sl!(), "source: virtiofsd {}", trim_buffer); + } + if buffer.contains("Waiting for vhost-user socket connection") { + tx.send(Ok(())).await.unwrap(); + } + } + + info!(sl!(), "wait virtiofsd {:?}", child.wait().await); + Ok(()) +} + +#[async_trait] +impl ShareFs for ShareVirtioFsStandalone { + fn get_share_fs_mount(&self) -> Arc { + self.share_fs_mount.clone() + } + + async fn setup_device_before_start_vm(&self, _h: &dyn Hypervisor) -> Result<()> { + self.setup_virtiofsd().await.context("setup virtiofsd")?; + Ok(()) + } + + async fn setup_device_after_start_vm(&self, _h: &dyn Hypervisor) -> Result<()> { + Ok(()) + } + + async fn get_storages(&self) -> Result> { + Ok(vec![]) + } +} diff --git a/src/runtime-rs/crates/resource/src/share_fs/utils.rs b/src/runtime-rs/crates/resource/src/share_fs/utils.rs new file mode 100644 index 0000000000..3a4b0c7439 --- /dev/null +++ b/src/runtime-rs/crates/resource/src/share_fs/utils.rs @@ -0,0 +1,94 @@ +// Copyright (c) 2019-2022 Alibaba Cloud +// Copyright (c) 2019-2022 Ant Group +// +// SPDX-License-Identifier: Apache-2.0 +// + +use std::path::{Path, PathBuf}; + +use anyhow::Result; +use kata_sys_util::mount; + +use super::*; + +pub(crate) fn ensure_dir_exist(path: &Path) -> Result<()> { + if !path.exists() { + std::fs::create_dir_all(path).context(format!("failed to create directory {:?}", path))?; + } + Ok(()) +} + +pub(crate) fn share_to_guest( + // absolute path for source + source: &str, + // relative path for target + target: &str, + sid: &str, + cid: &str, + readonly: bool, + is_volume: bool, +) -> Result { + let host_dest = do_get_host_path(target, sid, cid, is_volume, false); + mount::bind_mount_unchecked(source, &host_dest, readonly) + .context(format!("failed to bind mount {} to {}", source, &host_dest))?; + + // bind mount remount event is not propagated to mount subtrees, so we have + // to remount the read only dir mount point directly. + if readonly { + let dst = do_get_host_path(target, sid, cid, is_volume, true); + mount::bind_remount_read_only(&dst).context("bind remount readonly")?; + } + + Ok(do_get_guest_path(target, cid, is_volume)) +} + +pub(crate) fn get_host_ro_shared_path(id: &str) -> PathBuf { + Path::new(KATA_HOST_SHARED_DIR).join(id).join("ro") +} + +pub(crate) fn get_host_rw_shared_path(id: &str) -> PathBuf { + Path::new(KATA_HOST_SHARED_DIR).join(id).join("rw") +} + +fn do_get_guest_any_path(target: &str, cid: &str, is_volume: bool, is_virtiofs: bool) -> String { + let dir = PASSTHROUGH_FS_DIR; + let guest_share_dir = if is_virtiofs { + Path::new("/") + } else { + Path::new(KATA_GUEST_SHARE_DIR) + }; + + let path = if is_volume && !is_virtiofs { + guest_share_dir.join(dir).join(target) + } else { + guest_share_dir.join(dir).join(cid).join(target) + }; + path.to_str().unwrap().to_string() +} + +fn do_get_guest_path(target: &str, cid: &str, is_volume: bool) -> String { + do_get_guest_any_path(target, cid, is_volume, false) +} + +fn do_get_host_path( + target: &str, + sid: &str, + cid: &str, + is_volume: bool, + read_only: bool, +) -> String { + let dir = PASSTHROUGH_FS_DIR; + + let get_host_path = if read_only { + get_host_ro_shared_path + } else { + get_host_rw_shared_path + }; + + let path = if is_volume { + get_host_path(sid).join(dir).join(target) + } else { + get_host_path(sid).join(dir).join(cid).join(target) + }; + path.to_str().unwrap().to_string() +} diff --git a/src/runtime-rs/crates/resource/src/share_fs/virtio_fs_share_mount.rs b/src/runtime-rs/crates/resource/src/share_fs/virtio_fs_share_mount.rs new file mode 100644 index 0000000000..1f1abdb1cb --- /dev/null +++ b/src/runtime-rs/crates/resource/src/share_fs/virtio_fs_share_mount.rs @@ -0,0 +1,50 @@ +// Copyright (c) 2019-2022 Alibaba Cloud +// Copyright (c) 2019-2022 Ant Group +// +// SPDX-License-Identifier: Apache-2.0 +// + +use anyhow::{Context, Result}; +use async_trait::async_trait; + +use super::{utils, ShareFsMount, ShareFsMountResult, ShareFsRootfsConfig, ShareFsVolumeConfig}; + +pub struct VirtiofsShareMount { + id: String, +} + +impl VirtiofsShareMount { + pub fn new(id: &str) -> Self { + Self { id: id.to_string() } + } +} + +#[async_trait] +impl ShareFsMount for VirtiofsShareMount { + async fn share_rootfs(&self, config: ShareFsRootfsConfig) -> Result { + // TODO: select virtiofs or support nydus + let guest_path = utils::share_to_guest( + &config.source, + &config.target, + &self.id, + &config.cid, + config.readonly, + false, + ) + .context("share to guest")?; + Ok(ShareFsMountResult { guest_path }) + } + + async fn share_volume(&self, config: ShareFsVolumeConfig) -> Result { + let guest_path = utils::share_to_guest( + &config.source, + &config.target, + &self.id, + &config.cid, + config.readonly, + true, + ) + .context("share to guest")?; + Ok(ShareFsMountResult { guest_path }) + } +} diff --git a/src/runtime-rs/crates/resource/src/volume/block_volume.rs b/src/runtime-rs/crates/resource/src/volume/block_volume.rs new file mode 100644 index 0000000000..f015c92785 --- /dev/null +++ b/src/runtime-rs/crates/resource/src/volume/block_volume.rs @@ -0,0 +1,37 @@ +// Copyright (c) 2019-2022 Alibaba Cloud +// Copyright (c) 2019-2022 Ant Group +// +// SPDX-License-Identifier: Apache-2.0 +// + +use anyhow::Result; + +use super::Volume; + +pub(crate) struct BlockVolume {} + +/// BlockVolume: block device volume +impl BlockVolume { + pub(crate) fn new(_m: &oci::Mount) -> Result { + Ok(Self {}) + } +} + +impl Volume for BlockVolume { + fn get_volume_mount(&self) -> anyhow::Result> { + todo!() + } + + fn get_storage(&self) -> Result> { + todo!() + } + + fn cleanup(&self) -> Result<()> { + todo!() + } +} + +pub(crate) fn is_block_volume(_m: &oci::Mount) -> bool { + // attach block device + false +} diff --git a/src/runtime-rs/crates/resource/src/volume/default_volume.rs b/src/runtime-rs/crates/resource/src/volume/default_volume.rs new file mode 100644 index 0000000000..3b7752a4e7 --- /dev/null +++ b/src/runtime-rs/crates/resource/src/volume/default_volume.rs @@ -0,0 +1,36 @@ +// Copyright (c) 2019-2022 Alibaba Cloud +// Copyright (c) 2019-2022 Ant Group +// +// SPDX-License-Identifier: Apache-2.0 +// + +use anyhow::Result; + +use super::Volume; + +pub(crate) struct DefaultVolume { + mount: oci::Mount, +} + +/// DefaultVolume: passthrough the mount to guest +impl DefaultVolume { + pub fn new(mount: &oci::Mount) -> Result { + Ok(Self { + mount: mount.clone(), + }) + } +} + +impl Volume for DefaultVolume { + fn get_volume_mount(&self) -> anyhow::Result> { + Ok(vec![self.mount.clone()]) + } + + fn get_storage(&self) -> Result> { + Ok(vec![]) + } + + fn cleanup(&self) -> Result<()> { + todo!() + } +} diff --git a/src/runtime-rs/crates/resource/src/volume/mod.rs b/src/runtime-rs/crates/resource/src/volume/mod.rs new file mode 100644 index 0000000000..2ad12f1196 --- /dev/null +++ b/src/runtime-rs/crates/resource/src/volume/mod.rs @@ -0,0 +1,99 @@ +// Copyright (c) 2019-2022 Alibaba Cloud +// Copyright (c) 2019-2022 Ant Group +// +// SPDX-License-Identifier: Apache-2.0 +// + +mod block_volume; +mod default_volume; +mod share_fs_volume; +mod shm_volume; + +use std::{sync::Arc, vec::Vec}; + +use anyhow::{Context, Result}; +use tokio::sync::RwLock; + +use crate::share_fs::ShareFs; + +pub trait Volume: Send + Sync { + fn get_volume_mount(&self) -> Result>; + fn get_storage(&self) -> Result>; + fn cleanup(&self) -> Result<()>; +} + +#[derive(Default)] +pub struct VolumeResourceInner { + volumes: Vec>, +} + +#[derive(Default)] +pub struct VolumeResource { + inner: Arc>, +} + +impl VolumeResource { + pub fn new() -> Self { + Self::default() + } + + pub async fn handler_volumes( + &self, + share_fs: &Option>, + cid: &str, + oci_mounts: &[oci::Mount], + ) -> Result>> { + let mut volumes: Vec> = vec![]; + for m in oci_mounts { + let volume: Arc = if shm_volume::is_shim_volume(m) { + let shm_size = shm_volume::DEFAULT_SHM_SIZE; + Arc::new( + shm_volume::ShmVolume::new(m, shm_size) + .context(format!("new shm volume {:?}", m))?, + ) + } else if share_fs_volume::is_share_fs_volume(m) { + Arc::new( + share_fs_volume::ShareFsVolume::new(share_fs, m, cid) + .await + .context(format!("new share fs volume {:?}", m))?, + ) + } else if block_volume::is_block_volume(m) { + Arc::new( + block_volume::BlockVolume::new(m) + .context(format!("new block volume {:?}", m))?, + ) + } else if is_skip_volume(m) { + info!(sl!(), "skip volume {:?}", m); + continue; + } else { + Arc::new( + default_volume::DefaultVolume::new(m) + .context(format!("new default volume {:?}", m))?, + ) + }; + + volumes.push(volume.clone()); + let mut inner = self.inner.write().await; + inner.volumes.push(volume); + } + + Ok(volumes) + } + + pub async fn dump(&self) { + let inner = self.inner.read().await; + for v in &inner.volumes { + info!( + sl!(), + "volume mount {:?}: count {}", + v.get_volume_mount(), + Arc::strong_count(v) + ); + } + } +} + +fn is_skip_volume(_m: &oci::Mount) -> bool { + // TODO: support volume check + false +} diff --git a/src/runtime-rs/crates/resource/src/volume/share_fs_volume.rs b/src/runtime-rs/crates/resource/src/volume/share_fs_volume.rs new file mode 100644 index 0000000000..4fa6b341f1 --- /dev/null +++ b/src/runtime-rs/crates/resource/src/volume/share_fs_volume.rs @@ -0,0 +1,153 @@ +// Copyright (c) 2019-2022 Alibaba Cloud +// Copyright (c) 2019-2022 Ant Group +// +// SPDX-License-Identifier: Apache-2.0 +// + +use std::{path::Path, sync::Arc}; + +use anyhow::{anyhow, Context, Result}; +use log::debug; +use nix::sys::stat::{stat, SFlag}; + +use super::Volume; +use crate::share_fs::{ShareFs, ShareFsVolumeConfig}; + +// copy file to container's rootfs if filesystem sharing is not supported, otherwise +// bind mount it in the shared directory. +// Ignore /dev, directories and all other device files. We handle +// only regular files in /dev. It does not make sense to pass the host +// device nodes to the guest. +// skip the volumes whose source had already set to guest share dir. +pub(crate) struct ShareFsVolume { + mounts: Vec, +} + +impl ShareFsVolume { + pub(crate) async fn new( + share_fs: &Option>, + m: &oci::Mount, + cid: &str, + ) -> Result { + let file_name = Path::new(&m.source).file_name().unwrap().to_str().unwrap(); + let file_name = generate_mount_path(cid, file_name); + + let mut volume = Self { mounts: vec![] }; + match share_fs { + None => { + let mut need_copy = false; + match stat(Path::new(&m.source)) { + Ok(stat) => { + // Ignore the mount if this is not a regular file (excludes + // directory, socket, device, ...) as it cannot be handled by + // a simple copy. But this should not be treated as an error, + // only as a limitation. + // golang implement: + // ModeType = ModeDir | ModeSymlink | ModeNamedPipe | ModeSocket | + // ModeDevice | ModeCharDevice | ModeIrregular + let file_type = SFlag::S_IFDIR + | SFlag::S_IFLNK + | SFlag::S_IFIFO + | SFlag::S_IFSOCK + | SFlag::S_IFCHR + | SFlag::S_IFREG; + if !file_type.contains(SFlag::from_bits_truncate(stat.st_mode)) { + debug!( + "Ignoring non-regular file as FS sharing not supported. mount: {:?}", + m + ); + return Ok(volume); + } + if SFlag::from_bits_truncate(stat.st_mode) != SFlag::S_IFDIR { + need_copy = true; + } + } + Err(err) => { + return Err(anyhow!(format!( + "failed to stat file {} {:?}", + &m.source, err + ))); + } + }; + + if need_copy { + // TODO: copy file + } + } + Some(share_fs) => { + let share_fs_mount = share_fs.get_share_fs_mount(); + let mount_result = share_fs_mount + .share_volume(ShareFsVolumeConfig { + cid: cid.to_string(), + source: m.source.clone(), + target: file_name, + readonly: false, + }) + .await + .context("share fs volume")?; + + volume.mounts.push(oci::Mount { + destination: m.destination.clone(), + r#type: "bind".to_string(), + source: mount_result.guest_path, + options: m.options.clone(), + }); + } + } + Ok(volume) + } +} + +impl Volume for ShareFsVolume { + fn get_volume_mount(&self) -> anyhow::Result> { + Ok(self.mounts.clone()) + } + + fn get_storage(&self) -> Result> { + Ok(vec![]) + } + + fn cleanup(&self) -> Result<()> { + todo!() + } +} + +pub(crate) fn is_share_fs_volume(m: &oci::Mount) -> bool { + m.r#type == "bind" && !is_host_device(&m.destination) +} + +fn is_host_device(dest: &str) -> bool { + if dest == "/dev" { + return true; + } + + if dest.starts_with("/dev") { + let src = match std::fs::canonicalize(dest) { + Err(_) => return false, + Ok(src) => src, + }; + + if src.is_file() { + return false; + } + + return true; + } + + false +} + +// Note, don't generate random name, attaching rafs depends on the predictable name. +// If template_mnt is passed, just use existed name in it +pub fn generate_mount_path(id: &str, file_name: &str) -> String { + let mut nid = String::from(id); + if nid.len() > 10 { + nid = nid.chars().take(10).collect(); + } + + let mut uid = uuid::Uuid::new_v4().to_string(); + let uid_vec: Vec<&str> = uid.splitn(2, '-').collect(); + uid = String::from(uid_vec[0]); + + format!("{}-{}-{}", nid, uid, file_name) +} diff --git a/src/runtime-rs/crates/resource/src/volume/shm_volume.rs b/src/runtime-rs/crates/resource/src/volume/shm_volume.rs new file mode 100644 index 0000000000..e26fe19046 --- /dev/null +++ b/src/runtime-rs/crates/resource/src/volume/shm_volume.rs @@ -0,0 +1,105 @@ +// Copyright (c) 2019-2022 Alibaba Cloud +// Copyright (c) 2019-2022 Ant Group +// +// SPDX-License-Identifier: Apache-2.0 +// + +use std::path::Path; + +use anyhow::Result; + +use super::Volume; +use crate::share_fs::DEFAULT_KATA_GUEST_SANDBOX_DIR; + +pub const SHM_DIR: &str = "shm"; +// DEFAULT_SHM_SIZE is the default shm size to be used in case host +// IPC is used. +pub const DEFAULT_SHM_SIZE: u64 = 65536 * 1024; + +// KATA_EPHEMERAL_DEV_TYPE creates a tmpfs backed volume for sharing files between containers. +pub const KATA_EPHEMERAL_DEV_TYPE: &str = "ephemeral"; + +pub(crate) struct ShmVolume { + mount: oci::Mount, + storage: Option, +} + +impl ShmVolume { + pub(crate) fn new(m: &oci::Mount, shm_size: u64) -> Result { + let (storage, mount) = if shm_size > 0 { + // storage + let mount_path = Path::new(DEFAULT_KATA_GUEST_SANDBOX_DIR).join(SHM_DIR); + let mount_path = mount_path.to_str().unwrap(); + let option = format!("size={}", shm_size); + + let options = vec![ + String::from("noexec"), + String::from("nosuid"), + String::from("nodev"), + String::from("mode=1777"), + option, + ]; + + let storage = agent::Storage { + driver: String::from(KATA_EPHEMERAL_DEV_TYPE), + driver_options: Vec::new(), + source: String::from("shm"), + fs_type: String::from("tmpfs"), + options, + mount_point: mount_path.to_string(), + }; + + // mount + let mount = oci::Mount { + r#type: "bind".to_string(), + destination: m.destination.clone(), + source: mount_path.to_string(), + options: vec!["rbind".to_string()], + }; + + (Some(storage), mount) + } else { + let mount = oci::Mount { + r#type: "tmpfs".to_string(), + destination: m.destination.clone(), + source: "shm".to_string(), + options: vec![ + "noexec", + "nosuid", + "nodev", + "mode=1777", + &format!("size={}", DEFAULT_SHM_SIZE), + ] + .iter() + .map(|s| s.to_string()) + .collect(), + }; + (None, mount) + }; + + Ok(Self { storage, mount }) + } +} + +impl Volume for ShmVolume { + fn get_volume_mount(&self) -> anyhow::Result> { + Ok(vec![self.mount.clone()]) + } + + fn get_storage(&self) -> Result> { + let s = if let Some(s) = self.storage.as_ref() { + vec![s.clone()] + } else { + vec![] + }; + Ok(s) + } + + fn cleanup(&self) -> Result<()> { + todo!() + } +} + +pub(crate) fn is_shim_volume(m: &oci::Mount) -> bool { + m.destination == "/dev/shm" && m.r#type != KATA_EPHEMERAL_DEV_TYPE +}