From 61f371d4fb41c0eed0ac801f0f4575d976ed6d50 Mon Sep 17 00:00:00 2001 From: Andrew Walbran Date: Tue, 14 Nov 2023 16:24:00 +0000 Subject: Upgrade nix to 0.27.1 This project was upgraded with external_updater. Usage: tools/external_updater/updater.sh update rust/crates/nix For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md Bug: 314091492 Test: TreeHugger Change-Id: I4205cd8e7ed49761d570614d37d6f67efaf83644 --- .cargo_vcs_info.json | 2 +- Android.bp | 27 +- CHANGELOG.md | 115 +++- Cargo.toml | 66 +- Cargo.toml.orig | 31 +- METADATA | 10 +- README.md | 103 +-- cargo2android.json | 6 +- patches/unistd.diff | 22 - src/dir.rs | 10 +- src/errno.rs | 247 +++++++ src/fcntl.rs | 331 ++++++--- src/features.rs | 16 +- src/kmod.rs | 8 +- src/lib.rs | 47 +- src/macros.rs | 5 +- src/mount/bsd.rs | 6 +- src/mount/linux.rs | 54 +- src/mqueue.rs | 110 ++- src/net/if_.rs | 2 + src/poll.rs | 52 +- src/pty.rs | 70 +- src/sched.rs | 44 +- src/sys/aio.rs | 18 +- src/sys/epoll.rs | 128 +++- src/sys/event.rs | 213 +++++- src/sys/eventfd.rs | 6 +- src/sys/inotify.rs | 32 +- src/sys/ioctl/linux.rs | 78 +-- src/sys/ioctl/mod.rs | 6 +- src/sys/memfd.rs | 7 +- src/sys/mman.rs | 36 +- src/sys/mod.rs | 9 +- src/sys/personality.rs | 5 +- src/sys/prctl.rs | 208 ++++++ src/sys/ptrace/linux.rs | 18 +- src/sys/ptrace/mod.rs | 2 +- src/sys/quota.rs | 3 +- src/sys/resource.rs | 4 + src/sys/select.rs | 313 ++++++--- src/sys/sendfile.rs | 62 +- src/sys/signal.rs | 333 +++++++-- src/sys/signalfd.rs | 38 +- src/sys/socket/addr.rs | 874 +++++------------------- src/sys/socket/mod.rs | 364 +++++----- src/sys/socket/sockopt.rs | 120 +++- src/sys/stat.rs | 4 +- src/sys/statfs.rs | 6 +- src/sys/statvfs.rs | 7 +- src/sys/termios.rs | 78 ++- src/sys/time.rs | 35 +- src/sys/timerfd.rs | 54 +- src/sys/uio.rs | 106 +-- src/sys/wait.rs | 15 +- src/unistd.rs | 1578 +++++++++++++++++++++++++++++-------------- test/sys/test_aio.rs | 10 +- test/sys/test_epoll.rs | 2 + test/sys/test_mman.rs | 14 +- test/sys/test_prctl.rs | 125 ++++ test/sys/test_select.rs | 31 +- test/sys/test_signal.rs | 12 +- test/sys/test_socket.rs | 713 ++++++++++--------- test/sys/test_sockopt.rs | 135 ++-- test/sys/test_termios.rs | 59 +- test/sys/test_uio.rs | 29 +- test/test.rs | 44 +- test/test_fcntl.rs | 95 +-- test/test_mount.rs | 42 +- test/test_mq.rs | 51 +- test/test_poll.rs | 22 +- test/test_pty.rs | 77 +-- test/test_ptymaster_drop.rs | 20 - test/test_sendfile.rs | 39 +- test/test_unistd.rs | 40 +- 74 files changed, 4658 insertions(+), 3046 deletions(-) delete mode 100644 patches/unistd.diff create mode 100644 src/sys/prctl.rs create mode 100644 test/sys/test_prctl.rs delete mode 100644 test/test_ptymaster_drop.rs diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index b9cb2ab..02451fb 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,6 +1,6 @@ { "git": { - "sha1": "1e3f062fd842b7ce130ea6c792a8eab7f78f82e3" + "sha1": "996db47d542ae20f09eb344b9fcb88c40ae38e3d" }, "path_in_vcs": "" } \ No newline at end of file diff --git a/Android.bp b/Android.bp index 0e94841..1f8cb07 100644 --- a/Android.bp +++ b/Android.bp @@ -26,53 +26,32 @@ rust_library { host_supported: true, crate_name: "nix", cargo_env_compat: true, - cargo_pkg_version: "0.26.2", + cargo_pkg_version: "0.27.1", srcs: ["src/lib.rs"], - edition: "2018", + edition: "2021", features: [ - "acct", - "aio", - "default", - "dir", - "env", "event", "feature", "fs", - "hostname", - "inotify", "ioctl", - "kmod", "memoffset", "mman", "mount", - "mqueue", - "net", - "personality", - "pin-utils", "poll", "process", - "pthread", - "ptrace", - "quota", - "reboot", - "resource", "sched", "signal", "socket", "term", "time", - "ucontext", "uio", "user", - "zerocopy", ], rustlibs: [ - "libbitflags-1.3.2", + "libbitflags", "libcfg_if", "liblibc", "libmemoffset", - "libpin_utils", - "libstatic_assertions", ], apex_available: [ "//apex_available:platform", diff --git a/CHANGELOG.md b/CHANGELOG.md index 283cb86..3a171af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,120 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/). +## [0.27.1] - 2023-08-28 + +### Fixed + +- Fixed generating the documentation on docs.rs. + ([#2111](https://github.com/nix-rust/nix/pull/2111)) + +## [0.27.0] - 2023-08-28 +### Added +- Added `AT_EACCESS` to `AtFlags` on all platforms but android + ([#1995](https://github.com/nix-rust/nix/pull/1995)) +- Add `PF_ROUTE` to `SockType` on macOS, iOS, all of the BSDs, Fuchsia, Haiku, Illumos. + ([#1867](https://github.com/nix-rust/nix/pull/1867)) +- Added `nix::ucontext` module on `aarch64-unknown-linux-gnu`. + (#[1662](https://github.com/nix-rust/nix/pull/1662)) +- Added `CanRaw` to `SockProtocol` and `CanBcm` as a separate `SocProtocol` constant. + ([#1912](https://github.com/nix-rust/nix/pull/1912)) +- Added `Generic` and `NFLOG` to `SockProtocol`. + ([#2092](https://github.com/nix-rust/nix/pull/2092)) +- Added `mq_timedreceive` to `::nix::mqueue`. + ([#1966])(https://github.com/nix-rust/nix/pull/1966) +- Added `LocalPeerPid` to `nix::sys::socket::sockopt` for macOS. ([#1967](https://github.com/nix-rust/nix/pull/1967)) +- Added `TFD_TIMER_CANCEL_ON_SET` to `::nix::sys::time::TimerSetTimeFlags` on Linux and Android. + ([#2040](https://github.com/nix-rust/nix/pull/2040)) +- Added `SOF_TIMESTAMPING_OPT_ID` and `SOF_TIMESTAMPING_OPT_TSONLY` to `nix::sys::socket::TimestampingFlag`. + ([#2048](https://github.com/nix-rust/nix/pull/2048)) +- Enabled socket timestamping options on Android. ([#2077](https://github.com/nix-rust/nix/pull/2077)) +- Added vsock support for macOS ([#2056](https://github.com/nix-rust/nix/pull/2056)) +- Added `SO_SETFIB` and `SO_USER_COOKIE` to `nix::sys::socket::sockopt` for FreeBSD. + ([#2085](https://github.com/nix-rust/nix/pull/2085)) +- Added `SO_RTABLE` for OpenBSD and `SO_ACCEPTFILTER` for FreeBSD/NetBSD to `nix::sys::socket::sockopt`. + ([#2085](https://github.com/nix-rust/nix/pull/2085)) +- Added `MSG_WAITFORONE` to `MsgFlags` on Android, Fuchsia, Linux, NetBSD, + FreeBSD, OpenBSD, and Solaris. + ([#2014](https://github.com/nix-rust/nix/pull/2014)) +- Added `SO_TS_CLOCK` for FreeBSD to `nix::sys::socket::sockopt`. + ([#2093](https://github.com/nix-rust/nix/pull/2093)) +- Added support for prctl in Linux. + (#[1550](https://github.com/nix-rust/nix/pull/1550)) +- `nix::socket` and `nix::select` are now available on Redox. + ([#2012](https://github.com/nix-rust/nix/pull/2012)) +- Implemented AsFd, AsRawFd, FromRawFd, and IntoRawFd for `mqueue::MqdT`. + ([#2097](https://github.com/nix-rust/nix/pull/2097)) +- Add the ability to set `kevent_flags` on `SigEvent`. + ([#1731](https://github.com/nix-rust/nix/pull/1731)) + +### Changed + +- All Cargo features have been removed from the default set. Users will need to + specify which features they depend on in their Cargo.toml. + ([#2091](https://github.com/nix-rust/nix/pull/2091)) +- Implemented I/O safety for many, but not all, of Nix's APIs. Many public + functions argument and return types have changed: + | Original Type | New Type | + | ------------- | --------------------- | + | AsRawFd | AsFd | + | RawFd | BorrowedFd or OwnedFd | + + (#[1906](https://github.com/nix-rust/nix/pull/1906)) +- Use I/O safety with `copy_file_range`, and expose it on FreeBSD. + (#[1906](https://github.com/nix-rust/nix/pull/1906)) +- The MSRV is now 1.65 + ([#1862](https://github.com/nix-rust/nix/pull/1862)) + ([#2104](https://github.com/nix-rust/nix/pull/2104)) +- The epoll interface now uses a type. + ([#1882](https://github.com/nix-rust/nix/pull/1882)) +- With I/O-safe type applied in `pty::OpenptyResult` and `pty::ForkptyResult`, + users no longer need to manually close the file descriptors in these types. + ([#1921](https://github.com/nix-rust/nix/pull/1921)) +- Refactored `name` parameter of `mq_open` and `mq_unlink` to be generic over + `NixPath`. + ([#2102](https://github.com/nix-rust/nix/pull/2102)). +- Made `clone` unsafe, like `fork`. + ([#1993](https://github.com/nix-rust/nix/pull/1993)) + +### Removed + +- `sys::event::{kevent, kevent_ts}` are deprecated in favor of + `sys::kevent::Kqueue::kevent`, and `sys::event::kqueue` is deprecated in + favor of `sys::kevent::Kqueue::new`. + ([#1943](https://github.com/nix-rust/nix/pull/1943)) +- Removed deprecated IoVec API. + ([#1855](https://github.com/nix-rust/nix/pull/1855)) +- Removed deprecated net APIs. + ([#1861](https://github.com/nix-rust/nix/pull/1861)) +- `nix::sys::signalfd::signalfd` is deprecated. Use + `nix::sys::signalfd::SignalFd` instead. + ([#1938](https://github.com/nix-rust/nix/pull/1938)) +- Removed `SigEvent` support on Fuchsia, where it was unsound. + ([#2079](https://github.com/nix-rust/nix/pull/2079)) +- Removed `flock` from `::nix::fcntl` on Solaris. + ([#2082](https://github.com/nix-rust/nix/pull/2082)) + +## [0.26.3] - 2023-08-27 + +### Fixed +- Fix: send `ETH_P_ALL` in htons format + ([#1925](https://github.com/nix-rust/nix/pull/1925)) +- Fix: `recvmsg` now sets the length of the received `sockaddr_un` field + correctly on Linux platforms. ([#2041](https://github.com/nix-rust/nix/pull/2041)) +- Fix potentially invalid conversions in + `SockaddrIn::from`, + `SockaddrIn6::from`, `IpMembershipRequest::new`, and + `Ipv6MembershipRequest::new` with future Rust versions. + ([#2061](https://github.com/nix-rust/nix/pull/2061)) +- Fixed an incorrect lifetime returned from `recvmsg`. + ([#2095](https://github.com/nix-rust/nix/pull/2095)) + ## [0.26.2] - 2023-01-18 + ### Fixed -- Fix `SockaddrIn6` bug that was swapping flowinfo and scope_id byte ordering. + +- Fix `SockaddrIn6` bug that was swapping `flowinfo` and `scope_id` byte + ordering. ([#1964](https://github.com/nix-rust/nix/pull/1964)) ## [0.26.1] - 2022-11-29 @@ -209,7 +320,7 @@ This project adheres to [Semantic Versioning](https://semver.org/). (#[1563](https://github.com/nix-rust/nix/pull/1563)) - Added `process_vm_readv` and `process_vm_writev` on Android. (#[1557](https://github.com/nix-rust/nix/pull/1557)) -- Added `nix::uncontext` module on s390x. +- Added `nix::ucontext` module on s390x. (#[1662](https://github.com/nix-rust/nix/pull/1662)) - Implemented `Extend`, `FromIterator`, and `IntoIterator` for `SigSet` and added `SigSet::iter` and `SigSetIter`. diff --git a/Cargo.toml b/Cargo.toml index 0afc445..bb04ab2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,10 +10,10 @@ # See Cargo.toml.orig for the original contents. [package] -edition = "2018" -rust-version = "1.56" +edition = "2021" +rust-version = "1.65" name = "nix" -version = "0.26.2" +version = "0.27.1" authors = ["The nix-rust Project Developers"] include = [ "src/**/*", @@ -29,6 +29,7 @@ license = "MIT" repository = "https://github.com/nix-rust/nix" [package.metadata.docs.rs] +all-features = true rustdoc-args = [ "--cfg", "docsrs", @@ -65,32 +66,30 @@ path = "test/test_mount.rs" harness = false [[test]] -name = "test-ptymaster-drop" -path = "test/test_ptymaster_drop.rs" +name = "test-prctl" +path = "test/sys/test_prctl.rs" [dependencies.bitflags] -version = "1.1" +version = "2.3.1" [dependencies.cfg-if] version = "1.0" [dependencies.libc] -version = "0.2.137" +version = "0.2.147" features = ["extra_traits"] +[dependencies.memoffset] +version = "0.9" +optional = true + [dependencies.pin-utils] version = "0.1.0" optional = true -[dependencies.static_assertions] -version = "1" - [dev-dependencies.assert-impl] version = "0.1" -[dev-dependencies.lazy_static] -version = "1.4" - [dev-dependencies.parking_lot] version = "0.12" @@ -101,45 +100,12 @@ version = "0.8" version = "1.0.7" [dev-dependencies.tempfile] -version = "3.3.0" +version = "3.7.1" [features] acct = [] aio = ["pin-utils"] -default = [ - "acct", - "aio", - "dir", - "env", - "event", - "feature", - "fs", - "hostname", - "inotify", - "ioctl", - "kmod", - "mman", - "mount", - "mqueue", - "net", - "personality", - "poll", - "process", - "pthread", - "ptrace", - "quota", - "reboot", - "resource", - "sched", - "signal", - "socket", - "term", - "time", - "ucontext", - "uio", - "user", - "zerocopy", -] +default = [] dir = ["fs"] env = [] event = [] @@ -177,9 +143,5 @@ zerocopy = [ [target."cfg(any(target_os = \"android\", target_os = \"linux\"))".dev-dependencies.caps] version = "0.5.3" -[target."cfg(not(target_os = \"redox\"))".dependencies.memoffset] -version = "0.7" -optional = true - [target."cfg(target_os = \"freebsd\")".dev-dependencies.sysctl] version = "0.4" diff --git a/Cargo.toml.orig b/Cargo.toml.orig index 8b1d873..5a78060 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -1,9 +1,9 @@ [package] name = "nix" description = "Rust friendly bindings to *nix APIs" -edition = "2018" -version = "0.26.2" -rust-version = "1.56" +edition = "2021" +version = "0.27.1" +rust-version = "1.65" authors = ["The nix-rust Project Developers"] repository = "https://github.com/nix-rust/nix" license = "MIT" @@ -11,6 +11,7 @@ categories = ["os::unix-apis"] include = ["src/**/*", "test/**/*", "LICENSE", "README.md", "CHANGELOG.md"] [package.metadata.docs.rs] +all-features = true rustdoc-args = ["--cfg", "docsrs"] targets = [ "x86_64-unknown-linux-gnu", @@ -27,23 +28,14 @@ targets = [ ] [dependencies] -libc = { version = "0.2.137", features = [ "extra_traits" ] } -bitflags = "1.1" +libc = { version = "0.2.147", features = ["extra_traits"] } +bitflags = "2.3.1" cfg-if = "1.0" pin-utils = { version = "0.1.0", optional = true } -static_assertions = "1" - -[target.'cfg(not(target_os = "redox"))'.dependencies] -memoffset = { version = "0.7", optional = true } +memoffset = { version = "0.9", optional = true } [features] -default = [ - "acct", "aio", "dir", "env", "event", "feature", "fs", - "hostname", "inotify", "ioctl", "kmod", "mman", "mount", "mqueue", - "net", "personality", "poll", "process", "pthread", "ptrace", "quota", - "reboot", "resource", "sched", "signal", "socket", "term", "time", - "ucontext", "uio", "user", "zerocopy", -] +default = [] acct = [] aio = ["pin-utils"] @@ -80,10 +72,9 @@ zerocopy = ["fs", "uio"] [dev-dependencies] assert-impl = "0.1" -lazy_static = "1.4" parking_lot = "0.12" rand = "0.8" -tempfile = "3.3.0" +tempfile = "3.7.1" semver = "1.0.7" [target.'cfg(any(target_os = "android", target_os = "linux"))'.dev-dependencies] @@ -110,5 +101,5 @@ path = "test/test_mount.rs" harness = false [[test]] -name = "test-ptymaster-drop" -path = "test/test_ptymaster_drop.rs" +name = "test-prctl" +path = "test/sys/test_prctl.rs" diff --git a/METADATA b/METADATA index b1b992d..54b6c34 100644 --- a/METADATA +++ b/METADATA @@ -1,6 +1,6 @@ # This project was upgraded with external_updater. # Usage: tools/external_updater/updater.sh update rust/crates/nix -# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md +# For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md name: "nix" description: "Rust friendly bindings to *nix APIs" @@ -11,13 +11,13 @@ third_party { } url { type: ARCHIVE - value: "https://static.crates.io/crates/nix/nix-0.26.2.crate" + value: "https://static.crates.io/crates/nix/nix-0.27.1.crate" } - version: "0.26.2" + version: "0.27.1" license_type: NOTICE last_upgrade_date { year: 2023 - month: 2 - day: 16 + month: 11 + day: 14 } } diff --git a/README.md b/README.md index 2c42b90..e172de2 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ pub fn gethostname() -> Result; ## Supported Platforms -nix target support consists of two tiers. While nix attempts to support all +nix target support consists of three tiers. While nix attempts to support all platforms supported by [libc](https://github.com/rust-lang/libc), only some platforms are actively supported due to either technical or manpower limitations. Support for platforms is split into three tiers: @@ -41,55 +41,70 @@ limitations. Support for platforms is split into three tiers: blocks the inclusion of new code. Tests may be run, but failures in tests don't block the inclusion of new code. * Tier 3 - Builds for this target are run in CI. Failures during the build - *do not* block the inclusion of new code. Testing may be run, but - failures in tests don't block the inclusion of new code. + *do not* necessarily block the inclusion of new code. That is, at + our discretion a Tier 3 target may be dropped at any time, if it + would otherwise block development. + +Platforms not listed are supported on a best-effort basis, relying on our users +to report any problems. The following targets are supported by `nix`: -Tier 1: - * aarch64-apple-darwin - * aarch64-unknown-linux-gnu - * arm-unknown-linux-gnueabi - * armv7-unknown-linux-gnueabihf - * i686-unknown-freebsd - * i686-unknown-linux-gnu - * i686-unknown-linux-musl - * mips-unknown-linux-gnu - * mips64-unknown-linux-gnuabi64 - * mips64el-unknown-linux-gnuabi64 - * mipsel-unknown-linux-gnu - * powerpc64le-unknown-linux-gnu - * x86_64-unknown-freebsd - * x86_64-unknown-linux-gnu - * x86_64-unknown-linux-musl - -Tier 2: - * aarch64-apple-ios - * aarch64-linux-android - * arm-linux-androideabi - * arm-unknown-linux-musleabi - * armv7-linux-androideabi - * i686-linux-android - * powerpc-unknown-linux-gnu - * s390x-unknown-linux-gnu - * x86_64-apple-ios - * x86_64-linux-android - * x86_64-apple-darwin - * x86_64-unknown-illumos - * x86_64-unknown-netbsd - -Tier 3: - * armv7-unknown-linux-uclibceabihf - * x86_64-fuchsia - * x86_64-unknown-dragonfly - * x86_64-unknown-haiku - * x86_64-unknown-linux-gnux32 - * x86_64-unknown-openbsd - * x86_64-unknown-redox + + + + + + + + + + + +
Tier 1Tier 2Tier 3
+
    +
  • aarch64-apple-darwin
  • +
  • aarch64-unknown-linux-gnu
  • +
  • arm-unknown-linux-gnueabi
  • +
  • armv7-unknown-linux-gnueabihf
  • +
  • i686-unknown-freebsd
  • +
  • i686-unknown-linux-gnu
  • +
  • i686-unknown-linux-musl
  • +
  • mips-unknown-linux-gnu
  • +
  • mips64-unknown-linux-gnuabi64
  • +
  • mips64el-unknown-linux-gnuabi64
  • +
  • mipsel-unknown-linux-gnu
  • +
  • powerpc64le-unknown-linux-gnu
  • +
  • x86_64-unknown-freebsd
  • +
  • x86_64-unknown-linux-gnu
  • +
  • x86_64-unknown-linux-musl
  • +
+
+
    +
  • aarch64-apple-ios
  • +
  • aarch64-linux-android
  • +
  • arm-linux-androideabi
  • +
  • arm-unknown-linux-musleabi
  • +
  • armv7-linux-androideabi
  • +
  • i686-linux-android
  • +
  • s390x-unknown-linux-gnu
  • +
  • x86_64-linux-android
  • +
  • x86_64-unknown-illumos
  • +
  • x86_64-unknown-netbsd
  • +
+
  • armv7-unknown-linux-uclibceabihf
  • +
  • powerpc64-unknown-linux-gnu
  • +
  • x86_64-fuchsia
  • +
  • x86_64-unknown-dragonfly
  • +
  • x86_64-unknown-haiku
  • +
  • x86_64-unknown-linux-gnux32
  • +
  • x86_64-unknown-openbsd
  • +
  • x86_64-unknown-redox
  • +
    ## Minimum Supported Rust Version (MSRV) -nix is supported on Rust 1.56.1 and higher. Its MSRV will not be +nix is supported on Rust 1.65 and higher. Its MSRV will not be changed in the future without bumping the major or minor version. ## Contributing diff --git a/cargo2android.json b/cargo2android.json index 1bd5b8b..4045280 100644 --- a/cargo2android.json +++ b/cargo2android.json @@ -1,10 +1,8 @@ { "dependencies": true, "device": true, + "features": "event,feature,fs,ioctl,mount,mman,poll,sched,signal,socket,term,time,uio,user", "min-sdk-version": "29", "run": true, - "vendor-available": true, - "dep-suffixes": { - "bitflags": "-1.3.2" - } + "vendor-available": true } diff --git a/patches/unistd.diff b/patches/unistd.diff deleted file mode 100644 index 4815309..0000000 --- a/patches/unistd.diff +++ /dev/null @@ -1,22 +0,0 @@ -diff --git a/src/unistd.rs b/src/unistd.rs -index 42e1456..8cdb54b 100644 ---- a/src/unistd.rs -+++ b/src/unistd.rs -@@ -2984,12 +2984,12 @@ impl From<&libc::passwd> for User { - fn from(pw: &libc::passwd) -> User { - unsafe { - User { -- name: CStr::from_ptr(pw.pw_name).to_string_lossy().into_owned(), -- passwd: CString::new(CStr::from_ptr(pw.pw_passwd).to_bytes()).unwrap(), -+ name: if pw.pw_name.is_null() { Default::default() } else { CStr::from_ptr(pw.pw_name).to_string_lossy().into_owned() }, -+ passwd: if pw.pw_passwd.is_null() { Default::default() } else { CString::new(CStr::from_ptr(pw.pw_passwd).to_bytes()).unwrap() }, - #[cfg(not(all(target_os = "android", target_pointer_width = "32")))] -- gecos: CString::new(CStr::from_ptr(pw.pw_gecos).to_bytes()).unwrap(), -- dir: PathBuf::from(OsStr::from_bytes(CStr::from_ptr(pw.pw_dir).to_bytes())), -- shell: PathBuf::from(OsStr::from_bytes(CStr::from_ptr(pw.pw_shell).to_bytes())), -+ gecos: if pw.pw_gecos.is_null() { Default::default() } else { CString::new(CStr::from_ptr(pw.pw_gecos).to_bytes()).unwrap() }, -+ dir: if pw.pw_dir.is_null() { Default::default() } else { PathBuf::from(OsStr::from_bytes(CStr::from_ptr(pw.pw_dir).to_bytes())) }, -+ shell: if pw.pw_shell.is_null() { Default::default() } else { PathBuf::from(OsStr::from_bytes(CStr::from_ptr(pw.pw_shell).to_bytes())) }, - uid: Uid::from_raw(pw.pw_uid), - gid: Gid::from_raw(pw.pw_gid), - #[cfg(not(any(target_os = "android", diff --git a/src/dir.rs b/src/dir.rs index 5ce5036..96a5843 100644 --- a/src/dir.rs +++ b/src/dir.rs @@ -101,6 +101,9 @@ impl Drop for Dir { } } +// The pass by mut is technically needless only because the inner NonNull is +// Copy. But philosophically we're mutating the Dir, so we pass by mut. +#[allow(clippy::needless_pass_by_ref_mut)] fn next(dir: &mut Dir) -> Option> { unsafe { // Note: POSIX specifies that portable applications should dynamically allocate a @@ -221,7 +224,8 @@ impl Entry { #[allow(clippy::unnecessary_cast)] pub fn ino(&self) -> u64 { cfg_if! { - if #[cfg(any(target_os = "android", + if #[cfg(any(target_os = "aix", + target_os = "android", target_os = "emscripten", target_os = "fuchsia", target_os = "haiku", @@ -240,7 +244,7 @@ impl Entry { /// Returns the bare file name of this directory entry without any other leading path component. pub fn file_name(&self) -> &ffi::CStr { - unsafe { ::std::ffi::CStr::from_ptr(self.0.d_name.as_ptr()) } + unsafe { ffi::CStr::from_ptr(self.0.d_name.as_ptr()) } } /// Returns the type of this directory entry, if known. @@ -250,6 +254,7 @@ impl Entry { /// `fstat` if this returns `None`. pub fn file_type(&self) -> Option { #[cfg(not(any( + target_os = "aix", target_os = "illumos", target_os = "solaris", target_os = "haiku" @@ -267,6 +272,7 @@ impl Entry { // illumos, Solaris, and Haiku systems do not have the d_type member at all: #[cfg(any( + target_os = "aix", target_os = "illumos", target_os = "solaris", target_os = "haiku" diff --git a/src/errno.rs b/src/errno.rs index d8ad28d..50b3524 100644 --- a/src/errno.rs +++ b/src/errno.rs @@ -34,6 +34,10 @@ cfg_if! { unsafe fn errno_location() -> *mut c_int { libc::_errnop() } + } else if #[cfg(any(target_os = "aix"))] { + unsafe fn errno_location() -> *mut c_int { + libc::_Errno() + } } } @@ -223,6 +227,7 @@ fn desc(errno: Errno) -> &'static str { #[cfg(any( target_os = "linux", target_os = "android", + target_os = "aix", target_os = "illumos", target_os = "solaris", target_os = "fuchsia" @@ -232,6 +237,7 @@ fn desc(errno: Errno) -> &'static str { #[cfg(any( target_os = "linux", target_os = "android", + target_os = "aix", target_os = "illumos", target_os = "solaris", target_os = "fuchsia" @@ -241,6 +247,7 @@ fn desc(errno: Errno) -> &'static str { #[cfg(any( target_os = "linux", target_os = "android", + target_os = "aix", target_os = "illumos", target_os = "solaris", target_os = "fuchsia" @@ -250,6 +257,7 @@ fn desc(errno: Errno) -> &'static str { #[cfg(any( target_os = "linux", target_os = "android", + target_os = "aix", target_os = "illumos", target_os = "solaris", target_os = "fuchsia" @@ -259,6 +267,7 @@ fn desc(errno: Errno) -> &'static str { #[cfg(any( target_os = "linux", target_os = "android", + target_os = "aix", target_os = "illumos", target_os = "solaris", target_os = "fuchsia" @@ -268,6 +277,7 @@ fn desc(errno: Errno) -> &'static str { #[cfg(any( target_os = "linux", target_os = "android", + target_os = "aix", target_os = "illumos", target_os = "solaris", target_os = "fuchsia" @@ -277,6 +287,7 @@ fn desc(errno: Errno) -> &'static str { #[cfg(any( target_os = "linux", target_os = "android", + target_os = "aix", target_os = "illumos", target_os = "solaris", target_os = "fuchsia" @@ -286,6 +297,7 @@ fn desc(errno: Errno) -> &'static str { #[cfg(any( target_os = "linux", target_os = "android", + target_os = "aix", target_os = "illumos", target_os = "solaris", target_os = "fuchsia" @@ -421,6 +433,7 @@ fn desc(errno: Errno) -> &'static str { #[cfg(any( target_os = "linux", target_os = "android", + target_os = "aix", target_os = "illumos", target_os = "solaris", target_os = "fuchsia" @@ -457,6 +470,7 @@ fn desc(errno: Errno) -> &'static str { #[cfg(any( target_os = "linux", target_os = "android", + target_os = "aix", target_os = "illumos", target_os = "solaris", target_os = "fuchsia" @@ -466,6 +480,7 @@ fn desc(errno: Errno) -> &'static str { #[cfg(any( target_os = "linux", target_os = "android", + target_os = "aix", target_os = "illumos", target_os = "solaris", target_os = "fuchsia" @@ -482,6 +497,7 @@ fn desc(errno: Errno) -> &'static str { #[cfg(any( target_os = "linux", target_os = "android", + target_os = "aix", target_os = "fuchsia" ))] EBADMSG => "Not a data message", @@ -492,6 +508,7 @@ fn desc(errno: Errno) -> &'static str { #[cfg(any( target_os = "linux", target_os = "android", + target_os = "aix", target_os = "fuchsia", target_os = "haiku" ))] @@ -572,6 +589,7 @@ fn desc(errno: Errno) -> &'static str { #[cfg(any( target_os = "linux", target_os = "android", + target_os = "aix", target_os = "illumos", target_os = "solaris", target_os = "fuchsia", @@ -582,6 +600,7 @@ fn desc(errno: Errno) -> &'static str { #[cfg(any( target_os = "linux", target_os = "android", + target_os = "aix", target_os = "illumos", target_os = "solaris", target_os = "fuchsia" @@ -722,6 +741,7 @@ fn desc(errno: Errno) -> &'static str { #[cfg(any( target_os = "linux", target_os = "android", + target_os = "aix", target_os = "fuchsia" ))] EOWNERDEAD => "Owner died", @@ -732,6 +752,7 @@ fn desc(errno: Errno) -> &'static str { #[cfg(any( target_os = "linux", target_os = "android", + target_os = "aix", target_os = "fuchsia" ))] ENOTRECOVERABLE => "State not recoverable", @@ -868,6 +889,7 @@ fn desc(errno: Errno) -> &'static str { target_os = "ios", target_os = "openbsd", target_os = "netbsd", + target_os = "aix", target_os = "illumos", target_os = "solaris", target_os = "haiku" @@ -879,6 +901,7 @@ fn desc(errno: Errno) -> &'static str { target_os = "freebsd", target_os = "dragonfly", target_os = "ios", + target_os = "aix", target_os = "openbsd", target_os = "netbsd" ))] @@ -889,6 +912,7 @@ fn desc(errno: Errno) -> &'static str { target_os = "freebsd", target_os = "dragonfly", target_os = "ios", + target_os = "aix", target_os = "openbsd", target_os = "netbsd", target_os = "redox" @@ -903,6 +927,7 @@ fn desc(errno: Errno) -> &'static str { target_os = "openbsd", target_os = "netbsd", target_os = "redox", + target_os = "aix", target_os = "illumos", target_os = "solaris", target_os = "haiku" @@ -917,6 +942,7 @@ fn desc(errno: Errno) -> &'static str { target_os = "openbsd", target_os = "netbsd", target_os = "redox", + target_os = "aix", target_os = "illumos", target_os = "solaris", target_os = "haiku" @@ -928,6 +954,7 @@ fn desc(errno: Errno) -> &'static str { target_os = "freebsd", target_os = "dragonfly", target_os = "ios", + target_os = "aix", target_os = "openbsd", target_os = "netbsd", target_os = "redox" @@ -1009,6 +1036,7 @@ fn desc(errno: Errno) -> &'static str { target_os = "freebsd", target_os = "dragonfly", target_os = "ios", + target_os = "aix", target_os = "openbsd", target_os = "netbsd", target_os = "redox" @@ -1044,6 +1072,7 @@ fn desc(errno: Errno) -> &'static str { #[cfg(any( target_os = "macos", target_os = "ios", + target_os = "aix", target_os = "netbsd", target_os = "redox" ))] @@ -1060,6 +1089,7 @@ fn desc(errno: Errno) -> &'static str { #[cfg(any( target_os = "macos", target_os = "ios", + target_os = "aix", target_os = "netbsd", target_os = "redox" ))] @@ -1068,6 +1098,7 @@ fn desc(errno: Errno) -> &'static str { #[cfg(any( target_os = "macos", target_os = "ios", + target_os = "aix", target_os = "netbsd", target_os = "redox" ))] @@ -1076,6 +1107,7 @@ fn desc(errno: Errno) -> &'static str { #[cfg(any( target_os = "macos", target_os = "ios", + target_os = "aix", target_os = "netbsd", target_os = "redox" ))] @@ -1084,6 +1116,7 @@ fn desc(errno: Errno) -> &'static str { #[cfg(any( target_os = "macos", target_os = "ios", + target_os = "aix", target_os = "illumos", target_os = "solaris" ))] @@ -3131,3 +3164,217 @@ mod consts { } } } + +#[cfg(target_os = "aix")] +mod consts { + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + #[repr(i32)] + #[non_exhaustive] + pub enum Errno { + UnknownErrno = 0, + EPERM = libc::EPERM, + ENOENT = libc::ENOENT, + ESRCH = libc::ESRCH, + EINTR = libc::EINTR, + EIO = libc::EIO, + ENXIO = libc::ENXIO, + E2BIG = libc::E2BIG, + ENOEXEC = libc::ENOEXEC, + EBADF = libc::EBADF, + ECHILD = libc::ECHILD, + EAGAIN = libc::EAGAIN, + ENOMEM = libc::ENOMEM, + EACCES = libc::EACCES, + EFAULT = libc::EFAULT, + ENOTBLK = libc::ENOTBLK, + EBUSY = libc::EBUSY, + EEXIST = libc::EEXIST, + EXDEV = libc::EXDEV, + ENODEV = libc::ENODEV, + ENOTDIR = libc::ENOTDIR, + EISDIR = libc::EISDIR, + EINVAL = libc::EINVAL, + ENFILE = libc::ENFILE, + EMFILE = libc::EMFILE, + ENOTTY = libc::ENOTTY, + ETXTBSY = libc::ETXTBSY, + EFBIG = libc::EFBIG, + ENOSPC = libc::ENOSPC, + ESPIPE = libc::ESPIPE, + EROFS = libc::EROFS, + EMLINK = libc::EMLINK, + EPIPE = libc::EPIPE, + EDOM = libc::EDOM, + ERANGE = libc::ERANGE, + EDEADLK = libc::EDEADLK, + ENAMETOOLONG = libc::ENAMETOOLONG, + ENOLCK = libc::ENOLCK, + ENOSYS = libc::ENOSYS, + ENOTEMPTY = libc::ENOTEMPTY, + ELOOP = libc::ELOOP, + ENOMSG = libc::ENOMSG, + EIDRM = libc::EIDRM, + EINPROGRESS = libc::EINPROGRESS, + EALREADY = libc::EALREADY, + ENOTSOCK = libc::ENOTSOCK, + EDESTADDRREQ = libc::EDESTADDRREQ, + EMSGSIZE = libc::EMSGSIZE, + EPROTOTYPE = libc::EPROTOTYPE, + ENOPROTOOPT = libc::ENOPROTOOPT, + EPROTONOSUPPORT = libc::EPROTONOSUPPORT, + ESOCKTNOSUPPORT = libc::ESOCKTNOSUPPORT, + EPFNOSUPPORT = libc::EPFNOSUPPORT, + EAFNOSUPPORT = libc::EAFNOSUPPORT, + EADDRINUSE = libc::EADDRINUSE, + EADDRNOTAVAIL = libc::EADDRNOTAVAIL, + ENETDOWN = libc::ENETDOWN, + ENETUNREACH = libc::ENETUNREACH, + ENETRESET = libc::ENETRESET, + ECONNABORTED = libc::ECONNABORTED, + ECONNRESET = libc::ECONNRESET, + ENOBUFS = libc::ENOBUFS, + EISCONN = libc::EISCONN, + ENOTCONN = libc::ENOTCONN, + ESHUTDOWN = libc::ESHUTDOWN, + ETOOMANYREFS = libc::ETOOMANYREFS, + ETIMEDOUT = libc::ETIMEDOUT, + ECONNREFUSED = libc::ECONNREFUSED, + EHOSTDOWN = libc::EHOSTDOWN, + EHOSTUNREACH = libc::EHOSTUNREACH, + ECHRNG = libc::ECHRNG, + EL2NSYNC = libc::EL2NSYNC, + EL3HLT = libc::EL3HLT, + EL3RST = libc::EL3RST, + ELNRNG = libc::ELNRNG, + EUNATCH = libc::EUNATCH, + ENOCSI = libc::ENOCSI, + EL2HLT = libc::EL2HLT, + ENOLINK = libc::ENOLINK, + EPROTO = libc::EPROTO, + EMULTIHOP = libc::EMULTIHOP, + EBADMSG = libc::EBADMSG, + EOVERFLOW = libc::EOVERFLOW, + EILSEQ = libc::EILSEQ, + ERESTART = libc::ERESTART, + EOWNERDEAD = libc::EOWNERDEAD, + ENOTRECOVERABLE = libc::ENOTRECOVERABLE, + ENOTSUP = libc::ENOTSUP, + EPROCLIM = libc::EPROCLIM, + EUSERS = libc::EUSERS, + EDQUOT = libc::EDQUOT, + ESTALE = libc::ESTALE, + EREMOTE = libc::EREMOTE, + ECANCELED = libc::ECANCELED, + ENODATA = libc::ENODATA, + ENOSR = libc::ENOSR, + ENOSTR = libc::ENOSTR, + ETIME = libc::ETIME, + EOPNOTSUPP = libc::EOPNOTSUPP, + } + + pub const fn from_i32(e: i32) -> Errno { + use self::Errno::*; + + match e { + libc::EPERM => EPERM, + libc::ENOENT => ENOENT, + libc::ESRCH => ESRCH, + libc::EINTR => EINTR, + libc::EIO => EIO, + libc::ENXIO => ENXIO, + libc::E2BIG => E2BIG, + libc::ENOEXEC => ENOEXEC, + libc::EBADF => EBADF, + libc::ECHILD => ECHILD, + libc::EAGAIN => EAGAIN, + libc::ENOMEM => ENOMEM, + libc::EACCES => EACCES, + libc::EFAULT => EFAULT, + libc::ENOTBLK => ENOTBLK, + libc::EBUSY => EBUSY, + libc::EEXIST => EEXIST, + libc::EXDEV => EXDEV, + libc::ENODEV => ENODEV, + libc::ENOTDIR => ENOTDIR, + libc::EISDIR => EISDIR, + libc::EINVAL => EINVAL, + libc::ENFILE => ENFILE, + libc::EMFILE => EMFILE, + libc::ENOTTY => ENOTTY, + libc::ETXTBSY => ETXTBSY, + libc::EFBIG => EFBIG, + libc::ENOSPC => ENOSPC, + libc::ESPIPE => ESPIPE, + libc::EROFS => EROFS, + libc::EMLINK => EMLINK, + libc::EPIPE => EPIPE, + libc::EDOM => EDOM, + libc::ERANGE => ERANGE, + libc::EDEADLK => EDEADLK, + libc::ENAMETOOLONG => ENAMETOOLONG, + libc::ENOLCK => ENOLCK, + libc::ENOSYS => ENOSYS, + libc::ENOTEMPTY => ENOTEMPTY, + libc::ELOOP => ELOOP, + libc::ENOMSG => ENOMSG, + libc::EIDRM => EIDRM, + libc::EINPROGRESS => EINPROGRESS, + libc::EALREADY => EALREADY, + libc::ENOTSOCK => ENOTSOCK, + libc::EDESTADDRREQ => EDESTADDRREQ, + libc::EMSGSIZE => EMSGSIZE, + libc::EPROTOTYPE => EPROTOTYPE, + libc::ENOPROTOOPT => ENOPROTOOPT, + libc::EPROTONOSUPPORT => EPROTONOSUPPORT, + libc::ESOCKTNOSUPPORT => ESOCKTNOSUPPORT, + libc::EPFNOSUPPORT => EPFNOSUPPORT, + libc::EAFNOSUPPORT => EAFNOSUPPORT, + libc::EADDRINUSE => EADDRINUSE, + libc::EADDRNOTAVAIL => EADDRNOTAVAIL, + libc::ENETDOWN => ENETDOWN, + libc::ENETUNREACH => ENETUNREACH, + libc::ENETRESET => ENETRESET, + libc::ECONNABORTED => ECONNABORTED, + libc::ECONNRESET => ECONNRESET, + libc::ENOBUFS => ENOBUFS, + libc::EISCONN => EISCONN, + libc::ENOTCONN => ENOTCONN, + libc::ESHUTDOWN => ESHUTDOWN, + libc::ETOOMANYREFS => ETOOMANYREFS, + libc::ETIMEDOUT => ETIMEDOUT, + libc::ECONNREFUSED => ECONNREFUSED, + libc::EHOSTDOWN => EHOSTDOWN, + libc::EHOSTUNREACH => EHOSTUNREACH, + libc::ECHRNG => ECHRNG, + libc::EL2NSYNC => EL2NSYNC, + libc::EL3HLT => EL3HLT, + libc::EL3RST => EL3RST, + libc::ELNRNG => ELNRNG, + libc::EUNATCH => EUNATCH, + libc::ENOCSI => ENOCSI, + libc::EL2HLT => EL2HLT, + libc::ENOLINK => ENOLINK, + libc::EPROTO => EPROTO, + libc::EMULTIHOP => EMULTIHOP, + libc::EBADMSG => EBADMSG, + libc::EOVERFLOW => EOVERFLOW, + libc::EILSEQ => EILSEQ, + libc::ERESTART => ERESTART, + libc::ENOTRECOVERABLE => ENOTRECOVERABLE, + libc::EOWNERDEAD => EOWNERDEAD, + libc::ENOTSUP => ENOTSUP, + libc::EPROCLIM => EPROCLIM, + libc::EUSERS => EUSERS, + libc::EDQUOT => EDQUOT, + libc::ESTALE => ESTALE, + libc::EREMOTE => EREMOTE, + libc::ECANCELED => ECANCELED, + libc::ENODATA => ENODATA, + libc::ENOSR => ENOSR, + libc::ENOSTR => ENOSTR, + libc::ETIME => ETIME, + libc::EOPNOTSUPP => EOPNOTSUPP, + _ => UnknownErrno, + } + } +} diff --git a/src/fcntl.rs b/src/fcntl.rs index 6508283..9bfecda 100644 --- a/src/fcntl.rs +++ b/src/fcntl.rs @@ -5,11 +5,19 @@ use std::ffi::OsString; use std::os::raw; use std::os::unix::ffi::OsStringExt; use std::os::unix::io::RawFd; +// For splice and copy_file_range +#[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "linux" +))] +use std::{ + os::unix::io::{AsFd, AsRawFd}, + ptr, +}; #[cfg(feature = "fs")] use crate::{sys::stat::Mode, NixPath, Result}; -#[cfg(any(target_os = "android", target_os = "linux"))] -use std::ptr; // For splice and copy_file_range #[cfg(any( target_os = "linux", @@ -35,7 +43,7 @@ libc_bitflags! { AT_NO_AUTOMOUNT; #[cfg(any(target_os = "android", target_os = "linux"))] AT_EMPTY_PATH; - #[cfg(any(target_os = "illumos", target_os = "solaris"))] + #[cfg(not(target_os = "android"))] AT_EACCESS; } } @@ -54,7 +62,10 @@ libc_bitflags!( /// Open the file in append-only mode. O_APPEND; /// Generate a signal when input or output becomes possible. - #[cfg(not(any(target_os = "illumos", target_os = "solaris", target_os = "haiku")))] + #[cfg(not(any(target_os = "aix", + target_os = "illumos", + target_os = "solaris", + target_os = "haiku")))] #[cfg_attr(docsrs, doc(cfg(all())))] O_ASYNC; /// Closes the file descriptor once an `execve` call is made. @@ -193,9 +204,13 @@ feature! { // The conversion is not identical on all operating systems. #[allow(clippy::useless_conversion)] -pub fn open(path: &P, oflag: OFlag, mode: Mode) -> Result { - let fd = path.with_nix_path(|cstr| { - unsafe { libc::open(cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) } +pub fn open( + path: &P, + oflag: OFlag, + mode: Mode, +) -> Result { + let fd = path.with_nix_path(|cstr| unsafe { + libc::open(cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) })?; Errno::result(fd) @@ -210,8 +225,8 @@ pub fn openat( oflag: OFlag, mode: Mode, ) -> Result { - let fd = path.with_nix_path(|cstr| { - unsafe { libc::openat(dirfd, cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) } + let fd = path.with_nix_path(|cstr| unsafe { + libc::openat(dirfd, cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) })?; Errno::result(fd) } @@ -237,7 +252,7 @@ pub fn renameat( } } -#[cfg(all(target_os = "linux", target_env = "gnu",))] +#[cfg(all(target_os = "linux", target_env = "gnu"))] #[cfg(feature = "fs")] libc_bitflags! { #[cfg_attr(docsrs, doc(cfg(feature = "fs")))] @@ -250,10 +265,7 @@ libc_bitflags! { feature! { #![feature = "fs"] -#[cfg(all( - target_os = "linux", - target_env = "gnu", -))] +#[cfg(all(target_os = "linux", target_env = "gnu"))] pub fn renameat2( old_dirfd: Option, old_path: &P1, @@ -306,56 +318,85 @@ fn readlink_maybe_at( }) } -fn inner_readlink(dirfd: Option, path: &P) -> Result { +fn inner_readlink( + dirfd: Option, + path: &P, +) -> Result { let mut v = Vec::with_capacity(libc::PATH_MAX as usize); - // simple case: result is strictly less than `PATH_MAX` - let res = readlink_maybe_at(dirfd, path, &mut v)?; - let len = Errno::result(res)?; - debug_assert!(len >= 0); - if (len as usize) < v.capacity() { - return wrap_readlink_result(v, res); + + { + // simple case: result is strictly less than `PATH_MAX` + let res = readlink_maybe_at(dirfd, path, &mut v)?; + let len = Errno::result(res)?; + debug_assert!(len >= 0); + if (len as usize) < v.capacity() { + return wrap_readlink_result(v, res); + } } + // Uh oh, the result is too long... // Let's try to ask lstat how many bytes to allocate. - let reported_size = match dirfd { - #[cfg(target_os = "redox")] - Some(_) => unreachable!(), - #[cfg(any(target_os = "android", target_os = "linux"))] - Some(dirfd) => { - let flags = if path.is_empty() { AtFlags::AT_EMPTY_PATH } else { AtFlags::empty() }; - super::sys::stat::fstatat(dirfd, path, flags | AtFlags::AT_SYMLINK_NOFOLLOW) - }, - #[cfg(not(any(target_os = "android", target_os = "linux", target_os = "redox")))] - Some(dirfd) => super::sys::stat::fstatat(dirfd, path, AtFlags::AT_SYMLINK_NOFOLLOW), - None => super::sys::stat::lstat(path) - } + let mut try_size = { + let reported_size = match dirfd { + #[cfg(target_os = "redox")] + Some(_) => unreachable!(), + #[cfg(any(target_os = "android", target_os = "linux"))] + Some(dirfd) => { + let flags = if path.is_empty() { + AtFlags::AT_EMPTY_PATH + } else { + AtFlags::empty() + }; + super::sys::stat::fstatat( + dirfd, + path, + flags | AtFlags::AT_SYMLINK_NOFOLLOW, + ) + } + #[cfg(not(any( + target_os = "android", + target_os = "linux", + target_os = "redox" + )))] + Some(dirfd) => super::sys::stat::fstatat( + dirfd, + path, + AtFlags::AT_SYMLINK_NOFOLLOW, + ), + None => super::sys::stat::lstat(path), + } .map(|x| x.st_size) .unwrap_or(0); - let mut try_size = if reported_size > 0 { - // Note: even if `lstat`'s apparently valid answer turns out to be - // wrong, we will still read the full symlink no matter what. - reported_size as usize + 1 - } else { - // If lstat doesn't cooperate, or reports an error, be a little less - // precise. - (libc::PATH_MAX as usize).max(128) << 1 + + if reported_size > 0 { + // Note: even if `lstat`'s apparently valid answer turns out to be + // wrong, we will still read the full symlink no matter what. + reported_size as usize + 1 + } else { + // If lstat doesn't cooperate, or reports an error, be a little less + // precise. + (libc::PATH_MAX as usize).max(128) << 1 + } }; + loop { - v.reserve_exact(try_size); - let res = readlink_maybe_at(dirfd, path, &mut v)?; - let len = Errno::result(res)?; - debug_assert!(len >= 0); - if (len as usize) < v.capacity() { - break wrap_readlink_result(v, res); - } else { - // Ugh! Still not big enough! - match try_size.checked_shl(1) { - Some(next_size) => try_size = next_size, - // It's absurd that this would happen, but handle it sanely - // anyway. - None => break Err(Errno::ENAMETOOLONG), + { + v.reserve_exact(try_size); + let res = readlink_maybe_at(dirfd, path, &mut v)?; + let len = Errno::result(res)?; + debug_assert!(len >= 0); + if (len as usize) < v.capacity() { + return wrap_readlink_result(v, res); } } + + // Ugh! Still not big enough! + match try_size.checked_shl(1) { + Some(next_size) => try_size = next_size, + // It's absurd that this would happen, but handle it sanely + // anyway. + None => break Err(Errno::ENAMETOOLONG), + } } } @@ -364,7 +405,10 @@ pub fn readlink(path: &P) -> Result { } #[cfg(not(target_os = "redox"))] -pub fn readlinkat(dirfd: RawFd, path: &P) -> Result { +pub fn readlinkat( + dirfd: RawFd, + path: &P, +) -> Result { inner_readlink(Some(dirfd), path) } @@ -427,9 +471,17 @@ pub enum FcntlArg<'a> { F_OFD_SETLKW(&'a libc::flock), #[cfg(any(target_os = "linux", target_os = "android"))] F_OFD_GETLK(&'a mut libc::flock), - #[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))] + #[cfg(any( + target_os = "android", + target_os = "linux", + target_os = "freebsd" + ))] F_ADD_SEALS(SealFlag), - #[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))] + #[cfg(any( + target_os = "android", + target_os = "linux", + target_os = "freebsd" + ))] F_GET_SEALS, #[cfg(any(target_os = "macos", target_os = "ios"))] F_FULLFSYNC, @@ -458,7 +510,9 @@ pub fn fcntl(fd: RawFd, arg: FcntlArg) -> Result { let res = unsafe { match arg { F_DUPFD(rawfd) => libc::fcntl(fd, libc::F_DUPFD, rawfd), - F_DUPFD_CLOEXEC(rawfd) => libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, rawfd), + F_DUPFD_CLOEXEC(rawfd) => { + libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, rawfd) + } F_GETFD => libc::fcntl(fd, libc::F_GETFD), F_SETFD(flag) => libc::fcntl(fd, libc::F_SETFD, flag.bits()), F_GETFL => libc::fcntl(fd, libc::F_GETFL), @@ -475,9 +529,19 @@ pub fn fcntl(fd: RawFd, arg: FcntlArg) -> Result { F_OFD_SETLKW(flock) => libc::fcntl(fd, libc::F_OFD_SETLKW, flock), #[cfg(any(target_os = "android", target_os = "linux"))] F_OFD_GETLK(flock) => libc::fcntl(fd, libc::F_OFD_GETLK, flock), - #[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))] - F_ADD_SEALS(flag) => libc::fcntl(fd, libc::F_ADD_SEALS, flag.bits()), - #[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))] + #[cfg(any( + target_os = "android", + target_os = "linux", + target_os = "freebsd" + ))] + F_ADD_SEALS(flag) => { + libc::fcntl(fd, libc::F_ADD_SEALS, flag.bits()) + } + #[cfg(any( + target_os = "android", + target_os = "linux", + target_os = "freebsd" + ))] F_GET_SEALS => libc::fcntl(fd, libc::F_GET_SEALS), #[cfg(any(target_os = "macos", target_os = "ios"))] F_FULLFSYNC => libc::fcntl(fd, libc::F_FULLFSYNC), @@ -503,7 +567,7 @@ pub enum FlockArg { UnlockNonblock, } -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "solaris")))] pub fn flock(fd: RawFd, arg: FlockArg) -> Result<()> { use self::FlockArg::*; @@ -512,8 +576,12 @@ pub fn flock(fd: RawFd, arg: FlockArg) -> Result<()> { LockShared => libc::flock(fd, libc::LOCK_SH), LockExclusive => libc::flock(fd, libc::LOCK_EX), Unlock => libc::flock(fd, libc::LOCK_UN), - LockSharedNonblock => libc::flock(fd, libc::LOCK_SH | libc::LOCK_NB), - LockExclusiveNonblock => libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB), + LockSharedNonblock => { + libc::flock(fd, libc::LOCK_SH | libc::LOCK_NB) + } + LockExclusiveNonblock => { + libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB) + } UnlockNonblock => libc::flock(fd, libc::LOCK_UN | libc::LOCK_NB), } }; @@ -552,44 +620,65 @@ feature! { /// /// The `copy_file_range` system call performs an in-kernel copy between /// file descriptors `fd_in` and `fd_out` without the additional cost of -/// transferring data from the kernel to user space and then back into the -/// kernel. It copies up to `len` bytes of data from file descriptor `fd_in` to -/// file descriptor `fd_out`, overwriting any data that exists within the -/// requested range of the target file. +/// transferring data from the kernel to user space and back again. There may be +/// additional optimizations for specific file systems. It copies up to `len` +/// bytes of data from file descriptor `fd_in` to file descriptor `fd_out`, +/// overwriting any data that exists within the requested range of the target +/// file. /// /// If the `off_in` and/or `off_out` arguments are used, the values /// will be mutated to reflect the new position within the file after -/// copying. If they are not used, the relevant filedescriptors will be seeked +/// copying. If they are not used, the relevant file descriptors will be seeked /// to the new position. /// /// On successful completion the number of bytes actually copied will be /// returned. -#[cfg(any(target_os = "android", target_os = "linux"))] -pub fn copy_file_range( - fd_in: RawFd, - off_in: Option<&mut libc::loff_t>, - fd_out: RawFd, - off_out: Option<&mut libc::loff_t>, +// Note: FreeBSD defines the offset argument as "off_t". Linux and Android +// define it as "loff_t". But on both OSes, on all supported platforms, those +// are 64 bits. So Nix uses i64 to make the docs simple and consistent. +#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))] +pub fn copy_file_range( + fd_in: Fd1, + off_in: Option<&mut i64>, + fd_out: Fd2, + off_out: Option<&mut i64>, len: usize, ) -> Result { let off_in = off_in - .map(|offset| offset as *mut libc::loff_t) + .map(|offset| offset as *mut i64) .unwrap_or(ptr::null_mut()); let off_out = off_out - .map(|offset| offset as *mut libc::loff_t) + .map(|offset| offset as *mut i64) .unwrap_or(ptr::null_mut()); - let ret = unsafe { - libc::syscall( - libc::SYS_copy_file_range, - fd_in, - off_in, - fd_out, - off_out, - len, - 0, - ) - }; + cfg_if::cfg_if! { + if #[cfg(target_os = "freebsd")] { + let ret = unsafe { + libc::copy_file_range( + fd_in.as_fd().as_raw_fd(), + off_in, + fd_out.as_fd().as_raw_fd(), + off_out, + len, + 0, + ) + }; + } else { + // May Linux distros still don't include copy_file_range in their + // libc implementations, so we need to make a direct syscall. + let ret = unsafe { + libc::syscall( + libc::SYS_copy_file_range, + fd_in, + off_in, + fd_out.as_fd().as_raw_fd(), + off_out, + len, + 0, + ) + }; + } + } Errno::result(ret).map(|r| r as usize) } @@ -609,12 +698,19 @@ pub fn splice( .map(|offset| offset as *mut libc::loff_t) .unwrap_or(ptr::null_mut()); - let ret = unsafe { libc::splice(fd_in, off_in, fd_out, off_out, len, flags.bits()) }; + let ret = unsafe { + libc::splice(fd_in, off_in, fd_out, off_out, len, flags.bits()) + }; Errno::result(ret).map(|r| r as usize) } #[cfg(any(target_os = "linux", target_os = "android"))] -pub fn tee(fd_in: RawFd, fd_out: RawFd, len: usize, flags: SpliceFFlags) -> Result { +pub fn tee( + fd_in: RawFd, + fd_out: RawFd, + len: usize, + flags: SpliceFFlags, +) -> Result { let ret = unsafe { libc::tee(fd_in, fd_out, len, flags.bits()) }; Errno::result(ret).map(|r| r as usize) } @@ -623,9 +719,8 @@ pub fn tee(fd_in: RawFd, fd_out: RawFd, len: usize, flags: SpliceFFlags) -> Resu pub fn vmsplice( fd: RawFd, iov: &[std::io::IoSlice<'_>], - flags: SpliceFFlags - ) -> Result -{ + flags: SpliceFFlags, +) -> Result { let ret = unsafe { libc::vmsplice( fd, @@ -638,7 +733,7 @@ pub fn vmsplice( } } -#[cfg(any(target_os = "linux"))] +#[cfg(target_os = "linux")] #[cfg(feature = "fs")] libc_bitflags!( /// Mode argument flags for fallocate determining operation performed on a given range. @@ -678,7 +773,7 @@ feature! { /// /// Allows the caller to directly manipulate the allocated disk space for the /// file referred to by fd. -#[cfg(any(target_os = "linux"))] +#[cfg(target_os = "linux")] #[cfg(feature = "fs")] pub fn fallocate( fd: RawFd, @@ -755,14 +850,19 @@ impl SpacectlRange { /// ``` #[cfg(target_os = "freebsd")] pub fn fspacectl(fd: RawFd, range: SpacectlRange) -> Result { - let mut rqsr = libc::spacectl_range{r_offset: range.0, r_len: range.1}; - let res = unsafe { libc::fspacectl( + let mut rqsr = libc::spacectl_range { + r_offset: range.0, + r_len: range.1, + }; + let res = unsafe { + libc::fspacectl( fd, libc::SPACECTL_DEALLOC, // Only one command is supported ATM &rqsr, - 0, // No flags are currently supported - &mut rqsr - )}; + 0, // No flags are currently supported + &mut rqsr, + ) + }; Errno::result(res).map(|_| SpacectlRange(rqsr.r_offset, rqsr.r_len)) } @@ -797,18 +897,25 @@ pub fn fspacectl(fd: RawFd, range: SpacectlRange) -> Result { /// assert_eq!(buf, b"012\0\0\0\0\0\09abcdef"); /// ``` #[cfg(target_os = "freebsd")] -pub fn fspacectl_all(fd: RawFd, offset: libc::off_t, len: libc::off_t) - -> Result<()> -{ - let mut rqsr = libc::spacectl_range{r_offset: offset, r_len: len}; +pub fn fspacectl_all( + fd: RawFd, + offset: libc::off_t, + len: libc::off_t, +) -> Result<()> { + let mut rqsr = libc::spacectl_range { + r_offset: offset, + r_len: len, + }; while rqsr.r_len > 0 { - let res = unsafe { libc::fspacectl( + let res = unsafe { + libc::fspacectl( fd, libc::SPACECTL_DEALLOC, // Only one command is supported ATM &rqsr, - 0, // No flags are currently supported - &mut rqsr - )}; + 0, // No flags are currently supported + &mut rqsr, + ) + }; Errno::result(res)?; } Ok(()) @@ -825,8 +932,8 @@ pub fn fspacectl_all(fd: RawFd, offset: libc::off_t, len: libc::off_t) ))] mod posix_fadvise { use crate::errno::Errno; - use std::os::unix::io::RawFd; use crate::Result; + use std::os::unix::io::RawFd; #[cfg(feature = "fs")] libc_enum! { @@ -871,7 +978,11 @@ mod posix_fadvise { target_os = "wasi", target_os = "freebsd" ))] -pub fn posix_fallocate(fd: RawFd, offset: libc::off_t, len: libc::off_t) -> Result<()> { +pub fn posix_fallocate( + fd: RawFd, + offset: libc::off_t, + len: libc::off_t, +) -> Result<()> { let res = unsafe { libc::posix_fallocate(fd, offset, len) }; match Errno::result(res) { Err(err) => Err(err), diff --git a/src/features.rs b/src/features.rs index 39d1760..9e292cb 100644 --- a/src/features.rs +++ b/src/features.rs @@ -6,6 +6,7 @@ mod os { use crate::sys::utsname::uname; use crate::Result; use std::os::unix::ffi::OsStrExt; + use std::sync::atomic::{AtomicUsize, Ordering}; // Features: // * atomic cloexec on socket: 2.6.27 @@ -72,15 +73,15 @@ mod os { } fn kernel_version() -> Result { - static mut KERNEL_VERS: usize = 0; + static KERNEL_VERS: AtomicUsize = AtomicUsize::new(0); + let mut kernel_vers = KERNEL_VERS.load(Ordering::Relaxed); - unsafe { - if KERNEL_VERS == 0 { - KERNEL_VERS = parse_kernel_version()?; - } - - Ok(KERNEL_VERS) + if kernel_vers == 0 { + kernel_vers = parse_kernel_version()?; + KERNEL_VERS.store(kernel_vers, Ordering::Relaxed); } + + Ok(kernel_vers) } /// Check if the OS supports atomic close-on-exec for sockets @@ -112,6 +113,7 @@ mod os { } #[cfg(any( + target_os = "aix", target_os = "macos", target_os = "ios", target_os = "fuchsia", diff --git a/src/kmod.rs b/src/kmod.rs index 1fa6c17..d3725c3 100644 --- a/src/kmod.rs +++ b/src/kmod.rs @@ -3,7 +3,7 @@ //! For more details see use std::ffi::CStr; -use std::os::unix::io::AsRawFd; +use std::os::unix::io::{AsFd, AsRawFd}; use crate::errno::Errno; use crate::Result; @@ -79,15 +79,15 @@ libc_bitflags!( /// ``` /// /// See [`man init_module(2)`](https://man7.org/linux/man-pages/man2/init_module.2.html) for more information. -pub fn finit_module( - fd: &T, +pub fn finit_module( + fd: Fd, param_values: &CStr, flags: ModuleInitFlags, ) -> Result<()> { let res = unsafe { libc::syscall( libc::SYS_finit_module, - fd.as_raw_fd(), + fd.as_fd().as_raw_fd(), param_values.as_ptr(), flags.bits(), ) diff --git a/src/lib.rs b/src/lib.rs index 6b82125..af0c67b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,7 @@ //! * `fs` - File system functionality //! * `hostname` - Get and set the system's hostname //! * `inotify` - Linux's `inotify` file system notification API -//! * `ioctl` - The `ioctl` syscall, and wrappers for my specific instances +//! * `ioctl` - The `ioctl` syscall, and wrappers for many specific instances //! * `kmod` - Load and unload kernel modules //! * `mman` - Stuff relating to memory management //! * `mount` - Mount and unmount file systems @@ -47,7 +47,43 @@ #![recursion_limit = "500"] #![deny(unused)] #![allow(unused_macros)] -#![cfg_attr(not(feature = "default"), allow(unused_imports))] +#![cfg_attr( + not(all( + feature = "acct", + feature = "aio", + feature = "dir", + feature = "env", + feature = "event", + feature = "feature", + feature = "fs", + feature = "hostname", + feature = "inotify", + feature = "ioctl", + feature = "kmod", + feature = "mman", + feature = "mount", + feature = "mqueue", + feature = "net", + feature = "personality", + feature = "poll", + feature = "process", + feature = "pthread", + feature = "ptrace", + feature = "quota", + feature = "reboot", + feature = "resource", + feature = "sched", + feature = "socket", + feature = "signal", + feature = "term", + feature = "time", + feature = "ucontext", + feature = "uio", + feature = "user", + feature = "zerocopy", + )), + allow(unused_imports) +)] #![deny(unstable_features)] #![deny(missing_copy_implementations)] #![deny(missing_debug_implementations)] @@ -144,7 +180,12 @@ feature! { // provides bindings for them. #[cfg(all( target_os = "linux", - any(target_arch = "s390x", target_arch = "x86", target_arch = "x86_64") + any( + target_arch = "aarch64", + target_arch = "s390x", + target_arch = "x86", + target_arch = "x86_64" + ) ))] feature! { #![feature = "ucontext"] diff --git a/src/macros.rs b/src/macros.rs index 99e0de8..adff2bc 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -63,6 +63,8 @@ macro_rules! libc_bitflags { } ) => { ::bitflags::bitflags! { + #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + #[repr(transparent)] $(#[$outer])* pub struct $BitFlags: $T { $( @@ -95,7 +97,6 @@ macro_rules! libc_bitflags { /// } /// ``` // Some targets don't use all rules. -#[allow(unknown_lints)] #[allow(unused_macro_rules)] macro_rules! libc_enum { // Exit rule. @@ -133,6 +134,8 @@ macro_rules! libc_enum { impl ::std::convert::TryFrom<$repr> for $BitFlags { type Error = $crate::Error; #[allow(unused_doc_comments)] + #[allow(deprecated)] + #[allow(unused_attributes)] fn try_from(x: $repr) -> $crate::Result { match x { $($try_froms)* diff --git a/src/mount/bsd.rs b/src/mount/bsd.rs index d124f1f..6ed2dc7 100644 --- a/src/mount/bsd.rs +++ b/src/mount/bsd.rs @@ -391,8 +391,8 @@ impl<'a> Nmount<'a> { }); let niov = self.iov.len() as c_uint; - let iovp = self.iov.as_mut_ptr() as *mut libc::iovec; - let res = unsafe { libc::nmount(iovp, niov, flags.bits) }; + let iovp = self.iov.as_mut_ptr(); + let res = unsafe { libc::nmount(iovp, niov, flags.bits()) }; match Errno::result(res) { Ok(_) => Ok(()), Err(error) => { @@ -446,7 +446,7 @@ where P: ?Sized + NixPath, { let res = mountpoint.with_nix_path(|cstr| unsafe { - libc::unmount(cstr.as_ptr(), flags.bits) + libc::unmount(cstr.as_ptr(), flags.bits()) })?; Errno::result(res).map(drop) diff --git a/src/mount/linux.rs b/src/mount/linux.rs index cf6a60b..e987603 100644 --- a/src/mount/linux.rs +++ b/src/mount/linux.rs @@ -1,9 +1,9 @@ -#![allow(missing_docs)] use crate::errno::Errno; use crate::{NixPath, Result}; use libc::{self, c_int, c_ulong}; libc_bitflags!( + /// Used with [`mount`]. pub struct MsFlags: c_ulong { /// Mount read-only MS_RDONLY; @@ -27,36 +27,80 @@ libc_bitflags!( MS_NODIRATIME; /// Linux 2.4.0 - Bind directory at different place MS_BIND; + /// Move an existing mount to a new location MS_MOVE; + /// Used to create a recursive bind mount. MS_REC; + /// Suppress the display of certain kernel warning messages. MS_SILENT; + /// VFS does not apply the umask MS_POSIXACL; + /// The resulting mount cannot subsequently be bind mounted. MS_UNBINDABLE; + /// Make this mount point private. MS_PRIVATE; + /// If this is a shared mount point that is a member of a peer group + /// that contains other members, convert it to a slave mount. MS_SLAVE; + /// Make this mount point shared. MS_SHARED; + /// When a file on this filesystem is accessed, update the file's + /// last access time (atime) only if the current value of atime is + /// less than or equal to the file's last modification time (mtime) or + /// last status change time (ctime). MS_RELATIME; + /// Mount request came from within the kernel + #[deprecated(since = "0.27.0", note = "Should only be used in-kernel")] MS_KERNMOUNT; + /// Update inode I_version field MS_I_VERSION; + /// Always update the last access time (atime) when files on this + /// filesystem are accessed. MS_STRICTATIME; + /// Reduce on-disk updates of inode timestamps (atime, mtime, ctime) by + /// maintaining these changes only in memory. MS_LAZYTIME; + #[deprecated(since = "0.27.0", note = "Should only be used in-kernel")] + #[allow(missing_docs)] // Not documented in Linux MS_ACTIVE; + #[deprecated(since = "0.27.0", note = "Should only be used in-kernel")] + #[allow(missing_docs)] // Not documented in Linux MS_NOUSER; + #[allow(missing_docs)] // Not documented in Linux; possibly kernel-only MS_RMT_MASK; + #[allow(missing_docs)] // Not documented in Linux; possibly kernel-only MS_MGC_VAL; + #[allow(missing_docs)] // Not documented in Linux; possibly kernel-only MS_MGC_MSK; } ); libc_bitflags!( + /// Used with [`umount2]. pub struct MntFlags: c_int { + /// Attempt to unmount even if still in use, aborting pending requests. MNT_FORCE; + /// Lazy unmount. Disconnect the file system immediately, but don't + /// actually unmount it until it ceases to be busy. MNT_DETACH; + /// Mark the mount point as expired. MNT_EXPIRE; + /// Don't dereference `target` if it is a symlink. UMOUNT_NOFOLLOW; } ); +/// Mount a file system. +/// +/// # Arguments +/// - `source` - Specifies the file system. e.g. `/dev/sd0`. +/// - `target` - Specifies the destination. e.g. `/mnt`. +/// - `fstype` - The file system type, e.g. `ext4`. +/// - `flags` - Optional flags controlling the mount. +/// - `data` - Optional file system specific data. +/// +/// # See Also +/// [`mount`](https://man7.org/linux/man-pages/man2/mount.2.html) pub fn mount< P1: ?Sized + NixPath, P2: ?Sized + NixPath, @@ -88,7 +132,7 @@ pub fn mount< s, t.as_ptr(), ty, - flags.bits, + flags.bits(), d as *const libc::c_void, ) }) @@ -99,6 +143,7 @@ pub fn mount< Errno::result(res).map(drop) } +/// Unmount the file system mounted at `target`. pub fn umount(target: &P) -> Result<()> { let res = target.with_nix_path(|cstr| unsafe { libc::umount(cstr.as_ptr()) })?; @@ -106,9 +151,12 @@ pub fn umount(target: &P) -> Result<()> { Errno::result(res).map(drop) } +/// Unmount the file system mounted at `target`. +/// +/// See also [`umount`](https://man7.org/linux/man-pages/man2/umount.2.html) pub fn umount2(target: &P, flags: MntFlags) -> Result<()> { let res = target.with_nix_path(|cstr| unsafe { - libc::umount2(cstr.as_ptr(), flags.bits) + libc::umount2(cstr.as_ptr(), flags.bits()) })?; Errno::result(res).map(drop) diff --git a/src/mqueue.rs b/src/mqueue.rs index 33599bf..fb07d2a 100644 --- a/src/mqueue.rs +++ b/src/mqueue.rs @@ -9,16 +9,16 @@ //! use nix::sys::stat::Mode; //! //! const MSG_SIZE: mq_attr_member_t = 32; -//! let mq_name= CString::new("/a_nix_test_queue").unwrap(); +//! let mq_name= "/a_nix_test_queue"; //! //! let oflag0 = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY; //! let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH; -//! let mqd0 = mq_open(&mq_name, oflag0, mode, None).unwrap(); +//! let mqd0 = mq_open(mq_name, oflag0, mode, None).unwrap(); //! let msg_to_send = b"msg_1"; //! mq_send(&mqd0, msg_to_send, 1).unwrap(); //! //! let oflag1 = MQ_OFlag::O_CREAT | MQ_OFlag::O_RDONLY; -//! let mqd1 = mq_open(&mq_name, oflag1, mode, None).unwrap(); +//! let mqd1 = mq_open(mq_name, oflag1, mode, None).unwrap(); //! let mut buf = [0u8; 32]; //! let mut prio = 0u32; //! let len = mq_receive(&mqd1, &mut buf, &mut prio).unwrap(); @@ -31,12 +31,20 @@ //! [Further reading and details on the C API](https://man7.org/linux/man-pages/man7/mq_overview.7.html) use crate::errno::Errno; +use crate::NixPath; use crate::Result; use crate::sys::stat::Mode; use libc::{self, c_char, mqd_t, size_t}; -use std::ffi::CStr; use std::mem; +#[cfg(any( + target_os = "linux", + target_os = "netbsd", + target_os = "dragonfly" +))] +use std::os::unix::io::{ + AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd, +}; libc_bitflags! { /// Used with [`mq_open`]. @@ -139,33 +147,41 @@ impl MqAttr { /// Open a message queue /// /// See also [`mq_open(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_open.html) -// The mode.bits cast is only lossless on some OSes +// The mode.bits() cast is only lossless on some OSes #[allow(clippy::cast_lossless)] -pub fn mq_open( - name: &CStr, +pub fn mq_open

    ( + name: &P, oflag: MQ_OFlag, mode: Mode, attr: Option<&MqAttr>, -) -> Result { - let res = match attr { +) -> Result +where + P: ?Sized + NixPath, +{ + let res = name.with_nix_path(|cstr| match attr { Some(mq_attr) => unsafe { libc::mq_open( - name.as_ptr(), + cstr.as_ptr(), oflag.bits(), mode.bits() as libc::c_int, &mq_attr.mq_attr as *const libc::mq_attr, ) }, - None => unsafe { libc::mq_open(name.as_ptr(), oflag.bits()) }, - }; + None => unsafe { libc::mq_open(cstr.as_ptr(), oflag.bits()) }, + })?; + Errno::result(res).map(MqdT) } /// Remove a message queue /// /// See also [`mq_unlink(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_unlink.html) -pub fn mq_unlink(name: &CStr) -> Result<()> { - let res = unsafe { libc::mq_unlink(name.as_ptr()) }; +pub fn mq_unlink

    (name: &P) -> Result<()> +where + P: ?Sized + NixPath, +{ + let res = + name.with_nix_path(|cstr| unsafe { libc::mq_unlink(cstr.as_ptr()) })?; Errno::result(res).map(drop) } @@ -197,6 +213,32 @@ pub fn mq_receive( Errno::result(res).map(|r| r as usize) } +feature! { + #![feature = "time"] + use crate::sys::time::TimeSpec; + /// Receive a message from a message queue with a timeout + /// + /// See also ['mq_timedreceive(2)'](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_receive.html) + pub fn mq_timedreceive( + mqdes: &MqdT, + message: &mut [u8], + msg_prio: &mut u32, + abstime: &TimeSpec, + ) -> Result { + let len = message.len() as size_t; + let res = unsafe { + libc::mq_timedreceive( + mqdes.0, + message.as_mut_ptr() as *mut c_char, + len, + msg_prio as *mut u32, + abstime.as_ref(), + ) + }; + Errno::result(res).map(|r| r as usize) + } +} + /// Send a message to a message queue /// /// See also [`mq_send(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html) @@ -274,3 +316,43 @@ pub fn mq_remove_nonblock(mqd: &MqdT) -> Result { ); mq_setattr(mqd, &newattr) } + +#[cfg(any(target_os = "linux", target_os = "netbsd", target_os = "dragonfly"))] +impl AsFd for MqdT { + /// Borrow the underlying message queue descriptor. + fn as_fd(&self) -> BorrowedFd { + // SAFETY: [MqdT] will only contain a valid fd by construction. + unsafe { BorrowedFd::borrow_raw(self.0) } + } +} + +#[cfg(any(target_os = "linux", target_os = "netbsd", target_os = "dragonfly"))] +impl AsRawFd for MqdT { + /// Return the underlying message queue descriptor. + /// + /// Returned descriptor is a "shallow copy" of the descriptor, so it refers + /// to the same underlying kernel object as `self`. + fn as_raw_fd(&self) -> RawFd { + self.0 + } +} + +#[cfg(any(target_os = "linux", target_os = "netbsd", target_os = "dragonfly"))] +impl FromRawFd for MqdT { + /// Construct an [MqdT] from [RawFd]. + /// + /// # Safety + /// The `fd` given must be a valid and open file descriptor for a message + /// queue. + unsafe fn from_raw_fd(fd: RawFd) -> MqdT { + MqdT(fd) + } +} + +#[cfg(any(target_os = "linux", target_os = "netbsd", target_os = "dragonfly"))] +impl IntoRawFd for MqdT { + /// Consume this [MqdT] and return a [RawFd]. + fn into_raw_fd(self) -> RawFd { + self.0 + } +} diff --git a/src/net/if_.rs b/src/net/if_.rs index b2423bc..ec46260 100644 --- a/src/net/if_.rs +++ b/src/net/if_.rs @@ -334,6 +334,7 @@ libc_bitflags!( target_os = "macos", target_os = "netbsd", target_os = "openbsd", + target_os = "illumos", ))] #[cfg_attr(docsrs, doc(cfg(all())))] mod if_nameindex { @@ -465,5 +466,6 @@ mod if_nameindex { target_os = "macos", target_os = "netbsd", target_os = "openbsd", + target_os = "illumos", ))] pub use if_nameindex::*; diff --git a/src/poll.rs b/src/poll.rs index 6f227fe..9181bf7 100644 --- a/src/poll.rs +++ b/src/poll.rs @@ -1,5 +1,5 @@ //! Wait for events to trigger on specific file descriptors -use std::os::unix::io::{AsRawFd, RawFd}; +use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd}; use crate::errno::Errno; use crate::Result; @@ -14,20 +14,36 @@ use crate::Result; /// retrieved by calling [`revents()`](#method.revents) on the `PollFd`. #[repr(transparent)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub struct PollFd { +pub struct PollFd<'fd> { pollfd: libc::pollfd, + _fd: std::marker::PhantomData>, } -impl PollFd { +impl<'fd> PollFd<'fd> { /// Creates a new `PollFd` specifying the events of interest /// for a given file descriptor. - pub const fn new(fd: RawFd, events: PollFlags) -> PollFd { + // + // Different from other I/O-safe interfaces, here, we have to take `AsFd` + // by reference to prevent the case where the `fd` is closed but it is + // still in use. For example: + // + // ```rust + // let (reader, _) = pipe().unwrap(); + // + // // If `PollFd::new()` takes `AsFd` by value, then `reader` will be consumed, + // // but the file descriptor of `reader` will still be in use. + // let pollfd = PollFd::new(reader, flag); + // + // // Do something with `pollfd`, which uses the CLOSED fd. + // ``` + pub fn new(fd: &'fd Fd, events: PollFlags) -> PollFd<'fd> { PollFd { pollfd: libc::pollfd { - fd, + fd: fd.as_fd().as_raw_fd(), events: events.bits(), revents: PollFlags::empty().bits(), }, + _fd: std::marker::PhantomData, } } @@ -68,9 +84,29 @@ impl PollFd { } } -impl AsRawFd for PollFd { - fn as_raw_fd(&self) -> RawFd { - self.pollfd.fd +impl<'fd> AsFd for PollFd<'fd> { + fn as_fd(&self) -> BorrowedFd<'_> { + // Safety: + // + // BorrowedFd::borrow_raw(RawFd) requires that the raw fd being passed + // must remain open for the duration of the returned BorrowedFd, this is + // guaranteed as the returned BorrowedFd has the lifetime parameter same + // as `self`: + // "fn as_fd<'self>(&'self self) -> BorrowedFd<'self>" + // which means that `self` (PollFd) is guaranteed to outlive the returned + // BorrowedFd. (Lifetime: PollFd > BorrowedFd) + // + // And the lifetime parameter of PollFd::new(fd, ...) ensures that `fd` + // (an owned file descriptor) must outlive the returned PollFd: + // "pub fn new(fd: &'fd Fd, events: PollFlags) -> PollFd<'fd>" + // (Lifetime: Owned fd > PollFd) + // + // With two above relationships, we can conclude that the `Owned file + // descriptor` will outlive the returned BorrowedFd, + // (Lifetime: Owned fd > BorrowedFd) + // i.e., the raw fd being passed will remain valid for the lifetime of + // the returned BorrowedFd. + unsafe { BorrowedFd::borrow_raw(self.pollfd.fd) } } } diff --git a/src/pty.rs b/src/pty.rs index 28ae5e9..455828b 100644 --- a/src/pty.rs +++ b/src/pty.rs @@ -5,37 +5,39 @@ pub use libc::winsize as Winsize; use std::ffi::CStr; use std::io; +#[cfg(not(target_os = "aix"))] use std::mem; use std::os::unix::prelude::*; use crate::errno::Errno; +#[cfg(not(target_os = "aix"))] use crate::sys::termios::Termios; #[cfg(feature = "process")] -use crate::unistd::{ForkResult, Pid}; +use crate::unistd::ForkResult; +#[cfg(all(feature = "process", not(target_os = "aix")))] +use crate::unistd::Pid; use crate::{fcntl, unistd, Result}; /// Representation of a master/slave pty pair /// -/// This is returned by `openpty`. Note that this type does *not* implement `Drop`, so the user -/// must manually close the file descriptors. -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +/// This is returned by [`openpty`]. +#[derive(Debug)] pub struct OpenptyResult { /// The master port in a virtual pty pair - pub master: RawFd, + pub master: OwnedFd, /// The slave port in a virtual pty pair - pub slave: RawFd, + pub slave: OwnedFd, } feature! { #![feature = "process"] /// Representation of a master with a forked pty /// -/// This is returned by `forkpty`. Note that this type does *not* implement `Drop`, so the user -/// must manually close the file descriptors. -#[derive(Clone, Copy, Debug)] +/// This is returned by [`forkpty`]. +#[derive(Debug)] pub struct ForkptyResult { /// The master port in a virtual pty pair - pub master: RawFd, + pub master: OwnedFd, /// Metadata about forked process pub fork_result: ForkResult, } @@ -43,51 +45,33 @@ pub struct ForkptyResult { /// Representation of the Master device in a master/slave pty pair /// -/// 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(Debug, Eq, Hash, PartialEq)] -pub struct PtyMaster(RawFd); +/// While this datatype is a thin wrapper around `OwnedFd`, it enforces that the available PTY +/// functions are given the correct file descriptor. +#[derive(Debug)] +pub struct PtyMaster(OwnedFd); impl AsRawFd for PtyMaster { fn as_raw_fd(&self) -> RawFd { - self.0 + self.0.as_raw_fd() } } impl IntoRawFd for PtyMaster { fn into_raw_fd(self) -> RawFd { let fd = self.0; - mem::forget(self); - fd - } -} - -impl Drop for PtyMaster { - fn drop(&mut self) { - // On drop, we ignore errors like EINTR and EIO because there's no clear - // way to handle them, we can't return anything, and (on FreeBSD at - // least) the file descriptor is deallocated in these cases. However, - // we must panic on EBADF, because it is always an error to close an - // invalid file descriptor. That frequently indicates a double-close - // condition, which can cause confusing errors for future I/O - // operations. - let e = unistd::close(self.0); - if e == Err(Errno::EBADF) { - panic!("Closing an invalid file descriptor!"); - }; + fd.into_raw_fd() } } impl io::Read for PtyMaster { fn read(&mut self, buf: &mut [u8]) -> io::Result { - unistd::read(self.0, buf).map_err(io::Error::from) + unistd::read(self.0.as_raw_fd(), buf).map_err(io::Error::from) } } impl io::Write for PtyMaster { fn write(&mut self, buf: &[u8]) -> io::Result { - unistd::write(self.0, buf).map_err(io::Error::from) + unistd::write(self.0.as_raw_fd(), buf).map_err(io::Error::from) } fn flush(&mut self) -> io::Result<()> { Ok(()) @@ -96,13 +80,13 @@ impl io::Write for PtyMaster { impl io::Read for &PtyMaster { fn read(&mut self, buf: &mut [u8]) -> io::Result { - unistd::read(self.0, buf).map_err(io::Error::from) + unistd::read(self.0.as_raw_fd(), buf).map_err(io::Error::from) } } impl io::Write for &PtyMaster { fn write(&mut self, buf: &[u8]) -> io::Result { - unistd::write(self.0, buf).map_err(io::Error::from) + unistd::write(self.0.as_raw_fd(), buf).map_err(io::Error::from) } fn flush(&mut self) -> io::Result<()> { Ok(()) @@ -164,7 +148,7 @@ pub fn posix_openpt(flags: fcntl::OFlag) -> Result { return Err(Errno::last()); } - Ok(PtyMaster(fd)) + Ok(PtyMaster(unsafe { OwnedFd::from_raw_fd(fd) })) } /// Get the name of the slave pseudoterminal (see @@ -244,6 +228,7 @@ pub fn unlockpt(fd: &PtyMaster) -> Result<()> { /// the values in `winsize`. If `termios` is not `None`, the pseudoterminal's /// terminal settings of the slave will be set to the values in `termios`. #[inline] +#[cfg(not(target_os = "aix"))] pub fn openpty< 'a, 'b, @@ -308,8 +293,8 @@ pub fn openpty< unsafe { Ok(OpenptyResult { - master: master.assume_init(), - slave: slave.assume_init(), + master: OwnedFd::from_raw_fd(master.assume_init()), + slave: OwnedFd::from_raw_fd(slave.assume_init()), }) } } @@ -335,6 +320,7 @@ feature! { /// special care must be taken to only invoke code you can control and audit. /// /// [async-signal-safe]: https://man7.org/linux/man-pages/man7/signal-safety.7.html +#[cfg(not(target_os = "aix"))] pub unsafe fn forkpty<'a, 'b, T: Into>, U: Into>>( winsize: T, termios: U, @@ -364,7 +350,7 @@ pub unsafe fn forkpty<'a, 'b, T: Into>, U: Into isize>) -> i32, - ), - ptr_aligned as *mut c_void, - combined, - &mut cb as *mut _ as *mut c_void, - ) - }; + let combined = flags.bits() | signal.unwrap_or(0); + let ptr = stack.as_mut_ptr().add(stack.len()); + let ptr_aligned = ptr.sub(ptr as usize % 16); + let res = libc::clone( + mem::transmute( + callback + as extern "C" fn(*mut Box isize>) -> i32, + ), + ptr_aligned as *mut c_void, + combined, + &mut cb as *mut _ as *mut c_void, + ); Errno::result(res).map(Pid::from_raw) } @@ -136,8 +144,8 @@ mod sched_linux_like { /// reassociate thread with a namespace /// /// See also [setns(2)](https://man7.org/linux/man-pages/man2/setns.2.html) - pub fn setns(fd: RawFd, nstype: CloneFlags) -> Result<()> { - let res = unsafe { libc::setns(fd, nstype.bits()) }; + pub fn setns(fd: Fd, nstype: CloneFlags) -> Result<()> { + let res = unsafe { libc::setns(fd.as_fd().as_raw_fd(), nstype.bits()) }; Errno::result(res).map(drop) } diff --git a/src/sys/aio.rs b/src/sys/aio.rs index e2ce19b..5471177 100644 --- a/src/sys/aio.rs +++ b/src/sys/aio.rs @@ -163,7 +163,7 @@ impl AioCb { 0 => Ok(()), num if num > 0 => Err(Errno::from_i32(num)), -1 => Err(Errno::last()), - num => panic!("unknown aio_error return value {:?}", num), + num => panic!("unknown aio_error return value {num:?}"), } } @@ -1051,8 +1051,14 @@ pub fn aio_suspend( list: &[&dyn AsRef], timeout: Option, ) -> Result<()> { - let p = list as *const [&dyn AsRef] - as *const [*const libc::aiocb] as *const *const libc::aiocb; + // Note that this allocation could be eliminated by making the argument + // generic, and accepting arguments like &[AioWrite]. But that would + // prevent using aio_suspend to wait on a heterogeneous list of mixed + // operations. + let v = list.iter() + .map(|x| x.as_ref() as *const libc::aiocb) + .collect::>(); + let p = v.as_ptr(); let timep = match timeout { None => ptr::null::(), Some(x) => x.as_ref() as *const libc::timespec, @@ -1136,14 +1142,11 @@ pub fn aio_suspend( /// # use std::sync::atomic::{AtomicBool, Ordering}; /// # use std::thread; /// # use std::time; -/// # use lazy_static::lazy_static; /// # use nix::errno::Errno; /// # use nix::sys::aio::*; /// # use nix::sys::signal::*; /// # use tempfile::tempfile; -/// lazy_static! { -/// pub static ref SIGNALED: AtomicBool = AtomicBool::new(false); -/// } +/// pub static SIGNALED: AtomicBool = AtomicBool::new(false); /// /// extern fn sigfunc(_: c_int) { /// SIGNALED.store(true, Ordering::Relaxed); @@ -1172,6 +1175,7 @@ pub fn aio_suspend( /// // notification, we know that all operations are complete. /// assert_eq!(aiow.as_mut().aio_return().unwrap(), WBUF.len()); /// ``` +#[deprecated(since = "0.27.0", note = "https://github.com/nix-rust/nix/issues/2017")] pub fn lio_listio( mode: LioMode, list: &mut [Pin<&mut dyn AsMut>], diff --git a/src/sys/epoll.rs b/src/sys/epoll.rs index 58def2e..36f9c17 100644 --- a/src/sys/epoll.rs +++ b/src/sys/epoll.rs @@ -2,8 +2,7 @@ use crate::errno::Errno; use crate::Result; use libc::{self, c_int}; use std::mem; -use std::os::unix::io::RawFd; -use std::ptr; +use std::os::unix::io::{AsFd, AsRawFd, FromRawFd, OwnedFd, RawFd}; libc_bitflags!( pub struct EpollFlags: c_int { @@ -70,6 +69,126 @@ impl EpollEvent { } } +/// A safe wrapper around [`epoll`](https://man7.org/linux/man-pages/man7/epoll.7.html). +/// ``` +/// # use nix::sys::{epoll::{Epoll, EpollEvent, EpollFlags, EpollCreateFlags}, eventfd::{eventfd, EfdFlags}}; +/// # use nix::unistd::write; +/// # use std::os::unix::io::{OwnedFd, FromRawFd, AsRawFd, AsFd}; +/// # use std::time::{Instant, Duration}; +/// # fn main() -> nix::Result<()> { +/// const DATA: u64 = 17; +/// const MILLIS: u64 = 100; +/// +/// // Create epoll +/// let epoll = Epoll::new(EpollCreateFlags::empty())?; +/// +/// // Create eventfd & Add event +/// let eventfd = eventfd(0, EfdFlags::empty())?; +/// epoll.add(&eventfd, EpollEvent::new(EpollFlags::EPOLLIN,DATA))?; +/// +/// // Arm eventfd & Time wait +/// write(eventfd.as_raw_fd(), &1u64.to_ne_bytes())?; +/// let now = Instant::now(); +/// +/// // Wait on event +/// let mut events = [EpollEvent::empty()]; +/// epoll.wait(&mut events, MILLIS as isize)?; +/// +/// // Assert data correct & timeout didn't occur +/// assert_eq!(events[0].data(), DATA); +/// assert!(now.elapsed() < Duration::from_millis(MILLIS)); +/// # Ok(()) +/// # } +/// ``` +#[derive(Debug)] +pub struct Epoll(pub OwnedFd); +impl Epoll { + /// Creates a new epoll instance and returns a file descriptor referring to that instance. + /// + /// [`epoll_create1`](https://man7.org/linux/man-pages/man2/epoll_create1.2.html). + pub fn new(flags: EpollCreateFlags) -> Result { + let res = unsafe { libc::epoll_create1(flags.bits()) }; + let fd = Errno::result(res)?; + let owned_fd = unsafe { OwnedFd::from_raw_fd(fd) }; + Ok(Self(owned_fd)) + } + /// Add an entry to the interest list of the epoll file descriptor for + /// specified in events. + /// + /// [`epoll_ctl`](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html) with `EPOLL_CTL_ADD`. + pub fn add(&self, fd: Fd, mut event: EpollEvent) -> Result<()> { + self.epoll_ctl(EpollOp::EpollCtlAdd, fd, &mut event) + } + /// Remove (deregister) the target file descriptor `fd` from the interest list. + /// + /// [`epoll_ctl`](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html) with `EPOLL_CTL_DEL` . + pub fn delete(&self, fd: Fd) -> Result<()> { + self.epoll_ctl(EpollOp::EpollCtlDel, fd, None) + } + /// Change the settings associated with `fd` in the interest list to the new settings specified + /// in `event`. + /// + /// [`epoll_ctl`](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html) with `EPOLL_CTL_MOD`. + pub fn modify( + &self, + fd: Fd, + event: &mut EpollEvent, + ) -> Result<()> { + self.epoll_ctl(EpollOp::EpollCtlMod, fd, event) + } + /// Waits for I/O events, blocking the calling thread if no events are currently available. + /// (This can be thought of as fetching items from the ready list of the epoll instance.) + /// + /// [`epoll_wait`](https://man7.org/linux/man-pages/man2/epoll_wait.2.html) + pub fn wait( + &self, + events: &mut [EpollEvent], + timeout: isize, + ) -> Result { + let res = unsafe { + libc::epoll_wait( + self.0.as_raw_fd(), + events.as_mut_ptr() as *mut libc::epoll_event, + events.len() as c_int, + timeout as c_int, + ) + }; + + Errno::result(res).map(|r| r as usize) + } + /// This system call is used to add, modify, or remove entries in the interest list of the epoll + /// instance referred to by `self`. It requests that the operation `op` be performed for the + /// target file descriptor, `fd`. + /// + /// When possible prefer [`Epoll::add`], [`Epoll::delete`] and [`Epoll::modify`]. + /// + /// [`epoll_ctl`](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html) + fn epoll_ctl<'a, Fd: AsFd, T>( + &self, + op: EpollOp, + fd: Fd, + event: T, + ) -> Result<()> + where + T: Into>, + { + let event: Option<&mut EpollEvent> = event.into(); + let ptr = event + .map(|x| &mut x.event as *mut libc::epoll_event) + .unwrap_or(std::ptr::null_mut()); + unsafe { + Errno::result(libc::epoll_ctl( + self.0.as_raw_fd(), + op as c_int, + fd.as_fd().as_raw_fd(), + ptr, + )) + .map(drop) + } + } +} + +#[deprecated(since = "0.27.0", note = "Use Epoll::new() instead")] #[inline] pub fn epoll_create() -> Result { let res = unsafe { libc::epoll_create(1024) }; @@ -77,6 +196,7 @@ pub fn epoll_create() -> Result { Errno::result(res) } +#[deprecated(since = "0.27.0", note = "Use Epoll::new() instead")] #[inline] pub fn epoll_create1(flags: EpollCreateFlags) -> Result { let res = unsafe { libc::epoll_create1(flags.bits()) }; @@ -84,6 +204,7 @@ pub fn epoll_create1(flags: EpollCreateFlags) -> Result { Errno::result(res) } +#[deprecated(since = "0.27.0", note = "Use Epoll::epoll_ctl() instead")] #[inline] pub fn epoll_ctl<'a, T>( epfd: RawFd, @@ -102,13 +223,14 @@ where if let Some(ref mut event) = event { libc::epoll_ctl(epfd, op as c_int, fd, &mut event.event) } else { - libc::epoll_ctl(epfd, op as c_int, fd, ptr::null_mut()) + libc::epoll_ctl(epfd, op as c_int, fd, std::ptr::null_mut()) } }; Errno::result(res).map(drop) } } +#[deprecated(since = "0.27.0", note = "Use Epoll::wait() instead")] #[inline] pub fn epoll_wait( epfd: RawFd, diff --git a/src/sys/event.rs b/src/sys/event.rs index d8ad628..ec7f7e2 100644 --- a/src/sys/event.rs +++ b/src/sys/event.rs @@ -1,5 +1,7 @@ -/* TOOD: Implement for other kqueue based systems - */ +//! Kernel event notification mechanism +//! +//! # See Also +//! [kqueue(2)](https://www.freebsd.org/cgi/man.cgi?query=kqueue) use crate::{Errno, Result}; #[cfg(not(target_os = "netbsd"))] @@ -8,16 +10,74 @@ use libc::{c_int, c_long, intptr_t, time_t, timespec, uintptr_t}; use libc::{c_long, intptr_t, size_t, time_t, timespec, uintptr_t}; use std::convert::TryInto; use std::mem; -use std::os::unix::io::RawFd; +use std::os::unix::io::{AsRawFd, FromRawFd, OwnedFd}; use std::ptr; -// Redefine kevent in terms of programmer-friendly enums and bitfields. +/// A kernel event queue. Used to notify a process of various asynchronous +/// events. #[repr(C)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct KEvent { kevent: libc::kevent, } +/// A kernel event queue. +/// +/// Used by the kernel to notify the process of various types of asynchronous +/// events. +#[repr(transparent)] +#[derive(Debug)] +pub struct Kqueue(OwnedFd); + +impl Kqueue { + /// Create a new kernel event queue. + pub fn new() -> Result { + let res = unsafe { libc::kqueue() }; + + Errno::result(res).map(|fd| unsafe { Self(OwnedFd::from_raw_fd(fd)) }) + } + + /// Register new events with the kqueue, and return any pending events to + /// the user. + /// + /// This method will block until either the timeout expires, or a registered + /// event triggers a notification. + /// + /// # Arguments + /// - `changelist` - Any new kevents to register for notifications. + /// - `eventlist` - Storage space for the kernel to return notifications. + /// - `timeout` - An optional timeout. + /// + /// # Returns + /// Returns the number of events placed in the `eventlist`. If an error + /// occurs while processing an element of the `changelist` and there is + /// enough room in the `eventlist`, then the event will be placed in the + /// `eventlist` with `EV_ERROR` set in `flags` and the system error in + /// `data`. + pub fn kevent( + &self, + changelist: &[KEvent], + eventlist: &mut [KEvent], + timeout_opt: Option, + ) -> Result { + let res = unsafe { + libc::kevent( + self.0.as_raw_fd(), + changelist.as_ptr() as *const libc::kevent, + changelist.len() as type_of_nchanges, + eventlist.as_mut_ptr() as *mut libc::kevent, + eventlist.len() as type_of_nchanges, + if let Some(ref timeout) = timeout_opt { + timeout as *const timespec + } else { + ptr::null() + }, + ) + }; + Errno::result(res).map(|r| r as usize) + } +} + #[cfg(any( target_os = "dragonfly", target_os = "freebsd", @@ -26,7 +86,7 @@ pub struct KEvent { target_os = "openbsd" ))] type type_of_udata = *mut libc::c_void; -#[cfg(any(target_os = "netbsd"))] +#[cfg(target_os = "netbsd")] type type_of_udata = intptr_t; #[cfg(target_os = "netbsd")] @@ -37,22 +97,34 @@ libc_enum! { #[cfg_attr(target_os = "netbsd", repr(u32))] #[cfg_attr(not(target_os = "netbsd"), repr(i16))] #[non_exhaustive] + /// Kqueue filter types. These are all the different types of event that a + /// kqueue can notify for. pub enum EventFilter { + /// Notifies on the completion of a POSIX AIO operation. EVFILT_AIO, - /// Returns whenever there is no remaining data in the write buffer #[cfg(target_os = "freebsd")] + /// Returns whenever there is no remaining data in the write buffer EVFILT_EMPTY, #[cfg(target_os = "dragonfly")] + /// Takes a descriptor as the identifier, and returns whenever one of + /// the specified exceptional conditions has occurred on the descriptor. EVFILT_EXCEPT, #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "ios", target_os = "macos"))] + /// Establishes a file system monitor. EVFILT_FS, #[cfg(target_os = "freebsd")] + /// Notify for completion of a list of POSIX AIO operations. + /// # See Also + /// [lio_listio(2)](https://www.freebsd.org/cgi/man.cgi?query=lio_listio) EVFILT_LIO, #[cfg(any(target_os = "ios", target_os = "macos"))] + /// Mach portsets EVFILT_MACHPORT, + /// Notifies when a process performs one or more of the requested + /// events. EVFILT_PROC, /// Returns events associated with the process referenced by a given /// process descriptor, created by `pdfork()`. The events to monitor are: @@ -60,20 +132,31 @@ libc_enum! { /// - NOTE_EXIT: the process has exited. The exit status will be stored in data. #[cfg(target_os = "freebsd")] EVFILT_PROCDESC, + /// Takes a file descriptor as the identifier, and notifies whenever + /// there is data available to read. EVFILT_READ, - /// Returns whenever an asynchronous `sendfile()` call completes. #[cfg(target_os = "freebsd")] + #[doc(hidden)] + #[deprecated(since = "0.27.0", note = "Never fully implemented by the OS")] EVFILT_SENDFILE, + /// Takes a signal number to monitor as the identifier and notifies when + /// the given signal is delivered to the process. EVFILT_SIGNAL, + /// Establishes a timer and notifies when the timer expires. EVFILT_TIMER, #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "ios", target_os = "macos"))] + /// Notifies only when explicitly requested by the user. EVFILT_USER, #[cfg(any(target_os = "ios", target_os = "macos"))] + /// Virtual memory events EVFILT_VM, + /// Notifies when a requested event happens on a specified file. EVFILT_VNODE, + /// Takes a file descriptor as the identifier, and notifies whenever + /// it is possible to write to the file without blocking. EVFILT_WRITE, } impl TryFrom @@ -86,131 +169,194 @@ libc_enum! { target_os = "macos", target_os = "openbsd" ))] +#[doc(hidden)] pub type type_of_event_flag = u16; -#[cfg(any(target_os = "netbsd"))] +#[cfg(target_os = "netbsd")] +#[doc(hidden)] pub type type_of_event_flag = u32; libc_bitflags! { + /// Event flags. See the man page for details. + // There's no useful documentation we can write for the individual flags + // that wouldn't simply be repeating the man page. pub struct EventFlag: type_of_event_flag { + #[allow(missing_docs)] EV_ADD; + #[allow(missing_docs)] EV_CLEAR; + #[allow(missing_docs)] EV_DELETE; + #[allow(missing_docs)] EV_DISABLE; #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "ios", target_os = "macos", target_os = "netbsd", target_os = "openbsd"))] + #[allow(missing_docs)] EV_DISPATCH; #[cfg(target_os = "freebsd")] + #[allow(missing_docs)] EV_DROP; + #[allow(missing_docs)] EV_ENABLE; + #[allow(missing_docs)] EV_EOF; + #[allow(missing_docs)] EV_ERROR; #[cfg(any(target_os = "macos", target_os = "ios"))] + #[allow(missing_docs)] EV_FLAG0; + #[allow(missing_docs)] EV_FLAG1; #[cfg(target_os = "dragonfly")] + #[allow(missing_docs)] EV_NODATA; + #[allow(missing_docs)] EV_ONESHOT; #[cfg(any(target_os = "macos", target_os = "ios"))] + #[allow(missing_docs)] EV_OOBAND; #[cfg(any(target_os = "macos", target_os = "ios"))] + #[allow(missing_docs)] EV_POLL; #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "ios", target_os = "macos", target_os = "netbsd", target_os = "openbsd"))] + #[allow(missing_docs)] EV_RECEIPT; } } libc_bitflags!( + /// Filter-specific flags. See the man page for details. + // There's no useful documentation we can write for the individual flags + // that wouldn't simply be repeating the man page. + #[allow(missing_docs)] pub struct FilterFlag: u32 { #[cfg(any(target_os = "macos", target_os = "ios"))] + #[allow(missing_docs)] NOTE_ABSOLUTE; + #[allow(missing_docs)] NOTE_ATTRIB; + #[allow(missing_docs)] NOTE_CHILD; + #[allow(missing_docs)] NOTE_DELETE; #[cfg(target_os = "openbsd")] + #[allow(missing_docs)] NOTE_EOF; + #[allow(missing_docs)] NOTE_EXEC; + #[allow(missing_docs)] NOTE_EXIT; #[cfg(any(target_os = "macos", target_os = "ios"))] + #[allow(missing_docs)] NOTE_EXITSTATUS; + #[allow(missing_docs)] NOTE_EXTEND; #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "dragonfly"))] + #[allow(missing_docs)] NOTE_FFAND; #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "dragonfly"))] + #[allow(missing_docs)] NOTE_FFCOPY; #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "dragonfly"))] + #[allow(missing_docs)] NOTE_FFCTRLMASK; #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "dragonfly"))] + #[allow(missing_docs)] NOTE_FFLAGSMASK; #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "dragonfly"))] + #[allow(missing_docs)] NOTE_FFNOP; #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "dragonfly"))] + #[allow(missing_docs)] NOTE_FFOR; + #[allow(missing_docs)] NOTE_FORK; + #[allow(missing_docs)] NOTE_LINK; + #[allow(missing_docs)] NOTE_LOWAT; #[cfg(target_os = "freebsd")] + #[allow(missing_docs)] NOTE_MSECONDS; #[cfg(any(target_os = "macos", target_os = "ios"))] + #[allow(missing_docs)] NOTE_NONE; #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))] + #[allow(missing_docs)] NOTE_NSECONDS; #[cfg(target_os = "dragonfly")] + #[allow(missing_docs)] NOTE_OOB; + #[allow(missing_docs)] NOTE_PCTRLMASK; + #[allow(missing_docs)] NOTE_PDATAMASK; + #[allow(missing_docs)] NOTE_RENAME; + #[allow(missing_docs)] NOTE_REVOKE; #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))] + #[allow(missing_docs)] NOTE_SECONDS; #[cfg(any(target_os = "macos", target_os = "ios"))] + #[allow(missing_docs)] NOTE_SIGNAL; + #[allow(missing_docs)] NOTE_TRACK; + #[allow(missing_docs)] NOTE_TRACKERR; #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "dragonfly"))] + #[allow(missing_docs)] NOTE_TRIGGER; #[cfg(target_os = "openbsd")] + #[allow(missing_docs)] NOTE_TRUNCATE; #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))] + #[allow(missing_docs)] NOTE_USECONDS; #[cfg(any(target_os = "macos", target_os = "ios"))] + #[allow(missing_docs)] NOTE_VM_ERROR; #[cfg(any(target_os = "macos", target_os = "ios"))] + #[allow(missing_docs)] NOTE_VM_PRESSURE; #[cfg(any(target_os = "macos", target_os = "ios"))] + #[allow(missing_docs)] NOTE_VM_PRESSURE_SUDDEN_TERMINATE; #[cfg(any(target_os = "macos", target_os = "ios"))] + #[allow(missing_docs)] NOTE_VM_PRESSURE_TERMINATE; + #[allow(missing_docs)] NOTE_WRITE; } ); -pub fn kqueue() -> Result { - let res = unsafe { libc::kqueue() }; - - Errno::result(res) +#[allow(missing_docs)] +#[deprecated(since = "0.27.0", note = "Use KEvent::new instead")] +pub fn kqueue() -> Result { + Kqueue::new() } // KEvent can't derive Send because on some operating systems, udata is defined @@ -220,6 +366,8 @@ unsafe impl Send for KEvent {} impl KEvent { #[allow(clippy::needless_update)] // Not needless on all platforms. + /// Construct a new `KEvent` suitable for submission to the kernel via the + /// `changelist` argument of [`Kqueue::kevent`]. pub fn new( ident: uintptr_t, filter: EventFilter, @@ -242,33 +390,46 @@ impl KEvent { } } + /// Value used to identify this event. The exact interpretation is + /// determined by the attached filter, but often is a raw file descriptor. pub fn ident(&self) -> uintptr_t { self.kevent.ident } + /// Identifies the kernel filter used to process this event. + /// + /// Will only return an error if the kernel reports an event via a filter + /// that is unknown to Nix. pub fn filter(&self) -> Result { self.kevent.filter.try_into() } + /// Flags control what the kernel will do when this event is added with + /// [`Kqueue::kevent`]. pub fn flags(&self) -> EventFlag { EventFlag::from_bits(self.kevent.flags).unwrap() } + /// Filter-specific flags. pub fn fflags(&self) -> FilterFlag { FilterFlag::from_bits(self.kevent.fflags).unwrap() } + /// Filter-specific data value. pub fn data(&self) -> intptr_t { self.kevent.data as intptr_t } + /// Opaque user-defined value passed through the kernel unchanged. pub fn udata(&self) -> intptr_t { self.kevent.udata as intptr_t } } +#[allow(missing_docs)] +#[deprecated(since = "0.27.0", note = "Use Kqueue::kevent instead")] pub fn kevent( - kq: RawFd, + kq: &Kqueue, changelist: &[KEvent], eventlist: &mut [KEvent], timeout_ms: usize, @@ -279,7 +440,7 @@ pub fn kevent( tv_nsec: ((timeout_ms % 1000) * 1_000_000) as c_long, }; - kevent_ts(kq, changelist, eventlist, Some(timeout)) + kq.kevent(changelist, eventlist, Some(timeout)) } #[cfg(any( @@ -293,30 +454,20 @@ type type_of_nchanges = c_int; #[cfg(target_os = "netbsd")] type type_of_nchanges = size_t; +#[allow(missing_docs)] +#[deprecated(since = "0.27.0", note = "Use Kqueue::kevent instead")] pub fn kevent_ts( - kq: RawFd, + kq: &Kqueue, changelist: &[KEvent], eventlist: &mut [KEvent], timeout_opt: Option, ) -> Result { - let res = unsafe { - libc::kevent( - kq, - changelist.as_ptr() as *const libc::kevent, - changelist.len() as type_of_nchanges, - eventlist.as_mut_ptr() as *mut libc::kevent, - eventlist.len() as type_of_nchanges, - if let Some(ref timeout) = timeout_opt { - timeout as *const timespec - } else { - ptr::null() - }, - ) - }; - - Errno::result(res).map(|r| r as usize) + kq.kevent(changelist, eventlist, timeout_opt) } +/// Modify an existing [`KEvent`]. +// Probably should deprecate. Would anybody ever use it over `KEvent::new`? +#[deprecated(since = "0.27.0", note = "Use Kqueue::kevent instead")] #[inline] pub fn ev_set( ev: &mut KEvent, diff --git a/src/sys/eventfd.rs b/src/sys/eventfd.rs index cd90672..f172351 100644 --- a/src/sys/eventfd.rs +++ b/src/sys/eventfd.rs @@ -1,6 +1,6 @@ use crate::errno::Errno; use crate::Result; -use std::os::unix::io::RawFd; +use std::os::unix::io::{FromRawFd, OwnedFd}; libc_bitflags! { pub struct EfdFlags: libc::c_int { @@ -10,8 +10,8 @@ libc_bitflags! { } } -pub fn eventfd(initval: libc::c_uint, flags: EfdFlags) -> Result { +pub fn eventfd(initval: libc::c_uint, flags: EfdFlags) -> Result { let res = unsafe { libc::eventfd(initval, flags.bits()) }; - Errno::result(res).map(|r| r as RawFd) + Errno::result(res).map(|r| unsafe { OwnedFd::from_raw_fd(r) }) } diff --git a/src/sys/inotify.rs b/src/sys/inotify.rs index 84356ec..e5fe930 100644 --- a/src/sys/inotify.rs +++ b/src/sys/inotify.rs @@ -32,7 +32,7 @@ use libc::{c_char, c_int}; use std::ffi::{CStr, OsStr, OsString}; use std::mem::{size_of, MaybeUninit}; use std::os::unix::ffi::OsStrExt; -use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; +use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd}; use std::ptr; libc_bitflags! { @@ -101,9 +101,9 @@ libc_bitflags! { /// An inotify 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 Inotify { - fd: RawFd, + fd: OwnedFd, } /// This object is returned when you create a new watch on an inotify instance. @@ -143,7 +143,7 @@ impl Inotify { pub fn init(flags: InitFlags) -> Result { let res = Errno::result(unsafe { libc::inotify_init1(flags.bits()) }); - res.map(|fd| Inotify { fd }) + res.map(|fd| Inotify { fd: unsafe { OwnedFd::from_raw_fd(fd) } }) } /// Adds a new watch on the target file or directory. @@ -152,12 +152,12 @@ impl Inotify { /// /// For more information see, [inotify_add_watch(2)](https://man7.org/linux/man-pages/man2/inotify_add_watch.2.html). pub fn add_watch( - self, + &self, path: &P, mask: AddWatchFlags, ) -> Result { let res = path.with_nix_path(|cstr| unsafe { - libc::inotify_add_watch(self.fd, cstr.as_ptr(), mask.bits()) + libc::inotify_add_watch(self.fd.as_raw_fd(), cstr.as_ptr(), mask.bits()) })?; Errno::result(res).map(|wd| WatchDescriptor { wd }) @@ -169,7 +169,7 @@ impl Inotify { /// Returns an EINVAL error if the watch descriptor is invalid. /// /// For more information see, [inotify_rm_watch(2)](https://man7.org/linux/man-pages/man2/inotify_rm_watch.2.html). - pub fn rm_watch(self, wd: WatchDescriptor) -> Result<()> { + pub fn rm_watch(&self, wd: WatchDescriptor) -> Result<()> { cfg_if! { if #[cfg(target_os = "linux")] { let arg = wd.wd; @@ -177,7 +177,7 @@ impl Inotify { let arg = wd.wd as u32; } } - let res = unsafe { libc::inotify_rm_watch(self.fd, arg) }; + let res = unsafe { libc::inotify_rm_watch(self.fd.as_raw_fd(), arg) }; Errno::result(res).map(drop) } @@ -188,14 +188,14 @@ impl Inotify { /// /// Returns as many events as available. If the call was non blocking and no /// events could be read then the EAGAIN error is returned. - pub fn read_events(self) -> Result> { + pub fn read_events(&self) -> Result> { let header_size = size_of::(); const BUFSIZ: usize = 4096; let mut buffer = [0u8; BUFSIZ]; let mut events = Vec::new(); let mut offset = 0; - let nread = read(self.fd, &mut buffer)?; + let nread = read(self.fd.as_raw_fd(), &mut buffer)?; while (nread - offset) >= header_size { let event = unsafe { @@ -235,14 +235,14 @@ impl Inotify { } } -impl AsRawFd for Inotify { - fn as_raw_fd(&self) -> RawFd { - self.fd +impl FromRawFd for Inotify { + unsafe fn from_raw_fd(fd: RawFd) -> Self { + Inotify { fd: OwnedFd::from_raw_fd(fd) } } } -impl FromRawFd for Inotify { - unsafe fn from_raw_fd(fd: RawFd) -> Self { - Inotify { fd } +impl AsFd for Inotify { + fn as_fd(&'_ self) -> BorrowedFd<'_> { + self.fd.as_fd() } } diff --git a/src/sys/ioctl/linux.rs b/src/sys/ioctl/linux.rs index 0c0a209..610b8dd 100644 --- a/src/sys/ioctl/linux.rs +++ b/src/sys/ioctl/linux.rs @@ -1,3 +1,5 @@ +use cfg_if::cfg_if; + /// The datatype used for the ioctl number #[cfg(any(target_os = "android", target_env = "musl"))] #[doc(hidden)] @@ -14,47 +16,41 @@ pub const NRBITS: ioctl_num_type = 8; #[doc(hidden)] pub const TYPEBITS: ioctl_num_type = 8; -#[cfg(any( - target_arch = "mips", - target_arch = "mips64", - target_arch = "powerpc", - target_arch = "powerpc64", - target_arch = "sparc64" -))] -mod consts { - #[doc(hidden)] - pub const NONE: u8 = 1; - #[doc(hidden)] - pub const READ: u8 = 2; - #[doc(hidden)] - pub const WRITE: u8 = 4; - #[doc(hidden)] - pub const SIZEBITS: u8 = 13; - #[doc(hidden)] - pub const DIRBITS: u8 = 3; -} - -// "Generic" ioctl protocol -#[cfg(any( - target_arch = "x86", - target_arch = "arm", - target_arch = "s390x", - target_arch = "x86_64", - target_arch = "aarch64", - target_arch = "riscv32", - target_arch = "riscv64" -))] -mod consts { - #[doc(hidden)] - pub const NONE: u8 = 0; - #[doc(hidden)] - pub const READ: u8 = 2; - #[doc(hidden)] - pub const WRITE: u8 = 1; - #[doc(hidden)] - pub const SIZEBITS: u8 = 14; - #[doc(hidden)] - pub const DIRBITS: u8 = 2; +cfg_if! { + if #[cfg(any( + target_arch = "mips", + target_arch = "mips64", + target_arch = "powerpc", + target_arch = "powerpc64", + target_arch = "sparc64" + ))] { + mod consts { + #[doc(hidden)] + pub const NONE: u8 = 1; + #[doc(hidden)] + pub const READ: u8 = 2; + #[doc(hidden)] + pub const WRITE: u8 = 4; + #[doc(hidden)] + pub const SIZEBITS: u8 = 13; + #[doc(hidden)] + pub const DIRBITS: u8 = 3; + } + } else { + // "Generic" ioctl protocol + mod consts { + #[doc(hidden)] + pub const NONE: u8 = 0; + #[doc(hidden)] + pub const READ: u8 = 2; + #[doc(hidden)] + pub const WRITE: u8 = 1; + #[doc(hidden)] + pub const SIZEBITS: u8 = 14; + #[doc(hidden)] + pub const DIRBITS: u8 = 2; + } + } } pub use self::consts::*; diff --git a/src/sys/ioctl/mod.rs b/src/sys/ioctl/mod.rs index 98d6b5c..0b3fe3e 100644 --- a/src/sys/ioctl/mod.rs +++ b/src/sys/ioctl/mod.rs @@ -712,7 +712,7 @@ macro_rules! ioctl_read_buf { pub unsafe fn $name(fd: $crate::libc::c_int, data: &mut [$ty]) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, request_code_read!($ioty, $nr, data.len() * ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) + convert_ioctl_res!($crate::libc::ioctl(fd, request_code_read!($ioty, $nr, ::std::mem::size_of_val(data)) as $crate::sys::ioctl::ioctl_num_type, data)) } ) } @@ -751,7 +751,7 @@ macro_rules! ioctl_write_buf { pub unsafe fn $name(fd: $crate::libc::c_int, data: &[$ty]) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, request_code_write!($ioty, $nr, data.len() * ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) + convert_ioctl_res!($crate::libc::ioctl(fd, request_code_write!($ioty, $nr, ::std::mem::size_of_val(data)) as $crate::sys::ioctl::ioctl_num_type, data)) } ) } @@ -780,7 +780,7 @@ macro_rules! ioctl_readwrite_buf { pub unsafe fn $name(fd: $crate::libc::c_int, data: &mut [$ty]) -> $crate::Result<$crate::libc::c_int> { - convert_ioctl_res!($crate::libc::ioctl(fd, request_code_readwrite!($ioty, $nr, data.len() * ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) + convert_ioctl_res!($crate::libc::ioctl(fd, request_code_readwrite!($ioty, $nr, ::std::mem::size_of_val(data)) as $crate::sys::ioctl::ioctl_num_type, data)) } ) } diff --git a/src/sys/memfd.rs b/src/sys/memfd.rs index e43e1e5..d147ccf 100644 --- a/src/sys/memfd.rs +++ b/src/sys/memfd.rs @@ -1,7 +1,7 @@ //! Interfaces for managing memory-backed files. use cfg_if::cfg_if; -use std::os::unix::io::RawFd; +use std::os::unix::io::{FromRawFd, OwnedFd, RawFd}; use crate::errno::Errno; use crate::Result; @@ -40,7 +40,8 @@ libc_bitflags!( /// For more information, see [`memfd_create(2)`]. /// /// [`memfd_create(2)`]: https://man7.org/linux/man-pages/man2/memfd_create.2.html -pub fn memfd_create(name: &CStr, flags: MemFdCreateFlag) -> Result { +#[inline] // Delays codegen, preventing linker errors with dylibs and --no-allow-shlib-undefined +pub fn memfd_create(name: &CStr, flags: MemFdCreateFlag) -> Result { let res = unsafe { cfg_if! { if #[cfg(all( @@ -62,5 +63,5 @@ pub fn memfd_create(name: &CStr, flags: MemFdCreateFlag) -> Result { } }; - Errno::result(res).map(|r| r as RawFd) + Errno::result(res).map(|r| unsafe { OwnedFd::from_raw_fd(r as RawFd) }) } diff --git a/src/sys/mman.rs b/src/sys/mman.rs index 2bee091..8cfd6d6 100644 --- a/src/sys/mman.rs +++ b/src/sys/mman.rs @@ -8,7 +8,7 @@ use crate::Result; #[cfg(feature = "fs")] use crate::{fcntl::OFlag, sys::stat::Mode}; use libc::{self, c_int, c_void, off_t, size_t}; -use std::{os::unix::io::RawFd, num::NonZeroUsize}; +use std::{num::NonZeroUsize, os::unix::io::{AsRawFd, AsFd}}; libc_bitflags! { /// Desired memory protection of a memory mapping. @@ -82,7 +82,7 @@ libc_bitflags! { /// Do not reserve swap space for this mapping. /// /// This was removed in FreeBSD 11 and is unused in DragonFlyBSD. - #[cfg(not(any(target_os = "dragonfly", target_os = "freebsd")))] + #[cfg(not(any(target_os = "dragonfly", target_os = "freebsd", target_os = "aix")))] #[cfg_attr(docsrs, doc(cfg(all())))] MAP_NORESERVE; /// Populate page tables for a mapping. @@ -282,6 +282,8 @@ libc_enum! { #[cfg_attr(docsrs, doc(cfg(all())))] MADV_DODUMP, /// Specify that the application no longer needs the pages in the given range. + #[cfg(not(target_os = "aix"))] + #[cfg_attr(docsrs, doc(cfg(all())))] MADV_FREE, /// Request that the system not flush the current range to disk unless it needs to. #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] @@ -416,20 +418,20 @@ pub fn munlockall() -> Result<()> { /// See the [`mmap(2)`] man page for detailed requirements. /// /// [`mmap(2)`]: https://man7.org/linux/man-pages/man2/mmap.2.html -pub unsafe fn mmap( +pub unsafe fn mmap( addr: Option, length: NonZeroUsize, prot: ProtFlags, flags: MapFlags, - fd: RawFd, + f: Option, offset: off_t, ) -> Result<*mut c_void> { - let ptr = addr.map_or( - std::ptr::null_mut(), - |a| usize::from(a) as *mut c_void - ); - - let ret = libc::mmap(ptr, length.into(), prot.bits(), flags.bits(), fd, offset); + let ptr = + addr.map_or(std::ptr::null_mut(), |a| usize::from(a) as *mut c_void); + + let fd = f.map(|f| f.as_fd().as_raw_fd()).unwrap_or(-1); + let ret = + libc::mmap(ptr, length.into(), prot.bits(), flags.bits(), fd, offset); if ret == libc::MAP_FAILED { Err(Errno::last()) @@ -519,11 +521,12 @@ pub unsafe fn madvise( /// # use nix::libc::size_t; /// # use nix::sys::mman::{mmap, mprotect, MapFlags, ProtFlags}; /// # use std::ptr; +/// # use std::os::unix::io::BorrowedFd; /// const ONE_K: size_t = 1024; /// let one_k_non_zero = std::num::NonZeroUsize::new(ONE_K).unwrap(); /// let mut slice: &mut [u8] = unsafe { -/// let mem = mmap(None, one_k_non_zero, ProtFlags::PROT_NONE, -/// MapFlags::MAP_ANON | MapFlags::MAP_PRIVATE, -1, 0).unwrap(); +/// let mem = mmap::(None, one_k_non_zero, ProtFlags::PROT_NONE, +/// MapFlags::MAP_ANON | MapFlags::MAP_PRIVATE, None, 0).unwrap(); /// mprotect(mem, ONE_K, ProtFlags::PROT_READ | ProtFlags::PROT_WRITE).unwrap(); /// std::slice::from_raw_parts_mut(mem as *mut u8, ONE_K) /// }; @@ -567,9 +570,11 @@ pub fn shm_open

    ( name: &P, flag: OFlag, mode: Mode - ) -> Result + ) -> Result where P: ?Sized + NixPath { + use std::os::unix::io::{FromRawFd, OwnedFd}; + let ret = name.with_nix_path(|cstr| { #[cfg(any(target_os = "macos", target_os = "ios"))] unsafe { @@ -581,7 +586,10 @@ pub fn shm_open

    ( } })?; - Errno::result(ret) + match ret { + -1 => Err(Errno::last()), + fd => Ok(unsafe{ OwnedFd::from_raw_fd(fd) }) + } } } diff --git a/src/sys/mod.rs b/src/sys/mod.rs index 2065059..bf047b3 100644 --- a/src/sys/mod.rs +++ b/src/sys/mod.rs @@ -25,7 +25,6 @@ feature! { target_os = "macos", target_os = "netbsd", target_os = "openbsd"))] - #[allow(missing_docs)] pub mod event; #[cfg(any(target_os = "android", target_os = "linux"))] @@ -68,6 +67,12 @@ feature! { pub mod personality; } +#[cfg(target_os = "linux")] +feature! { + #![feature = "process"] + pub mod prctl; +} + feature! { #![feature = "pthread"] pub mod pthread; @@ -111,7 +116,6 @@ feature! { pub mod resource; } -#[cfg(not(target_os = "redox"))] feature! { #![feature = "poll"] pub mod select; @@ -139,7 +143,6 @@ feature! { pub mod signalfd; } -#[cfg(not(target_os = "redox"))] feature! { #![feature = "socket"] #[allow(missing_docs)] diff --git a/src/sys/personality.rs b/src/sys/personality.rs index f295a05..30231dd 100644 --- a/src/sys/personality.rs +++ b/src/sys/personality.rs @@ -80,7 +80,10 @@ pub fn get() -> Result { /// /// Example: /// -/// ``` +// Disable test on aarch64 until we know why it fails. +// https://github.com/nix-rust/nix/issues/2060 +#[cfg_attr(target_arch = "aarch64", doc = " ```no_run")] +#[cfg_attr(not(target_arch = "aarch64"), doc = " ```")] /// # use nix::sys::personality::{self, Persona}; /// let mut pers = personality::get().unwrap(); /// assert!(!pers.contains(Persona::ADDR_NO_RANDOMIZE)); diff --git a/src/sys/prctl.rs b/src/sys/prctl.rs new file mode 100644 index 0000000..995382c --- /dev/null +++ b/src/sys/prctl.rs @@ -0,0 +1,208 @@ +//! prctl is a Linux-only API for performing operations on a process or thread. +//! +//! Note that careless use of some prctl() operations can confuse the user-space run-time +//! environment, so these operations should be used with care. +//! +//! For more documentation, please read [prctl(2)](https://man7.org/linux/man-pages/man2/prctl.2.html). + +use crate::errno::Errno; +use crate::sys::signal::Signal; +use crate::Result; + +use libc::{c_int, c_ulong}; +use std::convert::TryFrom; +use std::ffi::{CStr, CString}; + +libc_enum! { + /// The type of hardware memory corruption kill policy for the thread. + + #[repr(i32)] + #[non_exhaustive] + #[allow(non_camel_case_types)] + pub enum PrctlMCEKillPolicy { + /// The thread will receive SIGBUS as soon as a memory corruption is detected. + PR_MCE_KILL_EARLY, + /// The process is killed only when it accesses a corrupted page. + PR_MCE_KILL_LATE, + /// Uses the system-wide default. + PR_MCE_KILL_DEFAULT, + } + impl TryFrom +} + +fn prctl_set_bool(option: c_int, status: bool) -> Result<()> { + let res = unsafe { libc::prctl(option, status as c_ulong, 0, 0, 0) }; + Errno::result(res).map(drop) +} + +fn prctl_get_bool(option: c_int) -> Result { + let res = unsafe { libc::prctl(option, 0, 0, 0, 0) }; + Errno::result(res).map(|res| res != 0) +} + +/// Set the "child subreaper" attribute for this process +pub fn set_child_subreaper(attribute: bool) -> Result<()> { + prctl_set_bool(libc::PR_SET_CHILD_SUBREAPER, attribute) +} + +/// Get the "child subreaper" attribute for this process +pub fn get_child_subreaper() -> Result { + // prctl writes into this var + let mut subreaper: c_int = 0; + + let res = unsafe { libc::prctl(libc::PR_GET_CHILD_SUBREAPER, &mut subreaper, 0, 0, 0) }; + + Errno::result(res).map(|_| subreaper != 0) +} + +/// Set the dumpable attribute which determines if core dumps are created for this process. +pub fn set_dumpable(attribute: bool) -> Result<()> { + prctl_set_bool(libc::PR_SET_DUMPABLE, attribute) +} + +/// Get the dumpable attribute for this process. +pub fn get_dumpable() -> Result { + prctl_get_bool(libc::PR_GET_DUMPABLE) +} + +/// Set the "keep capabilities" attribute for this process. This causes the thread to retain +/// capabilities even if it switches its UID to a nonzero value. +pub fn set_keepcaps(attribute: bool) -> Result<()> { + prctl_set_bool(libc::PR_SET_KEEPCAPS, attribute) +} + +/// Get the "keep capabilities" attribute for this process +pub fn get_keepcaps() -> Result { + prctl_get_bool(libc::PR_GET_KEEPCAPS) +} + +/// Clear the thread memory corruption kill policy and use the system-wide default +pub fn clear_mce_kill() -> Result<()> { + let res = unsafe { libc::prctl(libc::PR_MCE_KILL, libc::PR_MCE_KILL_CLEAR, 0, 0, 0) }; + + Errno::result(res).map(drop) +} + +/// Set the thread memory corruption kill policy +pub fn set_mce_kill(policy: PrctlMCEKillPolicy) -> Result<()> { + let res = unsafe { + libc::prctl( + libc::PR_MCE_KILL, + libc::PR_MCE_KILL_SET, + policy as c_ulong, + 0, + 0, + ) + }; + + Errno::result(res).map(drop) +} + +/// Get the thread memory corruption kill policy +pub fn get_mce_kill() -> Result { + let res = unsafe { libc::prctl(libc::PR_MCE_KILL_GET, 0, 0, 0, 0) }; + + match Errno::result(res) { + Ok(val) => Ok(PrctlMCEKillPolicy::try_from(val)?), + Err(e) => Err(e), + } +} + +/// Set the parent-death signal of the calling process. This is the signal that the calling process +/// will get when its parent dies. +pub fn set_pdeathsig>>(signal: T) -> Result<()> { + let sig = match signal.into() { + Some(s) => s as c_int, + None => 0, + }; + + let res = unsafe { libc::prctl(libc::PR_SET_PDEATHSIG, sig, 0, 0, 0) }; + + Errno::result(res).map(drop) +} + +/// Returns the current parent-death signal +pub fn get_pdeathsig() -> Result> { + // prctl writes into this var + let mut sig: c_int = 0; + + let res = unsafe { libc::prctl(libc::PR_GET_PDEATHSIG, &mut sig, 0, 0, 0) }; + + match Errno::result(res) { + Ok(_) => Ok(match sig { + 0 => None, + _ => Some(Signal::try_from(sig)?), + }), + Err(e) => Err(e), + } +} + +/// Set the name of the calling thread. Strings longer than 15 bytes will be truncated. +pub fn set_name(name: &CStr) -> Result<()> { + let res = unsafe { libc::prctl(libc::PR_SET_NAME, name.as_ptr(), 0, 0, 0) }; + + Errno::result(res).map(drop) +} + +/// Return the name of the calling thread +pub fn get_name() -> Result { + // Size of buffer determined by linux/sched.h TASK_COMM_LEN + let buf = [0u8; 16]; + + let res = unsafe { libc::prctl(libc::PR_GET_NAME, &buf, 0, 0, 0) }; + + let len = buf.iter().position(|&c| c == 0).unwrap_or(buf.len()); + let name = CStr::from_bytes_with_nul(&buf[..=len]).map_err(|_| Errno::EINVAL)?; + + Errno::result(res).map(|_| name.to_owned()) +} + +/// Sets the timer slack value for the calling thread. Timer slack is used by the kernel to group +/// timer expirations and make them the supplied amount of nanoseconds late. +pub fn set_timerslack(ns: u64) -> Result<()> { + let res = unsafe { libc::prctl(libc::PR_SET_TIMERSLACK, ns, 0, 0, 0) }; + + Errno::result(res).map(drop) +} + +/// Get the timerslack for the calling thread. +pub fn get_timerslack() -> Result { + let res = unsafe { libc::prctl(libc::PR_GET_TIMERSLACK, 0, 0, 0, 0) }; + + Errno::result(res) +} + +/// Disable all performance counters attached to the calling process. +pub fn task_perf_events_disable() -> Result<()> { + let res = unsafe { libc::prctl(libc::PR_TASK_PERF_EVENTS_DISABLE, 0, 0, 0, 0) }; + + Errno::result(res).map(drop) +} + +/// Enable all performance counters attached to the calling process. +pub fn task_perf_events_enable() -> Result<()> { + let res = unsafe { libc::prctl(libc::PR_TASK_PERF_EVENTS_ENABLE, 0, 0, 0, 0) }; + + Errno::result(res).map(drop) +} + +/// Set the calling threads "no new privs" attribute. Once set this option can not be unset. +pub fn set_no_new_privs() -> Result<()> { + prctl_set_bool(libc::PR_SET_NO_NEW_PRIVS, true) // Cannot be unset +} + +/// Get the "no new privs" attribute for the calling thread. +pub fn get_no_new_privs() -> Result { + prctl_get_bool(libc::PR_GET_NO_NEW_PRIVS) +} + +/// Set the state of the "THP disable" flag for the calling thread. Setting this disables +/// transparent huge pages. +pub fn set_thp_disable(flag: bool) -> Result<()> { + prctl_set_bool(libc::PR_SET_THP_DISABLE, flag) +} + +/// Get the "THP disable" flag for the calling thread. +pub fn get_thp_disable() -> Result { + prctl_get_bool(libc::PR_GET_THP_DISABLE) +} diff --git a/src/sys/ptrace/linux.rs b/src/sys/ptrace/linux.rs index 9687e05..8c134cf 100644 --- a/src/sys/ptrace/linux.rs +++ b/src/sys/ptrace/linux.rs @@ -269,7 +269,7 @@ unsafe fn ptrace_other( .map(|_| 0) } -/// Set options, as with `ptrace(PTRACE_SETOPTIONS,...)`. +/// Set options, as with `ptrace(PTRACE_SETOPTIONS, ...)`. pub fn setoptions(pid: Pid, options: Options) -> Result<()> { let res = unsafe { libc::ptrace( @@ -282,17 +282,17 @@ pub fn setoptions(pid: Pid, options: Options) -> Result<()> { Errno::result(res).map(drop) } -/// Gets a ptrace event as described by `ptrace(PTRACE_GETEVENTMSG,...)` +/// Gets a ptrace event as described by `ptrace(PTRACE_GETEVENTMSG, ...)` pub fn getevent(pid: Pid) -> Result { ptrace_get_data::(Request::PTRACE_GETEVENTMSG, pid) } -/// Get siginfo as with `ptrace(PTRACE_GETSIGINFO,...)` +/// Get siginfo as with `ptrace(PTRACE_GETSIGINFO, ...)` pub fn getsiginfo(pid: Pid) -> Result { ptrace_get_data::(Request::PTRACE_GETSIGINFO, pid) } -/// Set siginfo as with `ptrace(PTRACE_SETSIGINFO,...)` +/// Set siginfo as with `ptrace(PTRACE_SETSIGINFO, ...)` pub fn setsiginfo(pid: Pid, sig: &siginfo_t) -> Result<()> { let ret = unsafe { Errno::clear(); @@ -517,12 +517,14 @@ pub fn sysemu_step>>(pid: Pid, sig: T) -> Result<()> { } } -/// Reads a word from a processes memory at the given address +/// Reads a word from a processes memory at the given address, as with +/// ptrace(PTRACE_PEEKDATA, ...) pub fn read(pid: Pid, addr: AddressType) -> Result { ptrace_peek(Request::PTRACE_PEEKDATA, pid, addr, ptr::null_mut()) } -/// Writes a word into the processes memory at the given address +/// Writes a word into the processes memory at the given address, as with +/// ptrace(PTRACE_POKEDATA, ...) /// /// # Safety /// @@ -536,13 +538,13 @@ pub unsafe fn write( ptrace_other(Request::PTRACE_POKEDATA, pid, addr, data).map(drop) } -/// Reads a word from a user area at `offset`. +/// Reads a word from a user area at `offset`, as with ptrace(PTRACE_PEEKUSER, ...). /// The user struct definition can be found in `/usr/include/sys/user.h`. pub fn read_user(pid: Pid, offset: AddressType) -> Result { ptrace_peek(Request::PTRACE_PEEKUSER, pid, offset, ptr::null_mut()) } -/// Writes a word to a user area at `offset`. +/// Writes a word to a user area at `offset`, as with ptrace(PTRACE_POKEUSER, ...). /// The user struct definition can be found in `/usr/include/sys/user.h`. /// /// # Safety diff --git a/src/sys/ptrace/mod.rs b/src/sys/ptrace/mod.rs index 2b121c0..88648ac 100644 --- a/src/sys/ptrace/mod.rs +++ b/src/sys/ptrace/mod.rs @@ -1,4 +1,4 @@ -///! Provides helpers for making ptrace system calls +//! Provides helpers for making ptrace system calls #[cfg(any(target_os = "android", target_os = "linux"))] mod linux; diff --git a/src/sys/quota.rs b/src/sys/quota.rs index b3c44ca..a32d07a 100644 --- a/src/sys/quota.rs +++ b/src/sys/quota.rs @@ -21,9 +21,8 @@ use std::{mem, ptr}; struct QuotaCmd(QuotaSubCmd, QuotaType); impl QuotaCmd { - #[allow(unused_unsafe)] fn as_int(&self) -> c_int { - unsafe { libc::QCMD(self.0 as i32, self.1 as i32) } + libc::QCMD(self.0 as i32, self.1 as i32) } } diff --git a/src/sys/resource.rs b/src/sys/resource.rs index 8927737..f42d32e 100644 --- a/src/sys/resource.rs +++ b/src/sys/resource.rs @@ -20,6 +20,7 @@ cfg_if! { target_os = "ios", target_os = "android", target_os = "dragonfly", + target_os = "aix", all(target_os = "linux", not(target_env = "gnu")) ))]{ use libc::rlimit; @@ -51,6 +52,7 @@ libc_enum! { target_os = "ios", target_os = "android", target_os = "dragonfly", + target_os = "aix", all(target_os = "linux", not(any(target_env = "gnu", target_env = "uclibc"))) ), repr(i32))] #[non_exhaustive] @@ -115,6 +117,7 @@ libc_enum! { target_os = "netbsd", target_os = "openbsd", target_os = "linux", + target_os = "aix", ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The maximum number of simultaneous processes for this user id. @@ -131,6 +134,7 @@ libc_enum! { target_os = "netbsd", target_os = "openbsd", target_os = "linux", + target_os = "aix", ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// When there is memory pressure and swap is available, prioritize diff --git a/src/sys/select.rs b/src/sys/select.rs index 7a94cff..0e2193b 100644 --- a/src/sys/select.rs +++ b/src/sys/select.rs @@ -7,7 +7,7 @@ use std::convert::TryFrom; use std::iter::FusedIterator; use std::mem; use std::ops::Range; -use std::os::unix::io::RawFd; +use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; use std::ptr::{null, null_mut}; pub use libc::FD_SETSIZE; @@ -15,7 +15,10 @@ pub use libc::FD_SETSIZE; /// Contains a set of file descriptors used by [`select`] #[repr(transparent)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub struct FdSet(libc::fd_set); +pub struct FdSet<'fd> { + set: libc::fd_set, + _fd: std::marker::PhantomData>, +} fn assert_fd_valid(fd: RawFd) { assert!( @@ -24,37 +27,40 @@ fn assert_fd_valid(fd: RawFd) { ); } -impl FdSet { +impl<'fd> FdSet<'fd> { /// Create an empty `FdSet` - pub fn new() -> FdSet { + pub fn new() -> FdSet<'fd> { let mut fdset = mem::MaybeUninit::uninit(); unsafe { libc::FD_ZERO(fdset.as_mut_ptr()); - FdSet(fdset.assume_init()) + Self { + set: fdset.assume_init(), + _fd: std::marker::PhantomData, + } } } /// Add a file descriptor to an `FdSet` - pub fn insert(&mut self, fd: RawFd) { - assert_fd_valid(fd); - unsafe { libc::FD_SET(fd, &mut self.0) }; + pub fn insert(&mut self, fd: &'fd Fd) { + assert_fd_valid(fd.as_fd().as_raw_fd()); + unsafe { libc::FD_SET(fd.as_fd().as_raw_fd(), &mut self.set) }; } /// Remove a file descriptor from an `FdSet` - pub fn remove(&mut self, fd: RawFd) { - assert_fd_valid(fd); - unsafe { libc::FD_CLR(fd, &mut self.0) }; + pub fn remove(&mut self, fd: &'fd Fd) { + assert_fd_valid(fd.as_fd().as_raw_fd()); + unsafe { libc::FD_CLR(fd.as_fd().as_raw_fd(), &mut self.set) }; } /// Test an `FdSet` for the presence of a certain file descriptor. - pub fn contains(&self, fd: RawFd) -> bool { - assert_fd_valid(fd); - unsafe { libc::FD_ISSET(fd, &self.0) } + pub fn contains(&self, fd: &'fd Fd) -> bool { + assert_fd_valid(fd.as_fd().as_raw_fd()); + unsafe { libc::FD_ISSET(fd.as_fd().as_raw_fd(), &self.set) } } /// Remove all file descriptors from this `FdSet`. pub fn clear(&mut self) { - unsafe { libc::FD_ZERO(&mut self.0) }; + unsafe { libc::FD_ZERO(&mut self.set) }; } /// Finds the highest file descriptor in the set. @@ -66,15 +72,18 @@ impl FdSet { /// # Example /// /// ``` + /// # use std::os::unix::io::{AsRawFd, BorrowedFd}; /// # use nix::sys::select::FdSet; + /// let fd_four = unsafe {BorrowedFd::borrow_raw(4)}; + /// let fd_nine = unsafe {BorrowedFd::borrow_raw(9)}; /// let mut set = FdSet::new(); - /// set.insert(4); - /// set.insert(9); - /// assert_eq!(set.highest(), Some(9)); + /// set.insert(&fd_four); + /// set.insert(&fd_nine); + /// assert_eq!(set.highest().map(|borrowed_fd|borrowed_fd.as_raw_fd()), Some(9)); /// ``` /// /// [`select`]: fn.select.html - pub fn highest(&self) -> Option { + pub fn highest(&self) -> Option> { self.fds(None).next_back() } @@ -88,11 +97,13 @@ impl FdSet { /// /// ``` /// # use nix::sys::select::FdSet; - /// # use std::os::unix::io::RawFd; + /// # use std::os::unix::io::{AsRawFd, BorrowedFd, RawFd}; /// let mut set = FdSet::new(); - /// set.insert(4); - /// set.insert(9); - /// let fds: Vec = set.fds(None).collect(); + /// let fd_four = unsafe {BorrowedFd::borrow_raw(4)}; + /// let fd_nine = unsafe {BorrowedFd::borrow_raw(9)}; + /// set.insert(&fd_four); + /// set.insert(&fd_nine); + /// let fds: Vec = set.fds(None).map(|borrowed_fd|borrowed_fd.as_raw_fd()).collect(); /// assert_eq!(fds, vec![4, 9]); /// ``` #[inline] @@ -104,7 +115,7 @@ impl FdSet { } } -impl Default for FdSet { +impl<'fd> Default for FdSet<'fd> { fn default() -> Self { Self::new() } @@ -112,18 +123,19 @@ impl Default for FdSet { /// Iterator over `FdSet`. #[derive(Debug)] -pub struct Fds<'a> { - set: &'a FdSet, +pub struct Fds<'a, 'fd> { + set: &'a FdSet<'fd>, range: Range, } -impl<'a> Iterator for Fds<'a> { - type Item = RawFd; +impl<'a, 'fd> Iterator for Fds<'a, 'fd> { + type Item = BorrowedFd<'fd>; - fn next(&mut self) -> Option { + fn next(&mut self) -> Option { for i in &mut self.range { - if self.set.contains(i as RawFd) { - return Some(i as RawFd); + let borrowed_i = unsafe { BorrowedFd::borrow_raw(i as RawFd) }; + if self.set.contains(&borrowed_i) { + return Some(borrowed_i); } } None @@ -136,19 +148,20 @@ impl<'a> Iterator for Fds<'a> { } } -impl<'a> DoubleEndedIterator for Fds<'a> { +impl<'a, 'fd> DoubleEndedIterator for Fds<'a, 'fd> { #[inline] - fn next_back(&mut self) -> Option { + fn next_back(&mut self) -> Option> { while let Some(i) = self.range.next_back() { - if self.set.contains(i as RawFd) { - return Some(i as RawFd); + let borrowed_i = unsafe { BorrowedFd::borrow_raw(i as RawFd) }; + if self.set.contains(&borrowed_i) { + return Some(borrowed_i); } } None } } -impl<'a> FusedIterator for Fds<'a> {} +impl<'a, 'fd> FusedIterator for Fds<'a, 'fd> {} /// Monitors file descriptors for readiness /// @@ -173,7 +186,7 @@ impl<'a> FusedIterator for Fds<'a> {} /// [select(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/select.html) /// /// [`FdSet::highest`]: struct.FdSet.html#method.highest -pub fn select<'a, N, R, W, E, T>( +pub fn select<'a, 'fd, N, R, W, E, T>( nfds: N, readfds: R, writefds: W, @@ -181,10 +194,11 @@ pub fn select<'a, N, R, W, E, T>( timeout: T, ) -> Result where + 'fd: 'a, N: Into>, - R: Into>, - W: Into>, - E: Into>, + R: Into>>, + W: Into>>, + E: Into>>, T: Into>, { let mut readfds = readfds.into(); @@ -197,7 +211,11 @@ where .iter_mut() .chain(writefds.iter_mut()) .chain(errorfds.iter_mut()) - .map(|set| set.highest().unwrap_or(-1)) + .map(|set| { + set.highest() + .map(|borrowed_fd| borrowed_fd.as_raw_fd()) + .unwrap_or(-1) + }) .max() .unwrap_or(-1) + 1 @@ -256,17 +274,18 @@ use crate::sys::signal::SigSet; /// [The new pselect() system call](https://lwn.net/Articles/176911/) /// /// [`FdSet::highest`]: struct.FdSet.html#method.highest -pub fn pselect<'a, N, R, W, E, T, S>(nfds: N, +pub fn pselect<'a, 'fd, N, R, W, E, T, S>(nfds: N, readfds: R, writefds: W, errorfds: E, timeout: T, sigmask: S) -> Result where + 'fd: 'a, N: Into>, - R: Into>, - W: Into>, - E: Into>, + R: Into>>, + W: Into>>, + E: Into>>, T: Into>, S: Into>, { @@ -280,7 +299,7 @@ where readfds.iter_mut() .chain(writefds.iter_mut()) .chain(errorfds.iter_mut()) - .map(|set| set.highest().unwrap_or(-1)) + .map(|set| set.highest().map(|borrowed_fd|borrowed_fd.as_raw_fd()).unwrap_or(-1)) .max() .unwrap_or(-1) + 1 }); @@ -303,20 +322,22 @@ where mod tests { use super::*; use crate::sys::time::{TimeVal, TimeValLike}; - use crate::unistd::{pipe, write}; - use std::os::unix::io::RawFd; + use crate::unistd::{close, pipe, write}; + use std::os::unix::io::{FromRawFd, OwnedFd, RawFd}; #[test] fn fdset_insert() { let mut fd_set = FdSet::new(); for i in 0..FD_SETSIZE { - assert!(!fd_set.contains(i as RawFd)); + let borrowed_i = unsafe { BorrowedFd::borrow_raw(i as RawFd) }; + assert!(!fd_set.contains(&borrowed_i)); } - fd_set.insert(7); + let fd_seven = unsafe { BorrowedFd::borrow_raw(7) }; + fd_set.insert(&fd_seven); - assert!(fd_set.contains(7)); + assert!(fd_set.contains(&fd_seven)); } #[test] @@ -324,107 +345,183 @@ mod tests { let mut fd_set = FdSet::new(); for i in 0..FD_SETSIZE { - assert!(!fd_set.contains(i as RawFd)); + let borrowed_i = unsafe { BorrowedFd::borrow_raw(i as RawFd) }; + assert!(!fd_set.contains(&borrowed_i)); } - fd_set.insert(7); - fd_set.remove(7); + let fd_seven = unsafe { BorrowedFd::borrow_raw(7) }; + fd_set.insert(&fd_seven); + fd_set.remove(&fd_seven); for i in 0..FD_SETSIZE { - assert!(!fd_set.contains(i as RawFd)); + let borrowed_i = unsafe { BorrowedFd::borrow_raw(i as RawFd) }; + assert!(!fd_set.contains(&borrowed_i)); } } #[test] + #[allow(non_snake_case)] fn fdset_clear() { let mut fd_set = FdSet::new(); - fd_set.insert(1); - fd_set.insert((FD_SETSIZE / 2) as RawFd); - fd_set.insert((FD_SETSIZE - 1) as RawFd); + let fd_one = unsafe { BorrowedFd::borrow_raw(1) }; + let fd_FD_SETSIZE_devided_by_two = + unsafe { BorrowedFd::borrow_raw((FD_SETSIZE / 2) as RawFd) }; + let fd_FD_SETSIZE_minus_one = + unsafe { BorrowedFd::borrow_raw((FD_SETSIZE - 1) as RawFd) }; + fd_set.insert(&fd_one); + fd_set.insert(&fd_FD_SETSIZE_devided_by_two); + fd_set.insert(&fd_FD_SETSIZE_minus_one); fd_set.clear(); for i in 0..FD_SETSIZE { - assert!(!fd_set.contains(i as RawFd)); + let borrowed_i = unsafe { BorrowedFd::borrow_raw(i as RawFd) }; + assert!(!fd_set.contains(&borrowed_i)); } } #[test] fn fdset_highest() { let mut set = FdSet::new(); - assert_eq!(set.highest(), None); - set.insert(0); - assert_eq!(set.highest(), Some(0)); - set.insert(90); - assert_eq!(set.highest(), Some(90)); - set.remove(0); - assert_eq!(set.highest(), Some(90)); - set.remove(90); - assert_eq!(set.highest(), None); - - set.insert(4); - set.insert(5); - set.insert(7); - assert_eq!(set.highest(), Some(7)); + assert_eq!( + set.highest().map(|borrowed_fd| borrowed_fd.as_raw_fd()), + None + ); + let fd_zero = unsafe { BorrowedFd::borrow_raw(0) }; + let fd_ninety = unsafe { BorrowedFd::borrow_raw(90) }; + set.insert(&fd_zero); + assert_eq!( + set.highest().map(|borrowed_fd| borrowed_fd.as_raw_fd()), + Some(0) + ); + set.insert(&fd_ninety); + assert_eq!( + set.highest().map(|borrowed_fd| borrowed_fd.as_raw_fd()), + Some(90) + ); + set.remove(&fd_zero); + assert_eq!( + set.highest().map(|borrowed_fd| borrowed_fd.as_raw_fd()), + Some(90) + ); + set.remove(&fd_ninety); + assert_eq!( + set.highest().map(|borrowed_fd| borrowed_fd.as_raw_fd()), + None + ); + + let fd_four = unsafe { BorrowedFd::borrow_raw(4) }; + let fd_five = unsafe { BorrowedFd::borrow_raw(5) }; + let fd_seven = unsafe { BorrowedFd::borrow_raw(7) }; + set.insert(&fd_four); + set.insert(&fd_five); + set.insert(&fd_seven); + assert_eq!( + set.highest().map(|borrowed_fd| borrowed_fd.as_raw_fd()), + Some(7) + ); } #[test] fn fdset_fds() { let mut set = FdSet::new(); - assert_eq!(set.fds(None).collect::>(), vec![]); - set.insert(0); - assert_eq!(set.fds(None).collect::>(), vec![0]); - set.insert(90); - assert_eq!(set.fds(None).collect::>(), vec![0, 90]); + let fd_zero = unsafe { BorrowedFd::borrow_raw(0) }; + let fd_ninety = unsafe { BorrowedFd::borrow_raw(90) }; + assert_eq!( + set.fds(None) + .map(|borrowed_fd| borrowed_fd.as_raw_fd()) + .collect::>(), + vec![] + ); + set.insert(&fd_zero); + assert_eq!( + set.fds(None) + .map(|borrowed_fd| borrowed_fd.as_raw_fd()) + .collect::>(), + vec![0] + ); + set.insert(&fd_ninety); + assert_eq!( + set.fds(None) + .map(|borrowed_fd| borrowed_fd.as_raw_fd()) + .collect::>(), + vec![0, 90] + ); // highest limit - assert_eq!(set.fds(Some(89)).collect::>(), vec![0]); - assert_eq!(set.fds(Some(90)).collect::>(), vec![0, 90]); + assert_eq!( + set.fds(Some(89)) + .map(|borrowed_fd| borrowed_fd.as_raw_fd()) + .collect::>(), + vec![0] + ); + assert_eq!( + set.fds(Some(90)) + .map(|borrowed_fd| borrowed_fd.as_raw_fd()) + .collect::>(), + vec![0, 90] + ); } #[test] fn test_select() { let (r1, w1) = pipe().unwrap(); - write(w1, b"hi!").unwrap(); + let r1 = unsafe { OwnedFd::from_raw_fd(r1) }; + let w1 = unsafe { OwnedFd::from_raw_fd(w1) }; let (r2, _w2) = pipe().unwrap(); + let r2 = unsafe { OwnedFd::from_raw_fd(r2) }; + write(w1.as_raw_fd(), b"hi!").unwrap(); let mut fd_set = FdSet::new(); - fd_set.insert(r1); - fd_set.insert(r2); + fd_set.insert(&r1); + fd_set.insert(&r2); let mut timeout = TimeVal::seconds(10); assert_eq!( 1, select(None, &mut fd_set, None, None, &mut timeout).unwrap() ); - assert!(fd_set.contains(r1)); - assert!(!fd_set.contains(r2)); + assert!(fd_set.contains(&r1)); + assert!(!fd_set.contains(&r2)); + close(_w2).unwrap(); } #[test] fn test_select_nfds() { let (r1, w1) = pipe().unwrap(); - write(w1, b"hi!").unwrap(); let (r2, _w2) = pipe().unwrap(); + let r1 = unsafe { OwnedFd::from_raw_fd(r1) }; + let w1 = unsafe { OwnedFd::from_raw_fd(w1) }; + let r2 = unsafe { OwnedFd::from_raw_fd(r2) }; + write(w1.as_raw_fd(), b"hi!").unwrap(); let mut fd_set = FdSet::new(); - fd_set.insert(r1); - fd_set.insert(r2); + fd_set.insert(&r1); + fd_set.insert(&r2); let mut timeout = TimeVal::seconds(10); - assert_eq!( - 1, - select( - Some(fd_set.highest().unwrap() + 1), - &mut fd_set, - None, - None, - &mut timeout - ) - .unwrap() - ); - assert!(fd_set.contains(r1)); - assert!(!fd_set.contains(r2)); + { + assert_eq!( + 1, + select( + Some( + fd_set + .highest() + .map(|borrowed_fd| borrowed_fd.as_raw_fd()) + .unwrap() + + 1 + ), + &mut fd_set, + None, + None, + &mut timeout + ) + .unwrap() + ); + } + assert!(fd_set.contains(&r1)); + assert!(!fd_set.contains(&r2)); + close(_w2).unwrap(); } #[test] @@ -432,16 +529,17 @@ mod tests { let (r1, w1) = pipe().unwrap(); write(w1, b"hi!").unwrap(); let (r2, _w2) = pipe().unwrap(); - + let r1 = unsafe { OwnedFd::from_raw_fd(r1) }; + let r2 = unsafe { OwnedFd::from_raw_fd(r2) }; let mut fd_set = FdSet::new(); - fd_set.insert(r1); - fd_set.insert(r2); + fd_set.insert(&r1); + fd_set.insert(&r2); let mut timeout = TimeVal::seconds(10); assert_eq!( 1, select( - ::std::cmp::max(r1, r2) + 1, + std::cmp::max(r1.as_raw_fd(), r2.as_raw_fd()) + 1, &mut fd_set, None, None, @@ -449,7 +547,8 @@ mod tests { ) .unwrap() ); - assert!(fd_set.contains(r1)); - assert!(!fd_set.contains(r2)); + assert!(fd_set.contains(&r1)); + assert!(!fd_set.contains(&r2)); + close(_w2).unwrap(); } } diff --git a/src/sys/sendfile.rs b/src/sys/sendfile.rs index fb293a4..9f3c333 100644 --- a/src/sys/sendfile.rs +++ b/src/sys/sendfile.rs @@ -1,7 +1,7 @@ //! Send data from a file to a socket, bypassing userland. use cfg_if::cfg_if; -use std::os::unix::io::RawFd; +use std::os::unix::io::{AsFd, AsRawFd}; use std::ptr; use libc::{self, off_t}; @@ -23,16 +23,23 @@ use crate::Result; /// For more information, see [the sendfile(2) man page.](https://man7.org/linux/man-pages/man2/sendfile.2.html) #[cfg(any(target_os = "android", target_os = "linux"))] #[cfg_attr(docsrs, doc(cfg(all())))] -pub fn sendfile( - out_fd: RawFd, - in_fd: RawFd, +pub fn sendfile( + out_fd: F1, + in_fd: F2, offset: Option<&mut off_t>, count: usize, ) -> Result { let offset = offset .map(|offset| offset as *mut _) .unwrap_or(ptr::null_mut()); - let ret = unsafe { libc::sendfile(out_fd, in_fd, offset, count) }; + let ret = unsafe { + libc::sendfile( + out_fd.as_fd().as_raw_fd(), + in_fd.as_fd().as_raw_fd(), + offset, + count, + ) + }; Errno::result(ret).map(|r| r as usize) } @@ -50,16 +57,23 @@ pub fn sendfile( /// For more information, see [the sendfile(2) man page.](https://man7.org/linux/man-pages/man2/sendfile.2.html) #[cfg(target_os = "linux")] #[cfg_attr(docsrs, doc(cfg(all())))] -pub fn sendfile64( - out_fd: RawFd, - in_fd: RawFd, +pub fn sendfile64( + out_fd: F1, + in_fd: F2, offset: Option<&mut libc::off64_t>, count: usize, ) -> Result { let offset = offset .map(|offset| offset as *mut _) .unwrap_or(ptr::null_mut()); - let ret = unsafe { libc::sendfile64(out_fd, in_fd, offset, count) }; + let ret = unsafe { + libc::sendfile64( + out_fd.as_fd().as_raw_fd(), + in_fd.as_fd().as_raw_fd(), + offset, + count, + ) + }; Errno::result(ret).map(|r| r as usize) } @@ -156,9 +170,9 @@ cfg_if! { /// For more information, see /// [the sendfile(2) man page.](https://www.freebsd.org/cgi/man.cgi?query=sendfile&sektion=2) #[allow(clippy::too_many_arguments)] - pub fn sendfile( - in_fd: RawFd, - out_sock: RawFd, + pub fn sendfile( + in_fd: F1, + out_sock: F2, offset: off_t, count: Option, headers: Option<&[&[u8]]>, @@ -175,8 +189,8 @@ cfg_if! { let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers)); let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.0 as *const libc::sf_hdtr); let return_code = unsafe { - libc::sendfile(in_fd, - out_sock, + libc::sendfile(in_fd.as_fd().as_raw_fd(), + out_sock.as_fd().as_raw_fd(), offset, count.unwrap_or(0), hdtr_ptr as *mut libc::sf_hdtr, @@ -206,9 +220,9 @@ cfg_if! { /// /// For more information, see /// [the sendfile(2) man page.](https://leaf.dragonflybsd.org/cgi/web-man?command=sendfile§ion=2) - pub fn sendfile( - in_fd: RawFd, - out_sock: RawFd, + pub fn sendfile( + in_fd: F1, + out_sock: F2, offset: off_t, count: Option, headers: Option<&[&[u8]]>, @@ -218,8 +232,8 @@ cfg_if! { let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers)); let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.0 as *const libc::sf_hdtr); let return_code = unsafe { - libc::sendfile(in_fd, - out_sock, + libc::sendfile(in_fd.as_fd().as_raw_fd(), + out_sock.as_fd().as_raw_fd(), offset, count.unwrap_or(0), hdtr_ptr as *mut libc::sf_hdtr, @@ -252,9 +266,9 @@ cfg_if! { /// /// For more information, see /// [the sendfile(2) man page.](https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man2/sendfile.2.html) - pub fn sendfile( - in_fd: RawFd, - out_sock: RawFd, + pub fn sendfile( + in_fd: F1, + out_sock: F2, offset: off_t, count: Option, headers: Option<&[&[u8]]>, @@ -264,8 +278,8 @@ cfg_if! { let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers)); let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.0 as *const libc::sf_hdtr); let return_code = unsafe { - libc::sendfile(in_fd, - out_sock, + libc::sendfile(in_fd.as_fd().as_raw_fd(), + out_sock.as_fd().as_raw_fd(), offset, &mut len as *mut off_t, hdtr_ptr as *mut libc::sf_hdtr, diff --git a/src/sys/signal.rs b/src/sys/signal.rs index d3746e6..c946e4a 100644 --- a/src/sys/signal.rs +++ b/src/sys/signal.rs @@ -13,7 +13,11 @@ use std::os::unix::io::RawFd; use std::ptr; use std::str::FromStr; -#[cfg(not(any(target_os = "openbsd", target_os = "redox")))] +#[cfg(not(any( + target_os = "fuchsia", + target_os = "openbsd", + target_os = "redox" +)))] #[cfg(any(feature = "aio", feature = "signal"))] pub use self::sigevent::*; @@ -93,7 +97,8 @@ libc_enum! { #[cfg_attr(docsrs, doc(cfg(all())))] SIGIO, #[cfg(any(target_os = "android", target_os = "emscripten", - target_os = "fuchsia", target_os = "linux"))] + target_os = "fuchsia", target_os = "linux", + target_os = "aix"))] #[cfg_attr(docsrs, doc(cfg(all())))] /// Power failure imminent. SIGPWR, @@ -107,7 +112,8 @@ libc_enum! { SIGEMT, #[cfg(not(any(target_os = "android", target_os = "emscripten", target_os = "fuchsia", target_os = "linux", - target_os = "redox", target_os = "haiku")))] + target_os = "redox", target_os = "haiku", + target_os = "aix")))] #[cfg_attr(docsrs, doc(cfg(all())))] /// Information request SIGINFO, @@ -186,6 +192,7 @@ impl FromStr for Signal { target_os = "fuchsia", target_os = "linux", target_os = "redox", + target_os = "aix", target_os = "haiku" )))] "SIGINFO" => Signal::SIGINFO, @@ -250,6 +257,7 @@ impl Signal { target_os = "android", target_os = "emscripten", target_os = "fuchsia", + target_os = "aix", target_os = "linux" ))] Signal::SIGPWR => "SIGPWR", @@ -269,6 +277,7 @@ impl Signal { target_os = "fuchsia", target_os = "linux", target_os = "redox", + target_os = "aix", target_os = "haiku" )))] Signal::SIGINFO => "SIGINFO", @@ -345,11 +354,20 @@ const SIGNALS: [Signal; 30] = [ SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGXCPU, SIGXFSZ, SIGVTALRM, SIGPROF, SIGWINCH, SIGIO, SIGPWR, SIGSYS, ]; +#[cfg(target_os = "aix")] +#[cfg(feature = "signal")] +const SIGNALS: [Signal; 30] = [ + SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGEMT, SIGFPE, SIGKILL, SIGSEGV, + SIGSYS, SIGPIPE, SIGALRM, SIGTERM, SIGUSR1, SIGUSR2, SIGPWR, SIGWINCH, + SIGURG, SIGPOLL, SIGIO, SIGSTOP, SIGTSTP, SIGCONT, SIGTTIN, SIGTTOU, + SIGVTALRM, SIGPROF, SIGXCPU, SIGXFSZ, SIGTRAP, +]; #[cfg(not(any( target_os = "linux", target_os = "android", target_os = "fuchsia", target_os = "emscripten", + target_os = "aix", target_os = "redox", target_os = "haiku" )))] @@ -641,7 +659,6 @@ impl<'a> IntoIterator for &'a SigSet { } /// A signal handler. -#[allow(unknown_lints)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum SigHandler { /// Default signal handling. @@ -670,6 +687,7 @@ impl SigAction { /// is the `SigAction` variant). `mask` specifies other signals to block during execution of /// the signal-catching function. pub fn new(handler: SigHandler, flags: SaFlags, mask: SigSet) -> SigAction { + #[cfg(not(target_os = "aix"))] unsafe fn install_sig(p: *mut libc::sigaction, handler: SigHandler) { (*p).sa_sigaction = match handler { SigHandler::SigDfl => libc::SIG_DFL, @@ -680,6 +698,16 @@ impl SigAction { }; } + #[cfg(target_os = "aix")] + unsafe fn install_sig(p: *mut libc::sigaction, handler: SigHandler) { + (*p).sa_union.__su_sigaction = match handler { + SigHandler::SigDfl => mem::transmute::(libc::SIG_DFL), + SigHandler::SigIgn => mem::transmute::(libc::SIG_IGN), + SigHandler::Handler(f) => mem::transmute::(f), + SigHandler::SigAction(f) => f, + }; + } + let mut s = mem::MaybeUninit::::uninit(); unsafe { let p = s.as_mut_ptr(); @@ -707,6 +735,7 @@ impl SigAction { } /// Returns the action's handler. + #[cfg(not(target_os = "aix"))] pub fn handler(&self) -> SigHandler { match self.sigaction.sa_sigaction { libc::SIG_DFL => SigHandler::SigDfl, @@ -739,6 +768,26 @@ impl SigAction { as extern fn(libc::c_int)), } } + + /// Returns the action's handler. + #[cfg(target_os = "aix")] + pub fn handler(&self) -> SigHandler { + unsafe { + match self.sigaction.sa_union.__su_sigaction as usize { + libc::SIG_DFL => SigHandler::SigDfl, + libc::SIG_IGN => SigHandler::SigIgn, + p if self.flags().contains(SaFlags::SA_SIGINFO) => + SigHandler::SigAction( + *(&p as *const usize + as *const extern fn(_, _, _)) + as extern fn(_, _, _)), + p => SigHandler::Handler( + *(&p as *const usize + as *const extern fn(libc::c_int)) + as extern fn(libc::c_int)), + } + } + } } /// Changes the action taken by a process on receipt of a specific signal. @@ -792,13 +841,10 @@ pub unsafe fn sigaction(signal: Signal, sigaction: &SigAction) -> Result fmt::Result { + let mut ds = f.debug_struct("sigevent"); + ds.field("sigev_notify", &self.sigev_notify) + .field("sigev_signo", &self.sigev_signo) + .field("sigev_value", &self.sigev_value); + // Safe because we check the sigev_notify discriminant + unsafe { + match self.sigev_notify { + libc::SIGEV_KEVENT => { + ds.field("sigev_notify_kevent_flags", &self._sigev_un._kevent_flags); + } + libc::SIGEV_THREAD_ID => { + ds.field("sigev_notify_thread_id", &self._sigev_un._threadid); + } + libc::SIGEV_THREAD => { + ds.field("sigev_notify_function", &self._sigev_un._sigev_thread._function); + ds.field("sigev_notify_attributes", &self._sigev_un._sigev_thread._attribute); + } + _ => () + }; + } + ds.finish() + } + } + + impl PartialEq for sigevent { + fn eq(&self, other: &Self) -> bool { + let mut equals = self.sigev_notify == other.sigev_notify; + equals &= self.sigev_signo == other.sigev_signo; + equals &= self.sigev_value == other.sigev_value; + // Safe because we check the sigev_notify discriminant + unsafe { + match self.sigev_notify { + libc::SIGEV_KEVENT => { + equals &= self._sigev_un._kevent_flags == other._sigev_un._kevent_flags; + } + libc::SIGEV_THREAD_ID => { + equals &= self._sigev_un._threadid == other._sigev_un._threadid; + } + libc::SIGEV_THREAD => { + equals &= self._sigev_un._sigev_thread == other._sigev_un._sigev_thread; + } + _ => /* The union field is don't care */ () + } + } + equals + } + } + + impl Eq for sigevent {} + + impl hash::Hash for sigevent { + fn hash(&self, s: &mut H) { + self.sigev_notify.hash(s); + self.sigev_signo.hash(s); + self.sigev_value.hash(s); + // Safe because we check the sigev_notify discriminant + unsafe { + match self.sigev_notify { + libc::SIGEV_KEVENT => { + self._sigev_un._kevent_flags.hash(s); + } + libc::SIGEV_THREAD_ID => { + self._sigev_un._threadid.hash(s); + } + libc::SIGEV_THREAD => { + self._sigev_un._sigev_thread.hash(s); + } + _ => /* The union field is don't care */ () + } + } + } + } + } /// Used to request asynchronous notification of the completion of certain /// events, such as POSIX AIO and timers. #[repr(C)] - #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] + #[derive(Clone, Debug, Eq, Hash, PartialEq)] + // It can't be Copy on all platforms. + #[allow(missing_copy_implementations)] pub struct SigEvent { - sigevent: libc::sigevent + sigevent: libc_sigevent } impl SigEvent { @@ -1053,69 +1240,91 @@ mod sigevent { /// Linux, Solaris, and portable programs should prefer `SIGEV_THREAD_ID` or /// `SIGEV_SIGNAL`. That field is part of a union that shares space with the /// more genuinely useful `sigev_notify_thread_id` - // Allow invalid_value warning on Fuchsia only. - // See https://github.com/nix-rust/nix/issues/1441 - #[cfg_attr(target_os = "fuchsia", allow(invalid_value))] pub fn new(sigev_notify: SigevNotify) -> SigEvent { - let mut sev = unsafe { mem::MaybeUninit::::zeroed().assume_init() }; - sev.sigev_notify = match sigev_notify { - SigevNotify::SigevNone => libc::SIGEV_NONE, - SigevNotify::SigevSignal{..} => libc::SIGEV_SIGNAL, + let mut sev: libc_sigevent = unsafe { mem::zeroed() }; + match sigev_notify { + SigevNotify::SigevNone => { + sev.sigev_notify = libc::SIGEV_NONE; + }, + SigevNotify::SigevSignal{signal, si_value} => { + sev.sigev_notify = libc::SIGEV_SIGNAL; + sev.sigev_signo = signal as libc::c_int; + sev.sigev_value.sival_ptr = si_value as *mut libc::c_void + }, #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] - SigevNotify::SigevKevent{..} => libc::SIGEV_KEVENT, + SigevNotify::SigevKevent{kq, udata} => { + sev.sigev_notify = libc::SIGEV_KEVENT; + sev.sigev_signo = kq; + sev.sigev_value.sival_ptr = udata as *mut libc::c_void; + }, #[cfg(target_os = "freebsd")] - SigevNotify::SigevThreadId{..} => libc::SIGEV_THREAD_ID, - #[cfg(all(target_os = "linux", target_env = "gnu", not(target_arch = "mips")))] - SigevNotify::SigevThreadId{..} => libc::SIGEV_THREAD_ID, - #[cfg(all(target_os = "linux", target_env = "uclibc"))] - SigevNotify::SigevThreadId{..} => libc::SIGEV_THREAD_ID, - #[cfg(any(all(target_os = "linux", target_env = "musl"), target_arch = "mips"))] - SigevNotify::SigevThreadId{..} => 4 // No SIGEV_THREAD_ID defined - }; - sev.sigev_signo = match sigev_notify { - SigevNotify::SigevSignal{ signal, .. } => signal as libc::c_int, - #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] - SigevNotify::SigevKevent{ kq, ..} => kq, - #[cfg(any(target_os = "linux", target_os = "freebsd"))] - SigevNotify::SigevThreadId{ signal, .. } => signal as libc::c_int, - _ => 0 - }; - sev.sigev_value.sival_ptr = match sigev_notify { - SigevNotify::SigevNone => ptr::null_mut::(), - SigevNotify::SigevSignal{ si_value, .. } => si_value as *mut libc::c_void, - #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] - SigevNotify::SigevKevent{ udata, .. } => udata as *mut libc::c_void, - #[cfg(any(target_os = "freebsd", target_os = "linux"))] - SigevNotify::SigevThreadId{ si_value, .. } => si_value as *mut libc::c_void, - }; - SigEvent::set_tid(&mut sev, &sigev_notify); + #[cfg(feature = "event")] + SigevNotify::SigevKeventFlags{kq, udata, flags} => { + sev.sigev_notify = libc::SIGEV_KEVENT; + sev.sigev_signo = kq; + sev.sigev_value.sival_ptr = udata as *mut libc::c_void; + sev._sigev_un._kevent_flags = flags.bits(); + }, + #[cfg(target_os = "freebsd")] + SigevNotify::SigevThreadId{signal, thread_id, si_value} => { + sev.sigev_notify = libc::SIGEV_THREAD_ID; + sev.sigev_signo = signal as libc::c_int; + sev.sigev_value.sival_ptr = si_value as *mut libc::c_void; + sev._sigev_un._threadid = thread_id; + } + #[cfg(any(target_env = "gnu", target_env = "uclibc"))] + SigevNotify::SigevThreadId{signal, thread_id, si_value} => { + sev.sigev_notify = libc::SIGEV_THREAD_ID; + sev.sigev_signo = signal as libc::c_int; + sev.sigev_value.sival_ptr = si_value as *mut libc::c_void; + sev.sigev_notify_thread_id = thread_id; + } + } SigEvent{sigevent: sev} } - #[cfg(any(target_os = "freebsd", target_os = "linux"))] - fn set_tid(sev: &mut libc::sigevent, sigev_notify: &SigevNotify) { - sev.sigev_notify_thread_id = match *sigev_notify { - SigevNotify::SigevThreadId { thread_id, .. } => thread_id, - _ => 0 as type_of_thread_id - }; - } - - #[cfg(not(any(target_os = "freebsd", target_os = "linux")))] - fn set_tid(_sev: &mut libc::sigevent, _sigev_notify: &SigevNotify) { + /// Return a copy of the inner structure + #[cfg(target_os = "freebsd")] + pub fn sigevent(&self) -> libc::sigevent { + // Safe because they're really the same structure. See + // https://github.com/rust-lang/libc/pull/2813 + unsafe { + mem::transmute::(self.sigevent) + } } /// Return a copy of the inner structure + #[cfg(not(target_os = "freebsd"))] pub fn sigevent(&self) -> libc::sigevent { self.sigevent } /// Returns a mutable pointer to the `sigevent` wrapped by `self` + #[cfg(target_os = "freebsd")] + pub fn as_mut_ptr(&mut self) -> *mut libc::sigevent { + // Safe because they're really the same structure. See + // https://github.com/rust-lang/libc/pull/2813 + &mut self.sigevent as *mut libc_sigevent as *mut libc::sigevent + } + + /// Returns a mutable pointer to the `sigevent` wrapped by `self` + #[cfg(not(target_os = "freebsd"))] pub fn as_mut_ptr(&mut self) -> *mut libc::sigevent { &mut self.sigevent } } impl<'a> From<&'a libc::sigevent> for SigEvent { + #[cfg(target_os = "freebsd")] + fn from(sigevent: &libc::sigevent) -> Self { + // Safe because they're really the same structure. See + // https://github.com/rust-lang/libc/pull/2813 + let sigevent = unsafe { + mem::transmute::(*sigevent) + }; + SigEvent{ sigevent } + } + #[cfg(not(target_os = "freebsd"))] fn from(sigevent: &libc::sigevent) -> Self { SigEvent{ sigevent: *sigevent } } diff --git a/src/sys/signalfd.rs b/src/sys/signalfd.rs index 095e590..2b80ea6 100644 --- a/src/sys/signalfd.rs +++ b/src/sys/signalfd.rs @@ -17,12 +17,11 @@ //! signal handlers. use crate::errno::Errno; pub use crate::sys::signal::{self, SigSet}; -use crate::unistd; use crate::Result; pub use libc::signalfd_siginfo as siginfo; use std::mem; -use std::os::unix::io::{AsRawFd, RawFd}; +use std::os::unix::io::{AsRawFd, RawFd, FromRawFd, OwnedFd, AsFd, BorrowedFd}; libc_bitflags! { pub struct SfdFlags: libc::c_int { @@ -31,7 +30,6 @@ libc_bitflags! { } } -pub const SIGNALFD_NEW: RawFd = -1; #[deprecated(since = "0.23.0", note = "use mem::size_of::() instead")] pub const SIGNALFD_SIGINFO_SIZE: usize = mem::size_of::(); @@ -46,13 +44,19 @@ pub const SIGNALFD_SIGINFO_SIZE: usize = mem::size_of::(); /// signalfd (the default handler will be invoked instead). /// /// See [the signalfd man page for more information](https://man7.org/linux/man-pages/man2/signalfd.2.html) -pub fn signalfd(fd: RawFd, mask: &SigSet, flags: SfdFlags) -> Result { +#[deprecated(since = "0.27.0", note = "Use SignalFd instead")] +pub fn signalfd(fd: Option, mask: &SigSet, flags: SfdFlags) -> Result { + _signalfd(fd, mask, flags) +} + +fn _signalfd(fd: Option, mask: &SigSet, flags: SfdFlags) -> Result { + let raw_fd = fd.map_or(-1, |x|x.as_fd().as_raw_fd()); unsafe { Errno::result(libc::signalfd( - fd as libc::c_int, + raw_fd, mask.as_ref(), flags.bits(), - )) + )).map(|raw_fd|FromRawFd::from_raw_fd(raw_fd)) } } @@ -82,8 +86,8 @@ pub fn signalfd(fd: RawFd, mask: &SigSet, flags: SfdFlags) -> Result { /// Err(err) => (), // some error happend /// } /// ``` -#[derive(Debug, Eq, Hash, PartialEq)] -pub struct SignalFd(RawFd); +#[derive(Debug)] +pub struct SignalFd(OwnedFd); impl SignalFd { pub fn new(mask: &SigSet) -> Result { @@ -91,13 +95,13 @@ impl SignalFd { } pub fn with_flags(mask: &SigSet, flags: SfdFlags) -> Result { - let fd = signalfd(SIGNALFD_NEW, mask, flags)?; + let fd = _signalfd(None::, mask, flags)?; Ok(SignalFd(fd)) } pub fn set_mask(&mut self, mask: &SigSet) -> Result<()> { - signalfd(self.0, mask, SfdFlags::empty()).map(drop) + _signalfd(Some(self.0.as_fd()), mask, SfdFlags::empty()).map(drop) } pub fn read_signal(&mut self) -> Result> { @@ -105,7 +109,7 @@ impl SignalFd { let size = mem::size_of_val(&buffer); let res = Errno::result(unsafe { - libc::read(self.0, buffer.as_mut_ptr() as *mut libc::c_void, size) + libc::read(self.0.as_raw_fd(), buffer.as_mut_ptr() as *mut libc::c_void, size) }) .map(|r| r as usize); match res { @@ -117,18 +121,14 @@ impl SignalFd { } } -impl Drop for SignalFd { - fn drop(&mut self) { - let e = unistd::close(self.0); - if !std::thread::panicking() && e == Err(Errno::EBADF) { - panic!("Closing an invalid file descriptor!"); - }; +impl AsFd for SignalFd { + fn as_fd(&self) -> BorrowedFd { + self.0.as_fd() } } - impl AsRawFd for SignalFd { fn as_raw_fd(&self) -> RawFd { - self.0 + self.0.as_raw_fd() } } diff --git a/src/sys/socket/addr.rs b/src/sys/socket/addr.rs index 4e565a5..1783531 100644 --- a/src/sys/socket/addr.rs +++ b/src/sys/socket/addr.rs @@ -9,11 +9,12 @@ target_os = "netbsd", target_os = "openbsd", target_os = "haiku", - target_os = "fuchsia" + target_os = "fuchsia", + target_os = "aix", ))] #[cfg(feature = "net")] pub use self::datalink::LinkAddr; -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))] pub use self::vsock::VsockAddr; use super::sa_family_t; use crate::errno::Errno; @@ -33,29 +34,22 @@ use std::convert::TryInto; use std::ffi::OsStr; use std::hash::{Hash, Hasher}; use std::os::unix::ffi::OsStrExt; -#[cfg(any(target_os = "ios", target_os = "macos"))] -use std::os::unix::io::RawFd; use std::path::Path; use std::{fmt, mem, net, ptr, slice}; /// Convert a std::net::Ipv4Addr into the libc form. #[cfg(feature = "net")] pub(crate) const fn ipv4addr_to_libc(addr: net::Ipv4Addr) -> libc::in_addr { - static_assertions::assert_eq_size!(net::Ipv4Addr, libc::in_addr); - // Safe because both types have the same memory layout, and no fancy Drop - // impls. - unsafe { - mem::transmute(addr) + libc::in_addr { + s_addr: u32::from_ne_bytes(addr.octets()) } } /// Convert a std::net::Ipv6Addr into the libc form. #[cfg(feature = "net")] pub(crate) const fn ipv6addr_to_libc(addr: &net::Ipv6Addr) -> libc::in6_addr { - static_assertions::assert_eq_size!(net::Ipv6Addr, libc::in6_addr); - // Safe because both are Newtype wrappers around the same libc type - unsafe { - mem::transmute(*addr) + libc::in6_addr { + s6_addr: addr.octets() } } @@ -80,6 +74,13 @@ pub enum AddressFamily { #[cfg(any(target_os = "android", target_os = "linux"))] #[cfg_attr(docsrs, doc(cfg(all())))] Netlink = libc::AF_NETLINK, + /// Kernel interface for interacting with the routing table + #[cfg(not(any( + target_os = "redox", + target_os = "linux", + target_os = "android" + )))] + Route = libc::PF_ROUTE, /// Low level packet interface (see [`packet(7)`](https://man7.org/linux/man-pages/man7/packet.7.html)) #[cfg(any( target_os = "android", @@ -99,8 +100,11 @@ pub enum AddressFamily { #[cfg_attr(docsrs, doc(cfg(all())))] Ax25 = libc::AF_AX25, /// IPX - Novell protocols + #[cfg(not(any(target_os = "aix", target_os = "redox")))] + #[cfg_attr(docsrs, doc(cfg(all())))] Ipx = libc::AF_IPX, /// AppleTalk + #[cfg(not(target_os = "redox"))] AppleTalk = libc::AF_APPLETALK, /// AX.25 packet layer protocol. /// (see [netrom(4)](https://www.unix.com/man-page/linux/4/netrom/)) @@ -129,7 +133,7 @@ pub enum AddressFamily { #[cfg_attr(docsrs, doc(cfg(all())))] Rose = libc::AF_ROSE, /// DECet protocol sockets. - #[cfg(not(target_os = "haiku"))] + #[cfg(not(any(target_os = "haiku", target_os = "redox")))] Decnet = libc::AF_DECnet, /// Reserved for "802.2LLC project"; never used. #[cfg(any(target_os = "android", target_os = "linux"))] @@ -161,7 +165,7 @@ pub enum AddressFamily { #[cfg_attr(docsrs, doc(cfg(all())))] Rds = libc::AF_RDS, /// IBM SNA - #[cfg(not(target_os = "haiku"))] + #[cfg(not(any(target_os = "haiku", target_os = "redox")))] Sna = libc::AF_SNA, /// Socket interface over IrDA #[cfg(any(target_os = "android", target_os = "linux"))] @@ -198,10 +202,12 @@ pub enum AddressFamily { Tipc = libc::AF_TIPC, /// Bluetooth low-level socket protocol #[cfg(not(any( + target_os = "aix", target_os = "illumos", target_os = "ios", target_os = "macos", - target_os = "solaris" + target_os = "solaris", + target_os = "redox", )))] #[cfg_attr(docsrs, doc(cfg(all())))] Bluetooth = libc::AF_BLUETOOTH, @@ -216,9 +222,11 @@ pub enum AddressFamily { RxRpc = libc::AF_RXRPC, /// New "modular ISDN" driver interface protocol #[cfg(not(any( + target_os = "aix", target_os = "illumos", target_os = "solaris", - target_os = "haiku" + target_os = "haiku", + target_os = "redox", )))] #[cfg_attr(docsrs, doc(cfg(all())))] Isdn = libc::AF_ISDN, @@ -244,7 +252,7 @@ pub enum AddressFamily { #[cfg_attr(docsrs, doc(cfg(all())))] Nfc = libc::AF_NFC, /// VMWare VSockets protocol for hypervisor-guest interaction. - #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))] #[cfg_attr(docsrs, doc(cfg(all())))] Vsock = libc::AF_VSOCK, /// ARPANet IMP addresses @@ -421,6 +429,12 @@ impl AddressFamily { libc::AF_NETLINK => Some(AddressFamily::Netlink), #[cfg(any(target_os = "macos", target_os = "macos"))] libc::AF_SYSTEM => Some(AddressFamily::System), + #[cfg(not(any( + target_os = "redox", + target_os = "linux", + target_os = "android" + )))] + libc::PF_ROUTE => Some(AddressFamily::Route), #[cfg(any(target_os = "android", target_os = "linux"))] libc::AF_PACKET => Some(AddressFamily::Packet), #[cfg(any( @@ -433,315 +447,13 @@ impl AddressFamily { target_os = "openbsd" ))] libc::AF_LINK => Some(AddressFamily::Link), - #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))] libc::AF_VSOCK => Some(AddressFamily::Vsock), _ => None, } } } -feature! { -#![feature = "net"] - -#[deprecated( - since = "0.24.0", - note = "use SockaddrIn, SockaddrIn6, or SockaddrStorage instead" -)] -#[allow(missing_docs)] // Since they're all deprecated anyway -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum InetAddr { - V4(libc::sockaddr_in), - V6(libc::sockaddr_in6), -} - -#[allow(missing_docs)] // It's deprecated anyway -#[allow(deprecated)] -impl InetAddr { - #[allow(clippy::needless_update)] // It isn't needless on all OSes - pub fn from_std(std: &net::SocketAddr) -> InetAddr { - match *std { - net::SocketAddr::V4(ref addr) => { - InetAddr::V4(libc::sockaddr_in { - #[cfg(any(target_os = "dragonfly", target_os = "freebsd", - target_os = "haiku", target_os = "hermit", - target_os = "ios", target_os = "macos", - target_os = "netbsd", target_os = "openbsd"))] - sin_len: mem::size_of::() as u8, - sin_family: AddressFamily::Inet as sa_family_t, - sin_port: addr.port().to_be(), // network byte order - sin_addr: Ipv4Addr::from_std(addr.ip()).0, - .. unsafe { mem::zeroed() } - }) - } - net::SocketAddr::V6(ref addr) => { - InetAddr::V6(libc::sockaddr_in6 { - #[cfg(any(target_os = "dragonfly", target_os = "freebsd", - target_os = "haiku", target_os = "hermit", - target_os = "ios", target_os = "macos", - target_os = "netbsd", target_os = "openbsd"))] - sin6_len: mem::size_of::() as u8, - sin6_family: AddressFamily::Inet6 as sa_family_t, - sin6_port: addr.port().to_be(), // network byte order - sin6_addr: Ipv6Addr::from_std(addr.ip()).0, - sin6_flowinfo: addr.flowinfo(), // host byte order - sin6_scope_id: addr.scope_id(), // host byte order - .. unsafe { mem::zeroed() } - }) - } - } - } - - #[allow(clippy::needless_update)] // It isn't needless on all OSes - pub fn new(ip: IpAddr, port: u16) -> InetAddr { - match ip { - IpAddr::V4(ref ip) => { - InetAddr::V4(libc::sockaddr_in { - sin_family: AddressFamily::Inet as sa_family_t, - sin_port: port.to_be(), - sin_addr: ip.0, - .. unsafe { mem::zeroed() } - }) - } - IpAddr::V6(ref ip) => { - InetAddr::V6(libc::sockaddr_in6 { - sin6_family: AddressFamily::Inet6 as sa_family_t, - sin6_port: port.to_be(), - sin6_addr: ip.0, - .. unsafe { mem::zeroed() } - }) - } - } - } - /// Gets the IP address associated with this socket address. - pub const fn ip(&self) -> IpAddr { - match *self { - InetAddr::V4(ref sa) => IpAddr::V4(Ipv4Addr(sa.sin_addr)), - InetAddr::V6(ref sa) => IpAddr::V6(Ipv6Addr(sa.sin6_addr)), - } - } - - /// Gets the port number associated with this socket address - pub const fn port(&self) -> u16 { - match *self { - InetAddr::V6(ref sa) => u16::from_be(sa.sin6_port), - InetAddr::V4(ref sa) => u16::from_be(sa.sin_port), - } - } - - pub fn to_std(&self) -> net::SocketAddr { - match *self { - InetAddr::V4(ref sa) => net::SocketAddr::V4( - net::SocketAddrV4::new( - Ipv4Addr(sa.sin_addr).to_std(), - self.port())), - InetAddr::V6(ref sa) => net::SocketAddr::V6( - net::SocketAddrV6::new( - Ipv6Addr(sa.sin6_addr).to_std(), - self.port(), - sa.sin6_flowinfo, - sa.sin6_scope_id)), - } - } - - #[deprecated(since = "0.23.0", note = "use .to_string() instead")] - pub fn to_str(&self) -> String { - format!("{}", self) - } -} - -#[allow(deprecated)] -impl fmt::Display for InetAddr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - InetAddr::V4(_) => write!(f, "{}:{}", self.ip(), self.port()), - InetAddr::V6(_) => write!(f, "[{}]:{}", self.ip(), self.port()), - } - } -} - -/* - * - * ===== IpAddr ===== - * - */ -#[allow(missing_docs)] // Since they're all deprecated anyway -#[allow(deprecated)] -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -#[deprecated( - since = "0.24.0", - note = "Use std::net::IpAddr instead" -)] -pub enum IpAddr { - V4(Ipv4Addr), - V6(Ipv6Addr), -} - -#[allow(deprecated)] -#[allow(missing_docs)] // Since they're all deprecated anyway -impl IpAddr { - /// Create a new IpAddr that contains an IPv4 address. - /// - /// The result will represent the IP address a.b.c.d - pub const fn new_v4(a: u8, b: u8, c: u8, d: u8) -> IpAddr { - IpAddr::V4(Ipv4Addr::new(a, b, c, d)) - } - - /// Create a new IpAddr that contains an IPv6 address. - /// - /// The result will represent the IP address a:b:c:d:e:f - #[allow(clippy::many_single_char_names)] - #[allow(clippy::too_many_arguments)] - pub const fn new_v6(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> IpAddr { - IpAddr::V6(Ipv6Addr::new(a, b, c, d, e, f, g, h)) - } - - pub fn from_std(std: &net::IpAddr) -> IpAddr { - match *std { - net::IpAddr::V4(ref std) => IpAddr::V4(Ipv4Addr::from_std(std)), - net::IpAddr::V6(ref std) => IpAddr::V6(Ipv6Addr::from_std(std)), - } - } - - pub const fn to_std(&self) -> net::IpAddr { - match *self { - IpAddr::V4(ref ip) => net::IpAddr::V4(ip.to_std()), - IpAddr::V6(ref ip) => net::IpAddr::V6(ip.to_std()), - } - } -} - -#[allow(deprecated)] -impl fmt::Display for IpAddr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - IpAddr::V4(ref v4) => v4.fmt(f), - IpAddr::V6(ref v6) => v6.fmt(f) - } - } -} - -/* - * - * ===== Ipv4Addr ===== - * - */ - -#[deprecated( - since = "0.24.0", - note = "Use std::net::Ipv4Addr instead" -)] -#[allow(missing_docs)] // Since they're all deprecated anyway -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -#[repr(transparent)] -pub struct Ipv4Addr(pub libc::in_addr); - -#[allow(deprecated)] -#[allow(missing_docs)] // Since they're all deprecated anyway -impl Ipv4Addr { - #[allow(clippy::identity_op)] // More readable this way - pub const fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr { - let ip = (((a as u32) << 24) | - ((b as u32) << 16) | - ((c as u32) << 8) | - ((d as u32) << 0)).to_be(); - - Ipv4Addr(libc::in_addr { s_addr: ip }) - } - - // Use pass by reference for symmetry with Ipv6Addr::from_std - #[allow(clippy::trivially_copy_pass_by_ref)] - pub fn from_std(std: &net::Ipv4Addr) -> Ipv4Addr { - let bits = std.octets(); - Ipv4Addr::new(bits[0], bits[1], bits[2], bits[3]) - } - - pub const fn any() -> Ipv4Addr { - Ipv4Addr(libc::in_addr { s_addr: libc::INADDR_ANY }) - } - - pub const fn octets(self) -> [u8; 4] { - let bits = u32::from_be(self.0.s_addr); - [(bits >> 24) as u8, (bits >> 16) as u8, (bits >> 8) as u8, bits as u8] - } - - pub const fn to_std(self) -> net::Ipv4Addr { - let bits = self.octets(); - net::Ipv4Addr::new(bits[0], bits[1], bits[2], bits[3]) - } -} - -#[allow(deprecated)] -impl fmt::Display for Ipv4Addr { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - let octets = self.octets(); - write!(fmt, "{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3]) - } -} - -/* - * - * ===== Ipv6Addr ===== - * - */ - -#[deprecated( - since = "0.24.0", - note = "Use std::net::Ipv6Addr instead" -)] -#[allow(missing_docs)] // Since they're all deprecated anyway -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -#[repr(transparent)] -pub struct Ipv6Addr(pub libc::in6_addr); - -// Note that IPv6 addresses are stored in big endian order on all architectures. -// See https://tools.ietf.org/html/rfc1700 or consult your favorite search -// engine. - -macro_rules! to_u8_array { - ($($num:ident),*) => { - [ $(($num>>8) as u8, ($num&0xff) as u8,)* ] - } -} - -macro_rules! to_u16_array { - ($slf:ident, $($first:expr, $second:expr),*) => { - [$( (($slf.0.s6_addr[$first] as u16) << 8) + $slf.0.s6_addr[$second] as u16,)*] - } -} - -#[allow(deprecated)] -#[allow(missing_docs)] // Since they're all deprecated anyway -impl Ipv6Addr { - #[allow(clippy::many_single_char_names)] - #[allow(clippy::too_many_arguments)] - pub const fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> Ipv6Addr { - Ipv6Addr(libc::in6_addr{s6_addr: to_u8_array!(a,b,c,d,e,f,g,h)}) - } - - pub fn from_std(std: &net::Ipv6Addr) -> Ipv6Addr { - let s = std.segments(); - Ipv6Addr::new(s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7]) - } - - /// Return the eight 16-bit segments that make up this address - pub const fn segments(&self) -> [u16; 8] { - to_u16_array!(self, 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15) - } - - pub const fn to_std(&self) -> net::Ipv6Addr { - let s = self.segments(); - net::Ipv6Addr::new(s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7]) - } -} - -#[allow(deprecated)] -impl fmt::Display for Ipv6Addr { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - self.to_std().fmt(fmt) - } -} -} - /// A wrapper around `sockaddr_un`. #[derive(Clone, Copy, Debug)] #[repr(C)] @@ -755,7 +467,8 @@ pub struct UnixAddr { target_os = "android", target_os = "fuchsia", target_os = "illumos", - target_os = "linux" + target_os = "linux", + target_os = "redox", ))] sun_len: u8, } @@ -775,6 +488,7 @@ enum UnixAddrKind<'a> { } impl<'a> UnixAddrKind<'a> { /// Safety: sun & sun_len must be valid + #[allow(clippy::unnecessary_cast)] // Not unnecessary on all platforms unsafe fn get(sun: &'a libc::sockaddr_un, sun_len: u8) -> Self { assert!(sun_len as usize >= offset_of!(libc::sockaddr_un, sun_path)); let path_len = @@ -811,6 +525,7 @@ impl<'a> UnixAddrKind<'a> { impl UnixAddr { /// Create a new sockaddr_un representing a filesystem path. + #[allow(clippy::unnecessary_cast)] // Not unnecessary on all platforms pub fn new(path: &P) -> Result { path.with_nix_path(|cstr| unsafe { let mut ret = libc::sockaddr_un { @@ -858,6 +573,7 @@ impl UnixAddr { /// processes to communicate with processes having a different filesystem view. #[cfg(any(target_os = "android", target_os = "linux"))] #[cfg_attr(docsrs, doc(cfg(all())))] + #[allow(clippy::unnecessary_cast)] // Not unnecessary on all platforms pub fn new_abstract(path: &[u8]) -> Result { unsafe { let mut ret = libc::sockaddr_un { @@ -891,10 +607,11 @@ impl UnixAddr { pub fn new_unnamed() -> UnixAddr { let ret = libc::sockaddr_un { sun_family: AddressFamily::Unix as sa_family_t, - .. unsafe { mem::zeroed() } + ..unsafe { mem::zeroed() } }; - let sun_len: u8 = offset_of!(libc::sockaddr_un, sun_path).try_into().unwrap(); + let sun_len: u8 = + offset_of!(libc::sockaddr_un, sun_path).try_into().unwrap(); unsafe { UnixAddr::from_raw_parts(ret, sun_len) } } @@ -918,7 +635,8 @@ impl UnixAddr { if #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "illumos", - target_os = "linux" + target_os = "linux", + target_os = "redox", ))] { UnixAddr { sun, sun_len } @@ -984,7 +702,8 @@ impl UnixAddr { if #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "illumos", - target_os = "linux" + target_os = "linux", + target_os = "redox", ))] { self.sun_len @@ -1030,7 +749,8 @@ impl SockaddrLike for UnixAddr { if #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "illumos", - target_os = "linux" + target_os = "linux", + target_os = "redox", ))] { let su_len = len.unwrap_or( mem::size_of::() as libc::socklen_t @@ -1049,6 +769,22 @@ impl SockaddrLike for UnixAddr { { mem::size_of::() as libc::socklen_t } + + unsafe fn set_length(&mut self, new_length: usize) -> std::result::Result<(), SocketAddressLengthNotDynamic> { + // `new_length` is only used on some platforms, so it must be provided even when not used + #![allow(unused_variables)] + cfg_if! { + if #[cfg(any(target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "redox", + ))] { + self.sun_len = new_length as u8; + } + }; + Ok(()) + } } impl AsRef for UnixAddr { @@ -1139,9 +875,10 @@ pub trait SockaddrLike: private::SockaddrLikePriv { /// One common use is to match on the family of a union type, like this: /// ``` /// # use nix::sys::socket::*; + /// # use std::os::unix::io::AsRawFd; /// let fd = socket(AddressFamily::Inet, SockType::Stream, /// SockFlag::empty(), None).unwrap(); - /// let ss: SockaddrStorage = getsockname(fd).unwrap(); + /// let ss: SockaddrStorage = getsockname(fd.as_raw_fd()).unwrap(); /// match ss.family().unwrap() { /// AddressFamily::Inet => println!("{}", ss.as_sockaddr_in().unwrap()), /// AddressFamily::Inet6 => println!("{}", ss.as_sockaddr_in6().unwrap()), @@ -1198,7 +935,32 @@ pub trait SockaddrLike: private::SockaddrLikePriv { { mem::size_of::() as libc::socklen_t } + + /// Set the length of this socket address + /// + /// This method may only be called on socket addresses whose lengths are dynamic, and it + /// returns an error if called on a type whose length is static. + /// + /// # Safety + /// + /// `new_length` must be a valid length for this type of address. Specifically, reads of that + /// length from `self` must be valid. + #[doc(hidden)] + unsafe fn set_length(&mut self, _new_length: usize) -> std::result::Result<(), SocketAddressLengthNotDynamic> { + Err(SocketAddressLengthNotDynamic) + } +} + +/// The error returned by [`SockaddrLike::set_length`] on an address whose length is statically +/// fixed. +#[derive(Copy, Clone, Debug)] +pub struct SocketAddressLengthNotDynamic; +impl fmt::Display for SocketAddressLengthNotDynamic { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("Attempted to set length on socket whose length is statically fixed") + } } +impl std::error::Error for SocketAddressLengthNotDynamic {} impl private::SockaddrLikePriv for () { fn as_mut_ptr(&mut self) -> *mut libc::sockaddr { @@ -1235,9 +997,6 @@ impl SockaddrLike for () { } /// An IPv4 socket address -// This is identical to net::SocketAddrV4. But the standard library -// doesn't allow direct access to the libc fields, which we need. So we -// reimplement it here. #[cfg(feature = "net")] #[repr(transparent)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] @@ -1260,6 +1019,7 @@ impl SockaddrIn { target_os = "ios", target_os = "macos", target_os = "netbsd", + target_os = "aix", target_os = "haiku", target_os = "openbsd" ))] @@ -1502,11 +1262,12 @@ impl std::str::FromStr for SockaddrIn6 { /// ``` /// # use nix::sys::socket::*; /// # use std::str::FromStr; +/// # use std::os::unix::io::AsRawFd; /// let localhost = SockaddrIn::from_str("127.0.0.1:8081").unwrap(); /// let fd = socket(AddressFamily::Inet, SockType::Stream, SockFlag::empty(), /// None).unwrap(); -/// bind(fd, &localhost).expect("bind"); -/// let ss: SockaddrStorage = getsockname(fd).expect("getsockname"); +/// bind(fd.as_raw_fd(), &localhost).expect("bind"); +/// let ss: SockaddrStorage = getsockname(fd.as_raw_fd()).expect("getsockname"); /// assert_eq!(&localhost, ss.as_sockaddr_in().unwrap()); /// ``` #[derive(Clone, Copy, Eq)] @@ -1515,7 +1276,7 @@ pub union SockaddrStorage { #[cfg(any(target_os = "android", target_os = "linux"))] #[cfg_attr(docsrs, doc(cfg(all())))] alg: AlgAddr, - #[cfg(feature = "net")] + #[cfg(all(feature = "net", not(target_os = "redox")))] #[cfg_attr(docsrs, doc(cfg(feature = "net")))] dl: LinkAddr, #[cfg(any(target_os = "android", target_os = "linux"))] @@ -1532,7 +1293,7 @@ pub union SockaddrStorage { sin6: SockaddrIn6, ss: libc::sockaddr_storage, su: UnixAddr, - #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos" ))] #[cfg_attr(docsrs, doc(cfg(all())))] vsock: VsockAddr, } @@ -1567,7 +1328,9 @@ impl SockaddrLike for SockaddrStorage { if i32::from(ss.ss_family) == libc::AF_UNIX { // Safe because we UnixAddr is strictly smaller than // SockaddrStorage, and we just initialized the structure. - (*(&mut ss as *mut libc::sockaddr_storage as *mut UnixAddr)).sun_len = len as u8; + (*(&mut ss as *mut libc::sockaddr_storage + as *mut UnixAddr)) + .sun_len = len as u8; } Some(Self { ss }) } @@ -1622,7 +1385,7 @@ impl SockaddrLike for SockaddrStorage { libc::AF_SYSTEM => { SysControlAddr::from_raw(addr, l).map(|sctl| Self { sctl }) } - #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos" ))] libc::AF_VSOCK => { VsockAddr::from_raw(addr, l).map(|vsock| Self { vsock }) } @@ -1642,7 +1405,16 @@ impl SockaddrLike for SockaddrStorage { // The UnixAddr type knows its own length Some(ua) => ua.len(), // For all else, we're just a boring SockaddrStorage - None => mem::size_of_val(self) as libc::socklen_t + None => mem::size_of_val(self) as libc::socklen_t, + } + } + + unsafe fn set_length(&mut self, new_length: usize) -> std::result::Result<(), SocketAddressLengthNotDynamic> { + match self.as_unix_addr_mut() { + Some(addr) => { + addr.set_length(new_length) + }, + None => Err(SocketAddressLengthNotDynamic), } } } @@ -1702,12 +1474,13 @@ impl SockaddrStorage { } } // Sanity checks - if self.family() != Some(AddressFamily::Unix) || - len < offset_of!(libc::sockaddr_un, sun_path) || - len > mem::size_of::() { + if self.family() != Some(AddressFamily::Unix) + || len < offset_of!(libc::sockaddr_un, sun_path) + || len > mem::size_of::() + { None } else { - Some(unsafe{&self.su}) + Some(unsafe { &self.su }) } } @@ -1731,12 +1504,13 @@ impl SockaddrStorage { } } // Sanity checks - if self.family() != Some(AddressFamily::Unix) || - len < offset_of!(libc::sockaddr_un, sun_path) || - len > mem::size_of::() { + if self.family() != Some(AddressFamily::Unix) + || len < offset_of!(libc::sockaddr_un, sun_path) + || len > mem::size_of::() + { None } else { - Some(unsafe{&mut self.su}) + Some(unsafe { &mut self.su }) } } @@ -1787,7 +1561,7 @@ impl SockaddrStorage { accessors! {as_sys_control_addr, as_sys_control_addr_mut, SysControlAddr, AddressFamily::System, libc::sockaddr_ctl, sctl} - #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))] #[cfg_attr(docsrs, doc(cfg(all())))] accessors! {as_vsock_addr, as_vsock_addr_mut, VsockAddr, AddressFamily::Vsock, libc::sockaddr_vm, vsock} @@ -1837,7 +1611,7 @@ impl fmt::Display for SockaddrStorage { #[cfg(feature = "ioctl")] libc::AF_SYSTEM => self.sctl.fmt(f), libc::AF_UNIX => self.su.fmt(f), - #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))] libc::AF_VSOCK => self.vsock.fmt(f), _ => "

    ".fmt(f), } @@ -1911,7 +1685,7 @@ impl Hash for SockaddrStorage { #[cfg(feature = "ioctl")] libc::AF_SYSTEM => self.sctl.hash(s), libc::AF_UNIX => self.su.hash(s), - #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))] libc::AF_VSOCK => self.vsock.hash(s), _ => self.ss.hash(s), } @@ -1953,7 +1727,7 @@ impl PartialEq for SockaddrStorage { #[cfg(feature = "ioctl")] (libc::AF_SYSTEM, libc::AF_SYSTEM) => self.sctl == other.sctl, (libc::AF_UNIX, libc::AF_UNIX) => self.su == other.su, - #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))] (libc::AF_VSOCK, libc::AF_VSOCK) => self.vsock == other.vsock, _ => false, } @@ -1961,7 +1735,7 @@ impl PartialEq for SockaddrStorage { } } -mod private { +pub(super) mod private { pub trait SockaddrLikePriv { /// Returns a mutable raw pointer to the inner structure. /// @@ -1977,358 +1751,6 @@ mod private { } } -/// Represents a socket address -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -#[deprecated( - since = "0.24.0", - note = "use SockaddrLike or SockaddrStorage instead" -)] -#[allow(missing_docs)] // Since they're all deprecated anyway -#[allow(deprecated)] -#[non_exhaustive] -pub enum SockAddr { - #[cfg(feature = "net")] - #[cfg_attr(docsrs, doc(cfg(feature = "net")))] - Inet(InetAddr), - Unix(UnixAddr), - #[cfg(any(target_os = "android", target_os = "linux"))] - #[cfg_attr(docsrs, doc(cfg(all())))] - Netlink(NetlinkAddr), - #[cfg(any(target_os = "android", target_os = "linux"))] - #[cfg_attr(docsrs, doc(cfg(all())))] - Alg(AlgAddr), - #[cfg(all( - feature = "ioctl", - any(target_os = "ios", target_os = "macos") - ))] - #[cfg_attr(docsrs, doc(cfg(feature = "ioctl")))] - SysControl(SysControlAddr), - /// Datalink address (MAC) - #[cfg(any( - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "ios", - target_os = "linux", - target_os = "macos", - target_os = "illumos", - target_os = "netbsd", - target_os = "openbsd" - ))] - #[cfg(feature = "net")] - #[cfg_attr(docsrs, doc(cfg(feature = "net")))] - Link(LinkAddr), - #[cfg(any(target_os = "android", target_os = "linux"))] - #[cfg_attr(docsrs, doc(cfg(all())))] - Vsock(VsockAddr), -} - -#[allow(missing_docs)] // Since they're all deprecated anyway -#[allow(deprecated)] -impl SockAddr { - feature! { - #![feature = "net"] - pub fn new_inet(addr: InetAddr) -> SockAddr { - SockAddr::Inet(addr) - } - } - - pub fn new_unix(path: &P) -> Result { - Ok(SockAddr::Unix(UnixAddr::new(path)?)) - } - - #[cfg(any(target_os = "android", target_os = "linux"))] - #[cfg_attr(docsrs, doc(cfg(all())))] - pub fn new_netlink(pid: u32, groups: u32) -> SockAddr { - SockAddr::Netlink(NetlinkAddr::new(pid, groups)) - } - - #[cfg(any(target_os = "android", target_os = "linux"))] - #[cfg_attr(docsrs, doc(cfg(all())))] - pub fn new_alg(alg_type: &str, alg_name: &str) -> SockAddr { - SockAddr::Alg(AlgAddr::new(alg_type, alg_name)) - } - - feature! { - #![feature = "ioctl"] - #[cfg(any(target_os = "ios", target_os = "macos"))] - pub fn new_sys_control(sockfd: RawFd, name: &str, unit: u32) -> Result { - SysControlAddr::from_name(sockfd, name, unit).map(SockAddr::SysControl) - } - } - - #[cfg(any(target_os = "android", target_os = "linux"))] - #[cfg_attr(docsrs, doc(cfg(all())))] - pub fn new_vsock(cid: u32, port: u32) -> SockAddr { - SockAddr::Vsock(VsockAddr::new(cid, port)) - } - - pub fn family(&self) -> AddressFamily { - match *self { - #[cfg(feature = "net")] - SockAddr::Inet(InetAddr::V4(..)) => AddressFamily::Inet, - #[cfg(feature = "net")] - SockAddr::Inet(InetAddr::V6(..)) => AddressFamily::Inet6, - SockAddr::Unix(..) => AddressFamily::Unix, - #[cfg(any(target_os = "android", target_os = "linux"))] - SockAddr::Netlink(..) => AddressFamily::Netlink, - #[cfg(any(target_os = "android", target_os = "linux"))] - SockAddr::Alg(..) => AddressFamily::Alg, - #[cfg(all( - feature = "ioctl", - any(target_os = "ios", target_os = "macos") - ))] - SockAddr::SysControl(..) => AddressFamily::System, - #[cfg(any(target_os = "android", target_os = "linux"))] - #[cfg(feature = "net")] - SockAddr::Link(..) => AddressFamily::Packet, - #[cfg(any( - target_os = "dragonfly", - target_os = "freebsd", - target_os = "ios", - target_os = "macos", - target_os = "netbsd", - target_os = "illumos", - target_os = "openbsd" - ))] - #[cfg(feature = "net")] - SockAddr::Link(..) => AddressFamily::Link, - #[cfg(any(target_os = "android", target_os = "linux"))] - SockAddr::Vsock(..) => AddressFamily::Vsock, - } - } - - #[deprecated(since = "0.23.0", note = "use .to_string() instead")] - pub fn to_str(&self) -> String { - format!("{}", self) - } - - /// Creates a `SockAddr` struct from libc's sockaddr. - /// - /// Supports only the following address families: Unix, Inet (v4 & v6), Netlink and System. - /// Returns None for unsupported families. - /// - /// # Safety - /// - /// unsafe because it takes a raw pointer as argument. The caller must - /// ensure that the pointer is valid. - #[cfg(not(target_os = "fuchsia"))] - #[cfg(feature = "net")] - pub(crate) unsafe fn from_libc_sockaddr( - addr: *const libc::sockaddr, - ) -> Option { - if addr.is_null() { - None - } else { - match AddressFamily::from_i32(i32::from((*addr).sa_family)) { - Some(AddressFamily::Unix) => None, - #[cfg(feature = "net")] - Some(AddressFamily::Inet) => Some(SockAddr::Inet( - InetAddr::V4(ptr::read_unaligned(addr as *const _)), - )), - #[cfg(feature = "net")] - Some(AddressFamily::Inet6) => Some(SockAddr::Inet( - InetAddr::V6(ptr::read_unaligned(addr as *const _)), - )), - #[cfg(any(target_os = "android", target_os = "linux"))] - Some(AddressFamily::Netlink) => Some(SockAddr::Netlink( - NetlinkAddr(ptr::read_unaligned(addr as *const _)), - )), - #[cfg(all( - feature = "ioctl", - any(target_os = "ios", target_os = "macos") - ))] - Some(AddressFamily::System) => Some(SockAddr::SysControl( - SysControlAddr(ptr::read_unaligned(addr as *const _)), - )), - #[cfg(any(target_os = "android", target_os = "linux"))] - #[cfg(feature = "net")] - Some(AddressFamily::Packet) => Some(SockAddr::Link(LinkAddr( - ptr::read_unaligned(addr as *const _), - ))), - #[cfg(any( - target_os = "dragonfly", - target_os = "freebsd", - target_os = "ios", - target_os = "macos", - target_os = "netbsd", - target_os = "illumos", - target_os = "openbsd" - ))] - #[cfg(feature = "net")] - Some(AddressFamily::Link) => { - let ether_addr = - LinkAddr(ptr::read_unaligned(addr as *const _)); - if ether_addr.is_empty() { - None - } else { - Some(SockAddr::Link(ether_addr)) - } - } - #[cfg(any(target_os = "android", target_os = "linux"))] - Some(AddressFamily::Vsock) => Some(SockAddr::Vsock(VsockAddr( - ptr::read_unaligned(addr as *const _), - ))), - // Other address families are currently not supported and simply yield a None - // entry instead of a proper conversion to a `SockAddr`. - Some(_) | None => None, - } - } - } - - /// Conversion from nix's SockAddr type to the underlying libc sockaddr type. - /// - /// This is useful for interfacing with other libc functions that don't yet have nix wrappers. - /// Returns a reference to the underlying data type (as a sockaddr reference) along - /// with the size of the actual data type. sockaddr is commonly used as a proxy for - /// a superclass as C doesn't support inheritance, so many functions that take - /// a sockaddr * need to take the size of the underlying type as well and then internally cast it back. - pub fn as_ffi_pair(&self) -> (&libc::sockaddr, libc::socklen_t) { - match *self { - #[cfg(feature = "net")] - SockAddr::Inet(InetAddr::V4(ref addr)) => ( - // This cast is always allowed in C - unsafe { - &*(addr as *const libc::sockaddr_in - as *const libc::sockaddr) - }, - mem::size_of_val(addr) as libc::socklen_t, - ), - #[cfg(feature = "net")] - SockAddr::Inet(InetAddr::V6(ref addr)) => ( - // This cast is always allowed in C - unsafe { - &*(addr as *const libc::sockaddr_in6 - as *const libc::sockaddr) - }, - mem::size_of_val(addr) as libc::socklen_t, - ), - SockAddr::Unix(ref unix_addr) => ( - // This cast is always allowed in C - unsafe { - &*(&unix_addr.sun as *const libc::sockaddr_un - as *const libc::sockaddr) - }, - unix_addr.sun_len() as libc::socklen_t, - ), - #[cfg(any(target_os = "android", target_os = "linux"))] - SockAddr::Netlink(NetlinkAddr(ref sa)) => ( - // This cast is always allowed in C - unsafe { - &*(sa as *const libc::sockaddr_nl as *const libc::sockaddr) - }, - mem::size_of_val(sa) as libc::socklen_t, - ), - #[cfg(any(target_os = "android", target_os = "linux"))] - SockAddr::Alg(AlgAddr(ref sa)) => ( - // This cast is always allowed in C - unsafe { - &*(sa as *const libc::sockaddr_alg as *const libc::sockaddr) - }, - mem::size_of_val(sa) as libc::socklen_t, - ), - #[cfg(all( - feature = "ioctl", - any(target_os = "ios", target_os = "macos") - ))] - SockAddr::SysControl(SysControlAddr(ref sa)) => ( - // This cast is always allowed in C - unsafe { - &*(sa as *const libc::sockaddr_ctl as *const libc::sockaddr) - }, - mem::size_of_val(sa) as libc::socklen_t, - ), - #[cfg(any(target_os = "android", target_os = "linux"))] - #[cfg(feature = "net")] - SockAddr::Link(LinkAddr(ref addr)) => ( - // This cast is always allowed in C - unsafe { - &*(addr as *const libc::sockaddr_ll - as *const libc::sockaddr) - }, - mem::size_of_val(addr) as libc::socklen_t, - ), - #[cfg(any( - target_os = "dragonfly", - target_os = "freebsd", - target_os = "ios", - target_os = "macos", - target_os = "illumos", - target_os = "netbsd", - target_os = "openbsd" - ))] - #[cfg(feature = "net")] - SockAddr::Link(LinkAddr(ref addr)) => ( - // This cast is always allowed in C - unsafe { - &*(addr as *const libc::sockaddr_dl - as *const libc::sockaddr) - }, - mem::size_of_val(addr) as libc::socklen_t, - ), - #[cfg(any(target_os = "android", target_os = "linux"))] - SockAddr::Vsock(VsockAddr(ref sa)) => ( - // This cast is always allowed in C - unsafe { - &*(sa as *const libc::sockaddr_vm as *const libc::sockaddr) - }, - mem::size_of_val(sa) as libc::socklen_t, - ), - } - } -} - -#[allow(deprecated)] -impl fmt::Display for SockAddr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - #[cfg(feature = "net")] - SockAddr::Inet(ref inet) => inet.fmt(f), - SockAddr::Unix(ref unix) => unix.fmt(f), - #[cfg(any(target_os = "android", target_os = "linux"))] - SockAddr::Netlink(ref nl) => nl.fmt(f), - #[cfg(any(target_os = "android", target_os = "linux"))] - SockAddr::Alg(ref nl) => nl.fmt(f), - #[cfg(all( - feature = "ioctl", - any(target_os = "ios", target_os = "macos") - ))] - SockAddr::SysControl(ref sc) => sc.fmt(f), - #[cfg(any( - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "ios", - target_os = "linux", - target_os = "macos", - target_os = "netbsd", - target_os = "illumos", - target_os = "openbsd" - ))] - #[cfg(feature = "net")] - SockAddr::Link(ref ether_addr) => ether_addr.fmt(f), - #[cfg(any(target_os = "android", target_os = "linux"))] - SockAddr::Vsock(ref svm) => svm.fmt(f), - } - } -} - -#[cfg(not(target_os = "fuchsia"))] -#[cfg(feature = "net")] -#[allow(deprecated)] -impl private::SockaddrLikePriv for SockAddr {} -#[cfg(not(target_os = "fuchsia"))] -#[cfg(feature = "net")] -#[allow(deprecated)] -impl SockaddrLike for SockAddr { - unsafe fn from_raw( - addr: *const libc::sockaddr, - _len: Option, - ) -> Option { - Self::from_libc_sockaddr(addr) - } -} - #[cfg(any(target_os = "android", target_os = "linux"))] #[cfg_attr(docsrs, doc(cfg(all())))] pub mod netlink { @@ -2738,6 +2160,7 @@ mod datalink { target_os = "illumos", target_os = "netbsd", target_os = "haiku", + target_os = "aix", target_os = "openbsd" ))] #[cfg_attr(docsrs, doc(cfg(all())))] @@ -2850,11 +2273,10 @@ mod datalink { &self.0 } } - } } -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))] #[cfg_attr(docsrs, doc(cfg(all())))] pub mod vsock { use super::*; @@ -2900,20 +2322,33 @@ pub mod vsock { } impl PartialEq for VsockAddr { + #[cfg(any(target_os = "android", target_os = "linux"))] fn eq(&self, other: &Self) -> bool { let (inner, other) = (self.0, other.0); (inner.svm_family, inner.svm_cid, inner.svm_port) == (other.svm_family, other.svm_cid, other.svm_port) } + #[cfg(target_os = "macos")] + fn eq(&self, other: &Self) -> bool { + let (inner, other) = (self.0, other.0); + (inner.svm_family, inner.svm_cid, inner.svm_port, inner.svm_len) + == (other.svm_family, other.svm_cid, other.svm_port, inner.svm_len) + } } impl Eq for VsockAddr {} impl Hash for VsockAddr { + #[cfg(any(target_os = "android", target_os = "linux"))] fn hash(&self, s: &mut H) { let inner = self.0; (inner.svm_family, inner.svm_cid, inner.svm_port).hash(s); } + #[cfg(target_os = "macos")] + fn hash(&self, s: &mut H) { + let inner = self.0; + (inner.svm_family, inner.svm_cid, inner.svm_port, inner.svm_len).hash(s); + } } /// VSOCK Address @@ -2928,6 +2363,10 @@ pub mod vsock { addr.svm_cid = cid; addr.svm_port = port; + #[cfg(target_os = "macos")] + { + addr.svm_len = std::mem::size_of::() as u8; + } VsockAddr(addr) } @@ -2980,6 +2419,7 @@ mod tests { } } + #[cfg(not(target_os = "redox"))] mod link { #![allow(clippy::cast_ptr_alignment)] @@ -3015,7 +2455,7 @@ mod tests { sdl_slen: 0, ..unsafe { mem::zeroed() } }); - format!("{}", la); + format!("{la}"); } #[cfg(all( @@ -3109,6 +2549,7 @@ mod tests { #[test] fn size() { #[cfg(any( + target_os = "aix", target_os = "dragonfly", target_os = "freebsd", target_os = "ios", @@ -3137,7 +2578,7 @@ mod tests { fn display() { let s = "127.0.0.1:8080"; let addr = SockaddrIn::from_str(s).unwrap(); - assert_eq!(s, format!("{}", addr)); + assert_eq!(s, format!("{addr}")); } #[test] @@ -3157,7 +2598,7 @@ mod tests { fn display() { let s = "[1234:5678:90ab:cdef::1111:2222]:8080"; let addr = SockaddrIn6::from_str(s).unwrap(); - assert_eq!(s, format!("{}", addr)); + assert_eq!(s, format!("{addr}")); } #[test] @@ -3176,7 +2617,7 @@ mod tests { nix_sin6.0.sin6_flowinfo = 0x12345678; nix_sin6.0.sin6_scope_id = 0x9abcdef0; - let std_sin6 : std::net::SocketAddrV6 = nix_sin6.into(); + let std_sin6: std::net::SocketAddrV6 = nix_sin6.into(); assert_eq!(nix_sin6, std_sin6.into()); } } @@ -3188,9 +2629,8 @@ mod tests { fn from_sockaddr_un_named() { let ua = UnixAddr::new("/var/run/mysock").unwrap(); let ptr = ua.as_ptr() as *const libc::sockaddr; - let ss = unsafe { - SockaddrStorage::from_raw(ptr, Some(ua.len())) - }.unwrap(); + let ss = unsafe { SockaddrStorage::from_raw(ptr, Some(ua.len())) } + .unwrap(); assert_eq!(ss.len(), ua.len()); } @@ -3200,9 +2640,8 @@ mod tests { let name = String::from("nix\0abstract\0test"); let ua = UnixAddr::new_abstract(name.as_bytes()).unwrap(); let ptr = ua.as_ptr() as *const libc::sockaddr; - let ss = unsafe { - SockaddrStorage::from_raw(ptr, Some(ua.len())) - }.unwrap(); + let ss = unsafe { SockaddrStorage::from_raw(ptr, Some(ua.len())) } + .unwrap(); assert_eq!(ss.len(), ua.len()); } @@ -3211,9 +2650,8 @@ mod tests { fn from_sockaddr_un_abstract_unnamed() { let ua = UnixAddr::new_unnamed(); let ptr = ua.as_ptr() as *const libc::sockaddr; - let ss = unsafe { - SockaddrStorage::from_raw(ptr, Some(ua.len())) - }.unwrap(); + let ss = unsafe { SockaddrStorage::from_raw(ptr, Some(ua.len())) } + .unwrap(); assert_eq!(ss.len(), ua.len()); } } diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs index 8513b6f..78dd617 100644 --- a/src/sys/socket/mod.rs +++ b/src/sys/socket/mod.rs @@ -1,23 +1,25 @@ //! Socket interface functions //! //! [Further reading](https://man7.org/linux/man-pages/man7/socket.7.html) -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "android", target_os = "linux"))] #[cfg(feature = "uio")] use crate::sys::time::TimeSpec; +#[cfg(not(target_os = "redox"))] #[cfg(feature = "uio")] use crate::sys::time::TimeVal; use crate::{errno::Errno, Result}; use cfg_if::cfg_if; +use libc::{self, c_int, c_void, size_t, socklen_t}; +#[cfg(all(feature = "uio", not(target_os = "redox")))] use libc::{ - self, c_int, c_void, iovec, size_t, socklen_t, CMSG_DATA, CMSG_FIRSTHDR, - CMSG_LEN, CMSG_NXTHDR, + iovec, CMSG_DATA, CMSG_FIRSTHDR, CMSG_LEN, CMSG_NXTHDR, CMSG_SPACE, }; -use std::convert::{TryFrom, TryInto}; +#[cfg(not(target_os = "redox"))] use std::io::{IoSlice, IoSliceMut}; #[cfg(feature = "net")] use std::net; -use std::os::unix::io::RawFd; -use std::{mem, ptr, slice}; +use std::os::unix::io::{AsFd, AsRawFd, FromRawFd, RawFd, OwnedFd}; +use std::{mem, ptr}; #[deny(missing_docs)] mod addr; @@ -32,32 +34,26 @@ pub mod sockopt; pub use self::addr::{SockaddrLike, SockaddrStorage}; -#[cfg(not(any(target_os = "illumos", target_os = "solaris")))] -#[allow(deprecated)] -pub use self::addr::{AddressFamily, SockAddr, UnixAddr}; #[cfg(any(target_os = "illumos", target_os = "solaris"))] -#[allow(deprecated)] -pub use self::addr::{AddressFamily, SockAddr, UnixAddr}; -#[allow(deprecated)] +pub use self::addr::{AddressFamily, UnixAddr}; +#[cfg(not(any(target_os = "illumos", target_os = "solaris")))] +pub use self::addr::{AddressFamily, UnixAddr}; #[cfg(not(any( target_os = "illumos", target_os = "solaris", - target_os = "haiku" + target_os = "haiku", + target_os = "redox", )))] #[cfg(feature = "net")] -pub use self::addr::{ - InetAddr, IpAddr, Ipv4Addr, Ipv6Addr, LinkAddr, SockaddrIn, SockaddrIn6, -}; -#[allow(deprecated)] +pub use self::addr::{LinkAddr, SockaddrIn, SockaddrIn6}; #[cfg(any( target_os = "illumos", target_os = "solaris", - target_os = "haiku" + target_os = "haiku", + target_os = "redox", ))] #[cfg(feature = "net")] -pub use self::addr::{ - InetAddr, IpAddr, Ipv4Addr, Ipv6Addr, SockaddrIn, SockaddrIn6, -}; +pub use self::addr::{SockaddrIn, SockaddrIn6}; #[cfg(any(target_os = "android", target_os = "linux"))] pub use crate::sys::socket::addr::alg::AlgAddr; @@ -66,19 +62,15 @@ pub use crate::sys::socket::addr::netlink::NetlinkAddr; #[cfg(any(target_os = "ios", target_os = "macos"))] #[cfg(feature = "ioctl")] pub use crate::sys::socket::addr::sys_control::SysControlAddr; -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))] pub use crate::sys::socket::addr::vsock::VsockAddr; -#[cfg(feature = "uio")] +#[cfg(all(feature = "uio", not(target_os = "redox")))] pub use libc::{cmsghdr, msghdr}; pub use libc::{sa_family_t, sockaddr, sockaddr_storage, sockaddr_un}; #[cfg(feature = "net")] pub use libc::{sockaddr_in, sockaddr_in6}; -// Needed by the cmsg_space macro -#[doc(hidden)] -pub use libc::{c_uint, CMSG_SPACE}; - #[cfg(feature = "net")] use crate::sys::socket::addr::{ipv4addr_to_libc, ipv6addr_to_libc}; @@ -101,10 +93,11 @@ pub enum SockType { /// entire packet with each input system call. SeqPacket = libc::SOCK_SEQPACKET, /// Provides raw network protocol access. + #[cfg(not(target_os = "redox"))] Raw = libc::SOCK_RAW, /// Provides a reliable datagram layer that does not /// guarantee ordering. - #[cfg(not(any(target_os = "haiku")))] + #[cfg(not(any(target_os = "haiku", target_os = "redox")))] Rdm = libc::SOCK_RDM, } // The TryFrom impl could've been derived using libc_enum!. But for @@ -118,10 +111,11 @@ impl TryFrom for SockType { libc::SOCK_STREAM => Ok(Self::Stream), libc::SOCK_DGRAM => Ok(Self::Datagram), libc::SOCK_SEQPACKET => Ok(Self::SeqPacket), + #[cfg(not(target_os = "redox"))] libc::SOCK_RAW => Ok(Self::Raw), - #[cfg(not(any(target_os = "haiku")))] + #[cfg(not(any(target_os = "haiku", target_os = "redox")))] libc::SOCK_RDM => Ok(Self::Rdm), - _ => Err(Errno::EINVAL) + _ => Err(Errno::EINVAL), } } } @@ -164,6 +158,11 @@ pub enum SockProtocol { #[cfg(any(target_os = "android", target_os = "linux"))] #[cfg_attr(docsrs, doc(cfg(all())))] NetlinkSockDiag = libc::NETLINK_SOCK_DIAG, + /// Netfilter/iptables ULOG. + /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html)) + #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg_attr(docsrs, doc(cfg(all())))] + NetlinkNFLOG = libc::NETLINK_NFLOG, /// SELinux event notifications. /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html)) #[cfg(any(target_os = "android", target_os = "linux"))] @@ -214,6 +213,11 @@ pub enum SockProtocol { #[cfg(any(target_os = "android", target_os = "linux"))] #[cfg_attr(docsrs, doc(cfg(all())))] NetlinkKObjectUEvent = libc::NETLINK_KOBJECT_UEVENT, + /// Generic netlink family for simplified netlink usage. + /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html)) + #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg_attr(docsrs, doc(cfg(all())))] + NetlinkGeneric = libc::NETLINK_GENERIC, /// Netlink interface to request information about ciphers registered with the kernel crypto API as well as allow /// configuration of the kernel crypto API. /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html)) @@ -226,28 +230,45 @@ pub enum SockProtocol { // The protocol number is fed into the socket syscall in network byte order. #[cfg(any(target_os = "android", target_os = "linux"))] #[cfg_attr(docsrs, doc(cfg(all())))] - EthAll = libc::ETH_P_ALL.to_be(), + EthAll = (libc::ETH_P_ALL as u16).to_be() as i32, + /// The Controller Area Network raw socket protocol + /// ([ref](https://docs.kernel.org/networking/can.html#how-to-use-socketcan)) + #[cfg(target_os = "linux")] + #[cfg_attr(docsrs, doc(cfg(all())))] + CanRaw = libc::CAN_RAW, } -#[cfg(any(target_os = "linux"))] +impl SockProtocol { + /// The Controller Area Network broadcast manager protocol + /// ([ref](https://docs.kernel.org/networking/can.html#how-to-use-socketcan)) + #[cfg(target_os = "linux")] + #[cfg_attr(docsrs, doc(cfg(all())))] + #[allow(non_upper_case_globals)] + pub const CanBcm: SockProtocol = SockProtocol::NetlinkUserSock; // Matches libc::CAN_BCM +} +#[cfg(any(target_os = "android", target_os = "linux"))] libc_bitflags! { /// Configuration flags for `SO_TIMESTAMPING` interface /// /// For use with [`Timestamping`][sockopt::Timestamping]. /// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html) - pub struct TimestampingFlag: c_uint { + pub struct TimestampingFlag: libc::c_uint { /// Report any software timestamps when available. SOF_TIMESTAMPING_SOFTWARE; /// Report hardware timestamps as generated by SOF_TIMESTAMPING_TX_HARDWARE when available. SOF_TIMESTAMPING_RAW_HARDWARE; - /// Collect transmiting timestamps as reported by hardware + /// Collect transmitting timestamps as reported by hardware SOF_TIMESTAMPING_TX_HARDWARE; - /// Collect transmiting timestamps as reported by software + /// Collect transmitting timestamps as reported by software SOF_TIMESTAMPING_TX_SOFTWARE; /// Collect receiving timestamps as reported by hardware SOF_TIMESTAMPING_RX_HARDWARE; /// Collect receiving timestamps as reported by software SOF_TIMESTAMPING_RX_SOFTWARE; + /// Generate a unique identifier along with each transmitted packet + SOF_TIMESTAMPING_OPT_ID; + /// Return transmit timestamps alongside an empty packet instead of the original packet + SOF_TIMESTAMPING_OPT_TSONLY; } } @@ -292,17 +313,14 @@ libc_bitflags! { /// Sends or requests out-of-band data on sockets that support this notion /// (e.g., of type [`Stream`](enum.SockType.html)); the underlying protocol must also /// support out-of-band data. - #[allow(deprecated)] // Suppress useless warnings from libc PR 2963 MSG_OOB; /// Peeks at an incoming message. The data is treated as unread and the next /// [`recv()`](fn.recv.html) /// or similar function shall still return this data. - #[allow(deprecated)] // Suppress useless warnings from libc PR 2963 MSG_PEEK; /// Receive operation blocks until the full amount of data can be /// returned. The function may return smaller amount of data if a signal /// is caught, an error or disconnect occurs. - #[allow(deprecated)] // Suppress useless warnings from libc PR 2963 MSG_WAITALL; /// Enables nonblocking operation; if the operation would block, /// `EAGAIN` or `EWOULDBLOCK` is returned. This provides similar @@ -314,10 +332,10 @@ libc_bitflags! { /// which will affect all threads in /// the calling process and as well as other processes that hold /// file descriptors referring to the same open file description. - #[allow(deprecated)] // Suppress useless warnings from libc PR 2963 + #[cfg(not(target_os = "aix"))] + #[cfg_attr(docsrs, doc(cfg(all())))] MSG_DONTWAIT; /// Receive flags: Control Data was discarded (buffer too small) - #[allow(deprecated)] // Suppress useless warnings from libc PR 2963 MSG_CTRUNC; /// For raw ([`Packet`](addr/enum.AddressFamily.html)), Internet datagram /// (since Linux 2.4.27/2.6.8), @@ -327,18 +345,15 @@ libc_bitflags! { /// domain ([unix(7)](https://linux.die.net/man/7/unix)) sockets. /// /// For use with Internet stream sockets, see [tcp(7)](https://linux.die.net/man/7/tcp). - #[allow(deprecated)] // Suppress useless warnings from libc PR 2963 MSG_TRUNC; /// Terminates a record (when this notion is supported, as for /// sockets of type [`SeqPacket`](enum.SockType.html)). - #[allow(deprecated)] // Suppress useless warnings from libc PR 2963 MSG_EOR; /// This flag specifies that queued errors should be received from /// the socket error queue. (For more details, see /// [recvfrom(2)](https://linux.die.net/man/2/recvfrom)) #[cfg(any(target_os = "android", target_os = "linux"))] #[cfg_attr(docsrs, doc(cfg(all())))] - #[allow(deprecated)] // Suppress useless warnings from libc PR 2963 MSG_ERRQUEUE; /// Set the `close-on-exec` flag for the file descriptor received via a UNIX domain /// file descriptor using the `SCM_RIGHTS` operation (described in @@ -354,7 +369,6 @@ libc_bitflags! { target_os = "netbsd", target_os = "openbsd"))] #[cfg_attr(docsrs, doc(cfg(all())))] - #[allow(deprecated)] // Suppress useless warnings from libc PR 2963 MSG_CMSG_CLOEXEC; /// Requests not to send `SIGPIPE` errors when the other end breaks the connection. /// (For more details, see [send(2)](https://linux.die.net/man/2/send)). @@ -369,8 +383,18 @@ libc_bitflags! { target_os = "openbsd", target_os = "solaris"))] #[cfg_attr(docsrs, doc(cfg(all())))] - #[allow(deprecated)] // Suppress useless warnings from libc PR 2963 MSG_NOSIGNAL; + /// Turns on [`MSG_DONTWAIT`] after the first message has been received (only for + /// `recvmmsg()`). + #[cfg(any(target_os = "android", + target_os = "fuchsia", + target_os = "linux", + target_os = "netbsd", + target_os = "freebsd", + target_os = "openbsd", + target_os = "solaris"))] + #[cfg_attr(docsrs, doc(cfg(all())))] + MSG_WAITFORONE; } } @@ -462,7 +486,7 @@ cfg_if! { /// Returns a list group identifiers (the first one being the effective GID) pub fn groups(&self) -> &[libc::gid_t] { unsafe { - slice::from_raw_parts( + std::slice::from_raw_parts( self.0.cmcred_groups.as_ptr() as *const libc::gid_t, self.0.cmcred_ngroups as _ ) @@ -555,6 +579,7 @@ impl Ipv6MembershipRequest { } } +#[cfg(not(target_os = "redox"))] feature! { #![feature = "uio"] @@ -584,18 +609,19 @@ feature! { macro_rules! cmsg_space { ( $( $x:ty ),* ) => { { - let mut space = 0; - $( - // CMSG_SPACE is always safe - space += unsafe { - $crate::sys::socket::CMSG_SPACE(::std::mem::size_of::<$x>() as $crate::sys::socket::c_uint) - } as usize; - )* + let space = 0 $(+ $crate::sys::socket::cmsg_space::<$x>())*; Vec::::with_capacity(space) } } } +#[inline] +#[doc(hidden)] +pub fn cmsg_space() -> usize { + // SAFETY: CMSG_SPACE is always safe + unsafe { libc::CMSG_SPACE(mem::size_of::() as libc::c_uint) as usize } +} + #[derive(Clone, Copy, Debug, Eq, PartialEq)] /// Contains outcome of sending or receiving a message /// @@ -688,6 +714,7 @@ pub enum ControlMessageOwned { /// # use std::io::{IoSlice, IoSliceMut}; /// # use std::time::*; /// # use std::str::FromStr; + /// # use std::os::unix::io::AsRawFd; /// # fn main() { /// // Set up /// let message = "Ohayō!".as_bytes(); @@ -696,22 +723,22 @@ pub enum ControlMessageOwned { /// SockType::Datagram, /// SockFlag::empty(), /// None).unwrap(); - /// setsockopt(in_socket, sockopt::ReceiveTimestamp, &true).unwrap(); + /// setsockopt(&in_socket, sockopt::ReceiveTimestamp, &true).unwrap(); /// let localhost = SockaddrIn::from_str("127.0.0.1:0").unwrap(); - /// bind(in_socket, &localhost).unwrap(); - /// let address: SockaddrIn = getsockname(in_socket).unwrap(); + /// bind(in_socket.as_raw_fd(), &localhost).unwrap(); + /// let address: SockaddrIn = getsockname(in_socket.as_raw_fd()).unwrap(); /// // Get initial time /// let time0 = SystemTime::now(); /// // Send the message /// let iov = [IoSlice::new(message)]; /// let flags = MsgFlags::empty(); - /// let l = sendmsg(in_socket, &iov, &[], flags, Some(&address)).unwrap(); + /// let l = sendmsg(in_socket.as_raw_fd(), &iov, &[], flags, Some(&address)).unwrap(); /// assert_eq!(message.len(), l); /// // Receive the message /// let mut buffer = vec![0u8; message.len()]; /// let mut cmsgspace = cmsg_space!(TimeVal); /// let mut iov = [IoSliceMut::new(&mut buffer)]; - /// let r = recvmsg::(in_socket, &mut iov, Some(&mut cmsgspace), flags) + /// let r = recvmsg::(in_socket.as_raw_fd(), &mut iov, Some(&mut cmsgspace), flags) /// .unwrap(); /// let rtime = match r.cmsgs().next() { /// Some(ControlMessageOwned::ScmTimestamp(rtime)) => rtime, @@ -727,19 +754,18 @@ pub enum ControlMessageOwned { /// assert!(time0.duration_since(UNIX_EPOCH).unwrap() <= rduration); /// assert!(rduration <= time1.duration_since(UNIX_EPOCH).unwrap()); /// // Close socket - /// nix::unistd::close(in_socket).unwrap(); /// # } /// ``` ScmTimestamp(TimeVal), /// A set of nanosecond resolution timestamps /// /// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html) - #[cfg(all(target_os = "linux"))] + #[cfg(any(target_os = "android", target_os = "linux"))] ScmTimestampsns(Timestamps), /// Nanoseconds resolution timestamp /// /// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html) - #[cfg(all(target_os = "linux"))] + #[cfg(any(target_os = "android", target_os = "linux"))] #[cfg_attr(docsrs, doc(cfg(all())))] ScmTimestampns(TimeSpec), #[cfg(any( @@ -836,7 +862,7 @@ pub enum ControlMessageOwned { } /// For representing packet timestamps via `SO_TIMESTAMPING` interface -#[cfg(all(target_os = "linux"))] +#[cfg(any(target_os = "android", target_os = "linux"))] #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct Timestamps { /// software based timestamp, usually one containing data @@ -884,17 +910,17 @@ impl ControlMessageOwned { let cred: libc::cmsgcred = ptr::read_unaligned(p as *const _); ControlMessageOwned::ScmCreds(cred.into()) } - #[cfg(not(target_os = "haiku"))] + #[cfg(not(any(target_os = "aix", target_os = "haiku")))] (libc::SOL_SOCKET, libc::SCM_TIMESTAMP) => { let tv: libc::timeval = ptr::read_unaligned(p as *const _); ControlMessageOwned::ScmTimestamp(TimeVal::from(tv)) }, - #[cfg(all(target_os = "linux"))] + #[cfg(any(target_os = "android", target_os = "linux"))] (libc::SOL_SOCKET, libc::SCM_TIMESTAMPNS) => { let ts: libc::timespec = ptr::read_unaligned(p as *const _); ControlMessageOwned::ScmTimestampns(TimeSpec::from(ts)) } - #[cfg(all(target_os = "linux"))] + #[cfg(any(target_os = "android", target_os = "linux"))] (libc::SOL_SOCKET, libc::SCM_TIMESTAMPING) => { let tp = p as *const libc::timespec; let ts: libc::timespec = ptr::read_unaligned(tp); @@ -990,7 +1016,7 @@ impl ControlMessageOwned { ControlMessageOwned::Ipv6OrigDstAddr(dl) }, (_, _) => { - let sl = slice::from_raw_parts(p, len); + let sl = std::slice::from_raw_parts(p, len); let ucmsg = UnknownCmsg(*header, Vec::::from(sl)); ControlMessageOwned::Unknown(ucmsg) } @@ -1446,6 +1472,7 @@ impl<'a> ControlMessage<'a> { /// # use nix::sys::socket::*; /// # use nix::unistd::pipe; /// # use std::io::IoSlice; +/// # use std::os::unix::io::AsRawFd; /// let (fd1, fd2) = socketpair(AddressFamily::Unix, SockType::Stream, None, /// SockFlag::empty()) /// .unwrap(); @@ -1454,7 +1481,7 @@ impl<'a> ControlMessage<'a> { /// let iov = [IoSlice::new(b"hello")]; /// let fds = [r]; /// let cmsg = ControlMessage::ScmRights(&fds); -/// sendmsg::<()>(fd1, &iov, &[cmsg], MsgFlags::empty(), None).unwrap(); +/// sendmsg::<()>(fd1.as_raw_fd(), &iov, &[cmsg], MsgFlags::empty(), None).unwrap(); /// ``` /// When directing to a specific address, the generic type will be inferred. /// ``` @@ -1462,6 +1489,7 @@ impl<'a> ControlMessage<'a> { /// # use nix::unistd::pipe; /// # use std::io::IoSlice; /// # use std::str::FromStr; +/// # use std::os::unix::io::AsRawFd; /// let localhost = SockaddrIn::from_str("1.2.3.4:8080").unwrap(); /// let fd = socket(AddressFamily::Inet, SockType::Datagram, SockFlag::empty(), /// None).unwrap(); @@ -1470,7 +1498,7 @@ impl<'a> ControlMessage<'a> { /// let iov = [IoSlice::new(b"hello")]; /// let fds = [r]; /// let cmsg = ControlMessage::ScmRights(&fds); -/// sendmsg(fd, &iov, &[cmsg], MsgFlags::empty(), Some(&localhost)).unwrap(); +/// sendmsg(fd.as_raw_fd(), &iov, &[cmsg], MsgFlags::empty(), Some(&localhost)).unwrap(); /// ``` pub fn sendmsg(fd: RawFd, iov: &[IoSlice<'_>], cmsgs: &[ControlMessage], flags: MsgFlags, addr: Option<&S>) -> Result @@ -1535,7 +1563,7 @@ pub fn sendmmsg<'a, XS, AS, C, I, S>( for (i, ((slice, addr), mmsghdr)) in slices.into_iter().zip(addrs.as_ref()).zip(data.items.iter_mut() ).enumerate() { - let mut p = &mut mmsghdr.msg_hdr; + let p = &mut mmsghdr.msg_hdr; p.msg_iov = slice.as_ref().as_ptr() as *mut libc::iovec; p.msg_iovlen = slice.as_ref().len() as _; @@ -1590,8 +1618,7 @@ pub struct MultiHeaders { addresses: Box<[mem::MaybeUninit]>, // while we are not using it directly - this is used to store control messages // and we retain pointers to them inside items array - #[allow(dead_code)] - cmsg_buffers: Option>, + _cmsg_buffers: Option>, msg_controllen: usize, } @@ -1611,24 +1638,24 @@ impl MultiHeaders { { // we will be storing pointers to addresses inside mhdr - convert it into boxed // slice so it can'be changed later by pushing anything into self.addresses - let mut addresses = vec![std::mem::MaybeUninit::uninit(); num_slices].into_boxed_slice(); + let mut addresses = vec![std::mem::MaybeUninit::::uninit(); num_slices].into_boxed_slice(); let msg_controllen = cmsg_buffer.as_ref().map_or(0, |v| v.capacity()); // we'll need a cmsg_buffer for each slice, we preallocate a vector and split // it into "slices" parts - let cmsg_buffers = + let mut cmsg_buffers = cmsg_buffer.map(|v| vec![0u8; v.capacity() * num_slices].into_boxed_slice()); let items = addresses .iter_mut() .enumerate() .map(|(ix, address)| { - let (ptr, cap) = match &cmsg_buffers { - Some(v) => ((&v[ix * msg_controllen] as *const u8), msg_controllen), - None => (std::ptr::null(), 0), + let (ptr, cap) = match &mut cmsg_buffers { + Some(v) => ((&mut v[ix * msg_controllen] as *mut u8), msg_controllen), + None => (std::ptr::null_mut(), 0), }; - let msg_hdr = unsafe { pack_mhdr_to_receive(std::ptr::null(), 0, ptr, cap, address.as_mut_ptr()) }; + let msg_hdr = unsafe { pack_mhdr_to_receive(std::ptr::null_mut(), 0, ptr, cap, address.as_mut_ptr()) }; libc::mmsghdr { msg_hdr, msg_len: 0, @@ -1639,7 +1666,7 @@ impl MultiHeaders { Self { items: items.into_boxed_slice(), addresses, - cmsg_buffers, + _cmsg_buffers: cmsg_buffers, msg_controllen, } } @@ -1689,7 +1716,7 @@ where { let mut count = 0; for (i, (slice, mmsghdr)) in slices.into_iter().zip(data.items.iter_mut()).enumerate() { - let mut p = &mut mmsghdr.msg_hdr; + let p = &mut mmsghdr.msg_hdr; p.msg_iov = slice.as_ref().as_ptr() as *mut libc::iovec; p.msg_iovlen = slice.as_ref().len() as _; count = i + 1; @@ -1819,6 +1846,7 @@ mod test { use crate::sys::socket::{AddressFamily, ControlMessageOwned}; use crate::*; use std::str::FromStr; + use std::os::unix::io::AsRawFd; #[cfg_attr(qemu, ignore)] #[test] @@ -1845,9 +1873,9 @@ mod test { None, )?; - crate::sys::socket::bind(rsock, &sock_addr)?; + crate::sys::socket::bind(rsock.as_raw_fd(), &sock_addr)?; - setsockopt(rsock, Timestamping, &TimestampingFlag::all())?; + setsockopt(&rsock, Timestamping, &TimestampingFlag::all())?; let sbuf = (0..400).map(|i| i as u8).collect::>(); @@ -1869,13 +1897,13 @@ mod test { let iov1 = [IoSlice::new(&sbuf)]; let cmsg = cmsg_space!(crate::sys::socket::Timestamps); - sendmsg(ssock, &iov1, &[], flags, Some(&sock_addr)).unwrap(); + sendmsg(ssock.as_raw_fd(), &iov1, &[], flags, Some(&sock_addr)).unwrap(); let mut data = super::MultiHeaders::<()>::preallocate(recv_iovs.len(), Some(cmsg)); let t = sys::time::TimeSpec::from_duration(std::time::Duration::from_secs(10)); - let recv = super::recvmmsg(rsock, &mut data, recv_iovs.iter(), flags, Some(t))?; + let recv = super::recvmmsg(rsock.as_raw_fd(), &mut data, recv_iovs.iter(), flags, Some(t))?; for rmsg in recv { #[cfg(not(any(qemu, target_arch = "aarch64")))] @@ -1916,7 +1944,7 @@ unsafe fn read_mhdr<'a, 'i, S>( mhdr: msghdr, r: isize, msg_controllen: usize, - address: S, + mut address: S, ) -> RecvMsg<'a, 'i, S> where S: SockaddrLike { @@ -1932,6 +1960,11 @@ unsafe fn read_mhdr<'a, 'i, S>( }.as_ref() }; + // Ignore errors if this socket address has statically-known length + // + // This is to ensure that unix socket addresses have their length set appropriately. + let _ = address.set_length(mhdr.msg_namelen as usize); + RecvMsg { bytes: r as usize, cmsghdr, @@ -1954,9 +1987,9 @@ unsafe fn read_mhdr<'a, 'i, S>( /// /// Buffers must remain valid for the whole lifetime of msghdr unsafe fn pack_mhdr_to_receive( - iov_buffer: *const IoSliceMut, + iov_buffer: *mut IoSliceMut, iov_buffer_len: usize, - cmsg_buffer: *const u8, + cmsg_buffer: *mut u8, cmsg_capacity: usize, address: *mut S, ) -> msghdr @@ -1967,7 +2000,7 @@ unsafe fn pack_mhdr_to_receive( // initialize it. let mut mhdr = mem::MaybeUninit::::zeroed(); let p = mhdr.as_mut_ptr(); - (*p).msg_name = (*address).as_mut_ptr() as *mut c_void; + (*p).msg_name = address as *mut c_void; (*p).msg_namelen = S::size(); (*p).msg_iov = iov_buffer as *mut iovec; (*p).msg_iovlen = iov_buffer_len as _; @@ -1992,7 +2025,7 @@ fn pack_mhdr_to_send<'a, I, C, S>( // The message header must be initialized before the individual cmsgs. let cmsg_ptr = if capacity > 0 { - cmsg_buffer.as_ptr() as *mut c_void + cmsg_buffer.as_mut_ptr() as *mut c_void } else { ptr::null_mut() }; @@ -2046,7 +2079,7 @@ fn pack_mhdr_to_send<'a, I, C, S>( /// [recvmsg(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvmsg.html) pub fn recvmsg<'a, 'outer, 'inner, S>(fd: RawFd, iov: &'outer mut [IoSliceMut<'inner>], mut cmsg_buffer: Option<&'a mut Vec>, - flags: MsgFlags) -> Result> + flags: MsgFlags) -> Result> where S: SockaddrLike + 'a, 'inner: 'outer { @@ -2056,7 +2089,7 @@ pub fn recvmsg<'a, 'outer, 'inner, S>(fd: RawFd, iov: &'outer mut [IoSliceMut<'i .map(|v| (v.as_mut_ptr(), v.capacity())) .unwrap_or((ptr::null_mut(), 0)); let mut mhdr = unsafe { - pack_mhdr_to_receive(iov.as_ref().as_ptr(), iov.len(), msg_control, msg_controllen, address.as_mut_ptr()) + pack_mhdr_to_receive(iov.as_mut().as_mut_ptr(), iov.len(), msg_control, msg_controllen, address.as_mut_ptr()) }; let ret = unsafe { libc::recvmsg(fd, &mut mhdr, flags.bits()) }; @@ -2082,7 +2115,7 @@ pub fn socket>>( ty: SockType, flags: SockFlag, protocol: T, -) -> Result { +) -> Result { let protocol = match protocol.into() { None => 0, Some(p) => p as c_int, @@ -2096,7 +2129,13 @@ pub fn socket>>( let res = unsafe { libc::socket(domain as c_int, ty, protocol) }; - Errno::result(res) + match res { + -1 => Err(Errno::last()), + fd => { + // Safe because libc::socket returned success + unsafe { Ok(OwnedFd::from_raw_fd(fd)) } + } + } } /// Create a pair of connected sockets @@ -2107,7 +2146,7 @@ pub fn socketpair>>( ty: SockType, protocol: T, flags: SockFlag, -) -> Result<(RawFd, RawFd)> { +) -> Result<(OwnedFd, OwnedFd)> { let protocol = match protocol.into() { None => 0, Some(p) => p as c_int, @@ -2126,14 +2165,18 @@ pub fn socketpair>>( }; Errno::result(res)?; - Ok((fds[0], fds[1])) + // Safe because socketpair returned success. + unsafe { + Ok((OwnedFd::from_raw_fd(fds[0]), OwnedFd::from_raw_fd(fds[1]))) + } } /// Listen for connections on a socket /// /// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/listen.html) -pub fn listen(sockfd: RawFd, backlog: usize) -> Result<()> { - let res = unsafe { libc::listen(sockfd, backlog as c_int) }; +pub fn listen(sock: &F, backlog: usize) -> Result<()> { + let fd = sock.as_fd().as_raw_fd(); + let res = unsafe { libc::listen(fd, backlog as c_int) }; Errno::result(res).map(drop) } @@ -2202,7 +2245,7 @@ pub fn recv(sockfd: RawFd, buf: &mut [u8], flags: MsgFlags) -> Result { unsafe { let ret = libc::recv( sockfd, - buf.as_ptr() as *mut c_void, + buf.as_mut_ptr() as *mut c_void, buf.len() as size_t, flags.bits(), ); @@ -2226,17 +2269,17 @@ pub fn recvfrom( let ret = Errno::result(libc::recvfrom( sockfd, - buf.as_ptr() as *mut c_void, + buf.as_mut_ptr() as *mut c_void, buf.len() as size_t, 0, - addr.as_mut_ptr() as *mut libc::sockaddr, + addr.as_mut_ptr() as *mut sockaddr, &mut len as *mut socklen_t, ))? as usize; Ok(( ret, T::from_raw( - addr.assume_init().as_ptr() as *const libc::sockaddr, + addr.assume_init().as_ptr(), Some(len), ), )) @@ -2293,7 +2336,7 @@ pub trait GetSockOpt: Copy { type Val; /// Look up the value of this socket option on the given socket. - fn get(&self, fd: RawFd) -> Result; + fn get(&self, fd: &F) -> Result; } /// Represents a socket option that can be set. @@ -2301,13 +2344,13 @@ pub trait SetSockOpt: Clone { type Val; /// Set the value of this socket option on the given socket. - fn set(&self, fd: RawFd, val: &Self::Val) -> Result<()>; + fn set(&self, fd: &F, val: &Self::Val) -> Result<()>; } /// Get the current value for the requested socket option /// /// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockopt.html) -pub fn getsockopt(fd: RawFd, opt: O) -> Result { +pub fn getsockopt(fd: &F, opt: O) -> Result { opt.get(fd) } @@ -2321,15 +2364,14 @@ pub fn getsockopt(fd: RawFd, opt: O) -> Result { /// use nix::sys::socket::setsockopt; /// use nix::sys::socket::sockopt::KeepAlive; /// use std::net::TcpListener; -/// use std::os::unix::io::AsRawFd; /// /// let listener = TcpListener::bind("0.0.0.0:0").unwrap(); -/// let fd = listener.as_raw_fd(); -/// let res = setsockopt(fd, KeepAlive, &true); +/// let fd = listener; +/// let res = setsockopt(&fd, KeepAlive, &true); /// assert!(res.is_ok()); /// ``` -pub fn setsockopt( - fd: RawFd, +pub fn setsockopt( + fd: &F, opt: O, val: &O::Val, ) -> Result<()> { @@ -2344,11 +2386,8 @@ pub fn getpeername(fd: RawFd) -> Result { let mut addr = mem::MaybeUninit::::uninit(); let mut len = T::size(); - let ret = libc::getpeername( - fd, - addr.as_mut_ptr() as *mut libc::sockaddr, - &mut len, - ); + let ret = + libc::getpeername(fd, addr.as_mut_ptr() as *mut sockaddr, &mut len); Errno::result(ret)?; @@ -2364,11 +2403,8 @@ pub fn getsockname(fd: RawFd) -> Result { let mut addr = mem::MaybeUninit::::uninit(); let mut len = T::size(); - let ret = libc::getsockname( - fd, - addr.as_mut_ptr() as *mut libc::sockaddr, - &mut len, - ); + let ret = + libc::getsockname(fd, addr.as_mut_ptr() as *mut sockaddr, &mut len); Errno::result(ret)?; @@ -2376,81 +2412,6 @@ pub fn getsockname(fd: RawFd) -> Result { } } -/// Return the appropriate `SockAddr` type from a `sockaddr_storage` of a -/// certain size. -/// -/// In C this would usually be done by casting. The `len` argument -/// should be the number of bytes in the `sockaddr_storage` that are actually -/// allocated and valid. It must be at least as large as all the useful parts -/// of the structure. Note that in the case of a `sockaddr_un`, `len` need not -/// include the terminating null. -#[deprecated( - since = "0.24.0", - note = "use SockaddrLike or SockaddrStorage instead" -)] -#[allow(deprecated)] -pub fn sockaddr_storage_to_addr( - addr: &sockaddr_storage, - len: usize, -) -> Result { - assert!(len <= mem::size_of::()); - if len < mem::size_of_val(&addr.ss_family) { - return Err(Errno::ENOTCONN); - } - - match c_int::from(addr.ss_family) { - #[cfg(feature = "net")] - libc::AF_INET => { - assert!(len >= mem::size_of::()); - let sin = unsafe { - *(addr as *const sockaddr_storage as *const sockaddr_in) - }; - Ok(SockAddr::Inet(InetAddr::V4(sin))) - } - #[cfg(feature = "net")] - libc::AF_INET6 => { - assert!(len >= mem::size_of::()); - let sin6 = unsafe { *(addr as *const _ as *const sockaddr_in6) }; - Ok(SockAddr::Inet(InetAddr::V6(sin6))) - } - libc::AF_UNIX => unsafe { - let sun = *(addr as *const _ as *const sockaddr_un); - let sun_len = len.try_into().unwrap(); - Ok(SockAddr::Unix(UnixAddr::from_raw_parts(sun, sun_len))) - }, - #[cfg(any(target_os = "android", target_os = "linux"))] - #[cfg(feature = "net")] - libc::AF_PACKET => { - use libc::sockaddr_ll; - // Don't assert anything about the size. - // Apparently the Linux kernel can return smaller sizes when - // the value in the last element of sockaddr_ll (`sll_addr`) is - // smaller than the declared size of that field - let sll = unsafe { *(addr as *const _ as *const sockaddr_ll) }; - Ok(SockAddr::Link(LinkAddr(sll))) - } - #[cfg(any(target_os = "android", target_os = "linux"))] - libc::AF_NETLINK => { - use libc::sockaddr_nl; - let snl = unsafe { *(addr as *const _ as *const sockaddr_nl) }; - Ok(SockAddr::Netlink(NetlinkAddr(snl))) - } - #[cfg(any(target_os = "android", target_os = "linux"))] - libc::AF_ALG => { - use libc::sockaddr_alg; - let salg = unsafe { *(addr as *const _ as *const sockaddr_alg) }; - Ok(SockAddr::Alg(AlgAddr(salg))) - } - #[cfg(any(target_os = "android", target_os = "linux"))] - libc::AF_VSOCK => { - use libc::sockaddr_vm; - let svm = unsafe { *(addr as *const _ as *const sockaddr_vm) }; - Ok(SockAddr::Vsock(VsockAddr(svm))) - } - af => panic!("unexpected address family {}", af), - } -} - #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum Shutdown { /// Further receptions will be disallowed. @@ -2480,8 +2441,25 @@ pub fn shutdown(df: RawFd, how: Shutdown) -> Result<()> { #[cfg(test)] mod tests { + #[cfg(not(target_os = "redox"))] #[test] fn can_use_cmsg_space() { let _ = cmsg_space!(u8); } + + #[cfg(not(any( + target_os = "redox", + target_os = "linux", + target_os = "android" + )))] + #[test] + fn can_open_routing_socket() { + let _ = super::socket( + super::AddressFamily::Route, + super::SockType::Raw, + super::SockFlag::empty(), + None, + ) + .expect("Failed to open routing socket"); + } } diff --git a/src/sys/socket/sockopt.rs b/src/sys/socket/sockopt.rs index 06e9ee4..44f3ebb 100644 --- a/src/sys/socket/sockopt.rs +++ b/src/sys/socket/sockopt.rs @@ -6,13 +6,10 @@ use crate::Result; use cfg_if::cfg_if; use libc::{self, c_int, c_void, socklen_t}; use std::ffi::{OsStr, OsString}; -use std::{ - convert::TryFrom, - mem::{self, MaybeUninit} -}; +use std::mem::{self, MaybeUninit}; #[cfg(target_family = "unix")] use std::os::unix::ffi::OsStrExt; -use std::os::unix::io::RawFd; +use std::os::unix::io::{AsFd, AsRawFd}; // Constants // TCP_CA_NAME_MAX isn't defined in user space include files @@ -47,12 +44,12 @@ macro_rules! setsockopt_impl { impl SetSockOpt for $name { type Val = $ty; - fn set(&self, fd: RawFd, val: &$ty) -> Result<()> { + fn set(&self, fd: &F, val: &$ty) -> Result<()> { unsafe { let setter: $setter = Set::new(val); let res = libc::setsockopt( - fd, + fd.as_fd().as_raw_fd(), $level, $flag, setter.ffi_ptr(), @@ -92,12 +89,12 @@ macro_rules! getsockopt_impl { impl GetSockOpt for $name { type Val = $ty; - fn get(&self, fd: RawFd) -> Result<$ty> { + fn get(&self, fd: &F) -> Result<$ty> { unsafe { let mut getter: $getter = Get::uninit(); let res = libc::getsockopt( - fd, + fd.as_fd().as_raw_fd(), $level, $flag, getter.ffi_ptr(), @@ -107,7 +104,7 @@ macro_rules! getsockopt_impl { match <$ty>::try_from(getter.assume_init()) { Err(_) => Err(Errno::EINVAL), - Ok(r) => Ok(r) + Ok(r) => Ok(r), } } } @@ -141,7 +138,6 @@ macro_rules! getsockopt_impl { /// * `$getter:ty`: `Get` implementation; optional; only for `GetOnly` and `Both`. /// * `$setter:ty`: `Set` implementation; optional; only for `SetOnly` and `Both`. // Some targets don't use all rules. -#[allow(unknown_lints)] #[allow(unused_macro_rules)] macro_rules! sockopt_impl { ($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, bool) => { @@ -496,6 +492,15 @@ sockopt_impl!( libc::LOCAL_PEERCRED, super::XuCred ); +#[cfg(any(target_os = "macos", target_os = "ios"))] +sockopt_impl!( + /// Get the PID of the peer process of a connected unix domain socket. + LocalPeerPid, + GetOnly, + 0, + libc::LOCAL_PEERPID, + libc::c_int +); #[cfg(any(target_os = "android", target_os = "linux"))] sockopt_impl!( /// Return the credentials of the foreign process connected to this socket. @@ -539,13 +544,13 @@ cfg_if! { sockopt_impl!( /// The maximum segment size for outgoing TCP packets. TcpMaxSeg, Both, libc::IPPROTO_TCP, libc::TCP_MAXSEG, u32); - } else { + } else if #[cfg(not(target_os = "redox"))] { sockopt_impl!( /// The maximum segment size for outgoing TCP packets. TcpMaxSeg, GetOnly, libc::IPPROTO_TCP, libc::TCP_MAXSEG, u32); } } -#[cfg(not(any(target_os = "openbsd", target_os = "haiku")))] +#[cfg(not(any(target_os = "openbsd", target_os = "haiku", target_os = "redox")))] #[cfg(feature = "net")] sockopt_impl!( #[cfg_attr(docsrs, doc(cfg(feature = "net")))] @@ -567,7 +572,7 @@ sockopt_impl!( libc::TCP_REPAIR, u32 ); -#[cfg(not(any(target_os = "openbsd", target_os = "haiku")))] +#[cfg(not(any(target_os = "openbsd", target_os = "haiku", target_os = "redox")))] #[cfg(feature = "net")] sockopt_impl!( #[cfg_attr(docsrs, doc(cfg(feature = "net")))] @@ -678,7 +683,7 @@ sockopt_impl!( libc::IP6T_SO_ORIGINAL_DST, libc::sockaddr_in6 ); -#[cfg(any(target_os = "linux"))] +#[cfg(any(target_os = "android", target_os = "linux"))] sockopt_impl!( /// Specifies exact type of timestamping information collected by the kernel /// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html) @@ -688,7 +693,7 @@ sockopt_impl!( libc::SO_TIMESTAMPING, super::TimestampingFlag ); -#[cfg(not(target_os = "haiku"))] +#[cfg(not(any(target_os = "aix", target_os = "haiku", target_os = "redox")))] sockopt_impl!( /// Enable or disable the receiving of the `SO_TIMESTAMP` control message. ReceiveTimestamp, @@ -697,7 +702,7 @@ sockopt_impl!( libc::SO_TIMESTAMP, bool ); -#[cfg(all(target_os = "linux"))] +#[cfg(any(target_os = "android", target_os = "linux"))] sockopt_impl!( /// Enable or disable the receiving of the `SO_TIMESTAMPNS` control message. ReceiveTimestampns, @@ -706,6 +711,16 @@ sockopt_impl!( libc::SO_TIMESTAMPNS, bool ); +#[cfg(target_os = "freebsd")] +sockopt_impl!( + /// Sets a specific timestamp format instead of the classic `SCM_TIMESTAMP`, + /// to follow up after `SO_TIMESTAMP` is set. + TsClock, + Both, + libc::SOL_SOCKET, + libc::SO_TS_CLOCK, + i32 +); #[cfg(any(target_os = "android", target_os = "linux"))] #[cfg(feature = "net")] sockopt_impl!( @@ -741,6 +756,46 @@ sockopt_impl!( libc::IP_BINDANY, bool ); +#[cfg(target_os = "freebsd")] +sockopt_impl!( + /// Set the route table (FIB) for this socket up to the `net.fibs` OID limit + /// (more specific than the setfib command line/call which are process based). + Fib, + SetOnly, + libc::SOL_SOCKET, + libc::SO_SETFIB, + i32 +); +#[cfg(target_os = "freebsd")] +sockopt_impl!( + /// Set `so_user_cookie` for this socket allowing network traffic based + /// upon it, similar to Linux's netfilter MARK. + UserCookie, + SetOnly, + libc::SOL_SOCKET, + libc::SO_USER_COOKIE, + u32 +); +#[cfg(target_os = "openbsd")] +sockopt_impl!( + /// Set the route table for this socket, needs a privileged user if + /// the process/socket had been set to the non default route. + Rtable, + SetOnly, + libc::SOL_SOCKET, + libc::SO_RTABLE, + i32 +); +#[cfg(any(target_os = "freebsd", target_os = "netbsd"))] +sockopt_impl!( + /// Get/set a filter on this socket before accepting connections similarly + /// to Linux's TCP_DEFER_ACCEPT but after the listen's call. + AcceptFilter, + Both, + libc::SOL_SOCKET, + libc::SO_ACCEPTFILTER, + libc::accept_filter_arg +); #[cfg(target_os = "linux")] sockopt_impl!( /// Set the mark for each packet sent through this socket (similar to the @@ -1008,10 +1063,10 @@ pub struct AlgSetAeadAuthSize; impl SetSockOpt for AlgSetAeadAuthSize { type Val = usize; - fn set(&self, fd: RawFd, val: &usize) -> Result<()> { + fn set(&self, fd: &F, val: &usize) -> Result<()> { unsafe { let res = libc::setsockopt( - fd, + fd.as_fd().as_raw_fd(), libc::SOL_ALG, libc::ALG_SET_AEAD_AUTHSIZE, ::std::ptr::null(), @@ -1042,10 +1097,10 @@ where { type Val = T; - fn set(&self, fd: RawFd, val: &T) -> Result<()> { + fn set(&self, fd: &F, val: &T) -> Result<()> { unsafe { let res = libc::setsockopt( - fd, + fd.as_fd().as_raw_fd(), libc::SOL_ALG, libc::ALG_SET_KEY, val.as_ref().as_ptr() as *const _, @@ -1358,8 +1413,8 @@ mod test { SockFlag::empty(), ) .unwrap(); - let a_cred = getsockopt(a, super::PeerCredentials).unwrap(); - let b_cred = getsockopt(b, super::PeerCredentials).unwrap(); + let a_cred = getsockopt(&a, super::PeerCredentials).unwrap(); + let b_cred = getsockopt(&b, super::PeerCredentials).unwrap(); assert_eq!(a_cred, b_cred); assert_ne!(a_cred.pid(), 0); } @@ -1367,25 +1422,21 @@ mod test { #[test] fn is_socket_type_unix() { use super::super::*; - use crate::unistd::close; - let (a, b) = socketpair( + let (a, _b) = socketpair( AddressFamily::Unix, SockType::Stream, None, SockFlag::empty(), ) .unwrap(); - let a_type = getsockopt(a, super::SockType).unwrap(); + let a_type = getsockopt(&a, super::SockType).unwrap(); assert_eq!(a_type, SockType::Stream); - close(a).unwrap(); - close(b).unwrap(); } #[test] fn is_socket_type_dgram() { use super::super::*; - use crate::unistd::close; let s = socket( AddressFamily::Inet, @@ -1394,16 +1445,14 @@ mod test { None, ) .unwrap(); - let s_type = getsockopt(s, super::SockType).unwrap(); + let s_type = getsockopt(&s, super::SockType).unwrap(); assert_eq!(s_type, SockType::Datagram); - close(s).unwrap(); } #[cfg(any(target_os = "freebsd", target_os = "linux"))] #[test] fn can_get_listen_on_tcp_socket() { use super::super::*; - use crate::unistd::close; let s = socket( AddressFamily::Inet, @@ -1412,11 +1461,10 @@ mod test { None, ) .unwrap(); - let s_listening = getsockopt(s, super::AcceptConn).unwrap(); + let s_listening = getsockopt(&s, super::AcceptConn).unwrap(); assert!(!s_listening); - listen(s, 10).unwrap(); - let s_listening2 = getsockopt(s, super::AcceptConn).unwrap(); + listen(&s, 10).unwrap(); + let s_listening2 = getsockopt(&s, super::AcceptConn).unwrap(); assert!(s_listening2); - close(s).unwrap(); } } diff --git a/src/sys/stat.rs b/src/sys/stat.rs index 78203bf..7e51c03 100644 --- a/src/sys/stat.rs +++ b/src/sys/stat.rs @@ -177,7 +177,7 @@ pub fn mknod( dev: dev_t, ) -> Result<()> { let res = path.with_nix_path(|cstr| unsafe { - libc::mknod(cstr.as_ptr(), kind.bits | perm.bits() as mode_t, dev) + libc::mknod(cstr.as_ptr(), kind.bits() | perm.bits() as mode_t, dev) })?; Errno::result(res).map(drop) @@ -202,7 +202,7 @@ pub fn mknodat( libc::mknodat( dirfd, cstr.as_ptr(), - kind.bits | perm.bits() as mode_t, + kind.bits() | perm.bits() as mode_t, dev, ) })?; diff --git a/src/sys/statfs.rs b/src/sys/statfs.rs index 9be8ca6..5111df2 100644 --- a/src/sys/statfs.rs +++ b/src/sys/statfs.rs @@ -5,7 +5,7 @@ use std::ffi::CStr; use std::fmt::{self, Debug}; use std::mem; -use std::os::unix::io::AsRawFd; +use std::os::unix::io::{AsFd, AsRawFd}; use cfg_if::cfg_if; @@ -740,10 +740,10 @@ pub fn statfs(path: &P) -> Result { /// # Arguments /// /// `fd` - File descriptor of any open file within the file system to describe -pub fn fstatfs(fd: &T) -> Result { +pub fn fstatfs(fd: Fd) -> Result { unsafe { let mut stat = mem::MaybeUninit::::uninit(); - Errno::result(LIBC_FSTATFS(fd.as_raw_fd(), stat.as_mut_ptr())) + Errno::result(LIBC_FSTATFS(fd.as_fd().as_raw_fd(), stat.as_mut_ptr())) .map(|_| Statfs(stat.assume_init())) } } diff --git a/src/sys/statvfs.rs b/src/sys/statvfs.rs index 8de369f..35424e5 100644 --- a/src/sys/statvfs.rs +++ b/src/sys/statvfs.rs @@ -3,7 +3,7 @@ //! See [the man pages](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fstatvfs.html) //! for more details. use std::mem; -use std::os::unix::io::AsRawFd; +use std::os::unix::io::{AsFd, AsRawFd}; use libc::{self, c_ulong}; @@ -12,7 +12,6 @@ use crate::{errno::Errno, NixPath, Result}; #[cfg(not(target_os = "redox"))] libc_bitflags!( /// File system mount Flags - #[repr(C)] #[derive(Default)] pub struct FsFlags: c_ulong { /// Read Only @@ -146,11 +145,11 @@ pub fn statvfs(path: &P) -> Result { } /// Return a `Statvfs` object with information about `fd` -pub fn fstatvfs(fd: &T) -> Result { +pub fn fstatvfs(fd: Fd) -> Result { unsafe { Errno::clear(); let mut stat = mem::MaybeUninit::::uninit(); - Errno::result(libc::fstatvfs(fd.as_raw_fd(), stat.as_mut_ptr())) + Errno::result(libc::fstatvfs(fd.as_fd().as_raw_fd(), stat.as_mut_ptr())) .map(|_| Statvfs(stat.assume_init())) } } diff --git a/src/sys/termios.rs b/src/sys/termios.rs index fba2cd8..ecaa3ea 100644 --- a/src/sys/termios.rs +++ b/src/sys/termios.rs @@ -222,7 +222,7 @@ use libc::{self, c_int, tcflag_t}; use std::cell::{Ref, RefCell}; use std::convert::From; use std::mem; -use std::os::unix::io::RawFd; +use std::os::unix::io::{AsFd, AsRawFd}; #[cfg(feature = "process")] use crate::unistd::Pid; @@ -309,7 +309,7 @@ impl Termios { let termios = *self.inner.borrow_mut(); self.input_flags = InputFlags::from_bits_truncate(termios.c_iflag); self.output_flags = OutputFlags::from_bits_truncate(termios.c_oflag); - self.control_flags = ControlFlags::from_bits_truncate(termios.c_cflag); + self.control_flags = ControlFlags::from_bits_retain(termios.c_cflag); self.local_flags = LocalFlags::from_bits_truncate(termios.c_lflag); self.control_chars = termios.c_cc; #[cfg(any( @@ -355,9 +355,9 @@ libc_enum! { /// enum. /// /// B0 is special and will disable the port. - #[cfg_attr(all(any(target_os = "haiku"), target_pointer_width = "64"), repr(u8))] + #[cfg_attr(target_os = "haiku", repr(u8))] #[cfg_attr(all(any(target_os = "ios", target_os = "macos"), target_pointer_width = "64"), repr(u64))] - #[cfg_attr(not(all(any(target_os = "ios", target_os = "macos", target_os = "haiku"), target_pointer_width = "64")), repr(u32))] + #[cfg_attr(all(not(all(any(target_os = "ios", target_os = "macos"), target_pointer_width = "64")), not(target_os = "haiku")), repr(u32))] #[non_exhaustive] pub enum BaudRate { B0, @@ -397,6 +397,8 @@ libc_enum! { #[cfg_attr(docsrs, doc(cfg(all())))] B28800, B38400, + #[cfg(not(target_os = "aix"))] + #[cfg_attr(docsrs, doc(cfg(all())))] B57600, #[cfg(any(target_os = "dragonfly", target_os = "freebsd", @@ -405,10 +407,14 @@ libc_enum! { target_os = "openbsd"))] #[cfg_attr(docsrs, doc(cfg(all())))] B76800, + #[cfg(not(target_os = "aix"))] + #[cfg_attr(docsrs, doc(cfg(all())))] B115200, #[cfg(any(target_os = "illumos", target_os = "solaris"))] #[cfg_attr(docsrs, doc(cfg(all())))] B153600, + #[cfg(not(target_os = "aix"))] + #[cfg_attr(docsrs, doc(cfg(all())))] B230400, #[cfg(any(target_os = "illumos", target_os = "solaris"))] #[cfg_attr(docsrs, doc(cfg(all())))] @@ -542,6 +548,8 @@ libc_enum! { #[repr(usize)] #[non_exhaustive] pub enum SpecialCharacterIndices { + #[cfg(not(target_os = "aix"))] + #[cfg_attr(docsrs, doc(cfg(all())))] VDISCARD, #[cfg(any(target_os = "dragonfly", target_os = "freebsd", @@ -549,6 +557,7 @@ libc_enum! { target_os = "macos", target_os = "netbsd", target_os = "openbsd", + target_os = "aix", target_os = "solaris"))] #[cfg_attr(docsrs, doc(cfg(all())))] VDSUSP, @@ -566,7 +575,7 @@ libc_enum! { VKILL, VLNEXT, #[cfg(not(any(all(target_os = "linux", target_arch = "sparc64"), - target_os = "illumos", target_os = "solaris")))] + target_os = "illumos", target_os = "solaris", target_os = "aix")))] #[cfg_attr(docsrs, doc(cfg(all())))] VMIN, VQUIT, @@ -590,9 +599,11 @@ libc_enum! { #[cfg_attr(docsrs, doc(cfg(all())))] VSWTCH, #[cfg(not(any(all(target_os = "linux", target_arch = "sparc64"), - target_os = "illumos", target_os = "solaris")))] + target_os = "illumos", target_os = "solaris", target_os = "aix")))] #[cfg_attr(docsrs, doc(cfg(all())))] VTIME, + #[cfg(not(target_os = "aix"))] + #[cfg_attr(docsrs, doc(cfg(all())))] VWERASE, #[cfg(target_os = "dragonfly")] #[cfg_attr(docsrs, doc(cfg(all())))] @@ -603,7 +614,8 @@ libc_enum! { #[cfg(any( all(target_os = "linux", target_arch = "sparc64"), target_os = "illumos", - target_os = "solaris" + target_os = "solaris", + target_os = "aix", ))] impl SpecialCharacterIndices { pub const VMIN: SpecialCharacterIndices = SpecialCharacterIndices::VEOF; @@ -616,6 +628,7 @@ pub use libc::NCCS; target_os = "dragonfly", target_os = "freebsd", target_os = "linux", + target_os = "aix", target_os = "macos", target_os = "netbsd", target_os = "openbsd" @@ -881,7 +894,7 @@ libc_bitflags! { PARODD; HUPCL; CLOCAL; - #[cfg(not(target_os = "redox"))] + #[cfg(not(any(target_os = "redox", target_os = "aix")))] #[cfg_attr(docsrs, doc(cfg(all())))] CRTSCTS; #[cfg(any(target_os = "android", target_os = "linux"))] @@ -967,7 +980,7 @@ libc_bitflags! { #[cfg_attr(docsrs, doc(cfg(all())))] ALTWERASE; IEXTEN; - #[cfg(not(any(target_os = "redox", target_os = "haiku")))] + #[cfg(not(any(target_os = "redox", target_os = "haiku", target_os = "aix")))] #[cfg_attr(docsrs, doc(cfg(all())))] EXTPROC; TOSTOP; @@ -1143,10 +1156,12 @@ pub fn cfmakesane(termios: &mut Termios) { /// `tcgetattr()` returns a `Termios` structure with the current configuration for a port. Modifying /// this structure *will not* reconfigure the port, instead the modifications should be done to /// the `Termios` structure and then the port should be reconfigured using `tcsetattr()`. -pub fn tcgetattr(fd: RawFd) -> Result { +pub fn tcgetattr(fd: Fd) -> Result { let mut termios = mem::MaybeUninit::uninit(); - let res = unsafe { libc::tcgetattr(fd, termios.as_mut_ptr()) }; + let res = unsafe { + libc::tcgetattr(fd.as_fd().as_raw_fd(), termios.as_mut_ptr()) + }; Errno::result(res)?; @@ -1159,18 +1174,26 @@ pub fn tcgetattr(fd: RawFd) -> Result { /// `tcsetattr()` reconfigures the given port based on a given `Termios` structure. This change /// takes affect at a time specified by `actions`. Note that this function may return success if /// *any* of the parameters were successfully set, not only if all were set successfully. -pub fn tcsetattr(fd: RawFd, actions: SetArg, termios: &Termios) -> Result<()> { +pub fn tcsetattr( + fd: Fd, + actions: SetArg, + termios: &Termios, +) -> Result<()> { let inner_termios = termios.get_libc_termios(); Errno::result(unsafe { - libc::tcsetattr(fd, actions as c_int, &*inner_termios) + libc::tcsetattr( + fd.as_fd().as_raw_fd(), + actions as c_int, + &*inner_termios, + ) }) .map(drop) } /// Block until all output data is written (see /// [tcdrain(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcdrain.html)). -pub fn tcdrain(fd: RawFd) -> Result<()> { - Errno::result(unsafe { libc::tcdrain(fd) }).map(drop) +pub fn tcdrain(fd: Fd) -> Result<()> { + Errno::result(unsafe { libc::tcdrain(fd.as_fd().as_raw_fd()) }).map(drop) } /// Suspend or resume the transmission or reception of data (see @@ -1178,8 +1201,11 @@ pub fn tcdrain(fd: RawFd) -> Result<()> { /// /// `tcflow()` suspends of resumes the transmission or reception of data for the given port /// depending on the value of `action`. -pub fn tcflow(fd: RawFd, action: FlowArg) -> Result<()> { - Errno::result(unsafe { libc::tcflow(fd, action as c_int) }).map(drop) +pub fn tcflow(fd: Fd, action: FlowArg) -> Result<()> { + Errno::result(unsafe { + libc::tcflow(fd.as_fd().as_raw_fd(), action as c_int) + }) + .map(drop) } /// Discard data in the output or input queue (see @@ -1187,8 +1213,11 @@ pub fn tcflow(fd: RawFd, action: FlowArg) -> Result<()> { /// /// `tcflush()` will discard data for a terminal port in the input queue, output queue, or both /// depending on the value of `action`. -pub fn tcflush(fd: RawFd, action: FlushArg) -> Result<()> { - Errno::result(unsafe { libc::tcflush(fd, action as c_int) }).map(drop) +pub fn tcflush(fd: Fd, action: FlushArg) -> Result<()> { + Errno::result(unsafe { + libc::tcflush(fd.as_fd().as_raw_fd(), action as c_int) + }) + .map(drop) } /// Send a break for a specific duration (see @@ -1196,16 +1225,19 @@ pub fn tcflush(fd: RawFd, action: FlushArg) -> Result<()> { /// /// When using asynchronous data transmission `tcsendbreak()` will transmit a continuous stream /// of zero-valued bits for an implementation-defined duration. -pub fn tcsendbreak(fd: RawFd, duration: c_int) -> Result<()> { - Errno::result(unsafe { libc::tcsendbreak(fd, duration) }).map(drop) +pub fn tcsendbreak(fd: Fd, duration: c_int) -> Result<()> { + Errno::result(unsafe { + libc::tcsendbreak(fd.as_fd().as_raw_fd(), duration) + }) + .map(drop) } feature! { #![feature = "process"] /// Get the session controlled by the given terminal (see /// [tcgetsid(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetsid.html)). -pub fn tcgetsid(fd: RawFd) -> Result { - let res = unsafe { libc::tcgetsid(fd) }; +pub fn tcgetsid(fd: Fd) -> Result { + let res = unsafe { libc::tcgetsid(fd.as_fd().as_raw_fd()) }; Errno::result(res).map(Pid::from_raw) } diff --git a/src/sys/time.rs b/src/sys/time.rs index 0042c45..a0160e2 100644 --- a/src/sys/time.rs +++ b/src/sys/time.rs @@ -91,8 +91,10 @@ pub(crate) mod timer { #[cfg(any(target_os = "android", target_os = "linux"))] bitflags! { /// Flags that are used for arming the timer. + #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct TimerSetTimeFlags: libc::c_int { const TFD_TIMER_ABSTIME = libc::TFD_TIMER_ABSTIME; + const TFD_TIMER_CANCEL_ON_SET = libc::TFD_TIMER_CANCEL_ON_SET; } } #[cfg(any( @@ -103,6 +105,7 @@ pub(crate) mod timer { ))] bitflags! { /// Flags that are used for arming the timer. + #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct TimerSetTimeFlags: libc::c_int { const TFD_TIMER_ABSTIME = libc::TIMER_ABSTIME; } @@ -261,8 +264,7 @@ impl TimeValLike for TimeSpec { fn seconds(seconds: i64) -> TimeSpec { assert!( (TS_MIN_SECONDS..=TS_MAX_SECONDS).contains(&seconds), - "TimeSpec out of bounds; seconds={}", - seconds + "TimeSpec out of bounds; seconds={seconds}", ); let mut ts = zero_init_timespec(); ts.tv_sec = seconds as time_t; @@ -428,20 +430,20 @@ impl fmt::Display for TimeSpec { let sec = abs.tv_sec(); - write!(f, "{}", sign)?; + write!(f, "{sign}")?; if abs.tv_nsec() == 0 { - if abs.tv_sec() == 1 { - write!(f, "{} second", sec)?; + if sec == 1 { + write!(f, "1 second")?; } else { - write!(f, "{} seconds", sec)?; + write!(f, "{sec} seconds")?; } } else if abs.tv_nsec() % 1_000_000 == 0 { - write!(f, "{}.{:03} seconds", sec, abs.tv_nsec() / 1_000_000)?; + write!(f, "{sec}.{:03} seconds", abs.tv_nsec() / 1_000_000)?; } else if abs.tv_nsec() % 1_000 == 0 { - write!(f, "{}.{:06} seconds", sec, abs.tv_nsec() / 1_000)?; + write!(f, "{sec}.{:06} seconds", abs.tv_nsec() / 1_000)?; } else { - write!(f, "{}.{:09} seconds", sec, abs.tv_nsec())?; + write!(f, "{sec}.{:09} seconds", abs.tv_nsec())?; } Ok(()) @@ -497,8 +499,7 @@ impl TimeValLike for TimeVal { fn seconds(seconds: i64) -> TimeVal { assert!( (TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&seconds), - "TimeVal out of bounds; seconds={}", - seconds + "TimeVal out of bounds; seconds={seconds}" ); #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848 @@ -662,18 +663,18 @@ impl fmt::Display for TimeVal { let sec = abs.tv_sec(); - write!(f, "{}", sign)?; + write!(f, "{sign}")?; if abs.tv_usec() == 0 { - if abs.tv_sec() == 1 { - write!(f, "{} second", sec)?; + if sec == 1 { + write!(f, "1 second")?; } else { - write!(f, "{} seconds", sec)?; + write!(f, "{sec} seconds")?; } } else if abs.tv_usec() % 1000 == 0 { - write!(f, "{}.{:03} seconds", sec, abs.tv_usec() / 1000)?; + write!(f, "{sec}.{:03} seconds", abs.tv_usec() / 1000)?; } else { - write!(f, "{}.{:06} seconds", sec, abs.tv_usec())?; + write!(f, "{sec}.{:06} seconds", abs.tv_usec())?; } Ok(()) diff --git a/src/sys/timerfd.rs b/src/sys/timerfd.rs index a35fc92..c4337c9 100644 --- a/src/sys/timerfd.rs +++ b/src/sys/timerfd.rs @@ -33,24 +33,28 @@ pub use crate::sys::time::timer::{Expiration, TimerSetTimeFlags}; use crate::unistd::read; use crate::{errno::Errno, Result}; use libc::c_int; -use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; +use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd}; /// A timerfd instance. This is also a file descriptor, you can feed it to -/// other interfaces consuming file descriptors, epoll for example. +/// other interfaces taking file descriptors as arguments, [`epoll`] for example. +/// +/// [`epoll`]: crate::sys::epoll #[derive(Debug)] pub struct TimerFd { - fd: RawFd, + fd: OwnedFd, } -impl AsRawFd for TimerFd { - fn as_raw_fd(&self) -> RawFd { - self.fd +impl AsFd for TimerFd { + fn as_fd(&self) -> BorrowedFd<'_> { + self.fd.as_fd() } } impl FromRawFd for TimerFd { unsafe fn from_raw_fd(fd: RawFd) -> Self { - TimerFd { fd } + TimerFd { + fd: OwnedFd::from_raw_fd(fd), + } } } @@ -97,7 +101,9 @@ impl TimerFd { Errno::result(unsafe { libc::timerfd_create(clockid as i32, flags.bits()) }) - .map(|fd| Self { fd }) + .map(|fd| Self { + fd: unsafe { OwnedFd::from_raw_fd(fd) }, + }) } /// Sets a new alarm on the timer. @@ -129,6 +135,13 @@ impl TimerFd { /// Then the one shot TimeSpec and the delay TimeSpec of the delayed /// interval are going to be interpreted as absolute. /// + /// # Cancel on a clock change + /// + /// If you set a `TFD_TIMER_CANCEL_ON_SET` alongside `TFD_TIMER_ABSTIME` + /// and the clock for this timer is `CLOCK_REALTIME` or `CLOCK_REALTIME_ALARM`, + /// then this timer is marked as cancelable if the real-time clock undergoes + /// a discontinuous change. + /// /// # Disabling alarms /// /// Note: Only one alarm can be set for any given timer. Setting a new alarm @@ -145,7 +158,7 @@ impl TimerFd { let timerspec: TimerSpec = expiration.into(); Errno::result(unsafe { libc::timerfd_settime( - self.fd, + self.fd.as_fd().as_raw_fd(), flags.bits(), timerspec.as_ref(), std::ptr::null_mut(), @@ -159,7 +172,10 @@ impl TimerFd { pub fn get(&self) -> Result> { let mut timerspec = TimerSpec::none(); Errno::result(unsafe { - libc::timerfd_gettime(self.fd, timerspec.as_mut()) + libc::timerfd_gettime( + self.fd.as_fd().as_raw_fd(), + timerspec.as_mut(), + ) }) .map(|_| { if timerspec.as_ref().it_interval.tv_sec == 0 @@ -179,7 +195,7 @@ impl TimerFd { pub fn unset(&self) -> Result<()> { Errno::result(unsafe { libc::timerfd_settime( - self.fd, + self.fd.as_fd().as_raw_fd(), TimerSetTimeFlags::empty().bits(), TimerSpec::none().as_ref(), std::ptr::null_mut(), @@ -192,7 +208,10 @@ impl TimerFd { /// /// Note: If the alarm is unset, then you will wait forever. pub fn wait(&self) -> Result<()> { - while let Err(e) = read(self.fd, &mut [0u8; 8]) { + while let Err(e) = read(self.fd.as_fd().as_raw_fd(), &mut [0u8; 8]) { + if e == Errno::ECANCELED { + break; + } if e != Errno::EINTR { return Err(e); } @@ -201,14 +220,3 @@ 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(Errno::EBADF) = result { - panic!("close of TimerFd encountered EBADF"); - } - } - } -} diff --git a/src/sys/uio.rs b/src/sys/uio.rs index b31c306..eaf61ed 100644 --- a/src/sys/uio.rs +++ b/src/sys/uio.rs @@ -4,13 +4,12 @@ use crate::errno::Errno; use crate::Result; use libc::{self, c_int, c_void, off_t, size_t}; use std::io::{IoSlice, IoSliceMut}; -use std::marker::PhantomData; -use std::os::unix::io::RawFd; +use std::os::unix::io::{AsFd, AsRawFd}; /// Low-level vectored write to a raw file descriptor /// /// See also [writev(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/writev.html) -pub fn writev(fd: RawFd, iov: &[IoSlice<'_>]) -> Result { +pub fn writev(fd: Fd, iov: &[IoSlice<'_>]) -> Result { // SAFETY: to quote the documentation for `IoSlice`: // // [IoSlice] is semantically a wrapper around a &[u8], but is @@ -19,7 +18,7 @@ pub fn writev(fd: RawFd, iov: &[IoSlice<'_>]) -> Result { // // Because it is ABI compatible, a pointer cast here is valid let res = unsafe { - libc::writev(fd, iov.as_ptr() as *const libc::iovec, iov.len() as c_int) + libc::writev(fd.as_fd().as_raw_fd(), iov.as_ptr() as *const libc::iovec, iov.len() as c_int) }; Errno::result(res).map(|r| r as usize) @@ -28,10 +27,13 @@ pub fn writev(fd: RawFd, iov: &[IoSlice<'_>]) -> Result { /// Low-level vectored read from a raw file descriptor /// /// See also [readv(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/readv.html) -pub fn readv(fd: RawFd, iov: &mut [IoSliceMut<'_>]) -> Result { +// Clippy doesn't know that we need to pass iov mutably only because the +// mutation happens after converting iov to a pointer +#[allow(clippy::needless_pass_by_ref_mut)] +pub fn readv(fd: Fd, iov: &mut [IoSliceMut<'_>]) -> Result { // SAFETY: same as in writev(), IoSliceMut is ABI-compatible with iovec let res = unsafe { - libc::readv(fd, iov.as_ptr() as *const libc::iovec, iov.len() as c_int) + libc::readv(fd.as_fd().as_raw_fd(), iov.as_ptr() as *const libc::iovec, iov.len() as c_int) }; Errno::result(res).map(|r| r as usize) @@ -45,14 +47,14 @@ pub fn readv(fd: RawFd, iov: &mut [IoSliceMut<'_>]) -> Result { /// See also: [`writev`](fn.writev.html) and [`pwrite`](fn.pwrite.html) #[cfg(not(any(target_os = "redox", target_os = "haiku")))] #[cfg_attr(docsrs, doc(cfg(all())))] -pub fn pwritev(fd: RawFd, iov: &[IoSlice<'_>], offset: off_t) -> Result { +pub fn pwritev(fd: Fd, iov: &[IoSlice<'_>], offset: off_t) -> Result { #[cfg(target_env = "uclibc")] let offset = offset as libc::off64_t; // uclibc doesn't use off_t // SAFETY: same as in writev() let res = unsafe { libc::pwritev( - fd, + fd.as_fd().as_raw_fd(), iov.as_ptr() as *const libc::iovec, iov.len() as c_int, offset, @@ -71,8 +73,11 @@ pub fn pwritev(fd: RawFd, iov: &[IoSlice<'_>], offset: off_t) -> Result { /// See also: [`readv`](fn.readv.html) and [`pread`](fn.pread.html) #[cfg(not(any(target_os = "redox", target_os = "haiku")))] #[cfg_attr(docsrs, doc(cfg(all())))] -pub fn preadv( - fd: RawFd, +// Clippy doesn't know that we need to pass iov mutably only because the +// mutation happens after converting iov to a pointer +#[allow(clippy::needless_pass_by_ref_mut)] +pub fn preadv( + fd: Fd, iov: &mut [IoSliceMut<'_>], offset: off_t, ) -> Result { @@ -82,7 +87,7 @@ pub fn preadv( // SAFETY: same as in readv() let res = unsafe { libc::preadv( - fd, + fd.as_fd().as_raw_fd(), iov.as_ptr() as *const libc::iovec, iov.len() as c_int, offset, @@ -96,10 +101,10 @@ pub fn preadv( /// /// See also [pwrite(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pwrite.html) // TODO: move to unistd -pub fn pwrite(fd: RawFd, buf: &[u8], offset: off_t) -> Result { +pub fn pwrite(fd: Fd, buf: &[u8], offset: off_t) -> Result { let res = unsafe { libc::pwrite( - fd, + fd.as_fd().as_raw_fd(), buf.as_ptr() as *const c_void, buf.len() as size_t, offset, @@ -113,10 +118,10 @@ pub fn pwrite(fd: RawFd, buf: &[u8], offset: off_t) -> Result { /// /// See also [pread(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pread.html) // TODO: move to unistd -pub fn pread(fd: RawFd, buf: &mut [u8], offset: off_t) -> Result { +pub fn pread(fd: Fd, buf: &mut [u8], offset: off_t) -> Result { let res = unsafe { libc::pread( - fd, + fd.as_fd().as_raw_fd(), buf.as_mut_ptr() as *mut c_void, buf.len() as size_t, offset, @@ -145,77 +150,6 @@ pub struct RemoteIoVec { pub len: usize, } -/// A vector of buffers. -/// -/// Vectored I/O methods like [`writev`] and [`readv`] use this structure for -/// both reading and writing. Each `IoVec` specifies the base address and -/// length of an area in memory. -#[deprecated( - since = "0.24.0", - note = "`IoVec` is no longer used in the public interface, use `IoSlice` or `IoSliceMut` instead" -)] -#[repr(transparent)] -#[allow(renamed_and_removed_lints)] -#[allow(clippy::unknown_clippy_lints)] -// Clippy false positive: https://github.com/rust-lang/rust-clippy/issues/8867 -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub struct IoVec(pub(crate) libc::iovec, PhantomData); - -#[allow(deprecated)] -impl IoVec { - /// View the `IoVec` as a Rust slice. - #[deprecated( - since = "0.24.0", - note = "Use the `Deref` impl of `IoSlice` or `IoSliceMut` instead" - )] - #[inline] - pub fn as_slice(&self) -> &[u8] { - use std::slice; - - unsafe { - slice::from_raw_parts(self.0.iov_base as *const u8, self.0.iov_len) - } - } -} - -#[allow(deprecated)] -impl<'a> IoVec<&'a [u8]> { - /// Create an `IoVec` from a Rust slice. - #[deprecated(since = "0.24.0", note = "Use `IoSlice::new` instead")] - pub fn from_slice(buf: &'a [u8]) -> IoVec<&'a [u8]> { - IoVec( - libc::iovec { - iov_base: buf.as_ptr() as *mut c_void, - iov_len: buf.len() as size_t, - }, - PhantomData, - ) - } -} - -#[allow(deprecated)] -impl<'a> IoVec<&'a mut [u8]> { - /// Create an `IoVec` from a mutable Rust slice. - #[deprecated(since = "0.24.0", note = "Use `IoSliceMut::new` instead")] - pub fn from_mut_slice(buf: &'a mut [u8]) -> IoVec<&'a mut [u8]> { - IoVec( - libc::iovec { - iov_base: buf.as_ptr() as *mut c_void, - iov_len: buf.len() as size_t, - }, - PhantomData, - ) - } -} - -// The only reason IoVec isn't automatically Send+Sync is because libc::iovec -// contains raw pointers. -#[allow(deprecated)] -unsafe impl Send for IoVec where T: Send {} -#[allow(deprecated)] -unsafe impl Sync for IoVec where T: Sync {} - feature! { #![feature = "process"] diff --git a/src/sys/wait.rs b/src/sys/wait.rs index b6524e8..f7a63ff 100644 --- a/src/sys/wait.rs +++ b/src/sys/wait.rs @@ -10,7 +10,7 @@ use std::convert::TryFrom; target_os = "android", all(target_os = "linux", not(target_env = "uclibc")), ))] -use std::os::unix::io::RawFd; +use std::os::unix::io::{AsRawFd, BorrowedFd}; libc_bitflags!( /// Controls the behavior of [`waitpid`]. @@ -343,8 +343,8 @@ pub fn wait() -> Result { target_os = "haiku", all(target_os = "linux", not(target_env = "uclibc")), ))] -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum Id { +#[derive(Debug)] +pub enum Id<'fd> { /// Wait for any child All, /// Wait for the child whose process ID matches the given PID @@ -355,7 +355,11 @@ pub enum Id { PGid(Pid), /// Wait for the child referred to by the given PID file descriptor #[cfg(any(target_os = "android", target_os = "linux"))] - PIDFd(RawFd), + PIDFd(BorrowedFd<'fd>), + /// A helper variant to resolve the unused parameter (`'fd`) problem on platforms + /// other than Linux and Android. + #[doc(hidden)] + _Unreachable(std::marker::PhantomData<&'fd std::convert::Infallible>), } /// Wait for a process to change status @@ -373,7 +377,8 @@ pub fn waitid(id: Id, flags: WaitPidFlag) -> Result { Id::Pid(pid) => (libc::P_PID, pid.as_raw() as libc::id_t), Id::PGid(pid) => (libc::P_PGID, pid.as_raw() as libc::id_t), #[cfg(any(target_os = "android", target_os = "linux"))] - Id::PIDFd(fd) => (libc::P_PIDFD, fd as libc::id_t), + Id::PIDFd(fd) => (libc::P_PIDFD, fd.as_raw_fd() as libc::id_t), + Id::_Unreachable(_) => unreachable!("This variant could never be constructed"), }; let siginfo = unsafe { diff --git a/src/unistd.rs b/src/unistd.rs index ca07b34..bb9f1c1 100644 --- a/src/unistd.rs +++ b/src/unistd.rs @@ -35,6 +35,7 @@ use std::ffi::{CString, OsStr}; use std::os::unix::ffi::OsStrExt; use std::os::unix::ffi::OsStringExt; use std::os::unix::io::RawFd; +use std::os::unix::io::{AsFd, AsRawFd}; use std::path::PathBuf; use std::{fmt, mem, ptr}; @@ -217,7 +218,6 @@ impl fmt::Display for Pid { } } - /// Represents the successful result of calling `fork` /// /// When `fork` is called, the process continues execution in the parent process @@ -230,7 +230,6 @@ pub enum ForkResult { } impl ForkResult { - /// Return `true` if this is the child process of the `fork()` #[inline] pub fn is_child(self) -> bool { @@ -478,9 +477,8 @@ fn dup3_polyfill(oldfd: RawFd, newfd: RawFd, flags: OFlag) -> Result { /// pages for additional details on possible failure cases. #[inline] pub fn chdir(path: &P) -> Result<()> { - let res = path.with_nix_path(|cstr| { - unsafe { libc::chdir(cstr.as_ptr()) } - })?; + let res = + path.with_nix_path(|cstr| unsafe { libc::chdir(cstr.as_ptr()) })?; Errno::result(res).map(drop) } @@ -527,8 +525,8 @@ pub fn fchdir(dirfd: RawFd) -> Result<()> { /// ``` #[inline] pub fn mkdir(path: &P, mode: Mode) -> Result<()> { - let res = path.with_nix_path(|cstr| { - unsafe { libc::mkdir(cstr.as_ptr(), mode.bits() as mode_t) } + let res = path.with_nix_path(|cstr| unsafe { + libc::mkdir(cstr.as_ptr(), mode.bits() as mode_t) })?; Errno::result(res).map(drop) @@ -566,8 +564,8 @@ pub fn mkdir(path: &P, mode: Mode) -> Result<()> { #[inline] #[cfg(not(target_os = "redox"))] // RedoxFS does not support fifo yet pub fn mkfifo(path: &P, mode: Mode) -> Result<()> { - let res = path.with_nix_path(|cstr| { - unsafe { libc::mkfifo(cstr.as_ptr(), mode.bits() as mode_t) } + let res = path.with_nix_path(|cstr| unsafe { + libc::mkfifo(cstr.as_ptr(), mode.bits() as mode_t) })?; Errno::result(res).map(drop) @@ -585,9 +583,17 @@ pub fn mkfifo(path: &P, mode: Mode) -> Result<()> { // mkfifoat is not implemented in OSX or android #[inline] #[cfg(not(any( - target_os = "macos", target_os = "ios", target_os = "haiku", - target_os = "android", target_os = "redox")))] -pub fn mkfifoat(dirfd: Option, path: &P, mode: Mode) -> Result<()> { + target_os = "macos", + target_os = "ios", + target_os = "haiku", + target_os = "android", + target_os = "redox" +)))] +pub fn mkfifoat( + dirfd: Option, + path: &P, + mode: Mode, +) -> Result<()> { let res = path.with_nix_path(|cstr| unsafe { libc::mkfifoat(at_rawfd(dirfd), cstr.as_ptr(), mode.bits() as mode_t) })?; @@ -608,19 +614,17 @@ pub fn mkfifoat(dirfd: Option, path: &P, mode: Mode) pub fn symlinkat( path1: &P1, dirfd: Option, - path2: &P2) -> Result<()> { - let res = - path1.with_nix_path(|path1| { - path2.with_nix_path(|path2| { - unsafe { - libc::symlinkat( - path1.as_ptr(), - dirfd.unwrap_or(libc::AT_FDCWD), - path2.as_ptr() - ) - } - }) - })??; + path2: &P2, +) -> Result<()> { + let res = path1.with_nix_path(|path1| { + path2.with_nix_path(|path2| unsafe { + libc::symlinkat( + path1.as_ptr(), + dirfd.unwrap_or(libc::AT_FDCWD), + path2.as_ptr(), + ) + }) + })??; Errno::result(res).map(drop) } } @@ -670,7 +674,9 @@ pub fn getcwd() -> Result { // To safely handle this we start with a reasonable size (512 bytes) // and double the buffer size upon every error if !libc::getcwd(ptr, buf.capacity()).is_null() { - let len = CStr::from_ptr(buf.as_ptr() as *const c_char).to_bytes().len(); + let len = CStr::from_ptr(buf.as_ptr() as *const c_char) + .to_bytes() + .len(); buf.set_len(len); buf.shrink_to_fit(); return Ok(PathBuf::from(OsString::from_vec(buf))); @@ -680,7 +686,7 @@ pub fn getcwd() -> Result { if error != Errno::ERANGE { return Err(error); } - } + } // Trigger the internal buffer resizing logic. reserve_double_buffer_size(&mut buf, PATH_MAX as usize)?; @@ -695,13 +701,15 @@ feature! { /// Computes the raw UID and GID values to pass to a `*chown` call. // The cast is not unnecessary on all platforms. #[allow(clippy::unnecessary_cast)] -fn chown_raw_ids(owner: Option, group: Option) -> (libc::uid_t, libc::gid_t) { +fn chown_raw_ids(owner: Option, group: Option) -> (uid_t, gid_t) { // According to the POSIX specification, -1 is used to indicate that owner and group // are not to be changed. Since uid_t and gid_t are unsigned types, we have to wrap // around to get -1. - let uid = owner.map(Into::into) + let uid = owner + .map(Into::into) .unwrap_or_else(|| (0 as uid_t).wrapping_sub(1)); - let gid = group.map(Into::into) + let gid = group + .map(Into::into) .unwrap_or_else(|| (0 as gid_t).wrapping_sub(1)); (uid, gid) } @@ -714,7 +722,11 @@ fn chown_raw_ids(owner: Option, group: Option) -> (libc::uid_t, libc:: /// provided for that argument. Ownership change will be attempted for the path /// only if `Some` owner/group is provided. #[inline] -pub fn chown(path: &P, owner: Option, group: Option) -> Result<()> { +pub fn chown( + path: &P, + owner: Option, + group: Option, +) -> Result<()> { let res = path.with_nix_path(|cstr| { let (uid, gid) = chown_raw_ids(owner, group); unsafe { libc::chown(cstr.as_ptr(), uid, gid) } @@ -773,15 +785,19 @@ pub fn fchownat( group: Option, flag: FchownatFlags, ) -> Result<()> { - let atflag = - match flag { - FchownatFlags::FollowSymlink => AtFlags::empty(), - FchownatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW, - }; + let atflag = match flag { + FchownatFlags::FollowSymlink => AtFlags::empty(), + FchownatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW, + }; let res = path.with_nix_path(|cstr| unsafe { let (uid, gid) = chown_raw_ids(owner, group); - libc::fchownat(at_rawfd(dirfd), cstr.as_ptr(), uid, gid, - atflag.bits() as libc::c_int) + libc::fchownat( + at_rawfd(dirfd), + cstr.as_ptr(), + uid, + gid, + atflag.bits() as libc::c_int, + ) })?; Errno::result(res).map(drop) @@ -808,14 +824,11 @@ fn to_exec_array>(args: &[S]) -> Vec<*const c_char> { pub fn execv>(path: &CStr, argv: &[S]) -> Result { let args_p = to_exec_array(argv); - unsafe { - libc::execv(path.as_ptr(), args_p.as_ptr()) - }; + unsafe { libc::execv(path.as_ptr(), args_p.as_ptr()) }; Err(Errno::last()) } - /// Replace the current process image with a new one (see /// [execve(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html)). /// @@ -829,13 +842,15 @@ pub fn execv>(path: &CStr, argv: &[S]) -> Result { /// in the `args` list is an argument to the new process. Each element in the /// `env` list should be a string in the form "key=value". #[inline] -pub fn execve, SE: AsRef>(path: &CStr, args: &[SA], env: &[SE]) -> Result { +pub fn execve, SE: AsRef>( + path: &CStr, + args: &[SA], + env: &[SE], +) -> Result { let args_p = to_exec_array(args); let env_p = to_exec_array(env); - unsafe { - libc::execve(path.as_ptr(), args_p.as_ptr(), env_p.as_ptr()) - }; + unsafe { libc::execve(path.as_ptr(), args_p.as_ptr(), env_p.as_ptr()) }; Err(Errno::last()) } @@ -850,12 +865,13 @@ pub fn execve, SE: AsRef>(path: &CStr, args: &[SA], env: & /// would not work if "bash" was specified for the path argument, but `execvp` /// would assuming that a bash executable was on the system `PATH`. #[inline] -pub fn execvp>(filename: &CStr, args: &[S]) -> Result { +pub fn execvp>( + filename: &CStr, + args: &[S], +) -> Result { let args_p = to_exec_array(args); - unsafe { - libc::execvp(filename.as_ptr(), args_p.as_ptr()) - }; + unsafe { libc::execvp(filename.as_ptr(), args_p.as_ptr()) }; Err(Errno::last()) } @@ -867,10 +883,12 @@ pub fn execvp>(filename: &CStr, args: &[S]) -> Result /// This functions like a combination of `execvp(2)` and `execve(2)` to pass an /// environment and have a search path. See these two for additional /// information. -#[cfg(any(target_os = "haiku", - target_os = "linux", - target_os = "openbsd"))] -pub fn execvpe, SE: AsRef>(filename: &CStr, args: &[SA], env: &[SE]) -> Result { +#[cfg(any(target_os = "haiku", target_os = "linux", target_os = "openbsd"))] +pub fn execvpe, SE: AsRef>( + filename: &CStr, + args: &[SA], + env: &[SE], +) -> Result { let args_p = to_exec_array(args); let env_p = to_exec_array(env); @@ -891,18 +909,22 @@ pub fn execvpe, SE: AsRef>(filename: &CStr, args: &[SA], e /// /// This function is similar to `execve`, except that the program to be executed /// is referenced as a file descriptor instead of a path. -#[cfg(any(target_os = "android", - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd"))] +#[cfg(any( + target_os = "android", + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd" +))] #[inline] -pub fn fexecve ,SE: AsRef>(fd: RawFd, args: &[SA], env: &[SE]) -> Result { +pub fn fexecve, SE: AsRef>( + fd: RawFd, + args: &[SA], + env: &[SE], +) -> Result { let args_p = to_exec_array(args); let env_p = to_exec_array(env); - unsafe { - libc::fexecve(fd, args_p.as_ptr(), env_p.as_ptr()) - }; + unsafe { libc::fexecve(fd, args_p.as_ptr(), env_p.as_ptr()) }; Err(Errno::last()) } @@ -919,14 +941,25 @@ pub fn fexecve ,SE: AsRef>(fd: RawFd, args: &[SA], env: &[ /// is referenced as a file descriptor to the base directory plus a path. #[cfg(any(target_os = "android", target_os = "linux"))] #[inline] -pub fn execveat,SE: AsRef>(dirfd: RawFd, pathname: &CStr, args: &[SA], - env: &[SE], flags: super::fcntl::AtFlags) -> Result { +pub fn execveat, SE: AsRef>( + dirfd: RawFd, + pathname: &CStr, + args: &[SA], + env: &[SE], + flags: super::fcntl::AtFlags, +) -> Result { let args_p = to_exec_array(args); let env_p = to_exec_array(env); unsafe { - libc::syscall(libc::SYS_execveat, dirfd, pathname.as_ptr(), - args_p.as_ptr(), env_p.as_ptr(), flags); + libc::syscall( + libc::SYS_execveat, + dirfd, + pathname.as_ptr(), + args_p.as_ptr(), + env_p.as_ptr(), + flags, + ); }; Err(Errno::last()) @@ -957,14 +990,16 @@ pub fn execveat,SE: AsRef>(dirfd: RawFd, pathname: &CStr, /// descriptors will remain identical after daemonizing. /// * `noclose = false`: The process' stdin, stdout, and stderr will point to /// `/dev/null` after daemonizing. -#[cfg(any(target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "illumos", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "solaris"))] +#[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris" +))] pub fn daemon(nochdir: bool, noclose: bool) -> Result<()> { let res = unsafe { libc::daemon(nochdir as c_int, noclose as c_int) }; Errno::result(res).map(drop) @@ -990,6 +1025,7 @@ pub fn sethostname>(name: S) -> Result<()> { target_os = "illumos", target_os = "ios", target_os = "macos", + target_os = "aix", target_os = "solaris", ))] { type sethostname_len_t = c_int; } else { @@ -1106,23 +1142,27 @@ pub enum Whence { /// Specify an offset relative to the next location in the file greater than or /// equal to offset that contains some data. If offset points to /// some data, then the file offset is set to offset. - #[cfg(any(target_os = "dragonfly", - target_os = "freebsd", - target_os = "illumos", - target_os = "linux", - target_os = "solaris"))] + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "linux", + target_os = "solaris" + ))] SeekData = libc::SEEK_DATA, /// Specify an offset relative to the next hole in the file greater than /// or equal to offset. If offset points into the middle of a hole, then /// the file offset should be set to offset. If there is no hole past offset, /// then the file offset should be adjusted to the end of the file (i.e., there /// is an implicit hole at the end of any file). - #[cfg(any(target_os = "dragonfly", - target_os = "freebsd", - target_os = "illumos", - target_os = "linux", - target_os = "solaris"))] - SeekHole = libc::SEEK_HOLE + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "linux", + target_os = "solaris" + ))] + SeekHole = libc::SEEK_HOLE, } /// Move the read/write file offset. @@ -1135,7 +1175,11 @@ pub fn lseek(fd: RawFd, offset: off_t, whence: Whence) -> Result { } #[cfg(any(target_os = "linux", target_os = "android"))] -pub fn lseek64(fd: RawFd, offset: libc::off64_t, whence: Whence) -> Result { +pub fn lseek64( + fd: RawFd, + offset: libc::off64_t, + whence: Whence, +) -> Result { let res = unsafe { libc::lseek64(fd, offset, whence as i32) }; Errno::result(res).map(|r| r as libc::off64_t) @@ -1163,27 +1207,34 @@ feature! { /// created: /// /// - `O_CLOEXEC`: Set the close-on-exec flag for the new file descriptors. -#[cfg_attr(target_os = "linux", doc = "- `O_DIRECT`: Create a pipe that performs I/O in \"packet\" mode.")] -#[cfg_attr(target_os = "netbsd", doc = "- `O_NOSIGPIPE`: Return `EPIPE` instead of raising `SIGPIPE`.")] +#[cfg_attr( + target_os = "linux", + doc = "- `O_DIRECT`: Create a pipe that performs I/O in \"packet\" mode." +)] +#[cfg_attr( + target_os = "netbsd", + doc = "- `O_NOSIGPIPE`: Return `EPIPE` instead of raising `SIGPIPE`." +)] /// - `O_NONBLOCK`: Set the non-blocking flag for the ends of the pipe. /// /// See also [pipe(2)](https://man7.org/linux/man-pages/man2/pipe.2.html) -#[cfg(any(target_os = "android", - target_os = "dragonfly", - target_os = "emscripten", - target_os = "freebsd", - target_os = "illumos", - target_os = "linux", - target_os = "redox", - target_os = "netbsd", - target_os = "openbsd", - target_os = "solaris"))] +#[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "emscripten", + target_os = "freebsd", + target_os = "illumos", + target_os = "linux", + target_os = "redox", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris" +))] pub fn pipe2(flags: OFlag) -> Result<(RawFd, RawFd)> { let mut fds = mem::MaybeUninit::<[c_int; 2]>::uninit(); - let res = unsafe { - libc::pipe2(fds.as_mut_ptr() as *mut c_int, flags.bits()) - }; + let res = + unsafe { libc::pipe2(fds.as_mut_ptr() as *mut c_int, flags.bits()) }; Errno::result(res)?; @@ -1196,11 +1247,8 @@ pub fn pipe2(flags: OFlag) -> Result<(RawFd, RawFd)> { /// [truncate(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/truncate.html) #[cfg(not(any(target_os = "redox", target_os = "fuchsia")))] pub fn truncate(path: &P, len: off_t) -> Result<()> { - let res = path.with_nix_path(|cstr| { - unsafe { - libc::truncate(cstr.as_ptr(), len) - } - })?; + let res = path + .with_nix_path(|cstr| unsafe { libc::truncate(cstr.as_ptr(), len) })?; Errno::result(res).map(drop) } @@ -1209,8 +1257,8 @@ pub fn truncate(path: &P, len: off_t) -> Result<()> { /// /// See also /// [ftruncate(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html) -pub fn ftruncate(fd: RawFd, len: off_t) -> Result<()> { - Errno::result(unsafe { libc::ftruncate(fd, len) }).map(drop) +pub fn ftruncate(fd: Fd, len: off_t) -> Result<()> { + Errno::result(unsafe { libc::ftruncate(fd.as_fd().as_raw_fd(), len) }).map(drop) } pub fn isatty(fd: RawFd) -> Result { @@ -1224,7 +1272,7 @@ pub fn isatty(fd: RawFd) -> Result { Errno::ENOTTY => Ok(false), err => Err(err), } - } + } } } @@ -1256,40 +1304,31 @@ pub fn linkat( newpath: &P, flag: LinkatFlags, ) -> Result<()> { + let atflag = match flag { + LinkatFlags::SymlinkFollow => AtFlags::AT_SYMLINK_FOLLOW, + LinkatFlags::NoSymlinkFollow => AtFlags::empty(), + }; - let atflag = - match flag { - LinkatFlags::SymlinkFollow => AtFlags::AT_SYMLINK_FOLLOW, - LinkatFlags::NoSymlinkFollow => AtFlags::empty(), - }; - - let res = - oldpath.with_nix_path(|oldcstr| { - newpath.with_nix_path(|newcstr| { - unsafe { - libc::linkat( - at_rawfd(olddirfd), - oldcstr.as_ptr(), - at_rawfd(newdirfd), - newcstr.as_ptr(), - atflag.bits() as libc::c_int - ) - } - }) - })??; + let res = oldpath.with_nix_path(|oldcstr| { + newpath.with_nix_path(|newcstr| unsafe { + libc::linkat( + at_rawfd(olddirfd), + oldcstr.as_ptr(), + at_rawfd(newdirfd), + newcstr.as_ptr(), + atflag.bits() as libc::c_int, + ) + }) + })??; Errno::result(res).map(drop) } - /// Remove a directory entry /// /// See also [unlink(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlink.html) pub fn unlink(path: &P) -> Result<()> { - let res = path.with_nix_path(|cstr| { - unsafe { - libc::unlink(cstr.as_ptr()) - } - })?; + let res = + path.with_nix_path(|cstr| unsafe { libc::unlink(cstr.as_ptr()) })?; Errno::result(res).map(drop) } @@ -1316,26 +1355,25 @@ pub fn unlinkat( path: &P, flag: UnlinkatFlags, ) -> Result<()> { - let atflag = - match flag { - UnlinkatFlags::RemoveDir => AtFlags::AT_REMOVEDIR, - UnlinkatFlags::NoRemoveDir => AtFlags::empty(), - }; - let res = path.with_nix_path(|cstr| { - unsafe { - libc::unlinkat(at_rawfd(dirfd), cstr.as_ptr(), atflag.bits() as libc::c_int) - } + let atflag = match flag { + UnlinkatFlags::RemoveDir => AtFlags::AT_REMOVEDIR, + UnlinkatFlags::NoRemoveDir => AtFlags::empty(), + }; + let res = path.with_nix_path(|cstr| unsafe { + libc::unlinkat( + at_rawfd(dirfd), + cstr.as_ptr(), + atflag.bits() as libc::c_int, + ) })?; Errno::result(res).map(drop) } - #[inline] #[cfg(not(target_os = "fuchsia"))] pub fn chroot(path: &P) -> Result<()> { - let res = path.with_nix_path(|cstr| { - unsafe { libc::chroot(cstr.as_ptr()) } - })?; + let res = + path.with_nix_path(|cstr| unsafe { libc::chroot(cstr.as_ptr()) })?; Errno::result(res).map(drop) } @@ -1379,15 +1417,17 @@ pub fn fsync(fd: RawFd) -> Result<()> { /// /// See also /// [fdatasync(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fdatasync.html) -#[cfg(any(target_os = "linux", - target_os = "android", - target_os = "emscripten", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "netbsd", - target_os = "openbsd", - target_os = "illumos", - target_os = "solaris"))] +#[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "emscripten", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "netbsd", + target_os = "openbsd", + target_os = "illumos", + target_os = "solaris" +))] #[inline] pub fn fdatasync(fd: RawFd) -> Result<()> { let res = unsafe { libc::fdatasync(fd) }; @@ -1539,27 +1579,31 @@ pub fn getgroups() -> Result> { // Now actually get the groups. We try multiple times in case the number of // groups has changed since the first call to getgroups() and the buffer is // now too small. - let mut groups = Vec::::with_capacity(Errno::result(ngroups)? as usize); + let mut groups = + Vec::::with_capacity(Errno::result(ngroups)? as usize); loop { // FIXME: On the platforms we currently support, the `Gid` struct has // the same representation in memory as a bare `gid_t`. This is not // necessarily the case on all Rust platforms, though. See RFC 1785. let ngroups = unsafe { - libc::getgroups(groups.capacity() as c_int, groups.as_mut_ptr() as *mut gid_t) + libc::getgroups( + groups.capacity() as c_int, + groups.as_mut_ptr() as *mut gid_t, + ) }; match Errno::result(ngroups) { Ok(s) => { unsafe { groups.set_len(s as usize) }; return Ok(groups); - }, + } Err(Errno::EINVAL) => { // EINVAL indicates that the buffer size was too // small, resize it up to ngroups_max as limit. reserve_double_buffer_size(&mut groups, ngroups_max) .or(Err(Errno::EINVAL))?; - }, - Err(e) => return Err(e) + } + Err(e) => return Err(e), } } } @@ -1595,17 +1639,23 @@ pub fn getgroups() -> Result> { /// # /// # try_main().unwrap(); /// ``` -#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox", target_os = "haiku")))] +#[cfg(not(any( + target_os = "ios", + target_os = "macos", + target_os = "redox", + target_os = "haiku" +)))] pub fn setgroups(groups: &[Gid]) -> Result<()> { cfg_if! { - if #[cfg(any(target_os = "dragonfly", + if #[cfg(any(target_os = "aix", + target_os = "dragonfly", target_os = "freebsd", target_os = "illumos", target_os = "ios", target_os = "macos", target_os = "netbsd", - target_os = "illumos", - target_os = "openbsd"))] { + target_os = "openbsd", + target_os = "solaris"))] { type setgroups_ngroups_t = c_int; } else { type setgroups_ngroups_t = size_t; @@ -1615,7 +1665,10 @@ pub fn setgroups(groups: &[Gid]) -> Result<()> { // same representation in memory as a bare `gid_t`. This is not necessarily // the case on all Rust platforms, though. See RFC 1785. let res = unsafe { - libc::setgroups(groups.len() as setgroups_ngroups_t, groups.as_ptr() as *const gid_t) + libc::setgroups( + groups.len() as setgroups_ngroups_t, + groups.as_ptr() as *const gid_t, + ) }; Errno::result(res).map(drop) @@ -1641,10 +1694,13 @@ pub fn setgroups(groups: &[Gid]) -> Result<()> { /// and `setgroups()`. Additionally, while some implementations will return a /// partial list of groups when `NGROUPS_MAX` is exceeded, this implementation /// will only ever return the complete list or else an error. -#[cfg(not(any(target_os = "illumos", - target_os = "ios", - target_os = "macos", - target_os = "redox")))] +#[cfg(not(any( + target_os = "aix", + target_os = "illumos", + target_os = "ios", + target_os = "macos", + target_os = "redox" +)))] pub fn getgrouplist(user: &CStr, group: Gid) -> Result> { let ngroups_max = match sysconf(SysconfVar::NGROUPS_MAX) { Ok(Some(n)) => n as c_int, @@ -1663,10 +1719,12 @@ pub fn getgrouplist(user: &CStr, group: Gid) -> Result> { loop { let mut ngroups = groups.capacity() as i32; let ret = unsafe { - libc::getgrouplist(user.as_ptr(), - gid as getgrouplist_group_t, - groups.as_mut_ptr() as *mut getgrouplist_group_t, - &mut ngroups) + libc::getgrouplist( + user.as_ptr(), + gid as getgrouplist_group_t, + groups.as_mut_ptr() as *mut getgrouplist_group_t, + &mut ngroups, + ) }; // BSD systems only return 0 or -1, Linux returns ngroups on success. @@ -1722,7 +1780,12 @@ pub fn getgrouplist(user: &CStr, group: Gid) -> Result> { /// # /// # try_main().unwrap(); /// ``` -#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox", target_os = "haiku")))] +#[cfg(not(any( + target_os = "ios", + target_os = "macos", + target_os = "redox", + target_os = "haiku" +)))] pub fn initgroups(user: &CStr, group: Gid) -> Result<()> { cfg_if! { if #[cfg(any(target_os = "ios", target_os = "macos"))] { @@ -1732,7 +1795,8 @@ pub fn initgroups(user: &CStr, group: Gid) -> Result<()> { } } let gid: gid_t = group.into(); - let res = unsafe { libc::initgroups(user.as_ptr(), gid as initgroups_group_t) }; + let res = + unsafe { libc::initgroups(user.as_ptr(), gid as initgroups_group_t) }; Errno::result(res).map(drop) } @@ -1776,8 +1840,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")] + #![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}; @@ -1853,17 +1917,16 @@ feature! { #[cfg(not(any(target_os = "redox", target_os = "haiku")))] pub mod acct { - use crate::{Result, NixPath}; use crate::errno::Errno; + use crate::{NixPath, Result}; use std::ptr; /// Enable process accounting /// /// See also [acct(2)](https://linux.die.net/man/2/acct) pub fn enable(filename: &P) -> Result<()> { - let res = filename.with_nix_path(|cstr| { - unsafe { libc::acct(cstr.as_ptr()) } - })?; + let res = filename + .with_nix_path(|cstr| unsafe { libc::acct(cstr.as_ptr()) })?; Errno::result(res).map(drop) } @@ -1905,7 +1968,8 @@ feature! { /// ``` #[inline] pub fn mkstemp(template: &P) -> Result<(RawFd, PathBuf)> { - let mut path = template.with_nix_path(|path| {path.to_bytes_with_nul().to_owned()})?; + let mut path = + template.with_nix_path(|path| path.to_bytes_with_nul().to_owned())?; let p = path.as_mut_ptr() as *mut _; let fd = unsafe { libc::mkstemp(p) }; let last = path.pop(); // drop the trailing nul @@ -1939,8 +2003,14 @@ feature! { #[repr(i32)] #[non_exhaustive] pub enum PathconfVar { - #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "linux", - target_os = "netbsd", target_os = "openbsd", target_os = "redox"))] + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_os = "redox" + ))] /// Minimum number of bits needed to represent, as a signed integer value, /// the maximum size of a regular file allowed in the specified directory. #[cfg_attr(docsrs, doc(cfg(all())))] @@ -1964,42 +2034,86 @@ pub enum PathconfVar { /// Maximum number of bytes that is guaranteed to be atomic when writing to /// a pipe. PIPE_BUF = libc::_PC_PIPE_BUF, - #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "illumos", - target_os = "linux", target_os = "netbsd", target_os = "openbsd", - target_os = "redox", target_os = "solaris"))] + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_os = "redox", + target_os = "solaris" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// Symbolic links can be created. POSIX2_SYMLINKS = libc::_PC_2_SYMLINKS, - #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", - target_os = "linux", target_os = "openbsd", target_os = "redox"))] + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "openbsd", + target_os = "redox" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// Minimum number of bytes of storage actually allocated for any portion of /// a file. POSIX_ALLOC_SIZE_MIN = libc::_PC_ALLOC_SIZE_MIN, - #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", - target_os = "linux", target_os = "openbsd"))] + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// Recommended increment for file transfer sizes between the /// `POSIX_REC_MIN_XFER_SIZE` and `POSIX_REC_MAX_XFER_SIZE` values. POSIX_REC_INCR_XFER_SIZE = libc::_PC_REC_INCR_XFER_SIZE, - #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", - target_os = "linux", target_os = "openbsd", target_os = "redox"))] + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "openbsd", + target_os = "redox" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// Maximum recommended file transfer size. POSIX_REC_MAX_XFER_SIZE = libc::_PC_REC_MAX_XFER_SIZE, - #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", - target_os = "linux", target_os = "openbsd", target_os = "redox"))] + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "openbsd", + target_os = "redox" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// Minimum recommended file transfer size. POSIX_REC_MIN_XFER_SIZE = libc::_PC_REC_MIN_XFER_SIZE, - #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", - target_os = "linux", target_os = "openbsd", target_os = "redox"))] + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "openbsd", + target_os = "redox" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// Recommended file transfer buffer alignment. POSIX_REC_XFER_ALIGN = libc::_PC_REC_XFER_ALIGN, - #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", - target_os = "illumos", target_os = "linux", target_os = "netbsd", - target_os = "openbsd", target_os = "redox", target_os = "solaris"))] + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_os = "redox", + target_os = "solaris" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// Maximum number of bytes in a symbolic link. SYMLINK_MAX = libc::_PC_SYMLINK_MAX, @@ -2013,23 +2127,45 @@ pub enum PathconfVar { /// This symbol shall be defined to be the value of a character that shall /// disable terminal special character handling. _POSIX_VDISABLE = libc::_PC_VDISABLE, - #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", - target_os = "illumos", target_os = "linux", target_os = "openbsd", - target_os = "redox", target_os = "solaris"))] + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "linux", + target_os = "openbsd", + target_os = "redox", + target_os = "solaris" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// Asynchronous input or output operations may be performed for the /// associated file. _POSIX_ASYNC_IO = libc::_PC_ASYNC_IO, - #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", - target_os = "illumos", target_os = "linux", target_os = "openbsd", - target_os = "redox", target_os = "solaris"))] + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "linux", + target_os = "openbsd", + target_os = "redox", + target_os = "solaris" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// Prioritized input or output operations may be performed for the /// associated file. _POSIX_PRIO_IO = libc::_PC_PRIO_IO, - #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", - target_os = "illumos", target_os = "linux", target_os = "netbsd", - target_os = "openbsd", target_os = "redox", target_os = "solaris"))] + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_os = "redox", + target_os = "solaris" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// Synchronized input or output operations may be performed for the /// associated file. @@ -2037,7 +2173,7 @@ pub enum PathconfVar { #[cfg(any(target_os = "dragonfly", target_os = "openbsd"))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The resolution in nanoseconds for all file timestamps. - _POSIX_TIMESTAMP_RESOLUTION = libc::_PC_TIMESTAMP_RESOLUTION + _POSIX_TIMESTAMP_RESOLUTION = libc::_PC_TIMESTAMP_RESOLUTION, } /// Like `pathconf`, but works with file descriptors instead of paths (see @@ -2093,12 +2229,13 @@ pub fn fpathconf(fd: RawFd, var: PathconfVar) -> Result> { /// - `Ok(None)`: the variable has no limit (for limit variables) or is /// unsupported (for option variables) /// - `Err(x)`: an error occurred -pub fn pathconf(path: &P, var: PathconfVar) -> Result> { - let raw = path.with_nix_path(|cstr| { - unsafe { - Errno::clear(); - libc::pathconf(cstr.as_ptr(), var as c_int) - } +pub fn pathconf( + path: &P, + var: PathconfVar, +) -> Result> { + let raw = path.with_nix_path(|cstr| unsafe { + Errno::clear(); + libc::pathconf(cstr.as_ptr(), var as c_int) })?; if raw == -1 { if errno::errno() == 0 { @@ -2145,9 +2282,15 @@ pub enum SysconfVar { #[cfg(not(any(target_os = "redox", target_os = "haiku")))] #[cfg_attr(docsrs, doc(cfg(all())))] AIO_MAX = libc::_SC_AIO_MAX, - #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd", - target_os = "ios", target_os="linux", target_os = "macos", - target_os="openbsd"))] + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The maximum amount by which a process can decrease its asynchronous I/O /// priority level from its own scheduling priority. @@ -2192,9 +2335,17 @@ pub enum SysconfVar { #[cfg(not(any(target_os = "redox", target_os = "haiku")))] #[cfg_attr(docsrs, doc(cfg(all())))] EXPR_NEST_MAX = libc::_SC_EXPR_NEST_MAX, - #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos", - target_os = "ios", target_os="linux", target_os = "macos", - target_os="netbsd", target_os="openbsd", target_os = "solaris"))] + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// Maximum length of a host name (not including the terminating null) as /// returned from the `gethostname` function @@ -2235,14 +2386,28 @@ pub enum SysconfVar { /// A value one greater than the maximum value that the system may assign to /// a newly-created file descriptor. OPEN_MAX = libc::_SC_OPEN_MAX, - #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", - target_os="linux", target_os = "macos", target_os="openbsd"))] + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation supports the Advisory Information option. _POSIX_ADVISORY_INFO = libc::_SC_ADVISORY_INFO, - #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos", - target_os = "ios", target_os="linux", target_os = "macos", - target_os="netbsd", target_os="openbsd", target_os = "solaris"))] + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation supports barriers. _POSIX_BARRIERS = libc::_SC_BARRIERS, @@ -2250,15 +2415,31 @@ pub enum SysconfVar { #[cfg(not(any(target_os = "redox", target_os = "haiku")))] #[cfg_attr(docsrs, doc(cfg(all())))] _POSIX_ASYNCHRONOUS_IO = libc::_SC_ASYNCHRONOUS_IO, - #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos", - target_os = "ios", target_os="linux", target_os = "macos", - target_os="netbsd", target_os="openbsd", target_os = "solaris"))] + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation supports clock selection. _POSIX_CLOCK_SELECTION = libc::_SC_CLOCK_SELECTION, - #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos", - target_os = "ios", target_os="linux", target_os = "macos", - target_os="netbsd", target_os="openbsd", target_os = "solaris"))] + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation supports the Process CPU-Time Clocks option. _POSIX_CPUTIME = libc::_SC_CPUTIME, @@ -2266,9 +2447,16 @@ pub enum SysconfVar { #[cfg(not(any(target_os = "redox", target_os = "haiku")))] #[cfg_attr(docsrs, doc(cfg(all())))] _POSIX_FSYNC = libc::_SC_FSYNC, - #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos", - target_os = "ios", target_os="linux", target_os = "macos", - target_os="openbsd", target_os = "solaris"))] + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "openbsd", + target_os = "solaris" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation supports the IPv6 option. _POSIX_IPV6 = libc::_SC_IPV6, @@ -2300,9 +2488,17 @@ pub enum SysconfVar { #[cfg(not(target_os = "redox"))] #[cfg_attr(docsrs, doc(cfg(all())))] _POSIX_MONOTONIC_CLOCK = libc::_SC_MONOTONIC_CLOCK, - #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd", - target_os = "illumos", target_os = "ios", target_os="linux", - target_os = "macos", target_os="openbsd", target_os = "solaris"))] + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "openbsd", + target_os = "solaris" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation supports the Prioritized Input and Output option. _POSIX_PRIORITIZED_IO = libc::_SC_PRIORITIZED_IO, @@ -2310,27 +2506,56 @@ pub enum SysconfVar { #[cfg(not(any(target_os = "redox", target_os = "haiku")))] #[cfg_attr(docsrs, doc(cfg(all())))] _POSIX_PRIORITY_SCHEDULING = libc::_SC_PRIORITY_SCHEDULING, - #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos", - target_os = "ios", target_os="linux", target_os = "macos", - target_os="openbsd", target_os = "solaris"))] + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "openbsd", + target_os = "solaris" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation supports the Raw Sockets option. _POSIX_RAW_SOCKETS = libc::_SC_RAW_SOCKETS, - #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos", - target_os = "ios", target_os="linux", target_os = "macos", - target_os="netbsd", target_os="openbsd", target_os = "solaris"))] + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation supports read-write locks. _POSIX_READER_WRITER_LOCKS = libc::_SC_READER_WRITER_LOCKS, - #[cfg(any(target_os = "android", target_os="dragonfly", target_os="freebsd", - target_os = "ios", target_os="linux", target_os = "macos", - target_os = "openbsd"))] + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation supports realtime signals. _POSIX_REALTIME_SIGNALS = libc::_SC_REALTIME_SIGNALS, - #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos", - target_os = "ios", target_os="linux", target_os = "macos", - target_os="netbsd", target_os="openbsd", target_os = "solaris"))] + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation supports the Regular Expression Handling option. _POSIX_REGEXP = libc::_SC_REGEXP, @@ -2346,31 +2571,59 @@ pub enum SysconfVar { #[cfg(not(any(target_os = "redox", target_os = "haiku")))] #[cfg_attr(docsrs, doc(cfg(all())))] _POSIX_SHARED_MEMORY_OBJECTS = libc::_SC_SHARED_MEMORY_OBJECTS, - #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", - target_os="linux", target_os = "macos", target_os="netbsd", - target_os="openbsd"))] + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation supports the POSIX shell. _POSIX_SHELL = libc::_SC_SHELL, - #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", - target_os="linux", target_os = "macos", target_os="netbsd", - target_os="openbsd"))] + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation supports the Spawn option. _POSIX_SPAWN = libc::_SC_SPAWN, - #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", - target_os="linux", target_os = "macos", target_os="netbsd", - target_os="openbsd"))] + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation supports spin locks. _POSIX_SPIN_LOCKS = libc::_SC_SPIN_LOCKS, - #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", - target_os="linux", target_os = "macos", target_os="openbsd"))] + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation supports the Process Sporadic Server option. _POSIX_SPORADIC_SERVER = libc::_SC_SPORADIC_SERVER, - #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos", - target_os="openbsd"))] + #[cfg(any( + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] _POSIX_SS_REPL_MAX = libc::_SC_SS_REPL_MAX, /// The implementation supports the Synchronized Input and Output option. @@ -2385,8 +2638,13 @@ pub enum SysconfVar { #[cfg(not(target_os = "redox"))] #[cfg_attr(docsrs, doc(cfg(all())))] _POSIX_THREAD_ATTR_STACKSIZE = libc::_SC_THREAD_ATTR_STACKSIZE, - #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos", - target_os="netbsd", target_os="openbsd"))] + #[cfg(any( + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation supports the Thread CPU-Time Clocks option. _POSIX_THREAD_CPUTIME = libc::_SC_THREAD_CPUTIME, @@ -2403,18 +2661,32 @@ pub enum SysconfVar { #[cfg(not(target_os = "redox"))] #[cfg_attr(docsrs, doc(cfg(all())))] _POSIX_THREAD_PRIORITY_SCHEDULING = libc::_SC_THREAD_PRIORITY_SCHEDULING, - #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", - target_os="linux", target_os = "macos", target_os="netbsd", - target_os="openbsd"))] + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation supports the Thread Process-Shared Synchronization /// option. _POSIX_THREAD_PROCESS_SHARED = libc::_SC_THREAD_PROCESS_SHARED, - #[cfg(any(target_os="dragonfly", target_os="linux", target_os="openbsd"))] + #[cfg(any( + target_os = "dragonfly", + target_os = "linux", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation supports the Robust Mutex Priority Inheritance option. _POSIX_THREAD_ROBUST_PRIO_INHERIT = libc::_SC_THREAD_ROBUST_PRIO_INHERIT, - #[cfg(any(target_os="dragonfly", target_os="linux", target_os="openbsd"))] + #[cfg(any( + target_os = "dragonfly", + target_os = "linux", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation supports the Robust Mutex Priority Protection option. _POSIX_THREAD_ROBUST_PRIO_PROTECT = libc::_SC_THREAD_ROBUST_PRIO_PROTECT, @@ -2422,8 +2694,14 @@ pub enum SysconfVar { #[cfg(not(any(target_os = "redox", target_os = "haiku")))] #[cfg_attr(docsrs, doc(cfg(all())))] _POSIX_THREAD_SAFE_FUNCTIONS = libc::_SC_THREAD_SAFE_FUNCTIONS, - #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", - target_os="linux", target_os = "macos", target_os="openbsd"))] + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation supports the Thread Sporadic Server option. _POSIX_THREAD_SPORADIC_SERVER = libc::_SC_THREAD_SPORADIC_SERVER, @@ -2431,8 +2709,14 @@ pub enum SysconfVar { #[cfg(not(target_os = "redox"))] #[cfg_attr(docsrs, doc(cfg(all())))] _POSIX_THREADS = libc::_SC_THREADS, - #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", - target_os="linux", target_os = "macos", target_os="openbsd"))] + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation supports timeouts. _POSIX_TIMEOUTS = libc::_SC_TIMEOUTS, @@ -2440,44 +2724,90 @@ pub enum SysconfVar { #[cfg(not(target_os = "redox"))] #[cfg_attr(docsrs, doc(cfg(all())))] _POSIX_TIMERS = libc::_SC_TIMERS, - #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", - target_os="linux", target_os = "macos", target_os="openbsd"))] + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation supports the Trace option. _POSIX_TRACE = libc::_SC_TRACE, - #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", - target_os="linux", target_os = "macos", target_os="openbsd"))] + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation supports the Trace Event Filter option. _POSIX_TRACE_EVENT_FILTER = libc::_SC_TRACE_EVENT_FILTER, - #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos", - target_os="openbsd"))] + #[cfg(any( + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] _POSIX_TRACE_EVENT_NAME_MAX = libc::_SC_TRACE_EVENT_NAME_MAX, - #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", - target_os="linux", target_os = "macos", target_os="openbsd"))] + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation supports the Trace Inherit option. _POSIX_TRACE_INHERIT = libc::_SC_TRACE_INHERIT, - #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", - target_os="linux", target_os = "macos", target_os="openbsd"))] + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation supports the Trace Log option. _POSIX_TRACE_LOG = libc::_SC_TRACE_LOG, - #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos", - target_os="openbsd"))] + #[cfg(any( + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] _POSIX_TRACE_NAME_MAX = libc::_SC_TRACE_NAME_MAX, - #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos", - target_os="openbsd"))] + #[cfg(any( + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] _POSIX_TRACE_SYS_MAX = libc::_SC_TRACE_SYS_MAX, - #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos", - target_os="openbsd"))] + #[cfg(any( + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] _POSIX_TRACE_USER_EVENT_MAX = libc::_SC_TRACE_USER_EVENT_MAX, - #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", - target_os="linux", target_os = "macos", target_os="openbsd"))] + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation supports the Typed Memory Objects option. _POSIX_TYPED_MEMORY_OBJECTS = libc::_SC_TYPED_MEMORY_OBJECTS, @@ -2485,31 +2815,55 @@ pub enum SysconfVar { /// to which the implementation conforms. For implementations conforming to /// POSIX.1-2008, the value shall be 200809L. _POSIX_VERSION = libc::_SC_VERSION, - #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", - target_os="linux", target_os = "macos", target_os="netbsd", - target_os="openbsd"))] + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation provides a C-language compilation environment with /// 32-bit `int`, `long`, `pointer`, and `off_t` types. _POSIX_V6_ILP32_OFF32 = libc::_SC_V6_ILP32_OFF32, - #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", - target_os="linux", target_os = "macos", target_os="netbsd", - target_os="openbsd"))] + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation provides a C-language compilation environment with /// 32-bit `int`, `long`, and pointer types and an `off_t` type using at /// least 64 bits. _POSIX_V6_ILP32_OFFBIG = libc::_SC_V6_ILP32_OFFBIG, - #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", - target_os="linux", target_os = "macos", target_os="netbsd", - target_os="openbsd"))] + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation provides a C-language compilation environment with /// 32-bit `int` and 64-bit `long`, `pointer`, and `off_t` types. _POSIX_V6_LP64_OFF64 = libc::_SC_V6_LP64_OFF64, - #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", - target_os="linux", target_os = "macos", target_os="netbsd", - target_os="openbsd"))] + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation provides a C-language compilation environment with an /// `int` type using at least 32 bits and `long`, pointer, and `off_t` types @@ -2540,40 +2894,76 @@ pub enum SysconfVar { #[cfg(not(any(target_os = "redox", target_os = "haiku")))] #[cfg_attr(docsrs, doc(cfg(all())))] _POSIX2_LOCALEDEF = libc::_SC_2_LOCALEDEF, - #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", - target_os="linux", target_os = "macos", target_os="netbsd", - target_os="openbsd"))] + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation supports the Batch Environment Services and Utilities /// option. _POSIX2_PBS = libc::_SC_2_PBS, - #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", - target_os="linux", target_os = "macos", target_os="netbsd", - target_os="openbsd"))] + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation supports the Batch Accounting option. _POSIX2_PBS_ACCOUNTING = libc::_SC_2_PBS_ACCOUNTING, - #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", - target_os="linux", target_os = "macos", target_os="netbsd", - target_os="openbsd"))] + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation supports the Batch Checkpoint/Restart option. _POSIX2_PBS_CHECKPOINT = libc::_SC_2_PBS_CHECKPOINT, - #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", - target_os="linux", target_os = "macos", target_os="netbsd", - target_os="openbsd"))] + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation supports the Locate Batch Job Request option. _POSIX2_PBS_LOCATE = libc::_SC_2_PBS_LOCATE, - #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", - target_os="linux", target_os = "macos", target_os="netbsd", - target_os="openbsd"))] + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation supports the Batch Job Message Request option. _POSIX2_PBS_MESSAGE = libc::_SC_2_PBS_MESSAGE, - #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", - target_os="linux", target_os = "macos", target_os="netbsd", - target_os="openbsd"))] + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation supports the Track Batch Job Request option. _POSIX2_PBS_TRACK = libc::_SC_2_PBS_TRACK, @@ -2609,28 +2999,52 @@ pub enum SysconfVar { PTHREAD_THREADS_MAX = libc::_SC_THREAD_THREADS_MAX, #[cfg(not(target_os = "haiku"))] RE_DUP_MAX = libc::_SC_RE_DUP_MAX, - #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd", - target_os = "ios", target_os="linux", target_os = "macos", - target_os="openbsd"))] + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] RTSIG_MAX = libc::_SC_RTSIG_MAX, #[cfg(not(target_os = "redox"))] #[cfg_attr(docsrs, doc(cfg(all())))] SEM_NSEMS_MAX = libc::_SC_SEM_NSEMS_MAX, - #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd", - target_os = "ios", target_os="linux", target_os = "macos", - target_os="openbsd"))] + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] SEM_VALUE_MAX = libc::_SC_SEM_VALUE_MAX, - #[cfg(any(target_os = "android", target_os="dragonfly", target_os="freebsd", - target_os = "ios", target_os="linux", target_os = "macos", - target_os = "openbsd"))] + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] SIGQUEUE_MAX = libc::_SC_SIGQUEUE_MAX, STREAM_MAX = libc::_SC_STREAM_MAX, - #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", - target_os="linux", target_os = "macos", target_os="netbsd", - target_os="openbsd"))] + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] SYMLOOP_MAX = libc::_SC_SYMLOOP_MAX, #[cfg(not(target_os = "redox"))] @@ -2638,33 +3052,63 @@ pub enum SysconfVar { TIMER_MAX = libc::_SC_TIMER_MAX, TTY_NAME_MAX = libc::_SC_TTY_NAME_MAX, TZNAME_MAX = libc::_SC_TZNAME_MAX, - #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd", - target_os = "ios", target_os="linux", target_os = "macos", - target_os="openbsd"))] + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation supports the X/Open Encryption Option Group. _XOPEN_CRYPT = libc::_SC_XOPEN_CRYPT, - #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd", - target_os = "ios", target_os="linux", target_os = "macos", - target_os="openbsd"))] + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation supports the Issue 4, Version 2 Enhanced /// Internationalization Option Group. _XOPEN_ENH_I18N = libc::_SC_XOPEN_ENH_I18N, - #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd", - target_os = "ios", target_os="linux", target_os = "macos", - target_os="openbsd"))] + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] _XOPEN_LEGACY = libc::_SC_XOPEN_LEGACY, - #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd", - target_os = "ios", target_os="linux", target_os = "macos", - target_os="openbsd"))] + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation supports the X/Open Realtime Option Group. _XOPEN_REALTIME = libc::_SC_XOPEN_REALTIME, - #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd", - target_os = "ios", target_os="linux", target_os = "macos", - target_os="openbsd"))] + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation supports the X/Open Realtime Threads Option Group. _XOPEN_REALTIME_THREADS = libc::_SC_XOPEN_REALTIME_THREADS, @@ -2673,36 +3117,54 @@ pub enum SysconfVar { #[cfg(not(any(target_os = "redox", target_os = "haiku")))] #[cfg_attr(docsrs, doc(cfg(all())))] _XOPEN_SHM = libc::_SC_XOPEN_SHM, - #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", - target_os="linux", target_os = "macos", target_os="openbsd"))] + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation supports the XSI STREAMS Option Group. _XOPEN_STREAMS = libc::_SC_XOPEN_STREAMS, - #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd", - target_os = "ios", target_os="linux", target_os = "macos", - target_os="openbsd"))] + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// The implementation supports the XSI option _XOPEN_UNIX = libc::_SC_XOPEN_UNIX, - #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd", - target_os = "ios", target_os="linux", target_os = "macos", - target_os="openbsd"))] + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "openbsd" + ))] #[cfg_attr(docsrs, doc(cfg(all())))] /// Integer value indicating version of the X/Open Portability Guide to /// which the implementation conforms. _XOPEN_VERSION = libc::_SC_XOPEN_VERSION, /// The number of pages of physical memory. Note that it is possible for /// the product of this value to overflow. - #[cfg(any(target_os="android", target_os="linux"))] + #[cfg(any(target_os = "android", target_os = "linux"))] _PHYS_PAGES = libc::_SC_PHYS_PAGES, /// The number of currently available pages of physical memory. - #[cfg(any(target_os="android", target_os="linux"))] + #[cfg(any(target_os = "android", target_os = "linux"))] _AVPHYS_PAGES = libc::_SC_AVPHYS_PAGES, /// The number of processors configured. - #[cfg(any(target_os="android", target_os="linux"))] + #[cfg(any(target_os = "android", target_os = "linux"))] _NPROCESSORS_CONF = libc::_SC_NPROCESSORS_CONF, /// The number of processors currently online (available). - #[cfg(any(target_os="android", target_os="linux"))] + #[cfg(any(target_os = "android", target_os = "linux"))] _NPROCESSORS_ONLN = libc::_SC_NPROCESSORS_ONLN, } @@ -2739,28 +3201,29 @@ pub fn sysconf(var: SysconfVar) -> Result> { } } -feature! { -#![feature = "fs"] - #[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(feature = "fs")] mod pivot_root { - use crate::{Result, NixPath}; use crate::errno::Errno; + use crate::{NixPath, Result}; pub fn pivot_root( - new_root: &P1, put_old: &P2) -> Result<()> { + new_root: &P1, + put_old: &P2, + ) -> Result<()> { let res = new_root.with_nix_path(|new_root| { - put_old.with_nix_path(|put_old| { - unsafe { - libc::syscall(libc::SYS_pivot_root, new_root.as_ptr(), put_old.as_ptr()) - } + put_old.with_nix_path(|put_old| unsafe { + libc::syscall( + libc::SYS_pivot_root, + new_root.as_ptr(), + put_old.as_ptr(), + ) }) })??; Errno::result(res).map(drop) } } -} #[cfg(any( target_os = "android", @@ -2773,9 +3236,9 @@ mod setres { feature! { #![feature = "user"] - use crate::Result; + use super::{Gid, Uid}; use crate::errno::Errno; - use super::{Uid, Gid}; + use crate::Result; /// Sets the real, effective, and saved uid. /// ([see setresuid(2)](https://man7.org/linux/man-pages/man2/setresuid.2.html)) @@ -2788,7 +3251,8 @@ mod setres { /// Err is returned if the user doesn't have permission to set this UID. #[inline] pub fn setresuid(ruid: Uid, euid: Uid, suid: Uid) -> Result<()> { - let res = unsafe { libc::setresuid(ruid.into(), euid.into(), suid.into()) }; + let res = + unsafe { libc::setresuid(ruid.into(), euid.into(), suid.into()) }; Errno::result(res).map(drop) } @@ -2804,7 +3268,8 @@ mod setres { /// Err is returned if the user doesn't have permission to set this GID. #[inline] pub fn setresgid(rgid: Gid, egid: Gid, sgid: Gid) -> Result<()> { - let res = unsafe { libc::setresgid(rgid.into(), egid.into(), sgid.into()) }; + let res = + unsafe { libc::setresgid(rgid.into(), egid.into(), sgid.into()) }; Errno::result(res).map(drop) } @@ -2822,16 +3287,16 @@ mod getres { feature! { #![feature = "user"] - use crate::Result; + use super::{Gid, Uid}; use crate::errno::Errno; - use super::{Uid, Gid}; + use crate::Result; /// Real, effective and saved user IDs. #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct ResUid { pub real: Uid, pub effective: Uid, - pub saved: Uid + pub saved: Uid, } /// Real, effective and saved group IDs. @@ -2839,7 +3304,7 @@ mod getres { pub struct ResGid { pub real: Gid, pub effective: Gid, - pub saved: Gid + pub saved: Gid, } /// Gets the real, effective, and saved user IDs. @@ -2858,7 +3323,11 @@ mod getres { let mut suid = libc::uid_t::max_value(); let res = unsafe { libc::getresuid(&mut ruid, &mut euid, &mut suid) }; - Errno::result(res).map(|_| ResUid{ real: Uid(ruid), effective: Uid(euid), saved: Uid(suid) }) + Errno::result(res).map(|_| ResUid { + real: Uid(ruid), + effective: Uid(euid), + saved: Uid(suid), + }) } /// Gets the real, effective, and saved group IDs. @@ -2877,7 +3346,11 @@ mod getres { let mut sgid = libc::gid_t::max_value(); let res = unsafe { libc::getresgid(&mut rgid, &mut egid, &mut sgid) }; - Errno::result(res).map(|_| ResGid { real: Gid(rgid), effective: Gid(egid), saved: Gid(sgid) } ) + Errno::result(res).map(|_| ResGid { + real: Gid(rgid), + effective: Gid(egid), + saved: Gid(sgid), + }) } } } @@ -2904,10 +3377,8 @@ feature! { /// Checks the file named by `path` for accessibility according to the flags given by `amode` /// See [access(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/access.html) pub fn access(path: &P, amode: AccessFlags) -> Result<()> { - let res = path.with_nix_path(|cstr| { - unsafe { - libc::access(cstr.as_ptr(), amode.bits) - } + let res = path.with_nix_path(|cstr| unsafe { + libc::access(cstr.as_ptr(), amode.bits()) })?; Errno::result(res).map(drop) } @@ -2923,11 +3394,19 @@ pub fn access(path: &P, amode: AccessFlags) -> Result<()> { /// [faccessat(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/faccessat.html) // redox: does not appear to support the *at family of syscalls. #[cfg(not(target_os = "redox"))] -pub fn faccessat(dirfd: Option, path: &P, mode: AccessFlags, flags: AtFlags) -> Result<()> { - let res = path.with_nix_path(|cstr| { - unsafe { - libc::faccessat(at_rawfd(dirfd), cstr.as_ptr(), mode.bits(), flags.bits()) - } +pub fn faccessat( + dirfd: Option, + path: &P, + mode: AccessFlags, + flags: AtFlags, +) -> Result<()> { + let res = path.with_nix_path(|cstr| unsafe { + libc::faccessat( + at_rawfd(dirfd), + cstr.as_ptr(), + mode.bits(), + flags.bits(), + ) })?; Errno::result(res).map(drop) } @@ -2945,10 +3424,8 @@ pub fn faccessat(dirfd: Option, path: &P, mode: Acce target_os = "dragonfly" ))] pub fn eaccess(path: &P, mode: AccessFlags) -> Result<()> { - let res = path.with_nix_path(|cstr| { - unsafe { - libc::eaccess(cstr.as_ptr(), mode.bits) - } + let res = path.with_nix_path(|cstr| unsafe { + libc::eaccess(cstr.as_ptr(), mode.bits()) })?; Errno::result(res).map(drop) } @@ -2982,32 +3459,41 @@ pub struct User { /// Path to shell pub shell: PathBuf, /// Login class - #[cfg(not(any(target_os = "android", - target_os = "fuchsia", - target_os = "haiku", - target_os = "illumos", - target_os = "linux", - target_os = "solaris")))] + #[cfg(not(any( + target_os = "aix", + target_os = "android", + target_os = "fuchsia", + target_os = "haiku", + target_os = "illumos", + target_os = "linux", + target_os = "solaris" + )))] #[cfg_attr(docsrs, doc(cfg(all())))] pub class: CString, /// Last password change - #[cfg(not(any(target_os = "android", - target_os = "fuchsia", - target_os = "haiku", - target_os = "illumos", - target_os = "linux", - target_os = "solaris")))] + #[cfg(not(any( + target_os = "aix", + target_os = "android", + target_os = "fuchsia", + target_os = "haiku", + target_os = "illumos", + target_os = "linux", + target_os = "solaris" + )))] #[cfg_attr(docsrs, doc(cfg(all())))] pub change: libc::time_t, /// Expiration time of account - #[cfg(not(any(target_os = "android", - target_os = "fuchsia", - target_os = "haiku", - target_os = "illumos", - target_os = "linux", - target_os = "solaris")))] + #[cfg(not(any( + target_os = "aix", + target_os = "android", + target_os = "fuchsia", + target_os = "haiku", + target_os = "illumos", + target_os = "linux", + target_os = "solaris" + )))] #[cfg_attr(docsrs, doc(cfg(all())))] - pub expire: libc::time_t + pub expire: libc::time_t, } #[cfg(not(target_os = "redox"))] //RedoxFS does not support passwd @@ -3015,35 +3501,74 @@ impl From<&libc::passwd> for User { fn from(pw: &libc::passwd) -> User { unsafe { User { - name: if pw.pw_name.is_null() { Default::default() } else { CStr::from_ptr(pw.pw_name).to_string_lossy().into_owned() }, - passwd: if pw.pw_passwd.is_null() { Default::default() } else { CString::new(CStr::from_ptr(pw.pw_passwd).to_bytes()).unwrap() }, - #[cfg(not(all(target_os = "android", target_pointer_width = "32")))] - gecos: if pw.pw_gecos.is_null() { Default::default() } else { CString::new(CStr::from_ptr(pw.pw_gecos).to_bytes()).unwrap() }, - dir: if pw.pw_dir.is_null() { Default::default() } else { PathBuf::from(OsStr::from_bytes(CStr::from_ptr(pw.pw_dir).to_bytes())) }, - shell: if pw.pw_shell.is_null() { Default::default() } else { PathBuf::from(OsStr::from_bytes(CStr::from_ptr(pw.pw_shell).to_bytes())) }, + name: if pw.pw_name.is_null() { + Default::default() + } else { + CStr::from_ptr(pw.pw_name).to_string_lossy().into_owned() + }, + passwd: if pw.pw_passwd.is_null() { + Default::default() + } else { + CString::new(CStr::from_ptr(pw.pw_passwd).to_bytes()) + .unwrap() + }, + #[cfg(not(all( + target_os = "android", + target_pointer_width = "32" + )))] + gecos: if pw.pw_gecos.is_null() { + Default::default() + } else { + CString::new(CStr::from_ptr(pw.pw_gecos).to_bytes()) + .unwrap() + }, + dir: if pw.pw_dir.is_null() { + Default::default() + } else { + PathBuf::from(OsStr::from_bytes( + CStr::from_ptr(pw.pw_dir).to_bytes(), + )) + }, + shell: if pw.pw_shell.is_null() { + Default::default() + } else { + PathBuf::from(OsStr::from_bytes( + CStr::from_ptr(pw.pw_shell).to_bytes(), + )) + }, uid: Uid::from_raw(pw.pw_uid), gid: Gid::from_raw(pw.pw_gid), - #[cfg(not(any(target_os = "android", - target_os = "fuchsia", - target_os = "haiku", - target_os = "illumos", - target_os = "linux", - target_os = "solaris")))] - class: CString::new(CStr::from_ptr(pw.pw_class).to_bytes()).unwrap(), - #[cfg(not(any(target_os = "android", - target_os = "fuchsia", - target_os = "haiku", - target_os = "illumos", - target_os = "linux", - target_os = "solaris")))] + #[cfg(not(any( + target_os = "aix", + target_os = "android", + target_os = "fuchsia", + target_os = "haiku", + target_os = "illumos", + target_os = "linux", + target_os = "solaris" + )))] + class: CString::new(CStr::from_ptr(pw.pw_class).to_bytes()) + .unwrap(), + #[cfg(not(any( + target_os = "aix", + target_os = "android", + target_os = "fuchsia", + target_os = "haiku", + target_os = "illumos", + target_os = "linux", + target_os = "solaris" + )))] change: pw.pw_change, - #[cfg(not(any(target_os = "android", - target_os = "fuchsia", - target_os = "haiku", - target_os = "illumos", - target_os = "linux", - target_os = "solaris")))] - expire: pw.pw_expire + #[cfg(not(any( + target_os = "aix", + target_os = "android", + target_os = "fuchsia", + target_os = "haiku", + target_os = "illumos", + target_os = "linux", + target_os = "solaris" + )))] + expire: pw.pw_expire, } } } @@ -3067,32 +3592,44 @@ impl From for libc::passwd { Self { pw_name: name, pw_passwd: u.passwd.into_raw(), - #[cfg(not(all(target_os = "android", target_pointer_width = "32")))] + #[cfg(not(all( + target_os = "android", + target_pointer_width = "32" + )))] pw_gecos: u.gecos.into_raw(), pw_dir: dir, pw_shell: shell, pw_uid: u.uid.0, pw_gid: u.gid.0, - #[cfg(not(any(target_os = "android", - target_os = "fuchsia", - target_os = "haiku", - target_os = "illumos", - target_os = "linux", - target_os = "solaris")))] + #[cfg(not(any( + target_os = "aix", + target_os = "android", + target_os = "fuchsia", + target_os = "haiku", + target_os = "illumos", + target_os = "linux", + target_os = "solaris" + )))] pw_class: u.class.into_raw(), - #[cfg(not(any(target_os = "android", - target_os = "fuchsia", - target_os = "haiku", - target_os = "illumos", - target_os = "linux", - target_os = "solaris")))] + #[cfg(not(any( + target_os = "aix", + target_os = "android", + target_os = "fuchsia", + target_os = "haiku", + target_os = "illumos", + target_os = "linux", + target_os = "solaris" + )))] pw_change: u.change, - #[cfg(not(any(target_os = "android", - target_os = "fuchsia", - target_os = "haiku", - target_os = "illumos", - target_os = "linux", - target_os = "solaris")))] + #[cfg(not(any( + target_os = "aix", + target_os = "android", + target_os = "fuchsia", + target_os = "haiku", + target_os = "illumos", + target_os = "linux", + target_os = "solaris" + )))] pw_expire: u.expire, #[cfg(target_os = "illumos")] pw_age: CString::new("").unwrap().into_raw(), @@ -3106,12 +3643,19 @@ impl From for libc::passwd { #[cfg(not(target_os = "redox"))] // RedoxFS does not support passwd impl User { - fn from_anything(f: F) -> Result> + /// # Safety + /// + /// If `f` writes to its `*mut *mut libc::passwd` parameter, then it must + /// also initialize the value pointed to by its `*mut libc::group` + /// parameter. + unsafe fn from_anything(f: F) -> Result> where - F: Fn(*mut libc::passwd, - *mut c_char, - libc::size_t, - *mut *mut libc::passwd) -> libc::c_int + F: Fn( + *mut libc::passwd, + *mut c_char, + libc::size_t, + *mut *mut libc::passwd, + ) -> libc::c_int, { let buflimit = 1048576; let bufsize = match sysconf(SysconfVar::GETPW_R_SIZE_MAX) { @@ -3124,12 +3668,19 @@ impl User { let mut res = ptr::null_mut(); loop { - let error = f(pwd.as_mut_ptr(), cbuf.as_mut_ptr(), cbuf.capacity(), &mut res); + let error = f( + pwd.as_mut_ptr(), + cbuf.as_mut_ptr(), + cbuf.capacity(), + &mut res, + ); if error == 0 { if res.is_null() { return Ok(None); } else { - let pwd = unsafe { pwd.assume_init() }; + // SAFETY: `f` guarantees that `pwd` is initialized if `res` + // is not null. + let pwd = pwd.assume_init(); return Ok(Some(User::from(&pwd))); } } else if Errno::last() == Errno::ERANGE { @@ -3155,15 +3706,19 @@ impl User { /// assert_eq!(res.name, "root"); /// ``` pub fn from_uid(uid: Uid) -> Result> { - User::from_anything(|pwd, cbuf, cap, res| { - unsafe { libc::getpwuid_r(uid.0, pwd, cbuf, cap, res) } - }) + // SAFETY: `getpwuid_r` will write to `res` if it initializes the value + // at `pwd`. + unsafe { + User::from_anything(|pwd, cbuf, cap, res| { + libc::getpwuid_r(uid.0, pwd, cbuf, cap, res) + }) + } } /// Get a user by name. /// /// Internally, this function calls - /// [getpwnam_r(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwuid_r.html) + /// [getpwnam_r(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwnam_r.html) /// /// # Examples /// @@ -3178,9 +3733,13 @@ impl User { Ok(c_str) => c_str, Err(_nul_error) => return Ok(None), }; - User::from_anything(|pwd, cbuf, cap, res| { - unsafe { libc::getpwnam_r(name.as_ptr(), pwd, cbuf, cap, res) } - }) + // SAFETY: `getpwnam_r` will write to `res` if it initializes the value + // at `pwd`. + unsafe { + User::from_anything(|pwd, cbuf, cap, res| { + libc::getpwnam_r(name.as_ptr(), pwd, cbuf, cap, res) + }) + } } } @@ -3195,7 +3754,7 @@ pub struct Group { /// Group ID pub gid: Gid, /// List of Group members - pub mem: Vec + pub mem: Vec, } #[cfg(not(target_os = "redox"))] // RedoxFS does not support passwd @@ -3203,10 +3762,23 @@ impl From<&libc::group> for Group { fn from(gr: &libc::group) -> Group { unsafe { Group { - name: CStr::from_ptr(gr.gr_name).to_string_lossy().into_owned(), - passwd: CString::new(CStr::from_ptr(gr.gr_passwd).to_bytes()).unwrap(), + name: if gr.gr_name.is_null() { + Default::default() + } else { + CStr::from_ptr(gr.gr_name).to_string_lossy().into_owned() + }, + passwd: if gr.gr_passwd.is_null() { + Default::default() + } else { + CString::new(CStr::from_ptr(gr.gr_passwd).to_bytes()) + .unwrap() + }, gid: Gid::from_raw(gr.gr_gid), - mem: Group::members(gr.gr_mem) + mem: if gr.gr_mem.is_null() { + Default::default() + } else { + Group::members(gr.gr_mem) + }, } } } @@ -3230,12 +3802,19 @@ impl Group { ret } - fn from_anything(f: F) -> Result> + /// # Safety + /// + /// If `f` writes to its `*mut *mut libc::group` parameter, then it must + /// also initialize the value pointed to by its `*mut libc::group` + /// parameter. + unsafe fn from_anything(f: F) -> Result> where - F: Fn(*mut libc::group, - *mut c_char, - libc::size_t, - *mut *mut libc::group) -> libc::c_int + F: Fn( + *mut libc::group, + *mut c_char, + libc::size_t, + *mut *mut libc::group, + ) -> libc::c_int, { let buflimit = 1048576; let bufsize = match sysconf(SysconfVar::GETGR_R_SIZE_MAX) { @@ -3248,12 +3827,19 @@ impl Group { let mut res = ptr::null_mut(); loop { - let error = f(grp.as_mut_ptr(), cbuf.as_mut_ptr(), cbuf.capacity(), &mut res); + let error = f( + grp.as_mut_ptr(), + cbuf.as_mut_ptr(), + cbuf.capacity(), + &mut res, + ); if error == 0 { if res.is_null() { return Ok(None); } else { - let grp = unsafe { grp.assume_init() }; + // SAFETY: `f` guarantees that `grp` is initialized if `res` + // is not null. + let grp = grp.assume_init(); return Ok(Some(Group::from(&grp))); } } else if Errno::last() == Errno::ERANGE { @@ -3281,9 +3867,13 @@ impl Group { /// assert!(res.name == "root"); /// ``` pub fn from_gid(gid: Gid) -> Result> { - Group::from_anything(|grp, cbuf, cap, res| { - unsafe { libc::getgrgid_r(gid.0, grp, cbuf, cap, res) } - }) + // SAFETY: `getgrgid_r` will write to `res` if it initializes the value + // at `grp`. + unsafe { + Group::from_anything(|grp, cbuf, cap, res| { + libc::getgrgid_r(gid.0, grp, cbuf, cap, res) + }) + } } /// Get a group by name. @@ -3306,9 +3896,13 @@ impl Group { Ok(c_str) => c_str, Err(_nul_error) => return Ok(None), }; - Group::from_anything(|grp, cbuf, cap, res| { - unsafe { libc::getgrnam_r(name.as_ptr(), grp, cbuf, cap, res) } - }) + // SAFETY: `getgrnam_r` will write to `res` if it initializes the value + // at `grp`. + unsafe { + Group::from_anything(|grp, cbuf, cap, res| { + libc::getgrnam_r(name.as_ptr(), grp, cbuf, cap, res) + }) + } } } } diff --git a/test/sys/test_aio.rs b/test/sys/test_aio.rs index 84086f8..5035b5a 100644 --- a/test/sys/test_aio.rs +++ b/test/sys/test_aio.rs @@ -21,9 +21,7 @@ use nix::{ }; use tempfile::tempfile; -lazy_static! { - pub static ref SIGNALED: AtomicBool = AtomicBool::new(false); -} +pub static SIGNALED: AtomicBool = AtomicBool::new(false); extern "C" fn sigfunc(_: c_int) { SIGNALED.store(true, Ordering::Relaxed); @@ -200,7 +198,7 @@ mod aio_read { assert_eq!(err, Ok(())); assert_eq!(aior.as_mut().aio_return().unwrap(), EXPECT.len()); } - assert_eq!(EXPECT, rbuf.deref().deref()); + assert_eq!(EXPECT, rbuf.deref()); } // Like ok, but allocates the structure on the stack. @@ -223,7 +221,7 @@ mod aio_read { assert_eq!(err, Ok(())); assert_eq!(aior.as_mut().aio_return().unwrap(), EXPECT.len()); } - assert_eq!(EXPECT, rbuf.deref().deref()); + assert_eq!(EXPECT, rbuf.deref()); } } @@ -610,7 +608,7 @@ fn test_aio_suspend() { let r = aio_suspend(&cbbuf[..], Some(timeout)); match r { Err(Errno::EINTR) => continue, - Err(e) => panic!("aio_suspend returned {:?}", e), + Err(e) => panic!("aio_suspend returned {e:?}"), Ok(_) => (), }; } diff --git a/test/sys/test_epoll.rs b/test/sys/test_epoll.rs index 9156915..84b100c 100644 --- a/test/sys/test_epoll.rs +++ b/test/sys/test_epoll.rs @@ -1,3 +1,5 @@ +#![allow(deprecated)] + use nix::errno::Errno; use nix::sys::epoll::{epoll_create1, epoll_ctl}; use nix::sys::epoll::{EpollCreateFlags, EpollEvent, EpollFlags, EpollOp}; diff --git a/test/sys/test_mman.rs b/test/sys/test_mman.rs index e748427..b4674e5 100644 --- a/test/sys/test_mman.rs +++ b/test/sys/test_mman.rs @@ -1,15 +1,15 @@ use nix::sys::mman::{mmap, MapFlags, ProtFlags}; -use std::num::NonZeroUsize; +use std::{num::NonZeroUsize, os::unix::io::BorrowedFd}; #[test] fn test_mmap_anonymous() { unsafe { - let ptr = mmap( + let ptr = mmap::( None, NonZeroUsize::new(1).unwrap(), ProtFlags::PROT_READ | ProtFlags::PROT_WRITE, MapFlags::MAP_PRIVATE | MapFlags::MAP_ANONYMOUS, - -1, + None, 0, ) .unwrap() as *mut u8; @@ -29,12 +29,12 @@ fn test_mremap_grow() { let one_k_non_zero = NonZeroUsize::new(ONE_K).unwrap(); let slice: &mut [u8] = unsafe { - let mem = mmap( + let mem = mmap::( None, one_k_non_zero, ProtFlags::PROT_READ | ProtFlags::PROT_WRITE, MapFlags::MAP_ANONYMOUS | MapFlags::MAP_PRIVATE, - -1, + None, 0, ) .unwrap(); @@ -87,12 +87,12 @@ fn test_mremap_shrink() { const ONE_K: size_t = 1024; let ten_one_k = NonZeroUsize::new(10 * ONE_K).unwrap(); let slice: &mut [u8] = unsafe { - let mem = mmap( + let mem = mmap::( None, ten_one_k, ProtFlags::PROT_READ | ProtFlags::PROT_WRITE, MapFlags::MAP_ANONYMOUS | MapFlags::MAP_PRIVATE, - -1, + None, 0, ) .unwrap(); diff --git a/test/sys/test_prctl.rs b/test/sys/test_prctl.rs new file mode 100644 index 0000000..351213b --- /dev/null +++ b/test/sys/test_prctl.rs @@ -0,0 +1,125 @@ +#[cfg(target_os = "linux")] +#[cfg(feature = "process")] +mod test_prctl { + use std::ffi::CStr; + + use nix::sys::prctl; + + #[cfg_attr(qemu, ignore)] + #[test] + fn test_get_set_subreaper() { + let original = prctl::get_child_subreaper().unwrap(); + + prctl::set_child_subreaper(true).unwrap(); + let subreaper = prctl::get_child_subreaper().unwrap(); + assert!(subreaper); + + prctl::set_child_subreaper(original).unwrap(); + } + + #[test] + fn test_get_set_dumpable() { + let original = prctl::get_dumpable().unwrap(); + + prctl::set_dumpable(false).unwrap(); + let dumpable = prctl::get_dumpable().unwrap(); + assert!(!dumpable); + + prctl::set_dumpable(original).unwrap(); + } + + #[test] + fn test_get_set_keepcaps() { + let original = prctl::get_keepcaps().unwrap(); + + prctl::set_keepcaps(true).unwrap(); + let keepcaps = prctl::get_keepcaps().unwrap(); + assert!(keepcaps); + + prctl::set_keepcaps(original).unwrap(); + } + + #[test] + fn test_get_set_clear_mce_kill() { + use prctl::PrctlMCEKillPolicy::*; + + prctl::set_mce_kill(PR_MCE_KILL_LATE).unwrap(); + let mce = prctl::get_mce_kill().unwrap(); + assert_eq!(mce, PR_MCE_KILL_LATE); + + prctl::clear_mce_kill().unwrap(); + let mce = prctl::get_mce_kill().unwrap(); + assert_eq!(mce, PR_MCE_KILL_DEFAULT); + } + + #[cfg_attr(qemu, ignore)] + #[test] + fn test_get_set_pdeathsig() { + use nix::sys::signal::Signal; + + let original = prctl::get_pdeathsig().unwrap(); + + prctl::set_pdeathsig(Signal::SIGUSR1).unwrap(); + let sig = prctl::get_pdeathsig().unwrap(); + assert_eq!(sig, Some(Signal::SIGUSR1)); + + prctl::set_pdeathsig(original).unwrap(); + } + + #[test] + fn test_get_set_name() { + let original = prctl::get_name().unwrap(); + + let long_name = + CStr::from_bytes_with_nul(b"0123456789abcdefghijklmn\0").unwrap(); + prctl::set_name(long_name).unwrap(); + let res = prctl::get_name().unwrap(); + + // name truncated by kernel to TASK_COMM_LEN + assert_eq!(&long_name.to_str().unwrap()[..15], res.to_str().unwrap()); + + let short_name = CStr::from_bytes_with_nul(b"01234567\0").unwrap(); + prctl::set_name(short_name).unwrap(); + let res = prctl::get_name().unwrap(); + assert_eq!(short_name.to_str().unwrap(), res.to_str().unwrap()); + + prctl::set_name(&original).unwrap(); + } + + #[cfg_attr(qemu, ignore)] + #[test] + fn test_get_set_timerslack() { + let original = prctl::get_timerslack().unwrap(); + + let slack = 60_000; + prctl::set_timerslack(slack).unwrap(); + let res = prctl::get_timerslack().unwrap(); + assert_eq!(slack, res as u64); + + prctl::set_timerslack(original as u64).unwrap(); + } + + #[test] + fn test_disable_enable_perf_events() { + prctl::task_perf_events_disable().unwrap(); + prctl::task_perf_events_enable().unwrap(); + } + + #[test] + fn test_get_set_no_new_privs() { + prctl::set_no_new_privs().unwrap(); + let no_new_privs = prctl::get_no_new_privs().unwrap(); + assert!(no_new_privs); + } + + #[test] + fn test_get_set_thp_disable() { + let original = prctl::get_thp_disable().unwrap(); + + prctl::set_thp_disable(true).unwrap(); + let thp_disable = prctl::get_thp_disable().unwrap(); + assert!(thp_disable); + + prctl::set_thp_disable(original).unwrap(); + } +} diff --git a/test/sys/test_select.rs b/test/sys/test_select.rs index 40bda4d..79f75de 100644 --- a/test/sys/test_select.rs +++ b/test/sys/test_select.rs @@ -2,6 +2,7 @@ use nix::sys::select::*; use nix::sys::signal::SigSet; use nix::sys::time::{TimeSpec, TimeValLike}; use nix::unistd::{pipe, write}; +use std::os::unix::io::{AsRawFd, BorrowedFd, FromRawFd, OwnedFd}; #[test] pub fn test_pselect() { @@ -9,11 +10,13 @@ pub fn test_pselect() { let (r1, w1) = pipe().unwrap(); write(w1, b"hi!").unwrap(); + let r1 = unsafe { OwnedFd::from_raw_fd(r1) }; let (r2, _w2) = pipe().unwrap(); + let r2 = unsafe { OwnedFd::from_raw_fd(r2) }; let mut fd_set = FdSet::new(); - fd_set.insert(r1); - fd_set.insert(r2); + fd_set.insert(&r1); + fd_set.insert(&r2); let timeout = TimeSpec::seconds(10); let sigmask = SigSet::empty(); @@ -21,25 +24,27 @@ pub fn test_pselect() { 1, pselect(None, &mut fd_set, None, None, &timeout, &sigmask).unwrap() ); - assert!(fd_set.contains(r1)); - assert!(!fd_set.contains(r2)); + assert!(fd_set.contains(&r1)); + assert!(!fd_set.contains(&r2)); } #[test] pub fn test_pselect_nfds2() { let (r1, w1) = pipe().unwrap(); write(w1, b"hi!").unwrap(); + let r1 = unsafe { OwnedFd::from_raw_fd(r1) }; let (r2, _w2) = pipe().unwrap(); + let r2 = unsafe { OwnedFd::from_raw_fd(r2) }; let mut fd_set = FdSet::new(); - fd_set.insert(r1); - fd_set.insert(r2); + fd_set.insert(&r1); + fd_set.insert(&r2); let timeout = TimeSpec::seconds(10); assert_eq!( 1, pselect( - ::std::cmp::max(r1, r2) + 1, + std::cmp::max(r1.as_raw_fd(), r2.as_raw_fd()) + 1, &mut fd_set, None, None, @@ -48,8 +53,8 @@ pub fn test_pselect_nfds2() { ) .unwrap() ); - assert!(fd_set.contains(r1)); - assert!(!fd_set.contains(r2)); + assert!(fd_set.contains(&r1)); + assert!(!fd_set.contains(&r2)); } macro_rules! generate_fdset_bad_fd_tests { @@ -58,17 +63,13 @@ macro_rules! generate_fdset_bad_fd_tests { #[test] #[should_panic] fn $method() { - FdSet::new().$method($fd); + let bad_fd = unsafe{BorrowedFd::borrow_raw($fd)}; + FdSet::new().$method(&bad_fd); } )* } } -mod test_fdset_negative_fd { - use super::*; - generate_fdset_bad_fd_tests!(-1, insert, remove, contains); -} - mod test_fdset_too_large_fd { use super::*; use std::convert::TryInto; diff --git a/test/sys/test_signal.rs b/test/sys/test_signal.rs index 3ad14f4..ca25ff9 100644 --- a/test/sys/test_signal.rs +++ b/test/sys/test_signal.rs @@ -54,9 +54,8 @@ fn test_sigprocmask() { // test don't make sense. assert!( !old_signal_set.contains(SIGNAL), - "the {:?} signal is already blocked, please change to a \ - different one", - SIGNAL + "the {SIGNAL:?} signal is already blocked, please change to a \ + different one" ); // Now block the signal. @@ -71,8 +70,7 @@ fn test_sigprocmask() { .expect("expect to be able to retrieve old signals"); assert!( old_signal_set.contains(SIGNAL), - "expected the {:?} to be blocked", - SIGNAL + "expected the {SIGNAL:?} to be blocked" ); // Reset the signal. @@ -80,9 +78,7 @@ fn test_sigprocmask() { .expect("expect to be able to block signals"); } -lazy_static! { - static ref SIGNALED: AtomicBool = AtomicBool::new(false); -} +static SIGNALED: AtomicBool = AtomicBool::new(false); extern "C" fn test_sigaction_handler(signal: libc::c_int) { let signal = Signal::try_from(signal).unwrap(); diff --git a/test/sys/test_socket.rs b/test/sys/test_socket.rs index 5adc77e..ed1686e 100644 --- a/test/sys/test_socket.rs +++ b/test/sys/test_socket.rs @@ -1,74 +1,16 @@ #[cfg(any(target_os = "linux", target_os = "android"))] use crate::*; -use libc::{c_char, sockaddr_storage}; -#[allow(deprecated)] -use nix::sys::socket::InetAddr; -use nix::sys::socket::{ - getsockname, sockaddr, sockaddr_in6, AddressFamily, UnixAddr, -}; +use libc::c_char; +use nix::sys::socket::{getsockname, AddressFamily, UnixAddr}; use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; -use std::mem::{self, MaybeUninit}; -use std::net::{self, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; -use std::os::unix::io::RawFd; +use std::net::{SocketAddrV4, SocketAddrV6}; +use std::os::unix::io::{AsRawFd, RawFd}; use std::path::Path; use std::slice; use std::str::FromStr; -#[allow(deprecated)] -#[test] -pub fn test_inetv4_addr_to_sock_addr() { - let actual: net::SocketAddr = FromStr::from_str("127.0.0.1:3000").unwrap(); - let addr = InetAddr::from_std(&actual); - - match addr { - InetAddr::V4(addr) => { - let ip: u32 = 0x7f00_0001; - let port: u16 = 3000; - let saddr = addr.sin_addr.s_addr; - - assert_eq!(saddr, ip.to_be()); - assert_eq!(addr.sin_port, port.to_be()); - } - _ => panic!("nope"), - } - - assert_eq!(addr.to_string(), "127.0.0.1:3000"); - - let inet = addr.to_std(); - assert_eq!(actual, inet); -} - -#[allow(deprecated)] -#[test] -pub fn test_inetv4_addr_roundtrip_sockaddr_storage_to_addr() { - use nix::sys::socket::{sockaddr_storage_to_addr, SockAddr}; - - let actual: net::SocketAddr = FromStr::from_str("127.0.0.1:3000").unwrap(); - let addr = InetAddr::from_std(&actual); - let sockaddr = SockAddr::new_inet(addr); - - let (storage, ffi_size) = { - let mut storage = MaybeUninit::::zeroed(); - let storage_ptr = storage.as_mut_ptr().cast::(); - let (ffi_ptr, ffi_size) = sockaddr.as_ffi_pair(); - assert_eq!(mem::size_of::(), ffi_size as usize); - unsafe { - storage_ptr.copy_from_nonoverlapping(ffi_ptr as *const sockaddr, 1); - (storage.assume_init(), ffi_size) - } - }; - - let from_storage = - sockaddr_storage_to_addr(&storage, ffi_size as usize).unwrap(); - assert_eq!(from_storage, sockaddr); - let from_storage = - sockaddr_storage_to_addr(&storage, mem::size_of::()) - .unwrap(); - assert_eq!(from_storage, sockaddr); -} - -#[cfg(any(target_os = "linux"))] +#[cfg(target_os = "linux")] #[cfg_attr(qemu, ignore)] #[test] pub fn test_timestamping() { @@ -96,9 +38,9 @@ pub fn test_timestamping() { None, ) .unwrap(); - nix::sys::socket::bind(rsock, &sock_addr).unwrap(); + nix::sys::socket::bind(rsock.as_raw_fd(), &sock_addr).unwrap(); - setsockopt(rsock, Timestamping, &TimestampingFlag::all()).unwrap(); + setsockopt(&rsock, Timestamping, &TimestampingFlag::all()).unwrap(); let sbuf = [0u8; 2048]; let mut rbuf = [0u8; 2048]; @@ -107,8 +49,10 @@ pub fn test_timestamping() { let mut iov2 = [IoSliceMut::new(&mut rbuf)]; let mut cmsg = cmsg_space!(nix::sys::socket::Timestamps); - sendmsg(ssock, &iov1, &[], flags, Some(&sock_addr)).unwrap(); - let recv = recvmsg::<()>(rsock, &mut iov2, Some(&mut cmsg), flags).unwrap(); + sendmsg(ssock.as_raw_fd(), &iov1, &[], flags, Some(&sock_addr)).unwrap(); + let recv = + recvmsg::<()>(rsock.as_raw_fd(), &mut iov2, Some(&mut cmsg), flags) + .unwrap(); let mut ts = None; for c in recv.cmsgs() { @@ -128,44 +72,6 @@ pub fn test_timestamping() { assert!(std::time::Duration::from(diff).as_secs() < 60); } -#[allow(deprecated)] -#[test] -pub fn test_inetv6_addr_roundtrip_sockaddr_storage_to_addr() { - use nix::sys::socket::{sockaddr_storage_to_addr, SockAddr}; - - let port: u16 = 3000; - let flowinfo: u32 = 1; - let scope_id: u32 = 2; - let ip: Ipv6Addr = "fe80::1".parse().unwrap(); - - let actual = - SocketAddr::V6(SocketAddrV6::new(ip, port, flowinfo, scope_id)); - let addr = InetAddr::from_std(&actual); - let sockaddr = SockAddr::new_inet(addr); - - let (storage, ffi_size) = { - let mut storage = MaybeUninit::::zeroed(); - let storage_ptr = storage.as_mut_ptr().cast::(); - let (ffi_ptr, ffi_size) = sockaddr.as_ffi_pair(); - assert_eq!(mem::size_of::(), ffi_size as usize); - unsafe { - storage_ptr.copy_from_nonoverlapping( - (ffi_ptr as *const sockaddr).cast::(), - 1, - ); - (storage.assume_init(), ffi_size) - } - }; - - let from_storage = - sockaddr_storage_to_addr(&storage, ffi_size as usize).unwrap(); - assert_eq!(from_storage, sockaddr); - let from_storage = - sockaddr_storage_to_addr(&storage, mem::size_of::()) - .unwrap(); - assert_eq!(from_storage, sockaddr); -} - #[test] pub fn test_path_to_sock_addr() { let path = "/foo/bar"; @@ -275,8 +181,11 @@ pub fn test_getsockname() { ) .expect("socket failed"); let sockaddr = UnixAddr::new(&sockname).unwrap(); - bind(sock, &sockaddr).expect("bind failed"); - assert_eq!(sockaddr, getsockname(sock).expect("getsockname failed")); + bind(sock.as_raw_fd(), &sockaddr).expect("bind failed"); + assert_eq!( + sockaddr, + getsockname(sock.as_raw_fd()).expect("getsockname failed") + ); } #[test] @@ -291,13 +200,53 @@ pub fn test_socketpair() { SockFlag::empty(), ) .unwrap(); - write(fd1, b"hello").unwrap(); + write(fd1.as_raw_fd(), b"hello").unwrap(); let mut buf = [0; 5]; - read(fd2, &mut buf).unwrap(); + read(fd2.as_raw_fd(), &mut buf).unwrap(); assert_eq!(&buf[..], b"hello"); } +#[test] +pub fn test_recvmsg_sockaddr_un() { + use nix::sys::socket::{ + self, bind, socket, AddressFamily, MsgFlags, SockFlag, SockType, + }; + + let tempdir = tempfile::tempdir().unwrap(); + let sockname = tempdir.path().join("sock"); + let sock = socket( + AddressFamily::Unix, + SockType::Datagram, + SockFlag::empty(), + None, + ) + .expect("socket failed"); + let sockaddr = UnixAddr::new(&sockname).unwrap(); + bind(sock.as_raw_fd(), &sockaddr).expect("bind failed"); + + // Send a message + let send_buffer = "hello".as_bytes(); + if let Err(e) = socket::sendmsg( + sock.as_raw_fd(), + &[std::io::IoSlice::new(send_buffer)], + &[], + MsgFlags::empty(), + Some(&sockaddr), + ) { + crate::skip!("Couldn't send ({e:?}), so skipping test"); + } + + // Receive the message + let mut recv_buffer = [0u8; 32]; + let mut iov = [std::io::IoSliceMut::new(&mut recv_buffer)]; + let received = + socket::recvmsg(sock.as_raw_fd(), &mut iov, None, MsgFlags::empty()) + .unwrap(); + // Check the address in the received message + assert_eq!(sockaddr, received.address.unwrap()); +} + #[test] pub fn test_std_conversions() { use nix::sys::socket::*; @@ -361,7 +310,7 @@ mod recvfrom { ) .unwrap(); // Ignore from for stream sockets - let _ = sendrecv(fd1, fd2, send, |_, _| {}); + let _ = sendrecv(fd1.as_raw_fd(), fd2.as_raw_fd(), send, |_, _| {}); } #[test] @@ -375,7 +324,7 @@ mod recvfrom { None, ) .unwrap(); - bind(rsock, &sock_addr).unwrap(); + bind(rsock.as_raw_fd(), &sock_addr).unwrap(); let ssock = socket( AddressFamily::Inet, SockType::Datagram, @@ -384,9 +333,9 @@ mod recvfrom { ) .expect("send socket failed"); let from = sendrecv( - rsock, - ssock, - move |s, m, flags| sendto(s, m, &sock_addr, flags), + rsock.as_raw_fd(), + ssock.as_raw_fd(), + move |s, m, flags| sendto(s.as_raw_fd(), m, &sock_addr, flags), |_, _| {}, ); // UDP sockets should set the from address @@ -420,10 +369,10 @@ mod recvfrom { ) .unwrap(); - setsockopt(rsock, UdpGsoSegment, &(segment_size as _)) + setsockopt(&rsock, UdpGsoSegment, &(segment_size as _)) .expect("setsockopt UDP_SEGMENT failed"); - bind(rsock, &sock_addr).unwrap(); + bind(rsock.as_raw_fd(), &sock_addr).unwrap(); let ssock = socket( AddressFamily::Inet, SockType::Datagram, @@ -435,12 +384,18 @@ mod recvfrom { let mut num_packets_received: i32 = 0; sendrecv( - rsock, - ssock, + rsock.as_raw_fd(), + ssock.as_raw_fd(), move |s, m, flags| { let iov = [IoSlice::new(m)]; let cmsg = ControlMessage::UdpGsoSegments(&segment_size); - sendmsg(s, &iov, &[cmsg], flags, Some(&sock_addr)) + sendmsg( + s.as_raw_fd(), + &iov, + &[cmsg], + flags, + Some(&sock_addr), + ) }, { let num_packets_received_ref = &mut num_packets_received; @@ -477,7 +432,7 @@ mod recvfrom { ) .unwrap(); - setsockopt(rsock, UdpGroSegment, &true) + setsockopt(&rsock, UdpGroSegment, &true) .expect("setsockopt UDP_GRO failed"); } } @@ -504,7 +459,7 @@ mod recvfrom { None, ) .unwrap(); - bind(rsock, &sock_addr).unwrap(); + bind(rsock.as_raw_fd(), &sock_addr).unwrap(); let ssock = socket( AddressFamily::Inet, SockType::Datagram, @@ -514,8 +469,8 @@ mod recvfrom { .expect("send socket failed"); let from = sendrecv( - rsock, - ssock, + rsock.as_raw_fd(), + ssock.as_raw_fd(), move |s, m, flags| { let batch_size = 15; let mut iovs = Vec::with_capacity(1 + batch_size); @@ -573,7 +528,7 @@ mod recvfrom { None, ) .unwrap(); - bind(rsock, &sock_addr).unwrap(); + bind(rsock.as_raw_fd(), &sock_addr).unwrap(); let ssock = socket( AddressFamily::Inet, SockType::Datagram, @@ -584,8 +539,13 @@ mod recvfrom { let send_thread = thread::spawn(move || { for _ in 0..NUM_MESSAGES_SENT { - sendto(ssock, &DATA[..], &sock_addr, MsgFlags::empty()) - .unwrap(); + sendto( + ssock.as_raw_fd(), + &DATA[..], + &sock_addr, + MsgFlags::empty(), + ) + .unwrap(); } }); @@ -602,10 +562,15 @@ mod recvfrom { let mut data = MultiHeaders::::preallocate(msgs.len(), None); - let res: Vec> = - recvmmsg(rsock, &mut data, msgs.iter(), MsgFlags::empty(), None) - .expect("recvmmsg") - .collect(); + let res: Vec> = recvmmsg( + rsock.as_raw_fd(), + &mut data, + msgs.iter(), + MsgFlags::empty(), + None, + ) + .expect("recvmmsg") + .collect(); assert_eq!(res.len(), DATA.len()); for RecvMsg { address, bytes, .. } in res.into_iter() { @@ -644,7 +609,7 @@ mod recvfrom { None, ) .unwrap(); - bind(rsock, &sock_addr).unwrap(); + bind(rsock.as_raw_fd(), &sock_addr).unwrap(); let ssock = socket( AddressFamily::Inet, SockType::Datagram, @@ -655,8 +620,13 @@ mod recvfrom { let send_thread = thread::spawn(move || { for _ in 0..NUM_MESSAGES_SENT { - sendto(ssock, &DATA[..], &sock_addr, MsgFlags::empty()) - .unwrap(); + sendto( + ssock.as_raw_fd(), + &DATA[..], + &sock_addr, + MsgFlags::empty(), + ) + .unwrap(); } }); // Ensure we've sent all the messages before continuing so `recvmmsg` @@ -681,7 +651,7 @@ mod recvfrom { ); let res: Vec> = recvmmsg( - rsock, + rsock.as_raw_fd(), &mut data, msgs.iter(), MsgFlags::MSG_DONTWAIT, @@ -717,12 +687,12 @@ mod recvfrom { None, ) .expect("receive socket failed"); - match bind(rsock, &raddr) { + match bind(rsock.as_raw_fd(), &raddr) { Err(Errno::EADDRNOTAVAIL) => { println!("IPv6 not available, skipping test."); return; } - Err(e) => panic!("bind: {}", e), + Err(e) => panic!("bind: {e}"), Ok(()) => (), } let ssock = socket( @@ -732,11 +702,11 @@ mod recvfrom { None, ) .expect("send socket failed"); - bind(ssock, &saddr).unwrap(); + bind(ssock.as_raw_fd(), &saddr).unwrap(); let from = sendrecv( - rsock, - ssock, - move |s, m, flags| sendto(s, m, &raddr, flags), + rsock.as_raw_fd(), + ssock.as_raw_fd(), + move |s, m, flags| sendto(s.as_raw_fd(), m, &raddr, flags), |_, _| {}, ); assert_eq!(AddressFamily::Inet6, from.unwrap().family().unwrap()); @@ -758,7 +728,7 @@ pub fn test_recvmsg_ebadf() { let mut iov = [IoSliceMut::new(&mut buf[..])]; let fd = -1; // Bad file descriptor - let r = recvmsg::<()>(fd, &mut iov, None, MsgFlags::empty()); + let r = recvmsg::<()>(fd.as_raw_fd(), &mut iov, None, MsgFlags::empty()); assert_eq!(r.err().unwrap(), Errno::EBADF); } @@ -790,11 +760,17 @@ pub fn test_scm_rights() { let fds = [r]; let cmsg = ControlMessage::ScmRights(&fds); assert_eq!( - sendmsg::<()>(fd1, &iov, &[cmsg], MsgFlags::empty(), None).unwrap(), + sendmsg::<()>( + fd1.as_raw_fd(), + &iov, + &[cmsg], + MsgFlags::empty(), + None + ) + .unwrap(), 5 ); close(r).unwrap(); - close(fd1).unwrap(); } { @@ -803,7 +779,7 @@ pub fn test_scm_rights() { let mut iov = [IoSliceMut::new(&mut buf[..])]; let mut cmsgspace = cmsg_space!([RawFd; 1]); let msg = recvmsg::<()>( - fd2, + fd2.as_raw_fd(), &mut iov, Some(&mut cmsgspace), MsgFlags::empty(), @@ -823,14 +799,13 @@ pub fn test_scm_rights() { assert!(!msg .flags .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); - close(fd2).unwrap(); } let received_r = received_r.expect("Did not receive passed fd"); // Ensure that the received file descriptor works - write(w, b"world").unwrap(); + write(w.as_raw_fd(), b"world").unwrap(); let mut buf = [0u8; 5]; - read(received_r, &mut buf).unwrap(); + read(received_r.as_raw_fd(), &mut buf).unwrap(); assert_eq!(&buf[..], b"world"); close(received_r).unwrap(); close(w).unwrap(); @@ -874,25 +849,32 @@ pub fn test_af_alg_cipher() { .expect("socket failed"); let sockaddr = AlgAddr::new(alg_type, alg_name); - bind(sock, &sockaddr).expect("bind failed"); + bind(sock.as_raw_fd(), &sockaddr).expect("bind failed"); assert_eq!(sockaddr.alg_name().to_string_lossy(), alg_name); assert_eq!(sockaddr.alg_type().to_string_lossy(), alg_type); - setsockopt(sock, AlgSetKey::default(), &key).expect("setsockopt"); - let session_socket = accept(sock).expect("accept failed"); + setsockopt(&sock, AlgSetKey::default(), &key).expect("setsockopt"); + let session_socket = accept(sock.as_raw_fd()).expect("accept failed"); let msgs = [ ControlMessage::AlgSetOp(&libc::ALG_OP_ENCRYPT), ControlMessage::AlgSetIv(iv.as_slice()), ]; let iov = IoSlice::new(&payload); - sendmsg::<()>(session_socket, &[iov], &msgs, MsgFlags::empty(), None) - .expect("sendmsg encrypt"); + sendmsg::<()>( + session_socket.as_raw_fd(), + &[iov], + &msgs, + MsgFlags::empty(), + None, + ) + .expect("sendmsg encrypt"); // allocate buffer for encrypted data let mut encrypted = vec![0u8; payload_len]; - let num_bytes = read(session_socket, &mut encrypted).expect("read encrypt"); + let num_bytes = + read(session_socket.as_raw_fd(), &mut encrypted).expect("read encrypt"); assert_eq!(num_bytes, payload_len); let iov = IoSlice::new(&encrypted); @@ -903,12 +885,19 @@ pub fn test_af_alg_cipher() { ControlMessage::AlgSetOp(&libc::ALG_OP_DECRYPT), ControlMessage::AlgSetIv(iv.as_slice()), ]; - sendmsg::<()>(session_socket, &[iov], &msgs, MsgFlags::empty(), None) - .expect("sendmsg decrypt"); + sendmsg::<()>( + session_socket.as_raw_fd(), + &[iov], + &msgs, + MsgFlags::empty(), + None, + ) + .expect("sendmsg decrypt"); // allocate buffer for decrypted data let mut decrypted = vec![0u8; payload_len]; - let num_bytes = read(session_socket, &mut decrypted).expect("read decrypt"); + let num_bytes = + read(session_socket.as_raw_fd(), &mut decrypted).expect("read decrypt"); assert_eq!(num_bytes, payload_len); assert_eq!(decrypted, payload); @@ -927,7 +916,7 @@ pub fn test_af_alg_aead() { accept, bind, sendmsg, setsockopt, socket, AddressFamily, AlgAddr, ControlMessage, MsgFlags, SockFlag, SockType, }; - use nix::unistd::{close, read}; + use nix::unistd::read; use std::io::IoSlice; skip_if_cirrus!("Fails for an unknown reason Cirrus CI. Bug #1352"); @@ -969,12 +958,13 @@ pub fn test_af_alg_aead() { .expect("socket failed"); let sockaddr = AlgAddr::new(alg_type, alg_name); - bind(sock, &sockaddr).expect("bind failed"); + bind(sock.as_raw_fd(), &sockaddr).expect("bind failed"); - setsockopt(sock, AlgSetAeadAuthSize, &auth_size) + setsockopt(&sock, AlgSetAeadAuthSize, &auth_size) .expect("setsockopt AlgSetAeadAuthSize"); - setsockopt(sock, AlgSetKey::default(), &key).expect("setsockopt AlgSetKey"); - let session_socket = accept(sock).expect("accept failed"); + setsockopt(&sock, AlgSetKey::default(), &key) + .expect("setsockopt AlgSetKey"); + let session_socket = accept(sock.as_raw_fd()).expect("accept failed"); let msgs = [ ControlMessage::AlgSetOp(&ALG_OP_ENCRYPT), @@ -983,15 +973,21 @@ pub fn test_af_alg_aead() { ]; let iov = IoSlice::new(&payload); - sendmsg::<()>(session_socket, &[iov], &msgs, MsgFlags::empty(), None) - .expect("sendmsg encrypt"); + sendmsg::<()>( + session_socket.as_raw_fd(), + &[iov], + &msgs, + MsgFlags::empty(), + None, + ) + .expect("sendmsg encrypt"); // allocate buffer for encrypted data let mut encrypted = vec![0u8; (assoc_size as usize) + payload_len + auth_size]; - let num_bytes = read(session_socket, &mut encrypted).expect("read encrypt"); + let num_bytes = + read(session_socket.as_raw_fd(), &mut encrypted).expect("read encrypt"); assert_eq!(num_bytes, payload_len + auth_size + (assoc_size as usize)); - close(session_socket).expect("close"); for i in 0..assoc_size { encrypted[i as usize] = 10; @@ -1001,15 +997,21 @@ pub fn test_af_alg_aead() { let iv = vec![1u8; iv_len]; - let session_socket = accept(sock).expect("accept failed"); + let session_socket = accept(sock.as_raw_fd()).expect("accept failed"); let msgs = [ ControlMessage::AlgSetOp(&ALG_OP_DECRYPT), ControlMessage::AlgSetIv(iv.as_slice()), ControlMessage::AlgSetAeadAssoclen(&assoc_size), ]; - sendmsg::<()>(session_socket, &[iov], &msgs, MsgFlags::empty(), None) - .expect("sendmsg decrypt"); + sendmsg::<()>( + session_socket.as_raw_fd(), + &[iov], + &msgs, + MsgFlags::empty(), + None, + ) + .expect("sendmsg decrypt"); // allocate buffer for decrypted data let mut decrypted = @@ -1020,7 +1022,8 @@ pub fn test_af_alg_aead() { // Do not block on read, as we may have fewer bytes than buffer size fcntl(session_socket, FcntlArg::F_SETFL(OFlag::O_NONBLOCK)) .expect("fcntl non_blocking"); - let num_bytes = read(session_socket, &mut decrypted).expect("read decrypt"); + let num_bytes = + read(session_socket.as_raw_fd(), &mut decrypted).expect("read decrypt"); assert!(num_bytes >= payload_len + (assoc_size as usize)); assert_eq!( @@ -1056,7 +1059,7 @@ pub fn test_sendmsg_ipv4packetinfo() { let sock_addr = SockaddrIn::new(127, 0, 0, 1, 4000); - bind(sock, &sock_addr).expect("bind failed"); + bind(sock.as_raw_fd(), &sock_addr).expect("bind failed"); let slice = [1u8, 2, 3, 4, 5, 6, 7, 8]; let iov = [IoSlice::new(&slice)]; @@ -1078,8 +1081,14 @@ pub fn test_sendmsg_ipv4packetinfo() { let cmsg = [ControlMessage::Ipv4PacketInfo(&pi)]; - sendmsg(sock, &iov, &cmsg, MsgFlags::empty(), Some(&sock_addr)) - .expect("sendmsg"); + sendmsg( + sock.as_raw_fd(), + &iov, + &cmsg, + MsgFlags::empty(), + Some(&sock_addr), + ) + .expect("sendmsg"); } // Verify `ControlMessage::Ipv6PacketInfo` for `sendmsg`. @@ -1116,7 +1125,7 @@ pub fn test_sendmsg_ipv6packetinfo() { let std_sa = SocketAddrV6::from_str("[::1]:6000").unwrap(); let sock_addr: SockaddrIn6 = SockaddrIn6::from(std_sa); - if let Err(Errno::EADDRNOTAVAIL) = bind(sock, &sock_addr) { + if let Err(Errno::EADDRNOTAVAIL) = bind(sock.as_raw_fd(), &sock_addr) { println!("IPv6 not available, skipping test."); return; } @@ -1132,7 +1141,7 @@ pub fn test_sendmsg_ipv6packetinfo() { let cmsg = [ControlMessage::Ipv6PacketInfo(&pi)]; sendmsg::( - sock, + sock.as_raw_fd(), &iov, &cmsg, MsgFlags::empty(), @@ -1172,8 +1181,8 @@ pub fn test_sendmsg_ipv4sendsrcaddr() { .expect("socket failed"); let unspec_sock_addr = SockaddrIn::new(0, 0, 0, 0, 0); - bind(sock, &unspec_sock_addr).expect("bind failed"); - let bound_sock_addr: SockaddrIn = getsockname(sock).unwrap(); + bind(sock.as_raw_fd(), &unspec_sock_addr).expect("bind failed"); + let bound_sock_addr: SockaddrIn = getsockname(sock.as_raw_fd()).unwrap(); let localhost_sock_addr: SockaddrIn = SockaddrIn::new(127, 0, 0, 1, bound_sock_addr.port()); @@ -1184,7 +1193,7 @@ pub fn test_sendmsg_ipv4sendsrcaddr() { )]; sendmsg( - sock, + sock.as_raw_fd(), &iov, &cmsg, MsgFlags::empty(), @@ -1261,7 +1270,6 @@ pub fn test_sendmsg_empty_cmsgs() { recvmsg, sendmsg, socketpair, AddressFamily, MsgFlags, SockFlag, SockType, }; - use nix::unistd::close; use std::io::{IoSlice, IoSliceMut}; let (fd1, fd2) = socketpair( @@ -1275,10 +1283,10 @@ pub fn test_sendmsg_empty_cmsgs() { { let iov = [IoSlice::new(b"hello")]; assert_eq!( - sendmsg::<()>(fd1, &iov, &[], MsgFlags::empty(), None).unwrap(), + sendmsg::<()>(fd1.as_raw_fd(), &iov, &[], MsgFlags::empty(), None) + .unwrap(), 5 ); - close(fd1).unwrap(); } { @@ -1287,7 +1295,7 @@ pub fn test_sendmsg_empty_cmsgs() { let mut cmsgspace = cmsg_space!([RawFd; 1]); let msg = recvmsg::<()>( - fd2, + fd2.as_raw_fd(), &mut iov, Some(&mut cmsgspace), MsgFlags::empty(), @@ -1301,7 +1309,6 @@ pub fn test_sendmsg_empty_cmsgs() { .flags .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); assert_eq!(msg.bytes, 5); - close(fd2).unwrap(); } } @@ -1319,7 +1326,7 @@ fn test_scm_credentials() { }; #[cfg(any(target_os = "android", target_os = "linux"))] use nix::sys::socket::{setsockopt, sockopt::PassCred}; - use nix::unistd::{close, getgid, getpid, getuid}; + use nix::unistd::{getgid, getpid, getuid}; use std::io::{IoSlice, IoSliceMut}; let (send, recv) = socketpair( @@ -1330,7 +1337,7 @@ fn test_scm_credentials() { ) .unwrap(); #[cfg(any(target_os = "android", target_os = "linux"))] - setsockopt(recv, PassCred, &true).unwrap(); + setsockopt(&recv, PassCred, &true).unwrap(); { let iov = [IoSlice::new(b"hello")]; @@ -1341,11 +1348,16 @@ fn test_scm_credentials() { #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] let cmsg = ControlMessage::ScmCreds; assert_eq!( - sendmsg::<()>(send, &iov, &[cmsg], MsgFlags::empty(), None) - .unwrap(), + sendmsg::<()>( + send.as_raw_fd(), + &iov, + &[cmsg], + MsgFlags::empty(), + None + ) + .unwrap(), 5 ); - close(send).unwrap(); } { @@ -1354,7 +1366,7 @@ fn test_scm_credentials() { let mut cmsgspace = cmsg_space!(UnixCredentials); let msg = recvmsg::<()>( - recv, + recv.as_raw_fd(), &mut iov, Some(&mut cmsgspace), MsgFlags::empty(), @@ -1368,7 +1380,7 @@ fn test_scm_credentials() { ControlMessageOwned::ScmCredentials(cred) => cred, #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] ControlMessageOwned::ScmCreds(cred) => cred, - other => panic!("unexpected cmsg {:?}", other), + other => panic!("unexpected cmsg {other:?}"), }; assert!(received_cred.is_none()); assert_eq!(cred.pid(), getpid().as_raw()); @@ -1381,7 +1393,6 @@ fn test_scm_credentials() { assert!(!msg .flags .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); - close(recv).unwrap(); } } @@ -1427,7 +1438,7 @@ fn test_impl_scm_credentials_and_rights(mut space: Vec) { SockFlag::empty(), ) .unwrap(); - setsockopt(recv, PassCred, &true).unwrap(); + setsockopt(&recv, PassCred, &true).unwrap(); let (r, w) = pipe().unwrap(); let mut received_r: Option = None; @@ -1446,19 +1457,29 @@ fn test_impl_scm_credentials_and_rights(mut space: Vec) { ControlMessage::ScmRights(&fds), ]; assert_eq!( - sendmsg::<()>(send, &iov, &cmsgs, MsgFlags::empty(), None).unwrap(), + sendmsg::<()>( + send.as_raw_fd(), + &iov, + &cmsgs, + MsgFlags::empty(), + None + ) + .unwrap(), 5 ); close(r).unwrap(); - close(send).unwrap(); } { let mut buf = [0u8; 5]; let mut iov = [IoSliceMut::new(&mut buf[..])]; - let msg = - recvmsg::<()>(recv, &mut iov, Some(&mut space), MsgFlags::empty()) - .unwrap(); + let msg = recvmsg::<()>( + recv.as_raw_fd(), + &mut iov, + Some(&mut space), + MsgFlags::empty(), + ) + .unwrap(); let mut received_cred = None; assert_eq!(msg.cmsgs().count(), 2, "expected 2 cmsgs"); @@ -1485,14 +1506,13 @@ fn test_impl_scm_credentials_and_rights(mut space: Vec) { assert!(!msg .flags .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); - close(recv).unwrap(); } let received_r = received_r.expect("Did not receive passed fd"); // Ensure that the received file descriptor works - write(w, b"world").unwrap(); + write(w.as_raw_fd(), b"world").unwrap(); let mut buf = [0u8; 5]; - read(received_r, &mut buf).unwrap(); + read(received_r.as_raw_fd(), &mut buf).unwrap(); assert_eq!(&buf[..], b"world"); close(received_r).unwrap(); close(w).unwrap(); @@ -1503,7 +1523,7 @@ fn test_impl_scm_credentials_and_rights(mut space: Vec) { pub fn test_named_unixdomain() { use nix::sys::socket::{accept, bind, connect, listen, socket, UnixAddr}; use nix::sys::socket::{SockFlag, SockType}; - use nix::unistd::{close, read, write}; + use nix::unistd::{read, write}; use std::thread; let tempdir = tempfile::tempdir().unwrap(); @@ -1516,8 +1536,8 @@ pub fn test_named_unixdomain() { ) .expect("socket failed"); let sockaddr = UnixAddr::new(&sockname).unwrap(); - bind(s1, &sockaddr).expect("bind failed"); - listen(s1, 10).expect("listen failed"); + bind(s1.as_raw_fd(), &sockaddr).expect("bind failed"); + listen(&s1, 10).expect("listen failed"); let thr = thread::spawn(move || { let s2 = socket( @@ -1527,17 +1547,14 @@ pub fn test_named_unixdomain() { None, ) .expect("socket failed"); - connect(s2, &sockaddr).expect("connect failed"); - write(s2, b"hello").expect("write failed"); - close(s2).unwrap(); + connect(s2.as_raw_fd(), &sockaddr).expect("connect failed"); + write(s2.as_raw_fd(), b"hello").expect("write failed"); }); - let s3 = accept(s1).expect("accept failed"); + let s3 = accept(s1.as_raw_fd()).expect("accept failed"); let mut buf = [0; 5]; - read(s3, &mut buf).unwrap(); - close(s3).unwrap(); - close(s1).unwrap(); + read(s3.as_raw_fd(), &mut buf).unwrap(); thr.join().unwrap(); assert_eq!(&buf[..], b"hello"); @@ -1549,9 +1566,8 @@ pub fn test_named_unixdomain() { pub fn test_unnamed_unixdomain() { use nix::sys::socket::{getsockname, socketpair}; use nix::sys::socket::{SockFlag, SockType}; - use nix::unistd::close; - let (fd_1, fd_2) = socketpair( + let (fd_1, _fd_2) = socketpair( AddressFamily::Unix, SockType::Stream, None, @@ -1559,11 +1575,9 @@ pub fn test_unnamed_unixdomain() { ) .expect("socketpair failed"); - let addr_1: UnixAddr = getsockname(fd_1).expect("getsockname failed"); + let addr_1: UnixAddr = + getsockname(fd_1.as_raw_fd()).expect("getsockname failed"); assert!(addr_1.is_unnamed()); - - close(fd_1).unwrap(); - close(fd_2).unwrap(); } // Test creating and using unnamed unix domain addresses for autobinding sockets @@ -1572,7 +1586,6 @@ pub fn test_unnamed_unixdomain() { pub fn test_unnamed_unixdomain_autobind() { use nix::sys::socket::{bind, getsockname, socket}; use nix::sys::socket::{SockFlag, SockType}; - use nix::unistd::close; let fd = socket( AddressFamily::Unix, @@ -1584,16 +1597,15 @@ pub fn test_unnamed_unixdomain_autobind() { // unix(7): "If a bind(2) call specifies addrlen as `sizeof(sa_family_t)`, or [...], then the // socket is autobound to an abstract address" - bind(fd, &UnixAddr::new_unnamed()).expect("bind failed"); + bind(fd.as_raw_fd(), &UnixAddr::new_unnamed()).expect("bind failed"); - let addr: UnixAddr = getsockname(fd).expect("getsockname failed"); + let addr: UnixAddr = + getsockname(fd.as_raw_fd()).expect("getsockname failed"); let addr = addr.as_abstract().unwrap(); // changed from 8 to 5 bytes in Linux 2.3.15, and rust's minimum supported Linux version is 3.2 // (as of 2022-11) assert_eq!(addr.len(), 5); - - close(fd).unwrap(); } // Test creating and using named system control sockets @@ -1612,15 +1624,15 @@ pub fn test_syscontrol() { SockProtocol::KextControl, ) .expect("socket failed"); - SysControlAddr::from_name(fd, "com.apple.net.utun_control", 0) + SysControlAddr::from_name(fd.as_raw_fd(), "com.apple.net.utun_control", 0) .expect("resolving sys_control name failed"); assert_eq!( - SysControlAddr::from_name(fd, "foo.bar.lol", 0).err(), + SysControlAddr::from_name(fd.as_raw_fd(), "foo.bar.lol", 0).err(), Some(Errno::ENOENT) ); // requires root privileges - // connect(fd, &sockaddr).expect("connect failed"); + // connect(fd.as_raw_fd(), &sockaddr).expect("connect failed"); } #[cfg(any( @@ -1646,7 +1658,7 @@ fn loopback_address( Err(e) => { let stdioerr = io::stderr(); let mut handle = stdioerr.lock(); - writeln!(handle, "getifaddrs: {:?}", e).unwrap(); + writeln!(handle, "getifaddrs: {e:?}").unwrap(); return None; } }; @@ -1701,9 +1713,10 @@ pub fn test_recv_ipv4pktinfo() { None, ) .expect("receive socket failed"); - bind(receive, &lo).expect("bind failed"); - let sa: SockaddrIn = getsockname(receive).expect("getsockname failed"); - setsockopt(receive, Ipv4PacketInfo, &true).expect("setsockopt failed"); + bind(receive.as_raw_fd(), &lo).expect("bind failed"); + let sa: SockaddrIn = + getsockname(receive.as_raw_fd()).expect("getsockname failed"); + setsockopt(&receive, Ipv4PacketInfo, &true).expect("setsockopt failed"); { let slice = [1u8, 2, 3, 4, 5, 6, 7, 8]; @@ -1716,7 +1729,7 @@ pub fn test_recv_ipv4pktinfo() { None, ) .expect("send socket failed"); - sendmsg(send, &iov, &[], MsgFlags::empty(), Some(&sa)) + sendmsg(send.as_raw_fd(), &iov, &[], MsgFlags::empty(), Some(&sa)) .expect("sendmsg failed"); } @@ -1726,7 +1739,7 @@ pub fn test_recv_ipv4pktinfo() { let mut space = cmsg_space!(libc::in_pktinfo); let msg = recvmsg::<()>( - receive, + receive.as_raw_fd(), &mut iovec, Some(&mut space), MsgFlags::empty(), @@ -1795,11 +1808,12 @@ pub fn test_recvif() { None, ) .expect("receive socket failed"); - bind(receive, &lo).expect("bind failed"); - let sa: SockaddrIn = getsockname(receive).expect("getsockname failed"); - setsockopt(receive, Ipv4RecvIf, &true) + bind(receive.as_raw_fd(), &lo).expect("bind failed"); + let sa: SockaddrIn = + getsockname(receive.as_raw_fd()).expect("getsockname failed"); + setsockopt(&receive, Ipv4RecvIf, &true) .expect("setsockopt IP_RECVIF failed"); - setsockopt(receive, Ipv4RecvDstAddr, &true) + setsockopt(&receive, Ipv4RecvDstAddr, &true) .expect("setsockopt IP_RECVDSTADDR failed"); { @@ -1813,7 +1827,7 @@ pub fn test_recvif() { None, ) .expect("send socket failed"); - sendmsg(send, &iov, &[], MsgFlags::empty(), Some(&sa)) + sendmsg(send.as_raw_fd(), &iov, &[], MsgFlags::empty(), Some(&sa)) .expect("sendmsg failed"); } @@ -1822,7 +1836,7 @@ pub fn test_recvif() { let mut iovec = [IoSliceMut::new(&mut buf)]; let mut space = cmsg_space!(libc::sockaddr_dl, libc::in_addr); let msg = recvmsg::<()>( - receive, + receive.as_raw_fd(), &mut iovec, Some(&mut space), MsgFlags::empty(), @@ -1894,9 +1908,10 @@ pub fn test_recvif_ipv4() { None, ) .expect("receive socket failed"); - bind(receive, &lo).expect("bind failed"); - let sa: SockaddrIn = getsockname(receive).expect("getsockname failed"); - setsockopt(receive, Ipv4OrigDstAddr, &true) + bind(receive.as_raw_fd(), &lo).expect("bind failed"); + let sa: SockaddrIn = + getsockname(receive.as_raw_fd()).expect("getsockname failed"); + setsockopt(&receive, Ipv4OrigDstAddr, &true) .expect("setsockopt IP_ORIGDSTADDR failed"); { @@ -1910,7 +1925,7 @@ pub fn test_recvif_ipv4() { None, ) .expect("send socket failed"); - sendmsg(send, &iov, &[], MsgFlags::empty(), Some(&sa)) + sendmsg(send.as_raw_fd(), &iov, &[], MsgFlags::empty(), Some(&sa)) .expect("sendmsg failed"); } @@ -1919,7 +1934,7 @@ pub fn test_recvif_ipv4() { let mut iovec = [IoSliceMut::new(&mut buf)]; let mut space = cmsg_space!(libc::sockaddr_in); let msg = recvmsg::<()>( - receive, + receive.as_raw_fd(), &mut iovec, Some(&mut space), MsgFlags::empty(), @@ -1979,9 +1994,10 @@ pub fn test_recvif_ipv6() { None, ) .expect("receive socket failed"); - bind(receive, &lo).expect("bind failed"); - let sa: SockaddrIn6 = getsockname(receive).expect("getsockname failed"); - setsockopt(receive, Ipv6OrigDstAddr, &true) + bind(receive.as_raw_fd(), &lo).expect("bind failed"); + let sa: SockaddrIn6 = + getsockname(receive.as_raw_fd()).expect("getsockname failed"); + setsockopt(&receive, Ipv6OrigDstAddr, &true) .expect("setsockopt IP_ORIGDSTADDR failed"); { @@ -1995,7 +2011,7 @@ pub fn test_recvif_ipv6() { None, ) .expect("send socket failed"); - sendmsg(send, &iov, &[], MsgFlags::empty(), Some(&sa)) + sendmsg(send.as_raw_fd(), &iov, &[], MsgFlags::empty(), Some(&sa)) .expect("sendmsg failed"); } @@ -2004,7 +2020,7 @@ pub fn test_recvif_ipv6() { let mut iovec = [IoSliceMut::new(&mut buf)]; let mut space = cmsg_space!(libc::sockaddr_in6); let msg = recvmsg::<()>( - receive, + receive.as_raw_fd(), &mut iovec, Some(&mut space), MsgFlags::empty(), @@ -2084,9 +2100,10 @@ pub fn test_recv_ipv6pktinfo() { None, ) .expect("receive socket failed"); - bind(receive, &lo).expect("bind failed"); - let sa: SockaddrIn6 = getsockname(receive).expect("getsockname failed"); - setsockopt(receive, Ipv6RecvPacketInfo, &true).expect("setsockopt failed"); + bind(receive.as_raw_fd(), &lo).expect("bind failed"); + let sa: SockaddrIn6 = + getsockname(receive.as_raw_fd()).expect("getsockname failed"); + setsockopt(&receive, Ipv6RecvPacketInfo, &true).expect("setsockopt failed"); { let slice = [1u8, 2, 3, 4, 5, 6, 7, 8]; @@ -2099,7 +2116,7 @@ pub fn test_recv_ipv6pktinfo() { None, ) .expect("send socket failed"); - sendmsg(send, &iov, &[], MsgFlags::empty(), Some(&sa)) + sendmsg(send.as_raw_fd(), &iov, &[], MsgFlags::empty(), Some(&sa)) .expect("sendmsg failed"); } @@ -2109,7 +2126,7 @@ pub fn test_recv_ipv6pktinfo() { let mut space = cmsg_space!(libc::in6_pktinfo); let msg = recvmsg::<()>( - receive, + receive.as_raw_fd(), &mut iovec, Some(&mut space), MsgFlags::empty(), @@ -2136,63 +2153,92 @@ pub fn test_recv_ipv6pktinfo() { } #[cfg(any(target_os = "android", target_os = "linux"))] -#[cfg_attr(graviton, ignore = "Not supported by the CI environment")] #[test] pub fn test_vsock() { - use nix::errno::Errno; - use nix::sys::socket::{ - bind, connect, listen, socket, AddressFamily, SockFlag, SockType, - VsockAddr, - }; - use nix::unistd::close; - use std::thread; + use nix::sys::socket::SockaddrLike; + use nix::sys::socket::{AddressFamily, VsockAddr}; + use std::mem; let port: u32 = 3000; - let s1 = socket( - AddressFamily::Vsock, - SockType::Stream, - SockFlag::empty(), - None, - ) - .expect("socket failed"); + let addr_local = VsockAddr::new(libc::VMADDR_CID_LOCAL, port); + assert_eq!(addr_local.cid(), libc::VMADDR_CID_LOCAL); + assert_eq!(addr_local.port(), port); - // VMADDR_CID_HYPERVISOR is reserved, so we expect an EADDRNOTAVAIL error. - let sockaddr_hv = VsockAddr::new(libc::VMADDR_CID_HYPERVISOR, port); - assert_eq!(bind(s1, &sockaddr_hv).err(), Some(Errno::EADDRNOTAVAIL)); + let addr_any = VsockAddr::new(libc::VMADDR_CID_ANY, libc::VMADDR_PORT_ANY); + assert_eq!(addr_any.cid(), libc::VMADDR_CID_ANY); + assert_eq!(addr_any.port(), libc::VMADDR_PORT_ANY); - let sockaddr_any = VsockAddr::new(libc::VMADDR_CID_ANY, port); - assert_eq!(bind(s1, &sockaddr_any), Ok(())); - listen(s1, 10).expect("listen failed"); + assert_ne!(addr_local, addr_any); + assert_ne!(calculate_hash(&addr_local), calculate_hash(&addr_any)); - let thr = thread::spawn(move || { - let cid: u32 = libc::VMADDR_CID_HOST; + let addr1 = VsockAddr::new(libc::VMADDR_CID_HOST, port); + let addr2 = VsockAddr::new(libc::VMADDR_CID_HOST, port); + assert_eq!(addr1, addr2); + assert_eq!(calculate_hash(&addr1), calculate_hash(&addr2)); - let s2 = socket( - AddressFamily::Vsock, - SockType::Stream, - SockFlag::empty(), - None, + let addr3 = unsafe { + VsockAddr::from_raw( + addr2.as_ref() as *const libc::sockaddr_vm as *const libc::sockaddr, + Some(mem::size_of::().try_into().unwrap()), ) - .expect("socket failed"); + } + .unwrap(); + assert_eq!( + addr3.as_ref().svm_family, + AddressFamily::Vsock as libc::sa_family_t + ); + assert_eq!(addr3.as_ref().svm_cid, addr1.cid()); + assert_eq!(addr3.as_ref().svm_port, addr1.port()); +} - let sockaddr_host = VsockAddr::new(cid, port); +#[cfg(target_os = "macos")] +#[test] +pub fn test_vsock() { + use nix::sys::socket::SockaddrLike; + use nix::sys::socket::{AddressFamily, VsockAddr}; + use std::mem; - // The current implementation does not support loopback devices, so, - // for now, we expect a failure on the connect. - assert_ne!(connect(s2, &sockaddr_host), Ok(())); + let port: u32 = 3000; - close(s2).unwrap(); - }); + // macOS doesn't have a VMADDR_CID_LOCAL, so test with host again + let addr_host = VsockAddr::new(libc::VMADDR_CID_HOST, port); + assert_eq!(addr_host.cid(), libc::VMADDR_CID_HOST); + assert_eq!(addr_host.port(), port); - close(s1).unwrap(); - thr.join().unwrap(); + let addr_any = VsockAddr::new(libc::VMADDR_CID_ANY, libc::VMADDR_PORT_ANY); + assert_eq!(addr_any.cid(), libc::VMADDR_CID_ANY); + assert_eq!(addr_any.port(), libc::VMADDR_PORT_ANY); + + assert_ne!(addr_host, addr_any); + assert_ne!(calculate_hash(&addr_host), calculate_hash(&addr_any)); + + let addr1 = VsockAddr::new(libc::VMADDR_CID_HOST, port); + let addr2 = VsockAddr::new(libc::VMADDR_CID_HOST, port); + assert_eq!(addr1, addr2); + assert_eq!(calculate_hash(&addr1), calculate_hash(&addr2)); + + let addr3 = unsafe { + VsockAddr::from_raw( + addr2.as_ref() as *const libc::sockaddr_vm as *const libc::sockaddr, + Some(mem::size_of::().try_into().unwrap()), + ) + } + .unwrap(); + assert_eq!( + addr3.as_ref().svm_family, + AddressFamily::Vsock as libc::sa_family_t + ); + let cid = addr3.as_ref().svm_cid; + let port = addr3.as_ref().svm_port; + assert_eq!(cid, addr1.cid()); + assert_eq!(port, addr1.port()); } // Disable the test on emulated platforms because it fails in Cirrus-CI. Lack // of QEMU support is suspected. #[cfg_attr(qemu, ignore)] -#[cfg(all(target_os = "linux"))] +#[cfg(target_os = "linux")] #[test] fn test_recvmsg_timestampns() { use nix::sys::socket::*; @@ -2209,24 +2255,30 @@ fn test_recvmsg_timestampns() { None, ) .unwrap(); - setsockopt(in_socket, sockopt::ReceiveTimestampns, &true).unwrap(); + setsockopt(&in_socket, sockopt::ReceiveTimestampns, &true).unwrap(); let localhost = SockaddrIn::new(127, 0, 0, 1, 0); - bind(in_socket, &localhost).unwrap(); - let address: SockaddrIn = getsockname(in_socket).unwrap(); + bind(in_socket.as_raw_fd(), &localhost).unwrap(); + let address: SockaddrIn = getsockname(in_socket.as_raw_fd()).unwrap(); // Get initial time let time0 = SystemTime::now(); // Send the message let iov = [IoSlice::new(message)]; let flags = MsgFlags::empty(); - let l = sendmsg(in_socket, &iov, &[], flags, Some(&address)).unwrap(); + let l = sendmsg(in_socket.as_raw_fd(), &iov, &[], flags, Some(&address)) + .unwrap(); assert_eq!(message.len(), l); // Receive the message let mut buffer = vec![0u8; message.len()]; let mut cmsgspace = nix::cmsg_space!(TimeSpec); let mut iov = [IoSliceMut::new(&mut buffer)]; - let r = recvmsg::<()>(in_socket, &mut iov, Some(&mut cmsgspace), flags) - .unwrap(); + let r = recvmsg::<()>( + in_socket.as_raw_fd(), + &mut iov, + Some(&mut cmsgspace), + flags, + ) + .unwrap(); let rtime = match r.cmsgs().next() { Some(ControlMessageOwned::ScmTimestampns(rtime)) => rtime, Some(_) => panic!("Unexpected control message"), @@ -2240,14 +2292,12 @@ fn test_recvmsg_timestampns() { Duration::new(rtime.tv_sec() as u64, rtime.tv_nsec() as u32); assert!(time0.duration_since(UNIX_EPOCH).unwrap() <= rduration); assert!(rduration <= time1.duration_since(UNIX_EPOCH).unwrap()); - // Close socket - nix::unistd::close(in_socket).unwrap(); } // Disable the test on emulated platforms because it fails in Cirrus-CI. Lack // of QEMU support is suspected. #[cfg_attr(qemu, ignore)] -#[cfg(all(target_os = "linux"))] +#[cfg(target_os = "linux")] #[test] fn test_recvmmsg_timestampns() { use nix::sys::socket::*; @@ -2264,16 +2314,17 @@ fn test_recvmmsg_timestampns() { None, ) .unwrap(); - setsockopt(in_socket, sockopt::ReceiveTimestampns, &true).unwrap(); + setsockopt(&in_socket, sockopt::ReceiveTimestampns, &true).unwrap(); let localhost = SockaddrIn::from_str("127.0.0.1:0").unwrap(); - bind(in_socket, &localhost).unwrap(); - let address: SockaddrIn = getsockname(in_socket).unwrap(); + bind(in_socket.as_raw_fd(), &localhost).unwrap(); + let address: SockaddrIn = getsockname(in_socket.as_raw_fd()).unwrap(); // Get initial time let time0 = SystemTime::now(); // Send the message let iov = [IoSlice::new(message)]; let flags = MsgFlags::empty(); - let l = sendmsg(in_socket, &iov, &[], flags, Some(&address)).unwrap(); + let l = sendmsg(in_socket.as_raw_fd(), &iov, &[], flags, Some(&address)) + .unwrap(); assert_eq!(message.len(), l); // Receive the message let mut buffer = vec![0u8; message.len()]; @@ -2281,7 +2332,7 @@ fn test_recvmmsg_timestampns() { let iov = vec![[IoSliceMut::new(&mut buffer)]]; let mut data = MultiHeaders::preallocate(1, Some(cmsgspace)); let r: Vec> = - recvmmsg(in_socket, &mut data, iov.iter(), flags, None) + recvmmsg(in_socket.as_raw_fd(), &mut data, iov.iter(), flags, None) .unwrap() .collect(); let rtime = match r[0].cmsgs().next() { @@ -2297,8 +2348,6 @@ fn test_recvmmsg_timestampns() { Duration::new(rtime.tv_sec() as u64, rtime.tv_nsec() as u32); assert!(time0.duration_since(UNIX_EPOCH).unwrap() <= rduration); assert!(rduration <= time1.duration_since(UNIX_EPOCH).unwrap()); - // Close socket - nix::unistd::close(in_socket).unwrap(); } // Disable the test on emulated platforms because it fails in Cirrus-CI. Lack @@ -2331,16 +2380,16 @@ fn test_recvmsg_rxq_ovfl() { .unwrap(); let localhost = SockaddrIn::from_str("127.0.0.1:0").unwrap(); - bind(in_socket, &localhost).unwrap(); + bind(in_socket.as_raw_fd(), &localhost).unwrap(); - let address: SockaddrIn = getsockname(in_socket).unwrap(); - connect(out_socket, &address).unwrap(); + let address: SockaddrIn = getsockname(in_socket.as_raw_fd()).unwrap(); + connect(out_socket.as_raw_fd(), &address).unwrap(); // Set SO_RXQ_OVFL flag. - setsockopt(in_socket, RxqOvfl, &1).unwrap(); + setsockopt(&in_socket, RxqOvfl, &1).unwrap(); // Set the receiver buffer size to hold only 2 messages. - setsockopt(in_socket, RcvBuf, &bufsize).unwrap(); + setsockopt(&in_socket, RcvBuf, &bufsize).unwrap(); let mut drop_counter = 0; @@ -2351,8 +2400,14 @@ fn test_recvmsg_rxq_ovfl() { // Send the 3 messages (the receiver buffer can only hold 2 messages) // to create an overflow. for _ in 0..3 { - let l = - sendmsg(out_socket, &iov, &[], flags, Some(&address)).unwrap(); + let l = sendmsg( + out_socket.as_raw_fd(), + &iov, + &[], + flags, + Some(&address), + ) + .unwrap(); assert_eq!(message.len(), l); } @@ -2364,7 +2419,7 @@ fn test_recvmsg_rxq_ovfl() { let mut iov = [IoSliceMut::new(&mut buffer)]; match recvmsg::<()>( - in_socket, + in_socket.as_raw_fd(), &mut iov, Some(&mut cmsgspace), MsgFlags::MSG_DONTWAIT, @@ -2390,16 +2445,13 @@ fn test_recvmsg_rxq_ovfl() { // One packet lost. assert_eq!(drop_counter, 1); - - // Close sockets - nix::unistd::close(in_socket).unwrap(); - nix::unistd::close(out_socket).unwrap(); } #[cfg(any(target_os = "linux", target_os = "android",))] mod linux_errqueue { use super::FromStr; use nix::sys::socket::*; + use std::os::unix::io::AsRawFd; // Send a UDP datagram to a bogus destination address and observe an ICMP error (v4). // @@ -2443,7 +2495,7 @@ mod linux_errqueue { } *ext_err } else { - panic!("Unexpected control message {:?}", cmsg); + panic!("Unexpected control message {cmsg:?}"); } }, ) @@ -2494,7 +2546,7 @@ mod linux_errqueue { } *ext_err } else { - panic!("Unexpected control message {:?}", cmsg); + panic!("Unexpected control message {cmsg:?}"); } }, ) @@ -2520,15 +2572,15 @@ mod linux_errqueue { let sock_addr = SockaddrStorage::from(std_sa); let sock = socket(af, SockType::Datagram, SockFlag::SOCK_CLOEXEC, None) .unwrap(); - setsockopt(sock, opt, &true).unwrap(); + setsockopt(&sock, opt, &true).unwrap(); if let Err(e) = sendto( - sock, + sock.as_raw_fd(), MESSAGE_CONTENTS.as_bytes(), &sock_addr, MsgFlags::empty(), ) { assert_eq!(e, Errno::EADDRNOTAVAIL); - println!("{:?} not available, skipping test.", af); + println!("{af:?} not available, skipping test."); return; } @@ -2537,7 +2589,7 @@ mod linux_errqueue { let mut cspace = cmsg_space!(libc::sock_extended_err, SA); let msg = recvmsg( - sock, + sock.as_raw_fd(), &mut iovec, Some(&mut cspace), MsgFlags::MSG_ERRQUEUE, @@ -2600,7 +2652,7 @@ pub fn test_txtime() { clockid: libc::CLOCK_MONOTONIC, flags: 0, }; - setsockopt(ssock, sockopt::TxTime, &txtime_cfg).unwrap(); + setsockopt(&ssock, sockopt::TxTime, &txtime_cfg).unwrap(); let rsock = socket( AddressFamily::Inet, @@ -2609,7 +2661,7 @@ pub fn test_txtime() { None, ) .unwrap(); - bind(rsock, &sock_addr).unwrap(); + bind(rsock.as_raw_fd(), &sock_addr).unwrap(); let sbuf = [0u8; 2048]; let iov1 = [std::io::IoSlice::new(&sbuf)]; @@ -2619,10 +2671,17 @@ pub fn test_txtime() { let txtime = (now + delay).num_nanoseconds() as u64; let cmsg = ControlMessage::TxTime(&txtime); - sendmsg(ssock, &iov1, &[cmsg], MsgFlags::empty(), Some(&sock_addr)) - .unwrap(); + sendmsg( + ssock.as_raw_fd(), + &iov1, + &[cmsg], + MsgFlags::empty(), + Some(&sock_addr), + ) + .unwrap(); let mut rbuf = [0u8; 2048]; let mut iov2 = [std::io::IoSliceMut::new(&mut rbuf)]; - recvmsg::<()>(rsock, &mut iov2, None, MsgFlags::empty()).unwrap(); + recvmsg::<()>(rsock.as_raw_fd(), &mut iov2, None, MsgFlags::empty()) + .unwrap(); } diff --git a/test/sys/test_sockopt.rs b/test/sys/test_sockopt.rs index 34bef94..0e34917 100644 --- a/test/sys/test_sockopt.rs +++ b/test/sys/test_sockopt.rs @@ -5,6 +5,7 @@ use nix::sys::socket::{ SockProtocol, SockType, }; use rand::{thread_rng, Rng}; +use std::os::unix::io::AsRawFd; // NB: FreeBSD supports LOCAL_PEERCRED for SOCK_SEQPACKET, but OSX does not. #[cfg(any(target_os = "dragonfly", target_os = "freebsd",))] @@ -22,7 +23,7 @@ pub fn test_local_peercred_seqpacket() { SockFlag::empty(), ) .unwrap(); - let xucred = getsockopt(fd1, sockopt::LocalPeerCred).unwrap(); + let xucred = getsockopt(&fd1, sockopt::LocalPeerCred).unwrap(); assert_eq!(xucred.version(), 0); assert_eq!(Uid::from_raw(xucred.uid()), Uid::current()); assert_eq!(Gid::from_raw(xucred.groups()[0]), Gid::current()); @@ -48,12 +49,28 @@ pub fn test_local_peercred_stream() { SockFlag::empty(), ) .unwrap(); - let xucred = getsockopt(fd1, sockopt::LocalPeerCred).unwrap(); + let xucred = getsockopt(&fd1, sockopt::LocalPeerCred).unwrap(); assert_eq!(xucred.version(), 0); assert_eq!(Uid::from_raw(xucred.uid()), Uid::current()); assert_eq!(Gid::from_raw(xucred.groups()[0]), Gid::current()); } +#[cfg(any(target_os = "ios", target_os = "macos"))] +#[test] +pub fn test_local_peer_pid() { + use nix::sys::socket::socketpair; + + let (fd1, _fd2) = socketpair( + AddressFamily::Unix, + SockType::Stream, + None, + SockFlag::empty(), + ) + .unwrap(); + let pid = getsockopt(&fd1, sockopt::LocalPeerPid).unwrap(); + assert_eq!(pid, std::process::id() as _); +} + #[cfg(target_os = "linux")] #[test] fn is_so_mark_functional() { @@ -68,8 +85,8 @@ fn is_so_mark_functional() { None, ) .unwrap(); - setsockopt(s, sockopt::Mark, &1337).unwrap(); - let mark = getsockopt(s, sockopt::Mark).unwrap(); + setsockopt(&s, sockopt::Mark, &1337).unwrap(); + let mark = getsockopt(&s, sockopt::Mark).unwrap(); assert_eq!(mark, 1337); } @@ -83,18 +100,18 @@ fn test_so_buf() { ) .unwrap(); let bufsize: usize = thread_rng().gen_range(4096..131_072); - setsockopt(fd, sockopt::SndBuf, &bufsize).unwrap(); - let actual = getsockopt(fd, sockopt::SndBuf).unwrap(); + setsockopt(&fd, sockopt::SndBuf, &bufsize).unwrap(); + let actual = getsockopt(&fd, sockopt::SndBuf).unwrap(); assert!(actual >= bufsize); - setsockopt(fd, sockopt::RcvBuf, &bufsize).unwrap(); - let actual = getsockopt(fd, sockopt::RcvBuf).unwrap(); + setsockopt(&fd, sockopt::RcvBuf, &bufsize).unwrap(); + let actual = getsockopt(&fd, sockopt::RcvBuf).unwrap(); assert!(actual >= bufsize); } #[test] fn test_so_tcp_maxseg() { use nix::sys::socket::{accept, bind, connect, listen, SockaddrIn}; - use nix::unistd::{close, write}; + use nix::unistd::write; use std::net::SocketAddrV4; use std::str::FromStr; @@ -108,9 +125,9 @@ fn test_so_tcp_maxseg() { SockProtocol::Tcp, ) .unwrap(); - bind(rsock, &sock_addr).unwrap(); - listen(rsock, 10).unwrap(); - let initial = getsockopt(rsock, sockopt::TcpMaxSeg).unwrap(); + bind(rsock.as_raw_fd(), &sock_addr).unwrap(); + listen(&rsock, 10).unwrap(); + let initial = getsockopt(&rsock, sockopt::TcpMaxSeg).unwrap(); // Initial MSS is expected to be 536 (https://tools.ietf.org/html/rfc879#section-1) but some // platforms keep it even lower. This might fail if you've tuned your initial MSS to be larger // than 700 @@ -118,7 +135,7 @@ fn test_so_tcp_maxseg() { if #[cfg(any(target_os = "android", target_os = "linux"))] { let segsize: u32 = 873; assert!(initial < segsize); - setsockopt(rsock, sockopt::TcpMaxSeg, &segsize).unwrap(); + setsockopt(&rsock, sockopt::TcpMaxSeg, &segsize).unwrap(); } else { assert!(initial < 700); } @@ -132,10 +149,10 @@ fn test_so_tcp_maxseg() { SockProtocol::Tcp, ) .unwrap(); - connect(ssock, &sock_addr).unwrap(); - let rsess = accept(rsock).unwrap(); + connect(ssock.as_raw_fd(), &sock_addr).unwrap(); + let rsess = accept(rsock.as_raw_fd()).unwrap(); write(rsess, b"hello").unwrap(); - let actual = getsockopt(ssock, sockopt::TcpMaxSeg).unwrap(); + let actual = getsockopt(&ssock, sockopt::TcpMaxSeg).unwrap(); // Actual max segment size takes header lengths into account, max IPv4 options (60 bytes) + max // TCP options (40 bytes) are subtracted from the requested maximum as a lower boundary. cfg_if! { @@ -147,8 +164,6 @@ fn test_so_tcp_maxseg() { assert!(536 < actual); } } - close(rsock).unwrap(); - close(ssock).unwrap(); } #[test] @@ -161,7 +176,7 @@ fn test_so_type() { ) .unwrap(); - assert_eq!(Ok(SockType::Stream), getsockopt(sockfd, sockopt::SockType)); + assert_eq!(Ok(SockType::Stream), getsockopt(&sockfd, sockopt::SockType)); } /// getsockopt(_, sockopt::SockType) should gracefully handle unknown socket @@ -170,12 +185,14 @@ fn test_so_type() { #[test] fn test_so_type_unknown() { use nix::errno::Errno; + use std::os::unix::io::{FromRawFd, OwnedFd}; require_capability!("test_so_type", CAP_NET_RAW); - let sockfd = unsafe { libc::socket(libc::AF_PACKET, libc::SOCK_PACKET, 0) }; - assert!(sockfd >= 0, "Error opening socket: {}", nix::Error::last()); + let raw_fd = unsafe { libc::socket(libc::AF_PACKET, libc::SOCK_PACKET, 0) }; + assert!(raw_fd >= 0, "Error opening socket: {}", nix::Error::last()); + let sockfd = unsafe { OwnedFd::from_raw_fd(raw_fd) }; - assert_eq!(Err(Errno::EINVAL), getsockopt(sockfd, sockopt::SockType)); + assert_eq!(Err(Errno::EINVAL), getsockopt(&sockfd, sockopt::SockType)); } // The CI doesn't supported getsockopt and setsockopt on emulated processors. @@ -198,17 +215,17 @@ fn test_tcp_congestion() { ) .unwrap(); - let val = getsockopt(fd, sockopt::TcpCongestion).unwrap(); - setsockopt(fd, sockopt::TcpCongestion, &val).unwrap(); + let val = getsockopt(&fd, sockopt::TcpCongestion).unwrap(); + setsockopt(&fd, sockopt::TcpCongestion, &val).unwrap(); setsockopt( - fd, + &fd, sockopt::TcpCongestion, &OsString::from("tcp_congestion_does_not_exist"), ) .unwrap_err(); - assert_eq!(getsockopt(fd, sockopt::TcpCongestion).unwrap(), val); + assert_eq!(getsockopt(&fd, sockopt::TcpCongestion).unwrap(), val); } #[test] @@ -224,10 +241,10 @@ fn test_bindtodevice() { ) .unwrap(); - let val = getsockopt(fd, sockopt::BindToDevice).unwrap(); - setsockopt(fd, sockopt::BindToDevice, &val).unwrap(); + let val = getsockopt(&fd, sockopt::BindToDevice).unwrap(); + setsockopt(&fd, sockopt::BindToDevice, &val).unwrap(); - assert_eq!(getsockopt(fd, sockopt::BindToDevice).unwrap(), val); + assert_eq!(getsockopt(&fd, sockopt::BindToDevice).unwrap(), val); } #[test] @@ -239,8 +256,8 @@ fn test_so_tcp_keepalive() { SockProtocol::Tcp, ) .unwrap(); - setsockopt(fd, sockopt::KeepAlive, &true).unwrap(); - assert!(getsockopt(fd, sockopt::KeepAlive).unwrap()); + setsockopt(&fd, sockopt::KeepAlive, &true).unwrap(); + assert!(getsockopt(&fd, sockopt::KeepAlive).unwrap()); #[cfg(any( target_os = "android", @@ -249,17 +266,17 @@ fn test_so_tcp_keepalive() { target_os = "linux" ))] { - let x = getsockopt(fd, sockopt::TcpKeepIdle).unwrap(); - setsockopt(fd, sockopt::TcpKeepIdle, &(x + 1)).unwrap(); - assert_eq!(getsockopt(fd, sockopt::TcpKeepIdle).unwrap(), x + 1); + let x = getsockopt(&fd, sockopt::TcpKeepIdle).unwrap(); + setsockopt(&fd, sockopt::TcpKeepIdle, &(x + 1)).unwrap(); + assert_eq!(getsockopt(&fd, sockopt::TcpKeepIdle).unwrap(), x + 1); - let x = getsockopt(fd, sockopt::TcpKeepCount).unwrap(); - setsockopt(fd, sockopt::TcpKeepCount, &(x + 1)).unwrap(); - assert_eq!(getsockopt(fd, sockopt::TcpKeepCount).unwrap(), x + 1); + let x = getsockopt(&fd, sockopt::TcpKeepCount).unwrap(); + setsockopt(&fd, sockopt::TcpKeepCount, &(x + 1)).unwrap(); + assert_eq!(getsockopt(&fd, sockopt::TcpKeepCount).unwrap(), x + 1); - let x = getsockopt(fd, sockopt::TcpKeepInterval).unwrap(); - setsockopt(fd, sockopt::TcpKeepInterval, &(x + 1)).unwrap(); - assert_eq!(getsockopt(fd, sockopt::TcpKeepInterval).unwrap(), x + 1); + let x = getsockopt(&fd, sockopt::TcpKeepInterval).unwrap(); + setsockopt(&fd, sockopt::TcpKeepInterval, &(x + 1)).unwrap(); + assert_eq!(getsockopt(&fd, sockopt::TcpKeepInterval).unwrap(), x + 1); } } @@ -283,11 +300,11 @@ fn test_get_mtu() { .unwrap(); // Bind and initiate connection - bind(usock, &SockaddrIn::from(std_sa)).unwrap(); - connect(usock, &SockaddrIn::from(std_sb)).unwrap(); + bind(usock.as_raw_fd(), &SockaddrIn::from(std_sa)).unwrap(); + connect(usock.as_raw_fd(), &SockaddrIn::from(std_sb)).unwrap(); // Loopback connections have 2^16 - the maximum - MTU - assert_eq!(getsockopt(usock, sockopt::IpMtu), Ok(u16::MAX as i32)) + assert_eq!(getsockopt(&usock, sockopt::IpMtu), Ok(u16::MAX as i32)) } #[test] @@ -300,7 +317,7 @@ fn test_ttl_opts() { None, ) .unwrap(); - setsockopt(fd4, sockopt::Ipv4Ttl, &1) + setsockopt(&fd4, sockopt::Ipv4Ttl, &1) .expect("setting ipv4ttl on an inet socket should succeed"); let fd6 = socket( AddressFamily::Inet6, @@ -309,7 +326,7 @@ fn test_ttl_opts() { None, ) .unwrap(); - setsockopt(fd6, sockopt::Ipv6Ttl, &1) + setsockopt(&fd6, sockopt::Ipv6Ttl, &1) .expect("setting ipv6ttl on an inet6 socket should succeed"); } @@ -323,9 +340,9 @@ fn test_dontfrag_opts() { SockProtocol::Tcp, ) .unwrap(); - setsockopt(fd4, sockopt::IpDontFrag, &true) + setsockopt(&fd4, sockopt::IpDontFrag, &true) .expect("setting IP_DONTFRAG on an inet stream socket should succeed"); - setsockopt(fd4, sockopt::IpDontFrag, &false).expect( + setsockopt(&fd4, sockopt::IpDontFrag, &false).expect( "unsetting IP_DONTFRAG on an inet stream socket should succeed", ); let fd4d = socket( @@ -335,10 +352,10 @@ fn test_dontfrag_opts() { None, ) .unwrap(); - setsockopt(fd4d, sockopt::IpDontFrag, &true).expect( + setsockopt(&fd4d, sockopt::IpDontFrag, &true).expect( "setting IP_DONTFRAG on an inet datagram socket should succeed", ); - setsockopt(fd4d, sockopt::IpDontFrag, &false).expect( + setsockopt(&fd4d, sockopt::IpDontFrag, &false).expect( "unsetting IP_DONTFRAG on an inet datagram socket should succeed", ); } @@ -361,10 +378,10 @@ fn test_v6dontfrag_opts() { SockProtocol::Tcp, ) .unwrap(); - setsockopt(fd6, sockopt::Ipv6DontFrag, &true).expect( + setsockopt(&fd6, sockopt::Ipv6DontFrag, &true).expect( "setting IPV6_DONTFRAG on an inet6 stream socket should succeed", ); - setsockopt(fd6, sockopt::Ipv6DontFrag, &false).expect( + setsockopt(&fd6, sockopt::Ipv6DontFrag, &false).expect( "unsetting IPV6_DONTFRAG on an inet6 stream socket should succeed", ); let fd6d = socket( @@ -374,10 +391,10 @@ fn test_v6dontfrag_opts() { None, ) .unwrap(); - setsockopt(fd6d, sockopt::Ipv6DontFrag, &true).expect( + setsockopt(&fd6d, sockopt::Ipv6DontFrag, &true).expect( "setting IPV6_DONTFRAG on an inet6 datagram socket should succeed", ); - setsockopt(fd6d, sockopt::Ipv6DontFrag, &false).expect( + setsockopt(&fd6d, sockopt::Ipv6DontFrag, &false).expect( "unsetting IPV6_DONTFRAG on an inet6 datagram socket should succeed", ); } @@ -393,8 +410,8 @@ fn test_so_priority() { ) .unwrap(); let priority = 3; - setsockopt(fd, sockopt::Priority, &priority).unwrap(); - assert_eq!(getsockopt(fd, sockopt::Priority).unwrap(), priority); + setsockopt(&fd, sockopt::Priority, &priority).unwrap(); + assert_eq!(getsockopt(&fd, sockopt::Priority).unwrap(), priority); } #[test] @@ -408,8 +425,8 @@ fn test_ip_tos() { ) .unwrap(); let tos = 0x80; // CS4 - setsockopt(fd, sockopt::IpTos, &tos).unwrap(); - assert_eq!(getsockopt(fd, sockopt::IpTos).unwrap(), tos); + setsockopt(&fd, sockopt::IpTos, &tos).unwrap(); + assert_eq!(getsockopt(&fd, sockopt::IpTos).unwrap(), tos); } #[test] @@ -426,6 +443,6 @@ fn test_ipv6_tclass() { ) .unwrap(); let class = 0x80; // CS4 - setsockopt(fd, sockopt::Ipv6TClass, &class).unwrap(); - assert_eq!(getsockopt(fd, sockopt::Ipv6TClass).unwrap(), class); + setsockopt(&fd, sockopt::Ipv6TClass, &class).unwrap(); + assert_eq!(getsockopt(&fd, sockopt::Ipv6TClass).unwrap(), class); } diff --git a/test/sys/test_termios.rs b/test/sys/test_termios.rs index aaf0008..8391937 100644 --- a/test/sys/test_termios.rs +++ b/test/sys/test_termios.rs @@ -1,17 +1,17 @@ -use std::os::unix::prelude::*; +use std::os::unix::io::{AsFd, AsRawFd}; use tempfile::tempfile; use nix::errno::Errno; use nix::fcntl; use nix::pty::openpty; use nix::sys::termios::{self, tcgetattr, LocalFlags, OutputFlags}; -use nix::unistd::{close, read, write}; +use nix::unistd::{read, write}; -/// Helper function analogous to `std::io::Write::write_all`, but for `RawFd`s -fn write_all(f: RawFd, buf: &[u8]) { +/// Helper function analogous to `std::io::Write::write_all`, but for `Fd`s +fn write_all(f: Fd, buf: &[u8]) { let mut len = 0; while len < buf.len() { - len += write(f, &buf[len..]).unwrap(); + len += write(f.as_fd().as_raw_fd(), &buf[len..]).unwrap(); } } @@ -22,25 +22,14 @@ fn test_tcgetattr_pty() { let _m = crate::PTSNAME_MTX.lock(); let pty = openpty(None, None).expect("openpty failed"); - termios::tcgetattr(pty.slave).unwrap(); - close(pty.master).expect("closing the master failed"); - close(pty.slave).expect("closing the slave failed"); + termios::tcgetattr(&pty.slave).unwrap(); } // Test tcgetattr on something that isn't a terminal #[test] fn test_tcgetattr_enotty() { let file = tempfile().unwrap(); - assert_eq!( - termios::tcgetattr(file.as_raw_fd()).err(), - Some(Errno::ENOTTY) - ); -} - -// Test tcgetattr on an invalid file descriptor -#[test] -fn test_tcgetattr_ebadf() { - assert_eq!(termios::tcgetattr(-1).err(), Some(Errno::EBADF)); + assert_eq!(termios::tcgetattr(&file).err(), Some(Errno::ENOTTY)); } // Test modifying output flags @@ -52,12 +41,7 @@ fn test_output_flags() { // Open one pty to get attributes for the second one let mut termios = { let pty = openpty(None, None).expect("openpty failed"); - assert!(pty.master > 0); - assert!(pty.slave > 0); - let termios = tcgetattr(pty.slave).expect("tcgetattr failed"); - close(pty.master).unwrap(); - close(pty.slave).unwrap(); - termios + tcgetattr(&pty.slave).expect("tcgetattr failed") }; // Make sure postprocessing '\r' isn't specified by default or this test is useless. @@ -73,19 +57,15 @@ fn test_output_flags() { // Open a pty let pty = openpty(None, &termios).unwrap(); - assert!(pty.master > 0); - assert!(pty.slave > 0); // Write into the master let string = "foofoofoo\r"; - write_all(pty.master, string.as_bytes()); + write_all(&pty.master, string.as_bytes()); // Read from the slave verifying that the output has been properly transformed let mut buf = [0u8; 10]; - crate::read_exact(pty.slave, &mut buf); + crate::read_exact(&pty.slave, &mut buf); let transformed_string = "foofoofoo\n"; - close(pty.master).unwrap(); - close(pty.slave).unwrap(); assert_eq!(&buf, transformed_string.as_bytes()); } @@ -98,12 +78,7 @@ fn test_local_flags() { // Open one pty to get attributes for the second one let mut termios = { let pty = openpty(None, None).unwrap(); - assert!(pty.master > 0); - assert!(pty.slave > 0); - let termios = tcgetattr(pty.slave).unwrap(); - close(pty.master).unwrap(); - close(pty.slave).unwrap(); - termios + tcgetattr(&pty.slave).unwrap() }; // Make sure echo is specified by default or this test is useless. @@ -114,23 +89,19 @@ fn test_local_flags() { // Open a new pty with our modified termios settings let pty = openpty(None, &termios).unwrap(); - assert!(pty.master > 0); - assert!(pty.slave > 0); // Set the master is in nonblocking mode or reading will never return. - let flags = fcntl::fcntl(pty.master, fcntl::F_GETFL).unwrap(); + let flags = fcntl::fcntl(pty.master.as_raw_fd(), fcntl::F_GETFL).unwrap(); let new_flags = fcntl::OFlag::from_bits_truncate(flags) | fcntl::OFlag::O_NONBLOCK; - fcntl::fcntl(pty.master, fcntl::F_SETFL(new_flags)).unwrap(); + fcntl::fcntl(pty.master.as_raw_fd(), fcntl::F_SETFL(new_flags)).unwrap(); // Write into the master let string = "foofoofoo\r"; - write_all(pty.master, string.as_bytes()); + write_all(&pty.master, string.as_bytes()); // Try to read from the master, which should not have anything as echoing was disabled. let mut buf = [0u8; 10]; - let read = read(pty.master, &mut buf).unwrap_err(); - close(pty.master).unwrap(); - close(pty.slave).unwrap(); + let read = read(pty.master.as_raw_fd(), &mut buf).unwrap_err(); assert_eq!(read, Errno::EAGAIN); } diff --git a/test/sys/test_uio.rs b/test/sys/test_uio.rs index 0f4b8a6..fc09465 100644 --- a/test/sys/test_uio.rs +++ b/test/sys/test_uio.rs @@ -4,7 +4,7 @@ use rand::distributions::Alphanumeric; use rand::{thread_rng, Rng}; use std::fs::OpenOptions; use std::io::IoSlice; -use std::os::unix::io::AsRawFd; +use std::os::unix::io::{FromRawFd, OwnedFd}; use std::{cmp, iter}; #[cfg(not(target_os = "redox"))] @@ -40,12 +40,16 @@ fn test_writev() { iovecs.push(IoSlice::new(b)); consumed += slice_len; } - let pipe_res = pipe(); - let (reader, writer) = pipe_res.expect("Couldn't create pipe"); + let (reader, writer) = pipe().expect("Couldn't create pipe"); // FileDesc will close its filedesc (reader). let mut read_buf: Vec = iter::repeat(0u8).take(128 * 16).collect(); + + // Temporary workaround to cope with the existing RawFd pipe(2), should be + // removed when pipe(2) becomes I/O-safe. + let writer = unsafe { OwnedFd::from_raw_fd(writer) }; + // Blocking io, should write all data. - let write_res = writev(writer, &iovecs); + let write_res = writev(&writer, &iovecs); let written = write_res.expect("couldn't write"); // Check whether we written all data assert_eq!(to_write.len(), written); @@ -55,7 +59,6 @@ fn test_writev() { assert_eq!(read, written); // Check equality of written and read data assert_eq!(&to_write, &read_buf); - close(writer).expect("closed writer"); close(reader).expect("closed reader"); } @@ -88,7 +91,12 @@ fn test_readv() { let (reader, writer) = pipe().expect("couldn't create pipe"); // Blocking io, should write all data. write(writer, &to_write).expect("write failed"); - let read = readv(reader, &mut iovecs[..]).expect("read failed"); + + // Temporary workaround to cope with the existing RawFd pipe(2), should be + // removed when pipe(2) becomes I/O-safe. + let reader = unsafe { OwnedFd::from_raw_fd(reader) }; + + let read = readv(&reader, &mut iovecs[..]).expect("read failed"); // Check whether we've read all data assert_eq!(to_write.len(), read); // Cccumulate data from iovecs @@ -100,7 +108,6 @@ fn test_readv() { assert_eq!(read_buf.len(), to_write.len()); // Check equality of written and read data assert_eq!(&read_buf, &to_write); - close(reader).expect("couldn't close reader"); close(writer).expect("couldn't close writer"); } @@ -111,7 +118,7 @@ fn test_pwrite() { let mut file = tempfile().unwrap(); let buf = [1u8; 8]; - assert_eq!(Ok(8), pwrite(file.as_raw_fd(), &buf, 8)); + assert_eq!(Ok(8), pwrite(&file, &buf, 8)); let mut file_content = Vec::new(); file.read_to_end(&mut file_content).unwrap(); let mut expected = vec![0u8; 8]; @@ -137,7 +144,7 @@ fn test_pread() { file.write_all(&file_content).unwrap(); let mut buf = [0u8; 16]; - assert_eq!(Ok(16), pread(file.as_raw_fd(), &mut buf, 16)); + assert_eq!(Ok(16), pread(&file, &mut buf, 16)); let expected: Vec<_> = (16..32).collect(); assert_eq!(&buf[..], &expected[..]); } @@ -168,7 +175,7 @@ fn test_pwritev() { .open(path) .unwrap(); - let written = pwritev(file.as_raw_fd(), &iovecs, 100).ok().unwrap(); + let written = pwritev(&file, &iovecs, 100).ok().unwrap(); assert_eq!(written, to_write.len()); // Read the data back and make sure it matches @@ -206,7 +213,7 @@ fn test_preadv() { .iter_mut() .map(|buf| IoSliceMut::new(&mut buf[..])) .collect(); - assert_eq!(Ok(100), preadv(file.as_raw_fd(), &mut iovecs, 100)); + assert_eq!(Ok(100), preadv(&file, &mut iovecs, 100)); } let all = buffers.concat(); diff --git a/test/test.rs b/test/test.rs index 6b42aad..7e73bb3 100644 --- a/test/test.rs +++ b/test/test.rs @@ -2,8 +2,6 @@ extern crate cfg_if; #[cfg_attr(not(any(target_os = "redox", target_os = "haiku")), macro_use)] extern crate nix; -#[macro_use] -extern crate lazy_static; mod common; mod sys; @@ -66,37 +64,35 @@ mod test_unistd; use nix::unistd::{chdir, getcwd, read}; use parking_lot::{Mutex, RwLock, RwLockWriteGuard}; -use std::os::unix::io::RawFd; +use std::os::unix::io::{AsFd, AsRawFd}; use std::path::PathBuf; -/// Helper function analogous to `std::io::Read::read_exact`, but for `RawFD`s -fn read_exact(f: RawFd, buf: &mut [u8]) { +/// Helper function analogous to `std::io::Read::read_exact`, but for `Fd`s +fn read_exact(f: Fd, buf: &mut [u8]) { let mut len = 0; while len < buf.len() { // get_mut would be better than split_at_mut, but it requires nightly let (_, remaining) = buf.split_at_mut(len); - len += read(f, remaining).unwrap(); + len += read(f.as_fd().as_raw_fd(), remaining).unwrap(); } } -lazy_static! { - /// Any test that changes the process's current working directory must grab - /// the RwLock exclusively. Any process that cares about the current - /// working directory must grab it shared. - pub static ref CWD_LOCK: RwLock<()> = RwLock::new(()); - /// Any test that creates child processes must grab this mutex, regardless - /// of what it does with those children. - pub static ref FORK_MTX: Mutex<()> = Mutex::new(()); - /// Any test that changes the process's supplementary groups must grab this - /// mutex - pub static ref GROUPS_MTX: Mutex<()> = Mutex::new(()); - /// Any tests that loads or unloads kernel modules must grab this mutex - pub static ref KMOD_MTX: Mutex<()> = Mutex::new(()); - /// Any test that calls ptsname(3) must grab this mutex. - pub static ref PTSNAME_MTX: Mutex<()> = Mutex::new(()); - /// Any test that alters signal handling must grab this mutex. - pub static ref SIGNAL_MTX: Mutex<()> = Mutex::new(()); -} +/// Any test that creates child processes must grab this mutex, regardless +/// of what it does with those children. +pub static FORK_MTX: std::sync::Mutex<()> = std::sync::Mutex::new(()); +/// Any test that changes the process's current working directory must grab +/// the RwLock exclusively. Any process that cares about the current +/// working directory must grab it shared. +pub static CWD_LOCK: RwLock<()> = RwLock::new(()); +/// Any test that changes the process's supplementary groups must grab this +/// mutex +pub static GROUPS_MTX: Mutex<()> = Mutex::new(()); +/// Any tests that loads or unloads kernel modules must grab this mutex +pub static KMOD_MTX: Mutex<()> = Mutex::new(()); +/// Any test that calls ptsname(3) must grab this mutex. +pub static PTSNAME_MTX: Mutex<()> = Mutex::new(()); +/// Any test that alters signal handling must grab this mutex. +pub static SIGNAL_MTX: Mutex<()> = Mutex::new(()); /// RAII object that restores a test's original directory on drop struct DirRestore<'a> { diff --git a/test/test_fcntl.rs b/test/test_fcntl.rs index e51044a..5fef04b 100644 --- a/test/test_fcntl.rs +++ b/test/test_fcntl.rs @@ -26,7 +26,7 @@ use std::io::prelude::*; #[cfg(not(target_os = "redox"))] use std::os::unix::fs; #[cfg(not(target_os = "redox"))] -use tempfile::{self, NamedTempFile}; +use tempfile::NamedTempFile; #[test] #[cfg(not(target_os = "redox"))] @@ -227,6 +227,51 @@ fn test_readlink() { ); } +/// 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 +/// resulting file is read and should contain the contents `bar`. +/// The from_offset should be updated by the call to reflect +/// the 3 bytes read (6). +#[cfg(any( + target_os = "linux", + // Not available until FreeBSD 13.0 + all(target_os = "freebsd", fbsd14), + target_os = "android" +))] +#[test] +// QEMU does not support copy_file_range. Skip under qemu +#[cfg_attr(qemu, ignore)] +fn test_copy_file_range() { + use nix::fcntl::copy_file_range; + use std::os::unix::io::AsFd; + + const CONTENTS: &[u8] = b"foobarbaz"; + + let mut tmp1 = tempfile::tempfile().unwrap(); + let mut tmp2 = tempfile::tempfile().unwrap(); + + tmp1.write_all(CONTENTS).unwrap(); + tmp1.flush().unwrap(); + + let mut from_offset: i64 = 3; + copy_file_range( + tmp1.as_fd(), + Some(&mut from_offset), + tmp2.as_fd(), + None, + 3, + ) + .unwrap(); + + let mut res: String = String::new(); + tmp2.rewind().unwrap(); + tmp2.read_to_string(&mut res).unwrap(); + + assert_eq!(res, String::from("bar")); + assert_eq!(from_offset, 6); +} + #[cfg(any(target_os = "linux", target_os = "android"))] mod linux_android { use libc::loff_t; @@ -238,47 +283,11 @@ mod linux_android { use nix::unistd::{close, pipe, read, write}; use tempfile::tempfile; - #[cfg(any(target_os = "linux"))] + #[cfg(target_os = "linux")] use 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 - /// resulting file is read and should contain the contents `bar`. - /// The from_offset should be updated by the call to reflect - /// the 3 bytes read (6). - #[test] - // QEMU does not support copy_file_range. Skip under qemu - #[cfg_attr(qemu, ignore)] - fn test_copy_file_range() { - const CONTENTS: &[u8] = b"foobarbaz"; - - let mut tmp1 = tempfile().unwrap(); - let mut tmp2 = tempfile().unwrap(); - - tmp1.write_all(CONTENTS).unwrap(); - tmp1.flush().unwrap(); - - let mut from_offset: i64 = 3; - copy_file_range( - tmp1.as_raw_fd(), - Some(&mut from_offset), - tmp2.as_raw_fd(), - None, - 3, - ) - .unwrap(); - - let mut res: String = String::new(); - tmp2.rewind().unwrap(); - tmp2.read_to_string(&mut res).unwrap(); - - assert_eq!(res, String::from("bar")); - assert_eq!(from_offset, 6); - } - #[test] fn test_splice() { const CONTENTS: &[u8] = b"abcdef123456"; @@ -340,7 +349,7 @@ mod linux_android { let buf1 = b"abcdef"; let buf2 = b"defghi"; - let iovecs = vec![IoSlice::new(&buf1[0..3]), IoSlice::new(&buf2[0..3])]; + let iovecs = [IoSlice::new(&buf1[0..3]), IoSlice::new(&buf2[0..3])]; let res = vmsplice(wr, &iovecs[..], SpliceFFlags::empty()).unwrap(); @@ -355,7 +364,7 @@ mod linux_android { close(wr).unwrap(); } - #[cfg(any(target_os = "linux"))] + #[cfg(target_os = "linux")] #[test] fn test_fallocate() { let tmp = NamedTempFile::new().unwrap(); @@ -383,7 +392,7 @@ mod linux_android { let tmp = NamedTempFile::new().unwrap(); let fd = tmp.as_raw_fd(); - let statfs = nix::sys::statfs::fstatfs(&tmp).unwrap(); + let statfs = nix::sys::statfs::fstatfs(tmp.as_file()).unwrap(); if statfs.filesystem_type() == nix::sys::statfs::OVERLAYFS_SUPER_MAGIC { // OverlayFS is a union file system. It returns one inode value in // stat(2), but a different one shows up in /proc/locks. So we must @@ -421,7 +430,7 @@ mod linux_android { let tmp = NamedTempFile::new().unwrap(); let fd = tmp.as_raw_fd(); - let statfs = nix::sys::statfs::fstatfs(&tmp).unwrap(); + let statfs = nix::sys::statfs::fstatfs(tmp.as_file()).unwrap(); if statfs.filesystem_type() == nix::sys::statfs::OVERLAYFS_SUPER_MAGIC { // OverlayFS is a union file system. It returns one inode value in // stat(2), but a different one shows up in /proc/locks. So we must @@ -559,7 +568,7 @@ mod test_posix_fallocate { let err = posix_fallocate(rd as RawFd, 0, 100).unwrap_err(); match err { Errno::EINVAL | Errno::ENODEV | Errno::ESPIPE | Errno::EBADF => (), - errno => panic!("unexpected errno {}", errno,), + errno => panic!("unexpected errno {errno}",), } } } diff --git a/test/test_mount.rs b/test/test_mount.rs index 2fd612e..5cf0040 100644 --- a/test/test_mount.rs +++ b/test/test_mount.rs @@ -38,7 +38,7 @@ exit 23"; MsFlags::empty(), NONE, ) - .unwrap_or_else(|e| panic!("mount failed: {}", e)); + .unwrap_or_else(|e| panic!("mount failed: {e}")); let test_path = tempdir.path().join("test"); @@ -67,17 +67,17 @@ exit 23"; .unwrap(); process::exit(0); } else { - panic!("open failed: {}", e); + panic!("open failed: {e}"); } }) .and_then(|mut f| f.write(SCRIPT_CONTENTS)) - .unwrap_or_else(|e| panic!("write failed: {}", e)); + .unwrap_or_else(|e| panic!("write failed: {e}")); // Verify read. let mut buf = Vec::new(); File::open(&test_path) .and_then(|mut f| f.read_to_end(&mut buf)) - .unwrap_or_else(|e| panic!("read failed: {}", e)); + .unwrap_or_else(|e| panic!("read failed: {e}")); assert_eq!(buf, SCRIPT_CONTENTS); // Verify execute. @@ -85,13 +85,12 @@ exit 23"; EXPECTED_STATUS, Command::new(&test_path) .status() - .unwrap_or_else(|e| panic!("exec failed: {}", e)) + .unwrap_or_else(|e| panic!("exec failed: {e}")) .code() .unwrap_or_else(|| panic!("child killed by signal")) ); - umount(tempdir.path()) - .unwrap_or_else(|e| panic!("umount failed: {}", e)); + umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {e}")); } pub fn test_mount_rdonly_disallows_write() { @@ -104,7 +103,7 @@ exit 23"; MsFlags::MS_RDONLY, NONE, ) - .unwrap_or_else(|e| panic!("mount failed: {}", e)); + .unwrap_or_else(|e| panic!("mount failed: {e}")); // EROFS: Read-only file system assert_eq!( @@ -115,8 +114,7 @@ exit 23"; .unwrap() ); - umount(tempdir.path()) - .unwrap_or_else(|e| panic!("umount failed: {}", e)); + umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {e}")); } pub fn test_mount_noexec_disallows_exec() { @@ -129,7 +127,7 @@ exit 23"; MsFlags::MS_NOEXEC, NONE, ) - .unwrap_or_else(|e| panic!("mount failed: {}", e)); + .unwrap_or_else(|e| panic!("mount failed: {e}")); let test_path = tempdir.path().join("test"); @@ -139,13 +137,13 @@ exit 23"; .mode((Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO).bits()) .open(&test_path) .and_then(|mut f| f.write(SCRIPT_CONTENTS)) - .unwrap_or_else(|e| panic!("write failed: {}", e)); + .unwrap_or_else(|e| panic!("write failed: {e}")); // Verify that we cannot execute despite a+x permissions being set. let mode = stat::Mode::from_bits_truncate( fs::metadata(&test_path) .map(|md| md.permissions().mode()) - .unwrap_or_else(|e| panic!("metadata failed: {}", e)), + .unwrap_or_else(|e| panic!("metadata failed: {e}")), ); assert!( @@ -164,8 +162,7 @@ exit 23"; .unwrap() ); - umount(tempdir.path()) - .unwrap_or_else(|e| panic!("umount failed: {}", e)); + umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {e}")); } pub fn test_mount_bind() { @@ -182,7 +179,7 @@ exit 23"; MsFlags::MS_BIND, NONE, ) - .unwrap_or_else(|e| panic!("mount failed: {}", e)); + .unwrap_or_else(|e| panic!("mount failed: {e}")); fs::OpenOptions::new() .create(true) @@ -190,10 +187,10 @@ exit 23"; .mode((Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO).bits()) .open(mount_point.path().join(file_name)) .and_then(|mut f| f.write(SCRIPT_CONTENTS)) - .unwrap_or_else(|e| panic!("write failed: {}", e)); + .unwrap_or_else(|e| panic!("write failed: {e}")); umount(mount_point.path()) - .unwrap_or_else(|e| panic!("umount failed: {}", e)); + .unwrap_or_else(|e| panic!("umount failed: {e}")); } // Verify the file written in the mount shows up in source directory, even @@ -202,7 +199,7 @@ exit 23"; let mut buf = Vec::new(); File::open(tempdir.path().join(file_name)) .and_then(|mut f| f.read_to_end(&mut buf)) - .unwrap_or_else(|e| panic!("read failed: {}", e)); + .unwrap_or_else(|e| panic!("read failed: {e}")); assert_eq!(buf, SCRIPT_CONTENTS); } @@ -214,8 +211,7 @@ exit 23"; let stderr = io::stderr(); let mut handle = stderr.lock(); writeln!(handle, - "unshare failed: {}. Are unprivileged user namespaces available?", - e).unwrap(); + "unshare failed: {e}. Are unprivileged user namespaces available?").unwrap(); writeln!(handle, "mount is not being tested").unwrap(); // Exit with success because not all systems support unprivileged user namespaces, and // that's not what we're testing for. @@ -226,8 +222,8 @@ exit 23"; fs::OpenOptions::new() .write(true) .open("/proc/self/uid_map") - .and_then(|mut f| f.write(format!("1000 {} 1\n", uid).as_bytes())) - .unwrap_or_else(|e| panic!("could not write uid map: {}", e)); + .and_then(|mut f| f.write(format!("1000 {uid} 1\n").as_bytes())) + .unwrap_or_else(|e| panic!("could not write uid map: {e}")); } } diff --git a/test/test_mq.rs b/test/test_mq.rs index 7b48e7a..1fd8929 100644 --- a/test/test_mq.rs +++ b/test/test_mq.rs @@ -1,11 +1,14 @@ use cfg_if::cfg_if; -use std::ffi::CString; use std::str; use nix::errno::Errno; -use nix::mqueue::{mq_attr_member_t, mq_close, mq_open, mq_receive, mq_send}; +use nix::mqueue::{ + mq_attr_member_t, mq_close, mq_open, mq_receive, mq_send, mq_timedreceive, +}; use nix::mqueue::{MQ_OFlag, MqAttr}; use nix::sys::stat::Mode; +use nix::sys::time::{TimeSpec, TimeValLike}; +use nix::time::{clock_gettime, ClockId}; // Defined as a macro such that the error source is reported as the caller's location. macro_rules! assert_attr_eq { @@ -30,7 +33,7 @@ macro_rules! assert_attr_eq { fn test_mq_send_and_receive() { const MSG_SIZE: mq_attr_member_t = 32; let attr = MqAttr::new(0, 10, MSG_SIZE, 0); - let mq_name = &CString::new(b"/a_nix_test_queue".as_ref()).unwrap(); + let mq_name = "/a_nix_test_queue"; let oflag0 = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY; let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH; @@ -55,12 +58,43 @@ fn test_mq_send_and_receive() { assert_eq!(msg_to_send, str::from_utf8(&buf[0..len]).unwrap()); } +#[test] +fn test_mq_timedreceive() { + const MSG_SIZE: mq_attr_member_t = 32; + let attr = MqAttr::new(0, 10, MSG_SIZE, 0); + let mq_name = "/a_nix_test_queue"; + + let oflag0 = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY; + let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH; + let r0 = mq_open(mq_name, oflag0, mode, Some(&attr)); + if let Err(Errno::ENOSYS) = r0 { + println!("message queues not supported or module not loaded?"); + return; + }; + let mqd0 = r0.unwrap(); + let msg_to_send = "msg_1"; + mq_send(&mqd0, msg_to_send.as_bytes(), 1).unwrap(); + + let oflag1 = MQ_OFlag::O_CREAT | MQ_OFlag::O_RDONLY; + let mqd1 = mq_open(mq_name, oflag1, mode, Some(&attr)).unwrap(); + let mut buf = [0u8; 32]; + let mut prio = 0u32; + let abstime = + clock_gettime(ClockId::CLOCK_REALTIME).unwrap() + TimeSpec::seconds(1); + let len = mq_timedreceive(&mqd1, &mut buf, &mut prio, &abstime).unwrap(); + assert_eq!(prio, 1); + + mq_close(mqd1).unwrap(); + mq_close(mqd0).unwrap(); + assert_eq!(msg_to_send, str::from_utf8(&buf[0..len]).unwrap()); +} + #[test] fn test_mq_getattr() { use nix::mqueue::mq_getattr; const MSG_SIZE: mq_attr_member_t = 32; let initial_attr = MqAttr::new(0, 10, MSG_SIZE, 0); - let mq_name = &CString::new(b"/attr_test_get_attr".as_ref()).unwrap(); + let mq_name = "/attr_test_get_attr"; let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY; let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH; let r = mq_open(mq_name, oflag, mode, Some(&initial_attr)); @@ -85,7 +119,7 @@ fn test_mq_setattr() { use nix::mqueue::{mq_getattr, mq_setattr}; const MSG_SIZE: mq_attr_member_t = 32; let initial_attr = MqAttr::new(0, 10, MSG_SIZE, 0); - let mq_name = &CString::new(b"/attr_test_get_attr".as_ref()).unwrap(); + let mq_name = "/attr_test_get_attr"; let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY; let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH; let r = mq_open(mq_name, oflag, mode, Some(&initial_attr)); @@ -135,7 +169,7 @@ fn test_mq_set_nonblocking() { use nix::mqueue::{mq_getattr, mq_remove_nonblock, mq_set_nonblock}; const MSG_SIZE: mq_attr_member_t = 32; let initial_attr = MqAttr::new(0, 10, MSG_SIZE, 0); - let mq_name = &CString::new(b"/attr_test_get_attr".as_ref()).unwrap(); + let mq_name = "/attr_test_get_attr"; let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY; let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH; let r = mq_open(mq_name, oflag, mode, Some(&initial_attr)); @@ -159,10 +193,9 @@ fn test_mq_unlink() { use nix::mqueue::mq_unlink; const MSG_SIZE: mq_attr_member_t = 32; let initial_attr = MqAttr::new(0, 10, MSG_SIZE, 0); - let mq_name_opened = &CString::new(b"/mq_unlink_test".as_ref()).unwrap(); + let mq_name_opened = "/mq_unlink_test"; #[cfg(not(any(target_os = "dragonfly", target_os = "netbsd")))] - let mq_name_not_opened = - &CString::new(b"/mq_unlink_test".as_ref()).unwrap(); + let mq_name_not_opened = "/mq_unlink_test"; let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY; let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH; let r = mq_open(mq_name_opened, oflag, mode, Some(&initial_attr)); diff --git a/test/test_poll.rs b/test/test_poll.rs index 53964e2..045ccd3 100644 --- a/test/test_poll.rs +++ b/test/test_poll.rs @@ -1,8 +1,9 @@ use nix::{ errno::Errno, poll::{poll, PollFd, PollFlags}, - unistd::{pipe, write}, + unistd::{close, pipe, write}, }; +use std::os::unix::io::{BorrowedFd, FromRawFd, OwnedFd}; macro_rules! loop_while_eintr { ($poll_expr: expr) => { @@ -19,7 +20,8 @@ macro_rules! loop_while_eintr { #[test] fn test_poll() { let (r, w) = pipe().unwrap(); - let mut fds = [PollFd::new(r, PollFlags::POLLIN)]; + let r = unsafe { OwnedFd::from_raw_fd(r) }; + let mut fds = [PollFd::new(&r, PollFlags::POLLIN)]; // Poll an idle pipe. Should timeout let nfds = loop_while_eintr!(poll(&mut fds, 100)); @@ -32,6 +34,7 @@ fn test_poll() { let nfds = poll(&mut fds, 100).unwrap(); assert_eq!(nfds, 1); assert!(fds[0].revents().unwrap().contains(PollFlags::POLLIN)); + close(w).unwrap(); } // ppoll(2) is the same as poll except for how it handles timeouts and signals. @@ -51,7 +54,8 @@ fn test_ppoll() { let timeout = TimeSpec::milliseconds(1); let (r, w) = pipe().unwrap(); - let mut fds = [PollFd::new(r, PollFlags::POLLIN)]; + let r = unsafe { OwnedFd::from_raw_fd(r) }; + let mut fds = [PollFd::new(&r, PollFlags::POLLIN)]; // Poll an idle pipe. Should timeout let sigset = SigSet::empty(); @@ -65,19 +69,13 @@ fn test_ppoll() { let nfds = ppoll(&mut fds, Some(timeout), None).unwrap(); assert_eq!(nfds, 1); assert!(fds[0].revents().unwrap().contains(PollFlags::POLLIN)); -} - -#[test] -fn test_pollfd_fd() { - use std::os::unix::io::AsRawFd; - - let pfd = PollFd::new(0x1234, PollFlags::empty()); - assert_eq!(pfd.as_raw_fd(), 0x1234); + close(w).unwrap(); } #[test] fn test_pollfd_events() { - let mut pfd = PollFd::new(-1, PollFlags::POLLIN); + let fd_zero = unsafe { BorrowedFd::borrow_raw(0) }; + let mut pfd = PollFd::new(&fd_zero, PollFlags::POLLIN); assert_eq!(pfd.events(), PollFlags::POLLIN); pfd.set_events(PollFlags::POLLOUT); assert_eq!(pfd.events(), PollFlags::POLLOUT); diff --git a/test/test_pty.rs b/test/test_pty.rs index 5c27e2d..4cc6620 100644 --- a/test/test_pty.rs +++ b/test/test_pty.rs @@ -2,28 +2,13 @@ use std::fs::File; use std::io::{Read, Write}; use std::os::unix::prelude::*; use std::path::Path; -use tempfile::tempfile; use libc::{_exit, STDOUT_FILENO}; use nix::fcntl::{open, OFlag}; use nix::pty::*; use nix::sys::stat; use nix::sys::termios::*; -use nix::unistd::{close, pause, write}; - -/// Regression test for Issue #659 -/// This is the correct way to explicitly close a `PtyMaster` -#[test] -fn test_explicit_close() { - let mut f = { - let m = posix_openpt(OFlag::O_RDWR).unwrap(); - close(m.into_raw_fd()).unwrap(); - tempfile().unwrap() - }; - // This should work. But if there's been a double close, then it will - // return EBADF - f.write_all(b"whatever").unwrap(); -} +use nix::unistd::{pause, write}; /// Test equivalence of `ptsname` and `ptsname_r` #[test] @@ -50,7 +35,6 @@ fn test_ptsname_copy() { // Open a new PTTY master let master_fd = posix_openpt(OFlag::O_RDWR).unwrap(); - assert!(master_fd.as_raw_fd() > 0); // Get the name of the slave let slave_name1 = unsafe { ptsname(&master_fd) }.unwrap(); @@ -67,7 +51,6 @@ fn test_ptsname_copy() { fn test_ptsname_r_copy() { // Open a new PTTY master let master_fd = posix_openpt(OFlag::O_RDWR).unwrap(); - assert!(master_fd.as_raw_fd() > 0); // Get the name of the slave let slave_name1 = ptsname_r(&master_fd).unwrap(); @@ -84,11 +67,9 @@ fn test_ptsname_unique() { // Open a new PTTY master let master1_fd = posix_openpt(OFlag::O_RDWR).unwrap(); - assert!(master1_fd.as_raw_fd() > 0); // Open a second PTTY master let master2_fd = posix_openpt(OFlag::O_RDWR).unwrap(); - assert!(master2_fd.as_raw_fd() > 0); // Get the name of the slave let slave_name1 = unsafe { ptsname(&master1_fd) }.unwrap(); @@ -147,26 +128,24 @@ fn open_ptty_pair() -> (PtyMaster, File) { /// /// This uses a common `open_ptty_pair` because much of these functions aren't useful by /// themselves. So for this test we perform the basic act of getting a file handle for a -/// master/slave PTTY pair, then just sanity-check the raw values. +/// master/slave PTTY pair. #[test] fn test_open_ptty_pair() { - let (master, slave) = open_ptty_pair(); - assert!(master.as_raw_fd() > 0); - assert!(slave.as_raw_fd() > 0); + let (_, _) = open_ptty_pair(); } /// Put the terminal in raw mode. -fn make_raw(fd: RawFd) { - let mut termios = tcgetattr(fd).unwrap(); +fn make_raw(fd: Fd) { + let mut termios = tcgetattr(&fd).unwrap(); cfmakeraw(&mut termios); - tcsetattr(fd, SetArg::TCSANOW, &termios).unwrap(); + tcsetattr(&fd, SetArg::TCSANOW, &termios).unwrap(); } /// Test `io::Read` on the PTTY master #[test] fn test_read_ptty_pair() { let (mut master, mut slave) = open_ptty_pair(); - make_raw(slave.as_raw_fd()); + make_raw(&slave); let mut buf = [0u8; 5]; slave.write_all(b"hello").unwrap(); @@ -183,7 +162,7 @@ fn test_read_ptty_pair() { #[test] fn test_write_ptty_pair() { let (mut master, mut slave) = open_ptty_pair(); - make_raw(slave.as_raw_fd()); + make_raw(&slave); let mut buf = [0u8; 5]; master.write_all(b"adios").unwrap(); @@ -202,33 +181,28 @@ fn test_openpty() { let _m = crate::PTSNAME_MTX.lock(); let pty = openpty(None, None).unwrap(); - assert!(pty.master > 0); - assert!(pty.slave > 0); // Writing to one should be readable on the other one let string = "foofoofoo\n"; let mut buf = [0u8; 10]; - write(pty.master, string.as_bytes()).unwrap(); - crate::read_exact(pty.slave, &mut buf); + write(pty.master.as_raw_fd(), string.as_bytes()).unwrap(); + crate::read_exact(&pty.slave, &mut buf); assert_eq!(&buf, string.as_bytes()); // Read the echo as well let echoed_string = "foofoofoo\r\n"; let mut buf = [0u8; 11]; - crate::read_exact(pty.master, &mut buf); + crate::read_exact(&pty.master, &mut buf); assert_eq!(&buf, echoed_string.as_bytes()); let string2 = "barbarbarbar\n"; let echoed_string2 = "barbarbarbar\r\n"; let mut buf = [0u8; 14]; - write(pty.slave, string2.as_bytes()).unwrap(); - crate::read_exact(pty.master, &mut buf); + write(pty.slave.as_raw_fd(), string2.as_bytes()).unwrap(); + crate::read_exact(&pty.master, &mut buf); assert_eq!(&buf, echoed_string2.as_bytes()); - - close(pty.master).unwrap(); - close(pty.slave).unwrap(); } #[test] @@ -239,44 +213,34 @@ fn test_openpty_with_termios() { // Open one pty to get attributes for the second one let mut termios = { let pty = openpty(None, None).unwrap(); - assert!(pty.master > 0); - assert!(pty.slave > 0); - let termios = tcgetattr(pty.slave).unwrap(); - close(pty.master).unwrap(); - close(pty.slave).unwrap(); - termios + tcgetattr(&pty.slave).unwrap() }; // Make sure newlines are not transformed so the data is preserved when sent. termios.output_flags.remove(OutputFlags::ONLCR); let pty = openpty(None, &termios).unwrap(); // Must be valid file descriptors - assert!(pty.master > 0); - assert!(pty.slave > 0); // Writing to one should be readable on the other one let string = "foofoofoo\n"; let mut buf = [0u8; 10]; - write(pty.master, string.as_bytes()).unwrap(); - crate::read_exact(pty.slave, &mut buf); + write(pty.master.as_raw_fd(), string.as_bytes()).unwrap(); + crate::read_exact(&pty.slave, &mut buf); assert_eq!(&buf, string.as_bytes()); // read the echo as well let echoed_string = "foofoofoo\n"; - crate::read_exact(pty.master, &mut buf); + crate::read_exact(&pty.master, &mut buf); assert_eq!(&buf, echoed_string.as_bytes()); let string2 = "barbarbarbar\n"; let echoed_string2 = "barbarbarbar\n"; let mut buf = [0u8; 13]; - write(pty.slave, string2.as_bytes()).unwrap(); - crate::read_exact(pty.master, &mut buf); + write(pty.slave.as_raw_fd(), string2.as_bytes()).unwrap(); + crate::read_exact(&pty.master, &mut buf); assert_eq!(&buf, echoed_string2.as_bytes()); - - close(pty.master).unwrap(); - close(pty.slave).unwrap(); } #[test] @@ -303,11 +267,10 @@ fn test_forkpty() { Parent { child } => { let mut buf = [0u8; 10]; assert!(child.as_raw() > 0); - crate::read_exact(pty.master, &mut buf); + crate::read_exact(&pty.master, &mut buf); kill(child, SIGTERM).unwrap(); wait().unwrap(); // keep other tests using generic wait from getting our child assert_eq!(&buf, echoed_string.as_bytes()); - close(pty.master).unwrap(); } } } diff --git a/test/test_ptymaster_drop.rs b/test/test_ptymaster_drop.rs deleted file mode 100644 index ffbaa56..0000000 --- a/test/test_ptymaster_drop.rs +++ /dev/null @@ -1,20 +0,0 @@ -#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))] -mod t { - use nix::fcntl::OFlag; - use nix::pty::*; - use nix::unistd::close; - use std::os::unix::io::AsRawFd; - - /// Regression test for Issue #659 - /// - /// `PtyMaster` should panic rather than double close the file descriptor - /// This must run in its own test process because it deliberately creates a - /// race condition. - #[test] - #[should_panic(expected = "Closing an invalid file descriptor!")] - fn test_double_close() { - let m = posix_openpt(OFlag::O_RDWR).unwrap(); - close(m.as_raw_fd()).unwrap(); - drop(m); // should panic here - } -} diff --git a/test/test_sendfile.rs b/test/test_sendfile.rs index f73a3b5..b85e030 100644 --- a/test/test_sendfile.rs +++ b/test/test_sendfile.rs @@ -1,5 +1,6 @@ use std::io::prelude::*; -use std::os::unix::prelude::*; +#[cfg(any(target_os = "android", target_os = "linux"))] +use std::os::unix::io::{FromRawFd, OwnedFd}; use libc::off_t; use nix::sys::sendfile::*; @@ -23,7 +24,12 @@ fn test_sendfile_linux() { let (rd, wr) = pipe().unwrap(); let mut offset: off_t = 5; - let res = sendfile(wr, tmp.as_raw_fd(), Some(&mut offset), 2).unwrap(); + // The construct of this `OwnedFd` is a temporary workaround, when `pipe(2)` + // becomes I/O-safe: + // pub fn pipe() -> std::result::Result<(OwnedFd, OwnedFd), Error> + // then it is no longer needed. + let wr = unsafe { OwnedFd::from_raw_fd(wr) }; + let res = sendfile(&wr, &tmp, Some(&mut offset), 2).unwrap(); assert_eq!(2, res); @@ -33,7 +39,6 @@ fn test_sendfile_linux() { assert_eq!(7, offset); close(rd).unwrap(); - close(wr).unwrap(); } #[cfg(target_os = "linux")] @@ -45,7 +50,12 @@ fn test_sendfile64_linux() { let (rd, wr) = pipe().unwrap(); let mut offset: libc::off64_t = 5; - let res = sendfile64(wr, tmp.as_raw_fd(), Some(&mut offset), 2).unwrap(); + // The construct of this `OwnedFd` is a temporary workaround, when `pipe(2)` + // becomes I/O-safe: + // pub fn pipe() -> std::result::Result<(OwnedFd, OwnedFd), Error> + // then it is no longer needed. + let wr = unsafe { OwnedFd::from_raw_fd(wr) }; + let res = sendfile64(&wr, &tmp, Some(&mut offset), 2).unwrap(); assert_eq!(2, res); @@ -55,7 +65,6 @@ fn test_sendfile64_linux() { assert_eq!(7, offset); close(rd).unwrap(); - close(wr).unwrap(); } #[cfg(target_os = "freebsd")] @@ -63,10 +72,10 @@ fn test_sendfile64_linux() { fn test_sendfile_freebsd() { // Declare the content let header_strings = - vec!["HTTP/1.1 200 OK\n", "Content-Type: text/plain\n", "\n"]; + ["HTTP/1.1 200 OK\n", "Content-Type: text/plain\n", "\n"]; let body = "Xabcdef123456"; let body_offset = 1; - let trailer_strings = vec!["\n", "Served by Make Believe\n"]; + let trailer_strings = ["\n", "Served by Make Believe\n"]; // Write the body to a file let mut tmp = tempfile().unwrap(); @@ -83,8 +92,8 @@ fn test_sendfile_freebsd() { // Call the test method let (res, bytes_written) = sendfile( - tmp.as_raw_fd(), - wr.as_raw_fd(), + &tmp, + &wr, body_offset as off_t, None, Some(headers.as_slice()), @@ -114,10 +123,10 @@ fn test_sendfile_freebsd() { fn test_sendfile_dragonfly() { // Declare the content let header_strings = - vec!["HTTP/1.1 200 OK\n", "Content-Type: text/plain\n", "\n"]; + ["HTTP/1.1 200 OK\n", "Content-Type: text/plain\n", "\n"]; let body = "Xabcdef123456"; let body_offset = 1; - let trailer_strings = vec!["\n", "Served by Make Believe\n"]; + let trailer_strings = ["\n", "Served by Make Believe\n"]; // Write the body to a file let mut tmp = tempfile().unwrap(); @@ -134,8 +143,8 @@ fn test_sendfile_dragonfly() { // Call the test method let (res, bytes_written) = sendfile( - tmp.as_raw_fd(), - wr.as_raw_fd(), + &tmp, + &wr, body_offset as off_t, None, Some(headers.as_slice()), @@ -183,8 +192,8 @@ fn test_sendfile_darwin() { // Call the test method let (res, bytes_written) = sendfile( - tmp.as_raw_fd(), - wr.as_raw_fd(), + &tmp, + &wr, body_offset as off_t, None, Some(headers.as_slice()), diff --git a/test/test_unistd.rs b/test/test_unistd.rs index 9e20f97..10284e4 100644 --- a/test/test_unistd.rs +++ b/test/test_unistd.rs @@ -56,11 +56,11 @@ fn test_fork_and_waitpid() { // panic, must never happen s @ Ok(_) => { - panic!("Child exited {:?}, should never happen", s) + panic!("Child exited {s:?}, should never happen") } // panic, waitpid should never fail - Err(s) => panic!("Error: waitpid returned Err({:?}", s), + Err(s) => panic!("Error: waitpid returned Err({s:?}"), } } } @@ -94,7 +94,7 @@ fn test_mkstemp() { close(fd).unwrap(); unlink(path.as_path()).unwrap(); } - Err(e) => panic!("mkstemp failed: {}", e), + Err(e) => panic!("mkstemp failed: {e}"), } } @@ -555,16 +555,13 @@ fn test_lseek() { const CONTENTS: &[u8] = b"abcdef123456"; let mut tmp = tempfile().unwrap(); tmp.write_all(CONTENTS).unwrap(); - let tmpfd = tmp.into_raw_fd(); let offset: off_t = 5; - lseek(tmpfd, offset, Whence::SeekSet).unwrap(); + lseek(tmp.as_raw_fd(), offset, Whence::SeekSet).unwrap(); let mut buf = [0u8; 7]; - crate::read_exact(tmpfd, &mut buf); + crate::read_exact(&tmp, &mut buf); assert_eq!(b"f123456", &buf); - - close(tmpfd).unwrap(); } #[cfg(any(target_os = "linux", target_os = "android"))] @@ -573,15 +570,12 @@ fn test_lseek64() { const CONTENTS: &[u8] = b"abcdef123456"; let mut tmp = tempfile().unwrap(); tmp.write_all(CONTENTS).unwrap(); - let tmpfd = tmp.into_raw_fd(); - lseek64(tmpfd, 5, Whence::SeekSet).unwrap(); + lseek64(tmp.as_raw_fd(), 5, Whence::SeekSet).unwrap(); let mut buf = [0u8; 7]; - crate::read_exact(tmpfd, &mut buf); + crate::read_exact(&tmp, &mut buf); assert_eq!(b"f123456", &buf); - - close(tmpfd).unwrap(); } cfg_if! { @@ -778,15 +772,12 @@ fn test_ftruncate() { let tempdir = tempdir().unwrap(); let path = tempdir.path().join("file"); - let tmpfd = { - let mut tmp = File::create(&path).unwrap(); - const CONTENTS: &[u8] = b"12345678"; - tmp.write_all(CONTENTS).unwrap(); - tmp.into_raw_fd() - }; + let mut file = File::create(&path).unwrap(); + const CONTENTS: &[u8] = b"12345678"; + file.write_all(CONTENTS).unwrap(); - ftruncate(tmpfd, 2).unwrap(); - close(tmpfd).unwrap(); + ftruncate(&file, 2).unwrap(); + drop(file); let metadata = fs::metadata(&path).unwrap(); assert_eq!(2, metadata.len()); @@ -799,12 +790,7 @@ static mut ALARM_CALLED: bool = false; // Used in `test_alarm`. #[cfg(not(target_os = "redox"))] pub extern "C" fn alarm_signal_handler(raw_signal: libc::c_int) { - assert_eq!( - raw_signal, - libc::SIGALRM, - "unexpected signal: {}", - raw_signal - ); + assert_eq!(raw_signal, libc::SIGALRM, "unexpected signal: {raw_signal}"); unsafe { ALARM_CALLED = true }; } -- cgit v1.2.3 From c7abcf1bf01b3e41fa768b73859368828daee178 Mon Sep 17 00:00:00 2001 From: Andrew Walbran Date: Thu, 7 Dec 2023 12:16:46 +0000 Subject: Use from_bits_retain for all Termios wrapper fields. In case there are any unrecognised bits, they should be kept when setting fields of the underlying libc termios struct in get_libc_termios, rather than being dropped. This ensures that termios can be roundtripped even with unrecognised bits set. Test: Treehugger Change-Id: I478b660042edac07b82dedf5fe997e5deac14777 --- src/sys/termios.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sys/termios.rs b/src/sys/termios.rs index ecaa3ea..74c5fc5 100644 --- a/src/sys/termios.rs +++ b/src/sys/termios.rs @@ -307,10 +307,10 @@ impl Termios { /// Updates the wrapper values from the internal `libc::termios` data structure. pub(crate) fn update_wrapper(&mut self) { let termios = *self.inner.borrow_mut(); - self.input_flags = InputFlags::from_bits_truncate(termios.c_iflag); - self.output_flags = OutputFlags::from_bits_truncate(termios.c_oflag); + self.input_flags = InputFlags::from_bits_retain(termios.c_iflag); + self.output_flags = OutputFlags::from_bits_retain(termios.c_oflag); self.control_flags = ControlFlags::from_bits_retain(termios.c_cflag); - self.local_flags = LocalFlags::from_bits_truncate(termios.c_lflag); + self.local_flags = LocalFlags::from_bits_retain(termios.c_lflag); self.control_chars = termios.c_cc; #[cfg(any( target_os = "linux", @@ -327,10 +327,10 @@ impl From for Termios { fn from(termios: libc::termios) -> Self { Termios { inner: RefCell::new(termios), - input_flags: InputFlags::from_bits_truncate(termios.c_iflag), - output_flags: OutputFlags::from_bits_truncate(termios.c_oflag), - control_flags: ControlFlags::from_bits_truncate(termios.c_cflag), - local_flags: LocalFlags::from_bits_truncate(termios.c_lflag), + input_flags: InputFlags::from_bits_retain(termios.c_iflag), + output_flags: OutputFlags::from_bits_retain(termios.c_oflag), + control_flags: ControlFlags::from_bits_retain(termios.c_cflag), + local_flags: LocalFlags::from_bits_retain(termios.c_lflag), control_chars: termios.c_cc, #[cfg(any( target_os = "linux", -- cgit v1.2.3 From 34237e29e055f8ad5c314eb31ea8aecba9a2d55e Mon Sep 17 00:00:00 2001 From: Andrew Walbran Date: Tue, 14 Nov 2023 16:30:51 +0000 Subject: Migrate to cargo_embargo. Bug: 293289578 Test: Ran cargo_embargo, compared Android.bp Change-Id: I97059061e4e60ea45f1a212d4ad41159c0df1f4f --- Android.bp | 2 +- cargo2android.json | 8 -------- cargo_embargo.json | 20 ++++++++++++++++++++ 3 files changed, 21 insertions(+), 9 deletions(-) delete mode 100644 cargo2android.json create mode 100644 cargo_embargo.json diff --git a/Android.bp b/Android.bp index 1f8cb07..a6b444a 100644 --- a/Android.bp +++ b/Android.bp @@ -1,4 +1,4 @@ -// This file is generated by cargo2android.py --config cargo2android.json. +// This file is generated by cargo_embargo. // Do not modify this file as changes will be overridden on upgrade. package { diff --git a/cargo2android.json b/cargo2android.json deleted file mode 100644 index 4045280..0000000 --- a/cargo2android.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "dependencies": true, - "device": true, - "features": "event,feature,fs,ioctl,mount,mman,poll,sched,signal,socket,term,time,uio,user", - "min-sdk-version": "29", - "run": true, - "vendor-available": true -} diff --git a/cargo_embargo.json b/cargo_embargo.json new file mode 100644 index 0000000..30b09cd --- /dev/null +++ b/cargo_embargo.json @@ -0,0 +1,20 @@ +{ + "features": [ + "event", + "feature", + "fs", + "ioctl", + "mount", + "mman", + "poll", + "sched", + "signal", + "socket", + "term", + "time", + "uio", + "user" + ], + "min_sdk_version": "29", + "run_cargo": false +} -- cgit v1.2.3 From 02994e4018b5e81efd3911350f1db83d79d49588 Mon Sep 17 00:00:00 2001 From: Felix Obenhuber Date: Tue, 16 Jan 2024 08:31:34 +0100 Subject: Enable the net feature The net feature of nix enables networking related functionality that is useful when interacting with e.g raw sockets. Test: Treehugger Change-Id: I2b0db54eb692181753c8a484c2235ec7f8e3d46f --- Android.bp | 1 + cargo_embargo.json | 1 + 2 files changed, 2 insertions(+) diff --git a/Android.bp b/Android.bp index a6b444a..ddbca61 100644 --- a/Android.bp +++ b/Android.bp @@ -37,6 +37,7 @@ rust_library { "memoffset", "mman", "mount", + "net", "poll", "process", "sched", diff --git a/cargo_embargo.json b/cargo_embargo.json index 30b09cd..6df39c1 100644 --- a/cargo_embargo.json +++ b/cargo_embargo.json @@ -6,6 +6,7 @@ "ioctl", "mount", "mman", + "net", "poll", "sched", "signal", -- cgit v1.2.3