summary refs log tree commit diff
diff options
context:
space:
mode:
authorAlyssa Ross <hi@alyssa.is>2020-03-25 08:38:01 +0000
committerAlyssa Ross <hi@alyssa.is>2020-06-15 09:37:12 +0000
commitb6549a605935e29ab0ae4291737f8b0158bca1fb (patch)
tree7f4242993ce003cb787b242a264e3b8ea47e3430
parent2885f9ca1a79d30421deeb025e92ae0118fc6d3a (diff)
downloadcrosvm-b6549a605935e29ab0ae4291737f8b0158bca1fb.tar
crosvm-b6549a605935e29ab0ae4291737f8b0158bca1fb.tar.gz
crosvm-b6549a605935e29ab0ae4291737f8b0158bca1fb.tar.bz2
crosvm-b6549a605935e29ab0ae4291737f8b0158bca1fb.tar.lz
crosvm-b6549a605935e29ab0ae4291737f8b0158bca1fb.tar.xz
crosvm-b6549a605935e29ab0ae4291737f8b0158bca1fb.tar.zst
crosvm-b6549a605935e29ab0ae4291737f8b0158bca1fb.zip
recursive deserialization
-rw-r--r--Cargo.lock3
-rw-r--r--devices/Cargo.toml1
-rw-r--r--devices/src/lib.rs7
-rw-r--r--devices/src/pci/pci_root.rs15
-rw-r--r--devices/src/virtio/controller.rs316
-rw-r--r--devices/src/virtio/queue.rs5
-rw-r--r--msg_socket2/Cargo.toml9
-rw-r--r--msg_socket2/LICENSE-APACHE201
-rw-r--r--msg_socket2/LICENSE-MIT23
-rw-r--r--msg_socket2/README12
-rw-r--r--msg_socket2/src/de.rs731
-rw-r--r--msg_socket2/src/error.rs3
-rw-r--r--msg_socket2/src/fd.rs59
-rw-r--r--msg_socket2/src/lib.rs55
-rw-r--r--msg_socket2/src/ser.rs147
-rw-r--r--msg_socket2/src/socket.rs18
-rw-r--r--msg_socket2/tests/round_trip.rs79
-rw-r--r--vm_control/Cargo.toml1
-rw-r--r--vm_control/src/lib.rs16
19 files changed, 1359 insertions, 342 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 9ad31d6..8e079d2 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -206,7 +206,6 @@ dependencies = [
  "msg_on_socket_derive 0.1.0",
  "msg_socket 0.1.0",
  "msg_socket2 0.1.0",
- "msg_socket2_derive 0.1.0",
  "net_sys 0.1.0",
  "net_util 0.1.0",
  "p9 0.1.0",
@@ -505,6 +504,7 @@ name = "msg_socket2"
 version = "0.1.0"
 dependencies = [
  "bincode 1.3.0 (git+https://github.com/alyssais/bincode?branch=from_slice)",
+ "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
  "msg_socket2_derive 0.1.0",
  "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
  "sys_util 0.1.0",
@@ -846,7 +846,6 @@ dependencies = [
  "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
  "msg_socket 0.1.0",
  "msg_socket2 0.1.0",
- "msg_socket2_derive 0.1.0",
  "resources 0.1.0",
  "sys_util 0.1.0",
 ]
diff --git a/devices/Cargo.toml b/devices/Cargo.toml
index 4d6c4d8..830cb86 100644
--- a/devices/Cargo.toml
+++ b/devices/Cargo.toml
@@ -35,7 +35,6 @@ linux_input_sys = { path = "../linux_input_sys" }
 msg_on_socket_derive = { path = "../msg_socket/msg_on_socket_derive" }
 msg_socket = { path = "../msg_socket" }
 msg_socket2 = { path = "../msg_socket2" }
-msg_socket2_derive = { path = "../msg_socket2/derive" }
 net_sys = { path = "../net_sys" }
 net_util = { path = "../net_util" }
 p9 = { path = "../p9" }
diff --git a/devices/src/lib.rs b/devices/src/lib.rs
index 7df9c62..ce57e61 100644
--- a/devices/src/lib.rs
+++ b/devices/src/lib.rs
@@ -49,12 +49,9 @@ pub use self::usb::xhci::xhci_controller::XhciController;
 pub use self::vfio::{VfioContainer, VfioDevice};
 pub use self::virtio::VirtioPciDevice;
 
-use msg_socket::MsgOnSocket;
-use serde::{Deserialize, Serialize};
+use msg_socket2::{Deserialize, DeserializeWithFds, Serialize, SerializeWithFds};
 
-use msg_socket2_derive::SerializeWithFds;
-
-#[derive(Clone, Copy, Debug, MsgOnSocket, Serialize, SerializeWithFds, Deserialize)]
+#[derive(Clone, Copy, Debug, Serialize, SerializeWithFds, Deserialize, DeserializeWithFds)]
 #[msg_socket2(strategy = "serde")]
 pub struct MemoryParams {
     /// Physical memory size in bytes for the VM.
diff --git a/devices/src/pci/pci_root.rs b/devices/src/pci/pci_root.rs
index 76f9d82..d100941 100644
--- a/devices/src/pci/pci_root.rs
+++ b/devices/src/pci/pci_root.rs
@@ -8,8 +8,7 @@ use std::fmt::{self, Display};
 use std::os::unix::io::RawFd;
 use std::sync::Arc;
 
-use msg_socket2_derive::SerializeWithFds;
-use serde::{Deserialize, Serialize};
+use msg_socket2::{Deserialize, DeserializeWithFds, Serialize, SerializeWithFds};
 use sync::Mutex;
 
 use crate::pci::pci_configuration::{
@@ -45,7 +44,17 @@ impl PciDevice for PciRootConfiguration {
 
 /// PCI Device Address, AKA Bus:Device.Function
 #[derive(
-    Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize, SerializeWithFds,
+    Clone,
+    Copy,
+    Debug,
+    Deserialize,
+    DeserializeWithFds,
+    Eq,
+    Ord,
+    PartialEq,
+    PartialOrd,
+    Serialize,
+    SerializeWithFds,
 )]
 #[msg_socket2(strategy = "serde")]
 pub struct PciAddress {
diff --git a/devices/src/virtio/controller.rs b/devices/src/virtio/controller.rs
index 0a59072..644ece1 100644
--- a/devices/src/virtio/controller.rs
+++ b/devices/src/virtio/controller.rs
@@ -28,8 +28,6 @@
 //! the virtio queue, and routing messages in and out of `WlState`. Possible events include the kill
 //! event, available descriptors on the `in` or `out` queue, and incoming data on any vfd's socket.
 
-use std::os::unix::prelude::*;
-
 use std::collections::BTreeMap as Map;
 use std::fmt::{self, Formatter};
 use std::os::unix::io::{AsRawFd, RawFd};
@@ -38,10 +36,9 @@ use std::sync::Arc;
 use std::thread;
 
 use msg_socket::{MsgReceiver, MsgSocket};
+use msg_socket2::de::{EnumAccessWithFds, SeqAccessWithFds, VariantAccessWithFds, VisitorWithFds};
 use msg_socket2::ser::{SerializeStructVariantFds, SerializeTupleVariantFds};
 use msg_socket2::{DeserializeWithFds, DeserializerWithFds, FdSerializer, SerializeWithFds};
-use msg_socket2_derive::SerializeWithFds;
-use serde::de::{Deserializer, EnumAccess, SeqAccess, VariantAccess};
 use serde::ser::{SerializeStructVariant, SerializeTupleVariant, Serializer};
 use serde::{Deserialize, Serialize};
 use sys_util::net::UnixSeqpacket;
@@ -136,11 +133,9 @@ impl SerializeWithFds for Request {
                 vm_socket: _,
                 memory_params,
             } => {
-                let mut sv = serializer.serialize_struct_variant("Request", 0, "Create", 1)?;
-
-                sv.skip_field("vm_socket")?;
+                let mut sv = serializer.serialize_struct_variant("Request", 0, "Create", 2)?;
+                sv.serialize_field("vm_socket", &())?;
                 sv.serialize_field("memory_params", memory_params)?;
-
                 sv.end()
             }
 
@@ -180,16 +175,14 @@ impl SerializeWithFds for Request {
                 in_queue_evt: _,
                 out_queue_evt: _,
             } => {
-                let mut sv = serializer.serialize_struct_variant("Request", 8, "Activate", 2)?;
-
-                sv.skip_field("shm")?;
-                sv.skip_field("interrupt")?;
-                sv.skip_field("interrupt_resample_evt")?;
+                let mut sv = serializer.serialize_struct_variant("Request", 8, "Activate", 7)?;
+                sv.serialize_field("shm", &())?;
+                sv.serialize_field("interrupt", &())?;
+                sv.serialize_field("interrupt_resample_evt", &())?;
                 sv.serialize_field("in_queue", in_queue)?;
                 sv.serialize_field("out_queue", out_queue)?;
-                sv.skip_field("in_queue_evt")?;
-                sv.skip_field("out_queue_evt")?;
-
+                sv.serialize_field("in_queue_evt", &())?;
+                sv.serialize_field("out_queue_evt", &())?;
                 sv.end()
             }
 
@@ -284,26 +277,20 @@ impl SerializeWithFds for Request {
 }
 
 impl<'de> DeserializeWithFds<'de> for Request {
-    fn deserialize<I, De>(deserializer: DeserializerWithFds<I, De>) -> Result<Self, De::Error>
-    where
-        I: Iterator<Item = RawFd>,
-        De: Deserializer<'de>,
-    {
-        struct Visitor<'iter, Iter> {
-            fds: &'iter mut Iter,
-        }
-
-        impl<'iter, 'de, Iter> serde::de::Visitor<'de> for Visitor<'iter, Iter>
-        where
-            Iter: Iterator<Item = RawFd>,
-        {
+    fn deserialize<D: DeserializerWithFds<'de>>(deserializer: D) -> Result<Self, D::Error> {
+        struct Visitor;
+
+        impl<'de> VisitorWithFds<'de> for Visitor {
             type Value = Request;
 
             fn expecting(&self, f: &mut Formatter) -> fmt::Result {
                 write!(f, "enum Request")
             }
 
-            fn visit_enum<A: EnumAccess<'de>>(self, data: A) -> Result<Self::Value, A::Error> {
+            fn visit_enum<A: EnumAccessWithFds<'de>>(
+                self,
+                data: A,
+            ) -> Result<Self::Value, A::Error> {
                 #[derive(Debug, Deserialize)]
                 enum Variant {
                     Create,
@@ -323,54 +310,38 @@ impl<'de> DeserializeWithFds<'de> for Request {
 
                 match data.variant()? {
                     (Variant::Create, variant) => {
-                        struct Visitor<'iter, Iter> {
-                            fds: &'iter mut Iter,
-                        }
+                        struct Visitor;
 
-                        impl<'iter, 'de, Iter> serde::de::Visitor<'de> for Visitor<'iter, Iter>
-                        where
-                            Iter: Iterator<Item = RawFd>,
-                        {
+                        impl<'de> VisitorWithFds<'de> for Visitor {
                             type Value = Request;
 
                             fn expecting(&self, f: &mut Formatter) -> fmt::Result {
                                 write!(f, "struct variant Request::Create")
                             }
 
-                            fn visit_seq<A: SeqAccess<'de>>(
+                            fn visit_seq<A: SeqAccessWithFds<'de>>(
                                 self,
                                 mut seq: A,
                             ) -> Result<Request, A::Error> {
                                 use serde::de::Error;
 
+                                fn too_short<E: Error>(len: usize) -> E {
+                                    E::invalid_length(
+                                        len,
+                                        &"struct variant Request::Create with 2 elements",
+                                    )
+                                }
+
                                 Ok(Request::Create {
-                                    vm_socket: match self.fds.next() {
-                                        Some(vm_socket) => MaybeOwnedFd::Owned(unsafe {
-                                            UnixSeqpacket::from_raw_fd(vm_socket)
-                                        }),
-                                        None => {
-                                            return Err(Error::invalid_length(
-                                                0,
-                                                &"struct variant Request::Create with 2 elements",
-                                            ))
-                                        }
-                                    },
-
-                                    memory_params: match seq.next_element()? {
-                                        Some(memory_params) => memory_params,
-                                        None => {
-                                            return Err(Error::invalid_length(
-                                                1,
-                                                &"struct variant Request::Create with 2 elements",
-                                            ))
-                                        }
-                                    },
+                                    vm_socket: seq.next_element()?.ok_or_else(|| too_short(0))?,
+                                    memory_params: seq
+                                        .next_element()?
+                                        .ok_or_else(|| too_short(1))?,
                                 })
                             }
                         }
 
-                        let visitor = Visitor { fds: self.fds };
-                        variant.struct_variant(&["memory_params"], visitor)
+                        variant.struct_variant(&["vm_socket", "memory_params"], Visitor)
                     }
 
                     (Variant::DebugLabel, variant) => {
@@ -400,35 +371,29 @@ impl<'de> DeserializeWithFds<'de> for Request {
                     (Variant::ReadConfig, variant) => {
                         struct Visitor;
 
-                        impl<'de> serde::de::Visitor<'de> for Visitor {
+                        impl<'de> VisitorWithFds<'de> for Visitor {
                             type Value = Request;
 
                             fn expecting(&self, f: &mut Formatter) -> fmt::Result {
                                 write!(f, "struct variant Request::ReadConfig")
                             }
 
-                            fn visit_seq<A: SeqAccess<'de>>(
+                            fn visit_seq<A: SeqAccessWithFds<'de>>(
                                 self,
                                 mut seq: A,
                             ) -> Result<Request, A::Error> {
                                 use serde::de::Error;
 
+                                fn too_short<E: Error>(len: usize) -> E {
+                                    E::invalid_length(
+                                        len,
+                                        &"struct variant Request::ReadConfig with 2 elements",
+                                    )
+                                }
+
                                 Ok(Request::ReadConfig {
-                                    offset: match seq.next_element()? {
-                                        Some(offset) => offset,
-                                        None => return Err(Error::invalid_length(
-                                            0,
-                                            &"struct variant Request::ReadConfig with 2 elements",
-                                        )),
-                                    },
-
-                                    len: match seq.next_element()? {
-                                        Some(len) => len,
-                                        None => return Err(Error::invalid_length(
-                                            1,
-                                            &"struct variant Request::ReadConfig with 2 elements",
-                                        )),
-                                    },
+                                    offset: seq.next_element()?.ok_or_else(|| too_short(0))?,
+                                    len: seq.next_element()?.ok_or_else(|| too_short(1))?,
                                 })
                             }
                         }
@@ -439,35 +404,29 @@ impl<'de> DeserializeWithFds<'de> for Request {
                     (Variant::WriteConfig, variant) => {
                         struct Visitor;
 
-                        impl<'de> serde::de::Visitor<'de> for Visitor {
+                        impl<'de> VisitorWithFds<'de> for Visitor {
                             type Value = Request;
 
                             fn expecting(&self, f: &mut Formatter) -> fmt::Result {
                                 write!(f, "struct variant Request::WriteConfig")
                             }
 
-                            fn visit_seq<A: SeqAccess<'de>>(
+                            fn visit_seq<A: SeqAccessWithFds<'de>>(
                                 self,
                                 mut seq: A,
                             ) -> Result<Request, A::Error> {
                                 use serde::de::Error;
 
+                                fn too_short<E: Error>(len: usize) -> E {
+                                    E::invalid_length(
+                                        len,
+                                        &"struct variant Request::WriteConfig with 2 elements",
+                                    )
+                                }
+
                                 Ok(Request::WriteConfig {
-                                    offset: match seq.next_element()? {
-                                        Some(offset) => offset,
-                                        None => return Err(Error::invalid_length(
-                                            0,
-                                            &"struct variant Request::WriteConfig with 2 elements",
-                                        )),
-                                    },
-
-                                    data: match seq.next_element()? {
-                                        Some(data) => data,
-                                        None => return Err(Error::invalid_length(
-                                            1,
-                                            &"struct variant Request::WriteConfig with 2 elements",
-                                        )),
-                                    },
+                                    offset: seq.next_element()?.ok_or_else(|| too_short(0))?,
+                                    data: seq.next_element()?.ok_or_else(|| too_short(1))?,
                                 })
                             }
                         }
@@ -476,114 +435,58 @@ impl<'de> DeserializeWithFds<'de> for Request {
                     }
 
                     (Variant::Activate, variant) => {
-                        struct Visitor<'iter, Iter> {
-                            fds: &'iter mut Iter,
-                        }
+                        struct Visitor;
 
-                        impl<'iter, 'de, Iter> serde::de::Visitor<'de> for Visitor<'iter, Iter>
-                        where
-                            Iter: Iterator<Item = RawFd>,
-                        {
+                        impl<'de> VisitorWithFds<'de> for Visitor {
                             type Value = Request;
 
                             fn expecting(&self, f: &mut Formatter) -> fmt::Result {
                                 write!(f, "struct variant Request::Activate")
                             }
 
-                            fn visit_seq<A: SeqAccess<'de>>(
+                            fn visit_seq<A: SeqAccessWithFds<'de>>(
                                 self,
                                 mut seq: A,
                             ) -> Result<Request, A::Error> {
                                 use serde::de::Error;
 
+                                fn too_short<E: Error>(len: usize) -> E {
+                                    E::invalid_length(
+                                        len,
+                                        &"struct variant Request::Activate with 7 elements",
+                                    )
+                                }
+
                                 Ok(Request::Activate {
-                                    shm: match self.fds.next() {
-                                        Some(shm) => MaybeOwnedFd::Owned(unsafe {
-                                            FromRawFd::from_raw_fd(shm)
-                                        }),
-                                        None => {
-                                            return Err(Error::invalid_length(
-                                                0,
-                                                &"struct variant Request::Activate with 7 elements",
-                                            ))
-                                        }
-                                    },
-
-                                    interrupt: match self.fds.next() {
-                                        Some(interrupt) => MaybeOwnedFd::Owned(unsafe {
-                                            UnixSeqpacket::from_raw_fd(interrupt)
-                                        }),
-                                        None => {
-                                            return Err(Error::invalid_length(
-                                                1,
-                                                &"struct variant Request::Activate with 7 elements",
-                                            ))
-                                        }
-                                    },
-
-                                    interrupt_resample_evt: match self.fds.next() {
-                                        Some(interrupt_resample_evt) => {
-                                            MaybeOwnedFd::Owned(unsafe {
-                                                EventFd::from_raw_fd(interrupt_resample_evt)
-                                            })
-                                        }
-                                        None => {
-                                            return Err(Error::invalid_length(
-                                                2,
-                                                &"struct variant Request::Activate with 7 elements",
-                                            ))
-                                        }
-                                    },
-
-                                    in_queue: match seq.next_element()? {
-                                        Some(in_queue) => in_queue,
-                                        None => {
-                                            return Err(Error::invalid_length(
-                                                3,
-                                                &"struct variant Request::Activate with 7 elements",
-                                            ))
-                                        }
-                                    },
-
-                                    out_queue: match seq.next_element()? {
-                                        Some(out_queue) => out_queue,
-                                        None => {
-                                            return Err(Error::invalid_length(
-                                                4,
-                                                &"struct variant Request::Activate with 7 elements",
-                                            ))
-                                        }
-                                    },
-
-                                    in_queue_evt: match self.fds.next() {
-                                        Some(in_queue_evt) => MaybeOwnedFd::Owned(unsafe {
-                                            EventFd::from_raw_fd(in_queue_evt)
-                                        }),
-                                        None => {
-                                            return Err(Error::invalid_length(
-                                                5,
-                                                &"struct variant Request::Activate with 7 elements",
-                                            ))
-                                        }
-                                    },
-
-                                    out_queue_evt: match self.fds.next() {
-                                        Some(out_queue_evt) => MaybeOwnedFd::Owned(unsafe {
-                                            EventFd::from_raw_fd(out_queue_evt)
-                                        }),
-                                        None => {
-                                            return Err(Error::invalid_length(
-                                                6,
-                                                &"struct variant Request::Activate with 7 elements",
-                                            ))
-                                        }
-                                    },
+                                    shm: seq.next_element()?.ok_or_else(|| too_short(0))?,
+                                    interrupt: seq.next_element()?.ok_or_else(|| too_short(1))?,
+                                    interrupt_resample_evt: seq
+                                        .next_element()?
+                                        .ok_or_else(|| too_short(2))?,
+                                    in_queue: seq.next_element()?.ok_or_else(|| too_short(3))?,
+                                    out_queue: seq.next_element()?.ok_or_else(|| too_short(4))?,
+                                    in_queue_evt: seq
+                                        .next_element()?
+                                        .ok_or_else(|| too_short(5))?,
+                                    out_queue_evt: seq
+                                        .next_element()?
+                                        .ok_or_else(|| too_short(6))?,
                                 })
                             }
                         }
 
-                        let visitor = Visitor { fds: self.fds };
-                        variant.struct_variant(&["in_queue", "out_queue"], visitor)
+                        variant.struct_variant(
+                            &[
+                                "shm",
+                                "interrupt",
+                                "interrupt_resample_evt",
+                                "in_queue",
+                                "out_queue",
+                                "in_queue_evt",
+                                "out_queue_evt",
+                            ],
+                            Visitor,
+                        )
                     }
 
                     (Variant::Reset, variant) => {
@@ -594,28 +497,29 @@ impl<'de> DeserializeWithFds<'de> for Request {
                     (Variant::GetDeviceBars, variant) => {
                         struct Visitor;
 
-                        impl<'de> serde::de::Visitor<'de> for Visitor {
+                        impl<'de> VisitorWithFds<'de> for Visitor {
                             type Value = Request;
 
                             fn expecting(&self, f: &mut Formatter) -> fmt::Result {
                                 write!(f, "struct variant Request::GetDeviceBars")
                             }
 
-                            fn visit_seq<A: SeqAccess<'de>>(
+                            fn visit_seq<A: SeqAccessWithFds<'de>>(
                                 self,
                                 mut seq: A,
                             ) -> Result<Request, A::Error> {
                                 use serde::de::Error;
 
-                                Ok(Request::GetDeviceBars(match seq.next_element()? {
-                                    Some(address) => address,
-                                    None => {
-                                        return Err(Error::invalid_length(
-                                            0,
-                                            &"struct variant Request::GetDeviceBars with 1 element",
-                                        ))
-                                    }
-                                }))
+                                fn too_short<E: Error>(len: usize) -> E {
+                                    E::invalid_length(
+                                        len,
+                                        &"struct variant Request::GetDeviceBars with 2 elements",
+                                    )
+                                }
+
+                                Ok(Request::GetDeviceBars(
+                                    seq.next_element()?.ok_or_else(|| too_short(0))?,
+                                ))
                             }
                         }
 
@@ -635,11 +539,6 @@ impl<'de> DeserializeWithFds<'de> for Request {
             }
         }
 
-        let DeserializerWithFds {
-            mut fds,
-            deserializer,
-        } = deserializer;
-        let visitor = Visitor { fds: &mut fds };
         deserializer.deserialize_enum(
             "Request",
             &[
@@ -657,12 +556,12 @@ impl<'de> DeserializeWithFds<'de> for Request {
                 "GetDeviceCaps",
                 "Kill",
             ],
-            visitor,
+            Visitor,
         )
     }
 }
 
-#[derive(Debug, Deserialize, Serialize, SerializeWithFds)]
+#[derive(Debug, Deserialize, DeserializeWithFds, Serialize, SerializeWithFds)]
 #[msg_socket2(strategy = "serde")]
 pub enum Response {
     DebugLabel(String),
@@ -676,15 +575,6 @@ pub enum Response {
     Kill,
 }
 
-impl<'de> DeserializeWithFds<'de> for Response {
-    fn deserialize<I, De>(deserializer: DeserializerWithFds<I, De>) -> Result<Self, De::Error>
-    where
-        De: Deserializer<'de>,
-    {
-        Deserialize::deserialize(deserializer.deserializer)
-    }
-}
-
 type Socket = msg_socket2::Socket<Request, Response>;
 
 // TODO: support arbitrary number of queues
diff --git a/devices/src/virtio/queue.rs b/devices/src/virtio/queue.rs
index f2310fa..c4c5217 100644
--- a/devices/src/virtio/queue.rs
+++ b/devices/src/virtio/queue.rs
@@ -6,8 +6,7 @@ use std::cmp::min;
 use std::num::Wrapping;
 use std::sync::atomic::{fence, Ordering};
 
-use msg_socket::MsgOnSocket;
-use msg_socket2_derive::SerializeWithFds;
+use msg_socket2::{DeserializeWithFds, SerializeWithFds};
 use sys_util::{error, GuestAddress, GuestMemory};
 use virtio_sys::virtio_ring::VIRTIO_RING_F_EVENT_IDX;
 
@@ -203,7 +202,7 @@ impl<'a, 'b> Iterator for AvailIter<'a, 'b> {
 
 use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, MsgOnSocket, Serialize, SerializeWithFds, Deserialize)]
+#[derive(Clone, Debug, Serialize, SerializeWithFds, Deserialize, DeserializeWithFds)]
 #[msg_socket2(strategy = "serde")]
 /// A virtio queue's parameters.
 pub struct Queue {
diff --git a/msg_socket2/Cargo.toml b/msg_socket2/Cargo.toml
index 7a7cf73..1662ab3 100644
--- a/msg_socket2/Cargo.toml
+++ b/msg_socket2/Cargo.toml
@@ -1,12 +1,19 @@
+# SPDX-License-Identifier: MIT OR Apache-2.0
+# Copyright 2020, Alyssa Ross
+
 [package]
 name = "msg_socket2"
 version = "0.1.0"
 authors = ["Alyssa Ross <hi@alyssa.is>"]
+license = "MIT OR Apache-2.0"
 edition = "2018"
+description = "Send messages over Unix domain sockets, including file descriptors"
+readme = "README"
 
 [dependencies]
+libc = "*"
 msg_socket2_derive = { path = "derive" }
-serde = "1.0.104"
+serde = { version = "1.0.104" }
 sys_util = { path = "../sys_util" }
 
 bincode = { git = "https://github.com/alyssais/bincode", branch = "from_slice" }
diff --git a/msg_socket2/LICENSE-APACHE b/msg_socket2/LICENSE-APACHE
new file mode 100644
index 0000000..16fe87b
--- /dev/null
+++ b/msg_socket2/LICENSE-APACHE
@@ -0,0 +1,201 @@
+                              Apache License
+                        Version 2.0, January 2004
+                     http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+   "License" shall mean the terms and conditions for use, reproduction,
+   and distribution as defined by Sections 1 through 9 of this document.
+
+   "Licensor" shall mean the copyright owner or entity authorized by
+   the copyright owner that is granting the License.
+
+   "Legal Entity" shall mean the union of the acting entity and all
+   other entities that control, are controlled by, or are under common
+   control with that entity. For the purposes of this definition,
+   "control" means (i) the power, direct or indirect, to cause the
+   direction or management of such entity, whether by contract or
+   otherwise, or (ii) ownership of fifty percent (50%) or more of the
+   outstanding shares, or (iii) beneficial ownership of such entity.
+
+   "You" (or "Your") shall mean an individual or Legal Entity
+   exercising permissions granted by this License.
+
+   "Source" form shall mean the preferred form for making modifications,
+   including but not limited to software source code, documentation
+   source, and configuration files.
+
+   "Object" form shall mean any form resulting from mechanical
+   transformation or translation of a Source form, including but
+   not limited to compiled object code, generated documentation,
+   and conversions to other media types.
+
+   "Work" shall mean the work of authorship, whether in Source or
+   Object form, made available under the License, as indicated by a
+   copyright notice that is included in or attached to the work
+   (an example is provided in the Appendix below).
+
+   "Derivative Works" shall mean any work, whether in Source or Object
+   form, that is based on (or derived from) the Work and for which the
+   editorial revisions, annotations, elaborations, or other modifications
+   represent, as a whole, an original work of authorship. For the purposes
+   of this License, Derivative Works shall not include works that remain
+   separable from, or merely link (or bind by name) to the interfaces of,
+   the Work and Derivative Works thereof.
+
+   "Contribution" shall mean any work of authorship, including
+   the original version of the Work and any modifications or additions
+   to that Work or Derivative Works thereof, that is intentionally
+   submitted to Licensor for inclusion in the Work by the copyright owner
+   or by an individual or Legal Entity authorized to submit on behalf of
+   the copyright owner. For the purposes of this definition, "submitted"
+   means any form of electronic, verbal, or written communication sent
+   to the Licensor or its representatives, including but not limited to
+   communication on electronic mailing lists, source code control systems,
+   and issue tracking systems that are managed by, or on behalf of, the
+   Licensor for the purpose of discussing and improving the Work, but
+   excluding communication that is conspicuously marked or otherwise
+   designated in writing by the copyright owner as "Not a Contribution."
+
+   "Contributor" shall mean Licensor and any individual or Legal Entity
+   on behalf of whom a Contribution has been received by Licensor and
+   subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   copyright license to reproduce, prepare Derivative Works of,
+   publicly display, publicly perform, sublicense, and distribute the
+   Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   (except as stated in this section) patent license to make, have made,
+   use, offer to sell, sell, import, and otherwise transfer the Work,
+   where such license applies only to those patent claims licensable
+   by such Contributor that are necessarily infringed by their
+   Contribution(s) alone or by combination of their Contribution(s)
+   with the Work to which such Contribution(s) was submitted. If You
+   institute patent litigation against any entity (including a
+   cross-claim or counterclaim in a lawsuit) alleging that the Work
+   or a Contribution incorporated within the Work constitutes direct
+   or contributory patent infringement, then any patent licenses
+   granted to You under this License for that Work shall terminate
+   as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+   Work or Derivative Works thereof in any medium, with or without
+   modifications, and in Source or Object form, provided that You
+   meet the following conditions:
+
+   (a) You must give any other recipients of the Work or
+       Derivative Works a copy of this License; and
+
+   (b) You must cause any modified files to carry prominent notices
+       stating that You changed the files; and
+
+   (c) You must retain, in the Source form of any Derivative Works
+       that You distribute, all copyright, patent, trademark, and
+       attribution notices from the Source form of the Work,
+       excluding those notices that do not pertain to any part of
+       the Derivative Works; and
+
+   (d) If the Work includes a "NOTICE" text file as part of its
+       distribution, then any Derivative Works that You distribute must
+       include a readable copy of the attribution notices contained
+       within such NOTICE file, excluding those notices that do not
+       pertain to any part of the Derivative Works, in at least one
+       of the following places: within a NOTICE text file distributed
+       as part of the Derivative Works; within the Source form or
+       documentation, if provided along with the Derivative Works; or,
+       within a display generated by the Derivative Works, if and
+       wherever such third-party notices normally appear. The contents
+       of the NOTICE file are for informational purposes only and
+       do not modify the License. You may add Your own attribution
+       notices within Derivative Works that You distribute, alongside
+       or as an addendum to the NOTICE text from the Work, provided
+       that such additional attribution notices cannot be construed
+       as modifying the License.
+
+   You may add Your own copyright statement to Your modifications and
+   may provide additional or different license terms and conditions
+   for use, reproduction, or distribution of Your modifications, or
+   for any such Derivative Works as a whole, provided Your use,
+   reproduction, and distribution of the Work otherwise complies with
+   the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+   any Contribution intentionally submitted for inclusion in the Work
+   by You to the Licensor shall be under the terms and conditions of
+   this License, without any additional terms or conditions.
+   Notwithstanding the above, nothing herein shall supersede or modify
+   the terms of any separate license agreement you may have executed
+   with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+   names, trademarks, service marks, or product names of the Licensor,
+   except as required for reasonable and customary use in describing the
+   origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+   agreed to in writing, Licensor provides the Work (and each
+   Contributor provides its Contributions) on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+   implied, including, without limitation, any warranties or conditions
+   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+   PARTICULAR PURPOSE. You are solely responsible for determining the
+   appropriateness of using or redistributing the Work and assume any
+   risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+   whether in tort (including negligence), contract, or otherwise,
+   unless required by applicable law (such as deliberate and grossly
+   negligent acts) or agreed to in writing, shall any Contributor be
+   liable to You for damages, including any direct, indirect, special,
+   incidental, or consequential damages of any character arising as a
+   result of this License or out of the use or inability to use the
+   Work (including but not limited to damages for loss of goodwill,
+   work stoppage, computer failure or malfunction, or any and all
+   other commercial damages or losses), even if such Contributor
+   has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+   the Work or Derivative Works thereof, You may choose to offer,
+   and charge a fee for, acceptance of support, warranty, indemnity,
+   or other liability obligations and/or rights consistent with this
+   License. However, in accepting such obligations, You may act only
+   on Your own behalf and on Your sole responsibility, not on behalf
+   of any other Contributor, and only if You agree to indemnify,
+   defend, and hold each Contributor harmless for any liability
+   incurred by, or claims asserted against, such Contributor by reason
+   of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+   To apply the Apache License to your work, attach the following
+   boilerplate notice, with the fields enclosed by brackets "[]"
+   replaced with your own identifying information. (Don't include
+   the brackets!)  The text should be enclosed in the appropriate
+   comment syntax for the file format. We also recommend that a
+   file or class name and description of purpose be included on the
+   same "printed page" as the copyright notice for easier
+   identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/msg_socket2/LICENSE-MIT b/msg_socket2/LICENSE-MIT
new file mode 100644
index 0000000..31aa793
--- /dev/null
+++ b/msg_socket2/LICENSE-MIT
@@ -0,0 +1,23 @@
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/msg_socket2/README b/msg_socket2/README
new file mode 100644
index 0000000..96ae145
--- /dev/null
+++ b/msg_socket2/README
@@ -0,0 +1,12 @@
+msg_socket2
+===========
+
+A library for sending messages over Unix domain sockets, including
+file descriptors.
+
+HTML usage documentation can be generated with:
+
+	cargo doc
+
+Licensed under either of Apache License (LICENSE-APACHE), Version 2.0
+or MIT license (LICENSE-MIT) at your option.
diff --git a/msg_socket2/src/de.rs b/msg_socket2/src/de.rs
index 1d3a9e1..0dec746 100644
--- a/msg_socket2/src/de.rs
+++ b/msg_socket2/src/de.rs
@@ -1,21 +1,730 @@
-use serde::Deserializer;
-use std::os::unix::prelude::*;
+// SPDX-License-Identifier: MIT OR Apache-2.0
+// Copyright 2020, Alyssa Ross
+//
+// Portions Copyright Erick Tryzelaar and David Tolnay.
+// Licensed under either of Apache License, Version 2.0
+// or MIT license at your option.
 
+//! Data structure deserialization framework for Unix domain sockets.
+//!
+//! Much like `serde::de`, except receiving file descriptors is also
+//! supported.
+
+use crate::Fd;
+use std::cmp::min;
+use std::collections::BTreeMap;
+use std::fmt::{self, Formatter};
+use std::marker::PhantomData;
+
+pub use serde::de::{
+    Deserialize, DeserializeSeed, Deserializer, EnumAccess, Error, SeqAccess, StdError,
+    VariantAccess, Visitor,
+};
+
+/// A generic container for an inner serde type, and some file descriptors.
+///
+/// Useful for implementing assorted `*WithFds` traits in terms of
+/// their serde equivalents.
+#[derive(Debug)]
+pub(crate) struct WithFds<'fds, T, F> {
+    pub inner: T,
+    pub fds: &'fds mut F,
+}
+
+/// Like `Deserialize`, but provides file descriptors as well as data.
 pub trait DeserializeWithFds<'de>: Sized {
-    fn deserialize<I, De>(deserializer: DeserializerWithFds<I, De>) -> Result<Self, De::Error>
+    /// Like `Deserialize::deserialize`, but `deserializer` is a
+    /// `DeserializerWithFds` instead of a `Deserializer`.
+    fn deserialize<D: DeserializerWithFds<'de>>(deserializer: D) -> Result<Self, D::Error>;
+}
+
+/// Like `DeserializeSeed`, but provides file descriptors as well as
+/// data.
+pub trait DeserializeWithFdsSeed<'de>: Sized {
+    /// Like `DeserializeSeed::Value`.
+    type Value;
+
+    /// Like `DeserializeSeed::deserialize`, but `deserializer` is a
+    /// `DeserializerWithFds` instead of a `Deserializer`.
+    fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
+    where
+        D: DeserializerWithFds<'de>;
+}
+
+impl<'de, T: DeserializeWithFds<'de>> DeserializeWithFdsSeed<'de> for PhantomData<T> {
+    type Value = T;
+
+    fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
+    where
+        D: DeserializerWithFds<'de>,
+    {
+        T::deserialize(deserializer)
+    }
+}
+
+impl<'fds, 'de, T, F> DeserializeSeed<'de> for WithFds<'fds, T, F>
+where
+    F: Iterator<Item = Fd>,
+    T: DeserializeWithFdsSeed<'de>,
+{
+    type Value = T::Value;
+
+    fn deserialize<D: Deserializer<'de>>(self, deserializer: D) -> Result<Self::Value, D::Error> {
+        let deserializer_with_fds = WithFds {
+            inner: deserializer,
+            fds: self.fds,
+        };
+
+        self.inner.deserialize(deserializer_with_fds)
+    }
+}
+
+/// Like `Deserializer` but provides file descriptors as well as data.
+pub trait DeserializerWithFds<'de> {
+    /// Like `Deserializer::Error`.
+    type Error: Error;
+
+    /// Deserialize a file descriptor.
+    fn deserialize_fd(self) -> Result<Fd, Self::Error>;
+
+    /// Like `Deserializer::deserialize_option`, but `visitor` is a
+    /// `VisitorWithFds` instead of a `Visitor`.
+    fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
     where
-        I: Iterator<Item = RawFd>,
-        De: Deserializer<'de>;
+        V: VisitorWithFds<'de>;
+
+    /// Like `Deserializer::deserialize_seq` but `visitor` is a
+    /// `VisitorWithFds` instead of a `Visitor`.
+    fn deserialize_seq<V: VisitorWithFds<'de>>(self, visitor: V) -> Result<V::Value, Self::Error>;
+
+    /// Like `Deserializer::deserialize_map` but `visitor` is a
+    /// `VisitorWithFds` instead of a `Visitor`.
+    fn deserialize_tuple_struct<V: VisitorWithFds<'de>>(
+        self,
+        name: &'static str,
+        len: usize,
+        visitor: V,
+    ) -> Result<V::Value, Self::Error>;
+
+    /// Like `Deserializer::deserialize_map` but `visitor` is a
+    /// `VisitorWithFds` instead of a `Visitor`.
+    fn deserialize_map<V: VisitorWithFds<'de>>(self, visitor: V) -> Result<V::Value, Self::Error>;
+
+    /// Like `Deserializer::deserialize_struct` but `visitor` is a
+    /// `VisitorWithFds` instead of a `Visitor`.
+    fn deserialize_struct<V: VisitorWithFds<'de>>(
+        self,
+        name: &'static str,
+        fields: &'static [&'static str],
+        visitor: V,
+    ) -> Result<V::Value, Self::Error>;
+
+    /// Like `Deserializer::deserialize_enum` but `visitor` is a
+    /// `VisitorWithFds` instead of a `Visitor`.
+    fn deserialize_enum<V: VisitorWithFds<'de>>(
+        self,
+        name: &'static str,
+        variants: &'static [&'static str],
+        visitor: V,
+    ) -> Result<V::Value, Self::Error>;
+
+    /// Invite a `Visitor` to visit a `Some` value.
+    ///
+    /// The purpose of this indirection is to allow implementations to
+    /// control how `Visitor::visit_some` is called.  This means that
+    /// whatever way they get a `Deserializer` to pass to it doesn't
+    /// have to be exposed publicly through the trait.
+    ///
+    /// This looks a lot like a `deserialize_*` method, but it takes a
+    /// `Visitor` instead of a `VisitorWithFds`, and it's not part of
+    /// the serde interface.
+    fn invite_visit_some<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error>;
+
+    /// Invite a `Deserialize` to deserialize.
+    ///
+    /// The purpose of this indirection is to allow implementations to
+    /// control how `Deserialize::deserialize` is called.  This means
+    /// that whatever way they get a `Deserializer` to pass to it
+    /// doesn't have to be exposed publicly through the trait.
+    fn invite_deserialize<T: Deserialize<'de>>(self) -> Result<T, Self::Error>;
+}
+
+impl<'fds, 'de, D, F> DeserializerWithFds<'de> for WithFds<'fds, D, F>
+where
+    D: Deserializer<'de>,
+    F: Iterator<Item = Fd>,
+{
+    type Error = D::Error;
+
+    fn deserialize_fd(self) -> Result<Fd, Self::Error> {
+        struct UnitVisitor;
+
+        impl<'de> Visitor<'de> for UnitVisitor {
+            type Value = ();
+
+            fn expecting(&self, f: &mut Formatter) -> fmt::Result {
+                write!(f, "expecting unit")
+            }
+
+            fn visit_unit<E>(self) -> Result<Self::Value, E> {
+                Ok(())
+            }
+        }
+
+        // This is probably not necessary, since unit should be
+        // serialized to nothing, but it's more correct to do this,
+        // because unit was serialized, and so unit should be
+        // deserialized.
+        self.inner.deserialize_unit(UnitVisitor)?;
+
+        self.fds
+            .next()
+            .ok_or_else(|| Self::Error::custom("no fds to deserialize"))
+    }
+
+    fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+    where
+        V: VisitorWithFds<'de>,
+    {
+        let wrapper = WithFds {
+            inner: visitor,
+            fds: self.fds,
+        };
+
+        self.inner.deserialize_option(wrapper)
+    }
+
+    fn deserialize_seq<V: VisitorWithFds<'de>>(self, visitor: V) -> Result<V::Value, D::Error> {
+        let wrapper = WithFds {
+            inner: visitor,
+            fds: self.fds,
+        };
+
+        self.inner.deserialize_seq(wrapper)
+    }
+
+    fn deserialize_tuple_struct<V: VisitorWithFds<'de>>(
+        self,
+        name: &'static str,
+        len: usize,
+        visitor: V,
+    ) -> Result<V::Value, D::Error> {
+        let wrapper = WithFds {
+            inner: visitor,
+            fds: self.fds,
+        };
+
+        self.inner.deserialize_tuple_struct(name, len, wrapper)
+    }
+
+    fn deserialize_map<V: VisitorWithFds<'de>>(self, visitor: V) -> Result<V::Value, D::Error> {
+        let wrapper = WithFds {
+            inner: visitor,
+            fds: self.fds,
+        };
+
+        self.inner.deserialize_map(wrapper)
+    }
+
+    fn deserialize_struct<V: VisitorWithFds<'de>>(
+        self,
+        name: &'static str,
+        fields: &'static [&'static str],
+        visitor: V,
+    ) -> Result<V::Value, D::Error> {
+        let wrapper = WithFds {
+            inner: visitor,
+            fds: self.fds,
+        };
+
+        self.inner.deserialize_struct(name, fields, wrapper)
+    }
+
+    fn deserialize_enum<V: VisitorWithFds<'de>>(
+        self,
+        name: &'static str,
+        variants: &'static [&'static str],
+        visitor: V,
+    ) -> Result<V::Value, D::Error> {
+        let wrapper = WithFds {
+            inner: visitor,
+            fds: self.fds,
+        };
+
+        self.inner.deserialize_enum(name, variants, wrapper)
+    }
+
+    fn invite_visit_some<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, D::Error> {
+        visitor.visit_some(self.inner)
+    }
+
+    fn invite_deserialize<T: Deserialize<'de>>(self) -> Result<T, D::Error> {
+        T::deserialize(self.inner)
+    }
 }
 
