Update nix to 0.23.1 am: d1a942bdc4 am: cc57d70224 am: ae4c8445f7 am: d09577a8d8

Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/nix/+/2032805

Change-Id: Iad647b9d49d357ee1b8496ba92c4ba31b171d62c
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index fb7ddb9..90eb8fa 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,5 +1,6 @@
 {
   "git": {
-    "sha1": "b29a85165b0bf494974642cc6ba58a924f143948"
-  }
-}
+    "sha1": "afba7c5a33dd4fd62b047e64089487cf822ccec2"
+  },
+  "path_in_vcs": ""
+}
\ No newline at end of file
diff --git a/.gitattributes b/.gitattributes
deleted file mode 100644
index 3d432e0..0000000
--- a/.gitattributes
+++ /dev/null
@@ -1 +0,0 @@
-/CHANGELOG.md merge=union
diff --git a/Android.bp b/Android.bp
index 2257d36..88940a1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -26,7 +26,7 @@
     host_supported: true,
     crate_name: "nix",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.22.1",
+    cargo_pkg_version: "0.23.1",
     srcs: ["src/lib.rs"],
     edition: "2018",
     rustlibs: [
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5367d7a..77d5b2a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,18 +3,132 @@
 All notable changes to this project will be documented in this file.
 This project adheres to [Semantic Versioning](https://semver.org/).
 
-## [0.22.1] - 13 August 2021
+## [0.23.1] - 2021-12-16
+
 ### Added
 ### 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))
+
 ### Fixed
 
-- Locked bitflags to < 1.3.0 to fix the build with rust < 1.46.0.
+- Fixed soundness issues in `FdSet::insert`, `FdSet::remove`, and
+  `FdSet::contains` involving file descriptors outside of the range
+  `0..FD_SETSIZE`.
+  (#[1575](https://github.com/nix-rust/nix/pull/1575))
+
+### Removed
+
+## [0.23.0] - 2021-09-28
+### Added
+
+- Added the `LocalPeerCred` sockopt.
+  (#[1482](https://github.com/nix-rust/nix/pull/1482))
+- Added `TimeSpec::from_duration` and `TimeSpec::from_timespec`
+  (#[1465](https://github.com/nix-rust/nix/pull/1465))
+- Added `IPV6_V6ONLY` sockopt.
+  (#[1470](https://github.com/nix-rust/nix/pull/1470))
+- Added `impl From<User> for libc::passwd` trait implementation to convert a `User`
+  into a `libc::passwd`. Consumes the `User` struct to give ownership over
+  the member pointers.
+  (#[1471](https://github.com/nix-rust/nix/pull/1471))
+- Added `pthread_kill`.
+  (#[1472](https://github.com/nix-rust/nix/pull/1472))
+- Added `mknodat`.
+  (#[1473](https://github.com/nix-rust/nix/pull/1473))
+- Added `setrlimit` and `getrlimit`.
+  (#[1302](https://github.com/nix-rust/nix/pull/1302))
+- Added `ptrace::interrupt` method for platforms that support `PTRACE_INTERRUPT`
+  (#[1422](https://github.com/nix-rust/nix/pull/1422))
+- Added `IP6T_SO_ORIGINAL_DST` sockopt.
+  (#[1490](https://github.com/nix-rust/nix/pull/1490))
+- Added the `PTRACE_EVENT_STOP` variant to the `sys::ptrace::Event` enum
+  (#[1335](https://github.com/nix-rust/nix/pull/1335))
+- Exposed `SockAddr::from_raw_sockaddr`
+  (#[1447](https://github.com/nix-rust/nix/pull/1447))
+- Added `TcpRepair`
+  (#[1503](https://github.com/nix-rust/nix/pull/1503))
+- Enabled `pwritev` and `preadv` for more operating systems.
+  (#[1511](https://github.com/nix-rust/nix/pull/1511))
+- Added support for `TCP_MAXSEG` TCP Maximum Segment Size socket options
+  (#[1292](https://github.com/nix-rust/nix/pull/1292))
+- Added `Ipv4RecvErr` and `Ipv6RecvErr` sockopts and associated control messages.
+  (#[1514](https://github.com/nix-rust/nix/pull/1514))
+- Added `AsRawFd` implementation on `PollFd`.
+  (#[1516](https://github.com/nix-rust/nix/pull/1516))
+- Added `Ipv4Ttl` and `Ipv6Ttl` sockopts.
+  (#[1515](https://github.com/nix-rust/nix/pull/1515))
+- Added `MAP_EXCL`, `MAP_ALIGNED_SUPER`, and `MAP_CONCEAL` mmap flags, and
+  exposed `MAP_ANONYMOUS` for all operating systems.
+  (#[1522](https://github.com/nix-rust/nix/pull/1522))
+  (#[1525](https://github.com/nix-rust/nix/pull/1525))
+  (#[1531](https://github.com/nix-rust/nix/pull/1531))
+  (#[1534](https://github.com/nix-rust/nix/pull/1534))
+- Added read/write accessors for 'events' on `PollFd`.
+  (#[1517](https://github.com/nix-rust/nix/pull/1517))
+
+### Changed
+
+- `FdSet::{contains, highest, fds}` no longer require a mutable reference.
+  (#[1464](https://github.com/nix-rust/nix/pull/1464))
+- `User::gecos` and corresponding `libc::passwd::pw_gecos` are supported on
+  64-bit Android, change conditional compilation to include the field in
+  64-bit Android builds
+  (#[1471](https://github.com/nix-rust/nix/pull/1471))
+- `eventfd`s are supported on Android, change conditional compilation to
+  include `sys::eventfd::eventfd` and `sys::eventfd::EfdFlags`for Android
+  builds.
+  (#[1481](https://github.com/nix-rust/nix/pull/1481))
+- Most enums that come from C, for example `Errno`, are now marked as
+  `#[non_exhaustive]`.
+  (#[1474](https://github.com/nix-rust/nix/pull/1474))
+- Many more functions, mostly contructors, are now `const`.
+  (#[1476](https://github.com/nix-rust/nix/pull/1476))
+  (#[1492](https://github.com/nix-rust/nix/pull/1492))
+- `sys::event::KEvent::filter` now returns a `Result` instead of being
+  infalliable.  The only cases where it will now return an error are cases
+  where it previously would've had undefined behavior.
+  (#[1484](https://github.com/nix-rust/nix/pull/1484))
+- Minimum supported Rust version is now 1.46.0.
+  ([#1492](https://github.com/nix-rust/nix/pull/1492))
+- Rework `UnixAddr` to encapsulate internals better in order to fix soundness
+  issues. No longer allows creating a `UnixAddr` from a raw `sockaddr_un`.
+  ([#1496](https://github.com/nix-rust/nix/pull/1496))
+- Raised bitflags to 1.3.0 and the MSRV to 1.46.0.
+  ([#1492](https://github.com/nix-rust/nix/pull/1492))
+
+### Fixed
+
+- `posix_fadvise` now returns errors in the conventional way, rather than as a
+  non-zero value in `Ok()`.
+  (#[1538](https://github.com/nix-rust/nix/pull/1538))
+- Added more errno definitions for better backwards compatibility with
+  Nix 0.21.0.
+  (#[1467](https://github.com/nix-rust/nix/pull/1467))
+- Fixed potential undefined behavior in `Signal::try_from` on some platforms.
+  (#[1484](https://github.com/nix-rust/nix/pull/1484))
+- Fixed buffer overflow in `unistd::getgrouplist`.
+  (#[1545](https://github.com/nix-rust/nix/pull/1545))
+
 
 ### Removed
 
 - Removed a couple of termios constants on redox that were never actually
   supported.
   (#[1483](https://github.com/nix-rust/nix/pull/1483))
+- Removed `nix::sys::signal::NSIG`.  It was of dubious utility, and not correct
+  for all platforms.
+  (#[1484](https://github.com/nix-rust/nix/pull/1484))
+- Removed support for 32-bit Apple targets, since they've been dropped by both
+  Rustc and Xcode.
+  (#[1492](https://github.com/nix-rust/nix/pull/1492))
+- Deprecated `SockAddr/InetAddr::to_str` in favor of `ToString::to_string`
+  (#[1495](https://github.com/nix-rust/nix/pull/1495))
+- Removed `SigevNotify` on OpenBSD and Redox.
+  (#[1511](https://github.com/nix-rust/nix/pull/1511))
 
 ## [0.22.0] - 9 July 2021
 ### Added
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
deleted file mode 100644
index 55990c4..0000000
--- a/CONTRIBUTING.md
+++ /dev/null
@@ -1,114 +0,0 @@
-# Contributing to nix
-
-We're really glad you're interested in contributing to nix! This
-document has a few pointers and guidelines to help get you started.
-
-To have a welcoming and inclusive project, nix uses the Rust project's
-[Code of Conduct][conduct]. All contributors are expected to follow it.
-
-[conduct]: https://www.rust-lang.org/conduct.html
-
-
-# Issues
-
-We use GitHub's [issue tracker][issues].
-
-[issues]: https://github.com/nix-rust/nix/issues
-
-
-## Bug reports
-
-Before submitting a new bug report, please [search existing
-issues][issue-search] to see if there's something related. If not, just
-[open a new issue][new-issue]!
-
-As a reminder, the more information you can give in your issue, the
-easier it is to figure out how to fix it. For nix, this will likely
-include the OS and version, and the architecture.
-
-[issue-search]: https://github.com/nix-rust/nix/search?utf8=%E2%9C%93&q=is%3Aissue&type=Issues
-[new-issue]: https://github.com/nix-rust/nix/issues/new
-
-
-## Feature / API requests
-
-If you'd like a new API or feature added, please [open a new
-issue][new-issue] requesting it. As with reporting a bug, the more
-information you can provide, the better.
-
-
-## Labels
-
-We use labels to help manage issues. The structure is modeled after
-[Rust's issue labeling scheme][rust-labels]:
-- **A-**prefixed labels state which area of the project the issue
-  relates to
-- **E-**prefixed labels explain the level of experience necessary to fix the
-  issue
-- **O-**prefixed labels specify the OS for issues that are OS-specific
-- **R-**prefixed labels specify the architecture for issues that are
-  architecture-specific
-
-[rust-labels]: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#issue-triage
-
-
-# Pull requests
-
-GitHub pull requests are the primary mechanism we use to change nix. GitHub itself has
-some [great documentation][pr-docs] on using the Pull Request feature. We use the 'fork and
-pull' model described there.
-
-Please make pull requests against the `master` branch.
-
-If you change the API by way of adding, removing or changing something or if
-you fix a bug, please add an appropriate note to the [change log][cl]. We
-follow the conventions of [Keep A CHANGELOG][kacl].
-
-[cl]: https://github.com/nix-rust/nix/blob/master/CHANGELOG.md
-[kacl]: https://github.com/olivierlacan/keep-a-changelog/tree/18adb5f5be7a898d046f6a4acb93e39dcf40c4ad
-[pr-docs]: https://help.github.com/articles/using-pull-requests/
-
-## Testing
-
-nix has a test suite that you can run with `cargo test`. Ideally, we'd like pull
-requests to include tests where they make sense. For example, when fixing a bug,
-add a test that would have failed without the fix.
-
-After you've made your change, make sure the tests pass in your development
-environment. We also have [continuous integration set up on
-Cirrus-CI][cirrus-ci], which might find some issues on other platforms. The CI
-will run once you open a pull request.
-
-There is also infrastructure for running tests for other targets
-locally.  More information is available in the [CI Readme][ci-readme].
-
-[cirrus-ci]: https://cirrus-ci.com/github/nix-rust/nix
-[ci-readme]: ci/README.md
-
-### Disabling a test in the CI environment
-
-Sometimes there are features that cannot be tested in the CI environment.
-To stop a test from running under CI, add `skip_if_cirrus!()` to it. Please
-describe the reason it shouldn't run under CI, and a link to an issue if
-possible!
-
-## bors, the bot who merges all the PRs
-
-All pull requests are merged via [bors], an integration bot. After the
-pull request has been reviewed, the reviewer will leave a comment like
-
-> bors r+
-
-to let bors know that it was approved. Then bors will check that it passes
-tests when merged with the latest changes in the `master` branch, and
-merge if the tests succeed.
-
-[bors]: https://bors-ng.github.io/
-
-
-## API conventions
-
-If you're adding a new API, we have a [document with
-conventions][conventions] to use throughout the nix project.
-
-[conventions]: https://github.com/nix-rust/nix/blob/master/CONVENTIONS.md
diff --git a/CONVENTIONS.md b/CONVENTIONS.md
deleted file mode 100644
index 2461085..0000000
--- a/CONVENTIONS.md
+++ /dev/null
@@ -1,86 +0,0 @@
-# Conventions
-
-In order to achieve our goal of wrapping [libc][libc] code in idiomatic rust
-constructs with minimal performance overhead, we follow the following
-conventions.
-
-Note that, thus far, not all the code follows these conventions and not all
-conventions we try to follow have been documented here. If you find an instance
-of either, feel free to remedy the flaw by opening a pull request with
-appropriate changes or additions.
-
-## Change Log
-
-We follow the conventions laid out in [Keep A CHANGELOG][kacl].
-
-[kacl]: https://github.com/olivierlacan/keep-a-changelog/tree/18adb5f5be7a898d046f6a4acb93e39dcf40c4ad
-
-## libc constants, functions and structs
-
-We do not define integer constants ourselves, but use or reexport them from the
-[libc crate][libc].
-
-We use the functions exported from [libc][libc] instead of writing our own
-`extern` declarations.
-
-We use the `struct` definitions from [libc][libc] internally instead of writing
-our own. If we want to add methods to a libc type, we use the newtype pattern.
-For example,
-
-```rust
-pub struct SigSet(libc::sigset_t);
-
-impl SigSet {
-    ...
-}
-```
-
-When creating newtypes, we use Rust's `CamelCase` type naming convention.
-
-## Bitflags
-
-Many C functions have flags parameters that are combined from constants using
-bitwise operations. We represent the types of these parameters by types defined
-using our `libc_bitflags!` macro, which is a convenience wrapper around the
-`bitflags!` macro from the [bitflags crate][bitflags] that brings in the
-constant value from `libc`.
-
-We name the type for a set of constants whose element's names start with `FOO_`
-`FooFlags`.
-
-For example,
-
-```rust
-libc_bitflags!{
-    pub struct ProtFlags: libc::c_int {
-        PROT_NONE;
-        PROT_READ;
-        PROT_WRITE;
-        PROT_EXEC;
-        #[cfg(any(target_os = "linux", target_os = "android"))]
-        PROT_GROWSDOWN;
-        #[cfg(any(target_os = "linux", target_os = "android"))]
-        PROT_GROWSUP;
-    }
-}
-```
-
-
-## Enumerations
-
-We represent sets of constants that are intended as mutually exclusive arguments
-to parameters of functions by [enumerations][enum].
-
-
-## Structures Initialized by libc Functions
-
-Whenever we need to use a [libc][libc] function to properly initialize a
-variable and said function allows us to use uninitialized memory, we use
-[`std::mem::MaybeUninit`][std_MaybeUninit] when defining the variable. This
-allows us to avoid the overhead incurred by zeroing or otherwise initializing
-the variable.
-
-[bitflags]: https://crates.io/crates/bitflags/
-[enum]: https://doc.rust-lang.org/reference.html#enumerations
-[libc]: https://crates.io/crates/libc/
-[std_MaybeUninit]: https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html
diff --git a/Cargo.toml b/Cargo.toml
index 09c5a73..122c1af 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -11,10 +11,11 @@
 
 [package]
 edition = "2018"
+rust-version = "1.46"
 name = "nix"
-version = "0.22.1"
+version = "0.23.1"
 authors = ["The nix-rust Project Developers"]
-exclude = ["/.gitignore", "/.cirrus.yml", "/ci/*", "/Cross.toml", "/RELEASE_PROCEDURE.md", "/bors.toml"]
+include = ["src/**/*", "test/**/*", "LICENSE", "README.md", "CHANGELOG.md"]
 description = "Rust friendly bindings to *nix APIs"
 categories = ["os::unix-apis"]
 license = "MIT"
@@ -47,13 +48,13 @@
 name = "test-ptymaster-drop"
 path = "test/test_ptymaster_drop.rs"
 [dependencies.bitflags]
-version = ">= 1.1.0, < 1.3.0"
+version = "1.1"
 
 [dependencies.cfg-if]
 version = "1.0"
 
 [dependencies.libc]
-version = "0.2.99"
+version = "0.2.102"
 features = ["extra_traits"]
 [dev-dependencies.assert-impl]
 version = "0.1"
@@ -61,6 +62,9 @@
 [dev-dependencies.lazy_static]
 version = "1.2"
 
+[dev-dependencies.parking_lot]
+version = "0.11.2"
+
 [dev-dependencies.rand]
 version = "0.8"
 
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index 7a527d3..d2ca8ee 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -2,19 +2,13 @@
 name        = "nix"
 description = "Rust friendly bindings to *nix APIs"
 edition     = "2018"
-version     = "0.22.1"
+version     = "0.23.1"
+rust-version = "1.46"
 authors     = ["The nix-rust Project Developers"]
 repository  = "https://github.com/nix-rust/nix"
 license     = "MIT"
 categories  = ["os::unix-apis"]
-exclude     = [
-  "/.gitignore",
-  "/.cirrus.yml",
-  "/ci/*",
-  "/Cross.toml",
-  "/RELEASE_PROCEDURE.md",
-  "/bors.toml"
-]
+include = ["src/**/*", "test/**/*", "LICENSE", "README.md", "CHANGELOG.md"]
 
 [package.metadata.docs.rs]
 targets = [
@@ -32,8 +26,8 @@
 ]
 
 [dependencies]
-libc = { version = "0.2.99", features = [ "extra_traits" ] }
-bitflags = ">= 1.1.0, < 1.3.0"
+libc = { version = "0.2.102", features = [ "extra_traits" ] }
+bitflags = "1.1"
 cfg-if = "1.0"
 
 [target.'cfg(not(target_os = "redox"))'.dependencies]
@@ -45,6 +39,7 @@
 [dev-dependencies]
 assert-impl = "0.1"
 lazy_static = "1.2"
+parking_lot = "0.11.2"
 rand = "0.8"
 tempfile = "3.2.0"
 semver = "1.0.0"
diff --git a/METADATA b/METADATA
index 163016d..e0fef6c 100644
--- a/METADATA
+++ b/METADATA
@@ -7,13 +7,13 @@
   }
   url {
     type: ARCHIVE
-    value: "https://static.crates.io/crates/nix/nix-0.22.1.crate"
+    value: "https://static.crates.io/crates/nix/nix-0.23.1.crate"
   }
-  version: "0.22.1"
+  version: "0.23.1"
   license_type: NOTICE
   last_upgrade_date {
-    year: 2021
-    month: 9
-    day: 22
+    year: 2022
+    month: 3
+    day: 16
   }
 }
diff --git a/README.md b/README.md
index 566043f..a8759f1 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
 # Rust bindings to *nix APIs
 
 [![Cirrus Build Status](https://api.cirrus-ci.com/github/nix-rust/nix.svg)](https://cirrus-ci.com/github/nix-rust/nix)
-[![crates.io](https://meritbadge.herokuapp.com/nix)](https://crates.io/crates/nix)
+[![crates.io](https://img.shields.io/crates/v/nix.svg)](https://crates.io/crates/nix)
 
 [Documentation (Releases)](https://docs.rs/nix/)
 
@@ -68,33 +68,26 @@
   * aarch64-linux-android
   * arm-linux-androideabi
   * arm-unknown-linux-musleabi
-  * armv7-apple-ios
   * armv7-linux-androideabi
-  * armv7s-apple-ios
-  * i386-apple-ios
-  * i686-apple-darwin
   * i686-linux-android
   * powerpc-unknown-linux-gnu
   * s390x-unknown-linux-gnu
   * x86_64-apple-ios
   * x86_64-linux-android
+  * x86_64-unknown-illumos
   * x86_64-unknown-netbsd
 
 Tier 3:
   * x86_64-fuchsia
-  * x86_64-unknown-redox
+  * x86_64-unknown-dragonfly
   * x86_64-unknown-linux-gnux32
+  * x86_64-unknown-openbsd
+  * x86_64-unknown-redox
 
-## Usage
+## Minimum Supported Rust Version (MSRV)
 
-`nix` requires Rust 1.41.0 or newer.
-
-To use `nix`, add this to your `Cargo.toml`:
-
-```toml
-[dependencies]
-nix = "0.22.1"
-```
+nix is supported on Rust 1.46.0 and higher.  It's MSRV will not be
+changed in the future without bumping the major or minor version.
 
 ## Contributing
 
diff --git a/patches/0001-Allow-android-compiled-binaries-to-use-timerfd.patch b/patches/0001-Allow-android-compiled-binaries-to-use-timerfd.patch
deleted file mode 100644
index e164a3c..0000000
--- a/patches/0001-Allow-android-compiled-binaries-to-use-timerfd.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-From e09a4f4e8661e8036819deb97dc1d9fcf2a49d2b Mon Sep 17 00:00:00 2001
-From: Zach Johnson <zachoverflow@google.com>
-Date: Tue, 17 Nov 2020 11:05:57 -0800
-Subject: [PATCH] Allow android compiled binaries to use timerfd
-
-Bug: 171749953
-Test: compile
-Change-Id: Iaf16615cf834df02537e3b569ab08dd98a497a70
----
- src/sys/mod.rs | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/sys/mod.rs b/src/sys/mod.rs
-index bf7f541..b8b9e6f 100644
---- a/src/sys/mod.rs
-+++ b/src/sys/mod.rs
-@@ -103,5 +103,5 @@ pub mod wait;
- #[cfg(any(target_os = "android", target_os = "linux"))]
- pub mod inotify;
- 
--#[cfg(target_os = "linux")]
-+#[cfg(any(target_os = "android", target_os = "linux"))]
- pub mod timerfd;
--- 
-2.29.2.299.gdc1121823c-goog
-
diff --git a/src/dir.rs b/src/dir.rs
index 65ed2e9..ed70a45 100644
--- a/src/dir.rs
+++ b/src/dir.rs
@@ -84,7 +84,7 @@
 impl Drop for Dir {
     fn drop(&mut self) {
         let e = Errno::result(unsafe { libc::closedir(self.0.as_ptr()) });
-        if !std::thread::panicking() && e == Err(Error::from(Errno::EBADF)) {
+        if !std::thread::panicking() && e == Err(Errno::EBADF) {
             panic!("Closing an invalid file descriptor!");
         };
     }
@@ -211,6 +211,7 @@
                   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)
     }
diff --git a/src/env.rs b/src/env.rs
index 613b0cd..bcae287 100644
--- a/src/env.rs
+++ b/src/env.rs
@@ -1,3 +1,4 @@
+//! Environment variables
 use cfg_if::cfg_if;
 use std::fmt;
 
@@ -38,7 +39,6 @@
 ///  environment access in the program is via `std::env`, but the requirement on
 ///  thread safety must still be upheld.
 pub unsafe fn clearenv() -> std::result::Result<(), ClearEnvError> {
-    let ret;
     cfg_if! {
         if #[cfg(any(target_os = "fuchsia",
                      target_os = "wasi",
@@ -47,13 +47,13 @@
                      target_os = "linux",
                      target_os = "android",
                      target_os = "emscripten"))] {
-            ret = libc::clearenv();
+            let ret = libc::clearenv();
         } else {
             use std::env;
             for (name, _) in env::vars_os() {
                 env::remove_var(name);
             }
-            ret = 0;
+            let ret = 0;
         }
     }
 
diff --git a/src/errno.rs b/src/errno.rs
index 00e2014..3da246e 100644
--- a/src/errno.rs
+++ b/src/errno.rs
@@ -63,7 +63,7 @@
         since = "0.22.0",
         note = "It's a no-op now; just delete it."
     )]
-    pub fn as_errno(self) -> Option<Self> {
+    pub const fn as_errno(self) -> Option<Self> {
         Some(self)
     }
 
@@ -72,8 +72,9 @@
         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 {
-        Error::from(errno)
+        errno
     }
 
     /// Create a new invalid argument error (`EINVAL`)
@@ -81,7 +82,7 @@
         since = "0.22.0",
         note = "Use Errno::EINVAL instead"
     )]
-    pub fn invalid_argument() -> Error {
+    pub const fn invalid_argument() -> Error {
         Errno::EINVAL
     }
 
@@ -93,7 +94,7 @@
         desc(self)
     }
 
-    pub fn from_i32(err: i32) -> Errno {
+    pub const fn from_i32(err: i32) -> Errno {
         from_i32(err)
     }
 
@@ -103,6 +104,7 @@
 
     /// Returns `Ok(value)` if it does not contain the sentinel value. This
     /// should not be used when `-1` is not the errno sentinel value.
+    #[inline]
     pub fn result<S: ErrnoSentinel + PartialEq<S>>(value: S) -> Result<S> {
         if value == S::sentinel() {
             Err(Self::last())
@@ -122,7 +124,7 @@
     )]
     #[allow(non_snake_case)]
     #[inline]
-    pub fn Sys(errno: Errno) -> Error {
+    pub const fn Sys(errno: Errno) -> Error {
         errno
     }
 }
@@ -768,6 +770,7 @@
 mod consts {
     #[derive(Clone, Copy, Debug, Eq, PartialEq)]
     #[repr(i32)]
+    #[non_exhaustive]
     pub enum Errno {
         UnknownErrno    = 0,
         EPERM           = libc::EPERM,
@@ -905,13 +908,29 @@
         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 fn from_i32(e: i32) -> Errno {
+    pub const fn from_i32(e: i32) -> Errno {
         use self::Errno::*;
 
         match e {
@@ -1057,6 +1076,7 @@
 mod consts {
     #[derive(Clone, Copy, Debug, Eq, PartialEq)]
     #[repr(i32)]
+    #[non_exhaustive]
     pub enum Errno {
         UnknownErrno    = 0,
         EPERM           = libc::EPERM,
@@ -1167,13 +1187,29 @@
         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 EWOULDBLOCK: Errno = Errno::EAGAIN;
         pub const EDEADLOCK:   Errno = Errno::EDEADLK;
     }
 
-    pub fn from_i32(e: i32) -> Errno {
+    pub const fn from_i32(e: i32) -> Errno {
         use self::Errno::*;
 
         match e {
@@ -1292,6 +1328,7 @@
 mod consts {
     #[derive(Clone, Copy, Debug, Eq, PartialEq)]
     #[repr(i32)]
+    #[non_exhaustive]
     pub enum Errno {
         UnknownErrno    = 0,
         EPERM           = libc::EPERM,
@@ -1392,6 +1429,27 @@
         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 EWOULDBLOCK: Errno = Errno::EAGAIN;
@@ -1399,7 +1457,7 @@
         pub const EOPNOTSUPP:  Errno = Errno::ENOTSUP;
     }
 
-    pub fn from_i32(e: i32) -> Errno {
+    pub const fn from_i32(e: i32) -> Errno {
         use self::Errno::*;
 
         match e {
@@ -1509,6 +1567,7 @@
 mod consts {
     #[derive(Clone, Copy, Debug, Eq, PartialEq)]
     #[repr(i32)]
+    #[non_exhaustive]
     pub enum Errno {
         UnknownErrno    = 0,
         EPERM           = libc::EPERM,
@@ -1607,6 +1666,27 @@
         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 EWOULDBLOCK: Errno = Errno::EAGAIN;
@@ -1614,7 +1694,7 @@
         pub const EOPNOTSUPP:  Errno = Errno::ENOTSUP;
     }
 
-    pub fn from_i32(e: i32) -> Errno {
+    pub const fn from_i32(e: i32) -> Errno {
         use self::Errno::*;
 
         match e {
@@ -1722,6 +1802,7 @@
 mod consts {
     #[derive(Clone, Copy, Debug, Eq, PartialEq)]
     #[repr(i32)]
+    #[non_exhaustive]
     pub enum Errno {
         UnknownErrno    = 0,
         EPERM           = libc::EPERM,
@@ -1821,12 +1902,23 @@
         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 EWOULDBLOCK: Errno = Errno::EAGAIN;
     }
 
-    pub fn from_i32(e: i32) -> Errno {
+    pub const fn from_i32(e: i32) -> Errno {
         use self::Errno::*;
 
         match e {
@@ -1934,6 +2026,7 @@
 mod consts {
     #[derive(Clone, Copy, Debug, Eq, PartialEq)]
     #[repr(i32)]
+    #[non_exhaustive]
     pub enum Errno {
         UnknownErrno    = 0,
         EPERM           = libc::EPERM,
@@ -2034,12 +2127,23 @@
         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 EWOULDBLOCK: Errno = Errno::EAGAIN;
     }
 
-    pub fn from_i32(e: i32) -> Errno {
+    pub const fn from_i32(e: i32) -> Errno {
         use self::Errno::*;
 
         match e {
@@ -2148,6 +2252,7 @@
 mod consts {
     #[derive(Clone, Copy, Debug, Eq, PartialEq)]
     #[repr(i32)]
+    #[non_exhaustive]
     pub enum Errno {
         UnknownErrno = 0,
         EPERM = libc::EPERM,
@@ -2237,11 +2342,17 @@
         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;
     }
 
-    pub fn from_i32(e: i32) -> Errno {
+    pub const fn from_i32(e: i32) -> Errno {
         use self::Errno::*;
 
         match e {
@@ -2339,6 +2450,7 @@
 mod consts {
     #[derive(Clone, Copy, Debug, Eq, PartialEq)]
     #[repr(i32)]
+    #[non_exhaustive]
     pub enum Errno {
         UnknownErrno = 0,
         EPERM = libc::EPERM,
@@ -2464,12 +2576,23 @@
         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 EWOULDBLOCK: Errno = Errno::EAGAIN;
     }
 
-    pub fn from_i32(e: i32) -> Errno {
+    pub const fn from_i32(e: i32) -> Errno {
         use self::Errno::*;
 
         match e {
diff --git a/src/fcntl.rs b/src/fcntl.rs
index f8f1372..dd8e59a 100644
--- a/src/fcntl.rs
+++ b/src/fcntl.rs
@@ -325,7 +325,7 @@
                 Some(next_size) => try_size = next_size,
                 // It's absurd that this would happen, but handle it sanely
                 // anyway.
-                None => break Err(super::Error::from(Errno::ENAMETOOLONG)),
+                None => break Err(Errno::ENAMETOOLONG),
             }
         }
     }
@@ -374,6 +374,7 @@
 
 #[cfg(not(target_os = "redox"))]
 #[derive(Debug, Eq, Hash, PartialEq)]
+#[non_exhaustive]
 pub enum FcntlArg<'a> {
     F_DUPFD(RawFd),
     F_DUPFD_CLOEXEC(RawFd),
@@ -405,6 +406,7 @@
 
 #[cfg(target_os = "redox")]
 #[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
+#[non_exhaustive]
 pub enum FcntlArg {
     F_DUPFD(RawFd),
     F_DUPFD_CLOEXEC(RawFd),
@@ -454,6 +456,7 @@
 }
 
 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[non_exhaustive]
 pub enum FlockArg {
     LockShared,
     LockExclusive,
@@ -643,12 +646,12 @@
 ))]
 mod posix_fadvise {
     use crate::errno::Errno;
-    use libc;
     use std::os::unix::io::RawFd;
     use crate::Result;
 
     libc_enum! {
         #[repr(i32)]
+        #[non_exhaustive]
         pub enum PosixFadviseAdvice {
             POSIX_FADV_NORMAL,
             POSIX_FADV_SEQUENTIAL,
@@ -664,9 +667,14 @@
         offset: libc::off_t,
         len: libc::off_t,
         advice: PosixFadviseAdvice,
-    ) -> Result<libc::c_int> {
+    ) -> Result<()> {
         let res = unsafe { libc::posix_fadvise(fd, offset, len, advice as libc::c_int) };
-        Errno::result(res)
+
+        if res == 0 {
+            Ok(())
+        } else {
+            Err(Errno::from_i32(res))
+        }
     }
 }
 
@@ -683,6 +691,6 @@
     match Errno::result(res) {
         Err(err) => Err(err),
         Ok(0) => Ok(()),
-        Ok(errno) => Err(crate::Error::from(Errno::from_i32(errno))),
+        Ok(errno) => Err(Errno::from_i32(errno)),
     }
 }
diff --git a/src/features.rs b/src/features.rs
index bcda45d..ed80fd7 100644
--- a/src/features.rs
+++ b/src/features.rs
@@ -94,22 +94,28 @@
     }
 }
 
-#[cfg(any(target_os = "illumos"))]
+#[cfg(any(
+        target_os = "dragonfly",    // Since ???
+        target_os = "freebsd",      // Since 10.0
+        target_os = "illumos",      // Since ???
+        target_os = "netbsd",       // Since 6.0
+        target_os = "openbsd",      // Since 5.7
+        target_os = "redox",        // Since 1-july-2020
+))]
 mod os {
     /// Check if the OS supports atomic close-on-exec for sockets
-    pub fn socket_atomic_cloexec() -> bool {
+    pub const fn socket_atomic_cloexec() -> bool {
         true
     }
 }
 
-#[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 = "fuchsia",
+#[cfg(any(target_os = "macos",
+          target_os = "ios",
+          target_os = "fuchsia",
           target_os = "solaris"))]
 mod os {
     /// Check if the OS supports atomic close-on-exec for sockets
-    pub fn socket_atomic_cloexec() -> bool {
+    pub const fn socket_atomic_cloexec() -> bool {
         false
     }
 }
diff --git a/src/kmod.rs b/src/kmod.rs
index 89e577a..c42068c 100644
--- a/src/kmod.rs
+++ b/src/kmod.rs
@@ -2,7 +2,6 @@
 //!
 //! For more details see
 
-use libc;
 use std::ffi::CStr;
 use std::os::unix::io::AsRawFd;
 
diff --git a/src/lib.rs b/src/lib.rs
index 3b534a5..3a2b63a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -5,15 +5,13 @@
 #![crate_name = "nix"]
 #![cfg(unix)]
 #![allow(non_camel_case_types)]
-// latest bitflags triggers a rustc bug with cross-crate macro expansions causing dead_code
-// warnings even though the macro expands into something with allow(dead_code)
-#![allow(dead_code)]
 #![cfg_attr(test, deny(warnings))]
 #![recursion_limit = "500"]
 #![deny(unused)]
 #![deny(unstable_features)]
 #![deny(missing_copy_implementations)]
 #![deny(missing_debug_implementations)]
+#![warn(missing_docs)]
 
 // Re-exported external crates
 pub use libc;
@@ -23,13 +21,14 @@
 
 // Public crates
 #[cfg(not(target_os = "redox"))]
+#[allow(missing_docs)]
 pub mod dir;
 pub mod env;
+#[allow(missing_docs)]
 pub mod errno;
-#[deny(missing_docs)]
 pub mod features;
+#[allow(missing_docs)]
 pub mod fcntl;
-#[deny(missing_docs)]
 #[cfg(any(target_os = "android",
           target_os = "dragonfly",
           target_os = "freebsd",
@@ -42,6 +41,7 @@
 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",
@@ -52,23 +52,24 @@
           target_os = "fushsia",
           target_os = "linux",
           target_os = "netbsd"))]
+#[allow(missing_docs)]
 pub mod mqueue;
-#[deny(missing_docs)]
 #[cfg(not(target_os = "redox"))]
 pub mod net;
-#[deny(missing_docs)]
 pub mod poll;
-#[deny(missing_docs)]
 #[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
 pub mod pty;
 pub mod sched;
 pub mod sys;
+#[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;
+#[allow(missing_docs)]
 pub mod unistd;
 
 /*
@@ -101,11 +102,17 @@
 /// ones.
 pub type Error = Errno;
 
+/// Common trait used to represent file system paths by many Nix functions.
 pub trait NixPath {
+    /// Is the path empty?
     fn is_empty(&self) -> bool;
 
+    /// Length of the path in bytes
     fn len(&self) -> usize;
 
+    /// Execute a function with this path as a `CStr`.
+    ///
+    /// Mostly used internally by Nix.
     fn with_nix_path<T, F>(&self, f: F) -> Result<T>
         where F: FnOnce(&CStr) -> T;
 }
@@ -153,7 +160,7 @@
             where F: FnOnce(&CStr) -> T {
         // Equivalence with the [u8] impl.
         if self.len() >= PATH_MAX as usize {
-            return Err(Error::from(Errno::ENAMETOOLONG))
+            return Err(Errno::ENAMETOOLONG)
         }
 
         Ok(f(self))
@@ -174,11 +181,11 @@
         let mut buf = [0u8; PATH_MAX as usize];
 
         if self.len() >= PATH_MAX as usize {
-            return Err(Error::from(Errno::ENAMETOOLONG))
+            return Err(Errno::ENAMETOOLONG)
         }
 
         match self.iter().position(|b| *b == 0) {
-            Some(_) => Err(Error::from(Errno::EINVAL)),
+            Some(_) => Err(Errno::EINVAL),
             None => {
                 unsafe {
                     // TODO: Replace with bytes::copy_memory. rust-lang/rust#24028
diff --git a/src/macros.rs b/src/macros.rs
index 7d6ac8d..3ccbfdd 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -83,9 +83,9 @@
 macro_rules! libc_enum {
     // Exit rule.
     (@make_enum
+        name: $BitFlags:ident,
         {
             $v:vis
-            name: $BitFlags:ident,
             attrs: [$($attrs:tt)*],
             entries: [$($entries:tt)*],
         }
@@ -97,38 +97,98 @@
         }
     };
 
-    // Done accumulating.
-    (@accumulate_entries
+    // Exit rule including TryFrom
+    (@make_enum
+        name: $BitFlags:ident,
         {
             $v:vis
-            name: $BitFlags:ident,
+            attrs: [$($attrs:tt)*],
+            entries: [$($entries:tt)*],
+            from_type: $repr:path,
+            try_froms: [$($try_froms:tt)*]
+        }
+    ) => {
+        $($attrs)*
+        #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
+        $v enum $BitFlags {
+            $($entries)*
+        }
+        impl ::std::convert::TryFrom<$repr> for $BitFlags {
+            type Error = $crate::Error;
+            #[allow(unused_doc_comments)]
+            fn try_from(x: $repr) -> $crate::Result<Self> {
+                match x {
+                    $($try_froms)*
+                    _ => Err($crate::Error::EINVAL)
+                }
+            }
+        }
+    };
+
+    // Done accumulating.
+    (@accumulate_entries
+        name: $BitFlags:ident,
+        {
+            $v:vis
             attrs: $attrs:tt,
         },
-        $entries:tt;
+        $entries:tt,
+        $try_froms:tt;
     ) => {
         libc_enum! {
             @make_enum
+            name: $BitFlags,
             {
                 $v
-                name: $BitFlags,
                 attrs: $attrs,
                 entries: $entries,
             }
         }
     };
 
+    // Done accumulating and want TryFrom
+    (@accumulate_entries
+        name: $BitFlags:ident,
+        {
+            $v:vis
+            attrs: $attrs:tt,
+            from_type: $repr:path,
+        },
+        $entries:tt,
+        $try_froms:tt;
+    ) => {
+        libc_enum! {
+            @make_enum
+            name: $BitFlags,
+            {
+                $v
+                attrs: $attrs,
+                entries: $entries,
+                from_type: $repr,
+                try_froms: $try_froms
+            }
+        }
+    };
+
     // Munch an attr.
     (@accumulate_entries
+        name: $BitFlags:ident,
         $prefix:tt,
-        [$($entries:tt)*];
+        [$($entries:tt)*],
+        [$($try_froms:tt)*];
         #[$attr:meta] $($tail:tt)*
     ) => {
         libc_enum! {
             @accumulate_entries
+            name: $BitFlags,
             $prefix,
             [
                 $($entries)*
                 #[$attr]
+            ],
+            [
+                $($try_froms)*
+                #[$attr]
             ];
             $($tail)*
         }
@@ -136,32 +196,47 @@
 
     // Munch last ident if not followed by a comma.
     (@accumulate_entries
+        name: $BitFlags:ident,
         $prefix:tt,
-        [$($entries:tt)*];
+        [$($entries:tt)*],
+        [$($try_froms:tt)*];
         $entry:ident
     ) => {
         libc_enum! {
             @accumulate_entries
+            name: $BitFlags,
             $prefix,
             [
                 $($entries)*
                 $entry = libc::$entry,
+            ],
+            [
+                $($try_froms)*
+                libc::$entry => Ok($BitFlags::$entry),
             ];
         }
     };
 
     // Munch an ident; covers terminating comma case.
     (@accumulate_entries
+        name: $BitFlags:ident,
         $prefix:tt,
-        [$($entries:tt)*];
-        $entry:ident, $($tail:tt)*
+        [$($entries:tt)*],
+        [$($try_froms:tt)*];
+        $entry:ident,
+        $($tail:tt)*
     ) => {
         libc_enum! {
             @accumulate_entries
+            name: $BitFlags,
             $prefix,
             [
                 $($entries)*
                 $entry = libc::$entry,
+            ],
+            [
+                $($try_froms)*
+                libc::$entry => Ok($BitFlags::$entry),
             ];
             $($tail)*
         }
@@ -169,16 +244,24 @@
 
     // Munch an ident and cast it to the given type; covers terminating comma.
     (@accumulate_entries
+        name: $BitFlags:ident,
         $prefix:tt,
-        [$($entries:tt)*];
-        $entry:ident as $ty:ty, $($tail:tt)*
+        [$($entries:tt)*],
+        [$($try_froms:tt)*];
+        $entry:ident as $ty:ty,
+        $($tail:tt)*
     ) => {
         libc_enum! {
             @accumulate_entries
+            name: $BitFlags,
             $prefix,
             [
                 $($entries)*
                 $entry = libc::$entry as $ty,
+            ],
+            [
+                $($try_froms)*
+                libc::$entry as $ty => Ok($BitFlags::$entry),
             ];
             $($tail)*
         }
@@ -193,11 +276,34 @@
     ) => {
         libc_enum! {
             @accumulate_entries
+            name: $BitFlags,
             {
                 $v
-                name: $BitFlags,
                 attrs: [$(#[$attr])*],
             },
+            [],
+            [];
+            $($vals)*
+        }
+    };
+
+    // Entry rule including TryFrom
+    (
+        $(#[$attr:meta])*
+        $v:vis enum $BitFlags:ident {
+            $($vals:tt)*
+        }
+        impl TryFrom<$repr:path>
+    ) => {
+        libc_enum! {
+            @accumulate_entries
+            name: $BitFlags,
+            {
+                $v
+                attrs: [$(#[$attr])*],
+                from_type: $repr,
+            },
+            [],
             [];
             $($vals)*
         }
diff --git a/src/mount/bsd.rs b/src/mount/bsd.rs
index 0144908..627bfa5 100644
--- a/src/mount/bsd.rs
+++ b/src/mount/bsd.rs
@@ -111,7 +111,7 @@
     }
 
     /// Returns the inner [`Error`]
-    pub fn error(&self) -> Error {
+    pub const fn error(&self) -> Error {
         self.errno
     }
 
@@ -347,6 +347,7 @@
         self
     }
 
+    /// Create a new `Nmount` struct with no options
     pub fn new() -> Self {
         Self::default()
     }
@@ -382,7 +383,7 @@
                         Some(CStr::from_bytes_with_nul(sl).unwrap())
                     }
                 };
-                Err(NmountError::new(error.into(), errmsg))
+                Err(NmountError::new(error, errmsg))
             }
         }
     }
@@ -396,7 +397,7 @@
                 // Free the owned string.  Safe because we recorded ownership,
                 // and Nmount does not implement Clone.
                 unsafe {
-                    CString::from_raw(iov.0.iov_base as *mut c_char);
+                    drop(CString::from_raw(iov.0.iov_base as *mut c_char));
                 }
             }
         }
diff --git a/src/mount/linux.rs b/src/mount/linux.rs
index edb8afb..4cb2fa5 100644
--- a/src/mount/linux.rs
+++ b/src/mount/linux.rs
@@ -1,3 +1,4 @@
+#![allow(missing_docs)]
 use libc::{self, c_ulong, c_int};
 use crate::{Result, NixPath};
 use crate::errno::Errno;
diff --git a/src/mount/mod.rs b/src/mount/mod.rs
index 8538bf3..14bf2a9 100644
--- a/src/mount/mod.rs
+++ b/src/mount/mod.rs
@@ -1,3 +1,4 @@
+//! Mount file systems
 #[cfg(any(target_os = "android", target_os = "linux"))]
 mod linux;
 
diff --git a/src/mqueue.rs b/src/mqueue.rs
index 3e49480..34fd802 100644
--- a/src/mqueue.rs
+++ b/src/mqueue.rs
@@ -59,7 +59,7 @@
         }
     }
 
-    pub fn flags(&self) -> mq_attr_member_t {
+    pub const fn flags(&self) -> mq_attr_member_t {
         self.mq_attr.mq_flags
     }
 }
@@ -155,6 +155,7 @@
 /// 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> {
     let oldattr = mq_getattr(mqd)?;
     let newattr = MqAttr::new(mq_attr_member_t::from(MQ_OFlag::O_NONBLOCK.bits()),
diff --git a/src/poll.rs b/src/poll.rs
index 0c3f208..8556c1b 100644
--- a/src/poll.rs
+++ b/src/poll.rs
@@ -3,7 +3,7 @@
 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::RawFd;
+use std::os::unix::io::{AsRawFd, RawFd};
 
 use crate::Result;
 use crate::errno::Errno;
@@ -25,7 +25,7 @@
 impl PollFd {
     /// Creates a new `PollFd` specifying the events of interest
     /// for a given file descriptor.
-    pub fn new(fd: RawFd, events: PollFlags) -> PollFd {
+    pub const fn new(fd: RawFd, events: PollFlags) -> PollFd {
         PollFd {
             pollfd: libc::pollfd {
                 fd,
@@ -35,10 +35,27 @@
         }
     }
 
-    /// Returns the events that occured in the last call to `poll` or `ppoll`.
+    /// Returns the events that 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.
     pub fn revents(self) -> Option<PollFlags> {
         PollFlags::from_bits(self.pollfd.revents)
     }
+
+    /// The events of interest for this `PollFd`.
+    pub fn events(self) -> PollFlags {
+        PollFlags::from_bits(self.pollfd.events).unwrap()
+    }
+
+    /// Modify the events of interest for this `PollFd`.
+    pub fn set_events(&mut self, events: PollFlags) {
+        self.pollfd.events = events.bits();
+    }
+}
+
+impl AsRawFd for PollFd {
+    fn as_raw_fd(&self) -> RawFd {
+        self.pollfd.fd
+    }
 }
 
 libc_bitflags! {
diff --git a/src/pty.rs b/src/pty.rs
index a8eb938..facc9aa 100644
--- a/src/pty.rs
+++ b/src/pty.rs
@@ -10,7 +10,7 @@
 
 use crate::sys::termios::Termios;
 use crate::unistd::{self, ForkResult, Pid};
-use crate::{Result, Error, fcntl};
+use crate::{Result, fcntl};
 use crate::errno::Errno;
 
 /// Representation of a master/slave pty pair
@@ -99,7 +99,7 @@
 #[inline]
 pub fn grantpt(fd: &PtyMaster) -> Result<()> {
     if unsafe { libc::grantpt(fd.as_raw_fd()) } < 0 {
-        return Err(Error::from(Errno::last()));
+        return Err(Errno::last());
     }
 
     Ok(())
@@ -145,7 +145,7 @@
     };
 
     if fd < 0 {
-        return Err(Error::from(Errno::last()));
+        return Err(Errno::last());
     }
 
     Ok(PtyMaster(fd))
@@ -171,7 +171,7 @@
 pub unsafe fn ptsname(fd: &PtyMaster) -> Result<String> {
     let name_ptr = libc::ptsname(fd.as_raw_fd());
     if name_ptr.is_null() {
-        return Err(Error::from(Errno::last()));
+        return Err(Errno::last());
     }
 
     let name = CStr::from_ptr(name_ptr);
@@ -195,7 +195,7 @@
     let cname = unsafe {
         let cap = name_buf.capacity();
         if libc::ptsname_r(fd.as_raw_fd(), name_buf_ptr, cap) != 0 {
-            return Err(Error::last());
+            return Err(crate::Error::last());
         }
         CStr::from_ptr(name_buf.as_ptr())
     };
@@ -213,7 +213,7 @@
 #[inline]
 pub fn unlockpt(fd: &PtyMaster) -> Result<()> {
     if unsafe { libc::unlockpt(fd.as_raw_fd()) } < 0 {
-        return Err(Error::from(Errno::last()));
+        return Err(Errno::last());
     }
 
     Ok(())
diff --git a/src/sched.rs b/src/sched.rs
index bf51bc1..c2dd7b8 100644
--- a/src/sched.rs
+++ b/src/sched.rs
@@ -1,3 +1,7 @@
+//! Execution scheduling
+//!
+//! See Also
+//! [sched.h](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sched.h.html)
 use crate::{Errno, Result};
 
 #[cfg(any(target_os = "android", target_os = "linux"))]
@@ -11,38 +15,75 @@
     use std::option::Option;
     use std::os::unix::io::RawFd;
     use crate::unistd::Pid;
-    use crate::{Error, Result};
+    use crate::Result;
 
     // For some functions taking with a parameter of type CloneFlags,
     // only a subset of these flags have an effect.
     libc_bitflags! {
+        /// Options for use with [`clone`]
         pub struct CloneFlags: c_int {
+            /// The calling process and the child process run in the same
+            /// memory space.
             CLONE_VM;
+            /// The caller and the child process share the same  filesystem
+            /// information.
             CLONE_FS;
+            /// The calling process and the child process share the same file
+            /// descriptor table.
             CLONE_FILES;
+            /// The calling process and the child process share the same table
+            /// of signal handlers.
             CLONE_SIGHAND;
+            /// If the calling process is being traced, then trace the child
+            /// also.
             CLONE_PTRACE;
+            /// The execution of the calling process is suspended until the
+            /// child releases its virtual memory resources via a call to
+            /// execve(2) or _exit(2) (as with vfork(2)).
             CLONE_VFORK;
+            /// The parent of the new child  (as returned by getppid(2))
+            /// will be the same as that of the calling process.
             CLONE_PARENT;
+            /// The child is placed in the same thread group as the calling
+            /// process.
             CLONE_THREAD;
+            /// The cloned child is started in a new mount namespace.
             CLONE_NEWNS;
+            /// The child and the calling process share a single list of System
+            /// V semaphore adjustment values
             CLONE_SYSVSEM;
-            CLONE_SETTLS;
-            CLONE_PARENT_SETTID;
-            CLONE_CHILD_CLEARTID;
+            // Not supported by Nix due to lack of varargs support in Rust FFI
+            // CLONE_SETTLS;
+            // Not supported by Nix due to lack of varargs support in Rust FFI
+            // CLONE_PARENT_SETTID;
+            // Not supported by Nix due to lack of varargs support in Rust FFI
+            // CLONE_CHILD_CLEARTID;
+            /// Unused since Linux 2.6.2
+            #[deprecated(since = "0.23.0", note = "Deprecated by Linux 2.6.2")]
             CLONE_DETACHED;
+            /// A tracing process cannot force `CLONE_PTRACE` on this child
+            /// process.
             CLONE_UNTRACED;
-            CLONE_CHILD_SETTID;
+            // Not supported by Nix due to lack of varargs support in Rust FFI
+            // CLONE_CHILD_SETTID;
+            /// Create the process in a new cgroup namespace.
             CLONE_NEWCGROUP;
+            /// Create the process in a new UTS namespace.
             CLONE_NEWUTS;
+            /// Create the process in a new IPC namespace.
             CLONE_NEWIPC;
+            /// Create the process in a new user namespace.
             CLONE_NEWUSER;
+            /// Create the process in a new PID namespace.
             CLONE_NEWPID;
+            /// Create the process in a new network namespace.
             CLONE_NEWNET;
+            /// The new process shares an I/O context with the calling process.
             CLONE_IO;
         }
     }
 
+    /// Type for the function executed by [`clone`].
     pub type CloneCb<'a> = Box<dyn FnMut() -> isize + 'a>;
 
     /// CpuSet represent a bit-mask of CPUs.
@@ -68,7 +109,7 @@
         /// `field` is the CPU id to test
         pub fn is_set(&self, field: usize) -> Result<bool> {
             if field >= CpuSet::count() {
-                Err(Error::from(Errno::EINVAL))
+                Err(Errno::EINVAL)
             } else {
                 Ok(unsafe { libc::CPU_ISSET(field, &self.cpu_set) })
             }
@@ -78,7 +119,7 @@
         /// `field` is the CPU id to add
         pub fn set(&mut self, field: usize) -> Result<()> {
             if field >= CpuSet::count() {
-                Err(Error::from(Errno::EINVAL))
+                Err(Errno::EINVAL)
             } else {
                 unsafe { libc::CPU_SET(field, &mut self.cpu_set); }
                 Ok(())
@@ -89,7 +130,7 @@
         /// `field` is the CPU id to remove
         pub fn unset(&mut self, field: usize) -> Result<()> {
             if field >= CpuSet::count() {
-                Err(Error::from(Errno::EINVAL))
+                Err(Errno::EINVAL)
             } else {
                 unsafe { libc::CPU_CLR(field, &mut self.cpu_set);}
                 Ok(())
@@ -97,7 +138,7 @@
         }
 
         /// Return the maximum number of CPU in CpuSet
-        pub fn count() -> usize {
+        pub const fn count() -> usize {
             8 * mem::size_of::<libc::cpu_set_t>()
         }
     }
@@ -212,12 +253,18 @@
         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()) };
 
diff --git a/src/sys/aio.rs b/src/sys/aio.rs
index b63affb..e64a2a8 100644
--- a/src/sys/aio.rs
+++ b/src/sys/aio.rs
@@ -21,7 +21,7 @@
 //! [`aio_cancel_all`](fn.aio_cancel_all.html), though the operating system may
 //! not support this for all filesystems and devices.
 
-use crate::{Error, Result};
+use crate::Result;
 use crate::errno::Errno;
 use std::os::unix::io::RawFd;
 use libc::{c_void, off_t, size_t};
@@ -39,6 +39,7 @@
     /// Mode for `AioCb::fsync`.  Controls whether only data or both data and
     /// metadata are synced.
     #[repr(i32)]
+    #[non_exhaustive]
     pub enum AioFsyncMode {
         /// do it like `fsync`
         O_SYNC,
@@ -57,9 +58,13 @@
     /// 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,
     }
 }
@@ -143,15 +148,13 @@
     /// # use std::{thread, time};
     /// # use std::os::unix::io::AsRawFd;
     /// # use tempfile::tempfile;
-    /// # fn main() {
     /// 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(Error::from(Errno::EINPROGRESS))) {
+    /// 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>>> {
@@ -224,7 +227,6 @@
     /// # use std::io::Write;
     /// # use std::os::unix::io::AsRawFd;
     /// # use tempfile::tempfile;
-    /// # fn main() {
     /// const INITIAL: &[u8] = b"abcdef123456";
     /// const LEN: usize = 4;
     /// let mut rbuf = vec![0; LEN];
@@ -238,13 +240,12 @@
     ///         SigevNotify::SigevNone,
     ///         LioOpcode::LIO_NOP);
     ///     aiocb.read().unwrap();
-    ///     while (aiocb.error() == Err(Error::from(Errno::EINPROGRESS))) {
+    ///     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,
@@ -399,7 +400,6 @@
     /// # use std::{thread, time};
     /// # use std::os::unix::io::AsRawFd;
     /// # use tempfile::tempfile;
-    /// # fn main() {
     /// const WBUF: &[u8] = b"abcdef123456";
     /// let mut f = tempfile().unwrap();
     /// let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
@@ -409,11 +409,10 @@
     ///     SigevNotify::SigevNone,
     ///     LioOpcode::LIO_NOP);
     /// aiocb.write().unwrap();
-    /// while (aiocb.error() == Err(Error::from(Errno::EINPROGRESS))) {
+    /// 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
@@ -475,7 +474,6 @@
     /// # use std::io::Write;
     /// # use std::os::unix::io::AsRawFd;
     /// # use tempfile::tempfile;
-    /// # fn main() {
     /// let wbuf = b"CDEF";
     /// let mut f = tempfile().unwrap();
     /// let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
@@ -487,13 +485,12 @@
     /// aiocb.write().unwrap();
     /// let cs = aiocb.cancel().unwrap();
     /// if cs == AioCancelStat::AioNotCanceled {
-    ///     while (aiocb.error() == Err(Error::from(Errno::EINPROGRESS))) {
+    ///     while (aiocb.error() == Err(Errno::EINPROGRESS)) {
     ///         thread::sleep(time::Duration::from_millis(10));
     ///     }
     /// }
     /// // Must call `aio_return`, but ignore the result
     /// let _ = aiocb.aio_return();
-    /// # }
     /// ```
     ///
     /// # References
@@ -508,7 +505,7 @@
             libc::AIO_CANCELED => Ok(AioCancelStat::AioCanceled),
             libc::AIO_NOTCANCELED => Ok(AioCancelStat::AioNotCanceled),
             libc::AIO_ALLDONE => Ok(AioCancelStat::AioAllDone),
-            -1 => Err(Error::from(Errno::last())),
+            -1 => Err(Errno::last()),
             _ => panic!("unknown aio_cancel return value")
         }
     }
@@ -519,8 +516,8 @@
         };
         match r {
             0 => Ok(()),
-            num if num > 0 => Err(Error::from(Errno::from_i32(num))),
-            -1 => Err(Error::from(Errno::last())),
+            num if num > 0 => Err(Errno::from_i32(num)),
+            -1 => Err(Errno::last()),
             num => panic!("unknown aio_error return value {:?}", num)
         }
     }
@@ -543,7 +540,6 @@
     /// # use std::{thread, time};
     /// # use std::os::unix::io::AsRawFd;
     /// # use tempfile::tempfile;
-    /// # fn main() {
     /// const WBUF: &[u8] = b"abcdef123456";
     /// let mut f = tempfile().unwrap();
     /// let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
@@ -553,11 +549,10 @@
     ///     SigevNotify::SigevNone,
     ///     LioOpcode::LIO_NOP);
     /// aiocb.write().unwrap();
-    /// while (aiocb.error() == Err(Error::from(Errno::EINPROGRESS))) {
+    /// while (aiocb.error() == Err(Errno::EINPROGRESS)) {
     ///     thread::sleep(time::Duration::from_millis(10));
     /// }
     /// assert_eq!(aiocb.aio_return().unwrap() as usize, WBUF.len());
-    /// # }
     /// ```
     ///
     /// # References
@@ -706,7 +701,6 @@
 /// # use std::io::Write;
 /// # use std::os::unix::io::AsRawFd;
 /// # use tempfile::tempfile;
-/// # fn main() {
 /// let wbuf = b"CDEF";
 /// let mut f = tempfile().unwrap();
 /// let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
@@ -718,13 +712,12 @@
 /// aiocb.write().unwrap();
 /// let cs = aio_cancel_all(f.as_raw_fd()).unwrap();
 /// if cs == AioCancelStat::AioNotCanceled {
-///     while (aiocb.error() == Err(Error::from(Errno::EINPROGRESS))) {
+///     while (aiocb.error() == Err(Errno::EINPROGRESS)) {
 ///         thread::sleep(time::Duration::from_millis(10));
 ///     }
 /// }
 /// // Must call `aio_return`, but ignore the result
 /// let _ = aiocb.aio_return();
-/// # }
 /// ```
 ///
 /// # References
@@ -735,7 +728,7 @@
         libc::AIO_CANCELED => Ok(AioCancelStat::AioCanceled),
         libc::AIO_NOTCANCELED => Ok(AioCancelStat::AioNotCanceled),
         libc::AIO_ALLDONE => Ok(AioCancelStat::AioAllDone),
-        -1 => Err(Error::from(Errno::last())),
+        -1 => Err(Errno::last()),
         _ => panic!("unknown aio_cancel return value")
     }
 }
@@ -754,7 +747,6 @@
 /// # use nix::sys::signal::SigevNotify;
 /// # use std::os::unix::io::AsRawFd;
 /// # use tempfile::tempfile;
-/// # fn main() {
 /// const WBUF: &[u8] = b"abcdef123456";
 /// let mut f = tempfile().unwrap();
 /// let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
@@ -766,7 +758,6 @@
 /// aiocb.write().unwrap();
 /// aio_suspend(&[aiocb.as_ref()], None).expect("aio_suspend failed");
 /// assert_eq!(aiocb.aio_return().unwrap() as usize, WBUF.len());
-/// # }
 /// ```
 /// # References
 ///
@@ -838,6 +829,7 @@
 
 #[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()
     }
@@ -870,7 +862,6 @@
     /// # use nix::sys::signal::SigevNotify;
     /// # use std::os::unix::io::AsRawFd;
     /// # use tempfile::tempfile;
-    /// # fn main() {
     /// const WBUF: &[u8] = b"abcdef123456";
     /// let mut f = tempfile().unwrap();
     /// let mut liocb = LioCbBuilder::with_capacity(1)
@@ -885,7 +876,6 @@
     /// liocb.listio(LioMode::LIO_WAIT,
     ///              SigevNotify::SigevNone).unwrap();
     /// assert_eq!(liocb.aio_return(0).unwrap() as usize, WBUF.len());
-    /// # }
     /// ```
     ///
     /// # References
@@ -930,7 +920,6 @@
     /// # use std::os::unix::io::AsRawFd;
     /// # use std::{thread, time};
     /// # use tempfile::tempfile;
-    /// # fn main() {
     /// const WBUF: &[u8] = b"abcdef123456";
     /// let mut f = tempfile().unwrap();
     /// let mut liocb = LioCbBuilder::with_capacity(1)
@@ -943,13 +932,12 @@
     ///         LioOpcode::LIO_WRITE
     ///     ).finish();
     /// let mut err = liocb.listio(LioMode::LIO_WAIT, SigevNotify::SigevNone);
-    /// while err == Err(Error::from(Errno::EIO)) ||
-    ///       err == Err(Error::from(Errno::EAGAIN)) {
+    /// 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
diff --git a/src/sys/epoll.rs b/src/sys/epoll.rs
index b73af13..6bc2a25 100644
--- a/src/sys/epoll.rs
+++ b/src/sys/epoll.rs
@@ -1,4 +1,4 @@
-use crate::{Error, Result};
+use crate::Result;
 use crate::errno::Errno;
 use libc::{self, c_int};
 use std::os::unix::io::RawFd;
@@ -29,6 +29,7 @@
 
 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
 #[repr(i32)]
+#[non_exhaustive]
 pub enum EpollOp {
     EpollCtlAdd = libc::EPOLL_CTL_ADD,
     EpollCtlDel = libc::EPOLL_CTL_DEL,
@@ -85,7 +86,7 @@
 {
     let mut event: Option<&mut EpollEvent> = event.into();
     if event.is_none() && op != EpollOp::EpollCtlDel {
-        Err(Error::from(Errno::EINVAL))
+        Err(Errno::EINVAL)
     } else {
         let res = unsafe {
             if let Some(ref mut event) = event {
diff --git a/src/sys/event.rs b/src/sys/event.rs
index 8050af3..c648f5e 100644
--- a/src/sys/event.rs
+++ b/src/sys/event.rs
@@ -6,9 +6,9 @@
 use libc::{timespec, time_t, c_int, c_long, intptr_t, uintptr_t};
 #[cfg(target_os = "netbsd")]
 use libc::{timespec, time_t, c_long, intptr_t, uintptr_t, size_t};
+use std::convert::TryInto;
 use std::os::unix::io::RawFd;
 use std::ptr;
-use std::mem;
 
 // Redefine kevent in terms of programmer-friendly enums and bitfields.
 #[repr(C)]
@@ -36,6 +36,7 @@
 libc_enum! {
     #[cfg_attr(target_os = "netbsd", repr(u32))]
     #[cfg_attr(not(target_os = "netbsd"), repr(i16))]
+    #[non_exhaustive]
     pub enum EventFilter {
         EVFILT_AIO,
         /// Returns whenever there is no remaining data in the write buffer
@@ -75,6 +76,7 @@
         EVFILT_VNODE,
         EVFILT_WRITE,
     }
+    impl TryFrom<type_of_event_filter>
 }
 
 #[cfg(any(target_os = "dragonfly", target_os = "freebsd",
@@ -232,8 +234,8 @@
         self.kevent.ident
     }
 
-    pub fn filter(&self) -> EventFilter {
-        unsafe { mem::transmute(self.kevent.filter as type_of_event_filter) }
+    pub fn filter(&self) -> Result<EventFilter> {
+        self.kevent.filter.try_into()
     }
 
     pub fn flags(&self) -> EventFlag {
@@ -312,6 +314,8 @@
 
 #[test]
 fn test_struct_kevent() {
+    use std::mem;
+
     let udata : intptr_t = 12345;
 
     let actual = KEvent::new(0xdead_beef,
@@ -321,10 +325,24 @@
                              0x1337,
                              udata);
     assert_eq!(0xdead_beef, actual.ident());
-    assert_eq!(libc::EVFILT_READ, actual.filter() as type_of_event_filter);
+    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!(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 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 baaaa89..c54f952 100644
--- a/src/sys/eventfd.rs
+++ b/src/sys/eventfd.rs
@@ -1,4 +1,3 @@
-use libc;
 use std::os::unix::io::RawFd;
 use crate::Result;
 use crate::errno::Errno;
diff --git a/src/sys/memfd.rs b/src/sys/memfd.rs
index 51b7e6b..642676b 100644
--- a/src/sys/memfd.rs
+++ b/src/sys/memfd.rs
@@ -1,16 +1,43 @@
-use libc;
+//! Interfaces for managing memory-backed files.
+
 use std::os::unix::io::RawFd;
 use crate::Result;
 use crate::errno::Errno;
 use std::ffi::CStr;
 
 libc_bitflags!(
+    /// Options that change the behavior of [`memfd_create`].
     pub struct MemFdCreateFlag: libc::c_uint {
+        /// Set the close-on-exec ([`FD_CLOEXEC`]) flag on the new file descriptor.
+        ///
+        /// By default, the new file descriptor is set to remain open across an [`execve`]
+        /// (the `FD_CLOEXEC` flag is initially disabled). This flag can be used to change
+        /// this default. The file offset is set to the beginning of the file (see [`lseek`]).
+        ///
+        /// See also the description of the `O_CLOEXEC` flag in [`open(2)`].
+        ///
+        /// [`execve`]: crate::unistd::execve
+        /// [`lseek`]: crate::unistd::lseek
+        /// [`FD_CLOEXEC`]: crate::fcntl::FdFlag::FD_CLOEXEC
+        /// [`open(2)`]: https://man7.org/linux/man-pages/man2/open.2.html
         MFD_CLOEXEC;
+        /// Allow sealing operations on this file.
+        ///
+        /// See also the file sealing notes given in [`memfd_create(2)`].
+        ///
+        /// [`memfd_create(2)`]: https://man7.org/linux/man-pages/man2/memfd_create.2.html
         MFD_ALLOW_SEALING;
     }
 );
 
+/// Creates an anonymous file that lives in memory, and return a file-descriptor to it.
+///
+/// The file behaves like a regular file, and so can be modified, truncated, memory-mapped, and so on.
+/// However, unlike a regular file, it lives in RAM and has a volatile backing storage.
+///
+/// For more information, see [`memfd_create(2)`].
+///
+/// [`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())
diff --git a/src/sys/mman.rs b/src/sys/mman.rs
index 58edf08..0ef1ca8 100644
--- a/src/sys/mman.rs
+++ b/src/sys/mman.rs
@@ -1,4 +1,6 @@
-use crate::{Error, Result};
+//! Memory management declarations.
+
+use crate::Result;
 #[cfg(not(target_os = "android"))]
 use crate::NixPath;
 use crate::errno::Errno;
@@ -30,7 +32,7 @@
 }
 
 libc_bitflags!{
-    /// Additional parameters for `mmap()`.
+    /// Additional parameters for [`mmap`].
     pub struct MapFlags: c_int {
         /// Compatibility flag. Ignored.
         MAP_FILE;
@@ -40,10 +42,13 @@
         MAP_PRIVATE;
         /// Place the mapping at exactly the address specified in `addr`.
         MAP_FIXED;
+        /// To be used with `MAP_FIXED`, to forbid the system
+        /// to select a different address than the one specified.
+        #[cfg(target_os = "freebsd")]
+        MAP_EXCL;
         /// Synonym for `MAP_ANONYMOUS`.
         MAP_ANON;
         /// The mapping is not backed by any file.
-        #[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
         MAP_ANONYMOUS;
         /// Put the mapping into the first 2GB of the process address space.
         #[cfg(any(all(any(target_os = "android", target_os = "linux"),
@@ -65,8 +70,8 @@
         MAP_LOCKED;
         /// Do not reserve swap space for this mapping.
         ///
-        /// This was removed in FreeBSD 11.
-        #[cfg(not(target_os = "freebsd"))]
+        /// This was removed in FreeBSD 11 and is unused in DragonFlyBSD.
+        #[cfg(not(any(target_os = "dragonfly", target_os = "freebsd")))]
         MAP_NORESERVE;
         /// Populate page tables for a mapping.
         #[cfg(any(target_os = "android", target_os = "linux"))]
@@ -122,39 +127,55 @@
         MAP_NOSYNC;
         /// Rename private pages to a file.
         ///
-        /// This was removed in FreeBSD 11.
-        #[cfg(any(target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd"))]
+        /// This was removed in FreeBSD 11 and is unused in DragonFlyBSD.
+        #[cfg(any(target_os = "netbsd", target_os = "openbsd"))]
         MAP_RENAME;
         /// Region may contain semaphores.
         #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
         MAP_HASSEMAPHORE;
         /// Region grows down, like a stack.
-        #[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", target_os = "openbsd"))]
         MAP_STACK;
         /// Pages in this mapping are not retained in the kernel's memory cache.
         #[cfg(any(target_os = "ios", target_os = "macos"))]
         MAP_NOCACHE;
+        /// Allows the W/X bit on the page, it's necessary on aarch64 architecture.
         #[cfg(any(target_os = "ios", target_os = "macos"))]
         MAP_JIT;
+        /// Allows to use large pages, underlying alignment based on size.
+        #[cfg(target_os = "freebsd")]
+        MAP_ALIGNED_SUPER;
+        /// Pages will be discarded in the core dumps.
+        #[cfg(target_os = "openbsd")]
+        MAP_CONCEAL;
     }
 }
 
-#[cfg(target_os = "linux")]
+#[cfg(any(target_os = "linux", target_os = "netbsd"))]
 libc_bitflags!{
-    /// Options for `mremap()`.
+    /// 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")]
         MREMAP_MAYMOVE;
         /// Place the mapping at exactly the address specified in `new_address`.
+        #[cfg(target_os = "linux")]
         MREMAP_FIXED;
+        /// Permits to use the old and new address as hints to relocate the mapping.
+        #[cfg(target_os = "netbsd")]
+        MAP_FIXED;
+        /// Allows to duplicate the mapping to be able to apply different flags on the copy.
+        #[cfg(target_os = "netbsd")]
+        MAP_REMAPDUP;
     }
 }
 
 libc_enum!{
     /// Usage information for a range of memory to allow for performance optimizations by the kernel.
     ///
-    /// Used by [`madvise`](./fn.madvise.html).
+    /// Used by [`madvise`].
     #[repr(i32)]
+    #[non_exhaustive]
     pub enum MmapAdvise {
         /// No further special treatment. This is the default.
         MADV_NORMAL,
@@ -191,7 +212,8 @@
             all(target_os = "linux", any(
                 target_arch = "aarch64",
                 target_arch = "arm",
-                target_arch = "ppc",
+                target_arch = "powerpc",
+                target_arch = "powerpc64",
                 target_arch = "s390x",
                 target_arch = "x86",
                 target_arch = "x86_64",
@@ -244,7 +266,7 @@
 }
 
 libc_bitflags!{
-    /// Configuration flags for `msync`.
+    /// Configuration flags for [`msync`].
     pub struct MsFlags: c_int {
         /// Schedule an update but return immediately.
         MS_ASYNC;
@@ -262,7 +284,7 @@
 }
 
 libc_bitflags!{
-    /// Flags for `mlockall`.
+    /// Flags for [`mlockall`].
     pub struct MlockAllFlags: c_int {
         /// Lock pages that are currently mapped into the address space of the process.
         MCL_CURRENT;
@@ -278,7 +300,9 @@
 ///
 /// # Safety
 ///
-/// `addr` must meet all the requirements described in the `mlock(2)` man page.
+/// `addr` must meet all the requirements described in the [`mlock(2)`] man page.
+///
+/// [`mlock(2)`]: https://man7.org/linux/man-pages/man2/mlock.2.html
 pub unsafe fn mlock(addr: *const c_void, length: size_t) -> Result<()> {
     Errno::result(libc::mlock(addr, length)).map(drop)
 }
@@ -288,25 +312,28 @@
 ///
 /// # Safety
 ///
-/// `addr` must meet all the requirements described in the `munlock(2)` man
+/// `addr` must meet all the requirements described in the [`munlock(2)`] man
 /// page.
+///
+/// [`munlock(2)`]: https://man7.org/linux/man-pages/man2/munlock.2.html
 pub unsafe fn munlock(addr: *const c_void, length: size_t) -> Result<()> {
     Errno::result(libc::munlock(addr, length)).map(drop)
 }
 
 /// Locks all memory pages mapped into this process' address space.
 ///
-/// Locked pages never move to the swap area.
+/// Locked pages never move to the swap area. For more information, see [`mlockall(2)`].
 ///
-/// # Safety
-///
-/// `addr` must meet all the requirements described in the `mlockall(2)` man
-/// page.
+/// [`mlockall(2)`]: https://man7.org/linux/man-pages/man2/mlockall.2.html
 pub fn mlockall(flags: MlockAllFlags) -> Result<()> {
     unsafe { Errno::result(libc::mlockall(flags.bits())) }.map(drop)
 }
 
 /// Unlocks all memory pages mapped into this process' address space.
+///
+/// For more information, see [`munlockall(2)`].
+///
+/// [`munlockall(2)`]: https://man7.org/linux/man-pages/man2/munlockall.2.html
 pub fn munlockall() -> Result<()> {
     unsafe { Errno::result(libc::munlockall()) }.map(drop)
 }
@@ -315,12 +342,14 @@
 ///
 /// # Safety
 ///
-/// See the `mmap(2)` man page for detailed requirements.
+/// 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);
 
     if ret == libc::MAP_FAILED {
-        Err(Error::from(Errno::last()))
+        Err(Errno::last())
     } else {
         Ok(ret)
     }
@@ -333,7 +362,7 @@
 ///
 /// See the `mremap(2)` [man page](https://man7.org/linux/man-pages/man2/mremap.2.html) for
 /// detailed requirements.
-#[cfg(target_os = "linux")]
+#[cfg(any(target_os = "linux", target_os = "netbsd"))]
 pub unsafe fn mremap(
     addr: *mut c_void,
     old_size: size_t,
@@ -341,10 +370,19 @@
     flags: MRemapFlags,
     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()));
+    #[cfg(target_os = "netbsd")]
+    let ret = libc::mremap(
+        addr,
+        old_size,
+        new_address.unwrap_or(std::ptr::null_mut()),
+        new_size,
+        flags.bits(),
+        );
 
     if ret == libc::MAP_FAILED {
-        Err(Error::from(Errno::last()))
+        Err(Errno::last())
     } else {
         Ok(ret)
     }
@@ -354,8 +392,10 @@
 ///
 /// # Safety
 ///
-/// `addr` must meet all the requirements described in the `munmap(2)` man
+/// `addr` must meet all the requirements described in the [`munmap(2)`] man
 /// page.
+///
+/// [`munmap(2)`]: https://man7.org/linux/man-pages/man2/munmap.2.html
 pub unsafe fn munmap(addr: *mut c_void, len: size_t) -> Result<()> {
     Errno::result(libc::munmap(addr, len)).map(drop)
 }
@@ -364,8 +404,10 @@
 ///
 /// # Safety
 ///
-/// See the `madvise(2)` man page.  Take special care when using
-/// `MmapAdvise::MADV_FREE`.
+/// See the [`madvise(2)`] man page.  Take special care when using
+/// [`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<()> {
     Errno::result(libc::madvise(addr, length, advise as i32)).map(drop)
 }
@@ -403,12 +445,19 @@
 ///
 /// # Safety
 ///
-/// `addr` must meet all the requirements described in the `msync(2)` man
+/// `addr` must meet all the requirements described in the [`msync(2)`] man
 /// 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<()> {
     Errno::result(libc::msync(addr, length, flags.bits())).map(drop)
 }
 
+/// 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> {
     let ret = name.with_nix_path(|cstr| {
@@ -425,6 +474,11 @@
     Errno::result(ret)
 }
 
+/// Performs the converse of [`shm_open`], removing an object previously created.
+///
+/// For more information, see [`shm_unlink(3)`].
+///
+/// [`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| {
diff --git a/src/sys/mod.rs b/src/sys/mod.rs
index 51f0eed..156b0d9 100644
--- a/src/sys/mod.rs
+++ b/src/sys/mod.rs
@@ -1,3 +1,4 @@
+//! Mostly platform-specific functionality
 #[cfg(any(target_os = "dragonfly",
           target_os = "freebsd",
           target_os = "ios",
@@ -7,6 +8,7 @@
 pub mod aio;
 
 #[cfg(any(target_os = "android", target_os = "linux"))]
+#[allow(missing_docs)]
 pub mod epoll;
 
 #[cfg(any(target_os = "dragonfly",
@@ -15,9 +17,11 @@
           target_os = "macos",
           target_os = "netbsd",
           target_os = "openbsd"))]
+#[allow(missing_docs)]
 pub mod event;
 
-#[cfg(target_os = "linux")]
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[allow(missing_docs)]
 pub mod eventfd;
 
 #[cfg(any(target_os = "android",
@@ -37,9 +41,11 @@
 pub mod memfd;
 
 #[cfg(not(target_os = "redox"))]
+#[allow(missing_docs)]
 pub mod mman;
 
 #[cfg(target_os = "linux")]
+#[allow(missing_docs)]
 pub mod personality;
 
 pub mod pthread;
@@ -51,14 +57,19 @@
           target_os = "macos",
           target_os = "netbsd",
           target_os = "openbsd"))]
+#[allow(missing_docs)]
 pub mod ptrace;
 
 #[cfg(target_os = "linux")]
 pub mod quota;
 
 #[cfg(any(target_os = "linux"))]
+#[allow(missing_docs)]
 pub mod reboot;
 
+#[cfg(not(any(target_os = "redox", target_os = "fuchsia", target_os = "illumos")))]
+pub mod resource;
+
 #[cfg(not(target_os = "redox"))]
 pub mod select;
 
@@ -72,11 +83,14 @@
 pub mod signal;
 
 #[cfg(any(target_os = "android", target_os = "linux"))]
+#[allow(missing_docs)]
 pub mod signalfd;
 
 #[cfg(not(target_os = "redox"))]
+#[allow(missing_docs)]
 pub mod socket;
 
+#[allow(missing_docs)]
 pub mod stat;
 
 #[cfg(any(target_os = "android",
@@ -92,10 +106,13 @@
 pub mod statvfs;
 
 #[cfg(any(target_os = "android", target_os = "linux"))]
+#[allow(missing_docs)]
 pub mod sysinfo;
 
+#[allow(missing_docs)]
 pub mod termios;
 
+#[allow(missing_docs)]
 pub mod time;
 
 pub mod uio;
@@ -105,7 +122,9 @@
 pub mod wait;
 
 #[cfg(any(target_os = "android", target_os = "linux"))]
+#[allow(missing_docs)]
 pub mod inotify;
 
 #[cfg(any(target_os = "android", target_os = "linux"))]
+#[allow(missing_docs)]
 pub mod timerfd;
diff --git a/src/sys/personality.rs b/src/sys/personality.rs
index 6548b65..b15956c 100644
--- a/src/sys/personality.rs
+++ b/src/sys/personality.rs
@@ -39,7 +39,7 @@
         libc::personality(0xFFFFFFFF)
     };
 
-    Errno::result(res).map(|r| Persona::from_bits_truncate(r))
+    Errno::result(res).map(Persona::from_bits_truncate)
 }
 
 /// Set the current process personality.
@@ -66,5 +66,5 @@
         libc::personality(persona.bits() as c_ulong)
     };
 
-    Errno::result(res).map(|r| Persona::from_bits_truncate(r))
+    Errno::result(res).map(Persona::from_bits_truncate)
 }
diff --git a/src/sys/pthread.rs b/src/sys/pthread.rs
index f730408..d42e45d 100644
--- a/src/sys/pthread.rs
+++ b/src/sys/pthread.rs
@@ -1,5 +1,14 @@
+//! Low level threading primitives
+
+#[cfg(not(target_os = "redox"))]
+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.
 pub type Pthread = pthread_t;
 
 /// Obtain ID of the calling thread (see
@@ -11,3 +20,19 @@
 pub fn pthread_self() -> Pthread {
     unsafe { libc::pthread_self() }
 }
+
+/// 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
+#[cfg(not(target_os = "redox"))]
+pub fn pthread_kill<T: Into<Option<Signal>>>(thread: Pthread, signal: T) -> Result<()> {
+    let sig = match signal.into() {
+        Some(s) => s as libc::c_int,
+        None => 0,
+    };
+    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 141dfbc..a62881e 100644
--- a/src/sys/ptrace/bsd.rs
+++ b/src/sys/ptrace/bsd.rs
@@ -24,6 +24,7 @@
 libc_enum! {
     #[repr(i32)]
     /// Ptrace Request enum defining the action to be taken.
+    #[non_exhaustive]
     pub enum Request {
         PT_TRACE_ME,
         PT_READ_I,
diff --git a/src/sys/ptrace/linux.rs b/src/sys/ptrace/linux.rs
index 4ac4393..3723679 100644
--- a/src/sys/ptrace/linux.rs
+++ b/src/sys/ptrace/linux.rs
@@ -33,6 +33,7 @@
     #[cfg_attr(not(any(target_env = "musl", target_os = "android")), repr(u32))]
     #[cfg_attr(any(target_env = "musl", target_os = "android"), repr(i32))]
     /// Ptrace Request enum defining the action to be taken.
+    #[non_exhaustive]
     pub enum Request {
         PTRACE_TRACEME,
         PTRACE_PEEKTEXT,
@@ -97,11 +98,9 @@
         #[cfg(all(target_os = "linux", not(any(target_arch = "mips",
                                                target_arch = "mips64"))))]
         PTRACE_SETREGSET,
-        #[cfg(all(target_os = "linux", not(any(target_arch = "mips",
-                                               target_arch = "mips64"))))]
+        #[cfg(target_os = "linux")]
         PTRACE_SEIZE,
-        #[cfg(all(target_os = "linux", not(any(target_arch = "mips",
-                                               target_arch = "mips64"))))]
+        #[cfg(target_os = "linux")]
         PTRACE_INTERRUPT,
         #[cfg(all(target_os = "linux", not(any(target_arch = "mips",
                                                target_arch = "mips64"))))]
@@ -123,6 +122,7 @@
     /// 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
     /// in `man ptrace`.
+    #[non_exhaustive]
     pub enum Event {
         /// Event that stops before a return from fork or clone.
         PTRACE_EVENT_FORK,
@@ -137,9 +137,11 @@
         /// Event for a stop before an exit. Unlike the waitpid Exit status program.
         /// registers can still be examined
         PTRACE_EVENT_EXIT,
-        /// STop triggered by a seccomp rule on a tracee.
+        /// Stop triggered by a seccomp rule on a tracee.
         PTRACE_EVENT_SECCOMP,
-        // PTRACE_EVENT_STOP not provided by libc because it's defined in glibc 2.26
+        /// Stop triggered by the `INTERRUPT` syscall, or a group stop,
+        /// or when a new child is attached.
+        PTRACE_EVENT_STOP,
     }
 }
 
@@ -337,7 +339,7 @@
 /// Attach to a running process, as with `ptrace(PTRACE_SEIZE, ...)`
 ///
 /// Attaches to the process specified in pid, making it a tracee of the calling process.
-#[cfg(all(target_os = "linux", not(any(target_arch = "mips", target_arch = "mips64"))))]
+#[cfg(target_os = "linux")]
 pub fn seize(pid: Pid, options: Options) -> Result<()> {
     unsafe {
         ptrace_other(
@@ -382,6 +384,16 @@
     }
 }
 
+/// Stop a tracee, as with `ptrace(PTRACE_INTERRUPT, ...)`
+///
+/// This request is equivalent to `ptrace(PTRACE_INTERRUPT, ...)`
+#[cfg(target_os = "linux")]
+pub fn interrupt(pid: Pid) -> Result<()> {
+    unsafe {
+        ptrace_other(Request::PTRACE_INTERRUPT, pid, ptr::null_mut(), ptr::null_mut()).map(drop)
+    }
+}
+
 /// Issues a kill request as with `ptrace(PTRACE_KILL, ...)`
 ///
 /// This request is equivalent to `ptrace(PTRACE_CONT, ..., SIGKILL);`
@@ -391,7 +403,7 @@
     }
 }
 
-/// Move the stopped tracee process forward by a single step as with 
+/// Move the stopped tracee process forward by a single step as with
 /// `ptrace(PTRACE_SINGLESTEP, ...)`
 ///
 /// Advances the execution of the process with PID `pid` by a single step optionally delivering a
@@ -401,18 +413,17 @@
 /// ```rust
 /// use nix::sys::ptrace::step;
 /// use nix::unistd::Pid;
-/// use nix::sys::signal::Signal; 
+/// use nix::sys::signal::Signal;
 /// use nix::sys::wait::*;
-/// fn main() {
-///     // If a process changes state to the stopped state because of a SIGUSR1 
-///     // signal, this will step the process forward and forward the user 
-///     // signal to the stopped process
-///     match waitpid(Pid::from_raw(-1), None) {
-///         Ok(WaitStatus::Stopped(pid, Signal::SIGUSR1)) => {
-///             let _ = step(pid, Signal::SIGUSR1);
-///         }
-///         _ => {},
+///
+/// // If a process changes state to the stopped state because of a SIGUSR1
+/// // signal, this will step the process forward and forward the user
+/// // signal to the stopped process
+/// match waitpid(Pid::from_raw(-1), None) {
+///     Ok(WaitStatus::Stopped(pid, Signal::SIGUSR1)) => {
+///         let _ = step(pid, Signal::SIGUSR1);
 ///     }
+///     _ => {},
 /// }
 /// ```
 pub fn step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
diff --git a/src/sys/quota.rs b/src/sys/quota.rs
index 1933013..6e34e38 100644
--- a/src/sys/quota.rs
+++ b/src/sys/quota.rs
@@ -42,6 +42,7 @@
 libc_enum!{
     /// The scope of the quota.
     #[repr(i32)]
+    #[non_exhaustive]
     pub enum QuotaType {
         /// Specify a user quota
         USRQUOTA,
@@ -53,6 +54,7 @@
 libc_enum!{
     /// The type of quota format to use.
     #[repr(i32)]
+    #[non_exhaustive]
     pub enum QuotaFmt {
         /// Use the original quota format.
         QFMT_VFS_OLD,
diff --git a/src/sys/reboot.rs b/src/sys/reboot.rs
index 5b37682..46ab68b 100644
--- a/src/sys/reboot.rs
+++ b/src/sys/reboot.rs
@@ -1,8 +1,7 @@
 //! Reboot/shutdown or enable/disable Ctrl-Alt-Delete.
 
-use crate::{Error, Result};
+use crate::Result;
 use crate::errno::Errno;
-use libc;
 use std::convert::Infallible;
 use std::mem::drop;
 
@@ -12,6 +11,7 @@
     /// See [`set_cad_enabled()`](fn.set_cad_enabled.html) for
     /// enabling/disabling Ctrl-Alt-Delete.
     #[repr(i32)]
+    #[non_exhaustive]
     pub enum RebootMode {
         RB_HALT_SYSTEM,
         RB_KEXEC,
@@ -26,7 +26,7 @@
     unsafe {
         libc::reboot(how as libc::c_int)
     };
-    Err(Error::from(Errno::last()))
+    Err(Errno::last())
 }
 
 /// Enable or disable the reboot keystroke (Ctrl-Alt-Delete).
diff --git a/src/sys/resource.rs b/src/sys/resource.rs
new file mode 100644
index 0000000..f3bfb67
--- /dev/null
+++ b/src/sys/resource.rs
@@ -0,0 +1,233 @@
+//! Configure the process resource limits.
+use cfg_if::cfg_if;
+
+use crate::errno::Errno;
+use crate::Result;
+pub use libc::rlim_t;
+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(
+        target_os = "freebsd",
+        target_os = "openbsd",
+        target_os = "netbsd",
+        target_os = "macos",
+        target_os = "ios",
+        target_os = "android",
+        target_os = "dragonfly",
+        all(target_os = "linux", not(target_env = "gnu"))
+    ))]{
+        use libc::{c_int, rlimit, RLIM_INFINITY};
+    }
+}
+
+libc_enum! {
+    /// The Resource enum is platform dependent. Check different platform
+    /// manuals for more details. Some platform links has been provided for
+    /// earier reference (non-exhaustive).
+    ///
+    /// * [Linux](https://man7.org/linux/man-pages/man2/getrlimit.2.html)
+    /// * [FreeBSD](https://www.freebsd.org/cgi/man.cgi?query=setrlimit)
+
+    // 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(any(
+            target_os = "freebsd",
+            target_os = "openbsd",
+            target_os = "netbsd",
+            target_os = "macos",
+            target_os = "ios",
+            target_os = "android",
+            target_os = "dragonfly",
+            all(target_os = "linux", not(target_env = "gnu"))
+        ), repr(i32))]
+    #[non_exhaustive]
+    pub enum Resource {
+        #[cfg(not(any(
+                    target_os = "freebsd",
+                    target_os = "netbsd",
+                    target_os = "openbsd"
+        )))]
+        /// The maximum amount (in bytes) of virtual memory the process is
+        /// allowed to map.
+        RLIMIT_AS,
+        /// The largest size (in bytes) core(5) file that may be created.
+        RLIMIT_CORE,
+        /// The maximum amount of cpu time (in seconds) to be used by each
+        /// process.
+        RLIMIT_CPU,
+        /// The maximum size (in bytes) of the data segment for a process
+        RLIMIT_DATA,
+        /// The largest size (in bytes) file that may be created.
+        RLIMIT_FSIZE,
+        /// The maximum number of open files for this process.
+        RLIMIT_NOFILE,
+        /// The maximum size (in bytes) of the stack segment for a process.
+        RLIMIT_STACK,
+
+        #[cfg(target_os = "freebsd")]
+        /// The maximum number of kqueues this user id is allowed to create.
+        RLIMIT_KQUEUES,
+
+        #[cfg(any(target_os = "android", target_os = "linux"))]
+        /// 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"))]
+        /// 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"))]
+        /// 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"))]
+        /// 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"))]
+        /// The maximum number of simultaneous processes for this user id.
+        RLIMIT_NPROC,
+
+        #[cfg(target_os = "freebsd")]
+        /// 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"))]
+        /// 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"))]
+        /// 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"))]
+        /// 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"))]
+        /// 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"))]
+        /// The maximum size (in bytes) of socket buffer usage for this user.
+        RLIMIT_SBSIZE,
+
+        #[cfg(target_os = "freebsd")]
+        /// 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")]
+        /// An alias for RLIMIT_AS.
+        RLIMIT_VMEM,
+    }
+}
+
+/// Get the current processes resource limits
+///
+/// A value of `None` indicates the value equals to `RLIM_INFINITY` which means
+/// there is no limit.
+///
+/// # Parameters
+///
+/// * `resource`: The [`Resource`] that we want to get the limits of.
+///
+/// # Examples
+///
+/// ```
+/// # 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);
+/// ```
+///
+/// # References
+///
+/// [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>)> {
+    let mut old_rlim = mem::MaybeUninit::<rlimit>::uninit();
+
+    cfg_if! {
+        if #[cfg(all(target_os = "linux", target_env = "gnu"))]{
+            let res = unsafe { libc::getrlimit(resource as __rlimit_resource_t, old_rlim.as_mut_ptr()) };
+        }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))
+    })
+}
+
+/// Set the current processes resource limits
+///
+/// # Parameters
+///
+/// * `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`.
+/// * `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`.
+///
+/// > 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.
+///
+/// # Examples
+///
+/// ```
+/// # use nix::sys::resource::{setrlimit, Resource};
+///
+/// let soft_limit = Some(512);
+/// let hard_limit = Some(1024);
+/// setrlimit(Resource::RLIMIT_NOFILE, soft_limit, hard_limit).unwrap();
+/// ```
+///
+/// # References
+///
+/// [setrlimit(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getrlimit.html#tag_16_215)
+///
+/// [`Resource`]: enum.Resource.html
+///
+/// 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>,
+) -> Result<()> {
+    let new_rlim = rlimit {
+        rlim_cur: soft_limit.unwrap_or(RLIM_INFINITY),
+        rlim_max: hard_limit.unwrap_or(RLIM_INFINITY),
+    };
+    cfg_if! {
+        if #[cfg(all(target_os = "linux", target_env = "gnu"))]{
+            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) };
+        }
+    }
+
+    Errno::result(res).map(drop)
+}
diff --git a/src/sys/select.rs b/src/sys/select.rs
index 5eb6423..4d7576a 100644
--- a/src/sys/select.rs
+++ b/src/sys/select.rs
@@ -1,3 +1,5 @@
+//! Portably monitor a group of file descriptors for readiness.
+use std::convert::TryFrom;
 use std::iter::FusedIterator;
 use std::mem;
 use std::ops::Range;
@@ -11,11 +13,20 @@
 
 pub use libc::FD_SETSIZE;
 
+/// Contains a set of file descriptors used by [`select`]
 #[repr(transparent)]
 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
 pub struct FdSet(libc::fd_set);
 
+fn assert_fd_valid(fd: RawFd) {
+    assert!(
+        usize::try_from(fd).map_or(false, |fd| fd < FD_SETSIZE),
+        "fd must be in the range 0..FD_SETSIZE",
+    );
+}
+
 impl FdSet {
+    /// Create an empty `FdSet`
     pub fn new() -> FdSet {
         let mut fdset = mem::MaybeUninit::uninit();
         unsafe {
@@ -24,18 +35,25 @@
         }
     }
 
+    /// Add a file descriptor to an `FdSet`
     pub fn insert(&mut self, fd: RawFd) {
+        assert_fd_valid(fd);
         unsafe { libc::FD_SET(fd, &mut self.0) };
     }
 
+    /// Remove a file descriptor from an `FdSet`
     pub fn remove(&mut self, fd: RawFd) {
+        assert_fd_valid(fd);
         unsafe { libc::FD_CLR(fd, &mut self.0) };
     }
 
-    pub fn contains(&mut self, fd: RawFd) -> bool {
-        unsafe { libc::FD_ISSET(fd, &mut self.0) }
+    /// Test an `FdSet` for the presence of a certain file descriptor.
+    pub fn contains(&self, fd: RawFd) -> bool {
+        assert_fd_valid(fd);
+        unsafe { libc::FD_ISSET(fd, &self.0) }
     }
 
+    /// Remove all file descriptors from this `FdSet`.
     pub fn clear(&mut self) {
         unsafe { libc::FD_ZERO(&mut self.0) };
     }
@@ -57,7 +75,7 @@
     /// ```
     ///
     /// [`select`]: fn.select.html
-    pub fn highest(&mut self) -> Option<RawFd> {
+    pub fn highest(&self) -> Option<RawFd> {
         self.fds(None).next_back()
     }
 
@@ -79,7 +97,7 @@
     /// assert_eq!(fds, vec![4, 9]);
     /// ```
     #[inline]
-    pub fn fds(&mut self, highest: Option<RawFd>) -> Fds {
+    pub fn fds(&self, highest: Option<RawFd>) -> Fds {
         Fds {
             set: self,
             range: 0..highest.map(|h| h as usize + 1).unwrap_or(FD_SETSIZE),
@@ -96,7 +114,7 @@
 /// Iterator over `FdSet`.
 #[derive(Debug)]
 pub struct Fds<'a> {
-    set: &'a mut FdSet,
+    set: &'a FdSet,
     range: Range<usize>,
 }
 
@@ -104,7 +122,7 @@
     type Item = RawFd;
 
     fn next(&mut self) -> Option<RawFd> {
-        while let Some(i) = self.range.next() {
+        for i in &mut self.range {
             if self.set.contains(i as RawFd) {
                 return Some(i as RawFd);
             }
diff --git a/src/sys/sendfile.rs b/src/sys/sendfile.rs
index a12c041..7a210c6 100644
--- a/src/sys/sendfile.rs
+++ b/src/sys/sendfile.rs
@@ -1,3 +1,5 @@
+//! Send data from a file to a socket, bypassing userland.
+
 use cfg_if::cfg_if;
 use std::os::unix::io::RawFd;
 use std::ptr;
diff --git a/src/sys/signal.rs b/src/sys/signal.rs
index 273b352..61bdc74 100644
--- a/src/sys/signal.rs
+++ b/src/sys/signal.rs
@@ -1,12 +1,11 @@
 // Portions of this file are Copyright 2014 The Rust Project Developers.
 // See https://www.rust-lang.org/policies/licenses.
 
-///! Operating system signals.
+//! Operating system signals.
 
 use crate::{Error, Result};
 use crate::errno::Errno;
 use crate::unistd::Pid;
-use std::convert::TryFrom;
 use std::mem;
 use std::fmt;
 use std::str::FromStr;
@@ -18,58 +17,94 @@
 pub use self::sigevent::*;
 
 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.
     // We would prefer to use the libc::c_int alias in the repr attribute. Unfortunately
     // this is not (yet) possible.
     #[repr(i32)]
+    #[non_exhaustive]
     pub enum Signal {
+        /// Hangup
         SIGHUP,
+        /// Interrupt
         SIGINT,
+        /// Quit
         SIGQUIT,
+        /// Illegal instruction (not reset when caught)
         SIGILL,
+        /// Trace trap (not reset when caught)
         SIGTRAP,
+        /// Abort
         SIGABRT,
+        /// Bus error
         SIGBUS,
+        /// Floating point exception
         SIGFPE,
+        /// Kill (cannot be caught or ignored)
         SIGKILL,
+        /// User defined signal 1
         SIGUSR1,
+        /// Segmentation violation
         SIGSEGV,
+        /// User defined signal 2
         SIGUSR2,
+        /// Write on a pipe with no one to read it
         SIGPIPE,
+        /// Alarm clock
         SIGALRM,
+        /// Software termination signal from kill
         SIGTERM,
+        /// Stack fault (obsolete)
         #[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,
+        /// To parent on child stop or exit
         SIGCHLD,
+        /// Continue a stopped process
         SIGCONT,
+        /// Sendable stop signal not from tty
         SIGSTOP,
+        /// Stop signal from tty
         SIGTSTP,
+        /// To readers pgrp upon background tty read
         SIGTTIN,
+        /// Like TTIN if (tp->t_local&LTOSTOP)
         SIGTTOU,
+        /// Urgent condition on IO channel
         SIGURG,
+        /// Exceeded CPU time limit
         SIGXCPU,
+        /// Exceeded file size limit
         SIGXFSZ,
+        /// Virtual time alarm
         SIGVTALRM,
+        /// Profiling time alarm
         SIGPROF,
+        /// Window size changes
         SIGWINCH,
+        /// Input/output possible signal
         SIGIO,
         #[cfg(any(target_os = "android", target_os = "emscripten",
                   target_os = "fuchsia", target_os = "linux"))]
+        /// 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")))]
+        /// Emulator trap
         SIGEMT,
         #[cfg(not(any(target_os = "android", target_os = "emscripten",
                       target_os = "fuchsia", target_os = "linux",
                       target_os = "redox")))]
+        /// Information request
         SIGINFO,
     }
+    impl TryFrom<i32>
 }
 
 impl FromStr for Signal {
@@ -121,7 +156,7 @@
                           target_os = "fuchsia", target_os = "linux",
                           target_os = "redox")))]
             "SIGINFO" => Signal::SIGINFO,
-            _ => return Err(Error::from(Errno::EINVAL)),
+            _ => return Err(Errno::EINVAL),
         })
     }
 }
@@ -132,7 +167,7 @@
     /// This function is equivalent to `<Signal as AsRef<str>>::as_ref()`,
     /// with difference that returned string is `'static`
     /// and not bound to `self`'s lifetime.
-    pub fn as_str(self) -> &'static str {
+    pub const fn as_str(self) -> &'static str {
         match self {
             Signal::SIGHUP => "SIGHUP",
             Signal::SIGINT => "SIGINT",
@@ -334,9 +369,8 @@
     SIGEMT,
     SIGINFO];
 
-pub const NSIG: libc::c_int = 32;
-
 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+/// Iterate through all signals defined by this operating system
 pub struct SignalIterator {
     next: usize,
 }
@@ -356,25 +390,17 @@
 }
 
 impl Signal {
-    pub fn iterator() -> SignalIterator {
+    /// Iterate through all signals defined by this OS
+    pub const fn iterator() -> SignalIterator {
         SignalIterator{next: 0}
     }
 }
 
-impl TryFrom<libc::c_int> for Signal {
-    type Error = Error;
-
-    fn try_from(signum: libc::c_int) -> Result<Signal> {
-        if 0 < signum && signum < NSIG {
-            Ok(unsafe { mem::transmute(signum) })
-        } else {
-            Err(Error::from(Errno::EINVAL))
-        }
-    }
-}
-
+/// Alias for [`SIGABRT`]
 pub const SIGIOT : Signal = SIGABRT;
+/// Alias for [`SIGIO`]
 pub const SIGPOLL : Signal = SIGIO;
+/// Alias for [`SIGSYS`]
 pub const SIGUNUSED : Signal = SIGSYS;
 
 #[cfg(not(target_os = "redox"))]
@@ -383,26 +409,48 @@
 type SaFlags_t = libc::c_ulong;
 
 libc_bitflags!{
+    /// Controls the behavior of a [`SigAction`]
     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
+        /// stops.
         SA_NOCLDSTOP;
+        /// When catching a [`Signal::SIGCHLD`] signal, the system will not
+        /// create zombie processes when children of the calling process exit.
         SA_NOCLDWAIT;
+        /// Further occurrences of the delivered signal are not masked during
+        /// the execution of the handler.
         SA_NODEFER;
+        /// The system will deliver the signal to the process on a signal stack,
+        /// specified by each thread with sigaltstack(2).
         SA_ONSTACK;
+        /// The handler is reset back to the default at the moment the signal is
+        /// delivered.
         SA_RESETHAND;
+        /// Requests that certain system calls restart if interrupted by this
+        /// signal.  See the man page for complete details.
         SA_RESTART;
+        /// This flag is controlled internally by Nix.
         SA_SIGINFO;
     }
 }
 
 libc_enum! {
+    /// Specifies how certain functions should manipulate a signal mask
     #[repr(i32)]
+    #[non_exhaustive]
     pub enum SigmaskHow {
+        /// The new mask is the union of the current mask and the specified set.
         SIG_BLOCK,
+        /// The new mask is the intersection of the current mask and the
+        /// complement of the specified set.
         SIG_UNBLOCK,
+        /// The current mask is replaced by the specified set.
         SIG_SETMASK,
     }
 }
 
+/// Specifies a set of [`Signal`]s that may be blocked, waited for, etc.
 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
 pub struct SigSet {
     sigset: libc::sigset_t
@@ -410,6 +458,7 @@
 
 
 impl SigSet {
+    /// Initialize to include all signals.
     pub fn all() -> SigSet {
         let mut sigset = mem::MaybeUninit::uninit();
         let _ = unsafe { libc::sigfillset(sigset.as_mut_ptr()) };
@@ -417,6 +466,7 @@
         unsafe{ SigSet { sigset: sigset.assume_init() } }
     }
 
+    /// Initialize to include nothing.
     pub fn empty() -> SigSet {
         let mut sigset = mem::MaybeUninit::uninit();
         let _ = unsafe { libc::sigemptyset(sigset.as_mut_ptr()) };
@@ -424,18 +474,22 @@
         unsafe{ SigSet { sigset: sigset.assume_init() } }
     }
 
+    /// Add the specified signal to the set.
     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.
     pub fn clear(&mut self) {
         unsafe { libc::sigemptyset(&mut self.sigset as *mut libc::sigset_t) };
     }
 
+    /// Remove the specified signal from this set.
     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.
     pub fn contains(&self, signal: Signal) -> bool {
         let res = unsafe { libc::sigismember(&self.sigset as *const libc::sigset_t, signal as libc::c_int) };
 
@@ -446,6 +500,8 @@
         }
     }
 
+    /// 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) {
@@ -487,6 +543,8 @@
     /// signal mask becomes pending, and returns the accepted signal.
     #[cfg(not(target_os = "redox"))] // RedoxFS does not yet support sigwait
     pub fn wait(&self) -> Result<Signal> {
+        use std::convert::TryFrom;
+
         let mut signum = mem::MaybeUninit::uninit();
         let res = unsafe { libc::sigwait(&self.sigset as *const libc::sigset_t, signum.as_mut_ptr()) };
 
@@ -582,9 +640,31 @@
         match self.sigaction.sa_sigaction {
             libc::SIG_DFL => SigHandler::SigDfl,
             libc::SIG_IGN => SigHandler::SigIgn,
-            f if self.flags().contains(SaFlags::SA_SIGINFO) =>
-                SigHandler::SigAction( unsafe { mem::transmute(f) } ),
-            f => SigHandler::Handler( unsafe { mem::transmute(f) } ),
+            p if self.flags().contains(SaFlags::SA_SIGINFO) =>
+                SigHandler::SigAction(
+                // 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(_, _, _))
+                }
+                as extern fn(_, _, _)),
+            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)),
         }
     }
 
@@ -594,7 +674,18 @@
         match self.sigaction.sa_handler {
             libc::SIG_DFL => SigHandler::SigDfl,
             libc::SIG_IGN => SigHandler::SigIgn,
-            f => SigHandler::Handler( unsafe { mem::transmute(f) } ),
+            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)),
         }
     }
 }
@@ -606,9 +697,16 @@
 ///
 /// # Safety
 ///
-/// Signal handlers may be called at any point during execution, which limits what is safe to do in
-/// the body of the signal-catching function. Be certain to only make syscalls that are explicitly
-/// marked safe for signal handlers and only share global data using atomics.
+/// * Signal handlers may be called at any point during execution, which limits
+///   what is safe to do in the body of the signal-catching function. Be certain
+///   to only make syscalls that are explicitly marked safe for signal handlers
+///   and only share global data using atomics.
+///
+/// * There is also no guarantee that the old signal handler was installed
+///   correctly.  If it was installed by this crate, it will be.  But if it was
+///   installed by, for example, C code, then there is no guarantee its function
+///   pointer is valid.  In that case, this function effectively dereferences a
+///   raw pointer of unknown provenance.
 pub unsafe fn sigaction(signal: Signal, sigaction: &SigAction) -> Result<SigAction> {
     let mut oldact = mem::MaybeUninit::<libc::sigaction>::uninit();
 
@@ -681,13 +779,16 @@
         SigHandler::SigIgn => libc::signal(signal, libc::SIG_IGN),
         SigHandler::Handler(handler) => libc::signal(signal, handler as libc::sighandler_t),
         #[cfg(not(target_os = "redox"))]
-        SigHandler::SigAction(_) => return Err(Error::from(Errno::ENOTSUP)),
+        SigHandler::SigAction(_) => return Err(Errno::ENOTSUP),
     };
     Errno::result(res).map(|oldhandler| {
         match oldhandler {
             libc::SIG_DFL => SigHandler::SigDfl,
             libc::SIG_IGN => SigHandler::SigIgn,
-            f => SigHandler::Handler(mem::transmute(f)),
+            p => SigHandler::Handler(
+                *(&p as *const usize
+                     as *const extern fn(libc::c_int))
+                as extern fn(libc::c_int)),
         }
     })
 }
@@ -754,6 +855,24 @@
     Errno::result(res).map(drop)
 }
 
+/// Send a signal to a process
+///
+/// # Arguments
+///
+/// * `pid` -    Specifies which processes should receive the signal.
+///   - 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`].
+///   - 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
+///     process group ID is equal to the absolute value of `pid`.
+/// * `signal` - Signal to send. If `None`, error checking is performed
+///              but no signal is actually sent.
+///
+/// See Also
+/// [`kill(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/kill.html)
 pub fn kill<T: Into<Option<Signal>>>(pid: Pid, signal: T) -> Result<()> {
     let res = unsafe { libc::kill(pid.into(),
                                   match signal.into() {
@@ -764,12 +883,16 @@
     Errno::result(res).map(drop)
 }
 
-/// Send a signal to a process group [(see
-/// killpg(3))](https://pubs.opengroup.org/onlinepubs/9699919799/functions/killpg.html).
+/// Send a signal to a process group
 ///
-/// If `pgrp` less then or equal 1, the behavior is platform-specific.
-/// If `signal` is `None`, `killpg` will only preform error checking and won't
-/// send any signal.
+/// # Arguments
+///
+/// * `pgrp` -   Process group to signal.  If less then or equal 1, the behavior
+///              is platform-specific.
+/// * `signal` - Signal to send. If `None`, `killpg` will only preform error
+///              checking and won't send any signal.
+///
+/// See Also [killpg(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/killpg.html).
 #[cfg(not(target_os = "fuchsia"))]
 pub fn killpg<T: Into<Option<Signal>>>(pgrp: Pid, signal: T) -> Result<()> {
     let res = unsafe { libc::killpg(pgrp.into(),
@@ -781,6 +904,9 @@
     Errno::result(res).map(drop)
 }
 
+/// Send a signal to the current thread
+///
+/// See Also [raise(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/raise.html)
 pub fn raise(signal: Signal) -> Result<()> {
     let res = unsafe { libc::raise(signal as libc::c_int) };
 
@@ -788,36 +914,51 @@
 }
 
 
+/// Identifies a thread for [`SigevNotify::SigevThreadId`]
 #[cfg(target_os = "freebsd")]
 pub type type_of_thread_id = libc::lwpid_t;
+/// Identifies a thread for [`SigevNotify::SigevThreadId`]
 #[cfg(target_os = "linux")]
 pub type type_of_thread_id = libc::pid_t;
 
-/// Used to request asynchronous notification of certain events, for example,
-/// with POSIX AIO, POSIX message queues, and POSIX timers.
+/// Specifies the notification method used by a [`SigEvent`]
 // sigval is actually a union of a int and a void*.  But it's never really used
 // as a pointer, because neither libc nor the kernel ever dereference it.  nix
 // therefore presents it as an intptr_t, which is how kevent uses it.
+#[cfg(not(any(target_os = "openbsd", target_os = "redox")))]
 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
 pub enum SigevNotify {
     /// No notification will be delivered
     SigevNone,
-    /// The signal given by `signal` will be delivered to the process.  The
-    /// value in `si_value` will be present in the `si_value` field of the
-    /// `siginfo_t` structure of the queued signal.
-    SigevSignal { signal: Signal, si_value: libc::intptr_t },
+    /// Notify by delivering a signal to the process.
+    SigevSignal {
+        /// Signal to deliver
+        signal: Signal,
+        /// Will be present in the `si_value` field of the [`libc::siginfo_t`]
+        /// structure of the queued signal.
+        si_value: libc::intptr_t
+    },
     // Note: SIGEV_THREAD is not implemented because libc::sigevent does not
     // expose a way to set the union members needed by SIGEV_THREAD.
-    /// A new `kevent` is posted to the kqueue `kq`.  The `kevent`'s `udata`
-    /// field will contain the value in `udata`.
+    /// Notify by delivering an event to a kqueue.
     #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
-    SigevKevent { kq: RawFd, udata: libc::intptr_t },
-    /// The signal `signal` is queued to the thread whose LWP ID is given in
-    /// `thread_id`.  The value stored in `si_value` will be present in the
-    /// `si_value` of the `siginfo_t` structure of the queued signal.
+    SigevKevent {
+        /// File descriptor of the kqueue to notify.
+        kq: RawFd,
+        /// Will be contained in the kevent's `udata` field.
+        udata: libc::intptr_t
+    },
+    /// Notify by delivering a signal to a thread.
     #[cfg(any(target_os = "freebsd", target_os = "linux"))]
-    SigevThreadId { signal: Signal, thread_id: type_of_thread_id,
-                    si_value: libc::intptr_t },
+    SigevThreadId {
+        /// Signal to send
+        signal: Signal,
+        /// LWP ID of the thread to notify
+        thread_id: type_of_thread_id,
+        /// Will be present in the `si_value` field of the [`libc::siginfo_t`]
+        /// structure of the queued signal.
+        si_value: libc::intptr_t
+    },
 }
 
 #[cfg(not(any(target_os = "openbsd", target_os = "redox")))]
@@ -899,6 +1040,7 @@
         fn set_tid(_sev: &mut libc::sigevent, _sigev_notify: &SigevNotify) {
         }
 
+        /// Return a copy of the inner structure
         pub fn sigevent(&self) -> libc::sigevent {
             self.sigevent
         }
@@ -949,7 +1091,7 @@
 
     #[test]
     fn test_from_str_invalid_value() {
-        let errval = Err(Error::from(Errno::EINVAL));
+        let errval = Err(Errno::EINVAL);
         assert_eq!("NOSIGNAL".parse::<Signal>(), errval);
         assert_eq!("kill".parse::<Signal>(), errval);
         assert_eq!("9".parse::<Signal>(), errval);
diff --git a/src/sys/signalfd.rs b/src/sys/signalfd.rs
index 49811a1..bc4a452 100644
--- a/src/sys/signalfd.rs
+++ b/src/sys/signalfd.rs
@@ -15,9 +15,8 @@
 //!
 //! Please note that signal discarding is not specific to `signalfd`, but also happens with regular
 //! signal handlers.
-use libc;
 use crate::unistd;
-use crate::{Error, Result};
+use crate::Result;
 use crate::errno::Errno;
 pub use crate::sys::signal::{self, SigSet};
 pub use libc::signalfd_siginfo as siginfo;
@@ -34,7 +33,8 @@
 }
 
 pub const SIGNALFD_NEW: RawFd = -1;
-pub const SIGNALFD_SIGINFO_SIZE: usize = 128;
+#[deprecated(since = "0.23.0", note = "use mem::size_of::<siginfo>() instead")]
+pub const SIGNALFD_SIGINFO_SIZE: usize = mem::size_of::<siginfo>();
 
 /// Creates a new file descriptor for reading signals.
 ///
@@ -98,15 +98,14 @@
     }
 
     pub fn read_signal(&mut self) -> Result<Option<siginfo>> {
-        let mut buffer = mem::MaybeUninit::<[u8; SIGNALFD_SIGINFO_SIZE]>::uninit();
+        let mut buffer = mem::MaybeUninit::<siginfo>::uninit();
 
+        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,
-                       SIGNALFD_SIGINFO_SIZE as libc::size_t)
+            libc::read(self.0, buffer.as_mut_ptr() as *mut libc::c_void, size)
         }).map(|r| r as usize);
         match res {
-            Ok(SIGNALFD_SIGINFO_SIZE) => Ok(Some(unsafe { mem::transmute(buffer.assume_init()) })),
+            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)
@@ -117,7 +116,7 @@
 impl Drop for SignalFd {
     fn drop(&mut self) {
         let e = unistd::close(self.0);
-        if !std::thread::panicking() && e == Err(Error::from(Errno::EBADF)) {
+        if !std::thread::panicking() && e == Err(Errno::EBADF) {
             panic!("Closing an invalid file descriptor!");
         };
     }
@@ -144,14 +143,6 @@
 #[cfg(test)]
 mod tests {
     use super::*;
-    use std::mem;
-    use libc;
-
-
-    #[test]
-    fn check_siginfo_size() {
-        assert_eq!(mem::size_of::<libc::signalfd_siginfo>(), SIGNALFD_SIGINFO_SIZE);
-    }
 
     #[test]
     fn create_signalfd() {
diff --git a/src/sys/socket/addr.rs b/src/sys/socket/addr.rs
index d486056..b119642 100644
--- a/src/sys/socket/addr.rs
+++ b/src/sys/socket/addr.rs
@@ -1,5 +1,5 @@
 use super::sa_family_t;
-use crate::{Error, Result, NixPath};
+use crate::{Result, NixPath};
 use crate::errno::Errno;
 use memoffset::offset_of;
 use std::{fmt, mem, net, ptr, slice};
@@ -32,6 +32,7 @@
 /// These constants specify the protocol family to be used
 /// in [`socket`](fn.socket.html) and [`socketpair`](fn.socketpair.html)
 #[repr(i32)]
+#[non_exhaustive]
 #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
 pub enum AddressFamily {
     /// Local communication (see [`unix(7)`](https://man7.org/linux/man-pages/man7/unix.7.html))
@@ -236,7 +237,7 @@
     ///
     /// Currently only supports these address families: Unix, Inet (v4 & v6), Netlink, Link/Packet
     /// and System. Returns None for unsupported or unknown address families.
-    pub fn from_i32(family: i32) -> Option<AddressFamily> {
+    pub const fn from_i32(family: i32) -> Option<AddressFamily> {
         match family {
             libc::AF_UNIX => Some(AddressFamily::Unix),
             libc::AF_INET => Some(AddressFamily::Inet),
@@ -269,6 +270,7 @@
 }
 
 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) => {
@@ -292,6 +294,7 @@
         }
     }
 
+    #[allow(clippy::needless_update)]   // It isn't needless on all OSes
     pub fn new(ip: IpAddr, port: u16) -> InetAddr {
         match ip {
             IpAddr::V4(ref ip) => {
@@ -313,7 +316,7 @@
         }
     }
     /// Gets the IP address associated with this socket address.
-    pub fn ip(&self) -> IpAddr {
+    pub const fn ip(&self) -> IpAddr {
         match *self {
             InetAddr::V4(ref sa) => IpAddr::V4(Ipv4Addr(sa.sin_addr)),
             InetAddr::V6(ref sa) => IpAddr::V6(Ipv6Addr(sa.sin6_addr)),
@@ -321,7 +324,7 @@
     }
 
     /// Gets the port number associated with this socket address
-    pub fn port(&self) -> u16 {
+    pub const fn port(&self) -> u16 {
         match *self {
             InetAddr::V6(ref sa) => u16::from_be(sa.sin6_port),
             InetAddr::V4(ref sa) => u16::from_be(sa.sin_port),
@@ -343,6 +346,7 @@
         }
     }
 
+    #[deprecated(since = "0.23.0", note = "use .to_string() instead")]
     pub fn to_str(&self) -> String {
         format!("{}", self)
     }
@@ -372,7 +376,7 @@
     /// Create a new IpAddr that contains an IPv4 address.
     ///
     /// The result will represent the IP address a.b.c.d
-    pub fn new_v4(a: u8, b: u8, c: u8, d: u8) -> IpAddr {
+    pub const fn new_v4(a: u8, b: u8, c: u8, d: u8) -> IpAddr {
         IpAddr::V4(Ipv4Addr::new(a, b, c, d))
     }
 
@@ -381,7 +385,7 @@
     /// The result will represent the IP address a:b:c:d:e:f
     #[allow(clippy::many_single_char_names)]
     #[allow(clippy::too_many_arguments)]
-    pub fn new_v6(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> IpAddr {
+    pub const fn new_v6(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> IpAddr {
         IpAddr::V6(Ipv6Addr::new(a, b, c, d, e, f, g, h))
     }
 
@@ -392,7 +396,7 @@
         }
     }
 
-    pub fn to_std(&self) -> net::IpAddr {
+    pub const fn to_std(&self) -> net::IpAddr {
         match *self {
             IpAddr::V4(ref ip) => net::IpAddr::V4(ip.to_std()),
             IpAddr::V6(ref ip) => net::IpAddr::V6(ip.to_std()),
@@ -420,11 +424,11 @@
 
 impl Ipv4Addr {
     #[allow(clippy::identity_op)]   // More readable this way
-    pub fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr {
-        let ip = ((u32::from(a) << 24) |
-                  (u32::from(b) << 16) |
-                  (u32::from(c) <<  8) |
-                  (u32::from(d) <<  0)).to_be();
+    pub const fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr {
+        let ip = (((a as u32) << 24) |
+                  ((b as u32) << 16) |
+                  ((c as u32) <<  8) |
+                  ((d as u32) <<  0)).to_be();
 
         Ipv4Addr(libc::in_addr { s_addr: ip })
     }
@@ -436,16 +440,16 @@
         Ipv4Addr::new(bits[0], bits[1], bits[2], bits[3])
     }
 
-    pub fn any() -> Ipv4Addr {
+    pub const fn any() -> Ipv4Addr {
         Ipv4Addr(libc::in_addr { s_addr: libc::INADDR_ANY })
     }
 
-    pub fn octets(self) -> [u8; 4] {
+    pub const fn octets(self) -> [u8; 4] {
         let bits = u32::from_be(self.0.s_addr);
         [(bits >> 24) as u8, (bits >> 16) as u8, (bits >> 8) as u8, bits as u8]
     }
 
-    pub fn to_std(self) -> net::Ipv4Addr {
+    pub const fn to_std(self) -> net::Ipv4Addr {
         let bits = self.octets();
         net::Ipv4Addr::new(bits[0], bits[1], bits[2], bits[3])
     }
@@ -486,7 +490,7 @@
 impl Ipv6Addr {
     #[allow(clippy::many_single_char_names)]
     #[allow(clippy::too_many_arguments)]
-    pub fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> Ipv6Addr {
+    pub const fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> Ipv6Addr {
         Ipv6Addr(libc::in6_addr{s6_addr: to_u8_array!(a,b,c,d,e,f,g,h)})
     }
 
@@ -496,11 +500,11 @@
     }
 
     /// Return the eight 16-bit segments that make up this address
-    pub fn segments(&self) -> [u16; 8] {
+    pub const fn segments(&self) -> [u16; 8] {
         to_u16_array!(self, 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15)
     }
 
-    pub fn to_std(&self) -> net::Ipv6Addr {
+    pub const fn to_std(&self) -> net::Ipv6Addr {
         let s = self.segments();
         net::Ipv6Addr::new(s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7])
     }
@@ -513,15 +517,42 @@
 }
 
 /// A wrapper around `sockaddr_un`.
-///
-/// This also tracks the length of `sun_path` address (excluding
-/// a terminating null), because it may not be null-terminated.  For example,
-/// unconnected and Linux abstract sockets are never null-terminated, and POSIX
-/// does not require that `sun_len` include the terminating null even for normal
-/// sockets.  Note that the actual sockaddr length is greater by
-/// `offset_of!(libc::sockaddr_un, sun_path)`
 #[derive(Clone, Copy, Debug)]
-pub struct UnixAddr(pub libc::sockaddr_un, pub usize);
+pub struct UnixAddr {
+    // INVARIANT: sun & path_len are valid as defined by docs for from_raw_parts
+    sun: libc::sockaddr_un,
+    path_len: usize,
+}
+
+// linux man page unix(7) says there are 3 kinds of unix socket:
+// pathname: addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(sun_path) + 1
+// unnamed: addrlen = sizeof(sa_family_t)
+// abstract: addren > sizeof(sa_family_t), name = sun_path[..(addrlen - sizeof(sa_family_t))]
+//
+// what we call path_len = addrlen - offsetof(struct sockaddr_un, sun_path)
+#[derive(PartialEq, Eq, Hash)]
+enum UnixAddrKind<'a> {
+    Pathname(&'a Path),
+    Unnamed,
+    #[cfg(any(target_os = "android", target_os = "linux"))]
+    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 {
+        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);
+            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)))
+    }
+}
 
 impl UnixAddr {
     /// Create a new sockaddr_un representing a filesystem path.
@@ -535,15 +566,15 @@
 
                 let bytes = cstr.to_bytes();
 
-                if bytes.len() > ret.sun_path.len() {
-                    return Err(Error::from(Errno::ENAMETOOLONG));
+                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(ret, bytes.len()))
+                Ok(UnixAddr::from_raw_parts(ret, bytes.len() + 1))
             }
         })?
     }
@@ -562,8 +593,8 @@
                 .. mem::zeroed()
             };
 
-            if path.len() + 1 > ret.sun_path.len() {
-                return Err(Error::from(Errno::ENAMETOOLONG));
+            if path.len() >= ret.sun_path.len() {
+                return Err(Errno::ENAMETOOLONG);
             }
 
             // Abstract addresses are represented by sun_path[0] ==
@@ -572,28 +603,39 @@
                                      ret.sun_path.as_mut_ptr().offset(1) as *mut u8,
                                      path.len());
 
-            Ok(UnixAddr(ret, path.len() + 1))
+            Ok(UnixAddr::from_raw_parts(ret, path.len() + 1))
         }
     }
 
-    fn sun_path(&self) -> &[u8] {
-        unsafe { slice::from_raw_parts(self.0.sun_path.as_ptr() as *const u8, self.1) }
+    /// 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`.
+    ///
+    /// # 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
+            }
+        }
+        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) }
     }
 
     /// If this address represents a filesystem path, return that path.
     pub fn path(&self) -> Option<&Path> {
-        if self.1 == 0 || self.0.sun_path[0] == 0 {
-            // unnamed or abstract
-            None
-        } else {
-            let p = self.sun_path();
-            // POSIX only requires that `sun_len` be at least long enough to
-            // contain the pathname, and it need not be null-terminated.  So we
-            // need to create a string that is the shorter of the
-            // null-terminated length or the full length.
-            let ptr = &self.0.sun_path as *const libc::c_char;
-            let reallen = unsafe { libc::strnlen(ptr, p.len()) };
-            Some(Path::new(<OsStr as OsStrExt>::from_bytes(&p[..reallen])))
+        match self.kind() {
+            UnixAddrKind::Pathname(path) => Some(path),
+            _ => None,
         }
     }
 
@@ -603,31 +645,55 @@
     /// leading null byte. `None` is returned for unnamed or path-backed sockets.
     #[cfg(any(target_os = "android", target_os = "linux"))]
     pub fn as_abstract(&self) -> Option<&[u8]> {
-        if self.1 >= 1 && self.0.sun_path[0] == 0 {
-            Some(&self.sun_path()[1..])
-        } else {
-            // unnamed or filesystem path
-            None
+        match self.kind() {
+            UnixAddrKind::Abstract(name) => Some(name),
+            _ => None,
         }
     }
+
+    /// Returns the addrlen of this socket - `offsetof(struct sockaddr_un, sun_path)`
+    #[inline]
+    pub fn path_len(&self) -> usize {
+        self.path_len
+    }
+    /// Returns a pointer to the raw `sockaddr_un` struct
+    #[inline]
+    pub fn as_ptr(&self) -> *const libc::sockaddr_un {
+        &self.sun
+    }
+    /// Returns a mutable pointer to the raw `sockaddr_un` struct
+    #[inline]
+    pub fn as_mut_ptr(&mut self) -> *mut libc::sockaddr_un {
+        &mut self.sun
+    }
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+fn fmt_abstract(abs: &[u8], f: &mut fmt::Formatter) -> fmt::Result {
+    use fmt::Write;
+    f.write_str("@\"")?;
+    for &b in abs {
+        use fmt::Display;
+        char::from(b).escape_default().fmt(f)?;
+    }
+    f.write_char('"')?;
+    Ok(())
 }
 
 impl fmt::Display for UnixAddr {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        if self.1 == 0 {
-            f.write_str("<unbound UNIX socket>")
-        } else if let Some(path) = self.path() {
-            path.display().fmt(f)
-        } else {
-            let display = String::from_utf8_lossy(&self.sun_path()[1..]);
-            write!(f, "@{}", display)
+        match self.kind() {
+            UnixAddrKind::Pathname(path) => path.display().fmt(f),
+            UnixAddrKind::Unnamed => f.pad("<unbound UNIX socket>"),
+            #[cfg(any(target_os = "android", target_os = "linux"))]
+            UnixAddrKind::Abstract(name) => fmt_abstract(name, f),
         }
     }
 }
 
 impl PartialEq for UnixAddr {
     fn eq(&self, other: &UnixAddr) -> bool {
-        self.sun_path() == other.sun_path()
+        self.kind() == other.kind()
     }
 }
 
@@ -635,12 +701,13 @@
 
 impl Hash for UnixAddr {
     fn hash<H: Hasher>(&self, s: &mut H) {
-        ( self.0.sun_family, self.sun_path() ).hash(s)
+        self.kind().hash(s)
     }
 }
 
 /// Represents a socket address
 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[non_exhaustive]
 pub enum SockAddr {
     Inet(InetAddr),
     Unix(UnixAddr),
@@ -686,7 +753,7 @@
 
     #[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(|a| SockAddr::SysControl(a))
+        SysControlAddr::from_name(sockfd, name, unit).map(SockAddr::SysControl)
     }
 
     #[cfg(any(target_os = "android", target_os = "linux"))]
@@ -720,6 +787,7 @@
         }
     }
 
+    #[deprecated(since = "0.23.0", note = "use .to_string() instead")]
     pub fn to_str(&self) -> String {
         format!("{}", self)
     }
@@ -801,12 +869,12 @@
                 },
                 mem::size_of_val(addr) as libc::socklen_t
             ),
-            SockAddr::Unix(UnixAddr(ref addr, len)) => (
+            SockAddr::Unix(UnixAddr { ref sun, path_len }) => (
                 // This cast is always allowed in C
                 unsafe {
-                    &*(addr as *const libc::sockaddr_un as *const libc::sockaddr)
+                    &*(sun as *const libc::sockaddr_un as *const libc::sockaddr)
                 },
-                (len + offset_of!(libc::sockaddr_un, sun_path)) as libc::socklen_t
+                (path_len + offset_of!(libc::sockaddr_un, sun_path)) as libc::socklen_t
             ),
             #[cfg(any(target_os = "android", target_os = "linux"))]
             SockAddr::Netlink(NetlinkAddr(ref sa)) => (
@@ -913,11 +981,11 @@
             NetlinkAddr(addr)
         }
 
-        pub fn pid(&self) -> u32 {
+        pub const fn pid(&self) -> u32 {
             self.0.nl_pid
         }
 
-        pub fn groups(&self) -> u32 {
+        pub const fn groups(&self) -> u32 {
             self.0.nl_groups
         }
     }
@@ -998,7 +1066,7 @@
     use libc::{self, c_uchar};
     use std::{fmt, mem};
     use std::os::unix::io::RawFd;
-    use crate::{Errno, Error, Result};
+    use crate::{Errno, Result};
 
     // FIXME: Move type into `libc`
     #[repr(C)]
@@ -1009,7 +1077,7 @@
         pub ctl_name: [c_uchar; MAX_KCTL_NAME],
     }
 
-    const CTL_IOC_MAGIC: u8 = 'N' as u8;
+    const CTL_IOC_MAGIC: u8 = b'N';
     const CTL_IOC_INFO: u8 = 3;
     const MAX_KCTL_NAME: usize = 96;
 
@@ -1020,7 +1088,7 @@
     pub struct SysControlAddr(pub libc::sockaddr_ctl);
 
     impl SysControlAddr {
-        pub fn new(id: u32, unit: u32) -> SysControlAddr {
+        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,
                 sc_family: AddressFamily::System as c_uchar,
@@ -1035,7 +1103,7 @@
 
         pub fn from_name(sockfd: RawFd, name: &str, unit: u32) -> Result<SysControlAddr> {
             if name.len() > MAX_KCTL_NAME {
-                return Err(Error::from(Errno::ENAMETOOLONG));
+                return Err(Errno::ENAMETOOLONG);
             }
 
             let mut ctl_name = [0; MAX_KCTL_NAME];
@@ -1047,11 +1115,11 @@
             Ok(SysControlAddr::new(info.ctl_id, unit))
         }
 
-        pub fn id(&self) -> u32 {
+        pub const fn id(&self) -> u32 {
             self.0.sc_id
         }
 
-        pub fn unit(&self) -> u32 {
+        pub const fn unit(&self) -> u32 {
             self.0.sc_unit
         }
     }
@@ -1372,11 +1440,8 @@
         let name = String::from("nix\0abstract\0test");
         let addr = UnixAddr::new_abstract(name.as_bytes()).unwrap();
 
-        let sun_path1 = addr.sun_path();
-        let sun_path2 = [0u8, 110, 105, 120, 0, 97, 98, 115, 116, 114, 97, 99, 116, 0, 116, 101, 115, 116];
-        assert_eq!(sun_path1.len(), sun_path2.len());
-        for i in 0..sun_path1.len() {
-            assert_eq!(sun_path1[i], sun_path2[i]);
-        }
+        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);
     }
 }
diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs
index da5573c..97eea3d 100644
--- a/src/sys/socket/mod.rs
+++ b/src/sys/socket/mod.rs
@@ -2,7 +2,7 @@
 //!
 //! [Further reading](https://man7.org/linux/man-pages/man7/socket.7.html)
 use cfg_if::cfg_if;
-use crate::{Error, Result, errno::Errno};
+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;
@@ -14,6 +14,7 @@
 use crate::sys::uio::IoVec;
 
 mod addr;
+#[deny(missing_docs)]
 pub mod sockopt;
 
 /*
@@ -70,6 +71,7 @@
 /// when creating a socket with [`socket()`](fn.socket.html)
 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
 #[repr(i32)]
+#[non_exhaustive]
 pub enum SockType {
     /// Provides sequenced, reliable, two-way, connection-
     /// based byte streams.  An out-of-band data transmission
@@ -94,6 +96,7 @@
 /// to specify the protocol to use.
 #[repr(i32)]
 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[non_exhaustive]
 pub enum SockProtocol {
     /// TCP protocol ([ip(7)](https://man7.org/linux/man-pages/man7/ip.7.html))
     Tcp = libc::IPPROTO_TCP,
@@ -308,9 +311,9 @@
             }
         }
 
-        impl Into<libc::ucred> for UnixCredentials {
-            fn into(self) -> libc::ucred {
-                self.0
+        impl From<UnixCredentials> for libc::ucred {
+            fn from(uc: UnixCredentials) -> Self {
+                uc.0
             }
         }
     } else if #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] {
@@ -356,6 +359,38 @@
     }
 }
 
+cfg_if!{
+    if #[cfg(any(
+                target_os = "dragonfly",
+                target_os = "freebsd",
+                target_os = "macos",
+                target_os = "ios"
+        ))] {
+        /// Return type of [`LocalPeerCred`](crate::sys::socket::sockopt::LocalPeerCred)
+        #[repr(transparent)]
+        #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+        pub struct XuCred(libc::xucred);
+
+        impl XuCred {
+            /// Structure layout version
+            pub fn version(&self) -> u32 {
+                self.0.cr_version
+            }
+
+            /// Effective user ID
+            pub fn uid(&self) -> libc::uid_t {
+                self.0.cr_uid
+            }
+
+            /// Returns a list of group identifiers (the first one being the
+            /// effective GID)
+            pub fn groups(&self) -> &[libc::gid_t] {
+                &self.0.cr_groups
+            }
+        }
+    }
+}
+
 /// Request for multicast socket operations
 ///
 /// This is a wrapper type around `ip_mreq`.
@@ -384,7 +419,7 @@
 
 impl Ipv6MembershipRequest {
     /// Instantiate a new `Ipv6MembershipRequest`
-    pub fn new(group: Ipv6Addr) -> Self {
+    pub const fn new(group: Ipv6Addr) -> Self {
         Ipv6MembershipRequest(libc::ipv6_mreq {
             ipv6mr_multiaddr: group.0,
             ipv6mr_interface: 0,
@@ -490,16 +525,14 @@
 //
 //  See https://github.com/nix-rust/nix/issues/999
 #[derive(Clone, Debug, Eq, PartialEq)]
+#[non_exhaustive]
 pub enum ControlMessageOwned {
-    /// Received version of
-    /// [`ControlMessage::ScmRights`][#enum.ControlMessage.html#variant.ScmRights]
+    /// Received version of [`ControlMessage::ScmRights`]
     ScmRights(Vec<RawFd>),
-    /// Received version of
-    /// [`ControlMessage::ScmCredentials`][#enum.ControlMessage.html#variant.ScmCredentials]
+    /// Received version of [`ControlMessage::ScmCredentials`]
     #[cfg(any(target_os = "android", target_os = "linux"))]
     ScmCredentials(UnixCredentials),
-    /// Received version of
-    /// [`ControlMessage::ScmCreds`][#enum.ControlMessage.html#variant.ScmCreds]
+    /// Received version of [`ControlMessage::ScmCreds`]
     #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
     ScmCreds(UnixCredentials),
     /// A message of type `SCM_TIMESTAMP`, containing the time the
@@ -621,6 +654,13 @@
     #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
     RxqOvfl(u32),
 
+    /// Socket error queue control messages read with the `MSG_ERRQUEUE` flag.
+    #[cfg(any(target_os = "android", target_os = "linux"))]
+    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"))]
+    Ipv6RecvErr(libc::sock_extended_err, Option<sockaddr_in6>),
+
     /// Catch-all variant for unimplemented cmsg types.
     #[doc(hidden)]
     Unknown(UnknownCmsg),
@@ -724,6 +764,16 @@
                 let drop_counter = ptr::read_unaligned(p as *const u32);
                 ControlMessageOwned::RxqOvfl(drop_counter)
             },
+            #[cfg(any(target_os = "android", target_os = "linux"))]
+            (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"))]
+            (libc::IPPROTO_IPV6, libc::IPV6_RECVERR) => {
+                let (err, addr) = Self::recv_err_helper::<sockaddr_in6>(p, len);
+                ControlMessageOwned::Ipv6RecvErr(err, addr)
+            },
             (_, _) => {
                 let sl = slice::from_raw_parts(p, len);
                 let ucmsg = UnknownCmsg(*header, Vec::<u8>::from(sl));
@@ -731,6 +781,24 @@
             }
         }
     }
+
+    #[cfg(any(target_os = "android", target_os = "linux"))]
+    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);
+
+        // For errors originating on the network, SO_EE_OFFENDER(ee) points inside the p[..len]
+        // CMSG_DATA buffer.  For local errors, there is no address included in the control
+        // message, and SO_EE_OFFENDER(ee) points beyond the end of the buffer.  So, we need to
+        // validate that the address object is in-bounds before we attempt to copy it.
+        let addrp = libc::SO_EE_OFFENDER(ee) as *const T;
+
+        if addrp.offset(1) as usize - (p as usize) > len {
+            (err, None)
+        } else {
+            (err, Some(ptr::read_unaligned(addrp)))
+        }
+    }
 }
 
 /// A type-safe zero-copy wrapper around a single control message, as used wih
@@ -739,6 +807,7 @@
 ///
 /// [Further reading](https://man7.org/linux/man-pages/man3/cmsg.3.html)
 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+#[non_exhaustive]
 pub enum ControlMessage<'a> {
     /// A message of type `SCM_RIGHTS`, containing an array of file
     /// descriptors passed between processes.
@@ -772,7 +841,7 @@
     ///
     /// Credentials are always overwritten by the kernel, so this variant does have
     /// any data, unlike the receive-side
-    /// [`ControlMessageOwned::ScmCreds`][#enum.ControlMessageOwned.html#variant.ScmCreds].
+    /// [`ControlMessageOwned::ScmCreds`].
     ///
     /// For further information, please refer to the
     /// [`unix(4)`](https://www.freebsd.org/cgi/man.cgi?query=unix) man page.
@@ -1231,6 +1300,7 @@
     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>,
@@ -1646,21 +1716,19 @@
  *
  */
 
-/// Represents a socket option that can be accessed or set. Used as an argument
-/// to `getsockopt`
+/// Represents a socket option that can be retrieved.
 pub trait GetSockOpt : Copy {
     type Val;
 
-    #[doc(hidden)]
+    /// Look up the value of this socket option on the given socket.
     fn get(&self, fd: RawFd) -> Result<Self::Val>;
 }
 
-/// Represents a socket option that can be accessed or set. Used as an argument
-/// to `setsockopt`
+/// Represents a socket option that can be set.
 pub trait SetSockOpt : Clone {
     type Val;
 
-    #[doc(hidden)]
+    /// Set the value of this socket option on the given socket.
     fn set(&self, fd: RawFd, val: &Self::Val) -> Result<()>;
 }
 
@@ -1744,21 +1812,21 @@
     addr: &sockaddr_storage,
     len: usize) -> Result<SockAddr> {
 
-    assert!(len <= mem::size_of::<sockaddr_un>());
+    assert!(len <= mem::size_of::<sockaddr_storage>());
     if len < mem::size_of_val(&addr.ss_family) {
-        return Err(Error::from(Errno::ENOTCONN));
+        return Err(Errno::ENOTCONN);
     }
 
     match c_int::from(addr.ss_family) {
         libc::AF_INET => {
-            assert_eq!(len as usize, mem::size_of::<sockaddr_in>());
+            assert!(len as usize >= mem::size_of::<sockaddr_in>());
             let sin = unsafe {
                 *(addr as *const sockaddr_storage as *const sockaddr_in)
             };
             Ok(SockAddr::Inet(InetAddr::V4(sin)))
         }
         libc::AF_INET6 => {
-            assert_eq!(len as usize, mem::size_of::<sockaddr_in6>());
+            assert!(len as usize >= mem::size_of::<sockaddr_in6>());
             let sin6 = unsafe {
                 *(addr as *const _ as *const sockaddr_in6)
             };
@@ -1766,18 +1834,18 @@
         }
         libc::AF_UNIX => {
             let pathlen = len - offset_of!(sockaddr_un, sun_path);
-            let sun = unsafe {
-                *(addr as *const _ as *const sockaddr_un)
-            };
-            Ok(SockAddr::Unix(UnixAddr(sun, pathlen)))
+            unsafe {
+                let sun = *(addr as *const _ as *const sockaddr_un);
+                Ok(SockAddr::Unix(UnixAddr::from_raw_parts(sun, pathlen)))
+            }
         }
         #[cfg(any(target_os = "android", target_os = "linux"))]
         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
-            assert!(len as usize <= mem::size_of::<sockaddr_ll>());
             let sll = unsafe {
                 *(addr as *const _ as *const sockaddr_ll)
             };
diff --git a/src/sys/socket/sockopt.rs b/src/sys/socket/sockopt.rs
index e2f2caf..fcb4be8 100644
--- a/src/sys/socket/sockopt.rs
+++ b/src/sys/socket/sockopt.rs
@@ -1,3 +1,4 @@
+//! Socket options as used by `setsockopt` and `getsockopt`.
 use cfg_if::cfg_if;
 use super::{GetSockOpt, SetSockOpt};
 use crate::Result;
@@ -30,7 +31,7 @@
 /// # Arguments
 ///
 /// * `$name:ident`: name of the type you want to implement `SetSockOpt` for.
-/// * `$level:path` : socket layer, or a `protocol level`: could be *raw sockets*
+/// * `$level:expr` : socket layer, or a `protocol level`: could be *raw sockets*
 ///    (`libc::SOL_SOCKET`), *ip protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`),
 ///    and more. Please refer to your system manual for more options. Will be passed as the second
 ///    argument (`level`) to the `setsockopt` call.
@@ -41,7 +42,7 @@
 /// * Type that implements the `Set` trait for the type from the previous item (like `SetBool` for
 ///    `bool`, `SetUsize` for `usize`, etc.).
 macro_rules! setsockopt_impl {
-    ($name:ident, $level:path, $flag:path, $ty:ty, $setter:ty) => {
+    ($name:ident, $level:expr, $flag:path, $ty:ty, $setter:ty) => {
         impl SetSockOpt for $name {
             type Val = $ty;
 
@@ -82,7 +83,7 @@
 /// * Type that implements the `Get` trait for the type from the previous item (`GetBool` for
 ///    `bool`, `GetUsize` for `usize`, etc.).
 macro_rules! getsockopt_impl {
-    ($name:ident, $level:path, $flag:path, $ty:ty, $getter:ty) => {
+    ($name:ident, $level:expr, $flag:path, $ty:ty, $getter:ty) => {
         impl GetSockOpt for $name {
             type Val = $ty;
 
@@ -117,7 +118,7 @@
 /// * `GetOnly`, `SetOnly` or `Both`: whether you want to implement only getter, only setter or
 ///    both of them.
 /// * `$name:ident`: name of type `GetSockOpt`/`SetSockOpt` will be implemented for.
-/// * `$level:path` : socket layer, or a `protocol level`: could be *raw sockets*
+/// * `$level:expr` : socket layer, or a `protocol level`: could be *raw sockets*
 ///    (`lic::SOL_SOCKET`), *ip protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`),
 ///    and more. Please refer to your system manual for more options. Will be passed as the second
 ///    argument (`level`) to the `getsockopt`/`setsockopt` call.
@@ -128,73 +129,99 @@
 /// * `$getter:ty`: `Get` implementation; optional; only for `GetOnly` and `Both`.
 /// * `$setter:ty`: `Set` implementation; optional; only for `SetOnly` and `Both`.
 macro_rules! sockopt_impl {
-    (GetOnly, $name:ident, $level:path, $flag:path, bool) => {
-        sockopt_impl!(GetOnly, $name, $level, $flag, bool, GetBool);
+    ($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, bool) => {
+        sockopt_impl!($(#[$attr])*
+                      $name, GetOnly, $level, $flag, bool, GetBool);
     };
 
-    (GetOnly, $name:ident, $level:path, $flag:path, u8) => {
-        sockopt_impl!(GetOnly, $name, $level, $flag, u8, GetU8);
+    ($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, u8) => {
+        sockopt_impl!($(#[$attr])* $name, GetOnly, $level, $flag, u8, GetU8);
     };
 
-    (GetOnly, $name:ident, $level:path, $flag:path, usize) => {
-        sockopt_impl!(GetOnly, $name, $level, $flag, usize, GetUsize);
+    ($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, usize) =>
+    {
+        sockopt_impl!($(#[$attr])*
+                      $name, GetOnly, $level, $flag, usize, GetUsize);
     };
 
-    (SetOnly, $name:ident, $level:path, $flag:path, bool) => {
-        sockopt_impl!(SetOnly, $name, $level, $flag, bool, SetBool);
+    ($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, bool) => {
+        sockopt_impl!($(#[$attr])*
+                      $name, SetOnly, $level, $flag, bool, SetBool);
     };
 
-    (SetOnly, $name:ident, $level:path, $flag:path, u8) => {
-        sockopt_impl!(SetOnly, $name, $level, $flag, u8, SetU8);
+    ($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, u8) => {
+        sockopt_impl!($(#[$attr])* $name, SetOnly, $level, $flag, u8, SetU8);
     };
 
-    (SetOnly, $name:ident, $level:path, $flag:path, usize) => {
-        sockopt_impl!(SetOnly, $name, $level, $flag, usize, SetUsize);
+    ($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, usize) =>
+    {
+        sockopt_impl!($(#[$attr])*
+                      $name, SetOnly, $level, $flag, usize, SetUsize);
     };
 
-    (Both, $name:ident, $level:path, $flag:path, bool) => {
-        sockopt_impl!(Both, $name, $level, $flag, bool, GetBool, SetBool);
+    ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, bool) => {
+        sockopt_impl!($(#[$attr])*
+                      $name, Both, $level, $flag, bool, GetBool, SetBool);
     };
 
-    (Both, $name:ident, $level:path, $flag:path, u8) => {
-        sockopt_impl!(Both, $name, $level, $flag, u8, GetU8, SetU8);
+    ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, u8) => {
+        sockopt_impl!($(#[$attr])*
+                      $name, Both, $level, $flag, u8, GetU8, SetU8);
     };
 
-    (Both, $name:ident, $level:path, $flag:path, usize) => {
-        sockopt_impl!(Both, $name, $level, $flag, usize, GetUsize, SetUsize);
+    ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, usize) => {
+        sockopt_impl!($(#[$attr])*
+                      $name, Both, $level, $flag, usize, GetUsize, SetUsize);
     };
 
-    (Both, $name:ident, $level:path, $flag:path, OsString<$array:ty>) => {
-        sockopt_impl!(Both, $name, $level, $flag, OsString, GetOsString<$array>, SetOsString);
+    ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path,
+     OsString<$array:ty>) =>
+    {
+        sockopt_impl!($(#[$attr])*
+                      $name, Both, $level, $flag, OsString, GetOsString<$array>,
+                      SetOsString);
     };
 
     /*
      * Matchers with generic getter types must be placed at the end, so
      * they'll only match _after_ specialized matchers fail
      */
-    (GetOnly, $name:ident, $level:path, $flag:path, $ty:ty) => {
-        sockopt_impl!(GetOnly, $name, $level, $flag, $ty, GetStruct<$ty>);
+    ($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, $ty:ty) =>
+    {
+        sockopt_impl!($(#[$attr])*
+                      $name, GetOnly, $level, $flag, $ty, GetStruct<$ty>);
     };
 
-    (GetOnly, $name:ident, $level:path, $flag:path, $ty:ty, $getter:ty) => {
+    ($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, $ty:ty,
+     $getter:ty) =>
+    {
+        $(#[$attr])*
         #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
         pub struct $name;
 
         getsockopt_impl!($name, $level, $flag, $ty, $getter);
     };
 
-    (SetOnly, $name:ident, $level:path, $flag:path, $ty:ty) => {
-        sockopt_impl!(SetOnly, $name, $level, $flag, $ty, SetStruct<$ty>);
+    ($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, $ty:ty) =>
+    {
+        sockopt_impl!($(#[$attr])*
+                      $name, SetOnly, $level, $flag, $ty, SetStruct<$ty>);
     };
 
-    (SetOnly, $name:ident, $level:path, $flag:path, $ty:ty, $setter:ty) => {
+    ($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, $ty:ty,
+     $setter:ty) =>
+    {
+        $(#[$attr])*
         #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
         pub struct $name;
 
         setsockopt_impl!($name, $level, $flag, $ty, $setter);
     };
 
-    (Both, $name:ident, $level:path, $flag:path, $ty:ty, $getter:ty, $setter:ty) => {
+    ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, $ty:ty,
+     $getter:ty, $setter:ty) =>
+    {
+        $(#[$attr])*
         #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
         pub struct $name;
 
@@ -202,8 +229,10 @@
         getsockopt_impl!($name, $level, $flag, $ty, $getter);
     };
 
-    (Both, $name:ident, $level:path, $flag:path, $ty:ty) => {
-        sockopt_impl!(Both, $name, $level, $flag, $ty, GetStruct<$ty>, SetStruct<$ty>);
+    ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, $ty:ty) => {
+        sockopt_impl!($(#[$attr])*
+                      $name, Both, $level, $flag, $ty, GetStruct<$ty>,
+                      SetStruct<$ty>);
     };
 }
 
@@ -213,17 +242,45 @@
  *
  */
 
-sockopt_impl!(Both, ReuseAddr, libc::SOL_SOCKET, libc::SO_REUSEADDR, bool);
+sockopt_impl!(
+    /// Enables local address reuse
+    ReuseAddr, Both, libc::SOL_SOCKET, libc::SO_REUSEADDR, bool
+);
 #[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
-sockopt_impl!(Both, ReusePort, libc::SOL_SOCKET, libc::SO_REUSEPORT, bool);
-sockopt_impl!(Both, TcpNoDelay, libc::IPPROTO_TCP, libc::TCP_NODELAY, bool);
-sockopt_impl!(Both, Linger, libc::SOL_SOCKET, libc::SO_LINGER, libc::linger);
-sockopt_impl!(SetOnly, IpAddMembership, libc::IPPROTO_IP, libc::IP_ADD_MEMBERSHIP, super::IpMembershipRequest);
-sockopt_impl!(SetOnly, IpDropMembership, libc::IPPROTO_IP, libc::IP_DROP_MEMBERSHIP, super::IpMembershipRequest);
+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);
+sockopt_impl!(
+    /// 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
+    /// received.  For a small number of clients, such as window systems that
+    /// 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);
+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);
+sockopt_impl!(
+    /// Join a multicast group
+    IpAddMembership, SetOnly, libc::IPPROTO_IP, libc::IP_ADD_MEMBERSHIP,
+    super::IpMembershipRequest);
+sockopt_impl!(
+    /// Leave a multicast group.
+    IpDropMembership, SetOnly, libc::IPPROTO_IP, libc::IP_DROP_MEMBERSHIP,
+    super::IpMembershipRequest);
 cfg_if! {
     if #[cfg(any(target_os = "android", target_os = "linux"))] {
-        sockopt_impl!(SetOnly, Ipv6AddMembership, libc::IPPROTO_IPV6, libc::IPV6_ADD_MEMBERSHIP, super::Ipv6MembershipRequest);
-        sockopt_impl!(SetOnly, Ipv6DropMembership, libc::IPPROTO_IPV6, libc::IPV6_DROP_MEMBERSHIP, super::Ipv6MembershipRequest);
+        sockopt_impl!(
+            /// Join an IPv6 multicast group.
+            Ipv6AddMembership, SetOnly, libc::IPPROTO_IPV6, libc::IPV6_ADD_MEMBERSHIP, super::Ipv6MembershipRequest);
+        sockopt_impl!(
+            /// Leave an IPv6 multicast group.
+            Ipv6DropMembership, SetOnly, libc::IPPROTO_IPV6, libc::IPV6_DROP_MEMBERSHIP, super::Ipv6MembershipRequest);
     } else if #[cfg(any(target_os = "dragonfly",
                         target_os = "freebsd",
                         target_os = "illumos",
@@ -232,64 +289,185 @@
                         target_os = "netbsd",
                         target_os = "openbsd",
                         target_os = "solaris"))] {
-        sockopt_impl!(SetOnly, Ipv6AddMembership, libc::IPPROTO_IPV6, libc::IPV6_JOIN_GROUP, super::Ipv6MembershipRequest);
-        sockopt_impl!(SetOnly, Ipv6DropMembership, libc::IPPROTO_IPV6, libc::IPV6_LEAVE_GROUP, super::Ipv6MembershipRequest);
+        sockopt_impl!(
+            /// Join an IPv6 multicast group.
+            Ipv6AddMembership, SetOnly, libc::IPPROTO_IPV6,
+            libc::IPV6_JOIN_GROUP, super::Ipv6MembershipRequest);
+        sockopt_impl!(
+            /// Leave an IPv6 multicast group.
+            Ipv6DropMembership, SetOnly, libc::IPPROTO_IPV6,
+            libc::IPV6_LEAVE_GROUP, super::Ipv6MembershipRequest);
     }
 }
-sockopt_impl!(Both, IpMulticastTtl, libc::IPPROTO_IP, libc::IP_MULTICAST_TTL, u8);
-sockopt_impl!(Both, IpMulticastLoop, libc::IPPROTO_IP, libc::IP_MULTICAST_LOOP, bool);
+sockopt_impl!(
+    /// 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);
+sockopt_impl!(
+    /// 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"))]
-sockopt_impl!(Both, IpFreebind, libc::IPPROTO_IP, libc::IP_FREEBIND, bool);
-sockopt_impl!(Both, ReceiveTimeout, libc::SOL_SOCKET, libc::SO_RCVTIMEO, TimeVal);
-sockopt_impl!(Both, SendTimeout, libc::SOL_SOCKET, libc::SO_SNDTIMEO, TimeVal);
-sockopt_impl!(Both, Broadcast, libc::SOL_SOCKET, libc::SO_BROADCAST, bool);
-sockopt_impl!(Both, OobInline, libc::SOL_SOCKET, libc::SO_OOBINLINE, bool);
-sockopt_impl!(GetOnly, SocketError, libc::SOL_SOCKET, libc::SO_ERROR, i32);
-sockopt_impl!(Both, KeepAlive, libc::SOL_SOCKET, libc::SO_KEEPALIVE, bool);
+sockopt_impl!(
+    /// 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);
+sockopt_impl!(
+    /// Specify the receiving timeout until reporting an error.
+    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);
+sockopt_impl!(
+    /// Set or get the broadcast flag.
+    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);
+sockopt_impl!(
+    /// Get and clear the pending socket error.
+    SocketError, GetOnly, libc::SOL_SOCKET, libc::SO_ERROR, i32);
+sockopt_impl!(
+    /// Enable sending of keep-alive messages on connection-oriented sockets.
+    KeepAlive, Both, libc::SOL_SOCKET, libc::SO_KEEPALIVE, bool);
+#[cfg(any(
+        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);
 #[cfg(any(target_os = "android", target_os = "linux"))]
-sockopt_impl!(GetOnly, PeerCredentials, libc::SOL_SOCKET, libc::SO_PEERCRED, super::UnixCredentials);
+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"))]
-sockopt_impl!(Both, TcpKeepAlive, libc::IPPROTO_TCP, libc::TCP_KEEPALIVE, u32);
+sockopt_impl!(
+    /// 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"))]
-sockopt_impl!(Both, TcpKeepIdle, libc::IPPROTO_TCP, libc::TCP_KEEPIDLE, u32);
+sockopt_impl!(
+    /// 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);
+cfg_if! {
+    if #[cfg(any(target_os = "android", target_os = "linux"))] {
+        sockopt_impl!(
+            /// The maximum segment size for outgoing TCP packets.
+            TcpMaxSeg, Both, libc::IPPROTO_TCP, libc::TCP_MAXSEG, u32);
+    } else {
+        sockopt_impl!(
+            /// The maximum segment size for outgoing TCP packets.
+            TcpMaxSeg, GetOnly, libc::IPPROTO_TCP, libc::TCP_MAXSEG, u32);
+    }
+}
 #[cfg(not(target_os = "openbsd"))]
-sockopt_impl!(Both, TcpKeepCount, libc::IPPROTO_TCP, libc::TCP_KEEPCNT, u32);
+sockopt_impl!(
+    /// 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"))]
+sockopt_impl!(
+    #[allow(missing_docs)]
+    // Not documented by Linux!
+    TcpRepair, Both, libc::IPPROTO_TCP, libc::TCP_REPAIR, u32);
 #[cfg(not(target_os = "openbsd"))]
-sockopt_impl!(Both, TcpKeepInterval, libc::IPPROTO_TCP, libc::TCP_KEEPINTVL, u32);
+sockopt_impl!(
+    /// The time (in seconds) between individual keepalive probes.
+    TcpKeepInterval, Both, libc::IPPROTO_TCP, libc::TCP_KEEPINTVL, u32);
 #[cfg(any(target_os = "fuchsia", target_os = "linux"))]
-sockopt_impl!(Both, TcpUserTimeout, libc::IPPROTO_TCP, libc::TCP_USER_TIMEOUT, u32);
-sockopt_impl!(Both, RcvBuf, libc::SOL_SOCKET, libc::SO_RCVBUF, usize);
-sockopt_impl!(Both, SndBuf, libc::SOL_SOCKET, libc::SO_SNDBUF, usize);
+sockopt_impl!(
+    /// 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);
+sockopt_impl!(
+    /// Sets or gets the maximum socket receive buffer in bytes.
+    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);
 #[cfg(any(target_os = "android", target_os = "linux"))]
-sockopt_impl!(SetOnly, RcvBufForce, libc::SOL_SOCKET, libc::SO_RCVBUFFORCE, usize);
+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);
 #[cfg(any(target_os = "android", target_os = "linux"))]
-sockopt_impl!(SetOnly, SndBufForce, libc::SOL_SOCKET, libc::SO_SNDBUFFORCE, usize);
-sockopt_impl!(GetOnly, SockType, libc::SOL_SOCKET, libc::SO_TYPE, super::SockType);
-sockopt_impl!(GetOnly, AcceptConn, libc::SOL_SOCKET, libc::SO_ACCEPTCONN, bool);
+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);
+sockopt_impl!(
+    /// Gets the socket type as an integer.
+    SockType, GetOnly, libc::SOL_SOCKET, libc::SO_TYPE, super::SockType);
+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);
 #[cfg(any(target_os = "android", target_os = "linux"))]
-sockopt_impl!(Both, BindToDevice, libc::SOL_SOCKET, libc::SO_BINDTODEVICE, OsString<[u8; libc::IFNAMSIZ]>);
+sockopt_impl!(
+    /// Bind this socket to a particular device like “eth0”.
+    BindToDevice, Both, libc::SOL_SOCKET, libc::SO_BINDTODEVICE, OsString<[u8; libc::IFNAMSIZ]>);
 #[cfg(any(target_os = "android", target_os = "linux"))]
-sockopt_impl!(GetOnly, OriginalDst, libc::SOL_IP, libc::SO_ORIGINAL_DST, libc::sockaddr_in);
-sockopt_impl!(Both, ReceiveTimestamp, libc::SOL_SOCKET, libc::SO_TIMESTAMP, bool);
+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"))]
+sockopt_impl!(
+    #[allow(missing_docs)]
+    // Not documented by Linux!
+    Ip6tOriginalDst, GetOnly, libc::SOL_IPV6, libc::IP6T_SO_ORIGINAL_DST, libc::sockaddr_in6);
+sockopt_impl!( 
+    /// Enable or disable the receiving of the `SO_TIMESTAMP` control message.
+    ReceiveTimestamp, Both, libc::SOL_SOCKET, libc::SO_TIMESTAMP, bool);
 #[cfg(all(target_os = "linux"))]
-sockopt_impl!(Both, ReceiveTimestampns, libc::SOL_SOCKET, libc::SO_TIMESTAMPNS, bool);
+sockopt_impl!(
+    /// Enable or disable the receiving of the `SO_TIMESTAMPNS` control message.
+    ReceiveTimestampns, Both, libc::SOL_SOCKET, libc::SO_TIMESTAMPNS, bool);
 #[cfg(any(target_os = "android", target_os = "linux"))]
-sockopt_impl!(Both, IpTransparent, libc::SOL_IP, libc::IP_TRANSPARENT, bool);
+sockopt_impl!(
+    /// Setting this boolean option enables transparent proxying on this socket.
+    IpTransparent, Both, libc::SOL_IP, libc::IP_TRANSPARENT, bool);
 #[cfg(target_os = "openbsd")]
-sockopt_impl!(Both, BindAny, libc::SOL_SOCKET, libc::SO_BINDANY, bool);
+sockopt_impl!(
+    /// 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);
 #[cfg(target_os = "freebsd")]
-sockopt_impl!(Both, BindAny, libc::IPPROTO_IP, libc::IP_BINDANY, bool);
+sockopt_impl!(
+    /// 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);
 #[cfg(target_os = "linux")]
-sockopt_impl!(Both, Mark, libc::SOL_SOCKET, libc::SO_MARK, u32);
+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);
 #[cfg(any(target_os = "android", target_os = "linux"))]
-sockopt_impl!(Both, PassCred, libc::SOL_SOCKET, libc::SO_PASSCRED, bool);
+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"))] 
-sockopt_impl!(Both, TcpCongestion, libc::IPPROTO_TCP, libc::TCP_CONGESTION, OsString<[u8; TCP_CA_NAME_MAX]>);
+sockopt_impl!(
+    /// 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]>);
 #[cfg(any(
     target_os = "android",
     target_os = "ios",
@@ -297,7 +475,10 @@
     target_os = "macos",
     target_os = "netbsd",
 ))]
-sockopt_impl!(Both, Ipv4PacketInfo, libc::IPPROTO_IP, libc::IP_PKTINFO, bool);
+sockopt_impl!(
+    /// 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);
 #[cfg(any(
     target_os = "android",
     target_os = "freebsd",
@@ -307,7 +488,10 @@
     target_os = "netbsd",
     target_os = "openbsd",
 ))]
-sockopt_impl!(Both, Ipv6RecvPacketInfo, libc::IPPROTO_IPV6, libc::IPV6_RECVPKTINFO, bool);
+sockopt_impl!(
+    /// Set delivery of the `IPV6_PKTINFO` control message on incoming
+    /// datagrams.
+    Ipv6RecvPacketInfo, Both, libc::IPPROTO_IPV6, libc::IPV6_RECVPKTINFO, bool);
 #[cfg(any(
     target_os = "freebsd",
     target_os = "ios",
@@ -315,7 +499,10 @@
     target_os = "netbsd",
     target_os = "openbsd",
 ))]
-sockopt_impl!(Both, Ipv4RecvIf, libc::IPPROTO_IP, libc::IP_RECVIF, bool);
+sockopt_impl!(
+    /// 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);
 #[cfg(any(
     target_os = "freebsd",
     target_os = "ios",
@@ -323,14 +510,49 @@
     target_os = "netbsd",
     target_os = "openbsd",
 ))]
-sockopt_impl!(Both, Ipv4RecvDstAddr, libc::IPPROTO_IP, libc::IP_RECVDSTADDR, bool);
+sockopt_impl!(
+    /// 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")]
-sockopt_impl!(Both, UdpGsoSegment, libc::SOL_UDP, libc::UDP_SEGMENT, libc::c_int);
+sockopt_impl!(
+    #[allow(missing_docs)]
+    // Not documented by Linux!
+    UdpGsoSegment, Both, libc::SOL_UDP, libc::UDP_SEGMENT, libc::c_int);
 #[cfg(target_os = "linux")]
-sockopt_impl!(Both, UdpGroSegment, libc::IPPROTO_UDP, libc::UDP_GRO, bool);
+sockopt_impl!(
+    #[allow(missing_docs)]
+    // Not documented by Linux!
+    UdpGroSegment, Both, libc::IPPROTO_UDP, libc::UDP_GRO, bool);
 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
-sockopt_impl!(Both, RxqOvfl, libc::SOL_SOCKET, libc::SO_RXQ_OVFL, libc::c_int);
+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);
+sockopt_impl!(
+    /// The socket is restricted to sending and receiving IPv6 packets only.
+    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);
+#[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);
+#[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);
+#[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);
 
+#[allow(missing_docs)]
+// Not documented by Linux!
 #[cfg(any(target_os = "android", target_os = "linux"))]
 #[derive(Copy, Clone, Debug)]
 pub struct AlgSetAeadAuthSize;
@@ -353,6 +575,8 @@
     }
 }
 
+#[allow(missing_docs)]
+// Not documented by Linux!
 #[cfg(any(target_os = "android", target_os = "linux"))]
 #[derive(Clone, Debug)]
 pub struct AlgSetKey<T>(::std::marker::PhantomData<T>);
@@ -387,9 +611,9 @@
  */
 
 /// Helper trait that describes what is expected from a `GetSockOpt` getter.
-unsafe trait Get<T> {
+trait Get<T> {
     /// Returns an uninitialized value.
-    unsafe fn uninit() -> Self;
+    fn uninit() -> Self;
     /// Returns a pointer to the stored value. This pointer will be passed to the system's
     /// `getsockopt` call (`man 3p getsockopt`, argument `option_value`).
     fn ffi_ptr(&mut self) -> *mut c_void;
@@ -401,7 +625,7 @@
 }
 
 /// Helper trait that describes what is expected from a `SetSockOpt` setter.
-unsafe trait Set<'a, T> {
+trait Set<'a, T> {
     /// Initialize the setter with a given value.
     fn new(val: &'a T) -> Self;
     /// Returns a pointer to the stored value. This pointer will be passed to the system's
@@ -418,8 +642,8 @@
     val: MaybeUninit<T>,
 }
 
-unsafe impl<T> Get<T> for GetStruct<T> {
-    unsafe fn uninit() -> Self {
+impl<T> Get<T> for GetStruct<T> {
+    fn uninit() -> Self {
         GetStruct {
             len: mem::size_of::<T>() as socklen_t,
             val: MaybeUninit::uninit(),
@@ -445,7 +669,7 @@
     ptr: &'a T,
 }
 
-unsafe impl<'a, T> Set<'a, T> for SetStruct<'a, T> {
+impl<'a, T> Set<'a, T> for SetStruct<'a, T> {
     fn new(ptr: &'a T) -> SetStruct<'a, T> {
         SetStruct { ptr }
     }
@@ -465,8 +689,8 @@
     val: MaybeUninit<c_int>,
 }
 
-unsafe impl Get<bool> for GetBool {
-    unsafe fn uninit() -> Self {
+impl Get<bool> for GetBool {
+    fn uninit() -> Self {
         GetBool {
             len: mem::size_of::<c_int>() as socklen_t,
             val: MaybeUninit::uninit(),
@@ -492,7 +716,7 @@
     val: c_int,
 }
 
-unsafe impl<'a> Set<'a, bool> for SetBool {
+impl<'a> Set<'a, bool> for SetBool {
     fn new(val: &'a bool) -> SetBool {
         SetBool { val: if *val { 1 } else { 0 } }
     }
@@ -512,8 +736,8 @@
     val: MaybeUninit<u8>,
 }
 
-unsafe impl Get<u8> for GetU8 {
-    unsafe fn uninit() -> Self {
+impl Get<u8> for GetU8 {
+    fn uninit() -> Self {
         GetU8 {
             len: mem::size_of::<u8>() as socklen_t,
             val: MaybeUninit::uninit(),
@@ -539,7 +763,7 @@
     val: u8,
 }
 
-unsafe impl<'a> Set<'a, u8> for SetU8 {
+impl<'a> Set<'a, u8> for SetU8 {
     fn new(val: &'a u8) -> SetU8 {
         SetU8 { val: *val as u8 }
     }
@@ -559,8 +783,8 @@
     val: MaybeUninit<c_int>,
 }
 
-unsafe impl Get<usize> for GetUsize {
-    unsafe fn uninit() -> Self {
+impl Get<usize> for GetUsize {
+    fn uninit() -> Self {
         GetUsize {
             len: mem::size_of::<c_int>() as socklen_t,
             val: MaybeUninit::uninit(),
@@ -586,7 +810,7 @@
     val: c_int,
 }
 
-unsafe impl<'a> Set<'a, usize> for SetUsize {
+impl<'a> Set<'a, usize> for SetUsize {
     fn new(val: &'a usize) -> SetUsize {
         SetUsize { val: *val as c_int }
     }
@@ -606,8 +830,8 @@
     val: MaybeUninit<T>,
 }
 
-unsafe impl<T: AsMut<[u8]>> Get<OsString> for GetOsString<T> {
-    unsafe fn uninit() -> Self {
+impl<T: AsMut<[u8]>> Get<OsString> for GetOsString<T> {
+    fn uninit() -> Self {
         GetOsString {
             len: mem::size_of::<T>() as socklen_t,
             val: MaybeUninit::uninit(),
@@ -634,7 +858,7 @@
     val: &'a OsStr,
 }
 
-unsafe impl<'a> Set<'a, OsString> for SetOsString<'a> {
+impl<'a> Set<'a, OsString> for SetOsString<'a> {
     fn new(val: &'a OsString) -> SetOsString {
         SetOsString { val: val.as_os_str() }
     }
diff --git a/src/sys/stat.rs b/src/sys/stat.rs
index 15451e7..c8f1041 100644
--- a/src/sys/stat.rs
+++ b/src/sys/stat.rs
@@ -9,6 +9,7 @@
 use crate::sys::time::{TimeSpec, TimeVal};
 
 libc_bitflags!(
+    /// "File type" flags for `mknod` and related functions.
     pub struct SFlag: mode_t {
         S_IFIFO;
         S_IFCHR;
@@ -22,6 +23,7 @@
 );
 
 libc_bitflags! {
+    /// "File mode / permissions" flags.
     pub struct Mode: mode_t {
         S_IRWXU;
         S_IRUSR;
@@ -41,30 +43,45 @@
     }
 }
 
+/// Create a special or ordinary file, by pathname.
 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)
-        }
+    let res = path.with_nix_path(|cstr| unsafe {
+        libc::mknod(cstr.as_ptr(), kind.bits | perm.bits() as mode_t, dev)
+    })?;
+
+    Errno::result(res).map(drop)
+}
+
+/// Create a special or ordinary file, relative to a given directory.
+#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox")))]
+pub fn mknodat<P: ?Sized + NixPath>(
+    dirfd: RawFd,
+    path: &P,
+    kind: SFlag,
+    perm: Mode,
+    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)
     })?;
 
     Errno::result(res).map(drop)
 }
 
 #[cfg(target_os = "linux")]
-pub fn major(dev: dev_t) -> u64 {
+pub const fn major(dev: dev_t) -> u64 {
     ((dev >> 32) & 0xffff_f000) |
     ((dev >>  8) & 0x0000_0fff)
 }
 
 #[cfg(target_os = "linux")]
-pub fn minor(dev: dev_t) -> u64 {
+pub const fn minor(dev: dev_t) -> u64 {
     ((dev >> 12) & 0xffff_ff00) |
     ((dev      ) & 0x0000_00ff)
 }
 
 #[cfg(target_os = "linux")]
-pub fn makedev(major: u64, minor: u64) -> dev_t {
+pub const fn makedev(major: u64, minor: u64) -> dev_t {
     ((major & 0xffff_f000) << 32) |
     ((major & 0x0000_0fff) <<  8) |
     ((minor & 0xffff_ff00) << 12) |
@@ -239,6 +256,7 @@
 }
 
 /// Flags for `utimensat` function.
+// TODO: replace with fcntl::AtFlags
 #[derive(Clone, Copy, Debug)]
 pub enum UtimensatFlags {
     FollowSymlink,
diff --git a/src/sys/statfs.rs b/src/sys/statfs.rs
index 27b7259..829be57 100644
--- a/src/sys/statfs.rs
+++ b/src/sys/statfs.rs
@@ -1,3 +1,6 @@
+//! Get filesystem statistics, non-portably
+//!
+//! See [`statvfs`](crate::sys::statvfs) for a portable alternative.
 use std::fmt::{self, Debug};
 use std::mem;
 use std::os::unix::io::AsRawFd;
@@ -6,11 +9,14 @@
 
 use crate::{NixPath, Result, errno::Errno};
 
+/// Identifies a mounted file system
 #[cfg(target_os = "android")]
 pub type fsid_t = libc::__fsid_t;
+/// Identifies a mounted file system
 #[cfg(not(target_os = "android"))]
 pub type fsid_t = libc::fsid_t;
 
+/// Describes a mounted file system
 #[derive(Clone, Copy)]
 #[repr(transparent)]
 pub struct Statfs(libc::statfs);
@@ -26,6 +32,7 @@
 #[cfg(all(target_os = "linux", not(any(target_arch = "s390x", target_env = "musl"))))]
 type fs_type_t = libc::__fsword_t;
 
+/// Describes the file system type as known by the operating system.
 #[cfg(any(
     target_os = "freebsd",
     target_os = "android",
@@ -36,63 +43,94 @@
 #[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")))]
+#[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")))]
+#[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")))]
+#[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")))]
+#[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")))]
+#[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")))]
+#[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")))]
+#[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")))]
+#[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")))]
+#[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")))]
+#[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")))]
+#[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")))]
+#[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")))]
+#[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")))]
+#[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")))]
+#[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")))]
+#[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")))]
+#[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")))]
+#[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")))]
+#[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")))]
+#[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")))]
+#[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")))]
+#[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")))]
+#[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")))]
+#[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")))]
+#[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")))]
+#[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")))]
+#[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")))]
+#[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")))]
+#[allow(missing_docs)]
 pub const CGROUP2_SUPER_MAGIC: FsType = FsType(libc::CGROUP2_SUPER_MAGIC as fs_type_t);
 
 
@@ -451,6 +489,14 @@
     }
 }
 
+/// Describes a mounted file system.
+///
+/// The result is OS-dependent.  For a portabable alternative, see
+/// [`statvfs`](crate::sys::statvfs::statvfs).
+///
+/// # Arguments
+///
+/// `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();
@@ -459,6 +505,14 @@
     }
 }
 
+/// Describes a mounted file system.
+///
+/// The result is OS-dependent.  For a portabable alternative, see
+/// [`fstatvfs`](crate::sys::statvfs::fstatvfs).
+///
+/// # Arguments
+///
+/// `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();
diff --git a/src/sys/statvfs.rs b/src/sys/statvfs.rs
index 508fa8d..15e7a7d 100644
--- a/src/sys/statvfs.rs
+++ b/src/sys/statvfs.rs
@@ -150,7 +150,7 @@
 
     #[test]
     fn statvfs_call() {
-        statvfs("/".as_bytes()).unwrap();
+        statvfs(&b"/"[..]).unwrap();
     }
 
     #[test]
diff --git a/src/sys/termios.rs b/src/sys/termios.rs
index 36a3601..01d4608 100644
--- a/src/sys/termios.rs
+++ b/src/sys/termios.rs
@@ -152,11 +152,11 @@
 //! # }
 //! ```
 use cfg_if::cfg_if;
-use crate::{Error, Result};
+use crate::Result;
 use crate::errno::Errno;
 use libc::{self, c_int, tcflag_t};
 use std::cell::{Ref, RefCell};
-use std::convert::{From, TryFrom};
+use std::convert::From;
 use std::mem;
 use std::os::unix::io::RawFd;
 
@@ -256,6 +256,7 @@
     /// B0 is special and will disable the port.
     #[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))]
+    #[non_exhaustive]
     pub enum BaudRate {
         B0,
         B50,
@@ -339,119 +340,7 @@
         #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
         B4000000,
     }
-}
-
-impl TryFrom<libc::speed_t> for BaudRate {
-    type Error = Error;
-
-    fn try_from(s: libc::speed_t) -> Result<BaudRate> {
-        use libc::{B0, B50, B75, B110, B134, B150, B200, B300, B600, B1200, B1800, B2400, B4800,
-                   B9600, B19200, B38400, B57600, B115200, B230400};
-        #[cfg(any(target_os = "android", target_os = "linux"))]
-        use libc::{B500000, B576000, B1000000, B1152000, B1500000, B2000000};
-        #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
-        use libc::{B2500000, B3000000, B3500000, B4000000};
-        #[cfg(any(target_os = "dragonfly",
-                  target_os = "freebsd",
-                  target_os = "macos",
-                  target_os = "netbsd",
-                  target_os = "openbsd"))]
-        use libc::{B7200, B14400, B28800, B76800};
-        #[cfg(any(target_os = "android",
-                  target_os = "freebsd",
-                  target_os = "linux",
-                  target_os = "netbsd"))]
-        use libc::{B460800, B921600};
-        #[cfg(any(target_os = "illumos", target_os = "solaris"))]
-        use libc::{B153600, B307200, B460800, B921600};
-
-        match s {
-            B0 => Ok(BaudRate::B0),
-            B50 => Ok(BaudRate::B50),
-            B75 => Ok(BaudRate::B75),
-            B110 => Ok(BaudRate::B110),
-            B134 => Ok(BaudRate::B134),
-            B150 => Ok(BaudRate::B150),
-            B200 => Ok(BaudRate::B200),
-            B300 => Ok(BaudRate::B300),
-            B600 => Ok(BaudRate::B600),
-            B1200 => Ok(BaudRate::B1200),
-            B1800 => Ok(BaudRate::B1800),
-            B2400 => Ok(BaudRate::B2400),
-            B4800 => Ok(BaudRate::B4800),
-            #[cfg(any(target_os = "dragonfly",
-                      target_os = "freebsd",
-                      target_os = "macos",
-                      target_os = "netbsd",
-                      target_os = "openbsd"))]
-            B7200 => Ok(BaudRate::B7200),
-            B9600 => Ok(BaudRate::B9600),
-            #[cfg(any(target_os = "dragonfly",
-                      target_os = "freebsd",
-                      target_os = "macos",
-                      target_os = "netbsd",
-                      target_os = "openbsd"))]
-            B14400 => Ok(BaudRate::B14400),
-            B19200 => Ok(BaudRate::B19200),
-            #[cfg(any(target_os = "dragonfly",
-                      target_os = "freebsd",
-                      target_os = "macos",
-                      target_os = "netbsd",
-                      target_os = "openbsd"))]
-            B28800 => Ok(BaudRate::B28800),
-            B38400 => Ok(BaudRate::B38400),
-            B57600 => Ok(BaudRate::B57600),
-            #[cfg(any(target_os = "dragonfly",
-                      target_os = "freebsd",
-                      target_os = "macos",
-                      target_os = "netbsd",
-                      target_os = "openbsd"))]
-            B76800 => Ok(BaudRate::B76800),
-            B115200 => Ok(BaudRate::B115200),
-            #[cfg(any(target_os = "illumos",
-                      target_os = "solaris"))]
-            B153600 => Ok(BaudRate::B153600),
-            B230400 => Ok(BaudRate::B230400),
-            #[cfg(any(target_os = "illumos",
-                      target_os = "solaris"))]
-            B307200 => Ok(BaudRate::B307200),
-            #[cfg(any(target_os = "android",
-                      target_os = "freebsd",
-                      target_os = "illumos",
-                      target_os = "linux",
-                      target_os = "netbsd",
-                      target_os = "solaris"))]
-            B460800 => Ok(BaudRate::B460800),
-            #[cfg(any(target_os = "android", target_os = "linux"))]
-            B500000 => Ok(BaudRate::B500000),
-            #[cfg(any(target_os = "android", target_os = "linux"))]
-            B576000 => Ok(BaudRate::B576000),
-            #[cfg(any(target_os = "android",
-                      target_os = "freebsd",
-                      target_os = "illumos",
-                      target_os = "linux",
-                      target_os = "netbsd",
-                      target_os = "solaris"))]
-            B921600 => Ok(BaudRate::B921600),
-            #[cfg(any(target_os = "android", target_os = "linux"))]
-            B1000000 => Ok(BaudRate::B1000000),
-            #[cfg(any(target_os = "android", target_os = "linux"))]
-            B1152000 => Ok(BaudRate::B1152000),
-            #[cfg(any(target_os = "android", target_os = "linux"))]
-            B1500000 => Ok(BaudRate::B1500000),
-            #[cfg(any(target_os = "android", target_os = "linux"))]
-            B2000000 => Ok(BaudRate::B2000000),
-            #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
-            B2500000 => Ok(BaudRate::B2500000),
-            #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
-            B3000000 => Ok(BaudRate::B3000000),
-            #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
-            B3500000 => Ok(BaudRate::B3500000),
-            #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
-            B4000000 => Ok(BaudRate::B4000000),
-            _ => Err(Error::from(Errno::EINVAL))
-        }
-    }
+    impl TryFrom<libc::speed_t>
 }
 
 #[cfg(any(target_os = "freebsd",
@@ -472,6 +361,7 @@
     ///
     /// Used as an argument to `tcsetattr()`
     #[repr(i32)]
+    #[non_exhaustive]
     pub enum SetArg {
         /// The change will occur immediately
         TCSANOW,
@@ -487,6 +377,7 @@
     ///
     /// Used as an argument to `tcflush()`.
     #[repr(i32)]
+    #[non_exhaustive]
     pub enum FlushArg {
         /// Flush data that was received but not read
         TCIFLUSH,
@@ -502,6 +393,7 @@
     ///
     /// Used as an argument to `tcflow()`.
     #[repr(i32)]
+    #[non_exhaustive]
     pub enum FlowArg {
         /// Suspend transmission
         TCOOFF,
@@ -518,6 +410,7 @@
 libc_enum! {
     /// Indices into the `termios.c_cc` array for special characters.
     #[repr(usize)]
+    #[non_exhaustive]
     pub enum SpecialCharacterIndices {
         VDISCARD,
         #[cfg(any(target_os = "dragonfly",
@@ -1113,6 +1006,7 @@
 #[cfg(test)]
 mod test {
     use super::*;
+    use std::convert::TryFrom;
 
     #[test]
     fn try_from() {
diff --git a/src/sys/time.rs b/src/sys/time.rs
index 7546d1b..ac42471 100644
--- a/src/sys/time.rs
+++ b/src/sys/time.rs
@@ -77,11 +77,7 @@
 
 impl From<Duration> for TimeSpec {
     fn from(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
-        })
+        Self::from_duration(duration)
     }
 }
 
@@ -191,13 +187,25 @@
     }
 
     #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
-    pub fn tv_sec(&self) -> time_t {
+    pub const fn tv_sec(&self) -> time_t {
         self.0.tv_sec
     }
 
-    pub fn tv_nsec(&self) -> timespec_tv_nsec_t {
+    pub const fn tv_nsec(&self) -> timespec_tv_nsec_t {
         self.0.tv_nsec
     }
+
+    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
+        })
+    }
+
+    pub const fn from_timespec(timespec: timespec) -> Self {
+        Self(timespec)
+    }
 }
 
 impl ops::Neg for TimeSpec {
@@ -396,11 +404,11 @@
     }
 
     #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
-    pub fn tv_sec(&self) -> time_t {
+    pub const fn tv_sec(&self) -> time_t {
         self.0.tv_sec
     }
 
-    pub fn tv_usec(&self) -> suseconds_t {
+    pub const fn tv_usec(&self) -> suseconds_t {
         self.0.tv_usec
     }
 }
diff --git a/src/sys/timerfd.rs b/src/sys/timerfd.rs
index 44915be..705a3c4 100644
--- a/src/sys/timerfd.rs
+++ b/src/sys/timerfd.rs
@@ -58,6 +58,7 @@
     /// The type of the clock used to mark the progress of the timer. For more
     /// details on each kind of clock, please refer to [timerfd_create(2)](https://man7.org/linux/man-pages/man2/timerfd_create.2.html).
     #[repr(i32)]
+    #[non_exhaustive]
     pub enum ClockId {
         CLOCK_REALTIME,
         CLOCK_MONOTONIC,
@@ -87,7 +88,7 @@
 struct TimerSpec(libc::itimerspec);
 
 impl TimerSpec {
-    pub fn none() -> Self {
+    pub const fn none() -> Self {
         Self(libc::itimerspec {
             it_interval: libc::timespec {
                 tv_sec: 0,
@@ -256,14 +257,9 @@
     ///
     /// Note: If the alarm is unset, then you will wait forever.
     pub fn wait(&self) -> Result<()> {
-        loop {
-            if let Err(e) = read(self.fd, &mut [0u8; 8]) {
-                match e {
-                    Errno::EINTR => continue,
-                    _ => return Err(e),
-                }
-            } else {
-                break;
+        while let Err(e) = read(self.fd, &mut [0u8; 8]) {
+            if e != Errno::EINTR {
+                return Err(e)
             }
         }
 
diff --git a/src/sys/uio.rs b/src/sys/uio.rs
index 48a0efd..3abcde2 100644
--- a/src/sys/uio.rs
+++ b/src/sys/uio.rs
@@ -1,5 +1,4 @@
-// Silence invalid warnings due to rust-lang/rust#16719
-#![allow(improper_ctypes)]
+//! Vectored I/O
 
 use crate::Result;
 use crate::errno::Errno;
@@ -7,12 +6,18 @@
 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) };
 
     Errno::result(res).map(|r| r as usize)
 }
 
+/// 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) };
 
@@ -25,11 +30,7 @@
 /// or an error occurs. The file offset is not changed.
 ///
 /// See also: [`writev`](fn.writev.html) and [`pwrite`](fn.pwrite.html)
-#[cfg(any(target_os = "dragonfly",
-          target_os = "freebsd",
-          target_os = "linux",
-          target_os = "netbsd",
-          target_os = "openbsd"))]
+#[cfg(not(target_os = "redox"))]
 pub fn pwritev(fd: RawFd, iov: &[IoVec<&[u8]>],
                offset: off_t) -> Result<usize> {
     let res = unsafe {
@@ -46,11 +47,7 @@
 /// changed.
 ///
 /// See also: [`readv`](fn.readv.html) and [`pread`](fn.pread.html)
-#[cfg(any(target_os = "dragonfly",
-          target_os = "freebsd",
-          target_os = "linux",
-          target_os = "netbsd",
-          target_os = "openbsd"))]
+#[cfg(not(target_os = "redox"))]
 pub fn preadv(fd: RawFd, iov: &[IoVec<&mut [u8]>],
               offset: off_t) -> Result<usize> {
     let res = unsafe {
@@ -60,6 +57,10 @@
     Errno::result(res).map(|r| r as usize)
 }
 
+/// Low-level write to a file, with specified offset.
+///
+/// See also [pwrite(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pwrite.html)
+// 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,
@@ -69,6 +70,10 @@
     Errno::result(res).map(|r| r as usize)
 }
 
+/// Low-level write to 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>{
     let res = unsafe {
         libc::pread(fd, buf.as_mut_ptr() as *mut c_void, buf.len() as size_t,
@@ -166,11 +171,17 @@
     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;
@@ -192,6 +203,7 @@
         }, 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,
@@ -201,6 +213,7 @@
 }
 
 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,
diff --git a/src/sys/utsname.rs b/src/sys/utsname.rs
index bf1a814..98edee0 100644
--- a/src/sys/utsname.rs
+++ b/src/sys/utsname.rs
@@ -1,34 +1,42 @@
+//! Get system identification
 use std::mem;
 use libc::{self, c_char};
 use std::ffi::CStr;
 use std::str::from_utf8_unchecked;
 
+/// Describes the running system.  Return type of [`uname`].
 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
 #[repr(transparent)]
 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)
     }
 
+    /// Network name of this machine.
     pub fn nodename(&self) -> &str {
         to_str(&(&self.0.nodename as *const c_char ) as *const *const c_char)
     }
 
+    /// 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)
     }
 
+    /// 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)
     }
 
+    /// Machine hardware platform.
     pub fn machine(&self) -> &str {
         to_str(&(&self.0.machine as *const c_char ) as *const *const c_char)
     }
 }
 
+/// Get system identification
 pub fn uname() -> UtsName {
     unsafe {
         let mut ret = mem::MaybeUninit::uninit();
diff --git a/src/sys/wait.rs b/src/sys/wait.rs
index 6c5c0f0..ee49e37 100644
--- a/src/sys/wait.rs
+++ b/src/sys/wait.rs
@@ -1,3 +1,4 @@
+//! Wait for a process to change status
 use crate::errno::Errno;
 use crate::sys::signal::Signal;
 use crate::unistd::Pid;
@@ -7,9 +8,17 @@
 use std::convert::TryFrom;
 
 libc_bitflags!(
+    /// Controls the behavior of [`waitpid`].
     pub struct WaitPidFlag: c_int {
+        /// Do not block when there are no processes wishing to report status.
         WNOHANG;
+        /// Report the status of selected processes which are stopped due to a
+        /// [`SIGTTIN`](crate::sys::signal::Signal::SIGTTIN),
+        /// [`SIGTTOU`](crate::sys::signal::Signal::SIGTTOU),
+        /// [`SIGTSTP`](crate::sys::signal::Signal::SIGTSTP), or
+        /// [`SIGSTOP`](crate::sys::signal::Signal::SIGSTOP) signal.
         WUNTRACED;
+        /// Report the status of selected processes which have terminated.
         #[cfg(any(target_os = "android",
                   target_os = "freebsd",
                   target_os = "haiku",
@@ -19,7 +28,11 @@
                   target_os = "macos",
                   target_os = "netbsd"))]
         WEXITED;
+        /// Report the status of selected processes that have continued from a
+        /// job control stop by receiving a
+        /// [`SIGCONT`](crate::sys::signal::Signal::SIGCONT) signal.
         WCONTINUED;
+        /// An alias for WUNTRACED.
         #[cfg(any(target_os = "android",
                   target_os = "freebsd",
                   target_os = "haiku",
@@ -45,6 +58,7 @@
         /// Wait on all children, regardless of type
         #[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
         __WALL;
+        /// Wait for "clone" children only.
         #[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
         __WCLONE;
     }
@@ -213,6 +227,9 @@
     }
 }
 
+/// 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> {
     use self::WaitStatus::*;
 
@@ -237,6 +254,9 @@
     }
 }
 
+/// Wait for any child process to change status or a signal is received.
+///
+/// See also [wait(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html)
 pub fn wait() -> Result<WaitStatus> {
     waitpid(None, None)
 }
diff --git a/src/time.rs b/src/time.rs
index 45dd26e..6275b59 100644
--- a/src/time.rs
+++ b/src/time.rs
@@ -6,7 +6,7 @@
     target_os = "android",
     target_os = "emscripten",
 ))]
-use crate::{unistd::Pid, Error};
+use crate::unistd::Pid;
 use crate::{Errno, Result};
 use libc::{self, clockid_t};
 use std::mem::MaybeUninit;
@@ -20,7 +20,7 @@
 
 impl ClockId {
     /// Creates `ClockId` from raw `clockid_t`
-    pub fn from_raw(clk_id: clockid_t) -> Self {
+    pub const fn from_raw(clk_id: clockid_t) -> Self {
         ClockId(clk_id)
     }
 
@@ -61,7 +61,7 @@
     }
 
     /// Gets the raw `clockid_t` wrapped by `self`
-    pub fn as_raw(self) -> clockid_t {
+    pub const fn as_raw(self) -> clockid_t {
         self.0
     }
 
@@ -255,6 +255,6 @@
         let res = unsafe { clk_id.assume_init() };
         Ok(ClockId::from(res))
     } else {
-        Err(Error::from(Errno::from_i32(ret)))
+        Err(Errno::from_i32(ret))
     }
 }
diff --git a/src/ucontext.rs b/src/ucontext.rs
index a5b8cc7..f2338bd 100644
--- a/src/ucontext.rs
+++ b/src/ucontext.rs
@@ -1,4 +1,3 @@
-use libc;
 #[cfg(not(target_env = "musl"))]
 use crate::Result;
 #[cfg(not(target_env = "musl"))]
diff --git a/src/unistd.rs b/src/unistd.rs
index de3b049..2c89d77 100644
--- a/src/unistd.rs
+++ b/src/unistd.rs
@@ -180,10 +180,7 @@
     /// Return `true` if this is the child process of the `fork()`
     #[inline]
     pub fn is_child(self) -> bool {
-        match self {
-            ForkResult::Child => true,
-            _ => false
-        }
+        matches!(self, ForkResult::Child)
     }
 
     /// Returns `true` if this is the parent process of the `fork()`
@@ -200,14 +197,19 @@
 /// be created that are identical with the exception of their pid and the
 /// return value of this function.  As an example:
 ///
-/// ```no_run
-/// use nix::unistd::{fork, ForkResult};
+/// ```
+/// use nix::{sys::wait::waitpid,unistd::{fork, ForkResult, write}};
 ///
 /// match unsafe{fork()} {
 ///    Ok(ForkResult::Parent { child, .. }) => {
 ///        println!("Continuing execution in parent process, new child has pid: {}", child);
+///        waitpid(child, None).unwrap();
 ///    }
-///    Ok(ForkResult::Child) => println!("I'm a new child process"),
+///    Ok(ForkResult::Child) => {
+///        // Unsafe to use `println!` (or `unwrap`) here. See Safety.
+///        write(libc::STDOUT_FILENO, "I'm a new child process\n".as_bytes()).ok();
+///        unsafe { libc::_exit(0) };
+///    }
 ///    Err(_) => println!("Fork failed"),
 /// }
 /// ```
@@ -393,7 +395,7 @@
 #[inline]
 fn dup3_polyfill(oldfd: RawFd, newfd: RawFd, flags: OFlag) -> Result<RawFd> {
     if oldfd == newfd {
-        return Err(Error::from(Errno::EINVAL));
+        return Err(Errno::EINVAL);
     }
 
     let fd = dup2(oldfd, newfd)?;
@@ -567,7 +569,7 @@
     use std::cmp::min;
 
     if buf.capacity() >= limit {
-        return Err(Error::from(Errno::ERANGE))
+        return Err(Errno::ERANGE)
     }
 
     let capacity = min(buf.capacity() * 2, limit);
@@ -610,9 +612,9 @@
                 let error = Errno::last();
                 // ERANGE means buffer was too small to store directory name
                 if error != Errno::ERANGE {
-                    return Err(Error::from(error));
+                    return Err(error);
                 }
-            }
+           }
 
             // Trigger the internal buffer resizing logic.
             reserve_double_buffer_size(&mut buf, PATH_MAX as usize)?;
@@ -734,7 +736,7 @@
         libc::execv(path.as_ptr(), args_p.as_ptr())
     };
 
-    Err(Error::from(Errno::last()))
+    Err(Errno::last())
 }
 
 
@@ -759,7 +761,7 @@
         libc::execve(path.as_ptr(), args_p.as_ptr(), env_p.as_ptr())
     };
 
-    Err(Error::from(Errno::last()))
+    Err(Errno::last())
 }
 
 /// Replace the current process image with a new one and replicate shell `PATH`
@@ -779,7 +781,7 @@
         libc::execvp(filename.as_ptr(), args_p.as_ptr())
     };
 
-    Err(Error::from(Errno::last()))
+    Err(Errno::last())
 }
 
 /// Replace the current process image with a new one and replicate shell `PATH`
@@ -800,7 +802,7 @@
         libc::execvpe(filename.as_ptr(), args_p.as_ptr(), env_p.as_ptr())
     };
 
-    Err(Error::from(Errno::last()))
+    Err(Errno::last())
 }
 
 /// Replace the current process image with a new one (see
@@ -828,7 +830,7 @@
         libc::fexecve(fd, args_p.as_ptr(), env_p.as_ptr())
     };
 
-    Err(Error::from(Errno::last()))
+    Err(Errno::last())
 }
 
 /// Execute program relative to a directory file descriptor (see
@@ -853,7 +855,7 @@
                       args_p.as_ptr(), env_p.as_ptr(), flags);
     };
 
-    Err(Error::from(Errno::last()))
+    Err(Errno::last())
 }
 
 /// Daemonize this process by detaching from the controlling terminal (see
@@ -1073,10 +1075,10 @@
 /// The following flags are supported, and will be set atomically as the pipe is
 /// created:
 ///
-/// `O_CLOEXEC`:    Set the close-on-exec flag for the new file descriptors.
-#[cfg_attr(target_os = "linux", doc = "`O_DIRECT`: Create a pipe that performs I/O in \"packet\" mode.  ")]
-#[cfg_attr(target_os = "netbsd", doc = "`O_NOSIGPIPE`: Return `EPIPE` instead of raising `SIGPIPE`.  ")]
-/// `O_NONBLOCK`:   Set the non-blocking flag for the ends of the pipe.
+/// - `O_CLOEXEC`:    Set the close-on-exec flag for the new file descriptors.
+#[cfg_attr(target_os = "linux", doc = "- `O_DIRECT`: Create a pipe that performs I/O in \"packet\" mode.")]
+#[cfg_attr(target_os = "netbsd", doc = "- `O_NOSIGPIPE`: Return `EPIPE` instead of raising `SIGPIPE`.")]
+/// - `O_NONBLOCK`:   Set the non-blocking flag for the ends of the pipe.
 ///
 /// See also [pipe(2)](https://man7.org/linux/man-pages/man2/pipe.2.html)
 #[cfg(any(target_os = "android",
@@ -1133,9 +1135,9 @@
         } else {
             match Errno::last() {
                 Errno::ENOTTY => Ok(false),
-                err => Err(Error::from(err)),
+                err => Err(err),
             }
-        }
+       }
     }
 }
 
@@ -1415,6 +1417,14 @@
     // Next, get the number of groups so we can size our Vec
     let ngroups = unsafe { libc::getgroups(0, ptr::null_mut()) };
 
+    // If there are no supplementary groups, return early.
+    // This prevents a potential buffer over-read if the number of groups
+    // increases from zero before the next call. It would return the total
+    // number of groups beyond the capacity of the buffer.
+    if ngroups == 0 {
+        return Ok(Vec::new());
+    }
+
     // Now actually get the groups. We try multiple times in case the number of
     // groups has changed since the first call to getgroups() and the buffer is
     // now too small.
@@ -1436,7 +1446,7 @@
                 // EINVAL indicates that the buffer size was too
                 // small, resize it up to ngroups_max as limit.
                 reserve_double_buffer_size(&mut groups, ngroups_max)
-                    .or(Err(Error::from(Errno::EINVAL)))?;
+                    .or(Err(Errno::EINVAL))?;
             },
             Err(e) => return Err(e)
         }
@@ -1530,8 +1540,7 @@
         Ok(None) | Err(_) => <c_int>::max_value(),
     };
     use std::cmp::min;
-    let mut ngroups = min(ngroups_max, 8);
-    let mut groups = Vec::<Gid>::with_capacity(ngroups as usize);
+    let mut groups = Vec::<Gid>::with_capacity(min(ngroups_max, 8) as usize);
     cfg_if! {
         if #[cfg(any(target_os = "ios", target_os = "macos"))] {
             type getgrouplist_group_t = c_int;
@@ -1541,6 +1550,7 @@
     }
     let gid: gid_t = group.into();
     loop {
+        let mut ngroups = groups.capacity() as i32;
         let ret = unsafe {
             libc::getgrouplist(user.as_ptr(),
                                gid as getgrouplist_group_t,
@@ -1558,7 +1568,7 @@
             // groups as possible, but Linux manpages do not mention this
             // behavior.
             reserve_double_buffer_size(&mut groups, ngroups_max as usize)
-                .map_err(|_| Error::from(Errno::EINVAL))?;
+                .map_err(|_| Errno::EINVAL)?;
         }
     }
 }
@@ -1801,6 +1811,7 @@
 /// - [unistd.h](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/unistd.h.html)
 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
 #[repr(i32)]
+#[non_exhaustive]
 pub enum PathconfVar {
     #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "linux",
               target_os = "netbsd", target_os = "openbsd", target_os = "redox"))]
@@ -1916,7 +1927,7 @@
         if errno::errno() == 0 {
             Ok(None)
         } else {
-            Err(Error::from(Errno::last()))
+            Err(Errno::last())
         }
     } else {
         Ok(Some(raw))
@@ -1955,7 +1966,7 @@
         if errno::errno() == 0 {
             Ok(None)
         } else {
-            Err(Error::from(Errno::last()))
+            Err(Errno::last())
         }
     } else {
         Ok(Some(raw))
@@ -1980,6 +1991,7 @@
 /// - [limits.h](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/limits.h.html)
 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
 #[repr(i32)]
+#[non_exhaustive]
 pub enum SysconfVar {
     /// Maximum number of I/O operations in a single list I/O call supported by
     /// the implementation.
@@ -2454,7 +2466,7 @@
         if errno::errno() == 0 {
             Ok(None)
         } else {
-            Err(Error::from(Errno::last()))
+            Err(Errno::last())
         }
     } else {
         Ok(Some(raw))
@@ -2624,7 +2636,7 @@
     /// Group ID
     pub gid: Gid,
     /// User information
-    #[cfg(not(target_os = "android"))]
+    #[cfg(not(all(target_os = "android", target_pointer_width = "32")))]
     pub gecos: CString,
     /// Home directory
     pub dir: PathBuf,
@@ -2660,7 +2672,7 @@
             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(),
-                #[cfg(not(target_os = "android"))]
+                #[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())),
@@ -2690,6 +2702,58 @@
 }
 
 #[cfg(not(target_os = "redox"))] // RedoxFS does not support passwd
+impl From<User> for libc::passwd {
+    fn from(u: User) -> Self {
+        let name = match CString::new(u.name) {
+            Ok(n) => n.into_raw(),
+            Err(_) => CString::new("").unwrap().into_raw(),
+        };
+        let dir = match u.dir.into_os_string().into_string() {
+            Ok(s) => CString::new(s.as_str()).unwrap().into_raw(),
+            Err(_) => CString::new("").unwrap().into_raw(),
+        };
+        let shell = match u.shell.into_os_string().into_string() {
+            Ok(s) => CString::new(s.as_str()).unwrap().into_raw(),
+            Err(_) => CString::new("").unwrap().into_raw(),
+        };
+        Self {
+            pw_name: name,
+            pw_passwd: u.passwd.into_raw(),
+            #[cfg(not(all(target_os = "android", target_pointer_width = "32")))]
+            pw_gecos: u.gecos.into_raw(),
+            pw_dir: dir,
+            pw_shell: shell,
+            pw_uid: u.uid.0,
+            pw_gid: u.gid.0,
+            #[cfg(not(any(target_os = "android",
+                          target_os = "fuchsia",
+                          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 = "illumos",
+                          target_os = "linux",
+                          target_os = "solaris")))]
+            pw_change: u.change,
+            #[cfg(not(any(target_os = "android",
+                          target_os = "fuchsia",
+                          target_os = "illumos",
+                          target_os = "linux",
+                          target_os = "solaris")))]
+            pw_expire: u.expire,
+            #[cfg(target_os = "illumos")]
+            pw_age: CString::new("").unwrap().into_raw(),
+            #[cfg(target_os = "illumos")]
+            pw_comment: CString::new("").unwrap().into_raw(),
+            #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+            pw_fields: 0,
+        }
+    }
+}
+
+#[cfg(not(target_os = "redox"))] // RedoxFS does not support passwd
 impl User {
     fn from_anything<F>(f: F) -> Result<Option<Self>>
     where
@@ -2721,7 +2785,7 @@
                 // Trigger the internal buffer resizing logic.
                 reserve_double_buffer_size(&mut cbuf, buflimit)?;
             } else {
-                return Err(Error::from(Errno::last()));
+                return Err(Errno::last());
             }
         }
     }
@@ -2842,7 +2906,7 @@
                 // Trigger the internal buffer resizing logic.
                 reserve_double_buffer_size(&mut cbuf, buflimit)?;
             } else {
-                return Err(Error::from(Errno::last()));
+                return Err(Errno::last());
             }
         }
     }
@@ -2901,7 +2965,7 @@
 
     let ret = unsafe { libc::ttyname_r(fd, c_buf, buf.len()) };
     if ret != 0 {
-        return Err(Error::from(Errno::from_i32(ret)));
+        return Err(Errno::from_i32(ret));
     }
 
     let nul = buf.iter().position(|c| *c == b'\0').unwrap();
diff --git a/test/common/mod.rs b/test/common/mod.rs
index cdc3258..84a0b4f 100644
--- a/test/common/mod.rs
+++ b/test/common/mod.rs
@@ -14,19 +14,19 @@
 cfg_if! {
     if #[cfg(any(target_os = "android", target_os = "linux"))] {
         #[macro_export] macro_rules! require_capability {
-            ($capname:ident) => {
+            ($name:expr, $capname:ident) => {
                 use ::caps::{Capability, CapSet, has_cap};
 
                 if !has_cap(None, CapSet::Effective, Capability::$capname)
                     .unwrap()
                 {
-                    skip!("Insufficient capabilities. Skipping test.");
+                    skip!("{} requires capability {}. Skipping test.", $name, Capability::$capname);
                 }
             }
         }
     } else if #[cfg(not(target_os = "redox"))] {
         #[macro_export] macro_rules! require_capability {
-            ($capname:ident) => {}
+            ($name:expr, $capname:ident) => {}
         }
     }
 }
diff --git a/test/sys/mod.rs b/test/sys/mod.rs
index 4f5316f..e73d9b1 100644
--- a/test/sys/mod.rs
+++ b/test/sys/mod.rs
@@ -11,6 +11,8 @@
           target_os = "macos",
           target_os = "netbsd"))]
 mod test_aio;
+#[cfg(not(target_os = "redox"))]
+mod test_mman;
 #[cfg(target_os = "linux")]
 mod test_signalfd;
 #[cfg(not(target_os = "redox"))]
@@ -41,5 +43,5 @@
           target_os = "netbsd",
           target_os = "openbsd"))]
 mod test_ptrace;
-#[cfg(target_os = "linux")]
+#[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 3208410..80cd053 100644
--- a/test/sys/test_aio.rs
+++ b/test/sys/test_aio.rs
@@ -1,5 +1,5 @@
 use libc::{c_int, c_void};
-use nix::{Error, Result};
+use nix::Result;
 use nix::errno::*;
 use nix::sys::aio::*;
 use nix::sys::signal::{SaFlags, SigAction, sigaction, SigevNotify, SigHandler, Signal, SigSet};
@@ -16,7 +16,7 @@
 fn poll_aio(aiocb: &mut Pin<Box<AioCb>>) -> Result<()> {
     loop {
         let err = aiocb.error();
-        if err != Err(Error::from(Errno::EINPROGRESS)) { return err; };
+        if err != Err(Errno::EINPROGRESS) { return err; };
         thread::sleep(time::Duration::from_millis(10));
     }
 }
@@ -26,7 +26,7 @@
 fn poll_lio(liocb: &mut LioCb, i: usize) -> Result<()> {
     loop {
         let err = liocb.error(i);
-        if err != Err(Error::from(Errno::EINPROGRESS)) { return err; };
+        if err != Err(Errno::EINPROGRESS) { return err; };
         thread::sleep(time::Duration::from_millis(10));
     }
 }
@@ -70,7 +70,7 @@
                             LioOpcode::LIO_NOP);
     aiocb.write().unwrap();
     let err = aiocb.error();
-    assert!(err == Ok(()) || err == Err(Error::from(Errno::EINPROGRESS)));
+    assert!(err == Ok(()) || err == Err(Errno::EINPROGRESS));
 
     let cancelstat = aiocb.cancel();
     assert!(cancelstat.is_ok());
@@ -95,7 +95,7 @@
                             LioOpcode::LIO_NOP);
     aiocb.write().unwrap();
     let err = aiocb.error();
-    assert!(err == Ok(()) || err == Err(Error::from(Errno::EINPROGRESS)));
+    assert!(err == Ok(()) || err == Err(Errno::EINPROGRESS));
 
     let cancelstat = aio_cancel_all(f.as_raw_fd());
     assert!(cancelstat.is_ok());
@@ -182,8 +182,8 @@
                 Ok(_) => ()
             };
         }
-        if rcb.error() != Err(Error::from(Errno::EINPROGRESS)) &&
-           wcb.error() != Err(Error::from(Errno::EINPROGRESS)) {
+        if rcb.error() != Err(Errno::EINPROGRESS) &&
+           wcb.error() != Err(Errno::EINPROGRESS) {
             break
         }
     }
@@ -406,7 +406,7 @@
 #[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().expect("Mutex got poisoned by another test");
+    let _m = crate::SIGNAL_MTX.lock();
     let sa = SigAction::new(SigHandler::Handler(sigfunc),
                             SaFlags::SA_RESETHAND,
                             SigSet::empty());
@@ -544,7 +544,7 @@
 #[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().expect("Mutex got poisoned by another test");
+    let _m = crate::SIGNAL_MTX.lock();
     const INITIAL: &[u8] = b"abcdef123456";
     const WBUF: &[u8] = b"CDEF";
     let mut rbuf = vec![0; 4];
diff --git a/test/sys/test_epoll.rs b/test/sys/test_epoll.rs
index 57bc484..8d44cd0 100644
--- a/test/sys/test_epoll.rs
+++ b/test/sys/test_epoll.rs
@@ -1,6 +1,5 @@
 use nix::sys::epoll::{EpollCreateFlags, EpollFlags, EpollOp, EpollEvent};
 use nix::sys::epoll::{epoll_create1, epoll_ctl};
-use nix::Error;
 use nix::errno::Errno;
 
 #[test]
@@ -8,11 +7,11 @@
     let efd = epoll_create1(EpollCreateFlags::empty()).unwrap();
     let result = epoll_ctl(efd, EpollOp::EpollCtlDel, 1, None);
     assert!(result.is_err());
-    assert_eq!(result.unwrap_err(), Error::from(Errno::ENOENT));
+    assert_eq!(result.unwrap_err(), Errno::ENOENT);
 
     let result = epoll_ctl(efd, EpollOp::EpollCtlAdd, 1, None);
     assert!(result.is_err());
-    assert_eq!(result.unwrap_err(), Error::from(Errno::EINVAL));
+    assert_eq!(result.unwrap_err(), Errno::EINVAL);
 }
 
 #[test]
diff --git a/test/sys/test_inotify.rs b/test/sys/test_inotify.rs
index 121b726..137816a 100644
--- a/test/sys/test_inotify.rs
+++ b/test/sys/test_inotify.rs
@@ -1,7 +1,5 @@
 use nix::sys::inotify::{AddWatchFlags,InitFlags,Inotify};
-use nix::Error;
 use nix::errno::Errno;
-use tempfile;
 use std::ffi::OsString;
 use std::fs::{rename, File};
 
@@ -14,7 +12,7 @@
     instance.add_watch(tempdir.path(), AddWatchFlags::IN_ALL_EVENTS).unwrap();
 
     let events = instance.read_events();
-    assert_eq!(events.unwrap_err(), Error::from(Errno::EAGAIN));
+    assert_eq!(events.unwrap_err(), Errno::EAGAIN);
 
     File::create(tempdir.path().join("test")).unwrap();
 
@@ -31,7 +29,7 @@
     instance.add_watch(tempdir.path(), AddWatchFlags::IN_ALL_EVENTS).unwrap();
 
     let events = instance.read_events();
-    assert_eq!(events.unwrap_err(), Error::from(Errno::EAGAIN));
+    assert_eq!(events.unwrap_err(), Errno::EAGAIN);
 
     File::create(tempdir.path().join("test")).unwrap();
     rename(tempdir.path().join("test"), tempdir.path().join("test2")).unwrap();
diff --git a/test/sys/test_mman.rs b/test/sys/test_mman.rs
index 152fff6..a7ceedc 100644
--- a/test/sys/test_mman.rs
+++ b/test/sys/test_mman.rs
@@ -1,27 +1,24 @@
-use nix::Error;
-use nix::libc::{c_void, size_t};
 use nix::sys::mman::{mmap, MapFlags, ProtFlags};
 
-#[cfg(target_os = "linux")]
-use nix::sys::mman::{mremap, MRemapFlags};
-
 #[test]
 fn test_mmap_anonymous() {
-    let ref mut byte = unsafe {
+    unsafe {
         let ptr = mmap(std::ptr::null_mut(), 1,
                        ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
                        MapFlags::MAP_PRIVATE | MapFlags::MAP_ANONYMOUS, -1, 0)
-                      .unwrap();
-        *(ptr as * mut u8)
-    };
-    assert_eq !(*byte, 0x00u8);
-    *byte = 0xffu8;
-    assert_eq !(*byte, 0xffu8);
+                      .unwrap() as *mut u8;
+        assert_eq !(*ptr, 0x00u8);
+        *ptr = 0xffu8;
+        assert_eq !(*ptr, 0xffu8);
+    }
 }
 
 #[test]
-#[cfg(target_os = "linux")]
+#[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};
+
     const ONE_K : size_t = 1024;
     let slice : &mut[u8] = unsafe {
         let mem = mmap(std::ptr::null_mut(), ONE_K,
@@ -35,9 +32,14 @@
     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, 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)
     };
 
@@ -51,8 +53,13 @@
 }
 
 #[test]
-#[cfg(target_os = "linux")]
+#[cfg(any(target_os = "linux", target_os = "netbsd"))]
+// 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};
+
     const ONE_K : size_t = 1024;
     let slice : &mut[u8] = unsafe {
         let mem = mmap(std::ptr::null_mut(), 10 * ONE_K,
@@ -66,11 +73,16 @@
     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();
         // 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)
     };
diff --git a/test/sys/test_pthread.rs b/test/sys/test_pthread.rs
index 1fc3dd9..fa9b510 100644
--- a/test/sys/test_pthread.rs
+++ b/test/sys/test_pthread.rs
@@ -13,3 +13,10 @@
     let tid = pthread_self();
     assert!(tid != 0);
 }
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_pthread_kill_none() {
+    pthread_kill(pthread_self(), None)
+        .expect("Should be able to send signal to my thread.");
+}
diff --git a/test/sys/test_ptrace.rs b/test/sys/test_ptrace.rs
index 985945d..83fff9a 100644
--- a/test/sys/test_ptrace.rs
+++ b/test/sys/test_ptrace.rs
@@ -13,7 +13,7 @@
 fn test_ptrace() {
     // Just make sure ptrace can be called at all, for now.
     // FIXME: qemu-user doesn't implement ptrace on all arches, so permit ENOSYS
-    require_capability!(CAP_SYS_PTRACE);
+    require_capability!("test_ptrace", CAP_SYS_PTRACE);
     let err = ptrace::attach(getpid()).unwrap_err();
     assert!(err == Errno::EPERM || err == Errno::EINVAL ||
             err == Errno::ENOSYS);
@@ -23,7 +23,7 @@
 #[test]
 #[cfg(any(target_os = "android", target_os = "linux"))]
 fn test_ptrace_setoptions() {
-    require_capability!(CAP_SYS_PTRACE);
+    require_capability!("test_ptrace_setoptions", CAP_SYS_PTRACE);
     let err = ptrace::setoptions(getpid(), Options::PTRACE_O_TRACESYSGOOD).unwrap_err();
     assert!(err != Errno::EOPNOTSUPP);
 }
@@ -32,7 +32,7 @@
 #[test]
 #[cfg(any(target_os = "android", target_os = "linux"))]
 fn test_ptrace_getevent() {
-    require_capability!(CAP_SYS_PTRACE);
+    require_capability!("test_ptrace_getevent", CAP_SYS_PTRACE);
     let err = ptrace::getevent(getpid()).unwrap_err();
     assert!(err != Errno::EOPNOTSUPP);
 }
@@ -41,7 +41,7 @@
 #[test]
 #[cfg(any(target_os = "android", target_os = "linux"))]
 fn test_ptrace_getsiginfo() {
-    require_capability!(CAP_SYS_PTRACE);
+    require_capability!("test_ptrace_getsiginfo", CAP_SYS_PTRACE);
     if let Err(Errno::EOPNOTSUPP) = ptrace::getsiginfo(getpid()) {
         panic!("ptrace_getsiginfo returns Errno::EOPNOTSUPP!");
     }
@@ -51,7 +51,7 @@
 #[test]
 #[cfg(any(target_os = "android", target_os = "linux"))]
 fn test_ptrace_setsiginfo() {
-    require_capability!(CAP_SYS_PTRACE);
+    require_capability!("test_ptrace_setsiginfo", CAP_SYS_PTRACE);
     let siginfo = unsafe { mem::zeroed() };
     if let Err(Errno::EOPNOTSUPP) = ptrace::setsiginfo(getpid(), &siginfo) {
         panic!("ptrace_setsiginfo returns Errno::EOPNOTSUPP!");
@@ -67,9 +67,9 @@
     use nix::unistd::fork;
     use nix::unistd::ForkResult::*;
 
-    require_capability!(CAP_SYS_PTRACE);
+    require_capability!("test_ptrace_cont", CAP_SYS_PTRACE);
 
-    let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test");
+    let _m = crate::FORK_MTX.lock();
 
     // FIXME: qemu-user doesn't implement ptrace on all architectures
     // and retunrs ENOSYS in this case.
@@ -114,6 +114,48 @@
     }
 }
 
+#[cfg(target_os = "linux")]
+#[test]
+fn test_ptrace_interrupt() {
+    use nix::sys::ptrace;
+    use nix::sys::signal::Signal;
+    use nix::sys::wait::{waitpid, WaitPidFlag, WaitStatus};
+    use nix::unistd::fork;
+    use nix::unistd::ForkResult::*;
+    use std::thread::sleep;
+    use std::time::Duration;
+
+    require_capability!("test_ptrace_interrupt", CAP_SYS_PTRACE);
+
+    let _m = crate::FORK_MTX.lock();
+
+    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::interrupt(child).unwrap();
+            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)));
+            ptrace::detach(child, Some(Signal::SIGKILL)).unwrap();
+            match waitpid(child, None) {
+                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));
+                    }
+                }
+                _ => 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",
@@ -129,9 +171,9 @@
     use nix::unistd::getpid;
     use nix::unistd::ForkResult::*;
 
-    require_capability!(CAP_SYS_PTRACE);
+    require_capability!("test_ptrace_syscall", CAP_SYS_PTRACE);
 
-    let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test");
+    let _m = crate::FORK_MTX.lock();
 
     match unsafe{fork()}.expect("Error: Fork Failed") {
         Child => {
diff --git a/test/sys/test_select.rs b/test/sys/test_select.rs
index 3795108..2f7396b 100644
--- a/test/sys/test_select.rs
+++ b/test/sys/test_select.rs
@@ -5,9 +5,7 @@
 
 #[test]
 pub fn test_pselect() {
-    let _mtx = crate::SIGNAL_MTX
-        .lock()
-        .expect("Mutex got poisoned by another test");
+    let _mtx = crate::SIGNAL_MTX.lock();
 
     let (r1, w1) = pipe().unwrap();
     write(w1, b"hi!").unwrap();
@@ -52,3 +50,31 @@
     assert!(fd_set.contains(r1));
     assert!(!fd_set.contains(r2));
 }
+
+macro_rules! generate_fdset_bad_fd_tests {
+    ($fd:expr, $($method:ident),* $(,)?) => {
+        $(
+            #[test]
+            #[should_panic]
+            fn $method() {
+                FdSet::new().$method($fd);
+            }
+        )*
+    }
+}
+
+mod test_fdset_negative_fd {
+    use super::*;
+    generate_fdset_bad_fd_tests!(-1, insert, remove, contains);
+}
+
+mod test_fdset_too_large_fd {
+    use super::*;
+    use std::convert::TryInto;
+    generate_fdset_bad_fd_tests!(
+        FD_SETSIZE.try_into().unwrap(),
+        insert,
+        remove,
+        contains,
+    );
+}
diff --git a/test/sys/test_signal.rs b/test/sys/test_signal.rs
index 1b89af5..fdd2568 100644
--- a/test/sys/test_signal.rs
+++ b/test/sys/test_signal.rs
@@ -19,7 +19,7 @@
 
 #[test]
 fn test_old_sigaction_flags() {
-    let _m = crate::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test");
+    let _m = crate::SIGNAL_MTX.lock();
 
     extern "C" fn handler(_: ::libc::c_int) {}
     let act = SigAction::new(
@@ -41,7 +41,7 @@
 
 #[test]
 fn test_sigprocmask() {
-    let _m = crate::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test");
+    let _m = crate::SIGNAL_MTX.lock();
 
     // This needs to be a signal that rust doesn't use in the test harness.
     const SIGNAL: Signal = Signal::SIGCHLD;
@@ -89,7 +89,7 @@
 #[test]
 #[cfg(not(target_os = "redox"))]
 fn test_signal_sigaction() {
-    let _m = crate::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test");
+    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);
@@ -97,7 +97,7 @@
 
 #[test]
 fn test_signal() {
-    let _m = crate::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test");
+    let _m = crate::SIGNAL_MTX.lock();
 
     unsafe { signal(Signal::SIGINT, SigHandler::SigIgn) }.unwrap();
     raise(Signal::SIGINT).unwrap();
diff --git a/test/sys/test_signalfd.rs b/test/sys/test_signalfd.rs
index af04c22..b6f748b 100644
--- a/test/sys/test_signalfd.rs
+++ b/test/sys/test_signalfd.rs
@@ -6,7 +6,7 @@
     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().expect("Mutex got poisoned by another test");
+    let _m = crate::SIGNAL_MTX.lock();
 
     // Block the SIGUSR1 signal from automatic processing for this thread
     let mut mask = SigSet::empty();
diff --git a/test/sys/test_socket.rs b/test/sys/test_socket.rs
index 5471afe..0f6fac6 100644
--- a/test/sys/test_socket.rs
+++ b/test/sys/test_socket.rs
@@ -1,12 +1,13 @@
-use nix::sys::socket::{AddressFamily, InetAddr, UnixAddr, getsockname};
+use nix::sys::socket::{AddressFamily, InetAddr, SockAddr, UnixAddr, getsockname, sockaddr, sockaddr_in6, sockaddr_storage_to_addr};
 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::os::unix::io::RawFd;
 use std::path::Path;
 use std::slice;
 use std::str::FromStr;
-use libc::c_char;
+use libc::{c_char, sockaddr_storage};
 #[cfg(any(target_os = "linux", target_os= "android"))]
 use crate::*;
 
@@ -27,13 +28,36 @@
         _ => panic!("nope"),
     }
 
-    assert_eq!(addr.to_str(), "127.0.0.1:3000");
+    assert_eq!(addr.to_string(), "127.0.0.1:3000");
 
     let inet = addr.to_std();
     assert_eq!(actual, inet);
 }
 
 #[test]
+pub fn test_inetv4_addr_roundtrip_sockaddr_storage_to_addr() {
+    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);
+
+    let (storage, ffi_size) = {
+        let mut storage = MaybeUninit::<sockaddr_storage>::zeroed();
+        let storage_ptr = storage.as_mut_ptr().cast::<sockaddr>();
+        let (ffi_ptr, ffi_size) = sockaddr.as_ffi_pair();
+        assert_eq!(mem::size_of::<sockaddr>(), ffi_size as usize);
+        unsafe {
+            storage_ptr.copy_from_nonoverlapping(ffi_ptr as *const sockaddr, 1);
+            (storage.assume_init(), ffi_size)
+        }
+    };
+
+    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();
+    assert_eq!(from_storage, sockaddr);
+}
+
+#[test]
 pub fn test_inetv6_addr_to_sock_addr() {
     let port: u16 = 3000;
     let flowinfo: u32 = 1;
@@ -54,6 +78,33 @@
 
     assert_eq!(actual, addr.to_std());
 }
+#[test]
+pub fn test_inetv6_addr_roundtrip_sockaddr_storage_to_addr() {
+    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 addr = InetAddr::from_std(&actual);
+    let sockaddr = SockAddr::new_inet(addr);
+
+    let (storage, ffi_size) = {
+        let mut storage = MaybeUninit::<sockaddr_storage>::zeroed();
+        let storage_ptr = storage.as_mut_ptr().cast::<sockaddr_in6>();
+        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.assume_init(), ffi_size)
+        }
+    };
+
+    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();
+    assert_eq!(from_storage, sockaddr);
+}
 
 #[test]
 pub fn test_path_to_sock_addr() {
@@ -62,9 +113,9 @@
     let addr = UnixAddr::new(actual).unwrap();
 
     let expect: &[c_char] = unsafe {
-        slice::from_raw_parts(path.as_bytes().as_ptr() as *const c_char, path.len())
+        slice::from_raw_parts(path.as_ptr() as *const c_char, path.len())
     };
-    assert_eq!(&addr.0.sun_path[..8], expect);
+    assert_eq!(unsafe { &(*addr.as_ptr()).sun_path[..8] }, expect);
 
     assert_eq!(addr.path(), Some(actual));
 }
@@ -82,7 +133,7 @@
     let addr1 = UnixAddr::new(actual).unwrap();
     let mut addr2 = addr1;
 
-    addr2.0.sun_path[10] = 127;
+    unsafe { (*addr2.as_mut_ptr()).sun_path[10] = 127 };
 
     assert_eq!(addr1, addr2);
     assert_eq!(calculate_hash(&addr1), calculate_hash(&addr2));
@@ -101,12 +152,12 @@
 pub fn test_addr_equality_abstract() {
     let name = String::from("nix\0abstract\0test");
     let addr1 = UnixAddr::new_abstract(name.as_bytes()).unwrap();
-    let mut addr2 = addr1.clone();
+    let mut addr2 = addr1;
 
     assert_eq!(addr1, addr2);
     assert_eq!(calculate_hash(&addr1), calculate_hash(&addr2));
 
-    addr2.0.sun_path[17] = 127;
+    unsafe { (*addr2.as_mut_ptr()).sun_path[17] = 127 };
     assert_ne!(addr1, addr2);
     assert_ne!(calculate_hash(&addr1), calculate_hash(&addr2));
 }
@@ -129,7 +180,7 @@
     assert_eq!(addr.path(), None);
 
     // Internally, name is null-prefixed (abstract namespace)
-    assert_eq!(addr.0.sun_path[0], 0);
+    assert_eq!(unsafe { (*addr.as_ptr()).sun_path[0] }, 0);
 }
 
 #[test]
@@ -143,8 +194,7 @@
                .expect("socket failed");
     let sockaddr = SockAddr::new_unix(&sockname).unwrap();
     bind(sock, &sockaddr).expect("bind failed");
-    assert_eq!(sockaddr.to_str(),
-               getsockname(sock).expect("getsockname failed").to_str());
+    assert_eq!(sockaddr, getsockname(sock).expect("getsockname failed"));
 }
 
 #[test]
@@ -237,9 +287,9 @@
         use nix::sys::socket::sockopt::{UdpGroSegment, UdpGsoSegment};
 
         #[test]
-        // Disable the test on emulated platforms because it fails in Cirrus-CI.  Lack of QEMU
-        // support is suspected.
-        #[cfg_attr(not(any(target_arch = "x86_64", target_arch="i686")), ignore)]
+        // Disable the test under emulation because it fails in Cirrus-CI.  Lack
+        // of QEMU support is suspected.
+        #[cfg_attr(qemu, ignore)]
         pub fn gso() {
             require_kernel_version!(udp_offload::gso, ">= 4.18");
 
@@ -291,9 +341,9 @@
         }
 
         #[test]
-        // Disable the test on emulated platforms because it fails in Cirrus-CI.  Lack of QEMU
-        // support is suspected.
-        #[cfg_attr(not(any(target_arch = "x86_64", target_arch="i686")), ignore)]
+        // Disable the test on emulated platforms because it fails in Cirrus-CI.
+        // Lack of QEMU support is suspected.
+        #[cfg_attr(qemu, ignore)]
         pub fn gro() {
             require_kernel_version!(udp_offload::gro, ">= 5.3");
 
@@ -317,7 +367,6 @@
         target_os = "freebsd",
         target_os = "netbsd",
     ))]
-    #[allow(clippy::vec_init_then_push)]
     #[test]
     pub fn udp_sendmmsg() {
         use nix::sys::uio::IoVec;
@@ -344,14 +393,14 @@
 
         let from = sendrecv(rsock, ssock, move |s, m, flags| {
             let iov = [IoVec::from_slice(m)];
-            let mut msgs = Vec::new();
-            msgs.push(
+            let mut msgs = vec![
                 SendMmsgData {
                     iov: &iov,
                     cmsgs: &[],
                     addr: Some(sock_addr),
                     _lt: Default::default(),
-                });
+                }
+            ];
 
             let batch_size = 15;
 
@@ -532,7 +581,7 @@
 
 // Disable the test on emulated platforms due to a bug in QEMU versions <
 // 2.12.0.  https://bugs.launchpad.net/qemu/+bug/1701808
-#[cfg_attr(not(any(target_arch = "x86_64", target_arch="i686")), ignore)]
+#[cfg_attr(qemu, ignore)]
 #[test]
 pub fn test_scm_rights() {
     use nix::sys::uio::IoVec;
@@ -586,11 +635,10 @@
 }
 
 // Disable the test on emulated platforms due to not enabled support of AF_ALG in QEMU from rust cross
-#[cfg_attr(not(any(target_arch = "x86_64", target_arch = "i686")), ignore)]
 #[cfg(any(target_os = "linux", target_os= "android"))]
+#[cfg_attr(qemu, ignore)]
 #[test]
 pub fn test_af_alg_cipher() {
-    use libc;
     use nix::sys::uio::IoVec;
     use nix::unistd::read;
     use nix::sys::socket::{socket, sendmsg, bind, accept, setsockopt,
@@ -654,12 +702,14 @@
     assert_eq!(decrypted, payload);
 }
 
-// Disable the test on emulated platforms due to not enabled support of AF_ALG in QEMU from rust cross
-#[cfg_attr(not(any(target_arch = "x86_64", target_arch = "i686")), ignore)]
+// 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_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,
@@ -738,6 +788,11 @@
 
     // allocate buffer for decrypted data
     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");
     let num_bytes = read(session_socket, &mut decrypted).expect("read decrypt");
 
     assert!(num_bytes >= payload_len + (assoc_size as usize));
@@ -780,7 +835,7 @@
     if let InetAddr::V4(sin) = inet_addr {
         cfg_if! {
             if #[cfg(target_os = "netbsd")] {
-                drop(sin);
+                let _dontcare = sin;
                 let pi = libc::in_pktinfo {
                     ipi_ifindex: 0, /* Unspecified interface */
                     ipi_addr: libc::in_addr { s_addr: 0 },
@@ -859,7 +914,7 @@
 /// Tests that passing multiple fds using a single `ControlMessage` works.
 // Disable the test on emulated platforms due to a bug in QEMU versions <
 // 2.12.0.  https://bugs.launchpad.net/qemu/+bug/1701808
-#[cfg_attr(not(any(target_arch = "x86_64", target_arch="i686")), ignore)]
+#[cfg_attr(qemu, ignore)]
 #[test]
 fn test_scm_rights_single_cmsg_multiple_fds() {
     use std::os::unix::net::UnixDatagram;
@@ -868,7 +923,6 @@
     use nix::sys::socket::{ControlMessage, ControlMessageOwned, MsgFlags,
         sendmsg, recvmsg};
     use nix::sys::uio::IoVec;
-    use libc;
 
     let (send, receive) = UnixDatagram::pair().unwrap();
     let thread = thread::spawn(move || {
@@ -1006,13 +1060,11 @@
 /// Ensure that we can send `SCM_CREDENTIALS` and `SCM_RIGHTS` with a single
 /// `sendmsg` call.
 #[cfg(any(target_os = "android", target_os = "linux"))]
-// qemu's handling of multiple cmsgs is bugged, ignore tests on non-x86
+// qemu's handling of multiple cmsgs is bugged, ignore tests under emulation
 // see https://bugs.launchpad.net/qemu/+bug/1781280
-#[cfg_attr(not(any(target_arch = "x86_64", target_arch = "x86")), ignore)]
+#[cfg_attr(qemu, ignore)]
 #[test]
 fn test_scm_credentials_and_rights() {
-    use libc;
-
     let space = cmsg_space!(libc::ucred, RawFd);
     test_impl_scm_credentials_and_rights(space);
 }
@@ -1020,9 +1072,9 @@
 /// Ensure that passing a an oversized control message buffer to recvmsg
 /// still works.
 #[cfg(any(target_os = "android", target_os = "linux"))]
-// qemu's handling of multiple cmsgs is bugged, ignore tests on non-x86
+// qemu's handling of multiple cmsgs is bugged, ignore tests under emulation
 // see https://bugs.launchpad.net/qemu/+bug/1781280
-#[cfg_attr(not(any(target_arch = "x86_64", target_arch = "x86")), ignore)]
+#[cfg_attr(qemu, ignore)]
 #[test]
 fn test_too_large_cmsgspace() {
     let space = vec![0u8; 1024];
@@ -1169,7 +1221,6 @@
     use std::io;
     use std::io::Write;
     use nix::ifaddrs::getifaddrs;
-    use nix::sys::socket::SockAddr;
     use nix::net::if_::*;
 
     let addrs = match getifaddrs() {
@@ -1212,14 +1263,16 @@
     target_os = "netbsd",
 ))]
 // qemu doesn't seem to be emulating this correctly in these architectures
-#[cfg_attr(any(
-    target_arch = "mips",
-    target_arch = "mips64",
-    target_arch = "powerpc64",
+#[cfg_attr(all(
+    qemu,
+    any(
+        target_arch = "mips",
+        target_arch = "mips64",
+        target_arch = "powerpc64",
+    )
 ), ignore)]
 #[test]
 pub fn test_recv_ipv4pktinfo() {
-    use libc;
     use nix::sys::socket::sockopt::Ipv4PacketInfo;
     use nix::sys::socket::{bind, SockFlag, SockType};
     use nix::sys::socket::{getsockname, setsockopt, socket};
@@ -1272,18 +1325,15 @@
         );
 
         let mut cmsgs = msg.cmsgs();
-        match cmsgs.next() {
-            Some(ControlMessageOwned::Ipv4PacketInfo(pktinfo)) => {
-                let i = if_nametoindex(lo_name.as_bytes()).expect("if_nametoindex");
-                assert_eq!(
-                    pktinfo.ipi_ifindex as libc::c_uint,
-                    i,
-                    "unexpected ifindex (expected {}, got {})",
-                    i,
-                    pktinfo.ipi_ifindex
-                );
-            }
-            _ => (),
+        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,
+                "unexpected ifindex (expected {}, got {})",
+                i,
+                pktinfo.ipi_ifindex
+            );
         }
         assert!(cmsgs.next().is_none(), "unexpected additional control msg");
         assert_eq!(msg.bytes, 8);
@@ -1302,14 +1352,16 @@
     target_os = "openbsd",
 ))]
 // qemu doesn't seem to be emulating this correctly in these architectures
-#[cfg_attr(any(
-    target_arch = "mips",
-    target_arch = "mips64",
-    target_arch = "powerpc64",
+#[cfg_attr(all(
+    qemu,
+    any(
+        target_arch = "mips",
+        target_arch = "mips64",
+        target_arch = "powerpc64",
+    )
 ), ignore)]
 #[test]
 pub fn test_recvif() {
-    use libc;
     use nix::net::if_::*;
     use nix::sys::socket::sockopt::{Ipv4RecvIf, Ipv4RecvDstAddr};
     use nix::sys::socket::{bind, SockFlag, SockType};
@@ -1413,14 +1465,16 @@
     target_os = "openbsd",
 ))]
 // qemu doesn't seem to be emulating this correctly in these architectures
-#[cfg_attr(any(
-    target_arch = "mips",
-    target_arch = "mips64",
-    target_arch = "powerpc64",
+#[cfg_attr(all(
+    qemu,
+    any(
+        target_arch = "mips",
+        target_arch = "mips64",
+        target_arch = "powerpc64",
+    )
 ), ignore)]
 #[test]
 pub fn test_recv_ipv6pktinfo() {
-    use libc;
     use nix::net::if_::*;
     use nix::sys::socket::sockopt::Ipv6RecvPacketInfo;
     use nix::sys::socket::{bind, SockFlag, SockType};
@@ -1494,9 +1548,9 @@
 }
 
 #[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg_attr(graviton, ignore = "Not supported by the CI environment")]
 #[test]
 pub fn test_vsock() {
-    use libc;
     use nix::errno::Errno;
     use nix::sys::socket::{AddressFamily, socket, bind, connect, listen,
                            SockAddr, SockType, SockFlag};
@@ -1538,9 +1592,9 @@
     thr.join().unwrap();
 }
 
-// Disable the test on emulated platforms because it fails in Cirrus-CI.  Lack of QEMU
-// support is suspected.
-#[cfg_attr(not(any(target_arch = "x86_64")), ignore)]
+// Disable the test on emulated platforms because it fails in Cirrus-CI.  Lack
+// of QEMU support is suspected.
+#[cfg_attr(qemu, ignore)]
 #[cfg(all(target_os = "linux"))]
 #[test]
 fn test_recvmsg_timestampns() {
@@ -1589,9 +1643,9 @@
     nix::unistd::close(in_socket).unwrap();
 }
 
-// Disable the test on emulated platforms because it fails in Cirrus-CI.  Lack of QEMU
-// support is suspected.
-#[cfg_attr(not(any(target_arch = "x86_64")), ignore)]
+// Disable the test on emulated platforms because it fails in Cirrus-CI.  Lack
+// of QEMU support is suspected.
+#[cfg_attr(qemu, ignore)]
 #[cfg(all(target_os = "linux"))]
 #[test]
 fn test_recvmmsg_timestampns() {
@@ -1646,9 +1700,9 @@
     nix::unistd::close(in_socket).unwrap();
 }
 
-// Disable the test on emulated platforms because it fails in Cirrus-CI.  Lack of QEMU
-// support is suspected.
-#[cfg_attr(not(any(target_arch = "x86_64")), ignore)]
+// Disable the test on emulated platforms because it fails in Cirrus-CI.  Lack
+// of QEMU support is suspected.
+#[cfg_attr(qemu, ignore)]
 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
 #[test]
 fn test_recvmsg_rxq_ovfl() {
@@ -1728,3 +1782,160 @@
     nix::unistd::close(in_socket).unwrap();
     nix::unistd::close(out_socket).unwrap();
 }
+
+#[cfg(any(
+    target_os = "linux",
+    target_os = "android",
+))]
+mod linux_errqueue {
+    use nix::sys::socket::*;
+    use super::{FromStr, SocketAddr};
+
+    // Send a UDP datagram to a bogus destination address and observe an ICMP error (v4).
+    //
+    // Disable the test on QEMU because QEMU emulation of IP_RECVERR is broken (as documented on PR
+    // #1514).
+    #[cfg_attr(qemu, ignore)]
+    #[test]
+    fn test_recverr_v4() {
+        #[repr(u8)]
+        enum IcmpTypes {
+            DestUnreach = 3, // ICMP_DEST_UNREACH
+        }
+        #[repr(u8)]
+        enum IcmpUnreachCodes {
+            PortUnreach = 3, // ICMP_PORT_UNREACH
+        }
+
+        test_recverr_impl::<sockaddr_in, _, _>(
+            "127.0.0.1:6800",
+            AddressFamily::Inet,
+            sockopt::Ipv4RecvErr,
+            libc::SO_EE_ORIGIN_ICMP,
+            IcmpTypes::DestUnreach as u8,
+            IcmpUnreachCodes::PortUnreach as u8,
+            // 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 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_port, 0);
+                    } else {
+                        panic!("Expected some error origin");
+                    }
+                    *ext_err
+                } else {
+                    panic!("Unexpected control message {:?}", cmsg);
+                }
+            },
+        )
+    }
+
+    // Essentially the same test as v4.
+    //
+    // Disable the test on QEMU because QEMU emulation of IPV6_RECVERR is broken (as documented on
+    // PR #1514).
+    #[cfg_attr(qemu, ignore)]
+    #[test]
+    fn test_recverr_v6() {
+        #[repr(u8)]
+        enum IcmpV6Types {
+            DestUnreach = 1, // ICMPV6_DEST_UNREACH
+        }
+        #[repr(u8)]
+        enum IcmpV6UnreachCodes {
+            PortUnreach = 4, // ICMPV6_PORT_UNREACH
+        }
+
+        test_recverr_impl::<sockaddr_in6, _, _>(
+            "[::1]:6801",
+            AddressFamily::Inet6,
+            sockopt::Ipv6RecvErr,
+            libc::SO_EE_ORIGIN_ICMP6,
+            IcmpV6Types::DestUnreach as u8,
+            IcmpV6UnreachCodes::PortUnreach as u8,
+            // 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 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()),
+                        );
+                        assert_eq!(origin.sin6_port, 0);
+                    } else {
+                        panic!("Expected some error origin");
+                    }
+                    *ext_err
+                } else {
+                    panic!("Unexpected control message {:?}", cmsg);
+                }
+            },
+        )
+    }
+
+    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;
+
+        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();
+        setsockopt(sock, opt, &true).unwrap();
+        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 cspace = cmsg_space!(libc::sock_extended_err, SA);
+
+        let msg = recvmsg(sock, &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
+        // 127.0.0.1 (::1).
+        assert_eq!(msg.address, Some(sock_addr));
+
+        // Check for expected control message.
+        let ext_err = match msg.cmsgs().next() {
+            Some(cmsg) => testf(&cmsg),
+            None => panic!("No control message"),
+        };
+
+        assert_eq!(ext_err.ee_errno, libc::ECONNREFUSED as u32);
+        assert_eq!(ext_err.ee_origin, ee_origin);
+        // ip(7): ee_type and ee_code are set from the type and code fields of the ICMP (ICMPv6)
+        // header.
+        assert_eq!(ext_err.ee_type, ee_type);
+        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);
+    }
+}
diff --git a/test/sys/test_sockopt.rs b/test/sys/test_sockopt.rs
index e0ed0f7..01920fd 100644
--- a/test/sys/test_sockopt.rs
+++ b/test/sys/test_sockopt.rs
@@ -3,12 +3,53 @@
 #[cfg(any(target_os = "android", target_os = "linux"))]
 use crate::*;
 
