Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/virtio/fs/worker.rs
5394 views
1
// Copyright 2019 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
use std::convert::TryFrom;
6
use std::convert::TryInto;
7
use std::fs::File;
8
use std::io;
9
use std::os::unix::io::AsRawFd;
10
use std::sync::Arc;
11
12
use base::error;
13
use base::syscall;
14
use base::Event;
15
use base::EventToken;
16
use base::Protection;
17
use base::SafeDescriptor;
18
use base::Tube;
19
use base::WaitContext;
20
use fuse::filesystem::FileSystem;
21
use fuse::filesystem::ZeroCopyReader;
22
use fuse::filesystem::ZeroCopyWriter;
23
use sync::Mutex;
24
use vm_control::FsMappingRequest;
25
use vm_control::VmResponse;
26
27
use crate::virtio::fs::Error;
28
use crate::virtio::fs::Result;
29
use crate::virtio::Queue;
30
use crate::virtio::Reader;
31
use crate::virtio::Writer;
32
33
impl fuse::Reader for Reader {}
34
35
impl fuse::Writer for Writer {
36
type ClosureWriter = Self;
37
38
fn write_at<F>(&mut self, offset: usize, f: F) -> io::Result<usize>
39
where
40
F: Fn(&mut Self) -> io::Result<usize>,
41
{
42
let mut writer = Writer::split_at(self, offset);
43
f(&mut writer)
44
}
45
46
fn has_sufficient_buffer(&self, size: u32) -> bool {
47
self.available_bytes() >= size as usize
48
}
49
}
50
51
impl ZeroCopyReader for Reader {
52
fn read_to(&mut self, f: &mut File, count: usize, off: u64) -> io::Result<usize> {
53
self.read_to_at(f, count, off)
54
}
55
}
56
57
impl ZeroCopyWriter for Writer {
58
fn write_from(&mut self, f: &mut File, count: usize, off: u64) -> io::Result<usize> {
59
self.write_from_at(f, count, off)
60
}
61
}
62
63
struct Mapper {
64
tube: Arc<Mutex<Tube>>,
65
slot: u32,
66
}
67
68
impl Mapper {
69
fn new(tube: Arc<Mutex<Tube>>, slot: u32) -> Self {
70
Self { tube, slot }
71
}
72
73
fn process_request(&self, request: &FsMappingRequest) -> io::Result<()> {
74
let tube = self.tube.lock();
75
76
tube.send(request).map_err(|e| {
77
error!("failed to send request {:?}: {}", request, e);
78
io::Error::from_raw_os_error(libc::EINVAL)
79
})?;
80
81
match tube.recv() {
82
Ok(VmResponse::Ok) => Ok(()),
83
Ok(VmResponse::Err(e)) => Err(e.into()),
84
r => {
85
error!("failed to process {:?}: {:?}", request, r);
86
Err(io::Error::from_raw_os_error(libc::EIO))
87
}
88
}
89
}
90
}
91
92
impl fuse::Mapper for Mapper {
93
fn map(
94
&self,
95
mem_offset: u64,
96
size: usize,
97
fd: &dyn AsRawFd,
98
file_offset: u64,
99
prot: Protection,
100
) -> io::Result<()> {
101
let mem_offset: usize = mem_offset.try_into().map_err(|e| {
102
error!("mem_offset {} is too big: {}", mem_offset, e);
103
io::Error::from_raw_os_error(libc::EINVAL)
104
})?;
105
106
let fd = SafeDescriptor::try_from(fd)?;
107
108
let request = FsMappingRequest::CreateMemoryMapping {
109
slot: self.slot,
110
fd,
111
size,
112
file_offset,
113
prot,
114
mem_offset,
115
};
116
117
self.process_request(&request)
118
}
119
120
fn unmap(&self, offset: u64, size: u64) -> io::Result<()> {
121
let offset: usize = offset.try_into().map_err(|e| {
122
error!("offset {} is too big: {}", offset, e);
123
io::Error::from_raw_os_error(libc::EINVAL)
124
})?;
125
let size: usize = size.try_into().map_err(|e| {
126
error!("size {} is too big: {}", size, e);
127
io::Error::from_raw_os_error(libc::EINVAL)
128
})?;
129
130
let request = FsMappingRequest::RemoveMemoryMapping {
131
slot: self.slot,
132
offset,
133
size,
134
};
135
136
self.process_request(&request)
137
}
138
}
139
140
pub struct Worker<F: FileSystem + Sync> {
141
pub(crate) queue: Queue,
142
server: Arc<fuse::Server<F>>,
143
tube: Arc<Mutex<Tube>>,
144
slot: u32,
145
}
146
147
fn process_fs_queue<F: FileSystem + Sync>(
148
queue: &mut Queue,
149
server: &Arc<fuse::Server<F>>,
150
tube: &Arc<Mutex<Tube>>,
151
slot: u32,
152
) -> Result<()> {
153
let mapper = Mapper::new(Arc::clone(tube), slot);
154
while let Some(mut avail_desc) = queue.pop() {
155
let total =
156
server.handle_message(&mut avail_desc.reader, &mut avail_desc.writer, &mapper)?;
157
158
queue.add_used_with_bytes_written(avail_desc, total as u32);
159
queue.trigger_interrupt();
160
}
161
162
Ok(())
163
}
164
165
impl<F: FileSystem + Sync> Worker<F> {
166
pub fn new(
167
queue: Queue,
168
server: Arc<fuse::Server<F>>,
169
tube: Arc<Mutex<Tube>>,
170
slot: u32,
171
) -> Worker<F> {
172
Worker {
173
queue,
174
server,
175
tube,
176
slot,
177
}
178
}
179
180
pub fn run(&mut self, kill_evt: Event) -> Result<()> {
181
let mut ruid: libc::uid_t = 0;
182
let mut euid: libc::uid_t = 0;
183
let mut suid: libc::uid_t = 0;
184
// SAFETY: Safe because this doesn't modify any memory and we check the return value.
185
syscall!(unsafe { libc::getresuid(&mut ruid, &mut euid, &mut suid) })
186
.map_err(Error::GetResuid)?;
187
188
// Only need to set SECBIT_NO_SETUID_FIXUP for threads which could change uid.
189
if ruid == 0 || ruid != euid || ruid != suid {
190
// We need to set the no setuid fixup secure bit so that we don't drop capabilities when
191
// changing the thread uid/gid. Without this, creating new entries can fail in some
192
// corner cases.
193
const SECBIT_NO_SETUID_FIXUP: i32 = 1 << 2;
194
195
let mut securebits = syscall!(
196
// SAFETY:
197
// Safe because this doesn't modify any memory and we check the return value.
198
unsafe { libc::prctl(libc::PR_GET_SECUREBITS) }
199
)
200
.map_err(Error::GetSecurebits)?;
201
202
securebits |= SECBIT_NO_SETUID_FIXUP;
203
204
syscall!(
205
// SAFETY:
206
// Safe because this doesn't modify any memory and we check the return value.
207
unsafe { libc::prctl(libc::PR_SET_SECUREBITS, securebits) }
208
)
209
.map_err(Error::SetSecurebits)?;
210
}
211
212
// To avoid extra locking, unshare filesystem attributes from parent. This includes the
213
// current working directory and umask.
214
syscall!(
215
// SAFETY: Safe because this doesn't modify any memory and we check the return value.
216
unsafe { libc::unshare(libc::CLONE_FS) }
217
)
218
.map_err(Error::UnshareFromParent)?;
219
220
#[derive(EventToken)]
221
enum Token {
222
// A request is ready on the queue.
223
QueueReady,
224
// The parent thread requested an exit.
225
Kill,
226
}
227
228
let wait_ctx = WaitContext::build_with(&[
229
(self.queue.event(), Token::QueueReady),
230
(&kill_evt, Token::Kill),
231
])
232
.map_err(Error::CreateWaitContext)?;
233
234
loop {
235
let events = wait_ctx.wait().map_err(Error::WaitError)?;
236
for event in events.iter().filter(|e| e.is_readable) {
237
match event.token {
238
Token::QueueReady => {
239
self.queue.event().wait().map_err(Error::ReadQueueEvent)?;
240
if let Err(e) =
241
process_fs_queue(&mut self.queue, &self.server, &self.tube, self.slot)
242
{
243
error!("virtio-fs transport error: {}", e);
244
return Err(e);
245
}
246
}
247
Token::Kill => return Ok(()),
248
}
249
}
250
}
251
}
252
}
253
254