+/// A type implementing `Visitor` without any overrides.
+///
+/// This allows for referring to the default serde implementation of
+/// the `Visitor` methods from our own overrides.
 #[derive(Debug)]
-pub struct DeserializerWithFds<'iter, Iter, De> {
-    pub deserializer: De,
-    pub fds: &'iter mut Iter,
+struct VisitorDefaults<'a, V>(PhantomData<&'a V>);
+
+impl<'a, 'de, V> Visitor<'de> for VisitorDefaults<'a, V> {
+    type Value = V;
+
+    fn expecting(&self, _: &mut Formatter) -> fmt::Result {
+        unreachable!()
+    }
+}
+
+/// Like `SeqAccess`, but elements provide access to file descriptors
+/// as well as data.
+pub trait SeqAccessWithFds<'de> {
+    /// Like `SeqAccess::Error`.
+    type Error: Error;
+
+    /// Like `SeqAccess::next_element_seed`, but `seed` is a
+    /// `DeserializeWithFdsSeed` instead of a `DeserializeSeed`.
+    fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
+    where
+        T: DeserializeWithFdsSeed<'de>;
+
+    /// Like `SeqAccess::next_element`, but returns a
+    /// `DeserializeWithFds` instead of a `Deserialize`.
+    fn next_element<T: DeserializeWithFds<'de>>(&mut self) -> Result<Option<T>, Self::Error> {
+        self.next_element_seed(PhantomData)
+    }
+
+    /// See `SeqAccess::size_hint`.
+    fn size_hint(&self) -> Option<usize> {
+        None
+    }
+
+    /// Invite a `Visitor` to visit.  The inverse of `Visitor::visit`.
+    ///
+    /// The purpose of this indirection is to allow implementations to
+    /// control how `Visitor::visit_seq` is called.  This means that
+    /// whatever way they get a `SeqAccess` to pass to it doesn't have
+    /// to be exposed publicly through the trait.
+    fn invite<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error>;
+}
+
+impl<'de, 'fds, A, F> SeqAccessWithFds<'de> for WithFds<'fds, A, F>
+where
+    A: SeqAccess<'de>,
+    F: Iterator<Item = Fd>,
+{
+    type Error = A::Error;
+
+    fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
+    where
+        T: DeserializeWithFdsSeed<'de>,
+    {
+        let wrapper = WithFds {
+            inner: seed,
+            fds: self.fds,
+        };
+
+        self.inner.next_element_seed(wrapper)
+    }
+
+    fn size_hint(&self) -> Option<usize> {
+        self.inner.size_hint()
+    }
+
+    fn invite<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
+        visitor.visit_seq(self.inner)
+    }
 }
 
