Mark ab/7061308 as merged in stage.

Bug: 180401296
Merged-In: I2a9b064c190526b7b714f26d1ec34c15c8af589b
Change-Id: I370bdca8c53f22fe1209d37baf07ebf3d304732b
diff --git a/.gitignore b/.gitignore
index 0c60586..de0d2e3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -47,7 +47,6 @@
 
 # Patch files.
 *.diff
-*.patch
 *.orig
 *.rej
 
diff --git a/Android.bp b/Android.bp
index 1db119e..fe386a1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,25 +1,39 @@
-// This file is generated by cargo2android.py --run --device --tests --dependencies.
+// This file is generated by cargo2android.py --run --device --tests --dependencies --global_defaults=crosvm_defaults.
+
+package {
+    default_applicable_licenses: ["external_vm_tools_p9_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+    name: "external_vm_tools_p9_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-BSD",
+    ],
+    license_text: [
+        "LICENSE",
+    ],
+}
 
 rust_library {
     name: "libp9",
+    defaults: ["crosvm_defaults"],
     host_supported: true,
     crate_name: "p9",
     srcs: ["src/lib.rs"],
     edition: "2018",
     rustlibs: [
         "liblibc",
+        "liblibchromeos",
     ],
-    proc_macros: [
-        "libwire_format_derive",
-    ],
-    apex_available: [
-        "//apex_available:platform",
-        "com.android.virt",
-    ],
+    proc_macros: ["libwire_format_derive"],
 }
 
 rust_defaults {
     name: "p9_defaults",
+    defaults: ["crosvm_defaults"],
     crate_name: "p9",
     srcs: ["src/lib.rs"],
     test_suites: ["general-tests"],
@@ -27,10 +41,9 @@
     edition: "2018",
     rustlibs: [
         "liblibc",
+        "liblibchromeos",
     ],
-    proc_macros: [
-        "libwire_format_derive",
-    ],
+    proc_macros: ["libwire_format_derive"],
 }
 
 rust_test_host {
@@ -44,8 +57,26 @@
 }
 
 // dependent_library ["feature_list"]
-//   libc-0.2.79 "default,std"
+//   ../../crosvm/assertions/src/lib.rs
+//   ../../crosvm/data_model/src/lib.rs
+//   ../../libchromeos-rs/src/lib.rs
+//   autocfg-1.0.1
+//   cfg-if-0.1.10
+//   futures-0.3.9 "alloc"
+//   futures-channel-0.3.9 "alloc,futures-sink,sink"
+//   futures-core-0.3.9 "alloc"
+//   futures-io-0.3.9
+//   futures-sink-0.3.9 "alloc"
+//   futures-task-0.3.9 "alloc"
+//   futures-util-0.3.9 "alloc,futures-sink,sink"
+//   intrusive-collections-0.9.0 "alloc,default"
+//   libc-0.2.82 "default,std"
+//   log-0.4.11
+//   memoffset-0.5.6 "default"
+//   pin-project-lite-0.2.1
+//   pin-utils-0.1.0
 //   proc-macro2-1.0.24 "default,proc-macro"
-//   quote-1.0.7 "default,proc-macro"
-//   syn-1.0.45 "clone-impls,default,derive,parsing,printing,proc-macro,quote"
+//   protobuf-2.20.0
+//   quote-1.0.8 "default,proc-macro"
+//   syn-1.0.58 "clone-impls,default,derive,parsing,printing,proc-macro,quote"
 //   unicode-xid-0.2.1 "default"
diff --git a/Cargo.toml b/Cargo.toml
index abb7a45..11ea913 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,6 +6,7 @@
 
 [dependencies]
 libc = "*"
+libchromeos = { path = "../../libchromeos-rs" } # provided by ebuild
 wire_format_derive = { path = "wire_format_derive", version = "*" }
 
 [features]
diff --git a/METADATA b/METADATA
index 0feb0d2..4626851 100644
--- a/METADATA
+++ b/METADATA
@@ -8,7 +8,7 @@
     type: GIT
     value: "https://chromium.googlesource.com/chromiumos/platform2"
   }
-  version: "8739fdb7d25a53496536f328d2675824c7199e17"
-  last_upgrade_date { year: 2020 month: 10 day: 6 }
+  version: "2c3e8252c684673e83278a0124a998e997dbbcc2"
+  last_upgrade_date { year: 2020 month: 12 day: 3 }
   license_type: NOTICE
 }
diff --git a/TEST_MAPPING b/TEST_MAPPING
deleted file mode 100644
index 656256d..0000000
--- a/TEST_MAPPING
+++ /dev/null
@@ -1,13 +0,0 @@
-// Generated by cargo2android.py for tests in Android.bp
-{
-  "presubmit": [
-    {
-      "name": "p9_host_test_src_lib",
-      "host": true
-// Presubmit tries to run x86, but we only support 64-bit builds.
-//    },
-//    {
-//      "name": "p9_device_test_src_lib"
-    }
-  ]
-}
diff --git a/fuzz/OWNERS b/fuzz/OWNERS
new file mode 100644
index 0000000..cef66ae
--- /dev/null
+++ b/fuzz/OWNERS
@@ -0,0 +1,3 @@
+# ANDROID: Remove because Gerrit does not recognize these emails and reject the upload.
+#chirantan@chromium.org
+#dgreid@chromium.org
\ No newline at end of file
diff --git a/src/protocol/wire_format.rs b/src/protocol/wire_format.rs
index b787990..7120937 100644
--- a/src/protocol/wire_format.rs
+++ b/src/protocol/wire_format.rs
@@ -223,17 +223,14 @@
             0xef as u8,
             WireFormat::decode(&mut Cursor::new(&buf)).unwrap()
         );
-        assert_eq!(
-            0xbeef as u16,
-            WireFormat::decode(&mut Cursor::new(&buf)).unwrap()
-        );
+        assert_eq!(0xbeef as u16, u16::decode(&mut Cursor::new(&buf)).unwrap());
         assert_eq!(
             0xdeadbeef as u32,
-            WireFormat::decode(&mut Cursor::new(&buf)).unwrap()
+            u32::decode(&mut Cursor::new(&buf)).unwrap()
         );
         assert_eq!(
             0x8badf00d_deadbeef as u64,
-            WireFormat::decode(&mut Cursor::new(&buf)).unwrap()
+            u64::decode(&mut Cursor::new(&buf)).unwrap()
         );
     }
 
diff --git a/src/server/mod.rs b/src/server/mod.rs
index 6a4a5c1..880a998 100644
--- a/src/server/mod.rs
+++ b/src/server/mod.rs
@@ -4,19 +4,22 @@
 
 use std::cmp::min;
 use std::collections::{btree_map, BTreeMap};
-use std::ffi::CString;
-use std::fs;
+use std::ffi::{CStr, CString};
+use std::fs::File;
 use std::io::{self, Cursor, Read, Write};
-use std::mem;
-use std::os::unix::fs::MetadataExt;
-use std::os::unix::fs::{DirBuilderExt, FileExt, OpenOptionsExt};
-use std::os::unix::io::AsRawFd;
-use std::path::{Component, Path, PathBuf};
+use std::mem::{self, MaybeUninit};
+use std::ops::Deref;
+use std::os::unix::ffi::OsStrExt;
+use std::os::unix::fs::FileExt;
+use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
+use std::path::Path;
+
+use libchromeos::{read_dir, syscall};
 
 use crate::protocol::*;
 
 // Tlopen and Tlcreate flags.  Taken from "include/net/9p/9p.h" in the linux tree.
-const _P9_RDONLY: u32 = 0o00000000;
+const P9_RDONLY: u32 = 0o00000000;
 const P9_WRONLY: u32 = 0o00000001;
 const P9_RDWR: u32 = 0o00000002;
 const P9_NOACCESS: u32 = 0o00000003;
@@ -37,7 +40,9 @@
 const P9_SYNC: u32 = 0o04000000;
 
 // Mapping from 9P flags to libc flags.
