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