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