-impl<'iter, Iter, De> DeserializerWithFds<'iter, Iter, De> {
-    pub fn new(fds: &'iter mut Iter, deserializer: De) -> Self {
-        Self { deserializer, fds }
+/// Like `MapAccess`, but keys and values provide access to file
+/// descriptors as well as data.
+pub trait MapAccessWithFds<'de>: Sized {
+    /// Like `MapAccess::Error`.
+    type Error: Error;
+
+    /// Like `MapAccess::next_key_seed`, but `seed` is a
+    /// `DeserializeWithFdsSeed` instead of a `DeserializeSeed`.
+    fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error>
+    where
+        K: DeserializeWithFdsSeed<'de>;
+
+    /// Like `MapAccess::next_value_seed`, but `seed` is a
+    /// `DeserializeWithFdsSeed` instead of a `DeserializeSeed`.
+    fn next_value_seed<V>(&mut self, seed: V) -> Result<Option<V::Value>, Self::Error>
+    where
+        V: DeserializeWithFdsSeed<'de>;
+
+    /// Like `MapAccess::next_entry_seed`, but `kseed` and `vseed` are
+    /// `DeserializeWithFdsSeed` instead of `DeserializeSeed`.
+    fn next_entry_seed<K: DeserializeWithFdsSeed<'de>, V: DeserializeWithFdsSeed<'de>>(
+        &mut self,
+        kseed: K,
+        vseed: V,
+    ) -> Result<Option<(K::Value, V::Value)>, Self::Error>;
+
+    /// Like `MapAccess::next_key`, but returns a `DeserializeWithFds`
+    /// instead of a `Deserialize`.
+    fn next_key<K: DeserializeWithFds<'de>>(&mut self) -> Result<Option<K>, Self::Error> {
+        self.next_key_seed(PhantomData)
+    }
+
+    /// Like `MapAccess::next_value`, but returns a `DeserializeWithFds`
+    /// instead of a `Deserialize`.
+    fn next_value<K: DeserializeWithFds<'de>>(&mut self) -> Result<Option<K>, Self::Error> {
+        self.next_value_seed(PhantomData)
+    }
+
+    /// Like `MapAccess:next_entry`, but return values are
+    /// `DeserializeWithFds` instead of `Deserialize`.
+    fn next_entry<K, V>(&mut self) -> Result<Option<(K, V)>, Self::Error>
+    where
+        K: DeserializeWithFds<'de>,
+        V: DeserializeWithFds<'de>,
+    {
+        self.next_entry_seed(PhantomData, PhantomData)
+    }
+
+    /// Like `MapAccess::size_hint`.
+    fn size_hint(&self) -> Option<usize> {
+        None
+    }
+
+    /// Invite a `Visitor` to visit.  The inverse of `Visitor::visit`.
+    ///
+    /// The purpose of this indirection is to allow implementations to
+    /// control how `Visitor::visit_map` is called.  This means that
+    /// whatever way they get a `MapAccess` to pass to it doesn't have
+    /// to be exposed publicly through the trait.
+    fn invite<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error>;
+}
+
+/// Like `EnumAccess`, but variants provide access to file descriptors
+/// as well as data.
+pub trait EnumAccessWithFds<'de>: Sized {
+    /// Like `EnumAccess::Error`.
+    type Error: Error;
+
+    /// Like `EnumAccess::Variant`, but also provides access to file
+    /// descriptors.
+    type Variant: VariantAccessWithFds<'de, Error = Self::Error>;
+
+    /// Like `EnumAccess::variant_seed`, but `seed` is a
+    /// `DeserializeWithFdsSeed` instead of a `DeserializeSeed`.
+    fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error>
+    where
+        V: DeserializeSeed<'de>;
+
+    /// Like `EnumAccess::variant`, but returns a `DeserializeWithFds`
+    /// instead of a `Deserialize`.
+    fn variant<V>(self) -> Result<(V, Self::Variant), Self::Error>
+    where
+        V: Deserialize<'de>,
+    {
+        self.variant_seed(PhantomData)
+    }
+
+    /// Invite a `Visitor` to visit.  The inverse of `Visitor::visit`.
+    ///
+    /// The purpose of this indirection is to allow implementations to
+    /// control how `Visitor::visit_enum` is called.  This means that
+    /// whatever way they get an `EnumAccess` to pass to it doesn't
+    /// have to be exposed publicly through the trait.
+    fn invite<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error>;
+}
+
+impl<'fds, 'de, A, F> EnumAccessWithFds<'de> for WithFds<'fds, A, F>
+where
+    A: EnumAccess<'de>,
+    F: Iterator<Item = Fd>,
+{
+    type Error = A::Error;
+    type Variant = WithFds<'fds, A::Variant, F>;
+
+    fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error>
+    where
+        V: DeserializeSeed<'de>,
+    {
+        let (value, variant) = self.inner.variant_seed(seed)?;
+        let variant_with_fds = WithFds {
+            inner: variant,
+            fds: self.fds,
+        };
+
+        Ok((value, variant_with_fds))
+    }
+
+    fn invite<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
+        visitor.visit_enum(self.inner)
+    }
+}
+
+/// Like `VariantAccess`, but provides file descriptors as well as data.
+pub trait VariantAccessWithFds<'de>: Sized {
+    /// Like `VariantAccess::Error`.
+    type Error: Error;
+
+    /// Like `VariantAccess::unit_variant`.
+    fn unit_variant(self) -> Result<(), Self::Error>;
+
+    /// Like `VariantAccess::newtype_variant_seed`, but `seed` is a
+    /// `DeserializeWithFdsSeed` instead of a `DeserializeSeed`.
+    fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value, Self::Error>
+    where
+        T: DeserializeWithFdsSeed<'de>;
+
+    /// Like `VariantAccess::newtype_variant`, but returns a
+    /// `DeserializeWithFds` instead of a `Deserialize`.
+    fn newtype_variant<T: DeserializeWithFds<'de>>(self) -> Result<T, Self::Error> {
+        self.newtype_variant_seed(PhantomData)
+    }
+
+    /// Like `VariantAccess::struct_variant`, but returns a
+    /// `DeserializeWithFds` instead of a `Deserialize`.
+    fn struct_variant<V: VisitorWithFds<'de>>(
+        self,
+        fields: &'static [&'static str],
+        visitor: V,
+    ) -> Result<V::Value, Self::Error>;
+}
+
+impl<'de, 'fds, A, F> VariantAccessWithFds<'de> for WithFds<'fds, A, F>
+where
+    A: VariantAccess<'de>,
+    F: Iterator<Item = Fd>,
+{
+    type Error = A::Error;
+
+    fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value, Self::Error>
+    where
+        T: DeserializeWithFdsSeed<'de>,
+    {
+        let wrapper = WithFds {
+            inner: seed,
+            fds: self.fds,
+        };
+
+        self.inner.newtype_variant_seed(wrapper)
+    }
+
+    fn unit_variant(self) -> Result<(), Self::Error> {
+        self.inner.unit_variant()
+    }
+
+    fn struct_variant<V: VisitorWithFds<'de>>(
+        self,
+        fields: &'static [&'static str],
+        visitor: V,
+    ) -> Result<V::Value, Self::Error> {
+        let wrapper = WithFds {
+            inner: visitor,
+            fds: self.fds,
+        };
+
+        self.inner.struct_variant(fields, wrapper)
+    }
+}
+
+/// Like `Visitor`, but provides file descriptors as well as data.
+pub trait VisitorWithFds<'de>: Sized {
+    /// Like `Visitor::Value`.
+    type Value;
+
+    /// Like `Visitor::expecting`.
+    fn expecting(&self, f: &mut Formatter) -> fmt::Result;
+
+    /// Like `Visitor::visit_none`.
+    fn visit_none<E: Error>(self) -> Result<Self::Value, E> {
+        VisitorDefaults(PhantomData).visit_none()
+    }
+
+    /// Like `Visitor:::visit_some`, but `deserializer` is a
+    /// `DeserializerWithFds` instead of a `Deserializer`.
+    fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
+    where
+        D: DeserializerWithFds<'de>,
+    {
+        deserializer.invite_visit_some(VisitorDefaults(PhantomData))
+    }
+
+    /// Like `Visitor::visit_seq`, but `seq` is a `SeqAccessWithFds`
+    /// instead of a `SeqAccess`.
+    fn visit_seq<A: SeqAccessWithFds<'de>>(self, seq: A) -> Result<Self::Value, A::Error> {
+        seq.invite(VisitorDefaults(PhantomData))
+    }
+
+    /// Like `Visitor::visit_map`, but `map` is a `MapAccessWithFds`
+    /// instead of a `MapAccess`.
+    fn visit_map<A: MapAccessWithFds<'de>>(self, map: A) -> Result<Self::Value, A::Error> {
+        map.invite(VisitorDefaults(PhantomData))
+    }
+
+    /// Like `Visitor::visit_enum`, but `data` is an
+    /// `EnumAccessWithFds` instead of an `EnumAccess`.
+    fn visit_enum<A: EnumAccessWithFds<'de>>(self, data: A) -> Result<Self::Value, A::Error> {
+        data.invite(VisitorDefaults(PhantomData))
+    }
+}
+
+impl<'de, 'fds, V, F> Visitor<'de> for WithFds<'fds, V, F>
+where
+    V: VisitorWithFds<'de>,
+    F: Iterator<Item = Fd>,
+{
+    type Value = V::Value;
+
+    fn expecting(&self, f: &mut Formatter) -> fmt::Result {
+        self.inner.expecting(f)
+    }
+
+    fn visit_seq<A: SeqAccess<'de>>(self, data: A) -> Result<Self::Value, A::Error> {
+        self.inner.visit_seq(WithFds {
+            inner: data,
+            fds: self.fds,
+        })
+    }
+
+    fn visit_enum<A: EnumAccess<'de>>(self, data: A) -> Result<Self::Value, A::Error> {
+        self.inner.visit_enum(WithFds {
+            inner: data,
+            fds: self.fds,
+        })
+    }
+}
+
+macro_rules! deserialize_impl {
+    ($type:ty) => {
+        impl<'de> DeserializeWithFds<'de> for $type {
+            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+            where
+                D: DeserializerWithFds<'de>,
+            {
+                deserializer.invite_deserialize()
+            }
+        }
+    };
+}
+
+macro_rules! fd_impl {
+    ($type:ty) => {
+        impl<'de> DeserializeWithFds<'de> for $type {
+            fn deserialize<D: DeserializerWithFds<'de>>(deserializer: D) -> Result<Self, D::Error> {
+                deserializer.deserialize_fd().map(|x| x.specialize())
+            }
+        }
+    };
+}
+
+deserialize_impl!(());
+
+deserialize_impl!(u8);
+deserialize_impl!(u16);
+deserialize_impl!(u32);
+deserialize_impl!(u64);
+deserialize_impl!(usize);
+
+deserialize_impl!(String);
+deserialize_impl!(std::path::PathBuf);
+
+fd_impl!(std::fs::File);
+fd_impl!(std::net::TcpListener);
+fd_impl!(std::net::TcpStream);
+fd_impl!(std::net::UdpSocket);
+fd_impl!(std::os::unix::net::UnixDatagram);
+fd_impl!(std::os::unix::net::UnixListener);
+fd_impl!(std::os::unix::net::UnixStream);
+fd_impl!(sys_util::net::UnixSeqpacket);
+
+impl<'de, T: DeserializeWithFds<'de>> DeserializeWithFds<'de> for Option<T> {
+    fn deserialize<D: DeserializerWithFds<'de>>(deserializer: D) -> Result<Self, D::Error> {
+        struct Visitor<T>(PhantomData<T>);
+
+        impl<'de, T: DeserializeWithFds<'de>> VisitorWithFds<'de> for Visitor<T> {
+            type Value = Option<T>;
+
+            fn expecting(&self, f: &mut Formatter) -> fmt::Result {
+                write!(f, "option")
+            }
+
+            fn visit_none<E: Error>(self) -> Result<Self::Value, E> {
+                Ok(None)
+            }
+
+            fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
+            where
+                D: DeserializerWithFds<'de>,
+            {
+                T::deserialize(deserializer).map(Some)
+            }
+        }
+
+        deserializer.deserialize_option(Visitor(PhantomData))
+    }
+}
+
+impl<'de, T: DeserializeWithFds<'de>> DeserializeWithFds<'de> for Vec<T> {
+    fn deserialize<D: DeserializerWithFds<'de>>(deserializer: D) -> Result<Self, D::Error> {
+        struct Visitor<T>(PhantomData<T>);
+
+        impl<'de, T: DeserializeWithFds<'de>> VisitorWithFds<'de> for Visitor<T> {
+            type Value = Vec<T>;
+
+            fn expecting(&self, f: &mut Formatter) -> fmt::Result {
+                write!(f, "a sequence")
+            }
+
+            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
+            where
+                A: SeqAccessWithFds<'de>,
+            {
+                let mut values = Vec::with_capacity(min(seq.size_hint().unwrap_or(0), 4096));
+
+                while let Some(value) = seq.next_element()? {
+                    values.push(value);
+                }
+
+                Ok(values)
+            }
+        }
+
+        deserializer.deserialize_seq(Visitor(PhantomData))
+    }
+}
+
+impl<'de, K, V> DeserializeWithFds<'de> for BTreeMap<K, V>
+where
+    K: DeserializeWithFds<'de> + Ord,
+    V: DeserializeWithFds<'de>,
+{
+    fn deserialize<D: DeserializerWithFds<'de>>(deserializer: D) -> Result<Self, D::Error> {
+        struct Visitor<K, V>(PhantomData<(K, V)>);
+
+        impl<'de, K, V> VisitorWithFds<'de> for Visitor<K, V>
+        where
+            K: DeserializeWithFds<'de> + Ord,
+            V: DeserializeWithFds<'de>,
+        {
+            type Value = BTreeMap<K, V>;
+
+            fn expecting(&self, f: &mut Formatter) -> fmt::Result {
+                write!(f, "a map")
+            }
+
+            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
+            where
+                A: MapAccessWithFds<'de>,
+            {
+                let mut values = BTreeMap::new();
+
+                while let Some((key, value)) = map.next_entry()? {
+                    values.insert(key, value);
+                }
+
+                Ok(values)
+            }
+        }
+
+        deserializer.deserialize_map(Visitor(PhantomData))
     }
 }