+// NB: FreeBSD supports LOCAL_PEERCRED for SOCK_SEQPACKET, but OSX does not.
+#[cfg(any(
+        target_os = "dragonfly",
+        target_os = "freebsd",
+))]
+#[test]
+pub fn test_local_peercred_seqpacket() {
+    use nix::{
+        unistd::{Gid, Uid},
+        sys::socket::socketpair
+    };
+
+    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());
+    assert_eq!(Gid::from_raw(xucred.groups()[0]), Gid::current());
+}
+
+#[cfg(any(
+        target_os = "dragonfly",
+        target_os = "freebsd",
+        target_os = "macos",
+        target_os = "ios"
+))]
+#[test]
+pub fn test_local_peercred_stream() {
+    use nix::{
+        unistd::{Gid, Uid},
+        sys::socket::socketpair
+    };
+
+    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());
+    assert_eq!(Gid::from_raw(xucred.groups()[0]), Gid::current());
+}
+
 #[cfg(target_os = "linux")]
 #[test]
 fn is_so_mark_functional() {
     use nix::sys::socket::sockopt;
 
-    require_capability!(CAP_NET_ADMIN);
+    require_capability!("is_so_mark_functional", CAP_NET_ADMIN);
 
     let s = socket(AddressFamily::Inet, SockType::Stream, SockFlag::empty(), None).unwrap();
     setsockopt(s, sockopt::Mark, &1337).unwrap();
@@ -29,6 +70,57 @@
     assert!(actual >= bufsize);
 }
 
