diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-05-10 07:01:46 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-05-10 07:01:46 +0000 |
commit | 2cf7b893b8e6de214a0ca1802deaa408c4d76d19 (patch) | |
tree | fb2abcd53c7bec252defd92f2b61bb3ddff8949d | |
parent | fdb16c9d501c4b6752f13c07c14d288e077a55f0 (diff) | |
parent | 70ec7dceffbc665db8853a3b099f15eb84590c74 (diff) | |
download | nix-aml_per_331913010.tar.gz |
Snap for 8564071 from 70ec7dceffbc665db8853a3b099f15eb84590c74 to mainline-permission-releaseaml_per_331913010aml_per_331812030aml_per_331710050aml_per_331611010aml_per_331512020aml_per_331411000aml_per_331313010aml_per_331115020aml_per_331019040aml_per_330912010aml_per_330811030android13-mainline-permission-release
Change-Id: Ib24f785b0dcb518e15b71d419467229e4d744f1b
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 @@ -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 @@ -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" @@ -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 } } @@ -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 - @@ -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 } } @@ -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()) }; @@ -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, @@ -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<OSTOP) 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] |