diff --git a/msg_socket2/src/error.rs b/msg_socket2/src/error.rs
index 2daa450..3aa0dd5 100644
--- a/msg_socket2/src/error.rs
+++ b/msg_socket2/src/error.rs
@@ -1,3 +1,6 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+// Copyright 2020, Alyssa Ross
+
 use std::fmt::{self, Display, Formatter};
 
 #[derive(Debug)]
diff --git a/msg_socket2/src/fd.rs b/msg_socket2/src/fd.rs
new file mode 100644
index 0000000..648be3e
--- /dev/null
+++ b/msg_socket2/src/fd.rs
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+// Copyright 2020, Alyssa Ross
+
+use libc::close;
+use std::mem::forget;
+use std::os::unix::prelude::*;
+
+/// A type representing ownership of a file descriptor.
+pub struct Fd(RawFd);
+
+impl Fd {
+    /// Construct an owned `Fd` from a raw file descriptor.
+    ///
+    /// This function is unsafe because `Fd` must be the sole owner of
+    /// the file descriptor, so that it can be closed when the `Fd` is
+    /// dropped.
+    pub unsafe fn new(fd: RawFd) -> Self {
+        Self(fd)
+    }
+
+    /// Converts this `Fd` into a different implementation of
+    /// `FromRawFd` containing the same file descriptor.
+    ///
+    /// No checks that the file descriptor is of the appropriate type
+    /// are performed.  The file descriptor will remain open.
+    pub fn specialize<T: FromRawFd>(self) -> T {
+        let fd = self.into_raw_fd();
+
+        // Safe because we transfer ownership of the file descriptor.
+        unsafe { T::from_raw_fd(fd) }
+    }
+}
+
+impl IntoRawFd for Fd {
+    fn into_raw_fd(self) -> RawFd {
+        let fd = self.0;
+        forget(self);
+        fd
+    }
+}
+
+impl AsRawFd for Fd {
+    fn as_raw_fd(&self) -> RawFd {
+        self.0
+    }
+}
+
+impl FromRawFd for Fd {
+    unsafe fn from_raw_fd(fd: RawFd) -> Self {
+        Self(fd)
+    }
+}
+
+impl Drop for Fd {
+    fn drop(&mut self) {
+        // Safe because we have sole ownership of fd.
+        unsafe { close(self.0) };
+    }
+}
diff --git a/msg_socket2/src/lib.rs b/msg_socket2/src/lib.rs
index 9bb2526..00500bb 100644
--- a/msg_socket2/src/lib.rs
+++ b/msg_socket2/src/lib.rs
@@ -1,36 +1,39 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
 // Copyright 2020, Alyssa Ross
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//     * Redistributions of source code must retain the above copyright
-//       notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above copyright
-//       notice, this list of conditions and the following disclaimer in the
-//       documentation and/or other materials provided with the distribution.
-//     * Neither the name of the <organization> nor the
-//       names of its contributors may be used to endorse or promote products
-//       derived from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
-// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-mod de;
+//! A library for sending messages over Unix domain sockets, including
+//! file descriptors.
+//!
+//! The serialization and deserialization interface is a wrapper on
+//! top of bincode and serde.  `Serializer` and `Deserializer` are
+//! very large interfaces, so they have so far only been implemented
+//! on-demand.  But, implementing other methods as required should be
+//! extremely straightforward to do based on the methods that have
+//! already been implemented.
+//!
+//! In addition to serialization and deserialization, the library
+//! provides a `Socket` type.  This is a wrapper around a Unix
+//! SOCK_SEQPACKET socket that automatically serializes outgoing
+//! messages, and deserializes incoming ones.  This might be changed
+//! to `UnixStream`, to remove the dependency on `sys_util`.
+
+// TODO: document macros.
+
+pub mod de;
 mod error;
