Path: blob/main/devices/src/virtio/vhost_user_backend/fs.rs
5394 views
// Copyright 2021 The ChromiumOS Authors1// Use of this source code is governed by a BSD-style license that can be2// found in the LICENSE file.34mod sys;56use std::collections::BTreeMap;7use std::path::PathBuf;8use std::sync::Arc;910use anyhow::bail;11use argh::FromArgs;12use base::error;13use base::info;14use base::warn;15use base::RawDescriptor;16use base::Tube;17use base::WorkerThread;18use data_model::Le32;19use fuse::Server;20use hypervisor::ProtectionType;21use snapshot::AnySnapshot;22use sync::Mutex;23pub use sys::start_device as run_fs_device;24use virtio_sys::virtio_fs::virtio_fs_config;25use vm_memory::GuestMemory;26use vmm_vhost::message::VhostUserProtocolFeatures;27use vmm_vhost::VHOST_USER_F_PROTOCOL_FEATURES;28use zerocopy::IntoBytes;2930use crate::virtio;31use crate::virtio::copy_config;32use crate::virtio::device_constants::fs::FS_MAX_TAG_LEN;33use crate::virtio::fs::passthrough::PassthroughFs;34use crate::virtio::fs::Config;35use crate::virtio::fs::Worker;36use crate::virtio::vhost_user_backend::handler::Error as DeviceError;37use crate::virtio::vhost_user_backend::handler::VhostUserDevice;38use crate::virtio::Queue;3940const MAX_QUEUE_NUM: usize = 2; /* worker queue and high priority queue */4142struct FsBackend {43server: Arc<fuse::Server<PassthroughFs>>,44tag: String,45avail_features: u64,46workers: BTreeMap<usize, WorkerThread<Queue>>,47keep_rds: Vec<RawDescriptor>,48unmap_guest_memory_on_fork: bool,49}5051impl FsBackend {52#[allow(unused_variables)]53pub fn new(54tag: &str,55shared_dir: &str,56skip_pivot_root: bool,57cfg: Option<Config>,58) -> anyhow::Result<Self> {59if tag.len() > FS_MAX_TAG_LEN {60bail!(61"fs tag is too long: {} (max supported: {})",62tag.len(),63FS_MAX_TAG_LEN64);65}6667let avail_features = virtio::base_features(ProtectionType::Unprotected)68| 1 << VHOST_USER_F_PROTOCOL_FEATURES;6970let cfg = cfg.unwrap_or_default();7172#[cfg(any(target_os = "android", target_os = "linux"))]73let unmap_guest_memory_on_fork = cfg.unmap_guest_memory_on_fork;74#[cfg(not(any(target_os = "android", target_os = "linux")))]75let unmap_guest_memory_on_fork = false;7677// Use default passthroughfs config78#[allow(unused_mut)]79let mut fs = PassthroughFs::new(tag, cfg)?;80#[cfg(feature = "fs_runtime_ugid_map")]81if skip_pivot_root {82fs.set_root_dir(shared_dir.to_string())?;83}8485let mut keep_rds: Vec<RawDescriptor> = [0, 1, 2].to_vec();86keep_rds.append(&mut fs.keep_rds());8788let server = Arc::new(Server::new(fs));8990Ok(FsBackend {91server,92tag: tag.to_owned(),93avail_features,94workers: Default::default(),95keep_rds,96unmap_guest_memory_on_fork,97})98}99}100101impl VhostUserDevice for FsBackend {102fn max_queue_num(&self) -> usize {103MAX_QUEUE_NUM104}105106fn features(&self) -> u64 {107self.avail_features108}109110fn protocol_features(&self) -> VhostUserProtocolFeatures {111VhostUserProtocolFeatures::CONFIG | VhostUserProtocolFeatures::MQ112}113114fn read_config(&self, offset: u64, data: &mut [u8]) {115let mut config = virtio_fs_config {116tag: [0; FS_MAX_TAG_LEN],117num_request_queues: Le32::from(1),118};119config.tag[..self.tag.len()].copy_from_slice(self.tag.as_bytes());120copy_config(data, 0, config.as_bytes(), offset);121}122123fn reset(&mut self) {124for worker in std::mem::take(&mut self.workers).into_values() {125let _ = worker.stop();126}127}128129fn start_queue(130&mut self,131idx: usize,132queue: virtio::Queue,133_mem: GuestMemory,134) -> anyhow::Result<()> {135if self.workers.contains_key(&idx) {136warn!("Starting new queue handler without stopping old handler");137self.stop_queue(idx)?;138}139140let (_, fs_device_tube) = Tube::pair()?;141let tube = Arc::new(Mutex::new(fs_device_tube));142143let server = self.server.clone();144145// Slot is always going to be 0 because we do not support DAX146let slot: u32 = 0;147148let worker = WorkerThread::start(format!("v_fs:{}:{}", self.tag, idx), move |kill_evt| {149let mut worker = Worker::new(queue, server, tube, slot);150if let Err(e) = worker.run(kill_evt) {151error!("vhost-user-fs worker failed: {e:#}");152}153worker.queue154});155self.workers.insert(idx, worker);156157Ok(())158}159160fn stop_queue(&mut self, idx: usize) -> anyhow::Result<virtio::Queue> {161// TODO(b/440937769): Remove debug logs once the issue is resolved.162info!("Stopping vhost-user fs queue [{idx}]");163if let Some(worker) = self.workers.remove(&idx) {164let queue = worker.stop();165Ok(queue)166} else {167Err(anyhow::Error::new(DeviceError::WorkerNotFound))168}169}170171fn unmap_guest_memory_on_fork(&self) -> bool {172self.unmap_guest_memory_on_fork173}174175fn enter_suspended_state(&mut self) -> anyhow::Result<()> {176// No non-queue workers.177Ok(())178}179180fn snapshot(&mut self) -> anyhow::Result<AnySnapshot> {181bail!("snapshot not implemented for vhost-user fs");182}183184fn restore(&mut self, _data: AnySnapshot) -> anyhow::Result<()> {185bail!("snapshot not implemented for vhost-user fs");186}187}188189#[derive(FromArgs)]190#[argh(subcommand, name = "fs")]191/// FS Device192pub struct Options {193#[argh(option, arg_name = "PATH", hidden_help)]194/// deprecated - please use --socket-path instead195socket: Option<String>,196#[argh(option, arg_name = "PATH")]197/// path to the vhost-user socket to bind to.198/// If this flag is set, --fd cannot be specified.199socket_path: Option<String>,200#[argh(option, arg_name = "FD")]201/// file descriptor of a connected vhost-user socket.202/// If this flag is set, --socket-path cannot be specified.203fd: Option<RawDescriptor>,204205#[argh(option, arg_name = "TAG")]206/// the virtio-fs tag207tag: String,208#[argh(option, arg_name = "DIR")]209/// path to a directory to share210shared_dir: PathBuf,211#[argh(option, arg_name = "UIDMAP")]212/// uid map to use213uid_map: Option<String>,214#[argh(option, arg_name = "GIDMAP")]215/// gid map to use216gid_map: Option<String>,217#[argh(option, arg_name = "CFG")]218/// colon-separated options for configuring a directory to be219/// shared with the VM through virtio-fs. The format is the same as220/// `crosvm run --shared-dir` flag except only the keys related to virtio-fs221/// are valid here.222cfg: Option<Config>,223#[argh(option, arg_name = "UID", default = "0")]224/// uid of the device process in the new user namespace created by minijail.225/// These two options (uid/gid) are useful when the crosvm process cannot226/// get CAP_SETGID/CAP_SETUID but an identity mapping of the current227/// user/group between the VM and the host is required.228/// Say the current user and the crosvm process has uid 5000, a user can use229/// "uid=5000" and "uidmap=5000 5000 1" such that files owned by user 5000230/// still appear to be owned by user 5000 in the VM. These 2 options are231/// useful only when there is 1 user in the VM accessing shared files.232/// If multiple users want to access the shared file, gid/uid options are233/// useless. It'd be better to create a new user namespace and give234/// CAP_SETUID/CAP_SETGID to the crosvm.235/// Default: 0.236uid: u32,237#[argh(option, arg_name = "GID", default = "0")]238/// gid of the device process in the new user namespace created by minijail.239/// Default: 0.240gid: u32,241#[argh(switch)]242/// disable-sandbox controls whether vhost-user-fs device uses minijail sandbox.243/// By default, it is false, the vhost-user-fs will enter new mnt/user/pid/net244/// namespace. If the this option is true, the vhost-user-fs device only create245/// a new mount namespace and run without seccomp filter.246/// Default: false.247disable_sandbox: bool,248#[argh(option, arg_name = "skip_pivot_root", default = "false")]249/// disable pivot_root when process is jailed.250///251/// virtio-fs typically uses mount namespaces and pivot_root for file system isolation,252/// making the jailed process's root directory "/".253///254/// Android's security model restricts crosvm's access to certain system capabilities,255/// specifically those related to managing mount namespaces and using pivot_root.256/// These capabilities are typically associated with the SYS_ADMIN capability.257/// To maintain a secure environment, Android relies on mechanisms like SELinux to258/// enforce isolation and control access to directories.259#[allow(dead_code)]260skip_pivot_root: bool,261}262263264