• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
write_at<F>(&mut self, offset: usize, f: F) -> io::Result<usize> where F: Fn(&mut Self) -> io::Result<usize>,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 
has_sufficient_buffer(&self, size: u32) -> bool46     fn has_sufficient_buffer(&self, size: u32) -> bool {
47         self.available_bytes() >= size as usize
48     }
49 }
50 
51 impl ZeroCopyReader for Reader {
read_to(&mut self, f: &mut File, count: usize, off: u64) -> io::Result<usize>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 {
write_from(&mut self, f: &mut File, count: usize, off: u64) -> io::Result<usize>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 {
new(tube: Arc<Mutex<Tube>>, slot: u32) -> Self69     fn new(tube: Arc<Mutex<Tube>>, slot: u32) -> Self {
70         Self { tube, slot }
71     }
72 
process_request(&self, request: &FsMappingRequest) -> io::Result<()>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 {
map( &self, mem_offset: u64, size: usize, fd: &dyn AsRawFd, file_offset: u64, prot: Protection, ) -> io::Result<()>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 
unmap(&self, offset: u64, size: u64) -> io::Result<()>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 
process_fs_queue<F: FileSystem + Sync>( queue: &mut Queue, server: &Arc<fuse::Server<F>>, tube: &Arc<Mutex<Tube>>, slot: u32, ) -> Result<()>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(avail_desc, total as u32);
159         queue.trigger_interrupt();
160     }
161 
162     Ok(())
163 }
164 
165 impl<F: FileSystem + Sync> Worker<F> {
new( queue: Queue, server: Arc<fuse::Server<F>>, tube: Arc<Mutex<Tube>>, slot: u32, ) -> 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 
run(&mut self, kill_evt: Event) -> Result<()>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