+mod fd;
 pub mod ser;
 mod socket;
 
 pub(crate) use ser::FdSerializerImpl;
 
-pub use de::{DeserializeWithFds, DeserializerWithFds};
+pub use de::{DeserializeWithFds, Deserializer, DeserializerWithFds};
 pub use error::Error;
-pub use ser::{FdSerializer, Serialize, SerializeWithFds, Serializer};
+use fd::Fd;
+pub use msg_socket2_derive::{DeserializeWithFds, SerializeWithFds};
+pub use ser::{FdSerializer, SerializeWithFds, Serializer};
 pub use socket::Socket;
+
+// Don't use ser and de re-exports, because they import from
+// serde::ser and serde::de, so won't pick up derives.
+pub use serde::{Deserialize, Serialize};
diff --git a/msg_socket2/src/ser.rs b/msg_socket2/src/ser.rs
index e2b9900..07ead80 100644
--- a/msg_socket2/src/ser.rs
+++ b/msg_socket2/src/ser.rs
@@ -1,45 +1,22 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
 // Copyright 2020, Alyssa Ross
-// All rights reserved.
 //
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//     * Redistributions of source code must retain the above copyright
-//       notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above copyright
-//       notice, this list of conditions and the following disclaimer in the
-//       documentation and/or other materials provided with the distribution.
-//     * Neither the name of the <organization> nor the
-//       names of its contributors may be used to endorse or promote products
-//       derived from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
-// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-// Portions Copyright Erick Tryzelaar and David Tolnay,
+// Portions Copyright Erick Tryzelaar and David Tolnay.
 // Licensed under either of Apache License, Version 2.0
 // or MIT license at your option.
 
 //! Data structure serialization framework for Unix domain sockets.
 //!