+#[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::unistd::{close, write};
+
+    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 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();
+    // Initial MSS is expected to be 536 (https://tools.ietf.org/html/rfc879#section-1) but some
+    // platforms keep it even lower. This might fail if you've tuned your initial MSS to be larger
+    // than 700
+    cfg_if! {
+        if #[cfg(any(target_os = "android", target_os = "linux"))] {
+            let segsize: u32 = 873;
+            assert!(initial < segsize);
+            setsockopt(rsock, sockopt::TcpMaxSeg, &segsize).unwrap();
+        } else {
+            assert!(initial < 700);
+        }
+    }
+
+    // Connect and check the MSS that was advertised
+    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();
+    let actual = getsockopt(ssock, sockopt::TcpMaxSeg).unwrap();
+    // Actual max segment size takes header lengths into account, max IPv4 options (60 bytes) + max
+    // TCP options (40 bytes) are subtracted from the requested maximum as a lower boundary.
+    cfg_if! {
+        if #[cfg(any(target_os = "android", target_os = "linux"))] {
+            assert!((segsize - 100) <= actual);
+            assert!(actual <= segsize);
+        } else {
+            assert!(initial < actual);
+            assert!(536 < actual);
+        }
+    }
+    close(rsock).unwrap();
+    close(ssock).unwrap();
+}
+
 // 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.
 // Current CI just run the binary with QEMU but the Kernel remains the same as the host.
