aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-05-10 07:01:46 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-05-10 07:01:46 +0000
commit2cf7b893b8e6de214a0ca1802deaa408c4d76d19 (patch)
treefb2abcd53c7bec252defd92f2b61bb3ddff8949d
parentfdb16c9d501c4b6752f13c07c14d288e077a55f0 (diff)
parent70ec7dceffbc665db8853a3b099f15eb84590c74 (diff)
downloadnix-aml_per_331913010.tar.gz
Change-Id: Ib24f785b0dcb518e15b71d419467229e4d744f1b
-rw-r--r--.cargo_vcs_info.json7
-rw-r--r--.gitattributes1
-rw-r--r--Android.bp12
-rw-r--r--CHANGELOG.md211
-rw-r--r--CONTRIBUTING.md114
-rw-r--r--CONVENTIONS.md86
-rw-r--r--Cargo.toml33
-rw-r--r--Cargo.toml.orig29
-rw-r--r--METADATA10
-rw-r--r--README.md25
-rw-r--r--TEST_MAPPING90
-rw-r--r--cargo2android.json8
-rw-r--r--patches/0001-Allow-android-compiled-binaries-to-use-timerfd.patch26
-rw-r--r--src/dir.rs20
-rw-r--r--src/env.rs26
-rw-r--r--src/errno.rs578
-rw-r--r--src/fcntl.rs78
-rw-r--r--src/features.rs25
-rw-r--r--src/kmod.rs9
-rw-r--r--src/lib.rs121
-rw-r--r--src/macros.rs139
-rw-r--r--src/mount/bsd.rs426
-rw-r--r--src/mount/linux.rs (renamed from src/mount.rs)2
-rw-r--r--src/mount/mod.rs21
-rw-r--r--src/mqueue.rs19
-rw-r--r--src/net/if_.rs236
-rw-r--r--src/poll.rs33
-rw-r--r--src/pty.rs80
-rw-r--r--src/sched.rs79
-rw-r--r--src/sys/aio.rs798
-rw-r--r--src/sys/epoll.rs4
-rw-r--r--src/sys/event.rs26
-rw-r--r--src/sys/eventfd.rs1
-rw-r--r--src/sys/inotify.rs32
-rw-r--r--src/sys/ioctl/bsd.rs7
-rw-r--r--src/sys/ioctl/mod.rs4
-rw-r--r--src/sys/memfd.rs29
-rw-r--r--src/sys/mman.rs114
-rw-r--r--src/sys/mod.rs22
-rw-r--r--src/sys/personality.rs4
-rw-r--r--src/sys/pthread.rs27
-rw-r--r--src/sys/ptrace/bsd.rs17
-rw-r--r--src/sys/ptrace/linux.rs51
-rw-r--r--src/sys/quota.rs2
-rw-r--r--src/sys/reboot.rs6
-rw-r--r--src/sys/resource.rs233
-rw-r--r--src/sys/select.rs38
-rw-r--r--src/sys/sendfile.rs30
-rw-r--r--src/sys/signal.rs256
-rw-r--r--src/sys/signalfd.rs29
-rw-r--r--src/sys/socket/addr.rs281
-rw-r--r--src/sys/socket/mod.rs283
-rw-r--r--src/sys/socket/sockopt.rs429
-rw-r--r--src/sys/stat.rs44
-rw-r--r--src/sys/statfs.rs54
-rw-r--r--src/sys/statvfs.rs6
-rw-r--r--src/sys/sysinfo.rs2
-rw-r--r--src/sys/termios.rs184
-rw-r--r--src/sys/time.rs26
-rw-r--r--src/sys/timerfd.rs22
-rw-r--r--src/sys/uio.rs51
-rw-r--r--src/sys/utsname.rs8
-rw-r--r--src/sys/wait.rs24
-rw-r--r--src/time.rs14
-rw-r--r--src/ucontext.rs1
-rw-r--r--src/unistd.rs595
-rw-r--r--test/common/mod.rs24
-rw-r--r--test/sys/mod.rs2
-rw-r--r--test/sys/test_aio.rs210
-rw-r--r--test/sys/test_aio_drop.rs1
-rw-r--r--test/sys/test_epoll.rs5
-rw-r--r--test/sys/test_inotify.rs6
-rw-r--r--test/sys/test_ioctl.rs48
-rw-r--r--test/sys/test_lio_listio_resubmit.rs27
-rw-r--r--test/sys/test_mman.rs40
-rw-r--r--test/sys/test_pthread.rs7
-rw-r--r--test/sys/test_ptrace.rs79
-rw-r--r--test/sys/test_select.rs32
-rw-r--r--test/sys/test_signal.rs30
-rw-r--r--test/sys/test_signalfd.rs2
-rw-r--r--test/sys/test_socket.rs612
-rw-r--r--test/sys/test_sockopt.rs109
-rw-r--r--test/sys/test_termios.rs14
-rw-r--r--test/sys/test_uio.rs28
-rw-r--r--test/sys/test_wait.rs14
-rw-r--r--test/test.rs8
-rw-r--r--test/test_dir.rs21
-rw-r--r--test/test_fcntl.rs243
-rw-r--r--test/test_kmod/mod.rs59
-rw-r--r--test/test_mount.rs5
-rw-r--r--test/test_mq.rs29
-rw-r--r--test/test_net.rs2
-rw-r--r--test/test_nmount.rs51
-rw-r--r--test/test_poll.rs21
-rw-r--r--test/test_pty.rs44
-rw-r--r--test/test_ptymaster_drop.rs4
-rw-r--r--test/test_resource.rs23
-rw-r--r--test/test_sendfile.rs22
-rw-r--r--test/test_stat.rs83
-rw-r--r--test/test_time.rs6
-rw-r--r--test/test_unistd.rs120
101 files changed, 5900 insertions, 2429 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index 82a8727..90eb8fa 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,5 +1,6 @@
{
"git": {
- "sha1": "4c7021787a174493bf1abb90a711d7464e6c80f6"
- }
-}
+ "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 50fe7cb..88940a1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -25,20 +25,22 @@ rust_library {
name: "libnix",
host_supported: true,
crate_name: "nix",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.23.1",
srcs: ["src/lib.rs"],
edition: "2018",
rustlibs: [
"libbitflags",
"libcfg_if",
"liblibc",
+ "libmemoffset",
],
apex_available: [
"//apex_available:platform",
+ "com.android.bluetooth",
+ "com.android.compos",
"com.android.virt",
],
+ vendor_available: true,
+ min_sdk_version: "29",
}
-
-// dependent_library ["feature_list"]
-// bitflags-1.2.1 "default"
-// cfg-if-1.0.0
-// libc-0.2.94 "default,extra_traits,std"
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1297ba7..77d5b2a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,216 @@
# Change Log
All notable changes to this project will be documented in this file.
-This project adheres to [Semantic Versioning](http://semver.org/).
+This project adheres to [Semantic Versioning](https://semver.org/).
+
+## [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
+
+- 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
+- Added `if_nameindex` (#[1445](https://github.com/nix-rust/nix/pull/1445))
+- Added `nmount` for FreeBSD.
+ (#[1453](https://github.com/nix-rust/nix/pull/1453))
+- Added `IpFreebind` socket option (sockopt) on Linux, Fuchsia and Android.
+ (#[1456](https://github.com/nix-rust/nix/pull/1456))
+- Added `TcpUserTimeout` socket option (sockopt) on Linux and Fuchsia.
+ (#[1457](https://github.com/nix-rust/nix/pull/1457))
+- Added `renameat2` for Linux
+ (#[1458](https://github.com/nix-rust/nix/pull/1458))
+- Added `RxqOvfl` support on Linux, Fuchsia and Android.
+ (#[1455](https://github.com/nix-rust/nix/pull/1455))
+
+### Changed
+- `ptsname_r` now returns a lossily-converted string in the event of bad UTF,
+ just like `ptsname`.
+ ([#1446](https://github.com/nix-rust/nix/pull/1446))
+- Nix's error type is now a simple wrapper around the platform's Errno. This
+ means it is now `Into<std::io::Error>`. It's also `Clone`, `Copy`, `Eq`, and
+ has a small fixed size. It also requires less typing. For example, the old
+ enum variant `nix::Error::Sys(nix::errno::Errno::EINVAL)` is now simply
+ `nix::Error::EINVAL`.
+ ([#1446](https://github.com/nix-rust/nix/pull/1446))
+
+### Fixed
+### Removed
+
+## [0.21.0] - 31 May 2021
+### Added
+- Added `getresuid` and `getresgid`
+ (#[1430](https://github.com/nix-rust/nix/pull/1430))
+- Added TIMESTAMPNS support for linux
+ (#[1402](https://github.com/nix-rust/nix/pull/1402))
+- Added `sendfile64` (#[1439](https://github.com/nix-rust/nix/pull/1439))
+- Added `MS_LAZYTIME` to `MsFlags`
+ (#[1437](https://github.com/nix-rust/nix/pull/1437))
+
+### Changed
+- Made `forkpty` unsafe, like `fork`
+ (#[1390](https://github.com/nix-rust/nix/pull/1390))
+- Made `Uid`, `Gid` and `Pid` methods `from_raw` and `as_raw` a `const fn`
+ (#[1429](https://github.com/nix-rust/nix/pull/1429))
+- Made `Uid::is_root` a `const fn`
+ (#[1429](https://github.com/nix-rust/nix/pull/1429))
+- `AioCb` is now always pinned. Once a `libc::aiocb` gets sent to the kernel,
+ its address in memory must not change. Nix now enforces that by using
+ `std::pin`. Most users won't need to change anything, except when using
+ `aio_suspend`. See that method's documentation for the new usage.
+ (#[1440](https://github.com/nix-rust/nix/pull/1440))
+- `LioCb` is now constructed using a distinct `LioCbBuilder` struct. This
+ avoids a soundness issue with the old `LioCb`. Usage is similar but
+ construction now uses the builder pattern. See the documentation for
+ details.
+ (#[1440](https://github.com/nix-rust/nix/pull/1440))
+- Minimum supported Rust version is now 1.41.0.
+ ([#1440](https://github.com/nix-rust/nix/pull/1440))
+- Errno aliases are now associated consts on `Errno`, instead of consts in the
+ `errno` module.
+ (#[1452](https://github.com/nix-rust/nix/pull/1452))
+
+### Fixed
+- Allow `sockaddr_ll` size, as reported by the Linux kernel, to be smaller then it's definition
+ (#[1395](https://github.com/nix-rust/nix/pull/1395))
+- Fix spurious errors using `sendmmsg` with multiple cmsgs
+ (#[1414](https://github.com/nix-rust/nix/pull/1414))
+- Added `Errno::EOPNOTSUPP` to FreeBSD, where it was missing.
+ (#[1452](https://github.com/nix-rust/nix/pull/1452))
+
+### Removed
+
+- Removed `sys::socket::accept4` from Android arm because libc removed it in
+ version 0.2.87.
+ ([#1399](https://github.com/nix-rust/nix/pull/1399))
+- `AioCb::from_boxed_slice` and `AioCb::from_boxed_mut_slice` have been
+ removed. They were useful with earlier versions of Rust, but should no
+ longer be needed now that async/await are available. `AioCb`s now work
+ exclusively with borrowed buffers, not owned ones.
+ (#[1440](https://github.com/nix-rust/nix/pull/1440))
+- Removed some Errno values from platforms where they aren't actually defined.
+ (#[1452](https://github.com/nix-rust/nix/pull/1452))
## [0.20.0] - 20 February 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 0562246..122c1af 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,25 +3,25 @@
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
-# to registry (e.g., crates.io) dependencies
+# to registry (e.g., crates.io) dependencies.
#
-# If you believe there's an error in this file please file an
-# issue against the rust-lang/cargo repository. If you're
-# editing this file be aware that the upstream Cargo.toml
-# will likely look very different (and much more reasonable)
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
[package]
edition = "2018"
+rust-version = "1.46"
name = "nix"
-version = "0.20.0"
+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"
repository = "https://github.com/nix-rust/nix"
[package.metadata.docs.rs]
-targets = ["x86_64-unknown-linux-gnu", "aarch64-linux-android", "x86_64-apple-darwin", "aarch64-apple-ios", "x86_64-unknown-freebsd", "x86_64-unknown-openbsd", "x86_64-unknown-netbsd", "x86_64-unknown-dragonfly", "x86_64-fuchsia", "x86_64-unknown-redox"]
+targets = ["x86_64-unknown-linux-gnu", "aarch64-linux-android", "x86_64-apple-darwin", "aarch64-apple-ios", "x86_64-unknown-freebsd", "x86_64-unknown-openbsd", "x86_64-unknown-netbsd", "x86_64-unknown-dragonfly", "x86_64-fuchsia", "x86_64-unknown-redox", "x86_64-unknown-illumos"]
[[test]]
name = "test"
@@ -54,24 +54,29 @@ version = "1.1"
version = "1.0"
[dependencies.libc]
-version = "0.2.82"
+version = "0.2.102"
features = ["extra_traits"]
-[dev-dependencies.bytes]
-version = "0.4.8"
+[dev-dependencies.assert-impl]
+version = "0.1"
[dev-dependencies.lazy_static]
version = "1.2"
+[dev-dependencies.parking_lot]
+version = "0.11.2"
+
[dev-dependencies.rand]
-version = "0.6"
+version = "0.8"
[dev-dependencies.semver]
-version = "0.9.0"
+version = "1.0.0"
[dev-dependencies.tempfile]
-version = "3.0.5"
+version = "3.2.0"
[target."cfg(any(target_os = \"android\", target_os = \"linux\"))".dev-dependencies.caps]
version = "0.5.1"
+[target."cfg(not(target_os = \"redox\"))".dependencies.memoffset]
+version = "0.6.3"
[target."cfg(target_os = \"dragonfly\")".build-dependencies.cc]
version = "1"
[target."cfg(target_os = \"freebsd\")".dev-dependencies.sysctl]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index 885fa10..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.20.0"
+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 = [
@@ -27,23 +21,28 @@ targets = [
"x86_64-unknown-netbsd",
"x86_64-unknown-dragonfly",
"x86_64-fuchsia",
- "x86_64-unknown-redox"
+ "x86_64-unknown-redox",
+ "x86_64-unknown-illumos"
]
[dependencies]
-libc = { version = "0.2.82", features = [ "extra_traits" ] }
+libc = { version = "0.2.102", features = [ "extra_traits" ] }
bitflags = "1.1"
cfg-if = "1.0"
+[target.'cfg(not(target_os = "redox"))'.dependencies]
+memoffset = "0.6.3"
+
[target.'cfg(target_os = "dragonfly")'.build-dependencies]
cc = "1"
[dev-dependencies]
-bytes = "0.4.8"
+assert-impl = "0.1"
lazy_static = "1.2"
-rand = "0.6"
-tempfile = "3.0.5"
-semver = "0.9.0"
+parking_lot = "0.11.2"
+rand = "0.8"
+tempfile = "3.2.0"
+semver = "1.0.0"
[target.'cfg(any(target_os = "android", target_os = "linux"))'.dev-dependencies]
caps = "0.5.1"
diff --git a/METADATA b/METADATA
index a71ef07..e0fef6c 100644
--- a/METADATA
+++ b/METADATA
@@ -7,13 +7,13 @@ third_party {
}
url {
type: ARCHIVE
- value: "https://static.crates.io/crates/nix/nix-0.20.0.crate"
+ value: "https://static.crates.io/crates/nix/nix-0.23.1.crate"
}
- version: "0.20.0"
+ version: "0.23.1"
license_type: NOTICE
last_upgrade_date {
- year: 2021
- month: 4
- day: 2
+ year: 2022
+ month: 3
+ day: 16
}
}
diff --git a/README.md b/README.md
index 167d192..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](http://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/)
@@ -17,7 +17,7 @@ usage.
As an example of what Nix provides, examine the differences between what is
exposed by libc and nix for the
-[gethostname](http://man7.org/linux/man-pages/man2/gethostname.2.html) system
+[gethostname](https://man7.org/linux/man-pages/man2/gethostname.2.html) system
call:
```rust,ignore
@@ -68,33 +68,26 @@ Tier 2:
* 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
-
-`nix` requires Rust 1.40.0 or newer.
-
-To use `nix`, add this to your `Cargo.toml`:
+## Minimum Supported Rust Version (MSRV)
-```toml
-[dependencies]
-nix = "0.20.0"
-```
+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/TEST_MAPPING b/TEST_MAPPING
index 5d7fef5..b44b9e2 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -1,14 +1,102 @@
// Generated by update_crate_tests.py for tests that depend on this crate.
{
+ "imports": [
+ {
+ "path": "external/rust/crates/tokio"
+ }
+ ],
"presubmit": [
{
+ "name": "ZipFuseTest"
+ },
+ {
+ "name": "apkdmverity.test"
+ },
+ {
+ "name": "authfs_device_test_src_lib"
+ },
+ {
+ "name": "diced_open_dice_cbor_test"
+ },
+ {
+ "name": "diced_sample_inputs_test"
+ },
+ {
+ "name": "diced_test"
+ },
+ {
+ "name": "diced_utils_test"
+ },
+ {
+ "name": "diced_vendor_test"
+ },
+ {
+ "name": "keystore2_crypto_test_rust"
+ },
+ {
+ "name": "keystore2_selinux_concurrency_test"
+ },
+ {
"name": "keystore2_test"
},
{
+ "name": "keystore2_test_utils_test"
+ },
+ {
+ "name": "legacykeystore_test"
+ },
+ {
+ "name": "microdroid_manager_test"
+ },
+ {
+ "name": "virtualizationservice_device_test"
+ }
+ ],
+ "presubmit-rust": [
+ {
+ "name": "ZipFuseTest"
+ },
+ {
+ "name": "apkdmverity.test"
+ },
+ {
+ "name": "authfs_device_test_src_lib"
+ },
+ {
+ "name": "diced_open_dice_cbor_test"
+ },
+ {
+ "name": "diced_sample_inputs_test"
+ },
+ {
+ "name": "diced_test"
+ },
+ {
+ "name": "diced_utils_test"
+ },
+ {
+ "name": "diced_vendor_test"
+ },
+ {
"name": "keystore2_crypto_test_rust"
},
{
- "name": "vpnprofilestore_test"
+ "name": "keystore2_selinux_concurrency_test"
+ },
+ {
+ "name": "keystore2_test"
+ },
+ {
+ "name": "keystore2_test_utils_test"
+ },
+ {
+ "name": "legacykeystore_test"
+ },
+ {
+ "name": "microdroid_manager_test"
+ },
+ {
+ "name": "virtualizationservice_device_test"
}
]
}
diff --git a/cargo2android.json b/cargo2android.json
index 42b7833..f8fb37c 100644
--- a/cargo2android.json
+++ b/cargo2android.json
@@ -1,9 +1,13 @@
{
"apex-available": [
"//apex_available:platform",
+ "com.android.bluetooth",
+ "com.android.compos",
"com.android.virt"
],
"dependencies": true,
"device": true,
- "run": true
-} \ No newline at end of file
+ "min-sdk-version": "29",
+ "run": true,
+ "vendor-available": true
+}
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 7d4ab82..ed70a45 100644
--- a/src/dir.rs
+++ b/src/dir.rs
@@ -53,14 +53,12 @@ impl Dir {
/// Converts from a file descriptor, closing it on success or failure.
pub fn from_fd(fd: RawFd) -> Result<Self> {
- let d = unsafe { libc::fdopendir(fd) };
- if d.is_null() {
+ let d = ptr::NonNull::new(unsafe { libc::fdopendir(fd) }).ok_or_else(|| {
let e = Error::last();
unsafe { libc::close(fd) };
- return Err(e);
- };
- // Always guaranteed to be non-null by the previous check
- Ok(Dir(ptr::NonNull::new(d).unwrap()))
+ e
+ })?;
+ Ok(Dir(d))
}
/// Returns an iterator of `Result<Entry>` which rewinds when finished.
@@ -86,7 +84,7 @@ impl AsRawFd for Dir {
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::Sys(Errno::EBADF)) {
+ if !std::thread::panicking() && e == Err(Errno::EBADF) {
panic!("Closing an invalid file descriptor!");
};
}
@@ -192,6 +190,7 @@ impl Entry {
target_os = "emscripten",
target_os = "fuchsia",
target_os = "haiku",
+ target_os = "illumos",
target_os = "ios",
target_os = "l4re",
target_os = "linux",
@@ -206,11 +205,13 @@ impl Entry {
target_os = "emscripten",
target_os = "fuchsia",
target_os = "haiku",
+ target_os = "illumos",
target_os = "ios",
target_os = "l4re",
target_os = "linux",
target_os = "macos",
target_os = "solaris")))]
+ #[allow(clippy::useless_conversion)] // Not useless on all OSes
pub fn ino(&self) -> u64 {
u64::from(self.0.d_fileno)
}
@@ -226,6 +227,7 @@ impl Entry {
/// notably, some Linux filesystems don't implement this. The caller should use `stat` or
/// `fstat` if this returns `None`.
pub fn file_type(&self) -> Option<Type> {
+ #[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
match self.0.d_type {
libc::DT_FIFO => Some(Type::Fifo),
libc::DT_CHR => Some(Type::CharacterDevice),
@@ -236,5 +238,9 @@ impl Entry {
libc::DT_SOCK => Some(Type::Socket),
/* libc::DT_UNKNOWN | */ _ => None,
}
+
+ // illumos and Solaris systems do not have the d_type member at all:
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ None
}
}
diff --git a/src/env.rs b/src/env.rs
index f144dfe..bcae287 100644
--- a/src/env.rs
+++ b/src/env.rs
@@ -1,12 +1,25 @@
+//! Environment variables
use cfg_if::cfg_if;
-use crate::{Error, Result};
+use std::fmt;
+
+/// Indicates that [`clearenv`] failed for some unknown reason
+#[derive(Clone, Copy, Debug)]
+pub struct ClearEnvError;
+
+impl fmt::Display for ClearEnvError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "clearenv failed")
+ }
+}
+
+impl std::error::Error for ClearEnvError {}
/// Clear the environment of all name-value pairs.
///
/// On platforms where libc provides `clearenv()`, it will be used. libc's
/// `clearenv()` is documented to return an error code but not set errno; if the
/// return value indicates a failure, this function will return
-/// `Error::UnsupportedOperation`.
+/// [`ClearEnvError`].
///
/// On platforms where libc does not provide `clearenv()`, a fallback
/// implementation will be used that iterates over all environment variables and
@@ -25,8 +38,7 @@ use crate::{Error, Result};
/// `environ` is currently held. The latter is not an issue if the only other
/// environment access in the program is via `std::env`, but the requirement on
/// thread safety must still be upheld.
-pub unsafe fn clearenv() -> Result<()> {
- let ret;
+pub unsafe fn clearenv() -> std::result::Result<(), ClearEnvError> {
cfg_if! {
if #[cfg(any(target_os = "fuchsia",
target_os = "wasi",
@@ -35,19 +47,19 @@ pub unsafe fn clearenv() -> Result<()> {
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;
}
}
if ret == 0 {
Ok(())
} else {
- Err(Error::UnsupportedOperation)
+ Err(ClearEnvError)
}
}
diff --git a/src/errno.rs b/src/errno.rs
index e5c7092..3da246e 100644
--- a/src/errno.rs
+++ b/src/errno.rs
@@ -1,5 +1,6 @@
use cfg_if::cfg_if;
use libc::{c_int, c_void};
+use std::convert::TryFrom;
use std::{fmt, io, error};
use crate::{Error, Result};
@@ -25,6 +26,10 @@ cfg_if! {
unsafe fn errno_location() -> *mut c_int {
libc::__errno_location()
}
+ } else if #[cfg(any(target_os = "illumos", target_os = "solaris"))] {
+ unsafe fn errno_location() -> *mut c_int {
+ libc::___errno()
+ }
}
}
@@ -44,6 +49,43 @@ pub fn errno() -> i32 {
}
impl Errno {
+ /// Convert this `Error` to an [`Errno`](enum.Errno.html).
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # use nix::Error;
+ /// # use nix::errno::Errno;
+ /// let e = Error::from(Errno::EPERM);
+ /// assert_eq!(Some(Errno::EPERM), e.as_errno());
+ /// ```
+ #[deprecated(
+ since = "0.22.0",
+ note = "It's a no-op now; just delete it."
+ )]
+ pub const fn as_errno(self) -> Option<Self> {
+ Some(self)
+ }
+
+ /// Create a nix Error from a given errno
+ #[deprecated(
+ since = "0.22.0",
+ note = "It's a no-op now; just delete it."
+ )]
+ #[allow(clippy::wrong_self_convention)] // False positive
+ pub fn from_errno(errno: Errno) -> Error {
+ errno
+ }
+
+ /// Create a new invalid argument error (`EINVAL`)
+ #[deprecated(
+ since = "0.22.0",
+ note = "Use Errno::EINVAL instead"
+ )]
+ pub const fn invalid_argument() -> Error {
+ Errno::EINVAL
+ }
+
pub fn last() -> Self {
last()
}
@@ -52,7 +94,7 @@ impl Errno {
desc(self)
}
- pub fn from_i32(err: i32) -> Errno {
+ pub const fn from_i32(err: i32) -> Errno {
from_i32(err)
}
@@ -62,13 +104,29 @@ impl Errno {
/// 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(Error::Sys(Self::last()))
+ Err(Self::last())
} else {
Ok(value)
}
}
+
+ /// Backwards compatibility hack for Nix <= 0.21.0 users
+ ///
+ /// In older versions of Nix, `Error::Sys` was an enum variant. Now it's a
+ /// function, which is compatible with most of the former use cases of the
+ /// enum variant. But you should use `Error(Errno::...)` instead.
+ #[deprecated(
+ since = "0.22.0",
+ note = "Use Errno::... instead"
+ )]
+ #[allow(non_snake_case)]
+ #[inline]
+ pub const fn Sys(errno: Errno) -> Error {
+ errno
+ }
}
/// The sentinel value indicates that a function failed and more detailed
@@ -90,7 +148,7 @@ impl ErrnoSentinel for i64 {
}
impl ErrnoSentinel for *mut c_void {
- fn sentinel() -> Self { (-1 as isize) as *mut c_void }
+ fn sentinel() -> Self { -1isize as *mut c_void }
}
impl ErrnoSentinel for libc::sighandler_t {
@@ -111,6 +169,16 @@ impl From<Errno> for io::Error {
}
}
+impl TryFrom<io::Error> for Errno {
+ type Error = io::Error;
+
+ fn try_from(ioerror: io::Error) -> std::result::Result<Self, io::Error> {
+ ioerror.raw_os_error()
+ .map(Errno::from_i32)
+ .ok_or(ioerror)
+ }
+}
+
fn last() -> Errno {
Errno::from_i32(errno())
}
@@ -190,114 +258,142 @@ fn desc(errno: Errno) -> &'static str {
EHOSTUNREACH => "No route to host",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
ECHRNG => "Channel number out of range",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
EL2NSYNC => "Level 2 not synchronized",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
EL3HLT => "Level 3 halted",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
EL3RST => "Level 3 reset",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
ELNRNG => "Link number out of range",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
EUNATCH => "Protocol driver not attached",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
ENOCSI => "No CSI structure available",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
EL2HLT => "Level 2 halted",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
EBADE => "Invalid exchange",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
EBADR => "Invalid request descriptor",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
EXFULL => "Exchange full",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
ENOANO => "No anode",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
EBADRQC => "Invalid request code",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
EBADSLT => "Invalid slot",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
EBFONT => "Bad font file format",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
ENOSTR => "Device not a stream",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
ENODATA => "No data available",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
ETIME => "Timer expired",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
ENOSR => "Out of streams resources",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
ENONET => "Machine is not on the network",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
ENOPKG => "Package not installed",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
EREMOTE => "Object is remote",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
ENOLINK => "Link has been severed",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
EADV => "Advertise error",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
ESRMNT => "Srmount error",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
ECOMM => "Communication error on send",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
EPROTO => "Protocol error",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
EMULTIHOP => "Multihop attempted",
@@ -309,55 +405,70 @@ fn desc(errno: Errno) -> &'static str {
target_os = "fuchsia"))]
EBADMSG => "Not a data message",
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ EBADMSG => "Trying to read unreadable message",
+
#[cfg(any(target_os = "linux", target_os = "android",
target_os = "fuchsia"))]
EOVERFLOW => "Value too large for defined data type",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
ENOTUNIQ => "Name not unique on network",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
EBADFD => "File descriptor in bad state",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
EREMCHG => "Remote address changed",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
ELIBACC => "Can not access a needed shared library",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
ELIBBAD => "Accessing a corrupted shared library",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
ELIBSCN => ".lib section in a.out corrupted",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
ELIBMAX => "Attempting to link in too many shared libraries",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
ELIBEXEC => "Cannot exec a shared library directly",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia", target_os = "openbsd"))]
EILSEQ => "Illegal byte sequence",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
ERESTART => "Interrupted system call should be restarted",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
ESTRPIPE => "Streams pipe error",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
EUSERS => "Too many users",
@@ -404,6 +515,7 @@ fn desc(errno: Errno) -> &'static str {
EMEDIUMTYPE => "Wrong medium type",
#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "illumos", target_os = "solaris",
target_os = "fuchsia"))]
ECANCELED => "Operation canceled",
@@ -427,10 +539,16 @@ fn desc(errno: Errno) -> &'static str {
target_os = "fuchsia"))]
EOWNERDEAD => "Owner died",
+ #[cfg(any( target_os = "illumos", target_os = "solaris"))]
+ EOWNERDEAD => "Process died with lock",
+
#[cfg(any(target_os = "linux", target_os = "android",
target_os = "fuchsia"))]
ENOTRECOVERABLE => "State not recoverable",
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ ENOTRECOVERABLE => "Lock is not recoverable",
+
#[cfg(any(all(target_os = "linux", not(target_arch="mips")),
target_os = "fuchsia"))]
ERFKILL => "Operation not possible due to RF-kill",
@@ -445,7 +563,8 @@ fn desc(errno: Errno) -> &'static str {
#[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "redox"))]
EMULTIHOP => "Multihop attempted",
- #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "redox"))]
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly",
+ target_os = "redox"))]
ENOLINK => "Link has been severed",
#[cfg(target_os = "freebsd")]
@@ -462,7 +581,8 @@ fn desc(errno: Errno) -> &'static str {
#[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 = "redox", target_os = "illumos",
+ target_os = "solaris"))]
EOVERFLOW => "Value too large to be stored in data type",
#[cfg(any(target_os = "macos", target_os = "freebsd",
@@ -488,7 +608,7 @@ fn desc(errno: Errno) -> &'static str {
EPROTO => "Protocol error",
#[cfg(any(target_os = "macos", target_os = "freebsd",
- target_os = "ios", target_os = "openbsd", ))]
+ target_os = "ios", target_os = "openbsd"))]
ENOTRECOVERABLE => "State not recoverable",
#[cfg(any(target_os = "macos", target_os = "freebsd",
@@ -497,7 +617,8 @@ fn desc(errno: Errno) -> &'static str {
#[cfg(any(target_os = "macos", target_os = "freebsd",
target_os = "dragonfly", target_os = "ios",
- target_os = "openbsd", target_os = "netbsd"))]
+ target_os = "openbsd", target_os = "netbsd",
+ target_os = "illumos", target_os = "solaris"))]
ENOTSUP => "Operation not supported",
#[cfg(any(target_os = "macos", target_os = "freebsd",
@@ -514,13 +635,15 @@ fn desc(errno: Errno) -> &'static str {
#[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 = "redox", target_os = "illumos",
+ target_os = "solaris"))]
EDQUOT => "Disc quota exceeded",
#[cfg(any(target_os = "macos", target_os = "freebsd",
target_os = "dragonfly", target_os = "ios",
target_os = "openbsd", target_os = "netbsd",
- target_os = "redox"))]
+ target_os = "redox", target_os = "illumos",
+ target_os = "solaris"))]
ESTALE => "Stale NFS file handle",
#[cfg(any(target_os = "macos", target_os = "freebsd",
@@ -588,14 +711,16 @@ fn desc(errno: Errno) -> &'static str {
#[cfg(any(target_os = "macos", target_os = "ios"))]
EBADMACHO => "Malformed Macho file",
- #[cfg(any(target_os = "macos", target_os = "ios", target_os = "netbsd"))]
+ #[cfg(any(target_os = "macos", target_os = "ios",
+ target_os = "netbsd"))]
EMULTIHOP => "Reserved",
#[cfg(any(target_os = "macos", target_os = "ios",
target_os = "netbsd", target_os = "redox"))]
ENODATA => "No message available on STREAM",
- #[cfg(any(target_os = "macos", target_os = "ios", target_os = "netbsd"))]
+ #[cfg(any(target_os = "macos", target_os = "ios",
+ target_os = "netbsd"))]
ENOLINK => "Reserved",
#[cfg(any(target_os = "macos", target_os = "ios",
@@ -610,7 +735,8 @@ fn desc(errno: Errno) -> &'static str {
target_os = "netbsd", target_os = "redox"))]
ETIME => "STREAM ioctl timeout",
- #[cfg(any(target_os = "macos", target_os = "ios"))]
+ #[cfg(any(target_os = "macos", target_os = "ios",
+ target_os = "illumos", target_os = "solaris"))]
EOPNOTSUPP => "Operation not supported on socket",
#[cfg(any(target_os = "macos", target_os = "ios"))]
@@ -627,6 +753,15 @@ fn desc(errno: Errno) -> &'static str {
#[cfg(target_os = "dragonfly")]
EASYNC => "Async",
+
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ EDEADLOCK => "Resource deadlock would occur",
+
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ ELOCKUNMAPPED => "Locked lock was unmapped",
+
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ ENOTACTIVE => "Facility is not active",
}
}
@@ -635,6 +770,7 @@ fn desc(errno: Errno) -> &'static str {
mod consts {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(i32)]
+ #[non_exhaustive]
pub enum Errno {
UnknownErrno = 0,
EPERM = libc::EPERM,
@@ -772,11 +908,29 @@ mod consts {
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;
- pub const ENOTSUP: Errno = Errno::EOPNOTSUPP;
+ #[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 {
@@ -922,6 +1076,7 @@ mod consts {
mod consts {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(i32)]
+ #[non_exhaustive]
pub enum Errno {
UnknownErrno = 0,
EPERM = libc::EPERM,
@@ -1032,13 +1187,29 @@ mod consts {
EQFULL = libc::EQFULL,
}
- pub const ELAST: Errno = Errno::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;
- pub const EL2NSYNC: Errno = Errno::UnknownErrno;
+ 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 {
@@ -1157,6 +1328,7 @@ mod consts {
mod consts {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(i32)]
+ #[non_exhaustive]
pub enum Errno {
UnknownErrno = 0,
EPERM = libc::EPERM,
@@ -1257,13 +1429,35 @@ mod consts {
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;
- pub const EL2NSYNC: Errno = Errno::UnknownErrno;
+ impl Errno {
+ pub const ELAST: Errno = Errno::EOWNERDEAD;
+ pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
+ pub const EDEADLOCK: Errno = Errno::EDEADLK;
+ pub const EOPNOTSUPP: Errno = Errno::ENOTSUP;
+ }
- pub fn from_i32(e: i32) -> Errno {
+ pub const fn from_i32(e: i32) -> Errno {
use self::Errno::*;
match e {
@@ -1373,6 +1567,7 @@ mod consts {
mod consts {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(i32)]
+ #[non_exhaustive]
pub enum Errno {
UnknownErrno = 0,
EPERM = libc::EPERM,
@@ -1471,14 +1666,35 @@ mod consts {
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;
- pub const EL2NSYNC: Errno = Errno::UnknownErrno;
+ impl Errno {
+ pub const ELAST: Errno = Errno::EASYNC;
+ pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
+ pub const EDEADLOCK: Errno = Errno::EDEADLK;
+ pub const EOPNOTSUPP: Errno = Errno::ENOTSUP;
+ }
- pub fn from_i32(e: i32) -> Errno {
+ pub const fn from_i32(e: i32) -> Errno {
use self::Errno::*;
match e {
@@ -1586,6 +1802,7 @@ mod consts {
mod consts {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(i32)]
+ #[non_exhaustive]
pub enum Errno {
UnknownErrno = 0,
EPERM = libc::EPERM,
@@ -1685,12 +1902,23 @@ mod consts {
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;
- pub const EL2NSYNC: Errno = Errno::UnknownErrno;
+ 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 {
@@ -1798,6 +2026,7 @@ mod consts {
mod consts {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(i32)]
+ #[non_exhaustive]
pub enum Errno {
UnknownErrno = 0,
EPERM = libc::EPERM,
@@ -1898,12 +2127,23 @@ mod consts {
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;
- pub const EL2NSYNC: Errno = Errno::UnknownErrno;
+ 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 {
@@ -2012,6 +2252,7 @@ mod consts {
mod consts {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(i32)]
+ #[non_exhaustive]
pub enum Errno {
UnknownErrno = 0,
EPERM = libc::EPERM,
@@ -2101,12 +2342,17 @@ mod consts {
EPROTO = libc::EPROTO,
}
- pub const ELAST: Errno = Errno::UnknownErrno;
+ #[deprecated(
+ since = "0.22.1",
+ note = "use nix::errno::Errno::EWOULDBLOCK instead"
+ )]
pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
- pub const EL2NSYNC: Errno = Errno::UnknownErrno;
+ 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 {
@@ -2199,3 +2445,279 @@ mod consts {
}
}
}
+
+#[cfg(any(target_os = "illumos", target_os = "solaris"))]
+mod consts {
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+ #[repr(i32)]
+ #[non_exhaustive]
+ pub enum Errno {
+ UnknownErrno = 0,
+ EPERM = libc::EPERM,
+ ENOENT = libc::ENOENT,
+ ESRCH = libc::ESRCH,
+ EINTR = libc::EINTR,
+ EIO = libc::EIO,
+ ENXIO = libc::ENXIO,
+ E2BIG = libc::E2BIG,
+ ENOEXEC = libc::ENOEXEC,
+ EBADF = libc::EBADF,
+ ECHILD = libc::ECHILD,
+ EAGAIN = libc::EAGAIN,
+ ENOMEM = libc::ENOMEM,
+ EACCES = libc::EACCES,
+ EFAULT = libc::EFAULT,
+ ENOTBLK = libc::ENOTBLK,
+ EBUSY = libc::EBUSY,
+ EEXIST = libc::EEXIST,
+ EXDEV = libc::EXDEV,
+ ENODEV = libc::ENODEV,
+ ENOTDIR = libc::ENOTDIR,
+ EISDIR = libc::EISDIR,
+ EINVAL = libc::EINVAL,
+ ENFILE = libc::ENFILE,
+ EMFILE = libc::EMFILE,
+ ENOTTY = libc::ENOTTY,
+ ETXTBSY = libc::ETXTBSY,
+ EFBIG = libc::EFBIG,
+ ENOSPC = libc::ENOSPC,
+ ESPIPE = libc::ESPIPE,
+ EROFS = libc::EROFS,
+ EMLINK = libc::EMLINK,
+ EPIPE = libc::EPIPE,
+ EDOM = libc::EDOM,
+ ERANGE = libc::ERANGE,
+ ENOMSG = libc::ENOMSG,
+ EIDRM = libc::EIDRM,
+ ECHRNG = libc::ECHRNG,
+ EL2NSYNC = libc::EL2NSYNC,
+ EL3HLT = libc::EL3HLT,
+ EL3RST = libc::EL3RST,
+ ELNRNG = libc::ELNRNG,
+ EUNATCH = libc::EUNATCH,
+ ENOCSI = libc::ENOCSI,
+ EL2HLT = libc::EL2HLT,
+ EDEADLK = libc::EDEADLK,
+ ENOLCK = libc::ENOLCK,
+ ECANCELED = libc::ECANCELED,
+ ENOTSUP = libc::ENOTSUP,
+ EDQUOT = libc::EDQUOT,
+ EBADE = libc::EBADE,
+ EBADR = libc::EBADR,
+ EXFULL = libc::EXFULL,
+ ENOANO = libc::ENOANO,
+ EBADRQC = libc::EBADRQC,
+ EBADSLT = libc::EBADSLT,
+ EDEADLOCK = libc::EDEADLOCK,
+ EBFONT = libc::EBFONT,
+ EOWNERDEAD = libc::EOWNERDEAD,
+ ENOTRECOVERABLE = libc::ENOTRECOVERABLE,
+ ENOSTR = libc::ENOSTR,
+ ENODATA = libc::ENODATA,
+ ETIME = libc::ETIME,
+ ENOSR = libc::ENOSR,
+ ENONET = libc::ENONET,
+ ENOPKG = libc::ENOPKG,
+ EREMOTE = libc::EREMOTE,
+ ENOLINK = libc::ENOLINK,
+ EADV = libc::EADV,
+ ESRMNT = libc::ESRMNT,
+ ECOMM = libc::ECOMM,
+ EPROTO = libc::EPROTO,
+ ELOCKUNMAPPED = libc::ELOCKUNMAPPED,
+ ENOTACTIVE = libc::ENOTACTIVE,
+ EMULTIHOP = libc::EMULTIHOP,
+ EBADMSG = libc::EBADMSG,
+ ENAMETOOLONG = libc::ENAMETOOLONG,
+ EOVERFLOW = libc::EOVERFLOW,
+ ENOTUNIQ = libc::ENOTUNIQ,
+ EBADFD = libc::EBADFD,
+ EREMCHG = libc::EREMCHG,
+ ELIBACC = libc::ELIBACC,
+ ELIBBAD = libc::ELIBBAD,
+ ELIBSCN = libc::ELIBSCN,
+ ELIBMAX = libc::ELIBMAX,
+ ELIBEXEC = libc::ELIBEXEC,
+ EILSEQ = libc::EILSEQ,
+ ENOSYS = libc::ENOSYS,
+ ELOOP = libc::ELOOP,
+ ERESTART = libc::ERESTART,
+ ESTRPIPE = libc::ESTRPIPE,
+ ENOTEMPTY = libc::ENOTEMPTY,
+ EUSERS = libc::EUSERS,
+ ENOTSOCK = libc::ENOTSOCK,
+ EDESTADDRREQ = libc::EDESTADDRREQ,
+ EMSGSIZE = libc::EMSGSIZE,
+ EPROTOTYPE = libc::EPROTOTYPE,
+ ENOPROTOOPT = libc::ENOPROTOOPT,
+ EPROTONOSUPPORT = libc::EPROTONOSUPPORT,
+ ESOCKTNOSUPPORT = libc::ESOCKTNOSUPPORT,
+ EOPNOTSUPP = libc::EOPNOTSUPP,
+ EPFNOSUPPORT = libc::EPFNOSUPPORT,
+ EAFNOSUPPORT = libc::EAFNOSUPPORT,
+ EADDRINUSE = libc::EADDRINUSE,
+ EADDRNOTAVAIL = libc::EADDRNOTAVAIL,
+ ENETDOWN = libc::ENETDOWN,
+ ENETUNREACH = libc::ENETUNREACH,
+ ENETRESET = libc::ENETRESET,
+ ECONNABORTED = libc::ECONNABORTED,
+ ECONNRESET = libc::ECONNRESET,
+ ENOBUFS = libc::ENOBUFS,
+ EISCONN = libc::EISCONN,
+ ENOTCONN = libc::ENOTCONN,
+ ESHUTDOWN = libc::ESHUTDOWN,
+ ETOOMANYREFS = libc::ETOOMANYREFS,
+ ETIMEDOUT = libc::ETIMEDOUT,
+ ECONNREFUSED = libc::ECONNREFUSED,
+ EHOSTDOWN = libc::EHOSTDOWN,
+ EHOSTUNREACH = libc::EHOSTUNREACH,
+ EALREADY = libc::EALREADY,
+ EINPROGRESS = libc::EINPROGRESS,
+ ESTALE = libc::ESTALE,
+ }
+
+ #[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 const fn from_i32(e: i32) -> Errno {
+ use self::Errno::*;
+
+ match e {
+ libc::EPERM => EPERM,
+ libc::ENOENT => ENOENT,
+ libc::ESRCH => ESRCH,
+ libc::EINTR => EINTR,
+ libc::EIO => EIO,
+ libc::ENXIO => ENXIO,
+ libc::E2BIG => E2BIG,
+ libc::ENOEXEC => ENOEXEC,
+ libc::EBADF => EBADF,
+ libc::ECHILD => ECHILD,
+ libc::EAGAIN => EAGAIN,
+ libc::ENOMEM => ENOMEM,
+ libc::EACCES => EACCES,
+ libc::EFAULT => EFAULT,
+ libc::ENOTBLK => ENOTBLK,
+ libc::EBUSY => EBUSY,
+ libc::EEXIST => EEXIST,
+ libc::EXDEV => EXDEV,
+ libc::ENODEV => ENODEV,
+ libc::ENOTDIR => ENOTDIR,
+ libc::EISDIR => EISDIR,
+ libc::EINVAL => EINVAL,
+ libc::ENFILE => ENFILE,
+ libc::EMFILE => EMFILE,
+ libc::ENOTTY => ENOTTY,
+ libc::ETXTBSY => ETXTBSY,
+ libc::EFBIG => EFBIG,
+ libc::ENOSPC => ENOSPC,
+ libc::ESPIPE => ESPIPE,
+ libc::EROFS => EROFS,
+ libc::EMLINK => EMLINK,
+ libc::EPIPE => EPIPE,
+ libc::EDOM => EDOM,
+ libc::ERANGE => ERANGE,
+ libc::ENOMSG => ENOMSG,
+ libc::EIDRM => EIDRM,
+ libc::ECHRNG => ECHRNG,
+ libc::EL2NSYNC => EL2NSYNC,
+ libc::EL3HLT => EL3HLT,
+ libc::EL3RST => EL3RST,
+ libc::ELNRNG => ELNRNG,
+ libc::EUNATCH => EUNATCH,
+ libc::ENOCSI => ENOCSI,
+ libc::EL2HLT => EL2HLT,
+ libc::EDEADLK => EDEADLK,
+ libc::ENOLCK => ENOLCK,
+ libc::ECANCELED => ECANCELED,
+ libc::ENOTSUP => ENOTSUP,
+ libc::EDQUOT => EDQUOT,
+ libc::EBADE => EBADE,
+ libc::EBADR => EBADR,
+ libc::EXFULL => EXFULL,
+ libc::ENOANO => ENOANO,
+ libc::EBADRQC => EBADRQC,
+ libc::EBADSLT => EBADSLT,
+ libc::EDEADLOCK => EDEADLOCK,
+ libc::EBFONT => EBFONT,
+ libc::EOWNERDEAD => EOWNERDEAD,
+ libc::ENOTRECOVERABLE => ENOTRECOVERABLE,
+ libc::ENOSTR => ENOSTR,
+ libc::ENODATA => ENODATA,
+ libc::ETIME => ETIME,
+ libc::ENOSR => ENOSR,
+ libc::ENONET => ENONET,
+ libc::ENOPKG => ENOPKG,
+ libc::EREMOTE => EREMOTE,
+ libc::ENOLINK => ENOLINK,
+ libc::EADV => EADV,
+ libc::ESRMNT => ESRMNT,
+ libc::ECOMM => ECOMM,
+ libc::EPROTO => EPROTO,
+ libc::ELOCKUNMAPPED => ELOCKUNMAPPED,
+ libc::ENOTACTIVE => ENOTACTIVE,
+ libc::EMULTIHOP => EMULTIHOP,
+ libc::EBADMSG => EBADMSG,
+ libc::ENAMETOOLONG => ENAMETOOLONG,
+ libc::EOVERFLOW => EOVERFLOW,
+ libc::ENOTUNIQ => ENOTUNIQ,
+ libc::EBADFD => EBADFD,
+ libc::EREMCHG => EREMCHG,
+ libc::ELIBACC => ELIBACC,
+ libc::ELIBBAD => ELIBBAD,
+ libc::ELIBSCN => ELIBSCN,
+ libc::ELIBMAX => ELIBMAX,
+ libc::ELIBEXEC => ELIBEXEC,
+ libc::EILSEQ => EILSEQ,
+ libc::ENOSYS => ENOSYS,
+ libc::ELOOP => ELOOP,
+ libc::ERESTART => ERESTART,
+ libc::ESTRPIPE => ESTRPIPE,
+ libc::ENOTEMPTY => ENOTEMPTY,
+ libc::EUSERS => EUSERS,
+ libc::ENOTSOCK => ENOTSOCK,
+ libc::EDESTADDRREQ => EDESTADDRREQ,
+ libc::EMSGSIZE => EMSGSIZE,
+ libc::EPROTOTYPE => EPROTOTYPE,
+ libc::ENOPROTOOPT => ENOPROTOOPT,
+ libc::EPROTONOSUPPORT => EPROTONOSUPPORT,
+ libc::ESOCKTNOSUPPORT => ESOCKTNOSUPPORT,
+ libc::EOPNOTSUPP => EOPNOTSUPP,
+ libc::EPFNOSUPPORT => EPFNOSUPPORT,
+ libc::EAFNOSUPPORT => EAFNOSUPPORT,
+ libc::EADDRINUSE => EADDRINUSE,
+ libc::EADDRNOTAVAIL => EADDRNOTAVAIL,
+ libc::ENETDOWN => ENETDOWN,
+ libc::ENETUNREACH => ENETUNREACH,
+ libc::ENETRESET => ENETRESET,
+ libc::ECONNABORTED => ECONNABORTED,
+ libc::ECONNRESET => ECONNRESET,
+ libc::ENOBUFS => ENOBUFS,
+ libc::EISCONN => EISCONN,
+ libc::ENOTCONN => ENOTCONN,
+ libc::ESHUTDOWN => ESHUTDOWN,
+ libc::ETOOMANYREFS => ETOOMANYREFS,
+ libc::ETIMEDOUT => ETIMEDOUT,
+ libc::ECONNREFUSED => ECONNREFUSED,
+ libc::EHOSTDOWN => EHOSTDOWN,
+ libc::EHOSTUNREACH => EHOSTUNREACH,
+ libc::EALREADY => EALREADY,
+ libc::EINPROGRESS => EINPROGRESS,
+ libc::ESTALE => ESTALE,
+ _ => UnknownErrno,
+ }
+ }
+}
diff --git a/src/fcntl.rs b/src/fcntl.rs
index d2242da..dd8e59a 100644
--- a/src/fcntl.rs
+++ b/src/fcntl.rs
@@ -34,6 +34,8 @@ libc_bitflags! {
AT_NO_AUTOMOUNT;
#[cfg(any(target_os = "android", target_os = "linux"))]
AT_EMPTY_PATH;
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ AT_EACCESS;
}
}
@@ -48,6 +50,7 @@ libc_bitflags!(
/// Open the file in append-only mode.
O_APPEND;
/// Generate a signal when input or output becomes possible.
+ #[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
O_ASYNC;
/// Closes the file descriptor once an `execve` call is made.
///
@@ -63,6 +66,7 @@ libc_bitflags!(
target_os = "netbsd"))]
O_DIRECT;
/// If the specified path isn't a directory, fail.
+ #[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
O_DIRECTORY;
/// Implicitly follow each `write()` with an `fdatasync()`.
#[cfg(any(target_os = "android",
@@ -162,7 +166,7 @@ libc_bitflags!(
);
// The conversion is not identical on all operating systems.
-#[allow(clippy::identity_conversion)]
+#[allow(clippy::useless_conversion)]
pub fn open<P: ?Sized + NixPath>(path: &P, oflag: OFlag, mode: Mode) -> Result<RawFd> {
let fd = path.with_nix_path(|cstr| {
unsafe { libc::open(cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) }
@@ -172,7 +176,7 @@ pub fn open<P: ?Sized + NixPath>(path: &P, oflag: OFlag, mode: Mode) -> Result<R
}
// The conversion is not identical on all operating systems.
-#[allow(clippy::identity_conversion)]
+#[allow(clippy::useless_conversion)]
#[cfg(not(target_os = "redox"))]
pub fn openat<P: ?Sized + NixPath>(
dirfd: RawFd,
@@ -206,6 +210,43 @@ pub fn renameat<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
Errno::result(res).map(drop)
}
+#[cfg(all(
+ target_os = "linux",
+ target_env = "gnu",
+))]
+libc_bitflags! {
+ pub struct RenameFlags: u32 {
+ RENAME_EXCHANGE;
+ RENAME_NOREPLACE;
+ RENAME_WHITEOUT;
+ }
+}
+
+#[cfg(all(
+ target_os = "linux",
+ target_env = "gnu",
+))]
+pub fn renameat2<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
+ old_dirfd: Option<RawFd>,
+ old_path: &P1,
+ new_dirfd: Option<RawFd>,
+ new_path: &P2,
+ flags: RenameFlags,
+) -> Result<()> {
+ let res = old_path.with_nix_path(|old_cstr| {
+ new_path.with_nix_path(|new_cstr| unsafe {
+ libc::renameat2(
+ at_rawfd(old_dirfd),
+ old_cstr.as_ptr(),
+ at_rawfd(new_dirfd),
+ new_cstr.as_ptr(),
+ flags.bits(),
+ )
+ })
+ })??;
+ Errno::result(res).map(drop)
+}
+
fn wrap_readlink_result(mut v: Vec<u8>, len: ssize_t) -> Result<OsString> {
unsafe { v.set_len(len as usize) }
v.shrink_to_fit();
@@ -248,8 +289,19 @@ fn inner_readlink<P: ?Sized + NixPath>(dirfd: Option<RawFd>, path: &P) -> Result
}
// Uh oh, the result is too long...
// Let's try to ask lstat how many bytes to allocate.
- let reported_size = super::sys::stat::lstat(path)
- .and_then(|x| Ok(x.st_size))
+ let reported_size = match dirfd {
+ #[cfg(target_os = "redox")]
+ Some(_) => unreachable!(),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ Some(dirfd) => {
+ let flags = if path.is_empty() { AtFlags::AT_EMPTY_PATH } else { AtFlags::empty() };
+ super::sys::stat::fstatat(dirfd, path, flags | AtFlags::AT_SYMLINK_NOFOLLOW)
+ },
+ #[cfg(not(any(target_os = "android", target_os = "linux", target_os = "redox")))]
+ Some(dirfd) => super::sys::stat::fstatat(dirfd, path, AtFlags::AT_SYMLINK_NOFOLLOW),
+ None => super::sys::stat::lstat(path)
+ }
+ .map(|x| x.st_size)
.unwrap_or(0);
let mut try_size = if reported_size > 0 {
// Note: even if `lstat`'s apparently valid answer turns out to be
@@ -273,7 +325,7 @@ fn inner_readlink<P: ?Sized + NixPath>(dirfd: Option<RawFd>, path: &P) -> Result
Some(next_size) => try_size = next_size,
// It's absurd that this would happen, but handle it sanely
// anyway.
- None => break Err(super::Error::Sys(Errno::ENAMETOOLONG)),
+ None => break Err(Errno::ENAMETOOLONG),
}
}
}
@@ -322,6 +374,7 @@ libc_bitflags!(
#[cfg(not(target_os = "redox"))]
#[derive(Debug, Eq, Hash, PartialEq)]
+#[non_exhaustive]
pub enum FcntlArg<'a> {
F_DUPFD(RawFd),
F_DUPFD_CLOEXEC(RawFd),
@@ -353,6 +406,7 @@ pub enum FcntlArg<'a> {
#[cfg(target_os = "redox")]
#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
+#[non_exhaustive]
pub enum FcntlArg {
F_DUPFD(RawFd),
F_DUPFD_CLOEXEC(RawFd),
@@ -402,6 +456,7 @@ pub fn fcntl(fd: RawFd, arg: FcntlArg) -> Result<c_int> {
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[non_exhaustive]
pub enum FlockArg {
LockShared,
LockExclusive,
@@ -591,12 +646,12 @@ pub fn fallocate(
))]
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,
@@ -612,9 +667,14 @@ mod posix_fadvise {
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))
+ }
}
}
@@ -631,6 +691,6 @@ pub fn posix_fallocate(fd: RawFd, offset: libc::off_t, len: libc::off_t) -> Resu
match Errno::result(res) {
Err(err) => Err(err),
Ok(0) => Ok(()),
- Ok(errno) => Err(crate::Error::Sys(Errno::from_i32(errno))),
+ Ok(errno) => Err(Errno::from_i32(errno)),
}
}
diff --git a/src/features.rs b/src/features.rs
index 6b1cff5..ed80fd7 100644
--- a/src/features.rs
+++ b/src/features.rs
@@ -94,13 +94,28 @@ mod os {
}
}
-#[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 = "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 = "ios",
+ target_os = "fuchsia",
+ target_os = "solaris"))]
+mod os {
+ /// Check if the OS supports atomic close-on-exec for sockets
+ pub const fn socket_atomic_cloexec() -> bool {
false
}
}
diff --git a/src/kmod.rs b/src/kmod.rs
index 8789cb6..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;
@@ -42,7 +41,7 @@ use crate::Result;
/// init_module(&mut contents, &CString::new("who=Rust when=Now,12").unwrap()).unwrap();
/// ```
///
-/// See [`man init_module(2)`](http://man7.org/linux/man-pages/man2/init_module.2.html) for more information.
+/// See [`man init_module(2)`](https://man7.org/linux/man-pages/man2/init_module.2.html) for more information.
pub fn init_module(module_image: &[u8], param_values: &CStr) -> Result<()> {
let res = unsafe {
libc::syscall(
@@ -79,7 +78,7 @@ libc_bitflags!(
/// finit_module(&f, &CString::new("").unwrap(), ModuleInitFlags::empty()).unwrap();
/// ```
///
-/// See [`man init_module(2)`](http://man7.org/linux/man-pages/man2/init_module.2.html) for more information.
+/// See [`man init_module(2)`](https://man7.org/linux/man-pages/man2/init_module.2.html) for more information.
pub fn finit_module<T: AsRawFd>(fd: &T, param_values: &CStr, flags: ModuleInitFlags) -> Result<()> {
let res = unsafe {
libc::syscall(
@@ -96,7 +95,7 @@ pub fn finit_module<T: AsRawFd>(fd: &T, param_values: &CStr, flags: ModuleInitFl
libc_bitflags!(
/// Flags used by `delete_module`.
///
- /// See [`man delete_module(2)`](http://man7.org/linux/man-pages/man2/delete_module.2.html)
+ /// See [`man delete_module(2)`](https://man7.org/linux/man-pages/man2/delete_module.2.html)
/// for a detailed description how these flags work.
pub struct DeleteModuleFlags: libc::c_int {
O_NONBLOCK;
@@ -115,7 +114,7 @@ libc_bitflags!(
/// delete_module(&CString::new("mymod").unwrap(), DeleteModuleFlags::O_NONBLOCK).unwrap();
/// ```
///
-/// See [`man delete_module(2)`](http://man7.org/linux/man-pages/man2/delete_module.2.html) for more information.
+/// See [`man delete_module(2)`](https://man7.org/linux/man-pages/man2/delete_module.2.html) for more information.
pub fn delete_module(name: &CStr, flags: DeleteModuleFlags) -> Result<()> {
let res = unsafe { libc::syscall(libc::SYS_delete_module, name.as_ptr(), flags.bits()) };
diff --git a/src/lib.rs b/src/lib.rs
index e62c158..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 @@ pub use libc;
// 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",
@@ -37,12 +36,15 @@ pub mod fcntl;
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
+ target_os = "illumos",
target_os = "openbsd"))]
pub mod ifaddrs;
#[cfg(any(target_os = "android",
target_os = "linux"))]
+#[allow(missing_docs)]
pub mod kmod;
#[cfg(any(target_os = "android",
+ target_os = "freebsd",
target_os = "linux"))]
pub mod mount;
#[cfg(any(target_os = "dragonfly",
@@ -50,23 +52,24 @@ pub mod mount;
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;
/*
@@ -77,7 +80,7 @@ pub mod unistd;
use libc::{c_char, PATH_MAX};
-use std::{error, fmt, ptr, result};
+use std::{ptr, result};
use std::ffi::{CStr, OsStr};
use std::os::unix::ffi::OsStrExt;
use std::path::{Path, PathBuf};
@@ -85,89 +88,31 @@ use std::path::{Path, PathBuf};
use errno::Errno;
/// Nix Result Type
-pub type Result<T> = result::Result<T, Error>;
+pub type Result<T> = result::Result<T, Errno>;
-/// Nix Error Type
+/// Nix's main error type.
///
-/// The nix error type provides a common way of dealing with
-/// various system system/libc calls that might fail. Each
-/// error has a corresponding errno (usually the one from the
-/// underlying OS) to which it can be mapped in addition to
-/// implementing other common traits.
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-pub enum Error {
- Sys(Errno),
- InvalidPath,
- /// The operation involved a conversion to Rust's native String type, which failed because the
- /// string did not contain all valid UTF-8.
- InvalidUtf8,
- /// The operation is not supported by Nix, in this instance either use the libc bindings or
- /// consult the module documentation to see if there is a more appropriate interface available.
- UnsupportedOperation,
-}
-
-impl Error {
- /// Convert this `Error` to an [`Errno`](enum.Errno.html).
- ///
- /// # Example
- ///
- /// ```
- /// # use nix::Error;
- /// # use nix::errno::Errno;
- /// let e = Error::from(Errno::EPERM);
- /// assert_eq!(Some(Errno::EPERM), e.as_errno());
- /// ```
- pub fn as_errno(self) -> Option<Errno> {
- if let Error::Sys(e) = self {
- Some(e)
- } else {
- None
- }
- }
-
- /// Create a nix Error from a given errno
- pub fn from_errno(errno: Errno) -> Error {
- Error::Sys(errno)
- }
-
- /// Get the current errno and convert it to a nix Error
- pub fn last() -> Error {
- Error::Sys(Errno::last())
- }
-
- /// Create a new invalid argument error (`EINVAL`)
- pub fn invalid_argument() -> Error {
- Error::Sys(Errno::EINVAL)
- }
-
-}
-
-impl From<Errno> for Error {
- fn from(errno: Errno) -> Error { Error::from_errno(errno) }
-}
-
-impl From<std::string::FromUtf8Error> for Error {
- fn from(_: std::string::FromUtf8Error) -> Error { Error::InvalidUtf8 }
-}
-
-impl error::Error for Error {}
-
-impl fmt::Display for Error {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match *self {
- Error::InvalidPath => write!(f, "Invalid path"),
- Error::InvalidUtf8 => write!(f, "Invalid UTF-8 string"),
- Error::UnsupportedOperation => write!(f, "Unsupported Operation"),
- Error::Sys(errno) => write!(f, "{:?}: {}", errno, errno.desc()),
- }
- }
-}
-
+/// It's a wrapper around Errno. As such, it's very interoperable with
+/// [`std::io::Error`], but it has the advantages of:
+/// * `Clone`
+/// * `Copy`
+/// * `Eq`
+/// * Small size
+/// * Represents all of the system's errnos, instead of just the most common
+/// 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;
}
@@ -215,7 +160,7 @@ impl NixPath for CStr {
where F: FnOnce(&CStr) -> T {
// Equivalence with the [u8] impl.
if self.len() >= PATH_MAX as usize {
- return Err(Error::InvalidPath);
+ return Err(Errno::ENAMETOOLONG)
}
Ok(f(self))
@@ -236,11 +181,11 @@ impl NixPath for [u8] {
let mut buf = [0u8; PATH_MAX as usize];
if self.len() >= PATH_MAX as usize {
- return Err(Error::InvalidPath);
+ return Err(Errno::ENAMETOOLONG)
}
match self.iter().position(|b| *b == 0) {
- Some(_) => Err(Error::InvalidPath),
+ 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 feb02ea..3ccbfdd 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -83,9 +83,9 @@ macro_rules! libc_bitflags {
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 @@ macro_rules! libc_enum {
}
};
+ // Exit rule including TryFrom
+ (@make_enum
+ name: $BitFlags:ident,
+ {
+ $v:vis
+ 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,
+ $try_froms:tt;
+ ) => {
+ libc_enum! {
+ @make_enum
+ name: $BitFlags,
+ {
+ $v
+ attrs: $attrs,
+ entries: $entries,
+ }
+ }
+ };
+
+ // Done accumulating and want TryFrom
+ (@accumulate_entries
+ name: $BitFlags:ident,
{
$v:vis
- name: $BitFlags:ident,
attrs: $attrs:tt,
+ from_type: $repr:path,
},
- $entries:tt;
+ $entries:tt,
+ $try_froms:tt;
) => {
libc_enum! {
@make_enum
+ name: $BitFlags,
{
$v
- name: $BitFlags,
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 @@ macro_rules! libc_enum {
// 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 @@ macro_rules! libc_enum {
// 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,26 +276,36 @@ macro_rules! libc_enum {
) => {
libc_enum! {
@accumulate_entries
+ name: $BitFlags,
{
$v
- name: $BitFlags,
attrs: [$(#[$attr])*],
},
+ [],
[];
$($vals)*
}
};
-}
-/// A Rust version of the familiar C `offset_of` macro. It returns the byte
-/// offset of `field` within struct `ty`
-#[cfg(not(target_os = "redox"))]
-macro_rules! offset_of {
- ($ty:ty, $field:ident) => {{
- // Safe because we don't actually read from the dereferenced pointer
- #[allow(unused_unsafe)] // for when the macro is used in an unsafe block
- unsafe {
- &(*(ptr::null() as *const $ty)).$field as *const _ as usize
- }
- }}
+ // 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
new file mode 100644
index 0000000..627bfa5
--- /dev/null
+++ b/src/mount/bsd.rs
@@ -0,0 +1,426 @@
+use crate::{
+ Error,
+ Errno,
+ NixPath,
+ Result,
+ sys::uio::IoVec
+};
+use libc::{c_char, c_int, c_uint, c_void};
+use std::{
+ borrow::Cow,
+ ffi::{CString, CStr},
+ fmt,
+ io,
+ ptr
+};
+
+
+libc_bitflags!(
+ /// Used with [`Nmount::nmount`].
+ pub struct MntFlags: c_int {
+ /// ACL support enabled.
+ #[cfg(any(target_os = "netbsd", target_os = "freebsd"))]
+ MNT_ACLS;
+ /// All I/O to the file system should be done asynchronously.
+ MNT_ASYNC;
+ /// dir should instead be a file system ID encoded as “FSID:val0:val1”.
+ #[cfg(target_os = "freebsd")]
+ MNT_BYFSID;
+ /// Force a read-write mount even if the file system appears to be
+ /// unclean.
+ MNT_FORCE;
+ /// GEOM journal support enabled.
+ #[cfg(target_os = "freebsd")]
+ MNT_GJOURNAL;
+ /// MAC support for objects.
+ #[cfg(any(target_os = "macos", target_os = "freebsd"))]
+ MNT_MULTILABEL;
+ /// Disable read clustering.
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ MNT_NOCLUSTERR;
+ /// Disable write clustering.
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ MNT_NOCLUSTERW;
+ /// Enable NFS version 4 ACLs.
+ #[cfg(target_os = "freebsd")]
+ MNT_NFS4ACLS;
+ /// Do not update access times.
+ MNT_NOATIME;
+ /// Disallow program execution.
+ MNT_NOEXEC;
+ /// Do not honor setuid or setgid bits on files when executing them.
+ MNT_NOSUID;
+ /// Do not follow symlinks.
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ MNT_NOSYMFOLLOW;
+ /// Mount read-only.
+ MNT_RDONLY;
+ /// Causes the vfs subsystem to update its data structures pertaining to
+ /// the specified already mounted file system.
+ MNT_RELOAD;
+ /// Create a snapshot of the file system.
+ ///
+ /// See [mksnap_ffs(8)](https://www.freebsd.org/cgi/man.cgi?query=mksnap_ffs)
+ #[cfg(any(target_os = "macos", target_os = "freebsd"))]
+ MNT_SNAPSHOT;
+ /// Using soft updates.
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ MNT_SOFTDEP;
+ /// Directories with the SUID bit set chown new files to their own
+ /// owner.
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ MNT_SUIDDIR;
+ /// All I/O to the file system should be done synchronously.
+ MNT_SYNCHRONOUS;
+ /// Union with underlying fs.
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "netbsd"
+ ))]
+ MNT_UNION;
+ /// Indicates that the mount command is being applied to an already
+ /// mounted file system.
+ MNT_UPDATE;
+ /// Check vnode use counts.
+ #[cfg(target_os = "freebsd")]
+ MNT_NONBUSY;
+ }
+);
+
+
+/// The Error type of [`Nmount::nmount`].
+///
+/// It wraps an [`Errno`], but also may contain an additional message returned
+/// by `nmount(2)`.
+#[derive(Debug)]
+pub struct NmountError {
+ errno: Error,
+ errmsg: Option<String>
+}
+
+impl NmountError {
+ /// Returns the additional error string sometimes generated by `nmount(2)`.
+ pub fn errmsg(&self) -> Option<&str> {
+ self.errmsg.as_deref()
+ }
+
+ /// Returns the inner [`Error`]
+ pub const fn error(&self) -> Error {
+ self.errno
+ }
+
+ fn new(error: Error, errmsg: Option<&CStr>) -> Self {
+ Self {
+ errno: error,
+ errmsg: errmsg.map(CStr::to_string_lossy).map(Cow::into_owned)
+ }
+ }
+}
+
+impl std::error::Error for NmountError {}
+
+impl fmt::Display for NmountError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ if let Some(errmsg) = &self.errmsg {
+ write!(f, "{:?}: {}: {}", self.errno, errmsg, self.errno.desc())
+ } else {
+ write!(f, "{:?}: {}", self.errno, self.errno.desc())
+ }
+ }
+}
+
+impl From<NmountError> for io::Error {
+ fn from(err: NmountError) -> Self {
+ err.errno.into()
+ }
+}
+
+/// Result type of [`Nmount::nmount`].
+pub type NmountResult = std::result::Result<(), NmountError>;
+
+/// Mount a FreeBSD file system.
+///
+/// The `nmount(2)` system call works similarly to the `mount(8)` program; it
+/// takes its options as a series of name-value pairs. Most of the values are
+/// strings, as are all of the names. The `Nmount` structure builds up an
+/// argument list and then executes the syscall.
+///
+/// # Examples
+///
+/// To mount `target` onto `mountpoint` with `nullfs`:
+/// ```
+/// # use nix::unistd::Uid;
+/// # use ::sysctl::CtlValue;
+/// # if !Uid::current().is_root() && CtlValue::Int(0) == ::sysctl::value("vfs.usermount").unwrap() {
+/// # return;
+/// # };
+/// use nix::mount::{MntFlags, Nmount, unmount};
+/// use std::ffi::CString;
+/// use tempfile::tempdir;
+///
+/// let mountpoint = tempdir().unwrap();
+/// let target = tempdir().unwrap();
+///
+/// let fstype = CString::new("fstype").unwrap();
+/// let nullfs = CString::new("nullfs").unwrap();
+/// Nmount::new()
+/// .str_opt(&fstype, &nullfs)
+/// .str_opt_owned("fspath", mountpoint.path().to_str().unwrap())
+/// .str_opt_owned("target", target.path().to_str().unwrap())
+/// .nmount(MntFlags::empty()).unwrap();
+///
+/// unmount(mountpoint.path(), MntFlags::empty()).unwrap();
+/// ```
+///
+/// # See Also
+/// * [`nmount(2)`](https://www.freebsd.org/cgi/man.cgi?query=nmount)
+/// * [`nullfs(5)`](https://www.freebsd.org/cgi/man.cgi?query=nullfs)
+#[cfg(target_os = "freebsd")]
+#[derive(Debug, Default)]
+pub struct Nmount<'a>{
+ iov: Vec<IoVec<&'a [u8]>>,
+ is_owned: Vec<bool>,
+}
+
+#[cfg(target_os = "freebsd")]
+impl<'a> Nmount<'a> {
+ /// Add an opaque mount option.
+ ///
+ /// Some file systems take binary-valued mount options. They can be set
+ /// with this method.
+ ///
+ /// # Safety
+ ///
+ /// Unsafe because it will cause `Nmount::nmount` to dereference a raw
+ /// pointer. The user is responsible for ensuring that `val` is valid and
+ /// its lifetime outlives `self`! An easy way to do that is to give the
+ /// value a larger scope than `name`
+ ///
+ /// # Examples
+ /// ```
+ /// use libc::c_void;
+ /// use nix::mount::Nmount;
+ /// use std::ffi::CString;
+ /// use std::mem;
+ ///
+ /// // Note that flags outlives name
+ /// let mut flags: u32 = 0xdeadbeef;
+ /// let name = CString::new("flags").unwrap();
+ /// let p = &mut flags as *mut u32 as *mut c_void;
+ /// let len = mem::size_of_val(&flags);
+ /// let mut nmount = Nmount::new();
+ /// unsafe { nmount.mut_ptr_opt(&name, p, len) };
+ /// ```
+ pub unsafe fn mut_ptr_opt(
+ &mut self,
+ name: &'a CStr,
+ val: *mut c_void,
+ len: usize
+ ) -> &mut Self
+ {
+ self.iov.push(IoVec::from_slice(name.to_bytes_with_nul()));
+ self.is_owned.push(false);
+ self.iov.push(IoVec::from_raw_parts(val, len));
+ self.is_owned.push(false);
+ self
+ }
+
+ /// Add a mount option that does not take a value.
+ ///
+ /// # Examples
+ /// ```
+ /// use nix::mount::Nmount;
+ /// use std::ffi::CString;
+ ///
+ /// let read_only = CString::new("ro").unwrap();
+ /// Nmount::new()
+ /// .null_opt(&read_only);
+ /// ```
+ pub fn null_opt(&mut self, name: &'a CStr) -> &mut Self {
+ self.iov.push(IoVec::from_slice(name.to_bytes_with_nul()));
+ self.is_owned.push(false);
+ self.iov.push(IoVec::from_raw_parts(ptr::null_mut(), 0));
+ self.is_owned.push(false);
+ self
+ }
+
+ /// Add a mount option that does not take a value, but whose name must be
+ /// owned.
+ ///
+ ///
+ /// This has higher runtime cost than [`Nmount::null_opt`], but is useful
+ /// when the name's lifetime doesn't outlive the `Nmount`, or it's a
+ /// different string type than `CStr`.
+ ///
+ /// # Examples
+ /// ```
+ /// use nix::mount::Nmount;
+ ///
+ /// let read_only = "ro";
+ /// let mut nmount: Nmount<'static> = Nmount::new();
+ /// nmount.null_opt_owned(read_only);
+ /// ```
+ pub fn null_opt_owned<P: ?Sized + NixPath>(&mut self, name: &P) -> &mut Self
+ {
+ name.with_nix_path(|s| {
+ let len = s.to_bytes_with_nul().len();
+ self.iov.push(IoVec::from_raw_parts(
+ // Must free it later
+ s.to_owned().into_raw() as *mut c_void,
+ len
+ ));
+ self.is_owned.push(true);
+ }).unwrap();
+ self.iov.push(IoVec::from_raw_parts(ptr::null_mut(), 0));
+ self.is_owned.push(false);
+ self
+ }
+
+ /// Add a mount option as a [`CStr`].
+ ///
+ /// # Examples
+ /// ```
+ /// use nix::mount::Nmount;
+ /// use std::ffi::CString;
+ ///
+ /// let fstype = CString::new("fstype").unwrap();
+ /// let nullfs = CString::new("nullfs").unwrap();
+ /// Nmount::new()
+ /// .str_opt(&fstype, &nullfs);
+ /// ```
+ pub fn str_opt(
+ &mut self,
+ name: &'a CStr,
+ val: &'a CStr
+ ) -> &mut Self
+ {
+ self.iov.push(IoVec::from_slice(name.to_bytes_with_nul()));
+ self.is_owned.push(false);
+ self.iov.push(IoVec::from_slice(val.to_bytes_with_nul()));
+ self.is_owned.push(false);
+ self
+ }
+
+ /// Add a mount option as an owned string.
+ ///
+ /// This has higher runtime cost than [`Nmount::str_opt`], but is useful
+ /// when the value's lifetime doesn't outlive the `Nmount`, or it's a
+ /// different string type than `CStr`.
+ ///
+ /// # Examples
+ /// ```
+ /// use nix::mount::Nmount;
+ /// use std::path::Path;
+ ///
+ /// let mountpoint = Path::new("/mnt");
+ /// Nmount::new()
+ /// .str_opt_owned("fspath", mountpoint.to_str().unwrap());
+ /// ```
+ pub fn str_opt_owned<P1, P2>(&mut self, name: &P1, val: &P2) -> &mut Self
+ where P1: ?Sized + NixPath,
+ P2: ?Sized + NixPath
+ {
+ name.with_nix_path(|s| {
+ let len = s.to_bytes_with_nul().len();
+ self.iov.push(IoVec::from_raw_parts(
+ // Must free it later
+ s.to_owned().into_raw() as *mut c_void,
+ len
+ ));
+ self.is_owned.push(true);
+ }).unwrap();
+ val.with_nix_path(|s| {
+ let len = s.to_bytes_with_nul().len();
+ self.iov.push(IoVec::from_raw_parts(
+ // Must free it later
+ s.to_owned().into_raw() as *mut c_void,
+ len
+ ));
+ self.is_owned.push(true);
+ }).unwrap();
+ self
+ }
+
+ /// Create a new `Nmount` struct with no options
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ /// Actually mount the file system.
+ pub fn nmount(&mut self, flags: MntFlags) -> NmountResult {
+ // nmount can return extra error information via a "errmsg" return
+ // argument.
+ const ERRMSG_NAME: &[u8] = b"errmsg\0";
+ let mut errmsg = vec![0u8; 255];
+ self.iov.push(IoVec::from_raw_parts(
+ ERRMSG_NAME.as_ptr() as *mut c_void,
+ ERRMSG_NAME.len()
+ ));
+ self.iov.push(IoVec::from_raw_parts(
+ errmsg.as_mut_ptr() as *mut c_void,
+ errmsg.len()
+ ));
+
+ let niov = self.iov.len() as c_uint;
+ let iovp = self.iov.as_mut_ptr() as *mut libc::iovec;
+ let res = unsafe {
+ libc::nmount(iovp, niov, flags.bits)
+ };
+ match Errno::result(res) {
+ Ok(_) => Ok(()),
+ Err(error) => {
+ let errmsg = match errmsg.iter().position(|&x| x == 0) {
+ None => None,
+ Some(0) => None,
+ Some(n) => {
+ let sl = &errmsg[0..n + 1];
+ Some(CStr::from_bytes_with_nul(sl).unwrap())
+ }
+ };
+ Err(NmountError::new(error, errmsg))
+ }
+ }
+ }
+}
+
+#[cfg(target_os = "freebsd")]
+impl<'a> Drop for Nmount<'a> {
+ fn drop(&mut self) {
+ for (iov, is_owned) in self.iov.iter().zip(self.is_owned.iter()) {
+ if *is_owned {
+ // Free the owned string. Safe because we recorded ownership,
+ // and Nmount does not implement Clone.
+ unsafe {
+ drop(CString::from_raw(iov.0.iov_base as *mut c_char));
+ }
+ }
+ }
+ }
+}
+
+/// Unmount the file system mounted at `mountpoint`.
+///
+/// Useful flags include
+/// * `MNT_FORCE` - Unmount even if still in use.
+/// * `MNT_BYFSID` - `mountpoint` is not a path, but a file system ID
+/// encoded as `FSID:val0:val1`, where `val0` and `val1`
+/// are the contents of the `fsid_t val[]` array in decimal.
+/// The file system that has the specified file system ID
+/// will be unmounted. See
+/// [`statfs`](crate::sys::statfs::statfs) to determine the
+/// `fsid`.
+pub fn unmount<P>(mountpoint: &P, flags: MntFlags) -> Result<()>
+ where P: ?Sized + NixPath
+{
+ let res = mountpoint.with_nix_path(|cstr| {
+ unsafe { libc::unmount(cstr.as_ptr(), flags.bits) }
+ })?;
+
+ Errno::result(res).map(drop)
+}
diff --git a/src/mount.rs b/src/mount/linux.rs
index 2c54761..4cb2fa5 100644
--- a/src/mount.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;
@@ -38,6 +39,7 @@ libc_bitflags!(
MS_KERNMOUNT;
MS_I_VERSION;
MS_STRICTATIME;
+ MS_LAZYTIME;
MS_ACTIVE;
MS_NOUSER;
MS_RMT_MASK;
diff --git a/src/mount/mod.rs b/src/mount/mod.rs
new file mode 100644
index 0000000..14bf2a9
--- /dev/null
+++ b/src/mount/mod.rs
@@ -0,0 +1,21 @@
+//! Mount file systems
+#[cfg(any(target_os = "android", target_os = "linux"))]
+mod linux;
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+pub use self::linux::*;
+
+#[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+mod bsd;
+
+#[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+pub use self::bsd::*;
diff --git a/src/mqueue.rs b/src/mqueue.rs
index 0215de5..34fd802 100644
--- a/src/mqueue.rs
+++ b/src/mqueue.rs
@@ -1,6 +1,6 @@
//! Posix Message Queue functions
//!
-//! [Further reading and details on the C API](http://man7.org/linux/man-pages/man7/mq_overview.7.html)
+//! [Further reading and details on the C API](https://man7.org/linux/man-pages/man7/mq_overview.7.html)
use crate::Result;
use crate::errno::Errno;
@@ -59,7 +59,7 @@ impl MqAttr {
}
}
- pub fn flags(&self) -> mq_attr_member_t {
+ pub const fn flags(&self) -> mq_attr_member_t {
self.mq_attr.mq_flags
}
}
@@ -67,7 +67,7 @@ impl MqAttr {
/// Open a message queue
///
-/// See also [`mq_open(2)`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_open.html)
+/// See also [`mq_open(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_open.html)
// The mode.bits cast is only lossless on some OSes
#[allow(clippy::cast_lossless)]
pub fn mq_open(name: &CString,
@@ -89,7 +89,7 @@ pub fn mq_open(name: &CString,
/// Remove a message queue
///
-/// See also [`mq_unlink(2)`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_unlink.html)
+/// See also [`mq_unlink(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_unlink.html)
pub fn mq_unlink(name: &CString) -> Result<()> {
let res = unsafe { libc::mq_unlink(name.as_ptr()) };
Errno::result(res).map(drop)
@@ -97,7 +97,7 @@ pub fn mq_unlink(name: &CString) -> Result<()> {
/// Close a message queue
///
-/// See also [`mq_close(2)`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_close.html)
+/// See also [`mq_close(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_close.html)
pub fn mq_close(mqdes: mqd_t) -> Result<()> {
let res = unsafe { libc::mq_close(mqdes) };
Errno::result(res).map(drop)
@@ -105,7 +105,7 @@ pub fn mq_close(mqdes: mqd_t) -> Result<()> {
/// Receive a message from a message queue
///
-/// See also [`mq_receive(2)`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_receive.html)
+/// See also [`mq_receive(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_receive.html)
pub fn mq_receive(mqdes: mqd_t, message: &mut [u8], msg_prio: &mut u32) -> Result<usize> {
let len = message.len() as size_t;
let res = unsafe {
@@ -119,7 +119,7 @@ pub fn mq_receive(mqdes: mqd_t, message: &mut [u8], msg_prio: &mut u32) -> Resul
/// Send a message to a message queue
///
-/// See also [`mq_send(2)`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html)
+/// See also [`mq_send(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html)
pub fn mq_send(mqdes: mqd_t, message: &[u8], msq_prio: u32) -> Result<()> {
let res = unsafe {
libc::mq_send(mqdes,
@@ -132,7 +132,7 @@ pub fn mq_send(mqdes: mqd_t, message: &[u8], msq_prio: u32) -> Result<()> {
/// Get message queue attributes
///
-/// See also [`mq_getattr(2)`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_getattr.html)
+/// See also [`mq_getattr(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_getattr.html)
pub fn mq_getattr(mqd: mqd_t) -> Result<MqAttr> {
let mut attr = mem::MaybeUninit::<libc::mq_attr>::uninit();
let res = unsafe { libc::mq_getattr(mqd, attr.as_mut_ptr()) };
@@ -143,7 +143,7 @@ pub fn mq_getattr(mqd: mqd_t) -> Result<MqAttr> {
/// Returns the old attributes
/// It is recommend to use the `mq_set_nonblock()` and `mq_remove_nonblock()` convenience functions as they are easier to use
///
-/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_setattr.html)
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_setattr.html)
pub fn mq_setattr(mqd: mqd_t, newattr: &MqAttr) -> Result<MqAttr> {
let mut attr = mem::MaybeUninit::<libc::mq_attr>::uninit();
let res = unsafe {
@@ -155,6 +155,7 @@ pub fn mq_setattr(mqd: mqd_t, newattr: &MqAttr) -> Result<MqAttr> {
/// 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/net/if_.rs b/src/net/if_.rs
index 9636488..bc00a43 100644
--- a/src/net/if_.rs
+++ b/src/net/if_.rs
@@ -3,8 +3,8 @@
//! Uses Linux and/or POSIX functions to resolve interface names like "eth0"
//! or "socan1" into device numbers.
+use crate::{Error, NixPath, Result};
use libc::c_uint;
-use crate::{Result, Error, NixPath};
/// Resolve an interface into a interface number.
pub fn if_nametoindex<P: ?Sized + NixPath>(name: &P) -> Result<c_uint> {
@@ -21,39 +21,41 @@ libc_bitflags!(
/// Standard interface flags, used by `getifaddrs`
pub struct InterfaceFlags: libc::c_int {
/// Interface is running. (see
- /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
IFF_UP;
/// Valid broadcast address set. (see
- /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
IFF_BROADCAST;
/// Internal debugging flag. (see
- /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
IFF_DEBUG;
/// Interface is a loopback interface. (see
- /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
IFF_LOOPBACK;
/// Interface is a point-to-point link. (see
- /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
IFF_POINTOPOINT;
/// Avoid use of trailers. (see
- /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
#[cfg(any(target_os = "android",
target_os = "fuchsia",
target_os = "ios",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
+ target_os = "illumos",
target_os = "solaris"))]
IFF_NOTRAILERS;
/// Interface manages own routes.
#[cfg(any(target_os = "dragonfly"))]
IFF_SMART;
/// Resources allocated. (see
- /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
#[cfg(any(target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "fuchsia",
+ target_os = "illumos",
target_os = "ios",
target_os = "linux",
target_os = "macos",
@@ -62,16 +64,16 @@ libc_bitflags!(
target_os = "solaris"))]
IFF_RUNNING;
/// No arp protocol, L2 destination address not set. (see
- /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
IFF_NOARP;
/// Interface is in promiscuous mode. (see
- /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
IFF_PROMISC;
/// Receive all multicast packets. (see
- /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
IFF_ALLMULTI;
/// Master of a load balancing bundle. (see
- /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
IFF_MASTER;
/// transmission in progress, tx hardware queue is full
@@ -82,10 +84,10 @@ libc_bitflags!(
target_os = "ios"))]
IFF_OACTIVE;
/// Protocol code on board.
- #[cfg(target_os = "solaris")]
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
IFF_INTELLIGENT;
/// Slave of a load balancing bundle. (see
- /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
IFF_SLAVE;
/// Can't hear own transmissions.
@@ -97,7 +99,7 @@ libc_bitflags!(
target_os = "osx"))]
IFF_SIMPLEX;
/// Supports multicast. (see
- /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
IFF_MULTICAST;
/// Per link layer defined bit.
#[cfg(any(target_os = "dragonfly",
@@ -108,10 +110,10 @@ libc_bitflags!(
target_os = "ios"))]
IFF_LINK0;
/// Multicast using broadcast.
- #[cfg(any(target_os = "solaris"))]
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
IFF_MULTI_BCAST;
/// Is able to select media type via ifmap. (see
- /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
IFF_PORTSEL;
/// Per link layer defined bit.
@@ -123,10 +125,10 @@ libc_bitflags!(
target_os = "ios"))]
IFF_LINK1;
/// Non-unique address.
- #[cfg(any(target_os = "solaris"))]
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
IFF_UNNUMBERED;
/// Auto media selection active. (see
- /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
IFF_AUTOMEDIA;
/// Per link layer defined bit.
@@ -143,15 +145,15 @@ libc_bitflags!(
target_os = "macos",
target_os = "ios"))]
IFF_ALTPHYS;
- /// DHCP controlls interface.
- #[cfg(any(target_os = "solaris"))]
+ /// DHCP controls interface.
+ #[cfg(any(target_os = "solaris", target_os = "illumos"))]
IFF_DHCPRUNNING;
/// The addresses are lost when the interface goes down. (see
- /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
IFF_DYNAMIC;
/// Do not advertise.
- #[cfg(any(target_os = "solaris"))]
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
IFF_PRIVATE;
/// Driver signals L1 up. Volatile.
#[cfg(any(target_os = "fuchsia", target_os = "linux"))]
@@ -163,7 +165,7 @@ libc_bitflags!(
#[cfg(any(target_os = "freebsd"))]
IFF_CANTCONFIG;
/// Do not transmit packets.
- #[cfg(any(target_os = "solaris"))]
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
IFF_NOXMIT;
/// Driver signals dormant. Volatile.
#[cfg(any(target_os = "fuchsia", target_os = "linux"))]
@@ -172,7 +174,7 @@ libc_bitflags!(
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
IFF_PPROMISC;
/// Just on-link subnet.
- #[cfg(any(target_os = "solaris"))]
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
IFF_NOLOCAL;
/// Echo sent packets. Volatile.
#[cfg(any(target_os = "fuchsia", target_os = "linux"))]
@@ -181,19 +183,19 @@ libc_bitflags!(
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
IFF_MONITOR;
/// Address is deprecated.
- #[cfg(any(target_os = "solaris"))]
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
IFF_DEPRECATED;
/// Static ARP.
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
IFF_STATICARP;
/// Address from stateless addrconf.
- #[cfg(any(target_os = "solaris"))]
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
IFF_ADDRCONF;
/// Interface is in polling mode.
#[cfg(any(target_os = "dragonfly"))]
IFF_NPOLLING;
/// Router on interface.
- #[cfg(any(target_os = "solaris"))]
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
IFF_ROUTER;
/// Interface is in polling mode.
#[cfg(any(target_os = "dragonfly"))]
@@ -202,66 +204,208 @@ libc_bitflags!(
#[cfg(any(target_os = "freebsd"))]
IFF_DYING;
/// No NUD on interface.
- #[cfg(any(target_os = "solaris"))]
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
IFF_NONUD;
/// Interface is being renamed
#[cfg(any(target_os = "freebsd"))]
IFF_RENAMING;
/// Anycast address.
- #[cfg(any(target_os = "solaris"))]
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
IFF_ANYCAST;
/// Don't exchange routing info.
- #[cfg(any(target_os = "solaris"))]
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
IFF_NORTEXCH;
/// Do not provide packet information
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
IFF_NO_PI as libc::c_int;
- /// TUN device (no Ethernet headers)
+ /// TUN device (no Ethernet headers)
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
IFF_TUN as libc::c_int;
/// TAP device
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
IFF_TAP as libc::c_int;
/// IPv4 interface.
- #[cfg(any(target_os = "solaris"))]
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
IFF_IPV4;
/// IPv6 interface.
- #[cfg(any(target_os = "solaris"))]
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
IFF_IPV6;
/// in.mpathd test address
- #[cfg(any(target_os = "solaris"))]
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
IFF_NOFAILOVER;
/// Interface has failed
- #[cfg(any(target_os = "solaris"))]
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
IFF_FAILED;
/// Interface is a hot-spare
- #[cfg(any(target_os = "solaris"))]
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
IFF_STANDBY;
/// Functioning but not used
- #[cfg(any(target_os = "solaris"))]
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
IFF_INACTIVE;
/// Interface is offline
- #[cfg(any(target_os = "solaris"))]
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
IFF_OFFLINE;
- #[cfg(any(target_os = "solaris"))]
+ #[cfg(target_os = "solaris")]
IFF_COS_ENABLED;
/// Prefer as source addr.
- #[cfg(any(target_os = "solaris"))]
+ #[cfg(target_os = "solaris")]
IFF_PREFERRED;
/// RFC3041
- #[cfg(any(target_os = "solaris"))]
+ #[cfg(target_os = "solaris")]
IFF_TEMPORARY;
/// MTU set with SIOCSLIFMTU
- #[cfg(any(target_os = "solaris"))]
+ #[cfg(target_os = "solaris")]
IFF_FIXEDMTU;
/// Cannot send / receive packets
- #[cfg(any(target_os = "solaris"))]
+ #[cfg(target_os = "solaris")]
IFF_VIRTUAL;
/// Local address in use
- #[cfg(any(target_os = "solaris"))]
+ #[cfg(target_os = "solaris")]
IFF_DUPLICATE;
/// IPMP IP interface
- #[cfg(any(target_os = "solaris"))]
+ #[cfg(target_os = "solaris")]
IFF_IPMP;
}
);
+
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+))]
+mod if_nameindex {
+ use super::*;
+
+ use std::ffi::CStr;
+ use std::fmt;
+ use std::marker::PhantomData;
+ use std::ptr::NonNull;
+
+ /// A network interface. Has a name like "eth0" or "wlp4s0" or "wlan0", as well as an index
+ /// (1, 2, 3, etc) that identifies it in the OS's networking stack.
+ #[allow(missing_copy_implementations)]
+ #[repr(transparent)]
+ pub struct Interface(libc::if_nameindex);
+
+ impl Interface {
+ /// Obtain the index of this interface.
+ pub fn index(&self) -> c_uint {
+ self.0.if_index
+ }
+
+ /// Obtain the name of this interface.
+ pub fn name(&self) -> &CStr {
+ unsafe { CStr::from_ptr(self.0.if_name) }
+ }
+ }
+
+ impl fmt::Debug for Interface {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("Interface")
+ .field("index", &self.index())
+ .field("name", &self.name())
+ .finish()
+ }
+ }
+
+ /// A list of the network interfaces available on this system. Obtained from [`if_nameindex()`].
+ pub struct Interfaces {
+ ptr: NonNull<libc::if_nameindex>,
+ }
+
+ impl Interfaces {
+ /// Iterate over the interfaces in this list.
+ #[inline]
+ pub fn iter(&self) -> InterfacesIter<'_> {
+ self.into_iter()
+ }
+
+ /// Convert this to a slice of interfaces. Note that the underlying interfaces list is
+ /// null-terminated, so calling this calculates the length. If random access isn't needed,
+ /// [`Interfaces::iter()`] should be used instead.
+ pub fn to_slice(&self) -> &[Interface] {
+ let ifs = self.ptr.as_ptr() as *const Interface;
+ let len = self.iter().count();
+ unsafe { std::slice::from_raw_parts(ifs, len) }
+ }
+ }
+
+ impl Drop for Interfaces {
+ fn drop(&mut self) {
+ unsafe { libc::if_freenameindex(self.ptr.as_ptr()) };
+ }
+ }
+
+ impl fmt::Debug for Interfaces {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ self.to_slice().fmt(f)
+ }
+ }
+
+ impl<'a> IntoIterator for &'a Interfaces {
+ type IntoIter = InterfacesIter<'a>;
+ type Item = &'a Interface;
+ #[inline]
+ fn into_iter(self) -> Self::IntoIter {
+ InterfacesIter {
+ ptr: self.ptr.as_ptr(),
+ _marker: PhantomData,
+ }
+ }
+ }
+
+ /// An iterator over the interfaces in an [`Interfaces`].
+ #[derive(Debug)]
+ pub struct InterfacesIter<'a> {
+ ptr: *const libc::if_nameindex,
+ _marker: PhantomData<&'a Interfaces>,
+ }
+
+ impl<'a> Iterator for InterfacesIter<'a> {
+ type Item = &'a Interface;
+ #[inline]
+ fn next(&mut self) -> Option<Self::Item> {
+ unsafe {
+ if (*self.ptr).if_index == 0 {
+ None
+ } else {
+ let ret = &*(self.ptr as *const Interface);
+ self.ptr = self.ptr.add(1);
+ Some(ret)
+ }
+ }
+ }
+ }
+
+ /// Retrieve a list of the network interfaces available on the local system.
+ ///
+ /// ```
+ /// let interfaces = nix::net::if_::if_nameindex().unwrap();
+ /// for iface in &interfaces {
+ /// println!("Interface #{} is called {}", iface.index(), iface.name().to_string_lossy());
+ /// }
+ /// ```
+ pub fn if_nameindex() -> Result<Interfaces> {
+ unsafe {
+ let ifs = libc::if_nameindex();
+ let ptr = NonNull::new(ifs).ok_or_else(Error::last)?;
+ Ok(Interfaces { ptr })
+ }
+ }
+}
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+))]
+pub use if_nameindex::*;
diff --git a/src/poll.rs b/src/poll.rs
index be5bf22..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 @@ pub struct PollFd {
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 @@ impl PollFd {
}
}
- /// 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! {
@@ -51,12 +68,12 @@ libc_bitflags! {
/// Possibilities include:
///
/// * There is out-of-band data on a TCP socket (see
- /// [tcp(7)](http://man7.org/linux/man-pages/man7/tcp.7.html)).
+ /// [tcp(7)](https://man7.org/linux/man-pages/man7/tcp.7.html)).
/// * A pseudoterminal master in packet mode has seen a state
/// change on the slave (see
- /// [ioctl_tty(2)](http://man7.org/linux/man-pages/man2/ioctl_tty.2.html)).
+ /// [ioctl_tty(2)](https://man7.org/linux/man-pages/man2/ioctl_tty.2.html)).
/// * A cgroup.events file has been modified (see
- /// [cgroups(7)](http://man7.org/linux/man-pages/man7/cgroups.7.html)).
+ /// [cgroups(7)](https://man7.org/linux/man-pages/man7/cgroups.7.html)).
POLLPRI;
/// Writing is now possible, though a write larger that the
/// available space in a socket or pipe will still block (unless
@@ -96,7 +113,7 @@ libc_bitflags! {
}
/// `poll` waits for one of a set of file descriptors to become ready to perform I/O.
-/// ([`poll(2)`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/poll.html))
+/// ([`poll(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/poll.html))
///
/// `fds` contains all [`PollFd`](struct.PollFd.html) to poll.
/// The function will return as soon as any event occur for any of these `PollFd`s.
@@ -127,7 +144,7 @@ pub fn poll(fds: &mut [PollFd], timeout: libc::c_int) -> Result<libc::c_int> {
/// `ppoll()` allows an application to safely wait until either a file
/// descriptor becomes ready or until a signal is caught.
-/// ([`poll(2)`](http://man7.org/linux/man-pages/man2/poll.2.html))
+/// ([`poll(2)`](https://man7.org/linux/man-pages/man2/poll.2.html))
///
/// `ppoll` behaves like `poll`, but let you specify what signals may interrupt it
/// with the `sigmask` argument. If you want `ppoll` to block indefinitely,
diff --git a/src/pty.rs b/src/pty.rs
index d67518f..facc9aa 100644
--- a/src/pty.rs
+++ b/src/pty.rs
@@ -10,7 +10,7 @@ use std::os::unix::prelude::*;
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
@@ -70,7 +70,7 @@ impl Drop for PtyMaster {
// condition, which can cause confusing errors for future I/O
// operations.
let e = unistd::close(self.0);
- if e == Err(Error::Sys(Errno::EBADF)) {
+ if e == Err(Errno::EBADF) {
panic!("Closing an invalid file descriptor!");
};
}
@@ -78,13 +78,13 @@ impl Drop for PtyMaster {
impl io::Read for PtyMaster {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
- unistd::read(self.0, buf).map_err(|e| e.as_errno().unwrap().into())
+ unistd::read(self.0, buf).map_err(io::Error::from)
}
}
impl io::Write for PtyMaster {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
- unistd::write(self.0, buf).map_err(|e| e.as_errno().unwrap().into())
+ unistd::write(self.0, buf).map_err(io::Error::from)
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
@@ -92,21 +92,21 @@ impl io::Write for PtyMaster {
}
/// Grant access to a slave pseudoterminal (see
-/// [`grantpt(3)`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/grantpt.html))
+/// [`grantpt(3)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/grantpt.html))
///
/// `grantpt()` changes the mode and owner of the slave pseudoterminal device corresponding to the
/// master pseudoterminal referred to by `fd`. This is a necessary step towards opening the slave.
#[inline]
pub fn grantpt(fd: &PtyMaster) -> Result<()> {
if unsafe { libc::grantpt(fd.as_raw_fd()) } < 0 {
- return Err(Error::last());
+ return Err(Errno::last());
}
Ok(())
}
/// Open a pseudoterminal device (see
-/// [`posix_openpt(3)`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_openpt.html))
+/// [`posix_openpt(3)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_openpt.html))
///
/// `posix_openpt()` returns a file descriptor to an existing unused pseuterminal master device.
///
@@ -145,14 +145,14 @@ pub fn posix_openpt(flags: fcntl::OFlag) -> Result<PtyMaster> {
};
if fd < 0 {
- return Err(Error::last());
+ return Err(Errno::last());
}
Ok(PtyMaster(fd))
}
/// Get the name of the slave pseudoterminal (see
-/// [`ptsname(3)`](http://man7.org/linux/man-pages/man3/ptsname.3.html))
+/// [`ptsname(3)`](https://man7.org/linux/man-pages/man3/ptsname.3.html))
///
/// `ptsname()` returns the name of the slave pseudoterminal device corresponding to the master
/// referred to by `fd`.
@@ -171,7 +171,7 @@ pub fn posix_openpt(flags: fcntl::OFlag) -> Result<PtyMaster> {
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::last());
+ return Err(Errno::last());
}
let name = CStr::from_ptr(name_ptr);
@@ -179,7 +179,7 @@ pub unsafe fn ptsname(fd: &PtyMaster) -> Result<String> {
}
/// Get the name of the slave pseudoterminal (see
-/// [`ptsname(3)`](http://man7.org/linux/man-pages/man3/ptsname.3.html))
+/// [`ptsname(3)`](https://man7.org/linux/man-pages/man3/ptsname.3.html))
///
/// `ptsname_r()` returns the name of the slave pseudoterminal device corresponding to the master
/// referred to by `fd`. This is the threadsafe version of `ptsname()`, but it is not part of the
@@ -190,23 +190,22 @@ pub unsafe fn ptsname(fd: &PtyMaster) -> Result<String> {
#[cfg(any(target_os = "android", target_os = "linux"))]
#[inline]
pub fn ptsname_r(fd: &PtyMaster) -> Result<String> {
- let mut name_buf = vec![0u8; 64];
- let name_buf_ptr = name_buf.as_mut_ptr() as *mut libc::c_char;
- if unsafe { libc::ptsname_r(fd.as_raw_fd(), name_buf_ptr, name_buf.capacity()) } != 0 {
- return Err(Error::last());
- }
-
- // Find the first null-character terminating this string. This is guaranteed to succeed if the
- // return value of `libc::ptsname_r` is 0.
- let null_index = name_buf.iter().position(|c| *c == b'\0').unwrap();
- name_buf.truncate(null_index);
+ let mut name_buf = Vec::<libc::c_char>::with_capacity(64);
+ let name_buf_ptr = name_buf.as_mut_ptr();
+ let cname = unsafe {
+ let cap = name_buf.capacity();
+ if libc::ptsname_r(fd.as_raw_fd(), name_buf_ptr, cap) != 0 {
+ return Err(crate::Error::last());
+ }
+ CStr::from_ptr(name_buf.as_ptr())
+ };
- let name = String::from_utf8(name_buf)?;
+ let name = cname.to_string_lossy().into_owned();
Ok(name)
}
/// Unlock a pseudoterminal master/slave pseudoterminal pair (see
-/// [`unlockpt(3)`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/unlockpt.html))
+/// [`unlockpt(3)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlockpt.html))
///
/// `unlockpt()` unlocks the slave pseudoterminal device corresponding to the master pseudoterminal
/// referred to by `fd`. This must be called before trying to open the slave side of a
@@ -214,7 +213,7 @@ pub fn ptsname_r(fd: &PtyMaster) -> Result<String> {
#[inline]
pub fn unlockpt(fd: &PtyMaster) -> Result<()> {
if unsafe { libc::unlockpt(fd.as_raw_fd()) } < 0 {
- return Err(Error::last());
+ return Err(Errno::last());
}
Ok(())
@@ -223,7 +222,7 @@ pub fn unlockpt(fd: &PtyMaster) -> Result<()> {
/// Create a new pseudoterminal, returning the slave and master file descriptors
/// in `OpenptyResult`
-/// (see [`openpty`](http://man7.org/linux/man-pages/man3/openpty.3.html)).
+/// (see [`openpty`](https://man7.org/linux/man-pages/man3/openpty.3.html)).
///
/// If `winsize` is not `None`, the window size of the slave will be set to
/// the values in `winsize`. If `termios` is not `None`, the pseudoterminal's
@@ -297,12 +296,24 @@ pub fn openpty<'a, 'b, T: Into<Option<&'a Winsize>>, U: Into<Option<&'b Termios>
/// Create a new pseudoterminal, returning the master file descriptor and forked pid.
/// in `ForkptyResult`
-/// (see [`forkpty`](http://man7.org/linux/man-pages/man3/forkpty.3.html)).
+/// (see [`forkpty`](https://man7.org/linux/man-pages/man3/forkpty.3.html)).
///
/// If `winsize` is not `None`, the window size of the slave will be set to
/// the values in `winsize`. If `termios` is not `None`, the pseudoterminal's
/// terminal settings of the slave will be set to the values in `termios`.
-pub fn forkpty<'a, 'b, T: Into<Option<&'a Winsize>>, U: Into<Option<&'b Termios>>>(
+///
+/// # Safety
+///
+/// In a multithreaded program, only [async-signal-safe] functions like `pause`
+/// and `_exit` may be called by the child (the parent isn't restricted). Note
+/// that memory allocation may **not** be async-signal-safe and thus must be
+/// prevented.
+///
+/// Those functions are only a small subset of your operating system's API, so
+/// special care must be taken to only invoke code you can control and audit.
+///
+/// [async-signal-safe]: https://man7.org/linux/man-pages/man7/signal-safety.7.html
+pub unsafe fn forkpty<'a, 'b, T: Into<Option<&'a Winsize>>, U: Into<Option<&'b Termios>>>(
winsize: T,
termios: U,
) -> Result<ForkptyResult> {
@@ -323,20 +334,15 @@ pub fn forkpty<'a, 'b, T: Into<Option<&'a Winsize>>, U: Into<Option<&'b Termios>
.map(|ws| ws as *const Winsize as *mut _)
.unwrap_or(ptr::null_mut());
- let res = unsafe {
- libc::forkpty(master.as_mut_ptr(), ptr::null_mut(), term, win)
- };
+ let res = libc::forkpty(master.as_mut_ptr(), ptr::null_mut(), term, win);
let fork_result = Errno::result(res).map(|res| match res {
0 => ForkResult::Child,
res => ForkResult::Parent { child: Pid::from_raw(res) },
})?;
- unsafe {
- Ok(ForkptyResult {
- master: master.assume_init(),
- fork_result,
- })
- }
+ Ok(ForkptyResult {
+ master: master.assume_init(),
+ fork_result,
+ })
}
-
diff --git a/src/sched.rs b/src/sched.rs
index 3b48b4a..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 @@ mod sched_linux_like {
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 @@ mod sched_linux_like {
/// `field` is the CPU id to test
pub fn is_set(&self, field: usize) -> Result<bool> {
if field >= CpuSet::count() {
- Err(Error::Sys(Errno::EINVAL))
+ Err(Errno::EINVAL)
} else {
Ok(unsafe { libc::CPU_ISSET(field, &self.cpu_set) })
}
@@ -78,7 +119,7 @@ mod sched_linux_like {
/// `field` is the CPU id to add
pub fn set(&mut self, field: usize) -> Result<()> {
if field >= CpuSet::count() {
- Err(Error::Sys(Errno::EINVAL))
+ Err(Errno::EINVAL)
} else {
unsafe { libc::CPU_SET(field, &mut self.cpu_set); }
Ok(())
@@ -89,7 +130,7 @@ mod sched_linux_like {
/// `field` is the CPU id to remove
pub fn unset(&mut self, field: usize) -> Result<()> {
if field >= CpuSet::count() {
- Err(Error::Sys(Errno::EINVAL))
+ Err(Errno::EINVAL)
} else {
unsafe { libc::CPU_CLR(field, &mut self.cpu_set);}
Ok(())
@@ -97,7 +138,7 @@ mod sched_linux_like {
}
/// 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>()
}
}
@@ -109,7 +150,7 @@ mod sched_linux_like {
}
/// `sched_setaffinity` set a thread's CPU affinity mask
- /// ([`sched_setaffinity(2)`](http://man7.org/linux/man-pages/man2/sched_setaffinity.2.html))
+ /// ([`sched_setaffinity(2)`](https://man7.org/linux/man-pages/man2/sched_setaffinity.2.html))
///
/// `pid` is the thread ID to update.
/// If pid is zero, then the calling thread is updated.
@@ -142,7 +183,7 @@ mod sched_linux_like {
}
/// `sched_getaffinity` get a thread's CPU affinity mask
- /// ([`sched_getaffinity(2)`](http://man7.org/linux/man-pages/man2/sched_getaffinity.2.html))
+ /// ([`sched_getaffinity(2)`](https://man7.org/linux/man-pages/man2/sched_getaffinity.2.html))
///
/// `pid` is the thread ID to check.
/// If pid is zero, then the calling thread is checked.
@@ -176,6 +217,14 @@ mod sched_linux_like {
Errno::result(res).and(Ok(cpuset))
}
+ /// `clone` create a child process
+ /// ([`clone(2)`](https://man7.org/linux/man-pages/man2/clone.2.html))
+ ///
+ /// `stack` is a reference to an array which will hold the stack of the new
+ /// process. Unlike when calling `clone(2)` from C, the provided stack
+ /// address need not be the highest address of the region. Nix will take
+ /// care of that requirement. The user only needs to provide a reference to
+ /// a normally allocated buffer.
pub fn clone(
mut cb: CloneCb,
stack: &mut [u8],
@@ -204,12 +253,18 @@ mod sched_linux_like {
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()) };
@@ -219,7 +274,7 @@ mod sched_linux_like {
/// Explicitly yield the processor to other threads.
///
-/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/sched_yield.html)
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sched_yield.html)
pub fn sched_yield() -> Result<()> {
let res = unsafe { libc::sched_yield() };
diff --git a/src/sys/aio.rs b/src/sys/aio.rs
index 1afdb35..e64a2a8 100644
--- a/src/sys/aio.rs
+++ b/src/sys/aio.rs
@@ -21,15 +21,15 @@
//! [`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};
-use std::borrow::{Borrow, BorrowMut};
use std::fmt;
use std::fmt::Debug;
use std::marker::PhantomData;
use std::mem;
+use std::pin::Pin;
use std::ptr::{null, null_mut};
use crate::sys::signal::*;
use std::thread;
@@ -39,6 +39,7 @@ libc_enum! {
/// 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 @@ libc_enum! {
/// 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,
}
}
@@ -90,120 +95,31 @@ pub enum AioCancelStat {
AioAllDone = libc::AIO_ALLDONE,
}
-/// Owns (uniquely or shared) a memory buffer to keep it from `Drop`ing while
-/// the kernel has a pointer to it.
-pub enum Buffer<'a> {
- /// No buffer to own.
- ///
- /// Used for operations like `aio_fsync` that have no data, or for unsafe
- /// operations that work with raw pointers.
- None,
- /// Keeps a reference to a slice
- Phantom(PhantomData<&'a mut [u8]>),
- /// Generic thing that keeps a buffer from dropping
- BoxedSlice(Box<dyn Borrow<[u8]>>),
- /// Generic thing that keeps a mutable buffer from dropping
- BoxedMutSlice(Box<dyn BorrowMut<[u8]>>),
-}
+/// Newtype that adds Send and Sync to libc::aiocb, which contains raw pointers
+#[repr(transparent)]
+struct LibcAiocb(libc::aiocb);
-impl<'a> Debug for Buffer<'a> {
- // Note: someday it may be possible to Derive Debug for a trait object, but
- // not today.
- // https://github.com/rust-lang/rust/issues/1563
- fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
- match *self {
- Buffer::None => write!(fmt, "None"),
- Buffer::Phantom(p) => p.fmt(fmt),
- Buffer::BoxedSlice(ref bs) => {
- let borrowed : &dyn Borrow<[u8]> = bs.borrow();
- write!(fmt, "BoxedSlice({:?})",
- borrowed as *const dyn Borrow<[u8]>)
- },
- Buffer::BoxedMutSlice(ref bms) => {
- let borrowed : &dyn BorrowMut<[u8]> = bms.borrow();
- write!(fmt, "BoxedMutSlice({:?})",
- borrowed as *const dyn BorrowMut<[u8]>)
- }
- }
- }
-}
+unsafe impl Send for LibcAiocb {}
+unsafe impl Sync for LibcAiocb {}
/// AIO Control Block.
///
/// The basic structure used by all aio functions. Each `AioCb` represents one
/// I/O request.
pub struct AioCb<'a> {
- aiocb: libc::aiocb,
+ aiocb: LibcAiocb,
/// Tracks whether the buffer pointed to by `libc::aiocb.aio_buf` is mutable
mutable: bool,
/// Could this `AioCb` potentially have any in-kernel state?
in_progress: bool,
- /// Optionally keeps a reference to the data.
- ///
- /// Used to keep buffers from `Drop`'ing, and may be returned once the
- /// `AioCb` is completed by [`buffer`](#method.buffer).
- buffer: Buffer<'a>
+ _buffer: std::marker::PhantomData<&'a [u8]>,
+ _pin: std::marker::PhantomPinned
}
impl<'a> AioCb<'a> {
- /// Remove the inner `Buffer` and return it
- ///
- /// It is an error to call this method while the `AioCb` is still in
- /// progress.
- pub fn buffer(&mut self) -> Buffer<'a> {
- assert!(!self.in_progress);
- let mut x = Buffer::None;
- mem::swap(&mut self.buffer, &mut x);
- x
- }
-
- /// Remove the inner boxed slice, if any, and return it.
- ///
- /// The returned value will be the argument that was passed to
- /// `from_boxed_slice` when this `AioCb` was created.
- ///
- /// It is an error to call this method while the `AioCb` is still in
- /// progress.
- pub fn boxed_slice(&mut self) -> Option<Box<dyn Borrow<[u8]>>> {
- assert!(!self.in_progress, "Can't remove the buffer from an AioCb that's still in-progress. Did you forget to call aio_return?");
- if let Buffer::BoxedSlice(_) = self.buffer {
- let mut oldbuffer = Buffer::None;
- mem::swap(&mut self.buffer, &mut oldbuffer);
- if let Buffer::BoxedSlice(inner) = oldbuffer {
- Some(inner)
- } else {
- unreachable!();
- }
- } else {
- None
- }
- }
-
- /// Remove the inner boxed mutable slice, if any, and return it.
- ///
- /// The returned value will be the argument that was passed to
- /// `from_boxed_mut_slice` when this `AioCb` was created.
- ///
- /// It is an error to call this method while the `AioCb` is still in
- /// progress.
- pub fn boxed_mut_slice(&mut self) -> Option<Box<dyn BorrowMut<[u8]>>> {
- assert!(!self.in_progress, "Can't remove the buffer from an AioCb that's still in-progress. Did you forget to call aio_return?");
- if let Buffer::BoxedMutSlice(_) = self.buffer {
- let mut oldbuffer = Buffer::None;
- mem::swap(&mut self.buffer, &mut oldbuffer);
- if let Buffer::BoxedMutSlice(inner) = oldbuffer {
- Some(inner)
- } else {
- unreachable!();
- }
- } else {
- None
- }
- }
-
/// Returns the underlying file descriptor associated with the `AioCb`
pub fn fd(&self) -> RawFd {
- self.aiocb.aio_fildes
+ self.aiocb.0.aio_fildes
}
/// Constructs a new `AioCb` with no associated buffer.
@@ -232,28 +148,48 @@ impl<'a> AioCb<'a> {
/// # 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) -> AioCb<'a> {
+ sigev_notify: SigevNotify) -> Pin<Box<AioCb<'a>>> {
let mut a = AioCb::common_init(fd, prio, sigev_notify);
- a.aio_offset = 0;
- a.aio_nbytes = 0;
- a.aio_buf = null_mut();
+ a.0.aio_offset = 0;
+ a.0.aio_nbytes = 0;
+ a.0.aio_buf = null_mut();
- AioCb {
+ Box::pin(AioCb {
aiocb: a,
mutable: false,
in_progress: false,
- buffer: Buffer::None
+ _buffer: PhantomData,
+ _pin: std::marker::PhantomPinned
+ })
+ }
+
+ // Private helper
+ #[cfg(not(any(target_os = "ios", target_os = "macos")))]
+ fn from_mut_slice_unpinned(fd: RawFd, offs: off_t, buf: &'a mut [u8],
+ prio: libc::c_int, sigev_notify: SigevNotify,
+ opcode: LioOpcode) -> AioCb<'a>
+ {
+ let mut a = AioCb::common_init(fd, prio, sigev_notify);
+ a.0.aio_offset = offs;
+ a.0.aio_nbytes = buf.len() as size_t;
+ a.0.aio_buf = buf.as_ptr() as *mut c_void;
+ a.0.aio_lio_opcode = opcode as libc::c_int;
+
+ AioCb {
+ aiocb: a,
+ mutable: true,
+ in_progress: false,
+ _buffer: PhantomData,
+ _pin: std::marker::PhantomPinned
}
}
@@ -262,8 +198,7 @@ impl<'a> AioCb<'a> {
/// The resulting `AioCb` will be suitable for both read and write
/// operations, but only if the borrow checker can guarantee that the slice
/// will outlive the `AioCb`. That will usually be the case if the `AioCb`
- /// is stack-allocated. If the borrow checker gives you trouble, try using
- /// [`from_boxed_mut_slice`](#method.from_boxed_mut_slice) instead.
+ /// is stack-allocated.
///
/// # Parameters
///
@@ -292,7 +227,6 @@ impl<'a> AioCb<'a> {
/// # 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];
@@ -306,220 +240,29 @@ impl<'a> AioCb<'a> {
/// 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,
- opcode: LioOpcode) -> AioCb<'a> {
+ opcode: LioOpcode) -> Pin<Box<AioCb<'a>>> {
let mut a = AioCb::common_init(fd, prio, sigev_notify);
- a.aio_offset = offs;
- a.aio_nbytes = buf.len() as size_t;
- a.aio_buf = buf.as_ptr() as *mut c_void;
- a.aio_lio_opcode = opcode as libc::c_int;
+ a.0.aio_offset = offs;
+ a.0.aio_nbytes = buf.len() as size_t;
+ a.0.aio_buf = buf.as_ptr() as *mut c_void;
+ a.0.aio_lio_opcode = opcode as libc::c_int;
- AioCb {
+ Box::pin(AioCb {
aiocb: a,
mutable: true,
in_progress: false,
- buffer: Buffer::Phantom(PhantomData),
- }
- }
-
- /// The safest and most flexible way to create an `AioCb`.
- ///
- /// Unlike [`from_slice`], this method returns a structure suitable for
- /// placement on the heap. It may be used for write operations, but not
- /// read operations. Unlike `from_ptr`, this method will ensure that the
- /// buffer doesn't `drop` while the kernel is still processing it. Any
- /// object that can be borrowed as a boxed slice will work.
- ///
- /// # Parameters
- ///
- /// * `fd`: File descriptor. Required for all aio functions.
- /// * `offs`: File offset
- /// * `buf`: A boxed slice-like object
- /// * `prio`: If POSIX Prioritized IO is supported, then the
- /// operation will be prioritized at the process's
- /// priority level minus `prio`
- /// * `sigev_notify`: Determines how you will be notified of event
- /// completion.
- /// * `opcode`: This field is only used for `lio_listio`. It
- /// determines which operation to use for this individual
- /// aiocb
- ///
- /// # Examples
- ///
- /// Create an `AioCb` from a Vector and use it for writing
- ///
- /// ```
- /// # use nix::errno::Errno;
- /// # use nix::Error;
- /// # use nix::sys::aio::*;
- /// # use nix::sys::signal::SigevNotify;
- /// # use std::{thread, time};
- /// # use std::io::Write;
- /// # use std::os::unix::io::AsRawFd;
- /// # use tempfile::tempfile;
- /// # fn main() {
- /// let wbuf = Box::new(Vec::from("CDEF"));
- /// let expected_len = wbuf.len();
- /// let mut f = tempfile().unwrap();
- /// let mut aiocb = AioCb::from_boxed_slice( f.as_raw_fd(),
- /// 2, //offset
- /// wbuf,
- /// 0, //priority
- /// SigevNotify::SigevNone,
- /// LioOpcode::LIO_NOP);
- /// aiocb.write().unwrap();
- /// while (aiocb.error() == Err(Error::from(Errno::EINPROGRESS))) {
- /// thread::sleep(time::Duration::from_millis(10));
- /// }
- /// assert_eq!(aiocb.aio_return().unwrap() as usize, expected_len);
- /// # }
- /// ```
- ///
- /// Create an `AioCb` from a `Bytes` object
- ///
- /// ```
- /// # use bytes::Bytes;
- /// # use nix::sys::aio::*;
- /// # use nix::sys::signal::SigevNotify;
- /// # use std::os::unix::io::AsRawFd;
- /// # use tempfile::tempfile;
- /// # fn main() {
- /// let wbuf = Box::new(Bytes::from(&b"CDEF"[..]));
- /// let mut f = tempfile().unwrap();
- /// let mut aiocb = AioCb::from_boxed_slice( f.as_raw_fd(),
- /// 2, //offset
- /// wbuf,
- /// 0, //priority
- /// SigevNotify::SigevNone,
- /// LioOpcode::LIO_NOP);
- /// # }
- /// ```
- ///
- /// If a library needs to work with buffers that aren't `Box`ed, it can
- /// create a `Box`ed container for use with this method. Here's an example
- /// using an un`Box`ed `Bytes` object.
- ///
- /// ```
- /// # use bytes::Bytes;
- /// # use nix::sys::aio::*;
- /// # use nix::sys::signal::SigevNotify;
- /// # use std::borrow::Borrow;
- /// # use std::os::unix::io::AsRawFd;
- /// # use tempfile::tempfile;
- /// struct BytesContainer(Bytes);
- /// impl Borrow<[u8]> for BytesContainer {
- /// fn borrow(&self) -> &[u8] {
- /// self.0.as_ref()
- /// }
- /// }
- /// fn main() {
- /// let wbuf = Bytes::from(&b"CDEF"[..]);
- /// let boxed_wbuf = Box::new(BytesContainer(wbuf));
- /// let mut f = tempfile().unwrap();
- /// let mut aiocb = AioCb::from_boxed_slice( f.as_raw_fd(),
- /// 2, //offset
- /// boxed_wbuf,
- /// 0, //priority
- /// SigevNotify::SigevNone,
- /// LioOpcode::LIO_NOP);
- /// }
- /// ```
- ///
- /// [`from_slice`]: #method.from_slice
- pub fn from_boxed_slice(fd: RawFd, offs: off_t, buf: Box<dyn Borrow<[u8]>>,
- prio: libc::c_int, sigev_notify: SigevNotify,
- opcode: LioOpcode) -> AioCb<'a> {
- let mut a = AioCb::common_init(fd, prio, sigev_notify);
- {
- let borrowed : &dyn Borrow<[u8]> = buf.borrow();
- let slice : &[u8] = borrowed.borrow();
- a.aio_nbytes = slice.len() as size_t;
- a.aio_buf = slice.as_ptr() as *mut c_void;
- }
- a.aio_offset = offs;
- a.aio_lio_opcode = opcode as libc::c_int;
-
- AioCb {
- aiocb: a,
- mutable: false,
- in_progress: false,
- buffer: Buffer::BoxedSlice(buf),
- }
- }
-
- /// The safest and most flexible way to create an `AioCb` for reading.
- ///
- /// Like [`from_boxed_slice`], but the slice is a mutable one. More
- /// flexible than [`from_mut_slice`], because a wide range of objects can be
- /// used.
- ///
- /// # Examples
- ///
- /// Create an `AioCb` from a Vector and use it for reading
- ///
- /// ```
- /// # use nix::errno::Errno;
- /// # use nix::Error;
- /// # use nix::sys::aio::*;
- /// # use nix::sys::signal::SigevNotify;
- /// # use std::{thread, time};
- /// # use std::io::Write;
- /// # use std::os::unix::io::AsRawFd;
- /// # use tempfile::tempfile;
- /// # fn main() {
- /// const INITIAL: &[u8] = b"abcdef123456";
- /// const LEN: usize = 4;
- /// let rbuf = Box::new(vec![0; LEN]);
- /// let mut f = tempfile().unwrap();
- /// f.write_all(INITIAL).unwrap();
- /// let mut aiocb = AioCb::from_boxed_mut_slice( f.as_raw_fd(),
- /// 2, //offset
- /// rbuf,
- /// 0, //priority
- /// SigevNotify::SigevNone,
- /// LioOpcode::LIO_NOP);
- /// aiocb.read().unwrap();
- /// while (aiocb.error() == Err(Error::from(Errno::EINPROGRESS))) {
- /// thread::sleep(time::Duration::from_millis(10));
- /// }
- /// assert_eq!(aiocb.aio_return().unwrap() as usize, LEN);
- /// let mut buffer = aiocb.boxed_mut_slice().unwrap();
- /// const EXPECT: &[u8] = b"cdef";
- /// assert_eq!(buffer.borrow_mut(), EXPECT);
- /// # }
- /// ```
- ///
- /// [`from_boxed_slice`]: #method.from_boxed_slice
- /// [`from_mut_slice`]: #method.from_mut_slice
- pub fn from_boxed_mut_slice(fd: RawFd, offs: off_t,
- mut buf: Box<dyn BorrowMut<[u8]>>,
- prio: libc::c_int, sigev_notify: SigevNotify,
- opcode: LioOpcode) -> AioCb<'a> {
- let mut a = AioCb::common_init(fd, prio, sigev_notify);
- {
- let borrowed : &mut dyn BorrowMut<[u8]> = buf.borrow_mut();
- let slice : &mut [u8] = borrowed.borrow_mut();
- a.aio_nbytes = slice.len() as size_t;
- a.aio_buf = slice.as_mut_ptr() as *mut c_void;
- }
- a.aio_offset = offs;
- a.aio_lio_opcode = opcode as libc::c_int;
-
- AioCb {
- aiocb: a,
- mutable: true,
- in_progress: false,
- buffer: Buffer::BoxedMutSlice(buf),
- }
+ _buffer: PhantomData,
+ _pin: std::marker::PhantomPinned
+ })
}
/// Constructs a new `AioCb` from a mutable raw pointer
@@ -527,8 +270,7 @@ impl<'a> AioCb<'a> {
/// Unlike `from_mut_slice`, this method returns a structure suitable for
/// placement on the heap. It may be used for both reads and writes. Due
/// to its unsafety, this method is not recommended. It is most useful when
- /// heap allocation is required but for some reason the data cannot be
- /// wrapped in a `struct` that implements `BorrowMut<[u8]>`
+ /// heap allocation is required.
///
/// # Parameters
///
@@ -552,28 +294,27 @@ impl<'a> AioCb<'a> {
pub unsafe fn from_mut_ptr(fd: RawFd, offs: off_t,
buf: *mut c_void, len: usize,
prio: libc::c_int, sigev_notify: SigevNotify,
- opcode: LioOpcode) -> AioCb<'a> {
+ opcode: LioOpcode) -> Pin<Box<AioCb<'a>>> {
let mut a = AioCb::common_init(fd, prio, sigev_notify);
- a.aio_offset = offs;
- a.aio_nbytes = len;
- a.aio_buf = buf;
- a.aio_lio_opcode = opcode as libc::c_int;
+ a.0.aio_offset = offs;
+ a.0.aio_nbytes = len;
+ a.0.aio_buf = buf;
+ a.0.aio_lio_opcode = opcode as libc::c_int;
- AioCb {
+ Box::pin(AioCb {
aiocb: a,
mutable: true,
in_progress: false,
- buffer: Buffer::None
- }
+ _buffer: PhantomData,
+ _pin: std::marker::PhantomPinned,
+ })
}
/// Constructs a new `AioCb` from a raw pointer.
///
/// Unlike `from_slice`, this method returns a structure suitable for
/// placement on the heap. Due to its unsafety, this method is not
- /// recommended. It is most useful when heap allocation is required but for
- /// some reason the data cannot be wrapped in a `struct` that implements
- /// `Borrow<[u8]>`
+ /// recommended. It is most useful when heap allocation is required.
///
/// # Parameters
///
@@ -597,24 +338,49 @@ impl<'a> AioCb<'a> {
pub unsafe fn from_ptr(fd: RawFd, offs: off_t,
buf: *const c_void, len: usize,
prio: libc::c_int, sigev_notify: SigevNotify,
- opcode: LioOpcode) -> AioCb<'a> {
+ opcode: LioOpcode) -> Pin<Box<AioCb<'a>>> {
let mut a = AioCb::common_init(fd, prio, sigev_notify);
- a.aio_offset = offs;
- a.aio_nbytes = len;
+ a.0.aio_offset = offs;
+ a.0.aio_nbytes = len;
// casting a const ptr to a mutable ptr here is ok, because we set the
// AioCb's mutable field to false
- a.aio_buf = buf as *mut c_void;
- a.aio_lio_opcode = opcode as libc::c_int;
+ a.0.aio_buf = buf as *mut c_void;
+ a.0.aio_lio_opcode = opcode as libc::c_int;
+
+ Box::pin(AioCb {
+ aiocb: a,
+ mutable: false,
+ in_progress: false,
+ _buffer: PhantomData,
+ _pin: std::marker::PhantomPinned
+ })
+ }
+
+ // Private helper
+ fn from_slice_unpinned(fd: RawFd, offs: off_t, buf: &'a [u8],
+ prio: libc::c_int, sigev_notify: SigevNotify,
+ opcode: LioOpcode) -> AioCb
+ {
+ let mut a = AioCb::common_init(fd, prio, sigev_notify);
+ a.0.aio_offset = offs;
+ a.0.aio_nbytes = buf.len() as size_t;
+ // casting an immutable buffer to a mutable pointer looks unsafe,
+ // but technically its only unsafe to dereference it, not to create
+ // it.
+ a.0.aio_buf = buf.as_ptr() as *mut c_void;
+ assert!(opcode != LioOpcode::LIO_READ, "Can't read into an immutable buffer");
+ a.0.aio_lio_opcode = opcode as libc::c_int;
AioCb {
aiocb: a,
mutable: false,
in_progress: false,
- buffer: Buffer::None
+ _buffer: PhantomData,
+ _pin: std::marker::PhantomPinned
}
}
- /// Like `from_mut_slice`, but works on constant slices rather than
+ /// Like [`AioCb::from_mut_slice`], but works on constant slices rather than
/// mutable slices.
///
/// An `AioCb` created this way cannot be used with `read`, and its
@@ -634,7 +400,6 @@ impl<'a> AioCb<'a> {
/// # 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(),
@@ -644,11 +409,10 @@ impl<'a> AioCb<'a> {
/// 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
@@ -657,27 +421,14 @@ impl<'a> AioCb<'a> {
// AioCb, and they must all be of the same type.
pub fn from_slice(fd: RawFd, offs: off_t, buf: &'a [u8],
prio: libc::c_int, sigev_notify: SigevNotify,
- opcode: LioOpcode) -> AioCb {
- let mut a = AioCb::common_init(fd, prio, sigev_notify);
- a.aio_offset = offs;
- a.aio_nbytes = buf.len() as size_t;
- // casting an immutable buffer to a mutable pointer looks unsafe,
- // but technically its only unsafe to dereference it, not to create
- // it.
- a.aio_buf = buf.as_ptr() as *mut c_void;
- assert!(opcode != LioOpcode::LIO_READ, "Can't read into an immutable buffer");
- a.aio_lio_opcode = opcode as libc::c_int;
-
- AioCb {
- aiocb: a,
- mutable: false,
- in_progress: false,
- buffer: Buffer::None,
- }
+ opcode: LioOpcode) -> Pin<Box<AioCb>>
+ {
+ Box::pin(AioCb::from_slice_unpinned(fd, offs, buf, prio, sigev_notify,
+ opcode))
}
fn common_init(fd: RawFd, prio: libc::c_int,
- sigev_notify: SigevNotify) -> libc::aiocb {
+ sigev_notify: SigevNotify) -> LibcAiocb {
// Use mem::zeroed instead of explicitly zeroing each field, because the
// number and name of reserved fields is OS-dependent. On some OSes,
// some reserved fields are used the kernel for state, and must be
@@ -686,12 +437,18 @@ impl<'a> AioCb<'a> {
a.aio_fildes = fd;
a.aio_reqprio = prio;
a.aio_sigevent = SigEvent::new(sigev_notify).sigevent();
- a
+ LibcAiocb(a)
}
/// Update the notification settings for an existing `aiocb`
- pub fn set_sigev_notify(&mut self, sigev_notify: SigevNotify) {
- self.aiocb.aio_sigevent = SigEvent::new(sigev_notify).sigevent();
+ pub fn set_sigev_notify(self: &mut Pin<Box<Self>>,
+ sigev_notify: SigevNotify)
+ {
+ // Safe because we don't move any of the data
+ let selfp = unsafe {
+ self.as_mut().get_unchecked_mut()
+ };
+ selfp.aiocb.0.aio_sigevent = SigEvent::new(sigev_notify).sigevent();
}
/// Cancels an outstanding AIO request.
@@ -717,7 +474,6 @@ impl<'a> AioCb<'a> {
/// # 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(),
@@ -729,28 +485,43 @@ impl<'a> AioCb<'a> {
/// 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
///
- /// [aio_cancel](http://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_cancel.html)
- pub fn cancel(&mut self) -> Result<AioCancelStat> {
- match unsafe { libc::aio_cancel(self.aiocb.aio_fildes, &mut self.aiocb) } {
+ /// [aio_cancel](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_cancel.html)
+ pub fn cancel(self: &mut Pin<Box<Self>>) -> Result<AioCancelStat> {
+ let r = unsafe {
+ let selfp = self.as_mut().get_unchecked_mut();
+ libc::aio_cancel(selfp.aiocb.0.aio_fildes, &mut selfp.aiocb.0)
+ };
+ match r {
libc::AIO_CANCELED => Ok(AioCancelStat::AioCanceled),
libc::AIO_NOTCANCELED => Ok(AioCancelStat::AioNotCanceled),
libc::AIO_ALLDONE => Ok(AioCancelStat::AioAllDone),
- -1 => Err(Error::last()),
+ -1 => Err(Errno::last()),
_ => panic!("unknown aio_cancel return value")
}
}
+ fn error_unpinned(&mut self) -> Result<()> {
+ let r = unsafe {
+ libc::aio_error(&mut self.aiocb.0 as *mut libc::aiocb)
+ };
+ match r {
+ 0 => Ok(()),
+ num if num > 0 => Err(Errno::from_i32(num)),
+ -1 => Err(Errno::last()),
+ num => panic!("unknown aio_error return value {:?}", num)
+ }
+ }
+
/// Retrieve error status of an asynchronous operation.
///
/// If the request has not yet completed, returns `EINPROGRESS`. Otherwise,
@@ -769,7 +540,6 @@ impl<'a> AioCb<'a> {
/// # 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(),
@@ -779,37 +549,39 @@ impl<'a> AioCb<'a> {
/// 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
///
- /// [aio_error](http://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_error.html)
- pub fn error(&mut self) -> Result<()> {
- match unsafe { libc::aio_error(&mut self.aiocb as *mut libc::aiocb) } {
- 0 => Ok(()),
- num if num > 0 => Err(Error::from_errno(Errno::from_i32(num))),
- -1 => Err(Error::last()),
- num => panic!("unknown aio_error return value {:?}", num)
- }
+ /// [aio_error](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_error.html)
+ pub fn error(self: &mut Pin<Box<Self>>) -> Result<()> {
+ // Safe because error_unpinned doesn't move the data
+ let selfp = unsafe {
+ self.as_mut().get_unchecked_mut()
+ };
+ selfp.error_unpinned()
}
/// An asynchronous version of `fsync(2)`.
///
/// # References
///
- /// [aio_fsync](http://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_fsync.html)
- pub fn fsync(&mut self, mode: AioFsyncMode) -> Result<()> {
- let p: *mut libc::aiocb = &mut self.aiocb;
- Errno::result(unsafe {
+ /// [aio_fsync](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_fsync.html)
+ pub fn fsync(self: &mut Pin<Box<Self>>, mode: AioFsyncMode) -> Result<()> {
+ // Safe because we don't move the libc::aiocb
+ unsafe {
+ let selfp = self.as_mut().get_unchecked_mut();
+ Errno::result({
+ let p: *mut libc::aiocb = &mut selfp.aiocb.0;
libc::aio_fsync(mode as libc::c_int, p)
- }).map(|_| {
- self.in_progress = true;
- })
+ }).map(|_| {
+ selfp.in_progress = true;
+ })
+ }
}
/// Returns the `aiocb`'s `LioOpcode` field
@@ -817,7 +589,7 @@ impl<'a> AioCb<'a> {
/// If the value cannot be represented as an `LioOpcode`, returns `None`
/// instead.
pub fn lio_opcode(&self) -> Option<LioOpcode> {
- match self.aiocb.aio_lio_opcode {
+ match self.aiocb.0.aio_lio_opcode {
libc::LIO_READ => Some(LioOpcode::LIO_READ),
libc::LIO_WRITE => Some(LioOpcode::LIO_WRITE),
libc::LIO_NOP => Some(LioOpcode::LIO_NOP),
@@ -831,37 +603,49 @@ impl<'a> AioCb<'a> {
/// number of bytes actually read or written by a completed operation, use
/// `aio_return` instead.
pub fn nbytes(&self) -> usize {
- self.aiocb.aio_nbytes
+ self.aiocb.0.aio_nbytes
}
/// Returns the file offset stored in the `AioCb`
pub fn offset(&self) -> off_t {
- self.aiocb.aio_offset
+ self.aiocb.0.aio_offset
}
/// Returns the priority of the `AioCb`
pub fn priority(&self) -> libc::c_int {
- self.aiocb.aio_reqprio
+ self.aiocb.0.aio_reqprio
}
/// Asynchronously reads from a file descriptor into a buffer
///
/// # References
///
- /// [aio_read](http://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_read.html)
- pub fn read(&mut self) -> Result<()> {
+ /// [aio_read](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_read.html)
+ pub fn read(self: &mut Pin<Box<Self>>) -> Result<()> {
assert!(self.mutable, "Can't read into an immutable buffer");
- let p: *mut libc::aiocb = &mut self.aiocb;
- Errno::result(unsafe {
- libc::aio_read(p)
+ // Safe because we don't move anything
+ let selfp = unsafe {
+ self.as_mut().get_unchecked_mut()
+ };
+ Errno::result({
+ let p: *mut libc::aiocb = &mut selfp.aiocb.0;
+ unsafe { libc::aio_read(p) }
}).map(|_| {
- self.in_progress = true;
+ selfp.in_progress = true;
})
}
/// Returns the `SigEvent` stored in the `AioCb`
pub fn sigevent(&self) -> SigEvent {
- SigEvent::from(&self.aiocb.aio_sigevent)
+ SigEvent::from(&self.aiocb.0.aio_sigevent)
+ }
+
+ fn aio_return_unpinned(&mut self) -> Result<isize> {
+ unsafe {
+ let p: *mut libc::aiocb = &mut self.aiocb.0;
+ self.in_progress = false;
+ Errno::result(libc::aio_return(p))
+ }
}
/// Retrieve return status of an asynchronous operation.
@@ -872,28 +656,33 @@ impl<'a> AioCb<'a> {
///
/// # References
///
- /// [aio_return](http://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_return.html)
+ /// [aio_return](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_return.html)
// Note: this should be just `return`, but that's a reserved word
- pub fn aio_return(&mut self) -> Result<isize> {
- let p: *mut libc::aiocb = &mut self.aiocb;
- self.in_progress = false;
- Errno::result(unsafe { libc::aio_return(p) })
+ pub fn aio_return(self: &mut Pin<Box<Self>>) -> Result<isize> {
+ // Safe because aio_return_unpinned does not move the data
+ let selfp = unsafe {
+ self.as_mut().get_unchecked_mut()
+ };
+ selfp.aio_return_unpinned()
}
/// Asynchronously writes from a buffer to a file descriptor
///
/// # References
///
- /// [aio_write](http://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_write.html)
- pub fn write(&mut self) -> Result<()> {
- let p: *mut libc::aiocb = &mut self.aiocb;
- Errno::result(unsafe {
- libc::aio_write(p)
+ /// [aio_write](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_write.html)
+ pub fn write(self: &mut Pin<Box<Self>>) -> Result<()> {
+ // Safe because we don't move anything
+ let selfp = unsafe {
+ self.as_mut().get_unchecked_mut()
+ };
+ Errno::result({
+ let p: *mut libc::aiocb = &mut selfp.aiocb.0;
+ unsafe{ libc::aio_write(p) }
}).map(|_| {
- self.in_progress = true;
+ selfp.in_progress = true;
})
}
-
}
/// Cancels outstanding AIO requests for a given file descriptor.
@@ -912,7 +701,6 @@ impl<'a> AioCb<'a> {
/// # 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(),
@@ -924,24 +712,23 @@ impl<'a> AioCb<'a> {
/// 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
///
-/// [`aio_cancel`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_cancel.html)
+/// [`aio_cancel`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_cancel.html)
pub fn aio_cancel_all(fd: RawFd) -> Result<AioCancelStat> {
match unsafe { libc::aio_cancel(fd, null_mut()) } {
libc::AIO_CANCELED => Ok(AioCancelStat::AioCanceled),
libc::AIO_NOTCANCELED => Ok(AioCancelStat::AioNotCanceled),
libc::AIO_ALLDONE => Ok(AioCancelStat::AioAllDone),
- -1 => Err(Error::last()),
+ -1 => Err(Errno::last()),
_ => panic!("unknown aio_cancel return value")
}
}
@@ -960,7 +747,6 @@ pub fn aio_cancel_all(fd: RawFd) -> Result<AioCancelStat> {
/// # 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(),
@@ -970,15 +756,14 @@ pub fn aio_cancel_all(fd: RawFd) -> Result<AioCancelStat> {
/// SigevNotify::SigevNone,
/// LioOpcode::LIO_NOP);
/// aiocb.write().unwrap();
-/// aio_suspend(&[&aiocb], None).expect("aio_suspend failed");
+/// aio_suspend(&[aiocb.as_ref()], None).expect("aio_suspend failed");
/// assert_eq!(aiocb.aio_return().unwrap() as usize, WBUF.len());
-/// # }
/// ```
/// # References
///
-/// [`aio_suspend`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_suspend.html)
-pub fn aio_suspend(list: &[&AioCb], timeout: Option<TimeSpec>) -> Result<()> {
- let plist = list as *const [&AioCb] as *const [*const libc::aiocb];
+/// [`aio_suspend`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_suspend.html)
+pub fn aio_suspend(list: &[Pin<&AioCb>], timeout: Option<TimeSpec>) -> Result<()> {
+ let plist = list as *const [Pin<&AioCb>] as *const [*const libc::aiocb];
let p = plist as *const *const libc::aiocb;
let timep = match timeout {
None => null::<libc::timespec>(),
@@ -992,7 +777,7 @@ pub fn aio_suspend(list: &[&AioCb], timeout: Option<TimeSpec>) -> Result<()> {
impl<'a> Debug for AioCb<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("AioCb")
- .field("aiocb", &self.aiocb)
+ .field("aiocb", &self.aiocb.0)
.field("mutable", &self.mutable)
.field("in_progress", &self.in_progress)
.finish()
@@ -1018,7 +803,9 @@ pub struct LioCb<'a> {
///
/// [`AioCb`]: struct.AioCb.html
/// [`listio`]: #method.listio
- pub aiocbs: Vec<AioCb<'a>>,
+ // Their locations in memory must be fixed once they are passed to the
+ // kernel. So this field must be non-public so the user can't swap.
+ aiocbs: Box<[AioCb<'a>]>,
/// The actual list passed to `libc::lio_listio`.
///
@@ -1032,15 +819,24 @@ pub struct LioCb<'a> {
results: Vec<Option<Result<isize>>>
}
+/// LioCb can't automatically impl Send and Sync just because of the raw
+/// pointers in list. But that's stupid. There's no reason that raw pointers
+/// should automatically be non-Send
+#[cfg(not(any(target_os = "ios", target_os = "macos")))]
+unsafe impl<'a> Send for LioCb<'a> {}
+#[cfg(not(any(target_os = "ios", target_os = "macos")))]
+unsafe impl<'a> Sync for LioCb<'a> {}
+
#[cfg(not(any(target_os = "ios", target_os = "macos")))]
impl<'a> LioCb<'a> {
- /// Initialize an empty `LioCb`
- pub fn with_capacity(capacity: usize) -> LioCb<'a> {
- LioCb {
- aiocbs: Vec::with_capacity(capacity),
- list: Vec::with_capacity(capacity),
- results: Vec::with_capacity(capacity)
- }
+ /// Are no [`AioCb`]s contained?
+ pub fn is_empty(&self) -> bool {
+ self.aiocbs.is_empty()
+ }
+
+ /// Return the number of individual [`AioCb`]s contained.
+ pub fn len(&self) -> usize {
+ self.aiocbs.len()
}
/// Submits multiple asynchronous I/O requests with a single system call.
@@ -1066,25 +862,25 @@ impl<'a> LioCb<'a> {
/// # 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 = LioCb::with_capacity(1);
- /// liocb.aiocbs.push(AioCb::from_slice( f.as_raw_fd(),
- /// 2, //offset
- /// WBUF,
- /// 0, //priority
- /// SigevNotify::SigevNone,
- /// LioOpcode::LIO_WRITE));
+ /// let mut liocb = LioCbBuilder::with_capacity(1)
+ /// .emplace_slice(
+ /// f.as_raw_fd(),
+ /// 2, //offset
+ /// WBUF,
+ /// 0, //priority
+ /// SigevNotify::SigevNone,
+ /// LioOpcode::LIO_WRITE
+ /// ).finish();
/// liocb.listio(LioMode::LIO_WAIT,
/// SigevNotify::SigevNone).unwrap();
/// assert_eq!(liocb.aio_return(0).unwrap() as usize, WBUF.len());
- /// # }
/// ```
///
/// # References
///
- /// [`lio_listio`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/lio_listio.html)
+ /// [`lio_listio`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/lio_listio.html)
///
/// [`aio_suspend`]: fn.aio_suspend.html
/// [`AioCb::error`]: struct.AioCb.html#method.error
@@ -1093,7 +889,7 @@ impl<'a> LioCb<'a> {
let sigev = SigEvent::new(sigev_notify);
let sigevp = &mut sigev.sigevent() as *mut libc::sigevent;
self.list.clear();
- for a in &mut self.aiocbs {
+ for a in &mut self.aiocbs.iter_mut() {
a.in_progress = true;
self.list.push(a as *mut AioCb<'a>
as *mut libc::aiocb);
@@ -1124,31 +920,31 @@ impl<'a> LioCb<'a> {
/// # 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 = LioCb::with_capacity(1);
- /// liocb.aiocbs.push(AioCb::from_slice( f.as_raw_fd(),
- /// 2, //offset
- /// WBUF,
- /// 0, //priority
- /// SigevNotify::SigevNone,
- /// LioOpcode::LIO_WRITE));
+ /// let mut liocb = LioCbBuilder::with_capacity(1)
+ /// .emplace_slice(
+ /// f.as_raw_fd(),
+ /// 2, //offset
+ /// WBUF,
+ /// 0, //priority
+ /// SigevNotify::SigevNone,
+ /// LioOpcode::LIO_WRITE
+ /// ).finish();
/// let mut err = liocb.listio(LioMode::LIO_WAIT, SigevNotify::SigevNone);
- /// while err == Err(Error::Sys(Errno::EIO)) ||
- /// err == Err(Error::Sys(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
///
- /// [`lio_listio`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/lio_listio.html)
+ /// [`lio_listio`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/lio_listio.html)
///
- /// [`lio_listio`]: http://pubs.opengroup.org/onlinepubs/9699919799/functions/lio_listio.html
+ /// [`lio_listio`]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/lio_listio.html
/// [`LioCb::aio_return`]: struct.LioCb.html#method.aio_return
// Note: the addresses of any EINPROGRESS or EOK aiocbs _must_ not be
// changed by this method, because the kernel relies on their addresses
@@ -1170,18 +966,18 @@ impl<'a> LioCb<'a> {
// Already collected final status for this operation
continue;
}
- match a.error() {
+ match a.error_unpinned() {
Ok(()) => {
// aiocb is complete; collect its status and don't resubmit
- self.results[i] = Some(a.aio_return());
+ self.results[i] = Some(a.aio_return_unpinned());
},
- Err(Error::Sys(Errno::EAGAIN)) => {
+ Err(Errno::EAGAIN) => {
self.list.push(a as *mut AioCb<'a> as *mut libc::aiocb);
},
- Err(Error::Sys(Errno::EINPROGRESS)) => {
+ Err(Errno::EINPROGRESS) => {
// aiocb is was successfully queued; no need to do anything
},
- Err(Error::Sys(Errno::EINVAL)) => panic!(
+ Err(Errno::EINVAL) => panic!(
"AioCb was never submitted, or already finalized"),
_ => unreachable!()
}
@@ -1202,7 +998,7 @@ impl<'a> LioCb<'a> {
/// [`LioCb::listio_resubmit`]: #method.listio_resubmit
pub fn aio_return(&mut self, i: usize) -> Result<isize> {
if i >= self.results.len() || self.results[i].is_none() {
- self.aiocbs[i].aio_return()
+ self.aiocbs[i].aio_return_unpinned()
} else {
self.results[i].unwrap()
}
@@ -1218,7 +1014,7 @@ impl<'a> LioCb<'a> {
/// [`LioCb::listio_resubmit`]: #method.listio_resubmit
pub fn error(&mut self, i: usize) -> Result<()> {
if i >= self.results.len() || self.results[i].is_none() {
- self.aiocbs[i].error()
+ self.aiocbs[i].error_unpinned()
} else {
Ok(())
}
@@ -1234,13 +1030,93 @@ impl<'a> Debug for LioCb<'a> {
}
}
+/// Used to construct `LioCb`
+// This must be a separate class from LioCb due to pinning constraints. LioCb
+// must use a boxed slice of AioCbs so they will have stable storage, but
+// LioCbBuilder must use a Vec to make construction possible when the final size
+// is unknown.
#[cfg(not(any(target_os = "ios", target_os = "macos")))]
-impl<'a> From<Vec<AioCb<'a>>> for LioCb<'a> {
- fn from(src: Vec<AioCb<'a>>) -> LioCb<'a> {
+#[derive(Debug)]
+pub struct LioCbBuilder<'a> {
+ /// A collection of [`AioCb`]s.
+ ///
+ /// [`AioCb`]: struct.AioCb.html
+ pub aiocbs: Vec<AioCb<'a>>,
+}
+
+#[cfg(not(any(target_os = "ios", target_os = "macos")))]
+impl<'a> LioCbBuilder<'a> {
+ /// Initialize an empty `LioCb`
+ pub fn with_capacity(capacity: usize) -> LioCbBuilder<'a> {
+ LioCbBuilder {
+ aiocbs: Vec::with_capacity(capacity),
+ }
+ }
+
+ /// Add a new operation on an immutable slice to the [`LioCb`] under
+ /// construction.
+ ///
+ /// Arguments are the same as for [`AioCb::from_slice`]
+ ///
+ /// [`LioCb`]: struct.LioCb.html
+ /// [`AioCb::from_slice`]: struct.AioCb.html#method.from_slice
+ pub fn emplace_slice(mut self, fd: RawFd, offs: off_t, buf: &'a [u8],
+ prio: libc::c_int, sigev_notify: SigevNotify,
+ opcode: LioOpcode) -> Self
+ {
+ self.aiocbs.push(AioCb::from_slice_unpinned(fd, offs, buf, prio,
+ sigev_notify, opcode));
+ self
+ }
+
+ /// Add a new operation on a mutable slice to the [`LioCb`] under
+ /// construction.
+ ///
+ /// Arguments are the same as for [`AioCb::from_mut_slice`]
+ ///
+ /// [`LioCb`]: struct.LioCb.html
+ /// [`AioCb::from_mut_slice`]: struct.AioCb.html#method.from_mut_slice
+ pub fn emplace_mut_slice(mut self, fd: RawFd, offs: off_t,
+ buf: &'a mut [u8], prio: libc::c_int,
+ sigev_notify: SigevNotify, opcode: LioOpcode)
+ -> Self
+ {
+ self.aiocbs.push(AioCb::from_mut_slice_unpinned(fd, offs, buf, prio,
+ sigev_notify, opcode));
+ self
+ }
+
+ /// Finalize this [`LioCb`].
+ ///
+ /// Afterwards it will be possible to issue the operations with
+ /// [`LioCb::listio`]. Conversely, it will no longer be possible to add new
+ /// operations with [`LioCbBuilder::emplace_slice`] or
+ /// [`LioCbBuilder::emplace_mut_slice`].
+ ///
+ /// [`LioCb::listio`]: struct.LioCb.html#method.listio
+ /// [`LioCb::from_mut_slice`]: struct.LioCb.html#method.from_mut_slice
+ /// [`LioCb::from_slice`]: struct.LioCb.html#method.from_slice
+ pub fn finish(self) -> LioCb<'a> {
+ let len = self.aiocbs.len();
LioCb {
- list: Vec::with_capacity(src.capacity()),
- results: Vec::with_capacity(src.capacity()),
- aiocbs: src,
+ aiocbs: self.aiocbs.into(),
+ list: Vec::with_capacity(len),
+ results: Vec::with_capacity(len)
}
}
}
+
+#[cfg(not(any(target_os = "ios", target_os = "macos")))]
+#[cfg(test)]
+mod t {
+ use super::*;
+
+ // It's important that `LioCb` be `UnPin`. The tokio-file crate relies on
+ // it.
+ #[test]
+ fn liocb_is_unpin() {
+ use assert_impl::assert_impl;
+
+ assert_impl!(Unpin: LioCb);
+ }
+}
diff --git a/src/sys/epoll.rs b/src/sys/epoll.rs
index 2437bbe..6bc2a25 100644
--- a/src/sys/epoll.rs
+++ b/src/sys/epoll.rs
@@ -4,7 +4,6 @@ use libc::{self, c_int};
use std::os::unix::io::RawFd;
use std::ptr;
use std::mem;
-use crate::Error;
libc_bitflags!(
pub struct EpollFlags: c_int {
@@ -30,6 +29,7 @@ libc_bitflags!(
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[repr(i32)]
+#[non_exhaustive]
pub enum EpollOp {
EpollCtlAdd = libc::EPOLL_CTL_ADD,
EpollCtlDel = libc::EPOLL_CTL_DEL,
@@ -86,7 +86,7 @@ pub fn epoll_ctl<'a, T>(epfd: RawFd, op: EpollOp, fd: RawFd, event: T) -> Result
{
let mut event: Option<&mut EpollEvent> = event.into();
if event.is_none() && op != EpollOp::EpollCtlDel {
- Err(Error::Sys(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 crate::{Errno, Result};
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 @@ type type_of_event_filter = i16;
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 @@ libc_enum! {
EVFILT_VNODE,
EVFILT_WRITE,
}
+ impl TryFrom<type_of_event_filter>
}
#[cfg(any(target_os = "dragonfly", target_os = "freebsd",
@@ -232,8 +234,8 @@ impl KEvent {
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 @@ pub fn ev_set(ev: &mut KEvent,
#[test]
fn test_struct_kevent() {
+ use std::mem;
+
let udata : intptr_t = 12345;
let actual = KEvent::new(0xdead_beef,
@@ -321,10 +325,24 @@ fn test_struct_kevent() {
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/inotify.rs b/src/sys/inotify.rs
index 4880a4a..3f5ae22 100644
--- a/src/sys/inotify.rs
+++ b/src/sys/inotify.rs
@@ -2,8 +2,8 @@
//!
//! Inotify is a Linux-only API to monitor filesystems events.
//!
-//! For more documentation, please read [inotify(7)](http://man7.org/linux/man-pages/man7/inotify.7.html).
-//!
+//! For more documentation, please read [inotify(7)](https://man7.org/linux/man-pages/man7/inotify.7.html).
+//!
//! # Examples
//!
//! Monitor all events happening in directory "test":
@@ -86,7 +86,7 @@ pub struct Inotify {
/// This object is returned when you create a new watch on an inotify instance.
/// It is then returned as part of an event once triggered. It allows you to
-/// know which watch triggered which event.
+/// know which watch triggered which event.
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct WatchDescriptor {
wd: i32
@@ -94,18 +94,18 @@ pub struct WatchDescriptor {
/// A single inotify event.
///
-/// For more documentation see, [inotify(7)](http://man7.org/linux/man-pages/man7/inotify.7.html).
+/// For more documentation see, [inotify(7)](https://man7.org/linux/man-pages/man7/inotify.7.html).
#[derive(Debug)]
pub struct InotifyEvent {
/// Watch descriptor. This field corresponds to the watch descriptor you
/// were issued when calling add_watch. It allows you to know which watch
- /// this event comes from.
+ /// this event comes from.
pub wd: WatchDescriptor,
/// Event mask. This field is a bitfield describing the exact event that
/// occured.
pub mask: AddWatchFlags,
/// This cookie is a number that allows you to connect related events. For
- /// now only IN_MOVED_FROM and IN_MOVED_TO can be connected.
+ /// now only IN_MOVED_FROM and IN_MOVED_TO can be connected.
pub cookie: u32,
/// Filename. This field exists only if the event was triggered for a file
/// inside the watched directory.
@@ -117,7 +117,7 @@ impl Inotify {
///
/// Returns a Result containing an inotify instance.
///
- /// For more information see, [inotify_init(2)](http://man7.org/linux/man-pages/man2/inotify_init.2.html).
+ /// For more information see, [inotify_init(2)](https://man7.org/linux/man-pages/man2/inotify_init.2.html).
pub fn init(flags: InitFlags) -> Result<Inotify> {
let res = Errno::result(unsafe {
libc::inotify_init1(flags.bits())
@@ -126,14 +126,14 @@ impl Inotify {
res.map(|fd| Inotify { fd })
}
- /// Adds a new watch on the target file or directory.
+ /// Adds a new watch on the target file or directory.
///
- /// Returns a watch descriptor. This is not a File Descriptor!
+ /// Returns a watch descriptor. This is not a File Descriptor!
///
- /// For more information see, [inotify_add_watch(2)](http://man7.org/linux/man-pages/man2/inotify_add_watch.2.html).
+ /// For more information see, [inotify_add_watch(2)](https://man7.org/linux/man-pages/man2/inotify_add_watch.2.html).
pub fn add_watch<P: ?Sized + NixPath>(self,
path: &P,
- mask: AddWatchFlags)
+ mask: AddWatchFlags)
-> Result<WatchDescriptor>
{
let res = path.with_nix_path(|cstr| {
@@ -150,7 +150,7 @@ impl Inotify {
///
/// Returns an EINVAL error if the watch descriptor is invalid.
///
- /// For more information see, [inotify_rm_watch(2)](http://man7.org/linux/man-pages/man2/inotify_rm_watch.2.html).
+ /// For more information see, [inotify_rm_watch(2)](https://man7.org/linux/man-pages/man2/inotify_rm_watch.2.html).
#[cfg(target_os = "linux")]
pub fn rm_watch(self, wd: WatchDescriptor) -> Result<()> {
let res = unsafe { libc::inotify_rm_watch(self.fd, wd.wd) };
@@ -167,8 +167,8 @@ impl Inotify {
/// Reads a collection of events from the inotify file descriptor. This call
/// can either be blocking or non blocking depending on whether IN_NONBLOCK
- /// was set at initialization.
- ///
+ /// was set at initialization.
+ ///
/// Returns as many events as available. If the call was non blocking and no
/// events could be read then the EAGAIN error is returned.
pub fn read_events(self) -> Result<Vec<InotifyEvent>> {
@@ -194,14 +194,14 @@ impl Inotify {
let name = match event.len {
0 => None,
_ => {
- let ptr = unsafe {
+ let ptr = unsafe {
buffer
.as_ptr()
.add(offset + header_size)
as *const c_char
};
let cstr = unsafe { CStr::from_ptr(ptr) };
-
+
Some(OsStr::from_bytes(cstr.to_bytes()).to_owned())
}
};
diff --git a/src/sys/ioctl/bsd.rs b/src/sys/ioctl/bsd.rs
index f39c0eb..4ce4d33 100644
--- a/src/sys/ioctl/bsd.rs
+++ b/src/sys/ioctl/bsd.rs
@@ -1,6 +1,12 @@
/// The datatype used for the ioctl number
#[doc(hidden)]
+#[cfg(not(target_os = "illumos"))]
pub type ioctl_num_type = ::libc::c_ulong;
+
+#[doc(hidden)]
+#[cfg(target_os = "illumos")]
+pub type ioctl_num_type = ::libc::c_int;
+
/// The datatype used for the 3rd argument
#[doc(hidden)]
pub type ioctl_param_type = ::libc::c_int;
@@ -12,6 +18,7 @@ mod consts {
#[doc(hidden)]
pub const OUT: ioctl_num_type = 0x4000_0000;
#[doc(hidden)]
+ #[allow(overflowing_literals)]
pub const IN: ioctl_num_type = 0x8000_0000;
#[doc(hidden)]
pub const INOUT: ioctl_num_type = IN|OUT;
diff --git a/src/sys/ioctl/mod.rs b/src/sys/ioctl/mod.rs
index 8858a9d..203b7d0 100644
--- a/src/sys/ioctl/mod.rs
+++ b/src/sys/ioctl/mod.rs
@@ -104,7 +104,7 @@
//! respectively. To determine the specific `write_` variant to use you'll need to find
//! what the argument type is supposed to be. If it's an `int`, then `write_int` should be used,
//! otherwise it should be a pointer and `write_ptr` should be used. On Linux the
-//! [`ioctl_list` man page](http://man7.org/linux/man-pages/man2/ioctl_list.2.html) describes a
+//! [`ioctl_list` man page](https://man7.org/linux/man-pages/man2/ioctl_list.2.html) describes a
//! large number of `ioctl`s and describes their argument data type.
//!
//! Using "bad" `ioctl`s
@@ -232,6 +232,7 @@ pub use self::linux::*;
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
+ target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
@@ -241,6 +242,7 @@ mod bsd;
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
+ target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
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 63a0779..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!{
}
libc_bitflags!{
- /// Additional parameters for `mmap()`.
+ /// Additional parameters for [`mmap`].
pub struct MapFlags: c_int {
/// Compatibility flag. Ignored.
MAP_FILE;
@@ -40,10 +42,13 @@ libc_bitflags!{
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 @@ libc_bitflags!{
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 @@ libc_bitflags!{
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 @@ libc_enum!{
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_enum!{
}
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!{
}
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 @@ libc_bitflags!{
///
/// # 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 @@ pub unsafe fn mlock(addr: *const c_void, length: size_t) -> Result<()> {
///
/// # 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.
-///
-/// # Safety
+/// Locked pages never move to the swap area. For more information, see [`mlockall(2)`].
///
-/// `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 @@ pub fn munlockall() -> Result<()> {
///
/// # 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::Sys(Errno::last()))
+ Err(Errno::last())
} else {
Ok(ret)
}
@@ -333,7 +362,7 @@ pub unsafe fn mmap(addr: *mut c_void, length: size_t, prot: ProtFlags, flags: Ma
///
/// 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 @@ pub unsafe fn mremap(
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::Sys(Errno::last()))
+ Err(Errno::last())
} else {
Ok(ret)
}
@@ -354,8 +392,10 @@ pub unsafe fn mremap(
///
/// # 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,15 +404,17 @@ pub unsafe fn munmap(addr: *mut c_void, len: size_t) -> Result<()> {
///
/// # 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)
}
/// Set protection of memory mapping.
///
-/// See [`mprotect(3)`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/mprotect.html) for
+/// See [`mprotect(3)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mprotect.html) for
/// details.
///
/// # Safety
@@ -403,12 +445,19 @@ pub unsafe fn mprotect(addr: *mut c_void, length: size_t, prot: ProtFlags) -> Re
///
/// # 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 @@ pub fn shm_open<P: ?Sized + NixPath>(name: &P, flag: OFlag, mode: Mode) -> Resul
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 02edfd7..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 @@ pub mod epoll;
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",
@@ -28,6 +32,7 @@ pub mod eventfd;
target_os = "redox",
target_os = "macos",
target_os = "netbsd",
+ target_os = "illumos",
target_os = "openbsd"))]
#[macro_use]
pub mod ioctl;
@@ -36,9 +41,11 @@ pub mod ioctl;
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;
@@ -50,14 +57,19 @@ pub mod pthread;
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;
@@ -71,11 +83,14 @@ pub mod sendfile;
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",
@@ -91,10 +106,13 @@ pub mod statfs;
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;
@@ -104,7 +122,9 @@ pub mod utsname;
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 @@ pub fn get() -> Result<Persona> {
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 @@ pub fn set(persona: Persona) -> Result<Persona> {
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 a4d9825..d42e45d 100644
--- a/src/sys/pthread.rs
+++ b/src/sys/pthread.rs
@@ -1,9 +1,18 @@
+//! 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
-/// [`pthread_self(3)`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_self.html)
+/// [`pthread_self(3)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_self.html)
///
/// The thread ID returned by `pthread_self()` is not the same thing as
/// the kernel thread ID returned by a call to `gettid(2)`.
@@ -11,3 +20,19 @@ pub type Pthread = pthread_t;
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 e85afc7..a62881e 100644
--- a/src/sys/ptrace/bsd.rs
+++ b/src/sys/ptrace/bsd.rs
@@ -24,6 +24,7 @@ cfg_if! {
libc_enum! {
#[repr(i32)]
/// Ptrace Request enum defining the action to be taken.
+ #[non_exhaustive]
pub enum Request {
PT_TRACE_ME,
PT_READ_I,
@@ -133,16 +134,14 @@ pub fn kill(pid: Pid) -> Result<()> {
/// use nix::unistd::Pid;
/// 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);
/// }
+/// _ => {},
/// }
/// ```
#[cfg(
diff --git a/src/sys/ptrace/linux.rs b/src/sys/ptrace/linux.rs
index 8d1dd16..3723679 100644
--- a/src/sys/ptrace/linux.rs
+++ b/src/sys/ptrace/linux.rs
@@ -2,7 +2,7 @@
use cfg_if::cfg_if;
use std::{mem, ptr};
-use crate::{Error, Result};
+use crate::Result;
use crate::errno::Errno;
use libc::{self, c_void, c_long, siginfo_t};
use crate::unistd::Pid;
@@ -33,6 +33,7 @@ libc_enum!{
#[cfg_attr(not(any(target_env = "musl", target_os = "android")), repr(u32))]
#[cfg_attr(any(target_env = "musl", target_os = "android"), repr(i32))]
/// Ptrace Request enum defining the action to be taken.
+ #[non_exhaustive]
pub enum Request {
PTRACE_TRACEME,
PTRACE_PEEKTEXT,
@@ -97,11 +98,9 @@ libc_enum!{
#[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 @@ libc_enum!{
/// 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 @@ libc_enum!{
/// 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,
}
}
@@ -180,7 +182,7 @@ fn ptrace_peek(request: Request, pid: Pid, addr: AddressType, data: *mut c_void)
libc::ptrace(request as RequestType, libc::pid_t::from(pid), addr, data)
};
match Errno::result(ret) {
- Ok(..) | Err(Error::Sys(Errno::UnknownErrno)) => Ok(ret),
+ Ok(..) | Err(Errno::UnknownErrno) => Ok(ret),
err @ Err(..) => err,
}
}
@@ -337,7 +339,7 @@ pub fn attach(pid: Pid) -> Result<()> {
/// 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 @@ pub fn cont<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
}
}
+/// 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 @@ pub fn kill(pid: Pid) -> Result<()> {
}
}
-/// 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 @@ pub fn kill(pid: Pid) -> Result<()> {
/// ```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!{
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!{
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 e319130..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 @@ libc_enum! {
/// 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 @@ pub fn reboot(how: RebootMode) -> Result<Infallible> {
unsafe {
libc::reboot(how as libc::c_int)
};
- Err(Error::Sys(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 a576c7e..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 @@ use crate::sys::time::{TimeSpec, TimeVal};
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 @@ impl FdSet {
}
}
+ /// 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) };
}
@@ -50,21 +68,19 @@ impl FdSet {
///
/// ```
/// # use nix::sys::select::FdSet;
- /// # fn main() {
/// let mut set = FdSet::new();
/// set.insert(4);
/// set.insert(9);
/// assert_eq!(set.highest(), Some(9));
- /// # }
/// ```
///
/// [`select`]: fn.select.html
- pub fn highest(&mut self) -> Option<RawFd> {
+ pub fn highest(&self) -> Option<RawFd> {
self.fds(None).next_back()
}
/// Returns an iterator over the file descriptors in the set.
- ///
+ ///
/// For performance, it takes an optional higher bound: the iterator will
/// not return any elements of the set greater than the given file
/// descriptor.
@@ -81,7 +97,7 @@ impl FdSet {
/// 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),
@@ -98,7 +114,7 @@ impl Default for FdSet {
/// Iterator over `FdSet`.
#[derive(Debug)]
pub struct Fds<'a> {
- set: &'a mut FdSet,
+ set: &'a FdSet,
range: Range<usize>,
}
@@ -106,7 +122,7 @@ impl<'a> Iterator for Fds<'a> {
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);
}
@@ -155,7 +171,7 @@ impl<'a> FusedIterator for Fds<'a> {}
///
/// # References
///
-/// [select(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/select.html)
+/// [select(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/select.html)
///
/// [`FdSet::highest`]: struct.FdSet.html#method.highest
pub fn select<'a, N, R, W, E, T>(nfds: N,
@@ -221,7 +237,7 @@ where
///
/// # References
///
-/// [pselect(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/pselect.html)
+/// [pselect(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pselect.html)
///
/// [The new pselect() system call](https://lwn.net/Articles/176911/)
///
diff --git a/src/sys/sendfile.rs b/src/sys/sendfile.rs
index 84fe2a9..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;
@@ -18,7 +20,7 @@ use crate::errno::Errno;
///
/// `in_fd` must support `mmap`-like operations and therefore cannot be a socket.
///
-/// For more information, see [the sendfile(2) man page.](http://man7.org/linux/man-pages/man2/sendfile.2.html)
+/// For more information, see [the sendfile(2) man page.](https://man7.org/linux/man-pages/man2/sendfile.2.html)
#[cfg(any(target_os = "android", target_os = "linux"))]
pub fn sendfile(
out_fd: RawFd,
@@ -33,6 +35,32 @@ pub fn sendfile(
Errno::result(ret).map(|r| r as usize)
}
+/// Copy up to `count` bytes to `out_fd` from `in_fd` starting at `offset`.
+///
+/// Returns a `Result` with the number of bytes written.
+///
+/// If `offset` is `None`, `sendfile` will begin reading at the current offset of `in_fd`and will
+/// update the offset of `in_fd`. If `offset` is `Some`, `sendfile` will begin at the specified
+/// offset and will not update the offset of `in_fd`. Instead, it will mutate `offset` to point to
+/// the byte after the last byte copied.
+///
+/// `in_fd` must support `mmap`-like operations and therefore cannot be a socket.
+///
+/// For more information, see [the sendfile(2) man page.](https://man7.org/linux/man-pages/man2/sendfile.2.html)
+#[cfg(target_os = "linux")]
+pub fn sendfile64(
+ out_fd: RawFd,
+ in_fd: RawFd,
+ offset: Option<&mut libc::off64_t>,
+ count: usize,
+) -> Result<usize> {
+ let offset = offset
+ .map(|offset| offset as *mut _)
+ .unwrap_or(ptr::null_mut());
+ let ret = unsafe { libc::sendfile64(out_fd, in_fd, offset, count) };
+ Errno::result(ret).map(|r| r as usize)
+}
+
cfg_if! {
if #[cfg(any(target_os = "freebsd",
target_os = "ios",
diff --git a/src/sys/signal.rs b/src/sys/signal.rs
index 2f8b5fa..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 http://rust-lang.org/COPYRIGHT.
+// 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 @@ use std::ptr;
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 @@ impl FromStr for Signal {
target_os = "fuchsia", target_os = "linux",
target_os = "redox")))]
"SIGINFO" => Signal::SIGINFO,
- _ => return Err(Error::invalid_argument()),
+ _ => return Err(Errno::EINVAL),
})
}
}
@@ -132,7 +167,7 @@ impl Signal {
/// 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 @@ const SIGNALS: [Signal; 31] = [
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 Iterator for SignalIterator {
}
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::invalid_argument())
- }
- }
-}
-
+/// 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_int;
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 @@ pub struct SigSet {
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 @@ impl SigSet {
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 @@ impl SigSet {
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 @@ impl SigSet {
}
}
+ /// 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 @@ impl SigSet {
/// 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 @@ impl SigAction {
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 @@ impl SigAction {
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 @@ impl SigAction {
///
/// # 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();
@@ -619,7 +717,7 @@ pub unsafe fn sigaction(signal: Signal, sigaction: &SigAction) -> Result<SigActi
Errno::result(res).map(|_| SigAction { sigaction: oldact.assume_init() })
}
-/// Signal management (see [signal(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/signal.html))
+/// Signal management (see [signal(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/signal.html))
///
/// Installs `handler` for the given `signal`, returning the previous signal
/// handler. `signal` should only be used following another call to `signal` or
@@ -664,7 +762,7 @@ pub unsafe fn sigaction(signal: Signal, sigaction: &SigAction) -> Result<SigActi
///
/// # Errors
///
-/// Returns [`Error::UnsupportedOperation`] if `handler` is
+/// Returns [`Error(Errno::EOPNOTSUPP)`] if `handler` is
/// [`SigAction`][SigActionStruct]. Use [`sigaction`][SigActionFn] instead.
///
/// `signal` also returns any error from `libc::signal`, such as when an attempt
@@ -681,13 +779,16 @@ pub unsafe fn signal(signal: Signal, handler: SigHandler) -> Result<SigHandler>
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::UnsupportedOperation),
+ 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)),
}
})
}
@@ -724,8 +825,8 @@ fn do_pthread_sigmask(how: SigmaskHow,
///
/// If both `set` and `oldset` is None, this function is a no-op.
///
-/// For more information, visit the [`pthread_sigmask`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_sigmask.html),
-/// or [`sigprocmask`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigprocmask.html) man pages.
+/// For more information, visit the [`pthread_sigmask`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_sigmask.html),
+/// or [`sigprocmask`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigprocmask.html) man pages.
pub fn pthread_sigmask(how: SigmaskHow,
set: Option<&SigSet>,
oldset: Option<&mut SigSet>) -> Result<()>
@@ -736,7 +837,7 @@ pub fn pthread_sigmask(how: SigmaskHow,
/// Examine and change blocked signals.
///
/// For more informations see the [`sigprocmask` man
-/// pages](http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigprocmask.html).
+/// pages](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigprocmask.html).
pub fn sigprocmask(how: SigmaskHow, set: Option<&SigSet>, oldset: Option<&mut SigSet>) -> Result<()> {
if set.is_none() && oldset.is_none() {
return Ok(())
@@ -754,6 +855,24 @@ pub fn sigprocmask(how: SigmaskHow, set: Option<&SigSet>, oldset: Option<&mut Si
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 @@ pub fn kill<T: Into<Option<Signal>>>(pid: Pid, signal: T) -> Result<()> {
Errno::result(res).map(drop)
}
-/// Send a signal to a process group [(see
-/// killpg(3))](http://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 @@ pub fn killpg<T: Into<Option<Signal>>>(pgrp: Pid, signal: T) -> Result<()> {
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 @@ pub fn raise(signal: Signal) -> Result<()> {
}
+/// 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")))]
@@ -850,10 +991,10 @@ mod sigevent {
/// Linux, Solaris, and portable programs should prefer `SIGEV_THREAD_ID` or
/// `SIGEV_SIGNAL`. That field is part of a union that shares space with the
/// more genuinely useful `sigev_notify_thread_id`
+ // Allow invalid_value warning on Fuchsia only.
+ // See https://github.com/nix-rust/nix/issues/1441
+ #[cfg_attr(target_os = "fuchsia", allow(invalid_value))]
pub fn new(sigev_notify: SigevNotify) -> SigEvent {
- // NB: This uses MaybeUninit rather than mem::zeroed because libc::sigevent contains a
- // function pointer on Fuchsia as of https://github.com/rust-lang/libc/commit/2f59370,
- // and function pointers must not be null.
let mut sev = unsafe { mem::MaybeUninit::<libc::sigevent>::zeroed().assume_init() };
sev.sigev_notify = match sigev_notify {
SigevNotify::SigevNone => libc::SIGEV_NONE,
@@ -899,6 +1040,7 @@ mod sigevent {
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 @@ mod tests {
#[test]
fn test_from_str_invalid_value() {
- let errval = Err(Error::Sys(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 c43b450..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 @@ libc_bitflags!{
}
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.
///
@@ -46,7 +46,7 @@ pub const SIGNALFD_SIGINFO_SIZE: usize = 128;
/// A signal must be blocked on every thread in a process, otherwise it won't be visible from
/// signalfd (the default handler will be invoked instead).
///
-/// See [the signalfd man page for more information](http://man7.org/linux/man-pages/man2/signalfd.2.html)
+/// See [the signalfd man page for more information](https://man7.org/linux/man-pages/man2/signalfd.2.html)
pub fn signalfd(fd: RawFd, mask: &SigSet, flags: SfdFlags) -> Result<RawFd> {
unsafe {
Errno::result(libc::signalfd(fd as libc::c_int, mask.as_ref(), flags.bits()))
@@ -98,17 +98,16 @@ impl SignalFd {
}
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(Error::Sys(Errno::EAGAIN)) => Ok(None),
+ Err(Errno::EAGAIN) => Ok(None),
Err(error) => Err(error)
}
}
@@ -117,7 +116,7 @@ impl SignalFd {
impl Drop for SignalFd {
fn drop(&mut self) {
let e = unistd::close(self.0);
- if !std::thread::panicking() && e == Err(Error::Sys(Errno::EBADF)) {
+ if !std::thread::panicking() && e == Err(Errno::EBADF) {
panic!("Closing an invalid file descriptor!");
};
}
@@ -144,14 +143,6 @@ impl Iterator for SignalFd {
#[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 2299c57..b119642 100644
--- a/src/sys/socket/addr.rs
+++ b/src/sys/socket/addr.rs
@@ -1,6 +1,7 @@
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};
use std::ffi::OsStr;
use std::hash::{Hash, Hasher};
@@ -20,6 +21,7 @@ use crate::sys::socket::addr::sys_control::SysControlAddr;
target_os = "ios",
target_os = "linux",
target_os = "macos",
+ target_os = "illumos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "fuchsia"))]
@@ -30,19 +32,24 @@ pub use self::vsock::VsockAddr;
/// 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)`](http://man7.org/linux/man-pages/man7/unix.7.html))
+ /// Local communication (see [`unix(7)`](https://man7.org/linux/man-pages/man7/unix.7.html))
Unix = libc::AF_UNIX,
- /// IPv4 Internet protocols (see [`ip(7)`](http://man7.org/linux/man-pages/man7/ip.7.html))
+ /// IPv4 Internet protocols (see [`ip(7)`](https://man7.org/linux/man-pages/man7/ip.7.html))
Inet = libc::AF_INET,
- /// IPv6 Internet protocols (see [`ipv6(7)`](http://man7.org/linux/man-pages/man7/ipv6.7.html))
+ /// IPv6 Internet protocols (see [`ipv6(7)`](https://man7.org/linux/man-pages/man7/ipv6.7.html))
Inet6 = libc::AF_INET6,
- /// Kernel user interface device (see [`netlink(7)`](http://man7.org/linux/man-pages/man7/netlink.7.html))
+ /// Kernel user interface device (see [`netlink(7)`](https://man7.org/linux/man-pages/man7/netlink.7.html))
#[cfg(any(target_os = "android", target_os = "linux"))]
Netlink = libc::AF_NETLINK,
- /// Low level packet interface (see [`packet(7)`](http://man7.org/linux/man-pages/man7/packet.7.html))
- #[cfg(any(target_os = "android", target_os = "linux", target_os = "fuchsia"))]
+ /// Low level packet interface (see [`packet(7)`](https://man7.org/linux/man-pages/man7/packet.7.html))
+ #[cfg(any(target_os = "android",
+ target_os = "linux",
+ target_os = "illumos",
+ target_os = "fuchsia",
+ target_os = "solaris"))]
Packet = libc::AF_PACKET,
/// KEXT Controls and Notifications
#[cfg(any(target_os = "ios", target_os = "macos"))]
@@ -61,7 +68,7 @@ pub enum AddressFamily {
/// Access to raw ATM PVCs
#[cfg(any(target_os = "android", target_os = "linux"))]
AtmPvc = libc::AF_ATMPVC,
- /// ITU-T X.25 / ISO-8208 protocol (see [`x25(7)`](http://man7.org/linux/man-pages/man7/x25.7.html))
+ /// ITU-T X.25 / ISO-8208 protocol (see [`x25(7)`](https://man7.org/linux/man-pages/man7/x25.7.html))
#[cfg(any(target_os = "android", target_os = "linux"))]
X25 = libc::AF_X25,
#[cfg(any(target_os = "android", target_os = "linux"))]
@@ -98,12 +105,16 @@ pub enum AddressFamily {
Can = libc::AF_CAN,
#[cfg(any(target_os = "android", target_os = "linux"))]
Tipc = libc::AF_TIPC,
- #[cfg(not(any(target_os = "ios", target_os = "macos")))]
+ #[cfg(not(any(target_os = "illumos",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "solaris")))]
Bluetooth = libc::AF_BLUETOOTH,
#[cfg(any(target_os = "android", target_os = "linux"))]
Iucv = libc::AF_IUCV,
#[cfg(any(target_os = "android", target_os = "linux"))]
RxRpc = libc::AF_RXRPC,
+ #[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
Isdn = libc::AF_ISDN,
#[cfg(any(target_os = "android", target_os = "linux"))]
Phonet = libc::AF_PHONET,
@@ -190,6 +201,7 @@ pub enum AddressFamily {
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
+ target_os = "illumos",
target_os = "netbsd",
target_os = "openbsd"))]
Link = libc::AF_LINK,
@@ -214,7 +226,7 @@ pub enum AddressFamily {
target_os = "netbsd",
target_os = "openbsd"))]
Natm = libc::AF_NATM,
- /// Unspecified address family, (see [`getaddrinfo(3)`](http://man7.org/linux/man-pages/man3/getaddrinfo.3.html))
+ /// Unspecified address family, (see [`getaddrinfo(3)`](https://man7.org/linux/man-pages/man3/getaddrinfo.3.html))
#[cfg(any(target_os = "android", target_os = "linux"))]
Unspec = libc::AF_UNSPEC,
}
@@ -225,7 +237,7 @@ impl AddressFamily {
///
/// 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),
@@ -241,6 +253,7 @@ impl AddressFamily {
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
+ target_os = "illumos",
target_os = "openbsd"))]
libc::AF_LINK => Some(AddressFamily::Link),
#[cfg(any(target_os = "android", target_os = "linux"))]
@@ -257,6 +270,7 @@ pub enum InetAddr {
}
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) => {
@@ -280,6 +294,7 @@ impl InetAddr {
}
}
+ #[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) => {
@@ -301,7 +316,7 @@ impl InetAddr {
}
}
/// 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)),
@@ -309,7 +324,7 @@ impl InetAddr {
}
/// 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),
@@ -331,6 +346,7 @@ impl InetAddr {
}
}
+ #[deprecated(since = "0.23.0", note = "use .to_string() instead")]
pub fn to_str(&self) -> String {
format!("{}", self)
}
@@ -360,7 +376,7 @@ impl IpAddr {
/// 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))
}
@@ -369,7 +385,7 @@ impl IpAddr {
/// 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))
}
@@ -380,7 +396,7 @@ impl IpAddr {
}
}
- 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()),
@@ -408,11 +424,11 @@ pub struct Ipv4Addr(pub libc::in_addr);
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 })
}
@@ -424,16 +440,16 @@ impl Ipv4Addr {
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])
}
@@ -474,7 +490,7 @@ macro_rules! to_u16_array {
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)})
}
@@ -484,11 +500,11 @@ impl Ipv6Addr {
}
/// 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])
}
@@ -501,15 +517,42 @@ impl fmt::Display for Ipv6Addr {
}
/// 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.
@@ -523,15 +566,15 @@ impl UnixAddr {
let bytes = cstr.to_bytes();
- if bytes.len() > ret.sun_path.len() {
- return Err(Error::Sys(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))
}
})?
}
@@ -550,8 +593,8 @@ impl UnixAddr {
.. mem::zeroed()
};
- if path.len() + 1 > ret.sun_path.len() {
- return Err(Error::Sys(Errno::ENAMETOOLONG));
+ if path.len() >= ret.sun_path.len() {
+ return Err(Errno::ENAMETOOLONG);
}
// Abstract addresses are represented by sun_path[0] ==
@@ -560,28 +603,39 @@ impl UnixAddr {
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,
}
}
@@ -591,31 +645,55 @@ impl UnixAddr {
/// 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()
}
}
@@ -623,12 +701,13 @@ impl Eq for UnixAddr {}
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),
@@ -645,6 +724,7 @@ pub enum SockAddr {
target_os = "ios",
target_os = "linux",
target_os = "macos",
+ target_os = "illumos",
target_os = "netbsd",
target_os = "openbsd"))]
Link(LinkAddr),
@@ -673,7 +753,7 @@ impl SockAddr {
#[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"))]
@@ -699,6 +779,7 @@ impl SockAddr {
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
+ target_os = "illumos",
target_os = "openbsd"))]
SockAddr::Link(..) => AddressFamily::Link,
#[cfg(any(target_os = "android", target_os = "linux"))]
@@ -706,6 +787,7 @@ impl SockAddr {
}
}
+ #[deprecated(since = "0.23.0", note = "use .to_string() instead")]
pub fn to_str(&self) -> String {
format!("{}", self)
}
@@ -744,6 +826,7 @@ impl SockAddr {
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
+ target_os = "illumos",
target_os = "openbsd"))]
Some(AddressFamily::Link) => {
let ether_addr = LinkAddr(*(addr as *const libc::sockaddr_dl));
@@ -786,12 +869,12 @@ impl SockAddr {
},
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)) => (
@@ -830,6 +913,7 @@ impl SockAddr {
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
+ target_os = "illumos",
target_os = "netbsd",
target_os = "openbsd"))]
SockAddr::Link(LinkAddr(ref addr)) => (
@@ -869,6 +953,7 @@ impl fmt::Display for SockAddr {
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
+ target_os = "illumos",
target_os = "openbsd"))]
SockAddr::Link(ref ether_addr) => ether_addr.fmt(f),
#[cfg(any(target_os = "android", target_os = "linux"))]
@@ -896,11 +981,11 @@ pub mod netlink {
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
}
}
@@ -981,7 +1066,7 @@ pub mod sys_control {
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)]
@@ -992,7 +1077,7 @@ pub mod sys_control {
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;
@@ -1003,7 +1088,7 @@ pub mod sys_control {
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,
@@ -1018,7 +1103,7 @@ pub mod sys_control {
pub fn from_name(sockfd: RawFd, name: &str, unit: u32) -> Result<SysControlAddr> {
if name.len() > MAX_KCTL_NAME {
- return Err(Error::Sys(Errno::ENAMETOOLONG));
+ return Err(Errno::ENAMETOOLONG);
}
let mut ctl_name = [0; MAX_KCTL_NAME];
@@ -1030,11 +1115,11 @@ pub mod sys_control {
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
}
}
@@ -1118,6 +1203,7 @@ mod datalink {
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
+ target_os = "illumos",
target_os = "netbsd",
target_os = "openbsd"))]
mod datalink {
@@ -1129,6 +1215,7 @@ mod datalink {
impl LinkAddr {
/// Total length of sockaddr
+ #[cfg(not(target_os = "illumos"))]
pub fn len(&self) -> usize {
self.0.sdl_len as usize
}
@@ -1280,6 +1367,7 @@ mod tests {
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
+ target_os = "illumos",
target_os = "openbsd"))]
use super::*;
@@ -1324,17 +1412,36 @@ mod tests {
};
}
+ #[cfg(target_os = "illumos")]
+ #[test]
+ fn test_illumos_tap_datalink_addr() {
+ let bytes = [25u8, 0, 0, 0, 6, 0, 6, 0, 24, 101, 144, 221, 76, 176];
+ let ptr = bytes.as_ptr();
+ let sa = ptr as *const libc::sockaddr;
+ let _sock_addr = unsafe { SockAddr::from_libc_sockaddr(sa) };
+
+ assert!(_sock_addr.is_some());
+
+ let sock_addr = _sock_addr.unwrap();
+
+ assert_eq!(sock_addr.family(), AddressFamily::Link);
+
+ match sock_addr {
+ SockAddr::Link(ether_addr) => {
+ assert_eq!(ether_addr.addr(), [24u8, 101, 144, 221, 76, 176]);
+ },
+ _ => { unreachable!() }
+ };
+ }
+
#[cfg(any(target_os = "android", target_os = "linux"))]
#[test]
fn test_abstract_sun_path() {
let name = String::from("nix\0abstract\0test");
let addr = UnixAddr::new_abstract(name.as_bytes()).unwrap();
- 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 11ed329..97eea3d 100644
--- a/src/sys/socket/mod.rs
+++ b/src/sys/socket/mod.rs
@@ -1,16 +1,20 @@
//! Socket interface functions
//!
-//! [Further reading](http://man7.org/linux/man-pages/man7/socket.7.html)
+//! [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;
use std::{mem, ptr, slice};
use std::os::unix::io::RawFd;
+#[cfg(all(target_os = "linux"))]
+use crate::sys::time::TimeSpec;
use crate::sys::time::TimeVal;
use crate::sys::uio::IoVec;
mod addr;
+#[deny(missing_docs)]
pub mod sockopt;
/*
@@ -19,6 +23,7 @@ pub mod sockopt;
*
*/
+#[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
pub use self::addr::{
AddressFamily,
SockAddr,
@@ -29,6 +34,17 @@ pub use self::addr::{
Ipv6Addr,
LinkAddr,
};
+#[cfg(any(target_os = "illumos", target_os = "solaris"))]
+pub use self::addr::{
+ AddressFamily,
+ SockAddr,
+ InetAddr,
+ UnixAddr,
+ IpAddr,
+ Ipv4Addr,
+ Ipv6Addr,
+};
+
#[cfg(any(target_os = "android", target_os = "linux"))]
pub use crate::sys::socket::addr::netlink::NetlinkAddr;
#[cfg(any(target_os = "android", target_os = "linux"))]
@@ -55,6 +71,7 @@ pub use libc::{c_uint, CMSG_SPACE};
/// 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
@@ -79,10 +96,11 @@ pub enum SockType {
/// to specify the protocol to use.
#[repr(i32)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[non_exhaustive]
pub enum SockProtocol {
- /// TCP protocol ([ip(7)](http://man7.org/linux/man-pages/man7/ip.7.html))
+ /// TCP protocol ([ip(7)](https://man7.org/linux/man-pages/man7/ip.7.html))
Tcp = libc::IPPROTO_TCP,
- /// UDP protocol ([ip(7)](http://man7.org/linux/man-pages/man7/ip.7.html))
+ /// UDP protocol ([ip(7)](https://man7.org/linux/man-pages/man7/ip.7.html))
Udp = libc::IPPROTO_UDP,
/// Allows applications and other KEXTs to be notified when certain kernel events occur
/// ([ref](https://developer.apple.com/library/content/documentation/Darwin/Conceptual/NKEConceptual/control/control.html))
@@ -159,6 +177,7 @@ libc_bitflags!{
#[cfg(any(target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
+ target_os = "illumos",
target_os = "linux",
target_os = "netbsd",
target_os = "openbsd"))]
@@ -167,6 +186,7 @@ libc_bitflags!{
#[cfg(any(target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
+ target_os = "illumos",
target_os = "linux",
target_os = "netbsd",
target_os = "openbsd"))]
@@ -202,7 +222,7 @@ libc_bitflags!{
/// (via the [`fcntl`](../../fcntl/fn.fcntl.html)
/// `F_SETFL` operation), but differs in that `MSG_DONTWAIT` is a per-
/// call option, whereas `O_NONBLOCK` is a setting on the open file
- /// description (see [open(2)](http://man7.org/linux/man-pages/man2/open.2.html)),
+ /// description (see [open(2)](https://man7.org/linux/man-pages/man2/open.2.html)),
/// which will affect all threads in
/// the calling process and as well as other processes that hold
/// file descriptors referring to the same open file description.
@@ -230,7 +250,7 @@ libc_bitflags!{
/// file descriptor using the `SCM_RIGHTS` operation (described in
/// [unix(7)](https://linux.die.net/man/7/unix)).
/// This flag is useful for the same reasons as the `O_CLOEXEC` flag of
- /// [open(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html).
+ /// [open(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html).
///
/// Only used in [`recvmsg`](fn.recvmsg.html) function.
#[cfg(any(target_os = "android",
@@ -291,9 +311,9 @@ cfg_if! {
}
}
- 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"))] {
@@ -339,6 +359,38 @@ cfg_if! {
}
}
+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`.
@@ -367,7 +419,7 @@ pub struct Ipv6MembershipRequest(libc::ipv6_mreq);
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,
@@ -401,13 +453,11 @@ impl Ipv6MembershipRequest {
macro_rules! cmsg_space {
( $( $x:ty ),* ) => {
{
- use nix::sys::socket::{c_uint, CMSG_SPACE};
- use std::mem;
let mut space = 0;
$(
// CMSG_SPACE is always safe
space += unsafe {
- CMSG_SPACE(mem::size_of::<$x>() as c_uint)
+ $crate::sys::socket::CMSG_SPACE(::std::mem::size_of::<$x>() as $crate::sys::socket::c_uint)
} as usize;
)*
Vec::<u8>::with_capacity(space)
@@ -467,7 +517,7 @@ impl<'a> Iterator for CmsgIterator<'a> {
/// A type-safe wrapper around a single control message, as used with
/// [`recvmsg`](#fn.recvmsg).
///
-/// [Further reading](http://man7.org/linux/man-pages/man3/cmsg.3.html)
+/// [Further reading](https://man7.org/linux/man-pages/man3/cmsg.3.html)
// Nix version 0.13.0 and earlier used ControlMessage for both recvmsg and
// sendmsg. However, on some platforms the messages returned by recvmsg may be
// unaligned. ControlMessageOwned takes those messages by copy, obviating any
@@ -475,16 +525,14 @@ impl<'a> Iterator for CmsgIterator<'a> {
//
// 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
@@ -495,6 +543,7 @@ pub enum ControlMessageOwned {
///
/// # Examples
///
+ /// ```
/// # #[macro_use] extern crate nix;
/// # use nix::sys::socket::*;
/// # use nix::sys::uio::IoVec;
@@ -542,6 +591,11 @@ pub enum ControlMessageOwned {
/// # }
/// ```
ScmTimestamp(TimeVal),
+ /// Nanoseconds resolution timestamp
+ ///
+ /// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
+ #[cfg(all(target_os = "linux"))]
+ ScmTimestampns(TimeSpec),
#[cfg(any(
target_os = "android",
target_os = "ios",
@@ -589,6 +643,24 @@ pub enum ControlMessageOwned {
#[cfg(target_os = "linux")]
UdpGroSegments(u16),
+ /// SO_RXQ_OVFL indicates that an unsigned 32 bit value
+ /// ancilliary msg (cmsg) should be attached to recieved
+ /// skbs indicating the number of packets dropped by the
+ /// socket between the last recieved packet and this
+ /// received packet.
+ ///
+ /// `RxqOvfl` socket option should be enabled on a socket
+ /// to allow receiving the drop counter.
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ 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),
@@ -633,6 +705,11 @@ impl ControlMessageOwned {
let tv: libc::timeval = ptr::read_unaligned(p as *const _);
ControlMessageOwned::ScmTimestamp(TimeVal::from(tv))
},
+ #[cfg(all(target_os = "linux"))]
+ (libc::SOL_SOCKET, libc::SCM_TIMESTAMPNS) => {
+ let ts: libc::timespec = ptr::read_unaligned(p as *const _);
+ ControlMessageOwned::ScmTimestampns(TimeSpec::from(ts))
+ }
#[cfg(any(
target_os = "android",
target_os = "freebsd",
@@ -682,27 +759,61 @@ impl ControlMessageOwned {
let gso_size: u16 = ptr::read_unaligned(p as *const _);
ControlMessageOwned::UdpGroSegments(gso_size)
},
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ (libc::SOL_SOCKET, libc::SO_RXQ_OVFL) => {
+ 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[..]));
+ let ucmsg = UnknownCmsg(*header, Vec::<u8>::from(sl));
ControlMessageOwned::Unknown(ucmsg)
}
}
}
+
+ #[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
/// [`sendmsg`](#fn.sendmsg). More types may be added to this enum; do not
/// exhaustively pattern-match it.
///
-/// [Further reading](http://man7.org/linux/man-pages/man3/cmsg.3.html)
+/// [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.
///
/// See the description in the "Ancillary messages" section of the
- /// [unix(7) man page](http://man7.org/linux/man-pages/man7/unix.7.html).
+ /// [unix(7) man page](https://man7.org/linux/man-pages/man7/unix.7.html).
///
/// Using multiple `ScmRights` messages for a single `sendmsg` call isn't
/// recommended since it causes platform-dependent behaviour: It might
@@ -719,7 +830,7 @@ pub enum ControlMessage<'a> {
/// processes are verified by the kernel.
///
/// For further information, please refer to the
- /// [`unix(7)`](http://man7.org/linux/man-pages/man7/unix.7.html) man page.
+ /// [`unix(7)`](https://man7.org/linux/man-pages/man7/unix.7.html) man page.
#[cfg(any(target_os = "android", target_os = "linux"))]
ScmCredentials(&'a UnixCredentials),
/// A message of type `SCM_CREDS`, containing the pid, uid, euid, gid and groups of
@@ -730,7 +841,7 @@ pub enum ControlMessage<'a> {
///
/// 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.
@@ -781,7 +892,7 @@ pub enum ControlMessage<'a> {
/// Configure the sending addressing and interface for v4
///
/// For further information, please refer to the
- /// [`ip(7)`](http://man7.org/linux/man-pages/man7/ip.7.html) man page.
+ /// [`ip(7)`](https://man7.org/linux/man-pages/man7/ip.7.html) man page.
#[cfg(any(target_os = "linux",
target_os = "macos",
target_os = "netbsd",
@@ -792,7 +903,7 @@ pub enum ControlMessage<'a> {
/// Configure the sending addressing and interface for v6
///
/// For further information, please refer to the
- /// [`ipv6(7)`](http://man7.org/linux/man-pages/man7/ipv6.7.html) man page.
+ /// [`ipv6(7)`](https://man7.org/linux/man-pages/man7/ipv6.7.html) man page.
#[cfg(any(target_os = "linux",
target_os = "macos",
target_os = "netbsd",
@@ -800,6 +911,14 @@ pub enum ControlMessage<'a> {
target_os = "android",
target_os = "ios",))]
Ipv6PacketInfo(&'a libc::in6_pktinfo),
+
+ /// SO_RXQ_OVFL indicates that an unsigned 32 bit value
+ /// ancilliary msg (cmsg) should be attached to recieved
+ /// skbs indicating the number of packets dropped by the
+ /// socket between the last recieved packet and this
+ /// received packet.
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ RxqOvfl(&'a u32),
}
// An opaque structure used to prevent cmsghdr from being a public type
@@ -890,6 +1009,10 @@ impl<'a> ControlMessage<'a> {
target_os = "netbsd", target_os = "freebsd",
target_os = "android", target_os = "ios",))]
ControlMessage::Ipv6PacketInfo(info) => info as *const _ as *const u8,
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ ControlMessage::RxqOvfl(drop_count) => {
+ drop_count as *const _ as *const u8
+ },
};
unsafe {
ptr::copy_nonoverlapping(
@@ -938,6 +1061,10 @@ impl<'a> ControlMessage<'a> {
target_os = "netbsd", target_os = "freebsd",
target_os = "android", target_os = "ios",))]
ControlMessage::Ipv6PacketInfo(info) => mem::size_of_val(info),
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ ControlMessage::RxqOvfl(drop_count) => {
+ mem::size_of_val(drop_count)
+ },
}
}
@@ -962,6 +1089,8 @@ impl<'a> ControlMessage<'a> {
target_os = "netbsd", target_os = "freebsd",
target_os = "android", target_os = "ios",))]
ControlMessage::Ipv6PacketInfo(_) => libc::IPPROTO_IPV6,
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ ControlMessage::RxqOvfl(_) => libc::SOL_SOCKET,
}
}
@@ -997,6 +1126,10 @@ impl<'a> ControlMessage<'a> {
target_os = "netbsd", target_os = "freebsd",
target_os = "android", target_os = "ios",))]
ControlMessage::Ipv6PacketInfo(_) => libc::IPV6_PKTINFO,
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ ControlMessage::RxqOvfl(_) => {
+ libc::SO_RXQ_OVFL
+ },
}
}
@@ -1089,23 +1222,22 @@ pub fn sendmmsg<'a, I, C>(
let mut output = Vec::<libc::mmsghdr>::with_capacity(reserve_items);
- let mut cmsgs_buffer = vec![0u8; 0];
+ let mut cmsgs_buffers = Vec::<Vec<u8>>::with_capacity(reserve_items);
for d in iter {
- let cmsgs_start = cmsgs_buffer.len();
- let cmsgs_required_capacity: usize = d.cmsgs.as_ref().iter().map(|c| c.space()).sum();
- let cmsgs_buffer_need_capacity = cmsgs_start + cmsgs_required_capacity;
- cmsgs_buffer.resize(cmsgs_buffer_need_capacity, 0);
+ let capacity: usize = d.cmsgs.as_ref().iter().map(|c| c.space()).sum();
+ let mut cmsgs_buffer = vec![0u8; capacity];
output.push(libc::mmsghdr {
msg_hdr: pack_mhdr_to_send(
- &mut cmsgs_buffer[cmsgs_start..],
+ &mut cmsgs_buffer,
&d.iov,
&d.cmsgs,
d.addr.as_ref()
),
msg_len: 0,
});
+ cmsgs_buffers.push(cmsgs_buffer);
};
let ret = unsafe { libc::sendmmsg(fd, output.as_mut_ptr(), output.len() as _, flags.bits() as _) };
@@ -1168,6 +1300,7 @@ pub struct RecvMmsgData<'a, I>
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>,
@@ -1378,7 +1511,7 @@ fn pack_mhdr_to_send<'a, I, C>(
/// * `flags`: Optional flags passed directly to the operating system.
///
/// # References
-/// [recvmsg(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/recvmsg.html)
+/// [recvmsg(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvmsg.html)
pub fn recvmsg<'a>(fd: RawFd, iov: &[IoVec<&mut [u8]>],
mut cmsg_buffer: Option<&'a mut Vec<u8>>,
flags: MsgFlags) -> Result<RecvMsg<'a>>
@@ -1406,7 +1539,7 @@ pub fn recvmsg<'a>(fd: RawFd, iov: &[IoVec<&mut [u8]>],
/// protocols may exist, in which case a particular protocol must be
/// specified in this manner.
///
-/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html)
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html)
pub fn socket<T: Into<Option<SockProtocol>>>(domain: AddressFamily, ty: SockType, flags: SockFlag, protocol: T) -> Result<RawFd> {
let protocol = match protocol.into() {
None => 0,
@@ -1426,7 +1559,7 @@ pub fn socket<T: Into<Option<SockProtocol>>>(domain: AddressFamily, ty: SockType
/// Create a pair of connected sockets
///
-/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/socketpair.html)
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/socketpair.html)
pub fn socketpair<T: Into<Option<SockProtocol>>>(domain: AddressFamily, ty: SockType, protocol: T,
flags: SockFlag) -> Result<(RawFd, RawFd)> {
let protocol = match protocol.into() {
@@ -1450,7 +1583,7 @@ pub fn socketpair<T: Into<Option<SockProtocol>>>(domain: AddressFamily, ty: Sock
/// Listen for connections on a socket
///
-/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/listen.html)
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/listen.html)
pub fn listen(sockfd: RawFd, backlog: usize) -> Result<()> {
let res = unsafe { libc::listen(sockfd, backlog as c_int) };
@@ -1459,7 +1592,7 @@ pub fn listen(sockfd: RawFd, backlog: usize) -> Result<()> {
/// Bind a name to a socket
///
-/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html)
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html)
pub fn bind(fd: RawFd, addr: &SockAddr) -> Result<()> {
let res = unsafe {
let (ptr, len) = addr.as_ffi_pair();
@@ -1471,7 +1604,7 @@ pub fn bind(fd: RawFd, addr: &SockAddr) -> Result<()> {
/// Accept a connection on a socket
///
-/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/accept.html)
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/accept.html)
pub fn accept(sockfd: RawFd) -> Result<RawFd> {
let res = unsafe { libc::accept(sockfd, ptr::null_mut(), ptr::null_mut()) };
@@ -1480,8 +1613,15 @@ pub fn accept(sockfd: RawFd) -> Result<RawFd> {
/// Accept a connection on a socket
///
-/// [Further reading](http://man7.org/linux/man-pages/man2/accept.2.html)
-#[cfg(any(target_os = "android",
+/// [Further reading](https://man7.org/linux/man-pages/man2/accept.2.html)
+#[cfg(any(all(
+ target_os = "android",
+ any(
+ target_arch = "aarch64",
+ target_arch = "x86",
+ target_arch = "x86_64"
+ )
+ ),
target_os = "freebsd",
target_os = "linux",
target_os = "openbsd"))]
@@ -1493,7 +1633,7 @@ pub fn accept4(sockfd: RawFd, flags: SockFlag) -> Result<RawFd> {
/// Initiate a connection on a socket
///
-/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html)
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html)
pub fn connect(fd: RawFd, addr: &SockAddr) -> Result<()> {
let res = unsafe {
let (ptr, len) = addr.as_ffi_pair();
@@ -1506,7 +1646,7 @@ pub fn connect(fd: RawFd, addr: &SockAddr) -> Result<()> {
/// Receive data from a connection-oriented socket. Returns the number of
/// bytes read
///
-/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/recv.html)
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/recv.html)
pub fn recv(sockfd: RawFd, buf: &mut [u8], flags: MsgFlags) -> Result<usize> {
unsafe {
let ret = libc::recv(
@@ -1523,7 +1663,7 @@ pub fn recv(sockfd: RawFd, buf: &mut [u8], flags: MsgFlags) -> Result<usize> {
/// the number of bytes read and, for connectionless sockets, the socket
/// address of the sender.
///
-/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/recvfrom.html)
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvfrom.html)
pub fn recvfrom(sockfd: RawFd, buf: &mut [u8])
-> Result<(usize, Option<SockAddr>)>
{
@@ -1540,7 +1680,7 @@ pub fn recvfrom(sockfd: RawFd, buf: &mut [u8])
&mut len as *mut socklen_t))? as usize;
match sockaddr_storage_to_addr(&addr, len as usize) {
- Err(Error::Sys(Errno::ENOTCONN)) => Ok((ret, None)),
+ Err(Errno::ENOTCONN) => Ok((ret, None)),
Ok(addr) => Ok((ret, Some(addr))),
Err(e) => Err(e)
}
@@ -1549,7 +1689,7 @@ pub fn recvfrom(sockfd: RawFd, buf: &mut [u8])
/// Send a message to a socket
///
-/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/sendto.html)
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendto.html)
pub fn sendto(fd: RawFd, buf: &[u8], addr: &SockAddr, flags: MsgFlags) -> Result<usize> {
let ret = unsafe {
let (ptr, len) = addr.as_ffi_pair();
@@ -1561,7 +1701,7 @@ pub fn sendto(fd: RawFd, buf: &[u8], addr: &SockAddr, flags: MsgFlags) -> Result
/// Send data to a connection-oriented socket. Returns the number of bytes read
///
-/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/send.html)
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/send.html)
pub fn send(fd: RawFd, buf: &[u8], flags: MsgFlags) -> Result<usize> {
let ret = unsafe {
libc::send(fd, buf.as_ptr() as *const c_void, buf.len() as size_t, flags.bits())
@@ -1576,34 +1716,32 @@ pub fn send(fd: RawFd, buf: &[u8], flags: MsgFlags) -> Result<usize> {
*
*/
-/// 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<()>;
}
/// Get the current value for the requested socket option
///
-/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockopt.html)
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockopt.html)
pub fn getsockopt<O: GetSockOpt>(fd: RawFd, opt: O) -> Result<O::Val> {
opt.get(fd)
}
/// Sets the value for the requested socket option
///
-/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/setsockopt.html)
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/setsockopt.html)
///
/// # Examples
///
@@ -1624,7 +1762,7 @@ pub fn setsockopt<O: SetSockOpt>(fd: RawFd, opt: O, val: &O::Val) -> Result<()>
/// Get the address of the peer connected to the socket `fd`.
///
-/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getpeername.html)
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpeername.html)
pub fn getpeername(fd: RawFd) -> Result<SockAddr> {
unsafe {
let mut addr = mem::MaybeUninit::uninit();
@@ -1644,7 +1782,7 @@ pub fn getpeername(fd: RawFd) -> Result<SockAddr> {
/// Get the current address to which the socket `fd` is bound.
///
-/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockname.html)
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockname.html)
pub fn getsockname(fd: RawFd) -> Result<SockAddr> {
unsafe {
let mut addr = mem::MaybeUninit::uninit();
@@ -1674,21 +1812,21 @@ pub fn sockaddr_storage_to_addr(
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::Sys(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)
};
@@ -1696,15 +1834,18 @@ pub fn sockaddr_storage_to_addr(
}
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;
- assert_eq!(len as usize, mem::size_of::<sockaddr_ll>());
+ // Don't assert anything about the size.
+ // Apparently the Linux kernel can return smaller sizes when
+ // the value in the last element of sockaddr_ll (`sll_addr`) is
+ // smaller than the declared size of that field
let sll = unsafe {
*(addr as *const _ as *const sockaddr_ll)
};
@@ -1751,7 +1892,7 @@ pub enum Shutdown {
/// Shut down part of a full-duplex connection.
///
-/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/shutdown.html)
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/shutdown.html)
pub fn shutdown(df: RawFd, how: Shutdown) -> Result<()> {
unsafe {
use libc::shutdown;
@@ -1765,3 +1906,11 @@ pub fn shutdown(df: RawFd, how: Shutdown) -> Result<()> {
Errno::result(shutdown(df, how)).map(drop)
}
}
+
+#[cfg(test)]
+mod tests {
+ #[test]
+ fn can_use_cmsg_space() {
+ let _ = cmsg_space!(u8);
+ }
+}
diff --git a/src/sys/socket/sockopt.rs b/src/sys/socket/sockopt.rs
index 5b7b4fe..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 @@ const TCP_CA_NAME_MAX: usize = 16;
/// # 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 @@ const TCP_CA_NAME_MAX: usize = 16;
/// * 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 @@ macro_rules! setsockopt_impl {
/// * 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 @@ macro_rules! getsockopt_impl {
/// * `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 @@ macro_rules! getsockopt_impl {
/// * `$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 @@ macro_rules! sockopt_impl {
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,74 +242,232 @@ macro_rules! sockopt_impl {
*
*/
-sockopt_impl!(Both, ReuseAddr, libc::SOL_SOCKET, libc::SO_REUSEADDR, bool);
-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!(
+ /// Enables local address reuse
+ ReuseAddr, Both, libc::SOL_SOCKET, libc::SO_REUSEADDR, bool
+);
+#[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
+sockopt_impl!(
+ /// Permits multiple AF_INET or AF_INET6 sockets to be bound to an
+ /// identical socket address.
+ ReusePort, Both, libc::SOL_SOCKET, libc::SO_REUSEPORT, bool);
+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",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
- target_os = "openbsd"))] {
- 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);
+ target_os = "openbsd",
+ target_os = "solaris"))] {
+ 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!(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!(
+ /// 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!(
+ /// 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!(Both, RcvBuf, libc::SOL_SOCKET, libc::SO_RCVBUF, usize);
-sockopt_impl!(Both, SndBuf, libc::SOL_SOCKET, libc::SO_SNDBUF, usize);
+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!(
+ /// 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!(
+ /// 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, 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_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!(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!(
+ /// 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!(Both, BindToDevice, libc::SOL_SOCKET, libc::SO_BINDTODEVICE, OsString<[u8; libc::IFNAMSIZ]>);
+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!(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!
+ 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!(
+ /// 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",
@@ -288,7 +475,10 @@ sockopt_impl!(Both, TcpCongestion, libc::IPPROTO_TCP, libc::TCP_CONGESTION, OsSt
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",
@@ -298,7 +488,10 @@ sockopt_impl!(Both, Ipv4PacketInfo, libc::IPPROTO_IP, libc::IP_PKTINFO, bool);
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",
@@ -306,7 +499,10 @@ sockopt_impl!(Both, Ipv6RecvPacketInfo, libc::IPPROTO_IPV6, libc::IPV6_RECVPKTIN
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",
@@ -314,12 +510,49 @@ sockopt_impl!(Both, Ipv4RecvIf, libc::IPPROTO_IP, libc::IP_RECVIF, bool);
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!(
+ /// 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;
@@ -342,6 +575,8 @@ impl SetSockOpt for AlgSetAeadAuthSize {
}
}
+#[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>);
@@ -376,9 +611,9 @@ impl<T> SetSockOpt for AlgSetKey<T> where T: AsRef<[u8]> + Clone {
*/
/// 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;
@@ -390,7 +625,7 @@ unsafe trait Get<T> {
}
/// 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
@@ -407,8 +642,8 @@ struct GetStruct<T> {
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(),
@@ -434,7 +669,7 @@ struct SetStruct<'a, T: 'static> {
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 }
}
@@ -454,8 +689,8 @@ struct GetBool {
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(),
@@ -481,7 +716,7 @@ struct SetBool {
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 } }
}
@@ -501,8 +736,8 @@ struct GetU8 {
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(),
@@ -528,7 +763,7 @@ struct SetU8 {
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 }
}
@@ -548,8 +783,8 @@ struct GetUsize {
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(),
@@ -575,7 +810,7 @@ struct SetUsize {
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 }
}
@@ -595,8 +830,8 @@ struct GetOsString<T: AsMut<[u8]>> {
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(),
@@ -623,7 +858,7 @@ struct SetOsString<'a> {
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 df81a2c..c8f1041 100644
--- a/src/sys/stat.rs
+++ b/src/sys/stat.rs
@@ -9,6 +9,7 @@ use std::os::unix::io::RawFd;
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!(
);
libc_bitflags! {
+ /// "File mode / permissions" flags.
pub struct Mode: mode_t {
S_IRWXU;
S_IRUSR;
@@ -41,30 +43,45 @@ libc_bitflags! {
}
}
+/// 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) |
@@ -127,7 +144,7 @@ pub fn fstatat<P: ?Sized + NixPath>(dirfd: RawFd, pathname: &P, f: AtFlags) -> R
///
/// # References
///
-/// [fchmod(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmod.html).
+/// [fchmod(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmod.html).
pub fn fchmod(fd: RawFd, mode: Mode) -> Result<()> {
let res = unsafe { libc::fchmod(fd, mode.bits() as mode_t) };
@@ -156,7 +173,7 @@ pub enum FchmodatFlags {
///
/// # References
///
-/// [fchmodat(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmodat.html).
+/// [fchmodat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmodat.html).
#[cfg(not(target_os = "redox"))]
pub fn fchmodat<P: ?Sized + NixPath>(
dirfd: Option<RawFd>,
@@ -190,7 +207,7 @@ pub fn fchmodat<P: ?Sized + NixPath>(
///
/// # References
///
-/// [utimes(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/utimes.html).
+/// [utimes(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/utimes.html).
pub fn utimes<P: ?Sized + NixPath>(path: &P, atime: &TimeVal, mtime: &TimeVal) -> Result<()> {
let times: [libc::timeval; 2] = [*atime.as_ref(), *mtime.as_ref()];
let res = path.with_nix_path(|cstr| unsafe {
@@ -209,7 +226,7 @@ pub fn utimes<P: ?Sized + NixPath>(path: &P, atime: &TimeVal, mtime: &TimeVal) -
///
/// # References
///
-/// [lutimes(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/lutimes.html).
+/// [lutimes(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/lutimes.html).
#[cfg(any(target_os = "linux",
target_os = "haiku",
target_os = "ios",
@@ -229,7 +246,7 @@ pub fn lutimes<P: ?Sized + NixPath>(path: &P, atime: &TimeVal, mtime: &TimeVal)
///
/// # References
///
-/// [futimens(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html).
+/// [futimens(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html).
#[inline]
pub fn futimens(fd: RawFd, atime: &TimeSpec, mtime: &TimeSpec) -> Result<()> {
let times: [libc::timespec; 2] = [*atime.as_ref(), *mtime.as_ref()];
@@ -239,6 +256,7 @@ pub fn futimens(fd: RawFd, atime: &TimeSpec, mtime: &TimeSpec) -> Result<()> {
}
/// Flags for `utimensat` function.
+// TODO: replace with fcntl::AtFlags
#[derive(Clone, Copy, Debug)]
pub enum UtimensatFlags {
FollowSymlink,
@@ -260,7 +278,7 @@ pub enum UtimensatFlags {
///
/// # References
///
-/// [utimensat(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/utimens.html).
+/// [utimensat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/utimens.html).
#[cfg(not(target_os = "redox"))]
pub fn utimensat<P: ?Sized + NixPath>(
dirfd: Option<RawFd>,
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 std::ffi::CStr;
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 @@ type fs_type_t = libc::c_ulong;
#[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 @@ type fs_type_t = libc::__fsword_t;
#[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 @@ impl Debug for Statfs {
}
}
+/// 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 @@ pub fn statfs<P: ?Sized + NixPath>(path: &P) -> Result<Statfs> {
}
}
+/// 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 9bea973..15e7a7d 100644
--- a/src/sys/statvfs.rs
+++ b/src/sys/statvfs.rs
@@ -1,6 +1,6 @@
//! Get filesystem statistics
//!
-//! See [the man pages](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fstatvfs.html)
+//! See [the man pages](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fstatvfs.html)
//! for more details.
use std::mem;
use std::os::unix::io::AsRawFd;
@@ -54,7 +54,7 @@ libc_bitflags!(
/// Wrapper around the POSIX `statvfs` struct
///
-/// For more information see the [`statvfs(3)` man pages](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_statvfs.h.html).
+/// For more information see the [`statvfs(3)` man pages](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_statvfs.h.html).
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Statvfs(libc::statvfs);
@@ -150,7 +150,7 @@ mod test {
#[test]
fn statvfs_call() {
- statvfs("/".as_bytes()).unwrap();
+ statvfs(&b"/"[..]).unwrap();
}
#[test]
diff --git a/src/sys/sysinfo.rs b/src/sys/sysinfo.rs
index 222a2fc..dc943c1 100644
--- a/src/sys/sysinfo.rs
+++ b/src/sys/sysinfo.rs
@@ -71,7 +71,7 @@ impl SysInfo {
/// Returns system information.
///
-/// [See `sysinfo(2)`](http://man7.org/linux/man-pages/man2/sysinfo.2.html).
+/// [See `sysinfo(2)`](https://man7.org/linux/man-pages/man2/sysinfo.2.html).
pub fn sysinfo() -> Result<SysInfo> {
let mut info = mem::MaybeUninit::uninit();
let res = unsafe { libc::sysinfo(info.as_mut_ptr()) };
diff --git a/src/sys/termios.rs b/src/sys/termios.rs
index c30de80..01d4608 100644
--- a/src/sys/termios.rs
+++ b/src/sys/termios.rs
@@ -5,7 +5,7 @@
//! types here or exported directly.
//!
//! If you are unfamiliar with the `termios` API, you should first read the
-//! [API documentation](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/termios.h.html) and
+//! [API documentation](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/termios.h.html) and
//! then come back to understand how `nix` safely wraps it.
//!
//! It should be noted that this API incurs some runtime overhead above the base `libc` definitions.
@@ -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 @@ libc_enum!{
/// 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,
@@ -299,11 +300,17 @@ libc_enum!{
target_os = "openbsd"))]
B76800,
B115200,
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ B153600,
B230400,
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ B307200,
#[cfg(any(target_os = "android",
target_os = "freebsd",
+ target_os = "illumos",
target_os = "linux",
- target_os = "netbsd"))]
+ target_os = "netbsd",
+ target_os = "solaris"))]
B460800,
#[cfg(any(target_os = "android", target_os = "linux"))]
B500000,
@@ -311,8 +318,10 @@ libc_enum!{
B576000,
#[cfg(any(target_os = "android",
target_os = "freebsd",
+ target_os = "illumos",
target_os = "linux",
- target_os = "netbsd"))]
+ target_os = "netbsd",
+ target_os = "solaris"))]
B921600,
#[cfg(any(target_os = "android", target_os = "linux"))]
B1000000,
@@ -331,107 +340,7 @@ libc_enum!{
#[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};
-
- 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),
- B230400 => Ok(BaudRate::B230400),
- #[cfg(any(target_os = "android",
- target_os = "freebsd",
- target_os = "linux",
- target_os = "netbsd"))]
- 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 = "linux",
- target_os = "netbsd"))]
- 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::invalid_argument())
- }
- }
+ impl TryFrom<libc::speed_t>
}
#[cfg(any(target_os = "freebsd",
@@ -452,6 +361,7 @@ libc_enum! {
///
/// Used as an argument to `tcsetattr()`
#[repr(i32)]
+ #[non_exhaustive]
pub enum SetArg {
/// The change will occur immediately
TCSANOW,
@@ -467,6 +377,7 @@ libc_enum! {
///
/// Used as an argument to `tcflush()`.
#[repr(i32)]
+ #[non_exhaustive]
pub enum FlushArg {
/// Flush data that was received but not read
TCIFLUSH,
@@ -482,6 +393,7 @@ libc_enum! {
///
/// Used as an argument to `tcflow()`.
#[repr(i32)]
+ #[non_exhaustive]
pub enum FlowArg {
/// Suspend transmission
TCOOFF,
@@ -498,41 +410,51 @@ libc_enum! {
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",
target_os = "freebsd",
+ target_os = "illumos",
target_os = "macos",
target_os = "netbsd",
- target_os = "openbsd"))]
+ target_os = "openbsd",
+ target_os = "solaris"))]
VDSUSP,
VEOF,
VEOL,
VEOL2,
VERASE,
- #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "solaris"))]
VERASE2,
VINTR,
VKILL,
VLNEXT,
- #[cfg(not(all(target_os = "linux", target_arch = "sparc64")))]
+ #[cfg(not(any(all(target_os = "linux", target_arch = "sparc64"),
+ target_os = "illumos", target_os = "solaris")))]
VMIN,
VQUIT,
VREPRINT,
VSTART,
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
+ target_os = "illumos",
target_os = "macos",
target_os = "netbsd",
- target_os = "openbsd"))]
+ target_os = "openbsd",
+ target_os = "solaris"))]
VSTATUS,
VSTOP,
VSUSP,
#[cfg(target_os = "linux")]
VSWTC,
- #[cfg(target_os = "haiku")]
+ #[cfg(any(target_os = "haiku", target_os = "illumos", target_os = "solaris"))]
VSWTCH,
- #[cfg(not(all(target_os = "linux", target_arch = "sparc64")))]
+ #[cfg(not(any(all(target_os = "linux", target_arch = "sparc64"),
+ target_os = "illumos", target_os = "solaris")))]
VTIME,
VWERASE,
#[cfg(target_os = "dragonfly")]
@@ -540,7 +462,8 @@ libc_enum! {
}
}
-#[cfg(all(target_os = "linux", target_arch = "sparc64"))]
+#[cfg(any(all(target_os = "linux", target_arch = "sparc64"),
+ target_os = "illumos", target_os = "solaris"))]
impl SpecialCharacterIndices {
pub const VMIN: SpecialCharacterIndices = SpecialCharacterIndices::VEOF;
pub const VTIME: SpecialCharacterIndices = SpecialCharacterIndices::VEOL;
@@ -569,7 +492,9 @@ libc_bitflags! {
ICRNL;
IXON;
IXOFF;
+ #[cfg(not(target_os = "redox"))]
IXANY;
+ #[cfg(not(target_os = "redox"))]
IMAXBEL;
#[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
IUTF8;
@@ -876,7 +801,7 @@ cfg_if!{
target_os = "netbsd",
target_os = "openbsd"))] {
/// Get input baud rate (see
- /// [cfgetispeed(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetispeed.html)).
+ /// [cfgetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetispeed.html)).
///
/// `cfgetispeed()` extracts the input baud rate from the given `Termios` structure.
pub fn cfgetispeed(termios: &Termios) -> u32 {
@@ -885,7 +810,7 @@ cfg_if!{
}
/// Get output baud rate (see
- /// [cfgetospeed(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetospeed.html)).
+ /// [cfgetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetospeed.html)).
///
/// `cfgetospeed()` extracts the output baud rate from the given `Termios` structure.
pub fn cfgetospeed(termios: &Termios) -> u32 {
@@ -894,7 +819,7 @@ cfg_if!{
}
/// Set input baud rate (see
- /// [cfsetispeed(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetispeed.html)).
+ /// [cfsetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetispeed.html)).
///
/// `cfsetispeed()` sets the intput baud rate in the given `Termios` structure.
pub fn cfsetispeed<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> {
@@ -905,7 +830,7 @@ cfg_if!{
}
/// Set output baud rate (see
- /// [cfsetospeed(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetospeed.html)).
+ /// [cfsetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetospeed.html)).
///
/// `cfsetospeed()` sets the output baud rate in the given termios structure.
pub fn cfsetospeed<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> {
@@ -930,7 +855,7 @@ cfg_if!{
use std::convert::TryInto;
/// Get input baud rate (see
- /// [cfgetispeed(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetispeed.html)).
+ /// [cfgetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetispeed.html)).
///
/// `cfgetispeed()` extracts the input baud rate from the given `Termios` structure.
pub fn cfgetispeed(termios: &Termios) -> BaudRate {
@@ -939,7 +864,7 @@ cfg_if!{
}
/// Get output baud rate (see
- /// [cfgetospeed(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetospeed.html)).
+ /// [cfgetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetospeed.html)).
///
/// `cfgetospeed()` extracts the output baud rate from the given `Termios` structure.
pub fn cfgetospeed(termios: &Termios) -> BaudRate {
@@ -948,7 +873,7 @@ cfg_if!{
}
/// Set input baud rate (see
- /// [cfsetispeed(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetispeed.html)).
+ /// [cfsetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetispeed.html)).
///
/// `cfsetispeed()` sets the intput baud rate in the given `Termios` structure.
pub fn cfsetispeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
@@ -959,7 +884,7 @@ cfg_if!{
}
/// Set output baud rate (see
- /// [cfsetospeed(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetospeed.html)).
+ /// [cfsetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetospeed.html)).
///
/// `cfsetospeed()` sets the output baud rate in the given `Termios` structure.
pub fn cfsetospeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
@@ -984,7 +909,7 @@ cfg_if!{
}
/// Configures the port to something like the "raw" mode of the old Version 7 terminal driver (see
-/// [termios(3)](http://man7.org/linux/man-pages/man3/termios.3.html)).
+/// [termios(3)](https://man7.org/linux/man-pages/man3/termios.3.html)).
///
/// `cfmakeraw()` configures the termios structure such that input is available character-by-
/// character, echoing is disabled, and all special input and output processing is disabled. Note
@@ -1011,7 +936,7 @@ pub fn cfmakesane(termios: &mut Termios) {
}
/// Return the configuration of a port
-/// [tcgetattr(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetattr.html)).
+/// [tcgetattr(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetattr.html)).
///
/// `tcgetattr()` returns a `Termios` structure with the current configuration for a port. Modifying
/// this structure *will not* reconfigure the port, instead the modifications should be done to
@@ -1027,7 +952,7 @@ pub fn tcgetattr(fd: RawFd) -> Result<Termios> {
}
/// Set the configuration for a terminal (see
-/// [tcsetattr(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsetattr.html)).
+/// [tcsetattr(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsetattr.html)).
///
/// `tcsetattr()` reconfigures the given port based on a given `Termios` structure. This change
/// takes affect at a time specified by `actions`. Note that this function may return success if
@@ -1038,13 +963,13 @@ pub fn tcsetattr(fd: RawFd, actions: SetArg, termios: &Termios) -> Result<()> {
}
/// Block until all output data is written (see
-/// [tcdrain(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/tcdrain.html)).
+/// [tcdrain(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcdrain.html)).
pub fn tcdrain(fd: RawFd) -> Result<()> {
Errno::result(unsafe { libc::tcdrain(fd) }).map(drop)
}
/// Suspend or resume the transmission or reception of data (see
-/// [tcflow(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/tcflow.html)).
+/// [tcflow(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcflow.html)).
///
/// `tcflow()` suspends of resumes the transmission or reception of data for the given port
/// depending on the value of `action`.
@@ -1053,7 +978,7 @@ pub fn tcflow(fd: RawFd, action: FlowArg) -> Result<()> {
}
/// Discard data in the output or input queue (see
-/// [tcflush(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/tcflush.html)).
+/// [tcflush(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcflush.html)).
///
/// `tcflush()` will discard data for a terminal port in the input queue, output queue, or both
/// depending on the value of `action`.
@@ -1062,7 +987,7 @@ pub fn tcflush(fd: RawFd, action: FlushArg) -> Result<()> {
}
/// Send a break for a specific duration (see
-/// [tcsendbreak(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsendbreak.html)).
+/// [tcsendbreak(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsendbreak.html)).
///
/// When using asynchronous data transmission `tcsendbreak()` will transmit a continuous stream
/// of zero-valued bits for an implementation-defined duration.
@@ -1071,7 +996,7 @@ pub fn tcsendbreak(fd: RawFd, duration: c_int) -> Result<()> {
}
/// Get the session controlled by the given terminal (see
-/// [tcgetsid(3)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetsid.html)).
+/// [tcgetsid(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetsid.html)).
pub fn tcgetsid(fd: RawFd) -> Result<Pid> {
let res = unsafe { libc::tcgetsid(fd) };
@@ -1081,6 +1006,7 @@ pub fn tcgetsid(fd: RawFd) -> Result<Pid> {
#[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<timespec> for TimeSpec {
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 @@ impl TimeSpec {
}
#[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 @@ impl TimeVal {
}
#[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 4a24719..705a3c4 100644
--- a/src/sys/timerfd.rs
+++ b/src/sys/timerfd.rs
@@ -3,7 +3,7 @@
//! Timer FD is a Linux-only API to create timers and get expiration
//! notifications through file descriptors.
//!
-//! For more documentation, please read [timerfd_create(2)](http://man7.org/linux/man-pages/man2/timerfd_create.2.html).
+//! For more documentation, please read [timerfd_create(2)](https://man7.org/linux/man-pages/man2/timerfd_create.2.html).
//!
//! # Examples
//!
@@ -30,7 +30,7 @@
//! ```
use crate::sys::time::TimeSpec;
use crate::unistd::read;
-use crate::{errno::Errno, Error, Result};
+use crate::{errno::Errno, Result};
use bitflags::bitflags;
use libc::c_int;
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
@@ -56,8 +56,9 @@ impl FromRawFd for TimerFd {
libc_enum! {
/// 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)](http://man7.org/linux/man-pages/man2/timerfd_create.2.html).
+ /// 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 @@ bitflags! {
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 @@ impl TimerFd {
///
/// 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 {
- Error::Sys(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)
}
}
@@ -277,7 +273,7 @@ impl Drop for TimerFd {
let result = Errno::result(unsafe {
libc::close(self.fd)
});
- if let Err(Error::Sys(Errno::EBADF)) = result {
+ if let Err(Errno::EBADF) = result {
panic!("close of TimerFd encountered EBADF");
}
}
diff --git a/src/sys/uio.rs b/src/sys/uio.rs
index 6533422..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 libc::{self, c_int, c_void, size_t, off_t};
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 @@ pub fn readv(fd: RawFd, iov: &mut [IoVec<&mut [u8]>]) -> Result<usize> {
/// 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 @@ pub fn pwritev(fd: RawFd, iov: &[IoVec<&[u8]>],
/// 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 @@ pub fn preadv(fd: RawFd, iov: &[IoVec<&mut [u8]>],
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 @@ pub fn pwrite(fd: RawFd, buf: &[u8], offset: off_t) -> Result<usize> {
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,
@@ -112,7 +117,7 @@ pub struct RemoteIoVec {
///
/// This function is only available on Linux.
///
-/// [`process_vm_writev`(2)]: http://man7.org/linux/man-pages/man2/process_vm_writev.2.html
+/// [`process_vm_writev`(2)]: https://man7.org/linux/man-pages/man2/process_vm_writev.2.html
/// [ptrace]: ../ptrace/index.html
/// [`IoVec`]: struct.IoVec.html
/// [`RemoteIoVec`]: struct.RemoteIoVec.html
@@ -147,7 +152,7 @@ pub fn process_vm_writev(
///
/// This function is only available on Linux.
///
-/// [`process_vm_readv`(2)]: http://man7.org/linux/man-pages/man2/process_vm_readv.2.html
+/// [`process_vm_readv`(2)]: https://man7.org/linux/man-pages/man2/process_vm_readv.2.html
/// [`ptrace`]: ../ptrace/index.html
/// [`IoVec`]: struct.IoVec.html
/// [`RemoteIoVec`]: struct.RemoteIoVec.html
@@ -166,11 +171,17 @@ pub fn process_vm_readv(
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>(libc::iovec, PhantomData<T>);
+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;
@@ -184,6 +195,15 @@ impl<T> IoVec<T> {
}
impl<'a> IoVec<&'a [u8]> {
+ #[cfg(target_os = "freebsd")]
+ pub(crate) fn from_raw_parts(base: *mut c_void, len: usize) -> Self {
+ IoVec(libc::iovec {
+ iov_base: base,
+ iov_len: len
+ }, PhantomData)
+ }
+
+ /// Create an `IoVec` from a Rust slice.
pub fn from_slice(buf: &'a [u8]) -> IoVec<&'a [u8]> {
IoVec(libc::iovec {
iov_base: buf.as_ptr() as *mut c_void,
@@ -193,6 +213,7 @@ impl<'a> IoVec<&'a [u8]> {
}
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 faf8543..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 libc::{self, c_int};
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 @@ libc_bitflags!(
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 @@ libc_bitflags!(
/// 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;
}
@@ -81,14 +95,14 @@ pub enum WaitStatus {
/// field is the `PTRACE_EVENT_*` value of the event.
///
/// [`nix::sys::ptrace`]: ../ptrace/index.html
- /// [`ptrace`(2)]: http://man7.org/linux/man-pages/man2/ptrace.2.html
+ /// [`ptrace`(2)]: https://man7.org/linux/man-pages/man2/ptrace.2.html
#[cfg(any(target_os = "linux", target_os = "android"))]
PtraceEvent(Pid, Signal, c_int),
/// The traced process was stopped by execution of a system call,
/// and `PTRACE_O_TRACESYSGOOD` is in effect. See [`ptrace`(2)] for
/// more information.
///
- /// [`ptrace`(2)]: http://man7.org/linux/man-pages/man2/ptrace.2.html
+ /// [`ptrace`(2)]: https://man7.org/linux/man-pages/man2/ptrace.2.html
#[cfg(any(target_os = "linux", target_os = "android"))]
PtraceSyscall(Pid),
/// The process was previously stopped but has resumed execution
@@ -213,6 +227,9 @@ impl WaitStatus {
}
}
+/// 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 @@ pub fn waitpid<P: Into<Option<Pid>>>(pid: P, options: Option<WaitPidFlag>) -> Re
}
}
+/// 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 e6c3f8d..6275b59 100644
--- a/src/time.rs
+++ b/src/time.rs
@@ -6,7 +6,7 @@ use crate::sys::time::TimeSpec;
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 @@ pub struct ClockId(clockid_t);
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 @@ impl ClockId {
}
/// 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
}
@@ -185,9 +185,9 @@ impl ClockId {
pub const CLOCK_VIRTUAL: ClockId = ClockId(libc::CLOCK_VIRTUAL);
}
-impl Into<clockid_t> for ClockId {
- fn into(self) -> clockid_t {
- self.as_raw()
+impl From<ClockId> for clockid_t {
+ fn from(clock_id: ClockId) -> Self {
+ clock_id.as_raw()
}
}
@@ -255,6 +255,6 @@ pub fn clock_getcpuclockid(pid: Pid) -> Result<ClockId> {
let res = unsafe { clk_id.assume_init() };
Ok(ClockId::from(res))
} else {
- Err(Error::Sys(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 7a4517e..2c89d77 100644
--- a/src/unistd.rs
+++ b/src/unistd.rs
@@ -29,6 +29,9 @@ pub use self::pivot_root::*;
target_os = "linux", target_os = "openbsd"))]
pub use self::setres::*;
+#[cfg(any(target_os = "android", target_os = "linux"))]
+pub use self::getres::*;
+
/// User identifier
///
/// Newtype pattern around `uid_t` (which is just alias). It prevents bugs caused by accidentally
@@ -38,7 +41,7 @@ pub struct Uid(uid_t);
impl Uid {
/// Creates `Uid` from raw `uid_t`.
- pub fn from_raw(uid: uid_t) -> Self {
+ pub const fn from_raw(uid: uid_t) -> Self {
Uid(uid)
}
@@ -53,12 +56,12 @@ impl Uid {
}
/// Returns true if the `Uid` represents privileged user - root. (If it equals zero.)
- pub fn is_root(self) -> bool {
- self == ROOT
+ pub const fn is_root(self) -> bool {
+ self.0 == ROOT.0
}
/// Get the raw `uid_t` wrapped by `self`.
- pub fn as_raw(self) -> uid_t {
+ pub const fn as_raw(self) -> uid_t {
self.0
}
}
@@ -87,7 +90,7 @@ pub struct Gid(gid_t);
impl Gid {
/// Creates `Gid` from raw `gid_t`.
- pub fn from_raw(gid: gid_t) -> Self {
+ pub const fn from_raw(gid: gid_t) -> Self {
Gid(gid)
}
@@ -102,7 +105,7 @@ impl Gid {
}
/// Get the raw `gid_t` wrapped by `self`.
- pub fn as_raw(self) -> gid_t {
+ pub const fn as_raw(self) -> gid_t {
self.0
}
}
@@ -128,7 +131,7 @@ pub struct Pid(pid_t);
impl Pid {
/// Creates `Pid` from raw `pid_t`.
- pub fn from_raw(pid: pid_t) -> Self {
+ pub const fn from_raw(pid: pid_t) -> Self {
Pid(pid)
}
@@ -143,7 +146,7 @@ impl Pid {
}
/// Get the raw `pid_t` wrapped by `self`.
- pub fn as_raw(self) -> pid_t {
+ pub const fn as_raw(self) -> pid_t {
self.0
}
}
@@ -177,10 +180,7 @@ impl ForkResult {
/// 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()`
@@ -191,20 +191,25 @@ impl ForkResult {
}
/// Create a new child process duplicating the parent process ([see
-/// fork(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html)).
+/// fork(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html)).
///
/// After calling the fork system call (successfully) two processes will
/// be created that are identical with the exception of their pid and the
/// 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) => {
+/// // 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) };
/// }
-/// Ok(ForkResult::Child) => println!("I'm a new child process"),
/// Err(_) => println!("Fork failed"),
/// }
/// ```
@@ -228,7 +233,7 @@ impl ForkResult {
/// Those functions are only a small subset of your operating system's API, so
/// special care must be taken to only invoke code you can control and audit.
///
-/// [async-signal-safe]: http://man7.org/linux/man-pages/man7/signal-safety.7.html
+/// [async-signal-safe]: https://man7.org/linux/man-pages/man7/signal-safety.7.html
#[inline]
pub unsafe fn fork() -> Result<ForkResult> {
use self::ForkResult::*;
@@ -241,7 +246,7 @@ pub unsafe fn fork() -> Result<ForkResult> {
}
/// Get the pid of this process (see
-/// [getpid(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getpid.html)).
+/// [getpid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpid.html)).
///
/// Since you are running code, there is always a pid to return, so there
/// is no error case that needs to be handled.
@@ -251,7 +256,7 @@ pub fn getpid() -> Pid {
}
/// Get the pid of this processes' parent (see
-/// [getpid(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getppid.html)).
+/// [getpid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getppid.html)).
///
/// There is always a parent pid to return, so there is no error case that needs
/// to be handled.
@@ -261,7 +266,7 @@ pub fn getppid() -> Pid {
}
/// Set a process group ID (see
-/// [setpgid(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/setpgid.html)).
+/// [setpgid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/setpgid.html)).
///
/// Set the process group id (PGID) of a particular process. If a pid of zero
/// is specified, then the pid of the calling process is used. Process groups
@@ -281,14 +286,14 @@ pub fn getpgid(pid: Option<Pid>) -> Result<Pid> {
}
/// Create new session and set process group id (see
-/// [setsid(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/setsid.html)).
+/// [setsid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/setsid.html)).
#[inline]
pub fn setsid() -> Result<Pid> {
Errno::result(unsafe { libc::setsid() }).map(Pid)
}
/// Get the process group ID of a session leader
-/// [getsid(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getsid.html).
+/// [getsid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsid.html).
///
/// Obtain the process group ID of the process that is the session leader of the process specified
/// by pid. If pid is zero, it specifies the calling process.
@@ -301,7 +306,7 @@ pub fn getsid(pid: Option<Pid>) -> Result<Pid> {
/// Get the terminal foreground process group (see
-/// [tcgetpgrp(3)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetpgrp.html)).
+/// [tcgetpgrp(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetpgrp.html)).
///
/// Get the group process id (GPID) of the foreground process group on the
/// terminal associated to file descriptor (FD).
@@ -311,7 +316,7 @@ pub fn tcgetpgrp(fd: c_int) -> Result<Pid> {
Errno::result(res).map(Pid)
}
/// Set the terminal foreground process group (see
-/// [tcgetpgrp(3)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsetpgrp.html)).
+/// [tcgetpgrp(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsetpgrp.html)).
///
/// Get the group process id (PGID) to the foreground process group on the
/// terminal associated to file descriptor (FD).
@@ -323,7 +328,7 @@ pub fn tcsetpgrp(fd: c_int, pgrp: Pid) -> Result<()> {
/// Get the group id of the calling process (see
-///[getpgrp(3)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getpgrp.html)).
+///[getpgrp(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpgrp.html)).
///
/// Get the process group id (PGID) of the calling process.
/// According to the man page it is always successful.
@@ -333,7 +338,7 @@ pub fn getpgrp() -> Pid {
}
/// Get the caller's thread ID (see
-/// [gettid(2)](http://man7.org/linux/man-pages/man2/gettid.2.html).
+/// [gettid(2)](https://man7.org/linux/man-pages/man2/gettid.2.html).
///
/// This function is only available on Linux based systems. In a single
/// threaded process, the main thread will have the same ID as the process. In
@@ -349,7 +354,7 @@ pub fn gettid() -> Pid {
}
/// Create a copy of the specified file descriptor (see
-/// [dup(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/dup.html)).
+/// [dup(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/dup.html)).
///
/// The new file descriptor will be have a new index but refer to the same
/// resource as the old file descriptor and the old and new file descriptors may
@@ -366,7 +371,7 @@ pub fn dup(oldfd: RawFd) -> Result<RawFd> {
}
/// Create a copy of the specified file descriptor using the specified fd (see
-/// [dup(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/dup.html)).
+/// [dup(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/dup.html)).
///
/// This function behaves similar to `dup()` except that it will try to use the
/// specified fd instead of allocating a new one. See the man pages for more
@@ -379,7 +384,7 @@ pub fn dup2(oldfd: RawFd, newfd: RawFd) -> Result<RawFd> {
}
/// Create a new copy of the specified file descriptor using the specified fd
-/// and flags (see [dup(2)](http://man7.org/linux/man-pages/man2/dup.2.html)).
+/// and flags (see [dup(2)](https://man7.org/linux/man-pages/man2/dup.2.html)).
///
/// This function behaves similar to `dup2()` but allows for flags to be
/// specified.
@@ -390,7 +395,7 @@ pub fn dup3(oldfd: RawFd, newfd: RawFd, flags: OFlag) -> Result<RawFd> {
#[inline]
fn dup3_polyfill(oldfd: RawFd, newfd: RawFd, flags: OFlag) -> Result<RawFd> {
if oldfd == newfd {
- return Err(Error::Sys(Errno::EINVAL));
+ return Err(Errno::EINVAL);
}
let fd = dup2(oldfd, newfd)?;
@@ -406,7 +411,7 @@ fn dup3_polyfill(oldfd: RawFd, newfd: RawFd, flags: OFlag) -> Result<RawFd> {
}
/// Change the current working directory of the calling process (see
-/// [chdir(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/chdir.html)).
+/// [chdir(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/chdir.html)).
///
/// This function may fail in a number of different scenarios. See the man
/// pages for additional details on possible failure cases.
@@ -421,7 +426,7 @@ pub fn chdir<P: ?Sized + NixPath>(path: &P) -> Result<()> {
/// Change the current working directory of the process to the one
/// given as an open file descriptor (see
-/// [fchdir(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fchdir.html)).
+/// [fchdir(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchdir.html)).
///
/// This function may fail in a number of different scenarios. See the man
/// pages for additional details on possible failure cases.
@@ -433,7 +438,7 @@ pub fn fchdir(dirfd: RawFd) -> Result<()> {
Errno::result(res).map(drop)
}
-/// Creates new directory `path` with access rights `mode`. (see [mkdir(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdir.html))
+/// Creates new directory `path` with access rights `mode`. (see [mkdir(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdir.html))
///
/// # Errors
///
@@ -450,15 +455,13 @@ pub fn fchdir(dirfd: RawFd) -> Result<()> {
/// use nix::sys::stat;
/// use tempfile::tempdir;
///
-/// fn main() {
-/// let tmp_dir1 = tempdir().unwrap();
-/// let tmp_dir2 = tmp_dir1.path().join("new_dir");
+/// let tmp_dir1 = tempdir().unwrap();
+/// let tmp_dir2 = tmp_dir1.path().join("new_dir");
///
-/// // create new directory and give read, write and execute rights to the owner
-/// match unistd::mkdir(&tmp_dir2, stat::Mode::S_IRWXU) {
-/// Ok(_) => println!("created {:?}", tmp_dir2),
-/// Err(err) => println!("Error creating directory: {}", err),
-/// }
+/// // create new directory and give read, write and execute rights to the owner
+/// match unistd::mkdir(&tmp_dir2, stat::Mode::S_IRWXU) {
+/// Ok(_) => println!("created {:?}", tmp_dir2),
+/// Err(err) => println!("Error creating directory: {}", err),
/// }
/// ```
#[inline]
@@ -481,7 +484,7 @@ pub fn mkdir<P: ?Sized + NixPath>(path: &P, mode: Mode) -> Result<()> {
/// - the path name is too long (longer than `PATH_MAX`, usually 4096 on linux, 1024 on OS X)
///
/// For a full list consult
-/// [posix specification](http://pubs.opengroup.org/onlinepubs/9699919799/functions/mkfifo.html)
+/// [posix specification](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkfifo.html)
///
/// # Example
///
@@ -490,15 +493,13 @@ pub fn mkdir<P: ?Sized + NixPath>(path: &P, mode: Mode) -> Result<()> {
/// use nix::sys::stat;
/// use tempfile::tempdir;
///
-/// fn main() {
-/// let tmp_dir = tempdir().unwrap();
-/// let fifo_path = tmp_dir.path().join("foo.pipe");
+/// let tmp_dir = tempdir().unwrap();
+/// let fifo_path = tmp_dir.path().join("foo.pipe");
///
-/// // create new fifo and give read, write and execute rights to the owner
-/// match unistd::mkfifo(&fifo_path, stat::Mode::S_IRWXU) {
-/// Ok(_) => println!("created {:?}", fifo_path),
-/// Err(err) => println!("Error creating fifo: {}", err),
-/// }
+/// // create new fifo and give read, write and execute rights to the owner
+/// match unistd::mkfifo(&fifo_path, stat::Mode::S_IRWXU) {
+/// Ok(_) => println!("created {:?}", fifo_path),
+/// Err(err) => println!("Error creating fifo: {}", err),
/// }
/// ```
#[inline]
@@ -519,7 +520,7 @@ pub fn mkfifo<P: ?Sized + NixPath>(path: &P, mode: Mode) -> Result<()> {
///
/// # References
///
-/// [mkfifoat(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/mkfifoat.html).
+/// [mkfifoat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkfifoat.html).
// mkfifoat is not implemented in OSX or android
#[inline]
#[cfg(not(any(
@@ -541,7 +542,7 @@ pub fn mkfifoat<P: ?Sized + NixPath>(dirfd: Option<RawFd>, path: &P, mode: Mode)
/// If `dirfd` is `None`, then `path2` is relative to the current working
/// directory. This is identical to `libc::symlink(path1, path2)`.
///
-/// See also [symlinkat(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/symlinkat.html).
+/// See also [symlinkat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/symlinkat.html).
#[cfg(not(target_os = "redox"))]
pub fn symlinkat<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
path1: &P1,
@@ -568,7 +569,7 @@ fn reserve_double_buffer_size<T>(buf: &mut Vec<T>, limit: usize) -> Result<()> {
use std::cmp::min;
if buf.capacity() >= limit {
- return Err(Error::Sys(Errno::ERANGE))
+ return Err(Errno::ERANGE)
}
let capacity = min(buf.capacity() * 2, limit);
@@ -587,11 +588,9 @@ fn reserve_double_buffer_size<T>(buf: &mut Vec<T>, limit: usize) -> Result<()> {
/// ```rust
/// use nix::unistd;
///
-/// fn main() {
-/// // assume that we are allowed to get current directory
-/// let dir = unistd::getcwd().unwrap();
-/// println!("The current directory is {:?}", dir);
-/// }
+/// // assume that we are allowed to get current directory
+/// let dir = unistd::getcwd().unwrap();
+/// println!("The current directory is {:?}", dir);
/// ```
#[inline]
pub fn getcwd() -> Result<PathBuf> {
@@ -613,9 +612,9 @@ pub fn getcwd() -> Result<PathBuf> {
let error = Errno::last();
// ERANGE means buffer was too small to store directory name
if error != Errno::ERANGE {
- return Err(Error::Sys(error));
+ return Err(error);
}
- }
+ }
// Trigger the internal buffer resizing logic.
reserve_double_buffer_size(&mut buf, PATH_MAX as usize)?;
@@ -624,6 +623,8 @@ pub fn getcwd() -> Result<PathBuf> {
}
/// Computes the raw UID and GID values to pass to a `*chown` call.
+// The cast is not unnecessary on all platforms.
+#[allow(clippy::unnecessary_cast)]
fn chown_raw_ids(owner: Option<Uid>, group: Option<Gid>) -> (libc::uid_t, libc::gid_t) {
// According to the POSIX specification, -1 is used to indicate that owner and group
// are not to be changed. Since uid_t and gid_t are unsigned types, we have to wrap
@@ -637,7 +638,7 @@ fn chown_raw_ids(owner: Option<Uid>, group: Option<Gid>) -> (libc::uid_t, libc::
/// Change the ownership of the file at `path` to be owned by the specified
/// `owner` (user) and `group` (see
-/// [chown(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/chown.html)).
+/// [chown(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/chown.html)).
///
/// The owner/group for the provided path name will not be modified if `None` is
/// provided for that argument. Ownership change will be attempted for the path
@@ -693,7 +694,7 @@ pub enum FchownatFlags {
///
/// # References
///
-/// [fchownat(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fchownat.html).
+/// [fchownat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchownat.html).
#[cfg(not(target_os = "redox"))]
pub fn fchownat<P: ?Sized + NixPath>(
dirfd: Option<RawFd>,
@@ -722,7 +723,7 @@ fn to_exec_array<S: AsRef<CStr>>(args: &[S]) -> Vec<*const c_char> {
}
/// Replace the current process image with a new one (see
-/// [exec(3)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html)).
+/// [exec(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html)).
///
/// See the `::nix::unistd::execve` system call for additional details. `execv`
/// performs the same action but does not allow for customization of the
@@ -735,12 +736,12 @@ pub fn execv<S: AsRef<CStr>>(path: &CStr, argv: &[S]) -> Result<Infallible> {
libc::execv(path.as_ptr(), args_p.as_ptr())
};
- Err(Error::Sys(Errno::last()))
+ Err(Errno::last())
}
/// Replace the current process image with a new one (see
-/// [execve(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html)).
+/// [execve(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html)).
///
/// The execve system call allows for another process to be "called" which will
/// replace the current process image. That is, this process becomes the new
@@ -760,12 +761,12 @@ pub fn execve<SA: AsRef<CStr>, SE: AsRef<CStr>>(path: &CStr, args: &[SA], env: &
libc::execve(path.as_ptr(), args_p.as_ptr(), env_p.as_ptr())
};
- Err(Error::Sys(Errno::last()))
+ Err(Errno::last())
}
/// Replace the current process image with a new one and replicate shell `PATH`
/// searching behavior (see
-/// [exec(3)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html)).
+/// [exec(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html)).
///
/// See `::nix::unistd::execve` for additional details. `execvp` behaves the
/// same as execv except that it will examine the `PATH` environment variables
@@ -780,12 +781,12 @@ pub fn execvp<S: AsRef<CStr>>(filename: &CStr, args: &[S]) -> Result<Infallible>
libc::execvp(filename.as_ptr(), args_p.as_ptr())
};
- Err(Error::Sys(Errno::last()))
+ Err(Errno::last())
}
/// Replace the current process image with a new one and replicate shell `PATH`
/// searching behavior (see
-/// [`execvpe(3)`](http://man7.org/linux/man-pages/man3/exec.3.html)).
+/// [`execvpe(3)`](https://man7.org/linux/man-pages/man3/exec.3.html)).
///
/// This functions like a combination of `execvp(2)` and `execve(2)` to pass an
/// environment and have a search path. See these two for additional
@@ -801,11 +802,11 @@ pub fn execvpe<SA: AsRef<CStr>, SE: AsRef<CStr>>(filename: &CStr, args: &[SA], e
libc::execvpe(filename.as_ptr(), args_p.as_ptr(), env_p.as_ptr())
};
- Err(Error::Sys(Errno::last()))
+ Err(Errno::last())
}
/// Replace the current process image with a new one (see
-/// [fexecve(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fexecve.html)).
+/// [fexecve(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fexecve.html)).
///
/// The `fexecve` function allows for another process to be "called" which will
/// replace the current process image. That is, this process becomes the new
@@ -829,11 +830,11 @@ pub fn fexecve<SA: AsRef<CStr> ,SE: AsRef<CStr>>(fd: RawFd, args: &[SA], env: &[
libc::fexecve(fd, args_p.as_ptr(), env_p.as_ptr())
};
- Err(Error::Sys(Errno::last()))
+ Err(Errno::last())
}
/// Execute program relative to a directory file descriptor (see
-/// [execveat(2)](http://man7.org/linux/man-pages/man2/execveat.2.html)).
+/// [execveat(2)](https://man7.org/linux/man-pages/man2/execveat.2.html)).
///
/// The `execveat` function allows for another process to be "called" which will
/// replace the current process image. That is, this process becomes the new
@@ -854,11 +855,11 @@ pub fn execveat<SA: AsRef<CStr>,SE: AsRef<CStr>>(dirfd: RawFd, pathname: &CStr,
args_p.as_ptr(), env_p.as_ptr(), flags);
};
- Err(Error::Sys(Errno::last()))
+ Err(Errno::last())
}
/// Daemonize this process by detaching from the controlling terminal (see
-/// [daemon(3)](http://man7.org/linux/man-pages/man3/daemon.3.html)).
+/// [daemon(3)](https://man7.org/linux/man-pages/man3/daemon.3.html)).
///
/// When a process is launched it is typically associated with a parent and it,
/// in turn, by its controlling terminal/process. In order for a process to run
@@ -885,16 +886,18 @@ pub fn execveat<SA: AsRef<CStr>,SE: AsRef<CStr>>(dirfd: RawFd, pathname: &CStr,
#[cfg(any(target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
+ target_os = "illumos",
target_os = "linux",
target_os = "netbsd",
- target_os = "openbsd"))]
+ target_os = "openbsd",
+ target_os = "solaris"))]
pub fn daemon(nochdir: bool, noclose: bool) -> Result<()> {
let res = unsafe { libc::daemon(nochdir as c_int, noclose as c_int) };
Errno::result(res).map(drop)
}
/// Set the system host name (see
-/// [sethostname(2)](http://man7.org/linux/man-pages/man2/gethostname.2.html)).
+/// [sethostname(2)](https://man7.org/linux/man-pages/man2/gethostname.2.html)).
///
/// Given a name, attempt to update the system host name to the given string.
/// On some systems, the host name is limited to as few as 64 bytes. An error
@@ -906,8 +909,10 @@ pub fn sethostname<S: AsRef<OsStr>>(name: S) -> Result<()> {
cfg_if! {
if #[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
+ target_os = "illumos",
target_os = "ios",
- target_os = "macos", ))] {
+ target_os = "macos",
+ target_os = "solaris", ))] {
type sethostname_len_t = c_int;
} else {
type sethostname_len_t = size_t;
@@ -922,7 +927,7 @@ pub fn sethostname<S: AsRef<OsStr>>(name: S) -> Result<()> {
/// Get the host name and store it in the provided buffer, returning a pointer
/// the `CStr` in that buffer on success (see
-/// [gethostname(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/gethostname.html)).
+/// [gethostname(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/gethostname.html)).
///
/// This function call attempts to get the host name for the running system and
/// store it in a provided buffer. The buffer will be populated with bytes up
@@ -957,7 +962,7 @@ pub fn gethostname(buffer: &mut [u8]) -> Result<&CStr> {
/// `std::fs::File`. Explicitly closing them with this method too can result in
/// a double-close condition, which can cause confusing `EBADF` errors in
/// seemingly unrelated code. Caveat programmer. See also
-/// [close(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html).
+/// [close(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html).
///
/// # Examples
///
@@ -965,20 +970,16 @@ pub fn gethostname(buffer: &mut [u8]) -> Result<&CStr> {
/// use std::os::unix::io::AsRawFd;
/// use nix::unistd::close;
///
-/// fn main() {
-/// let f = tempfile::tempfile().unwrap();
-/// close(f.as_raw_fd()).unwrap(); // Bad! f will also close on drop!
-/// }
+/// let f = tempfile::tempfile().unwrap();
+/// close(f.as_raw_fd()).unwrap(); // Bad! f will also close on drop!
/// ```
///
/// ```rust
/// use std::os::unix::io::IntoRawFd;
/// use nix::unistd::close;
///
-/// fn main() {
-/// let f = tempfile::tempfile().unwrap();
-/// close(f.into_raw_fd()).unwrap(); // Good. into_raw_fd consumes f
-/// }
+/// let f = tempfile::tempfile().unwrap();
+/// close(f.into_raw_fd()).unwrap(); // Good. into_raw_fd consumes f
/// ```
pub fn close(fd: RawFd) -> Result<()> {
let res = unsafe { libc::close(fd) };
@@ -987,7 +988,7 @@ pub fn close(fd: RawFd) -> Result<()> {
/// Read from a raw file descriptor.
///
-/// See also [read(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/read.html)
+/// See also [read(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/read.html)
pub fn read(fd: RawFd, buf: &mut [u8]) -> Result<usize> {
let res = unsafe { libc::read(fd, buf.as_mut_ptr() as *mut c_void, buf.len() as size_t) };
@@ -996,7 +997,7 @@ pub fn read(fd: RawFd, buf: &mut [u8]) -> Result<usize> {
/// Write to a raw file descriptor.
///
-/// See also [write(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html)
+/// See also [write(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html)
pub fn write(fd: RawFd, buf: &[u8]) -> Result<usize> {
let res = unsafe { libc::write(fd, buf.as_ptr() as *const c_void, buf.len() as size_t) };
@@ -1019,20 +1020,28 @@ pub enum Whence {
/// Specify an offset relative to the next location in the file greater than or
/// equal to offset that contains some data. If offset points to
/// some data, then the file offset is set to offset.
- #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "linux"))]
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris"))]
SeekData = libc::SEEK_DATA,
/// Specify an offset relative to the next hole in the file greater than
/// or equal to offset. If offset points into the middle of a hole, then
/// the file offset should be set to offset. If there is no hole past offset,
/// then the file offset should be adjusted to the end of the file (i.e., there
/// is an implicit hole at the end of any file).
- #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "linux"))]
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris"))]
SeekHole = libc::SEEK_HOLE
}
/// Move the read/write file offset.
///
-/// See also [lseek(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/lseek.html)
+/// See also [lseek(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/lseek.html)
pub fn lseek(fd: RawFd, offset: off_t, whence: Whence) -> Result<off_t> {
let res = unsafe { libc::lseek(fd, offset, whence as i32) };
@@ -1048,14 +1057,14 @@ pub fn lseek64(fd: RawFd, offset: libc::off64_t, whence: Whence) -> Result<libc:
/// Create an interprocess channel.
///
-/// See also [pipe(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/pipe.html)
-pub fn pipe() -> Result<(RawFd, RawFd)> {
+/// See also [pipe(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pipe.html)
+pub fn pipe() -> std::result::Result<(RawFd, RawFd), Error> {
unsafe {
let mut fds = mem::MaybeUninit::<[c_int; 2]>::uninit();
let res = libc::pipe(fds.as_mut_ptr() as *mut c_int);
- Errno::result(res)?;
+ Error::result(res)?;
Ok((fds.assume_init()[0], fds.assume_init()[1]))
}
@@ -1066,20 +1075,22 @@ pub fn pipe() -> Result<(RawFd, RawFd)> {
/// 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)](http://man7.org/linux/man-pages/man2/pipe.2.html)
+/// See also [pipe(2)](https://man7.org/linux/man-pages/man2/pipe.2.html)
#[cfg(any(target_os = "android",
target_os = "dragonfly",
target_os = "emscripten",
target_os = "freebsd",
+ target_os = "illumos",
target_os = "linux",
target_os = "redox",
target_os = "netbsd",
- target_os = "openbsd"))]
+ target_os = "openbsd",
+ target_os = "solaris"))]
pub fn pipe2(flags: OFlag) -> Result<(RawFd, RawFd)> {
let mut fds = mem::MaybeUninit::<[c_int; 2]>::uninit();
@@ -1095,7 +1106,7 @@ pub fn pipe2(flags: OFlag) -> Result<(RawFd, RawFd)> {
/// Truncate a file to a specified length
///
/// See also
-/// [truncate(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/truncate.html)
+/// [truncate(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/truncate.html)
#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
pub fn truncate<P: ?Sized + NixPath>(path: &P, len: off_t) -> Result<()> {
let res = path.with_nix_path(|cstr| {
@@ -1110,7 +1121,7 @@ pub fn truncate<P: ?Sized + NixPath>(path: &P, len: off_t) -> Result<()> {
/// Truncate a file to a specified length
///
/// See also
-/// [ftruncate(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html)
+/// [ftruncate(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html)
pub fn ftruncate(fd: RawFd, len: off_t) -> Result<()> {
Errno::result(unsafe { libc::ftruncate(fd, len) }).map(drop)
}
@@ -1124,9 +1135,9 @@ pub fn isatty(fd: RawFd) -> Result<bool> {
} else {
match Errno::last() {
Errno::ENOTTY => Ok(false),
- err => Err(Error::Sys(err)),
+ err => Err(err),
}
- }
+ }
}
}
@@ -1149,7 +1160,7 @@ pub enum LinkatFlags {
/// process. If either `oldpath` or `newpath` is absolute, then `dirfd` is ignored.
///
/// # References
-/// See also [linkat(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/linkat.html)
+/// See also [linkat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/linkat.html)
#[cfg(not(target_os = "redox"))] // RedoxFS does not support symlinks yet
pub fn linkat<P: ?Sized + NixPath>(
olddirfd: Option<RawFd>,
@@ -1185,7 +1196,7 @@ pub fn linkat<P: ?Sized + NixPath>(
/// Remove a directory entry
///
-/// See also [unlink(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/unlink.html)
+/// See also [unlink(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlink.html)
pub fn unlink<P: ?Sized + NixPath>(path: &P) -> Result<()> {
let res = path.with_nix_path(|cstr| {
unsafe {
@@ -1211,7 +1222,7 @@ pub enum UnlinkatFlags {
/// is performed.
///
/// # References
-/// See also [unlinkat(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/unlinkat.html)
+/// See also [unlinkat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlinkat.html)
#[cfg(not(target_os = "redox"))]
pub fn unlinkat<P: ?Sized + NixPath>(
dirfd: Option<RawFd>,
@@ -1244,7 +1255,7 @@ pub fn chroot<P: ?Sized + NixPath>(path: &P) -> Result<()> {
/// Commit filesystem caches to disk
///
-/// See also [sync(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/sync.html)
+/// See also [sync(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sync.html)
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
@@ -1258,7 +1269,7 @@ pub fn sync() {
/// Synchronize changes to a file
///
-/// See also [fsync(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fsync.html)
+/// See also [fsync(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fsync.html)
#[inline]
pub fn fsync(fd: RawFd) -> Result<()> {
let res = unsafe { libc::fsync(fd) };
@@ -1269,12 +1280,14 @@ pub fn fsync(fd: RawFd) -> Result<()> {
/// Synchronize the data of a file
///
/// See also
-/// [fdatasync(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fdatasync.html)
+/// [fdatasync(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fdatasync.html)
// `fdatasync(2) is in POSIX, but in libc it is only defined in `libc::notbsd`.
// TODO: exclude only Apple systems after https://github.com/rust-lang/libc/pull/211
#[cfg(any(target_os = "linux",
target_os = "android",
- target_os = "emscripten"))]
+ target_os = "emscripten",
+ target_os = "illumos",
+ target_os = "solaris"))]
#[inline]
pub fn fdatasync(fd: RawFd) -> Result<()> {
let res = unsafe { libc::fdatasync(fd) };
@@ -1284,7 +1297,7 @@ pub fn fdatasync(fd: RawFd) -> Result<()> {
/// Get a real user ID
///
-/// See also [getuid(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getuid.html)
+/// See also [getuid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getuid.html)
// POSIX requires that getuid is always successful, so no need to check return
// value or errno.
#[inline]
@@ -1294,7 +1307,7 @@ pub fn getuid() -> Uid {
/// Get the effective user ID
///
-/// See also [geteuid(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/geteuid.html)
+/// See also [geteuid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/geteuid.html)
// POSIX requires that geteuid is always successful, so no need to check return
// value or errno.
#[inline]
@@ -1304,7 +1317,7 @@ pub fn geteuid() -> Uid {
/// Get the real group ID
///
-/// See also [getgid(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getgid.html)
+/// See also [getgid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getgid.html)
// POSIX requires that getgid is always successful, so no need to check return
// value or errno.
#[inline]
@@ -1314,7 +1327,7 @@ pub fn getgid() -> Gid {
/// Get the effective group ID
///
-/// See also [getegid(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getegid.html)
+/// See also [getegid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getegid.html)
// POSIX requires that getegid is always successful, so no need to check return
// value or errno.
#[inline]
@@ -1324,7 +1337,7 @@ pub fn getegid() -> Gid {
/// Set the effective user ID
///
-/// See also [seteuid(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/seteuid.html)
+/// See also [seteuid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/seteuid.html)
#[inline]
pub fn seteuid(euid: Uid) -> Result<()> {
let res = unsafe { libc::seteuid(euid.into()) };
@@ -1334,7 +1347,7 @@ pub fn seteuid(euid: Uid) -> Result<()> {
/// Set the effective group ID
///
-/// See also [setegid(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/setegid.html)
+/// See also [setegid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/setegid.html)
#[inline]
pub fn setegid(egid: Gid) -> Result<()> {
let res = unsafe { libc::setegid(egid.into()) };
@@ -1344,7 +1357,7 @@ pub fn setegid(egid: Gid) -> Result<()> {
/// Set the user ID
///
-/// See also [setuid(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/setuid.html)
+/// See also [setuid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/setuid.html)
#[inline]
pub fn setuid(uid: Uid) -> Result<()> {
let res = unsafe { libc::setuid(uid.into()) };
@@ -1354,7 +1367,7 @@ pub fn setuid(uid: Uid) -> Result<()> {
/// Set the group ID
///
-/// See also [setgid(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/setgid.html)
+/// See also [setgid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/setgid.html)
#[inline]
pub fn setgid(gid: Gid) -> Result<()> {
let res = unsafe { libc::setgid(gid.into()) };
@@ -1366,7 +1379,7 @@ pub fn setgid(gid: Gid) -> Result<()> {
/// On both success and failure, this call returns the previous filesystem user
/// ID of the caller.
///
-/// See also [setfsuid(2)](http://man7.org/linux/man-pages/man2/setfsuid.2.html)
+/// See also [setfsuid(2)](https://man7.org/linux/man-pages/man2/setfsuid.2.html)
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn setfsuid(uid: Uid) -> Uid {
let prev_fsuid = unsafe { libc::setfsuid(uid.into()) };
@@ -1377,7 +1390,7 @@ pub fn setfsuid(uid: Uid) -> Uid {
/// On both success and failure, this call returns the previous filesystem group
/// ID of the caller.
///
-/// See also [setfsgid(2)](http://man7.org/linux/man-pages/man2/setfsgid.2.html)
+/// See also [setfsgid(2)](https://man7.org/linux/man-pages/man2/setfsgid.2.html)
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn setfsgid(gid: Gid) -> Gid {
let prev_fsgid = unsafe { libc::setfsgid(gid.into()) };
@@ -1386,7 +1399,7 @@ pub fn setfsgid(gid: Gid) -> Gid {
/// Get the list of supplementary group IDs of the calling process.
///
-/// [Further reading](http://pubs.opengroup.org/onlinepubs/009695399/functions/getgroups.html)
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/009695399/functions/getgroups.html)
///
/// **Note:** This function is not available for Apple platforms. On those
/// platforms, checking group membership should be achieved via communication
@@ -1404,6 +1417,14 @@ pub fn getgroups() -> Result<Vec<Gid>> {
// 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.
@@ -1421,11 +1442,11 @@ pub fn getgroups() -> Result<Vec<Gid>> {
unsafe { groups.set_len(s as usize) };
return Ok(groups);
},
- Err(Error::Sys(Errno::EINVAL)) => {
+ Err(Errno::EINVAL) => {
// 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::Sys(Errno::EINVAL)))?;
+ .or(Err(Errno::EINVAL))?;
},
Err(e) => return Err(e)
}
@@ -1434,7 +1455,7 @@ pub fn getgroups() -> Result<Vec<Gid>> {
/// Set the list of supplementary group IDs for the calling process.
///
-/// [Further reading](http://man7.org/linux/man-pages/man2/getgroups.2.html)
+/// [Further reading](https://man7.org/linux/man-pages/man2/getgroups.2.html)
///
/// **Note:** This function is not available for Apple platforms. On those
/// platforms, group membership management should be achieved via communication
@@ -1468,9 +1489,11 @@ pub fn setgroups(groups: &[Gid]) -> Result<()> {
cfg_if! {
if #[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
+ target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
+ target_os = "illumos",
target_os = "openbsd"))] {
type setgroups_ngroups_t = c_int;
} else {
@@ -1492,7 +1515,7 @@ pub fn setgroups(groups: &[Gid]) -> Result<()> {
/// Gets the group IDs of all groups that `user` is a member of. The additional
/// group `group` is also added to the list.
///
-/// [Further reading](http://man7.org/linux/man-pages/man3/getgrouplist.3.html)
+/// [Further reading](https://man7.org/linux/man-pages/man3/getgrouplist.3.html)
///
/// **Note:** This function is not available for Apple platforms. On those
/// platforms, checking group membership should be achieved via communication
@@ -1507,15 +1530,17 @@ pub fn setgroups(groups: &[Gid]) -> Result<()> {
/// and `setgroups()`. Additionally, while some implementations will return a
/// partial list of groups when `NGROUPS_MAX` is exceeded, this implementation
/// will only ever return the complete list or else an error.
-#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox")))]
+#[cfg(not(any(target_os = "illumos",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "redox")))]
pub fn getgrouplist(user: &CStr, group: Gid) -> Result<Vec<Gid>> {
let ngroups_max = match sysconf(SysconfVar::NGROUPS_MAX) {
Ok(Some(n)) => n as c_int,
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;
@@ -1525,6 +1550,7 @@ pub fn getgrouplist(user: &CStr, group: Gid) -> Result<Vec<Gid>> {
}
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,
@@ -1542,7 +1568,7 @@ pub fn getgrouplist(user: &CStr, group: Gid) -> Result<Vec<Gid>> {
// groups as possible, but Linux manpages do not mention this
// behavior.
reserve_double_buffer_size(&mut groups, ngroups_max as usize)
- .or_else(|_| Err(Error::invalid_argument()))?;
+ .map_err(|_| Errno::EINVAL)?;
}
}
}
@@ -1553,7 +1579,7 @@ pub fn getgrouplist(user: &CStr, group: Gid) -> Result<Vec<Gid>> {
/// that `user` is a member of. The additional group `group` is also added to
/// the list.
///
-/// [Further reading](http://man7.org/linux/man-pages/man3/initgroups.3.html)
+/// [Further reading](https://man7.org/linux/man-pages/man3/initgroups.3.html)
///
/// **Note:** This function is not available for Apple platforms. On those
/// platforms, group membership management should be achieved via communication
@@ -1602,7 +1628,7 @@ pub fn initgroups(user: &CStr, group: Gid) -> Result<()> {
/// Suspend the thread until a signal is received.
///
-/// See also [pause(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/pause.html).
+/// See also [pause(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pause.html).
#[inline]
#[cfg(not(target_os = "redox"))]
pub fn pause() {
@@ -1647,17 +1673,18 @@ pub mod alarm {
//! extern fn signal_handler(_: nix::libc::c_int) { }
//! let sa = SigAction::new(
//! SigHandler::Handler(signal_handler),
- //! SaFlags::empty(),
+ //! SaFlags::SA_RESTART,
//! SigSet::empty()
//! );
//! unsafe {
//! sigaction(Signal::SIGALRM, &sa);
//! }
//!
+ //! let start = Instant::now();
+ //!
//! // Set an alarm for 1 second from now.
//! alarm::set(1);
//!
- //! let start = Instant::now();
//! // Pause the process until the alarm signal is received.
//! let mut sigset = SigSet::empty();
//! sigset.add(Signal::SIGALRM);
@@ -1668,7 +1695,7 @@ pub mod alarm {
//!
//! # References
//!
- //! See also [alarm(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/alarm.html).
+ //! See also [alarm(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/alarm.html).
/// Schedule an alarm signal.
///
@@ -1698,7 +1725,7 @@ pub mod alarm {
/// Suspend execution for an interval of time
///
-/// See also [sleep(2)](http://pubs.opengroup.org/onlinepubs/009695399/functions/sleep.html#tag_03_705_05)
+/// See also [sleep(2)](https://pubs.opengroup.org/onlinepubs/009695399/functions/sleep.html#tag_03_705_05)
// Per POSIX, does not fail
#[inline]
pub fn sleep(seconds: c_uint) -> c_uint {
@@ -1738,7 +1765,7 @@ pub mod acct {
/// Err is returned either if no temporary filename could be created or the template doesn't
/// end with XXXXXX
///
-/// See also [mkstemp(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/mkstemp.html)
+/// See also [mkstemp(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkstemp.html)
///
/// # Example
///
@@ -1769,7 +1796,7 @@ pub fn mkstemp<P: ?Sized + NixPath>(template: &P) -> Result<(RawFd, PathBuf)> {
/// Variable names for `pathconf`
///
/// Nix uses the same naming convention for these variables as the
-/// [getconf(1)](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/getconf.html) utility.
+/// [getconf(1)](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/getconf.html) utility.
/// That is, `PathconfVar` variables have the same name as the abstract
/// variables shown in the `pathconf(2)` man page. Usually, it's the same as
/// the C variable name without the leading `_PC_`.
@@ -1779,11 +1806,12 @@ pub fn mkstemp<P: ?Sized + NixPath>(template: &P) -> Result<(RawFd, PathBuf)> {
///
/// # References
///
-/// - [pathconf(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/pathconf.html)
-/// - [limits.h](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/limits.h.html)
-/// - [unistd.h](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/unistd.h.html)
+/// - [pathconf(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pathconf.html)
+/// - [limits.h](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/limits.h.html)
+/// - [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"))]
@@ -1809,8 +1837,9 @@ pub enum PathconfVar {
/// Maximum number of bytes that is guaranteed to be atomic when writing to
/// a pipe.
PIPE_BUF = libc::_PC_PIPE_BUF,
- #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "linux",
- target_os = "netbsd", target_os = "openbsd", target_os = "redox"))]
+ #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "illumos",
+ target_os = "linux", target_os = "netbsd", target_os = "openbsd",
+ target_os = "redox", target_os = "solaris"))]
/// Symbolic links can be created.
POSIX2_SYMLINKS = libc::_PC_2_SYMLINKS,
#[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd",
@@ -1836,8 +1865,8 @@ pub enum PathconfVar {
/// Recommended file transfer buffer alignment.
POSIX_REC_XFER_ALIGN = libc::_PC_REC_XFER_ALIGN,
#[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd",
- target_os = "linux", target_os = "netbsd", target_os = "openbsd",
- target_os = "redox"))]
+ target_os = "illumos", target_os = "linux", target_os = "netbsd",
+ target_os = "openbsd", target_os = "redox", target_os = "solaris"))]
/// Maximum number of bytes in a symbolic link.
SYMLINK_MAX = libc::_PC_SYMLINK_MAX,
/// The use of `chown` and `fchown` is restricted to a process with
@@ -1851,18 +1880,20 @@ pub enum PathconfVar {
/// disable terminal special character handling.
_POSIX_VDISABLE = libc::_PC_VDISABLE,
#[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd",
- target_os = "linux", target_os = "openbsd", target_os = "redox"))]
+ target_os = "illumos", target_os = "linux", target_os = "openbsd",
+ target_os = "redox", target_os = "solaris"))]
/// Asynchronous input or output operations may be performed for the
/// associated file.
_POSIX_ASYNC_IO = libc::_PC_ASYNC_IO,
#[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd",
- target_os = "linux", target_os = "openbsd", target_os = "redox"))]
+ target_os = "illumos", target_os = "linux", target_os = "openbsd",
+ target_os = "redox", target_os = "solaris"))]
/// Prioritized input or output operations may be performed for the
/// associated file.
_POSIX_PRIO_IO = libc::_PC_PRIO_IO,
#[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd",
- target_os = "linux", target_os = "netbsd", target_os = "openbsd",
- target_os = "redox"))]
+ target_os = "illumos", target_os = "linux", target_os = "netbsd",
+ target_os = "openbsd", target_os = "redox", target_os = "solaris"))]
/// Synchronized input or output operations may be performed for the
/// associated file.
_POSIX_SYNC_IO = libc::_PC_SYNC_IO,
@@ -1872,7 +1903,7 @@ pub enum PathconfVar {
}
/// Like `pathconf`, but works with file descriptors instead of paths (see
-/// [fpathconf(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/pathconf.html))
+/// [fpathconf(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pathconf.html))
///
/// # Parameters
///
@@ -1896,7 +1927,7 @@ pub fn fpathconf(fd: RawFd, var: PathconfVar) -> Result<Option<c_long>> {
if errno::errno() == 0 {
Ok(None)
} else {
- Err(Error::Sys(Errno::last()))
+ Err(Errno::last())
}
} else {
Ok(Some(raw))
@@ -1904,12 +1935,12 @@ pub fn fpathconf(fd: RawFd, var: PathconfVar) -> Result<Option<c_long>> {
}
/// Get path-dependent configurable system variables (see
-/// [pathconf(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/pathconf.html))
+/// [pathconf(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pathconf.html))
///
/// Returns the value of a path-dependent configurable system variable. Most
/// supported variables also have associated compile-time constants, but POSIX
/// allows their values to change at runtime. There are generally two types of
-/// `pathconf` variables: options and limits. See [pathconf(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/pathconf.html) for more details.
+/// `pathconf` variables: options and limits. See [pathconf(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pathconf.html) for more details.
///
/// # Parameters
///
@@ -1935,7 +1966,7 @@ pub fn pathconf<P: ?Sized + NixPath>(path: &P, var: PathconfVar) -> Result<Optio
if errno::errno() == 0 {
Ok(None)
} else {
- Err(Error::Sys(Errno::last()))
+ Err(Errno::last())
}
} else {
Ok(Some(raw))
@@ -1945,7 +1976,7 @@ pub fn pathconf<P: ?Sized + NixPath>(path: &P, var: PathconfVar) -> Result<Optio
/// Variable names for `sysconf`
///
/// Nix uses the same naming convention for these variables as the
-/// [getconf(1)](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/getconf.html) utility.
+/// [getconf(1)](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/getconf.html) utility.
/// That is, `SysconfVar` variables have the same name as the abstract variables
/// shown in the `sysconf(3)` man page. Usually, it's the same as the C
/// variable name without the leading `_SC_`.
@@ -1955,11 +1986,12 @@ pub fn pathconf<P: ?Sized + NixPath>(path: &P, var: PathconfVar) -> Result<Optio
///
/// # References
///
-/// - [sysconf(3)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/sysconf.html)
-/// - [unistd.h](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/unistd.h.html)
-/// - [limits.h](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/limits.h.html)
+/// - [sysconf(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sysconf.html)
+/// - [unistd.h](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/unistd.h.html)
+/// - [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.
@@ -2007,9 +2039,9 @@ pub enum SysconfVar {
/// the expr utility.
#[cfg(not(target_os = "redox"))]
EXPR_NEST_MAX = libc::_SC_EXPR_NEST_MAX,
- #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
- target_os="linux", target_os = "macos", target_os="netbsd",
- target_os="openbsd"))]
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="netbsd", target_os="openbsd", target_os = "solaris"))]
/// Maximum length of a host name (not including the terminating null) as
/// returned from the `gethostname` function
HOST_NAME_MAX = libc::_SC_HOST_NAME_MAX,
@@ -2046,29 +2078,30 @@ pub enum SysconfVar {
target_os="linux", target_os = "macos", target_os="openbsd"))]
/// The implementation supports the Advisory Information option.
_POSIX_ADVISORY_INFO = libc::_SC_ADVISORY_INFO,
- #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
- target_os="linux", target_os = "macos", target_os="netbsd",
- target_os="openbsd"))]
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="netbsd", target_os="openbsd", target_os = "solaris"))]
/// The implementation supports barriers.
_POSIX_BARRIERS = libc::_SC_BARRIERS,
/// The implementation supports asynchronous input and output.
#[cfg(not(target_os = "redox"))]
_POSIX_ASYNCHRONOUS_IO = libc::_SC_ASYNCHRONOUS_IO,
- #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
- target_os="linux", target_os = "macos", target_os="netbsd",
- target_os="openbsd"))]
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="netbsd", target_os="openbsd", target_os = "solaris"))]
/// The implementation supports clock selection.
_POSIX_CLOCK_SELECTION = libc::_SC_CLOCK_SELECTION,
- #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
- target_os="linux", target_os = "macos", target_os="netbsd",
- target_os="openbsd"))]
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="netbsd", target_os="openbsd", target_os = "solaris"))]
/// The implementation supports the Process CPU-Time Clocks option.
_POSIX_CPUTIME = libc::_SC_CPUTIME,
/// The implementation supports the File Synchronization option.
#[cfg(not(target_os = "redox"))]
_POSIX_FSYNC = libc::_SC_FSYNC,
- #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
- target_os="linux", target_os = "macos", target_os="openbsd"))]
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd", target_os = "solaris"))]
/// The implementation supports the IPv6 option.
_POSIX_IPV6 = libc::_SC_IPV6,
/// The implementation supports job control.
@@ -2093,20 +2126,21 @@ pub enum SysconfVar {
#[cfg(not(target_os = "redox"))]
_POSIX_MONOTONIC_CLOCK = libc::_SC_MONOTONIC_CLOCK,
#[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
- target_os = "ios", target_os="linux", target_os = "macos",
- target_os="openbsd"))]
+ target_os = "illumos", target_os = "ios", target_os="linux",
+ target_os = "macos", target_os="openbsd", target_os = "solaris"))]
/// The implementation supports the Prioritized Input and Output option.
_POSIX_PRIORITIZED_IO = libc::_SC_PRIORITIZED_IO,
/// The implementation supports the Process Scheduling option.
#[cfg(not(target_os = "redox"))]
_POSIX_PRIORITY_SCHEDULING = libc::_SC_PRIORITY_SCHEDULING,
- #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
- target_os="linux", target_os = "macos", target_os="openbsd"))]
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd", target_os = "solaris"))]
/// The implementation supports the Raw Sockets option.
_POSIX_RAW_SOCKETS = libc::_SC_RAW_SOCKETS,
- #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
- target_os="linux", target_os = "macos", target_os="netbsd",
- target_os="openbsd"))]
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="netbsd", target_os="openbsd", target_os = "solaris"))]
/// The implementation supports read-write locks.
_POSIX_READER_WRITER_LOCKS = libc::_SC_READER_WRITER_LOCKS,
#[cfg(any(target_os = "android", target_os="dragonfly", target_os="freebsd",
@@ -2114,9 +2148,9 @@ pub enum SysconfVar {
target_os = "openbsd"))]
/// The implementation supports realtime signals.
_POSIX_REALTIME_SIGNALS = libc::_SC_REALTIME_SIGNALS,
- #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
- target_os="linux", target_os = "macos", target_os="netbsd",
- target_os="openbsd"))]
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="netbsd", target_os="openbsd", target_os = "solaris"))]
/// The implementation supports the Regular Expression Handling option.
_POSIX_REGEXP = libc::_SC_REGEXP,
/// Each process has a saved set-user-ID and a saved set-group-ID.
@@ -2408,7 +2442,7 @@ pub enum SysconfVar {
}
/// Get configurable system variables (see
-/// [sysconf(3)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/sysconf.html))
+/// [sysconf(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sysconf.html))
///
/// Returns the value of a configurable system variable. Most supported
/// variables also have associated compile-time constants, but POSIX
@@ -2432,7 +2466,7 @@ pub fn sysconf(var: SysconfVar) -> Result<Option<c_long>> {
if errno::errno() == 0 {
Ok(None)
} else {
- Err(Error::Sys(Errno::last()))
+ Err(Errno::last())
}
} else {
Ok(Some(raw))
@@ -2466,7 +2500,7 @@ mod setres {
use super::{Uid, Gid};
/// Sets the real, effective, and saved uid.
- /// ([see setresuid(2)](http://man7.org/linux/man-pages/man2/setresuid.2.html))
+ /// ([see setresuid(2)](https://man7.org/linux/man-pages/man2/setresuid.2.html))
///
/// * `ruid`: real user id
/// * `euid`: effective user id
@@ -2482,7 +2516,7 @@ mod setres {
}
/// Sets the real, effective, and saved gid.
- /// ([see setresuid(2)](http://man7.org/linux/man-pages/man2/setresuid.2.html))
+ /// ([see setresuid(2)](https://man7.org/linux/man-pages/man2/setresuid.2.html))
///
/// * `rgid`: real group id
/// * `egid`: effective group id
@@ -2498,6 +2532,67 @@ mod setres {
}
}
+#[cfg(any(target_os = "android", target_os = "linux"))]
+mod getres {
+ use crate::Result;
+ use crate::errno::Errno;
+ use super::{Uid, Gid};
+
+ /// Real, effective and saved user IDs.
+ #[derive(Debug, Copy, Clone, Eq, PartialEq)]
+ pub struct ResUid {
+ pub real: Uid,
+ pub effective: Uid,
+ pub saved: Uid
+ }
+
+ /// Real, effective and saved group IDs.
+ #[derive(Debug, Copy, Clone, Eq, PartialEq)]
+ pub struct ResGid {
+ pub real: Gid,
+ pub effective: Gid,
+ pub saved: Gid
+ }
+
+ /// Gets the real, effective, and saved user IDs.
+ ///
+ /// ([see getresuid(2)](http://man7.org/linux/man-pages/man2/getresuid.2.html))
+ ///
+ /// #Returns
+ ///
+ /// - `Ok((Uid, Uid, Uid))`: tuple of real, effective and saved uids on success.
+ /// - `Err(x)`: libc error code on failure.
+ ///
+ #[inline]
+ pub fn getresuid() -> Result<ResUid> {
+ let mut ruid = libc::uid_t::max_value();
+ let mut euid = libc::uid_t::max_value();
+ let mut suid = libc::uid_t::max_value();
+ let res = unsafe { libc::getresuid(&mut ruid, &mut euid, &mut suid) };
+
+ Errno::result(res).map(|_| ResUid{ real: Uid(ruid), effective: Uid(euid), saved: Uid(suid) })
+ }
+
+ /// Gets the real, effective, and saved group IDs.
+ ///
+ /// ([see getresgid(2)](http://man7.org/linux/man-pages/man2/getresgid.2.html))
+ ///
+ /// #Returns
+ ///
+ /// - `Ok((Gid, Gid, Gid))`: tuple of real, effective and saved gids on success.
+ /// - `Err(x)`: libc error code on failure.
+ ///
+ #[inline]
+ pub fn getresgid() -> Result<ResGid> {
+ let mut rgid = libc::gid_t::max_value();
+ let mut egid = libc::gid_t::max_value();
+ let mut sgid = libc::gid_t::max_value();
+ let res = unsafe { libc::getresgid(&mut rgid, &mut egid, &mut sgid) };
+
+ Errno::result(res).map(|_| ResGid { real: Gid(rgid), effective: Gid(egid), saved: Gid(sgid) } )
+ }
+}
+
libc_bitflags!{
/// Options for access()
pub struct AccessFlags : c_int {
@@ -2513,7 +2608,7 @@ libc_bitflags!{
}
/// Checks the file named by `path` for accessibility according to the flags given by `amode`
-/// See [access(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/access.html)
+/// See [access(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/access.html)
pub fn access<P: ?Sized + NixPath>(path: &P, amode: AccessFlags) -> Result<()> {
let res = path.with_nix_path(|cstr| {
unsafe {
@@ -2541,23 +2636,32 @@ pub struct User {
/// 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,
/// Path to shell
pub shell: PathBuf,
/// Login class
- #[cfg(not(any(target_os = "android", target_os = "fuchsia",
- target_os = "linux")))]
+ #[cfg(not(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris")))]
pub class: CString,
/// Last password change
- #[cfg(not(any(target_os = "android", target_os = "fuchsia",
- target_os = "linux")))]
+ #[cfg(not(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris")))]
pub change: libc::time_t,
/// Expiration time of account
- #[cfg(not(any(target_os = "android", target_os = "fuchsia",
- target_os = "linux")))]
+ #[cfg(not(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris")))]
pub expire: libc::time_t
}
@@ -2568,20 +2672,29 @@ impl From<&libc::passwd> for User {
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())),
uid: Uid::from_raw((*pw).pw_uid),
gid: Gid::from_raw((*pw).pw_gid),
- #[cfg(not(any(target_os = "android", target_os = "fuchsia",
- target_os = "linux")))]
+ #[cfg(not(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris")))]
class: CString::new(CStr::from_ptr((*pw).pw_class).to_bytes()).unwrap(),
- #[cfg(not(any(target_os = "android", target_os = "fuchsia",
- target_os = "linux")))]
+ #[cfg(not(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris")))]
change: (*pw).pw_change,
- #[cfg(not(any(target_os = "android", target_os = "fuchsia",
- target_os = "linux")))]
+ #[cfg(not(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris")))]
expire: (*pw).pw_expire
}
}
@@ -2589,6 +2702,58 @@ impl From<&libc::passwd> for User {
}
#[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
@@ -2597,10 +2762,10 @@ impl User {
libc::size_t,
*mut *mut libc::passwd) -> libc::c_int
{
- let buflimit = 16384;
+ let buflimit = 1048576;
let bufsize = match sysconf(SysconfVar::GETPW_R_SIZE_MAX) {
Ok(Some(n)) => n as usize,
- Ok(None) | Err(_) => buflimit as usize,
+ Ok(None) | Err(_) => 16384,
};
let mut cbuf = Vec::with_capacity(bufsize);
@@ -2620,7 +2785,7 @@ impl User {
// Trigger the internal buffer resizing logic.
reserve_double_buffer_size(&mut cbuf, buflimit)?;
} else {
- return Err(Error::Sys(Errno::last()));
+ return Err(Errno::last());
}
}
}
@@ -2628,7 +2793,7 @@ impl User {
/// Get a user by UID.
///
/// Internally, this function calls
- /// [getpwuid_r(3)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwuid_r.html)
+ /// [getpwuid_r(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwuid_r.html)
///
/// # Examples
///
@@ -2647,7 +2812,7 @@ impl User {
/// Get a user by name.
///
/// Internally, this function calls
- /// [getpwnam_r(3)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwuid_r.html)
+ /// [getpwnam_r(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwuid_r.html)
///
/// # Examples
///
@@ -2718,10 +2883,10 @@ impl Group {
libc::size_t,
*mut *mut libc::group) -> libc::c_int
{
- let buflimit = 16384;
+ let buflimit = 1048576;
let bufsize = match sysconf(SysconfVar::GETGR_R_SIZE_MAX) {
Ok(Some(n)) => n as usize,
- Ok(None) | Err(_) => buflimit as usize,
+ Ok(None) | Err(_) => 16384,
};
let mut cbuf = Vec::with_capacity(bufsize);
@@ -2741,7 +2906,7 @@ impl Group {
// Trigger the internal buffer resizing logic.
reserve_double_buffer_size(&mut cbuf, buflimit)?;
} else {
- return Err(Error::Sys(Errno::last()));
+ return Err(Errno::last());
}
}
}
@@ -2749,7 +2914,7 @@ impl Group {
/// Get a group by GID.
///
/// Internally, this function calls
- /// [getgrgid_r(3)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwuid_r.html)
+ /// [getgrgid_r(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwuid_r.html)
///
/// # Examples
///
@@ -2770,7 +2935,7 @@ impl Group {
/// Get a group by name.
///
/// Internally, this function calls
- /// [getgrnam_r(3)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwuid_r.html)
+ /// [getgrnam_r(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwuid_r.html)
///
/// # Examples
///
@@ -2791,7 +2956,7 @@ impl Group {
}
/// Get the name of the terminal device that is open on file descriptor fd
-/// (see [`ttyname(3)`](http://man7.org/linux/man-pages/man3/ttyname.3.html)).
+/// (see [`ttyname(3)`](https://man7.org/linux/man-pages/man3/ttyname.3.html)).
#[cfg(not(target_os = "fuchsia"))]
pub fn ttyname(fd: RawFd) -> Result<PathBuf> {
const PATH_MAX: usize = libc::PATH_MAX as usize;
@@ -2800,7 +2965,7 @@ pub fn ttyname(fd: RawFd) -> Result<PathBuf> {
let ret = unsafe { libc::ttyname_r(fd, c_buf, buf.len()) };
if ret != 0 {
- return Err(Error::Sys(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 a871b47..84a0b4f 100644
--- a/test/common/mod.rs
+++ b/test/common/mod.rs
@@ -14,19 +14,33 @@ use cfg_if::cfg_if;
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) => {}
+ }
+ }
+}
+
+/// Skip the test if we don't have the ability to mount file systems.
+#[cfg(target_os = "freebsd")]
+#[macro_export] macro_rules! require_mount {
+ ($name:expr) => {
+ use ::sysctl::CtlValue;
+ use nix::unistd::Uid;
+
+ if !Uid::current().is_root() && CtlValue::Int(0) == ::sysctl::value("vfs.usermount").unwrap()
+ {
+ skip!("{} requires the ability to mount file systems. Skipping test.", $name);
}
}
}
@@ -114,8 +128,8 @@ cfg_if! {
let mut version = Version::parse(fixed_release).unwrap();
//Keep only numeric parts
- version.pre.clear();
- version.build.clear();
+ version.pre = semver::Prerelease::EMPTY;
+ version.build = semver::BuildMetadata::EMPTY;
if !version_requirement.matches(&version) {
skip!("Skip {} because kernel version `{}` doesn't match the requirement `{}`",
diff --git a/test/sys/mod.rs b/test/sys/mod.rs
index 14b0378..e73d9b1 100644
--- a/test/sys/mod.rs
+++ b/test/sys/mod.rs
@@ -11,6 +11,8 @@ mod test_signal;
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"))]
diff --git a/test/sys/test_aio.rs b/test/sys/test_aio.rs
index 3878da9..80cd053 100644
--- a/test/sys/test_aio.rs
+++ b/test/sys/test_aio.rs
@@ -1,6 +1,5 @@
-use bytes::{Bytes, BytesMut};
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};
@@ -8,15 +7,26 @@ use nix::sys::time::{TimeSpec, TimeValLike};
use std::io::{Write, Read, Seek, SeekFrom};
use std::ops::Deref;
use std::os::unix::io::AsRawFd;
+use std::pin::Pin;
use std::sync::atomic::{AtomicBool, Ordering};
use std::{thread, time};
use tempfile::tempfile;
// Helper that polls an AioCb for completion or error
-fn poll_aio(aiocb: &mut AioCb) -> Result<()> {
+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));
+ }
+}
+
+// Helper that polls a component of an LioCb for completion or error
+#[cfg(not(any(target_os = "ios", target_os = "macos")))]
+fn poll_lio(liocb: &mut LioCb, i: usize) -> Result<()> {
+ loop {
+ let err = liocb.error(i);
+ if err != Err(Errno::EINPROGRESS) { return err; };
thread::sleep(time::Duration::from_millis(10));
}
}
@@ -60,7 +70,7 @@ fn test_cancel() {
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());
@@ -85,7 +95,7 @@ fn test_aio_cancel_all() {
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());
@@ -132,9 +142,7 @@ fn test_fsync_error() {
}
#[test]
-#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
-// On Travis, aio_suspend hits an assertion within glibc. This is either a bug
-// in Travis's version of glibc or Linux. Either way, we must skip the test.
+// On Cirrus on Linux, this test fails due to a glibc bug.
// https://github.com/nix-rust/nix/issues/1099
#[cfg_attr(target_os = "linux", ignore)]
// On Cirrus, aio_suspend is failing with EINVAL
@@ -166,16 +174,16 @@ fn test_aio_suspend() {
rcb.read().unwrap();
loop {
{
- let cbbuf = [&wcb, &rcb];
+ let cbbuf = [wcb.as_ref(), rcb.as_ref()];
let r = aio_suspend(&cbbuf[..], Some(timeout));
match r {
- Err(Error::Sys(Errno::EINTR)) => continue,
+ Err(Errno::EINTR) => continue,
Err(e) => panic!("aio_suspend returned {:?}", e),
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
}
}
@@ -335,61 +343,6 @@ fn test_write() {
assert_eq!(rbuf, EXPECT);
}
-// Tests `AioCb::from_boxed_slice` with `Bytes`
-#[test]
-#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
-fn test_write_bytes() {
- const INITIAL: &[u8] = b"abcdef123456";
- let wbuf = Box::new(Bytes::from(&b"CDEF"[..]));
- let mut rbuf = Vec::new();
- const EXPECT: &[u8] = b"abCDEF123456";
- let expected_len = wbuf.len();
-
- let mut f = tempfile().unwrap();
- f.write_all(INITIAL).unwrap();
- let mut aiocb = AioCb::from_boxed_slice( f.as_raw_fd(),
- 2, //offset
- wbuf,
- 0, //priority
- SigevNotify::SigevNone,
- LioOpcode::LIO_NOP);
- aiocb.write().unwrap();
-
- let err = poll_aio(&mut aiocb);
- assert_eq!(err, Ok(()));
- assert_eq!(aiocb.aio_return().unwrap() as usize, expected_len);
-
- f.seek(SeekFrom::Start(0)).unwrap();
- let len = f.read_to_end(&mut rbuf).unwrap();
- assert_eq!(len, EXPECT.len());
- assert_eq!(rbuf, EXPECT);
-}
-
-// Tests `AioCb::from_boxed_mut_slice` with `BytesMut`
-#[test]
-#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
-fn test_read_bytes_mut_small() {
- const INITIAL: &[u8] = b"abcdef";
- let rbuf = Box::new(BytesMut::from(vec![0; 4]));
- const EXPECT: &[u8] = b"cdef";
- let mut f = tempfile().unwrap();
- f.write_all(INITIAL).unwrap();
-
- let mut aiocb = AioCb::from_boxed_mut_slice( f.as_raw_fd(),
- 2, //offset
- rbuf,
- 0, //priority
- SigevNotify::SigevNone,
- LioOpcode::LIO_NOP);
- aiocb.read().unwrap();
-
- let err = poll_aio(&mut aiocb);
- assert_eq!(err, Ok(()));
- assert_eq!(aiocb.aio_return().unwrap() as usize, EXPECT.len());
- let buffer = aiocb.boxed_mut_slice().unwrap();
- assert_eq!(buffer.borrow(), EXPECT);
-}
-
// Tests `AioCb::from_ptr`
#[test]
#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
@@ -453,7 +406,7 @@ extern fn sigfunc(_: c_int) {
#[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());
@@ -505,22 +458,22 @@ fn test_liocb_listio_wait() {
f.write_all(INITIAL).unwrap();
{
- let wcb = AioCb::from_slice( f.as_raw_fd(),
- 2, //offset
- WBUF,
- 0, //priority
- SigevNotify::SigevNone,
- LioOpcode::LIO_WRITE);
-
- let rcb = AioCb::from_mut_slice( f.as_raw_fd(),
- 8, //offset
- &mut rbuf,
- 0, //priority
- SigevNotify::SigevNone,
- LioOpcode::LIO_READ);
- let mut liocb = LioCb::with_capacity(2);
- liocb.aiocbs.push(wcb);
- liocb.aiocbs.push(rcb);
+ let mut liocb = LioCbBuilder::with_capacity(2)
+ .emplace_slice(
+ f.as_raw_fd(),
+ 2, //offset
+ WBUF,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_WRITE
+ ).emplace_mut_slice(
+ f.as_raw_fd(),
+ 8, //offset
+ &mut rbuf,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_READ
+ ).finish();
let err = liocb.listio(LioMode::LIO_WAIT, SigevNotify::SigevNone);
err.expect("lio_listio");
@@ -552,29 +505,29 @@ fn test_liocb_listio_nowait() {
f.write_all(INITIAL).unwrap();
{
- let wcb = AioCb::from_slice( f.as_raw_fd(),
- 2, //offset
- WBUF,
- 0, //priority
- SigevNotify::SigevNone,
- LioOpcode::LIO_WRITE);
-
- let rcb = AioCb::from_mut_slice( f.as_raw_fd(),
- 8, //offset
- &mut rbuf,
- 0, //priority
- SigevNotify::SigevNone,
- LioOpcode::LIO_READ);
- let mut liocb = LioCb::with_capacity(2);
- liocb.aiocbs.push(wcb);
- liocb.aiocbs.push(rcb);
+ let mut liocb = LioCbBuilder::with_capacity(2)
+ .emplace_slice(
+ f.as_raw_fd(),
+ 2, //offset
+ WBUF,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_WRITE
+ ).emplace_mut_slice(
+ f.as_raw_fd(),
+ 8, //offset
+ &mut rbuf,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_READ
+ ).finish();
let err = liocb.listio(LioMode::LIO_NOWAIT, SigevNotify::SigevNone);
err.expect("lio_listio");
- poll_aio(&mut liocb.aiocbs[0]).unwrap();
- poll_aio(&mut liocb.aiocbs[1]).unwrap();
- assert_eq!(liocb.aiocbs[0].aio_return().unwrap() as usize, WBUF.len());
- assert_eq!(liocb.aiocbs[1].aio_return().unwrap() as usize, rlen);
+ poll_lio(&mut liocb, 0).unwrap();
+ poll_lio(&mut liocb, 1).unwrap();
+ assert_eq!(liocb.aio_return(0).unwrap() as usize, WBUF.len());
+ assert_eq!(liocb.aio_return(1).unwrap() as usize, rlen);
}
assert_eq!(rbuf.deref().deref(), b"3456");
@@ -591,7 +544,7 @@ fn test_liocb_listio_nowait() {
#[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];
@@ -608,22 +561,22 @@ fn test_liocb_listio_signal() {
f.write_all(INITIAL).unwrap();
{
- let wcb = AioCb::from_slice( f.as_raw_fd(),
- 2, //offset
- WBUF,
- 0, //priority
- SigevNotify::SigevNone,
- LioOpcode::LIO_WRITE);
-
- let rcb = AioCb::from_mut_slice( f.as_raw_fd(),
- 8, //offset
- &mut rbuf,
- 0, //priority
- SigevNotify::SigevNone,
- LioOpcode::LIO_READ);
- let mut liocb = LioCb::with_capacity(2);
- liocb.aiocbs.push(wcb);
- liocb.aiocbs.push(rcb);
+ let mut liocb = LioCbBuilder::with_capacity(2)
+ .emplace_slice(
+ f.as_raw_fd(),
+ 2, //offset
+ WBUF,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_WRITE
+ ).emplace_mut_slice(
+ f.as_raw_fd(),
+ 8, //offset
+ &mut rbuf,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_READ
+ ).finish();
SIGNALED.store(false, Ordering::Relaxed);
unsafe { sigaction(Signal::SIGUSR2, &sa) }.unwrap();
let err = liocb.listio(LioMode::LIO_NOWAIT, sigev_notify);
@@ -632,8 +585,8 @@ fn test_liocb_listio_signal() {
thread::sleep(time::Duration::from_millis(10));
}
- assert_eq!(liocb.aiocbs[0].aio_return().unwrap() as usize, WBUF.len());
- assert_eq!(liocb.aiocbs[1].aio_return().unwrap() as usize, rlen);
+ assert_eq!(liocb.aio_return(0).unwrap() as usize, WBUF.len());
+ assert_eq!(liocb.aio_return(1).unwrap() as usize, rlen);
}
assert_eq!(rbuf.deref().deref(), b"3456");
@@ -654,13 +607,14 @@ fn test_liocb_listio_read_immutable() {
let f = tempfile().unwrap();
- let mut liocb = LioCb::from(vec![
- AioCb::from_slice( f.as_raw_fd(),
+ let mut liocb = LioCbBuilder::with_capacity(1)
+ .emplace_slice(
+ f.as_raw_fd(),
2, //offset
rbuf,
0, //priority
SigevNotify::SigevNone,
- LioOpcode::LIO_READ)
- ]);
+ LioOpcode::LIO_READ
+ ).finish();
let _ = liocb.listio(LioMode::LIO_NOWAIT, SigevNotify::SigevNone);
}
diff --git a/test/sys/test_aio_drop.rs b/test/sys/test_aio_drop.rs
index 784ee3e..71a2183 100644
--- a/test/sys/test_aio_drop.rs
+++ b/test/sys/test_aio_drop.rs
@@ -9,7 +9,6 @@
target_os = "macos",
target_os = "freebsd",
target_os = "netbsd")))]
-#[cfg_attr(target_env = "gnu", ignore = "Occasionally fails in Travis; glibc bug suspected")]
fn test_drop() {
use nix::sys::aio::*;
use nix::sys::signal::*;
diff --git a/test/sys/test_epoll.rs b/test/sys/test_epoll.rs
index e0dc513..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 @@ pub fn test_epoll_errno() {
let efd = epoll_create1(EpollCreateFlags::empty()).unwrap();
let result = epoll_ctl(efd, EpollOp::EpollCtlDel, 1, None);
assert!(result.is_err());
- assert_eq!(result.unwrap_err(), Error::Sys(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::Sys(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 a8ead46..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 @@ pub fn test_inotify() {
instance.add_watch(tempdir.path(), AddWatchFlags::IN_ALL_EVENTS).unwrap();
let events = instance.read_events();
- assert_eq!(events.unwrap_err(), Error::Sys(Errno::EAGAIN));
+ assert_eq!(events.unwrap_err(), Errno::EAGAIN);
File::create(tempdir.path().join("test")).unwrap();
@@ -31,7 +29,7 @@ pub fn test_inotify_multi_events() {
instance.add_watch(tempdir.path(), AddWatchFlags::IN_ALL_EVENTS).unwrap();
let events = instance.read_events();
- assert_eq!(events.unwrap_err(), Error::Sys(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_ioctl.rs b/test/sys/test_ioctl.rs
index fa4510a..236d242 100644
--- a/test/sys/test_ioctl.rs
+++ b/test/sys/test_ioctl.rs
@@ -56,10 +56,10 @@ mod linux {
#[test]
fn test_op_write_64() {
if cfg!(any(target_arch = "mips64", target_arch="powerpc64")){
- assert_eq!(request_code_write!(b'z', 10, (1 as u64) << 32) as u32,
+ assert_eq!(request_code_write!(b'z', 10, 1u64 << 32) as u32,
0x8000_7A0A);
} else {
- assert_eq!(request_code_write!(b'z', 10, (1 as u64) << 32) as u32,
+ assert_eq!(request_code_write!(b'z', 10, 1u64 << 32) as u32,
0x4000_7A0A);
}
@@ -80,10 +80,10 @@ mod linux {
#[test]
fn test_op_read_64() {
if cfg!(any(target_arch = "mips64", target_arch="powerpc64")){
- assert_eq!(request_code_read!(b'z', 10, (1 as u64) << 32) as u32,
+ assert_eq!(request_code_read!(b'z', 10, 1u64 << 32) as u32,
0x4000_7A0A);
} else {
- assert_eq!(request_code_read!(b'z', 10, (1 as u64) << 32) as u32,
+ assert_eq!(request_code_read!(b'z', 10, 1u64 << 32) as u32,
0x8000_7A0A);
}
}
@@ -97,7 +97,7 @@ mod linux {
#[cfg(target_pointer_width = "64")]
#[test]
fn test_op_read_write_64() {
- assert_eq!(request_code_readwrite!(b'z', 10, (1 as u64) << 32) as u32,
+ assert_eq!(request_code_readwrite!(b'z', 10, 1u64 << 32) as u32,
0xC000_7A0A);
}
}
@@ -131,7 +131,7 @@ mod bsd {
#[cfg(target_pointer_width = "64")]
#[test]
fn test_op_write_64() {
- assert_eq!(request_code_write!(b'z', 10, (1 as u64) << 32), 0x8000_7A0A);
+ assert_eq!(request_code_write!(b'z', 10, 1u64 << 32), 0x8000_7A0A);
}
#[test]
@@ -143,7 +143,7 @@ mod bsd {
#[cfg(target_pointer_width = "64")]
#[test]
fn test_op_read_64() {
- assert_eq!(request_code_read!(b'z', 10, (1 as u64) << 32), 0x4000_7A0A);
+ assert_eq!(request_code_read!(b'z', 10, 1u64 << 32), 0x4000_7A0A);
}
#[test]
@@ -155,7 +155,7 @@ mod bsd {
#[cfg(target_pointer_width = "64")]
#[test]
fn test_op_read_write_64() {
- assert_eq!(request_code_readwrite!(b'z', 10, (1 as u64) << 32), 0xC000_7A0A);
+ assert_eq!(request_code_readwrite!(b'z', 10, 1u64 << 32), 0xC000_7A0A);
}
}
@@ -167,15 +167,14 @@ mod linux_ioctls {
use tempfile::tempfile;
use libc::{TCGETS, TCSBRK, TCSETS, TIOCNXCL, termios};
- use nix::Error::Sys;
- use nix::errno::Errno::{ENOTTY, ENOSYS};
+ use nix::errno::Errno;
ioctl_none_bad!(tiocnxcl, TIOCNXCL);
#[test]
fn test_ioctl_none_bad() {
let file = tempfile().unwrap();
let res = unsafe { tiocnxcl(file.as_raw_fd()) };
- assert_eq!(res, Err(Sys(ENOTTY)));
+ assert_eq!(res, Err(Errno::ENOTTY));
}
ioctl_read_bad!(tcgets, TCGETS, termios);
@@ -184,7 +183,7 @@ mod linux_ioctls {
let file = tempfile().unwrap();
let mut termios = unsafe { mem::zeroed() };
let res = unsafe { tcgets(file.as_raw_fd(), &mut termios) };
- assert_eq!(res, Err(Sys(ENOTTY)));
+ assert_eq!(res, Err(Errno::ENOTTY));
}
ioctl_write_int_bad!(tcsbrk, TCSBRK);
@@ -192,7 +191,7 @@ mod linux_ioctls {
fn test_ioctl_write_int_bad() {
let file = tempfile().unwrap();
let res = unsafe { tcsbrk(file.as_raw_fd(), 0) };
- assert_eq!(res, Err(Sys(ENOTTY)));
+ assert_eq!(res, Err(Errno::ENOTTY));
}
ioctl_write_ptr_bad!(tcsets, TCSETS, termios);
@@ -201,7 +200,7 @@ mod linux_ioctls {
let file = tempfile().unwrap();
let termios: termios = unsafe { mem::zeroed() };
let res = unsafe { tcsets(file.as_raw_fd(), &termios) };
- assert_eq!(res, Err(Sys(ENOTTY)));
+ assert_eq!(res, Err(Errno::ENOTTY));
}
// FIXME: Find a suitable example for `ioctl_readwrite_bad`
@@ -212,7 +211,7 @@ mod linux_ioctls {
fn test_ioctl_none() {
let file = tempfile().unwrap();
let res = unsafe { log_status(file.as_raw_fd()) };
- assert!(res == Err(Sys(ENOTTY)) || res == Err(Sys(ENOSYS)));
+ assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS));
}
#[repr(C)]
@@ -231,7 +230,7 @@ mod linux_ioctls {
let file = tempfile().unwrap();
let data: v4l2_audio = unsafe { mem::zeroed() };
let res = unsafe { s_audio(file.as_raw_fd(), &data) };
- assert!(res == Err(Sys(ENOTTY)) || res == Err(Sys(ENOSYS)));
+ assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS));
}
// From linux/net/bluetooth/hci_sock.h
@@ -242,7 +241,7 @@ mod linux_ioctls {
fn test_ioctl_write_int() {
let file = tempfile().unwrap();
let res = unsafe { hcidevup(file.as_raw_fd(), 0) };
- assert!(res == Err(Sys(ENOTTY)) || res == Err(Sys(ENOSYS)));
+ assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS));
}
// From linux/videodev2.h
@@ -252,7 +251,7 @@ mod linux_ioctls {
let file = tempfile().unwrap();
let mut data: v4l2_audio = unsafe { mem::zeroed() };
let res = unsafe { g_audio(file.as_raw_fd(), &mut data) };
- assert!(res == Err(Sys(ENOTTY)) || res == Err(Sys(ENOSYS)));
+ assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS));
}
// From linux/videodev2.h
@@ -262,7 +261,7 @@ mod linux_ioctls {
let file = tempfile().unwrap();
let mut data: v4l2_audio = unsafe { mem::zeroed() };
let res = unsafe { enum_audio(file.as_raw_fd(), &mut data) };
- assert!(res == Err(Sys(ENOTTY)) || res == Err(Sys(ENOSYS)));
+ assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS));
}
// FIXME: Find a suitable example for `ioctl_read_buf`.
@@ -288,7 +287,7 @@ mod linux_ioctls {
let file = tempfile().unwrap();
let data: [spi_ioc_transfer; 4] = unsafe { mem::zeroed() };
let res = unsafe { spi_ioc_message(file.as_raw_fd(), &data[..]) };
- assert!(res == Err(Sys(ENOTTY)) || res == Err(Sys(ENOSYS)));
+ assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS));
}
// FIXME: Find a suitable example for `ioctl_readwrite_buf`.
@@ -302,8 +301,7 @@ mod freebsd_ioctls {
use tempfile::tempfile;
use libc::termios;
- use nix::Error::Sys;
- use nix::errno::Errno::ENOTTY;
+ use nix::errno::Errno;
// From sys/sys/ttycom.h
const TTY_IOC_MAGIC: u8 = b't';
@@ -316,7 +314,7 @@ mod freebsd_ioctls {
fn test_ioctl_none() {
let file = tempfile().unwrap();
let res = unsafe { tiocnxcl(file.as_raw_fd()) };
- assert_eq!(res, Err(Sys(ENOTTY)));
+ assert_eq!(res, Err(Errno::ENOTTY));
}
ioctl_read!(tiocgeta, TTY_IOC_MAGIC, TTY_IOC_TYPE_GETA, termios);
@@ -325,7 +323,7 @@ mod freebsd_ioctls {
let file = tempfile().unwrap();
let mut termios = unsafe { mem::zeroed() };
let res = unsafe { tiocgeta(file.as_raw_fd(), &mut termios) };
- assert_eq!(res, Err(Sys(ENOTTY)));
+ assert_eq!(res, Err(Errno::ENOTTY));
}
ioctl_write_ptr!(tiocseta, TTY_IOC_MAGIC, TTY_IOC_TYPE_SETA, termios);
@@ -334,6 +332,6 @@ mod freebsd_ioctls {
let file = tempfile().unwrap();
let termios: termios = unsafe { mem::zeroed() };
let res = unsafe { tiocseta(file.as_raw_fd(), &termios) };
- assert_eq!(res, Err(Sys(ENOTTY)));
+ assert_eq!(res, Err(Errno::ENOTTY));
}
}
diff --git a/test/sys/test_lio_listio_resubmit.rs b/test/sys/test_lio_listio_resubmit.rs
index 0795370..c907789 100644
--- a/test/sys/test_lio_listio_resubmit.rs
+++ b/test/sys/test_lio_listio_resubmit.rs
@@ -4,7 +4,6 @@
// we must disable the test here rather than in Cargo.toml
#![cfg(target_os = "freebsd")]
-use nix::Error;
use nix::errno::*;
use nix::libc::off_t;
use nix::sys::aio::*;
@@ -20,12 +19,12 @@ const BYTES_PER_OP: usize = 512;
/// Attempt to collect final status for all of `liocb`'s operations, freeing
/// system resources
fn finish_liocb(liocb: &mut LioCb) {
- for j in 0..liocb.aiocbs.len() {
+ for j in 0..liocb.len() {
loop {
let e = liocb.error(j);
match e {
Ok(()) => break,
- Err(Error::Sys(Errno::EINPROGRESS)) =>
+ Err(Errno::EINPROGRESS) =>
thread::sleep(time::Duration::from_millis(10)),
Err(x) => panic!("aio_error({:?})", x)
}
@@ -70,21 +69,21 @@ fn test_lio_listio_resubmit() {
}).collect::<Vec<_>>();
let mut liocbs = (0..num_listios).map(|i| {
- let mut liocb = LioCb::with_capacity(ops_per_listio);
+ let mut builder = LioCbBuilder::with_capacity(ops_per_listio);
for j in 0..ops_per_listio {
let offset = (BYTES_PER_OP * (i * ops_per_listio + j)) as off_t;
- let wcb = AioCb::from_slice( f.as_raw_fd(),
- offset,
- &buffer_set[i][j][..],
- 0, //priority
- SigevNotify::SigevNone,
- LioOpcode::LIO_WRITE);
- liocb.aiocbs.push(wcb);
+ builder = builder.emplace_slice(f.as_raw_fd(),
+ offset,
+ &buffer_set[i][j][..],
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_WRITE);
}
+ let mut liocb = builder.finish();
let mut err = liocb.listio(LioMode::LIO_NOWAIT, SigevNotify::SigevNone);
- while err == Err(Error::Sys(Errno::EIO)) ||
- err == Err(Error::Sys(Errno::EAGAIN)) ||
- err == Err(Error::Sys(Errno::EINTR)) {
+ while err == Err(Errno::EIO) ||
+ err == Err(Errno::EAGAIN) ||
+ err == Err(Errno::EINTR) {
//
thread::sleep(time::Duration::from_millis(10));
resubmit_count += 1;
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 @@ fn test_mremap_grow() {
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 @@ fn test_mremap_grow() {
}
#[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 @@ fn test_mremap_shrink() {
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 @@ fn test_pthread_self() {
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 b9793b3..83fff9a 100644
--- a/test/sys/test_ptrace.rs
+++ b/test/sys/test_ptrace.rs
@@ -1,4 +1,3 @@
-use nix::Error;
use nix::errno::Errno;
use nix::unistd::getpid;
use nix::sys::ptrace;
@@ -14,37 +13,37 @@ use crate::*;
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 == Error::Sys(Errno::EPERM) || err == Error::Sys(Errno::EINVAL) ||
- err == Error::Sys(Errno::ENOSYS));
+ assert!(err == Errno::EPERM || err == Errno::EINVAL ||
+ err == Errno::ENOSYS);
}
// Just make sure ptrace_setoptions can be called at all, for now.
#[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 != Error::UnsupportedOperation);
+ assert!(err != Errno::EOPNOTSUPP);
}
// Just make sure ptrace_getevent can be called at all, for now.
#[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 != Error::UnsupportedOperation);
+ assert!(err != Errno::EOPNOTSUPP);
}
// Just make sure ptrace_getsiginfo can be called at all, for now.
#[test]
#[cfg(any(target_os = "android", target_os = "linux"))]
fn test_ptrace_getsiginfo() {
- require_capability!(CAP_SYS_PTRACE);
- if let Err(Error::UnsupportedOperation) = ptrace::getsiginfo(getpid()) {
- panic!("ptrace_getsiginfo returns Error::UnsupportedOperation!");
+ require_capability!("test_ptrace_getsiginfo", CAP_SYS_PTRACE);
+ if let Err(Errno::EOPNOTSUPP) = ptrace::getsiginfo(getpid()) {
+ panic!("ptrace_getsiginfo returns Errno::EOPNOTSUPP!");
}
}
@@ -52,10 +51,10 @@ fn test_ptrace_getsiginfo() {
#[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(Error::UnsupportedOperation) = ptrace::setsiginfo(getpid(), &siginfo) {
- panic!("ptrace_setsiginfo returns Error::UnsupportedOperation!");
+ if let Err(Errno::EOPNOTSUPP) = ptrace::setsiginfo(getpid(), &siginfo) {
+ panic!("ptrace_setsiginfo returns Errno::EOPNOTSUPP!");
}
}
@@ -68,9 +67,9 @@ fn test_ptrace_cont() {
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.
@@ -79,7 +78,7 @@ fn test_ptrace_cont() {
// On valid platforms the ptrace call should return Errno::EPERM, this
// is already tested by `test_ptrace`.
let err = ptrace::attach(getpid()).unwrap_err();
- if err == Error::Sys(Errno::ENOSYS) {
+ if err == Errno::ENOSYS {
return;
}
@@ -115,6 +114,48 @@ fn test_ptrace_cont() {
}
}
+#[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",
@@ -130,9 +171,9 @@ fn test_ptrace_syscall() {
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 @@ use nix::sys::time::{TimeSpec, TimeValLike};
#[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 @@ pub fn test_pselect_nfds2() {
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 ae22527..fdd2568 100644
--- a/test/sys/test_signal.rs
+++ b/test/sys/test_signal.rs
@@ -1,6 +1,5 @@
-use libc;
#[cfg(not(target_os = "redox"))]
-use nix::Error;
+use nix::errno::Errno;
use nix::sys::signal::*;
use nix::unistd::*;
use std::convert::TryFrom;
@@ -20,7 +19,7 @@ fn test_killpg_none() {
#[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(
@@ -42,7 +41,7 @@ fn test_sigprocmask_noop() {
#[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;
@@ -53,9 +52,9 @@ fn test_sigprocmask() {
// Make sure the old set doesn't contain the signal, otherwise the following
// test don't make sense.
- assert_eq!(old_signal_set.contains(SIGNAL), false,
- "the {:?} signal is already blocked, please change to a \
- different one", SIGNAL);
+ assert!(!old_signal_set.contains(SIGNAL),
+ "the {:?} signal is already blocked, please change to a \
+ different one", SIGNAL);
// Now block the signal.
let mut signal_set = SigSet::empty();
@@ -67,8 +66,8 @@ fn test_sigprocmask() {
old_signal_set.clear();
sigprocmask(SigmaskHow::SIG_BLOCK, None, Some(&mut old_signal_set))
.expect("expect to be able to retrieve old signals");
- assert_eq!(old_signal_set.contains(SIGNAL), true,
- "expected the {:?} to be blocked", SIGNAL);
+ assert!(old_signal_set.contains(SIGNAL),
+ "expected the {:?} to be blocked", SIGNAL);
// Reset the signal.
sigprocmask(SigmaskHow::SIG_UNBLOCK, Some(&signal_set), None)
@@ -90,15 +89,15 @@ extern fn test_sigaction_action(_: libc::c_int, _: *mut libc::siginfo_t, _: *mut
#[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(), Error::UnsupportedOperation);
+ assert_eq!(unsafe { signal(Signal::SIGINT, action_handler) }.unwrap_err(), Errno::ENOTSUP);
}
#[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();
@@ -108,8 +107,15 @@ fn test_signal() {
assert_eq!(unsafe { signal(Signal::SIGINT, handler) }.unwrap(), SigHandler::SigDfl);
raise(Signal::SIGINT).unwrap();
assert!(SIGNALED.load(Ordering::Relaxed));
+
+ #[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
assert_eq!(unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(), handler);
+ // System V based OSes (e.g. illumos and Solaris) always resets the
+ // disposition to SIG_DFL prior to calling the signal handler
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ assert_eq!(unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(), SigHandler::SigDfl);
+
// Restore default signal handler
unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap();
}
diff --git a/test/sys/test_signalfd.rs b/test/sys/test_signalfd.rs
index af04c22..b6f748b 100644
--- a/test/sys/test_signalfd.rs
+++ b/test/sys/test_signalfd.rs
@@ -6,7 +6,7 @@ fn test_signalfd() {
use nix::sys::signal::{self, raise, Signal, SigSet};
// Grab the mutex for altering signals so we don't interfere with other tests.
- let _m = crate::SIGNAL_MTX.lock().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 7eab28c..0f6fac6 100644
--- a/test/sys/test_socket.rs
+++ b/test/sys/test_socket.rs
@@ -1,13 +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 tempfile;
+use libc::{c_char, sockaddr_storage};
#[cfg(any(target_os = "linux", target_os= "android"))]
use crate::*;
@@ -28,13 +28,36 @@ pub fn test_inetv4_addr_to_sock_addr() {
_ => 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;
@@ -55,6 +78,33 @@ pub fn test_inetv6_addr_to_sock_addr() {
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() {
@@ -63,9 +113,9 @@ pub fn test_path_to_sock_addr() {
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));
}
@@ -81,9 +131,9 @@ pub fn test_addr_equality_path() {
let path = "/foo/bar";
let actual = Path::new(path);
let addr1 = UnixAddr::new(actual).unwrap();
- let mut addr2 = addr1.clone();
+ 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));
@@ -102,12 +152,12 @@ pub fn test_abstract_sun_path_too_long() {
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));
}
@@ -130,7 +180,7 @@ pub fn test_abstract_uds_addr() {
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]
@@ -144,8 +194,7 @@ pub fn test_getsockname() {
.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]
@@ -168,7 +217,7 @@ mod recvfrom {
use std::thread;
use super::*;
- const MSG: &'static [u8] = b"Hello, World!";
+ const MSG: &[u8] = b"Hello, World!";
fn sendrecv<Fs, Fr>(rsock: RawFd, ssock: RawFd, f_send: Fs, mut f_recv: Fr) -> Option<SockAddr>
where
@@ -238,9 +287,9 @@ mod recvfrom {
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");
@@ -292,9 +341,9 @@ mod recvfrom {
}
#[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");
@@ -344,14 +393,14 @@ mod recvfrom {
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;
@@ -367,7 +416,7 @@ mod recvfrom {
}
sendmmsg(s, msgs.iter(), flags)
.map(move |sent_bytes| {
- assert!(sent_bytes.len() >= 1);
+ assert!(!sent_bytes.is_empty());
for sent in &sent_bytes {
assert_eq!(*sent, m.len());
}
@@ -425,7 +474,7 @@ mod recvfrom {
for iov in &iovs {
msgs.push_back(RecvMmsgData {
- iov: iov,
+ iov,
cmsg_buffer: None,
})
};
@@ -497,7 +546,7 @@ mod recvfrom {
for iov in &iovs {
msgs.push_back(RecvMmsgData {
- iov: iov,
+ iov,
cmsg_buffer: None,
})
};
@@ -519,7 +568,6 @@ mod recvfrom {
// Test error handling of our recvmsg wrapper
#[test]
pub fn test_recvmsg_ebadf() {
- use nix::Error;
use nix::errno::Errno;
use nix::sys::socket::{MsgFlags, recvmsg};
use nix::sys::uio::IoVec;
@@ -528,12 +576,12 @@ pub fn test_recvmsg_ebadf() {
let iov = [IoVec::from_mut_slice(&mut buf[..])];
let fd = -1; // Bad file descriptor
let r = recvmsg(fd, &iov, None, MsgFlags::empty());
- assert_eq!(r.err().unwrap(), Error::Sys(Errno::EBADF));
+ assert_eq!(r.err().unwrap(), Errno::EBADF);
}
// 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;
@@ -587,11 +635,10 @@ pub fn test_scm_rights() {
}
// 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,
@@ -655,12 +702,14 @@ pub fn test_af_alg_cipher() {
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,
@@ -739,6 +788,11 @@ pub fn test_af_alg_aead() {
// 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));
@@ -757,6 +811,7 @@ pub fn test_af_alg_aead() {
target_os = "netbsd"))]
#[test]
pub fn test_sendmsg_ipv4packetinfo() {
+ use cfg_if::cfg_if;
use nix::sys::uio::IoVec;
use nix::sys::socket::{socket, sendmsg, bind,
AddressFamily, SockType, SockFlag, SockAddr,
@@ -778,11 +833,21 @@ pub fn test_sendmsg_ipv4packetinfo() {
let iov = [IoVec::from_slice(&slice)];
if let InetAddr::V4(sin) = inet_addr {
- let pi = libc::in_pktinfo {
- ipi_ifindex: 0, /* Unspecified interface */
- ipi_addr: libc::in_addr { s_addr: 0 },
- ipi_spec_dst: sin.sin_addr,
- };
+ cfg_if! {
+ if #[cfg(target_os = "netbsd")] {
+ let _dontcare = sin;
+ let pi = libc::in_pktinfo {
+ ipi_ifindex: 0, /* Unspecified interface */
+ ipi_addr: libc::in_addr { s_addr: 0 },
+ };
+ } else {
+ let pi = libc::in_pktinfo {
+ ipi_ifindex: 0, /* Unspecified interface */
+ ipi_addr: libc::in_addr { s_addr: 0 },
+ ipi_spec_dst: sin.sin_addr,
+ };
+ }
+ }
let cmsg = [ControlMessage::Ipv4PacketInfo(&pi)];
@@ -807,7 +872,6 @@ pub fn test_sendmsg_ipv4packetinfo() {
target_os = "freebsd"))]
#[test]
pub fn test_sendmsg_ipv6packetinfo() {
- use nix::Error;
use nix::errno::Errno;
use nix::sys::uio::IoVec;
use nix::sys::socket::{socket, sendmsg, bind,
@@ -824,12 +888,9 @@ pub fn test_sendmsg_ipv6packetinfo() {
let inet_addr = InetAddr::from_std(&std_sa);
let sock_addr = SockAddr::new_inet(inet_addr);
- match bind(sock, &sock_addr) {
- Err(Error::Sys(Errno::EADDRNOTAVAIL)) => {
- println!("IPv6 not available, skipping test.");
- return;
- },
- _ => (),
+ if let Err(Errno::EADDRNOTAVAIL) = bind(sock, &sock_addr) {
+ println!("IPv6 not available, skipping test.");
+ return;
}
let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
@@ -853,7 +914,7 @@ pub fn test_sendmsg_ipv6packetinfo() {
/// 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;
@@ -862,7 +923,6 @@ fn test_scm_rights_single_cmsg_multiple_fds() {
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 || {
@@ -1000,13 +1060,11 @@ fn test_scm_credentials() {
/// 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);
}
@@ -1014,9 +1072,9 @@ fn test_scm_credentials_and_rights() {
/// 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];
@@ -1137,7 +1195,6 @@ pub fn test_unixdomain() {
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[test]
pub fn test_syscontrol() {
- use nix::Error;
use nix::errno::Errno;
use nix::sys::socket::{socket, SockAddr, SockType, SockFlag, SockProtocol};
@@ -1145,7 +1202,7 @@ pub fn test_syscontrol() {
SockFlag::empty(), SockProtocol::KextControl)
.expect("socket failed");
let _sockaddr = SockAddr::new_sys_control(fd, "com.apple.net.utun_control", 0).expect("resolving sys_control name failed");
- assert_eq!(SockAddr::new_sys_control(fd, "foo.bar.lol", 0).err(), Some(Error::Sys(Errno::ENOENT)));
+ assert_eq!(SockAddr::new_sys_control(fd, "foo.bar.lol", 0).err(), Some(Errno::ENOENT));
// requires root privileges
// connect(fd, &sockaddr).expect("connect failed");
@@ -1164,7 +1221,6 @@ fn loopback_address(family: AddressFamily) -> Option<nix::ifaddrs::InterfaceAddr
use std::io;
use std::io::Write;
use nix::ifaddrs::getifaddrs;
- use nix::sys::socket::SockAddr;
use nix::net::if_::*;
let addrs = match getifaddrs() {
@@ -1207,14 +1263,16 @@ fn loopback_address(family: AddressFamily) -> Option<nix::ifaddrs::InterfaceAddr
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};
@@ -1267,18 +1325,15 @@ pub fn test_recv_ipv4pktinfo() {
);
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);
@@ -1297,14 +1352,16 @@ pub fn test_recv_ipv4pktinfo() {
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};
@@ -1388,8 +1445,8 @@ pub fn test_recvif() {
_ => panic!("unexpected additional control msg"),
}
}
- assert_eq!(rx_recvif, true);
- assert_eq!(rx_recvdstaddr, true);
+ assert!(rx_recvif);
+ assert!(rx_recvdstaddr);
assert_eq!(msg.bytes, 8);
assert_eq!(
iovec[0].as_slice(),
@@ -1408,14 +1465,16 @@ pub fn test_recvif() {
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};
@@ -1468,18 +1527,16 @@ pub fn test_recv_ipv6pktinfo() {
);
let mut cmsgs = msg.cmsgs();
- match cmsgs.next() {
- Some(ControlMessageOwned::Ipv6PacketInfo(pktinfo)) => {
- let i = if_nametoindex(lo_name.as_bytes()).expect("if_nametoindex");
- assert_eq!(
- pktinfo.ipi6_ifindex,
- i,
- "unexpected ifindex (expected {}, got {})",
- i,
- pktinfo.ipi6_ifindex
- );
- }
- _ => (),
+ if let Some(ControlMessageOwned::Ipv6PacketInfo(pktinfo)) = cmsgs.next()
+ {
+ let i = if_nametoindex(lo_name.as_bytes()).expect("if_nametoindex");
+ assert_eq!(
+ pktinfo.ipi6_ifindex as libc::c_uint,
+ i,
+ "unexpected ifindex (expected {}, got {})",
+ i,
+ pktinfo.ipi6_ifindex
+ );
}
assert!(cmsgs.next().is_none(), "unexpected additional control msg");
assert_eq!(msg.bytes, 8);
@@ -1491,10 +1548,9 @@ pub fn test_recv_ipv6pktinfo() {
}
#[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::Error;
use nix::errno::Errno;
use nix::sys::socket::{AddressFamily, socket, bind, connect, listen,
SockAddr, SockType, SockFlag};
@@ -1507,16 +1563,10 @@ pub fn test_vsock() {
SockFlag::empty(), None)
.expect("socket failed");
- // VMADDR_CID_HYPERVISOR and VMADDR_CID_LOCAL are reserved, so we expect
- // an EADDRNOTAVAIL error.
+ // VMADDR_CID_HYPERVISOR is reserved, so we expect an EADDRNOTAVAIL error.
let sockaddr = SockAddr::new_vsock(libc::VMADDR_CID_HYPERVISOR, port);
assert_eq!(bind(s1, &sockaddr).err(),
- Some(Error::Sys(Errno::EADDRNOTAVAIL)));
-
- let sockaddr = SockAddr::new_vsock(libc::VMADDR_CID_LOCAL, port);
- assert_eq!(bind(s1, &sockaddr).err(),
- Some(Error::Sys(Errno::EADDRNOTAVAIL)));
-
+ Some(Errno::EADDRNOTAVAIL));
let sockaddr = SockAddr::new_vsock(libc::VMADDR_CID_ANY, port);
assert_eq!(bind(s1, &sockaddr), Ok(()));
@@ -1541,3 +1591,351 @@ pub fn test_vsock() {
close(s1).unwrap();
thr.join().unwrap();
}
+
+// 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() {
+ use nix::sys::socket::*;
+ use nix::sys::uio::IoVec;
+ use nix::sys::time::*;
+ use std::time::*;
+
+ // Set up
+ let message = "Ohayō!".as_bytes();
+ let in_socket = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None).unwrap();
+ setsockopt(in_socket, sockopt::ReceiveTimestampns, &true).unwrap();
+ let localhost = InetAddr::new(IpAddr::new_v4(127, 0, 0, 1), 0);
+ bind(in_socket, &SockAddr::new_inet(localhost)).unwrap();
+ let address = getsockname(in_socket).unwrap();
+ // Get initial time
+ let time0 = SystemTime::now();
+ // Send the message
+ let iov = [IoVec::from_slice(message)];
+ let flags = MsgFlags::empty();
+ let l = sendmsg(in_socket, &iov, &[], flags, Some(&address)).unwrap();
+ assert_eq!(message.len(), l);
+ // Receive the message
+ let mut buffer = vec![0u8; message.len()];
+ let mut cmsgspace = nix::cmsg_space!(TimeSpec);
+ let iov = [IoVec::from_mut_slice(&mut buffer)];
+ let r = recvmsg(in_socket, &iov, Some(&mut cmsgspace), flags).unwrap();
+ let rtime = match r.cmsgs().next() {
+ Some(ControlMessageOwned::ScmTimestampns(rtime)) => rtime,
+ Some(_) => panic!("Unexpected control message"),
+ None => panic!("No control message")
+ };
+ // Check the final time
+ let time1 = SystemTime::now();
+ // the packet's received timestamp should lie in-between the two system
+ // times, unless the system clock was adjusted in the meantime.
+ let rduration = Duration::new(rtime.tv_sec() as u64,
+ rtime.tv_nsec() as u32);
+ assert!(time0.duration_since(UNIX_EPOCH).unwrap() <= rduration);
+ assert!(rduration <= time1.duration_since(UNIX_EPOCH).unwrap());
+ // Close socket
+ 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(qemu, ignore)]
+#[cfg(all(target_os = "linux"))]
+#[test]
+fn test_recvmmsg_timestampns() {
+ use nix::sys::socket::*;
+ use nix::sys::uio::IoVec;
+ use nix::sys::time::*;
+ use std::time::*;
+
+ // Set up
+ let message = "Ohayō!".as_bytes();
+ let in_socket = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None).unwrap();
+ setsockopt(in_socket, sockopt::ReceiveTimestampns, &true).unwrap();
+ let localhost = InetAddr::new(IpAddr::new_v4(127, 0, 0, 1), 0);
+ bind(in_socket, &SockAddr::new_inet(localhost)).unwrap();
+ let address = getsockname(in_socket).unwrap();
+ // Get initial time
+ let time0 = SystemTime::now();
+ // Send the message
+ let iov = [IoVec::from_slice(message)];
+ let flags = MsgFlags::empty();
+ let l = sendmsg(in_socket, &iov, &[], flags, Some(&address)).unwrap();
+ assert_eq!(message.len(), l);
+ // Receive the message
+ let mut buffer = vec![0u8; message.len()];
+ let mut cmsgspace = nix::cmsg_space!(TimeSpec);
+ let iov = [IoVec::from_mut_slice(&mut buffer)];
+ let mut data = vec![
+ RecvMmsgData {
+ iov,
+ cmsg_buffer: Some(&mut cmsgspace),
+ },
+ ];
+ let r = recvmmsg(in_socket, &mut data, flags, None).unwrap();
+ let rtime = match r[0].cmsgs().next() {
+ Some(ControlMessageOwned::ScmTimestampns(rtime)) => rtime,
+ Some(_) => panic!("Unexpected control message"),
+ None => panic!("No control message")
+ };
+ // Check the final time
+ let time1 = SystemTime::now();
+ // the packet's received timestamp should lie in-between the two system
+ // times, unless the system clock was adjusted in the meantime.
+ let rduration = Duration::new(rtime.tv_sec() as u64,
+ rtime.tv_nsec() as u32);
+ assert!(time0.duration_since(UNIX_EPOCH).unwrap() <= rduration);
+ assert!(rduration <= time1.duration_since(UNIX_EPOCH).unwrap());
+ // Close socket
+ 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(qemu, ignore)]
+#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+#[test]
+fn test_recvmsg_rxq_ovfl() {
+ use nix::Error;
+ use nix::sys::socket::*;
+ use nix::sys::uio::IoVec;
+ use nix::sys::socket::sockopt::{RxqOvfl, RcvBuf};
+
+ let message = [0u8; 2048];
+ let bufsize = message.len() * 2;
+
+ let in_socket = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None).unwrap();
+ let out_socket = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None).unwrap();
+
+ let localhost = InetAddr::new(IpAddr::new_v4(127, 0, 0, 1), 0);
+ bind(in_socket, &SockAddr::new_inet(localhost)).unwrap();
+
+ let address = getsockname(in_socket).unwrap();
+ connect(out_socket, &address).unwrap();
+
+ // Set SO_RXQ_OVFL flag.
+ setsockopt(in_socket, RxqOvfl, &1).unwrap();
+
+ // Set the receiver buffer size to hold only 2 messages.
+ setsockopt(in_socket, RcvBuf, &bufsize).unwrap();
+
+ let mut drop_counter = 0;
+
+ for _ in 0..2 {
+ let iov = [IoVec::from_slice(&message)];
+ let flags = MsgFlags::empty();
+
+ // Send the 3 messages (the receiver buffer can only hold 2 messages)
+ // to create an overflow.
+ for _ in 0..3 {
+ let l = sendmsg(out_socket, &iov, &[], flags, Some(&address)).unwrap();
+ assert_eq!(message.len(), l);
+ }
+
+ // Receive the message and check the drop counter if any.
+ loop {
+ let mut buffer = vec![0u8; message.len()];
+ let mut cmsgspace = nix::cmsg_space!(u32);
+
+ let iov = [IoVec::from_mut_slice(&mut buffer)];
+
+ match recvmsg(
+ in_socket,
+ &iov,
+ Some(&mut cmsgspace),
+ MsgFlags::MSG_DONTWAIT) {
+ Ok(r) => {
+ drop_counter = match r.cmsgs().next() {
+ Some(ControlMessageOwned::RxqOvfl(drop_counter)) => drop_counter,
+ Some(_) => panic!("Unexpected control message"),
+ None => 0,
+ };
+ },
+ Err(Error::EAGAIN) => { break; },
+ _ => { panic!("unknown recvmsg() error"); },
+ }
+ }
+ }
+
+ // One packet lost.
+ assert_eq!(drop_counter, 1);
+
+ // Close sockets
+ 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 5606593..01920fd 100644
--- a/test/sys/test_sockopt.rs
+++ b/test/sys/test_sockopt.rs
@@ -3,12 +3,53 @@ use nix::sys::socket::{socket, sockopt, getsockopt, setsockopt, AddressFamily, S
#[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();
@@ -20,7 +61,7 @@ fn is_so_mark_functional() {
fn test_so_buf() {
let fd = socket(AddressFamily::Inet, SockType::Datagram, SockFlag::empty(), SockProtocol::Udp)
.unwrap();
- let bufsize: usize = thread_rng().gen_range(4096, 131_072);
+ let bufsize: usize = thread_rng().gen_range(4096..131_072);
setsockopt(fd, sockopt::SndBuf, &bufsize).unwrap();
let actual = getsockopt(fd, sockopt::SndBuf).unwrap();
assert!(actual >= bufsize);
@@ -29,6 +70,57 @@ fn test_so_buf() {
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.
@@ -74,7 +166,7 @@ fn test_bindtodevice() {
fn test_so_tcp_keepalive() {
let fd = socket(AddressFamily::Inet, SockType::Stream, SockFlag::empty(), SockProtocol::Tcp).unwrap();
setsockopt(fd, sockopt::KeepAlive, &true).unwrap();
- assert_eq!(getsockopt(fd, sockopt::KeepAlive).unwrap(), true);
+ assert!(getsockopt(fd, sockopt::KeepAlive).unwrap());
#[cfg(any(target_os = "android",
target_os = "dragonfly",
@@ -94,3 +186,14 @@ fn test_so_tcp_keepalive() {
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 00aeb2f..4a86154 100644
--- a/test/sys/test_termios.rs
+++ b/test/sys/test_termios.rs
@@ -1,7 +1,7 @@
use std::os::unix::prelude::*;
use tempfile::tempfile;
-use nix::{Error, fcntl};
+use nix::fcntl;
use nix::errno::Errno;
use nix::pty::openpty;
use nix::sys::termios::{self, LocalFlags, OutputFlags, tcgetattr};
@@ -19,7 +19,7 @@ fn write_all(f: RawFd, buf: &[u8]) {
#[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());
@@ -32,21 +32,21 @@ fn test_tcgetattr_pty() {
fn test_tcgetattr_enotty() {
let file = tempfile().unwrap();
assert_eq!(termios::tcgetattr(file.as_raw_fd()).err(),
- Some(Error::Sys(Errno::ENOTTY)));
+ Some(Errno::ENOTTY));
}
// Test tcgetattr on an invalid file descriptor
#[test]
fn test_tcgetattr_ebadf() {
assert_eq!(termios::tcgetattr(-1).err(),
- Some(Error::Sys(Errno::EBADF)));
+ Some(Errno::EBADF));
}
// Test modifying output flags
#[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 @@ fn test_output_flags() {
#[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 = {
@@ -126,5 +126,5 @@ fn test_local_flags() {
let read = read(pty.master, &mut buf).unwrap_err();
close(pty.master).unwrap();
close(pty.slave).unwrap();
- assert_eq!(read, Error::Sys(Errno::EAGAIN));
+ assert_eq!(read, Errno::EAGAIN);
}
diff --git a/test/sys/test_uio.rs b/test/sys/test_uio.rs
index 8d22bf1..c63b581 100644
--- a/test/sys/test_uio.rs
+++ b/test/sys/test_uio.rs
@@ -14,7 +14,11 @@ use tempfile::tempdir;
fn test_writev() {
let mut to_write = Vec::with_capacity(16 * 128);
for _ in 0..16 {
- let s: String = thread_rng().sample_iter(&Alphanumeric).take(128).collect();
+ let s: String = thread_rng()
+ .sample_iter(&Alphanumeric)
+ .map(char::from)
+ .take(128)
+ .collect();
let b = s.as_bytes();
to_write.extend(b.iter().cloned());
}
@@ -23,7 +27,7 @@ fn test_writev() {
let mut consumed = 0;
while consumed < to_write.len() {
let left = to_write.len() - consumed;
- let slice_len = if left <= 64 { left } else { thread_rng().gen_range(64, cmp::min(256, left)) };
+ let slice_len = if left <= 64 { left } else { thread_rng().gen_range(64..cmp::min(256, left)) };
let b = &to_write[consumed..consumed+slice_len];
iovecs.push(IoVec::from_slice(b));
consumed += slice_len;
@@ -57,13 +61,17 @@ fn test_writev() {
#[test]
#[cfg(not(target_os = "redox"))]
fn test_readv() {
- let s:String = thread_rng().sample_iter(&Alphanumeric).take(128).collect();
+ let s:String = thread_rng()
+ .sample_iter(&Alphanumeric)
+ .map(char::from)
+ .take(128)
+ .collect();
let to_write = s.as_bytes().to_vec();
let mut storage = Vec::new();
let mut allocated = 0;
while allocated < to_write.len() {
let left = to_write.len() - allocated;
- let vec_len = if left <= 64 { left } else { thread_rng().gen_range(64, cmp::min(256, left)) };
+ let vec_len = if left <= 64 { left } else { thread_rng().gen_range(64..cmp::min(256, left)) };
let v: Vec<u8> = iter::repeat(0u8).take(vec_len).collect();
storage.push(v);
allocated += vec_len;
@@ -133,7 +141,7 @@ fn test_pread() {
}
#[test]
-#[cfg(target_os = "linux")]
+#[cfg(not(target_os = "redox"))]
fn test_pwritev() {
use std::io::Read;
@@ -163,7 +171,7 @@ fn test_pwritev() {
}
#[test]
-#[cfg(target_os = "linux")]
+#[cfg(not(target_os = "redox"))]
fn test_preadv() {
use std::io::Write;
@@ -197,16 +205,16 @@ fn test_preadv() {
#[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 _ = 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 5bb298e..afe4f42 100644
--- a/test/sys/test_wait.rs
+++ b/test/sys/test_wait.rs
@@ -1,4 +1,4 @@
-use nix::Error;
+use nix::errno::Errno;
use nix::unistd::*;
use nix::unistd::ForkResult::*;
use nix::sys::signal::*;
@@ -8,7 +8,7 @@ use libc::_exit;
#[test]
#[cfg(not(target_os = "redox"))]
fn test_wait_signal() {
- let _ = 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 @@ fn test_wait_signal() {
#[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") {
@@ -41,12 +41,12 @@ fn test_waitstatus_from_raw() {
let pid = Pid::from_raw(1);
assert_eq!(WaitStatus::from_raw(pid, 0x0002), Ok(WaitStatus::Signaled(pid, Signal::SIGINT, false)));
assert_eq!(WaitStatus::from_raw(pid, 0x0200), Ok(WaitStatus::Exited(pid, 2)));
- assert_eq!(WaitStatus::from_raw(pid, 0x7f7f), Err(Error::invalid_argument()));
+ assert_eq!(WaitStatus::from_raw(pid, 0x7f7f), Err(Errno::EINVAL));
}
#[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 @@ mod ptrace {
#[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 5a5330b..aade937 100644
--- a/test/test.rs
+++ b/test/test.rs
@@ -13,6 +13,8 @@ mod test_fcntl;
#[cfg(any(target_os = "android",
target_os = "linux"))]
mod test_kmod;
+#[cfg(target_os = "freebsd")]
+mod test_nmount;
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "fushsia",
@@ -22,6 +24,7 @@ mod test_mq;
#[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;
@@ -40,7 +43,7 @@ mod test_unistd;
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};
@@ -81,8 +84,7 @@ struct DirRestore<'a> {
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 505277e..2940b6e 100644
--- a/test/test_dir.rs
+++ b/test/test_dir.rs
@@ -4,13 +4,25 @@ use nix::sys::stat::Mode;
use std::fs::File;
use tempfile::tempdir;
+
+#[cfg(test)]
+fn flags() -> OFlag {
+ #[cfg(target_os = "illumos")]
+ let f = OFlag::O_RDONLY | OFlag::O_CLOEXEC;
+
+ #[cfg(not(target_os = "illumos"))]
+ let f = OFlag::O_RDONLY | OFlag::O_CLOEXEC | OFlag::O_DIRECTORY;
+
+ f
+}
+
#[test]
+#[allow(clippy::unnecessary_sort_by)] // False positive
fn read() {
let tmp = tempdir().unwrap();
File::create(&tmp.path().join("foo")).unwrap();
::std::os::unix::fs::symlink("foo", tmp.path().join("bar")).unwrap();
- let mut dir = Dir::open(tmp.path(), OFlag::O_DIRECTORY | OFlag::O_RDONLY | OFlag::O_CLOEXEC,
- Mode::empty()).unwrap();
+ let mut dir = Dir::open(tmp.path(), flags(), Mode::empty()).unwrap();
let mut entries: Vec<_> = dir.iter().map(|e| e.unwrap()).collect();
entries.sort_by(|a, b| a.file_name().cmp(b.file_name()));
let entry_names: Vec<_> = entries
@@ -30,8 +42,7 @@ fn read() {
#[test]
fn rewind() {
let tmp = tempdir().unwrap();
- let mut dir = Dir::open(tmp.path(), OFlag::O_DIRECTORY | OFlag::O_RDONLY | OFlag::O_CLOEXEC,
- Mode::empty()).unwrap();
+ let mut dir = Dir::open(tmp.path(), flags(), Mode::empty()).unwrap();
let entries1: Vec<_> = dir.iter().map(|e| e.unwrap().file_name().to_owned()).collect();
let entries2: Vec<_> = dir.iter().map(|e| e.unwrap().file_name().to_owned()).collect();
let entries3: Vec<_> = dir.into_iter().map(|e| e.unwrap().file_name().to_owned()).collect();
@@ -41,5 +52,5 @@ fn rewind() {
#[test]
fn ebadf() {
- assert_eq!(Dir::from_fd(-1).unwrap_err(), nix::Error::Sys(nix::errno::Errno::EBADF));
+ assert_eq!(Dir::from_fd(-1).unwrap_err(), nix::Error::EBADF);
}
diff --git a/test/test_fcntl.rs b/test/test_fcntl.rs
index 5d1bafe..db2acfb 100644
--- a/test/test_fcntl.rs
+++ b/test/test_fcntl.rs
@@ -1,11 +1,20 @@
#[cfg(not(target_os = "redox"))]
-use nix::Error;
-#[cfg(not(target_os = "redox"))]
use nix::errno::*;
#[cfg(not(target_os = "redox"))]
use nix::fcntl::{open, OFlag, readlink};
#[cfg(not(target_os = "redox"))]
use nix::fcntl::{openat, readlinkat, renameat};
+#[cfg(all(
+ target_os = "linux",
+ target_env = "gnu",
+ any(
+ target_arch = "x86_64",
+ target_arch = "x32",
+ target_arch = "powerpc",
+ target_arch = "s390x"
+ )
+))]
+use nix::fcntl::{RenameFlags, renameat2};
#[cfg(not(target_os = "redox"))]
use nix::sys::stat::Mode;
#[cfg(not(target_os = "redox"))]
@@ -19,8 +28,6 @@ use std::io::prelude::*;
#[cfg(not(target_os = "redox"))]
use std::os::unix::fs;
-use crate::*;
-
#[test]
#[cfg(not(target_os = "redox"))]
fn test_openat() {
@@ -55,13 +62,139 @@ fn test_renameat() {
let new_dirfd = open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap();
assert_eq!(renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap_err(),
- Error::Sys(Errno::ENOENT));
+ Errno::ENOENT);
+ close(old_dirfd).unwrap();
+ close(new_dirfd).unwrap();
+ assert!(new_dir.path().join("new").exists());
+}
+
+#[test]
+#[cfg(all(
+ target_os = "linux",
+ target_env = "gnu",
+ any(
+ target_arch = "x86_64",
+ target_arch = "x32",
+ target_arch = "powerpc",
+ target_arch = "s390x"
+ )
+))]
+fn test_renameat2_behaves_like_renameat_with_no_flags() {
+ let old_dir = tempfile::tempdir().unwrap();
+ let old_dirfd = open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
+ let old_path = old_dir.path().join("old");
+ File::create(&old_path).unwrap();
+ let new_dir = tempfile::tempdir().unwrap();
+ let new_dirfd = open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
+ renameat2(
+ Some(old_dirfd),
+ "old",
+ Some(new_dirfd),
+ "new",
+ RenameFlags::empty(),
+ )
+ .unwrap();
+ assert_eq!(
+ renameat2(
+ Some(old_dirfd),
+ "old",
+ Some(new_dirfd),
+ "new",
+ RenameFlags::empty()
+ )
+ .unwrap_err(),
+ Errno::ENOENT
+ );
close(old_dirfd).unwrap();
close(new_dirfd).unwrap();
assert!(new_dir.path().join("new").exists());
}
#[test]
+#[cfg(all(
+ target_os = "linux",
+ target_env = "gnu",
+ any(
+ target_arch = "x86_64",
+ target_arch = "x32",
+ target_arch = "powerpc",
+ target_arch = "s390x"
+ )
+))]
+fn test_renameat2_exchange() {
+ let old_dir = tempfile::tempdir().unwrap();
+ let old_dirfd = open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
+ let old_path = old_dir.path().join("old");
+ {
+ let mut old_f = File::create(&old_path).unwrap();
+ old_f.write_all(b"old").unwrap();
+ }
+ let new_dir = tempfile::tempdir().unwrap();
+ let new_dirfd = open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
+ let new_path = new_dir.path().join("new");
+ {
+ let mut new_f = File::create(&new_path).unwrap();
+ new_f.write_all(b"new").unwrap();
+ }
+ renameat2(
+ Some(old_dirfd),
+ "old",
+ Some(new_dirfd),
+ "new",
+ RenameFlags::RENAME_EXCHANGE,
+ )
+ .unwrap();
+ let mut buf = String::new();
+ let mut new_f = File::open(&new_path).unwrap();
+ new_f.read_to_string(&mut buf).unwrap();
+ assert_eq!(buf, "old");
+ buf = "".to_string();
+ let mut old_f = File::open(&old_path).unwrap();
+ old_f.read_to_string(&mut buf).unwrap();
+ assert_eq!(buf, "new");
+ close(old_dirfd).unwrap();
+ close(new_dirfd).unwrap();
+}
+
+#[test]
+#[cfg(all(
+ target_os = "linux",
+ target_env = "gnu",
+ any(
+ target_arch = "x86_64",
+ target_arch = "x32",
+ target_arch = "powerpc",
+ target_arch = "s390x"
+ )
+))]
+fn test_renameat2_noreplace() {
+ let old_dir = tempfile::tempdir().unwrap();
+ let old_dirfd = open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
+ let old_path = old_dir.path().join("old");
+ File::create(&old_path).unwrap();
+ 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");
+ File::create(&new_path).unwrap();
+ assert_eq!(
+ renameat2(
+ Some(old_dirfd),
+ "old",
+ Some(new_dirfd),
+ "new",
+ RenameFlags::RENAME_NOREPLACE
+ )
+ .unwrap_err(),
+ Errno::EEXIST
+ );
+ close(old_dirfd).unwrap();
+ close(new_dirfd).unwrap();
+ assert!(new_dir.path().join("new").exists());
+ assert!(old_dir.path().join("old").exists());
+}
+
+
+#[test]
#[cfg(not(target_os = "redox"))]
fn test_readlink() {
let tempdir = tempfile::tempdir().unwrap();
@@ -81,19 +214,18 @@ fn test_readlink() {
#[cfg(any(target_os = "linux", target_os = "android"))]
mod linux_android {
- use std::fs::File;
use std::io::prelude::*;
- use std::io::{BufRead, BufReader, SeekFrom};
+ use std::io::SeekFrom;
use std::os::unix::prelude::*;
-
use libc::loff_t;
use nix::fcntl::*;
- use nix::sys::stat::fstat;
use nix::sys::uio::IoVec;
use nix::unistd::{close, pipe, read, write};
- use tempfile::{tempfile, NamedTempFile};
+ use tempfile::tempfile;
+ #[cfg(any(target_os = "linux"))]
+ use tempfile::NamedTempFile;
use crate::*;
@@ -103,11 +235,9 @@ mod linux_android {
/// resulting file is read and should contain the contents `bar`.
/// The from_offset should be updated by the call to reflect
/// the 3 bytes read (6).
- ///
- /// FIXME: This test is disabled for linux based builds, because Travis
- /// Linux version is too old for `copy_file_range`.
#[test]
- #[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";
@@ -189,9 +319,10 @@ mod linux_android {
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();
@@ -206,6 +337,7 @@ mod linux_android {
close(wr).unwrap();
}
+ #[cfg(any(target_os = "linux"))]
#[test]
fn test_fallocate() {
let tmp = NamedTempFile::new().unwrap();
@@ -224,17 +356,11 @@ mod linux_android {
// they run under QEMU.
#[test]
- #[cfg(not(any(target_arch = "aarch64",
- target_arch = "arm",
- target_arch = "armv7",
- target_arch = "x86",
- target_arch = "mips",
- target_arch = "mips64",
- target_arch = "mips64el",
- target_arch = "powerpc64",
- target_arch = "powerpc64le",
- target_env = "musl")))]
+ #[cfg(all(target_os = "linux", not(target_env = "musl")))]
fn test_ofd_write_lock() {
+ use nix::sys::stat::fstat;
+ use std::mem;
+
let tmp = NamedTempFile::new().unwrap();
let fd = tmp.as_raw_fd();
@@ -247,13 +373,14 @@ mod linux_android {
}
let inode = fstat(fd).expect("fstat failed").st_ino as usize;
- let mut flock = libc::flock {
- l_type: libc::F_WRLCK as libc::c_short,
- l_whence: libc::SEEK_SET as libc::c_short,
- l_start: 0,
- l_len: 0,
- l_pid: 0,
+ let mut flock: libc::flock = unsafe {
+ mem::zeroed() // required for Linux/mips
};
+ flock.l_type = libc::F_WRLCK as libc::c_short;
+ flock.l_whence = libc::SEEK_SET as libc::c_short;
+ flock.l_start = 0;
+ flock.l_len = 0;
+ flock.l_pid = 0;
fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("write lock failed");
assert_eq!(
Some(("OFDLCK".to_string(), "WRITE".to_string())),
@@ -266,17 +393,11 @@ mod linux_android {
}
#[test]
- #[cfg(not(any(target_arch = "aarch64",
- target_arch = "arm",
- target_arch = "armv7",
- target_arch = "x86",
- target_arch = "mips",
- target_arch = "mips64",
- target_arch = "mips64el",
- target_arch = "powerpc64",
- target_arch = "powerpc64le",
- target_env = "musl")))]
+ #[cfg(all(target_os = "linux", not(target_env = "musl")))]
fn test_ofd_read_lock() {
+ use nix::sys::stat::fstat;
+ use std::mem;
+
let tmp = NamedTempFile::new().unwrap();
let fd = tmp.as_raw_fd();
@@ -289,13 +410,14 @@ mod linux_android {
}
let inode = fstat(fd).expect("fstat failed").st_ino as usize;
- let mut flock = libc::flock {
- l_type: libc::F_RDLCK as libc::c_short,
- l_whence: libc::SEEK_SET as libc::c_short,
- l_start: 0,
- l_len: 0,
- l_pid: 0,
+ let mut flock: libc::flock = unsafe {
+ mem::zeroed() // required for Linux/mips
};
+ flock.l_type = libc::F_RDLCK as libc::c_short;
+ flock.l_whence = libc::SEEK_SET as libc::c_short;
+ flock.l_start = 0;
+ flock.l_len = 0;
+ flock.l_pid = 0;
fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("read lock failed");
assert_eq!(
Some(("OFDLCK".to_string(), "READ".to_string())),
@@ -307,7 +429,13 @@ mod linux_android {
assert_eq!(None, lock_info(inode));
}
+ #[cfg(all(target_os = "linux", not(target_env = "musl")))]
fn lock_info(inode: usize) -> Option<(String, String)> {
+ use std::{
+ fs::File,
+ io::BufReader
+ };
+
let file = File::open("/proc/locks").expect("open /proc/locks failed");
let buf = BufReader::new(file);
@@ -345,17 +473,16 @@ mod test_posix_fadvise {
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));
}
}
@@ -385,7 +512,7 @@ mod test_posix_fallocate {
assert_eq!(tmp.read(&mut data).expect("read failure"), LEN);
assert_eq!(&data[..], &[0u8; LEN][..]);
}
- Err(nix::Error::Sys(Errno::EINVAL)) => {
+ Err(Errno::EINVAL) => {
// POSIX requires posix_fallocate to return EINVAL both for
// invalid arguments (i.e. len < 0) and if the operation is not
// supported by the file system.
@@ -401,12 +528,8 @@ mod test_posix_fallocate {
fn errno() {
let (rd, _wr) = pipe().unwrap();
let err = posix_fallocate(rd as RawFd, 0, 100).unwrap_err();
- use nix::Error::Sys;
match err {
- Sys(Errno::EINVAL)
- | Sys(Errno::ENODEV)
- | Sys(Errno::ESPIPE)
- | Sys(Errno::EBADF) => (),
+ Errno::EINVAL | Errno::ENODEV | Errno::ESPIPE | Errno::EBADF => (),
errno =>
panic!(
"unexpected errno {}",
diff --git a/test/test_kmod/mod.rs b/test/test_kmod/mod.rs
index fb7260b..8eef538 100644
--- a/test/test_kmod/mod.rs
+++ b/test/test_kmod/mod.rs
@@ -5,9 +5,7 @@ use tempfile::{tempdir, TempDir};
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 @@ fn compile_kernel_module() -> (PathBuf, String, TempDir) {
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 @@ fn test_finit_and_delete_module() {
}
#[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 @@ fn test_finit_and_delete_modul_with_params() {
#[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 @@ fn test_init_and_delete_module() {
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 @@ fn test_init_and_delete_module() {
#[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 @@ fn test_init_and_delete_module_with_params() {
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 @@ fn test_init_and_delete_module_with_params() {
#[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::Sys(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 @@ fn test_finit_module_twice_and_delete_module() {
let result = finit_module(&f, &CString::new("").unwrap(), ModuleInitFlags::empty());
- assert_eq!(result.unwrap_err(), Error::Sys(Errno::EEXIST));
+ assert_eq!(result.unwrap_err(), Errno::EEXIST);
delete_module(
&CString::new(kmod_name).unwrap(),
@@ -157,11 +154,11 @@ fn test_finit_module_twice_and_delete_module() {
#[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::Sys(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 @@ mod test_mount {
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 1667a35..430df5d 100644
--- a/test/test_mq.rs
+++ b/test/test_mq.rs
@@ -1,8 +1,7 @@
use std::ffi::CString;
use std::str;
-use nix::errno::Errno::*;
-use nix::Error::Sys;
+use nix::errno::Errno;
use nix::mqueue::{mq_open, mq_close, mq_send, mq_receive, mq_attr_member_t};
use nix::mqueue::{MqAttr, MQ_OFlag};
use nix::sys::stat::Mode;
@@ -16,7 +15,7 @@ fn test_mq_send_and_receive() {
let oflag0 = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
let r0 = mq_open(mq_name, oflag0, mode, Some(&attr));
- if let Err(Sys(ENOSYS)) = r0 {
+ if let Err(Errno::ENOSYS) = r0 {
println!("message queues not supported or module not loaded?");
return;
};
@@ -47,7 +46,7 @@ fn test_mq_getattr() {
let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
let r = mq_open(mq_name, oflag, mode, Some(&initial_attr));
- if let Err(Sys(ENOSYS)) = r {
+ if let Err(Errno::ENOSYS) = r {
println!("message queues not supported or module not loaded?");
return;
};
@@ -61,7 +60,11 @@ fn test_mq_getattr() {
// 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;
@@ -70,7 +73,7 @@ fn test_mq_setattr() {
let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
let r = mq_open(mq_name, oflag, mode, Some(&initial_attr));
- if let Err(Sys(ENOSYS)) = r {
+ if let Err(Errno::ENOSYS) = r {
println!("message queues not supported or module not loaded?");
return;
};
@@ -98,7 +101,11 @@ fn test_mq_setattr() {
// 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;
@@ -107,7 +114,7 @@ fn test_mq_set_nonblocking() {
let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
let r = mq_open(mq_name, oflag, mode, Some(&initial_attr));
- if let Err(Sys(ENOSYS)) = r {
+ if let Err(Errno::ENOSYS) = r {
println!("message queues not supported or module not loaded?");
return;
};
@@ -132,7 +139,7 @@ fn test_mq_unlink() {
let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
let r = mq_open(mq_name_opened, oflag, mode, Some(&initial_attr));
- if let Err(Sys(ENOSYS)) = r {
+ if let Err(Errno::ENOSYS) = r {
println!("message queues not supported or module not loaded?");
return;
};
@@ -142,9 +149,9 @@ fn test_mq_unlink() {
assert_eq!(res_unlink, Ok(()) );
let res_unlink_not_opened = mq_unlink(mq_name_not_opened);
- assert_eq!(res_unlink_not_opened, Err(Sys(ENOENT)) );
+ assert_eq!(res_unlink_not_opened, Err(Errno::ENOENT) );
mq_close(mqd).unwrap();
let res_unlink_after_close = mq_unlink(mq_name_opened);
- assert_eq!(res_unlink_after_close, Err(Sys(ENOENT)) );
+ assert_eq!(res_unlink_after_close, Err(Errno::ENOENT) );
}
diff --git a/test/test_net.rs b/test/test_net.rs
index b8940e7..40ecd6b 100644
--- a/test/test_net.rs
+++ b/test/test_net.rs
@@ -8,5 +8,5 @@ const LOOPBACK: &[u8] = b"lo0";
#[test]
fn test_if_nametoindex() {
- assert!(if_nametoindex(&LOOPBACK[..]).is_ok());
+ assert!(if_nametoindex(LOOPBACK).is_ok());
}
diff --git a/test/test_nmount.rs b/test/test_nmount.rs
new file mode 100644
index 0000000..4c74ecf
--- /dev/null
+++ b/test/test_nmount.rs
@@ -0,0 +1,51 @@
+use crate::*;
+use nix::{
+ errno::Errno,
+ mount::{MntFlags, Nmount, unmount}
+};
+use std::{
+ ffi::CString,
+ fs::File,
+ path::Path
+};
+use tempfile::tempdir;
+
+#[test]
+fn ok() {
+ require_mount!("nullfs");
+
+ let mountpoint = tempdir().unwrap();
+ let target = tempdir().unwrap();
+ let _sentry = File::create(target.path().join("sentry")).unwrap();
+
+ let fstype = CString::new("fstype").unwrap();
+ let nullfs = CString::new("nullfs").unwrap();
+ Nmount::new()
+ .str_opt(&fstype, &nullfs)
+ .str_opt_owned("fspath", mountpoint.path().to_str().unwrap())
+ .str_opt_owned("target", target.path().to_str().unwrap())
+ .nmount(MntFlags::empty()).unwrap();
+
+ // Now check that the sentry is visible through the mountpoint
+ let exists = Path::exists(&mountpoint.path().join("sentry"));
+
+ // Cleanup the mountpoint before asserting
+ unmount(mountpoint.path(), MntFlags::empty()).unwrap();
+
+ assert!(exists);
+}
+
+#[test]
+fn bad_fstype() {
+ let mountpoint = tempdir().unwrap();
+ let target = tempdir().unwrap();
+ let _sentry = File::create(target.path().join("sentry")).unwrap();
+
+ let e = Nmount::new()
+ .str_opt_owned("fspath", mountpoint.path().to_str().unwrap())
+ .str_opt_owned("target", target.path().to_str().unwrap())
+ .nmount(MntFlags::empty()).unwrap_err();
+
+ assert_eq!(e.error(), Errno::EINVAL);
+ assert_eq!(e.errmsg(), Some("Invalid fstype"));
+}
diff --git a/test/test_poll.rs b/test/test_poll.rs
index a5e2d25..e4b369f 100644
--- a/test/test_poll.rs
+++ b/test/test_poll.rs
@@ -1,5 +1,4 @@
use nix::{
- Error,
errno::Errno,
poll::{PollFlags, poll, PollFd},
unistd::{write, pipe}
@@ -10,8 +9,8 @@ macro_rules! loop_while_eintr {
loop {
match $poll_expr {
Ok(nfds) => break nfds,
- Err(Error::Sys(Errno::EINTR)) => (),
- Err(e) => panic!(e)
+ Err(Errno::EINTR) => (),
+ Err(e) => panic!("{}", e)
}
}
}
@@ -65,3 +64,19 @@ fn test_ppoll() {
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 ab347bb..71932f2 100644
--- a/test/test_pty.rs
+++ b/test/test_pty.rs
@@ -29,7 +29,7 @@ fn test_explicit_close() {
#[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 @@ fn test_ptsname_equivalence() {
#[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 @@ fn test_ptsname_r_copy() {
#[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 @@ fn test_ptsname_unique() {
/// 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");
@@ -112,6 +112,30 @@ fn open_ptty_pair() -> (PtyMaster, File) {
// Open the slave device
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};
+
+ // On illumos systems, as per pts(7D), one must push STREAMS modules
+ // after opening a device path returned from ptsname().
+ let ptem = b"ptem\0";
+ let ldterm = b"ldterm\0";
+ let r = unsafe { ioctl(slave_fd, I_FIND, ldterm.as_ptr()) };
+ if r < 0 {
+ panic!("I_FIND failure");
+ } else if r == 0 {
+ if unsafe { ioctl(slave_fd, I_PUSH, ptem.as_ptr()) } < 0 {
+ panic!("I_PUSH ptem failure");
+ }
+ if unsafe { ioctl(slave_fd, I_PUSH, ldterm.as_ptr()) } < 0 {
+ panic!("I_PUSH ldterm failure");
+ }
+ }
+ }
+
let slave = unsafe { File::from_raw_fd(slave_fd) };
(master, slave)
@@ -163,7 +187,7 @@ fn test_write_ptty_pair() {
#[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);
@@ -198,7 +222,7 @@ fn test_openpty() {
#[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 = {
@@ -249,13 +273,15 @@ fn test_forkpty() {
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";
- let pty = forkpty(None, None).unwrap();
+ let pty = unsafe {
+ forkpty(None, None).unwrap()
+ };
match pty.fork_result {
Child => {
write(STDOUT_FILENO, string.as_bytes()).unwrap();
diff --git a/test/test_ptymaster_drop.rs b/test/test_ptymaster_drop.rs
index ff939b9..a68f81e 100644
--- a/test/test_ptymaster_drop.rs
+++ b/test/test_ptymaster_drop.rs
@@ -12,10 +12,6 @@ mod t {
/// race condition.
#[test]
#[should_panic(expected = "Closing an invalid file descriptor!")]
- // In Travis on i686-unknown-linux-musl, this test gets SIGABRT. I don't
- // know why. It doesn't happen on any other target, and it doesn't happen
- // on my PC.
- #[cfg_attr(all(target_env = "musl", target_arch = "x86"), ignore)]
fn test_double_close() {
let m = posix_openpt(OFlag::O_RDWR).unwrap();
close(m.as_raw_fd()).unwrap();
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_sendfile.rs b/test/test_sendfile.rs
index 3bc7932..b6559d3 100644
--- a/test/test_sendfile.rs
+++ b/test/test_sendfile.rs
@@ -36,6 +36,28 @@ fn test_sendfile_linux() {
close(wr).unwrap();
}
+#[cfg(target_os = "linux")]
+#[test]
+fn test_sendfile64_linux() {
+ const CONTENTS: &[u8] = b"abcdef123456";
+ let mut tmp = tempfile().unwrap();
+ tmp.write_all(CONTENTS).unwrap();
+
+ let (rd, wr) = pipe().unwrap();
+ let mut offset: libc::off64_t = 5;
+ let res = sendfile64(wr, tmp.as_raw_fd(), Some(&mut offset), 2).unwrap();
+
+ assert_eq!(2, res);
+
+ let mut buf = [0u8; 1024];
+ assert_eq!(2, read(rd, &mut buf).unwrap());
+ assert_eq!(b"f1", &buf[0..2]);
+ assert_eq!(7, offset);
+
+ close(rd).unwrap();
+ close(wr).unwrap();
+}
+
#[cfg(target_os = "freebsd")]
#[test]
fn test_sendfile_freebsd() {
diff --git a/test/test_stat.rs b/test/test_stat.rs
index 0b94666..33cf748 100644
--- a/test/test_stat.rs
+++ b/test/test_stat.rs
@@ -10,10 +10,11 @@ use std::time::{Duration, UNIX_EPOCH};
use std::path::Path;
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
-use libc::{S_IFMT, S_IFLNK, mode_t};
+use libc::{S_IFMT, S_IFLNK};
+use libc::mode_t;
#[cfg(not(target_os = "redox"))]
-use nix::{fcntl, Error};
+use nix::fcntl;
#[cfg(not(target_os = "redox"))]
use nix::errno::Errno;
#[cfg(not(target_os = "redox"))]
@@ -42,18 +43,6 @@ use nix::unistd::chdir;
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
use nix::Result;
-use tempfile;
-
-#[allow(unused_comparisons)]
-// uid and gid are signed on Windows, but not on other platforms. This function
-// allows warning free compiles on all platforms, and can be removed when
-// expression-level #[allow] is available.
-#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
-fn valid_uid_gid(stat: FileStat) -> bool {
- // uid could be 0 for the `root` user. This quite possible when
- // the tests are being run on a rooted Android device.
- stat.st_uid >= 0 && stat.st_gid >= 0
-}
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
fn assert_stat_results(stat_result: Result<FileStat>) {
@@ -62,13 +51,15 @@ fn assert_stat_results(stat_result: Result<FileStat>) {
assert!(stats.st_ino > 0); // inode is positive integer, exact number machine dependent
assert!(stats.st_mode > 0); // must be positive integer
assert_eq!(stats.st_nlink, 1); // there links created, must be 1
- assert!(valid_uid_gid(stats)); // must be positive integers
assert_eq!(stats.st_size, 0); // size is 0 because we did not write anything to the file
assert!(stats.st_blksize > 0); // must be positive integer, exact number machine dependent
assert!(stats.st_blocks <= 16); // Up to 16 blocks can be allocated for a blank file
}
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
+// (Android's st_blocks is ulonglong which is always non-negative.)
+#[cfg_attr(target_os = "android", allow(unused_comparisons))]
+#[allow(clippy::absurd_extreme_comparisons)] // Not absurd on all OSes
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
@@ -80,13 +71,11 @@ fn assert_lstat_results(stat_result: Result<FileStat>) {
// On other platforms they are the same (either both are u16 or u32).
assert_eq!((stats.st_mode as usize) & (S_IFMT as usize), S_IFLNK as usize); // should be a link
assert_eq!(stats.st_nlink, 1); // there links created, must be 1
- assert!(valid_uid_gid(stats)); // must be positive integers
assert!(stats.st_size > 0); // size is > 0 because it points to another file
assert!(stats.st_blksize > 0); // must be positive integer, exact number machine dependent
// st_blocks depends on whether the machine's file system uses fast
// or slow symlinks, so just make sure it's not negative
- // (Android's st_blocks is ulonglong which is always non-negative.)
assert!(stats.st_blocks >= 0);
}
@@ -159,14 +148,14 @@ fn test_fchmod() {
fchmod(file.as_raw_fd(), mode1).unwrap();
let file_stat1 = stat(&filename).unwrap();
- assert_eq!(file_stat1.st_mode & 0o7777, mode1.bits());
+ assert_eq!(file_stat1.st_mode as mode_t & 0o7777, mode1.bits());
let mut mode2 = Mode::empty();
mode2.insert(Mode::S_IROTH);
fchmod(file.as_raw_fd(), mode2).unwrap();
let file_stat2 = stat(&filename).unwrap();
- assert_eq!(file_stat2.st_mode & 0o7777, mode2.bits());
+ assert_eq!(file_stat2.st_mode as mode_t & 0o7777, mode2.bits());
}
#[test]
@@ -186,7 +175,7 @@ fn test_fchmodat() {
fchmodat(Some(dirfd), filename, mode1, FchmodatFlags::FollowSymlink).unwrap();
let file_stat1 = stat(&fullpath).unwrap();
- assert_eq!(file_stat1.st_mode & 0o7777, mode1.bits());
+ assert_eq!(file_stat1.st_mode as mode_t & 0o7777, mode1.bits());
chdir(tempdir.path()).unwrap();
@@ -195,7 +184,7 @@ fn test_fchmodat() {
fchmodat(None, filename, mode2, FchmodatFlags::FollowSymlink).unwrap();
let file_stat2 = stat(&fullpath).unwrap();
- assert_eq!(file_stat2.st_mode & 0o7777, mode2.bits());
+ assert_eq!(file_stat2.st_mode as mode_t & 0o7777, mode2.bits());
}
/// Asserts that the atime and mtime in a file's metadata match expected values.
@@ -315,5 +304,55 @@ fn test_mkdirat_fail() {
let dirfd = fcntl::open(&tempdir.path().join(not_dir_filename), fcntl::OFlag::O_CREAT,
stat::Mode::empty()).unwrap();
let result = mkdirat(dirfd, filename, Mode::S_IRWXU).unwrap_err();
- assert_eq!(result, Error::Sys(Errno::ENOTDIR));
+ 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 @@ pub fn test_clock_getcpuclockid() {
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 16a8a05..61062ad 100644
--- a/test/test_unistd.rs
+++ b/test/test_unistd.rs
@@ -1,6 +1,6 @@
#[cfg(not(target_os = "redox"))]
use nix::fcntl::{self, open, readlink};
-use nix::fcntl::{fcntl, FcntlArg, FdFlag, OFlag};
+use nix::fcntl::OFlag;
use nix::unistd::*;
use nix::unistd::ForkResult::*;
#[cfg(not(target_os = "redox"))]
@@ -10,27 +10,25 @@ use nix::sys::stat::{self, Mode, SFlag};
#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
use nix::pty::{posix_openpt, grantpt, unlockpt, ptsname};
use nix::errno::Errno;
-#[cfg(not(target_os = "redox"))]
-use nix::Error;
-use std::{env, iter};
-#[cfg(not(target_os = "redox"))]
+use std::env;
+#[cfg(not(any(target_os = "fuchsia", target_os = "redox")))]
use std::ffi::CString;
#[cfg(not(target_os = "redox"))]
use std::fs::DirBuilder;
use std::fs::{self, File};
use std::io::Write;
use std::os::unix::prelude::*;
-#[cfg(not(target_os = "redox"))]
+#[cfg(not(any(target_os = "fuchsia", target_os = "redox")))]
use std::path::Path;
use tempfile::{tempdir, tempfile};
-use libc::{_exit, off_t};
+use libc::{_exit, mode_t, off_t};
use crate::*;
#[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") {
@@ -58,7 +56,7 @@ fn test_fork_and_waitpid() {
#[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") {
@@ -102,7 +100,7 @@ fn test_mkfifo() {
mkfifo(&mkfifo_fifo, Mode::S_IRUSR).unwrap();
let stats = stat::stat(&mkfifo_fifo).unwrap();
- let typ = stat::SFlag::from_bits_truncate(stats.st_mode);
+ let typ = stat::SFlag::from_bits_truncate(stats.st_mode as mode_t);
assert!(typ == SFlag::S_IFIFO);
}
@@ -118,7 +116,7 @@ fn test_mkfifo_directory() {
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");
@@ -135,6 +133,8 @@ fn test_mkfifoat_none() {
target_os = "macos", target_os = "ios",
target_os = "android", target_os = "redox")))]
fn test_mkfifoat() {
+ use nix::fcntl;
+
let tempdir = tempdir().unwrap();
let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
let mkfifoat_name = "mkfifoat_name";
@@ -151,10 +151,10 @@ fn test_mkfifoat() {
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 @@ fn test_mkfifoat_directory() {
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 @@ fn test_setgroups() {
// 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();
@@ -224,13 +224,17 @@ fn test_setgroups() {
#[test]
// `getgroups()` and `setgroups()` do not behave as expected on Apple platforms
-#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox", target_os = "fuchsia")))]
+#[cfg(not(any(target_os = "ios",
+ target_os = "macos",
+ target_os = "redox",
+ target_os = "fuchsia",
+ target_os = "illumos")))]
fn test_initgroups() {
// Skip this test when not run as root as `initgroups()` and `setgroups()`
// 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();
@@ -254,7 +258,7 @@ fn test_initgroups() {
setgroups(&old_groups).unwrap();
}
-#[cfg(not(target_os = "redox"))]
+#[cfg(not(any(target_os = "fuchsia", target_os = "redox")))]
macro_rules! execve_test_factory(
($test_name:ident, $syscall:ident, $exe: expr $(, $pathname:expr, $flags:expr)*) => (
@@ -300,7 +304,7 @@ macro_rules! execve_test_factory(
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();
@@ -366,10 +370,12 @@ cfg_if!{
execve_test_factory!(test_execve, execve, CString::new("/bin/sh").unwrap().as_c_str());
execve_test_factory!(test_fexecve, fexecve, File::open("/bin/sh").unwrap().into_raw_fd());
} else if #[cfg(any(target_os = "dragonfly",
+ target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
- target_os = "openbsd"))] {
+ target_os = "openbsd",
+ target_os = "solaris"))] {
execve_test_factory!(test_execve, execve, CString::new("/bin/sh").unwrap().as_c_str());
// No fexecve() on DragonFly, ios, macos, NetBSD, OpenBSD.
//
@@ -435,9 +441,9 @@ fn test_getcwd() {
// kicks in. Note: One path cannot be longer than 255 bytes
// (NAME_MAX) whole path cannot be longer than PATH_MAX (usually
// 4096 on linux, 1024 on macos)
- let mut inner_tmp_dir = tmpdir_path.to_path_buf();
+ 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());
}
@@ -543,7 +549,7 @@ cfg_if!{
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")] {
@@ -569,7 +575,7 @@ fn test_acct() {
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();
@@ -618,15 +624,34 @@ fn test_sysconf_unsupported() {
assert!(open_max.expect("sysconf failed").is_none())
}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[test]
+fn test_getresuid() {
+ let resuids = getresuid().unwrap();
+ assert!(resuids.real.as_raw() != libc::uid_t::max_value());
+ assert!(resuids.effective.as_raw() != libc::uid_t::max_value());
+ assert!(resuids.saved.as_raw() != libc::uid_t::max_value());
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[test]
+fn test_getresgid() {
+ let resgids = getresgid().unwrap();
+ assert!(resgids.real.as_raw() != libc::gid_t::max_value());
+ assert!(resgids.effective.as_raw() != libc::gid_t::max_value());
+ assert!(resgids.saved.as_raw() != libc::gid_t::max_value());
+}
+
// Test that we can create a pair of pipes. No need to verify that they pass
// data; that's the domain of the OS, not nix.
#[test]
fn test_pipe() {
let (fd0, fd1) = pipe().unwrap();
- let m0 = stat::SFlag::from_bits_truncate(stat::fstat(fd0).unwrap().st_mode);
+ let m0 = stat::SFlag::from_bits_truncate(stat::fstat(fd0).unwrap().st_mode as mode_t);
// S_IFIFO means it's a pipe
assert_eq!(m0, SFlag::S_IFIFO);
- let m1 = stat::SFlag::from_bits_truncate(stat::fstat(fd1).unwrap().st_mode);
+ let m1 = stat::SFlag::from_bits_truncate(stat::fstat(fd1).unwrap().st_mode as mode_t);
assert_eq!(m1, SFlag::S_IFIFO);
}
@@ -636,12 +661,16 @@ fn test_pipe() {
target_os = "dragonfly",
target_os = "emscripten",
target_os = "freebsd",
+ target_os = "illumos",
target_os = "linux",
target_os = "netbsd",
target_os = "openbsd",
- target_os = "redox"))]
+ target_os = "redox",
+ target_os = "solaris"))]
#[test]
fn test_pipe2() {
+ use nix::fcntl::{fcntl, FcntlArg, FdFlag};
+
let (fd0, fd1) = pipe2(OFlag::O_CLOEXEC).unwrap();
let f0 = FdFlag::from_bits_truncate(fcntl(fd0, FcntlArg::F_GETFD).unwrap());
assert!(f0.contains(FdFlag::FD_CLOEXEC));
@@ -706,7 +735,7 @@ fn test_alarm() {
};
// 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());
@@ -721,7 +750,7 @@ fn test_alarm() {
// Overwriting an alarm should return the old alarm.
assert_eq!(alarm::set(1), Some(60));
- // We should be woken up after 1 second by the alarm, so we'll sleep for 2
+ // We should be woken up after 1 second by the alarm, so we'll sleep for 3
// seconds to be sure.
let starttime = Instant::now();
loop {
@@ -744,7 +773,7 @@ fn test_alarm() {
#[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);
@@ -755,7 +784,7 @@ fn test_canceling_alarm() {
#[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();
@@ -854,7 +883,7 @@ fn test_linkat_newdirfd_none() {
#[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";
@@ -891,7 +920,7 @@ fn test_linkat_no_follow_symlink() {
#[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";
@@ -918,7 +947,9 @@ fn test_linkat_follow_symlink() {
let newfilestat = stat::stat(&newfilepath).unwrap();
// Check the file type of the new link
- assert!((stat::SFlag::from_bits_truncate(newfilestat.st_mode) & SFlag::S_IFMT) == SFlag::S_IFREG);
+ assert_eq!((stat::SFlag::from_bits_truncate(newfilestat.st_mode as mode_t) & SFlag::S_IFMT),
+ SFlag::S_IFREG
+ );
// Check the number of hard links to the original file
assert_eq!(newfilestat.st_nlink, 2);
@@ -939,7 +970,7 @@ fn test_unlinkat_dir_noremovedir() {
// Attempt unlink dir at relative path without proper flag
let err_result = unlinkat(Some(dirfd), dirname, UnlinkatFlags::NoRemoveDir).unwrap_err();
- assert!(err_result == Error::Sys(Errno::EISDIR) || err_result == Error::Sys(Errno::EPERM));
+ assert!(err_result == Errno::EISDIR || err_result == Errno::EPERM);
}
#[test]
@@ -982,7 +1013,7 @@ fn test_unlinkat_file() {
fn test_access_not_existing() {
let tempdir = tempdir().unwrap();
let dir = tempdir.path().join("does_not_exist.txt");
- assert_eq!(access(&dir, AccessFlags::F_OK).err().unwrap().as_errno().unwrap(),
+ assert_eq!(access(&dir, AccessFlags::F_OK).err().unwrap(),
Errno::ENOENT);
}
@@ -994,13 +1025,22 @@ fn test_access_file_exists() {
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();
@@ -1011,7 +1051,7 @@ fn test_setfsuid() {
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 || {
@@ -1061,13 +1101,13 @@ fn test_ttyname() {
fn test_ttyname_not_pty() {
let fd = File::open("/dev/zero").unwrap();
assert!(fd.as_raw_fd() > 0);
- assert_eq!(ttyname(fd.as_raw_fd()), Err(Error::Sys(Errno::ENOTTY)));
+ assert_eq!(ttyname(fd.as_raw_fd()), Err(Errno::ENOTTY));
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
fn test_ttyname_invalid_fd() {
- assert_eq!(ttyname(-1), Err(Error::Sys(Errno::EBADF)));
+ assert_eq!(ttyname(-1), Err(Errno::EBADF));
}
#[test]