devices/virtio/vhost_user_backend/
fs.rs1mod sys;
6
7use std::collections::BTreeMap;
8use std::path::PathBuf;
9use std::sync::Arc;
10
11use anyhow::bail;
12use argh::FromArgs;
13use base::error;
14use base::info;
15use base::warn;
16use base::RawDescriptor;
17use base::Tube;
18use base::WorkerThread;
19use data_model::Le32;
20use fuse::Server;
21use hypervisor::ProtectionType;
22use snapshot::AnySnapshot;
23use sync::Mutex;
24pub use sys::start_device as run_fs_device;
25use virtio_sys::virtio_fs::virtio_fs_config;
26use vm_memory::GuestMemory;
27use vmm_vhost::message::VhostUserProtocolFeatures;
28use vmm_vhost::VHOST_USER_F_PROTOCOL_FEATURES;
29use zerocopy::IntoBytes;
30
31use crate::virtio;
32use crate::virtio::copy_config;
33use crate::virtio::device_constants::fs::FS_MAX_TAG_LEN;
34use crate::virtio::fs::passthrough::PassthroughFs;
35use crate::virtio::fs::Config;
36use crate::virtio::fs::Worker;
37use crate::virtio::vhost_user_backend::handler::Error as DeviceError;
38use crate::virtio::vhost_user_backend::handler::VhostUserDevice;
39use crate::virtio::Queue;
40
41const MAX_QUEUE_NUM: usize = 2; struct FsBackend {
44 server: Arc<fuse::Server<PassthroughFs>>,
45 tag: String,
46 avail_features: u64,
47 workers: BTreeMap<usize, WorkerThread<Queue>>,
48 keep_rds: Vec<RawDescriptor>,
49 unmap_guest_memory_on_fork: bool,
50}
51
52impl FsBackend {
53 #[allow(unused_variables)]
54 pub fn new(
55 tag: &str,
56 shared_dir: &str,
57 skip_pivot_root: bool,
58 cfg: Option<Config>,
59 ) -> anyhow::Result<Self> {
60 if tag.len() > FS_MAX_TAG_LEN {
61 bail!(
62 "fs tag is too long: {} (max supported: {})",
63 tag.len(),
64 FS_MAX_TAG_LEN
65 );
66 }
67
68 let avail_features = virtio::base_features(ProtectionType::Unprotected)
69 | 1 << VHOST_USER_F_PROTOCOL_FEATURES;
70
71 let cfg = cfg.unwrap_or_default();
72
73 #[cfg(any(target_os = "android", target_os = "linux"))]
74 let unmap_guest_memory_on_fork = cfg.unmap_guest_memory_on_fork;
75 #[cfg(not(any(target_os = "android", target_os = "linux")))]
76 let unmap_guest_memory_on_fork = false;
77
78 #[allow(unused_mut)]
80 let mut fs = PassthroughFs::new(tag, cfg)?;
81 #[cfg(feature = "fs_runtime_ugid_map")]
82 if skip_pivot_root {
83 fs.set_root_dir(shared_dir.to_string())?;
84 }
85
86 let mut keep_rds: Vec<RawDescriptor> = [0, 1, 2].to_vec();
87 keep_rds.append(&mut fs.keep_rds());
88
89 let server = Arc::new(Server::new(fs));
90
91 Ok(FsBackend {
92 server,
93 tag: tag.to_owned(),
94 avail_features,
95 workers: Default::default(),
96 keep_rds,
97 unmap_guest_memory_on_fork,
98 })
99 }
100}
101
102impl VhostUserDevice for FsBackend {
103 fn max_queue_num(&self) -> usize {
104 MAX_QUEUE_NUM
105 }
106
107 fn features(&self) -> u64 {
108 self.avail_features
109 }
110
111 fn protocol_features(&self) -> VhostUserProtocolFeatures {
112 VhostUserProtocolFeatures::CONFIG | VhostUserProtocolFeatures::MQ
113 }
114
115 fn read_config(&self, offset: u64, data: &mut [u8]) {
116 let mut config = virtio_fs_config {
117 tag: [0; FS_MAX_TAG_LEN],
118 num_request_queues: Le32::from(1),
119 };
120 config.tag[..self.tag.len()].copy_from_slice(self.tag.as_bytes());
121 copy_config(data, 0, config.as_bytes(), offset);
122 }
123
124 fn reset(&mut self) {
125 for worker in std::mem::take(&mut self.workers).into_values() {
126 let _ = worker.stop();
127 }
128 }
129
130 fn start_queue(
131 &mut self,
132 idx: usize,
133 queue: virtio::Queue,
134 _mem: GuestMemory,
135 ) -> anyhow::Result<()> {
136 if self.workers.contains_key(&idx) {
137 warn!("Starting new queue handler without stopping old handler");
138 self.stop_queue(idx)?;
139 }
140
141 let (_, fs_device_tube) = Tube::pair()?;
142 let tube = Arc::new(Mutex::new(fs_device_tube));
143
144 let server = self.server.clone();
145
146 let slot: u32 = 0;
148
149 let worker = WorkerThread::start(format!("v_fs:{}:{}", self.tag, idx), move |kill_evt| {
150 let mut worker = Worker::new(queue, server, tube, slot);
151 if let Err(e) = worker.run(kill_evt) {
152 error!("vhost-user-fs worker failed: {e:#}");
153 }
154 worker.queue
155 });
156 self.workers.insert(idx, worker);
157
158 Ok(())
159 }
160
161 fn stop_queue(&mut self, idx: usize) -> anyhow::Result<virtio::Queue> {
162 info!("Stopping vhost-user fs queue [{idx}]");
164 if let Some(worker) = self.workers.remove(&idx) {
165 let queue = worker.stop();
166 Ok(queue)
167 } else {
168 Err(anyhow::Error::new(DeviceError::WorkerNotFound))
169 }
170 }
171
172 fn unmap_guest_memory_on_fork(&self) -> bool {
173 self.unmap_guest_memory_on_fork
174 }
175
176 fn enter_suspended_state(&mut self) -> anyhow::Result<()> {
177 Ok(())
179 }
180
181 fn snapshot(&mut self) -> anyhow::Result<AnySnapshot> {
182 bail!("snapshot not implemented for vhost-user fs");
183 }
184
185 fn restore(&mut self, _data: AnySnapshot) -> anyhow::Result<()> {
186 bail!("snapshot not implemented for vhost-user fs");
187 }
188}
189
190#[derive(FromArgs)]
191#[argh(subcommand, name = "fs")]
192pub struct Options {
194 #[argh(option, arg_name = "PATH", hidden_help)]
195 socket: Option<String>,
197 #[argh(option, arg_name = "PATH")]
198 socket_path: Option<String>,
201 #[argh(option, arg_name = "FD")]
202 fd: Option<RawDescriptor>,
205
206 #[argh(option, arg_name = "TAG")]
207 tag: String,
209 #[argh(option, arg_name = "DIR")]
210 shared_dir: PathBuf,
212 #[argh(option, arg_name = "UIDMAP")]
213 uid_map: Option<String>,
215 #[argh(option, arg_name = "GIDMAP")]
216 gid_map: Option<String>,
218 #[argh(option, arg_name = "CFG")]
219 cfg: Option<Config>,
224 #[argh(option, arg_name = "UID", default = "0")]
225 uid: u32,
238 #[argh(option, arg_name = "GID", default = "0")]
239 gid: u32,
242 #[argh(switch)]
243 disable_sandbox: bool,
249 #[argh(option, arg_name = "skip_pivot_root", default = "false")]
250 #[allow(dead_code)]
261 skip_pivot_root: bool,
262}