@@ -94,3 +186,14 @@
         assert_eq!(getsockopt(fd, sockopt::TcpKeepInterval).unwrap(), x + 1);
     }
 }
+
+#[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();
+    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();
+    setsockopt(fd6, sockopt::Ipv6Ttl, &1)
+        .expect("setting ipv6ttl on an inet6 socket should succeed");
+}
diff --git a/test/sys/test_termios.rs b/test/sys/test_termios.rs
index 63d6a51..4a86154 100644
--- a/test/sys/test_termios.rs
+++ b/test/sys/test_termios.rs
@@ -19,7 +19,7 @@
 #[test]
 fn test_tcgetattr_pty() {
     // openpty uses ptname(3) internally
-    let _m = crate::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
+    let _m = crate::PTSNAME_MTX.lock();
 
     let pty = openpty(None, None).expect("openpty failed");
     assert!(termios::tcgetattr(pty.slave).is_ok());
@@ -46,7 +46,7 @@
 #[test]
 fn test_output_flags() {
     // openpty uses ptname(3) internally
-    let _m = crate::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
+    let _m = crate::PTSNAME_MTX.lock();
 
     // Open one pty to get attributes for the second one
     let mut termios = {
@@ -88,7 +88,7 @@
 #[test]
 fn test_local_flags() {
     // openpty uses ptname(3) internally
-    let _m = crate::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
+    let _m = crate::PTSNAME_MTX.lock();
 
     // Open one pty to get attributes for the second one
     let mut termios = {
diff --git a/test/sys/test_uio.rs b/test/sys/test_uio.rs
index 9dd4f01..c63b581 100644
--- a/test/sys/test_uio.rs
+++ b/test/sys/test_uio.rs
@@ -141,7 +141,7 @@
 }
 
 #[test]
-#[cfg(target_os = "linux")]
+#[cfg(not(target_os = "redox"))]
 fn test_pwritev() {
     use std::io::Read;
 
@@ -171,7 +171,7 @@
 }
 
 #[test]
-#[cfg(target_os = "linux")]
+#[cfg(not(target_os = "redox"))]
 fn test_preadv() {
     use std::io::Write;
 
@@ -205,16 +205,16 @@
 
 #[test]
 #[cfg(target_os = "linux")]
-// FIXME: qemu-user doesn't implement process_vm_readv/writev on most arches
-#[cfg_attr(not(any(target_arch = "x86", target_arch = "x86_64")), ignore)]
+// 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 nix::sys::signal::*;
     use nix::sys::wait::*;
     use crate::*;
 
-    require_capability!(CAP_SYS_PTRACE);
-    let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test");
+    require_capability!("test_process_vm_readv", CAP_SYS_PTRACE);
+    let _m = crate::FORK_MTX.lock();
 
     // Pre-allocate memory in the child, since allocation isn't safe
     // post-fork (~= async-signal-safe)
diff --git a/test/sys/test_wait.rs b/test/sys/test_wait.rs
index 2d26fb8..afe4f42 100644
--- a/test/sys/test_wait.rs
+++ b/test/sys/test_wait.rs
@@ -8,7 +8,7 @@
 #[test]
 #[cfg(not(target_os = "redox"))]
 fn test_wait_signal() {
-    let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test");
+    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") {
@@ -25,7 +25,7 @@
 
 #[test]
 fn test_wait_exit() {
-    let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test");
+    let _m = crate::FORK_MTX.lock();
 
     // Safe: Child only calls `_exit`, which is async-signal-safe.
     match unsafe{fork()}.expect("Error: Fork Failed") {
@@ -46,7 +46,7 @@
 
 #[test]
 fn test_waitstatus_pid() {
-    let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test");
+    let _m = crate::FORK_MTX.lock();
 
     match unsafe{fork()}.unwrap() {
         Child => unsafe { _exit(0) },
@@ -96,8 +96,8 @@
 
     #[test]
     fn test_wait_ptrace() {
-        require_capability!(CAP_SYS_PTRACE);
-        let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test");
+        require_capability!("test_wait_ptrace", CAP_SYS_PTRACE);
+        let _m = crate::FORK_MTX.lock();
 
         match unsafe{fork()}.expect("Error: Fork Failed") {
             Child => ptrace_child(),
diff --git a/test/test.rs b/test/test.rs
index 94f8e22..aade937 100644
--- a/test/test.rs
+++ b/test/test.rs
@@ -24,6 +24,7 @@
 #[cfg(not(target_os = "redox"))]
 mod test_net;
 mod test_nix_path;
+mod test_resource;
 mod test_poll;
 #[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
 mod test_pty;
@@ -42,7 +43,7 @@
 
 use std::os::unix::io::RawFd;
 use std::path::PathBuf;
-use std::sync::{Mutex, RwLock, RwLockWriteGuard};
+use parking_lot::{Mutex, RwLock, RwLockWriteGuard};
 use nix::unistd::{chdir, getcwd, read};
 
 
@@ -83,8 +84,7 @@
 
 impl<'a> DirRestore<'a> {
     fn new() -> Self {
-        let guard = crate::CWD_LOCK.write()
-            .expect("Lock got poisoned by another test");
+        let guard = crate::CWD_LOCK.write();
         DirRestore{
             _g: guard,
             d: getcwd().unwrap(),
diff --git a/test/test_dir.rs b/test/test_dir.rs
index 0dc7308..2940b6e 100644
--- a/test/test_dir.rs
+++ b/test/test_dir.rs
@@ -17,6 +17,7 @@
 }
 
 #[test]
+#[allow(clippy::unnecessary_sort_by)]   // False positive
 fn read() {
     let tmp = tempdir().unwrap();
     File::create(&tmp.path().join("foo")).unwrap();
diff --git a/test/test_fcntl.rs b/test/test_fcntl.rs
index ae6756e..db2acfb 100644
--- a/test/test_fcntl.rs
+++ b/test/test_fcntl.rs
@@ -28,8 +28,6 @@
 #[cfg(not(target_os = "redox"))]
 use std::os::unix::fs;
 
-use crate::*;
-
 #[test]
 #[cfg(not(target_os = "redox"))]
 fn test_openat() {
@@ -129,14 +127,14 @@
     let old_path = old_dir.path().join("old");
     {
         let mut old_f = File::create(&old_path).unwrap();
-        old_f.write(b"old").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_path = new_dir.path().join("new");
     {
         let mut new_f = File::create(&new_path).unwrap();
-        new_f.write(b"new").unwrap();
+        new_f.write_all(b"new").unwrap();
     }
     renameat2(
         Some(old_dirfd),
@@ -238,14 +236,8 @@
     /// The from_offset should be updated by the call to reflect
     /// the 3 bytes read (6).
     #[test]
-    // QEMU does not support copy_file_range. Skip platforms that use QEMU in CI
-    #[cfg_attr(all(target_os = "linux", any(
-            target_arch = "aarch64",
-            target_arch = "arm",
-            target_arch = "mips",
-            target_arch = "mips64",
-            target_arch = "powerpc64"
-    )), ignore)]
+    // QEMU does not support copy_file_range. Skip under qemu
+    #[cfg_attr(qemu, ignore)]
     fn test_copy_file_range() {
         const CONTENTS: &[u8] = b"foobarbaz";
 
@@ -327,9 +319,10 @@
 
         let buf1 = b"abcdef";
         let buf2 = b"defghi";
-        let mut iovecs = Vec::with_capacity(2);
-        iovecs.push(IoVec::from_slice(&buf1[0..3]));
-        iovecs.push(IoVec::from_slice(&buf2[0..3]));
+        let iovecs = vec![
+            IoVec::from_slice(&buf1[0..3]),
+            IoVec::from_slice(&buf2[0..3])
+        ];
 
         let res = vmsplice(wr, &iovecs[..], SpliceFFlags::empty()).unwrap();
 
@@ -480,17 +473,16 @@
     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).unwrap();
+        let res = posix_fadvise(fd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED);
 
-        assert_eq!(res, 0);
+        assert!(res.is_ok());
     }
 
     #[test]
     fn test_errno() {
         let (rd, _wr) = pipe().unwrap();
-        let errno = posix_fadvise(rd as RawFd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED)
-                                 .unwrap();
-        assert_eq!(errno, Errno::ESPIPE as i32);
+        let res = posix_fadvise(rd as RawFd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED);
+        assert_eq!(res, Err(Errno::ESPIPE));
     }
 }
 
diff --git a/test/test_kmod/mod.rs b/test/test_kmod/mod.rs
index 7626330..8eef538 100644
--- a/test/test_kmod/mod.rs
+++ b/test/test_kmod/mod.rs
@@ -5,9 +5,7 @@
 use crate::*;
 
 fn compile_kernel_module() -> (PathBuf, String, TempDir) {
-    let _m = crate::FORK_MTX
-        .lock()
-        .expect("Mutex got poisoned by another test");
+    let _m = crate::FORK_MTX.lock();
 
     let tmp_dir = tempdir().expect("unable to create temporary build directory");
 
@@ -34,16 +32,15 @@
 use nix::errno::Errno;
 use nix::kmod::{delete_module, DeleteModuleFlags};
 use nix::kmod::{finit_module, init_module, ModuleInitFlags};
-use nix::Error;
 use std::ffi::CString;
 use std::fs::File;
 use std::io::Read;
 
 #[test]
 fn test_finit_and_delete_module() {
-    require_capability!(CAP_SYS_MODULE);
-    let _m0 = crate::KMOD_MTX.lock().expect("Mutex got poisoned by another test");
-    let _m1 = crate::CWD_LOCK.read().expect("Mutex got poisoned by another test");
+    require_capability!("test_finit_and_delete_module", CAP_SYS_MODULE);
+    let _m0 = crate::KMOD_MTX.lock();
+    let _m1 = crate::CWD_LOCK.read();
 
     let (kmod_path, kmod_name, _kmod_dir) = compile_kernel_module();
 
@@ -58,10 +55,10 @@
 }
 
 #[test]
-fn test_finit_and_delete_modul_with_params() {
-    require_capability!(CAP_SYS_MODULE);
-    let _m0 = crate::KMOD_MTX.lock().expect("Mutex got poisoned by another test");
-    let _m1 = crate::CWD_LOCK.read().expect("Mutex got poisoned by another test");
+fn test_finit_and_delete_module_with_params() {
+    require_capability!("test_finit_and_delete_module_with_params", CAP_SYS_MODULE);
+    let _m0 = crate::KMOD_MTX.lock();
+    let _m1 = crate::CWD_LOCK.read();
 
     let (kmod_path, kmod_name, _kmod_dir) = compile_kernel_module();
 
@@ -80,9 +77,9 @@
 
 #[test]
 fn test_init_and_delete_module() {
-    require_capability!(CAP_SYS_MODULE);
-    let _m0 = crate::KMOD_MTX.lock().expect("Mutex got poisoned by another test");
-    let _m1 = crate::CWD_LOCK.read().expect("Mutex got poisoned by another test");
+    require_capability!("test_init_and_delete_module", CAP_SYS_MODULE);
+    let _m0 = crate::KMOD_MTX.lock();
+    let _m1 = crate::CWD_LOCK.read();
 
     let (kmod_path, kmod_name, _kmod_dir) = compile_kernel_module();
 
@@ -90,7 +87,7 @@
     let mut contents: Vec<u8> = Vec::new();
     f.read_to_end(&mut contents)
         .expect("unable to read kernel module content to buffer");
-    init_module(&mut 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(),
@@ -100,9 +97,9 @@
 
 #[test]
 fn test_init_and_delete_module_with_params() {
-    require_capability!(CAP_SYS_MODULE);
-    let _m0 = crate::KMOD_MTX.lock().expect("Mutex got poisoned by another test");
-    let _m1 = crate::CWD_LOCK.read().expect("Mutex got poisoned by another test");
+    require_capability!("test_init_and_delete_module_with_params", CAP_SYS_MODULE);
+    let _m0 = crate::KMOD_MTX.lock();
+    let _m1 = crate::CWD_LOCK.read();
 
     let (kmod_path, kmod_name, _kmod_dir) = compile_kernel_module();
 
@@ -110,7 +107,7 @@
     let mut contents: Vec<u8> = Vec::new();
     f.read_to_end(&mut contents)
         .expect("unable to read kernel module content to buffer");
-    init_module(&mut contents, &CString::new("who=Nix number=2015").unwrap())
+    init_module(&contents, &CString::new("who=Nix number=2015").unwrap())
         .expect("unable to load kernel module");
 
     delete_module(
@@ -121,23 +118,23 @@
 
 #[test]
 fn test_finit_module_invalid() {
-    require_capability!(CAP_SYS_MODULE);
-    let _m0 = crate::KMOD_MTX.lock().expect("Mutex got poisoned by another test");
-    let _m1 = crate::CWD_LOCK.read().expect("Mutex got poisoned by another test");
+    require_capability!("test_finit_module_invalid", CAP_SYS_MODULE);
+    let _m0 = crate::KMOD_MTX.lock();
+    let _m1 = crate::CWD_LOCK.read();
 
     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());
 
-    assert_eq!(result.unwrap_err(), Error::from(Errno::EINVAL));
+    assert_eq!(result.unwrap_err(), Errno::EINVAL);
 }
 
 #[test]
 fn test_finit_module_twice_and_delete_module() {
-    require_capability!(CAP_SYS_MODULE);
-    let _m0 = crate::KMOD_MTX.lock().expect("Mutex got poisoned by another test");
-    let _m1 = crate::CWD_LOCK.read().expect("Mutex got poisoned by another test");
+    require_capability!("test_finit_module_twice_and_delete_module", CAP_SYS_MODULE);
+    let _m0 = crate::KMOD_MTX.lock();
+    let _m1 = crate::CWD_LOCK.read();
 
     let (kmod_path, kmod_name, _kmod_dir) = compile_kernel_module();
 
@@ -147,7 +144,7 @@
 
     let result = finit_module(&f, &CString::new("").unwrap(), ModuleInitFlags::empty());
 
-    assert_eq!(result.unwrap_err(), Error::from(Errno::EEXIST));
+    assert_eq!(result.unwrap_err(), Errno::EEXIST);
 
     delete_module(
         &CString::new(kmod_name).unwrap(),
@@ -157,11 +154,11 @@
 
 #[test]
 fn test_delete_module_not_loaded() {
-    require_capability!(CAP_SYS_MODULE);
-    let _m0 = crate::KMOD_MTX.lock().expect("Mutex got poisoned by another test");
-    let _m1 = crate::CWD_LOCK.read().expect("Mutex got poisoned by another test");
+    require_capability!("test_delete_module_not_loaded", CAP_SYS_MODULE);
+    let _m0 = crate::KMOD_MTX.lock();
+    let _m1 = crate::CWD_LOCK.read();
 
     let result = delete_module(&CString::new("hello").unwrap(), DeleteModuleFlags::empty());
 
-    assert_eq!(result.unwrap_err(), Error::from(Errno::ENOENT));
+    assert_eq!(result.unwrap_err(), Errno::ENOENT);
 }
diff --git a/test/test_mount.rs b/test/test_mount.rs
index c1b6c8a..44287f9 100644
--- a/test/test_mount.rs
+++ b/test/test_mount.rs
@@ -21,14 +21,13 @@
     use nix::sys::stat::{self, Mode};
     use nix::unistd::getuid;
 
-    use tempfile;
-
-    static SCRIPT_CONTENTS: &'static [u8] = b"#!/bin/sh
+    static SCRIPT_CONTENTS: &[u8] = b"#!/bin/sh
 exit 23";
 
     const EXPECTED_STATUS: i32 = 23;
 
     const NONE: Option<&'static [u8]> = None;
+    #[allow(clippy::bind_instead_of_map)]   // False positive
     pub fn test_mount_tmpfs_without_flags_allows_rwx() {
         let tempdir = tempfile::tempdir().unwrap();
 
diff --git a/test/test_mq.rs b/test/test_mq.rs
index d082692..430df5d 100644
--- a/test/test_mq.rs
+++ b/test/test_mq.rs
@@ -60,7 +60,11 @@
 // FIXME: Fix failures for mips in QEMU
 #[test]
 #[cfg(not(any(target_os = "netbsd")))]
-#[cfg_attr(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;
@@ -97,7 +101,11 @@
 // FIXME: Fix failures for mips in QEMU
 #[test]
 #[cfg(not(any(target_os = "netbsd")))]
-#[cfg_attr(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};
     const MSG_SIZE: mq_attr_member_t = 32;
diff --git a/test/test_poll.rs b/test/test_poll.rs
index 0395512..e4b369f 100644
--- a/test/test_poll.rs
+++ b/test/test_poll.rs
@@ -64,3 +64,19 @@
     assert_eq!(nfds, 1);
     assert!(fds[0].revents().unwrap().contains(PollFlags::POLLIN));
 }
+
+#[test]
+fn test_pollfd_fd() {
+    use std::os::unix::io::AsRawFd;
+
+    let pfd = PollFd::new(0x1234, PollFlags::empty());
+    assert_eq!(pfd.as_raw_fd(), 0x1234);
+}
+
+#[test]
+fn test_pollfd_events() {
+    let mut pfd = PollFd::new(-1, PollFlags::POLLIN);
+    assert_eq!(pfd.events(), PollFlags::POLLIN);
+    pfd.set_events(PollFlags::POLLOUT);
+    assert_eq!(pfd.events(), PollFlags::POLLOUT);
+}
diff --git a/test/test_pty.rs b/test/test_pty.rs
index 52b6334..71932f2 100644
--- a/test/test_pty.rs
+++ b/test/test_pty.rs
@@ -29,7 +29,7 @@
 #[test]
 #[cfg(any(target_os = "android", target_os = "linux"))]
 fn test_ptsname_equivalence() {
-    let _m = crate::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
+    let _m = crate::PTSNAME_MTX.lock();
 
     // Open a new PTTY master
     let master_fd = posix_openpt(OFlag::O_RDWR).unwrap();
@@ -46,7 +46,7 @@
 #[test]
 #[cfg(any(target_os = "android", target_os = "linux"))]
 fn test_ptsname_copy() {
-    let _m = crate::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
+    let _m = crate::PTSNAME_MTX.lock();
 
     // Open a new PTTY master
     let master_fd = posix_openpt(OFlag::O_RDWR).unwrap();
@@ -80,7 +80,7 @@
 #[test]
 #[cfg(any(target_os = "android", target_os = "linux"))]
 fn test_ptsname_unique() {
-    let _m = crate::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
+    let _m = crate::PTSNAME_MTX.lock();
 
     // Open a new PTTY master
     let master1_fd = posix_openpt(OFlag::O_RDWR).unwrap();
@@ -98,7 +98,7 @@
 
 /// Common setup for testing PTTY pairs
 fn open_ptty_pair() -> (PtyMaster, File) {
-    let _m = crate::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
+    let _m = crate::PTSNAME_MTX.lock();
 
     // Open a new PTTY master
     let master = posix_openpt(OFlag::O_RDWR).expect("posix_openpt failed");
@@ -114,6 +114,8 @@
     let slave_fd = open(Path::new(&slave_name), OFlag::O_RDWR, stat::Mode::empty()).unwrap();
 
     #[cfg(target_os = "illumos")]
+    // TODO: rewrite using ioctl!
+    #[allow(clippy::comparison_chain)]
     {
         use libc::{ioctl, I_FIND, I_PUSH};
 
@@ -185,7 +187,7 @@
 #[test]
 fn test_openpty() {
     // openpty uses ptname(3) internally
-    let _m = crate::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
+    let _m = crate::PTSNAME_MTX.lock();
 
     let pty = openpty(None, None).unwrap();
     assert!(pty.master > 0);
@@ -220,7 +222,7 @@
 #[test]
 fn test_openpty_with_termios() {
     // openpty uses ptname(3) internally
-    let _m = crate::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
+    let _m = crate::PTSNAME_MTX.lock();
 
     // Open one pty to get attributes for the second one
     let mut termios = {
@@ -271,9 +273,9 @@
     use nix::sys::signal::*;
     use nix::sys::wait::wait;
     // forkpty calls openpty which uses ptname(3) internally.
-    let _m0 = crate::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
+    let _m0 = crate::PTSNAME_MTX.lock();
     // forkpty spawns a child process
-    let _m1 = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test");
+    let _m1 = crate::FORK_MTX.lock();
 
     let string = "naninani\n";
     let echoed_string = "naninani\r\n";
diff --git a/test/test_resource.rs b/test/test_resource.rs
new file mode 100644
index 0000000..5969750
--- /dev/null
+++ b/test/test_resource.rs
@@ -0,0 +1,23 @@
+#[cfg(not(any(target_os = "redox", target_os = "fuchsia", target_os = "illumos")))]
+use nix::sys::resource::{getrlimit, setrlimit, Resource};
+
+/// Tests the RLIMIT_NOFILE functionality of getrlimit(), where the resource RLIMIT_NOFILE refers
+/// to the maximum file descriptor number that can be opened by the process (aka the maximum number
+/// of file descriptors that the process can open, since Linux 4.5).
+///
+/// We first fetch the existing file descriptor maximum values using getrlimit(), then edit the
+/// soft limit to make sure it has a new and distinct value to the hard limit. We then setrlimit()
+/// 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")))]
+pub fn test_resource_limits_nofile() {
+    let (soft_limit, hard_limit) = getrlimit(Resource::RLIMIT_NOFILE).unwrap();
+
+    let soft_limit = Some(soft_limit.map_or(1024, |v| v - 1));
+    assert_ne!(soft_limit, hard_limit);
+    setrlimit(Resource::RLIMIT_NOFILE, soft_limit, hard_limit).unwrap();
+
+    let (new_soft_limit, _) = getrlimit(Resource::RLIMIT_NOFILE).unwrap();
+    assert_eq!(new_soft_limit, soft_limit);
+}
diff --git a/test/test_stat.rs b/test/test_stat.rs
index 424371f..33cf748 100644
--- a/test/test_stat.rs
+++ b/test/test_stat.rs
@@ -11,7 +11,6 @@
 
 #[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
 use libc::{S_IFMT, S_IFLNK};
-#[cfg(not(target_os = "redox"))]
 use libc::mode_t;
 
 #[cfg(not(target_os = "redox"))]
@@ -60,6 +59,7 @@
 #[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
 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
@@ -306,3 +306,53 @@
     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")))]
+fn test_mknod() {
+    use stat::{lstat, mknod, SFlag};
+
+    let file_name = "test_file";
+    let tempdir = tempfile::tempdir().unwrap();
+    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);
+}
+
+#[test]
+#[cfg(not(any(target_os = "freebsd",
+              target_os = "illumos",
+              target_os = "ios",
+              target_os = "macos",
+              target_os = "redox")))]
+fn test_mknodat() {
+    use fcntl::{AtFlags, OFlag};
+    use nix::dir::Dir;
+    use stat::{fstatat, mknodat, SFlag};
+
+    let file_name = "test_file";
+    let tempdir = tempfile::tempdir().unwrap();
+    let target_dir = Dir::open(tempdir.path(), OFlag::O_DIRECTORY, Mode::S_IRWXU).unwrap();
+    mknodat(
+        target_dir.as_raw_fd(),
+        file_name,
+        SFlag::S_IFREG,
+        Mode::S_IRWXU,
+        0,
+    )
+    .unwrap();
+    let mode = fstatat(
+        target_dir.as_raw_fd(),
+        file_name,
+        AtFlags::AT_SYMLINK_NOFOLLOW,
+    )
+    .unwrap()
+    .st_mode as mode_t;
+    assert!(mode & libc::S_IFREG == libc::S_IFREG);
+    assert!(mode & libc::S_IRWXU == libc::S_IRWXU);
+}
diff --git a/test/test_time.rs b/test/test_time.rs
index c321352..dc307e5 100644
--- a/test/test_time.rs
+++ b/test/test_time.rs
@@ -6,11 +6,12 @@
     target_os = "emscripten",
 ))]
 use nix::time::clock_getcpuclockid;
-use nix::time::{clock_getres, clock_gettime, ClockId};
+use nix::time::{clock_gettime, ClockId};
 
+#[cfg(not(target_os = "redox"))]
 #[test]
 pub fn test_clock_getres() {
-    assert!(clock_getres(ClockId::CLOCK_REALTIME).is_ok());
+    assert!(nix::time::clock_getres(ClockId::CLOCK_REALTIME).is_ok());
 }
 
 #[test]
@@ -31,6 +32,7 @@
     assert!(clock_gettime(clock_id).is_ok());
 }
 
+#[cfg(not(target_os = "redox"))]
 #[test]
 pub fn test_clock_id_res() {
     assert!(ClockId::CLOCK_REALTIME.res().is_ok());
diff --git a/test/test_unistd.rs b/test/test_unistd.rs
index b95f154..61062ad 100644
--- a/test/test_unistd.rs
+++ b/test/test_unistd.rs
@@ -10,7 +10,7 @@
 #[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
 use nix::pty::{posix_openpt, grantpt, unlockpt, ptsname};
 use nix::errno::Errno;
-use std::{env, iter};
+use std::env;
 #[cfg(not(any(target_os = "fuchsia", target_os = "redox")))]
 use std::ffi::CString;
 #[cfg(not(target_os = "redox"))]
@@ -28,7 +28,7 @@
 #[test]
 #[cfg(not(any(target_os = "netbsd")))]
 fn test_fork_and_waitpid() {
-    let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test");
+    let _m = crate::FORK_MTX.lock();
 
     // Safe: Child only calls `_exit`, which is signal-safe
     match unsafe{fork()}.expect("Error: Fork Failed") {
@@ -56,7 +56,7 @@
 #[test]
 fn test_wait() {
     // Grab FORK_MTX so wait doesn't reap a different test's child process
-    let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test");
+    let _m = crate::FORK_MTX.lock();
 
     // Safe: Child only calls `_exit`, which is signal-safe
     match unsafe{fork()}.expect("Error: Fork Failed") {
@@ -116,7 +116,7 @@
     target_os = "macos", target_os = "ios",
     target_os = "android", target_os = "redox")))]
 fn test_mkfifoat_none() {
-    let _m = crate::CWD_LOCK.read().expect("Mutex got poisoned by another test");
+    let _m = crate::CWD_LOCK.read();
 
     let tempdir = tempdir().unwrap();
     let mkfifoat_fifo = tempdir.path().join("mkfifoat_fifo");
@@ -151,10 +151,10 @@
     target_os = "macos", target_os = "ios",
     target_os = "android", target_os = "redox")))]
 fn test_mkfifoat_directory_none() {
-    let _m = crate::CWD_LOCK.read().expect("Mutex got poisoned by another test");
+    let _m = crate::CWD_LOCK.read();
 
     // mkfifoat should fail if a directory is given
-    assert!(!mkfifoat(None, &env::temp_dir(), Mode::S_IRUSR).is_ok());
+    assert!(mkfifoat(None, &env::temp_dir(), Mode::S_IRUSR).is_err());
 }
 
 #[test]
@@ -168,7 +168,7 @@
     let mkfifoat_dir = "mkfifoat_dir";
     stat::mkdirat(dirfd, mkfifoat_dir, Mode::S_IRUSR).unwrap();
 
-    assert!(!mkfifoat(Some(dirfd), mkfifoat_dir, Mode::S_IRUSR).is_ok());
+    assert!(mkfifoat(Some(dirfd), mkfifoat_dir, Mode::S_IRUSR).is_err());
 }
 
 #[test]
@@ -206,7 +206,7 @@
     // Skip this test when not run as root as `setgroups()` requires root.
     skip_if_not_root!("test_setgroups");
 
-    let _m = crate::GROUPS_MTX.lock().expect("Mutex got poisoned by another test");
+    let _m = crate::GROUPS_MTX.lock();
 
     // Save the existing groups
     let old_groups = getgroups().unwrap();
@@ -234,7 +234,7 @@
     // require root.
     skip_if_not_root!("test_initgroups");
 
-    let _m = crate::GROUPS_MTX.lock().expect("Mutex got poisoned by another test");
+    let _m = crate::GROUPS_MTX.lock();
 
     // Save the existing groups
     let old_groups = getgroups().unwrap();
@@ -304,7 +304,7 @@
             skip_if_seccomp!($test_name);
         }
 
-        let m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test");
+        let m = crate::FORK_MTX.lock();
         // The `exec`d process will write to `writer`, and we'll read that
         // data from `reader`.
         let (reader, writer) = pipe().unwrap();
@@ -443,7 +443,7 @@
     // 4096 on linux, 1024 on macos)
     let mut inner_tmp_dir = tmpdir_path;
     for _ in 0..5 {
-        let newdir = iter::repeat("a").take(100).collect::<String>();
+        let newdir = "a".repeat(100);
         inner_tmp_dir.push(newdir);
         assert!(mkdir(inner_tmp_dir.as_path(), Mode::S_IRWXU).is_ok());
     }
@@ -549,7 +549,7 @@
     if #[cfg(any(target_os = "android", target_os = "linux"))] {
         macro_rules! require_acct{
             () => {
-                require_capability!(CAP_SYS_PACCT);
+                require_capability!("test_acct", CAP_SYS_PACCT);
             }
         }
     } else if #[cfg(target_os = "freebsd")] {
@@ -575,7 +575,7 @@
     use std::process::Command;
     use std::{thread, time};
 
-    let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test");
+    let _m = crate::FORK_MTX.lock();
     require_acct!();
 
     let file = NamedTempFile::new().unwrap();
@@ -735,7 +735,7 @@
     };
 
     // Maybe other tests that fork interfere with this one?
-    let _m = crate::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test");
+    let _m = crate::SIGNAL_MTX.lock();
 
     let handler = SigHandler::Handler(alarm_signal_handler);
     let signal_action = SigAction::new(handler, SaFlags::SA_RESTART, SigSet::empty());
@@ -773,7 +773,7 @@
 #[test]
 #[cfg(not(target_os = "redox"))]
 fn test_canceling_alarm() {
-    let _m = crate::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test");
+    let _m = crate::SIGNAL_MTX.lock();
 
     assert_eq!(alarm::cancel(), None);
 
@@ -784,7 +784,7 @@
 #[test]
 #[cfg(not(target_os = "redox"))]
 fn test_symlinkat() {
-    let _m = crate::CWD_LOCK.read().expect("Mutex got poisoned by another test");
+    let _m = crate::CWD_LOCK.read();
 
     let tempdir = tempdir().unwrap();
 
@@ -883,7 +883,7 @@
 #[test]
 #[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox")))]
 fn test_linkat_no_follow_symlink() {
-    let _m = crate::CWD_LOCK.read().expect("Mutex got poisoned by another test");
+    let _m = crate::CWD_LOCK.read();
 
     let tempdir = tempdir().unwrap();
     let oldfilename = "foo.txt";
@@ -920,7 +920,7 @@
 #[test]
 #[cfg(not(target_os = "redox"))]
 fn test_linkat_follow_symlink() {
-    let _m = crate::CWD_LOCK.read().expect("Mutex got poisoned by another test");
+    let _m = crate::CWD_LOCK.read();
 
     let tempdir = tempdir().unwrap();
     let oldfilename = "foo.txt";
@@ -1025,13 +1025,22 @@
     assert!(access(&path, AccessFlags::R_OK | AccessFlags::W_OK).is_ok());
 }
 
+#[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();
+    let pwd: libc::passwd = nobody.into();
+    let _: User = (&pwd).into();
+}
+
 /// Tests setting the filesystem UID with `setfsuid`.
 #[cfg(any(target_os = "linux", target_os = "android"))]
 #[test]
 fn test_setfsuid() {
     use std::os::unix::fs::PermissionsExt;
     use std::{fs, io, thread};
-    require_capability!(CAP_SETUID);
+    require_capability!("test_setfsuid", CAP_SETUID);
 
     // get the UID of the "nobody" user
     let nobody = User::from_name("nobody").unwrap().unwrap();
@@ -1042,7 +1051,7 @@
     dbg!(&temp_path);
     let temp_path_2 = (&temp_path).to_path_buf();
     let mut permissions = fs::metadata(&temp_path).unwrap().permissions();
-    permissions.set_mode(640);
+    permissions.set_mode(0o640);
 
     // spawn a new thread where to test setfsuid
     thread::spawn(move || {