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 libchromeos::{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(¤t, &name).or_else(|e| {
288 if ascii_casefold {
289 if let Some(libc::ENOENT) = e.raw_os_error() {
290 return ascii_casefold_lookup(proc, ¤t, name.to_bytes());
291 }
292 }
293
294 Err(e)
295 })?;
296 mds.push(stat(¤t)?);
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
flush(&mut self, _flush: &Tflush) -> io::Result<()>510 fn flush(&mut self, _flush: &Tflush) -> io::Result<()> {
511 // TODO: Since everything is synchronous we can't actually flush requests.
512 Ok(())
513 }
514
walk(&mut self, walk: Twalk) -> io::Result<Rwalk>515 fn walk(&mut self, walk: Twalk) -> io::Result<Rwalk> {
516 // `newfid` must not currently be in use unless it is the same as `fid`.
517 if walk.fid != walk.newfid && self.fids.contains_key(&walk.newfid) {
518 return Err(io::Error::from_raw_os_error(libc::EBADF));
519 }
520
521 // We need to walk the tree. First get the starting path.
522 let start = self
523 .fids
524 .get(&walk.fid)
525 .ok_or_else(ebadf)
526 .and_then(|fid| fid.path.try_clone())?;
527
528 // Now walk the tree and break on the first error, if any.
529 let expected_len = walk.wnames.len();
530 let mut mds = Vec::with_capacity(expected_len);
531 match do_walk(
532 &self.proc,
533 walk.wnames,
534 start,
535 self.cfg.ascii_casefold,
536 &mut mds,
537 ) {
538 Ok(end) => {
539 // Store the new fid if the full walk succeeded.
540 if mds.len() == expected_len {
541 let st = mds.last().copied().map(Ok).unwrap_or_else(|| stat(&end))?;
542 self.fids.insert(
543 walk.newfid,
544 Fid {
545 path: end,
546 file: None,
547 filetype: st.st_mode.into(),
548 },
549 );
550 }
551 }
552 Err(e) => {
553 // Only return an error if it occurred on the first component.
554 if mds.is_empty() {
555 return Err(e);
556 }
557 }
558 }
559
560 Ok(Rwalk {
561 wqids: mds.into_iter().map(Qid::from).collect(),
562 })
563 }
564
read(&mut self, read: &Tread) -> io::Result<Rread>565 fn read(&mut self, read: &Tread) -> io::Result<Rread> {
566 // Thankfully, `read` cannot be used to read directories in 9P2000.L.
567 let file = self
568 .fids
569 .get_mut(&read.fid)
570 .and_then(|fid| fid.file.as_mut())
571 .ok_or_else(ebadf)?;
572
573 // Use an empty Rread struct to figure out the overhead of the header.
574 let header_size = Rframe {
575 tag: 0,
576 msg: Rmessage::Read(Rread {
577 data: Data(Vec::new()),
578 }),
579 }
580 .byte_size();
581
582 let capacity = min(self.cfg.msize - header_size, read.count);
583 let mut buf = Data(vec![0u8; capacity as usize]);
584
585 let count = file.read_at(&mut buf, read.offset)?;
586 buf.truncate(count);
587
588 Ok(Rread { data: buf })
589 }
590
write(&mut self, write: &Twrite) -> io::Result<Rwrite>591 fn write(&mut self, write: &Twrite) -> io::Result<Rwrite> {
592 let file = self
593 .fids
594 .get_mut(&write.fid)
595 .and_then(|fid| fid.file.as_mut())
596 .ok_or_else(ebadf)?;
597
598 let count = file.write_at(&write.data, write.offset)?;
599 Ok(Rwrite {
600 count: count as u32,
601 })
602 }
603
clunk(&mut self, clunk: &Tclunk) -> io::Result<()>604 fn clunk(&mut self, clunk: &Tclunk) -> io::Result<()> {
605 match self.fids.entry(clunk.fid) {
606 btree_map::Entry::Vacant(_) => Err(io::Error::from_raw_os_error(libc::EBADF)),
607 btree_map::Entry::Occupied(entry) => {
608 entry.remove();
609 Ok(())
610 }
611 }
612 }
613
remove(&mut self, _remove: &Tremove) -> io::Result<()>614 fn remove(&mut self, _remove: &Tremove) -> io::Result<()> {
615 // Since a file could be linked into multiple locations, there is no way to know exactly
616 // which path we are supposed to unlink. Linux uses unlink_at anyway, so we can just return
617 // an error here.
618 Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP))
619 }
620
statfs(&mut self, statfs: &Tstatfs) -> io::Result<Rstatfs>621 fn statfs(&mut self, statfs: &Tstatfs) -> io::Result<Rstatfs> {
622 let fid = self.fids.get(&statfs.fid).ok_or_else(ebadf)?;
623 let mut buf = MaybeUninit::zeroed();
624
625 // Safe because this will only modify `out` and we check the return value.
626 syscall!(unsafe { libc::fstatfs64(fid.path.as_raw_fd(), buf.as_mut_ptr()) })?;
627
628 // Safe because this only has integer types and any value is valid.
629 let out = unsafe { buf.assume_init() };
630 Ok(Rstatfs {
631 ty: out.f_type as u32,
632 bsize: out.f_bsize as u32,
633 blocks: out.f_blocks,
634 bfree: out.f_bfree,
635 bavail: out.f_bavail,
636 files: out.f_files,
637 ffree: out.f_ffree,
638 // Safe because the fsid has only integer fields and the compiler will verify that is
639 // the same width as the `fsid` field in Rstatfs.
640 fsid: unsafe { mem::transmute(out.f_fsid) },
641 namelen: out.f_namelen as u32,
642 })
643 }
644
lopen(&mut self, lopen: &Tlopen) -> io::Result<Rlopen>645 fn lopen(&mut self, lopen: &Tlopen) -> io::Result<Rlopen> {
646 let fid = self.fids.get_mut(&lopen.fid).ok_or_else(ebadf)?;
647
648 let file = open_fid(&self.proc, &fid.path, lopen.flags)?;
649 let st = stat(&file)?;
650
651 fid.file = Some(file);
652 let iounit = st.st_blksize as u32;
653 Ok(Rlopen {
654 qid: st.into(),
655 iounit,
656 })
657 }
658
lcreate(&mut self, lcreate: Tlcreate) -> io::Result<Rlcreate>659 fn lcreate(&mut self, lcreate: Tlcreate) -> io::Result<Rlcreate> {
660 let fid = self.fids.get_mut(&lcreate.fid).ok_or_else(ebadf)?;
661
662 if fid.filetype != FileType::Directory {
663 return Err(io::Error::from_raw_os_error(libc::ENOTDIR));
664 }
665
666 let mut flags: i32 = libc::O_CLOEXEC | libc::O_CREAT | libc::O_EXCL;
667 for &(p9f, of) in &MAPPED_FLAGS {
668 if (lcreate.flags & p9f) != 0 {
669 flags |= of;
670 }
671 }
672 if lcreate.flags & P9_NOACCESS == P9_RDONLY {
673 flags |= libc::O_RDONLY;
674 }
675
676 let name = string_to_cstring(lcreate.name)?;
677
678 // Safe because this doesn't modify any memory and we check the return value.
679 let fd = syscall!(unsafe {
680 libc::openat(fid.path.as_raw_fd(), name.as_ptr(), flags, lcreate.mode)
681 })?;
682
683 // Safe because we just opened this fd and we know it is valid.
684 let file = unsafe { File::from_raw_fd(fd) };
685 let st = stat(&file)?;
686 let iounit = st.st_blksize as u32;
687
688 fid.file = Some(file);
689
690 // This fid now refers to the newly created file so we need to update the O_PATH fd for it
691 // as well.
692 fid.path = lookup(&fid.path, &name)?;
693
694 Ok(Rlcreate {
695 qid: st.into(),
696 iounit,
697 })
698 }
699
symlink(&mut self, _symlink: &Tsymlink) -> io::Result<Rsymlink>700 fn symlink(&mut self, _symlink: &Tsymlink) -> io::Result<Rsymlink> {
701 // symlinks are not allowed.
702 Err(io::Error::from_raw_os_error(libc::EACCES))
703 }
704
mknod(&mut self, _mknod: &Tmknod) -> io::Result<Rmknod>705 fn mknod(&mut self, _mknod: &Tmknod) -> io::Result<Rmknod> {
706 // No nodes either.
707 Err(io::Error::from_raw_os_error(libc::EACCES))
708 }
709
rename(&mut self, _rename: &Trename) -> io::Result<()>710 fn rename(&mut self, _rename: &Trename) -> io::Result<()> {
711 // We cannot support this as an inode may be linked into multiple directories but we don't
712 // know which one the client wants us to rename. Linux uses rename_at anyway, so we don't
713 // need to worry about this.
714 Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP))
715 }
716
readlink(&mut self, _readlink: &Treadlink) -> io::Result<Rreadlink>717 fn readlink(&mut self, _readlink: &Treadlink) -> io::Result<Rreadlink> {
718 // symlinks are not allowed
719 Err(io::Error::from_raw_os_error(libc::EACCES))
720 }
721
get_attr(&mut self, get_attr: &Tgetattr) -> io::Result<Rgetattr>722 fn get_attr(&mut self, get_attr: &Tgetattr) -> io::Result<Rgetattr> {
723 let fid = self.fids.get_mut(&get_attr.fid).ok_or_else(ebadf)?;
724
725 let st = stat(&fid.path)?;
726
727 Ok(Rgetattr {
728 valid: P9_GETATTR_BASIC,
729 qid: st.into(),
730 mode: st.st_mode,
731 uid: map_id_from_host(&self.cfg.uid_map, st.st_uid),
732 gid: map_id_from_host(&self.cfg.gid_map, st.st_gid),
733 nlink: st.st_nlink as u64,
734 rdev: st.st_rdev,
735 size: st.st_size as u64,
736 blksize: st.st_blksize as u64,
737 blocks: st.st_blocks as u64,
738 atime_sec: st.st_atime as u64,
739 atime_nsec: st.st_atime_nsec as u64,
740 mtime_sec: st.st_mtime as u64,
741 mtime_nsec: st.st_mtime_nsec as u64,
742 ctime_sec: st.st_ctime as u64,
743 ctime_nsec: st.st_ctime_nsec as u64,
744 btime_sec: 0,
745 btime_nsec: 0,
746 gen: 0,
747 data_version: 0,
748 })
749 }
750
set_attr(&mut self, set_attr: &Tsetattr) -> io::Result<()>751 fn set_attr(&mut self, set_attr: &Tsetattr) -> io::Result<()> {
752 let blocked_ops = P9_SETATTR_MODE | P9_SETATTR_UID | P9_SETATTR_GID;
753 if set_attr.valid & blocked_ops != 0 {
754 return Err(io::Error::from_raw_os_error(libc::EPERM));
755 }
756
757 let fid = self.fids.get(&set_attr.fid).ok_or_else(ebadf)?;
758
759 let file = if let Some(ref file) = fid.file {
760 MaybeOwned::Borrowed(file)
761 } else {
762 let flags = match fid.filetype {
763 FileType::Regular => P9_RDWR,
764 FileType::Directory => P9_RDONLY | P9_DIRECTORY,
765 FileType::Other => P9_RDWR,
766 };
767 MaybeOwned::Owned(open_fid(&self.proc, &fid.path, P9_NONBLOCK | flags)?)
768 };
769
770 if set_attr.valid & P9_SETATTR_SIZE != 0 {
771 file.set_len(set_attr.size)?;
772 }
773
774 if set_attr.valid & (P9_SETATTR_ATIME | P9_SETATTR_MTIME) != 0 {
775 let times = [
776 libc::timespec {
777 tv_sec: set_attr.atime_sec as _,
778 tv_nsec: if set_attr.valid & P9_SETATTR_ATIME == 0 {
779 libc::UTIME_OMIT
780 } else if set_attr.valid & P9_SETATTR_ATIME_SET == 0 {
781 libc::UTIME_NOW
782 } else {
783 set_attr.atime_nsec as _
784 },
785 },
786 libc::timespec {
787 tv_sec: set_attr.mtime_sec as _,
788 tv_nsec: if set_attr.valid & P9_SETATTR_MTIME == 0 {
789 libc::UTIME_OMIT
790 } else if set_attr.valid & P9_SETATTR_MTIME_SET == 0 {
791 libc::UTIME_NOW
792 } else {
793 set_attr.mtime_nsec as _
794 },
795 },
796 ];
797
798 // Safe because file is valid and we have initialized times fully.
799 let ret = unsafe { libc::futimens(file.as_raw_fd(), × as *const libc::timespec) };
800 if ret < 0 {
801 return Err(io::Error::last_os_error());
802 }
803 }
804
805 // The ctime would have been updated by any of the above operations so we only
806 // need to change it if it was the only option given.
807 if set_attr.valid & P9_SETATTR_CTIME != 0 && set_attr.valid & (!P9_SETATTR_CTIME) == 0 {
808 // Setting -1 as the uid and gid will not actually change anything but will
809 // still update the ctime.
810 let ret = unsafe {
811 libc::fchown(
812 file.as_raw_fd(),
813 libc::uid_t::max_value(),
814 libc::gid_t::max_value(),
815 )
816 };
817 if ret < 0 {
818 return Err(io::Error::last_os_error());
819 }
820 }
821
822 Ok(())
823 }
824
xattr_walk(&mut self, _xattr_walk: &Txattrwalk) -> io::Result<Rxattrwalk>825 fn xattr_walk(&mut self, _xattr_walk: &Txattrwalk) -> io::Result<Rxattrwalk> {
826 Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP))
827 }
828
xattr_create(&mut self, _xattr_create: &Txattrcreate) -> io::Result<()>829 fn xattr_create(&mut self, _xattr_create: &Txattrcreate) -> io::Result<()> {
830 Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP))
831 }
832
readdir(&mut self, readdir: &Treaddir) -> io::Result<Rreaddir>833 fn readdir(&mut self, readdir: &Treaddir) -> io::Result<Rreaddir> {
834 let fid = self.fids.get_mut(&readdir.fid).ok_or_else(ebadf)?;
835
836 if fid.filetype != FileType::Directory {
837 return Err(io::Error::from_raw_os_error(libc::ENOTDIR));
838 }
839
840 // Use an empty Rreaddir struct to figure out the maximum number of bytes that
841 // can be returned.
842 let header_size = Rframe {
843 tag: 0,
844 msg: Rmessage::Readdir(Rreaddir {
845 data: Data(Vec::new()),
846 }),
847 }
848 .byte_size();
849 let count = min(self.cfg.msize - header_size, readdir.count);
850 let mut cursor = Cursor::new(Vec::with_capacity(count as usize));
851
852 let dir = fid.file.as_mut().ok_or_else(ebadf)?;
853 let mut dirents = read_dir(dir, readdir.offset as libc::off64_t)?;
854 while let Some(dirent) = dirents.next().transpose()? {
855 let st = statat(&fid.path, &dirent.name, 0)?;
856
857 let name = dirent
858 .name
859 .to_str()
860 .map(String::from)
861 .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
862
863 let entry = Dirent {
864 qid: st.into(),
865 offset: dirent.offset,
866 ty: dirent.type_,
867 name,
868 };
869
870 let byte_size = entry.byte_size() as usize;
871
872 if cursor.get_ref().capacity() - cursor.get_ref().len() < byte_size {
873 // No more room in the buffer.
874 break;
875 }
876
877 entry.encode(&mut cursor)?;
878 }
879
880 Ok(Rreaddir {
881 data: Data(cursor.into_inner()),
882 })
883 }
884
fsync(&mut self, fsync: &Tfsync) -> io::Result<()>885 fn fsync(&mut self, fsync: &Tfsync) -> io::Result<()> {
886 let file = self
887 .fids
888 .get(&fsync.fid)
889 .and_then(|fid| fid.file.as_ref())
890 .ok_or_else(ebadf)?;
891
892 if fsync.datasync == 0 {
893 file.sync_all()?;
894 } else {
895 file.sync_data()?;
896 }
897 Ok(())
898 }
899
lock(&mut self, _lock: &Tlock) -> io::Result<Rlock>900 fn lock(&mut self, _lock: &Tlock) -> io::Result<Rlock> {
901 // File locking is not supported.
902 Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP))
903 }
get_lock(&mut self, _get_lock: &Tgetlock) -> io::Result<Rgetlock>904 fn get_lock(&mut self, _get_lock: &Tgetlock) -> io::Result<Rgetlock> {
905 // File locking is not supported.
906 Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP))
907 }
908
link(&mut self, link: Tlink) -> io::Result<()>909 fn link(&mut self, link: Tlink) -> io::Result<()> {
910 let target = self.fids.get(&link.fid).ok_or_else(ebadf)?;
911 let path = string_to_cstring(format!("self/fd/{}", target.path.as_raw_fd()))?;
912
913 let dir = self.fids.get(&link.dfid).ok_or_else(ebadf)?;
914 let name = string_to_cstring(link.name)?;
915
916 // Safe because this doesn't modify any memory and we check the return value.
917 syscall!(unsafe {
918 libc::linkat(
919 self.proc.as_raw_fd(),
920 path.as_ptr(),
921 dir.path.as_raw_fd(),
922 name.as_ptr(),
923 libc::AT_SYMLINK_FOLLOW,
924 )
925 })?;
926 Ok(())
927 }
928
mkdir(&mut self, mkdir: Tmkdir) -> io::Result<Rmkdir>929 fn mkdir(&mut self, mkdir: Tmkdir) -> io::Result<Rmkdir> {
930 let fid = self.fids.get(&mkdir.dfid).ok_or_else(ebadf)?;
931 let name = string_to_cstring(mkdir.name)?;
932
933 // Safe because this doesn't modify any memory and we check the return value.
934 syscall!(unsafe { libc::mkdirat(fid.path.as_raw_fd(), name.as_ptr(), mkdir.mode) })?;
935 Ok(Rmkdir {
936 qid: statat(&fid.path, &name, 0).map(Qid::from)?,
937 })
938 }
939
rename_at(&mut self, rename_at: Trenameat) -> io::Result<()>940 fn rename_at(&mut self, rename_at: Trenameat) -> io::Result<()> {
941 let olddir = self.fids.get(&rename_at.olddirfid).ok_or_else(ebadf)?;
942 let oldname = string_to_cstring(rename_at.oldname)?;
943
944 let newdir = self.fids.get(&rename_at.newdirfid).ok_or_else(ebadf)?;
945 let newname = string_to_cstring(rename_at.newname)?;
946
947 // Safe because this doesn't modify any memory and we check the return value.
948 syscall!(unsafe {
949 libc::renameat(
950 olddir.path.as_raw_fd(),
951 oldname.as_ptr(),
952 newdir.path.as_raw_fd(),
953 newname.as_ptr(),
954 )
955 })?;
956
957 Ok(())
958 }
959
unlink_at(&mut self, unlink_at: Tunlinkat) -> io::Result<()>960 fn unlink_at(&mut self, unlink_at: Tunlinkat) -> io::Result<()> {
961 let dir = self.fids.get(&unlink_at.dirfd).ok_or_else(ebadf)?;
962 let name = string_to_cstring(unlink_at.name)?;
963
964 syscall!(unsafe {
965 libc::unlinkat(
966 dir.path.as_raw_fd(),
967 name.as_ptr(),
968 unlink_at.flags as libc::c_int,
969 )
970 })?;
971
972 Ok(())
973 }
974 }
975
976 #[cfg(test)]
977 mod tests;
978