diff options
author | Joel Galenson <jgalenson@google.com> | 2021-04-05 21:35:15 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2021-04-05 21:35:15 +0000 |
commit | 720f2e28efe9e2c2feec310517bcc1c91f8d8ec8 (patch) | |
tree | 26dcc75506c099ec150c6790879c824b6e4b1016 | |
parent | d0ce284484644b412e1f8e57be94f291d48362ec (diff) | |
parent | e0d64e1271234a1c646273626b7513d3189d767d (diff) | |
download | nix-720f2e28efe9e2c2feec310517bcc1c91f8d8ec8.tar.gz |
Upgrade rust/crates/nix to 0.20.0 am: 4727c11bc6 am: e0d64e1271
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/nix/+/1662521
Change-Id: I365944d87eb43e57860b0fe1cf546374a6fac124
52 files changed, 1140 insertions, 495 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index 35cd54b..82a8727 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,5 +1,5 @@ { "git": { - "sha1": "c8e8deff0e26a05165e66db17732794c2cd67324" + "sha1": "4c7021787a174493bf1abb90a711d7464e6c80f6" } } diff --git a/.cirrus.yml b/.cirrus.yml deleted file mode 100644 index 3591c0e..0000000 --- a/.cirrus.yml +++ /dev/null @@ -1,24 +0,0 @@ -freebsd_instance: - image: freebsd-11-4-release-amd64 - -# Test FreeBSD in a full VM on cirrus-ci.com. Test the i686 target too, in the -# same VM. The binary will be built in 32-bit mode, but will execute on a -# 64-bit kernel and in a 64-bit environment. Our tests don't execute any of -# the system's binaries, so the environment shouldn't matter. -task: - name: FreeBSD 11.4 - cargo_cache: - folder: $CARGO_HOME/registry - fingerprint_script: cat Cargo.lock || echo "" - # Install Rust - setup_script: - - fetch https://sh.rustup.rs -o rustup.sh - - sh rustup.sh -y --profile=minimal --default-toolchain 1.36.0 - - $HOME/.cargo/bin/rustup target add i686-unknown-freebsd - amd64_test_script: - - . $HOME/.cargo/env - - cargo test - i386_test_script: - - . $HOME/.cargo/env - - cargo test --target i686-unknown-freebsd - before_cache_script: rm -rf $CARGO_HOME/registry/index @@ -23,7 +23,6 @@ license { rust_library { name: "libnix", - // has rustc warnings host_supported: true, crate_name: "nix", srcs: ["src/lib.rs"], @@ -41,5 +40,5 @@ rust_library { // dependent_library ["feature_list"] // bitflags-1.2.1 "default" -// cfg-if-0.1.10 -// libc-0.2.89 "default,extra_traits,std" +// cfg-if-1.0.0 +// libc-0.2.92 "default,extra_traits,std" diff --git a/CHANGELOG.md b/CHANGELOG.md index e018ab2..1297ba7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,12 +3,49 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). -## [Unreleased] - ReleaseDate +## [0.20.0] - 20 February 2021 ### Added + +- Added a `passwd` field to `Group` (#[1338](https://github.com/nix-rust/nix/pull/1338)) +- Added `mremap` (#[1306](https://github.com/nix-rust/nix/pull/1306)) +- Added `personality` (#[1331](https://github.com/nix-rust/nix/pull/1331)) +- Added limited Fuchsia support (#[1285](https://github.com/nix-rust/nix/pull/1285)) +- Added `getpeereid` (#[1342](https://github.com/nix-rust/nix/pull/1342)) +- Implemented `IntoIterator` for `Dir` + (#[1333](https://github.com/nix-rust/nix/pull/1333)). + ### Changed + +- Minimum supported Rust version is now 1.40.0. + ([#1356](https://github.com/nix-rust/nix/pull/1356)) +- i686-apple-darwin has been demoted to Tier 2 support, because it's deprecated + by Xcode. + (#[1350](https://github.com/nix-rust/nix/pull/1350)) +- Fixed calling `recvfrom` on an `AddrFamily::Packet` socket + (#[1344](https://github.com/nix-rust/nix/pull/1344)) + ### Fixed +- `TimerFd` now closes the underlying fd on drop. + ([#1381](https://github.com/nix-rust/nix/pull/1381)) +- Define `*_MAGIC` filesystem constants on Linux s390x + (#[1372](https://github.com/nix-rust/nix/pull/1372)) +- mqueue, sysinfo, timespec, statfs, test_ptrace_syscall() on x32 + (#[1366](https://github.com/nix-rust/nix/pull/1366)) + ### Removed +- `Dir`, `SignalFd`, and `PtyMaster` are no longer `Clone`. + (#[1382](https://github.com/nix-rust/nix/pull/1382)) +- Removed `SockLevel`, which hasn't been used for a few years + (#[1362](https://github.com/nix-rust/nix/pull/1362)) +- Removed both `Copy` and `Clone` from `TimerFd`. + ([#1381](https://github.com/nix-rust/nix/pull/1381)) + +## [0.19.1] - 28 November 2020 +### Fixed +- Fixed bugs in `recvmmsg`. + (#[1341](https://github.com/nix-rust/nix/pull/1341)) + ## [0.19.0] - 6 October 2020 ### Added - Added Netlink protocol families to the `SockProtocol` enum @@ -52,7 +89,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Derived `Ord`, `PartialOrd` for `unistd::Pid` (#[1189](https://github.com/nix-rust/nix/pull/1189)) - Added `select::FdSet::fds` method to iterate over file descriptors in a set. ([#1207](https://github.com/nix-rust/nix/pull/1207)) -- Added support for UDP generic segmentation offload (GSO) and generic +- Added support for UDP generic segmentation offload (GSO) and generic receive offload (GRO) ([#1209](https://github.com/nix-rust/nix/pull/1209)) - Added support for `sendmmsg` and `recvmmsg` calls (#[1208](https://github.com/nix-rust/nix/pull/1208)) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 03a1f63..55990c4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -76,21 +76,21 @@ 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 -Travis-CI][travis-ci], which might find some issues on other platforms. The CI +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]. -[travis-ci]: https://travis-ci.org/nix-rust/nix +[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 `#[cfg_attr(travis, ignore)]` -to it. Please include a comment describing the reason it shouldn't run -under CI, and a link to an upstream issue if possible! +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 @@ -13,13 +13,15 @@ [package] edition = "2018" name = "nix" -version = "0.19.0" +version = "0.20.0" authors = ["The nix-rust Project Developers"] -exclude = ["/.gitignore", "/.travis.yml", "/ci/*", "/Cross.toml", "/RELEASE_PROCEDURE.md", "/bors.toml"] +exclude = ["/.gitignore", "/.cirrus.yml", "/ci/*", "/Cross.toml", "/RELEASE_PROCEDURE.md", "/bors.toml"] 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"] [[test]] name = "test" @@ -49,10 +51,10 @@ path = "test/test_ptymaster_drop.rs" version = "1.1" [dependencies.cfg-if] -version = "0.1.10" +version = "1.0" [dependencies.libc] -version = "0.2.78" +version = "0.2.82" features = ["extra_traits"] [dev-dependencies.bytes] version = "0.4.8" @@ -69,7 +71,7 @@ version = "0.9.0" [dev-dependencies.tempfile] version = "3.0.5" [target."cfg(any(target_os = \"android\", target_os = \"linux\"))".dev-dependencies.caps] -version = "0.3.1" +version = "0.5.1" [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 31c3d71..885fa10 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -2,24 +2,38 @@ name = "nix" description = "Rust friendly bindings to *nix APIs" edition = "2018" -version = "0.19.0" +version = "0.20.0" authors = ["The nix-rust Project Developers"] repository = "https://github.com/nix-rust/nix" license = "MIT" categories = ["os::unix-apis"] exclude = [ "/.gitignore", - "/.travis.yml", + "/.cirrus.yml", "/ci/*", "/Cross.toml", "/RELEASE_PROCEDURE.md", "/bors.toml" ] +[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" +] + [dependencies] -libc = { version = "0.2.78", features = [ "extra_traits" ] } +libc = { version = "0.2.82", features = [ "extra_traits" ] } bitflags = "1.1" -cfg-if = "0.1.10" +cfg-if = "1.0" [target.'cfg(target_os = "dragonfly")'.build-dependencies] cc = "1" @@ -32,7 +46,7 @@ tempfile = "3.0.5" semver = "0.9.0" [target.'cfg(any(target_os = "android", target_os = "linux"))'.dev-dependencies] -caps = "0.3.1" +caps = "0.5.1" [target.'cfg(target_os = "freebsd")'.dev-dependencies] sysctl = "0.1" @@ -7,13 +7,13 @@ third_party { } url { type: ARCHIVE - value: "https://static.crates.io/crates/nix/nix-0.19.0.crate" + value: "https://static.crates.io/crates/nix/nix-0.20.0.crate" } - version: "0.19.0" + version: "0.20.0" license_type: NOTICE last_upgrade_date { - year: 2020 - month: 10 - day: 13 + year: 2021 + month: 4 + day: 2 } } @@ -1,6 +1,5 @@ # Rust bindings to *nix APIs -[![Travis Build Status](https://travis-ci.org/nix-rust/nix.svg?branch=master)](https://travis-ci.org/nix-rust/nix) [![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) @@ -51,7 +50,6 @@ Tier 1: * aarch64-unknown-linux-gnu * arm-unknown-linux-gnueabi * armv7-unknown-linux-gnueabihf - * i686-apple-darwin * i686-unknown-freebsd * i686-unknown-linux-gnu * i686-unknown-linux-musl @@ -74,6 +72,7 @@ Tier 2: * armv7-linux-androideabi * armv7s-apple-ios * i386-apple-ios + * i686-apple-darwin * i686-linux-android * powerpc-unknown-linux-gnu * s390x-unknown-linux-gnu @@ -82,17 +81,19 @@ Tier 2: * x86_64-unknown-netbsd Tier 3: + * x86_64-fuchsia * x86_64-unknown-redox + * x86_64-unknown-linux-gnux32 ## Usage -`nix` requires Rust 1.36.0 or newer. +`nix` requires Rust 1.40.0 or newer. To use `nix`, add this to your `Cargo.toml`: ```toml [dependencies] -nix = "0.19.0" +nix = "0.20.0" ``` ## Contributing diff --git a/TEST_MAPPING b/TEST_MAPPING index 7397f01..5d7fef5 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -2,13 +2,13 @@ { "presubmit": [ { - "name": "keystore2_crypto_test_rust" + "name": "keystore2_test" }, { - "name": "vpnprofilestore_test" + "name": "keystore2_crypto_test_rust" }, { - "name": "keystore2_test" + "name": "vpnprofilestore_test" } ] } @@ -25,7 +25,7 @@ use libc::{dirent, readdir_r}; /// * returns entries for `.` (current directory) and `..` (parent directory). /// * returns entries' names as a `CStr` (no allocation or conversion beyond whatever libc /// does). -#[derive(Clone, Debug, Eq, Hash, PartialEq)] +#[derive(Debug, Eq, Hash, PartialEq)] pub struct Dir( ptr::NonNull<libc::DIR> ); @@ -85,7 +85,32 @@ impl AsRawFd for Dir { impl Drop for Dir { fn drop(&mut self) { - unsafe { libc::closedir(self.0.as_ptr()) }; + let e = Errno::result(unsafe { libc::closedir(self.0.as_ptr()) }); + if !std::thread::panicking() && e == Err(Error::Sys(Errno::EBADF)) { + panic!("Closing an invalid file descriptor!"); + }; + } +} + +fn next(dir: &mut Dir) -> Option<Result<Entry>> { + unsafe { + // Note: POSIX specifies that portable applications should dynamically allocate a + // buffer with room for a `d_name` field of size `pathconf(..., _PC_NAME_MAX)` plus 1 + // for the NUL byte. It doesn't look like the std library does this; it just uses + // fixed-sized buffers (and libc's dirent seems to be sized so this is appropriate). + // Probably fine here too then. + let mut ent = std::mem::MaybeUninit::<dirent>::uninit(); + let mut result = ptr::null_mut(); + if let Err(e) = Errno::result( + readdir_r(dir.0.as_ptr(), ent.as_mut_ptr(), &mut result)) + { + return Some(Err(e)); + } + if result.is_null() { + return None; + } + assert_eq!(result, ent.as_mut_ptr()); + Some(Ok(Entry(ent.assume_init()))) } } @@ -96,25 +121,7 @@ impl<'d> Iterator for Iter<'d> { type Item = Result<Entry>; fn next(&mut self) -> Option<Self::Item> { - unsafe { - // Note: POSIX specifies that portable applications should dynamically allocate a - // buffer with room for a `d_name` field of size `pathconf(..., _PC_NAME_MAX)` plus 1 - // for the NUL byte. It doesn't look like the std library does this; it just uses - // fixed-sized buffers (and libc's dirent seems to be sized so this is appropriate). - // Probably fine here too then. - let mut ent = std::mem::MaybeUninit::<dirent>::uninit(); - let mut result = ptr::null_mut(); - if let Err(e) = Errno::result( - readdir_r((self.0).0.as_ptr(), ent.as_mut_ptr(), &mut result)) - { - return Some(Err(e)); - } - if result.is_null() { - return None; - } - assert_eq!(result, ent.as_mut_ptr()); - Some(Ok(Entry(ent.assume_init()))) - } + next(self.0) } } @@ -124,6 +131,43 @@ impl<'d> Drop for Iter<'d> { } } +/// The return type of [Dir::into_iter] +#[derive(Debug, Eq, Hash, PartialEq)] +pub struct OwningIter(Dir); + +impl Iterator for OwningIter { + type Item = Result<Entry>; + + fn next(&mut self) -> Option<Self::Item> { + next(&mut self.0) + } +} + +impl IntoIterator for Dir { + type Item = Result<Entry>; + type IntoIter = OwningIter; + + /// Creates a owning iterator, that is, one that takes ownership of the + /// `Dir`. The `Dir` cannot be used after calling this. This can be useful + /// when you have a function that both creates a `Dir` instance and returns + /// an `Iterator`. + /// + /// Example: + /// + /// ``` + /// use nix::{dir::Dir, fcntl::OFlag, sys::stat::Mode}; + /// use std::{iter::Iterator, string::String}; + /// + /// fn ls_upper(dirname: &str) -> impl Iterator<Item=String> { + /// let d = Dir::open(dirname, OFlag::O_DIRECTORY, Mode::S_IXUSR).unwrap(); + /// d.into_iter().map(|x| x.unwrap().file_name().as_ref().to_string_lossy().to_ascii_uppercase()) + /// } + /// ``` + fn into_iter(self) -> Self::IntoIter { + OwningIter(self) + } +} + /// A directory entry, similar to `std::fs::DirEntry`. /// /// Note that unlike the std version, this may represent the `.` or `..` entries. diff --git a/src/errno.rs b/src/errno.rs index 03a7f0e..e5c7092 100644 --- a/src/errno.rs +++ b/src/errno.rs @@ -20,7 +20,8 @@ cfg_if! { } } else if #[cfg(any(target_os = "linux", target_os = "redox", - target_os = "dragonfly"))] { + target_os = "dragonfly", + target_os = "fuchsia"))] { unsafe fn errno_location() -> *mut c_int { libc::__errno_location() } @@ -188,192 +189,254 @@ fn desc(errno: Errno) -> &'static str { EHOSTDOWN => "Host is down", EHOSTUNREACH => "No route to host", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] ECHRNG => "Channel number out of range", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] EL2NSYNC => "Level 2 not synchronized", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] EL3HLT => "Level 3 halted", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] EL3RST => "Level 3 reset", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] ELNRNG => "Link number out of range", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] EUNATCH => "Protocol driver not attached", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] ENOCSI => "No CSI structure available", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] EL2HLT => "Level 2 halted", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] EBADE => "Invalid exchange", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] EBADR => "Invalid request descriptor", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] EXFULL => "Exchange full", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] ENOANO => "No anode", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] EBADRQC => "Invalid request code", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] EBADSLT => "Invalid slot", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] EBFONT => "Bad font file format", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] ENOSTR => "Device not a stream", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] ENODATA => "No data available", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] ETIME => "Timer expired", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] ENOSR => "Out of streams resources", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] ENONET => "Machine is not on the network", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] ENOPKG => "Package not installed", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] EREMOTE => "Object is remote", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] ENOLINK => "Link has been severed", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] EADV => "Advertise error", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] ESRMNT => "Srmount error", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] ECOMM => "Communication error on send", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] EPROTO => "Protocol error", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] EMULTIHOP => "Multihop attempted", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] EDOTDOT => "RFS specific error", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] EBADMSG => "Not a data message", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[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"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] ENOTUNIQ => "Name not unique on network", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] EBADFD => "File descriptor in bad state", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] EREMCHG => "Remote address changed", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] ELIBACC => "Can not access a needed shared library", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] ELIBBAD => "Accessing a corrupted shared library", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] ELIBSCN => ".lib section in a.out corrupted", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] ELIBMAX => "Attempting to link in too many shared libraries", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] ELIBEXEC => "Cannot exec a shared library directly", - #[cfg(any(target_os = "linux", target_os = "android", target_os = "openbsd"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia", target_os = "openbsd"))] EILSEQ => "Illegal byte sequence", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] ERESTART => "Interrupted system call should be restarted", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] ESTRPIPE => "Streams pipe error", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] EUSERS => "Too many users", #[cfg(any(target_os = "linux", target_os = "android", - target_os = "netbsd", target_os = "redox"))] + target_os = "fuchsia", target_os = "netbsd", + target_os = "redox"))] EOPNOTSUPP => "Operation not supported on transport endpoint", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] ESTALE => "Stale file handle", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] EUCLEAN => "Structure needs cleaning", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] ENOTNAM => "Not a XENIX named type file", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] ENAVAIL => "No XENIX semaphores available", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] EISNAM => "Is a named type file", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] EREMOTEIO => "Remote I/O error", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] EDQUOT => "Quota exceeded", #[cfg(any(target_os = "linux", target_os = "android", - target_os = "openbsd", target_os = "dragonfly"))] + target_os = "fuchsia", target_os = "openbsd", + target_os = "dragonfly"))] ENOMEDIUM => "No medium found", - #[cfg(any(target_os = "linux", target_os = "android", target_os = "openbsd"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia", target_os = "openbsd"))] EMEDIUMTYPE => "Wrong medium type", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] ECANCELED => "Operation canceled", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] ENOKEY => "Required key not available", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] EKEYEXPIRED => "Key has expired", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] EKEYREVOKED => "Key has been revoked", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] EKEYREJECTED => "Key was rejected by service", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] EOWNERDEAD => "Owner died", - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] ENOTRECOVERABLE => "State not recoverable", - #[cfg(all(target_os = "linux", not(target_arch="mips")))] + #[cfg(any(all(target_os = "linux", not(target_arch="mips")), + target_os = "fuchsia"))] ERFKILL => "Operation not possible due to RF-kill", - #[cfg(all(target_os = "linux", not(target_arch="mips")))] + #[cfg(any(all(target_os = "linux", not(target_arch="mips")), + target_os = "fuchsia"))] EHWPOISON => "Memory page has hardware error", #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] @@ -567,7 +630,8 @@ fn desc(errno: Errno) -> &'static str { } } -#[cfg(any(target_os = "linux", target_os = "android"))] +#[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] mod consts { #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[repr(i32)] diff --git a/src/fcntl.rs b/src/fcntl.rs index 1581d3a..d2242da 100644 --- a/src/fcntl.rs +++ b/src/fcntl.rs @@ -20,7 +20,7 @@ use crate::sys::uio::IoVec; // For vmsplice target_os = "fuchsia", any(target_os = "wasi", target_env = "wasi"), target_env = "uclibc", - target_env = "freebsd" + target_os = "freebsd" ))] pub use self::posix_fadvise::*; @@ -587,7 +587,7 @@ pub fn fallocate( target_os = "fuchsia", any(target_os = "wasi", target_env = "wasi"), target_env = "uclibc", - target_env = "freebsd" + target_os = "freebsd" ))] mod posix_fadvise { use crate::errno::Errno; diff --git a/src/features.rs b/src/features.rs index c3a53fb..6b1cff5 100644 --- a/src/features.rs +++ b/src/features.rs @@ -97,7 +97,7 @@ 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 = "redox", target_os = "fuchsia"))] mod os { /// Check if the OS supports atomic close-on-exec for sockets pub fn socket_atomic_cloexec() -> bool { @@ -57,7 +57,7 @@ pub mod net; #[deny(missing_docs)] pub mod poll; #[deny(missing_docs)] -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))] pub mod pty; pub mod sched; pub mod sys; diff --git a/src/mqueue.rs b/src/mqueue.rs index 122705a..0215de5 100644 --- a/src/mqueue.rs +++ b/src/mqueue.rs @@ -5,7 +5,7 @@ use crate::Result; use crate::errno::Errno; -use libc::{self, c_char, c_long, mqd_t, size_t}; +use libc::{self, c_char, mqd_t, size_t}; use std::ffi::CString; use crate::sys::stat::Mode; use std::mem; @@ -34,11 +34,18 @@ pub struct MqAttr { mq_attr: libc::mq_attr, } +// x32 compatibility +// See https://sourceware.org/bugzilla/show_bug.cgi?id=21279 +#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))] +pub type mq_attr_member_t = i64; +#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))] +pub type mq_attr_member_t = libc::c_long; + impl MqAttr { - pub fn new(mq_flags: c_long, - mq_maxmsg: c_long, - mq_msgsize: c_long, - mq_curmsgs: c_long) + pub fn new(mq_flags: mq_attr_member_t, + mq_maxmsg: mq_attr_member_t, + mq_msgsize: mq_attr_member_t, + mq_curmsgs: mq_attr_member_t) -> MqAttr { let mut attr = mem::MaybeUninit::<libc::mq_attr>::uninit(); @@ -52,7 +59,7 @@ impl MqAttr { } } - pub fn flags(&self) -> c_long { + pub fn flags(&self) -> mq_attr_member_t { self.mq_attr.mq_flags } } @@ -150,7 +157,7 @@ pub fn mq_setattr(mqd: mqd_t, newattr: &MqAttr) -> Result<MqAttr> { /// Returns the old attributes pub fn mq_set_nonblock(mqd: mqd_t) -> Result<MqAttr> { let oldattr = mq_getattr(mqd)?; - let newattr = MqAttr::new(c_long::from(MQ_OFlag::O_NONBLOCK.bits()), + let newattr = MqAttr::new(mq_attr_member_t::from(MQ_OFlag::O_NONBLOCK.bits()), oldattr.mq_attr.mq_maxmsg, oldattr.mq_attr.mq_msgsize, oldattr.mq_attr.mq_curmsgs); @@ -43,7 +43,7 @@ pub struct ForkptyResult { /// While this datatype is a thin wrapper around `RawFd`, it enforces that the available PTY /// functions are given the correct file descriptor. Additionally this type implements `Drop`, /// so that when it's consumed or goes out of scope, it's automatically cleaned-up. -#[derive(Clone, Debug, Eq, Hash, PartialEq)] +#[derive(Debug, Eq, Hash, PartialEq)] pub struct PtyMaster(RawFd); impl AsRawFd for PtyMaster { diff --git a/src/sys/mman.rs b/src/sys/mman.rs index b2bed6e..63a0779 100644 --- a/src/sys/mman.rs +++ b/src/sys/mman.rs @@ -139,6 +139,17 @@ libc_bitflags!{ } } +#[cfg(target_os = "linux")] +libc_bitflags!{ + /// Options for `mremap()`. + pub struct MRemapFlags: c_int { + /// Permit the kernel to relocate the mapping to a new virtual address, if necessary. + MREMAP_MAYMOVE; + /// Place the mapping at exactly the address specified in `new_address`. + MREMAP_FIXED; + } +} + libc_enum!{ /// Usage information for a range of memory to allow for performance optimizations by the kernel. /// @@ -315,6 +326,30 @@ pub unsafe fn mmap(addr: *mut c_void, length: size_t, prot: ProtFlags, flags: Ma } } +/// Expands (or shrinks) an existing memory mapping, potentially moving it at +/// the same time. +/// +/// # Safety +/// +/// See the `mremap(2)` [man page](https://man7.org/linux/man-pages/man2/mremap.2.html) for +/// detailed requirements. +#[cfg(target_os = "linux")] +pub unsafe fn mremap( + addr: *mut c_void, + old_size: size_t, + new_size: size_t, + flags: MRemapFlags, + new_address: Option<* mut c_void>, +) -> Result<*mut c_void> { + let ret = libc::mremap(addr, old_size, new_size, flags.bits(), new_address.unwrap_or(std::ptr::null_mut())); + + if ret == libc::MAP_FAILED { + Err(Error::Sys(Errno::last())) + } else { + Ok(ret) + } +} + /// remove a mapping /// /// # Safety diff --git a/src/sys/mod.rs b/src/sys/mod.rs index b8b9e6f..02edfd7 100644 --- a/src/sys/mod.rs +++ b/src/sys/mod.rs @@ -38,6 +38,9 @@ pub mod memfd; #[cfg(not(target_os = "redox"))] pub mod mman; +#[cfg(target_os = "linux")] +pub mod personality; + pub mod pthread; #[cfg(any(target_os = "android", diff --git a/src/sys/personality.rs b/src/sys/personality.rs new file mode 100644 index 0000000..6548b65 --- /dev/null +++ b/src/sys/personality.rs @@ -0,0 +1,70 @@ +use crate::Result; +use crate::errno::Errno; + +use libc::{self, c_int, c_ulong}; + +libc_bitflags! { + /// Flags used and returned by [`get()`](fn.get.html) and + /// [`set()`](fn.set.html). + pub struct Persona: c_int { + ADDR_COMPAT_LAYOUT; + ADDR_NO_RANDOMIZE; + ADDR_LIMIT_32BIT; + ADDR_LIMIT_3GB; + #[cfg(not(target_env = "musl"))] + FDPIC_FUNCPTRS; + MMAP_PAGE_ZERO; + READ_IMPLIES_EXEC; + SHORT_INODE; + STICKY_TIMEOUTS; + #[cfg(not(target_env = "musl"))] + UNAME26; + WHOLE_SECONDS; + } +} + +/// Retrieve the current process personality. +/// +/// Returns a Result containing a Persona instance. +/// +/// Example: +/// +/// ``` +/// # use nix::sys::personality::{self, Persona}; +/// let pers = personality::get().unwrap(); +/// assert!(!pers.contains(Persona::WHOLE_SECONDS)); +/// ``` +pub fn get() -> Result<Persona> { + let res = unsafe { + libc::personality(0xFFFFFFFF) + }; + + Errno::result(res).map(|r| Persona::from_bits_truncate(r)) +} + +/// Set the current process personality. +/// +/// Returns a Result containing the *previous* personality for the +/// process, as a Persona. +/// +/// For more information, see [personality(2)](https://man7.org/linux/man-pages/man2/personality.2.html) +/// +/// **NOTE**: This call **replaces** the current personality entirely. +/// To **update** the personality, first call `get()` and then `set()` +/// with the modified persona. +/// +/// Example: +/// +/// ``` +/// # use nix::sys::personality::{self, Persona}; +/// let mut pers = personality::get().unwrap(); +/// assert!(!pers.contains(Persona::ADDR_NO_RANDOMIZE)); +/// personality::set(pers | Persona::ADDR_NO_RANDOMIZE); +/// ``` +pub fn set(persona: Persona) -> Result<Persona> { + let res = unsafe { + libc::personality(persona.bits() as c_ulong) + }; + + Errno::result(res).map(|r| Persona::from_bits_truncate(r)) +} diff --git a/src/sys/signal.rs b/src/sys/signal.rs index 710e65f..2f8b5fa 100644 --- a/src/sys/signal.rs +++ b/src/sys/signal.rs @@ -39,8 +39,10 @@ libc_enum!{ SIGPIPE, SIGALRM, SIGTERM, - #[cfg(all(any(target_os = "android", target_os = "emscripten", target_os = "linux"), - not(any(target_arch = "mips", target_arch = "mips64", target_arch = "sparc64"))))] + #[cfg(all(any(target_os = "android", target_os = "emscripten", + target_os = "fuchsia", target_os = "linux"), + not(any(target_arch = "mips", target_arch = "mips64", + target_arch = "sparc64"))))] SIGSTKFLT, SIGCHLD, SIGCONT, @@ -55,14 +57,17 @@ libc_enum!{ SIGPROF, SIGWINCH, SIGIO, - #[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))] + #[cfg(any(target_os = "android", target_os = "emscripten", + target_os = "fuchsia", target_os = "linux"))] SIGPWR, SIGSYS, #[cfg(not(any(target_os = "android", target_os = "emscripten", - target_os = "linux", target_os = "redox")))] + target_os = "fuchsia", target_os = "linux", + target_os = "redox")))] SIGEMT, #[cfg(not(any(target_os = "android", target_os = "emscripten", - target_os = "linux", target_os = "redox")))] + target_os = "fuchsia", target_os = "linux", + target_os = "redox")))] SIGINFO, } } @@ -86,8 +91,10 @@ impl FromStr for Signal { "SIGPIPE" => Signal::SIGPIPE, "SIGALRM" => Signal::SIGALRM, "SIGTERM" => Signal::SIGTERM, - #[cfg(all(any(target_os = "android", target_os = "emscripten", target_os = "linux"), - not(any(target_arch = "mips", target_arch = "mips64", target_arch = "sparc64"))))] + #[cfg(all(any(target_os = "android", target_os = "emscripten", + target_os = "fuchsia", target_os = "linux"), + not(any(target_arch = "mips", target_arch = "mips64", + target_arch = "sparc64"))))] "SIGSTKFLT" => Signal::SIGSTKFLT, "SIGCHLD" => Signal::SIGCHLD, "SIGCONT" => Signal::SIGCONT, @@ -102,14 +109,17 @@ impl FromStr for Signal { "SIGPROF" => Signal::SIGPROF, "SIGWINCH" => Signal::SIGWINCH, "SIGIO" => Signal::SIGIO, - #[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))] + #[cfg(any(target_os = "android", target_os = "emscripten", + target_os = "fuchsia", target_os = "linux"))] "SIGPWR" => Signal::SIGPWR, "SIGSYS" => Signal::SIGSYS, #[cfg(not(any(target_os = "android", target_os = "emscripten", - target_os = "linux", target_os = "redox")))] + target_os = "fuchsia", target_os = "linux", + target_os = "redox")))] "SIGEMT" => Signal::SIGEMT, #[cfg(not(any(target_os = "android", target_os = "emscripten", - target_os = "linux", target_os = "redox")))] + target_os = "fuchsia", target_os = "linux", + target_os = "redox")))] "SIGINFO" => Signal::SIGINFO, _ => return Err(Error::invalid_argument()), }) @@ -139,7 +149,8 @@ impl Signal { Signal::SIGPIPE => "SIGPIPE", Signal::SIGALRM => "SIGALRM", Signal::SIGTERM => "SIGTERM", - #[cfg(all(any(target_os = "android", target_os = "emscripten", target_os = "linux"), + #[cfg(all(any(target_os = "android", target_os = "emscripten", + target_os = "fuchsia", target_os = "linux"), not(any(target_arch = "mips", target_arch = "mips64", target_arch = "sparc64"))))] Signal::SIGSTKFLT => "SIGSTKFLT", Signal::SIGCHLD => "SIGCHLD", @@ -155,14 +166,17 @@ impl Signal { Signal::SIGPROF => "SIGPROF", Signal::SIGWINCH => "SIGWINCH", Signal::SIGIO => "SIGIO", - #[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))] + #[cfg(any(target_os = "android", target_os = "emscripten", + target_os = "fuchsia", target_os = "linux"))] Signal::SIGPWR => "SIGPWR", Signal::SIGSYS => "SIGSYS", #[cfg(not(any(target_os = "android", target_os = "emscripten", - target_os = "linux", target_os = "redox")))] + target_os = "fuchsia", target_os = "linux", + target_os = "redox")))] Signal::SIGEMT => "SIGEMT", #[cfg(not(any(target_os = "android", target_os = "emscripten", - target_os = "linux", target_os = "redox")))] + target_os = "fuchsia", target_os = "linux", + target_os = "redox")))] Signal::SIGINFO => "SIGINFO", } } @@ -213,7 +227,10 @@ const SIGNALS: [Signal; 29] = [ SIGWINCH, SIGIO, SIGSYS]; -#[cfg(all(any(target_os = "linux", target_os = "android", target_os = "emscripten"), not(any(target_arch = "mips", target_arch = "mips64", target_arch = "sparc64"))))] +#[cfg(all(any(target_os = "linux", target_os = "android", + target_os = "emscripten", target_os = "fuchsia"), + not(any(target_arch = "mips", target_arch = "mips64", + target_arch = "sparc64"))))] const SIGNALS: [Signal; 31] = [ SIGHUP, SIGINT, @@ -246,7 +263,10 @@ const SIGNALS: [Signal; 31] = [ SIGIO, SIGPWR, SIGSYS]; -#[cfg(all(any(target_os = "linux", target_os = "android", target_os = "emscripten"), any(target_arch = "mips", target_arch = "mips64", target_arch = "sparc64")))] +#[cfg(all(any(target_os = "linux", target_os = "android", + target_os = "emscripten", target_os = "fuchsia"), + any(target_arch = "mips", target_arch = "mips64", + target_arch = "sparc64")))] const SIGNALS: [Signal; 30] = [ SIGHUP, SIGINT, @@ -279,7 +299,8 @@ const SIGNALS: [Signal; 30] = [ SIGPWR, SIGSYS]; #[cfg(not(any(target_os = "linux", target_os = "android", - target_os = "emscripten", target_os = "redox")))] + target_os = "fuchsia", target_os = "emscripten", + target_os = "redox")))] const SIGNALS: [Signal; 31] = [ SIGHUP, SIGINT, @@ -749,6 +770,7 @@ pub fn kill<T: Into<Option<Signal>>>(pid: Pid, signal: T) -> Result<()> { /// 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. +#[cfg(not(target_os = "fuchsia"))] pub fn killpg<T: Into<Option<Signal>>>(pgrp: Pid, signal: T) -> Result<()> { let res = unsafe { libc::killpg(pgrp.into(), match signal.into() { @@ -829,7 +851,10 @@ mod sigevent { /// `SIGEV_SIGNAL`. That field is part of a union that shares space with the /// more genuinely useful `sigev_notify_thread_id` pub fn new(sigev_notify: SigevNotify) -> SigEvent { - let mut sev = unsafe { mem::zeroed::<libc::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, SigevNotify::SigevSignal{..} => libc::SIGEV_SIGNAL, diff --git a/src/sys/signalfd.rs b/src/sys/signalfd.rs index e3ded1f..c43b450 100644 --- a/src/sys/signalfd.rs +++ b/src/sys/signalfd.rs @@ -79,7 +79,7 @@ pub fn signalfd(fd: RawFd, mask: &SigSet, flags: SfdFlags) -> Result<RawFd> { /// Err(err) => (), // some error happend /// } /// ``` -#[derive(Clone, Debug, Eq, Hash, PartialEq)] +#[derive(Debug, Eq, Hash, PartialEq)] pub struct SignalFd(RawFd); impl SignalFd { @@ -116,7 +116,10 @@ impl SignalFd { impl Drop for SignalFd { fn drop(&mut self) { - let _ = unistd::close(self.0); + let e = unistd::close(self.0); + if !std::thread::panicking() && e == Err(Error::Sys(Errno::EBADF)) { + panic!("Closing an invalid file descriptor!"); + }; } } diff --git a/src/sys/socket/addr.rs b/src/sys/socket/addr.rs index 8784a37..2299c57 100644 --- a/src/sys/socket/addr.rs +++ b/src/sys/socket/addr.rs @@ -21,7 +21,8 @@ use crate::sys::socket::addr::sys_control::SysControlAddr; target_os = "linux", target_os = "macos", target_os = "netbsd", - target_os = "openbsd"))] + target_os = "openbsd", + target_os = "fuchsia"))] pub use self::datalink::LinkAddr; #[cfg(any(target_os = "android", target_os = "linux"))] pub use self::vsock::VsockAddr; @@ -41,7 +42,7 @@ pub enum AddressFamily { #[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"))] + #[cfg(any(target_os = "android", target_os = "linux", target_os = "fuchsia"))] Packet = libc::AF_PACKET, /// KEXT Controls and Notifications #[cfg(any(target_os = "ios", target_os = "macos"))] @@ -718,6 +719,7 @@ impl SockAddr { /// /// unsafe because it takes a raw pointer as argument. The caller must /// ensure that the pointer is valid. + #[cfg(not(target_os = "fuchsia"))] pub(crate) unsafe fn from_libc_sockaddr(addr: *const libc::sockaddr) -> Option<SockAddr> { if addr.is_null() { None @@ -1045,7 +1047,7 @@ pub mod sys_control { } -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(any(target_os = "android", target_os = "linux", target_os = "fuchsia"))] mod datalink { use super::{fmt, AddressFamily}; diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs index 5e5fb8d..11ed329 100644 --- a/src/sys/socket/mod.rs +++ b/src/sys/socket/mod.rs @@ -847,12 +847,13 @@ impl<'a> ControlMessage<'a> { } #[cfg(any(target_os = "android", target_os = "linux"))] ControlMessage::AlgSetIv(iv) => { + #[allow(deprecated)] // https://github.com/rust-lang/libc/issues/1501 let af_alg_iv = libc::af_alg_iv { ivlen: iv.len() as u32, iv: [0u8; 0], }; - let size = mem::size_of::<libc::af_alg_iv>(); + let size = mem::size_of_val(&af_alg_iv); unsafe { ptr::copy_nonoverlapping( @@ -915,7 +916,7 @@ impl<'a> ControlMessage<'a> { } #[cfg(any(target_os = "android", target_os = "linux"))] ControlMessage::AlgSetIv(iv) => { - mem::size_of::<libc::af_alg_iv>() + iv.len() + mem::size_of_val(&iv) + iv.len() }, #[cfg(any(target_os = "android", target_os = "linux"))] ControlMessage::AlgSetOp(op) => { @@ -1216,17 +1217,18 @@ pub fn recvmmsg<'a, I>( let ret = unsafe { libc::recvmmsg(fd, output.as_mut_ptr(), output.len() as _, flags.bits() as _, timeout) }; - let r = Errno::result(ret)?; + let _ = Errno::result(ret)?; Ok(output .into_iter() + .take(ret as usize) .zip(addresses.iter().map(|addr| unsafe{addr.assume_init()})) .zip(results.into_iter()) .map(|((mmsghdr, address), (msg_controllen, cmsg_buffer))| { unsafe { read_mhdr( mmsghdr.msg_hdr, - r as isize, + mmsghdr.msg_len as isize, msg_controllen, address, cmsg_buffer @@ -1574,24 +1576,6 @@ pub fn send(fd: RawFd, buf: &[u8], flags: MsgFlags) -> Result<usize> { * */ -/// The protocol level at which to get / set socket options. Used as an -/// argument to `getsockopt` and `setsockopt`. -/// -/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/setsockopt.html) -#[repr(i32)] -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum SockLevel { - Socket = libc::SOL_SOCKET, - Tcp = libc::IPPROTO_TCP, - Ip = libc::IPPROTO_IP, - Ipv6 = libc::IPPROTO_IPV6, - Udp = libc::IPPROTO_UDP, - #[cfg(any(target_os = "android", target_os = "linux"))] - Netlink = libc::SOL_NETLINK, - #[cfg(any(target_os = "android", target_os = "linux"))] - Alg = libc::SOL_ALG, -} - /// Represents a socket option that can be accessed or set. Used as an argument /// to `getsockopt` pub trait GetSockOpt : Copy { @@ -1718,6 +1702,15 @@ pub fn sockaddr_storage_to_addr( Ok(SockAddr::Unix(UnixAddr(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>()); + let sll = unsafe { + *(addr as *const _ as *const sockaddr_ll) + }; + Ok(SockAddr::Link(LinkAddr(sll))) + } + #[cfg(any(target_os = "android", target_os = "linux"))] libc::AF_NETLINK => { use libc::sockaddr_nl; let snl = unsafe { diff --git a/src/sys/socket/sockopt.rs b/src/sys/socket/sockopt.rs index e41a472..5b7b4fe 100644 --- a/src/sys/socket/sockopt.rs +++ b/src/sys/socket/sockopt.rs @@ -252,7 +252,9 @@ sockopt_impl!(Both, TcpKeepAlive, libc::IPPROTO_TCP, libc::TCP_KEEPALIVE, u32); target_os = "linux", target_os = "nacl"))] sockopt_impl!(Both, TcpKeepIdle, libc::IPPROTO_TCP, libc::TCP_KEEPIDLE, u32); +#[cfg(not(target_os = "openbsd"))] sockopt_impl!(Both, TcpKeepCount, libc::IPPROTO_TCP, libc::TCP_KEEPCNT, 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); diff --git a/src/sys/statfs.rs b/src/sys/statfs.rs index 8e90a58..27b7259 100644 --- a/src/sys/statfs.rs +++ b/src/sys/statfs.rs @@ -16,79 +16,85 @@ pub type fsid_t = libc::fsid_t; pub struct Statfs(libc::statfs); #[cfg(target_os = "freebsd")] -#[derive(Eq, Copy, Clone, PartialEq, Debug)] -pub struct FsType(pub u32); +type fs_type_t = u32; #[cfg(target_os = "android")] -#[derive(Eq, Copy, Clone, PartialEq, Debug)] -pub struct FsType(pub libc::c_ulong); +type fs_type_t = libc::c_ulong; #[cfg(all(target_os = "linux", target_arch = "s390x"))] -#[derive(Eq, Copy, Clone, PartialEq, Debug)] -pub struct FsType(pub u32); +type fs_type_t = libc::c_uint; #[cfg(all(target_os = "linux", target_env = "musl"))] -#[derive(Eq, Copy, Clone, PartialEq, Debug)] -pub struct FsType(pub libc::c_ulong); +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; + +#[cfg(any( + target_os = "freebsd", + target_os = "android", + all(target_os = "linux", target_arch = "s390x"), + all(target_os = "linux", target_env = "musl"), + all(target_os = "linux", not(any(target_arch = "s390x", target_env = "musl"))), +))] #[derive(Eq, Copy, Clone, PartialEq, Debug)] -pub struct FsType(pub libc::c_long); - -#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] -pub const ADFS_SUPER_MAGIC: FsType = FsType(libc::ADFS_SUPER_MAGIC); -#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] -pub const AFFS_SUPER_MAGIC: FsType = FsType(libc::AFFS_SUPER_MAGIC); -#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] -pub const CODA_SUPER_MAGIC: FsType = FsType(libc::CODA_SUPER_MAGIC); -#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] -pub const CRAMFS_MAGIC: FsType = FsType(libc::CRAMFS_MAGIC); -#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] -pub const EFS_SUPER_MAGIC: FsType = FsType(libc::EFS_SUPER_MAGIC); -#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] -pub const EXT2_SUPER_MAGIC: FsType = FsType(libc::EXT2_SUPER_MAGIC); -#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] -pub const EXT3_SUPER_MAGIC: FsType = FsType(libc::EXT3_SUPER_MAGIC); -#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] -pub const EXT4_SUPER_MAGIC: FsType = FsType(libc::EXT4_SUPER_MAGIC); -#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] -pub const HPFS_SUPER_MAGIC: FsType = FsType(libc::HPFS_SUPER_MAGIC); -#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] -pub const HUGETLBFS_MAGIC: FsType = FsType(libc::HUGETLBFS_MAGIC); -#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] -pub const ISOFS_SUPER_MAGIC: FsType = FsType(libc::ISOFS_SUPER_MAGIC); -#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] -pub const JFFS2_SUPER_MAGIC: FsType = FsType(libc::JFFS2_SUPER_MAGIC); -#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] -pub const MINIX_SUPER_MAGIC: FsType = FsType(libc::MINIX_SUPER_MAGIC); -#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] -pub const MINIX_SUPER_MAGIC2: FsType = FsType(libc::MINIX_SUPER_MAGIC2); -#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] -pub const MINIX2_SUPER_MAGIC: FsType = FsType(libc::MINIX2_SUPER_MAGIC); -#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] -pub const MINIX2_SUPER_MAGIC2: FsType = FsType(libc::MINIX2_SUPER_MAGIC2); -#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] -pub const MSDOS_SUPER_MAGIC: FsType = FsType(libc::MSDOS_SUPER_MAGIC); -#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] -pub const NCP_SUPER_MAGIC: FsType = FsType(libc::NCP_SUPER_MAGIC); -#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] -pub const NFS_SUPER_MAGIC: FsType = FsType(libc::NFS_SUPER_MAGIC); -#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] -pub const OPENPROM_SUPER_MAGIC: FsType = FsType(libc::OPENPROM_SUPER_MAGIC); -#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] -pub const OVERLAYFS_SUPER_MAGIC: FsType = FsType(libc::OVERLAYFS_SUPER_MAGIC); -#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] -pub const PROC_SUPER_MAGIC: FsType = FsType(libc::PROC_SUPER_MAGIC); -#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] -pub const QNX4_SUPER_MAGIC: FsType = FsType(libc::QNX4_SUPER_MAGIC); -#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] -pub const REISERFS_SUPER_MAGIC: FsType = FsType(libc::REISERFS_SUPER_MAGIC); -#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] -pub const SMB_SUPER_MAGIC: FsType = FsType(libc::SMB_SUPER_MAGIC); -#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] -pub const TMPFS_MAGIC: FsType = FsType(libc::TMPFS_MAGIC); -#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] -pub const USBDEVICE_SUPER_MAGIC: FsType = FsType(libc::USBDEVICE_SUPER_MAGIC); -#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] -pub const CGROUP_SUPER_MAGIC: FsType = FsType(libc::CGROUP_SUPER_MAGIC); -#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] -pub const CGROUP2_SUPER_MAGIC: FsType = FsType(libc::CGROUP2_SUPER_MAGIC); +pub struct FsType(pub fs_type_t); + +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +pub const ADFS_SUPER_MAGIC: FsType = FsType(libc::ADFS_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +pub const AFFS_SUPER_MAGIC: FsType = FsType(libc::AFFS_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +pub const CODA_SUPER_MAGIC: FsType = FsType(libc::CODA_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +pub const CRAMFS_MAGIC: FsType = FsType(libc::CRAMFS_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +pub const EFS_SUPER_MAGIC: FsType = FsType(libc::EFS_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +pub const EXT2_SUPER_MAGIC: FsType = FsType(libc::EXT2_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +pub const EXT3_SUPER_MAGIC: FsType = FsType(libc::EXT3_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +pub const EXT4_SUPER_MAGIC: FsType = FsType(libc::EXT4_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +pub const HPFS_SUPER_MAGIC: FsType = FsType(libc::HPFS_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +pub const HUGETLBFS_MAGIC: FsType = FsType(libc::HUGETLBFS_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +pub const ISOFS_SUPER_MAGIC: FsType = FsType(libc::ISOFS_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +pub const JFFS2_SUPER_MAGIC: FsType = FsType(libc::JFFS2_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +pub const MINIX_SUPER_MAGIC: FsType = FsType(libc::MINIX_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +pub const MINIX_SUPER_MAGIC2: FsType = FsType(libc::MINIX_SUPER_MAGIC2 as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +pub const MINIX2_SUPER_MAGIC: FsType = FsType(libc::MINIX2_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +pub const MINIX2_SUPER_MAGIC2: FsType = FsType(libc::MINIX2_SUPER_MAGIC2 as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +pub const MSDOS_SUPER_MAGIC: FsType = FsType(libc::MSDOS_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +pub const NCP_SUPER_MAGIC: FsType = FsType(libc::NCP_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +pub const NFS_SUPER_MAGIC: FsType = FsType(libc::NFS_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +pub const OPENPROM_SUPER_MAGIC: FsType = FsType(libc::OPENPROM_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +pub const OVERLAYFS_SUPER_MAGIC: FsType = FsType(libc::OVERLAYFS_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +pub const PROC_SUPER_MAGIC: FsType = FsType(libc::PROC_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +pub const QNX4_SUPER_MAGIC: FsType = FsType(libc::QNX4_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +pub const REISERFS_SUPER_MAGIC: FsType = FsType(libc::REISERFS_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +pub const SMB_SUPER_MAGIC: FsType = FsType(libc::SMB_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +pub const TMPFS_MAGIC: FsType = FsType(libc::TMPFS_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +pub const USBDEVICE_SUPER_MAGIC: FsType = FsType(libc::USBDEVICE_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +pub const CGROUP_SUPER_MAGIC: FsType = FsType(libc::CGROUP_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +pub const CGROUP2_SUPER_MAGIC: FsType = FsType(libc::CGROUP2_SUPER_MAGIC as fs_type_t); + impl Statfs { /// Magic code defining system type @@ -138,7 +144,7 @@ impl Statfs { /// Optimal transfer block size #[cfg(all(target_os = "linux", not(any(target_arch = "s390x", target_env = "musl"))))] - pub fn optimal_transfer_size(&self) -> libc::c_long { + pub fn optimal_transfer_size(&self) -> libc::__fsword_t { self.0.f_bsize } @@ -177,7 +183,7 @@ impl Statfs { /// Size of a block // f_bsize on linux: https://github.com/torvalds/linux/blob/master/fs/nfs/super.c#L471 #[cfg(all(target_os = "linux", not(any(target_arch = "s390x", target_env = "musl"))))] - pub fn block_size(&self) -> libc::c_long { + pub fn block_size(&self) -> libc::__fsword_t { self.0.f_bsize } @@ -219,7 +225,7 @@ impl Statfs { /// Maximum length of filenames #[cfg(all(target_os = "linux", not(any(target_arch = "s390x", target_env = "musl"))))] - pub fn maximum_name_length(&self) -> libc::c_long { + pub fn maximum_name_length(&self) -> libc::__fsword_t { self.0.f_namelen } @@ -248,7 +254,7 @@ impl Statfs { } /// Total data blocks in filesystem - #[cfg(all(target_os = "linux", target_env = "musl"))] + #[cfg(all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32"))))] pub fn blocks(&self) -> u64 { self.0.f_blocks } @@ -261,7 +267,7 @@ impl Statfs { target_os = "freebsd", target_os = "openbsd", target_os = "dragonfly", - all(target_os = "linux", target_env = "musl") + all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32"))) )))] pub fn blocks(&self) -> libc::c_ulong { self.0.f_blocks @@ -286,7 +292,7 @@ impl Statfs { } /// Free blocks in filesystem - #[cfg(all(target_os = "linux", target_env = "musl"))] + #[cfg(all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32"))))] pub fn blocks_free(&self) -> u64 { self.0.f_bfree } @@ -299,7 +305,7 @@ impl Statfs { target_os = "freebsd", target_os = "openbsd", target_os = "dragonfly", - all(target_os = "linux", target_env = "musl") + all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32"))) )))] pub fn blocks_free(&self) -> libc::c_ulong { self.0.f_bfree @@ -324,7 +330,7 @@ impl Statfs { } /// Free blocks available to unprivileged user - #[cfg(all(target_os = "linux", target_env = "musl"))] + #[cfg(all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32"))))] pub fn blocks_available(&self) -> u64 { self.0.f_bavail } @@ -337,7 +343,7 @@ impl Statfs { target_os = "freebsd", target_os = "openbsd", target_os = "dragonfly", - all(target_os = "linux", target_env = "musl") + all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32"))) )))] pub fn blocks_available(&self) -> libc::c_ulong { self.0.f_bavail @@ -362,8 +368,8 @@ impl Statfs { } /// Total file nodes in filesystem - #[cfg(all(target_os = "linux", target_env = "musl"))] - pub fn files(&self) -> u64 { + #[cfg(all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32"))))] + pub fn files(&self) -> libc::fsfilcnt_t { self.0.f_files } @@ -375,7 +381,7 @@ impl Statfs { target_os = "freebsd", target_os = "openbsd", target_os = "dragonfly", - all(target_os = "linux", target_env = "musl") + all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32"))) )))] pub fn files(&self) -> libc::c_ulong { self.0.f_files @@ -385,7 +391,6 @@ impl Statfs { #[cfg(any( target_os = "android", target_os = "ios", - all(target_os = "linux", target_env = "musl"), target_os = "macos", target_os = "openbsd" ))] @@ -406,6 +411,12 @@ impl Statfs { } /// Free file nodes in filesystem + #[cfg(all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32"))))] + pub fn files_free(&self) -> libc::fsfilcnt_t { + self.0.f_ffree + } + + /// Free file nodes in filesystem #[cfg(not(any( target_os = "ios", target_os = "macos", @@ -413,7 +424,7 @@ impl Statfs { target_os = "freebsd", target_os = "openbsd", target_os = "dragonfly", - all(target_os = "linux", target_env = "musl") + all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32"))) )))] pub fn files_free(&self) -> libc::c_ulong { self.0.f_ffree diff --git a/src/sys/sysinfo.rs b/src/sys/sysinfo.rs index f4b8279..222a2fc 100644 --- a/src/sys/sysinfo.rs +++ b/src/sys/sysinfo.rs @@ -10,6 +10,12 @@ use crate::errno::Errno; #[repr(transparent)] pub struct SysInfo(libc::sysinfo); +// The fields are c_ulong on 32-bit linux, u64 on 64-bit linux; x32's ulong is u32 +#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))] +type mem_blocks_t = u64; +#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))] +type mem_blocks_t = libc::c_ulong; + impl SysInfo { /// Returns the load average tuple. /// @@ -58,7 +64,7 @@ impl SysInfo { self.scale_mem(self.0.freeram) } - fn scale_mem(&self, units: libc::c_ulong) -> u64 { + fn scale_mem(&self, units: mem_blocks_t) -> u64 { units as u64 * self.0.mem_unit as u64 } } diff --git a/src/sys/time.rs b/src/sys/time.rs index 269b425..7546d1b 100644 --- a/src/sys/time.rs +++ b/src/sys/time.rs @@ -1,7 +1,8 @@ use std::{cmp, fmt, ops}; use std::time::Duration; use std::convert::From; -use libc::{c_long, timespec, timeval}; +use libc::{timespec, timeval}; +#[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848 pub use libc::{time_t, suseconds_t}; pub trait TimeValLike: Sized { @@ -61,6 +62,13 @@ const TS_MAX_SECONDS: i64 = ::std::isize::MAX as i64; const TS_MIN_SECONDS: i64 = -TS_MAX_SECONDS; +// x32 compatibility +// See https://sourceware.org/bugzilla/show_bug.cgi?id=16437 +#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))] +type timespec_tv_nsec_t = i64; +#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))] +type timespec_tv_nsec_t = libc::c_long; + impl From<timespec> for TimeSpec { fn from(ts: timespec) -> Self { Self(ts) @@ -69,9 +77,10 @@ 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 c_long + tv_nsec: duration.subsec_nanos() as timespec_tv_nsec_t }) } } @@ -117,6 +126,7 @@ impl TimeValLike for TimeSpec { fn seconds(seconds: i64) -> TimeSpec { assert!(seconds >= TS_MIN_SECONDS && seconds <= TS_MAX_SECONDS, "TimeSpec out of bounds; seconds={}", seconds); + #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848 TimeSpec(timespec {tv_sec: seconds as time_t, tv_nsec: 0 }) } @@ -143,8 +153,9 @@ impl TimeValLike for TimeSpec { let (secs, nanos) = div_mod_floor_64(nanoseconds, NANOS_PER_SEC); assert!(secs >= TS_MIN_SECONDS && secs <= TS_MAX_SECONDS, "TimeSpec out of bounds"); + #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848 TimeSpec(timespec {tv_sec: secs as time_t, - tv_nsec: nanos as c_long }) + tv_nsec: nanos as timespec_tv_nsec_t }) } fn num_seconds(&self) -> i64 { @@ -171,19 +182,20 @@ impl TimeValLike for TimeSpec { } impl TimeSpec { - fn nanos_mod_sec(&self) -> c_long { + fn nanos_mod_sec(&self) -> timespec_tv_nsec_t { if self.tv_sec() < 0 && self.tv_nsec() > 0 { - self.tv_nsec() - NANOS_PER_SEC as c_long + self.tv_nsec() - NANOS_PER_SEC as timespec_tv_nsec_t } else { self.tv_nsec() } } + #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848 pub fn tv_sec(&self) -> time_t { self.0.tv_sec } - pub fn tv_nsec(&self) -> c_long { + pub fn tv_nsec(&self) -> timespec_tv_nsec_t { self.0.tv_nsec } } @@ -315,6 +327,7 @@ impl TimeValLike for TimeVal { fn seconds(seconds: i64) -> TimeVal { assert!(seconds >= TV_MIN_SECONDS && seconds <= TV_MAX_SECONDS, "TimeVal out of bounds; seconds={}", seconds); + #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848 TimeVal(timeval {tv_sec: seconds as time_t, tv_usec: 0 }) } @@ -332,6 +345,7 @@ impl TimeValLike for TimeVal { let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC); assert!(secs >= TV_MIN_SECONDS && secs <= TV_MAX_SECONDS, "TimeVal out of bounds"); + #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848 TimeVal(timeval {tv_sec: secs as time_t, tv_usec: micros as suseconds_t }) } @@ -344,6 +358,7 @@ impl TimeValLike for TimeVal { let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC); assert!(secs >= TV_MIN_SECONDS && secs <= TV_MAX_SECONDS, "TimeVal out of bounds"); + #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848 TimeVal(timeval {tv_sec: secs as time_t, tv_usec: micros as suseconds_t }) } @@ -380,6 +395,7 @@ impl TimeVal { } } + #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848 pub fn tv_sec(&self) -> time_t { self.0.tv_sec } diff --git a/src/sys/timerfd.rs b/src/sys/timerfd.rs index 3086309..4a24719 100644 --- a/src/sys/timerfd.rs +++ b/src/sys/timerfd.rs @@ -37,7 +37,7 @@ use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; /// A timerfd instance. This is also a file descriptor, you can feed it to /// other interfaces consuming file descriptors, epoll for example. -#[derive(Debug, Clone, Copy)] +#[derive(Debug)] pub struct TimerFd { fd: RawFd, } @@ -166,7 +166,7 @@ pub enum Expiration { impl TimerFd { /// Creates a new timer based on the clock defined by `clockid`. The /// underlying fd can be assigned specific flags with `flags` (CLOEXEC, - /// NONBLOCK). + /// NONBLOCK). The underlying fd will be closed on drop. pub fn new(clockid: ClockId, flags: TimerFlags) -> Result<Self> { Errno::result(unsafe { libc::timerfd_create(clockid as i32, flags.bits()) }) .map(|fd| Self { fd }) @@ -270,3 +270,16 @@ impl TimerFd { Ok(()) } } + +impl Drop for TimerFd { + fn drop(&mut self) { + if !std::thread::panicking() { + let result = Errno::result(unsafe { + libc::close(self.fd) + }); + if let Err(Error::Sys(Errno::EBADF)) = result { + panic!("close of TimerFd encountered EBADF"); + } + } + } +} diff --git a/src/sys/wait.rs b/src/sys/wait.rs index 0c04042..faf8543 100644 --- a/src/sys/wait.rs +++ b/src/sys/wait.rs @@ -1,9 +1,9 @@ -use cfg_if::cfg_if; -use libc::{self, c_int}; -use crate::Result; use crate::errno::Errno; -use crate::unistd::Pid; use crate::sys::signal::Signal; +use crate::unistd::Pid; +use crate::Result; +use cfg_if::cfg_if; +use libc::{self, c_int}; use std::convert::TryFrom; libc_bitflags!( @@ -108,8 +108,7 @@ impl WaitStatus { pub fn pid(&self) -> Option<Pid> { use self::WaitStatus::*; match *self { - Exited(p, _) | Signaled(p, _, _) | - Stopped(p, _) | Continued(p) => Some(p), + Exited(p, _) | Signaled(p, _, _) | Stopped(p, _) | Continued(p) => Some(p), StillAlive => None, #[cfg(any(target_os = "android", target_os = "linux"))] PtraceEvent(p, _, _) | PtraceSyscall(p) => Some(p), @@ -117,49 +116,41 @@ impl WaitStatus { } } -#[allow(unused_unsafe)] fn exited(status: i32) -> bool { - unsafe { libc::WIFEXITED(status) } + libc::WIFEXITED(status) } -#[allow(unused_unsafe)] fn exit_status(status: i32) -> i32 { - unsafe { libc::WEXITSTATUS(status) } + libc::WEXITSTATUS(status) } -#[allow(unused_unsafe)] fn signaled(status: i32) -> bool { - unsafe { libc::WIFSIGNALED(status) } + libc::WIFSIGNALED(status) } -#[allow(unused_unsafe)] fn term_signal(status: i32) -> Result<Signal> { - Signal::try_from(unsafe { libc::WTERMSIG(status) }) + Signal::try_from(libc::WTERMSIG(status)) } -#[allow(unused_unsafe)] fn dumped_core(status: i32) -> bool { - unsafe { libc::WCOREDUMP(status) } + libc::WCOREDUMP(status) } -#[allow(unused_unsafe)] fn stopped(status: i32) -> bool { - unsafe { libc::WIFSTOPPED(status) } + libc::WIFSTOPPED(status) } -#[allow(unused_unsafe)] fn stop_signal(status: i32) -> Result<Signal> { - Signal::try_from(unsafe { libc::WSTOPSIG(status) }) + Signal::try_from(libc::WSTOPSIG(status)) } #[cfg(any(target_os = "android", target_os = "linux"))] -#[allow(unused_unsafe)] fn syscall_stop(status: i32) -> bool { // From ptrace(2), setting PTRACE_O_TRACESYSGOOD has the effect // of delivering SIGTRAP | 0x80 as the signal number for syscall // stops. This allows easily distinguishing syscall stops from // genuine SIGTRAP signals. - unsafe { libc::WSTOPSIG(status) == libc::SIGTRAP | 0x80 } + libc::WSTOPSIG(status) == libc::SIGTRAP | 0x80 } #[cfg(any(target_os = "android", target_os = "linux"))] @@ -167,9 +158,8 @@ fn stop_additional(status: i32) -> c_int { (status >> 16) as c_int } -#[allow(unused_unsafe)] fn continued(status: i32) -> bool { - unsafe { libc::WIFCONTINUED(status) } + libc::WIFCONTINUED(status) } impl WaitStatus { diff --git a/src/time.rs b/src/time.rs index 54989c2..e6c3f8d 100644 --- a/src/time.rs +++ b/src/time.rs @@ -37,6 +37,7 @@ impl ClockId { } /// Returns resolution of the clock id + #[cfg(not(target_os = "redox"))] pub fn res(self) -> Result<TimeSpec> { clock_getres(self) } @@ -204,6 +205,7 @@ impl std::fmt::Display for ClockId { /// Get the resolution of the specified clock, (see /// [clock_getres(2)](https://pubs.opengroup.org/onlinepubs/7908799/xsh/clock_getres.html)). +#[cfg(not(target_os = "redox"))] pub fn clock_getres(clock_id: ClockId) -> Result<TimeSpec> { let mut c_time: MaybeUninit<libc::timespec> = MaybeUninit::uninit(); let ret = unsafe { libc::clock_getres(clock_id.as_raw(), c_time.as_mut_ptr()) }; diff --git a/src/unistd.rs b/src/unistd.rs index 04031e3..7a4517e 100644 --- a/src/unistd.rs +++ b/src/unistd.rs @@ -12,9 +12,9 @@ use libc::{self, c_char, c_void, c_int, c_long, c_uint, size_t, pid_t, off_t, uid_t, gid_t, mode_t, PATH_MAX}; use std::{fmt, mem, ptr}; use std::convert::Infallible; -use std::ffi::{CStr, CString, OsString}; +use std::ffi::{CStr, OsString}; #[cfg(not(target_os = "redox"))] -use std::ffi::{OsStr}; +use std::ffi::{CString, OsStr}; use std::os::unix::ffi::OsStringExt; #[cfg(not(target_os = "redox"))] use std::os::unix::ffi::OsStrExt; @@ -426,6 +426,7 @@ pub fn chdir<P: ?Sized + NixPath>(path: &P) -> Result<()> { /// This function may fail in a number of different scenarios. See the man /// pages for additional details on possible failure cases. #[inline] +#[cfg(not(target_os = "fuchsia"))] pub fn fchdir(dirfd: RawFd) -> Result<()> { let res = unsafe { libc::fchdir(dirfd) }; @@ -1095,7 +1096,7 @@ pub fn pipe2(flags: OFlag) -> Result<(RawFd, RawFd)> { /// /// See also /// [truncate(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/truncate.html) -#[cfg(not(target_os = "redox"))] +#[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| { unsafe { @@ -1232,6 +1233,7 @@ pub fn unlinkat<P: ?Sized + NixPath>( #[inline] +#[cfg(not(target_os = "fuchsia"))] pub fn chroot<P: ?Sized + NixPath>(path: &P) -> Result<()> { let res = path.with_nix_path(|cstr| { unsafe { libc::chroot(cstr.as_ptr()) } @@ -1633,7 +1635,8 @@ pub mod alarm { //! //! Scheduling an alarm and waiting for the signal: //! - //! ``` +#![cfg_attr(target_os = "redox", doc = " ```rust,ignore")] +#![cfg_attr(not(target_os = "redox"), doc = " ```rust")] //! use std::time::{Duration, Instant}; //! //! use nix::unistd::{alarm, pause}; @@ -1642,14 +1645,23 @@ pub mod alarm { //! // We need to setup an empty signal handler to catch the alarm signal, //! // otherwise the program will be terminated once the signal is delivered. //! extern fn signal_handler(_: nix::libc::c_int) { } - //! unsafe { sigaction(Signal::SIGALRM, &SigAction::new(SigHandler::Handler(signal_handler), SaFlags::empty(), SigSet::empty())); } + //! let sa = SigAction::new( + //! SigHandler::Handler(signal_handler), + //! SaFlags::empty(), + //! SigSet::empty() + //! ); + //! unsafe { + //! sigaction(Signal::SIGALRM, &sa); + //! } //! //! // Set an alarm for 1 second from now. //! alarm::set(1); //! //! let start = Instant::now(); //! // Pause the process until the alarm signal is received. - //! pause(); + //! let mut sigset = SigSet::empty(); + //! sigset.add(Signal::SIGALRM); + //! sigset.wait(); //! //! assert!(start.elapsed() >= Duration::from_secs(1)); //! ``` @@ -2536,13 +2548,16 @@ pub struct User { /// Path to shell pub shell: PathBuf, /// Login class - #[cfg(not(any(target_os = "android", target_os = "linux")))] + #[cfg(not(any(target_os = "android", target_os = "fuchsia", + target_os = "linux")))] pub class: CString, /// Last password change - #[cfg(not(any(target_os = "android", target_os = "linux")))] + #[cfg(not(any(target_os = "android", target_os = "fuchsia", + target_os = "linux")))] pub change: libc::time_t, /// Expiration time of account - #[cfg(not(any(target_os = "android", target_os = "linux")))] + #[cfg(not(any(target_os = "android", target_os = "fuchsia", + target_os = "linux")))] pub expire: libc::time_t } @@ -2559,11 +2574,14 @@ impl From<&libc::passwd> for User { 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 = "linux")))] + #[cfg(not(any(target_os = "android", target_os = "fuchsia", + target_os = "linux")))] class: CString::new(CStr::from_ptr((*pw).pw_class).to_bytes()).unwrap(), - #[cfg(not(any(target_os = "android", target_os = "linux")))] + #[cfg(not(any(target_os = "android", target_os = "fuchsia", + target_os = "linux")))] change: (*pw).pw_change, - #[cfg(not(any(target_os = "android", target_os = "linux")))] + #[cfg(not(any(target_os = "android", target_os = "fuchsia", + target_os = "linux")))] expire: (*pw).pw_expire } } @@ -2653,6 +2671,8 @@ impl User { pub struct Group { /// Group name pub name: String, + /// Group password + pub passwd: CString, /// Group ID pub gid: Gid, /// List of Group members @@ -2665,6 +2685,7 @@ impl From<&libc::group> for Group { unsafe { Group { name: CStr::from_ptr((*gr).gr_name).to_string_lossy().into_owned(), + passwd: CString::new(CStr::from_ptr((*gr).gr_passwd).to_bytes()).unwrap(), gid: Gid::from_raw((*gr).gr_gid), mem: Group::members((*gr).gr_mem) } @@ -2771,6 +2792,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)). +#[cfg(not(target_os = "fuchsia"))] pub fn ttyname(fd: RawFd) -> Result<PathBuf> { const PATH_MAX: usize = libc::PATH_MAX as usize; let mut buf = vec![0_u8; PATH_MAX]; @@ -2785,3 +2807,23 @@ pub fn ttyname(fd: RawFd) -> Result<PathBuf> { buf.truncate(nul); Ok(OsString::from_vec(buf).into()) } + +/// Get the effective user ID and group ID associated with a Unix domain socket. +/// +/// See also [getpeereid(3)](https://www.freebsd.org/cgi/man.cgi?query=getpeereid) +#[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "openbsd", + target_os = "netbsd", + target_os = "dragonfly", +))] +pub fn getpeereid(fd: RawFd) -> Result<(Uid, Gid)> { + let mut uid = 1; + let mut gid = 1; + + let ret = unsafe { libc::getpeereid(fd, &mut uid, &mut gid) }; + + Errno::result(ret).map(|_| (Uid(uid), Gid(gid))) +} diff --git a/test/common/mod.rs b/test/common/mod.rs new file mode 100644 index 0000000..a871b47 --- /dev/null +++ b/test/common/mod.rs @@ -0,0 +1,127 @@ +use cfg_if::cfg_if; + +#[macro_export] macro_rules! skip { + ($($reason: expr),+) => { + use ::std::io::{self, Write}; + + let stderr = io::stderr(); + let mut handle = stderr.lock(); + writeln!(handle, $($reason),+).unwrap(); + return; + } +} + +cfg_if! { + if #[cfg(any(target_os = "android", target_os = "linux"))] { + #[macro_export] macro_rules! require_capability { + ($capname:ident) => { + use ::caps::{Capability, CapSet, has_cap}; + + if !has_cap(None, CapSet::Effective, Capability::$capname) + .unwrap() + { + skip!("Insufficient capabilities. Skipping test."); + } + } + } + } else if #[cfg(not(target_os = "redox"))] { + #[macro_export] macro_rules! require_capability { + ($capname:ident) => {} + } + } +} + +#[cfg(any(target_os = "linux", target_os= "android"))] +#[macro_export] macro_rules! skip_if_cirrus { + ($reason:expr) => { + if std::env::var_os("CIRRUS_CI").is_some() { + skip!("{}", $reason); + } + } +} + +#[cfg(target_os = "freebsd")] +#[macro_export] macro_rules! skip_if_jailed { + ($name:expr) => { + use ::sysctl::CtlValue; + + if let CtlValue::Int(1) = ::sysctl::value("security.jail.jailed") + .unwrap() + { + skip!("{} cannot run in a jail. Skipping test.", $name); + } + } +} + +#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))] +#[macro_export] macro_rules! skip_if_not_root { + ($name:expr) => { + use nix::unistd::Uid; + + if !Uid::current().is_root() { + skip!("{} requires root privileges. Skipping test.", $name); + } + }; +} + +cfg_if! { + if #[cfg(any(target_os = "android", target_os = "linux"))] { + #[macro_export] macro_rules! skip_if_seccomp { + ($name:expr) => { + if let Ok(s) = std::fs::read_to_string("/proc/self/status") { + for l in s.lines() { + let mut fields = l.split_whitespace(); + if fields.next() == Some("Seccomp:") && + fields.next() != Some("0") + { + skip!("{} cannot be run in Seccomp mode. Skipping test.", + stringify!($name)); + } + } + } + } + } + } else if #[cfg(not(target_os = "redox"))] { + #[macro_export] macro_rules! skip_if_seccomp { + ($name:expr) => {} + } + } +} + +cfg_if! { + if #[cfg(target_os = "linux")] { + #[macro_export] macro_rules! require_kernel_version { + ($name:expr, $version_requirement:expr) => { + use semver::{Version, VersionReq}; + + let version_requirement = VersionReq::parse($version_requirement) + .expect("Bad match_version provided"); + + let uname = nix::sys::utsname::uname(); + println!("{}", uname.sysname()); + println!("{}", uname.nodename()); + println!("{}", uname.release()); + println!("{}", uname.version()); + println!("{}", uname.machine()); + + // Fix stuff that the semver parser can't handle + let fixed_release = &uname.release().to_string() + // Fedora 33 reports version as 4.18.el8_2.x86_64 or + // 5.18.200-fc33.x86_64. Remove the underscore. + .replace("_", "-") + // Cirrus-CI reports version as 4.19.112+ . Remove the + + .replace("+", ""); + let mut version = Version::parse(fixed_release).unwrap(); + + //Keep only numeric parts + version.pre.clear(); + version.build.clear(); + + if !version_requirement.matches(&version) { + skip!("Skip {} because kernel version `{}` doesn't match the requirement `{}`", + stringify!($name), version, version_requirement); + } + } + } + } +} diff --git a/test/sys/mod.rs b/test/sys/mod.rs index c4391c7..14b0378 100644 --- a/test/sys/mod.rs +++ b/test/sys/mod.rs @@ -21,9 +21,9 @@ mod test_sockopt; mod test_select; #[cfg(any(target_os = "android", target_os = "linux"))] mod test_sysinfo; -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))] mod test_termios; -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))] mod test_ioctl; mod test_wait; mod test_uio; diff --git a/test/sys/test_aio.rs b/test/sys/test_aio.rs index 6b9bd91..3878da9 100644 --- a/test/sys/test_aio.rs +++ b/test/sys/test_aio.rs @@ -137,6 +137,9 @@ fn test_fsync_error() { // in Travis's version of glibc or Linux. Either way, we must skip the test. // https://github.com/nix-rust/nix/issues/1099 #[cfg_attr(target_os = "linux", ignore)] +// On Cirrus, aio_suspend is failing with EINVAL +// https://github.com/nix-rust/nix/issues/1361 +#[cfg_attr(target_os = "macos", ignore)] fn test_aio_suspend() { const INITIAL: &[u8] = b"abcdef123456"; const WBUF: &[u8] = b"CDEFG"; @@ -164,7 +167,12 @@ fn test_aio_suspend() { loop { { let cbbuf = [&wcb, &rcb]; - assert!(aio_suspend(&cbbuf[..], Some(timeout)).is_ok()); + let r = aio_suspend(&cbbuf[..], Some(timeout)); + match r { + Err(Error::Sys(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)) { diff --git a/test/sys/test_aio_drop.rs b/test/sys/test_aio_drop.rs index 71a2183..784ee3e 100644 --- a/test/sys/test_aio_drop.rs +++ b/test/sys/test_aio_drop.rs @@ -9,6 +9,7 @@ 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_mman.rs b/test/sys/test_mman.rs new file mode 100644 index 0000000..152fff6 --- /dev/null +++ b/test/sys/test_mman.rs @@ -0,0 +1,80 @@ +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 { + 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); +} + +#[test] +#[cfg(target_os = "linux")] +fn test_mremap_grow() { + const ONE_K : size_t = 1024; + let slice : &mut[u8] = unsafe { + let mem = mmap(std::ptr::null_mut(), ONE_K, + ProtFlags::PROT_READ | ProtFlags::PROT_WRITE, + MapFlags::MAP_ANONYMOUS | MapFlags::MAP_PRIVATE, -1, 0) + .unwrap(); + std::slice::from_raw_parts_mut(mem as * mut u8, ONE_K) + }; + assert_eq !(slice[ONE_K - 1], 0x00); + slice[ONE_K - 1] = 0xFF; + assert_eq !(slice[ONE_K - 1], 0xFF); + + let slice : &mut[u8] = unsafe { + let mem = mremap(slice.as_mut_ptr() as * mut c_void, ONE_K, 10 * ONE_K, + MRemapFlags::MREMAP_MAYMOVE, None) + .unwrap(); + std::slice::from_raw_parts_mut(mem as * mut u8, 10 * ONE_K) + }; + + // The first KB should still have the old data in it. + assert_eq !(slice[ONE_K - 1], 0xFF); + + // The additional range should be zero-init'd and accessible. + assert_eq !(slice[10 * ONE_K - 1], 0x00); + slice[10 * ONE_K - 1] = 0xFF; + assert_eq !(slice[10 * ONE_K - 1], 0xFF); +} + +#[test] +#[cfg(target_os = "linux")] +fn test_mremap_shrink() { + const ONE_K : size_t = 1024; + let slice : &mut[u8] = unsafe { + let mem = mmap(std::ptr::null_mut(), 10 * ONE_K, + ProtFlags::PROT_READ | ProtFlags::PROT_WRITE, + MapFlags::MAP_ANONYMOUS | MapFlags::MAP_PRIVATE, -1, 0) + .unwrap(); + std::slice::from_raw_parts_mut(mem as * mut u8, ONE_K) + }; + assert_eq !(slice[ONE_K - 1], 0x00); + slice[ONE_K - 1] = 0xFF; + assert_eq !(slice[ONE_K - 1], 0xFF); + + let slice : &mut[u8] = unsafe { + 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. + assert_eq !(mem, slice.as_mut_ptr() as * mut c_void); + std::slice::from_raw_parts_mut(mem as * mut u8, ONE_K) + }; + + // The first KB should still be accessible and have the old data in it. + assert_eq !(slice[ONE_K - 1], 0xFF); +} diff --git a/test/sys/test_ptrace.rs b/test/sys/test_ptrace.rs index 3b60dd7..b9793b3 100644 --- a/test/sys/test_ptrace.rs +++ b/test/sys/test_ptrace.rs @@ -8,6 +8,8 @@ use nix::sys::ptrace::Options; #[cfg(any(target_os = "android", target_os = "linux"))] use std::mem; +use crate::*; + #[test] fn test_ptrace() { // Just make sure ptrace can be called at all, for now. @@ -98,7 +100,7 @@ fn test_ptrace_cont() { ptrace::cont(child, Some(Signal::SIGKILL)).unwrap(); match waitpid(child, None) { Ok(WaitStatus::Signaled(pid, Signal::SIGKILL, _)) if pid == child => { - // FIXME It's been observed on some systems (apple) the + // FIXME It's been observed on some systems (apple) the // tracee may not be killed but remain as a zombie process // affecting other wait based tests. Add an extra kill just // to make sure there are no zombies. @@ -148,11 +150,11 @@ fn test_ptrace_syscall() { // set this option to recognize syscall-stops ptrace::setoptions(child, ptrace::Options::PTRACE_O_TRACESYSGOOD).unwrap(); - #[cfg(target_pointer_width = "64")] - let get_syscall_id = || ptrace::getregs(child).unwrap().orig_rax as i64; + #[cfg(target_arch = "x86_64")] + let get_syscall_id = || ptrace::getregs(child).unwrap().orig_rax as libc::c_long; - #[cfg(target_pointer_width = "32")] - let get_syscall_id = || ptrace::getregs(child).unwrap().orig_eax as i32; + #[cfg(target_arch = "x86")] + let get_syscall_id = || ptrace::getregs(child).unwrap().orig_eax as libc::c_long; // kill entry ptrace::syscall(child, None).unwrap(); diff --git a/test/sys/test_signal.rs b/test/sys/test_signal.rs index 5e0b276..ae22527 100644 --- a/test/sys/test_signal.rs +++ b/test/sys/test_signal.rs @@ -12,6 +12,7 @@ fn test_kill_none() { } #[test] +#[cfg(not(target_os = "fuchsia"))] fn test_killpg_none() { killpg(getpgrp(), None) .expect("Should be able to send signal to my process group."); diff --git a/test/sys/test_socket.rs b/test/sys/test_socket.rs index 1003598..7eab28c 100644 --- a/test/sys/test_socket.rs +++ b/test/sys/test_socket.rs @@ -1,4 +1,3 @@ -use nix::ifaddrs::InterfaceAddress; use nix::sys::socket::{AddressFamily, InetAddr, UnixAddr, getsockname}; use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; @@ -9,6 +8,8 @@ use std::slice; use std::str::FromStr; use libc::c_char; use tempfile; +#[cfg(any(target_os = "linux", target_os= "android"))] +use crate::*; #[test] pub fn test_inetv4_addr_to_sock_addr() { @@ -237,6 +238,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)] pub fn gso() { require_kernel_version!(udp_offload::gso, ">= 4.18"); @@ -288,6 +292,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)] pub fn gro() { require_kernel_version!(udp_offload::gro, ">= 5.3"); @@ -437,6 +444,76 @@ mod recvfrom { send_thread.join().unwrap(); } + + #[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "netbsd", + ))] + #[test] + pub fn udp_recvmmsg_dontwait_short_read() { + use nix::sys::uio::IoVec; + use nix::sys::socket::{MsgFlags, recvmmsg}; + + const NUM_MESSAGES_SENT: usize = 2; + const DATA: [u8; 4] = [1,2,3,4]; + + let std_sa = SocketAddr::from_str("127.0.0.1:6799").unwrap(); + let inet_addr = InetAddr::from_std(&std_sa); + let sock_addr = SockAddr::new_inet(inet_addr); + + let rsock = socket(AddressFamily::Inet, + SockType::Datagram, + SockFlag::empty(), + None + ).unwrap(); + bind(rsock, &sock_addr).unwrap(); + let ssock = socket( + AddressFamily::Inet, + SockType::Datagram, + SockFlag::empty(), + None, + ).expect("send socket failed"); + + let send_thread = thread::spawn(move || { + for _ in 0..NUM_MESSAGES_SENT { + sendto(ssock, &DATA[..], &sock_addr, MsgFlags::empty()).unwrap(); + } + }); + // Ensure we've sent all the messages before continuing so `recvmmsg` + // will return right away + send_thread.join().unwrap(); + + let mut msgs = std::collections::LinkedList::new(); + + // Buffers to receive >`NUM_MESSAGES_SENT` messages to ensure `recvmmsg` + // will return when there are fewer than requested messages in the + // kernel buffers when using `MSG_DONTWAIT`. + let mut receive_buffers = [[0u8; 32]; NUM_MESSAGES_SENT + 2]; + let iovs: Vec<_> = receive_buffers.iter_mut().map(|buf| { + [IoVec::from_mut_slice(&mut buf[..])] + }).collect(); + + for iov in &iovs { + msgs.push_back(RecvMmsgData { + iov: iov, + cmsg_buffer: None, + }) + }; + + let res = recvmmsg(rsock, &mut msgs, MsgFlags::MSG_DONTWAIT, None).expect("recvmmsg"); + assert_eq!(res.len(), NUM_MESSAGES_SENT); + + for RecvMsg { address, bytes, .. } in res.into_iter() { + assert_eq!(AddressFamily::Inet, address.unwrap().family()); + assert_eq!(DATA.len(), bytes); + } + + for buf in &receive_buffers[..NUM_MESSAGES_SENT] { + assert_eq!(&buf[..DATA.len()], DATA); + } + } } // Test error handling of our recvmsg wrapper @@ -522,12 +599,13 @@ pub fn test_af_alg_cipher() { ControlMessage, MsgFlags}; use nix::sys::socket::sockopt::AlgSetKey; + skip_if_cirrus!("Fails for an unknown reason Cirrus CI. Bug #1352"); // Travis's seccomp profile blocks AF_ALG // https://docs.docker.com/engine/security/seccomp/ skip_if_seccomp!(test_af_alg_cipher); let alg_type = "skcipher"; - let alg_name = "ctr(aes)"; + let alg_name = "ctr-aes-aesni"; // 256-bits secret key let key = vec![0u8; 32]; // 16-bytes IV @@ -590,6 +668,7 @@ pub fn test_af_alg_aead() { ControlMessage, MsgFlags}; use nix::sys::socket::sockopt::{AlgSetKey, AlgSetAeadAuthSize}; + skip_if_cirrus!("Fails for an unknown reason Cirrus CI. Bug #1352"); // Travis's seccomp profile blocks AF_ALG // https://docs.docker.com/engine/security/seccomp/ skip_if_seccomp!(test_af_alg_aead); @@ -948,7 +1027,7 @@ fn test_too_large_cmsgspace() { fn test_impl_scm_credentials_and_rights(mut space: Vec<u8>) { use libc::ucred; use nix::sys::uio::IoVec; - use nix::unistd::{pipe, read, write, close, getpid, getuid, getgid}; + use nix::unistd::{pipe, write, close, getpid, getuid, getgid}; use nix::sys::socket::{socketpair, sendmsg, recvmsg, setsockopt, SockType, SockFlag, ControlMessage, ControlMessageOwned, MsgFlags}; @@ -1081,7 +1160,7 @@ pub fn test_syscontrol() { target_os = "netbsd", target_os = "openbsd", ))] -fn loopback_address(family: AddressFamily) -> Option<InterfaceAddress> { +fn loopback_address(family: AddressFamily) -> Option<nix::ifaddrs::InterfaceAddress> { use std::io; use std::io::Write; use nix::ifaddrs::getifaddrs; diff --git a/test/sys/test_sockopt.rs b/test/sys/test_sockopt.rs index 8e2adce..5606593 100644 --- a/test/sys/test_sockopt.rs +++ b/test/sys/test_sockopt.rs @@ -1,5 +1,7 @@ use rand::{thread_rng, Rng}; use nix::sys::socket::{socket, sockopt, getsockopt, setsockopt, AddressFamily, SockType, SockFlag, SockProtocol}; +#[cfg(any(target_os = "android", target_os = "linux"))] +use crate::*; #[cfg(target_os = "linux")] #[test] diff --git a/test/sys/test_uio.rs b/test/sys/test_uio.rs index 4fa838c..8d22bf1 100644 --- a/test/sys/test_uio.rs +++ b/test/sys/test_uio.rs @@ -203,6 +203,7 @@ 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"); diff --git a/test/sys/test_wait.rs b/test/sys/test_wait.rs index d105625..5bb298e 100644 --- a/test/sys/test_wait.rs +++ b/test/sys/test_wait.rs @@ -67,6 +67,7 @@ mod ptrace { use nix::unistd::*; use nix::unistd::ForkResult::*; use libc::_exit; + use crate::*; fn ptrace_child() -> ! { ptrace::traceme().unwrap(); diff --git a/test/test.rs b/test/test.rs index 8ad09b6..5a5330b 100644 --- a/test/test.rs +++ b/test/test.rs @@ -5,111 +5,7 @@ extern crate nix; #[macro_use] extern crate lazy_static; -macro_rules! skip { - ($($reason: expr),+) => { - use ::std::io::{self, Write}; - - let stderr = io::stderr(); - let mut handle = stderr.lock(); - writeln!(handle, $($reason),+).unwrap(); - return; - } -} - -cfg_if! { - if #[cfg(any(target_os = "android", target_os = "linux"))] { - macro_rules! require_capability { - ($capname:ident) => { - use ::caps::{Capability, CapSet, has_cap}; - - if !has_cap(None, CapSet::Effective, Capability::$capname) - .unwrap() - { - skip!("Insufficient capabilities. Skipping test."); - } - } - } - } else if #[cfg(not(target_os = "redox"))] { - macro_rules! require_capability { - ($capname:ident) => {} - } - } -} - -#[cfg(target_os = "freebsd")] -macro_rules! skip_if_jailed { - ($name:expr) => { - use ::sysctl::CtlValue; - - if let CtlValue::Int(1) = ::sysctl::value("security.jail.jailed") - .unwrap() - { - skip!("{} cannot run in a jail. Skipping test.", $name); - } - } -} - -#[cfg(not(target_os = "redox"))] -macro_rules! skip_if_not_root { - ($name:expr) => { - use nix::unistd::Uid; - - if !Uid::current().is_root() { - skip!("{} requires root privileges. Skipping test.", $name); - } - }; -} - -cfg_if! { - if #[cfg(any(target_os = "android", target_os = "linux"))] { - macro_rules! skip_if_seccomp { - ($name:expr) => { - if let Ok(s) = std::fs::read_to_string("/proc/self/status") { - for l in s.lines() { - let mut fields = l.split_whitespace(); - if fields.next() == Some("Seccomp:") && - fields.next() != Some("0") - { - skip!("{} cannot be run in Seccomp mode. Skipping test.", - stringify!($name)); - } - } - } - } - } - } else if #[cfg(not(target_os = "redox"))] { - macro_rules! skip_if_seccomp { - ($name:expr) => {} - } - } -} - -cfg_if! { - if #[cfg(target_os = "linux")] { - macro_rules! require_kernel_version { - ($name:expr, $version_requirement:expr) => { - use semver::{Version, VersionReq}; - - let version_requirement = VersionReq::parse($version_requirement) - .expect("Bad match_version provided"); - - let uname = nix::sys::utsname::uname(); - - let mut version = Version::parse(uname.release()).unwrap(); - - //Keep only numeric parts - version.pre.clear(); - version.build.clear(); - - if !version_requirement.matches(&version) { - skip!("Skip {} because kernel version `{}` doesn't match the requirement `{}`", - stringify!($name), version, version_requirement); - } - } - } - } -} - +mod common; mod sys; #[cfg(not(target_os = "redox"))] mod test_dir; @@ -127,7 +23,7 @@ mod test_mq; mod test_net; mod test_nix_path; mod test_poll; -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))] mod test_pty; #[cfg(any(target_os = "android", target_os = "linux"))] @@ -147,6 +43,7 @@ use std::path::PathBuf; use std::sync::{Mutex, RwLock, RwLockWriteGuard}; use nix::unistd::{chdir, getcwd, read}; + /// Helper function analogous to `std::io::Read::read_exact`, but for `RawFD`s fn read_exact(f: RawFd, buf: &mut [u8]) { let mut len = 0; diff --git a/test/test_dir.rs b/test/test_dir.rs index c5f9c51..505277e 100644 --- a/test/test_dir.rs +++ b/test/test_dir.rs @@ -34,7 +34,9 @@ fn rewind() { 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(); assert_eq!(entries1, entries2); + assert_eq!(entries2, entries3); } #[test] diff --git a/test/test_fcntl.rs b/test/test_fcntl.rs index 2971907..5d1bafe 100644 --- a/test/test_fcntl.rs +++ b/test/test_fcntl.rs @@ -19,6 +19,8 @@ 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() { @@ -93,6 +95,8 @@ mod linux_android { use tempfile::{tempfile, NamedTempFile}; + use crate::*; + /// This test creates a temporary file containing the contents /// 'foobarbaz' and uses the `copy_file_range` call to transfer /// 3 bytes at offset 3 (`bar`) to another empty file at offset 0. The @@ -328,7 +332,7 @@ mod linux_android { target_os = "fuchsia", any(target_os = "wasi", target_env = "wasi"), target_env = "uclibc", - target_env = "freebsd"))] + target_os = "freebsd"))] mod test_posix_fadvise { use tempfile::NamedTempFile; diff --git a/test/test_kmod/mod.rs b/test/test_kmod/mod.rs index e7472ab..fb7260b 100644 --- a/test/test_kmod/mod.rs +++ b/test/test_kmod/mod.rs @@ -2,6 +2,7 @@ use std::fs::copy; use std::path::PathBuf; use std::process::Command; use tempfile::{tempdir, TempDir}; +use crate::*; fn compile_kernel_module() -> (PathBuf, String, TempDir) { let _m = crate::FORK_MTX diff --git a/test/test_mount.rs b/test/test_mount.rs index 605276b..c1b6c8a 100644 --- a/test/test_mount.rs +++ b/test/test_mount.rs @@ -1,3 +1,5 @@ +mod common; + // Impelmentation note: to allow unprivileged users to run it, this test makes // use of user and mount namespaces. On systems that allow unprivileged user // namespaces (Linux >= 3.8 compiled with CONFIG_USER_NS), the test should run @@ -222,6 +224,7 @@ fn main() { use test_mount::{setup_namespaces, test_mount_tmpfs_without_flags_allows_rwx, test_mount_rdonly_disallows_write, test_mount_noexec_disallows_exec, test_mount_bind}; + skip_if_cirrus!("Fails for an unknown reason Cirrus CI. Bug #1351"); setup_namespaces(); run_tests!(test_mount_tmpfs_without_flags_allows_rwx, diff --git a/test/test_mq.rs b/test/test_mq.rs index ecee200..1667a35 100644 --- a/test/test_mq.rs +++ b/test/test_mq.rs @@ -1,17 +1,15 @@ -use libc::c_long; - use std::ffi::CString; use std::str; use nix::errno::Errno::*; use nix::Error::Sys; -use nix::mqueue::{mq_open, mq_close, mq_send, mq_receive}; +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; #[test] fn test_mq_send_and_receive() { - const MSG_SIZE: c_long = 32; + const MSG_SIZE: mq_attr_member_t = 32; let attr = MqAttr::new(0, 10, MSG_SIZE, 0); let mq_name= &CString::new(b"/a_nix_test_queue".as_ref()).unwrap(); @@ -43,7 +41,7 @@ fn test_mq_send_and_receive() { #[cfg(not(any(target_os = "netbsd")))] fn test_mq_getattr() { use nix::mqueue::mq_getattr; - const MSG_SIZE: c_long = 32; + const MSG_SIZE: mq_attr_member_t = 32; let initial_attr = MqAttr::new(0, 10, MSG_SIZE, 0); let mq_name = &CString::new(b"/attr_test_get_attr".as_ref()).unwrap(); let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY; @@ -66,7 +64,7 @@ fn test_mq_getattr() { #[cfg_attr(any(target_arch = "mips", target_arch = "mips64"), ignore)] fn test_mq_setattr() { use nix::mqueue::{mq_getattr, mq_setattr}; - const MSG_SIZE: c_long = 32; + const MSG_SIZE: mq_attr_member_t = 32; let initial_attr = MqAttr::new(0, 10, MSG_SIZE, 0); let mq_name = &CString::new(b"/attr_test_get_attr".as_ref()).unwrap(); let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY; @@ -87,7 +85,7 @@ fn test_mq_setattr() { // O_NONBLOCK can be set (see tests below) assert_ne!(new_attr_get, new_attr); - let new_attr_non_blocking = MqAttr::new(MQ_OFlag::O_NONBLOCK.bits() as c_long, 10, MSG_SIZE, 0); + let new_attr_non_blocking = MqAttr::new(MQ_OFlag::O_NONBLOCK.bits() as mq_attr_member_t, 10, MSG_SIZE, 0); mq_setattr(mqd, &new_attr_non_blocking).unwrap(); let new_attr_get = mq_getattr(mqd).unwrap(); @@ -103,7 +101,7 @@ fn test_mq_setattr() { #[cfg_attr(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: c_long = 32; + const MSG_SIZE: mq_attr_member_t = 32; let initial_attr = MqAttr::new(0, 10, MSG_SIZE, 0); let mq_name = &CString::new(b"/attr_test_get_attr".as_ref()).unwrap(); let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY; @@ -116,7 +114,7 @@ fn test_mq_set_nonblocking() { let mqd = r.unwrap(); mq_set_nonblock(mqd).unwrap(); let new_attr = mq_getattr(mqd); - assert_eq!(new_attr.unwrap().flags(), MQ_OFlag::O_NONBLOCK.bits() as c_long); + assert_eq!(new_attr.unwrap().flags(), MQ_OFlag::O_NONBLOCK.bits() as mq_attr_member_t); mq_remove_nonblock(mqd).unwrap(); let new_attr = mq_getattr(mqd); assert_eq!(new_attr.unwrap().flags(), 0); @@ -127,7 +125,7 @@ fn test_mq_set_nonblocking() { #[cfg(not(any(target_os = "netbsd")))] fn test_mq_unlink() { use nix::mqueue::mq_unlink; - const MSG_SIZE: c_long = 32; + const MSG_SIZE: mq_attr_member_t = 32; let initial_attr = MqAttr::new(0, 10, MSG_SIZE, 0); let mq_name_opened = &CString::new(b"/mq_unlink_test".as_ref()).unwrap(); let mq_name_not_opened = &CString::new(b"/mq_unlink_test".as_ref()).unwrap(); diff --git a/test/test_poll.rs b/test/test_poll.rs index d1974ac..a5e2d25 100644 --- a/test/test_poll.rs +++ b/test/test_poll.rs @@ -1,5 +1,21 @@ -use nix::poll::{PollFlags, poll, PollFd}; -use nix::unistd::{write, pipe}; +use nix::{ + Error, + errno::Errno, + poll::{PollFlags, poll, PollFd}, + unistd::{write, pipe} +}; + +macro_rules! loop_while_eintr { + ($poll_expr: expr) => { + loop { + match $poll_expr { + Ok(nfds) => break nfds, + Err(Error::Sys(Errno::EINTR)) => (), + Err(e) => panic!(e) + } + } + } +} #[test] fn test_poll() { @@ -7,7 +23,7 @@ fn test_poll() { let mut fds = [PollFd::new(r, PollFlags::POLLIN)]; // Poll an idle pipe. Should timeout - let nfds = poll(&mut fds, 100).unwrap(); + let nfds = loop_while_eintr!(poll(&mut fds, 100)); assert_eq!(nfds, 0); assert!(!fds[0].revents().unwrap().contains(PollFlags::POLLIN)); @@ -37,7 +53,8 @@ fn test_ppoll() { let mut fds = [PollFd::new(r, PollFlags::POLLIN)]; // Poll an idle pipe. Should timeout - let nfds = ppoll(&mut fds, Some(timeout), SigSet::empty()).unwrap(); + let sigset = SigSet::empty(); + let nfds = loop_while_eintr!(ppoll(&mut fds, Some(timeout), sigset)); assert_eq!(nfds, 0); assert!(!fds[0].revents().unwrap().contains(PollFlags::POLLIN)); diff --git a/test/test_ptymaster_drop.rs b/test/test_ptymaster_drop.rs index 5a5c55d..ff939b9 100644 --- a/test/test_ptymaster_drop.rs +++ b/test/test_ptymaster_drop.rs @@ -1,4 +1,4 @@ -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))] mod t { use nix::fcntl::OFlag; use nix::pty::*; diff --git a/test/test_unistd.rs b/test/test_unistd.rs index c7a75fb..16a8a05 100644 --- a/test/test_unistd.rs +++ b/test/test_unistd.rs @@ -7,7 +7,7 @@ use nix::unistd::ForkResult::*; use nix::sys::signal::{SaFlags, SigAction, SigHandler, SigSet, Signal, sigaction}; use nix::sys::wait::*; use nix::sys::stat::{self, Mode, SFlag}; -#[cfg(not(target_os = "redox"))] +#[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"))] @@ -19,13 +19,14 @@ use std::ffi::CString; use std::fs::DirBuilder; use std::fs::{self, File}; use std::io::Write; -use std::mem; use std::os::unix::prelude::*; #[cfg(not(target_os = "redox"))] use std::path::Path; use tempfile::{tempdir, tempfile}; use libc::{_exit, off_t}; +use crate::*; + #[test] #[cfg(not(any(target_os = "netbsd")))] fn test_fork_and_waitpid() { @@ -200,7 +201,7 @@ mod linux_android { #[test] // `getgroups()` and `setgroups()` do not behave as expected on Apple platforms -#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox")))] +#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox", target_os = "fuchsia")))] fn test_setgroups() { // Skip this test when not run as root as `setgroups()` requires root. skip_if_not_root!("test_setgroups"); @@ -223,7 +224,7 @@ 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")))] +#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox", target_os = "fuchsia")))] fn test_initgroups() { // Skip this test when not run as root as `initgroups()` and `setgroups()` // require root. @@ -259,18 +260,24 @@ macro_rules! execve_test_factory( #[cfg(test)] mod $test_name { + use std::ffi::CStr; use super::*; + const EMPTY: &'static [u8] = b"\0"; + const DASH_C: &'static [u8] = b"-c\0"; + const BIGARG: &'static [u8] = b"echo nix!!! && echo foo=$foo && echo baz=$baz\0"; + const FOO: &'static [u8] = b"foo=bar\0"; + const BAZ: &'static [u8] = b"baz=quux\0"; + fn syscall_cstr_ref() -> Result<std::convert::Infallible, nix::Error> { $syscall( $exe, $(CString::new($pathname).unwrap().as_c_str(), )* - &[CString::new(b"".as_ref()).unwrap().as_c_str(), - CString::new(b"-c".as_ref()).unwrap().as_c_str(), - CString::new(b"echo nix!!! && echo foo=$foo && echo baz=$baz" - .as_ref()).unwrap().as_c_str()], - &[CString::new(b"foo=bar".as_ref()).unwrap().as_c_str(), - CString::new(b"baz=quux".as_ref()).unwrap().as_c_str()] + &[CStr::from_bytes_with_nul(EMPTY).unwrap(), + CStr::from_bytes_with_nul(DASH_C).unwrap(), + CStr::from_bytes_with_nul(BIGARG).unwrap()], + &[CStr::from_bytes_with_nul(FOO).unwrap(), + CStr::from_bytes_with_nul(BAZ).unwrap()] $(, $flags)*) } @@ -278,12 +285,11 @@ macro_rules! execve_test_factory( $syscall( $exe, $(CString::new($pathname).unwrap().as_c_str(), )* - &[CString::new(b"".as_ref()).unwrap(), - CString::new(b"-c".as_ref()).unwrap(), - CString::new(b"echo nix!!! && echo foo=$foo && echo baz=$baz" - .as_ref()).unwrap()], - &[CString::new(b"foo=bar".as_ref()).unwrap(), - CString::new(b"baz=quux".as_ref()).unwrap()] + &[CString::from(CStr::from_bytes_with_nul(EMPTY).unwrap()), + CString::from(CStr::from_bytes_with_nul(DASH_C).unwrap()), + CString::from(CStr::from_bytes_with_nul(BIGARG).unwrap())], + &[CString::from(CStr::from_bytes_with_nul(FOO).unwrap()), + CString::from(CStr::from_bytes_with_nul(BAZ).unwrap())] $(, $flags)*) } @@ -329,11 +335,17 @@ macro_rules! execve_test_factory( } } + // These tests frequently fail on musl, probably due to + // https://github.com/nix-rust/nix/issues/555 + #[cfg_attr(target_env = "musl", ignore)] #[test] fn test_cstr_ref() { common_test(syscall_cstr_ref); } + // These tests frequently fail on musl, probably due to + // https://github.com/nix-rust/nix/issues/555 + #[cfg_attr(target_env = "musl", ignore)] #[test] fn test_cstring() { common_test(syscall_cstring); @@ -349,6 +361,8 @@ cfg_if!{ execve_test_factory!(test_fexecve, fexecve, File::open("/system/bin/sh").unwrap().into_raw_fd()); } else if #[cfg(any(target_os = "freebsd", target_os = "linux"))] { + // These tests frequently fail on musl, probably due to + // https://github.com/nix-rust/nix/issues/555 execve_test_factory!(test_execve, execve, CString::new("/bin/sh").unwrap().as_c_str()); execve_test_factory!(test_fexecve, fexecve, File::open("/bin/sh").unwrap().into_raw_fd()); } else if #[cfg(any(target_os = "dragonfly", @@ -371,13 +385,16 @@ execve_test_factory!(test_execvpe, execvpe, &CString::new("sh").unwrap()); cfg_if!{ if #[cfg(target_os = "android")] { use nix::fcntl::AtFlags; - execve_test_factory!(test_execveat_empty, execveat, File::open("/system/bin/sh").unwrap().into_raw_fd(), + execve_test_factory!(test_execveat_empty, execveat, + File::open("/system/bin/sh").unwrap().into_raw_fd(), "", AtFlags::AT_EMPTY_PATH); - execve_test_factory!(test_execveat_relative, execveat, File::open("/system/bin/").unwrap().into_raw_fd(), + execve_test_factory!(test_execveat_relative, execveat, + File::open("/system/bin/").unwrap().into_raw_fd(), "./sh", AtFlags::empty()); - execve_test_factory!(test_execveat_absolute, execveat, File::open("/").unwrap().into_raw_fd(), + execve_test_factory!(test_execveat_absolute, execveat, + File::open("/").unwrap().into_raw_fd(), "/system/bin/sh", AtFlags::empty()); - } else if #[cfg(all(target_os = "linux"), any(target_arch ="x86_64", target_arch ="x86"))] { + } else if #[cfg(all(target_os = "linux", any(target_arch ="x86_64", target_arch ="x86")))] { use nix::fcntl::AtFlags; execve_test_factory!(test_execveat_empty, execveat, File::open("/bin/sh").unwrap().into_raw_fd(), "", AtFlags::AT_EMPTY_PATH); @@ -389,6 +406,7 @@ cfg_if!{ } #[test] +#[cfg(not(target_os = "fuchsia"))] fn test_fchdir() { // fchdir changes the process's cwd let _dr = crate::DirRestore::new(); @@ -459,9 +477,7 @@ fn test_fchown() { fchown(fd, uid, gid).unwrap(); fchown(fd, uid, None).unwrap(); fchown(fd, None, gid).unwrap(); - - mem::drop(path); - fchown(fd, uid, gid).unwrap_err(); + fchown(999999999, uid, gid).unwrap_err(); } #[test] @@ -537,7 +553,7 @@ cfg_if!{ skip_if_jailed!("test_acct"); } } - } else if #[cfg(not(target_os = "redox"))] { + } else if #[cfg(not(any(target_os = "redox", target_os = "fuchsia")))] { macro_rules! require_acct{ () => { skip_if_not_root!("test_acct"); @@ -547,7 +563,7 @@ cfg_if!{ } #[test] -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))] fn test_acct() { use tempfile::NamedTempFile; use std::process::Command; @@ -634,7 +650,7 @@ fn test_pipe2() { } #[test] -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))] fn test_truncate() { let tempdir = tempdir().unwrap(); let path = tempdir.path().join("file"); @@ -684,6 +700,12 @@ pub extern fn alarm_signal_handler(raw_signal: libc::c_int) { #[test] #[cfg(not(target_os = "redox"))] fn test_alarm() { + use std::{ + time::{Duration, Instant,}, + thread + }; + + // Maybe other tests that fork interfere with this one? let _m = crate::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test"); let handler = SigHandler::Handler(alarm_signal_handler); @@ -701,8 +723,16 @@ fn test_alarm() { // We should be woken up after 1 second by the alarm, so we'll sleep for 2 // seconds to be sure. - sleep(2); - assert_eq!(unsafe { ALARM_CALLED }, true, "expected our alarm signal handler to be called"); + let starttime = Instant::now(); + loop { + thread::sleep(Duration::from_millis(100)); + if unsafe { ALARM_CALLED} { + break; + } + if starttime.elapsed() > Duration::from_secs(3) { + panic!("Timeout waiting for SIGALRM"); + } + } // Reset the signal. unsafe { @@ -976,8 +1006,9 @@ fn test_setfsuid() { let nobody = User::from_name("nobody").unwrap().unwrap(); // create a temporary file with permissions '-rw-r-----' - let file = tempfile::NamedTempFile::new().unwrap(); + let file = tempfile::NamedTempFile::new_in("/var/tmp").unwrap(); let temp_path = file.into_temp_path(); + dbg!(&temp_path); let temp_path_2 = (&temp_path).to_path_buf(); let mut permissions = fs::metadata(&temp_path).unwrap().permissions(); permissions.set_mode(640); @@ -1003,7 +1034,7 @@ fn test_setfsuid() { } #[test] -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))] fn test_ttyname() { let fd = posix_openpt(OFlag::O_RDWR).expect("posix_openpt failed"); assert!(fd.as_raw_fd() > 0); @@ -1026,7 +1057,7 @@ fn test_ttyname() { } #[test] -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))] fn test_ttyname_not_pty() { let fd = File::open("/dev/zero").unwrap(); assert!(fd.as_raw_fd() > 0); @@ -1034,13 +1065,46 @@ fn test_ttyname_not_pty() { } #[test] -#[cfg(all(not(target_os = "redox"), not(target_env = "musl")))] +#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))] fn test_ttyname_invalid_fd() { assert_eq!(ttyname(-1), Err(Error::Sys(Errno::EBADF))); } #[test] -#[cfg(all(not(target_os = "redox"), target_env = "musl"))] -fn test_ttyname_invalid_fd() { - assert_eq!(ttyname(-1), Err(Error::Sys(Errno::ENOTTY))); +#[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "openbsd", + target_os = "netbsd", + target_os = "dragonfly", +))] +fn test_getpeereid() { + use std::os::unix::net::UnixStream; + let (sock_a, sock_b) = UnixStream::pair().unwrap(); + + let (uid_a, gid_a) = getpeereid(sock_a.as_raw_fd()).unwrap(); + let (uid_b, gid_b) = getpeereid(sock_b.as_raw_fd()).unwrap(); + + let uid = geteuid(); + let gid = getegid(); + + assert_eq!(uid, uid_a); + assert_eq!(gid, gid_a); + assert_eq!(uid_a, uid_b); + assert_eq!(gid_a, gid_b); +} + +#[test] +#[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "openbsd", + target_os = "netbsd", + target_os = "dragonfly", +))] +fn test_getpeereid_invalid_fd() { + // getpeereid is not POSIX, so error codes are inconsistent between different Unices. + assert!(getpeereid(-1).is_err()); } |