-const MAPPED_FLAGS: [(u32, i32); 14] = [
+const MAPPED_FLAGS: [(u32, i32); 16] = [
+    (P9_WRONLY, libc::O_WRONLY),
+    (P9_RDWR, libc::O_RDWR),
     (P9_CREATE, libc::O_CREAT),
     (P9_EXCL, libc::O_EXCL),
     (P9_NOCTTY, libc::O_NOCTTY),
@@ -61,7 +66,7 @@
 const _P9_QTMOUNT: u8 = 0x10;
 const _P9_QTAUTH: u8 = 0x08;
 const _P9_QTTMP: u8 = 0x04;
-const _P9_QTSYMLINK: u8 = 0x02;
+const P9_QTSYMLINK: u8 = 0x02;
 const _P9_QTLINK: u8 = 0x01;
 const P9_QTFILE: u8 = 0x00;
 
@@ -100,37 +105,84 @@
 const MIN_MESSAGE_SIZE: u32 = 256;
 const MAX_MESSAGE_SIZE: u32 = ::std::u16::MAX as u32;
 
+#[derive(PartialEq, Eq)]
+enum FileType {
+    Regular,
+    Directory,
+    Other,
+}
+
+impl From<libc::mode_t> for FileType {
+    fn from(mode: libc::mode_t) -> Self {
+        match mode & libc::S_IFMT {
+            libc::S_IFREG => FileType::Regular,
+            libc::S_IFDIR => FileType::Directory,
+            _ => FileType::Other,
+        }
+    }
+}
+
 // Represents state that the server is holding on behalf of a client. Fids are somewhat like file
 // descriptors but are not restricted to open files and directories. Fids are identified by a unique
 // 32-bit number chosen by the client. Most messages sent by clients include a fid on which to
 // operate. The fid in a Tattach message represents the root of the file system tree that the client
 // is allowed to access. A client can create more fids by walking the directory tree from that fid.
-#[derive(Debug)]
 struct Fid {
-    path: Box<Path>,
-    metadata: fs::Metadata,
-    file: Option<fs::File>,
-    dirents: Option<Vec<Dirent>>,
+    path: File,
+    file: Option<File>,
+    filetype: FileType,
 }
 
-fn metadata_to_qid(metadata: &fs::Metadata) -> Qid {
-    let ty = if metadata.is_dir() {
-        P9_QTDIR
-    } else if metadata.is_file() {
-        P9_QTFILE
-    } else {
-        // Unknown file type...
-        0
-    };
+impl From<libc::stat64> for Qid {
+    fn from(st: libc::stat64) -> Qid {
+        let ty = match st.st_mode & libc::S_IFMT {
+            libc::S_IFDIR => P9_QTDIR,
+            libc::S_IFREG => P9_QTFILE,
+            libc::S_IFLNK => P9_QTSYMLINK,
+            _ => 0,
+        };
 
-    Qid {
-        ty,
-        // TODO: deal with the 2038 problem before 2038
-        version: metadata.mtime() as u32,
-        path: metadata.ino(),
+        Qid {
+            ty,
+            // TODO: deal with the 2038 problem before 2038
+            version: st.st_mtime as u32,
+            path: st.st_ino,
+        }
     }
 }
 
+fn statat(d: &File, name: &CStr, flags: libc::c_int) -> io::Result<libc::stat64> {
+    let mut st = MaybeUninit::<libc::stat64>::zeroed();
+
+    // Safe because the kernel will only write data in `st` and we check the return
+    // value.
+    let res = unsafe {
+        libc::fstatat64(
+            d.as_raw_fd(),
+            name.as_ptr(),
+            st.as_mut_ptr(),
+            flags | libc::AT_SYMLINK_NOFOLLOW,
+        )
+    };
+    if res >= 0 {
+        // Safe because the kernel guarantees that the struct is now fully initialized.
+        Ok(unsafe { st.assume_init() })
+    } else {
+        Err(io::Error::last_os_error())
+    }
+}
+
+fn stat(f: &File) -> io::Result<libc::stat64> {
+    // Safe because this is a constant value and a valid C string.
+    let pathname = unsafe { CStr::from_bytes_with_nul_unchecked(b"\0") };
+
+    statat(f, pathname, libc::AT_EMPTY_PATH)
+}
+
+fn string_to_cstring(s: String) -> io::Result<CString> {
+    CString::new(s).map_err(|_| io::Error::from_raw_os_error(libc::EINVAL))
+}
+
 fn error_to_rmessage(err: io::Error) -> Rmessage {
     let errno = if let Some(errno) = err.raw_os_error() {
         errno
@@ -164,45 +216,25 @@
     })
 }
 
-// Joins `path` to `buf`.  If `path` is '..', removes the last component from `buf`
-// only if `buf` != `root` but does nothing if `buf` == `root`.  Pushes `path` onto
-// `buf` if it is a normal path component.
-//
-// Returns an error if `path` is absolute, has more than one component, or contains
-// a '.' component.
-fn join_path<P: AsRef<Path>, R: AsRef<Path>>(
-    mut buf: PathBuf,
-    path: P,
-    root: R,
-) -> io::Result<PathBuf> {
-    let path = path.as_ref();
-    let root = root.as_ref();
-    debug_assert!(buf.starts_with(root));
+// Sigh.. Cow requires the underlying type to implement Clone.
+enum MaybeOwned<'b, T> {
+    Borrowed(&'b T),
+    Owned(T),
+}
 
-    if path.components().count() > 1 {
-        return Err(io::Error::from_raw_os_error(libc::EINVAL));
-    }
-
-    for component in path.components() {
-        match component {
-            // Prefix should only appear on windows systems.
-            Component::Prefix(_) => return Err(io::Error::from_raw_os_error(libc::EINVAL)),
-            // Absolute paths are not allowed.
-            Component::RootDir => return Err(io::Error::from_raw_os_error(libc::EINVAL)),
-            // '.' elements are not allowed.
-            Component::CurDir => return Err(io::Error::from_raw_os_error(libc::EINVAL)),
-            Component::ParentDir => {
-                // We only remove the parent path if we are not already at the root of the
-                // file system.
-                if buf != root {
-                    buf.pop();
-                }
-            }
-            Component::Normal(element) => buf.push(element),
+impl<'a, T> Deref for MaybeOwned<'a, T> {
+    type Target = T;
+    fn deref(&self) -> &Self::Target {
+        use MaybeOwned::*;
+        match *self {
+            Borrowed(borrowed) => borrowed,
+            Owned(ref owned) => owned,
         }
     }
+}
 
-    Ok(buf)
+fn ebadf() -> io::Error {
+    io::Error::from_raw_os_error(libc::EBADF)
 }
 
 pub type ServerIdMap<T> = BTreeMap<T, T>;
@@ -213,12 +245,115 @@
     map.get(&id).map_or(id.clone(), |v| v.clone())
 }
 
+// Performs an ascii case insensitive lookup and returns an O_PATH fd for the entry, if found.
+fn ascii_casefold_lookup(proc: &File, parent: &File, name: &[u8]) -> io::Result<File> {
+    let mut dir = open_fid(proc, &parent, P9_DIRECTORY)?;
+    let mut dirents = read_dir(&mut dir, 0)?;
+
+    while let Some(entry) = dirents.next().transpose()? {
+        if name.eq_ignore_ascii_case(entry.name.to_bytes()) {
+            return lookup(parent, entry.name);
+        }
+    }
+
+    Err(io::Error::from_raw_os_error(libc::ENOENT))
+}
+
+fn lookup(parent: &File, name: &CStr) -> io::Result<File> {
+    // Safe because this doesn't modify any memory and we check the return value.
+    let fd = syscall!(unsafe {
+        libc::openat(
+            parent.as_raw_fd(),
+            name.as_ptr(),
+            libc::O_PATH | libc::O_NOFOLLOW | libc::O_CLOEXEC,
+        )
+    })?;
+
+    // Safe because we just opened this fd.
+    Ok(unsafe { File::from_raw_fd(fd) })
+}
+
+fn do_walk(
+    proc: &File,
+    wnames: Vec<String>,
+    start: File,
+    ascii_casefold: bool,
+    mds: &mut Vec<libc::stat64>,
+) -> io::Result<File> {
+    let mut current = start;
+
+    for wname in wnames {
+        let name = string_to_cstring(wname)?;
+        current = lookup(&current, &name).or_else(|e| {
+            if ascii_casefold {
+                if let Some(libc::ENOENT) = e.raw_os_error() {
+                    return ascii_casefold_lookup(proc, &current, name.to_bytes());
+                }
+            }
+
+            Err(e)
+        })?;
+        mds.push(stat(&current)?);
+    }
+
+    Ok(current)
+}
+
+fn open_fid(proc: &File, path: &File, p9_flags: u32) -> io::Result<File> {
+    let pathname = string_to_cstring(format!("self/fd/{}", path.as_raw_fd()))?;
+
+    // We always open files with O_CLOEXEC.
+    let mut flags: i32 = libc::O_CLOEXEC;
+    for &(p9f, of) in &MAPPED_FLAGS {
+        if (p9_flags & p9f) != 0 {
+            flags |= of;
+        }
+    }
+
+    if p9_flags & P9_NOACCESS == P9_RDONLY {
+        flags |= libc::O_RDONLY;
+    }
+
+    // Safe because this doesn't modify any memory and we check the return value. We need to
+    // clear the O_NOFOLLOW flag because we want to follow the proc symlink.
+    let fd = syscall!(unsafe {
+        libc::openat(
+            proc.as_raw_fd(),
+            pathname.as_ptr(),
+            flags & !libc::O_NOFOLLOW,
+        )
+    })?;
+
+    // Safe because we just opened this fd and we know it is valid.
+    Ok(unsafe { File::from_raw_fd(fd) })
+}
+
+#[derive(Clone)]
+pub struct Config {
+    pub root: Box<Path>,
+    pub msize: u32,
+
+    pub uid_map: ServerUidMap,
+    pub gid_map: ServerGidMap,
+
+    pub ascii_casefold: bool,
+}
+
+impl Default for Config {
+    fn default() -> Config {
+        Config {
+            root: Path::new("/").into(),
+            msize: MAX_MESSAGE_SIZE,
+            uid_map: Default::default(),
+            gid_map: Default::default(),
+            ascii_casefold: false,
+        }
+    }
+}
 pub struct Server {
-    root: Box<Path>,
-    msize: u32,
     fids: BTreeMap<u32, Fid>,
-    uid_map: ServerUidMap,
-    gid_map: ServerGidMap,
+    proc: File,
+    cfg: Config,
 }
 
 impl Server {
@@ -226,14 +361,40 @@
         root: P,
         uid_map: ServerUidMap,
         gid_map: ServerGidMap,
-    ) -> Server {
-        Server {
+    ) -> io::Result<Server> {
+        Server::with_config(Config {
             root: root.into(),
             msize: MAX_MESSAGE_SIZE,
-            fids: BTreeMap::new(),
             uid_map,
             gid_map,
-        }
+            ascii_casefold: false,
+        })
+    }
+
+    pub fn with_config(cfg: Config) -> io::Result<Server> {
+        // Safe because this is a valid c-string.
+        let proc_cstr = unsafe { CStr::from_bytes_with_nul_unchecked(b"/proc\0") };
+
+        // Safe because this doesn't modify any memory and we check the return value.
+        let fd = syscall!(unsafe {
+            libc::openat(
+                libc::AT_FDCWD,
+                proc_cstr.as_ptr(),
+                libc::O_PATH | libc::O_NOFOLLOW | libc::O_CLOEXEC,
+            )
+        })?;
+
+        // Safe because we just opened this fd and we know it is valid.
+        let proc = unsafe { File::from_raw_fd(fd) };
+        Ok(Server {
+            fids: BTreeMap::new(),
+            proc,
+            cfg,
+        })
+    }
+
+    pub fn keep_fds(&self) -> Vec<RawFd> {
+        vec![self.proc.as_raw_fd()]
     }
 
     pub fn handle_message<R: Read, W: Write>(
@@ -241,16 +402,12 @@
         reader: &mut R,
         writer: &mut W,
     ) -> io::Result<()> {
-        let request: Tframe = WireFormat::decode(&mut reader.take(self.msize as u64))?;
+        let Tframe { tag, msg } = WireFormat::decode(&mut reader.take(self.cfg.msize as u64))?;
 
-        if cfg!(feature = "trace") {
-            println!("{:?}", &request);
-        }
-
-        let rmsg = match request.msg {
+        let rmsg = match msg {
             Tmessage::Version(ref version) => self.version(version).map(Rmessage::Version),
             Tmessage::Flush(ref flush) => self.flush(flush).and(Ok(Rmessage::Flush)),
-            Tmessage::Walk(ref walk) => self.walk(walk).map(Rmessage::Walk),
+            Tmessage::Walk(walk) => self.walk(walk).map(Rmessage::Walk),
             Tmessage::Read(ref read) => self.read(read).map(Rmessage::Read),
             Tmessage::Write(ref write) => self.write(write).map(Rmessage::Write),
             Tmessage::Clunk(ref clunk) => self.clunk(clunk).and(Ok(Rmessage::Clunk)),
@@ -259,7 +416,7 @@
             Tmessage::Auth(ref auth) => self.auth(auth).map(Rmessage::Auth),
             Tmessage::Statfs(ref statfs) => self.statfs(statfs).map(Rmessage::Statfs),
             Tmessage::Lopen(ref lopen) => self.lopen(lopen).map(Rmessage::Lopen),
-            Tmessage::Lcreate(ref lcreate) => self.lcreate(lcreate).map(Rmessage::Lcreate),
+            Tmessage::Lcreate(lcreate) => self.lcreate(lcreate).map(Rmessage::Lcreate),
             Tmessage::Symlink(ref symlink) => self.symlink(symlink).map(Rmessage::Symlink),
             Tmessage::Mknod(ref mknod) => self.mknod(mknod).map(Rmessage::Mknod),
             Tmessage::Rename(ref rename) => self.rename(rename).and(Ok(Rmessage::Rename)),
@@ -276,26 +433,18 @@
             Tmessage::Fsync(ref fsync) => self.fsync(fsync).and(Ok(Rmessage::Fsync)),
             Tmessage::Lock(ref lock) => self.lock(lock).map(Rmessage::Lock),
             Tmessage::GetLock(ref get_lock) => self.get_lock(get_lock).map(Rmessage::GetLock),
-            Tmessage::Link(ref link) => self.link(link).and(Ok(Rmessage::Link)),
-            Tmessage::Mkdir(ref mkdir) => self.mkdir(mkdir).map(Rmessage::Mkdir),
-            Tmessage::RenameAt(ref rename_at) => {
-                self.rename_at(rename_at).and(Ok(Rmessage::RenameAt))
-            }
-            Tmessage::UnlinkAt(ref unlink_at) => {
-                self.unlink_at(unlink_at).and(Ok(Rmessage::UnlinkAt))
-            }
+            Tmessage::Link(link) => self.link(link).and(Ok(Rmessage::Link)),
+            Tmessage::Mkdir(mkdir) => self.mkdir(mkdir).map(Rmessage::Mkdir),
+            Tmessage::RenameAt(rename_at) => self.rename_at(rename_at).and(Ok(Rmessage::RenameAt)),
+            Tmessage::UnlinkAt(unlink_at) => self.unlink_at(unlink_at).and(Ok(Rmessage::UnlinkAt)),
         };
 
         // Errors while handling requests are never fatal.
         let response = Rframe {
-            tag: request.tag,
+            tag,
             msg: rmsg.unwrap_or_else(error_to_rmessage),
         };
 
-        if cfg!(feature = "trace") {
-            println!("{:?}", &response);
-        }
-
         response.encode(writer)?;
         writer.flush()
     }
@@ -310,15 +459,28 @@
         // TODO: Check attach parameters
         match self.fids.entry(attach.fid) {
             btree_map::Entry::Vacant(entry) => {
+                let root = CString::new(self.cfg.root.as_os_str().as_bytes())
+                    .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
+
+                // Safe because this doesn't modify any memory and we check the return value.
+                let fd = syscall!(unsafe {
+                    libc::openat(
+                        libc::AT_FDCWD,
+                        root.as_ptr(),
+                        libc::O_PATH | libc::O_NOFOLLOW | libc::O_CLOEXEC,
+                    )
+                })?;
+
+                let root_path = unsafe { File::from_raw_fd(fd) };
+                let st = stat(&root_path)?;
+
                 let fid = Fid {
-                    path: self.root.to_path_buf().into_boxed_path(),
-                    metadata: fs::metadata(&self.root)?,
+                    // Safe because we just opened this fd.
+                    path: root_path,
                     file: None,
-                    dirents: None,
+                    filetype: st.st_mode.into(),
                 };
-                let response = Rattach {
-                    qid: metadata_to_qid(&fid.metadata),
-                };
+                let response = Rattach { qid: st.into() };
                 entry.insert(fid);
                 Ok(response)
             }
@@ -333,10 +495,10 @@
 
         // A Tversion request clunks all open fids and terminates any pending I/O.
         self.fids.clear();
-        self.msize = min(MAX_MESSAGE_SIZE, version.msize);
+        self.cfg.msize = min(self.cfg.msize, version.msize);
 
         Ok(Rversion {
-            msize: self.msize,
+            msize: self.cfg.msize,
             version: if version.version == "9P2000.L" {
                 String::from("9P2000.L")
             } else {
@@ -350,54 +512,39 @@
         Ok(())
     }
 
-    fn do_walk(
-        &self,
-        wnames: &[String],
-        mut buf: PathBuf,
-        mds: &mut Vec<fs::Metadata>,
-    ) -> io::Result<PathBuf> {
-        for wname in wnames {
-            let name = Path::new(wname);
-            buf = join_path(buf, name, &*self.root)?;
-            mds.push(fs::metadata(&buf)?);
-        }
-
-        Ok(buf)
-    }
-
-    fn walk(&mut self, walk: &Twalk) -> io::Result<Rwalk> {
+    fn walk(&mut self, walk: Twalk) -> io::Result<Rwalk> {
         // `newfid` must not currently be in use unless it is the same as `fid`.
         if walk.fid != walk.newfid && self.fids.contains_key(&walk.newfid) {
             return Err(io::Error::from_raw_os_error(libc::EBADF));
         }
 
         // We need to walk the tree.  First get the starting path.
-        let (buf, oldmd) = self
+        let start = self
             .fids
             .get(&walk.fid)
-            .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))
-            .map(|fid| (fid.path.to_path_buf(), fid.metadata.clone()))?;
+            .ok_or_else(ebadf)
+            .and_then(|fid| fid.path.try_clone())?;
 
         // Now walk the tree and break on the first error, if any.
-        let mut mds = Vec::with_capacity(walk.wnames.len());
-        match self.do_walk(&walk.wnames, buf, &mut mds) {
-            Ok(buf) => {
+        let expected_len = walk.wnames.len();
+        let mut mds = Vec::with_capacity(expected_len);
+        match do_walk(
+            &self.proc,
+            walk.wnames,
+            start,
+            self.cfg.ascii_casefold,
+            &mut mds,
+        ) {
+            Ok(end) => {
                 // Store the new fid if the full walk succeeded.
-                if mds.len() == walk.wnames.len() {
-                    // This could just be a duplication operation.
-                    let md = if let Some(md) = mds.last() {
-                        md.clone()
-                    } else {
-                        oldmd
-                    };
-
+                if mds.len() == expected_len {
+                    let st = mds.last().copied().map(Ok).unwrap_or_else(|| stat(&end))?;
                     self.fids.insert(
                         walk.newfid,
                         Fid {
-                            path: buf.into_boxed_path(),
-                            metadata: md,
+                            path: end,
                             file: None,
-                            dirents: None,
+                            filetype: st.st_mode.into(),
                         },
                     );
                 }
@@ -411,7 +558,7 @@
         }
 
         Ok(Rwalk {
-            wqids: mds.iter().map(metadata_to_qid).collect(),
+            wqids: mds.into_iter().map(Qid::from).collect(),
         })
     }
 
@@ -421,7 +568,7 @@
             .fids
             .get_mut(&read.fid)
             .and_then(|fid| fid.file.as_mut())
-            .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
+            .ok_or_else(ebadf)?;
 
         // Use an empty Rread struct to figure out the overhead of the header.
         let header_size = Rframe {
@@ -432,12 +579,11 @@
         }
         .byte_size();
 
-        let capacity = min(self.msize - header_size, read.count);
-        let mut buf = Data(Vec::with_capacity(capacity as usize));
-        buf.resize(capacity as usize, 0);
+        let capacity = min(self.cfg.msize - header_size, read.count);
+        let mut buf = Data(vec![0u8; capacity as usize]);
 
         let count = file.read_at(&mut buf, read.offset)?;
-        buf.resize(count, 0);
+        buf.truncate(count);
 
         Ok(Rread { data: buf })
     }
@@ -447,7 +593,7 @@
             .fids
             .get_mut(&write.fid)
             .and_then(|fid| fid.file.as_mut())
-            .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
+            .ok_or_else(ebadf)?;
 
         let count = file.write_at(&write.data, write.offset)?;
         Ok(Rwrite {
@@ -465,44 +611,22 @@
         }
     }
 
-    fn remove(&mut self, remove: &Tremove) -> io::Result<()> {
-        match self.fids.entry(remove.fid) {
-            btree_map::Entry::Vacant(_) => Err(io::Error::from_raw_os_error(libc::EBADF)),
-            btree_map::Entry::Occupied(o) => {
-                let (_, fid) = o.remove_entry();
-
-                if fid.metadata.is_dir() {
-                    fs::remove_dir(&fid.path)?;
-                } else {
-                    fs::remove_file(&fid.path)?;
-                }
-
-                Ok(())
-            }
-        }
+    fn remove(&mut self, _remove: &Tremove) -> io::Result<()> {
+        // Since a file could be linked into multiple locations, there is no way to know exactly
+        // which path we are supposed to unlink. Linux uses unlink_at anyway, so we can just return
+        // an error here.
+        Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP))
     }
 
     fn statfs(&mut self, statfs: &Tstatfs) -> io::Result<Rstatfs> {
-        let fid = self
-            .fids
-            .get(&statfs.fid)
-            .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
-        let path = fid
-            .path
-            .to_str()
-            .and_then(|path| CString::new(path).ok())
-            .ok_or_else(|| io::Error::from_raw_os_error(libc::EINVAL))?;
+        let fid = self.fids.get(&statfs.fid).ok_or_else(ebadf)?;
+        let mut buf = MaybeUninit::zeroed();
 
-        // Safe because we are zero-initializing a C struct with only primitive
-        // data members.
-        let mut out: libc::statfs64 = unsafe { mem::zeroed() };
+        // Safe because this will only modify `out` and we check the return value.
+        syscall!(unsafe { libc::fstatfs64(fid.path.as_raw_fd(), buf.as_mut_ptr()) })?;
 
-        // Safe because we know that `path` is valid and we have already initialized `out`.
-        let ret = unsafe { libc::statfs64(path.as_ptr(), &mut out) };
-        if ret != 0 {
-            return Err(io::Error::last_os_error());
-        }
-
+        // Safe because this only has integer types and any value is valid.
+        let out = unsafe { buf.assume_init() };
         Ok(Rstatfs {
             ty: out.f_type as u32,
             bsize: out.f_bsize as u32,
@@ -511,77 +635,65 @@
             bavail: out.f_bavail,
             files: out.f_files,
             ffree: out.f_ffree,
-            fsid: 0, // No way to get the fields of a libc::fsid_t
+            // Safe because the fsid has only integer fields and the compiler will verify that is
+            // the same width as the `fsid` field in Rstatfs.
+            fsid: unsafe { mem::transmute(out.f_fsid) },
             namelen: out.f_namelen as u32,
         })
     }
 
     fn lopen(&mut self, lopen: &Tlopen) -> io::Result<Rlopen> {
-        let fid = self
-            .fids
-            .get_mut(&lopen.fid)
-            .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
+        let fid = self.fids.get_mut(&lopen.fid).ok_or_else(ebadf)?;
 
-        // We always open files with O_CLOEXEC.
-        let mut custom_flags: i32 = libc::O_CLOEXEC;
-        for &(p9f, of) in &MAPPED_FLAGS {
-            if (lopen.flags & p9f) != 0 {
-                custom_flags |= of;
-            }
-        }
+        let file = open_fid(&self.proc, &fid.path, lopen.flags)?;
+        let st = stat(&file)?;
 
-        // MAPPED_FLAGS will handle append, create[_new], and truncate.
-        let file = fs::OpenOptions::new()
-            .read((lopen.flags & P9_NOACCESS) == 0 || (lopen.flags & P9_RDWR) != 0)
-            .write((lopen.flags & P9_WRONLY) != 0 || (lopen.flags & P9_RDWR) != 0)
-            .custom_flags(custom_flags)
-            .open(&fid.path)?;
-
-        fid.metadata = file.metadata()?;
         fid.file = Some(file);
-
+        let iounit = st.st_blksize as u32;
         Ok(Rlopen {
-            qid: metadata_to_qid(&fid.metadata),
-            iounit: 0,
+            qid: st.into(),
+            iounit,
         })
     }
 
-    fn lcreate(&mut self, lcreate: &Tlcreate) -> io::Result<Rlcreate> {
-        let fid = self
-            .fids
-            .get_mut(&lcreate.fid)
-            .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
+    fn lcreate(&mut self, lcreate: Tlcreate) -> io::Result<Rlcreate> {
+        let fid = self.fids.get_mut(&lcreate.fid).ok_or_else(ebadf)?;
 
-        if !fid.metadata.is_dir() {
+        if fid.filetype != FileType::Directory {
             return Err(io::Error::from_raw_os_error(libc::ENOTDIR));
         }
 
-        let name = Path::new(&lcreate.name);
-        let path = join_path(fid.path.to_path_buf(), name, &*self.root)?;
-
-        let mut custom_flags: i32 = libc::O_CLOEXEC;
+        let mut flags: i32 = libc::O_CLOEXEC | libc::O_CREAT | libc::O_EXCL;
         for &(p9f, of) in &MAPPED_FLAGS {
             if (lcreate.flags & p9f) != 0 {
-                custom_flags |= of;
+                flags |= of;
             }
         }
+        if lcreate.flags & P9_NOACCESS == P9_RDONLY {
+            flags |= libc::O_RDONLY;
+        }
 
-        // Set O_CREAT|O_EXCL, MAPPED_FLAGS will handle append and truncate.
-        custom_flags |= libc::O_CREAT | libc::O_EXCL;
-        let file = fs::OpenOptions::new()
-            .read((lcreate.flags & P9_NOACCESS) == 0 || (lcreate.flags & P9_RDWR) != 0)
-            .write((lcreate.flags & P9_WRONLY) != 0 || (lcreate.flags & P9_RDWR) != 0)
-            .custom_flags(custom_flags)
-            .mode(lcreate.mode & 0o755)
-            .open(&path)?;
+        let name = string_to_cstring(lcreate.name)?;
 
-        fid.metadata = file.metadata()?;
+        // Safe because this doesn't modify any memory and we check the return value.
+        let fd = syscall!(unsafe {
+            libc::openat(fid.path.as_raw_fd(), name.as_ptr(), flags, lcreate.mode)
+        })?;
+
+        // Safe because we just opened this fd and we know it is valid.
+        let file = unsafe { File::from_raw_fd(fd) };
+        let st = stat(&file)?;
+        let iounit = st.st_blksize as u32;
+
         fid.file = Some(file);
-        fid.path = path.into_boxed_path();
+
+        // This fid now refers to the newly created file so we need to update the O_PATH fd for it
+        // as well.
+        fid.path = lookup(&fid.path, &name)?;
 
         Ok(Rlcreate {
-            qid: metadata_to_qid(&fid.metadata),
-            iounit: 0,
+            qid: st.into(),
+            iounit,
         })
     }
 
@@ -595,26 +707,11 @@
         Err(io::Error::from_raw_os_error(libc::EACCES))
     }
 
-    fn rename(&mut self, rename: &Trename) -> io::Result<()> {
-        let newname = Path::new(&rename.name);
-        let buf = self
-            .fids
-            .get(&rename.dfid)
-            .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))
-            .map(|dfid| dfid.path.to_path_buf())?;
-        let newpath = join_path(buf, newname, &*self.root)?;
-
-        let fid = self
-            .fids
-            .get_mut(&rename.fid)
-            .ok_or_else(|| io::Error::from_raw_os_error(libc::EINVAL))?;
-
-        fs::rename(&fid.path, &newpath)?;
-
-        // TODO: figure out if the client expects |fid.path| to point to
-        // the renamed path.
-        fid.path = newpath.into_boxed_path();
-        Ok(())
+    fn rename(&mut self, _rename: &Trename) -> io::Result<()> {
+        // We cannot support this as an inode may be linked into multiple directories but we don't
+        // know which one the client wants us to rename. Linux uses rename_at anyway, so we don't
+        // need to worry about this.
+        Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP))
     }
 
     fn readlink(&mut self, _readlink: &Treadlink) -> io::Result<Rreadlink> {
@@ -623,31 +720,27 @@
     }
 
     fn get_attr(&mut self, get_attr: &Tgetattr) -> io::Result<Rgetattr> {
-        let fid = self
-            .fids
-            .get_mut(&get_attr.fid)
-            .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
+        let fid = self.fids.get_mut(&get_attr.fid).ok_or_else(ebadf)?;
 
-        // Refresh the metadata since we were explicitly asked for it.
-        fid.metadata = fs::metadata(&fid.path)?;
+        let st = stat(&fid.path)?;
 
         Ok(Rgetattr {
             valid: P9_GETATTR_BASIC,
-            qid: metadata_to_qid(&fid.metadata),
-            mode: fid.metadata.mode(),
-            uid: map_id_from_host(&self.uid_map, fid.metadata.uid()),
-            gid: map_id_from_host(&self.gid_map, fid.metadata.gid()),
-            nlink: fid.metadata.nlink(),
-            rdev: fid.metadata.rdev(),
-            size: fid.metadata.size(),
-            blksize: fid.metadata.blksize(),
-            blocks: fid.metadata.blocks(),
-            atime_sec: fid.metadata.atime() as u64,
-            atime_nsec: fid.metadata.atime_nsec() as u64,
-            mtime_sec: fid.metadata.mtime() as u64,
-            mtime_nsec: fid.metadata.mtime_nsec() as u64,
-            ctime_sec: fid.metadata.ctime() as u64,
-            ctime_nsec: fid.metadata.ctime_nsec() as u64,
+            qid: st.into(),
+            mode: st.st_mode,
+            uid: map_id_from_host(&self.cfg.uid_map, st.st_uid),
+            gid: map_id_from_host(&self.cfg.gid_map, st.st_gid),
+            nlink: st.st_nlink as u64,
+            rdev: st.st_rdev,
+            size: st.st_size as u64,
+            blksize: st.st_blksize as u64,
+            blocks: st.st_blocks as u64,
+            atime_sec: st.st_atime as u64,
+            atime_nsec: st.st_atime_nsec as u64,
+            mtime_sec: st.st_mtime as u64,
+            mtime_nsec: st.st_mtime_nsec as u64,
+            ctime_sec: st.st_ctime as u64,
+            ctime_nsec: st.st_ctime_nsec as u64,
             btime_sec: 0,
             btime_nsec: 0,
             gen: 0,
@@ -661,12 +754,18 @@
             return Err(io::Error::from_raw_os_error(libc::EPERM));
         }
 
-        let fid = self
-            .fids
-            .get_mut(&set_attr.fid)
-            .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
+        let fid = self.fids.get(&set_attr.fid).ok_or_else(ebadf)?;
 
-        let file = fs::OpenOptions::new().write(true).open(&fid.path)?;
+        let file = if let Some(ref file) = fid.file {
+            MaybeOwned::Borrowed(file)
+        } else {
+            let flags = match fid.filetype {
+                FileType::Regular => P9_RDWR,
+                FileType::Directory => P9_RDONLY | P9_DIRECTORY,
+                FileType::Other => P9_RDWR,
+            };
+            MaybeOwned::Owned(open_fid(&self.proc, &fid.path, P9_NONBLOCK | flags)?)
+        };
 
         if set_attr.valid & P9_SETATTR_SIZE != 0 {
             file.set_len(set_attr.size)?;
@@ -732,71 +831,12 @@
     }
 
     fn readdir(&mut self, readdir: &Treaddir) -> io::Result<Rreaddir> {
-        let fid = self
-            .fids
-            .get_mut(&readdir.fid)
-            .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
+        let fid = self.fids.get_mut(&readdir.fid).ok_or_else(ebadf)?;
 
-        if !fid.metadata.is_dir() {
+        if fid.filetype != FileType::Directory {
             return Err(io::Error::from_raw_os_error(libc::ENOTDIR));
         }
 
-        // The p9 client implementation in the kernel doesn't fully read all the contents
-        // of the directory.  This means that if some application performs a getdents()
-        // call, followed by removing some files, followed by another getdents() call,
-        // the offset that we get from the kernel is completely meaningless.  Instead
-        // we fully read the contents of the directory here and only re-read the directory
-        // if the offset we get from the client is 0.  Any other offset is served from the
-        // directory entries in memory.  This ensures consistency even if the directory
-        // changes in between Treaddir messages.
-        if readdir.offset == 0 {
-            let mut offset = 0;
-            let iter = fs::read_dir(&fid.path)?;
-            let dirents = iter.map(|item| -> io::Result<Dirent> {
-                let entry = item?;
-
-                let md = entry.metadata()?;
-                let qid = metadata_to_qid(&md);
-
-                let ty = if md.is_dir() {
-                    libc::DT_DIR
-                } else if md.is_file() {
-                    libc::DT_REG
-                } else {
-                    libc::DT_UNKNOWN
-                };
-
-                let name = entry
-                    .file_name()
-                    .into_string()
-                    .map_err(|_| io::Error::from_raw_os_error(libc::EINVAL))?;
-
-                let mut out = Dirent {
-                    qid,
-                    offset: 0, // set below
-                    ty,
-                    name,
-                };
-
-                offset += out.byte_size() as u64;
-                out.offset = offset;
-
-                Ok(out)
-            });
-
-            // This is taking advantage of the fact that we can turn a Iterator of Result<T, E>
-            // into a Result<FromIterator<T>, E> since Result implements FromIterator<Result<T, E>>.
-            fid.dirents = Some(dirents.collect::<io::Result<Vec<Dirent>>>()?);
-        }
-
-        let mut entries = fid
-            .dirents
-            .as_ref()
-            .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?
-            .iter()
-            .skip_while(|entry| entry.offset <= readdir.offset)
-            .peekable();
-
         // Use an empty Rreaddir struct to figure out the maximum number of bytes that
         // can be returned.
         let header_size = Rframe {
@@ -806,10 +846,27 @@
             }),
         }
         .byte_size();
-        let count = min(self.msize - header_size, readdir.count);
+        let count = min(self.cfg.msize - header_size, readdir.count);
         let mut cursor = Cursor::new(Vec::with_capacity(count as usize));
 
-        while let Some(entry) = entries.peek() {
+        let dir = fid.file.as_mut().ok_or_else(ebadf)?;
+        let mut dirents = read_dir(dir, readdir.offset as libc::off64_t)?;
+        while let Some(dirent) = dirents.next().transpose()? {
+            let st = statat(&fid.path, &dirent.name, 0)?;
+
+            let name = dirent
+                .name
+                .to_str()
+                .map(String::from)
+                .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
+
+            let entry = Dirent {
+                qid: st.into(),
+                offset: dirent.offset,
+                ty: dirent.type_,
+                name,
+            };
+
             let byte_size = entry.byte_size() as usize;
 
             if cursor.get_ref().capacity() - cursor.get_ref().len() < byte_size {
@@ -817,8 +874,7 @@
                 break;
             }
 
-            // Safe because we just checked that the iterator contains at least one more item.
-            entries.next().unwrap().encode(&mut cursor)?;
+            entry.encode(&mut cursor)?;
         }
 
         Ok(Rreaddir {
@@ -831,7 +887,7 @@
             .fids
             .get(&fsync.fid)
             .and_then(|fid| fid.file.as_ref())
-            .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
+            .ok_or_else(ebadf)?;
 
         if fsync.datasync == 0 {
             file.sync_all()?;
@@ -850,84 +906,68 @@
         Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP))
     }
 
-    fn link(&mut self, link: &Tlink) -> io::Result<()> {
-        let newname = Path::new(&link.name);
-        let buf = self
-            .fids
-            .get(&link.dfid)
-            .map(|dfid| dfid.path.to_path_buf())
-            .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
-        let newpath = join_path(buf, newname, &*self.root)?;
+    fn link(&mut self, link: Tlink) -> io::Result<()> {
+        let target = self.fids.get(&link.fid).ok_or_else(ebadf)?;
+        let path = string_to_cstring(format!("self/fd/{}", target.path.as_raw_fd()))?;
 
-        let path = self
-            .fids
-            .get(&link.fid)
-            .map(|fid| &fid.path)
-            .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
+        let dir = self.fids.get(&link.dfid).ok_or_else(ebadf)?;
+        let name = string_to_cstring(link.name)?;
 
-        fs::hard_link(path, &newpath)?;
+        // Safe because this doesn't modify any memory and we check the return value.
+        syscall!(unsafe {
+            libc::linkat(
+                self.proc.as_raw_fd(),
+                path.as_ptr(),
+                dir.path.as_raw_fd(),
+                name.as_ptr(),
+                libc::AT_SYMLINK_FOLLOW,
+            )
+        })?;
         Ok(())
     }
 
-    fn mkdir(&mut self, mkdir: &Tmkdir) -> io::Result<Rmkdir> {
-        let fid = self
-            .fids
-            .get(&mkdir.dfid)
-            .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
+    fn mkdir(&mut self, mkdir: Tmkdir) -> io::Result<Rmkdir> {
+        let fid = self.fids.get(&mkdir.dfid).ok_or_else(ebadf)?;
+        let name = string_to_cstring(mkdir.name)?;
 
-        let name = Path::new(&mkdir.name);
-        let newpath = join_path(fid.path.to_path_buf(), name, &*self.root)?;
-
-        fs::DirBuilder::new()
-            .recursive(false)
-            .mode(mkdir.mode & 0o755)
-            .create(&newpath)?;
-
+        // Safe because this doesn't modify any memory and we check the return value.
+        syscall!(unsafe { libc::mkdirat(fid.path.as_raw_fd(), name.as_ptr(), mkdir.mode) })?;
         Ok(Rmkdir {
-            qid: metadata_to_qid(&fs::metadata(&newpath)?),
+            qid: statat(&fid.path, &name, 0).map(Qid::from)?,
         })
     }
 
-    fn rename_at(&mut self, rename_at: &Trenameat) -> io::Result<()> {
-        let oldname = Path::new(&rename_at.oldname);
-        let oldbuf = self
-            .fids
-            .get(&rename_at.olddirfid)
-            .map(|dfid| dfid.path.to_path_buf())
-            .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
-        let oldpath = join_path(oldbuf, oldname, &*self.root)?;
+    fn rename_at(&mut self, rename_at: Trenameat) -> io::Result<()> {
+        let olddir = self.fids.get(&rename_at.olddirfid).ok_or_else(ebadf)?;
+        let oldname = string_to_cstring(rename_at.oldname)?;
 
-        let newname = Path::new(&rename_at.newname);
-        let newbuf = self
-            .fids
-            .get(&rename_at.newdirfid)
-            .map(|dfid| dfid.path.to_path_buf())
-            .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
-        let newpath = join_path(newbuf, newname, &*self.root)?;
+        let newdir = self.fids.get(&rename_at.newdirfid).ok_or_else(ebadf)?;
+        let newname = string_to_cstring(rename_at.newname)?;
 
-        fs::rename(&oldpath, &newpath)?;
+        // Safe because this doesn't modify any memory and we check the return value.
+        syscall!(unsafe {
+            libc::renameat(
+                olddir.path.as_raw_fd(),
+                oldname.as_ptr(),
+                newdir.path.as_raw_fd(),
+                newname.as_ptr(),
+            )
+        })?;
+
         Ok(())
     }
 
-    fn unlink_at(&mut self, unlink_at: &Tunlinkat) -> io::Result<()> {
-        let name = Path::new(&unlink_at.name);
-        let buf = self
-            .fids
-            .get(&unlink_at.dirfd)
-            .map(|fid| fid.path.to_path_buf())
-            .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
-        let path = join_path(buf, name, &*self.root)?;
+    fn unlink_at(&mut self, unlink_at: Tunlinkat) -> io::Result<()> {
+        let dir = self.fids.get(&unlink_at.dirfd).ok_or_else(ebadf)?;
+        let name = string_to_cstring(unlink_at.name)?;
 
-        let md = fs::metadata(&path)?;
-        if md.is_dir() && (unlink_at.flags & (libc::AT_REMOVEDIR as u32)) == 0 {
-            return Err(io::Error::from_raw_os_error(libc::EISDIR));
-        }
-
-        if md.is_dir() {
-            fs::remove_dir(&path)?;
-        } else {
-            fs::remove_file(&path)?;
-        }
+        syscall!(unsafe {
+            libc::unlinkat(
+                dir.path.as_raw_fd(),
+                name.as_ptr(),
+                unlink_at.flags as libc::c_int,
+            )
+        })?;
 
         Ok(())
     }
diff --git a/src/server/tests.rs b/src/server/tests.rs
index cc000b6..ee122dd 100644
--- a/src/server/tests.rs
+++ b/src/server/tests.rs
@@ -8,13 +8,13 @@
 use std::collections::{HashSet, VecDeque};
 use std::env;
 use std::ffi::{CString, OsString};
-use std::fs::File;
+use std::fs::{self, File};
 use std::io::{self, Cursor};
 use std::mem;
 use std::ops::Deref;
-use std::os::unix::fs::MetadataExt;
 use std::os::unix::ffi::OsStringExt;
-use std::path::{Path, PathBuf};
+use std::os::unix::fs::MetadataExt;
+use std::path::{Component, Path, PathBuf};
 use std::u32;
 
 // Used to indicate that there is no fid associated with this message.
@@ -26,6 +26,47 @@
 // How big we want the default buffer to be when running tests.
 const DEFAULT_BUFFER_SIZE: u32 = 4096;
 
+// Joins `path` to `buf`.  If `path` is '..', removes the last component from `buf`
+// only if `buf` != `root` but does nothing if `buf` == `root`.  Pushes `path` onto
+// `buf` if it is a normal path component.
+//
+// Returns an error if `path` is absolute, has more than one component, or contains
+// a '.' component.
+fn join_path<P: AsRef<Path>, R: AsRef<Path>>(
+    mut buf: PathBuf,
+    path: P,
+    root: R,
+) -> io::Result<PathBuf> {
+    let path = path.as_ref();
+    let root = root.as_ref();
+    debug_assert!(buf.starts_with(root));
+
+    if path.components().count() > 1 {
+        return Err(io::Error::from_raw_os_error(libc::EINVAL));
+    }
+
+    for component in path.components() {
+        match component {
+            // Prefix should only appear on windows systems.
+            Component::Prefix(_) => return Err(io::Error::from_raw_os_error(libc::EINVAL)),
+            // Absolute paths are not allowed.
+            Component::RootDir => return Err(io::Error::from_raw_os_error(libc::EINVAL)),
+            // '.' elements are not allowed.
+            Component::CurDir => return Err(io::Error::from_raw_os_error(libc::EINVAL)),
+            Component::ParentDir => {
+                // We only remove the parent path if we are not already at the root of the
+                // file system.
+                if buf != root {
+                    buf.pop();
+                }
+            }
+            Component::Normal(element) => buf.push(element),
+        }
+    }
+
+    Ok(buf)
+}
+
 // Automatically deletes the path it contains when it goes out of scope.
 struct ScopedPath<P: AsRef<Path>>(P);
 
@@ -107,7 +148,7 @@
     } else if md.is_file() {
         P9_QTFILE
     } else if md.file_type().is_symlink() {
-        _P9_QTSYMLINK
+        P9_QTSYMLINK
     } else {
         panic!("unknown file type: {:?}", md.file_type());
     };
@@ -129,7 +170,7 @@
     } else if md.is_file() {
         P9_QTFILE
     } else if md.file_type().is_symlink() {
-        _P9_QTSYMLINK
+        P9_QTSYMLINK
     } else {
         panic!("unknown file type: {:?}", md.file_type());
     };
@@ -191,7 +232,7 @@
         wnames: names,
     };
 
-    let rwalk = server.walk(&twalk).expect("failed to walk directoy");
+    let rwalk = server.walk(twalk).expect("failed to walk directoy");
     assert_eq!(mds.len(), rwalk.wqids.len());
     for (md, qid) in mds.iter().zip(rwalk.wqids.iter()) {
         check_qid(qid, md);
@@ -206,7 +247,12 @@
     fid: u32,
     flags: u32,
 ) -> io::Result<Rlopen> {
-    walk(server, dir, dir_fid, fid, vec![String::from(name)]);
+    let wnames = if name.is_empty() {
+        vec![]
+    } else {
+        vec![String::from(name)]
+    };
+    walk(server, dir, dir_fid, fid, wnames);
 
     let tlopen = Tlopen { fid, flags };
 
@@ -270,7 +316,7 @@
         gid: 0,
     };
 
-    server.lcreate(&tlcreate)
+    server.lcreate(tlcreate)
 }
 
 struct Readdir<'a> {
@@ -389,7 +435,8 @@
         .symlink_metadata()
         .expect("failed to get metadata for root dir");
 
-    let mut server = Server::new(&*test_dir, Default::default(), Default::default());
+    let mut server = Server::new(&*test_dir, Default::default(), Default::default())
+        .expect("Failed to create server");
 
     let tversion = Tversion {
         msize: DEFAULT_BUFFER_SIZE,
@@ -478,7 +525,7 @@
     dirs.push_back(test_dir.to_path_buf());
 
     while let Some(dir) = dirs.pop_front() {
-        let fid = next_fid;
+        let dfid = next_fid;
         next_fid += 1;
 
         let wnames: Vec<String> = dir
@@ -487,13 +534,20 @@
             .components()
             .map(|c| Path::new(&c).to_string_lossy().to_string())
             .collect();
-        walk(&mut server, &*test_dir, ROOT_FID, fid, wnames);
+        walk(&mut server, &*test_dir, ROOT_FID, dfid, wnames);
 
         let md = dir.symlink_metadata().expect("failed to get metadata");
 
-        check_attr(&mut server, fid, &md);
+        check_attr(&mut server, dfid, &md);
 
+        let fid = next_fid;
+        next_fid += 1;
+        open(&mut server, &dir, dfid, "", fid, P9_DIRECTORY).expect("Failed to open directory");
         for dirent in readdir(&mut server, fid) {
+            if dirent.name == "." || dirent.name == ".." {
+                continue;
+            }
+
             let entry_path = dir.join(&dirent.name);
             assert!(
                 entry_path.exists(),
@@ -542,14 +596,38 @@
     .expect_err("successfully created existing file");
 }
 
-fn set_attr_test<F>(set_fields: F) -> io::Result<fs::Metadata>
+enum SetAttrKind {
+    File,
+    Directory,
+}
+
+fn set_attr_test<F>(kind: SetAttrKind, set_fields: F) -> io::Result<fs::Metadata>
 where
     F: FnOnce(&mut Tsetattr),
 {
     let (test_dir, mut server) = setup("set_attr");
 
     let name = "existing";
-    create_local_file(&test_dir, name);
+    match kind {
+        SetAttrKind::File => {
+            create_local_file(&test_dir, name);
+        }
+        SetAttrKind::Directory => {
+            let tmkdir = Tmkdir {
+                dfid: ROOT_FID,
+                name: String::from(name),
+                mode: 0o755,
+                gid: 0,
+            };
+
+            let rmkdir = server.mkdir(tmkdir).expect("failed to create directory");
+            let md = fs::symlink_metadata(test_dir.join(name))
+                .expect("failed to get metadata for directory");
+
+            assert!(md.is_dir());
+            check_qid(&rmkdir.qid, &md);
+        }
+    };
 
     let fid = ROOT_FID + 1;
     walk(
@@ -582,7 +660,7 @@
 #[test]
 fn set_len() {
     let len = 661;
-    let md = set_attr_test(|tsetattr| {
+    let md = set_attr_test(SetAttrKind::File, |tsetattr| {
         tsetattr.valid = P9_SETATTR_SIZE;
         tsetattr.size = len;
     })
@@ -592,9 +670,9 @@
 }
 
 #[test]
-fn set_mode() {
+fn set_file_mode() {
     let mode = 0o640;
-    let err = set_attr_test(|tsetattr| {
+    let err = set_attr_test(SetAttrKind::File, |tsetattr| {
         tsetattr.valid = P9_SETATTR_MODE;
         tsetattr.mode = mode;
     })
@@ -604,9 +682,9 @@
 }
 
 #[test]
-fn set_uid() {
+fn set_file_uid() {
     let uid = 294;
-    let err = set_attr_test(|tsetattr| {
+    let err = set_attr_test(SetAttrKind::File, |tsetattr| {
         tsetattr.valid = P9_SETATTR_UID;
         tsetattr.uid = uid;
     })
@@ -616,9 +694,9 @@
 }
 
 #[test]
-fn set_gid() {
+fn set_file_gid() {
     let gid = 9024;
-    let err = set_attr_test(|tsetattr| {
+    let err = set_attr_test(SetAttrKind::File, |tsetattr| {
         tsetattr.valid = P9_SETATTR_GID;
         tsetattr.gid = gid;
     })
@@ -628,9 +706,9 @@
 }
 
 #[test]
-fn set_mtime() {
+fn set_file_mtime() {
     let (secs, nanos) = (1245247825, 524617);
-    let md = set_attr_test(|tsetattr| {
+    let md = set_attr_test(SetAttrKind::File, |tsetattr| {
         tsetattr.valid = P9_SETATTR_MTIME | P9_SETATTR_MTIME_SET;
         tsetattr.mtime_sec = secs;
         tsetattr.mtime_nsec = nanos;
@@ -642,9 +720,73 @@
 }
 
 #[test]
-fn set_atime() {
+fn set_file_atime() {
     let (secs, nanos) = (9247605, 4016);
-    let md = set_attr_test(|tsetattr| {
+    let md = set_attr_test(SetAttrKind::File, |tsetattr| {
+        tsetattr.valid = P9_SETATTR_ATIME | P9_SETATTR_ATIME_SET;
+        tsetattr.atime_sec = secs;
+        tsetattr.atime_nsec = nanos;
+    })
+    .expect("failed to set atime");
+
+    assert_eq!(md.atime() as u64, secs);
+    assert_eq!(md.atime_nsec() as u64, nanos);
+}
+
+#[test]
+fn set_dir_mode() {
+    let mode = 0o640;
+    let err = set_attr_test(SetAttrKind::Directory, |tsetattr| {
+        tsetattr.valid = P9_SETATTR_MODE;
+        tsetattr.mode = mode;
+    })
+    .expect_err("successfully set mode");
+
+    assert_eq!(err.kind(), io::ErrorKind::PermissionDenied);
+}
+
+#[test]
+fn set_dir_uid() {
+    let uid = 294;
+    let err = set_attr_test(SetAttrKind::Directory, |tsetattr| {
+        tsetattr.valid = P9_SETATTR_UID;
+        tsetattr.uid = uid;
+    })
+    .expect_err("successfully set uid");
+
+    assert_eq!(err.kind(), io::ErrorKind::PermissionDenied);
+}
+
+#[test]
+fn set_dir_gid() {
+    let gid = 9024;
+    let err = set_attr_test(SetAttrKind::Directory, |tsetattr| {
+        tsetattr.valid = P9_SETATTR_GID;
+        tsetattr.gid = gid;
+    })
+    .expect_err("successfully set gid");
+
+    assert_eq!(err.kind(), io::ErrorKind::PermissionDenied);
+}
+
+#[test]
+fn set_dir_mtime() {
+    let (secs, nanos) = (1245247825, 524617);
+    let md = set_attr_test(SetAttrKind::Directory, |tsetattr| {
+        tsetattr.valid = P9_SETATTR_MTIME | P9_SETATTR_MTIME_SET;
+        tsetattr.mtime_sec = secs;
+        tsetattr.mtime_nsec = nanos;
+    })
+    .expect("failed to set mtime");
+
+    assert_eq!(md.mtime() as u64, secs);
+    assert_eq!(md.mtime_nsec() as u64, nanos);
+}
+
+#[test]
+fn set_dir_atime() {
+    let (secs, nanos) = (9247605, 4016);
+    let md = set_attr_test(SetAttrKind::Directory, |tsetattr| {
         tsetattr.valid = P9_SETATTR_ATIME | P9_SETATTR_ATIME_SET;
         tsetattr.atime_sec = secs;
         tsetattr.atime_nsec = nanos;
@@ -663,12 +805,12 @@
     let newdir = test_dir.join(name);
     fs::create_dir(&newdir).expect("failed to create directory");
 
-    let fid = ROOT_FID + 1;
+    let dfid = ROOT_FID + 1;
     walk(
         &mut server,
         &*test_dir,
         ROOT_FID,
-        fid,
+        dfid,
         vec![String::from(name)],
     );
 
@@ -680,14 +822,20 @@
         assert!(filenames.insert(name));
     }
 
+    let fid = dfid + 1;
+    open(&mut server, &newdir, dfid, "", fid, P9_DIRECTORY).expect("Failed to open directory");
     for f in readdir(&mut server, fid) {
         let path = newdir.join(&f.name);
 
         let md = fs::symlink_metadata(path).expect("failed to get metadata for path");
         check_qid(&f.qid, &md);
 
-        assert_eq!(f.ty, libc::DT_REG);
-        assert!(filenames.remove(&f.name));
+        if f.name == "." || f.name == ".." {
+            assert_eq!(f.ty, libc::DT_DIR);
+        } else {
+            assert_eq!(f.ty, libc::DT_REG);
+            assert!(filenames.remove(&f.name));
+        }
     }
 
     assert!(filenames.is_empty());
@@ -705,7 +853,7 @@
         gid: 0,
     };
 
-    let rmkdir = server.mkdir(&tmkdir).expect("failed to create directory");
+    let rmkdir = server.mkdir(tmkdir).expect("failed to create directory");
     let md =
         fs::symlink_metadata(test_dir.join(name)).expect("failed to get metadata for directory");
 
@@ -714,51 +862,6 @@
 }
 
 #[test]
-fn remove_all() {
-    let (test_dir, mut server) = setup("readdir");
-
-    let mut next_fid = ROOT_FID + 1;
-
-    let mut dirs = VecDeque::new();
-    dirs.push_back(test_dir.to_path_buf());
-
-    // First iterate over the whole directory.
-    let mut fids = VecDeque::new();
-    while let Some(dir) = dirs.pop_front() {
-        for entry in fs::read_dir(dir).expect("failed to read directory") {
-            let entry = entry.expect("unable to iterate over directory");
-            let fid = next_fid;
-            next_fid += 1;
-
-            let wnames: Vec<String> = entry
-                .path()
-                .strip_prefix(&test_dir)
-                .expect("test directory is not prefix of subdir")
-                .components()
-                .map(|c| Path::new(&c).to_string_lossy().to_string())
-                .collect();
-            walk(&mut server, &*test_dir, ROOT_FID, fid, wnames);
-
-            let ft = entry
-                .file_type()
-                .expect("failed to get file type for entry");
-            if ft.is_dir() {
-                dirs.push_back(entry.path());
-            }
-
-            fids.push_back(fid);
-        }
-    }
-
-    // Now remove everything in reverse order.
-    while let Some(fid) = fids.pop_back() {
-        let tremove = Tremove { fid };
-
-        server.remove(&tremove).expect("failed to remove entry");
-    }
-}
-
-#[test]
 fn unlink_all() {
     let (test_dir, mut server) = setup("readdir");
 
@@ -816,49 +919,12 @@
                 flags,
             };
 
-            server.unlink_at(&tunlinkat).expect("failed to unlink path");
+            server.unlink_at(tunlinkat).expect("failed to unlink path");
         }
     }
 }
 
 #[test]
-fn rename() {
-    let (test_dir, mut server) = setup("rename");
-
-    let name = "oldfile";
-    let content = create_local_file(&test_dir, name);
-
-    // First walk to the file to be renamed.
-    let fid = ROOT_FID + 1;
-    walk(
-        &mut server,
-        &*test_dir,
-        ROOT_FID,
-        fid,
-        vec![String::from(name)],
-    );
-
-    let newname = "newfile";
-    let trename = Trename {
-        fid,
-        dfid: ROOT_FID,
-        name: String::from(newname),
-    };
-
-    server.rename(&trename).expect("failed to rename file");
-
-    assert!(!test_dir.join(name).exists());
-
-    let mut newcontent = Vec::with_capacity(content.len());
-    let size = File::open(test_dir.join(newname))
-        .expect("failed to open file")
-        .read_to_end(&mut newcontent)
-        .expect("failed to read new file content");
-    assert_eq!(size, content.len());
-    assert_eq!(newcontent, content);
-}
-
-#[test]
 fn rename_at() {
     let (test_dir, mut server) = setup("rename");
 
@@ -873,7 +939,7 @@
         newname: String::from(newname),
     };
 
-    server.rename_at(&trename).expect("failed to rename file");
+    server.rename_at(trename).expect("failed to rename file");
 
     assert!(!test_dir.join(name).exists());
 
@@ -902,7 +968,7 @@
             let md =
                 fs::symlink_metadata(test_dir.join(name)).expect("failed to get metadata for file");
             check_qid(&rlopen.qid, &md);
-            assert_eq!(rlopen.iounit, 0);
+            assert_eq!(rlopen.iounit, md.blksize() as u32);
 
             check_attr(&mut server, fid, &md);
 
@@ -940,25 +1006,25 @@
     };
 }
 
-open_test!(read_only_file_open, _P9_RDONLY);
+open_test!(read_only_file_open, P9_RDONLY);
 open_test!(read_write_file_open, P9_RDWR);
 open_test!(write_only_file_open, P9_WRONLY);
 
-open_test!(create_read_only_file_open, P9_CREATE | _P9_RDONLY);
+open_test!(create_read_only_file_open, P9_CREATE | P9_RDONLY);
 open_test!(create_read_write_file_open, P9_CREATE | P9_RDWR);
 open_test!(create_write_only_file_open, P9_CREATE | P9_WRONLY);
 
-open_test!(append_read_only_file_open, P9_APPEND | _P9_RDONLY);
+open_test!(append_read_only_file_open, P9_APPEND | P9_RDONLY);
 open_test!(append_read_write_file_open, P9_APPEND | P9_RDWR);
 open_test!(append_write_only_file_open, P9_APPEND | P9_WRONLY);
 
-open_test!(trunc_read_only_file_open, P9_TRUNC | _P9_RDONLY);
+open_test!(trunc_read_only_file_open, P9_TRUNC | P9_RDONLY);
 open_test!(trunc_read_write_file_open, P9_TRUNC | P9_RDWR);
 open_test!(trunc_write_only_file_open, P9_TRUNC | P9_WRONLY);
 
 open_test!(
     create_append_read_only_file_open,
-    P9_CREATE | P9_APPEND | _P9_RDONLY
+    P9_CREATE | P9_APPEND | P9_RDONLY
 );
 open_test!(
     create_append_read_write_file_open,
@@ -971,7 +1037,7 @@
 
 open_test!(
     create_trunc_read_only_file_open,
-    P9_CREATE | P9_TRUNC | _P9_RDONLY
+    P9_CREATE | P9_TRUNC | P9_RDONLY
 );
 open_test!(
     create_trunc_read_write_file_open,
@@ -984,7 +1050,7 @@
 
 open_test!(
     append_trunc_read_only_file_open,
-    P9_APPEND | P9_TRUNC | _P9_RDONLY
+    P9_APPEND | P9_TRUNC | P9_RDONLY
 );
 open_test!(
     append_trunc_read_write_file_open,
@@ -997,7 +1063,7 @@
 
 open_test!(
     create_append_trunc_read_only_file_open,
-    P9_CREATE | P9_APPEND | P9_TRUNC | _P9_RDONLY
+    P9_CREATE | P9_APPEND | P9_TRUNC | P9_RDONLY
 );
 open_test!(
     create_append_trunc_read_write_file_open,
@@ -1010,7 +1076,7 @@
 
 open_test!(
     create_excl_read_only_file_open,
-    P9_CREATE | P9_EXCL | _P9_RDONLY,
+    P9_CREATE | P9_EXCL | P9_RDONLY,
     io::ErrorKind::AlreadyExists
 );
 open_test!(
@@ -1037,7 +1103,7 @@
 
             let md =
                 fs::symlink_metadata(test_dir.join(name)).expect("failed to get metadata for file");
-            assert_eq!(rlcreate.iounit, 0);
+            assert_eq!(rlcreate.iounit, md.blksize() as u32);
             check_qid(&rlcreate.qid, &md);
             check_attr(&mut server, fid, &md);
 
@@ -1068,13 +1134,13 @@
     };
 }
 
-create_test!(read_only_file_create, _P9_RDONLY, 0o600u32);
+create_test!(read_only_file_create, P9_RDONLY, 0o600u32);
 create_test!(read_write_file_create, P9_RDWR, 0o600u32);
 create_test!(write_only_file_create, P9_WRONLY, 0o600u32);
 
 create_test!(
     append_read_only_file_create,
-    P9_APPEND | _P9_RDONLY,
+    P9_APPEND | P9_RDONLY,
     0o600u32
 );
 create_test!(append_read_write_file_create, P9_APPEND | P9_RDWR, 0o600u32);
diff --git a/wire_format_derive/Android.bp b/wire_format_derive/Android.bp
index 1604435..b5908db 100644
--- a/wire_format_derive/Android.bp
+++ b/wire_format_derive/Android.bp
@@ -1,4 +1,13 @@
-// This file is generated by cargo2android.py --run --device --tests --dependencies.
+// This file is generated by cargo2android.py --run --device --tests --dependencies --patch=patches/Android.bp.patch.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "external_vm_tools_p9_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-BSD
+    default_applicable_licenses: ["external_vm_tools_p9_license"],
+}
 
 rust_proc_macro {
     name: "libwire_format_derive",