vm_tools: p9: Migrate from crosvm repo

The p9 crate was historically in the crosvm repo because that was the
only place where we could build rust code.  Now that we have a proper
eclass for rust code, move it into vm_tools since that's a better fit
for it.

BUG=none
TEST='FEATURES=test emerge-{eve,kevin} dev-rust/p9`

Change-Id: Ic4f6fe0dd2575640df31143ba1ba8fedb5b6bd13
Signed-off-by: Chirantan Ekbote <chirantan@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/1279119
diff --git a/vm_tools/README.md b/vm_tools/README.md
index 6e47b68..91391c2 100644
--- a/vm_tools/README.md
+++ b/vm_tools/README.md
@@ -14,7 +14,7 @@
 
 When `vm_concierge` receives a request to start a VM it allocates various
 resources for that VM (IPv4 address, vsock context id, etc) from a shared pool
-of resources.  It then launches a new instance of `crosvm` to actually run the
+of resources.  It then launches a new instance of [crosvm] to actually run the
 VM.
 
 Once the VM has started up `vm_concierge` communicates with the `maitred`
@@ -57,12 +57,17 @@
 IPs are allowed to connect to the corresponding port for `garcon` that is open
 in `vm_cicerone`.
 
+## p9
+
+The [p9](p9/) directory holds a server implementation of the [9p] file system
+protocol.  It is used by [crosvm] and the [9s](#9s) daemon to share files and
+directories between the host and the VM.
+
 ## seneschal
 
 `seneschal` is the steward of the user's /home directory. It manages processes
-that serve the [9p](http://man.cat-v.org/plan_9/5/0intro) file system protocol.
-The 9p client lives in the guest kernel and communicates with the server over
-[vsock](https://lwn.net/Articles/695981/).
+that serve the [9p] file system protocol. The 9p client lives in the guest
+kernel and communicates with the server over [vsock].
 
 Each server initially does not have access to any path but can be
 granted access to specific paths in the user's home directory by sending
@@ -110,11 +115,11 @@
 ## VM <-> host communication
 
 All communication between `vm_concierge` and the applications inside the VM
-happen over a [vsock](https://lwn.net/Articles/695981/) transport. The actual
-RPC communication uses the [gRPC](http://grpc.io) framework. Every `maitred`
-instance listens on a known port in the vsock namespace (port 8888).
+happen over a [vsock] transport. The actual RPC communication uses the
+[gRPC](http://grpc.io) framework. Every `maitred` instance listens on a known
+port in the vsock namespace (port 8888).
 
-See [docs/vsock.md](docs/vsock.md) for more details about vsock.
+See [docs/vsock.md](docs/vsock.md) for more details about [vsock].
 
 ### Authentication
 
@@ -132,3 +137,8 @@
 the serialization format for messages sent over the vsock and IP transport.  The
 [proto](proto/) directory holds the definitions for all the messages sent and
 services provided between the host and the VM/container.
+
+
+[9p]: http://man.cat-v.org/plan_9/5/0intro
+[crosvm]: https://chromium.googlesource.com/chromiumos/platform/crosvm
+[vsock]: https://lwn.net/Articles/695981/
diff --git a/vm_tools/p9/Cargo.toml b/vm_tools/p9/Cargo.toml
new file mode 100644
index 0000000..cdc6989
--- /dev/null
+++ b/vm_tools/p9/Cargo.toml
@@ -0,0 +1,24 @@
+[package]
+name = "p9"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+
+[dependencies]
+libc = "*"
+wire_format_derive = "*"
+
+[features]
+trace = []
+
+[profile.dev]
+# We want some optimization even in dev builds.
+opt-level = 2
+
+[profile.release]
+# We still want debug information in release builds.
+debug = true
+lto = true
+panic = 'abort'
+
+[patch.crates-io]
+wire_format_derive = { path = "wire_format_derive" }
diff --git a/vm_tools/p9/README.md b/vm_tools/p9/README.md
new file mode 100644
index 0000000..b85cc15
--- /dev/null
+++ b/vm_tools/p9/README.md
@@ -0,0 +1,21 @@
+# p9 - Server implementation of the [9p] file system protocol
+
+This directory contains the protocol definition and a server implementation of
+the [9p] file system protocol.
+
+*   [wire_format_derive] - A [procedural macro] that derives the serialization
+    and de-serialization implementation for a struct into the [9p] wire
+    format.
+*   [src/protocol] - Defines all the messages used in the [9p] protocol.  Also
+    implements serialization and de-serialization for some base types
+    (integers, strings, vectors) that form the foundation of all [9p]
+    messages.  Wire format implementations for all other messages are derived
+    using the `wire_format_derive` macro.
+*   [src/server.rs] - Implements a full [9p] server, carrying out file system
+    requests on behalf of clients.
+
+[9p]: http://man.cat-v.org/plan_9/5/intro
+[procedural macro]: https://doc.rust-lang.org/proc_macro/index.html
+[src/protocol]: src/protocol/
+[src/server.rs]: src/server.rs
+[wire_format_derive]: wire_format_derive/
diff --git a/vm_tools/p9/src/lib.rs b/vm_tools/p9/src/lib.rs
new file mode 100644
index 0000000..ef0744e
--- /dev/null
+++ b/vm_tools/p9/src/lib.rs
@@ -0,0 +1,13 @@
+// Copyright 2018 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+extern crate libc;
+
+#[macro_use]
+extern crate wire_format_derive;
+
+mod protocol;
+mod server;
+
+pub use server::Server;
diff --git a/vm_tools/p9/src/protocol/messages.rs b/vm_tools/p9/src/protocol/messages.rs
new file mode 100644
index 0000000..23f0d9f
--- /dev/null
+++ b/vm_tools/p9/src/protocol/messages.rs
@@ -0,0 +1,840 @@
+// Copyright 2018 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::io::{self, ErrorKind, Read, Write};
+use std::mem;
+use std::string::String;
+use std::vec::Vec;
+
+use protocol::wire_format::{Data, WireFormat};
+
+// Message type constants.  Taken from "include/net/9p/9p.h" in the linux kernel
+// tree.  The protocol specifies each R* message to be the corresponding T*
+// message plus one.
+const TLERROR: u8 = 6;
+const RLERROR: u8 = TLERROR + 1;
+const TSTATFS: u8 = 8;
+const RSTATFS: u8 = TSTATFS + 1;
+const TLOPEN: u8 = 12;
+const RLOPEN: u8 = TLOPEN + 1;
+const TLCREATE: u8 = 14;
+const RLCREATE: u8 = TLCREATE + 1;
+const TSYMLINK: u8 = 16;
+const RSYMLINK: u8 = TSYMLINK + 1;
+const TMKNOD: u8 = 18;
+const RMKNOD: u8 = TMKNOD + 1;
+const TRENAME: u8 = 20;
+const RRENAME: u8 = TRENAME + 1;
+const TREADLINK: u8 = 22;
+const RREADLINK: u8 = TREADLINK + 1;
+const TGETATTR: u8 = 24;
+const RGETATTR: u8 = TGETATTR + 1;
+const TSETATTR: u8 = 26;
+const RSETATTR: u8 = TSETATTR + 1;
+const TXATTRWALK: u8 = 30;
+const RXATTRWALK: u8 = TXATTRWALK + 1;
+const TXATTRCREATE: u8 = 32;
+const RXATTRCREATE: u8 = TXATTRCREATE + 1;
+const TREADDIR: u8 = 40;
+const RREADDIR: u8 = TREADDIR + 1;
+const TFSYNC: u8 = 50;
+const RFSYNC: u8 = TFSYNC + 1;
+const TLOCK: u8 = 52;
+const RLOCK: u8 = TLOCK + 1;
+const TGETLOCK: u8 = 54;
+const RGETLOCK: u8 = TGETLOCK + 1;
+const TLINK: u8 = 70;
+const RLINK: u8 = TLINK + 1;
+const TMKDIR: u8 = 72;
+const RMKDIR: u8 = TMKDIR + 1;
+const TRENAMEAT: u8 = 74;
+const RRENAMEAT: u8 = TRENAMEAT + 1;
+const TUNLINKAT: u8 = 76;
+const RUNLINKAT: u8 = TUNLINKAT + 1;
+const TVERSION: u8 = 100;
+const RVERSION: u8 = TVERSION + 1;
+const TAUTH: u8 = 102;
+const RAUTH: u8 = TAUTH + 1;
+const TATTACH: u8 = 104;
+const RATTACH: u8 = TATTACH + 1;
+const _TERROR: u8 = 106;
+const _RERROR: u8 = _TERROR + 1;
+const TFLUSH: u8 = 108;
+const RFLUSH: u8 = TFLUSH + 1;
+const TWALK: u8 = 110;
+const RWALK: u8 = TWALK + 1;
+const _TOPEN: u8 = 112;
+const _ROPEN: u8 = _TOPEN + 1;
+const _TCREATE: u8 = 114;
+const _RCREATE: u8 = _TCREATE + 1;
+const TREAD: u8 = 116;
+const RREAD: u8 = TREAD + 1;
+const TWRITE: u8 = 118;
+const RWRITE: u8 = TWRITE + 1;
+const TCLUNK: u8 = 120;
+const RCLUNK: u8 = TCLUNK + 1;
+const TREMOVE: u8 = 122;
+const RREMOVE: u8 = TREMOVE + 1;
+const _TSTAT: u8 = 124;
+const _RSTAT: u8 = _TSTAT + 1;
+const _TWSTAT: u8 = 126;
+const _RWSTAT: u8 = _TWSTAT + 1;
+
+/// A message sent from a 9P client to a 9P server.
+#[derive(Debug)]
+pub enum Tmessage {
+    Version(Tversion),
+    Flush(Tflush),
+    Walk(Twalk),
+    Read(Tread),
+    Write(Twrite),
+    Clunk(Tclunk),
+    Remove(Tremove),
+    Attach(Tattach),
+    Auth(Tauth),
+    Statfs(Tstatfs),
+    Lopen(Tlopen),
+    Lcreate(Tlcreate),
+    Symlink(Tsymlink),
+    Mknod(Tmknod),
+    Rename(Trename),
+    Readlink(Treadlink),
+    GetAttr(Tgetattr),
+    SetAttr(Tsetattr),
+    XattrWalk(Txattrwalk),
+    XattrCreate(Txattrcreate),
+    Readdir(Treaddir),
+    Fsync(Tfsync),
+    Lock(Tlock),
+    GetLock(Tgetlock),
+    Link(Tlink),
+    Mkdir(Tmkdir),
+    RenameAt(Trenameat),
+    UnlinkAt(Tunlinkat),
+}
+
+#[derive(Debug)]
+pub struct Tframe {
+    pub tag: u16,
+    pub msg: Tmessage,
+}
+
+impl WireFormat for Tframe {
+    fn byte_size(&self) -> u32 {
+        let msg_size = match self.msg {
+            Tmessage::Version(ref version) => version.byte_size(),
+            Tmessage::Flush(ref flush) => flush.byte_size(),
+            Tmessage::Walk(ref walk) => walk.byte_size(),
+            Tmessage::Read(ref read) => read.byte_size(),
+            Tmessage::Write(ref write) => write.byte_size(),
+            Tmessage::Clunk(ref clunk) => clunk.byte_size(),
+            Tmessage::Remove(ref remove) => remove.byte_size(),
+            Tmessage::Attach(ref attach) => attach.byte_size(),
+            Tmessage::Auth(ref auth) => auth.byte_size(),
+            Tmessage::Statfs(ref statfs) => statfs.byte_size(),
+            Tmessage::Lopen(ref lopen) => lopen.byte_size(),
+            Tmessage::Lcreate(ref lcreate) => lcreate.byte_size(),
+            Tmessage::Symlink(ref symlink) => symlink.byte_size(),
+            Tmessage::Mknod(ref mknod) => mknod.byte_size(),
+            Tmessage::Rename(ref rename) => rename.byte_size(),
+            Tmessage::Readlink(ref readlink) => readlink.byte_size(),
+            Tmessage::GetAttr(ref getattr) => getattr.byte_size(),
+            Tmessage::SetAttr(ref setattr) => setattr.byte_size(),
+            Tmessage::XattrWalk(ref xattrwalk) => xattrwalk.byte_size(),
+            Tmessage::XattrCreate(ref xattrcreate) => xattrcreate.byte_size(),
+            Tmessage::Readdir(ref readdir) => readdir.byte_size(),
+            Tmessage::Fsync(ref fsync) => fsync.byte_size(),
+            Tmessage::Lock(ref lock) => lock.byte_size(),
+            Tmessage::GetLock(ref getlock) => getlock.byte_size(),
+            Tmessage::Link(ref link) => link.byte_size(),
+            Tmessage::Mkdir(ref mkdir) => mkdir.byte_size(),
+            Tmessage::RenameAt(ref renameat) => renameat.byte_size(),
+            Tmessage::UnlinkAt(ref unlinkat) => unlinkat.byte_size(),
+        };
+
+        // size + type + tag + message size
+        (mem::size_of::<u32>() + mem::size_of::<u8>() + mem::size_of::<u16>()) as u32 + msg_size
+    }
+
+    fn encode<W: Write>(&self, writer: &mut W) -> io::Result<()> {
+        self.byte_size().encode(writer)?;
+
+        let ty = match self.msg {
+            Tmessage::Version(_) => TVERSION,
+            Tmessage::Flush(_) => TFLUSH,
+            Tmessage::Walk(_) => TWALK,
+            Tmessage::Read(_) => TREAD,
+            Tmessage::Write(_) => TWRITE,
+            Tmessage::Clunk(_) => TCLUNK,
+            Tmessage::Remove(_) => TREMOVE,
+            Tmessage::Attach(_) => TATTACH,
+            Tmessage::Auth(_) => TAUTH,
+            Tmessage::Statfs(_) => TSTATFS,
+            Tmessage::Lopen(_) => TLOPEN,
+            Tmessage::Lcreate(_) => TLCREATE,
+            Tmessage::Symlink(_) => TSYMLINK,
+            Tmessage::Mknod(_) => TMKNOD,
+            Tmessage::Rename(_) => TRENAME,
+            Tmessage::Readlink(_) => TREADLINK,
+            Tmessage::GetAttr(_) => TGETATTR,
+            Tmessage::SetAttr(_) => TSETATTR,
+            Tmessage::XattrWalk(_) => TXATTRWALK,
+            Tmessage::XattrCreate(_) => TXATTRCREATE,
+            Tmessage::Readdir(_) => TREADDIR,
+            Tmessage::Fsync(_) => TFSYNC,
+            Tmessage::Lock(_) => TLOCK,
+            Tmessage::GetLock(_) => TGETLOCK,
+            Tmessage::Link(_) => TLINK,
+            Tmessage::Mkdir(_) => TMKDIR,
+            Tmessage::RenameAt(_) => TRENAMEAT,
+            Tmessage::UnlinkAt(_) => TUNLINKAT,
+        };
+
+        ty.encode(writer)?;
+        self.tag.encode(writer)?;
+
+        match self.msg {
+            Tmessage::Version(ref version) => version.encode(writer),
+            Tmessage::Flush(ref flush) => flush.encode(writer),
+            Tmessage::Walk(ref walk) => walk.encode(writer),
+            Tmessage::Read(ref read) => read.encode(writer),
+            Tmessage::Write(ref write) => write.encode(writer),
+            Tmessage::Clunk(ref clunk) => clunk.encode(writer),
+            Tmessage::Remove(ref remove) => remove.encode(writer),
+            Tmessage::Attach(ref attach) => attach.encode(writer),
+            Tmessage::Auth(ref auth) => auth.encode(writer),
+            Tmessage::Statfs(ref statfs) => statfs.encode(writer),
+            Tmessage::Lopen(ref lopen) => lopen.encode(writer),
+            Tmessage::Lcreate(ref lcreate) => lcreate.encode(writer),
+            Tmessage::Symlink(ref symlink) => symlink.encode(writer),
+            Tmessage::Mknod(ref mknod) => mknod.encode(writer),
+            Tmessage::Rename(ref rename) => rename.encode(writer),
+            Tmessage::Readlink(ref readlink) => readlink.encode(writer),
+            Tmessage::GetAttr(ref getattr) => getattr.encode(writer),
+            Tmessage::SetAttr(ref setattr) => setattr.encode(writer),
+            Tmessage::XattrWalk(ref xattrwalk) => xattrwalk.encode(writer),
+            Tmessage::XattrCreate(ref xattrcreate) => xattrcreate.encode(writer),
+            Tmessage::Readdir(ref readdir) => readdir.encode(writer),
+            Tmessage::Fsync(ref fsync) => fsync.encode(writer),
+            Tmessage::Lock(ref lock) => lock.encode(writer),
+            Tmessage::GetLock(ref getlock) => getlock.encode(writer),
+            Tmessage::Link(ref link) => link.encode(writer),
+            Tmessage::Mkdir(ref mkdir) => mkdir.encode(writer),
+            Tmessage::RenameAt(ref renameat) => renameat.encode(writer),
+            Tmessage::UnlinkAt(ref unlinkat) => unlinkat.encode(writer),
+        }
+    }
+
+    fn decode<R: Read>(reader: &mut R) -> io::Result<Self> {
+        let byte_size: u32 = WireFormat::decode(reader)?;
+
+        // byte_size includes the size of byte_size so remove that from the
+        // expected length of the message.  Also make sure that byte_size is at least
+        // that long to begin with.
+        if byte_size < mem::size_of::<u32>() as u32 {
+            return Err(io::Error::new(
+                ErrorKind::InvalidData,
+                format!("byte_size(= {}) is less than 4 bytes", byte_size),
+            ));
+        }
+
+        let reader = &mut reader.take((byte_size - mem::size_of::<u32>() as u32) as u64);
+
+        let mut ty = [0u8];
+        reader.read_exact(&mut ty)?;
+
+        let tag: u16 = WireFormat::decode(reader)?;
+
+        let msg = match ty[0] {
+            TVERSION => Ok(Tmessage::Version(WireFormat::decode(reader)?)),
+            TFLUSH => Ok(Tmessage::Flush(WireFormat::decode(reader)?)),
+            TWALK => Ok(Tmessage::Walk(WireFormat::decode(reader)?)),
+            TREAD => Ok(Tmessage::Read(WireFormat::decode(reader)?)),
+            TWRITE => Ok(Tmessage::Write(WireFormat::decode(reader)?)),
+            TCLUNK => Ok(Tmessage::Clunk(WireFormat::decode(reader)?)),
+            TREMOVE => Ok(Tmessage::Remove(WireFormat::decode(reader)?)),
+            TATTACH => Ok(Tmessage::Attach(WireFormat::decode(reader)?)),
+            TAUTH => Ok(Tmessage::Auth(WireFormat::decode(reader)?)),
+            TSTATFS => Ok(Tmessage::Statfs(WireFormat::decode(reader)?)),
+            TLOPEN => Ok(Tmessage::Lopen(WireFormat::decode(reader)?)),
+            TLCREATE => Ok(Tmessage::Lcreate(WireFormat::decode(reader)?)),
+            TSYMLINK => Ok(Tmessage::Symlink(WireFormat::decode(reader)?)),
+            TMKNOD => Ok(Tmessage::Mknod(WireFormat::decode(reader)?)),
+            TRENAME => Ok(Tmessage::Rename(WireFormat::decode(reader)?)),
+            TREADLINK => Ok(Tmessage::Readlink(WireFormat::decode(reader)?)),
+            TGETATTR => Ok(Tmessage::GetAttr(WireFormat::decode(reader)?)),
+            TSETATTR => Ok(Tmessage::SetAttr(WireFormat::decode(reader)?)),
+            TXATTRWALK => Ok(Tmessage::XattrWalk(WireFormat::decode(reader)?)),
+            TXATTRCREATE => Ok(Tmessage::XattrCreate(WireFormat::decode(reader)?)),
+            TREADDIR => Ok(Tmessage::Readdir(WireFormat::decode(reader)?)),
+            TFSYNC => Ok(Tmessage::Fsync(WireFormat::decode(reader)?)),
+            TLOCK => Ok(Tmessage::Lock(WireFormat::decode(reader)?)),
+            TGETLOCK => Ok(Tmessage::GetLock(WireFormat::decode(reader)?)),
+            TLINK => Ok(Tmessage::Link(WireFormat::decode(reader)?)),
+            TMKDIR => Ok(Tmessage::Mkdir(WireFormat::decode(reader)?)),
+            TRENAMEAT => Ok(Tmessage::RenameAt(WireFormat::decode(reader)?)),
+            TUNLINKAT => Ok(Tmessage::UnlinkAt(WireFormat::decode(reader)?)),
+            err => Err(io::Error::new(
+                ErrorKind::InvalidData,
+                format!("unknown message type {}", err),
+            )),
+        }?;
+
+        Ok(Tframe { tag: tag, msg: msg })
+    }
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tversion {
+    pub msize: u32,
+    pub version: String,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tflush {
+    pub oldtag: u16,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Twalk {
+    pub fid: u32,
+    pub newfid: u32,
+    pub wnames: Vec<String>,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tread {
+    pub fid: u32,
+    pub offset: u64,
+    pub count: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Twrite {
+    pub fid: u32,
+    pub offset: u64,
+    pub data: Data,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tclunk {
+    pub fid: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tremove {
+    pub fid: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tauth {
+    pub afid: u32,
+    pub uname: String,
+    pub aname: String,
+    pub n_uname: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tattach {
+    pub fid: u32,
+    pub afid: u32,
+    pub uname: String,
+    pub aname: String,
+    pub n_uname: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tstatfs {
+    pub fid: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tlopen {
+    pub fid: u32,
+    pub flags: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tlcreate {
+    pub fid: u32,
+    pub name: String,
+    pub flags: u32,
+    pub mode: u32,
+    pub gid: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tsymlink {
+    pub fid: u32,
+    pub name: String,
+    pub symtgt: String,
+    pub gid: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tmknod {
+    pub dfid: u32,
+    pub name: String,
+    pub mode: u32,
+    pub major: u32,
+    pub minor: u32,
+    pub gid: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Trename {
+    pub fid: u32,
+    pub dfid: u32,
+    pub name: String,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Treadlink {
+    pub fid: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tgetattr {
+    pub fid: u32,
+    pub request_mask: u64,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tsetattr {
+    pub fid: u32,
+    pub valid: u32,
+    pub mode: u32,
+    pub uid: u32,
+    pub gid: u32,
+    pub size: u64,
+    pub atime_sec: u64,
+    pub atime_nsec: u64,
+    pub mtime_sec: u64,
+    pub mtime_nsec: u64,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Txattrwalk {
+    pub fid: u32,
+    pub newfid: u32,
+    pub name: String,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Txattrcreate {
+    pub fid: u32,
+    pub name: String,
+    pub attr_size: u64,
+    pub flags: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Treaddir {
+    pub fid: u32,
+    pub offset: u64,
+    pub count: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tfsync {
+    pub fid: u32,
+    pub datasync: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tlock {
+    pub fid: u32,
+    pub type_: u8,
+    pub flags: u32,
+    pub start: u64,
+    pub length: u64,
+    pub proc_id: u32,
+    pub client_id: String,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tgetlock {
+    pub fid: u32,
+    pub type_: u8,
+    pub start: u64,
+    pub length: u64,
+    pub proc_id: u32,
+    pub client_id: String,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tlink {
+    pub dfid: u32,
+    pub fid: u32,
+    pub name: String,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tmkdir {
+    pub dfid: u32,
+    pub name: String,
+    pub mode: u32,
+    pub gid: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Trenameat {
+    pub olddirfid: u32,
+    pub oldname: String,
+    pub newdirfid: u32,
+    pub newname: String,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Tunlinkat {
+    pub dirfd: u32,
+    pub name: String,
+    pub flags: u32,
+}
+
+/// A message sent from a 9P server to a 9P client in response to a request from
+/// that client.  Encapsulates a full frame.
+#[derive(Debug)]
+pub enum Rmessage {
+    Version(Rversion),
+    Flush,
+    Walk(Rwalk),
+    Read(Rread),
+    Write(Rwrite),
+    Clunk,
+    Remove,
+    Attach(Rattach),
+    Auth(Rauth),
+    Statfs(Rstatfs),
+    Lopen(Rlopen),
+    Lcreate(Rlcreate),
+    Symlink(Rsymlink),
+    Mknod(Rmknod),
+    Rename,
+    Readlink(Rreadlink),
+    GetAttr(Rgetattr),
+    SetAttr,
+    XattrWalk(Rxattrwalk),
+    XattrCreate,
+    Readdir(Rreaddir),
+    Fsync,
+    Lock(Rlock),
+    GetLock(Rgetlock),
+    Link,
+    Mkdir(Rmkdir),
+    RenameAt,
+    UnlinkAt,
+    Lerror(Rlerror),
+}
+
+#[derive(Debug)]
+pub struct Rframe {
+    pub tag: u16,
+    pub msg: Rmessage,
+}
+
+impl WireFormat for Rframe {
+    fn byte_size(&self) -> u32 {
+        let msg_size = match self.msg {
+            Rmessage::Version(ref version) => version.byte_size(),
+            Rmessage::Flush => 0,
+            Rmessage::Walk(ref walk) => walk.byte_size(),
+            Rmessage::Read(ref read) => read.byte_size(),
+            Rmessage::Write(ref write) => write.byte_size(),
+            Rmessage::Clunk => 0,
+            Rmessage::Remove => 0,
+            Rmessage::Attach(ref attach) => attach.byte_size(),
+            Rmessage::Auth(ref auth) => auth.byte_size(),
+            Rmessage::Statfs(ref statfs) => statfs.byte_size(),
+            Rmessage::Lopen(ref lopen) => lopen.byte_size(),
+            Rmessage::Lcreate(ref lcreate) => lcreate.byte_size(),
+            Rmessage::Symlink(ref symlink) => symlink.byte_size(),
+            Rmessage::Mknod(ref mknod) => mknod.byte_size(),
+            Rmessage::Rename => 0,
+            Rmessage::Readlink(ref readlink) => readlink.byte_size(),
+            Rmessage::GetAttr(ref getattr) => getattr.byte_size(),
+            Rmessage::SetAttr => 0,
+            Rmessage::XattrWalk(ref xattrwalk) => xattrwalk.byte_size(),
+            Rmessage::XattrCreate => 0,
+            Rmessage::Readdir(ref readdir) => readdir.byte_size(),
+            Rmessage::Fsync => 0,
+            Rmessage::Lock(ref lock) => lock.byte_size(),
+            Rmessage::GetLock(ref getlock) => getlock.byte_size(),
+            Rmessage::Link => 0,
+            Rmessage::Mkdir(ref mkdir) => mkdir.byte_size(),
+            Rmessage::RenameAt => 0,
+            Rmessage::UnlinkAt => 0,
+            Rmessage::Lerror(ref lerror) => lerror.byte_size(),
+        };
+
+        // size + type + tag + message size
+        (mem::size_of::<u32>() + mem::size_of::<u8>() + mem::size_of::<u16>()) as u32 + msg_size
+    }
+
+    fn encode<W: Write>(&self, writer: &mut W) -> io::Result<()> {
+        self.byte_size().encode(writer)?;
+
+        let ty = match self.msg {
+            Rmessage::Version(_) => RVERSION,
+            Rmessage::Flush => RFLUSH,
+            Rmessage::Walk(_) => RWALK,
+            Rmessage::Read(_) => RREAD,
+            Rmessage::Write(_) => RWRITE,
+            Rmessage::Clunk => RCLUNK,
+            Rmessage::Remove => RREMOVE,
+            Rmessage::Attach(_) => RATTACH,
+            Rmessage::Auth(_) => RAUTH,
+            Rmessage::Statfs(_) => RSTATFS,
+            Rmessage::Lopen(_) => RLOPEN,
+            Rmessage::Lcreate(_) => RLCREATE,
+            Rmessage::Symlink(_) => RSYMLINK,
+            Rmessage::Mknod(_) => RMKNOD,
+            Rmessage::Rename => RRENAME,
+            Rmessage::Readlink(_) => RREADLINK,
+            Rmessage::GetAttr(_) => RGETATTR,
+            Rmessage::SetAttr => RSETATTR,
+            Rmessage::XattrWalk(_) => RXATTRWALK,
+            Rmessage::XattrCreate => RXATTRCREATE,
+            Rmessage::Readdir(_) => RREADDIR,
+            Rmessage::Fsync => RFSYNC,
+            Rmessage::Lock(_) => RLOCK,
+            Rmessage::GetLock(_) => RGETLOCK,
+            Rmessage::Link => RLINK,
+            Rmessage::Mkdir(_) => RMKDIR,
+            Rmessage::RenameAt => RRENAMEAT,
+            Rmessage::UnlinkAt => RUNLINKAT,
+            Rmessage::Lerror(_) => RLERROR,
+        };
+
+        ty.encode(writer)?;
+        self.tag.encode(writer)?;
+
+        match self.msg {
+            Rmessage::Version(ref version) => version.encode(writer),
+            Rmessage::Flush => Ok(()),
+            Rmessage::Walk(ref walk) => walk.encode(writer),
+            Rmessage::Read(ref read) => read.encode(writer),
+            Rmessage::Write(ref write) => write.encode(writer),
+            Rmessage::Clunk => Ok(()),
+            Rmessage::Remove => Ok(()),
+            Rmessage::Attach(ref attach) => attach.encode(writer),
+            Rmessage::Auth(ref auth) => auth.encode(writer),
+            Rmessage::Statfs(ref statfs) => statfs.encode(writer),
+            Rmessage::Lopen(ref lopen) => lopen.encode(writer),
+            Rmessage::Lcreate(ref lcreate) => lcreate.encode(writer),
+            Rmessage::Symlink(ref symlink) => symlink.encode(writer),
+            Rmessage::Mknod(ref mknod) => mknod.encode(writer),
+            Rmessage::Rename => Ok(()),
+            Rmessage::Readlink(ref readlink) => readlink.encode(writer),
+            Rmessage::GetAttr(ref getattr) => getattr.encode(writer),
+            Rmessage::SetAttr => Ok(()),
+            Rmessage::XattrWalk(ref xattrwalk) => xattrwalk.encode(writer),
+            Rmessage::XattrCreate => Ok(()),
+            Rmessage::Readdir(ref readdir) => readdir.encode(writer),
+            Rmessage::Fsync => Ok(()),
+            Rmessage::Lock(ref lock) => lock.encode(writer),
+            Rmessage::GetLock(ref getlock) => getlock.encode(writer),
+            Rmessage::Link => Ok(()),
+            Rmessage::Mkdir(ref mkdir) => mkdir.encode(writer),
+            Rmessage::RenameAt => Ok(()),
+            Rmessage::UnlinkAt => Ok(()),
+            Rmessage::Lerror(ref lerror) => lerror.encode(writer),
+        }
+    }
+
+    fn decode<R: Read>(reader: &mut R) -> io::Result<Self> {
+        let byte_size: u32 = WireFormat::decode(reader)?;
+
+        // byte_size includes the size of byte_size so remove that from the
+        // expected length of the message.
+        let reader = &mut reader.take((byte_size - mem::size_of::<u32>() as u32) as u64);
+
+        let mut ty = [0u8];
+        reader.read_exact(&mut ty)?;
+
+        let tag: u16 = WireFormat::decode(reader)?;
+
+        let msg = match ty[0] {
+            RVERSION => Ok(Rmessage::Version(WireFormat::decode(reader)?)),
+            RFLUSH => Ok(Rmessage::Flush),
+            RWALK => Ok(Rmessage::Walk(WireFormat::decode(reader)?)),
+            RREAD => Ok(Rmessage::Read(WireFormat::decode(reader)?)),
+            RWRITE => Ok(Rmessage::Write(WireFormat::decode(reader)?)),
+            RCLUNK => Ok(Rmessage::Clunk),
+            RREMOVE => Ok(Rmessage::Remove),
+            RATTACH => Ok(Rmessage::Attach(WireFormat::decode(reader)?)),
+            RAUTH => Ok(Rmessage::Auth(WireFormat::decode(reader)?)),
+            RSTATFS => Ok(Rmessage::Statfs(WireFormat::decode(reader)?)),
+            RLOPEN => Ok(Rmessage::Lopen(WireFormat::decode(reader)?)),
+            RLCREATE => Ok(Rmessage::Lcreate(WireFormat::decode(reader)?)),
+            RSYMLINK => Ok(Rmessage::Symlink(WireFormat::decode(reader)?)),
+            RMKNOD => Ok(Rmessage::Mknod(WireFormat::decode(reader)?)),
+            RRENAME => Ok(Rmessage::Rename),
+            RREADLINK => Ok(Rmessage::Readlink(WireFormat::decode(reader)?)),
+            RGETATTR => Ok(Rmessage::GetAttr(WireFormat::decode(reader)?)),
+            RSETATTR => Ok(Rmessage::SetAttr),
+            RXATTRWALK => Ok(Rmessage::XattrWalk(WireFormat::decode(reader)?)),
+            RXATTRCREATE => Ok(Rmessage::XattrCreate),
+            RREADDIR => Ok(Rmessage::Readdir(WireFormat::decode(reader)?)),
+            RFSYNC => Ok(Rmessage::Fsync),
+            RLOCK => Ok(Rmessage::Lock(WireFormat::decode(reader)?)),
+            RGETLOCK => Ok(Rmessage::GetLock(WireFormat::decode(reader)?)),
+            RLINK => Ok(Rmessage::Link),
+            RMKDIR => Ok(Rmessage::Mkdir(WireFormat::decode(reader)?)),
+            RRENAMEAT => Ok(Rmessage::RenameAt),
+            RUNLINKAT => Ok(Rmessage::UnlinkAt),
+            RLERROR => Ok(Rmessage::Lerror(WireFormat::decode(reader)?)),
+            err => Err(io::Error::new(
+                ErrorKind::InvalidData,
+                format!("unknown message type {}", err),
+            )),
+        }?;
+
+        Ok(Rframe { tag: tag, msg: msg })
+    }
+}
+
+#[derive(Debug, Copy, Clone, P9WireFormat)]
+pub struct Qid {
+    pub ty: u8,
+    pub version: u32,
+    pub path: u64,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Dirent {
+    pub qid: Qid,
+    pub offset: u64,
+    pub ty: u8,
+    pub name: String,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rversion {
+    pub msize: u32,
+    pub version: String,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rwalk {
+    pub wqids: Vec<Qid>,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rread {
+    pub data: Data,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rwrite {
+    pub count: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rauth {
+    pub aqid: Qid,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rattach {
+    pub qid: Qid,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rlerror {
+    pub ecode: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rstatfs {
+    pub ty: u32,
+    pub bsize: u32,
+    pub blocks: u64,
+    pub bfree: u64,
+    pub bavail: u64,
+    pub files: u64,
+    pub ffree: u64,
+    pub fsid: u64,
+    pub namelen: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rlopen {
+    pub qid: Qid,
+    pub iounit: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rlcreate {
+    pub qid: Qid,
+    pub iounit: u32,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rsymlink {
+    pub qid: Qid,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rmknod {
+    pub qid: Qid,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rreadlink {
+    pub target: String,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rgetattr {
+    pub valid: u64,
+    pub qid: Qid,
+    pub mode: u32,
+    pub uid: u32,
+    pub gid: u32,
+    pub nlink: u64,
+    pub rdev: u64,
+    pub size: u64,
+    pub blksize: u64,
+    pub blocks: u64,
+    pub atime_sec: u64,
+    pub atime_nsec: u64,
+    pub mtime_sec: u64,
+    pub mtime_nsec: u64,
+    pub ctime_sec: u64,
+    pub ctime_nsec: u64,
+    pub btime_sec: u64,
+    pub btime_nsec: u64,
+    pub gen: u64,
+    pub data_version: u64,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rxattrwalk {
+    pub size: u64,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rreaddir {
+    pub data: Data,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rlock {
+    pub status: u8,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rgetlock {
+    pub ty: u8,
+    pub start: u64,
+    pub length: u64,
+    pub proc_id: u32,
+    pub client_id: String,
+}
+
+#[derive(Debug, P9WireFormat)]
+pub struct Rmkdir {
+    pub qid: Qid,
+}
diff --git a/vm_tools/p9/src/protocol/mod.rs b/vm_tools/p9/src/protocol/mod.rs
new file mode 100644
index 0000000..9c278ee
--- /dev/null
+++ b/vm_tools/p9/src/protocol/mod.rs
@@ -0,0 +1,9 @@
+// Copyright 2018 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+mod messages;
+mod wire_format;
+
+pub use self::messages::*;
+pub use self::wire_format::{Data, WireFormat};
diff --git a/vm_tools/p9/src/protocol/wire_format.rs b/vm_tools/p9/src/protocol/wire_format.rs
new file mode 100644
index 0000000..afdc097
--- /dev/null
+++ b/vm_tools/p9/src/protocol/wire_format.rs
@@ -0,0 +1,708 @@
+// Copyright 2018 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std;
+use std::fmt;
+use std::io;
+use std::io::{ErrorKind, Read, Write};
+use std::mem;
+use std::ops::{Deref, DerefMut};
+use std::string::String;
+use std::vec::Vec;
+
+/// A type that can be encoded on the wire using the 9P protocol.
+pub trait WireFormat: std::marker::Sized {
+    /// Returns the number of bytes necessary to fully encode `self`.
+    fn byte_size(&self) -> u32;
+
+    /// Encodes `self` into `writer`.
+    fn encode<W: Write>(&self, writer: &mut W) -> io::Result<()>;
+
+    /// Decodes `Self` from `reader`.
+    fn decode<R: Read>(reader: &mut R) -> io::Result<Self>;
+}
+
+// This doesn't really _need_ to be a macro but unfortunately there is no trait bound to
+// express "can be casted to another type", which means we can't write `T as u8` in a trait
+// based implementation.  So instead we have this macro, which is implemented for all the
+// stable unsigned types with the added benefit of not being implemented for the signed
+// types which are not allowed by the protocol.
+macro_rules! uint_wire_format_impl {
+    ($Ty:ty) => {
+        impl WireFormat for $Ty {
+            fn byte_size(&self) -> u32 {
+                mem::size_of::<$Ty>() as u32
+            }
+
+            fn encode<W: Write>(&self, writer: &mut W) -> io::Result<()> {
+                let mut buf = [0u8; mem::size_of::<$Ty>()];
+
+                // Encode the bytes into the buffer in little endian order.
+                for idx in 0..mem::size_of::<$Ty>() {
+                    buf[idx] = (self >> (8 * idx)) as u8;
+                }
+
+                writer.write_all(&buf)
+            }
+
+            fn decode<R: Read>(reader: &mut R) -> io::Result<Self> {
+                let mut buf = [0u8; mem::size_of::<$Ty>()];
+                reader.read_exact(&mut buf)?;
+
+                // Read bytes from the buffer in little endian order.
+                let mut result = 0;
+                for idx in 0..mem::size_of::<$Ty>() {
+                    result |= (buf[idx] as $Ty) << (8 * idx);
+                }
+
+                Ok(result)
+            }
+        }
+    };
+}
+uint_wire_format_impl!(u8);
+uint_wire_format_impl!(u16);
+uint_wire_format_impl!(u32);
+uint_wire_format_impl!(u64);
+
+// The 9P protocol requires that strings are UTF-8 encoded.  The wire format is a u16
+// count |N|, encoded in little endian, followed by |N| bytes of UTF-8 data.
+impl WireFormat for String {
+    fn byte_size(&self) -> u32 {
+        (mem::size_of::<u16>() + self.len()) as u32
+    }
+
+    fn encode<W: Write>(&self, writer: &mut W) -> io::Result<()> {
+        if self.len() > std::u16::MAX as usize {
+            return Err(io::Error::new(
+                ErrorKind::InvalidInput,
+                "string is too long",
+            ));
+        }
+
+        (self.len() as u16).encode(writer)?;
+        writer.write_all(self.as_bytes())
+    }
+
+    fn decode<R: Read>(reader: &mut R) -> io::Result<Self> {
+        let len: u16 = WireFormat::decode(reader)?;
+        let mut result = String::with_capacity(len as usize);
+        reader.take(len as u64).read_to_string(&mut result)?;
+        Ok(result)
+    }
+}
+
+// The wire format for repeated types is similar to that of strings: a little endian
+// encoded u16 |N|, followed by |N| instances of the given type.
+impl<T: WireFormat> WireFormat for Vec<T> {
+    fn byte_size(&self) -> u32 {
+        mem::size_of::<u16>() as u32 + self.iter().map(|elem| elem.byte_size()).sum::<u32>()
+    }
+
+    fn encode<W: Write>(&self, writer: &mut W) -> io::Result<()> {
+        if self.len() > std::u16::MAX as usize {
+            return Err(io::Error::new(
+                ErrorKind::InvalidInput,
+                "too many elements in vector",
+            ));
+        }
+
+        (self.len() as u16).encode(writer)?;
+        for elem in self {
+            elem.encode(writer)?;
+        }
+
+        Ok(())
+    }
+
+    fn decode<R: Read>(reader: &mut R) -> io::Result<Self> {
+        let len: u16 = WireFormat::decode(reader)?;
+        let mut result = Vec::with_capacity(len as usize);
+
+        for _ in 0..len {
+            result.push(WireFormat::decode(reader)?);
+        }
+
+        Ok(result)
+    }
+}
+
+/// A type that encodes an arbitrary number of bytes of data.  Typically used for Rread
+/// Twrite messages.  This differs from a `Vec<u8>` in that it encodes the number of bytes
+/// using a `u32` instead of a `u16`.
+#[derive(PartialEq)]
+pub struct Data(pub Vec<u8>);
+
+// The maximum length of a data buffer that we support.  In practice the server's max message
+// size should prevent us from reading too much data so this check is mainly to ensure a
+// malicious client cannot trick us into allocating massive amounts of memory.
+const MAX_DATA_LENGTH: u32 = 32 * 1024 * 1024;
+
+impl fmt::Debug for Data {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        // There may be a lot of data and we don't want to spew it all out in a trace.  Instead
+        // just print out the number of bytes in the buffer.
+        write!(f, "Data({} bytes)", self.len())
+    }
+}
+
+// Implement Deref and DerefMut so that we don't have to use self.0 everywhere.
+impl Deref for Data {
+    type Target = Vec<u8>;
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+impl DerefMut for Data {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.0
+    }
+}
+
+// Same as Vec<u8> except that it encodes the length as a u32 instead of a u16.
+impl WireFormat for Data {
+    fn byte_size(&self) -> u32 {
+        mem::size_of::<u32>() as u32 + self.iter().map(|elem| elem.byte_size()).sum::<u32>()
+    }
+
+    fn encode<W: Write>(&self, writer: &mut W) -> io::Result<()> {
+        if self.len() > std::u32::MAX as usize {
+            return Err(io::Error::new(ErrorKind::InvalidInput, "data is too large"));
+        }
+        (self.len() as u32).encode(writer)?;
+        writer.write_all(self)
+    }
+
+    fn decode<R: Read>(reader: &mut R) -> io::Result<Self> {
+        let len: u32 = WireFormat::decode(reader)?;
+        if len > MAX_DATA_LENGTH {
+            return Err(io::Error::new(
+                ErrorKind::InvalidData,
+                format!("data length ({} bytes) is too large", len),
+            ));
+        }
+
+        let mut buf = Vec::with_capacity(len as usize);
+        reader.take(len as u64).read_to_end(&mut buf)?;
+
+        if buf.len() == len as usize {
+            Ok(Data(buf))
+        } else {
+            Err(io::Error::new(
+                ErrorKind::UnexpectedEof,
+                format!(
+                    "unexpected end of data: want: {} bytes, got: {} bytes",
+                    len,
+                    buf.len()
+                ),
+            ))
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use std::io::Cursor;
+    use std::mem;
+    use std::string::String;
+
+    #[test]
+    fn integer_byte_size() {
+        assert_eq!(1, 0u8.byte_size());
+        assert_eq!(2, 0u16.byte_size());
+        assert_eq!(4, 0u32.byte_size());
+        assert_eq!(8, 0u64.byte_size());
+    }
+
+    #[test]
+    fn integer_decode() {
+        let buf: [u8; 8] = [0xef, 0xbe, 0xad, 0xde, 0x0d, 0xf0, 0xad, 0x8b];
+
+        assert_eq!(
+            0xef as u8,
+            WireFormat::decode(&mut Cursor::new(&buf)).unwrap()
+        );
+        assert_eq!(
+            0xbeef as u16,
+            WireFormat::decode(&mut Cursor::new(&buf)).unwrap()
+        );
+        assert_eq!(
+            0xdeadbeef as u32,
+            WireFormat::decode(&mut Cursor::new(&buf)).unwrap()
+        );
+        assert_eq!(
+            0x8badf00d_deadbeef as u64,
+            WireFormat::decode(&mut Cursor::new(&buf)).unwrap()
+        );
+    }
+
+    #[test]
+    fn integer_encode() {
+        let value: u64 = 0x8badf00d_deadbeef;
+        let expected: [u8; 8] = [0xef, 0xbe, 0xad, 0xde, 0x0d, 0xf0, 0xad, 0x8b];
+
+        let mut buf = Vec::with_capacity(8);
+        buf.resize(8, 0);
+
+        (value as u8).encode(&mut Cursor::new(&mut *buf)).unwrap();
+        assert_eq!(expected[0..1], buf[0..1]);
+
+        (value as u16).encode(&mut Cursor::new(&mut *buf)).unwrap();
+        assert_eq!(expected[0..2], buf[0..2]);
+
+        (value as u32).encode(&mut Cursor::new(&mut *buf)).unwrap();
+        assert_eq!(expected[0..4], buf[0..4]);
+
+        value.encode(&mut Cursor::new(&mut *buf)).unwrap();
+        assert_eq!(expected[0..8], buf[0..8]);
+    }
+
+    #[test]
+    fn string_byte_size() {
+        let values = [
+            String::from("Google Video"),
+            String::from("网页 图片 资讯更多 »"),
+            String::from("Παγκόσμιος Ιστός"),
+            String::from("Поиск страниц на русском"),
+            String::from("전체서비스"),
+        ];
+
+        let exp = values
+            .iter()
+            .map(|v| (mem::size_of::<u16>() + v.len()) as u32);
+
+        for (value, expected) in values.iter().zip(exp) {
+            assert_eq!(expected, value.byte_size());
+        }
+    }
+
+    #[test]
+    fn zero_length_string() {
+        let s = String::from("");
+        assert_eq!(s.byte_size(), mem::size_of::<u16>() as u32);
+
+        let mut buf = [0xffu8; 4];
+
+        s.encode(&mut Cursor::new(&mut buf[..]))
+            .expect("failed to encode empty string");
+        assert_eq!(&[0, 0, 0xff, 0xff], &buf);
+
+        assert_eq!(
+            s,
+            <String as WireFormat>::decode(&mut Cursor::new(&[0, 0, 0x61, 0x61][..]))
+                .expect("failed to decode empty string")
+        );
+    }
+
+    #[test]
+    fn string_encode() {
+        let values = [
+            String::from("Google Video"),
+            String::from("网页 图片 资讯更多 »"),
+            String::from("Παγκόσμιος Ιστός"),
+            String::from("Поиск страниц на русском"),
+            String::from("전체서비스"),
+        ];
+
+        let expected = values.iter().map(|v| {
+            let len = v.as_bytes().len();
+            let mut buf = Vec::with_capacity(len + mem::size_of::<u16>());
+
+            buf.push(len as u8);
+            buf.push((len >> 8) as u8);
+
+            buf.extend_from_slice(v.as_bytes());
+
+            buf
+        });
+
+        for (val, exp) in values.iter().zip(expected) {
+            let mut buf = Vec::with_capacity(exp.len());
+            buf.resize(exp.len(), 0);
+
+            WireFormat::encode(val, &mut Cursor::new(&mut *buf)).unwrap();
+            assert_eq!(exp, buf);
+        }
+    }
+
+    #[test]
+    fn string_decode() {
+        assert_eq!(
+            String::from("Google Video"),
+            <String as WireFormat>::decode(&mut Cursor::new(
+                &[
+                    0x0c, 0x00, 0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65, 0x20, 0x56, 0x69, 0x64, 0x65,
+                    0x6F,
+                ][..]
+            )).unwrap()
+        );
+        assert_eq!(
+            String::from("网页 图片 资讯更多 »"),
+            <String as WireFormat>::decode(&mut Cursor::new(
+                &[
+                    0x1d, 0x00, 0xE7, 0xBD, 0x91, 0xE9, 0xA1, 0xB5, 0x20, 0xE5, 0x9B, 0xBE, 0xE7,
+                    0x89, 0x87, 0x20, 0xE8, 0xB5, 0x84, 0xE8, 0xAE, 0xAF, 0xE6, 0x9B, 0xB4, 0xE5,
+                    0xA4, 0x9A, 0x20, 0xC2, 0xBB,
+                ][..]
+            )).unwrap()
+        );
+        assert_eq!(
+            String::from("Παγκόσμιος Ιστός"),
+            <String as WireFormat>::decode(&mut Cursor::new(
+                &[
+                    0x1f, 0x00, 0xCE, 0xA0, 0xCE, 0xB1, 0xCE, 0xB3, 0xCE, 0xBA, 0xCF, 0x8C, 0xCF,
+                    0x83, 0xCE, 0xBC, 0xCE, 0xB9, 0xCE, 0xBF, 0xCF, 0x82, 0x20, 0xCE, 0x99, 0xCF,
+                    0x83, 0xCF, 0x84, 0xCF, 0x8C, 0xCF, 0x82,
+                ][..]
+            )).unwrap()
+        );
+        assert_eq!(
+            String::from("Поиск страниц на русском"),
+            <String as WireFormat>::decode(&mut Cursor::new(
+                &[
+                    0x2d, 0x00, 0xD0, 0x9F, 0xD0, 0xBE, 0xD0, 0xB8, 0xD1, 0x81, 0xD0, 0xBA, 0x20,
+                    0xD1, 0x81, 0xD1, 0x82, 0xD1, 0x80, 0xD0, 0xB0, 0xD0, 0xBD, 0xD0, 0xB8, 0xD1,
+                    0x86, 0x20, 0xD0, 0xBD, 0xD0, 0xB0, 0x20, 0xD1, 0x80, 0xD1, 0x83, 0xD1, 0x81,
+                    0xD1, 0x81, 0xD0, 0xBA, 0xD0, 0xBE, 0xD0, 0xBC,
+                ][..]
+            )).unwrap()
+        );
+        assert_eq!(
+            String::from("전체서비스"),
+            <String as WireFormat>::decode(&mut Cursor::new(
+                &[
+                    0x0f, 0x00, 0xEC, 0xA0, 0x84, 0xEC, 0xB2, 0xB4, 0xEC, 0x84, 0x9C, 0xEB, 0xB9,
+                    0x84, 0xEC, 0x8A, 0xA4,
+                ][..]
+            )).unwrap()
+        );
+    }
+
+    #[test]
+    fn invalid_string_decode() {
+        let _ = <String as WireFormat>::decode(&mut Cursor::new(&[
+            0x06, 0x00, 0xed, 0xa0, 0x80, 0xed, 0xbf, 0xbf,
+        ])).expect_err("surrogate code point");
+
+        let _ = <String as WireFormat>::decode(&mut Cursor::new(&[
+            0x05, 0x00, 0xf8, 0x80, 0x80, 0x80, 0xbf,
+        ])).expect_err("overlong sequence");
+
+        let _ =
+            <String as WireFormat>::decode(&mut Cursor::new(&[0x04, 0x00, 0xf4, 0x90, 0x80, 0x80]))
+                .expect_err("out of range");
+
+        let _ =
+            <String as WireFormat>::decode(&mut Cursor::new(&[0x04, 0x00, 0x63, 0x61, 0x66, 0xe9]))
+                .expect_err("ISO-8859-1");
+
+        let _ =
+            <String as WireFormat>::decode(&mut Cursor::new(&[0x04, 0x00, 0xb0, 0xa1, 0xb0, 0xa2]))
+                .expect_err("EUC-KR");
+    }
+
+    #[test]
+    fn vector_encode() {
+        let values: Vec<u32> = vec![291, 18_916, 2_497, 22, 797_162, 2_119_732, 3_213_929_716];
+        let mut expected: Vec<u8> =
+            Vec::with_capacity(values.len() * mem::size_of::<u32>() + mem::size_of::<u16>());
+        expected.push(values.len() as u8);
+        expected.push((values.len() >> 8) as u8);
+
+        const MASK: u32 = 0xff;
+        for val in &values {
+            expected.push((val & MASK) as u8);
+            expected.push(((val >> 8) & MASK) as u8);
+            expected.push(((val >> 16) & MASK) as u8);
+            expected.push(((val >> 24) & MASK) as u8);
+        }
+
+        let mut actual: Vec<u8> = Vec::with_capacity(expected.len());
+        actual.resize(expected.len(), 0);
+
+        WireFormat::encode(&values, &mut Cursor::new(&mut *actual))
+            .expect("failed to encode vector");
+        assert_eq!(expected, actual);
+    }
+
+    #[test]
+    fn vector_decode() {
+        let expected: Vec<u32> = vec![
+            2_498,
+            24,
+            897,
+            4_097_789_579,
+            8_498_119,
+            684_279,
+            961_189_198,
+            7,
+        ];
+        let mut input: Vec<u8> =
+            Vec::with_capacity(expected.len() * mem::size_of::<u32>() + mem::size_of::<u16>());
+        input.push(expected.len() as u8);
+        input.push((expected.len() >> 8) as u8);
+
+        const MASK: u32 = 0xff;
+        for val in &expected {
+            input.push((val & MASK) as u8);
+            input.push(((val >> 8) & MASK) as u8);
+            input.push(((val >> 16) & MASK) as u8);
+            input.push(((val >> 24) & MASK) as u8);
+        }
+
+        assert_eq!(
+            expected,
+            <Vec<u32> as WireFormat>::decode(&mut Cursor::new(&*input))
+                .expect("failed to decode vector")
+        );
+    }
+
+    #[test]
+    fn data_encode() {
+        let values = Data(vec![169, 155, 79, 67, 182, 199, 25, 73, 129, 200]);
+        let mut expected: Vec<u8> =
+            Vec::with_capacity(values.len() * mem::size_of::<u8>() + mem::size_of::<u32>());
+        expected.push(values.len() as u8);
+        expected.push((values.len() >> 8) as u8);
+        expected.push((values.len() >> 16) as u8);
+        expected.push((values.len() >> 24) as u8);
+        expected.extend_from_slice(&values);
+
+        let mut actual: Vec<u8> = Vec::with_capacity(expected.len());
+        actual.resize(expected.len(), 0);
+
+        WireFormat::encode(&values, &mut Cursor::new(&mut *actual))
+            .expect("failed to encode datar");
+        assert_eq!(expected, actual);
+    }
+
+    #[test]
+    fn data_decode() {
+        let expected = Data(vec![219, 15, 8, 155, 194, 129, 79, 91, 46, 53, 173]);
+        let mut input: Vec<u8> =
+            Vec::with_capacity(expected.len() * mem::size_of::<u8>() + mem::size_of::<u32>());
+        input.push(expected.len() as u8);
+        input.push((expected.len() >> 8) as u8);
+        input.push((expected.len() >> 16) as u8);
+        input.push((expected.len() >> 24) as u8);
+        input.extend_from_slice(&expected);
+
+        assert_eq!(
+            expected,
+            <Data as WireFormat>::decode(&mut Cursor::new(&mut *input))
+                .expect("failed to decode data")
+        );
+    }
+
+    #[test]
+    fn error_cases() {
+        // string is too long.
+        let mut foo = String::with_capacity(std::u16::MAX as usize);
+        while foo.len() < std::u16::MAX as usize {
+            foo.push_str("long");
+        }
+        foo.push_str("!");
+
+        let count = foo.len() + mem::size_of::<u16>();
+        let mut buf = Vec::with_capacity(count);
+        buf.resize(count, 0);
+
+        foo.encode(&mut Cursor::new(&mut *buf))
+            .expect_err("long string");
+
+        // vector is too long.
+        let mut bar: Vec<u32> = Vec::with_capacity(std::u16::MAX as usize);
+        while bar.len() < std::u16::MAX as usize {
+            bar.push(0x8bad_f00d);
+        }
+        bar.push(0x00ba_b10c);
+
+        let count = bar.len() * mem::size_of::<u32>();
+        let mut buf = Vec::with_capacity(count);
+        buf.resize(count, 0);
+
+        WireFormat::encode(&bar, &mut Cursor::new(&mut *buf)).expect_err("long vector");
+    }
+
+    #[derive(Debug, PartialEq, P9WireFormat)]
+    struct Item {
+        foo: u64,
+        bar: String,
+        baz: Vec<u16>,
+        buf: Data,
+    }
+
+    #[test]
+    fn struct_encode() {
+        let item = Item {
+            foo: 0xdead10cc_00bab10c,
+            bar: String::from("冻住,不许走!"),
+            baz: vec![359, 492, 8891],
+            buf: Data(vec![254, 129, 0, 62, 49, 172]),
+        };
+
+        let mut expected: Vec<u8> = vec![0x0c, 0xb1, 0xba, 0x00, 0xcc, 0x10, 0xad, 0xde];
+        let strlen = item.bar.len() as u16;
+        expected.push(strlen as u8);
+        expected.push((strlen >> 8) as u8);
+        expected.extend_from_slice(item.bar.as_bytes());
+
+        let veclen = item.baz.len() as u16;
+        expected.push(veclen as u8);
+        expected.push((veclen >> 8) as u8);
+        for val in &item.baz {
+            expected.push(*val as u8);
+            expected.push((val >> 8) as u8);
+        }
+
+        let buflen = item.buf.len() as u32;
+        expected.push(buflen as u8);
+        expected.push((buflen >> 8) as u8);
+        expected.push((buflen >> 16) as u8);
+        expected.push((buflen >> 24) as u8);
+        expected.extend_from_slice(&item.buf);
+
+        let mut actual = Vec::with_capacity(expected.len());
+        actual.resize(expected.len(), 0);
+
+        WireFormat::encode(&item, &mut Cursor::new(&mut *actual)).expect("failed to encode item");
+
+        assert_eq!(expected, actual);
+    }
+
+    #[test]
+    fn struct_decode() {
+        let expected = Item {
+            foo: 0xface_b00c_0404_4b1d,
+            bar: String::from("Огонь по готовности!"),
+            baz: vec![20067, 32449, 549, 4972, 77, 1987],
+            buf: Data(vec![126, 236, 79, 59, 6, 159]),
+        };
+
+        let mut input: Vec<u8> = vec![0x1d, 0x4b, 0x04, 0x04, 0x0c, 0xb0, 0xce, 0xfa];
+        let strlen = expected.bar.len() as u16;
+        input.push(strlen as u8);
+        input.push((strlen >> 8) as u8);
+        input.extend_from_slice(expected.bar.as_bytes());
+
+        let veclen = expected.baz.len() as u16;
+        input.push(veclen as u8);
+        input.push((veclen >> 8) as u8);
+        for val in &expected.baz {
+            input.push(*val as u8);
+            input.push((val >> 8) as u8);
+        }
+
+        let buflen = expected.buf.len() as u32;
+        input.push(buflen as u8);
+        input.push((buflen >> 8) as u8);
+        input.push((buflen >> 16) as u8);
+        input.push((buflen >> 24) as u8);
+        input.extend_from_slice(&expected.buf);
+
+        let actual: Item =
+            WireFormat::decode(&mut Cursor::new(input)).expect("failed to decode item");
+
+        assert_eq!(expected, actual);
+    }
+
+    #[derive(Debug, PartialEq, P9WireFormat)]
+    struct Nested {
+        item: Item,
+        val: Vec<u64>,
+    }
+
+    fn build_encoded_buffer(value: &Nested) -> Vec<u8> {
+        let mut result: Vec<u8> = Vec::new();
+
+        // encode foo
+        result.push(value.item.foo as u8);
+        result.push((value.item.foo >> 8) as u8);
+        result.push((value.item.foo >> 16) as u8);
+        result.push((value.item.foo >> 24) as u8);
+        result.push((value.item.foo >> 32) as u8);
+        result.push((value.item.foo >> 40) as u8);
+        result.push((value.item.foo >> 48) as u8);
+        result.push((value.item.foo >> 56) as u8);
+
+        // encode bar
+        result.push(value.item.bar.len() as u8);
+        result.push((value.item.bar.len() >> 8) as u8);
+        result.extend_from_slice(value.item.bar.as_bytes());
+
+        // encode baz
+        result.push(value.item.baz.len() as u8);
+        result.push((value.item.baz.len() >> 8) as u8);
+        for val in &value.item.baz {
+            result.push((val & 0xffu16) as u8);
+            result.push(((val >> 8) & 0xffu16) as u8);
+        }
+
+        // encode buf
+        result.push(value.item.buf.len() as u8);
+        result.push((value.item.buf.len() >> 8) as u8);
+        result.push((value.item.buf.len() >> 16) as u8);
+        result.push((value.item.buf.len() >> 24) as u8);
+        result.extend_from_slice(&value.item.buf);
+
+        // encode val
+        result.push(value.val.len() as u8);
+        result.push((value.val.len() >> 8) as u8);
+        for val in &value.val {
+            result.push(*val as u8);
+            result.push((val >> 8) as u8);
+            result.push((val >> 16) as u8);
+            result.push((val >> 24) as u8);
+            result.push((val >> 32) as u8);
+            result.push((val >> 40) as u8);
+            result.push((val >> 48) as u8);
+            result.push((val >> 56) as u8);
+        }
+
+        result
+    }
+
+    #[test]
+    fn nested_encode() {
+        let value = Nested {
+            item: Item {
+                foo: 0xcafe_d00d_8bad_f00d,
+                bar: String::from("龍が我が敵を喰らう!"),
+                baz: vec![2679, 55_919, 44, 38_819, 792],
+                buf: Data(vec![129, 55, 200, 93, 7, 68]),
+            },
+            val: vec![1954978, 59, 4519, 15679],
+        };
+
+        let expected = build_encoded_buffer(&value);
+
+        let mut actual = Vec::with_capacity(expected.len());
+        actual.resize(expected.len(), 0);
+
+        WireFormat::encode(&value, &mut Cursor::new(&mut *actual)).expect("failed to encode value");
+        assert_eq!(expected, actual);
+    }
+
+    #[test]
+    fn nested_decode() {
+        let expected = Nested {
+            item: Item {
+                foo: 0x0ff1ce,
+                bar: String::from("龍神の剣を喰らえ!"),
+                baz: vec![21687, 159, 55, 9217, 192],
+                buf: Data(vec![189, 22, 7, 59, 235]),
+            },
+            val: vec![15679, 8619196, 319746, 123957, 77, 0, 492],
+        };
+
+        let input = build_encoded_buffer(&expected);
+
+        assert_eq!(
+            expected,
+            <Nested as WireFormat>::decode(&mut Cursor::new(&*input))
+                .expect("failed to decode value")
+        );
+    }
+}
diff --git a/vm_tools/p9/src/server.rs b/vm_tools/p9/src/server.rs
new file mode 100644
index 0000000..e69e100
--- /dev/null
+++ b/vm_tools/p9/src/server.rs
@@ -0,0 +1,952 @@
+// Copyright 2018 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use libc;
+
+use std::cmp::min;
+use std::collections::{btree_map, BTreeMap};
+use std::ffi::CString;
+use std::fs;
+use std::io::{self, Cursor, Read, Write};
+use std::mem;
+use std::os::linux::fs::MetadataExt;
+use std::os::unix::fs::{DirBuilderExt, FileExt, OpenOptionsExt};
+use std::os::unix::io::AsRawFd;
+use std::path::{Component, Path, PathBuf};
+
+use protocol::*;
+
+// Tlopen and Tlcreate flags.  Taken from "include/net/9p/9p.h" in the linux tree.
+const _P9_RDONLY: u32 = 0o00000000;
+const P9_WRONLY: u32 = 0o00000001;
+const P9_RDWR: u32 = 0o00000002;
+const P9_NOACCESS: u32 = 0o00000003;
+const P9_CREATE: u32 = 0o00000100;
+const P9_EXCL: u32 = 0o00000200;
+const P9_NOCTTY: u32 = 0o00000400;
+const P9_TRUNC: u32 = 0o00001000;
+const P9_APPEND: u32 = 0o00002000;
+const P9_NONBLOCK: u32 = 0o00004000;
+const P9_DSYNC: u32 = 0o00010000;
+const P9_FASYNC: u32 = 0o00020000;
+const P9_DIRECT: u32 = 0o00040000;
+const P9_LARGEFILE: u32 = 0o00100000;
+const P9_DIRECTORY: u32 = 0o00200000;
+const P9_NOFOLLOW: u32 = 0o00400000;
+const P9_NOATIME: u32 = 0o01000000;
+const _P9_CLOEXEC: u32 = 0o02000000;
+const P9_SYNC: u32 = 0o04000000;
+
+// Mapping from 9P flags to libc flags.
+const MAPPED_FLAGS: [(u32, i32); 10] = [
+    (P9_NOCTTY, libc::O_NOCTTY),
+    (P9_NONBLOCK, libc::O_NONBLOCK),
+    (P9_DSYNC, libc::O_DSYNC),
+    (P9_FASYNC, 0), // Unsupported
+    (P9_DIRECT, libc::O_DIRECT),
+    (P9_LARGEFILE, libc::O_LARGEFILE),
+    (P9_DIRECTORY, libc::O_DIRECTORY),
+    (P9_NOFOLLOW, libc::O_NOFOLLOW),
+    (P9_NOATIME, libc::O_NOATIME),
+    (P9_SYNC, libc::O_SYNC),
+];
+
+// 9P Qid types.  Taken from "include/net/9p/9p.h" in the linux tree.
+const P9_QTDIR: u8 = 0x80;
+const _P9_QTAPPEND: u8 = 0x40;
+const _P9_QTEXCL: u8 = 0x20;
+const _P9_QTMOUNT: u8 = 0x10;
+const _P9_QTAUTH: u8 = 0x08;
+const _P9_QTTMP: u8 = 0x04;
+const _P9_QTSYMLINK: u8 = 0x02;
+const _P9_QTLINK: u8 = 0x01;
+const P9_QTFILE: u8 = 0x00;
+
+// Bitmask values for the getattr request.
+const _P9_GETATTR_MODE: u64 = 0x00000001;
+const _P9_GETATTR_NLINK: u64 = 0x00000002;
+const _P9_GETATTR_UID: u64 = 0x00000004;
+const _P9_GETATTR_GID: u64 = 0x00000008;
+const _P9_GETATTR_RDEV: u64 = 0x00000010;
+const _P9_GETATTR_ATIME: u64 = 0x00000020;
+const _P9_GETATTR_MTIME: u64 = 0x00000040;
+const _P9_GETATTR_CTIME: u64 = 0x00000080;
+const _P9_GETATTR_INO: u64 = 0x00000100;
+const _P9_GETATTR_SIZE: u64 = 0x00000200;
+const _P9_GETATTR_BLOCKS: u64 = 0x00000400;
+
+const _P9_GETATTR_BTIME: u64 = 0x00000800;
+const _P9_GETATTR_GEN: u64 = 0x00001000;
+const _P9_GETATTR_DATA_VERSION: u64 = 0x00002000;
+
+const P9_GETATTR_BASIC: u64 = 0x000007ff; /* Mask for fields up to BLOCKS */
+const _P9_GETATTR_ALL: u64 = 0x00003fff; /* Mask for All fields above */
+
+// Bitmask values for the setattr request.
+const P9_SETATTR_MODE: u32 = 0x00000001;
+const P9_SETATTR_UID: u32 = 0x00000002;
+const P9_SETATTR_GID: u32 = 0x00000004;
+const P9_SETATTR_SIZE: u32 = 0x00000008;
+const P9_SETATTR_ATIME: u32 = 0x00000010;
+const P9_SETATTR_MTIME: u32 = 0x00000020;
+const P9_SETATTR_CTIME: u32 = 0x00000040;
+const P9_SETATTR_ATIME_SET: u32 = 0x00000080;
+const P9_SETATTR_MTIME_SET: u32 = 0x00000100;
+
+// Minimum and maximum message size that we'll expect from the client.
+const MIN_MESSAGE_SIZE: u32 = 256;
+const MAX_MESSAGE_SIZE: u32 = ::std::u16::MAX as u32;
+
+// 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.
+struct Fid {
+    path: Box<Path>,
+    metadata: fs::Metadata,
+    file: Option<fs::File>,
+    dirents: Option<Vec<Dirent>>,
+}
+
+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
+    };
+
+    Qid {
+        ty: ty,
+        // TODO: deal with the 2038 problem before 2038
+        version: metadata.st_mtime() as u32,
+        path: metadata.st_ino(),
+    }
+}
+
+fn error_to_rmessage(err: io::Error) -> Rmessage {
+    let errno = if let Some(errno) = err.raw_os_error() {
+        errno
+    } else {
+        // Make a best-effort guess based on the kind.
+        match err.kind() {
+            io::ErrorKind::NotFound => libc::ENOENT,
+            io::ErrorKind::PermissionDenied => libc::EPERM,
+            io::ErrorKind::ConnectionRefused => libc::ECONNREFUSED,
+            io::ErrorKind::ConnectionReset => libc::ECONNRESET,
+            io::ErrorKind::ConnectionAborted => libc::ECONNABORTED,
+            io::ErrorKind::NotConnected => libc::ENOTCONN,
+            io::ErrorKind::AddrInUse => libc::EADDRINUSE,
+            io::ErrorKind::AddrNotAvailable => libc::EADDRNOTAVAIL,
+            io::ErrorKind::BrokenPipe => libc::EPIPE,
+            io::ErrorKind::AlreadyExists => libc::EEXIST,
+            io::ErrorKind::WouldBlock => libc::EWOULDBLOCK,
+            io::ErrorKind::InvalidInput => libc::EINVAL,
+            io::ErrorKind::InvalidData => libc::EINVAL,
+            io::ErrorKind::TimedOut => libc::ETIMEDOUT,
+            io::ErrorKind::WriteZero => libc::EIO,
+            io::ErrorKind::Interrupted => libc::EINTR,
+            io::ErrorKind::Other => libc::EIO,
+            io::ErrorKind::UnexpectedEof => libc::EIO,
+            _ => libc::EIO,
+        }
+    };
+
+    Rmessage::Lerror(Rlerror {
+        ecode: errno as u32,
+    })
+}
+
+// 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)
+}
+
+pub struct Server {
+    root: Box<Path>,
+    msize: u32,
+    fids: BTreeMap<u32, Fid>,
+}
+
+impl Server {
+    pub fn new<P: AsRef<Path>>(root: P) -> Server {
+        Server {
+            root: root.as_ref().into(),
+            msize: MAX_MESSAGE_SIZE,
+            fids: BTreeMap::new(),
+        }
+    }
+
+    pub fn handle_message<R: Read, W: Write>(
+        &mut self,
+        reader: &mut R,
+        writer: &mut W,
+    ) -> io::Result<()> {
+        let request: Tframe = WireFormat::decode(&mut reader.take(self.msize as u64))?;
+
+        if cfg!(feature = "trace") {
+            println!("{:?}", &request);
+        }
+
+        let rmsg = match request.msg {
+            Tmessage::Version(ref version) => self.version(version),
+            Tmessage::Flush(ref flush) => self.flush(flush),
+            Tmessage::Walk(ref walk) => self.walk(walk),
+            Tmessage::Read(ref read) => self.read(read),
+            Tmessage::Write(ref write) => self.write(write),
+            Tmessage::Clunk(ref clunk) => self.clunk(clunk),
+            Tmessage::Remove(ref remove) => self.remove(remove),
+            Tmessage::Attach(ref attach) => self.attach(attach),
+            Tmessage::Auth(ref auth) => self.auth(auth),
+            Tmessage::Statfs(ref statfs) => self.statfs(statfs),
+            Tmessage::Lopen(ref lopen) => self.lopen(lopen),
+            Tmessage::Lcreate(ref lcreate) => self.lcreate(lcreate),
+            Tmessage::Symlink(ref symlink) => self.symlink(symlink),
+            Tmessage::Mknod(ref mknod) => self.mknod(mknod),
+            Tmessage::Rename(ref rename) => self.rename(rename),
+            Tmessage::Readlink(ref readlink) => self.readlink(readlink),
+            Tmessage::GetAttr(ref get_attr) => self.get_attr(get_attr),
+            Tmessage::SetAttr(ref set_attr) => self.set_attr(set_attr),
+            Tmessage::XattrWalk(ref xattr_walk) => self.xattr_walk(xattr_walk),
+            Tmessage::XattrCreate(ref xattr_create) => self.xattr_create(xattr_create),
+            Tmessage::Readdir(ref readdir) => self.readdir(readdir),
+            Tmessage::Fsync(ref fsync) => self.fsync(fsync),
+            Tmessage::Lock(ref lock) => self.lock(lock),
+            Tmessage::GetLock(ref get_lock) => self.get_lock(get_lock),
+            Tmessage::Link(ref link) => self.link(link),
+            Tmessage::Mkdir(ref mkdir) => self.mkdir(mkdir),
+            Tmessage::RenameAt(ref rename_at) => self.rename_at(rename_at),
+            Tmessage::UnlinkAt(ref unlink_at) => self.unlink_at(unlink_at),
+        };
+
+        // Errors while handling requests are never fatal.
+        let response = Rframe {
+            tag: request.tag,
+            msg: rmsg.unwrap_or_else(error_to_rmessage),
+        };
+
+        if cfg!(feature = "trace") {
+            println!("{:?}", &response);
+        }
+
+        response.encode(writer)?;
+        writer.flush()
+    }
+
+    fn auth(&mut self, _auth: &Tauth) -> io::Result<Rmessage> {
+        // Returning an error for the auth message means that the server does not require
+        // authentication.
+        Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP))
+    }
+
+    fn attach(&mut self, attach: &Tattach) -> io::Result<Rmessage> {
+        // TODO: Check attach parameters
+        match self.fids.entry(attach.fid) {
+            btree_map::Entry::Vacant(entry) => {
+                let fid = Fid {
+                    path: self.root.to_path_buf().into_boxed_path(),
+                    metadata: fs::metadata(&self.root)?,
+                    file: None,
+                    dirents: None,
+                };
+                let response = Rattach {
+                    qid: metadata_to_qid(&fid.metadata),
+                };
+                entry.insert(fid);
+                Ok(Rmessage::Attach(response))
+            }
+            btree_map::Entry::Occupied(_) => Err(io::Error::from_raw_os_error(libc::EBADF)),
+        }
+    }
+
+    fn version(&mut self, version: &Tversion) -> io::Result<Rmessage> {
+        if version.msize < MIN_MESSAGE_SIZE {
+            return Err(io::Error::from_raw_os_error(libc::EINVAL));
+        }
+
+        // A Tversion request clunks all open fids and terminates any pending I/O.
+        self.fids.clear();
+        self.msize = min(MAX_MESSAGE_SIZE, version.msize);
+
+        Ok(Rmessage::Version(Rversion {
+            msize: self.msize,
+            version: if version.version == "9P2000.L" {
+                String::from("9P2000.L")
+            } else {
+                String::from("unknown")
+            },
+        }))
+    }
+
+    fn flush(&mut self, _flush: &Tflush) -> io::Result<Rmessage> {
+        // TODO: Since everything is synchronous we can't actually flush requests.
+        Ok(Rmessage::Flush)
+    }
+
+    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<Rmessage> {
+        // `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
+            .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()))?;
+
+        // 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) => {
+                // 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
+                    };
+
+                    self.fids.insert(
+                        walk.newfid,
+                        Fid {
+                            path: buf.into_boxed_path(),
+                            metadata: md,
+                            file: None,
+                            dirents: None,
+                        },
+                    );
+                }
+            }
+            Err(e) => {
+                // Only return an error if it occurred on the first component.
+                if mds.is_empty() {
+                    return Err(e);
+                }
+            }
+        }
+
+        Ok(Rmessage::Walk(Rwalk {
+            wqids: mds.iter().map(metadata_to_qid).collect(),
+        }))
+    }
+
+    fn read(&mut self, read: &Tread) -> io::Result<Rmessage> {
+        // Thankfully, `read` cannot be used to read directories in 9P2000.L.
+        let file = self
+            .fids
+            .get_mut(&read.fid)
+            .and_then(|fid| fid.file.as_mut())
+            .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
+
+        // Use an empty Rread struct to figure out the overhead of the header.
+        let header_size = Rframe {
+            tag: 0,
+            msg: Rmessage::Read(Rread {
+                data: Data(Vec::new()),
+            }),
+        }.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 count = file.read_at(&mut buf, read.offset)?;
+        buf.resize(count, 0);
+
+        Ok(Rmessage::Read(Rread { data: buf }))
+    }
+
+    fn write(&mut self, write: &Twrite) -> io::Result<Rmessage> {
+        let file = self
+            .fids
+            .get_mut(&write.fid)
+            .and_then(|fid| fid.file.as_mut())
+            .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
+
+        let count = file.write_at(&write.data, write.offset)?;
+        Ok(Rmessage::Write(Rwrite {
+            count: count as u32,
+        }))
+    }
+
+    fn clunk(&mut self, clunk: &Tclunk) -> io::Result<Rmessage> {
+        match self.fids.entry(clunk.fid) {
+            btree_map::Entry::Vacant(_) => Err(io::Error::from_raw_os_error(libc::EBADF)),
+            btree_map::Entry::Occupied(entry) => {
+                entry.remove();
+                Ok(Rmessage::Clunk)
+            }
+        }
+    }
+
+    fn remove(&mut self, remove: &Tremove) -> io::Result<Rmessage> {
+        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(Rmessage::Remove)
+            }
+        }
+    }
+
+    fn statfs(&mut self, statfs: &Tstatfs) -> io::Result<Rmessage> {
+        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))?;
+
+        // Safe because we are zero-initializing a C struct with only primitive
+        // data members.
+        let mut out: libc::statfs64 = unsafe { mem::zeroed() };
+
+        // 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());
+        }
+
+        Ok(Rmessage::Statfs(Rstatfs {
+            ty: out.f_type as u32,
+            bsize: out.f_bsize as u32,
+            blocks: out.f_blocks,
+            bfree: out.f_bfree,
+            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
+            namelen: out.f_namelen as u32,
+        }))
+    }
+
+    fn lopen(&mut self, lopen: &Tlopen) -> io::Result<Rmessage> {
+        let fid = self
+            .fids
+            .get_mut(&lopen.fid)
+            .ok_or_else(|| io::Error::from_raw_os_error(libc::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 = 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)
+            .append((lopen.flags & P9_APPEND) != 0)
+            .truncate((lopen.flags & P9_TRUNC) != 0)
+            .create((lopen.flags & P9_CREATE) != 0)
+            .create_new((lopen.flags & P9_CREATE) != 0 && (lopen.flags & P9_EXCL) != 0)
+            .custom_flags(custom_flags)
+            .open(&fid.path)?;
+
+        fid.metadata = file.metadata()?;
+        fid.file = Some(file);
+
+        Ok(Rmessage::Lopen(Rlopen {
+            qid: metadata_to_qid(&fid.metadata),
+            iounit: 0,
+        }))
+    }
+
+    fn lcreate(&mut self, lcreate: &Tlcreate) -> io::Result<Rmessage> {
+        let fid = self
+            .fids
+            .get_mut(&lcreate.fid)
+            .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
+
+        if !fid.metadata.is_dir() {
+            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;
+        for &(p9f, of) in &MAPPED_FLAGS {
+            if (lcreate.flags & p9f) != 0 {
+                custom_flags |= of;
+            }
+        }
+
+        let file = fs::OpenOptions::new()
+            .read(false)
+            .write(true)
+            .truncate(true)
+            .create(true)
+            .append((lcreate.flags & P9_APPEND) != 0)
+            .create_new((lcreate.flags & P9_EXCL) != 0)
+            .custom_flags(custom_flags)
+            .mode(lcreate.mode & 0o755)
+            .open(&path)?;
+
+        fid.metadata = file.metadata()?;
+        fid.file = Some(file);
+        fid.path = path.into_boxed_path();
+
+        Ok(Rmessage::Lcreate(Rlcreate {
+            qid: metadata_to_qid(&fid.metadata),
+            iounit: 0,
+        }))
+    }
+
+    fn symlink(&mut self, _symlink: &Tsymlink) -> io::Result<Rmessage> {
+        // symlinks are not allowed.
+        Err(io::Error::from_raw_os_error(libc::EACCES))
+    }
+
+    fn mknod(&mut self, _mknod: &Tmknod) -> io::Result<Rmessage> {
+        // No nodes either.
+        Err(io::Error::from_raw_os_error(libc::EACCES))
+    }
+
+    fn rename(&mut self, rename: &Trename) -> io::Result<Rmessage> {
+        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(Rmessage::Rename)
+    }
+
+    fn readlink(&mut self, _readlink: &Treadlink) -> io::Result<Rmessage> {
+        // symlinks are not allowed
+        Err(io::Error::from_raw_os_error(libc::EACCES))
+    }
+
+    fn get_attr(&mut self, get_attr: &Tgetattr) -> io::Result<Rmessage> {
+        let fid = self
+            .fids
+            .get_mut(&get_attr.fid)
+            .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
+
+        // Refresh the metadata since we were explicitly asked for it.
+        fid.metadata = fs::metadata(&fid.path)?;
+
+        Ok(Rmessage::GetAttr(Rgetattr {
+            valid: P9_GETATTR_BASIC,
+            qid: metadata_to_qid(&fid.metadata),
+            mode: fid.metadata.st_mode(),
+            uid: fid.metadata.st_uid(),
+            gid: fid.metadata.st_gid(),
+            nlink: fid.metadata.st_nlink(),
+            rdev: fid.metadata.st_rdev(),
+            size: fid.metadata.st_size(),
+            blksize: fid.metadata.st_blksize(),
+            blocks: fid.metadata.st_blocks(),
+            atime_sec: fid.metadata.st_atime() as u64,
+            atime_nsec: fid.metadata.st_atime_nsec() as u64,
+            mtime_sec: fid.metadata.st_mtime() as u64,
+            mtime_nsec: fid.metadata.st_mtime_nsec() as u64,
+            ctime_sec: fid.metadata.st_ctime() as u64,
+            ctime_nsec: fid.metadata.st_ctime_nsec() as u64,
+            btime_sec: 0,
+            btime_nsec: 0,
+            gen: 0,
+            data_version: 0,
+        }))
+    }
+
+    fn set_attr(&mut self, set_attr: &Tsetattr) -> io::Result<Rmessage> {
+        let blocked_ops = P9_SETATTR_MODE | P9_SETATTR_UID | P9_SETATTR_GID;
+        if set_attr.valid & blocked_ops != 0 {
+            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 file = fs::OpenOptions::new().write(true).open(&fid.path)?;
+
+        if set_attr.valid & P9_SETATTR_SIZE != 0 {
+            file.set_len(set_attr.size)?;
+        }
+
+        if set_attr.valid & (P9_SETATTR_ATIME | P9_SETATTR_MTIME) != 0 {
+            let times = [
+                libc::timespec {
+                    tv_sec: set_attr.atime_sec as _,
+                    tv_nsec: if set_attr.valid & P9_SETATTR_ATIME == 0 {
+                        libc::UTIME_OMIT
+                    } else if set_attr.valid & P9_SETATTR_ATIME_SET == 0 {
+                        libc::UTIME_NOW
+                    } else {
+                        set_attr.atime_nsec as _
+                    },
+                },
+                libc::timespec {
+                    tv_sec: set_attr.mtime_sec as _,
+                    tv_nsec: if set_attr.valid & P9_SETATTR_MTIME == 0 {
+                        libc::UTIME_OMIT
+                    } else if set_attr.valid & P9_SETATTR_MTIME_SET == 0 {
+                        libc::UTIME_NOW
+                    } else {
+                        set_attr.mtime_nsec as _
+                    },
+                },
+            ];
+
+            // Safe because file is valid and we have initialized times fully.
+            let ret = unsafe { libc::futimens(file.as_raw_fd(), &times as *const libc::timespec) };
+            if ret < 0 {
+                return Err(io::Error::last_os_error());
+            }
+        }
+
+        // The ctime would have been updated by any of the above operations so we only
+        // need to change it if it was the only option given.
+        if set_attr.valid & P9_SETATTR_CTIME != 0 && set_attr.valid & (!P9_SETATTR_CTIME) == 0 {
+            // Setting -1 as the uid and gid will not actually change anything but will
+            // still update the ctime.
+            let ret = unsafe {
+                libc::fchown(
+                    file.as_raw_fd(),
+                    libc::uid_t::max_value(),
+                    libc::gid_t::max_value(),
+                )
+            };
+            if ret < 0 {
+                return Err(io::Error::last_os_error());
+            }
+        }
+
+        Ok(Rmessage::SetAttr)
+    }
+
+    fn xattr_walk(&mut self, _xattr_walk: &Txattrwalk) -> io::Result<Rmessage> {
+        Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP))
+    }
+
+    fn xattr_create(&mut self, _xattr_create: &Txattrcreate) -> io::Result<Rmessage> {
+        Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP))
+    }
+
+    fn readdir(&mut self, readdir: &Treaddir) -> io::Result<Rmessage> {
+        let fid = self
+            .fids
+            .get_mut(&readdir.fid)
+            .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
+
+        if !fid.metadata.is_dir() {
+            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: qid,
+                    offset: 0, // set below
+                    ty: ty,
+                    name: 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 {
+            tag: 0,
+            msg: Rmessage::Readdir(Rreaddir {
+                data: Data(Vec::new()),
+            }),
+        }.byte_size();
+        let count = min(self.msize - header_size, readdir.count);
+        let mut cursor = Cursor::new(Vec::with_capacity(count as usize));
+
+        loop {
+            let byte_size = if let Some(entry) = entries.peek() {
+                entry.byte_size() as usize
+            } else {
+                // No more entries.
+                break;
+            };
+
+            if cursor.get_ref().capacity() - cursor.get_ref().len() < byte_size {
+                // No more room in the buffer.
+                break;
+            }
+
+            // Safe because we just checked that the iterator contains at least one more item.
+            entries.next().unwrap().encode(&mut cursor)?;
+        }
+
+        Ok(Rmessage::Readdir(Rreaddir {
+            data: Data(cursor.into_inner()),
+        }))
+    }
+
+    fn fsync(&mut self, fsync: &Tfsync) -> io::Result<Rmessage> {
+        let file = self
+            .fids
+            .get(&fsync.fid)
+            .and_then(|fid| fid.file.as_ref())
+            .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
+
+        if fsync.datasync == 0 {
+            file.sync_all()?;
+        } else {
+            file.sync_data()?;
+        }
+        Ok(Rmessage::Fsync)
+    }
+
+    fn lock(&mut self, _lock: &Tlock) -> io::Result<Rmessage> {
+        // File locking is not supported.
+        Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP))
+    }
+    fn get_lock(&mut self, _get_lock: &Tgetlock) -> io::Result<Rmessage> {
+        // File locking is not supported.
+        Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP))
+    }
+
+    fn link(&mut self, link: &Tlink) -> io::Result<Rmessage> {
+        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)?;
+
+        let path = self
+            .fids
+            .get(&link.fid)
+            .map(|fid| &fid.path)
+            .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
+
+        fs::hard_link(path, &newpath)?;
+        Ok(Rmessage::Link)
+    }
+
+    fn mkdir(&mut self, mkdir: &Tmkdir) -> io::Result<Rmessage> {
+        let fid = self
+            .fids
+            .get(&mkdir.dfid)
+            .ok_or_else(|| io::Error::from_raw_os_error(libc::EBADF))?;
+
+        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)?;
+
+        Ok(Rmessage::Mkdir(Rmkdir {
+            qid: metadata_to_qid(&fs::metadata(&newpath)?),
+        }))
+    }
+
+    fn rename_at(&mut self, rename_at: &Trenameat) -> io::Result<Rmessage> {
+        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)?;
+
+        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)?;
+
+        fs::rename(&oldpath, &newpath)?;
+        Ok(Rmessage::RenameAt)
+    }
+
+    fn unlink_at(&mut self, unlink_at: &Tunlinkat) -> io::Result<Rmessage> {
+        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)?;
+
+        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)?;
+        }
+
+        Ok(Rmessage::UnlinkAt)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    // Most of the server implementation is tested via integration tests.
+    use super::*;
+
+    #[test]
+    fn path_joins() {
+        let root = PathBuf::from("/a/b/c");
+        let path = PathBuf::from("/a/b/c/d/e/f");
+
+        assert_eq!(
+            &join_path(path.clone(), "nested", &root).expect("normal"),
+            Path::new("/a/b/c/d/e/f/nested")
+        );
+
+        let p1 = join_path(path.clone(), "..", &root).expect("parent 1");
+        assert_eq!(&p1, Path::new("/a/b/c/d/e/"));
+
+        let p2 = join_path(p1, "..", &root).expect("parent 2");
+        assert_eq!(&p2, Path::new("/a/b/c/d/"));
+
+        let p3 = join_path(p2, "..", &root).expect("parent 3");
+        assert_eq!(&p3, Path::new("/a/b/c/"));
+
+        let p4 = join_path(p3, "..", &root).expect("parent of root");
+        assert_eq!(&p4, Path::new("/a/b/c/"));
+    }
+
+    #[test]
+    fn invalid_joins() {
+        let root = PathBuf::from("/a");
+        let path = PathBuf::from("/a/b");
+
+        join_path(path.clone(), ".", &root).expect_err("current directory");
+        join_path(path.clone(), "c/d/e", &root).expect_err("too many components");
+        join_path(path.clone(), "/c/d/e", &root).expect_err("absolute path");
+    }
+}
diff --git a/vm_tools/p9/wire_format_derive/Cargo.toml b/vm_tools/p9/wire_format_derive/Cargo.toml
new file mode 100644
index 0000000..e556216
--- /dev/null
+++ b/vm_tools/p9/wire_format_derive/Cargo.toml
@@ -0,0 +1,23 @@
+[package]
+name = "wire_format_derive"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+
+[dependencies]
+syn = "0.12.15"
+quote = "0.4.2"
+proc-macro2 = "0.2.3"
+
+[lib]
+proc-macro = true
+path = "wire_format_derive.rs"
+
+[profile.dev]
+# We want some optimization even in dev builds.
+opt-level = 2
+
+[profile.release]
+# We still want debug information in release builds.
+debug = true
+lto = true
+panic = 'abort'
\ No newline at end of file
diff --git a/vm_tools/p9/wire_format_derive/wire_format_derive.rs b/vm_tools/p9/wire_format_derive/wire_format_derive.rs
new file mode 100644
index 0000000..5268b92
--- /dev/null
+++ b/vm_tools/p9/wire_format_derive/wire_format_derive.rs
@@ -0,0 +1,317 @@
+// Copyright 2018 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+//! Derives a 9P wire format encoding for a struct by recursively calling
+//! `WireFormat::encode` or `WireFormat::decode` on the fields of the struct.
+//! This is only intended to be used from within the `p9` crate.
+
+#![recursion_limit = "256"]
+
+extern crate proc_macro2;
+extern crate proc_macro;
+
+#[macro_use]
+extern crate quote;
+
+#[cfg_attr(test, macro_use)]
+extern crate syn;
+
+use proc_macro::TokenStream;
+use proc_macro2::Span;
+use quote::Tokens;
+use syn::{Data, DeriveInput, Fields, Ident};
+use syn::spanned::Spanned;
+
+/// The function that derives the actual implementation.
+#[proc_macro_derive(P9WireFormat)]
+pub fn p9_wire_format(input: TokenStream) -> TokenStream {
+    p9_wire_format_inner(syn::parse(input).unwrap()).into()
+}
+
+fn p9_wire_format_inner(input: DeriveInput) -> Tokens {
+    if !input.generics.params.is_empty() {
+        return quote! {
+            compile_error!("derive(P9WireFormat) does not support generic parameters");
+        };
+    }
+
+    let var = quote!(self);
+    let ident = input.ident;
+    let name = quote!(#ident);
+
+    let call_site = Span::call_site();
+    let import = quote_spanned!(call_site=> use protocol::WireFormat; );
+    let wire_format = quote_spanned!(call_site=> WireFormat);
+
+    let byte_size_impl = byte_size_sum(&input.data, &wire_format, &var);
+    let encode_impl = encode_wire_format(&input.data, &wire_format, &var);
+    let decode_impl = decode_wire_format(&input.data, &wire_format, &name);
+
+    let scope = Ident::from(format!("wire_format_{}", ident).to_lowercase());
+    quote! {
+        mod #scope {
+            extern crate std;
+            use self::std::io;
+            use self::std::result::Result::Ok;
+
+            use super::#name;
+
+            #import
+
+            impl #wire_format for #name {
+                fn byte_size(&#var) -> u32 {
+                    #byte_size_impl
+                }
+
+                fn encode<W: io::Write>(&#var, _writer: &mut W) -> io::Result<()> {
+                    #encode_impl
+                }
+
+                fn decode<R: io::Read>(_reader: &mut R) -> io::Result<Self> {
+                    #decode_impl
+                }
+            }
+        }
+    }
+}
+
+// Generate code that recursively calls byte_size on every field in the struct.
+fn byte_size_sum(data: &Data, wire_format: &Tokens, var: &Tokens) -> Tokens {
+    let def_site = Span::def_site();
+    let call_site = Span::call_site();
+
+    if let Data::Struct(ref data) = *data {
+        if let Fields::Named(ref fields) = data.fields {
+            let fields = fields.named.iter().map(|f| {
+                let name = f.ident;
+                let access = quote_spanned!(call_site=> #var.#name);
+                let span = f.span().resolved_at(def_site);
+                quote_spanned!(span=> #wire_format::byte_size(&#access) )
+            });
+
+            quote! {
+                0 #(+ #fields)*
+            }
+        } else {
+            unimplemented!();
+        }
+    } else {
+        unimplemented!();
+    }
+}
+
+// Generate code that recursively calls encode on every field in the struct.
+fn encode_wire_format(data: &Data, wire_format: &Tokens, var: &Tokens) -> Tokens {
+    let def_site = Span::def_site();
+    let call_site = Span::call_site();
+
+    if let Data::Struct(ref data) = *data {
+        if let Fields::Named(ref fields) = data.fields {
+            let fields = fields.named.iter().map(|f| {
+                let name = f.ident;
+                let access = quote_spanned!(call_site=> #var.#name);
+                let span = f.span().resolved_at(def_site);
+                quote_spanned!(span=> #wire_format::encode(&#access, _writer)?; )
+            });
+
+            quote! {
+                #(#fields)*
+
+                Ok(())
+            }
+        } else {
+            unimplemented!();
+        }
+    } else {
+        unimplemented!();
+    }
+}
+
+// Generate code that recursively calls decode on every field in the struct.
+fn decode_wire_format(data: &Data, wire_format: &Tokens, name: &Tokens) -> Tokens {
+    let def_site = Span::def_site();
+    let call_site = Span::call_site();
+
+    if let Data::Struct(ref data) = *data {
+        if let Fields::Named(ref fields) = data.fields {
+            let values = fields.named.iter().map(|f| {
+                let name = f.ident;
+                let access = quote_spanned!(call_site=> #name);
+                let span = f.span().resolved_at(def_site);
+                quote_spanned!(span=> let #access = #wire_format::decode(_reader)?; )
+            });
+
+            let members = fields.named.iter().map(|f| {
+                let name = f.ident;
+                quote_spanned!(call_site=>
+                    #name: #name,
+                )
+            });
+
+            quote! {
+                #(#values)*
+
+                Ok(#name {
+                    #(#members)*
+                })
+            }
+        } else {
+            unimplemented!();
+        }
+    } else {
+        unimplemented!();
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn byte_size() {
+        let input: DeriveInput = parse_quote! {
+            struct Item {
+                ident: u32,
+                with_underscores: String,
+                other: u8,
+            }
+        };
+
+        let var = quote!(self);
+        let wire_format = quote!(WireFormat);
+        let expected = quote! {
+            0
+                + WireFormat::byte_size(&self.ident)
+                + WireFormat::byte_size(&self.with_underscores)
+                + WireFormat::byte_size(&self.other)
+        };
+
+        assert_eq!(byte_size_sum(&input.data, &wire_format, &var), expected);
+    }
+
+    #[test]
+    fn encode() {
+        let input: DeriveInput = parse_quote! {
+            struct Item {
+                ident: u32,
+                with_underscores: String,
+                other: u8,
+            }
+        };
+
+        let var = quote!(self);
+        let wire_format = quote!(WireFormat);
+        let expected = quote! {
+            WireFormat::encode(&self.ident, _writer)?;
+            WireFormat::encode(&self.with_underscores, _writer)?;
+            WireFormat::encode(&self.other, _writer)?;
+            Ok(())
+        };
+
+        assert_eq!(
+            encode_wire_format(&input.data, &wire_format, &var),
+            expected
+        );
+    }
+
+    #[test]
+    fn decode() {
+        let input: DeriveInput = parse_quote! {
+            struct Item {
+                ident: u32,
+                with_underscores: String,
+                other: u8,
+            }
+        };
+
+        let name = quote!(Item);
+        let wire_format = quote!(WireFormat);
+        let expected = quote! {
+            let ident = WireFormat::decode(_reader)?;
+            let with_underscores = WireFormat::decode(_reader)?;
+            let other = WireFormat::decode(_reader)?;
+            Ok(Item {
+                ident: ident,
+                with_underscores: with_underscores,
+                other: other,
+            })
+        };
+
+        assert_eq!(
+            decode_wire_format(&input.data, &wire_format, &name),
+            expected
+        );
+    }
+
+    #[test]
+    fn end_to_end() {
+        let input: DeriveInput = parse_quote! {
+            struct Niijima_先輩 {
+                a: u8,
+                b: u16,
+                c: u32,
+                d: u64,
+                e: String,
+                f: Vec<String>,
+                g: Nested,
+            }
+        };
+
+        let expected = quote! {
+            mod wire_format_niijima_先輩 {
+                extern crate std;
+                use self::std::io;
+                use self::std::result::Result::Ok;
+
+                use super::Niijima_先輩;
+
+                use protocol::WireFormat;
+
+                impl WireFormat for Niijima_先輩 {
+                    fn byte_size(&self) -> u32 {
+                        0
+                        + WireFormat::byte_size(&self.a)
+                        + WireFormat::byte_size(&self.b)
+                        + WireFormat::byte_size(&self.c)
+                        + WireFormat::byte_size(&self.d)
+                        + WireFormat::byte_size(&self.e)
+                        + WireFormat::byte_size(&self.f)
+                        + WireFormat::byte_size(&self.g)
+                    }
+
+                    fn encode<W: io::Write>(&self, _writer: &mut W) -> io::Result<()> {
+                        WireFormat::encode(&self.a, _writer)?;
+                        WireFormat::encode(&self.b, _writer)?;
+                        WireFormat::encode(&self.c, _writer)?;
+                        WireFormat::encode(&self.d, _writer)?;
+                        WireFormat::encode(&self.e, _writer)?;
+                        WireFormat::encode(&self.f, _writer)?;
+                        WireFormat::encode(&self.g, _writer)?;
+                        Ok(())
+                    }
+                    fn decode<R: io::Read>(_reader: &mut R) -> io::Result<Self> {
+                        let a = WireFormat::decode(_reader)?;
+                        let b = WireFormat::decode(_reader)?;
+                        let c = WireFormat::decode(_reader)?;
+                        let d = WireFormat::decode(_reader)?;
+                        let e = WireFormat::decode(_reader)?;
+                        let f = WireFormat::decode(_reader)?;
+                        let g = WireFormat::decode(_reader)?;
+                        Ok(Niijima_先輩 {
+                            a: a,
+                            b: b,
+                            c: c,
+                            d: d,
+                            e: e,
+                            f: f,
+                            g: g,
+                        })
+                    }
+                }
+            }
+        };
+
+        assert_eq!(p9_wire_format_inner(input), expected);
+    }
+}