-//! Much like serde::ser, except sending file descriptors is also
+//! Much like `serde::ser`, except sending file descriptors is also
 //! supported.
 
-use serde::ser::*;
 use std::collections::BTreeMap;
 use std::fmt::{self, Display, Formatter};
 use std::os::unix::prelude::*;
 
 pub use serde::ser::{
-    Serialize, SerializeSeq, SerializeStruct, SerializeStructVariant, SerializeTupleStruct,
-    SerializeTupleVariant, Serializer,
+    Error, Serialize, SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant,
+    SerializeTupleStruct, SerializeTupleVariant, Serializer, StdError,
 };
 
 #[derive(Debug)]
@@ -64,15 +41,21 @@ pub(crate) struct Composite<'ser, S> {
     serializer: &'ser mut S,
 }
 
-/// Returned from `SerializerWithFds::serialize_seq`.
+/// Like `SerializeSeq`, but accepts file descriptors as well as data.
 pub trait SerializeSeqFds {
+    /// Like `SerializeSeq::Ok`.
     type Ok;
+
+    /// Like `SerializeSeq::Error`.
     type Error: Error;
 
+    /// Like `SerializeSeq::serialize_element`, but `value` is a
+    /// `SerializeWithFds` instead of a `Serialize`.
     fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
     where
         T: SerializeWithFds + ?Sized;
 
+    /// Like `SerializeSeq::end`.
     fn end(self) -> Result<Self::Ok, Self::Error>;
 }
 
