• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 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::cmp::min;
6 use std::collections::{btree_map, BTreeMap};
7 use std::ffi::{CStr, CString};
8 use std::fs::File;
9 use std::io::{self, Cursor, Read, Write};
10 use std::mem::{self, MaybeUninit};
11 use std::ops::Deref;
12 use std::os::unix::ffi::OsStrExt;
13 use std::os::unix::fs::FileExt;
14 use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
15 use std::path::Path;
16 
17 use sys_util::{read_dir::read_dir, syscall};
18 
19 use crate::protocol::*;
20 
21 // Tlopen and Tlcreate flags.  Taken from "include/net/9p/9p.h" in the linux tree.
22 const P9_RDONLY: u32 = 0o00000000;
23 const P9_WRONLY: u32 = 0o00000001;
24 const P9_RDWR: u32 = 0o00000002;
25 const P9_NOACCESS: u32 = 0o00000003;
26 const P9_CREATE: u32 = 0o00000100;
27 const P9_EXCL: u32 = 0o00000200;
28 const P9_NOCTTY: u32 = 0o00000400;
29 const P9_TRUNC: u32 = 0o00001000;
30 const P9_APPEND: u32 = 0o00002000;
31 const P9_NONBLOCK: u32 = 0o00004000;
32 const P9_DSYNC: u32 = 0o00010000;
33 const P9_FASYNC: u32 = 0o00020000;
34 const P9_DIRECT: u32 = 0o00040000;
35 const P9_LARGEFILE: u32 = 0o00100000;
36 const P9_DIRECTORY: u32 = 0o00200000;
37 const P9_NOFOLLOW: u32 = 0o00400000;
38 const P9_NOATIME: u32 = 0o01000000;
39 const _P9_CLOEXEC: u32 = 0o02000000;
40 const P9_SYNC: u32 = 0o04000000;
41 
42 // Mapping from 9P flags to libc flags.
43 const MAPPED_FLAGS: [(u32, i32); 16] = [
44     (P9_WRONLY, libc::O_WRONLY),
45     (P9_RDWR, libc::O_RDWR),
46     (P9_CREATE, libc::O_CREAT),
47     (P9_EXCL, libc::O_EXCL),
48     (P9_NOCTTY, libc::O_NOCTTY),
49     (P9_TRUNC, libc::O_TRUNC),
50     (P9_APPEND, libc::O_APPEND),
51     (P9_NONBLOCK, libc::O_NONBLOCK),
52     (P9_DSYNC, libc::O_DSYNC),
53     (P9_FASYNC, 0), // Unsupported
54     (P9_DIRECT, libc::O_DIRECT),
55     (P9_LARGEFILE, libc::O_LARGEFILE),
56     (P9_DIRECTORY, libc::O_DIRECTORY),
57     (P9_NOFOLLOW, libc::O_NOFOLLOW),
58     (P9_NOATIME, libc::O_NOATIME),
59     (P9_SYNC, libc::O_SYNC),
60 ];
61 
62 // 9P Qid types.  Taken from "include/net/9p/9p.h" in the linux tree.
63 const P9_QTDIR: u8 = 0x80;
64 const _P9_QTAPPEND: u8 = 0x40;
65 const _P9_QTEXCL: u8 = 0x20;
66 const _P9_QTMOUNT: u8 = 0x10;
67 const _P9_QTAUTH: u8 = 0x08;
68 const _P9_QTTMP: u8 = 0x04;
69 const P9_QTSYMLINK: u8 = 0x02;
70 const _P9_QTLINK: u8 = 0x01;
71 const P9_QTFILE: u8 = 0x00;
72 
73 // Bitmask values for the getattr request.
74 const _P9_GETATTR_MODE: u64 = 0x00000001;
75 const _P9_GETATTR_NLINK: u64 = 0x00000002;
76 const _P9_GETATTR_UID: u64 = 0x00000004;
77 const _P9_GETATTR_GID: u64 = 0x00000008;
78 const _P9_GETATTR_RDEV: u64 = 0x00000010;
79 const _P9_GETATTR_ATIME: u64 = 0x00000020;
80 const _P9_GETATTR_MTIME: u64 = 0x00000040;
81 const _P9_GETATTR_CTIME: u64 = 0x00000080;
82 const _P9_GETATTR_INO: u64 = 0x00000100;
83 const _P9_GETATTR_SIZE: u64 = 0x00000200;
84 const _P9_GETATTR_BLOCKS: u64 = 0x00000400;
85 
86 const _P9_GETATTR_BTIME: u64 = 0x00000800;
87 const _P9_GETATTR_GEN: u64 = 0x00001000;
88 const _P9_GETATTR_DATA_VERSION: u64 = 0x00002000;
89 
90 const P9_GETATTR_BASIC: u64 = 0x000007ff; /* Mask for fields up to BLOCKS */
91 const _P9_GETATTR_ALL: u64 = 0x00003fff; /* Mask for All fields above */
92 
93 // Bitmask values for the setattr request.
94 const P9_SETATTR_MODE: u32 = 0x00000001;
95 const P9_SETATTR_UID: u32 = 0x00000002;
96 const P9_SETATTR_GID: u32 = 0x00000004;
97 const P9_SETATTR_SIZE: u32 = 0x00000008;
98 const P9_SETATTR_ATIME: u32 = 0x00000010;
99 const P9_SETATTR_MTIME: u32 = 0x00000020;
100 const P9_SETATTR_CTIME: u32 = 0x00000040;
101 const P9_SETATTR_ATIME_SET: u32 = 0x00000080;
102 const P9_SETATTR_MTIME_SET: u32 = 0x00000100;
103 
104 // Minimum and maximum message size that we'll expect from the client.
105 const MIN_MESSAGE_SIZE: u32 = 256;
106 const MAX_MESSAGE_SIZE: u32 = ::std::u16::MAX as u32;
107 
108 #[derive(PartialEq, Eq)]
109 enum FileType {
110     Regular,
111     Directory,
112     Other,
113 }
114 
115 impl From<libc::mode_t> for FileType {
from(mode: libc::mode_t) -> Self116     fn from(mode: libc::mode_t) -> Self {
117         match mode & libc::S_IFMT {
118             libc::S_IFREG => FileType::Regular,
119             libc::S_IFDIR => FileType::Directory,
120             _ => FileType::Other,
121         }
122     }
123 }
124 
125 // Represents state that the server is holding on behalf of a client. Fids are somewhat like file
126 // descriptors but are not restricted to open files and directories. Fids are identified by a unique
127 // 32-bit number chosen by the client. Most messages sent by clients include a fid on which to
128 // operate. The fid in a Tattach message represents the root of the file system tree that the client
129 // is allowed to access. A client can create more fids by walking the directory tree from that fid.
130 struct Fid {
131     path: File,
132     file: Option<File>,
133     filetype: FileType,
134 }
135 
136 impl From<libc::stat64> for Qid {
from(st: libc::stat64) -> Qid137     fn from(st: libc::stat64) -> Qid {
138         let ty = match st.st_mode & libc::S_IFMT {
139             libc::S_IFDIR => P9_QTDIR,
140             libc::S_IFREG => P9_QTFILE,
141             libc::S_IFLNK => P9_QTSYMLINK,
142             _ => 0,
143         };
144 
145         Qid {
146             ty,
147             // TODO: deal with the 2038 problem before 2038
148             version: st.st_mtime as u32,
149             path: st.st_ino,
150         }
151     }
152 }
153 
statat(d: &File, name: &CStr, flags: libc::c_int) -> io::Result<libc::stat64>154 fn statat(d: &File, name: &CStr, flags: libc::c_int) -> io::Result<libc::stat64> {
155     let mut st = MaybeUninit::<libc::stat64>::zeroed();
156 
157     // Safe because the kernel will only write data in `st` and we check the return
158     // value.
159     let res = unsafe {
160         libc::fstatat64(
161             d.as_raw_fd(),
162             name.as_ptr(),
163             st.as_mut_ptr(),
164             flags | libc::AT_SYMLINK_NOFOLLOW,
165         )
166     };
167     if res >= 0 {
168         // Safe because the kernel guarantees that the struct is now fully initialized.
169         Ok(unsafe { st.assume_init() })
170     } else {
171         Err(io::Error::last_os_error())
172     }
173 }
174 
stat(f: &File) -> io::Result<libc::stat64>175 fn stat(f: &File) -> io::Result<libc::stat64> {
176     // Safe because this is a constant value and a valid C string.
177     let pathname = unsafe { CStr::from_bytes_with_nul_unchecked(b"\0") };
178 
179     statat(f, pathname, libc::AT_EMPTY_PATH)
180 }
181 
string_to_cstring(s: String) -> io::Result<CString>182 fn string_to_cstring(s: String) -> io::Result<CString> {
183     CString::new(s).map_err(|_| io::Error::from_raw_os_error(libc::EINVAL))
184 }
185 
error_to_rmessage(err: io::Error) -> Rmessage186 fn error_to_rmessage(err: io::Error) -> Rmessage {
187     let errno = if let Some(errno) = err.raw_os_error() {
188         errno
189     } else {
190         // Make a best-effort guess based on the kind.
191         match err.kind() {
192             io::ErrorKind::NotFound => libc::ENOENT,
193             io::ErrorKind::PermissionDenied => libc::EPERM,
194             io::ErrorKind::ConnectionRefused => libc::ECONNREFUSED,
195             io::ErrorKind::ConnectionReset => libc::ECONNRESET,
196             io::ErrorKind::ConnectionAborted => libc::ECONNABORTED,
197             io::ErrorKind::NotConnected => libc::ENOTCONN,
198             io::ErrorKind::AddrInUse => libc::EADDRINUSE,
199             io::ErrorKind::AddrNotAvailable => libc::EADDRNOTAVAIL,
200             io::ErrorKind::BrokenPipe => libc::EPIPE,
201             io::ErrorKind::AlreadyExists => libc::EEXIST,
202             io::ErrorKind::WouldBlock => libc::EWOULDBLOCK,
203             io::ErrorKind::InvalidInput => libc::EINVAL,
204             io::ErrorKind::InvalidData => libc::EINVAL,
205             io::ErrorKind::TimedOut => libc::ETIMEDOUT,
206             io::ErrorKind::WriteZero => libc::EIO,
207             io::ErrorKind::Interrupted => libc::EINTR,
208             io::ErrorKind::Other => libc::EIO,
209             io::ErrorKind::UnexpectedEof => libc::EIO,
210             _ => libc::EIO,
211         }
212     };
213 
214     Rmessage::Lerror(Rlerror {
215         ecode: errno as u32,
216     })
217 }
218 
219 // Sigh.. Cow requires the underlying type to implement Clone.
220 enum MaybeOwned<'b, T> {
221     Borrowed(&'b T),
222     Owned(T),
223 }
224 
225 impl<'a, T> Deref for MaybeOwned<'a, T> {
226     type Target = T;
deref(&self) -> &Self::Target227     fn deref(&self) -> &Self::Target {
228         use MaybeOwned::*;
229         match *self {
230             Borrowed(borrowed) => borrowed,
231             Owned(ref owned) => owned,
232         }
233     }
234 }
235 
ebadf() -> io::Error236 fn ebadf() -> io::Error {
237     io::Error::from_raw_os_error(libc::EBADF)
238 }
239 
240 pub type ServerIdMap<T> = BTreeMap<T, T>;
241 pub type ServerUidMap = ServerIdMap<libc::uid_t>;
242 pub type ServerGidMap = ServerIdMap<libc::gid_t>;
243 
map_id_from_host<T: Clone + Ord>(map: &ServerIdMap<T>, id: T) -> T244 fn map_id_from_host<T: Clone + Ord>(map: &ServerIdMap<T>, id: T) -> T {
245     map.get(&id).map_or(id.clone(), |v| v.clone())
246 }
247 
248 // Performs an ascii case insensitive lookup and returns an O_PATH fd for the entry, if found.
ascii_casefold_lookup(proc: &File, parent: &File, name: &[u8]) -> io::Result<File>249 fn ascii_casefold_lookup(proc: &File, parent: &File, name: &[u8]) -> io::Result<File> {
250     let mut dir = open_fid(proc, parent, P9_DIRECTORY)?;
251     let mut dirents = read_dir(&mut dir, 0)?;
252 
253     while let Some(entry) = dirents.next().transpose()? {
254         if name.eq_ignore_ascii_case(entry.name.to_bytes()) {
255             return lookup(parent, entry.name);
256         }
257     }
258 
259     Err(io::Error::from_raw_os_error(libc::ENOENT))
260 }
261 
lookup(parent: &File, name: &CStr) -> io::Result<File>262 fn lookup(parent: &File, name: &CStr) -> io::Result<File> {
263     // Safe because this doesn't modify any memory and we check the return value.
264     let fd = syscall!(unsafe {
265         libc::openat(
266             parent.as_raw_fd(),
267             name.as_ptr(),
268             libc::O_PATH | libc::O_NOFOLLOW | libc::O_CLOEXEC,
269         )
270     })?;
271 
272     // Safe because we just opened this fd.
273     Ok(unsafe { File::from_raw_fd(fd) })
274 }
275 
do_walk( proc: &File, wnames: Vec<String>, start: File, ascii_casefold: bool, mds: &mut Vec<libc::stat64>, ) -> io::Result<File>276 fn do_walk(
277     proc: &File,
278     wnames: Vec<String>,
279     start: File,
280     ascii_casefold: bool,
281     mds: &mut Vec<libc::stat64>,
282 ) -> io::Result<File> {
283     let mut current = start;
284 
285     for wname in wnames {
286         let name = string_to_cstring(wname)?;
287         current = lookup(&current, &name).or_else(|e| {
288             if ascii_casefold {
289                 if let Some(libc::ENOENT) = e.raw_os_error() {
290                     return ascii_casefold_lookup(proc, &current, name.to_bytes());
291                 }
292             }
293 
294             Err(e)
295         })?;
296         mds.push(stat(&current)?);
297     }
298 
299     Ok(current)
300 }
301 
open_fid(proc: &File, path: &File, p9_flags: u32) -> io::Result<File>302 fn open_fid(proc: &File, path: &File, p9_flags: u32) -> io::Result<File> {
303     let pathname = string_to_cstring(format!("self/fd/{}", path.as_raw_fd()))?;
304 
305     // We always open files with O_CLOEXEC.
306     let mut flags: i32 = libc::O_CLOEXEC;
307     for &(p9f, of) in &MAPPED_FLAGS {
308         if (p9_flags & p9f) != 0 {
309             flags |= of;
310         }
311     }
312 
313     if p9_flags & P9_NOACCESS == P9_RDONLY {
314         flags |= libc::O_RDONLY;
315     }
316 
317     // Safe because this doesn't modify any memory and we check the return value. We need to
318     // clear the O_NOFOLLOW flag because we want to follow the proc symlink.
319     let fd = syscall!(unsafe {
320         libc::openat(
321             proc.as_raw_fd(),
322             pathname.as_ptr(),
323             flags & !libc::O_NOFOLLOW,
324         )
325     })?;
326 
327     // Safe because we just opened this fd and we know it is valid.
328     Ok(unsafe { File::from_raw_fd(fd) })
329 }
330 
331 #[derive(Clone)]
332 pub struct Config {
333     pub root: Box<Path>,
334     pub msize: u32,
335 
336     pub uid_map: ServerUidMap,
337     pub gid_map: ServerGidMap,
338 
339     pub ascii_casefold: bool,
340 }
341 
342 impl Default for Config {
default() -> Config343     fn default() -> Config {
344         Config {
345             root: Path::new("/").into(),
346             msize: MAX_MESSAGE_SIZE,
347             uid_map: Default::default(),
348             gid_map: Default::default(),
349             ascii_casefold: false,
350         }
351     }
352 }
353 pub struct Server {
354     fids: BTreeMap<u32, Fid>,
355     proc: File,
356     cfg: Config,
357 }
358 
359 impl Server {
new<P: Into<Box<Path>>>( root: P, uid_map: ServerUidMap, gid_map: ServerGidMap, ) -> io::Result<Server>360     pub fn new<P: Into<Box<Path>>>(
361         root: P,
362         uid_map: ServerUidMap,
363         gid_map: ServerGidMap,
364     ) -> io::Result<Server> {
365         Server::with_config(Config {
366             root: root.into(),
367             msize: MAX_MESSAGE_SIZE,
368             uid_map,
369             gid_map,
370             ascii_casefold: false,
371         })
372     }
373 
with_config(cfg: Config) -> io::Result<Server>374     pub fn with_config(cfg: Config) -> io::Result<Server> {
375         // Safe because this is a valid c-string.
376         let proc_cstr = unsafe { CStr::from_bytes_with_nul_unchecked(b"/proc\0") };
377 
378         // Safe because this doesn't modify any memory and we check the return value.
379         let fd = syscall!(unsafe {
380             libc::openat(
381                 libc::AT_FDCWD,
382                 proc_cstr.as_ptr(),
383                 libc::O_PATH | libc::O_NOFOLLOW | libc::O_CLOEXEC,
384             )
385         })?;
386 
387         // Safe because we just opened this fd and we know it is valid.
388         let proc = unsafe { File::from_raw_fd(fd) };
389         Ok(Server {
390             fids: BTreeMap::new(),
391             proc,
392             cfg,
393         })
394     }
395 
keep_fds(&self) -> Vec<RawFd>396     pub fn keep_fds(&self) -> Vec<RawFd> {
397         vec![self.proc.as_raw_fd()]
398     }
399 
handle_message<R: Read, W: Write>( &mut self, reader: &mut R, writer: &mut W, ) -> io::Result<()>400     pub fn handle_message<R: Read, W: Write>(
401         &mut self,
402         reader: &mut R,
403         writer: &mut W,
404     ) -> io::Result<()> {
405         let Tframe { tag, msg } = WireFormat::decode(&mut reader.take(self.cfg.msize as u64))?;
406 
407         let rmsg = match msg {
408             Tmessage::Version(ref version) => self.version(version).map(Rmessage::Version),
409             Tmessage::Flush(ref flush) => self.flush(flush).and(Ok(Rmessage::Flush)),
410             Tmessage::Walk(walk) => self.walk(walk).map(Rmessage::Walk),
411             Tmessage::Read(ref read) => self.read(read).map(Rmessage::Read),
412             Tmessage::Write(ref write) => self.write(write).map(Rmessage::Write),
413             Tmessage::Clunk(ref clunk) => self.clunk(clunk).and(Ok(Rmessage::Clunk)),
414             Tmessage::Remove(ref remove) => self.remove(remove).and(Ok(Rmessage::Remove)),
415             Tmessage::Attach(ref attach) => self.attach(attach).map(Rmessage::Attach),
416             Tmessage::Auth(ref auth) => self.auth(auth).map(Rmessage::Auth),
417             Tmessage::Statfs(ref statfs) => self.statfs(statfs).map(Rmessage::Statfs),
418             Tmessage::Lopen(ref lopen) => self.lopen(lopen).map(Rmessage::Lopen),
419             Tmessage::Lcreate(lcreate) => self.lcreate(lcreate).map(Rmessage::Lcreate),
420             Tmessage::Symlink(ref symlink) => self.symlink(symlink).map(Rmessage::Symlink),
421             Tmessage::Mknod(ref mknod) => self.mknod(mknod).map(Rmessage::Mknod),
422             Tmessage::Rename(ref rename) => self.rename(rename).and(Ok(Rmessage::Rename)),
423             Tmessage::Readlink(ref readlink) => self.readlink(readlink).map(Rmessage::Readlink),
424             Tmessage::GetAttr(ref get_attr) => self.get_attr(get_attr).map(Rmessage::GetAttr),
425             Tmessage::SetAttr(ref set_attr) => self.set_attr(set_attr).and(Ok(Rmessage::SetAttr)),
426             Tmessage::XattrWalk(ref xattr_walk) => {
427                 self.xattr_walk(xattr_walk).map(Rmessage::XattrWalk)
428             }
429             Tmessage::XattrCreate(ref xattr_create) => self
430                 .xattr_create(xattr_create)
431                 .and(Ok(Rmessage::XattrCreate)),
432             Tmessage::Readdir(ref readdir) => self.readdir(readdir).map(Rmessage::Readdir),
433             Tmessage::Fsync(ref fsync) => self.fsync(fsync).and(Ok(Rmessage::Fsync)),
434             Tmessage::Lock(ref lock) => self.lock(lock).map(Rmessage::Lock),
435             Tmessage::GetLock(ref get_lock) => self.get_lock(get_lock).map(Rmessage::GetLock),
436             Tmessage::Link(link) => self.link(link).and(Ok(Rmessage::Link)),
437             Tmessage::Mkdir(mkdir) => self.mkdir(mkdir).map(Rmessage::Mkdir),
438             Tmessage::RenameAt(rename_at) => self.rename_at(rename_at).and(Ok(Rmessage::RenameAt)),
439             Tmessage::UnlinkAt(unlink_at) => self.unlink_at(unlink_at).and(Ok(Rmessage::UnlinkAt)),
440         };
441 
442         // Errors while handling requests are never fatal.
443         let response = Rframe {
444             tag,
445             msg: rmsg.unwrap_or_else(error_to_rmessage),
446         };
447 
448         response.encode(writer)?;
449         writer.flush()
450     }
451 
auth(&mut self, _auth: &Tauth) -> io::Result<Rauth>452     fn auth(&mut self, _auth: &Tauth) -> io::Result<Rauth> {
453         // Returning an error for the auth message means that the server does not require
454         // authentication.
455         Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP))
456     }
457 
attach(&mut self, attach: &Tattach) -> io::Result<Rattach>458     fn attach(&mut self, attach: &Tattach) -> io::Result<Rattach> {
459         // TODO: Check attach parameters
460         match self.fids.entry(attach.fid) {
461             btree_map::Entry::Vacant(entry) => {
462                 let root = CString::new(self.cfg.root.as_os_str().as_bytes())
463                     .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
464 
465                 // Safe because this doesn't modify any memory and we check the return value.
466                 let fd = syscall!(unsafe {
467                     libc::openat(
468                         libc::AT_FDCWD,
469                         root.as_ptr(),
470                         libc::O_PATH | libc::O_NOFOLLOW | libc::O_CLOEXEC,
471                     )
472                 })?;
473 
474                 let root_path = unsafe { File::from_raw_fd(fd) };
475                 let st = stat(&root_path)?;
476 
477                 let fid = Fid {
478                     // Safe because we just opened this fd.
479                     path: root_path,
480                     file: None,
481                     filetype: st.st_mode.into(),
482                 };
483                 let response = Rattach { qid: st.into() };
484                 entry.insert(fid);
485                 Ok(response)
486             }
487             btree_map::Entry::Occupied(_) => Err(io::Error::from_raw_os_error(libc::EBADF)),
488         }
489     }
490 
version(&mut self, version: &Tversion) -> io::Result<Rversion>491     fn version(&mut self, version: &Tversion) -> io::Result<Rversion> {
492         if version.msize < MIN_MESSAGE_SIZE {
493             return Err(io::Error::from_raw_os_error(libc::EINVAL));
494         }
495 
496         // A Tversion request clunks all open fids and terminates any pending I/O.
497         self.fids.clear();
498         self.cfg.msize = min(self.cfg.msize, version.msize);
499 
500         Ok(Rversion {
501             msize: self.cfg.msize,
502             version: if version.version == "9P2000.L" {
503                 String::from("9P2000.L")
504             } else {
505                 String::from("unknown")
506             },
507         })
508     }
509 
510     #[allow(clippy::unnecessary_wraps)]
flush(&mut self, _flush: &Tflush) -> io::Result<()>511     fn flush(&mut self, _flush: &Tflush) -> io::Result<()> {
512         // TODO: Since everything is synchronous we can't actually flush requests.
513         Ok(())
514     }
515 
walk(&mut self, walk: Twalk) -> io::Result<Rwalk>516     fn walk(&mut self, walk: Twalk) -> io::Result<Rwalk> {
517         // `newfid` must not currently be in use unless it is the same as `fid`.
518         if walk.fid != walk.newfid && self.fids.contains_key(&walk.newfid) {
519             return Err(io::Error::from_raw_os_error(libc::EBADF));
520         }
521 
522         // We need to walk the tree.  First get the starting path.
523         let start = self
524             .fids
525             .get(&walk.fid)
526             .ok_or_else(ebadf)
527             .and_then(|fid| fid.path.try_clone())?;
528 
529         // Now walk the tree and break on the first error, if any.
530         let expected_len = walk.wnames.len();
531         let mut mds = Vec::with_capacity(expected_len);
532         match do_walk(
533             &self.proc,
534             walk.wnames,
535             start,
536             self.cfg.ascii_casefold,
537             &mut mds,
538         ) {
539             Ok(end) => {
540                 // Store the new fid if the full walk succeeded.
541                 if mds.len() == expected_len {
542                     let st = mds.last().copied().map(Ok).unwrap_or_else(|| stat(&end))?;
543                     self.fids.insert(
544                         walk.newfid,
545                         Fid {
546                             path: end,
547                             file: None,
548                             filetype: st.st_mode.into(),
549                         },
550                     );
551                 }
552             }
553             Err(e) => {
554                 // Only return an error if it occurred on the first component.
555                 if mds.is_empty() {
556                     return Err(e);
557                 }
558             }
559         }
560 
561         Ok(Rwalk {
562             wqids: mds.into_iter().map(Qid::from).collect(),
563         })
564     }
565 
read(&mut self, read: &Tread) -> io::Result<Rread>566     fn read(&mut self, read: &Tread) -> io::Result<Rread> {
567         // Thankfully, `read` cannot be used to read directories in 9P2000.L.
568         let file = self
569             .fids
570             .get_mut(&read.fid)
571             .and_then(|fid| fid.file.as_mut())
572             .ok_or_else(ebadf)?;
573 
574         // Use an empty Rread struct to figure out the overhead of the header.
575         let header_size = Rframe {
576             tag: 0,
577             msg: Rmessage::Read(Rread {
578                 data: Data(Vec::new()),
579             }),
580         }
581         .byte_size();
582 
583         let capacity = min(self.cfg.msize - header_size, read.count);
584         let mut buf = Data(vec![0u8; capacity as usize]);
585 
586         let count = file.read_at(&mut buf, read.offset)?;
587         buf.truncate(count);
588 
589         Ok(Rread { data: buf })
590     }
591 
write(&mut self, write: &Twrite) -> io::Result<Rwrite>592     fn write(&mut self, write: &Twrite) -> io::Result<Rwrite> {
593         let file = self
594             .fids
595             .get_mut(&write.fid)
596             .and_then(|fid| fid.file.as_mut())
597             .ok_or_else(ebadf)?;
598 
599         let count = file.write_at(&write.data, write.offset)?;
600         Ok(Rwrite {
601             count: count as u32,
602         })
603     }
604 
clunk(&mut self, clunk: &Tclunk) -> io::Result<()>605     fn clunk(&mut self, clunk: &Tclunk) -> io::Result<()> {
606         match self.fids.entry(clunk.fid) {
607             btree_map::Entry::Vacant(_) => Err(io::Error::from_raw_os_error(libc::EBADF)),
608             btree_map::Entry::Occupied(entry) => {
609                 entry.remove();
610                 Ok(())
611             }
612         }
613     }
614 
remove(&mut self, _remove: &Tremove) -> io::Result<()>615     fn remove(&mut self, _remove: &Tremove) -> io::Result<()> {
616         // Since a file could be linked into multiple locations, there is no way to know exactly
617         // which path we are supposed to unlink. Linux uses unlink_at anyway, so we can just return
618         // an error here.
619         Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP))
620     }
621 
statfs(&mut self, statfs: &Tstatfs) -> io::Result<Rstatfs>622     fn statfs(&mut self, statfs: &Tstatfs) -> io::Result<Rstatfs> {
623         let fid = self.fids.get(&statfs.fid).ok_or_else(ebadf)?;
624         let mut buf = MaybeUninit::zeroed();
625 
626         // Safe because this will only modify `out` and we check the return value.
627         syscall!(unsafe { libc::fstatfs64(fid.path.as_raw_fd(), buf.as_mut_ptr()) })?;
628 
629         // Safe because this only has integer types and any value is valid.
630         let out = unsafe { buf.assume_init() };
631         Ok(Rstatfs {
632             ty: out.f_type as u32,
633             bsize: out.f_bsize as u32,
634             blocks: out.f_blocks,
635             bfree: out.f_bfree,
636             bavail: out.f_bavail,
637             files: out.f_files,
638             ffree: out.f_ffree,
639             // Safe because the fsid has only integer fields and the compiler will verify that is
640             // the same width as the `fsid` field in Rstatfs.
641             fsid: unsafe { mem::transmute(out.f_fsid) },
642             namelen: out.f_namelen as u32,
643         })
644     }
645 
lopen(&mut self, lopen: &Tlopen) -> io::Result<Rlopen>646     fn lopen(&mut self, lopen: &Tlopen) -> io::Result<Rlopen> {
647         let fid = self.fids.get_mut(&lopen.fid).ok_or_else(ebadf)?;
648 
649         let file = open_fid(&self.proc, &fid.path, lopen.flags)?;
650         let st = stat(&file)?;
651 
652         fid.file = Some(file);
653         let iounit = st.st_blksize as u32;
654         Ok(Rlopen {
655             qid: st.into(),
656             iounit,
657         })
658     }
659 
lcreate(&mut self, lcreate: Tlcreate) -> io::Result<Rlcreate>660     fn lcreate(&mut self, lcreate: Tlcreate) -> io::Result<Rlcreate> {
661         let fid = self.fids.get_mut(&lcreate.fid).ok_or_else(ebadf)?;
662 
663         if fid.filetype != FileType::Directory {
664             return Err(io::Error::from_raw_os_error(libc::ENOTDIR));
665         }
666 
667         let mut flags: i32 = libc::O_CLOEXEC | libc::O_CREAT | libc::O_EXCL;
668         for &(p9f, of) in &MAPPED_FLAGS {
669             if (lcreate.flags & p9f) != 0 {
670                 flags |= of;
671             }
672         }
673         if lcreate.flags & P9_NOACCESS == P9_RDONLY {
674             flags |= libc::O_RDONLY;
675         }
676 
677         let name = string_to_cstring(lcreate.name)?;
678 
679         // Safe because this doesn't modify any memory and we check the return value.
680         let fd = syscall!(unsafe {
681             libc::openat(fid.path.as_raw_fd(), name.as_ptr(), flags, lcreate.mode)
682         })?;
683 
684         // Safe because we just opened this fd and we know it is valid.
685         let file = unsafe { File::from_raw_fd(fd) };
686         let st = stat(&file)?;
687         let iounit = st.st_blksize as u32;
688 
689         fid.file = Some(file);
690 
691         // This fid now refers to the newly created file so we need to update the O_PATH fd for it
692         // as well.
693         fid.path = lookup(&fid.path, &name)?;
694 
695         Ok(Rlcreate {
696             qid: st.into(),
697             iounit,
698         })
699     }
700 
symlink(&mut self, _symlink: &Tsymlink) -> io::Result<Rsymlink>701     fn symlink(&mut self, _symlink: &Tsymlink) -> io::Result<Rsymlink> {
702         // symlinks are not allowed.
703         Err(io::Error::from_raw_os_error(libc::EACCES))
704     }
705 
mknod(&mut self, _mknod: &Tmknod) -> io::Result<Rmknod>706     fn mknod(&mut self, _mknod: &Tmknod) -> io::Result<Rmknod> {
707         // No nodes either.
708         Err(io::Error::from_raw_os_error(libc::EACCES))
709     }
710 
rename(&mut self, _rename: &Trename) -> io::Result<()>711     fn rename(&mut self, _rename: &Trename) -> io::Result<()> {
712         // We cannot support this as an inode may be linked into multiple directories but we don't
713         // know which one the client wants us to rename. Linux uses rename_at anyway, so we don't
714         // need to worry about this.
715         Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP))
716     }
717 
readlink(&mut self, _readlink: &Treadlink) -> io::Result<Rreadlink>718     fn readlink(&mut self, _readlink: &Treadlink) -> io::Result<Rreadlink> {
719         // symlinks are not allowed
720         Err(io::Error::from_raw_os_error(libc::EACCES))
721     }
722 
get_attr(&mut self, get_attr: &Tgetattr) -> io::Result<Rgetattr>723     fn get_attr(&mut self, get_attr: &Tgetattr) -> io::Result<Rgetattr> {
724         let fid = self.fids.get_mut(&get_attr.fid).ok_or_else(ebadf)?;
725 
726         let st = stat(&fid.path)?;
727 
728         Ok(Rgetattr {
729             valid: P9_GETATTR_BASIC,
730             qid: st.into(),
731             mode: st.st_mode,
732             uid: map_id_from_host(&self.cfg.uid_map, st.st_uid),
733             gid: map_id_from_host(&self.cfg.gid_map, st.st_gid),
734             nlink: st.st_nlink as u64,
735             rdev: st.st_rdev,
736             size: st.st_size as u64,
737             blksize: st.st_blksize as u64,
738             blocks: st.st_blocks as u64,
739             atime_sec: st.st_atime as u64,
740             atime_nsec: st.st_atime_nsec as u64,
741             mtime_sec: st.st_mtime as u64,
742             mtime_nsec: st.st_mtime_nsec as u64,
743             ctime_sec: st.st_ctime as u64,
744             ctime_nsec: st.st_ctime_nsec as u64,
745             btime_sec: 0,
746             btime_nsec: 0,
747             gen: 0,
748             data_version: 0,
749         })
750     }
751 
set_attr(&mut self, set_attr: &Tsetattr) -> io::Result<()>752     fn set_attr(&mut self, set_attr: &Tsetattr) -> io::Result<()> {
753         let fid = self.fids.get(&set_attr.fid).ok_or_else(ebadf)?;
754 
755         let file = if let Some(ref file) = fid.file {
756             MaybeOwned::Borrowed(file)
757         } else {
758             let flags = match fid.filetype {
759                 FileType::Regular => P9_RDWR,
760                 FileType::Directory => P9_RDONLY | P9_DIRECTORY,
761                 FileType::Other => P9_RDWR,
762             };
763             MaybeOwned::Owned(open_fid(&self.proc, &fid.path, P9_NONBLOCK | flags)?)
764         };
765 
766         if set_attr.valid & P9_SETATTR_MODE != 0 {
767             // Safe because this doesn't modify any memory and we check the return value.
768             syscall!(unsafe { libc::fchmod(file.as_raw_fd(), set_attr.mode) })?;
769         }
770 
771         if set_attr.valid & (P9_SETATTR_UID | P9_SETATTR_GID) != 0 {
772             let uid = if set_attr.valid & P9_SETATTR_UID != 0 {
773                 set_attr.uid
774             } else {
775                 -1i32 as u32
776             };
777             let gid = if set_attr.valid & P9_SETATTR_GID != 0 {
778                 set_attr.gid
779             } else {
780                 -1i32 as u32
781             };
782 
783             // Safe because this doesn't modify any memory and we check the return value.
784             syscall!(unsafe { libc::fchown(file.as_raw_fd(), uid, gid) })?;
785         }
786 
787         if set_attr.valid & P9_SETATTR_SIZE != 0 {
788             file.set_len(set_attr.size)?;
789         }
790 
791         if set_attr.valid & (P9_SETATTR_ATIME | P9_SETATTR_MTIME) != 0 {
792             let times = [
793                 libc::timespec {
794                     tv_sec: set_attr.atime_sec as _,
795                     tv_nsec: if set_attr.valid & P9_SETATTR_ATIME == 0 {
796                         libc::UTIME_OMIT
797                     } else if set_attr.valid & P9_SETATTR_ATIME_SET == 0 {
798                         libc::UTIME_NOW
799                     } else {
800                         set_attr.atime_nsec as _
801                     },
802                 },
803                 libc::timespec {
804                     tv_sec: set_attr.mtime_sec as _,
805                     tv_nsec: if set_attr.valid & P9_SETATTR_MTIME == 0 {
806                         libc::UTIME_OMIT
807                     } else if set_attr.valid & P9_SETATTR_MTIME_SET == 0 {
808                         libc::UTIME_NOW
809                     } else {
810                         set_attr.mtime_nsec as _
811                     },
812                 },
813             ];
814 
815             // Safe because file is valid and we have initialized times fully.
816             let ret = unsafe { libc::futimens(file.as_raw_fd(), &times as *const libc::timespec) };
817             if ret < 0 {
818                 return Err(io::Error::last_os_error());
819             }
820         }
821 
822         // The ctime would have been updated by any of the above operations so we only
823         // need to change it if it was the only option given.
824         if set_attr.valid & P9_SETATTR_CTIME != 0 && set_attr.valid & (!P9_SETATTR_CTIME) == 0 {
825             // Setting -1 as the uid and gid will not actually change anything but will
826             // still update the ctime.
827             let ret = unsafe {
828                 libc::fchown(
829                     file.as_raw_fd(),
830                     libc::uid_t::max_value(),
831                     libc::gid_t::max_value(),
832                 )
833             };
834             if ret < 0 {
835                 return Err(io::Error::last_os_error());
836             }
837         }
838 
839         Ok(())
840     }
841 
xattr_walk(&mut self, _xattr_walk: &Txattrwalk) -> io::Result<Rxattrwalk>842     fn xattr_walk(&mut self, _xattr_walk: &Txattrwalk) -> io::Result<Rxattrwalk> {
843         Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP))
844     }
845 
xattr_create(&mut self, _xattr_create: &Txattrcreate) -> io::Result<()>846     fn xattr_create(&mut self, _xattr_create: &Txattrcreate) -> io::Result<()> {
847         Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP))
848     }
849 
readdir(&mut self, readdir: &Treaddir) -> io::Result<Rreaddir>850     fn readdir(&mut self, readdir: &Treaddir) -> io::Result<Rreaddir> {
851         let fid = self.fids.get_mut(&readdir.fid).ok_or_else(ebadf)?;
852 
853         if fid.filetype != FileType::Directory {
854             return Err(io::Error::from_raw_os_error(libc::ENOTDIR));
855         }
856 
857         // Use an empty Rreaddir struct to figure out the maximum number of bytes that
858         // can be returned.
859         let header_size = Rframe {
860             tag: 0,
861             msg: Rmessage::Readdir(Rreaddir {
862                 data: Data(Vec::new()),
863             }),
864         }
865         .byte_size();
866         let count = min(self.cfg.msize - header_size, readdir.count);
867         let mut cursor = Cursor::new(Vec::with_capacity(count as usize));
868 
869         let dir = fid.file.as_mut().ok_or_else(ebadf)?;
870         let mut dirents = read_dir(dir, readdir.offset as libc::off64_t)?;
871         while let Some(dirent) = dirents.next().transpose()? {
872             let st = statat(&fid.path, dirent.name, 0)?;
873 
874             let name = dirent
875                 .name
876                 .to_str()
877                 .map(String::from)
878                 .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
879 
880             let entry = Dirent {
881                 qid: st.into(),
882                 offset: dirent.offset,
883                 ty: dirent.type_,
884                 name,
885             };
886 
887             let byte_size = entry.byte_size() as usize;
888 
889             if cursor.get_ref().capacity() - cursor.get_ref().len() < byte_size {
890                 // No more room in the buffer.
891                 break;
892             }
893 
894             entry.encode(&mut cursor)?;
895         }
896 
897         Ok(Rreaddir {
898             data: Data(cursor.into_inner()),
899         })
900     }
901 
fsync(&mut self, fsync: &Tfsync) -> io::Result<()>902     fn fsync(&mut self, fsync: &Tfsync) -> io::Result<()> {
903         let file = self
904             .fids
905             .get(&fsync.fid)
906             .and_then(|fid| fid.file.as_ref())
907             .ok_or_else(ebadf)?;
908 
909         if fsync.datasync == 0 {
910             file.sync_all()?;
911         } else {
912             file.sync_data()?;
913         }
914         Ok(())
915     }
916 
lock(&mut self, _lock: &Tlock) -> io::Result<Rlock>917     fn lock(&mut self, _lock: &Tlock) -> io::Result<Rlock> {
918         // File locking is not supported.
919         Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP))
920     }
get_lock(&mut self, _get_lock: &Tgetlock) -> io::Result<Rgetlock>921     fn get_lock(&mut self, _get_lock: &Tgetlock) -> io::Result<Rgetlock> {
922         // File locking is not supported.
923         Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP))
924     }
925 
link(&mut self, link: Tlink) -> io::Result<()>926     fn link(&mut self, link: Tlink) -> io::Result<()> {
927         let target = self.fids.get(&link.fid).ok_or_else(ebadf)?;
928         let path = string_to_cstring(format!("self/fd/{}", target.path.as_raw_fd()))?;
929 
930         let dir = self.fids.get(&link.dfid).ok_or_else(ebadf)?;
931         let name = string_to_cstring(link.name)?;
932 
933         // Safe because this doesn't modify any memory and we check the return value.
934         syscall!(unsafe {
935             libc::linkat(
936                 self.proc.as_raw_fd(),
937                 path.as_ptr(),
938                 dir.path.as_raw_fd(),
939                 name.as_ptr(),
940                 libc::AT_SYMLINK_FOLLOW,
941             )
942         })?;
943         Ok(())
944     }
945 
mkdir(&mut self, mkdir: Tmkdir) -> io::Result<Rmkdir>946     fn mkdir(&mut self, mkdir: Tmkdir) -> io::Result<Rmkdir> {
947         let fid = self.fids.get(&mkdir.dfid).ok_or_else(ebadf)?;
948         let name = string_to_cstring(mkdir.name)?;
949 
950         // Safe because this doesn't modify any memory and we check the return value.
951         syscall!(unsafe { libc::mkdirat(fid.path.as_raw_fd(), name.as_ptr(), mkdir.mode) })?;
952         Ok(Rmkdir {
953             qid: statat(&fid.path, &name, 0).map(Qid::from)?,
954         })
955     }
956 
rename_at(&mut self, rename_at: Trenameat) -> io::Result<()>957     fn rename_at(&mut self, rename_at: Trenameat) -> io::Result<()> {
958         let olddir = self.fids.get(&rename_at.olddirfid).ok_or_else(ebadf)?;
959         let oldname = string_to_cstring(rename_at.oldname)?;
960 
961         let newdir = self.fids.get(&rename_at.newdirfid).ok_or_else(ebadf)?;
962         let newname = string_to_cstring(rename_at.newname)?;
963 
964         // Safe because this doesn't modify any memory and we check the return value.
965         syscall!(unsafe {
966             libc::renameat(
967                 olddir.path.as_raw_fd(),
968                 oldname.as_ptr(),
969                 newdir.path.as_raw_fd(),
970                 newname.as_ptr(),
971             )
972         })?;
973 
974         Ok(())
975     }
976 
unlink_at(&mut self, unlink_at: Tunlinkat) -> io::Result<()>977     fn unlink_at(&mut self, unlink_at: Tunlinkat) -> io::Result<()> {
978         let dir = self.fids.get(&unlink_at.dirfd).ok_or_else(ebadf)?;
979         let name = string_to_cstring(unlink_at.name)?;
980 
981         syscall!(unsafe {
982             libc::unlinkat(
983                 dir.path.as_raw_fd(),
984                 name.as_ptr(),
985                 unlink_at.flags as libc::c_int,
986             )
987         })?;
988 
989         Ok(())
990     }
991 }
992 
993 #[cfg(test)]
994 mod tests;
995