Snap for 10453563 from 7d7d6b1b864f59139bad9bed64193786e1367bfe to mainline-media-release

Change-Id: Ie29c51fbb9519de59f62ad1ccb740e69caa51cca
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index 90eb8fa..b9cb2ab 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,6 +1,6 @@
 {
   "git": {
-    "sha1": "afba7c5a33dd4fd62b047e64089487cf822ccec2"
+    "sha1": "1e3f062fd842b7ce130ea6c792a8eab7f78f82e3"
   },
   "path_in_vcs": ""
 }
\ No newline at end of file
diff --git a/Android.bp b/Android.bp
index 88940a1..f44d482 100644
--- a/Android.bp
+++ b/Android.bp
@@ -26,21 +26,61 @@
     host_supported: true,
     crate_name: "nix",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.23.1",
+    cargo_pkg_version: "0.26.2",
     srcs: ["src/lib.rs"],
     edition: "2018",
+    features: [
+        "acct",
+        "aio",
+        "default",
+        "dir",
+        "env",
+        "event",
+        "feature",
+        "fs",
+        "hostname",
+        "inotify",
+        "ioctl",
+        "kmod",
+        "memoffset",
+        "mman",
+        "mount",
+        "mqueue",
+        "net",
+        "personality",
+        "pin-utils",
+        "poll",
+        "process",
+        "pthread",
+        "ptrace",
+        "quota",
+        "reboot",
+        "resource",
+        "sched",
+        "signal",
+        "socket",
+        "term",
+        "time",
+        "ucontext",
+        "uio",
+        "user",
+        "zerocopy",
+    ],
     rustlibs: [
-        "libbitflags",
+        "libbitflags-1.3.2",
         "libcfg_if",
         "liblibc",
         "libmemoffset",
+        "libpin_utils",
+        "libstatic_assertions",
     ],
     apex_available: [
         "//apex_available:platform",
-        "com.android.bluetooth",
+        "com.android.btservices",
         "com.android.compos",
         "com.android.virt",
     ],
+    product_available: true,
     vendor_available: true,
     min_sdk_version: "29",
 }
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 77d5b2a..283cb86 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,9 +3,276 @@
 All notable changes to this project will be documented in this file.
 This project adheres to [Semantic Versioning](https://semver.org/).
 
+## [0.26.2] - 2023-01-18
+### Fixed
+- Fix `SockaddrIn6` bug that was swapping flowinfo and scope_id byte ordering.
+  ([#1964](https://github.com/nix-rust/nix/pull/1964))
+
+## [0.26.1] - 2022-11-29
+### Fixed
+- Fix UB with `sys::socket::sockopt::SockType` using `SOCK_PACKET`.
+  ([#1821](https://github.com/nix-rust/nix/pull/1821))
+
+## [0.26.0] - 2022-11-29
+### Added
+
+- Added `SockaddrStorage::{as_unix_addr, as_unix_addr_mut}`
+  ([#1871](https://github.com/nix-rust/nix/pull/1871))
+- Added `MntFlags` and `unmount` on all of the BSDs.
+- Added `any()` and `all()` to `poll::PollFd`.
+  ([#1877](https://github.com/nix-rust/nix/pull/1877))
+- Add `MntFlags` and `unmount` on all of the BSDs.
+  ([#1849](https://github.com/nix-rust/nix/pull/1849))
+- Added a `Statfs::flags` method.
+  ([#1849](https://github.com/nix-rust/nix/pull/1849))
+- Added `NSFS_MAGIC` FsType on Linux and Android.
+  ([#1829](https://github.com/nix-rust/nix/pull/1829))
+- Added `sched_getcpu` on platforms that support it.
+  ([#1825](https://github.com/nix-rust/nix/pull/1825))
+- Added `sched_getaffinity` and `sched_setaffinity` on FreeBSD.
+  ([#1804](https://github.com/nix-rust/nix/pull/1804))
+- Added `line_discipline` field to `Termios` on Linux, Android and Haiku
+  ([#1805](https://github.com/nix-rust/nix/pull/1805))
+- Expose the memfd module on FreeBSD (memfd was added in FreeBSD 13)
+  ([#1808](https://github.com/nix-rust/nix/pull/1808))
+- Added `domainname` field of `UtsName` on Android and Linux
+  ([#1817](https://github.com/nix-rust/nix/pull/1817))
+- Re-export `RLIM_INFINITY` from `libc`
+  ([#1831](https://github.com/nix-rust/nix/pull/1831))
+- Added `syncfs(2)` on Linux
+  ([#1833](https://github.com/nix-rust/nix/pull/1833))
+- Added `faccessat(2)` on illumos
+  ([#1841](https://github.com/nix-rust/nix/pull/1841))
+- Added `eaccess()` on FreeBSD, DragonFly and Linux (glibc and musl).
+  ([#1842](https://github.com/nix-rust/nix/pull/1842))
+- Added `IP_TOS` `SO_PRIORITY` and `IPV6_TCLASS` sockopts for Linux
+  ([#1853](https://github.com/nix-rust/nix/pull/1853))
+- Added `new_unnamed` and `is_unnamed` for `UnixAddr` on Linux and Android.
+  ([#1857](https://github.com/nix-rust/nix/pull/1857))
+- Added `SockProtocol::Raw` for raw sockets
+  ([#1848](https://github.com/nix-rust/nix/pull/1848))
+- added `IP_MTU` (`IpMtu`) `IPPROTO_IP` sockopt on Linux and Android.
+  ([#1865](https://github.com/nix-rust/nix/pull/1865))
+
+### Changed
+
+- The MSRV is now 1.56.1
+  ([#1792](https://github.com/nix-rust/nix/pull/1792))
+- The `addr` argument of `sys::mman::mmap` is now of type `Option<NonZeroUsize>`.
+  ([#1870](https://github.com/nix-rust/nix/pull/1870))
+- The `length` argument of `sys::mman::mmap` is now of type `NonZeroUsize`.
+  ([#1873](https://github.com/nix-rust/nix/pull/1873))
+
+### Fixed
+
+- Fixed using `SockaddrStorage` to store a Unix-domain socket address on Linux.
+  ([#1871](https://github.com/nix-rust/nix/pull/1871))
+- Fix microsecond calculation for `TimeSpec`.
+  ([#1801](https://github.com/nix-rust/nix/pull/1801))
+- Fix `User::from_name` and `Group::from_name` panicking
+  when given a name containing a nul.
+  ([#1815](https://github.com/nix-rust/nix/pull/1815))
+- Fix `User::from_uid` and `User::from_name` crash on Android platform.
+  ([#1824](https://github.com/nix-rust/nix/pull/1824))
+- Workaround XNU bug causing netmasks returned by `getifaddrs` to misbehave.
+  ([#1788](https://github.com/nix-rust/nix/pull/1788))
+  
+### Removed
+
+- Removed deprecated error constants and conversions.
+  ([#1860](https://github.com/nix-rust/nix/pull/1860))
+
+## [0.25.0] - 2022-08-13
+### Added
+
+- Added `faccessat`
+  ([#1780](https://github.com/nix-rust/nix/pull/1780))
+- Added `memfd` on Android.
+  (#[1773](https://github.com/nix-rust/nix/pull/1773))
+- Added `ETH_P_ALL` to `SockProtocol` enum
+  (#[1768](https://github.com/nix-rust/nix/pull/1768))
+- Added four non-standard Linux `SysconfVar` variants
+  (#[1761](https://github.com/nix-rust/nix/pull/1761))
+- Added const constructors for `TimeSpec` and `TimeVal`
+  (#[1760](https://github.com/nix-rust/nix/pull/1760))
+- Added `chflags`.
+  (#[1758](https://github.com/nix-rust/nix/pull/1758))
+- Added `aio_writev` and `aio_readv`.
+  (#[1713](https://github.com/nix-rust/nix/pull/1713))
+- impl `From<uid_t>` for `Uid` and `From<gid_t>` for `Gid`
+  (#[1727](https://github.com/nix-rust/nix/pull/1727))
+- impl `From<SockaddrIn>` for `std::net::SocketAddrV4` and
+  impl `From<SockaddrIn6>` for `std::net::SocketAddrV6`.
+  (#[1711](https://github.com/nix-rust/nix/pull/1711))
+- Added support for the `x86_64-unknown-haiku` target.
+  (#[1703](https://github.com/nix-rust/nix/pull/1703))
+- Added `ptrace::read_user` and `ptrace::write_user` for Linux.
+  (#[1697](https://github.com/nix-rust/nix/pull/1697))
+- Added `getrusage` and helper types `UsageWho` and `Usage`
+  (#[1747](https://github.com/nix-rust/nix/pull/1747))
+- Added the `DontRoute` SockOpt
+  (#[1752](https://github.com/nix-rust/nix/pull/1752))
+- Added `signal::SigSet::from_sigset_t_unchecked()`.
+  (#[1741](https://github.com/nix-rust/nix/pull/1741))
+- Added the `Ipv4OrigDstAddr` sockopt and control message.
+  (#[1772](https://github.com/nix-rust/nix/pull/1772))
+- Added the `Ipv6OrigDstAddr` sockopt and control message.
+  (#[1772](https://github.com/nix-rust/nix/pull/1772))
+- Added the `Ipv4SendSrcAddr` control message.
+  (#[1776](https://github.com/nix-rust/nix/pull/1776))
+
+### Changed
+
+- Reimplemented sendmmsg/recvmmsg to avoid allocations and with better API
+  (#[1744](https://github.com/nix-rust/nix/pull/1744))
+
+- Rewrote the aio module.  The new module:
+  * Does more type checking at compile time rather than runtime.
+  * Gives the caller control over whether and when to `Box` an aio operation.
+  * Changes the type of the `priority` arguments to `i32`.
+  * Changes the return type of `aio_return` to `usize`.
+  (#[1713](https://github.com/nix-rust/nix/pull/1713))
+- `nix::poll::ppoll`: `sigmask` parameter is now optional.
+  (#[1739](https://github.com/nix-rust/nix/pull/1739))
+- Changed `gethostname` to return an owned `OsString`.
+  (#[1745](https://github.com/nix-rust/nix/pull/1745))
+- `signal:SigSet` is now marked as `repr(transparent)`.
+  (#[1741](https://github.com/nix-rust/nix/pull/1741))
+
+### Removed
+
+- Removed support for resubmitting partially complete `lio_listio` operations.
+  It was too complicated, and didn't fit Nix's theme of zero-cost abstractions.
+  Instead, it can be reimplemented downstream.
+  (#[1713](https://github.com/nix-rust/nix/pull/1713))
+
+## [0.24.2] - 2022-07-17
+### Fixed
+
+- Fixed buffer overflow in `nix::sys::socket::recvfrom`.
+  (#[1763](https://github.com/nix-rust/nix/pull/1763))
+- Enabled `SockaddrStorage::{as_link_addr, as_link_addr_mut}` for Linux-like
+  operating systems.
+  (#[1729](https://github.com/nix-rust/nix/pull/1729))
+- Fixed `SockaddrLike::from_raw` implementations for `VsockAddr` and
+  `SysControlAddr`.
+  (#[1736](https://github.com/nix-rust/nix/pull/1736))
+
+## [0.24.1] - 2022-04-22
+### Fixed
+
+- Fixed `UnixAddr::size` on Linux-based OSes.
+  (#[1702](https://github.com/nix-rust/nix/pull/1702))
+
+## [0.24.0] - 2022-04-21
+### Added
+
+- Added fine-grained features flags.  Most Nix functionality can now be
+  conditionally enabled.  By default, all features are enabled.
+  (#[1611](https://github.com/nix-rust/nix/pull/1611))
+- Added statfs FS type magic constants for `target_os = "android"`
+  and synced constants with libc v0.2.121.
+  (#[1690](https://github.com/nix-rust/nix/pull/1690))
+- Added `fexecve` on DragonFly.
+  (#[1577](https://github.com/nix-rust/nix/pull/1577))
+- `sys::uio::IoVec` is now `Send` and `Sync`
+  (#[1582](https://github.com/nix-rust/nix/pull/1582))
+- Added `EPOLLEXCLUSIVE` on Android.
+  (#[1567](https://github.com/nix-rust/nix/pull/1567))
+- Added `fdatasync` for FreeBSD, Fuchsia, NetBSD, and OpenBSD.
+  (#[1581](https://github.com/nix-rust/nix/pull/1581))
+- Added `sched_setaffinity` and `sched_getaffinity` on DragonFly.
+  (#[1537](https://github.com/nix-rust/nix/pull/1537))
+- Added `posix_fallocate` on DragonFly.
+  (#[1621](https://github.com/nix-rust/nix/pull/1621))
+- Added `SO_TIMESTAMPING` support
+  (#[1547](https://github.com/nix-rust/nix/pull/1547))
+- Added getter methods to `MqAttr` struct
+  (#[1619](https://github.com/nix-rust/nix/pull/1619))
+- Added the `TxTime` sockopt and control message.
+  (#[1564](https://github.com/nix-rust/nix/pull/1564))
+- Added POSIX per-process timer support
+  (#[1622](https://github.com/nix-rust/nix/pull/1622))
+- Added `sendfile` on DragonFly.
+  (#[1615](https://github.com/nix-rust/nix/pull/1615))
+- Added `UMOUNT_NOFOLLOW`, `FUSE_SUPER_MAGIC` on Linux.
+  (#[1634](https://github.com/nix-rust/nix/pull/1634))
+- Added `getresuid`, `setresuid`, `getresgid`, and `setresgid` on DragonFly, FreeBSD, and OpenBSD.
+  (#[1628](https://github.com/nix-rust/nix/pull/1628))
+- Added `MAP_FIXED_NOREPLACE` on Linux.
+  (#[1636](https://github.com/nix-rust/nix/pull/1636))
+- Added `fspacectl` on FreeBSD
+  (#[1640](https://github.com/nix-rust/nix/pull/1640))
+- Added `accept4` on DragonFly, Emscripten, Fuchsia, Illumos, and NetBSD.
+  (#[1654](https://github.com/nix-rust/nix/pull/1654))
+- Added `AsRawFd` implementation on `OwningIter`.
+  (#[1563](https://github.com/nix-rust/nix/pull/1563))
+- Added `process_vm_readv` and `process_vm_writev` on Android.
+  (#[1557](https://github.com/nix-rust/nix/pull/1557))
+- Added `nix::uncontext` module on s390x.
+  (#[1662](https://github.com/nix-rust/nix/pull/1662))
+- Implemented `Extend`, `FromIterator`, and `IntoIterator` for `SigSet` and
+  added `SigSet::iter` and `SigSetIter`.
+  (#[1553](https://github.com/nix-rust/nix/pull/1553))
+- Added `ENOTRECOVERABLE` and `EOWNERDEAD` error codes on DragonFly.
+  (#[1665](https://github.com/nix-rust/nix/pull/1665))
+- Implemented `Read` and `Write` for `&PtyMaster`
+  (#[1664](https://github.com/nix-rust/nix/pull/1664))
+- Added `MSG_NOSIGNAL` for Android, Dragonfly, FreeBSD, Fuchsia, Haiku, Illumos, Linux, NetBSD, OpenBSD and Solaris.
+  (#[1670](https://github.com/nix-rust/nix/pull/1670))
+- Added `waitid`.
+  (#[1584](https://github.com/nix-rust/nix/pull/1584))
+- Added `Ipv6DontFrag` for android, iOS, linux and macOS.
+- Added `IpDontFrag` for iOS, macOS.
+  (#[1692](https://github.com/nix-rust/nix/pull/1692))
+
+### Changed
+
+- `mqueue` functions now operate on a distinct type, `nix::mqueue::MqdT`.
+  Accessors take this type by reference, not by value.
+  (#[1639](https://github.com/nix-rust/nix/pull/1639))
+- Removed `SigSet::extend` in favor of `<SigSet as Extend<Signal>>::extend`.
+  Because of this change, you now need `use std::iter::Extend` to call `extend`
+  on a `SigSet`.
+  (#[1553](https://github.com/nix-rust/nix/pull/1553))
+- Removed the the `PATH_MAX` restriction from APIs accepting paths. Paths
+  will now be allocated on the heap if they are too long. In addition, large
+  instruction count improvements (~30x) were made to path handling.
+  (#[1656](https://github.com/nix-rust/nix/pull/1656))
+- Changed `getrlimit` and `setrlimit` to use `rlim_t` directly
+  instead of `Option<rlim_t>`.
+  (#[1668](https://github.com/nix-rust/nix/pull/1668))
+- Deprecated `InetAddr` and `SockAddr` in favor of `SockaddrIn`, `SockaddrIn6`,
+  and `SockaddrStorage`.
+  (#[1684](https://github.com/nix-rust/nix/pull/1684))
+- Deprecated `IpAddr`, `Ipv4Addr`, and `Ipv6Addr` in favor of their equivalents
+  from the standard library.
+  (#[1685](https://github.com/nix-rust/nix/pull/1685))
+- `uname` now returns a `Result<UtsName>` instead of just a `UtsName` and
+  ignoring failures from libc.  And getters on the `UtsName` struct now return
+  an `&OsStr` instead of `&str`.
+  (#[1672](https://github.com/nix-rust/nix/pull/1672))
+- Replaced `IoVec` with `IoSlice` and `IoSliceMut`, and replaced `IoVec::from_slice` with
+  `IoSlice::new`. (#[1643](https://github.com/nix-rust/nix/pull/1643))
+
+### Fixed
+
+- `InetAddr::from_std` now sets the `sin_len`/`sin6_len` fields on the BSDs.
+  (#[1642](https://github.com/nix-rust/nix/pull/1642))
+- Fixed a panic in `LinkAddr::addr`.  That function now returns an `Option`.
+  (#[1675](https://github.com/nix-rust/nix/pull/1675))
+  (#[1677](https://github.com/nix-rust/nix/pull/1677))
+
+### Removed
+
+- Removed public access to the inner fields of `NetlinkAddr`, `AlgAddr`,
+  `SysControlAddr`, `LinkAddr`, and `VsockAddr`.
+  (#[1614](https://github.com/nix-rust/nix/pull/1614))
+- Removed `EventFlag::EV_SYSFLAG`.
+  (#[1635](https://github.com/nix-rust/nix/pull/1635))
+
 ## [0.23.1] - 2021-12-16
 
-### Added
 ### Changed
 
 - Relaxed the bitflags requirement from 1.3.1 to 1.1.  This partially reverts
@@ -20,8 +287,6 @@
   `0..FD_SETSIZE`.
   (#[1575](https://github.com/nix-rust/nix/pull/1575))
 
-### Removed
-
 ## [0.23.0] - 2021-09-28
 ### Added
 
@@ -130,6 +395,30 @@
 - Removed `SigevNotify` on OpenBSD and Redox.
   (#[1511](https://github.com/nix-rust/nix/pull/1511))
 
+## [0.22.3] - 22 January 2022
+### Changed
+- Relaxed the bitflags requirement from 1.3.1 to 1.1.  This partially reverts
+  #1492.  From now on, the MSRV is not guaranteed to work with all versions of
+  all dependencies, just with some version of all dependencies.
+  (#[1607](https://github.com/nix-rust/nix/pull/1607))
+
+## [0.22.2] - 28 September 2021
+### Fixed
+- Fixed buffer overflow in `unistd::getgrouplist`.
+  (#[1545](https://github.com/nix-rust/nix/pull/1545))
+- Added more errno definitions for better backwards compatibility with
+  Nix 0.21.0.
+  (#[1467](https://github.com/nix-rust/nix/pull/1467))
+
+## [0.22.1] - 13 August 2021
+### Fixed
+- Locked bitflags to < 1.3.0 to fix the build with rust < 1.46.0.
+
+### Removed
+- Removed a couple of termios constants on redox that were never actually
+  supported.
+  (#[1483](https://github.com/nix-rust/nix/pull/1483))
+
 ## [0.22.0] - 9 July 2021
 ### Added
 - Added `if_nameindex` (#[1445](https://github.com/nix-rust/nix/pull/1445))
@@ -155,8 +444,19 @@
   `nix::Error::EINVAL`.
   ([#1446](https://github.com/nix-rust/nix/pull/1446))
 
+## [0.21.2] - 29 September 2021
 ### Fixed
+- Fixed buffer overflow in `unistd::getgrouplist`.
+  (#[1545](https://github.com/nix-rust/nix/pull/1545))
+
+## [0.21.1] - 13 August 2021
+### Fixed
+- Locked bitflags to < 1.3.0 to fix the build with rust < 1.46.0.
+
 ### Removed
+- Removed a couple of termios constants on redox that were never actually
+  supported.
+  (#[1483](https://github.com/nix-rust/nix/pull/1483))
 
 ## [0.21.0] - 31 May 2021
 ### Added
@@ -212,6 +512,20 @@
 - Removed some Errno values from platforms where they aren't actually defined.
   (#[1452](https://github.com/nix-rust/nix/pull/1452))
 
+## [0.20.2] - 28 September 2021
+### Fixed
+- Fixed buffer overflow in `unistd::getgrouplist`.
+  (#[1545](https://github.com/nix-rust/nix/pull/1545))
+
+## [0.20.1] - 13 August 2021
+### Fixed
+- Locked bitflags to < 1.3.0 to fix the build with rust < 1.46.0.
+
+### Removed
+- Removed a couple of termios constants on redox that were never actually
+  supported.
+  (#[1483](https://github.com/nix-rust/nix/pull/1483))
+
 ## [0.20.0] - 20 February 2021
 ### Added
 
@@ -275,8 +589,6 @@
   (#[1278](https://github.com/nix-rust/nix/pull/1278))
 - Made `unistd::fork` an unsafe funtion, bringing it in line with [libstd's decision](https://github.com/rust-lang/rust/pull/58059).
   (#[1293](https://github.com/nix-rust/nix/pull/1293))
-### Fixed
-### Removed
 
 ## [0.18.0] - 26 July 2020
 ### Added
@@ -389,22 +701,16 @@
 ### Added
 - Add `CLK_TCK` to `SysconfVar`
   (#[1177](https://github.com/nix-rust/nix/pull/1177))
-### Changed
-### Fixed
 ### Removed
 - Removed deprecated Error::description from error types
   (#[1175](https://github.com/nix-rust/nix/pull/1175))
 
 ## [0.16.1] - 23 December 2019
-### Added
-### Changed
 ### Fixed
 
 - Fixed the build for OpenBSD
   (#[1168](https://github.com/nix-rust/nix/pull/1168))
 
-### Removed
-
 ## [0.16.0] - 1 December 2019
 ### Added
 - Added `ptrace::seize()`: similar to `attach()` on Linux
@@ -532,8 +838,6 @@
 - Enabled `sched_yield` for all nix hosts.
   ([#1090](https://github.com/nix-rust/nix/pull/1090))
 
-### Removed
-
 ## [0.14.1] - 2019-06-06
 ### Added
 - Macros exported by `nix` may now be imported via `use` on the Rust 2018
@@ -558,8 +862,6 @@
 - Fix the build on Android and Linux/mips with recent versions of libc.
   ([#1072](https://github.com/nix-rust/nix/pull/1072/commits))
 
-### Removed
-
 ## [0.14.0] - 2019-05-21
 ### Added
 - Add IP_RECVIF & IP_RECVDSTADDR. Enable IP_PKTINFO and IP6_PKTINFO on netbsd/openbsd.
@@ -628,6 +930,23 @@
   should've been defined in the first place.
   ([#1055](https://github.com/nix-rust/nix/pull/1055))
 
+## [0.13.1] - 2019-06-10
+### Changed
+- Changed some public types from reexports of libc types like `uint32_t` to the
+  native equivalents like `u32.`
+  ([#1072](https://github.com/nix-rust/nix/pull/1072/commits))
+
+### Fixed
+- Fix the build on Android and Linux/mips with recent versions of libc.
+  ([#1072](https://github.com/nix-rust/nix/pull/1072/commits))
+- Fixed build on Linux/arm and Linux/s390x with the latest Rust libc
+  ([52102cb](https://github.com/nix-rust/nix/commit/52102cb76398c4dfb9ea141b98c5b01a2e050973))
+
+### Removed
+- `Daemon`, `NOTE_REAP`, and `NOTE_EXIT_REPARENTED` are now deprecated on OSX
+  and iOS.
+  ([#1033](https://github.com/nix-rust/nix/pull/1033))
+
 ## [0.13.0] - 2019-01-15
 ### Added
 - Added PKTINFO(V4) & V6PKTINFO cmsg support - Android/FreeBSD/iOS/Linux/MacOS.
@@ -645,14 +964,30 @@
 - Added an `mprotect` wrapper.
   ([#991](https://github.com/nix-rust/nix/pull/991))
 
-### Changed
 ### Fixed
 - `lutimes` never worked on OpenBSD as it is not implemented on OpenBSD. It has
   been removed. ([#1000](https://github.com/nix-rust/nix/pull/1000))
 - `fexecve` never worked on NetBSD or on OpenBSD as it is not implemented on
   either OS. It has been removed. ([#1000](https://github.com/nix-rust/nix/pull/1000))
 
+## [0.12.1] 2019-06-08
+### Changed
+- Changed some public types from reexports of libc types like `uint32_t` to the
+  native equivalents like `u32.`
+  ([#1072](https://github.com/nix-rust/nix/pull/1072/commits))
+
+### Fixed
+- Fix the build on Android and Linux/mips with recent versions of libc.
+  ([#1072](https://github.com/nix-rust/nix/pull/1072/commits))
+- Fixed build on Linux/arm and Linux/s390x with the latest Rust libc
+  ([52102cb](https://github.com/nix-rust/nix/commit/52102cb76398c4dfb9ea141b98c5b01a2e050973))
+
 ### Removed
+- `fexecve` never worked on NetBSD or on OpenBSD as it is not implemented on
+  either OS. It has been removed. ([#1000](https://github.com/nix-rust/nix/pull/1000))
+- `Daemon`, `NOTE_REAP`, and `NOTE_EXIT_REPARENTED` are now deprecated on OSX
+  and iOS.
+  ([#1033](https://github.com/nix-rust/nix/pull/1033))
 
 ## [0.12.0] 2018-11-28
 
@@ -704,7 +1039,24 @@
 - Fixed passing multiple file descriptors over Unix Sockets.
   ([#918](https://github.com/nix-rust/nix/pull/918))
 
+## [0.11.1] 2019-06-06
+### Changed
+- Changed some public types from reexports of libc types like `uint32_t` to the
+  native equivalents like `u32.`
+  ([#1072](https://github.com/nix-rust/nix/pull/1072/commits))
+
+### Fixed
+- Fix the build on Android and Linux/mips with recent versions of libc.
+  ([#1072](https://github.com/nix-rust/nix/pull/1072/commits))
+- Fixed build on Linux/arm and Linux/s390x with the latest Rust libc
+  ([52102cb](https://github.com/nix-rust/nix/commit/52102cb76398c4dfb9ea141b98c5b01a2e050973))
+
 ### Removed
+- `fexecve` never worked on NetBSD or on OpenBSD as it is not implemented on
+  either OS. It has been removed. ([#1000](https://github.com/nix-rust/nix/pull/1000))
+- `Daemon`, `NOTE_REAP`, and `NOTE_EXIT_REPARENTED` are now deprecated on OSX
+  and iOS.
+  ([#1033](https://github.com/nix-rust/nix/pull/1033))
 
 ## [0.11.0] 2018-06-01
 
diff --git a/Cargo.toml b/Cargo.toml
index 122c1af..0afc445 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -11,17 +11,41 @@
 
 [package]
 edition = "2018"
-rust-version = "1.46"
+rust-version = "1.56"
 name = "nix"
-version = "0.23.1"
+version = "0.26.2"
 authors = ["The nix-rust Project Developers"]
-include = ["src/**/*", "test/**/*", "LICENSE", "README.md", "CHANGELOG.md"]
+include = [
+    "src/**/*",
+    "test/**/*",
+    "LICENSE",
+    "README.md",
+    "CHANGELOG.md",
+]
 description = "Rust friendly bindings to *nix APIs"
+readme = "README.md"
 categories = ["os::unix-apis"]
 license = "MIT"
 repository = "https://github.com/nix-rust/nix"
+
 [package.metadata.docs.rs]
-targets = ["x86_64-unknown-linux-gnu", "aarch64-linux-android", "x86_64-apple-darwin", "aarch64-apple-ios", "x86_64-unknown-freebsd", "x86_64-unknown-openbsd", "x86_64-unknown-netbsd", "x86_64-unknown-dragonfly", "x86_64-fuchsia", "x86_64-unknown-redox", "x86_64-unknown-illumos"]
+rustdoc-args = [
+    "--cfg",
+    "docsrs",
+]
+targets = [
+    "x86_64-unknown-linux-gnu",
+    "aarch64-linux-android",
+    "x86_64-apple-darwin",
+    "aarch64-apple-ios",
+    "x86_64-unknown-freebsd",
+    "x86_64-unknown-openbsd",
+    "x86_64-unknown-netbsd",
+    "x86_64-unknown-dragonfly",
+    "x86_64-fuchsia",
+    "x86_64-unknown-redox",
+    "x86_64-unknown-illumos",
+]
 
 [[test]]
 name = "test"
@@ -36,10 +60,6 @@
 path = "test/test_clearenv.rs"
 
 [[test]]
-name = "test-lio-listio-resubmit"
-path = "test/sys/test_lio_listio_resubmit.rs"
-
-[[test]]
 name = "test-mount"
 path = "test/test_mount.rs"
 harness = false
@@ -47,6 +67,7 @@
 [[test]]
 name = "test-ptymaster-drop"
 path = "test/test_ptymaster_drop.rs"
+
 [dependencies.bitflags]
 version = "1.1"
 
@@ -54,30 +75,111 @@
 version = "1.0"
 
 [dependencies.libc]
-version = "0.2.102"
+version = "0.2.137"
 features = ["extra_traits"]
+
+[dependencies.pin-utils]
+version = "0.1.0"
+optional = true
+
+[dependencies.static_assertions]
+version = "1"
+
 [dev-dependencies.assert-impl]
 version = "0.1"
 
 [dev-dependencies.lazy_static]
-version = "1.2"
+version = "1.4"
 
 [dev-dependencies.parking_lot]
-version = "0.11.2"
+version = "0.12"
 
 [dev-dependencies.rand]
 version = "0.8"
 
 [dev-dependencies.semver]
-version = "1.0.0"
+version = "1.0.7"
 
 [dev-dependencies.tempfile]
-version = "3.2.0"
+version = "3.3.0"
+
+[features]
+acct = []
+aio = ["pin-utils"]
+default = [
+    "acct",
+    "aio",
+    "dir",
+    "env",
+    "event",
+    "feature",
+    "fs",
+    "hostname",
+    "inotify",
+    "ioctl",
+    "kmod",
+    "mman",
+    "mount",
+    "mqueue",
+    "net",
+    "personality",
+    "poll",
+    "process",
+    "pthread",
+    "ptrace",
+    "quota",
+    "reboot",
+    "resource",
+    "sched",
+    "signal",
+    "socket",
+    "term",
+    "time",
+    "ucontext",
+    "uio",
+    "user",
+    "zerocopy",
+]
+dir = ["fs"]
+env = []
+event = []
+feature = []
+fs = []
+hostname = []
+inotify = []
+ioctl = []
+kmod = []
+mman = []
+mount = ["uio"]
+mqueue = ["fs"]
+net = ["socket"]
+personality = []
+poll = []
+process = []
+pthread = []
+ptrace = ["process"]
+quota = []
+reboot = []
+resource = []
+sched = ["process"]
+signal = ["process"]
+socket = ["memoffset"]
+term = []
+time = []
+ucontext = ["signal"]
+uio = []
+user = ["feature"]
+zerocopy = [
+    "fs",
+    "uio",
+]
+
 [target."cfg(any(target_os = \"android\", target_os = \"linux\"))".dev-dependencies.caps]
-version = "0.5.1"
+version = "0.5.3"
+
 [target."cfg(not(target_os = \"redox\"))".dependencies.memoffset]
-version = "0.6.3"
-[target."cfg(target_os = \"dragonfly\")".build-dependencies.cc]
-version = "1"
+version = "0.7"
+optional = true
+
 [target."cfg(target_os = \"freebsd\")".dev-dependencies.sysctl]
-version = "0.1"
+version = "0.4"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index d2ca8ee..8b1d873 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -2,8 +2,8 @@
 name        = "nix"
 description = "Rust friendly bindings to *nix APIs"
 edition     = "2018"
-version     = "0.23.1"
-rust-version = "1.46"
+version     = "0.26.2"
+rust-version = "1.56"
 authors     = ["The nix-rust Project Developers"]
 repository  = "https://github.com/nix-rust/nix"
 license     = "MIT"
@@ -11,6 +11,7 @@
 include = ["src/**/*", "test/**/*", "LICENSE", "README.md", "CHANGELOG.md"]
 
 [package.metadata.docs.rs]
+rustdoc-args = ["--cfg", "docsrs"]
 targets = [
   "x86_64-unknown-linux-gnu",
   "aarch64-linux-android",
@@ -26,29 +27,70 @@
 ]
 
 [dependencies]
-libc = { version = "0.2.102", features = [ "extra_traits" ] }
+libc = { version = "0.2.137", features = [ "extra_traits" ] }
 bitflags = "1.1"
 cfg-if = "1.0"
+pin-utils = { version = "0.1.0", optional = true }
+static_assertions = "1"
 
 [target.'cfg(not(target_os = "redox"))'.dependencies]
-memoffset = "0.6.3"
+memoffset = { version = "0.7", optional = true }
 
-[target.'cfg(target_os = "dragonfly")'.build-dependencies]
-cc = "1"
+[features]
+default = [
+  "acct", "aio", "dir", "env", "event", "feature", "fs",
+  "hostname", "inotify", "ioctl", "kmod", "mman", "mount", "mqueue",
+  "net", "personality", "poll", "process", "pthread", "ptrace", "quota",
+  "reboot", "resource", "sched", "signal", "socket", "term", "time",
+  "ucontext", "uio", "user", "zerocopy",
+]
+
+acct = []
+aio = ["pin-utils"]
+dir = ["fs"]
+env = []
+event = []
+feature = []
+fs = []
+hostname = []
+inotify = []
+ioctl = []
+kmod = []
+mman = []
+mount = ["uio"]
+mqueue = ["fs"]
+net = ["socket"]
+personality = []
+poll = []
+pthread = []
+ptrace = ["process"]
+quota = []
+process = []
+reboot = []
+resource = []
+sched = ["process"]
+signal = ["process"]
+socket = ["memoffset"]
+term = []
+time = []
+ucontext = ["signal"]
+uio = []
+user = ["feature"]
+zerocopy = ["fs", "uio"]
 
 [dev-dependencies]
 assert-impl = "0.1"
-lazy_static = "1.2"
-parking_lot = "0.11.2"
+lazy_static = "1.4"
+parking_lot = "0.12"
 rand = "0.8"
-tempfile = "3.2.0"
-semver = "1.0.0"
+tempfile = "3.3.0"
+semver = "1.0.7"
 
 [target.'cfg(any(target_os = "android", target_os = "linux"))'.dev-dependencies]
-caps = "0.5.1"
+caps = "0.5.3"
 
 [target.'cfg(target_os = "freebsd")'.dev-dependencies]
-sysctl = "0.1"
+sysctl = "0.4"
 
 [[test]]
 name = "test"
@@ -63,10 +105,6 @@
 path = "test/test_clearenv.rs"
 
 [[test]]
-name = "test-lio-listio-resubmit"
-path = "test/sys/test_lio_listio_resubmit.rs"
-
-[[test]]
 name = "test-mount"
 path = "test/test_mount.rs"
 harness = false
diff --git a/METADATA b/METADATA
index e0fef6c..b1b992d 100644
--- a/METADATA
+++ b/METADATA
@@ -1,3 +1,7 @@
+# This project was upgraded with external_updater.
+# Usage: tools/external_updater/updater.sh update rust/crates/nix
+# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md
+
 name: "nix"
 description: "Rust friendly bindings to *nix APIs"
 third_party {
@@ -7,13 +11,13 @@
   }
   url {
     type: ARCHIVE
-    value: "https://static.crates.io/crates/nix/nix-0.23.1.crate"
+    value: "https://static.crates.io/crates/nix/nix-0.26.2.crate"
   }
-  version: "0.23.1"
+  version: "0.26.2"
   license_type: NOTICE
   last_upgrade_date {
-    year: 2022
-    month: 3
+    year: 2023
+    month: 2
     day: 16
   }
 }
diff --git a/README.md b/README.md
index a8759f1..2c42b90 100644
--- a/README.md
+++ b/README.md
@@ -24,8 +24,8 @@
 // libc api (unsafe, requires handling return code/errno)
 pub unsafe extern fn gethostname(name: *mut c_char, len: size_t) -> c_int;
 
-// nix api (returns a nix::Result<CStr>)
-pub fn gethostname<'a>(buffer: &'a mut [u8]) -> Result<&'a CStr>;
+// nix api (returns a nix::Result<OsString>)
+pub fn gethostname() -> Result<OsString>;
 ```
 
 ## Supported Platforms
@@ -47,6 +47,7 @@
 The following targets are supported by `nix`:
 
 Tier 1:
+  * aarch64-apple-darwin
   * aarch64-unknown-linux-gnu
   * arm-unknown-linux-gnueabi
   * armv7-unknown-linux-gnueabihf
@@ -58,7 +59,6 @@
   * mips64el-unknown-linux-gnuabi64
   * mipsel-unknown-linux-gnu
   * powerpc64le-unknown-linux-gnu
-  * x86_64-apple-darwin
   * x86_64-unknown-freebsd
   * x86_64-unknown-linux-gnu
   * x86_64-unknown-linux-musl
@@ -74,19 +74,22 @@
   * s390x-unknown-linux-gnu
   * x86_64-apple-ios
   * x86_64-linux-android
+  * x86_64-apple-darwin
   * x86_64-unknown-illumos
   * x86_64-unknown-netbsd
 
 Tier 3:
+  * armv7-unknown-linux-uclibceabihf
   * x86_64-fuchsia
   * x86_64-unknown-dragonfly
+  * x86_64-unknown-haiku
   * x86_64-unknown-linux-gnux32
   * x86_64-unknown-openbsd
   * x86_64-unknown-redox
 
 ## Minimum Supported Rust Version (MSRV)
 
-nix is supported on Rust 1.46.0 and higher.  It's MSRV will not be
+nix is supported on Rust 1.56.1 and higher.  Its MSRV will not be
 changed in the future without bumping the major or minor version.
 
 ## Contributing
diff --git a/TEST_MAPPING b/TEST_MAPPING
index b44b9e2..10e0efb 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -3,100 +3,45 @@
   "imports": [
     {
       "path": "external/rust/crates/tokio"
-    }
-  ],
-  "presubmit": [
-    {
-      "name": "ZipFuseTest"
     },
     {
-      "name": "apkdmverity.test"
+      "path": "packages/modules/Virtualization/apkdmverity"
     },
     {
-      "name": "authfs_device_test_src_lib"
+      "path": "packages/modules/Virtualization/authfs"
     },
     {
-      "name": "diced_open_dice_cbor_test"
+      "path": "packages/modules/Virtualization/encryptedstore"
     },
     {
-      "name": "diced_sample_inputs_test"
+      "path": "packages/modules/Virtualization/libs/capabilities"
     },
     {
-      "name": "diced_test"
+      "path": "packages/modules/Virtualization/libs/devicemapper"
     },
     {
-      "name": "diced_utils_test"
+      "path": "packages/modules/Virtualization/microdroid_manager"
     },
     {
-      "name": "diced_vendor_test"
+      "path": "packages/modules/Virtualization/virtualizationmanager"
     },
     {
-      "name": "keystore2_crypto_test_rust"
+      "path": "packages/modules/Virtualization/vm"
     },
     {
-      "name": "keystore2_selinux_concurrency_test"
+      "path": "packages/modules/Virtualization/zipfuse"
     },
     {
-      "name": "keystore2_test"
+      "path": "system/security/diced"
     },
     {
-      "name": "keystore2_test_utils_test"
+      "path": "system/security/keystore2"
     },
     {
-      "name": "legacykeystore_test"
+      "path": "system/security/keystore2/legacykeystore"
     },
     {
-      "name": "microdroid_manager_test"
-    },
-    {
-      "name": "virtualizationservice_device_test"
-    }
-  ],
-  "presubmit-rust": [
-    {
-      "name": "ZipFuseTest"
-    },
-    {
-      "name": "apkdmverity.test"
-    },
-    {
-      "name": "authfs_device_test_src_lib"
-    },
-    {
-      "name": "diced_open_dice_cbor_test"
-    },
-    {
-      "name": "diced_sample_inputs_test"
-    },
-    {
-      "name": "diced_test"
-    },
-    {
-      "name": "diced_utils_test"
-    },
-    {
-      "name": "diced_vendor_test"
-    },
-    {
-      "name": "keystore2_crypto_test_rust"
-    },
-    {
-      "name": "keystore2_selinux_concurrency_test"
-    },
-    {
-      "name": "keystore2_test"
-    },
-    {
-      "name": "keystore2_test_utils_test"
-    },
-    {
-      "name": "legacykeystore_test"
-    },
-    {
-      "name": "microdroid_manager_test"
-    },
-    {
-      "name": "virtualizationservice_device_test"
+      "path": "system/security/keystore2/src/crypto"
     }
   ]
 }
diff --git a/cargo2android.json b/cargo2android.json
index f8fb37c..ece6226 100644
--- a/cargo2android.json
+++ b/cargo2android.json
@@ -1,7 +1,7 @@
 {
   "apex-available": [
     "//apex_available:platform",
-    "com.android.bluetooth",
+    "com.android.btservices",
     "com.android.compos",
     "com.android.virt"
   ],
diff --git a/patches/Android.bp.diff b/patches/Android.bp.diff
new file mode 100644
index 0000000..c7025be
--- /dev/null
+++ b/patches/Android.bp.diff
@@ -0,0 +1,13 @@
+diff --git a/Android.bp b/Android.bp
+index f471209..f44d482 100644
+--- a/Android.bp
++++ b/Android.bp
+@@ -67,7 +67,7 @@ rust_library {
+         "zerocopy",
+     ],
+     rustlibs: [
+-        "libbitflags",
++        "libbitflags-1.3.2",
+         "libcfg_if",
+         "liblibc",
+         "libmemoffset",
diff --git a/patches/memfd.diff b/patches/memfd.diff
new file mode 100644
index 0000000..a5d4609
--- /dev/null
+++ b/patches/memfd.diff
@@ -0,0 +1,15 @@
+diff --git a/src/sys/memfd.rs b/src/sys/memfd.rs
+index ad9345e..e43e1e5 100644
+--- a/src/sys/memfd.rs
++++ b/src/sys/memfd.rs
+@@ -49,7 +49,9 @@ pub fn memfd_create(name: &CStr, flags: MemFdCreateFlag) -> Result<RawFd> {
+                 any(
+                     target_os = "freebsd",
+                     // If the OS is Linux, gnu and musl expose a memfd_create symbol but not uclibc
+-                    target_env = "gnu",
++                    //
++                    // ANDROID: Our glibc is too old to have memfd_create.
++                    // target_env = "gnu",
+                     target_env = "musl",
+                 )))]
+             {
diff --git a/patches/unistd.diff b/patches/unistd.diff
new file mode 100644
index 0000000..4815309
--- /dev/null
+++ b/patches/unistd.diff
@@ -0,0 +1,22 @@
+diff --git a/src/unistd.rs b/src/unistd.rs
+index 42e1456..8cdb54b 100644
+--- a/src/unistd.rs
++++ b/src/unistd.rs
+@@ -2984,12 +2984,12 @@ impl From<&libc::passwd> for User {
+     fn from(pw: &libc::passwd) -> User {
+         unsafe {
+             User {
+-                name: CStr::from_ptr(pw.pw_name).to_string_lossy().into_owned(),
+-                passwd: CString::new(CStr::from_ptr(pw.pw_passwd).to_bytes()).unwrap(),
++                name: if pw.pw_name.is_null() { Default::default() } else { CStr::from_ptr(pw.pw_name).to_string_lossy().into_owned() },
++                passwd: if pw.pw_passwd.is_null() { Default::default() } else { CString::new(CStr::from_ptr(pw.pw_passwd).to_bytes()).unwrap() },
+                 #[cfg(not(all(target_os = "android", target_pointer_width = "32")))]
+-                gecos: CString::new(CStr::from_ptr(pw.pw_gecos).to_bytes()).unwrap(),
+-                dir: PathBuf::from(OsStr::from_bytes(CStr::from_ptr(pw.pw_dir).to_bytes())),
+-                shell: PathBuf::from(OsStr::from_bytes(CStr::from_ptr(pw.pw_shell).to_bytes())),
++                gecos: if pw.pw_gecos.is_null() { Default::default() } else { CString::new(CStr::from_ptr(pw.pw_gecos).to_bytes()).unwrap() },
++                dir: if pw.pw_dir.is_null() { Default::default() } else { PathBuf::from(OsStr::from_bytes(CStr::from_ptr(pw.pw_dir).to_bytes())) },
++                shell: if pw.pw_shell.is_null() { Default::default() } else { PathBuf::from(OsStr::from_bytes(CStr::from_ptr(pw.pw_shell).to_bytes())) },
+                 uid: Uid::from_raw(pw.pw_uid),
+                 gid: Gid::from_raw(pw.pw_gid),
+                 #[cfg(not(any(target_os = "android",
diff --git a/src/dir.rs b/src/dir.rs
index ed70a45..5ce5036 100644
--- a/src/dir.rs
+++ b/src/dir.rs
@@ -1,10 +1,13 @@
-use crate::{Error, NixPath, Result};
+//! List directory contents
+
 use crate::errno::Errno;
 use crate::fcntl::{self, OFlag};
+use crate::sys;
+use crate::{Error, NixPath, Result};
+use cfg_if::cfg_if;
+use std::ffi;
 use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
 use std::ptr;
-use std::ffi;
-use crate::sys;
 
 #[cfg(target_os = "linux")]
 use libc::{dirent64 as dirent, readdir64_r as readdir_r};
@@ -26,21 +29,26 @@
 ///    * returns entries' names as a `CStr` (no allocation or conversion beyond whatever libc
 ///      does).
 #[derive(Debug, Eq, Hash, PartialEq)]
-pub struct Dir(
-    ptr::NonNull<libc::DIR>
-);
+pub struct Dir(ptr::NonNull<libc::DIR>);
 
 impl Dir {
     /// Opens the given path as with `fcntl::open`.
-    pub fn open<P: ?Sized + NixPath>(path: &P, oflag: OFlag,
-                                     mode: sys::stat::Mode) -> Result<Self> {
+    pub fn open<P: ?Sized + NixPath>(
+        path: &P,
+        oflag: OFlag,
+        mode: sys::stat::Mode,
+    ) -> Result<Self> {
         let fd = fcntl::open(path, oflag, mode)?;
         Dir::from_fd(fd)
     }
 
     /// Opens the given path as with `fcntl::openat`.
-    pub fn openat<P: ?Sized + NixPath>(dirfd: RawFd, path: &P, oflag: OFlag,
-                                       mode: sys::stat::Mode) -> Result<Self> {
+    pub fn openat<P: ?Sized + NixPath>(
+        dirfd: RawFd,
+        path: &P,
+        oflag: OFlag,
+        mode: sys::stat::Mode,
+    ) -> Result<Self> {
         let fd = fcntl::openat(dirfd, path, oflag, mode)?;
         Dir::from_fd(fd)
     }
@@ -52,12 +60,15 @@
     }
 
     /// Converts from a file descriptor, closing it on success or failure.
+    #[doc(alias("fdopendir"))]
     pub fn from_fd(fd: RawFd) -> Result<Self> {
-        let d = ptr::NonNull::new(unsafe { libc::fdopendir(fd) }).ok_or_else(|| {
-            let e = Error::last();
-            unsafe { libc::close(fd) };
-            e
-        })?;
+        let d = ptr::NonNull::new(unsafe { libc::fdopendir(fd) }).ok_or_else(
+            || {
+                let e = Error::last();
+                unsafe { libc::close(fd) };
+                e
+            },
+        )?;
         Ok(Dir(d))
     }
 
@@ -99,9 +110,11 @@
         // Probably fine here too then.
         let mut ent = std::mem::MaybeUninit::<dirent>::uninit();
         let mut result = ptr::null_mut();
-        if let Err(e) = Errno::result(
-            readdir_r(dir.0.as_ptr(), ent.as_mut_ptr(), &mut result))
-        {
+        if let Err(e) = Errno::result(readdir_r(
+            dir.0.as_ptr(),
+            ent.as_mut_ptr(),
+            &mut result,
+        )) {
             return Some(Err(e));
         }
         if result.is_null() {
@@ -112,6 +125,7 @@
     }
 }
 
+/// Return type of [`Dir::iter`].
 #[derive(Debug, Eq, Hash, PartialEq)]
 pub struct Iter<'d>(&'d mut Dir);
 
@@ -141,6 +155,14 @@
     }
 }
 
+/// The file descriptor continues to be owned by the `OwningIter`,
+/// so callers must not keep a `RawFd` after the `OwningIter` is dropped.
+impl AsRawFd for OwningIter {
+    fn as_raw_fd(&self) -> RawFd {
+        self.0.as_raw_fd()
+    }
+}
+
 impl IntoIterator for Dir {
     type Item = Result<Entry>;
     type IntoIter = OwningIter;
@@ -173,47 +195,47 @@
 #[repr(transparent)]
 pub struct Entry(dirent);
 
+/// Type of file referenced by a directory entry
 #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
 pub enum Type {
+    /// FIFO (Named pipe)
     Fifo,
+    /// Character device
     CharacterDevice,
+    /// Directory
     Directory,
+    /// Block device
     BlockDevice,
+    /// Regular file
     File,
+    /// Symbolic link
     Symlink,
+    /// Unix-domain socket
     Socket,
 }
 
 impl Entry {
     /// Returns the inode number (`d_ino`) of the underlying `dirent`.
-    #[cfg(any(target_os = "android",
-              target_os = "emscripten",
-              target_os = "fuchsia",
-              target_os = "haiku",
-              target_os = "illumos",
-              target_os = "ios",
-              target_os = "l4re",
-              target_os = "linux",
-              target_os = "macos",
-              target_os = "solaris"))]
+    #[allow(clippy::useless_conversion)] // Not useless on all OSes
+    // The cast is not unnecessary on all platforms.
+    #[allow(clippy::unnecessary_cast)]
     pub fn ino(&self) -> u64 {
-        self.0.d_ino as u64
-    }
-
-    /// Returns the inode number (`d_fileno`) of the underlying `dirent`.
-    #[cfg(not(any(target_os = "android",
-                  target_os = "emscripten",
-                  target_os = "fuchsia",
-                  target_os = "haiku",
-                  target_os = "illumos",
-                  target_os = "ios",
-                  target_os = "l4re",
-                  target_os = "linux",
-                  target_os = "macos",
-                  target_os = "solaris")))]
-    #[allow(clippy::useless_conversion)]    // Not useless on all OSes
-    pub fn ino(&self) -> u64 {
-        u64::from(self.0.d_fileno)
+        cfg_if! {
+            if #[cfg(any(target_os = "android",
+                         target_os = "emscripten",
+                         target_os = "fuchsia",
+                         target_os = "haiku",
+                         target_os = "illumos",
+                         target_os = "ios",
+                         target_os = "l4re",
+                         target_os = "linux",
+                         target_os = "macos",
+                         target_os = "solaris"))] {
+                self.0.d_ino as u64
+            } else {
+                u64::from(self.0.d_fileno)
+            }
+        }
     }
 
     /// Returns the bare file name of this directory entry without any other leading path component.
@@ -227,7 +249,11 @@
     /// notably, some Linux filesystems don't implement this. The caller should use `stat` or
     /// `fstat` if this returns `None`.
     pub fn file_type(&self) -> Option<Type> {
-        #[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
+        #[cfg(not(any(
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "haiku"
+        )))]
         match self.0.d_type {
             libc::DT_FIFO => Some(Type::Fifo),
             libc::DT_CHR => Some(Type::CharacterDevice),
@@ -239,8 +265,12 @@
             /* libc::DT_UNKNOWN | */ _ => None,
         }
 
-        // illumos and Solaris systems do not have the d_type member at all:
-        #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+        // illumos, Solaris, and Haiku systems do not have the d_type member at all:
+        #[cfg(any(
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "haiku"
+        ))]
         None
     }
 }
diff --git a/src/env.rs b/src/env.rs
index bcae287..95177a1 100644
--- a/src/env.rs
+++ b/src/env.rs
@@ -42,7 +42,6 @@
     cfg_if! {
         if #[cfg(any(target_os = "fuchsia",
                      target_os = "wasi",
-                     target_env = "wasi",
                      target_env = "uclibc",
                      target_os = "linux",
                      target_os = "android",
diff --git a/src/errno.rs b/src/errno.rs
index 3da246e..d8ad28d 100644
--- a/src/errno.rs
+++ b/src/errno.rs
@@ -1,8 +1,8 @@
+use crate::Result;
 use cfg_if::cfg_if;
 use libc::{c_int, c_void};
 use std::convert::TryFrom;
-use std::{fmt, io, error};
-use crate::{Error, Result};
+use std::{error, fmt, io};
 
 pub use self::consts::*;
 
@@ -30,6 +30,10 @@
         unsafe fn errno_location() -> *mut c_int {
             libc::___errno()
         }
+    } else if #[cfg(any(target_os = "haiku",))] {
+        unsafe fn errno_location() -> *mut c_int {
+            libc::_errnop()
+        }
     }
 }
 
@@ -43,49 +47,10 @@
 
 /// Returns the platform-specific value of errno
 pub fn errno() -> i32 {
-    unsafe {
-        (*errno_location()) as i32
-    }
+    unsafe { *errno_location() }
 }
 
 impl Errno {
-    /// Convert this `Error` to an [`Errno`](enum.Errno.html).
-    ///
-    /// # Example
-    ///
-    /// ```
-    /// # use nix::Error;
-    /// # use nix::errno::Errno;
-    /// let e = Error::from(Errno::EPERM);
-    /// assert_eq!(Some(Errno::EPERM), e.as_errno());
-    /// ```
-    #[deprecated(
-        since = "0.22.0",
-        note = "It's a no-op now; just delete it."
-    )]
-    pub const fn as_errno(self) -> Option<Self> {
-        Some(self)
-    }
-
-    /// Create a nix Error from a given errno
-    #[deprecated(
-        since = "0.22.0",
-        note = "It's a no-op now; just delete it."
-    )]
-    #[allow(clippy::wrong_self_convention)] // False positive
-    pub fn from_errno(errno: Errno) -> Error {
-        errno
-    }
-
-    /// Create a new invalid argument error (`EINVAL`)
-    #[deprecated(
-        since = "0.22.0",
-        note = "Use Errno::EINVAL instead"
-    )]
-    pub const fn invalid_argument() -> Error {
-        Errno::EINVAL
-    }
-
     pub fn last() -> Self {
         last()
     }
@@ -112,21 +77,6 @@
             Ok(value)
         }
     }
-
-    /// Backwards compatibility hack for Nix <= 0.21.0 users
-    ///
-    /// In older versions of Nix, `Error::Sys` was an enum variant.  Now it's a
-    /// function, which is compatible with most of the former use cases of the
-    /// enum variant.  But you should use `Error(Errno::...)` instead.
-    #[deprecated(
-        since = "0.22.0",
-        note = "Use Errno::... instead"
-    )]
-    #[allow(non_snake_case)]
-    #[inline]
-    pub const fn Sys(errno: Errno) -> Error {
-        errno
-    }
 }
 
 /// The sentinel value indicates that a function failed and more detailed
@@ -136,23 +86,33 @@
 }
 
 impl ErrnoSentinel for isize {
-    fn sentinel() -> Self { -1 }
+    fn sentinel() -> Self {
+        -1
+    }
 }
 
 impl ErrnoSentinel for i32 {
-    fn sentinel() -> Self { -1 }
+    fn sentinel() -> Self {
+        -1
+    }
 }
 
 impl ErrnoSentinel for i64 {
-    fn sentinel() -> Self { -1 }
+    fn sentinel() -> Self {
+        -1
+    }
 }
 
 impl ErrnoSentinel for *mut c_void {
-    fn sentinel() -> Self { -1isize as *mut c_void }
+    fn sentinel() -> Self {
+        -1isize as *mut c_void
+    }
 }
 
 impl ErrnoSentinel for libc::sighandler_t {
-    fn sentinel() -> Self { libc::SIG_ERR }
+    fn sentinel() -> Self {
+        libc::SIG_ERR
+    }
 }
 
 impl error::Error for Errno {}
@@ -173,9 +133,7 @@
     type Error = io::Error;
 
     fn try_from(ioerror: io::Error) -> std::result::Result<Self, io::Error> {
-        ioerror.raw_os_error()
-            .map(Errno::from_i32)
-            .ok_or(ioerror)
+        ioerror.raw_os_error().map(Errno::from_i32).ok_or(ioerror)
     }
 }
 
@@ -186,748 +144,1123 @@
 fn desc(errno: Errno) -> &'static str {
     use self::Errno::*;
     match errno {
-        UnknownErrno    => "Unknown errno",
-        EPERM           => "Operation not permitted",
-        ENOENT          => "No such file or directory",
-        ESRCH           => "No such process",
-        EINTR           => "Interrupted system call",
-        EIO             => "I/O error",
-        ENXIO           => "No such device or address",
-        E2BIG           => "Argument list too long",
-        ENOEXEC         => "Exec format error",
-        EBADF           => "Bad file number",
-        ECHILD          => "No child processes",
-        EAGAIN          => "Try again",
-        ENOMEM          => "Out of memory",
-        EACCES          => "Permission denied",
-        EFAULT          => "Bad address",
-        ENOTBLK         => "Block device required",
-        EBUSY           => "Device or resource busy",
-        EEXIST          => "File exists",
-        EXDEV           => "Cross-device link",
-        ENODEV          => "No such device",
-        ENOTDIR         => "Not a directory",
-        EISDIR          => "Is a directory",
-        EINVAL          => "Invalid argument",
-        ENFILE          => "File table overflow",
-        EMFILE          => "Too many open files",
-        ENOTTY          => "Not a typewriter",
-        ETXTBSY         => "Text file busy",
-        EFBIG           => "File too large",
-        ENOSPC          => "No space left on device",
-        ESPIPE          => "Illegal seek",
-        EROFS           => "Read-only file system",
-        EMLINK          => "Too many links",
-        EPIPE           => "Broken pipe",
-        EDOM            => "Math argument out of domain of func",
-        ERANGE          => "Math result not representable",
-        EDEADLK         => "Resource deadlock would occur",
-        ENAMETOOLONG    => "File name too long",
-        ENOLCK          => "No record locks available",
-        ENOSYS          => "Function not implemented",
-        ENOTEMPTY       => "Directory not empty",
-        ELOOP           => "Too many symbolic links encountered",
-        ENOMSG          => "No message of desired type",
-        EIDRM           => "Identifier removed",
-        EINPROGRESS     => "Operation now in progress",
-        EALREADY        => "Operation already in progress",
-        ENOTSOCK        => "Socket operation on non-socket",
-        EDESTADDRREQ    => "Destination address required",
-        EMSGSIZE        => "Message too long",
-        EPROTOTYPE      => "Protocol wrong type for socket",
-        ENOPROTOOPT     => "Protocol not available",
+        UnknownErrno => "Unknown errno",
+        EPERM => "Operation not permitted",
+        ENOENT => "No such file or directory",
+        ESRCH => "No such process",
+        EINTR => "Interrupted system call",
+        EIO => "I/O error",
+        ENXIO => "No such device or address",
+        E2BIG => "Argument list too long",
+        ENOEXEC => "Exec format error",
+        EBADF => "Bad file number",
+        ECHILD => "No child processes",
+        EAGAIN => "Try again",
+        ENOMEM => "Out of memory",
+        EACCES => "Permission denied",
+        EFAULT => "Bad address",
+        #[cfg(not(target_os = "haiku"))]
+        ENOTBLK => "Block device required",
+        EBUSY => "Device or resource busy",
+        EEXIST => "File exists",
+        EXDEV => "Cross-device link",
+        ENODEV => "No such device",
+        ENOTDIR => "Not a directory",
+        EISDIR => "Is a directory",
+        EINVAL => "Invalid argument",
+        ENFILE => "File table overflow",
+        EMFILE => "Too many open files",
+        ENOTTY => "Not a typewriter",
+        ETXTBSY => "Text file busy",
+        EFBIG => "File too large",
+        ENOSPC => "No space left on device",
+        ESPIPE => "Illegal seek",
+        EROFS => "Read-only file system",
+        EMLINK => "Too many links",
+        EPIPE => "Broken pipe",
+        EDOM => "Math argument out of domain of func",
+        ERANGE => "Math result not representable",
+        EDEADLK => "Resource deadlock would occur",
+        ENAMETOOLONG => "File name too long",
+        ENOLCK => "No record locks available",
+        ENOSYS => "Function not implemented",
+        ENOTEMPTY => "Directory not empty",
+        ELOOP => "Too many symbolic links encountered",
+        ENOMSG => "No message of desired type",
+        EIDRM => "Identifier removed",
+        EINPROGRESS => "Operation now in progress",
+        EALREADY => "Operation already in progress",
+        ENOTSOCK => "Socket operation on non-socket",
+        EDESTADDRREQ => "Destination address required",
+        EMSGSIZE => "Message too long",
+        EPROTOTYPE => "Protocol wrong type for socket",
+        ENOPROTOOPT => "Protocol not available",
         EPROTONOSUPPORT => "Protocol not supported",
+        #[cfg(not(target_os = "haiku"))]
         ESOCKTNOSUPPORT => "Socket type not supported",
-        EPFNOSUPPORT    => "Protocol family not supported",
-        EAFNOSUPPORT    => "Address family not supported by protocol",
-        EADDRINUSE      => "Address already in use",
-        EADDRNOTAVAIL   => "Cannot assign requested address",
-        ENETDOWN        => "Network is down",
-        ENETUNREACH     => "Network is unreachable",
-        ENETRESET       => "Network dropped connection because of reset",
-        ECONNABORTED    => "Software caused connection abort",
-        ECONNRESET      => "Connection reset by peer",
-        ENOBUFS         => "No buffer space available",
-        EISCONN         => "Transport endpoint is already connected",
-        ENOTCONN        => "Transport endpoint is not connected",
-        ESHUTDOWN       => "Cannot send after transport endpoint shutdown",
-        ETOOMANYREFS    => "Too many references: cannot splice",
-        ETIMEDOUT       => "Connection timed out",
-        ECONNREFUSED    => "Connection refused",
-        EHOSTDOWN       => "Host is down",
-        EHOSTUNREACH    => "No route to host",
+        #[cfg(not(target_os = "haiku"))]
+        EPFNOSUPPORT => "Protocol family not supported",
+        #[cfg(not(target_os = "haiku"))]
+        EAFNOSUPPORT => "Address family not supported by protocol",
+        EADDRINUSE => "Address already in use",
+        EADDRNOTAVAIL => "Cannot assign requested address",
+        ENETDOWN => "Network is down",
+        ENETUNREACH => "Network is unreachable",
+        ENETRESET => "Network dropped connection because of reset",
+        ECONNABORTED => "Software caused connection abort",
+        ECONNRESET => "Connection reset by peer",
+        ENOBUFS => "No buffer space available",
+        EISCONN => "Transport endpoint is already connected",
+        ENOTCONN => "Transport endpoint is not connected",
+        ESHUTDOWN => "Cannot send after transport endpoint shutdown",
+        #[cfg(not(target_os = "haiku"))]
+        ETOOMANYREFS => "Too many references: cannot splice",
+        ETIMEDOUT => "Connection timed out",
+        ECONNREFUSED => "Connection refused",
+        EHOSTDOWN => "Host is down",
+        EHOSTUNREACH => "No route to host",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        ECHRNG          => "Channel number out of range",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        ECHRNG => "Channel number out of range",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        EL2NSYNC        => "Level 2 not synchronized",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        EL2NSYNC => "Level 2 not synchronized",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        EL3HLT          => "Level 3 halted",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        EL3HLT => "Level 3 halted",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        EL3RST          => "Level 3 reset",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        EL3RST => "Level 3 reset",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        ELNRNG          => "Link number out of range",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        ELNRNG => "Link number out of range",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        EUNATCH         => "Protocol driver not attached",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        EUNATCH => "Protocol driver not attached",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        ENOCSI          => "No CSI structure available",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        ENOCSI => "No CSI structure available",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        EL2HLT          => "Level 2 halted",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        EL2HLT => "Level 2 halted",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        EBADE           => "Invalid exchange",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        EBADE => "Invalid exchange",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        EBADR           => "Invalid request descriptor",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        EBADR => "Invalid request descriptor",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        EXFULL          => "Exchange full",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        EXFULL => "Exchange full",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        ENOANO          => "No anode",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        ENOANO => "No anode",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        EBADRQC         => "Invalid request code",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        EBADRQC => "Invalid request code",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        EBADSLT         => "Invalid slot",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        EBADSLT => "Invalid slot",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        EBFONT          => "Bad font file format",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        EBFONT => "Bad font file format",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        ENOSTR          => "Device not a stream",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        ENOSTR => "Device not a stream",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        ENODATA         => "No data available",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        ENODATA => "No data available",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        ETIME           => "Timer expired",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        ETIME => "Timer expired",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        ENOSR           => "Out of streams resources",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        ENOSR => "Out of streams resources",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        ENONET          => "Machine is not on the network",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        ENONET => "Machine is not on the network",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        ENOPKG          => "Package not installed",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        ENOPKG => "Package not installed",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        EREMOTE         => "Object is remote",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        EREMOTE => "Object is remote",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        ENOLINK         => "Link has been severed",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        ENOLINK => "Link has been severed",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        EADV            => "Advertise error",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        EADV => "Advertise error",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        ESRMNT          => "Srmount error",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        ESRMNT => "Srmount error",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        ECOMM           => "Communication error on send",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        ECOMM => "Communication error on send",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        EPROTO          => "Protocol error",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        EPROTO => "Protocol error",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        EMULTIHOP       => "Multihop attempted",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        EMULTIHOP => "Multihop attempted",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "fuchsia"))]
-        EDOTDOT         => "RFS specific error",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "fuchsia"
+        ))]
+        EDOTDOT => "RFS specific error",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "fuchsia"))]
-        EBADMSG         => "Not a data message",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "fuchsia"
+        ))]
+        EBADMSG => "Not a data message",
 
         #[cfg(any(target_os = "illumos", target_os = "solaris"))]
-        EBADMSG         => "Trying to read unreadable message",
+        EBADMSG => "Trying to read unreadable message",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "fuchsia"))]
-        EOVERFLOW       => "Value too large for defined data type",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "fuchsia",
+            target_os = "haiku"
+        ))]
+        EOVERFLOW => "Value too large for defined data type",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        ENOTUNIQ        => "Name not unique on network",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        ENOTUNIQ => "Name not unique on network",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        EBADFD          => "File descriptor in bad state",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        EBADFD => "File descriptor in bad state",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        EREMCHG         => "Remote address changed",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        EREMCHG => "Remote address changed",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        ELIBACC         => "Can not access a needed shared library",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        ELIBACC => "Can not access a needed shared library",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        ELIBBAD         => "Accessing a corrupted shared library",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        ELIBBAD => "Accessing a corrupted shared library",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        ELIBSCN         => ".lib section in a.out corrupted",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        ELIBSCN => ".lib section in a.out corrupted",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        ELIBMAX         => "Attempting to link in too many shared libraries",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        ELIBMAX => "Attempting to link in too many shared libraries",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        ELIBEXEC        => "Cannot exec a shared library directly",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        ELIBEXEC => "Cannot exec a shared library directly",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia", target_os = "openbsd"))]
-        EILSEQ          => "Illegal byte sequence",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia",
+            target_os = "openbsd"
+        ))]
+        EILSEQ => "Illegal byte sequence",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        ERESTART        => "Interrupted system call should be restarted",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        ERESTART => "Interrupted system call should be restarted",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        ESTRPIPE        => "Streams pipe error",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        ESTRPIPE => "Streams pipe error",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        EUSERS          => "Too many users",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia"
+        ))]
+        EUSERS => "Too many users",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "fuchsia", target_os = "netbsd",
-                  target_os = "redox"))]
-        EOPNOTSUPP      => "Operation not supported on transport endpoint",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "fuchsia",
+            target_os = "netbsd",
+            target_os = "redox"
+        ))]
+        EOPNOTSUPP => "Operation not supported on transport endpoint",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "fuchsia"))]
-        ESTALE          => "Stale file handle",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "fuchsia"
+        ))]
+        ESTALE => "Stale file handle",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "fuchsia"))]
-        EUCLEAN         => "Structure needs cleaning",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "fuchsia"
+        ))]
+        EUCLEAN => "Structure needs cleaning",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "fuchsia"))]
-        ENOTNAM         => "Not a XENIX named type file",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "fuchsia"
+        ))]
+        ENOTNAM => "Not a XENIX named type file",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "fuchsia"))]
-        ENAVAIL         => "No XENIX semaphores available",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "fuchsia"
+        ))]
+        ENAVAIL => "No XENIX semaphores available",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "fuchsia"))]
-        EISNAM          => "Is a named type file",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "fuchsia"
+        ))]
+        EISNAM => "Is a named type file",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "fuchsia"))]
-        EREMOTEIO       => "Remote I/O error",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "fuchsia"
+        ))]
+        EREMOTEIO => "Remote I/O error",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "fuchsia"))]
-        EDQUOT          => "Quota exceeded",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "fuchsia"
+        ))]
+        EDQUOT => "Quota exceeded",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "fuchsia", target_os = "openbsd",
-                  target_os = "dragonfly"))]
-        ENOMEDIUM       => "No medium found",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "fuchsia",
+            target_os = "openbsd",
+            target_os = "dragonfly"
+        ))]
+        ENOMEDIUM => "No medium found",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "fuchsia", target_os = "openbsd"))]
-        EMEDIUMTYPE     => "Wrong medium type",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "fuchsia",
+            target_os = "openbsd"
+        ))]
+        EMEDIUMTYPE => "Wrong medium type",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "illumos", target_os = "solaris",
-                  target_os = "fuchsia"))]
-        ECANCELED       => "Operation canceled",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "fuchsia",
+            target_os = "haiku"
+        ))]
+        ECANCELED => "Operation canceled",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "fuchsia"))]
-        ENOKEY          => "Required key not available",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "fuchsia"
+        ))]
+        ENOKEY => "Required key not available",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "fuchsia"))]
-        EKEYEXPIRED     => "Key has expired",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "fuchsia"
+        ))]
+        EKEYEXPIRED => "Key has expired",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "fuchsia"))]
-        EKEYREVOKED     => "Key has been revoked",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "fuchsia"
+        ))]
+        EKEYREVOKED => "Key has been revoked",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "fuchsia"))]
-        EKEYREJECTED    => "Key was rejected by service",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "fuchsia"
+        ))]
+        EKEYREJECTED => "Key was rejected by service",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "fuchsia"))]
-        EOWNERDEAD      => "Owner died",
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "fuchsia"
+        ))]
+        EOWNERDEAD => "Owner died",
 
-        #[cfg(any( target_os = "illumos", target_os = "solaris"))]
-        EOWNERDEAD      => "Process died with lock",
+        #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+        EOWNERDEAD => "Process died with lock",
 
-        #[cfg(any(target_os = "linux", target_os = "android",
-                  target_os = "fuchsia"))]
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "fuchsia"
+        ))]
         ENOTRECOVERABLE => "State not recoverable",
 
         #[cfg(any(target_os = "illumos", target_os = "solaris"))]
         ENOTRECOVERABLE => "Lock is not recoverable",
 
-        #[cfg(any(all(target_os = "linux", not(target_arch="mips")),
-                  target_os = "fuchsia"))]
-        ERFKILL         => "Operation not possible due to RF-kill",
+        #[cfg(any(
+            all(target_os = "linux", not(target_arch = "mips")),
+            target_os = "fuchsia"
+        ))]
+        ERFKILL => "Operation not possible due to RF-kill",
 
-        #[cfg(any(all(target_os = "linux", not(target_arch="mips")),
-                  target_os = "fuchsia"))]
-        EHWPOISON       => "Memory page has hardware error",
+        #[cfg(any(
+            all(target_os = "linux", not(target_arch = "mips")),
+            target_os = "fuchsia"
+        ))]
+        EHWPOISON => "Memory page has hardware error",
 
         #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
-        EDOOFUS         => "Programming error",
+        EDOOFUS => "Programming error",
 
-        #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "redox"))]
-        EMULTIHOP       => "Multihop attempted",
+        #[cfg(any(
+            target_os = "freebsd",
+            target_os = "dragonfly",
+            target_os = "redox"
+        ))]
+        EMULTIHOP => "Multihop attempted",
 
-        #[cfg(any(target_os = "freebsd", target_os = "dragonfly",
-                  target_os = "redox"))]
-        ENOLINK         => "Link has been severed",
+        #[cfg(any(
+            target_os = "freebsd",
+            target_os = "dragonfly",
+            target_os = "redox"
+        ))]
+        ENOLINK => "Link has been severed",
 
         #[cfg(target_os = "freebsd")]
-        ENOTCAPABLE     => "Capabilities insufficient",
+        ENOTCAPABLE => "Capabilities insufficient",
 
         #[cfg(target_os = "freebsd")]
-        ECAPMODE        => "Not permitted in capability mode",
+        ECAPMODE => "Not permitted in capability mode",
 
-        #[cfg(any(target_os = "macos", target_os = "freebsd",
-                  target_os = "dragonfly", target_os = "ios",
-                  target_os = "openbsd", target_os = "netbsd"))]
-        ENEEDAUTH       => "Need authenticator",
+        #[cfg(any(
+            target_os = "macos",
+            target_os = "freebsd",
+            target_os = "dragonfly",
+            target_os = "ios",
+            target_os = "openbsd",
+            target_os = "netbsd"
+        ))]
+        ENEEDAUTH => "Need authenticator",
 
-        #[cfg(any(target_os = "macos", target_os = "freebsd",
-                  target_os = "dragonfly", target_os = "ios",
-                  target_os = "openbsd", target_os = "netbsd",
-                  target_os = "redox", target_os = "illumos",
-                  target_os = "solaris"))]
-        EOVERFLOW       => "Value too large to be stored in data type",
+        #[cfg(any(
+            target_os = "macos",
+            target_os = "freebsd",
+            target_os = "dragonfly",
+            target_os = "ios",
+            target_os = "openbsd",
+            target_os = "netbsd",
+            target_os = "redox",
+            target_os = "illumos",
+            target_os = "solaris"
+        ))]
+        EOVERFLOW => "Value too large to be stored in data type",
 
-        #[cfg(any(target_os = "macos", target_os = "freebsd",
-                  target_os = "dragonfly", target_os = "ios",
-                  target_os = "netbsd", target_os = "redox"))]
-        EILSEQ          => "Illegal byte sequence",
+        #[cfg(any(
+            target_os = "macos",
+            target_os = "freebsd",
+            target_os = "dragonfly",
+            target_os = "ios",
+            target_os = "netbsd",
+            target_os = "redox",
+            target_os = "haiku"
+        ))]
+        EILSEQ => "Illegal byte sequence",
 
-        #[cfg(any(target_os = "macos", target_os = "freebsd",
-                  target_os = "dragonfly", target_os = "ios",
-                  target_os = "openbsd", target_os = "netbsd"))]
-        ENOATTR         => "Attribute not found",
+        #[cfg(any(
+            target_os = "macos",
+            target_os = "freebsd",
+            target_os = "dragonfly",
+            target_os = "ios",
+            target_os = "openbsd",
+            target_os = "netbsd",
+            target_os = "haiku"
+        ))]
+        ENOATTR => "Attribute not found",
 
-        #[cfg(any(target_os = "macos", target_os = "freebsd",
-                  target_os = "dragonfly", target_os = "ios",
-                  target_os = "openbsd", target_os = "netbsd",
-                  target_os = "redox"))]
-        EBADMSG         => "Bad message",
+        #[cfg(any(
+            target_os = "macos",
+            target_os = "freebsd",
+            target_os = "dragonfly",
+            target_os = "ios",
+            target_os = "openbsd",
+            target_os = "netbsd",
+            target_os = "redox",
+            target_os = "haiku"
+        ))]
+        EBADMSG => "Bad message",
 
-        #[cfg(any(target_os = "macos", target_os = "freebsd",
-                  target_os = "dragonfly", target_os = "ios",
-                  target_os = "openbsd", target_os = "netbsd",
-                  target_os = "redox"))]
-        EPROTO          => "Protocol error",
+        #[cfg(any(
+            target_os = "macos",
+            target_os = "freebsd",
+            target_os = "dragonfly",
+            target_os = "ios",
+            target_os = "openbsd",
+            target_os = "netbsd",
+            target_os = "redox",
+            target_os = "haiku"
+        ))]
+        EPROTO => "Protocol error",
 
-        #[cfg(any(target_os = "macos", target_os = "freebsd",
-                  target_os = "ios", target_os = "openbsd"))]
+        #[cfg(any(
+            target_os = "macos",
+            target_os = "freebsd",
+            target_os = "dragonfly",
+            target_os = "ios",
+            target_os = "openbsd"
+        ))]
         ENOTRECOVERABLE => "State not recoverable",
 
-        #[cfg(any(target_os = "macos", target_os = "freebsd",
-                  target_os = "ios", target_os = "openbsd"))]
-        EOWNERDEAD      => "Previous owner died",
+        #[cfg(any(
+            target_os = "macos",
+            target_os = "freebsd",
+            target_os = "dragonfly",
+            target_os = "ios",
+            target_os = "openbsd"
+        ))]
+        EOWNERDEAD => "Previous owner died",
 
-        #[cfg(any(target_os = "macos", target_os = "freebsd",
-                  target_os = "dragonfly", target_os = "ios",
-                  target_os = "openbsd", target_os = "netbsd",
-                  target_os = "illumos", target_os = "solaris"))]
-        ENOTSUP         => "Operation not supported",
+        #[cfg(any(
+            target_os = "macos",
+            target_os = "freebsd",
+            target_os = "dragonfly",
+            target_os = "ios",
+            target_os = "openbsd",
+            target_os = "netbsd",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "haiku"
+        ))]
+        ENOTSUP => "Operation not supported",
 
-        #[cfg(any(target_os = "macos", target_os = "freebsd",
-                  target_os = "dragonfly", target_os = "ios",
-                  target_os = "openbsd", target_os = "netbsd"))]
-        EPROCLIM        => "Too many processes",
+        #[cfg(any(
+            target_os = "macos",
+            target_os = "freebsd",
+            target_os = "dragonfly",
+            target_os = "ios",
+            target_os = "openbsd",
+            target_os = "netbsd"
+        ))]
+        EPROCLIM => "Too many processes",
 
-        #[cfg(any(target_os = "macos", target_os = "freebsd",
-                  target_os = "dragonfly", target_os = "ios",
-                  target_os = "openbsd", target_os = "netbsd",
-                  target_os = "redox"))]
-        EUSERS          => "Too many users",
+        #[cfg(any(
+            target_os = "macos",
+            target_os = "freebsd",
+            target_os = "dragonfly",
+            target_os = "ios",
+            target_os = "openbsd",
+            target_os = "netbsd",
+            target_os = "redox"
+        ))]
+        EUSERS => "Too many users",
 
-        #[cfg(any(target_os = "macos", target_os = "freebsd",
-                  target_os = "dragonfly", target_os = "ios",
-                  target_os = "openbsd", target_os = "netbsd",
-                  target_os = "redox", target_os = "illumos",
-                  target_os = "solaris"))]
-        EDQUOT          => "Disc quota exceeded",
+        #[cfg(any(
+            target_os = "macos",
+            target_os = "freebsd",
+            target_os = "dragonfly",
+            target_os = "ios",
+            target_os = "openbsd",
+            target_os = "netbsd",
+            target_os = "redox",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "haiku"
+        ))]
+        EDQUOT => "Disc quota exceeded",
 
-        #[cfg(any(target_os = "macos", target_os = "freebsd",
-                  target_os = "dragonfly", target_os = "ios",
-                  target_os = "openbsd", target_os = "netbsd",
-                  target_os = "redox", target_os = "illumos",
-                  target_os = "solaris"))]
-        ESTALE          => "Stale NFS file handle",
+        #[cfg(any(
+            target_os = "macos",
+            target_os = "freebsd",
+            target_os = "dragonfly",
+            target_os = "ios",
+            target_os = "openbsd",
+            target_os = "netbsd",
+            target_os = "redox",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_os = "haiku"
+        ))]
+        ESTALE => "Stale NFS file handle",
 
-        #[cfg(any(target_os = "macos", target_os = "freebsd",
-                  target_os = "dragonfly", target_os = "ios",
-                  target_os = "openbsd", target_os = "netbsd",
-                  target_os = "redox"))]
-        EREMOTE         => "Too many levels of remote in path",
+        #[cfg(any(
+            target_os = "macos",
+            target_os = "freebsd",
+            target_os = "dragonfly",
+            target_os = "ios",
+            target_os = "openbsd",
+            target_os = "netbsd",
+            target_os = "redox"
+        ))]
+        EREMOTE => "Too many levels of remote in path",
 
-        #[cfg(any(target_os = "macos", target_os = "freebsd",
-                  target_os = "dragonfly", target_os = "ios",
-                  target_os = "openbsd", target_os = "netbsd"))]
-        EBADRPC         => "RPC struct is bad",
+        #[cfg(any(
+            target_os = "macos",
+            target_os = "freebsd",
+            target_os = "dragonfly",
+            target_os = "ios",
+            target_os = "openbsd",
+            target_os = "netbsd"
+        ))]
+        EBADRPC => "RPC struct is bad",
 
-        #[cfg(any(target_os = "macos", target_os = "freebsd",
-                  target_os = "dragonfly", target_os = "ios",
-                  target_os = "openbsd", target_os = "netbsd"))]
-        ERPCMISMATCH    => "RPC version wrong",
+        #[cfg(any(
+            target_os = "macos",
+            target_os = "freebsd",
+            target_os = "dragonfly",
+            target_os = "ios",
+            target_os = "openbsd",
+            target_os = "netbsd"
+        ))]
+        ERPCMISMATCH => "RPC version wrong",
 
-        #[cfg(any(target_os = "macos", target_os = "freebsd",
-                  target_os = "dragonfly", target_os = "ios",
-                  target_os = "openbsd", target_os = "netbsd"))]
-        EPROGUNAVAIL    => "RPC prog. not avail",
+        #[cfg(any(
+            target_os = "macos",
+            target_os = "freebsd",
+            target_os = "dragonfly",
+            target_os = "ios",
+            target_os = "openbsd",
+            target_os = "netbsd"
+        ))]
+        EPROGUNAVAIL => "RPC prog. not avail",
 
-        #[cfg(any(target_os = "macos", target_os = "freebsd",
-                  target_os = "dragonfly", target_os = "ios",
-                  target_os = "openbsd", target_os = "netbsd"))]
-        EPROGMISMATCH   => "Program version wrong",
+        #[cfg(any(
+            target_os = "macos",
+            target_os = "freebsd",
+            target_os = "dragonfly",
+            target_os = "ios",
+            target_os = "openbsd",
+            target_os = "netbsd"
+        ))]
+        EPROGMISMATCH => "Program version wrong",
 
-        #[cfg(any(target_os = "macos", target_os = "freebsd",
-                  target_os = "dragonfly", target_os = "ios",
-                  target_os = "openbsd", target_os = "netbsd"))]
-        EPROCUNAVAIL    => "Bad procedure for program",
+        #[cfg(any(
+            target_os = "macos",
+            target_os = "freebsd",
+            target_os = "dragonfly",
+            target_os = "ios",
+            target_os = "openbsd",
+            target_os = "netbsd"
+        ))]
+        EPROCUNAVAIL => "Bad procedure for program",
 
-        #[cfg(any(target_os = "macos", target_os = "freebsd",
-                  target_os = "dragonfly", target_os = "ios",
-                  target_os = "openbsd", target_os = "netbsd"))]
-        EFTYPE          => "Inappropriate file type or format",
+        #[cfg(any(
+            target_os = "macos",
+            target_os = "freebsd",
+            target_os = "dragonfly",
+            target_os = "ios",
+            target_os = "openbsd",
+            target_os = "netbsd"
+        ))]
+        EFTYPE => "Inappropriate file type or format",
 
-        #[cfg(any(target_os = "macos", target_os = "freebsd",
-                  target_os = "dragonfly", target_os = "ios",
-                  target_os = "openbsd", target_os = "netbsd"))]
-        EAUTH           => "Authentication error",
+        #[cfg(any(
+            target_os = "macos",
+            target_os = "freebsd",
+            target_os = "dragonfly",
+            target_os = "ios",
+            target_os = "openbsd",
+            target_os = "netbsd"
+        ))]
+        EAUTH => "Authentication error",
 
-        #[cfg(any(target_os = "macos", target_os = "freebsd",
-                  target_os = "dragonfly", target_os = "ios",
-                  target_os = "openbsd", target_os = "netbsd",
-                  target_os = "redox"))]
-        ECANCELED       => "Operation canceled",
+        #[cfg(any(
+            target_os = "macos",
+            target_os = "freebsd",
+            target_os = "dragonfly",
+            target_os = "ios",
+            target_os = "openbsd",
+            target_os = "netbsd",
+            target_os = "redox"
+        ))]
+        ECANCELED => "Operation canceled",
 
         #[cfg(any(target_os = "macos", target_os = "ios"))]
-        EPWROFF         => "Device power is off",
+        EPWROFF => "Device power is off",
 
         #[cfg(any(target_os = "macos", target_os = "ios"))]
-        EDEVERR         => "Device error, e.g. paper out",
+        EDEVERR => "Device error, e.g. paper out",
 
         #[cfg(any(target_os = "macos", target_os = "ios"))]
-        EBADEXEC        => "Bad executable",
+        EBADEXEC => "Bad executable",
 
         #[cfg(any(target_os = "macos", target_os = "ios"))]
-        EBADARCH        => "Bad CPU type in executable",
+        EBADARCH => "Bad CPU type in executable",
 
         #[cfg(any(target_os = "macos", target_os = "ios"))]
-        ESHLIBVERS      => "Shared library version mismatch",
+        ESHLIBVERS => "Shared library version mismatch",
 
         #[cfg(any(target_os = "macos", target_os = "ios"))]
-        EBADMACHO       => "Malformed Macho file",
+        EBADMACHO => "Malformed Macho file",
 
-        #[cfg(any(target_os = "macos", target_os = "ios",
-                  target_os = "netbsd"))]
-        EMULTIHOP       => "Reserved",
+        #[cfg(any(
+            target_os = "macos",
+            target_os = "ios",
+            target_os = "netbsd",
+            target_os = "haiku"
+        ))]
+        EMULTIHOP => "Reserved",
 
-        #[cfg(any(target_os = "macos", target_os = "ios",
-                  target_os = "netbsd", target_os = "redox"))]
-        ENODATA         => "No message available on STREAM",
+        #[cfg(any(
+            target_os = "macos",
+            target_os = "ios",
+            target_os = "netbsd",
+            target_os = "redox"
+        ))]
+        ENODATA => "No message available on STREAM",
 
-        #[cfg(any(target_os = "macos", target_os = "ios",
-                  target_os = "netbsd"))]
-        ENOLINK         => "Reserved",
+        #[cfg(any(
+            target_os = "macos",
+            target_os = "ios",
+            target_os = "netbsd",
+            target_os = "haiku"
+        ))]
+        ENOLINK => "Reserved",
 
-        #[cfg(any(target_os = "macos", target_os = "ios",
-                  target_os = "netbsd", target_os = "redox"))]
-        ENOSR           => "No STREAM resources",
+        #[cfg(any(
+            target_os = "macos",
+            target_os = "ios",
+            target_os = "netbsd",
+            target_os = "redox"
+        ))]
+        ENOSR => "No STREAM resources",
 
-        #[cfg(any(target_os = "macos", target_os = "ios",
-                  target_os = "netbsd", target_os = "redox"))]
-        ENOSTR          => "Not a STREAM",
+        #[cfg(any(
+            target_os = "macos",
+            target_os = "ios",
+            target_os = "netbsd",
+            target_os = "redox"
+        ))]
+        ENOSTR => "Not a STREAM",
 
-        #[cfg(any(target_os = "macos", target_os = "ios",
-                  target_os = "netbsd", target_os = "redox"))]
-        ETIME           => "STREAM ioctl timeout",
+        #[cfg(any(
+            target_os = "macos",
+            target_os = "ios",
+            target_os = "netbsd",
+            target_os = "redox"
+        ))]
+        ETIME => "STREAM ioctl timeout",
 
-        #[cfg(any(target_os = "macos", target_os = "ios",
-                  target_os = "illumos", target_os = "solaris"))]
-        EOPNOTSUPP      => "Operation not supported on socket",
+        #[cfg(any(
+            target_os = "macos",
+            target_os = "ios",
+            target_os = "illumos",
+            target_os = "solaris"
+        ))]
+        EOPNOTSUPP => "Operation not supported on socket",
 
         #[cfg(any(target_os = "macos", target_os = "ios"))]
-        ENOPOLICY       => "No such policy registered",
+        ENOPOLICY => "No such policy registered",
 
         #[cfg(any(target_os = "macos", target_os = "ios"))]
-        EQFULL          => "Interface output queue is full",
+        EQFULL => "Interface output queue is full",
 
         #[cfg(target_os = "openbsd")]
-        EOPNOTSUPP      => "Operation not supported",
+        EOPNOTSUPP => "Operation not supported",
 
         #[cfg(target_os = "openbsd")]
-        EIPSEC          => "IPsec processing failure",
+        EIPSEC => "IPsec processing failure",
 
         #[cfg(target_os = "dragonfly")]
-        EASYNC          => "Async",
+        EASYNC => "Async",
 
         #[cfg(any(target_os = "illumos", target_os = "solaris"))]
-        EDEADLOCK       => "Resource deadlock would occur",
+        EDEADLOCK => "Resource deadlock would occur",
 
         #[cfg(any(target_os = "illumos", target_os = "solaris"))]
-        ELOCKUNMAPPED   => "Locked lock was unmapped",
+        ELOCKUNMAPPED => "Locked lock was unmapped",
 
         #[cfg(any(target_os = "illumos", target_os = "solaris"))]
-        ENOTACTIVE      => "Facility is not active",
+        ENOTACTIVE => "Facility is not active",
     }
 }
 
-#[cfg(any(target_os = "linux", target_os = "android",
-          target_os = "fuchsia"))]
+#[cfg(any(target_os = "linux", target_os = "android", target_os = "fuchsia"))]
 mod consts {
     #[derive(Clone, Copy, Debug, Eq, PartialEq)]
     #[repr(i32)]
     #[non_exhaustive]
     pub enum Errno {
-        UnknownErrno    = 0,
-        EPERM           = libc::EPERM,
-        ENOENT          = libc::ENOENT,
-        ESRCH           = libc::ESRCH,
-        EINTR           = libc::EINTR,
-        EIO             = libc::EIO,
-        ENXIO           = libc::ENXIO,
-        E2BIG           = libc::E2BIG,
-        ENOEXEC         = libc::ENOEXEC,
-        EBADF           = libc::EBADF,
-        ECHILD          = libc::ECHILD,
-        EAGAIN          = libc::EAGAIN,
-        ENOMEM          = libc::ENOMEM,
-        EACCES          = libc::EACCES,
-        EFAULT          = libc::EFAULT,
-        ENOTBLK         = libc::ENOTBLK,
-        EBUSY           = libc::EBUSY,
-        EEXIST          = libc::EEXIST,
-        EXDEV           = libc::EXDEV,
-        ENODEV          = libc::ENODEV,
-        ENOTDIR         = libc::ENOTDIR,
-        EISDIR          = libc::EISDIR,
-        EINVAL          = libc::EINVAL,
-        ENFILE          = libc::ENFILE,
-        EMFILE          = libc::EMFILE,
-        ENOTTY          = libc::ENOTTY,
-        ETXTBSY         = libc::ETXTBSY,
-        EFBIG           = libc::EFBIG,
-        ENOSPC          = libc::ENOSPC,
-        ESPIPE          = libc::ESPIPE,
-        EROFS           = libc::EROFS,
-        EMLINK          = libc::EMLINK,
-        EPIPE           = libc::EPIPE,
-        EDOM            = libc::EDOM,
-        ERANGE          = libc::ERANGE,
-        EDEADLK         = libc::EDEADLK,
-        ENAMETOOLONG    = libc::ENAMETOOLONG,
-        ENOLCK          = libc::ENOLCK,
-        ENOSYS          = libc::ENOSYS,
-        ENOTEMPTY       = libc::ENOTEMPTY,
-        ELOOP           = libc::ELOOP,
-        ENOMSG          = libc::ENOMSG,
-        EIDRM           = libc::EIDRM,
-        ECHRNG          = libc::ECHRNG,
-        EL2NSYNC        = libc::EL2NSYNC,
-        EL3HLT          = libc::EL3HLT,
-        EL3RST          = libc::EL3RST,
-        ELNRNG          = libc::ELNRNG,
-        EUNATCH         = libc::EUNATCH,
-        ENOCSI          = libc::ENOCSI,
-        EL2HLT          = libc::EL2HLT,
-        EBADE           = libc::EBADE,
-        EBADR           = libc::EBADR,
-        EXFULL          = libc::EXFULL,
-        ENOANO          = libc::ENOANO,
-        EBADRQC         = libc::EBADRQC,
-        EBADSLT         = libc::EBADSLT,
-        EBFONT          = libc::EBFONT,
-        ENOSTR          = libc::ENOSTR,
-        ENODATA         = libc::ENODATA,
-        ETIME           = libc::ETIME,
-        ENOSR           = libc::ENOSR,
-        ENONET          = libc::ENONET,
-        ENOPKG          = libc::ENOPKG,
-        EREMOTE         = libc::EREMOTE,
-        ENOLINK         = libc::ENOLINK,
-        EADV            = libc::EADV,
-        ESRMNT          = libc::ESRMNT,
-        ECOMM           = libc::ECOMM,
-        EPROTO          = libc::EPROTO,
-        EMULTIHOP       = libc::EMULTIHOP,
-        EDOTDOT         = libc::EDOTDOT,
-        EBADMSG         = libc::EBADMSG,
-        EOVERFLOW       = libc::EOVERFLOW,
-        ENOTUNIQ        = libc::ENOTUNIQ,
-        EBADFD          = libc::EBADFD,
-        EREMCHG         = libc::EREMCHG,
-        ELIBACC         = libc::ELIBACC,
-        ELIBBAD         = libc::ELIBBAD,
-        ELIBSCN         = libc::ELIBSCN,
-        ELIBMAX         = libc::ELIBMAX,
-        ELIBEXEC        = libc::ELIBEXEC,
-        EILSEQ          = libc::EILSEQ,
-        ERESTART        = libc::ERESTART,
-        ESTRPIPE        = libc::ESTRPIPE,
-        EUSERS          = libc::EUSERS,
-        ENOTSOCK        = libc::ENOTSOCK,
-        EDESTADDRREQ    = libc::EDESTADDRREQ,
-        EMSGSIZE        = libc::EMSGSIZE,
-        EPROTOTYPE      = libc::EPROTOTYPE,
-        ENOPROTOOPT     = libc::ENOPROTOOPT,
+        UnknownErrno = 0,
+        EPERM = libc::EPERM,
+        ENOENT = libc::ENOENT,
+        ESRCH = libc::ESRCH,
+        EINTR = libc::EINTR,
+        EIO = libc::EIO,
+        ENXIO = libc::ENXIO,
+        E2BIG = libc::E2BIG,
+        ENOEXEC = libc::ENOEXEC,
+        EBADF = libc::EBADF,
+        ECHILD = libc::ECHILD,
+        EAGAIN = libc::EAGAIN,
+        ENOMEM = libc::ENOMEM,
+        EACCES = libc::EACCES,
+        EFAULT = libc::EFAULT,
+        ENOTBLK = libc::ENOTBLK,
+        EBUSY = libc::EBUSY,
+        EEXIST = libc::EEXIST,
+        EXDEV = libc::EXDEV,
+        ENODEV = libc::ENODEV,
+        ENOTDIR = libc::ENOTDIR,
+        EISDIR = libc::EISDIR,
+        EINVAL = libc::EINVAL,
+        ENFILE = libc::ENFILE,
+        EMFILE = libc::EMFILE,
+        ENOTTY = libc::ENOTTY,
+        ETXTBSY = libc::ETXTBSY,
+        EFBIG = libc::EFBIG,
+        ENOSPC = libc::ENOSPC,
+        ESPIPE = libc::ESPIPE,
+        EROFS = libc::EROFS,
+        EMLINK = libc::EMLINK,
+        EPIPE = libc::EPIPE,
+        EDOM = libc::EDOM,
+        ERANGE = libc::ERANGE,
+        EDEADLK = libc::EDEADLK,
+        ENAMETOOLONG = libc::ENAMETOOLONG,
+        ENOLCK = libc::ENOLCK,
+        ENOSYS = libc::ENOSYS,
+        ENOTEMPTY = libc::ENOTEMPTY,
+        ELOOP = libc::ELOOP,
+        ENOMSG = libc::ENOMSG,
+        EIDRM = libc::EIDRM,
+        ECHRNG = libc::ECHRNG,
+        EL2NSYNC = libc::EL2NSYNC,
+        EL3HLT = libc::EL3HLT,
+        EL3RST = libc::EL3RST,
+        ELNRNG = libc::ELNRNG,
+        EUNATCH = libc::EUNATCH,
+        ENOCSI = libc::ENOCSI,
+        EL2HLT = libc::EL2HLT,
+        EBADE = libc::EBADE,
+        EBADR = libc::EBADR,
+        EXFULL = libc::EXFULL,
+        ENOANO = libc::ENOANO,
+        EBADRQC = libc::EBADRQC,
+        EBADSLT = libc::EBADSLT,
+        EBFONT = libc::EBFONT,
+        ENOSTR = libc::ENOSTR,
+        ENODATA = libc::ENODATA,
+        ETIME = libc::ETIME,
+        ENOSR = libc::ENOSR,
+        ENONET = libc::ENONET,
+        ENOPKG = libc::ENOPKG,
+        EREMOTE = libc::EREMOTE,
+        ENOLINK = libc::ENOLINK,
+        EADV = libc::EADV,
+        ESRMNT = libc::ESRMNT,
+        ECOMM = libc::ECOMM,
+        EPROTO = libc::EPROTO,
+        EMULTIHOP = libc::EMULTIHOP,
+        EDOTDOT = libc::EDOTDOT,
+        EBADMSG = libc::EBADMSG,
+        EOVERFLOW = libc::EOVERFLOW,
+        ENOTUNIQ = libc::ENOTUNIQ,
+        EBADFD = libc::EBADFD,
+        EREMCHG = libc::EREMCHG,
+        ELIBACC = libc::ELIBACC,
+        ELIBBAD = libc::ELIBBAD,
+        ELIBSCN = libc::ELIBSCN,
+        ELIBMAX = libc::ELIBMAX,
+        ELIBEXEC = libc::ELIBEXEC,
+        EILSEQ = libc::EILSEQ,
+        ERESTART = libc::ERESTART,
+        ESTRPIPE = libc::ESTRPIPE,
+        EUSERS = libc::EUSERS,
+        ENOTSOCK = libc::ENOTSOCK,
+        EDESTADDRREQ = libc::EDESTADDRREQ,
+        EMSGSIZE = libc::EMSGSIZE,
+        EPROTOTYPE = libc::EPROTOTYPE,
+        ENOPROTOOPT = libc::ENOPROTOOPT,
         EPROTONOSUPPORT = libc::EPROTONOSUPPORT,
         ESOCKTNOSUPPORT = libc::ESOCKTNOSUPPORT,
-        EOPNOTSUPP      = libc::EOPNOTSUPP,
-        EPFNOSUPPORT    = libc::EPFNOSUPPORT,
-        EAFNOSUPPORT    = libc::EAFNOSUPPORT,
-        EADDRINUSE      = libc::EADDRINUSE,
-        EADDRNOTAVAIL   = libc::EADDRNOTAVAIL,
-        ENETDOWN        = libc::ENETDOWN,
-        ENETUNREACH     = libc::ENETUNREACH,
-        ENETRESET       = libc::ENETRESET,
-        ECONNABORTED    = libc::ECONNABORTED,
-        ECONNRESET      = libc::ECONNRESET,
-        ENOBUFS         = libc::ENOBUFS,
-        EISCONN         = libc::EISCONN,
-        ENOTCONN        = libc::ENOTCONN,
-        ESHUTDOWN       = libc::ESHUTDOWN,
-        ETOOMANYREFS    = libc::ETOOMANYREFS,
-        ETIMEDOUT       = libc::ETIMEDOUT,
-        ECONNREFUSED    = libc::ECONNREFUSED,
-        EHOSTDOWN       = libc::EHOSTDOWN,
-        EHOSTUNREACH    = libc::EHOSTUNREACH,
-        EALREADY        = libc::EALREADY,
-        EINPROGRESS     = libc::EINPROGRESS,
-        ESTALE          = libc::ESTALE,
-        EUCLEAN         = libc::EUCLEAN,
-        ENOTNAM         = libc::ENOTNAM,
-        ENAVAIL         = libc::ENAVAIL,
-        EISNAM          = libc::EISNAM,
-        EREMOTEIO       = libc::EREMOTEIO,
-        EDQUOT          = libc::EDQUOT,
-        ENOMEDIUM       = libc::ENOMEDIUM,
-        EMEDIUMTYPE     = libc::EMEDIUMTYPE,
-        ECANCELED       = libc::ECANCELED,
-        ENOKEY          = libc::ENOKEY,
-        EKEYEXPIRED     = libc::EKEYEXPIRED,
-        EKEYREVOKED     = libc::EKEYREVOKED,
-        EKEYREJECTED    = libc::EKEYREJECTED,
-        EOWNERDEAD      = libc::EOWNERDEAD,
+        EOPNOTSUPP = libc::EOPNOTSUPP,
+        EPFNOSUPPORT = libc::EPFNOSUPPORT,
+        EAFNOSUPPORT = libc::EAFNOSUPPORT,
+        EADDRINUSE = libc::EADDRINUSE,
+        EADDRNOTAVAIL = libc::EADDRNOTAVAIL,
+        ENETDOWN = libc::ENETDOWN,
+        ENETUNREACH = libc::ENETUNREACH,
+        ENETRESET = libc::ENETRESET,
+        ECONNABORTED = libc::ECONNABORTED,
+        ECONNRESET = libc::ECONNRESET,
+        ENOBUFS = libc::ENOBUFS,
+        EISCONN = libc::EISCONN,
+        ENOTCONN = libc::ENOTCONN,
+        ESHUTDOWN = libc::ESHUTDOWN,
+        ETOOMANYREFS = libc::ETOOMANYREFS,
+        ETIMEDOUT = libc::ETIMEDOUT,
+        ECONNREFUSED = libc::ECONNREFUSED,
+        EHOSTDOWN = libc::EHOSTDOWN,
+        EHOSTUNREACH = libc::EHOSTUNREACH,
+        EALREADY = libc::EALREADY,
+        EINPROGRESS = libc::EINPROGRESS,
+        ESTALE = libc::ESTALE,
+        EUCLEAN = libc::EUCLEAN,
+        ENOTNAM = libc::ENOTNAM,
+        ENAVAIL = libc::ENAVAIL,
+        EISNAM = libc::EISNAM,
+        EREMOTEIO = libc::EREMOTEIO,
+        EDQUOT = libc::EDQUOT,
+        ENOMEDIUM = libc::ENOMEDIUM,
+        EMEDIUMTYPE = libc::EMEDIUMTYPE,
+        ECANCELED = libc::ECANCELED,
+        ENOKEY = libc::ENOKEY,
+        EKEYEXPIRED = libc::EKEYEXPIRED,
+        EKEYREVOKED = libc::EKEYREVOKED,
+        EKEYREJECTED = libc::EKEYREJECTED,
+        EOWNERDEAD = libc::EOWNERDEAD,
         ENOTRECOVERABLE = libc::ENOTRECOVERABLE,
-        #[cfg(not(any(target_os = "android", target_arch="mips")))]
-        ERFKILL         = libc::ERFKILL,
-        #[cfg(not(any(target_os = "android", target_arch="mips")))]
-        EHWPOISON       = libc::EHWPOISON,
+        #[cfg(not(any(target_os = "android", target_arch = "mips")))]
+        ERFKILL = libc::ERFKILL,
+        #[cfg(not(any(target_os = "android", target_arch = "mips")))]
+        EHWPOISON = libc::EHWPOISON,
     }
 
-    #[deprecated(
-        since = "0.22.1",
-        note = "use nix::errno::Errno::EWOULDBLOCK instead"
-    )]
-    pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
-    #[deprecated(
-        since = "0.22.1",
-        note = "use nix::errno::Errno::EDEADLOCK instead"
-    )]
-    pub const EDEADLOCK:   Errno = Errno::EDEADLK;
-    #[deprecated(
-        since = "0.22.1",
-        note = "use nix::errno::Errno::ENOTSUP instead"
-    )]
-    pub const ENOTSUP:  Errno = Errno::EOPNOTSUPP;
-
     impl Errno {
         pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
-        pub const EDEADLOCK:   Errno = Errno::EDEADLK;
-        pub const ENOTSUP:     Errno = Errno::EOPNOTSUPP;
+        pub const EDEADLOCK: Errno = Errno::EDEADLK;
+        pub const ENOTSUP: Errno = Errno::EOPNOTSUPP;
     }
 
     pub const fn from_i32(e: i32) -> Errno {
@@ -1063,11 +1396,11 @@
             libc::EKEYREJECTED => EKEYREJECTED,
             libc::EOWNERDEAD => EOWNERDEAD,
             libc::ENOTRECOVERABLE => ENOTRECOVERABLE,
-            #[cfg(not(any(target_os = "android", target_arch="mips")))]
+            #[cfg(not(any(target_os = "android", target_arch = "mips")))]
             libc::ERFKILL => ERFKILL,
-            #[cfg(not(any(target_os = "android", target_arch="mips")))]
+            #[cfg(not(any(target_os = "android", target_arch = "mips")))]
             libc::EHWPOISON => EHWPOISON,
-            _   => UnknownErrno,
+            _ => UnknownErrno,
         }
     }
 }
@@ -1078,135 +1411,119 @@
     #[repr(i32)]
     #[non_exhaustive]
     pub enum Errno {
-        UnknownErrno    = 0,
-        EPERM           = libc::EPERM,
-        ENOENT          = libc::ENOENT,
-        ESRCH           = libc::ESRCH,
-        EINTR           = libc::EINTR,
-        EIO             = libc::EIO,
-        ENXIO           = libc::ENXIO,
-        E2BIG           = libc::E2BIG,
-        ENOEXEC         = libc::ENOEXEC,
-        EBADF           = libc::EBADF,
-        ECHILD          = libc::ECHILD,
-        EDEADLK         = libc::EDEADLK,
-        ENOMEM          = libc::ENOMEM,
-        EACCES          = libc::EACCES,
-        EFAULT          = libc::EFAULT,
-        ENOTBLK         = libc::ENOTBLK,
-        EBUSY           = libc::EBUSY,
-        EEXIST          = libc::EEXIST,
-        EXDEV           = libc::EXDEV,
-        ENODEV          = libc::ENODEV,
-        ENOTDIR         = libc::ENOTDIR,
-        EISDIR          = libc::EISDIR,
-        EINVAL          = libc::EINVAL,
-        ENFILE          = libc::ENFILE,
-        EMFILE          = libc::EMFILE,
-        ENOTTY          = libc::ENOTTY,
-        ETXTBSY         = libc::ETXTBSY,
-        EFBIG           = libc::EFBIG,
-        ENOSPC          = libc::ENOSPC,
-        ESPIPE          = libc::ESPIPE,
-        EROFS           = libc::EROFS,
-        EMLINK          = libc::EMLINK,
-        EPIPE           = libc::EPIPE,
-        EDOM            = libc::EDOM,
-        ERANGE          = libc::ERANGE,
-        EAGAIN          = libc::EAGAIN,
-        EINPROGRESS     = libc::EINPROGRESS,
-        EALREADY        = libc::EALREADY,
-        ENOTSOCK        = libc::ENOTSOCK,
-        EDESTADDRREQ    = libc::EDESTADDRREQ,
-        EMSGSIZE        = libc::EMSGSIZE,
-        EPROTOTYPE      = libc::EPROTOTYPE,
-        ENOPROTOOPT     = libc::ENOPROTOOPT,
+        UnknownErrno = 0,
+        EPERM = libc::EPERM,
+        ENOENT = libc::ENOENT,
+        ESRCH = libc::ESRCH,
+        EINTR = libc::EINTR,
+        EIO = libc::EIO,
+        ENXIO = libc::ENXIO,
+        E2BIG = libc::E2BIG,
+        ENOEXEC = libc::ENOEXEC,
+        EBADF = libc::EBADF,
+        ECHILD = libc::ECHILD,
+        EDEADLK = libc::EDEADLK,
+        ENOMEM = libc::ENOMEM,
+        EACCES = libc::EACCES,
+        EFAULT = libc::EFAULT,
+        ENOTBLK = libc::ENOTBLK,
+        EBUSY = libc::EBUSY,
+        EEXIST = libc::EEXIST,
+        EXDEV = libc::EXDEV,
+        ENODEV = libc::ENODEV,
+        ENOTDIR = libc::ENOTDIR,
+        EISDIR = libc::EISDIR,
+        EINVAL = libc::EINVAL,
+        ENFILE = libc::ENFILE,
+        EMFILE = libc::EMFILE,
+        ENOTTY = libc::ENOTTY,
+        ETXTBSY = libc::ETXTBSY,
+        EFBIG = libc::EFBIG,
+        ENOSPC = libc::ENOSPC,
+        ESPIPE = libc::ESPIPE,
+        EROFS = libc::EROFS,
+        EMLINK = libc::EMLINK,
+        EPIPE = libc::EPIPE,
+        EDOM = libc::EDOM,
+        ERANGE = libc::ERANGE,
+        EAGAIN = libc::EAGAIN,
+        EINPROGRESS = libc::EINPROGRESS,
+        EALREADY = libc::EALREADY,
+        ENOTSOCK = libc::ENOTSOCK,
+        EDESTADDRREQ = libc::EDESTADDRREQ,
+        EMSGSIZE = libc::EMSGSIZE,
+        EPROTOTYPE = libc::EPROTOTYPE,
+        ENOPROTOOPT = libc::ENOPROTOOPT,
         EPROTONOSUPPORT = libc::EPROTONOSUPPORT,
         ESOCKTNOSUPPORT = libc::ESOCKTNOSUPPORT,
-        ENOTSUP         = libc::ENOTSUP,
-        EPFNOSUPPORT    = libc::EPFNOSUPPORT,
-        EAFNOSUPPORT    = libc::EAFNOSUPPORT,
-        EADDRINUSE      = libc::EADDRINUSE,
-        EADDRNOTAVAIL   = libc::EADDRNOTAVAIL,
-        ENETDOWN        = libc::ENETDOWN,
-        ENETUNREACH     = libc::ENETUNREACH,
-        ENETRESET       = libc::ENETRESET,
-        ECONNABORTED    = libc::ECONNABORTED,
-        ECONNRESET      = libc::ECONNRESET,
-        ENOBUFS         = libc::ENOBUFS,
-        EISCONN         = libc::EISCONN,
-        ENOTCONN        = libc::ENOTCONN,
-        ESHUTDOWN       = libc::ESHUTDOWN,
-        ETOOMANYREFS    = libc::ETOOMANYREFS,
-        ETIMEDOUT       = libc::ETIMEDOUT,
-        ECONNREFUSED    = libc::ECONNREFUSED,
-        ELOOP           = libc::ELOOP,
-        ENAMETOOLONG    = libc::ENAMETOOLONG,
-        EHOSTDOWN       = libc::EHOSTDOWN,
-        EHOSTUNREACH    = libc::EHOSTUNREACH,
-        ENOTEMPTY       = libc::ENOTEMPTY,
-        EPROCLIM        = libc::EPROCLIM,
-        EUSERS          = libc::EUSERS,
-        EDQUOT          = libc::EDQUOT,
-        ESTALE          = libc::ESTALE,
-        EREMOTE         = libc::EREMOTE,
-        EBADRPC         = libc::EBADRPC,
-        ERPCMISMATCH    = libc::ERPCMISMATCH,
-        EPROGUNAVAIL    = libc::EPROGUNAVAIL,
-        EPROGMISMATCH   = libc::EPROGMISMATCH,
-        EPROCUNAVAIL    = libc::EPROCUNAVAIL,
-        ENOLCK          = libc::ENOLCK,
-        ENOSYS          = libc::ENOSYS,
-        EFTYPE          = libc::EFTYPE,
-        EAUTH           = libc::EAUTH,
-        ENEEDAUTH       = libc::ENEEDAUTH,
-        EPWROFF         = libc::EPWROFF,
-        EDEVERR         = libc::EDEVERR,
-        EOVERFLOW       = libc::EOVERFLOW,
-        EBADEXEC        = libc::EBADEXEC,
-        EBADARCH        = libc::EBADARCH,
-        ESHLIBVERS      = libc::ESHLIBVERS,
-        EBADMACHO       = libc::EBADMACHO,
-        ECANCELED       = libc::ECANCELED,
-        EIDRM           = libc::EIDRM,
-        ENOMSG          = libc::ENOMSG,
-        EILSEQ          = libc::EILSEQ,
-        ENOATTR         = libc::ENOATTR,
-        EBADMSG         = libc::EBADMSG,
-        EMULTIHOP       = libc::EMULTIHOP,
-        ENODATA         = libc::ENODATA,
-        ENOLINK         = libc::ENOLINK,
-        ENOSR           = libc::ENOSR,
-        ENOSTR          = libc::ENOSTR,
-        EPROTO          = libc::EPROTO,
-        ETIME           = libc::ETIME,
-        EOPNOTSUPP      = libc::EOPNOTSUPP,
-        ENOPOLICY       = libc::ENOPOLICY,
+        ENOTSUP = libc::ENOTSUP,
+        EPFNOSUPPORT = libc::EPFNOSUPPORT,
+        EAFNOSUPPORT = libc::EAFNOSUPPORT,
+        EADDRINUSE = libc::EADDRINUSE,
+        EADDRNOTAVAIL = libc::EADDRNOTAVAIL,
+        ENETDOWN = libc::ENETDOWN,
+        ENETUNREACH = libc::ENETUNREACH,
+        ENETRESET = libc::ENETRESET,
+        ECONNABORTED = libc::ECONNABORTED,
+        ECONNRESET = libc::ECONNRESET,
+        ENOBUFS = libc::ENOBUFS,
+        EISCONN = libc::EISCONN,
+        ENOTCONN = libc::ENOTCONN,
+        ESHUTDOWN = libc::ESHUTDOWN,
+        ETOOMANYREFS = libc::ETOOMANYREFS,
+        ETIMEDOUT = libc::ETIMEDOUT,
+        ECONNREFUSED = libc::ECONNREFUSED,
+        ELOOP = libc::ELOOP,
+        ENAMETOOLONG = libc::ENAMETOOLONG,
+        EHOSTDOWN = libc::EHOSTDOWN,
+        EHOSTUNREACH = libc::EHOSTUNREACH,
+        ENOTEMPTY = libc::ENOTEMPTY,
+        EPROCLIM = libc::EPROCLIM,
+        EUSERS = libc::EUSERS,
+        EDQUOT = libc::EDQUOT,
+        ESTALE = libc::ESTALE,
+        EREMOTE = libc::EREMOTE,
+        EBADRPC = libc::EBADRPC,
+        ERPCMISMATCH = libc::ERPCMISMATCH,
+        EPROGUNAVAIL = libc::EPROGUNAVAIL,
+        EPROGMISMATCH = libc::EPROGMISMATCH,
+        EPROCUNAVAIL = libc::EPROCUNAVAIL,
+        ENOLCK = libc::ENOLCK,
+        ENOSYS = libc::ENOSYS,
+        EFTYPE = libc::EFTYPE,
+        EAUTH = libc::EAUTH,
+        ENEEDAUTH = libc::ENEEDAUTH,
+        EPWROFF = libc::EPWROFF,
+        EDEVERR = libc::EDEVERR,
+        EOVERFLOW = libc::EOVERFLOW,
+        EBADEXEC = libc::EBADEXEC,
+        EBADARCH = libc::EBADARCH,
+        ESHLIBVERS = libc::ESHLIBVERS,
+        EBADMACHO = libc::EBADMACHO,
+        ECANCELED = libc::ECANCELED,
+        EIDRM = libc::EIDRM,
+        ENOMSG = libc::ENOMSG,
+        EILSEQ = libc::EILSEQ,
+        ENOATTR = libc::ENOATTR,
+        EBADMSG = libc::EBADMSG,
+        EMULTIHOP = libc::EMULTIHOP,
+        ENODATA = libc::ENODATA,
+        ENOLINK = libc::ENOLINK,
+        ENOSR = libc::ENOSR,
+        ENOSTR = libc::ENOSTR,
+        EPROTO = libc::EPROTO,
+        ETIME = libc::ETIME,
+        EOPNOTSUPP = libc::EOPNOTSUPP,
+        ENOPOLICY = libc::ENOPOLICY,
         ENOTRECOVERABLE = libc::ENOTRECOVERABLE,
-        EOWNERDEAD      = libc::EOWNERDEAD,
-        EQFULL          = libc::EQFULL,
+        EOWNERDEAD = libc::EOWNERDEAD,
+        EQFULL = libc::EQFULL,
     }
 
-    #[deprecated(
-        since = "0.22.1",
-        note = "use nix::errno::Errno::ELAST instead"
-    )]
-    pub const ELAST:  Errno = Errno::EQFULL;
-    #[deprecated(
-        since = "0.22.1",
-        note = "use nix::errno::Errno::EWOULDBLOCK instead"
-    )]
-    pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
-    #[deprecated(
-        since = "0.22.1",
-        note = "use nix::errno::Errno::EDEADLOCK instead"
-    )]
-    pub const EDEADLOCK:   Errno = Errno::EDEADLK;
-
     impl Errno {
-        pub const ELAST: Errno       = Errno::EQFULL;
+        pub const ELAST: Errno = Errno::EQFULL;
         pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
-        pub const EDEADLOCK:   Errno = Errno::EDEADLK;
+        pub const EDEADLOCK: Errno = Errno::EDEADLK;
     }
 
     pub const fn from_i32(e: i32) -> Errno {
@@ -1319,7 +1636,7 @@
             libc::ENOTRECOVERABLE => ENOTRECOVERABLE,
             libc::EOWNERDEAD => EOWNERDEAD,
             libc::EQFULL => EQFULL,
-            _   => UnknownErrno,
+            _ => UnknownErrno,
         }
     }
 }
@@ -1330,131 +1647,110 @@
     #[repr(i32)]
     #[non_exhaustive]
     pub enum Errno {
-        UnknownErrno    = 0,
-        EPERM           = libc::EPERM,
-        ENOENT          = libc::ENOENT,
-        ESRCH           = libc::ESRCH,
-        EINTR           = libc::EINTR,
-        EIO             = libc::EIO,
-        ENXIO           = libc::ENXIO,
-        E2BIG           = libc::E2BIG,
-        ENOEXEC         = libc::ENOEXEC,
-        EBADF           = libc::EBADF,
-        ECHILD          = libc::ECHILD,
-        EDEADLK         = libc::EDEADLK,
-        ENOMEM          = libc::ENOMEM,
-        EACCES          = libc::EACCES,
-        EFAULT          = libc::EFAULT,
-        ENOTBLK         = libc::ENOTBLK,
-        EBUSY           = libc::EBUSY,
-        EEXIST          = libc::EEXIST,
-        EXDEV           = libc::EXDEV,
-        ENODEV          = libc::ENODEV,
-        ENOTDIR         = libc::ENOTDIR,
-        EISDIR          = libc::EISDIR,
-        EINVAL          = libc::EINVAL,
-        ENFILE          = libc::ENFILE,
-        EMFILE          = libc::EMFILE,
-        ENOTTY          = libc::ENOTTY,
-        ETXTBSY         = libc::ETXTBSY,
-        EFBIG           = libc::EFBIG,
-        ENOSPC          = libc::ENOSPC,
-        ESPIPE          = libc::ESPIPE,
-        EROFS           = libc::EROFS,
-        EMLINK          = libc::EMLINK,
-        EPIPE           = libc::EPIPE,
-        EDOM            = libc::EDOM,
-        ERANGE          = libc::ERANGE,
-        EAGAIN          = libc::EAGAIN,
-        EINPROGRESS     = libc::EINPROGRESS,
-        EALREADY        = libc::EALREADY,
-        ENOTSOCK        = libc::ENOTSOCK,
-        EDESTADDRREQ    = libc::EDESTADDRREQ,
-        EMSGSIZE        = libc::EMSGSIZE,
-        EPROTOTYPE      = libc::EPROTOTYPE,
-        ENOPROTOOPT     = libc::ENOPROTOOPT,
+        UnknownErrno = 0,
+        EPERM = libc::EPERM,
+        ENOENT = libc::ENOENT,
+        ESRCH = libc::ESRCH,
+        EINTR = libc::EINTR,
+        EIO = libc::EIO,
+        ENXIO = libc::ENXIO,
+        E2BIG = libc::E2BIG,
+        ENOEXEC = libc::ENOEXEC,
+        EBADF = libc::EBADF,
+        ECHILD = libc::ECHILD,
+        EDEADLK = libc::EDEADLK,
+        ENOMEM = libc::ENOMEM,
+        EACCES = libc::EACCES,
+        EFAULT = libc::EFAULT,
+        ENOTBLK = libc::ENOTBLK,
+        EBUSY = libc::EBUSY,
+        EEXIST = libc::EEXIST,
+        EXDEV = libc::EXDEV,
+        ENODEV = libc::ENODEV,
+        ENOTDIR = libc::ENOTDIR,
+        EISDIR = libc::EISDIR,
+        EINVAL = libc::EINVAL,
+        ENFILE = libc::ENFILE,
+        EMFILE = libc::EMFILE,
+        ENOTTY = libc::ENOTTY,
+        ETXTBSY = libc::ETXTBSY,
+        EFBIG = libc::EFBIG,
+        ENOSPC = libc::ENOSPC,
+        ESPIPE = libc::ESPIPE,
+        EROFS = libc::EROFS,
+        EMLINK = libc::EMLINK,
+        EPIPE = libc::EPIPE,
+        EDOM = libc::EDOM,
+        ERANGE = libc::ERANGE,
+        EAGAIN = libc::EAGAIN,
+        EINPROGRESS = libc::EINPROGRESS,
+        EALREADY = libc::EALREADY,
+        ENOTSOCK = libc::ENOTSOCK,
+        EDESTADDRREQ = libc::EDESTADDRREQ,
+        EMSGSIZE = libc::EMSGSIZE,
+        EPROTOTYPE = libc::EPROTOTYPE,
+        ENOPROTOOPT = libc::ENOPROTOOPT,
         EPROTONOSUPPORT = libc::EPROTONOSUPPORT,
         ESOCKTNOSUPPORT = libc::ESOCKTNOSUPPORT,
-        ENOTSUP         = libc::ENOTSUP,
-        EPFNOSUPPORT    = libc::EPFNOSUPPORT,
-        EAFNOSUPPORT    = libc::EAFNOSUPPORT,
-        EADDRINUSE      = libc::EADDRINUSE,
-        EADDRNOTAVAIL   = libc::EADDRNOTAVAIL,
-        ENETDOWN        = libc::ENETDOWN,
-        ENETUNREACH     = libc::ENETUNREACH,
-        ENETRESET       = libc::ENETRESET,
-        ECONNABORTED    = libc::ECONNABORTED,
-        ECONNRESET      = libc::ECONNRESET,
-        ENOBUFS         = libc::ENOBUFS,
-        EISCONN         = libc::EISCONN,
-        ENOTCONN        = libc::ENOTCONN,
-        ESHUTDOWN       = libc::ESHUTDOWN,
-        ETOOMANYREFS    = libc::ETOOMANYREFS,
-        ETIMEDOUT       = libc::ETIMEDOUT,
-        ECONNREFUSED    = libc::ECONNREFUSED,
-        ELOOP           = libc::ELOOP,
-        ENAMETOOLONG    = libc::ENAMETOOLONG,
-        EHOSTDOWN       = libc::EHOSTDOWN,
-        EHOSTUNREACH    = libc::EHOSTUNREACH,
-        ENOTEMPTY       = libc::ENOTEMPTY,
-        EPROCLIM        = libc::EPROCLIM,
-        EUSERS          = libc::EUSERS,
-        EDQUOT          = libc::EDQUOT,
-        ESTALE          = libc::ESTALE,
-        EREMOTE         = libc::EREMOTE,
-        EBADRPC         = libc::EBADRPC,
-        ERPCMISMATCH    = libc::ERPCMISMATCH,
-        EPROGUNAVAIL    = libc::EPROGUNAVAIL,
-        EPROGMISMATCH   = libc::EPROGMISMATCH,
-        EPROCUNAVAIL    = libc::EPROCUNAVAIL,
-        ENOLCK          = libc::ENOLCK,
-        ENOSYS          = libc::ENOSYS,
-        EFTYPE          = libc::EFTYPE,
-        EAUTH           = libc::EAUTH,
-        ENEEDAUTH       = libc::ENEEDAUTH,
-        EIDRM           = libc::EIDRM,
-        ENOMSG          = libc::ENOMSG,
-        EOVERFLOW       = libc::EOVERFLOW,
-        ECANCELED       = libc::ECANCELED,
-        EILSEQ          = libc::EILSEQ,
-        ENOATTR         = libc::ENOATTR,
-        EDOOFUS         = libc::EDOOFUS,
-        EBADMSG         = libc::EBADMSG,
-        EMULTIHOP       = libc::EMULTIHOP,
-        ENOLINK         = libc::ENOLINK,
-        EPROTO          = libc::EPROTO,
-        ENOTCAPABLE     = libc::ENOTCAPABLE,
-        ECAPMODE        = libc::ECAPMODE,
+        ENOTSUP = libc::ENOTSUP,
+        EPFNOSUPPORT = libc::EPFNOSUPPORT,
+        EAFNOSUPPORT = libc::EAFNOSUPPORT,
+        EADDRINUSE = libc::EADDRINUSE,
+        EADDRNOTAVAIL = libc::EADDRNOTAVAIL,
+        ENETDOWN = libc::ENETDOWN,
+        ENETUNREACH = libc::ENETUNREACH,
+        ENETRESET = libc::ENETRESET,
+        ECONNABORTED = libc::ECONNABORTED,
+        ECONNRESET = libc::ECONNRESET,
+        ENOBUFS = libc::ENOBUFS,
+        EISCONN = libc::EISCONN,
+        ENOTCONN = libc::ENOTCONN,
+        ESHUTDOWN = libc::ESHUTDOWN,
+        ETOOMANYREFS = libc::ETOOMANYREFS,
+        ETIMEDOUT = libc::ETIMEDOUT,
+        ECONNREFUSED = libc::ECONNREFUSED,
+        ELOOP = libc::ELOOP,
+        ENAMETOOLONG = libc::ENAMETOOLONG,
+        EHOSTDOWN = libc::EHOSTDOWN,
+        EHOSTUNREACH = libc::EHOSTUNREACH,
+        ENOTEMPTY = libc::ENOTEMPTY,
+        EPROCLIM = libc::EPROCLIM,
+        EUSERS = libc::EUSERS,
+        EDQUOT = libc::EDQUOT,
+        ESTALE = libc::ESTALE,
+        EREMOTE = libc::EREMOTE,
+        EBADRPC = libc::EBADRPC,
+        ERPCMISMATCH = libc::ERPCMISMATCH,
+        EPROGUNAVAIL = libc::EPROGUNAVAIL,
+        EPROGMISMATCH = libc::EPROGMISMATCH,
+        EPROCUNAVAIL = libc::EPROCUNAVAIL,
+        ENOLCK = libc::ENOLCK,
+        ENOSYS = libc::ENOSYS,
+        EFTYPE = libc::EFTYPE,
+        EAUTH = libc::EAUTH,
+        ENEEDAUTH = libc::ENEEDAUTH,
+        EIDRM = libc::EIDRM,
+        ENOMSG = libc::ENOMSG,
+        EOVERFLOW = libc::EOVERFLOW,
+        ECANCELED = libc::ECANCELED,
+        EILSEQ = libc::EILSEQ,
+        ENOATTR = libc::ENOATTR,
+        EDOOFUS = libc::EDOOFUS,
+        EBADMSG = libc::EBADMSG,
+        EMULTIHOP = libc::EMULTIHOP,
+        ENOLINK = libc::ENOLINK,
+        EPROTO = libc::EPROTO,
+        ENOTCAPABLE = libc::ENOTCAPABLE,
+        ECAPMODE = libc::ECAPMODE,
         ENOTRECOVERABLE = libc::ENOTRECOVERABLE,
-        EOWNERDEAD      = libc::EOWNERDEAD,
+        EOWNERDEAD = libc::EOWNERDEAD,
     }
 
-    #[deprecated(
-        since = "0.22.1",
-        note = "use nix::errno::Errno::ELAST instead"
-    )]
-    pub const ELAST: Errno       = Errno::EOWNERDEAD;
-    #[deprecated(
-        since = "0.22.1",
-        note = "use nix::errno::Errno::EWOULDBLOCK instead"
-    )]
-    pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
-    #[deprecated(
-        since = "0.22.1",
-        note = "use nix::errno::Errno::EDEADLOCK instead"
-    )]
-    pub const EDEADLOCK:   Errno = Errno::EDEADLK;
-    #[deprecated(
-        since = "0.22.1",
-        note = "use nix::errno::Errno::EOPNOTSUPP instead"
-    )]
-    pub const EOPNOTSUPP:  Errno = Errno::ENOTSUP;
-
     impl Errno {
-        pub const ELAST: Errno       = Errno::EOWNERDEAD;
+        pub const ELAST: Errno = Errno::EOWNERDEAD;
         pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
-        pub const EDEADLOCK:   Errno = Errno::EDEADLK;
-        pub const EOPNOTSUPP:  Errno = Errno::ENOTSUP;
+        pub const EDEADLOCK: Errno = Errno::EDEADLK;
+        pub const EOPNOTSUPP: Errno = Errno::ENOTSUP;
     }
 
     pub const fn from_i32(e: i32) -> Errno {
@@ -1557,141 +1853,121 @@
             libc::ECAPMODE => ECAPMODE,
             libc::ENOTRECOVERABLE => ENOTRECOVERABLE,
             libc::EOWNERDEAD => EOWNERDEAD,
-            _   => UnknownErrno,
+            _ => UnknownErrno,
         }
     }
 }
 
-
 #[cfg(target_os = "dragonfly")]
 mod consts {
     #[derive(Clone, Copy, Debug, Eq, PartialEq)]
     #[repr(i32)]
     #[non_exhaustive]
     pub enum Errno {
-        UnknownErrno    = 0,
-        EPERM           = libc::EPERM,
-        ENOENT          = libc::ENOENT,
-        ESRCH           = libc::ESRCH,
-        EINTR           = libc::EINTR,
-        EIO             = libc::EIO,
-        ENXIO           = libc::ENXIO,
-        E2BIG           = libc::E2BIG,
-        ENOEXEC         = libc::ENOEXEC,
-        EBADF           = libc::EBADF,
-        ECHILD          = libc::ECHILD,
-        EDEADLK         = libc::EDEADLK,
-        ENOMEM          = libc::ENOMEM,
-        EACCES          = libc::EACCES,
-        EFAULT          = libc::EFAULT,
-        ENOTBLK         = libc::ENOTBLK,
-        EBUSY           = libc::EBUSY,
-        EEXIST          = libc::EEXIST,
-        EXDEV           = libc::EXDEV,
-        ENODEV          = libc::ENODEV,
-        ENOTDIR         = libc::ENOTDIR,
-        EISDIR          = libc::EISDIR,
-        EINVAL          = libc::EINVAL,
-        ENFILE          = libc::ENFILE,
-        EMFILE          = libc::EMFILE,
-        ENOTTY          = libc::ENOTTY,
-        ETXTBSY         = libc::ETXTBSY,
-        EFBIG           = libc::EFBIG,
-        ENOSPC          = libc::ENOSPC,
-        ESPIPE          = libc::ESPIPE,
-        EROFS           = libc::EROFS,
-        EMLINK          = libc::EMLINK,
-        EPIPE           = libc::EPIPE,
-        EDOM            = libc::EDOM,
-        ERANGE          = libc::ERANGE,
-        EAGAIN          = libc::EAGAIN,
-        EINPROGRESS     = libc::EINPROGRESS,
-        EALREADY        = libc::EALREADY,
-        ENOTSOCK        = libc::ENOTSOCK,
-        EDESTADDRREQ    = libc::EDESTADDRREQ,
-        EMSGSIZE        = libc::EMSGSIZE,
-        EPROTOTYPE      = libc::EPROTOTYPE,
-        ENOPROTOOPT     = libc::ENOPROTOOPT,
+        UnknownErrno = 0,
+        EPERM = libc::EPERM,
+        ENOENT = libc::ENOENT,
+        ESRCH = libc::ESRCH,
+        EINTR = libc::EINTR,
+        EIO = libc::EIO,
+        ENXIO = libc::ENXIO,
+        E2BIG = libc::E2BIG,
+        ENOEXEC = libc::ENOEXEC,
+        EBADF = libc::EBADF,
+        ECHILD = libc::ECHILD,
+        EDEADLK = libc::EDEADLK,
+        ENOMEM = libc::ENOMEM,
+        EACCES = libc::EACCES,
+        EFAULT = libc::EFAULT,
+        ENOTBLK = libc::ENOTBLK,
+        EBUSY = libc::EBUSY,
+        EEXIST = libc::EEXIST,
+        EXDEV = libc::EXDEV,
+        ENODEV = libc::ENODEV,
+        ENOTDIR = libc::ENOTDIR,
+        EISDIR = libc::EISDIR,
+        EINVAL = libc::EINVAL,
+        ENFILE = libc::ENFILE,
+        EMFILE = libc::EMFILE,
+        ENOTTY = libc::ENOTTY,
+        ETXTBSY = libc::ETXTBSY,
+        EFBIG = libc::EFBIG,
+        ENOSPC = libc::ENOSPC,
+        ESPIPE = libc::ESPIPE,
+        EROFS = libc::EROFS,
+        EMLINK = libc::EMLINK,
+        EPIPE = libc::EPIPE,
+        EDOM = libc::EDOM,
+        ERANGE = libc::ERANGE,
+        EAGAIN = libc::EAGAIN,
+        EINPROGRESS = libc::EINPROGRESS,
+        EALREADY = libc::EALREADY,
+        ENOTSOCK = libc::ENOTSOCK,
+        EDESTADDRREQ = libc::EDESTADDRREQ,
+        EMSGSIZE = libc::EMSGSIZE,
+        EPROTOTYPE = libc::EPROTOTYPE,
+        ENOPROTOOPT = libc::ENOPROTOOPT,
         EPROTONOSUPPORT = libc::EPROTONOSUPPORT,
         ESOCKTNOSUPPORT = libc::ESOCKTNOSUPPORT,
-        ENOTSUP         = libc::ENOTSUP,
-        EPFNOSUPPORT    = libc::EPFNOSUPPORT,
-        EAFNOSUPPORT    = libc::EAFNOSUPPORT,
-        EADDRINUSE      = libc::EADDRINUSE,
-        EADDRNOTAVAIL   = libc::EADDRNOTAVAIL,
-        ENETDOWN        = libc::ENETDOWN,
-        ENETUNREACH     = libc::ENETUNREACH,
-        ENETRESET       = libc::ENETRESET,
-        ECONNABORTED    = libc::ECONNABORTED,
-        ECONNRESET      = libc::ECONNRESET,
-        ENOBUFS         = libc::ENOBUFS,
-        EISCONN         = libc::EISCONN,
-        ENOTCONN        = libc::ENOTCONN,
-        ESHUTDOWN       = libc::ESHUTDOWN,
-        ETOOMANYREFS    = libc::ETOOMANYREFS,
-        ETIMEDOUT       = libc::ETIMEDOUT,
-        ECONNREFUSED    = libc::ECONNREFUSED,
-        ELOOP           = libc::ELOOP,
-        ENAMETOOLONG    = libc::ENAMETOOLONG,
-        EHOSTDOWN       = libc::EHOSTDOWN,
-        EHOSTUNREACH    = libc::EHOSTUNREACH,
-        ENOTEMPTY       = libc::ENOTEMPTY,
-        EPROCLIM        = libc::EPROCLIM,
-        EUSERS          = libc::EUSERS,
-        EDQUOT          = libc::EDQUOT,
-        ESTALE          = libc::ESTALE,
-        EREMOTE         = libc::EREMOTE,
-        EBADRPC         = libc::EBADRPC,
-        ERPCMISMATCH    = libc::ERPCMISMATCH,
-        EPROGUNAVAIL    = libc::EPROGUNAVAIL,
-        EPROGMISMATCH   = libc::EPROGMISMATCH,
-        EPROCUNAVAIL    = libc::EPROCUNAVAIL,
-        ENOLCK          = libc::ENOLCK,
-        ENOSYS          = libc::ENOSYS,
-        EFTYPE          = libc::EFTYPE,
-        EAUTH           = libc::EAUTH,
-        ENEEDAUTH       = libc::ENEEDAUTH,
-        EIDRM           = libc::EIDRM,
-        ENOMSG          = libc::ENOMSG,
-        EOVERFLOW       = libc::EOVERFLOW,
-        ECANCELED       = libc::ECANCELED,
-        EILSEQ          = libc::EILSEQ,
-        ENOATTR         = libc::ENOATTR,
-        EDOOFUS         = libc::EDOOFUS,
-        EBADMSG         = libc::EBADMSG,
-        EMULTIHOP       = libc::EMULTIHOP,
-        ENOLINK         = libc::ENOLINK,
-        EPROTO          = libc::EPROTO,
-        ENOMEDIUM       = libc::ENOMEDIUM,
-        EASYNC          = libc::EASYNC,
+        ENOTSUP = libc::ENOTSUP,
+        EPFNOSUPPORT = libc::EPFNOSUPPORT,
+        EAFNOSUPPORT = libc::EAFNOSUPPORT,
+        EADDRINUSE = libc::EADDRINUSE,
+        EADDRNOTAVAIL = libc::EADDRNOTAVAIL,
+        ENETDOWN = libc::ENETDOWN,
+        ENETUNREACH = libc::ENETUNREACH,
+        ENETRESET = libc::ENETRESET,
+        ECONNABORTED = libc::ECONNABORTED,
+        ECONNRESET = libc::ECONNRESET,
+        ENOBUFS = libc::ENOBUFS,
+        EISCONN = libc::EISCONN,
+        ENOTCONN = libc::ENOTCONN,
+        ESHUTDOWN = libc::ESHUTDOWN,
+        ETOOMANYREFS = libc::ETOOMANYREFS,
+        ETIMEDOUT = libc::ETIMEDOUT,
+        ECONNREFUSED = libc::ECONNREFUSED,
+        ELOOP = libc::ELOOP,
+        ENAMETOOLONG = libc::ENAMETOOLONG,
+        EHOSTDOWN = libc::EHOSTDOWN,
+        EHOSTUNREACH = libc::EHOSTUNREACH,
+        ENOTEMPTY = libc::ENOTEMPTY,
+        EPROCLIM = libc::EPROCLIM,
+        EUSERS = libc::EUSERS,
+        EDQUOT = libc::EDQUOT,
+        ESTALE = libc::ESTALE,
+        EREMOTE = libc::EREMOTE,
+        EBADRPC = libc::EBADRPC,
+        ERPCMISMATCH = libc::ERPCMISMATCH,
+        EPROGUNAVAIL = libc::EPROGUNAVAIL,
+        EPROGMISMATCH = libc::EPROGMISMATCH,
+        EPROCUNAVAIL = libc::EPROCUNAVAIL,
+        ENOLCK = libc::ENOLCK,
+        ENOSYS = libc::ENOSYS,
+        EFTYPE = libc::EFTYPE,
+        EAUTH = libc::EAUTH,
+        ENEEDAUTH = libc::ENEEDAUTH,
+        EIDRM = libc::EIDRM,
+        ENOMSG = libc::ENOMSG,
+        EOVERFLOW = libc::EOVERFLOW,
+        ECANCELED = libc::ECANCELED,
+        EILSEQ = libc::EILSEQ,
+        ENOATTR = libc::ENOATTR,
+        EDOOFUS = libc::EDOOFUS,
+        EBADMSG = libc::EBADMSG,
+        EMULTIHOP = libc::EMULTIHOP,
+        ENOLINK = libc::ENOLINK,
+        EPROTO = libc::EPROTO,
+        ENOMEDIUM = libc::ENOMEDIUM,
+        ENOTRECOVERABLE = libc::ENOTRECOVERABLE,
+        EOWNERDEAD = libc::EOWNERDEAD,
+        EASYNC = libc::EASYNC,
     }
 
-    #[deprecated(
-        since = "0.22.1",
-        note = "use nix::errno::Errno::ELAST instead"
-    )]
-    pub const ELAST: Errno       = Errno::EASYNC;
-    #[deprecated(
-        since = "0.22.1",
-        note = "use nix::errno::Errno::EWOULDBLOCK instead"
-    )]
-    pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
-    #[deprecated(
-        since = "0.22.1",
-        note = "use nix::errno::Errno::EDEADLOCK instead"
-    )]
-    pub const EDEADLOCK:   Errno = Errno::EDEADLK;
-    #[deprecated(
-        since = "0.22.1",
-        note = "use nix::errno::Errno::EOPNOTSUPP instead"
-    )]
-    pub const EOPNOTSUPP:  Errno = Errno::ENOTSUP;
-
     impl Errno {
-        pub const ELAST: Errno       = Errno::EASYNC;
+        pub const ELAST: Errno = Errno::EASYNC;
         pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
-        pub const EDEADLOCK:   Errno = Errno::EDEADLK;
-        pub const EOPNOTSUPP:  Errno = Errno::ENOTSUP;
+        pub const EDEADLOCK: Errno = Errno::EDEADLK;
+        pub const EOPNOTSUPP: Errno = Errno::ENOTSUP;
     }
 
     pub const fn from_i32(e: i32) -> Errno {
@@ -1718,7 +1994,7 @@
             libc::EXDEV => EXDEV,
             libc::ENODEV => ENODEV,
             libc::ENOTDIR => ENOTDIR,
-            libc::EISDIR=> EISDIR,
+            libc::EISDIR => EISDIR,
             libc::EINVAL => EINVAL,
             libc::ENFILE => ENFILE,
             libc::EMFILE => EMFILE,
@@ -1792,129 +2068,117 @@
             libc::EPROTO => EPROTO,
             libc::ENOMEDIUM => ENOMEDIUM,
             libc::EASYNC => EASYNC,
-            _   => UnknownErrno,
+            _ => UnknownErrno,
         }
     }
 }
 
-
 #[cfg(target_os = "openbsd")]
 mod consts {
     #[derive(Clone, Copy, Debug, Eq, PartialEq)]
     #[repr(i32)]
     #[non_exhaustive]
     pub enum Errno {
-        UnknownErrno    = 0,
-        EPERM           = libc::EPERM,
-        ENOENT          = libc::ENOENT,
-        ESRCH           = libc::ESRCH,
-        EINTR           = libc::EINTR,
-        EIO             = libc::EIO,
-        ENXIO           = libc::ENXIO,
-        E2BIG           = libc::E2BIG,
-        ENOEXEC         = libc::ENOEXEC,
-        EBADF           = libc::EBADF,
-        ECHILD          = libc::ECHILD,
-        EDEADLK         = libc::EDEADLK,
-        ENOMEM          = libc::ENOMEM,
-        EACCES          = libc::EACCES,
-        EFAULT          = libc::EFAULT,
-        ENOTBLK         = libc::ENOTBLK,
-        EBUSY           = libc::EBUSY,
-        EEXIST          = libc::EEXIST,
-        EXDEV           = libc::EXDEV,
-        ENODEV          = libc::ENODEV,
-        ENOTDIR         = libc::ENOTDIR,
-        EISDIR          = libc::EISDIR,
-        EINVAL          = libc::EINVAL,
-        ENFILE          = libc::ENFILE,
-        EMFILE          = libc::EMFILE,
-        ENOTTY          = libc::ENOTTY,
-        ETXTBSY         = libc::ETXTBSY,
-        EFBIG           = libc::EFBIG,
-        ENOSPC          = libc::ENOSPC,
-        ESPIPE          = libc::ESPIPE,
-        EROFS           = libc::EROFS,
-        EMLINK          = libc::EMLINK,
-        EPIPE           = libc::EPIPE,
-        EDOM            = libc::EDOM,
-        ERANGE          = libc::ERANGE,
-        EAGAIN          = libc::EAGAIN,
-        EINPROGRESS     = libc::EINPROGRESS,
-        EALREADY        = libc::EALREADY,
-        ENOTSOCK        = libc::ENOTSOCK,
-        EDESTADDRREQ    = libc::EDESTADDRREQ,
-        EMSGSIZE        = libc::EMSGSIZE,
-        EPROTOTYPE      = libc::EPROTOTYPE,
-        ENOPROTOOPT     = libc::ENOPROTOOPT,
+        UnknownErrno = 0,
+        EPERM = libc::EPERM,
+        ENOENT = libc::ENOENT,
+        ESRCH = libc::ESRCH,
+        EINTR = libc::EINTR,
+        EIO = libc::EIO,
+        ENXIO = libc::ENXIO,
+        E2BIG = libc::E2BIG,
+        ENOEXEC = libc::ENOEXEC,
+        EBADF = libc::EBADF,
+        ECHILD = libc::ECHILD,
+        EDEADLK = libc::EDEADLK,
+        ENOMEM = libc::ENOMEM,
+        EACCES = libc::EACCES,
+        EFAULT = libc::EFAULT,
+        ENOTBLK = libc::ENOTBLK,
+        EBUSY = libc::EBUSY,
+        EEXIST = libc::EEXIST,
+        EXDEV = libc::EXDEV,
+        ENODEV = libc::ENODEV,
+        ENOTDIR = libc::ENOTDIR,
+        EISDIR = libc::EISDIR,
+        EINVAL = libc::EINVAL,
+        ENFILE = libc::ENFILE,
+        EMFILE = libc::EMFILE,
+        ENOTTY = libc::ENOTTY,
+        ETXTBSY = libc::ETXTBSY,
+        EFBIG = libc::EFBIG,
+        ENOSPC = libc::ENOSPC,
+        ESPIPE = libc::ESPIPE,
+        EROFS = libc::EROFS,
+        EMLINK = libc::EMLINK,
+        EPIPE = libc::EPIPE,
+        EDOM = libc::EDOM,
+        ERANGE = libc::ERANGE,
+        EAGAIN = libc::EAGAIN,
+        EINPROGRESS = libc::EINPROGRESS,
+        EALREADY = libc::EALREADY,
+        ENOTSOCK = libc::ENOTSOCK,
+        EDESTADDRREQ = libc::EDESTADDRREQ,
+        EMSGSIZE = libc::EMSGSIZE,
+        EPROTOTYPE = libc::EPROTOTYPE,
+        ENOPROTOOPT = libc::ENOPROTOOPT,
         EPROTONOSUPPORT = libc::EPROTONOSUPPORT,
         ESOCKTNOSUPPORT = libc::ESOCKTNOSUPPORT,
-        EOPNOTSUPP      = libc::EOPNOTSUPP,
-        EPFNOSUPPORT    = libc::EPFNOSUPPORT,
-        EAFNOSUPPORT    = libc::EAFNOSUPPORT,
-        EADDRINUSE      = libc::EADDRINUSE,
-        EADDRNOTAVAIL   = libc::EADDRNOTAVAIL,
-        ENETDOWN        = libc::ENETDOWN,
-        ENETUNREACH     = libc::ENETUNREACH,
-        ENETRESET       = libc::ENETRESET,
-        ECONNABORTED    = libc::ECONNABORTED,
-        ECONNRESET      = libc::ECONNRESET,
-        ENOBUFS         = libc::ENOBUFS,
-        EISCONN         = libc::EISCONN,
-        ENOTCONN        = libc::ENOTCONN,
-        ESHUTDOWN       = libc::ESHUTDOWN,
-        ETOOMANYREFS    = libc::ETOOMANYREFS,
-        ETIMEDOUT       = libc::ETIMEDOUT,
-        ECONNREFUSED    = libc::ECONNREFUSED,
-        ELOOP           = libc::ELOOP,
-        ENAMETOOLONG    = libc::ENAMETOOLONG,
-        EHOSTDOWN       = libc::EHOSTDOWN,
-        EHOSTUNREACH    = libc::EHOSTUNREACH,
-        ENOTEMPTY       = libc::ENOTEMPTY,
-        EPROCLIM        = libc::EPROCLIM,
-        EUSERS          = libc::EUSERS,
-        EDQUOT          = libc::EDQUOT,
-        ESTALE          = libc::ESTALE,
-        EREMOTE         = libc::EREMOTE,
-        EBADRPC         = libc::EBADRPC,
-        ERPCMISMATCH    = libc::ERPCMISMATCH,
-        EPROGUNAVAIL    = libc::EPROGUNAVAIL,
-        EPROGMISMATCH   = libc::EPROGMISMATCH,
-        EPROCUNAVAIL    = libc::EPROCUNAVAIL,
-        ENOLCK          = libc::ENOLCK,
-        ENOSYS          = libc::ENOSYS,
-        EFTYPE          = libc::EFTYPE,
-        EAUTH           = libc::EAUTH,
-        ENEEDAUTH       = libc::ENEEDAUTH,
-        EIPSEC          = libc::EIPSEC,
-        ENOATTR         = libc::ENOATTR,
-        EILSEQ          = libc::EILSEQ,
-        ENOMEDIUM       = libc::ENOMEDIUM,
-        EMEDIUMTYPE     = libc::EMEDIUMTYPE,
-        EOVERFLOW       = libc::EOVERFLOW,
-        ECANCELED       = libc::ECANCELED,
-        EIDRM           = libc::EIDRM,
-        ENOMSG          = libc::ENOMSG,
-        ENOTSUP         = libc::ENOTSUP,
-        EBADMSG         = libc::EBADMSG,
+        EOPNOTSUPP = libc::EOPNOTSUPP,
+        EPFNOSUPPORT = libc::EPFNOSUPPORT,
+        EAFNOSUPPORT = libc::EAFNOSUPPORT,
+        EADDRINUSE = libc::EADDRINUSE,
+        EADDRNOTAVAIL = libc::EADDRNOTAVAIL,
+        ENETDOWN = libc::ENETDOWN,
+        ENETUNREACH = libc::ENETUNREACH,
+        ENETRESET = libc::ENETRESET,
+        ECONNABORTED = libc::ECONNABORTED,
+        ECONNRESET = libc::ECONNRESET,
+        ENOBUFS = libc::ENOBUFS,
+        EISCONN = libc::EISCONN,
+        ENOTCONN = libc::ENOTCONN,
+        ESHUTDOWN = libc::ESHUTDOWN,
+        ETOOMANYREFS = libc::ETOOMANYREFS,
+        ETIMEDOUT = libc::ETIMEDOUT,
+        ECONNREFUSED = libc::ECONNREFUSED,
+        ELOOP = libc::ELOOP,
+        ENAMETOOLONG = libc::ENAMETOOLONG,
+        EHOSTDOWN = libc::EHOSTDOWN,
+        EHOSTUNREACH = libc::EHOSTUNREACH,
+        ENOTEMPTY = libc::ENOTEMPTY,
+        EPROCLIM = libc::EPROCLIM,
+        EUSERS = libc::EUSERS,
+        EDQUOT = libc::EDQUOT,
+        ESTALE = libc::ESTALE,
+        EREMOTE = libc::EREMOTE,
+        EBADRPC = libc::EBADRPC,
+        ERPCMISMATCH = libc::ERPCMISMATCH,
+        EPROGUNAVAIL = libc::EPROGUNAVAIL,
+        EPROGMISMATCH = libc::EPROGMISMATCH,
+        EPROCUNAVAIL = libc::EPROCUNAVAIL,
+        ENOLCK = libc::ENOLCK,
+        ENOSYS = libc::ENOSYS,
+        EFTYPE = libc::EFTYPE,
+        EAUTH = libc::EAUTH,
+        ENEEDAUTH = libc::ENEEDAUTH,
+        EIPSEC = libc::EIPSEC,
+        ENOATTR = libc::ENOATTR,
+        EILSEQ = libc::EILSEQ,
+        ENOMEDIUM = libc::ENOMEDIUM,
+        EMEDIUMTYPE = libc::EMEDIUMTYPE,
+        EOVERFLOW = libc::EOVERFLOW,
+        ECANCELED = libc::ECANCELED,
+        EIDRM = libc::EIDRM,
+        ENOMSG = libc::ENOMSG,
+        ENOTSUP = libc::ENOTSUP,
+        EBADMSG = libc::EBADMSG,
         ENOTRECOVERABLE = libc::ENOTRECOVERABLE,
-        EOWNERDEAD      = libc::EOWNERDEAD,
-        EPROTO          = libc::EPROTO,
+        EOWNERDEAD = libc::EOWNERDEAD,
+        EPROTO = libc::EPROTO,
     }
 
-    #[deprecated(
-        since = "0.22.1",
-        note = "use nix::errno::Errno::ELAST instead"
-    )]
-    pub const ELAST: Errno       = Errno::ENOTSUP;
-    #[deprecated(
-        since = "0.22.1",
-        note = "use nix::errno::Errno::EWOULDBLOCK instead"
-    )]
-    pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
-
     impl Errno {
-        pub const ELAST: Errno       = Errno::ENOTSUP;
+        pub const ELAST: Errno = Errno::ENOTSUP;
         pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
     }
 
@@ -2017,7 +2281,7 @@
             libc::ENOTRECOVERABLE => ENOTRECOVERABLE,
             libc::EOWNERDEAD => EOWNERDEAD,
             libc::EPROTO => EPROTO,
-            _   => UnknownErrno,
+            _ => UnknownErrno,
         }
     }
 }
@@ -2028,118 +2292,107 @@
     #[repr(i32)]
     #[non_exhaustive]
     pub enum Errno {
-        UnknownErrno    = 0,
-        EPERM           = libc::EPERM,
-        ENOENT          = libc::ENOENT,
-        ESRCH           = libc::ESRCH,
-        EINTR           = libc::EINTR,
-        EIO             = libc::EIO,
-        ENXIO           = libc::ENXIO,
-        E2BIG           = libc::E2BIG,
-        ENOEXEC         = libc::ENOEXEC,
-        EBADF           = libc::EBADF,
-        ECHILD          = libc::ECHILD,
-        EDEADLK         = libc::EDEADLK,
-        ENOMEM          = libc::ENOMEM,
-        EACCES          = libc::EACCES,
-        EFAULT          = libc::EFAULT,
-        ENOTBLK         = libc::ENOTBLK,
-        EBUSY           = libc::EBUSY,
-        EEXIST          = libc::EEXIST,
-        EXDEV           = libc::EXDEV,
-        ENODEV          = libc::ENODEV,
-        ENOTDIR         = libc::ENOTDIR,
-        EISDIR          = libc::EISDIR,
-        EINVAL          = libc::EINVAL,
-        ENFILE          = libc::ENFILE,
-        EMFILE          = libc::EMFILE,
-        ENOTTY          = libc::ENOTTY,
-        ETXTBSY         = libc::ETXTBSY,
-        EFBIG           = libc::EFBIG,
-        ENOSPC          = libc::ENOSPC,
-        ESPIPE          = libc::ESPIPE,
-        EROFS           = libc::EROFS,
-        EMLINK          = libc::EMLINK,
-        EPIPE           = libc::EPIPE,
-        EDOM            = libc::EDOM,
-        ERANGE          = libc::ERANGE,
-        EAGAIN          = libc::EAGAIN,
-        EINPROGRESS     = libc::EINPROGRESS,
-        EALREADY        = libc::EALREADY,
-        ENOTSOCK        = libc::ENOTSOCK,
-        EDESTADDRREQ    = libc::EDESTADDRREQ,
-        EMSGSIZE        = libc::EMSGSIZE,
-        EPROTOTYPE      = libc::EPROTOTYPE,
-        ENOPROTOOPT     = libc::ENOPROTOOPT,
+        UnknownErrno = 0,
+        EPERM = libc::EPERM,
+        ENOENT = libc::ENOENT,
+        ESRCH = libc::ESRCH,
+        EINTR = libc::EINTR,
+        EIO = libc::EIO,
+        ENXIO = libc::ENXIO,
+        E2BIG = libc::E2BIG,
+        ENOEXEC = libc::ENOEXEC,
+        EBADF = libc::EBADF,
+        ECHILD = libc::ECHILD,
+        EDEADLK = libc::EDEADLK,
+        ENOMEM = libc::ENOMEM,
+        EACCES = libc::EACCES,
+        EFAULT = libc::EFAULT,
+        ENOTBLK = libc::ENOTBLK,
+        EBUSY = libc::EBUSY,
+        EEXIST = libc::EEXIST,
+        EXDEV = libc::EXDEV,
+        ENODEV = libc::ENODEV,
+        ENOTDIR = libc::ENOTDIR,
+        EISDIR = libc::EISDIR,
+        EINVAL = libc::EINVAL,
+        ENFILE = libc::ENFILE,
+        EMFILE = libc::EMFILE,
+        ENOTTY = libc::ENOTTY,
+        ETXTBSY = libc::ETXTBSY,
+        EFBIG = libc::EFBIG,
+        ENOSPC = libc::ENOSPC,
+        ESPIPE = libc::ESPIPE,
+        EROFS = libc::EROFS,
+        EMLINK = libc::EMLINK,
+        EPIPE = libc::EPIPE,
+        EDOM = libc::EDOM,
+        ERANGE = libc::ERANGE,
+        EAGAIN = libc::EAGAIN,
+        EINPROGRESS = libc::EINPROGRESS,
+        EALREADY = libc::EALREADY,
+        ENOTSOCK = libc::ENOTSOCK,
+        EDESTADDRREQ = libc::EDESTADDRREQ,
+        EMSGSIZE = libc::EMSGSIZE,
+        EPROTOTYPE = libc::EPROTOTYPE,
+        ENOPROTOOPT = libc::ENOPROTOOPT,
         EPROTONOSUPPORT = libc::EPROTONOSUPPORT,
         ESOCKTNOSUPPORT = libc::ESOCKTNOSUPPORT,
-        EOPNOTSUPP      = libc::EOPNOTSUPP,
-        EPFNOSUPPORT    = libc::EPFNOSUPPORT,
-        EAFNOSUPPORT    = libc::EAFNOSUPPORT,
-        EADDRINUSE      = libc::EADDRINUSE,
-        EADDRNOTAVAIL   = libc::EADDRNOTAVAIL,
-        ENETDOWN        = libc::ENETDOWN,
-        ENETUNREACH     = libc::ENETUNREACH,
-        ENETRESET       = libc::ENETRESET,
-        ECONNABORTED    = libc::ECONNABORTED,
-        ECONNRESET      = libc::ECONNRESET,
-        ENOBUFS         = libc::ENOBUFS,
-        EISCONN         = libc::EISCONN,
-        ENOTCONN        = libc::ENOTCONN,
-        ESHUTDOWN       = libc::ESHUTDOWN,
-        ETOOMANYREFS    = libc::ETOOMANYREFS,
-        ETIMEDOUT       = libc::ETIMEDOUT,
-        ECONNREFUSED    = libc::ECONNREFUSED,
-        ELOOP           = libc::ELOOP,
-        ENAMETOOLONG    = libc::ENAMETOOLONG,
-        EHOSTDOWN       = libc::EHOSTDOWN,
-        EHOSTUNREACH    = libc::EHOSTUNREACH,
-        ENOTEMPTY       = libc::ENOTEMPTY,
-        EPROCLIM        = libc::EPROCLIM,
-        EUSERS          = libc::EUSERS,
-        EDQUOT          = libc::EDQUOT,
-        ESTALE          = libc::ESTALE,
-        EREMOTE         = libc::EREMOTE,
-        EBADRPC         = libc::EBADRPC,
-        ERPCMISMATCH    = libc::ERPCMISMATCH,
-        EPROGUNAVAIL    = libc::EPROGUNAVAIL,
-        EPROGMISMATCH   = libc::EPROGMISMATCH,
-        EPROCUNAVAIL    = libc::EPROCUNAVAIL,
-        ENOLCK          = libc::ENOLCK,
-        ENOSYS          = libc::ENOSYS,
-        EFTYPE          = libc::EFTYPE,
-        EAUTH           = libc::EAUTH,
-        ENEEDAUTH       = libc::ENEEDAUTH,
-        EIDRM           = libc::EIDRM,
-        ENOMSG          = libc::ENOMSG,
-        EOVERFLOW       = libc::EOVERFLOW,
-        EILSEQ          = libc::EILSEQ,
-        ENOTSUP         = libc::ENOTSUP,
-        ECANCELED       = libc::ECANCELED,
-        EBADMSG         = libc::EBADMSG,
-        ENODATA         = libc::ENODATA,
-        ENOSR           = libc::ENOSR,
-        ENOSTR          = libc::ENOSTR,
-        ETIME           = libc::ETIME,
-        ENOATTR         = libc::ENOATTR,
-        EMULTIHOP       = libc::EMULTIHOP,
-        ENOLINK         = libc::ENOLINK,
-        EPROTO          = libc::EPROTO,
+        EOPNOTSUPP = libc::EOPNOTSUPP,
+        EPFNOSUPPORT = libc::EPFNOSUPPORT,
+        EAFNOSUPPORT = libc::EAFNOSUPPORT,
+        EADDRINUSE = libc::EADDRINUSE,
+        EADDRNOTAVAIL = libc::EADDRNOTAVAIL,
+        ENETDOWN = libc::ENETDOWN,
+        ENETUNREACH = libc::ENETUNREACH,
+        ENETRESET = libc::ENETRESET,
+        ECONNABORTED = libc::ECONNABORTED,
+        ECONNRESET = libc::ECONNRESET,
+        ENOBUFS = libc::ENOBUFS,
+        EISCONN = libc::EISCONN,
+        ENOTCONN = libc::ENOTCONN,
+        ESHUTDOWN = libc::ESHUTDOWN,
+        ETOOMANYREFS = libc::ETOOMANYREFS,
+        ETIMEDOUT = libc::ETIMEDOUT,
+        ECONNREFUSED = libc::ECONNREFUSED,
+        ELOOP = libc::ELOOP,
+        ENAMETOOLONG = libc::ENAMETOOLONG,
+        EHOSTDOWN = libc::EHOSTDOWN,
+        EHOSTUNREACH = libc::EHOSTUNREACH,
+        ENOTEMPTY = libc::ENOTEMPTY,
+        EPROCLIM = libc::EPROCLIM,
+        EUSERS = libc::EUSERS,
+        EDQUOT = libc::EDQUOT,
+        ESTALE = libc::ESTALE,
+        EREMOTE = libc::EREMOTE,
+        EBADRPC = libc::EBADRPC,
+        ERPCMISMATCH = libc::ERPCMISMATCH,
+        EPROGUNAVAIL = libc::EPROGUNAVAIL,
+        EPROGMISMATCH = libc::EPROGMISMATCH,
+        EPROCUNAVAIL = libc::EPROCUNAVAIL,
+        ENOLCK = libc::ENOLCK,
+        ENOSYS = libc::ENOSYS,
+        EFTYPE = libc::EFTYPE,
+        EAUTH = libc::EAUTH,
+        ENEEDAUTH = libc::ENEEDAUTH,
+        EIDRM = libc::EIDRM,
+        ENOMSG = libc::ENOMSG,
+        EOVERFLOW = libc::EOVERFLOW,
+        EILSEQ = libc::EILSEQ,
+        ENOTSUP = libc::ENOTSUP,
+        ECANCELED = libc::ECANCELED,
+        EBADMSG = libc::EBADMSG,
+        ENODATA = libc::ENODATA,
+        ENOSR = libc::ENOSR,
+        ENOSTR = libc::ENOSTR,
+        ETIME = libc::ETIME,
+        ENOATTR = libc::ENOATTR,
+        EMULTIHOP = libc::EMULTIHOP,
+        ENOLINK = libc::ENOLINK,
+        EPROTO = libc::EPROTO,
     }
 
-    #[deprecated(
-        since = "0.22.1",
-        note = "use nix::errno::Errno::ELAST instead"
-    )]
-    pub const ELAST: Errno       = Errno::ENOTSUP;
-    #[deprecated(
-        since = "0.22.1",
-        note = "use nix::errno::Errno::EWOULDBLOCK instead"
-    )]
-    pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
-
     impl Errno {
-        pub const ELAST: Errno       = Errno::ENOTSUP;
+        pub const ELAST: Errno = Errno::ENOTSUP;
         pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
     }
 
@@ -2243,7 +2496,7 @@
             libc::EMULTIHOP => EMULTIHOP,
             libc::ENOLINK => ENOLINK,
             libc::EPROTO => EPROTO,
-            _   => UnknownErrno,
+            _ => UnknownErrno,
         }
     }
 }
@@ -2342,12 +2595,6 @@
         EPROTO = libc::EPROTO,
     }
 
-    #[deprecated(
-        since = "0.22.1",
-        note = "use nix::errno::Errno::EWOULDBLOCK instead"
-    )]
-    pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
-
     impl Errno {
         pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
     }
@@ -2576,19 +2823,8 @@
         ESTALE = libc::ESTALE,
     }
 
-    #[deprecated(
-        since = "0.22.1",
-        note = "use nix::errno::Errno::ELAST instead"
-    )]
-    pub const ELAST: Errno = Errno::ELAST;
-    #[deprecated(
-        since = "0.22.1",
-        note = "use nix::errno::Errno::EWOULDBLOCK instead"
-    )]
-    pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
-
     impl Errno {
-        pub const ELAST: Errno       = Errno::ESTALE;
+        pub const ELAST: Errno = Errno::ESTALE;
         pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
     }
 
@@ -2721,3 +2957,177 @@
         }
     }
 }
+
+#[cfg(target_os = "haiku")]
+mod consts {
+    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+    #[repr(i32)]
+    #[non_exhaustive]
+    pub enum Errno {
+        UnknownErrno = 0,
+        EPERM = libc::EPERM,
+        ENOENT = libc::ENOENT,
+        ESRCH = libc::ESRCH,
+        EINTR = libc::EINTR,
+        EIO = libc::EIO,
+        ENXIO = libc::ENXIO,
+        E2BIG = libc::E2BIG,
+        ENOEXEC = libc::ENOEXEC,
+        EBADF = libc::EBADF,
+        ECHILD = libc::ECHILD,
+        EDEADLK = libc::EDEADLK,
+        ENOMEM = libc::ENOMEM,
+        EACCES = libc::EACCES,
+        EFAULT = libc::EFAULT,
+        EBUSY = libc::EBUSY,
+        EEXIST = libc::EEXIST,
+        EXDEV = libc::EXDEV,
+        ENODEV = libc::ENODEV,
+        ENOTDIR = libc::ENOTDIR,
+        EISDIR = libc::EISDIR,
+        EINVAL = libc::EINVAL,
+        ENFILE = libc::ENFILE,
+        EMFILE = libc::EMFILE,
+        ENOTTY = libc::ENOTTY,
+        ETXTBSY = libc::ETXTBSY,
+        EFBIG = libc::EFBIG,
+        ENOSPC = libc::ENOSPC,
+        ESPIPE = libc::ESPIPE,
+        EROFS = libc::EROFS,
+        EMLINK = libc::EMLINK,
+        EPIPE = libc::EPIPE,
+        EDOM = libc::EDOM,
+        ERANGE = libc::ERANGE,
+        EAGAIN = libc::EAGAIN,
+        EINPROGRESS = libc::EINPROGRESS,
+        EALREADY = libc::EALREADY,
+        ENOTSOCK = libc::ENOTSOCK,
+        EDESTADDRREQ = libc::EDESTADDRREQ,
+        EMSGSIZE = libc::EMSGSIZE,
+        EPROTOTYPE = libc::EPROTOTYPE,
+        ENOPROTOOPT = libc::ENOPROTOOPT,
+        EPROTONOSUPPORT = libc::EPROTONOSUPPORT,
+        ENOTSUP = libc::ENOTSUP,
+        EADDRINUSE = libc::EADDRINUSE,
+        EADDRNOTAVAIL = libc::EADDRNOTAVAIL,
+        ENETDOWN = libc::ENETDOWN,
+        ENETUNREACH = libc::ENETUNREACH,
+        ENETRESET = libc::ENETRESET,
+        ECONNABORTED = libc::ECONNABORTED,
+        ECONNRESET = libc::ECONNRESET,
+        ENOBUFS = libc::ENOBUFS,
+        EISCONN = libc::EISCONN,
+        ENOTCONN = libc::ENOTCONN,
+        ESHUTDOWN = libc::ESHUTDOWN,
+        ETIMEDOUT = libc::ETIMEDOUT,
+        ECONNREFUSED = libc::ECONNREFUSED,
+        ELOOP = libc::ELOOP,
+        ENAMETOOLONG = libc::ENAMETOOLONG,
+        EHOSTDOWN = libc::EHOSTDOWN,
+        EHOSTUNREACH = libc::EHOSTUNREACH,
+        ENOTEMPTY = libc::ENOTEMPTY,
+        EDQUOT = libc::EDQUOT,
+        ESTALE = libc::ESTALE,
+        ENOLCK = libc::ENOLCK,
+        ENOSYS = libc::ENOSYS,
+        EIDRM = libc::EIDRM,
+        ENOMSG = libc::ENOMSG,
+        EOVERFLOW = libc::EOVERFLOW,
+        ECANCELED = libc::ECANCELED,
+        EILSEQ = libc::EILSEQ,
+        ENOATTR = libc::ENOATTR,
+        EBADMSG = libc::EBADMSG,
+        EMULTIHOP = libc::EMULTIHOP,
+        ENOLINK = libc::ENOLINK,
+        EPROTO = libc::EPROTO,
+    }
+
+    impl Errno {
+        pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
+        pub const EDEADLOCK: Errno = Errno::EDEADLK;
+        pub const EOPNOTSUPP: Errno = Errno::ENOTSUP;
+    }
+
+    pub const fn from_i32(e: i32) -> Errno {
+        use self::Errno::*;
+
+        match e {
+            libc::EPERM => EPERM,
+            libc::ENOENT => ENOENT,
+            libc::ESRCH => ESRCH,
+            libc::EINTR => EINTR,
+            libc::EIO => EIO,
+            libc::ENXIO => ENXIO,
+            libc::E2BIG => E2BIG,
+            libc::ENOEXEC => ENOEXEC,
+            libc::EBADF => EBADF,
+            libc::ECHILD => ECHILD,
+            libc::EDEADLK => EDEADLK,
+            libc::ENOMEM => ENOMEM,
+            libc::EACCES => EACCES,
+            libc::EFAULT => EFAULT,
+            libc::EBUSY => EBUSY,
+            libc::EEXIST => EEXIST,
+            libc::EXDEV => EXDEV,
+            libc::ENODEV => ENODEV,
+            libc::ENOTDIR => ENOTDIR,
+            libc::EISDIR => EISDIR,
+            libc::EINVAL => EINVAL,
+            libc::ENFILE => ENFILE,
+            libc::EMFILE => EMFILE,
+            libc::ENOTTY => ENOTTY,
+            libc::ETXTBSY => ETXTBSY,
+            libc::EFBIG => EFBIG,
+            libc::ENOSPC => ENOSPC,
+            libc::ESPIPE => ESPIPE,
+            libc::EROFS => EROFS,
+            libc::EMLINK => EMLINK,
+            libc::EPIPE => EPIPE,
+            libc::EDOM => EDOM,
+            libc::ERANGE => ERANGE,
+            libc::EAGAIN => EAGAIN,
+            libc::EINPROGRESS => EINPROGRESS,
+            libc::EALREADY => EALREADY,
+            libc::ENOTSOCK => ENOTSOCK,
+            libc::EDESTADDRREQ => EDESTADDRREQ,
+            libc::EMSGSIZE => EMSGSIZE,
+            libc::EPROTOTYPE => EPROTOTYPE,
+            libc::ENOPROTOOPT => ENOPROTOOPT,
+            libc::EPROTONOSUPPORT => EPROTONOSUPPORT,
+            libc::ENOTSUP => ENOTSUP,
+            libc::EADDRINUSE => EADDRINUSE,
+            libc::EADDRNOTAVAIL => EADDRNOTAVAIL,
+            libc::ENETDOWN => ENETDOWN,
+            libc::ENETUNREACH => ENETUNREACH,
+            libc::ENETRESET => ENETRESET,
+            libc::ECONNABORTED => ECONNABORTED,
+            libc::ECONNRESET => ECONNRESET,
+            libc::ENOBUFS => ENOBUFS,
+            libc::EISCONN => EISCONN,
+            libc::ENOTCONN => ENOTCONN,
+            libc::ESHUTDOWN => ESHUTDOWN,
+            libc::ETIMEDOUT => ETIMEDOUT,
+            libc::ECONNREFUSED => ECONNREFUSED,
+            libc::ELOOP => ELOOP,
+            libc::ENAMETOOLONG => ENAMETOOLONG,
+            libc::EHOSTDOWN => EHOSTDOWN,
+            libc::EHOSTUNREACH => EHOSTUNREACH,
+            libc::ENOTEMPTY => ENOTEMPTY,
+            libc::EDQUOT => EDQUOT,
+            libc::ESTALE => ESTALE,
+            libc::ENOLCK => ENOLCK,
+            libc::ENOSYS => ENOSYS,
+            libc::EIDRM => EIDRM,
+            libc::ENOMSG => ENOMSG,
+            libc::EOVERFLOW => EOVERFLOW,
+            libc::ECANCELED => ECANCELED,
+            libc::EILSEQ => EILSEQ,
+            libc::ENOATTR => ENOATTR,
+            libc::EBADMSG => EBADMSG,
+            libc::EMULTIHOP => EMULTIHOP,
+            libc::ENOLINK => ENOLINK,
+            libc::EPROTO => EPROTO,
+            _ => UnknownErrno,
+        }
+    }
+}
diff --git a/src/fcntl.rs b/src/fcntl.rs
index dd8e59a..6508283 100644
--- a/src/fcntl.rs
+++ b/src/fcntl.rs
@@ -5,27 +5,28 @@
 use std::os::raw;
 use std::os::unix::ffi::OsStringExt;
 use std::os::unix::io::RawFd;
-use crate::sys::stat::Mode;
-use crate::{NixPath, Result};
 
+#[cfg(feature = "fs")]
+use crate::{sys::stat::Mode, NixPath, Result};
 #[cfg(any(target_os = "android", target_os = "linux"))]
 use std::ptr; // For splice and copy_file_range
-#[cfg(any(target_os = "android", target_os = "linux"))]
-use crate::sys::uio::IoVec; // For vmsplice
 
 #[cfg(any(
     target_os = "linux",
     target_os = "android",
     target_os = "emscripten",
     target_os = "fuchsia",
-    any(target_os = "wasi", target_env = "wasi"),
+    target_os = "wasi",
     target_env = "uclibc",
     target_os = "freebsd"
 ))]
-pub use self::posix_fadvise::*;
+#[cfg(feature = "fs")]
+pub use self::posix_fadvise::{posix_fadvise, PosixFadviseAdvice};
 
 #[cfg(not(target_os = "redox"))]
+#[cfg(any(feature = "fs", feature = "process"))]
 libc_bitflags! {
+    #[cfg_attr(docsrs, doc(cfg(any(feature = "fs", feature = "process"))))]
     pub struct AtFlags: c_int {
         AT_REMOVEDIR;
         AT_SYMLINK_FOLLOW;
@@ -39,18 +40,22 @@
     }
 }
 
+#[cfg(any(feature = "fs", feature = "term"))]
 libc_bitflags!(
     /// Configuration options for opened files.
+    #[cfg_attr(docsrs, doc(cfg(any(feature = "fs", feature = "term"))))]
     pub struct OFlag: c_int {
         /// Mask for the access mode of the file.
         O_ACCMODE;
         /// Use alternate I/O semantics.
         #[cfg(target_os = "netbsd")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         O_ALT_IO;
         /// Open the file in append-only mode.
         O_APPEND;
         /// Generate a signal when input or output becomes possible.
-        #[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
+        #[cfg(not(any(target_os = "illumos", target_os = "solaris", target_os = "haiku")))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         O_ASYNC;
         /// Closes the file descriptor once an `execve` call is made.
         ///
@@ -64,9 +69,11 @@
                   target_os = "freebsd",
                   target_os = "linux",
                   target_os = "netbsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         O_DIRECT;
         /// If the specified path isn't a directory, fail.
         #[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         O_DIRECTORY;
         /// Implicitly follow each `write()` with an `fdatasync()`.
         #[cfg(any(target_os = "android",
@@ -75,11 +82,13 @@
                   target_os = "macos",
                   target_os = "netbsd",
                   target_os = "openbsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         O_DSYNC;
         /// Error out if a file was not created.
         O_EXCL;
         /// Open for execute only.
         #[cfg(target_os = "freebsd")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         O_EXEC;
         /// Open with an exclusive file lock.
         #[cfg(any(target_os = "dragonfly",
@@ -89,6 +98,7 @@
                   target_os = "netbsd",
                   target_os = "openbsd",
                   target_os = "redox"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         O_EXLOCK;
         /// Same as `O_SYNC`.
         #[cfg(any(target_os = "dragonfly",
@@ -99,18 +109,23 @@
                   target_os = "netbsd",
                   target_os = "openbsd",
                   target_os = "redox"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         O_FSYNC;
         /// Allow files whose sizes can't be represented in an `off_t` to be opened.
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         O_LARGEFILE;
         /// Do not update the file last access time during `read(2)`s.
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         O_NOATIME;
         /// Don't attach the device as the process' controlling terminal.
         #[cfg(not(target_os = "redox"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         O_NOCTTY;
         /// Same as `O_NONBLOCK`.
-        #[cfg(not(target_os = "redox"))]
+        #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         O_NDELAY;
         /// `open()` will fail if the given path is a symbolic link.
         O_NOFOLLOW;
@@ -118,11 +133,13 @@
         O_NONBLOCK;
         /// Don't deliver `SIGPIPE`.
         #[cfg(target_os = "netbsd")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         O_NOSIGPIPE;
         /// Obtain a file descriptor for low-level access.
         ///
         /// The file itself is not opened and other file operations will fail.
         #[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         O_PATH;
         /// Only allow reading.
         ///
@@ -134,9 +151,11 @@
         O_RDWR;
         /// Similar to `O_DSYNC` but applies to `read`s instead.
         #[cfg(any(target_os = "linux", target_os = "netbsd", target_os = "openbsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         O_RSYNC;
         /// Skip search permission checks.
         #[cfg(target_os = "netbsd")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         O_SEARCH;
         /// Open with a shared file lock.
         #[cfg(any(target_os = "dragonfly",
@@ -146,17 +165,21 @@
                   target_os = "netbsd",
                   target_os = "openbsd",
                   target_os = "redox"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         O_SHLOCK;
         /// Implicitly follow each `write()` with an `fsync()`.
         #[cfg(not(target_os = "redox"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         O_SYNC;
         /// Create an unnamed temporary file.
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         O_TMPFILE;
         /// Truncate an existing regular file to 0 length if it allows writing.
         O_TRUNC;
         /// Restore default TTY attributes.
         #[cfg(target_os = "freebsd")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         O_TTY_INIT;
         /// Only allow writing.
         ///
@@ -165,6 +188,9 @@
     }
 );
 
+feature! {
+#![feature = "fs"]
+
 // The conversion is not identical on all operating systems.
 #[allow(clippy::useless_conversion)]
 pub fn open<P: ?Sized + NixPath>(path: &P, oflag: OFlag, mode: Mode) -> Result<RawFd> {
@@ -209,12 +235,12 @@
     })??;
     Errno::result(res).map(drop)
 }
+}
 
-#[cfg(all(
-    target_os = "linux",
-    target_env = "gnu",
-))]
+#[cfg(all(target_os = "linux", target_env = "gnu",))]
+#[cfg(feature = "fs")]
 libc_bitflags! {
+    #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
     pub struct RenameFlags: u32 {
         RENAME_EXCHANGE;
         RENAME_NOREPLACE;
@@ -222,6 +248,8 @@
     }
 }
 
+feature! {
+#![feature = "fs"]
 #[cfg(all(
     target_os = "linux",
     target_env = "gnu",
@@ -348,10 +376,13 @@
         Some(fd) => fd,
     }
 }
+}
 
-#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
+#[cfg(feature = "fs")]
 libc_bitflags!(
     /// Additional flags for file sealing, which allows for limiting operations on a file.
+    #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
     pub struct SealFlag: c_int {
         /// Prevents further calls to `fcntl()` with `F_ADD_SEALS`.
         F_SEAL_SEAL;
@@ -364,14 +395,19 @@
     }
 );
 
+#[cfg(feature = "fs")]
 libc_bitflags!(
     /// Additional configuration flags for `fcntl`'s `F_SETFD`.
+    #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
     pub struct FdFlag: c_int {
         /// The file descriptor will automatically be closed during a successful `execve(2)`.
         FD_CLOEXEC;
     }
 );
 
+feature! {
+#![feature = "fs"]
+
 #[cfg(not(target_os = "redox"))]
 #[derive(Debug, Eq, Hash, PartialEq)]
 #[non_exhaustive]
@@ -391,9 +427,9 @@
     F_OFD_SETLKW(&'a libc::flock),
     #[cfg(any(target_os = "linux", target_os = "android"))]
     F_OFD_GETLK(&'a mut libc::flock),
-    #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
     F_ADD_SEALS(SealFlag),
-    #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
     F_GET_SEALS,
     #[cfg(any(target_os = "macos", target_os = "ios"))]
     F_FULLFSYNC,
@@ -439,9 +475,9 @@
             F_OFD_SETLKW(flock) => libc::fcntl(fd, libc::F_OFD_SETLKW, flock),
             #[cfg(any(target_os = "android", target_os = "linux"))]
             F_OFD_GETLK(flock) => libc::fcntl(fd, libc::F_OFD_GETLK, flock),
-            #[cfg(any(target_os = "android", target_os = "linux"))]
+            #[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
             F_ADD_SEALS(flag) => libc::fcntl(fd, libc::F_ADD_SEALS, flag.bits()),
-            #[cfg(any(target_os = "android", target_os = "linux"))]
+            #[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
             F_GET_SEALS => libc::fcntl(fd, libc::F_GET_SEALS),
             #[cfg(any(target_os = "macos", target_os = "ios"))]
             F_FULLFSYNC => libc::fcntl(fd, libc::F_FULLFSYNC),
@@ -455,6 +491,7 @@
     Errno::result(res)
 }
 
+// TODO: convert to libc_enum
 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
 #[non_exhaustive]
 pub enum FlockArg {
@@ -483,10 +520,13 @@
 
     Errno::result(res).map(drop)
 }
+}
 
 #[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg(feature = "zerocopy")]
 libc_bitflags! {
     /// Additional flags to `splice` and friends.
+    #[cfg_attr(docsrs, doc(cfg(feature = "zerocopy")))]
     pub struct SpliceFFlags: c_uint {
         /// Request that pages be moved instead of copied.
         ///
@@ -505,6 +545,9 @@
     }
 }
 
+feature! {
+#![feature = "zerocopy"]
+
 /// Copy a range of data from one file to another
 ///
 /// The `copy_file_range` system call performs an in-kernel copy between
@@ -577,7 +620,12 @@
 }
 
 #[cfg(any(target_os = "linux", target_os = "android"))]
-pub fn vmsplice(fd: RawFd, iov: &[IoVec<&[u8]>], flags: SpliceFFlags) -> Result<usize> {
+pub fn vmsplice(
+    fd: RawFd,
+    iov: &[std::io::IoSlice<'_>],
+    flags: SpliceFFlags
+    ) -> Result<usize>
+{
     let ret = unsafe {
         libc::vmsplice(
             fd,
@@ -588,10 +636,13 @@
     };
     Errno::result(ret).map(|r| r as usize)
 }
+}
 
 #[cfg(any(target_os = "linux"))]
+#[cfg(feature = "fs")]
 libc_bitflags!(
     /// Mode argument flags for fallocate determining operation performed on a given range.
+    #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
     pub struct FallocateFlags: c_int {
         /// File size is not changed.
         ///
@@ -620,11 +671,15 @@
     }
 );
 
+feature! {
+#![feature = "fs"]
+
 /// Manipulates file space.
 ///
 /// Allows the caller to directly manipulate the allocated disk space for the
 /// file referred to by fd.
 #[cfg(any(target_os = "linux"))]
+#[cfg(feature = "fs")]
 pub fn fallocate(
     fd: RawFd,
     mode: FallocateFlags,
@@ -635,12 +690,136 @@
     Errno::result(res).map(drop)
 }
 
+/// Argument to [`fspacectl`] describing the range to zero.  The first member is
+/// the file offset, and the second is the length of the region.
+#[cfg(any(target_os = "freebsd"))]
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub struct SpacectlRange(pub libc::off_t, pub libc::off_t);
+
+#[cfg(any(target_os = "freebsd"))]
+impl SpacectlRange {
+    #[inline]
+    pub fn is_empty(&self) -> bool {
+        self.1 == 0
+    }
+
+    #[inline]
+    pub fn len(&self) -> libc::off_t {
+        self.1
+    }
+
+    #[inline]
+    pub fn offset(&self) -> libc::off_t {
+        self.0
+    }
+}
+
+/// Punch holes in a file.
+///
+/// `fspacectl` instructs the file system to deallocate a portion of a file.
+/// After a successful operation, this region of the file will return all zeroes
+/// if read.  If the file system supports deallocation, then it may free the
+/// underlying storage, too.
+///
+/// # Arguments
+///
+/// - `fd`      -   File to operate on
+/// - `range.0` -   File offset at which to begin deallocation
+/// - `range.1` -   Length of the region to deallocate
+///
+/// # Returns
+///
+/// The operation may deallocate less than the entire requested region.  On
+/// success, it returns the region that still remains to be deallocated.  The
+/// caller should loop until the returned region is empty.
+///
+/// # Example
+///
+#[cfg_attr(fbsd14, doc = " ```")]
+#[cfg_attr(not(fbsd14), doc = " ```no_run")]
+/// # use std::io::Write;
+/// # use std::os::unix::fs::FileExt;
+/// # use std::os::unix::io::AsRawFd;
+/// # use nix::fcntl::*;
+/// # use tempfile::tempfile;
+/// const INITIAL: &[u8] = b"0123456789abcdef";
+/// let mut f = tempfile().unwrap();
+/// f.write_all(INITIAL).unwrap();
+/// let mut range = SpacectlRange(3, 6);
+/// while (!range.is_empty()) {
+///     range = fspacectl(f.as_raw_fd(), range).unwrap();
+/// }
+/// let mut buf = vec![0; INITIAL.len()];
+/// f.read_exact_at(&mut buf, 0).unwrap();
+/// assert_eq!(buf, b"012\0\0\0\0\0\09abcdef");
+/// ```
+#[cfg(target_os = "freebsd")]
+pub fn fspacectl(fd: RawFd, range: SpacectlRange) -> Result<SpacectlRange> {
+    let mut rqsr = libc::spacectl_range{r_offset: range.0, r_len: range.1};
+    let res = unsafe { libc::fspacectl(
+            fd,
+            libc::SPACECTL_DEALLOC, // Only one command is supported ATM
+            &rqsr,
+            0,                      // No flags are currently supported
+            &mut rqsr
+    )};
+    Errno::result(res).map(|_| SpacectlRange(rqsr.r_offset, rqsr.r_len))
+}
+
+/// Like [`fspacectl`], but will never return incomplete.
+///
+/// # Arguments
+///
+/// - `fd`      -   File to operate on
+/// - `offset`  -   File offset at which to begin deallocation
+/// - `len`     -   Length of the region to deallocate
+///
+/// # Returns
+///
+/// Returns `()` on success.  On failure, the region may or may not be partially
+/// deallocated.
+///
+/// # Example
+///
+#[cfg_attr(fbsd14, doc = " ```")]
+#[cfg_attr(not(fbsd14), doc = " ```no_run")]
+/// # use std::io::Write;
+/// # use std::os::unix::fs::FileExt;
+/// # use std::os::unix::io::AsRawFd;
+/// # use nix::fcntl::*;
+/// # use tempfile::tempfile;
+/// const INITIAL: &[u8] = b"0123456789abcdef";
+/// let mut f = tempfile().unwrap();
+/// f.write_all(INITIAL).unwrap();
+/// fspacectl_all(f.as_raw_fd(), 3, 6).unwrap();
+/// let mut buf = vec![0; INITIAL.len()];
+/// f.read_exact_at(&mut buf, 0).unwrap();
+/// assert_eq!(buf, b"012\0\0\0\0\0\09abcdef");
+/// ```
+#[cfg(target_os = "freebsd")]
+pub fn fspacectl_all(fd: RawFd, offset: libc::off_t, len: libc::off_t)
+    -> Result<()>
+{
+    let mut rqsr = libc::spacectl_range{r_offset: offset, r_len: len};
+    while rqsr.r_len > 0 {
+        let res = unsafe { libc::fspacectl(
+                fd,
+                libc::SPACECTL_DEALLOC, // Only one command is supported ATM
+                &rqsr,
+                0,                      // No flags are currently supported
+                &mut rqsr
+        )};
+        Errno::result(res)?;
+    }
+    Ok(())
+}
+
 #[cfg(any(
     target_os = "linux",
     target_os = "android",
     target_os = "emscripten",
     target_os = "fuchsia",
-    any(target_os = "wasi", target_env = "wasi"),
+    target_os = "wasi",
     target_env = "uclibc",
     target_os = "freebsd"
 ))]
@@ -649,9 +828,11 @@
     use std::os::unix::io::RawFd;
     use crate::Result;
 
+    #[cfg(feature = "fs")]
     libc_enum! {
         #[repr(i32)]
         #[non_exhaustive]
+        #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
         pub enum PosixFadviseAdvice {
             POSIX_FADV_NORMAL,
             POSIX_FADV_SEQUENTIAL,
@@ -662,6 +843,8 @@
         }
     }
 
+    feature! {
+    #![feature = "fs"]
     pub fn posix_fadvise(
         fd: RawFd,
         offset: libc::off_t,
@@ -676,14 +859,16 @@
             Err(Errno::from_i32(res))
         }
     }
+    }
 }
 
 #[cfg(any(
     target_os = "linux",
     target_os = "android",
+    target_os = "dragonfly",
     target_os = "emscripten",
     target_os = "fuchsia",
-    any(target_os = "wasi", target_env = "wasi"),
+    target_os = "wasi",
     target_os = "freebsd"
 ))]
 pub fn posix_fallocate(fd: RawFd, offset: libc::off_t, len: libc::off_t) -> Result<()> {
@@ -694,3 +879,4 @@
         Ok(errno) => Err(Errno::from_i32(errno)),
     }
 }
+}
diff --git a/src/features.rs b/src/features.rs
index ed80fd7..39d1760 100644
--- a/src/features.rs
+++ b/src/features.rs
@@ -4,6 +4,8 @@
 #[cfg(any(target_os = "linux", target_os = "android"))]
 mod os {
     use crate::sys::utsname::uname;
+    use crate::Result;
+    use std::os::unix::ffi::OsStrExt;
 
     // Features:
     // * atomic cloexec on socket: 2.6.27
@@ -11,10 +13,10 @@
     // * accept4: 2.6.28
 
     static VERS_UNKNOWN: usize = 1;
-    static VERS_2_6_18:  usize = 2;
-    static VERS_2_6_27:  usize = 3;
-    static VERS_2_6_28:  usize = 4;
-    static VERS_3:       usize = 5;
+    static VERS_2_6_18: usize = 2;
+    static VERS_2_6_27: usize = 3;
+    static VERS_2_6_28: usize = 4;
+    static VERS_3: usize = 5;
 
     #[inline]
     fn digit(dst: &mut usize, b: u8) {
@@ -22,15 +24,15 @@
         *dst += (b - b'0') as usize;
     }
 
-    fn parse_kernel_version() -> usize {
-        let u = uname();
+    fn parse_kernel_version() -> Result<usize> {
+        let u = uname()?;
 
-        let mut curr:  usize = 0;
+        let mut curr: usize = 0;
         let mut major: usize = 0;
         let mut minor: usize = 0;
         let mut patch: usize = 0;
 
-        for b in u.release().bytes() {
+        for &b in u.release().as_bytes() {
             if curr >= 3 {
                 break;
             }
@@ -39,18 +41,16 @@
                 b'.' | b'-' => {
                     curr += 1;
                 }
-                b'0'..=b'9' => {
-                    match curr {
-                        0 => digit(&mut major, b),
-                        1 => digit(&mut minor, b),
-                        _ => digit(&mut patch, b),
-                    }
-                }
+                b'0'..=b'9' => match curr {
+                    0 => digit(&mut major, b),
+                    1 => digit(&mut minor, b),
+                    _ => digit(&mut patch, b),
+                },
                 _ => break,
             }
         }
 
-        if major >= 3 {
+        Ok(if major >= 3 {
             VERS_3
         } else if major >= 2 {
             if minor >= 7 {
@@ -68,29 +68,31 @@
             }
         } else {
             VERS_UNKNOWN
-        }
+        })
     }
 
-    fn kernel_version() -> usize {
+    fn kernel_version() -> Result<usize> {
         static mut KERNEL_VERS: usize = 0;
 
         unsafe {
             if KERNEL_VERS == 0 {
-                KERNEL_VERS = parse_kernel_version();
+                KERNEL_VERS = parse_kernel_version()?;
             }
 
-            KERNEL_VERS
+            Ok(KERNEL_VERS)
         }
     }
 
     /// Check if the OS supports atomic close-on-exec for sockets
     pub fn socket_atomic_cloexec() -> bool {
-        kernel_version() >= VERS_2_6_27
+        kernel_version()
+            .map(|version| version >= VERS_2_6_27)
+            .unwrap_or(false)
     }
 
     #[test]
     pub fn test_parsing_kernel_version() {
-        assert!(kernel_version() > 0);
+        assert!(kernel_version().unwrap() > 0);
     }
 }
 
@@ -109,10 +111,13 @@
     }
 }
 
-#[cfg(any(target_os = "macos",
-          target_os = "ios",
-          target_os = "fuchsia",
-          target_os = "solaris"))]
+#[cfg(any(
+    target_os = "macos",
+    target_os = "ios",
+    target_os = "fuchsia",
+    target_os = "haiku",
+    target_os = "solaris"
+))]
 mod os {
     /// Check if the OS supports atomic close-on-exec for sockets
     pub const fn socket_atomic_cloexec() -> bool {
diff --git a/src/ifaddrs.rs b/src/ifaddrs.rs
index ed6328f..70b50b0 100644
--- a/src/ifaddrs.rs
+++ b/src/ifaddrs.rs
@@ -4,14 +4,16 @@
 //! of interfaces and their associated addresses.
 
 use cfg_if::cfg_if;
+#[cfg(any(target_os = "ios", target_os = "macos"))]
+use std::convert::TryFrom;
 use std::ffi;
 use std::iter::Iterator;
 use std::mem;
 use std::option::Option;
 
-use crate::{Result, Errno};
-use crate::sys::socket::SockAddr;
 use crate::net::if_::*;
+use crate::sys::socket::{SockaddrLike, SockaddrStorage};
+use crate::{Errno, Result};
 
 /// Describes a single address for an interface as returned by `getifaddrs`.
 #[derive(Clone, Debug, Eq, Hash, PartialEq)]
@@ -21,13 +23,13 @@
     /// Flags as from `SIOCGIFFLAGS` ioctl
     pub flags: InterfaceFlags,
     /// Network address of this interface
-    pub address: Option<SockAddr>,
+    pub address: Option<SockaddrStorage>,
     /// Netmask of this interface
-    pub netmask: Option<SockAddr>,
+    pub netmask: Option<SockaddrStorage>,
     /// Broadcast address of this interface, if applicable
-    pub broadcast: Option<SockAddr>,
+    pub broadcast: Option<SockaddrStorage>,
     /// Point-to-point destination address
-    pub destination: Option<SockAddr>,
+    pub destination: Option<SockaddrStorage>,
 }
 
 cfg_if! {
@@ -42,12 +44,52 @@
     }
 }
 
+/// Workaround a bug in XNU where netmasks will always have the wrong size in
+/// the sa_len field due to the kernel ignoring trailing zeroes in the structure
+/// when setting the field. See https://github.com/nix-rust/nix/issues/1709#issuecomment-1199304470
+///
+/// To fix this, we stack-allocate a new sockaddr_storage, zero it out, and
+/// memcpy sa_len of the netmask to that new storage. Finally, we reset the
+/// ss_len field to sizeof(sockaddr_storage). This is supposedly valid as all
+/// members of the sockaddr_storage are "ok" with being zeroed out (there are
+/// no pointers).
+#[cfg(any(target_os = "ios", target_os = "macos"))]
+unsafe fn workaround_xnu_bug(info: &libc::ifaddrs) -> Option<SockaddrStorage> {
+    let src_sock = info.ifa_netmask;
+    if src_sock.is_null() {
+        return None;
+    }
+
+    let mut dst_sock = mem::MaybeUninit::<libc::sockaddr_storage>::zeroed();
+
+    // memcpy only sa_len bytes, assume the rest is zero
+    std::ptr::copy_nonoverlapping(
+        src_sock as *const u8,
+        dst_sock.as_mut_ptr() as *mut u8,
+        (*src_sock).sa_len.into(),
+    );
+
+    // Initialize ss_len to sizeof(libc::sockaddr_storage).
+    (*dst_sock.as_mut_ptr()).ss_len =
+        u8::try_from(mem::size_of::<libc::sockaddr_storage>()).unwrap();
+    let dst_sock = dst_sock.assume_init();
+
+    let dst_sock_ptr =
+        &dst_sock as *const libc::sockaddr_storage as *const libc::sockaddr;
+
+    SockaddrStorage::from_raw(dst_sock_ptr, None)
+}
+
 impl InterfaceAddress {
     /// Create an `InterfaceAddress` from the libc struct.
     fn from_libc_ifaddrs(info: &libc::ifaddrs) -> InterfaceAddress {
         let ifname = unsafe { ffi::CStr::from_ptr(info.ifa_name) };
-        let address = unsafe { SockAddr::from_libc_sockaddr(info.ifa_addr) };
-        let netmask = unsafe { SockAddr::from_libc_sockaddr(info.ifa_netmask) };
+        let address = unsafe { SockaddrStorage::from_raw(info.ifa_addr, None) };
+        #[cfg(any(target_os = "ios", target_os = "macos"))]
+        let netmask = unsafe { workaround_xnu_bug(info) };
+        #[cfg(not(any(target_os = "ios", target_os = "macos")))]
+        let netmask =
+            unsafe { SockaddrStorage::from_raw(info.ifa_netmask, None) };
         let mut addr = InterfaceAddress {
             interface_name: ifname.to_string_lossy().to_string(),
             flags: InterfaceFlags::from_bits_truncate(info.ifa_flags as i32),
@@ -59,9 +101,9 @@
 
         let ifu = get_ifu_from_sockaddr(info);
         if addr.flags.contains(InterfaceFlags::IFF_POINTOPOINT) {
-            addr.destination = unsafe { SockAddr::from_libc_sockaddr(ifu) };
+            addr.destination = unsafe { SockaddrStorage::from_raw(ifu, None) };
         } else if addr.flags.contains(InterfaceFlags::IFF_BROADCAST) {
-            addr.broadcast = unsafe { SockAddr::from_libc_sockaddr(ifu) };
+            addr.broadcast = unsafe { SockaddrStorage::from_raw(ifu, None) };
         }
 
         addr
@@ -103,9 +145,9 @@
 /// Note that the underlying implementation differs between OSes. Only the
 /// most common address families are supported by the nix crate (due to
 /// lack of time and complexity of testing). The address family is encoded
-/// in the specific variant of `SockAddr` returned for the fields `address`,
-/// `netmask`, `broadcast`, and `destination`. For any entry not supported,
-/// the returned list will contain a `None` entry.
+/// in the specific variant of `SockaddrStorage` returned for the fields
+/// `address`, `netmask`, `broadcast`, and `destination`. For any entry not
+/// supported, the returned list will contain a `None` entry.
 ///
 /// # Example
 /// ```
@@ -144,4 +186,28 @@
     fn test_getifaddrs() {
         let _ = getifaddrs();
     }
+
+    // Ensures getting the netmask works, and in particular that
+    // `workaround_xnu_bug` works properly.
+    #[test]
+    fn test_getifaddrs_netmask_correct() {
+        let addrs = getifaddrs().unwrap();
+        for iface in addrs {
+            let sock = if let Some(sock) = iface.netmask {
+                sock
+            } else {
+                continue;
+            };
+            if sock.family() == Some(crate::sys::socket::AddressFamily::Inet) {
+                let _ = sock.as_sockaddr_in().unwrap();
+                return;
+            } else if sock.family()
+                == Some(crate::sys::socket::AddressFamily::Inet6)
+            {
+                let _ = sock.as_sockaddr_in6().unwrap();
+                return;
+            }
+        }
+        panic!("No address?");
+    }
 }
diff --git a/src/kmod.rs b/src/kmod.rs
index c42068c..1fa6c17 100644
--- a/src/kmod.rs
+++ b/src/kmod.rs
@@ -79,7 +79,11 @@
 /// ```
 ///
 /// See [`man init_module(2)`](https://man7.org/linux/man-pages/man2/init_module.2.html) for more information.
-pub fn finit_module<T: AsRawFd>(fd: &T, param_values: &CStr, flags: ModuleInitFlags) -> Result<()> {
+pub fn finit_module<T: AsRawFd>(
+    fd: &T,
+    param_values: &CStr,
+    flags: ModuleInitFlags,
+) -> Result<()> {
     let res = unsafe {
         libc::syscall(
             libc::SYS_finit_module,
@@ -116,7 +120,9 @@
 ///
 /// See [`man delete_module(2)`](https://man7.org/linux/man-pages/man2/delete_module.2.html) for more information.
 pub fn delete_module(name: &CStr, flags: DeleteModuleFlags) -> Result<()> {
-    let res = unsafe { libc::syscall(libc::SYS_delete_module, name.as_ptr(), flags.bits()) };
+    let res = unsafe {
+        libc::syscall(libc::SYS_delete_module, name.as_ptr(), flags.bits())
+    };
 
     Errno::result(res).map(drop)
 }
diff --git a/src/lib.rs b/src/lib.rs
index 3a2b63a..6b82125 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -2,88 +2,163 @@
 //!
 //! Modules are structured according to the C header file that they would be
 //! defined in.
+//!
+//! # Features
+//!
+//! Nix uses the following Cargo features to enable optional functionality.
+//! They may be enabled in any combination.
+//! * `acct` - Process accounting
+//! * `aio` - POSIX AIO
+//! * `dir` - Stuff relating to directory iteration
+//! * `env` - Manipulate environment variables
+//! * `event` - Event-driven APIs, like `kqueue` and `epoll`
+//! * `feature` - Query characteristics of the OS at runtime
+//! * `fs` - File system functionality
+//! * `hostname` - Get and set the system's hostname
+//! * `inotify` - Linux's `inotify` file system notification API
+//! * `ioctl` - The `ioctl` syscall, and wrappers for my specific instances
+//! * `kmod` - Load and unload kernel modules
+//! * `mman` - Stuff relating to memory management
+//! * `mount` - Mount and unmount file systems
+//! * `mqueue` - POSIX message queues
+//! * `net` - Networking-related functionality
+//! * `personality` - Set the process execution domain
+//! * `poll` - APIs like `poll` and `select`
+//! * `process` - Stuff relating to running processes
+//! * `pthread` - POSIX threads
+//! * `ptrace` - Process tracing and debugging
+//! * `quota` - File system quotas
+//! * `reboot` - Reboot the system
+//! * `resource` - Process resource limits
+//! * `sched` - Manipulate process's scheduling
+//! * `socket` - Sockets, whether for networking or local use
+//! * `signal` - Send and receive signals to processes
+//! * `term` - Terminal control APIs
+//! * `time` - Query the operating system's clocks
+//! * `ucontext` - User thread context
+//! * `uio` - Vectored I/O
+//! * `user` - Stuff relating to users and groups
+//! * `zerocopy` - APIs like `sendfile` and `copy_file_range`
 #![crate_name = "nix"]
 #![cfg(unix)]
+#![cfg_attr(docsrs, doc(cfg(all())))]
 #![allow(non_camel_case_types)]
 #![cfg_attr(test, deny(warnings))]
 #![recursion_limit = "500"]
 #![deny(unused)]
+#![allow(unused_macros)]
+#![cfg_attr(not(feature = "default"), allow(unused_imports))]
 #![deny(unstable_features)]
 #![deny(missing_copy_implementations)]
 #![deny(missing_debug_implementations)]
 #![warn(missing_docs)]
+#![cfg_attr(docsrs, feature(doc_cfg))]
+#![deny(clippy::cast_ptr_alignment)]
 
 // Re-exported external crates
 pub use libc;
 
 // Private internal modules
-#[macro_use] mod macros;
+#[macro_use]
+mod macros;
 
 // Public crates
 #[cfg(not(target_os = "redox"))]
-#[allow(missing_docs)]
-pub mod dir;
-pub mod env;
+feature! {
+    #![feature = "dir"]
+    pub mod dir;
+}
+feature! {
+    #![feature = "env"]
+    pub mod env;
+}
 #[allow(missing_docs)]
 pub mod errno;
-pub mod features;
+feature! {
+    #![feature = "feature"]
+
+    #[deny(missing_docs)]
+    pub mod features;
+}
 #[allow(missing_docs)]
 pub mod fcntl;
-#[cfg(any(target_os = "android",
-          target_os = "dragonfly",
-          target_os = "freebsd",
-          target_os = "ios",
-          target_os = "linux",
-          target_os = "macos",
-          target_os = "netbsd",
-          target_os = "illumos",
-          target_os = "openbsd"))]
-pub mod ifaddrs;
-#[cfg(any(target_os = "android",
-          target_os = "linux"))]
-#[allow(missing_docs)]
-pub mod kmod;
-#[cfg(any(target_os = "android",
-          target_os = "freebsd",
-          target_os = "linux"))]
-pub mod mount;
-#[cfg(any(target_os = "dragonfly",
-          target_os = "freebsd",
-          target_os = "fushsia",
-          target_os = "linux",
-          target_os = "netbsd"))]
-#[allow(missing_docs)]
-pub mod mqueue;
-#[cfg(not(target_os = "redox"))]
-pub mod net;
-pub mod poll;
+feature! {
+    #![feature = "net"]
+
+    #[cfg(any(target_os = "android",
+              target_os = "dragonfly",
+              target_os = "freebsd",
+              target_os = "ios",
+              target_os = "linux",
+              target_os = "macos",
+              target_os = "netbsd",
+              target_os = "illumos",
+              target_os = "openbsd"))]
+    #[deny(missing_docs)]
+    pub mod ifaddrs;
+    #[cfg(not(target_os = "redox"))]
+    #[deny(missing_docs)]
+    pub mod net;
+}
+#[cfg(any(target_os = "android", target_os = "linux"))]
+feature! {
+    #![feature = "kmod"]
+    #[allow(missing_docs)]
+    pub mod kmod;
+}
+feature! {
+    #![feature = "mount"]
+    pub mod mount;
+}
+#[cfg(any(
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "linux",
+    target_os = "netbsd"
+))]
+feature! {
+    #![feature = "mqueue"]
+    pub mod mqueue;
+}
+feature! {
+    #![feature = "poll"]
+    pub mod poll;
+}
 #[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
-pub mod pty;
-pub mod sched;
+feature! {
+    #![feature = "term"]
+    #[deny(missing_docs)]
+    pub mod pty;
+}
+feature! {
+    #![feature = "sched"]
+    pub mod sched;
+}
 pub mod sys;
-#[allow(missing_docs)]
-pub mod time;
+feature! {
+    #![feature = "time"]
+    #[allow(missing_docs)]
+    pub mod time;
+}
 // This can be implemented for other platforms as soon as libc
 // provides bindings for them.
-#[cfg(all(target_os = "linux",
-          any(target_arch = "x86", target_arch = "x86_64")))]
-#[allow(missing_docs)]
-pub mod ucontext;
+#[cfg(all(
+    target_os = "linux",
+    any(target_arch = "s390x", target_arch = "x86", target_arch = "x86_64")
+))]
+feature! {
+    #![feature = "ucontext"]
+    #[allow(missing_docs)]
+    pub mod ucontext;
+}
 #[allow(missing_docs)]
 pub mod unistd;
 
-/*
- *
- * ===== Result / Error =====
- *
- */
-
-use libc::{c_char, PATH_MAX};
-
-use std::{ptr, result};
-use std::ffi::{CStr, OsStr};
+use std::ffi::{CStr, CString, OsStr};
+use std::mem::MaybeUninit;
 use std::os::unix::ffi::OsStrExt;
 use std::path::{Path, PathBuf};
+use std::{ptr, result, slice};
 
 use errno::Errno;
 
@@ -114,7 +189,8 @@
     ///
     /// Mostly used internally by Nix.
     fn with_nix_path<T, F>(&self, f: F) -> Result<T>
-        where F: FnOnce(&CStr) -> T;
+    where
+        F: FnOnce(&CStr) -> T;
 }
 
 impl NixPath for str {
@@ -127,9 +203,11 @@
     }
 
     fn with_nix_path<T, F>(&self, f: F) -> Result<T>
-        where F: FnOnce(&CStr) -> T {
-            OsStr::new(self).with_nix_path(f)
-        }
+    where
+        F: FnOnce(&CStr) -> T,
+    {
+        OsStr::new(self).with_nix_path(f)
+    }
 }
 
 impl NixPath for OsStr {
@@ -142,9 +220,11 @@
     }
 
     fn with_nix_path<T, F>(&self, f: F) -> Result<T>
-        where F: FnOnce(&CStr) -> T {
-            self.as_bytes().with_nix_path(f)
-        }
+    where
+        F: FnOnce(&CStr) -> T,
+    {
+        self.as_bytes().with_nix_path(f)
+    }
 }
 
 impl NixPath for CStr {
@@ -157,12 +237,9 @@
     }
 
     fn with_nix_path<T, F>(&self, f: F) -> Result<T>
-            where F: FnOnce(&CStr) -> T {
-        // Equivalence with the [u8] impl.
-        if self.len() >= PATH_MAX as usize {
-            return Err(Errno::ENAMETOOLONG)
-        }
-
+    where
+        F: FnOnce(&CStr) -> T,
+    {
         Ok(f(self))
     }
 }
@@ -177,24 +254,47 @@
     }
 
     fn with_nix_path<T, F>(&self, f: F) -> Result<T>
-            where F: FnOnce(&CStr) -> T {
-        let mut buf = [0u8; PATH_MAX as usize];
+    where
+        F: FnOnce(&CStr) -> T,
+    {
+        // The real PATH_MAX is typically 4096, but it's statistically unlikely to have a path
+        // longer than ~300 bytes. See the the PR description to get stats for your own machine.
+        // https://github.com/nix-rust/nix/pull/1656
+        //
+        // By being smaller than a memory page, we also avoid the compiler inserting a probe frame:
+        // https://docs.rs/compiler_builtins/latest/compiler_builtins/probestack/index.html
+        const MAX_STACK_ALLOCATION: usize = 1024;
 
-        if self.len() >= PATH_MAX as usize {
-            return Err(Errno::ENAMETOOLONG)
+        if self.len() >= MAX_STACK_ALLOCATION {
+            return with_nix_path_allocating(self, f);
         }
 
-        match self.iter().position(|b| *b == 0) {
-            Some(_) => Err(Errno::EINVAL),
-            None => {
-                unsafe {
-                    // TODO: Replace with bytes::copy_memory. rust-lang/rust#24028
-                    ptr::copy_nonoverlapping(self.as_ptr(), buf.as_mut_ptr(), self.len());
-                    Ok(f(CStr::from_ptr(buf.as_ptr() as *const c_char)))
-                }
+        let mut buf = MaybeUninit::<[u8; MAX_STACK_ALLOCATION]>::uninit();
+        let buf_ptr = buf.as_mut_ptr() as *mut u8;
 
-            }
+        unsafe {
+            ptr::copy_nonoverlapping(self.as_ptr(), buf_ptr, self.len());
+            buf_ptr.add(self.len()).write(0);
         }
+
+        match CStr::from_bytes_with_nul(unsafe {
+            slice::from_raw_parts(buf_ptr, self.len() + 1)
+        }) {
+            Ok(s) => Ok(f(s)),
+            Err(_) => Err(Errno::EINVAL),
+        }
+    }
+}
+
+#[cold]
+#[inline(never)]
+fn with_nix_path_allocating<T, F>(from: &[u8], f: F) -> Result<T>
+where
+    F: FnOnce(&CStr) -> T,
+{
+    match CString::new(from) {
+        Ok(s) => Ok(f(&s)),
+        Err(_) => Err(Errno::EINVAL),
     }
 }
 
@@ -207,7 +307,10 @@
         NixPath::len(self.as_os_str())
     }
 
-    fn with_nix_path<T, F>(&self, f: F) -> Result<T> where F: FnOnce(&CStr) -> T {
+    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
+    where
+        F: FnOnce(&CStr) -> T,
+    {
         self.as_os_str().with_nix_path(f)
     }
 }
@@ -221,7 +324,10 @@
         NixPath::len(self.as_os_str())
     }
 
-    fn with_nix_path<T, F>(&self, f: F) -> Result<T> where F: FnOnce(&CStr) -> T {
+    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
+    where
+        F: FnOnce(&CStr) -> T,
+    {
         self.as_os_str().with_nix_path(f)
     }
 }
diff --git a/src/macros.rs b/src/macros.rs
index 3ccbfdd..99e0de8 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -1,3 +1,17 @@
+// Thanks to Tokio for this macro
+macro_rules! feature {
+    (
+        #![$meta:meta]
+        $($item:item)*
+    ) => {
+        $(
+            #[cfg($meta)]
+            #[cfg_attr(docsrs, doc(cfg($meta)))]
+            $item
+        )*
+    }
+}
+
 /// The `libc_bitflags!` macro helps with a common use case of defining a public bitflags type
 /// with values from the libc crate. It is used the same way as the `bitflags!` macro, except
 /// that only the name of the flag value has to be given.
@@ -5,7 +19,7 @@
 /// The `libc` crate must be in scope with the name `libc`.
 ///
 /// # Example
-/// ```
+/// ```ignore
 /// libc_bitflags!{
 ///     pub struct ProtFlags: libc::c_int {
 ///         PROT_NONE;
@@ -25,7 +39,7 @@
 /// various flags have different types, so we cast the broken ones to the right
 /// type.
 ///
-/// ```
+/// ```ignore
 /// libc_bitflags!{
 ///     pub struct SaFlags: libc::c_ulong {
 ///         SA_NOCLDSTOP as libc::c_ulong;
@@ -66,7 +80,7 @@
 /// The `libc` crate must be in scope with the name `libc`.
 ///
 /// # Example
-/// ```
+/// ```ignore
 /// libc_enum!{
 ///     pub enum ProtFlags {
 ///         PROT_NONE,
@@ -80,6 +94,9 @@
 ///     }
 /// }
 /// ```
+// Some targets don't use all rules.
+#[allow(unknown_lints)]
+#[allow(unused_macro_rules)]
 macro_rules! libc_enum {
     // Exit rule.
     (@make_enum
diff --git a/src/mount/bsd.rs b/src/mount/bsd.rs
index 627bfa5..d124f1f 100644
--- a/src/mount/bsd.rs
+++ b/src/mount/bsd.rs
@@ -1,48 +1,52 @@
-use crate::{
-    Error,
-    Errno,
-    NixPath,
-    Result,
-    sys::uio::IoVec
-};
-use libc::{c_char, c_int, c_uint, c_void};
+#[cfg(target_os = "freebsd")]
+use crate::Error;
+use crate::{Errno, NixPath, Result};
+use libc::c_int;
+#[cfg(target_os = "freebsd")]
+use libc::{c_char, c_uint, c_void};
+#[cfg(target_os = "freebsd")]
 use std::{
     borrow::Cow,
-    ffi::{CString, CStr},
-    fmt,
-    io,
-    ptr
+    ffi::{CStr, CString},
+    fmt, io,
+    marker::PhantomData,
 };
 
-
 libc_bitflags!(
     /// Used with [`Nmount::nmount`].
     pub struct MntFlags: c_int {
         /// ACL support enabled.
         #[cfg(any(target_os = "netbsd", target_os = "freebsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MNT_ACLS;
         /// All I/O to the file system should be done asynchronously.
         MNT_ASYNC;
         /// dir should instead be a file system ID encoded as “FSID:val0:val1”.
         #[cfg(target_os = "freebsd")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MNT_BYFSID;
         /// Force a read-write mount even if the file system appears to be
         /// unclean.
         MNT_FORCE;
         /// GEOM journal support enabled.
         #[cfg(target_os = "freebsd")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MNT_GJOURNAL;
         /// MAC support for objects.
         #[cfg(any(target_os = "macos", target_os = "freebsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MNT_MULTILABEL;
         /// Disable read clustering.
         #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MNT_NOCLUSTERR;
         /// Disable write clustering.
         #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MNT_NOCLUSTERW;
         /// Enable NFS version 4 ACLs.
         #[cfg(target_os = "freebsd")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MNT_NFS4ACLS;
         /// Do not update access times.
         MNT_NOATIME;
@@ -52,6 +56,7 @@
         MNT_NOSUID;
         /// Do not follow symlinks.
         #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MNT_NOSYMFOLLOW;
         /// Mount read-only.
         MNT_RDONLY;
@@ -62,6 +67,7 @@
         ///
         /// See [mksnap_ffs(8)](https://www.freebsd.org/cgi/man.cgi?query=mksnap_ffs)
         #[cfg(any(target_os = "macos", target_os = "freebsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MNT_SNAPSHOT;
         /// Using soft updates.
         #[cfg(any(
@@ -70,10 +76,12 @@
                 target_os = "netbsd",
                 target_os = "openbsd"
         ))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MNT_SOFTDEP;
         /// Directories with the SUID bit set chown new files to their own
         /// owner.
         #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MNT_SUIDDIR;
         /// All I/O to the file system should be done synchronously.
         MNT_SYNCHRONOUS;
@@ -83,27 +91,30 @@
                 target_os = "freebsd",
                 target_os = "netbsd"
         ))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MNT_UNION;
         /// Indicates that the mount command is being applied to an already
         /// mounted file system.
         MNT_UPDATE;
         /// Check vnode use counts.
         #[cfg(target_os = "freebsd")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MNT_NONBUSY;
     }
 );
 
-
 /// The Error type of [`Nmount::nmount`].
 ///
 /// It wraps an [`Errno`], but also may contain an additional message returned
 /// by `nmount(2)`.
+#[cfg(target_os = "freebsd")]
 #[derive(Debug)]
 pub struct NmountError {
     errno: Error,
-    errmsg: Option<String>
+    errmsg: Option<String>,
 }
 
+#[cfg(target_os = "freebsd")]
 impl NmountError {
     /// Returns the additional error string sometimes generated by `nmount(2)`.
     pub fn errmsg(&self) -> Option<&str> {
@@ -118,13 +129,15 @@
     fn new(error: Error, errmsg: Option<&CStr>) -> Self {
         Self {
             errno: error,
-            errmsg: errmsg.map(CStr::to_string_lossy).map(Cow::into_owned)
+            errmsg: errmsg.map(CStr::to_string_lossy).map(Cow::into_owned),
         }
     }
 }
 
+#[cfg(target_os = "freebsd")]
 impl std::error::Error for NmountError {}
 
+#[cfg(target_os = "freebsd")]
 impl fmt::Display for NmountError {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         if let Some(errmsg) = &self.errmsg {
@@ -135,6 +148,7 @@
     }
 }
 
+#[cfg(target_os = "freebsd")]
 impl From<NmountError> for io::Error {
     fn from(err: NmountError) -> Self {
         err.errno.into()
@@ -142,6 +156,7 @@
 }
 
 /// Result type of [`Nmount::nmount`].
+#[cfg(target_os = "freebsd")]
 pub type NmountResult = std::result::Result<(), NmountError>;
 
 /// Mount a FreeBSD file system.
@@ -156,8 +171,9 @@
 /// To mount `target` onto `mountpoint` with `nullfs`:
 /// ```
 /// # use nix::unistd::Uid;
-/// # use ::sysctl::CtlValue;
-/// # if !Uid::current().is_root() && CtlValue::Int(0) == ::sysctl::value("vfs.usermount").unwrap() {
+/// # use ::sysctl::{CtlValue, Sysctl};
+/// # let ctl = ::sysctl::Ctl::new("vfs.usermount").unwrap();
+/// # if !Uid::current().is_root() && CtlValue::Int(0) == ctl.value().unwrap() {
 /// #     return;
 /// # };
 /// use nix::mount::{MntFlags, Nmount, unmount};
@@ -174,7 +190,7 @@
 ///     .str_opt_owned("fspath", mountpoint.path().to_str().unwrap())
 ///     .str_opt_owned("target", target.path().to_str().unwrap())
 ///     .nmount(MntFlags::empty()).unwrap();
-/// 
+///
 /// unmount(mountpoint.path(), MntFlags::empty()).unwrap();
 /// ```
 ///
@@ -182,14 +198,54 @@
 /// * [`nmount(2)`](https://www.freebsd.org/cgi/man.cgi?query=nmount)
 /// * [`nullfs(5)`](https://www.freebsd.org/cgi/man.cgi?query=nullfs)
 #[cfg(target_os = "freebsd")]
+#[cfg_attr(docsrs, doc(cfg(all())))]
 #[derive(Debug, Default)]
-pub struct Nmount<'a>{
-    iov: Vec<IoVec<&'a [u8]>>,
+pub struct Nmount<'a> {
+    // n.b. notgull: In reality, this is a list that contains
+    //               both mutable and immutable pointers.
+    //               Be careful using this.
+    iov: Vec<libc::iovec>,
     is_owned: Vec<bool>,
+    marker: PhantomData<&'a ()>,
 }
 
 #[cfg(target_os = "freebsd")]
+#[cfg_attr(docsrs, doc(cfg(all())))]
 impl<'a> Nmount<'a> {
+    /// Helper function to push a slice onto the `iov` array.
+    fn push_slice(&mut self, val: &'a [u8], is_owned: bool) {
+        self.iov.push(libc::iovec {
+            iov_base: val.as_ptr() as *mut _,
+            iov_len: val.len(),
+        });
+        self.is_owned.push(is_owned);
+    }
+
+    /// Helper function to push a pointer and its length onto the `iov` array.
+    fn push_pointer_and_length(
+        &mut self,
+        val: *const u8,
+        len: usize,
+        is_owned: bool,
+    ) {
+        self.iov.push(libc::iovec {
+            iov_base: val as *mut _,
+            iov_len: len,
+        });
+        self.is_owned.push(is_owned);
+    }
+
+    /// Helper function to push a `nix` path as owned.
+    fn push_nix_path<P: ?Sized + NixPath>(&mut self, val: &P) {
+        val.with_nix_path(|s| {
+            let len = s.to_bytes_with_nul().len();
+            let ptr = s.to_owned().into_raw() as *const u8;
+
+            self.push_pointer_and_length(ptr, len, true);
+        })
+        .unwrap();
+    }
+
     /// Add an opaque mount option.
     ///
     /// Some file systems take binary-valued mount options.  They can be set
@@ -221,13 +277,10 @@
         &mut self,
         name: &'a CStr,
         val: *mut c_void,
-        len: usize
-    ) -> &mut Self
-    {
-        self.iov.push(IoVec::from_slice(name.to_bytes_with_nul()));
-        self.is_owned.push(false);
-        self.iov.push(IoVec::from_raw_parts(val, len));
-        self.is_owned.push(false);
+        len: usize,
+    ) -> &mut Self {
+        self.push_slice(name.to_bytes_with_nul(), false);
+        self.push_pointer_and_length(val.cast(), len, false);
         self
     }
 
@@ -243,10 +296,8 @@
     ///     .null_opt(&read_only);
     /// ```
     pub fn null_opt(&mut self, name: &'a CStr) -> &mut Self {
-        self.iov.push(IoVec::from_slice(name.to_bytes_with_nul()));
-        self.is_owned.push(false);
-        self.iov.push(IoVec::from_raw_parts(ptr::null_mut(), 0));
-        self.is_owned.push(false);
+        self.push_slice(name.to_bytes_with_nul(), false);
+        self.push_slice(&[], false);
         self
     }
 
@@ -266,19 +317,12 @@
     /// let mut nmount: Nmount<'static> = Nmount::new();
     /// nmount.null_opt_owned(read_only);
     /// ```
-    pub fn null_opt_owned<P: ?Sized + NixPath>(&mut self, name: &P) -> &mut Self
-    {
-        name.with_nix_path(|s| {
-            let len = s.to_bytes_with_nul().len();
-            self.iov.push(IoVec::from_raw_parts(
-                // Must free it later
-                s.to_owned().into_raw() as *mut c_void,
-                len
-            ));
-            self.is_owned.push(true);
-        }).unwrap();
-        self.iov.push(IoVec::from_raw_parts(ptr::null_mut(), 0));
-        self.is_owned.push(false);
+    pub fn null_opt_owned<P: ?Sized + NixPath>(
+        &mut self,
+        name: &P,
+    ) -> &mut Self {
+        self.push_nix_path(name);
+        self.push_slice(&[], false);
         self
     }
 
@@ -294,16 +338,9 @@
     /// Nmount::new()
     ///     .str_opt(&fstype, &nullfs);
     /// ```
-    pub fn str_opt(
-        &mut self,
-        name: &'a CStr,
-        val: &'a CStr
-    ) -> &mut Self
-    {
-        self.iov.push(IoVec::from_slice(name.to_bytes_with_nul()));
-        self.is_owned.push(false);
-        self.iov.push(IoVec::from_slice(val.to_bytes_with_nul()));
-        self.is_owned.push(false);
+    pub fn str_opt(&mut self, name: &'a CStr, val: &'a CStr) -> &mut Self {
+        self.push_slice(name.to_bytes_with_nul(), false);
+        self.push_slice(val.to_bytes_with_nul(), false);
         self
     }
 
@@ -323,27 +360,12 @@
     ///     .str_opt_owned("fspath", mountpoint.to_str().unwrap());
     /// ```
     pub fn str_opt_owned<P1, P2>(&mut self, name: &P1, val: &P2) -> &mut Self
-        where P1: ?Sized + NixPath,
-              P2: ?Sized + NixPath
+    where
+        P1: ?Sized + NixPath,
+        P2: ?Sized + NixPath,
     {
-        name.with_nix_path(|s| {
-            let len = s.to_bytes_with_nul().len();
-            self.iov.push(IoVec::from_raw_parts(
-                // Must free it later
-                s.to_owned().into_raw() as *mut c_void,
-                len
-            ));
-            self.is_owned.push(true);
-        }).unwrap();
-        val.with_nix_path(|s| {
-            let len = s.to_bytes_with_nul().len();
-            self.iov.push(IoVec::from_raw_parts(
-                // Must free it later
-                s.to_owned().into_raw() as *mut c_void,
-                len
-            ));
-            self.is_owned.push(true);
-        }).unwrap();
+        self.push_nix_path(name);
+        self.push_nix_path(val);
         self
     }
 
@@ -354,24 +376,23 @@
 
     /// Actually mount the file system.
     pub fn nmount(&mut self, flags: MntFlags) -> NmountResult {
-        // nmount can return extra error information via a "errmsg" return
-        // argument.
         const ERRMSG_NAME: &[u8] = b"errmsg\0";
         let mut errmsg = vec![0u8; 255];
-        self.iov.push(IoVec::from_raw_parts(
-                ERRMSG_NAME.as_ptr() as *mut c_void,
-                ERRMSG_NAME.len()
-        ));
-        self.iov.push(IoVec::from_raw_parts(
-                errmsg.as_mut_ptr() as *mut c_void,
-                errmsg.len()
-        ));
+
+        // nmount can return extra error information via a "errmsg" return
+        // argument.
+        self.push_slice(ERRMSG_NAME, false);
+
+        // SAFETY: we are pushing a mutable iovec here, so we can't use
+        //         the above method
+        self.iov.push(libc::iovec {
+            iov_base: errmsg.as_mut_ptr() as *mut c_void,
+            iov_len: errmsg.len(),
+        });
 
         let niov = self.iov.len() as c_uint;
         let iovp = self.iov.as_mut_ptr() as *mut libc::iovec;
-        let res = unsafe {
-            libc::nmount(iovp, niov, flags.bits)
-        };
+        let res = unsafe { libc::nmount(iovp, niov, flags.bits) };
         match Errno::result(res) {
             Ok(_) => Ok(()),
             Err(error) => {
@@ -397,7 +418,7 @@
                 // Free the owned string.  Safe because we recorded ownership,
                 // and Nmount does not implement Clone.
                 unsafe {
-                    drop(CString::from_raw(iov.0.iov_base as *mut c_char));
+                    drop(CString::from_raw(iov.iov_base as *mut c_char));
                 }
             }
         }
@@ -408,18 +429,24 @@
 ///
 /// Useful flags include
 /// * `MNT_FORCE` -     Unmount even if still in use.
-/// * `MNT_BYFSID` -    `mountpoint` is not a path, but a file system ID
-///                     encoded as `FSID:val0:val1`, where `val0` and `val1`
-///                     are the contents of the `fsid_t val[]` array in decimal.
-///                     The file system that has the specified file system ID
-///                     will be unmounted.  See
-///                     [`statfs`](crate::sys::statfs::statfs) to determine the
-///                     `fsid`.
+#[cfg_attr(
+    target_os = "freebsd",
+    doc = "
+* `MNT_BYFSID` -    `mountpoint` is not a path, but a file system ID
+                    encoded as `FSID:val0:val1`, where `val0` and `val1`
+                    are the contents of the `fsid_t val[]` array in decimal.
+                    The file system that has the specified file system ID
+                    will be unmounted.  See
+                    [`statfs`](crate::sys::statfs::statfs) to determine the
+                    `fsid`.
+"
+)]
 pub fn unmount<P>(mountpoint: &P, flags: MntFlags) -> Result<()>
-    where P: ?Sized + NixPath
+where
+    P: ?Sized + NixPath,
 {
-    let res = mountpoint.with_nix_path(|cstr| {
-        unsafe { libc::unmount(cstr.as_ptr(), flags.bits) }
+    let res = mountpoint.with_nix_path(|cstr| unsafe {
+        libc::unmount(cstr.as_ptr(), flags.bits)
     })?;
 
     Errno::result(res).map(drop)
diff --git a/src/mount/linux.rs b/src/mount/linux.rs
index 4cb2fa5..cf6a60b 100644
--- a/src/mount/linux.rs
+++ b/src/mount/linux.rs
@@ -1,7 +1,7 @@
 #![allow(missing_docs)]
-use libc::{self, c_ulong, c_int};
-use crate::{Result, NixPath};
 use crate::errno::Errno;
+use crate::{NixPath, Result};
+use libc::{self, c_int, c_ulong};
 
 libc_bitflags!(
     pub struct MsFlags: c_ulong {
@@ -53,39 +53,44 @@
         MNT_FORCE;
         MNT_DETACH;
         MNT_EXPIRE;
+        UMOUNT_NOFOLLOW;
     }
 );
 
-pub fn mount<P1: ?Sized + NixPath, P2: ?Sized + NixPath, P3: ?Sized + NixPath, P4: ?Sized + NixPath>(
-        source: Option<&P1>,
-        target: &P2,
-        fstype: Option<&P3>,
-        flags: MsFlags,
-        data: Option<&P4>) -> Result<()> {
-
+pub fn mount<
+    P1: ?Sized + NixPath,
+    P2: ?Sized + NixPath,
+    P3: ?Sized + NixPath,
+    P4: ?Sized + NixPath,
+>(
+    source: Option<&P1>,
+    target: &P2,
+    fstype: Option<&P3>,
+    flags: MsFlags,
+    data: Option<&P4>,
+) -> Result<()> {
     fn with_opt_nix_path<P, T, F>(p: Option<&P>, f: F) -> Result<T>
-        where P: ?Sized + NixPath,
-              F: FnOnce(*const libc::c_char) -> T
+    where
+        P: ?Sized + NixPath,
+        F: FnOnce(*const libc::c_char) -> T,
     {
         match p {
             Some(path) => path.with_nix_path(|p_str| f(p_str.as_ptr())),
-            None => Ok(f(std::ptr::null()))
+            None => Ok(f(std::ptr::null())),
         }
     }
 
     let res = with_opt_nix_path(source, |s| {
         target.with_nix_path(|t| {
             with_opt_nix_path(fstype, |ty| {
-                with_opt_nix_path(data, |d| {
-                    unsafe {
-                        libc::mount(
-                            s,
-                            t.as_ptr(),
-                            ty,
-                            flags.bits,
-                            d as *const libc::c_void
-                        )
-                    }
+                with_opt_nix_path(data, |d| unsafe {
+                    libc::mount(
+                        s,
+                        t.as_ptr(),
+                        ty,
+                        flags.bits,
+                        d as *const libc::c_void,
+                    )
                 })
             })
         })
@@ -95,16 +100,15 @@
 }
 
 pub fn umount<P: ?Sized + NixPath>(target: &P) -> Result<()> {
-    let res = target.with_nix_path(|cstr| {
-        unsafe { libc::umount(cstr.as_ptr()) }
-    })?;
+    let res =
+        target.with_nix_path(|cstr| unsafe { libc::umount(cstr.as_ptr()) })?;
 
     Errno::result(res).map(drop)
 }
 
 pub fn umount2<P: ?Sized + NixPath>(target: &P, flags: MntFlags) -> Result<()> {
-    let res = target.with_nix_path(|cstr| {
-        unsafe { libc::umount2(cstr.as_ptr(), flags.bits) }
+    let res = target.with_nix_path(|cstr| unsafe {
+        libc::umount2(cstr.as_ptr(), flags.bits)
     })?;
 
     Errno::result(res).map(drop)
diff --git a/src/mount/mod.rs b/src/mount/mod.rs
index 14bf2a9..e98b49c 100644
--- a/src/mount/mod.rs
+++ b/src/mount/mod.rs
@@ -1,21 +1,26 @@
 //! Mount file systems
 #[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
 mod linux;
 
 #[cfg(any(target_os = "android", target_os = "linux"))]
 pub use self::linux::*;
 
-#[cfg(any(target_os = "dragonfly",
-          target_os = "freebsd",
-          target_os = "macos",
-          target_os = "netbsd",
-          target_os = "openbsd"))]
+#[cfg(any(
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "macos",
+    target_os = "netbsd",
+    target_os = "openbsd"
+))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
 mod bsd;
 
-#[cfg(any(target_os = "dragonfly",
-          target_os = "freebsd",
-          target_os = "macos",
-          target_os = "netbsd",
-          target_os = "openbsd"
-          ))]
+#[cfg(any(
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "macos",
+    target_os = "netbsd",
+    target_os = "openbsd"
+))]
 pub use self::bsd::*;
diff --git a/src/mqueue.rs b/src/mqueue.rs
index 34fd802..33599bf 100644
--- a/src/mqueue.rs
+++ b/src/mqueue.rs
@@ -1,53 +1,107 @@
 //! Posix Message Queue functions
 //!
+//! # Example
+//!
+// no_run because a kernel module may be required.
+//! ```no_run
+//! # use std::ffi::CString;
+//! # use nix::mqueue::*;
+//! use nix::sys::stat::Mode;
+//!
+//! const MSG_SIZE: mq_attr_member_t = 32;
+//! let mq_name= CString::new("/a_nix_test_queue").unwrap();
+//!
+//! let oflag0 = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
+//! let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
+//! let mqd0 = mq_open(&mq_name, oflag0, mode, None).unwrap();
+//! let msg_to_send = b"msg_1";
+//! mq_send(&mqd0, msg_to_send, 1).unwrap();
+//!
+//! let oflag1 = MQ_OFlag::O_CREAT | MQ_OFlag::O_RDONLY;
+//! let mqd1 = mq_open(&mq_name, oflag1, mode, None).unwrap();
+//! let mut buf = [0u8; 32];
+//! let mut prio = 0u32;
+//! let len = mq_receive(&mqd1, &mut buf, &mut prio).unwrap();
+//! assert_eq!(prio, 1);
+//! assert_eq!(msg_to_send, &buf[0..len]);
+//!
+//! mq_close(mqd1).unwrap();
+//! mq_close(mqd0).unwrap();
+//! ```
 //! [Further reading and details on the C API](https://man7.org/linux/man-pages/man7/mq_overview.7.html)
 
-use crate::Result;
 use crate::errno::Errno;
+use crate::Result;
 
-use libc::{self, c_char, mqd_t, size_t};
-use std::ffi::CString;
 use crate::sys::stat::Mode;
+use libc::{self, c_char, mqd_t, size_t};
+use std::ffi::CStr;
 use std::mem;
 
-libc_bitflags!{
+libc_bitflags! {
+    /// Used with [`mq_open`].
     pub struct MQ_OFlag: libc::c_int {
+        /// Open the message queue for receiving messages.
         O_RDONLY;
+        /// Open the queue for sending messages.
         O_WRONLY;
+        /// Open the queue for both receiving and sending messages
         O_RDWR;
+        /// Create a message queue.
         O_CREAT;
+        /// If set along with `O_CREAT`, `mq_open` will fail if the message
+        /// queue name exists.
         O_EXCL;
+        /// `mq_send` and `mq_receive` should fail with `EAGAIN` rather than
+        /// wait for resources that are not currently available.
         O_NONBLOCK;
+        /// Set the close-on-exec flag for the message queue descriptor.
         O_CLOEXEC;
     }
 }
 
-libc_bitflags!{
-    pub struct FdFlag: libc::c_int {
-        FD_CLOEXEC;
-    }
-}
-
+/// A message-queue attribute, optionally used with [`mq_setattr`] and
+/// [`mq_getattr`] and optionally [`mq_open`],
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
 pub struct MqAttr {
     mq_attr: libc::mq_attr,
 }
 
+/// Identifies an open POSIX Message Queue
+// A safer wrapper around libc::mqd_t, which is a pointer on some platforms
+// Deliberately is not Clone to prevent use-after-close scenarios
+#[repr(transparent)]
+#[derive(Debug)]
+#[allow(missing_copy_implementations)]
+pub struct MqdT(mqd_t);
+
 // x32 compatibility
 // See https://sourceware.org/bugzilla/show_bug.cgi?id=21279
+/// Size of a message queue attribute member
 #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
 pub type mq_attr_member_t = i64;
+/// Size of a message queue attribute member
 #[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
 pub type mq_attr_member_t = libc::c_long;
 
 impl MqAttr {
-    pub fn new(mq_flags: mq_attr_member_t,
-               mq_maxmsg: mq_attr_member_t,
-               mq_msgsize: mq_attr_member_t,
-               mq_curmsgs: mq_attr_member_t)
-               -> MqAttr
-    {
+    /// Create a new message queue attribute
+    ///
+    /// # Arguments
+    ///
+    /// - `mq_flags`:   Either `0` or `O_NONBLOCK`.
+    /// - `mq_maxmsg`:  Maximum number of messages on the queue.
+    /// - `mq_msgsize`: Maximum message size in bytes.
+    /// - `mq_curmsgs`: Number of messages currently in the queue.
+    pub fn new(
+        mq_flags: mq_attr_member_t,
+        mq_maxmsg: mq_attr_member_t,
+        mq_msgsize: mq_attr_member_t,
+        mq_curmsgs: mq_attr_member_t,
+    ) -> MqAttr {
         let mut attr = mem::MaybeUninit::<libc::mq_attr>::uninit();
         unsafe {
             let p = attr.as_mut_ptr();
@@ -55,42 +109,62 @@
             (*p).mq_maxmsg = mq_maxmsg;
             (*p).mq_msgsize = mq_msgsize;
             (*p).mq_curmsgs = mq_curmsgs;
-            MqAttr { mq_attr: attr.assume_init() }
+            MqAttr {
+                mq_attr: attr.assume_init(),
+            }
         }
     }
 
+    /// The current flags, either `0` or `O_NONBLOCK`.
     pub const fn flags(&self) -> mq_attr_member_t {
         self.mq_attr.mq_flags
     }
-}
 
+    /// The max number of messages that can be held by the queue
+    pub const fn maxmsg(&self) -> mq_attr_member_t {
+        self.mq_attr.mq_maxmsg
+    }
+
+    /// The maximum size of each message (in bytes)
+    pub const fn msgsize(&self) -> mq_attr_member_t {
+        self.mq_attr.mq_msgsize
+    }
+
+    /// The number of messages currently held in the queue
+    pub const fn curmsgs(&self) -> mq_attr_member_t {
+        self.mq_attr.mq_curmsgs
+    }
+}
 
 /// Open a message queue
 ///
 /// See also [`mq_open(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_open.html)
 // The mode.bits cast is only lossless on some OSes
 #[allow(clippy::cast_lossless)]
-pub fn mq_open(name: &CString,
-               oflag: MQ_OFlag,
-               mode: Mode,
-               attr: Option<&MqAttr>)
-               -> Result<mqd_t> {
+pub fn mq_open(
+    name: &CStr,
+    oflag: MQ_OFlag,
+    mode: Mode,
+    attr: Option<&MqAttr>,
+) -> Result<MqdT> {
     let res = match attr {
         Some(mq_attr) => unsafe {
-            libc::mq_open(name.as_ptr(),
-                          oflag.bits(),
-                          mode.bits() as libc::c_int,
-                          &mq_attr.mq_attr as *const libc::mq_attr)
+            libc::mq_open(
+                name.as_ptr(),
+                oflag.bits(),
+                mode.bits() as libc::c_int,
+                &mq_attr.mq_attr as *const libc::mq_attr,
+            )
         },
         None => unsafe { libc::mq_open(name.as_ptr(), oflag.bits()) },
     };
-    Errno::result(res)
+    Errno::result(res).map(MqdT)
 }
 
 /// Remove a message queue
 ///
 /// See also [`mq_unlink(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_unlink.html)
-pub fn mq_unlink(name: &CString) -> Result<()> {
+pub fn mq_unlink(name: &CStr) -> Result<()> {
     let res = unsafe { libc::mq_unlink(name.as_ptr()) };
     Errno::result(res).map(drop)
 }
@@ -98,21 +172,27 @@
 /// Close a message queue
 ///
 /// See also [`mq_close(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_close.html)
-pub fn mq_close(mqdes: mqd_t) -> Result<()> {
-    let res = unsafe { libc::mq_close(mqdes) };
+pub fn mq_close(mqdes: MqdT) -> Result<()> {
+    let res = unsafe { libc::mq_close(mqdes.0) };
     Errno::result(res).map(drop)
 }
 
 /// Receive a message from a message queue
 ///
 /// See also [`mq_receive(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_receive.html)
-pub fn mq_receive(mqdes: mqd_t, message: &mut [u8], msg_prio: &mut u32) -> Result<usize> {
+pub fn mq_receive(
+    mqdes: &MqdT,
+    message: &mut [u8],
+    msg_prio: &mut u32,
+) -> Result<usize> {
     let len = message.len() as size_t;
     let res = unsafe {
-        libc::mq_receive(mqdes,
-                         message.as_mut_ptr() as *mut c_char,
-                         len,
-                         msg_prio as *mut u32)
+        libc::mq_receive(
+            mqdes.0,
+            message.as_mut_ptr() as *mut c_char,
+            len,
+            msg_prio as *mut u32,
+        )
     };
     Errno::result(res).map(|r| r as usize)
 }
@@ -120,12 +200,14 @@
 /// Send a message to a message queue
 ///
 /// See also [`mq_send(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html)
-pub fn mq_send(mqdes: mqd_t, message: &[u8], msq_prio: u32) -> Result<()> {
+pub fn mq_send(mqdes: &MqdT, message: &[u8], msq_prio: u32) -> Result<()> {
     let res = unsafe {
-        libc::mq_send(mqdes,
-                      message.as_ptr() as *const c_char,
-                      message.len(),
-                      msq_prio)
+        libc::mq_send(
+            mqdes.0,
+            message.as_ptr() as *const c_char,
+            message.len(),
+            msq_prio,
+        )
     };
     Errno::result(res).map(drop)
 }
@@ -133,10 +215,14 @@
 /// Get message queue attributes
 ///
 /// See also [`mq_getattr(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_getattr.html)
-pub fn mq_getattr(mqd: mqd_t) -> Result<MqAttr> {
+pub fn mq_getattr(mqd: &MqdT) -> Result<MqAttr> {
     let mut attr = mem::MaybeUninit::<libc::mq_attr>::uninit();
-    let res = unsafe { libc::mq_getattr(mqd, attr.as_mut_ptr()) };
-    Errno::result(res).map(|_| unsafe{MqAttr { mq_attr: attr.assume_init() }})
+    let res = unsafe { libc::mq_getattr(mqd.0, attr.as_mut_ptr()) };
+    Errno::result(res).map(|_| unsafe {
+        MqAttr {
+            mq_attr: attr.assume_init(),
+        }
+    })
 }
 
 /// Set the attributes of the message queue. Only `O_NONBLOCK` can be set, everything else will be ignored
@@ -144,35 +230,47 @@
 /// It is recommend to use the `mq_set_nonblock()` and `mq_remove_nonblock()` convenience functions as they are easier to use
 ///
 /// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_setattr.html)
-pub fn mq_setattr(mqd: mqd_t, newattr: &MqAttr) -> Result<MqAttr> {
+pub fn mq_setattr(mqd: &MqdT, newattr: &MqAttr) -> Result<MqAttr> {
     let mut attr = mem::MaybeUninit::<libc::mq_attr>::uninit();
     let res = unsafe {
-        libc::mq_setattr(mqd, &newattr.mq_attr as *const libc::mq_attr, attr.as_mut_ptr())
+        libc::mq_setattr(
+            mqd.0,
+            &newattr.mq_attr as *const libc::mq_attr,
+            attr.as_mut_ptr(),
+        )
     };
-    Errno::result(res).map(|_| unsafe{ MqAttr { mq_attr: attr.assume_init() }})
+    Errno::result(res).map(|_| unsafe {
+        MqAttr {
+            mq_attr: attr.assume_init(),
+        }
+    })
 }
 
 /// Convenience function.
 /// Sets the `O_NONBLOCK` attribute for a given message queue descriptor
 /// Returns the old attributes
-#[allow(clippy::useless_conversion)]    // Not useless on all OSes
-pub fn mq_set_nonblock(mqd: mqd_t) -> Result<MqAttr> {
+#[allow(clippy::useless_conversion)] // Not useless on all OSes
+pub fn mq_set_nonblock(mqd: &MqdT) -> Result<MqAttr> {
     let oldattr = mq_getattr(mqd)?;
-    let newattr = MqAttr::new(mq_attr_member_t::from(MQ_OFlag::O_NONBLOCK.bits()),
-                              oldattr.mq_attr.mq_maxmsg,
-                              oldattr.mq_attr.mq_msgsize,
-                              oldattr.mq_attr.mq_curmsgs);
+    let newattr = MqAttr::new(
+        mq_attr_member_t::from(MQ_OFlag::O_NONBLOCK.bits()),
+        oldattr.mq_attr.mq_maxmsg,
+        oldattr.mq_attr.mq_msgsize,
+        oldattr.mq_attr.mq_curmsgs,
+    );
     mq_setattr(mqd, &newattr)
 }
 
 /// Convenience function.
 /// Removes `O_NONBLOCK` attribute for a given message queue descriptor
 /// Returns the old attributes
-pub fn mq_remove_nonblock(mqd: mqd_t) -> Result<MqAttr> {
+pub fn mq_remove_nonblock(mqd: &MqdT) -> Result<MqAttr> {
     let oldattr = mq_getattr(mqd)?;
-    let newattr = MqAttr::new(0,
-                              oldattr.mq_attr.mq_maxmsg,
-                              oldattr.mq_attr.mq_msgsize,
-                              oldattr.mq_attr.mq_curmsgs);
+    let newattr = MqAttr::new(
+        0,
+        oldattr.mq_attr.mq_maxmsg,
+        oldattr.mq_attr.mq_msgsize,
+        oldattr.mq_attr.mq_curmsgs,
+    );
     mq_setattr(mqd, &newattr)
 }
diff --git a/src/net/if_.rs b/src/net/if_.rs
index bc00a43..b2423bc 100644
--- a/src/net/if_.rs
+++ b/src/net/if_.rs
@@ -8,7 +8,8 @@
 
 /// Resolve an interface into a interface number.
 pub fn if_nametoindex<P: ?Sized + NixPath>(name: &P) -> Result<c_uint> {
-    let if_index = name.with_nix_path(|name| unsafe { libc::if_nametoindex(name.as_ptr()) })?;
+    let if_index = name
+        .with_nix_path(|name| unsafe { libc::if_nametoindex(name.as_ptr()) })?;
 
     if if_index == 0 {
         Err(Error::last())
@@ -28,6 +29,7 @@
         IFF_BROADCAST;
         /// Internal debugging flag. (see
         /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
+        #[cfg(not(target_os = "haiku"))]
         IFF_DEBUG;
         /// Interface is a loopback interface. (see
         /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
@@ -45,9 +47,11 @@
                   target_os = "netbsd",
                   target_os = "illumos",
                   target_os = "solaris"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_NOTRAILERS;
         /// Interface manages own routes.
         #[cfg(any(target_os = "dragonfly"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_SMART;
         /// Resources allocated. (see
         /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
@@ -62,6 +66,7 @@
                   target_os = "netbsd",
                   target_os = "openbsd",
                   target_os = "solaris"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_RUNNING;
         /// No arp protocol, L2 destination address not set. (see
         /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
@@ -75,6 +80,7 @@
         /// Master of a load balancing bundle. (see
         /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
         #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_MASTER;
         /// transmission in progress, tx hardware queue is full
         #[cfg(any(target_os = "freebsd",
@@ -82,21 +88,24 @@
                   target_os = "netbsd",
                   target_os = "openbsd",
                   target_os = "ios"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_OACTIVE;
         /// Protocol code on board.
         #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_INTELLIGENT;
         /// Slave of a load balancing bundle. (see
         /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
         #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_SLAVE;
         /// Can't hear own transmissions.
         #[cfg(any(target_os = "dragonfly",
                   target_os = "freebsd",
                   target_os = "macos",
                   target_os = "netbsd",
-                  target_os = "openbsd",
-                  target_os = "osx"))]
+                  target_os = "openbsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_SIMPLEX;
         /// Supports multicast. (see
         /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
@@ -108,13 +117,16 @@
                   target_os = "netbsd",
                   target_os = "openbsd",
                   target_os = "ios"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_LINK0;
         /// Multicast using broadcast.
         #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_MULTI_BCAST;
         /// Is able to select media type via ifmap. (see
         /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
         #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_PORTSEL;
         /// Per link layer defined bit.
         #[cfg(any(target_os = "dragonfly",
@@ -123,13 +135,16 @@
                   target_os = "netbsd",
                   target_os = "openbsd",
                   target_os = "ios"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_LINK1;
         /// Non-unique address.
         #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_UNNUMBERED;
         /// Auto media selection active. (see
         /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
         #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_AUTOMEDIA;
         /// Per link layer defined bit.
         #[cfg(any(target_os = "dragonfly",
@@ -138,132 +153,174 @@
                   target_os = "netbsd",
                   target_os = "openbsd",
                   target_os = "ios"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_LINK2;
         /// Use alternate physical connection.
         #[cfg(any(target_os = "dragonfly",
                   target_os = "freebsd",
                   target_os = "macos",
                   target_os = "ios"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_ALTPHYS;
         /// DHCP controls interface.
         #[cfg(any(target_os = "solaris", target_os = "illumos"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_DHCPRUNNING;
         /// The addresses are lost when the interface goes down. (see
         /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
         #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_DYNAMIC;
         /// Do not advertise.
         #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_PRIVATE;
         /// Driver signals L1 up. Volatile.
         #[cfg(any(target_os = "fuchsia", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_LOWER_UP;
         /// Interface is in polling mode.
         #[cfg(any(target_os = "dragonfly"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_POLLING_COMPAT;
         /// Unconfigurable using ioctl(2).
         #[cfg(any(target_os = "freebsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_CANTCONFIG;
         /// Do not transmit packets.
         #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_NOXMIT;
         /// Driver signals dormant. Volatile.
         #[cfg(any(target_os = "fuchsia", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_DORMANT;
         /// User-requested promisc mode.
         #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_PPROMISC;
         /// Just on-link subnet.
         #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_NOLOCAL;
         /// Echo sent packets. Volatile.
         #[cfg(any(target_os = "fuchsia", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_ECHO;
         /// User-requested monitor mode.
         #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_MONITOR;
         /// Address is deprecated.
         #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_DEPRECATED;
         /// Static ARP.
         #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_STATICARP;
         /// Address from stateless addrconf.
         #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_ADDRCONF;
         /// Interface is in polling mode.
         #[cfg(any(target_os = "dragonfly"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_NPOLLING;
         /// Router on interface.
         #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_ROUTER;
         /// Interface is in polling mode.
         #[cfg(any(target_os = "dragonfly"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_IDIRECT;
         /// Interface is winding down
         #[cfg(any(target_os = "freebsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_DYING;
         /// No NUD on interface.
         #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_NONUD;
         /// Interface is being renamed
         #[cfg(any(target_os = "freebsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_RENAMING;
         /// Anycast address.
         #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_ANYCAST;
         /// Don't exchange routing info.
         #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_NORTEXCH;
         /// Do not provide packet information
         #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_NO_PI as libc::c_int;
         /// TUN device (no Ethernet headers)
         #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_TUN as libc::c_int;
         /// TAP device
         #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_TAP as libc::c_int;
         /// IPv4 interface.
         #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_IPV4;
         /// IPv6 interface.
         #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_IPV6;
         /// in.mpathd test address
         #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_NOFAILOVER;
         /// Interface has failed
         #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_FAILED;
         /// Interface is a hot-spare
         #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_STANDBY;
         /// Functioning but not used
         #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_INACTIVE;
         /// Interface is offline
         #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_OFFLINE;
         #[cfg(target_os = "solaris")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_COS_ENABLED;
         /// Prefer as source addr.
         #[cfg(target_os = "solaris")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_PREFERRED;
         /// RFC3041
         #[cfg(target_os = "solaris")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_TEMPORARY;
         /// MTU set with SIOCSLIFMTU
         #[cfg(target_os = "solaris")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_FIXEDMTU;
         /// Cannot send / receive packets
         #[cfg(target_os = "solaris")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_VIRTUAL;
         /// Local address in use
         #[cfg(target_os = "solaris")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_DUPLICATE;
         /// IPMP IP interface
         #[cfg(target_os = "solaris")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IFF_IPMP;
     }
 );
@@ -278,6 +335,7 @@
     target_os = "netbsd",
     target_os = "openbsd",
 ))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
 mod if_nameindex {
     use super::*;
 
diff --git a/src/poll.rs b/src/poll.rs
index 8556c1b..6f227fe 100644
--- a/src/poll.rs
+++ b/src/poll.rs
@@ -1,12 +1,8 @@
 //! Wait for events to trigger on specific file descriptors
-#[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "linux"))]
-use crate::sys::time::TimeSpec;
-#[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "linux"))]
-use crate::sys::signal::SigSet;
 use std::os::unix::io::{AsRawFd, RawFd};
 
-use crate::Result;
 use crate::errno::Errno;
+use crate::Result;
 
 /// This is a wrapper around `libc::pollfd`.
 ///
@@ -14,7 +10,7 @@
 /// [`ppoll`](fn.ppoll.html) functions to specify the events of interest
 /// for a specific file descriptor.
 ///
-/// After a call to `poll` or `ppoll`, the events that occured can be
+/// After a call to `poll` or `ppoll`, the events that occurred can be
 /// retrieved by calling [`revents()`](#method.revents) on the `PollFd`.
 #[repr(transparent)]
 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
@@ -35,12 +31,32 @@
         }
     }
 
-    /// Returns the events that occured in the last call to `poll` or `ppoll`.  Will only return
+    /// Returns the events that occurred in the last call to `poll` or `ppoll`.  Will only return
     /// `None` if the kernel provides status flags that Nix does not know about.
     pub fn revents(self) -> Option<PollFlags> {
         PollFlags::from_bits(self.pollfd.revents)
     }
 
+    /// Returns if any of the events of interest occured in the last call to `poll` or `ppoll`. Will
+    /// only return `None` if the kernel provides status flags that Nix does not know about.
+    ///
+    /// Equivalent to `x.revents()? != PollFlags::empty()`.
+    ///
+    /// This is marginally more efficient than [`PollFd::all`].
+    pub fn any(self) -> Option<bool> {
+        Some(self.revents()? != PollFlags::empty())
+    }
+
+    /// Returns if all the events of interest occured in the last call to `poll` or `ppoll`. Will
+    /// only return `None` if the kernel provides status flags that Nix does not know about.
+    ///
+    /// Equivalent to `x.revents()? & x.events() == x.events()`.
+    ///
+    /// This is marginally less efficient than [`PollFd::any`].
+    pub fn all(self) -> Option<bool> {
+        Some(self.revents()? & self.events() == self.events())
+    }
+
     /// The events of interest for this `PollFd`.
     pub fn events(self) -> PollFlags {
         PollFlags::from_bits(self.pollfd.events).unwrap()
@@ -81,15 +97,19 @@
         POLLOUT;
         /// Equivalent to [`POLLIN`](constant.POLLIN.html)
         #[cfg(not(target_os = "redox"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         POLLRDNORM;
         #[cfg(not(target_os = "redox"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         /// Equivalent to [`POLLOUT`](constant.POLLOUT.html)
         POLLWRNORM;
         /// Priority band data can be read (generally unused on Linux).
         #[cfg(not(target_os = "redox"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         POLLRDBAND;
         /// Priority data may be written.
         #[cfg(not(target_os = "redox"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         POLLWRBAND;
         /// Error condition (only returned in
         /// [`PollFd::revents`](struct.PollFd.html#method.revents);
@@ -134,14 +154,18 @@
 /// ready.
 pub fn poll(fds: &mut [PollFd], timeout: libc::c_int) -> Result<libc::c_int> {
     let res = unsafe {
-        libc::poll(fds.as_mut_ptr() as *mut libc::pollfd,
-                   fds.len() as libc::nfds_t,
-                   timeout)
+        libc::poll(
+            fds.as_mut_ptr() as *mut libc::pollfd,
+            fds.len() as libc::nfds_t,
+            timeout,
+        )
     };
 
     Errno::result(res)
 }
 
+feature! {
+#![feature = "signal"]
 /// `ppoll()` allows an application to safely wait until either a file
 /// descriptor becomes ready or until a signal is caught.
 /// ([`poll(2)`](https://man7.org/linux/man-pages/man2/poll.2.html))
@@ -149,15 +173,25 @@
 /// `ppoll` behaves like `poll`, but let you specify what signals may interrupt it
 /// with the `sigmask` argument. If you want `ppoll` to block indefinitely,
 /// specify `None` as `timeout` (it is like `timeout = -1` for `poll`).
+/// If `sigmask` is `None`, then no signal mask manipulation is performed,
+/// so in that case `ppoll` differs from `poll` only in the precision of the
+/// timeout argument.
 ///
 #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "linux"))]
-pub fn ppoll(fds: &mut [PollFd], timeout: Option<TimeSpec>, sigmask: SigSet) -> Result<libc::c_int> {
+pub fn ppoll(
+    fds: &mut [PollFd],
+    timeout: Option<crate::sys::time::TimeSpec>,
+    sigmask: Option<crate::sys::signal::SigSet>
+    ) -> Result<libc::c_int>
+{
     let timeout = timeout.as_ref().map_or(core::ptr::null(), |r| r.as_ref());
+    let sigmask = sigmask.as_ref().map_or(core::ptr::null(), |r| r.as_ref());
     let res = unsafe {
         libc::ppoll(fds.as_mut_ptr() as *mut libc::pollfd,
                     fds.len() as libc::nfds_t,
                     timeout,
-                    sigmask.as_ref())
+                    sigmask)
     };
     Errno::result(res)
 }
+}
diff --git a/src/pty.rs b/src/pty.rs
index facc9aa..28ae5e9 100644
--- a/src/pty.rs
+++ b/src/pty.rs
@@ -8,10 +8,11 @@
 use std::mem;
 use std::os::unix::prelude::*;
 
-use crate::sys::termios::Termios;
-use crate::unistd::{self, ForkResult, Pid};
-use crate::{Result, fcntl};
 use crate::errno::Errno;
+use crate::sys::termios::Termios;
+#[cfg(feature = "process")]
+use crate::unistd::{ForkResult, Pid};
+use crate::{fcntl, unistd, Result};
 
 /// Representation of a master/slave pty pair
 ///
@@ -25,6 +26,8 @@
     pub slave: RawFd,
 }
 
+feature! {
+#![feature = "process"]
 /// Representation of a master with a forked pty
 ///
 /// This is returned by `forkpty`. Note that this type does *not* implement `Drop`, so the user
@@ -36,7 +39,7 @@
     /// Metadata about forked process
     pub fork_result: ForkResult,
 }
-
+}
 
 /// Representation of the Master device in a master/slave pty pair
 ///
@@ -91,6 +94,21 @@
     }
 }
 
+impl io::Read for &PtyMaster {
+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+        unistd::read(self.0, buf).map_err(io::Error::from)
+    }
+}
+
+impl io::Write for &PtyMaster {
+    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+        unistd::write(self.0, buf).map_err(io::Error::from)
+    }
+    fn flush(&mut self) -> io::Result<()> {
+        Ok(())
+    }
+}
+
 /// Grant access to a slave pseudoterminal (see
 /// [`grantpt(3)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/grantpt.html))
 ///
@@ -108,7 +126,7 @@
 /// Open a pseudoterminal device (see
 /// [`posix_openpt(3)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_openpt.html))
 ///
-/// `posix_openpt()` returns a file descriptor to an existing unused pseuterminal master device.
+/// `posix_openpt()` returns a file descriptor to an existing unused pseudoterminal master device.
 ///
 /// # Examples
 ///
@@ -140,9 +158,7 @@
 /// ```
 #[inline]
 pub fn posix_openpt(flags: fcntl::OFlag) -> Result<PtyMaster> {
-    let fd = unsafe {
-        libc::posix_openpt(flags.bits())
-    };
+    let fd = unsafe { libc::posix_openpt(flags.bits()) };
 
     if fd < 0 {
         return Err(Errno::last());
@@ -188,6 +204,7 @@
 /// This value is useful for opening the slave ptty once the master has already been opened with
 /// `posix_openpt()`.
 #[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
 #[inline]
 pub fn ptsname_r(fd: &PtyMaster) -> Result<String> {
     let mut name_buf = Vec::<libc::c_char>::with_capacity(64);
@@ -209,7 +226,7 @@
 ///
 /// `unlockpt()` unlocks the slave pseudoterminal device corresponding to the master pseudoterminal
 /// referred to by `fd`. This must be called before trying to open the slave side of a
-/// pseuoterminal.
+/// pseudoterminal.
 #[inline]
 pub fn unlockpt(fd: &PtyMaster) -> Result<()> {
     if unsafe { libc::unlockpt(fd.as_raw_fd()) } < 0 {
@@ -219,7 +236,6 @@
     Ok(())
 }
 
-
 /// Create a new pseudoterminal, returning the slave and master file descriptors
 /// in `OpenptyResult`
 /// (see [`openpty`](https://man7.org/linux/man-pages/man3/openpty.3.html)).
@@ -228,7 +244,15 @@
 /// the values in `winsize`. If `termios` is not `None`, the pseudoterminal's
 /// terminal settings of the slave will be set to the values in `termios`.
 #[inline]
-pub fn openpty<'a, 'b, T: Into<Option<&'a Winsize>>, U: Into<Option<&'b Termios>>>(winsize: T, termios: U) -> Result<OpenptyResult> {
+pub fn openpty<
+    'a,
+    'b,
+    T: Into<Option<&'a Winsize>>,
+    U: Into<Option<&'b Termios>>,
+>(
+    winsize: T,
+    termios: U,
+) -> Result<OpenptyResult> {
     use std::ptr;
 
     let mut slave = mem::MaybeUninit::<libc::c_int>::uninit();
@@ -247,17 +271,15 @@
                     )
                 }
             }
-            (None, Some(winsize)) => {
-                unsafe {
-                    libc::openpty(
-                        master.as_mut_ptr(),
-                        slave.as_mut_ptr(),
-                        ptr::null_mut(),
-                        ptr::null_mut(),
-                        winsize as *const Winsize as *mut _,
-                    )
-                }
-            }
+            (None, Some(winsize)) => unsafe {
+                libc::openpty(
+                    master.as_mut_ptr(),
+                    slave.as_mut_ptr(),
+                    ptr::null_mut(),
+                    ptr::null_mut(),
+                    winsize as *const Winsize as *mut _,
+                )
+            },
             (Some(termios), None) => {
                 let inner_termios = termios.get_libc_termios();
                 unsafe {
@@ -270,17 +292,15 @@
                     )
                 }
             }
-            (None, None) => {
-                unsafe {
-                    libc::openpty(
-                        master.as_mut_ptr(),
-                        slave.as_mut_ptr(),
-                        ptr::null_mut(),
-                        ptr::null_mut(),
-                        ptr::null_mut(),
-                    )
-                }
-            }
+            (None, None) => unsafe {
+                libc::openpty(
+                    master.as_mut_ptr(),
+                    slave.as_mut_ptr(),
+                    ptr::null_mut(),
+                    ptr::null_mut(),
+                    ptr::null_mut(),
+                )
+            },
         }
     };
 
@@ -294,6 +314,8 @@
     }
 }
 
+feature! {
+#![feature = "process"]
 /// Create a new pseudoterminal, returning the master file descriptor and forked pid.
 /// in `ForkptyResult`
 /// (see [`forkpty`](https://man7.org/linux/man-pages/man3/forkpty.3.html)).
@@ -346,3 +368,4 @@
         fork_result,
     })
 }
+}
diff --git a/src/sched.rs b/src/sched.rs
index c2dd7b8..d5b1233 100644
--- a/src/sched.rs
+++ b/src/sched.rs
@@ -8,14 +8,15 @@
 pub use self::sched_linux_like::*;
 
 #[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
 mod sched_linux_like {
     use crate::errno::Errno;
+    use crate::unistd::Pid;
+    use crate::Result;
     use libc::{self, c_int, c_void};
     use std::mem;
     use std::option::Option;
     use std::os::unix::io::RawFd;
-    use crate::unistd::Pid;
-    use crate::Result;
 
     // For some functions taking with a parameter of type CloneFlags,
     // only a subset of these flags have an effect.
@@ -86,15 +87,94 @@
     /// Type for the function executed by [`clone`].
     pub type CloneCb<'a> = Box<dyn FnMut() -> isize + 'a>;
 
+    /// `clone` create a child process
+    /// ([`clone(2)`](https://man7.org/linux/man-pages/man2/clone.2.html))
+    ///
+    /// `stack` is a reference to an array which will hold the stack of the new
+    /// process.  Unlike when calling `clone(2)` from C, the provided stack
+    /// address need not be the highest address of the region.  Nix will take
+    /// care of that requirement.  The user only needs to provide a reference to
+    /// a normally allocated buffer.
+    pub fn clone(
+        mut cb: CloneCb,
+        stack: &mut [u8],
+        flags: CloneFlags,
+        signal: Option<c_int>,
+    ) -> Result<Pid> {
+        extern "C" fn callback(data: *mut CloneCb) -> c_int {
+            let cb: &mut CloneCb = unsafe { &mut *data };
+            (*cb)() as c_int
+        }
+
+        let res = unsafe {
+            let combined = flags.bits() | signal.unwrap_or(0);
+            let ptr = stack.as_mut_ptr().add(stack.len());
+            let ptr_aligned = ptr.sub(ptr as usize % 16);
+            libc::clone(
+                mem::transmute(
+                    callback
+                        as extern "C" fn(*mut Box<dyn FnMut() -> isize>) -> i32,
+                ),
+                ptr_aligned as *mut c_void,
+                combined,
+                &mut cb as *mut _ as *mut c_void,
+            )
+        };
+
+        Errno::result(res).map(Pid::from_raw)
+    }
+
+    /// disassociate parts of the process execution context
+    ///
+    /// See also [unshare(2)](https://man7.org/linux/man-pages/man2/unshare.2.html)
+    pub fn unshare(flags: CloneFlags) -> Result<()> {
+        let res = unsafe { libc::unshare(flags.bits()) };
+
+        Errno::result(res).map(drop)
+    }
+
+    /// reassociate thread with a namespace
+    ///
+    /// See also [setns(2)](https://man7.org/linux/man-pages/man2/setns.2.html)
+    pub fn setns(fd: RawFd, nstype: CloneFlags) -> Result<()> {
+        let res = unsafe { libc::setns(fd, nstype.bits()) };
+
+        Errno::result(res).map(drop)
+    }
+}
+
+#[cfg(any(
+    target_os = "android",
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "linux"
+))]
+pub use self::sched_affinity::*;
+
+#[cfg(any(
+    target_os = "android",
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "linux"
+))]
+mod sched_affinity {
+    use crate::errno::Errno;
+    use crate::unistd::Pid;
+    use crate::Result;
+    use std::mem;
+
     /// CpuSet represent a bit-mask of CPUs.
     /// CpuSets are used by sched_setaffinity and
     /// sched_getaffinity for example.
     ///
     /// This is a wrapper around `libc::cpu_set_t`.
-    #[repr(C)]
+    #[repr(transparent)]
     #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
     pub struct CpuSet {
+        #[cfg(not(target_os = "freebsd"))]
         cpu_set: libc::cpu_set_t,
+        #[cfg(target_os = "freebsd")]
+        cpu_set: libc::cpuset_t,
     }
 
     impl CpuSet {
@@ -121,7 +201,9 @@
             if field >= CpuSet::count() {
                 Err(Errno::EINVAL)
             } else {
-                unsafe { libc::CPU_SET(field, &mut self.cpu_set); }
+                unsafe {
+                    libc::CPU_SET(field, &mut self.cpu_set);
+                }
                 Ok(())
             }
         }
@@ -132,14 +214,21 @@
             if field >= CpuSet::count() {
                 Err(Errno::EINVAL)
             } else {
-                unsafe { libc::CPU_CLR(field, &mut self.cpu_set);}
+                unsafe {
+                    libc::CPU_CLR(field, &mut self.cpu_set);
+                }
                 Ok(())
             }
         }
 
         /// Return the maximum number of CPU in CpuSet
         pub const fn count() -> usize {
-            8 * mem::size_of::<libc::cpu_set_t>()
+            #[cfg(not(target_os = "freebsd"))]
+            let bytes = mem::size_of::<libc::cpu_set_t>();
+            #[cfg(target_os = "freebsd")]
+            let bytes = mem::size_of::<libc::cpuset_t>();
+
+            8 * bytes
         }
     }
 
@@ -167,8 +256,8 @@
     /// use nix::unistd::Pid;
     ///
     /// let mut cpu_set = CpuSet::new();
-    /// cpu_set.set(0);
-    /// sched_setaffinity(Pid::from_raw(0), &cpu_set);
+    /// cpu_set.set(0).unwrap();
+    /// sched_setaffinity(Pid::from_raw(0), &cpu_set).unwrap();
     /// ```
     pub fn sched_setaffinity(pid: Pid, cpuset: &CpuSet) -> Result<()> {
         let res = unsafe {
@@ -217,58 +306,11 @@
         Errno::result(res).and(Ok(cpuset))
     }
 
-    /// `clone` create a child process
-    /// ([`clone(2)`](https://man7.org/linux/man-pages/man2/clone.2.html))
-    ///
-    /// `stack` is a reference to an array which will hold the stack of the new
-    /// process.  Unlike when calling `clone(2)` from C, the provided stack
-    /// address need not be the highest address of the region.  Nix will take
-    /// care of that requirement.  The user only needs to provide a reference to
-    /// a normally allocated buffer.
-    pub fn clone(
-        mut cb: CloneCb,
-        stack: &mut [u8],
-        flags: CloneFlags,
-        signal: Option<c_int>,
-    ) -> Result<Pid> {
-        extern "C" fn callback(data: *mut CloneCb) -> c_int {
-            let cb: &mut CloneCb = unsafe { &mut *data };
-            (*cb)() as c_int
-        }
+    /// Determines the CPU on which the calling thread is running.
+    pub fn sched_getcpu() -> Result<usize> {
+        let res = unsafe { libc::sched_getcpu() };
 
-        let res = unsafe {
-            let combined = flags.bits() | signal.unwrap_or(0);
-            let ptr = stack.as_mut_ptr().add(stack.len());
-            let ptr_aligned = ptr.sub(ptr as usize % 16);
-            libc::clone(
-                mem::transmute(
-                    callback as extern "C" fn(*mut Box<dyn FnMut() -> isize>) -> i32,
-                ),
-                ptr_aligned as *mut c_void,
-                combined,
-                &mut cb as *mut _ as *mut c_void,
-            )
-        };
-
-        Errno::result(res).map(Pid::from_raw)
-    }
-
-    /// disassociate parts of the process execution context
-    ///
-    /// See also [unshare(2)](https://man7.org/linux/man-pages/man2/unshare.2.html)
-    pub fn unshare(flags: CloneFlags) -> Result<()> {
-        let res = unsafe { libc::unshare(flags.bits()) };
-
-        Errno::result(res).map(drop)
-    }
-
-    /// reassociate thread with a namespace
-    ///
-    /// See also [setns(2)](https://man7.org/linux/man-pages/man2/setns.2.html)
-    pub fn setns(fd: RawFd, nstype: CloneFlags) -> Result<()> {
-        let res = unsafe { libc::setns(fd, nstype.bits()) };
-
-        Errno::result(res).map(drop)
+        Errno::result(res).map(|int| int as usize)
     }
 }
 
diff --git a/src/sys/aio.rs b/src/sys/aio.rs
index e64a2a8..e2ce19b 100644
--- a/src/sys/aio.rs
+++ b/src/sys/aio.rs
@@ -2,9 +2,12 @@
 //! POSIX Asynchronous I/O
 //!
 //! The POSIX AIO interface is used for asynchronous I/O on files and disk-like
-//! devices.  It supports [`read`](struct.AioCb.html#method.read),
-//! [`write`](struct.AioCb.html#method.write), and
-//! [`fsync`](struct.AioCb.html#method.fsync) operations.  Completion
+//! devices.  It supports [`read`](struct.AioRead.html#method.new),
+//! [`write`](struct.AioWrite.html#method.new),
+//! [`fsync`](struct.AioFsync.html#method.new),
+//! [`readv`](struct.AioReadv.html#method.new), and
+//! [`writev`](struct.AioWritev.html#method.new), operations, subject to
+//! platform support.  Completion
 //! notifications can optionally be delivered via
 //! [signals](../signal/enum.SigevNotify.html#variant.SigevSignal), via the
 //! [`aio_suspend`](fn.aio_suspend.html) function, or via polling.  Some
@@ -17,23 +20,29 @@
 //! that they will be executed atomically.
 //!
 //! Outstanding operations may be cancelled with
-//! [`cancel`](struct.AioCb.html#method.cancel) or
+//! [`cancel`](trait.Aio.html#method.cancel) or
 //! [`aio_cancel_all`](fn.aio_cancel_all.html), though the operating system may
 //! not support this for all filesystems and devices.
+#[cfg(target_os = "freebsd")]
+use std::io::{IoSlice, IoSliceMut};
+use std::{
+    convert::TryFrom,
+    fmt::{self, Debug},
+    marker::{PhantomData, PhantomPinned},
+    mem,
+    os::unix::io::RawFd,
+    pin::Pin,
+    ptr, thread,
+};
 
-use crate::Result;
-use crate::errno::Errno;
-use std::os::unix::io::RawFd;
-use libc::{c_void, off_t, size_t};
-use std::fmt;
-use std::fmt::Debug;
-use std::marker::PhantomData;
-use std::mem;
-use std::pin::Pin;
-use std::ptr::{null, null_mut};
-use crate::sys::signal::*;
-use std::thread;
-use crate::sys::time::TimeSpec;
+use libc::{c_void, off_t};
+use pin_utils::unsafe_pinned;
+
+use crate::{
+    errno::Errno,
+    sys::{signal::*, time::TimeSpec},
+    Result,
+};
 
 libc_enum! {
     /// Mode for `AioCb::fsync`.  Controls whether only data or both data and
@@ -49,24 +58,10 @@
                   target_os = "macos",
                   target_os = "netbsd",
                   target_os = "openbsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         O_DSYNC
     }
-}
-
-libc_enum! {
-    /// When used with [`lio_listio`](fn.lio_listio.html), determines whether a
-    /// given `aiocb` should be used for a read operation, a write operation, or
-    /// ignored.  Has no effect for any other aio functions.
-    #[repr(i32)]
-    #[non_exhaustive]
-    pub enum LioOpcode {
-        /// No operation
-        LIO_NOP,
-        /// Write data as if by a call to [`AioCb::write`]
-        LIO_WRITE,
-        /// Write data as if by a call to [`AioCb::read`]
-        LIO_READ,
-    }
+    impl TryFrom<i32>
 }
 
 libc_enum! {
@@ -102,355 +97,134 @@
 unsafe impl Send for LibcAiocb {}
 unsafe impl Sync for LibcAiocb {}
 
-/// AIO Control Block.
-///
-/// The basic structure used by all aio functions.  Each `AioCb` represents one
-/// I/O request.
-pub struct AioCb<'a> {
+/// Base class for all AIO operations.  Should only be used directly when
+/// checking for completion.
+// We could create some kind of AsPinnedMut trait, and implement it for all aio
+// ops, allowing the crate's users to get pinned references to `AioCb`.  That
+// could save some code for things like polling methods.  But IMHO it would
+// provide polymorphism at the wrong level.  Instead, the best place for
+// polymorphism is at the level of `Futures`.
+#[repr(C)]
+struct AioCb {
     aiocb: LibcAiocb,
-    /// Tracks whether the buffer pointed to by `libc::aiocb.aio_buf` is mutable
-    mutable: bool,
     /// Could this `AioCb` potentially have any in-kernel state?
+    // It would be really nice to perform the in-progress check entirely at
+    // compile time.  But I can't figure out how, because:
+    // * Future::poll takes a `Pin<&mut self>` rather than `self`, and
+    // * Rust's lack of an equivalent of C++'s Guaranteed Copy Elision means
+    //   that there's no way to write an AioCb constructor that neither boxes
+    //   the object itself, nor moves it during return.
     in_progress: bool,
-    _buffer: std::marker::PhantomData<&'a [u8]>,
-    _pin: std::marker::PhantomPinned
 }
 
-impl<'a> AioCb<'a> {
-    /// Returns the underlying file descriptor associated with the `AioCb`
-    pub fn fd(&self) -> RawFd {
-        self.aiocb.0.aio_fildes
+impl AioCb {
+    pin_utils::unsafe_unpinned!(aiocb: LibcAiocb);
+
+    fn aio_return(mut self: Pin<&mut Self>) -> Result<usize> {
+        self.in_progress = false;
+        unsafe {
+            let p: *mut libc::aiocb = &mut self.aiocb.0;
+            Errno::result(libc::aio_return(p))
+        }
+        .map(|r| r as usize)
     }
 
-    /// Constructs a new `AioCb` with no associated buffer.
-    ///
-    /// The resulting `AioCb` structure is suitable for use with `AioCb::fsync`.
-    ///
-    /// # Parameters
-    ///
-    /// * `fd`:           File descriptor.  Required for all aio functions.
-    /// * `prio`:         If POSIX Prioritized IO is supported, then the
-    ///                   operation will be prioritized at the process's
-    ///                   priority level minus `prio`.
-    /// * `sigev_notify`: Determines how you will be notified of event
-    ///                    completion.
-    ///
-    /// # Examples
-    ///
-    /// Create an `AioCb` from a raw file descriptor and use it for an
-    /// [`fsync`](#method.fsync) operation.
-    ///
-    /// ```
-    /// # use nix::errno::Errno;
-    /// # use nix::Error;
-    /// # use nix::sys::aio::*;
-    /// # use nix::sys::signal::SigevNotify::SigevNone;
-    /// # use std::{thread, time};
-    /// # use std::os::unix::io::AsRawFd;
-    /// # use tempfile::tempfile;
-    /// let f = tempfile().unwrap();
-    /// let mut aiocb = AioCb::from_fd( f.as_raw_fd(), 0, SigevNone);
-    /// aiocb.fsync(AioFsyncMode::O_SYNC).expect("aio_fsync failed early");
-    /// while (aiocb.error() == Err(Errno::EINPROGRESS)) {
-    ///     thread::sleep(time::Duration::from_millis(10));
-    /// }
-    /// aiocb.aio_return().expect("aio_fsync failed late");
-    /// ```
-    pub fn from_fd(fd: RawFd, prio: libc::c_int,
-                    sigev_notify: SigevNotify) -> Pin<Box<AioCb<'a>>> {
-        let mut a = AioCb::common_init(fd, prio, sigev_notify);
-        a.0.aio_offset = 0;
-        a.0.aio_nbytes = 0;
-        a.0.aio_buf = null_mut();
-
-        Box::pin(AioCb {
-            aiocb: a,
-            mutable: false,
-            in_progress: false,
-            _buffer: PhantomData,
-            _pin: std::marker::PhantomPinned
-        })
-    }
-
-    // Private helper
-    #[cfg(not(any(target_os = "ios", target_os = "macos")))]
-    fn from_mut_slice_unpinned(fd: RawFd, offs: off_t, buf: &'a mut [u8],
-                          prio: libc::c_int, sigev_notify: SigevNotify,
-                          opcode: LioOpcode) -> AioCb<'a>
-    {
-        let mut a = AioCb::common_init(fd, prio, sigev_notify);
-        a.0.aio_offset = offs;
-        a.0.aio_nbytes = buf.len() as size_t;
-        a.0.aio_buf = buf.as_ptr() as *mut c_void;
-        a.0.aio_lio_opcode = opcode as libc::c_int;
-
-        AioCb {
-            aiocb: a,
-            mutable: true,
-            in_progress: false,
-            _buffer: PhantomData,
-            _pin: std::marker::PhantomPinned
+    fn cancel(mut self: Pin<&mut Self>) -> Result<AioCancelStat> {
+        let r = unsafe {
+            libc::aio_cancel(self.aiocb.0.aio_fildes, &mut self.aiocb.0)
+        };
+        match r {
+            libc::AIO_CANCELED => Ok(AioCancelStat::AioCanceled),
+            libc::AIO_NOTCANCELED => Ok(AioCancelStat::AioNotCanceled),
+            libc::AIO_ALLDONE => Ok(AioCancelStat::AioAllDone),
+            -1 => Err(Errno::last()),
+            _ => panic!("unknown aio_cancel return value"),
         }
     }
 
-    /// Constructs a new `AioCb` from a mutable slice.
-    ///
-    /// The resulting `AioCb` will be suitable for both read and write
-    /// operations, but only if the borrow checker can guarantee that the slice
-    /// will outlive the `AioCb`.  That will usually be the case if the `AioCb`
-    /// is stack-allocated.
-    ///
-    /// # Parameters
-    ///
-    /// * `fd`:           File descriptor.  Required for all aio functions.
-    /// * `offs`:         File offset
-    /// * `buf`:          A memory buffer
-    /// * `prio`:         If POSIX Prioritized IO is supported, then the
-    ///                   operation will be prioritized at the process's
-    ///                   priority level minus `prio`
-    /// * `sigev_notify`: Determines how you will be notified of event
-    ///                   completion.
-    /// * `opcode`:       This field is only used for `lio_listio`.  It
-    ///                   determines which operation to use for this individual
-    ///                   aiocb
-    ///
-    /// # Examples
-    ///
-    /// Create an `AioCb` from a mutable slice and read into it.
-    ///
-    /// ```
-    /// # use nix::errno::Errno;
-    /// # use nix::Error;
-    /// # use nix::sys::aio::*;
-    /// # use nix::sys::signal::SigevNotify;
-    /// # use std::{thread, time};
-    /// # use std::io::Write;
-    /// # use std::os::unix::io::AsRawFd;
-    /// # use tempfile::tempfile;
-    /// const INITIAL: &[u8] = b"abcdef123456";
-    /// const LEN: usize = 4;
-    /// let mut rbuf = vec![0; LEN];
-    /// let mut f = tempfile().unwrap();
-    /// f.write_all(INITIAL).unwrap();
-    /// {
-    ///     let mut aiocb = AioCb::from_mut_slice( f.as_raw_fd(),
-    ///         2,   //offset
-    ///         &mut rbuf,
-    ///         0,   //priority
-    ///         SigevNotify::SigevNone,
-    ///         LioOpcode::LIO_NOP);
-    ///     aiocb.read().unwrap();
-    ///     while (aiocb.error() == Err(Errno::EINPROGRESS)) {
-    ///         thread::sleep(time::Duration::from_millis(10));
-    ///     }
-    ///     assert_eq!(aiocb.aio_return().unwrap() as usize, LEN);
-    /// }
-    /// assert_eq!(rbuf, b"cdef");
-    /// ```
-    pub fn from_mut_slice(fd: RawFd, offs: off_t, buf: &'a mut [u8],
-                          prio: libc::c_int, sigev_notify: SigevNotify,
-                          opcode: LioOpcode) -> Pin<Box<AioCb<'a>>> {
-        let mut a = AioCb::common_init(fd, prio, sigev_notify);
-        a.0.aio_offset = offs;
-        a.0.aio_nbytes = buf.len() as size_t;
-        a.0.aio_buf = buf.as_ptr() as *mut c_void;
-        a.0.aio_lio_opcode = opcode as libc::c_int;
-
-        Box::pin(AioCb {
-            aiocb: a,
-            mutable: true,
-            in_progress: false,
-            _buffer: PhantomData,
-            _pin: std::marker::PhantomPinned
-        })
-    }
-
-    /// Constructs a new `AioCb` from a mutable raw pointer
-    ///
-    /// Unlike `from_mut_slice`, this method returns a structure suitable for
-    /// placement on the heap.  It may be used for both reads and writes.  Due
-    /// to its unsafety, this method is not recommended.  It is most useful when
-    /// heap allocation is required.
-    ///
-    /// # Parameters
-    ///
-    /// * `fd`:           File descriptor.  Required for all aio functions.
-    /// * `offs`:         File offset
-    /// * `buf`:          Pointer to the memory buffer
-    /// * `len`:          Length of the buffer pointed to by `buf`
-    /// * `prio`:         If POSIX Prioritized IO is supported, then the
-    ///                   operation will be prioritized at the process's
-    ///                   priority level minus `prio`
-    /// * `sigev_notify`: Determines how you will be notified of event
-    ///                   completion.
-    /// * `opcode`:       This field is only used for `lio_listio`.  It
-    ///                   determines which operation to use for this individual
-    ///                   aiocb
-    ///
-    /// # Safety
-    ///
-    /// The caller must ensure that the storage pointed to by `buf` outlives the
-    /// `AioCb`.  The lifetime checker can't help here.
-    pub unsafe fn from_mut_ptr(fd: RawFd, offs: off_t,
-                           buf: *mut c_void, len: usize,
-                           prio: libc::c_int, sigev_notify: SigevNotify,
-                           opcode: LioOpcode) -> Pin<Box<AioCb<'a>>> {
-        let mut a = AioCb::common_init(fd, prio, sigev_notify);
-        a.0.aio_offset = offs;
-        a.0.aio_nbytes = len;
-        a.0.aio_buf = buf;
-        a.0.aio_lio_opcode = opcode as libc::c_int;
-
-        Box::pin(AioCb {
-            aiocb: a,
-            mutable: true,
-            in_progress: false,
-            _buffer: PhantomData,
-            _pin: std::marker::PhantomPinned,
-        })
-    }
-
-    /// Constructs a new `AioCb` from a raw pointer.
-    ///
-    /// Unlike `from_slice`, this method returns a structure suitable for
-    /// placement on the heap.  Due to its unsafety, this method is not
-    /// recommended.  It is most useful when heap allocation is required.
-    ///
-    /// # Parameters
-    ///
-    /// * `fd`:           File descriptor.  Required for all aio functions.
-    /// * `offs`:         File offset
-    /// * `buf`:          Pointer to the memory buffer
-    /// * `len`:          Length of the buffer pointed to by `buf`
-    /// * `prio`:         If POSIX Prioritized IO is supported, then the
-    ///                   operation will be prioritized at the process's
-    ///                   priority level minus `prio`
-    /// * `sigev_notify`: Determines how you will be notified of event
-    ///                   completion.
-    /// * `opcode`:       This field is only used for `lio_listio`.  It
-    ///                   determines which operation to use for this individual
-    ///                   aiocb
-    ///
-    /// # Safety
-    ///
-    /// The caller must ensure that the storage pointed to by `buf` outlives the
-    /// `AioCb`.  The lifetime checker can't help here.
-    pub unsafe fn from_ptr(fd: RawFd, offs: off_t,
-                           buf: *const c_void, len: usize,
-                           prio: libc::c_int, sigev_notify: SigevNotify,
-                           opcode: LioOpcode) -> Pin<Box<AioCb<'a>>> {
-        let mut a = AioCb::common_init(fd, prio, sigev_notify);
-        a.0.aio_offset = offs;
-        a.0.aio_nbytes = len;
-        // casting a const ptr to a mutable ptr here is ok, because we set the
-        // AioCb's mutable field to false
-        a.0.aio_buf = buf as *mut c_void;
-        a.0.aio_lio_opcode = opcode as libc::c_int;
-
-        Box::pin(AioCb {
-            aiocb: a,
-            mutable: false,
-            in_progress: false,
-            _buffer: PhantomData,
-            _pin: std::marker::PhantomPinned
-        })
-    }
-
-    // Private helper
-    fn from_slice_unpinned(fd: RawFd, offs: off_t, buf: &'a [u8],
-                           prio: libc::c_int, sigev_notify: SigevNotify,
-                           opcode: LioOpcode) -> AioCb
-    {
-        let mut a = AioCb::common_init(fd, prio, sigev_notify);
-        a.0.aio_offset = offs;
-        a.0.aio_nbytes = buf.len() as size_t;
-        // casting an immutable buffer to a mutable pointer looks unsafe,
-        // but technically its only unsafe to dereference it, not to create
-        // it.
-        a.0.aio_buf = buf.as_ptr() as *mut c_void;
-        assert!(opcode != LioOpcode::LIO_READ, "Can't read into an immutable buffer");
-        a.0.aio_lio_opcode = opcode as libc::c_int;
-
-        AioCb {
-            aiocb: a,
-            mutable: false,
-            in_progress: false,
-            _buffer: PhantomData,
-            _pin: std::marker::PhantomPinned
-        }
-    }
-
-    /// Like [`AioCb::from_mut_slice`], but works on constant slices rather than
-    /// mutable slices.
-    ///
-    /// An `AioCb` created this way cannot be used with `read`, and its
-    /// `LioOpcode` cannot be set to `LIO_READ`.  This method is useful when
-    /// writing a const buffer with `AioCb::write`, since `from_mut_slice` can't
-    /// work with const buffers.
-    ///
-    /// # Examples
-    ///
-    /// Construct an `AioCb` from a slice and use it for writing.
-    ///
-    /// ```
-    /// # use nix::errno::Errno;
-    /// # use nix::Error;
-    /// # use nix::sys::aio::*;
-    /// # use nix::sys::signal::SigevNotify;
-    /// # use std::{thread, time};
-    /// # use std::os::unix::io::AsRawFd;
-    /// # use tempfile::tempfile;
-    /// const WBUF: &[u8] = b"abcdef123456";
-    /// let mut f = tempfile().unwrap();
-    /// let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
-    ///     2,   //offset
-    ///     WBUF,
-    ///     0,   //priority
-    ///     SigevNotify::SigevNone,
-    ///     LioOpcode::LIO_NOP);
-    /// aiocb.write().unwrap();
-    /// while (aiocb.error() == Err(Errno::EINPROGRESS)) {
-    ///     thread::sleep(time::Duration::from_millis(10));
-    /// }
-    /// assert_eq!(aiocb.aio_return().unwrap() as usize, WBUF.len());
-    /// ```
-    // Note: another solution to the problem of writing const buffers would be
-    // to genericize AioCb for both &mut [u8] and &[u8] buffers.  AioCb::read
-    // could take the former and AioCb::write could take the latter.  However,
-    // then lio_listio wouldn't work, because that function needs a slice of
-    // AioCb, and they must all be of the same type.
-    pub fn from_slice(fd: RawFd, offs: off_t, buf: &'a [u8],
-                      prio: libc::c_int, sigev_notify: SigevNotify,
-                      opcode: LioOpcode) -> Pin<Box<AioCb>>
-    {
-        Box::pin(AioCb::from_slice_unpinned(fd, offs, buf, prio, sigev_notify,
-                                            opcode))
-    }
-
-    fn common_init(fd: RawFd, prio: libc::c_int,
-                   sigev_notify: SigevNotify) -> LibcAiocb {
+    fn common_init(fd: RawFd, prio: i32, sigev_notify: SigevNotify) -> Self {
         // Use mem::zeroed instead of explicitly zeroing each field, because the
         // number and name of reserved fields is OS-dependent.  On some OSes,
         // some reserved fields are used the kernel for state, and must be
         // explicitly zeroed when allocated.
-        let mut a = unsafe { mem::zeroed::<libc::aiocb>()};
+        let mut a = unsafe { mem::zeroed::<libc::aiocb>() };
         a.aio_fildes = fd;
         a.aio_reqprio = prio;
         a.aio_sigevent = SigEvent::new(sigev_notify).sigevent();
-        LibcAiocb(a)
+        AioCb {
+            aiocb: LibcAiocb(a),
+            in_progress: false,
+        }
     }
 
-    /// Update the notification settings for an existing `aiocb`
-    pub fn set_sigev_notify(self: &mut Pin<Box<Self>>,
-                            sigev_notify: SigevNotify)
-    {
-        // Safe because we don't move any of the data
-        let selfp = unsafe {
-            self.as_mut().get_unchecked_mut()
-        };
-        selfp.aiocb.0.aio_sigevent = SigEvent::new(sigev_notify).sigevent();
+    fn error(self: Pin<&mut Self>) -> Result<()> {
+        let r = unsafe { libc::aio_error(&self.aiocb().0) };
+        match r {
+            0 => Ok(()),
+            num if num > 0 => Err(Errno::from_i32(num)),
+            -1 => Err(Errno::last()),
+            num => panic!("unknown aio_error return value {:?}", num),
+        }
     }
 
+    fn in_progress(&self) -> bool {
+        self.in_progress
+    }
+
+    fn set_in_progress(mut self: Pin<&mut Self>) {
+        self.as_mut().in_progress = true;
+    }
+
+    /// Update the notification settings for an existing AIO operation that has
+    /// not yet been submitted.
+    // Takes a normal reference rather than a pinned one because this method is
+    // normally called before the object needs to be pinned, that is, before
+    // it's been submitted to the kernel.
+    fn set_sigev_notify(&mut self, sigev_notify: SigevNotify) {
+        assert!(
+            !self.in_progress,
+            "Can't change notification settings for an in-progress operation"
+        );
+        self.aiocb.0.aio_sigevent = SigEvent::new(sigev_notify).sigevent();
+    }
+}
+
+impl Debug for AioCb {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        fmt.debug_struct("AioCb")
+            .field("aiocb", &self.aiocb.0)
+            .field("in_progress", &self.in_progress)
+            .finish()
+    }
+}
+
+impl Drop for AioCb {
+    /// If the `AioCb` has no remaining state in the kernel, just drop it.
+    /// Otherwise, dropping constitutes a resource leak, which is an error
+    fn drop(&mut self) {
+        assert!(
+            thread::panicking() || !self.in_progress,
+            "Dropped an in-progress AioCb"
+        );
+    }
+}
+
+/// Methods common to all AIO operations
+pub trait Aio {
+    /// The return type of [`Aio::aio_return`].
+    type Output;
+
+    /// Retrieve return status of an asynchronous operation.
+    ///
+    /// Should only be called once for each operation, after [`Aio::error`]
+    /// indicates that it has completed.  The result is the same as for the
+    /// synchronous `read(2)`, `write(2)`, of `fsync(2)` functions.
+    ///
+    /// # References
+    ///
+    /// [aio_return](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_return.html)
+    fn aio_return(self: Pin<&mut Self>) -> Result<Self::Output>;
+
     /// Cancels an outstanding AIO request.
     ///
     /// The operating system is not required to implement cancellation for all
@@ -476,51 +250,26 @@
     /// # use tempfile::tempfile;
     /// let wbuf = b"CDEF";
     /// let mut f = tempfile().unwrap();
-    /// let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
+    /// let mut aiocb = Box::pin(AioWrite::new(f.as_raw_fd(),
     ///     2,   //offset
     ///     &wbuf[..],
     ///     0,   //priority
-    ///     SigevNotify::SigevNone,
-    ///     LioOpcode::LIO_NOP);
-    /// aiocb.write().unwrap();
-    /// let cs = aiocb.cancel().unwrap();
+    ///     SigevNotify::SigevNone));
+    /// aiocb.as_mut().submit().unwrap();
+    /// let cs = aiocb.as_mut().cancel().unwrap();
     /// if cs == AioCancelStat::AioNotCanceled {
-    ///     while (aiocb.error() == Err(Errno::EINPROGRESS)) {
+    ///     while (aiocb.as_mut().error() == Err(Errno::EINPROGRESS)) {
     ///         thread::sleep(time::Duration::from_millis(10));
     ///     }
     /// }
     /// // Must call `aio_return`, but ignore the result
-    /// let _ = aiocb.aio_return();
+    /// let _ = aiocb.as_mut().aio_return();
     /// ```
     ///
     /// # References
     ///
     /// [aio_cancel](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_cancel.html)
-    pub fn cancel(self: &mut Pin<Box<Self>>) -> Result<AioCancelStat> {
-        let r = unsafe {
-            let selfp = self.as_mut().get_unchecked_mut();
-            libc::aio_cancel(selfp.aiocb.0.aio_fildes, &mut selfp.aiocb.0)
-        };
-        match r {
-            libc::AIO_CANCELED => Ok(AioCancelStat::AioCanceled),
-            libc::AIO_NOTCANCELED => Ok(AioCancelStat::AioNotCanceled),
-            libc::AIO_ALLDONE => Ok(AioCancelStat::AioAllDone),
-            -1 => Err(Errno::last()),
-            _ => panic!("unknown aio_cancel return value")
-        }
-    }
-
-    fn error_unpinned(&mut self) -> Result<()> {
-        let r = unsafe {
-            libc::aio_error(&mut self.aiocb.0 as *mut libc::aiocb)
-        };
-        match r {
-            0 => Ok(()),
-            num if num > 0 => Err(Errno::from_i32(num)),
-            -1 => Err(Errno::last()),
-            num => panic!("unknown aio_error return value {:?}", num)
-        }
-    }
+    fn cancel(self: Pin<&mut Self>) -> Result<AioCancelStat>;
 
     /// Retrieve error status of an asynchronous operation.
     ///
@@ -542,60 +291,265 @@
     /// # use tempfile::tempfile;
     /// const WBUF: &[u8] = b"abcdef123456";
     /// let mut f = tempfile().unwrap();
-    /// let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
+    /// let mut aiocb = Box::pin(AioWrite::new(f.as_raw_fd(),
     ///     2,   //offset
     ///     WBUF,
     ///     0,   //priority
-    ///     SigevNotify::SigevNone,
-    ///     LioOpcode::LIO_NOP);
-    /// aiocb.write().unwrap();
-    /// while (aiocb.error() == Err(Errno::EINPROGRESS)) {
+    ///     SigevNotify::SigevNone));
+    /// aiocb.as_mut().submit().unwrap();
+    /// while (aiocb.as_mut().error() == Err(Errno::EINPROGRESS)) {
     ///     thread::sleep(time::Duration::from_millis(10));
     /// }
-    /// assert_eq!(aiocb.aio_return().unwrap() as usize, WBUF.len());
+    /// assert_eq!(aiocb.as_mut().aio_return().unwrap(), WBUF.len());
     /// ```
     ///
     /// # References
     ///
     /// [aio_error](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_error.html)
-    pub fn error(self: &mut Pin<Box<Self>>) -> Result<()> {
-        // Safe because error_unpinned doesn't move the data
-        let selfp = unsafe {
-            self.as_mut().get_unchecked_mut()
-        };
-        selfp.error_unpinned()
-    }
+    fn error(self: Pin<&mut Self>) -> Result<()>;
 
-    /// An asynchronous version of `fsync(2)`.
+    /// Returns the underlying file descriptor associated with the operation.
+    fn fd(&self) -> RawFd;
+
+    /// Does this operation currently have any in-kernel state?
     ///
-    /// # References
+    /// Dropping an operation that does have in-kernel state constitutes a
+    /// resource leak.
     ///
-    /// [aio_fsync](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_fsync.html)
-    pub fn fsync(self: &mut Pin<Box<Self>>, mode: AioFsyncMode) -> Result<()> {
-        // Safe because we don't move the libc::aiocb
-        unsafe {
-            let selfp = self.as_mut().get_unchecked_mut();
-            Errno::result({
-                let p: *mut libc::aiocb = &mut selfp.aiocb.0;
-                libc::aio_fsync(mode as libc::c_int, p)
-            }).map(|_| {
-                selfp.in_progress = true;
+    /// # Examples
+    ///
+    /// ```
+    /// # use nix::errno::Errno;
+    /// # use nix::Error;
+    /// # use nix::sys::aio::*;
+    /// # use nix::sys::signal::SigevNotify::SigevNone;
+    /// # use std::{thread, time};
+    /// # use std::os::unix::io::AsRawFd;
+    /// # use tempfile::tempfile;
+    /// let f = tempfile().unwrap();
+    /// let mut aiof = Box::pin(AioFsync::new(f.as_raw_fd(), AioFsyncMode::O_SYNC,
+    ///     0, SigevNone));
+    /// assert!(!aiof.as_mut().in_progress());
+    /// aiof.as_mut().submit().expect("aio_fsync failed early");
+    /// assert!(aiof.as_mut().in_progress());
+    /// while (aiof.as_mut().error() == Err(Errno::EINPROGRESS)) {
+    ///     thread::sleep(time::Duration::from_millis(10));
+    /// }
+    /// aiof.as_mut().aio_return().expect("aio_fsync failed late");
+    /// assert!(!aiof.as_mut().in_progress());
+    /// ```
+    fn in_progress(&self) -> bool;
+
+    /// Returns the priority of the `AioCb`
+    fn priority(&self) -> i32;
+
+    /// Update the notification settings for an existing AIO operation that has
+    /// not yet been submitted.
+    fn set_sigev_notify(&mut self, sev: SigevNotify);
+
+    /// Returns the `SigEvent` that will be used for notification.
+    fn sigevent(&self) -> SigEvent;
+
+    /// Actually start the I/O operation.
+    ///
+    /// After calling this method and until [`Aio::aio_return`] returns `Ok`,
+    /// the structure may not be moved in memory.
+    fn submit(self: Pin<&mut Self>) -> Result<()>;
+}
+
+macro_rules! aio_methods {
+    () => {
+        fn cancel(self: Pin<&mut Self>) -> Result<AioCancelStat> {
+            self.aiocb().cancel()
+        }
+
+        fn error(self: Pin<&mut Self>) -> Result<()> {
+            self.aiocb().error()
+        }
+
+        fn fd(&self) -> RawFd {
+            self.aiocb.aiocb.0.aio_fildes
+        }
+
+        fn in_progress(&self) -> bool {
+            self.aiocb.in_progress()
+        }
+
+        fn priority(&self) -> i32 {
+            self.aiocb.aiocb.0.aio_reqprio
+        }
+
+        fn set_sigev_notify(&mut self, sev: SigevNotify) {
+            self.aiocb.set_sigev_notify(sev)
+        }
+
+        fn sigevent(&self) -> SigEvent {
+            SigEvent::from(&self.aiocb.aiocb.0.aio_sigevent)
+        }
+    };
+    ($func:ident) => {
+        aio_methods!();
+
+        fn aio_return(self: Pin<&mut Self>) -> Result<<Self as Aio>::Output> {
+            self.aiocb().aio_return()
+        }
+
+        fn submit(mut self: Pin<&mut Self>) -> Result<()> {
+            let p: *mut libc::aiocb = &mut self.as_mut().aiocb().aiocb.0;
+            Errno::result({ unsafe { libc::$func(p) } }).map(|_| {
+                self.aiocb().set_in_progress();
             })
         }
+    };
+}
+
+/// An asynchronous version of `fsync(2)`.
+///
+/// # References
+///
+/// [aio_fsync](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_fsync.html)
+/// # Examples
+///
+/// ```
+/// # use nix::errno::Errno;
+/// # use nix::Error;
+/// # use nix::sys::aio::*;
+/// # use nix::sys::signal::SigevNotify::SigevNone;
+/// # use std::{thread, time};
+/// # use std::os::unix::io::AsRawFd;
+/// # use tempfile::tempfile;
+/// let f = tempfile().unwrap();
+/// let mut aiof = Box::pin(AioFsync::new(f.as_raw_fd(), AioFsyncMode::O_SYNC,
+///     0, SigevNone));
+/// aiof.as_mut().submit().expect("aio_fsync failed early");
+/// while (aiof.as_mut().error() == Err(Errno::EINPROGRESS)) {
+///     thread::sleep(time::Duration::from_millis(10));
+/// }
+/// aiof.as_mut().aio_return().expect("aio_fsync failed late");
+/// ```
+#[derive(Debug)]
+#[repr(transparent)]
+pub struct AioFsync {
+    aiocb: AioCb,
+    _pin: PhantomPinned,
+}
+
+impl AioFsync {
+    unsafe_pinned!(aiocb: AioCb);
+
+    /// Returns the operation's fsync mode: data and metadata or data only?
+    pub fn mode(&self) -> AioFsyncMode {
+        AioFsyncMode::try_from(self.aiocb.aiocb.0.aio_lio_opcode).unwrap()
     }
 
-    /// Returns the `aiocb`'s `LioOpcode` field
+    /// Create a new `AioFsync`.
     ///
-    /// If the value cannot be represented as an `LioOpcode`, returns `None`
-    /// instead.
-    pub fn lio_opcode(&self) -> Option<LioOpcode> {
-        match self.aiocb.0.aio_lio_opcode {
-            libc::LIO_READ => Some(LioOpcode::LIO_READ),
-            libc::LIO_WRITE => Some(LioOpcode::LIO_WRITE),
-            libc::LIO_NOP => Some(LioOpcode::LIO_NOP),
-            _ => None
+    /// # Arguments
+    ///
+    /// * `fd`:           File descriptor to sync.
+    /// * `mode`:         Whether to sync file metadata too, or just data.
+    /// * `prio`:         If POSIX Prioritized IO is supported, then the
+    ///                   operation will be prioritized at the process's
+    ///                   priority level minus `prio`.
+    /// * `sigev_notify`: Determines how you will be notified of event
+    ///                   completion.
+    pub fn new(
+        fd: RawFd,
+        mode: AioFsyncMode,
+        prio: i32,
+        sigev_notify: SigevNotify,
+    ) -> Self {
+        let mut aiocb = AioCb::common_init(fd, prio, sigev_notify);
+        // To save some memory, store mode in an unused field of the AioCb.
+        // True it isn't very much memory, but downstream creates will likely
+        // create an enum containing this and other AioCb variants and pack
+        // those enums into data structures like Vec, so it adds up.
+        aiocb.aiocb.0.aio_lio_opcode = mode as libc::c_int;
+        AioFsync {
+            aiocb,
+            _pin: PhantomPinned,
         }
     }
+}
+
+impl Aio for AioFsync {
+    type Output = ();
+
+    aio_methods!();
+
+    fn aio_return(self: Pin<&mut Self>) -> Result<()> {
+        self.aiocb().aio_return().map(drop)
+    }
+
+    fn submit(mut self: Pin<&mut Self>) -> Result<()> {
+        let aiocb = &mut self.as_mut().aiocb().aiocb.0;
+        let mode = mem::replace(&mut aiocb.aio_lio_opcode, 0);
+        let p: *mut libc::aiocb = aiocb;
+        Errno::result(unsafe { libc::aio_fsync(mode, p) }).map(|_| {
+            self.aiocb().set_in_progress();
+        })
+    }
+}
+
+// AioFsync does not need AsMut, since it can't be used with lio_listio
+
+impl AsRef<libc::aiocb> for AioFsync {
+    fn as_ref(&self) -> &libc::aiocb {
+        &self.aiocb.aiocb.0
+    }
+}
+
+/// Asynchronously reads from a file descriptor into a buffer
+///
+/// # References
+///
+/// [aio_read](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_read.html)
+///
+/// # Examples
+///
+///
+/// ```
+/// # use nix::errno::Errno;
+/// # use nix::Error;
+/// # use nix::sys::aio::*;
+/// # use nix::sys::signal::SigevNotify;
+/// # use std::{thread, time};
+/// # use std::io::Write;
+/// # use std::os::unix::io::AsRawFd;
+/// # use tempfile::tempfile;
+/// const INITIAL: &[u8] = b"abcdef123456";
+/// const LEN: usize = 4;
+/// let mut rbuf = vec![0; LEN];
+/// let mut f = tempfile().unwrap();
+/// f.write_all(INITIAL).unwrap();
+/// {
+///     let mut aior = Box::pin(
+///         AioRead::new(
+///             f.as_raw_fd(),
+///             2,   //offset
+///             &mut rbuf,
+///             0,   //priority
+///             SigevNotify::SigevNone
+///         )
+///     );
+///     aior.as_mut().submit().unwrap();
+///     while (aior.as_mut().error() == Err(Errno::EINPROGRESS)) {
+///         thread::sleep(time::Duration::from_millis(10));
+///     }
+///     assert_eq!(aior.as_mut().aio_return().unwrap(), LEN);
+/// }
+/// assert_eq!(rbuf, b"cdef");
+/// ```
+#[derive(Debug)]
+#[repr(transparent)]
+pub struct AioRead<'a> {
+    aiocb: AioCb,
+    _data: PhantomData<&'a [u8]>,
+    _pin: PhantomPinned,
+}
+
+impl<'a> AioRead<'a> {
+    unsafe_pinned!(aiocb: AioCb);
 
     /// Returns the requested length of the aio operation in bytes
     ///
@@ -603,85 +557,418 @@
     /// number of bytes actually read or written by a completed operation, use
     /// `aio_return` instead.
     pub fn nbytes(&self) -> usize {
-        self.aiocb.0.aio_nbytes
+        self.aiocb.aiocb.0.aio_nbytes
     }
 
-    /// Returns the file offset stored in the `AioCb`
-    pub fn offset(&self) -> off_t {
-        self.aiocb.0.aio_offset
-    }
-
-    /// Returns the priority of the `AioCb`
-    pub fn priority(&self) -> libc::c_int {
-        self.aiocb.0.aio_reqprio
-    }
-
-    /// Asynchronously reads from a file descriptor into a buffer
+    /// Create a new `AioRead`, placing the data in a mutable slice.
     ///
-    /// # References
+    /// # Arguments
     ///
-    /// [aio_read](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_read.html)
-    pub fn read(self: &mut Pin<Box<Self>>) -> Result<()> {
-        assert!(self.mutable, "Can't read into an immutable buffer");
-        // Safe because we don't move anything
-        let selfp = unsafe {
-            self.as_mut().get_unchecked_mut()
-        };
-        Errno::result({
-            let p: *mut libc::aiocb = &mut selfp.aiocb.0;
-            unsafe { libc::aio_read(p) }
-        }).map(|_| {
-            selfp.in_progress = true;
-        })
-    }
-
-    /// Returns the `SigEvent` stored in the `AioCb`
-    pub fn sigevent(&self) -> SigEvent {
-        SigEvent::from(&self.aiocb.0.aio_sigevent)
-    }
-
-    fn aio_return_unpinned(&mut self) -> Result<isize> {
-        unsafe {
-            let p: *mut libc::aiocb = &mut self.aiocb.0;
-            self.in_progress = false;
-            Errno::result(libc::aio_return(p))
+    /// * `fd`:           File descriptor to read from
+    /// * `offs`:         File offset
+    /// * `buf`:          A memory buffer.  It must outlive the `AioRead`.
+    /// * `prio`:         If POSIX Prioritized IO is supported, then the
+    ///                   operation will be prioritized at the process's
+    ///                   priority level minus `prio`
+    /// * `sigev_notify`: Determines how you will be notified of event
+    ///                   completion.
+    pub fn new(
+        fd: RawFd,
+        offs: off_t,
+        buf: &'a mut [u8],
+        prio: i32,
+        sigev_notify: SigevNotify,
+    ) -> Self {
+        let mut aiocb = AioCb::common_init(fd, prio, sigev_notify);
+        aiocb.aiocb.0.aio_nbytes = buf.len();
+        aiocb.aiocb.0.aio_buf = buf.as_mut_ptr() as *mut c_void;
+        aiocb.aiocb.0.aio_lio_opcode = libc::LIO_READ;
+        aiocb.aiocb.0.aio_offset = offs;
+        AioRead {
+            aiocb,
+            _data: PhantomData,
+            _pin: PhantomPinned,
         }
     }
 
-    /// Retrieve return status of an asynchronous operation.
-    ///
-    /// Should only be called once for each `AioCb`, after `AioCb::error`
-    /// indicates that it has completed.  The result is the same as for the
-    /// synchronous `read(2)`, `write(2)`, of `fsync(2)` functions.
-    ///
-    /// # References
-    ///
-    /// [aio_return](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_return.html)
-    // Note: this should be just `return`, but that's a reserved word
-    pub fn aio_return(self: &mut Pin<Box<Self>>) -> Result<isize> {
-        // Safe because aio_return_unpinned does not move the data
-        let selfp = unsafe {
-            self.as_mut().get_unchecked_mut()
-        };
-        selfp.aio_return_unpinned()
+    /// Returns the file offset of the operation.
+    pub fn offset(&self) -> off_t {
+        self.aiocb.aiocb.0.aio_offset
+    }
+}
+
+impl<'a> Aio for AioRead<'a> {
+    type Output = usize;
+
+    aio_methods!(aio_read);
+}
+
+impl<'a> AsMut<libc::aiocb> for AioRead<'a> {
+    fn as_mut(&mut self) -> &mut libc::aiocb {
+        &mut self.aiocb.aiocb.0
+    }
+}
+
+impl<'a> AsRef<libc::aiocb> for AioRead<'a> {
+    fn as_ref(&self) -> &libc::aiocb {
+        &self.aiocb.aiocb.0
+    }
+}
+
+/// Asynchronously reads from a file descriptor into a scatter/gather list of buffers.
+///
+/// # References
+///
+/// [aio_readv](https://www.freebsd.org/cgi/man.cgi?query=aio_readv)
+///
+/// # Examples
+///
+///
+#[cfg_attr(fbsd14, doc = " ```")]
+#[cfg_attr(not(fbsd14), doc = " ```no_run")]
+/// # use nix::errno::Errno;
+/// # use nix::Error;
+/// # use nix::sys::aio::*;
+/// # use nix::sys::signal::SigevNotify;
+/// # use std::{thread, time};
+/// # use std::io::{IoSliceMut, Write};
+/// # use std::os::unix::io::AsRawFd;
+/// # use tempfile::tempfile;
+/// const INITIAL: &[u8] = b"abcdef123456";
+/// let mut rbuf0 = vec![0; 4];
+/// let mut rbuf1 = vec![0; 2];
+/// let expected_len = rbuf0.len() + rbuf1.len();
+/// let mut rbufs = [IoSliceMut::new(&mut rbuf0), IoSliceMut::new(&mut rbuf1)];
+/// let mut f = tempfile().unwrap();
+/// f.write_all(INITIAL).unwrap();
+/// {
+///     let mut aior = Box::pin(
+///         AioReadv::new(
+///             f.as_raw_fd(),
+///             2,   //offset
+///             &mut rbufs,
+///             0,   //priority
+///             SigevNotify::SigevNone
+///         )
+///     );
+///     aior.as_mut().submit().unwrap();
+///     while (aior.as_mut().error() == Err(Errno::EINPROGRESS)) {
+///         thread::sleep(time::Duration::from_millis(10));
+///     }
+///     assert_eq!(aior.as_mut().aio_return().unwrap(), expected_len);
+/// }
+/// assert_eq!(rbuf0, b"cdef");
+/// assert_eq!(rbuf1, b"12");
+/// ```
+#[cfg(target_os = "freebsd")]
+#[derive(Debug)]
+#[repr(transparent)]
+pub struct AioReadv<'a> {
+    aiocb: AioCb,
+    _data: PhantomData<&'a [&'a [u8]]>,
+    _pin: PhantomPinned,
+}
+
+#[cfg(target_os = "freebsd")]
+impl<'a> AioReadv<'a> {
+    unsafe_pinned!(aiocb: AioCb);
+
+    /// Returns the number of buffers the operation will read into.
+    pub fn iovlen(&self) -> usize {
+        self.aiocb.aiocb.0.aio_nbytes
     }
 
-    /// Asynchronously writes from a buffer to a file descriptor
+    /// Create a new `AioReadv`, placing the data in a list of mutable slices.
     ///
-    /// # References
+    /// # Arguments
     ///
-    /// [aio_write](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_write.html)
-    pub fn write(self: &mut Pin<Box<Self>>) -> Result<()> {
-        // Safe because we don't move anything
-        let selfp = unsafe {
-            self.as_mut().get_unchecked_mut()
-        };
-        Errno::result({
-            let p: *mut libc::aiocb = &mut selfp.aiocb.0;
-            unsafe{ libc::aio_write(p) }
-        }).map(|_| {
-            selfp.in_progress = true;
-        })
+    /// * `fd`:           File descriptor to read from
+    /// * `offs`:         File offset
+    /// * `bufs`:         A scatter/gather list of memory buffers.  They must
+    ///                   outlive the `AioReadv`.
+    /// * `prio`:         If POSIX Prioritized IO is supported, then the
+    ///                   operation will be prioritized at the process's
+    ///                   priority level minus `prio`
+    /// * `sigev_notify`: Determines how you will be notified of event
+    ///                   completion.
+    pub fn new(
+        fd: RawFd,
+        offs: off_t,
+        bufs: &mut [IoSliceMut<'a>],
+        prio: i32,
+        sigev_notify: SigevNotify,
+    ) -> Self {
+        let mut aiocb = AioCb::common_init(fd, prio, sigev_notify);
+        // In vectored mode, aio_nbytes stores the length of the iovec array,
+        // not the byte count.
+        aiocb.aiocb.0.aio_nbytes = bufs.len();
+        aiocb.aiocb.0.aio_buf = bufs.as_mut_ptr() as *mut c_void;
+        aiocb.aiocb.0.aio_lio_opcode = libc::LIO_READV;
+        aiocb.aiocb.0.aio_offset = offs;
+        AioReadv {
+            aiocb,
+            _data: PhantomData,
+            _pin: PhantomPinned,
+        }
+    }
+
+    /// Returns the file offset of the operation.
+    pub fn offset(&self) -> off_t {
+        self.aiocb.aiocb.0.aio_offset
+    }
+}
+
+#[cfg(target_os = "freebsd")]
+impl<'a> Aio for AioReadv<'a> {
+    type Output = usize;
+
+    aio_methods!(aio_readv);
+}
+
+#[cfg(target_os = "freebsd")]
+impl<'a> AsMut<libc::aiocb> for AioReadv<'a> {
+    fn as_mut(&mut self) -> &mut libc::aiocb {
+        &mut self.aiocb.aiocb.0
+    }
+}
+
+#[cfg(target_os = "freebsd")]
+impl<'a> AsRef<libc::aiocb> for AioReadv<'a> {
+    fn as_ref(&self) -> &libc::aiocb {
+        &self.aiocb.aiocb.0
+    }
+}
+
+/// Asynchronously writes from a buffer to a file descriptor
+///
+/// # References
+///
+/// [aio_write](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_write.html)
+///
+/// # Examples
+///
+/// ```
+/// # use nix::errno::Errno;
+/// # use nix::Error;
+/// # use nix::sys::aio::*;
+/// # use nix::sys::signal::SigevNotify;
+/// # use std::{thread, time};
+/// # use std::os::unix::io::AsRawFd;
+/// # use tempfile::tempfile;
+/// const WBUF: &[u8] = b"abcdef123456";
+/// let mut f = tempfile().unwrap();
+/// let mut aiow = Box::pin(
+///     AioWrite::new(
+///         f.as_raw_fd(),
+///         2,   //offset
+///         WBUF,
+///         0,   //priority
+///         SigevNotify::SigevNone
+///     )
+/// );
+/// aiow.as_mut().submit().unwrap();
+/// while (aiow.as_mut().error() == Err(Errno::EINPROGRESS)) {
+///     thread::sleep(time::Duration::from_millis(10));
+/// }
+/// assert_eq!(aiow.as_mut().aio_return().unwrap(), WBUF.len());
+/// ```
+#[derive(Debug)]
+#[repr(transparent)]
+pub struct AioWrite<'a> {
+    aiocb: AioCb,
+    _data: PhantomData<&'a [u8]>,
+    _pin: PhantomPinned,
+}
+
+impl<'a> AioWrite<'a> {
+    unsafe_pinned!(aiocb: AioCb);
+
+    /// Returns the requested length of the aio operation in bytes
+    ///
+    /// This method returns the *requested* length of the operation.  To get the
+    /// number of bytes actually read or written by a completed operation, use
+    /// `aio_return` instead.
+    pub fn nbytes(&self) -> usize {
+        self.aiocb.aiocb.0.aio_nbytes
+    }
+
+    /// Construct a new `AioWrite`.
+    ///
+    /// # Arguments
+    ///
+    /// * `fd`:           File descriptor to write to
+    /// * `offs`:         File offset
+    /// * `buf`:          A memory buffer.  It must outlive the `AioWrite`.
+    /// * `prio`:         If POSIX Prioritized IO is supported, then the
+    ///                   operation will be prioritized at the process's
+    ///                   priority level minus `prio`
+    /// * `sigev_notify`: Determines how you will be notified of event
+    ///                   completion.
+    pub fn new(
+        fd: RawFd,
+        offs: off_t,
+        buf: &'a [u8],
+        prio: i32,
+        sigev_notify: SigevNotify,
+    ) -> Self {
+        let mut aiocb = AioCb::common_init(fd, prio, sigev_notify);
+        aiocb.aiocb.0.aio_nbytes = buf.len();
+        // casting an immutable buffer to a mutable pointer looks unsafe,
+        // but technically its only unsafe to dereference it, not to create
+        // it.  Type Safety guarantees that we'll never pass aiocb to
+        // aio_read or aio_readv.
+        aiocb.aiocb.0.aio_buf = buf.as_ptr() as *mut c_void;
+        aiocb.aiocb.0.aio_lio_opcode = libc::LIO_WRITE;
+        aiocb.aiocb.0.aio_offset = offs;
+        AioWrite {
+            aiocb,
+            _data: PhantomData,
+            _pin: PhantomPinned,
+        }
+    }
+
+    /// Returns the file offset of the operation.
+    pub fn offset(&self) -> off_t {
+        self.aiocb.aiocb.0.aio_offset
+    }
+}
+
+impl<'a> Aio for AioWrite<'a> {
+    type Output = usize;
+
+    aio_methods!(aio_write);
+}
+
+impl<'a> AsMut<libc::aiocb> for AioWrite<'a> {
+    fn as_mut(&mut self) -> &mut libc::aiocb {
+        &mut self.aiocb.aiocb.0
+    }
+}
+
+impl<'a> AsRef<libc::aiocb> for AioWrite<'a> {
+    fn as_ref(&self) -> &libc::aiocb {
+        &self.aiocb.aiocb.0
+    }
+}
+
+/// Asynchronously writes from a scatter/gather list of buffers to a file descriptor.
+///
+/// # References
+///
+/// [aio_writev](https://www.freebsd.org/cgi/man.cgi?query=aio_writev)
+///
+/// # Examples
+///
+#[cfg_attr(fbsd14, doc = " ```")]
+#[cfg_attr(not(fbsd14), doc = " ```no_run")]
+/// # use nix::errno::Errno;
+/// # use nix::Error;
+/// # use nix::sys::aio::*;
+/// # use nix::sys::signal::SigevNotify;
+/// # use std::{thread, time};
+/// # use std::io::IoSlice;
+/// # use std::os::unix::io::AsRawFd;
+/// # use tempfile::tempfile;
+/// const wbuf0: &[u8] = b"abcdef";
+/// const wbuf1: &[u8] = b"123456";
+/// let len = wbuf0.len() + wbuf1.len();
+/// let wbufs = [IoSlice::new(wbuf0), IoSlice::new(wbuf1)];
+/// let mut f = tempfile().unwrap();
+/// let mut aiow = Box::pin(
+///     AioWritev::new(
+///         f.as_raw_fd(),
+///         2,   //offset
+///         &wbufs,
+///         0,   //priority
+///         SigevNotify::SigevNone
+///     )
+/// );
+/// aiow.as_mut().submit().unwrap();
+/// while (aiow.as_mut().error() == Err(Errno::EINPROGRESS)) {
+///     thread::sleep(time::Duration::from_millis(10));
+/// }
+/// assert_eq!(aiow.as_mut().aio_return().unwrap(), len);
+/// ```
+#[cfg(target_os = "freebsd")]
+#[derive(Debug)]
+#[repr(transparent)]
+pub struct AioWritev<'a> {
+    aiocb: AioCb,
+    _data: PhantomData<&'a [&'a [u8]]>,
+    _pin: PhantomPinned,
+}
+
+#[cfg(target_os = "freebsd")]
+impl<'a> AioWritev<'a> {
+    unsafe_pinned!(aiocb: AioCb);
+
+    /// Returns the number of buffers the operation will read into.
+    pub fn iovlen(&self) -> usize {
+        self.aiocb.aiocb.0.aio_nbytes
+    }
+
+    /// Construct a new `AioWritev`.
+    ///
+    /// # Arguments
+    ///
+    /// * `fd`:           File descriptor to write to
+    /// * `offs`:         File offset
+    /// * `bufs`:         A scatter/gather list of memory buffers.  They must
+    ///                   outlive the `AioWritev`.
+    /// * `prio`:         If POSIX Prioritized IO is supported, then the
+    ///                   operation will be prioritized at the process's
+    ///                   priority level minus `prio`
+    /// * `sigev_notify`: Determines how you will be notified of event
+    ///                   completion.
+    pub fn new(
+        fd: RawFd,
+        offs: off_t,
+        bufs: &[IoSlice<'a>],
+        prio: i32,
+        sigev_notify: SigevNotify,
+    ) -> Self {
+        let mut aiocb = AioCb::common_init(fd, prio, sigev_notify);
+        // In vectored mode, aio_nbytes stores the length of the iovec array,
+        // not the byte count.
+        aiocb.aiocb.0.aio_nbytes = bufs.len();
+        // casting an immutable buffer to a mutable pointer looks unsafe,
+        // but technically its only unsafe to dereference it, not to create
+        // it.  Type Safety guarantees that we'll never pass aiocb to
+        // aio_read or aio_readv.
+        aiocb.aiocb.0.aio_buf = bufs.as_ptr() as *mut c_void;
+        aiocb.aiocb.0.aio_lio_opcode = libc::LIO_WRITEV;
+        aiocb.aiocb.0.aio_offset = offs;
+        AioWritev {
+            aiocb,
+            _data: PhantomData,
+            _pin: PhantomPinned,
+        }
+    }
+
+    /// Returns the file offset of the operation.
+    pub fn offset(&self) -> off_t {
+        self.aiocb.aiocb.0.aio_offset
+    }
+}
+
+#[cfg(target_os = "freebsd")]
+impl<'a> Aio for AioWritev<'a> {
+    type Output = usize;
+
+    aio_methods!(aio_writev);
+}
+
+#[cfg(target_os = "freebsd")]
+impl<'a> AsMut<libc::aiocb> for AioWritev<'a> {
+    fn as_mut(&mut self) -> &mut libc::aiocb {
+        &mut self.aiocb.aiocb.0
+    }
+}
+
+#[cfg(target_os = "freebsd")]
+impl<'a> AsRef<libc::aiocb> for AioWritev<'a> {
+    fn as_ref(&self) -> &libc::aiocb {
+        &self.aiocb.aiocb.0
     }
 }
 
@@ -703,38 +990,37 @@
 /// # use tempfile::tempfile;
 /// let wbuf = b"CDEF";
 /// let mut f = tempfile().unwrap();
-/// let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
+/// let mut aiocb = Box::pin(AioWrite::new(f.as_raw_fd(),
 ///     2,   //offset
 ///     &wbuf[..],
 ///     0,   //priority
-///     SigevNotify::SigevNone,
-///     LioOpcode::LIO_NOP);
-/// aiocb.write().unwrap();
+///     SigevNotify::SigevNone));
+/// aiocb.as_mut().submit().unwrap();
 /// let cs = aio_cancel_all(f.as_raw_fd()).unwrap();
 /// if cs == AioCancelStat::AioNotCanceled {
-///     while (aiocb.error() == Err(Errno::EINPROGRESS)) {
+///     while (aiocb.as_mut().error() == Err(Errno::EINPROGRESS)) {
 ///         thread::sleep(time::Duration::from_millis(10));
 ///     }
 /// }
 /// // Must call `aio_return`, but ignore the result
-/// let _ = aiocb.aio_return();
+/// let _ = aiocb.as_mut().aio_return();
 /// ```
 ///
 /// # References
 ///
 /// [`aio_cancel`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_cancel.html)
 pub fn aio_cancel_all(fd: RawFd) -> Result<AioCancelStat> {
-    match unsafe { libc::aio_cancel(fd, null_mut()) } {
+    match unsafe { libc::aio_cancel(fd, ptr::null_mut()) } {
         libc::AIO_CANCELED => Ok(AioCancelStat::AioCanceled),
         libc::AIO_NOTCANCELED => Ok(AioCancelStat::AioNotCanceled),
         libc::AIO_ALLDONE => Ok(AioCancelStat::AioAllDone),
         -1 => Err(Errno::last()),
-        _ => panic!("unknown aio_cancel return value")
+        _ => panic!("unknown aio_cancel return value"),
     }
 }
 
-/// Suspends the calling process until at least one of the specified `AioCb`s
-/// has completed, a signal is delivered, or the timeout has passed.
+/// Suspends the calling process until at least one of the specified operations
+/// have completed, a signal is delivered, or the timeout has passed.
 ///
 /// If `timeout` is `None`, `aio_suspend` will block indefinitely.
 ///
@@ -749,374 +1035,207 @@
 /// # use tempfile::tempfile;
 /// const WBUF: &[u8] = b"abcdef123456";
 /// let mut f = tempfile().unwrap();
-/// let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
+/// let mut aiocb = Box::pin(AioWrite::new(f.as_raw_fd(),
 ///     2,   //offset
 ///     WBUF,
 ///     0,   //priority
-///     SigevNotify::SigevNone,
-///     LioOpcode::LIO_NOP);
-/// aiocb.write().unwrap();
-/// aio_suspend(&[aiocb.as_ref()], None).expect("aio_suspend failed");
-/// assert_eq!(aiocb.aio_return().unwrap() as usize, WBUF.len());
+///     SigevNotify::SigevNone));
+/// aiocb.as_mut().submit().unwrap();
+/// aio_suspend(&[&*aiocb], None).expect("aio_suspend failed");
+/// assert_eq!(aiocb.as_mut().aio_return().unwrap() as usize, WBUF.len());
 /// ```
 /// # References
 ///
 /// [`aio_suspend`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_suspend.html)
-pub fn aio_suspend(list: &[Pin<&AioCb>], timeout: Option<TimeSpec>) -> Result<()> {
-    let plist = list as *const [Pin<&AioCb>] as *const [*const libc::aiocb];
-    let p = plist as *const *const libc::aiocb;
+pub fn aio_suspend(
+    list: &[&dyn AsRef<libc::aiocb>],
+    timeout: Option<TimeSpec>,
+) -> Result<()> {
+    let p = list as *const [&dyn AsRef<libc::aiocb>]
+        as *const [*const libc::aiocb] as *const *const libc::aiocb;
     let timep = match timeout {
-        None    => null::<libc::timespec>(),
-        Some(x) => x.as_ref() as *const libc::timespec
+        None => ptr::null::<libc::timespec>(),
+        Some(x) => x.as_ref() as *const libc::timespec,
     };
-    Errno::result(unsafe {
-        libc::aio_suspend(p, list.len() as i32, timep)
-    }).map(drop)
+    Errno::result(unsafe { libc::aio_suspend(p, list.len() as i32, timep) })
+        .map(drop)
 }
 
-impl<'a> Debug for AioCb<'a> {
-    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
-        fmt.debug_struct("AioCb")
-            .field("aiocb", &self.aiocb.0)
-            .field("mutable", &self.mutable)
-            .field("in_progress", &self.in_progress)
-            .finish()
-    }
-}
-
-impl<'a> Drop for AioCb<'a> {
-    /// If the `AioCb` has no remaining state in the kernel, just drop it.
-    /// Otherwise, dropping constitutes a resource leak, which is an error
-    fn drop(&mut self) {
-        assert!(thread::panicking() || !self.in_progress,
-                "Dropped an in-progress AioCb");
-    }
-}
-
-/// LIO Control Block.
+/// Submits multiple asynchronous I/O requests with a single system call.
 ///
-/// The basic structure used to issue multiple AIO operations simultaneously.
-#[cfg(not(any(target_os = "ios", target_os = "macos")))]
-pub struct LioCb<'a> {
-    /// A collection of [`AioCb`]s.  All of these will be issued simultaneously
-    /// by the [`listio`] method.
-    ///
-    /// [`AioCb`]: struct.AioCb.html
-    /// [`listio`]: #method.listio
-    // Their locations in memory must be fixed once they are passed to the
-    // kernel.  So this field must be non-public so the user can't swap.
-    aiocbs: Box<[AioCb<'a>]>,
-
-    /// The actual list passed to `libc::lio_listio`.
-    ///
-    /// It must live for as long as any of the operations are still being
-    /// processesed, because the aio subsystem uses its address as a unique
-    /// identifier.
-    list: Vec<*mut libc::aiocb>,
-
-    /// A partial set of results.  This field will get populated by
-    /// `listio_resubmit` when an `LioCb` is resubmitted after an error
-    results: Vec<Option<Result<isize>>>
+/// They are not guaranteed to complete atomically, and the order in which the
+/// requests are carried out is not specified. Reads, and writes may be freely
+/// mixed.
+///
+/// # Examples
+///
+/// Use `lio_listio` to submit an aio operation and wait for its completion. In
+/// this case, there is no need to use aio_suspend to wait or `error` to poll.
+/// This mode is useful for otherwise-synchronous programs that want to execute
+/// a handful of I/O operations in parallel.
+/// ```
+/// # use std::os::unix::io::AsRawFd;
+/// # use nix::sys::aio::*;
+/// # use nix::sys::signal::SigevNotify;
+/// # use tempfile::tempfile;
+/// const WBUF: &[u8] = b"abcdef123456";
+/// let mut f = tempfile().unwrap();
+/// let mut aiow = Box::pin(AioWrite::new(
+///     f.as_raw_fd(),
+///     2,      // offset
+///     WBUF,
+///     0,      // priority
+///     SigevNotify::SigevNone
+/// ));
+/// lio_listio(LioMode::LIO_WAIT, &mut[aiow.as_mut()], SigevNotify::SigevNone)
+///     .unwrap();
+/// // At this point, we are guaranteed that aiow is complete.
+/// assert_eq!(aiow.as_mut().aio_return().unwrap(), WBUF.len());
+/// ```
+///
+/// Use `lio_listio` to submit multiple asynchronous operations with a single
+/// syscall, but receive notification individually.  This is an efficient
+/// technique for reducing overall context-switch overhead, especially when
+/// combined with kqueue.
+/// ```
+/// # use std::os::unix::io::AsRawFd;
+/// # use std::thread;
+/// # use std::time;
+/// # use nix::errno::Errno;
+/// # use nix::sys::aio::*;
+/// # use nix::sys::signal::SigevNotify;
+/// # use tempfile::tempfile;
+/// const WBUF: &[u8] = b"abcdef123456";
+/// let mut f = tempfile().unwrap();
+/// let mut aiow = Box::pin(AioWrite::new(
+///     f.as_raw_fd(),
+///     2,      // offset
+///     WBUF,
+///     0,      // priority
+///     SigevNotify::SigevNone
+/// ));
+/// lio_listio(LioMode::LIO_NOWAIT, &mut[aiow.as_mut()], SigevNotify::SigevNone)
+///     .unwrap();
+/// // We must wait for the completion of each individual operation
+/// while (aiow.as_mut().error() == Err(Errno::EINPROGRESS)) {
+///     thread::sleep(time::Duration::from_millis(10));
+/// }
+/// assert_eq!(aiow.as_mut().aio_return().unwrap(), WBUF.len());
+/// ```
+///
+/// Use `lio_listio` to submit multiple operations, and receive notification
+/// only when all of them are complete.  This can be useful when there is some
+/// logical relationship between the operations.  But beware!  Errors or system
+/// resource limitations may cause `lio_listio` to return `EIO`, `EAGAIN`, or
+/// `EINTR`, in which case some but not all operations may have been submitted.
+/// In that case, you must check the status of each individual operation, and
+/// possibly resubmit some.
+/// ```
+/// # use libc::c_int;
+/// # use std::os::unix::io::AsRawFd;
+/// # use std::sync::atomic::{AtomicBool, Ordering};
+/// # use std::thread;
+/// # use std::time;
+/// # use lazy_static::lazy_static;
+/// # use nix::errno::Errno;
+/// # use nix::sys::aio::*;
+/// # use nix::sys::signal::*;
+/// # use tempfile::tempfile;
+/// lazy_static! {
+///     pub static ref SIGNALED: AtomicBool = AtomicBool::new(false);
+/// }
+///
+/// extern fn sigfunc(_: c_int) {
+///     SIGNALED.store(true, Ordering::Relaxed);
+/// }
+/// let sa = SigAction::new(SigHandler::Handler(sigfunc),
+///                         SaFlags::SA_RESETHAND,
+///                         SigSet::empty());
+/// SIGNALED.store(false, Ordering::Relaxed);
+/// unsafe { sigaction(Signal::SIGUSR2, &sa) }.unwrap();
+///
+/// const WBUF: &[u8] = b"abcdef123456";
+/// let mut f = tempfile().unwrap();
+/// let mut aiow = Box::pin(AioWrite::new(
+///     f.as_raw_fd(),
+///     2,      // offset
+///     WBUF,
+///     0,      // priority
+///     SigevNotify::SigevNone
+/// ));
+/// let sev = SigevNotify::SigevSignal { signal: Signal::SIGUSR2, si_value: 0 };
+/// lio_listio(LioMode::LIO_NOWAIT, &mut[aiow.as_mut()], sev).unwrap();
+/// while !SIGNALED.load(Ordering::Relaxed) {
+///     thread::sleep(time::Duration::from_millis(10));
+/// }
+/// // At this point, since `lio_listio` returned success and delivered its
+/// // notification, we know that all operations are complete.
+/// assert_eq!(aiow.as_mut().aio_return().unwrap(), WBUF.len());
+/// ```
+pub fn lio_listio(
+    mode: LioMode,
+    list: &mut [Pin<&mut dyn AsMut<libc::aiocb>>],
+    sigev_notify: SigevNotify,
+) -> Result<()> {
+    let p = list as *mut [Pin<&mut dyn AsMut<libc::aiocb>>]
+        as *mut [*mut libc::aiocb] as *mut *mut libc::aiocb;
+    let sigev = SigEvent::new(sigev_notify);
+    let sigevp = &mut sigev.sigevent() as *mut libc::sigevent;
+    Errno::result(unsafe {
+        libc::lio_listio(mode as i32, p, list.len() as i32, sigevp)
+    })
+    .map(drop)
 }
 
-/// LioCb can't automatically impl Send and Sync just because of the raw
-/// pointers in list.  But that's stupid.  There's no reason that raw pointers
-/// should automatically be non-Send
-#[cfg(not(any(target_os = "ios", target_os = "macos")))]
-unsafe impl<'a> Send for LioCb<'a> {}
-#[cfg(not(any(target_os = "ios", target_os = "macos")))]
-unsafe impl<'a> Sync for LioCb<'a> {}
-
-#[cfg(not(any(target_os = "ios", target_os = "macos")))]
-impl<'a> LioCb<'a> {
-    /// Are no [`AioCb`]s contained?
-    pub fn is_empty(&self) -> bool {
-        self.aiocbs.is_empty()
-    }
-
-    /// Return the number of individual [`AioCb`]s contained.
-    pub fn len(&self) -> usize {
-        self.aiocbs.len()
-    }
-
-    /// Submits multiple asynchronous I/O requests with a single system call.
-    ///
-    /// They are not guaranteed to complete atomically, and the order in which
-    /// the requests are carried out is not specified.  Reads, writes, and
-    /// fsyncs may be freely mixed.
-    ///
-    /// This function is useful for reducing the context-switch overhead of
-    /// submitting many AIO operations.  It can also be used with
-    /// `LioMode::LIO_WAIT` to block on the result of several independent
-    /// operations.  Used that way, it is often useful in programs that
-    /// otherwise make little use of AIO.
-    ///
-    /// # Examples
-    ///
-    /// Use `listio` to submit an aio operation and wait for its completion.  In
-    /// this case, there is no need to use [`aio_suspend`] to wait or
-    /// [`AioCb::error`] to poll.
-    ///
-    /// ```
-    /// # use nix::sys::aio::*;
-    /// # use nix::sys::signal::SigevNotify;
-    /// # use std::os::unix::io::AsRawFd;
-    /// # use tempfile::tempfile;
-    /// const WBUF: &[u8] = b"abcdef123456";
-    /// let mut f = tempfile().unwrap();
-    /// let mut liocb = LioCbBuilder::with_capacity(1)
-    ///     .emplace_slice(
-    ///         f.as_raw_fd(),
-    ///         2,   //offset
-    ///         WBUF,
-    ///         0,   //priority
-    ///         SigevNotify::SigevNone,
-    ///         LioOpcode::LIO_WRITE
-    ///     ).finish();
-    /// liocb.listio(LioMode::LIO_WAIT,
-    ///              SigevNotify::SigevNone).unwrap();
-    /// assert_eq!(liocb.aio_return(0).unwrap() as usize, WBUF.len());
-    /// ```
-    ///
-    /// # References
-    ///
-    /// [`lio_listio`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/lio_listio.html)
-    ///
-    /// [`aio_suspend`]: fn.aio_suspend.html
-    /// [`AioCb::error`]: struct.AioCb.html#method.error
-    pub fn listio(&mut self, mode: LioMode,
-                  sigev_notify: SigevNotify) -> Result<()> {
-        let sigev = SigEvent::new(sigev_notify);
-        let sigevp = &mut sigev.sigevent() as *mut libc::sigevent;
-        self.list.clear();
-        for a in &mut self.aiocbs.iter_mut() {
-            a.in_progress = true;
-            self.list.push(a as *mut AioCb<'a>
-                             as *mut libc::aiocb);
-        }
-        let p = self.list.as_ptr();
-        Errno::result(unsafe {
-            libc::lio_listio(mode as i32, p, self.list.len() as i32, sigevp)
-        }).map(drop)
-    }
-
-    /// Resubmits any incomplete operations with [`lio_listio`].
-    ///
-    /// Sometimes, due to system resource limitations, an `lio_listio` call will
-    /// return `EIO`, or `EAGAIN`.  Or, if a signal is received, it may return
-    /// `EINTR`.  In any of these cases, only a subset of its constituent
-    /// operations will actually have been initiated.  `listio_resubmit` will
-    /// resubmit any operations that are still uninitiated.
-    ///
-    /// After calling `listio_resubmit`, results should be collected by
-    /// [`LioCb::aio_return`].
-    ///
-    /// # Examples
-    /// ```no_run
-    /// # use nix::Error;
-    /// # use nix::errno::Errno;
-    /// # use nix::sys::aio::*;
-    /// # use nix::sys::signal::SigevNotify;
-    /// # use std::os::unix::io::AsRawFd;
-    /// # use std::{thread, time};
-    /// # use tempfile::tempfile;
-    /// const WBUF: &[u8] = b"abcdef123456";
-    /// let mut f = tempfile().unwrap();
-    /// let mut liocb = LioCbBuilder::with_capacity(1)
-    ///     .emplace_slice(
-    ///         f.as_raw_fd(),
-    ///         2,   //offset
-    ///         WBUF,
-    ///         0,   //priority
-    ///         SigevNotify::SigevNone,
-    ///         LioOpcode::LIO_WRITE
-    ///     ).finish();
-    /// let mut err = liocb.listio(LioMode::LIO_WAIT, SigevNotify::SigevNone);
-    /// while err == Err(Errno::EIO) ||
-    ///       err == Err(Errno::EAGAIN) {
-    ///     thread::sleep(time::Duration::from_millis(10));
-    ///     err = liocb.listio_resubmit(LioMode::LIO_WAIT, SigevNotify::SigevNone);
-    /// }
-    /// assert_eq!(liocb.aio_return(0).unwrap() as usize, WBUF.len());
-    /// ```
-    ///
-    /// # References
-    ///
-    /// [`lio_listio`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/lio_listio.html)
-    ///
-    /// [`lio_listio`]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/lio_listio.html
-    /// [`LioCb::aio_return`]: struct.LioCb.html#method.aio_return
-    // Note: the addresses of any EINPROGRESS or EOK aiocbs _must_ not be
-    // changed by this method, because the kernel relies on their addresses
-    // being stable.
-    // Note: aiocbs that are Ok(()) must be finalized by aio_return, or else the
-    // sigev_notify will immediately refire.
-    pub fn listio_resubmit(&mut self, mode:LioMode,
-                           sigev_notify: SigevNotify) -> Result<()> {
-        let sigev = SigEvent::new(sigev_notify);
-        let sigevp = &mut sigev.sigevent() as *mut libc::sigevent;
-        self.list.clear();
-
-        while self.results.len() < self.aiocbs.len() {
-            self.results.push(None);
-        }
-
-        for (i, a) in self.aiocbs.iter_mut().enumerate() {
-            if self.results[i].is_some() {
-                // Already collected final status for this operation
-                continue;
-            }
-            match a.error_unpinned() {
-                Ok(()) => {
-                    // aiocb is complete; collect its status and don't resubmit
-                    self.results[i] = Some(a.aio_return_unpinned());
-                },
-                Err(Errno::EAGAIN) => {
-                    self.list.push(a as *mut AioCb<'a> as *mut libc::aiocb);
-                },
-                Err(Errno::EINPROGRESS) => {
-                    // aiocb is was successfully queued; no need to do anything
-                },
-                Err(Errno::EINVAL) => panic!(
-                    "AioCb was never submitted, or already finalized"),
-                _ => unreachable!()
-            }
-        }
-        let p = self.list.as_ptr();
-        Errno::result(unsafe {
-            libc::lio_listio(mode as i32, p, self.list.len() as i32, sigevp)
-        }).map(drop)
-    }
-
-    /// Collect final status for an individual `AioCb` submitted as part of an
-    /// `LioCb`.
-    ///
-    /// This is just like [`AioCb::aio_return`], except it takes into account
-    /// operations that were restarted by [`LioCb::listio_resubmit`]
-    ///
-    /// [`AioCb::aio_return`]: struct.AioCb.html#method.aio_return
-    /// [`LioCb::listio_resubmit`]: #method.listio_resubmit
-    pub fn aio_return(&mut self, i: usize) -> Result<isize> {
-        if i >= self.results.len() || self.results[i].is_none() {
-            self.aiocbs[i].aio_return_unpinned()
-        } else {
-            self.results[i].unwrap()
-        }
-    }
-
-    /// Retrieve error status of an individual `AioCb` submitted as part of an
-    /// `LioCb`.
-    ///
-    /// This is just like [`AioCb::error`], except it takes into account
-    /// operations that were restarted by [`LioCb::listio_resubmit`]
-    ///
-    /// [`AioCb::error`]: struct.AioCb.html#method.error
-    /// [`LioCb::listio_resubmit`]: #method.listio_resubmit
-    pub fn error(&mut self, i: usize) -> Result<()> {
-        if i >= self.results.len() || self.results[i].is_none() {
-            self.aiocbs[i].error_unpinned()
-        } else {
-            Ok(())
-        }
-    }
-}
-
-#[cfg(not(any(target_os = "ios", target_os = "macos")))]
-impl<'a> Debug for LioCb<'a> {
-    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
-        fmt.debug_struct("LioCb")
-            .field("aiocbs", &self.aiocbs)
-            .finish()
-    }
-}
-
-/// Used to construct `LioCb`
-// This must be a separate class from LioCb due to pinning constraints.  LioCb
-// must use a boxed slice of AioCbs so they will have stable storage, but
-// LioCbBuilder must use a Vec to make construction possible when the final size
-// is unknown.
-#[cfg(not(any(target_os = "ios", target_os = "macos")))]
-#[derive(Debug)]
-pub struct LioCbBuilder<'a> {
-    /// A collection of [`AioCb`]s.
-    ///
-    /// [`AioCb`]: struct.AioCb.html
-    pub aiocbs: Vec<AioCb<'a>>,
-}
-
-#[cfg(not(any(target_os = "ios", target_os = "macos")))]
-impl<'a> LioCbBuilder<'a> {
-    /// Initialize an empty `LioCb`
-    pub fn with_capacity(capacity: usize) -> LioCbBuilder<'a> {
-        LioCbBuilder {
-            aiocbs: Vec::with_capacity(capacity),
-        }
-    }
-
-    /// Add a new operation on an immutable slice to the [`LioCb`] under
-    /// construction.
-    ///
-    /// Arguments are the same as for [`AioCb::from_slice`]
-    ///
-    /// [`LioCb`]: struct.LioCb.html
-    /// [`AioCb::from_slice`]: struct.AioCb.html#method.from_slice
-    pub fn emplace_slice(mut self, fd: RawFd, offs: off_t, buf: &'a [u8],
-                         prio: libc::c_int, sigev_notify: SigevNotify,
-                         opcode: LioOpcode) -> Self
-    {
-        self.aiocbs.push(AioCb::from_slice_unpinned(fd, offs, buf, prio,
-                                                    sigev_notify, opcode));
-        self
-    }
-
-    /// Add a new operation on a mutable slice to the [`LioCb`] under
-    /// construction.
-    ///
-    /// Arguments are the same as for [`AioCb::from_mut_slice`]
-    ///
-    /// [`LioCb`]: struct.LioCb.html
-    /// [`AioCb::from_mut_slice`]: struct.AioCb.html#method.from_mut_slice
-    pub fn emplace_mut_slice(mut self, fd: RawFd, offs: off_t,
-                             buf: &'a mut [u8], prio: libc::c_int,
-                             sigev_notify: SigevNotify, opcode: LioOpcode)
-        -> Self
-    {
-        self.aiocbs.push(AioCb::from_mut_slice_unpinned(fd, offs, buf, prio,
-                                                        sigev_notify, opcode));
-        self
-    }
-
-    /// Finalize this [`LioCb`].
-    ///
-    /// Afterwards it will be possible to issue the operations with
-    /// [`LioCb::listio`].  Conversely, it will no longer be possible to add new
-    /// operations with [`LioCbBuilder::emplace_slice`] or
-    /// [`LioCbBuilder::emplace_mut_slice`].
-    ///
-    /// [`LioCb::listio`]: struct.LioCb.html#method.listio
-    /// [`LioCb::from_mut_slice`]: struct.LioCb.html#method.from_mut_slice
-    /// [`LioCb::from_slice`]: struct.LioCb.html#method.from_slice
-    pub fn finish(self) -> LioCb<'a> {
-        let len = self.aiocbs.len();
-        LioCb {
-            aiocbs: self.aiocbs.into(),
-            list: Vec::with_capacity(len),
-            results: Vec::with_capacity(len)
-        }
-    }
-}
-
-#[cfg(not(any(target_os = "ios", target_os = "macos")))]
 #[cfg(test)]
 mod t {
     use super::*;
 
-    // It's important that `LioCb` be `UnPin`.  The tokio-file crate relies on
-    // it.
+    /// aio_suspend relies on casting Rust Aio* struct pointers to libc::aiocb
+    /// pointers.  This test ensures that such casts are valid.
     #[test]
-    fn liocb_is_unpin() {
-        use assert_impl::assert_impl;
+    fn casting() {
+        let sev = SigevNotify::SigevNone;
+        let aiof = AioFsync::new(666, AioFsyncMode::O_SYNC, 0, sev);
+        assert_eq!(
+            aiof.as_ref() as *const libc::aiocb,
+            &aiof as *const AioFsync as *const libc::aiocb
+        );
 
-        assert_impl!(Unpin: LioCb);
+        let mut rbuf = [];
+        let aior = AioRead::new(666, 0, &mut rbuf, 0, sev);
+        assert_eq!(
+            aior.as_ref() as *const libc::aiocb,
+            &aior as *const AioRead as *const libc::aiocb
+        );
+
+        let wbuf = [];
+        let aiow = AioWrite::new(666, 0, &wbuf, 0, sev);
+        assert_eq!(
+            aiow.as_ref() as *const libc::aiocb,
+            &aiow as *const AioWrite as *const libc::aiocb
+        );
+    }
+
+    #[cfg(target_os = "freebsd")]
+    #[test]
+    fn casting_vectored() {
+        let sev = SigevNotify::SigevNone;
+
+        let mut rbuf = [];
+        let mut rbufs = [IoSliceMut::new(&mut rbuf)];
+        let aiorv = AioReadv::new(666, 0, &mut rbufs[..], 0, sev);
+        assert_eq!(
+            aiorv.as_ref() as *const libc::aiocb,
+            &aiorv as *const AioReadv as *const libc::aiocb
+        );
+
+        let wbuf = [];
+        let wbufs = [IoSlice::new(&wbuf)];
+        let aiowv = AioWritev::new(666, 0, &wbufs, 0, sev);
+        assert_eq!(
+            aiowv.as_ref() as *const libc::aiocb,
+            &aiowv as *const AioWritev as *const libc::aiocb
+        );
     }
 }
diff --git a/src/sys/epoll.rs b/src/sys/epoll.rs
index 6bc2a25..58def2e 100644
--- a/src/sys/epoll.rs
+++ b/src/sys/epoll.rs
@@ -1,9 +1,9 @@
-use crate::Result;
 use crate::errno::Errno;
+use crate::Result;
 use libc::{self, c_int};
+use std::mem;
 use std::os::unix::io::RawFd;
 use std::ptr;
-use std::mem;
 
 libc_bitflags!(
     pub struct EpollFlags: c_int {
@@ -18,7 +18,6 @@
         EPOLLERR;
         EPOLLHUP;
         EPOLLRDHUP;
-        #[cfg(target_os = "linux")]  // Added in 4.5; not in Android.
         EPOLLEXCLUSIVE;
         #[cfg(not(target_arch = "mips"))]
         EPOLLWAKEUP;
@@ -36,7 +35,7 @@
     EpollCtlMod = libc::EPOLL_CTL_MOD,
 }
 
-libc_bitflags!{
+libc_bitflags! {
     pub struct EpollCreateFlags: c_int {
         EPOLL_CLOEXEC;
     }
@@ -50,7 +49,12 @@
 
 impl EpollEvent {
     pub fn new(events: EpollFlags, data: u64) -> Self {
-        EpollEvent { event: libc::epoll_event { events: events.bits() as u32, u64: data } }
+        EpollEvent {
+            event: libc::epoll_event {
+                events: events.bits() as u32,
+                u64: data,
+            },
+        }
     }
 
     pub fn empty() -> Self {
@@ -81,8 +85,14 @@
 }
 
 #[inline]
-pub fn epoll_ctl<'a, T>(epfd: RawFd, op: EpollOp, fd: RawFd, event: T) -> Result<()>
-    where T: Into<Option<&'a mut EpollEvent>>
+pub fn epoll_ctl<'a, T>(
+    epfd: RawFd,
+    op: EpollOp,
+    fd: RawFd,
+    event: T,
+) -> Result<()>
+where
+    T: Into<Option<&'a mut EpollEvent>>,
 {
     let mut event: Option<&mut EpollEvent> = event.into();
     if event.is_none() && op != EpollOp::EpollCtlDel {
@@ -100,9 +110,18 @@
 }
 
 #[inline]
-pub fn epoll_wait(epfd: RawFd, events: &mut [EpollEvent], timeout_ms: isize) -> Result<usize> {
+pub fn epoll_wait(
+    epfd: RawFd,
+    events: &mut [EpollEvent],
+    timeout_ms: isize,
+) -> Result<usize> {
     let res = unsafe {
-        libc::epoll_wait(epfd, events.as_mut_ptr() as *mut libc::epoll_event, events.len() as c_int, timeout_ms as c_int)
+        libc::epoll_wait(
+            epfd,
+            events.as_mut_ptr() as *mut libc::epoll_event,
+            events.len() as c_int,
+            timeout_ms as c_int,
+        )
     };
 
     Errno::result(res).map(|r| r as usize)
diff --git a/src/sys/event.rs b/src/sys/event.rs
index c648f5e..d8ad628 100644
--- a/src/sys/event.rs
+++ b/src/sys/event.rs
@@ -3,10 +3,11 @@
 
 use crate::{Errno, Result};
 #[cfg(not(target_os = "netbsd"))]
-use libc::{timespec, time_t, c_int, c_long, intptr_t, uintptr_t};
+use libc::{c_int, c_long, intptr_t, time_t, timespec, uintptr_t};
 #[cfg(target_os = "netbsd")]
-use libc::{timespec, time_t, c_long, intptr_t, uintptr_t, size_t};
+use libc::{c_long, intptr_t, size_t, time_t, timespec, uintptr_t};
 use std::convert::TryInto;
+use std::mem;
 use std::os::unix::io::RawFd;
 use std::ptr;
 
@@ -17,17 +18,16 @@
     kevent: libc::kevent,
 }
 
-#[cfg(any(target_os = "dragonfly", target_os = "freebsd",
-          target_os = "ios", target_os = "macos",
-          target_os = "openbsd"))]
+#[cfg(any(
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "ios",
+    target_os = "macos",
+    target_os = "openbsd"
+))]
 type type_of_udata = *mut libc::c_void;
-#[cfg(any(target_os = "dragonfly", target_os = "freebsd",
-          target_os = "ios", target_os = "macos"))]
-type type_of_data = intptr_t;
 #[cfg(any(target_os = "netbsd"))]
 type type_of_udata = intptr_t;
-#[cfg(any(target_os = "netbsd", target_os = "openbsd"))]
-type type_of_data = i64;
 
 #[cfg(target_os = "netbsd")]
 type type_of_event_filter = u32;
@@ -79,13 +79,17 @@
     impl TryFrom<type_of_event_filter>
 }
 
-#[cfg(any(target_os = "dragonfly", target_os = "freebsd",
-          target_os = "ios", target_os = "macos",
-          target_os = "openbsd"))]
+#[cfg(any(
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "ios",
+    target_os = "macos",
+    target_os = "openbsd"
+))]
 pub type type_of_event_flag = u16;
 #[cfg(any(target_os = "netbsd"))]
 pub type type_of_event_flag = u32;
-libc_bitflags!{
+libc_bitflags! {
     pub struct EventFlag: type_of_event_flag {
         EV_ADD;
         EV_CLEAR;
@@ -114,7 +118,6 @@
                   target_os = "ios", target_os = "macos",
                   target_os = "netbsd", target_os = "openbsd"))]
         EV_RECEIPT;
-        EV_SYSFLAGS;
     }
 }
 
@@ -210,24 +213,33 @@
     Errno::result(res)
 }
 
-
 // KEvent can't derive Send because on some operating systems, udata is defined
 // as a void*.  However, KEvent's public API always treats udata as an intptr_t,
 // which is safe to Send.
-unsafe impl Send for KEvent {
-}
+unsafe impl Send for KEvent {}
 
 impl KEvent {
-    pub fn new(ident: uintptr_t, filter: EventFilter, flags: EventFlag,
-               fflags:FilterFlag, data: intptr_t, udata: intptr_t) -> KEvent {
-        KEvent { kevent: libc::kevent {
-            ident,
-            filter: filter as type_of_event_filter,
-            flags: flags.bits(),
-            fflags: fflags.bits(),
-            data: data as type_of_data,
-            udata: udata as type_of_udata
-        } }
+    #[allow(clippy::needless_update)] // Not needless on all platforms.
+    pub fn new(
+        ident: uintptr_t,
+        filter: EventFilter,
+        flags: EventFlag,
+        fflags: FilterFlag,
+        data: intptr_t,
+        udata: intptr_t,
+    ) -> KEvent {
+        KEvent {
+            kevent: libc::kevent {
+                ident,
+                filter: filter as type_of_event_filter,
+                flags: flags.bits(),
+                fflags: fflags.bits(),
+                // data can be either i64 or intptr_t, depending on platform
+                data: data as _,
+                udata: udata as type_of_udata,
+                ..unsafe { mem::zeroed() }
+            },
+        }
     }
 
     pub fn ident(&self) -> uintptr_t {
@@ -255,34 +267,38 @@
     }
 }
 
-pub fn kevent(kq: RawFd,
-              changelist: &[KEvent],
-              eventlist: &mut [KEvent],
-              timeout_ms: usize) -> Result<usize> {
-
+pub fn kevent(
+    kq: RawFd,
+    changelist: &[KEvent],
+    eventlist: &mut [KEvent],
+    timeout_ms: usize,
+) -> Result<usize> {
     // Convert ms to timespec
     let timeout = timespec {
         tv_sec: (timeout_ms / 1000) as time_t,
-        tv_nsec: ((timeout_ms % 1000) * 1_000_000) as c_long
+        tv_nsec: ((timeout_ms % 1000) * 1_000_000) as c_long,
     };
 
     kevent_ts(kq, changelist, eventlist, Some(timeout))
 }
 
-#[cfg(any(target_os = "macos",
-          target_os = "ios",
-          target_os = "freebsd",
-          target_os = "dragonfly",
-          target_os = "openbsd"))]
+#[cfg(any(
+    target_os = "macos",
+    target_os = "ios",
+    target_os = "freebsd",
+    target_os = "dragonfly",
+    target_os = "openbsd"
+))]
 type type_of_nchanges = c_int;
 #[cfg(target_os = "netbsd")]
 type type_of_nchanges = size_t;
 
-pub fn kevent_ts(kq: RawFd,
-              changelist: &[KEvent],
-              eventlist: &mut [KEvent],
-              timeout_opt: Option<timespec>) -> Result<usize> {
-
+pub fn kevent_ts(
+    kq: RawFd,
+    changelist: &[KEvent],
+    eventlist: &mut [KEvent],
+    timeout_opt: Option<timespec>,
+) -> Result<usize> {
     let res = unsafe {
         libc::kevent(
             kq,
@@ -290,59 +306,69 @@
             changelist.len() as type_of_nchanges,
             eventlist.as_mut_ptr() as *mut libc::kevent,
             eventlist.len() as type_of_nchanges,
-            if let Some(ref timeout) = timeout_opt {timeout as *const timespec} else {ptr::null()})
+            if let Some(ref timeout) = timeout_opt {
+                timeout as *const timespec
+            } else {
+                ptr::null()
+            },
+        )
     };
 
     Errno::result(res).map(|r| r as usize)
 }
 
 #[inline]
-pub fn ev_set(ev: &mut KEvent,
-              ident: usize,
-              filter: EventFilter,
-              flags: EventFlag,
-              fflags: FilterFlag,
-              udata: intptr_t) {
-
-    ev.kevent.ident  = ident as uintptr_t;
+pub fn ev_set(
+    ev: &mut KEvent,
+    ident: usize,
+    filter: EventFilter,
+    flags: EventFlag,
+    fflags: FilterFlag,
+    udata: intptr_t,
+) {
+    ev.kevent.ident = ident as uintptr_t;
     ev.kevent.filter = filter as type_of_event_filter;
-    ev.kevent.flags  = flags.bits();
+    ev.kevent.flags = flags.bits();
     ev.kevent.fflags = fflags.bits();
-    ev.kevent.data   = 0;
-    ev.kevent.udata  = udata as type_of_udata;
+    ev.kevent.data = 0;
+    ev.kevent.udata = udata as type_of_udata;
 }
 
 #[test]
 fn test_struct_kevent() {
     use std::mem;
 
-    let udata : intptr_t = 12345;
+    let udata: intptr_t = 12345;
 
-    let actual = KEvent::new(0xdead_beef,
-                             EventFilter::EVFILT_READ,
-                             EventFlag::EV_ONESHOT | EventFlag::EV_ADD,
-                             FilterFlag::NOTE_CHILD | FilterFlag::NOTE_EXIT,
-                             0x1337,
-                             udata);
+    let actual = KEvent::new(
+        0xdead_beef,
+        EventFilter::EVFILT_READ,
+        EventFlag::EV_ONESHOT | EventFlag::EV_ADD,
+        FilterFlag::NOTE_CHILD | FilterFlag::NOTE_EXIT,
+        0x1337,
+        udata,
+    );
     assert_eq!(0xdead_beef, actual.ident());
     let filter = actual.kevent.filter;
     assert_eq!(libc::EVFILT_READ, filter);
     assert_eq!(libc::EV_ONESHOT | libc::EV_ADD, actual.flags().bits());
     assert_eq!(libc::NOTE_CHILD | libc::NOTE_EXIT, actual.fflags().bits());
-    assert_eq!(0x1337, actual.data() as type_of_data);
+    assert_eq!(0x1337, actual.data());
     assert_eq!(udata as type_of_udata, actual.udata() as type_of_udata);
     assert_eq!(mem::size_of::<libc::kevent>(), mem::size_of::<KEvent>());
 }
 
 #[test]
 fn test_kevent_filter() {
-    let udata : intptr_t = 12345;
+    let udata: intptr_t = 12345;
 
-    let actual = KEvent::new(0xdead_beef,
-                             EventFilter::EVFILT_READ,
-                             EventFlag::EV_ONESHOT | EventFlag::EV_ADD,
-                             FilterFlag::NOTE_CHILD | FilterFlag::NOTE_EXIT,
-                             0x1337,
-                             udata);
+    let actual = KEvent::new(
+        0xdead_beef,
+        EventFilter::EVFILT_READ,
+        EventFlag::EV_ONESHOT | EventFlag::EV_ADD,
+        FilterFlag::NOTE_CHILD | FilterFlag::NOTE_EXIT,
+        0x1337,
+        udata,
+    );
     assert_eq!(EventFilter::EVFILT_READ, actual.filter().unwrap());
 }
diff --git a/src/sys/eventfd.rs b/src/sys/eventfd.rs
index c54f952..cd90672 100644
--- a/src/sys/eventfd.rs
+++ b/src/sys/eventfd.rs
@@ -1,6 +1,6 @@
-use std::os::unix::io::RawFd;
-use crate::Result;
 use crate::errno::Errno;
+use crate::Result;
+use std::os::unix::io::RawFd;
 
 libc_bitflags! {
     pub struct EfdFlags: libc::c_int {
diff --git a/src/sys/inotify.rs b/src/sys/inotify.rs
index 3f5ae22..84356ec 100644
--- a/src/sys/inotify.rs
+++ b/src/sys/inotify.rs
@@ -23,48 +23,68 @@
 //! }
 //! ```
 
-use libc::{
-    c_char,
-    c_int,
-};
-use std::ffi::{OsString,OsStr,CStr};
-use std::os::unix::ffi::OsStrExt;
-use std::mem::{MaybeUninit, size_of};
-use std::os::unix::io::{RawFd,AsRawFd,FromRawFd};
-use std::ptr;
-use crate::unistd::read;
-use crate::Result;
-use crate::NixPath;
 use crate::errno::Errno;
+use crate::unistd::read;
+use crate::NixPath;
+use crate::Result;
+use cfg_if::cfg_if;
+use libc::{c_char, c_int};
+use std::ffi::{CStr, OsStr, OsString};
+use std::mem::{size_of, MaybeUninit};
+use std::os::unix::ffi::OsStrExt;
+use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
+use std::ptr;
 
 libc_bitflags! {
     /// Configuration options for [`inotify_add_watch`](fn.inotify_add_watch.html).
     pub struct AddWatchFlags: u32 {
+        /// File was accessed.
         IN_ACCESS;
+        /// File was modified.
         IN_MODIFY;
+        /// Metadata changed.
         IN_ATTRIB;
+        /// Writable file was closed.
         IN_CLOSE_WRITE;
+        /// Nonwritable file was closed.
         IN_CLOSE_NOWRITE;
+        /// File was opened.
         IN_OPEN;
+        /// File was moved from X.
         IN_MOVED_FROM;
+        /// File was moved to Y.
         IN_MOVED_TO;
+        /// Subfile was created.
         IN_CREATE;
+        /// Subfile was deleted.
         IN_DELETE;
+        /// Self was deleted.
         IN_DELETE_SELF;
+        /// Self was moved.
         IN_MOVE_SELF;
 
+        /// Backing filesystem was unmounted.
         IN_UNMOUNT;
+        /// Event queue overflowed.
         IN_Q_OVERFLOW;
+        /// File was ignored.
         IN_IGNORED;
 
+        /// Combination of `IN_CLOSE_WRITE` and `IN_CLOSE_NOWRITE`.
         IN_CLOSE;
+        /// Combination of `IN_MOVED_FROM` and `IN_MOVED_TO`.
         IN_MOVE;
 
+        /// Only watch the path if it is a directory.
         IN_ONLYDIR;
+        /// Don't follow symlinks.
         IN_DONT_FOLLOW;
 
+        /// Event occurred against directory.
         IN_ISDIR;
+        /// Only send event once.
         IN_ONESHOT;
+        /// All of the events.
         IN_ALL_EVENTS;
     }
 }
@@ -72,7 +92,9 @@
 libc_bitflags! {
     /// Configuration options for [`inotify_init1`](fn.inotify_init1.html).
     pub struct InitFlags: c_int {
+        /// Set the `FD_CLOEXEC` flag on the file descriptor.
         IN_CLOEXEC;
+        /// Set the `O_NONBLOCK` flag on the open file description referred to by the new file descriptor.
         IN_NONBLOCK;
     }
 }
@@ -81,7 +103,7 @@
 /// other interfaces consuming file descriptors, epoll for example.
 #[derive(Debug, Clone, Copy)]
 pub struct Inotify {
-    fd: RawFd
+    fd: RawFd,
 }
 
 /// This object is returned when you create a new watch on an inotify instance.
@@ -89,7 +111,7 @@
 /// know which watch triggered which event.
 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd)]
 pub struct WatchDescriptor {
-    wd: i32
+    wd: i32,
 }
 
 /// A single inotify event.
@@ -109,7 +131,7 @@
     pub cookie: u32,
     /// Filename. This field exists only if the event was triggered for a file
     /// inside the watched directory.
-    pub name: Option<OsString>
+    pub name: Option<OsString>,
 }
 
 impl Inotify {
@@ -119,9 +141,7 @@
     ///
     /// For more information see, [inotify_init(2)](https://man7.org/linux/man-pages/man2/inotify_init.2.html).
     pub fn init(flags: InitFlags) -> Result<Inotify> {
-        let res = Errno::result(unsafe {
-            libc::inotify_init1(flags.bits())
-        });
+        let res = Errno::result(unsafe { libc::inotify_init1(flags.bits()) });
 
         res.map(|fd| Inotify { fd })
     }
@@ -131,15 +151,13 @@
     /// Returns a watch descriptor. This is not a File Descriptor!
     ///
     /// For more information see, [inotify_add_watch(2)](https://man7.org/linux/man-pages/man2/inotify_add_watch.2.html).
-    pub fn add_watch<P: ?Sized + NixPath>(self,
-                                          path: &P,
-                                          mask: AddWatchFlags)
-                                            -> Result<WatchDescriptor>
-    {
-        let res = path.with_nix_path(|cstr| {
-            unsafe {
-                libc::inotify_add_watch(self.fd, cstr.as_ptr(), mask.bits())
-            }
+    pub fn add_watch<P: ?Sized + NixPath>(
+        self,
+        path: &P,
+        mask: AddWatchFlags,
+    ) -> Result<WatchDescriptor> {
+        let res = path.with_nix_path(|cstr| unsafe {
+            libc::inotify_add_watch(self.fd, cstr.as_ptr(), mask.bits())
         })?;
 
         Errno::result(res).map(|wd| WatchDescriptor { wd })
@@ -151,16 +169,15 @@
     /// Returns an EINVAL error if the watch descriptor is invalid.
     ///
     /// For more information see, [inotify_rm_watch(2)](https://man7.org/linux/man-pages/man2/inotify_rm_watch.2.html).
-    #[cfg(target_os = "linux")]
     pub fn rm_watch(self, wd: WatchDescriptor) -> Result<()> {
-        let res = unsafe { libc::inotify_rm_watch(self.fd, wd.wd) };
-
-        Errno::result(res).map(drop)
-    }
-
-    #[cfg(target_os = "android")]
-    pub fn rm_watch(self, wd: WatchDescriptor) -> Result<()> {
-        let res = unsafe { libc::inotify_rm_watch(self.fd, wd.wd as u32) };
+        cfg_if! {
+            if #[cfg(target_os = "linux")] {
+                let arg = wd.wd;
+            } else if #[cfg(target_os = "android")] {
+                let arg = wd.wd as u32;
+            }
+        }
+        let res = unsafe { libc::inotify_rm_watch(self.fd, arg) };
 
         Errno::result(res).map(drop)
     }
@@ -186,7 +203,7 @@
                 ptr::copy_nonoverlapping(
                     buffer.as_ptr().add(offset),
                     event.as_mut_ptr() as *mut u8,
-                    (BUFSIZ - offset).min(header_size)
+                    (BUFSIZ - offset).min(header_size),
                 );
                 event.assume_init()
             };
@@ -195,9 +212,7 @@
                 0 => None,
                 _ => {
                     let ptr = unsafe {
-                        buffer
-                            .as_ptr()
-                            .add(offset + header_size)
+                        buffer.as_ptr().add(offset + header_size)
                             as *const c_char
                     };
                     let cstr = unsafe { CStr::from_ptr(ptr) };
@@ -210,7 +225,7 @@
                 wd: WatchDescriptor { wd: event.wd },
                 mask: AddWatchFlags::from_bits_truncate(event.mask),
                 cookie: event.cookie,
-                name
+                name,
             });
 
             offset += header_size + event.len as usize;
diff --git a/src/sys/ioctl/bsd.rs b/src/sys/ioctl/bsd.rs
index 4ce4d33..307994c 100644
--- a/src/sys/ioctl/bsd.rs
+++ b/src/sys/ioctl/bsd.rs
@@ -21,7 +21,7 @@
     #[allow(overflowing_literals)]
     pub const IN: ioctl_num_type = 0x8000_0000;
     #[doc(hidden)]
-    pub const INOUT: ioctl_num_type = IN|OUT;
+    pub const INOUT: ioctl_num_type = IN | OUT;
     #[doc(hidden)]
     pub const IOCPARM_MASK: ioctl_num_type = 0x1fff;
 }
@@ -31,9 +31,14 @@
 #[macro_export]
 #[doc(hidden)]
 macro_rules! ioc {
-    ($inout:expr, $group:expr, $num:expr, $len:expr) => (
-        $inout | (($len as $crate::sys::ioctl::ioctl_num_type & $crate::sys::ioctl::IOCPARM_MASK) << 16) | (($group as $crate::sys::ioctl::ioctl_num_type) << 8) | ($num as $crate::sys::ioctl::ioctl_num_type)
-    )
+    ($inout:expr, $group:expr, $num:expr, $len:expr) => {
+        $inout
+            | (($len as $crate::sys::ioctl::ioctl_num_type
+                & $crate::sys::ioctl::IOCPARM_MASK)
+                << 16)
+            | (($group as $crate::sys::ioctl::ioctl_num_type) << 8)
+            | ($num as $crate::sys::ioctl::ioctl_num_type)
+    };
 }
 
 /// Generate an ioctl request code for a command that passes no data.
@@ -53,7 +58,9 @@
 /// ```
 #[macro_export(local_inner_macros)]
 macro_rules! request_code_none {
-    ($g:expr, $n:expr) => (ioc!($crate::sys::ioctl::VOID, $g, $n, 0))
+    ($g:expr, $n:expr) => {
+        ioc!($crate::sys::ioctl::VOID, $g, $n, 0)
+    };
 }
 
 /// Generate an ioctl request code for a command that passes an integer
@@ -64,7 +71,14 @@
 /// with is "bad" and you cannot use `ioctl_write_int!()` directly.
 #[macro_export(local_inner_macros)]
 macro_rules! request_code_write_int {
-    ($g:expr, $n:expr) => (ioc!($crate::sys::ioctl::VOID, $g, $n, ::std::mem::size_of::<$crate::libc::c_int>()))
+    ($g:expr, $n:expr) => {
+        ioc!(
+            $crate::sys::ioctl::VOID,
+            $g,
+            $n,
+            ::std::mem::size_of::<$crate::libc::c_int>()
+        )
+    };
 }
 
 /// Generate an ioctl request code for a command that reads.
@@ -79,7 +93,9 @@
 /// writing.
 #[macro_export(local_inner_macros)]
 macro_rules! request_code_read {
-    ($g:expr, $n:expr, $len:expr) => (ioc!($crate::sys::ioctl::OUT, $g, $n, $len))
+    ($g:expr, $n:expr, $len:expr) => {
+        ioc!($crate::sys::ioctl::OUT, $g, $n, $len)
+    };
 }
 
 /// Generate an ioctl request code for a command that writes.
@@ -94,7 +110,9 @@
 /// reading.
 #[macro_export(local_inner_macros)]
 macro_rules! request_code_write {
-    ($g:expr, $n:expr, $len:expr) => (ioc!($crate::sys::ioctl::IN, $g, $n, $len))
+    ($g:expr, $n:expr, $len:expr) => {
+        ioc!($crate::sys::ioctl::IN, $g, $n, $len)
+    };
 }
 
 /// Generate an ioctl request code for a command that reads and writes.
@@ -105,5 +123,7 @@
 /// with is "bad" and you cannot use `ioctl_readwrite!()` directly.
 #[macro_export(local_inner_macros)]
 macro_rules! request_code_readwrite {
-    ($g:expr, $n:expr, $len:expr) => (ioc!($crate::sys::ioctl::INOUT, $g, $n, $len))
+    ($g:expr, $n:expr, $len:expr) => {
+        ioc!($crate::sys::ioctl::INOUT, $g, $n, $len)
+    };
 }
diff --git a/src/sys/ioctl/linux.rs b/src/sys/ioctl/linux.rs
index 68ebaba..0c0a209 100644
--- a/src/sys/ioctl/linux.rs
+++ b/src/sys/ioctl/linux.rs
@@ -14,7 +14,13 @@
 #[doc(hidden)]
 pub const TYPEBITS: ioctl_num_type = 8;
 
-#[cfg(any(target_arch = "mips", target_arch = "mips64", target_arch = "powerpc", target_arch = "powerpc64", target_arch = "sparc64"))]
+#[cfg(any(
+    target_arch = "mips",
+    target_arch = "mips64",
+    target_arch = "powerpc",
+    target_arch = "powerpc64",
+    target_arch = "sparc64"
+))]
 mod consts {
     #[doc(hidden)]
     pub const NONE: u8 = 1;
@@ -29,12 +35,15 @@
 }
 
 // "Generic" ioctl protocol
-#[cfg(any(target_arch = "x86",
-          target_arch = "arm",
-          target_arch = "s390x",
-          target_arch = "x86_64",
-          target_arch = "aarch64",
-          target_arch = "riscv64"))]
+#[cfg(any(
+    target_arch = "x86",
+    target_arch = "arm",
+    target_arch = "s390x",
+    target_arch = "x86_64",
+    target_arch = "aarch64",
+    target_arch = "riscv32",
+    target_arch = "riscv64"
+))]
 mod consts {
     #[doc(hidden)]
     pub const NONE: u8 = 0;
@@ -72,11 +81,20 @@
 #[macro_export]
 #[doc(hidden)]
 macro_rules! ioc {
-    ($dir:expr, $ty:expr, $nr:expr, $sz:expr) => (
-        (($dir as $crate::sys::ioctl::ioctl_num_type & $crate::sys::ioctl::DIRMASK) << $crate::sys::ioctl::DIRSHIFT) |
-        (($ty as $crate::sys::ioctl::ioctl_num_type & $crate::sys::ioctl::TYPEMASK) << $crate::sys::ioctl::TYPESHIFT) |
-        (($nr as $crate::sys::ioctl::ioctl_num_type & $crate::sys::ioctl::NRMASK) << $crate::sys::ioctl::NRSHIFT) |
-        (($sz as $crate::sys::ioctl::ioctl_num_type & $crate::sys::ioctl::SIZEMASK) << $crate::sys::ioctl::SIZESHIFT))
+    ($dir:expr, $ty:expr, $nr:expr, $sz:expr) => {
+        (($dir as $crate::sys::ioctl::ioctl_num_type
+            & $crate::sys::ioctl::DIRMASK)
+            << $crate::sys::ioctl::DIRSHIFT)
+            | (($ty as $crate::sys::ioctl::ioctl_num_type
+                & $crate::sys::ioctl::TYPEMASK)
+                << $crate::sys::ioctl::TYPESHIFT)
+            | (($nr as $crate::sys::ioctl::ioctl_num_type
+                & $crate::sys::ioctl::NRMASK)
+                << $crate::sys::ioctl::NRSHIFT)
+            | (($sz as $crate::sys::ioctl::ioctl_num_type
+                & $crate::sys::ioctl::SIZEMASK)
+                << $crate::sys::ioctl::SIZESHIFT)
+    };
 }
 
 /// Generate an ioctl request code for a command that passes no data.
@@ -96,7 +114,9 @@
 /// ```
 #[macro_export(local_inner_macros)]
 macro_rules! request_code_none {
-    ($ty:expr, $nr:expr) => (ioc!($crate::sys::ioctl::NONE, $ty, $nr, 0))
+    ($ty:expr, $nr:expr) => {
+        ioc!($crate::sys::ioctl::NONE, $ty, $nr, 0)
+    };
 }
 
 /// Generate an ioctl request code for a command that reads.
@@ -111,7 +131,9 @@
 /// writing.
 #[macro_export(local_inner_macros)]
 macro_rules! request_code_read {
-    ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::sys::ioctl::READ, $ty, $nr, $sz))
+    ($ty:expr, $nr:expr, $sz:expr) => {
+        ioc!($crate::sys::ioctl::READ, $ty, $nr, $sz)
+    };
 }
 
 /// Generate an ioctl request code for a command that writes.
@@ -126,7 +148,9 @@
 /// reading.
 #[macro_export(local_inner_macros)]
 macro_rules! request_code_write {
-    ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::sys::ioctl::WRITE, $ty, $nr, $sz))
+    ($ty:expr, $nr:expr, $sz:expr) => {
+        ioc!($crate::sys::ioctl::WRITE, $ty, $nr, $sz)
+    };
 }
 
 /// Generate an ioctl request code for a command that reads and writes.
@@ -137,5 +161,12 @@
 /// with is "bad" and you cannot use `ioctl_readwrite!()` directly.
 #[macro_export(local_inner_macros)]
 macro_rules! request_code_readwrite {
-    ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::sys::ioctl::READ | $crate::sys::ioctl::WRITE, $ty, $nr, $sz))
+    ($ty:expr, $nr:expr, $sz:expr) => {
+        ioc!(
+            $crate::sys::ioctl::READ | $crate::sys::ioctl::WRITE,
+            $ty,
+            $nr,
+            $sz
+        )
+    };
 }
diff --git a/src/sys/ioctl/mod.rs b/src/sys/ioctl/mod.rs
index 203b7d0..98d6b5c 100644
--- a/src/sys/ioctl/mod.rs
+++ b/src/sys/ioctl/mod.rs
@@ -227,37 +227,45 @@
 #[macro_use]
 mod linux;
 
-#[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
+#[cfg(any(
+    target_os = "android",
+    target_os = "linux",
+    target_os = "redox"
+))]
 pub use self::linux::*;
 
-#[cfg(any(target_os = "dragonfly",
-          target_os = "freebsd",
-          target_os = "illumos",
-          target_os = "ios",
-          target_os = "macos",
-          target_os = "netbsd",
-          target_os = "openbsd"))]
+#[cfg(any(
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "illumos",
+    target_os = "ios",
+    target_os = "macos",
+    target_os = "netbsd",
+    target_os = "haiku",
+    target_os = "openbsd"
+))]
 #[macro_use]
 mod bsd;
 
-#[cfg(any(target_os = "dragonfly",
-          target_os = "freebsd",
-          target_os = "illumos",
-          target_os = "ios",
-          target_os = "macos",
-          target_os = "netbsd",
-          target_os = "openbsd"))]
+#[cfg(any(
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "illumos",
+    target_os = "ios",
+    target_os = "macos",
+    target_os = "netbsd",
+    target_os = "haiku",
+    target_os = "openbsd"
+))]
 pub use self::bsd::*;
 
 /// Convert raw ioctl return value to a Nix result
 #[macro_export]
 #[doc(hidden)]
 macro_rules! convert_ioctl_res {
-    ($w:expr) => (
-        {
-            $crate::errno::Errno::result($w)
-        }
-    );
+    ($w:expr) => {{
+        $crate::errno::Errno::result($w)
+    }};
 }
 
 /// Generates a wrapper function for an ioctl that passes no data to the kernel.
@@ -489,7 +497,7 @@
     )
 }
 
-cfg_if!{
+cfg_if! {
     if #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] {
         /// Generates a wrapper function for a ioctl that writes an integer to the kernel.
         ///
diff --git a/src/sys/memfd.rs b/src/sys/memfd.rs
index 642676b..e43e1e5 100644
--- a/src/sys/memfd.rs
+++ b/src/sys/memfd.rs
@@ -1,8 +1,10 @@
 //! Interfaces for managing memory-backed files.
 
+use cfg_if::cfg_if;
 use std::os::unix::io::RawFd;
-use crate::Result;
+
 use crate::errno::Errno;
+use crate::Result;
 use std::ffi::CStr;
 
 libc_bitflags!(
@@ -40,7 +42,24 @@
 /// [`memfd_create(2)`]: https://man7.org/linux/man-pages/man2/memfd_create.2.html
 pub fn memfd_create(name: &CStr, flags: MemFdCreateFlag) -> Result<RawFd> {
     let res = unsafe {
-        libc::syscall(libc::SYS_memfd_create, name.as_ptr(), flags.bits())
+        cfg_if! {
+            if #[cfg(all(
+                // Android does not have a memfd_create symbol
+                not(target_os = "android"),
+                any(
+                    target_os = "freebsd",
+                    // If the OS is Linux, gnu and musl expose a memfd_create symbol but not uclibc
+                    //
+                    // ANDROID: Our glibc is too old to have memfd_create.
+                    // target_env = "gnu",
+                    target_env = "musl",
+                )))]
+            {
+                libc::memfd_create(name.as_ptr(), flags.bits())
+            } else {
+                libc::syscall(libc::SYS_memfd_create, name.as_ptr(), flags.bits())
+            }
+        }
     };
 
     Errno::result(res).map(|r| r as RawFd)
diff --git a/src/sys/mman.rs b/src/sys/mman.rs
index 0ef1ca8..2bee091 100644
--- a/src/sys/mman.rs
+++ b/src/sys/mman.rs
@@ -1,17 +1,16 @@
 //! Memory management declarations.
 
-use crate::Result;
-#[cfg(not(target_os = "android"))]
-use crate::NixPath;
 use crate::errno::Errno;
 #[cfg(not(target_os = "android"))]
-use crate::fcntl::OFlag;
-use libc::{self, c_int, c_void, size_t, off_t};
+use crate::NixPath;
+use crate::Result;
 #[cfg(not(target_os = "android"))]
-use crate::sys::stat::Mode;
-use std::os::unix::io::RawFd;
+#[cfg(feature = "fs")]
+use crate::{fcntl::OFlag, sys::stat::Mode};
+use libc::{self, c_int, c_void, off_t, size_t};
+use std::{os::unix::io::RawFd, num::NonZeroUsize};
 
-libc_bitflags!{
+libc_bitflags! {
     /// Desired memory protection of a memory mapping.
     pub struct ProtFlags: c_int {
         /// Pages cannot be accessed.
@@ -24,14 +23,16 @@
         PROT_EXEC;
         /// Apply protection up to the end of a mapping that grows upwards.
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         PROT_GROWSDOWN;
         /// Apply protection down to the beginning of a mapping that grows downwards.
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         PROT_GROWSUP;
     }
 }
 
-libc_bitflags!{
+libc_bitflags! {
     /// Additional parameters for [`mmap`].
     pub struct MapFlags: c_int {
         /// Compatibility flag. Ignored.
@@ -42,9 +43,14 @@
         MAP_PRIVATE;
         /// Place the mapping at exactly the address specified in `addr`.
         MAP_FIXED;
+        /// Place the mapping at exactly the address specified in `addr`, but never clobber an existing range.
+        #[cfg(target_os = "linux")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
+        MAP_FIXED_NOREPLACE;
         /// To be used with `MAP_FIXED`, to forbid the system
         /// to select a different address than the one specified.
         #[cfg(target_os = "freebsd")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MAP_EXCL;
         /// Synonym for `MAP_ANONYMOUS`.
         MAP_ANON;
@@ -55,122 +61,156 @@
                       any(target_arch = "x86", target_arch = "x86_64")),
                   all(target_os = "linux", target_env = "musl", any(target_arch = "x86", target_arch = "x86_64")),
                   all(target_os = "freebsd", target_pointer_width = "64")))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MAP_32BIT;
         /// Used for stacks; indicates to the kernel that the mapping should extend downward in memory.
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MAP_GROWSDOWN;
         /// Compatibility flag. Ignored.
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MAP_DENYWRITE;
         /// Compatibility flag. Ignored.
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MAP_EXECUTABLE;
         /// Mark the mmaped region to be locked in the same way as `mlock(2)`.
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MAP_LOCKED;
         /// Do not reserve swap space for this mapping.
         ///
         /// This was removed in FreeBSD 11 and is unused in DragonFlyBSD.
         #[cfg(not(any(target_os = "dragonfly", target_os = "freebsd")))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MAP_NORESERVE;
         /// Populate page tables for a mapping.
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MAP_POPULATE;
         /// Only meaningful when used with `MAP_POPULATE`. Don't perform read-ahead.
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MAP_NONBLOCK;
         /// Allocate the mapping using "huge pages."
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MAP_HUGETLB;
         /// Make use of 64KB huge page (must be supported by the system)
         #[cfg(target_os = "linux")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MAP_HUGE_64KB;
         /// Make use of 512KB huge page (must be supported by the system)
         #[cfg(target_os = "linux")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MAP_HUGE_512KB;
         /// Make use of 1MB huge page (must be supported by the system)
         #[cfg(target_os = "linux")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MAP_HUGE_1MB;
         /// Make use of 2MB huge page (must be supported by the system)
         #[cfg(target_os = "linux")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MAP_HUGE_2MB;
         /// Make use of 8MB huge page (must be supported by the system)
         #[cfg(target_os = "linux")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MAP_HUGE_8MB;
         /// Make use of 16MB huge page (must be supported by the system)
         #[cfg(target_os = "linux")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MAP_HUGE_16MB;
         /// Make use of 32MB huge page (must be supported by the system)
         #[cfg(target_os = "linux")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MAP_HUGE_32MB;
         /// Make use of 256MB huge page (must be supported by the system)
         #[cfg(target_os = "linux")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MAP_HUGE_256MB;
         /// Make use of 512MB huge page (must be supported by the system)
         #[cfg(target_os = "linux")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MAP_HUGE_512MB;
         /// Make use of 1GB huge page (must be supported by the system)
         #[cfg(target_os = "linux")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MAP_HUGE_1GB;
         /// Make use of 2GB huge page (must be supported by the system)
         #[cfg(target_os = "linux")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MAP_HUGE_2GB;
         /// Make use of 16GB huge page (must be supported by the system)
         #[cfg(target_os = "linux")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MAP_HUGE_16GB;
 
         /// Lock the mapped region into memory as with `mlock(2)`.
         #[cfg(target_os = "netbsd")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MAP_WIRED;
         /// Causes dirtied data in the specified range to be flushed to disk only when necessary.
         #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MAP_NOSYNC;
         /// Rename private pages to a file.
         ///
         /// This was removed in FreeBSD 11 and is unused in DragonFlyBSD.
         #[cfg(any(target_os = "netbsd", target_os = "openbsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MAP_RENAME;
         /// Region may contain semaphores.
         #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MAP_HASSEMAPHORE;
         /// Region grows down, like a stack.
         #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "linux", target_os = "openbsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MAP_STACK;
         /// Pages in this mapping are not retained in the kernel's memory cache.
         #[cfg(any(target_os = "ios", target_os = "macos"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MAP_NOCACHE;
         /// Allows the W/X bit on the page, it's necessary on aarch64 architecture.
         #[cfg(any(target_os = "ios", target_os = "macos"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MAP_JIT;
         /// Allows to use large pages, underlying alignment based on size.
         #[cfg(target_os = "freebsd")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MAP_ALIGNED_SUPER;
         /// Pages will be discarded in the core dumps.
         #[cfg(target_os = "openbsd")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MAP_CONCEAL;
     }
 }
 
 #[cfg(any(target_os = "linux", target_os = "netbsd"))]
-libc_bitflags!{
+libc_bitflags! {
     /// Options for [`mremap`].
     pub struct MRemapFlags: c_int {
         /// Permit the kernel to relocate the mapping to a new virtual address, if necessary.
         #[cfg(target_os = "linux")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MREMAP_MAYMOVE;
         /// Place the mapping at exactly the address specified in `new_address`.
         #[cfg(target_os = "linux")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MREMAP_FIXED;
-        /// Permits to use the old and new address as hints to relocate the mapping.
+        /// Place the mapping at exactly the address specified in `new_address`.
         #[cfg(target_os = "netbsd")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MAP_FIXED;
         /// Allows to duplicate the mapping to be able to apply different flags on the copy.
         #[cfg(target_os = "netbsd")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MAP_REMAPDUP;
     }
 }
 
-libc_enum!{
+libc_enum! {
     /// Usage information for a range of memory to allow for performance optimizations by the kernel.
     ///
     /// Used by [`madvise`].
@@ -189,23 +229,29 @@
         MADV_DONTNEED,
         /// Free up a given range of pages and its associated backing store.
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MADV_REMOVE,
         /// Do not make pages in this range available to the child after a `fork(2)`.
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MADV_DONTFORK,
         /// Undo the effect of `MADV_DONTFORK`.
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MADV_DOFORK,
         /// Poison the given pages.
         ///
         /// Subsequent references to those pages are treated like hardware memory corruption.
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MADV_HWPOISON,
         /// Enable Kernel Samepage Merging (KSM) for the given pages.
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MADV_MERGEABLE,
         /// Undo the effect of `MADV_MERGEABLE`
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MADV_UNMERGEABLE,
         /// Preserve the memory of each page but offline the original page.
         #[cfg(any(target_os = "android",
@@ -221,51 +267,71 @@
         MADV_SOFT_OFFLINE,
         /// Enable Transparent Huge Pages (THP) for pages in the given range.
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MADV_HUGEPAGE,
         /// Undo the effect of `MADV_HUGEPAGE`.
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MADV_NOHUGEPAGE,
         /// Exclude the given range from a core dump.
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MADV_DONTDUMP,
         /// Undo the effect of an earlier `MADV_DONTDUMP`.
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MADV_DODUMP,
         /// Specify that the application no longer needs the pages in the given range.
         MADV_FREE,
         /// Request that the system not flush the current range to disk unless it needs to.
         #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MADV_NOSYNC,
         /// Undoes the effects of `MADV_NOSYNC` for any future pages dirtied within the given range.
         #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MADV_AUTOSYNC,
         /// Region is not included in a core file.
         #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MADV_NOCORE,
         /// Include region in a core file
         #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MADV_CORE,
+        /// This process should not be killed when swap space is exhausted.
         #[cfg(any(target_os = "freebsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MADV_PROTECT,
         /// Invalidate the hardware page table for the given region.
         #[cfg(target_os = "dragonfly")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MADV_INVAL,
         /// Set the offset of the page directory page to `value` for the virtual page table.
         #[cfg(target_os = "dragonfly")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MADV_SETMAP,
         /// Indicates that the application will not need the data in the given range.
         #[cfg(any(target_os = "ios", target_os = "macos"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MADV_ZERO_WIRED_PAGES,
+        /// Pages can be reused (by anyone).
         #[cfg(any(target_os = "ios", target_os = "macos"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MADV_FREE_REUSABLE,
+        /// Caller wants to reuse those pages.
         #[cfg(any(target_os = "ios", target_os = "macos"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MADV_FREE_REUSE,
+        // Darwin doesn't document this flag's behavior.
         #[cfg(any(target_os = "ios", target_os = "macos"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
+        #[allow(missing_docs)]
         MADV_CAN_REUSE,
     }
 }
 
-libc_bitflags!{
+libc_bitflags! {
     /// Configuration flags for [`msync`].
     pub struct MsFlags: c_int {
         /// Schedule an update but return immediately.
@@ -274,16 +340,19 @@
         MS_INVALIDATE;
         /// Invalidate pages, but leave them mapped.
         #[cfg(any(target_os = "ios", target_os = "macos"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MS_KILLPAGES;
         /// Deactivate pages, but leave them mapped.
         #[cfg(any(target_os = "ios", target_os = "macos"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MS_DEACTIVATE;
         /// Perform an update and wait for it to complete.
         MS_SYNC;
     }
 }
 
-libc_bitflags!{
+#[cfg(not(target_os = "haiku"))]
+libc_bitflags! {
     /// Flags for [`mlockall`].
     pub struct MlockAllFlags: c_int {
         /// Lock pages that are currently mapped into the address space of the process.
@@ -325,6 +394,7 @@
 /// Locked pages never move to the swap area. For more information, see [`mlockall(2)`].
 ///
 /// [`mlockall(2)`]: https://man7.org/linux/man-pages/man2/mlockall.2.html
+#[cfg(not(target_os = "haiku"))]
 pub fn mlockall(flags: MlockAllFlags) -> Result<()> {
     unsafe { Errno::result(libc::mlockall(flags.bits())) }.map(drop)
 }
@@ -334,6 +404,7 @@
 /// For more information, see [`munlockall(2)`].
 ///
 /// [`munlockall(2)`]: https://man7.org/linux/man-pages/man2/munlockall.2.html
+#[cfg(not(target_os = "haiku"))]
 pub fn munlockall() -> Result<()> {
     unsafe { Errno::result(libc::munlockall()) }.map(drop)
 }
@@ -345,8 +416,20 @@
 /// See the [`mmap(2)`] man page for detailed requirements.
 ///
 /// [`mmap(2)`]: https://man7.org/linux/man-pages/man2/mmap.2.html
-pub unsafe fn mmap(addr: *mut c_void, length: size_t, prot: ProtFlags, flags: MapFlags, fd: RawFd, offset: off_t) -> Result<*mut c_void> {
-    let ret = libc::mmap(addr, length, prot.bits(), flags.bits(), fd, offset);
+pub unsafe fn mmap(
+    addr: Option<NonZeroUsize>,
+    length: NonZeroUsize,
+    prot: ProtFlags,
+    flags: MapFlags,
+    fd: RawFd,
+    offset: off_t,
+) -> Result<*mut c_void> {
+    let ptr = addr.map_or(
+        std::ptr::null_mut(),
+        |a| usize::from(a) as *mut c_void
+    );
+    
+    let ret = libc::mmap(ptr, length.into(), prot.bits(), flags.bits(), fd, offset);
 
     if ret == libc::MAP_FAILED {
         Err(Errno::last())
@@ -368,10 +451,16 @@
     old_size: size_t,
     new_size: size_t,
     flags: MRemapFlags,
-    new_address: Option<* mut c_void>,
+    new_address: Option<*mut c_void>,
 ) -> Result<*mut c_void> {
     #[cfg(target_os = "linux")]
-    let ret = libc::mremap(addr, old_size, new_size, flags.bits(), new_address.unwrap_or(std::ptr::null_mut()));
+    let ret = libc::mremap(
+        addr,
+        old_size,
+        new_size,
+        flags.bits(),
+        new_address.unwrap_or(std::ptr::null_mut()),
+    );
     #[cfg(target_os = "netbsd")]
     let ret = libc::mremap(
         addr,
@@ -379,7 +468,7 @@
         new_address.unwrap_or(std::ptr::null_mut()),
         new_size,
         flags.bits(),
-        );
+    );
 
     if ret == libc::MAP_FAILED {
         Err(Errno::last())
@@ -408,7 +497,11 @@
 /// [`MmapAdvise::MADV_FREE`].
 ///
 /// [`madvise(2)`]: https://man7.org/linux/man-pages/man2/madvise.2.html
-pub unsafe fn madvise(addr: *mut c_void, length: size_t, advise: MmapAdvise) -> Result<()> {
+pub unsafe fn madvise(
+    addr: *mut c_void,
+    length: size_t,
+    advise: MmapAdvise,
+) -> Result<()> {
     Errno::result(libc::madvise(addr, length, advise as i32)).map(drop)
 }
 
@@ -427,8 +520,9 @@
 /// # use nix::sys::mman::{mmap, mprotect, MapFlags, ProtFlags};
 /// # use std::ptr;
 /// const ONE_K: size_t = 1024;
+/// let one_k_non_zero = std::num::NonZeroUsize::new(ONE_K).unwrap();
 /// let mut slice: &mut [u8] = unsafe {
-///     let mem = mmap(ptr::null_mut(), ONE_K, ProtFlags::PROT_NONE,
+///     let mem = mmap(None, one_k_non_zero, ProtFlags::PROT_NONE,
 ///                    MapFlags::MAP_ANON | MapFlags::MAP_PRIVATE, -1, 0).unwrap();
 ///     mprotect(mem, ONE_K, ProtFlags::PROT_READ | ProtFlags::PROT_WRITE).unwrap();
 ///     std::slice::from_raw_parts_mut(mem as *mut u8, ONE_K)
@@ -437,7 +531,11 @@
 /// slice[0] = 0xFF;
 /// assert_eq!(slice[0], 0xFF);
 /// ```
-pub unsafe fn mprotect(addr: *mut c_void, length: size_t, prot: ProtFlags) -> Result<()> {
+pub unsafe fn mprotect(
+    addr: *mut c_void,
+    length: size_t,
+    prot: ProtFlags,
+) -> Result<()> {
     Errno::result(libc::mprotect(addr, length, prot.bits())).map(drop)
 }
 
@@ -449,17 +547,29 @@
 /// page.
 ///
 /// [`msync(2)`]: https://man7.org/linux/man-pages/man2/msync.2.html
-pub unsafe fn msync(addr: *mut c_void, length: size_t, flags: MsFlags) -> Result<()> {
+pub unsafe fn msync(
+    addr: *mut c_void,
+    length: size_t,
+    flags: MsFlags,
+) -> Result<()> {
     Errno::result(libc::msync(addr, length, flags.bits())).map(drop)
 }
 
+#[cfg(not(target_os = "android"))]
+feature! {
+#![feature = "fs"]
 /// Creates and opens a new, or opens an existing, POSIX shared memory object.
 ///
 /// For more information, see [`shm_open(3)`].
 ///
 /// [`shm_open(3)`]: https://man7.org/linux/man-pages/man3/shm_open.3.html
-#[cfg(not(target_os = "android"))]
-pub fn shm_open<P: ?Sized + NixPath>(name: &P, flag: OFlag, mode: Mode) -> Result<RawFd> {
+pub fn shm_open<P>(
+    name: &P,
+    flag: OFlag,
+    mode: Mode
+    ) -> Result<RawFd>
+    where P: ?Sized + NixPath
+{
     let ret = name.with_nix_path(|cstr| {
         #[cfg(any(target_os = "macos", target_os = "ios"))]
         unsafe {
@@ -473,6 +583,7 @@
 
     Errno::result(ret)
 }
+}
 
 /// Performs the converse of [`shm_open`], removing an object previously created.
 ///
@@ -481,9 +592,8 @@
 /// [`shm_unlink(3)`]: https://man7.org/linux/man-pages/man3/shm_unlink.3.html
 #[cfg(not(target_os = "android"))]
 pub fn shm_unlink<P: ?Sized + NixPath>(name: &P) -> Result<()> {
-    let ret = name.with_nix_path(|cstr| {
-        unsafe { libc::shm_unlink(cstr.as_ptr()) }
-    })?;
+    let ret =
+        name.with_nix_path(|cstr| unsafe { libc::shm_unlink(cstr.as_ptr()) })?;
 
     Errno::result(ret).map(drop)
 }
diff --git a/src/sys/mod.rs b/src/sys/mod.rs
index 156b0d9..2065059 100644
--- a/src/sys/mod.rs
+++ b/src/sys/mod.rs
@@ -1,130 +1,228 @@
 //! Mostly platform-specific functionality
-#[cfg(any(target_os = "dragonfly",
-          target_os = "freebsd",
-          target_os = "ios",
-          target_os = "linux",
-          target_os = "macos",
-          target_os = "netbsd"))]
-pub mod aio;
+#[cfg(any(
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "ios",
+    all(target_os = "linux", not(target_env = "uclibc")),
+    target_os = "macos",
+    target_os = "netbsd"
+))]
+feature! {
+    #![feature = "aio"]
+    pub mod aio;
+}
 
-#[cfg(any(target_os = "android", target_os = "linux"))]
-#[allow(missing_docs)]
-pub mod epoll;
+feature! {
+    #![feature = "event"]
 
-#[cfg(any(target_os = "dragonfly",
-          target_os = "freebsd",
-          target_os = "ios",
-          target_os = "macos",
-          target_os = "netbsd",
-          target_os = "openbsd"))]
-#[allow(missing_docs)]
-pub mod event;
+    #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[allow(missing_docs)]
+    pub mod epoll;
 
-#[cfg(any(target_os = "android", target_os = "linux"))]
-#[allow(missing_docs)]
-pub mod eventfd;
+    #[cfg(any(target_os = "dragonfly",
+              target_os = "freebsd",
+              target_os = "ios",
+              target_os = "macos",
+              target_os = "netbsd",
+              target_os = "openbsd"))]
+    #[allow(missing_docs)]
+    pub mod event;
 
-#[cfg(any(target_os = "android",
-          target_os = "dragonfly",
-          target_os = "freebsd",
-          target_os = "ios",
-          target_os = "linux",
-          target_os = "redox",
-          target_os = "macos",
-          target_os = "netbsd",
-          target_os = "illumos",
-          target_os = "openbsd"))]
+    #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[allow(missing_docs)]
+    pub mod eventfd;
+}
+
+#[cfg(any(
+    target_os = "android",
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "ios",
+    target_os = "linux",
+    target_os = "redox",
+    target_os = "macos",
+    target_os = "netbsd",
+    target_os = "illumos",
+    target_os = "openbsd"
+))]
+#[cfg(feature = "ioctl")]
+#[cfg_attr(docsrs, doc(cfg(feature = "ioctl")))]
 #[macro_use]
 pub mod ioctl;
 
-#[cfg(target_os = "linux")]
-pub mod memfd;
+#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+feature! {
+    #![feature = "fs"]
+    pub mod memfd;
+}
 
 #[cfg(not(target_os = "redox"))]
-#[allow(missing_docs)]
-pub mod mman;
+feature! {
+    #![feature = "mman"]
+    pub mod mman;
+}
 
 #[cfg(target_os = "linux")]
-#[allow(missing_docs)]
-pub mod personality;
+feature! {
+    #![feature = "personality"]
+    pub mod personality;
+}
 
-pub mod pthread;
+feature! {
+    #![feature = "pthread"]
+    pub mod pthread;
+}
 
-#[cfg(any(target_os = "android",
-          target_os = "dragonfly",
-          target_os = "freebsd",
-          target_os = "linux",
-          target_os = "macos",
-          target_os = "netbsd",
-          target_os = "openbsd"))]
-#[allow(missing_docs)]
-pub mod ptrace;
+#[cfg(any(
+    target_os = "android",
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "linux",
+    target_os = "macos",
+    target_os = "netbsd",
+    target_os = "openbsd"
+))]
+feature! {
+    #![feature = "ptrace"]
+    #[allow(missing_docs)]
+    pub mod ptrace;
+}
 
 #[cfg(target_os = "linux")]
-pub mod quota;
+feature! {
+    #![feature = "quota"]
+    pub mod quota;
+}
 
-#[cfg(any(target_os = "linux"))]
-#[allow(missing_docs)]
-pub mod reboot;
+#[cfg(target_os = "linux")]
+feature! {
+    #![feature = "reboot"]
+    pub mod reboot;
+}
 
-#[cfg(not(any(target_os = "redox", target_os = "fuchsia", target_os = "illumos")))]
-pub mod resource;
+#[cfg(not(any(
+    target_os = "redox",
+    target_os = "fuchsia",
+    target_os = "illumos",
+    target_os = "haiku"
+)))]
+feature! {
+    #![feature = "resource"]
+    pub mod resource;
+}
 
 #[cfg(not(target_os = "redox"))]
-pub mod select;
+feature! {
+    #![feature = "poll"]
+    pub mod select;
+}
 
-#[cfg(any(target_os = "android",
-          target_os = "freebsd",
-          target_os = "ios",
-          target_os = "linux",
-          target_os = "macos"))]
-pub mod sendfile;
+#[cfg(any(
+    target_os = "android",
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "ios",
+    target_os = "linux",
+    target_os = "macos"
+))]
+feature! {
+    #![feature = "zerocopy"]
+    pub mod sendfile;
+}
 
 pub mod signal;
 
 #[cfg(any(target_os = "android", target_os = "linux"))]
-#[allow(missing_docs)]
-pub mod signalfd;
+feature! {
+    #![feature = "signal"]
+    #[allow(missing_docs)]
+    pub mod signalfd;
+}
 
 #[cfg(not(target_os = "redox"))]
-#[allow(missing_docs)]
-pub mod socket;
+feature! {
+    #![feature = "socket"]
+    #[allow(missing_docs)]
+    pub mod socket;
+}
 
-#[allow(missing_docs)]
-pub mod stat;
+feature! {
+    #![feature = "fs"]
+    #[allow(missing_docs)]
+    pub mod stat;
+}
 
-#[cfg(any(target_os = "android",
-          target_os = "dragonfly",
-          target_os = "freebsd",
-          target_os = "ios",
-          target_os = "linux",
-          target_os = "macos",
-          target_os = "openbsd"
+#[cfg(any(
+    target_os = "android",
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "ios",
+    target_os = "linux",
+    target_os = "macos",
+    target_os = "openbsd"
 ))]
-pub mod statfs;
+feature! {
+    #![feature = "fs"]
+    pub mod statfs;
+}
 
-pub mod statvfs;
+feature! {
+    #![feature = "fs"]
+    pub mod statvfs;
+}
 
 #[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
 #[allow(missing_docs)]
 pub mod sysinfo;
 
-#[allow(missing_docs)]
-pub mod termios;
+feature! {
+    #![feature = "term"]
+    #[allow(missing_docs)]
+    pub mod termios;
+}
 
 #[allow(missing_docs)]
 pub mod time;
 
-pub mod uio;
+feature! {
+    #![feature = "uio"]
+    pub mod uio;
+}
 
-pub mod utsname;
+feature! {
+    #![feature = "feature"]
+    pub mod utsname;
+}
 
-pub mod wait;
+feature! {
+    #![feature = "process"]
+    pub mod wait;
+}
 
 #[cfg(any(target_os = "android", target_os = "linux"))]
-#[allow(missing_docs)]
-pub mod inotify;
+feature! {
+    #![feature = "inotify"]
+    pub mod inotify;
+}
 
 #[cfg(any(target_os = "android", target_os = "linux"))]
-#[allow(missing_docs)]
-pub mod timerfd;
+feature! {
+    #![feature = "time"]
+    pub mod timerfd;
+}
+
+#[cfg(all(
+    any(
+        target_os = "freebsd",
+        target_os = "illumos",
+        target_os = "linux",
+        target_os = "netbsd"
+    ),
+    feature = "time",
+    feature = "signal"
+))]
+feature! {
+    #![feature = "time"]
+    pub mod timer;
+}
diff --git a/src/sys/personality.rs b/src/sys/personality.rs
index b15956c..f295a05 100644
--- a/src/sys/personality.rs
+++ b/src/sys/personality.rs
@@ -1,5 +1,6 @@
-use crate::Result;
+//! Process execution domains
 use crate::errno::Errno;
+use crate::Result;
 
 use libc::{self, c_int, c_ulong};
 
@@ -7,18 +8,44 @@
     /// Flags used and returned by [`get()`](fn.get.html) and
     /// [`set()`](fn.set.html).
     pub struct Persona: c_int {
+        /// Provide the legacy virtual address space layout.
         ADDR_COMPAT_LAYOUT;
+        /// Disable address-space-layout randomization.
         ADDR_NO_RANDOMIZE;
+        /// Limit the address space to 32 bits.
         ADDR_LIMIT_32BIT;
+        /// Use `0xc0000000` as the offset at which to search a virtual memory
+        /// chunk on [`mmap(2)`], otherwise use `0xffffe000`.
+        ///
+        /// [`mmap(2)`]: https://man7.org/linux/man-pages/man2/mmap.2.html
         ADDR_LIMIT_3GB;
-        #[cfg(not(target_env = "musl"))]
+        /// User-space function pointers to signal handlers point to descriptors.
+        #[cfg(not(any(target_env = "musl", target_env = "uclibc")))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         FDPIC_FUNCPTRS;
+        /// Map page 0 as read-only.
         MMAP_PAGE_ZERO;
+        /// `PROT_READ` implies `PROT_EXEC` for [`mmap(2)`].
+        ///
+        /// [`mmap(2)`]: https://man7.org/linux/man-pages/man2/mmap.2.html
         READ_IMPLIES_EXEC;
+        /// No effects.
         SHORT_INODE;
+        /// [`select(2)`], [`pselect(2)`], and [`ppoll(2)`] do not modify the
+        /// returned timeout argument when interrupted by a signal handler.
+        ///
+        /// [`select(2)`]: https://man7.org/linux/man-pages/man2/select.2.html
+        /// [`pselect(2)`]: https://man7.org/linux/man-pages/man2/pselect.2.html
+        /// [`ppoll(2)`]: https://man7.org/linux/man-pages/man2/ppoll.2.html
         STICKY_TIMEOUTS;
-        #[cfg(not(target_env = "musl"))]
+        /// Have [`uname(2)`] report a 2.6.40+ version number rather than a 3.x
+        /// version number.
+        ///
+        /// [`uname(2)`]: https://man7.org/linux/man-pages/man2/uname.2.html
+        #[cfg(not(any(target_env = "musl", target_env = "uclibc")))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         UNAME26;
+        /// No effects.
         WHOLE_SECONDS;
     }
 }
@@ -35,9 +62,7 @@
 /// assert!(!pers.contains(Persona::WHOLE_SECONDS));
 /// ```
 pub fn get() -> Result<Persona> {
-    let res = unsafe {
-        libc::personality(0xFFFFFFFF)
-    };
+    let res = unsafe { libc::personality(0xFFFFFFFF) };
 
     Errno::result(res).map(Persona::from_bits_truncate)
 }
@@ -59,12 +84,10 @@
 /// # use nix::sys::personality::{self, Persona};
 /// let mut pers = personality::get().unwrap();
 /// assert!(!pers.contains(Persona::ADDR_NO_RANDOMIZE));
-/// personality::set(pers | Persona::ADDR_NO_RANDOMIZE);
+/// personality::set(pers | Persona::ADDR_NO_RANDOMIZE).unwrap();
 /// ```
 pub fn set(persona: Persona) -> Result<Persona> {
-    let res = unsafe {
-        libc::personality(persona.bits() as c_ulong)
-    };
+    let res = unsafe { libc::personality(persona.bits() as c_ulong) };
 
     Errno::result(res).map(Persona::from_bits_truncate)
 }
diff --git a/src/sys/pthread.rs b/src/sys/pthread.rs
index d42e45d..6bad03a 100644
--- a/src/sys/pthread.rs
+++ b/src/sys/pthread.rs
@@ -4,8 +4,6 @@
 use crate::errno::Errno;
 #[cfg(not(target_os = "redox"))]
 use crate::Result;
-#[cfg(not(target_os = "redox"))]
-use crate::sys::signal::Signal;
 use libc::{self, pthread_t};
 
 /// Identifies an individual thread.
@@ -21,14 +19,20 @@
     unsafe { libc::pthread_self() }
 }
 
+feature! {
+#![feature = "signal"]
+
 /// Send a signal to a thread (see [`pthread_kill(3)`]).
 ///
 /// If `signal` is `None`, `pthread_kill` will only preform error checking and
 /// won't send any signal.
 ///
 /// [`pthread_kill(3)`]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_kill.html
+#[allow(clippy::not_unsafe_ptr_arg_deref)]
 #[cfg(not(target_os = "redox"))]
-pub fn pthread_kill<T: Into<Option<Signal>>>(thread: Pthread, signal: T) -> Result<()> {
+pub fn pthread_kill<T>(thread: Pthread, signal: T) -> Result<()>
+    where T: Into<Option<crate::sys::signal::Signal>>
+{
     let sig = match signal.into() {
         Some(s) => s as libc::c_int,
         None => 0,
@@ -36,3 +40,4 @@
     let res = unsafe { libc::pthread_kill(thread, sig) };
     Errno::result(res).map(drop)
 }
+}
diff --git a/src/sys/ptrace/bsd.rs b/src/sys/ptrace/bsd.rs
index a62881e..ba267c6 100644
--- a/src/sys/ptrace/bsd.rs
+++ b/src/sys/ptrace/bsd.rs
@@ -1,16 +1,16 @@
-use cfg_if::cfg_if;
 use crate::errno::Errno;
-use libc::{self, c_int};
-use std::ptr;
 use crate::sys::signal::Signal;
 use crate::unistd::Pid;
 use crate::Result;
+use cfg_if::cfg_if;
+use libc::{self, c_int};
+use std::ptr;
 
 pub type RequestType = c_int;
 
 cfg_if! {
-    if #[cfg(any(target_os = "dragonfly", 
-                 target_os = "freebsd", 
+    if #[cfg(any(target_os = "dragonfly",
+                 target_os = "freebsd",
                  target_os = "macos",
                  target_os = "openbsd"))] {
         #[doc(hidden)]
@@ -30,10 +30,12 @@
         PT_READ_I,
         PT_READ_D,
         #[cfg(target_os = "macos")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         PT_READ_U,
         PT_WRITE_I,
         PT_WRITE_D,
         #[cfg(target_os = "macos")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         PT_WRITE_U,
         PT_CONTINUE,
         PT_KILL,
@@ -47,10 +49,13 @@
         PT_ATTACH,
         PT_DETACH,
         #[cfg(target_os = "macos")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         PT_SIGEXC,
         #[cfg(target_os = "macos")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         PT_THUPDATE,
         #[cfg(target_os = "macos")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         PT_ATTACHEXC
     }
 }
@@ -66,7 +71,8 @@
         libc::pid_t::from(pid),
         addr,
         data,
-    )).map(|_| 0)
+    ))
+    .map(|_| 0)
 }
 
 /// Sets the process as traceable, as with `ptrace(PT_TRACEME, ...)`
@@ -74,14 +80,19 @@
 /// Indicates that this process is to be traced by its parent.
 /// This is the only ptrace request to be issued by the tracee.
 pub fn traceme() -> Result<()> {
-    unsafe { ptrace_other(Request::PT_TRACE_ME, Pid::from_raw(0), ptr::null_mut(), 0).map(drop) }
+    unsafe {
+        ptrace_other(Request::PT_TRACE_ME, Pid::from_raw(0), ptr::null_mut(), 0)
+            .map(drop)
+    }
 }
 
 /// Attach to a running process, as with `ptrace(PT_ATTACH, ...)`
 ///
 /// Attaches to the process specified by `pid`, making it a tracee of the calling process.
 pub fn attach(pid: Pid) -> Result<()> {
-    unsafe { ptrace_other(Request::PT_ATTACH, pid, ptr::null_mut(), 0).map(drop) }
+    unsafe {
+        ptrace_other(Request::PT_ATTACH, pid, ptr::null_mut(), 0).map(drop)
+    }
 }
 
 /// Detaches the current running process, as with `ptrace(PT_DETACH, ...)`
@@ -109,13 +120,14 @@
     };
     unsafe {
         // Ignore the useless return value
-        ptrace_other(Request::PT_CONTINUE, pid, 1 as AddressType, data).map(drop)
+        ptrace_other(Request::PT_CONTINUE, pid, 1 as AddressType, data)
+            .map(drop)
     }
 }
 
 /// Issues a kill request as with `ptrace(PT_KILL, ...)`
 ///
-/// This request is equivalent to `ptrace(PT_CONTINUE, ..., SIGKILL);` 
+/// This request is equivalent to `ptrace(PT_CONTINUE, ..., SIGKILL);`
 pub fn kill(pid: Pid) -> Result<()> {
     unsafe {
         ptrace_other(Request::PT_KILL, pid, 0 as AddressType, 0).map(drop)
@@ -144,24 +156,28 @@
 ///     _ => {},
 /// }
 /// ```
-#[cfg(
-    any(
-        any(target_os = "dragonfly", target_os = "freebsd", target_os = "macos"),
-        all(target_os = "openbsd", target_arch = "x86_64"),
-        all(target_os = "netbsd",
-            any(target_arch = "x86_64", target_arch = "powerpc")
-        )
+#[cfg(any(
+    any(target_os = "dragonfly", target_os = "freebsd", target_os = "macos"),
+    all(target_os = "openbsd", target_arch = "x86_64"),
+    all(
+        target_os = "netbsd",
+        any(target_arch = "x86_64", target_arch = "powerpc")
     )
-)]
+))]
 pub fn step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
     let data = match sig.into() {
         Some(s) => s as c_int,
         None => 0,
     };
-    unsafe { ptrace_other(Request::PT_STEP, pid, ptr::null_mut(), data).map(drop) }
+    unsafe {
+        ptrace_other(Request::PT_STEP, pid, ptr::null_mut(), data).map(drop)
+    }
 }
 
 /// Reads a word from a processes memory at the given address
+// Technically, ptrace doesn't dereference the pointer.  It passes it directly
+// to the kernel.
+#[allow(clippy::not_unsafe_ptr_arg_deref)]
 pub fn read(pid: Pid, addr: AddressType) -> Result<c_int> {
     unsafe {
         // Traditionally there was a difference between reading data or
@@ -171,6 +187,9 @@
 }
 
 /// Writes a word into the processes memory at the given address
+// Technically, ptrace doesn't dereference the pointer.  It passes it directly
+// to the kernel.
+#[allow(clippy::not_unsafe_ptr_arg_deref)]
 pub fn write(pid: Pid, addr: AddressType, data: c_int) -> Result<()> {
     unsafe { ptrace_other(Request::PT_WRITE_D, pid, addr, data).map(drop) }
 }
diff --git a/src/sys/ptrace/linux.rs b/src/sys/ptrace/linux.rs
index 3723679..9687e05 100644
--- a/src/sys/ptrace/linux.rs
+++ b/src/sys/ptrace/linux.rs
@@ -1,26 +1,31 @@
 //! For detailed description of the ptrace requests, consult `man ptrace`.
 
-use cfg_if::cfg_if;
-use std::{mem, ptr};
-use crate::Result;
 use crate::errno::Errno;
-use libc::{self, c_void, c_long, siginfo_t};
-use crate::unistd::Pid;
 use crate::sys::signal::Signal;
+use crate::unistd::Pid;
+use crate::Result;
+use cfg_if::cfg_if;
+use libc::{self, c_long, c_void, siginfo_t};
+use std::{mem, ptr};
 
 pub type AddressType = *mut ::libc::c_void;
 
 #[cfg(all(
     target_os = "linux",
-    any(all(target_arch = "x86_64",
-            any(target_env = "gnu", target_env = "musl")),
-        all(target_arch = "x86", target_env = "gnu"))
+    any(
+        all(
+            target_arch = "x86_64",
+            any(target_env = "gnu", target_env = "musl")
+        ),
+        all(target_arch = "x86", target_env = "gnu")
+    )
 ))]
 use libc::user_regs_struct;
 
 cfg_if! {
     if #[cfg(any(all(target_os = "linux", target_arch = "s390x"),
-                 all(target_os = "linux", target_env = "gnu")))] {
+                 all(target_os = "linux", target_env = "gnu"),
+                 target_env = "uclibc"))] {
         #[doc(hidden)]
         pub type RequestType = ::libc::c_uint;
     } else {
@@ -29,9 +34,9 @@
     }
 }
 
-libc_enum!{
-    #[cfg_attr(not(any(target_env = "musl", target_os = "android")), repr(u32))]
-    #[cfg_attr(any(target_env = "musl", target_os = "android"), repr(i32))]
+libc_enum! {
+    #[cfg_attr(not(any(target_env = "musl", target_env = "uclibc", target_os = "android")), repr(u32))]
+    #[cfg_attr(any(target_env = "musl", target_env = "uclibc", target_os = "android"), repr(i32))]
     /// Ptrace Request enum defining the action to be taken.
     #[non_exhaustive]
     pub enum Request {
@@ -99,8 +104,10 @@
                                                target_arch = "mips64"))))]
         PTRACE_SETREGSET,
         #[cfg(target_os = "linux")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         PTRACE_SEIZE,
         #[cfg(target_os = "linux")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         PTRACE_INTERRUPT,
         #[cfg(all(target_os = "linux", not(any(target_arch = "mips",
                                                target_arch = "mips64"))))]
@@ -117,7 +124,7 @@
     }
 }
 
-libc_enum!{
+libc_enum! {
     #[repr(i32)]
     /// Using the ptrace options the tracer can configure the tracee to stop
     /// at certain events. This enum is used to define those events as defined
@@ -171,12 +178,16 @@
         PTRACE_O_TRACESECCOMP;
         /// Send a SIGKILL to the tracee if the tracer exits.  This is useful
         /// for ptrace jailers to prevent tracees from escaping their control.
-        #[cfg(any(target_os = "android", target_os = "linux"))]
         PTRACE_O_EXITKILL;
     }
 }
 
-fn ptrace_peek(request: Request, pid: Pid, addr: AddressType, data: *mut c_void) -> Result<c_long> {
+fn ptrace_peek(
+    request: Request,
+    pid: Pid,
+    addr: AddressType,
+    data: *mut c_void,
+) -> Result<c_long> {
     let ret = unsafe {
         Errno::clear();
         libc::ptrace(request as RequestType, libc::pid_t::from(pid), addr, data)
@@ -190,9 +201,13 @@
 /// Get user registers, as with `ptrace(PTRACE_GETREGS, ...)`
 #[cfg(all(
     target_os = "linux",
-    any(all(target_arch = "x86_64",
-            any(target_env = "gnu", target_env = "musl")),
-        all(target_arch = "x86", target_env = "gnu"))
+    any(
+        all(
+            target_arch = "x86_64",
+            any(target_env = "gnu", target_env = "musl")
+        ),
+        all(target_arch = "x86", target_env = "gnu")
+    )
 ))]
 pub fn getregs(pid: Pid) -> Result<user_regs_struct> {
     ptrace_get_data::<user_regs_struct>(Request::PTRACE_GETREGS, pid)
@@ -201,16 +216,22 @@
 /// Set user registers, as with `ptrace(PTRACE_SETREGS, ...)`
 #[cfg(all(
     target_os = "linux",
-    any(all(target_arch = "x86_64",
-            any(target_env = "gnu", target_env = "musl")),
-        all(target_arch = "x86", target_env = "gnu"))
+    any(
+        all(
+            target_arch = "x86_64",
+            any(target_env = "gnu", target_env = "musl")
+        ),
+        all(target_arch = "x86", target_env = "gnu")
+    )
 ))]
 pub fn setregs(pid: Pid, regs: user_regs_struct) -> Result<()> {
     let res = unsafe {
-        libc::ptrace(Request::PTRACE_SETREGS as RequestType,
-                     libc::pid_t::from(pid),
-                     ptr::null_mut::<c_void>(),
-                     &regs as *const _ as *const c_void)
+        libc::ptrace(
+            Request::PTRACE_SETREGS as RequestType,
+            libc::pid_t::from(pid),
+            ptr::null_mut::<c_void>(),
+            &regs as *const _ as *const c_void,
+        )
     };
     Errno::result(res).map(drop)
 }
@@ -222,26 +243,41 @@
 fn ptrace_get_data<T>(request: Request, pid: Pid) -> Result<T> {
     let mut data = mem::MaybeUninit::uninit();
     let res = unsafe {
-        libc::ptrace(request as RequestType,
-                     libc::pid_t::from(pid),
-                     ptr::null_mut::<T>(),
-                     data.as_mut_ptr() as *const _ as *const c_void)
+        libc::ptrace(
+            request as RequestType,
+            libc::pid_t::from(pid),
+            ptr::null_mut::<T>(),
+            data.as_mut_ptr() as *const _ as *const c_void,
+        )
     };
     Errno::result(res)?;
-    Ok(unsafe{ data.assume_init() })
+    Ok(unsafe { data.assume_init() })
 }
 
-unsafe fn ptrace_other(request: Request, pid: Pid, addr: AddressType, data: *mut c_void) -> Result<c_long> {
-    Errno::result(libc::ptrace(request as RequestType, libc::pid_t::from(pid), addr, data)).map(|_| 0)
+unsafe fn ptrace_other(
+    request: Request,
+    pid: Pid,
+    addr: AddressType,
+    data: *mut c_void,
+) -> Result<c_long> {
+    Errno::result(libc::ptrace(
+        request as RequestType,
+        libc::pid_t::from(pid),
+        addr,
+        data,
+    ))
+    .map(|_| 0)
 }
 
 /// Set options, as with `ptrace(PTRACE_SETOPTIONS,...)`.
 pub fn setoptions(pid: Pid, options: Options) -> Result<()> {
     let res = unsafe {
-        libc::ptrace(Request::PTRACE_SETOPTIONS as RequestType,
-                     libc::pid_t::from(pid),
-                     ptr::null_mut::<c_void>(),
-                     options.bits() as *mut c_void)
+        libc::ptrace(
+            Request::PTRACE_SETOPTIONS as RequestType,
+            libc::pid_t::from(pid),
+            ptr::null_mut::<c_void>(),
+            options.bits() as *mut c_void,
+        )
     };
     Errno::result(res).map(drop)
 }
@@ -258,12 +294,14 @@
 
 /// Set siginfo as with `ptrace(PTRACE_SETSIGINFO,...)`
 pub fn setsiginfo(pid: Pid, sig: &siginfo_t) -> Result<()> {
-    let ret = unsafe{
+    let ret = unsafe {
         Errno::clear();
-        libc::ptrace(Request::PTRACE_SETSIGINFO as RequestType,
-                     libc::pid_t::from(pid),
-                     ptr::null_mut::<c_void>(),
-                     sig as *const _ as *const c_void)
+        libc::ptrace(
+            Request::PTRACE_SETSIGINFO as RequestType,
+            libc::pid_t::from(pid),
+            ptr::null_mut::<c_void>(),
+            sig as *const _ as *const c_void,
+        )
     };
     match Errno::result(ret) {
         Ok(_) => Ok(()),
@@ -282,7 +320,8 @@
             Pid::from_raw(0),
             ptr::null_mut(),
             ptr::null_mut(),
-        ).map(drop) // ignore the useless return value
+        )
+        .map(drop) // ignore the useless return value
     }
 }
 
@@ -296,12 +335,8 @@
         None => ptr::null_mut(),
     };
     unsafe {
-        ptrace_other(
-            Request::PTRACE_SYSCALL,
-            pid,
-            ptr::null_mut(),
-            data,
-        ).map(drop) // ignore the useless return value
+        ptrace_other(Request::PTRACE_SYSCALL, pid, ptr::null_mut(), data)
+            .map(drop) // ignore the useless return value
     }
 }
 
@@ -310,14 +345,19 @@
 /// In contrast to the `syscall` function, the syscall stopped at will not be executed.
 /// Thus the the tracee will only be stopped once per syscall,
 /// optionally delivering a signal specified by `sig`.
-#[cfg(all(target_os = "linux", target_env = "gnu", any(target_arch = "x86", target_arch = "x86_64")))]
+#[cfg(all(
+    target_os = "linux",
+    target_env = "gnu",
+    any(target_arch = "x86", target_arch = "x86_64")
+))]
 pub fn sysemu<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
     let data = match sig.into() {
         Some(s) => s as i32 as *mut c_void,
         None => ptr::null_mut(),
     };
     unsafe {
-        ptrace_other(Request::PTRACE_SYSEMU, pid, ptr::null_mut(), data).map(drop)
+        ptrace_other(Request::PTRACE_SYSEMU, pid, ptr::null_mut(), data)
+            .map(drop)
         // ignore the useless return value
     }
 }
@@ -332,7 +372,8 @@
             pid,
             ptr::null_mut(),
             ptr::null_mut(),
-        ).map(drop) // ignore the useless return value
+        )
+        .map(drop) // ignore the useless return value
     }
 }
 
@@ -340,6 +381,7 @@
 ///
 /// Attaches to the process specified in pid, making it a tracee of the calling process.
 #[cfg(target_os = "linux")]
+#[cfg_attr(docsrs, doc(cfg(all())))]
 pub fn seize(pid: Pid, options: Options) -> Result<()> {
     unsafe {
         ptrace_other(
@@ -347,7 +389,8 @@
             pid,
             ptr::null_mut(),
             options.bits() as *mut c_void,
-        ).map(drop) // ignore the useless return value
+        )
+        .map(drop) // ignore the useless return value
     }
 }
 
@@ -361,12 +404,8 @@
         None => ptr::null_mut(),
     };
     unsafe {
-        ptrace_other(
-            Request::PTRACE_DETACH,
-            pid,
-            ptr::null_mut(),
-            data
-        ).map(drop)
+        ptrace_other(Request::PTRACE_DETACH, pid, ptr::null_mut(), data)
+            .map(drop)
     }
 }
 
@@ -380,7 +419,8 @@
         None => ptr::null_mut(),
     };
     unsafe {
-        ptrace_other(Request::PTRACE_CONT, pid, ptr::null_mut(), data).map(drop) // ignore the useless return value
+        ptrace_other(Request::PTRACE_CONT, pid, ptr::null_mut(), data).map(drop)
+        // ignore the useless return value
     }
 }
 
@@ -388,9 +428,16 @@
 ///
 /// This request is equivalent to `ptrace(PTRACE_INTERRUPT, ...)`
 #[cfg(target_os = "linux")]
+#[cfg_attr(docsrs, doc(cfg(all())))]
 pub fn interrupt(pid: Pid) -> Result<()> {
     unsafe {
-        ptrace_other(Request::PTRACE_INTERRUPT, pid, ptr::null_mut(), ptr::null_mut()).map(drop)
+        ptrace_other(
+            Request::PTRACE_INTERRUPT,
+            pid,
+            ptr::null_mut(),
+            ptr::null_mut(),
+        )
+        .map(drop)
     }
 }
 
@@ -399,7 +446,13 @@
 /// This request is equivalent to `ptrace(PTRACE_CONT, ..., SIGKILL);`
 pub fn kill(pid: Pid) -> Result<()> {
     unsafe {
-        ptrace_other(Request::PTRACE_KILL, pid, ptr::null_mut(), ptr::null_mut()).map(drop)
+        ptrace_other(
+            Request::PTRACE_KILL,
+            pid,
+            ptr::null_mut(),
+            ptr::null_mut(),
+        )
+        .map(drop)
     }
 }
 
@@ -432,7 +485,8 @@
         None => ptr::null_mut(),
     };
     unsafe {
-        ptrace_other(Request::PTRACE_SINGLESTEP, pid, ptr::null_mut(), data).map(drop)
+        ptrace_other(Request::PTRACE_SINGLESTEP, pid, ptr::null_mut(), data)
+            .map(drop)
     }
 }
 
@@ -442,7 +496,11 @@
 /// Advances the execution by a single step or until the next syscall.
 /// In case the tracee is stopped at a syscall, the syscall will not be executed.
 /// Optionally, the signal specified by `sig` is delivered to the tracee upon continuation.
-#[cfg(all(target_os = "linux", target_env = "gnu", any(target_arch = "x86", target_arch = "x86_64")))]
+#[cfg(all(
+    target_os = "linux",
+    target_env = "gnu",
+    any(target_arch = "x86", target_arch = "x86_64")
+))]
 pub fn sysemu_step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
     let data = match sig.into() {
         Some(s) => s as i32 as *mut c_void,
@@ -473,7 +531,28 @@
 pub unsafe fn write(
     pid: Pid,
     addr: AddressType,
-    data: *mut c_void) -> Result<()>
-{
+    data: *mut c_void,
+) -> Result<()> {
     ptrace_other(Request::PTRACE_POKEDATA, pid, addr, data).map(drop)
 }
+
+/// Reads a word from a user area at `offset`.
+/// The user struct definition can be found in `/usr/include/sys/user.h`.
+pub fn read_user(pid: Pid, offset: AddressType) -> Result<c_long> {
+    ptrace_peek(Request::PTRACE_PEEKUSER, pid, offset, ptr::null_mut())
+}
+
+/// Writes a word to a user area at `offset`.
+/// The user struct definition can be found in `/usr/include/sys/user.h`.
+///
+/// # Safety
+///
+/// The `data` argument is passed directly to `ptrace(2)`.  Read that man page
+/// for guidance.
+pub unsafe fn write_user(
+    pid: Pid,
+    offset: AddressType,
+    data: *mut c_void,
+) -> Result<()> {
+    ptrace_other(Request::PTRACE_POKEUSER, pid, offset, data).map(drop)
+}
diff --git a/src/sys/ptrace/mod.rs b/src/sys/ptrace/mod.rs
index 782c304..2b121c0 100644
--- a/src/sys/ptrace/mod.rs
+++ b/src/sys/ptrace/mod.rs
@@ -1,4 +1,4 @@
-///! Provides helpers for making ptrace system calls 
+///! Provides helpers for making ptrace system calls
 
 #[cfg(any(target_os = "android", target_os = "linux"))]
 mod linux;
@@ -6,17 +6,20 @@
 #[cfg(any(target_os = "android", target_os = "linux"))]
 pub use self::linux::*;
 
-#[cfg(any(target_os = "dragonfly",
-          target_os = "freebsd",
-          target_os = "macos",
-          target_os = "netbsd",
-          target_os = "openbsd"))]
+#[cfg(any(
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "macos",
+    target_os = "netbsd",
+    target_os = "openbsd"
+))]
 mod bsd;
 
-#[cfg(any(target_os = "dragonfly",
-          target_os = "freebsd",
-          target_os = "macos",
-          target_os = "netbsd",
-          target_os = "openbsd"
-          ))]
+#[cfg(any(
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "macos",
+    target_os = "netbsd",
+    target_os = "openbsd"
+))]
 pub use self::bsd::*;
diff --git a/src/sys/quota.rs b/src/sys/quota.rs
index 6e34e38..b3c44ca 100644
--- a/src/sys/quota.rs
+++ b/src/sys/quota.rs
@@ -6,17 +6,17 @@
 //!
 //! ```rust,no_run
 //! # use nix::sys::quota::{Dqblk, quotactl_on, quotactl_set, QuotaFmt, QuotaType, QuotaValidFlags};
-//! quotactl_on(QuotaType::USRQUOTA, "/dev/sda1", QuotaFmt::QFMT_VFS_V1, "aquota.user");
+//! quotactl_on(QuotaType::USRQUOTA, "/dev/sda1", QuotaFmt::QFMT_VFS_V1, "aquota.user").unwrap();
 //! let mut dqblk: Dqblk = Default::default();
 //! dqblk.set_blocks_hard_limit(10000);
 //! dqblk.set_blocks_soft_limit(8000);
-//! quotactl_set(QuotaType::USRQUOTA, "/dev/sda1", 50, &dqblk, QuotaValidFlags::QIF_BLIMITS);
+//! quotactl_set(QuotaType::USRQUOTA, "/dev/sda1", 50, &dqblk, QuotaValidFlags::QIF_BLIMITS).unwrap();
 //! ```
+use crate::errno::Errno;
+use crate::{NixPath, Result};
+use libc::{self, c_char, c_int};
 use std::default::Default;
 use std::{mem, ptr};
-use libc::{self, c_int, c_char};
-use crate::{Result, NixPath};
-use crate::errno::Errno;
 
 struct QuotaCmd(QuotaSubCmd, QuotaType);
 
@@ -28,7 +28,7 @@
 }
 
 // linux quota version >= 2
-libc_enum!{
+libc_enum! {
     #[repr(i32)]
     enum QuotaSubCmd {
         Q_SYNC,
@@ -39,7 +39,7 @@
     }
 }
 
-libc_enum!{
+libc_enum! {
     /// The scope of the quota.
     #[repr(i32)]
     #[non_exhaustive]
@@ -51,7 +51,7 @@
     }
 }
 
-libc_enum!{
+libc_enum! {
     /// The type of quota format to use.
     #[repr(i32)]
     #[non_exhaustive]
@@ -120,7 +120,8 @@
 impl Dqblk {
     /// The absolute limit on disk quota blocks allocated.
     pub fn blocks_hard_limit(&self) -> Option<u64> {
-        let valid_fields = QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
+        let valid_fields =
+            QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
         if valid_fields.contains(QuotaValidFlags::QIF_BLIMITS) {
             Some(self.0.dqb_bhardlimit)
         } else {
@@ -135,7 +136,8 @@
 
     /// Preferred limit on disk quota blocks
     pub fn blocks_soft_limit(&self) -> Option<u64> {
-        let valid_fields = QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
+        let valid_fields =
+            QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
         if valid_fields.contains(QuotaValidFlags::QIF_BLIMITS) {
             Some(self.0.dqb_bsoftlimit)
         } else {
@@ -150,7 +152,8 @@
 
     /// Current occupied space (bytes).
     pub fn occupied_space(&self) -> Option<u64> {
-        let valid_fields = QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
+        let valid_fields =
+            QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
         if valid_fields.contains(QuotaValidFlags::QIF_SPACE) {
             Some(self.0.dqb_curspace)
         } else {
@@ -160,7 +163,8 @@
 
     /// Maximum number of allocated inodes.
     pub fn inodes_hard_limit(&self) -> Option<u64> {
-        let valid_fields = QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
+        let valid_fields =
+            QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
         if valid_fields.contains(QuotaValidFlags::QIF_ILIMITS) {
             Some(self.0.dqb_ihardlimit)
         } else {
@@ -175,7 +179,8 @@
 
     /// Preferred inode limit
     pub fn inodes_soft_limit(&self) -> Option<u64> {
-        let valid_fields = QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
+        let valid_fields =
+            QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
         if valid_fields.contains(QuotaValidFlags::QIF_ILIMITS) {
             Some(self.0.dqb_isoftlimit)
         } else {
@@ -190,7 +195,8 @@
 
     /// Current number of allocated inodes.
     pub fn allocated_inodes(&self) -> Option<u64> {
-        let valid_fields = QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
+        let valid_fields =
+            QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
         if valid_fields.contains(QuotaValidFlags::QIF_INODES) {
             Some(self.0.dqb_curinodes)
         } else {
@@ -200,7 +206,8 @@
 
     /// Time limit for excessive disk use.
     pub fn block_time_limit(&self) -> Option<u64> {
-        let valid_fields = QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
+        let valid_fields =
+            QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
         if valid_fields.contains(QuotaValidFlags::QIF_BTIME) {
             Some(self.0.dqb_btime)
         } else {
@@ -215,7 +222,8 @@
 
     /// Time limit for excessive files.
     pub fn inode_time_limit(&self) -> Option<u64> {
-        let valid_fields = QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
+        let valid_fields =
+            QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
         if valid_fields.contains(QuotaValidFlags::QIF_ITIME) {
             Some(self.0.dqb_itime)
         } else {
@@ -229,11 +237,18 @@
     }
 }
 
-fn quotactl<P: ?Sized + NixPath>(cmd: QuotaCmd, special: Option<&P>, id: c_int, addr: *mut c_char) -> Result<()> {
+fn quotactl<P: ?Sized + NixPath>(
+    cmd: QuotaCmd,
+    special: Option<&P>,
+    id: c_int,
+    addr: *mut c_char,
+) -> Result<()> {
     unsafe {
         Errno::clear();
         let res = match special {
-            Some(dev) => dev.with_nix_path(|path| libc::quotactl(cmd.as_int(), path.as_ptr(), id, addr)),
+            Some(dev) => dev.with_nix_path(|path| {
+                libc::quotactl(cmd.as_int(), path.as_ptr(), id, addr)
+            }),
             None => Ok(libc::quotactl(cmd.as_int(), ptr::null(), id, addr)),
         }?;
 
@@ -242,36 +257,82 @@
 }
 
 /// Turn on disk quotas for a block device.
-pub fn quotactl_on<P: ?Sized + NixPath>(which: QuotaType, special: &P, format: QuotaFmt, quota_file: &P) -> Result<()> {
+pub fn quotactl_on<P: ?Sized + NixPath>(
+    which: QuotaType,
+    special: &P,
+    format: QuotaFmt,
+    quota_file: &P,
+) -> Result<()> {
     quota_file.with_nix_path(|path| {
         let mut path_copy = path.to_bytes_with_nul().to_owned();
         let p: *mut c_char = path_copy.as_mut_ptr() as *mut c_char;
-        quotactl(QuotaCmd(QuotaSubCmd::Q_QUOTAON, which), Some(special), format as c_int, p)
+        quotactl(
+            QuotaCmd(QuotaSubCmd::Q_QUOTAON, which),
+            Some(special),
+            format as c_int,
+            p,
+        )
     })?
 }
 
 /// Disable disk quotas for a block device.
-pub fn quotactl_off<P: ?Sized + NixPath>(which: QuotaType, special: &P) -> Result<()> {
-    quotactl(QuotaCmd(QuotaSubCmd::Q_QUOTAOFF, which), Some(special), 0, ptr::null_mut())
+pub fn quotactl_off<P: ?Sized + NixPath>(
+    which: QuotaType,
+    special: &P,
+) -> Result<()> {
+    quotactl(
+        QuotaCmd(QuotaSubCmd::Q_QUOTAOFF, which),
+        Some(special),
+        0,
+        ptr::null_mut(),
+    )
 }
 
 /// Update the on-disk copy of quota usages for a filesystem.
 ///
 /// If `special` is `None`, then all file systems with active quotas are sync'd.
-pub fn quotactl_sync<P: ?Sized + NixPath>(which: QuotaType, special: Option<&P>) -> Result<()> {
-    quotactl(QuotaCmd(QuotaSubCmd::Q_SYNC, which), special, 0, ptr::null_mut())
+pub fn quotactl_sync<P: ?Sized + NixPath>(
+    which: QuotaType,
+    special: Option<&P>,
+) -> Result<()> {
+    quotactl(
+        QuotaCmd(QuotaSubCmd::Q_SYNC, which),
+        special,
+        0,
+        ptr::null_mut(),
+    )
 }
 
 /// Get disk quota limits and current usage for the given user/group id.
-pub fn quotactl_get<P: ?Sized + NixPath>(which: QuotaType, special: &P, id: c_int) -> Result<Dqblk> {
+pub fn quotactl_get<P: ?Sized + NixPath>(
+    which: QuotaType,
+    special: &P,
+    id: c_int,
+) -> Result<Dqblk> {
     let mut dqblk = mem::MaybeUninit::uninit();
-    quotactl(QuotaCmd(QuotaSubCmd::Q_GETQUOTA, which), Some(special), id, dqblk.as_mut_ptr() as *mut c_char)?;
-    Ok(unsafe{ Dqblk(dqblk.assume_init())})
+    quotactl(
+        QuotaCmd(QuotaSubCmd::Q_GETQUOTA, which),
+        Some(special),
+        id,
+        dqblk.as_mut_ptr() as *mut c_char,
+    )?;
+    Ok(unsafe { Dqblk(dqblk.assume_init()) })
 }
 
 /// Configure quota values for the specified fields for a given user/group id.
-pub fn quotactl_set<P: ?Sized + NixPath>(which: QuotaType, special: &P, id: c_int, dqblk: &Dqblk, fields: QuotaValidFlags) -> Result<()> {
+pub fn quotactl_set<P: ?Sized + NixPath>(
+    which: QuotaType,
+    special: &P,
+    id: c_int,
+    dqblk: &Dqblk,
+    fields: QuotaValidFlags,
+) -> Result<()> {
     let mut dqblk_copy = *dqblk;
     dqblk_copy.0.dqb_valid = fields.bits();
-    quotactl(QuotaCmd(QuotaSubCmd::Q_SETQUOTA, which), Some(special), id, &mut dqblk_copy as *mut _ as *mut c_char)
+    quotactl(
+        QuotaCmd(QuotaSubCmd::Q_SETQUOTA, which),
+        Some(special),
+        id,
+        &mut dqblk_copy as *mut _ as *mut c_char,
+    )
 }
diff --git a/src/sys/reboot.rs b/src/sys/reboot.rs
index 46ab68b..02d9816 100644
--- a/src/sys/reboot.rs
+++ b/src/sys/reboot.rs
@@ -1,7 +1,7 @@
 //! Reboot/shutdown or enable/disable Ctrl-Alt-Delete.
 
-use crate::Result;
 use crate::errno::Errno;
+use crate::Result;
 use std::convert::Infallible;
 use std::mem::drop;
 
@@ -13,19 +13,24 @@
     #[repr(i32)]
     #[non_exhaustive]
     pub enum RebootMode {
+        /// Halt the system.
         RB_HALT_SYSTEM,
+        /// Execute a kernel that has been loaded earlier with
+        /// [`kexec_load(2)`](https://man7.org/linux/man-pages/man2/kexec_load.2.html).
         RB_KEXEC,
+        /// Stop the system and switch off power, if possible.
         RB_POWER_OFF,
+        /// Restart the system.
         RB_AUTOBOOT,
-        // we do not support Restart2,
+        // we do not support Restart2.
+        /// Suspend the system using software suspend.
         RB_SW_SUSPEND,
     }
 }
 
+/// Reboots or shuts down the system.
 pub fn reboot(how: RebootMode) -> Result<Infallible> {
-    unsafe {
-        libc::reboot(how as libc::c_int)
-    };
+    unsafe { libc::reboot(how as libc::c_int) };
     Err(Errno::last())
 }
 
@@ -38,8 +43,6 @@
     } else {
         libc::RB_DISABLE_CAD
     };
-    let res = unsafe {
-        libc::reboot(cmd)
-    };
+    let res = unsafe { libc::reboot(cmd) };
     Errno::result(res).map(drop)
 }
diff --git a/src/sys/resource.rs b/src/sys/resource.rs
index f3bfb67..8927737 100644
--- a/src/sys/resource.rs
+++ b/src/sys/resource.rs
@@ -1,15 +1,18 @@
 //! Configure the process resource limits.
 use cfg_if::cfg_if;
+use libc::{c_int, c_long, rusage};
 
 use crate::errno::Errno;
+use crate::sys::time::TimeVal;
 use crate::Result;
 pub use libc::rlim_t;
+pub use libc::RLIM_INFINITY;
 use std::mem;
 
 cfg_if! {
-    if #[cfg(all(target_os = "linux", target_env = "gnu"))]{
-        use libc::{__rlimit_resource_t, rlimit, RLIM_INFINITY};
-    }else if #[cfg(any(
+    if #[cfg(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")))]{
+        use libc::{__rlimit_resource_t, rlimit};
+    } else if #[cfg(any(
         target_os = "freebsd",
         target_os = "openbsd",
         target_os = "netbsd",
@@ -19,24 +22,27 @@
         target_os = "dragonfly",
         all(target_os = "linux", not(target_env = "gnu"))
     ))]{
-        use libc::{c_int, rlimit, RLIM_INFINITY};
+        use libc::rlimit;
     }
 }
 
 libc_enum! {
+    /// Types of process resources.
+    ///
     /// The Resource enum is platform dependent. Check different platform
-    /// manuals for more details. Some platform links has been provided for
-    /// earier reference (non-exhaustive).
+    /// manuals for more details. Some platform links have been provided for
+    /// easier reference (non-exhaustive).
     ///
     /// * [Linux](https://man7.org/linux/man-pages/man2/getrlimit.2.html)
     /// * [FreeBSD](https://www.freebsd.org/cgi/man.cgi?query=setrlimit)
+    /// * [NetBSD](https://man.netbsd.org/setrlimit.2)
 
     // linux-gnu uses u_int as resource enum, which is implemented in libc as
     // well.
     //
     // https://gcc.gnu.org/legacy-ml/gcc/2015-08/msg00441.html
     // https://github.com/rust-lang/libc/blob/master/src/unix/linux_like/linux/gnu/mod.rs
-    #[cfg_attr(all(target_os = "linux", target_env = "gnu"), repr(u32))]
+    #[cfg_attr(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")), repr(u32))]
     #[cfg_attr(any(
             target_os = "freebsd",
             target_os = "openbsd",
@@ -45,15 +51,12 @@
             target_os = "ios",
             target_os = "android",
             target_os = "dragonfly",
-            all(target_os = "linux", not(target_env = "gnu"))
+            all(target_os = "linux", not(any(target_env = "gnu", target_env = "uclibc")))
         ), repr(i32))]
     #[non_exhaustive]
     pub enum Resource {
-        #[cfg(not(any(
-                    target_os = "freebsd",
-                    target_os = "netbsd",
-                    target_os = "openbsd"
-        )))]
+        #[cfg(not(any(target_os = "freebsd", target_os = "netbsd", target_os = "openbsd")))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         /// The maximum amount (in bytes) of virtual memory the process is
         /// allowed to map.
         RLIMIT_AS,
@@ -72,69 +75,100 @@
         RLIMIT_STACK,
 
         #[cfg(target_os = "freebsd")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         /// The maximum number of kqueues this user id is allowed to create.
         RLIMIT_KQUEUES,
 
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         /// A limit on the combined number of flock locks and fcntl leases that
         /// this process may establish.
         RLIMIT_LOCKS,
 
-        #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "openbsd", target_os = "linux"))]
+        #[cfg(any(
+            target_os = "android",
+            target_os = "freebsd",
+            target_os = "openbsd",
+            target_os = "linux",
+            target_os = "netbsd"
+        ))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         /// The maximum size (in bytes) which a process may lock into memory
         /// using the mlock(2) system call.
         RLIMIT_MEMLOCK,
 
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         /// A limit on the number of bytes that can be allocated for POSIX
         /// message queues  for  the  real  user  ID  of  the  calling process.
         RLIMIT_MSGQUEUE,
 
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         /// A ceiling to which the process's nice value can be raised using
         /// setpriority or nice.
         RLIMIT_NICE,
 
-        #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "openbsd", target_os = "linux"))]
+        #[cfg(any(
+            target_os = "android",
+            target_os = "freebsd",
+            target_os = "netbsd",
+            target_os = "openbsd",
+            target_os = "linux",
+        ))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         /// The maximum number of simultaneous processes for this user id.
         RLIMIT_NPROC,
 
         #[cfg(target_os = "freebsd")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         /// The maximum number of pseudo-terminals this user id is allowed to
         /// create.
         RLIMIT_NPTS,
 
-        #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "openbsd", target_os = "linux"))]
+        #[cfg(any(target_os = "android",
+            target_os = "freebsd",
+            target_os = "netbsd",
+            target_os = "openbsd",
+            target_os = "linux",
+        ))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         /// When there is memory pressure and swap is available, prioritize
         /// eviction of a process' resident pages beyond this amount (in bytes).
         RLIMIT_RSS,
 
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         /// A ceiling on the real-time priority that may be set for this process
         /// using sched_setscheduler and  sched_set‐ param.
         RLIMIT_RTPRIO,
 
         #[cfg(any(target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         /// A limit (in microseconds) on the amount of CPU time that a process
         /// scheduled under a real-time scheduling policy may con‐ sume without
         /// making a blocking system call.
         RLIMIT_RTTIME,
 
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         /// A limit on the number of signals that may be queued for the real
         /// user ID of the  calling  process.
         RLIMIT_SIGPENDING,
 
         #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         /// The maximum size (in bytes) of socket buffer usage for this user.
         RLIMIT_SBSIZE,
 
         #[cfg(target_os = "freebsd")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         /// The maximum size (in bytes) of the swap space that may be reserved
         /// or used by all of this user id's processes.
         RLIMIT_SWAP,
 
         #[cfg(target_os = "freebsd")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         /// An alias for RLIMIT_AS.
         RLIMIT_VMEM,
     }
@@ -142,8 +176,8 @@
 
 /// Get the current processes resource limits
 ///
-/// A value of `None` indicates the value equals to `RLIM_INFINITY` which means
-/// there is no limit.
+/// The special value [`RLIM_INFINITY`] indicates that no limit will be
+/// enforced.
 ///
 /// # Parameters
 ///
@@ -155,8 +189,8 @@
 /// # use nix::sys::resource::{getrlimit, Resource};
 ///
 /// let (soft_limit, hard_limit) = getrlimit(Resource::RLIMIT_NOFILE).unwrap();
-/// println!("current soft_limit: {:?}", soft_limit);
-/// println!("current hard_limit: {:?}", hard_limit);
+/// println!("current soft_limit: {}", soft_limit);
+/// println!("current hard_limit: {}", hard_limit);
 /// ```
 ///
 /// # References
@@ -164,20 +198,20 @@
 /// [getrlimit(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getrlimit.html#tag_16_215)
 ///
 /// [`Resource`]: enum.Resource.html
-pub fn getrlimit(resource: Resource) -> Result<(Option<rlim_t>, Option<rlim_t>)> {
+pub fn getrlimit(resource: Resource) -> Result<(rlim_t, rlim_t)> {
     let mut old_rlim = mem::MaybeUninit::<rlimit>::uninit();
 
     cfg_if! {
-        if #[cfg(all(target_os = "linux", target_env = "gnu"))]{
+        if #[cfg(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")))]{
             let res = unsafe { libc::getrlimit(resource as __rlimit_resource_t, old_rlim.as_mut_ptr()) };
-        }else{
+        } else {
             let res = unsafe { libc::getrlimit(resource as c_int, old_rlim.as_mut_ptr()) };
         }
     }
 
     Errno::result(res).map(|_| {
         let rlimit { rlim_cur, rlim_max } = unsafe { old_rlim.assume_init() };
-        (Some(rlim_cur), Some(rlim_max))
+        (rlim_cur, rlim_max)
     })
 }
 
@@ -187,21 +221,20 @@
 ///
 /// * `resource`: The [`Resource`] that we want to set the limits of.
 /// * `soft_limit`: The value that the kernel enforces for the corresponding
-///   resource. Note: `None` input will be replaced by constant `RLIM_INFINITY`.
+///   resource.
 /// * `hard_limit`: The ceiling for the soft limit. Must be lower or equal to
-///   the current hard limit for non-root users. Note: `None` input will be
-///   replaced by constant `RLIM_INFINITY`.
+///   the current hard limit for non-root users.
 ///
-/// > Note: for some os (linux_gnu), setting hard_limit to `RLIM_INFINITY` can
-/// > results `EPERM` Error. So you will need to set the number explicitly.
+/// The special value [`RLIM_INFINITY`] indicates that no limit will be
+/// enforced.
 ///
 /// # Examples
 ///
 /// ```
 /// # use nix::sys::resource::{setrlimit, Resource};
 ///
-/// let soft_limit = Some(512);
-/// let hard_limit = Some(1024);
+/// let soft_limit = 512;
+/// let hard_limit = 1024;
 /// setrlimit(Resource::RLIMIT_NOFILE, soft_limit, hard_limit).unwrap();
 /// ```
 ///
@@ -214,15 +247,15 @@
 /// Note: `setrlimit` provides a safe wrapper to libc's `setrlimit`.
 pub fn setrlimit(
     resource: Resource,
-    soft_limit: Option<rlim_t>,
-    hard_limit: Option<rlim_t>,
+    soft_limit: rlim_t,
+    hard_limit: rlim_t,
 ) -> Result<()> {
     let new_rlim = rlimit {
-        rlim_cur: soft_limit.unwrap_or(RLIM_INFINITY),
-        rlim_max: hard_limit.unwrap_or(RLIM_INFINITY),
+        rlim_cur: soft_limit,
+        rlim_max: hard_limit,
     };
     cfg_if! {
-        if #[cfg(all(target_os = "linux", target_env = "gnu"))]{
+        if #[cfg(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")))]{
             let res = unsafe { libc::setrlimit(resource as __rlimit_resource_t, &new_rlim as *const rlimit) };
         }else{
             let res = unsafe { libc::setrlimit(resource as c_int, &new_rlim as *const rlimit) };
@@ -231,3 +264,180 @@
 
     Errno::result(res).map(drop)
 }
+
+libc_enum! {
+    /// Whose resource usage should be returned by [`getrusage`].
+    #[repr(i32)]
+    #[non_exhaustive]
+    pub enum UsageWho {
+        /// Resource usage for the current process.
+        RUSAGE_SELF,
+
+        /// Resource usage for all the children that have terminated and been waited for.
+        RUSAGE_CHILDREN,
+
+        #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
+        /// Resource usage for the calling thread.
+        RUSAGE_THREAD,
+    }
+}
+
+/// Output of `getrusage` with information about resource usage. Some of the fields
+/// may be unused in some platforms, and will be always zeroed out. See their manuals
+/// for details.
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct Usage(rusage);
+
+impl AsRef<rusage> for Usage {
+    fn as_ref(&self) -> &rusage {
+        &self.0
+    }
+}
+
+impl AsMut<rusage> for Usage {
+    fn as_mut(&mut self) -> &mut rusage {
+        &mut self.0
+    }
+}
+
+impl Usage {
+    /// Total amount of time spent executing in user mode.
+    pub fn user_time(&self) -> TimeVal {
+        TimeVal::from(self.0.ru_utime)
+    }
+
+    /// Total amount of time spent executing in kernel mode.
+    pub fn system_time(&self) -> TimeVal {
+        TimeVal::from(self.0.ru_stime)
+    }
+
+    /// The resident set size at its peak, in kilobytes.
+    pub fn max_rss(&self) -> c_long {
+        self.0.ru_maxrss
+    }
+
+    /// Integral value expressed in kilobytes times ticks of execution indicating
+    /// the amount of text memory shared with other processes.
+    pub fn shared_integral(&self) -> c_long {
+        self.0.ru_ixrss
+    }
+
+    /// Integral value expressed in kilobytes times ticks of execution indicating
+    /// the amount of unshared memory used by data.
+    pub fn unshared_data_integral(&self) -> c_long {
+        self.0.ru_idrss
+    }
+
+    /// Integral value expressed in kilobytes times ticks of execution indicating
+    /// the amount of unshared memory used for stack space.
+    pub fn unshared_stack_integral(&self) -> c_long {
+        self.0.ru_isrss
+    }
+
+    /// Number of page faults that were served without resorting to I/O, with pages
+    /// that have been allocated previously by the kernel.
+    pub fn minor_page_faults(&self) -> c_long {
+        self.0.ru_minflt
+    }
+
+    /// Number of page faults that were served through I/O (i.e. swap).
+    pub fn major_page_faults(&self) -> c_long {
+        self.0.ru_majflt
+    }
+
+    /// Number of times all of the memory was fully swapped out.
+    pub fn full_swaps(&self) -> c_long {
+        self.0.ru_nswap
+    }
+
+    /// Number of times a read was done from a block device.
+    pub fn block_reads(&self) -> c_long {
+        self.0.ru_inblock
+    }
+
+    /// Number of times a write was done to a block device.
+    pub fn block_writes(&self) -> c_long {
+        self.0.ru_oublock
+    }
+
+    /// Number of IPC messages sent.
+    pub fn ipc_sends(&self) -> c_long {
+        self.0.ru_msgsnd
+    }
+
+    /// Number of IPC messages received.
+    pub fn ipc_receives(&self) -> c_long {
+        self.0.ru_msgrcv
+    }
+
+    /// Number of signals received.
+    pub fn signals(&self) -> c_long {
+        self.0.ru_nsignals
+    }
+
+    /// Number of times a context switch was voluntarily invoked.
+    pub fn voluntary_context_switches(&self) -> c_long {
+        self.0.ru_nvcsw
+    }
+
+    /// Number of times a context switch was imposed by the kernel (usually due to
+    /// time slice expiring or preemption by a higher priority process).
+    pub fn involuntary_context_switches(&self) -> c_long {
+        self.0.ru_nivcsw
+    }
+}
+
+/// Get usage information for a process, its children or the current thread
+///
+/// Real time information can be obtained for either the current process or (in some
+/// systems) thread, but information about children processes is only provided for
+/// those that have terminated and been waited for (see [`super::wait::wait`]).
+///
+/// Some information may be missing depending on the platform, and the way information
+/// is provided for children may also vary. Check the manuals for details.
+///
+/// # References
+///
+/// * [getrusage(2)](https://pubs.opengroup.org/onlinepubs/009696699/functions/getrusage.html)
+/// * [Linux](https://man7.org/linux/man-pages/man2/getrusage.2.html)
+/// * [FreeBSD](https://www.freebsd.org/cgi/man.cgi?query=getrusage)
+/// * [NetBSD](https://man.netbsd.org/getrusage.2)
+/// * [MacOS](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/getrusage.2.html)
+///
+/// [`UsageWho`]: enum.UsageWho.html
+///
+/// Note: `getrusage` provides a safe wrapper to libc's [`libc::getrusage`].
+pub fn getrusage(who: UsageWho) -> Result<Usage> {
+    unsafe {
+        let mut rusage = mem::MaybeUninit::<rusage>::uninit();
+        let res = libc::getrusage(who as c_int, rusage.as_mut_ptr());
+        Errno::result(res).map(|_| Usage(rusage.assume_init()))
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::{getrusage, UsageWho};
+
+    #[test]
+    pub fn test_self_cpu_time() {
+        // Make sure some CPU time is used.
+        let mut numbers: Vec<i32> = (1..1_000_000).collect();
+        numbers.iter_mut().for_each(|item| *item *= 2);
+
+        // FIXME: this is here to help ensure the compiler does not optimize the whole
+        // thing away. Replace the assert with test::black_box once stabilized.
+        assert_eq!(numbers[100..200].iter().sum::<i32>(), 30_100);
+
+        let usage = getrusage(UsageWho::RUSAGE_SELF)
+            .expect("Failed to call getrusage for SELF");
+        let rusage = usage.as_ref();
+
+        let user = usage.user_time();
+        assert!(user.tv_sec() > 0 || user.tv_usec() > 0);
+        assert_eq!(user.tv_sec(), rusage.ru_utime.tv_sec);
+        assert_eq!(user.tv_usec(), rusage.ru_utime.tv_usec);
+    }
+}
diff --git a/src/sys/select.rs b/src/sys/select.rs
index 4d7576a..7a94cff 100644
--- a/src/sys/select.rs
+++ b/src/sys/select.rs
@@ -1,15 +1,14 @@
 //! Portably monitor a group of file descriptors for readiness.
+use crate::errno::Errno;
+use crate::sys::time::{TimeSpec, TimeVal};
+use crate::Result;
+use libc::{self, c_int};
 use std::convert::TryFrom;
 use std::iter::FusedIterator;
 use std::mem;
 use std::ops::Range;
 use std::os::unix::io::RawFd;
 use std::ptr::{null, null_mut};
-use libc::{self, c_int};
-use crate::Result;
-use crate::errno::Errno;
-use crate::sys::signal::SigSet;
-use crate::sys::time::{TimeSpec, TimeVal};
 
 pub use libc::FD_SETSIZE;
 
@@ -174,11 +173,13 @@
 /// [select(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/select.html)
 ///
 /// [`FdSet::highest`]: struct.FdSet.html#method.highest
-pub fn select<'a, N, R, W, E, T>(nfds: N,
+pub fn select<'a, N, R, W, E, T>(
+    nfds: N,
     readfds: R,
     writefds: W,
     errorfds: E,
-                                 timeout: T) -> Result<c_int>
+    timeout: T,
+) -> Result<c_int>
 where
     N: Into<Option<c_int>>,
     R: Into<Option<&'a mut FdSet>>,
@@ -192,27 +193,40 @@
     let timeout = timeout.into();
 
     let nfds = nfds.into().unwrap_or_else(|| {
-        readfds.iter_mut()
+        readfds
+            .iter_mut()
             .chain(writefds.iter_mut())
             .chain(errorfds.iter_mut())
             .map(|set| set.highest().unwrap_or(-1))
             .max()
-            .unwrap_or(-1) + 1
+            .unwrap_or(-1)
+            + 1
     });
 
-    let readfds = readfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
-    let writefds = writefds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
-    let errorfds = errorfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
-    let timeout = timeout.map(|tv| tv as *mut _ as *mut libc::timeval)
+    let readfds = readfds
+        .map(|set| set as *mut _ as *mut libc::fd_set)
+        .unwrap_or(null_mut());
+    let writefds = writefds
+        .map(|set| set as *mut _ as *mut libc::fd_set)
+        .unwrap_or(null_mut());
+    let errorfds = errorfds
+        .map(|set| set as *mut _ as *mut libc::fd_set)
+        .unwrap_or(null_mut());
+    let timeout = timeout
+        .map(|tv| tv as *mut _ as *mut libc::timeval)
         .unwrap_or(null_mut());
 
-    let res = unsafe {
-        libc::select(nfds, readfds, writefds, errorfds, timeout)
-    };
+    let res =
+        unsafe { libc::select(nfds, readfds, writefds, errorfds, timeout) };
 
     Errno::result(res)
 }
 
+feature! {
+#![feature = "signal"]
+
+use crate::sys::signal::SigSet;
+
 /// Monitors file descriptors for readiness with an altered signal mask.
 ///
 /// Returns the total number of ready file descriptors in all sets. The sets are changed so that all
@@ -283,14 +297,14 @@
 
     Errno::result(res)
 }
-
+}
 
 #[cfg(test)]
 mod tests {
     use super::*;
-    use std::os::unix::io::RawFd;
     use crate::sys::time::{TimeVal, TimeValLike};
-    use crate::unistd::{write, pipe};
+    use crate::unistd::{pipe, write};
+    use std::os::unix::io::RawFd;
 
     #[test]
     fn fdset_insert() {
@@ -379,11 +393,10 @@
         fd_set.insert(r2);
 
         let mut timeout = TimeVal::seconds(10);
-        assert_eq!(1, select(None,
-                             &mut fd_set,
-                             None,
-                             None,
-                             &mut timeout).unwrap());
+        assert_eq!(
+            1,
+            select(None, &mut fd_set, None, None, &mut timeout).unwrap()
+        );
         assert!(fd_set.contains(r1));
         assert!(!fd_set.contains(r2));
     }
@@ -399,11 +412,17 @@
         fd_set.insert(r2);
 
         let mut timeout = TimeVal::seconds(10);
-        assert_eq!(1, select(Some(fd_set.highest().unwrap() + 1),
+        assert_eq!(
+            1,
+            select(
+                Some(fd_set.highest().unwrap() + 1),
                 &mut fd_set,
                 None,
                 None,
-                             &mut timeout).unwrap());
+                &mut timeout
+            )
+            .unwrap()
+        );
         assert!(fd_set.contains(r1));
         assert!(!fd_set.contains(r2));
     }
@@ -419,11 +438,17 @@
         fd_set.insert(r2);
 
         let mut timeout = TimeVal::seconds(10);
-        assert_eq!(1, select(::std::cmp::max(r1, r2) + 1,
+        assert_eq!(
+            1,
+            select(
+                ::std::cmp::max(r1, r2) + 1,
                 &mut fd_set,
                 None,
                 None,
-                             &mut timeout).unwrap());
+                &mut timeout
+            )
+            .unwrap()
+        );
         assert!(fd_set.contains(r1));
         assert!(!fd_set.contains(r2));
     }
diff --git a/src/sys/sendfile.rs b/src/sys/sendfile.rs
index 7a210c6..fb293a4 100644
--- a/src/sys/sendfile.rs
+++ b/src/sys/sendfile.rs
@@ -6,8 +6,8 @@
 
 use libc::{self, off_t};
 
-use crate::Result;
 use crate::errno::Errno;
+use crate::Result;
 
 /// Copy up to `count` bytes to `out_fd` from `in_fd` starting at `offset`.
 ///
@@ -22,6 +22,7 @@
 ///
 /// For more information, see [the sendfile(2) man page.](https://man7.org/linux/man-pages/man2/sendfile.2.html)
 #[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
 pub fn sendfile(
     out_fd: RawFd,
     in_fd: RawFd,
@@ -48,6 +49,7 @@
 ///
 /// For more information, see [the sendfile(2) man page.](https://man7.org/linux/man-pages/man2/sendfile.2.html)
 #[cfg(target_os = "linux")]
+#[cfg_attr(docsrs, doc(cfg(all())))]
 pub fn sendfile64(
     out_fd: RawFd,
     in_fd: RawFd,
@@ -62,16 +64,17 @@
 }
 
 cfg_if! {
-    if #[cfg(any(target_os = "freebsd",
+    if #[cfg(any(target_os = "dragonfly",
+                 target_os = "freebsd",
                  target_os = "ios",
                  target_os = "macos"))] {
-        use crate::sys::uio::IoVec;
+        use std::io::IoSlice;
 
-        #[derive(Clone, Debug, Eq, Hash, PartialEq)]
+        #[derive(Clone, Debug)]
         struct SendfileHeaderTrailer<'a>(
             libc::sf_hdtr,
-            Option<Vec<IoVec<&'a [u8]>>>,
-            Option<Vec<IoVec<&'a [u8]>>>,
+            Option<Vec<IoSlice<'a>>>,
+            Option<Vec<IoSlice<'a>>>,
         );
 
         impl<'a> SendfileHeaderTrailer<'a> {
@@ -79,10 +82,10 @@
                 headers: Option<&'a [&'a [u8]]>,
                 trailers: Option<&'a [&'a [u8]]>
             ) -> SendfileHeaderTrailer<'a> {
-                let header_iovecs: Option<Vec<IoVec<&[u8]>>> =
-                    headers.map(|s| s.iter().map(|b| IoVec::from_slice(b)).collect());
-                let trailer_iovecs: Option<Vec<IoVec<&[u8]>>> =
-                    trailers.map(|s| s.iter().map(|b| IoVec::from_slice(b)).collect());
+                let header_iovecs: Option<Vec<IoSlice<'_>>> =
+                    headers.map(|s| s.iter().map(|b| IoSlice::new(b)).collect());
+                let trailer_iovecs: Option<Vec<IoSlice<'_>>> =
+                    trailers.map(|s| s.iter().map(|b| IoSlice::new(b)).collect());
                 SendfileHeaderTrailer(
                     libc::sf_hdtr {
                         headers: {
@@ -182,6 +185,49 @@
             };
             (Errno::result(return_code).and(Ok(())), bytes_sent)
         }
+    } else if #[cfg(target_os = "dragonfly")] {
+        /// Read up to `count` bytes from `in_fd` starting at `offset` and write to `out_sock`.
+        ///
+        /// Returns a `Result` and a count of bytes written. Bytes written may be non-zero even if
+        /// an error occurs.
+        ///
+        /// `in_fd` must describe a regular file. `out_sock` must describe a stream socket.
+        ///
+        /// If `offset` falls past the end of the file, the function returns success and zero bytes
+        /// written.
+        ///
+        /// If `count` is `None` or 0, bytes will be read from `in_fd` until reaching the end of
+        /// file (EOF).
+        ///
+        /// `headers` and `trailers` specify optional slices of byte slices to be sent before and
+        /// after the data read from `in_fd`, respectively. The length of headers and trailers sent
+        /// is included in the returned count of bytes written. The values of `offset` and `count`
+        /// do not apply to headers or trailers.
+        ///
+        /// For more information, see
+        /// [the sendfile(2) man page.](https://leaf.dragonflybsd.org/cgi/web-man?command=sendfile&section=2)
+        pub fn sendfile(
+            in_fd: RawFd,
+            out_sock: RawFd,
+            offset: off_t,
+            count: Option<usize>,
+            headers: Option<&[&[u8]]>,
+            trailers: Option<&[&[u8]]>,
+        ) -> (Result<()>, off_t) {
+            let mut bytes_sent: off_t = 0;
+            let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers));
+            let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.0 as *const libc::sf_hdtr);
+            let return_code = unsafe {
+                libc::sendfile(in_fd,
+                               out_sock,
+                               offset,
+                               count.unwrap_or(0),
+                               hdtr_ptr as *mut libc::sf_hdtr,
+                               &mut bytes_sent as *mut off_t,
+                               0)
+            };
+            (Errno::result(return_code).and(Ok(())), bytes_sent)
+        }
     } else if #[cfg(any(target_os = "ios", target_os = "macos"))] {
         /// Read bytes from `in_fd` starting at `offset` and write up to `count` bytes to
         /// `out_sock`.
diff --git a/src/sys/signal.rs b/src/sys/signal.rs
index 61bdc74..d3746e6 100644
--- a/src/sys/signal.rs
+++ b/src/sys/signal.rs
@@ -3,20 +3,22 @@
 
 //! Operating system signals.
 
-use crate::{Error, Result};
 use crate::errno::Errno;
-use crate::unistd::Pid;
-use std::mem;
+use crate::{Error, Result};
+use cfg_if::cfg_if;
 use std::fmt;
-use std::str::FromStr;
+use std::mem;
 #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
 use std::os::unix::io::RawFd;
 use std::ptr;
+use std::str::FromStr;
 
 #[cfg(not(any(target_os = "openbsd", target_os = "redox")))]
+#[cfg(any(feature = "aio", feature = "signal"))]
 pub use self::sigevent::*;
 
-libc_enum!{
+#[cfg(any(feature = "aio", feature = "process", feature = "signal"))]
+libc_enum! {
     /// Types of operating system signals
     // Currently there is only one definition of c_int in libc, as well as only one
     // type for signal constants.
@@ -24,6 +26,7 @@
     // this is not (yet) possible.
     #[repr(i32)]
     #[non_exhaustive]
+    #[cfg_attr(docsrs, doc(cfg(any(feature = "aio", feature = "signal"))))]
     pub enum Signal {
         /// Hangup
         SIGHUP,
@@ -86,27 +89,33 @@
         /// Window size changes
         SIGWINCH,
         /// Input/output possible signal
+        #[cfg(not(target_os = "haiku"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         SIGIO,
         #[cfg(any(target_os = "android", target_os = "emscripten",
                   target_os = "fuchsia", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         /// Power failure imminent.
         SIGPWR,
         /// Bad system call
         SIGSYS,
         #[cfg(not(any(target_os = "android", target_os = "emscripten",
                       target_os = "fuchsia", target_os = "linux",
-                      target_os = "redox")))]
+                      target_os = "redox", target_os = "haiku")))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         /// Emulator trap
         SIGEMT,
         #[cfg(not(any(target_os = "android", target_os = "emscripten",
                       target_os = "fuchsia", target_os = "linux",
-                      target_os = "redox")))]
+                      target_os = "redox", target_os = "haiku")))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         /// Information request
         SIGINFO,
     }
     impl TryFrom<i32>
 }
 
+#[cfg(feature = "signal")]
 impl FromStr for Signal {
     type Err = Error;
     fn from_str(s: &str) -> Result<Signal> {
@@ -126,10 +135,19 @@
             "SIGPIPE" => Signal::SIGPIPE,
             "SIGALRM" => Signal::SIGALRM,
             "SIGTERM" => Signal::SIGTERM,
-            #[cfg(all(any(target_os = "android", target_os = "emscripten",
-                          target_os = "fuchsia", target_os = "linux"),
-                      not(any(target_arch = "mips", target_arch = "mips64",
-                              target_arch = "sparc64"))))]
+            #[cfg(all(
+                any(
+                    target_os = "android",
+                    target_os = "emscripten",
+                    target_os = "fuchsia",
+                    target_os = "linux"
+                ),
+                not(any(
+                    target_arch = "mips",
+                    target_arch = "mips64",
+                    target_arch = "sparc64"
+                ))
+            ))]
             "SIGSTKFLT" => Signal::SIGSTKFLT,
             "SIGCHLD" => Signal::SIGCHLD,
             "SIGCONT" => Signal::SIGCONT,
@@ -143,24 +161,40 @@
             "SIGVTALRM" => Signal::SIGVTALRM,
             "SIGPROF" => Signal::SIGPROF,
             "SIGWINCH" => Signal::SIGWINCH,
+            #[cfg(not(target_os = "haiku"))]
             "SIGIO" => Signal::SIGIO,
-            #[cfg(any(target_os = "android", target_os = "emscripten",
-                      target_os = "fuchsia", target_os = "linux"))]
+            #[cfg(any(
+                target_os = "android",
+                target_os = "emscripten",
+                target_os = "fuchsia",
+                target_os = "linux"
+            ))]
             "SIGPWR" => Signal::SIGPWR,
             "SIGSYS" => Signal::SIGSYS,
-            #[cfg(not(any(target_os = "android", target_os = "emscripten",
-                          target_os = "fuchsia", target_os = "linux",
-                          target_os = "redox")))]
+            #[cfg(not(any(
+                target_os = "android",
+                target_os = "emscripten",
+                target_os = "fuchsia",
+                target_os = "linux",
+                target_os = "redox",
+                target_os = "haiku"
+            )))]
             "SIGEMT" => Signal::SIGEMT,
-            #[cfg(not(any(target_os = "android", target_os = "emscripten",
-                          target_os = "fuchsia", target_os = "linux",
-                          target_os = "redox")))]
+            #[cfg(not(any(
+                target_os = "android",
+                target_os = "emscripten",
+                target_os = "fuchsia",
+                target_os = "linux",
+                target_os = "redox",
+                target_os = "haiku"
+            )))]
             "SIGINFO" => Signal::SIGINFO,
             _ => return Err(Errno::EINVAL),
         })
     }
 }
 
+#[cfg(feature = "signal")]
 impl Signal {
     /// Returns name of signal.
     ///
@@ -184,9 +218,19 @@
             Signal::SIGPIPE => "SIGPIPE",
             Signal::SIGALRM => "SIGALRM",
             Signal::SIGTERM => "SIGTERM",
-            #[cfg(all(any(target_os = "android", target_os = "emscripten",
-                          target_os = "fuchsia", target_os = "linux"),
-                      not(any(target_arch = "mips", target_arch = "mips64", target_arch = "sparc64"))))]
+            #[cfg(all(
+                any(
+                    target_os = "android",
+                    target_os = "emscripten",
+                    target_os = "fuchsia",
+                    target_os = "linux"
+                ),
+                not(any(
+                    target_arch = "mips",
+                    target_arch = "mips64",
+                    target_arch = "sparc64"
+                ))
+            ))]
             Signal::SIGSTKFLT => "SIGSTKFLT",
             Signal::SIGCHLD => "SIGCHLD",
             Signal::SIGCONT => "SIGCONT",
@@ -200,174 +244,125 @@
             Signal::SIGVTALRM => "SIGVTALRM",
             Signal::SIGPROF => "SIGPROF",
             Signal::SIGWINCH => "SIGWINCH",
+            #[cfg(not(target_os = "haiku"))]
             Signal::SIGIO => "SIGIO",
-            #[cfg(any(target_os = "android", target_os = "emscripten",
-                      target_os = "fuchsia", target_os = "linux"))]
+            #[cfg(any(
+                target_os = "android",
+                target_os = "emscripten",
+                target_os = "fuchsia",
+                target_os = "linux"
+            ))]
             Signal::SIGPWR => "SIGPWR",
             Signal::SIGSYS => "SIGSYS",
-            #[cfg(not(any(target_os = "android", target_os = "emscripten",
-                          target_os = "fuchsia", target_os = "linux",
-                          target_os = "redox")))]
+            #[cfg(not(any(
+                target_os = "android",
+                target_os = "emscripten",
+                target_os = "fuchsia",
+                target_os = "linux",
+                target_os = "redox",
+                target_os = "haiku"
+            )))]
             Signal::SIGEMT => "SIGEMT",
-            #[cfg(not(any(target_os = "android", target_os = "emscripten",
-                          target_os = "fuchsia", target_os = "linux",
-                          target_os = "redox")))]
+            #[cfg(not(any(
+                target_os = "android",
+                target_os = "emscripten",
+                target_os = "fuchsia",
+                target_os = "linux",
+                target_os = "redox",
+                target_os = "haiku"
+            )))]
             Signal::SIGINFO => "SIGINFO",
         }
     }
 }
 
+#[cfg(feature = "signal")]
 impl AsRef<str> for Signal {
     fn as_ref(&self) -> &str {
         self.as_str()
     }
 }
 
+#[cfg(feature = "signal")]
 impl fmt::Display for Signal {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         f.write_str(self.as_ref())
     }
 }
 
+#[cfg(feature = "signal")]
 pub use self::Signal::*;
 
 #[cfg(target_os = "redox")]
+#[cfg(feature = "signal")]
 const SIGNALS: [Signal; 29] = [
-    SIGHUP,
-    SIGINT,
-    SIGQUIT,
-    SIGILL,
-    SIGTRAP,
-    SIGABRT,
-    SIGBUS,
-    SIGFPE,
-    SIGKILL,
-    SIGUSR1,
-    SIGSEGV,
-    SIGUSR2,
-    SIGPIPE,
-    SIGALRM,
-    SIGTERM,
-    SIGCHLD,
-    SIGCONT,
-    SIGSTOP,
-    SIGTSTP,
-    SIGTTIN,
-    SIGTTOU,
-    SIGURG,
-    SIGXCPU,
-    SIGXFSZ,
-    SIGVTALRM,
-    SIGPROF,
-    SIGWINCH,
-    SIGIO,
-    SIGSYS];
-#[cfg(all(any(target_os = "linux", target_os = "android",
-              target_os = "emscripten", target_os = "fuchsia"),
-          not(any(target_arch = "mips", target_arch = "mips64",
-                  target_arch = "sparc64"))))]
+    SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGBUS, SIGFPE, SIGKILL,
+    SIGUSR1, SIGSEGV, SIGUSR2, SIGPIPE, SIGALRM, SIGTERM, SIGCHLD, SIGCONT,
+    SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGXCPU, SIGXFSZ, SIGVTALRM,
+    SIGPROF, SIGWINCH, SIGIO, SIGSYS,
+];
+#[cfg(target_os = "haiku")]
+#[cfg(feature = "signal")]
+const SIGNALS: [Signal; 28] = [
+    SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGBUS, SIGFPE, SIGKILL,
+    SIGUSR1, SIGSEGV, SIGUSR2, SIGPIPE, SIGALRM, SIGTERM, SIGCHLD, SIGCONT,
+    SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGXCPU, SIGXFSZ, SIGVTALRM,
+    SIGPROF, SIGWINCH, SIGSYS,
+];
+#[cfg(all(
+    any(
+        target_os = "linux",
+        target_os = "android",
+        target_os = "emscripten",
+        target_os = "fuchsia"
+    ),
+    not(any(
+        target_arch = "mips",
+        target_arch = "mips64",
+        target_arch = "sparc64"
+    ))
+))]
+#[cfg(feature = "signal")]
 const SIGNALS: [Signal; 31] = [
-    SIGHUP,
-    SIGINT,
-    SIGQUIT,
-    SIGILL,
-    SIGTRAP,
-    SIGABRT,
-    SIGBUS,
-    SIGFPE,
-    SIGKILL,
-    SIGUSR1,
-    SIGSEGV,
-    SIGUSR2,
-    SIGPIPE,
-    SIGALRM,
-    SIGTERM,
-    SIGSTKFLT,
-    SIGCHLD,
-    SIGCONT,
-    SIGSTOP,
-    SIGTSTP,
-    SIGTTIN,
-    SIGTTOU,
-    SIGURG,
-    SIGXCPU,
-    SIGXFSZ,
-    SIGVTALRM,
-    SIGPROF,
-    SIGWINCH,
-    SIGIO,
-    SIGPWR,
-    SIGSYS];
-#[cfg(all(any(target_os = "linux", target_os = "android",
-              target_os = "emscripten", target_os = "fuchsia"),
-          any(target_arch = "mips", target_arch = "mips64",
-              target_arch = "sparc64")))]
+    SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGBUS, SIGFPE, SIGKILL,
+    SIGUSR1, SIGSEGV, SIGUSR2, SIGPIPE, SIGALRM, SIGTERM, SIGSTKFLT, SIGCHLD,
+    SIGCONT, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGXCPU, SIGXFSZ,
+    SIGVTALRM, SIGPROF, SIGWINCH, SIGIO, SIGPWR, SIGSYS,
+];
+#[cfg(all(
+    any(
+        target_os = "linux",
+        target_os = "android",
+        target_os = "emscripten",
+        target_os = "fuchsia"
+    ),
+    any(target_arch = "mips", target_arch = "mips64", target_arch = "sparc64")
+))]
+#[cfg(feature = "signal")]
 const SIGNALS: [Signal; 30] = [
-    SIGHUP,
-    SIGINT,
-    SIGQUIT,
-    SIGILL,
-    SIGTRAP,
-    SIGABRT,
-    SIGBUS,
-    SIGFPE,
-    SIGKILL,
-    SIGUSR1,
-    SIGSEGV,
-    SIGUSR2,
-    SIGPIPE,
-    SIGALRM,
-    SIGTERM,
-    SIGCHLD,
-    SIGCONT,
-    SIGSTOP,
-    SIGTSTP,
-    SIGTTIN,
-    SIGTTOU,
-    SIGURG,
-    SIGXCPU,
-    SIGXFSZ,
-    SIGVTALRM,
-    SIGPROF,
-    SIGWINCH,
-    SIGIO,
-    SIGPWR,
-    SIGSYS];
-#[cfg(not(any(target_os = "linux", target_os = "android",
-              target_os = "fuchsia", target_os = "emscripten",
-              target_os = "redox")))]
+    SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGBUS, SIGFPE, SIGKILL,
+    SIGUSR1, SIGSEGV, SIGUSR2, SIGPIPE, SIGALRM, SIGTERM, SIGCHLD, SIGCONT,
+    SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGXCPU, SIGXFSZ, SIGVTALRM,
+    SIGPROF, SIGWINCH, SIGIO, SIGPWR, SIGSYS,
+];
+#[cfg(not(any(
+    target_os = "linux",
+    target_os = "android",
+    target_os = "fuchsia",
+    target_os = "emscripten",
+    target_os = "redox",
+    target_os = "haiku"
+)))]
+#[cfg(feature = "signal")]
 const SIGNALS: [Signal; 31] = [
-    SIGHUP,
-    SIGINT,
-    SIGQUIT,
-    SIGILL,
-    SIGTRAP,
-    SIGABRT,
-    SIGBUS,
-    SIGFPE,
-    SIGKILL,
-    SIGUSR1,
-    SIGSEGV,
-    SIGUSR2,
-    SIGPIPE,
-    SIGALRM,
-    SIGTERM,
-    SIGCHLD,
-    SIGCONT,
-    SIGSTOP,
-    SIGTSTP,
-    SIGTTIN,
-    SIGTTOU,
-    SIGURG,
-    SIGXCPU,
-    SIGXFSZ,
-    SIGVTALRM,
-    SIGPROF,
-    SIGWINCH,
-    SIGIO,
-    SIGSYS,
-    SIGEMT,
-    SIGINFO];
+    SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGBUS, SIGFPE, SIGKILL,
+    SIGUSR1, SIGSEGV, SIGUSR2, SIGPIPE, SIGALRM, SIGTERM, SIGCHLD, SIGCONT,
+    SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGXCPU, SIGXFSZ, SIGVTALRM,
+    SIGPROF, SIGWINCH, SIGIO, SIGSYS, SIGEMT, SIGINFO,
+];
+
+feature! {
+#![feature = "signal"]
 
 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
 /// Iterate through all signals defined by this operating system
@@ -399,17 +394,26 @@
 /// Alias for [`SIGABRT`]
 pub const SIGIOT : Signal = SIGABRT;
 /// Alias for [`SIGIO`]
+#[cfg(not(target_os = "haiku"))]
 pub const SIGPOLL : Signal = SIGIO;
 /// Alias for [`SIGSYS`]
 pub const SIGUNUSED : Signal = SIGSYS;
 
-#[cfg(not(target_os = "redox"))]
-type SaFlags_t = libc::c_int;
-#[cfg(target_os = "redox")]
-type SaFlags_t = libc::c_ulong;
+cfg_if! {
+    if #[cfg(target_os = "redox")] {
+        type SaFlags_t = libc::c_ulong;
+    } else if #[cfg(target_env = "uclibc")] {
+        type SaFlags_t = libc::c_ulong;
+    } else {
+        type SaFlags_t = libc::c_int;
+    }
+}
+}
 
-libc_bitflags!{
+#[cfg(feature = "signal")]
+libc_bitflags! {
     /// Controls the behavior of a [`SigAction`]
+    #[cfg_attr(docsrs, doc(cfg(feature = "signal")))]
     pub struct SaFlags: SaFlags_t {
         /// When catching a [`Signal::SIGCHLD`] signal, the signal will be
         /// generated only when a child process exits, not when a child process
@@ -435,10 +439,12 @@
     }
 }
 
+#[cfg(feature = "signal")]
 libc_enum! {
     /// Specifies how certain functions should manipulate a signal mask
     #[repr(i32)]
     #[non_exhaustive]
+    #[cfg_attr(docsrs, doc(cfg(feature = "signal")))]
     pub enum SigmaskHow {
         /// The new mask is the union of the current mask and the specified set.
         SIG_BLOCK,
@@ -450,15 +456,26 @@
     }
 }
 
+feature! {
+#![feature = "signal"]
+
+use crate::unistd::Pid;
+use std::iter::Extend;
+use std::iter::FromIterator;
+use std::iter::IntoIterator;
+
 /// Specifies a set of [`Signal`]s that may be blocked, waited for, etc.
+// We are using `transparent` here to be super sure that `SigSet`
+// is represented exactly like the `sigset_t` struct from C.
+#[repr(transparent)]
 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
 pub struct SigSet {
     sigset: libc::sigset_t
 }
 
-
 impl SigSet {
     /// Initialize to include all signals.
+    #[doc(alias("sigfillset"))]
     pub fn all() -> SigSet {
         let mut sigset = mem::MaybeUninit::uninit();
         let _ = unsafe { libc::sigfillset(sigset.as_mut_ptr()) };
@@ -467,6 +484,7 @@
     }
 
     /// Initialize to include nothing.
+    #[doc(alias("sigemptyset"))]
     pub fn empty() -> SigSet {
         let mut sigset = mem::MaybeUninit::uninit();
         let _ = unsafe { libc::sigemptyset(sigset.as_mut_ptr()) };
@@ -475,21 +493,25 @@
     }
 
     /// Add the specified signal to the set.
+    #[doc(alias("sigaddset"))]
     pub fn add(&mut self, signal: Signal) {
         unsafe { libc::sigaddset(&mut self.sigset as *mut libc::sigset_t, signal as libc::c_int) };
     }
 
     /// Remove all signals from this set.
+    #[doc(alias("sigemptyset"))]
     pub fn clear(&mut self) {
         unsafe { libc::sigemptyset(&mut self.sigset as *mut libc::sigset_t) };
     }
 
     /// Remove the specified signal from this set.
+    #[doc(alias("sigdelset"))]
     pub fn remove(&mut self, signal: Signal) {
         unsafe { libc::sigdelset(&mut self.sigset as *mut libc::sigset_t, signal as libc::c_int) };
     }
 
     /// Return whether this set includes the specified signal.
+    #[doc(alias("sigismember"))]
     pub fn contains(&self, signal: Signal) -> bool {
         let res = unsafe { libc::sigismember(&self.sigset as *const libc::sigset_t, signal as libc::c_int) };
 
@@ -500,14 +522,9 @@
         }
     }
 
-    /// Merge all of `other`'s signals into this set.
-    // TODO: use libc::sigorset on supported operating systems.
-    pub fn extend(&mut self, other: &SigSet) {
-        for signal in Signal::iterator() {
-            if other.contains(signal) {
-                self.add(signal);
-            }
-        }
+    /// Returns an iterator that yields the signals contained in this set.
+    pub fn iter(&self) -> SigSetIter<'_> {
+        self.into_iter()
     }
 
     /// Gets the currently blocked (masked) set of signals for the calling thread.
@@ -542,6 +559,7 @@
     /// Suspends execution of the calling thread until one of the signals in the
     /// signal mask becomes pending, and returns the accepted signal.
     #[cfg(not(target_os = "redox"))] // RedoxFS does not yet support sigwait
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn wait(&self) -> Result<Signal> {
         use std::convert::TryFrom;
 
@@ -552,6 +570,19 @@
             Signal::try_from(signum.assume_init()).unwrap()
         })
     }
+
+    /// Converts a `libc::sigset_t` object to a [`SigSet`] without checking  whether the
+    /// `libc::sigset_t` is already initialized.
+    ///
+    /// # Safety
+    ///
+    /// The `sigset` passed in must be a valid an initialized `libc::sigset_t` by calling either
+    /// [`sigemptyset(3)`](https://man7.org/linux/man-pages/man3/sigemptyset.3p.html) or
+    /// [`sigfillset(3)`](https://man7.org/linux/man-pages/man3/sigfillset.3p.html).
+    /// Otherwise, the results are undefined.
+    pub unsafe fn from_sigset_t_unchecked(sigset: libc::sigset_t) -> SigSet {
+        SigSet { sigset }
+    }
 }
 
 impl AsRef<libc::sigset_t> for SigSet {
@@ -560,6 +591,55 @@
     }
 }
 
+// TODO: Consider specialization for the case where T is &SigSet and libc::sigorset is available.
+impl Extend<Signal> for SigSet {
+    fn extend<T>(&mut self, iter: T)
+    where T: IntoIterator<Item = Signal> {
+        for signal in iter {
+            self.add(signal);
+        }
+    }
+}
+
+impl FromIterator<Signal> for SigSet {
+    fn from_iter<T>(iter: T) -> Self
+    where T: IntoIterator<Item = Signal> {
+        let mut sigset = SigSet::empty();
+        sigset.extend(iter);
+        sigset
+    }
+}
+
+/// Iterator for a [`SigSet`].
+///
+/// Call [`SigSet::iter`] to create an iterator.
+#[derive(Clone, Debug)]
+pub struct SigSetIter<'a> {
+    sigset: &'a SigSet,
+    inner: SignalIterator,
+}
+
+impl Iterator for SigSetIter<'_> {
+    type Item = Signal;
+    fn next(&mut self) -> Option<Signal> {
+        loop {
+            match self.inner.next() {
+                None => return None,
+                Some(signal) if self.sigset.contains(signal) => return Some(signal),
+                Some(_signal) => continue,
+            }
+        }
+    }
+}
+
+impl<'a> IntoIterator for &'a SigSet {
+    type Item = Signal;
+    type IntoIter = SigSetIter<'a>;
+    fn into_iter(self) -> Self::IntoIter {
+        SigSetIter { sigset: self, inner: Signal::iterator() }
+    }
+}
+
 /// A signal handler.
 #[allow(unknown_lints)]
 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
@@ -573,6 +653,7 @@
     /// Use the given signal-catching function, which takes in the signal, information about how
     /// the signal was generated, and a pointer to the threads `ucontext_t`.
     #[cfg(not(target_os = "redox"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     SigAction(extern fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void))
 }
 
@@ -589,21 +670,12 @@
     /// is the `SigAction` variant). `mask` specifies other signals to block during execution of
     /// the signal-catching function.
     pub fn new(handler: SigHandler, flags: SaFlags, mask: SigSet) -> SigAction {
-        #[cfg(target_os = "redox")]
-        unsafe fn install_sig(p: *mut libc::sigaction, handler: SigHandler) {
-            (*p).sa_handler = match handler {
-                SigHandler::SigDfl => libc::SIG_DFL,
-                SigHandler::SigIgn => libc::SIG_IGN,
-                SigHandler::Handler(f) => f as *const extern fn(libc::c_int) as usize,
-            };
-        }
-
-        #[cfg(not(target_os = "redox"))]
         unsafe fn install_sig(p: *mut libc::sigaction, handler: SigHandler) {
             (*p).sa_sigaction = match handler {
                 SigHandler::SigDfl => libc::SIG_DFL,
                 SigHandler::SigIgn => libc::SIG_IGN,
                 SigHandler::Handler(f) => f as *const extern fn(libc::c_int) as usize,
+                #[cfg(not(target_os = "redox"))]
                 SigHandler::SigAction(f) => f as *const extern fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void) as usize,
             };
         }
@@ -635,11 +707,11 @@
     }
 
     /// Returns the action's handler.
-    #[cfg(not(target_os = "redox"))]
     pub fn handler(&self) -> SigHandler {
         match self.sigaction.sa_sigaction {
             libc::SIG_DFL => SigHandler::SigDfl,
             libc::SIG_IGN => SigHandler::SigIgn,
+            #[cfg(not(target_os = "redox"))]
             p if self.flags().contains(SaFlags::SA_SIGINFO) =>
                 SigHandler::SigAction(
                 // Safe for one of two reasons:
@@ -667,27 +739,6 @@
                 as extern fn(libc::c_int)),
         }
     }
-
-    /// Returns the action's handler.
-    #[cfg(target_os = "redox")]
-    pub fn handler(&self) -> SigHandler {
-        match self.sigaction.sa_handler {
-            libc::SIG_DFL => SigHandler::SigDfl,
-            libc::SIG_IGN => SigHandler::SigIgn,
-            p => SigHandler::Handler(
-                // Safe for one of two reasons:
-                // * The SigHandler was created by SigHandler::new, in which
-                //   case the pointer is correct, or
-                // * The SigHandler was created by signal or sigaction, which
-                //   are unsafe functions, so the caller should've somehow
-                //   ensured that it is correctly initialized.
-                unsafe{
-                    *(&p as *const usize
-                         as *const extern fn(libc::c_int))
-                }
-                as extern fn(libc::c_int)),
-        }
-    }
 }
 
 /// Changes the action taken by a process on receipt of a specific signal.
@@ -836,7 +887,7 @@
 
 /// Examine and change blocked signals.
 ///
-/// For more informations see the [`sigprocmask` man
+/// For more information see the [`sigprocmask` man
 /// pages](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigprocmask.html).
 pub fn sigprocmask(how: SigmaskHow, set: Option<&SigSet>, oldset: Option<&mut SigSet>) -> Result<()> {
     if set.is_none() && oldset.is_none() {
@@ -860,10 +911,11 @@
 /// # Arguments
 ///
 /// * `pid` -    Specifies which processes should receive the signal.
-///   - If positive, specifies an individual process
+///   - If positive, specifies an individual process.
 ///   - If zero, the signal will be sent to all processes whose group
 ///     ID is equal to the process group ID of the sender.  This is a
-///     variant of [`killpg`].
+#[cfg_attr(target_os = "fuchsia", doc = "variant of `killpg`.")]
+#[cfg_attr(not(target_os = "fuchsia"), doc = "variant of [`killpg`].")]
 ///   - If `-1` and the process has super-user privileges, the signal
 ///     is sent to all processes exclusing system processes.
 ///   - If less than `-1`, the signal is sent to all processes whose
@@ -912,7 +964,10 @@
 
     Errno::result(res).map(drop)
 }
+}
 
+feature! {
+#![any(feature = "aio", feature = "signal")]
 
 /// Identifies a thread for [`SigevNotify::SigevThreadId`]
 #[cfg(target_os = "freebsd")]
@@ -942,6 +997,7 @@
     // expose a way to set the union members needed by SIGEV_THREAD.
     /// Notify by delivering an event to a kqueue.
     #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     SigevKevent {
         /// File descriptor of the kqueue to notify.
         kq: RawFd,
@@ -950,6 +1006,7 @@
     },
     /// Notify by delivering a signal to a thread.
     #[cfg(any(target_os = "freebsd", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     SigevThreadId {
         /// Signal to send
         signal: Signal,
@@ -960,9 +1017,14 @@
         si_value: libc::intptr_t
     },
 }
+}
 
 #[cfg(not(any(target_os = "openbsd", target_os = "redox")))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
 mod sigevent {
+    feature! {
+    #![any(feature = "aio", feature = "signal")]
+
     use std::mem;
     use std::ptr;
     use super::SigevNotify;
@@ -1005,6 +1067,8 @@
                 SigevNotify::SigevThreadId{..} => libc::SIGEV_THREAD_ID,
                 #[cfg(all(target_os = "linux", target_env = "gnu", not(target_arch = "mips")))]
                 SigevNotify::SigevThreadId{..} => libc::SIGEV_THREAD_ID,
+                #[cfg(all(target_os = "linux", target_env = "uclibc"))]
+                SigevNotify::SigevThreadId{..} => libc::SIGEV_THREAD_ID,
                 #[cfg(any(all(target_os = "linux", target_env = "musl"), target_arch = "mips"))]
                 SigevNotify::SigevThreadId{..} => 4  // No SIGEV_THREAD_ID defined
             };
@@ -1044,6 +1108,11 @@
         pub fn sigevent(&self) -> libc::sigevent {
             self.sigevent
         }
+
+        /// Returns a mutable pointer to the `sigevent` wrapped by `self`
+        pub fn as_mut_ptr(&mut self) -> *mut libc::sigevent {
+            &mut self.sigevent
+        }
     }
 
     impl<'a> From<&'a libc::sigevent> for SigEvent {
@@ -1051,13 +1120,14 @@
             SigEvent{ sigevent: *sigevent }
         }
     }
+    }
 }
 
 #[cfg(test)]
 mod tests {
+    use super::*;
     #[cfg(not(target_os = "redox"))]
     use std::thread;
-    use super::*;
 
     #[test]
     fn test_contains() {
@@ -1120,15 +1190,19 @@
             let mut test_mask = prev_mask;
             test_mask.add(SIGUSR1);
 
-            assert!(test_mask.thread_set_mask().is_ok());
-            let new_mask = SigSet::thread_get_mask()
-                .expect("Failed to get new mask!");
+            test_mask.thread_set_mask().expect("assertion failed");
+            let new_mask =
+                SigSet::thread_get_mask().expect("Failed to get new mask!");
 
             assert!(new_mask.contains(SIGUSR1));
             assert!(!new_mask.contains(SIGUSR2));
 
-            prev_mask.thread_set_mask().expect("Failed to revert signal mask!");
-        }).join().unwrap();
+            prev_mask
+                .thread_set_mask()
+                .expect("Failed to revert signal mask!");
+        })
+        .join()
+        .unwrap();
     }
 
     #[test]
@@ -1138,10 +1212,12 @@
             let mut mask = SigSet::empty();
             mask.add(SIGUSR1);
 
-            assert!(mask.thread_block().is_ok());
+            mask.thread_block().expect("assertion failed");
 
             assert!(SigSet::thread_get_mask().unwrap().contains(SIGUSR1));
-        }).join().unwrap();
+        })
+        .join()
+        .unwrap();
     }
 
     #[test]
@@ -1151,10 +1227,12 @@
             let mut mask = SigSet::empty();
             mask.add(SIGUSR1);
 
-            assert!(mask.thread_unblock().is_ok());
+            mask.thread_unblock().expect("assertion failed");
 
             assert!(!SigSet::thread_get_mask().unwrap().contains(SIGUSR1));
-        }).join().unwrap();
+        })
+        .join()
+        .unwrap();
     }
 
     #[test]
@@ -1170,36 +1248,51 @@
             let mut mask2 = SigSet::empty();
             mask2.add(SIGUSR2);
 
-            let oldmask = mask2.thread_swap_mask(SigmaskHow::SIG_SETMASK)
-                .unwrap();
+            let oldmask =
+                mask2.thread_swap_mask(SigmaskHow::SIG_SETMASK).unwrap();
 
             assert!(oldmask.contains(SIGUSR1));
             assert!(!oldmask.contains(SIGUSR2));
 
             assert!(SigSet::thread_get_mask().unwrap().contains(SIGUSR2));
-        }).join().unwrap();
+        })
+        .join()
+        .unwrap();
+    }
+
+    #[test]
+    fn test_from_and_into_iterator() {
+        let sigset = SigSet::from_iter(vec![Signal::SIGUSR1, Signal::SIGUSR2]);
+        let signals = sigset.into_iter().collect::<Vec<Signal>>();
+        assert_eq!(signals, [Signal::SIGUSR1, Signal::SIGUSR2]);
     }
 
     #[test]
     #[cfg(not(target_os = "redox"))]
     fn test_sigaction() {
         thread::spawn(|| {
-            extern fn test_sigaction_handler(_: libc::c_int) {}
-            extern fn test_sigaction_action(_: libc::c_int,
-                _: *mut libc::siginfo_t, _: *mut libc::c_void) {}
+            extern "C" fn test_sigaction_handler(_: libc::c_int) {}
+            extern "C" fn test_sigaction_action(
+                _: libc::c_int,
+                _: *mut libc::siginfo_t,
+                _: *mut libc::c_void,
+            ) {
+            }
 
             let handler_sig = SigHandler::Handler(test_sigaction_handler);
 
-            let flags = SaFlags::SA_ONSTACK | SaFlags::SA_RESTART |
-                        SaFlags::SA_SIGINFO;
+            let flags =
+                SaFlags::SA_ONSTACK | SaFlags::SA_RESTART | SaFlags::SA_SIGINFO;
 
             let mut mask = SigSet::empty();
             mask.add(SIGUSR1);
 
             let action_sig = SigAction::new(handler_sig, flags, mask);
 
-            assert_eq!(action_sig.flags(),
-                       SaFlags::SA_ONSTACK | SaFlags::SA_RESTART);
+            assert_eq!(
+                action_sig.flags(),
+                SaFlags::SA_ONSTACK | SaFlags::SA_RESTART
+            );
             assert_eq!(action_sig.handler(), handler_sig);
 
             mask = action_sig.mask();
@@ -1215,7 +1308,9 @@
 
             let action_ign = SigAction::new(SigHandler::SigIgn, flags, mask);
             assert_eq!(action_ign.handler(), SigHandler::SigIgn);
-        }).join().unwrap();
+        })
+        .join()
+        .unwrap();
     }
 
     #[test]
@@ -1229,6 +1324,25 @@
 
             raise(SIGUSR1).unwrap();
             assert_eq!(mask.wait().unwrap(), SIGUSR1);
-        }).join().unwrap();
+        })
+        .join()
+        .unwrap();
+    }
+
+    #[test]
+    fn test_from_sigset_t_unchecked() {
+        let src_set = SigSet::empty();
+        let set = unsafe { SigSet::from_sigset_t_unchecked(src_set.sigset) };
+
+        for signal in Signal::iterator() {
+            assert!(!set.contains(signal));
+        }
+
+        let src_set = SigSet::all();
+        let set = unsafe { SigSet::from_sigset_t_unchecked(src_set.sigset) };
+
+        for signal in Signal::iterator() {
+            assert!(set.contains(signal));
+        }
     }
 }
diff --git a/src/sys/signalfd.rs b/src/sys/signalfd.rs
index bc4a452..095e590 100644
--- a/src/sys/signalfd.rs
+++ b/src/sys/signalfd.rs
@@ -15,17 +15,16 @@
 //!
 //! Please note that signal discarding is not specific to `signalfd`, but also happens with regular
 //! signal handlers.
-use crate::unistd;
-use crate::Result;
 use crate::errno::Errno;
 pub use crate::sys::signal::{self, SigSet};
+use crate::unistd;
+use crate::Result;
 pub use libc::signalfd_siginfo as siginfo;
 
-use std::os::unix::io::{RawFd, AsRawFd};
 use std::mem;
+use std::os::unix::io::{AsRawFd, RawFd};
 
-
-libc_bitflags!{
+libc_bitflags! {
     pub struct SfdFlags: libc::c_int {
         SFD_NONBLOCK;
         SFD_CLOEXEC;
@@ -49,7 +48,11 @@
 /// See [the signalfd man page for more information](https://man7.org/linux/man-pages/man2/signalfd.2.html)
 pub fn signalfd(fd: RawFd, mask: &SigSet, flags: SfdFlags) -> Result<RawFd> {
     unsafe {
-        Errno::result(libc::signalfd(fd as libc::c_int, mask.as_ref(), flags.bits()))
+        Errno::result(libc::signalfd(
+            fd as libc::c_int,
+            mask.as_ref(),
+            flags.bits(),
+        ))
     }
 }
 
@@ -103,12 +106,13 @@
         let size = mem::size_of_val(&buffer);
         let res = Errno::result(unsafe {
             libc::read(self.0, buffer.as_mut_ptr() as *mut libc::c_void, size)
-        }).map(|r| r as usize);
+        })
+        .map(|r| r as usize);
         match res {
             Ok(x) if x == size => Ok(Some(unsafe { buffer.assume_init() })),
             Ok(_) => unreachable!("partial read on signalfd"),
             Err(Errno::EAGAIN) => Ok(None),
-            Err(error) => Err(error)
+            Err(error) => Err(error),
         }
     }
 }
@@ -139,7 +143,6 @@
     }
 }
 
-
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -147,21 +150,24 @@
     #[test]
     fn create_signalfd() {
         let mask = SigSet::empty();
-        let fd = SignalFd::new(&mask);
-        assert!(fd.is_ok());
+        SignalFd::new(&mask).unwrap();
     }
 
     #[test]
     fn create_signalfd_with_opts() {
         let mask = SigSet::empty();
-        let fd = SignalFd::with_flags(&mask, SfdFlags::SFD_CLOEXEC | SfdFlags::SFD_NONBLOCK);
-        assert!(fd.is_ok());
+        SignalFd::with_flags(
+            &mask,
+            SfdFlags::SFD_CLOEXEC | SfdFlags::SFD_NONBLOCK,
+        )
+        .unwrap();
     }
 
     #[test]
     fn read_empty_signalfd() {
         let mask = SigSet::empty();
-        let mut fd = SignalFd::with_flags(&mask, SfdFlags::SFD_NONBLOCK).unwrap();
+        let mut fd =
+            SignalFd::with_flags(&mask, SfdFlags::SFD_NONBLOCK).unwrap();
 
         let res = fd.read_signal();
         assert!(res.unwrap().is_none());
diff --git a/src/sys/socket/addr.rs b/src/sys/socket/addr.rs
index b119642..4e565a5 100644
--- a/src/sys/socket/addr.rs
+++ b/src/sys/socket/addr.rs
@@ -1,36 +1,71 @@
-use super::sa_family_t;
-use crate::{Result, NixPath};
-use crate::errno::Errno;
-use memoffset::offset_of;
-use std::{fmt, mem, net, ptr, slice};
-use std::ffi::OsStr;
-use std::hash::{Hash, Hasher};
-use std::path::Path;
-use std::os::unix::ffi::OsStrExt;
-#[cfg(any(target_os = "android", target_os = "linux"))]
-use crate::sys::socket::addr::netlink::NetlinkAddr;
-#[cfg(any(target_os = "android", target_os = "linux"))]
-use crate::sys::socket::addr::alg::AlgAddr;
-#[cfg(any(target_os = "ios", target_os = "macos"))]
-use std::os::unix::io::RawFd;
-#[cfg(any(target_os = "ios", target_os = "macos"))]
-use crate::sys::socket::addr::sys_control::SysControlAddr;
-#[cfg(any(target_os = "android",
-          target_os = "dragonfly",
-          target_os = "freebsd",
-          target_os = "ios",
-          target_os = "linux",
-          target_os = "macos",
-          target_os = "illumos",
-          target_os = "netbsd",
-          target_os = "openbsd",
-          target_os = "fuchsia"))]
+#[cfg(any(
+    target_os = "android",
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "ios",
+    target_os = "linux",
+    target_os = "macos",
+    target_os = "illumos",
+    target_os = "netbsd",
+    target_os = "openbsd",
+    target_os = "haiku",
+    target_os = "fuchsia"
+))]
+#[cfg(feature = "net")]
 pub use self::datalink::LinkAddr;
 #[cfg(any(target_os = "android", target_os = "linux"))]
 pub use self::vsock::VsockAddr;
+use super::sa_family_t;
+use crate::errno::Errno;
+#[cfg(any(target_os = "android", target_os = "linux"))]
+use crate::sys::socket::addr::alg::AlgAddr;
+#[cfg(any(target_os = "android", target_os = "linux"))]
+use crate::sys::socket::addr::netlink::NetlinkAddr;
+#[cfg(all(
+    feature = "ioctl",
+    any(target_os = "ios", target_os = "macos")
+))]
+use crate::sys::socket::addr::sys_control::SysControlAddr;
+use crate::{NixPath, Result};
+use cfg_if::cfg_if;
+use memoffset::offset_of;
+use std::convert::TryInto;
+use std::ffi::OsStr;
+use std::hash::{Hash, Hasher};
+use std::os::unix::ffi::OsStrExt;
+#[cfg(any(target_os = "ios", target_os = "macos"))]
+use std::os::unix::io::RawFd;
+use std::path::Path;
+use std::{fmt, mem, net, ptr, slice};
+
+/// Convert a std::net::Ipv4Addr into the libc form.
+#[cfg(feature = "net")]
+pub(crate) const fn ipv4addr_to_libc(addr: net::Ipv4Addr) -> libc::in_addr {
+    static_assertions::assert_eq_size!(net::Ipv4Addr, libc::in_addr);
+    // Safe because both types have the same memory layout, and no fancy Drop
+    // impls.
+    unsafe {
+        mem::transmute(addr)
+    }
+}
+
+/// Convert a std::net::Ipv6Addr into the libc form.
+#[cfg(feature = "net")]
+pub(crate) const fn ipv6addr_to_libc(addr: &net::Ipv6Addr) -> libc::in6_addr {
+    static_assertions::assert_eq_size!(net::Ipv6Addr, libc::in6_addr);
+    // Safe because both are Newtype wrappers around the same libc type
+    unsafe {
+        mem::transmute(*addr)
+    }
+}
 
 /// These constants specify the protocol family to be used
 /// in [`socket`](fn.socket.html) and [`socketpair`](fn.socketpair.html)
+///
+/// # References
+///
+/// [address_families(7)](https://man7.org/linux/man-pages/man7/address_families.7.html)
+// Should this be u8?
 #[repr(i32)]
 #[non_exhaustive]
 #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
@@ -43,191 +78,331 @@
     Inet6 = libc::AF_INET6,
     /// Kernel user interface device (see [`netlink(7)`](https://man7.org/linux/man-pages/man7/netlink.7.html))
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Netlink = libc::AF_NETLINK,
     /// Low level packet interface (see [`packet(7)`](https://man7.org/linux/man-pages/man7/packet.7.html))
-    #[cfg(any(target_os = "android",
-              target_os = "linux",
-              target_os = "illumos",
-              target_os = "fuchsia",
-              target_os = "solaris"))]
+    #[cfg(any(
+        target_os = "android",
+        target_os = "linux",
+        target_os = "illumos",
+        target_os = "fuchsia",
+        target_os = "solaris"
+    ))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Packet = libc::AF_PACKET,
     /// KEXT Controls and Notifications
     #[cfg(any(target_os = "ios", target_os = "macos"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     System = libc::AF_SYSTEM,
     /// Amateur radio AX.25 protocol
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Ax25 = libc::AF_AX25,
     /// IPX - Novell protocols
     Ipx = libc::AF_IPX,
     /// AppleTalk
     AppleTalk = libc::AF_APPLETALK,
+    /// AX.25 packet layer protocol.
+    /// (see [netrom(4)](https://www.unix.com/man-page/linux/4/netrom/))
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     NetRom = libc::AF_NETROM,
+    /// Can't be used for creating sockets; mostly used for bridge
+    /// links in
+    /// [rtnetlink(7)](https://man7.org/linux/man-pages/man7/rtnetlink.7.html)
+    /// protocol commands.
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Bridge = libc::AF_BRIDGE,
     /// Access to raw ATM PVCs
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     AtmPvc = libc::AF_ATMPVC,
     /// ITU-T X.25 / ISO-8208 protocol (see [`x25(7)`](https://man7.org/linux/man-pages/man7/x25.7.html))
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     X25 = libc::AF_X25,
+    /// RATS (Radio Amateur Telecommunications Society) Open
+    /// Systems environment (ROSE) AX.25 packet layer protocol.
+    /// (see [netrom(4)](https://www.unix.com/man-page/linux/4/netrom/))
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Rose = libc::AF_ROSE,
+    /// DECet protocol sockets.
+    #[cfg(not(target_os = "haiku"))]
     Decnet = libc::AF_DECnet,
+    /// Reserved for "802.2LLC project"; never used.
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     NetBeui = libc::AF_NETBEUI,
+    /// This was a short-lived (between Linux 2.1.30 and
+    /// 2.1.99pre2) protocol family for firewall upcalls.
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Security = libc::AF_SECURITY,
+    /// Key management protocol.
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Key = libc::AF_KEY,
+    #[allow(missing_docs)] // Not documented anywhere that I can find
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Ash = libc::AF_ASH,
+    /// Acorn Econet protocol
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Econet = libc::AF_ECONET,
+    /// Access to ATM Switched Virtual Circuits
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     AtmSvc = libc::AF_ATMSVC,
+    /// Reliable Datagram Sockets (RDS) protocol
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Rds = libc::AF_RDS,
+    /// IBM SNA
+    #[cfg(not(target_os = "haiku"))]
     Sna = libc::AF_SNA,
+    /// Socket interface over IrDA
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Irda = libc::AF_IRDA,
+    /// Generic PPP transport layer, for setting up L2 tunnels (L2TP and PPPoE)
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Pppox = libc::AF_PPPOX,
+    /// Legacy protocol for wide area network (WAN) connectivity that was used
+    /// by Sangoma WAN cards
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Wanpipe = libc::AF_WANPIPE,
+    /// Logical link control (IEEE 802.2 LLC) protocol
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Llc = libc::AF_LLC,
-    #[cfg(target_os = "linux")]
+    /// InfiniBand native addressing
+    #[cfg(all(target_os = "linux", not(target_env = "uclibc")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Ib = libc::AF_IB,
-    #[cfg(target_os = "linux")]
+    /// Multiprotocol Label Switching
+    #[cfg(all(target_os = "linux", not(target_env = "uclibc")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Mpls = libc::AF_MPLS,
+    /// Controller Area Network automotive bus protocol
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Can = libc::AF_CAN,
+    /// TIPC, "cluster domain sockets" protocol
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Tipc = libc::AF_TIPC,
-    #[cfg(not(any(target_os = "illumos",
-                  target_os = "ios",
-                  target_os = "macos",
-                  target_os = "solaris")))]
+    /// Bluetooth low-level socket protocol
+    #[cfg(not(any(
+        target_os = "illumos",
+        target_os = "ios",
+        target_os = "macos",
+        target_os = "solaris"
+    )))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Bluetooth = libc::AF_BLUETOOTH,
+    /// IUCV (inter-user communication vehicle) z/VM protocol for
+    /// hypervisor-guest interaction
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Iucv = libc::AF_IUCV,
+    /// Rx, Andrew File System remote procedure call protocol
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     RxRpc = libc::AF_RXRPC,
-    #[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
+    /// New "modular ISDN" driver interface protocol
+    #[cfg(not(any(
+        target_os = "illumos",
+        target_os = "solaris",
+        target_os = "haiku"
+    )))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Isdn = libc::AF_ISDN,
+    /// Nokia cellular modem IPC/RPC interface
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Phonet = libc::AF_PHONET,
+    /// IEEE 802.15.4 WPAN (wireless personal area network) raw packet protocol
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Ieee802154 = libc::AF_IEEE802154,
+    /// Ericsson's Communication CPU to Application CPU interface (CAIF)
+    /// protocol.
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Caif = libc::AF_CAIF,
     /// Interface to kernel crypto API
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Alg = libc::AF_ALG,
+    /// Near field communication
     #[cfg(target_os = "linux")]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Nfc = libc::AF_NFC,
+    /// VMWare VSockets protocol for hypervisor-guest interaction.
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Vsock = libc::AF_VSOCK,
-    #[cfg(any(target_os = "dragonfly",
-              target_os = "freebsd",
-              target_os = "ios",
-              target_os = "macos",
-              target_os = "netbsd",
-              target_os = "openbsd"))]
+    /// ARPANet IMP addresses
+    #[cfg(any(
+        target_os = "dragonfly",
+        target_os = "freebsd",
+        target_os = "ios",
+        target_os = "macos",
+        target_os = "netbsd",
+        target_os = "openbsd"
+    ))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     ImpLink = libc::AF_IMPLINK,
-    #[cfg(any(target_os = "dragonfly",
-              target_os = "freebsd",
-              target_os = "ios",
-              target_os = "macos",
-              target_os = "netbsd",
-              target_os = "openbsd"))]
+    /// PUP protocols, e.g. BSP
+    #[cfg(any(
+        target_os = "dragonfly",
+        target_os = "freebsd",
+        target_os = "ios",
+        target_os = "macos",
+        target_os = "netbsd",
+        target_os = "openbsd"
+    ))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Pup = libc::AF_PUP,
-    #[cfg(any(target_os = "dragonfly",
-              target_os = "freebsd",
-              target_os = "ios",
-              target_os = "macos",
-              target_os = "netbsd",
-              target_os = "openbsd"))]
+    /// MIT CHAOS protocols
+    #[cfg(any(
+        target_os = "dragonfly",
+        target_os = "freebsd",
+        target_os = "ios",
+        target_os = "macos",
+        target_os = "netbsd",
+        target_os = "openbsd"
+    ))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Chaos = libc::AF_CHAOS,
-    #[cfg(any(target_os = "ios",
-              target_os = "macos",
-              target_os = "netbsd",
-              target_os = "openbsd"))]
+    /// Novell and Xerox protocol
+    #[cfg(any(
+        target_os = "ios",
+        target_os = "macos",
+        target_os = "netbsd",
+        target_os = "openbsd"
+    ))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Ns = libc::AF_NS,
-    #[cfg(any(target_os = "dragonfly",
-              target_os = "freebsd",
-              target_os = "ios",
-              target_os = "macos",
-              target_os = "netbsd",
-              target_os = "openbsd"))]
+    #[allow(missing_docs)] // Not documented anywhere that I can find
+    #[cfg(any(
+        target_os = "dragonfly",
+        target_os = "freebsd",
+        target_os = "ios",
+        target_os = "macos",
+        target_os = "netbsd",
+        target_os = "openbsd"
+    ))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Iso = libc::AF_ISO,
-    #[cfg(any(target_os = "dragonfly",
-              target_os = "freebsd",
-              target_os = "ios",
-              target_os = "macos",
-              target_os = "netbsd",
-              target_os = "openbsd"))]
+    /// Bell Labs virtual circuit switch ?
+    #[cfg(any(
+        target_os = "dragonfly",
+        target_os = "freebsd",
+        target_os = "ios",
+        target_os = "macos",
+        target_os = "netbsd",
+        target_os = "openbsd"
+    ))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Datakit = libc::AF_DATAKIT,
-    #[cfg(any(target_os = "dragonfly",
-              target_os = "freebsd",
-              target_os = "ios",
-              target_os = "macos",
-              target_os = "netbsd",
-              target_os = "openbsd"))]
+    /// CCITT protocols, X.25 etc
+    #[cfg(any(
+        target_os = "dragonfly",
+        target_os = "freebsd",
+        target_os = "ios",
+        target_os = "macos",
+        target_os = "netbsd",
+        target_os = "openbsd"
+    ))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Ccitt = libc::AF_CCITT,
-    #[cfg(any(target_os = "dragonfly",
-              target_os = "freebsd",
-              target_os = "ios",
-              target_os = "macos",
-              target_os = "netbsd",
-              target_os = "openbsd"))]
+    /// DEC Direct data link interface
+    #[cfg(any(
+        target_os = "dragonfly",
+        target_os = "freebsd",
+        target_os = "ios",
+        target_os = "macos",
+        target_os = "netbsd",
+        target_os = "openbsd"
+    ))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Dli = libc::AF_DLI,
-    #[cfg(any(target_os = "dragonfly",
-              target_os = "freebsd",
-              target_os = "ios",
-              target_os = "macos",
-              target_os = "netbsd",
-              target_os = "openbsd"))]
+    #[allow(missing_docs)] // Not documented anywhere that I can find
+    #[cfg(any(
+        target_os = "dragonfly",
+        target_os = "freebsd",
+        target_os = "ios",
+        target_os = "macos",
+        target_os = "netbsd",
+        target_os = "openbsd"
+    ))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Lat = libc::AF_LAT,
-    #[cfg(any(target_os = "dragonfly",
-              target_os = "freebsd",
-              target_os = "ios",
-              target_os = "macos",
-              target_os = "netbsd",
-              target_os = "openbsd"))]
+    /// NSC Hyperchannel
+    #[cfg(any(
+        target_os = "dragonfly",
+        target_os = "freebsd",
+        target_os = "ios",
+        target_os = "macos",
+        target_os = "netbsd",
+        target_os = "openbsd"
+    ))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Hylink = libc::AF_HYLINK,
-    #[cfg(any(target_os = "dragonfly",
-              target_os = "freebsd",
-              target_os = "ios",
-              target_os = "macos",
-              target_os = "illumos",
-              target_os = "netbsd",
-              target_os = "openbsd"))]
+    /// Link layer interface
+    #[cfg(any(
+        target_os = "dragonfly",
+        target_os = "freebsd",
+        target_os = "ios",
+        target_os = "macos",
+        target_os = "illumos",
+        target_os = "netbsd",
+        target_os = "openbsd"
+    ))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Link = libc::AF_LINK,
-    #[cfg(any(target_os = "dragonfly",
-              target_os = "freebsd",
-              target_os = "ios",
-              target_os = "macos",
-              target_os = "netbsd",
-              target_os = "openbsd"))]
+    /// connection-oriented IP, aka ST II
+    #[cfg(any(
+        target_os = "dragonfly",
+        target_os = "freebsd",
+        target_os = "ios",
+        target_os = "macos",
+        target_os = "netbsd",
+        target_os = "openbsd"
+    ))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Coip = libc::AF_COIP,
-    #[cfg(any(target_os = "dragonfly",
-              target_os = "freebsd",
-              target_os = "ios",
-              target_os = "macos",
-              target_os = "netbsd",
-              target_os = "openbsd"))]
+    /// Computer Network Technology
+    #[cfg(any(
+        target_os = "dragonfly",
+        target_os = "freebsd",
+        target_os = "ios",
+        target_os = "macos",
+        target_os = "netbsd",
+        target_os = "openbsd"
+    ))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Cnt = libc::AF_CNT,
-    #[cfg(any(target_os = "dragonfly",
-              target_os = "freebsd",
-              target_os = "ios",
-              target_os = "macos",
-              target_os = "netbsd",
-              target_os = "openbsd"))]
+    /// Native ATM access
+    #[cfg(any(
+        target_os = "dragonfly",
+        target_os = "freebsd",
+        target_os = "ios",
+        target_os = "macos",
+        target_os = "netbsd",
+        target_os = "openbsd"
+    ))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Natm = libc::AF_NATM,
     /// Unspecified address family, (see [`getaddrinfo(3)`](https://man7.org/linux/man-pages/man3/getaddrinfo.3.html))
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Unspec = libc::AF_UNSPEC,
 }
 
@@ -248,33 +423,50 @@
             libc::AF_SYSTEM => Some(AddressFamily::System),
             #[cfg(any(target_os = "android", target_os = "linux"))]
             libc::AF_PACKET => Some(AddressFamily::Packet),
-            #[cfg(any(target_os = "dragonfly",
-                      target_os = "freebsd",
-                      target_os = "ios",
-                      target_os = "macos",
-                      target_os = "netbsd",
-                      target_os = "illumos",
-                      target_os = "openbsd"))]
+            #[cfg(any(
+                target_os = "dragonfly",
+                target_os = "freebsd",
+                target_os = "ios",
+                target_os = "macos",
+                target_os = "netbsd",
+                target_os = "illumos",
+                target_os = "openbsd"
+            ))]
             libc::AF_LINK => Some(AddressFamily::Link),
             #[cfg(any(target_os = "android", target_os = "linux"))]
             libc::AF_VSOCK => Some(AddressFamily::Vsock),
-            _ => None
+            _ => None,
         }
     }
 }
 
+feature! {
+#![feature = "net"]
+
+#[deprecated(
+    since = "0.24.0",
+    note = "use SockaddrIn, SockaddrIn6, or SockaddrStorage instead"
+)]
+#[allow(missing_docs)]  // Since they're all deprecated anyway
 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
 pub enum InetAddr {
     V4(libc::sockaddr_in),
     V6(libc::sockaddr_in6),
 }
 
+#[allow(missing_docs)]  // It's deprecated anyway
+#[allow(deprecated)]
 impl InetAddr {
     #[allow(clippy::needless_update)]   // It isn't needless on all OSes
     pub fn from_std(std: &net::SocketAddr) -> InetAddr {
         match *std {
             net::SocketAddr::V4(ref addr) => {
                 InetAddr::V4(libc::sockaddr_in {
+                    #[cfg(any(target_os = "dragonfly", target_os = "freebsd",
+                              target_os = "haiku", target_os = "hermit",
+                              target_os = "ios", target_os = "macos",
+                              target_os = "netbsd", target_os = "openbsd"))]
+                    sin_len: mem::size_of::<libc::sockaddr_in>() as u8,
                     sin_family: AddressFamily::Inet as sa_family_t,
                     sin_port: addr.port().to_be(),  // network byte order
                     sin_addr: Ipv4Addr::from_std(addr.ip()).0,
@@ -283,6 +475,11 @@
             }
             net::SocketAddr::V6(ref addr) => {
                 InetAddr::V6(libc::sockaddr_in6 {
+                    #[cfg(any(target_os = "dragonfly", target_os = "freebsd",
+                              target_os = "haiku", target_os = "hermit",
+                              target_os = "ios", target_os = "macos",
+                              target_os = "netbsd", target_os = "openbsd"))]
+                    sin6_len: mem::size_of::<libc::sockaddr_in6>() as u8,
                     sin6_family: AddressFamily::Inet6 as sa_family_t,
                     sin6_port: addr.port().to_be(),  // network byte order
                     sin6_addr: Ipv6Addr::from_std(addr.ip()).0,
@@ -352,6 +549,7 @@
     }
 }
 
+#[allow(deprecated)]
 impl fmt::Display for InetAddr {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match *self {
@@ -366,12 +564,20 @@
  * ===== IpAddr =====
  *
  */
+#[allow(missing_docs)]  // Since they're all deprecated anyway
+#[allow(deprecated)]
 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[deprecated(
+    since = "0.24.0",
+    note = "Use std::net::IpAddr instead"
+)]
 pub enum IpAddr {
     V4(Ipv4Addr),
     V6(Ipv6Addr),
 }
 
+#[allow(deprecated)]
+#[allow(missing_docs)]  // Since they're all deprecated anyway
 impl IpAddr {
     /// Create a new IpAddr that contains an IPv4 address.
     ///
@@ -404,6 +610,7 @@
     }
 }
 
+#[allow(deprecated)]
 impl fmt::Display for IpAddr {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match *self {
@@ -419,9 +626,17 @@
  *
  */
 
+#[deprecated(
+    since = "0.24.0",
+    note = "Use std::net::Ipv4Addr instead"
+)]
+#[allow(missing_docs)]  // Since they're all deprecated anyway
 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[repr(transparent)]
 pub struct Ipv4Addr(pub libc::in_addr);
 
+#[allow(deprecated)]
+#[allow(missing_docs)]  // Since they're all deprecated anyway
 impl Ipv4Addr {
     #[allow(clippy::identity_op)]   // More readable this way
     pub const fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr {
@@ -455,6 +670,7 @@
     }
 }
 
+#[allow(deprecated)]
 impl fmt::Display for Ipv4Addr {
     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
         let octets = self.octets();
@@ -468,7 +684,13 @@
  *
  */
 
+#[deprecated(
+    since = "0.24.0",
+    note = "Use std::net::Ipv6Addr instead"
+)]
+#[allow(missing_docs)]  // Since they're all deprecated anyway
 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[repr(transparent)]
 pub struct Ipv6Addr(pub libc::in6_addr);
 
 // Note that IPv6 addresses are stored in big endian order on all architectures.
@@ -487,6 +709,8 @@
     }
 }
 
+#[allow(deprecated)]
+#[allow(missing_docs)]  // Since they're all deprecated anyway
 impl Ipv6Addr {
     #[allow(clippy::many_single_char_names)]
     #[allow(clippy::too_many_arguments)]
@@ -510,18 +734,30 @@
     }
 }
 
+#[allow(deprecated)]
 impl fmt::Display for Ipv6Addr {
     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
         self.to_std().fmt(fmt)
     }
 }
+}
 
 /// A wrapper around `sockaddr_un`.
 #[derive(Clone, Copy, Debug)]
+#[repr(C)]
 pub struct UnixAddr {
-    // INVARIANT: sun & path_len are valid as defined by docs for from_raw_parts
+    // INVARIANT: sun & sun_len are valid as defined by docs for from_raw_parts
     sun: libc::sockaddr_un,
-    path_len: usize,
+    /// The length of the valid part of `sun`, including the sun_family field
+    /// but excluding any trailing nul.
+    // On the BSDs, this field is built into sun
+    #[cfg(any(
+        target_os = "android",
+        target_os = "fuchsia",
+        target_os = "illumos",
+        target_os = "linux"
+    ))]
+    sun_len: u8,
 }
 
 // linux man page unix(7) says there are 3 kinds of unix socket:
@@ -538,97 +774,164 @@
     Abstract(&'a [u8]),
 }
 impl<'a> UnixAddrKind<'a> {
-    /// Safety: sun & path_len must be valid
-    unsafe fn get(sun: &'a libc::sockaddr_un, path_len: usize) -> Self {
+    /// Safety: sun & sun_len must be valid
+    unsafe fn get(sun: &'a libc::sockaddr_un, sun_len: u8) -> Self {
+        assert!(sun_len as usize >= offset_of!(libc::sockaddr_un, sun_path));
+        let path_len =
+            sun_len as usize - offset_of!(libc::sockaddr_un, sun_path);
         if path_len == 0 {
             return Self::Unnamed;
         }
         #[cfg(any(target_os = "android", target_os = "linux"))]
         if sun.sun_path[0] == 0 {
-            let name =
-                slice::from_raw_parts(sun.sun_path.as_ptr().add(1) as *const u8, path_len - 1);
+            let name = slice::from_raw_parts(
+                sun.sun_path.as_ptr().add(1) as *const u8,
+                path_len - 1,
+            );
             return Self::Abstract(name);
         }
-        let pathname = slice::from_raw_parts(sun.sun_path.as_ptr() as *const u8, path_len - 1);
-        Self::Pathname(Path::new(OsStr::from_bytes(pathname)))
+        let pathname =
+            slice::from_raw_parts(sun.sun_path.as_ptr() as *const u8, path_len);
+        if pathname.last() == Some(&0) {
+            // A trailing NUL is not considered part of the path, and it does
+            // not need to be included in the addrlen passed to functions like
+            // bind().  However, Linux adds a trailing NUL, even if one was not
+            // originally present, when returning addrs from functions like
+            // getsockname() (the BSDs do not do that).  So we need to filter
+            // out any trailing NUL here, so sockaddrs can round-trip through
+            // the kernel and still compare equal.
+            Self::Pathname(Path::new(OsStr::from_bytes(
+                &pathname[0..pathname.len() - 1],
+            )))
+        } else {
+            Self::Pathname(Path::new(OsStr::from_bytes(pathname)))
+        }
     }
 }
 
 impl UnixAddr {
     /// Create a new sockaddr_un representing a filesystem path.
     pub fn new<P: ?Sized + NixPath>(path: &P) -> Result<UnixAddr> {
-        path.with_nix_path(|cstr| {
-            unsafe {
-                let mut ret = libc::sockaddr_un {
-                    sun_family: AddressFamily::Unix as sa_family_t,
-                    .. mem::zeroed()
-                };
+        path.with_nix_path(|cstr| unsafe {
+            let mut ret = libc::sockaddr_un {
+                sun_family: AddressFamily::Unix as sa_family_t,
+                ..mem::zeroed()
+            };
 
-                let bytes = cstr.to_bytes();
+            let bytes = cstr.to_bytes();
 
-                if bytes.len() >= ret.sun_path.len() {
-                    return Err(Errno::ENAMETOOLONG);
-                }
-
-                ptr::copy_nonoverlapping(bytes.as_ptr(),
-                                         ret.sun_path.as_mut_ptr() as *mut u8,
-                                         bytes.len());
-
-                Ok(UnixAddr::from_raw_parts(ret, bytes.len() + 1))
+            if bytes.len() >= ret.sun_path.len() {
+                return Err(Errno::ENAMETOOLONG);
             }
+
+            let sun_len = (bytes.len()
+                + offset_of!(libc::sockaddr_un, sun_path))
+            .try_into()
+            .unwrap();
+
+            #[cfg(any(
+                target_os = "dragonfly",
+                target_os = "freebsd",
+                target_os = "ios",
+                target_os = "macos",
+                target_os = "netbsd",
+                target_os = "openbsd"
+            ))]
+            {
+                ret.sun_len = sun_len;
+            }
+            ptr::copy_nonoverlapping(
+                bytes.as_ptr(),
+                ret.sun_path.as_mut_ptr() as *mut u8,
+                bytes.len(),
+            );
+
+            Ok(UnixAddr::from_raw_parts(ret, sun_len))
         })?
     }
 
     /// Create a new `sockaddr_un` representing an address in the "abstract namespace".
     ///
-    /// The leading null byte for the abstract namespace is automatically added;
-    /// thus the input `path` is expected to be the bare name, not null-prefixed.
+    /// The leading nul byte for the abstract namespace is automatically added;
+    /// thus the input `path` is expected to be the bare name, not NUL-prefixed.
     /// This is a Linux-specific extension, primarily used to allow chrooted
     /// processes to communicate with processes having a different filesystem view.
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn new_abstract(path: &[u8]) -> Result<UnixAddr> {
         unsafe {
             let mut ret = libc::sockaddr_un {
                 sun_family: AddressFamily::Unix as sa_family_t,
-                .. mem::zeroed()
+                ..mem::zeroed()
             };
 
             if path.len() >= ret.sun_path.len() {
                 return Err(Errno::ENAMETOOLONG);
             }
+            let sun_len =
+                (path.len() + 1 + offset_of!(libc::sockaddr_un, sun_path))
+                    .try_into()
+                    .unwrap();
 
             // Abstract addresses are represented by sun_path[0] ==
             // b'\0', so copy starting one byte in.
-            ptr::copy_nonoverlapping(path.as_ptr(),
-                                     ret.sun_path.as_mut_ptr().offset(1) as *mut u8,
-                                     path.len());
+            ptr::copy_nonoverlapping(
+                path.as_ptr(),
+                ret.sun_path.as_mut_ptr().offset(1) as *mut u8,
+                path.len(),
+            );
 
-            Ok(UnixAddr::from_raw_parts(ret, path.len() + 1))
+            Ok(UnixAddr::from_raw_parts(ret, sun_len))
         }
     }
 
-    /// Create a UnixAddr from a raw `sockaddr_un` struct and a size. `path_len` is the "addrlen"
-    /// of this address, but minus `offsetof(struct sockaddr_un, sun_path)`. Basically the length
-    /// of the data in `sun_path`.
+    /// Create a new `sockaddr_un` representing an "unnamed" unix socket address.
+    #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
+    pub fn new_unnamed() -> UnixAddr {
+        let ret = libc::sockaddr_un {
+            sun_family: AddressFamily::Unix as sa_family_t,
+            .. unsafe { mem::zeroed() }
+        };
+
+        let sun_len: u8 = offset_of!(libc::sockaddr_un, sun_path).try_into().unwrap();
+
+        unsafe { UnixAddr::from_raw_parts(ret, sun_len) }
+    }
+
+    /// Create a UnixAddr from a raw `sockaddr_un` struct and a size. `sun_len`
+    /// is the size of the valid portion of the struct, excluding any trailing
+    /// NUL.
     ///
     /// # Safety
-    /// This pair of sockaddr_un & path_len must be a valid unix addr, which means:
-    /// - path_len <= sockaddr_un.sun_path.len()
-    /// - if this is a unix addr with a pathname, sun.sun_path is a nul-terminated fs path and
-    ///   sun.sun_path[path_len - 1] == 0 || sun.sun_path[path_len] == 0
-    pub(crate) unsafe fn from_raw_parts(sun: libc::sockaddr_un, mut path_len: usize) -> UnixAddr {
-        if let UnixAddrKind::Pathname(_) = UnixAddrKind::get(&sun, path_len) {
-            if sun.sun_path[path_len - 1] != 0 {
-                assert_eq!(sun.sun_path[path_len], 0);
-                path_len += 1
+    /// This pair of sockaddr_un & sun_len must be a valid unix addr, which
+    /// means:
+    /// - sun_len >= offset_of(sockaddr_un, sun_path)
+    /// - sun_len <= sockaddr_un.sun_path.len() - offset_of(sockaddr_un, sun_path)
+    /// - if this is a unix addr with a pathname, sun.sun_path is a
+    ///   fs path, not necessarily nul-terminated.
+    pub(crate) unsafe fn from_raw_parts(
+        sun: libc::sockaddr_un,
+        sun_len: u8,
+    ) -> UnixAddr {
+        cfg_if! {
+            if #[cfg(any(target_os = "android",
+                     target_os = "fuchsia",
+                     target_os = "illumos",
+                     target_os = "linux"
+                ))]
+            {
+                UnixAddr { sun, sun_len }
+            } else {
+                assert_eq!(sun_len, sun.sun_len);
+                UnixAddr {sun}
             }
         }
-        UnixAddr { sun, path_len }
     }
 
     fn kind(&self) -> UnixAddrKind<'_> {
         // SAFETY: our sockaddr is always valid because of the invariant on the struct
-        unsafe { UnixAddrKind::get(&self.sun, self.path_len) }
+        unsafe { UnixAddrKind::get(&self.sun, self.sun_len()) }
     }
 
     /// If this address represents a filesystem path, return that path.
@@ -642,8 +945,9 @@
     /// If this address represents an abstract socket, return its name.
     ///
     /// For abstract sockets only the bare name is returned, without the
-    /// leading null byte. `None` is returned for unnamed or path-backed sockets.
+    /// leading NUL byte. `None` is returned for unnamed or path-backed sockets.
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn as_abstract(&self) -> Option<&[u8]> {
         match self.kind() {
             UnixAddrKind::Abstract(name) => Some(name),
@@ -651,10 +955,18 @@
         }
     }
 
+    /// Check if this address is an "unnamed" unix socket address.
+    #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
+    #[inline]
+    pub fn is_unnamed(&self) -> bool {
+        matches!(self.kind(), UnixAddrKind::Unnamed)
+    }
+
     /// Returns the addrlen of this socket - `offsetof(struct sockaddr_un, sun_path)`
     #[inline]
     pub fn path_len(&self) -> usize {
-        self.path_len
+        self.sun_len() as usize - offset_of!(libc::sockaddr_un, sun_path)
     }
     /// Returns a pointer to the raw `sockaddr_un` struct
     #[inline]
@@ -666,6 +978,83 @@
     pub fn as_mut_ptr(&mut self) -> *mut libc::sockaddr_un {
         &mut self.sun
     }
+
+    fn sun_len(&self) -> u8 {
+        cfg_if! {
+            if #[cfg(any(target_os = "android",
+                     target_os = "fuchsia",
+                     target_os = "illumos",
+                     target_os = "linux"
+                ))]
+            {
+                self.sun_len
+            } else {
+                self.sun.sun_len
+            }
+        }
+    }
+}
+
+impl private::SockaddrLikePriv for UnixAddr {}
+impl SockaddrLike for UnixAddr {
+    #[cfg(any(
+        target_os = "android",
+        target_os = "fuchsia",
+        target_os = "illumos",
+        target_os = "linux"
+    ))]
+    fn len(&self) -> libc::socklen_t {
+        self.sun_len.into()
+    }
+
+    unsafe fn from_raw(
+        addr: *const libc::sockaddr,
+        len: Option<libc::socklen_t>,
+    ) -> Option<Self>
+    where
+        Self: Sized,
+    {
+        if let Some(l) = len {
+            if (l as usize) < offset_of!(libc::sockaddr_un, sun_path)
+                || l > u8::MAX as libc::socklen_t
+            {
+                return None;
+            }
+        }
+        if (*addr).sa_family as i32 != libc::AF_UNIX {
+            return None;
+        }
+        let mut su: libc::sockaddr_un = mem::zeroed();
+        let sup = &mut su as *mut libc::sockaddr_un as *mut u8;
+        cfg_if! {
+            if #[cfg(any(target_os = "android",
+                         target_os = "fuchsia",
+                         target_os = "illumos",
+                         target_os = "linux"
+                ))] {
+                let su_len = len.unwrap_or(
+                    mem::size_of::<libc::sockaddr_un>() as libc::socklen_t
+                );
+            } else {
+                let su_len = len.unwrap_or((*addr).sa_len as libc::socklen_t);
+            }
+        };
+        ptr::copy(addr as *const u8, sup, su_len as usize);
+        Some(Self::from_raw_parts(su, su_len as u8))
+    }
+
+    fn size() -> libc::socklen_t
+    where
+        Self: Sized,
+    {
+        mem::size_of::<libc::sockaddr_un>() as libc::socklen_t
+    }
+}
+
+impl AsRef<libc::sockaddr_un> for UnixAddr {
+    fn as_ref(&self) -> &libc::sockaddr_un {
+        &self.sun
+    }
 }
 
 #[cfg(any(target_os = "android", target_os = "linux"))]
@@ -705,82 +1094,1004 @@
     }
 }
 
+/// Anything that, in C, can be cast back and forth to `sockaddr`.
+///
+/// Most implementors also implement `AsRef<libc::XXX>` to access their
+/// inner type read-only.
+#[allow(clippy::len_without_is_empty)]
+pub trait SockaddrLike: private::SockaddrLikePriv {
+    /// Returns a raw pointer to the inner structure.  Useful for FFI.
+    fn as_ptr(&self) -> *const libc::sockaddr {
+        self as *const Self as *const libc::sockaddr
+    }
+
+    /// Unsafe constructor from a variable length source
+    ///
+    /// Some C APIs from provide `len`, and others do not.  If it's provided it
+    /// will be validated.  If not, it will be guessed based on the family.
+    ///
+    /// # Arguments
+    ///
+    /// - `addr`:   raw pointer to something that can be cast to a
+    ///             `libc::sockaddr`. For example, `libc::sockaddr_in`,
+    ///             `libc::sockaddr_in6`, etc.
+    /// - `len`:    For fixed-width types like `sockaddr_in`, it will be
+    ///             validated if present and ignored if not.  For variable-width
+    ///             types it is required and must be the total length of valid
+    ///             data.  For example, if `addr` points to a
+    ///             named `sockaddr_un`, then `len` must be the length of the
+    ///             structure up to but not including the trailing NUL.
+    ///
+    /// # Safety
+    ///
+    /// `addr` must be valid for the specific type of sockaddr.  `len`, if
+    /// present, must not exceed the length of valid data in `addr`.
+    unsafe fn from_raw(
+        addr: *const libc::sockaddr,
+        len: Option<libc::socklen_t>,
+    ) -> Option<Self>
+    where
+        Self: Sized;
+
+    /// Return the address family of this socket
+    ///
+    /// # Examples
+    /// One common use is to match on the family of a union type, like this:
+    /// ```
+    /// # use nix::sys::socket::*;
+    /// let fd = socket(AddressFamily::Inet, SockType::Stream,
+    ///     SockFlag::empty(), None).unwrap();
+    /// let ss: SockaddrStorage = getsockname(fd).unwrap();
+    /// match ss.family().unwrap() {
+    ///     AddressFamily::Inet => println!("{}", ss.as_sockaddr_in().unwrap()),
+    ///     AddressFamily::Inet6 => println!("{}", ss.as_sockaddr_in6().unwrap()),
+    ///     _ => println!("Unexpected address family")
+    /// }
+    /// ```
+    fn family(&self) -> Option<AddressFamily> {
+        // Safe since all implementors have a sa_family field at the same
+        // address, and they're all repr(C)
+        AddressFamily::from_i32(unsafe {
+            (*(self as *const Self as *const libc::sockaddr)).sa_family as i32
+        })
+    }
+
+    cfg_if! {
+        if #[cfg(any(target_os = "dragonfly",
+                  target_os = "freebsd",
+                  target_os = "ios",
+                  target_os = "macos",
+                  target_os = "netbsd",
+                  target_os = "openbsd"))] {
+            /// Return the length of valid data in the sockaddr structure.
+            ///
+            /// For fixed-size sockaddrs, this should be the size of the
+            /// structure.  But for variable-sized types like [`UnixAddr`] it
+            /// may be less.
+            fn len(&self) -> libc::socklen_t {
+                // Safe since all implementors have a sa_len field at the same
+                // address, and they're all repr(transparent).
+                // Robust for all implementors.
+                unsafe {
+                    (*(self as *const Self as *const libc::sockaddr)).sa_len
+                }.into()
+            }
+        } else {
+            /// Return the length of valid data in the sockaddr structure.
+            ///
+            /// For fixed-size sockaddrs, this should be the size of the
+            /// structure.  But for variable-sized types like [`UnixAddr`] it
+            /// may be less.
+            fn len(&self) -> libc::socklen_t {
+                // No robust default implementation is possible without an
+                // sa_len field.  Implementors with a variable size must
+                // override this method.
+                mem::size_of_val(self) as libc::socklen_t
+            }
+        }
+    }
+
+    /// Return the available space in the structure
+    fn size() -> libc::socklen_t
+    where
+        Self: Sized,
+    {
+        mem::size_of::<Self>() as libc::socklen_t
+    }
+}
+
+impl private::SockaddrLikePriv for () {
+    fn as_mut_ptr(&mut self) -> *mut libc::sockaddr {
+        ptr::null_mut()
+    }
+}
+
+/// `()` can be used in place of a real Sockaddr when no address is expected,
+/// for example for a field of `Option<S> where S: SockaddrLike`.
+// If this RFC ever stabilizes, then ! will be a better choice.
+// https://github.com/rust-lang/rust/issues/35121
+impl SockaddrLike for () {
+    fn as_ptr(&self) -> *const libc::sockaddr {
+        ptr::null()
+    }
+
+    unsafe fn from_raw(
+        _: *const libc::sockaddr,
+        _: Option<libc::socklen_t>,
+    ) -> Option<Self>
+    where
+        Self: Sized,
+    {
+        None
+    }
+
+    fn family(&self) -> Option<AddressFamily> {
+        None
+    }
+
+    fn len(&self) -> libc::socklen_t {
+        0
+    }
+}
+
+/// An IPv4 socket address
+// This is identical to net::SocketAddrV4.  But the standard library
+// doesn't allow direct access to the libc fields, which we need.  So we
+// reimplement it here.
+#[cfg(feature = "net")]
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct SockaddrIn(libc::sockaddr_in);
+
+#[cfg(feature = "net")]
+impl SockaddrIn {
+    /// Returns the IP address associated with this socket address, in native
+    /// endian.
+    pub const fn ip(&self) -> libc::in_addr_t {
+        u32::from_be(self.0.sin_addr.s_addr)
+    }
+
+    /// Creates a new socket address from IPv4 octets and a port number.
+    pub fn new(a: u8, b: u8, c: u8, d: u8, port: u16) -> Self {
+        Self(libc::sockaddr_in {
+            #[cfg(any(
+                target_os = "dragonfly",
+                target_os = "freebsd",
+                target_os = "ios",
+                target_os = "macos",
+                target_os = "netbsd",
+                target_os = "haiku",
+                target_os = "openbsd"
+            ))]
+            sin_len: Self::size() as u8,
+            sin_family: AddressFamily::Inet as sa_family_t,
+            sin_port: u16::to_be(port),
+            sin_addr: libc::in_addr {
+                s_addr: u32::from_ne_bytes([a, b, c, d]),
+            },
+            sin_zero: unsafe { mem::zeroed() },
+        })
+    }
+
+    /// Returns the port number associated with this socket address, in native
+    /// endian.
+    pub const fn port(&self) -> u16 {
+        u16::from_be(self.0.sin_port)
+    }
+}
+
+#[cfg(feature = "net")]
+impl private::SockaddrLikePriv for SockaddrIn {}
+#[cfg(feature = "net")]
+impl SockaddrLike for SockaddrIn {
+    unsafe fn from_raw(
+        addr: *const libc::sockaddr,
+        len: Option<libc::socklen_t>,
+    ) -> Option<Self>
+    where
+        Self: Sized,
+    {
+        if let Some(l) = len {
+            if l != mem::size_of::<libc::sockaddr_in>() as libc::socklen_t {
+                return None;
+            }
+        }
+        if (*addr).sa_family as i32 != libc::AF_INET {
+            return None;
+        }
+        Some(Self(ptr::read_unaligned(addr as *const _)))
+    }
+}
+
+#[cfg(feature = "net")]
+impl AsRef<libc::sockaddr_in> for SockaddrIn {
+    fn as_ref(&self) -> &libc::sockaddr_in {
+        &self.0
+    }
+}
+
+#[cfg(feature = "net")]
+impl fmt::Display for SockaddrIn {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let ne = u32::from_be(self.0.sin_addr.s_addr);
+        let port = u16::from_be(self.0.sin_port);
+        write!(
+            f,
+            "{}.{}.{}.{}:{}",
+            ne >> 24,
+            (ne >> 16) & 0xFF,
+            (ne >> 8) & 0xFF,
+            ne & 0xFF,
+            port
+        )
+    }
+}
+
+#[cfg(feature = "net")]
+impl From<net::SocketAddrV4> for SockaddrIn {
+    fn from(addr: net::SocketAddrV4) -> Self {
+        Self(libc::sockaddr_in {
+            #[cfg(any(
+                target_os = "dragonfly",
+                target_os = "freebsd",
+                target_os = "haiku",
+                target_os = "hermit",
+                target_os = "ios",
+                target_os = "macos",
+                target_os = "netbsd",
+                target_os = "openbsd"
+            ))]
+            sin_len: mem::size_of::<libc::sockaddr_in>() as u8,
+            sin_family: AddressFamily::Inet as sa_family_t,
+            sin_port: addr.port().to_be(), // network byte order
+            sin_addr: ipv4addr_to_libc(*addr.ip()),
+            ..unsafe { mem::zeroed() }
+        })
+    }
+}
+
+#[cfg(feature = "net")]
+impl From<SockaddrIn> for net::SocketAddrV4 {
+    fn from(addr: SockaddrIn) -> Self {
+        net::SocketAddrV4::new(
+            net::Ipv4Addr::from(addr.0.sin_addr.s_addr.to_ne_bytes()),
+            u16::from_be(addr.0.sin_port),
+        )
+    }
+}
+
+#[cfg(feature = "net")]
+impl std::str::FromStr for SockaddrIn {
+    type Err = net::AddrParseError;
+
+    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
+        net::SocketAddrV4::from_str(s).map(SockaddrIn::from)
+    }
+}
+
+/// An IPv6 socket address
+#[cfg(feature = "net")]
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct SockaddrIn6(libc::sockaddr_in6);
+
+#[cfg(feature = "net")]
+impl SockaddrIn6 {
+    /// Returns the flow information associated with this address.
+    pub const fn flowinfo(&self) -> u32 {
+        self.0.sin6_flowinfo
+    }
+
+    /// Returns the IP address associated with this socket address.
+    pub fn ip(&self) -> net::Ipv6Addr {
+        net::Ipv6Addr::from(self.0.sin6_addr.s6_addr)
+    }
+
+    /// Returns the port number associated with this socket address, in native
+    /// endian.
+    pub const fn port(&self) -> u16 {
+        u16::from_be(self.0.sin6_port)
+    }
+
+    /// Returns the scope ID associated with this address.
+    pub const fn scope_id(&self) -> u32 {
+        self.0.sin6_scope_id
+    }
+}
+
+#[cfg(feature = "net")]
+impl private::SockaddrLikePriv for SockaddrIn6 {}
+#[cfg(feature = "net")]
+impl SockaddrLike for SockaddrIn6 {
+    unsafe fn from_raw(
+        addr: *const libc::sockaddr,
+        len: Option<libc::socklen_t>,
+    ) -> Option<Self>
+    where
+        Self: Sized,
+    {
+        if let Some(l) = len {
+            if l != mem::size_of::<libc::sockaddr_in6>() as libc::socklen_t {
+                return None;
+            }
+        }
+        if (*addr).sa_family as i32 != libc::AF_INET6 {
+            return None;
+        }
+        Some(Self(ptr::read_unaligned(addr as *const _)))
+    }
+}
+
+#[cfg(feature = "net")]
+impl AsRef<libc::sockaddr_in6> for SockaddrIn6 {
+    fn as_ref(&self) -> &libc::sockaddr_in6 {
+        &self.0
+    }
+}
+
+#[cfg(feature = "net")]
+impl fmt::Display for SockaddrIn6 {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        // These things are really hard to display properly.  Easier to let std
+        // do it.
+        let std = net::SocketAddrV6::new(
+            self.ip(),
+            self.port(),
+            self.flowinfo(),
+            self.scope_id(),
+        );
+        std.fmt(f)
+    }
+}
+
+#[cfg(feature = "net")]
+impl From<net::SocketAddrV6> for SockaddrIn6 {
+    fn from(addr: net::SocketAddrV6) -> Self {
+        #[allow(clippy::needless_update)] // It isn't needless on Illumos
+        Self(libc::sockaddr_in6 {
+            #[cfg(any(
+                target_os = "dragonfly",
+                target_os = "freebsd",
+                target_os = "haiku",
+                target_os = "hermit",
+                target_os = "ios",
+                target_os = "macos",
+                target_os = "netbsd",
+                target_os = "openbsd"
+            ))]
+            sin6_len: mem::size_of::<libc::sockaddr_in6>() as u8,
+            sin6_family: AddressFamily::Inet6 as sa_family_t,
+            sin6_port: addr.port().to_be(), // network byte order
+            sin6_addr: ipv6addr_to_libc(addr.ip()),
+            sin6_flowinfo: addr.flowinfo(), // host byte order
+            sin6_scope_id: addr.scope_id(), // host byte order
+            ..unsafe { mem::zeroed() }
+        })
+    }
+}
+
+#[cfg(feature = "net")]
+impl From<SockaddrIn6> for net::SocketAddrV6 {
+    fn from(addr: SockaddrIn6) -> Self {
+        net::SocketAddrV6::new(
+            net::Ipv6Addr::from(addr.0.sin6_addr.s6_addr),
+            u16::from_be(addr.0.sin6_port),
+            addr.0.sin6_flowinfo,
+            addr.0.sin6_scope_id,
+        )
+    }
+}
+
+#[cfg(feature = "net")]
+impl std::str::FromStr for SockaddrIn6 {
+    type Err = net::AddrParseError;
+
+    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
+        net::SocketAddrV6::from_str(s).map(SockaddrIn6::from)
+    }
+}
+
+/// A container for any sockaddr type
+///
+/// Just like C's `sockaddr_storage`, this type is large enough to hold any type
+/// of sockaddr.  It can be used as an argument with functions like
+/// [`bind`](super::bind) and [`getsockname`](super::getsockname).  Though it is
+/// a union, it can be safely accessed through the `as_*` methods.
+///
+/// # Example
+/// ```
+/// # use nix::sys::socket::*;
+/// # use std::str::FromStr;
+/// let localhost = SockaddrIn::from_str("127.0.0.1:8081").unwrap();
+/// let fd = socket(AddressFamily::Inet, SockType::Stream, SockFlag::empty(),
+///     None).unwrap();
+/// bind(fd, &localhost).expect("bind");
+/// let ss: SockaddrStorage = getsockname(fd).expect("getsockname");
+/// assert_eq!(&localhost, ss.as_sockaddr_in().unwrap());
+/// ```
+#[derive(Clone, Copy, Eq)]
+#[repr(C)]
+pub union SockaddrStorage {
+    #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
+    alg: AlgAddr,
+    #[cfg(feature = "net")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+    dl: LinkAddr,
+    #[cfg(any(target_os = "android", target_os = "linux"))]
+    nl: NetlinkAddr,
+    #[cfg(all(
+        feature = "ioctl",
+        any(target_os = "ios", target_os = "macos")
+    ))]
+    #[cfg_attr(docsrs, doc(cfg(feature = "ioctl")))]
+    sctl: SysControlAddr,
+    #[cfg(feature = "net")]
+    sin: SockaddrIn,
+    #[cfg(feature = "net")]
+    sin6: SockaddrIn6,
+    ss: libc::sockaddr_storage,
+    su: UnixAddr,
+    #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
+    vsock: VsockAddr,
+}
+impl private::SockaddrLikePriv for SockaddrStorage {}
+impl SockaddrLike for SockaddrStorage {
+    unsafe fn from_raw(
+        addr: *const libc::sockaddr,
+        l: Option<libc::socklen_t>,
+    ) -> Option<Self>
+    where
+        Self: Sized,
+    {
+        if addr.is_null() {
+            return None;
+        }
+        if let Some(len) = l {
+            let ulen = len as usize;
+            if ulen < offset_of!(libc::sockaddr, sa_data)
+                || ulen > mem::size_of::<libc::sockaddr_storage>()
+            {
+                None
+            } else {
+                let mut ss: libc::sockaddr_storage = mem::zeroed();
+                let ssp = &mut ss as *mut libc::sockaddr_storage as *mut u8;
+                ptr::copy(addr as *const u8, ssp, len as usize);
+                #[cfg(any(
+                    target_os = "android",
+                    target_os = "fuchsia",
+                    target_os = "illumos",
+                    target_os = "linux"
+                ))]
+                if i32::from(ss.ss_family) == libc::AF_UNIX {
+                    // Safe because we UnixAddr is strictly smaller than
+                    // SockaddrStorage, and we just initialized the structure.
+                    (*(&mut ss as *mut libc::sockaddr_storage as *mut UnixAddr)).sun_len = len as u8;
+                }
+                Some(Self { ss })
+            }
+        } else {
+            // If length is not available and addr is of a fixed-length type,
+            // copy it.  If addr is of a variable length type and len is not
+            // available, then there's nothing we can do.
+            match (*addr).sa_family as i32 {
+                #[cfg(any(target_os = "android", target_os = "linux"))]
+                libc::AF_ALG => {
+                    AlgAddr::from_raw(addr, l).map(|alg| Self { alg })
+                }
+                #[cfg(feature = "net")]
+                libc::AF_INET => {
+                    SockaddrIn::from_raw(addr, l).map(|sin| Self { sin })
+                }
+                #[cfg(feature = "net")]
+                libc::AF_INET6 => {
+                    SockaddrIn6::from_raw(addr, l).map(|sin6| Self { sin6 })
+                }
+                #[cfg(any(
+                    target_os = "dragonfly",
+                    target_os = "freebsd",
+                    target_os = "ios",
+                    target_os = "macos",
+                    target_os = "illumos",
+                    target_os = "netbsd",
+                    target_os = "haiku",
+                    target_os = "openbsd"
+                ))]
+                #[cfg(feature = "net")]
+                libc::AF_LINK => {
+                    LinkAddr::from_raw(addr, l).map(|dl| Self { dl })
+                }
+                #[cfg(any(target_os = "android", target_os = "linux"))]
+                libc::AF_NETLINK => {
+                    NetlinkAddr::from_raw(addr, l).map(|nl| Self { nl })
+                }
+                #[cfg(any(
+                    target_os = "android",
+                    target_os = "fuchsia",
+                    target_os = "linux"
+                ))]
+                #[cfg(feature = "net")]
+                libc::AF_PACKET => {
+                    LinkAddr::from_raw(addr, l).map(|dl| Self { dl })
+                }
+                #[cfg(all(
+                    feature = "ioctl",
+                    any(target_os = "ios", target_os = "macos")
+                ))]
+                libc::AF_SYSTEM => {
+                    SysControlAddr::from_raw(addr, l).map(|sctl| Self { sctl })
+                }
+                #[cfg(any(target_os = "android", target_os = "linux"))]
+                libc::AF_VSOCK => {
+                    VsockAddr::from_raw(addr, l).map(|vsock| Self { vsock })
+                }
+                _ => None,
+            }
+        }
+    }
+
+    #[cfg(any(
+        target_os = "android",
+        target_os = "fuchsia",
+        target_os = "illumos",
+        target_os = "linux"
+    ))]
+    fn len(&self) -> libc::socklen_t {
+        match self.as_unix_addr() {
+            // The UnixAddr type knows its own length
+            Some(ua) => ua.len(),
+            // For all else, we're just a boring SockaddrStorage
+            None => mem::size_of_val(self) as libc::socklen_t
+        }
+    }
+}
+
+macro_rules! accessors {
+    (
+        $fname:ident,
+        $fname_mut:ident,
+        $sockty:ty,
+        $family:expr,
+        $libc_ty:ty,
+        $field:ident) => {
+        /// Safely and falliably downcast to an immutable reference
+        pub fn $fname(&self) -> Option<&$sockty> {
+            if self.family() == Some($family)
+                && self.len() >= mem::size_of::<$libc_ty>() as libc::socklen_t
+            {
+                // Safe because family and len are validated
+                Some(unsafe { &self.$field })
+            } else {
+                None
+            }
+        }
+
+        /// Safely and falliably downcast to a mutable reference
+        pub fn $fname_mut(&mut self) -> Option<&mut $sockty> {
+            if self.family() == Some($family)
+                && self.len() >= mem::size_of::<$libc_ty>() as libc::socklen_t
+            {
+                // Safe because family and len are validated
+                Some(unsafe { &mut self.$field })
+            } else {
+                None
+            }
+        }
+    };
+}
+
+impl SockaddrStorage {
+    /// Downcast to an immutable `[UnixAddr]` reference.
+    pub fn as_unix_addr(&self) -> Option<&UnixAddr> {
+        cfg_if! {
+            if #[cfg(any(target_os = "android",
+                     target_os = "fuchsia",
+                     target_os = "illumos",
+                     target_os = "linux"
+                ))]
+            {
+                let p = unsafe{ &self.ss as *const libc::sockaddr_storage };
+                // Safe because UnixAddr is strictly smaller than
+                // sockaddr_storage, and we're fully initialized
+                let len = unsafe {
+                    (*(p as *const UnixAddr )).sun_len as usize
+                };
+            } else {
+                let len = self.len() as usize;
+            }
+        }
+        // Sanity checks
+        if self.family() != Some(AddressFamily::Unix) ||
+           len < offset_of!(libc::sockaddr_un, sun_path) ||
+           len > mem::size_of::<libc::sockaddr_un>() {
+            None
+        } else {
+            Some(unsafe{&self.su})
+        }
+    }
+
+    /// Downcast to a mutable `[UnixAddr]` reference.
+    pub fn as_unix_addr_mut(&mut self) -> Option<&mut UnixAddr> {
+        cfg_if! {
+            if #[cfg(any(target_os = "android",
+                     target_os = "fuchsia",
+                     target_os = "illumos",
+                     target_os = "linux"
+                ))]
+            {
+                let p = unsafe{ &self.ss as *const libc::sockaddr_storage };
+                // Safe because UnixAddr is strictly smaller than
+                // sockaddr_storage, and we're fully initialized
+                let len = unsafe {
+                    (*(p as *const UnixAddr )).sun_len as usize
+                };
+            } else {
+                let len = self.len() as usize;
+            }
+        }
+        // Sanity checks
+        if self.family() != Some(AddressFamily::Unix) ||
+           len < offset_of!(libc::sockaddr_un, sun_path) ||
+           len > mem::size_of::<libc::sockaddr_un>() {
+            None
+        } else {
+            Some(unsafe{&mut self.su})
+        }
+    }
+
+    #[cfg(any(target_os = "android", target_os = "linux"))]
+    accessors! {as_alg_addr, as_alg_addr_mut, AlgAddr,
+    AddressFamily::Alg, libc::sockaddr_alg, alg}
+
+    #[cfg(any(
+        target_os = "android",
+        target_os = "fuchsia",
+        target_os = "linux"
+    ))]
+    #[cfg(feature = "net")]
+    accessors! {
+    as_link_addr, as_link_addr_mut, LinkAddr,
+    AddressFamily::Packet, libc::sockaddr_ll, dl}
+
+    #[cfg(any(
+        target_os = "dragonfly",
+        target_os = "freebsd",
+        target_os = "ios",
+        target_os = "macos",
+        target_os = "illumos",
+        target_os = "netbsd",
+        target_os = "openbsd"
+    ))]
+    #[cfg(feature = "net")]
+    accessors! {
+    as_link_addr, as_link_addr_mut, LinkAddr,
+    AddressFamily::Link, libc::sockaddr_dl, dl}
+
+    #[cfg(feature = "net")]
+    accessors! {
+    as_sockaddr_in, as_sockaddr_in_mut, SockaddrIn,
+    AddressFamily::Inet, libc::sockaddr_in, sin}
+
+    #[cfg(feature = "net")]
+    accessors! {
+    as_sockaddr_in6, as_sockaddr_in6_mut, SockaddrIn6,
+    AddressFamily::Inet6, libc::sockaddr_in6, sin6}
+
+    #[cfg(any(target_os = "android", target_os = "linux"))]
+    accessors! {as_netlink_addr, as_netlink_addr_mut, NetlinkAddr,
+    AddressFamily::Netlink, libc::sockaddr_nl, nl}
+
+    #[cfg(all(feature = "ioctl", any(target_os = "ios", target_os = "macos")))]
+    #[cfg_attr(docsrs, doc(cfg(feature = "ioctl")))]
+    accessors! {as_sys_control_addr, as_sys_control_addr_mut, SysControlAddr,
+    AddressFamily::System, libc::sockaddr_ctl, sctl}
+
+    #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
+    accessors! {as_vsock_addr, as_vsock_addr_mut, VsockAddr,
+    AddressFamily::Vsock, libc::sockaddr_vm, vsock}
+}
+
+impl fmt::Debug for SockaddrStorage {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        f.debug_struct("SockaddrStorage")
+            // Safe because sockaddr_storage has the least specific
+            // field types
+            .field("ss", unsafe { &self.ss })
+            .finish()
+    }
+}
+
+impl fmt::Display for SockaddrStorage {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        unsafe {
+            match self.ss.ss_family as i32 {
+                #[cfg(any(target_os = "android", target_os = "linux"))]
+                libc::AF_ALG => self.alg.fmt(f),
+                #[cfg(feature = "net")]
+                libc::AF_INET => self.sin.fmt(f),
+                #[cfg(feature = "net")]
+                libc::AF_INET6 => self.sin6.fmt(f),
+                #[cfg(any(
+                    target_os = "dragonfly",
+                    target_os = "freebsd",
+                    target_os = "ios",
+                    target_os = "macos",
+                    target_os = "illumos",
+                    target_os = "netbsd",
+                    target_os = "openbsd"
+                ))]
+                #[cfg(feature = "net")]
+                libc::AF_LINK => self.dl.fmt(f),
+                #[cfg(any(target_os = "android", target_os = "linux"))]
+                libc::AF_NETLINK => self.nl.fmt(f),
+                #[cfg(any(
+                    target_os = "android",
+                    target_os = "linux",
+                    target_os = "fuchsia"
+                ))]
+                #[cfg(feature = "net")]
+                libc::AF_PACKET => self.dl.fmt(f),
+                #[cfg(any(target_os = "ios", target_os = "macos"))]
+                #[cfg(feature = "ioctl")]
+                libc::AF_SYSTEM => self.sctl.fmt(f),
+                libc::AF_UNIX => self.su.fmt(f),
+                #[cfg(any(target_os = "android", target_os = "linux"))]
+                libc::AF_VSOCK => self.vsock.fmt(f),
+                _ => "<Address family unspecified>".fmt(f),
+            }
+        }
+    }
+}
+
+#[cfg(feature = "net")]
+impl From<net::SocketAddrV4> for SockaddrStorage {
+    fn from(s: net::SocketAddrV4) -> Self {
+        unsafe {
+            let mut ss: Self = mem::zeroed();
+            ss.sin = SockaddrIn::from(s);
+            ss
+        }
+    }
+}
+
+#[cfg(feature = "net")]
+impl From<net::SocketAddrV6> for SockaddrStorage {
+    fn from(s: net::SocketAddrV6) -> Self {
+        unsafe {
+            let mut ss: Self = mem::zeroed();
+            ss.sin6 = SockaddrIn6::from(s);
+            ss
+        }
+    }
+}
+
+#[cfg(feature = "net")]
+impl From<net::SocketAddr> for SockaddrStorage {
+    fn from(s: net::SocketAddr) -> Self {
+        match s {
+            net::SocketAddr::V4(sa4) => Self::from(sa4),
+            net::SocketAddr::V6(sa6) => Self::from(sa6),
+        }
+    }
+}
+
+impl Hash for SockaddrStorage {
+    fn hash<H: Hasher>(&self, s: &mut H) {
+        unsafe {
+            match self.ss.ss_family as i32 {
+                #[cfg(any(target_os = "android", target_os = "linux"))]
+                libc::AF_ALG => self.alg.hash(s),
+                #[cfg(feature = "net")]
+                libc::AF_INET => self.sin.hash(s),
+                #[cfg(feature = "net")]
+                libc::AF_INET6 => self.sin6.hash(s),
+                #[cfg(any(
+                    target_os = "dragonfly",
+                    target_os = "freebsd",
+                    target_os = "ios",
+                    target_os = "macos",
+                    target_os = "illumos",
+                    target_os = "netbsd",
+                    target_os = "openbsd"
+                ))]
+                #[cfg(feature = "net")]
+                libc::AF_LINK => self.dl.hash(s),
+                #[cfg(any(target_os = "android", target_os = "linux"))]
+                libc::AF_NETLINK => self.nl.hash(s),
+                #[cfg(any(
+                    target_os = "android",
+                    target_os = "linux",
+                    target_os = "fuchsia"
+                ))]
+                #[cfg(feature = "net")]
+                libc::AF_PACKET => self.dl.hash(s),
+                #[cfg(any(target_os = "ios", target_os = "macos"))]
+                #[cfg(feature = "ioctl")]
+                libc::AF_SYSTEM => self.sctl.hash(s),
+                libc::AF_UNIX => self.su.hash(s),
+                #[cfg(any(target_os = "android", target_os = "linux"))]
+                libc::AF_VSOCK => self.vsock.hash(s),
+                _ => self.ss.hash(s),
+            }
+        }
+    }
+}
+
+impl PartialEq for SockaddrStorage {
+    fn eq(&self, other: &Self) -> bool {
+        unsafe {
+            match (self.ss.ss_family as i32, other.ss.ss_family as i32) {
+                #[cfg(any(target_os = "android", target_os = "linux"))]
+                (libc::AF_ALG, libc::AF_ALG) => self.alg == other.alg,
+                #[cfg(feature = "net")]
+                (libc::AF_INET, libc::AF_INET) => self.sin == other.sin,
+                #[cfg(feature = "net")]
+                (libc::AF_INET6, libc::AF_INET6) => self.sin6 == other.sin6,
+                #[cfg(any(
+                    target_os = "dragonfly",
+                    target_os = "freebsd",
+                    target_os = "ios",
+                    target_os = "macos",
+                    target_os = "illumos",
+                    target_os = "netbsd",
+                    target_os = "openbsd"
+                ))]
+                #[cfg(feature = "net")]
+                (libc::AF_LINK, libc::AF_LINK) => self.dl == other.dl,
+                #[cfg(any(target_os = "android", target_os = "linux"))]
+                (libc::AF_NETLINK, libc::AF_NETLINK) => self.nl == other.nl,
+                #[cfg(any(
+                    target_os = "android",
+                    target_os = "fuchsia",
+                    target_os = "linux"
+                ))]
+                #[cfg(feature = "net")]
+                (libc::AF_PACKET, libc::AF_PACKET) => self.dl == other.dl,
+                #[cfg(any(target_os = "ios", target_os = "macos"))]
+                #[cfg(feature = "ioctl")]
+                (libc::AF_SYSTEM, libc::AF_SYSTEM) => self.sctl == other.sctl,
+                (libc::AF_UNIX, libc::AF_UNIX) => self.su == other.su,
+                #[cfg(any(target_os = "android", target_os = "linux"))]
+                (libc::AF_VSOCK, libc::AF_VSOCK) => self.vsock == other.vsock,
+                _ => false,
+            }
+        }
+    }
+}
+
+mod private {
+    pub trait SockaddrLikePriv {
+        /// Returns a mutable raw pointer to the inner structure.
+        ///
+        /// # Safety
+        ///
+        /// This method is technically safe, but modifying the inner structure's
+        /// `family` or `len` fields may result in violating Nix's invariants.
+        /// It is best to use this method only with foreign functions that do
+        /// not change the sockaddr type.
+        fn as_mut_ptr(&mut self) -> *mut libc::sockaddr {
+            self as *mut Self as *mut libc::sockaddr
+        }
+    }
+}
+
 /// Represents a socket address
 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[deprecated(
+    since = "0.24.0",
+    note = "use SockaddrLike or SockaddrStorage instead"
+)]
+#[allow(missing_docs)] // Since they're all deprecated anyway
+#[allow(deprecated)]
 #[non_exhaustive]
 pub enum SockAddr {
+    #[cfg(feature = "net")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
     Inet(InetAddr),
     Unix(UnixAddr),
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Netlink(NetlinkAddr),
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Alg(AlgAddr),
-    #[cfg(any(target_os = "ios", target_os = "macos"))]
+    #[cfg(all(
+        feature = "ioctl",
+        any(target_os = "ios", target_os = "macos")
+    ))]
+    #[cfg_attr(docsrs, doc(cfg(feature = "ioctl")))]
     SysControl(SysControlAddr),
     /// Datalink address (MAC)
-    #[cfg(any(target_os = "android",
-              target_os = "dragonfly",
-              target_os = "freebsd",
-              target_os = "ios",
-              target_os = "linux",
-              target_os = "macos",
-              target_os = "illumos",
-              target_os = "netbsd",
-              target_os = "openbsd"))]
+    #[cfg(any(
+        target_os = "android",
+        target_os = "dragonfly",
+        target_os = "freebsd",
+        target_os = "ios",
+        target_os = "linux",
+        target_os = "macos",
+        target_os = "illumos",
+        target_os = "netbsd",
+        target_os = "openbsd"
+    ))]
+    #[cfg(feature = "net")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
     Link(LinkAddr),
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     Vsock(VsockAddr),
 }
 
+#[allow(missing_docs)] // Since they're all deprecated anyway
+#[allow(deprecated)]
 impl SockAddr {
+    feature! {
+    #![feature = "net"]
     pub fn new_inet(addr: InetAddr) -> SockAddr {
         SockAddr::Inet(addr)
     }
+    }
 
     pub fn new_unix<P: ?Sized + NixPath>(path: &P) -> Result<SockAddr> {
         Ok(SockAddr::Unix(UnixAddr::new(path)?))
     }
 
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn new_netlink(pid: u32, groups: u32) -> SockAddr {
         SockAddr::Netlink(NetlinkAddr::new(pid, groups))
     }
 
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn new_alg(alg_type: &str, alg_name: &str) -> SockAddr {
         SockAddr::Alg(AlgAddr::new(alg_type, alg_name))
     }
 
+    feature! {
+    #![feature = "ioctl"]
     #[cfg(any(target_os = "ios", target_os = "macos"))]
     pub fn new_sys_control(sockfd: RawFd, name: &str, unit: u32) -> Result<SockAddr> {
         SysControlAddr::from_name(sockfd, name, unit).map(SockAddr::SysControl)
     }
+    }
 
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn new_vsock(cid: u32, port: u32) -> SockAddr {
         SockAddr::Vsock(VsockAddr::new(cid, port))
     }
 
     pub fn family(&self) -> AddressFamily {
         match *self {
+            #[cfg(feature = "net")]
             SockAddr::Inet(InetAddr::V4(..)) => AddressFamily::Inet,
+            #[cfg(feature = "net")]
             SockAddr::Inet(InetAddr::V6(..)) => AddressFamily::Inet6,
             SockAddr::Unix(..) => AddressFamily::Unix,
             #[cfg(any(target_os = "android", target_os = "linux"))]
             SockAddr::Netlink(..) => AddressFamily::Netlink,
             #[cfg(any(target_os = "android", target_os = "linux"))]
             SockAddr::Alg(..) => AddressFamily::Alg,
-            #[cfg(any(target_os = "ios", target_os = "macos"))]
+            #[cfg(all(
+                feature = "ioctl",
+                any(target_os = "ios", target_os = "macos")
+            ))]
             SockAddr::SysControl(..) => AddressFamily::System,
             #[cfg(any(target_os = "android", target_os = "linux"))]
+            #[cfg(feature = "net")]
             SockAddr::Link(..) => AddressFamily::Packet,
-            #[cfg(any(target_os = "dragonfly",
-                      target_os = "freebsd",
-                      target_os = "ios",
-                      target_os = "macos",
-                      target_os = "netbsd",
-                      target_os = "illumos",
-                      target_os = "openbsd"))]
+            #[cfg(any(
+                target_os = "dragonfly",
+                target_os = "freebsd",
+                target_os = "ios",
+                target_os = "macos",
+                target_os = "netbsd",
+                target_os = "illumos",
+                target_os = "openbsd"
+            ))]
+            #[cfg(feature = "net")]
             SockAddr::Link(..) => AddressFamily::Link,
             #[cfg(any(target_os = "android", target_os = "linux"))]
             SockAddr::Vsock(..) => AddressFamily::Vsock,
@@ -802,43 +2113,62 @@
     /// unsafe because it takes a raw pointer as argument.  The caller must
     /// ensure that the pointer is valid.
     #[cfg(not(target_os = "fuchsia"))]
-    pub(crate) unsafe fn from_libc_sockaddr(addr: *const libc::sockaddr) -> Option<SockAddr> {
+    #[cfg(feature = "net")]
+    pub(crate) unsafe fn from_libc_sockaddr(
+        addr: *const libc::sockaddr,
+    ) -> Option<SockAddr> {
         if addr.is_null() {
             None
         } else {
             match AddressFamily::from_i32(i32::from((*addr).sa_family)) {
                 Some(AddressFamily::Unix) => None,
+                #[cfg(feature = "net")]
                 Some(AddressFamily::Inet) => Some(SockAddr::Inet(
-                    InetAddr::V4(*(addr as *const libc::sockaddr_in)))),
+                    InetAddr::V4(ptr::read_unaligned(addr as *const _)),
+                )),
+                #[cfg(feature = "net")]
                 Some(AddressFamily::Inet6) => Some(SockAddr::Inet(
-                    InetAddr::V6(*(addr as *const libc::sockaddr_in6)))),
+                    InetAddr::V6(ptr::read_unaligned(addr as *const _)),
+                )),
                 #[cfg(any(target_os = "android", target_os = "linux"))]
                 Some(AddressFamily::Netlink) => Some(SockAddr::Netlink(
-                    NetlinkAddr(*(addr as *const libc::sockaddr_nl)))),
-                #[cfg(any(target_os = "ios", target_os = "macos"))]
+                    NetlinkAddr(ptr::read_unaligned(addr as *const _)),
+                )),
+                #[cfg(all(
+                    feature = "ioctl",
+                    any(target_os = "ios", target_os = "macos")
+                ))]
                 Some(AddressFamily::System) => Some(SockAddr::SysControl(
-                    SysControlAddr(*(addr as *const libc::sockaddr_ctl)))),
+                    SysControlAddr(ptr::read_unaligned(addr as *const _)),
+                )),
                 #[cfg(any(target_os = "android", target_os = "linux"))]
-                Some(AddressFamily::Packet) => Some(SockAddr::Link(
-                    LinkAddr(*(addr as *const libc::sockaddr_ll)))),
-                #[cfg(any(target_os = "dragonfly",
-                          target_os = "freebsd",
-                          target_os = "ios",
-                          target_os = "macos",
-                          target_os = "netbsd",
-                          target_os = "illumos",
-                          target_os = "openbsd"))]
+                #[cfg(feature = "net")]
+                Some(AddressFamily::Packet) => Some(SockAddr::Link(LinkAddr(
+                    ptr::read_unaligned(addr as *const _),
+                ))),
+                #[cfg(any(
+                    target_os = "dragonfly",
+                    target_os = "freebsd",
+                    target_os = "ios",
+                    target_os = "macos",
+                    target_os = "netbsd",
+                    target_os = "illumos",
+                    target_os = "openbsd"
+                ))]
+                #[cfg(feature = "net")]
                 Some(AddressFamily::Link) => {
-                    let ether_addr = LinkAddr(*(addr as *const libc::sockaddr_dl));
+                    let ether_addr =
+                        LinkAddr(ptr::read_unaligned(addr as *const _));
                     if ether_addr.is_empty() {
                         None
                     } else {
                         Some(SockAddr::Link(ether_addr))
                     }
-                },
+                }
                 #[cfg(any(target_os = "android", target_os = "linux"))]
-                Some(AddressFamily::Vsock) => Some(SockAddr::Vsock(
-                    VsockAddr(*(addr as *const libc::sockaddr_vm)))),
+                Some(AddressFamily::Vsock) => Some(SockAddr::Vsock(VsockAddr(
+                    ptr::read_unaligned(addr as *const _),
+                ))),
                 // Other address families are currently not supported and simply yield a None
                 // entry instead of a proper conversion to a `SockAddr`.
                 Some(_) | None => None,
@@ -855,26 +2185,31 @@
     /// a sockaddr * need to take the size of the underlying type as well and then internally cast it back.
     pub fn as_ffi_pair(&self) -> (&libc::sockaddr, libc::socklen_t) {
         match *self {
+            #[cfg(feature = "net")]
             SockAddr::Inet(InetAddr::V4(ref addr)) => (
                 // This cast is always allowed in C
                 unsafe {
-                    &*(addr as *const libc::sockaddr_in as *const libc::sockaddr)
+                    &*(addr as *const libc::sockaddr_in
+                        as *const libc::sockaddr)
                 },
-                mem::size_of_val(addr) as libc::socklen_t
+                mem::size_of_val(addr) as libc::socklen_t,
             ),
+            #[cfg(feature = "net")]
             SockAddr::Inet(InetAddr::V6(ref addr)) => (
                 // This cast is always allowed in C
                 unsafe {
-                    &*(addr as *const libc::sockaddr_in6 as *const libc::sockaddr)
+                    &*(addr as *const libc::sockaddr_in6
+                        as *const libc::sockaddr)
                 },
-                mem::size_of_val(addr) as libc::socklen_t
+                mem::size_of_val(addr) as libc::socklen_t,
             ),
-            SockAddr::Unix(UnixAddr { ref sun, path_len }) => (
+            SockAddr::Unix(ref unix_addr) => (
                 // This cast is always allowed in C
                 unsafe {
-                    &*(sun as *const libc::sockaddr_un as *const libc::sockaddr)
+                    &*(&unix_addr.sun as *const libc::sockaddr_un
+                        as *const libc::sockaddr)
                 },
-                (path_len + offset_of!(libc::sockaddr_un, sun_path)) as libc::socklen_t
+                unix_addr.sun_len() as libc::socklen_t,
             ),
             #[cfg(any(target_os = "android", target_os = "linux"))]
             SockAddr::Netlink(NetlinkAddr(ref sa)) => (
@@ -882,7 +2217,7 @@
                 unsafe {
                     &*(sa as *const libc::sockaddr_nl as *const libc::sockaddr)
                 },
-                mem::size_of_val(sa) as libc::socklen_t
+                mem::size_of_val(sa) as libc::socklen_t,
             ),
             #[cfg(any(target_os = "android", target_os = "linux"))]
             SockAddr::Alg(AlgAddr(ref sa)) => (
@@ -890,38 +2225,46 @@
                 unsafe {
                     &*(sa as *const libc::sockaddr_alg as *const libc::sockaddr)
                 },
-                mem::size_of_val(sa) as libc::socklen_t
+                mem::size_of_val(sa) as libc::socklen_t,
             ),
-            #[cfg(any(target_os = "ios", target_os = "macos"))]
+            #[cfg(all(
+                feature = "ioctl",
+                any(target_os = "ios", target_os = "macos")
+            ))]
             SockAddr::SysControl(SysControlAddr(ref sa)) => (
                 // This cast is always allowed in C
                 unsafe {
                     &*(sa as *const libc::sockaddr_ctl as *const libc::sockaddr)
                 },
-                mem::size_of_val(sa) as libc::socklen_t
-
+                mem::size_of_val(sa) as libc::socklen_t,
             ),
             #[cfg(any(target_os = "android", target_os = "linux"))]
+            #[cfg(feature = "net")]
             SockAddr::Link(LinkAddr(ref addr)) => (
                 // This cast is always allowed in C
                 unsafe {
-                    &*(addr as *const libc::sockaddr_ll as *const libc::sockaddr)
+                    &*(addr as *const libc::sockaddr_ll
+                        as *const libc::sockaddr)
                 },
-                mem::size_of_val(addr) as libc::socklen_t
+                mem::size_of_val(addr) as libc::socklen_t,
             ),
-            #[cfg(any(target_os = "dragonfly",
-                      target_os = "freebsd",
-                      target_os = "ios",
-                      target_os = "macos",
-                      target_os = "illumos",
-                      target_os = "netbsd",
-                      target_os = "openbsd"))]
+            #[cfg(any(
+                target_os = "dragonfly",
+                target_os = "freebsd",
+                target_os = "ios",
+                target_os = "macos",
+                target_os = "illumos",
+                target_os = "netbsd",
+                target_os = "openbsd"
+            ))]
+            #[cfg(feature = "net")]
             SockAddr::Link(LinkAddr(ref addr)) => (
                 // This cast is always allowed in C
                 unsafe {
-                    &*(addr as *const libc::sockaddr_dl as *const libc::sockaddr)
+                    &*(addr as *const libc::sockaddr_dl
+                        as *const libc::sockaddr)
                 },
-                mem::size_of_val(addr) as libc::socklen_t
+                mem::size_of_val(addr) as libc::socklen_t,
             ),
             #[cfg(any(target_os = "android", target_os = "linux"))]
             SockAddr::Vsock(VsockAddr(ref sa)) => (
@@ -929,32 +2272,40 @@
                 unsafe {
                     &*(sa as *const libc::sockaddr_vm as *const libc::sockaddr)
                 },
-                mem::size_of_val(sa) as libc::socklen_t
+                mem::size_of_val(sa) as libc::socklen_t,
             ),
         }
     }
 }
 
+#[allow(deprecated)]
 impl fmt::Display for SockAddr {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match *self {
+            #[cfg(feature = "net")]
             SockAddr::Inet(ref inet) => inet.fmt(f),
             SockAddr::Unix(ref unix) => unix.fmt(f),
             #[cfg(any(target_os = "android", target_os = "linux"))]
             SockAddr::Netlink(ref nl) => nl.fmt(f),
             #[cfg(any(target_os = "android", target_os = "linux"))]
             SockAddr::Alg(ref nl) => nl.fmt(f),
-            #[cfg(any(target_os = "ios", target_os = "macos"))]
+            #[cfg(all(
+                feature = "ioctl",
+                any(target_os = "ios", target_os = "macos")
+            ))]
             SockAddr::SysControl(ref sc) => sc.fmt(f),
-            #[cfg(any(target_os = "android",
-                      target_os = "dragonfly",
-                      target_os = "freebsd",
-                      target_os = "ios",
-                      target_os = "linux",
-                      target_os = "macos",
-                      target_os = "netbsd",
-                      target_os = "illumos",
-                      target_os = "openbsd"))]
+            #[cfg(any(
+                target_os = "android",
+                target_os = "dragonfly",
+                target_os = "freebsd",
+                target_os = "ios",
+                target_os = "linux",
+                target_os = "macos",
+                target_os = "netbsd",
+                target_os = "illumos",
+                target_os = "openbsd"
+            ))]
+            #[cfg(feature = "net")]
             SockAddr::Link(ref ether_addr) => ether_addr.fmt(f),
             #[cfg(any(target_os = "android", target_os = "linux"))]
             SockAddr::Vsock(ref svm) => svm.fmt(f),
@@ -962,16 +2313,42 @@
     }
 }
 
+#[cfg(not(target_os = "fuchsia"))]
+#[cfg(feature = "net")]
+#[allow(deprecated)]
+impl private::SockaddrLikePriv for SockAddr {}
+#[cfg(not(target_os = "fuchsia"))]
+#[cfg(feature = "net")]
+#[allow(deprecated)]
+impl SockaddrLike for SockAddr {
+    unsafe fn from_raw(
+        addr: *const libc::sockaddr,
+        _len: Option<libc::socklen_t>,
+    ) -> Option<Self> {
+        Self::from_libc_sockaddr(addr)
+    }
+}
+
 #[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
 pub mod netlink {
+    use super::*;
     use crate::sys::socket::addr::AddressFamily;
     use libc::{sa_family_t, sockaddr_nl};
     use std::{fmt, mem};
 
+    /// Address for the Linux kernel user interface device.
+    ///
+    /// # References
+    ///
+    /// [netlink(7)](https://man7.org/linux/man-pages/man7/netlink.7.html)
     #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
-    pub struct NetlinkAddr(pub sockaddr_nl);
+    #[repr(transparent)]
+    pub struct NetlinkAddr(pub(in super::super) sockaddr_nl);
 
     impl NetlinkAddr {
+        /// Construct a new socket address from its port ID and multicast groups
+        /// mask.
         pub fn new(pid: u32, groups: u32) -> NetlinkAddr {
             let mut addr: sockaddr_nl = unsafe { mem::zeroed() };
             addr.nl_family = AddressFamily::Netlink as sa_family_t;
@@ -981,15 +2358,44 @@
             NetlinkAddr(addr)
         }
 
+        /// Return the socket's port ID.
         pub const fn pid(&self) -> u32 {
             self.0.nl_pid
         }
 
+        /// Return the socket's multicast groups mask
         pub const fn groups(&self) -> u32 {
             self.0.nl_groups
         }
     }
 
+    impl private::SockaddrLikePriv for NetlinkAddr {}
+    impl SockaddrLike for NetlinkAddr {
+        unsafe fn from_raw(
+            addr: *const libc::sockaddr,
+            len: Option<libc::socklen_t>,
+        ) -> Option<Self>
+        where
+            Self: Sized,
+        {
+            if let Some(l) = len {
+                if l != mem::size_of::<libc::sockaddr_nl>() as libc::socklen_t {
+                    return None;
+                }
+            }
+            if (*addr).sa_family as i32 != libc::AF_NETLINK {
+                return None;
+            }
+            Some(Self(ptr::read_unaligned(addr as *const _)))
+        }
+    }
+
+    impl AsRef<libc::sockaddr_nl> for NetlinkAddr {
+        fn as_ref(&self) -> &libc::sockaddr_nl {
+            &self.0
+        }
+    }
+
     impl fmt::Display for NetlinkAddr {
         fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
             write!(f, "pid: {} groups: {}", self.pid(), self.groups())
@@ -998,21 +2404,64 @@
 }
 
 #[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
 pub mod alg {
-    use libc::{AF_ALG, sockaddr_alg, c_char};
-    use std::{fmt, mem, str};
-    use std::hash::{Hash, Hasher};
+    use super::*;
+    use libc::{c_char, sockaddr_alg, AF_ALG};
     use std::ffi::CStr;
+    use std::hash::{Hash, Hasher};
+    use std::{fmt, mem, str};
 
+    /// Socket address for the Linux kernel crypto API
     #[derive(Copy, Clone)]
-    pub struct AlgAddr(pub sockaddr_alg);
+    #[repr(transparent)]
+    pub struct AlgAddr(pub(in super::super) sockaddr_alg);
+
+    impl private::SockaddrLikePriv for AlgAddr {}
+    impl SockaddrLike for AlgAddr {
+        unsafe fn from_raw(
+            addr: *const libc::sockaddr,
+            l: Option<libc::socklen_t>,
+        ) -> Option<Self>
+        where
+            Self: Sized,
+        {
+            if let Some(l) = l {
+                if l != mem::size_of::<libc::sockaddr_alg>() as libc::socklen_t
+                {
+                    return None;
+                }
+            }
+            if (*addr).sa_family as i32 != libc::AF_ALG {
+                return None;
+            }
+            Some(Self(ptr::read_unaligned(addr as *const _)))
+        }
+    }
+
+    impl AsRef<libc::sockaddr_alg> for AlgAddr {
+        fn as_ref(&self) -> &libc::sockaddr_alg {
+            &self.0
+        }
+    }
 
     // , PartialEq, Eq, Debug, Hash
     impl PartialEq for AlgAddr {
         fn eq(&self, other: &Self) -> bool {
             let (inner, other) = (self.0, other.0);
-            (inner.salg_family, &inner.salg_type[..], inner.salg_feat, inner.salg_mask, &inner.salg_name[..]) ==
-            (other.salg_family, &other.salg_type[..], other.salg_feat, other.salg_mask, &other.salg_name[..])
+            (
+                inner.salg_family,
+                &inner.salg_type[..],
+                inner.salg_feat,
+                inner.salg_mask,
+                &inner.salg_name[..],
+            ) == (
+                other.salg_family,
+                &other.salg_type[..],
+                other.salg_feat,
+                other.salg_mask,
+                &other.salg_name[..],
+            )
         }
     }
 
@@ -1021,35 +2470,53 @@
     impl Hash for AlgAddr {
         fn hash<H: Hasher>(&self, s: &mut H) {
             let inner = self.0;
-            (inner.salg_family, &inner.salg_type[..], inner.salg_feat, inner.salg_mask, &inner.salg_name[..]).hash(s);
+            (
+                inner.salg_family,
+                &inner.salg_type[..],
+                inner.salg_feat,
+                inner.salg_mask,
+                &inner.salg_name[..],
+            )
+                .hash(s);
         }
     }
 
     impl AlgAddr {
+        /// Construct an `AF_ALG` socket from its cipher name and type.
         pub fn new(alg_type: &str, alg_name: &str) -> AlgAddr {
             let mut addr: sockaddr_alg = unsafe { mem::zeroed() };
             addr.salg_family = AF_ALG as u16;
-            addr.salg_type[..alg_type.len()].copy_from_slice(alg_type.to_string().as_bytes());
-            addr.salg_name[..alg_name.len()].copy_from_slice(alg_name.to_string().as_bytes());
+            addr.salg_type[..alg_type.len()]
+                .copy_from_slice(alg_type.to_string().as_bytes());
+            addr.salg_name[..alg_name.len()]
+                .copy_from_slice(alg_name.to_string().as_bytes());
 
             AlgAddr(addr)
         }
 
-
+        /// Return the socket's cipher type, for example `hash` or `aead`.
         pub fn alg_type(&self) -> &CStr {
-            unsafe { CStr::from_ptr(self.0.salg_type.as_ptr() as *const c_char) }
+            unsafe {
+                CStr::from_ptr(self.0.salg_type.as_ptr() as *const c_char)
+            }
         }
 
+        /// Return the socket's cipher name, for example `sha1`.
         pub fn alg_name(&self) -> &CStr {
-            unsafe { CStr::from_ptr(self.0.salg_name.as_ptr() as *const c_char) }
+            unsafe {
+                CStr::from_ptr(self.0.salg_name.as_ptr() as *const c_char)
+            }
         }
     }
 
     impl fmt::Display for AlgAddr {
         fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-            write!(f, "type: {} alg: {}",
-                   self.alg_name().to_string_lossy(),
-                   self.alg_type().to_string_lossy())
+            write!(
+                f,
+                "type: {} alg: {}",
+                self.alg_name().to_string_lossy(),
+                self.alg_type().to_string_lossy()
+            )
         }
     }
 
@@ -1060,13 +2527,16 @@
     }
 }
 
+feature! {
+#![feature = "ioctl"]
 #[cfg(any(target_os = "ios", target_os = "macos"))]
 pub mod sys_control {
     use crate::sys::socket::addr::AddressFamily;
     use libc::{self, c_uchar};
-    use std::{fmt, mem};
+    use std::{fmt, mem, ptr};
     use std::os::unix::io::RawFd;
     use crate::{Errno, Result};
+    use super::{private, SockaddrLike};
 
     // FIXME: Move type into `libc`
     #[repr(C)]
@@ -1083,11 +2553,41 @@
 
     ioctl_readwrite!(ctl_info, CTL_IOC_MAGIC, CTL_IOC_INFO, ctl_ioc_info);
 
-    #[repr(C)]
+    /// Apple system control socket
+    ///
+    /// # References
+    ///
+    /// <https://developer.apple.com/documentation/kernel/sockaddr_ctl>
     #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
-    pub struct SysControlAddr(pub libc::sockaddr_ctl);
+    #[repr(transparent)]
+    pub struct SysControlAddr(pub(in super::super) libc::sockaddr_ctl);
+
+    impl private::SockaddrLikePriv for SysControlAddr {}
+    impl SockaddrLike for SysControlAddr {
+        unsafe fn from_raw(addr: *const libc::sockaddr, len: Option<libc::socklen_t>)
+            -> Option<Self> where Self: Sized
+        {
+            if let Some(l) = len {
+                if l != mem::size_of::<libc::sockaddr_ctl>() as libc::socklen_t {
+                    return None;
+                }
+            }
+            if (*addr).sa_family as i32 != libc::AF_SYSTEM {
+                return None;
+            }
+            Some(Self(ptr::read_unaligned(addr as *const _)))
+        }
+    }
+
+    impl AsRef<libc::sockaddr_ctl> for SysControlAddr {
+        fn as_ref(&self) -> &libc::sockaddr_ctl {
+            &self.0
+        }
+    }
 
     impl SysControlAddr {
+        /// Construct a new `SysControlAddr` from its kernel unique identifier
+        /// and unit number.
         pub const fn new(id: u32, unit: u32) -> SysControlAddr {
             let addr = libc::sockaddr_ctl {
                 sc_len: mem::size_of::<libc::sockaddr_ctl>() as c_uchar,
@@ -1101,6 +2601,8 @@
             SysControlAddr(addr)
         }
 
+        /// Construct a new `SysControlAddr` from its human readable name and
+        /// unit number.
         pub fn from_name(sockfd: RawFd, name: &str, unit: u32) -> Result<SysControlAddr> {
             if name.len() > MAX_KCTL_NAME {
                 return Err(Errno::ENAMETOOLONG);
@@ -1115,10 +2617,12 @@
             Ok(SysControlAddr::new(info.ctl_id, unit))
         }
 
+        /// Return the kernel unique identifier
         pub const fn id(&self) -> u32 {
             self.0.sc_id
         }
 
+        /// Return the kernel controller private unit number.
         pub const fn unit(&self) -> u32 {
             self.0.sc_unit
         }
@@ -1130,23 +2634,21 @@
         }
     }
 }
-
+}
 
 #[cfg(any(target_os = "android", target_os = "linux", target_os = "fuchsia"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
 mod datalink {
-    use super::{fmt, AddressFamily};
+    feature! {
+    #![feature = "net"]
+    use super::{fmt, mem, private, ptr, SockaddrLike};
 
     /// Hardware Address
     #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
-    pub struct LinkAddr(pub libc::sockaddr_ll);
+    #[repr(transparent)]
+    pub struct LinkAddr(pub(in super::super) libc::sockaddr_ll);
 
     impl LinkAddr {
-        /// Always AF_PACKET
-        pub fn family(&self) -> AddressFamily {
-            assert_eq!(self.0.sll_family as i32, libc::AF_PACKET);
-            AddressFamily::Packet
-        }
-
         /// Physical-layer protocol
         pub fn protocol(&self) -> u16 {
             self.0.sll_protocol
@@ -1173,70 +2675,96 @@
         }
 
         /// Physical-layer address (MAC)
-        pub fn addr(&self) -> [u8; 6] {
-            [
-                self.0.sll_addr[0] as u8,
-                self.0.sll_addr[1] as u8,
-                self.0.sll_addr[2] as u8,
-                self.0.sll_addr[3] as u8,
-                self.0.sll_addr[4] as u8,
-                self.0.sll_addr[5] as u8,
-            ]
+        // Returns an Option just for cross-platform compatibility
+        pub fn addr(&self) -> Option<[u8; 6]> {
+            Some([
+                self.0.sll_addr[0],
+                self.0.sll_addr[1],
+                self.0.sll_addr[2],
+                self.0.sll_addr[3],
+                self.0.sll_addr[4],
+                self.0.sll_addr[5],
+            ])
         }
     }
 
     impl fmt::Display for LinkAddr {
         fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-            let addr = self.addr();
-            write!(f, "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
-                addr[0],
-                addr[1],
-                addr[2],
-                addr[3],
-                addr[4],
-                addr[5])
+            if let Some(addr) = self.addr() {
+                write!(f, "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
+                    addr[0],
+                    addr[1],
+                    addr[2],
+                    addr[3],
+                    addr[4],
+                    addr[5])
+            } else {
+                Ok(())
+            }
         }
     }
+    impl private::SockaddrLikePriv for LinkAddr {}
+    impl SockaddrLike for LinkAddr {
+        unsafe fn from_raw(addr: *const libc::sockaddr,
+                           len: Option<libc::socklen_t>)
+            -> Option<Self> where Self: Sized
+        {
+            if let Some(l) = len {
+                if l != mem::size_of::<libc::sockaddr_ll>() as libc::socklen_t {
+                    return None;
+                }
+            }
+            if (*addr).sa_family as i32 != libc::AF_PACKET {
+                return None;
+            }
+            Some(Self(ptr::read_unaligned(addr as *const _)))
+        }
+    }
+
+    impl AsRef<libc::sockaddr_ll> for LinkAddr {
+        fn as_ref(&self) -> &libc::sockaddr_ll {
+            &self.0
+        }
+    }
+
+    }
 }
 
-#[cfg(any(target_os = "dragonfly",
-          target_os = "freebsd",
-          target_os = "ios",
-          target_os = "macos",
-          target_os = "illumos",
-          target_os = "netbsd",
-          target_os = "openbsd"))]
+#[cfg(any(
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "ios",
+    target_os = "macos",
+    target_os = "illumos",
+    target_os = "netbsd",
+    target_os = "haiku",
+    target_os = "openbsd"
+))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
 mod datalink {
-    use super::{fmt, AddressFamily};
+    feature! {
+    #![feature = "net"]
+    use super::{fmt, mem, private, ptr, SockaddrLike};
 
     /// Hardware Address
     #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
-    pub struct LinkAddr(pub libc::sockaddr_dl);
+    #[repr(transparent)]
+    pub struct LinkAddr(pub(in super::super) libc::sockaddr_dl);
 
     impl LinkAddr {
-        /// Total length of sockaddr
-        #[cfg(not(target_os = "illumos"))]
-        pub fn len(&self) -> usize {
-            self.0.sdl_len as usize
-        }
-
-        /// always == AF_LINK
-        pub fn family(&self) -> AddressFamily {
-            assert_eq!(i32::from(self.0.sdl_family), libc::AF_LINK);
-            AddressFamily::Link
-        }
-
         /// interface index, if != 0, system given index for interface
+        #[cfg(not(target_os = "haiku"))]
         pub fn ifindex(&self) -> usize {
             self.0.sdl_index as usize
         }
 
         /// Datalink type
+        #[cfg(not(target_os = "haiku"))]
         pub fn datalink_type(&self) -> u8 {
             self.0.sdl_type
         }
 
-        // MAC address start position
+        /// MAC address start position
         pub fn nlen(&self) -> usize {
             self.0.sdl_nlen as usize
         }
@@ -1247,6 +2775,7 @@
         }
 
         /// link layer selector length
+        #[cfg(not(target_os = "haiku"))]
         pub fn slen(&self) -> usize {
             self.0.sdl_slen as usize
         }
@@ -1262,52 +2791,119 @@
         }
 
         /// Physical-layer address (MAC)
-        pub fn addr(&self) -> [u8; 6] {
+        // The cast is not unnecessary on all platforms.
+        #[allow(clippy::unnecessary_cast)]
+        pub fn addr(&self) -> Option<[u8; 6]> {
             let nlen = self.nlen();
             let data = self.0.sdl_data;
 
-            assert!(!self.is_empty());
-
-            [
-                data[nlen] as u8,
-                data[nlen + 1] as u8,
-                data[nlen + 2] as u8,
-                data[nlen + 3] as u8,
-                data[nlen + 4] as u8,
-                data[nlen + 5] as u8,
-            ]
+            if self.is_empty() {
+                None
+            } else {
+                Some([
+                    data[nlen] as u8,
+                    data[nlen + 1] as u8,
+                    data[nlen + 2] as u8,
+                    data[nlen + 3] as u8,
+                    data[nlen + 4] as u8,
+                    data[nlen + 5] as u8,
+                ])
+            }
         }
     }
 
     impl fmt::Display for LinkAddr {
         fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-            let addr = self.addr();
-            write!(f, "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
-                addr[0],
-                addr[1],
-                addr[2],
-                addr[3],
-                addr[4],
-                addr[5])
+            if let Some(addr) = self.addr() {
+                write!(f, "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
+                    addr[0],
+                    addr[1],
+                    addr[2],
+                    addr[3],
+                    addr[4],
+                    addr[5])
+            } else {
+                Ok(())
+            }
         }
     }
+    impl private::SockaddrLikePriv for LinkAddr {}
+    impl SockaddrLike for LinkAddr {
+        unsafe fn from_raw(addr: *const libc::sockaddr,
+                           len: Option<libc::socklen_t>)
+            -> Option<Self> where Self: Sized
+        {
+            if let Some(l) = len {
+                if l != mem::size_of::<libc::sockaddr_dl>() as libc::socklen_t {
+                    return None;
+                }
+            }
+            if (*addr).sa_family as i32 != libc::AF_LINK {
+                return None;
+            }
+            Some(Self(ptr::read_unaligned(addr as *const _)))
+        }
+    }
+
+    impl AsRef<libc::sockaddr_dl> for LinkAddr {
+        fn as_ref(&self) -> &libc::sockaddr_dl {
+            &self.0
+        }
+    }
+
+    }
 }
 
 #[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
 pub mod vsock {
+    use super::*;
     use crate::sys::socket::addr::AddressFamily;
     use libc::{sa_family_t, sockaddr_vm};
-    use std::{fmt, mem};
     use std::hash::{Hash, Hasher};
+    use std::{fmt, mem};
 
+    /// Socket address for VMWare VSockets protocol
+    ///
+    /// # References
+    ///
+    /// [vsock(7)](https://man7.org/linux/man-pages/man7/vsock.7.html)
     #[derive(Copy, Clone)]
-    pub struct VsockAddr(pub sockaddr_vm);
+    #[repr(transparent)]
+    pub struct VsockAddr(pub(in super::super) sockaddr_vm);
+
+    impl private::SockaddrLikePriv for VsockAddr {}
+    impl SockaddrLike for VsockAddr {
+        unsafe fn from_raw(
+            addr: *const libc::sockaddr,
+            len: Option<libc::socklen_t>,
+        ) -> Option<Self>
+        where
+            Self: Sized,
+        {
+            if let Some(l) = len {
+                if l != mem::size_of::<libc::sockaddr_vm>() as libc::socklen_t {
+                    return None;
+                }
+            }
+            if (*addr).sa_family as i32 != libc::AF_VSOCK {
+                return None;
+            }
+            Some(Self(ptr::read_unaligned(addr as *const _)))
+        }
+    }
+
+    impl AsRef<libc::sockaddr_vm> for VsockAddr {
+        fn as_ref(&self) -> &libc::sockaddr_vm {
+            &self.0
+        }
+    }
 
     impl PartialEq for VsockAddr {
         fn eq(&self, other: &Self) -> bool {
             let (inner, other) = (self.0, other.0);
-            (inner.svm_family, inner.svm_cid, inner.svm_port) ==
-            (other.svm_family, other.svm_cid, other.svm_port)
+            (inner.svm_family, inner.svm_cid, inner.svm_port)
+                == (other.svm_family, other.svm_cid, other.svm_port)
         }
     }
 
@@ -1325,6 +2921,7 @@
     /// The address for AF_VSOCK socket is defined as a combination of a
     /// 32-bit Context Identifier (CID) and a 32-bit port number.
     impl VsockAddr {
+        /// Construct a `VsockAddr` from its raw fields.
         pub fn new(cid: u32, port: u32) -> VsockAddr {
             let mut addr: sockaddr_vm = unsafe { mem::zeroed() };
             addr.svm_family = AddressFamily::Vsock as sa_family_t;
@@ -1360,88 +2957,291 @@
 
 #[cfg(test)]
 mod tests {
-    #[cfg(any(target_os = "android",
-              target_os = "dragonfly",
-              target_os = "freebsd",
-              target_os = "ios",
-              target_os = "linux",
-              target_os = "macos",
-              target_os = "netbsd",
-              target_os = "illumos",
-              target_os = "openbsd"))]
     use super::*;
 
-    #[cfg(any(target_os = "dragonfly",
-              target_os = "freebsd",
-              target_os = "ios",
-              target_os = "macos",
-              target_os = "netbsd",
-              target_os = "openbsd"))]
-    #[test]
-    fn test_macos_loopback_datalink_addr() {
-        let bytes = [20i8, 18, 1, 0, 24, 3, 0, 0, 108, 111, 48, 0, 0, 0, 0, 0];
-        let sa = bytes.as_ptr() as *const libc::sockaddr;
-        let _sock_addr = unsafe { SockAddr::from_libc_sockaddr(sa) };
-        assert!(_sock_addr.is_none());
+    mod types {
+        use super::*;
+
+        #[test]
+        fn test_ipv4addr_to_libc() {
+            let s = std::net::Ipv4Addr::new(1, 2, 3, 4);
+            let l = ipv4addr_to_libc(s);
+            assert_eq!(l.s_addr, u32::to_be(0x01020304));
+        }
+
+        #[test]
+        fn test_ipv6addr_to_libc() {
+            let s = std::net::Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8);
+            let l = ipv6addr_to_libc(&s);
+            assert_eq!(
+                l.s6_addr,
+                [0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8]
+            );
+        }
     }
 
-    #[cfg(any(target_os = "dragonfly",
-              target_os = "freebsd",
-              target_os = "ios",
-              target_os = "macos",
-              target_os = "netbsd",
-              target_os = "openbsd"))]
-    #[test]
-    fn test_macos_tap_datalink_addr() {
-        let bytes = [20i8, 18, 7, 0, 6, 3, 6, 0, 101, 110, 48, 24, 101, -112, -35, 76, -80];
-        let ptr = bytes.as_ptr();
-        let sa = ptr as *const libc::sockaddr;
-        let _sock_addr = unsafe { SockAddr::from_libc_sockaddr(sa) };
+    mod link {
+        #![allow(clippy::cast_ptr_alignment)]
 
-        assert!(_sock_addr.is_some());
+        #[cfg(any(
+            target_os = "ios",
+            target_os = "macos",
+            target_os = "illumos"
+        ))]
+        use super::super::super::socklen_t;
+        use super::*;
 
-        let sock_addr = _sock_addr.unwrap();
+        /// Don't panic when trying to display an empty datalink address
+        #[cfg(any(
+            target_os = "dragonfly",
+            target_os = "freebsd",
+            target_os = "ios",
+            target_os = "macos",
+            target_os = "netbsd",
+            target_os = "openbsd"
+        ))]
+        #[test]
+        fn test_datalink_display() {
+            use super::super::LinkAddr;
+            use std::mem;
 
-        assert_eq!(sock_addr.family(), AddressFamily::Link);
+            let la = LinkAddr(libc::sockaddr_dl {
+                sdl_len: 56,
+                sdl_family: 18,
+                sdl_index: 5,
+                sdl_type: 24,
+                sdl_nlen: 3,
+                sdl_alen: 0,
+                sdl_slen: 0,
+                ..unsafe { mem::zeroed() }
+            });
+            format!("{}", la);
+        }
 
-        match sock_addr {
-            SockAddr::Link(ether_addr) => {
-                assert_eq!(ether_addr.addr(), [24u8, 101, 144, 221, 76, 176]);
-            },
-            _ => { unreachable!() }
-        };
+        #[cfg(all(
+            any(
+                target_os = "android",
+                target_os = "fuchsia",
+                target_os = "linux"
+            ),
+            target_endian = "little"
+        ))]
+        #[test]
+        fn linux_loopback() {
+            #[repr(align(2))]
+            struct Raw([u8; 20]);
+
+            let bytes = Raw([
+                17u8, 0, 0, 0, 1, 0, 0, 0, 4, 3, 0, 6, 1, 2, 3, 4, 5, 6, 0, 0,
+            ]);
+            let sa = bytes.0.as_ptr() as *const libc::sockaddr;
+            let len = None;
+            let sock_addr =
+                unsafe { SockaddrStorage::from_raw(sa, len) }.unwrap();
+            assert_eq!(sock_addr.family(), Some(AddressFamily::Packet));
+            match sock_addr.as_link_addr() {
+                Some(dl) => assert_eq!(dl.addr(), Some([1, 2, 3, 4, 5, 6])),
+                None => panic!("Can't unwrap sockaddr storage"),
+            }
+        }
+
+        #[cfg(any(target_os = "ios", target_os = "macos"))]
+        #[test]
+        fn macos_loopback() {
+            let bytes =
+                [20i8, 18, 1, 0, 24, 3, 0, 0, 108, 111, 48, 0, 0, 0, 0, 0];
+            let sa = bytes.as_ptr() as *const libc::sockaddr;
+            let len = Some(bytes.len() as socklen_t);
+            let sock_addr =
+                unsafe { SockaddrStorage::from_raw(sa, len) }.unwrap();
+            assert_eq!(sock_addr.family(), Some(AddressFamily::Link));
+            match sock_addr.as_link_addr() {
+                Some(dl) => {
+                    assert!(dl.addr().is_none());
+                }
+                None => panic!("Can't unwrap sockaddr storage"),
+            }
+        }
+
+        #[cfg(any(target_os = "ios", target_os = "macos"))]
+        #[test]
+        fn macos_tap() {
+            let bytes = [
+                20i8, 18, 7, 0, 6, 3, 6, 0, 101, 110, 48, 24, 101, -112, -35,
+                76, -80,
+            ];
+            let ptr = bytes.as_ptr();
+            let sa = ptr as *const libc::sockaddr;
+            let len = Some(bytes.len() as socklen_t);
+
+            let sock_addr =
+                unsafe { SockaddrStorage::from_raw(sa, len).unwrap() };
+            assert_eq!(sock_addr.family(), Some(AddressFamily::Link));
+            match sock_addr.as_link_addr() {
+                Some(dl) => {
+                    assert_eq!(dl.addr(), Some([24u8, 101, 144, 221, 76, 176]))
+                }
+                None => panic!("Can't unwrap sockaddr storage"),
+            }
+        }
+
+        #[cfg(target_os = "illumos")]
+        #[test]
+        fn illumos_tap() {
+            let bytes = [25u8, 0, 0, 0, 6, 0, 6, 0, 24, 101, 144, 221, 76, 176];
+            let ptr = bytes.as_ptr();
+            let sa = ptr as *const libc::sockaddr;
+            let len = Some(bytes.len() as socklen_t);
+            let _sock_addr = unsafe { SockaddrStorage::from_raw(sa, len) };
+
+            assert!(_sock_addr.is_some());
+
+            let sock_addr = _sock_addr.unwrap();
+
+            assert_eq!(sock_addr.family().unwrap(), AddressFamily::Link);
+
+            assert_eq!(
+                sock_addr.as_link_addr().unwrap().addr(),
+                Some([24u8, 101, 144, 221, 76, 176])
+            );
+        }
+
+        #[test]
+        fn size() {
+            #[cfg(any(
+                target_os = "dragonfly",
+                target_os = "freebsd",
+                target_os = "ios",
+                target_os = "macos",
+                target_os = "netbsd",
+                target_os = "illumos",
+                target_os = "openbsd",
+                target_os = "haiku"
+            ))]
+            let l = mem::size_of::<libc::sockaddr_dl>();
+            #[cfg(any(
+                target_os = "android",
+                target_os = "fuchsia",
+                target_os = "linux"
+            ))]
+            let l = mem::size_of::<libc::sockaddr_ll>();
+            assert_eq!(LinkAddr::size() as usize, l);
+        }
     }
 
-    #[cfg(target_os = "illumos")]
-    #[test]
-    fn test_illumos_tap_datalink_addr() {
-        let bytes = [25u8, 0, 0, 0, 6, 0, 6, 0, 24, 101, 144, 221, 76, 176];
-        let ptr = bytes.as_ptr();
-        let sa = ptr as *const libc::sockaddr;
-        let _sock_addr = unsafe { SockAddr::from_libc_sockaddr(sa) };
+    mod sockaddr_in {
+        use super::*;
+        use std::str::FromStr;
 
-        assert!(_sock_addr.is_some());
+        #[test]
+        fn display() {
+            let s = "127.0.0.1:8080";
+            let addr = SockaddrIn::from_str(s).unwrap();
+            assert_eq!(s, format!("{}", addr));
+        }
 
-        let sock_addr = _sock_addr.unwrap();
-
-        assert_eq!(sock_addr.family(), AddressFamily::Link);
-
-        match sock_addr {
-            SockAddr::Link(ether_addr) => {
-                assert_eq!(ether_addr.addr(), [24u8, 101, 144, 221, 76, 176]);
-            },
-            _ => { unreachable!() }
-        };
+        #[test]
+        fn size() {
+            assert_eq!(
+                mem::size_of::<libc::sockaddr_in>(),
+                SockaddrIn::size() as usize
+            );
+        }
     }
 
-    #[cfg(any(target_os = "android", target_os = "linux"))]
-    #[test]
-    fn test_abstract_sun_path() {
-        let name = String::from("nix\0abstract\0test");
-        let addr = UnixAddr::new_abstract(name.as_bytes()).unwrap();
+    mod sockaddr_in6 {
+        use super::*;
+        use std::str::FromStr;
 
-        let sun_path1 = unsafe { &(*addr.as_ptr()).sun_path[..addr.path_len()] };
-        let sun_path2 = [0, 110, 105, 120, 0, 97, 98, 115, 116, 114, 97, 99, 116, 0, 116, 101, 115, 116];
-        assert_eq!(sun_path1, sun_path2);
+        #[test]
+        fn display() {
+            let s = "[1234:5678:90ab:cdef::1111:2222]:8080";
+            let addr = SockaddrIn6::from_str(s).unwrap();
+            assert_eq!(s, format!("{}", addr));
+        }
+
+        #[test]
+        fn size() {
+            assert_eq!(
+                mem::size_of::<libc::sockaddr_in6>(),
+                SockaddrIn6::size() as usize
+            );
+        }
+
+        #[test]
+        // Ensure that we can convert to-and-from std::net variants without change.
+        fn to_and_from() {
+            let s = "[1234:5678:90ab:cdef::1111:2222]:8080";
+            let mut nix_sin6 = SockaddrIn6::from_str(s).unwrap();
+            nix_sin6.0.sin6_flowinfo = 0x12345678;
+            nix_sin6.0.sin6_scope_id = 0x9abcdef0;
+
+            let std_sin6 : std::net::SocketAddrV6 = nix_sin6.into();
+            assert_eq!(nix_sin6, std_sin6.into());
+        }
+    }
+
+    mod sockaddr_storage {
+        use super::*;
+
+        #[test]
+        fn from_sockaddr_un_named() {
+            let ua = UnixAddr::new("/var/run/mysock").unwrap();
+            let ptr = ua.as_ptr() as *const libc::sockaddr;
+            let ss = unsafe {
+                SockaddrStorage::from_raw(ptr, Some(ua.len()))
+            }.unwrap();
+            assert_eq!(ss.len(), ua.len());
+        }
+
+        #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[test]
+        fn from_sockaddr_un_abstract_named() {
+            let name = String::from("nix\0abstract\0test");
+            let ua = UnixAddr::new_abstract(name.as_bytes()).unwrap();
+            let ptr = ua.as_ptr() as *const libc::sockaddr;
+            let ss = unsafe {
+                SockaddrStorage::from_raw(ptr, Some(ua.len()))
+            }.unwrap();
+            assert_eq!(ss.len(), ua.len());
+        }
+
+        #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[test]
+        fn from_sockaddr_un_abstract_unnamed() {
+            let ua = UnixAddr::new_unnamed();
+            let ptr = ua.as_ptr() as *const libc::sockaddr;
+            let ss = unsafe {
+                SockaddrStorage::from_raw(ptr, Some(ua.len()))
+            }.unwrap();
+            assert_eq!(ss.len(), ua.len());
+        }
+    }
+
+    mod unixaddr {
+        use super::*;
+
+        #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[test]
+        fn abstract_sun_path() {
+            let name = String::from("nix\0abstract\0test");
+            let addr = UnixAddr::new_abstract(name.as_bytes()).unwrap();
+
+            let sun_path1 =
+                unsafe { &(*addr.as_ptr()).sun_path[..addr.path_len()] };
+            let sun_path2 = [
+                0, 110, 105, 120, 0, 97, 98, 115, 116, 114, 97, 99, 116, 0,
+                116, 101, 115, 116,
+            ];
+            assert_eq!(sun_path1, sun_path2);
+        }
+
+        #[test]
+        fn size() {
+            assert_eq!(
+                mem::size_of::<libc::sockaddr_un>(),
+                UnixAddr::size() as usize
+            );
+        }
     }
 }
diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs
index 97eea3d..8513b6f 100644
--- a/src/sys/socket/mod.rs
+++ b/src/sys/socket/mod.rs
@@ -1,18 +1,25 @@
 //! Socket interface functions
 //!
 //! [Further reading](https://man7.org/linux/man-pages/man7/socket.7.html)
-use cfg_if::cfg_if;
-use crate::{Result, errno::Errno};
-use libc::{self, c_void, c_int, iovec, socklen_t, size_t,
-        CMSG_FIRSTHDR, CMSG_NXTHDR, CMSG_DATA, CMSG_LEN};
-use memoffset::offset_of;
-use std::{mem, ptr, slice};
-use std::os::unix::io::RawFd;
-#[cfg(all(target_os = "linux"))]
+#[cfg(target_os = "linux")]
+#[cfg(feature = "uio")]
 use crate::sys::time::TimeSpec;
+#[cfg(feature = "uio")]
 use crate::sys::time::TimeVal;
-use crate::sys::uio::IoVec;
+use crate::{errno::Errno, Result};
+use cfg_if::cfg_if;
+use libc::{
+    self, c_int, c_void, iovec, size_t, socklen_t, CMSG_DATA, CMSG_FIRSTHDR,
+    CMSG_LEN, CMSG_NXTHDR,
+};
+use std::convert::{TryFrom, TryInto};
+use std::io::{IoSlice, IoSliceMut};
+#[cfg(feature = "net")]
+use std::net;
+use std::os::unix::io::RawFd;
+use std::{mem, ptr, slice};
 
+#[deny(missing_docs)]
 mod addr;
 #[deny(missing_docs)]
 pub mod sockopt;
@@ -23,50 +30,58 @@
  *
  */
 
+pub use self::addr::{SockaddrLike, SockaddrStorage};
+
 #[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
-pub use self::addr::{
-    AddressFamily,
-    SockAddr,
-    InetAddr,
-    UnixAddr,
-    IpAddr,
-    Ipv4Addr,
-    Ipv6Addr,
-    LinkAddr,
-};
+#[allow(deprecated)]
+pub use self::addr::{AddressFamily, SockAddr, UnixAddr};
 #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+#[allow(deprecated)]
+pub use self::addr::{AddressFamily, SockAddr, UnixAddr};
+#[allow(deprecated)]
+#[cfg(not(any(
+    target_os = "illumos",
+    target_os = "solaris",
+    target_os = "haiku"
+)))]
+#[cfg(feature = "net")]
 pub use self::addr::{
-    AddressFamily,
-    SockAddr,
-    InetAddr,
-    UnixAddr,
-    IpAddr,
-    Ipv4Addr,
-    Ipv6Addr,
+    InetAddr, IpAddr, Ipv4Addr, Ipv6Addr, LinkAddr, SockaddrIn, SockaddrIn6,
+};
+#[allow(deprecated)]
+#[cfg(any(
+    target_os = "illumos",
+    target_os = "solaris",
+    target_os = "haiku"
+))]
+#[cfg(feature = "net")]
+pub use self::addr::{
+    InetAddr, IpAddr, Ipv4Addr, Ipv6Addr, SockaddrIn, SockaddrIn6,
 };
 
 #[cfg(any(target_os = "android", target_os = "linux"))]
-pub use crate::sys::socket::addr::netlink::NetlinkAddr;
-#[cfg(any(target_os = "android", target_os = "linux"))]
 pub use crate::sys::socket::addr::alg::AlgAddr;
 #[cfg(any(target_os = "android", target_os = "linux"))]
+pub use crate::sys::socket::addr::netlink::NetlinkAddr;
+#[cfg(any(target_os = "ios", target_os = "macos"))]
+#[cfg(feature = "ioctl")]
+pub use crate::sys::socket::addr::sys_control::SysControlAddr;
+#[cfg(any(target_os = "android", target_os = "linux"))]
 pub use crate::sys::socket::addr::vsock::VsockAddr;
 
-pub use libc::{
-    cmsghdr,
-    msghdr,
-    sa_family_t,
-    sockaddr,
-    sockaddr_in,
-    sockaddr_in6,
-    sockaddr_storage,
-    sockaddr_un,
-};
+#[cfg(feature = "uio")]
+pub use libc::{cmsghdr, msghdr};
+pub use libc::{sa_family_t, sockaddr, sockaddr_storage, sockaddr_un};
+#[cfg(feature = "net")]
+pub use libc::{sockaddr_in, sockaddr_in6};
 
 // Needed by the cmsg_space macro
 #[doc(hidden)]
 pub use libc::{c_uint, CMSG_SPACE};
 
+#[cfg(feature = "net")]
+use crate::sys::socket::addr::{ipv4addr_to_libc, ipv6addr_to_libc};
+
 /// These constants are used to specify the communication semantics
 /// when creating a socket with [`socket()`](fn.socket.html)
 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
@@ -89,8 +104,27 @@
     Raw = libc::SOCK_RAW,
     /// Provides a reliable datagram layer that does not
     /// guarantee ordering.
+    #[cfg(not(any(target_os = "haiku")))]
     Rdm = libc::SOCK_RDM,
 }
+// The TryFrom impl could've been derived using libc_enum!.  But for
+// backwards-compatibility with Nix-0.25.0 we manually implement it, so as to
+// keep the old variant names.
+impl TryFrom<i32> for SockType {
+    type Error = crate::Error;
+
+    fn try_from(x: i32) -> Result<Self> {
+        match x {
+            libc::SOCK_STREAM => Ok(Self::Stream),
+            libc::SOCK_DGRAM => Ok(Self::Datagram),
+            libc::SOCK_SEQPACKET => Ok(Self::SeqPacket),
+            libc::SOCK_RAW => Ok(Self::Raw),
+            #[cfg(not(any(target_os = "haiku")))]
+            libc::SOCK_RDM => Ok(Self::Rdm),
+            _ => Err(Errno::EINVAL)
+        }
+    }
+}
 
 /// Constants used in [`socket`](fn.socket.html) and [`socketpair`](fn.socketpair.html)
 /// to specify the protocol to use.
@@ -102,75 +136,122 @@
     Tcp = libc::IPPROTO_TCP,
     /// UDP protocol ([ip(7)](https://man7.org/linux/man-pages/man7/ip.7.html))
     Udp = libc::IPPROTO_UDP,
+    /// Raw sockets ([raw(7)](https://man7.org/linux/man-pages/man7/raw.7.html))
+    Raw = libc::IPPROTO_RAW,
     /// Allows applications and other KEXTs to be notified when certain kernel events occur
     /// ([ref](https://developer.apple.com/library/content/documentation/Darwin/Conceptual/NKEConceptual/control/control.html))
     #[cfg(any(target_os = "ios", target_os = "macos"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     KextEvent = libc::SYSPROTO_EVENT,
     /// Allows applications to configure and control a KEXT
     /// ([ref](https://developer.apple.com/library/content/documentation/Darwin/Conceptual/NKEConceptual/control/control.html))
     #[cfg(any(target_os = "ios", target_os = "macos"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     KextControl = libc::SYSPROTO_CONTROL,
     /// Receives routing and link updates and may be used to modify the routing tables (both IPv4 and IPv6), IP addresses, link
     // parameters, neighbor setups, queueing disciplines, traffic classes and packet classifiers
     /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     NetlinkRoute = libc::NETLINK_ROUTE,
     /// Reserved for user-mode socket protocols
     /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     NetlinkUserSock = libc::NETLINK_USERSOCK,
     /// Query information about sockets of various protocol families from the kernel
     /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     NetlinkSockDiag = libc::NETLINK_SOCK_DIAG,
     /// SELinux event notifications.
     /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     NetlinkSELinux = libc::NETLINK_SELINUX,
     /// Open-iSCSI
     /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     NetlinkISCSI = libc::NETLINK_ISCSI,
     /// Auditing
     /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     NetlinkAudit = libc::NETLINK_AUDIT,
     /// Access to FIB lookup from user space
     /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     NetlinkFIBLookup = libc::NETLINK_FIB_LOOKUP,
     /// Netfilter subsystem
     /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     NetlinkNetFilter = libc::NETLINK_NETFILTER,
     /// SCSI Transports
     /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     NetlinkSCSITransport = libc::NETLINK_SCSITRANSPORT,
     /// Infiniband RDMA
     /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     NetlinkRDMA = libc::NETLINK_RDMA,
     /// Transport IPv6 packets from netfilter to user space.  Used by ip6_queue kernel module.
     /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     NetlinkIPv6Firewall = libc::NETLINK_IP6_FW,
     /// DECnet routing messages
     /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     NetlinkDECNetRoutingMessage = libc::NETLINK_DNRTMSG,
     /// Kernel messages to user space
     /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     NetlinkKObjectUEvent = libc::NETLINK_KOBJECT_UEVENT,
     /// Netlink interface to request information about ciphers registered with the kernel crypto API as well as allow
     /// configuration of the kernel crypto API.
     /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     NetlinkCrypto = libc::NETLINK_CRYPTO,
+    /// Non-DIX type protocol number defined for the Ethernet IEEE 802.3 interface that allows packets of all protocols
+    /// defined in the interface to be received.
+    /// ([ref](https://man7.org/linux/man-pages/man7/packet.7.html))
+    // The protocol number is fed into the socket syscall in network byte order.
+    #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
+    EthAll = libc::ETH_P_ALL.to_be(),
 }
 
-libc_bitflags!{
+#[cfg(any(target_os = "linux"))]
+libc_bitflags! {
+    /// Configuration flags for `SO_TIMESTAMPING` interface
+    ///
+    /// For use with [`Timestamping`][sockopt::Timestamping].
+    /// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
+    pub struct TimestampingFlag: c_uint {
+        /// Report any software timestamps when available.
+        SOF_TIMESTAMPING_SOFTWARE;
+        /// Report hardware timestamps as generated by SOF_TIMESTAMPING_TX_HARDWARE when available.
+        SOF_TIMESTAMPING_RAW_HARDWARE;
+        /// Collect transmiting timestamps as reported by hardware
+        SOF_TIMESTAMPING_TX_HARDWARE;
+        /// Collect transmiting timestamps as reported by software
+        SOF_TIMESTAMPING_TX_SOFTWARE;
+        /// Collect receiving timestamps as reported by hardware
+        SOF_TIMESTAMPING_RX_HARDWARE;
+        /// Collect receiving timestamps as reported by software
+        SOF_TIMESTAMPING_RX_SOFTWARE;
+    }
+}
+
+libc_bitflags! {
     /// Additional socket options
     pub struct SockFlag: c_int {
         /// Set non-blocking mode on the new socket
@@ -181,6 +262,7 @@
                   target_os = "linux",
                   target_os = "netbsd",
                   target_os = "openbsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         SOCK_NONBLOCK;
         /// Set close-on-exec on the new descriptor
         #[cfg(any(target_os = "android",
@@ -190,31 +272,37 @@
                   target_os = "linux",
                   target_os = "netbsd",
                   target_os = "openbsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         SOCK_CLOEXEC;
         /// Return `EPIPE` instead of raising `SIGPIPE`
         #[cfg(target_os = "netbsd")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         SOCK_NOSIGPIPE;
         /// For domains `AF_INET(6)`, only allow `connect(2)`, `sendto(2)`, or `sendmsg(2)`
         /// to the DNS port (typically 53)
         #[cfg(target_os = "openbsd")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         SOCK_DNS;
     }
 }
 
-libc_bitflags!{
+libc_bitflags! {
     /// Flags for send/recv and their relatives
     pub struct MsgFlags: c_int {
         /// Sends or requests out-of-band data on sockets that support this notion
         /// (e.g., of type [`Stream`](enum.SockType.html)); the underlying protocol must also
         /// support out-of-band data.
+        #[allow(deprecated)]    // Suppress useless warnings from libc PR 2963
         MSG_OOB;
         /// Peeks at an incoming message. The data is treated as unread and the next
         /// [`recv()`](fn.recv.html)
         /// or similar function shall still return this data.
+        #[allow(deprecated)]    // Suppress useless warnings from libc PR 2963
         MSG_PEEK;
         /// Receive operation blocks until the full amount of data can be
         /// returned. The function may return smaller amount of data if a signal
         /// is caught, an error or disconnect occurs.
+        #[allow(deprecated)]    // Suppress useless warnings from libc PR 2963
         MSG_WAITALL;
         /// Enables nonblocking operation; if the operation would block,
         /// `EAGAIN` or `EWOULDBLOCK` is returned.  This provides similar
@@ -226,8 +314,10 @@
         /// which will affect all threads in
         /// the calling process and as well as other processes that hold
         /// file descriptors referring to the same open file description.
+        #[allow(deprecated)]    // Suppress useless warnings from libc PR 2963
         MSG_DONTWAIT;
         /// Receive flags: Control Data was discarded (buffer too small)
+        #[allow(deprecated)]    // Suppress useless warnings from libc PR 2963
         MSG_CTRUNC;
         /// For raw ([`Packet`](addr/enum.AddressFamily.html)), Internet datagram
         /// (since Linux 2.4.27/2.6.8),
@@ -237,14 +327,18 @@
         /// domain ([unix(7)](https://linux.die.net/man/7/unix)) sockets.
         ///
         /// For use with Internet stream sockets, see [tcp(7)](https://linux.die.net/man/7/tcp).
+        #[allow(deprecated)]    // Suppress useless warnings from libc PR 2963
         MSG_TRUNC;
         /// Terminates a record (when this notion is supported, as for
         /// sockets of type [`SeqPacket`](enum.SockType.html)).
+        #[allow(deprecated)]    // Suppress useless warnings from libc PR 2963
         MSG_EOR;
         /// This flag specifies that queued errors should be received from
         /// the socket error queue. (For more details, see
         /// [recvfrom(2)](https://linux.die.net/man/2/recvfrom))
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
+        #[allow(deprecated)]    // Suppress useless warnings from libc PR 2963
         MSG_ERRQUEUE;
         /// Set the `close-on-exec` flag for the file descriptor received via a UNIX domain
         /// file descriptor using the `SCM_RIGHTS` operation (described in
@@ -259,7 +353,24 @@
                   target_os = "linux",
                   target_os = "netbsd",
                   target_os = "openbsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
+        #[allow(deprecated)]    // Suppress useless warnings from libc PR 2963
         MSG_CMSG_CLOEXEC;
+        /// Requests not to send `SIGPIPE` errors when the other end breaks the connection.
+        /// (For more details, see [send(2)](https://linux.die.net/man/2/send)).
+        #[cfg(any(target_os = "android",
+                  target_os = "dragonfly",
+                  target_os = "freebsd",
+                  target_os = "fuchsia",
+                  target_os = "haiku",
+                  target_os = "illumos",
+                  target_os = "linux",
+                  target_os = "netbsd",
+                  target_os = "openbsd",
+                  target_os = "solaris"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
+        #[allow(deprecated)]    // Suppress useless warnings from libc PR 2963
+        MSG_NOSIGNAL;
     }
 }
 
@@ -276,11 +387,14 @@
         impl UnixCredentials {
             /// Creates a new instance with the credentials of the current process
             pub fn new() -> Self {
-                UnixCredentials(libc::ucred {
-                    pid: crate::unistd::getpid().as_raw(),
-                    uid: crate::unistd::getuid().as_raw(),
-                    gid: crate::unistd::getgid().as_raw(),
-                })
+                // Safe because these FFI functions are inherently safe
+                unsafe {
+                    UnixCredentials(libc::ucred {
+                        pid: libc::getpid(),
+                        uid: libc::getuid(),
+                        gid: libc::getgid()
+                    })
+                }
             }
 
             /// Returns the process identifier
@@ -347,7 +461,12 @@
 
             /// Returns a list group identifiers (the first one being the effective GID)
             pub fn groups(&self) -> &[libc::gid_t] {
-                unsafe { slice::from_raw_parts(self.0.cmcred_groups.as_ptr() as *const libc::gid_t, self.0.cmcred_ngroups as _) }
+                unsafe {
+                    slice::from_raw_parts(
+                        self.0.cmcred_groups.as_ptr() as *const libc::gid_t,
+                        self.0.cmcred_ngroups as _
+                    )
+                }
             }
         }
 
@@ -359,7 +478,7 @@
     }
 }
 
-cfg_if!{
+cfg_if! {
     if #[cfg(any(
                 target_os = "dragonfly",
                 target_os = "freebsd",
@@ -391,6 +510,8 @@
     }
 }
 
+feature! {
+#![feature = "net"]
 /// Request for multicast socket operations
 ///
 /// This is a wrapper type around `ip_mreq`.
@@ -402,10 +523,16 @@
     /// Instantiate a new `IpMembershipRequest`
     ///
     /// If `interface` is `None`, then `Ipv4Addr::any()` will be used for the interface.
-    pub fn new(group: Ipv4Addr, interface: Option<Ipv4Addr>) -> Self {
+    pub fn new(group: net::Ipv4Addr, interface: Option<net::Ipv4Addr>)
+        -> Self
+    {
+        let imr_addr = match interface {
+            None => net::Ipv4Addr::UNSPECIFIED,
+            Some(addr) => addr
+        };
         IpMembershipRequest(libc::ip_mreq {
-            imr_multiaddr: group.0,
-            imr_interface: interface.unwrap_or_else(Ipv4Addr::any).0,
+            imr_multiaddr: ipv4addr_to_libc(group),
+            imr_interface: ipv4addr_to_libc(imr_addr)
         })
     }
 }
@@ -419,13 +546,17 @@
 
 impl Ipv6MembershipRequest {
     /// Instantiate a new `Ipv6MembershipRequest`
-    pub const fn new(group: Ipv6Addr) -> Self {
+    pub const fn new(group: net::Ipv6Addr) -> Self {
         Ipv6MembershipRequest(libc::ipv6_mreq {
-            ipv6mr_multiaddr: group.0,
+            ipv6mr_multiaddr: ipv6addr_to_libc(&group),
             ipv6mr_interface: 0,
         })
     }
 }
+}
+
+feature! {
+#![feature = "uio"]
 
 /// Create a buffer large enough for storing some control messages as returned
 /// by [`recvmsg`](fn.recvmsg.html).
@@ -466,15 +597,20 @@
 }
 
 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
-pub struct RecvMsg<'a> {
+/// Contains outcome of sending or receiving a message
+///
+/// Use [`cmsgs`][RecvMsg::cmsgs] to access all the control messages present, and
+/// [`iovs`][RecvMsg::iovs`] to access underlying io slices.
+pub struct RecvMsg<'a, 's, S> {
     pub bytes: usize,
     cmsghdr: Option<&'a cmsghdr>,
-    pub address: Option<SockAddr>,
+    pub address: Option<S>,
     pub flags: MsgFlags,
+    iobufs: std::marker::PhantomData<& 's()>,
     mhdr: msghdr,
 }
 
-impl<'a> RecvMsg<'a> {
+impl<'a, S> RecvMsg<'a, '_, S> {
     /// Iterate over the valid control messages pointed to by this
     /// msghdr.
     pub fn cmsgs(&self) -> CmsgIterator {
@@ -531,9 +667,11 @@
     ScmRights(Vec<RawFd>),
     /// Received version of [`ControlMessage::ScmCredentials`]
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     ScmCredentials(UnixCredentials),
     /// Received version of [`ControlMessage::ScmCreds`]
     #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     ScmCreds(UnixCredentials),
     /// A message of type `SCM_TIMESTAMP`, containing the time the
     /// packet was received by the kernel.
@@ -546,9 +684,10 @@
     /// ```
     /// # #[macro_use] extern crate nix;
     /// # use nix::sys::socket::*;
-    /// # use nix::sys::uio::IoVec;
     /// # use nix::sys::time::*;
+    /// # use std::io::{IoSlice, IoSliceMut};
     /// # use std::time::*;
+    /// # use std::str::FromStr;
     /// # fn main() {
     /// // Set up
     /// let message = "Ohayō!".as_bytes();
@@ -558,21 +697,22 @@
     ///     SockFlag::empty(),
     ///     None).unwrap();
     /// setsockopt(in_socket, sockopt::ReceiveTimestamp, &true).unwrap();
-    /// let localhost = InetAddr::new(IpAddr::new_v4(127, 0, 0, 1), 0);
-    /// bind(in_socket, &SockAddr::new_inet(localhost)).unwrap();
-    /// let address = getsockname(in_socket).unwrap();
+    /// let localhost = SockaddrIn::from_str("127.0.0.1:0").unwrap();
+    /// bind(in_socket, &localhost).unwrap();
+    /// let address: SockaddrIn = getsockname(in_socket).unwrap();
     /// // Get initial time
     /// let time0 = SystemTime::now();
     /// // Send the message
-    /// let iov = [IoVec::from_slice(message)];
+    /// let iov = [IoSlice::new(message)];
     /// let flags = MsgFlags::empty();
     /// let l = sendmsg(in_socket, &iov, &[], flags, Some(&address)).unwrap();
     /// assert_eq!(message.len(), l);
     /// // Receive the message
     /// let mut buffer = vec![0u8; message.len()];
     /// let mut cmsgspace = cmsg_space!(TimeVal);
-    /// let iov = [IoVec::from_mut_slice(&mut buffer)];
-    /// let r = recvmsg(in_socket, &iov, Some(&mut cmsgspace), flags).unwrap();
+    /// let mut iov = [IoSliceMut::new(&mut buffer)];
+    /// let r = recvmsg::<SockaddrIn>(in_socket, &mut iov, Some(&mut cmsgspace), flags)
+    ///     .unwrap();
     /// let rtime = match r.cmsgs().next() {
     ///     Some(ControlMessageOwned::ScmTimestamp(rtime)) => rtime,
     ///     Some(_) => panic!("Unexpected control message"),
@@ -591,10 +731,16 @@
     /// # }
     /// ```
     ScmTimestamp(TimeVal),
+    /// A set of nanosecond resolution timestamps
+    ///
+    /// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
+    #[cfg(all(target_os = "linux"))]
+    ScmTimestampsns(Timestamps),
     /// Nanoseconds resolution timestamp
     ///
     /// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
     #[cfg(all(target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     ScmTimestampns(TimeSpec),
     #[cfg(any(
         target_os = "android",
@@ -603,6 +749,8 @@
         target_os = "macos",
         target_os = "netbsd",
     ))]
+    #[cfg(feature = "net")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
     Ipv4PacketInfo(libc::in_pktinfo),
     #[cfg(any(
         target_os = "android",
@@ -614,6 +762,8 @@
         target_os = "openbsd",
         target_os = "netbsd",
     ))]
+    #[cfg(feature = "net")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
     Ipv6PacketInfo(libc::in6_pktinfo),
     #[cfg(any(
         target_os = "freebsd",
@@ -622,6 +772,8 @@
         target_os = "netbsd",
         target_os = "openbsd",
     ))]
+    #[cfg(feature = "net")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
     Ipv4RecvIf(libc::sockaddr_dl),
     #[cfg(any(
         target_os = "freebsd",
@@ -630,7 +782,17 @@
         target_os = "netbsd",
         target_os = "openbsd",
     ))]
+    #[cfg(feature = "net")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
     Ipv4RecvDstAddr(libc::in_addr),
+    #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+    #[cfg(feature = "net")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+    Ipv4OrigDstAddr(libc::sockaddr_in),
+    #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+    #[cfg(feature = "net")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+    Ipv6OrigDstAddr(libc::sockaddr_in6),
 
     /// UDP Generic Receive Offload (GRO) allows receiving multiple UDP
     /// packets from a single sender.
@@ -641,6 +803,8 @@
     /// `UdpGroSegment` socket option should be enabled on a socket
     /// to allow receiving GRO packets.
     #[cfg(target_os = "linux")]
+    #[cfg(feature = "net")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
     UdpGroSegments(u16),
 
     /// SO_RXQ_OVFL indicates that an unsigned 32 bit value
@@ -652,13 +816,18 @@
     /// `RxqOvfl` socket option should be enabled on a socket
     /// to allow receiving the drop counter.
     #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     RxqOvfl(u32),
 
     /// Socket error queue control messages read with the `MSG_ERRQUEUE` flag.
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg(feature = "net")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
     Ipv4RecvErr(libc::sock_extended_err, Option<sockaddr_in>),
     /// Socket error queue control messages read with the `MSG_ERRQUEUE` flag.
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg(feature = "net")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
     Ipv6RecvErr(libc::sock_extended_err, Option<sockaddr_in6>),
 
     /// Catch-all variant for unimplemented cmsg types.
@@ -666,6 +835,18 @@
     Unknown(UnknownCmsg),
 }
 
+/// For representing packet timestamps via `SO_TIMESTAMPING` interface
+#[cfg(all(target_os = "linux"))]
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub struct Timestamps {
+    /// software based timestamp, usually one containing data
+    pub system: TimeSpec,
+    /// legacy timestamp, usually empty
+    pub hw_trans: TimeSpec,
+    /// hardware based timestamp
+    pub hw_raw: TimeSpec,
+}
+
 impl ControlMessageOwned {
     /// Decodes a `ControlMessageOwned` from raw bytes.
     ///
@@ -679,6 +860,8 @@
     unsafe fn decode_from(header: &cmsghdr) -> ControlMessageOwned
     {
         let p = CMSG_DATA(header);
+        // The cast is not unnecessary on all platforms.
+        #[allow(clippy::unnecessary_cast)]
         let len = header as *const _ as usize + header.cmsg_len as usize
             - p as usize;
         match (header.cmsg_level, header.cmsg_type) {
@@ -701,6 +884,7 @@
                 let cred: libc::cmsgcred = ptr::read_unaligned(p as *const _);
                 ControlMessageOwned::ScmCreds(cred.into())
             }
+            #[cfg(not(target_os = "haiku"))]
             (libc::SOL_SOCKET, libc::SCM_TIMESTAMP) => {
                 let tv: libc::timeval = ptr::read_unaligned(p as *const _);
                 ControlMessageOwned::ScmTimestamp(TimeVal::from(tv))
@@ -710,6 +894,18 @@
                 let ts: libc::timespec = ptr::read_unaligned(p as *const _);
                 ControlMessageOwned::ScmTimestampns(TimeSpec::from(ts))
             }
+            #[cfg(all(target_os = "linux"))]
+            (libc::SOL_SOCKET, libc::SCM_TIMESTAMPING) => {
+                let tp = p as *const libc::timespec;
+                let ts: libc::timespec = ptr::read_unaligned(tp);
+                let system = TimeSpec::from(ts);
+                let ts: libc::timespec = ptr::read_unaligned(tp.add(1));
+                let hw_trans = TimeSpec::from(ts);
+                let ts: libc::timespec = ptr::read_unaligned(tp.add(2));
+                let hw_raw = TimeSpec::from(ts);
+                let timestamping = Timestamps { system, hw_trans, hw_raw };
+                ControlMessageOwned::ScmTimestampsns(timestamping)
+            }
             #[cfg(any(
                 target_os = "android",
                 target_os = "freebsd",
@@ -717,6 +913,7 @@
                 target_os = "linux",
                 target_os = "macos"
             ))]
+            #[cfg(feature = "net")]
             (libc::IPPROTO_IPV6, libc::IPV6_PKTINFO) => {
                 let info = ptr::read_unaligned(p as *const libc::in6_pktinfo);
                 ControlMessageOwned::Ipv6PacketInfo(info)
@@ -728,6 +925,7 @@
                 target_os = "macos",
                 target_os = "netbsd",
             ))]
+            #[cfg(feature = "net")]
             (libc::IPPROTO_IP, libc::IP_PKTINFO) => {
                 let info = ptr::read_unaligned(p as *const libc::in_pktinfo);
                 ControlMessageOwned::Ipv4PacketInfo(info)
@@ -739,6 +937,7 @@
                 target_os = "netbsd",
                 target_os = "openbsd",
             ))]
+            #[cfg(feature = "net")]
             (libc::IPPROTO_IP, libc::IP_RECVIF) => {
                 let dl = ptr::read_unaligned(p as *const libc::sockaddr_dl);
                 ControlMessageOwned::Ipv4RecvIf(dl)
@@ -750,11 +949,19 @@
                 target_os = "netbsd",
                 target_os = "openbsd",
             ))]
+            #[cfg(feature = "net")]
             (libc::IPPROTO_IP, libc::IP_RECVDSTADDR) => {
                 let dl = ptr::read_unaligned(p as *const libc::in_addr);
                 ControlMessageOwned::Ipv4RecvDstAddr(dl)
             },
+            #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+            #[cfg(feature = "net")]
+            (libc::IPPROTO_IP, libc::IP_ORIGDSTADDR) => {
+                let dl = ptr::read_unaligned(p as *const libc::sockaddr_in);
+                ControlMessageOwned::Ipv4OrigDstAddr(dl)
+            },
             #[cfg(target_os = "linux")]
+            #[cfg(feature = "net")]
             (libc::SOL_UDP, libc::UDP_GRO) => {
                 let gso_size: u16 = ptr::read_unaligned(p as *const _);
                 ControlMessageOwned::UdpGroSegments(gso_size)
@@ -765,15 +972,23 @@
                 ControlMessageOwned::RxqOvfl(drop_counter)
             },
             #[cfg(any(target_os = "android", target_os = "linux"))]
+            #[cfg(feature = "net")]
             (libc::IPPROTO_IP, libc::IP_RECVERR) => {
                 let (err, addr) = Self::recv_err_helper::<sockaddr_in>(p, len);
                 ControlMessageOwned::Ipv4RecvErr(err, addr)
             },
             #[cfg(any(target_os = "android", target_os = "linux"))]
+            #[cfg(feature = "net")]
             (libc::IPPROTO_IPV6, libc::IPV6_RECVERR) => {
                 let (err, addr) = Self::recv_err_helper::<sockaddr_in6>(p, len);
                 ControlMessageOwned::Ipv6RecvErr(err, addr)
             },
+            #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+            #[cfg(feature = "net")]
+            (libc::IPPROTO_IPV6, libc::IPV6_ORIGDSTADDR) => {
+                let dl = ptr::read_unaligned(p as *const libc::sockaddr_in6);
+                ControlMessageOwned::Ipv6OrigDstAddr(dl)
+            },
             (_, _) => {
                 let sl = slice::from_raw_parts(p, len);
                 let ucmsg = UnknownCmsg(*header, Vec::<u8>::from(sl));
@@ -783,6 +998,8 @@
     }
 
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg(feature = "net")]
+    #[allow(clippy::cast_ptr_alignment)]    // False positive
     unsafe fn recv_err_helper<T>(p: *mut libc::c_uchar, len: usize) -> (libc::sock_extended_err, Option<T>) {
         let ee = p as *const libc::sock_extended_err;
         let err = ptr::read_unaligned(ee);
@@ -832,6 +1049,7 @@
     /// For further information, please refer to the
     /// [`unix(7)`](https://man7.org/linux/man-pages/man7/unix.7.html) man page.
     #[cfg(any(target_os = "android", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     ScmCredentials(&'a UnixCredentials),
     /// A message of type `SCM_CREDS`, containing the pid, uid, euid, gid and groups of
     /// a process connected to the socket.
@@ -846,6 +1064,7 @@
     /// For further information, please refer to the
     /// [`unix(4)`](https://www.freebsd.org/cgi/man.cgi?query=unix) man page.
     #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     ScmCreds,
 
     /// Set IV for `AF_ALG` crypto API.
@@ -856,6 +1075,7 @@
         target_os = "android",
         target_os = "linux",
     ))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     AlgSetIv(&'a [u8]),
     /// Set crypto operation for `AF_ALG` crypto API. It may be one of
     /// `ALG_OP_ENCRYPT` or `ALG_OP_DECRYPT`
@@ -866,6 +1086,7 @@
         target_os = "android",
         target_os = "linux",
     ))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     AlgSetOp(&'a libc::c_int),
     /// Set the length of associated authentication data (AAD) (applicable only to AEAD algorithms)
     /// for `AF_ALG` crypto API.
@@ -876,6 +1097,7 @@
         target_os = "android",
         target_os = "linux",
     ))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     AlgSetAeadAssoclen(&'a u32),
 
     /// UDP GSO makes it possible for applications to generate network packets
@@ -887,6 +1109,8 @@
     /// Send buffer should consist of multiple fixed-size wire payloads
     /// following one by one, and the last, possibly smaller one.
     #[cfg(target_os = "linux")]
+    #[cfg(feature = "net")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
     UdpGsoSegments(&'a u16),
 
     /// Configure the sending addressing and interface for v4
@@ -898,6 +1122,8 @@
               target_os = "netbsd",
               target_os = "android",
               target_os = "ios",))]
+    #[cfg(feature = "net")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
     Ipv4PacketInfo(&'a libc::in_pktinfo),
 
     /// Configure the sending addressing and interface for v6
@@ -910,15 +1136,37 @@
               target_os = "freebsd",
               target_os = "android",
               target_os = "ios",))]
+    #[cfg(feature = "net")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
     Ipv6PacketInfo(&'a libc::in6_pktinfo),
 
+    /// Configure the IPv4 source address with `IP_SENDSRCADDR`.
+    #[cfg(any(
+        target_os = "netbsd",
+        target_os = "freebsd",
+        target_os = "openbsd",
+        target_os = "dragonfly",
+    ))]
+    #[cfg(feature = "net")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+    Ipv4SendSrcAddr(&'a libc::in_addr),
+
     /// SO_RXQ_OVFL indicates that an unsigned 32 bit value
     /// ancilliary msg (cmsg) should be attached to recieved
     /// skbs indicating the number of packets dropped by the
     /// socket between the last recieved packet and this
     /// received packet.
     #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     RxqOvfl(&'a u32),
+
+    /// Configure the transmission time of packets.
+    ///
+    /// For further information, please refer to the
+    /// [`tc-etf(8)`](https://man7.org/linux/man-pages/man8/tc-etf.8.html) man
+    /// page.
+    #[cfg(target_os = "linux")]
+    TxTime(&'a u64),
 }
 
 // An opaque structure used to prevent cmsghdr from being a public type
@@ -998,21 +1246,32 @@
                 len as *const _ as *const u8
             },
             #[cfg(target_os = "linux")]
+            #[cfg(feature = "net")]
             ControlMessage::UdpGsoSegments(gso_size) => {
                 gso_size as *const _ as *const u8
             },
             #[cfg(any(target_os = "linux", target_os = "macos",
                       target_os = "netbsd", target_os = "android",
                       target_os = "ios",))]
+            #[cfg(feature = "net")]
             ControlMessage::Ipv4PacketInfo(info) => info as *const _ as *const u8,
             #[cfg(any(target_os = "linux", target_os = "macos",
                       target_os = "netbsd", target_os = "freebsd",
                       target_os = "android", target_os = "ios",))]
+            #[cfg(feature = "net")]
             ControlMessage::Ipv6PacketInfo(info) => info as *const _ as *const u8,
+            #[cfg(any(target_os = "netbsd", target_os = "freebsd",
+                      target_os = "openbsd", target_os = "dragonfly"))]
+            #[cfg(feature = "net")]
+            ControlMessage::Ipv4SendSrcAddr(addr) => addr as *const _ as *const u8,
             #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
             ControlMessage::RxqOvfl(drop_count) => {
                 drop_count as *const _ as *const u8
             },
+            #[cfg(target_os = "linux")]
+            ControlMessage::TxTime(tx_time) => {
+                tx_time as *const _ as *const u8
+            },
         };
         unsafe {
             ptr::copy_nonoverlapping(
@@ -1039,7 +1298,7 @@
             }
             #[cfg(any(target_os = "android", target_os = "linux"))]
             ControlMessage::AlgSetIv(iv) => {
-                mem::size_of_val(&iv) + iv.len()
+                mem::size_of::<&[u8]>() + iv.len()
             },
             #[cfg(any(target_os = "android", target_os = "linux"))]
             ControlMessage::AlgSetOp(op) => {
@@ -1050,21 +1309,32 @@
                 mem::size_of_val(len)
             },
             #[cfg(target_os = "linux")]
+            #[cfg(feature = "net")]
             ControlMessage::UdpGsoSegments(gso_size) => {
                 mem::size_of_val(gso_size)
             },
             #[cfg(any(target_os = "linux", target_os = "macos",
               target_os = "netbsd", target_os = "android",
               target_os = "ios",))]
+            #[cfg(feature = "net")]
             ControlMessage::Ipv4PacketInfo(info) => mem::size_of_val(info),
             #[cfg(any(target_os = "linux", target_os = "macos",
               target_os = "netbsd", target_os = "freebsd",
               target_os = "android", target_os = "ios",))]
+            #[cfg(feature = "net")]
             ControlMessage::Ipv6PacketInfo(info) => mem::size_of_val(info),
+            #[cfg(any(target_os = "netbsd", target_os = "freebsd",
+                      target_os = "openbsd", target_os = "dragonfly"))]
+            #[cfg(feature = "net")]
+            ControlMessage::Ipv4SendSrcAddr(addr) => mem::size_of_val(addr),
             #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
             ControlMessage::RxqOvfl(drop_count) => {
                 mem::size_of_val(drop_count)
             },
+            #[cfg(target_os = "linux")]
+            ControlMessage::TxTime(tx_time) => {
+                mem::size_of_val(tx_time)
+            },
         }
     }
 
@@ -1080,17 +1350,26 @@
             ControlMessage::AlgSetIv(_) | ControlMessage::AlgSetOp(_) |
                 ControlMessage::AlgSetAeadAssoclen(_) => libc::SOL_ALG,
             #[cfg(target_os = "linux")]
+            #[cfg(feature = "net")]
             ControlMessage::UdpGsoSegments(_) => libc::SOL_UDP,
             #[cfg(any(target_os = "linux", target_os = "macos",
                       target_os = "netbsd", target_os = "android",
                       target_os = "ios",))]
+            #[cfg(feature = "net")]
             ControlMessage::Ipv4PacketInfo(_) => libc::IPPROTO_IP,
             #[cfg(any(target_os = "linux", target_os = "macos",
               target_os = "netbsd", target_os = "freebsd",
               target_os = "android", target_os = "ios",))]
+            #[cfg(feature = "net")]
             ControlMessage::Ipv6PacketInfo(_) => libc::IPPROTO_IPV6,
+            #[cfg(any(target_os = "netbsd", target_os = "freebsd",
+                      target_os = "openbsd", target_os = "dragonfly"))]
+            #[cfg(feature = "net")]
+            ControlMessage::Ipv4SendSrcAddr(_) => libc::IPPROTO_IP,
             #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
             ControlMessage::RxqOvfl(_) => libc::SOL_SOCKET,
+            #[cfg(target_os = "linux")]
+            ControlMessage::TxTime(_) => libc::SOL_SOCKET,
         }
     }
 
@@ -1115,21 +1394,32 @@
                 libc::ALG_SET_AEAD_ASSOCLEN
             },
             #[cfg(target_os = "linux")]
+            #[cfg(feature = "net")]
             ControlMessage::UdpGsoSegments(_) => {
                 libc::UDP_SEGMENT
             },
             #[cfg(any(target_os = "linux", target_os = "macos",
                       target_os = "netbsd", target_os = "android",
                       target_os = "ios",))]
+            #[cfg(feature = "net")]
             ControlMessage::Ipv4PacketInfo(_) => libc::IP_PKTINFO,
             #[cfg(any(target_os = "linux", target_os = "macos",
                       target_os = "netbsd", target_os = "freebsd",
                       target_os = "android", target_os = "ios",))]
+            #[cfg(feature = "net")]
             ControlMessage::Ipv6PacketInfo(_) => libc::IPV6_PKTINFO,
+            #[cfg(any(target_os = "netbsd", target_os = "freebsd",
+                      target_os = "openbsd", target_os = "dragonfly"))]
+            #[cfg(feature = "net")]
+            ControlMessage::Ipv4SendSrcAddr(_) => libc::IP_SENDSRCADDR,
             #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
             ControlMessage::RxqOvfl(_) => {
                 libc::SO_RXQ_OVFL
             },
+            #[cfg(target_os = "linux")]
+            ControlMessage::TxTime(_) => {
+                libc::SCM_TXTIME
+            },
         }
     }
 
@@ -1149,8 +1439,42 @@
 /// as with sendto.
 ///
 /// Allocates if cmsgs is nonempty.
-pub fn sendmsg(fd: RawFd, iov: &[IoVec<&[u8]>], cmsgs: &[ControlMessage],
-               flags: MsgFlags, addr: Option<&SockAddr>) -> Result<usize>
+///
+/// # Examples
+/// When not directing to any specific address, use `()` for the generic type
+/// ```
+/// # use nix::sys::socket::*;
+/// # use nix::unistd::pipe;
+/// # use std::io::IoSlice;
+/// let (fd1, fd2) = socketpair(AddressFamily::Unix, SockType::Stream, None,
+///     SockFlag::empty())
+///     .unwrap();
+/// let (r, w) = pipe().unwrap();
+///
+/// let iov = [IoSlice::new(b"hello")];
+/// let fds = [r];
+/// let cmsg = ControlMessage::ScmRights(&fds);
+/// sendmsg::<()>(fd1, &iov, &[cmsg], MsgFlags::empty(), None).unwrap();
+/// ```
+/// When directing to a specific address, the generic type will be inferred.
+/// ```
+/// # use nix::sys::socket::*;
+/// # use nix::unistd::pipe;
+/// # use std::io::IoSlice;
+/// # use std::str::FromStr;
+/// let localhost = SockaddrIn::from_str("1.2.3.4:8080").unwrap();
+/// let fd = socket(AddressFamily::Inet, SockType::Datagram, SockFlag::empty(),
+///     None).unwrap();
+/// let (r, w) = pipe().unwrap();
+///
+/// let iov = [IoSlice::new(b"hello")];
+/// let fds = [r];
+/// let cmsg = ControlMessage::ScmRights(&fds);
+/// sendmsg(fd, &iov, &[cmsg], MsgFlags::empty(), Some(&localhost)).unwrap();
+/// ```
+pub fn sendmsg<S>(fd: RawFd, iov: &[IoSlice<'_>], cmsgs: &[ControlMessage],
+               flags: MsgFlags, addr: Option<&S>) -> Result<usize>
+    where S: SockaddrLike
 {
     let capacity = cmsgs.iter().map(|c| c.space()).sum();
 
@@ -1158,30 +1482,13 @@
     // because subsequent code will not clear the padding bytes.
     let mut cmsg_buffer = vec![0u8; capacity];
 
-    let mhdr = pack_mhdr_to_send(&mut cmsg_buffer[..], &iov, &cmsgs, addr);
+    let mhdr = pack_mhdr_to_send(&mut cmsg_buffer[..], iov, cmsgs, addr);
 
     let ret = unsafe { libc::sendmsg(fd, &mhdr, flags.bits()) };
 
     Errno::result(ret).map(|r| r as usize)
 }
 
-#[cfg(any(
-    target_os = "linux",
-    target_os = "android",
-    target_os = "freebsd",
-    target_os = "netbsd",
-))]
-#[derive(Debug)]
-pub struct SendMmsgData<'a, I, C>
-    where
-        I: AsRef<[IoVec<&'a [u8]>]>,
-        C: AsRef<[ControlMessage<'a>]>
-{
-    pub iov: I,
-    pub cmsgs: C,
-    pub addr: Option<SockAddr>,
-    pub _lt: std::marker::PhantomData<&'a I>,
-}
 
 /// An extension of `sendmsg` that allows the caller to transmit multiple
 /// messages on a socket using a single system call. This has performance
@@ -1206,50 +1513,66 @@
     target_os = "freebsd",
     target_os = "netbsd",
 ))]
-pub fn sendmmsg<'a, I, C>(
+pub fn sendmmsg<'a, XS, AS, C, I, S>(
     fd: RawFd,
-    data: impl std::iter::IntoIterator<Item=&'a SendMmsgData<'a, I, C>>,
+    data: &'a mut MultiHeaders<S>,
+    slices: XS,
+    // one address per group of slices
+    addrs: AS,
+    // shared across all the messages
+    cmsgs: C,
     flags: MsgFlags
-) -> Result<Vec<usize>>
+) -> crate::Result<MultiResults<'a, S>>
     where
-        I: AsRef<[IoVec<&'a [u8]>]> + 'a,
+        XS: IntoIterator<Item = &'a I>,
+        AS: AsRef<[Option<S>]>,
+        I: AsRef<[IoSlice<'a>]> + 'a,
         C: AsRef<[ControlMessage<'a>]> + 'a,
+        S: SockaddrLike + 'a
 {
-    let iter = data.into_iter();
 
-    let size_hint = iter.size_hint();
-    let reserve_items = size_hint.1.unwrap_or(size_hint.0);
+    let mut count = 0;
 
-    let mut output = Vec::<libc::mmsghdr>::with_capacity(reserve_items);
 
-    let mut cmsgs_buffers = Vec::<Vec<u8>>::with_capacity(reserve_items);
+    for (i, ((slice, addr), mmsghdr)) in slices.into_iter().zip(addrs.as_ref()).zip(data.items.iter_mut() ).enumerate() {
+        let mut p = &mut mmsghdr.msg_hdr;
+        p.msg_iov = slice.as_ref().as_ptr() as *mut libc::iovec;
+        p.msg_iovlen = slice.as_ref().len() as _;
 
-    for d in iter {
-        let capacity: usize = d.cmsgs.as_ref().iter().map(|c| c.space()).sum();
-        let mut cmsgs_buffer = vec![0u8; capacity];
+        p.msg_namelen = addr.as_ref().map_or(0, S::len);
+        p.msg_name = addr.as_ref().map_or(ptr::null(), S::as_ptr) as _;
 
-        output.push(libc::mmsghdr {
-            msg_hdr: pack_mhdr_to_send(
-                &mut cmsgs_buffer,
-                &d.iov,
-                &d.cmsgs,
-                d.addr.as_ref()
-            ),
-            msg_len: 0,
-        });
-        cmsgs_buffers.push(cmsgs_buffer);
-    };
+        // Encode each cmsg.  This must happen after initializing the header because
+        // CMSG_NEXT_HDR and friends read the msg_control and msg_controllen fields.
+        // CMSG_FIRSTHDR is always safe
+        let mut pmhdr: *mut cmsghdr = unsafe { CMSG_FIRSTHDR(p) };
+        for cmsg in cmsgs.as_ref() {
+            assert_ne!(pmhdr, ptr::null_mut());
+            // Safe because we know that pmhdr is valid, and we initialized it with
+            // sufficient space
+            unsafe { cmsg.encode_into(pmhdr) };
+            // Safe because mhdr is valid
+            pmhdr = unsafe { CMSG_NXTHDR(p, pmhdr) };
+        }
 
-    let ret = unsafe { libc::sendmmsg(fd, output.as_mut_ptr(), output.len() as _, flags.bits() as _) };
-
-    let sent_messages = Errno::result(ret)? as usize;
-    let mut sent_bytes = Vec::with_capacity(sent_messages);
-
-    for item in &output {
-        sent_bytes.push(item.msg_len as usize);
+        count = i+1;
     }
 
-    Ok(sent_bytes)
+    let sent = Errno::result(unsafe {
+        libc::sendmmsg(
+            fd,
+            data.items.as_mut_ptr(),
+            count as _,
+            flags.bits() as _
+        )
+    })? as usize;
+
+    Ok(MultiResults {
+        rmm: data,
+        current_index: 0,
+        received: sent
+    })
+
 }
 
 
@@ -1260,131 +1583,347 @@
     target_os = "netbsd",
 ))]
 #[derive(Debug)]
-pub struct RecvMmsgData<'a, I>
-    where
-        I: AsRef<[IoVec<&'a mut [u8]>]> + 'a,
-{
-    pub iov: I,
-    pub cmsg_buffer: Option<&'a mut Vec<u8>>,
+/// Preallocated structures needed for [`recvmmsg`] and [`sendmmsg`] functions
+pub struct MultiHeaders<S> {
+    // preallocated boxed slice of mmsghdr
+    items: Box<[libc::mmsghdr]>,
+    addresses: Box<[mem::MaybeUninit<S>]>,
+    // while we are not using it directly - this is used to store control messages
+    // and we retain pointers to them inside items array
+    #[allow(dead_code)]
+    cmsg_buffers: Option<Box<[u8]>>,
+    msg_controllen: usize,
 }
 
-/// An extension of `recvmsg` that allows the caller to receive multiple
-/// messages from a socket using a single system call. This has
-/// performance benefits for some applications.
-///
-/// `iov` and `cmsg_buffer` should be constructed similarly to `recvmsg`
-///
-/// Multiple allocations are performed
-///
-/// # Arguments
-///
-/// * `fd`:             Socket file descriptor
-/// * `data`:           Struct that implements `IntoIterator` with `RecvMmsgData` items
-/// * `flags`:          Optional flags passed directly to the operating system.
-///
-/// # RecvMmsgData
-///
-/// * `iov`:            Scatter-gather list of buffers to receive the message
-/// * `cmsg_buffer`:    Space to receive ancillary data.  Should be created by
-///                     [`cmsg_space!`](macro.cmsg_space.html)
-///
-/// # Returns
-/// A `Vec` with multiple `RecvMsg`, one per received message
-///
-/// # References
-/// - [`recvmsg`](fn.recvmsg.html)
-/// - [`RecvMsg`](struct.RecvMsg.html)
 #[cfg(any(
     target_os = "linux",
     target_os = "android",
     target_os = "freebsd",
     target_os = "netbsd",
 ))]
-#[allow(clippy::needless_collect)]  // Complicated false positive
-pub fn recvmmsg<'a, I>(
-    fd: RawFd,
-    data: impl std::iter::IntoIterator<Item=&'a mut RecvMmsgData<'a, I>,
-        IntoIter=impl ExactSizeIterator + Iterator<Item=&'a mut RecvMmsgData<'a, I>>>,
-    flags: MsgFlags,
-    timeout: Option<crate::sys::time::TimeSpec>
-) -> Result<Vec<RecvMsg<'a>>>
+impl<S> MultiHeaders<S> {
+    /// Preallocate structure used by [`recvmmsg`] and [`sendmmsg`] takes number of headers to preallocate
+    ///
+    /// `cmsg_buffer` should be created with [`cmsg_space!`] if needed
+    pub fn preallocate(num_slices: usize, cmsg_buffer: Option<Vec<u8>>) -> Self
     where
-        I: AsRef<[IoVec<&'a mut [u8]>]> + 'a,
-{
-    let iter = data.into_iter();
+        S: Copy + SockaddrLike,
+    {
+        // we will be storing pointers to addresses inside mhdr - convert it into boxed
+        // slice so it can'be changed later by pushing anything into self.addresses
+        let mut addresses = vec![std::mem::MaybeUninit::uninit(); num_slices].into_boxed_slice();
 
-    let num_messages = iter.len();
+        let msg_controllen = cmsg_buffer.as_ref().map_or(0, |v| v.capacity());
 
-    let mut output: Vec<libc::mmsghdr> = Vec::with_capacity(num_messages);
+        // we'll need a cmsg_buffer for each slice, we preallocate a vector and split
+        // it into "slices" parts
+        let cmsg_buffers =
+            cmsg_buffer.map(|v| vec![0u8; v.capacity() * num_slices].into_boxed_slice());
 
-    // Addresses should be pre-allocated.  pack_mhdr_to_receive will store them
-    // as raw pointers, so we may not move them.  Turn the vec into a boxed
-    // slice so we won't inadvertently reallocate the vec.
-    let mut addresses = vec![mem::MaybeUninit::uninit(); num_messages]
-        .into_boxed_slice();
+        let items = addresses
+            .iter_mut()
+            .enumerate()
+            .map(|(ix, address)| {
+                let (ptr, cap) = match &cmsg_buffers {
+                    Some(v) => ((&v[ix * msg_controllen] as *const u8), msg_controllen),
+                    None => (std::ptr::null(), 0),
+                };
+                let msg_hdr = unsafe { pack_mhdr_to_receive(std::ptr::null(), 0, ptr, cap, address.as_mut_ptr()) };
+                libc::mmsghdr {
+                    msg_hdr,
+                    msg_len: 0,
+                }
+            })
+            .collect::<Vec<_>>();
 
-    let results: Vec<_> = iter.enumerate().map(|(i, d)| {
-        let (msg_controllen, mhdr) = unsafe {
-            pack_mhdr_to_receive(
-                d.iov.as_ref(),
-                &mut d.cmsg_buffer,
-                addresses[i].as_mut_ptr(),
-            )
-        };
-
-        output.push(
-            libc::mmsghdr {
-                msg_hdr: mhdr,
-                msg_len: 0,
-            }
-        );
-
-        (msg_controllen as usize, &mut d.cmsg_buffer)
-    }).collect();
-
-    let timeout = if let Some(mut t) = timeout {
-        t.as_mut() as *mut libc::timespec
-    } else {
-        ptr::null_mut()
-    };
-
-    let ret = unsafe { libc::recvmmsg(fd, output.as_mut_ptr(), output.len() as _, flags.bits() as _, timeout) };
-
-    let _ = Errno::result(ret)?;
-
-    Ok(output
-        .into_iter()
-        .take(ret as usize)
-        .zip(addresses.iter().map(|addr| unsafe{addr.assume_init()}))
-        .zip(results.into_iter())
-        .map(|((mmsghdr, address), (msg_controllen, cmsg_buffer))| {
-            unsafe {
-                read_mhdr(
-                    mmsghdr.msg_hdr,
-                    mmsghdr.msg_len as isize,
-                    msg_controllen,
-                    address,
-                    cmsg_buffer
-                )
-            }
-        })
-        .collect())
+        Self {
+            items: items.into_boxed_slice(),
+            addresses,
+            cmsg_buffers,
+            msg_controllen,
+        }
+    }
 }
 
-unsafe fn read_mhdr<'a, 'b>(
+/// An extension of recvmsg that allows the caller to receive multiple messages from a socket using a single system call.
+///
+/// This has performance benefits for some applications.
+///
+/// This method performs no allocations.
+///
+/// Returns an iterator producing [`RecvMsg`], one per received messages. Each `RecvMsg` can produce
+/// iterators over [`IoSlice`] with [`iovs`][RecvMsg::iovs`] and
+/// `ControlMessageOwned` with [`cmsgs`][RecvMsg::cmsgs].
+///
+/// # Bugs (in underlying implementation, at least in Linux)
+/// The timeout argument does not work as intended. The timeout is checked only after the receipt
+/// of each datagram, so that if up to `vlen`-1 datagrams are received before the timeout expires,
+/// but then no further datagrams are received, the call will block forever.
+///
+/// If an error occurs after at least one message has been received, the call succeeds, and returns
+/// the number of messages received. The error code is expected to be returned on a subsequent
+/// call to recvmmsg(). In the current implementation, however, the error code can be
+/// overwritten in the meantime by an unrelated network event on a socket, for example an
+/// incoming ICMP packet.
+
+// On aarch64 linux using recvmmsg and trying to get hardware/kernel timestamps might not
+// always produce the desired results - see https://github.com/nix-rust/nix/pull/1744 for more
+// details
+
+#[cfg(any(
+    target_os = "linux",
+    target_os = "android",
+    target_os = "freebsd",
+    target_os = "netbsd",
+))]
+pub fn recvmmsg<'a, XS, S, I>(
+    fd: RawFd,
+    data: &'a mut MultiHeaders<S>,
+    slices: XS,
+    flags: MsgFlags,
+    mut timeout: Option<crate::sys::time::TimeSpec>,
+) -> crate::Result<MultiResults<'a, S>>
+where
+    XS: IntoIterator<Item = &'a I>,
+    I: AsRef<[IoSliceMut<'a>]> + 'a,
+{
+    let mut count = 0;
+    for (i, (slice, mmsghdr)) in slices.into_iter().zip(data.items.iter_mut()).enumerate() {
+        let mut p = &mut mmsghdr.msg_hdr;
+        p.msg_iov = slice.as_ref().as_ptr() as *mut libc::iovec;
+        p.msg_iovlen = slice.as_ref().len() as _;
+        count = i + 1;
+    }
+
+    let timeout_ptr = timeout
+        .as_mut()
+        .map_or_else(std::ptr::null_mut, |t| t as *mut _ as *mut libc::timespec);
+
+    let received = Errno::result(unsafe {
+        libc::recvmmsg(
+            fd,
+            data.items.as_mut_ptr(),
+            count as _,
+            flags.bits() as _,
+            timeout_ptr,
+        )
+    })? as usize;
+
+    Ok(MultiResults {
+        rmm: data,
+        current_index: 0,
+        received,
+    })
+}
+
+#[cfg(any(
+    target_os = "linux",
+    target_os = "android",
+    target_os = "freebsd",
+    target_os = "netbsd",
+))]
+#[derive(Debug)]
+/// Iterator over results of [`recvmmsg`]/[`sendmmsg`]
+///
+///
+pub struct MultiResults<'a, S> {
+    // preallocated structures
+    rmm: &'a MultiHeaders<S>,
+    current_index: usize,
+    received: usize,
+}
+
+#[cfg(any(
+    target_os = "linux",
+    target_os = "android",
+    target_os = "freebsd",
+    target_os = "netbsd",
+))]
+impl<'a, S> Iterator for MultiResults<'a, S>
+where
+    S: Copy + SockaddrLike,
+{
+    type Item = RecvMsg<'a, 'a, S>;
+
+    // The cast is not unnecessary on all platforms.
+    #[allow(clippy::unnecessary_cast)]
+    fn next(&mut self) -> Option<Self::Item> {
+        if self.current_index >= self.received {
+            return None;
+        }
+        let mmsghdr = self.rmm.items[self.current_index];
+
+        // as long as we are not reading past the index writen by recvmmsg - address
+        // will be initialized
+        let address = unsafe { self.rmm.addresses[self.current_index].assume_init() };
+
+        self.current_index += 1;
+        Some(unsafe {
+            read_mhdr(
+                mmsghdr.msg_hdr,
+                mmsghdr.msg_len as isize,
+                self.rmm.msg_controllen,
+                address,
+            )
+        })
+    }
+}
+
+impl<'a, S> RecvMsg<'_, 'a, S> {
+    /// Iterate over the filled io slices pointed by this msghdr
+    pub fn iovs(&self) -> IoSliceIterator<'a> {
+        IoSliceIterator {
+            index: 0,
+            remaining: self.bytes,
+            slices: unsafe {
+                // safe for as long as mgdr is properly initialized and references are valid.
+                // for multi messages API we initialize it with an empty
+                // slice and replace with a concrete buffer
+                // for single message API we hold a lifetime reference to ioslices
+                std::slice::from_raw_parts(self.mhdr.msg_iov as *const _, self.mhdr.msg_iovlen as _)
+            },
+        }
+    }
+}
+
+#[derive(Debug)]
+pub struct IoSliceIterator<'a> {
+    index: usize,
+    remaining: usize,
+    slices: &'a [IoSlice<'a>],
+}
+
+impl<'a> Iterator for IoSliceIterator<'a> {
+    type Item = &'a [u8];
+
+    fn next(&mut self) -> Option<Self::Item> {
+        if self.index >= self.slices.len() {
+            return None;
+        }
+        let slice = &self.slices[self.index][..self.remaining.min(self.slices[self.index].len())];
+        self.remaining -= slice.len();
+        self.index += 1;
+        if slice.is_empty() {
+            return None;
+        }
+
+        Some(slice)
+    }
+}
+
+// test contains both recvmmsg and timestaping which is linux only
+// there are existing tests for recvmmsg only in tests/
+#[cfg(target_os = "linux")]
+#[cfg(test)]
+mod test {
+    use crate::sys::socket::{AddressFamily, ControlMessageOwned};
+    use crate::*;
+    use std::str::FromStr;
+
+    #[cfg_attr(qemu, ignore)]
+    #[test]
+    fn test_recvmm2() -> crate::Result<()> {
+        use crate::sys::socket::{
+            sendmsg, setsockopt, socket, sockopt::Timestamping, MsgFlags, SockFlag, SockType,
+            SockaddrIn, TimestampingFlag,
+        };
+        use std::io::{IoSlice, IoSliceMut};
+
+        let sock_addr = SockaddrIn::from_str("127.0.0.1:6790").unwrap();
+
+        let ssock = socket(
+            AddressFamily::Inet,
+            SockType::Datagram,
+            SockFlag::empty(),
+            None,
+        )?;
+
+        let rsock = socket(
+            AddressFamily::Inet,
+            SockType::Datagram,
+            SockFlag::SOCK_NONBLOCK,
+            None,
+        )?;
+
+        crate::sys::socket::bind(rsock, &sock_addr)?;
+
+        setsockopt(rsock, Timestamping, &TimestampingFlag::all())?;
+
+        let sbuf = (0..400).map(|i| i as u8).collect::<Vec<_>>();
+
+        let mut recv_buf = vec![0; 1024];
+
+        let mut recv_iovs = Vec::new();
+        let mut pkt_iovs = Vec::new();
+
+        for (ix, chunk) in recv_buf.chunks_mut(256).enumerate() {
+            pkt_iovs.push(IoSliceMut::new(chunk));
+            if ix % 2 == 1 {
+                recv_iovs.push(pkt_iovs);
+                pkt_iovs = Vec::new();
+            }
+        }
+        drop(pkt_iovs);
+
+        let flags = MsgFlags::empty();
+        let iov1 = [IoSlice::new(&sbuf)];
+
+        let cmsg = cmsg_space!(crate::sys::socket::Timestamps);
+        sendmsg(ssock, &iov1, &[], flags, Some(&sock_addr)).unwrap();
+
+        let mut data = super::MultiHeaders::<()>::preallocate(recv_iovs.len(), Some(cmsg));
+
+        let t = sys::time::TimeSpec::from_duration(std::time::Duration::from_secs(10));
+
+        let recv = super::recvmmsg(rsock, &mut data, recv_iovs.iter(), flags, Some(t))?;
+
+        for rmsg in recv {
+            #[cfg(not(any(qemu, target_arch = "aarch64")))]
+            let mut saw_time = false;
+            let mut recvd = 0;
+            for cmsg in rmsg.cmsgs() {
+                if let ControlMessageOwned::ScmTimestampsns(timestamps) = cmsg {
+                    let ts = timestamps.system;
+
+                    let sys_time =
+                        crate::time::clock_gettime(crate::time::ClockId::CLOCK_REALTIME)?;
+                    let diff = if ts > sys_time {
+                        ts - sys_time
+                    } else {
+                        sys_time - ts
+                    };
+                    assert!(std::time::Duration::from(diff).as_secs() < 60);
+                    #[cfg(not(any(qemu, target_arch = "aarch64")))]
+                    {
+                        saw_time = true;
+                    }
+                }
+            }
+
+            #[cfg(not(any(qemu, target_arch = "aarch64")))]
+            assert!(saw_time);
+
+            for iov in rmsg.iovs() {
+                recvd += iov.len();
+            }
+            assert_eq!(recvd, 400);
+        }
+
+        Ok(())
+    }
+}
+unsafe fn read_mhdr<'a, 'i, S>(
     mhdr: msghdr,
     r: isize,
     msg_controllen: usize,
-    address: sockaddr_storage,
-    cmsg_buffer: &'a mut Option<&'b mut Vec<u8>>
-) -> RecvMsg<'b> {
+    address: S,
+) -> RecvMsg<'a, 'i, S>
+    where S: SockaddrLike
+{
+    // The cast is not unnecessary on all platforms.
+    #[allow(clippy::unnecessary_cast)]
     let cmsghdr = {
         if mhdr.msg_controllen > 0 {
-            // got control message(s)
-            cmsg_buffer
-                .as_mut()
-                .unwrap()
-                .set_len(mhdr.msg_controllen as usize);
             debug_assert!(!mhdr.msg_control.is_null());
             debug_assert!(msg_controllen >= mhdr.msg_controllen as usize);
             CMSG_FIRSTHDR(&mhdr as *const msghdr)
@@ -1393,71 +1932,64 @@
         }.as_ref()
     };
 
-    let address = sockaddr_storage_to_addr(
-        &address ,
-         mhdr.msg_namelen as usize
-    ).ok();
-
     RecvMsg {
         bytes: r as usize,
         cmsghdr,
-        address,
+        address: Some(address),
         flags: MsgFlags::from_bits_truncate(mhdr.msg_flags),
         mhdr,
+        iobufs: std::marker::PhantomData,
     }
 }
 
-unsafe fn pack_mhdr_to_receive<'a, I>(
-    iov: I,
-    cmsg_buffer: &mut Option<&mut Vec<u8>>,
-    address: *mut sockaddr_storage,
-) -> (usize, msghdr)
+/// Pack pointers to various structures into into msghdr
+///
+/// # Safety
+/// `iov_buffer` and `iov_buffer_len` must point to a slice
+/// of `IoSliceMut` and number of available elements or be a null pointer and 0
+///
+/// `cmsg_buffer` and `cmsg_capacity` must point to a byte buffer used
+/// to store control headers later or be a null pointer and 0 if control
+/// headers are not used
+///
+/// Buffers must remain valid for the whole lifetime of msghdr
+unsafe fn pack_mhdr_to_receive<S>(
+    iov_buffer: *const IoSliceMut,
+    iov_buffer_len: usize,
+    cmsg_buffer: *const u8,
+    cmsg_capacity: usize,
+    address: *mut S,
+) -> msghdr
     where
-        I: AsRef<[IoVec<&'a mut [u8]>]> + 'a,
+        S: SockaddrLike
 {
-    let (msg_control, msg_controllen) = cmsg_buffer.as_mut()
-        .map(|v| (v.as_mut_ptr(), v.capacity()))
-        .unwrap_or((ptr::null_mut(), 0));
-
-    let mhdr = {
-        // Musl's msghdr has private fields, so this is the only way to
-        // initialize it.
-        let mut mhdr = mem::MaybeUninit::<msghdr>::zeroed();
-        let p = mhdr.as_mut_ptr();
-        (*p).msg_name = address as *mut c_void;
-        (*p).msg_namelen = mem::size_of::<sockaddr_storage>() as socklen_t;
-        (*p).msg_iov = iov.as_ref().as_ptr() as *mut iovec;
-        (*p).msg_iovlen = iov.as_ref().len() as _;
-        (*p).msg_control = msg_control as *mut c_void;
-        (*p).msg_controllen = msg_controllen as _;
-        (*p).msg_flags = 0;
-        mhdr.assume_init()
-    };
-
-    (msg_controllen, mhdr)
+    // Musl's msghdr has private fields, so this is the only way to
+    // initialize it.
+    let mut mhdr = mem::MaybeUninit::<msghdr>::zeroed();
+    let p = mhdr.as_mut_ptr();
+    (*p).msg_name = (*address).as_mut_ptr() as *mut c_void;
+    (*p).msg_namelen = S::size();
+    (*p).msg_iov = iov_buffer as *mut iovec;
+    (*p).msg_iovlen = iov_buffer_len as _;
+    (*p).msg_control = cmsg_buffer as *mut c_void;
+    (*p).msg_controllen = cmsg_capacity as _;
+    (*p).msg_flags = 0;
+    mhdr.assume_init()
 }
 
-fn pack_mhdr_to_send<'a, I, C>(
+fn pack_mhdr_to_send<'a, I, C, S>(
     cmsg_buffer: &mut [u8],
     iov: I,
     cmsgs: C,
-    addr: Option<&SockAddr>
+    addr: Option<&S>
 ) -> msghdr
     where
-        I: AsRef<[IoVec<&'a [u8]>]>,
-        C: AsRef<[ControlMessage<'a>]>
+        I: AsRef<[IoSlice<'a>]>,
+        C: AsRef<[ControlMessage<'a>]>,
+        S: SockaddrLike + 'a
 {
     let capacity = cmsg_buffer.len();
 
-    // Next encode the sending address, if provided
-    let (name, namelen) = match addr {
-        Some(addr) => {
-            let (x, y) = addr.as_ffi_pair();
-            (x as *const _, y)
-        },
-        None => (ptr::null(), 0),
-    };
-
     // The message header must be initialized before the individual cmsgs.
     let cmsg_ptr = if capacity > 0 {
         cmsg_buffer.as_ptr() as *mut c_void
@@ -1470,8 +2002,8 @@
         // initialize it.
         let mut mhdr = mem::MaybeUninit::<msghdr>::zeroed();
         let p = mhdr.as_mut_ptr();
-        (*p).msg_name = name as *mut _;
-        (*p).msg_namelen = namelen;
+        (*p).msg_name = addr.map(S::as_ptr).unwrap_or(ptr::null()) as *mut _;
+        (*p).msg_namelen = addr.map(S::len).unwrap_or(0);
         // transmute iov into a mutable pointer.  sendmsg doesn't really mutate
         // the buffer, but the standard says that it takes a mutable pointer
         (*p).msg_iov = iov.as_ref().as_ptr() as *mut _;
@@ -1507,28 +2039,33 @@
 /// * `fd`:             Socket file descriptor
 /// * `iov`:            Scatter-gather list of buffers to receive the message
 /// * `cmsg_buffer`:    Space to receive ancillary data.  Should be created by
-///                     [`cmsg_space!`](macro.cmsg_space.html)
+///                     [`cmsg_space!`](../../macro.cmsg_space.html)
 /// * `flags`:          Optional flags passed directly to the operating system.
 ///
 /// # References
 /// [recvmsg(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvmsg.html)
-pub fn recvmsg<'a>(fd: RawFd, iov: &[IoVec<&mut [u8]>],
+pub fn recvmsg<'a, 'outer, 'inner, S>(fd: RawFd, iov: &'outer mut [IoSliceMut<'inner>],
                    mut cmsg_buffer: Option<&'a mut Vec<u8>>,
-                   flags: MsgFlags) -> Result<RecvMsg<'a>>
+                   flags: MsgFlags) -> Result<RecvMsg<'a, 'inner, S>>
+    where S: SockaddrLike + 'a,
+    'inner: 'outer
 {
     let mut address = mem::MaybeUninit::uninit();
 
-    let (msg_controllen, mut mhdr) = unsafe {
-        pack_mhdr_to_receive(&iov, &mut cmsg_buffer, address.as_mut_ptr())
+    let (msg_control, msg_controllen) = cmsg_buffer.as_mut()
+        .map(|v| (v.as_mut_ptr(), v.capacity()))
+        .unwrap_or((ptr::null_mut(), 0));
+    let mut mhdr = unsafe {
+        pack_mhdr_to_receive(iov.as_ref().as_ptr(), iov.len(), msg_control, msg_controllen, address.as_mut_ptr())
     };
 
     let ret = unsafe { libc::recvmsg(fd, &mut mhdr, flags.bits()) };
 
     let r = Errno::result(ret)?;
 
-    Ok(unsafe { read_mhdr(mhdr, r, msg_controllen, address.assume_init(), &mut cmsg_buffer) })
+    Ok(unsafe { read_mhdr(mhdr, r, msg_controllen, address.assume_init()) })
 }
-
+}
 
 /// Create an endpoint for communication
 ///
@@ -1540,7 +2077,12 @@
 /// specified in this manner.
 ///
 /// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html)
-pub fn socket<T: Into<Option<SockProtocol>>>(domain: AddressFamily, ty: SockType, flags: SockFlag, protocol: T) -> Result<RawFd> {
+pub fn socket<T: Into<Option<SockProtocol>>>(
+    domain: AddressFamily,
+    ty: SockType,
+    flags: SockFlag,
+    protocol: T,
+) -> Result<RawFd> {
     let protocol = match protocol.into() {
         None => 0,
         Some(p) => p as c_int,
@@ -1560,8 +2102,12 @@
 /// Create a pair of connected sockets
 ///
 /// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/socketpair.html)
-pub fn socketpair<T: Into<Option<SockProtocol>>>(domain: AddressFamily, ty: SockType, protocol: T,
-                  flags: SockFlag) -> Result<(RawFd, RawFd)> {
+pub fn socketpair<T: Into<Option<SockProtocol>>>(
+    domain: AddressFamily,
+    ty: SockType,
+    protocol: T,
+    flags: SockFlag,
+) -> Result<(RawFd, RawFd)> {
     let protocol = match protocol.into() {
         None => 0,
         Some(p) => p as c_int,
@@ -1575,7 +2121,9 @@
 
     let mut fds = [-1, -1];
 
-    let res = unsafe { libc::socketpair(domain as c_int, ty, protocol, fds.as_mut_ptr()) };
+    let res = unsafe {
+        libc::socketpair(domain as c_int, ty, protocol, fds.as_mut_ptr())
+    };
     Errno::result(res)?;
 
     Ok((fds[0], fds[1]))
@@ -1593,11 +2141,8 @@
 /// Bind a name to a socket
 ///
 /// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html)
-pub fn bind(fd: RawFd, addr: &SockAddr) -> Result<()> {
-    let res = unsafe {
-        let (ptr, len) = addr.as_ffi_pair();
-        libc::bind(fd, ptr, len)
-    };
+pub fn bind(fd: RawFd, addr: &dyn SockaddrLike) -> Result<()> {
+    let res = unsafe { libc::bind(fd, addr.as_ptr(), addr.len()) };
 
     Errno::result(res).map(drop)
 }
@@ -1614,19 +2159,28 @@
 /// Accept a connection on a socket
 ///
 /// [Further reading](https://man7.org/linux/man-pages/man2/accept.2.html)
-#[cfg(any(all(
-            target_os = "android",
-            any(
-                target_arch = "aarch64",
-                target_arch = "x86",
-                target_arch = "x86_64"
-            )
-          ),
-          target_os = "freebsd",
-          target_os = "linux",
-          target_os = "openbsd"))]
+#[cfg(any(
+    all(
+        target_os = "android",
+        any(
+            target_arch = "aarch64",
+            target_arch = "x86",
+            target_arch = "x86_64"
+        )
+    ),
+    target_os = "dragonfly",
+    target_os = "emscripten",
+    target_os = "freebsd",
+    target_os = "fuchsia",
+    target_os = "illumos",
+    target_os = "linux",
+    target_os = "netbsd",
+    target_os = "openbsd"
+))]
 pub fn accept4(sockfd: RawFd, flags: SockFlag) -> Result<RawFd> {
-    let res = unsafe { libc::accept4(sockfd, ptr::null_mut(), ptr::null_mut(), flags.bits()) };
+    let res = unsafe {
+        libc::accept4(sockfd, ptr::null_mut(), ptr::null_mut(), flags.bits())
+    };
 
     Errno::result(res)
 }
@@ -1634,11 +2188,8 @@
 /// Initiate a connection on a socket
 ///
 /// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html)
-pub fn connect(fd: RawFd, addr: &SockAddr) -> Result<()> {
-    let res = unsafe {
-        let (ptr, len) = addr.as_ffi_pair();
-        libc::connect(fd, ptr, len)
-    };
+pub fn connect(fd: RawFd, addr: &dyn SockaddrLike) -> Result<()> {
+    let res = unsafe { libc::connect(fd, addr.as_ptr(), addr.len()) };
 
     Errno::result(res).map(drop)
 }
@@ -1653,7 +2204,8 @@
             sockfd,
             buf.as_ptr() as *mut c_void,
             buf.len() as size_t,
-            flags.bits());
+            flags.bits(),
+        );
 
         Errno::result(ret).map(|r| r as usize)
     }
@@ -1664,36 +2216,51 @@
 /// address of the sender.
 ///
 /// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvfrom.html)
-pub fn recvfrom(sockfd: RawFd, buf: &mut [u8])
-    -> Result<(usize, Option<SockAddr>)>
-{
+pub fn recvfrom<T: SockaddrLike>(
+    sockfd: RawFd,
+    buf: &mut [u8],
+) -> Result<(usize, Option<T>)> {
     unsafe {
-        let mut addr: sockaddr_storage = mem::zeroed();
-        let mut len = mem::size_of::<sockaddr_storage>() as socklen_t;
+        let mut addr = mem::MaybeUninit::<T>::uninit();
+        let mut len = mem::size_of_val(&addr) as socklen_t;
 
         let ret = Errno::result(libc::recvfrom(
             sockfd,
             buf.as_ptr() as *mut c_void,
             buf.len() as size_t,
             0,
-            &mut addr as *mut libc::sockaddr_storage as *mut libc::sockaddr,
-            &mut len as *mut socklen_t))? as usize;
+            addr.as_mut_ptr() as *mut libc::sockaddr,
+            &mut len as *mut socklen_t,
+        ))? as usize;
 
-        match sockaddr_storage_to_addr(&addr, len as usize) {
-            Err(Errno::ENOTCONN) => Ok((ret, None)),
-            Ok(addr) => Ok((ret, Some(addr))),
-            Err(e) => Err(e)
-        }
+        Ok((
+            ret,
+            T::from_raw(
+                addr.assume_init().as_ptr() as *const libc::sockaddr,
+                Some(len),
+            ),
+        ))
     }
 }
 
 /// Send a message to a socket
 ///
 /// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendto.html)
-pub fn sendto(fd: RawFd, buf: &[u8], addr: &SockAddr, flags: MsgFlags) -> Result<usize> {
+pub fn sendto(
+    fd: RawFd,
+    buf: &[u8],
+    addr: &dyn SockaddrLike,
+    flags: MsgFlags,
+) -> Result<usize> {
     let ret = unsafe {
-        let (ptr, len) = addr.as_ffi_pair();
-        libc::sendto(fd, buf.as_ptr() as *const c_void, buf.len() as size_t, flags.bits(), ptr, len)
+        libc::sendto(
+            fd,
+            buf.as_ptr() as *const c_void,
+            buf.len() as size_t,
+            flags.bits(),
+            addr.as_ptr(),
+            addr.len(),
+        )
     };
 
     Errno::result(ret).map(|r| r as usize)
@@ -1704,7 +2271,12 @@
 /// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/send.html)
 pub fn send(fd: RawFd, buf: &[u8], flags: MsgFlags) -> Result<usize> {
     let ret = unsafe {
-        libc::send(fd, buf.as_ptr() as *const c_void, buf.len() as size_t, flags.bits())
+        libc::send(
+            fd,
+            buf.as_ptr() as *const c_void,
+            buf.len() as size_t,
+            flags.bits(),
+        )
     };
 
     Errno::result(ret).map(|r| r as usize)
@@ -1717,7 +2289,7 @@
  */
 
 /// Represents a socket option that can be retrieved.
-pub trait GetSockOpt : Copy {
+pub trait GetSockOpt: Copy {
     type Val;
 
     /// Look up the value of this socket option on the given socket.
@@ -1725,7 +2297,7 @@
 }
 
 /// Represents a socket option that can be set.
-pub trait SetSockOpt : Clone {
+pub trait SetSockOpt: Clone {
     type Val;
 
     /// Set the value of this socket option on the given socket.
@@ -1756,47 +2328,51 @@
 /// let res = setsockopt(fd, KeepAlive, &true);
 /// assert!(res.is_ok());
 /// ```
-pub fn setsockopt<O: SetSockOpt>(fd: RawFd, opt: O, val: &O::Val) -> Result<()> {
+pub fn setsockopt<O: SetSockOpt>(
+    fd: RawFd,
+    opt: O,
+    val: &O::Val,
+) -> Result<()> {
     opt.set(fd, val)
 }
 
 /// Get the address of the peer connected to the socket `fd`.
 ///
 /// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpeername.html)
-pub fn getpeername(fd: RawFd) -> Result<SockAddr> {
+pub fn getpeername<T: SockaddrLike>(fd: RawFd) -> Result<T> {
     unsafe {
-        let mut addr = mem::MaybeUninit::uninit();
-        let mut len = mem::size_of::<sockaddr_storage>() as socklen_t;
+        let mut addr = mem::MaybeUninit::<T>::uninit();
+        let mut len = T::size();
 
         let ret = libc::getpeername(
             fd,
             addr.as_mut_ptr() as *mut libc::sockaddr,
-            &mut len
+            &mut len,
         );
 
         Errno::result(ret)?;
 
-        sockaddr_storage_to_addr(&addr.assume_init(), len as usize)
+        T::from_raw(addr.assume_init().as_ptr(), Some(len)).ok_or(Errno::EINVAL)
     }
 }
 
 /// Get the current address to which the socket `fd` is bound.
 ///
 /// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockname.html)
-pub fn getsockname(fd: RawFd) -> Result<SockAddr> {
+pub fn getsockname<T: SockaddrLike>(fd: RawFd) -> Result<T> {
     unsafe {
-        let mut addr = mem::MaybeUninit::uninit();
-        let mut len = mem::size_of::<sockaddr_storage>() as socklen_t;
+        let mut addr = mem::MaybeUninit::<T>::uninit();
+        let mut len = T::size();
 
         let ret = libc::getsockname(
             fd,
             addr.as_mut_ptr() as *mut libc::sockaddr,
-            &mut len
+            &mut len,
         );
 
         Errno::result(ret)?;
 
-        sockaddr_storage_to_addr(&addr.assume_init(), len as usize)
+        T::from_raw(addr.assume_init().as_ptr(), Some(len)).ok_or(Errno::EINVAL)
     }
 }
 
@@ -1808,78 +2384,73 @@
 /// allocated and valid.  It must be at least as large as all the useful parts
 /// of the structure.  Note that in the case of a `sockaddr_un`, `len` need not
 /// include the terminating null.
+#[deprecated(
+    since = "0.24.0",
+    note = "use SockaddrLike or SockaddrStorage instead"
+)]
+#[allow(deprecated)]
 pub fn sockaddr_storage_to_addr(
     addr: &sockaddr_storage,
-    len: usize) -> Result<SockAddr> {
-
+    len: usize,
+) -> Result<SockAddr> {
     assert!(len <= mem::size_of::<sockaddr_storage>());
     if len < mem::size_of_val(&addr.ss_family) {
         return Err(Errno::ENOTCONN);
     }
 
     match c_int::from(addr.ss_family) {
+        #[cfg(feature = "net")]
         libc::AF_INET => {
-            assert!(len as usize >= mem::size_of::<sockaddr_in>());
+            assert!(len >= mem::size_of::<sockaddr_in>());
             let sin = unsafe {
                 *(addr as *const sockaddr_storage as *const sockaddr_in)
             };
             Ok(SockAddr::Inet(InetAddr::V4(sin)))
         }
+        #[cfg(feature = "net")]
         libc::AF_INET6 => {
-            assert!(len as usize >= mem::size_of::<sockaddr_in6>());
-            let sin6 = unsafe {
-                *(addr as *const _ as *const sockaddr_in6)
-            };
+            assert!(len >= mem::size_of::<sockaddr_in6>());
+            let sin6 = unsafe { *(addr as *const _ as *const sockaddr_in6) };
             Ok(SockAddr::Inet(InetAddr::V6(sin6)))
         }
-        libc::AF_UNIX => {
-            let pathlen = len - offset_of!(sockaddr_un, sun_path);
-            unsafe {
-                let sun = *(addr as *const _ as *const sockaddr_un);
-                Ok(SockAddr::Unix(UnixAddr::from_raw_parts(sun, pathlen)))
-            }
-        }
+        libc::AF_UNIX => unsafe {
+            let sun = *(addr as *const _ as *const sockaddr_un);
+            let sun_len = len.try_into().unwrap();
+            Ok(SockAddr::Unix(UnixAddr::from_raw_parts(sun, sun_len)))
+        },
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg(feature = "net")]
         libc::AF_PACKET => {
             use libc::sockaddr_ll;
             // Don't assert anything about the size.
             // Apparently the Linux kernel can return smaller sizes when
             // the value in the last element of sockaddr_ll (`sll_addr`) is
             // smaller than the declared size of that field
-            let sll = unsafe {
-                *(addr as *const _ as *const sockaddr_ll)
-            };
+            let sll = unsafe { *(addr as *const _ as *const sockaddr_ll) };
             Ok(SockAddr::Link(LinkAddr(sll)))
         }
         #[cfg(any(target_os = "android", target_os = "linux"))]
         libc::AF_NETLINK => {
             use libc::sockaddr_nl;
-            let snl = unsafe {
-                *(addr as *const _ as *const sockaddr_nl)
-            };
+            let snl = unsafe { *(addr as *const _ as *const sockaddr_nl) };
             Ok(SockAddr::Netlink(NetlinkAddr(snl)))
         }
         #[cfg(any(target_os = "android", target_os = "linux"))]
         libc::AF_ALG => {
             use libc::sockaddr_alg;
-            let salg = unsafe {
-                *(addr as *const _ as *const sockaddr_alg)
-            };
+            let salg = unsafe { *(addr as *const _ as *const sockaddr_alg) };
             Ok(SockAddr::Alg(AlgAddr(salg)))
         }
         #[cfg(any(target_os = "android", target_os = "linux"))]
         libc::AF_VSOCK => {
             use libc::sockaddr_vm;
-            let svm = unsafe {
-                *(addr as *const _ as *const sockaddr_vm)
-            };
+            let svm = unsafe { *(addr as *const _ as *const sockaddr_vm) };
             Ok(SockAddr::Vsock(VsockAddr(svm)))
         }
         af => panic!("unexpected address family {}", af),
     }
 }
 
-
 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
 pub enum Shutdown {
     /// Further receptions will be disallowed.
@@ -1898,9 +2469,9 @@
         use libc::shutdown;
 
         let how = match how {
-            Shutdown::Read  => libc::SHUT_RD,
+            Shutdown::Read => libc::SHUT_RD,
             Shutdown::Write => libc::SHUT_WR,
-            Shutdown::Both  => libc::SHUT_RDWR,
+            Shutdown::Both => libc::SHUT_RDWR,
         };
 
         Errno::result(shutdown(df, how)).map(drop)
diff --git a/src/sys/socket/sockopt.rs b/src/sys/socket/sockopt.rs
index fcb4be8..06e9ee4 100644
--- a/src/sys/socket/sockopt.rs
+++ b/src/sys/socket/sockopt.rs
@@ -1,22 +1,23 @@
 //! Socket options as used by `setsockopt` and `getsockopt`.
-use cfg_if::cfg_if;
 use super::{GetSockOpt, SetSockOpt};
-use crate::Result;
 use crate::errno::Errno;
 use crate::sys::time::TimeVal;
+use crate::Result;
+use cfg_if::cfg_if;
 use libc::{self, c_int, c_void, socklen_t};
-use std::mem::{
-    self,
-    MaybeUninit
-};
-use std::os::unix::io::RawFd;
 use std::ffi::{OsStr, OsString};
+use std::{
+    convert::TryFrom,
+    mem::{self, MaybeUninit}
+};
 #[cfg(target_family = "unix")]
 use std::os::unix::ffi::OsStrExt;
+use std::os::unix::io::RawFd;
 
 // Constants
 // TCP_CA_NAME_MAX isn't defined in user space include files
-#[cfg(any(target_os = "freebsd", target_os = "linux"))] 
+#[cfg(any(target_os = "freebsd", target_os = "linux"))]
+#[cfg(feature = "net")]
 const TCP_CA_NAME_MAX: usize = 16;
 
 /// Helper for implementing `SetSockOpt` for a given socket option. See
@@ -50,14 +51,18 @@
                 unsafe {
                     let setter: $setter = Set::new(val);
 
-                    let res = libc::setsockopt(fd, $level, $flag,
-                                               setter.ffi_ptr(),
-                                               setter.ffi_len());
+                    let res = libc::setsockopt(
+                        fd,
+                        $level,
+                        $flag,
+                        setter.ffi_ptr(),
+                        setter.ffi_len(),
+                    );
                     Errno::result(res).map(drop)
                 }
             }
         }
-    }
+    };
 }
 
 /// Helper for implementing `GetSockOpt` for a given socket option. See
@@ -91,16 +96,23 @@
                 unsafe {
                     let mut getter: $getter = Get::uninit();
 
-                    let res = libc::getsockopt(fd, $level, $flag,
-                                               getter.ffi_ptr(),
-                                               getter.ffi_len());
+                    let res = libc::getsockopt(
+                        fd,
+                        $level,
+                        $flag,
+                        getter.ffi_ptr(),
+                        getter.ffi_len(),
+                    );
                     Errno::result(res)?;
 
-                    Ok(getter.assume_init())
+                    match <$ty>::try_from(getter.assume_init()) {
+                        Err(_) => Err(Errno::EINVAL),
+                        Ok(r) => Ok(r)
+                    }
                 }
             }
         }
-    }
+    };
 }
 
 /// Helper to generate the sockopt accessors. See
@@ -128,6 +140,9 @@
 /// * `$ty:ty`: type of the value that will be get/set.
 /// * `$getter:ty`: `Get` implementation; optional; only for `GetOnly` and `Both`.
 /// * `$setter:ty`: `Set` implementation; optional; only for `SetOnly` and `Both`.
+// Some targets don't use all rules.
+#[allow(unknown_lints)]
+#[allow(unused_macro_rules)]
 macro_rules! sockopt_impl {
     ($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, bool) => {
         sockopt_impl!($(#[$attr])*
@@ -244,14 +259,25 @@
 
 sockopt_impl!(
     /// Enables local address reuse
-    ReuseAddr, Both, libc::SOL_SOCKET, libc::SO_REUSEADDR, bool
+    ReuseAddr,
+    Both,
+    libc::SOL_SOCKET,
+    libc::SO_REUSEADDR,
+    bool
 );
 #[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
 sockopt_impl!(
     /// Permits multiple AF_INET or AF_INET6 sockets to be bound to an
     /// identical socket address.
-    ReusePort, Both, libc::SOL_SOCKET, libc::SO_REUSEPORT, bool);
+    ReusePort,
+    Both,
+    libc::SOL_SOCKET,
+    libc::SO_REUSEPORT,
+    bool
+);
+#[cfg(feature = "net")]
 sockopt_impl!(
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
     /// Under most circumstances, TCP sends data when it is presented; when
     /// outstanding data has not yet been acknowledged, it gathers small amounts
     /// of output to be sent in a single packet once an acknowledgement is
@@ -259,26 +285,52 @@
     /// send a stream of mouse events which receive no replies, this
     /// packetization may cause significant delays.  The boolean option
     /// TCP_NODELAY defeats this algorithm.
-    TcpNoDelay, Both, libc::IPPROTO_TCP, libc::TCP_NODELAY, bool);
+    TcpNoDelay,
+    Both,
+    libc::IPPROTO_TCP,
+    libc::TCP_NODELAY,
+    bool
+);
 sockopt_impl!(
     /// When enabled,  a close(2) or shutdown(2) will not return until all
     /// queued messages for the socket have been successfully sent or the
     /// linger timeout has been reached.
-    Linger, Both, libc::SOL_SOCKET, libc::SO_LINGER, libc::linger);
+    Linger,
+    Both,
+    libc::SOL_SOCKET,
+    libc::SO_LINGER,
+    libc::linger
+);
+#[cfg(feature = "net")]
 sockopt_impl!(
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
     /// Join a multicast group
-    IpAddMembership, SetOnly, libc::IPPROTO_IP, libc::IP_ADD_MEMBERSHIP,
-    super::IpMembershipRequest);
+    IpAddMembership,
+    SetOnly,
+    libc::IPPROTO_IP,
+    libc::IP_ADD_MEMBERSHIP,
+    super::IpMembershipRequest
+);
+#[cfg(feature = "net")]
 sockopt_impl!(
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
     /// Leave a multicast group.
-    IpDropMembership, SetOnly, libc::IPPROTO_IP, libc::IP_DROP_MEMBERSHIP,
-    super::IpMembershipRequest);
+    IpDropMembership,
+    SetOnly,
+    libc::IPPROTO_IP,
+    libc::IP_DROP_MEMBERSHIP,
+    super::IpMembershipRequest
+);
 cfg_if! {
     if #[cfg(any(target_os = "android", target_os = "linux"))] {
+        #[cfg(feature = "net")]
         sockopt_impl!(
+            #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
             /// Join an IPv6 multicast group.
             Ipv6AddMembership, SetOnly, libc::IPPROTO_IPV6, libc::IPV6_ADD_MEMBERSHIP, super::Ipv6MembershipRequest);
+        #[cfg(feature = "net")]
         sockopt_impl!(
+            #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
             /// Leave an IPv6 multicast group.
             Ipv6DropMembership, SetOnly, libc::IPPROTO_IPV6, libc::IPV6_DROP_MEMBERSHIP, super::Ipv6MembershipRequest);
     } else if #[cfg(any(target_os = "dragonfly",
@@ -289,77 +341,199 @@
                         target_os = "netbsd",
                         target_os = "openbsd",
                         target_os = "solaris"))] {
+        #[cfg(feature = "net")]
         sockopt_impl!(
+            #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
             /// Join an IPv6 multicast group.
             Ipv6AddMembership, SetOnly, libc::IPPROTO_IPV6,
             libc::IPV6_JOIN_GROUP, super::Ipv6MembershipRequest);
+        #[cfg(feature = "net")]
         sockopt_impl!(
+            #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
             /// Leave an IPv6 multicast group.
             Ipv6DropMembership, SetOnly, libc::IPPROTO_IPV6,
             libc::IPV6_LEAVE_GROUP, super::Ipv6MembershipRequest);
     }
 }
+#[cfg(feature = "net")]
 sockopt_impl!(
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
     /// Set or read the time-to-live value of outgoing multicast packets for
     /// this socket.
-    IpMulticastTtl, Both, libc::IPPROTO_IP, libc::IP_MULTICAST_TTL, u8);
+    IpMulticastTtl,
+    Both,
+    libc::IPPROTO_IP,
+    libc::IP_MULTICAST_TTL,
+    u8
+);
+#[cfg(feature = "net")]
 sockopt_impl!(
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
     /// Set or read a boolean integer argument that determines whether sent
     /// multicast packets should be looped back to the local sockets.
-    IpMulticastLoop, Both, libc::IPPROTO_IP, libc::IP_MULTICAST_LOOP, bool);
-#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+    IpMulticastLoop,
+    Both,
+    libc::IPPROTO_IP,
+    libc::IP_MULTICAST_LOOP,
+    bool
+);
+#[cfg(target_os = "linux")]
+#[cfg(feature = "net")]
 sockopt_impl!(
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+    /// Set the protocol-defined priority for all packets to be
+    /// sent on this socket
+    Priority,
+    Both,
+    libc::SOL_SOCKET,
+    libc::SO_PRIORITY,
+    libc::c_int
+);
+#[cfg(target_os = "linux")]
+#[cfg(feature = "net")]
+sockopt_impl!(
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+    /// Set or receive the Type-Of-Service (TOS) field that is
+    /// sent with every IP packet originating from this socket
+    IpTos,
+    Both,
+    libc::IPPROTO_IP,
+    libc::IP_TOS,
+    libc::c_int
+);
+#[cfg(target_os = "linux")]
+#[cfg(feature = "net")]
+sockopt_impl!(
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+    /// Traffic class associated with outgoing packets
+    Ipv6TClass,
+    Both,
+    libc::IPPROTO_IPV6,
+    libc::IPV6_TCLASS,
+    libc::c_int
+);
+#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
     /// If enabled, this boolean option allows binding to an IP address that
     /// is nonlocal or does not (yet) exist.
-    IpFreebind, Both, libc::IPPROTO_IP, libc::IP_FREEBIND, bool);
+    IpFreebind,
+    Both,
+    libc::IPPROTO_IP,
+    libc::IP_FREEBIND,
+    bool
+);
 sockopt_impl!(
     /// Specify the receiving timeout until reporting an error.
-    ReceiveTimeout, Both, libc::SOL_SOCKET, libc::SO_RCVTIMEO, TimeVal);
+    ReceiveTimeout,
+    Both,
+    libc::SOL_SOCKET,
+    libc::SO_RCVTIMEO,
+    TimeVal
+);
 sockopt_impl!(
     /// Specify the sending timeout until reporting an error.
-    SendTimeout, Both, libc::SOL_SOCKET, libc::SO_SNDTIMEO, TimeVal);
+    SendTimeout,
+    Both,
+    libc::SOL_SOCKET,
+    libc::SO_SNDTIMEO,
+    TimeVal
+);
 sockopt_impl!(
     /// Set or get the broadcast flag.
-    Broadcast, Both, libc::SOL_SOCKET, libc::SO_BROADCAST, bool);
+    Broadcast,
+    Both,
+    libc::SOL_SOCKET,
+    libc::SO_BROADCAST,
+    bool
+);
 sockopt_impl!(
     /// If this option is enabled, out-of-band data is directly placed into
     /// the receive data stream.
-    OobInline, Both, libc::SOL_SOCKET, libc::SO_OOBINLINE, bool);
+    OobInline,
+    Both,
+    libc::SOL_SOCKET,
+    libc::SO_OOBINLINE,
+    bool
+);
 sockopt_impl!(
     /// Get and clear the pending socket error.
-    SocketError, GetOnly, libc::SOL_SOCKET, libc::SO_ERROR, i32);
+    SocketError,
+    GetOnly,
+    libc::SOL_SOCKET,
+    libc::SO_ERROR,
+    i32
+);
+sockopt_impl!(
+    /// Set or get the don't route flag.
+    DontRoute,
+    Both,
+    libc::SOL_SOCKET,
+    libc::SO_DONTROUTE,
+    bool
+);
 sockopt_impl!(
     /// Enable sending of keep-alive messages on connection-oriented sockets.
-    KeepAlive, Both, libc::SOL_SOCKET, libc::SO_KEEPALIVE, bool);
+    KeepAlive,
+    Both,
+    libc::SOL_SOCKET,
+    libc::SO_KEEPALIVE,
+    bool
+);
 #[cfg(any(
-        target_os = "dragonfly",
-        target_os = "freebsd",
-        target_os = "macos",
-        target_os = "ios"
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "macos",
+    target_os = "ios"
 ))]
 sockopt_impl!(
     /// Get the credentials of the peer process of a connected unix domain
     /// socket.
-    LocalPeerCred, GetOnly, 0, libc::LOCAL_PEERCRED, super::XuCred);
+    LocalPeerCred,
+    GetOnly,
+    0,
+    libc::LOCAL_PEERCRED,
+    super::XuCred
+);
 #[cfg(any(target_os = "android", target_os = "linux"))]
 sockopt_impl!(
     /// Return the credentials of the foreign process connected to this socket.
-    PeerCredentials, GetOnly, libc::SOL_SOCKET, libc::SO_PEERCRED, super::UnixCredentials);
-#[cfg(any(target_os = "ios",
-          target_os = "macos"))]
+    PeerCredentials,
+    GetOnly,
+    libc::SOL_SOCKET,
+    libc::SO_PEERCRED,
+    super::UnixCredentials
+);
+#[cfg(any(target_os = "ios", target_os = "macos"))]
+#[cfg(feature = "net")]
 sockopt_impl!(
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
     /// Specify the amount of time, in seconds, that the connection must be idle
     /// before keepalive probes (if enabled) are sent.
-    TcpKeepAlive, Both, libc::IPPROTO_TCP, libc::TCP_KEEPALIVE, u32);
-#[cfg(any(target_os = "android",
-          target_os = "dragonfly",
-          target_os = "freebsd",
-          target_os = "linux",
-          target_os = "nacl"))]
+    TcpKeepAlive,
+    Both,
+    libc::IPPROTO_TCP,
+    libc::TCP_KEEPALIVE,
+    u32
+);
+#[cfg(any(
+    target_os = "android",
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "linux"
+))]
+#[cfg(feature = "net")]
 sockopt_impl!(
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
     /// The time (in seconds) the connection needs to remain idle before TCP
     /// starts sending keepalive probes
-    TcpKeepIdle, Both, libc::IPPROTO_TCP, libc::TCP_KEEPIDLE, u32);
+    TcpKeepIdle,
+    Both,
+    libc::IPPROTO_TCP,
+    libc::TCP_KEEPIDLE,
+    u32
+);
 cfg_if! {
     if #[cfg(any(target_os = "android", target_os = "linux"))] {
         sockopt_impl!(
@@ -371,103 +545,234 @@
             TcpMaxSeg, GetOnly, libc::IPPROTO_TCP, libc::TCP_MAXSEG, u32);
     }
 }
-#[cfg(not(target_os = "openbsd"))]
+#[cfg(not(any(target_os = "openbsd", target_os = "haiku")))]
+#[cfg(feature = "net")]
 sockopt_impl!(
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
     /// The maximum number of keepalive probes TCP should send before
     /// dropping the connection.
-    TcpKeepCount, Both, libc::IPPROTO_TCP, libc::TCP_KEEPCNT, u32);
-#[cfg(any(target_os = "android",
-          target_os = "fuchsia",
-          target_os = "linux"))]
+    TcpKeepCount,
+    Both,
+    libc::IPPROTO_TCP,
+    libc::TCP_KEEPCNT,
+    u32
+);
+#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
 sockopt_impl!(
     #[allow(missing_docs)]
     // Not documented by Linux!
-    TcpRepair, Both, libc::IPPROTO_TCP, libc::TCP_REPAIR, u32);
-#[cfg(not(target_os = "openbsd"))]
+    TcpRepair,
+    Both,
+    libc::IPPROTO_TCP,
+    libc::TCP_REPAIR,
+    u32
+);
+#[cfg(not(any(target_os = "openbsd", target_os = "haiku")))]
+#[cfg(feature = "net")]
 sockopt_impl!(
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
     /// The time (in seconds) between individual keepalive probes.
-    TcpKeepInterval, Both, libc::IPPROTO_TCP, libc::TCP_KEEPINTVL, u32);
+    TcpKeepInterval,
+    Both,
+    libc::IPPROTO_TCP,
+    libc::TCP_KEEPINTVL,
+    u32
+);
 #[cfg(any(target_os = "fuchsia", target_os = "linux"))]
+#[cfg(feature = "net")]
 sockopt_impl!(
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
     /// Specifies the maximum amount of time in milliseconds that transmitted
     /// data may remain unacknowledged before TCP will forcibly close the
     /// corresponding connection
-    TcpUserTimeout, Both, libc::IPPROTO_TCP, libc::TCP_USER_TIMEOUT, u32);
+    TcpUserTimeout,
+    Both,
+    libc::IPPROTO_TCP,
+    libc::TCP_USER_TIMEOUT,
+    u32
+);
 sockopt_impl!(
     /// Sets or gets the maximum socket receive buffer in bytes.
-    RcvBuf, Both, libc::SOL_SOCKET, libc::SO_RCVBUF, usize);
+    RcvBuf,
+    Both,
+    libc::SOL_SOCKET,
+    libc::SO_RCVBUF,
+    usize
+);
 sockopt_impl!(
     /// Sets or gets the maximum socket send buffer in bytes.
-    SndBuf, Both, libc::SOL_SOCKET, libc::SO_SNDBUF, usize);
+    SndBuf,
+    Both,
+    libc::SOL_SOCKET,
+    libc::SO_SNDBUF,
+    usize
+);
 #[cfg(any(target_os = "android", target_os = "linux"))]
 sockopt_impl!(
     /// Using this socket option, a privileged (`CAP_NET_ADMIN`) process can
     /// perform the same task as `SO_RCVBUF`, but the `rmem_max limit` can be
     /// overridden.
-    RcvBufForce, SetOnly, libc::SOL_SOCKET, libc::SO_RCVBUFFORCE, usize);
+    RcvBufForce,
+    SetOnly,
+    libc::SOL_SOCKET,
+    libc::SO_RCVBUFFORCE,
+    usize
+);
 #[cfg(any(target_os = "android", target_os = "linux"))]
 sockopt_impl!(
     /// Using this socket option, a privileged (`CAP_NET_ADMIN`)  process can
     /// perform the same task as `SO_SNDBUF`, but the `wmem_max` limit can be
     /// overridden.
-    SndBufForce, SetOnly, libc::SOL_SOCKET, libc::SO_SNDBUFFORCE, usize);
+    SndBufForce,
+    SetOnly,
+    libc::SOL_SOCKET,
+    libc::SO_SNDBUFFORCE,
+    usize
+);
 sockopt_impl!(
     /// Gets the socket type as an integer.
-    SockType, GetOnly, libc::SOL_SOCKET, libc::SO_TYPE, super::SockType);
+    SockType,
+    GetOnly,
+    libc::SOL_SOCKET,
+    libc::SO_TYPE,
+    super::SockType,
+    GetStruct<i32>
+);
 sockopt_impl!(
     /// Returns a value indicating whether or not this socket has been marked to
     /// accept connections with `listen(2)`.
-    AcceptConn, GetOnly, libc::SOL_SOCKET, libc::SO_ACCEPTCONN, bool);
+    AcceptConn,
+    GetOnly,
+    libc::SOL_SOCKET,
+    libc::SO_ACCEPTCONN,
+    bool
+);
 #[cfg(any(target_os = "android", target_os = "linux"))]
 sockopt_impl!(
     /// Bind this socket to a particular device like “eth0”.
-    BindToDevice, Both, libc::SOL_SOCKET, libc::SO_BINDTODEVICE, OsString<[u8; libc::IFNAMSIZ]>);
+    BindToDevice,
+    Both,
+    libc::SOL_SOCKET,
+    libc::SO_BINDTODEVICE,
+    OsString<[u8; libc::IFNAMSIZ]>
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+    #[allow(missing_docs)]
+    // Not documented by Linux!
+    OriginalDst,
+    GetOnly,
+    libc::SOL_IP,
+    libc::SO_ORIGINAL_DST,
+    libc::sockaddr_in
+);
 #[cfg(any(target_os = "android", target_os = "linux"))]
 sockopt_impl!(
     #[allow(missing_docs)]
     // Not documented by Linux!
-    OriginalDst, GetOnly, libc::SOL_IP, libc::SO_ORIGINAL_DST, libc::sockaddr_in);
-#[cfg(any(target_os = "android", target_os = "linux"))]
+    Ip6tOriginalDst,
+    GetOnly,
+    libc::SOL_IPV6,
+    libc::IP6T_SO_ORIGINAL_DST,
+    libc::sockaddr_in6
+);
+#[cfg(any(target_os = "linux"))]
 sockopt_impl!(
-    #[allow(missing_docs)]
-    // Not documented by Linux!
-    Ip6tOriginalDst, GetOnly, libc::SOL_IPV6, libc::IP6T_SO_ORIGINAL_DST, libc::sockaddr_in6);
-sockopt_impl!( 
+    /// Specifies exact type of timestamping information collected by the kernel
+    /// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
+    Timestamping,
+    Both,
+    libc::SOL_SOCKET,
+    libc::SO_TIMESTAMPING,
+    super::TimestampingFlag
+);
+#[cfg(not(target_os = "haiku"))]
+sockopt_impl!(
     /// Enable or disable the receiving of the `SO_TIMESTAMP` control message.
-    ReceiveTimestamp, Both, libc::SOL_SOCKET, libc::SO_TIMESTAMP, bool);
+    ReceiveTimestamp,
+    Both,
+    libc::SOL_SOCKET,
+    libc::SO_TIMESTAMP,
+    bool
+);
 #[cfg(all(target_os = "linux"))]
 sockopt_impl!(
     /// Enable or disable the receiving of the `SO_TIMESTAMPNS` control message.
-    ReceiveTimestampns, Both, libc::SOL_SOCKET, libc::SO_TIMESTAMPNS, bool);
+    ReceiveTimestampns,
+    Both,
+    libc::SOL_SOCKET,
+    libc::SO_TIMESTAMPNS,
+    bool
+);
 #[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg(feature = "net")]
 sockopt_impl!(
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
     /// Setting this boolean option enables transparent proxying on this socket.
-    IpTransparent, Both, libc::SOL_IP, libc::IP_TRANSPARENT, bool);
+    IpTransparent,
+    Both,
+    libc::SOL_IP,
+    libc::IP_TRANSPARENT,
+    bool
+);
 #[cfg(target_os = "openbsd")]
+#[cfg(feature = "net")]
 sockopt_impl!(
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
     /// Allows the socket to be bound to addresses which are not local to the
     /// machine, so it can be used to make a transparent proxy.
-    BindAny, Both, libc::SOL_SOCKET, libc::SO_BINDANY, bool);
+    BindAny,
+    Both,
+    libc::SOL_SOCKET,
+    libc::SO_BINDANY,
+    bool
+);
 #[cfg(target_os = "freebsd")]
+#[cfg(feature = "net")]
 sockopt_impl!(
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
     /// Can `bind(2)` to any address, even one not bound to any available
     /// network interface in the system.
-    BindAny, Both, libc::IPPROTO_IP, libc::IP_BINDANY, bool);
+    BindAny,
+    Both,
+    libc::IPPROTO_IP,
+    libc::IP_BINDANY,
+    bool
+);
 #[cfg(target_os = "linux")]
 sockopt_impl!(
     /// Set the mark for each packet sent through this socket (similar to the
     /// netfilter MARK target but socket-based).
-    Mark, Both, libc::SOL_SOCKET, libc::SO_MARK, u32);
+    Mark,
+    Both,
+    libc::SOL_SOCKET,
+    libc::SO_MARK,
+    u32
+);
 #[cfg(any(target_os = "android", target_os = "linux"))]
 sockopt_impl!(
     /// Enable or disable the receiving of the `SCM_CREDENTIALS` control
     /// message.
-    PassCred, Both, libc::SOL_SOCKET, libc::SO_PASSCRED, bool);
-#[cfg(any(target_os = "freebsd", target_os = "linux"))] 
+    PassCred,
+    Both,
+    libc::SOL_SOCKET,
+    libc::SO_PASSCRED,
+    bool
+);
+#[cfg(any(target_os = "freebsd", target_os = "linux"))]
+#[cfg(feature = "net")]
 sockopt_impl!(
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
     /// This option allows the caller to set the TCP congestion control
     /// algorithm to be used,  on a per-socket basis.
-    TcpCongestion, Both, libc::IPPROTO_TCP, libc::TCP_CONGESTION, OsString<[u8; TCP_CA_NAME_MAX]>);
+    TcpCongestion,
+    Both,
+    libc::IPPROTO_TCP,
+    libc::TCP_CONGESTION,
+    OsString<[u8; TCP_CA_NAME_MAX]>
+);
 #[cfg(any(
     target_os = "android",
     target_os = "ios",
@@ -475,10 +780,17 @@
     target_os = "macos",
     target_os = "netbsd",
 ))]
+#[cfg(feature = "net")]
 sockopt_impl!(
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
     /// Pass an `IP_PKTINFO` ancillary message that contains a pktinfo
     /// structure that supplies some information about the incoming packet.
-    Ipv4PacketInfo, Both, libc::IPPROTO_IP, libc::IP_PKTINFO, bool);
+    Ipv4PacketInfo,
+    Both,
+    libc::IPPROTO_IP,
+    libc::IP_PKTINFO,
+    bool
+);
 #[cfg(any(
     target_os = "android",
     target_os = "freebsd",
@@ -488,10 +800,17 @@
     target_os = "netbsd",
     target_os = "openbsd",
 ))]
+#[cfg(feature = "net")]
 sockopt_impl!(
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
     /// Set delivery of the `IPV6_PKTINFO` control message on incoming
     /// datagrams.
-    Ipv6RecvPacketInfo, Both, libc::IPPROTO_IPV6, libc::IPV6_RECVPKTINFO, bool);
+    Ipv6RecvPacketInfo,
+    Both,
+    libc::IPPROTO_IPV6,
+    libc::IPV6_RECVPKTINFO,
+    bool
+);
 #[cfg(any(
     target_os = "freebsd",
     target_os = "ios",
@@ -499,10 +818,17 @@
     target_os = "netbsd",
     target_os = "openbsd",
 ))]
+#[cfg(feature = "net")]
 sockopt_impl!(
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
     /// The `recvmsg(2)` call returns a `struct sockaddr_dl` corresponding to
     /// the interface on which the packet was received.
-    Ipv4RecvIf, Both, libc::IPPROTO_IP, libc::IP_RECVIF, bool);
+    Ipv4RecvIf,
+    Both,
+    libc::IPPROTO_IP,
+    libc::IP_RECVIF,
+    bool
+);
 #[cfg(any(
     target_os = "freebsd",
     target_os = "ios",
@@ -510,46 +836,165 @@
     target_os = "netbsd",
     target_os = "openbsd",
 ))]
+#[cfg(feature = "net")]
 sockopt_impl!(
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
     /// The `recvmsg(2)` call will return the destination IP address for a UDP
     /// datagram.
-    Ipv4RecvDstAddr, Both, libc::IPPROTO_IP, libc::IP_RECVDSTADDR, bool);
-#[cfg(target_os = "linux")]
+    Ipv4RecvDstAddr,
+    Both,
+    libc::IPPROTO_IP,
+    libc::IP_RECVDSTADDR,
+    bool
+);
+#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+#[cfg(feature = "net")]
 sockopt_impl!(
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+    /// The `recvmsg(2)` call will return the destination IP address for a UDP
+    /// datagram.
+    Ipv4OrigDstAddr,
+    Both,
+    libc::IPPROTO_IP,
+    libc::IP_ORIGDSTADDR,
+    bool
+);
+#[cfg(target_os = "linux")]
+#[cfg(feature = "net")]
+sockopt_impl!(
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
     #[allow(missing_docs)]
     // Not documented by Linux!
-    UdpGsoSegment, Both, libc::SOL_UDP, libc::UDP_SEGMENT, libc::c_int);
+    UdpGsoSegment,
+    Both,
+    libc::SOL_UDP,
+    libc::UDP_SEGMENT,
+    libc::c_int
+);
 #[cfg(target_os = "linux")]
+#[cfg(feature = "net")]
 sockopt_impl!(
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
     #[allow(missing_docs)]
     // Not documented by Linux!
-    UdpGroSegment, Both, libc::IPPROTO_UDP, libc::UDP_GRO, bool);
+    UdpGroSegment,
+    Both,
+    libc::IPPROTO_UDP,
+    libc::UDP_GRO,
+    bool
+);
+#[cfg(target_os = "linux")]
+sockopt_impl!(
+    /// Configures the behavior of time-based transmission of packets, for use
+    /// with the `TxTime` control message.
+    TxTime,
+    Both,
+    libc::SOL_SOCKET,
+    libc::SO_TXTIME,
+    libc::sock_txtime
+);
 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
 sockopt_impl!(
     /// Indicates that an unsigned 32-bit value ancillary message (cmsg) should
     /// be attached to received skbs indicating the number of packets dropped by
     /// the socket since its creation.
-    RxqOvfl, Both, libc::SOL_SOCKET, libc::SO_RXQ_OVFL, libc::c_int);
+    RxqOvfl,
+    Both,
+    libc::SOL_SOCKET,
+    libc::SO_RXQ_OVFL,
+    libc::c_int
+);
+#[cfg(feature = "net")]
 sockopt_impl!(
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
     /// The socket is restricted to sending and receiving IPv6 packets only.
-    Ipv6V6Only, Both, libc::IPPROTO_IPV6, libc::IPV6_V6ONLY, bool);
+    Ipv6V6Only,
+    Both,
+    libc::IPPROTO_IPV6,
+    libc::IPV6_V6ONLY,
+    bool
+);
 #[cfg(any(target_os = "android", target_os = "linux"))]
 sockopt_impl!(
     /// Enable extended reliable error message passing.
-    Ipv4RecvErr, Both, libc::IPPROTO_IP, libc::IP_RECVERR, bool);
+    Ipv4RecvErr,
+    Both,
+    libc::IPPROTO_IP,
+    libc::IP_RECVERR,
+    bool
+);
 #[cfg(any(target_os = "android", target_os = "linux"))]
 sockopt_impl!(
     /// Control receiving of asynchronous error options.
-    Ipv6RecvErr, Both, libc::IPPROTO_IPV6, libc::IPV6_RECVERR, bool);
+    Ipv6RecvErr,
+    Both,
+    libc::IPPROTO_IPV6,
+    libc::IPV6_RECVERR,
+    bool
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+sockopt_impl!(
+    /// Fetch the current system-estimated Path MTU.
+    IpMtu,
+    GetOnly,
+    libc::IPPROTO_IP,
+    libc::IP_MTU,
+    libc::c_int
+);
 #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
 sockopt_impl!(
     /// Set or retrieve the current time-to-live field that is used in every
     /// packet sent from this socket.
-    Ipv4Ttl, Both, libc::IPPROTO_IP, libc::IP_TTL, libc::c_int);
+    Ipv4Ttl,
+    Both,
+    libc::IPPROTO_IP,
+    libc::IP_TTL,
+    libc::c_int
+);
 #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
 sockopt_impl!(
     /// Set the unicast hop limit for the socket.
-    Ipv6Ttl, Both, libc::IPPROTO_IPV6, libc::IPV6_UNICAST_HOPS, libc::c_int);
+    Ipv6Ttl,
+    Both,
+    libc::IPPROTO_IPV6,
+    libc::IPV6_UNICAST_HOPS,
+    libc::c_int
+);
+#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+    /// The `recvmsg(2)` call will return the destination IP address for a UDP
+    /// datagram.
+    Ipv6OrigDstAddr,
+    Both,
+    libc::IPPROTO_IPV6,
+    libc::IPV6_ORIGDSTADDR,
+    bool
+);
+#[cfg(any(target_os = "ios", target_os = "macos"))]
+sockopt_impl!(
+    /// Set "don't fragment packet" flag on the IP packet.
+    IpDontFrag,
+    Both,
+    libc::IPPROTO_IP,
+    libc::IP_DONTFRAG,
+    bool
+);
+#[cfg(any(
+    target_os = "android",
+    target_os = "ios",
+    target_os = "linux",
+    target_os = "macos",
+))]
+sockopt_impl!(
+    /// Set "don't fragment packet" flag on the IPv6 packet.
+    Ipv6DontFrag,
+    Both,
+    libc::IPPROTO_IPV6,
+    libc::IPV6_DONTFRAG,
+    bool
+);
 
 #[allow(missing_docs)]
 // Not documented by Linux!
@@ -565,11 +1010,13 @@
 
     fn set(&self, fd: RawFd, val: &usize) -> Result<()> {
         unsafe {
-            let res = libc::setsockopt(fd,
-                                       libc::SOL_ALG,
-                                       libc::ALG_SET_AEAD_AUTHSIZE,
-                                       ::std::ptr::null(),
-                                       *val as libc::socklen_t);
+            let res = libc::setsockopt(
+                fd,
+                libc::SOL_ALG,
+                libc::ALG_SET_AEAD_AUTHSIZE,
+                ::std::ptr::null(),
+                *val as libc::socklen_t,
+            );
             Errno::result(res).map(drop)
         }
     }
@@ -589,16 +1036,21 @@
 }
 
 #[cfg(any(target_os = "android", target_os = "linux"))]
-impl<T> SetSockOpt for AlgSetKey<T> where T: AsRef<[u8]> + Clone {
+impl<T> SetSockOpt for AlgSetKey<T>
+where
+    T: AsRef<[u8]> + Clone,
+{
     type Val = T;
 
     fn set(&self, fd: RawFd, val: &T) -> Result<()> {
         unsafe {
-            let res = libc::setsockopt(fd,
-                                       libc::SOL_ALG,
-                                       libc::ALG_SET_KEY,
-                                       val.as_ref().as_ptr() as *const _,
-                                       val.as_ref().len() as libc::socklen_t);
+            let res = libc::setsockopt(
+                fd,
+                libc::SOL_ALG,
+                libc::ALG_SET_KEY,
+                val.as_ref().as_ptr() as *const _,
+                val.as_ref().len() as libc::socklen_t,
+            );
             Errno::result(res).map(drop)
         }
     }
@@ -659,7 +1111,11 @@
     }
 
     unsafe fn assume_init(self) -> T {
-        assert_eq!(self.len as usize, mem::size_of::<T>(), "invalid getsockopt implementation");
+        assert_eq!(
+            self.len as usize,
+            mem::size_of::<T>(),
+            "invalid getsockopt implementation"
+        );
         self.val.assume_init()
     }
 }
@@ -706,7 +1162,11 @@
     }
 
     unsafe fn assume_init(self) -> bool {
-        assert_eq!(self.len as usize, mem::size_of::<c_int>(), "invalid getsockopt implementation");
+        assert_eq!(
+            self.len as usize,
+            mem::size_of::<c_int>(),
+            "invalid getsockopt implementation"
+        );
         self.val.assume_init() != 0
     }
 }
@@ -718,7 +1178,9 @@
 
 impl<'a> Set<'a, bool> for SetBool {
     fn new(val: &'a bool) -> SetBool {
-        SetBool { val: if *val { 1 } else { 0 } }
+        SetBool {
+            val: i32::from(*val),
+        }
     }
 
     fn ffi_ptr(&self) -> *const c_void {
@@ -753,7 +1215,11 @@
     }
 
     unsafe fn assume_init(self) -> u8 {
-        assert_eq!(self.len as usize, mem::size_of::<u8>(), "invalid getsockopt implementation");
+        assert_eq!(
+            self.len as usize,
+            mem::size_of::<u8>(),
+            "invalid getsockopt implementation"
+        );
         self.val.assume_init()
     }
 }
@@ -765,7 +1231,7 @@
 
 impl<'a> Set<'a, u8> for SetU8 {
     fn new(val: &'a u8) -> SetU8 {
-        SetU8 { val: *val as u8 }
+        SetU8 { val: *val }
     }
 
     fn ffi_ptr(&self) -> *const c_void {
@@ -800,7 +1266,11 @@
     }
 
     unsafe fn assume_init(self) -> usize {
-        assert_eq!(self.len as usize, mem::size_of::<c_int>(), "invalid getsockopt implementation");
+        assert_eq!(
+            self.len as usize,
+            mem::size_of::<c_int>(),
+            "invalid getsockopt implementation"
+        );
         self.val.assume_init() as usize
     }
 }
@@ -860,7 +1330,9 @@
 
 impl<'a> Set<'a, OsString> for SetOsString<'a> {
     fn new(val: &'a OsString) -> SetOsString {
-        SetOsString { val: val.as_os_str() }
+        SetOsString {
+            val: val.as_os_str(),
+        }
     }
 
     fn ffi_ptr(&self) -> *const c_void {
@@ -872,7 +1344,6 @@
     }
 }
 
-
 #[cfg(test)]
 mod test {
     #[cfg(any(target_os = "android", target_os = "linux"))]
@@ -880,11 +1351,17 @@
     fn can_get_peercred_on_unix_socket() {
         use super::super::*;
 
-        let (a, b) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty()).unwrap();
+        let (a, b) = socketpair(
+            AddressFamily::Unix,
+            SockType::Stream,
+            None,
+            SockFlag::empty(),
+        )
+        .unwrap();
         let a_cred = getsockopt(a, super::PeerCredentials).unwrap();
         let b_cred = getsockopt(b, super::PeerCredentials).unwrap();
         assert_eq!(a_cred, b_cred);
-        assert!(a_cred.pid() != 0);
+        assert_ne!(a_cred.pid(), 0);
     }
 
     #[test]
@@ -892,7 +1369,13 @@
         use super::super::*;
         use crate::unistd::close;
 
-        let (a, b) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty()).unwrap();
+        let (a, b) = socketpair(
+            AddressFamily::Unix,
+            SockType::Stream,
+            None,
+            SockFlag::empty(),
+        )
+        .unwrap();
         let a_type = getsockopt(a, super::SockType).unwrap();
         assert_eq!(a_type, SockType::Stream);
         close(a).unwrap();
@@ -904,21 +1387,31 @@
         use super::super::*;
         use crate::unistd::close;
 
-        let s = socket(AddressFamily::Inet, SockType::Datagram, SockFlag::empty(), None).unwrap();
+        let s = socket(
+            AddressFamily::Inet,
+            SockType::Datagram,
+            SockFlag::empty(),
+            None,
+        )
+        .unwrap();
         let s_type = getsockopt(s, super::SockType).unwrap();
         assert_eq!(s_type, SockType::Datagram);
         close(s).unwrap();
     }
 
-    #[cfg(any(target_os = "freebsd",
-              target_os = "linux",
-              target_os = "nacl"))]
+    #[cfg(any(target_os = "freebsd", target_os = "linux"))]
     #[test]
     fn can_get_listen_on_tcp_socket() {
         use super::super::*;
         use crate::unistd::close;
 
-        let s = socket(AddressFamily::Inet, SockType::Stream, SockFlag::empty(), None).unwrap();
+        let s = socket(
+            AddressFamily::Inet,
+            SockType::Stream,
+            SockFlag::empty(),
+            None,
+        )
+        .unwrap();
         let s_listening = getsockopt(s, super::AcceptConn).unwrap();
         assert!(!s_listening);
         listen(s, 10).unwrap();
@@ -926,5 +1419,4 @@
         assert!(s_listening2);
         close(s).unwrap();
     }
-
 }
diff --git a/src/sys/stat.rs b/src/sys/stat.rs
index c8f1041..78203bf 100644
--- a/src/sys/stat.rs
+++ b/src/sys/stat.rs
@@ -1,12 +1,20 @@
-pub use libc::{dev_t, mode_t};
+#[cfg(any(target_os = "macos", target_os = "ios", target_os = "openbsd"))]
+pub use libc::c_uint;
+#[cfg(any(
+    target_os = "netbsd",
+    target_os = "freebsd",
+    target_os = "dragonfly"
+))]
+pub use libc::c_ulong;
 pub use libc::stat as FileStat;
+pub use libc::{dev_t, mode_t};
 
-use crate::{Result, NixPath, errno::Errno};
 #[cfg(not(target_os = "redox"))]
-use crate::fcntl::{AtFlags, at_rawfd};
+use crate::fcntl::{at_rawfd, AtFlags};
+use crate::sys::time::{TimeSpec, TimeVal};
+use crate::{errno::Errno, NixPath, Result};
 use std::mem;
 use std::os::unix::io::RawFd;
-use crate::sys::time::{TimeSpec, TimeVal};
 
 libc_bitflags!(
     /// "File type" flags for `mknod` and related functions.
@@ -25,26 +33,149 @@
 libc_bitflags! {
     /// "File mode / permissions" flags.
     pub struct Mode: mode_t {
+        /// Read, write and execute for owner.
         S_IRWXU;
+        /// Read for owner.
         S_IRUSR;
+        /// Write for owner.
         S_IWUSR;
+        /// Execute for owner.
         S_IXUSR;
+        /// Read write and execute for group.
         S_IRWXG;
+        /// Read fr group.
         S_IRGRP;
+        /// Write for group.
         S_IWGRP;
+        /// Execute for group.
         S_IXGRP;
+        /// Read, write and execute for other.
         S_IRWXO;
+        /// Read for other.
         S_IROTH;
+        /// Write for other.
         S_IWOTH;
+        /// Execute for other.
         S_IXOTH;
+        /// Set user id on execution.
         S_ISUID as mode_t;
+        /// Set group id on execution.
         S_ISGID as mode_t;
         S_ISVTX as mode_t;
     }
 }
 
+#[cfg(any(target_os = "macos", target_os = "ios", target_os = "openbsd"))]
+pub type type_of_file_flag = c_uint;
+#[cfg(any(
+    target_os = "netbsd",
+    target_os = "freebsd",
+    target_os = "dragonfly"
+))]
+pub type type_of_file_flag = c_ulong;
+
+#[cfg(any(
+    target_os = "openbsd",
+    target_os = "netbsd",
+    target_os = "freebsd",
+    target_os = "dragonfly",
+    target_os = "macos",
+    target_os = "ios"
+))]
+libc_bitflags! {
+    /// File flags.
+    #[cfg_attr(docsrs, doc(cfg(all())))]
+    pub struct FileFlag: type_of_file_flag {
+        /// The file may only be appended to.
+        SF_APPEND;
+        /// The file has been archived.
+        SF_ARCHIVED;
+        #[cfg(any(target_os = "dragonfly"))]
+        SF_CACHE;
+        /// The file may not be changed.
+        SF_IMMUTABLE;
+        /// Indicates a WAPBL journal file.
+        #[cfg(any(target_os = "netbsd"))]
+        SF_LOG;
+        /// Do not retain history for file
+        #[cfg(any(target_os = "dragonfly"))]
+        SF_NOHISTORY;
+        /// The file may not be renamed or deleted.
+        #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+        SF_NOUNLINK;
+        /// Mask of superuser changeable flags
+        SF_SETTABLE;
+        /// Snapshot is invalid.
+        #[cfg(any(target_os = "netbsd"))]
+        SF_SNAPINVAL;
+        /// The file is a snapshot file.
+        #[cfg(any(target_os = "netbsd", target_os = "freebsd"))]
+        SF_SNAPSHOT;
+        #[cfg(any(target_os = "dragonfly"))]
+        SF_XLINK;
+        /// The file may only be appended to.
+        UF_APPEND;
+        /// The file needs to be archived.
+        #[cfg(any(target_os = "freebsd"))]
+        UF_ARCHIVE;
+        #[cfg(any(target_os = "dragonfly"))]
+        UF_CACHE;
+        /// File is compressed at the file system level.
+        #[cfg(any(target_os = "macos", target_os = "ios"))]
+        UF_COMPRESSED;
+        /// The file may be hidden from directory listings at the application's
+        /// discretion.
+        #[cfg(any(
+            target_os = "freebsd",
+            target_os = "macos",
+            target_os = "ios",
+        ))]
+        UF_HIDDEN;
+        /// The file may not be changed.
+        UF_IMMUTABLE;
+        /// Do not dump the file.
+        UF_NODUMP;
+        #[cfg(any(target_os = "dragonfly"))]
+        UF_NOHISTORY;
+        /// The file may not be renamed or deleted.
+        #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+        UF_NOUNLINK;
+        /// The file is offline, or has the Windows and CIFS
+        /// `FILE_ATTRIBUTE_OFFLINE` attribute.
+        #[cfg(any(target_os = "freebsd"))]
+        UF_OFFLINE;
+        /// The directory is opaque when viewed through a union stack.
+        UF_OPAQUE;
+        /// The file is read only, and may not be written or appended.
+        #[cfg(any(target_os = "freebsd"))]
+        UF_READONLY;
+        /// The file contains a Windows reparse point.
+        #[cfg(any(target_os = "freebsd"))]
+        UF_REPARSE;
+        /// Mask of owner changeable flags.
+        UF_SETTABLE;
+        /// The file has the Windows `FILE_ATTRIBUTE_SPARSE_FILE` attribute.
+        #[cfg(any(target_os = "freebsd"))]
+        UF_SPARSE;
+        /// The file has the DOS, Windows and CIFS `FILE_ATTRIBUTE_SYSTEM`
+        /// attribute.
+        #[cfg(any(target_os = "freebsd"))]
+        UF_SYSTEM;
+        /// File renames and deletes are tracked.
+        #[cfg(any(target_os = "macos", target_os = "ios"))]
+        UF_TRACKED;
+        #[cfg(any(target_os = "dragonfly"))]
+        UF_XLINK;
+    }
+}
+
 /// Create a special or ordinary file, by pathname.
-pub fn mknod<P: ?Sized + NixPath>(path: &P, kind: SFlag, perm: Mode, dev: dev_t) -> Result<()> {
+pub fn mknod<P: ?Sized + NixPath>(
+    path: &P,
+    kind: SFlag,
+    perm: Mode,
+    dev: dev_t,
+) -> Result<()> {
     let res = path.with_nix_path(|cstr| unsafe {
         libc::mknod(cstr.as_ptr(), kind.bits | perm.bits() as mode_t, dev)
     })?;
@@ -53,7 +184,13 @@
 }
 
 /// Create a special or ordinary file, relative to a given directory.
-#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox")))]
+#[cfg(not(any(
+    target_os = "ios",
+    target_os = "macos",
+    target_os = "redox",
+    target_os = "haiku"
+)))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
 pub fn mknodat<P: ?Sized + NixPath>(
     dirfd: RawFd,
     path: &P,
@@ -62,30 +199,36 @@
     dev: dev_t,
 ) -> Result<()> {
     let res = path.with_nix_path(|cstr| unsafe {
-        libc::mknodat(dirfd, cstr.as_ptr(), kind.bits | perm.bits() as mode_t, dev)
+        libc::mknodat(
+            dirfd,
+            cstr.as_ptr(),
+            kind.bits | perm.bits() as mode_t,
+            dev,
+        )
     })?;
 
     Errno::result(res).map(drop)
 }
 
 #[cfg(target_os = "linux")]
+#[cfg_attr(docsrs, doc(cfg(all())))]
 pub const fn major(dev: dev_t) -> u64 {
-    ((dev >> 32) & 0xffff_f000) |
-    ((dev >>  8) & 0x0000_0fff)
+    ((dev >> 32) & 0xffff_f000) | ((dev >> 8) & 0x0000_0fff)
 }
 
 #[cfg(target_os = "linux")]
+#[cfg_attr(docsrs, doc(cfg(all())))]
 pub const fn minor(dev: dev_t) -> u64 {
-    ((dev >> 12) & 0xffff_ff00) |
-    ((dev      ) & 0x0000_00ff)
+    ((dev >> 12) & 0xffff_ff00) | ((dev) & 0x0000_00ff)
 }
 
 #[cfg(target_os = "linux")]
+#[cfg_attr(docsrs, doc(cfg(all())))]
 pub const fn makedev(major: u64, minor: u64) -> dev_t {
-    ((major & 0xffff_f000) << 32) |
-    ((major & 0x0000_0fff) <<  8) |
-    ((minor & 0xffff_ff00) << 12) |
-     (minor & 0x0000_00ff)
+    ((major & 0xffff_f000) << 32)
+        | ((major & 0x0000_0fff) << 8)
+        | ((minor & 0xffff_ff00) << 12)
+        | (minor & 0x0000_00ff)
 }
 
 pub fn umask(mode: Mode) -> Mode {
@@ -95,28 +238,24 @@
 
 pub fn stat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat> {
     let mut dst = mem::MaybeUninit::uninit();
-    let res = path.with_nix_path(|cstr| {
-        unsafe {
-            libc::stat(cstr.as_ptr(), dst.as_mut_ptr())
-        }
+    let res = path.with_nix_path(|cstr| unsafe {
+        libc::stat(cstr.as_ptr(), dst.as_mut_ptr())
     })?;
 
     Errno::result(res)?;
 
-    Ok(unsafe{dst.assume_init()})
+    Ok(unsafe { dst.assume_init() })
 }
 
 pub fn lstat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat> {
     let mut dst = mem::MaybeUninit::uninit();
-    let res = path.with_nix_path(|cstr| {
-        unsafe {
-            libc::lstat(cstr.as_ptr(), dst.as_mut_ptr())
-        }
+    let res = path.with_nix_path(|cstr| unsafe {
+        libc::lstat(cstr.as_ptr(), dst.as_mut_ptr())
     })?;
 
     Errno::result(res)?;
 
-    Ok(unsafe{dst.assume_init()})
+    Ok(unsafe { dst.assume_init() })
 }
 
 pub fn fstat(fd: RawFd) -> Result<FileStat> {
@@ -125,19 +264,29 @@
 
     Errno::result(res)?;
 
-    Ok(unsafe{dst.assume_init()})
+    Ok(unsafe { dst.assume_init() })
 }
 
 #[cfg(not(target_os = "redox"))]
-pub fn fstatat<P: ?Sized + NixPath>(dirfd: RawFd, pathname: &P, f: AtFlags) -> Result<FileStat> {
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub fn fstatat<P: ?Sized + NixPath>(
+    dirfd: RawFd,
+    pathname: &P,
+    f: AtFlags,
+) -> Result<FileStat> {
     let mut dst = mem::MaybeUninit::uninit();
-    let res = pathname.with_nix_path(|cstr| {
-        unsafe { libc::fstatat(dirfd, cstr.as_ptr(), dst.as_mut_ptr(), f.bits() as libc::c_int) }
+    let res = pathname.with_nix_path(|cstr| unsafe {
+        libc::fstatat(
+            dirfd,
+            cstr.as_ptr(),
+            dst.as_mut_ptr(),
+            f.bits() as libc::c_int,
+        )
     })?;
 
     Errno::result(res)?;
 
-    Ok(unsafe{dst.assume_init()})
+    Ok(unsafe { dst.assume_init() })
 }
 
 /// Change the file permission bits of the file specified by a file descriptor.
@@ -175,17 +324,17 @@
 ///
 /// [fchmodat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmodat.html).
 #[cfg(not(target_os = "redox"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
 pub fn fchmodat<P: ?Sized + NixPath>(
     dirfd: Option<RawFd>,
     path: &P,
     mode: Mode,
     flag: FchmodatFlags,
 ) -> Result<()> {
-    let atflag =
-        match flag {
-            FchmodatFlags::FollowSymlink => AtFlags::empty(),
-            FchmodatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW,
-        };
+    let atflag = match flag {
+        FchmodatFlags::FollowSymlink => AtFlags::empty(),
+        FchmodatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW,
+    };
     let res = path.with_nix_path(|cstr| unsafe {
         libc::fchmodat(
             at_rawfd(dirfd),
@@ -208,7 +357,11 @@
 /// # References
 ///
 /// [utimes(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/utimes.html).
-pub fn utimes<P: ?Sized + NixPath>(path: &P, atime: &TimeVal, mtime: &TimeVal) -> Result<()> {
+pub fn utimes<P: ?Sized + NixPath>(
+    path: &P,
+    atime: &TimeVal,
+    mtime: &TimeVal,
+) -> Result<()> {
     let times: [libc::timeval; 2] = [*atime.as_ref(), *mtime.as_ref()];
     let res = path.with_nix_path(|cstr| unsafe {
         libc::utimes(cstr.as_ptr(), &times[0])
@@ -227,13 +380,20 @@
 /// # References
 ///
 /// [lutimes(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/lutimes.html).
-#[cfg(any(target_os = "linux",
-          target_os = "haiku",
-          target_os = "ios",
-          target_os = "macos",
-          target_os = "freebsd",
-          target_os = "netbsd"))]
-pub fn lutimes<P: ?Sized + NixPath>(path: &P, atime: &TimeVal, mtime: &TimeVal) -> Result<()> {
+#[cfg(any(
+    target_os = "linux",
+    target_os = "haiku",
+    target_os = "ios",
+    target_os = "macos",
+    target_os = "freebsd",
+    target_os = "netbsd"
+))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub fn lutimes<P: ?Sized + NixPath>(
+    path: &P,
+    atime: &TimeVal,
+    mtime: &TimeVal,
+) -> Result<()> {
     let times: [libc::timeval; 2] = [*atime.as_ref(), *mtime.as_ref()];
     let res = path.with_nix_path(|cstr| unsafe {
         libc::lutimes(cstr.as_ptr(), &times[0])
@@ -280,18 +440,18 @@
 ///
 /// [utimensat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/utimens.html).
 #[cfg(not(target_os = "redox"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
 pub fn utimensat<P: ?Sized + NixPath>(
     dirfd: Option<RawFd>,
     path: &P,
     atime: &TimeSpec,
     mtime: &TimeSpec,
-    flag: UtimensatFlags
+    flag: UtimensatFlags,
 ) -> Result<()> {
-    let atflag =
-        match flag {
-            UtimensatFlags::FollowSymlink => AtFlags::empty(),
-            UtimensatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW,
-        };
+    let atflag = match flag {
+        UtimensatFlags::FollowSymlink => AtFlags::empty(),
+        UtimensatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW,
+    };
     let times: [libc::timespec; 2] = [*atime.as_ref(), *mtime.as_ref()];
     let res = path.with_nix_path(|cstr| unsafe {
         libc::utimensat(
@@ -306,9 +466,14 @@
 }
 
 #[cfg(not(target_os = "redox"))]
-pub fn mkdirat<P: ?Sized + NixPath>(fd: RawFd, path: &P, mode: Mode) -> Result<()> {
-    let res = path.with_nix_path(|cstr| {
-        unsafe { libc::mkdirat(fd, cstr.as_ptr(), mode.bits() as mode_t) }
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub fn mkdirat<P: ?Sized + NixPath>(
+    fd: RawFd,
+    path: &P,
+    mode: Mode,
+) -> Result<()> {
+    let res = path.with_nix_path(|cstr| unsafe {
+        libc::mkdirat(fd, cstr.as_ptr(), mode.bits() as mode_t)
     })?;
 
     Errno::result(res).map(drop)
diff --git a/src/sys/statfs.rs b/src/sys/statfs.rs
index 829be57..9be8ca6 100644
--- a/src/sys/statfs.rs
+++ b/src/sys/statfs.rs
@@ -1,25 +1,62 @@
 //! Get filesystem statistics, non-portably
 //!
 //! See [`statvfs`](crate::sys::statvfs) for a portable alternative.
+#[cfg(not(any(target_os = "linux", target_os = "android")))]
+use std::ffi::CStr;
 use std::fmt::{self, Debug};
 use std::mem;
 use std::os::unix::io::AsRawFd;
-#[cfg(not(any(target_os = "linux", target_os = "android")))]
-use std::ffi::CStr;
 
-use crate::{NixPath, Result, errno::Errno};
+use cfg_if::cfg_if;
+
+#[cfg(all(
+    feature = "mount",
+    any(
+        target_os = "dragonfly",
+        target_os = "freebsd",
+        target_os = "macos",
+        target_os = "netbsd",
+        target_os = "openbsd"
+    )
+))]
+use crate::mount::MntFlags;
+#[cfg(target_os = "linux")]
+use crate::sys::statvfs::FsFlags;
+use crate::{errno::Errno, NixPath, Result};
 
 /// Identifies a mounted file system
 #[cfg(target_os = "android")]
+#[cfg_attr(docsrs, doc(cfg(all())))]
 pub type fsid_t = libc::__fsid_t;
 /// Identifies a mounted file system
 #[cfg(not(target_os = "android"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
 pub type fsid_t = libc::fsid_t;
 
+cfg_if! {
+    if #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] {
+        type type_of_statfs = libc::statfs64;
+        const LIBC_FSTATFS: unsafe extern fn
+            (fd: libc::c_int, buf: *mut type_of_statfs) -> libc::c_int
+            = libc::fstatfs64;
+        const LIBC_STATFS: unsafe extern fn
+            (path: *const libc::c_char, buf: *mut type_of_statfs) -> libc::c_int
+            = libc::statfs64;
+    } else {
+        type type_of_statfs = libc::statfs;
+        const LIBC_FSTATFS: unsafe extern fn
+            (fd: libc::c_int, buf: *mut type_of_statfs) -> libc::c_int
+            = libc::fstatfs;
+        const LIBC_STATFS: unsafe extern fn
+            (path: *const libc::c_char, buf: *mut type_of_statfs) -> libc::c_int
+            = libc::statfs;
+    }
+}
+
 /// Describes a mounted file system
 #[derive(Clone, Copy)]
 #[repr(transparent)]
-pub struct Statfs(libc::statfs);
+pub struct Statfs(type_of_statfs);
 
 #[cfg(target_os = "freebsd")]
 type fs_type_t = u32;
@@ -29,7 +66,16 @@
 type fs_type_t = libc::c_uint;
 #[cfg(all(target_os = "linux", target_env = "musl"))]
 type fs_type_t = libc::c_ulong;
-#[cfg(all(target_os = "linux", not(any(target_arch = "s390x", target_env = "musl"))))]
+#[cfg(all(target_os = "linux", target_env = "uclibc"))]
+type fs_type_t = libc::c_int;
+#[cfg(all(
+    target_os = "linux",
+    not(any(
+        target_arch = "s390x",
+        target_env = "musl",
+        target_env = "uclibc"
+    ))
+))]
 type fs_type_t = libc::__fsword_t;
 
 /// Describes the file system type as known by the operating system.
@@ -38,101 +84,218 @@
     target_os = "android",
     all(target_os = "linux", target_arch = "s390x"),
     all(target_os = "linux", target_env = "musl"),
-    all(target_os = "linux", not(any(target_arch = "s390x", target_env = "musl"))),
+    all(
+        target_os = "linux",
+        not(any(target_arch = "s390x", target_env = "musl"))
+    ),
 ))]
 #[derive(Eq, Copy, Clone, PartialEq, Debug)]
 pub struct FsType(pub fs_type_t);
 
 // These constants are defined without documentation in the Linux headers, so we
 // can't very well document them here.
-#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+#[cfg(any(target_os = "linux", target_os = "android"))]
 #[allow(missing_docs)]
-pub const ADFS_SUPER_MAGIC: FsType = FsType(libc::ADFS_SUPER_MAGIC as fs_type_t);
-#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+pub const ADFS_SUPER_MAGIC: FsType =
+    FsType(libc::ADFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
 #[allow(missing_docs)]
-pub const AFFS_SUPER_MAGIC: FsType = FsType(libc::AFFS_SUPER_MAGIC as fs_type_t);
-#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+pub const AFFS_SUPER_MAGIC: FsType =
+    FsType(libc::AFFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
 #[allow(missing_docs)]
-pub const CODA_SUPER_MAGIC: FsType = FsType(libc::CODA_SUPER_MAGIC as fs_type_t);
-#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+pub const AFS_SUPER_MAGIC: FsType = FsType(libc::AFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const AUTOFS_SUPER_MAGIC: FsType =
+    FsType(libc::AUTOFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const BPF_FS_MAGIC: FsType = FsType(libc::BPF_FS_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const BTRFS_SUPER_MAGIC: FsType =
+    FsType(libc::BTRFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const CGROUP2_SUPER_MAGIC: FsType =
+    FsType(libc::CGROUP2_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const CGROUP_SUPER_MAGIC: FsType =
+    FsType(libc::CGROUP_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const CODA_SUPER_MAGIC: FsType =
+    FsType(libc::CODA_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
 #[allow(missing_docs)]
 pub const CRAMFS_MAGIC: FsType = FsType(libc::CRAMFS_MAGIC as fs_type_t);
-#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const DEBUGFS_MAGIC: FsType = FsType(libc::DEBUGFS_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const DEVPTS_SUPER_MAGIC: FsType =
+    FsType(libc::DEVPTS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const ECRYPTFS_SUPER_MAGIC: FsType =
+    FsType(libc::ECRYPTFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
 #[allow(missing_docs)]
 pub const EFS_SUPER_MAGIC: FsType = FsType(libc::EFS_SUPER_MAGIC as fs_type_t);
-#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+#[cfg(any(target_os = "linux", target_os = "android"))]
 #[allow(missing_docs)]
-pub const EXT2_SUPER_MAGIC: FsType = FsType(libc::EXT2_SUPER_MAGIC as fs_type_t);
-#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+pub const EXT2_SUPER_MAGIC: FsType =
+    FsType(libc::EXT2_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
 #[allow(missing_docs)]
-pub const EXT3_SUPER_MAGIC: FsType = FsType(libc::EXT3_SUPER_MAGIC as fs_type_t);
-#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+pub const EXT3_SUPER_MAGIC: FsType =
+    FsType(libc::EXT3_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
 #[allow(missing_docs)]
-pub const EXT4_SUPER_MAGIC: FsType = FsType(libc::EXT4_SUPER_MAGIC as fs_type_t);
-#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+pub const EXT4_SUPER_MAGIC: FsType =
+    FsType(libc::EXT4_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
 #[allow(missing_docs)]
-pub const HPFS_SUPER_MAGIC: FsType = FsType(libc::HPFS_SUPER_MAGIC as fs_type_t);
-#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+pub const F2FS_SUPER_MAGIC: FsType =
+    FsType(libc::F2FS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const FUSE_SUPER_MAGIC: FsType =
+    FsType(libc::FUSE_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const FUTEXFS_SUPER_MAGIC: FsType =
+    FsType(libc::FUTEXFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const HOSTFS_SUPER_MAGIC: FsType =
+    FsType(libc::HOSTFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const HPFS_SUPER_MAGIC: FsType =
+    FsType(libc::HPFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
 #[allow(missing_docs)]
 pub const HUGETLBFS_MAGIC: FsType = FsType(libc::HUGETLBFS_MAGIC as fs_type_t);
-#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+#[cfg(any(target_os = "linux", target_os = "android"))]
 #[allow(missing_docs)]
-pub const ISOFS_SUPER_MAGIC: FsType = FsType(libc::ISOFS_SUPER_MAGIC as fs_type_t);
-#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+pub const ISOFS_SUPER_MAGIC: FsType =
+    FsType(libc::ISOFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
 #[allow(missing_docs)]
-pub const JFFS2_SUPER_MAGIC: FsType = FsType(libc::JFFS2_SUPER_MAGIC as fs_type_t);
-#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+pub const JFFS2_SUPER_MAGIC: FsType =
+    FsType(libc::JFFS2_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
 #[allow(missing_docs)]
-pub const MINIX_SUPER_MAGIC: FsType = FsType(libc::MINIX_SUPER_MAGIC as fs_type_t);
-#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+pub const MINIX2_SUPER_MAGIC2: FsType =
+    FsType(libc::MINIX2_SUPER_MAGIC2 as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
 #[allow(missing_docs)]
-pub const MINIX_SUPER_MAGIC2: FsType = FsType(libc::MINIX_SUPER_MAGIC2 as fs_type_t);
-#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+pub const MINIX2_SUPER_MAGIC: FsType =
+    FsType(libc::MINIX2_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
 #[allow(missing_docs)]
-pub const MINIX2_SUPER_MAGIC: FsType = FsType(libc::MINIX2_SUPER_MAGIC as fs_type_t);
-#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+pub const MINIX3_SUPER_MAGIC: FsType =
+    FsType(libc::MINIX3_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
 #[allow(missing_docs)]
-pub const MINIX2_SUPER_MAGIC2: FsType = FsType(libc::MINIX2_SUPER_MAGIC2 as fs_type_t);
-#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+pub const MINIX_SUPER_MAGIC2: FsType =
+    FsType(libc::MINIX_SUPER_MAGIC2 as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
 #[allow(missing_docs)]
-pub const MSDOS_SUPER_MAGIC: FsType = FsType(libc::MSDOS_SUPER_MAGIC as fs_type_t);
-#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+pub const MINIX_SUPER_MAGIC: FsType =
+    FsType(libc::MINIX_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const MSDOS_SUPER_MAGIC: FsType =
+    FsType(libc::MSDOS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
 #[allow(missing_docs)]
 pub const NCP_SUPER_MAGIC: FsType = FsType(libc::NCP_SUPER_MAGIC as fs_type_t);
-#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+#[cfg(any(target_os = "linux", target_os = "android"))]
 #[allow(missing_docs)]
 pub const NFS_SUPER_MAGIC: FsType = FsType(libc::NFS_SUPER_MAGIC as fs_type_t);
-#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+#[cfg(any(target_os = "linux", target_os = "android"))]
 #[allow(missing_docs)]
-pub const OPENPROM_SUPER_MAGIC: FsType = FsType(libc::OPENPROM_SUPER_MAGIC as fs_type_t);
-#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+pub const NILFS_SUPER_MAGIC: FsType =
+    FsType(libc::NILFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
 #[allow(missing_docs)]
-pub const OVERLAYFS_SUPER_MAGIC: FsType = FsType(libc::OVERLAYFS_SUPER_MAGIC as fs_type_t);
-#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+pub const OCFS2_SUPER_MAGIC: FsType =
+    FsType(libc::OCFS2_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
 #[allow(missing_docs)]
-pub const PROC_SUPER_MAGIC: FsType = FsType(libc::PROC_SUPER_MAGIC as fs_type_t);
-#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+pub const OPENPROM_SUPER_MAGIC: FsType =
+    FsType(libc::OPENPROM_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
 #[allow(missing_docs)]
-pub const QNX4_SUPER_MAGIC: FsType = FsType(libc::QNX4_SUPER_MAGIC as fs_type_t);
-#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+pub const OVERLAYFS_SUPER_MAGIC: FsType =
+    FsType(libc::OVERLAYFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
 #[allow(missing_docs)]
-pub const REISERFS_SUPER_MAGIC: FsType = FsType(libc::REISERFS_SUPER_MAGIC as fs_type_t);
-#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+pub const PROC_SUPER_MAGIC: FsType =
+    FsType(libc::PROC_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const QNX4_SUPER_MAGIC: FsType =
+    FsType(libc::QNX4_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const QNX6_SUPER_MAGIC: FsType =
+    FsType(libc::QNX6_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const RDTGROUP_SUPER_MAGIC: FsType =
+    FsType(libc::RDTGROUP_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const REISERFS_SUPER_MAGIC: FsType =
+    FsType(libc::REISERFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const SECURITYFS_MAGIC: FsType =
+    FsType(libc::SECURITYFS_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const SELINUX_MAGIC: FsType = FsType(libc::SELINUX_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const SMACK_MAGIC: FsType = FsType(libc::SMACK_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
 #[allow(missing_docs)]
 pub const SMB_SUPER_MAGIC: FsType = FsType(libc::SMB_SUPER_MAGIC as fs_type_t);
-#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const SYSFS_MAGIC: FsType = FsType(libc::SYSFS_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
 #[allow(missing_docs)]
 pub const TMPFS_MAGIC: FsType = FsType(libc::TMPFS_MAGIC as fs_type_t);
-#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+#[cfg(any(target_os = "linux", target_os = "android"))]
 #[allow(missing_docs)]
-pub const USBDEVICE_SUPER_MAGIC: FsType = FsType(libc::USBDEVICE_SUPER_MAGIC as fs_type_t);
-#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+pub const TRACEFS_MAGIC: FsType = FsType(libc::TRACEFS_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
 #[allow(missing_docs)]
-pub const CGROUP_SUPER_MAGIC: FsType = FsType(libc::CGROUP_SUPER_MAGIC as fs_type_t);
-#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+pub const UDF_SUPER_MAGIC: FsType = FsType(libc::UDF_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
 #[allow(missing_docs)]
-pub const CGROUP2_SUPER_MAGIC: FsType = FsType(libc::CGROUP2_SUPER_MAGIC as fs_type_t);
-
+pub const USBDEVICE_SUPER_MAGIC: FsType =
+    FsType(libc::USBDEVICE_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const XENFS_SUPER_MAGIC: FsType =
+    FsType(libc::XENFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const NSFS_MAGIC: FsType = FsType(libc::NSFS_MAGIC as fs_type_t);
+#[cfg(all(
+    any(target_os = "linux", target_os = "android"),
+    not(target_env = "musl")
+))]
+#[allow(missing_docs)]
+pub const XFS_SUPER_MAGIC: FsType = FsType(libc::XFS_SUPER_MAGIC as fs_type_t);
 
 impl Statfs {
     /// Magic code defining system type
@@ -142,12 +305,14 @@
         target_os = "ios",
         target_os = "macos"
     )))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn filesystem_type(&self) -> FsType {
         FsType(self.0.f_type)
     }
 
     /// Magic code defining system type
     #[cfg(not(any(target_os = "linux", target_os = "android")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn filesystem_type_name(&self) -> &str {
         let c_str = unsafe { CStr::from_ptr(self.0.f_fstypename.as_ptr()) };
         c_str.to_str().unwrap()
@@ -155,18 +320,21 @@
 
     /// Optimal transfer block size
     #[cfg(any(target_os = "ios", target_os = "macos"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn optimal_transfer_size(&self) -> i32 {
         self.0.f_iosize
     }
 
     /// Optimal transfer block size
     #[cfg(target_os = "openbsd")]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn optimal_transfer_size(&self) -> u32 {
         self.0.f_iosize
     }
 
     /// Optimal transfer block size
     #[cfg(all(target_os = "linux", target_arch = "s390x"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn optimal_transfer_size(&self) -> u32 {
         self.0.f_bsize
     }
@@ -176,30 +344,49 @@
         target_os = "android",
         all(target_os = "linux", target_env = "musl")
     ))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn optimal_transfer_size(&self) -> libc::c_ulong {
         self.0.f_bsize
     }
 
     /// Optimal transfer block size
-    #[cfg(all(target_os = "linux", not(any(target_arch = "s390x", target_env = "musl"))))]
+    #[cfg(all(
+        target_os = "linux",
+        not(any(
+            target_arch = "s390x",
+            target_env = "musl",
+            target_env = "uclibc"
+        ))
+    ))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn optimal_transfer_size(&self) -> libc::__fsword_t {
         self.0.f_bsize
     }
 
     /// Optimal transfer block size
+    #[cfg(all(target_os = "linux", target_env = "uclibc"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
+    pub fn optimal_transfer_size(&self) -> libc::c_int {
+        self.0.f_bsize
+    }
+
+    /// Optimal transfer block size
     #[cfg(target_os = "dragonfly")]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn optimal_transfer_size(&self) -> libc::c_long {
         self.0.f_iosize
     }
 
     /// Optimal transfer block size
     #[cfg(target_os = "freebsd")]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn optimal_transfer_size(&self) -> u64 {
         self.0.f_iosize
     }
 
     /// Size of a block
     #[cfg(any(target_os = "ios", target_os = "macos", target_os = "openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn block_size(&self) -> u32 {
         self.0.f_bsize
     }
@@ -207,6 +394,7 @@
     /// Size of a block
     // f_bsize on linux: https://github.com/torvalds/linux/blob/master/fs/nfs/super.c#L471
     #[cfg(all(target_os = "linux", target_arch = "s390x"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn block_size(&self) -> u32 {
         self.0.f_bsize
     }
@@ -214,61 +402,126 @@
     /// Size of a block
     // f_bsize on linux: https://github.com/torvalds/linux/blob/master/fs/nfs/super.c#L471
     #[cfg(all(target_os = "linux", target_env = "musl"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn block_size(&self) -> libc::c_ulong {
         self.0.f_bsize
     }
 
     /// Size of a block
     // f_bsize on linux: https://github.com/torvalds/linux/blob/master/fs/nfs/super.c#L471
-    #[cfg(all(target_os = "linux", not(any(target_arch = "s390x", target_env = "musl"))))]
+    #[cfg(all(target_os = "linux", target_env = "uclibc"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
+    pub fn block_size(&self) -> libc::c_int {
+        self.0.f_bsize
+    }
+
+    /// Size of a block
+    // f_bsize on linux: https://github.com/torvalds/linux/blob/master/fs/nfs/super.c#L471
+    #[cfg(all(
+        target_os = "linux",
+        not(any(
+            target_arch = "s390x",
+            target_env = "musl",
+            target_env = "uclibc"
+        ))
+    ))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn block_size(&self) -> libc::__fsword_t {
         self.0.f_bsize
     }
 
     /// Size of a block
     #[cfg(target_os = "freebsd")]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn block_size(&self) -> u64 {
         self.0.f_bsize
     }
 
     /// Size of a block
     #[cfg(target_os = "android")]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn block_size(&self) -> libc::c_ulong {
         self.0.f_bsize
     }
 
     /// Size of a block
     #[cfg(target_os = "dragonfly")]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn block_size(&self) -> libc::c_long {
         self.0.f_bsize
     }
 
+    /// Get the mount flags
+    #[cfg(all(
+        feature = "mount",
+        any(
+            target_os = "dragonfly",
+            target_os = "freebsd",
+            target_os = "macos",
+            target_os = "netbsd",
+            target_os = "openbsd"
+        )
+    ))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
+    #[allow(clippy::unnecessary_cast)] // Not unnecessary on all arches
+    pub fn flags(&self) -> MntFlags {
+        MntFlags::from_bits_truncate(self.0.f_flags as i32)
+    }
+
+    /// Get the mount flags
+    // The f_flags field exists on Android and Fuchsia too, but without man
+    // pages I can't tell if it can be cast to FsFlags.
+    #[cfg(target_os = "linux")]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
+    pub fn flags(&self) -> FsFlags {
+        FsFlags::from_bits_truncate(self.0.f_flags as libc::c_ulong)
+    }
+
     /// Maximum length of filenames
     #[cfg(any(target_os = "freebsd", target_os = "openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn maximum_name_length(&self) -> u32 {
         self.0.f_namemax
     }
 
     /// Maximum length of filenames
     #[cfg(all(target_os = "linux", target_arch = "s390x"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn maximum_name_length(&self) -> u32 {
         self.0.f_namelen
     }
 
     /// Maximum length of filenames
     #[cfg(all(target_os = "linux", target_env = "musl"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn maximum_name_length(&self) -> libc::c_ulong {
         self.0.f_namelen
     }
 
     /// Maximum length of filenames
-    #[cfg(all(target_os = "linux", not(any(target_arch = "s390x", target_env = "musl"))))]
+    #[cfg(all(target_os = "linux", target_env = "uclibc"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
+    pub fn maximum_name_length(&self) -> libc::c_int {
+        self.0.f_namelen
+    }
+
+    /// Maximum length of filenames
+    #[cfg(all(
+        target_os = "linux",
+        not(any(
+            target_arch = "s390x",
+            target_env = "musl",
+            target_env = "uclibc"
+        ))
+    ))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn maximum_name_length(&self) -> libc::__fsword_t {
         self.0.f_namelen
     }
 
     /// Maximum length of filenames
     #[cfg(target_os = "android")]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn maximum_name_length(&self) -> libc::c_ulong {
         self.0.f_namelen
     }
@@ -279,35 +532,26 @@
         target_os = "macos",
         target_os = "android",
         target_os = "freebsd",
+        target_os = "fuchsia",
         target_os = "openbsd",
+        target_os = "linux",
     ))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn blocks(&self) -> u64 {
         self.0.f_blocks
     }
 
     /// Total data blocks in filesystem
     #[cfg(target_os = "dragonfly")]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn blocks(&self) -> libc::c_long {
         self.0.f_blocks
     }
 
     /// Total data blocks in filesystem
-    #[cfg(all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32"))))]
-    pub fn blocks(&self) -> u64 {
-        self.0.f_blocks
-    }
-
-    /// Total data blocks in filesystem
-    #[cfg(not(any(
-        target_os = "ios",
-        target_os = "macos",
-        target_os = "android",
-        target_os = "freebsd",
-        target_os = "openbsd",
-        target_os = "dragonfly",
-        all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32")))
-    )))]
-    pub fn blocks(&self) -> libc::c_ulong {
+    #[cfg(target_os = "emscripten")]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
+    pub fn blocks(&self) -> u32 {
         self.0.f_blocks
     }
 
@@ -317,73 +561,60 @@
         target_os = "macos",
         target_os = "android",
         target_os = "freebsd",
+        target_os = "fuchsia",
         target_os = "openbsd",
+        target_os = "linux",
     ))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn blocks_free(&self) -> u64 {
         self.0.f_bfree
     }
 
     /// Free blocks in filesystem
     #[cfg(target_os = "dragonfly")]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn blocks_free(&self) -> libc::c_long {
         self.0.f_bfree
     }
 
     /// Free blocks in filesystem
-    #[cfg(all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32"))))]
-    pub fn blocks_free(&self) -> u64 {
-        self.0.f_bfree
-    }
-
-    /// Free blocks in filesystem
-    #[cfg(not(any(
-        target_os = "ios",
-        target_os = "macos",
-        target_os = "android",
-        target_os = "freebsd",
-        target_os = "openbsd",
-        target_os = "dragonfly",
-        all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32")))
-    )))]
-    pub fn blocks_free(&self) -> libc::c_ulong {
+    #[cfg(target_os = "emscripten")]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
+    pub fn blocks_free(&self) -> u32 {
         self.0.f_bfree
     }
 
     /// Free blocks available to unprivileged user
-    #[cfg(any(target_os = "ios", target_os = "macos", target_os = "android"))]
+    #[cfg(any(
+        target_os = "ios",
+        target_os = "macos",
+        target_os = "android",
+        target_os = "fuchsia",
+        target_os = "linux",
+    ))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn blocks_available(&self) -> u64 {
         self.0.f_bavail
     }
 
     /// Free blocks available to unprivileged user
     #[cfg(target_os = "dragonfly")]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn blocks_available(&self) -> libc::c_long {
         self.0.f_bavail
     }
 
     /// Free blocks available to unprivileged user
     #[cfg(any(target_os = "freebsd", target_os = "openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn blocks_available(&self) -> i64 {
         self.0.f_bavail
     }
 
     /// Free blocks available to unprivileged user
-    #[cfg(all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32"))))]
-    pub fn blocks_available(&self) -> u64 {
-        self.0.f_bavail
-    }
-
-    /// Free blocks available to unprivileged user
-    #[cfg(not(any(
-        target_os = "ios",
-        target_os = "macos",
-        target_os = "android",
-        target_os = "freebsd",
-        target_os = "openbsd",
-        target_os = "dragonfly",
-        all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32")))
-    )))]
-    pub fn blocks_available(&self) -> libc::c_ulong {
+    #[cfg(target_os = "emscripten")]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
+    pub fn blocks_available(&self) -> u32 {
         self.0.f_bavail
     }
 
@@ -393,78 +624,61 @@
         target_os = "macos",
         target_os = "android",
         target_os = "freebsd",
+        target_os = "fuchsia",
         target_os = "openbsd",
+        target_os = "linux",
     ))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn files(&self) -> u64 {
         self.0.f_files
     }
 
     /// Total file nodes in filesystem
     #[cfg(target_os = "dragonfly")]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn files(&self) -> libc::c_long {
         self.0.f_files
     }
 
     /// Total file nodes in filesystem
-    #[cfg(all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32"))))]
-    pub fn files(&self) -> libc::fsfilcnt_t {
-        self.0.f_files
-    }
-
-    /// Total file nodes in filesystem
-    #[cfg(not(any(
-        target_os = "ios",
-        target_os = "macos",
-        target_os = "android",
-        target_os = "freebsd",
-        target_os = "openbsd",
-        target_os = "dragonfly",
-        all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32")))
-    )))]
-    pub fn files(&self) -> libc::c_ulong {
+    #[cfg(target_os = "emscripten")]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
+    pub fn files(&self) -> u32 {
         self.0.f_files
     }
 
     /// Free file nodes in filesystem
     #[cfg(any(
-            target_os = "android",
-            target_os = "ios",
-            target_os = "macos",
-            target_os = "openbsd"
+        target_os = "ios",
+        target_os = "macos",
+        target_os = "android",
+        target_os = "fuchsia",
+        target_os = "openbsd",
+        target_os = "linux",
     ))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn files_free(&self) -> u64 {
         self.0.f_ffree
     }
 
     /// Free file nodes in filesystem
     #[cfg(target_os = "dragonfly")]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn files_free(&self) -> libc::c_long {
         self.0.f_ffree
     }
 
     /// Free file nodes in filesystem
     #[cfg(target_os = "freebsd")]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn files_free(&self) -> i64 {
         self.0.f_ffree
     }
 
     /// Free file nodes in filesystem
-    #[cfg(all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32"))))]
-    pub fn files_free(&self) -> libc::fsfilcnt_t {
-        self.0.f_ffree
-    }
-
-    /// Free file nodes in filesystem
-    #[cfg(not(any(
-        target_os = "ios",
-        target_os = "macos",
-        target_os = "android",
-        target_os = "freebsd",
-        target_os = "openbsd",
-        target_os = "dragonfly",
-        all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32")))
-    )))]
-    pub fn files_free(&self) -> libc::c_ulong {
+    #[cfg(target_os = "emscripten")]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
+    pub fn files_free(&self) -> u32 {
         self.0.f_ffree
     }
 
@@ -476,22 +690,33 @@
 
 impl Debug for Statfs {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        f.debug_struct("Statfs")
-            .field("optimal_transfer_size", &self.optimal_transfer_size())
-            .field("block_size", &self.block_size())
-            .field("blocks", &self.blocks())
-            .field("blocks_free", &self.blocks_free())
-            .field("blocks_available", &self.blocks_available())
-            .field("files", &self.files())
-            .field("files_free", &self.files_free())
-            .field("filesystem_id", &self.filesystem_id())
-            .finish()
+        let mut ds = f.debug_struct("Statfs");
+        ds.field("optimal_transfer_size", &self.optimal_transfer_size());
+        ds.field("block_size", &self.block_size());
+        ds.field("blocks", &self.blocks());
+        ds.field("blocks_free", &self.blocks_free());
+        ds.field("blocks_available", &self.blocks_available());
+        ds.field("files", &self.files());
+        ds.field("files_free", &self.files_free());
+        ds.field("filesystem_id", &self.filesystem_id());
+        #[cfg(all(
+            feature = "mount",
+            any(
+                target_os = "dragonfly",
+                target_os = "freebsd",
+                target_os = "macos",
+                target_os = "netbsd",
+                target_os = "openbsd"
+            )
+        ))]
+        ds.field("flags", &self.flags());
+        ds.finish()
     }
 }
 
 /// Describes a mounted file system.
 ///
-/// The result is OS-dependent.  For a portabable alternative, see
+/// The result is OS-dependent.  For a portable alternative, see
 /// [`statvfs`](crate::sys::statvfs::statvfs).
 ///
 /// # Arguments
@@ -499,15 +724,17 @@
 /// `path` - Path to any file within the file system to describe
 pub fn statfs<P: ?Sized + NixPath>(path: &P) -> Result<Statfs> {
     unsafe {
-        let mut stat = mem::MaybeUninit::<libc::statfs>::uninit();
-        let res = path.with_nix_path(|path| libc::statfs(path.as_ptr(), stat.as_mut_ptr()))?;
+        let mut stat = mem::MaybeUninit::<type_of_statfs>::uninit();
+        let res = path.with_nix_path(|path| {
+            LIBC_STATFS(path.as_ptr(), stat.as_mut_ptr())
+        })?;
         Errno::result(res).map(|_| Statfs(stat.assume_init()))
     }
 }
 
 /// Describes a mounted file system.
 ///
-/// The result is OS-dependent.  For a portabable alternative, see
+/// The result is OS-dependent.  For a portable alternative, see
 /// [`fstatvfs`](crate::sys::statvfs::fstatvfs).
 ///
 /// # Arguments
@@ -515,8 +742,8 @@
 /// `fd` - File descriptor of any open file within the file system to describe
 pub fn fstatfs<T: AsRawFd>(fd: &T) -> Result<Statfs> {
     unsafe {
-        let mut stat = mem::MaybeUninit::<libc::statfs>::uninit();
-        Errno::result(libc::fstatfs(fd.as_raw_fd(), stat.as_mut_ptr()))
+        let mut stat = mem::MaybeUninit::<type_of_statfs>::uninit();
+        Errno::result(LIBC_FSTATFS(fd.as_raw_fd(), stat.as_mut_ptr()))
             .map(|_| Statfs(stat.assume_init()))
     }
 }
@@ -564,6 +791,8 @@
         assert_fs_equals(fs, vfs);
     }
 
+    // The cast is not unnecessary on all platforms.
+    #[allow(clippy::unnecessary_cast)]
     fn assert_fs_equals(fs: Statfs, vfs: Statvfs) {
         assert_eq!(fs.files() as u64, vfs.files() as u64);
         assert_eq!(fs.blocks() as u64, vfs.blocks() as u64);
@@ -611,6 +840,8 @@
         assert_fs_equals_strict(fs.unwrap(), vfs.unwrap())
     }
 
+    // The cast is not unnecessary on all platforms.
+    #[allow(clippy::unnecessary_cast)]
     fn assert_fs_equals_strict(fs: Statfs, vfs: Statvfs) {
         assert_eq!(fs.files_free() as u64, vfs.files_free() as u64);
         assert_eq!(fs.blocks_free() as u64, vfs.blocks_free() as u64);
diff --git a/src/sys/statvfs.rs b/src/sys/statvfs.rs
index 15e7a7d..8de369f 100644
--- a/src/sys/statvfs.rs
+++ b/src/sys/statvfs.rs
@@ -7,7 +7,7 @@
 
 use libc::{self, c_ulong};
 
-use crate::{Result, NixPath, errno::Errno};
+use crate::{errno::Errno, NixPath, Result};
 
 #[cfg(not(target_os = "redox"))]
 libc_bitflags!(
@@ -16,38 +16,50 @@
     #[derive(Default)]
     pub struct FsFlags: c_ulong {
         /// Read Only
+        #[cfg(not(target_os = "haiku"))]
         ST_RDONLY;
         /// Do not allow the set-uid bits to have an effect
+        #[cfg(not(target_os = "haiku"))]
         ST_NOSUID;
         /// Do not interpret character or block-special devices
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         ST_NODEV;
         /// Do not allow execution of binaries on the filesystem
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         ST_NOEXEC;
         /// All IO should be done synchronously
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         ST_SYNCHRONOUS;
         /// Allow mandatory locks on the filesystem
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         ST_MANDLOCK;
         /// Write on file/directory/symlink
         #[cfg(target_os = "linux")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         ST_WRITE;
         /// Append-only file
         #[cfg(target_os = "linux")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         ST_APPEND;
         /// Immutable file
         #[cfg(target_os = "linux")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         ST_IMMUTABLE;
         /// Do not update access times on files
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         ST_NOATIME;
         /// Do not update access times on files
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         ST_NODIRATIME;
         /// Update access time relative to modify/change time
         #[cfg(any(target_os = "android", all(target_os = "linux", not(target_env = "musl"))))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         ST_RELATIME;
     }
 );
@@ -109,6 +121,7 @@
 
     /// Get the mount flags
     #[cfg(not(target_os = "redox"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn flags(&self) -> FsFlags {
         FsFlags::from_bits_truncate(self.0.f_flag)
     }
@@ -117,7 +130,6 @@
     pub fn name_max(&self) -> c_ulong {
         self.0.f_namemax
     }
-
 }
 
 /// Return a `Statvfs` object with information about the `path`
@@ -125,9 +137,9 @@
     unsafe {
         Errno::clear();
         let mut stat = mem::MaybeUninit::<libc::statvfs>::uninit();
-        let res = path.with_nix_path(|path|
+        let res = path.with_nix_path(|path| {
             libc::statvfs(path.as_ptr(), stat.as_mut_ptr())
-        )?;
+        })?;
 
         Errno::result(res).map(|_| Statvfs(stat.assume_init()))
     }
@@ -145,8 +157,8 @@
 
 #[cfg(test)]
 mod test {
-    use std::fs::File;
     use crate::sys::statvfs::*;
+    use std::fs::File;
 
     #[test]
     fn statvfs_call() {
diff --git a/src/sys/sysinfo.rs b/src/sys/sysinfo.rs
index dc943c1..e8aa00b 100644
--- a/src/sys/sysinfo.rs
+++ b/src/sys/sysinfo.rs
@@ -1,9 +1,9 @@
 use libc::{self, SI_LOAD_SHIFT};
-use std::{cmp, mem};
 use std::time::Duration;
+use std::{cmp, mem};
 
-use crate::Result;
 use crate::errno::Errno;
+use crate::Result;
 
 /// System info structure returned by `sysinfo`.
 #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
@@ -30,6 +30,8 @@
     }
 
     /// Returns the time since system boot.
+    // The cast is not unnecessary on all platforms.
+    #[allow(clippy::unnecessary_cast)]
     pub fn uptime(&self) -> Duration {
         // Truncate negative values to 0
         Duration::from_secs(cmp::max(self.0.uptime, 0) as u64)
@@ -64,6 +66,8 @@
         self.scale_mem(self.0.freeram)
     }
 
+    // The cast is not unnecessary on all platforms.
+    #[allow(clippy::unnecessary_cast)]
     fn scale_mem(&self, units: mem_blocks_t) -> u64 {
         units as u64 * self.0.mem_unit as u64
     }
@@ -75,5 +79,5 @@
 pub fn sysinfo() -> Result<SysInfo> {
     let mut info = mem::MaybeUninit::uninit();
     let res = unsafe { libc::sysinfo(info.as_mut_ptr()) };
-    Errno::result(res).map(|_| unsafe{ SysInfo(info.assume_init()) })
+    Errno::result(res).map(|_| unsafe { SysInfo(info.assume_init()) })
 }
diff --git a/src/sys/termios.rs b/src/sys/termios.rs
index 01d4608..fba2cd8 100644
--- a/src/sys/termios.rs
+++ b/src/sys/termios.rs
@@ -64,9 +64,9 @@
 //! # use nix::sys::termios::{BaudRate, cfsetispeed, cfsetospeed, cfsetspeed, Termios};
 //! # fn main() {
 //! # let mut t: Termios = unsafe { std::mem::zeroed() };
-//! cfsetispeed(&mut t, BaudRate::B9600);
-//! cfsetospeed(&mut t, BaudRate::B9600);
-//! cfsetspeed(&mut t, BaudRate::B9600);
+//! cfsetispeed(&mut t, BaudRate::B9600).unwrap();
+//! cfsetospeed(&mut t, BaudRate::B9600).unwrap();
+//! cfsetspeed(&mut t, BaudRate::B9600).unwrap();
 //! # }
 //! ```
 //!
@@ -76,21 +76,37 @@
 //! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetispeed, cfsetspeed, Termios};
 //! # fn main() {
 //! # let mut t: Termios = unsafe { std::mem::zeroed() };
-//! # cfsetspeed(&mut t, BaudRate::B9600);
+//! # cfsetspeed(&mut t, BaudRate::B9600).unwrap();
 //! let speed = cfgetispeed(&t);
 //! assert_eq!(speed, cfgetospeed(&t));
-//! cfsetispeed(&mut t, speed);
+//! cfsetispeed(&mut t, speed).unwrap();
 //! # }
 //! ```
 //!
 //! On non-BSDs, `cfgetispeed()` and `cfgetospeed()` both return a `BaudRate`:
 //!
-#![cfg_attr(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
-                target_os = "macos", target_os = "netbsd", target_os = "openbsd"),
-            doc = " ```rust,ignore")]
-#![cfg_attr(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
-                    target_os = "macos", target_os = "netbsd", target_os = "openbsd")),
-            doc = " ```rust")]
+#![cfg_attr(
+    any(
+        target_os = "freebsd",
+        target_os = "dragonfly",
+        target_os = "ios",
+        target_os = "macos",
+        target_os = "netbsd",
+        target_os = "openbsd"
+    ),
+    doc = " ```rust,ignore"
+)]
+#![cfg_attr(
+    not(any(
+        target_os = "freebsd",
+        target_os = "dragonfly",
+        target_os = "ios",
+        target_os = "macos",
+        target_os = "netbsd",
+        target_os = "openbsd"
+    )),
+    doc = " ```rust"
+)]
 //! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetspeed, Termios};
 //! # fn main() {
 //! # let mut t: Termios = unsafe { std::mem::zeroed() };
@@ -102,12 +118,28 @@
 //!
 //! But on the BSDs, `cfgetispeed()` and `cfgetospeed()` both return `u32`s:
 //!
-#![cfg_attr(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
-                target_os = "macos", target_os = "netbsd", target_os = "openbsd"),
-            doc = " ```rust")]
-#![cfg_attr(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
-                    target_os = "macos", target_os = "netbsd", target_os = "openbsd")),
-            doc = " ```rust,ignore")]
+#![cfg_attr(
+    any(
+        target_os = "freebsd",
+        target_os = "dragonfly",
+        target_os = "ios",
+        target_os = "macos",
+        target_os = "netbsd",
+        target_os = "openbsd"
+    ),
+    doc = " ```rust"
+)]
+#![cfg_attr(
+    not(any(
+        target_os = "freebsd",
+        target_os = "dragonfly",
+        target_os = "ios",
+        target_os = "macos",
+        target_os = "netbsd",
+        target_os = "openbsd"
+    )),
+    doc = " ```rust,ignore"
+)]
 //! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetspeed, Termios};
 //! # fn main() {
 //! # let mut t: Termios = unsafe { std::mem::zeroed() };
@@ -119,12 +151,28 @@
 //!
 //! It's trivial to convert from a `BaudRate` to a `u32` on BSDs:
 //!
-#![cfg_attr(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
-                target_os = "macos", target_os = "netbsd", target_os = "openbsd"),
-            doc = " ```rust")]
-#![cfg_attr(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
-                    target_os = "macos", target_os = "netbsd", target_os = "openbsd")),
-            doc = " ```rust,ignore")]
+#![cfg_attr(
+    any(
+        target_os = "freebsd",
+        target_os = "dragonfly",
+        target_os = "ios",
+        target_os = "macos",
+        target_os = "netbsd",
+        target_os = "openbsd"
+    ),
+    doc = " ```rust"
+)]
+#![cfg_attr(
+    not(any(
+        target_os = "freebsd",
+        target_os = "dragonfly",
+        target_os = "ios",
+        target_os = "macos",
+        target_os = "netbsd",
+        target_os = "openbsd"
+    )),
+    doc = " ```rust,ignore"
+)]
 //! # use nix::sys::termios::{BaudRate, cfgetispeed, cfsetspeed, Termios};
 //! # fn main() {
 //! # let mut t: Termios = unsafe { std::mem::zeroed() };
@@ -137,12 +185,28 @@
 //! And on BSDs you can specify arbitrary baud rates (**note** this depends on hardware support)
 //! by specifying baud rates directly using `u32`s:
 //!
-#![cfg_attr(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
-                target_os = "macos", target_os = "netbsd", target_os = "openbsd"),
-            doc = " ```rust")]
-#![cfg_attr(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
-                    target_os = "macos", target_os = "netbsd", target_os = "openbsd")),
-            doc = " ```rust,ignore")]
+#![cfg_attr(
+    any(
+        target_os = "freebsd",
+        target_os = "dragonfly",
+        target_os = "ios",
+        target_os = "macos",
+        target_os = "netbsd",
+        target_os = "openbsd"
+    ),
+    doc = " ```rust"
+)]
+#![cfg_attr(
+    not(any(
+        target_os = "freebsd",
+        target_os = "dragonfly",
+        target_os = "ios",
+        target_os = "macos",
+        target_os = "netbsd",
+        target_os = "openbsd"
+    )),
+    doc = " ```rust,ignore"
+)]
 //! # use nix::sys::termios::{cfsetispeed, cfsetospeed, cfsetspeed, Termios};
 //! # fn main() {
 //! # let mut t: Termios = unsafe { std::mem::zeroed() };
@@ -151,15 +215,16 @@
 //! cfsetspeed(&mut t, 9600u32);
 //! # }
 //! ```
-use cfg_if::cfg_if;
-use crate::Result;
 use crate::errno::Errno;
+use crate::Result;
+use cfg_if::cfg_if;
 use libc::{self, c_int, tcflag_t};
 use std::cell::{Ref, RefCell};
 use std::convert::From;
 use std::mem;
 use std::os::unix::io::RawFd;
 
+#[cfg(feature = "process")]
 use crate::unistd::Pid;
 
 /// Stores settings for the termios API
@@ -180,6 +245,12 @@
     pub local_flags: LocalFlags,
     /// Control characters (see `termios.c_cc` documentation)
     pub control_chars: [libc::cc_t; NCCS],
+    /// Line discipline (see `termios.c_line` documentation)
+    #[cfg(any(target_os = "linux", target_os = "android",))]
+    pub line_discipline: libc::cc_t,
+    /// Line discipline (see `termios.c_line` documentation)
+    #[cfg(target_os = "haiku")]
+    pub line_discipline: libc::c_char,
 }
 
 impl Termios {
@@ -195,6 +266,14 @@
             termios.c_cflag = self.control_flags.bits();
             termios.c_lflag = self.local_flags.bits();
             termios.c_cc = self.control_chars;
+            #[cfg(any(
+                target_os = "linux",
+                target_os = "android",
+                target_os = "haiku",
+            ))]
+            {
+                termios.c_line = self.line_discipline;
+            }
         }
         self.inner.borrow()
     }
@@ -213,6 +292,14 @@
             termios.c_cflag = self.control_flags.bits();
             termios.c_lflag = self.local_flags.bits();
             termios.c_cc = self.control_chars;
+            #[cfg(any(
+                target_os = "linux",
+                target_os = "android",
+                target_os = "haiku",
+            ))]
+            {
+                termios.c_line = self.line_discipline;
+            }
         }
         self.inner.as_ptr()
     }
@@ -225,6 +312,14 @@
         self.control_flags = ControlFlags::from_bits_truncate(termios.c_cflag);
         self.local_flags = LocalFlags::from_bits_truncate(termios.c_lflag);
         self.control_chars = termios.c_cc;
+        #[cfg(any(
+            target_os = "linux",
+            target_os = "android",
+            target_os = "haiku",
+        ))]
+        {
+            self.line_discipline = termios.c_line;
+        }
     }
 }
 
@@ -237,6 +332,12 @@
             control_flags: ControlFlags::from_bits_truncate(termios.c_cflag),
             local_flags: LocalFlags::from_bits_truncate(termios.c_lflag),
             control_chars: termios.c_cc,
+            #[cfg(any(
+                target_os = "linux",
+                target_os = "android",
+                target_os = "haiku",
+            ))]
+            line_discipline: termios.c_line,
         }
     }
 }
@@ -247,15 +348,16 @@
     }
 }
 
-libc_enum!{
+libc_enum! {
     /// Baud rates supported by the system.
     ///
     /// For the BSDs, arbitrary baud rates can be specified by using `u32`s directly instead of this
     /// enum.
     ///
     /// B0 is special and will disable the port.
+    #[cfg_attr(all(any(target_os = "haiku"), target_pointer_width = "64"), repr(u8))]
     #[cfg_attr(all(any(target_os = "ios", target_os = "macos"), target_pointer_width = "64"), repr(u64))]
-    #[cfg_attr(not(all(any(target_os = "ios", target_os = "macos"), target_pointer_width = "64")), repr(u32))]
+    #[cfg_attr(not(all(any(target_os = "ios", target_os = "macos", target_os = "haiku"), target_pointer_width = "64")), repr(u32))]
     #[non_exhaustive]
     pub enum BaudRate {
         B0,
@@ -276,6 +378,7 @@
                 target_os = "macos",
                 target_os = "netbsd",
                 target_os = "openbsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         B7200,
         B9600,
         #[cfg(any(target_os = "dragonfly",
@@ -283,6 +386,7 @@
                 target_os = "macos",
                 target_os = "netbsd",
                 target_os = "openbsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         B14400,
         B19200,
         #[cfg(any(target_os = "dragonfly",
@@ -290,6 +394,7 @@
                 target_os = "macos",
                 target_os = "netbsd",
                 target_os = "openbsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         B28800,
         B38400,
         B57600,
@@ -298,12 +403,15 @@
                 target_os = "macos",
                 target_os = "netbsd",
                 target_os = "openbsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         B76800,
         B115200,
         #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         B153600,
         B230400,
         #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         B307200,
         #[cfg(any(target_os = "android",
                   target_os = "freebsd",
@@ -311,10 +419,13 @@
                   target_os = "linux",
                   target_os = "netbsd",
                   target_os = "solaris"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         B460800,
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         B500000,
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         B576000,
         #[cfg(any(target_os = "android",
                   target_os = "freebsd",
@@ -322,39 +433,57 @@
                   target_os = "linux",
                   target_os = "netbsd",
                   target_os = "solaris"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         B921600,
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         B1000000,
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         B1152000,
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         B1500000,
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         B2000000,
         #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         B2500000,
         #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         B3000000,
         #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         B3500000,
         #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         B4000000,
     }
     impl TryFrom<libc::speed_t>
 }
 
-#[cfg(any(target_os = "freebsd",
-          target_os = "dragonfly",
-          target_os = "ios",
-          target_os = "macos",
-          target_os = "netbsd",
-          target_os = "openbsd"))]
+#[cfg(any(
+    target_os = "freebsd",
+    target_os = "dragonfly",
+    target_os = "ios",
+    target_os = "macos",
+    target_os = "netbsd",
+    target_os = "openbsd"
+))]
 impl From<BaudRate> for u32 {
     fn from(b: BaudRate) -> u32 {
         b as u32
     }
 }
 
+#[cfg(target_os = "haiku")]
+impl From<BaudRate> for u8 {
+    fn from(b: BaudRate) -> u8 {
+        b as u8
+    }
+}
+
 // TODO: Add TCSASOFT, which will require treating this as a bitfield.
 libc_enum! {
     /// Specify when a port configuration change should occur.
@@ -407,6 +536,7 @@
 }
 
 // TODO: Make this usable directly as a slice index.
+#[cfg(not(target_os = "haiku"))]
 libc_enum! {
     /// Indices into the `termios.c_cc` array for special characters.
     #[repr(usize)]
@@ -420,6 +550,7 @@
                 target_os = "netbsd",
                 target_os = "openbsd",
                 target_os = "solaris"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         VDSUSP,
         VEOF,
         VEOL,
@@ -429,12 +560,14 @@
                   target_os = "freebsd",
                   target_os = "illumos",
                   target_os = "solaris"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         VERASE2,
         VINTR,
         VKILL,
         VLNEXT,
         #[cfg(not(any(all(target_os = "linux", target_arch = "sparc64"),
                 target_os = "illumos", target_os = "solaris")))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         VMIN,
         VQUIT,
         VREPRINT,
@@ -446,36 +579,48 @@
                 target_os = "netbsd",
                 target_os = "openbsd",
                 target_os = "solaris"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         VSTATUS,
         VSTOP,
         VSUSP,
         #[cfg(target_os = "linux")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         VSWTC,
         #[cfg(any(target_os = "haiku", target_os = "illumos", target_os = "solaris"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         VSWTCH,
         #[cfg(not(any(all(target_os = "linux", target_arch = "sparc64"),
                 target_os = "illumos", target_os = "solaris")))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         VTIME,
         VWERASE,
         #[cfg(target_os = "dragonfly")]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         VCHECKPT,
     }
 }
 
-#[cfg(any(all(target_os = "linux", target_arch = "sparc64"),
-        target_os = "illumos", target_os = "solaris"))]
+#[cfg(any(
+    all(target_os = "linux", target_arch = "sparc64"),
+    target_os = "illumos",
+    target_os = "solaris"
+))]
 impl SpecialCharacterIndices {
     pub const VMIN: SpecialCharacterIndices = SpecialCharacterIndices::VEOF;
     pub const VTIME: SpecialCharacterIndices = SpecialCharacterIndices::VEOL;
 }
 
 pub use libc::NCCS;
-#[cfg(any(target_os = "dragonfly",
-          target_os = "freebsd",
-          target_os = "linux",
-          target_os = "macos",
-          target_os = "netbsd",
-          target_os = "openbsd"))]
+#[cfg(any(
+    target_os = "android",
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "linux",
+    target_os = "macos",
+    target_os = "netbsd",
+    target_os = "openbsd"
+))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
 pub use libc::_POSIX_VDISABLE;
 
 libc_bitflags! {
@@ -493,10 +638,13 @@
         IXON;
         IXOFF;
         #[cfg(not(target_os = "redox"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IXANY;
-        #[cfg(not(target_os = "redox"))]
+        #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IMAXBEL;
         #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         IUTF8;
     }
 }
@@ -509,6 +657,7 @@
                   target_os = "haiku",
                   target_os = "linux",
                   target_os = "openbsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         OLCUC;
         ONLCR;
         OCRNL as tcflag_t;
@@ -519,48 +668,56 @@
                   target_os = "ios",
                   target_os = "linux",
                   target_os = "macos"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         OFILL as tcflag_t;
         #[cfg(any(target_os = "android",
                   target_os = "haiku",
                   target_os = "ios",
                   target_os = "linux",
                   target_os = "macos"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         OFDEL as tcflag_t;
         #[cfg(any(target_os = "android",
                   target_os = "haiku",
                   target_os = "ios",
                   target_os = "linux",
                   target_os = "macos"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         NL0 as tcflag_t;
         #[cfg(any(target_os = "android",
                   target_os = "haiku",
                   target_os = "ios",
                   target_os = "linux",
                   target_os = "macos"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         NL1 as tcflag_t;
         #[cfg(any(target_os = "android",
                   target_os = "haiku",
                   target_os = "ios",
                   target_os = "linux",
                   target_os = "macos"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         CR0 as tcflag_t;
         #[cfg(any(target_os = "android",
                   target_os = "haiku",
                   target_os = "ios",
                   target_os = "linux",
                   target_os = "macos"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         CR1 as tcflag_t;
         #[cfg(any(target_os = "android",
                   target_os = "haiku",
                   target_os = "ios",
                   target_os = "linux",
                   target_os = "macos"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         CR2 as tcflag_t;
         #[cfg(any(target_os = "android",
                   target_os = "haiku",
                   target_os = "ios",
                   target_os = "linux",
                   target_os = "macos"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         CR3 as tcflag_t;
         #[cfg(any(target_os = "android",
                   target_os = "freebsd",
@@ -568,18 +725,21 @@
                   target_os = "ios",
                   target_os = "linux",
                   target_os = "macos"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         TAB0 as tcflag_t;
         #[cfg(any(target_os = "android",
                   target_os = "haiku",
                   target_os = "ios",
                   target_os = "linux",
                   target_os = "macos"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         TAB1 as tcflag_t;
         #[cfg(any(target_os = "android",
                   target_os = "haiku",
                   target_os = "ios",
                   target_os = "linux",
                   target_os = "macos"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         TAB2 as tcflag_t;
         #[cfg(any(target_os = "android",
                   target_os = "freebsd",
@@ -587,44 +747,52 @@
                   target_os = "ios",
                   target_os = "linux",
                   target_os = "macos"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         TAB3 as tcflag_t;
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         XTABS;
         #[cfg(any(target_os = "android",
                   target_os = "haiku",
                   target_os = "ios",
                   target_os = "linux",
                   target_os = "macos"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         BS0 as tcflag_t;
         #[cfg(any(target_os = "android",
                   target_os = "haiku",
                   target_os = "ios",
                   target_os = "linux",
                   target_os = "macos"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         BS1 as tcflag_t;
         #[cfg(any(target_os = "android",
                   target_os = "haiku",
                   target_os = "ios",
                   target_os = "linux",
                   target_os = "macos"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         VT0 as tcflag_t;
         #[cfg(any(target_os = "android",
                   target_os = "haiku",
                   target_os = "ios",
                   target_os = "linux",
                   target_os = "macos"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         VT1 as tcflag_t;
         #[cfg(any(target_os = "android",
                   target_os = "haiku",
                   target_os = "ios",
                   target_os = "linux",
                   target_os = "macos"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         FF0 as tcflag_t;
         #[cfg(any(target_os = "android",
                   target_os = "haiku",
                   target_os = "ios",
                   target_os = "linux",
                   target_os = "macos"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         FF1 as tcflag_t;
         #[cfg(any(target_os = "freebsd",
                   target_os = "dragonfly",
@@ -632,12 +800,14 @@
                   target_os = "macos",
                   target_os = "netbsd",
                   target_os = "openbsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         OXTABS;
         #[cfg(any(target_os = "freebsd",
                   target_os = "dragonfly",
                   target_os = "macos",
                   target_os = "netbsd",
                   target_os = "openbsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         ONOEOT as tcflag_t;
 
         // Bitmasks for use with OutputFlags to select specific settings
@@ -649,12 +819,14 @@
                   target_os = "ios",
                   target_os = "linux",
                   target_os = "macos"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         NLDLY as tcflag_t; // FIXME: Datatype needs to be corrected in libc for mac
         #[cfg(any(target_os = "android",
                   target_os = "haiku",
                   target_os = "ios",
                   target_os = "linux",
                   target_os = "macos"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         CRDLY as tcflag_t;
         #[cfg(any(target_os = "android",
                   target_os = "freebsd",
@@ -662,24 +834,28 @@
                   target_os = "ios",
                   target_os = "linux",
                   target_os = "macos"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         TABDLY as tcflag_t;
         #[cfg(any(target_os = "android",
                   target_os = "haiku",
                   target_os = "ios",
                   target_os = "linux",
                   target_os = "macos"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         BSDLY as tcflag_t;
         #[cfg(any(target_os = "android",
                   target_os = "haiku",
                   target_os = "ios",
                   target_os = "linux",
                   target_os = "macos"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         VTDLY as tcflag_t;
         #[cfg(any(target_os = "android",
                   target_os = "haiku",
                   target_os = "ios",
                   target_os = "linux",
                   target_os = "macos"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         FFDLY as tcflag_t;
     }
 }
@@ -693,6 +869,7 @@
                   target_os = "macos",
                   target_os = "netbsd",
                   target_os = "openbsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         CIGNORE;
         CS5;
         CS6;
@@ -705,43 +882,54 @@
         HUPCL;
         CLOCAL;
         #[cfg(not(target_os = "redox"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         CRTSCTS;
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         CBAUD;
         #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "mips"))))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         CMSPAR;
         #[cfg(any(target_os = "android",
                   all(target_os = "linux",
                       not(any(target_arch = "powerpc", target_arch = "powerpc64")))))]
         CIBAUD;
         #[cfg(any(target_os = "android", target_os = "linux"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         CBAUDEX;
         #[cfg(any(target_os = "dragonfly",
                   target_os = "freebsd",
                   target_os = "macos",
                   target_os = "netbsd",
                   target_os = "openbsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         MDMBUF;
         #[cfg(any(target_os = "netbsd", target_os = "openbsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         CHWFLOW;
         #[cfg(any(target_os = "dragonfly",
                   target_os = "freebsd",
                   target_os = "netbsd",
                   target_os = "openbsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         CCTS_OFLOW;
         #[cfg(any(target_os = "dragonfly",
                   target_os = "freebsd",
                   target_os = "netbsd",
                   target_os = "openbsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         CRTS_IFLOW;
         #[cfg(any(target_os = "dragonfly",
                   target_os = "freebsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         CDTR_IFLOW;
         #[cfg(any(target_os = "dragonfly",
                   target_os = "freebsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         CDSR_OFLOW;
         #[cfg(any(target_os = "dragonfly",
                   target_os = "freebsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         CCAR_OFLOW;
 
         // Bitmasks for use with ControlFlags to select specific settings
@@ -756,14 +944,17 @@
     /// Flags for setting any local modes
     pub struct LocalFlags: tcflag_t {
         #[cfg(not(target_os = "redox"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         ECHOKE;
         ECHOE;
         ECHOK;
         ECHO;
         ECHONL;
         #[cfg(not(target_os = "redox"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         ECHOPRT;
         #[cfg(not(target_os = "redox"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         ECHOCTL;
         ISIG;
         ICANON;
@@ -773,12 +964,15 @@
                   target_os = "macos",
                   target_os = "netbsd",
                   target_os = "openbsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         ALTWERASE;
         IEXTEN;
-        #[cfg(not(target_os = "redox"))]
+        #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         EXTPROC;
         TOSTOP;
         #[cfg(not(target_os = "redox"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         FLUSHO;
         #[cfg(any(target_os = "freebsd",
                   target_os = "dragonfly",
@@ -786,14 +980,16 @@
                   target_os = "macos",
                   target_os = "netbsd",
                   target_os = "openbsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         NOKERNINFO;
         #[cfg(not(target_os = "redox"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         PENDIN;
         NOFLSH;
     }
 }
 
-cfg_if!{
+cfg_if! {
     if #[cfg(any(target_os = "freebsd",
                  target_os = "dragonfly",
                  target_os = "ios",
@@ -804,6 +1000,8 @@
         /// [cfgetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetispeed.html)).
         ///
         /// `cfgetispeed()` extracts the input baud rate from the given `Termios` structure.
+        // The cast is not unnecessary on all platforms.
+        #[allow(clippy::unnecessary_cast)]
         pub fn cfgetispeed(termios: &Termios) -> u32 {
             let inner_termios = termios.get_libc_termios();
             unsafe { libc::cfgetispeed(&*inner_termios) as u32 }
@@ -813,6 +1011,8 @@
         /// [cfgetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetospeed.html)).
         ///
         /// `cfgetospeed()` extracts the output baud rate from the given `Termios` structure.
+        // The cast is not unnecessary on all platforms.
+        #[allow(clippy::unnecessary_cast)]
         pub fn cfgetospeed(termios: &Termios) -> u32 {
             let inner_termios = termios.get_libc_termios();
             unsafe { libc::cfgetospeed(&*inner_termios) as u32 }
@@ -899,6 +1099,7 @@
         ///
         /// `cfsetspeed()` sets the input and output baud rate in the given `Termios` structure. Note that
         /// this is part of the 4.4BSD standard and not part of POSIX.
+        #[cfg(not(target_os = "haiku"))]
         pub fn cfsetspeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
             let inner_termios = unsafe { termios.get_libc_termios_mut() };
             let res = unsafe { libc::cfsetspeed(inner_termios, baud as libc::speed_t) };
@@ -927,6 +1128,7 @@
 ///
 /// Note that this is a non-standard function, available on FreeBSD.
 #[cfg(target_os = "freebsd")]
+#[cfg_attr(docsrs, doc(cfg(all())))]
 pub fn cfmakesane(termios: &mut Termios) {
     let inner_termios = unsafe { termios.get_libc_termios_mut() };
     unsafe {
@@ -959,7 +1161,10 @@
 /// *any* of the parameters were successfully set, not only if all were set successfully.
 pub fn tcsetattr(fd: RawFd, actions: SetArg, termios: &Termios) -> Result<()> {
     let inner_termios = termios.get_libc_termios();
-    Errno::result(unsafe { libc::tcsetattr(fd, actions as c_int, &*inner_termios) }).map(drop)
+    Errno::result(unsafe {
+        libc::tcsetattr(fd, actions as c_int, &*inner_termios)
+    })
+    .map(drop)
 }
 
 /// Block until all output data is written (see
@@ -995,6 +1200,8 @@
     Errno::result(unsafe { libc::tcsendbreak(fd, duration) }).map(drop)
 }
 
+feature! {
+#![feature = "process"]
 /// Get the session controlled by the given terminal (see
 /// [tcgetsid(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetsid.html)).
 pub fn tcgetsid(fd: RawFd) -> Result<Pid> {
@@ -1002,6 +1209,7 @@
 
     Errno::result(res).map(Pid::from_raw)
 }
+}
 
 #[cfg(test)]
 mod test {
@@ -1011,6 +1219,9 @@
     #[test]
     fn try_from() {
         assert_eq!(Ok(BaudRate::B0), BaudRate::try_from(libc::B0));
-        assert!(BaudRate::try_from(999999999).is_err());
+        #[cfg(not(target_os = "haiku"))]
+        BaudRate::try_from(999999999).expect_err("assertion failed");
+        #[cfg(target_os = "haiku")]
+        BaudRate::try_from(99).expect_err("assertion failed");
     }
 }
diff --git a/src/sys/time.rs b/src/sys/time.rs
index ac42471..0042c45 100644
--- a/src/sys/time.rs
+++ b/src/sys/time.rs
@@ -1,9 +1,144 @@
-use std::{cmp, fmt, ops};
-use std::time::Duration;
-use std::convert::From;
+#[cfg_attr(target_env = "musl", allow(deprecated))]
+// https://github.com/rust-lang/libc/issues/1848
+pub use libc::{suseconds_t, time_t};
 use libc::{timespec, timeval};
-#[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
-pub use libc::{time_t, suseconds_t};
+use std::convert::From;
+use std::time::Duration;
+use std::{cmp, fmt, ops};
+
+const fn zero_init_timespec() -> timespec {
+    // `std::mem::MaybeUninit::zeroed()` is not yet a const fn
+    // (https://github.com/rust-lang/rust/issues/91850) so we will instead initialize an array of
+    // the appropriate size to zero and then transmute it to a timespec value.
+    unsafe { std::mem::transmute([0u8; std::mem::size_of::<timespec>()]) }
+}
+
+#[cfg(any(
+    all(feature = "time", any(target_os = "android", target_os = "linux")),
+    all(
+        any(
+            target_os = "freebsd",
+            target_os = "illumos",
+            target_os = "linux",
+            target_os = "netbsd"
+        ),
+        feature = "time",
+        feature = "signal"
+    )
+))]
+pub(crate) mod timer {
+    use crate::sys::time::{zero_init_timespec, TimeSpec};
+    use bitflags::bitflags;
+
+    #[derive(Debug, Clone, Copy)]
+    pub(crate) struct TimerSpec(libc::itimerspec);
+
+    impl TimerSpec {
+        pub const fn none() -> Self {
+            Self(libc::itimerspec {
+                it_interval: zero_init_timespec(),
+                it_value: zero_init_timespec(),
+            })
+        }
+    }
+
+    impl AsMut<libc::itimerspec> for TimerSpec {
+        fn as_mut(&mut self) -> &mut libc::itimerspec {
+            &mut self.0
+        }
+    }
+
+    impl AsRef<libc::itimerspec> for TimerSpec {
+        fn as_ref(&self) -> &libc::itimerspec {
+            &self.0
+        }
+    }
+
+    impl From<Expiration> for TimerSpec {
+        fn from(expiration: Expiration) -> TimerSpec {
+            match expiration {
+                Expiration::OneShot(t) => TimerSpec(libc::itimerspec {
+                    it_interval: zero_init_timespec(),
+                    it_value: *t.as_ref(),
+                }),
+                Expiration::IntervalDelayed(start, interval) => {
+                    TimerSpec(libc::itimerspec {
+                        it_interval: *interval.as_ref(),
+                        it_value: *start.as_ref(),
+                    })
+                }
+                Expiration::Interval(t) => TimerSpec(libc::itimerspec {
+                    it_interval: *t.as_ref(),
+                    it_value: *t.as_ref(),
+                }),
+            }
+        }
+    }
+
+    /// An enumeration allowing the definition of the expiration time of an alarm,
+    /// recurring or not.
+    #[derive(Debug, Clone, Copy, Eq, PartialEq)]
+    pub enum Expiration {
+        /// Alarm will trigger once after the time given in `TimeSpec`
+        OneShot(TimeSpec),
+        /// Alarm will trigger after a specified delay and then every interval of
+        /// time.
+        IntervalDelayed(TimeSpec, TimeSpec),
+        /// Alarm will trigger every specified interval of time.
+        Interval(TimeSpec),
+    }
+
+    #[cfg(any(target_os = "android", target_os = "linux"))]
+    bitflags! {
+        /// Flags that are used for arming the timer.
+        pub struct TimerSetTimeFlags: libc::c_int {
+            const TFD_TIMER_ABSTIME = libc::TFD_TIMER_ABSTIME;
+        }
+    }
+    #[cfg(any(
+        target_os = "freebsd",
+        target_os = "netbsd",
+        target_os = "dragonfly",
+        target_os = "illumos"
+    ))]
+    bitflags! {
+        /// Flags that are used for arming the timer.
+        pub struct TimerSetTimeFlags: libc::c_int {
+            const TFD_TIMER_ABSTIME = libc::TIMER_ABSTIME;
+        }
+    }
+
+    impl From<TimerSpec> for Expiration {
+        fn from(timerspec: TimerSpec) -> Expiration {
+            match timerspec {
+                TimerSpec(libc::itimerspec {
+                    it_interval:
+                        libc::timespec {
+                            tv_sec: 0,
+                            tv_nsec: 0,
+                            ..
+                        },
+                    it_value: ts,
+                }) => Expiration::OneShot(ts.into()),
+                TimerSpec(libc::itimerspec {
+                    it_interval: int_ts,
+                    it_value: val_ts,
+                }) => {
+                    if (int_ts.tv_sec == val_ts.tv_sec)
+                        && (int_ts.tv_nsec == val_ts.tv_nsec)
+                    {
+                        Expiration::Interval(int_ts.into())
+                    } else {
+                        Expiration::IntervalDelayed(
+                            val_ts.into(),
+                            int_ts.into(),
+                        )
+                    }
+                }
+            }
+        }
+    }
+}
 
 pub trait TimeValLike: Sized {
     #[inline]
@@ -13,14 +148,16 @@
 
     #[inline]
     fn hours(hours: i64) -> Self {
-        let secs = hours.checked_mul(SECS_PER_HOUR)
+        let secs = hours
+            .checked_mul(SECS_PER_HOUR)
             .expect("TimeValLike::hours ouf of bounds");
         Self::seconds(secs)
     }
 
     #[inline]
     fn minutes(minutes: i64) -> Self {
-        let secs = minutes.checked_mul(SECS_PER_MINUTE)
+        let secs = minutes
+            .checked_mul(SECS_PER_MINUTE)
             .expect("TimeValLike::minutes out of bounds");
         Self::seconds(secs)
     }
@@ -55,10 +192,10 @@
 const SECS_PER_HOUR: i64 = 3600;
 
 #[cfg(target_pointer_width = "64")]
-const TS_MAX_SECONDS: i64 = (::std::i64::MAX / NANOS_PER_SEC) - 1;
+const TS_MAX_SECONDS: i64 = (i64::MAX / NANOS_PER_SEC) - 1;
 
 #[cfg(target_pointer_width = "32")]
-const TS_MAX_SECONDS: i64 = ::std::isize::MAX as i64;
+const TS_MAX_SECONDS: i64 = isize::MAX as i64;
 
 const TS_MIN_SECONDS: i64 = -TS_MAX_SECONDS;
 
@@ -119,16 +256,23 @@
 
 impl TimeValLike for TimeSpec {
     #[inline]
+    #[cfg_attr(target_env = "musl", allow(deprecated))]
+    // https://github.com/rust-lang/libc/issues/1848
     fn seconds(seconds: i64) -> TimeSpec {
-        assert!(seconds >= TS_MIN_SECONDS && seconds <= TS_MAX_SECONDS,
-                "TimeSpec out of bounds; seconds={}", seconds);
-        #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
-        TimeSpec(timespec {tv_sec: seconds as time_t, tv_nsec: 0 })
+        assert!(
+            (TS_MIN_SECONDS..=TS_MAX_SECONDS).contains(&seconds),
+            "TimeSpec out of bounds; seconds={}",
+            seconds
+        );
+        let mut ts = zero_init_timespec();
+        ts.tv_sec = seconds as time_t;
+        TimeSpec(ts)
     }
 
     #[inline]
     fn milliseconds(milliseconds: i64) -> TimeSpec {
-        let nanoseconds = milliseconds.checked_mul(1_000_000)
+        let nanoseconds = milliseconds
+            .checked_mul(1_000_000)
             .expect("TimeSpec::milliseconds out of bounds");
 
         TimeSpec::nanoseconds(nanoseconds)
@@ -137,7 +281,8 @@
     /// Makes a new `TimeSpec` with given number of microseconds.
     #[inline]
     fn microseconds(microseconds: i64) -> TimeSpec {
-        let nanoseconds = microseconds.checked_mul(1_000)
+        let nanoseconds = microseconds
+            .checked_mul(1_000)
             .expect("TimeSpec::milliseconds out of bounds");
 
         TimeSpec::nanoseconds(nanoseconds)
@@ -145,15 +290,22 @@
 
     /// Makes a new `TimeSpec` with given number of nanoseconds.
     #[inline]
+    #[cfg_attr(target_env = "musl", allow(deprecated))]
+    // https://github.com/rust-lang/libc/issues/1848
     fn nanoseconds(nanoseconds: i64) -> TimeSpec {
         let (secs, nanos) = div_mod_floor_64(nanoseconds, NANOS_PER_SEC);
-        assert!(secs >= TS_MIN_SECONDS && secs <= TS_MAX_SECONDS,
-                "TimeSpec out of bounds");
-        #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
-        TimeSpec(timespec {tv_sec: secs as time_t,
-                           tv_nsec: nanos as timespec_tv_nsec_t })
+        assert!(
+            (TS_MIN_SECONDS..=TS_MAX_SECONDS).contains(&secs),
+            "TimeSpec out of bounds"
+        );
+        let mut ts = zero_init_timespec();
+        ts.tv_sec = secs as time_t;
+        ts.tv_nsec = nanos as timespec_tv_nsec_t;
+        TimeSpec(ts)
     }
 
+    // The cast is not unnecessary on all platforms.
+    #[allow(clippy::unnecessary_cast)]
     fn num_seconds(&self) -> i64 {
         if self.tv_sec() < 0 && self.tv_nsec() > 0 {
             (self.tv_sec() + 1) as i64
@@ -167,9 +319,11 @@
     }
 
     fn num_microseconds(&self) -> i64 {
-        self.num_nanoseconds() / 1_000_000_000
+        self.num_nanoseconds() / 1_000
     }
 
+    // The cast is not unnecessary on all platforms.
+    #[allow(clippy::unnecessary_cast)]
     fn num_nanoseconds(&self) -> i64 {
         let secs = self.num_seconds() * 1_000_000_000;
         let nsec = self.nanos_mod_sec();
@@ -178,6 +332,15 @@
 }
 
 impl TimeSpec {
+    /// Construct a new `TimeSpec` from its components
+    #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
+    pub const fn new(seconds: time_t, nanoseconds: timespec_tv_nsec_t) -> Self {
+        let mut ts = zero_init_timespec();
+        ts.tv_sec = seconds;
+        ts.tv_nsec = nanoseconds;
+        Self(ts)
+    }
+
     fn nanos_mod_sec(&self) -> timespec_tv_nsec_t {
         if self.tv_sec() < 0 && self.tv_nsec() > 0 {
             self.tv_nsec() - NANOS_PER_SEC as timespec_tv_nsec_t
@@ -195,12 +358,13 @@
         self.0.tv_nsec
     }
 
+    #[cfg_attr(target_env = "musl", allow(deprecated))]
+    // https://github.com/rust-lang/libc/issues/1848
     pub const fn from_duration(duration: Duration) -> Self {
-        #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
-        TimeSpec(timespec {
-            tv_sec: duration.as_secs() as time_t,
-            tv_nsec: duration.subsec_nanos() as timespec_tv_nsec_t
-        })
+        let mut ts = zero_init_timespec();
+        ts.tv_sec = duration.as_secs() as time_t;
+        ts.tv_nsec = duration.subsec_nanos() as timespec_tv_nsec_t;
+        TimeSpec(ts)
     }
 
     pub const fn from_timespec(timespec: timespec) -> Self {
@@ -220,8 +384,7 @@
     type Output = TimeSpec;
 
     fn add(self, rhs: TimeSpec) -> TimeSpec {
-        TimeSpec::nanoseconds(
-            self.num_nanoseconds() + rhs.num_nanoseconds())
+        TimeSpec::nanoseconds(self.num_nanoseconds() + rhs.num_nanoseconds())
     }
 }
 
@@ -229,8 +392,7 @@
     type Output = TimeSpec;
 
     fn sub(self, rhs: TimeSpec) -> TimeSpec {
-        TimeSpec::nanoseconds(
-            self.num_nanoseconds() - rhs.num_nanoseconds())
+        TimeSpec::nanoseconds(self.num_nanoseconds() - rhs.num_nanoseconds())
     }
 }
 
@@ -238,7 +400,9 @@
     type Output = TimeSpec;
 
     fn mul(self, rhs: i32) -> TimeSpec {
-        let usec = self.num_nanoseconds().checked_mul(i64::from(rhs))
+        let usec = self
+            .num_nanoseconds()
+            .checked_mul(i64::from(rhs))
             .expect("TimeSpec multiply out of bounds");
 
         TimeSpec::nanoseconds(usec)
@@ -284,8 +448,6 @@
     }
 }
 
-
-
 #[repr(transparent)]
 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
 pub struct TimeVal(timeval);
@@ -293,10 +455,10 @@
 const MICROS_PER_SEC: i64 = 1_000_000;
 
 #[cfg(target_pointer_width = "64")]
-const TV_MAX_SECONDS: i64 = (::std::i64::MAX / MICROS_PER_SEC) - 1;
+const TV_MAX_SECONDS: i64 = (i64::MAX / MICROS_PER_SEC) - 1;
 
 #[cfg(target_pointer_width = "32")]
-const TV_MAX_SECONDS: i64 = ::std::isize::MAX as i64;
+const TV_MAX_SECONDS: i64 = isize::MAX as i64;
 
 const TV_MIN_SECONDS: i64 = -TV_MAX_SECONDS;
 
@@ -333,15 +495,23 @@
 impl TimeValLike for TimeVal {
     #[inline]
     fn seconds(seconds: i64) -> TimeVal {
-        assert!(seconds >= TV_MIN_SECONDS && seconds <= TV_MAX_SECONDS,
-                "TimeVal out of bounds; seconds={}", seconds);
-        #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
-        TimeVal(timeval {tv_sec: seconds as time_t, tv_usec: 0 })
+        assert!(
+            (TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&seconds),
+            "TimeVal out of bounds; seconds={}",
+            seconds
+        );
+        #[cfg_attr(target_env = "musl", allow(deprecated))]
+        // https://github.com/rust-lang/libc/issues/1848
+        TimeVal(timeval {
+            tv_sec: seconds as time_t,
+            tv_usec: 0,
+        })
     }
 
     #[inline]
     fn milliseconds(milliseconds: i64) -> TimeVal {
-        let microseconds = milliseconds.checked_mul(1_000)
+        let microseconds = milliseconds
+            .checked_mul(1_000)
             .expect("TimeVal::milliseconds out of bounds");
 
         TimeVal::microseconds(microseconds)
@@ -351,11 +521,16 @@
     #[inline]
     fn microseconds(microseconds: i64) -> TimeVal {
         let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC);
-        assert!(secs >= TV_MIN_SECONDS && secs <= TV_MAX_SECONDS,
-                "TimeVal out of bounds");
-        #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
-        TimeVal(timeval {tv_sec: secs as time_t,
-                           tv_usec: micros as suseconds_t })
+        assert!(
+            (TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&secs),
+            "TimeVal out of bounds"
+        );
+        #[cfg_attr(target_env = "musl", allow(deprecated))]
+        // https://github.com/rust-lang/libc/issues/1848
+        TimeVal(timeval {
+            tv_sec: secs as time_t,
+            tv_usec: micros as suseconds_t,
+        })
     }
 
     /// Makes a new `TimeVal` with given number of nanoseconds.  Some precision
@@ -364,13 +539,20 @@
     fn nanoseconds(nanoseconds: i64) -> TimeVal {
         let microseconds = nanoseconds / 1000;
         let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC);
-        assert!(secs >= TV_MIN_SECONDS && secs <= TV_MAX_SECONDS,
-                "TimeVal out of bounds");
-        #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
-        TimeVal(timeval {tv_sec: secs as time_t,
-                           tv_usec: micros as suseconds_t })
+        assert!(
+            (TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&secs),
+            "TimeVal out of bounds"
+        );
+        #[cfg_attr(target_env = "musl", allow(deprecated))]
+        // https://github.com/rust-lang/libc/issues/1848
+        TimeVal(timeval {
+            tv_sec: secs as time_t,
+            tv_usec: micros as suseconds_t,
+        })
     }
 
+    // The cast is not unnecessary on all platforms.
+    #[allow(clippy::unnecessary_cast)]
     fn num_seconds(&self) -> i64 {
         if self.tv_sec() < 0 && self.tv_usec() > 0 {
             (self.tv_sec() + 1) as i64
@@ -383,6 +565,8 @@
         self.num_microseconds() / 1_000
     }
 
+    // The cast is not unnecessary on all platforms.
+    #[allow(clippy::unnecessary_cast)]
     fn num_microseconds(&self) -> i64 {
         let secs = self.num_seconds() * 1_000_000;
         let usec = self.micros_mod_sec();
@@ -395,6 +579,15 @@
 }
 
 impl TimeVal {
+    /// Construct a new `TimeVal` from its components
+    #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
+    pub const fn new(seconds: time_t, microseconds: suseconds_t) -> Self {
+        Self(timeval {
+            tv_sec: seconds,
+            tv_usec: microseconds,
+        })
+    }
+
     fn micros_mod_sec(&self) -> suseconds_t {
         if self.tv_sec() < 0 && self.tv_usec() > 0 {
             self.tv_usec() - MICROS_PER_SEC as suseconds_t
@@ -425,8 +618,7 @@
     type Output = TimeVal;
 
     fn add(self, rhs: TimeVal) -> TimeVal {
-        TimeVal::microseconds(
-            self.num_microseconds() + rhs.num_microseconds())
+        TimeVal::microseconds(self.num_microseconds() + rhs.num_microseconds())
     }
 }
 
@@ -434,8 +626,7 @@
     type Output = TimeVal;
 
     fn sub(self, rhs: TimeVal) -> TimeVal {
-        TimeVal::microseconds(
-            self.num_microseconds() - rhs.num_microseconds())
+        TimeVal::microseconds(self.num_microseconds() - rhs.num_microseconds())
     }
 }
 
@@ -443,7 +634,9 @@
     type Output = TimeVal;
 
     fn mul(self, rhs: i32) -> TimeVal {
-        let usec = self.num_microseconds().checked_mul(i64::from(rhs))
+        let usec = self
+            .num_microseconds()
+            .checked_mul(i64::from(rhs))
             .expect("TimeVal multiply out of bounds");
 
         TimeVal::microseconds(usec)
@@ -501,18 +694,16 @@
 #[inline]
 fn div_floor_64(this: i64, other: i64) -> i64 {
     match div_rem_64(this, other) {
-        (d, r) if (r > 0 && other < 0)
-               || (r < 0 && other > 0) => d - 1,
-        (d, _)                         => d,
+        (d, r) if (r > 0 && other < 0) || (r < 0 && other > 0) => d - 1,
+        (d, _) => d,
     }
 }
 
 #[inline]
 fn mod_floor_64(this: i64, other: i64) -> i64 {
     match this % other {
-        r if (r > 0 && other < 0)
-          || (r < 0 && other > 0) => r + other,
-        r                         => r,
+        r if (r > 0 && other < 0) || (r < 0 && other > 0) => r + other,
+        r => r,
     }
 }
 
@@ -528,11 +719,15 @@
 
     #[test]
     pub fn test_timespec() {
-        assert!(TimeSpec::seconds(1) != TimeSpec::zero());
-        assert_eq!(TimeSpec::seconds(1) + TimeSpec::seconds(2),
-                   TimeSpec::seconds(3));
-        assert_eq!(TimeSpec::minutes(3) + TimeSpec::seconds(2),
-                   TimeSpec::seconds(182));
+        assert_ne!(TimeSpec::seconds(1), TimeSpec::zero());
+        assert_eq!(
+            TimeSpec::seconds(1) + TimeSpec::seconds(2),
+            TimeSpec::seconds(3)
+        );
+        assert_eq!(
+            TimeSpec::minutes(3) + TimeSpec::seconds(2),
+            TimeSpec::seconds(182)
+        );
     }
 
     #[test]
@@ -554,7 +749,7 @@
 
     #[test]
     pub fn test_timespec_ord() {
-        assert!(TimeSpec::seconds(1) == TimeSpec::nanoseconds(1_000_000_000));
+        assert_eq!(TimeSpec::seconds(1), TimeSpec::nanoseconds(1_000_000_000));
         assert!(TimeSpec::seconds(1) < TimeSpec::nanoseconds(1_000_000_001));
         assert!(TimeSpec::seconds(1) > TimeSpec::nanoseconds(999_999_999));
         assert!(TimeSpec::seconds(-1) < TimeSpec::nanoseconds(-999_999_999));
@@ -567,22 +762,29 @@
         assert_eq!(TimeSpec::seconds(42).to_string(), "42 seconds");
         assert_eq!(TimeSpec::milliseconds(42).to_string(), "0.042 seconds");
         assert_eq!(TimeSpec::microseconds(42).to_string(), "0.000042 seconds");
-        assert_eq!(TimeSpec::nanoseconds(42).to_string(), "0.000000042 seconds");
+        assert_eq!(
+            TimeSpec::nanoseconds(42).to_string(),
+            "0.000000042 seconds"
+        );
         assert_eq!(TimeSpec::seconds(-86401).to_string(), "-86401 seconds");
     }
 
     #[test]
     pub fn test_timeval() {
-        assert!(TimeVal::seconds(1) != TimeVal::zero());
-        assert_eq!(TimeVal::seconds(1) + TimeVal::seconds(2),
-                   TimeVal::seconds(3));
-        assert_eq!(TimeVal::minutes(3) + TimeVal::seconds(2),
-                   TimeVal::seconds(182));
+        assert_ne!(TimeVal::seconds(1), TimeVal::zero());
+        assert_eq!(
+            TimeVal::seconds(1) + TimeVal::seconds(2),
+            TimeVal::seconds(3)
+        );
+        assert_eq!(
+            TimeVal::minutes(3) + TimeVal::seconds(2),
+            TimeVal::seconds(182)
+        );
     }
 
     #[test]
     pub fn test_timeval_ord() {
-        assert!(TimeVal::seconds(1) == TimeVal::microseconds(1_000_000));
+        assert_eq!(TimeVal::seconds(1), TimeVal::microseconds(1_000_000));
         assert!(TimeVal::seconds(1) < TimeVal::microseconds(1_000_001));
         assert!(TimeVal::seconds(1) > TimeVal::microseconds(999_999));
         assert!(TimeVal::seconds(-1) < TimeVal::microseconds(-999_999));
diff --git a/src/sys/timer.rs b/src/sys/timer.rs
new file mode 100644
index 0000000..e1a3405
--- /dev/null
+++ b/src/sys/timer.rs
@@ -0,0 +1,187 @@
+//! Timer API via signals.
+//!
+//! Timer is a POSIX API to create timers and get expiration notifications
+//! through queued Unix signals, for the current process. This is similar to
+//! Linux's timerfd mechanism, except that API is specific to Linux and makes
+//! use of file polling.
+//!
+//! For more documentation, please read [timer_create](https://pubs.opengroup.org/onlinepubs/9699919799/functions/timer_create.html).
+//!
+//! # Examples
+//!
+//! Create an interval timer that signals SIGALARM every 250 milliseconds.
+//!
+//! ```no_run
+//! use nix::sys::signal::{self, SigEvent, SigHandler, SigevNotify, Signal};
+//! use nix::sys::timer::{Expiration, Timer, TimerSetTimeFlags};
+//! use nix::time::ClockId;
+//! use std::convert::TryFrom;
+//! use std::sync::atomic::{AtomicU64, Ordering};
+//! use std::thread::yield_now;
+//! use std::time::Duration;
+//!
+//! const SIG: Signal = Signal::SIGALRM;
+//! static ALARMS: AtomicU64 = AtomicU64::new(0);
+//!
+//! extern "C" fn handle_alarm(signal: libc::c_int) {
+//!     let signal = Signal::try_from(signal).unwrap();
+//!     if signal == SIG {
+//!         ALARMS.fetch_add(1, Ordering::Relaxed);
+//!     }
+//! }
+//!
+//! fn main() {
+//!     let clockid = ClockId::CLOCK_MONOTONIC;
+//!     let sigevent = SigEvent::new(SigevNotify::SigevSignal {
+//!         signal: SIG,
+//!         si_value: 0,
+//!     });
+//!
+//!     let mut timer = Timer::new(clockid, sigevent).unwrap();
+//!     let expiration = Expiration::Interval(Duration::from_millis(250).into());
+//!     let flags = TimerSetTimeFlags::empty();
+//!     timer.set(expiration, flags).expect("could not set timer");
+//!
+//!     let handler = SigHandler::Handler(handle_alarm);
+//!     unsafe { signal::signal(SIG, handler) }.unwrap();
+//!
+//!     loop {
+//!         let alarms = ALARMS.load(Ordering::Relaxed);
+//!         if alarms >= 10 {
+//!             println!("total alarms handled: {}", alarms);
+//!             break;
+//!         }
+//!         yield_now()
+//!     }
+//! }
+//! ```
+use crate::sys::signal::SigEvent;
+use crate::sys::time::timer::TimerSpec;
+pub use crate::sys::time::timer::{Expiration, TimerSetTimeFlags};
+use crate::time::ClockId;
+use crate::{errno::Errno, Result};
+use core::mem;
+
+/// A Unix signal per-process timer.
+#[derive(Debug)]
+#[repr(transparent)]
+pub struct Timer(libc::timer_t);
+
+impl Timer {
+    /// Creates a new timer based on the clock defined by `clockid`. The details
+    /// of the signal and its handler are defined by the passed `sigevent`.
+    #[doc(alias("timer_create"))]
+    pub fn new(clockid: ClockId, mut sigevent: SigEvent) -> Result<Self> {
+        let mut timer_id: mem::MaybeUninit<libc::timer_t> =
+            mem::MaybeUninit::uninit();
+        Errno::result(unsafe {
+            libc::timer_create(
+                clockid.as_raw(),
+                sigevent.as_mut_ptr(),
+                timer_id.as_mut_ptr(),
+            )
+        })
+        .map(|_| {
+            // SAFETY: libc::timer_create is responsible for initializing
+            // timer_id.
+            unsafe { Self(timer_id.assume_init()) }
+        })
+    }
+
+    /// Set a new alarm on the timer.
+    ///
+    /// # Types of alarm
+    ///
+    /// There are 3 types of alarms you can set:
+    ///
+    ///   - one shot: the alarm will trigger once after the specified amount of
+    /// time.
+    ///     Example: I want an alarm to go off in 60s and then disable itself.
+    ///
+    ///   - interval: the alarm will trigger every specified interval of time.
+    ///     Example: I want an alarm to go off every 60s. The alarm will first
+    ///     go off 60s after I set it and every 60s after that. The alarm will
+    ///     not disable itself.
+    ///
+    ///   - interval delayed: the alarm will trigger after a certain amount of
+    ///     time and then trigger at a specified interval.
+    ///     Example: I want an alarm to go off every 60s but only start in 1h.
+    ///     The alarm will first trigger 1h after I set it and then every 60s
+    ///     after that. The alarm will not disable itself.
+    ///
+    /// # Relative vs absolute alarm
+    ///
+    /// If you do not set any `TimerSetTimeFlags`, then the `TimeSpec` you pass
+    /// to the `Expiration` you want is relative. If however you want an alarm
+    /// to go off at a certain point in time, you can set `TFD_TIMER_ABSTIME`.
+    /// Then the one shot TimeSpec and the delay TimeSpec of the delayed
+    /// interval are going to be interpreted as absolute.
+    ///
+    /// # Disabling alarms
+    ///
+    /// Note: Only one alarm can be set for any given timer. Setting a new alarm
+    /// actually removes the previous one.
+    ///
+    /// Note: Setting a one shot alarm with a 0s TimeSpec disable the alarm
+    /// altogether.
+    #[doc(alias("timer_settime"))]
+    pub fn set(
+        &mut self,
+        expiration: Expiration,
+        flags: TimerSetTimeFlags,
+    ) -> Result<()> {
+        let timerspec: TimerSpec = expiration.into();
+        Errno::result(unsafe {
+            libc::timer_settime(
+                self.0,
+                flags.bits(),
+                timerspec.as_ref(),
+                core::ptr::null_mut(),
+            )
+        })
+        .map(drop)
+    }
+
+    /// Get the parameters for the alarm currently set, if any.
+    #[doc(alias("timer_gettime"))]
+    pub fn get(&self) -> Result<Option<Expiration>> {
+        let mut timerspec = TimerSpec::none();
+        Errno::result(unsafe {
+            libc::timer_gettime(self.0, timerspec.as_mut())
+        })
+        .map(|_| {
+            if timerspec.as_ref().it_interval.tv_sec == 0
+                && timerspec.as_ref().it_interval.tv_nsec == 0
+                && timerspec.as_ref().it_value.tv_sec == 0
+                && timerspec.as_ref().it_value.tv_nsec == 0
+            {
+                None
+            } else {
+                Some(timerspec.into())
+            }
+        })
+    }
+
+    /// Return the number of timers that have overrun
+    ///
+    /// Each timer is able to queue one signal to the process at a time, meaning
+    /// if the signal is not handled before the next expiration the timer has
+    /// 'overrun'. This function returns how many times that has happened to
+    /// this timer, up to `libc::DELAYTIMER_MAX`. If more than the maximum
+    /// number of overruns have happened the return is capped to the maximum.
+    #[doc(alias("timer_getoverrun"))]
+    pub fn overruns(&self) -> i32 {
+        unsafe { libc::timer_getoverrun(self.0) }
+    }
+}
+
+impl Drop for Timer {
+    fn drop(&mut self) {
+        if !std::thread::panicking() {
+            let result = Errno::result(unsafe { libc::timer_delete(self.0) });
+            if let Err(Errno::EINVAL) = result {
+                panic!("close of Timer encountered EINVAL");
+            }
+        }
+    }
+}
diff --git a/src/sys/timerfd.rs b/src/sys/timerfd.rs
index 705a3c4..a35fc92 100644
--- a/src/sys/timerfd.rs
+++ b/src/sys/timerfd.rs
@@ -28,10 +28,10 @@
 //! // We wait for the timer to expire.
 //! timer.wait().unwrap();
 //! ```
-use crate::sys::time::TimeSpec;
+use crate::sys::time::timer::TimerSpec;
+pub use crate::sys::time::timer::{Expiration, TimerSetTimeFlags};
 use crate::unistd::read;
 use crate::{errno::Errno, Result};
-use bitflags::bitflags;
 use libc::c_int;
 use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
 
@@ -60,10 +60,19 @@
     #[repr(i32)]
     #[non_exhaustive]
     pub enum ClockId {
+        /// A settable system-wide real-time clock.
         CLOCK_REALTIME,
+        /// A non-settable monotonically increasing clock.
+        ///
+        /// Does not change after system startup.
+        /// Does not measure time while the system is suspended.
         CLOCK_MONOTONIC,
+        /// Like `CLOCK_MONOTONIC`, except that `CLOCK_BOOTTIME` includes the time
+        /// that the system was suspended.
         CLOCK_BOOTTIME,
+        /// Like `CLOCK_REALTIME`, but will wake the system if it is suspended.
         CLOCK_REALTIME_ALARM,
+        /// Like `CLOCK_BOOTTIME`, but will wake the system if it is suspended.
         CLOCK_BOOTTIME_ALARM,
     }
 }
@@ -72,105 +81,23 @@
     /// Additional flags to change the behaviour of the file descriptor at the
     /// time of creation.
     pub struct TimerFlags: c_int {
+        /// Set the `O_NONBLOCK` flag on the open file description referred to by the new file descriptor.
         TFD_NONBLOCK;
+        /// Set the `FD_CLOEXEC` flag on the file descriptor.
         TFD_CLOEXEC;
     }
 }
 
-bitflags! {
-    /// Flags that are used for arming the timer.
-    pub struct TimerSetTimeFlags: libc::c_int {
-        const TFD_TIMER_ABSTIME = libc::TFD_TIMER_ABSTIME;
-    }
-}
-
-#[derive(Debug, Clone, Copy)]
-struct TimerSpec(libc::itimerspec);
-
-impl TimerSpec {
-    pub const fn none() -> Self {
-        Self(libc::itimerspec {
-            it_interval: libc::timespec {
-                tv_sec: 0,
-                tv_nsec: 0,
-            },
-            it_value: libc::timespec {
-                tv_sec: 0,
-                tv_nsec: 0,
-            },
-        })
-    }
-}
-
-impl AsRef<libc::itimerspec> for TimerSpec {
-    fn as_ref(&self) -> &libc::itimerspec {
-        &self.0
-    }
-}
-
-impl From<Expiration> for TimerSpec {
-    fn from(expiration: Expiration) -> TimerSpec {
-        match expiration {
-            Expiration::OneShot(t) => TimerSpec(libc::itimerspec {
-                it_interval: libc::timespec {
-                    tv_sec: 0,
-                    tv_nsec: 0,
-                },
-                it_value: *t.as_ref(),
-            }),
-            Expiration::IntervalDelayed(start, interval) => TimerSpec(libc::itimerspec {
-                it_interval: *interval.as_ref(),
-                it_value: *start.as_ref(),
-            }),
-            Expiration::Interval(t) => TimerSpec(libc::itimerspec {
-                it_interval: *t.as_ref(),
-                it_value: *t.as_ref(),
-            }),
-        }
-    }
-}
-
-impl From<TimerSpec> for Expiration {
-    fn from(timerspec: TimerSpec) -> Expiration {
-        match timerspec {
-            TimerSpec(libc::itimerspec {
-                it_interval:
-                    libc::timespec {
-                        tv_sec: 0,
-                        tv_nsec: 0,
-                    },
-                it_value: ts,
-            }) => Expiration::OneShot(ts.into()),
-            TimerSpec(libc::itimerspec {
-                it_interval: int_ts,
-                it_value: val_ts,
-            }) => {
-                if (int_ts.tv_sec == val_ts.tv_sec) && (int_ts.tv_nsec == val_ts.tv_nsec) {
-                    Expiration::Interval(int_ts.into())
-                } else {
-                    Expiration::IntervalDelayed(val_ts.into(), int_ts.into())
-                }
-            }
-        }
-    }
-}
-
-/// An enumeration allowing the definition of the expiration time of an alarm,
-/// recurring or not.
-#[derive(Debug, Clone, Copy, PartialEq)]
-pub enum Expiration {
-    OneShot(TimeSpec),
-    IntervalDelayed(TimeSpec, TimeSpec),
-    Interval(TimeSpec),
-}
-
 impl TimerFd {
     /// Creates a new timer based on the clock defined by `clockid`. The
     /// underlying fd can be assigned specific flags with `flags` (CLOEXEC,
     /// NONBLOCK). The underlying fd will be closed on drop.
+    #[doc(alias("timerfd_create"))]
     pub fn new(clockid: ClockId, flags: TimerFlags) -> Result<Self> {
-        Errno::result(unsafe { libc::timerfd_create(clockid as i32, flags.bits()) })
-            .map(|fd| Self { fd })
+        Errno::result(unsafe {
+            libc::timerfd_create(clockid as i32, flags.bits())
+        })
+        .map(|fd| Self { fd })
     }
 
     /// Sets a new alarm on the timer.
@@ -181,7 +108,7 @@
     ///
     ///   - one shot: the alarm will trigger once after the specified amount of
     /// time.
-    ///     Example: I want an alarm to go off in 60s and then disables itself.
+    ///     Example: I want an alarm to go off in 60s and then disable itself.
     ///
     ///   - interval: the alarm will trigger every specified interval of time.
     ///     Example: I want an alarm to go off every 60s. The alarm will first
@@ -209,7 +136,12 @@
     ///
     /// Note: Setting a one shot alarm with a 0s TimeSpec disables the alarm
     /// altogether.
-    pub fn set(&self, expiration: Expiration, flags: TimerSetTimeFlags) -> Result<()> {
+    #[doc(alias("timerfd_settime"))]
+    pub fn set(
+        &self,
+        expiration: Expiration,
+        flags: TimerSetTimeFlags,
+    ) -> Result<()> {
         let timerspec: TimerSpec = expiration.into();
         Errno::result(unsafe {
             libc::timerfd_settime(
@@ -223,15 +155,17 @@
     }
 
     /// Get the parameters for the alarm currently set, if any.
+    #[doc(alias("timerfd_gettime"))]
     pub fn get(&self) -> Result<Option<Expiration>> {
         let mut timerspec = TimerSpec::none();
-        let timerspec_ptr: *mut libc::itimerspec = &mut timerspec.0;
-
-        Errno::result(unsafe { libc::timerfd_gettime(self.fd, timerspec_ptr) }).map(|_| {
-            if timerspec.0.it_interval.tv_sec == 0
-                && timerspec.0.it_interval.tv_nsec == 0
-                && timerspec.0.it_value.tv_sec == 0
-                && timerspec.0.it_value.tv_nsec == 0
+        Errno::result(unsafe {
+            libc::timerfd_gettime(self.fd, timerspec.as_mut())
+        })
+        .map(|_| {
+            if timerspec.as_ref().it_interval.tv_sec == 0
+                && timerspec.as_ref().it_interval.tv_nsec == 0
+                && timerspec.as_ref().it_value.tv_sec == 0
+                && timerspec.as_ref().it_value.tv_nsec == 0
             {
                 None
             } else {
@@ -241,6 +175,7 @@
     }
 
     /// Remove the alarm if any is set.
+    #[doc(alias("timerfd_settime"))]
     pub fn unset(&self) -> Result<()> {
         Errno::result(unsafe {
             libc::timerfd_settime(
@@ -259,7 +194,7 @@
     pub fn wait(&self) -> Result<()> {
         while let Err(e) = read(self.fd, &mut [0u8; 8]) {
             if e != Errno::EINTR {
-                return Err(e)
+                return Err(e);
             }
         }
 
@@ -270,9 +205,7 @@
 impl Drop for TimerFd {
     fn drop(&mut self) {
         if !std::thread::panicking() {
-            let result = Errno::result(unsafe {
-                libc::close(self.fd)
-            });
+            let result = Errno::result(unsafe { libc::close(self.fd) });
             if let Err(Errno::EBADF) = result {
                 panic!("close of TimerFd encountered EBADF");
             }
diff --git a/src/sys/uio.rs b/src/sys/uio.rs
index 3abcde2..b31c306 100644
--- a/src/sys/uio.rs
+++ b/src/sys/uio.rs
@@ -1,16 +1,26 @@
 //! Vectored I/O
 
-use crate::Result;
 use crate::errno::Errno;
-use libc::{self, c_int, c_void, size_t, off_t};
+use crate::Result;
+use libc::{self, c_int, c_void, off_t, size_t};
+use std::io::{IoSlice, IoSliceMut};
 use std::marker::PhantomData;
 use std::os::unix::io::RawFd;
 
 /// Low-level vectored write to a raw file descriptor
 ///
 /// See also [writev(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/writev.html)
-pub fn writev(fd: RawFd, iov: &[IoVec<&[u8]>]) -> Result<usize> {
-    let res = unsafe { libc::writev(fd, iov.as_ptr() as *const libc::iovec, iov.len() as c_int) };
+pub fn writev(fd: RawFd, iov: &[IoSlice<'_>]) -> Result<usize> {
+    // SAFETY: to quote the documentation for `IoSlice`:
+    //
+    // [IoSlice] is semantically a wrapper around a &[u8], but is
+    // guaranteed to be ABI compatible with the iovec type on Unix
+    // platforms.
+    //
+    // Because it is ABI compatible, a pointer cast here is valid
+    let res = unsafe {
+        libc::writev(fd, iov.as_ptr() as *const libc::iovec, iov.len() as c_int)
+    };
 
     Errno::result(res).map(|r| r as usize)
 }
@@ -18,8 +28,11 @@
 /// Low-level vectored read from a raw file descriptor
 ///
 /// See also [readv(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/readv.html)
-pub fn readv(fd: RawFd, iov: &mut [IoVec<&mut [u8]>]) -> Result<usize> {
-    let res = unsafe { libc::readv(fd, iov.as_ptr() as *const libc::iovec, iov.len() as c_int) };
+pub fn readv(fd: RawFd, iov: &mut [IoSliceMut<'_>]) -> Result<usize> {
+    // SAFETY: same as in writev(), IoSliceMut is ABI-compatible with iovec
+    let res = unsafe {
+        libc::readv(fd, iov.as_ptr() as *const libc::iovec, iov.len() as c_int)
+    };
 
     Errno::result(res).map(|r| r as usize)
 }
@@ -30,11 +43,20 @@
 /// or an error occurs. The file offset is not changed.
 ///
 /// See also: [`writev`](fn.writev.html) and [`pwrite`](fn.pwrite.html)
-#[cfg(not(target_os = "redox"))]
-pub fn pwritev(fd: RawFd, iov: &[IoVec<&[u8]>],
-               offset: off_t) -> Result<usize> {
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub fn pwritev(fd: RawFd, iov: &[IoSlice<'_>], offset: off_t) -> Result<usize> {
+    #[cfg(target_env = "uclibc")]
+    let offset = offset as libc::off64_t; // uclibc doesn't use off_t
+
+    // SAFETY: same as in writev()
     let res = unsafe {
-        libc::pwritev(fd, iov.as_ptr() as *const libc::iovec, iov.len() as c_int, offset)
+        libc::pwritev(
+            fd,
+            iov.as_ptr() as *const libc::iovec,
+            iov.len() as c_int,
+            offset,
+        )
     };
 
     Errno::result(res).map(|r| r as usize)
@@ -47,11 +69,24 @@
 /// changed.
 ///
 /// See also: [`readv`](fn.readv.html) and [`pread`](fn.pread.html)
-#[cfg(not(target_os = "redox"))]
-pub fn preadv(fd: RawFd, iov: &[IoVec<&mut [u8]>],
-              offset: off_t) -> Result<usize> {
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub fn preadv(
+    fd: RawFd,
+    iov: &mut [IoSliceMut<'_>],
+    offset: off_t,
+) -> Result<usize> {
+    #[cfg(target_env = "uclibc")]
+    let offset = offset as libc::off64_t; // uclibc doesn't use off_t
+
+    // SAFETY: same as in readv()
     let res = unsafe {
-        libc::preadv(fd, iov.as_ptr() as *const libc::iovec, iov.len() as c_int, offset)
+        libc::preadv(
+            fd,
+            iov.as_ptr() as *const libc::iovec,
+            iov.len() as c_int,
+            offset,
+        )
     };
 
     Errno::result(res).map(|r| r as usize)
@@ -63,21 +98,29 @@
 // TODO: move to unistd
 pub fn pwrite(fd: RawFd, buf: &[u8], offset: off_t) -> Result<usize> {
     let res = unsafe {
-        libc::pwrite(fd, buf.as_ptr() as *const c_void, buf.len() as size_t,
-                    offset)
+        libc::pwrite(
+            fd,
+            buf.as_ptr() as *const c_void,
+            buf.len() as size_t,
+            offset,
+        )
     };
 
     Errno::result(res).map(|r| r as usize)
 }
 
-/// Low-level write to a file, with specified offset.
+/// Low-level read from a file, with specified offset.
 ///
 /// See also [pread(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pread.html)
 // TODO: move to unistd
-pub fn pread(fd: RawFd, buf: &mut [u8], offset: off_t) -> Result<usize>{
+pub fn pread(fd: RawFd, buf: &mut [u8], offset: off_t) -> Result<usize> {
     let res = unsafe {
-        libc::pread(fd, buf.as_mut_ptr() as *mut c_void, buf.len() as size_t,
-                   offset)
+        libc::pread(
+            fd,
+            buf.as_mut_ptr() as *mut c_void,
+            buf.len() as size_t,
+            offset,
+        )
     };
 
     Errno::result(res).map(|r| r as usize)
@@ -86,12 +129,13 @@
 /// A slice of memory in a remote process, starting at address `base`
 /// and consisting of `len` bytes.
 ///
-/// This is the same underlying C structure as [`IoVec`](struct.IoVec.html),
+/// This is the same underlying C structure as `IoSlice`,
 /// except that it refers to memory in some other process, and is
-/// therefore not represented in Rust by an actual slice as `IoVec` is. It
+/// therefore not represented in Rust by an actual slice as `IoSlice` is. It
 /// is used with [`process_vm_readv`](fn.process_vm_readv.html)
 /// and [`process_vm_writev`](fn.process_vm_writev.html).
-#[cfg(target_os = "linux")]
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
 pub struct RemoteIoVec {
@@ -101,10 +145,84 @@
     pub len: usize,
 }
 
+/// A vector of buffers.
+///
+/// Vectored I/O methods like [`writev`] and [`readv`] use this structure for
+/// both reading and writing.  Each `IoVec` specifies the base address and
+/// length of an area in memory.
+#[deprecated(
+    since = "0.24.0",
+    note = "`IoVec` is no longer used in the public interface, use `IoSlice` or `IoSliceMut` instead"
+)]
+#[repr(transparent)]
+#[allow(renamed_and_removed_lints)]
+#[allow(clippy::unknown_clippy_lints)]
+// Clippy false positive: https://github.com/rust-lang/rust-clippy/issues/8867
+#[allow(clippy::derive_partial_eq_without_eq)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct IoVec<T>(pub(crate) libc::iovec, PhantomData<T>);
+
+#[allow(deprecated)]
+impl<T> IoVec<T> {
+    /// View the `IoVec` as a Rust slice.
+    #[deprecated(
+        since = "0.24.0",
+        note = "Use the `Deref` impl of `IoSlice` or `IoSliceMut` instead"
+    )]
+    #[inline]
+    pub fn as_slice(&self) -> &[u8] {
+        use std::slice;
+
+        unsafe {
+            slice::from_raw_parts(self.0.iov_base as *const u8, self.0.iov_len)
+        }
+    }
+}
+
+#[allow(deprecated)]
+impl<'a> IoVec<&'a [u8]> {
+    /// Create an `IoVec` from a Rust slice.
+    #[deprecated(since = "0.24.0", note = "Use `IoSlice::new` instead")]
+    pub fn from_slice(buf: &'a [u8]) -> IoVec<&'a [u8]> {
+        IoVec(
+            libc::iovec {
+                iov_base: buf.as_ptr() as *mut c_void,
+                iov_len: buf.len() as size_t,
+            },
+            PhantomData,
+        )
+    }
+}
+
+#[allow(deprecated)]
+impl<'a> IoVec<&'a mut [u8]> {
+    /// Create an `IoVec` from a mutable Rust slice.
+    #[deprecated(since = "0.24.0", note = "Use `IoSliceMut::new` instead")]
+    pub fn from_mut_slice(buf: &'a mut [u8]) -> IoVec<&'a mut [u8]> {
+        IoVec(
+            libc::iovec {
+                iov_base: buf.as_ptr() as *mut c_void,
+                iov_len: buf.len() as size_t,
+            },
+            PhantomData,
+        )
+    }
+}
+
+// The only reason IoVec isn't automatically Send+Sync is because libc::iovec
+// contains raw pointers.
+#[allow(deprecated)]
+unsafe impl<T> Send for IoVec<T> where T: Send {}
+#[allow(deprecated)]
+unsafe impl<T> Sync for IoVec<T> where T: Sync {}
+
+feature! {
+#![feature = "process"]
+
 /// Write data directly to another process's virtual memory
 /// (see [`process_vm_writev`(2)]).
 ///
-/// `local_iov` is a list of [`IoVec`]s containing the data to be written,
+/// `local_iov` is a list of [`IoSlice`]s containing the data to be written,
 /// and `remote_iov` is a list of [`RemoteIoVec`]s identifying where the
 /// data should be written in the target process. On success, returns the
 /// number of bytes written, which will always be a whole
@@ -115,16 +233,16 @@
 /// `CAP_SYS_PTRACE`), or you must be running as the same user as the
 /// target process and the OS must have unprivileged debugging enabled.
 ///
-/// This function is only available on Linux.
+/// This function is only available on Linux and Android(SDK23+).
 ///
 /// [`process_vm_writev`(2)]: https://man7.org/linux/man-pages/man2/process_vm_writev.2.html
 /// [ptrace]: ../ptrace/index.html
-/// [`IoVec`]: struct.IoVec.html
+/// [`IoSlice`]: https://doc.rust-lang.org/std/io/struct.IoSlice.html
 /// [`RemoteIoVec`]: struct.RemoteIoVec.html
-#[cfg(target_os = "linux")]
+#[cfg(all(any(target_os = "linux", target_os = "android"), not(target_env = "uclibc")))]
 pub fn process_vm_writev(
     pid: crate::unistd::Pid,
-    local_iov: &[IoVec<&[u8]>],
+    local_iov: &[IoSlice<'_>],
     remote_iov: &[RemoteIoVec]) -> Result<usize>
 {
     let res = unsafe {
@@ -139,7 +257,7 @@
 /// Read data directly from another process's virtual memory
 /// (see [`process_vm_readv`(2)]).
 ///
-/// `local_iov` is a list of [`IoVec`]s containing the buffer to copy
+/// `local_iov` is a list of [`IoSliceMut`]s containing the buffer to copy
 /// data into, and `remote_iov` is a list of [`RemoteIoVec`]s identifying
 /// where the source data is in the target process. On success,
 /// returns the number of bytes written, which will always be a whole
@@ -150,16 +268,16 @@
 /// `CAP_SYS_PTRACE`), or you must be running as the same user as the
 /// target process and the OS must have unprivileged debugging enabled.
 ///
-/// This function is only available on Linux.
+/// This function is only available on Linux and Android(SDK23+).
 ///
 /// [`process_vm_readv`(2)]: https://man7.org/linux/man-pages/man2/process_vm_readv.2.html
 /// [`ptrace`]: ../ptrace/index.html
-/// [`IoVec`]: struct.IoVec.html
+/// [`IoSliceMut`]: https://doc.rust-lang.org/std/io/struct.IoSliceMut.html
 /// [`RemoteIoVec`]: struct.RemoteIoVec.html
-#[cfg(any(target_os = "linux"))]
+#[cfg(all(any(target_os = "linux", target_os = "android"), not(target_env = "uclibc")))]
 pub fn process_vm_readv(
     pid: crate::unistd::Pid,
-    local_iov: &[IoVec<&mut [u8]>],
+    local_iov: &mut [IoSliceMut<'_>],
     remote_iov: &[RemoteIoVec]) -> Result<usize>
 {
     let res = unsafe {
@@ -170,54 +288,4 @@
 
     Errno::result(res).map(|r| r as usize)
 }
-
-/// A vector of buffers.
-///
-/// Vectored I/O methods like [`writev`] and [`readv`] use this structure for
-/// both reading and writing.  Each `IoVec` specifies the base address and
-/// length of an area in memory.
-#[repr(transparent)]
-#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
-pub struct IoVec<T>(pub(crate) libc::iovec, PhantomData<T>);
-
-impl<T> IoVec<T> {
-    /// View the `IoVec` as a Rust slice.
-    #[inline]
-    pub fn as_slice(&self) -> &[u8] {
-        use std::slice;
-
-        unsafe {
-            slice::from_raw_parts(
-                self.0.iov_base as *const u8,
-                self.0.iov_len as usize)
-        }
-    }
-}
-
-impl<'a> IoVec<&'a [u8]> {
-    #[cfg(target_os = "freebsd")]
-    pub(crate) fn from_raw_parts(base: *mut c_void, len: usize) -> Self {
-        IoVec(libc::iovec {
-            iov_base: base,
-            iov_len: len
-        }, PhantomData)
-    }
-
-    /// Create an `IoVec` from a Rust slice.
-    pub fn from_slice(buf: &'a [u8]) -> IoVec<&'a [u8]> {
-        IoVec(libc::iovec {
-            iov_base: buf.as_ptr() as *mut c_void,
-            iov_len: buf.len() as size_t,
-        }, PhantomData)
-    }
-}
-
-impl<'a> IoVec<&'a mut [u8]> {
-    /// Create an `IoVec` from a mutable Rust slice.
-    pub fn from_mut_slice(buf: &'a mut [u8]) -> IoVec<&'a mut [u8]> {
-        IoVec(libc::iovec {
-            iov_base: buf.as_ptr() as *mut c_void,
-            iov_len: buf.len() as size_t,
-        }, PhantomData)
-    }
 }
diff --git a/src/sys/utsname.rs b/src/sys/utsname.rs
index 98edee0..b48ed9f 100644
--- a/src/sys/utsname.rs
+++ b/src/sys/utsname.rs
@@ -1,8 +1,9 @@
 //! Get system identification
+use crate::{Errno, Result};
+use libc::c_char;
+use std::ffi::OsStr;
 use std::mem;
-use libc::{self, c_char};
-use std::ffi::CStr;
-use std::str::from_utf8_unchecked;
+use std::os::unix::ffi::OsStrExt;
 
 /// Describes the running system.  Return type of [`uname`].
 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
@@ -10,47 +11,56 @@
 pub struct UtsName(libc::utsname);
 
 impl UtsName {
-    /// Name of the operating system implementation
-    pub fn sysname(&self) -> &str {
-        to_str(&(&self.0.sysname as *const c_char ) as *const *const c_char)
+    /// Name of the operating system implementation.
+    pub fn sysname(&self) -> &OsStr {
+        cast_and_trim(&self.0.sysname)
     }
 
     /// Network name of this machine.
-    pub fn nodename(&self) -> &str {
-        to_str(&(&self.0.nodename as *const c_char ) as *const *const c_char)
+    pub fn nodename(&self) -> &OsStr {
+        cast_and_trim(&self.0.nodename)
     }
 
     /// Release level of the operating system.
-    pub fn release(&self) -> &str {
-        to_str(&(&self.0.release as *const c_char ) as *const *const c_char)
+    pub fn release(&self) -> &OsStr {
+        cast_and_trim(&self.0.release)
     }
 
     /// Version level of the operating system.
-    pub fn version(&self) -> &str {
-        to_str(&(&self.0.version as *const c_char ) as *const *const c_char)
+    pub fn version(&self) -> &OsStr {
+        cast_and_trim(&self.0.version)
     }
 
     /// Machine hardware platform.
-    pub fn machine(&self) -> &str {
-        to_str(&(&self.0.machine as *const c_char ) as *const *const c_char)
+    pub fn machine(&self) -> &OsStr {
+        cast_and_trim(&self.0.machine)
+    }
+
+    /// NIS or YP domain name of this machine.
+    #[cfg(any(target_os = "android", target_os = "linux"))]
+    pub fn domainname(&self) -> &OsStr {
+        cast_and_trim(&self.0.domainname)
     }
 }
 
 /// Get system identification
-pub fn uname() -> UtsName {
+pub fn uname() -> Result<UtsName> {
     unsafe {
-        let mut ret = mem::MaybeUninit::uninit();
-        libc::uname(ret.as_mut_ptr());
-        UtsName(ret.assume_init())
+        let mut ret = mem::MaybeUninit::zeroed();
+        Errno::result(libc::uname(ret.as_mut_ptr()))?;
+        Ok(UtsName(ret.assume_init()))
     }
 }
 
-#[inline]
-fn to_str<'a>(s: *const *const c_char) -> &'a str {
-    unsafe {
-        let res = CStr::from_ptr(*s).to_bytes();
-        from_utf8_unchecked(res)
-    }
+fn cast_and_trim(slice: &[c_char]) -> &OsStr {
+    let length = slice
+        .iter()
+        .position(|&byte| byte == 0)
+        .unwrap_or(slice.len());
+    let bytes =
+        unsafe { std::slice::from_raw_parts(slice.as_ptr().cast(), length) };
+
+    OsStr::from_bytes(bytes)
 }
 
 #[cfg(test)]
@@ -58,18 +68,18 @@
     #[cfg(target_os = "linux")]
     #[test]
     pub fn test_uname_linux() {
-        assert_eq!(super::uname().sysname(), "Linux");
+        assert_eq!(super::uname().unwrap().sysname(), "Linux");
     }
 
     #[cfg(any(target_os = "macos", target_os = "ios"))]
     #[test]
     pub fn test_uname_darwin() {
-        assert_eq!(super::uname().sysname(), "Darwin");
+        assert_eq!(super::uname().unwrap().sysname(), "Darwin");
     }
 
     #[cfg(target_os = "freebsd")]
     #[test]
     pub fn test_uname_freebsd() {
-        assert_eq!(super::uname().sysname(), "FreeBSD");
+        assert_eq!(super::uname().unwrap().sysname(), "FreeBSD");
     }
 }
diff --git a/src/sys/wait.rs b/src/sys/wait.rs
index ee49e37..b6524e8 100644
--- a/src/sys/wait.rs
+++ b/src/sys/wait.rs
@@ -6,6 +6,11 @@
 use cfg_if::cfg_if;
 use libc::{self, c_int};
 use std::convert::TryFrom;
+#[cfg(any(
+    target_os = "android",
+    all(target_os = "linux", not(target_env = "uclibc")),
+))]
+use std::os::unix::io::RawFd;
 
 libc_bitflags!(
     /// Controls the behavior of [`waitpid`].
@@ -27,6 +32,7 @@
                   target_os = "redox",
                   target_os = "macos",
                   target_os = "netbsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         WEXITED;
         /// Report the status of selected processes that have continued from a
         /// job control stop by receiving a
@@ -41,6 +47,7 @@
                   target_os = "redox",
                   target_os = "macos",
                   target_os = "netbsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         WSTOPPED;
         /// Don't reap, just poll status.
         #[cfg(any(target_os = "android",
@@ -51,15 +58,19 @@
                   target_os = "redox",
                   target_os = "macos",
                   target_os = "netbsd"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         WNOWAIT;
         /// Don't wait on children of other threads in this group
         #[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         __WNOTHREAD;
         /// Wait on all children, regardless of type
         #[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         __WALL;
         /// Wait for "clone" children only.
         #[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
+        #[cfg_attr(docsrs, doc(cfg(all())))]
         __WCLONE;
     }
 );
@@ -97,6 +108,7 @@
     /// [`nix::sys::ptrace`]: ../ptrace/index.html
     /// [`ptrace`(2)]: https://man7.org/linux/man-pages/man2/ptrace.2.html
     #[cfg(any(target_os = "linux", target_os = "android"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     PtraceEvent(Pid, Signal, c_int),
     /// The traced process was stopped by execution of a system call,
     /// and `PTRACE_O_TRACESYSGOOD` is in effect. See [`ptrace`(2)] for
@@ -104,6 +116,7 @@
     ///
     /// [`ptrace`(2)]: https://man7.org/linux/man-pages/man2/ptrace.2.html
     #[cfg(any(target_os = "linux", target_os = "android"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     PtraceSyscall(Pid),
     /// The process was previously stopped but has resumed execution
     /// after receiving a `SIGCONT` signal. This is only reported if
@@ -122,7 +135,9 @@
     pub fn pid(&self) -> Option<Pid> {
         use self::WaitStatus::*;
         match *self {
-            Exited(p, _) | Signaled(p, _, _) | Stopped(p, _) | Continued(p) => Some(p),
+            Exited(p, _) | Signaled(p, _, _) | Stopped(p, _) | Continued(p) => {
+                Some(p)
+            }
             StillAlive => None,
             #[cfg(any(target_os = "android", target_os = "linux"))]
             PtraceEvent(p, _, _) | PtraceSyscall(p) => Some(p),
@@ -225,12 +240,72 @@
             WaitStatus::Continued(pid)
         })
     }
+
+    /// Convert a `siginfo_t` as returned by `waitid` to a `WaitStatus`
+    ///
+    /// # Errors
+    ///
+    /// Returns an `Error` corresponding to `EINVAL` for invalid values.
+    ///
+    /// # Safety
+    ///
+    /// siginfo_t is actually a union, not all fields may be initialized.
+    /// The functions si_pid() and si_status() must be valid to call on
+    /// the passed siginfo_t.
+    #[cfg(any(
+        target_os = "android",
+        target_os = "freebsd",
+        target_os = "haiku",
+        all(target_os = "linux", not(target_env = "uclibc")),
+    ))]
+    unsafe fn from_siginfo(siginfo: &libc::siginfo_t) -> Result<WaitStatus> {
+        let si_pid = siginfo.si_pid();
+        if si_pid == 0 {
+            return Ok(WaitStatus::StillAlive);
+        }
+
+        assert_eq!(siginfo.si_signo, libc::SIGCHLD);
+
+        let pid = Pid::from_raw(si_pid);
+        let si_status = siginfo.si_status();
+
+        let status = match siginfo.si_code {
+            libc::CLD_EXITED => WaitStatus::Exited(pid, si_status),
+            libc::CLD_KILLED | libc::CLD_DUMPED => WaitStatus::Signaled(
+                pid,
+                Signal::try_from(si_status)?,
+                siginfo.si_code == libc::CLD_DUMPED,
+            ),
+            libc::CLD_STOPPED => {
+                WaitStatus::Stopped(pid, Signal::try_from(si_status)?)
+            }
+            libc::CLD_CONTINUED => WaitStatus::Continued(pid),
+            #[cfg(any(target_os = "android", target_os = "linux"))]
+            libc::CLD_TRAPPED => {
+                if si_status == libc::SIGTRAP | 0x80 {
+                    WaitStatus::PtraceSyscall(pid)
+                } else {
+                    WaitStatus::PtraceEvent(
+                        pid,
+                        Signal::try_from(si_status & 0xff)?,
+                        (si_status >> 8) as c_int,
+                    )
+                }
+            }
+            _ => return Err(Errno::EINVAL),
+        };
+
+        Ok(status)
+    }
 }
 
 /// Wait for a process to change status
 ///
 /// See also [waitpid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/waitpid.html)
-pub fn waitpid<P: Into<Option<Pid>>>(pid: P, options: Option<WaitPidFlag>) -> Result<WaitStatus> {
+pub fn waitpid<P: Into<Option<Pid>>>(
+    pid: P,
+    options: Option<WaitPidFlag>,
+) -> Result<WaitStatus> {
     use self::WaitStatus::*;
 
     let mut status: i32 = 0;
@@ -260,3 +335,54 @@
 pub fn wait() -> Result<WaitStatus> {
     waitpid(None, None)
 }
+
+/// The ID argument for `waitid`
+#[cfg(any(
+    target_os = "android",
+    target_os = "freebsd",
+    target_os = "haiku",
+    all(target_os = "linux", not(target_env = "uclibc")),
+))]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum Id {
+    /// Wait for any child
+    All,
+    /// Wait for the child whose process ID matches the given PID
+    Pid(Pid),
+    /// Wait for the child whose process group ID matches the given PID
+    ///
+    /// If the PID is zero, the caller's process group is used since Linux 5.4.
+    PGid(Pid),
+    /// Wait for the child referred to by the given PID file descriptor
+    #[cfg(any(target_os = "android", target_os = "linux"))]
+    PIDFd(RawFd),
+}
+
+/// Wait for a process to change status
+///
+/// See also [waitid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/waitid.html)
+#[cfg(any(
+    target_os = "android",
+    target_os = "freebsd",
+    target_os = "haiku",
+    all(target_os = "linux", not(target_env = "uclibc")),
+))]
+pub fn waitid(id: Id, flags: WaitPidFlag) -> Result<WaitStatus> {
+    let (idtype, idval) = match id {
+        Id::All => (libc::P_ALL, 0),
+        Id::Pid(pid) => (libc::P_PID, pid.as_raw() as libc::id_t),
+        Id::PGid(pid) => (libc::P_PGID, pid.as_raw() as libc::id_t),
+        #[cfg(any(target_os = "android", target_os = "linux"))]
+        Id::PIDFd(fd) => (libc::P_PIDFD, fd as libc::id_t),
+    };
+
+    let siginfo = unsafe {
+        // Memory is zeroed rather than uninitialized, as not all platforms
+        // initialize the memory in the StillAlive case
+        let mut siginfo: libc::siginfo_t = std::mem::zeroed();
+        Errno::result(libc::waitid(idtype, idval, &mut siginfo, flags.bits()))?;
+        siginfo
+    };
+
+    unsafe { WaitStatus::from_siginfo(&siginfo) }
+}
diff --git a/src/time.rs b/src/time.rs
index 6275b59..2e03c46 100644
--- a/src/time.rs
+++ b/src/time.rs
@@ -6,6 +6,7 @@
     target_os = "android",
     target_os = "emscripten",
 ))]
+#[cfg(feature = "process")]
 use crate::unistd::Pid;
 use crate::{Errno, Result};
 use libc::{self, clockid_t};
@@ -13,7 +14,7 @@
 
 /// Clock identifier
 ///
-/// Newtype pattern around `clockid_t` (which is just alias). It pervents bugs caused by
+/// Newtype pattern around `clockid_t` (which is just alias). It prevents bugs caused by
 /// accidentally passing wrong value.
 #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
 pub struct ClockId(clockid_t);
@@ -24,6 +25,8 @@
         ClockId(clk_id)
     }
 
+    feature! {
+    #![feature = "process"]
     /// Returns `ClockId` of a `pid` CPU-time clock
     #[cfg(any(
         target_os = "freebsd",
@@ -32,12 +35,15 @@
         target_os = "android",
         target_os = "emscripten",
     ))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn pid_cpu_clock_id(pid: Pid) -> Result<Self> {
         clock_getcpuclockid(pid)
     }
+    }
 
     /// Returns resolution of the clock id
     #[cfg(not(target_os = "redox"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn res(self) -> Result<TimeSpec> {
         clock_getres(self)
     }
@@ -51,11 +57,10 @@
     #[cfg(not(any(
         target_os = "macos",
         target_os = "ios",
-        all(
-            not(any(target_env = "uclibc", target_env = "newlibc")),
-            any(target_os = "redox", target_os = "hermit",),
-        ),
+        target_os = "redox",
+        target_os = "hermit",
     )))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub fn set_time(self, timespec: TimeSpec) -> Result<()> {
         clock_settime(self, timespec)
     }
@@ -66,122 +71,134 @@
     }
 
     #[cfg(any(
+        target_os = "android",
+        target_os = "emscripten",
         target_os = "fuchsia",
-        all(
-            not(any(target_env = "uclibc", target_env = "newlib")),
-            any(target_os = "linux", target_os = "android", target_os = "emscripten"),
-        )
+        target_os = "linux"
     ))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub const CLOCK_BOOTTIME: ClockId = ClockId(libc::CLOCK_BOOTTIME);
     #[cfg(any(
+        target_os = "android",
+        target_os = "emscripten",
         target_os = "fuchsia",
-        all(
-            not(any(target_env = "uclibc", target_env = "newlib")),
-            any(target_os = "linux", target_os = "android", target_os = "emscripten")
-        )
+        target_os = "linux"
     ))]
-    pub const CLOCK_BOOTTIME_ALARM: ClockId = ClockId(libc::CLOCK_BOOTTIME_ALARM);
+    #[cfg_attr(docsrs, doc(cfg(all())))]
+    pub const CLOCK_BOOTTIME_ALARM: ClockId =
+        ClockId(libc::CLOCK_BOOTTIME_ALARM);
     pub const CLOCK_MONOTONIC: ClockId = ClockId(libc::CLOCK_MONOTONIC);
     #[cfg(any(
+        target_os = "android",
+        target_os = "emscripten",
         target_os = "fuchsia",
-        all(
-            not(any(target_env = "uclibc", target_env = "newlib")),
-            any(target_os = "linux", target_os = "android", target_os = "emscripten")
-        )
+        target_os = "linux"
     ))]
-    pub const CLOCK_MONOTONIC_COARSE: ClockId = ClockId(libc::CLOCK_MONOTONIC_COARSE);
+    #[cfg_attr(docsrs, doc(cfg(all())))]
+    pub const CLOCK_MONOTONIC_COARSE: ClockId =
+        ClockId(libc::CLOCK_MONOTONIC_COARSE);
     #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
-    pub const CLOCK_MONOTONIC_FAST: ClockId = ClockId(libc::CLOCK_MONOTONIC_FAST);
+    #[cfg_attr(docsrs, doc(cfg(all())))]
+    pub const CLOCK_MONOTONIC_FAST: ClockId =
+        ClockId(libc::CLOCK_MONOTONIC_FAST);
     #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
-    pub const CLOCK_MONOTONIC_PRECISE: ClockId = ClockId(libc::CLOCK_MONOTONIC_PRECISE);
+    #[cfg_attr(docsrs, doc(cfg(all())))]
+    pub const CLOCK_MONOTONIC_PRECISE: ClockId =
+        ClockId(libc::CLOCK_MONOTONIC_PRECISE);
     #[cfg(any(
+        target_os = "android",
+        target_os = "emscripten",
         target_os = "fuchsia",
-        all(
-            not(any(target_env = "uclibc", target_env = "newlib")),
-            any(target_os = "linux", target_os = "android", target_os = "emscripten")
-        )
+        target_os = "linux"
     ))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub const CLOCK_MONOTONIC_RAW: ClockId = ClockId(libc::CLOCK_MONOTONIC_RAW);
     #[cfg(any(
+        target_os = "android",
+        target_os = "emscripten",
         target_os = "fuchsia",
-        target_env = "uclibc",
         target_os = "macos",
         target_os = "ios",
         target_os = "freebsd",
         target_os = "dragonfly",
-        all(
-            not(target_env = "newlib"),
-            any(target_os = "linux", target_os = "android", target_os = "emscripten")
-        )
+        target_os = "redox",
+        target_os = "linux"
     ))]
-    pub const CLOCK_PROCESS_CPUTIME_ID: ClockId = ClockId(libc::CLOCK_PROCESS_CPUTIME_ID);
+    #[cfg_attr(docsrs, doc(cfg(all())))]
+    pub const CLOCK_PROCESS_CPUTIME_ID: ClockId =
+        ClockId(libc::CLOCK_PROCESS_CPUTIME_ID);
     #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub const CLOCK_PROF: ClockId = ClockId(libc::CLOCK_PROF);
     pub const CLOCK_REALTIME: ClockId = ClockId(libc::CLOCK_REALTIME);
     #[cfg(any(
+        target_os = "android",
+        target_os = "emscripten",
         target_os = "fuchsia",
-        all(
-            not(any(target_env = "uclibc", target_env = "newlib")),
-            any(target_os = "linux", target_os = "android", target_os = "emscripten")
-        )
+        target_os = "linux"
     ))]
-    pub const CLOCK_REALTIME_ALARM: ClockId = ClockId(libc::CLOCK_REALTIME_ALARM);
+    #[cfg_attr(docsrs, doc(cfg(all())))]
+    pub const CLOCK_REALTIME_ALARM: ClockId =
+        ClockId(libc::CLOCK_REALTIME_ALARM);
     #[cfg(any(
+        target_os = "android",
+        target_os = "emscripten",
         target_os = "fuchsia",
-        all(
-            not(any(target_env = "uclibc", target_env = "newlib")),
-            any(target_os = "linux", target_os = "android", target_os = "emscripten")
-        )
+        target_os = "linux"
     ))]
-    pub const CLOCK_REALTIME_COARSE: ClockId = ClockId(libc::CLOCK_REALTIME_COARSE);
+    #[cfg_attr(docsrs, doc(cfg(all())))]
+    pub const CLOCK_REALTIME_COARSE: ClockId =
+        ClockId(libc::CLOCK_REALTIME_COARSE);
     #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub const CLOCK_REALTIME_FAST: ClockId = ClockId(libc::CLOCK_REALTIME_FAST);
     #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
-    pub const CLOCK_REALTIME_PRECISE: ClockId = ClockId(libc::CLOCK_REALTIME_PRECISE);
+    #[cfg_attr(docsrs, doc(cfg(all())))]
+    pub const CLOCK_REALTIME_PRECISE: ClockId =
+        ClockId(libc::CLOCK_REALTIME_PRECISE);
     #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub const CLOCK_SECOND: ClockId = ClockId(libc::CLOCK_SECOND);
     #[cfg(any(
+        target_os = "emscripten",
         target_os = "fuchsia",
-        all(
-            not(any(target_env = "uclibc", target_env = "newlib")),
-            any(
-                target_os = "emscripten",
-                all(target_os = "linux", target_env = "musl")
-            )
-        )
+        all(target_os = "linux", target_env = "musl")
     ))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub const CLOCK_SGI_CYCLE: ClockId = ClockId(libc::CLOCK_SGI_CYCLE);
     #[cfg(any(
+        target_os = "android",
+        target_os = "emscripten",
         target_os = "fuchsia",
-        all(
-            not(any(target_env = "uclibc", target_env = "newlib")),
-            any(
-                target_os = "emscripten",
-                all(target_os = "linux", target_env = "musl")
-            )
-        )
+        target_os = "linux"
     ))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub const CLOCK_TAI: ClockId = ClockId(libc::CLOCK_TAI);
     #[cfg(any(
-        target_env = "uclibc",
+        target_os = "android",
+        target_os = "emscripten",
         target_os = "fuchsia",
         target_os = "ios",
         target_os = "macos",
         target_os = "freebsd",
         target_os = "dragonfly",
-        all(
-            not(target_env = "newlib"),
-            any(target_os = "linux", target_os = "android", target_os = "emscripten",),
-        ),
+        target_os = "linux"
     ))]
-    pub const CLOCK_THREAD_CPUTIME_ID: ClockId = ClockId(libc::CLOCK_THREAD_CPUTIME_ID);
+    #[cfg_attr(docsrs, doc(cfg(all())))]
+    pub const CLOCK_THREAD_CPUTIME_ID: ClockId =
+        ClockId(libc::CLOCK_THREAD_CPUTIME_ID);
     #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub const CLOCK_UPTIME: ClockId = ClockId(libc::CLOCK_UPTIME);
     #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub const CLOCK_UPTIME_FAST: ClockId = ClockId(libc::CLOCK_UPTIME_FAST);
     #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
-    pub const CLOCK_UPTIME_PRECISE: ClockId = ClockId(libc::CLOCK_UPTIME_PRECISE);
+    #[cfg_attr(docsrs, doc(cfg(all())))]
+    pub const CLOCK_UPTIME_PRECISE: ClockId =
+        ClockId(libc::CLOCK_UPTIME_PRECISE);
     #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub const CLOCK_VIRTUAL: ClockId = ClockId(libc::CLOCK_VIRTUAL);
 }
 
@@ -206,9 +223,11 @@
 /// Get the resolution of the specified clock, (see
 /// [clock_getres(2)](https://pubs.opengroup.org/onlinepubs/7908799/xsh/clock_getres.html)).
 #[cfg(not(target_os = "redox"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
 pub fn clock_getres(clock_id: ClockId) -> Result<TimeSpec> {
     let mut c_time: MaybeUninit<libc::timespec> = MaybeUninit::uninit();
-    let ret = unsafe { libc::clock_getres(clock_id.as_raw(), c_time.as_mut_ptr()) };
+    let ret =
+        unsafe { libc::clock_getres(clock_id.as_raw(), c_time.as_mut_ptr()) };
     Errno::result(ret)?;
     let res = unsafe { c_time.assume_init() };
     Ok(TimeSpec::from(res))
@@ -218,7 +237,8 @@
 /// [clock_gettime(2)](https://pubs.opengroup.org/onlinepubs/7908799/xsh/clock_gettime.html)).
 pub fn clock_gettime(clock_id: ClockId) -> Result<TimeSpec> {
     let mut c_time: MaybeUninit<libc::timespec> = MaybeUninit::uninit();
-    let ret = unsafe { libc::clock_gettime(clock_id.as_raw(), c_time.as_mut_ptr()) };
+    let ret =
+        unsafe { libc::clock_gettime(clock_id.as_raw(), c_time.as_mut_ptr()) };
     Errno::result(ret)?;
     let res = unsafe { c_time.assume_init() };
     Ok(TimeSpec::from(res))
@@ -229,13 +249,13 @@
 #[cfg(not(any(
     target_os = "macos",
     target_os = "ios",
-    all(
-        not(any(target_env = "uclibc", target_env = "newlibc")),
-        any(target_os = "redox", target_os = "hermit",),
-    ),
+    target_os = "redox",
+    target_os = "hermit",
 )))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
 pub fn clock_settime(clock_id: ClockId, timespec: TimeSpec) -> Result<()> {
-    let ret = unsafe { libc::clock_settime(clock_id.as_raw(), timespec.as_ref()) };
+    let ret =
+        unsafe { libc::clock_settime(clock_id.as_raw(), timespec.as_ref()) };
     Errno::result(ret).map(drop)
 }
 
@@ -248,9 +268,12 @@
     target_os = "android",
     target_os = "emscripten",
 ))]
+#[cfg(feature = "process")]
+#[cfg_attr(docsrs, doc(cfg(feature = "process")))]
 pub fn clock_getcpuclockid(pid: Pid) -> Result<ClockId> {
     let mut clk_id: MaybeUninit<libc::clockid_t> = MaybeUninit::uninit();
-    let ret = unsafe { libc::clock_getcpuclockid(pid.into(), clk_id.as_mut_ptr()) };
+    let ret =
+        unsafe { libc::clock_getcpuclockid(pid.into(), clk_id.as_mut_ptr()) };
     if ret == 0 {
         let res = unsafe { clk_id.assume_init() };
         Ok(ClockId::from(res))
diff --git a/src/ucontext.rs b/src/ucontext.rs
index f2338bd..b2a39f7 100644
--- a/src/ucontext.rs
+++ b/src/ucontext.rs
@@ -1,10 +1,10 @@
 #[cfg(not(target_env = "musl"))]
+use crate::errno::Errno;
+use crate::sys::signal::SigSet;
+#[cfg(not(target_env = "musl"))]
 use crate::Result;
 #[cfg(not(target_env = "musl"))]
-use crate::errno::Errno;
-#[cfg(not(target_env = "musl"))]
 use std::mem;
-use crate::sys::signal::SigSet;
 
 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
 pub struct UContext {
@@ -17,7 +17,9 @@
         let mut context = mem::MaybeUninit::<libc::ucontext_t>::uninit();
         let res = unsafe { libc::getcontext(context.as_mut_ptr()) };
         Errno::result(res).map(|_| unsafe {
-            UContext { context: context.assume_init()}
+            UContext {
+                context: context.assume_init(),
+            }
         })
     }
 
@@ -31,13 +33,15 @@
 
     pub fn sigmask_mut(&mut self) -> &mut SigSet {
         unsafe {
-            &mut *(&mut self.context.uc_sigmask as *mut libc::sigset_t as *mut SigSet)
+            &mut *(&mut self.context.uc_sigmask as *mut libc::sigset_t
+                as *mut SigSet)
         }
     }
 
     pub fn sigmask(&self) -> &SigSet {
         unsafe {
-            &*(&self.context.uc_sigmask as *const libc::sigset_t as *const SigSet)
+            &*(&self.context.uc_sigmask as *const libc::sigset_t
+                as *const SigSet)
         }
     }
 }
diff --git a/src/unistd.rs b/src/unistd.rs
index 2c89d77..ca07b34 100644
--- a/src/unistd.rs
+++ b/src/unistd.rs
@@ -1,37 +1,70 @@
 //! Safe wrappers around functions found in libc "unistd.h" header
 
+use crate::errno::{self, Errno};
+#[cfg(not(target_os = "redox"))]
+#[cfg(feature = "fs")]
+use crate::fcntl::{at_rawfd, AtFlags};
+#[cfg(feature = "fs")]
+use crate::fcntl::{fcntl, FcntlArg::F_SETFD, FdFlag, OFlag};
+#[cfg(all(
+    feature = "fs",
+    any(
+        target_os = "openbsd",
+        target_os = "netbsd",
+        target_os = "freebsd",
+        target_os = "dragonfly",
+        target_os = "macos",
+        target_os = "ios"
+    )
+))]
+use crate::sys::stat::FileFlag;
+#[cfg(feature = "fs")]
+use crate::sys::stat::Mode;
+use crate::{Error, NixPath, Result};
 #[cfg(not(target_os = "redox"))]
 use cfg_if::cfg_if;
-use crate::errno::{self, Errno};
-use crate::{Error, Result, NixPath};
-#[cfg(not(target_os = "redox"))]
-use crate::fcntl::{AtFlags, at_rawfd};
-use crate::fcntl::{FdFlag, OFlag, fcntl};
-use crate::fcntl::FcntlArg::F_SETFD;
-use libc::{self, c_char, c_void, c_int, c_long, c_uint, size_t, pid_t, off_t,
-           uid_t, gid_t, mode_t, PATH_MAX};
-use std::{fmt, mem, ptr};
+use libc::{
+    self, c_char, c_int, c_long, c_uint, c_void, gid_t, mode_t, off_t, pid_t,
+    size_t, uid_t, PATH_MAX,
+};
 use std::convert::Infallible;
 use std::ffi::{CStr, OsString};
 #[cfg(not(target_os = "redox"))]
 use std::ffi::{CString, OsStr};
-use std::os::unix::ffi::OsStringExt;
 #[cfg(not(target_os = "redox"))]
 use std::os::unix::ffi::OsStrExt;
+use std::os::unix::ffi::OsStringExt;
 use std::os::unix::io::RawFd;
 use std::path::PathBuf;
-use crate::sys::stat::Mode;
+use std::{fmt, mem, ptr};
 
-#[cfg(any(target_os = "android", target_os = "linux"))]
-pub use self::pivot_root::*;
+feature! {
+    #![feature = "fs"]
+    #[cfg(any(target_os = "android", target_os = "linux"))]
+    pub use self::pivot_root::*;
+}
 
-#[cfg(any(target_os = "android", target_os = "freebsd",
-          target_os = "linux", target_os = "openbsd"))]
+#[cfg(any(
+    target_os = "android",
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "linux",
+    target_os = "openbsd"
+))]
 pub use self::setres::*;
 
-#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg(any(
+    target_os = "android",
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "linux",
+    target_os = "openbsd"
+))]
 pub use self::getres::*;
 
+feature! {
+#![feature = "user"]
+
 /// User identifier
 ///
 /// Newtype pattern around `uid_t` (which is just alias). It prevents bugs caused by accidentally
@@ -46,11 +79,13 @@
     }
 
     /// Returns Uid of calling process. This is practically a more Rusty alias for `getuid`.
+    #[doc(alias("getuid"))]
     pub fn current() -> Self {
         getuid()
     }
 
     /// Returns effective Uid of calling process. This is practically a more Rusty alias for `geteuid`.
+    #[doc(alias("geteuid"))]
     pub fn effective() -> Self {
         geteuid()
     }
@@ -72,6 +107,12 @@
     }
 }
 
+impl From<uid_t> for Uid {
+    fn from(uid: uid_t) -> Self {
+        Uid(uid)
+    }
+}
+
 impl fmt::Display for Uid {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         fmt::Display::fmt(&self.0, f)
@@ -95,11 +136,13 @@
     }
 
     /// Returns Gid of calling process. This is practically a more Rusty alias for `getgid`.
+    #[doc(alias("getgid"))]
     pub fn current() -> Self {
         getgid()
     }
 
     /// Returns effective Gid of calling process. This is practically a more Rusty alias for `getegid`.
+    #[doc(alias("getegid"))]
     pub fn effective() -> Self {
         getegid()
     }
@@ -116,12 +159,21 @@
     }
 }
 
+impl From<gid_t> for Gid {
+    fn from(gid: gid_t) -> Self {
+        Gid(gid)
+    }
+}
+
 impl fmt::Display for Gid {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         fmt::Display::fmt(&self.0, f)
     }
 }
+}
 
+feature! {
+#![feature = "process"]
 /// Process identifier
 ///
 /// Newtype pattern around `pid_t` (which is just alias). It prevents bugs caused by accidentally
@@ -136,11 +188,13 @@
     }
 
     /// Returns PID of calling process
+    #[doc(alias("getpid"))]
     pub fn this() -> Self {
         getpid()
     }
 
     /// Returns PID of parent of calling process
+    #[doc(alias("getppid"))]
     pub fn parent() -> Self {
         getppid()
     }
@@ -193,8 +247,8 @@
 /// Create a new child process duplicating the parent process ([see
 /// fork(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html)).
 ///
-/// After calling the fork system call (successfully) two processes will
-/// be created that are identical with the exception of their pid and the
+/// After successfully calling the fork system call, a second process will
+/// be created which is identical to the original except for the pid and the
 /// return value of this function.  As an example:
 ///
 /// ```
@@ -214,7 +268,7 @@
 /// }
 /// ```
 ///
-/// This will print something like the following (order indeterministic).  The
+/// This will print something like the following (order nondeterministic).  The
 /// thing to note is that you end up with two processes continuing execution
 /// immediately after the fork call but with different match arms.
 ///
@@ -303,8 +357,10 @@
     let res = unsafe { libc::getsid(pid.unwrap_or(Pid(0)).into()) };
     Errno::result(res).map(Pid)
 }
+}
 
-
+feature! {
+#![all(feature = "process", feature = "term")]
 /// Get the terminal foreground process group (see
 /// [tcgetpgrp(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetpgrp.html)).
 ///
@@ -325,8 +381,10 @@
     let res = unsafe { libc::tcsetpgrp(fd, pgrp.into()) };
     Errno::result(res).map(drop)
 }
+}
 
-
+feature! {
+#![feature = "process"]
 /// Get the group id of the calling process (see
 ///[getpgrp(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpgrp.html)).
 ///
@@ -352,11 +410,14 @@
 pub fn gettid() -> Pid {
     Pid(unsafe { libc::syscall(libc::SYS_gettid) as pid_t })
 }
+}
 
+feature! {
+#![feature = "fs"]
 /// Create a copy of the specified file descriptor (see
 /// [dup(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/dup.html)).
 ///
-/// The new file descriptor will be have a new index but refer to the same
+/// The new file descriptor will have a new index but refer to the same
 /// resource as the old file descriptor and the old and new file descriptors may
 /// be used interchangeably.  The new and old file descriptor share the same
 /// underlying resource, offset, and file status flags.  The actual index used
@@ -524,7 +585,7 @@
 // mkfifoat is not implemented in OSX or android
 #[inline]
 #[cfg(not(any(
-    target_os = "macos", target_os = "ios",
+    target_os = "macos", target_os = "ios", target_os = "haiku",
     target_os = "android", target_os = "redox")))]
 pub fn mkfifoat<P: ?Sized + NixPath>(dirfd: Option<RawFd>, path: &P, mode: Mode) -> Result<()> {
     let res = path.with_nix_path(|cstr| unsafe {
@@ -562,14 +623,16 @@
         })??;
     Errno::result(res).map(drop)
 }
+}
 
 // Double the buffer capacity up to limit. In case it already has
 // reached the limit, return Errno::ERANGE.
+#[cfg(any(feature = "fs", feature = "user"))]
 fn reserve_double_buffer_size<T>(buf: &mut Vec<T>, limit: usize) -> Result<()> {
     use std::cmp::min;
 
     if buf.capacity() >= limit {
-        return Err(Errno::ERANGE)
+        return Err(Errno::ERANGE);
     }
 
     let capacity = min(buf.capacity() * 2, limit);
@@ -578,6 +641,9 @@
     Ok(())
 }
 
+feature! {
+#![feature = "fs"]
+
 /// Returns the current directory as a `PathBuf`
 ///
 /// Err is returned if the current user doesn't have the permission to read or search a component
@@ -621,6 +687,10 @@
         }
     }
 }
+}
+
+feature! {
+#![all(feature = "user", feature = "fs")]
 
 /// Computes the raw UID and GID values to pass to a `*chown` call.
 // The cast is not unnecessary on all platforms.
@@ -688,8 +758,8 @@
 /// If `flag` is `FchownatFlags::NoFollowSymlink` and `path` names a symbolic link,
 /// then the mode of the symbolic link is changed.
 ///
-/// `fchownat(None, path, mode, FchownatFlags::NoFollowSymlink)` is identical to
-/// a call `libc::lchown(path, mode)`.  That's why `lchmod` is unimplemented in
+/// `fchownat(None, path, owner, group, FchownatFlags::NoFollowSymlink)` is identical to
+/// a call `libc::lchown(path, owner, group)`.  That's why `lchown` is unimplemented in
 /// the `nix` crate.
 ///
 /// # References
@@ -716,10 +786,16 @@
 
     Errno::result(res).map(drop)
 }
+}
 
+feature! {
+#![feature = "process"]
 fn to_exec_array<S: AsRef<CStr>>(args: &[S]) -> Vec<*const c_char> {
     use std::iter::once;
-    args.iter().map(|s| s.as_ref().as_ptr()).chain(once(ptr::null())).collect()
+    args.iter()
+        .map(|s| s.as_ref().as_ptr())
+        .chain(once(ptr::null()))
+        .collect()
 }
 
 /// Replace the current process image with a new one (see
@@ -815,11 +891,9 @@
 ///
 /// This function is similar to `execve`, except that the program to be executed
 /// is referenced as a file descriptor instead of a path.
-// Note for NetBSD and OpenBSD: although rust-lang/libc includes it (under
-// unix/bsd/netbsdlike/) fexecve is not currently implemented on NetBSD nor on
-// OpenBSD.
 #[cfg(any(target_os = "android",
           target_os = "linux",
+          target_os = "dragonfly",
           target_os = "freebsd"))]
 #[inline]
 pub fn fexecve<SA: AsRef<CStr> ,SE: AsRef<CStr>>(fd: RawFd, args: &[SA], env: &[SE]) -> Result<Infallible> {
@@ -895,14 +969,18 @@
     let res = unsafe { libc::daemon(nochdir as c_int, noclose as c_int) };
     Errno::result(res).map(drop)
 }
+}
+
+feature! {
+#![feature = "hostname"]
 
 /// Set the system host name (see
 /// [sethostname(2)](https://man7.org/linux/man-pages/man2/gethostname.2.html)).
 ///
 /// Given a name, attempt to update the system host name to the given string.
 /// On some systems, the host name is limited to as few as 64 bytes.  An error
-/// will be return if the name is not valid or the current process does not have
-/// permissions to update the host name.
+/// will be returned if the name is not valid or the current process does not
+/// have permissions to update the host name.
 #[cfg(not(target_os = "redox"))]
 pub fn sethostname<S: AsRef<OsStr>>(name: S) -> Result<()> {
     // Handle some differences in type of the len arg across platforms.
@@ -925,36 +1003,37 @@
     Errno::result(res).map(drop)
 }
 
-/// Get the host name and store it in the provided buffer, returning a pointer
-/// the `CStr` in that buffer on success (see
+/// Get the host name and store it in an internally allocated buffer, returning an
+/// `OsString` on success (see
 /// [gethostname(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/gethostname.html)).
 ///
 /// This function call attempts to get the host name for the running system and
-/// store it in a provided buffer.  The buffer will be populated with bytes up
-/// to the length of the provided slice including a NUL terminating byte.  If
-/// the hostname is longer than the length provided, no error will be provided.
-/// The posix specification does not specify whether implementations will
-/// null-terminate in this case, but the nix implementation will ensure that the
-/// buffer is null terminated in this case.
+/// store it in an internal buffer, returning it as an `OsString` if successful.
 ///
 /// ```no_run
 /// use nix::unistd;
 ///
-/// let mut buf = [0u8; 64];
-/// let hostname_cstr = unistd::gethostname(&mut buf).expect("Failed getting hostname");
-/// let hostname = hostname_cstr.to_str().expect("Hostname wasn't valid UTF-8");
+/// let hostname = unistd::gethostname().expect("Failed getting hostname");
+/// let hostname = hostname.into_string().expect("Hostname wasn't valid UTF-8");
 /// println!("Hostname: {}", hostname);
 /// ```
-pub fn gethostname(buffer: &mut [u8]) -> Result<&CStr> {
+pub fn gethostname() -> Result<OsString> {
+    // The capacity is the max length of a hostname plus the NUL terminator.
+    let mut buffer: Vec<u8> = Vec::with_capacity(256);
     let ptr = buffer.as_mut_ptr() as *mut c_char;
-    let len = buffer.len() as size_t;
+    let len = buffer.capacity() as size_t;
 
     let res = unsafe { libc::gethostname(ptr, len) };
     Errno::result(res).map(|_| {
-        buffer[len - 1] = 0; // ensure always null-terminated
-        unsafe { CStr::from_ptr(buffer.as_ptr() as *const c_char) }
+        unsafe {
+            buffer.as_mut_ptr().wrapping_add(len - 1).write(0); // ensure always null-terminated
+            let len = CStr::from_ptr(buffer.as_ptr() as *const c_char).len();
+            buffer.set_len(len);
+        }
+        OsString::from_vec(buffer)
     })
 }
+}
 
 /// Close a raw file descriptor
 ///
@@ -990,7 +1069,9 @@
 ///
 /// See also [read(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/read.html)
 pub fn read(fd: RawFd, buf: &mut [u8]) -> Result<usize> {
-    let res = unsafe { libc::read(fd, buf.as_mut_ptr() as *mut c_void, buf.len() as size_t) };
+    let res = unsafe {
+        libc::read(fd, buf.as_mut_ptr() as *mut c_void, buf.len() as size_t)
+    };
 
     Errno::result(res).map(|r| r as usize)
 }
@@ -999,11 +1080,16 @@
 ///
 /// See also [write(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html)
 pub fn write(fd: RawFd, buf: &[u8]) -> Result<usize> {
-    let res = unsafe { libc::write(fd, buf.as_ptr() as *const c_void, buf.len() as size_t) };
+    let res = unsafe {
+        libc::write(fd, buf.as_ptr() as *const c_void, buf.len() as size_t)
+    };
 
     Errno::result(res).map(|r| r as usize)
 }
 
+feature! {
+#![feature = "fs"]
+
 /// Directive that tells [`lseek`] and [`lseek64`] what the offset is relative to.
 ///
 /// [`lseek`]: ./fn.lseek.html
@@ -1054,22 +1140,23 @@
 
     Errno::result(res).map(|r| r as libc::off64_t)
 }
+}
 
 /// Create an interprocess channel.
 ///
 /// See also [pipe(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pipe.html)
 pub fn pipe() -> std::result::Result<(RawFd, RawFd), Error> {
-    unsafe {
-        let mut fds = mem::MaybeUninit::<[c_int; 2]>::uninit();
+    let mut fds = mem::MaybeUninit::<[c_int; 2]>::uninit();
 
-        let res = libc::pipe(fds.as_mut_ptr() as *mut c_int);
+    let res = unsafe { libc::pipe(fds.as_mut_ptr() as *mut c_int) };
 
-        Error::result(res)?;
+    Error::result(res)?;
 
-        Ok((fds.assume_init()[0], fds.assume_init()[1]))
-    }
+    unsafe { Ok((fds.assume_init()[0], fds.assume_init()[1])) }
 }
 
+feature! {
+#![feature = "fs"]
 /// Like `pipe`, but allows setting certain file descriptor flags.
 ///
 /// The following flags are supported, and will be set atomically as the pipe is
@@ -1267,6 +1354,17 @@
     unsafe { libc::sync() };
 }
 
+/// Commit filesystem caches containing file referred to by the open file
+/// descriptor `fd` to disk
+///
+/// See also [syncfs(2)](https://man7.org/linux/man-pages/man2/sync.2.html)
+#[cfg(target_os = "linux")]
+pub fn syncfs(fd: RawFd) -> Result<()> {
+    let res = unsafe { libc::syncfs(fd) };
+
+    Errno::result(res).map(drop)
+}
+
 /// Synchronize changes to a file
 ///
 /// See also [fsync(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fsync.html)
@@ -1281,11 +1379,13 @@
 ///
 /// See also
 /// [fdatasync(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fdatasync.html)
-// `fdatasync(2) is in POSIX, but in libc it is only defined in `libc::notbsd`.
-// TODO: exclude only Apple systems after https://github.com/rust-lang/libc/pull/211
 #[cfg(any(target_os = "linux",
           target_os = "android",
           target_os = "emscripten",
+          target_os = "freebsd",
+          target_os = "fuchsia",
+          target_os = "netbsd",
+          target_os = "openbsd",
           target_os = "illumos",
           target_os = "solaris"))]
 #[inline]
@@ -1294,6 +1394,10 @@
 
     Errno::result(res).map(drop)
 }
+}
+
+feature! {
+#![feature = "user"]
 
 /// Get a real user ID
 ///
@@ -1374,7 +1478,10 @@
 
     Errno::result(res).map(drop)
 }
+}
 
+feature! {
+#![all(feature = "fs", feature = "user")]
 /// Set the user identity used for filesystem checks per-thread.
 /// On both success and failure, this call returns the previous filesystem user
 /// ID of the caller.
@@ -1396,6 +1503,10 @@
     let prev_fsgid = unsafe { libc::setfsgid(gid.into()) };
     Gid::from_raw(prev_fsgid as gid_t)
 }
+}
+
+feature! {
+#![feature = "user"]
 
 /// Get the list of supplementary group IDs of the calling process.
 ///
@@ -1472,7 +1583,7 @@
 /// # use std::error::Error;
 /// # use nix::unistd::*;
 /// #
-/// # fn try_main() -> Result<(), Box<Error>> {
+/// # fn try_main() -> Result<(), Box<dyn Error>> {
 /// let uid = Uid::from_raw(33);
 /// let gid = Gid::from_raw(34);
 /// setgroups(&[gid])?;
@@ -1484,7 +1595,7 @@
 /// #
 /// # try_main().unwrap();
 /// ```
-#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox")))]
+#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox", target_os = "haiku")))]
 pub fn setgroups(groups: &[Gid]) -> Result<()> {
     cfg_if! {
         if #[cfg(any(target_os = "dragonfly",
@@ -1598,7 +1709,7 @@
 /// # use std::ffi::CString;
 /// # use nix::unistd::*;
 /// #
-/// # fn try_main() -> Result<(), Box<Error>> {
+/// # fn try_main() -> Result<(), Box<dyn Error>> {
 /// let user = CString::new("www-data").unwrap();
 /// let uid = Uid::from_raw(33);
 /// let gid = Gid::from_raw(33);
@@ -1611,7 +1722,7 @@
 /// #
 /// # try_main().unwrap();
 /// ```
-#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox")))]
+#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox", target_os = "haiku")))]
 pub fn initgroups(user: &CStr, group: Gid) -> Result<()> {
     cfg_if! {
         if #[cfg(any(target_os = "ios", target_os = "macos"))] {
@@ -1625,6 +1736,10 @@
 
     Errno::result(res).map(drop)
 }
+}
+
+feature! {
+#![feature = "signal"]
 
 /// Suspend the thread until a signal is received.
 ///
@@ -1722,6 +1837,7 @@
         }
     }
 }
+}
 
 /// Suspend execution for an interval of time
 ///
@@ -1732,7 +1848,10 @@
     unsafe { libc::sleep(seconds) }
 }
 
-#[cfg(not(target_os = "redox"))]
+feature! {
+#![feature = "acct"]
+
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
 pub mod acct {
     use crate::{Result, NixPath};
     use crate::errno::Errno;
@@ -1756,7 +1875,10 @@
         Errno::result(res).map(drop)
     }
 }
+}
 
+feature! {
+#![feature = "fs"]
 /// Creates a regular file which persists even after process termination
 ///
 /// * `template`: a path whose 6 rightmost characters must be X, e.g. `/tmp/tmpfile_XXXXXX`
@@ -1792,6 +1914,10 @@
     Errno::result(fd)?;
     Ok((fd, PathBuf::from(pathname)))
 }
+}
+
+feature! {
+#![all(feature = "fs", feature = "feature")]
 
 /// Variable names for `pathconf`
 ///
@@ -1817,6 +1943,7 @@
               target_os = "netbsd", target_os = "openbsd", target_os = "redox"))]
     /// Minimum number of bits needed to represent, as a signed integer value,
     /// the maximum size of a regular file allowed in the specified directory.
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     FILESIZEBITS = libc::_PC_FILESIZEBITS,
     /// Maximum number of links to a single file.
     LINK_MAX = libc::_PC_LINK_MAX,
@@ -1840,33 +1967,40 @@
     #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "illumos",
               target_os = "linux", target_os = "netbsd", target_os = "openbsd",
               target_os = "redox", target_os = "solaris"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// Symbolic links can be created.
     POSIX2_SYMLINKS = libc::_PC_2_SYMLINKS,
     #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd",
               target_os = "linux", target_os = "openbsd", target_os = "redox"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// Minimum number of bytes of storage actually allocated for any portion of
     /// a file.
     POSIX_ALLOC_SIZE_MIN = libc::_PC_ALLOC_SIZE_MIN,
     #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd",
               target_os = "linux", target_os = "openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// Recommended increment for file transfer sizes between the
     /// `POSIX_REC_MIN_XFER_SIZE` and `POSIX_REC_MAX_XFER_SIZE` values.
     POSIX_REC_INCR_XFER_SIZE = libc::_PC_REC_INCR_XFER_SIZE,
     #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd",
               target_os = "linux", target_os = "openbsd", target_os = "redox"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// Maximum recommended file transfer size.
     POSIX_REC_MAX_XFER_SIZE = libc::_PC_REC_MAX_XFER_SIZE,
     #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd",
               target_os = "linux", target_os = "openbsd", target_os = "redox"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// Minimum recommended file transfer size.
     POSIX_REC_MIN_XFER_SIZE = libc::_PC_REC_MIN_XFER_SIZE,
     #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd",
               target_os = "linux", target_os = "openbsd", target_os = "redox"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     ///  Recommended file transfer buffer alignment.
     POSIX_REC_XFER_ALIGN = libc::_PC_REC_XFER_ALIGN,
     #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd",
               target_os = "illumos", target_os = "linux", target_os = "netbsd",
               target_os = "openbsd", target_os = "redox", target_os = "solaris"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// Maximum number of bytes in a symbolic link.
     SYMLINK_MAX = libc::_PC_SYMLINK_MAX,
     /// The use of `chown` and `fchown` is restricted to a process with
@@ -1882,22 +2016,26 @@
     #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd",
               target_os = "illumos", target_os = "linux", target_os = "openbsd",
               target_os = "redox", target_os = "solaris"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// Asynchronous input or output operations may be performed for the
     /// associated file.
     _POSIX_ASYNC_IO = libc::_PC_ASYNC_IO,
     #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd",
               target_os = "illumos", target_os = "linux", target_os = "openbsd",
               target_os = "redox", target_os = "solaris"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// Prioritized input or output operations may be performed for the
     /// associated file.
     _POSIX_PRIO_IO = libc::_PC_PRIO_IO,
     #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd",
               target_os = "illumos", target_os = "linux", target_os = "netbsd",
               target_os = "openbsd", target_os = "redox", target_os = "solaris"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// Synchronized input or output operations may be performed for the
     /// associated file.
     _POSIX_SYNC_IO = libc::_PC_SYNC_IO,
     #[cfg(any(target_os = "dragonfly", target_os = "openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The resolution in nanoseconds for all file timestamps.
     _POSIX_TIMESTAMP_RESOLUTION = libc::_PC_TIMESTAMP_RESOLUTION
 }
@@ -1972,6 +2110,10 @@
         Ok(Some(raw))
     }
 }
+}
+
+feature! {
+#![feature = "feature"]
 
 /// Variable names for `sysconf`
 ///
@@ -1995,15 +2137,18 @@
 pub enum SysconfVar {
     /// Maximum number of I/O operations in a single list I/O call supported by
     /// the implementation.
-    #[cfg(not(target_os = "redox"))]
+    #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     AIO_LISTIO_MAX = libc::_SC_AIO_LISTIO_MAX,
     /// Maximum number of outstanding asynchronous I/O operations supported by
     /// the implementation.
-    #[cfg(not(target_os = "redox"))]
+    #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     AIO_MAX = libc::_SC_AIO_MAX,
     #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
               target_os = "ios", target_os="linux", target_os = "macos",
               target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The maximum amount by which a process can decrease its asynchronous I/O
     /// priority level from its own scheduling priority.
     AIO_PRIO_DELTA_MAX = libc::_SC_AIO_PRIO_DELTA_MAX,
@@ -2011,18 +2156,23 @@
     ARG_MAX = libc::_SC_ARG_MAX,
     /// Maximum number of functions that may be registered with `atexit`.
     #[cfg(not(target_os = "redox"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     ATEXIT_MAX = libc::_SC_ATEXIT_MAX,
     /// Maximum obase values allowed by the bc utility.
-    #[cfg(not(target_os = "redox"))]
+    #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     BC_BASE_MAX = libc::_SC_BC_BASE_MAX,
     /// Maximum number of elements permitted in an array by the bc utility.
-    #[cfg(not(target_os = "redox"))]
+    #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     BC_DIM_MAX = libc::_SC_BC_DIM_MAX,
     /// Maximum scale value allowed by the bc utility.
-    #[cfg(not(target_os = "redox"))]
+    #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     BC_SCALE_MAX = libc::_SC_BC_SCALE_MAX,
     /// Maximum length of a string constant accepted by the bc utility.
-    #[cfg(not(target_os = "redox"))]
+    #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     BC_STRING_MAX = libc::_SC_BC_STRING_MAX,
     /// Maximum number of simultaneous processes per real user ID.
     CHILD_MAX = libc::_SC_CHILD_MAX,
@@ -2030,242 +2180,305 @@
     CLK_TCK = libc::_SC_CLK_TCK,
     /// Maximum number of weights that can be assigned to an entry of the
     /// LC_COLLATE order keyword in the locale definition file
-    #[cfg(not(target_os = "redox"))]
+    #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     COLL_WEIGHTS_MAX = libc::_SC_COLL_WEIGHTS_MAX,
     /// Maximum number of timer expiration overruns.
-    #[cfg(not(target_os = "redox"))]
+    #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     DELAYTIMER_MAX = libc::_SC_DELAYTIMER_MAX,
     /// Maximum number of expressions that can be nested within parentheses by
     /// the expr utility.
-    #[cfg(not(target_os = "redox"))]
+    #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     EXPR_NEST_MAX = libc::_SC_EXPR_NEST_MAX,
     #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos",
               target_os = "ios", target_os="linux", target_os = "macos",
               target_os="netbsd", target_os="openbsd", target_os = "solaris"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// Maximum length of a host name (not including the terminating null) as
     /// returned from the `gethostname` function
     HOST_NAME_MAX = libc::_SC_HOST_NAME_MAX,
     /// Maximum number of iovec structures that one process has available for
     /// use with `readv` or `writev`.
     #[cfg(not(target_os = "redox"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     IOV_MAX = libc::_SC_IOV_MAX,
     /// Unless otherwise noted, the maximum length, in bytes, of a utility's
     /// input line (either standard input or another file), when the utility is
     /// described as processing text files. The length includes room for the
-    /// trailing <newline>.
-    #[cfg(not(target_os = "redox"))]
+    /// trailing newline.
+    #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     LINE_MAX = libc::_SC_LINE_MAX,
     /// Maximum length of a login name.
+    #[cfg(not(target_os = "haiku"))]
     LOGIN_NAME_MAX = libc::_SC_LOGIN_NAME_MAX,
     /// Maximum number of simultaneous supplementary group IDs per process.
     NGROUPS_MAX = libc::_SC_NGROUPS_MAX,
     /// Initial size of `getgrgid_r` and `getgrnam_r` data buffers
     #[cfg(not(target_os = "redox"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     GETGR_R_SIZE_MAX = libc::_SC_GETGR_R_SIZE_MAX,
     /// Initial size of `getpwuid_r` and `getpwnam_r` data buffers
     #[cfg(not(target_os = "redox"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     GETPW_R_SIZE_MAX = libc::_SC_GETPW_R_SIZE_MAX,
     /// The maximum number of open message queue descriptors a process may hold.
-    #[cfg(not(target_os = "redox"))]
+    #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     MQ_OPEN_MAX = libc::_SC_MQ_OPEN_MAX,
     /// The maximum number of message priorities supported by the implementation.
-    #[cfg(not(target_os = "redox"))]
+    #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     MQ_PRIO_MAX = libc::_SC_MQ_PRIO_MAX,
     /// A value one greater than the maximum value that the system may assign to
     /// a newly-created file descriptor.
     OPEN_MAX = libc::_SC_OPEN_MAX,
     #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
               target_os="linux", target_os = "macos", target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation supports the Advisory Information option.
     _POSIX_ADVISORY_INFO = libc::_SC_ADVISORY_INFO,
     #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos",
               target_os = "ios", target_os="linux", target_os = "macos",
               target_os="netbsd", target_os="openbsd", target_os = "solaris"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation supports barriers.
     _POSIX_BARRIERS = libc::_SC_BARRIERS,
     /// The implementation supports asynchronous input and output.
-    #[cfg(not(target_os = "redox"))]
+    #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _POSIX_ASYNCHRONOUS_IO = libc::_SC_ASYNCHRONOUS_IO,
     #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos",
               target_os = "ios", target_os="linux", target_os = "macos",
               target_os="netbsd", target_os="openbsd", target_os = "solaris"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation supports clock selection.
     _POSIX_CLOCK_SELECTION = libc::_SC_CLOCK_SELECTION,
     #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos",
               target_os = "ios", target_os="linux", target_os = "macos",
               target_os="netbsd", target_os="openbsd", target_os = "solaris"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation supports the Process CPU-Time Clocks option.
     _POSIX_CPUTIME = libc::_SC_CPUTIME,
     /// The implementation supports the File Synchronization option.
-    #[cfg(not(target_os = "redox"))]
+    #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _POSIX_FSYNC = libc::_SC_FSYNC,
     #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos",
               target_os = "ios", target_os="linux", target_os = "macos",
               target_os="openbsd", target_os = "solaris"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation supports the IPv6 option.
     _POSIX_IPV6 = libc::_SC_IPV6,
     /// The implementation supports job control.
     #[cfg(not(target_os = "redox"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _POSIX_JOB_CONTROL = libc::_SC_JOB_CONTROL,
     /// The implementation supports memory mapped Files.
-    #[cfg(not(target_os = "redox"))]
+    #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _POSIX_MAPPED_FILES = libc::_SC_MAPPED_FILES,
     /// The implementation supports the Process Memory Locking option.
-    #[cfg(not(target_os = "redox"))]
+    #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _POSIX_MEMLOCK = libc::_SC_MEMLOCK,
     /// The implementation supports the Range Memory Locking option.
-    #[cfg(not(target_os = "redox"))]
+    #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _POSIX_MEMLOCK_RANGE = libc::_SC_MEMLOCK_RANGE,
     /// The implementation supports memory protection.
     #[cfg(not(target_os = "redox"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _POSIX_MEMORY_PROTECTION = libc::_SC_MEMORY_PROTECTION,
     /// The implementation supports the Message Passing option.
-    #[cfg(not(target_os = "redox"))]
+    #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _POSIX_MESSAGE_PASSING = libc::_SC_MESSAGE_PASSING,
     /// The implementation supports the Monotonic Clock option.
     #[cfg(not(target_os = "redox"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _POSIX_MONOTONIC_CLOCK = libc::_SC_MONOTONIC_CLOCK,
     #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
               target_os = "illumos", target_os = "ios", target_os="linux",
               target_os = "macos", target_os="openbsd", target_os = "solaris"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation supports the Prioritized Input and Output option.
     _POSIX_PRIORITIZED_IO = libc::_SC_PRIORITIZED_IO,
     /// The implementation supports the Process Scheduling option.
-    #[cfg(not(target_os = "redox"))]
+    #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _POSIX_PRIORITY_SCHEDULING = libc::_SC_PRIORITY_SCHEDULING,
     #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos",
               target_os = "ios", target_os="linux", target_os = "macos",
               target_os="openbsd", target_os = "solaris"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation supports the Raw Sockets option.
     _POSIX_RAW_SOCKETS = libc::_SC_RAW_SOCKETS,
     #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos",
               target_os = "ios", target_os="linux", target_os = "macos",
               target_os="netbsd", target_os="openbsd", target_os = "solaris"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation supports read-write locks.
     _POSIX_READER_WRITER_LOCKS = libc::_SC_READER_WRITER_LOCKS,
     #[cfg(any(target_os = "android", target_os="dragonfly", target_os="freebsd",
               target_os = "ios", target_os="linux", target_os = "macos",
               target_os = "openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation supports realtime signals.
     _POSIX_REALTIME_SIGNALS = libc::_SC_REALTIME_SIGNALS,
     #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos",
               target_os = "ios", target_os="linux", target_os = "macos",
               target_os="netbsd", target_os="openbsd", target_os = "solaris"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation supports the Regular Expression Handling option.
     _POSIX_REGEXP = libc::_SC_REGEXP,
     /// Each process has a saved set-user-ID and a saved set-group-ID.
     #[cfg(not(target_os = "redox"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _POSIX_SAVED_IDS = libc::_SC_SAVED_IDS,
     /// The implementation supports semaphores.
     #[cfg(not(target_os = "redox"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _POSIX_SEMAPHORES = libc::_SC_SEMAPHORES,
     /// The implementation supports the Shared Memory Objects option.
-    #[cfg(not(target_os = "redox"))]
+    #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _POSIX_SHARED_MEMORY_OBJECTS = libc::_SC_SHARED_MEMORY_OBJECTS,
     #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
               target_os="linux", target_os = "macos", target_os="netbsd",
               target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation supports the POSIX shell.
     _POSIX_SHELL = libc::_SC_SHELL,
     #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
               target_os="linux", target_os = "macos", target_os="netbsd",
               target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation supports the Spawn option.
     _POSIX_SPAWN = libc::_SC_SPAWN,
     #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
               target_os="linux", target_os = "macos", target_os="netbsd",
               target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation supports spin locks.
     _POSIX_SPIN_LOCKS = libc::_SC_SPIN_LOCKS,
     #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
               target_os="linux", target_os = "macos", target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation supports the Process Sporadic Server option.
     _POSIX_SPORADIC_SERVER = libc::_SC_SPORADIC_SERVER,
     #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos",
               target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _POSIX_SS_REPL_MAX = libc::_SC_SS_REPL_MAX,
     /// The implementation supports the Synchronized Input and Output option.
-    #[cfg(not(target_os = "redox"))]
+    #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _POSIX_SYNCHRONIZED_IO = libc::_SC_SYNCHRONIZED_IO,
     /// The implementation supports the Thread Stack Address Attribute option.
     #[cfg(not(target_os = "redox"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _POSIX_THREAD_ATTR_STACKADDR = libc::_SC_THREAD_ATTR_STACKADDR,
     /// The implementation supports the Thread Stack Size Attribute option.
     #[cfg(not(target_os = "redox"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _POSIX_THREAD_ATTR_STACKSIZE = libc::_SC_THREAD_ATTR_STACKSIZE,
     #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos",
               target_os="netbsd", target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation supports the Thread CPU-Time Clocks option.
     _POSIX_THREAD_CPUTIME = libc::_SC_THREAD_CPUTIME,
     /// The implementation supports the Non-Robust Mutex Priority Inheritance
     /// option.
-    #[cfg(not(target_os = "redox"))]
+    #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _POSIX_THREAD_PRIO_INHERIT = libc::_SC_THREAD_PRIO_INHERIT,
     /// The implementation supports the Non-Robust Mutex Priority Protection option.
-    #[cfg(not(target_os = "redox"))]
+    #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _POSIX_THREAD_PRIO_PROTECT = libc::_SC_THREAD_PRIO_PROTECT,
     /// The implementation supports the Thread Execution Scheduling option.
     #[cfg(not(target_os = "redox"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _POSIX_THREAD_PRIORITY_SCHEDULING = libc::_SC_THREAD_PRIORITY_SCHEDULING,
     #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
               target_os="linux", target_os = "macos", target_os="netbsd",
               target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation supports the Thread Process-Shared Synchronization
     /// option.
     _POSIX_THREAD_PROCESS_SHARED = libc::_SC_THREAD_PROCESS_SHARED,
     #[cfg(any(target_os="dragonfly", target_os="linux", target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation supports the Robust Mutex Priority Inheritance option.
     _POSIX_THREAD_ROBUST_PRIO_INHERIT = libc::_SC_THREAD_ROBUST_PRIO_INHERIT,
     #[cfg(any(target_os="dragonfly", target_os="linux", target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation supports the Robust Mutex Priority Protection option.
     _POSIX_THREAD_ROBUST_PRIO_PROTECT = libc::_SC_THREAD_ROBUST_PRIO_PROTECT,
     /// The implementation supports thread-safe functions.
-    #[cfg(not(target_os = "redox"))]
+    #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _POSIX_THREAD_SAFE_FUNCTIONS = libc::_SC_THREAD_SAFE_FUNCTIONS,
     #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
               target_os="linux", target_os = "macos", target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation supports the Thread Sporadic Server option.
     _POSIX_THREAD_SPORADIC_SERVER = libc::_SC_THREAD_SPORADIC_SERVER,
     /// The implementation supports threads.
     #[cfg(not(target_os = "redox"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _POSIX_THREADS = libc::_SC_THREADS,
     #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
               target_os="linux", target_os = "macos", target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation supports timeouts.
     _POSIX_TIMEOUTS = libc::_SC_TIMEOUTS,
     /// The implementation supports timers.
     #[cfg(not(target_os = "redox"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _POSIX_TIMERS = libc::_SC_TIMERS,
     #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
               target_os="linux", target_os = "macos", target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation supports the Trace option.
     _POSIX_TRACE = libc::_SC_TRACE,
     #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
               target_os="linux", target_os = "macos", target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation supports the Trace Event Filter option.
     _POSIX_TRACE_EVENT_FILTER = libc::_SC_TRACE_EVENT_FILTER,
     #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos",
               target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _POSIX_TRACE_EVENT_NAME_MAX = libc::_SC_TRACE_EVENT_NAME_MAX,
     #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
               target_os="linux", target_os = "macos", target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation supports the Trace Inherit option.
     _POSIX_TRACE_INHERIT = libc::_SC_TRACE_INHERIT,
     #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
               target_os="linux", target_os = "macos", target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation supports the Trace Log option.
     _POSIX_TRACE_LOG = libc::_SC_TRACE_LOG,
     #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos",
               target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _POSIX_TRACE_NAME_MAX = libc::_SC_TRACE_NAME_MAX,
     #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos",
               target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _POSIX_TRACE_SYS_MAX = libc::_SC_TRACE_SYS_MAX,
     #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos",
               target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _POSIX_TRACE_USER_EVENT_MAX = libc::_SC_TRACE_USER_EVENT_MAX,
     #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
               target_os="linux", target_os = "macos", target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation supports the Typed Memory Objects option.
     _POSIX_TYPED_MEMORY_OBJECTS = libc::_SC_TYPED_MEMORY_OBJECTS,
     /// Integer value indicating version of this standard (C-language binding)
@@ -2275,12 +2488,14 @@
     #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
               target_os="linux", target_os = "macos", target_os="netbsd",
               target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation provides a C-language compilation environment with
     /// 32-bit `int`, `long`, `pointer`, and `off_t` types.
     _POSIX_V6_ILP32_OFF32 = libc::_SC_V6_ILP32_OFF32,
     #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
               target_os="linux", target_os = "macos", target_os="netbsd",
               target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation provides a C-language compilation environment with
     /// 32-bit `int`, `long`, and pointer types and an `off_t` type using at
     /// least 64 bits.
@@ -2288,157 +2503,207 @@
     #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
               target_os="linux", target_os = "macos", target_os="netbsd",
               target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation provides a C-language compilation environment with
     /// 32-bit `int` and 64-bit `long`, `pointer`, and `off_t` types.
     _POSIX_V6_LP64_OFF64 = libc::_SC_V6_LP64_OFF64,
     #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
               target_os="linux", target_os = "macos", target_os="netbsd",
               target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation provides a C-language compilation environment with an
     /// `int` type using at least 32 bits and `long`, pointer, and `off_t` types
     /// using at least 64 bits.
     _POSIX_V6_LPBIG_OFFBIG = libc::_SC_V6_LPBIG_OFFBIG,
     /// The implementation supports the C-Language Binding option.
-    #[cfg(not(target_os = "redox"))]
+    #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _POSIX2_C_BIND = libc::_SC_2_C_BIND,
     /// The implementation supports the C-Language Development Utilities option.
-    #[cfg(not(target_os = "redox"))]
+    #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _POSIX2_C_DEV = libc::_SC_2_C_DEV,
     /// The implementation supports the Terminal Characteristics option.
-    #[cfg(not(target_os = "redox"))]
+    #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _POSIX2_CHAR_TERM = libc::_SC_2_CHAR_TERM,
     /// The implementation supports the FORTRAN Development Utilities option.
-    #[cfg(not(target_os = "redox"))]
+    #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _POSIX2_FORT_DEV = libc::_SC_2_FORT_DEV,
     /// The implementation supports the FORTRAN Runtime Utilities option.
-    #[cfg(not(target_os = "redox"))]
+    #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _POSIX2_FORT_RUN = libc::_SC_2_FORT_RUN,
     /// The implementation supports the creation of locales by the localedef
     /// utility.
-    #[cfg(not(target_os = "redox"))]
+    #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _POSIX2_LOCALEDEF = libc::_SC_2_LOCALEDEF,
     #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
               target_os="linux", target_os = "macos", target_os="netbsd",
               target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation supports the Batch Environment Services and Utilities
     /// option.
     _POSIX2_PBS = libc::_SC_2_PBS,
     #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
               target_os="linux", target_os = "macos", target_os="netbsd",
               target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation supports the Batch Accounting option.
     _POSIX2_PBS_ACCOUNTING = libc::_SC_2_PBS_ACCOUNTING,
     #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
               target_os="linux", target_os = "macos", target_os="netbsd",
               target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation supports the Batch Checkpoint/Restart option.
     _POSIX2_PBS_CHECKPOINT = libc::_SC_2_PBS_CHECKPOINT,
     #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
               target_os="linux", target_os = "macos", target_os="netbsd",
               target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation supports the Locate Batch Job Request option.
     _POSIX2_PBS_LOCATE = libc::_SC_2_PBS_LOCATE,
     #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
               target_os="linux", target_os = "macos", target_os="netbsd",
               target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation supports the Batch Job Message Request option.
     _POSIX2_PBS_MESSAGE = libc::_SC_2_PBS_MESSAGE,
     #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
               target_os="linux", target_os = "macos", target_os="netbsd",
               target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation supports the Track Batch Job Request option.
     _POSIX2_PBS_TRACK = libc::_SC_2_PBS_TRACK,
     /// The implementation supports the Software Development Utilities option.
-    #[cfg(not(target_os = "redox"))]
+    #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _POSIX2_SW_DEV = libc::_SC_2_SW_DEV,
     /// The implementation supports the User Portability Utilities option.
-    #[cfg(not(target_os = "redox"))]
+    #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _POSIX2_UPE = libc::_SC_2_UPE,
     /// Integer value indicating version of the Shell and Utilities volume of
     /// POSIX.1 to which the implementation conforms.
-    #[cfg(not(target_os = "redox"))]
+    #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _POSIX2_VERSION = libc::_SC_2_VERSION,
     /// The size of a system page in bytes.
     ///
     /// POSIX also defines an alias named `PAGESIZE`, but Rust does not allow two
     /// enum constants to have the same value, so nix omits `PAGESIZE`.
     PAGE_SIZE = libc::_SC_PAGE_SIZE,
-    #[cfg(not(target_os = "redox"))]
+    #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     PTHREAD_DESTRUCTOR_ITERATIONS = libc::_SC_THREAD_DESTRUCTOR_ITERATIONS,
-    #[cfg(not(target_os = "redox"))]
+    #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     PTHREAD_KEYS_MAX = libc::_SC_THREAD_KEYS_MAX,
     #[cfg(not(target_os = "redox"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     PTHREAD_STACK_MIN = libc::_SC_THREAD_STACK_MIN,
-    #[cfg(not(target_os = "redox"))]
+    #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     PTHREAD_THREADS_MAX = libc::_SC_THREAD_THREADS_MAX,
+    #[cfg(not(target_os = "haiku"))]
     RE_DUP_MAX = libc::_SC_RE_DUP_MAX,
     #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
               target_os = "ios", target_os="linux", target_os = "macos",
               target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     RTSIG_MAX = libc::_SC_RTSIG_MAX,
     #[cfg(not(target_os = "redox"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     SEM_NSEMS_MAX = libc::_SC_SEM_NSEMS_MAX,
     #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
               target_os = "ios", target_os="linux", target_os = "macos",
               target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     SEM_VALUE_MAX = libc::_SC_SEM_VALUE_MAX,
     #[cfg(any(target_os = "android", target_os="dragonfly", target_os="freebsd",
               target_os = "ios", target_os="linux", target_os = "macos",
               target_os = "openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     SIGQUEUE_MAX = libc::_SC_SIGQUEUE_MAX,
     STREAM_MAX = libc::_SC_STREAM_MAX,
     #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
               target_os="linux", target_os = "macos", target_os="netbsd",
               target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     SYMLOOP_MAX = libc::_SC_SYMLOOP_MAX,
     #[cfg(not(target_os = "redox"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     TIMER_MAX = libc::_SC_TIMER_MAX,
     TTY_NAME_MAX = libc::_SC_TTY_NAME_MAX,
     TZNAME_MAX = libc::_SC_TZNAME_MAX,
     #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
               target_os = "ios", target_os="linux", target_os = "macos",
               target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation supports the X/Open Encryption Option Group.
     _XOPEN_CRYPT = libc::_SC_XOPEN_CRYPT,
     #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
               target_os = "ios", target_os="linux", target_os = "macos",
               target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation supports the Issue 4, Version 2 Enhanced
     /// Internationalization Option Group.
     _XOPEN_ENH_I18N = libc::_SC_XOPEN_ENH_I18N,
     #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
               target_os = "ios", target_os="linux", target_os = "macos",
               target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _XOPEN_LEGACY = libc::_SC_XOPEN_LEGACY,
     #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
               target_os = "ios", target_os="linux", target_os = "macos",
               target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation supports the X/Open Realtime Option Group.
     _XOPEN_REALTIME = libc::_SC_XOPEN_REALTIME,
     #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
               target_os = "ios", target_os="linux", target_os = "macos",
               target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation supports the X/Open Realtime Threads Option Group.
     _XOPEN_REALTIME_THREADS = libc::_SC_XOPEN_REALTIME_THREADS,
     /// The implementation supports the Issue 4, Version 2 Shared Memory Option
     /// Group.
-    #[cfg(not(target_os = "redox"))]
+    #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     _XOPEN_SHM = libc::_SC_XOPEN_SHM,
     #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
               target_os="linux", target_os = "macos", target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation supports the XSI STREAMS Option Group.
     _XOPEN_STREAMS = libc::_SC_XOPEN_STREAMS,
     #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
               target_os = "ios", target_os="linux", target_os = "macos",
               target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// The implementation supports the XSI option
     _XOPEN_UNIX = libc::_SC_XOPEN_UNIX,
     #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
               target_os = "ios", target_os="linux", target_os = "macos",
               target_os="openbsd"))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     /// Integer value indicating version of the X/Open Portability Guide to
     /// which the implementation conforms.
     _XOPEN_VERSION = libc::_SC_XOPEN_VERSION,
+    /// The number of pages of physical memory. Note that it is possible for
+    /// the product of this value to overflow.
+    #[cfg(any(target_os="android", target_os="linux"))]
+    _PHYS_PAGES = libc::_SC_PHYS_PAGES,
+    /// The number of currently available pages of physical memory.
+    #[cfg(any(target_os="android", target_os="linux"))]
+    _AVPHYS_PAGES = libc::_SC_AVPHYS_PAGES,
+    /// The number of processors configured.
+    #[cfg(any(target_os="android", target_os="linux"))]
+    _NPROCESSORS_CONF = libc::_SC_NPROCESSORS_CONF,
+    /// The number of processors currently online (available).
+    #[cfg(any(target_os="android", target_os="linux"))]
+    _NPROCESSORS_ONLN = libc::_SC_NPROCESSORS_ONLN,
 }
 
 /// Get configurable system variables (see
@@ -2472,6 +2737,10 @@
         Ok(Some(raw))
     }
 }
+}
+
+feature! {
+#![feature = "fs"]
 
 #[cfg(any(target_os = "android", target_os = "linux"))]
 mod pivot_root {
@@ -2491,10 +2760,19 @@
         Errno::result(res).map(drop)
     }
 }
+}
 
-#[cfg(any(target_os = "android", target_os = "freebsd",
-          target_os = "linux", target_os = "openbsd"))]
+#[cfg(any(
+    target_os = "android",
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "linux",
+    target_os = "openbsd"
+))]
 mod setres {
+    feature! {
+    #![feature = "user"]
+
     use crate::Result;
     use crate::errno::Errno;
     use super::{Uid, Gid};
@@ -2530,10 +2808,20 @@
 
         Errno::result(res).map(drop)
     }
+    }
 }
 
-#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg(any(
+    target_os = "android",
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "linux",
+    target_os = "openbsd"
+))]
 mod getres {
+    feature! {
+    #![feature = "user"]
+
     use crate::Result;
     use crate::errno::Errno;
     use super::{Uid, Gid};
@@ -2591,10 +2879,13 @@
 
         Errno::result(res).map(|_| ResGid { real: Gid(rgid), effective: Gid(egid), saved: Gid(sgid) } )
     }
+    }
 }
 
-libc_bitflags!{
+#[cfg(feature = "fs")]
+libc_bitflags! {
     /// Options for access()
+    #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
     pub struct AccessFlags : c_int {
         /// Test for existence of file.
         F_OK;
@@ -2607,6 +2898,9 @@
     }
 }
 
+feature! {
+#![feature = "fs"]
+
 /// Checks the file named by `path` for accessibility according to the flags given by `amode`
 /// See [access(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/access.html)
 pub fn access<P: ?Sized + NixPath>(path: &P, amode: AccessFlags) -> Result<()> {
@@ -2618,6 +2912,51 @@
     Errno::result(res).map(drop)
 }
 
+/// Checks the file named by `path` for accessibility according to the flags given by `mode`
+///
+/// If `dirfd` has a value, then `path` is relative to directory associated with the file descriptor.
+///
+/// If `dirfd` is `None`, then `path` is relative to the current working directory.
+///
+/// # References
+///
+/// [faccessat(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/faccessat.html)
+// redox: does not appear to support the *at family of syscalls.
+#[cfg(not(target_os = "redox"))]
+pub fn faccessat<P: ?Sized + NixPath>(dirfd: Option<RawFd>, path: &P, mode: AccessFlags, flags: AtFlags) -> Result<()> {
+    let res = path.with_nix_path(|cstr| {
+        unsafe {
+            libc::faccessat(at_rawfd(dirfd), cstr.as_ptr(), mode.bits(), flags.bits())
+        }
+    })?;
+    Errno::result(res).map(drop)
+}
+
+/// Checks the file named by `path` for accessibility according to the flags given
+/// by `mode` using effective UID, effective GID and supplementary group lists.
+///
+/// # References
+///
+/// * [FreeBSD man page](https://www.freebsd.org/cgi/man.cgi?query=eaccess&sektion=2&n=1)
+/// * [Linux man page](https://man7.org/linux/man-pages/man3/euidaccess.3.html)
+#[cfg(any(
+    all(target_os = "linux", not(target_env = "uclibc")),
+    target_os = "freebsd",
+    target_os = "dragonfly"
+))]
+pub fn eaccess<P: ?Sized + NixPath>(path: &P, mode: AccessFlags) -> Result<()> {
+    let res = path.with_nix_path(|cstr| {
+        unsafe {
+            libc::eaccess(cstr.as_ptr(), mode.bits)
+        }
+    })?;
+    Errno::result(res).map(drop)
+}
+}
+
+feature! {
+#![feature = "user"]
+
 /// Representation of a User, based on `libc::passwd`
 ///
 /// The reason some fields in this struct are `String` and others are `CString` is because some
@@ -2625,11 +2964,11 @@
 /// guaranteed to conform to [`NAME_REGEX`](https://serverfault.com/a/73101/407341), which only
 /// contains ASCII.
 #[cfg(not(target_os = "redox"))] // RedoxFS does not support passwd
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, Eq, PartialEq)]
 pub struct User {
     /// Username
     pub name: String,
-    /// User password (probably encrypted)
+    /// User password (probably hashed)
     pub passwd: CString,
     /// User ID
     pub uid: Uid,
@@ -2645,57 +2984,66 @@
     /// Login class
     #[cfg(not(any(target_os = "android",
                   target_os = "fuchsia",
+                  target_os = "haiku",
                   target_os = "illumos",
                   target_os = "linux",
                   target_os = "solaris")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub class: CString,
     /// Last password change
     #[cfg(not(any(target_os = "android",
                   target_os = "fuchsia",
+                  target_os = "haiku",
                   target_os = "illumos",
                   target_os = "linux",
                   target_os = "solaris")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub change: libc::time_t,
     /// Expiration time of account
     #[cfg(not(any(target_os = "android",
                   target_os = "fuchsia",
+                  target_os = "haiku",
                   target_os = "illumos",
                   target_os = "linux",
                   target_os = "solaris")))]
+    #[cfg_attr(docsrs, doc(cfg(all())))]
     pub expire: libc::time_t
 }
 
-#[cfg(not(target_os = "redox"))] // RedoxFS does not support passwd
+#[cfg(not(target_os = "redox"))] //RedoxFS does not support passwd
 impl From<&libc::passwd> for User {
     fn from(pw: &libc::passwd) -> User {
         unsafe {
             User {
-                name: CStr::from_ptr((*pw).pw_name).to_string_lossy().into_owned(),
-                passwd: CString::new(CStr::from_ptr((*pw).pw_passwd).to_bytes()).unwrap(),
+                name: if pw.pw_name.is_null() { Default::default() } else { CStr::from_ptr(pw.pw_name).to_string_lossy().into_owned() },
+                passwd: if pw.pw_passwd.is_null() { Default::default() } else { CString::new(CStr::from_ptr(pw.pw_passwd).to_bytes()).unwrap() },
                 #[cfg(not(all(target_os = "android", target_pointer_width = "32")))]
-                gecos: CString::new(CStr::from_ptr((*pw).pw_gecos).to_bytes()).unwrap(),
-                dir: PathBuf::from(OsStr::from_bytes(CStr::from_ptr((*pw).pw_dir).to_bytes())),
-                shell: PathBuf::from(OsStr::from_bytes(CStr::from_ptr((*pw).pw_shell).to_bytes())),
-                uid: Uid::from_raw((*pw).pw_uid),
-                gid: Gid::from_raw((*pw).pw_gid),
+                gecos: if pw.pw_gecos.is_null() { Default::default() } else { CString::new(CStr::from_ptr(pw.pw_gecos).to_bytes()).unwrap() },
+                dir: if pw.pw_dir.is_null() { Default::default() } else { PathBuf::from(OsStr::from_bytes(CStr::from_ptr(pw.pw_dir).to_bytes())) },
+                shell: if pw.pw_shell.is_null() { Default::default() } else { PathBuf::from(OsStr::from_bytes(CStr::from_ptr(pw.pw_shell).to_bytes())) },
+                uid: Uid::from_raw(pw.pw_uid),
+                gid: Gid::from_raw(pw.pw_gid),
                 #[cfg(not(any(target_os = "android",
                               target_os = "fuchsia",
+                              target_os = "haiku",
                               target_os = "illumos",
                               target_os = "linux",
                               target_os = "solaris")))]
-                class: CString::new(CStr::from_ptr((*pw).pw_class).to_bytes()).unwrap(),
+                class: CString::new(CStr::from_ptr(pw.pw_class).to_bytes()).unwrap(),
                 #[cfg(not(any(target_os = "android",
                               target_os = "fuchsia",
+                              target_os = "haiku",
                               target_os = "illumos",
                               target_os = "linux",
                               target_os = "solaris")))]
-                change: (*pw).pw_change,
+                change: pw.pw_change,
                 #[cfg(not(any(target_os = "android",
                               target_os = "fuchsia",
+                              target_os = "haiku",
                               target_os = "illumos",
                               target_os = "linux",
                               target_os = "solaris")))]
-                expire: (*pw).pw_expire
+                expire: pw.pw_expire
             }
         }
     }
@@ -2727,18 +3075,21 @@
             pw_gid: u.gid.0,
             #[cfg(not(any(target_os = "android",
                           target_os = "fuchsia",
+                          target_os = "haiku",
                           target_os = "illumos",
                           target_os = "linux",
                           target_os = "solaris")))]
             pw_class: u.class.into_raw(),
             #[cfg(not(any(target_os = "android",
                           target_os = "fuchsia",
+                          target_os = "haiku",
                           target_os = "illumos",
                           target_os = "linux",
                           target_os = "solaris")))]
             pw_change: u.change,
             #[cfg(not(any(target_os = "android",
                           target_os = "fuchsia",
+                          target_os = "haiku",
                           target_os = "illumos",
                           target_os = "linux",
                           target_os = "solaris")))]
@@ -2758,7 +3109,7 @@
     fn from_anything<F>(f: F) -> Result<Option<Self>>
     where
         F: Fn(*mut libc::passwd,
-              *mut libc::c_char,
+              *mut c_char,
               libc::size_t,
               *mut *mut libc::passwd) -> libc::c_int
     {
@@ -2801,7 +3152,7 @@
     /// use nix::unistd::{Uid, User};
     /// // Returns an Result<Option<User>>, thus the double unwrap.
     /// let res = User::from_uid(Uid::from_raw(0)).unwrap().unwrap();
-    /// assert!(res.name == "root");
+    /// assert_eq!(res.name, "root");
     /// ```
     pub fn from_uid(uid: Uid) -> Result<Option<Self>> {
         User::from_anything(|pwd, cbuf, cap, res| {
@@ -2820,10 +3171,13 @@
     /// use nix::unistd::User;
     /// // Returns an Result<Option<User>>, thus the double unwrap.
     /// let res = User::from_name("root").unwrap().unwrap();
-    /// assert!(res.name == "root");
+    /// assert_eq!(res.name, "root");
     /// ```
     pub fn from_name(name: &str) -> Result<Option<Self>> {
-        let name = CString::new(name).unwrap();
+        let name = match CString::new(name) {
+            Ok(c_str) => c_str,
+            Err(_nul_error) => return Ok(None),
+        };
         User::from_anything(|pwd, cbuf, cap, res| {
             unsafe { libc::getpwnam_r(name.as_ptr(), pwd, cbuf, cap, res) }
         })
@@ -2832,7 +3186,7 @@
 
 /// Representation of a Group, based on `libc::group`
 #[cfg(not(target_os = "redox"))] // RedoxFS does not support passwd
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, Eq, PartialEq)]
 pub struct Group {
     /// Group name
     pub name: String,
@@ -2849,10 +3203,10 @@
     fn from(gr: &libc::group) -> Group {
         unsafe {
             Group {
-                name: CStr::from_ptr((*gr).gr_name).to_string_lossy().into_owned(),
-                passwd: CString::new(CStr::from_ptr((*gr).gr_passwd).to_bytes()).unwrap(),
-                gid: Gid::from_raw((*gr).gr_gid),
-                mem: Group::members((*gr).gr_mem)
+                name: CStr::from_ptr(gr.gr_name).to_string_lossy().into_owned(),
+                passwd: CString::new(CStr::from_ptr(gr.gr_passwd).to_bytes()).unwrap(),
+                gid: Gid::from_raw(gr.gr_gid),
+                mem: Group::members(gr.gr_mem)
             }
         }
     }
@@ -2879,7 +3233,7 @@
     fn from_anything<F>(f: F) -> Result<Option<Self>>
     where
         F: Fn(*mut libc::group,
-              *mut libc::c_char,
+              *mut c_char,
               libc::size_t,
               *mut *mut libc::group) -> libc::c_int
     {
@@ -2948,12 +3302,19 @@
     /// assert!(res.name == "root");
     /// ```
     pub fn from_name(name: &str) -> Result<Option<Self>> {
-        let name = CString::new(name).unwrap();
+        let name = match CString::new(name) {
+            Ok(c_str) => c_str,
+            Err(_nul_error) => return Ok(None),
+        };
         Group::from_anything(|grp, cbuf, cap, res| {
             unsafe { libc::getgrnam_r(name.as_ptr(), grp, cbuf, cap, res) }
         })
     }
 }
+}
+
+feature! {
+#![feature = "term"]
 
 /// Get the name of the terminal device that is open on file descriptor fd
 /// (see [`ttyname(3)`](https://man7.org/linux/man-pages/man3/ttyname.3.html)).
@@ -2972,6 +3333,10 @@
     buf.truncate(nul);
     Ok(OsString::from_vec(buf).into())
 }
+}
+
+feature! {
+#![all(feature = "socket", feature = "user")]
 
 /// Get the effective user ID and group ID associated with a Unix domain socket.
 ///
@@ -2992,3 +3357,27 @@
 
     Errno::result(ret).map(|_| (Uid(uid), Gid(gid)))
 }
+}
+
+feature! {
+#![all(feature = "fs")]
+
+/// Set the file flags.
+///
+/// See also [chflags(2)](https://www.freebsd.org/cgi/man.cgi?query=chflags&sektion=2)
+#[cfg(any(
+    target_os = "openbsd",
+    target_os = "netbsd",
+    target_os = "freebsd",
+    target_os = "dragonfly",
+    target_os = "macos",
+    target_os = "ios"
+))]
+pub fn chflags<P: ?Sized + NixPath>(path: &P, flags: FileFlag) -> Result<()> {
+    let res = path.with_nix_path(|cstr| unsafe {
+        libc::chflags(cstr.as_ptr(), flags.bits())
+    })?;
+
+    Errno::result(res).map(drop)
+}
+}
diff --git a/test/common/mod.rs b/test/common/mod.rs
index 84a0b4f..bb056aa 100644
--- a/test/common/mod.rs
+++ b/test/common/mod.rs
@@ -1,6 +1,7 @@
 use cfg_if::cfg_if;
 
-#[macro_export] macro_rules! skip {
+#[macro_export]
+macro_rules! skip {
     ($($reason: expr),+) => {
         use ::std::io::{self, Write};
 
@@ -33,42 +34,49 @@
 
 /// Skip the test if we don't have the ability to mount file systems.
 #[cfg(target_os = "freebsd")]
-#[macro_export] macro_rules! require_mount {
+#[macro_export]
+macro_rules! require_mount {
     ($name:expr) => {
-        use ::sysctl::CtlValue;
+        use ::sysctl::{CtlValue, Sysctl};
         use nix::unistd::Uid;
 
-        if !Uid::current().is_root() && CtlValue::Int(0) == ::sysctl::value("vfs.usermount").unwrap()
+        let ctl = ::sysctl::Ctl::new("vfs.usermount").unwrap();
+        if !Uid::current().is_root() && CtlValue::Int(0) == ctl.value().unwrap()
         {
-            skip!("{} requires the ability to mount file systems. Skipping test.", $name);
+            skip!(
+                "{} requires the ability to mount file systems. Skipping test.",
+                $name
+            );
         }
-    }
+    };
 }
 
-#[cfg(any(target_os = "linux", target_os= "android"))]
-#[macro_export] macro_rules! skip_if_cirrus {
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[macro_export]
+macro_rules! skip_if_cirrus {
     ($reason:expr) => {
         if std::env::var_os("CIRRUS_CI").is_some() {
             skip!("{}", $reason);
         }
-    }
+    };
 }
 
 #[cfg(target_os = "freebsd")]
-#[macro_export] macro_rules! skip_if_jailed {
+#[macro_export]
+macro_rules! skip_if_jailed {
     ($name:expr) => {
-        use ::sysctl::CtlValue;
+        use ::sysctl::{CtlValue, Sysctl};
 
-        if let CtlValue::Int(1) = ::sysctl::value("security.jail.jailed")
-            .unwrap()
-        {
+        let ctl = ::sysctl::Ctl::new("security.jail.jailed").unwrap();
+        if let CtlValue::Int(1) = ctl.value().unwrap() {
             skip!("{} cannot run in a jail. Skipping test.", $name);
         }
-    }
+    };
 }
 
 #[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
-#[macro_export] macro_rules! skip_if_not_root {
+#[macro_export]
+macro_rules! skip_if_not_root {
     ($name:expr) => {
         use nix::unistd::Uid;
 
@@ -111,15 +119,15 @@
                 let version_requirement = VersionReq::parse($version_requirement)
                         .expect("Bad match_version provided");
 
-                let uname = nix::sys::utsname::uname();
-                println!("{}", uname.sysname());
-                println!("{}", uname.nodename());
-                println!("{}", uname.release());
-                println!("{}", uname.version());
-                println!("{}", uname.machine());
+                let uname = nix::sys::utsname::uname().unwrap();
+                println!("{}", uname.sysname().to_str().unwrap());
+                println!("{}", uname.nodename().to_str().unwrap());
+                println!("{}", uname.release().to_str().unwrap());
+                println!("{}", uname.version().to_str().unwrap());
+                println!("{}", uname.machine().to_str().unwrap());
 
                 // Fix stuff that the semver parser can't handle
-                let fixed_release = &uname.release().to_string()
+                let fixed_release = &uname.release().to_str().unwrap().to_string()
                     // Fedora 33 reports version as 4.18.el8_2.x86_64 or
                     // 5.18.200-fc33.x86_64.  Remove the underscore.
                     .replace("_", "-")
diff --git a/test/sys/mod.rs b/test/sys/mod.rs
index e73d9b1..2031212 100644
--- a/test/sys/mod.rs
+++ b/test/sys/mod.rs
@@ -5,43 +5,56 @@
 // works or not heavily depends on which pthread implementation is chosen
 // by the user at link time. For this reason we do not want to run aio test
 // cases on DragonFly.
-#[cfg(any(target_os = "freebsd",
-          target_os = "ios",
-          target_os = "linux",
-          target_os = "macos",
-          target_os = "netbsd"))]
+#[cfg(any(
+    target_os = "freebsd",
+    target_os = "ios",
+    all(target_os = "linux", not(target_env = "uclibc")),
+    target_os = "macos",
+    target_os = "netbsd"
+))]
 mod test_aio;
+#[cfg(not(any(
+    target_os = "redox",
+    target_os = "fuchsia",
+    target_os = "haiku"
+)))]
+mod test_ioctl;
 #[cfg(not(target_os = "redox"))]
 mod test_mman;
-#[cfg(target_os = "linux")]
-mod test_signalfd;
-#[cfg(not(target_os = "redox"))]
-mod test_socket;
-#[cfg(not(target_os = "redox"))]
-mod test_sockopt;
 #[cfg(not(target_os = "redox"))]
 mod test_select;
+#[cfg(target_os = "linux")]
+mod test_signalfd;
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+mod test_socket;
+#[cfg(not(any(target_os = "redox")))]
+mod test_sockopt;
+mod test_stat;
 #[cfg(any(target_os = "android", target_os = "linux"))]
 mod test_sysinfo;
-#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
+#[cfg(not(any(
+    target_os = "redox",
+    target_os = "fuchsia",
+    target_os = "haiku"
+)))]
 mod test_termios;
-#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
-mod test_ioctl;
-mod test_wait;
 mod test_uio;
+mod test_wait;
 
-#[cfg(target_os = "linux")]
+#[cfg(any(target_os = "android", target_os = "linux"))]
 mod test_epoll;
 #[cfg(target_os = "linux")]
 mod test_inotify;
 mod test_pthread;
-#[cfg(any(target_os = "android",
-          target_os = "dragonfly",
-          target_os = "freebsd",
-          target_os = "linux",
-          target_os = "macos",
-          target_os = "netbsd",
-          target_os = "openbsd"))]
+#[cfg(any(
+    target_os = "android",
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "linux",
+    target_os = "macos",
+    target_os = "netbsd",
+    target_os = "openbsd"
+))]
 mod test_ptrace;
 #[cfg(any(target_os = "android", target_os = "linux"))]
 mod test_timerfd;
diff --git a/test/sys/test_aio.rs b/test/sys/test_aio.rs
index 80cd053..84086f8 100644
--- a/test/sys/test_aio.rs
+++ b/test/sys/test_aio.rs
@@ -1,83 +1,546 @@
-use libc::{c_int, c_void};
-use nix::Result;
-use nix::errno::*;
-use nix::sys::aio::*;
-use nix::sys::signal::{SaFlags, SigAction, sigaction, SigevNotify, SigHandler, Signal, SigSet};
-use nix::sys::time::{TimeSpec, TimeValLike};
-use std::io::{Write, Read, Seek, SeekFrom};
-use std::ops::Deref;
-use std::os::unix::io::AsRawFd;
-use std::pin::Pin;
-use std::sync::atomic::{AtomicBool, Ordering};
-use std::{thread, time};
+use std::{
+    io::{Read, Seek, Write},
+    ops::Deref,
+    os::unix::io::AsRawFd,
+    pin::Pin,
+    sync::atomic::{AtomicBool, Ordering},
+    thread, time,
+};
+
+use libc::c_int;
+use nix::{
+    errno::*,
+    sys::{
+        aio::*,
+        signal::{
+            sigaction, SaFlags, SigAction, SigHandler, SigSet, SigevNotify,
+            Signal,
+        },
+        time::{TimeSpec, TimeValLike},
+    },
+};
 use tempfile::tempfile;
 
+lazy_static! {
+    pub static ref SIGNALED: AtomicBool = AtomicBool::new(false);
+}
+
+extern "C" fn sigfunc(_: c_int) {
+    SIGNALED.store(true, Ordering::Relaxed);
+}
+
 // Helper that polls an AioCb for completion or error
-fn poll_aio(aiocb: &mut Pin<Box<AioCb>>) -> Result<()> {
-    loop {
-        let err = aiocb.error();
-        if err != Err(Errno::EINPROGRESS) { return err; };
-        thread::sleep(time::Duration::from_millis(10));
+macro_rules! poll_aio {
+    ($aiocb: expr) => {
+        loop {
+            let err = $aiocb.as_mut().error();
+            if err != Err(Errno::EINPROGRESS) {
+                break err;
+            };
+            thread::sleep(time::Duration::from_millis(10));
+        }
+    };
+}
+
+mod aio_fsync {
+    use super::*;
+
+    #[test]
+    fn test_accessors() {
+        let aiocb = AioFsync::new(
+            1001,
+            AioFsyncMode::O_SYNC,
+            42,
+            SigevNotify::SigevSignal {
+                signal: Signal::SIGUSR2,
+                si_value: 99,
+            },
+        );
+        assert_eq!(1001, aiocb.fd());
+        assert_eq!(AioFsyncMode::O_SYNC, aiocb.mode());
+        assert_eq!(42, aiocb.priority());
+        let sev = aiocb.sigevent().sigevent();
+        assert_eq!(Signal::SIGUSR2 as i32, sev.sigev_signo);
+        assert_eq!(99, sev.sigev_value.sival_ptr as i64);
+    }
+
+    /// `AioFsync::submit` should not modify the `AioCb` object if
+    /// `libc::aio_fsync` returns an error
+    // Skip on Linux, because Linux's AIO implementation can't detect errors
+    // synchronously
+    #[test]
+    #[cfg(any(target_os = "freebsd", target_os = "macos"))]
+    fn error() {
+        use std::mem;
+
+        const INITIAL: &[u8] = b"abcdef123456";
+        // Create an invalid AioFsyncMode
+        let mode = unsafe { mem::transmute(666) };
+        let mut f = tempfile().unwrap();
+        f.write_all(INITIAL).unwrap();
+        let mut aiof = Box::pin(AioFsync::new(
+            f.as_raw_fd(),
+            mode,
+            0,
+            SigevNotify::SigevNone,
+        ));
+        let err = aiof.as_mut().submit();
+        err.expect_err("assertion failed");
+    }
+
+    #[test]
+    #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
+    fn ok() {
+        const INITIAL: &[u8] = b"abcdef123456";
+        let mut f = tempfile().unwrap();
+        f.write_all(INITIAL).unwrap();
+        let fd = f.as_raw_fd();
+        let mut aiof = Box::pin(AioFsync::new(
+            fd,
+            AioFsyncMode::O_SYNC,
+            0,
+            SigevNotify::SigevNone,
+        ));
+        aiof.as_mut().submit().unwrap();
+        poll_aio!(&mut aiof).unwrap();
+        aiof.as_mut().aio_return().unwrap();
     }
 }
 
-// Helper that polls a component of an LioCb for completion or error
-#[cfg(not(any(target_os = "ios", target_os = "macos")))]
-fn poll_lio(liocb: &mut LioCb, i: usize) -> Result<()> {
-    loop {
-        let err = liocb.error(i);
-        if err != Err(Errno::EINPROGRESS) { return err; };
-        thread::sleep(time::Duration::from_millis(10));
+mod aio_read {
+    use super::*;
+
+    #[test]
+    fn test_accessors() {
+        let mut rbuf = vec![0; 4];
+        let aiocb = AioRead::new(
+            1001,
+            2, //offset
+            &mut rbuf,
+            42, //priority
+            SigevNotify::SigevSignal {
+                signal: Signal::SIGUSR2,
+                si_value: 99,
+            },
+        );
+        assert_eq!(1001, aiocb.fd());
+        assert_eq!(4, aiocb.nbytes());
+        assert_eq!(2, aiocb.offset());
+        assert_eq!(42, aiocb.priority());
+        let sev = aiocb.sigevent().sigevent();
+        assert_eq!(Signal::SIGUSR2 as i32, sev.sigev_signo);
+        assert_eq!(99, sev.sigev_value.sival_ptr as i64);
+    }
+
+    // Tests AioWrite.cancel.  We aren't trying to test the OS's implementation,
+    // only our bindings.  So it's sufficient to check that cancel
+    // returned any AioCancelStat value.
+    #[test]
+    #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
+    fn cancel() {
+        const INITIAL: &[u8] = b"abcdef123456";
+        let mut rbuf = vec![0; 4];
+        let mut f = tempfile().unwrap();
+        f.write_all(INITIAL).unwrap();
+        let fd = f.as_raw_fd();
+        let mut aior =
+            Box::pin(AioRead::new(fd, 2, &mut rbuf, 0, SigevNotify::SigevNone));
+        aior.as_mut().submit().unwrap();
+
+        aior.as_mut().cancel().unwrap();
+
+        // Wait for aiow to complete, but don't care whether it succeeded
+        let _ = poll_aio!(&mut aior);
+        let _ = aior.as_mut().aio_return();
+    }
+
+    /// `AioRead::submit` should not modify the `AioCb` object if
+    /// `libc::aio_read` returns an error
+    // Skip on Linux, because Linux's AIO implementation can't detect errors
+    // synchronously
+    #[test]
+    #[cfg(any(target_os = "freebsd", target_os = "macos"))]
+    fn error() {
+        const INITIAL: &[u8] = b"abcdef123456";
+        let mut rbuf = vec![0; 4];
+        let mut f = tempfile().unwrap();
+        f.write_all(INITIAL).unwrap();
+        let mut aior = Box::pin(AioRead::new(
+            f.as_raw_fd(),
+            -1, //an invalid offset
+            &mut rbuf,
+            0, //priority
+            SigevNotify::SigevNone,
+        ));
+        aior.as_mut().submit().expect_err("assertion failed");
+    }
+
+    // Test a simple aio operation with no completion notification.  We must
+    // poll for completion
+    #[test]
+    #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
+    fn ok() {
+        const INITIAL: &[u8] = b"abcdef123456";
+        let mut rbuf = vec![0; 4];
+        const EXPECT: &[u8] = b"cdef";
+        let mut f = tempfile().unwrap();
+        f.write_all(INITIAL).unwrap();
+        {
+            let fd = f.as_raw_fd();
+            let mut aior = Box::pin(AioRead::new(
+                fd,
+                2,
+                &mut rbuf,
+                0,
+                SigevNotify::SigevNone,
+            ));
+            aior.as_mut().submit().unwrap();
+
+            let err = poll_aio!(&mut aior);
+            assert_eq!(err, Ok(()));
+            assert_eq!(aior.as_mut().aio_return().unwrap(), EXPECT.len());
+        }
+        assert_eq!(EXPECT, rbuf.deref().deref());
+    }
+
+    // Like ok, but allocates the structure on the stack.
+    #[test]
+    #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
+    fn on_stack() {
+        const INITIAL: &[u8] = b"abcdef123456";
+        let mut rbuf = vec![0; 4];
+        const EXPECT: &[u8] = b"cdef";
+        let mut f = tempfile().unwrap();
+        f.write_all(INITIAL).unwrap();
+        {
+            let fd = f.as_raw_fd();
+            let mut aior =
+                AioRead::new(fd, 2, &mut rbuf, 0, SigevNotify::SigevNone);
+            let mut aior = unsafe { Pin::new_unchecked(&mut aior) };
+            aior.as_mut().submit().unwrap();
+
+            let err = poll_aio!(&mut aior);
+            assert_eq!(err, Ok(()));
+            assert_eq!(aior.as_mut().aio_return().unwrap(), EXPECT.len());
+        }
+        assert_eq!(EXPECT, rbuf.deref().deref());
     }
 }
 
-#[test]
-fn test_accessors() {
-    let mut rbuf = vec![0; 4];
-    let aiocb = AioCb::from_mut_slice( 1001,
-                           2,   //offset
-                           &mut rbuf,
-                           42,   //priority
-                           SigevNotify::SigevSignal {
-                               signal: Signal::SIGUSR2,
-                               si_value: 99
-                           },
-                           LioOpcode::LIO_NOP);
-    assert_eq!(1001, aiocb.fd());
-    assert_eq!(Some(LioOpcode::LIO_NOP), aiocb.lio_opcode());
-    assert_eq!(4, aiocb.nbytes());
-    assert_eq!(2, aiocb.offset());
-    assert_eq!(42, aiocb.priority());
-    let sev = aiocb.sigevent().sigevent();
-    assert_eq!(Signal::SIGUSR2 as i32, sev.sigev_signo);
-    assert_eq!(99, sev.sigev_value.sival_ptr as i64);
+#[cfg(target_os = "freebsd")]
+#[cfg(fbsd14)]
+mod aio_readv {
+    use std::io::IoSliceMut;
+
+    use super::*;
+
+    #[test]
+    fn test_accessors() {
+        let mut rbuf0 = vec![0; 4];
+        let mut rbuf1 = vec![0; 8];
+        let mut rbufs =
+            [IoSliceMut::new(&mut rbuf0), IoSliceMut::new(&mut rbuf1)];
+        let aiocb = AioReadv::new(
+            1001,
+            2, //offset
+            &mut rbufs,
+            42, //priority
+            SigevNotify::SigevSignal {
+                signal: Signal::SIGUSR2,
+                si_value: 99,
+            },
+        );
+        assert_eq!(1001, aiocb.fd());
+        assert_eq!(2, aiocb.iovlen());
+        assert_eq!(2, aiocb.offset());
+        assert_eq!(42, aiocb.priority());
+        let sev = aiocb.sigevent().sigevent();
+        assert_eq!(Signal::SIGUSR2 as i32, sev.sigev_signo);
+        assert_eq!(99, sev.sigev_value.sival_ptr as i64);
+    }
+
+    #[test]
+    #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
+    fn ok() {
+        const INITIAL: &[u8] = b"abcdef123456";
+        let mut rbuf0 = vec![0; 4];
+        let mut rbuf1 = vec![0; 2];
+        let mut rbufs =
+            [IoSliceMut::new(&mut rbuf0), IoSliceMut::new(&mut rbuf1)];
+        const EXPECT0: &[u8] = b"cdef";
+        const EXPECT1: &[u8] = b"12";
+        let mut f = tempfile().unwrap();
+        f.write_all(INITIAL).unwrap();
+        {
+            let fd = f.as_raw_fd();
+            let mut aior = Box::pin(AioReadv::new(
+                fd,
+                2,
+                &mut rbufs,
+                0,
+                SigevNotify::SigevNone,
+            ));
+            aior.as_mut().submit().unwrap();
+
+            let err = poll_aio!(&mut aior);
+            assert_eq!(err, Ok(()));
+            assert_eq!(
+                aior.as_mut().aio_return().unwrap(),
+                EXPECT0.len() + EXPECT1.len()
+            );
+        }
+        assert_eq!(&EXPECT0, &rbuf0);
+        assert_eq!(&EXPECT1, &rbuf1);
+    }
 }
 
-// Tests AioCb.cancel.  We aren't trying to test the OS's implementation, only
-// our bindings.  So it's sufficient to check that AioCb.cancel returned any
-// AioCancelStat value.
+mod aio_write {
+    use super::*;
+
+    #[test]
+    fn test_accessors() {
+        let wbuf = vec![0; 4];
+        let aiocb = AioWrite::new(
+            1001,
+            2, //offset
+            &wbuf,
+            42, //priority
+            SigevNotify::SigevSignal {
+                signal: Signal::SIGUSR2,
+                si_value: 99,
+            },
+        );
+        assert_eq!(1001, aiocb.fd());
+        assert_eq!(4, aiocb.nbytes());
+        assert_eq!(2, aiocb.offset());
+        assert_eq!(42, aiocb.priority());
+        let sev = aiocb.sigevent().sigevent();
+        assert_eq!(Signal::SIGUSR2 as i32, sev.sigev_signo);
+        assert_eq!(99, sev.sigev_value.sival_ptr as i64);
+    }
+
+    // Tests AioWrite.cancel.  We aren't trying to test the OS's implementation,
+    // only our bindings.  So it's sufficient to check that cancel
+    // returned any AioCancelStat value.
+    #[test]
+    #[cfg_attr(target_env = "musl", ignore)]
+    fn cancel() {
+        let wbuf: &[u8] = b"CDEF";
+
+        let f = tempfile().unwrap();
+        let mut aiow = Box::pin(AioWrite::new(
+            f.as_raw_fd(),
+            0,
+            wbuf,
+            0,
+            SigevNotify::SigevNone,
+        ));
+        aiow.as_mut().submit().unwrap();
+        let err = aiow.as_mut().error();
+        assert!(err == Ok(()) || err == Err(Errno::EINPROGRESS));
+
+        aiow.as_mut().cancel().unwrap();
+
+        // Wait for aiow to complete, but don't care whether it succeeded
+        let _ = poll_aio!(&mut aiow);
+        let _ = aiow.as_mut().aio_return();
+    }
+
+    // Test a simple aio operation with no completion notification.  We must
+    // poll for completion.
+    #[test]
+    #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
+    fn ok() {
+        const INITIAL: &[u8] = b"abcdef123456";
+        let wbuf = "CDEF".to_string().into_bytes();
+        let mut rbuf = Vec::new();
+        const EXPECT: &[u8] = b"abCDEF123456";
+
+        let mut f = tempfile().unwrap();
+        f.write_all(INITIAL).unwrap();
+        let mut aiow = Box::pin(AioWrite::new(
+            f.as_raw_fd(),
+            2,
+            &wbuf,
+            0,
+            SigevNotify::SigevNone,
+        ));
+        aiow.as_mut().submit().unwrap();
+
+        let err = poll_aio!(&mut aiow);
+        assert_eq!(err, Ok(()));
+        assert_eq!(aiow.as_mut().aio_return().unwrap(), wbuf.len());
+
+        f.rewind().unwrap();
+        let len = f.read_to_end(&mut rbuf).unwrap();
+        assert_eq!(len, EXPECT.len());
+        assert_eq!(rbuf, EXPECT);
+    }
+
+    // Like ok, but allocates the structure on the stack.
+    #[test]
+    #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
+    fn on_stack() {
+        const INITIAL: &[u8] = b"abcdef123456";
+        let wbuf = "CDEF".to_string().into_bytes();
+        let mut rbuf = Vec::new();
+        const EXPECT: &[u8] = b"abCDEF123456";
+
+        let mut f = tempfile().unwrap();
+        f.write_all(INITIAL).unwrap();
+        let mut aiow = AioWrite::new(
+            f.as_raw_fd(),
+            2, //offset
+            &wbuf,
+            0, //priority
+            SigevNotify::SigevNone,
+        );
+        let mut aiow = unsafe { Pin::new_unchecked(&mut aiow) };
+        aiow.as_mut().submit().unwrap();
+
+        let err = poll_aio!(&mut aiow);
+        assert_eq!(err, Ok(()));
+        assert_eq!(aiow.as_mut().aio_return().unwrap(), wbuf.len());
+
+        f.rewind().unwrap();
+        let len = f.read_to_end(&mut rbuf).unwrap();
+        assert_eq!(len, EXPECT.len());
+        assert_eq!(rbuf, EXPECT);
+    }
+
+    /// `AioWrite::write` should not modify the `AioCb` object if
+    /// `libc::aio_write` returns an error.
+    // Skip on Linux, because Linux's AIO implementation can't detect errors
+    // synchronously
+    #[test]
+    #[cfg(any(target_os = "freebsd", target_os = "macos"))]
+    fn error() {
+        let wbuf = "CDEF".to_string().into_bytes();
+        let mut aiow = Box::pin(AioWrite::new(
+            666, // An invalid file descriptor
+            0,   //offset
+            &wbuf,
+            0, //priority
+            SigevNotify::SigevNone,
+        ));
+        aiow.as_mut().submit().expect_err("assertion failed");
+        // Dropping the AioWrite at this point should not panic
+    }
+}
+
+#[cfg(target_os = "freebsd")]
+#[cfg(fbsd14)]
+mod aio_writev {
+    use std::io::IoSlice;
+
+    use super::*;
+
+    #[test]
+    fn test_accessors() {
+        let wbuf0 = vec![0; 4];
+        let wbuf1 = vec![0; 8];
+        let wbufs = [IoSlice::new(&wbuf0), IoSlice::new(&wbuf1)];
+        let aiocb = AioWritev::new(
+            1001,
+            2, //offset
+            &wbufs,
+            42, //priority
+            SigevNotify::SigevSignal {
+                signal: Signal::SIGUSR2,
+                si_value: 99,
+            },
+        );
+        assert_eq!(1001, aiocb.fd());
+        assert_eq!(2, aiocb.iovlen());
+        assert_eq!(2, aiocb.offset());
+        assert_eq!(42, aiocb.priority());
+        let sev = aiocb.sigevent().sigevent();
+        assert_eq!(Signal::SIGUSR2 as i32, sev.sigev_signo);
+        assert_eq!(99, sev.sigev_value.sival_ptr as i64);
+    }
+
+    // Test a simple aio operation with no completion notification.  We must
+    // poll for completion.
+    #[test]
+    #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
+    fn ok() {
+        const INITIAL: &[u8] = b"abcdef123456";
+        let wbuf0 = b"BC";
+        let wbuf1 = b"DEF";
+        let wbufs = [IoSlice::new(wbuf0), IoSlice::new(wbuf1)];
+        let wlen = wbuf0.len() + wbuf1.len();
+        let mut rbuf = Vec::new();
+        const EXPECT: &[u8] = b"aBCDEF123456";
+
+        let mut f = tempfile().unwrap();
+        f.write_all(INITIAL).unwrap();
+        let mut aiow = Box::pin(AioWritev::new(
+            f.as_raw_fd(),
+            1,
+            &wbufs,
+            0,
+            SigevNotify::SigevNone,
+        ));
+        aiow.as_mut().submit().unwrap();
+
+        let err = poll_aio!(&mut aiow);
+        assert_eq!(err, Ok(()));
+        assert_eq!(aiow.as_mut().aio_return().unwrap(), wlen);
+
+        f.rewind().unwrap();
+        let len = f.read_to_end(&mut rbuf).unwrap();
+        assert_eq!(len, EXPECT.len());
+        assert_eq!(rbuf, EXPECT);
+    }
+}
+
+// Test an aio operation with completion delivered by a signal
 #[test]
-#[cfg_attr(target_env = "musl", ignore)]
-fn test_cancel() {
-    let wbuf: &[u8] = b"CDEF";
+#[cfg_attr(
+    any(
+        all(target_env = "musl", target_arch = "x86_64"),
+        target_arch = "mips",
+        target_arch = "mips64"
+    ),
+    ignore
+)]
+fn sigev_signal() {
+    let _m = crate::SIGNAL_MTX.lock();
+    let sa = SigAction::new(
+        SigHandler::Handler(sigfunc),
+        SaFlags::SA_RESETHAND,
+        SigSet::empty(),
+    );
+    SIGNALED.store(false, Ordering::Relaxed);
+    unsafe { sigaction(Signal::SIGUSR2, &sa) }.unwrap();
 
-    let f = tempfile().unwrap();
-    let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
-                            0,   //offset
-                            wbuf,
-                            0,   //priority
-                            SigevNotify::SigevNone,
-                            LioOpcode::LIO_NOP);
-    aiocb.write().unwrap();
-    let err = aiocb.error();
-    assert!(err == Ok(()) || err == Err(Errno::EINPROGRESS));
+    const INITIAL: &[u8] = b"abcdef123456";
+    const WBUF: &[u8] = b"CDEF";
+    let mut rbuf = Vec::new();
+    const EXPECT: &[u8] = b"abCDEF123456";
 
-    let cancelstat = aiocb.cancel();
-    assert!(cancelstat.is_ok());
+    let mut f = tempfile().unwrap();
+    f.write_all(INITIAL).unwrap();
+    let mut aiow = Box::pin(AioWrite::new(
+        f.as_raw_fd(),
+        2, //offset
+        WBUF,
+        0, //priority
+        SigevNotify::SigevSignal {
+            signal: Signal::SIGUSR2,
+            si_value: 0, //TODO: validate in sigfunc
+        },
+    ));
+    aiow.as_mut().submit().unwrap();
+    while !SIGNALED.load(Ordering::Relaxed) {
+        thread::sleep(time::Duration::from_millis(10));
+    }
 
-    // Wait for aiocb to complete, but don't care whether it succeeded
-    let _ = poll_aio(&mut aiocb);
-    let _ = aiocb.aio_return();
+    assert_eq!(aiow.as_mut().aio_return().unwrap(), WBUF.len());
+    f.rewind().unwrap();
+    let len = f.read_to_end(&mut rbuf).unwrap();
+    assert_eq!(len, EXPECT.len());
+    assert_eq!(rbuf, EXPECT);
 }
 
 // Tests using aio_cancel_all for all outstanding IOs.
@@ -87,58 +550,22 @@
     let wbuf: &[u8] = b"CDEF";
 
     let f = tempfile().unwrap();
-    let mut aiocb = AioCb::from_slice(f.as_raw_fd(),
-                            0,   //offset
-                            wbuf,
-                            0,   //priority
-                            SigevNotify::SigevNone,
-                            LioOpcode::LIO_NOP);
-    aiocb.write().unwrap();
-    let err = aiocb.error();
+    let mut aiocb = Box::pin(AioWrite::new(
+        f.as_raw_fd(),
+        0, //offset
+        wbuf,
+        0, //priority
+        SigevNotify::SigevNone,
+    ));
+    aiocb.as_mut().submit().unwrap();
+    let err = aiocb.as_mut().error();
     assert!(err == Ok(()) || err == Err(Errno::EINPROGRESS));
 
-    let cancelstat = aio_cancel_all(f.as_raw_fd());
-    assert!(cancelstat.is_ok());
+    aio_cancel_all(f.as_raw_fd()).unwrap();
 
     // Wait for aiocb to complete, but don't care whether it succeeded
-    let _ = poll_aio(&mut aiocb);
-    let _ = aiocb.aio_return();
-}
-
-#[test]
-#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
-fn test_fsync() {
-    const INITIAL: &[u8] = b"abcdef123456";
-    let mut f = tempfile().unwrap();
-    f.write_all(INITIAL).unwrap();
-    let mut aiocb = AioCb::from_fd( f.as_raw_fd(),
-                            0,   //priority
-                            SigevNotify::SigevNone);
-    let err = aiocb.fsync(AioFsyncMode::O_SYNC);
-    assert!(err.is_ok());
-    poll_aio(&mut aiocb).unwrap();
-    aiocb.aio_return().unwrap();
-}
-
-/// `AioCb::fsync` should not modify the `AioCb` object if `libc::aio_fsync` returns
-/// an error
-// Skip on Linux, because Linux's AIO implementation can't detect errors
-// synchronously
-#[test]
-#[cfg(any(target_os = "freebsd", target_os = "macos"))]
-fn test_fsync_error() {
-    use std::mem;
-
-    const INITIAL: &[u8] = b"abcdef123456";
-    // Create an invalid AioFsyncMode
-    let mode = unsafe { mem::transmute(666) };
-    let mut f = tempfile().unwrap();
-    f.write_all(INITIAL).unwrap();
-    let mut aiocb = AioCb::from_fd( f.as_raw_fd(),
-                            0,   //priority
-                            SigevNotify::SigevNone);
-    let err = aiocb.fsync(mode);
-    assert!(err.is_err());
+    let _ = poll_aio!(&mut aiocb);
+    let _ = aiocb.as_mut().aio_return();
 }
 
 #[test]
@@ -157,464 +584,43 @@
     let mut f = tempfile().unwrap();
     f.write_all(INITIAL).unwrap();
 
-    let mut wcb = AioCb::from_slice( f.as_raw_fd(),
-                           2,   //offset
-                           WBUF,
-                           0,   //priority
-                           SigevNotify::SigevNone,
-                           LioOpcode::LIO_WRITE);
+    let mut wcb = Box::pin(AioWrite::new(
+        f.as_raw_fd(),
+        2, //offset
+        WBUF,
+        0, //priority
+        SigevNotify::SigevNone,
+    ));
 
-    let mut rcb = AioCb::from_mut_slice( f.as_raw_fd(),
-                            8,   //offset
-                            &mut rbuf,
-                            0,   //priority
-                            SigevNotify::SigevNone,
-                            LioOpcode::LIO_READ);
-    wcb.write().unwrap();
-    rcb.read().unwrap();
+    let mut rcb = Box::pin(AioRead::new(
+        f.as_raw_fd(),
+        8, //offset
+        &mut rbuf,
+        0, //priority
+        SigevNotify::SigevNone,
+    ));
+    wcb.as_mut().submit().unwrap();
+    rcb.as_mut().submit().unwrap();
     loop {
         {
-            let cbbuf = [wcb.as_ref(), rcb.as_ref()];
+            let cbbuf = [
+                &*wcb as &dyn AsRef<libc::aiocb>,
+                &*rcb as &dyn AsRef<libc::aiocb>,
+            ];
             let r = aio_suspend(&cbbuf[..], Some(timeout));
             match r {
                 Err(Errno::EINTR) => continue,
                 Err(e) => panic!("aio_suspend returned {:?}", e),
-                Ok(_) => ()
+                Ok(_) => (),
             };
         }
-        if rcb.error() != Err(Errno::EINPROGRESS) &&
-           wcb.error() != Err(Errno::EINPROGRESS) {
-            break
+        if rcb.as_mut().error() != Err(Errno::EINPROGRESS)
+            && wcb.as_mut().error() != Err(Errno::EINPROGRESS)
+        {
+            break;
         }
     }
 
-    assert_eq!(wcb.aio_return().unwrap() as usize, WBUF.len());
-    assert_eq!(rcb.aio_return().unwrap() as usize, rlen);
-}
-
-// Test a simple aio operation with no completion notification.  We must poll
-// for completion
-#[test]
-#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
-fn test_read() {
-    const INITIAL: &[u8] = b"abcdef123456";
-    let mut rbuf = vec![0; 4];
-    const EXPECT: &[u8] = b"cdef";
-    let mut f = tempfile().unwrap();
-    f.write_all(INITIAL).unwrap();
-    {
-        let mut aiocb = AioCb::from_mut_slice( f.as_raw_fd(),
-                               2,   //offset
-                               &mut rbuf,
-                               0,   //priority
-                               SigevNotify::SigevNone,
-                               LioOpcode::LIO_NOP);
-        aiocb.read().unwrap();
-
-        let err = poll_aio(&mut aiocb);
-        assert_eq!(err, Ok(()));
-        assert_eq!(aiocb.aio_return().unwrap() as usize, EXPECT.len());
-    }
-
-    assert_eq!(EXPECT, rbuf.deref().deref());
-}
-
-/// `AioCb::read` should not modify the `AioCb` object if `libc::aio_read`
-/// returns an error
-// Skip on Linux, because Linux's AIO implementation can't detect errors
-// synchronously
-#[test]
-#[cfg(any(target_os = "freebsd", target_os = "macos"))]
-fn test_read_error() {
-    const INITIAL: &[u8] = b"abcdef123456";
-    let mut rbuf = vec![0; 4];
-    let mut f = tempfile().unwrap();
-    f.write_all(INITIAL).unwrap();
-    let mut aiocb = AioCb::from_mut_slice( f.as_raw_fd(),
-                           -1,   //an invalid offset
-                           &mut rbuf,
-                           0,   //priority
-                           SigevNotify::SigevNone,
-                           LioOpcode::LIO_NOP);
-    assert!(aiocb.read().is_err());
-}
-
-// Tests from_mut_slice
-#[test]
-#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
-fn test_read_into_mut_slice() {
-    const INITIAL: &[u8] = b"abcdef123456";
-    let mut rbuf = vec![0; 4];
-    const EXPECT: &[u8] = b"cdef";
-    let mut f = tempfile().unwrap();
-    f.write_all(INITIAL).unwrap();
-    {
-        let mut aiocb = AioCb::from_mut_slice( f.as_raw_fd(),
-                               2,   //offset
-                               &mut rbuf,
-                               0,   //priority
-                               SigevNotify::SigevNone,
-                               LioOpcode::LIO_NOP);
-        aiocb.read().unwrap();
-
-        let err = poll_aio(&mut aiocb);
-        assert_eq!(err, Ok(()));
-        assert_eq!(aiocb.aio_return().unwrap() as usize, EXPECT.len());
-    }
-
-    assert_eq!(rbuf, EXPECT);
-}
-
-// Tests from_ptr
-#[test]
-#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
-fn test_read_into_pointer() {
-    const INITIAL: &[u8] = b"abcdef123456";
-    let mut rbuf = vec![0; 4];
-    const EXPECT: &[u8] = b"cdef";
-    let mut f = tempfile().unwrap();
-    f.write_all(INITIAL).unwrap();
-    {
-        // Safety: ok because rbuf lives until after poll_aio
-        let mut aiocb = unsafe {
-            AioCb::from_mut_ptr( f.as_raw_fd(),
-                                 2,   //offset
-                                 rbuf.as_mut_ptr() as *mut c_void,
-                                 rbuf.len(),
-                                 0,   //priority
-                                 SigevNotify::SigevNone,
-                                 LioOpcode::LIO_NOP)
-        };
-        aiocb.read().unwrap();
-
-        let err = poll_aio(&mut aiocb);
-        assert_eq!(err, Ok(()));
-        assert_eq!(aiocb.aio_return().unwrap() as usize, EXPECT.len());
-    }
-
-    assert_eq!(rbuf, EXPECT);
-}
-
-// Test reading into an immutable buffer.  It should fail
-// FIXME: This test fails to panic on Linux/musl
-#[test]
-#[should_panic(expected = "Can't read into an immutable buffer")]
-#[cfg_attr(target_env = "musl", ignore)]
-fn test_read_immutable_buffer() {
-    let rbuf: &[u8] = b"CDEF";
-    let f = tempfile().unwrap();
-    let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
-                           2,   //offset
-                           rbuf,
-                           0,   //priority
-                           SigevNotify::SigevNone,
-                           LioOpcode::LIO_NOP);
-    aiocb.read().unwrap();
-}
-
-
-// Test a simple aio operation with no completion notification.  We must poll
-// for completion.  Unlike test_aio_read, this test uses AioCb::from_slice
-#[test]
-#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
-fn test_write() {
-    const INITIAL: &[u8] = b"abcdef123456";
-    let wbuf = "CDEF".to_string().into_bytes();
-    let mut rbuf = Vec::new();
-    const EXPECT: &[u8] = b"abCDEF123456";
-
-    let mut f = tempfile().unwrap();
-    f.write_all(INITIAL).unwrap();
-    let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
-                           2,   //offset
-                           &wbuf,
-                           0,   //priority
-                           SigevNotify::SigevNone,
-                           LioOpcode::LIO_NOP);
-    aiocb.write().unwrap();
-
-    let err = poll_aio(&mut aiocb);
-    assert_eq!(err, Ok(()));
-    assert_eq!(aiocb.aio_return().unwrap() as usize, wbuf.len());
-
-    f.seek(SeekFrom::Start(0)).unwrap();
-    let len = f.read_to_end(&mut rbuf).unwrap();
-    assert_eq!(len, EXPECT.len());
-    assert_eq!(rbuf, EXPECT);
-}
-
-// Tests `AioCb::from_ptr`
-#[test]
-#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
-fn test_write_from_pointer() {
-    const INITIAL: &[u8] = b"abcdef123456";
-    let wbuf = "CDEF".to_string().into_bytes();
-    let mut rbuf = Vec::new();
-    const EXPECT: &[u8] = b"abCDEF123456";
-
-    let mut f = tempfile().unwrap();
-    f.write_all(INITIAL).unwrap();
-    // Safety: ok because aiocb outlives poll_aio
-    let mut aiocb = unsafe {
-        AioCb::from_ptr( f.as_raw_fd(),
-                         2,   //offset
-                         wbuf.as_ptr() as *const c_void,
-                         wbuf.len(),
-                         0,   //priority
-                         SigevNotify::SigevNone,
-                         LioOpcode::LIO_NOP)
-    };
-    aiocb.write().unwrap();
-
-    let err = poll_aio(&mut aiocb);
-    assert_eq!(err, Ok(()));
-    assert_eq!(aiocb.aio_return().unwrap() as usize, wbuf.len());
-
-    f.seek(SeekFrom::Start(0)).unwrap();
-    let len = f.read_to_end(&mut rbuf).unwrap();
-    assert_eq!(len, EXPECT.len());
-    assert_eq!(rbuf, EXPECT);
-}
-
-/// `AioCb::write` should not modify the `AioCb` object if `libc::aio_write`
-/// returns an error
-// Skip on Linux, because Linux's AIO implementation can't detect errors
-// synchronously
-#[test]
-#[cfg(any(target_os = "freebsd", target_os = "macos"))]
-fn test_write_error() {
-    let wbuf = "CDEF".to_string().into_bytes();
-    let mut aiocb = AioCb::from_slice( 666, // An invalid file descriptor
-                           0,   //offset
-                           &wbuf,
-                           0,   //priority
-                           SigevNotify::SigevNone,
-                           LioOpcode::LIO_NOP);
-    assert!(aiocb.write().is_err());
-}
-
-lazy_static! {
-    pub static ref SIGNALED: AtomicBool = AtomicBool::new(false);
-}
-
-extern fn sigfunc(_: c_int) {
-    SIGNALED.store(true, Ordering::Relaxed);
-}
-
-// Test an aio operation with completion delivered by a signal
-// FIXME: This test is ignored on mips because of failures in qemu in CI
-#[test]
-#[cfg_attr(any(all(target_env = "musl", target_arch = "x86_64"), target_arch = "mips", target_arch = "mips64"), ignore)]
-fn test_write_sigev_signal() {
-    let _m = crate::SIGNAL_MTX.lock();
-    let sa = SigAction::new(SigHandler::Handler(sigfunc),
-                            SaFlags::SA_RESETHAND,
-                            SigSet::empty());
-    SIGNALED.store(false, Ordering::Relaxed);
-    unsafe { sigaction(Signal::SIGUSR2, &sa) }.unwrap();
-
-    const INITIAL: &[u8] = b"abcdef123456";
-    const WBUF: &[u8] = b"CDEF";
-    let mut rbuf = Vec::new();
-    const EXPECT: &[u8] = b"abCDEF123456";
-
-    let mut f = tempfile().unwrap();
-    f.write_all(INITIAL).unwrap();
-    let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
-                           2,   //offset
-                           WBUF,
-                           0,   //priority
-                           SigevNotify::SigevSignal {
-                               signal: Signal::SIGUSR2,
-                               si_value: 0  //TODO: validate in sigfunc
-                           },
-                           LioOpcode::LIO_NOP);
-    aiocb.write().unwrap();
-    while !SIGNALED.load(Ordering::Relaxed) {
-        thread::sleep(time::Duration::from_millis(10));
-    }
-
-    assert_eq!(aiocb.aio_return().unwrap() as usize, WBUF.len());
-    f.seek(SeekFrom::Start(0)).unwrap();
-    let len = f.read_to_end(&mut rbuf).unwrap();
-    assert_eq!(len, EXPECT.len());
-    assert_eq!(rbuf, EXPECT);
-}
-
-// Test LioCb::listio with LIO_WAIT, so all AIO ops should be complete by the
-// time listio returns.
-#[test]
-#[cfg(not(any(target_os = "ios", target_os = "macos")))]
-#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
-fn test_liocb_listio_wait() {
-    const INITIAL: &[u8] = b"abcdef123456";
-    const WBUF: &[u8] = b"CDEF";
-    let mut rbuf = vec![0; 4];
-    let rlen = rbuf.len();
-    let mut rbuf2 = Vec::new();
-    const EXPECT: &[u8] = b"abCDEF123456";
-    let mut f = tempfile().unwrap();
-
-    f.write_all(INITIAL).unwrap();
-
-    {
-        let mut liocb = LioCbBuilder::with_capacity(2)
-            .emplace_slice(
-                f.as_raw_fd(),
-                2,   //offset
-                WBUF,
-                0,   //priority
-                SigevNotify::SigevNone,
-                LioOpcode::LIO_WRITE
-            ).emplace_mut_slice(
-                f.as_raw_fd(),
-                8,   //offset
-                &mut rbuf,
-                0,   //priority
-                SigevNotify::SigevNone,
-                LioOpcode::LIO_READ
-            ).finish();
-        let err = liocb.listio(LioMode::LIO_WAIT, SigevNotify::SigevNone);
-        err.expect("lio_listio");
-
-        assert_eq!(liocb.aio_return(0).unwrap() as usize, WBUF.len());
-        assert_eq!(liocb.aio_return(1).unwrap() as usize, rlen);
-    }
-    assert_eq!(rbuf.deref().deref(), b"3456");
-
-    f.seek(SeekFrom::Start(0)).unwrap();
-    let len = f.read_to_end(&mut rbuf2).unwrap();
-    assert_eq!(len, EXPECT.len());
-    assert_eq!(rbuf2, EXPECT);
-}
-
-// Test LioCb::listio with LIO_NOWAIT and no SigEvent, so we must use some other
-// mechanism to check for the individual AioCb's completion.
-#[test]
-#[cfg(not(any(target_os = "ios", target_os = "macos")))]
-#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
-fn test_liocb_listio_nowait() {
-    const INITIAL: &[u8] = b"abcdef123456";
-    const WBUF: &[u8] = b"CDEF";
-    let mut rbuf = vec![0; 4];
-    let rlen = rbuf.len();
-    let mut rbuf2 = Vec::new();
-    const EXPECT: &[u8] = b"abCDEF123456";
-    let mut f = tempfile().unwrap();
-
-    f.write_all(INITIAL).unwrap();
-
-    {
-        let mut liocb = LioCbBuilder::with_capacity(2)
-            .emplace_slice(
-                f.as_raw_fd(),
-                2,   //offset
-                WBUF,
-                0,   //priority
-                SigevNotify::SigevNone,
-                LioOpcode::LIO_WRITE
-            ).emplace_mut_slice(
-                f.as_raw_fd(),
-                8,   //offset
-                &mut rbuf,
-                0,   //priority
-                SigevNotify::SigevNone,
-                LioOpcode::LIO_READ
-            ).finish();
-        let err = liocb.listio(LioMode::LIO_NOWAIT, SigevNotify::SigevNone);
-        err.expect("lio_listio");
-
-        poll_lio(&mut liocb, 0).unwrap();
-        poll_lio(&mut liocb, 1).unwrap();
-        assert_eq!(liocb.aio_return(0).unwrap() as usize, WBUF.len());
-        assert_eq!(liocb.aio_return(1).unwrap() as usize, rlen);
-    }
-    assert_eq!(rbuf.deref().deref(), b"3456");
-
-    f.seek(SeekFrom::Start(0)).unwrap();
-    let len = f.read_to_end(&mut rbuf2).unwrap();
-    assert_eq!(len, EXPECT.len());
-    assert_eq!(rbuf2, EXPECT);
-}
-
-// Test LioCb::listio with LIO_NOWAIT and a SigEvent to indicate when all
-// AioCb's are complete.
-// FIXME: This test is ignored on mips/mips64 because of failures in qemu in CI.
-#[test]
-#[cfg(not(any(target_os = "ios", target_os = "macos")))]
-#[cfg_attr(any(target_arch = "mips", target_arch = "mips64", target_env = "musl"), ignore)]
-fn test_liocb_listio_signal() {
-    let _m = crate::SIGNAL_MTX.lock();
-    const INITIAL: &[u8] = b"abcdef123456";
-    const WBUF: &[u8] = b"CDEF";
-    let mut rbuf = vec![0; 4];
-    let rlen = rbuf.len();
-    let mut rbuf2 = Vec::new();
-    const EXPECT: &[u8] = b"abCDEF123456";
-    let mut f = tempfile().unwrap();
-    let sa = SigAction::new(SigHandler::Handler(sigfunc),
-                            SaFlags::SA_RESETHAND,
-                            SigSet::empty());
-    let sigev_notify = SigevNotify::SigevSignal { signal: Signal::SIGUSR2,
-                                                  si_value: 0 };
-
-    f.write_all(INITIAL).unwrap();
-
-    {
-        let mut liocb = LioCbBuilder::with_capacity(2)
-            .emplace_slice(
-                f.as_raw_fd(),
-                2,   //offset
-                WBUF,
-                0,   //priority
-                SigevNotify::SigevNone,
-                LioOpcode::LIO_WRITE
-            ).emplace_mut_slice(
-                f.as_raw_fd(),
-                8,   //offset
-                &mut rbuf,
-                0,   //priority
-                SigevNotify::SigevNone,
-                LioOpcode::LIO_READ
-            ).finish();
-        SIGNALED.store(false, Ordering::Relaxed);
-        unsafe { sigaction(Signal::SIGUSR2, &sa) }.unwrap();
-        let err = liocb.listio(LioMode::LIO_NOWAIT, sigev_notify);
-        err.expect("lio_listio");
-        while !SIGNALED.load(Ordering::Relaxed) {
-            thread::sleep(time::Duration::from_millis(10));
-        }
-
-        assert_eq!(liocb.aio_return(0).unwrap() as usize, WBUF.len());
-        assert_eq!(liocb.aio_return(1).unwrap() as usize, rlen);
-    }
-    assert_eq!(rbuf.deref().deref(), b"3456");
-
-    f.seek(SeekFrom::Start(0)).unwrap();
-    let len = f.read_to_end(&mut rbuf2).unwrap();
-    assert_eq!(len, EXPECT.len());
-    assert_eq!(rbuf2, EXPECT);
-}
-
-// Try to use LioCb::listio to read into an immutable buffer.  It should fail
-// FIXME: This test fails to panic on Linux/musl
-#[test]
-#[cfg(not(any(target_os = "ios", target_os = "macos")))]
-#[should_panic(expected = "Can't read into an immutable buffer")]
-#[cfg_attr(target_env = "musl", ignore)]
-fn test_liocb_listio_read_immutable() {
-    let rbuf: &[u8] = b"abcd";
-    let f = tempfile().unwrap();
-
-
-    let mut liocb = LioCbBuilder::with_capacity(1)
-        .emplace_slice(
-            f.as_raw_fd(),
-            2,   //offset
-            rbuf,
-            0,   //priority
-            SigevNotify::SigevNone,
-            LioOpcode::LIO_READ
-        ).finish();
-    let _ = liocb.listio(LioMode::LIO_NOWAIT, SigevNotify::SigevNone);
+    assert_eq!(wcb.as_mut().aio_return().unwrap(), WBUF.len());
+    assert_eq!(rcb.as_mut().aio_return().unwrap(), rlen);
 }
diff --git a/test/sys/test_aio_drop.rs b/test/sys/test_aio_drop.rs
index 71a2183..bbe6623 100644
--- a/test/sys/test_aio_drop.rs
+++ b/test/sys/test_aio_drop.rs
@@ -3,12 +3,17 @@
 // the AIO subsystem and causes subsequent tests to fail
 #[test]
 #[should_panic(expected = "Dropped an in-progress AioCb")]
-#[cfg(all(not(target_env = "musl"),
-          any(target_os = "linux",
-              target_os = "ios",
-              target_os = "macos",
-              target_os = "freebsd",
-              target_os = "netbsd")))]
+#[cfg(all(
+    not(target_env = "musl"),
+    not(target_env = "uclibc"),
+    any(
+        target_os = "linux",
+        target_os = "ios",
+        target_os = "macos",
+        target_os = "freebsd",
+        target_os = "netbsd"
+    )
+))]
 fn test_drop() {
     use nix::sys::aio::*;
     use nix::sys::signal::*;
@@ -19,11 +24,12 @@
 
     let f = tempfile().unwrap();
     f.set_len(6).unwrap();
-    let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
-                           2,   //offset
-                           WBUF,
-                           0,   //priority
-                           SigevNotify::SigevNone,
-                           LioOpcode::LIO_NOP);
-    aiocb.write().unwrap();
+    let mut aiocb = Box::pin(AioWrite::new(
+        f.as_raw_fd(),
+        2, //offset
+        WBUF,
+        0, //priority
+        SigevNotify::SigevNone,
+    ));
+    aiocb.as_mut().submit().unwrap();
 }
diff --git a/test/sys/test_epoll.rs b/test/sys/test_epoll.rs
index 8d44cd0..9156915 100644
--- a/test/sys/test_epoll.rs
+++ b/test/sys/test_epoll.rs
@@ -1,23 +1,24 @@
-use nix::sys::epoll::{EpollCreateFlags, EpollFlags, EpollOp, EpollEvent};
-use nix::sys::epoll::{epoll_create1, epoll_ctl};
 use nix::errno::Errno;
+use nix::sys::epoll::{epoll_create1, epoll_ctl};
+use nix::sys::epoll::{EpollCreateFlags, EpollEvent, EpollFlags, EpollOp};
 
 #[test]
 pub fn test_epoll_errno() {
     let efd = epoll_create1(EpollCreateFlags::empty()).unwrap();
     let result = epoll_ctl(efd, EpollOp::EpollCtlDel, 1, None);
-    assert!(result.is_err());
+    result.expect_err("assertion failed");
     assert_eq!(result.unwrap_err(), Errno::ENOENT);
 
     let result = epoll_ctl(efd, EpollOp::EpollCtlAdd, 1, None);
-    assert!(result.is_err());
+    result.expect_err("assertion failed");
     assert_eq!(result.unwrap_err(), Errno::EINVAL);
 }
 
 #[test]
 pub fn test_epoll_ctl() {
     let efd = epoll_create1(EpollCreateFlags::empty()).unwrap();
-    let mut event = EpollEvent::new(EpollFlags::EPOLLIN | EpollFlags::EPOLLERR, 1);
+    let mut event =
+        EpollEvent::new(EpollFlags::EPOLLIN | EpollFlags::EPOLLERR, 1);
     epoll_ctl(efd, EpollOp::EpollCtlAdd, 1, &mut event).unwrap();
     epoll_ctl(efd, EpollOp::EpollCtlDel, 1, None).unwrap();
 }
diff --git a/test/sys/test_inotify.rs b/test/sys/test_inotify.rs
index 137816a..bb5851a 100644
--- a/test/sys/test_inotify.rs
+++ b/test/sys/test_inotify.rs
@@ -1,15 +1,16 @@
-use nix::sys::inotify::{AddWatchFlags,InitFlags,Inotify};
 use nix::errno::Errno;
+use nix::sys::inotify::{AddWatchFlags, InitFlags, Inotify};
 use std::ffi::OsString;
 use std::fs::{rename, File};
 
 #[test]
 pub fn test_inotify() {
-    let instance = Inotify::init(InitFlags::IN_NONBLOCK)
-        .unwrap();
+    let instance = Inotify::init(InitFlags::IN_NONBLOCK).unwrap();
     let tempdir = tempfile::tempdir().unwrap();
 
-    instance.add_watch(tempdir.path(), AddWatchFlags::IN_ALL_EVENTS).unwrap();
+    instance
+        .add_watch(tempdir.path(), AddWatchFlags::IN_ALL_EVENTS)
+        .unwrap();
 
     let events = instance.read_events();
     assert_eq!(events.unwrap_err(), Errno::EAGAIN);
@@ -22,11 +23,12 @@
 
 #[test]
 pub fn test_inotify_multi_events() {
-    let instance = Inotify::init(InitFlags::IN_NONBLOCK)
-        .unwrap();
+    let instance = Inotify::init(InitFlags::IN_NONBLOCK).unwrap();
     let tempdir = tempfile::tempdir().unwrap();
 
-    instance.add_watch(tempdir.path(), AddWatchFlags::IN_ALL_EVENTS).unwrap();
+    instance
+        .add_watch(tempdir.path(), AddWatchFlags::IN_ALL_EVENTS)
+        .unwrap();
 
     let events = instance.read_events();
     assert_eq!(events.unwrap_err(), Errno::EAGAIN);
diff --git a/test/sys/test_ioctl.rs b/test/sys/test_ioctl.rs
index 236d242..40f60cf 100644
--- a/test/sys/test_ioctl.rs
+++ b/test/sys/test_ioctl.rs
@@ -30,9 +30,16 @@
 
 #[cfg(any(target_os = "linux", target_os = "android"))]
 mod linux {
+    // The cast is not unnecessary on all platforms.
+    #[allow(clippy::unnecessary_cast)]
     #[test]
     fn test_op_none() {
-        if cfg!(any(target_arch = "mips", target_arch = "mips64", target_arch="powerpc", target_arch="powerpc64")){
+        if cfg!(any(
+            target_arch = "mips",
+            target_arch = "mips64",
+            target_arch = "powerpc",
+            target_arch = "powerpc64"
+        )) {
             assert_eq!(request_code_none!(b'q', 10) as u32, 0x2000_710A);
             assert_eq!(request_code_none!(b'a', 255) as u32, 0x2000_61FF);
         } else {
@@ -41,9 +48,16 @@
         }
     }
 
+    // The cast is not unnecessary on all platforms.
+    #[allow(clippy::unnecessary_cast)]
     #[test]
     fn test_op_write() {
-        if cfg!(any(target_arch = "mips", target_arch = "mips64", target_arch="powerpc", target_arch="powerpc64")){
+        if cfg!(any(
+            target_arch = "mips",
+            target_arch = "mips64",
+            target_arch = "powerpc",
+            target_arch = "powerpc64"
+        )) {
             assert_eq!(request_code_write!(b'z', 10, 1) as u32, 0x8001_7A0A);
             assert_eq!(request_code_write!(b'z', 10, 512) as u32, 0x8200_7A0A);
         } else {
@@ -55,19 +69,29 @@
     #[cfg(target_pointer_width = "64")]
     #[test]
     fn test_op_write_64() {
-        if cfg!(any(target_arch = "mips64", target_arch="powerpc64")){
-            assert_eq!(request_code_write!(b'z', 10, 1u64 << 32) as u32,
-                       0x8000_7A0A);
+        if cfg!(any(target_arch = "mips64", target_arch = "powerpc64")) {
+            assert_eq!(
+                request_code_write!(b'z', 10, 1u64 << 32) as u32,
+                0x8000_7A0A
+            );
         } else {
-            assert_eq!(request_code_write!(b'z', 10, 1u64 << 32) as u32,
-                       0x4000_7A0A);
+            assert_eq!(
+                request_code_write!(b'z', 10, 1u64 << 32) as u32,
+                0x4000_7A0A
+            );
         }
-
     }
 
+    // The cast is not unnecessary on all platforms.
+    #[allow(clippy::unnecessary_cast)]
     #[test]
     fn test_op_read() {
-        if cfg!(any(target_arch = "mips", target_arch = "mips64", target_arch="powerpc", target_arch="powerpc64")){
+        if cfg!(any(
+            target_arch = "mips",
+            target_arch = "mips64",
+            target_arch = "powerpc",
+            target_arch = "powerpc64"
+        )) {
             assert_eq!(request_code_read!(b'z', 10, 1) as u32, 0x4001_7A0A);
             assert_eq!(request_code_read!(b'z', 10, 512) as u32, 0x4200_7A0A);
         } else {
@@ -79,15 +103,21 @@
     #[cfg(target_pointer_width = "64")]
     #[test]
     fn test_op_read_64() {
-        if cfg!(any(target_arch = "mips64", target_arch="powerpc64")){
-            assert_eq!(request_code_read!(b'z', 10, 1u64 << 32) as u32,
-                       0x4000_7A0A);
+        if cfg!(any(target_arch = "mips64", target_arch = "powerpc64")) {
+            assert_eq!(
+                request_code_read!(b'z', 10, 1u64 << 32) as u32,
+                0x4000_7A0A
+            );
         } else {
-            assert_eq!(request_code_read!(b'z', 10, 1u64 << 32) as u32,
-                       0x8000_7A0A);
+            assert_eq!(
+                request_code_read!(b'z', 10, 1u64 << 32) as u32,
+                0x8000_7A0A
+            );
         }
     }
 
+    // The cast is not unnecessary on all platforms.
+    #[allow(clippy::unnecessary_cast)]
     #[test]
     fn test_op_read_write() {
         assert_eq!(request_code_readwrite!(b'z', 10, 1) as u32, 0xC001_7A0A);
@@ -97,17 +127,21 @@
     #[cfg(target_pointer_width = "64")]
     #[test]
     fn test_op_read_write_64() {
-        assert_eq!(request_code_readwrite!(b'z', 10, 1u64 << 32) as u32,
-                   0xC000_7A0A);
+        assert_eq!(
+            request_code_readwrite!(b'z', 10, 1u64 << 32) as u32,
+            0xC000_7A0A
+        );
     }
 }
 
-#[cfg(any(target_os = "dragonfly",
-          target_os = "freebsd",
-          target_os = "ios",
-          target_os = "macos",
-          target_os = "netbsd",
-          target_os = "openbsd"))]
+#[cfg(any(
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "ios",
+    target_os = "macos",
+    target_os = "netbsd",
+    target_os = "openbsd"
+))]
 mod bsd {
     #[test]
     fn test_op_none() {
@@ -164,8 +198,8 @@
     use std::mem;
     use std::os::unix::io::AsRawFd;
 
+    use libc::{termios, TCGETS, TCSBRK, TCSETS, TIOCNXCL};
     use tempfile::tempfile;
-    use libc::{TCGETS, TCSBRK, TCSETS, TIOCNXCL, termios};
 
     use nix::errno::Errno;
 
@@ -255,7 +289,7 @@
     }
 
     // From linux/videodev2.h
-    ioctl_readwrite!(enum_audio,  b'V', 65, v4l2_audio);
+    ioctl_readwrite!(enum_audio, b'V', 65, v4l2_audio);
     #[test]
     fn test_ioctl_readwrite() {
         let file = tempfile().unwrap();
@@ -281,7 +315,12 @@
     }
 
     // From linux/spi/spidev.h
-    ioctl_write_buf!(spi_ioc_message, super::SPI_IOC_MAGIC, super::SPI_IOC_MESSAGE, spi_ioc_transfer);
+    ioctl_write_buf!(
+        spi_ioc_message,
+        super::SPI_IOC_MAGIC,
+        super::SPI_IOC_MESSAGE,
+        spi_ioc_transfer
+    );
     #[test]
     fn test_ioctl_write_buf() {
         let file = tempfile().unwrap();
@@ -298,8 +337,8 @@
     use std::mem;
     use std::os::unix::io::AsRawFd;
 
-    use tempfile::tempfile;
     use libc::termios;
+    use tempfile::tempfile;
 
     use nix::errno::Errno;
 
diff --git a/test/sys/test_lio_listio_resubmit.rs b/test/sys/test_lio_listio_resubmit.rs
deleted file mode 100644
index c907789..0000000
--- a/test/sys/test_lio_listio_resubmit.rs
+++ /dev/null
@@ -1,106 +0,0 @@
-// vim: tw=80
-
-// Annoyingly, Cargo is unable to conditionally build an entire test binary.  So
-// we must disable the test here rather than in Cargo.toml
-#![cfg(target_os = "freebsd")]
-
-use nix::errno::*;
-use nix::libc::off_t;
-use nix::sys::aio::*;
-use nix::sys::signal::SigevNotify;
-use nix::unistd::{SysconfVar, sysconf};
-use std::os::unix::io::AsRawFd;
-use std::{thread, time};
-use sysctl::CtlValue;
-use tempfile::tempfile;
-
-const BYTES_PER_OP: usize = 512;
-
-/// Attempt to collect final status for all of `liocb`'s operations, freeing
-/// system resources
-fn finish_liocb(liocb: &mut LioCb) {
-    for j in 0..liocb.len() {
-        loop {
-            let e = liocb.error(j);
-            match e {
-                Ok(()) => break,
-                Err(Errno::EINPROGRESS) =>
-                    thread::sleep(time::Duration::from_millis(10)),
-                Err(x) => panic!("aio_error({:?})", x)
-            }
-        }
-        assert_eq!(liocb.aio_return(j).unwrap(), BYTES_PER_OP as isize);
-    }
-}
-
-// Deliberately exceed system resource limits, causing lio_listio to return EIO.
-// This test must run in its own process since it deliberately uses all AIO
-// resources.  ATM it is only enabled on FreeBSD, because I don't know how to
-// check system AIO limits on other operating systems.
-#[test]
-fn test_lio_listio_resubmit() {
-    let mut resubmit_count = 0;
-
-    // Lookup system resource limits
-    let alm = sysconf(SysconfVar::AIO_LISTIO_MAX)
-        .expect("sysconf").unwrap() as usize;
-    let maqpp = if let CtlValue::Int(x) = sysctl::value(
-            "vfs.aio.max_aio_queue_per_proc").unwrap(){
-        x as usize
-    } else {
-        panic!("unknown sysctl");
-    };
-
-    // Find lio_listio sizes that satisfy the AIO_LISTIO_MAX constraint and also
-    // result in a final lio_listio call that can only partially be queued
-    let target_ops = maqpp + alm / 2;
-    let num_listios = (target_ops + alm - 3) / (alm - 2);
-    let ops_per_listio = (target_ops + num_listios - 1) / num_listios;
-    assert!((num_listios - 1) * ops_per_listio < maqpp,
-        "the last lio_listio won't make any progress; fix the algorithm");
-    println!("Using {:?} LioCbs of {:?} operations apiece", num_listios,
-             ops_per_listio);
-
-    let f = tempfile().unwrap();
-    let buffer_set = (0..num_listios).map(|_| {
-        (0..ops_per_listio).map(|_| {
-            vec![0u8; BYTES_PER_OP]
-        }).collect::<Vec<_>>()
-    }).collect::<Vec<_>>();
-
-    let mut liocbs = (0..num_listios).map(|i| {
-        let mut builder = LioCbBuilder::with_capacity(ops_per_listio);
-        for j in 0..ops_per_listio {
-            let offset = (BYTES_PER_OP * (i * ops_per_listio + j)) as off_t;
-            builder = builder.emplace_slice(f.as_raw_fd(),
-                                offset,
-                                &buffer_set[i][j][..],
-                                0,   //priority
-                                SigevNotify::SigevNone,
-                                LioOpcode::LIO_WRITE);
-        }
-        let mut liocb = builder.finish();
-        let mut err = liocb.listio(LioMode::LIO_NOWAIT, SigevNotify::SigevNone);
-        while err == Err(Errno::EIO) ||
-              err == Err(Errno::EAGAIN) ||
-              err == Err(Errno::EINTR) {
-            // 
-            thread::sleep(time::Duration::from_millis(10));
-            resubmit_count += 1;
-            err = liocb.listio_resubmit(LioMode::LIO_NOWAIT,
-                                        SigevNotify::SigevNone);
-        }
-        liocb
-    }).collect::<Vec<_>>();
-
-    // Ensure that every AioCb completed
-    for liocb in liocbs.iter_mut() {
-        finish_liocb(liocb);
-    }
-
-    if resubmit_count > 0 {
-        println!("Resubmitted {:?} times, test passed", resubmit_count);
-    } else {
-        println!("Never resubmitted.  Test ambiguous");
-    }
-}
diff --git a/test/sys/test_mman.rs b/test/sys/test_mman.rs
index a7ceedc..e748427 100644
--- a/test/sys/test_mman.rs
+++ b/test/sys/test_mman.rs
@@ -1,55 +1,78 @@
 use nix::sys::mman::{mmap, MapFlags, ProtFlags};
+use std::num::NonZeroUsize;
 
 #[test]
 fn test_mmap_anonymous() {
     unsafe {
-        let ptr = mmap(std::ptr::null_mut(), 1,
-                       ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
-                       MapFlags::MAP_PRIVATE | MapFlags::MAP_ANONYMOUS, -1, 0)
-                      .unwrap() as *mut u8;
-        assert_eq !(*ptr, 0x00u8);
+        let ptr = mmap(
+            None,
+            NonZeroUsize::new(1).unwrap(),
+            ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
+            MapFlags::MAP_PRIVATE | MapFlags::MAP_ANONYMOUS,
+            -1,
+            0,
+        )
+        .unwrap() as *mut u8;
+        assert_eq!(*ptr, 0x00u8);
         *ptr = 0xffu8;
-        assert_eq !(*ptr, 0xffu8);
+        assert_eq!(*ptr, 0xffu8);
     }
 }
 
 #[test]
 #[cfg(any(target_os = "linux", target_os = "netbsd"))]
 fn test_mremap_grow() {
-    use nix::sys::mman::{mremap, MRemapFlags};
     use nix::libc::{c_void, size_t};
+    use nix::sys::mman::{mremap, MRemapFlags};
 
-    const ONE_K : size_t = 1024;
-    let slice : &mut[u8] = unsafe {
-        let mem = mmap(std::ptr::null_mut(), ONE_K,
-                       ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
-                       MapFlags::MAP_ANONYMOUS | MapFlags::MAP_PRIVATE, -1, 0)
-                      .unwrap();
-        std::slice::from_raw_parts_mut(mem as * mut u8, ONE_K)
+    const ONE_K: size_t = 1024;
+    let one_k_non_zero = NonZeroUsize::new(ONE_K).unwrap();
+
+    let slice: &mut [u8] = unsafe {
+        let mem = mmap(
+            None,
+            one_k_non_zero,
+            ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
+            MapFlags::MAP_ANONYMOUS | MapFlags::MAP_PRIVATE,
+            -1,
+            0,
+        )
+        .unwrap();
+        std::slice::from_raw_parts_mut(mem as *mut u8, ONE_K)
     };
-    assert_eq !(slice[ONE_K - 1], 0x00);
+    assert_eq!(slice[ONE_K - 1], 0x00);
     slice[ONE_K - 1] = 0xFF;
-    assert_eq !(slice[ONE_K - 1], 0xFF);
+    assert_eq!(slice[ONE_K - 1], 0xFF);
 
-    let slice : &mut[u8] = unsafe {
+    let slice: &mut [u8] = unsafe {
         #[cfg(target_os = "linux")]
-        let mem = mremap(slice.as_mut_ptr() as * mut c_void, ONE_K, 10 * ONE_K,
-                         MRemapFlags::MREMAP_MAYMOVE, None)
-                      .unwrap();
+        let mem = mremap(
+            slice.as_mut_ptr() as *mut c_void,
+            ONE_K,
+            10 * ONE_K,
+            MRemapFlags::MREMAP_MAYMOVE,
+            None,
+        )
+        .unwrap();
         #[cfg(target_os = "netbsd")]
-        let mem = mremap(slice.as_mut_ptr() as * mut c_void, ONE_K, 10 * ONE_K,
-                         MRemapFlags::MAP_REMAPDUP, None)
-                      .unwrap();
-        std::slice::from_raw_parts_mut(mem as * mut u8, 10 * ONE_K)
+        let mem = mremap(
+            slice.as_mut_ptr() as *mut c_void,
+            ONE_K,
+            10 * ONE_K,
+            MRemapFlags::MAP_REMAPDUP,
+            None,
+        )
+        .unwrap();
+        std::slice::from_raw_parts_mut(mem as *mut u8, 10 * ONE_K)
     };
 
     // The first KB should still have the old data in it.
-    assert_eq !(slice[ONE_K - 1], 0xFF);
+    assert_eq!(slice[ONE_K - 1], 0xFF);
 
     // The additional range should be zero-init'd and accessible.
-    assert_eq !(slice[10 * ONE_K - 1], 0x00);
+    assert_eq!(slice[10 * ONE_K - 1], 0x00);
     slice[10 * ONE_K - 1] = 0xFF;
-    assert_eq !(slice[10 * ONE_K - 1], 0xFF);
+    assert_eq!(slice[10 * ONE_K - 1], 0xFF);
 }
 
 #[test]
@@ -57,36 +80,43 @@
 // Segfaults for unknown reasons under QEMU for 32-bit targets
 #[cfg_attr(all(target_pointer_width = "32", qemu), ignore)]
 fn test_mremap_shrink() {
-    use nix::sys::mman::{mremap, MRemapFlags};
     use nix::libc::{c_void, size_t};
+    use nix::sys::mman::{mremap, MRemapFlags};
+    use std::num::NonZeroUsize;
 
-    const ONE_K : size_t = 1024;
-    let slice : &mut[u8] = unsafe {
-        let mem = mmap(std::ptr::null_mut(), 10 * ONE_K,
-                       ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
-                       MapFlags::MAP_ANONYMOUS | MapFlags::MAP_PRIVATE, -1, 0)
-                      .unwrap();
-        std::slice::from_raw_parts_mut(mem as * mut u8, ONE_K)
+    const ONE_K: size_t = 1024;
+    let ten_one_k = NonZeroUsize::new(10 * ONE_K).unwrap();
+    let slice: &mut [u8] = unsafe {
+        let mem = mmap(
+            None,
+            ten_one_k,
+            ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
+            MapFlags::MAP_ANONYMOUS | MapFlags::MAP_PRIVATE,
+            -1,
+            0,
+        )
+        .unwrap();
+        std::slice::from_raw_parts_mut(mem as *mut u8, ONE_K)
     };
-    assert_eq !(slice[ONE_K - 1], 0x00);
+    assert_eq!(slice[ONE_K - 1], 0x00);
     slice[ONE_K - 1] = 0xFF;
-    assert_eq !(slice[ONE_K - 1], 0xFF);
+    assert_eq!(slice[ONE_K - 1], 0xFF);
 
-    let slice : &mut[u8] = unsafe {
-        #[cfg(target_os = "linux")]
-        let mem = mremap(slice.as_mut_ptr() as * mut c_void, 10 * ONE_K, ONE_K,
-                         MRemapFlags::empty(), None)
-                      .unwrap();
+    let slice: &mut [u8] = unsafe {
+        let mem = mremap(
+            slice.as_mut_ptr() as *mut c_void,
+            ten_one_k.into(),
+            ONE_K,
+            MRemapFlags::empty(),
+            None,
+        )
+        .unwrap();
         // Since we didn't supply MREMAP_MAYMOVE, the address should be the
         // same.
-        #[cfg(target_os = "netbsd")]
-        let mem = mremap(slice.as_mut_ptr() as * mut c_void, 10 * ONE_K, ONE_K,
-                         MRemapFlags::MAP_FIXED, None)
-                      .unwrap();
-        assert_eq !(mem, slice.as_mut_ptr() as * mut c_void);
-        std::slice::from_raw_parts_mut(mem as * mut u8, ONE_K)
+        assert_eq!(mem, slice.as_mut_ptr() as *mut c_void);
+        std::slice::from_raw_parts_mut(mem as *mut u8, ONE_K)
     };
 
     // The first KB should still be accessible and have the old data in it.
-    assert_eq !(slice[ONE_K - 1], 0xFF);
+    assert_eq!(slice[ONE_K - 1], 0xFF);
 }
diff --git a/test/sys/test_pthread.rs b/test/sys/test_pthread.rs
index fa9b510..ce048ba 100644
--- a/test/sys/test_pthread.rs
+++ b/test/sys/test_pthread.rs
@@ -4,14 +4,14 @@
 #[test]
 fn test_pthread_self() {
     let tid = pthread_self();
-    assert!(tid != ::std::ptr::null_mut());
+    assert!(!tid.is_null());
 }
 
 #[cfg(not(any(target_env = "musl", target_os = "redox")))]
 #[test]
 fn test_pthread_self() {
     let tid = pthread_self();
-    assert!(tid != 0);
+    assert_ne!(tid, 0);
 }
 
 #[test]
diff --git a/test/sys/test_ptrace.rs b/test/sys/test_ptrace.rs
index 83fff9a..530560f 100644
--- a/test/sys/test_ptrace.rs
+++ b/test/sys/test_ptrace.rs
@@ -1,8 +1,14 @@
+#[cfg(all(
+    target_os = "linux",
+    any(target_arch = "x86_64", target_arch = "x86"),
+    target_env = "gnu"
+))]
+use memoffset::offset_of;
 use nix::errno::Errno;
-use nix::unistd::getpid;
 use nix::sys::ptrace;
 #[cfg(any(target_os = "android", target_os = "linux"))]
 use nix::sys::ptrace::Options;
+use nix::unistd::getpid;
 
 #[cfg(any(target_os = "android", target_os = "linux"))]
 use std::mem;
@@ -15,8 +21,9 @@
     // FIXME: qemu-user doesn't implement ptrace on all arches, so permit ENOSYS
     require_capability!("test_ptrace", CAP_SYS_PTRACE);
     let err = ptrace::attach(getpid()).unwrap_err();
-    assert!(err == Errno::EPERM || err == Errno::EINVAL ||
-            err == Errno::ENOSYS);
+    assert!(
+        err == Errno::EPERM || err == Errno::EINVAL || err == Errno::ENOSYS
+    );
 }
 
 // Just make sure ptrace_setoptions can be called at all, for now.
@@ -24,8 +31,9 @@
 #[cfg(any(target_os = "android", target_os = "linux"))]
 fn test_ptrace_setoptions() {
     require_capability!("test_ptrace_setoptions", CAP_SYS_PTRACE);
-    let err = ptrace::setoptions(getpid(), Options::PTRACE_O_TRACESYSGOOD).unwrap_err();
-    assert!(err != Errno::EOPNOTSUPP);
+    let err = ptrace::setoptions(getpid(), Options::PTRACE_O_TRACESYSGOOD)
+        .unwrap_err();
+    assert_ne!(err, Errno::EOPNOTSUPP);
 }
 
 // Just make sure ptrace_getevent can be called at all, for now.
@@ -34,7 +42,7 @@
 fn test_ptrace_getevent() {
     require_capability!("test_ptrace_getevent", CAP_SYS_PTRACE);
     let err = ptrace::getevent(getpid()).unwrap_err();
-    assert!(err != Errno::EOPNOTSUPP);
+    assert_ne!(err, Errno::EOPNOTSUPP);
 }
 
 // Just make sure ptrace_getsiginfo can be called at all, for now.
@@ -58,7 +66,6 @@
     }
 }
 
-
 #[test]
 fn test_ptrace_cont() {
     use nix::sys::ptrace;
@@ -72,7 +79,7 @@
     let _m = crate::FORK_MTX.lock();
 
     // FIXME: qemu-user doesn't implement ptrace on all architectures
-    // and retunrs ENOSYS in this case.
+    // and returns ENOSYS in this case.
     // We (ab)use this behavior to detect the affected platforms
     // and skip the test then.
     // On valid platforms the ptrace call should return Errno::EPERM, this
@@ -82,7 +89,7 @@
         return;
     }
 
-    match unsafe{fork()}.expect("Error: Fork Failed") {
+    match unsafe { fork() }.expect("Error: Fork Failed") {
         Child => {
             ptrace::traceme().unwrap();
             // As recommended by ptrace(2), raise SIGTRAP to pause the child
@@ -90,15 +97,22 @@
             loop {
                 raise(Signal::SIGTRAP).unwrap();
             }
-
-        },
+        }
         Parent { child } => {
-            assert_eq!(waitpid(child, None), Ok(WaitStatus::Stopped(child, Signal::SIGTRAP)));
+            assert_eq!(
+                waitpid(child, None),
+                Ok(WaitStatus::Stopped(child, Signal::SIGTRAP))
+            );
             ptrace::cont(child, None).unwrap();
-            assert_eq!(waitpid(child, None), Ok(WaitStatus::Stopped(child, Signal::SIGTRAP)));
+            assert_eq!(
+                waitpid(child, None),
+                Ok(WaitStatus::Stopped(child, Signal::SIGTRAP))
+            );
             ptrace::cont(child, Some(Signal::SIGKILL)).unwrap();
             match waitpid(child, None) {
-                Ok(WaitStatus::Signaled(pid, Signal::SIGKILL, _)) if pid == child => {
+                Ok(WaitStatus::Signaled(pid, Signal::SIGKILL, _))
+                    if pid == child =>
+                {
                     // FIXME It's been observed on some systems (apple) the
                     // tracee may not be killed but remain as a zombie process
                     // affecting other wait based tests. Add an extra kill just
@@ -110,7 +124,7 @@
                 }
                 _ => panic!("The process should have been killed"),
             }
-        },
+        }
     }
 }
 
@@ -129,22 +143,28 @@
 
     let _m = crate::FORK_MTX.lock();
 
-    match unsafe{fork()}.expect("Error: Fork Failed") {
-        Child => {
-            loop {
-                sleep(Duration::from_millis(1000));
-            }
-
+    match unsafe { fork() }.expect("Error: Fork Failed") {
+        Child => loop {
+            sleep(Duration::from_millis(1000));
         },
         Parent { child } => {
-            ptrace::seize(child, ptrace::Options::PTRACE_O_TRACESYSGOOD).unwrap();
+            ptrace::seize(child, ptrace::Options::PTRACE_O_TRACESYSGOOD)
+                .unwrap();
             ptrace::interrupt(child).unwrap();
-            assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceEvent(child, Signal::SIGTRAP, 128)));
+            assert_eq!(
+                waitpid(child, None),
+                Ok(WaitStatus::PtraceEvent(child, Signal::SIGTRAP, 128))
+            );
             ptrace::syscall(child, None).unwrap();
-            assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child)));
+            assert_eq!(
+                waitpid(child, None),
+                Ok(WaitStatus::PtraceSyscall(child))
+            );
             ptrace::detach(child, Some(Signal::SIGKILL)).unwrap();
             match waitpid(child, None) {
-                Ok(WaitStatus::Signaled(pid, Signal::SIGKILL, _)) if pid == child => {
+                Ok(WaitStatus::Signaled(pid, Signal::SIGKILL, _))
+                    if pid == child =>
+                {
                     let _ = waitpid(child, Some(WaitPidFlag::WNOHANG));
                     while ptrace::cont(child, Some(Signal::SIGKILL)).is_ok() {
                         let _ = waitpid(child, Some(WaitPidFlag::WNOHANG));
@@ -152,19 +172,20 @@
                 }
                 _ => panic!("The process should have been killed"),
             }
-        },
+        }
     }
 }
 
 // ptrace::{setoptions, getregs} are only available in these platforms
-#[cfg(all(target_os = "linux",
-          any(target_arch = "x86_64",
-              target_arch = "x86"),
-          target_env = "gnu"))]
+#[cfg(all(
+    target_os = "linux",
+    any(target_arch = "x86_64", target_arch = "x86"),
+    target_env = "gnu"
+))]
 #[test]
 fn test_ptrace_syscall() {
-    use nix::sys::signal::kill;
     use nix::sys::ptrace;
+    use nix::sys::signal::kill;
     use nix::sys::signal::Signal;
     use nix::sys::wait::{waitpid, WaitStatus};
     use nix::unistd::fork;
@@ -175,45 +196,80 @@
 
     let _m = crate::FORK_MTX.lock();
 
-    match unsafe{fork()}.expect("Error: Fork Failed") {
+    match unsafe { fork() }.expect("Error: Fork Failed") {
         Child => {
             ptrace::traceme().unwrap();
             // first sigstop until parent is ready to continue
             let pid = getpid();
             kill(pid, Signal::SIGSTOP).unwrap();
             kill(pid, Signal::SIGTERM).unwrap();
-            unsafe { ::libc::_exit(0); }
-        },
+            unsafe {
+                ::libc::_exit(0);
+            }
+        }
 
         Parent { child } => {
-            assert_eq!(waitpid(child, None), Ok(WaitStatus::Stopped(child, Signal::SIGSTOP)));
+            assert_eq!(
+                waitpid(child, None),
+                Ok(WaitStatus::Stopped(child, Signal::SIGSTOP))
+            );
 
             // set this option to recognize syscall-stops
-            ptrace::setoptions(child, ptrace::Options::PTRACE_O_TRACESYSGOOD).unwrap();
+            ptrace::setoptions(child, ptrace::Options::PTRACE_O_TRACESYSGOOD)
+                .unwrap();
 
             #[cfg(target_arch = "x86_64")]
-            let get_syscall_id = || ptrace::getregs(child).unwrap().orig_rax as libc::c_long;
+            let get_syscall_id =
+                || ptrace::getregs(child).unwrap().orig_rax as libc::c_long;
 
             #[cfg(target_arch = "x86")]
-            let get_syscall_id = || ptrace::getregs(child).unwrap().orig_eax as libc::c_long;
+            let get_syscall_id =
+                || ptrace::getregs(child).unwrap().orig_eax as libc::c_long;
+
+            // this duplicates `get_syscall_id` for the purpose of testing `ptrace::read_user`.
+            #[cfg(target_arch = "x86_64")]
+            let rax_offset = offset_of!(libc::user_regs_struct, orig_rax);
+            #[cfg(target_arch = "x86")]
+            let rax_offset = offset_of!(libc::user_regs_struct, orig_eax);
+
+            let get_syscall_from_user_area = || {
+                // Find the offset of `user.regs.rax` (or `user.regs.eax` for x86)
+                let rax_offset = offset_of!(libc::user, regs) + rax_offset;
+                ptrace::read_user(child, rax_offset as _).unwrap()
+                    as libc::c_long
+            };
 
             // kill entry
             ptrace::syscall(child, None).unwrap();
-            assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child)));
+            assert_eq!(
+                waitpid(child, None),
+                Ok(WaitStatus::PtraceSyscall(child))
+            );
             assert_eq!(get_syscall_id(), ::libc::SYS_kill);
+            assert_eq!(get_syscall_from_user_area(), ::libc::SYS_kill);
 
             // kill exit
             ptrace::syscall(child, None).unwrap();
-            assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child)));
+            assert_eq!(
+                waitpid(child, None),
+                Ok(WaitStatus::PtraceSyscall(child))
+            );
             assert_eq!(get_syscall_id(), ::libc::SYS_kill);
+            assert_eq!(get_syscall_from_user_area(), ::libc::SYS_kill);
 
             // receive signal
             ptrace::syscall(child, None).unwrap();
-            assert_eq!(waitpid(child, None), Ok(WaitStatus::Stopped(child, Signal::SIGTERM)));
+            assert_eq!(
+                waitpid(child, None),
+                Ok(WaitStatus::Stopped(child, Signal::SIGTERM))
+            );
 
             // inject signal
             ptrace::syscall(child, Signal::SIGTERM).unwrap();
-            assert_eq!(waitpid(child, None), Ok(WaitStatus::Signaled(child, Signal::SIGTERM, false)));
-        },
+            assert_eq!(
+                waitpid(child, None),
+                Ok(WaitStatus::Signaled(child, Signal::SIGTERM, false))
+            );
+        }
     }
 }
diff --git a/test/sys/test_select.rs b/test/sys/test_select.rs
index 2f7396b..40bda4d 100644
--- a/test/sys/test_select.rs
+++ b/test/sys/test_select.rs
@@ -1,7 +1,7 @@
 use nix::sys::select::*;
-use nix::unistd::{pipe, write};
 use nix::sys::signal::SigSet;
 use nix::sys::time::{TimeSpec, TimeValLike};
+use nix::unistd::{pipe, write};
 
 #[test]
 pub fn test_pselect() {
@@ -45,7 +45,8 @@
             None,
             &timeout,
             None
-        ).unwrap()
+        )
+        .unwrap()
     );
     assert!(fd_set.contains(r1));
     assert!(!fd_set.contains(r2));
diff --git a/test/sys/test_signal.rs b/test/sys/test_signal.rs
index fdd2568..3ad14f4 100644
--- a/test/sys/test_signal.rs
+++ b/test/sys/test_signal.rs
@@ -52,9 +52,12 @@
 
     // Make sure the old set doesn't contain the signal, otherwise the following
     // test don't make sense.
-    assert!(!old_signal_set.contains(SIGNAL),
-            "the {:?} signal is already blocked, please change to a \
-             different one", SIGNAL);
+    assert!(
+        !old_signal_set.contains(SIGNAL),
+        "the {:?} signal is already blocked, please change to a \
+             different one",
+        SIGNAL
+    );
 
     // Now block the signal.
     let mut signal_set = SigSet::empty();
@@ -66,8 +69,11 @@
     old_signal_set.clear();
     sigprocmask(SigmaskHow::SIG_BLOCK, None, Some(&mut old_signal_set))
         .expect("expect to be able to retrieve old signals");
-    assert!(old_signal_set.contains(SIGNAL),
-            "expected the {:?} to be blocked", SIGNAL);
+    assert!(
+        old_signal_set.contains(SIGNAL),
+        "expected the {:?} to be blocked",
+        SIGNAL
+    );
 
     // Reset the signal.
     sigprocmask(SigmaskHow::SIG_UNBLOCK, Some(&signal_set), None)
@@ -78,13 +84,18 @@
     static ref SIGNALED: AtomicBool = AtomicBool::new(false);
 }
 
-extern fn test_sigaction_handler(signal: libc::c_int) {
+extern "C" fn test_sigaction_handler(signal: libc::c_int) {
     let signal = Signal::try_from(signal).unwrap();
     SIGNALED.store(signal == Signal::SIGINT, Ordering::Relaxed);
 }
 
 #[cfg(not(target_os = "redox"))]
-extern fn test_sigaction_action(_: libc::c_int, _: *mut libc::siginfo_t, _: *mut libc::c_void) {}
+extern "C" fn test_sigaction_action(
+    _: libc::c_int,
+    _: *mut libc::siginfo_t,
+    _: *mut libc::c_void,
+) {
+}
 
 #[test]
 #[cfg(not(target_os = "redox"))]
@@ -92,7 +103,10 @@
     let _m = crate::SIGNAL_MTX.lock();
 
     let action_handler = SigHandler::SigAction(test_sigaction_action);
-    assert_eq!(unsafe { signal(Signal::SIGINT, action_handler) }.unwrap_err(), Errno::ENOTSUP);
+    assert_eq!(
+        unsafe { signal(Signal::SIGINT, action_handler) }.unwrap_err(),
+        Errno::ENOTSUP
+    );
 }
 
 #[test]
@@ -101,20 +115,32 @@
 
     unsafe { signal(Signal::SIGINT, SigHandler::SigIgn) }.unwrap();
     raise(Signal::SIGINT).unwrap();
-    assert_eq!(unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(), SigHandler::SigIgn);
+    assert_eq!(
+        unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(),
+        SigHandler::SigIgn
+    );
 
     let handler = SigHandler::Handler(test_sigaction_handler);
-    assert_eq!(unsafe { signal(Signal::SIGINT, handler) }.unwrap(), SigHandler::SigDfl);
+    assert_eq!(
+        unsafe { signal(Signal::SIGINT, handler) }.unwrap(),
+        SigHandler::SigDfl
+    );
     raise(Signal::SIGINT).unwrap();
     assert!(SIGNALED.load(Ordering::Relaxed));
 
     #[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
-    assert_eq!(unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(), handler);
+    assert_eq!(
+        unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(),
+        handler
+    );
 
     // System V based OSes (e.g. illumos and Solaris) always resets the
     // disposition to SIG_DFL prior to calling the signal handler
     #[cfg(any(target_os = "illumos", target_os = "solaris"))]
-    assert_eq!(unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(), SigHandler::SigDfl);
+    assert_eq!(
+        unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(),
+        SigHandler::SigDfl
+    );
 
     // Restore default signal handler
     unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap();
diff --git a/test/sys/test_signalfd.rs b/test/sys/test_signalfd.rs
index b6f748b..87153c9 100644
--- a/test/sys/test_signalfd.rs
+++ b/test/sys/test_signalfd.rs
@@ -2,8 +2,8 @@
 
 #[test]
 fn test_signalfd() {
+    use nix::sys::signal::{self, raise, SigSet, Signal};
     use nix::sys::signalfd::SignalFd;
-    use nix::sys::signal::{self, raise, Signal, SigSet};
 
     // Grab the mutex for altering signals so we don't interfere with other tests.
     let _m = crate::SIGNAL_MTX.lock();
diff --git a/test/sys/test_socket.rs b/test/sys/test_socket.rs
index 0f6fac6..5adc77e 100644
--- a/test/sys/test_socket.rs
+++ b/test/sys/test_socket.rs
@@ -1,16 +1,21 @@
-use nix::sys::socket::{AddressFamily, InetAddr, SockAddr, UnixAddr, getsockname, sockaddr, sockaddr_in6, sockaddr_storage_to_addr};
+#[cfg(any(target_os = "linux", target_os = "android"))]
+use crate::*;
+use libc::{c_char, sockaddr_storage};
+#[allow(deprecated)]
+use nix::sys::socket::InetAddr;
+use nix::sys::socket::{
+    getsockname, sockaddr, sockaddr_in6, AddressFamily, UnixAddr,
+};
 use std::collections::hash_map::DefaultHasher;
 use std::hash::{Hash, Hasher};
 use std::mem::{self, MaybeUninit};
-use std::net::{self, Ipv6Addr, SocketAddr, SocketAddrV6};
+use std::net::{self, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
 use std::os::unix::io::RawFd;
 use std::path::Path;
 use std::slice;
 use std::str::FromStr;
-use libc::{c_char, sockaddr_storage};
-#[cfg(any(target_os = "linux", target_os= "android"))]
-use crate::*;
 
+#[allow(deprecated)]
 #[test]
 pub fn test_inetv4_addr_to_sock_addr() {
     let actual: net::SocketAddr = FromStr::from_str("127.0.0.1:3000").unwrap();
@@ -34,8 +39,11 @@
     assert_eq!(actual, inet);
 }
 
+#[allow(deprecated)]
 #[test]
 pub fn test_inetv4_addr_roundtrip_sockaddr_storage_to_addr() {
+    use nix::sys::socket::{sockaddr_storage_to_addr, SockAddr};
+
     let actual: net::SocketAddr = FromStr::from_str("127.0.0.1:3000").unwrap();
     let addr = InetAddr::from_std(&actual);
     let sockaddr = SockAddr::new_inet(addr);
@@ -51,41 +59,87 @@
         }
     };
 
-    let from_storage = sockaddr_storage_to_addr(&storage, ffi_size as usize).unwrap();
+    let from_storage =
+        sockaddr_storage_to_addr(&storage, ffi_size as usize).unwrap();
     assert_eq!(from_storage, sockaddr);
-    let from_storage = sockaddr_storage_to_addr(&storage, mem::size_of::<sockaddr_storage>()).unwrap();
+    let from_storage =
+        sockaddr_storage_to_addr(&storage, mem::size_of::<sockaddr_storage>())
+            .unwrap();
     assert_eq!(from_storage, sockaddr);
 }
 
+#[cfg(any(target_os = "linux"))]
+#[cfg_attr(qemu, ignore)]
 #[test]
-pub fn test_inetv6_addr_to_sock_addr() {
-    let port: u16 = 3000;
-    let flowinfo: u32 = 1;
-    let scope_id: u32 = 2;
-    let ip: Ipv6Addr = "fe80::1".parse().unwrap();
+pub fn test_timestamping() {
+    use nix::sys::socket::{
+        recvmsg, sendmsg, setsockopt, socket, sockopt::Timestamping,
+        ControlMessageOwned, MsgFlags, SockFlag, SockType, SockaddrIn,
+        TimestampingFlag,
+    };
+    use std::io::{IoSlice, IoSliceMut};
 
-    let actual = SocketAddr::V6(SocketAddrV6::new(ip, port, flowinfo, scope_id));
-    let addr = InetAddr::from_std(&actual);
+    let sock_addr = SockaddrIn::from_str("127.0.0.1:6790").unwrap();
 
-    match addr {
-        InetAddr::V6(addr) => {
-            assert_eq!(addr.sin6_port, port.to_be());
-            assert_eq!(addr.sin6_flowinfo, flowinfo);
-            assert_eq!(addr.sin6_scope_id, scope_id);
+    let ssock = socket(
+        AddressFamily::Inet,
+        SockType::Datagram,
+        SockFlag::empty(),
+        None,
+    )
+    .expect("send socket failed");
+
+    let rsock = socket(
+        AddressFamily::Inet,
+        SockType::Datagram,
+        SockFlag::empty(),
+        None,
+    )
+    .unwrap();
+    nix::sys::socket::bind(rsock, &sock_addr).unwrap();
+
+    setsockopt(rsock, Timestamping, &TimestampingFlag::all()).unwrap();
+
+    let sbuf = [0u8; 2048];
+    let mut rbuf = [0u8; 2048];
+    let flags = MsgFlags::empty();
+    let iov1 = [IoSlice::new(&sbuf)];
+    let mut iov2 = [IoSliceMut::new(&mut rbuf)];
+
+    let mut cmsg = cmsg_space!(nix::sys::socket::Timestamps);
+    sendmsg(ssock, &iov1, &[], flags, Some(&sock_addr)).unwrap();
+    let recv = recvmsg::<()>(rsock, &mut iov2, Some(&mut cmsg), flags).unwrap();
+
+    let mut ts = None;
+    for c in recv.cmsgs() {
+        if let ControlMessageOwned::ScmTimestampsns(timestamps) = c {
+            ts = Some(timestamps.system);
         }
-        _ => panic!("nope"),
     }
-
-    assert_eq!(actual, addr.to_std());
+    let ts = ts.expect("ScmTimestampns is present");
+    let sys_time =
+        ::nix::time::clock_gettime(::nix::time::ClockId::CLOCK_REALTIME)
+            .unwrap();
+    let diff = if ts > sys_time {
+        ts - sys_time
+    } else {
+        sys_time - ts
+    };
+    assert!(std::time::Duration::from(diff).as_secs() < 60);
 }
+
+#[allow(deprecated)]
 #[test]
 pub fn test_inetv6_addr_roundtrip_sockaddr_storage_to_addr() {
+    use nix::sys::socket::{sockaddr_storage_to_addr, SockAddr};
+
     let port: u16 = 3000;
     let flowinfo: u32 = 1;
     let scope_id: u32 = 2;
     let ip: Ipv6Addr = "fe80::1".parse().unwrap();
 
-    let actual = SocketAddr::V6(SocketAddrV6::new(ip, port, flowinfo, scope_id));
+    let actual =
+        SocketAddr::V6(SocketAddrV6::new(ip, port, flowinfo, scope_id));
     let addr = InetAddr::from_std(&actual);
     let sockaddr = SockAddr::new_inet(addr);
 
@@ -95,14 +149,20 @@
         let (ffi_ptr, ffi_size) = sockaddr.as_ffi_pair();
         assert_eq!(mem::size_of::<sockaddr_in6>(), ffi_size as usize);
         unsafe {
-            storage_ptr.copy_from_nonoverlapping((ffi_ptr as *const sockaddr).cast::<sockaddr_in6>(), 1);
+            storage_ptr.copy_from_nonoverlapping(
+                (ffi_ptr as *const sockaddr).cast::<sockaddr_in6>(),
+                1,
+            );
             (storage.assume_init(), ffi_size)
         }
     };
 
-    let from_storage = sockaddr_storage_to_addr(&storage, ffi_size as usize).unwrap();
+    let from_storage =
+        sockaddr_storage_to_addr(&storage, ffi_size as usize).unwrap();
     assert_eq!(from_storage, sockaddr);
-    let from_storage = sockaddr_storage_to_addr(&storage, mem::size_of::<sockaddr_storage>()).unwrap();
+    let from_storage =
+        sockaddr_storage_to_addr(&storage, mem::size_of::<sockaddr_storage>())
+            .unwrap();
     assert_eq!(from_storage, sockaddr);
 }
 
@@ -144,7 +204,7 @@
 pub fn test_abstract_sun_path_too_long() {
     let name = String::from("nix\0abstract\0tesnix\0abstract\0tesnix\0abstract\0tesnix\0abstract\0tesnix\0abstract\0testttttnix\0abstract\0test\0make\0sure\0this\0is\0long\0enough");
     let addr = UnixAddr::new_abstract(name.as_bytes());
-    assert!(addr.is_err());
+    addr.expect_err("assertion failed");
 }
 
 #[cfg(any(target_os = "android", target_os = "linux"))]
@@ -163,7 +223,7 @@
 }
 
 // Test getting/setting abstract addresses (without unix socket creation)
-#[cfg(target_os = "linux")]
+#[cfg(any(target_os = "android", target_os = "linux"))]
 #[test]
 pub fn test_abstract_uds_addr() {
     let empty = String::new();
@@ -174,7 +234,8 @@
     let name = String::from("nix\0abstract\0test");
     let addr = UnixAddr::new_abstract(name.as_bytes()).unwrap();
     let sun_path = [
-        110u8, 105, 120, 0, 97, 98, 115, 116, 114, 97, 99, 116, 0, 116, 101, 115, 116
+        110u8, 105, 120, 0, 97, 98, 115, 116, 114, 97, 99, 116, 0, 116, 101,
+        115, 116,
     ];
     assert_eq!(addr.as_abstract(), Some(&sun_path[..]));
     assert_eq!(addr.path(), None);
@@ -183,46 +244,90 @@
     assert_eq!(unsafe { (*addr.as_ptr()).sun_path[0] }, 0);
 }
 
+// Test getting an unnamed address (without unix socket creation)
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[test]
+pub fn test_unnamed_uds_addr() {
+    use crate::nix::sys::socket::SockaddrLike;
+
+    let addr = UnixAddr::new_unnamed();
+
+    assert!(addr.is_unnamed());
+    assert_eq!(addr.len(), 2);
+    assert!(addr.path().is_none());
+    assert_eq!(addr.path_len(), 0);
+
+    assert!(addr.as_abstract().is_none());
+}
+
 #[test]
 pub fn test_getsockname() {
-    use nix::sys::socket::{socket, AddressFamily, SockType, SockFlag};
-    use nix::sys::socket::{bind, SockAddr};
+    use nix::sys::socket::bind;
+    use nix::sys::socket::{socket, AddressFamily, SockFlag, SockType};
 
     let tempdir = tempfile::tempdir().unwrap();
     let sockname = tempdir.path().join("sock");
-    let sock = socket(AddressFamily::Unix, SockType::Stream, SockFlag::empty(), None)
-               .expect("socket failed");
-    let sockaddr = SockAddr::new_unix(&sockname).unwrap();
+    let sock = socket(
+        AddressFamily::Unix,
+        SockType::Stream,
+        SockFlag::empty(),
+        None,
+    )
+    .expect("socket failed");
+    let sockaddr = UnixAddr::new(&sockname).unwrap();
     bind(sock, &sockaddr).expect("bind failed");
     assert_eq!(sockaddr, getsockname(sock).expect("getsockname failed"));
 }
 
 #[test]
 pub fn test_socketpair() {
+    use nix::sys::socket::{socketpair, AddressFamily, SockFlag, SockType};
     use nix::unistd::{read, write};
-    use nix::sys::socket::{socketpair, AddressFamily, SockType, SockFlag};
 
-    let (fd1, fd2) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty())
-                     .unwrap();
+    let (fd1, fd2) = socketpair(
+        AddressFamily::Unix,
+        SockType::Stream,
+        None,
+        SockFlag::empty(),
+    )
+    .unwrap();
     write(fd1, b"hello").unwrap();
-    let mut buf = [0;5];
+    let mut buf = [0; 5];
     read(fd2, &mut buf).unwrap();
 
     assert_eq!(&buf[..], b"hello");
 }
 
-mod recvfrom {
-    use nix::Result;
+#[test]
+pub fn test_std_conversions() {
     use nix::sys::socket::*;
-    use std::thread;
+
+    let std_sa = SocketAddrV4::from_str("127.0.0.1:6789").unwrap();
+    let sock_addr = SockaddrIn::from(std_sa);
+    assert_eq!(std_sa, sock_addr.into());
+
+    let std_sa = SocketAddrV6::from_str("[::1]:6000").unwrap();
+    let sock_addr: SockaddrIn6 = SockaddrIn6::from(std_sa);
+    assert_eq!(std_sa, sock_addr.into());
+}
+
+mod recvfrom {
     use super::*;
+    use nix::sys::socket::*;
+    use nix::{errno::Errno, Result};
+    use std::thread;
 
     const MSG: &[u8] = b"Hello, World!";
 
-    fn sendrecv<Fs, Fr>(rsock: RawFd, ssock: RawFd, f_send: Fs, mut f_recv: Fr) -> Option<SockAddr>
-        where
-            Fs: Fn(RawFd, &[u8], MsgFlags) -> Result<usize> + Send + 'static,
-            Fr: FnMut(usize, Option<SockAddr>),
+    fn sendrecv<Fs, Fr>(
+        rsock: RawFd,
+        ssock: RawFd,
+        f_send: Fs,
+        mut f_recv: Fr,
+    ) -> Option<SockaddrStorage>
+    where
+        Fs: Fn(RawFd, &[u8], MsgFlags) -> Result<usize> + Send + 'static,
+        Fr: FnMut(usize, Option<SockaddrStorage>),
     {
         let mut buf: [u8; 13] = [0u8; 13];
         let mut l = 0;
@@ -248,43 +353,51 @@
 
     #[test]
     pub fn stream() {
-        let (fd2, fd1) = socketpair(AddressFamily::Unix, SockType::Stream,
-                                    None, SockFlag::empty()).unwrap();
+        let (fd2, fd1) = socketpair(
+            AddressFamily::Unix,
+            SockType::Stream,
+            None,
+            SockFlag::empty(),
+        )
+        .unwrap();
         // Ignore from for stream sockets
-        let _ = sendrecv(fd1, fd2, |s, m, flags| {
-            send(s, m, flags)
-        }, |_, _| {});
+        let _ = sendrecv(fd1, fd2, send, |_, _| {});
     }
 
     #[test]
     pub fn udp() {
-        let std_sa = SocketAddr::from_str("127.0.0.1:6789").unwrap();
-        let inet_addr = InetAddr::from_std(&std_sa);
-        let sock_addr = SockAddr::new_inet(inet_addr);
-        let rsock = socket(AddressFamily::Inet,
+        let std_sa = SocketAddrV4::from_str("127.0.0.1:6789").unwrap();
+        let sock_addr = SockaddrIn::from(std_sa);
+        let rsock = socket(
+            AddressFamily::Inet,
             SockType::Datagram,
             SockFlag::empty(),
-            None
-        ).unwrap();
+            None,
+        )
+        .unwrap();
         bind(rsock, &sock_addr).unwrap();
         let ssock = socket(
             AddressFamily::Inet,
             SockType::Datagram,
             SockFlag::empty(),
             None,
-        ).expect("send socket failed");
-        let from = sendrecv(rsock, ssock, move |s, m, flags| {
-            sendto(s, m, &sock_addr, flags)
-        },|_, _| {});
+        )
+        .expect("send socket failed");
+        let from = sendrecv(
+            rsock,
+            ssock,
+            move |s, m, flags| sendto(s, m, &sock_addr, flags),
+            |_, _| {},
+        );
         // UDP sockets should set the from address
-        assert_eq!(AddressFamily::Inet, from.unwrap().family());
+        assert_eq!(AddressFamily::Inet, from.unwrap().family().unwrap());
     }
 
     #[cfg(target_os = "linux")]
     mod udp_offload {
         use super::*;
-        use nix::sys::uio::IoVec;
         use nix::sys::socket::sockopt::{UdpGroSegment, UdpGsoSegment};
+        use std::io::IoSlice;
 
         #[test]
         // Disable the test under emulation because it fails in Cirrus-CI.  Lack
@@ -298,14 +411,14 @@
             // with size 2 and two UDP packet with size 1 will be sent.
             let segment_size: u16 = 2;
 
-            let std_sa = SocketAddr::from_str("127.0.0.1:6791").unwrap();
-            let inet_addr = InetAddr::from_std(&std_sa);
-            let sock_addr = SockAddr::new_inet(inet_addr);
-            let rsock = socket(AddressFamily::Inet,
-                               SockType::Datagram,
-                               SockFlag::empty(),
-                               None
-            ).unwrap();
+            let sock_addr = SockaddrIn::new(127, 0, 0, 1, 6791);
+            let rsock = socket(
+                AddressFamily::Inet,
+                SockType::Datagram,
+                SockFlag::empty(),
+                None,
+            )
+            .unwrap();
 
             setsockopt(rsock, UdpGsoSegment, &(segment_size as _))
                 .expect("setsockopt UDP_SEGMENT failed");
@@ -316,24 +429,30 @@
                 SockType::Datagram,
                 SockFlag::empty(),
                 None,
-            ).expect("send socket failed");
+            )
+            .expect("send socket failed");
 
             let mut num_packets_received: i32 = 0;
 
-            sendrecv(rsock, ssock, move |s, m, flags| {
-                let iov = [IoVec::from_slice(m)];
-                let cmsg = ControlMessage::UdpGsoSegments(&segment_size);
-                sendmsg(s, &iov, &[cmsg], flags, Some(&sock_addr))
-            }, {
-                let num_packets_received_ref = &mut num_packets_received;
+            sendrecv(
+                rsock,
+                ssock,
+                move |s, m, flags| {
+                    let iov = [IoSlice::new(m)];
+                    let cmsg = ControlMessage::UdpGsoSegments(&segment_size);
+                    sendmsg(s, &iov, &[cmsg], flags, Some(&sock_addr))
+                },
+                {
+                    let num_packets_received_ref = &mut num_packets_received;
 
-                move |len, _| {
-                    // check that we receive UDP packets with payload size
-                    // less or equal to segment size
-                    assert!(len <= segment_size as usize);
-                    *num_packets_received_ref += 1;
-                }
-            });
+                    move |len, _| {
+                        // check that we receive UDP packets with payload size
+                        // less or equal to segment size
+                        assert!(len <= segment_size as usize);
+                        *num_packets_received_ref += 1;
+                    }
+                },
+            );
 
             // Buffer size is 13, we will receive six packets of size 2,
             // and one packet of size 1.
@@ -350,11 +469,13 @@
             // It's hard to guarantee receiving GRO packets. Just checking
             // that `setsockopt` doesn't fail with error
 
-            let rsock = socket(AddressFamily::Inet,
-                               SockType::Datagram,
-                               SockFlag::empty(),
-                               None
-            ).unwrap();
+            let rsock = socket(
+                AddressFamily::Inet,
+                SockType::Datagram,
+                SockFlag::empty(),
+                None,
+            )
+            .unwrap();
 
             setsockopt(rsock, UdpGroSegment, &true)
                 .expect("setsockopt UDP_GRO failed");
@@ -369,62 +490,63 @@
     ))]
     #[test]
     pub fn udp_sendmmsg() {
-        use nix::sys::uio::IoVec;
+        use std::io::IoSlice;
 
-        let std_sa = SocketAddr::from_str("127.0.0.1:6793").unwrap();
-        let std_sa2 = SocketAddr::from_str("127.0.0.1:6794").unwrap();
-        let inet_addr = InetAddr::from_std(&std_sa);
-        let inet_addr2 = InetAddr::from_std(&std_sa2);
-        let sock_addr = SockAddr::new_inet(inet_addr);
-        let sock_addr2 = SockAddr::new_inet(inet_addr2);
+        let std_sa = SocketAddrV4::from_str("127.0.0.1:6793").unwrap();
+        let std_sa2 = SocketAddrV4::from_str("127.0.0.1:6794").unwrap();
+        let sock_addr = SockaddrIn::from(std_sa);
+        let sock_addr2 = SockaddrIn::from(std_sa2);
 
-        let rsock = socket(AddressFamily::Inet,
+        let rsock = socket(
+            AddressFamily::Inet,
             SockType::Datagram,
             SockFlag::empty(),
-            None
-        ).unwrap();
+            None,
+        )
+        .unwrap();
         bind(rsock, &sock_addr).unwrap();
         let ssock = socket(
             AddressFamily::Inet,
             SockType::Datagram,
             SockFlag::empty(),
             None,
-        ).expect("send socket failed");
+        )
+        .expect("send socket failed");
 
-        let from = sendrecv(rsock, ssock, move |s, m, flags| {
-            let iov = [IoVec::from_slice(m)];
-            let mut msgs = vec![
-                SendMmsgData {
-                    iov: &iov,
-                    cmsgs: &[],
-                    addr: Some(sock_addr),
-                    _lt: Default::default(),
+        let from = sendrecv(
+            rsock,
+            ssock,
+            move |s, m, flags| {
+                let batch_size = 15;
+                let mut iovs = Vec::with_capacity(1 + batch_size);
+                let mut addrs = Vec::with_capacity(1 + batch_size);
+                let mut data = MultiHeaders::preallocate(1 + batch_size, None);
+                let iov = IoSlice::new(m);
+                // first chunk:
+                iovs.push([iov]);
+                addrs.push(Some(sock_addr));
+
+                for _ in 0..batch_size {
+                    iovs.push([iov]);
+                    addrs.push(Some(sock_addr2));
                 }
-            ];
 
-            let batch_size = 15;
-
-            for _ in 0..batch_size {
-                msgs.push(
-                    SendMmsgData {
-                        iov: &iov,
-                        cmsgs: &[],
-                        addr: Some(sock_addr2),
-                        _lt: Default::default(),
-                    }
-                );
-            }
-            sendmmsg(s, msgs.iter(), flags)
-                .map(move |sent_bytes| {
-                    assert!(!sent_bytes.is_empty());
-                    for sent in &sent_bytes {
-                        assert_eq!(*sent, m.len());
-                    }
-                    sent_bytes.len()
-                })
-        }, |_, _ | {});
+                let res = sendmmsg(s, &mut data, &iovs, addrs, [], flags)?;
+                let mut sent_messages = 0;
+                let mut sent_bytes = 0;
+                for item in res {
+                    sent_messages += 1;
+                    sent_bytes += item.bytes;
+                }
+                //
+                assert_eq!(sent_messages, iovs.len());
+                assert_eq!(sent_bytes, sent_messages * m.len());
+                Ok(sent_messages)
+            },
+            |_, _| {},
+        );
         // UDP sockets should set the from address
-        assert_eq!(AddressFamily::Inet, from.unwrap().family());
+        assert_eq!(AddressFamily::Inet, from.unwrap().family().unwrap());
     }
 
     #[cfg(any(
@@ -435,32 +557,35 @@
     ))]
     #[test]
     pub fn udp_recvmmsg() {
-        use nix::sys::uio::IoVec;
-        use nix::sys::socket::{MsgFlags, recvmmsg};
+        use nix::sys::socket::{recvmmsg, MsgFlags};
+        use std::io::IoSliceMut;
 
         const NUM_MESSAGES_SENT: usize = 2;
-        const DATA: [u8; 2] = [1,2];
+        const DATA: [u8; 2] = [1, 2];
 
-        let std_sa = SocketAddr::from_str("127.0.0.1:6798").unwrap();
-        let inet_addr = InetAddr::from_std(&std_sa);
-        let sock_addr = SockAddr::new_inet(inet_addr);
+        let inet_addr = SocketAddrV4::from_str("127.0.0.1:6798").unwrap();
+        let sock_addr = SockaddrIn::from(inet_addr);
 
-        let rsock = socket(AddressFamily::Inet,
+        let rsock = socket(
+            AddressFamily::Inet,
             SockType::Datagram,
             SockFlag::empty(),
-            None
-        ).unwrap();
+            None,
+        )
+        .unwrap();
         bind(rsock, &sock_addr).unwrap();
         let ssock = socket(
             AddressFamily::Inet,
             SockType::Datagram,
             SockFlag::empty(),
             None,
-        ).expect("send socket failed");
+        )
+        .expect("send socket failed");
 
         let send_thread = thread::spawn(move || {
             for _ in 0..NUM_MESSAGES_SENT {
-                sendto(ssock, &DATA[..], &sock_addr, MsgFlags::empty()).unwrap();
+                sendto(ssock, &DATA[..], &sock_addr, MsgFlags::empty())
+                    .unwrap();
             }
         });
 
@@ -468,22 +593,23 @@
 
         // Buffers to receive exactly `NUM_MESSAGES_SENT` messages
         let mut receive_buffers = [[0u8; 32]; NUM_MESSAGES_SENT];
-        let iovs: Vec<_> = receive_buffers.iter_mut().map(|buf| {
-            [IoVec::from_mut_slice(&mut buf[..])]
-        }).collect();
+        msgs.extend(
+            receive_buffers
+                .iter_mut()
+                .map(|buf| [IoSliceMut::new(&mut buf[..])]),
+        );
 
-        for iov in &iovs {
-            msgs.push_back(RecvMmsgData {
-                iov,
-                cmsg_buffer: None,
-            })
-        };
+        let mut data =
+            MultiHeaders::<SockaddrIn>::preallocate(msgs.len(), None);
 
-        let res = recvmmsg(rsock, &mut msgs, MsgFlags::empty(), None).expect("recvmmsg");
+        let res: Vec<RecvMsg<SockaddrIn>> =
+            recvmmsg(rsock, &mut data, msgs.iter(), MsgFlags::empty(), None)
+                .expect("recvmmsg")
+                .collect();
         assert_eq!(res.len(), DATA.len());
 
         for RecvMsg { address, bytes, .. } in res.into_iter() {
-            assert_eq!(AddressFamily::Inet, address.unwrap().family());
+            assert_eq!(AddressFamily::Inet, address.unwrap().family().unwrap());
             assert_eq!(DATA.len(), bytes);
         }
 
@@ -502,32 +628,35 @@
     ))]
     #[test]
     pub fn udp_recvmmsg_dontwait_short_read() {
-        use nix::sys::uio::IoVec;
-        use nix::sys::socket::{MsgFlags, recvmmsg};
+        use nix::sys::socket::{recvmmsg, MsgFlags};
+        use std::io::IoSliceMut;
 
         const NUM_MESSAGES_SENT: usize = 2;
-        const DATA: [u8; 4] = [1,2,3,4];
+        const DATA: [u8; 4] = [1, 2, 3, 4];
 
-        let std_sa = SocketAddr::from_str("127.0.0.1:6799").unwrap();
-        let inet_addr = InetAddr::from_std(&std_sa);
-        let sock_addr = SockAddr::new_inet(inet_addr);
+        let inet_addr = SocketAddrV4::from_str("127.0.0.1:6799").unwrap();
+        let sock_addr = SockaddrIn::from(inet_addr);
 
-        let rsock = socket(AddressFamily::Inet,
+        let rsock = socket(
+            AddressFamily::Inet,
             SockType::Datagram,
             SockFlag::empty(),
-            None
-        ).unwrap();
+            None,
+        )
+        .unwrap();
         bind(rsock, &sock_addr).unwrap();
         let ssock = socket(
             AddressFamily::Inet,
             SockType::Datagram,
             SockFlag::empty(),
             None,
-        ).expect("send socket failed");
+        )
+        .expect("send socket failed");
 
         let send_thread = thread::spawn(move || {
             for _ in 0..NUM_MESSAGES_SENT {
-                sendto(ssock, &DATA[..], &sock_addr, MsgFlags::empty()).unwrap();
+                sendto(ssock, &DATA[..], &sock_addr, MsgFlags::empty())
+                    .unwrap();
             }
         });
         // Ensure we've sent all the messages before continuing so `recvmmsg`
@@ -540,22 +669,30 @@
         // will return when there are fewer than requested messages in the
         // kernel buffers when using `MSG_DONTWAIT`.
         let mut receive_buffers = [[0u8; 32]; NUM_MESSAGES_SENT + 2];
-        let iovs: Vec<_> = receive_buffers.iter_mut().map(|buf| {
-            [IoVec::from_mut_slice(&mut buf[..])]
-        }).collect();
+        msgs.extend(
+            receive_buffers
+                .iter_mut()
+                .map(|buf| [IoSliceMut::new(&mut buf[..])]),
+        );
 
-        for iov in &iovs {
-            msgs.push_back(RecvMmsgData {
-                iov,
-                cmsg_buffer: None,
-            })
-        };
+        let mut data = MultiHeaders::<SockaddrIn>::preallocate(
+            NUM_MESSAGES_SENT + 2,
+            None,
+        );
 
-        let res = recvmmsg(rsock, &mut msgs, MsgFlags::MSG_DONTWAIT, None).expect("recvmmsg");
+        let res: Vec<RecvMsg<SockaddrIn>> = recvmmsg(
+            rsock,
+            &mut data,
+            msgs.iter(),
+            MsgFlags::MSG_DONTWAIT,
+            None,
+        )
+        .expect("recvmmsg")
+        .collect();
         assert_eq!(res.len(), NUM_MESSAGES_SENT);
 
         for RecvMsg { address, bytes, .. } in res.into_iter() {
-            assert_eq!(AddressFamily::Inet, address.unwrap().family());
+            assert_eq!(AddressFamily::Inet, address.unwrap().family().unwrap());
             assert_eq!(DATA.len(), bytes);
         }
 
@@ -563,19 +700,66 @@
             assert_eq!(&buf[..DATA.len()], DATA);
         }
     }
+
+    #[test]
+    pub fn udp_inet6() {
+        let addr = std::net::Ipv6Addr::from_str("::1").unwrap();
+        let rport = 6789;
+        let rstd_sa = SocketAddrV6::new(addr, rport, 0, 0);
+        let raddr = SockaddrIn6::from(rstd_sa);
+        let sport = 6790;
+        let sstd_sa = SocketAddrV6::new(addr, sport, 0, 0);
+        let saddr = SockaddrIn6::from(sstd_sa);
+        let rsock = socket(
+            AddressFamily::Inet6,
+            SockType::Datagram,
+            SockFlag::empty(),
+            None,
+        )
+        .expect("receive socket failed");
+        match bind(rsock, &raddr) {
+            Err(Errno::EADDRNOTAVAIL) => {
+                println!("IPv6 not available, skipping test.");
+                return;
+            }
+            Err(e) => panic!("bind: {}", e),
+            Ok(()) => (),
+        }
+        let ssock = socket(
+            AddressFamily::Inet6,
+            SockType::Datagram,
+            SockFlag::empty(),
+            None,
+        )
+        .expect("send socket failed");
+        bind(ssock, &saddr).unwrap();
+        let from = sendrecv(
+            rsock,
+            ssock,
+            move |s, m, flags| sendto(s, m, &raddr, flags),
+            |_, _| {},
+        );
+        assert_eq!(AddressFamily::Inet6, from.unwrap().family().unwrap());
+        let osent_addr = from.unwrap();
+        let sent_addr = osent_addr.as_sockaddr_in6().unwrap();
+        assert_eq!(sent_addr.ip(), addr);
+        assert_eq!(sent_addr.port(), sport);
+    }
 }
 
 // Test error handling of our recvmsg wrapper
 #[test]
 pub fn test_recvmsg_ebadf() {
     use nix::errno::Errno;
-    use nix::sys::socket::{MsgFlags, recvmsg};
-    use nix::sys::uio::IoVec;
+    use nix::sys::socket::{recvmsg, MsgFlags};
+    use std::io::IoSliceMut;
 
     let mut buf = [0u8; 5];
-    let iov = [IoVec::from_mut_slice(&mut buf[..])];
-    let fd = -1;    // Bad file descriptor
-    let r = recvmsg(fd, &iov, None, MsgFlags::empty());
+    let mut iov = [IoSliceMut::new(&mut buf[..])];
+
+    let fd = -1; // Bad file descriptor
+    let r = recvmsg::<()>(fd, &mut iov, None, MsgFlags::empty());
+
     assert_eq!(r.err().unwrap(), Errno::EBADF);
 }
 
@@ -584,31 +768,47 @@
 #[cfg_attr(qemu, ignore)]
 #[test]
 pub fn test_scm_rights() {
-    use nix::sys::uio::IoVec;
-    use nix::unistd::{pipe, read, write, close};
-    use nix::sys::socket::{socketpair, sendmsg, recvmsg,
-                           AddressFamily, SockType, SockFlag,
-                           ControlMessage, ControlMessageOwned, MsgFlags};
+    use nix::sys::socket::{
+        recvmsg, sendmsg, socketpair, AddressFamily, ControlMessage,
+        ControlMessageOwned, MsgFlags, SockFlag, SockType,
+    };
+    use nix::unistd::{close, pipe, read, write};
+    use std::io::{IoSlice, IoSliceMut};
 
-    let (fd1, fd2) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty())
-                     .unwrap();
+    let (fd1, fd2) = socketpair(
+        AddressFamily::Unix,
+        SockType::Stream,
+        None,
+        SockFlag::empty(),
+    )
+    .unwrap();
     let (r, w) = pipe().unwrap();
     let mut received_r: Option<RawFd> = None;
 
     {
-        let iov = [IoVec::from_slice(b"hello")];
+        let iov = [IoSlice::new(b"hello")];
         let fds = [r];
         let cmsg = ControlMessage::ScmRights(&fds);
-        assert_eq!(sendmsg(fd1, &iov, &[cmsg], MsgFlags::empty(), None).unwrap(), 5);
+        assert_eq!(
+            sendmsg::<()>(fd1, &iov, &[cmsg], MsgFlags::empty(), None).unwrap(),
+            5
+        );
         close(r).unwrap();
         close(fd1).unwrap();
     }
 
     {
         let mut buf = [0u8; 5];
-        let iov = [IoVec::from_mut_slice(&mut buf[..])];
+
+        let mut iov = [IoSliceMut::new(&mut buf[..])];
         let mut cmsgspace = cmsg_space!([RawFd; 1]);
-        let msg = recvmsg(fd2, &iov, Some(&mut cmsgspace), MsgFlags::empty()).unwrap();
+        let msg = recvmsg::<()>(
+            fd2,
+            &mut iov,
+            Some(&mut cmsgspace),
+            MsgFlags::empty(),
+        )
+        .unwrap();
 
         for cmsg in msg.cmsgs() {
             if let ControlMessageOwned::ScmRights(fd) = cmsg {
@@ -620,7 +820,9 @@
             }
         }
         assert_eq!(msg.bytes, 5);
-        assert!(!msg.flags.intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
+        assert!(!msg
+            .flags
+            .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
         close(fd2).unwrap();
     }
 
@@ -635,16 +837,17 @@
 }
 
 // Disable the test on emulated platforms due to not enabled support of AF_ALG in QEMU from rust cross
-#[cfg(any(target_os = "linux", target_os= "android"))]
+#[cfg(any(target_os = "linux", target_os = "android"))]
 #[cfg_attr(qemu, ignore)]
 #[test]
 pub fn test_af_alg_cipher() {
-    use nix::sys::uio::IoVec;
-    use nix::unistd::read;
-    use nix::sys::socket::{socket, sendmsg, bind, accept, setsockopt,
-                           AddressFamily, SockType, SockFlag, SockAddr,
-                           ControlMessage, MsgFlags};
     use nix::sys::socket::sockopt::AlgSetKey;
+    use nix::sys::socket::{
+        accept, bind, sendmsg, setsockopt, socket, AddressFamily, AlgAddr,
+        ControlMessage, MsgFlags, SockFlag, SockType,
+    };
+    use nix::unistd::read;
+    use std::io::IoSlice;
 
     skip_if_cirrus!("Fails for an unknown reason Cirrus CI.  Bug #1352");
     // Travis's seccomp profile blocks AF_ALG
@@ -662,37 +865,46 @@
     let payload_len = 256;
     let payload = vec![2u8; payload_len];
 
-    let sock = socket(AddressFamily::Alg, SockType::SeqPacket, SockFlag::empty(), None)
-        .expect("socket failed");
+    let sock = socket(
+        AddressFamily::Alg,
+        SockType::SeqPacket,
+        SockFlag::empty(),
+        None,
+    )
+    .expect("socket failed");
 
-    let sockaddr = SockAddr::new_alg(alg_type, alg_name);
+    let sockaddr = AlgAddr::new(alg_type, alg_name);
     bind(sock, &sockaddr).expect("bind failed");
 
-    if let SockAddr::Alg(alg) = sockaddr {
-        assert_eq!(alg.alg_name().to_string_lossy(), alg_name);
-        assert_eq!(alg.alg_type().to_string_lossy(), alg_type);
-    } else {
-        panic!("unexpected SockAddr");
-    }
+    assert_eq!(sockaddr.alg_name().to_string_lossy(), alg_name);
+    assert_eq!(sockaddr.alg_type().to_string_lossy(), alg_type);
 
     setsockopt(sock, AlgSetKey::default(), &key).expect("setsockopt");
     let session_socket = accept(sock).expect("accept failed");
 
-    let msgs = [ControlMessage::AlgSetOp(&libc::ALG_OP_ENCRYPT), ControlMessage::AlgSetIv(iv.as_slice())];
-    let iov = IoVec::from_slice(&payload);
-    sendmsg(session_socket, &[iov], &msgs, MsgFlags::empty(), None).expect("sendmsg encrypt");
+    let msgs = [
+        ControlMessage::AlgSetOp(&libc::ALG_OP_ENCRYPT),
+        ControlMessage::AlgSetIv(iv.as_slice()),
+    ];
+    let iov = IoSlice::new(&payload);
+    sendmsg::<()>(session_socket, &[iov], &msgs, MsgFlags::empty(), None)
+        .expect("sendmsg encrypt");
 
     // allocate buffer for encrypted data
     let mut encrypted = vec![0u8; payload_len];
     let num_bytes = read(session_socket, &mut encrypted).expect("read encrypt");
     assert_eq!(num_bytes, payload_len);
 
-    let iov = IoVec::from_slice(&encrypted);
+    let iov = IoSlice::new(&encrypted);
 
     let iv = vec![1u8; iv_len];
 
-    let msgs = [ControlMessage::AlgSetOp(&libc::ALG_OP_DECRYPT), ControlMessage::AlgSetIv(iv.as_slice())];
-    sendmsg(session_socket, &[iov], &msgs, MsgFlags::empty(), None).expect("sendmsg decrypt");
+    let msgs = [
+        ControlMessage::AlgSetOp(&libc::ALG_OP_DECRYPT),
+        ControlMessage::AlgSetIv(iv.as_slice()),
+    ];
+    sendmsg::<()>(session_socket, &[iov], &msgs, MsgFlags::empty(), None)
+        .expect("sendmsg decrypt");
 
     // allocate buffer for decrypted data
     let mut decrypted = vec![0u8; payload_len];
@@ -704,18 +916,19 @@
 
 // Disable the test on emulated platforms due to not enabled support of AF_ALG
 // in QEMU from rust cross
-#[cfg(any(target_os = "linux", target_os= "android"))]
+#[cfg(any(target_os = "linux", target_os = "android"))]
 #[cfg_attr(qemu, ignore)]
 #[test]
 pub fn test_af_alg_aead() {
     use libc::{ALG_OP_DECRYPT, ALG_OP_ENCRYPT};
     use nix::fcntl::{fcntl, FcntlArg, OFlag};
-    use nix::sys::uio::IoVec;
-    use nix::unistd::{read, close};
-    use nix::sys::socket::{socket, sendmsg, bind, accept, setsockopt,
-                           AddressFamily, SockType, SockFlag, SockAddr,
-                           ControlMessage, MsgFlags};
-    use nix::sys::socket::sockopt::{AlgSetKey, AlgSetAeadAuthSize};
+    use nix::sys::socket::sockopt::{AlgSetAeadAuthSize, AlgSetKey};
+    use nix::sys::socket::{
+        accept, bind, sendmsg, setsockopt, socket, AddressFamily, AlgAddr,
+        ControlMessage, MsgFlags, SockFlag, SockType,
+    };
+    use nix::unistd::{close, read};
+    use std::io::IoSlice;
 
     skip_if_cirrus!("Fails for an unknown reason Cirrus CI.  Bug #1352");
     // Travis's seccomp profile blocks AF_ALG
@@ -734,7 +947,8 @@
     let iv = vec![1u8; iv_len];
     // 256-bytes plain payload
     let payload_len = 256;
-    let mut payload = vec![2u8; payload_len + (assoc_size as usize) + auth_size];
+    let mut payload =
+        vec![2u8; payload_len + (assoc_size as usize) + auth_size];
 
     for i in 0..assoc_size {
         payload[i as usize] = 10;
@@ -746,25 +960,35 @@
         payload[len - 1 - i] = 0;
     }
 
-    let sock = socket(AddressFamily::Alg, SockType::SeqPacket, SockFlag::empty(), None)
-        .expect("socket failed");
+    let sock = socket(
+        AddressFamily::Alg,
+        SockType::SeqPacket,
+        SockFlag::empty(),
+        None,
+    )
+    .expect("socket failed");
 
-    let sockaddr = SockAddr::new_alg(alg_type, alg_name);
+    let sockaddr = AlgAddr::new(alg_type, alg_name);
     bind(sock, &sockaddr).expect("bind failed");
 
-    setsockopt(sock, AlgSetAeadAuthSize, &auth_size).expect("setsockopt AlgSetAeadAuthSize");
+    setsockopt(sock, AlgSetAeadAuthSize, &auth_size)
+        .expect("setsockopt AlgSetAeadAuthSize");
     setsockopt(sock, AlgSetKey::default(), &key).expect("setsockopt AlgSetKey");
     let session_socket = accept(sock).expect("accept failed");
 
     let msgs = [
         ControlMessage::AlgSetOp(&ALG_OP_ENCRYPT),
         ControlMessage::AlgSetIv(iv.as_slice()),
-        ControlMessage::AlgSetAeadAssoclen(&assoc_size)];
-    let iov = IoVec::from_slice(&payload);
-    sendmsg(session_socket, &[iov], &msgs, MsgFlags::empty(), None).expect("sendmsg encrypt");
+        ControlMessage::AlgSetAeadAssoclen(&assoc_size),
+    ];
+
+    let iov = IoSlice::new(&payload);
+    sendmsg::<()>(session_socket, &[iov], &msgs, MsgFlags::empty(), None)
+        .expect("sendmsg encrypt");
 
     // allocate buffer for encrypted data
-    let mut encrypted = vec![0u8; (assoc_size as usize) + payload_len + auth_size];
+    let mut encrypted =
+        vec![0u8; (assoc_size as usize) + payload_len + auth_size];
     let num_bytes = read(session_socket, &mut encrypted).expect("read encrypt");
     assert_eq!(num_bytes, payload_len + auth_size + (assoc_size as usize));
     close(session_socket).expect("close");
@@ -773,7 +997,7 @@
         encrypted[i as usize] = 10;
     }
 
-    let iov = IoVec::from_slice(&encrypted);
+    let iov = IoSlice::new(&encrypted);
 
     let iv = vec![1u8; iv_len];
 
@@ -784,19 +1008,25 @@
         ControlMessage::AlgSetIv(iv.as_slice()),
         ControlMessage::AlgSetAeadAssoclen(&assoc_size),
     ];
-    sendmsg(session_socket, &[iov], &msgs, MsgFlags::empty(), None).expect("sendmsg decrypt");
+    sendmsg::<()>(session_socket, &[iov], &msgs, MsgFlags::empty(), None)
+        .expect("sendmsg decrypt");
 
     // allocate buffer for decrypted data
-    let mut decrypted = vec![0u8; payload_len + (assoc_size as usize) + auth_size];
+    let mut decrypted =
+        vec![0u8; payload_len + (assoc_size as usize) + auth_size];
     // Starting with kernel 4.9, the interface changed slightly such that the
     // authentication tag memory is only needed in the output buffer for encryption
     // and in the input buffer for decryption.
     // Do not block on read, as we may have fewer bytes than buffer size
-    fcntl(session_socket,FcntlArg::F_SETFL(OFlag::O_NONBLOCK)).expect("fcntl non_blocking");
+    fcntl(session_socket, FcntlArg::F_SETFL(OFlag::O_NONBLOCK))
+        .expect("fcntl non_blocking");
     let num_bytes = read(session_socket, &mut decrypted).expect("read decrypt");
 
     assert!(num_bytes >= payload_len + (assoc_size as usize));
-    assert_eq!(decrypted[(assoc_size as usize)..(payload_len + (assoc_size as usize))], payload[(assoc_size as usize)..payload_len + (assoc_size as usize)]);
+    assert_eq!(
+        decrypted[(assoc_size as usize)..(payload_len + (assoc_size as usize))],
+        payload[(assoc_size as usize)..payload_len + (assoc_size as usize)]
+    );
 }
 
 // Verify `ControlMessage::Ipv4PacketInfo` for `sendmsg`.
@@ -806,56 +1036,50 @@
 // This would be a more interesting test if we could assume that the test host
 // has more than one IP address (since we could select a different address to
 // test from).
-#[cfg(any(target_os = "linux",
-        target_os = "macos",
-        target_os = "netbsd"))]
+#[cfg(any(target_os = "linux", target_os = "macos", target_os = "netbsd"))]
 #[test]
 pub fn test_sendmsg_ipv4packetinfo() {
     use cfg_if::cfg_if;
-    use nix::sys::uio::IoVec;
-    use nix::sys::socket::{socket, sendmsg, bind,
-                           AddressFamily, SockType, SockFlag, SockAddr,
-                           ControlMessage, MsgFlags};
+    use nix::sys::socket::{
+        bind, sendmsg, socket, AddressFamily, ControlMessage, MsgFlags,
+        SockFlag, SockType, SockaddrIn,
+    };
+    use std::io::IoSlice;
 
-    let sock = socket(AddressFamily::Inet,
-                      SockType::Datagram,
-                      SockFlag::empty(),
-                      None)
-        .expect("socket failed");
+    let sock = socket(
+        AddressFamily::Inet,
+        SockType::Datagram,
+        SockFlag::empty(),
+        None,
+    )
+    .expect("socket failed");
 
-    let std_sa = SocketAddr::from_str("127.0.0.1:4000").unwrap();
-    let inet_addr = InetAddr::from_std(&std_sa);
-    let sock_addr = SockAddr::new_inet(inet_addr);
+    let sock_addr = SockaddrIn::new(127, 0, 0, 1, 4000);
 
     bind(sock, &sock_addr).expect("bind failed");
 
     let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
-    let iov = [IoVec::from_slice(&slice)];
+    let iov = [IoSlice::new(&slice)];
 
-    if let InetAddr::V4(sin) = inet_addr {
-        cfg_if! {
-            if #[cfg(target_os = "netbsd")] {
-                let _dontcare = sin;
-                let pi = libc::in_pktinfo {
-                    ipi_ifindex: 0, /* Unspecified interface */
-                    ipi_addr: libc::in_addr { s_addr: 0 },
-                };
-            } else {
-                let pi = libc::in_pktinfo {
-                    ipi_ifindex: 0, /* Unspecified interface */
-                    ipi_addr: libc::in_addr { s_addr: 0 },
-                    ipi_spec_dst: sin.sin_addr,
-                };
-            }
+    cfg_if! {
+        if #[cfg(target_os = "netbsd")] {
+            let pi = libc::in_pktinfo {
+                ipi_ifindex: 0, /* Unspecified interface */
+                ipi_addr: libc::in_addr { s_addr: 0 },
+            };
+        } else {
+            let pi = libc::in_pktinfo {
+                ipi_ifindex: 0, /* Unspecified interface */
+                ipi_addr: libc::in_addr { s_addr: 0 },
+                ipi_spec_dst: sock_addr.as_ref().sin_addr,
+            };
         }
-
-        let cmsg = [ControlMessage::Ipv4PacketInfo(&pi)];
-
-        sendmsg(sock, &iov, &cmsg, MsgFlags::empty(), Some(&sock_addr))
-            .expect("sendmsg");
-    } else {
-        panic!("No IPv4 addresses available for testing?");
     }
+
+    let cmsg = [ControlMessage::Ipv4PacketInfo(&pi)];
+
+    sendmsg(sock, &iov, &cmsg, MsgFlags::empty(), Some(&sock_addr))
+        .expect("sendmsg");
 }
 
 // Verify `ControlMessage::Ipv6PacketInfo` for `sendmsg`.
@@ -866,27 +1090,31 @@
 // This would be a more interesting test if we could assume that the test host
 // has more than one IP address (since we could select a different address to
 // test from).
-#[cfg(any(target_os = "linux",
-        target_os = "macos",
-        target_os = "netbsd",
-        target_os = "freebsd"))]
+#[cfg(any(
+    target_os = "linux",
+    target_os = "macos",
+    target_os = "netbsd",
+    target_os = "freebsd"
+))]
 #[test]
 pub fn test_sendmsg_ipv6packetinfo() {
     use nix::errno::Errno;
-    use nix::sys::uio::IoVec;
-    use nix::sys::socket::{socket, sendmsg, bind,
-                           AddressFamily, SockType, SockFlag, SockAddr,
-                           ControlMessage, MsgFlags};
+    use nix::sys::socket::{
+        bind, sendmsg, socket, AddressFamily, ControlMessage, MsgFlags,
+        SockFlag, SockType, SockaddrIn6,
+    };
+    use std::io::IoSlice;
 
-    let sock = socket(AddressFamily::Inet6,
-                      SockType::Datagram,
-                      SockFlag::empty(),
-                      None)
-        .expect("socket failed");
+    let sock = socket(
+        AddressFamily::Inet6,
+        SockType::Datagram,
+        SockFlag::empty(),
+        None,
+    )
+    .expect("socket failed");
 
-    let std_sa = SocketAddr::from_str("[::1]:6000").unwrap();
-    let inet_addr = InetAddr::from_std(&std_sa);
-    let sock_addr = SockAddr::new_inet(inet_addr);
+    let std_sa = SocketAddrV6::from_str("[::1]:6000").unwrap();
+    let sock_addr: SockaddrIn6 = SockaddrIn6::from(std_sa);
 
     if let Err(Errno::EADDRNOTAVAIL) = bind(sock, &sock_addr) {
         println!("IPv6 not available, skipping test.");
@@ -894,21 +1122,75 @@
     }
 
     let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
-    let iov = [IoVec::from_slice(&slice)];
+    let iov = [IoSlice::new(&slice)];
 
-    if let InetAddr::V6(sin) = inet_addr {
-        let pi = libc::in6_pktinfo {
-            ipi6_ifindex: 0, /* Unspecified interface */
-            ipi6_addr: sin.sin6_addr,
-        };
+    let pi = libc::in6_pktinfo {
+        ipi6_ifindex: 0, /* Unspecified interface */
+        ipi6_addr: sock_addr.as_ref().sin6_addr,
+    };
 
-        let cmsg = [ControlMessage::Ipv6PacketInfo(&pi)];
+    let cmsg = [ControlMessage::Ipv6PacketInfo(&pi)];
 
-        sendmsg(sock, &iov, &cmsg, MsgFlags::empty(), Some(&sock_addr))
-            .expect("sendmsg");
-    } else {
-        println!("No IPv6 addresses available for testing: skipping testing Ipv6PacketInfo");
-    }
+    sendmsg::<SockaddrIn6>(
+        sock,
+        &iov,
+        &cmsg,
+        MsgFlags::empty(),
+        Some(&sock_addr),
+    )
+    .expect("sendmsg");
+}
+
+// Verify that ControlMessage::Ipv4SendSrcAddr works for sendmsg. This
+// creates a UDP socket bound to all local interfaces (0.0.0.0). It then
+// sends message to itself at 127.0.0.1 while explicitly specifying
+// 127.0.0.1 as the source address through an Ipv4SendSrcAddr
+// (IP_SENDSRCADDR) control message.
+//
+// Note that binding to 0.0.0.0 is *required* on FreeBSD; sendmsg
+// returns EINVAL otherwise. (See FreeBSD's ip(4) man page.)
+#[cfg(any(
+    target_os = "netbsd",
+    target_os = "freebsd",
+    target_os = "openbsd",
+    target_os = "dragonfly",
+))]
+#[test]
+pub fn test_sendmsg_ipv4sendsrcaddr() {
+    use nix::sys::socket::{
+        bind, sendmsg, socket, AddressFamily, ControlMessage, MsgFlags,
+        SockFlag, SockType, SockaddrIn,
+    };
+    use std::io::IoSlice;
+
+    let sock = socket(
+        AddressFamily::Inet,
+        SockType::Datagram,
+        SockFlag::empty(),
+        None,
+    )
+    .expect("socket failed");
+
+    let unspec_sock_addr = SockaddrIn::new(0, 0, 0, 0, 0);
+    bind(sock, &unspec_sock_addr).expect("bind failed");
+    let bound_sock_addr: SockaddrIn = getsockname(sock).unwrap();
+    let localhost_sock_addr: SockaddrIn =
+        SockaddrIn::new(127, 0, 0, 1, bound_sock_addr.port());
+
+    let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
+    let iov = [IoSlice::new(&slice)];
+    let cmsg = [ControlMessage::Ipv4SendSrcAddr(
+        &localhost_sock_addr.as_ref().sin_addr,
+    )];
+
+    sendmsg(
+        sock,
+        &iov,
+        &cmsg,
+        MsgFlags::empty(),
+        Some(&localhost_sock_addr),
+    )
+    .expect("sendmsg");
 }
 
 /// Tests that passing multiple fds using a single `ControlMessage` works.
@@ -917,46 +1199,55 @@
 #[cfg_attr(qemu, ignore)]
 #[test]
 fn test_scm_rights_single_cmsg_multiple_fds() {
+    use nix::sys::socket::{
+        recvmsg, sendmsg, ControlMessage, ControlMessageOwned, MsgFlags,
+    };
+    use std::io::{IoSlice, IoSliceMut};
+    use std::os::unix::io::{AsRawFd, RawFd};
     use std::os::unix::net::UnixDatagram;
-    use std::os::unix::io::{RawFd, AsRawFd};
     use std::thread;
-    use nix::sys::socket::{ControlMessage, ControlMessageOwned, MsgFlags,
-        sendmsg, recvmsg};
-    use nix::sys::uio::IoVec;
 
     let (send, receive) = UnixDatagram::pair().unwrap();
     let thread = thread::spawn(move || {
         let mut buf = [0u8; 8];
-        let iovec = [IoVec::from_mut_slice(&mut buf)];
+        let mut iovec = [IoSliceMut::new(&mut buf)];
+
         let mut space = cmsg_space!([RawFd; 2]);
-        let msg = recvmsg(
+        let msg = recvmsg::<()>(
             receive.as_raw_fd(),
-            &iovec,
+            &mut iovec,
             Some(&mut space),
-            MsgFlags::empty()
-        ).unwrap();
-        assert!(!msg.flags.intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
+            MsgFlags::empty(),
+        )
+        .unwrap();
+        assert!(!msg
+            .flags
+            .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
 
         let mut cmsgs = msg.cmsgs();
         match cmsgs.next() {
             Some(ControlMessageOwned::ScmRights(fds)) => {
-                assert_eq!(fds.len(), 2,
-                           "unexpected fd count (expected 2 fds, got {})",
-                           fds.len());
-            },
+                assert_eq!(
+                    fds.len(),
+                    2,
+                    "unexpected fd count (expected 2 fds, got {})",
+                    fds.len()
+                );
+            }
             _ => panic!(),
         }
         assert!(cmsgs.next().is_none(), "unexpected control msg");
 
         assert_eq!(msg.bytes, 8);
-        assert_eq!(iovec[0].as_slice(), [1u8, 2, 3, 4, 5, 6, 7, 8]);
+        assert_eq!(*iovec[0], [1u8, 2, 3, 4, 5, 6, 7, 8]);
     });
 
     let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
-    let iov = [IoVec::from_slice(&slice)];
-    let fds = [libc::STDIN_FILENO, libc::STDOUT_FILENO];    // pass stdin and stdout
+    let iov = [IoSlice::new(&slice)];
+    let fds = [libc::STDIN_FILENO, libc::STDOUT_FILENO]; // pass stdin and stdout
     let cmsg = [ControlMessage::ScmRights(&fds)];
-    sendmsg(send.as_raw_fd(), &iov, &cmsg, MsgFlags::empty(), None).unwrap();
+    sendmsg::<()>(send.as_raw_fd(), &iov, &cmsg, MsgFlags::empty(), None)
+        .unwrap();
     thread.join().unwrap();
 }
 
@@ -966,30 +1257,49 @@
 // raw `sendmsg`.
 #[test]
 pub fn test_sendmsg_empty_cmsgs() {
-    use nix::sys::uio::IoVec;
+    use nix::sys::socket::{
+        recvmsg, sendmsg, socketpair, AddressFamily, MsgFlags, SockFlag,
+        SockType,
+    };
     use nix::unistd::close;
-    use nix::sys::socket::{socketpair, sendmsg, recvmsg,
-                           AddressFamily, SockType, SockFlag, MsgFlags};
+    use std::io::{IoSlice, IoSliceMut};
 
-    let (fd1, fd2) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty())
-                     .unwrap();
+    let (fd1, fd2) = socketpair(
+        AddressFamily::Unix,
+        SockType::Stream,
+        None,
+        SockFlag::empty(),
+    )
+    .unwrap();
 
     {
-        let iov = [IoVec::from_slice(b"hello")];
-        assert_eq!(sendmsg(fd1, &iov, &[], MsgFlags::empty(), None).unwrap(), 5);
+        let iov = [IoSlice::new(b"hello")];
+        assert_eq!(
+            sendmsg::<()>(fd1, &iov, &[], MsgFlags::empty(), None).unwrap(),
+            5
+        );
         close(fd1).unwrap();
     }
 
     {
         let mut buf = [0u8; 5];
-        let iov = [IoVec::from_mut_slice(&mut buf[..])];
+        let mut iov = [IoSliceMut::new(&mut buf[..])];
+
         let mut cmsgspace = cmsg_space!([RawFd; 1]);
-        let msg = recvmsg(fd2, &iov, Some(&mut cmsgspace), MsgFlags::empty()).unwrap();
+        let msg = recvmsg::<()>(
+            fd2,
+            &mut iov,
+            Some(&mut cmsgspace),
+            MsgFlags::empty(),
+        )
+        .unwrap();
 
         for _ in msg.cmsgs() {
             panic!("unexpected cmsg");
         }
-        assert!(!msg.flags.intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
+        assert!(!msg
+            .flags
+            .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
         assert_eq!(msg.bytes, 5);
         close(fd2).unwrap();
     }
@@ -1003,37 +1313,53 @@
 ))]
 #[test]
 fn test_scm_credentials() {
-    use nix::sys::uio::IoVec;
-    use nix::unistd::{close, getpid, getuid, getgid};
-    use nix::sys::socket::{socketpair, sendmsg, recvmsg,
-                           AddressFamily, SockType, SockFlag,
-                           ControlMessage, ControlMessageOwned, MsgFlags,
-                           UnixCredentials};
+    use nix::sys::socket::{
+        recvmsg, sendmsg, socketpair, AddressFamily, ControlMessage,
+        ControlMessageOwned, MsgFlags, SockFlag, SockType, UnixCredentials,
+    };
     #[cfg(any(target_os = "android", target_os = "linux"))]
     use nix::sys::socket::{setsockopt, sockopt::PassCred};
+    use nix::unistd::{close, getgid, getpid, getuid};
+    use std::io::{IoSlice, IoSliceMut};
 
-    let (send, recv) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty())
-        .unwrap();
+    let (send, recv) = socketpair(
+        AddressFamily::Unix,
+        SockType::Stream,
+        None,
+        SockFlag::empty(),
+    )
+    .unwrap();
     #[cfg(any(target_os = "android", target_os = "linux"))]
     setsockopt(recv, PassCred, &true).unwrap();
 
     {
-        let iov = [IoVec::from_slice(b"hello")];
+        let iov = [IoSlice::new(b"hello")];
         #[cfg(any(target_os = "android", target_os = "linux"))]
         let cred = UnixCredentials::new();
         #[cfg(any(target_os = "android", target_os = "linux"))]
         let cmsg = ControlMessage::ScmCredentials(&cred);
         #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
         let cmsg = ControlMessage::ScmCreds;
-        assert_eq!(sendmsg(send, &iov, &[cmsg], MsgFlags::empty(), None).unwrap(), 5);
+        assert_eq!(
+            sendmsg::<()>(send, &iov, &[cmsg], MsgFlags::empty(), None)
+                .unwrap(),
+            5
+        );
         close(send).unwrap();
     }
 
     {
         let mut buf = [0u8; 5];
-        let iov = [IoVec::from_mut_slice(&mut buf[..])];
+        let mut iov = [IoSliceMut::new(&mut buf[..])];
+
         let mut cmsgspace = cmsg_space!(UnixCredentials);
-        let msg = recvmsg(recv, &iov, Some(&mut cmsgspace), MsgFlags::empty()).unwrap();
+        let msg = recvmsg::<()>(
+            recv,
+            &mut iov,
+            Some(&mut cmsgspace),
+            MsgFlags::empty(),
+        )
+        .unwrap();
         let mut received_cred = None;
 
         for cmsg in msg.cmsgs() {
@@ -1052,7 +1378,9 @@
         }
         received_cred.expect("no creds received");
         assert_eq!(msg.bytes, 5);
-        assert!(!msg.flags.intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
+        assert!(!msg
+            .flags
+            .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
         close(recv).unwrap();
     }
 }
@@ -1084,41 +1412,53 @@
 #[cfg(any(target_os = "android", target_os = "linux"))]
 fn test_impl_scm_credentials_and_rights(mut space: Vec<u8>) {
     use libc::ucred;
-    use nix::sys::uio::IoVec;
-    use nix::unistd::{pipe, write, close, getpid, getuid, getgid};
-    use nix::sys::socket::{socketpair, sendmsg, recvmsg, setsockopt,
-                           SockType, SockFlag,
-                           ControlMessage, ControlMessageOwned, MsgFlags};
     use nix::sys::socket::sockopt::PassCred;
+    use nix::sys::socket::{
+        recvmsg, sendmsg, setsockopt, socketpair, ControlMessage,
+        ControlMessageOwned, MsgFlags, SockFlag, SockType,
+    };
+    use nix::unistd::{close, getgid, getpid, getuid, pipe, write};
+    use std::io::{IoSlice, IoSliceMut};
 
-    let (send, recv) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty())
-        .unwrap();
+    let (send, recv) = socketpair(
+        AddressFamily::Unix,
+        SockType::Stream,
+        None,
+        SockFlag::empty(),
+    )
+    .unwrap();
     setsockopt(recv, PassCred, &true).unwrap();
 
     let (r, w) = pipe().unwrap();
     let mut received_r: Option<RawFd> = None;
 
     {
-        let iov = [IoVec::from_slice(b"hello")];
+        let iov = [IoSlice::new(b"hello")];
         let cred = ucred {
             pid: getpid().as_raw(),
             uid: getuid().as_raw(),
             gid: getgid().as_raw(),
-        }.into();
+        }
+        .into();
         let fds = [r];
         let cmsgs = [
             ControlMessage::ScmCredentials(&cred),
             ControlMessage::ScmRights(&fds),
         ];
-        assert_eq!(sendmsg(send, &iov, &cmsgs, MsgFlags::empty(), None).unwrap(), 5);
+        assert_eq!(
+            sendmsg::<()>(send, &iov, &cmsgs, MsgFlags::empty(), None).unwrap(),
+            5
+        );
         close(r).unwrap();
         close(send).unwrap();
     }
 
     {
         let mut buf = [0u8; 5];
-        let iov = [IoVec::from_mut_slice(&mut buf[..])];
-        let msg = recvmsg(recv, &iov, Some(&mut space), MsgFlags::empty()).unwrap();
+        let mut iov = [IoSliceMut::new(&mut buf[..])];
+        let msg =
+            recvmsg::<()>(recv, &mut iov, Some(&mut space), MsgFlags::empty())
+                .unwrap();
         let mut received_cred = None;
 
         assert_eq!(msg.cmsgs().count(), 2, "expected 2 cmsgs");
@@ -1142,7 +1482,9 @@
         }
         received_cred.expect("no creds received");
         assert_eq!(msg.bytes, 5);
-        assert!(!msg.flags.intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
+        assert!(!msg
+            .flags
+            .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
         close(recv).unwrap();
     }
 
@@ -1158,23 +1500,33 @@
 
 // Test creating and using named unix domain sockets
 #[test]
-pub fn test_unixdomain() {
-    use nix::sys::socket::{SockType, SockFlag};
-    use nix::sys::socket::{bind, socket, connect, listen, accept, SockAddr};
-    use nix::unistd::{read, write, close};
+pub fn test_named_unixdomain() {
+    use nix::sys::socket::{accept, bind, connect, listen, socket, UnixAddr};
+    use nix::sys::socket::{SockFlag, SockType};
+    use nix::unistd::{close, read, write};
     use std::thread;
 
     let tempdir = tempfile::tempdir().unwrap();
     let sockname = tempdir.path().join("sock");
-    let s1 = socket(AddressFamily::Unix, SockType::Stream,
-                    SockFlag::empty(), None).expect("socket failed");
-    let sockaddr = SockAddr::new_unix(&sockname).unwrap();
+    let s1 = socket(
+        AddressFamily::Unix,
+        SockType::Stream,
+        SockFlag::empty(),
+        None,
+    )
+    .expect("socket failed");
+    let sockaddr = UnixAddr::new(&sockname).unwrap();
     bind(s1, &sockaddr).expect("bind failed");
     listen(s1, 10).expect("listen failed");
 
     let thr = thread::spawn(move || {
-        let s2 = socket(AddressFamily::Unix, SockType::Stream, SockFlag::empty(), None)
-                 .expect("socket failed");
+        let s2 = socket(
+            AddressFamily::Unix,
+            SockType::Stream,
+            SockFlag::empty(),
+            None,
+        )
+        .expect("socket failed");
         connect(s2, &sockaddr).expect("connect failed");
         write(s2, b"hello").expect("write failed");
         close(s2).unwrap();
@@ -1182,7 +1534,7 @@
 
     let s3 = accept(s1).expect("accept failed");
 
-    let mut buf = [0;5];
+    let mut buf = [0; 5];
     read(s3, &mut buf).unwrap();
     close(s3).unwrap();
     close(s1).unwrap();
@@ -1191,18 +1543,81 @@
     assert_eq!(&buf[..], b"hello");
 }
 
+// Test using unnamed unix domain addresses
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[test]
+pub fn test_unnamed_unixdomain() {
+    use nix::sys::socket::{getsockname, socketpair};
+    use nix::sys::socket::{SockFlag, SockType};
+    use nix::unistd::close;
+
+    let (fd_1, fd_2) = socketpair(
+        AddressFamily::Unix,
+        SockType::Stream,
+        None,
+        SockFlag::empty(),
+    )
+    .expect("socketpair failed");
+
+    let addr_1: UnixAddr = getsockname(fd_1).expect("getsockname failed");
+    assert!(addr_1.is_unnamed());
+
+    close(fd_1).unwrap();
+    close(fd_2).unwrap();
+}
+
+// Test creating and using unnamed unix domain addresses for autobinding sockets
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[test]
+pub fn test_unnamed_unixdomain_autobind() {
+    use nix::sys::socket::{bind, getsockname, socket};
+    use nix::sys::socket::{SockFlag, SockType};
+    use nix::unistd::close;
+
+    let fd = socket(
+        AddressFamily::Unix,
+        SockType::Stream,
+        SockFlag::empty(),
+        None,
+    )
+    .expect("socket failed");
+
+    // unix(7): "If a bind(2) call specifies addrlen as `sizeof(sa_family_t)`, or [...], then the
+    // socket is autobound to an abstract address"
+    bind(fd, &UnixAddr::new_unnamed()).expect("bind failed");
+
+    let addr: UnixAddr = getsockname(fd).expect("getsockname failed");
+    let addr = addr.as_abstract().unwrap();
+
+    // changed from 8 to 5 bytes in Linux 2.3.15, and rust's minimum supported Linux version is 3.2
+    // (as of 2022-11)
+    assert_eq!(addr.len(), 5);
+
+    close(fd).unwrap();
+}
+
 // Test creating and using named system control sockets
 #[cfg(any(target_os = "macos", target_os = "ios"))]
 #[test]
 pub fn test_syscontrol() {
     use nix::errno::Errno;
-    use nix::sys::socket::{socket, SockAddr, SockType, SockFlag, SockProtocol};
+    use nix::sys::socket::{
+        socket, SockFlag, SockProtocol, SockType, SysControlAddr,
+    };
 
-    let fd = socket(AddressFamily::System, SockType::Datagram,
-                    SockFlag::empty(), SockProtocol::KextControl)
-             .expect("socket failed");
-    let _sockaddr = SockAddr::new_sys_control(fd, "com.apple.net.utun_control", 0).expect("resolving sys_control name failed");
-    assert_eq!(SockAddr::new_sys_control(fd, "foo.bar.lol", 0).err(), Some(Errno::ENOENT));
+    let fd = socket(
+        AddressFamily::System,
+        SockType::Datagram,
+        SockFlag::empty(),
+        SockProtocol::KextControl,
+    )
+    .expect("socket failed");
+    SysControlAddr::from_name(fd, "com.apple.net.utun_control", 0)
+        .expect("resolving sys_control name failed");
+    assert_eq!(
+        SysControlAddr::from_name(fd, "foo.bar.lol", 0).err(),
+        Some(Errno::ENOENT)
+    );
 
     // requires root privileges
     // connect(fd, &sockaddr).expect("connect failed");
@@ -1217,42 +1632,30 @@
     target_os = "netbsd",
     target_os = "openbsd",
 ))]
-fn loopback_address(family: AddressFamily) -> Option<nix::ifaddrs::InterfaceAddress> {
-    use std::io;
-    use std::io::Write;
+fn loopback_address(
+    family: AddressFamily,
+) -> Option<nix::ifaddrs::InterfaceAddress> {
     use nix::ifaddrs::getifaddrs;
     use nix::net::if_::*;
+    use nix::sys::socket::SockaddrLike;
+    use std::io;
+    use std::io::Write;
 
-    let addrs = match getifaddrs() {
+    let mut addrs = match getifaddrs() {
         Ok(iter) => iter,
         Err(e) => {
             let stdioerr = io::stderr();
             let mut handle = stdioerr.lock();
             writeln!(handle, "getifaddrs: {:?}", e).unwrap();
             return None;
-        },
+        }
     };
     // return first address matching family
-    for ifaddr in addrs {
-        if ifaddr.flags.contains(InterfaceFlags::IFF_LOOPBACK) {
-            match ifaddr.address {
-                Some(SockAddr::Inet(InetAddr::V4(..))) => {
-                    match family {
-                        AddressFamily::Inet => return Some(ifaddr),
-                        _ => continue
-                    }
-                },
-                Some(SockAddr::Inet(InetAddr::V6(..))) => {
-                    match family {
-                        AddressFamily::Inet6 => return Some(ifaddr),
-                        _ => continue
-                    }
-                },
-                _ => continue,
-            }
-        }
-    }
-    None
+    addrs.find(|ifaddr| {
+        ifaddr.flags.contains(InterfaceFlags::IFF_LOOPBACK)
+            && ifaddr.address.as_ref().and_then(SockaddrLike::family)
+                == Some(family)
+    })
 }
 
 #[cfg(any(
@@ -1263,84 +1666,89 @@
     target_os = "netbsd",
 ))]
 // qemu doesn't seem to be emulating this correctly in these architectures
-#[cfg_attr(all(
-    qemu,
-    any(
-        target_arch = "mips",
-        target_arch = "mips64",
-        target_arch = "powerpc64",
-    )
-), ignore)]
+#[cfg_attr(
+    all(
+        qemu,
+        any(
+            target_arch = "mips",
+            target_arch = "mips64",
+            target_arch = "powerpc64",
+        )
+    ),
+    ignore
+)]
 #[test]
 pub fn test_recv_ipv4pktinfo() {
+    use nix::net::if_::*;
     use nix::sys::socket::sockopt::Ipv4PacketInfo;
-    use nix::sys::socket::{bind, SockFlag, SockType};
+    use nix::sys::socket::{bind, SockFlag, SockType, SockaddrIn};
     use nix::sys::socket::{getsockname, setsockopt, socket};
     use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags};
-    use nix::sys::uio::IoVec;
-    use nix::net::if_::*;
+    use std::io::{IoSlice, IoSliceMut};
 
     let lo_ifaddr = loopback_address(AddressFamily::Inet);
     let (lo_name, lo) = match lo_ifaddr {
-        Some(ifaddr) => (ifaddr.interface_name,
-                         ifaddr.address.expect("Expect IPv4 address on interface")),
+        Some(ifaddr) => (
+            ifaddr.interface_name,
+            ifaddr.address.expect("Expect IPv4 address on interface"),
+        ),
         None => return,
     };
     let receive = socket(
-            AddressFamily::Inet,
-            SockType::Datagram,
-            SockFlag::empty(),
-            None,
-        ).expect("receive socket failed");
+        AddressFamily::Inet,
+        SockType::Datagram,
+        SockFlag::empty(),
+        None,
+    )
+    .expect("receive socket failed");
     bind(receive, &lo).expect("bind failed");
-    let sa = getsockname(receive).expect("getsockname failed");
+    let sa: SockaddrIn = getsockname(receive).expect("getsockname failed");
     setsockopt(receive, Ipv4PacketInfo, &true).expect("setsockopt failed");
 
     {
         let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
-        let iov = [IoVec::from_slice(&slice)];
+        let iov = [IoSlice::new(&slice)];
 
         let send = socket(
             AddressFamily::Inet,
             SockType::Datagram,
             SockFlag::empty(),
             None,
-        ).expect("send socket failed");
-        sendmsg(send, &iov, &[], MsgFlags::empty(), Some(&sa)).expect("sendmsg failed");
+        )
+        .expect("send socket failed");
+        sendmsg(send, &iov, &[], MsgFlags::empty(), Some(&sa))
+            .expect("sendmsg failed");
     }
 
     {
         let mut buf = [0u8; 8];
-        let iovec = [IoVec::from_mut_slice(&mut buf)];
+        let mut iovec = [IoSliceMut::new(&mut buf)];
+
         let mut space = cmsg_space!(libc::in_pktinfo);
-        let msg = recvmsg(
+        let msg = recvmsg::<()>(
             receive,
-            &iovec,
+            &mut iovec,
             Some(&mut space),
             MsgFlags::empty(),
-        ).expect("recvmsg failed");
-        assert!(
-            !msg.flags
-                .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)
-        );
+        )
+        .expect("recvmsg failed");
+        assert!(!msg
+            .flags
+            .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
 
         let mut cmsgs = msg.cmsgs();
-        if let Some(ControlMessageOwned::Ipv4PacketInfo(pktinfo)) = cmsgs.next() {
+        if let Some(ControlMessageOwned::Ipv4PacketInfo(pktinfo)) = cmsgs.next()
+        {
             let i = if_nametoindex(lo_name.as_bytes()).expect("if_nametoindex");
             assert_eq!(
-                pktinfo.ipi_ifindex as libc::c_uint,
-                i,
+                pktinfo.ipi_ifindex as libc::c_uint, i,
                 "unexpected ifindex (expected {}, got {})",
-                i,
-                pktinfo.ipi_ifindex
+                i, pktinfo.ipi_ifindex
             );
         }
         assert!(cmsgs.next().is_none(), "unexpected additional control msg");
         assert_eq!(msg.bytes, 8);
-        assert_eq!(
-            iovec[0].as_slice(),
-            [1u8, 2, 3, 4, 5, 6, 7, 8]
-        );
+        assert_eq!(*iovec[0], [1u8, 2, 3, 4, 5, 6, 7, 8]);
     }
 }
 
@@ -1352,27 +1760,32 @@
     target_os = "openbsd",
 ))]
 // qemu doesn't seem to be emulating this correctly in these architectures
-#[cfg_attr(all(
-    qemu,
-    any(
-        target_arch = "mips",
-        target_arch = "mips64",
-        target_arch = "powerpc64",
-    )
-), ignore)]
+#[cfg_attr(
+    all(
+        qemu,
+        any(
+            target_arch = "mips",
+            target_arch = "mips64",
+            target_arch = "powerpc64",
+        )
+    ),
+    ignore
+)]
 #[test]
 pub fn test_recvif() {
     use nix::net::if_::*;
-    use nix::sys::socket::sockopt::{Ipv4RecvIf, Ipv4RecvDstAddr};
-    use nix::sys::socket::{bind, SockFlag, SockType};
-    use nix::sys::socket::{getsockname, setsockopt, socket, SockAddr};
+    use nix::sys::socket::sockopt::{Ipv4RecvDstAddr, Ipv4RecvIf};
+    use nix::sys::socket::{bind, SockFlag, SockType, SockaddrIn};
+    use nix::sys::socket::{getsockname, setsockopt, socket};
     use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags};
-    use nix::sys::uio::IoVec;
+    use std::io::{IoSlice, IoSliceMut};
 
     let lo_ifaddr = loopback_address(AddressFamily::Inet);
     let (lo_name, lo) = match lo_ifaddr {
-        Some(ifaddr) => (ifaddr.interface_name,
-                         ifaddr.address.expect("Expect IPv4 address on interface")),
+        Some(ifaddr) => (
+            ifaddr.interface_name,
+            ifaddr.address.expect("Expect IPv4 address on interface"),
+        ),
         None => return,
     };
     let receive = socket(
@@ -1380,39 +1793,44 @@
         SockType::Datagram,
         SockFlag::empty(),
         None,
-    ).expect("receive socket failed");
+    )
+    .expect("receive socket failed");
     bind(receive, &lo).expect("bind failed");
-    let sa = getsockname(receive).expect("getsockname failed");
-    setsockopt(receive, Ipv4RecvIf, &true).expect("setsockopt IP_RECVIF failed");
-    setsockopt(receive, Ipv4RecvDstAddr, &true).expect("setsockopt IP_RECVDSTADDR failed");
+    let sa: SockaddrIn = getsockname(receive).expect("getsockname failed");
+    setsockopt(receive, Ipv4RecvIf, &true)
+        .expect("setsockopt IP_RECVIF failed");
+    setsockopt(receive, Ipv4RecvDstAddr, &true)
+        .expect("setsockopt IP_RECVDSTADDR failed");
 
     {
         let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
-        let iov = [IoVec::from_slice(&slice)];
+        let iov = [IoSlice::new(&slice)];
 
         let send = socket(
             AddressFamily::Inet,
             SockType::Datagram,
             SockFlag::empty(),
             None,
-        ).expect("send socket failed");
-        sendmsg(send, &iov, &[], MsgFlags::empty(), Some(&sa)).expect("sendmsg failed");
+        )
+        .expect("send socket failed");
+        sendmsg(send, &iov, &[], MsgFlags::empty(), Some(&sa))
+            .expect("sendmsg failed");
     }
 
     {
         let mut buf = [0u8; 8];
-        let iovec = [IoVec::from_mut_slice(&mut buf)];
+        let mut iovec = [IoSliceMut::new(&mut buf)];
         let mut space = cmsg_space!(libc::sockaddr_dl, libc::in_addr);
-        let msg = recvmsg(
+        let msg = recvmsg::<()>(
             receive,
-            &iovec,
+            &mut iovec,
             Some(&mut space),
             MsgFlags::empty(),
-        ).expect("recvmsg failed");
-        assert!(
-            !msg.flags
-                .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)
-        );
+        )
+        .expect("recvmsg failed");
+        assert!(!msg
+            .flags
+            .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
         assert_eq!(msg.cmsgs().count(), 2, "expected 2 cmsgs");
 
         let mut rx_recvif = false;
@@ -1421,37 +1839,203 @@
             match cmsg {
                 ControlMessageOwned::Ipv4RecvIf(dl) => {
                     rx_recvif = true;
-                    let i = if_nametoindex(lo_name.as_bytes()).expect("if_nametoindex");
+                    let i = if_nametoindex(lo_name.as_bytes())
+                        .expect("if_nametoindex");
                     assert_eq!(
-                        dl.sdl_index as libc::c_uint,
-                        i,
+                        dl.sdl_index as libc::c_uint, i,
                         "unexpected ifindex (expected {}, got {})",
-                        i,
-                        dl.sdl_index
+                        i, dl.sdl_index
                     );
-                },
+                }
                 ControlMessageOwned::Ipv4RecvDstAddr(addr) => {
                     rx_recvdstaddr = true;
-                    if let SockAddr::Inet(InetAddr::V4(a)) = lo {
-                        assert_eq!(a.sin_addr.s_addr,
+                    if let Some(sin) = lo.as_sockaddr_in() {
+                        assert_eq!(sin.as_ref().sin_addr.s_addr,
                                    addr.s_addr,
                                    "unexpected destination address (expected {}, got {})",
-                                   a.sin_addr.s_addr,
+                                   sin.as_ref().sin_addr.s_addr,
                                    addr.s_addr);
                     } else {
                         panic!("unexpected Sockaddr");
                     }
-                },
+                }
                 _ => panic!("unexpected additional control msg"),
             }
         }
         assert!(rx_recvif);
         assert!(rx_recvdstaddr);
         assert_eq!(msg.bytes, 8);
-        assert_eq!(
-            iovec[0].as_slice(),
-            [1u8, 2, 3, 4, 5, 6, 7, 8]
-        );
+        assert_eq!(*iovec[0], [1u8, 2, 3, 4, 5, 6, 7, 8]);
+    }
+}
+
+#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+#[cfg_attr(qemu, ignore)]
+#[test]
+pub fn test_recvif_ipv4() {
+    use nix::sys::socket::sockopt::Ipv4OrigDstAddr;
+    use nix::sys::socket::{bind, SockFlag, SockType, SockaddrIn};
+    use nix::sys::socket::{getsockname, setsockopt, socket};
+    use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags};
+    use std::io::{IoSlice, IoSliceMut};
+
+    let lo_ifaddr = loopback_address(AddressFamily::Inet);
+    let (_lo_name, lo) = match lo_ifaddr {
+        Some(ifaddr) => (
+            ifaddr.interface_name,
+            ifaddr.address.expect("Expect IPv4 address on interface"),
+        ),
+        None => return,
+    };
+    let receive = socket(
+        AddressFamily::Inet,
+        SockType::Datagram,
+        SockFlag::empty(),
+        None,
+    )
+    .expect("receive socket failed");
+    bind(receive, &lo).expect("bind failed");
+    let sa: SockaddrIn = getsockname(receive).expect("getsockname failed");
+    setsockopt(receive, Ipv4OrigDstAddr, &true)
+        .expect("setsockopt IP_ORIGDSTADDR failed");
+
+    {
+        let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
+        let iov = [IoSlice::new(&slice)];
+
+        let send = socket(
+            AddressFamily::Inet,
+            SockType::Datagram,
+            SockFlag::empty(),
+            None,
+        )
+        .expect("send socket failed");
+        sendmsg(send, &iov, &[], MsgFlags::empty(), Some(&sa))
+            .expect("sendmsg failed");
+    }
+
+    {
+        let mut buf = [0u8; 8];
+        let mut iovec = [IoSliceMut::new(&mut buf)];
+        let mut space = cmsg_space!(libc::sockaddr_in);
+        let msg = recvmsg::<()>(
+            receive,
+            &mut iovec,
+            Some(&mut space),
+            MsgFlags::empty(),
+        )
+        .expect("recvmsg failed");
+        assert!(!msg
+            .flags
+            .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
+        assert_eq!(msg.cmsgs().count(), 1, "expected 1 cmsgs");
+
+        let mut rx_recvorigdstaddr = false;
+        for cmsg in msg.cmsgs() {
+            match cmsg {
+                ControlMessageOwned::Ipv4OrigDstAddr(addr) => {
+                    rx_recvorigdstaddr = true;
+                    if let Some(sin) = lo.as_sockaddr_in() {
+                        assert_eq!(sin.as_ref().sin_addr.s_addr,
+                                   addr.sin_addr.s_addr,
+                                   "unexpected destination address (expected {}, got {})",
+                                   sin.as_ref().sin_addr.s_addr,
+                                   addr.sin_addr.s_addr);
+                    } else {
+                        panic!("unexpected Sockaddr");
+                    }
+                }
+                _ => panic!("unexpected additional control msg"),
+            }
+        }
+        assert!(rx_recvorigdstaddr);
+        assert_eq!(msg.bytes, 8);
+        assert_eq!(*iovec[0], [1u8, 2, 3, 4, 5, 6, 7, 8]);
+    }
+}
+
+#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+#[cfg_attr(qemu, ignore)]
+#[test]
+pub fn test_recvif_ipv6() {
+    use nix::sys::socket::sockopt::Ipv6OrigDstAddr;
+    use nix::sys::socket::{bind, SockFlag, SockType, SockaddrIn6};
+    use nix::sys::socket::{getsockname, setsockopt, socket};
+    use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags};
+    use std::io::{IoSlice, IoSliceMut};
+
+    let lo_ifaddr = loopback_address(AddressFamily::Inet6);
+    let (_lo_name, lo) = match lo_ifaddr {
+        Some(ifaddr) => (
+            ifaddr.interface_name,
+            ifaddr.address.expect("Expect IPv6 address on interface"),
+        ),
+        None => return,
+    };
+    let receive = socket(
+        AddressFamily::Inet6,
+        SockType::Datagram,
+        SockFlag::empty(),
+        None,
+    )
+    .expect("receive socket failed");
+    bind(receive, &lo).expect("bind failed");
+    let sa: SockaddrIn6 = getsockname(receive).expect("getsockname failed");
+    setsockopt(receive, Ipv6OrigDstAddr, &true)
+        .expect("setsockopt IP_ORIGDSTADDR failed");
+
+    {
+        let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
+        let iov = [IoSlice::new(&slice)];
+
+        let send = socket(
+            AddressFamily::Inet6,
+            SockType::Datagram,
+            SockFlag::empty(),
+            None,
+        )
+        .expect("send socket failed");
+        sendmsg(send, &iov, &[], MsgFlags::empty(), Some(&sa))
+            .expect("sendmsg failed");
+    }
+
+    {
+        let mut buf = [0u8; 8];
+        let mut iovec = [IoSliceMut::new(&mut buf)];
+        let mut space = cmsg_space!(libc::sockaddr_in6);
+        let msg = recvmsg::<()>(
+            receive,
+            &mut iovec,
+            Some(&mut space),
+            MsgFlags::empty(),
+        )
+        .expect("recvmsg failed");
+        assert!(!msg
+            .flags
+            .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
+        assert_eq!(msg.cmsgs().count(), 1, "expected 1 cmsgs");
+
+        let mut rx_recvorigdstaddr = false;
+        for cmsg in msg.cmsgs() {
+            match cmsg {
+                ControlMessageOwned::Ipv6OrigDstAddr(addr) => {
+                    rx_recvorigdstaddr = true;
+                    if let Some(sin) = lo.as_sockaddr_in6() {
+                        assert_eq!(sin.as_ref().sin6_addr.s6_addr,
+                                   addr.sin6_addr.s6_addr,
+                                   "unexpected destination address (expected {:?}, got {:?})",
+                                   sin.as_ref().sin6_addr.s6_addr,
+                                   addr.sin6_addr.s6_addr);
+                    } else {
+                        panic!("unexpected Sockaddr");
+                    }
+                }
+                _ => panic!("unexpected additional control msg"),
+            }
+        }
+        assert!(rx_recvorigdstaddr);
+        assert_eq!(msg.bytes, 8);
+        assert_eq!(*iovec[0], [1u8, 2, 3, 4, 5, 6, 7, 8]);
     }
 }
 
@@ -1465,27 +2049,32 @@
     target_os = "openbsd",
 ))]
 // qemu doesn't seem to be emulating this correctly in these architectures
-#[cfg_attr(all(
-    qemu,
-    any(
-        target_arch = "mips",
-        target_arch = "mips64",
-        target_arch = "powerpc64",
-    )
-), ignore)]
+#[cfg_attr(
+    all(
+        qemu,
+        any(
+            target_arch = "mips",
+            target_arch = "mips64",
+            target_arch = "powerpc64",
+        )
+    ),
+    ignore
+)]
 #[test]
 pub fn test_recv_ipv6pktinfo() {
     use nix::net::if_::*;
     use nix::sys::socket::sockopt::Ipv6RecvPacketInfo;
-    use nix::sys::socket::{bind, SockFlag, SockType};
+    use nix::sys::socket::{bind, SockFlag, SockType, SockaddrIn6};
     use nix::sys::socket::{getsockname, setsockopt, socket};
     use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags};
-    use nix::sys::uio::IoVec;
+    use std::io::{IoSlice, IoSliceMut};
 
     let lo_ifaddr = loopback_address(AddressFamily::Inet6);
     let (lo_name, lo) = match lo_ifaddr {
-        Some(ifaddr) => (ifaddr.interface_name,
-                         ifaddr.address.expect("Expect IPv4 address on interface")),
+        Some(ifaddr) => (
+            ifaddr.interface_name,
+            ifaddr.address.expect("Expect IPv6 address on interface"),
+        ),
         None => return,
     };
     let receive = socket(
@@ -1493,57 +2082,56 @@
         SockType::Datagram,
         SockFlag::empty(),
         None,
-    ).expect("receive socket failed");
+    )
+    .expect("receive socket failed");
     bind(receive, &lo).expect("bind failed");
-    let sa = getsockname(receive).expect("getsockname failed");
+    let sa: SockaddrIn6 = getsockname(receive).expect("getsockname failed");
     setsockopt(receive, Ipv6RecvPacketInfo, &true).expect("setsockopt failed");
 
     {
         let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
-        let iov = [IoVec::from_slice(&slice)];
+        let iov = [IoSlice::new(&slice)];
 
         let send = socket(
             AddressFamily::Inet6,
             SockType::Datagram,
             SockFlag::empty(),
             None,
-        ).expect("send socket failed");
-        sendmsg(send, &iov, &[], MsgFlags::empty(), Some(&sa)).expect("sendmsg failed");
+        )
+        .expect("send socket failed");
+        sendmsg(send, &iov, &[], MsgFlags::empty(), Some(&sa))
+            .expect("sendmsg failed");
     }
 
     {
         let mut buf = [0u8; 8];
-        let iovec = [IoVec::from_mut_slice(&mut buf)];
+        let mut iovec = [IoSliceMut::new(&mut buf)];
+
         let mut space = cmsg_space!(libc::in6_pktinfo);
-        let msg = recvmsg(
+        let msg = recvmsg::<()>(
             receive,
-            &iovec,
+            &mut iovec,
             Some(&mut space),
             MsgFlags::empty(),
-        ).expect("recvmsg failed");
-        assert!(
-            !msg.flags
-                .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)
-        );
+        )
+        .expect("recvmsg failed");
+        assert!(!msg
+            .flags
+            .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
 
         let mut cmsgs = msg.cmsgs();
         if let Some(ControlMessageOwned::Ipv6PacketInfo(pktinfo)) = cmsgs.next()
         {
             let i = if_nametoindex(lo_name.as_bytes()).expect("if_nametoindex");
             assert_eq!(
-                pktinfo.ipi6_ifindex as libc::c_uint,
-                i,
+                pktinfo.ipi6_ifindex as libc::c_uint, i,
                 "unexpected ifindex (expected {}, got {})",
-                i,
-                pktinfo.ipi6_ifindex
+                i, pktinfo.ipi6_ifindex
             );
         }
         assert!(cmsgs.next().is_none(), "unexpected additional control msg");
         assert_eq!(msg.bytes, 8);
-        assert_eq!(
-            iovec[0].as_slice(),
-            [1u8, 2, 3, 4, 5, 6, 7, 8]
-        );
+        assert_eq!(*iovec[0], [1u8, 2, 3, 4, 5, 6, 7, 8]);
     }
 }
 
@@ -1552,38 +2140,47 @@
 #[test]
 pub fn test_vsock() {
     use nix::errno::Errno;
-    use nix::sys::socket::{AddressFamily, socket, bind, connect, listen,
-                           SockAddr, SockType, SockFlag};
-    use nix::unistd::{close};
+    use nix::sys::socket::{
+        bind, connect, listen, socket, AddressFamily, SockFlag, SockType,
+        VsockAddr,
+    };
+    use nix::unistd::close;
     use std::thread;
 
     let port: u32 = 3000;
 
-    let s1 = socket(AddressFamily::Vsock,  SockType::Stream,
-                    SockFlag::empty(), None)
-             .expect("socket failed");
+    let s1 = socket(
+        AddressFamily::Vsock,
+        SockType::Stream,
+        SockFlag::empty(),
+        None,
+    )
+    .expect("socket failed");
 
     // VMADDR_CID_HYPERVISOR is reserved, so we expect an EADDRNOTAVAIL error.
-    let sockaddr = SockAddr::new_vsock(libc::VMADDR_CID_HYPERVISOR, port);
-    assert_eq!(bind(s1, &sockaddr).err(),
-               Some(Errno::EADDRNOTAVAIL));
+    let sockaddr_hv = VsockAddr::new(libc::VMADDR_CID_HYPERVISOR, port);
+    assert_eq!(bind(s1, &sockaddr_hv).err(), Some(Errno::EADDRNOTAVAIL));
 
-    let sockaddr = SockAddr::new_vsock(libc::VMADDR_CID_ANY, port);
-    assert_eq!(bind(s1, &sockaddr), Ok(()));
+    let sockaddr_any = VsockAddr::new(libc::VMADDR_CID_ANY, port);
+    assert_eq!(bind(s1, &sockaddr_any), Ok(()));
     listen(s1, 10).expect("listen failed");
 
     let thr = thread::spawn(move || {
         let cid: u32 = libc::VMADDR_CID_HOST;
 
-        let s2 = socket(AddressFamily::Vsock, SockType::Stream,
-                        SockFlag::empty(), None)
-                 .expect("socket failed");
+        let s2 = socket(
+            AddressFamily::Vsock,
+            SockType::Stream,
+            SockFlag::empty(),
+            None,
+        )
+        .expect("socket failed");
 
-        let sockaddr = SockAddr::new_vsock(cid, port);
+        let sockaddr_host = VsockAddr::new(cid, port);
 
         // The current implementation does not support loopback devices, so,
         // for now, we expect a failure on the connect.
-        assert_ne!(connect(s2, &sockaddr), Ok(()));
+        assert_ne!(connect(s2, &sockaddr_host), Ok(()));
 
         close(s2).unwrap();
     });
@@ -1599,8 +2196,8 @@
 #[test]
 fn test_recvmsg_timestampns() {
     use nix::sys::socket::*;
-    use nix::sys::uio::IoVec;
     use nix::sys::time::*;
+    use std::io::{IoSlice, IoSliceMut};
     use std::time::*;
 
     // Set up
@@ -1609,34 +2206,38 @@
         AddressFamily::Inet,
         SockType::Datagram,
         SockFlag::empty(),
-        None).unwrap();
+        None,
+    )
+    .unwrap();
     setsockopt(in_socket, sockopt::ReceiveTimestampns, &true).unwrap();
-    let localhost = InetAddr::new(IpAddr::new_v4(127, 0, 0, 1), 0);
-    bind(in_socket, &SockAddr::new_inet(localhost)).unwrap();
-    let address = getsockname(in_socket).unwrap();
+    let localhost = SockaddrIn::new(127, 0, 0, 1, 0);
+    bind(in_socket, &localhost).unwrap();
+    let address: SockaddrIn = getsockname(in_socket).unwrap();
     // Get initial time
     let time0 = SystemTime::now();
     // Send the message
-    let iov = [IoVec::from_slice(message)];
+    let iov = [IoSlice::new(message)];
     let flags = MsgFlags::empty();
     let l = sendmsg(in_socket, &iov, &[], flags, Some(&address)).unwrap();
     assert_eq!(message.len(), l);
     // Receive the message
     let mut buffer = vec![0u8; message.len()];
     let mut cmsgspace = nix::cmsg_space!(TimeSpec);
-    let iov = [IoVec::from_mut_slice(&mut buffer)];
-    let r = recvmsg(in_socket, &iov, Some(&mut cmsgspace), flags).unwrap();
+
+    let mut iov = [IoSliceMut::new(&mut buffer)];
+    let r = recvmsg::<()>(in_socket, &mut iov, Some(&mut cmsgspace), flags)
+        .unwrap();
     let rtime = match r.cmsgs().next() {
         Some(ControlMessageOwned::ScmTimestampns(rtime)) => rtime,
         Some(_) => panic!("Unexpected control message"),
-        None => panic!("No control message")
+        None => panic!("No control message"),
     };
     // Check the final time
     let time1 = SystemTime::now();
     // the packet's received timestamp should lie in-between the two system
     // times, unless the system clock was adjusted in the meantime.
-    let rduration = Duration::new(rtime.tv_sec() as u64,
-    rtime.tv_nsec() as u32);
+    let rduration =
+        Duration::new(rtime.tv_sec() as u64, rtime.tv_nsec() as u32);
     assert!(time0.duration_since(UNIX_EPOCH).unwrap() <= rduration);
     assert!(rduration <= time1.duration_since(UNIX_EPOCH).unwrap());
     // Close socket
@@ -1650,8 +2251,8 @@
 #[test]
 fn test_recvmmsg_timestampns() {
     use nix::sys::socket::*;
-    use nix::sys::uio::IoVec;
     use nix::sys::time::*;
+    use std::io::{IoSlice, IoSliceMut};
     use std::time::*;
 
     // Set up
@@ -1660,40 +2261,40 @@
         AddressFamily::Inet,
         SockType::Datagram,
         SockFlag::empty(),
-        None).unwrap();
+        None,
+    )
+    .unwrap();
     setsockopt(in_socket, sockopt::ReceiveTimestampns, &true).unwrap();
-    let localhost = InetAddr::new(IpAddr::new_v4(127, 0, 0, 1), 0);
-    bind(in_socket, &SockAddr::new_inet(localhost)).unwrap();
-    let address = getsockname(in_socket).unwrap();
+    let localhost = SockaddrIn::from_str("127.0.0.1:0").unwrap();
+    bind(in_socket, &localhost).unwrap();
+    let address: SockaddrIn = getsockname(in_socket).unwrap();
     // Get initial time
     let time0 = SystemTime::now();
     // Send the message
-    let iov = [IoVec::from_slice(message)];
+    let iov = [IoSlice::new(message)];
     let flags = MsgFlags::empty();
     let l = sendmsg(in_socket, &iov, &[], flags, Some(&address)).unwrap();
     assert_eq!(message.len(), l);
     // Receive the message
     let mut buffer = vec![0u8; message.len()];
-    let mut cmsgspace = nix::cmsg_space!(TimeSpec);
-    let iov = [IoVec::from_mut_slice(&mut buffer)];
-    let mut data = vec![
-        RecvMmsgData {
-            iov,
-            cmsg_buffer: Some(&mut cmsgspace),
-        },
-    ];
-    let r = recvmmsg(in_socket, &mut data, flags, None).unwrap();
+    let cmsgspace = nix::cmsg_space!(TimeSpec);
+    let iov = vec![[IoSliceMut::new(&mut buffer)]];
+    let mut data = MultiHeaders::preallocate(1, Some(cmsgspace));
+    let r: Vec<RecvMsg<()>> =
+        recvmmsg(in_socket, &mut data, iov.iter(), flags, None)
+            .unwrap()
+            .collect();
     let rtime = match r[0].cmsgs().next() {
         Some(ControlMessageOwned::ScmTimestampns(rtime)) => rtime,
         Some(_) => panic!("Unexpected control message"),
-        None => panic!("No control message")
+        None => panic!("No control message"),
     };
     // Check the final time
     let time1 = SystemTime::now();
     // the packet's received timestamp should lie in-between the two system
     // times, unless the system clock was adjusted in the meantime.
-    let rduration = Duration::new(rtime.tv_sec() as u64,
-    rtime.tv_nsec() as u32);
+    let rduration =
+        Duration::new(rtime.tv_sec() as u64, rtime.tv_nsec() as u32);
     assert!(time0.duration_since(UNIX_EPOCH).unwrap() <= rduration);
     assert!(rduration <= time1.duration_since(UNIX_EPOCH).unwrap());
     // Close socket
@@ -1706,10 +2307,10 @@
 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
 #[test]
 fn test_recvmsg_rxq_ovfl() {
-    use nix::Error;
+    use nix::sys::socket::sockopt::{RcvBuf, RxqOvfl};
     use nix::sys::socket::*;
-    use nix::sys::uio::IoVec;
-    use nix::sys::socket::sockopt::{RxqOvfl, RcvBuf};
+    use nix::Error;
+    use std::io::{IoSlice, IoSliceMut};
 
     let message = [0u8; 2048];
     let bufsize = message.len() * 2;
@@ -1718,17 +2319,21 @@
         AddressFamily::Inet,
         SockType::Datagram,
         SockFlag::empty(),
-        None).unwrap();
+        None,
+    )
+    .unwrap();
     let out_socket = socket(
         AddressFamily::Inet,
         SockType::Datagram,
         SockFlag::empty(),
-        None).unwrap();
+        None,
+    )
+    .unwrap();
 
-    let localhost = InetAddr::new(IpAddr::new_v4(127, 0, 0, 1), 0);
-    bind(in_socket, &SockAddr::new_inet(localhost)).unwrap();
+    let localhost = SockaddrIn::from_str("127.0.0.1:0").unwrap();
+    bind(in_socket, &localhost).unwrap();
 
-    let address = getsockname(in_socket).unwrap();
+    let address: SockaddrIn = getsockname(in_socket).unwrap();
     connect(out_socket, &address).unwrap();
 
     // Set SO_RXQ_OVFL flag.
@@ -1740,13 +2345,14 @@
     let mut drop_counter = 0;
 
     for _ in 0..2 {
-        let iov = [IoVec::from_slice(&message)];
+        let iov = [IoSlice::new(&message)];
         let flags = MsgFlags::empty();
 
         // Send the 3 messages (the receiver buffer can only hold 2 messages)
         // to create an overflow.
         for _ in 0..3 {
-            let l = sendmsg(out_socket, &iov, &[], flags, Some(&address)).unwrap();
+            let l =
+                sendmsg(out_socket, &iov, &[], flags, Some(&address)).unwrap();
             assert_eq!(message.len(), l);
         }
 
@@ -1755,22 +2361,29 @@
             let mut buffer = vec![0u8; message.len()];
             let mut cmsgspace = nix::cmsg_space!(u32);
 
-            let iov = [IoVec::from_mut_slice(&mut buffer)];
+            let mut iov = [IoSliceMut::new(&mut buffer)];
 
-            match recvmsg(
+            match recvmsg::<()>(
                 in_socket,
-                &iov,
+                &mut iov,
                 Some(&mut cmsgspace),
-                MsgFlags::MSG_DONTWAIT) {
+                MsgFlags::MSG_DONTWAIT,
+            ) {
                 Ok(r) => {
                     drop_counter = match r.cmsgs().next() {
-                        Some(ControlMessageOwned::RxqOvfl(drop_counter)) => drop_counter,
+                        Some(ControlMessageOwned::RxqOvfl(drop_counter)) => {
+                            drop_counter
+                        }
                         Some(_) => panic!("Unexpected control message"),
                         None => 0,
                     };
-                },
-                Err(Error::EAGAIN) => { break; },
-                _ => { panic!("unknown recvmsg() error"); },
+                }
+                Err(Error::EAGAIN) => {
+                    break;
+                }
+                _ => {
+                    panic!("unknown recvmsg() error");
+                }
             }
         }
     }
@@ -1783,13 +2396,10 @@
     nix::unistd::close(out_socket).unwrap();
 }
 
-#[cfg(any(
-    target_os = "linux",
-    target_os = "android",
-))]
+#[cfg(any(target_os = "linux", target_os = "android",))]
 mod linux_errqueue {
+    use super::FromStr;
     use nix::sys::socket::*;
-    use super::{FromStr, SocketAddr};
 
     // Send a UDP datagram to a bogus destination address and observe an ICMP error (v4).
     //
@@ -1817,11 +2427,16 @@
             // Closure handles protocol-specific testing and returns generic sock_extended_err for
             // protocol-independent test impl.
             |cmsg| {
-                if let ControlMessageOwned::Ipv4RecvErr(ext_err, err_addr) = cmsg {
+                if let ControlMessageOwned::Ipv4RecvErr(ext_err, err_addr) =
+                    cmsg
+                {
                     if let Some(origin) = err_addr {
                         // Validate that our network error originated from 127.0.0.1:0.
                         assert_eq!(origin.sin_family, AddressFamily::Inet as _);
-                        assert_eq!(Ipv4Addr(origin.sin_addr), Ipv4Addr::new(127, 0, 0, 1));
+                        assert_eq!(
+                            origin.sin_addr.s_addr,
+                            u32::from_be(0x7f000001)
+                        );
                         assert_eq!(origin.sin_port, 0);
                     } else {
                         panic!("Expected some error origin");
@@ -1860,13 +2475,18 @@
             // Closure handles protocol-specific testing and returns generic sock_extended_err for
             // protocol-independent test impl.
             |cmsg| {
-                if let ControlMessageOwned::Ipv6RecvErr(ext_err, err_addr) = cmsg {
+                if let ControlMessageOwned::Ipv6RecvErr(ext_err, err_addr) =
+                    cmsg
+                {
                     if let Some(origin) = err_addr {
                         // Validate that our network error originated from localhost:0.
-                        assert_eq!(origin.sin6_family, AddressFamily::Inet6 as _);
                         assert_eq!(
-                            Ipv6Addr(origin.sin6_addr),
-                            Ipv6Addr::from_std(&"::1".parse().unwrap()),
+                            origin.sin6_family,
+                            AddressFamily::Inet6 as _
+                        );
+                        assert_eq!(
+                            origin.sin6_addr.s6_addr,
+                            std::net::Ipv6Addr::LOCALHOST.octets()
                         );
                         assert_eq!(origin.sin6_port, 0);
                     } else {
@@ -1880,43 +2500,51 @@
         )
     }
 
-    fn test_recverr_impl<SA, OPT, TESTF>(sa: &str,
-                                         af: AddressFamily,
-                                         opt: OPT,
-                                         ee_origin: u8,
-                                         ee_type: u8,
-                                         ee_code: u8,
-                                         testf: TESTF)
-        where
-            OPT: SetSockOpt<Val = bool>,
-            TESTF: FnOnce(&ControlMessageOwned) -> libc::sock_extended_err,
+    fn test_recverr_impl<SA, OPT, TESTF>(
+        sa: &str,
+        af: AddressFamily,
+        opt: OPT,
+        ee_origin: u8,
+        ee_type: u8,
+        ee_code: u8,
+        testf: TESTF,
+    ) where
+        OPT: SetSockOpt<Val = bool>,
+        TESTF: FnOnce(&ControlMessageOwned) -> libc::sock_extended_err,
     {
         use nix::errno::Errno;
-        use nix::sys::uio::IoVec;
+        use std::io::IoSliceMut;
 
         const MESSAGE_CONTENTS: &str = "ABCDEF";
-
-        let sock_addr = {
-            let std_sa = SocketAddr::from_str(sa).unwrap();
-            let inet_addr = InetAddr::from_std(&std_sa);
-            SockAddr::new_inet(inet_addr)
-        };
-        let sock = socket(af, SockType::Datagram, SockFlag::SOCK_CLOEXEC, None).unwrap();
+        let std_sa = std::net::SocketAddr::from_str(sa).unwrap();
+        let sock_addr = SockaddrStorage::from(std_sa);
+        let sock = socket(af, SockType::Datagram, SockFlag::SOCK_CLOEXEC, None)
+            .unwrap();
         setsockopt(sock, opt, &true).unwrap();
-        if let Err(e) = sendto(sock, MESSAGE_CONTENTS.as_bytes(), &sock_addr, MsgFlags::empty()) {
+        if let Err(e) = sendto(
+            sock,
+            MESSAGE_CONTENTS.as_bytes(),
+            &sock_addr,
+            MsgFlags::empty(),
+        ) {
             assert_eq!(e, Errno::EADDRNOTAVAIL);
             println!("{:?} not available, skipping test.", af);
             return;
         }
 
         let mut buf = [0u8; 8];
-        let iovec = [IoVec::from_mut_slice(&mut buf)];
+        let mut iovec = [IoSliceMut::new(&mut buf)];
         let mut cspace = cmsg_space!(libc::sock_extended_err, SA);
 
-        let msg = recvmsg(sock, &iovec, Some(&mut cspace), MsgFlags::MSG_ERRQUEUE).unwrap();
+        let msg = recvmsg(
+            sock,
+            &mut iovec,
+            Some(&mut cspace),
+            MsgFlags::MSG_ERRQUEUE,
+        )
+        .unwrap();
         // The sent message / destination associated with the error is returned:
         assert_eq!(msg.bytes, MESSAGE_CONTENTS.as_bytes().len());
-        assert_eq!(&buf[..msg.bytes], MESSAGE_CONTENTS.as_bytes());
         // recvmsg(2): "The original destination address of the datagram that caused the error is
         // supplied via msg_name;" however, this is not literally true.  E.g., an earlier version
         // of this test used 0.0.0.0 (::0) as the destination address, which was mutated into
@@ -1937,5 +2565,64 @@
         assert_eq!(ext_err.ee_code, ee_code);
         // ip(7): ee_info contains the discovered MTU for EMSGSIZE errors.
         assert_eq!(ext_err.ee_info, 0);
+
+        let bytes = msg.bytes;
+        assert_eq!(&buf[..bytes], MESSAGE_CONTENTS.as_bytes());
     }
 }
+
+// Disable the test on emulated platforms because it fails in Cirrus-CI.  Lack
+// of QEMU support is suspected.
+#[cfg_attr(qemu, ignore)]
+#[cfg(target_os = "linux")]
+#[test]
+pub fn test_txtime() {
+    use nix::sys::socket::{
+        bind, recvmsg, sendmsg, setsockopt, socket, sockopt, ControlMessage,
+        MsgFlags, SockFlag, SockType, SockaddrIn,
+    };
+    use nix::sys::time::TimeValLike;
+    use nix::time::{clock_gettime, ClockId};
+
+    require_kernel_version!(test_txtime, ">= 5.8");
+
+    let sock_addr = SockaddrIn::from_str("127.0.0.1:6802").unwrap();
+
+    let ssock = socket(
+        AddressFamily::Inet,
+        SockType::Datagram,
+        SockFlag::empty(),
+        None,
+    )
+    .expect("send socket failed");
+
+    let txtime_cfg = libc::sock_txtime {
+        clockid: libc::CLOCK_MONOTONIC,
+        flags: 0,
+    };
+    setsockopt(ssock, sockopt::TxTime, &txtime_cfg).unwrap();
+
+    let rsock = socket(
+        AddressFamily::Inet,
+        SockType::Datagram,
+        SockFlag::empty(),
+        None,
+    )
+    .unwrap();
+    bind(rsock, &sock_addr).unwrap();
+
+    let sbuf = [0u8; 2048];
+    let iov1 = [std::io::IoSlice::new(&sbuf)];
+
+    let now = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap();
+    let delay = std::time::Duration::from_secs(1).into();
+    let txtime = (now + delay).num_nanoseconds() as u64;
+
+    let cmsg = ControlMessage::TxTime(&txtime);
+    sendmsg(ssock, &iov1, &[cmsg], MsgFlags::empty(), Some(&sock_addr))
+        .unwrap();
+
+    let mut rbuf = [0u8; 2048];
+    let mut iov2 = [std::io::IoSliceMut::new(&mut rbuf)];
+    recvmsg::<()>(rsock, &mut iov2, None, MsgFlags::empty()).unwrap();
+}
diff --git a/test/sys/test_sockopt.rs b/test/sys/test_sockopt.rs
index 01920fd..34bef94 100644
--- a/test/sys/test_sockopt.rs
+++ b/test/sys/test_sockopt.rs
@@ -1,22 +1,27 @@
-use rand::{thread_rng, Rng};
-use nix::sys::socket::{socket, sockopt, getsockopt, setsockopt, AddressFamily, SockType, SockFlag, SockProtocol};
 #[cfg(any(target_os = "android", target_os = "linux"))]
 use crate::*;
+use nix::sys::socket::{
+    getsockopt, setsockopt, socket, sockopt, AddressFamily, SockFlag,
+    SockProtocol, SockType,
+};
+use rand::{thread_rng, Rng};
 
 // NB: FreeBSD supports LOCAL_PEERCRED for SOCK_SEQPACKET, but OSX does not.
-#[cfg(any(
-        target_os = "dragonfly",
-        target_os = "freebsd",
-))]
+#[cfg(any(target_os = "dragonfly", target_os = "freebsd",))]
 #[test]
 pub fn test_local_peercred_seqpacket() {
     use nix::{
+        sys::socket::socketpair,
         unistd::{Gid, Uid},
-        sys::socket::socketpair
     };
 
-    let (fd1, _fd2) = socketpair(AddressFamily::Unix, SockType::SeqPacket, None,
-                                SockFlag::empty()).unwrap();
+    let (fd1, _fd2) = socketpair(
+        AddressFamily::Unix,
+        SockType::SeqPacket,
+        None,
+        SockFlag::empty(),
+    )
+    .unwrap();
     let xucred = getsockopt(fd1, sockopt::LocalPeerCred).unwrap();
     assert_eq!(xucred.version(), 0);
     assert_eq!(Uid::from_raw(xucred.uid()), Uid::current());
@@ -24,20 +29,25 @@
 }
 
 #[cfg(any(
-        target_os = "dragonfly",
-        target_os = "freebsd",
-        target_os = "macos",
-        target_os = "ios"
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "macos",
+    target_os = "ios"
 ))]
 #[test]
 pub fn test_local_peercred_stream() {
     use nix::{
+        sys::socket::socketpair,
         unistd::{Gid, Uid},
-        sys::socket::socketpair
     };
 
-    let (fd1, _fd2) = socketpair(AddressFamily::Unix, SockType::Stream, None,
-                                SockFlag::empty()).unwrap();
+    let (fd1, _fd2) = socketpair(
+        AddressFamily::Unix,
+        SockType::Stream,
+        None,
+        SockFlag::empty(),
+    )
+    .unwrap();
     let xucred = getsockopt(fd1, sockopt::LocalPeerCred).unwrap();
     assert_eq!(xucred.version(), 0);
     assert_eq!(Uid::from_raw(xucred.uid()), Uid::current());
@@ -51,7 +61,13 @@
 
     require_capability!("is_so_mark_functional", CAP_NET_ADMIN);
 
-    let s = socket(AddressFamily::Inet, SockType::Stream, SockFlag::empty(), None).unwrap();
+    let s = socket(
+        AddressFamily::Inet,
+        SockType::Stream,
+        SockFlag::empty(),
+        None,
+    )
+    .unwrap();
     setsockopt(s, sockopt::Mark, &1337).unwrap();
     let mark = getsockopt(s, sockopt::Mark).unwrap();
     assert_eq!(mark, 1337);
@@ -59,8 +75,13 @@
 
 #[test]
 fn test_so_buf() {
-    let fd = socket(AddressFamily::Inet, SockType::Datagram, SockFlag::empty(), SockProtocol::Udp)
-             .unwrap();
+    let fd = socket(
+        AddressFamily::Inet,
+        SockType::Datagram,
+        SockFlag::empty(),
+        SockProtocol::Udp,
+    )
+    .unwrap();
     let bufsize: usize = thread_rng().gen_range(4096..131_072);
     setsockopt(fd, sockopt::SndBuf, &bufsize).unwrap();
     let actual = getsockopt(fd, sockopt::SndBuf).unwrap();
@@ -72,17 +93,21 @@
 
 #[test]
 fn test_so_tcp_maxseg() {
-    use std::net::SocketAddr;
-    use std::str::FromStr;
-    use nix::sys::socket::{accept, bind, connect, listen, InetAddr, SockAddr};
+    use nix::sys::socket::{accept, bind, connect, listen, SockaddrIn};
     use nix::unistd::{close, write};
+    use std::net::SocketAddrV4;
+    use std::str::FromStr;
 
-    let std_sa = SocketAddr::from_str("127.0.0.1:4001").unwrap();
-    let inet_addr = InetAddr::from_std(&std_sa);
-    let sock_addr = SockAddr::new_inet(inet_addr);
+    let std_sa = SocketAddrV4::from_str("127.0.0.1:4001").unwrap();
+    let sock_addr = SockaddrIn::from(std_sa);
 
-    let rsock = socket(AddressFamily::Inet, SockType::Stream, SockFlag::empty(), SockProtocol::Tcp)
-                .unwrap();
+    let rsock = socket(
+        AddressFamily::Inet,
+        SockType::Stream,
+        SockFlag::empty(),
+        SockProtocol::Tcp,
+    )
+    .unwrap();
     bind(rsock, &sock_addr).unwrap();
     listen(rsock, 10).unwrap();
     let initial = getsockopt(rsock, sockopt::TcpMaxSeg).unwrap();
@@ -100,8 +125,13 @@
     }
 
     // Connect and check the MSS that was advertised
-    let ssock = socket(AddressFamily::Inet, SockType::Stream, SockFlag::empty(), SockProtocol::Tcp)
-                .unwrap();
+    let ssock = socket(
+        AddressFamily::Inet,
+        SockType::Stream,
+        SockFlag::empty(),
+        SockProtocol::Tcp,
+    )
+    .unwrap();
     connect(ssock, &sock_addr).unwrap();
     let rsess = accept(rsock).unwrap();
     write(rsess, b"hello").unwrap();
@@ -121,8 +151,35 @@
     close(ssock).unwrap();
 }
 
+#[test]
+fn test_so_type() {
+    let sockfd = socket(
+        AddressFamily::Inet,
+        SockType::Stream,
+        SockFlag::empty(),
+        None,
+    )
+    .unwrap();
+
+    assert_eq!(Ok(SockType::Stream), getsockopt(sockfd, sockopt::SockType));
+}
+
+/// getsockopt(_, sockopt::SockType) should gracefully handle unknown socket
+/// types.  Regression test for https://github.com/nix-rust/nix/issues/1819
+#[cfg(any(target_os = "android", target_os = "linux",))]
+#[test]
+fn test_so_type_unknown() {
+    use nix::errno::Errno;
+
+    require_capability!("test_so_type", CAP_NET_RAW);
+    let sockfd = unsafe { libc::socket(libc::AF_PACKET, libc::SOCK_PACKET, 0) };
+    assert!(sockfd >= 0, "Error opening socket: {}", nix::Error::last());
+
+    assert_eq!(Err(Errno::EINVAL), getsockopt(sockfd, sockopt::SockType));
+}
+
 // The CI doesn't supported getsockopt and setsockopt on emulated processors.
-// It's beleived that a QEMU issue, the tests run ok on a fully emulated system.
+// It's believed that a QEMU issue, the tests run ok on a fully emulated system.
 // Current CI just run the binary with QEMU but the Kernel remains the same as the host.
 // So the syscall doesn't work properly unless the kernel is also emulated.
 #[test]
@@ -133,17 +190,25 @@
 fn test_tcp_congestion() {
     use std::ffi::OsString;
 
-    let fd = socket(AddressFamily::Inet, SockType::Stream, SockFlag::empty(), None).unwrap();
+    let fd = socket(
+        AddressFamily::Inet,
+        SockType::Stream,
+        SockFlag::empty(),
+        None,
+    )
+    .unwrap();
 
     let val = getsockopt(fd, sockopt::TcpCongestion).unwrap();
     setsockopt(fd, sockopt::TcpCongestion, &val).unwrap();
 
-    setsockopt(fd, sockopt::TcpCongestion, &OsString::from("tcp_congestion_does_not_exist")).unwrap_err();
+    setsockopt(
+        fd,
+        sockopt::TcpCongestion,
+        &OsString::from("tcp_congestion_does_not_exist"),
+    )
+    .unwrap_err();
 
-    assert_eq!(
-        getsockopt(fd, sockopt::TcpCongestion).unwrap(),
-        val
-    );
+    assert_eq!(getsockopt(fd, sockopt::TcpCongestion).unwrap(), val);
 }
 
 #[test]
@@ -151,28 +216,39 @@
 fn test_bindtodevice() {
     skip_if_not_root!("test_bindtodevice");
 
-    let fd = socket(AddressFamily::Inet, SockType::Stream, SockFlag::empty(), None).unwrap();
+    let fd = socket(
+        AddressFamily::Inet,
+        SockType::Stream,
+        SockFlag::empty(),
+        None,
+    )
+    .unwrap();
 
     let val = getsockopt(fd, sockopt::BindToDevice).unwrap();
     setsockopt(fd, sockopt::BindToDevice, &val).unwrap();
 
-    assert_eq!(
-        getsockopt(fd, sockopt::BindToDevice).unwrap(),
-        val
-    );
+    assert_eq!(getsockopt(fd, sockopt::BindToDevice).unwrap(), val);
 }
 
 #[test]
 fn test_so_tcp_keepalive() {
-    let fd = socket(AddressFamily::Inet, SockType::Stream, SockFlag::empty(), SockProtocol::Tcp).unwrap();
+    let fd = socket(
+        AddressFamily::Inet,
+        SockType::Stream,
+        SockFlag::empty(),
+        SockProtocol::Tcp,
+    )
+    .unwrap();
     setsockopt(fd, sockopt::KeepAlive, &true).unwrap();
     assert!(getsockopt(fd, sockopt::KeepAlive).unwrap());
 
-    #[cfg(any(target_os = "android",
-              target_os = "dragonfly",
-              target_os = "freebsd",
-              target_os = "linux",
-              target_os = "nacl"))] {
+    #[cfg(any(
+        target_os = "android",
+        target_os = "dragonfly",
+        target_os = "freebsd",
+        target_os = "linux"
+    ))]
+    {
         let x = getsockopt(fd, sockopt::TcpKeepIdle).unwrap();
         setsockopt(fd, sockopt::TcpKeepIdle, &(x + 1)).unwrap();
         assert_eq!(getsockopt(fd, sockopt::TcpKeepIdle).unwrap(), x + 1);
@@ -188,12 +264,168 @@
 }
 
 #[test]
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg_attr(qemu, ignore)]
+fn test_get_mtu() {
+    use nix::sys::socket::{bind, connect, SockaddrIn};
+    use std::net::SocketAddrV4;
+    use std::str::FromStr;
+
+    let std_sa = SocketAddrV4::from_str("127.0.0.1:4001").unwrap();
+    let std_sb = SocketAddrV4::from_str("127.0.0.1:4002").unwrap();
+
+    let usock = socket(
+        AddressFamily::Inet,
+        SockType::Datagram,
+        SockFlag::empty(),
+        SockProtocol::Udp,
+    )
+    .unwrap();
+
+    // Bind and initiate connection
+    bind(usock, &SockaddrIn::from(std_sa)).unwrap();
+    connect(usock, &SockaddrIn::from(std_sb)).unwrap();
+
+    // Loopback connections have 2^16 - the maximum - MTU
+    assert_eq!(getsockopt(usock, sockopt::IpMtu), Ok(u16::MAX as i32))
+}
+
+#[test]
 #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
 fn test_ttl_opts() {
-    let fd4 = socket(AddressFamily::Inet, SockType::Datagram, SockFlag::empty(), None).unwrap();
+    let fd4 = socket(
+        AddressFamily::Inet,
+        SockType::Datagram,
+        SockFlag::empty(),
+        None,
+    )
+    .unwrap();
     setsockopt(fd4, sockopt::Ipv4Ttl, &1)
         .expect("setting ipv4ttl on an inet socket should succeed");
-    let fd6 = socket(AddressFamily::Inet6, SockType::Datagram, SockFlag::empty(), None).unwrap();
+    let fd6 = socket(
+        AddressFamily::Inet6,
+        SockType::Datagram,
+        SockFlag::empty(),
+        None,
+    )
+    .unwrap();
     setsockopt(fd6, sockopt::Ipv6Ttl, &1)
         .expect("setting ipv6ttl on an inet6 socket should succeed");
 }
+
+#[test]
+#[cfg(any(target_os = "ios", target_os = "macos"))]
+fn test_dontfrag_opts() {
+    let fd4 = socket(
+        AddressFamily::Inet,
+        SockType::Stream,
+        SockFlag::empty(),
+        SockProtocol::Tcp,
+    )
+    .unwrap();
+    setsockopt(fd4, sockopt::IpDontFrag, &true)
+        .expect("setting IP_DONTFRAG on an inet stream socket should succeed");
+    setsockopt(fd4, sockopt::IpDontFrag, &false).expect(
+        "unsetting IP_DONTFRAG on an inet stream socket should succeed",
+    );
+    let fd4d = socket(
+        AddressFamily::Inet,
+        SockType::Datagram,
+        SockFlag::empty(),
+        None,
+    )
+    .unwrap();
+    setsockopt(fd4d, sockopt::IpDontFrag, &true).expect(
+        "setting IP_DONTFRAG on an inet datagram socket should succeed",
+    );
+    setsockopt(fd4d, sockopt::IpDontFrag, &false).expect(
+        "unsetting IP_DONTFRAG on an inet datagram socket should succeed",
+    );
+}
+
+#[test]
+#[cfg(any(
+    target_os = "android",
+    target_os = "ios",
+    target_os = "linux",
+    target_os = "macos",
+))]
+// Disable the test under emulation because it fails in Cirrus-CI.  Lack
+// of QEMU support is suspected.
+#[cfg_attr(qemu, ignore)]
+fn test_v6dontfrag_opts() {
+    let fd6 = socket(
+        AddressFamily::Inet6,
+        SockType::Stream,
+        SockFlag::empty(),
+        SockProtocol::Tcp,
+    )
+    .unwrap();
+    setsockopt(fd6, sockopt::Ipv6DontFrag, &true).expect(
+        "setting IPV6_DONTFRAG on an inet6 stream socket should succeed",
+    );
+    setsockopt(fd6, sockopt::Ipv6DontFrag, &false).expect(
+        "unsetting IPV6_DONTFRAG on an inet6 stream socket should succeed",
+    );
+    let fd6d = socket(
+        AddressFamily::Inet6,
+        SockType::Datagram,
+        SockFlag::empty(),
+        None,
+    )
+    .unwrap();
+    setsockopt(fd6d, sockopt::Ipv6DontFrag, &true).expect(
+        "setting IPV6_DONTFRAG on an inet6 datagram socket should succeed",
+    );
+    setsockopt(fd6d, sockopt::Ipv6DontFrag, &false).expect(
+        "unsetting IPV6_DONTFRAG on an inet6 datagram socket should succeed",
+    );
+}
+
+#[test]
+#[cfg(target_os = "linux")]
+fn test_so_priority() {
+    let fd = socket(
+        AddressFamily::Inet,
+        SockType::Stream,
+        SockFlag::empty(),
+        SockProtocol::Tcp,
+    )
+    .unwrap();
+    let priority = 3;
+    setsockopt(fd, sockopt::Priority, &priority).unwrap();
+    assert_eq!(getsockopt(fd, sockopt::Priority).unwrap(), priority);
+}
+
+#[test]
+#[cfg(target_os = "linux")]
+fn test_ip_tos() {
+    let fd = socket(
+        AddressFamily::Inet,
+        SockType::Stream,
+        SockFlag::empty(),
+        SockProtocol::Tcp,
+    )
+    .unwrap();
+    let tos = 0x80; // CS4
+    setsockopt(fd, sockopt::IpTos, &tos).unwrap();
+    assert_eq!(getsockopt(fd, sockopt::IpTos).unwrap(), tos);
+}
+
+#[test]
+#[cfg(target_os = "linux")]
+// Disable the test under emulation because it fails in Cirrus-CI.  Lack
+// of QEMU support is suspected.
+#[cfg_attr(qemu, ignore)]
+fn test_ipv6_tclass() {
+    let fd = socket(
+        AddressFamily::Inet6,
+        SockType::Stream,
+        SockFlag::empty(),
+        SockProtocol::Tcp,
+    )
+    .unwrap();
+    let class = 0x80; // CS4
+    setsockopt(fd, sockopt::Ipv6TClass, &class).unwrap();
+    assert_eq!(getsockopt(fd, sockopt::Ipv6TClass).unwrap(), class);
+}
diff --git a/test/sys/test_stat.rs b/test/sys/test_stat.rs
new file mode 100644
index 0000000..426b4b6
--- /dev/null
+++ b/test/sys/test_stat.rs
@@ -0,0 +1,29 @@
+// The conversion is not useless on all platforms.
+#[allow(clippy::useless_conversion)]
+#[cfg(target_os = "freebsd")]
+#[test]
+fn test_chflags() {
+    use nix::{
+        sys::stat::{fstat, FileFlag},
+        unistd::chflags,
+    };
+    use std::os::unix::io::AsRawFd;
+    use tempfile::NamedTempFile;
+
+    let f = NamedTempFile::new().unwrap();
+
+    let initial = FileFlag::from_bits_truncate(
+        fstat(f.as_raw_fd()).unwrap().st_flags.into(),
+    );
+    // UF_OFFLINE is preserved by all FreeBSD file systems, but not interpreted
+    // in any way, so it's handy for testing.
+    let commanded = initial ^ FileFlag::UF_OFFLINE;
+
+    chflags(f.path(), commanded).unwrap();
+
+    let changed = FileFlag::from_bits_truncate(
+        fstat(f.as_raw_fd()).unwrap().st_flags.into(),
+    );
+
+    assert_eq!(commanded, changed);
+}
diff --git a/test/sys/test_sysinfo.rs b/test/sys/test_sysinfo.rs
index 73e6586..2897366 100644
--- a/test/sys/test_sysinfo.rs
+++ b/test/sys/test_sysinfo.rs
@@ -9,10 +9,12 @@
     assert!(l5 >= 0.0);
     assert!(l15 >= 0.0);
 
-    info.uptime();  // just test Duration construction
+    info.uptime(); // just test Duration construction
 
-    assert!(info.swap_free() <= info.swap_total(),
-            "more swap available than installed (free: {}, total: {})",
-            info.swap_free(),
-            info.swap_total());
+    assert!(
+        info.swap_free() <= info.swap_total(),
+        "more swap available than installed (free: {}, total: {})",
+        info.swap_free(),
+        info.swap_total()
+    );
 }
diff --git a/test/sys/test_termios.rs b/test/sys/test_termios.rs
index 4a86154..aaf0008 100644
--- a/test/sys/test_termios.rs
+++ b/test/sys/test_termios.rs
@@ -1,11 +1,11 @@
 use std::os::unix::prelude::*;
 use tempfile::tempfile;
 
-use nix::fcntl;
 use nix::errno::Errno;
+use nix::fcntl;
 use nix::pty::openpty;
-use nix::sys::termios::{self, LocalFlags, OutputFlags, tcgetattr};
-use nix::unistd::{read, write, close};
+use nix::sys::termios::{self, tcgetattr, LocalFlags, OutputFlags};
+use nix::unistd::{close, read, write};
 
 /// Helper function analogous to `std::io::Write::write_all`, but for `RawFd`s
 fn write_all(f: RawFd, buf: &[u8]) {
@@ -22,7 +22,7 @@
     let _m = crate::PTSNAME_MTX.lock();
 
     let pty = openpty(None, None).expect("openpty failed");
-    assert!(termios::tcgetattr(pty.slave).is_ok());
+    termios::tcgetattr(pty.slave).unwrap();
     close(pty.master).expect("closing the master failed");
     close(pty.slave).expect("closing the slave failed");
 }
@@ -31,15 +31,16 @@
 #[test]
 fn test_tcgetattr_enotty() {
     let file = tempfile().unwrap();
-    assert_eq!(termios::tcgetattr(file.as_raw_fd()).err(),
-               Some(Errno::ENOTTY));
+    assert_eq!(
+        termios::tcgetattr(file.as_raw_fd()).err(),
+        Some(Errno::ENOTTY)
+    );
 }
 
 // Test tcgetattr on an invalid file descriptor
 #[test]
 fn test_tcgetattr_ebadf() {
-    assert_eq!(termios::tcgetattr(-1).err(),
-               Some(Errno::EBADF));
+    assert_eq!(termios::tcgetattr(-1).err(), Some(Errno::EBADF));
 }
 
 // Test modifying output flags
@@ -60,11 +61,15 @@
     };
 
     // Make sure postprocessing '\r' isn't specified by default or this test is useless.
-    assert!(!termios.output_flags.contains(OutputFlags::OPOST | OutputFlags::OCRNL));
+    assert!(!termios
+        .output_flags
+        .contains(OutputFlags::OPOST | OutputFlags::OCRNL));
 
     // Specify that '\r' characters should be transformed to '\n'
     // OPOST is specified to enable post-processing
-    termios.output_flags.insert(OutputFlags::OPOST | OutputFlags::OCRNL);
+    termios
+        .output_flags
+        .insert(OutputFlags::OPOST | OutputFlags::OCRNL);
 
     // Open a pty
     let pty = openpty(None, &termios).unwrap();
@@ -114,7 +119,8 @@
 
     // Set the master is in nonblocking mode or reading will never return.
     let flags = fcntl::fcntl(pty.master, fcntl::F_GETFL).unwrap();
-    let new_flags = fcntl::OFlag::from_bits_truncate(flags) | fcntl::OFlag::O_NONBLOCK;
+    let new_flags =
+        fcntl::OFlag::from_bits_truncate(flags) | fcntl::OFlag::O_NONBLOCK;
     fcntl::fcntl(pty.master, fcntl::F_SETFL(new_flags)).unwrap();
 
     // Write into the master
diff --git a/test/sys/test_timerfd.rs b/test/sys/test_timerfd.rs
index 24fb2ac..08e2921 100644
--- a/test/sys/test_timerfd.rs
+++ b/test/sys/test_timerfd.rs
@@ -1,10 +1,13 @@
 use nix::sys::time::{TimeSpec, TimeValLike};
-use nix::sys::timerfd::{ClockId, Expiration, TimerFd, TimerFlags, TimerSetTimeFlags};
+use nix::sys::timerfd::{
+    ClockId, Expiration, TimerFd, TimerFlags, TimerSetTimeFlags,
+};
 use std::time::Instant;
 
 #[test]
 pub fn test_timerfd_oneshot() {
-    let timer = TimerFd::new(ClockId::CLOCK_MONOTONIC, TimerFlags::empty()).unwrap();
+    let timer =
+        TimerFd::new(ClockId::CLOCK_MONOTONIC, TimerFlags::empty()).unwrap();
 
     let before = Instant::now();
 
@@ -23,12 +26,16 @@
 
 #[test]
 pub fn test_timerfd_interval() {
-    let timer = TimerFd::new(ClockId::CLOCK_MONOTONIC, TimerFlags::empty()).unwrap();
+    let timer =
+        TimerFd::new(ClockId::CLOCK_MONOTONIC, TimerFlags::empty()).unwrap();
 
     let before = Instant::now();
     timer
         .set(
-            Expiration::IntervalDelayed(TimeSpec::seconds(1), TimeSpec::seconds(2)),
+            Expiration::IntervalDelayed(
+                TimeSpec::seconds(1),
+                TimeSpec::seconds(2),
+            ),
             TimerSetTimeFlags::empty(),
         )
         .unwrap();
@@ -46,7 +53,8 @@
 
 #[test]
 pub fn test_timerfd_unset() {
-    let timer = TimerFd::new(ClockId::CLOCK_MONOTONIC, TimerFlags::empty()).unwrap();
+    let timer =
+        TimerFd::new(ClockId::CLOCK_MONOTONIC, TimerFlags::empty()).unwrap();
 
     timer
         .set(
@@ -57,5 +65,5 @@
 
     timer.unset().unwrap();
 
-    assert!(timer.get().unwrap() == None);
+    assert!(timer.get().unwrap().is_none());
 }
diff --git a/test/sys/test_uio.rs b/test/sys/test_uio.rs
index c63b581..0f4b8a6 100644
--- a/test/sys/test_uio.rs
+++ b/test/sys/test_uio.rs
@@ -1,14 +1,18 @@
 use nix::sys::uio::*;
 use nix::unistd::*;
-use rand::{thread_rng, Rng};
 use rand::distributions::Alphanumeric;
-use std::{cmp, iter};
-use std::fs::{OpenOptions};
+use rand::{thread_rng, Rng};
+use std::fs::OpenOptions;
+use std::io::IoSlice;
 use std::os::unix::io::AsRawFd;
+use std::{cmp, iter};
 
 #[cfg(not(target_os = "redox"))]
-use tempfile::tempfile;
+use std::io::IoSliceMut;
+
 use tempfile::tempdir;
+#[cfg(not(target_os = "redox"))]
+use tempfile::tempfile;
 
 #[test]
 fn test_writev() {
@@ -27,41 +31,38 @@
     let mut consumed = 0;
     while consumed < to_write.len() {
         let left = to_write.len() - consumed;
-        let slice_len = if left <= 64 { left } else { thread_rng().gen_range(64..cmp::min(256, left)) };
-        let b = &to_write[consumed..consumed+slice_len];
-        iovecs.push(IoVec::from_slice(b));
+        let slice_len = if left <= 64 {
+            left
+        } else {
+            thread_rng().gen_range(64..cmp::min(256, left))
+        };
+        let b = &to_write[consumed..consumed + slice_len];
+        iovecs.push(IoSlice::new(b));
         consumed += slice_len;
     }
     let pipe_res = pipe();
-    assert!(pipe_res.is_ok());
-    let (reader, writer) = pipe_res.ok().unwrap();
+    let (reader, writer) = pipe_res.expect("Couldn't create pipe");
     // FileDesc will close its filedesc (reader).
     let mut read_buf: Vec<u8> = iter::repeat(0u8).take(128 * 16).collect();
     // Blocking io, should write all data.
     let write_res = writev(writer, &iovecs);
-    // Successful write
-    assert!(write_res.is_ok());
-    let written = write_res.ok().unwrap();
+    let written = write_res.expect("couldn't write");
     // Check whether we written all data
     assert_eq!(to_write.len(), written);
     let read_res = read(reader, &mut read_buf[..]);
-    // Successful read
-    assert!(read_res.is_ok());
-    let read = read_res.ok().unwrap() as usize;
+    let read = read_res.expect("couldn't read");
     // Check we have read as much as we written
     assert_eq!(read, written);
     // Check equality of written and read data
     assert_eq!(&to_write, &read_buf);
-    let close_res = close(writer);
-    assert!(close_res.is_ok());
-    let close_res = close(reader);
-    assert!(close_res.is_ok());
+    close(writer).expect("closed writer");
+    close(reader).expect("closed reader");
 }
 
 #[test]
 #[cfg(not(target_os = "redox"))]
 fn test_readv() {
-    let s:String = thread_rng()
+    let s: String = thread_rng()
         .sample_iter(&Alphanumeric)
         .map(char::from)
         .take(128)
@@ -71,40 +72,36 @@
     let mut allocated = 0;
     while allocated < to_write.len() {
         let left = to_write.len() - allocated;
-        let vec_len = if left <= 64 { left } else { thread_rng().gen_range(64..cmp::min(256, left)) };
+        let vec_len = if left <= 64 {
+            left
+        } else {
+            thread_rng().gen_range(64..cmp::min(256, left))
+        };
         let v: Vec<u8> = iter::repeat(0u8).take(vec_len).collect();
         storage.push(v);
         allocated += vec_len;
     }
     let mut iovecs = Vec::with_capacity(storage.len());
     for v in &mut storage {
-        iovecs.push(IoVec::from_mut_slice(&mut v[..]));
+        iovecs.push(IoSliceMut::new(&mut v[..]));
     }
-    let pipe_res = pipe();
-    assert!(pipe_res.is_ok());
-    let (reader, writer) = pipe_res.ok().unwrap();
+    let (reader, writer) = pipe().expect("couldn't create pipe");
     // Blocking io, should write all data.
-    let write_res = write(writer, &to_write);
-    // Successful write
-    assert!(write_res.is_ok());
-    let read_res = readv(reader, &mut iovecs[..]);
-    assert!(read_res.is_ok());
-    let read = read_res.ok().unwrap();
+    write(writer, &to_write).expect("write failed");
+    let read = readv(reader, &mut iovecs[..]).expect("read failed");
     // Check whether we've read all data
     assert_eq!(to_write.len(), read);
     // Cccumulate data from iovecs
     let mut read_buf = Vec::with_capacity(to_write.len());
     for iovec in &iovecs {
-        read_buf.extend(iovec.as_slice().iter().cloned());
+        read_buf.extend(iovec.iter().cloned());
     }
     // Check whether iovecs contain all written data
     assert_eq!(read_buf.len(), to_write.len());
     // Check equality of written and read data
     assert_eq!(&read_buf, &to_write);
-    let close_res = close(reader);
-    assert!(close_res.is_ok());
-    let close_res = close(writer);
-    assert!(close_res.is_ok());
+    close(reader).expect("couldn't close reader");
+    close(writer).expect("couldn't close writer");
 }
 
 #[test]
@@ -113,12 +110,12 @@
     use std::io::Read;
 
     let mut file = tempfile().unwrap();
-    let buf = [1u8;8];
+    let buf = [1u8; 8];
     assert_eq!(Ok(8), pwrite(file.as_raw_fd(), &buf, 8));
     let mut file_content = Vec::new();
     file.read_to_end(&mut file_content).unwrap();
-    let mut expected = vec![0u8;8];
-    expected.extend(vec![1;8]);
+    let mut expected = vec![0u8; 8];
+    expected.extend(vec![1; 8]);
     assert_eq!(file_content, expected);
 }
 
@@ -129,37 +126,47 @@
     let tempdir = tempdir().unwrap();
 
     let path = tempdir.path().join("pread_test_file");
-    let mut file = OpenOptions::new().write(true).read(true).create(true)
-                                    .truncate(true).open(path).unwrap();
+    let mut file = OpenOptions::new()
+        .write(true)
+        .read(true)
+        .create(true)
+        .truncate(true)
+        .open(path)
+        .unwrap();
     let file_content: Vec<u8> = (0..64).collect();
     file.write_all(&file_content).unwrap();
 
-    let mut buf = [0u8;16];
+    let mut buf = [0u8; 16];
     assert_eq!(Ok(16), pread(file.as_raw_fd(), &mut buf, 16));
     let expected: Vec<_> = (16..32).collect();
     assert_eq!(&buf[..], &expected[..]);
 }
 
 #[test]
-#[cfg(not(target_os = "redox"))]
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
 fn test_pwritev() {
     use std::io::Read;
 
     let to_write: Vec<u8> = (0..128).collect();
-    let expected: Vec<u8> = [vec![0;100], to_write.clone()].concat();
+    let expected: Vec<u8> = [vec![0; 100], to_write.clone()].concat();
 
     let iovecs = [
-        IoVec::from_slice(&to_write[0..17]),
-        IoVec::from_slice(&to_write[17..64]),
-        IoVec::from_slice(&to_write[64..128]),
+        IoSlice::new(&to_write[0..17]),
+        IoSlice::new(&to_write[17..64]),
+        IoSlice::new(&to_write[64..128]),
     ];
 
     let tempdir = tempdir().unwrap();
 
     // pwritev them into a temporary file
     let path = tempdir.path().join("pwritev_test_file");
-    let mut file = OpenOptions::new().write(true).read(true).create(true)
-                                    .truncate(true).open(path).unwrap();
+    let mut file = OpenOptions::new()
+        .write(true)
+        .read(true)
+        .create(true)
+        .truncate(true)
+        .open(path)
+        .unwrap();
 
     let written = pwritev(file.as_raw_fd(), &iovecs, 100).ok().unwrap();
     assert_eq!(written, to_write.len());
@@ -171,7 +178,7 @@
 }
 
 #[test]
-#[cfg(not(target_os = "redox"))]
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
 fn test_preadv() {
     use std::io::Write;
 
@@ -182,21 +189,24 @@
 
     let path = tempdir.path().join("preadv_test_file");
 
-    let mut file = OpenOptions::new().read(true).write(true).create(true)
-                                    .truncate(true).open(path).unwrap();
+    let mut file = OpenOptions::new()
+        .read(true)
+        .write(true)
+        .create(true)
+        .truncate(true)
+        .open(path)
+        .unwrap();
     file.write_all(&to_write).unwrap();
 
-    let mut buffers: Vec<Vec<u8>> = vec![
-        vec![0; 24],
-        vec![0; 1],
-        vec![0; 75],
-    ];
+    let mut buffers: Vec<Vec<u8>> = vec![vec![0; 24], vec![0; 1], vec![0; 75]];
 
     {
         // Borrow the buffers into IoVecs and preadv into them
-        let iovecs: Vec<_> = buffers.iter_mut().map(
-            |buf| IoVec::from_mut_slice(&mut buf[..])).collect();
-        assert_eq!(Ok(100), preadv(file.as_raw_fd(), &iovecs, 100));
+        let mut iovecs: Vec<_> = buffers
+            .iter_mut()
+            .map(|buf| IoSliceMut::new(&mut buf[..]))
+            .collect();
+        assert_eq!(Ok(100), preadv(file.as_raw_fd(), &mut iovecs, 100));
     }
 
     let all = buffers.concat();
@@ -204,14 +214,15 @@
 }
 
 #[test]
-#[cfg(target_os = "linux")]
+#[cfg(all(target_os = "linux", not(target_env = "uclibc")))]
+// uclibc doesn't implement process_vm_readv
 // qemu-user doesn't implement process_vm_readv/writev on most arches
 #[cfg_attr(qemu, ignore)]
 fn test_process_vm_readv() {
-    use nix::unistd::ForkResult::*;
+    use crate::*;
     use nix::sys::signal::*;
     use nix::sys::wait::*;
-    use crate::*;
+    use nix::unistd::ForkResult::*;
 
     require_capability!("test_process_vm_readv", CAP_SYS_PTRACE);
     let _m = crate::FORK_MTX.lock();
@@ -221,7 +232,7 @@
     let mut vector = vec![1u8, 2, 3, 4, 5];
 
     let (r, w) = pipe().unwrap();
-    match unsafe{fork()}.expect("Error: Fork Failed") {
+    match unsafe { fork() }.expect("Error: Fork Failed") {
         Parent { child } => {
             close(w).unwrap();
             // wait for child
@@ -232,16 +243,18 @@
             let remote_iov = RemoteIoVec { base: ptr, len: 5 };
             let mut buf = vec![0u8; 5];
 
-            let ret = process_vm_readv(child,
-                                       &[IoVec::from_mut_slice(&mut buf)],
-                                       &[remote_iov]);
+            let ret = process_vm_readv(
+                child,
+                &mut [IoSliceMut::new(&mut buf)],
+                &[remote_iov],
+            );
 
             kill(child, SIGTERM).unwrap();
             waitpid(child, None).unwrap();
 
             assert_eq!(Ok(5), ret);
             assert_eq!(20u8, buf.iter().sum());
-        },
+        }
         Child => {
             let _ = close(r);
             for i in &mut vector {
@@ -249,7 +262,9 @@
             }
             let _ = write(w, b"\0");
             let _ = close(w);
-            loop { let _ = pause(); }
-        },
+            loop {
+                pause();
+            }
+        }
     }
 }
diff --git a/test/sys/test_wait.rs b/test/sys/test_wait.rs
index afe4f42..d472f1e 100644
--- a/test/sys/test_wait.rs
+++ b/test/sys/test_wait.rs
@@ -1,25 +1,55 @@
+use libc::_exit;
 use nix::errno::Errno;
-use nix::unistd::*;
-use nix::unistd::ForkResult::*;
 use nix::sys::signal::*;
 use nix::sys::wait::*;
-use libc::_exit;
+use nix::unistd::ForkResult::*;
+use nix::unistd::*;
 
 #[test]
-#[cfg(not(target_os = "redox"))]
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
 fn test_wait_signal() {
     let _m = crate::FORK_MTX.lock();
 
     // Safe: The child only calls `pause` and/or `_exit`, which are async-signal-safe.
-    match unsafe{fork()}.expect("Error: Fork Failed") {
-      Child => {
-          pause();
-          unsafe { _exit(123) }
-      },
-      Parent { child } => {
-          kill(child, Some(SIGKILL)).expect("Error: Kill Failed");
-          assert_eq!(waitpid(child, None), Ok(WaitStatus::Signaled(child, SIGKILL, false)));
-      },
+    match unsafe { fork() }.expect("Error: Fork Failed") {
+        Child => {
+            pause();
+            unsafe { _exit(123) }
+        }
+        Parent { child } => {
+            kill(child, Some(SIGKILL)).expect("Error: Kill Failed");
+            assert_eq!(
+                waitpid(child, None),
+                Ok(WaitStatus::Signaled(child, SIGKILL, false))
+            );
+        }
+    }
+}
+
+#[test]
+#[cfg(any(
+    target_os = "android",
+    target_os = "freebsd",
+    //target_os = "haiku",
+    all(target_os = "linux", not(target_env = "uclibc")),
+))]
+#[cfg(not(any(target_arch = "mips", target_arch = "mips64")))]
+fn test_waitid_signal() {
+    let _m = crate::FORK_MTX.lock();
+
+    // Safe: The child only calls `pause` and/or `_exit`, which are async-signal-safe.
+    match unsafe { fork() }.expect("Error: Fork Failed") {
+        Child => {
+            pause();
+            unsafe { _exit(123) }
+        }
+        Parent { child } => {
+            kill(child, Some(SIGKILL)).expect("Error: Kill Failed");
+            assert_eq!(
+                waitid(Id::Pid(child), WaitPidFlag::WEXITED),
+                Ok(WaitStatus::Signaled(child, SIGKILL, false)),
+            );
+        }
     }
 }
 
@@ -28,19 +58,53 @@
     let _m = crate::FORK_MTX.lock();
 
     // Safe: Child only calls `_exit`, which is async-signal-safe.
-    match unsafe{fork()}.expect("Error: Fork Failed") {
-      Child => unsafe { _exit(12); },
-      Parent { child } => {
-          assert_eq!(waitpid(child, None), Ok(WaitStatus::Exited(child, 12)));
-      },
+    match unsafe { fork() }.expect("Error: Fork Failed") {
+        Child => unsafe {
+            _exit(12);
+        },
+        Parent { child } => {
+            assert_eq!(waitpid(child, None), Ok(WaitStatus::Exited(child, 12)));
+        }
+    }
+}
+
+#[cfg(not(target_os = "haiku"))]
+#[test]
+#[cfg(any(
+    target_os = "android",
+    target_os = "freebsd",
+    target_os = "haiku",
+    all(target_os = "linux", not(target_env = "uclibc")),
+))]
+#[cfg(not(any(target_arch = "mips", target_arch = "mips64")))]
+fn test_waitid_exit() {
+    let _m = crate::FORK_MTX.lock();
+
+    // Safe: Child only calls `_exit`, which is async-signal-safe.
+    match unsafe { fork() }.expect("Error: Fork Failed") {
+        Child => unsafe {
+            _exit(12);
+        },
+        Parent { child } => {
+            assert_eq!(
+                waitid(Id::Pid(child), WaitPidFlag::WEXITED),
+                Ok(WaitStatus::Exited(child, 12)),
+            );
+        }
     }
 }
 
 #[test]
 fn test_waitstatus_from_raw() {
     let pid = Pid::from_raw(1);
-    assert_eq!(WaitStatus::from_raw(pid, 0x0002), Ok(WaitStatus::Signaled(pid, Signal::SIGINT, false)));
-    assert_eq!(WaitStatus::from_raw(pid, 0x0200), Ok(WaitStatus::Exited(pid, 2)));
+    assert_eq!(
+        WaitStatus::from_raw(pid, 0x0002),
+        Ok(WaitStatus::Signaled(pid, Signal::SIGINT, false))
+    );
+    assert_eq!(
+        WaitStatus::from_raw(pid, 0x0200),
+        Ok(WaitStatus::Exited(pid, 2))
+    );
     assert_eq!(WaitStatus::from_raw(pid, 0x7f7f), Err(Errno::EINVAL));
 }
 
@@ -48,7 +112,7 @@
 fn test_waitstatus_pid() {
     let _m = crate::FORK_MTX.lock();
 
-    match unsafe{fork()}.unwrap() {
+    match unsafe { fork() }.unwrap() {
         Child => unsafe { _exit(0) },
         Parent { child } => {
             let status = waitpid(child, None).unwrap();
@@ -57,17 +121,36 @@
     }
 }
 
+#[test]
+#[cfg(any(
+    target_os = "android",
+    target_os = "freebsd",
+    target_os = "haiku",
+    all(target_os = "linux", not(target_env = "uclibc")),
+))]
+fn test_waitid_pid() {
+    let _m = crate::FORK_MTX.lock();
+
+    match unsafe { fork() }.unwrap() {
+        Child => unsafe { _exit(0) },
+        Parent { child } => {
+            let status = waitid(Id::Pid(child), WaitPidFlag::WEXITED).unwrap();
+            assert_eq!(status.pid(), Some(child));
+        }
+    }
+}
+
 #[cfg(any(target_os = "linux", target_os = "android"))]
 // FIXME: qemu-user doesn't implement ptrace on most arches
 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
 mod ptrace {
-    use nix::sys::ptrace::{self, Options, Event};
+    use crate::*;
+    use libc::_exit;
+    use nix::sys::ptrace::{self, Event, Options};
     use nix::sys::signal::*;
     use nix::sys::wait::*;
-    use nix::unistd::*;
     use nix::unistd::ForkResult::*;
-    use libc::_exit;
-    use crate::*;
+    use nix::unistd::*;
 
     fn ptrace_child() -> ! {
         ptrace::traceme().unwrap();
@@ -77,31 +160,98 @@
         unsafe { _exit(0) }
     }
 
-    fn ptrace_parent(child: Pid) {
+    fn ptrace_wait_parent(child: Pid) {
         // Wait for the raised SIGTRAP
-        assert_eq!(waitpid(child, None), Ok(WaitStatus::Stopped(child, SIGTRAP)));
+        assert_eq!(
+            waitpid(child, None),
+            Ok(WaitStatus::Stopped(child, SIGTRAP))
+        );
         // We want to test a syscall stop and a PTRACE_EVENT stop
-        assert!(ptrace::setoptions(child, Options::PTRACE_O_TRACESYSGOOD | Options::PTRACE_O_TRACEEXIT).is_ok());
+        ptrace::setoptions(
+            child,
+            Options::PTRACE_O_TRACESYSGOOD | Options::PTRACE_O_TRACEEXIT,
+        )
+        .expect("setoptions failed");
 
         // First, stop on the next system call, which will be exit()
-        assert!(ptrace::syscall(child, None).is_ok());
+        ptrace::syscall(child, None).expect("syscall failed");
         assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child)));
         // Then get the ptrace event for the process exiting
-        assert!(ptrace::cont(child, None).is_ok());
-        assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceEvent(child, SIGTRAP, Event::PTRACE_EVENT_EXIT as i32)));
+        ptrace::cont(child, None).expect("cont failed");
+        assert_eq!(
+            waitpid(child, None),
+            Ok(WaitStatus::PtraceEvent(
+                child,
+                SIGTRAP,
+                Event::PTRACE_EVENT_EXIT as i32
+            ))
+        );
         // Finally get the normal wait() result, now that the process has exited
-        assert!(ptrace::cont(child, None).is_ok());
+        ptrace::cont(child, None).expect("cont failed");
         assert_eq!(waitpid(child, None), Ok(WaitStatus::Exited(child, 0)));
     }
 
+    #[cfg(not(target_env = "uclibc"))]
+    fn ptrace_waitid_parent(child: Pid) {
+        // Wait for the raised SIGTRAP
+        //
+        // Unlike waitpid(), waitid() can distinguish trap events from regular
+        // stop events, so unlike ptrace_wait_parent(), we get a PtraceEvent here
+        assert_eq!(
+            waitid(Id::Pid(child), WaitPidFlag::WEXITED),
+            Ok(WaitStatus::PtraceEvent(child, SIGTRAP, 0)),
+        );
+        // We want to test a syscall stop and a PTRACE_EVENT stop
+        ptrace::setoptions(
+            child,
+            Options::PTRACE_O_TRACESYSGOOD | Options::PTRACE_O_TRACEEXIT,
+        )
+        .expect("setopts failed");
+
+        // First, stop on the next system call, which will be exit()
+        ptrace::syscall(child, None).expect("syscall failed");
+        assert_eq!(
+            waitid(Id::Pid(child), WaitPidFlag::WEXITED),
+            Ok(WaitStatus::PtraceSyscall(child)),
+        );
+        // Then get the ptrace event for the process exiting
+        ptrace::cont(child, None).expect("cont failed");
+        assert_eq!(
+            waitid(Id::Pid(child), WaitPidFlag::WEXITED),
+            Ok(WaitStatus::PtraceEvent(
+                child,
+                SIGTRAP,
+                Event::PTRACE_EVENT_EXIT as i32
+            )),
+        );
+        // Finally get the normal wait() result, now that the process has exited
+        ptrace::cont(child, None).expect("cont failed");
+        assert_eq!(
+            waitid(Id::Pid(child), WaitPidFlag::WEXITED),
+            Ok(WaitStatus::Exited(child, 0)),
+        );
+    }
+
     #[test]
     fn test_wait_ptrace() {
         require_capability!("test_wait_ptrace", CAP_SYS_PTRACE);
         let _m = crate::FORK_MTX.lock();
 
-        match unsafe{fork()}.expect("Error: Fork Failed") {
+        match unsafe { fork() }.expect("Error: Fork Failed") {
             Child => ptrace_child(),
-            Parent { child } => ptrace_parent(child),
+            Parent { child } => ptrace_wait_parent(child),
+        }
+    }
+
+    #[test]
+    #[cfg(not(target_env = "uclibc"))]
+    fn test_waitid_ptrace() {
+        require_capability!("test_waitid_ptrace", CAP_SYS_PTRACE);
+        let _m = crate::FORK_MTX.lock();
+
+        match unsafe { fork() }.expect("Error: Fork Failed") {
+            Child => ptrace_child(),
+            Parent { child } => ptrace_waitid_parent(child),
         }
     }
 }
diff --git a/test/test.rs b/test/test.rs
index aade937..6b42aad 100644
--- a/test/test.rs
+++ b/test/test.rs
@@ -1,6 +1,6 @@
 #[macro_use]
 extern crate cfg_if;
-#[cfg_attr(not(target_os = "redox"), macro_use)]
+#[cfg_attr(not(any(target_os = "redox", target_os = "haiku")), macro_use)]
 extern crate nix;
 #[macro_use]
 extern crate lazy_static;
@@ -10,45 +10,67 @@
 #[cfg(not(target_os = "redox"))]
 mod test_dir;
 mod test_fcntl;
-#[cfg(any(target_os = "android",
-          target_os = "linux"))]
+#[cfg(any(target_os = "android", target_os = "linux"))]
 mod test_kmod;
-#[cfg(target_os = "freebsd")]
-mod test_nmount;
-#[cfg(any(target_os = "dragonfly",
-          target_os = "freebsd",
-          target_os = "fushsia",
-          target_os = "linux",
-          target_os = "netbsd"))]
+#[cfg(any(
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "fushsia",
+    target_os = "linux",
+    target_os = "netbsd"
+))]
 mod test_mq;
 #[cfg(not(target_os = "redox"))]
 mod test_net;
 mod test_nix_path;
-mod test_resource;
+#[cfg(target_os = "freebsd")]
+mod test_nmount;
 mod test_poll;
-#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
+#[cfg(not(any(
+    target_os = "redox",
+    target_os = "fuchsia",
+    target_os = "haiku"
+)))]
 mod test_pty;
-#[cfg(any(target_os = "android",
-          target_os = "linux"))]
+mod test_resource;
+#[cfg(any(
+    target_os = "android",
+    target_os = "dragonfly",
+    all(target_os = "freebsd", fbsd14),
+    target_os = "linux"
+))]
 mod test_sched;
-#[cfg(any(target_os = "android",
-          target_os = "freebsd",
-          target_os = "ios",
-          target_os = "linux",
-          target_os = "macos"))]
+#[cfg(any(
+    target_os = "android",
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "ios",
+    target_os = "linux",
+    target_os = "macos"
+))]
 mod test_sendfile;
 mod test_stat;
 mod test_time;
+#[cfg(all(
+    any(
+        target_os = "freebsd",
+        target_os = "illumos",
+        target_os = "linux",
+        target_os = "netbsd"
+    ),
+    feature = "time",
+    feature = "signal"
+))]
+mod test_timer;
 mod test_unistd;
 
+use nix::unistd::{chdir, getcwd, read};
+use parking_lot::{Mutex, RwLock, RwLockWriteGuard};
 use std::os::unix::io::RawFd;
 use std::path::PathBuf;
-use parking_lot::{Mutex, RwLock, RwLockWriteGuard};
-use nix::unistd::{chdir, getcwd, read};
-
 
 /// Helper function analogous to `std::io::Read::read_exact`, but for `RawFD`s
-fn read_exact(f: RawFd, buf: &mut  [u8]) {
+fn read_exact(f: RawFd, buf: &mut [u8]) {
     let mut len = 0;
     while len < buf.len() {
         // get_mut would be better than split_at_mut, but it requires nightly
@@ -79,13 +101,13 @@
 /// RAII object that restores a test's original directory on drop
 struct DirRestore<'a> {
     d: PathBuf,
-    _g: RwLockWriteGuard<'a, ()>
+    _g: RwLockWriteGuard<'a, ()>,
 }
 
 impl<'a> DirRestore<'a> {
     fn new() -> Self {
         let guard = crate::CWD_LOCK.write();
-        DirRestore{
+        DirRestore {
             _g: guard,
             d: getcwd().unwrap(),
         }
diff --git a/test/test_dir.rs b/test/test_dir.rs
index 2940b6e..2af4aa5 100644
--- a/test/test_dir.rs
+++ b/test/test_dir.rs
@@ -4,7 +4,6 @@
 use std::fs::File;
 use tempfile::tempdir;
 
-
 #[cfg(test)]
 fn flags() -> OFlag {
     #[cfg(target_os = "illumos")]
@@ -17,11 +16,11 @@
 }
 
 #[test]
-#[allow(clippy::unnecessary_sort_by)]   // False positive
+#[allow(clippy::unnecessary_sort_by)] // False positive
 fn read() {
     let tmp = tempdir().unwrap();
-    File::create(&tmp.path().join("foo")).unwrap();
-    ::std::os::unix::fs::symlink("foo", tmp.path().join("bar")).unwrap();
+    File::create(tmp.path().join("foo")).unwrap();
+    std::os::unix::fs::symlink("foo", tmp.path().join("bar")).unwrap();
     let mut dir = Dir::open(tmp.path(), flags(), Mode::empty()).unwrap();
     let mut entries: Vec<_> = dir.iter().map(|e| e.unwrap()).collect();
     entries.sort_by(|a, b| a.file_name().cmp(b.file_name()));
@@ -43,13 +42,23 @@
 fn rewind() {
     let tmp = tempdir().unwrap();
     let mut dir = Dir::open(tmp.path(), flags(), Mode::empty()).unwrap();
-    let entries1: Vec<_> = dir.iter().map(|e| e.unwrap().file_name().to_owned()).collect();
-    let entries2: Vec<_> = dir.iter().map(|e| e.unwrap().file_name().to_owned()).collect();
-    let entries3: Vec<_> = dir.into_iter().map(|e| e.unwrap().file_name().to_owned()).collect();
+    let entries1: Vec<_> = dir
+        .iter()
+        .map(|e| e.unwrap().file_name().to_owned())
+        .collect();
+    let entries2: Vec<_> = dir
+        .iter()
+        .map(|e| e.unwrap().file_name().to_owned())
+        .collect();
+    let entries3: Vec<_> = dir
+        .into_iter()
+        .map(|e| e.unwrap().file_name().to_owned())
+        .collect();
     assert_eq!(entries1, entries2);
     assert_eq!(entries2, entries3);
 }
 
+#[cfg(not(target_os = "haiku"))]
 #[test]
 fn ebadf() {
     assert_eq!(Dir::from_fd(-1).unwrap_err(), nix::Error::EBADF);
diff --git a/test/test_fcntl.rs b/test/test_fcntl.rs
index db2acfb..e51044a 100644
--- a/test/test_fcntl.rs
+++ b/test/test_fcntl.rs
@@ -1,7 +1,7 @@
 #[cfg(not(target_os = "redox"))]
 use nix::errno::*;
 #[cfg(not(target_os = "redox"))]
-use nix::fcntl::{open, OFlag, readlink};
+use nix::fcntl::{open, readlink, OFlag};
 #[cfg(not(target_os = "redox"))]
 use nix::fcntl::{openat, readlinkat, renameat};
 #[cfg(all(
@@ -14,34 +14,40 @@
         target_arch = "s390x"
     )
 ))]
-use nix::fcntl::{RenameFlags, renameat2};
+use nix::fcntl::{renameat2, RenameFlags};
 #[cfg(not(target_os = "redox"))]
 use nix::sys::stat::Mode;
 #[cfg(not(target_os = "redox"))]
 use nix::unistd::{close, read};
 #[cfg(not(target_os = "redox"))]
-use tempfile::{self, NamedTempFile};
-#[cfg(not(target_os = "redox"))]
 use std::fs::File;
 #[cfg(not(target_os = "redox"))]
 use std::io::prelude::*;
 #[cfg(not(target_os = "redox"))]
 use std::os::unix::fs;
+#[cfg(not(target_os = "redox"))]
+use tempfile::{self, NamedTempFile};
 
 #[test]
 #[cfg(not(target_os = "redox"))]
+// QEMU does not handle openat well enough to satisfy this test
+// https://gitlab.com/qemu-project/qemu/-/issues/829
+#[cfg_attr(qemu, ignore)]
 fn test_openat() {
     const CONTENTS: &[u8] = b"abcd";
     let mut tmp = NamedTempFile::new().unwrap();
     tmp.write_all(CONTENTS).unwrap();
 
-    let dirfd = open(tmp.path().parent().unwrap(),
-                     OFlag::empty(),
-                     Mode::empty()).unwrap();
-    let fd = openat(dirfd,
-                    tmp.path().file_name().unwrap(),
-                    OFlag::O_RDONLY,
-                    Mode::empty()).unwrap();
+    let dirfd =
+        open(tmp.path().parent().unwrap(), OFlag::empty(), Mode::empty())
+            .unwrap();
+    let fd = openat(
+        dirfd,
+        tmp.path().file_name().unwrap(),
+        OFlag::O_RDONLY,
+        Mode::empty(),
+    )
+    .unwrap();
 
     let mut buf = [0u8; 1024];
     assert_eq!(4, read(fd, &mut buf).unwrap());
@@ -55,14 +61,18 @@
 #[cfg(not(target_os = "redox"))]
 fn test_renameat() {
     let old_dir = tempfile::tempdir().unwrap();
-    let old_dirfd = open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
+    let old_dirfd =
+        open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
     let old_path = old_dir.path().join("old");
-    File::create(&old_path).unwrap();
+    File::create(old_path).unwrap();
     let new_dir = tempfile::tempdir().unwrap();
-    let new_dirfd = open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
+    let new_dirfd =
+        open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
     renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap();
-    assert_eq!(renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap_err(),
-               Errno::ENOENT);
+    assert_eq!(
+        renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap_err(),
+        Errno::ENOENT
+    );
     close(old_dirfd).unwrap();
     close(new_dirfd).unwrap();
     assert!(new_dir.path().join("new").exists());
@@ -81,11 +91,13 @@
 ))]
 fn test_renameat2_behaves_like_renameat_with_no_flags() {
     let old_dir = tempfile::tempdir().unwrap();
-    let old_dirfd = open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
+    let old_dirfd =
+        open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
     let old_path = old_dir.path().join("old");
-    File::create(&old_path).unwrap();
+    File::create(old_path).unwrap();
     let new_dir = tempfile::tempdir().unwrap();
-    let new_dirfd = open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
+    let new_dirfd =
+        open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
     renameat2(
         Some(old_dirfd),
         "old",
@@ -123,14 +135,16 @@
 ))]
 fn test_renameat2_exchange() {
     let old_dir = tempfile::tempdir().unwrap();
-    let old_dirfd = open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
+    let old_dirfd =
+        open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
     let old_path = old_dir.path().join("old");
     {
         let mut old_f = File::create(&old_path).unwrap();
         old_f.write_all(b"old").unwrap();
     }
     let new_dir = tempfile::tempdir().unwrap();
-    let new_dirfd = open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
+    let new_dirfd =
+        open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
     let new_path = new_dir.path().join("new");
     {
         let mut new_f = File::create(&new_path).unwrap();
@@ -169,13 +183,15 @@
 ))]
 fn test_renameat2_noreplace() {
     let old_dir = tempfile::tempdir().unwrap();
-    let old_dirfd = open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
+    let old_dirfd =
+        open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
     let old_path = old_dir.path().join("old");
-    File::create(&old_path).unwrap();
+    File::create(old_path).unwrap();
     let new_dir = tempfile::tempdir().unwrap();
-    let new_dirfd = open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
+    let new_dirfd =
+        open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
     let new_path = new_dir.path().join("new");
-    File::create(&new_path).unwrap();
+    File::create(new_path).unwrap();
     assert_eq!(
         renameat2(
             Some(old_dirfd),
@@ -193,7 +209,6 @@
     assert!(old_dir.path().join("old").exists());
 }
 
-
 #[test]
 #[cfg(not(target_os = "redox"))]
 fn test_readlink() {
@@ -201,26 +216,25 @@
     let src = tempdir.path().join("a");
     let dst = tempdir.path().join("b");
     println!("a: {:?}, b: {:?}", &src, &dst);
-    fs::symlink(&src.as_path(), &dst.as_path()).unwrap();
-    let dirfd = open(tempdir.path(),
-                     OFlag::empty(),
-                     Mode::empty()).unwrap();
+    fs::symlink(src.as_path(), dst.as_path()).unwrap();
+    let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
     let expected_dir = src.to_str().unwrap();
 
     assert_eq!(readlink(&dst).unwrap().to_str().unwrap(), expected_dir);
-    assert_eq!(readlinkat(dirfd, "b").unwrap().to_str().unwrap(), expected_dir);
-
+    assert_eq!(
+        readlinkat(dirfd, "b").unwrap().to_str().unwrap(),
+        expected_dir
+    );
 }
 
 #[cfg(any(target_os = "linux", target_os = "android"))]
 mod linux_android {
-    use std::io::prelude::*;
-    use std::io::SeekFrom;
-    use std::os::unix::prelude::*;
     use libc::loff_t;
+    use std::io::prelude::*;
+    use std::io::IoSlice;
+    use std::os::unix::prelude::*;
 
     use nix::fcntl::*;
-    use nix::sys::uio::IoVec;
     use nix::unistd::{close, pipe, read, write};
 
     use tempfile::tempfile;
@@ -258,7 +272,7 @@
         .unwrap();
 
         let mut res: String = String::new();
-        tmp2.seek(SeekFrom::Start(0)).unwrap();
+        tmp2.rewind().unwrap();
         tmp2.read_to_string(&mut res).unwrap();
 
         assert_eq!(res, String::from("bar"));
@@ -273,8 +287,15 @@
 
         let (rd, wr) = pipe().unwrap();
         let mut offset: loff_t = 5;
-        let res = splice(tmp.as_raw_fd(), Some(&mut offset),
-            wr, None, 2, SpliceFFlags::empty()).unwrap();
+        let res = splice(
+            tmp.as_raw_fd(),
+            Some(&mut offset),
+            wr,
+            None,
+            2,
+            SpliceFFlags::empty(),
+        )
+        .unwrap();
 
         assert_eq!(2, res);
 
@@ -319,10 +340,7 @@
 
         let buf1 = b"abcdef";
         let buf2 = b"defghi";
-        let iovecs = vec![
-            IoVec::from_slice(&buf1[0..3]),
-            IoVec::from_slice(&buf2[0..3])
-        ];
+        let iovecs = vec![IoSlice::new(&buf1[0..3]), IoSlice::new(&buf2[0..3])];
 
         let res = vmsplice(wr, &iovecs[..], SpliceFFlags::empty()).unwrap();
 
@@ -357,6 +375,7 @@
 
     #[test]
     #[cfg(all(target_os = "linux", not(target_env = "musl")))]
+    #[cfg_attr(target_env = "uclibc", ignore)] // uclibc doesn't support OFD locks, but the test should still compile
     fn test_ofd_write_lock() {
         use nix::sys::stat::fstat;
         use std::mem;
@@ -374,7 +393,7 @@
         let inode = fstat(fd).expect("fstat failed").st_ino as usize;
 
         let mut flock: libc::flock = unsafe {
-            mem::zeroed()  // required for Linux/mips
+            mem::zeroed() // required for Linux/mips
         };
         flock.l_type = libc::F_WRLCK as libc::c_short;
         flock.l_whence = libc::SEEK_SET as libc::c_short;
@@ -394,6 +413,7 @@
 
     #[test]
     #[cfg(all(target_os = "linux", not(target_env = "musl")))]
+    #[cfg_attr(target_env = "uclibc", ignore)] // uclibc doesn't support OFD locks, but the test should still compile
     fn test_ofd_read_lock() {
         use nix::sys::stat::fstat;
         use std::mem;
@@ -411,7 +431,7 @@
         let inode = fstat(fd).expect("fstat failed").st_ino as usize;
 
         let mut flock: libc::flock = unsafe {
-            mem::zeroed()  // required for Linux/mips
+            mem::zeroed() // required for Linux/mips
         };
         flock.l_type = libc::F_RDLCK as libc::c_short;
         flock.l_whence = libc::SEEK_SET as libc::c_short;
@@ -431,10 +451,7 @@
 
     #[cfg(all(target_os = "linux", not(target_env = "musl")))]
     fn lock_info(inode: usize) -> Option<(String, String)> {
-        use std::{
-            fs::File,
-            io::BufReader
-        };
+        use std::{fs::File, io::BufReader};
 
         let file = File::open("/proc/locks").expect("open /proc/locks failed");
         let buf = BufReader::new(file);
@@ -454,51 +471,63 @@
     }
 }
 
-#[cfg(any(target_os = "linux",
-          target_os = "android",
-          target_os = "emscripten",
-          target_os = "fuchsia",
-          any(target_os = "wasi", target_env = "wasi"),
-          target_env = "uclibc",
-          target_os = "freebsd"))]
+#[cfg(any(
+    target_os = "linux",
+    target_os = "android",
+    target_os = "emscripten",
+    target_os = "fuchsia",
+    target_os = "wasi",
+    target_env = "uclibc",
+    target_os = "freebsd"
+))]
 mod test_posix_fadvise {
 
-    use tempfile::NamedTempFile;
-    use std::os::unix::io::{RawFd, AsRawFd};
     use nix::errno::Errno;
     use nix::fcntl::*;
     use nix::unistd::pipe;
+    use std::os::unix::io::{AsRawFd, RawFd};
+    use tempfile::NamedTempFile;
 
     #[test]
     fn test_success() {
         let tmp = NamedTempFile::new().unwrap();
         let fd = tmp.as_raw_fd();
-        let res = posix_fadvise(fd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED);
-
-        assert!(res.is_ok());
+        posix_fadvise(fd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED)
+            .expect("posix_fadvise failed");
     }
 
     #[test]
     fn test_errno() {
         let (rd, _wr) = pipe().unwrap();
-        let res = posix_fadvise(rd as RawFd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED);
+        let res = posix_fadvise(
+            rd as RawFd,
+            0,
+            100,
+            PosixFadviseAdvice::POSIX_FADV_WILLNEED,
+        );
         assert_eq!(res, Err(Errno::ESPIPE));
     }
 }
 
-#[cfg(any(target_os = "linux",
-          target_os = "android",
-          target_os = "emscripten",
-          target_os = "fuchsia",
-          any(target_os = "wasi", target_env = "wasi"),
-          target_os = "freebsd"))]
+#[cfg(any(
+    target_os = "linux",
+    target_os = "android",
+    target_os = "dragonfly",
+    target_os = "emscripten",
+    target_os = "fuchsia",
+    target_os = "wasi",
+    target_os = "freebsd"
+))]
 mod test_posix_fallocate {
 
-    use tempfile::NamedTempFile;
-    use std::{io::Read, os::unix::io::{RawFd, AsRawFd}};
     use nix::errno::Errno;
     use nix::fcntl::*;
     use nix::unistd::pipe;
+    use std::{
+        io::Read,
+        os::unix::io::{AsRawFd, RawFd},
+    };
+    use tempfile::NamedTempFile;
 
     #[test]
     fn success() {
@@ -530,11 +559,7 @@
         let err = posix_fallocate(rd as RawFd, 0, 100).unwrap_err();
         match err {
             Errno::EINVAL | Errno::ENODEV | Errno::ESPIPE | Errno::EBADF => (),
-            errno =>
-                panic!(
-                    "unexpected errno {}",
-                    errno,
-                ),
+            errno => panic!("unexpected errno {}", errno,),
         }
     }
 }
diff --git a/test/test_kmod/mod.rs b/test/test_kmod/mod.rs
index 8eef538..6f9aaa8 100644
--- a/test/test_kmod/mod.rs
+++ b/test/test_kmod/mod.rs
@@ -1,22 +1,25 @@
+use crate::*;
 use std::fs::copy;
 use std::path::PathBuf;
 use std::process::Command;
 use tempfile::{tempdir, TempDir};
-use crate::*;
 
 fn compile_kernel_module() -> (PathBuf, String, TempDir) {
     let _m = crate::FORK_MTX.lock();
 
-    let tmp_dir = tempdir().expect("unable to create temporary build directory");
+    let tmp_dir =
+        tempdir().expect("unable to create temporary build directory");
 
     copy(
         "test/test_kmod/hello_mod/hello.c",
-        &tmp_dir.path().join("hello.c"),
-    ).expect("unable to copy hello.c to temporary build directory");
+        tmp_dir.path().join("hello.c"),
+    )
+    .expect("unable to copy hello.c to temporary build directory");
     copy(
         "test/test_kmod/hello_mod/Makefile",
-        &tmp_dir.path().join("Makefile"),
-    ).expect("unable to copy Makefile to temporary build directory");
+        tmp_dir.path().join("Makefile"),
+    )
+    .expect("unable to copy Makefile to temporary build directory");
 
     let status = Command::new("make")
         .current_dir(tmp_dir.path())
@@ -51,12 +54,16 @@
     delete_module(
         &CString::new(kmod_name).unwrap(),
         DeleteModuleFlags::empty(),
-    ).expect("unable to unload kernel module");
+    )
+    .expect("unable to unload kernel module");
 }
 
 #[test]
 fn test_finit_and_delete_module_with_params() {
-    require_capability!("test_finit_and_delete_module_with_params", CAP_SYS_MODULE);
+    require_capability!(
+        "test_finit_and_delete_module_with_params",
+        CAP_SYS_MODULE
+    );
     let _m0 = crate::KMOD_MTX.lock();
     let _m1 = crate::CWD_LOCK.read();
 
@@ -67,12 +74,14 @@
         &f,
         &CString::new("who=Rust number=2018").unwrap(),
         ModuleInitFlags::empty(),
-    ).expect("unable to load kernel module");
+    )
+    .expect("unable to load kernel module");
 
     delete_module(
         &CString::new(kmod_name).unwrap(),
         DeleteModuleFlags::empty(),
-    ).expect("unable to unload kernel module");
+    )
+    .expect("unable to unload kernel module");
 }
 
 #[test]
@@ -87,17 +96,22 @@
     let mut contents: Vec<u8> = Vec::new();
     f.read_to_end(&mut contents)
         .expect("unable to read kernel module content to buffer");
-    init_module(&contents, &CString::new("").unwrap()).expect("unable to load kernel module");
+    init_module(&contents, &CString::new("").unwrap())
+        .expect("unable to load kernel module");
 
     delete_module(
         &CString::new(kmod_name).unwrap(),
         DeleteModuleFlags::empty(),
-    ).expect("unable to unload kernel module");
+    )
+    .expect("unable to unload kernel module");
 }
 
 #[test]
 fn test_init_and_delete_module_with_params() {
-    require_capability!("test_init_and_delete_module_with_params", CAP_SYS_MODULE);
+    require_capability!(
+        "test_init_and_delete_module_with_params",
+        CAP_SYS_MODULE
+    );
     let _m0 = crate::KMOD_MTX.lock();
     let _m1 = crate::CWD_LOCK.read();
 
@@ -113,7 +127,8 @@
     delete_module(
         &CString::new(kmod_name).unwrap(),
         DeleteModuleFlags::empty(),
-    ).expect("unable to unload kernel module");
+    )
+    .expect("unable to unload kernel module");
 }
 
 #[test]
@@ -125,14 +140,18 @@
     let kmod_path = "/dev/zero";
 
     let f = File::open(kmod_path).expect("unable to open kernel module");
-    let result = finit_module(&f, &CString::new("").unwrap(), ModuleInitFlags::empty());
+    let result =
+        finit_module(&f, &CString::new("").unwrap(), ModuleInitFlags::empty());
 
     assert_eq!(result.unwrap_err(), Errno::EINVAL);
 }
 
 #[test]
 fn test_finit_module_twice_and_delete_module() {
-    require_capability!("test_finit_module_twice_and_delete_module", CAP_SYS_MODULE);
+    require_capability!(
+        "test_finit_module_twice_and_delete_module",
+        CAP_SYS_MODULE
+    );
     let _m0 = crate::KMOD_MTX.lock();
     let _m1 = crate::CWD_LOCK.read();
 
@@ -142,14 +161,16 @@
     finit_module(&f, &CString::new("").unwrap(), ModuleInitFlags::empty())
         .expect("unable to load kernel module");
 
-    let result = finit_module(&f, &CString::new("").unwrap(), ModuleInitFlags::empty());
+    let result =
+        finit_module(&f, &CString::new("").unwrap(), ModuleInitFlags::empty());
 
     assert_eq!(result.unwrap_err(), Errno::EEXIST);
 
     delete_module(
         &CString::new(kmod_name).unwrap(),
         DeleteModuleFlags::empty(),
-    ).expect("unable to unload kernel module");
+    )
+    .expect("unable to unload kernel module");
 }
 
 #[test]
@@ -158,7 +179,10 @@
     let _m0 = crate::KMOD_MTX.lock();
     let _m1 = crate::CWD_LOCK.read();
 
-    let result = delete_module(&CString::new("hello").unwrap(), DeleteModuleFlags::empty());
+    let result = delete_module(
+        &CString::new("hello").unwrap(),
+        DeleteModuleFlags::empty(),
+    );
 
     assert_eq!(result.unwrap_err(), Errno::ENOENT);
 }
diff --git a/test/test_mount.rs b/test/test_mount.rs
index 44287f9..2fd612e 100644
--- a/test/test_mount.rs
+++ b/test/test_mount.rs
@@ -1,6 +1,6 @@
 mod common;
 
-// Impelmentation note: to allow unprivileged users to run it, this test makes
+// Implementation note: to allow unprivileged users to run it, this test makes
 // use of user and mount namespaces. On systems that allow unprivileged user
 // namespaces (Linux >= 3.8 compiled with CONFIG_USER_NS), the test should run
 // without root.
@@ -27,16 +27,18 @@
     const EXPECTED_STATUS: i32 = 23;
 
     const NONE: Option<&'static [u8]> = None;
-    #[allow(clippy::bind_instead_of_map)]   // False positive
+    #[allow(clippy::bind_instead_of_map)] // False positive
     pub fn test_mount_tmpfs_without_flags_allows_rwx() {
         let tempdir = tempfile::tempdir().unwrap();
 
-        mount(NONE,
-              tempdir.path(),
-              Some(b"tmpfs".as_ref()),
-              MsFlags::empty(),
-              NONE)
-            .unwrap_or_else(|e| panic!("mount failed: {}", e));
+        mount(
+            NONE,
+            tempdir.path(),
+            Some(b"tmpfs".as_ref()),
+            MsFlags::empty(),
+            NONE,
+        )
+        .unwrap_or_else(|e| panic!("mount failed: {}", e));
 
         let test_path = tempdir.path().join("test");
 
@@ -46,8 +48,10 @@
             .write(true)
             .mode((Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO).bits())
             .open(&test_path)
-            .or_else(|e|
-                if Errno::from_i32(e.raw_os_error().unwrap()) == Errno::EOVERFLOW {
+            .or_else(|e| {
+                if Errno::from_i32(e.raw_os_error().unwrap())
+                    == Errno::EOVERFLOW
+                {
                     // Skip tests on certain Linux kernels which have a bug
                     // regarding tmpfs in namespaces.
                     // Ubuntu 14.04 and 16.04 are known to be affected; 16.10 is
@@ -56,13 +60,16 @@
                     // https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1659087
                     let stderr = io::stderr();
                     let mut handle = stderr.lock();
-                    writeln!(handle, "Buggy Linux kernel detected.  Skipping test.")
+                    writeln!(
+                        handle,
+                        "Buggy Linux kernel detected.  Skipping test."
+                    )
                     .unwrap();
                     process::exit(0);
-               } else {
-                   panic!("open failed: {}", e);
-               }
-            )
+                } else {
+                    panic!("open failed: {}", e);
+                }
+            })
             .and_then(|mut f| f.write(SCRIPT_CONTENTS))
             .unwrap_or_else(|e| panic!("write failed: {}", e));
 
@@ -74,42 +81,55 @@
         assert_eq!(buf, SCRIPT_CONTENTS);
 
         // Verify execute.
-        assert_eq!(EXPECTED_STATUS,
-                   Command::new(&test_path)
-                       .status()
-                       .unwrap_or_else(|e| panic!("exec failed: {}", e))
-                       .code()
-                       .unwrap_or_else(|| panic!("child killed by signal")));
+        assert_eq!(
+            EXPECTED_STATUS,
+            Command::new(&test_path)
+                .status()
+                .unwrap_or_else(|e| panic!("exec failed: {}", e))
+                .code()
+                .unwrap_or_else(|| panic!("child killed by signal"))
+        );
 
-        umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {}", e));
+        umount(tempdir.path())
+            .unwrap_or_else(|e| panic!("umount failed: {}", e));
     }
 
     pub fn test_mount_rdonly_disallows_write() {
         let tempdir = tempfile::tempdir().unwrap();
 
-        mount(NONE,
-              tempdir.path(),
-              Some(b"tmpfs".as_ref()),
-              MsFlags::MS_RDONLY,
-              NONE)
-            .unwrap_or_else(|e| panic!("mount failed: {}", e));
+        mount(
+            NONE,
+            tempdir.path(),
+            Some(b"tmpfs".as_ref()),
+            MsFlags::MS_RDONLY,
+            NONE,
+        )
+        .unwrap_or_else(|e| panic!("mount failed: {}", e));
 
         // EROFS: Read-only file system
-        assert_eq!(EROFS as i32,
-                   File::create(tempdir.path().join("test")).unwrap_err().raw_os_error().unwrap());
+        assert_eq!(
+            EROFS,
+            File::create(tempdir.path().join("test"))
+                .unwrap_err()
+                .raw_os_error()
+                .unwrap()
+        );
 
-        umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {}", e));
+        umount(tempdir.path())
+            .unwrap_or_else(|e| panic!("umount failed: {}", e));
     }
 
     pub fn test_mount_noexec_disallows_exec() {
         let tempdir = tempfile::tempdir().unwrap();
 
-        mount(NONE,
-              tempdir.path(),
-              Some(b"tmpfs".as_ref()),
-              MsFlags::MS_NOEXEC,
-              NONE)
-            .unwrap_or_else(|e| panic!("mount failed: {}", e));
+        mount(
+            NONE,
+            tempdir.path(),
+            Some(b"tmpfs".as_ref()),
+            MsFlags::MS_NOEXEC,
+            NONE,
+        )
+        .unwrap_or_else(|e| panic!("mount failed: {}", e));
 
         let test_path = tempdir.path().join("test");
 
@@ -122,21 +142,30 @@
             .unwrap_or_else(|e| panic!("write failed: {}", e));
 
         // Verify that we cannot execute despite a+x permissions being set.
-        let mode = stat::Mode::from_bits_truncate(fs::metadata(&test_path)
-                                                      .map(|md| md.permissions().mode())
-                                                      .unwrap_or_else(|e| {
-                                                          panic!("metadata failed: {}", e)
-                                                      }));
+        let mode = stat::Mode::from_bits_truncate(
+            fs::metadata(&test_path)
+                .map(|md| md.permissions().mode())
+                .unwrap_or_else(|e| panic!("metadata failed: {}", e)),
+        );
 
-        assert!(mode.contains(Mode::S_IXUSR | Mode::S_IXGRP | Mode::S_IXOTH),
-                "{:?} did not have execute permissions",
-                &test_path);
+        assert!(
+            mode.contains(Mode::S_IXUSR | Mode::S_IXGRP | Mode::S_IXOTH),
+            "{:?} did not have execute permissions",
+            &test_path
+        );
 
         // EACCES: Permission denied
-        assert_eq!(EACCES as i32,
-                   Command::new(&test_path).status().unwrap_err().raw_os_error().unwrap());
+        assert_eq!(
+            EACCES,
+            Command::new(&test_path)
+                .status()
+                .unwrap_err()
+                .raw_os_error()
+                .unwrap()
+        );
 
-        umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {}", e));
+        umount(tempdir.path())
+            .unwrap_or_else(|e| panic!("umount failed: {}", e));
     }
 
     pub fn test_mount_bind() {
@@ -146,12 +175,14 @@
         {
             let mount_point = tempfile::tempdir().unwrap();
 
-            mount(Some(tempdir.path()),
-                  mount_point.path(),
-                  NONE,
-                  MsFlags::MS_BIND,
-                  NONE)
-                .unwrap_or_else(|e| panic!("mount failed: {}", e));
+            mount(
+                Some(tempdir.path()),
+                mount_point.path(),
+                NONE,
+                MsFlags::MS_BIND,
+                NONE,
+            )
+            .unwrap_or_else(|e| panic!("mount failed: {}", e));
 
             fs::OpenOptions::new()
                 .create(true)
@@ -161,7 +192,8 @@
                 .and_then(|mut f| f.write(SCRIPT_CONTENTS))
                 .unwrap_or_else(|e| panic!("write failed: {}", e));
 
-            umount(mount_point.path()).unwrap_or_else(|e| panic!("umount failed: {}", e));
+            umount(mount_point.path())
+                .unwrap_or_else(|e| panic!("umount failed: {}", e));
         }
 
         // Verify the file written in the mount shows up in source directory, even
@@ -199,7 +231,6 @@
     }
 }
 
-
 // Test runner
 
 /// Mimic normal test output (hackishly).
@@ -220,16 +251,20 @@
 
 #[cfg(target_os = "linux")]
 fn main() {
-    use test_mount::{setup_namespaces, test_mount_tmpfs_without_flags_allows_rwx,
-                     test_mount_rdonly_disallows_write, test_mount_noexec_disallows_exec,
-                     test_mount_bind};
+    use test_mount::{
+        setup_namespaces, test_mount_bind, test_mount_noexec_disallows_exec,
+        test_mount_rdonly_disallows_write,
+        test_mount_tmpfs_without_flags_allows_rwx,
+    };
     skip_if_cirrus!("Fails for an unknown reason Cirrus CI.  Bug #1351");
     setup_namespaces();
 
-    run_tests!(test_mount_tmpfs_without_flags_allows_rwx,
-               test_mount_rdonly_disallows_write,
-               test_mount_noexec_disallows_exec,
-               test_mount_bind);
+    run_tests!(
+        test_mount_tmpfs_without_flags_allows_rwx,
+        test_mount_rdonly_disallows_write,
+        test_mount_noexec_disallows_exec,
+        test_mount_bind
+    );
 }
 
 #[cfg(not(target_os = "linux"))]
diff --git a/test/test_mq.rs b/test/test_mq.rs
index 430df5d..7b48e7a 100644
--- a/test/test_mq.rs
+++ b/test/test_mq.rs
@@ -1,16 +1,36 @@
+use cfg_if::cfg_if;
 use std::ffi::CString;
 use std::str;
 
 use nix::errno::Errno;
-use nix::mqueue::{mq_open, mq_close, mq_send, mq_receive, mq_attr_member_t};
-use nix::mqueue::{MqAttr, MQ_OFlag};
+use nix::mqueue::{mq_attr_member_t, mq_close, mq_open, mq_receive, mq_send};
+use nix::mqueue::{MQ_OFlag, MqAttr};
 use nix::sys::stat::Mode;
 
+// Defined as a macro such that the error source is reported as the caller's location.
+macro_rules! assert_attr_eq {
+    ($read_attr:ident, $initial_attr:ident) => {
+        cfg_if! {
+            if #[cfg(any(target_os = "dragonfly", target_os = "netbsd"))] {
+                // NetBSD (and others which inherit its implementation) include other flags
+                // in read_attr, such as those specified by oflag. Just make sure at least
+                // the correct bits are set.
+                assert_eq!($read_attr.flags() & $initial_attr.flags(), $initial_attr.flags());
+                assert_eq!($read_attr.maxmsg(), $initial_attr.maxmsg());
+                assert_eq!($read_attr.msgsize(), $initial_attr.msgsize());
+                assert_eq!($read_attr.curmsgs(), $initial_attr.curmsgs());
+            } else {
+                assert_eq!($read_attr, $initial_attr);
+            }
+        }
+    }
+}
+
 #[test]
 fn test_mq_send_and_receive() {
     const MSG_SIZE: mq_attr_member_t = 32;
-    let attr =  MqAttr::new(0, 10, MSG_SIZE, 0);
-    let mq_name= &CString::new(b"/a_nix_test_queue".as_ref()).unwrap();
+    let attr = MqAttr::new(0, 10, MSG_SIZE, 0);
+    let mq_name = &CString::new(b"/a_nix_test_queue".as_ref()).unwrap();
 
     let oflag0 = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
     let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
@@ -21,13 +41,13 @@
     };
     let mqd0 = r0.unwrap();
     let msg_to_send = "msg_1";
-    mq_send(mqd0, msg_to_send.as_bytes(), 1).unwrap();
+    mq_send(&mqd0, msg_to_send.as_bytes(), 1).unwrap();
 
     let oflag1 = MQ_OFlag::O_CREAT | MQ_OFlag::O_RDONLY;
     let mqd1 = mq_open(mq_name, oflag1, mode, Some(&attr)).unwrap();
     let mut buf = [0u8; 32];
     let mut prio = 0u32;
-    let len = mq_receive(mqd1, &mut buf, &mut prio).unwrap();
+    let len = mq_receive(&mqd1, &mut buf, &mut prio).unwrap();
     assert_eq!(prio, 1);
 
     mq_close(mqd1).unwrap();
@@ -35,13 +55,11 @@
     assert_eq!(msg_to_send, str::from_utf8(&buf[0..len]).unwrap());
 }
 
-
 #[test]
-#[cfg(not(any(target_os = "netbsd")))]
 fn test_mq_getattr() {
     use nix::mqueue::mq_getattr;
     const MSG_SIZE: mq_attr_member_t = 32;
-    let initial_attr =  MqAttr::new(0, 10, MSG_SIZE, 0);
+    let initial_attr = MqAttr::new(0, 10, MSG_SIZE, 0);
     let mq_name = &CString::new(b"/attr_test_get_attr".as_ref()).unwrap();
     let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
     let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
@@ -52,23 +70,21 @@
     };
     let mqd = r.unwrap();
 
-    let read_attr = mq_getattr(mqd).unwrap();
-    assert_eq!(read_attr, initial_attr);
+    let read_attr = mq_getattr(&mqd).unwrap();
+    assert_attr_eq!(read_attr, initial_attr);
     mq_close(mqd).unwrap();
 }
 
 // FIXME: Fix failures for mips in QEMU
 #[test]
-#[cfg(not(any(target_os = "netbsd")))]
-#[cfg_attr(all(
-        qemu,
-        any(target_arch = "mips", target_arch = "mips64")
-    ), ignore
+#[cfg_attr(
+    all(qemu, any(target_arch = "mips", target_arch = "mips64")),
+    ignore
 )]
 fn test_mq_setattr() {
     use nix::mqueue::{mq_getattr, mq_setattr};
     const MSG_SIZE: mq_attr_member_t = 32;
-    let initial_attr =  MqAttr::new(0, 10, MSG_SIZE, 0);
+    let initial_attr = MqAttr::new(0, 10, MSG_SIZE, 0);
     let mq_name = &CString::new(b"/attr_test_get_attr".as_ref()).unwrap();
     let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
     let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
@@ -79,37 +95,46 @@
     };
     let mqd = r.unwrap();
 
-    let new_attr =  MqAttr::new(0, 20, MSG_SIZE * 2, 100);
-    let old_attr = mq_setattr(mqd, &new_attr).unwrap();
-    assert_eq!(old_attr, initial_attr);
+    let new_attr = MqAttr::new(0, 20, MSG_SIZE * 2, 100);
+    let old_attr = mq_setattr(&mqd, &new_attr).unwrap();
+    assert_attr_eq!(old_attr, initial_attr);
 
-    let new_attr_get = mq_getattr(mqd).unwrap();
-    // The following tests make sense. No changes here because according to the Linux man page only
+    // No changes here because according to the Linux man page only
     // O_NONBLOCK can be set (see tests below)
-    assert_ne!(new_attr_get, new_attr);
+    #[cfg(not(any(target_os = "dragonfly", target_os = "netbsd")))]
+    {
+        let new_attr_get = mq_getattr(&mqd).unwrap();
+        assert_ne!(new_attr_get, new_attr);
+    }
 
-    let new_attr_non_blocking =  MqAttr::new(MQ_OFlag::O_NONBLOCK.bits() as mq_attr_member_t, 10, MSG_SIZE, 0);
-    mq_setattr(mqd, &new_attr_non_blocking).unwrap();
-    let new_attr_get = mq_getattr(mqd).unwrap();
+    let new_attr_non_blocking = MqAttr::new(
+        MQ_OFlag::O_NONBLOCK.bits() as mq_attr_member_t,
+        10,
+        MSG_SIZE,
+        0,
+    );
+    mq_setattr(&mqd, &new_attr_non_blocking).unwrap();
+    let new_attr_get = mq_getattr(&mqd).unwrap();
 
     // now the O_NONBLOCK flag has been set
-    assert_ne!(new_attr_get, initial_attr);
-    assert_eq!(new_attr_get, new_attr_non_blocking);
+    #[cfg(not(any(target_os = "dragonfly", target_os = "netbsd")))]
+    {
+        assert_ne!(new_attr_get, initial_attr);
+    }
+    assert_attr_eq!(new_attr_get, new_attr_non_blocking);
     mq_close(mqd).unwrap();
 }
 
 // FIXME: Fix failures for mips in QEMU
 #[test]
-#[cfg(not(any(target_os = "netbsd")))]
-#[cfg_attr(all(
-        qemu,
-        any(target_arch = "mips", target_arch = "mips64")
-    ), ignore
+#[cfg_attr(
+    all(qemu, any(target_arch = "mips", target_arch = "mips64")),
+    ignore
 )]
 fn test_mq_set_nonblocking() {
-    use nix::mqueue::{mq_getattr, mq_set_nonblock, mq_remove_nonblock};
+    use nix::mqueue::{mq_getattr, mq_remove_nonblock, mq_set_nonblock};
     const MSG_SIZE: mq_attr_member_t = 32;
-    let initial_attr =  MqAttr::new(0, 10, MSG_SIZE, 0);
+    let initial_attr = MqAttr::new(0, 10, MSG_SIZE, 0);
     let mq_name = &CString::new(b"/attr_test_get_attr".as_ref()).unwrap();
     let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
     let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
@@ -119,23 +144,25 @@
         return;
     };
     let mqd = r.unwrap();
-    mq_set_nonblock(mqd).unwrap();
-    let new_attr = mq_getattr(mqd);
-    assert_eq!(new_attr.unwrap().flags(), MQ_OFlag::O_NONBLOCK.bits() as mq_attr_member_t);
-    mq_remove_nonblock(mqd).unwrap();
-    let new_attr = mq_getattr(mqd);
-    assert_eq!(new_attr.unwrap().flags(), 0);
+    mq_set_nonblock(&mqd).unwrap();
+    let new_attr = mq_getattr(&mqd);
+    let o_nonblock_bits = MQ_OFlag::O_NONBLOCK.bits() as mq_attr_member_t;
+    assert_eq!(new_attr.unwrap().flags() & o_nonblock_bits, o_nonblock_bits);
+    mq_remove_nonblock(&mqd).unwrap();
+    let new_attr = mq_getattr(&mqd);
+    assert_eq!(new_attr.unwrap().flags() & o_nonblock_bits, 0);
     mq_close(mqd).unwrap();
 }
 
 #[test]
-#[cfg(not(any(target_os = "netbsd")))]
 fn test_mq_unlink() {
     use nix::mqueue::mq_unlink;
     const MSG_SIZE: mq_attr_member_t = 32;
-    let initial_attr =  MqAttr::new(0, 10, MSG_SIZE, 0);
+    let initial_attr = MqAttr::new(0, 10, MSG_SIZE, 0);
     let mq_name_opened = &CString::new(b"/mq_unlink_test".as_ref()).unwrap();
-    let mq_name_not_opened = &CString::new(b"/mq_unlink_test".as_ref()).unwrap();
+    #[cfg(not(any(target_os = "dragonfly", target_os = "netbsd")))]
+    let mq_name_not_opened =
+        &CString::new(b"/mq_unlink_test".as_ref()).unwrap();
     let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
     let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
     let r = mq_open(mq_name_opened, oflag, mode, Some(&initial_attr));
@@ -146,12 +173,18 @@
     let mqd = r.unwrap();
 
     let res_unlink = mq_unlink(mq_name_opened);
-    assert_eq!(res_unlink, Ok(()) );
+    assert_eq!(res_unlink, Ok(()));
 
-    let res_unlink_not_opened = mq_unlink(mq_name_not_opened);
-    assert_eq!(res_unlink_not_opened, Err(Errno::ENOENT) );
+    // NetBSD (and others which inherit its implementation) defer removing the message
+    // queue name until all references are closed, whereas Linux and others remove the
+    // message queue name immediately.
+    #[cfg(not(any(target_os = "dragonfly", target_os = "netbsd")))]
+    {
+        let res_unlink_not_opened = mq_unlink(mq_name_not_opened);
+        assert_eq!(res_unlink_not_opened, Err(Errno::ENOENT));
+    }
 
     mq_close(mqd).unwrap();
     let res_unlink_after_close = mq_unlink(mq_name_opened);
-    assert_eq!(res_unlink_after_close, Err(Errno::ENOENT) );
+    assert_eq!(res_unlink_after_close, Err(Errno::ENOENT));
 }
diff --git a/test/test_net.rs b/test/test_net.rs
index 40ecd6b..c44655a 100644
--- a/test/test_net.rs
+++ b/test/test_net.rs
@@ -3,10 +3,17 @@
 #[cfg(any(target_os = "android", target_os = "linux"))]
 const LOOPBACK: &[u8] = b"lo";
 
-#[cfg(not(any(target_os = "android", target_os = "linux")))]
+#[cfg(not(any(
+    target_os = "android",
+    target_os = "linux",
+    target_os = "haiku"
+)))]
 const LOOPBACK: &[u8] = b"lo0";
 
+#[cfg(target_os = "haiku")]
+const LOOPBACK: &[u8] = b"loop";
+
 #[test]
 fn test_if_nametoindex() {
-    assert!(if_nametoindex(LOOPBACK).is_ok());
+    if_nametoindex(LOOPBACK).expect("assertion failed");
 }
diff --git a/test/test_nix_path.rs b/test/test_nix_path.rs
index e69de29..8b13789 100644
--- a/test/test_nix_path.rs
+++ b/test/test_nix_path.rs
@@ -0,0 +1 @@
+
diff --git a/test/test_nmount.rs b/test/test_nmount.rs
index 4c74ecf..dec806a 100644
--- a/test/test_nmount.rs
+++ b/test/test_nmount.rs
@@ -1,13 +1,9 @@
 use crate::*;
 use nix::{
     errno::Errno,
-    mount::{MntFlags, Nmount, unmount}
+    mount::{unmount, MntFlags, Nmount},
 };
-use std::{
-    ffi::CString,
-    fs::File,
-    path::Path
-};
+use std::{ffi::CString, fs::File, path::Path};
 use tempfile::tempdir;
 
 #[test]
@@ -24,14 +20,15 @@
         .str_opt(&fstype, &nullfs)
         .str_opt_owned("fspath", mountpoint.path().to_str().unwrap())
         .str_opt_owned("target", target.path().to_str().unwrap())
-        .nmount(MntFlags::empty()).unwrap();
-    
+        .nmount(MntFlags::empty())
+        .unwrap();
+
     // Now check that the sentry is visible through the mountpoint
     let exists = Path::exists(&mountpoint.path().join("sentry"));
 
     // Cleanup the mountpoint before asserting
     unmount(mountpoint.path(), MntFlags::empty()).unwrap();
-    
+
     assert!(exists);
 }
 
@@ -44,8 +41,9 @@
     let e = Nmount::new()
         .str_opt_owned("fspath", mountpoint.path().to_str().unwrap())
         .str_opt_owned("target", target.path().to_str().unwrap())
-        .nmount(MntFlags::empty()).unwrap_err();
-    
+        .nmount(MntFlags::empty())
+        .unwrap_err();
+
     assert_eq!(e.error(), Errno::EINVAL);
     assert_eq!(e.errmsg(), Some("Invalid fstype"));
 }
diff --git a/test/test_poll.rs b/test/test_poll.rs
index e4b369f..53964e2 100644
--- a/test/test_poll.rs
+++ b/test/test_poll.rs
@@ -1,7 +1,7 @@
 use nix::{
     errno::Errno,
-    poll::{PollFlags, poll, PollFd},
-    unistd::{write, pipe}
+    poll::{poll, PollFd, PollFlags},
+    unistd::{pipe, write},
 };
 
 macro_rules! loop_while_eintr {
@@ -10,10 +10,10 @@
             match $poll_expr {
                 Ok(nfds) => break nfds,
                 Err(Errno::EINTR) => (),
-                Err(e) => panic!("{}", e)
+                Err(e) => panic!("{}", e),
             }
         }
-    }
+    };
 }
 
 #[test]
@@ -37,10 +37,12 @@
 // ppoll(2) is the same as poll except for how it handles timeouts and signals.
 // Repeating the test for poll(2) should be sufficient to check that our
 // bindings are correct.
-#[cfg(any(target_os = "android",
-          target_os = "dragonfly",
-          target_os = "freebsd",
-          target_os = "linux"))]
+#[cfg(any(
+    target_os = "android",
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "linux"
+))]
 #[test]
 fn test_ppoll() {
     use nix::poll::ppoll;
@@ -53,14 +55,14 @@
 
     // Poll an idle pipe.  Should timeout
     let sigset = SigSet::empty();
-    let nfds = loop_while_eintr!(ppoll(&mut fds, Some(timeout), sigset));
+    let nfds = loop_while_eintr!(ppoll(&mut fds, Some(timeout), Some(sigset)));
     assert_eq!(nfds, 0);
     assert!(!fds[0].revents().unwrap().contains(PollFlags::POLLIN));
 
     write(w, b".").unwrap();
 
     // Poll a readable pipe.  Should return an event.
-    let nfds = ppoll(&mut fds, Some(timeout), SigSet::empty()).unwrap();
+    let nfds = ppoll(&mut fds, Some(timeout), None).unwrap();
     assert_eq!(nfds, 1);
     assert!(fds[0].revents().unwrap().contains(PollFlags::POLLIN));
 }
diff --git a/test/test_pty.rs b/test/test_pty.rs
index 71932f2..5c27e2d 100644
--- a/test/test_pty.rs
+++ b/test/test_pty.rs
@@ -1,15 +1,15 @@
 use std::fs::File;
 use std::io::{Read, Write};
-use std::path::Path;
 use std::os::unix::prelude::*;
+use std::path::Path;
 use tempfile::tempfile;
 
 use libc::{_exit, STDOUT_FILENO};
-use nix::fcntl::{OFlag, open};
+use nix::fcntl::{open, OFlag};
 use nix::pty::*;
 use nix::sys::stat;
 use nix::sys::termios::*;
-use nix::unistd::{write, close, pause};
+use nix::unistd::{close, pause, write};
 
 /// Regression test for Issue #659
 /// This is the correct way to explicitly close a `PtyMaster`
@@ -36,7 +36,7 @@
     assert!(master_fd.as_raw_fd() > 0);
 
     // Get the name of the slave
-    let slave_name = unsafe { ptsname(&master_fd) }.unwrap() ;
+    let slave_name = unsafe { ptsname(&master_fd) }.unwrap();
     let slave_name_r = ptsname_r(&master_fd).unwrap();
     assert_eq!(slave_name, slave_name_r);
 }
@@ -58,7 +58,7 @@
     assert_eq!(slave_name1, slave_name2);
     // Also make sure that the string was actually copied and they point to different parts of
     // memory.
-    assert!(slave_name1.as_ptr() != slave_name2.as_ptr());
+    assert_ne!(slave_name1.as_ptr(), slave_name2.as_ptr());
 }
 
 /// Test data copying of `ptsname_r`
@@ -73,7 +73,7 @@
     let slave_name1 = ptsname_r(&master_fd).unwrap();
     let slave_name2 = ptsname_r(&master_fd).unwrap();
     assert_eq!(slave_name1, slave_name2);
-    assert!(slave_name1.as_ptr() != slave_name2.as_ptr());
+    assert_ne!(slave_name1.as_ptr(), slave_name2.as_ptr());
 }
 
 /// Test that `ptsname` returns different names for different devices
@@ -93,7 +93,7 @@
     // Get the name of the slave
     let slave_name1 = unsafe { ptsname(&master1_fd) }.unwrap();
     let slave_name2 = unsafe { ptsname(&master2_fd) }.unwrap();
-    assert!(slave_name1 != slave_name2);
+    assert_ne!(slave_name1, slave_name2);
 }
 
 /// Common setup for testing PTTY pairs
@@ -111,7 +111,9 @@
     let slave_name = unsafe { ptsname(&master) }.expect("ptsname failed");
 
     // Open the slave device
-    let slave_fd = open(Path::new(&slave_name), OFlag::O_RDWR, stat::Mode::empty()).unwrap();
+    let slave_fd =
+        open(Path::new(&slave_name), OFlag::O_RDWR, stat::Mode::empty())
+            .unwrap();
 
     #[cfg(target_os = "illumos")]
     // TODO: rewrite using ioctl!
@@ -170,6 +172,11 @@
     slave.write_all(b"hello").unwrap();
     master.read_exact(&mut buf).unwrap();
     assert_eq!(&buf, b"hello");
+
+    let mut master = &master;
+    slave.write_all(b"hello").unwrap();
+    master.read_exact(&mut buf).unwrap();
+    assert_eq!(&buf, b"hello");
 }
 
 /// Test `io::Write` on the PTTY master
@@ -182,6 +189,11 @@
     master.write_all(b"adios").unwrap();
     slave.read_exact(&mut buf).unwrap();
     assert_eq!(&buf, b"adios");
+
+    let mut master = &master;
+    master.write_all(b"adios").unwrap();
+    slave.read_exact(&mut buf).unwrap();
+    assert_eq!(&buf, b"adios");
 }
 
 #[test]
@@ -269,9 +281,9 @@
 
 #[test]
 fn test_forkpty() {
-    use nix::unistd::ForkResult::*;
     use nix::sys::signal::*;
     use nix::sys::wait::wait;
+    use nix::unistd::ForkResult::*;
     // forkpty calls openpty which uses ptname(3) internally.
     let _m0 = crate::PTSNAME_MTX.lock();
     // forkpty spawns a child process
@@ -279,15 +291,15 @@
 
     let string = "naninani\n";
     let echoed_string = "naninani\r\n";
-    let pty = unsafe {
-        forkpty(None, None).unwrap()
-    };
+    let pty = unsafe { forkpty(None, None).unwrap() };
     match pty.fork_result {
         Child => {
             write(STDOUT_FILENO, string.as_bytes()).unwrap();
-            pause();  // we need the child to stay alive until the parent calls read
-            unsafe { _exit(0); }
-        },
+            pause(); // we need the child to stay alive until the parent calls read
+            unsafe {
+                _exit(0);
+            }
+        }
         Parent { child } => {
             let mut buf = [0u8; 10];
             assert!(child.as_raw() > 0);
@@ -296,6 +308,6 @@
             wait().unwrap(); // keep other tests using generic wait from getting our child
             assert_eq!(&buf, echoed_string.as_bytes());
             close(pty.master).unwrap();
-        },
+        }
     }
 }
diff --git a/test/test_ptymaster_drop.rs b/test/test_ptymaster_drop.rs
index a68f81e..ffbaa56 100644
--- a/test/test_ptymaster_drop.rs
+++ b/test/test_ptymaster_drop.rs
@@ -15,6 +15,6 @@
     fn test_double_close() {
         let m = posix_openpt(OFlag::O_RDWR).unwrap();
         close(m.as_raw_fd()).unwrap();
-        drop(m);            // should panic here
+        drop(m); // should panic here
     }
 }
diff --git a/test/test_resource.rs b/test/test_resource.rs
index 5969750..2ab581b 100644
--- a/test/test_resource.rs
+++ b/test/test_resource.rs
@@ -1,4 +1,9 @@
-#[cfg(not(any(target_os = "redox", target_os = "fuchsia", target_os = "illumos")))]
+#[cfg(not(any(
+    target_os = "redox",
+    target_os = "fuchsia",
+    target_os = "illumos",
+    target_os = "haiku"
+)))]
 use nix::sys::resource::{getrlimit, setrlimit, Resource};
 
 /// Tests the RLIMIT_NOFILE functionality of getrlimit(), where the resource RLIMIT_NOFILE refers
@@ -10,11 +15,17 @@
 /// to put the new soft limit in effect, and then getrlimit() once more to ensure the limits have
 /// been updated.
 #[test]
-#[cfg(not(any(target_os = "redox", target_os = "fuchsia", target_os = "illumos")))]
+#[cfg(not(any(
+    target_os = "redox",
+    target_os = "fuchsia",
+    target_os = "illumos",
+    target_os = "haiku"
+)))]
 pub fn test_resource_limits_nofile() {
-    let (soft_limit, hard_limit) = getrlimit(Resource::RLIMIT_NOFILE).unwrap();
+    let (mut soft_limit, hard_limit) =
+        getrlimit(Resource::RLIMIT_NOFILE).unwrap();
 
-    let soft_limit = Some(soft_limit.map_or(1024, |v| v - 1));
+    soft_limit -= 1;
     assert_ne!(soft_limit, hard_limit);
     setrlimit(Resource::RLIMIT_NOFILE, soft_limit, hard_limit).unwrap();
 
diff --git a/test/test_sched.rs b/test/test_sched.rs
index 922196a..c52616b 100644
--- a/test/test_sched.rs
+++ b/test/test_sched.rs
@@ -1,4 +1,4 @@
-use nix::sched::{sched_getaffinity, sched_setaffinity, CpuSet};
+use nix::sched::{sched_getaffinity, sched_getcpu, sched_setaffinity, CpuSet};
 use nix::unistd::Pid;
 
 #[test]
@@ -24,9 +24,16 @@
     let updated_affinity = sched_getaffinity(Pid::from_raw(0)).unwrap();
     for field in 0..CpuSet::count() {
         // Should be set only for the CPU we set previously
-        assert_eq!(updated_affinity.is_set(field).unwrap(), field==last_valid_cpu)
+        assert_eq!(
+            updated_affinity.is_set(field).unwrap(),
+            field == last_valid_cpu
+        )
     }
 
+    // Now check that we're also currently running on the CPU in question.
+    let cur_cpu = sched_getcpu().unwrap();
+    assert_eq!(cur_cpu, last_valid_cpu);
+
     // Finally, reset the initial CPU set
     sched_setaffinity(Pid::from_raw(0), &initial_affinity).unwrap();
 }
diff --git a/test/test_sendfile.rs b/test/test_sendfile.rs
index b6559d3..f73a3b5 100644
--- a/test/test_sendfile.rs
+++ b/test/test_sendfile.rs
@@ -8,7 +8,7 @@
 cfg_if! {
     if #[cfg(any(target_os = "android", target_os = "linux"))] {
         use nix::unistd::{close, pipe, read};
-    } else if #[cfg(any(target_os = "freebsd", target_os = "ios", target_os = "macos"))] {
+    } else if #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "ios", target_os = "macos"))] {
         use std::net::Shutdown;
         use std::os::unix::net::UnixStream;
     }
@@ -62,7 +62,8 @@
 #[test]
 fn test_sendfile_freebsd() {
     // Declare the content
-    let header_strings = vec!["HTTP/1.1 200 OK\n", "Content-Type: text/plain\n", "\n"];
+    let header_strings =
+        vec!["HTTP/1.1 200 OK\n", "Content-Type: text/plain\n", "\n"];
     let body = "Xabcdef123456";
     let body_offset = 1;
     let trailer_strings = vec!["\n", "Served by Make Believe\n"];
@@ -72,8 +73,10 @@
     tmp.write_all(body.as_bytes()).unwrap();
 
     // Prepare headers and trailers for sendfile
-    let headers: Vec<&[u8]> = header_strings.iter().map(|s| s.as_bytes()).collect();
-    let trailers: Vec<&[u8]> = trailer_strings.iter().map(|s| s.as_bytes()).collect();
+    let headers: Vec<&[u8]> =
+        header_strings.iter().map(|s| s.as_bytes()).collect();
+    let trailers: Vec<&[u8]> =
+        trailer_strings.iter().map(|s| s.as_bytes()).collect();
 
     // Prepare socket pair
     let (mut rd, wr) = UnixStream::pair().unwrap();
@@ -93,8 +96,9 @@
     wr.shutdown(Shutdown::Both).unwrap();
 
     // Prepare the expected result
-    let expected_string =
-        header_strings.concat() + &body[body_offset..] + &trailer_strings.concat();
+    let expected_string = header_strings.concat()
+        + &body[body_offset..]
+        + &trailer_strings.concat();
 
     // Verify the message that was sent
     assert_eq!(bytes_written as usize, expected_string.as_bytes().len());
@@ -105,11 +109,12 @@
     assert_eq!(expected_string, read_string);
 }
 
-#[cfg(any(target_os = "ios", target_os = "macos"))]
+#[cfg(target_os = "dragonfly")]
 #[test]
-fn test_sendfile_darwin() {
+fn test_sendfile_dragonfly() {
     // Declare the content
-    let header_strings = vec!["HTTP/1.1 200 OK\n", "Content-Type: text/plain\n", "\n"];
+    let header_strings =
+        vec!["HTTP/1.1 200 OK\n", "Content-Type: text/plain\n", "\n"];
     let body = "Xabcdef123456";
     let body_offset = 1;
     let trailer_strings = vec!["\n", "Served by Make Believe\n"];
@@ -119,8 +124,10 @@
     tmp.write_all(body.as_bytes()).unwrap();
 
     // Prepare headers and trailers for sendfile
-    let headers: Vec<&[u8]> = header_strings.iter().map(|s| s.as_bytes()).collect();
-    let trailers: Vec<&[u8]> = trailer_strings.iter().map(|s| s.as_bytes()).collect();
+    let headers: Vec<&[u8]> =
+        header_strings.iter().map(|s| s.as_bytes()).collect();
+    let trailers: Vec<&[u8]> =
+        trailer_strings.iter().map(|s| s.as_bytes()).collect();
 
     // Prepare socket pair
     let (mut rd, wr) = UnixStream::pair().unwrap();
@@ -138,8 +145,58 @@
     wr.shutdown(Shutdown::Both).unwrap();
 
     // Prepare the expected result
-    let expected_string =
-        header_strings.concat() + &body[body_offset..] + &trailer_strings.concat();
+    let expected_string = header_strings.concat()
+        + &body[body_offset..]
+        + &trailer_strings.concat();
+
+    // Verify the message that was sent
+    assert_eq!(bytes_written as usize, expected_string.as_bytes().len());
+
+    let mut read_string = String::new();
+    let bytes_read = rd.read_to_string(&mut read_string).unwrap();
+    assert_eq!(bytes_written as usize, bytes_read);
+    assert_eq!(expected_string, read_string);
+}
+
+#[cfg(any(target_os = "ios", target_os = "macos"))]
+#[test]
+fn test_sendfile_darwin() {
+    // Declare the content
+    let header_strings =
+        vec!["HTTP/1.1 200 OK\n", "Content-Type: text/plain\n", "\n"];
+    let body = "Xabcdef123456";
+    let body_offset = 1;
+    let trailer_strings = vec!["\n", "Served by Make Believe\n"];
+
+    // Write the body to a file
+    let mut tmp = tempfile().unwrap();
+    tmp.write_all(body.as_bytes()).unwrap();
+
+    // Prepare headers and trailers for sendfile
+    let headers: Vec<&[u8]> =
+        header_strings.iter().map(|s| s.as_bytes()).collect();
+    let trailers: Vec<&[u8]> =
+        trailer_strings.iter().map(|s| s.as_bytes()).collect();
+
+    // Prepare socket pair
+    let (mut rd, wr) = UnixStream::pair().unwrap();
+
+    // Call the test method
+    let (res, bytes_written) = sendfile(
+        tmp.as_raw_fd(),
+        wr.as_raw_fd(),
+        body_offset as off_t,
+        None,
+        Some(headers.as_slice()),
+        Some(trailers.as_slice()),
+    );
+    assert!(res.is_ok());
+    wr.shutdown(Shutdown::Both).unwrap();
+
+    // Prepare the expected result
+    let expected_string = header_strings.concat()
+        + &body[body_offset..]
+        + &trailer_strings.concat();
 
     // Verify the message that was sent
     assert_eq!(bytes_written as usize, expected_string.as_bytes().len());
diff --git a/test/test_stat.rs b/test/test_stat.rs
index 33cf748..55f15c0 100644
--- a/test/test_stat.rs
+++ b/test/test_stat.rs
@@ -1,42 +1,51 @@
-#[cfg(not(target_os = "redox"))]
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
 use std::fs;
 use std::fs::File;
 #[cfg(not(target_os = "redox"))]
-use std::os::unix::fs::{symlink, PermissionsExt};
+use std::os::unix::fs::symlink;
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+use std::os::unix::fs::PermissionsExt;
 use std::os::unix::prelude::AsRawFd;
 #[cfg(not(target_os = "redox"))]
-use std::time::{Duration, UNIX_EPOCH};
-#[cfg(not(target_os = "redox"))]
 use std::path::Path;
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+use std::time::{Duration, UNIX_EPOCH};
 
-#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
-use libc::{S_IFMT, S_IFLNK};
 use libc::mode_t;
+#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
+use libc::{S_IFLNK, S_IFMT};
 
 #[cfg(not(target_os = "redox"))]
-use nix::fcntl;
-#[cfg(not(target_os = "redox"))]
 use nix::errno::Errno;
 #[cfg(not(target_os = "redox"))]
-use nix::sys::stat::{self, futimens, utimes};
+use nix::fcntl;
+#[cfg(any(
+    target_os = "linux",
+    target_os = "ios",
+    target_os = "macos",
+    target_os = "freebsd",
+    target_os = "netbsd"
+))]
+use nix::sys::stat::lutimes;
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+use nix::sys::stat::utimensat;
+#[cfg(not(target_os = "redox"))]
+use nix::sys::stat::FchmodatFlags;
+use nix::sys::stat::Mode;
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+use nix::sys::stat::UtimensatFlags;
+#[cfg(not(target_os = "redox"))]
+use nix::sys::stat::{self};
 use nix::sys::stat::{fchmod, stat};
 #[cfg(not(target_os = "redox"))]
-use nix::sys::stat::{fchmodat, utimensat, mkdirat};
-#[cfg(any(target_os = "linux",
-          target_os = "haiku",
-          target_os = "ios",
-          target_os = "macos",
-          target_os = "freebsd",
-          target_os = "netbsd"))]
-use nix::sys::stat::lutimes;
-#[cfg(not(target_os = "redox"))]
-use nix::sys::stat::{FchmodatFlags, UtimensatFlags};
-use nix::sys::stat::Mode;
+use nix::sys::stat::{fchmodat, mkdirat};
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+use nix::sys::stat::{futimens, utimes};
 
 #[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
 use nix::sys::stat::FileStat;
 
-#[cfg(not(target_os = "redox"))]
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
 use nix::sys::time::{TimeSpec, TimeVal, TimeValLike};
 #[cfg(not(target_os = "redox"))]
 use nix::unistd::chdir;
@@ -47,32 +56,35 @@
 #[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
 fn assert_stat_results(stat_result: Result<FileStat>) {
     let stats = stat_result.expect("stat call failed");
-    assert!(stats.st_dev > 0);      // must be positive integer, exact number machine dependent
-    assert!(stats.st_ino > 0);      // inode is positive integer, exact number machine dependent
-    assert!(stats.st_mode > 0);     // must be positive integer
-    assert_eq!(stats.st_nlink, 1);   // there links created, must be 1
-    assert_eq!(stats.st_size, 0);    // size is 0 because we did not write anything to the file
-    assert!(stats.st_blksize > 0);  // must be positive integer, exact number machine dependent
-    assert!(stats.st_blocks <= 16);  // Up to 16 blocks can be allocated for a blank file
+    assert!(stats.st_dev > 0); // must be positive integer, exact number machine dependent
+    assert!(stats.st_ino > 0); // inode is positive integer, exact number machine dependent
+    assert!(stats.st_mode > 0); // must be positive integer
+    assert_eq!(stats.st_nlink, 1); // there links created, must be 1
+    assert_eq!(stats.st_size, 0); // size is 0 because we did not write anything to the file
+    assert!(stats.st_blksize > 0); // must be positive integer, exact number machine dependent
+    assert!(stats.st_blocks <= 16); // Up to 16 blocks can be allocated for a blank file
 }
 
 #[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
 // (Android's st_blocks is ulonglong which is always non-negative.)
 #[cfg_attr(target_os = "android", allow(unused_comparisons))]
-#[allow(clippy::absurd_extreme_comparisons)]    // Not absurd on all OSes
+#[allow(clippy::absurd_extreme_comparisons)] // Not absurd on all OSes
 fn assert_lstat_results(stat_result: Result<FileStat>) {
     let stats = stat_result.expect("stat call failed");
-    assert!(stats.st_dev > 0);      // must be positive integer, exact number machine dependent
-    assert!(stats.st_ino > 0);      // inode is positive integer, exact number machine dependent
-    assert!(stats.st_mode > 0);     // must be positive integer
+    assert!(stats.st_dev > 0); // must be positive integer, exact number machine dependent
+    assert!(stats.st_ino > 0); // inode is positive integer, exact number machine dependent
+    assert!(stats.st_mode > 0); // must be positive integer
 
     // st_mode is c_uint (u32 on Android) while S_IFMT is mode_t
     // (u16 on Android), and that will be a compile error.
     // On other platforms they are the same (either both are u16 or u32).
-    assert_eq!((stats.st_mode as usize) & (S_IFMT as usize), S_IFLNK as usize); // should be a link
-    assert_eq!(stats.st_nlink, 1);   // there links created, must be 1
-    assert!(stats.st_size > 0);    // size is > 0 because it points to another file
-    assert!(stats.st_blksize > 0);  // must be positive integer, exact number machine dependent
+    assert_eq!(
+        (stats.st_mode as usize) & (S_IFMT as usize),
+        S_IFLNK as usize
+    ); // should be a link
+    assert_eq!(stats.st_nlink, 1); // there links created, must be 1
+    assert!(stats.st_size > 0); // size is > 0 because it points to another file
+    assert!(stats.st_blksize > 0); // must be positive integer, exact number machine dependent
 
     // st_blocks depends on whether the machine's file system uses fast
     // or slow symlinks, so just make sure it's not negative
@@ -101,13 +113,11 @@
     let tempdir = tempfile::tempdir().unwrap();
     let filename = tempdir.path().join("foo.txt");
     File::create(&filename).unwrap();
-    let dirfd = fcntl::open(tempdir.path(),
-                            fcntl::OFlag::empty(),
-                            stat::Mode::empty());
+    let dirfd =
+        fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty());
 
-    let result = stat::fstatat(dirfd.unwrap(),
-                               &filename,
-                               fcntl::AtFlags::empty());
+    let result =
+        stat::fstatat(dirfd.unwrap(), &filename, fcntl::AtFlags::empty());
     assert_stat_results(result);
 }
 
@@ -167,12 +177,15 @@
     let fullpath = tempdir.path().join(filename);
     File::create(&fullpath).unwrap();
 
-    let dirfd = fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty()).unwrap();
+    let dirfd =
+        fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
+            .unwrap();
 
     let mut mode1 = Mode::empty();
     mode1.insert(Mode::S_IRUSR);
     mode1.insert(Mode::S_IWUSR);
-    fchmodat(Some(dirfd), filename, mode1, FchmodatFlags::FollowSymlink).unwrap();
+    fchmodat(Some(dirfd), filename, mode1, FchmodatFlags::FollowSymlink)
+        .unwrap();
 
     let file_stat1 = stat(&fullpath).unwrap();
     assert_eq!(file_stat1.st_mode as mode_t & 0o7777, mode1.bits());
@@ -191,34 +204,42 @@
 ///
 /// The atime and mtime are expressed with a resolution of seconds because some file systems
 /// (like macOS's HFS+) do not have higher granularity.
-#[cfg(not(target_os = "redox"))]
-fn assert_times_eq(exp_atime_sec: u64, exp_mtime_sec: u64, attr: &fs::Metadata) {
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+fn assert_times_eq(
+    exp_atime_sec: u64,
+    exp_mtime_sec: u64,
+    attr: &fs::Metadata,
+) {
     assert_eq!(
         Duration::new(exp_atime_sec, 0),
-        attr.accessed().unwrap().duration_since(UNIX_EPOCH).unwrap());
+        attr.accessed().unwrap().duration_since(UNIX_EPOCH).unwrap()
+    );
     assert_eq!(
         Duration::new(exp_mtime_sec, 0),
-        attr.modified().unwrap().duration_since(UNIX_EPOCH).unwrap());
+        attr.modified().unwrap().duration_since(UNIX_EPOCH).unwrap()
+    );
 }
 
 #[test]
-#[cfg(not(target_os = "redox"))]
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
 fn test_utimes() {
     let tempdir = tempfile::tempdir().unwrap();
     let fullpath = tempdir.path().join("file");
     drop(File::create(&fullpath).unwrap());
 
-    utimes(&fullpath, &TimeVal::seconds(9990), &TimeVal::seconds(5550)).unwrap();
+    utimes(&fullpath, &TimeVal::seconds(9990), &TimeVal::seconds(5550))
+        .unwrap();
     assert_times_eq(9990, 5550, &fs::metadata(&fullpath).unwrap());
 }
 
 #[test]
-#[cfg(any(target_os = "linux",
-          target_os = "haiku",
-          target_os = "ios",
-          target_os = "macos",
-          target_os = "freebsd",
-          target_os = "netbsd"))]
+#[cfg(any(
+    target_os = "linux",
+    target_os = "ios",
+    target_os = "macos",
+    target_os = "freebsd",
+    target_os = "netbsd"
+))]
 fn test_lutimes() {
     let tempdir = tempfile::tempdir().unwrap();
     let target = tempdir.path().join("target");
@@ -227,31 +248,39 @@
     symlink(&target, &fullpath).unwrap();
 
     let exp_target_metadata = fs::symlink_metadata(&target).unwrap();
-    lutimes(&fullpath, &TimeVal::seconds(4560), &TimeVal::seconds(1230)).unwrap();
+    lutimes(&fullpath, &TimeVal::seconds(4560), &TimeVal::seconds(1230))
+        .unwrap();
     assert_times_eq(4560, 1230, &fs::symlink_metadata(&fullpath).unwrap());
 
     let target_metadata = fs::symlink_metadata(&target).unwrap();
-    assert_eq!(exp_target_metadata.accessed().unwrap(), target_metadata.accessed().unwrap(),
-               "atime of symlink target was unexpectedly modified");
-    assert_eq!(exp_target_metadata.modified().unwrap(), target_metadata.modified().unwrap(),
-               "mtime of symlink target was unexpectedly modified");
+    assert_eq!(
+        exp_target_metadata.accessed().unwrap(),
+        target_metadata.accessed().unwrap(),
+        "atime of symlink target was unexpectedly modified"
+    );
+    assert_eq!(
+        exp_target_metadata.modified().unwrap(),
+        target_metadata.modified().unwrap(),
+        "mtime of symlink target was unexpectedly modified"
+    );
 }
 
 #[test]
-#[cfg(not(target_os = "redox"))]
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
 fn test_futimens() {
     let tempdir = tempfile::tempdir().unwrap();
     let fullpath = tempdir.path().join("file");
     drop(File::create(&fullpath).unwrap());
 
-    let fd = fcntl::open(&fullpath, fcntl::OFlag::empty(), stat::Mode::empty()).unwrap();
+    let fd = fcntl::open(&fullpath, fcntl::OFlag::empty(), stat::Mode::empty())
+        .unwrap();
 
     futimens(fd, &TimeSpec::seconds(10), &TimeSpec::seconds(20)).unwrap();
     assert_times_eq(10, 20, &fs::metadata(&fullpath).unwrap());
 }
 
 #[test]
-#[cfg(not(target_os = "redox"))]
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
 fn test_utimensat() {
     let _dr = crate::DirRestore::new();
     let tempdir = tempfile::tempdir().unwrap();
@@ -259,16 +288,30 @@
     let fullpath = tempdir.path().join(filename);
     drop(File::create(&fullpath).unwrap());
 
-    let dirfd = fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty()).unwrap();
+    let dirfd =
+        fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
+            .unwrap();
 
-    utimensat(Some(dirfd), filename, &TimeSpec::seconds(12345), &TimeSpec::seconds(678),
-              UtimensatFlags::FollowSymlink).unwrap();
+    utimensat(
+        Some(dirfd),
+        filename,
+        &TimeSpec::seconds(12345),
+        &TimeSpec::seconds(678),
+        UtimensatFlags::FollowSymlink,
+    )
+    .unwrap();
     assert_times_eq(12345, 678, &fs::metadata(&fullpath).unwrap());
 
     chdir(tempdir.path()).unwrap();
 
-    utimensat(None, filename, &TimeSpec::seconds(500), &TimeSpec::seconds(800),
-              UtimensatFlags::FollowSymlink).unwrap();
+    utimensat(
+        None,
+        filename,
+        &TimeSpec::seconds(500),
+        &TimeSpec::seconds(800),
+        UtimensatFlags::FollowSymlink,
+    )
+    .unwrap();
     assert_times_eq(500, 800, &fs::metadata(&fullpath).unwrap());
 }
 
@@ -277,20 +320,27 @@
 fn test_mkdirat_success_path() {
     let tempdir = tempfile::tempdir().unwrap();
     let filename = "example_subdir";
-    let dirfd = fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty()).unwrap();
-    assert!((mkdirat(dirfd, filename, Mode::S_IRWXU)).is_ok());
+    let dirfd =
+        fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
+            .unwrap();
+    mkdirat(dirfd, filename, Mode::S_IRWXU).expect("mkdirat failed");
     assert!(Path::exists(&tempdir.path().join(filename)));
 }
 
 #[test]
-#[cfg(not(target_os = "redox"))]
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
 fn test_mkdirat_success_mode() {
-    let expected_bits = stat::SFlag::S_IFDIR.bits() | stat::Mode::S_IRWXU.bits();
+    let expected_bits =
+        stat::SFlag::S_IFDIR.bits() | stat::Mode::S_IRWXU.bits();
     let tempdir = tempfile::tempdir().unwrap();
     let filename = "example_subdir";
-    let dirfd = fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty()).unwrap();
-    assert!((mkdirat(dirfd, filename, Mode::S_IRWXU)).is_ok());
-    let permissions = fs::metadata(tempdir.path().join(filename)).unwrap().permissions();
+    let dirfd =
+        fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
+            .unwrap();
+    mkdirat(dirfd, filename, Mode::S_IRWXU).expect("mkdirat failed");
+    let permissions = fs::metadata(tempdir.path().join(filename))
+        .unwrap()
+        .permissions();
     let mode = permissions.mode();
     assert_eq!(mode as mode_t, expected_bits)
 }
@@ -299,19 +349,27 @@
 #[cfg(not(target_os = "redox"))]
 fn test_mkdirat_fail() {
     let tempdir = tempfile::tempdir().unwrap();
-    let not_dir_filename= "example_not_dir";
+    let not_dir_filename = "example_not_dir";
     let filename = "example_subdir_dir";
-    let dirfd = fcntl::open(&tempdir.path().join(not_dir_filename), fcntl::OFlag::O_CREAT,
-                            stat::Mode::empty()).unwrap();
+    let dirfd = fcntl::open(
+        &tempdir.path().join(not_dir_filename),
+        fcntl::OFlag::O_CREAT,
+        stat::Mode::empty(),
+    )
+    .unwrap();
     let result = mkdirat(dirfd, filename, Mode::S_IRWXU).unwrap_err();
     assert_eq!(result, Errno::ENOTDIR);
 }
 
 #[test]
-#[cfg(not(any(target_os = "freebsd",
-              target_os = "ios",
-              target_os = "macos",
-              target_os = "redox")))]
+#[cfg(not(any(
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "ios",
+    target_os = "macos",
+    target_os = "haiku",
+    target_os = "redox"
+)))]
 fn test_mknod() {
     use stat::{lstat, mknod, SFlag};
 
@@ -320,16 +378,20 @@
     let target = tempdir.path().join(file_name);
     mknod(&target, SFlag::S_IFREG, Mode::S_IRWXU, 0).unwrap();
     let mode = lstat(&target).unwrap().st_mode as mode_t;
-    assert!(mode & libc::S_IFREG == libc::S_IFREG);
-    assert!(mode & libc::S_IRWXU == libc::S_IRWXU);
+    assert_eq!(mode & libc::S_IFREG, libc::S_IFREG);
+    assert_eq!(mode & libc::S_IRWXU, libc::S_IRWXU);
 }
 
 #[test]
-#[cfg(not(any(target_os = "freebsd",
-              target_os = "illumos",
-              target_os = "ios",
-              target_os = "macos",
-              target_os = "redox")))]
+#[cfg(not(any(
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "illumos",
+    target_os = "ios",
+    target_os = "macos",
+    target_os = "haiku",
+    target_os = "redox"
+)))]
 fn test_mknodat() {
     use fcntl::{AtFlags, OFlag};
     use nix::dir::Dir;
@@ -337,7 +399,8 @@
 
     let file_name = "test_file";
     let tempdir = tempfile::tempdir().unwrap();
-    let target_dir = Dir::open(tempdir.path(), OFlag::O_DIRECTORY, Mode::S_IRWXU).unwrap();
+    let target_dir =
+        Dir::open(tempdir.path(), OFlag::O_DIRECTORY, Mode::S_IRWXU).unwrap();
     mknodat(
         target_dir.as_raw_fd(),
         file_name,
@@ -353,6 +416,6 @@
     )
     .unwrap()
     .st_mode as mode_t;
-    assert!(mode & libc::S_IFREG == libc::S_IFREG);
-    assert!(mode & libc::S_IRWXU == libc::S_IRWXU);
+    assert_eq!(mode & libc::S_IFREG, libc::S_IFREG);
+    assert_eq!(mode & libc::S_IRWXU, libc::S_IRWXU);
 }
diff --git a/test/test_time.rs b/test/test_time.rs
index dc307e5..5f76e61 100644
--- a/test/test_time.rs
+++ b/test/test_time.rs
@@ -11,12 +11,12 @@
 #[cfg(not(target_os = "redox"))]
 #[test]
 pub fn test_clock_getres() {
-    assert!(nix::time::clock_getres(ClockId::CLOCK_REALTIME).is_ok());
+    nix::time::clock_getres(ClockId::CLOCK_REALTIME).expect("assertion failed");
 }
 
 #[test]
 pub fn test_clock_gettime() {
-    assert!(clock_gettime(ClockId::CLOCK_REALTIME).is_ok());
+    clock_gettime(ClockId::CLOCK_REALTIME).expect("assertion failed");
 }
 
 #[cfg(any(
@@ -29,18 +29,18 @@
 #[test]
 pub fn test_clock_getcpuclockid() {
     let clock_id = clock_getcpuclockid(nix::unistd::Pid::this()).unwrap();
-    assert!(clock_gettime(clock_id).is_ok());
+    clock_gettime(clock_id).unwrap();
 }
 
 #[cfg(not(target_os = "redox"))]
 #[test]
 pub fn test_clock_id_res() {
-    assert!(ClockId::CLOCK_REALTIME.res().is_ok());
+    ClockId::CLOCK_REALTIME.res().unwrap();
 }
 
 #[test]
 pub fn test_clock_id_now() {
-    assert!(ClockId::CLOCK_REALTIME.now().is_ok());
+    ClockId::CLOCK_REALTIME.now().unwrap();
 }
 
 #[cfg(any(
@@ -52,7 +52,8 @@
 ))]
 #[test]
 pub fn test_clock_id_pid_cpu_clock_id() {
-    assert!(ClockId::pid_cpu_clock_id(nix::unistd::Pid::this())
+    ClockId::pid_cpu_clock_id(nix::unistd::Pid::this())
         .map(ClockId::now)
-        .is_ok());
+        .unwrap()
+        .unwrap();
 }
diff --git a/test/test_timer.rs b/test/test_timer.rs
new file mode 100644
index 0000000..ffd1468
--- /dev/null
+++ b/test/test_timer.rs
@@ -0,0 +1,102 @@
+use nix::sys::signal::{
+    sigaction, SaFlags, SigAction, SigEvent, SigHandler, SigSet, SigevNotify,
+    Signal,
+};
+use nix::sys::timer::{Expiration, Timer, TimerSetTimeFlags};
+use nix::time::ClockId;
+use std::convert::TryFrom;
+use std::sync::atomic::{AtomicBool, Ordering};
+use std::thread;
+use std::time::{Duration, Instant};
+
+const SIG: Signal = Signal::SIGALRM;
+static ALARM_CALLED: AtomicBool = AtomicBool::new(false);
+
+pub extern "C" fn handle_sigalarm(raw_signal: libc::c_int) {
+    let signal = Signal::try_from(raw_signal).unwrap();
+    if signal == SIG {
+        ALARM_CALLED.store(true, Ordering::Release);
+    }
+}
+
+#[test]
+fn alarm_fires() {
+    // Avoid interfering with other signal using tests by taking a mutex shared
+    // among other tests in this crate.
+    let _m = crate::SIGNAL_MTX.lock();
+    const TIMER_PERIOD: Duration = Duration::from_millis(100);
+
+    //
+    // Setup
+    //
+
+    // Create a handler for the test signal, `SIG`. The handler is responsible
+    // for flipping `ALARM_CALLED`.
+    let handler = SigHandler::Handler(handle_sigalarm);
+    let signal_action =
+        SigAction::new(handler, SaFlags::SA_RESTART, SigSet::empty());
+    let old_handler = unsafe {
+        sigaction(SIG, &signal_action)
+            .expect("unable to set signal handler for alarm")
+    };
+
+    // Create the timer. We use the monotonic clock here, though any would do
+    // really. The timer is set to fire every 250 milliseconds with no delay for
+    // the initial firing.
+    let clockid = ClockId::CLOCK_MONOTONIC;
+    let sigevent = SigEvent::new(SigevNotify::SigevSignal {
+        signal: SIG,
+        si_value: 0,
+    });
+    let mut timer =
+        Timer::new(clockid, sigevent).expect("failed to create timer");
+    let expiration = Expiration::Interval(TIMER_PERIOD.into());
+    let flags = TimerSetTimeFlags::empty();
+    timer.set(expiration, flags).expect("could not set timer");
+
+    //
+    // Test
+    //
+
+    // Determine that there's still an expiration tracked by the
+    // timer. Depending on when this runs either an `Expiration::Interval` or
+    // `Expiration::IntervalDelayed` will be present. That is, if the timer has
+    // not fired yet we'll get our original `expiration`, else the one that
+    // represents a delay to the next expiration. We're only interested in the
+    // timer still being extant.
+    match timer.get() {
+        Ok(Some(exp)) => assert!(matches!(
+            exp,
+            Expiration::Interval(..) | Expiration::IntervalDelayed(..)
+        )),
+        _ => panic!("timer lost its expiration"),
+    }
+
+    // Wait for 2 firings of the alarm before checking that it has fired and
+    // been handled at least the once. If we wait for 3 seconds and the handler
+    // is never called something has gone sideways and the test fails.
+    let starttime = Instant::now();
+    loop {
+        thread::sleep(2 * TIMER_PERIOD);
+        if ALARM_CALLED.load(Ordering::Acquire) {
+            break;
+        }
+        if starttime.elapsed() > Duration::from_secs(3) {
+            panic!("Timeout waiting for SIGALRM");
+        }
+    }
+
+    // Cleanup:
+    // 1) deregister the OS's timer.
+    // 2) Wait for a full timer period, since POSIX does not require that
+    //    disabling the timer will clear pending signals, and on NetBSD at least
+    //    it does not.
+    // 2) Replace the old signal handler now that we've completed the test. If
+    //    the test fails this process panics, so the fact we might not get here
+    //    is okay.
+    drop(timer);
+    thread::sleep(TIMER_PERIOD);
+    unsafe {
+        sigaction(SIG, &old_handler).expect("unable to reset signal handler");
+    }
+}
diff --git a/test/test_unistd.rs b/test/test_unistd.rs
index 61062ad..9e20f97 100644
--- a/test/test_unistd.rs
+++ b/test/test_unistd.rs
@@ -1,15 +1,24 @@
-#[cfg(not(target_os = "redox"))]
-use nix::fcntl::{self, open, readlink};
-use nix::fcntl::OFlag;
-use nix::unistd::*;
-use nix::unistd::ForkResult::*;
-#[cfg(not(target_os = "redox"))]
-use nix::sys::signal::{SaFlags, SigAction, SigHandler, SigSet, Signal, sigaction};
-use nix::sys::wait::*;
-use nix::sys::stat::{self, Mode, SFlag};
-#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
-use nix::pty::{posix_openpt, grantpt, unlockpt, ptsname};
+use libc::{_exit, mode_t, off_t};
 use nix::errno::Errno;
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+use nix::fcntl::readlink;
+use nix::fcntl::OFlag;
+#[cfg(not(target_os = "redox"))]
+use nix::fcntl::{self, open};
+#[cfg(not(any(
+    target_os = "redox",
+    target_os = "fuchsia",
+    target_os = "haiku"
+)))]
+use nix::pty::{grantpt, posix_openpt, ptsname, unlockpt};
+#[cfg(not(target_os = "redox"))]
+use nix::sys::signal::{
+    sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal,
+};
+use nix::sys::stat::{self, Mode, SFlag};
+use nix::sys::wait::*;
+use nix::unistd::ForkResult::*;
+use nix::unistd::*;
 use std::env;
 #[cfg(not(any(target_os = "fuchsia", target_os = "redox")))]
 use std::ffi::CString;
@@ -18,10 +27,13 @@
 use std::fs::{self, File};
 use std::io::Write;
 use std::os::unix::prelude::*;
-#[cfg(not(any(target_os = "fuchsia", target_os = "redox")))]
+#[cfg(not(any(
+    target_os = "fuchsia",
+    target_os = "redox",
+    target_os = "haiku"
+)))]
 use std::path::Path;
 use tempfile::{tempdir, tempfile};
-use libc::{_exit, mode_t, off_t};
 
 use crate::*;
 
@@ -31,7 +43,7 @@
     let _m = crate::FORK_MTX.lock();
 
     // Safe: Child only calls `_exit`, which is signal-safe
-    match unsafe{fork()}.expect("Error: Fork Failed") {
+    match unsafe { fork() }.expect("Error: Fork Failed") {
         Child => unsafe { _exit(0) },
         Parent { child } => {
             // assert that child was created and pid > 0
@@ -40,16 +52,17 @@
             let wait_status = waitpid(child, None);
             match wait_status {
                 // assert that waitpid returned correct status and the pid is the one of the child
-                Ok(WaitStatus::Exited(pid_t, _)) =>  assert_eq!(pid_t, child),
+                Ok(WaitStatus::Exited(pid_t, _)) => assert_eq!(pid_t, child),
 
                 // panic, must never happen
-                s @ Ok(_) => panic!("Child exited {:?}, should never happen", s),
+                s @ Ok(_) => {
+                    panic!("Child exited {:?}, should never happen", s)
+                }
 
                 // panic, waitpid should never fail
-                Err(s) => panic!("Error: waitpid returned Err({:?}", s)
+                Err(s) => panic!("Error: waitpid returned Err({:?}", s),
             }
-
-        },
+        }
     }
 }
 
@@ -59,14 +72,14 @@
     let _m = crate::FORK_MTX.lock();
 
     // Safe: Child only calls `_exit`, which is signal-safe
-    match unsafe{fork()}.expect("Error: Fork Failed") {
+    match unsafe { fork() }.expect("Error: Fork Failed") {
         Child => unsafe { _exit(0) },
         Parent { child } => {
             let wait_status = wait();
 
             // just assert that (any) one child returns with WaitStatus::Exited
             assert_eq!(wait_status, Ok(WaitStatus::Exited(child, 0)));
-        },
+        }
     }
 }
 
@@ -80,15 +93,15 @@
         Ok((fd, path)) => {
             close(fd).unwrap();
             unlink(path.as_path()).unwrap();
-        },
-        Err(e) => panic!("mkstemp failed: {}", e)
+        }
+        Err(e) => panic!("mkstemp failed: {}", e),
     }
 }
 
 #[test]
 fn test_mkstemp_directory() {
     // mkstemp should fail if a directory is given
-    assert!(mkstemp(&env::temp_dir()).is_err());
+    mkstemp(&env::temp_dir()).expect_err("assertion failed");
 }
 
 #[test]
@@ -101,20 +114,24 @@
 
     let stats = stat::stat(&mkfifo_fifo).unwrap();
     let typ = stat::SFlag::from_bits_truncate(stats.st_mode as mode_t);
-    assert!(typ == SFlag::S_IFIFO);
+    assert_eq!(typ, SFlag::S_IFIFO);
 }
 
 #[test]
 #[cfg(not(target_os = "redox"))]
 fn test_mkfifo_directory() {
     // mkfifo should fail if a directory is given
-    assert!(mkfifo(&env::temp_dir(), Mode::S_IRUSR).is_err());
+    mkfifo(&env::temp_dir(), Mode::S_IRUSR).expect_err("assertion failed");
 }
 
 #[test]
 #[cfg(not(any(
-    target_os = "macos", target_os = "ios",
-    target_os = "android", target_os = "redox")))]
+    target_os = "macos",
+    target_os = "ios",
+    target_os = "android",
+    target_os = "redox",
+    target_os = "haiku"
+)))]
 fn test_mkfifoat_none() {
     let _m = crate::CWD_LOCK.read();
 
@@ -130,8 +147,12 @@
 
 #[test]
 #[cfg(not(any(
-    target_os = "macos", target_os = "ios",
-    target_os = "android", target_os = "redox")))]
+    target_os = "macos",
+    target_os = "ios",
+    target_os = "android",
+    target_os = "redox",
+    target_os = "haiku"
+)))]
 fn test_mkfifoat() {
     use nix::fcntl;
 
@@ -141,26 +162,36 @@
 
     mkfifoat(Some(dirfd), mkfifoat_name, Mode::S_IRUSR).unwrap();
 
-    let stats = stat::fstatat(dirfd, mkfifoat_name, fcntl::AtFlags::empty()).unwrap();
+    let stats =
+        stat::fstatat(dirfd, mkfifoat_name, fcntl::AtFlags::empty()).unwrap();
     let typ = stat::SFlag::from_bits_truncate(stats.st_mode);
     assert_eq!(typ, SFlag::S_IFIFO);
 }
 
 #[test]
 #[cfg(not(any(
-    target_os = "macos", target_os = "ios",
-    target_os = "android", target_os = "redox")))]
+    target_os = "macos",
+    target_os = "ios",
+    target_os = "android",
+    target_os = "redox",
+    target_os = "haiku"
+)))]
 fn test_mkfifoat_directory_none() {
     let _m = crate::CWD_LOCK.read();
 
     // mkfifoat should fail if a directory is given
-    assert!(mkfifoat(None, &env::temp_dir(), Mode::S_IRUSR).is_err());
+    mkfifoat(None, &env::temp_dir(), Mode::S_IRUSR)
+        .expect_err("assertion failed");
 }
 
 #[test]
 #[cfg(not(any(
-    target_os = "macos", target_os = "ios",
-    target_os = "android", target_os = "redox")))]
+    target_os = "macos",
+    target_os = "ios",
+    target_os = "android",
+    target_os = "redox",
+    target_os = "haiku"
+)))]
 fn test_mkfifoat_directory() {
     // mkfifoat should fail if a directory is given
     let tempdir = tempdir().unwrap();
@@ -168,7 +199,8 @@
     let mkfifoat_dir = "mkfifoat_dir";
     stat::mkdirat(dirfd, mkfifoat_dir, Mode::S_IRUSR).unwrap();
 
-    assert!(mkfifoat(Some(dirfd), mkfifoat_dir, Mode::S_IRUSR).is_err());
+    mkfifoat(Some(dirfd), mkfifoat_dir, Mode::S_IRUSR)
+        .expect_err("assertion failed");
 }
 
 #[test]
@@ -201,7 +233,13 @@
 
 #[test]
 // `getgroups()` and `setgroups()` do not behave as expected on Apple platforms
-#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox", target_os = "fuchsia")))]
+#[cfg(not(any(
+    target_os = "ios",
+    target_os = "macos",
+    target_os = "redox",
+    target_os = "fuchsia",
+    target_os = "haiku"
+)))]
 fn test_setgroups() {
     // Skip this test when not run as root as `setgroups()` requires root.
     skip_if_not_root!("test_setgroups");
@@ -224,11 +262,14 @@
 
 #[test]
 // `getgroups()` and `setgroups()` do not behave as expected on Apple platforms
-#[cfg(not(any(target_os = "ios",
-              target_os = "macos",
-              target_os = "redox",
-              target_os = "fuchsia",
-              target_os = "illumos")))]
+#[cfg(not(any(
+    target_os = "ios",
+    target_os = "macos",
+    target_os = "redox",
+    target_os = "fuchsia",
+    target_os = "haiku",
+    target_os = "illumos"
+)))]
 fn test_initgroups() {
     // Skip this test when not run as root as `initgroups()` and `setgroups()`
     // require root.
@@ -259,7 +300,7 @@
 }
 
 #[cfg(not(any(target_os = "fuchsia", target_os = "redox")))]
-macro_rules! execve_test_factory(
+macro_rules! execve_test_factory (
     ($test_name:ident, $syscall:ident, $exe: expr $(, $pathname:expr, $flags:expr)*) => (
 
     #[cfg(test)]
@@ -359,36 +400,32 @@
     )
 );
 
-cfg_if!{
+cfg_if! {
     if #[cfg(target_os = "android")] {
         execve_test_factory!(test_execve, execve, CString::new("/system/bin/sh").unwrap().as_c_str());
         execve_test_factory!(test_fexecve, fexecve, File::open("/system/bin/sh").unwrap().into_raw_fd());
-    } else if #[cfg(any(target_os = "freebsd",
+    } else if #[cfg(any(target_os = "dragonfly",
+                        target_os = "freebsd",
                         target_os = "linux"))] {
         // These tests frequently fail on musl, probably due to
         // https://github.com/nix-rust/nix/issues/555
         execve_test_factory!(test_execve, execve, CString::new("/bin/sh").unwrap().as_c_str());
         execve_test_factory!(test_fexecve, fexecve, File::open("/bin/sh").unwrap().into_raw_fd());
-    } else if #[cfg(any(target_os = "dragonfly",
-                        target_os = "illumos",
+    } else if #[cfg(any(target_os = "illumos",
                         target_os = "ios",
                         target_os = "macos",
                         target_os = "netbsd",
                         target_os = "openbsd",
                         target_os = "solaris"))] {
         execve_test_factory!(test_execve, execve, CString::new("/bin/sh").unwrap().as_c_str());
-        // No fexecve() on DragonFly, ios, macos, NetBSD, OpenBSD.
-        //
-        // Note for NetBSD and OpenBSD: although rust-lang/libc includes it
-        // (under unix/bsd/netbsdlike/) fexecve is not currently implemented on
-        // NetBSD nor on OpenBSD.
+        // No fexecve() on ios, macos, NetBSD, OpenBSD.
     }
 }
 
 #[cfg(any(target_os = "haiku", target_os = "linux", target_os = "openbsd"))]
 execve_test_factory!(test_execvpe, execvpe, &CString::new("sh").unwrap());
 
-cfg_if!{
+cfg_if! {
     if #[cfg(target_os = "android")] {
         use nix::fcntl::AtFlags;
         execve_test_factory!(test_execveat_empty, execveat,
@@ -421,10 +458,10 @@
     let tmpdir_path = tmpdir.path().canonicalize().unwrap();
     let tmpdir_fd = File::open(&tmpdir_path).unwrap().into_raw_fd();
 
-    assert!(fchdir(tmpdir_fd).is_ok());
+    fchdir(tmpdir_fd).expect("assertion failed");
     assert_eq!(getcwd().unwrap(), tmpdir_path);
 
-    assert!(close(tmpdir_fd).is_ok());
+    close(tmpdir_fd).expect("assertion failed");
 }
 
 #[test]
@@ -434,7 +471,7 @@
 
     let tmpdir = tempdir().unwrap();
     let tmpdir_path = tmpdir.path().canonicalize().unwrap();
-    assert!(chdir(&tmpdir_path).is_ok());
+    chdir(&tmpdir_path).expect("assertion failed");
     assert_eq!(getcwd().unwrap(), tmpdir_path);
 
     // make path 500 chars longer so that buffer doubling in getcwd
@@ -445,9 +482,10 @@
     for _ in 0..5 {
         let newdir = "a".repeat(100);
         inner_tmp_dir.push(newdir);
-        assert!(mkdir(inner_tmp_dir.as_path(), Mode::S_IRWXU).is_ok());
+        mkdir(inner_tmp_dir.as_path(), Mode::S_IRWXU)
+            .expect("assertion failed");
     }
-    assert!(chdir(inner_tmp_dir.as_path()).is_ok());
+    chdir(inner_tmp_dir.as_path()).expect("assertion failed");
     assert_eq!(getcwd().unwrap(), inner_tmp_dir.as_path());
 }
 
@@ -502,7 +540,8 @@
 
     let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
 
-    fchownat(Some(dirfd), "file", uid, gid, FchownatFlags::FollowSymlink).unwrap();
+    fchownat(Some(dirfd), "file", uid, gid, FchownatFlags::FollowSymlink)
+        .unwrap();
 
     chdir(tempdir.path()).unwrap();
     fchownat(None, "file", uid, gid, FchownatFlags::FollowSymlink).unwrap();
@@ -545,7 +584,7 @@
     close(tmpfd).unwrap();
 }
 
-cfg_if!{
+cfg_if! {
     if #[cfg(any(target_os = "android", target_os = "linux"))] {
         macro_rules! require_acct{
             () => {
@@ -559,7 +598,7 @@
                 skip_if_jailed!("test_acct");
             }
         }
-    } else if #[cfg(not(any(target_os = "redox", target_os = "fuchsia")))] {
+    } else if #[cfg(not(any(target_os = "redox", target_os = "fuchsia", target_os = "haiku")))] {
         macro_rules! require_acct{
             () => {
                 skip_if_not_root!("test_acct");
@@ -569,11 +608,15 @@
 }
 
 #[test]
-#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
+#[cfg(not(any(
+    target_os = "redox",
+    target_os = "fuchsia",
+    target_os = "haiku"
+)))]
 fn test_acct() {
-    use tempfile::NamedTempFile;
     use std::process::Command;
     use std::{thread, time};
+    use tempfile::NamedTempFile;
 
     let _m = crate::FORK_MTX.lock();
     require_acct!();
@@ -584,9 +627,11 @@
     acct::enable(path).unwrap();
 
     loop {
-        Command::new("echo").arg("Hello world");
+        Command::new("echo").arg("Hello world").output().unwrap();
         let len = fs::metadata(path).unwrap().len();
-        if len > 0 { break; }
+        if len > 0 {
+            break;
+        }
         thread::sleep(time::Duration::from_millis(10));
     }
     acct::disable().unwrap();
@@ -597,21 +642,36 @@
     let f = tempfile().unwrap();
     // AFAIK, PATH_MAX is limited on all platforms, so it makes a good test
     let path_max = fpathconf(f.as_raw_fd(), PathconfVar::PATH_MAX);
-    assert!(path_max.expect("fpathconf failed").expect("PATH_MAX is unlimited") > 0);
+    assert!(
+        path_max
+            .expect("fpathconf failed")
+            .expect("PATH_MAX is unlimited")
+            > 0
+    );
 }
 
 #[test]
 fn test_pathconf_limited() {
     // AFAIK, PATH_MAX is limited on all platforms, so it makes a good test
     let path_max = pathconf("/", PathconfVar::PATH_MAX);
-    assert!(path_max.expect("pathconf failed").expect("PATH_MAX is unlimited") > 0);
+    assert!(
+        path_max
+            .expect("pathconf failed")
+            .expect("PATH_MAX is unlimited")
+            > 0
+    );
 }
 
 #[test]
 fn test_sysconf_limited() {
     // AFAIK, OPEN_MAX is limited on all platforms, so it makes a good test
     let open_max = sysconf(SysconfVar::OPEN_MAX);
-    assert!(open_max.expect("sysconf failed").expect("OPEN_MAX is unlimited") > 0);
+    assert!(
+        open_max
+            .expect("sysconf failed")
+            .expect("OPEN_MAX is unlimited")
+            > 0
+    );
 }
 
 #[cfg(target_os = "freebsd")]
@@ -624,23 +684,34 @@
     assert!(open_max.expect("sysconf failed").is_none())
 }
 
-
-#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg(any(
+    target_os = "android",
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "linux",
+    target_os = "openbsd"
+))]
 #[test]
 fn test_getresuid() {
     let resuids = getresuid().unwrap();
-    assert!(resuids.real.as_raw() != libc::uid_t::max_value());
-    assert!(resuids.effective.as_raw() != libc::uid_t::max_value());
-    assert!(resuids.saved.as_raw() != libc::uid_t::max_value());
+    assert_ne!(resuids.real.as_raw(), libc::uid_t::MAX);
+    assert_ne!(resuids.effective.as_raw(), libc::uid_t::MAX);
+    assert_ne!(resuids.saved.as_raw(), libc::uid_t::MAX);
 }
 
-#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg(any(
+    target_os = "android",
+    target_os = "dragonfly",
+    target_os = "freebsd",
+    target_os = "linux",
+    target_os = "openbsd"
+))]
 #[test]
 fn test_getresgid() {
     let resgids = getresgid().unwrap();
-    assert!(resgids.real.as_raw() != libc::gid_t::max_value());
-    assert!(resgids.effective.as_raw() != libc::gid_t::max_value());
-    assert!(resgids.saved.as_raw() != libc::gid_t::max_value());
+    assert_ne!(resgids.real.as_raw(), libc::gid_t::MAX);
+    assert_ne!(resgids.effective.as_raw(), libc::gid_t::MAX);
+    assert_ne!(resgids.saved.as_raw(), libc::gid_t::MAX);
 }
 
 // Test that we can create a pair of pipes.  No need to verify that they pass
@@ -648,25 +719,31 @@
 #[test]
 fn test_pipe() {
     let (fd0, fd1) = pipe().unwrap();
-    let m0 = stat::SFlag::from_bits_truncate(stat::fstat(fd0).unwrap().st_mode as mode_t);
+    let m0 = stat::SFlag::from_bits_truncate(
+        stat::fstat(fd0).unwrap().st_mode as mode_t,
+    );
     // S_IFIFO means it's a pipe
     assert_eq!(m0, SFlag::S_IFIFO);
-    let m1 = stat::SFlag::from_bits_truncate(stat::fstat(fd1).unwrap().st_mode as mode_t);
+    let m1 = stat::SFlag::from_bits_truncate(
+        stat::fstat(fd1).unwrap().st_mode as mode_t,
+    );
     assert_eq!(m1, SFlag::S_IFIFO);
 }
 
 // pipe2(2) is the same as pipe(2), except it allows setting some flags.  Check
 // that we can set a flag.
-#[cfg(any(target_os = "android",
-          target_os = "dragonfly",
-          target_os = "emscripten",
-          target_os = "freebsd",
-          target_os = "illumos",
-          target_os = "linux",
-          target_os = "netbsd",
-          target_os = "openbsd",
-          target_os = "redox",
-          target_os = "solaris"))]
+#[cfg(any(
+    target_os = "android",
+    target_os = "dragonfly",
+    target_os = "emscripten",
+    target_os = "freebsd",
+    target_os = "illumos",
+    target_os = "linux",
+    target_os = "netbsd",
+    target_os = "openbsd",
+    target_os = "redox",
+    target_os = "solaris"
+))]
 #[test]
 fn test_pipe2() {
     use nix::fcntl::{fcntl, FcntlArg, FdFlag};
@@ -721,8 +798,13 @@
 
 // Used in `test_alarm`.
 #[cfg(not(target_os = "redox"))]
-pub extern fn alarm_signal_handler(raw_signal: libc::c_int) {
-    assert_eq!(raw_signal, libc::SIGALRM, "unexpected signal: {}", raw_signal);
+pub extern "C" fn alarm_signal_handler(raw_signal: libc::c_int) {
+    assert_eq!(
+        raw_signal,
+        libc::SIGALRM,
+        "unexpected signal: {}",
+        raw_signal
+    );
     unsafe { ALARM_CALLED = true };
 }
 
@@ -730,15 +812,16 @@
 #[cfg(not(target_os = "redox"))]
 fn test_alarm() {
     use std::{
-        time::{Duration, Instant,},
-        thread
+        thread,
+        time::{Duration, Instant},
     };
 
     // Maybe other tests that fork interfere with this one?
     let _m = crate::SIGNAL_MTX.lock();
 
     let handler = SigHandler::Handler(alarm_signal_handler);
-    let signal_action = SigAction::new(handler, SaFlags::SA_RESTART, SigSet::empty());
+    let signal_action =
+        SigAction::new(handler, SaFlags::SA_RESTART, SigSet::empty());
     let old_handler = unsafe {
         sigaction(Signal::SIGALRM, &signal_action)
             .expect("unable to set signal handler for alarm")
@@ -755,7 +838,7 @@
     let starttime = Instant::now();
     loop {
         thread::sleep(Duration::from_millis(100));
-        if unsafe { ALARM_CALLED} {
+        if unsafe { ALARM_CALLED } {
             break;
         }
         if starttime.elapsed() > Duration::from_secs(3) {
@@ -782,7 +865,7 @@
 }
 
 #[test]
-#[cfg(not(target_os = "redox"))]
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
 fn test_symlinkat() {
     let _m = crate::CWD_LOCK.read();
 
@@ -810,7 +893,7 @@
 }
 
 #[test]
-#[cfg(not(target_os = "redox"))]
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
 fn test_linkat_file() {
     let tempdir = tempdir().unwrap();
     let oldfilename = "foo.txt";
@@ -820,18 +903,27 @@
     let newfilepath = tempdir.path().join(newfilename);
 
     // Create file
-    File::create(&oldfilepath).unwrap();
+    File::create(oldfilepath).unwrap();
 
     // Get file descriptor for base directory
-    let dirfd = fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty()).unwrap();
+    let dirfd =
+        fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
+            .unwrap();
 
     // Attempt hard link file at relative path
-    linkat(Some(dirfd), oldfilename, Some(dirfd), newfilename, LinkatFlags::SymlinkFollow).unwrap();
+    linkat(
+        Some(dirfd),
+        oldfilename,
+        Some(dirfd),
+        newfilename,
+        LinkatFlags::SymlinkFollow,
+    )
+    .unwrap();
     assert!(newfilepath.exists());
 }
 
 #[test]
-#[cfg(not(target_os = "redox"))]
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
 fn test_linkat_olddirfd_none() {
     let _dr = crate::DirRestore::new();
 
@@ -844,19 +936,31 @@
     let newfilepath = tempdir_newfile.path().join(newfilename);
 
     // Create file
-    File::create(&oldfilepath).unwrap();
+    File::create(oldfilepath).unwrap();
 
     // Get file descriptor for base directory of new file
-    let dirfd = fcntl::open(tempdir_newfile.path(), fcntl::OFlag::empty(), stat::Mode::empty()).unwrap();
+    let dirfd = fcntl::open(
+        tempdir_newfile.path(),
+        fcntl::OFlag::empty(),
+        stat::Mode::empty(),
+    )
+    .unwrap();
 
     // Attempt hard link file using curent working directory as relative path for old file path
     chdir(tempdir_oldfile.path()).unwrap();
-    linkat(None, oldfilename, Some(dirfd), newfilename, LinkatFlags::SymlinkFollow).unwrap();
+    linkat(
+        None,
+        oldfilename,
+        Some(dirfd),
+        newfilename,
+        LinkatFlags::SymlinkFollow,
+    )
+    .unwrap();
     assert!(newfilepath.exists());
 }
 
 #[test]
-#[cfg(not(target_os = "redox"))]
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
 fn test_linkat_newdirfd_none() {
     let _dr = crate::DirRestore::new();
 
@@ -869,19 +973,36 @@
     let newfilepath = tempdir_newfile.path().join(newfilename);
 
     // Create file
-    File::create(&oldfilepath).unwrap();
+    File::create(oldfilepath).unwrap();
 
     // Get file descriptor for base directory of old file
-    let dirfd = fcntl::open(tempdir_oldfile.path(), fcntl::OFlag::empty(), stat::Mode::empty()).unwrap();
+    let dirfd = fcntl::open(
+        tempdir_oldfile.path(),
+        fcntl::OFlag::empty(),
+        stat::Mode::empty(),
+    )
+    .unwrap();
 
     // Attempt hard link file using current working directory as relative path for new file path
     chdir(tempdir_newfile.path()).unwrap();
-    linkat(Some(dirfd), oldfilename, None, newfilename, LinkatFlags::SymlinkFollow).unwrap();
+    linkat(
+        Some(dirfd),
+        oldfilename,
+        None,
+        newfilename,
+        LinkatFlags::SymlinkFollow,
+    )
+    .unwrap();
     assert!(newfilepath.exists());
 }
 
 #[test]
-#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox")))]
+#[cfg(not(any(
+    target_os = "ios",
+    target_os = "macos",
+    target_os = "redox",
+    target_os = "haiku"
+)))]
 fn test_linkat_no_follow_symlink() {
     let _m = crate::CWD_LOCK.read();
 
@@ -902,23 +1023,29 @@
     symlinkat(&oldfilepath, None, &symoldfilepath).unwrap();
 
     // Get file descriptor for base directory
-    let dirfd = fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty()).unwrap();
+    let dirfd =
+        fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
+            .unwrap();
 
     // Attempt link symlink of file at relative path
-    linkat(Some(dirfd), symoldfilename, Some(dirfd), newfilename, LinkatFlags::NoSymlinkFollow).unwrap();
+    linkat(
+        Some(dirfd),
+        symoldfilename,
+        Some(dirfd),
+        newfilename,
+        LinkatFlags::NoSymlinkFollow,
+    )
+    .unwrap();
 
     // Assert newfile is actually a symlink to oldfile.
     assert_eq!(
-        readlink(&newfilepath)
-            .unwrap()
-            .to_str()
-            .unwrap(),
+        readlink(&newfilepath).unwrap().to_str().unwrap(),
         oldfilepath.to_str().unwrap()
     );
 }
 
 #[test]
-#[cfg(not(target_os = "redox"))]
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
 fn test_linkat_follow_symlink() {
     let _m = crate::CWD_LOCK.read();
 
@@ -939,15 +1066,26 @@
     symlinkat(&oldfilepath, None, &symoldfilepath).unwrap();
 
     // Get file descriptor for base directory
-    let dirfd = fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty()).unwrap();
+    let dirfd =
+        fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
+            .unwrap();
 
     // Attempt link target of symlink of file at relative path
-    linkat(Some(dirfd), symoldfilename, Some(dirfd), newfilename, LinkatFlags::SymlinkFollow).unwrap();
+    linkat(
+        Some(dirfd),
+        symoldfilename,
+        Some(dirfd),
+        newfilename,
+        LinkatFlags::SymlinkFollow,
+    )
+    .unwrap();
 
     let newfilestat = stat::stat(&newfilepath).unwrap();
 
     // Check the file type of the new link
-    assert_eq!((stat::SFlag::from_bits_truncate(newfilestat.st_mode as mode_t) & SFlag::S_IFMT),
+    assert_eq!(
+        (stat::SFlag::from_bits_truncate(newfilestat.st_mode as mode_t)
+            & SFlag::S_IFMT),
         SFlag::S_IFREG
     );
 
@@ -963,15 +1101,18 @@
     let dirpath = tempdir.path().join(dirname);
 
     // Create dir
-    DirBuilder::new().recursive(true).create(&dirpath).unwrap();
+    DirBuilder::new().recursive(true).create(dirpath).unwrap();
 
     // Get file descriptor for base directory
-    let dirfd = fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty()).unwrap();
+    let dirfd =
+        fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
+            .unwrap();
 
     // Attempt unlink dir at relative path without proper flag
-    let err_result = unlinkat(Some(dirfd), dirname, UnlinkatFlags::NoRemoveDir).unwrap_err();
+    let err_result =
+        unlinkat(Some(dirfd), dirname, UnlinkatFlags::NoRemoveDir).unwrap_err();
     assert!(err_result == Errno::EISDIR || err_result == Errno::EPERM);
- }
+}
 
 #[test]
 #[cfg(not(target_os = "redox"))]
@@ -984,12 +1125,14 @@
     DirBuilder::new().recursive(true).create(&dirpath).unwrap();
 
     // Get file descriptor for base directory
-    let dirfd = fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty()).unwrap();
+    let dirfd =
+        fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
+            .unwrap();
 
     // Attempt unlink dir at relative path with proper flag
     unlinkat(Some(dirfd), dirname, UnlinkatFlags::RemoveDir).unwrap();
     assert!(!dirpath.exists());
- }
+}
 
 #[test]
 #[cfg(not(target_os = "redox"))]
@@ -1002,34 +1145,47 @@
     File::create(&filepath).unwrap();
 
     // Get file descriptor for base directory
-    let dirfd = fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty()).unwrap();
+    let dirfd =
+        fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
+            .unwrap();
 
     // Attempt unlink file at relative path
     unlinkat(Some(dirfd), filename, UnlinkatFlags::NoRemoveDir).unwrap();
     assert!(!filepath.exists());
- }
+}
 
 #[test]
 fn test_access_not_existing() {
     let tempdir = tempdir().unwrap();
     let dir = tempdir.path().join("does_not_exist.txt");
-    assert_eq!(access(&dir, AccessFlags::F_OK).err().unwrap(),
-               Errno::ENOENT);
+    assert_eq!(
+        access(&dir, AccessFlags::F_OK).err().unwrap(),
+        Errno::ENOENT
+    );
 }
 
 #[test]
 fn test_access_file_exists() {
     let tempdir = tempdir().unwrap();
-    let path  = tempdir.path().join("does_exist.txt");
+    let path = tempdir.path().join("does_exist.txt");
     let _file = File::create(path.clone()).unwrap();
-    assert!(access(&path, AccessFlags::R_OK | AccessFlags::W_OK).is_ok());
+    access(&path, AccessFlags::R_OK | AccessFlags::W_OK)
+        .expect("assertion failed");
 }
 
+//Clippy false positive https://github.com/rust-lang/rust-clippy/issues/9111
+#[allow(clippy::needless_borrow)]
 #[cfg(not(target_os = "redox"))]
 #[test]
 fn test_user_into_passwd() {
     // get the UID of the "nobody" user
-    let nobody = User::from_name("nobody").unwrap().unwrap();
+    #[cfg(not(target_os = "haiku"))]
+    let test_username = "nobody";
+    // "nobody" unavailable on haiku
+    #[cfg(target_os = "haiku")]
+    let test_username = "user";
+
+    let nobody = User::from_name(test_username).unwrap().unwrap();
     let pwd: libc::passwd = nobody.into();
     let _: User = (&pwd).into();
 }
@@ -1048,8 +1204,7 @@
     // create a temporary file with permissions '-rw-r-----'
     let file = tempfile::NamedTempFile::new_in("/var/tmp").unwrap();
     let temp_path = file.into_temp_path();
-    dbg!(&temp_path);
-    let temp_path_2 = (&temp_path).to_path_buf();
+    let temp_path_2 = temp_path.to_path_buf();
     let mut permissions = fs::metadata(&temp_path).unwrap().permissions();
     permissions.set_mode(0o640);
 
@@ -1059,8 +1214,8 @@
         let fuid = setfsuid(nobody.uid);
         // trying to open the temporary file should fail with EACCES
         let res = fs::File::open(&temp_path);
-        assert!(res.is_err());
-        assert_eq!(res.err().unwrap().kind(), io::ErrorKind::PermissionDenied);
+        let err = res.expect_err("assertion failed");
+        assert_eq!(err.kind(), io::ErrorKind::PermissionDenied);
 
         // assert fuid actually changes
         let prev_fuid = setfsuid(Uid::from_raw(-1i32 as u32));
@@ -1074,7 +1229,11 @@
 }
 
 #[test]
-#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
+#[cfg(not(any(
+    target_os = "redox",
+    target_os = "fuchsia",
+    target_os = "haiku"
+)))]
 fn test_ttyname() {
     let fd = posix_openpt(OFlag::O_RDWR).expect("posix_openpt failed");
     assert!(fd.as_raw_fd() > 0);
@@ -1085,11 +1244,8 @@
     grantpt(&fd).expect("grantpt failed");
     unlockpt(&fd).expect("unlockpt failed");
     let sname = unsafe { ptsname(&fd) }.expect("ptsname failed");
-    let fds = open(
-        Path::new(&sname),
-        OFlag::O_RDWR,
-        stat::Mode::empty(),
-    ).expect("open failed");
+    let fds = open(Path::new(&sname), OFlag::O_RDWR, stat::Mode::empty())
+        .expect("open failed");
     assert!(fds > 0);
 
     let name = ttyname(fds).expect("ttyname failed");
@@ -1105,7 +1261,11 @@
 }
 
 #[test]
-#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
+#[cfg(not(any(
+    target_os = "redox",
+    target_os = "fuchsia",
+    target_os = "haiku"
+)))]
 fn test_ttyname_invalid_fd() {
     assert_eq!(ttyname(-1), Err(Errno::EBADF));
 }
@@ -1146,5 +1306,102 @@
 ))]
 fn test_getpeereid_invalid_fd() {
     // getpeereid is not POSIX, so error codes are inconsistent between different Unices.
-    assert!(getpeereid(-1).is_err());
+    getpeereid(-1).expect_err("assertion failed");
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_faccessat_none_not_existing() {
+    use nix::fcntl::AtFlags;
+    let tempdir = tempfile::tempdir().unwrap();
+    let dir = tempdir.path().join("does_not_exist.txt");
+    assert_eq!(
+        faccessat(None, &dir, AccessFlags::F_OK, AtFlags::empty())
+            .err()
+            .unwrap(),
+        Errno::ENOENT
+    );
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_faccessat_not_existing() {
+    use nix::fcntl::AtFlags;
+    let tempdir = tempfile::tempdir().unwrap();
+    let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
+    let not_exist_file = "does_not_exist.txt";
+    assert_eq!(
+        faccessat(
+            Some(dirfd),
+            not_exist_file,
+            AccessFlags::F_OK,
+            AtFlags::empty(),
+        )
+        .err()
+        .unwrap(),
+        Errno::ENOENT
+    );
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_faccessat_none_file_exists() {
+    use nix::fcntl::AtFlags;
+    let tempdir = tempfile::tempdir().unwrap();
+    let path = tempdir.path().join("does_exist.txt");
+    let _file = File::create(path.clone()).unwrap();
+    assert!(faccessat(
+        None,
+        &path,
+        AccessFlags::R_OK | AccessFlags::W_OK,
+        AtFlags::empty(),
+    )
+    .is_ok());
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_faccessat_file_exists() {
+    use nix::fcntl::AtFlags;
+    let tempdir = tempfile::tempdir().unwrap();
+    let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
+    let exist_file = "does_exist.txt";
+    let path = tempdir.path().join(exist_file);
+    let _file = File::create(path.clone()).unwrap();
+    assert!(faccessat(
+        Some(dirfd),
+        &path,
+        AccessFlags::R_OK | AccessFlags::W_OK,
+        AtFlags::empty(),
+    )
+    .is_ok());
+}
+
+#[test]
+#[cfg(any(
+    all(target_os = "linux", not(target_env = "uclibc")),
+    target_os = "freebsd",
+    target_os = "dragonfly"
+))]
+fn test_eaccess_not_existing() {
+    let tempdir = tempdir().unwrap();
+    let dir = tempdir.path().join("does_not_exist.txt");
+    assert_eq!(
+        eaccess(&dir, AccessFlags::F_OK).err().unwrap(),
+        Errno::ENOENT
+    );
+}
+
+#[test]
+#[cfg(any(
+    all(target_os = "linux", not(target_env = "uclibc")),
+    target_os = "freebsd",
+    target_os = "dragonfly"
+))]
+fn test_eaccess_file_exists() {
+    let tempdir = tempdir().unwrap();
+    let path = tempdir.path().join("does_exist.txt");
+    let _file = File::create(path.clone()).unwrap();
+    eaccess(&path, AccessFlags::R_OK | AccessFlags::W_OK)
+        .expect("assertion failed");
 }