@@ -93,15 +76,21 @@ impl<'ser, 'fds> SerializeSeqFds for Composite<'ser, FdSerializerImpl<'fds>> {
     }
 }
 
-/// Returned from `SerializerWithFds::serialize_tuple_struct`.
+/// Like `SerializeTupleStruct`, but accepts file descriptors as well as data.
 pub trait SerializeTupleStructFds {
+    /// Like `SerializeTupleStruct::Ok`.
     type Ok;
+
+    /// Like `SerializeTupleStruct::Error`.
     type Error: Error;
 
+    /// Like `SerializeTupleStruct::serialize_field`, but `value` is a
+    /// `SerializeWithFds` instead of a `Serialize`.
     fn serialize_field<T>(&mut self, value: &T) -> Result<(), Self::Error>
     where
         T: SerializeWithFds + ?Sized;
 
+    /// Like `SerializeTupleStruct::end`.
     fn end(self) -> Result<Self::Ok, Self::Error>;
 }
 
@@ -122,15 +111,22 @@ impl<'ser, 'fds> SerializeTupleStructFds for Composite<'ser, FdSerializerImpl<'f
     }
 }
 
-/// Returned from `SerializerWithFds::serialize_tuple_variant`.
+/// Like `SerializeTupleVariant`, but accepts file descriptors as well
+/// as data.
 pub trait SerializeTupleVariantFds {
+    /// Like `SerializeTupleVariant::Ok`.
     type Ok;
+
+    /// Like `SerializeTupleVariant::Error`.
     type Error: Error;
 
+    /// Like `SerializeTupleVariant::serialize_field`, but `value` is
+    /// a `SerializeWithFds` instead of a `Serialize`.
     fn serialize_field<T>(&mut self, value: &T) -> Result<(), Self::Error>
     where
         T: SerializeWithFds + ?Sized;
 
+    /// Like `SerializeTupleVariant::end`.
     fn end(self) -> Result<Self::Ok, Self::Error>;
 }
 
@@ -151,17 +147,24 @@ impl<'ser, 'fds> SerializeTupleVariantFds for Composite<'ser, FdSerializerImpl<'
     }
 }
 
-/// Returned from `SerializerWithFds::serialize_map`.
+/// Like `SerializeMap`, but provides file descriptors as well as data.
 pub trait SerializeMapFds {
+    /// Like `SerializeMap::Ok`.
     type Ok;
+
+    /// Like `SerializeMap::Error`.
     type Error: Error;
 
+    /// Like `SerializeMap::serialize_key`, but `key` is a `SerializeWithFds` instead of a `Serialize`.
     fn serialize_key<T: SerializeWithFds + ?Sized>(&mut self, key: &T) -> Result<(), Self::Error>;
 
+    /// Like `SerializeMap::serialize_value`, but `value` is a `SerializeWithFds` instead of a `Serialize`.
     fn serialize_value<T>(&mut self, value: &T) -> Result<(), Self::Error>
     where
         T: SerializeWithFds + ?Sized;
 
+    /// Like `SerializeMap::serialize_key`, but `key` and `value` are
+    /// `SerializeWithFds` instead of `Serialize`.
     fn serialize_entry<K: SerializeWithFds + ?Sized, V: SerializeWithFds + ?Sized>(
         &mut self,
         key: &K,
@@ -171,6 +174,7 @@ pub trait SerializeMapFds {
         self.serialize_value(value)
     }
 
+    /// Like `SerializeMap::end`.
     fn end(self) -> Result<Self::Ok, Self::Error>;
 }
 
@@ -196,20 +200,28 @@ impl<'ser, 'fds> SerializeMapFds for Composite<'ser, FdSerializerImpl<'fds>> {
     }
 }
 
-/// Returned from `SerializerWithFds::serialize_struct`.
+/// Like `SerializeStruct`, but provides file descriptors as well as
+/// data.
 pub trait SerializeStructFds {
+    /// Like `SerializeStruct::Ok`.
     type Ok;
+
+    /// Like `SerializeStruct::Error`.
     type Error: Error;
 
+    /// Like `SerializeStruct::serialize_field`, but `value` is a
+    /// `SerializeWithFds` instead of a `Serialize`.
     fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
     where
         T: SerializeWithFds + ?Sized;
 
+    /// Like `SerializeStruct::skip_field`.
     fn skip_field(&mut self, key: &'static str) -> Result<(), Self::Error> {
         let _ = key;
         Ok(())
     }
 
+    /// Like `SerializeStruct::end`.
     fn end(self) -> Result<Self::Ok, Self::Error>;
 }
 
@@ -230,20 +242,28 @@ impl<'ser, 'fds> SerializeStructFds for Composite<'ser, FdSerializerImpl<'fds>>
     }
 }
 
-/// Returned from `SerializerWithFds::serialize_struct_variant`.
+/// Like `SerializeStructVariant`, but provides file descriptors as
+/// well as data.
 pub trait SerializeStructVariantFds {
+    /// Like `SerializeStructVariant::Ok`.
     type Ok;
+
+    /// Like `SerializeStructVariant::Error`.
     type Error: Error;
 
+    /// Like `SerializeStructVariant::serialize_field`, but `key` and
+    /// `value` are `SerializeWithFds` instead of `Serialize`.
     fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
     where
         T: SerializeWithFds + ?Sized;
 
+    /// Like `SerializeStructVariant::skip_field`.
     fn skip_field(&mut self, key: &'static str) -> Result<(), Self::Error> {
         let _ = key;
         Ok(())
     }
 
+    /// Like `SerializeStructVariant::end`.
     fn end(self) -> Result<Self::Ok, Self::Error>;
 }
 
@@ -264,28 +284,54 @@ impl<'ser, 'fds> SerializeStructVariantFds for Composite<'ser, FdSerializerImpl<
     }
 }
 
