Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/virtio/vhost_user_backend/fs.rs
5394 views
1
// Copyright 2021 The ChromiumOS Authors
2
// Use of this source code is governed by a BSD-style license that can be
3
// found in the LICENSE file.
4
5
mod sys;
6
7
use std::collections::BTreeMap;
8
use std::path::PathBuf;
9
use std::sync::Arc;
10
11
use anyhow::bail;
12
use argh::FromArgs;
13
use base::error;
14
use base::info;
15
use base::warn;
16
use base::RawDescriptor;
17
use base::Tube;
18
use base::WorkerThread;
19
use data_model::Le32;
20
use fuse::Server;
21
use hypervisor::ProtectionType;
22
use snapshot::AnySnapshot;
23
use sync::Mutex;
24
pub use sys::start_device as run_fs_device;
25
use virtio_sys::virtio_fs::virtio_fs_config;
26
use vm_memory::GuestMemory;
27
use vmm_vhost::message::VhostUserProtocolFeatures;
28
use vmm_vhost::VHOST_USER_F_PROTOCOL_FEATURES;
29
use zerocopy::IntoBytes;
30
31
use crate::virtio;
32
use crate::virtio::copy_config;
33
use crate::virtio::device_constants::fs::FS_MAX_TAG_LEN;
34
use crate::virtio::fs::passthrough::PassthroughFs;
35
use crate::virtio::fs::Config;
36
use crate::virtio::fs::Worker;
37
use crate::virtio::vhost_user_backend::handler::Error as DeviceError;
38
use crate::virtio::vhost_user_backend::handler::VhostUserDevice;
39
use crate::virtio::Queue;
40
41
const MAX_QUEUE_NUM: usize = 2; /* worker queue and high priority queue */
42
43
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
52
impl 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
// Use default passthroughfs config
79
#[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
102
impl 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
// Slot is always going to be 0 because we do not support DAX
147
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
// TODO(b/440937769): Remove debug logs once the issue is resolved.
163
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
// No non-queue workers.
178
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")]
192
/// FS Device
193
pub struct Options {
194
#[argh(option, arg_name = "PATH", hidden_help)]
195
/// deprecated - please use --socket-path instead
196
socket: Option<String>,
197
#[argh(option, arg_name = "PATH")]
198
/// path to the vhost-user socket to bind to.
199
/// If this flag is set, --fd cannot be specified.
200
socket_path: Option<String>,
201
#[argh(option, arg_name = "FD")]
202
/// file descriptor of a connected vhost-user socket.
203
/// If this flag is set, --socket-path cannot be specified.
204
fd: Option<RawDescriptor>,
205
206
#[argh(option, arg_name = "TAG")]
207
/// the virtio-fs tag
208
tag: String,
209
#[argh(option, arg_name = "DIR")]
210
/// path to a directory to share
211
shared_dir: PathBuf,
212
#[argh(option, arg_name = "UIDMAP")]
213
/// uid map to use
214
uid_map: Option<String>,
215
#[argh(option, arg_name = "GIDMAP")]
216
/// gid map to use
217
gid_map: Option<String>,
218
#[argh(option, arg_name = "CFG")]
219
/// colon-separated options for configuring a directory to be
220
/// shared with the VM through virtio-fs. The format is the same as
221
/// `crosvm run --shared-dir` flag except only the keys related to virtio-fs
222
/// are valid here.
223
cfg: Option<Config>,
224
#[argh(option, arg_name = "UID", default = "0")]
225
/// uid of the device process in the new user namespace created by minijail.
226
/// These two options (uid/gid) are useful when the crosvm process cannot
227
/// get CAP_SETGID/CAP_SETUID but an identity mapping of the current
228
/// user/group between the VM and the host is required.
229
/// Say the current user and the crosvm process has uid 5000, a user can use
230
/// "uid=5000" and "uidmap=5000 5000 1" such that files owned by user 5000
231
/// still appear to be owned by user 5000 in the VM. These 2 options are
232
/// useful only when there is 1 user in the VM accessing shared files.
233
/// If multiple users want to access the shared file, gid/uid options are
234
/// useless. It'd be better to create a new user namespace and give
235
/// CAP_SETUID/CAP_SETGID to the crosvm.
236
/// Default: 0.
237
uid: u32,
238
#[argh(option, arg_name = "GID", default = "0")]
239
/// gid of the device process in the new user namespace created by minijail.
240
/// Default: 0.
241
gid: u32,
242
#[argh(switch)]
243
/// disable-sandbox controls whether vhost-user-fs device uses minijail sandbox.
244
/// By default, it is false, the vhost-user-fs will enter new mnt/user/pid/net
245
/// namespace. If the this option is true, the vhost-user-fs device only create
246
/// a new mount namespace and run without seccomp filter.
247
/// Default: false.
248
disable_sandbox: bool,
249
#[argh(option, arg_name = "skip_pivot_root", default = "false")]
250
/// disable pivot_root when process is jailed.
251
///
252
/// virtio-fs typically uses mount namespaces and pivot_root for file system isolation,
253
/// making the jailed process's root directory "/".
254
///
255
/// Android's security model restricts crosvm's access to certain system capabilities,
256
/// specifically those related to managing mount namespaces and using pivot_root.
257
/// These capabilities are typically associated with the SYS_ADMIN capability.
258
/// To maintain a secure environment, Android relies on mechanisms like SELinux to
259
/// enforce isolation and control access to directories.
260
#[allow(dead_code)]
261
skip_pivot_root: bool,
262
}
263
264