-/// A **data format** that can send file descriptors between Unix processes.
+/// Like `Serializer`, but accepts file descriptors instead of data.
 pub trait FdSerializer: Sized {
+    /// Like `Serializer::Ok`.
     type Ok;
+
+    /// Like `Serializer::Error`.
     type Error: Error;
 
+    /// Like `Serializer::SerializeSeq`, but serializes
+    /// `SerializeWithFds` instead of `Serialize`.
     type SerializeSeqFds: SerializeSeqFds<Ok = Self::Ok, Error = Self::Error>;
+
+    /// Like `Serializer::SerializeTupleStruct`, but serializes
+    /// `SerializeWithFds` instead of `Serialize`.
     type SerializeTupleStructFds: SerializeTupleStructFds<Ok = Self::Ok, Error = Self::Error>;
+
+    /// Like `Serializer::SerializeTupleVariant`, but serializes
+    /// `SerializeWithFds` instead of `Serialize`.
     type SerializeTupleVariantFds: SerializeTupleVariantFds<Ok = Self::Ok, Error = Self::Error>;
+
+    /// Like `Serializer::SerializeMap`, but serializes
+    /// `SerializeWithFds` instead of `Serialize`.
     type SerializeMapFds: SerializeMapFds<Ok = Self::Ok, Error = Self::Error>;
+
+    /// Like `Serializer::SerializeStruct`, but serializes
+    /// `SerializeWithFds` instead of `Serialize`.
     type SerializeStructFds: SerializeStructFds<Ok = Self::Ok, Error = Self::Error>;
+
+    /// Like `Serializer::SerializeStructVariant`, but serializes
+    /// `SerializeWithFds` instead of `Serialize`.
     type SerializeStructVariantFds: SerializeStructVariantFds<Ok = Self::Ok, Error = Self::Error>;
 
+    /// Serialize a file descriptor.
     fn serialize_raw_fd(self, value: RawFd) -> Result<Self::Ok, Self::Error>;
 
+    /// Like `Serializer::serialize_none`.
     fn serialize_none(self) -> Result<Self::Ok, Self::Error>;
 
+    /// Like `Serializer::serialize_some`, but `value` is a
+    /// `SerializeWithFds` instead of a `Serialize`.
     fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
     where
         T: SerializeWithFds + ?Sized;
 
+    /// Like `Serializer::serialize_unit`.
     fn serialize_unit(self) -> Result<Self::Ok, Self::Error>;
 
+    /// Like `Serializer::serialize_unit_variant`.
     fn serialize_unit_variant(
         self,
         name: &'static str,
@@ -293,14 +339,23 @@ pub trait FdSerializer: Sized {
         variant: &'static str,
     ) -> Result<Self::Ok, Self::Error>;
 
+    /// Like `Serializer::serialize_seq`, but returns a
+    /// `SerializeSeqFds`, which serializes file descriptors as well
+    /// as data.
     fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeqFds, Self::Error>;
 
+    /// Like `Serializer::serialize_tuple_struct`, but returns a
+    /// `SerializeTupleStructFds`, which serializes file descriptors as well
+    /// as data.
     fn serialize_tuple_struct(
         self,
         name: &'static str,
         len: usize,
     ) -> Result<Self::SerializeTupleStructFds, Self::Error>;
 
+    /// Like `Serializer::serialize_tuple_variant`, but returns a
+    /// `SerializeTupleVariantFds`, which serializes file descriptors
+    /// as well as data.
     fn serialize_tuple_variant(
         self,
         name: &'static str,
@@ -309,14 +364,23 @@ pub trait FdSerializer: Sized {
         len: usize,
     ) -> Result<Self::SerializeTupleVariantFds, Self::Error>;
 
+    /// Like `Serializer::serialize_map`, but returns a
+    /// `SerializeMapFds`, which serializes file descriptors as well
+    /// as data.
     fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMapFds, Self::Error>;
 
+    /// Like `Serializer::serialize_struct`, but returns a
+    /// `SerializeStructFds`, which serializes file descriptors as
+    /// well as data.
     fn serialize_struct(
         self,
         name: &'static str,
         len: usize,
     ) -> Result<Self::SerializeStructFds, Self::Error>;
 
+    /// Like `Serializer::serialize_struct_variant`, but returns a
+    /// `SerializeStructVariantFds`, which serializes file descriptors
+    /// as well as data.
     fn serialize_struct_variant(
         self,
         name: &'static str,
@@ -416,10 +480,16 @@ impl<'ser, 'fds> FdSerializer for &'ser mut FdSerializerImpl<'fds> {
     }
 }
 
-/// A **data structure** that can be sent over a Unix domain socket.
+/// Like `Serialize`, but additionally provides the `serialize_fds` method.
 pub trait SerializeWithFds {
-    fn serialize<Ser: Serializer>(&self, serializer: Ser) -> Result<Ser::Ok, Ser::Error>;
-    fn serialize_fds<Ser: FdSerializer>(&self, serializer: Ser) -> Result<Ser::Ok, Ser::Error>;
+    /// Like `Serialize::serialize`.
+    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error>;
+
+    /// Serialize all file descriptors associated with this value, recursively.
+    ///
+    /// In most cases, the implementation should largely mirror the
+    /// `serialize` implementation.
+    fn serialize_fds<S: FdSerializer>(&self, serializer: S) -> Result<S::Ok, S::Error>;
 }
 
 macro_rules! serialize_impl {
@@ -489,6 +559,7 @@ fd_impl!(std::os::unix::net::UnixStream);
 fd_impl!(std::process::ChildStderr);
 fd_impl!(std::process::ChildStdin);
 fd_impl!(std::process::ChildStdout);
+fd_impl!(sys_util::net::UnixSeqpacket);
 
 impl<T: SerializeWithFds> SerializeWithFds for Option<T> {
     fn serialize<Ser: Serializer>(&self, serializer: Ser) -> Result<Ser::Ok, Ser::Error> {
diff --git a/msg_socket2/src/socket.rs b/msg_socket2/src/socket.rs
index b836365..bd1bc00 100644
--- a/msg_socket2/src/socket.rs
+++ b/msg_socket2/src/socket.rs
@@ -1,11 +1,16 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+// Copyright 2020, Alyssa Ross
+
 use bincode::{DefaultOptions, Deserializer, Serializer};
 use std::io::IoSlice;
 use std::marker::PhantomData;
 use std::os::unix::prelude::*;
 use sys_util::{net::UnixSeqpacket, ScmSocket};
 
-use crate::{DeserializeWithFds, DeserializerWithFds, Error, FdSerializerImpl, SerializeWithFds};
+use crate::{de, DeserializeWithFds, Error, Fd, FdSerializerImpl, SerializeWithFds};
 
+/// A Unix **SOCK_SEQPACKET** socket that can send and receive values
+/// consisting of binary data and file descriptors.
 #[derive(Debug)]
 pub struct Socket<Send, Recv> {
     sock: UnixSeqpacket,
@@ -39,11 +44,16 @@ impl<Send: SerializeWithFds, Recv> Socket<Send, Recv> {
 
 impl<Send, Recv: for<'de> DeserializeWithFds<'de>> Socket<Send, Recv> {
     pub fn recv(&self) -> Result<Recv, Error> {
-        let (bytes, fds) = self.sock.recv_as_vec_with_fds()?;
-        let mut fds_iter = fds.into_iter();
+        let (bytes, raw_fds) = self.sock.recv_as_vec_with_fds()?;
+
+        // Safe because we just received ownership of the fds.
+        let mut fds = raw_fds.into_iter().map(|fd| unsafe { Fd::new(fd) });
 
         let mut deserializer = Deserializer::from_slice(&bytes, DefaultOptions::new());
-        let deserializer_with_fds = DeserializerWithFds::new(&mut fds_iter, &mut deserializer);
+        let deserializer_with_fds = de::WithFds {
+            inner: &mut deserializer,
+            fds: &mut fds,
+        };
 
         Ok(Recv::deserialize(deserializer_with_fds)?)
     }
diff --git a/msg_socket2/tests/round_trip.rs b/msg_socket2/tests/round_trip.rs
index 1bb6636..a89f414 100644
--- a/msg_socket2/tests/round_trip.rs
+++ b/msg_socket2/tests/round_trip.rs
@@ -1,20 +1,18 @@
-use std::os::unix::prelude::*;
-
 use std::fmt::{self, Formatter};
-use std::marker::PhantomData;
+use std::fs::File;
 
 use msg_socket2::{
+    de::{SeqAccessWithFds, VisitorWithFds},
     ser::{
-        SerializeAdapter, SerializeRawFd, SerializeStruct, SerializeStructFds,
-        SerializeTupleStruct, SerializeTupleStructFds,
+        SerializeAdapter, SerializeStruct, SerializeStructFds, SerializeTupleStruct,
+        SerializeTupleStructFds,
     },
     DeserializeWithFds, DeserializerWithFds, FdSerializer, SerializeWithFds, Serializer, Socket,
 };
-use serde::de::{Deserializer, SeqAccess};
 use sys_util::net::UnixSeqpacket;
 
 #[derive(Debug)]
-struct Inner(RawFd, u16);
+struct Inner(File, u16);
 
 impl SerializeWithFds for Inner {
     fn serialize<Ser: Serializer>(&self, serializer: Ser) -> Result<Ser::Ok, Ser::Error> {
@@ -25,14 +23,43 @@ impl SerializeWithFds for Inner {
 
     fn serialize_fds<Ser: FdSerializer>(&self, serializer: Ser) -> Result<Ser::Ok, Ser::Error> {
         let mut state = serializer.serialize_tuple_struct("Inner", 1)?;
-        state.serialize_field(&SerializeRawFd::new(&self.0))?;
+        state.serialize_field(&self.0)?;
         state.end()
     }
 }
 
+impl<'de> DeserializeWithFds<'de> for Inner {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: DeserializerWithFds<'de>,
+    {
+        struct Visitor;
+
+        impl<'de> VisitorWithFds<'de> for Visitor {
+            type Value = Inner;
+
+            fn expecting(&self, f: &mut Formatter) -> fmt::Result {
+                write!(f, "struct Inner")
+            }
+
+            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
+            where
+                A: SeqAccessWithFds<'de>,
+            {
+                Ok(Inner(
+                    seq.next_element()?.unwrap(),
+                    seq.next_element()?.unwrap(),
+                ))
+            }
+        }
+
+        deserializer.deserialize_tuple_struct("Inner", 2, Visitor)
+    }
+}
+
 #[derive(Debug)]
 struct Test {
-    fd: RawFd,
+    fd: File,
     inner: Inner,
 }
 
@@ -46,44 +73,38 @@ impl SerializeWithFds for Test {
 
     fn serialize_fds<Ser: FdSerializer>(&self, serializer: Ser) -> Result<Ser::Ok, Ser::Error> {
         let mut state = serializer.serialize_struct("Test", 2)?;
-        state.serialize_field("fd", &SerializeRawFd::new(&self.fd))?;
+        state.serialize_field("fd", &self.fd)?;
         state.serialize_field("inner", &self.inner)?;
         state.end()
     }
 }
 
 impl<'de> DeserializeWithFds<'de> for Test {
-    fn deserialize<I, De>(deserializer: DeserializerWithFds<I, De>) -> Result<Self, De::Error>
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
     where
-        I: Iterator<Item = RawFd>,
-        De: Deserializer<'de>,
+        D: DeserializerWithFds<'de>,
     {
-        struct Visitor<'iter, 'de, Iter>(&'iter mut Iter, PhantomData<&'de ()>);
+        struct Visitor;
 
-        impl<'iter, 'de, Iter: Iterator<Item = RawFd>> serde::de::Visitor<'de>
-            for Visitor<'iter, 'de, Iter>
-        {
+        impl<'de> VisitorWithFds<'de> for Visitor {
             type Value = Test;
 
             fn expecting(&self, f: &mut Formatter) -> fmt::Result {
                 write!(f, "struct Test")
             }
 
-            fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
+            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
+            where
+                A: SeqAccessWithFds<'de>,
+            {
                 Ok(Test {
-                    fd: self.0.next().unwrap(),
-                    inner: Inner(self.0.next().unwrap(), seq.next_element()?.unwrap()),
+                    fd: seq.next_element()?.unwrap(),
+                    inner: seq.next_element()?.unwrap(),
                 })
             }
         }
 
-        let DeserializerWithFds {
-            mut fds,
-            deserializer,
-        } = deserializer;
-
-        let visitor = Visitor(&mut fds, PhantomData);
-        deserializer.deserialize_struct("Test", &["fd", "inner"], visitor)
+        deserializer.deserialize_struct("Test", &["fd", "inner"], Visitor)
     }
 }
 
@@ -94,8 +115,8 @@ fn round_trip() {
     let s2: Socket<(), Test> = Socket::new(f2);
 
     s1.send(Test {
-        fd: 0,
-        inner: Inner(1, 0xACAB),
+        fd: File::open("/dev/null").unwrap(),
+        inner: Inner(File::open("/dev/null").unwrap(), 0xACAB),
     })
     .unwrap();
 
diff --git a/vm_control/Cargo.toml b/vm_control/Cargo.toml
index 3a6ec3f..8bde359 100644
--- a/vm_control/Cargo.toml
+++ b/vm_control/Cargo.toml
@@ -10,6 +10,5 @@ kvm = { path = "../kvm" }
 libc = "*"
 msg_socket = { path = "../msg_socket" }
 msg_socket2 = { path = "../msg_socket2" }
-msg_socket2_derive = { path = "../msg_socket2/derive" }
 resources = { path = "../resources" }
 sys_util = { path = "../sys_util" }
diff --git a/vm_control/src/lib.rs b/vm_control/src/lib.rs
index 3ec6be7..99fae7e 100644
--- a/vm_control/src/lib.rs
+++ b/vm_control/src/lib.rs
@@ -20,22 +20,22 @@ use libc::{EINVAL, EIO, ENODEV};
 
 use kvm::{IrqRoute, IrqSource, Vm};
 use msg_socket::{MsgError, MsgOnSocket, MsgResult, MsgSocket, UnixSeqpacketExt};
-use msg_socket2_derive::SerializeWithFds;
+use msg_socket2::{DeserializeWithFds, SerializeWithFds};
 use resources::{Alloc, GpuMemoryDesc, MmioType, SystemAllocator};
 use sys_util::net::UnixSeqpacket;
 use sys_util::{error, Error as SysError, EventFd, GuestAddress, MemoryMapping, MmapError, Result};
 
 /// A data structure that either owns or borrows a file descriptor.
-#[derive(SerializeWithFds, Debug)]
+#[derive(Debug, DeserializeWithFds, SerializeWithFds)]
 #[msg_socket2(strategy = "AsRawFd")]
-pub enum MaybeOwnedFd<Owned: AsRawFd> {
+pub enum MaybeOwnedFd<Owned: AsRawFd + FromRawFd> {
     /// Owned by this enum variant, and will be destructed automatically if not moved out.
     Owned(Owned),
     /// A file descriptor borrwed by this enum.
     Borrowed(RawFd),
 }
 
-impl<Owned: AsRawFd> MaybeOwnedFd<Owned> {
+impl<Owned: AsRawFd + FromRawFd> MaybeOwnedFd<Owned> {
     pub fn new_borrowed(fd: &dyn AsRawFd) -> Self {
         Self::Borrowed(fd.as_raw_fd())
     }
@@ -48,7 +48,7 @@ impl<Owned: AsRawFd> MaybeOwnedFd<Owned> {
     }
 }
 
-impl<Owned: AsRawFd> AsRawFd for MaybeOwnedFd<Owned> {
+impl<Owned: AsRawFd + FromRawFd> AsRawFd for MaybeOwnedFd<Owned> {
     fn as_raw_fd(&self) -> RawFd {
         match self {
             MaybeOwnedFd::Owned(f) => f.as_raw_fd(),
@@ -57,6 +57,12 @@ impl<Owned: AsRawFd> AsRawFd for MaybeOwnedFd<Owned> {
     }
 }
 
+impl<Owned: AsRawFd + FromRawFd> FromRawFd for MaybeOwnedFd<Owned> {
+    unsafe fn from_raw_fd(fd: RawFd) -> Self {
+        Self::Owned(Owned::from_raw_fd(fd))
+    }
+}
+
 // When sent, it could be owned or borrowed. On the receiver end, it always owned.
 impl<Owned: AsRawFd + FromRawFd + MsgOnSocket> MsgOnSocket for MaybeOwnedFd<Owned> {
     fn uses_fd() -> bool {