Bug 1977514 - Updated nix to version 0.30.1 a=RyanVM

Original Revision: https://phabricator.services.mozilla.com/D258707

Differential Revision: https://phabricator.services.mozilla.com/D262344
This commit is contained in:
Gabriele Svelto
2025-08-25 17:25:37 +00:00
committed by rvandermeulen@mozilla.com
parent 714b7245e2
commit 9e514a4382
85 changed files with 5976 additions and 1682 deletions

17
Cargo.lock generated
View File

@@ -1154,7 +1154,7 @@ dependencies = [
"anyhow",
"crash_helper_common",
"minidump-writer",
"nix 0.29.0",
"nix 0.30.1",
"num-derive",
"num-traits",
"rust_minidump_writer_linux",
@@ -1166,7 +1166,7 @@ name = "crash_helper_common"
version = "0.1.0"
dependencies = [
"minidump-writer",
"nix 0.29.0",
"nix 0.30.1",
"num-derive",
"num-traits",
"thiserror 2.0.9",
@@ -1190,7 +1190,7 @@ dependencies = [
"mozannotation_server",
"mozbuild",
"mozilla-central-workspace-hack",
"nix 0.29.0",
"nix 0.30.1",
"num-derive",
"num-traits",
"once_cell",
@@ -4831,14 +4831,21 @@ checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
name = "nix"
version = "0.26.99"
dependencies = [
"nix 0.29.0",
"nix 0.30.1",
]
[[package]]
name = "nix"
version = "0.29.0"
dependencies = [
"nix 0.30.1",
]
[[package]]
name = "nix"
version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
dependencies = [
"bitflags 2.9.0",
"cfg-if",

View File

@@ -166,8 +166,11 @@ bindgen_0_63 = { package = "bindgen", path = "build/rust/bindgen-0.63" }
# and for bug 1945218
bindgen = { path = "third_party/rust/bindgen" }
# Patch nix 0.26 to 0.29
nix = { path = "build/rust/nix" }
# Patch nix 0.26 to 0.30+
nix_0_26 = { package = "nix", path = "build/rust/nix" }
# Patch nix 0.29 to 0.30+
nix_0_30 = { package = "nix", path = "build/rust/nix-0.29" }
# Patch derive_more 0.99 to 1.0.0-beta
derive_more = { path = "build/rust/derive_more" }

View File

@@ -0,0 +1,37 @@
[package]
name = "nix"
version = "0.29.0"
edition = "2018"
license = "MPL-2.0"
[lib]
path = "lib.rs"
[dependencies.nix]
version = "0.30"
defaultfeatures = false
[features]
acct = ["nix/acct"]
aio = ["nix/aio"]
default = ["nix/default"]
dir = ["nix/dir"]
env = ["nix/env"]
event = ["nix/event"]
feature = ["nix/feature"]
fs = ["nix/fs"]
hostname = ["nix/hostname"]
inotify = ["nix/inotify"]
ioctl = ["nix/ioctl"]
kmod = ["nix/kmod"]
mman = ["nix/mman"]
mount = ["nix/mount"]
mqueue = ["nix/mqueue"]
net = ["nix/net"]
poll = ["nix/poll"]
process = ["nix/process"]
ptrace = ["nix/ptrace"]
signal = ["nix/signal"]
socket = ["nix/socket"]
uio = ["nix/uio"]
user = ["nix/user"]

View File

@@ -0,0 +1,5 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
pub use nix::*;

View File

@@ -8,8 +8,8 @@ license = "MPL-2.0"
path = "lib.rs"
[dependencies.nix]
version = "0.29"
default-features = false
version = "0.30"
defaultfeatures = false
[features]
acct = ["nix/acct"]
@@ -28,3 +28,10 @@ mman = ["nix/mman"]
mount = ["nix/mount"]
mqueue = ["nix/mqueue"]
net = ["nix/net"]
poll = ["nix/poll"]
process = ["nix/process"]
ptrace = ["nix/ptrace"]
signal = ["nix/signal"]
socket = ["nix/socket"]
uio = ["nix/uio"]
user = ["nix/user"]

View File

@@ -3859,6 +3859,12 @@ who = "Alex Franchuk <afranchuk@mozilla.com>"
criteria = "safe-to-deploy"
delta = "0.28.0 -> 0.29.0"
[[audits.nix]]
who = "Gabriele Svelto <gsvelto@mozilla.com>"
criteria = "safe-to-deploy"
delta = "0.29.0 -> 0.30.1"
notes = "Some new wrappers, support for minor platforms and lots of work around type safety that reduces the unsafe surafce."
[[audits.nom]]
who = "Mike Hommey <mh+mozilla@glandium.org>"
criteria = "safe-to-deploy"

File diff suppressed because one or more lines are too long

View File

@@ -3,6 +3,161 @@ This project adheres to [Semantic Versioning](https://semver.org/).
# Change Log
## [0.30.1] - 2025-05-04
### Fixed
- doc.rs build
([#2634](https://github.com/nix-rust/nix/pull/2634))
## [0.30.0] - 2025-04-29
### Added
- Add socket option `IPV6_PKTINFO` for BSDs/Linux/Android, also
`IPV6_RECVPKTINFO` for DragonFlyBSD
([#2113](https://github.com/nix-rust/nix/pull/2113))
- Add `fcntl`'s `F_PREALLOCATE` constant for Apple targets.
([#2393](https://github.com/nix-rust/nix/pull/2393))
- Improve support for extracting the TTL / Hop Limit from incoming packets
and support for DSCP (ToS / Traffic Class).
([#2425](https://github.com/nix-rust/nix/pull/2425))
- Add socket option IP_TOS (nix::sys::socket::sockopt::IpTos) IPV6_TCLASS
(nix::sys::socket::sockopt::Ipv6TClass) on Android/FreeBSD
([#2464](https://github.com/nix-rust/nix/pull/2464))
- Add `SeekData` and `SeekHole` to `Whence` for hurd and apple targets
([#2473](https://github.com/nix-rust/nix/pull/2473))
- Add `From` trait implementation between `SocketAddr` and `Sockaddr`,
`Sockaddr6` ([#2474](https://github.com/nix-rust/nix/pull/2474))
- Added wrappers for `posix_spawn` API
([#2475](https://github.com/nix-rust/nix/pull/2475))
- Add the support for Emscripten.
([#2477](https://github.com/nix-rust/nix/pull/2477))
- Add fcntl constant `F_RDADVISE` for Apple target
([#2480](https://github.com/nix-rust/nix/pull/2480))
- Add fcntl constant `F_RDAHEAD` for Apple target
([#2482](https://github.com/nix-rust/nix/pull/2482))
- Add `F_LOG2PHYS` and `F_LOG2PHYS_EXT` for Apple target
([#2483](https://github.com/nix-rust/nix/pull/2483))
- `MAP_SHARED_VALIDATE` was added for all linux targets. & `MAP_SYNC` was added
for linux with the exclusion of mips architecures, and uclibc
([#2499](https://github.com/nix-rust/nix/pull/2499))
- Add `getregs()`/`getregset()`/`setregset()` for Linux/musl/aarch64
([#2502](https://github.com/nix-rust/nix/pull/2502))
- Add FcntlArgs `F_TRANSFEREXTENTS` constant for Apple targets
([#2504](https://github.com/nix-rust/nix/pull/2504))
- Add `MapFlags::MAP_STACK` in `sys::man` for netbsd
([#2526](https://github.com/nix-rust/nix/pull/2526))
- Add support for `libc::LOCAL_PEERTOKEN` in `getsockopt`.
([#2529](https://github.com/nix-rust/nix/pull/2529))
- Add support for `syslog`, `openlog`, `closelog` on all `unix`.
([#2537](https://github.com/nix-rust/nix/pull/2537))
- Add the `TCP_FUNCTION_BLK` sockopt, on FreeBSD.
([#2539](https://github.com/nix-rust/nix/pull/2539))
- Implements `Into<OwnedFd>` for `PtyMaster/Fanotify/Inotify/SignalFd/TimerFd`
([#2548](https://github.com/nix-rust/nix/pull/2548))
- Add `MremapFlags::MREMAP_DONTUNMAP` to `sys::mman::mremap` for linux target.
([#2555](https://github.com/nix-rust/nix/pull/2555))
- Added `sockopt_impl!` to the public API. It's now possible for users to
define
their own sockopts without needing to make a PR to Nix.
([#2556](https://github.com/nix-rust/nix/pull/2556))
- Add the `TCP_FUNCTION_ALIAS` sockopt, on FreeBSD.
([#2558](https://github.com/nix-rust/nix/pull/2558))
- Add `sys::mman::MmapAdvise` `MADV_PAGEOUT`, `MADV_COLD`, `MADV_WIPEONFORK`,
`MADV_KEEPONFORK` for Linux and Android targets
([#2559](https://github.com/nix-rust/nix/pull/2559))
- Add socket protocol `Sctp`, as well as `MSG_NOTIFICATION` for non-Android
Linux targets. ([#2562](https://github.com/nix-rust/nix/pull/2562))
- Added `from_owned_fd` constructor to `EventFd`
([#2563](https://github.com/nix-rust/nix/pull/2563))
- Add `sys::mman::MmapAdvise` `MADV_POPULATE_READ`, `MADV_POPULATE_WRITE` for
Linux and Android targets
([#2565](https://github.com/nix-rust/nix/pull/2565))
- Added `from_owned_fd` constructor to
`PtyMaster/Fanotify/Inotify/SignalFd/TimerFd`
([#2566](https://github.com/nix-rust/nix/pull/2566))
- Added `FcntlArg::F_READAHEAD` for FreeBSD target
([#2569](https://github.com/nix-rust/nix/pull/2569))
- Added `sockopt::LingerSec` for Apple targets
([#2572](https://github.com/nix-rust/nix/pull/2572))
- Added `sockopt::EsclBind` for solarish targets
([#2573](https://github.com/nix-rust/nix/pull/2573))
- Exposed the ```std::os::fd::AsRawFd``` trait method for
```nix::sys::fanotify::Fanotify``` struct
([#2575](https://github.com/nix-rust/nix/pull/2575))
- Add support for syslog's `setlogmask` on all `unix`.
([#2579](https://github.com/nix-rust/nix/pull/2579))
- Added Fuchsia support for `ioctl`.
([#2580](https://github.com/nix-rust/nix/pull/2580))
- Add ```sys::socket::SockProtocol::EthIp```,
```sys::socket::SockProtocol::EthIpv6```,
```sys::socket::SockProtocol::EthLoop```
([#2581](https://github.com/nix-rust/nix/pull/2581))
- Add OpenHarmony target into CI and Update documents.
([#2599](https://github.com/nix-rust/nix/pull/2599))
- Added the TcpMaxSeg `setsockopt` option for apple targets
([#2603](https://github.com/nix-rust/nix/pull/2603))
- Add `FilAttach` and `FilDetach` to socket::sockopt for Illumos
([#2611](https://github.com/nix-rust/nix/pull/2611))
- Add `PeerPidfd` (`SO_PEERPIDFD`) to `socket::sockopt` for Linux
([#2620](https://github.com/nix-rust/nix/pull/2620))
- Added `socket::sockopt::AttachReusePortCbpf` for Linux
([#2621](https://github.com/nix-rust/nix/pull/2621))
- Add `ptrace::syscall_info` for linux/glibc
([#2627](https://github.com/nix-rust/nix/pull/2627))
### Changed
- Module sys/signal now adopts I/O safety
([#1936](https://github.com/nix-rust/nix/pull/1936))
- Change the type of the `name` argument of `memfd_create()` from `&CStr` to
`<P: NixPath>(name: &P)` ([#2431](https://github.com/nix-rust/nix/pull/2431))
- Public interfaces in `fcntl.rs` and `dir.rs` now use I/O-safe types.
([#2434](https://github.com/nix-rust/nix/pull/2434))
- Module `sys/stat` now adopts I/O safety.
([#2439](https://github.com/nix-rust/nix/pull/2439))
- Module unistd now adopts I/O safety.
([#2440](https://github.com/nix-rust/nix/pull/2440))
- Module sys/fanotify now adopts I/O safety
([#2443](https://github.com/nix-rust/nix/pull/2443))
- Socket option `IpTos` has been renamed to `Ipv4Tos`, the old symbol is
deprecated since 0.30.0 ([#2465](https://github.com/nix-rust/nix/pull/2465))
- Rename Flags `EventFlag` to `EvFlags`, and `MemFdCreateFlag` to `MFdFlags`
([#2476](https://github.com/nix-rust/nix/pull/2476))
- Made `nix::sys::socket::UnknownCmsg` public and more readable
([#2520](https://github.com/nix-rust/nix/pull/2520))
- recvmsg: take slice for cmsg_buffer instead of Vec
([#2524](https://github.com/nix-rust/nix/pull/2524))
- linkat: allow distinct types for path arguments
([#2582](https://github.com/nix-rust/nix/pull/2582))
### Fixed
- Disable unsupported signals on sparc-linux
([#2454](https://github.com/nix-rust/nix/pull/2454))
- Fix cmsg_len() return type on OpenHarmony
([#2456](https://github.com/nix-rust/nix/pull/2456))
- The `ns` argument of `sys::prctl::set_timerslack()` should be of type
`c_ulong` ([#2505](https://github.com/nix-rust/nix/pull/2505))
- Properly exclude NUL characters from `OSString`s returned by `getsockopt`.
([#2557](https://github.com/nix-rust/nix/pull/2557))
- Fixes the build on OpenHarmony
([#2587](https://github.com/nix-rust/nix/pull/2587))
### Removed
- Type `SigevNotify` is no longer `PartialEq`, `Eq` and `Hash` due to the use
of `BorrowedFd` ([#1936](https://github.com/nix-rust/nix/pull/1936))
- `EventFd::defuse()` is removed because it does nothing, `EventFd::arm()` is
also removed for symmetry reasons.
([#2452](https://github.com/nix-rust/nix/pull/2452))
- Removed the `Copy` trait from `PollFd`
([#2631](https://github.com/nix-rust/nix/pull/2631))
## [0.29.0] - 2024-05-24

48
third_party/rust/nix/Cargo.lock generated vendored
View File

@@ -72,20 +72,21 @@ checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
[[package]]
name = "getrandom"
version = "0.2.15"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasi",
]
[[package]]
name = "libc"
version = "0.2.155"
version = "0.2.172"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
[[package]]
name = "linux-raw-sys"
@@ -114,7 +115,7 @@ dependencies = [
[[package]]
name = "nix"
version = "0.29.0"
version = "0.30.1"
dependencies = [
"assert-impl",
"bitflags 2.5.0",
@@ -185,21 +186,26 @@ dependencies = [
]
[[package]]
name = "rand"
version = "0.8.5"
name = "r-efi"
version = "5.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
[[package]]
name = "rand"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core",
@@ -207,9 +213,9 @@ dependencies = [
[[package]]
name = "rand_core"
version = "0.6.4"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
dependencies = [
"getrandom",
]
@@ -337,9 +343,12 @@ dependencies = [
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
version = "0.14.2+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
dependencies = [
"wit-bindgen-rt",
]
[[package]]
name = "winapi-util"
@@ -422,3 +431,12 @@ name = "windows_x86_64_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
[[package]]
name = "wit-bindgen-rt"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
dependencies = [
"bitflags 2.5.0",
]

View File

@@ -13,8 +13,9 @@
edition = "2021"
rust-version = "1.69"
name = "nix"
version = "0.29.0"
version = "0.30.1"
authors = ["The nix-rust Project Developers"]
build = "build.rs"
include = [
"build.rs",
"src/**/*",
@@ -23,6 +24,11 @@ include = [
"README.md",
"CHANGELOG.md",
]
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Rust friendly bindings to *nix APIs"
readme = "README.md"
categories = ["os::unix-apis"]
@@ -44,70 +50,18 @@ targets = [
"x86_64-unknown-openbsd",
"x86_64-unknown-netbsd",
"x86_64-unknown-dragonfly",
"x86_64-fuchsia",
"x86_64-unknown-fuchsia",
"x86_64-unknown-redox",
"x86_64-unknown-illumos",
]
[[test]]
name = "test"
path = "test/test.rs"
[[test]]
name = "test-aio-drop"
path = "test/sys/test_aio_drop.rs"
[[test]]
name = "test-clearenv"
path = "test/test_clearenv.rs"
[[test]]
name = "test-prctl"
path = "test/sys/test_prctl.rs"
[dependencies.bitflags]
version = "2.3.1"
[dependencies.cfg-if]
version = "1.0"
[dependencies.libc]
version = "0.2.155"
features = ["extra_traits"]
[dependencies.memoffset]
version = "0.9"
optional = true
[dependencies.pin-utils]
version = "0.1.0"
optional = true
[dev-dependencies.assert-impl]
version = "0.1"
[dev-dependencies.parking_lot]
version = "0.12"
[dev-dependencies.rand]
version = "0.8"
[dev-dependencies.semver]
version = "1.0.7"
[dev-dependencies.tempfile]
version = "3.7.1"
[build-dependencies.cfg_aliases]
version = "0.2"
[features]
acct = []
aio = ["pin-utils"]
default = []
dir = ["fs"]
env = []
event = []
event = ["poll"]
fanotify = []
feature = []
fs = []
@@ -130,6 +84,7 @@ resource = []
sched = ["process"]
signal = ["process"]
socket = ["memoffset"]
syslog = []
term = []
time = []
ucontext = ["signal"]
@@ -140,8 +95,64 @@ zerocopy = [
"uio",
]
[target."cfg(any(target_os = \"android\", target_os = \"linux\"))".dev-dependencies.caps]
[lib]
name = "nix"
path = "src/lib.rs"
[[test]]
name = "test"
path = "test/test.rs"
[[test]]
name = "test-aio-drop"
path = "test/sys/test_aio_drop.rs"
[[test]]
name = "test-clearenv"
path = "test/test_clearenv.rs"
[[test]]
name = "test-prctl"
path = "test/sys/test_prctl.rs"
[dependencies.bitflags]
version = "2.3.3"
[dependencies.cfg-if]
version = "1.0"
[dependencies.libc]
version = "0.2.171"
features = ["extra_traits"]
[dependencies.memoffset]
version = "0.9"
optional = true
[dependencies.pin-utils]
version = "0.1.0"
optional = true
[dev-dependencies.assert-impl]
version = "0.1"
[dev-dependencies.parking_lot]
version = "0.12"
[dev-dependencies.rand]
version = "0.9"
[dev-dependencies.semver]
version = "1.0.7"
[dev-dependencies.tempfile]
version = "3.7.1"
[build-dependencies.cfg_aliases]
version = "0.2.1"
[target.'cfg(any(target_os = "android", target_os = "linux"))'.dev-dependencies.caps]
version = "0.5.3"
[target."cfg(target_os = \"freebsd\")".dev-dependencies.sysctl]
[target.'cfg(target_os = "freebsd")'.dev-dependencies.sysctl]
version = "0.4"

View File

@@ -2,9 +2,9 @@
[![Cirrus Build Status](https://api.cirrus-ci.com/github/nix-rust/nix.svg)](https://cirrus-ci.com/github/nix-rust/nix)
[![crates.io](https://img.shields.io/crates/v/nix.svg)](https://crates.io/crates/nix)
[![maintenance-status](https://img.shields.io/badge/maintenance-looking--for--maintainer-orange.svg)](https://github.com/nix-rust/nix/issues/2132)
[Documentation (Releases)](https://docs.rs/nix/)
[![docs.rs](https://img.shields.io/badge/docs.rs-nix-blue?style=flat-square&logo=docs.rs)](https://docs.rs/nix)
![maintenance-status](https://img.shields.io/badge/maintenance-actively--developed-brightgreen.svg)
[![msrv](https://img.shields.io/badge/msrv-1.69-blue?style=flat-square&logo=rust)](https://www.rust-lang.org)
Nix seeks to provide friendly bindings to various *nix platform APIs (Linux, Darwin,
...). The goal is to not provide a 100% unified interface, but to unify
@@ -81,19 +81,23 @@ The following targets are supported by `nix`:
<ul>
<li>aarch64-apple-ios</li>
<li>aarch64-linux-android</li>
<li>aarch64-unknown-linux-ohos</li>
<li>arm-linux-androideabi</li>
<li>arm-unknown-linux-musleabi</li>
<li>armv7-linux-androideabi</li>
<li>armv7-unknown-linux-ohos</li>
<li>i686-linux-android</li>
<li>loongarch64-unknown-linux-gnu</li>
<li>s390x-unknown-linux-gnu</li>
<li>x86_64-linux-android</li>
<li>x86_64-unknown-illumos</li>
<li>x86_64-unknown-linux-ohos</li>
<li>x86_64-unknown-netbsd</li>
</td>
<td>
<li>armv7-unknown-linux-uclibceabihf</li>
<li>powerpc64-unknown-linux-gnu</li>
<li>x86_64-fuchsia</li>
<li>x86_64-unknown-fuchsia</li>
<li>x86_64-unknown-dragonfly</li>
<li>x86_64-unknown-haiku</li>
<li>x86_64-unknown-linux-gnux32</li>
@@ -114,7 +118,7 @@ changed in the future without bumping the major or minor version.
Contributions are very welcome. Please See [CONTRIBUTING](CONTRIBUTING.md) for
additional details.
Feel free to join us in [the nix-rust/nix](https://gitter.im/nix-rust/nix) channel on Gitter to
Feel free to join us in [the nix-rust/nix](https://discord.com/invite/rkBeJUsmyd) channel on Discord to
discuss `nix` development.
## License

View File

@@ -27,14 +27,9 @@ fn main() {
solarish: { any(illumos, solaris) },
}
// Below are Nix's custom cfg values that we need to let the compiler know
println!("cargo:rustc-check-cfg=cfg(apple_targets)");
println!("cargo:rustc-check-cfg=cfg(bsd)");
println!("cargo:rustc-check-cfg=cfg(bsd_without_apple)");
println!("cargo:rustc-check-cfg=cfg(linux_android)");
println!("cargo:rustc-check-cfg=cfg(freebsdlike)");
println!("cargo:rustc-check-cfg=cfg(netbsdlike)");
println!("cargo:rustc-check-cfg=cfg(solarish)");
// Below are custom cfg values set during some CI steps.
println!("cargo:rustc-check-cfg=cfg(fbsd14)");
println!("cargo:rustc-check-cfg=cfg(qemu)");
// Cygwin target, added in 1.86
println!("cargo:rustc-check-cfg=cfg(target_os, values(\"cygwin\"))");
}

View File

@@ -3,7 +3,7 @@
use crate::errno::Errno;
use crate::fcntl::{self, OFlag};
use crate::sys;
use crate::{Error, NixPath, Result};
use crate::{NixPath, Result};
use cfg_if::cfg_if;
use std::ffi;
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
@@ -17,17 +17,38 @@ use libc::{dirent, readdir_r};
/// An open directory.
///
/// This is a lower-level interface than `std::fs::ReadDir`. Notable differences:
/// * can be opened from a file descriptor (as returned by `openat`, perhaps before knowing
/// if the path represents a file or directory).
/// * implements `AsRawFd`, so it can be passed to `fstat`, `openat`, etc.
/// The file descriptor continues to be owned by the `Dir`, so callers must not keep a `RawFd`
/// after the `Dir` is dropped.
/// This is a lower-level interface than [`std::fs::ReadDir`]. Notable differences:
/// * can be opened from a file descriptor (as returned by [`openat`][openat],
/// perhaps before knowing if the path represents a file or directory).
/// * implements [`AsFd`][AsFd], so it can be passed to [`fstat`][fstat],
/// [`openat`][openat], etc. The file descriptor continues to be owned by the
/// `Dir`, so callers must not keep a `RawFd` after the `Dir` is dropped.
/// * can be iterated through multiple times without closing and reopening the file
/// descriptor. Each iteration rewinds when finished.
/// * returns entries for `.` (current directory) and `..` (parent directory).
/// * returns entries' names as a `CStr` (no allocation or conversion beyond whatever libc
/// * returns entries' names as a [`CStr`][cstr] (no allocation or conversion beyond whatever libc
/// does).
///
/// [AsFd]: std::os::fd::AsFd
/// [fstat]: crate::sys::stat::fstat
/// [openat]: crate::fcntl::openat
/// [cstr]: std::ffi::CStr
///
/// # Examples
///
/// Traverse the current directory, and print entries' names:
///
/// ```
/// use nix::dir::Dir;
/// use nix::fcntl::OFlag;
/// use nix::sys::stat::Mode;
///
/// let mut cwd = Dir::open(".", OFlag::O_RDONLY | OFlag::O_CLOEXEC, Mode::empty()).unwrap();
/// for res_entry in cwd.iter() {
/// let entry = res_entry.unwrap();
/// println!("File name: {}", entry.file_name().to_string_lossy());
/// }
/// ```
#[derive(Debug, Eq, Hash, PartialEq)]
pub struct Dir(ptr::NonNull<libc::DIR>);
@@ -43,8 +64,8 @@ impl Dir {
}
/// Opens the given path as with `fcntl::openat`.
pub fn openat<P: ?Sized + NixPath>(
dirfd: Option<RawFd>,
pub fn openat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
dirfd: Fd,
path: &P,
oflag: OFlag,
mode: sys::stat::Mode,
@@ -54,21 +75,46 @@ impl Dir {
}
/// Converts from a descriptor-based object, closing the descriptor on success or failure.
///
/// # Safety
///
/// It is only safe if `fd` is an owned file descriptor.
#[inline]
pub fn from<F: IntoRawFd>(fd: F) -> Result<Self> {
Dir::from_fd(fd.into_raw_fd())
#[deprecated(
since = "0.30.0",
note = "Deprecate this since it is not I/O-safe, use from_fd instead."
)]
pub unsafe fn from<F: IntoRawFd>(fd: F) -> Result<Self> {
use std::os::fd::FromRawFd;
use std::os::fd::OwnedFd;
// SAFETY:
//
// This is indeed unsafe is `fd` it not an owned fd.
let owned_fd = unsafe { OwnedFd::from_raw_fd(fd.into_raw_fd()) };
Dir::from_fd(owned_fd)
}
/// Converts from a file descriptor, closing it on failure.
///
/// # Examples
///
/// `ENOTDIR` would be returned if `fd` does not refer to a directory:
///
/// ```should_panic
/// use std::os::fd::OwnedFd;
/// use nix::dir::Dir;
///
/// let temp_file = tempfile::tempfile().unwrap();
/// let temp_file_fd: OwnedFd = temp_file.into();
/// let never = Dir::from_fd(temp_file_fd).unwrap();
/// ```
#[doc(alias("fdopendir"))]
pub fn from_fd(fd: RawFd) -> Result<Self> {
let d = ptr::NonNull::new(unsafe { libc::fdopendir(fd) }).ok_or_else(
|| {
let e = Error::last();
unsafe { libc::close(fd) };
e
},
)?;
pub fn from_fd(fd: std::os::fd::OwnedFd) -> Result<Self> {
// take the ownership as the constructed `Dir` is now the owner
let raw_fd = fd.into_raw_fd();
let d = ptr::NonNull::new(unsafe { libc::fdopendir(raw_fd) })
.ok_or(Errno::last())?;
Ok(Dir(d))
}
@@ -86,6 +132,18 @@ impl Dir {
// `Dir` is safe to pass from one thread to another, as it's not reference-counted.
unsafe impl Send for Dir {}
impl std::os::fd::AsFd for Dir {
fn as_fd(&self) -> std::os::fd::BorrowedFd {
let raw_fd = self.as_raw_fd();
// SAFETY:
//
// `raw_fd` should be open and valid for the lifetime of the returned
// `BorrowedFd` as it is extracted from `&self`.
unsafe { std::os::fd::BorrowedFd::borrow_raw(raw_fd) }
}
}
impl AsRawFd for Dir {
fn as_raw_fd(&self) -> RawFd {
unsafe { libc::dirfd(self.0.as_ptr()) }
@@ -132,7 +190,7 @@ fn next(dir: &mut Dir) -> Option<Result<Entry>> {
#[derive(Debug, Eq, Hash, PartialEq)]
pub struct Iter<'d>(&'d mut Dir);
impl<'d> Iterator for Iter<'d> {
impl Iterator for Iter<'_> {
type Item = Result<Entry>;
fn next(&mut self) -> Option<Self::Item> {
@@ -140,7 +198,7 @@ impl<'d> Iterator for Iter<'d> {
}
}
impl<'d> Drop for Iter<'d> {
impl Drop for Iter<'_> {
fn drop(&mut self) {
unsafe { libc::rewinddir((self.0).0.as_ptr()) }
}
@@ -229,6 +287,7 @@ impl Entry {
target_os = "fuchsia",
target_os = "haiku",
target_os = "hurd",
target_os = "cygwin",
solarish,
linux_android,
apple_targets))] {

View File

@@ -24,7 +24,7 @@ cfg_if! {
unsafe fn errno_location() -> *mut c_int {
unsafe { libc::__error() }
}
} else if #[cfg(any(target_os = "android", netbsdlike))] {
} else if #[cfg(any(target_os = "android", netbsdlike, target_os = "cygwin"))] {
unsafe fn errno_location() -> *mut c_int {
unsafe { libc::__errno() }
}
@@ -32,7 +32,8 @@ cfg_if! {
target_os = "redox",
target_os = "dragonfly",
target_os = "fuchsia",
target_os = "hurd"))] {
target_os = "hurd",
target_os = "emscripten"))] {
unsafe fn errno_location() -> *mut c_int {
unsafe { libc::__errno_location() }
}
@@ -277,7 +278,9 @@ fn desc(errno: Errno) -> &'static str {
linux_android,
solarish,
target_os = "aix",
target_os = "fuchsia"
target_os = "fuchsia",
target_os = "emscripten",
target_os = "cygwin",
))]
ECHRNG => "Channel number out of range",
@@ -285,7 +288,9 @@ fn desc(errno: Errno) -> &'static str {
linux_android,
solarish,
target_os = "aix",
target_os = "fuchsia"
target_os = "fuchsia",
target_os = "emscripten",
target_os = "cygwin",
))]
EL2NSYNC => "Level 2 not synchronized",
@@ -293,7 +298,9 @@ fn desc(errno: Errno) -> &'static str {
linux_android,
solarish,
target_os = "aix",
target_os = "fuchsia"
target_os = "fuchsia",
target_os = "emscripten",
target_os = "cygwin",
))]
EL3HLT => "Level 3 halted",
@@ -301,7 +308,9 @@ fn desc(errno: Errno) -> &'static str {
linux_android,
solarish,
target_os = "aix",
target_os = "fuchsia"
target_os = "fuchsia",
target_os = "emscripten",
target_os = "cygwin",
))]
EL3RST => "Level 3 reset",
@@ -309,7 +318,9 @@ fn desc(errno: Errno) -> &'static str {
linux_android,
solarish,
target_os = "aix",
target_os = "fuchsia"
target_os = "fuchsia",
target_os = "emscripten",
target_os = "cygwin",
))]
ELNRNG => "Link number out of range",
@@ -317,7 +328,9 @@ fn desc(errno: Errno) -> &'static str {
linux_android,
solarish,
target_os = "aix",
target_os = "fuchsia"
target_os = "fuchsia",
target_os = "emscripten",
target_os = "cygwin",
))]
EUNATCH => "Protocol driver not attached",
@@ -325,7 +338,9 @@ fn desc(errno: Errno) -> &'static str {
linux_android,
solarish,
target_os = "aix",
target_os = "fuchsia"
target_os = "fuchsia",
target_os = "emscripten",
target_os = "cygwin",
))]
ENOCSI => "No CSI structure available",
@@ -333,36 +348,82 @@ fn desc(errno: Errno) -> &'static str {
linux_android,
solarish,
target_os = "aix",
target_os = "fuchsia"
target_os = "fuchsia",
target_os = "emscripten",
target_os = "cygwin",
))]
EL2HLT => "Level 2 halted",
#[cfg(any(linux_android, solarish, target_os = "fuchsia"))]
#[cfg(any(
linux_android,
solarish,
target_os = "fuchsia",
target_os = "emscripten",
target_os = "cygwin",
))]
EBADE => "Invalid exchange",
#[cfg(any(linux_android, solarish, target_os = "fuchsia"))]
#[cfg(any(
linux_android,
solarish,
target_os = "fuchsia",
target_os = "emscripten",
target_os = "cygwin",
))]
EBADR => "Invalid request descriptor",
#[cfg(any(linux_android, solarish, target_os = "fuchsia"))]
#[cfg(any(
linux_android,
solarish,
target_os = "fuchsia",
target_os = "emscripten",
target_os = "cygwin",
))]
EXFULL => "Exchange full",
#[cfg(any(linux_android, solarish, target_os = "fuchsia"))]
#[cfg(any(
linux_android,
solarish,
target_os = "fuchsia",
target_os = "emscripten",
target_os = "cygwin",
))]
ENOANO => "No anode",
#[cfg(any(linux_android, solarish, target_os = "fuchsia"))]
#[cfg(any(
linux_android,
solarish,
target_os = "fuchsia",
target_os = "emscripten",
target_os = "cygwin",
))]
EBADRQC => "Invalid request code",
#[cfg(any(linux_android, solarish, target_os = "fuchsia"))]
#[cfg(any(
linux_android,
solarish,
target_os = "fuchsia",
target_os = "emscripten",
target_os = "cygwin",
))]
EBADSLT => "Invalid slot",
#[cfg(any(linux_android, solarish, target_os = "fuchsia"))]
#[cfg(any(
linux_android,
solarish,
target_os = "fuchsia",
target_os = "emscripten",
target_os = "cygwin",
))]
EBFONT => "Bad font file format",
#[cfg(any(
linux_android,
solarish,
target_os = "fuchsia",
target_os = "hurd"
target_os = "hurd",
target_os = "emscripten",
target_os = "cygwin",
))]
ENOSTR => "Device not a stream",
@@ -370,7 +431,9 @@ fn desc(errno: Errno) -> &'static str {
linux_android,
solarish,
target_os = "fuchsia",
target_os = "hurd"
target_os = "hurd",
target_os = "emscripten",
target_os = "cygwin",
))]
ENODATA => "No data available",
@@ -378,7 +441,9 @@ fn desc(errno: Errno) -> &'static str {
linux_android,
solarish,
target_os = "fuchsia",
target_os = "hurd"
target_os = "hurd",
target_os = "emscripten",
target_os = "cygwin",
))]
ETIME => "Timer expired",
@@ -386,21 +451,37 @@ fn desc(errno: Errno) -> &'static str {
linux_android,
solarish,
target_os = "fuchsia",
target_os = "hurd"
target_os = "hurd",
target_os = "emscripten",
target_os = "cygwin",
))]
ENOSR => "Out of streams resources",
#[cfg(any(linux_android, solarish, target_os = "fuchsia"))]
#[cfg(any(
linux_android,
solarish,
target_os = "fuchsia",
target_os = "emscripten",
target_os = "cygwin",
))]
ENONET => "Machine is not on the network",
#[cfg(any(linux_android, solarish, target_os = "fuchsia"))]
#[cfg(any(
linux_android,
solarish,
target_os = "fuchsia",
target_os = "emscripten",
target_os = "cygwin",
))]
ENOPKG => "Package not installed",
#[cfg(any(
linux_android,
solarish,
target_os = "fuchsia",
target_os = "hurd"
target_os = "hurd",
target_os = "emscripten",
target_os = "cygwin",
))]
EREMOTE => "Object is remote",
@@ -408,17 +489,36 @@ fn desc(errno: Errno) -> &'static str {
linux_android,
solarish,
target_os = "aix",
target_os = "fuchsia"
target_os = "fuchsia",
target_os = "emscripten",
))]
ENOLINK => "Link has been severed",
#[cfg(any(linux_android, solarish, target_os = "fuchsia"))]
#[cfg(any(
linux_android,
solarish,
target_os = "fuchsia",
target_os = "emscripten",
target_os = "cygwin",
))]
EADV => "Advertise error",
#[cfg(any(linux_android, solarish, target_os = "fuchsia"))]
#[cfg(any(
linux_android,
solarish,
target_os = "fuchsia",
target_os = "emscripten",
target_os = "cygwin",
))]
ESRMNT => "Srmount error",
#[cfg(any(linux_android, solarish, target_os = "fuchsia"))]
#[cfg(any(
linux_android,
solarish,
target_os = "fuchsia",
target_os = "emscripten",
target_os = "cygwin",
))]
ECOMM => "Communication error on send",
#[cfg(any(
@@ -426,6 +526,8 @@ fn desc(errno: Errno) -> &'static str {
solarish,
target_os = "aix",
target_os = "fuchsia",
target_os = "emscripten",
target_os = "cygwin",
))]
EPROTO => "Protocol error",
@@ -433,14 +535,26 @@ fn desc(errno: Errno) -> &'static str {
linux_android,
solarish,
target_os = "aix",
target_os = "fuchsia"
target_os = "fuchsia",
target_os = "emscripten",
))]
EMULTIHOP => "Multihop attempted",
#[cfg(any(linux_android, target_os = "fuchsia"))]
#[cfg(any(
linux_android,
target_os = "fuchsia",
target_os = "emscripten",
target_os = "cygwin",
))]
EDOTDOT => "RFS specific error",
#[cfg(any(linux_android, target_os = "aix", target_os = "fuchsia"))]
#[cfg(any(
linux_android,
target_os = "aix",
target_os = "fuchsia",
target_os = "emscripten",
target_os = "cygwin",
))]
EBADMSG => "Not a data message",
#[cfg(solarish)]
@@ -451,36 +565,82 @@ fn desc(errno: Errno) -> &'static str {
target_os = "aix",
target_os = "fuchsia",
target_os = "haiku",
target_os = "hurd"
target_os = "hurd",
target_os = "emscripten",
target_os = "cygwin",
))]
EOVERFLOW => "Value too large for defined data type",
#[cfg(any(linux_android, solarish, target_os = "fuchsia"))]
#[cfg(any(
linux_android,
solarish,
target_os = "fuchsia",
target_os = "emscripten",
target_os = "cygwin",
))]
ENOTUNIQ => "Name not unique on network",
#[cfg(any(linux_android, solarish, target_os = "fuchsia"))]
#[cfg(any(
linux_android,
solarish,
target_os = "fuchsia",
target_os = "emscripten",
target_os = "cygwin",
))]
EBADFD => "File descriptor in bad state",
#[cfg(any(linux_android, solarish, target_os = "fuchsia"))]
#[cfg(any(
linux_android,
solarish,
target_os = "fuchsia",
target_os = "emscripten",
target_os = "cygwin",
))]
EREMCHG => "Remote address changed",
#[cfg(any(linux_android, solarish, target_os = "fuchsia"))]
#[cfg(any(
linux_android,
solarish,
target_os = "fuchsia",
target_os = "emscripten",
target_os = "cygwin",
))]
ELIBACC => "Can not access a needed shared library",
#[cfg(any(linux_android, solarish, target_os = "fuchsia"))]
#[cfg(any(
linux_android,
solarish,
target_os = "fuchsia",
target_os = "emscripten",
target_os = "cygwin",
))]
ELIBBAD => "Accessing a corrupted shared library",
#[cfg(any(linux_android, solarish, target_os = "fuchsia"))]
#[cfg(any(
linux_android,
solarish,
target_os = "fuchsia",
target_os = "emscripten",
target_os = "cygwin",
))]
ELIBSCN => ".lib section in a.out corrupted",
#[cfg(any(linux_android, solarish, target_os = "fuchsia"))]
#[cfg(any(
linux_android,
solarish,
target_os = "fuchsia",
target_os = "emscripten",
target_os = "cygwin",
))]
ELIBMAX => "Attempting to link in too many shared libraries",
#[cfg(any(
linux_android,
solarish,
target_os = "fuchsia",
target_os = "hurd"
target_os = "hurd",
target_os = "emscripten",
target_os = "cygwin",
))]
ELIBEXEC => "Cannot exec a shared library directly",
@@ -489,7 +649,9 @@ fn desc(errno: Errno) -> &'static str {
solarish,
target_os = "aix",
target_os = "fuchsia",
target_os = "openbsd"
target_os = "openbsd",
target_os = "emscripten",
target_os = "cygwin",
))]
EILSEQ => "Illegal byte sequence",
@@ -497,57 +659,106 @@ fn desc(errno: Errno) -> &'static str {
linux_android,
solarish,
target_os = "aix",
target_os = "fuchsia"
target_os = "fuchsia",
target_os = "emscripten",
))]
ERESTART => "Interrupted system call should be restarted",
#[cfg(any(linux_android, solarish, target_os = "fuchsia"))]
#[cfg(any(
linux_android,
solarish,
target_os = "fuchsia",
target_os = "emscripten",
target_os = "cygwin",
))]
ESTRPIPE => "Streams pipe error",
#[cfg(any(linux_android, solarish, target_os = "fuchsia"))]
#[cfg(any(
linux_android,
solarish,
target_os = "fuchsia",
target_os = "emscripten",
target_os = "cygwin",
))]
EUSERS => "Too many users",
#[cfg(any(
linux_android,
target_os = "fuchsia",
target_os = "netbsd",
target_os = "redox"
target_os = "redox",
target_os = "emscripten",
target_os = "cygwin",
))]
EOPNOTSUPP => "Operation not supported on transport endpoint",
#[cfg(any(linux_android, target_os = "fuchsia", target_os = "hurd"))]
#[cfg(any(
linux_android,
target_os = "fuchsia",
target_os = "hurd",
target_os = "emscripten",
target_os = "cygwin",
))]
ESTALE => "Stale file handle",
#[cfg(any(linux_android, target_os = "fuchsia"))]
#[cfg(any(
linux_android,
target_os = "fuchsia",
target_os = "emscripten",
))]
EUCLEAN => "Structure needs cleaning",
#[cfg(any(linux_android, target_os = "fuchsia"))]
#[cfg(any(
linux_android,
target_os = "fuchsia",
target_os = "emscripten",
))]
ENOTNAM => "Not a XENIX named type file",
#[cfg(any(linux_android, target_os = "fuchsia"))]
#[cfg(any(
linux_android,
target_os = "fuchsia",
target_os = "emscripten",
))]
ENAVAIL => "No XENIX semaphores available",
#[cfg(any(linux_android, target_os = "fuchsia"))]
#[cfg(any(
linux_android,
target_os = "fuchsia",
target_os = "emscripten",
))]
EISNAM => "Is a named type file",
#[cfg(any(linux_android, target_os = "fuchsia"))]
#[cfg(any(
linux_android,
target_os = "fuchsia",
target_os = "emscripten",
))]
EREMOTEIO => "Remote I/O error",
#[cfg(any(linux_android, target_os = "fuchsia"))]
#[cfg(any(
linux_android,
target_os = "fuchsia",
target_os = "emscripten",
target_os = "cygwin",
))]
EDQUOT => "Quota exceeded",
#[cfg(any(
linux_android,
target_os = "fuchsia",
target_os = "openbsd",
target_os = "dragonfly"
target_os = "dragonfly",
target_os = "emscripten",
target_os = "cygwin",
))]
ENOMEDIUM => "No medium found",
#[cfg(any(
linux_android,
target_os = "fuchsia",
target_os = "openbsd"
target_os = "openbsd",
target_os = "emscripten",
))]
EMEDIUMTYPE => "Wrong medium type",
@@ -555,34 +766,60 @@ fn desc(errno: Errno) -> &'static str {
linux_android,
solarish,
target_os = "fuchsia",
target_os = "haiku"
target_os = "haiku",
target_os = "emscripten",
target_os = "cygwin",
))]
ECANCELED => "Operation canceled",
#[cfg(any(linux_android, target_os = "fuchsia"))]
#[cfg(any(
linux_android,
target_os = "fuchsia",
target_os = "emscripten",
))]
ENOKEY => "Required key not available",
#[cfg(any(linux_android, target_os = "fuchsia"))]
#[cfg(any(
linux_android,
target_os = "fuchsia",
target_os = "emscripten",
))]
EKEYEXPIRED => "Key has expired",
#[cfg(any(linux_android, target_os = "fuchsia"))]
#[cfg(any(
linux_android,
target_os = "fuchsia",
target_os = "emscripten",
))]
EKEYREVOKED => "Key has been revoked",
#[cfg(any(linux_android, target_os = "fuchsia"))]
#[cfg(any(
linux_android,
target_os = "fuchsia",
target_os = "emscripten",
))]
EKEYREJECTED => "Key was rejected by service",
#[cfg(any(
linux_android,
target_os = "aix",
target_os = "fuchsia",
target_os = "hurd"
target_os = "hurd",
target_os = "emscripten",
target_os = "cygwin",
))]
EOWNERDEAD => "Owner died",
#[cfg(solarish)]
EOWNERDEAD => "Process died with lock",
#[cfg(any(linux_android, target_os = "aix", target_os = "fuchsia"))]
#[cfg(any(
linux_android,
target_os = "aix",
target_os = "fuchsia",
target_os = "emscripten",
target_os = "cygwin",
))]
ENOTRECOVERABLE => "State not recoverable",
#[cfg(solarish)]
@@ -590,23 +827,35 @@ fn desc(errno: Errno) -> &'static str {
#[cfg(any(
all(target_os = "linux", not(target_arch = "mips")),
target_os = "fuchsia"
target_os = "fuchsia",
target_os = "emscripten",
))]
ERFKILL => "Operation not possible due to RF-kill",
#[cfg(any(
all(target_os = "linux", not(target_arch = "mips")),
target_os = "fuchsia"
target_os = "fuchsia",
target_os = "emscripten",
))]
EHWPOISON => "Memory page has hardware error",
#[cfg(freebsdlike)]
EDOOFUS => "Programming error",
#[cfg(any(freebsdlike, target_os = "hurd", target_os = "redox"))]
#[cfg(any(
freebsdlike,
target_os = "hurd",
target_os = "redox",
target_os = "cygwin"
))]
EMULTIHOP => "Multihop attempted",
#[cfg(any(freebsdlike, target_os = "hurd", target_os = "redox"))]
#[cfg(any(
freebsdlike,
target_os = "hurd",
target_os = "redox",
target_os = "cygwin"
))]
ENOLINK => "Link has been severed",
#[cfg(target_os = "freebsd")]
@@ -666,11 +915,17 @@ fn desc(errno: Errno) -> &'static str {
target_os = "aix",
solarish,
target_os = "haiku",
target_os = "hurd"
target_os = "hurd",
target_os = "cygwin"
))]
ENOTSUP => "Operation not supported",
#[cfg(any(bsd, target_os = "aix", target_os = "hurd"))]
#[cfg(any(
bsd,
target_os = "aix",
target_os = "hurd",
target_os = "cygwin"
))]
EPROCLIM => "Too many processes",
#[cfg(any(
@@ -718,7 +973,7 @@ fn desc(errno: Errno) -> &'static str {
#[cfg(any(bsd, target_os = "hurd"))]
EPROCUNAVAIL => "Bad procedure for program",
#[cfg(any(bsd, target_os = "hurd"))]
#[cfg(any(bsd, target_os = "hurd", target_os = "cygwin"))]
EFTYPE => "Inappropriate file type or format",
#[cfg(any(bsd, target_os = "hurd"))]
@@ -806,7 +1061,7 @@ fn desc(errno: Errno) -> &'static str {
#[cfg(target_os = "dragonfly")]
EASYNC => "Async",
#[cfg(solarish)]
#[cfg(any(solarish, target_os = "cygwin"))]
EDEADLOCK => "Resource deadlock would occur",
#[cfg(solarish)]
@@ -832,7 +1087,7 @@ fn desc(errno: Errno) -> &'static str {
}
}
#[cfg(any(linux_android, target_os = "fuchsia"))]
#[cfg(any(linux_android, target_os = "fuchsia", target_os = "emscripten"))]
mod consts {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(i32)]
@@ -3334,3 +3589,271 @@ mod consts {
}
}
}
#[cfg(target_os = "cygwin")]
mod consts {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(i32)]
#[non_exhaustive]
pub enum Errno {
UnknownErrno = 0,
EPERM = libc::EPERM,
ENOENT = libc::ENOENT,
ESRCH = libc::ESRCH,
EINTR = libc::EINTR,
EIO = libc::EIO,
ENXIO = libc::ENXIO,
E2BIG = libc::E2BIG,
ENOEXEC = libc::ENOEXEC,
EBADF = libc::EBADF,
ECHILD = libc::ECHILD,
EAGAIN = libc::EAGAIN,
ENOMEM = libc::ENOMEM,
EACCES = libc::EACCES,
EFAULT = libc::EFAULT,
ENOTBLK = libc::ENOTBLK,
EBUSY = libc::EBUSY,
EEXIST = libc::EEXIST,
EXDEV = libc::EXDEV,
ENODEV = libc::ENODEV,
ENOTDIR = libc::ENOTDIR,
EISDIR = libc::EISDIR,
EINVAL = libc::EINVAL,
ENFILE = libc::ENFILE,
EMFILE = libc::EMFILE,
ENOTTY = libc::ENOTTY,
ETXTBSY = libc::ETXTBSY,
EFBIG = libc::EFBIG,
ENOSPC = libc::ENOSPC,
ESPIPE = libc::ESPIPE,
EROFS = libc::EROFS,
EMLINK = libc::EMLINK,
EPIPE = libc::EPIPE,
EDOM = libc::EDOM,
ERANGE = libc::ERANGE,
ENOMSG = libc::ENOMSG,
EIDRM = libc::EIDRM,
ECHRNG = libc::ECHRNG,
EL2NSYNC = libc::EL2NSYNC,
EL3HLT = libc::EL3HLT,
EL3RST = libc::EL3RST,
ELNRNG = libc::ELNRNG,
EUNATCH = libc::EUNATCH,
ENOCSI = libc::ENOCSI,
EL2HLT = libc::EL2HLT,
EDEADLK = libc::EDEADLK,
ENOLCK = libc::ENOLCK,
EBADE = libc::EBADE,
EBADR = libc::EBADR,
EXFULL = libc::EXFULL,
ENOANO = libc::ENOANO,
EBADRQC = libc::EBADRQC,
EBADSLT = libc::EBADSLT,
EDEADLOCK = libc::EDEADLOCK,
EBFONT = libc::EBFONT,
ENOSTR = libc::ENOSTR,
ENODATA = libc::ENODATA,
ETIME = libc::ETIME,
ENOSR = libc::ENOSR,
ENONET = libc::ENONET,
ENOPKG = libc::ENOPKG,
EREMOTE = libc::EREMOTE,
ENOLINK = libc::ENOLINK,
EADV = libc::EADV,
ESRMNT = libc::ESRMNT,
ECOMM = libc::ECOMM,
EPROTO = libc::EPROTO,
EMULTIHOP = libc::EMULTIHOP,
EDOTDOT = libc::EDOTDOT,
EBADMSG = libc::EBADMSG,
EFTYPE = libc::EFTYPE,
ENOTUNIQ = libc::ENOTUNIQ,
EBADFD = libc::EBADFD,
EREMCHG = libc::EREMCHG,
ELIBACC = libc::ELIBACC,
ELIBBAD = libc::ELIBBAD,
ELIBSCN = libc::ELIBSCN,
ELIBMAX = libc::ELIBMAX,
ELIBEXEC = libc::ELIBEXEC,
ENOSYS = libc::ENOSYS,
ENOTEMPTY = libc::ENOTEMPTY,
ENAMETOOLONG = libc::ENAMETOOLONG,
ELOOP = libc::ELOOP,
EOPNOTSUPP = libc::EOPNOTSUPP,
EPFNOSUPPORT = libc::EPFNOSUPPORT,
ECONNRESET = libc::ECONNRESET,
ENOBUFS = libc::ENOBUFS,
EAFNOSUPPORT = libc::EAFNOSUPPORT,
EPROTOTYPE = libc::EPROTOTYPE,
ENOTSOCK = libc::ENOTSOCK,
ENOPROTOOPT = libc::ENOPROTOOPT,
ESHUTDOWN = libc::ESHUTDOWN,
ECONNREFUSED = libc::ECONNREFUSED,
EADDRINUSE = libc::EADDRINUSE,
ECONNABORTED = libc::ECONNABORTED,
ENETUNREACH = libc::ENETUNREACH,
ENETDOWN = libc::ENETDOWN,
ETIMEDOUT = libc::ETIMEDOUT,
EHOSTDOWN = libc::EHOSTDOWN,
EHOSTUNREACH = libc::EHOSTUNREACH,
EINPROGRESS = libc::EINPROGRESS,
EALREADY = libc::EALREADY,
EDESTADDRREQ = libc::EDESTADDRREQ,
EMSGSIZE = libc::EMSGSIZE,
EPROTONOSUPPORT = libc::EPROTONOSUPPORT,
ESOCKTNOSUPPORT = libc::ESOCKTNOSUPPORT,
EADDRNOTAVAIL = libc::EADDRNOTAVAIL,
ENETRESET = libc::ENETRESET,
EISCONN = libc::EISCONN,
ENOTCONN = libc::ENOTCONN,
ETOOMANYREFS = libc::ETOOMANYREFS,
EPROCLIM = libc::EPROCLIM,
EUSERS = libc::EUSERS,
EDQUOT = libc::EDQUOT,
ESTALE = libc::ESTALE,
ENOTSUP = libc::ENOTSUP,
ENOMEDIUM = libc::ENOMEDIUM,
EILSEQ = libc::EILSEQ,
EOVERFLOW = libc::EOVERFLOW,
ECANCELED = libc::ECANCELED,
ENOTRECOVERABLE = libc::ENOTRECOVERABLE,
EOWNERDEAD = libc::EOWNERDEAD,
ESTRPIPE = libc::ESTRPIPE,
}
impl Errno {
pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
pub const EDEADLOCK: Errno = Errno::EDEADLK;
pub const EOPNOTSUPP: Errno = Errno::ENOTSUP;
}
pub(crate) const fn from_i32(e: i32) -> Errno {
use self::Errno::*;
match e {
libc::EPERM => EPERM,
libc::ENOENT => ENOENT,
libc::ESRCH => ESRCH,
libc::EINTR => EINTR,
libc::EIO => EIO,
libc::ENXIO => ENXIO,
libc::E2BIG => E2BIG,
libc::ENOEXEC => ENOEXEC,
libc::EBADF => EBADF,
libc::ECHILD => ECHILD,
libc::EAGAIN => EAGAIN,
libc::ENOMEM => ENOMEM,
libc::EACCES => EACCES,
libc::EFAULT => EFAULT,
libc::ENOTBLK => ENOTBLK,
libc::EBUSY => EBUSY,
libc::EEXIST => EEXIST,
libc::EXDEV => EXDEV,
libc::ENODEV => ENODEV,
libc::ENOTDIR => ENOTDIR,
libc::EISDIR => EISDIR,
libc::EINVAL => EINVAL,
libc::ENFILE => ENFILE,
libc::EMFILE => EMFILE,
libc::ENOTTY => ENOTTY,
libc::ETXTBSY => ETXTBSY,
libc::EFBIG => EFBIG,
libc::ENOSPC => ENOSPC,
libc::ESPIPE => ESPIPE,
libc::EROFS => EROFS,
libc::EMLINK => EMLINK,
libc::EPIPE => EPIPE,
libc::EDOM => EDOM,
libc::ERANGE => ERANGE,
libc::ENOMSG => ENOMSG,
libc::EIDRM => EIDRM,
libc::ECHRNG => ECHRNG,
libc::EL2NSYNC => EL2NSYNC,
libc::EL3HLT => EL3HLT,
libc::EL3RST => EL3RST,
libc::ELNRNG => ELNRNG,
libc::EUNATCH => EUNATCH,
libc::ENOCSI => ENOCSI,
libc::EL2HLT => EL2HLT,
libc::EDEADLK => EDEADLK,
libc::ENOLCK => ENOLCK,
libc::EBADE => EBADE,
libc::EBADR => EBADR,
libc::EXFULL => EXFULL,
libc::ENOANO => ENOANO,
libc::EBADRQC => EBADRQC,
libc::EBADSLT => EBADSLT,
libc::EDEADLOCK => EDEADLOCK,
libc::EBFONT => EBFONT,
libc::ENOSTR => ENOSTR,
libc::ENODATA => ENODATA,
libc::ETIME => ETIME,
libc::ENOSR => ENOSR,
libc::ENONET => ENONET,
libc::ENOPKG => ENOPKG,
libc::EREMOTE => EREMOTE,
libc::ENOLINK => ENOLINK,
libc::EADV => EADV,
libc::ESRMNT => ESRMNT,
libc::ECOMM => ECOMM,
libc::EPROTO => EPROTO,
libc::EMULTIHOP => EMULTIHOP,
libc::EDOTDOT => EDOTDOT,
libc::EBADMSG => EBADMSG,
libc::EFTYPE => EFTYPE,
libc::ENOTUNIQ => ENOTUNIQ,
libc::EBADFD => EBADFD,
libc::EREMCHG => EREMCHG,
libc::ELIBACC => ELIBACC,
libc::ELIBBAD => ELIBBAD,
libc::ELIBSCN => ELIBSCN,
libc::ELIBMAX => ELIBMAX,
libc::ELIBEXEC => ELIBEXEC,
libc::ENOSYS => ENOSYS,
libc::ENOTEMPTY => ENOTEMPTY,
libc::ENAMETOOLONG => ENAMETOOLONG,
libc::ELOOP => ELOOP,
libc::EOPNOTSUPP => EOPNOTSUPP,
libc::EPFNOSUPPORT => EPFNOSUPPORT,
libc::ECONNRESET => ECONNRESET,
libc::ENOBUFS => ENOBUFS,
libc::EAFNOSUPPORT => EAFNOSUPPORT,
libc::EPROTOTYPE => EPROTOTYPE,
libc::ENOTSOCK => ENOTSOCK,
libc::ENOPROTOOPT => ENOPROTOOPT,
libc::ESHUTDOWN => ESHUTDOWN,
libc::ECONNREFUSED => ECONNREFUSED,
libc::EADDRINUSE => EADDRINUSE,
libc::ECONNABORTED => ECONNABORTED,
libc::ENETUNREACH => ENETUNREACH,
libc::ENETDOWN => ENETDOWN,
libc::ETIMEDOUT => ETIMEDOUT,
libc::EHOSTDOWN => EHOSTDOWN,
libc::EHOSTUNREACH => EHOSTUNREACH,
libc::EINPROGRESS => EINPROGRESS,
libc::EALREADY => EALREADY,
libc::EDESTADDRREQ => EDESTADDRREQ,
libc::EMSGSIZE => EMSGSIZE,
libc::EPROTONOSUPPORT => EPROTONOSUPPORT,
libc::ESOCKTNOSUPPORT => ESOCKTNOSUPPORT,
libc::EADDRNOTAVAIL => EADDRNOTAVAIL,
libc::ENETRESET => ENETRESET,
libc::EISCONN => EISCONN,
libc::ENOTCONN => ENOTCONN,
libc::ETOOMANYREFS => ETOOMANYREFS,
libc::EPROCLIM => EPROCLIM,
libc::EUSERS => EUSERS,
libc::EDQUOT => EDQUOT,
libc::ESTALE => ESTALE,
libc::ENOTSUP => ENOTSUP,
libc::ENOMEDIUM => ENOMEDIUM,
libc::EILSEQ => EILSEQ,
libc::EOVERFLOW => EOVERFLOW,
libc::ECANCELED => ECANCELED,
libc::ENOTRECOVERABLE => ENOTRECOVERABLE,
libc::EOWNERDEAD => EOWNERDEAD,
libc::ESTRPIPE => ESTRPIPE,
_ => UnknownErrno,
}
}
}

View File

@@ -1,4 +1,4 @@
//! file control options
//! File control options
use crate::errno::Errno;
#[cfg(all(target_os = "freebsd", target_arch = "x86_64"))]
use core::slice;
@@ -13,12 +13,10 @@ use std::ffi::CStr;
use std::ffi::OsString;
#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
use std::ops::{Deref, DerefMut};
#[cfg(not(target_os = "redox"))]
use std::os::raw;
use std::os::unix::ffi::OsStringExt;
#[cfg(not(target_os = "redox"))]
use std::os::unix::io::OwnedFd;
use std::os::unix::io::RawFd;
#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
use std::os::unix::io::{AsRawFd, OwnedFd};
#[cfg(any(
target_os = "netbsd",
apple_targets,
@@ -27,7 +25,7 @@ use std::os::unix::io::{AsRawFd, OwnedFd};
))]
use std::path::PathBuf;
#[cfg(any(linux_android, target_os = "freebsd"))]
use std::{os::unix::io::AsFd, ptr};
use std::ptr;
#[cfg(feature = "fs")]
use crate::{sys::stat::Mode, NixPath, Result};
@@ -43,6 +41,42 @@ use crate::{sys::stat::Mode, NixPath, Result};
#[cfg(feature = "fs")]
pub use self::posix_fadvise::{posix_fadvise, PosixFadviseAdvice};
/// A file descriptor referring to the working directory of the current process
/// **that should be ONLY passed to the `dirfd` argument of those `xxat()` functions**.
///
/// # Examples
///
/// Use it in [`openat()`]:
///
/// ```no_run
/// use nix::fcntl::AT_FDCWD;
/// use nix::fcntl::openat;
/// use nix::fcntl::OFlag;
/// use nix::sys::stat::Mode;
///
/// let fd = openat(AT_FDCWD, "foo", OFlag::O_RDONLY | OFlag::O_CLOEXEC, Mode::empty()).unwrap();
/// ```
///
/// # WARNING
///
/// Do NOT pass this symbol to non-`xxat()` functions, it won't work:
///
/// ```should_panic
/// use nix::errno::Errno;
/// use nix::fcntl::AT_FDCWD;
/// use nix::sys::stat::fstat;
///
/// let never = fstat(AT_FDCWD).unwrap();
/// ```
//
// SAFETY:
// 1. `AT_FDCWD` is usable for the whole process life, so it is `'static`.
// 2. It is not a valid file descriptor, but OS will handle it for us when passed
// to `xxat(2)` calls.
#[cfg(not(target_os = "redox"))] // Redox does not have this
pub const AT_FDCWD: std::os::fd::BorrowedFd<'static> =
unsafe { std::os::fd::BorrowedFd::borrow_raw(libc::AT_FDCWD) };
#[cfg(not(target_os = "redox"))]
#[cfg(any(feature = "fs", feature = "process", feature = "user"))]
libc_bitflags! {
@@ -94,7 +128,8 @@ libc_bitflags!(
#[cfg(not(any(
solarish,
target_os = "aix",
target_os = "haiku"
target_os = "haiku",
target_os = "cygwin"
)))]
O_ASYNC;
/// Closes the file descriptor once an `execve` call is made.
@@ -107,7 +142,7 @@ libc_bitflags!(
#[cfg(any(
freebsdlike,
linux_android,
solarish,
target_os = "illumos",
target_os = "netbsd"
))]
O_DIRECT;
@@ -139,7 +174,7 @@ libc_bitflags!(
#[cfg(not(target_os = "redox"))]
O_NOCTTY;
/// Same as `O_NONBLOCK`.
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
#[cfg(not(any(target_os = "redox", target_os = "haiku", target_os = "cygwin")))]
O_NDELAY;
/// `open()` will fail if the given path is a symbolic link.
O_NOFOLLOW;
@@ -198,16 +233,6 @@ libc_bitflags!(
}
);
/// Computes the raw fd consumed by a function of the form `*at`.
#[cfg(any(
all(feature = "fs", not(target_os = "redox")),
all(feature = "process", linux_android),
all(feature = "fanotify", target_os = "linux")
))]
pub(crate) fn at_rawfd(fd: Option<RawFd>) -> raw::c_int {
fd.unwrap_or(libc::AT_FDCWD)
}
feature! {
#![feature = "fs"]
@@ -221,35 +246,49 @@ pub fn open<P: ?Sized + NixPath>(
path: &P,
oflag: OFlag,
mode: Mode,
) -> Result<RawFd> {
) -> Result<std::os::fd::OwnedFd> {
use std::os::fd::FromRawFd;
let fd = path.with_nix_path(|cstr| unsafe {
libc::open(cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint)
})?;
Errno::result(fd)?;
Errno::result(fd)
// SAFETY:
//
// `open(2)` should return a valid owned fd on success
Ok( unsafe { std::os::fd::OwnedFd::from_raw_fd(fd) } )
}
/// open or create a file for reading, writing or executing
///
/// The `openat` function is equivalent to the [`open`] function except in the case where the path
/// specifies a relative path. In that case, the file to be opened is determined relative to the
/// directory associated with the file descriptor `fd`.
/// directory associated with the file descriptor `dirfd`.
///
/// # See Also
/// [`openat`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/openat.html)
// The conversion is not identical on all operating systems.
#[allow(clippy::useless_conversion)]
#[cfg(not(target_os = "redox"))]
pub fn openat<P: ?Sized + NixPath>(
dirfd: Option<RawFd>,
pub fn openat<P: ?Sized + NixPath, Fd: std::os::fd::AsFd>(
dirfd: Fd,
path: &P,
oflag: OFlag,
mode: Mode,
) -> Result<RawFd> {
) -> Result<OwnedFd> {
use std::os::fd::AsRawFd;
use std::os::fd::FromRawFd;
let fd = path.with_nix_path(|cstr| unsafe {
libc::openat(at_rawfd(dirfd), cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint)
libc::openat(dirfd.as_fd().as_raw_fd(), cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint)
})?;
Errno::result(fd)
Errno::result(fd)?;
// SAFETY:
//
// `openat(2)` should return a valid owned fd on success
Ok( unsafe { OwnedFd::from_raw_fd(fd) } )
}
cfg_if::cfg_if! {
@@ -287,9 +326,11 @@ cfg_if::cfg_if! {
}
}
/// Specifies how [openat2] should open a pathname.
/// Specifies how [`openat2()`] should open a pathname.
///
/// See <https://man7.org/linux/man-pages/man2/open_how.2type.html>
/// # Reference
///
/// * [Linux](https://man7.org/linux/man-pages/man2/open_how.2type.html)
#[repr(transparent)]
#[derive(Clone, Copy, Debug)]
pub struct OpenHow(libc::open_how);
@@ -345,22 +386,29 @@ cfg_if::cfg_if! {
/// # See also
///
/// [openat2](https://man7.org/linux/man-pages/man2/openat2.2.html)
pub fn openat2<P: ?Sized + NixPath>(
dirfd: RawFd,
pub fn openat2<P: ?Sized + NixPath, Fd: std::os::fd::AsFd>(
dirfd: Fd,
path: &P,
mut how: OpenHow,
) -> Result<RawFd> {
) -> Result<OwnedFd> {
use std::os::fd::AsRawFd;
use std::os::fd::FromRawFd;
let fd = path.with_nix_path(|cstr| unsafe {
libc::syscall(
libc::SYS_openat2,
dirfd,
dirfd.as_fd().as_raw_fd(),
cstr.as_ptr(),
&mut how as *mut OpenHow,
std::mem::size_of::<libc::open_how>(),
)
})?;
})? as RawFd;
Errno::result(fd)?;
Errno::result(fd as RawFd)
// SAFETY:
//
// `openat2(2)` should return a valid owned fd on success
Ok( unsafe { OwnedFd::from_raw_fd(fd) } )
}
}
}
@@ -374,18 +422,20 @@ cfg_if::cfg_if! {
/// # See Also
/// [`renameat`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/rename.html)
#[cfg(not(target_os = "redox"))]
pub fn renameat<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
old_dirfd: Option<RawFd>,
pub fn renameat<P1: ?Sized + NixPath, P2: ?Sized + NixPath, Fd1: std::os::fd::AsFd, Fd2: std::os::fd::AsFd>(
old_dirfd: Fd1,
old_path: &P1,
new_dirfd: Option<RawFd>,
new_dirfd: Fd2,
new_path: &P2,
) -> Result<()> {
use std::os::fd::AsRawFd;
let res = old_path.with_nix_path(|old_cstr| {
new_path.with_nix_path(|new_cstr| unsafe {
libc::renameat(
at_rawfd(old_dirfd),
old_dirfd.as_fd().as_raw_fd(),
old_cstr.as_ptr(),
at_rawfd(new_dirfd),
new_dirfd.as_fd().as_raw_fd(),
new_cstr.as_ptr(),
)
})
@@ -422,19 +472,21 @@ feature! {
/// # See Also
/// * [`rename`](https://man7.org/linux/man-pages/man2/rename.2.html)
#[cfg(all(target_os = "linux", target_env = "gnu"))]
pub fn renameat2<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
old_dirfd: Option<RawFd>,
pub fn renameat2<P1: ?Sized + NixPath, P2: ?Sized + NixPath, Fd1: std::os::fd::AsFd, Fd2: std::os::fd::AsFd>(
old_dirfd: Fd1,
old_path: &P1,
new_dirfd: Option<RawFd>,
new_dirfd: Fd2,
new_path: &P2,
flags: RenameFlags,
) -> Result<()> {
use std::os::fd::AsRawFd;
let res = old_path.with_nix_path(|old_cstr| {
new_path.with_nix_path(|new_cstr| unsafe {
libc::renameat2(
at_rawfd(old_dirfd),
old_dirfd.as_fd().as_raw_fd(),
old_cstr.as_ptr(),
at_rawfd(new_dirfd),
new_dirfd.as_fd().as_raw_fd(),
new_cstr.as_ptr(),
flags.bits(),
)
@@ -449,7 +501,16 @@ fn wrap_readlink_result(mut v: Vec<u8>, len: ssize_t) -> Result<OsString> {
Ok(OsString::from_vec(v.to_vec()))
}
fn readlink_maybe_at<P: ?Sized + NixPath>(
/// Read the symlink specified by `path` and `dirfd` and put the contents in `v`.
/// Return the number of bytes placed in `v`.
///
/// This function can call `readlink(2)` or `readlinkat(2)` depending on if `dirfd`
/// is some, if it is, then `readlinkat(2)` is called, otherwise, call `readlink(2)`.
///
/// # Safety
///
/// This function is not I/O-safe considering it employs the `RawFd` type.
unsafe fn readlink_maybe_at<P: ?Sized + NixPath>(
dirfd: Option<RawFd>,
path: &P,
v: &mut Vec<u8>,
@@ -457,7 +518,7 @@ fn readlink_maybe_at<P: ?Sized + NixPath>(
path.with_nix_path(|cstr| unsafe {
match dirfd {
#[cfg(target_os = "redox")]
Some(_) => unreachable!(),
Some(_) => unreachable!("redox does not have readlinkat(2)"),
#[cfg(not(target_os = "redox"))]
Some(dirfd) => libc::readlinkat(
dirfd,
@@ -474,7 +535,15 @@ fn readlink_maybe_at<P: ?Sized + NixPath>(
})
}
fn inner_readlink<P: ?Sized + NixPath>(
/// The actual implementation of [`readlink(2)`] or [`readlinkat(2)`].
///
/// This function can call `readlink(2)` or `readlinkat(2)` depending on if `dirfd`
/// is some, if it is, then `readlinkat(2)` is called, otherwise, call `readlink(2)`.
///
/// # Safety
///
/// This function is marked unsafe because it uses `RawFd`.
unsafe fn inner_readlink<P: ?Sized + NixPath>(
dirfd: Option<RawFd>,
path: &P,
) -> Result<OsString> {
@@ -486,7 +555,12 @@ fn inner_readlink<P: ?Sized + NixPath>(
{
// simple case: result is strictly less than `PATH_MAX`
let res = readlink_maybe_at(dirfd, path, &mut v)?;
// SAFETY:
//
// If this call of `readlink_maybe_at()` is safe or not depends on the
// usage of `unsafe fn inner_readlink()`.
let res = unsafe { readlink_maybe_at(dirfd, path, &mut v)? };
let len = Errno::result(res)?;
debug_assert!(len >= 0);
if (len as usize) < v.capacity() {
@@ -499,16 +573,23 @@ fn inner_readlink<P: ?Sized + NixPath>(
let mut try_size = {
let reported_size = match dirfd {
#[cfg(target_os = "redox")]
Some(_) => unreachable!(),
Some(_) => unreachable!("redox does not have readlinkat(2)"),
#[cfg(any(linux_android, target_os = "freebsd", target_os = "hurd"))]
Some(dirfd) => {
// SAFETY:
//
// If this call of `borrow_raw()` is safe or not depends on the
// usage of `unsafe fn inner_readlink()`.
let dirfd = unsafe {
std::os::fd::BorrowedFd::borrow_raw(dirfd)
};
let flags = if path.is_empty() {
AtFlags::AT_EMPTY_PATH
} else {
AtFlags::empty()
};
super::sys::stat::fstatat(
Some(dirfd),
dirfd,
path,
flags | AtFlags::AT_SYMLINK_NOFOLLOW,
)
@@ -519,11 +600,16 @@ fn inner_readlink<P: ?Sized + NixPath>(
target_os = "freebsd",
target_os = "hurd"
)))]
Some(dirfd) => super::sys::stat::fstatat(
Some(dirfd),
path,
AtFlags::AT_SYMLINK_NOFOLLOW,
),
Some(dirfd) => {
// SAFETY:
//
// If this call of `borrow_raw()` is safe or not depends on the
// usage of `unsafe fn inner_readlink()`.
let dirfd = unsafe {
std::os::fd::BorrowedFd::borrow_raw(dirfd)
};
super::sys::stat::fstatat(dirfd, path, AtFlags::AT_SYMLINK_NOFOLLOW)
},
None => super::sys::stat::lstat(path),
}
.map(|x| x.st_size)
@@ -543,7 +629,11 @@ fn inner_readlink<P: ?Sized + NixPath>(
loop {
{
v.reserve_exact(try_size);
let res = readlink_maybe_at(dirfd, path, &mut v)?;
// SAFETY:
//
// If this call of `readlink_maybe_at()` is safe or not depends on the
// usage of `unsafe fn inner_readlink()`.
let res = unsafe { readlink_maybe_at(dirfd, path, &mut v)? };
let len = Errno::result(res)?;
debug_assert!(len >= 0);
if (len as usize) < v.capacity() {
@@ -566,23 +656,39 @@ fn inner_readlink<P: ?Sized + NixPath>(
/// # See Also
/// * [`readlink`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/readlink.html)
pub fn readlink<P: ?Sized + NixPath>(path: &P) -> Result<OsString> {
inner_readlink(None, path)
// argument `dirfd` should be `None` since we call it from `readlink()`
//
// Do NOT call it with `Some(AT_CWD)` as in that way, we are emulating
// `readlink(2)` with `readlinkat(2)`, which will make us lose `readlink(2)`
// on Redox.
//
// SAFETY:
//
// It is definitely safe because the argument involving `RawFd` is `None`
unsafe { inner_readlink(None, path) }
}
/// Read value of a symbolic link.
///
/// Equivalent to [`readlink` ] except where `path` specifies a relative path. In that case,
/// interpret `path` relative to open file specified by `dirfd`.
/// Equivalent to [`readlink` ] except for the case where `path` specifies a
/// relative path, `path` will be interpreted relative to the path specified
/// by `dirfd`. (Use [`AT_FDCWD`] to make it relative to the working directory).
///
/// # See Also
/// * [`readlink`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/readlink.html)
#[cfg(not(target_os = "redox"))]
pub fn readlinkat<P: ?Sized + NixPath>(
dirfd: Option<RawFd>,
pub fn readlinkat<Fd: std::os::fd::AsFd,P: ?Sized + NixPath>(
dirfd: Fd,
path: &P,
) -> Result<OsString> {
let dirfd = at_rawfd(dirfd);
inner_readlink(Some(dirfd), path)
use std::os::fd::AsRawFd;
// argument `dirfd` should be `Some` since we call it from `readlinkat()`
//
// SAFETY:
//
// The passed `RawFd` should be valid since it is borrowed from `Fd: AsFd`.
unsafe { inner_readlink(Some(dirfd.as_fd().as_raw_fd()), path) }
}
}
@@ -692,6 +798,39 @@ pub enum FcntlArg<'a> {
/// Return the full path without firmlinks of the fd.
#[cfg(apple_targets)]
F_GETPATH_NOFIRMLINK(&'a mut PathBuf),
/// Issue an advisory read async with no copy to user
#[cfg(apple_targets)]
F_RDADVISE(libc::radvisory),
/// Turn read ahead off/on
#[cfg(apple_targets)]
F_RDAHEAD(bool),
/// Pre-allocate storage with different policies on fd.
/// Note that we want a mutable reference for the OUT
/// fstore_t field fst_bytesalloc.
#[cfg(apple_targets)]
F_PREALLOCATE(&'a mut libc::fstore_t),
#[cfg(apple_targets)]
/// Get disk device information. In practice,
/// only the file offset data is set.
F_LOG2PHYS(&'a mut libc::off_t),
#[cfg(apple_targets)]
/// Get disk device information. In practice,
/// only the file offset data is set.
/// The difference with F_LOG2PHYS is the struct passed
/// is used as both IN/OUT as both its l2p_devoffset and
/// l2p_contigbytes can be used for more specific queries.
F_LOG2PHYS_EXT(&'a mut libc::log2phys),
/// Transfer any extra space in the file past the logical EOF
/// (as previously allocated via F_PREALLOCATE) to another file.
/// The other file is specified via a file descriptor as the lone extra argument.
/// Both descriptors must reference regular files in the same volume.
#[cfg(apple_targets)]
F_TRANSFEREXTENTS(RawFd),
/// Set or clear the read ahead (pre-fetch) amount for sequential access or
/// disable it with 0 or to system default for any value < 0.
/// It manages how the kernel caches file data.
#[cfg(target_os = "freebsd")]
F_READAHEAD(c_int),
// TODO: Rest of flags
}
@@ -720,7 +859,10 @@ pub use self::FcntlArg::*;
/// # See Also
/// * [`fcntl`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fcntl.html)
// TODO: Figure out how to handle value fcntl returns
pub fn fcntl(fd: RawFd, arg: FcntlArg) -> Result<c_int> {
pub fn fcntl<Fd: std::os::fd::AsFd>(fd: Fd, arg: FcntlArg) -> Result<c_int> {
use std::os::fd::AsRawFd;
let fd = fd.as_fd().as_raw_fd();
let res = unsafe {
match arg {
F_DUPFD(rawfd) => libc::fcntl(fd, libc::F_DUPFD, rawfd),
@@ -797,6 +939,39 @@ pub fn fcntl(fd: RawFd, arg: FcntlArg) -> Result<c_int> {
*path = PathBuf::from(OsString::from(optr.to_str().unwrap()));
return Ok(ok_res)
},
#[cfg(apple_targets)]
F_RDADVISE(rad) => {
libc::fcntl(fd, libc::F_RDADVISE, &rad)
},
#[cfg(apple_targets)]
F_LOG2PHYS(offset) => {
let mut info: libc::log2phys = std::mem::zeroed();
let res = libc::fcntl(fd, libc::F_LOG2PHYS, &mut info);
let ok_res = Errno::result(res)?;
*offset = info.l2p_devoffset;
return Ok(ok_res)
}
#[cfg(apple_targets)]
F_LOG2PHYS_EXT(info) => {
libc::fcntl(fd, libc::F_LOG2PHYS_EXT, info)
}
#[cfg(apple_targets)]
F_RDAHEAD(on) => {
let val = if on { 1 } else { 0 };
libc::fcntl(fd, libc::F_RDAHEAD, val)
},
#[cfg(apple_targets)]
F_PREALLOCATE(st) => {
libc::fcntl(fd, libc::F_PREALLOCATE, st)
},
#[cfg(apple_targets)]
F_TRANSFEREXTENTS(rawfd) => {
libc::fcntl(fd, libc::F_TRANSFEREXTENTS, rawfd)
},
#[cfg(target_os = "freebsd")]
F_READAHEAD(val) => {
libc::fcntl(fd, libc::F_READAHEAD, val)
},
}
};
@@ -853,7 +1028,7 @@ pub fn flock(fd: RawFd, arg: FlockArg) -> Result<()> {
/// # Safety
/// Types implementing this must not be `Clone`.
#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
pub unsafe trait Flockable: AsRawFd {}
pub unsafe trait Flockable: std::os::fd::AsRawFd {}
/// Represents an owned flock, which unlocks on drop.
///
@@ -1037,13 +1212,15 @@ feature! {
// 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(linux_android, target_os = "freebsd"))]
pub fn copy_file_range<Fd1: AsFd, Fd2: AsFd>(
pub fn copy_file_range<Fd1: std::os::fd::AsFd, Fd2: std::os::fd::AsFd>(
fd_in: Fd1,
off_in: Option<&mut i64>,
fd_out: Fd2,
off_out: Option<&mut i64>,
len: usize,
) -> Result<usize> {
use std::os::fd::AsRawFd;
let off_in = off_in
.map(|offset| offset as *mut i64)
.unwrap_or(ptr::null_mut());
@@ -1087,7 +1264,7 @@ pub fn copy_file_range<Fd1: AsFd, Fd2: AsFd>(
/// # See Also
/// *[`splice`](https://man7.org/linux/man-pages/man2/splice.2.html)
#[cfg(linux_android)]
pub fn splice<Fd1: AsFd, Fd2: AsFd>(
pub fn splice<Fd1: std::os::fd::AsFd, Fd2: std::os::fd::AsFd>(
fd_in: Fd1,
off_in: Option<&mut libc::loff_t>,
fd_out: Fd2,
@@ -1095,6 +1272,8 @@ pub fn splice<Fd1: AsFd, Fd2: AsFd>(
len: usize,
flags: SpliceFFlags,
) -> Result<usize> {
use std::os::fd::AsRawFd;
let off_in = off_in
.map(|offset| offset as *mut libc::loff_t)
.unwrap_or(ptr::null_mut());
@@ -1113,12 +1292,14 @@ pub fn splice<Fd1: AsFd, Fd2: AsFd>(
/// # See Also
/// *[`tee`](https://man7.org/linux/man-pages/man2/tee.2.html)
#[cfg(linux_android)]
pub fn tee<Fd1: AsFd, Fd2: AsFd>(
pub fn tee<Fd1: std::os::fd::AsFd, Fd2: std::os::fd::AsFd>(
fd_in: Fd1,
fd_out: Fd2,
len: usize,
flags: SpliceFFlags,
) -> Result<usize> {
use std::os::fd::AsRawFd;
let ret = unsafe { libc::tee(fd_in.as_fd().as_raw_fd(), fd_out.as_fd().as_raw_fd(), len, flags.bits()) };
Errno::result(ret).map(|r| r as usize)
}
@@ -1128,11 +1309,13 @@ pub fn tee<Fd1: AsFd, Fd2: AsFd>(
/// # See Also
/// *[`vmsplice`](https://man7.org/linux/man-pages/man2/vmsplice.2.html)
#[cfg(linux_android)]
pub fn vmsplice<F: AsFd>(
pub fn vmsplice<F: std::os::fd::AsFd>(
fd: F,
iov: &[std::io::IoSlice<'_>],
flags: SpliceFFlags,
) -> Result<usize> {
use std::os::fd::AsRawFd;
let ret = unsafe {
libc::vmsplice(
fd.as_fd().as_raw_fd(),
@@ -1173,7 +1356,7 @@ libc_bitflags!(
FALLOC_FL_INSERT_RANGE;
/// Shared file data extants are made private to the file.
///
/// Gaurantees that a subsequent write will not fail due to lack of space.
/// Guarantees that a subsequent write will not fail due to lack of space.
FALLOC_FL_UNSHARE_RANGE;
}
);
@@ -1187,13 +1370,15 @@ feature! {
/// file referred to by fd.
#[cfg(target_os = "linux")]
#[cfg(feature = "fs")]
pub fn fallocate(
fd: RawFd,
pub fn fallocate<Fd: std::os::fd::AsFd>(
fd: Fd,
mode: FallocateFlags,
offset: libc::off_t,
len: libc::off_t,
) -> Result<()> {
let res = unsafe { libc::fallocate(fd, mode.bits(), offset, len) };
use std::os::fd::AsRawFd;
let res = unsafe { libc::fallocate(fd.as_fd().as_raw_fd(), mode.bits(), offset, len) };
Errno::result(res).map(drop)
}
@@ -1260,7 +1445,7 @@ impl SpacectlRange {
/// f.write_all(INITIAL).unwrap();
/// let mut range = SpacectlRange(3, 6);
/// while (!range.is_empty()) {
/// range = fspacectl(f.as_raw_fd(), range).unwrap();
/// range = fspacectl(&f, range).unwrap();
/// }
/// let mut buf = vec![0; INITIAL.len()];
/// f.read_exact_at(&mut buf, 0).unwrap();
@@ -1268,14 +1453,16 @@ impl SpacectlRange {
/// ```
#[cfg(target_os = "freebsd")]
#[inline] // Delays codegen, preventing linker errors with dylibs and --no-allow-shlib-undefined
pub fn fspacectl(fd: RawFd, range: SpacectlRange) -> Result<SpacectlRange> {
pub fn fspacectl<Fd: std::os::fd::AsFd>(fd: Fd, range: SpacectlRange) -> Result<SpacectlRange> {
use std::os::fd::AsRawFd;
let mut rqsr = libc::spacectl_range {
r_offset: range.0,
r_len: range.1,
};
let res = unsafe {
libc::fspacectl(
fd,
fd.as_fd().as_raw_fd(),
libc::SPACECTL_DEALLOC, // Only one command is supported ATM
&rqsr,
0, // No flags are currently supported
@@ -1310,18 +1497,20 @@ pub fn fspacectl(fd: RawFd, range: SpacectlRange) -> Result<SpacectlRange> {
/// const INITIAL: &[u8] = b"0123456789abcdef";
/// let mut f = tempfile().unwrap();
/// f.write_all(INITIAL).unwrap();
/// fspacectl_all(f.as_raw_fd(), 3, 6).unwrap();
/// fspacectl_all(&f, 3, 6).unwrap();
/// let mut buf = vec![0; INITIAL.len()];
/// f.read_exact_at(&mut buf, 0).unwrap();
/// assert_eq!(buf, b"012\0\0\0\0\0\09abcdef");
/// ```
#[cfg(target_os = "freebsd")]
#[inline] // Delays codegen, preventing linker errors with dylibs and --no-allow-shlib-undefined
pub fn fspacectl_all(
fd: RawFd,
pub fn fspacectl_all<Fd: std::os::fd::AsFd>(
fd: Fd,
offset: libc::off_t,
len: libc::off_t,
) -> Result<()> {
use std::os::fd::AsRawFd;
let mut rqsr = libc::spacectl_range {
r_offset: offset,
r_len: len,
@@ -1329,7 +1518,7 @@ pub fn fspacectl_all(
while rqsr.r_len > 0 {
let res = unsafe {
libc::fspacectl(
fd,
fd.as_fd().as_raw_fd(),
libc::SPACECTL_DEALLOC, // Only one command is supported ATM
&rqsr,
0, // No flags are currently supported
@@ -1352,7 +1541,6 @@ pub fn fspacectl_all(
mod posix_fadvise {
use crate::errno::Errno;
use crate::Result;
use std::os::unix::io::RawFd;
#[cfg(feature = "fs")]
libc_enum! {
@@ -1384,13 +1572,15 @@ mod posix_fadvise {
///
/// # See Also
/// * [`posix_fadvise`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_fadvise.html)
pub fn posix_fadvise(
fd: RawFd,
pub fn posix_fadvise<Fd: std::os::fd::AsFd>(
fd: Fd,
offset: libc::off_t,
len: libc::off_t,
advice: PosixFadviseAdvice,
) -> Result<()> {
let res = unsafe { libc::posix_fadvise(fd, offset, len, advice as libc::c_int) };
use std::os::fd::AsRawFd;
let res = unsafe { libc::posix_fadvise(fd.as_fd().as_raw_fd(), offset, len, advice as libc::c_int) };
if res == 0 {
Ok(())
@@ -1412,12 +1602,14 @@ mod posix_fadvise {
target_os = "fuchsia",
target_os = "wasi",
))]
pub fn posix_fallocate(
fd: RawFd,
pub fn posix_fallocate<Fd: std::os::fd::AsFd>(
fd: Fd,
offset: libc::off_t,
len: libc::off_t,
) -> Result<()> {
let res = unsafe { libc::posix_fallocate(fd, offset, len) };
use std::os::fd::AsRawFd;
let res = unsafe { libc::posix_fallocate(fd.as_fd().as_raw_fd(), offset, len) };
match Errno::result(res) {
Err(err) => Err(err),
Ok(0) => Ok(()),

View File

@@ -1,7 +1,7 @@
//! Feature tests for OS functionality
pub use self::os::*;
#[cfg(linux_android)]
#[cfg(any(linux_android, target_os = "emscripten"))]
mod os {
use crate::sys::utsname::uname;
use crate::Result;
@@ -92,7 +92,7 @@ mod os {
}
#[test]
pub fn test_parsing_kernel_version() {
fn test_parsing_kernel_version() {
assert!(kernel_version().unwrap() > 0);
}
}
@@ -103,6 +103,7 @@ mod os {
target_os = "hurd", // Since glibc 2.28
target_os = "illumos", // Since ???
target_os = "redox", // Since 1-july-2020
target_os = "cygwin",
))]
mod os {
/// Check if the OS supports atomic close-on-exec for sockets

View File

@@ -93,7 +93,7 @@ impl InterfaceAddress {
let netmask =
unsafe { SockaddrStorage::from_raw(info.ifa_netmask, None) };
let mut addr = InterfaceAddress {
interface_name: ifname.to_string_lossy().to_string(),
interface_name: ifname.to_string_lossy().into_owned(),
flags: InterfaceFlags::from_bits_truncate(
info.ifa_flags as IflagsType,
),

View File

@@ -34,6 +34,7 @@
//! * `sched` - Manipulate process's scheduling
//! * `socket` - Sockets, whether for networking or local use
//! * `signal` - Send and receive signals to processes
//! * `syslog` - System logging
//! * `term` - Terminal control APIs
//! * `time` - Query the operating system's clocks
//! * `ucontext` - User thread context
@@ -43,9 +44,12 @@
#![crate_name = "nix"]
#![cfg(unix)]
#![allow(non_camel_case_types)]
#![cfg_attr(test, deny(warnings))]
// A clear document is a good document no matter if it has a summary in its
// first paragraph or not.
#![allow(clippy::too_long_first_doc_paragraph)]
#![recursion_limit = "500"]
#![deny(unused)]
#![deny(unexpected_cfgs)]
#![allow(unused_macros)]
#![cfg_attr(
not(all(
@@ -76,6 +80,7 @@
feature = "sched",
feature = "socket",
feature = "signal",
feature = "syslog",
feature = "term",
feature = "time",
feature = "ucontext",
@@ -92,6 +97,11 @@
#![cfg_attr(docsrs, feature(doc_cfg))]
#![deny(clippy::cast_ptr_alignment)]
#![deny(unsafe_op_in_unsafe_fn)]
// I found the change suggested by this rules could hurt code readability. I cannot
// remeber every type's default value, in such cases, it forces me to open
// the std doc to insepct the Default value, which is unnecessary with
// `.unwrap_or(value)`.
#![allow(clippy::unwrap_or_default)]
// Re-exported external crates
pub use libc;
@@ -140,7 +150,11 @@ feature! {
#![feature = "mount"]
pub mod mount;
}
#[cfg(any(freebsdlike, target_os = "linux", target_os = "netbsd"))]
#[cfg(any(
freebsdlike,
all(target_os = "linux", not(target_env = "ohos")),
target_os = "netbsd"
))]
feature! {
#![feature = "mqueue"]
pub mod mqueue;
@@ -185,6 +199,23 @@ pub mod unistd;
#[cfg(any(feature = "poll", feature = "event"))]
mod poll_timeout;
#[cfg(any(
target_os = "freebsd",
target_os = "haiku",
target_os = "linux",
target_os = "netbsd",
apple_targets
))]
feature! {
#![feature = "process"]
pub mod spawn;
}
feature! {
#![feature = "syslog"]
pub mod syslog;
}
use std::ffi::{CStr, CString, OsStr};
use std::mem::MaybeUninit;
use std::os::unix::ffi::OsStrExt;
@@ -205,7 +236,7 @@ pub type Result<T> = result::Result<T, Errno>;
/// * `Eq`
/// * Small size
/// * Represents all of the system's errnos, instead of just the most common
/// ones.
/// ones.
pub type Error = Errno;
/// Common trait used to represent file system paths by many Nix functions.
@@ -289,7 +320,7 @@ impl NixPath for [u8] {
F: FnOnce(&CStr) -> T,
{
// The real PATH_MAX is typically 4096, but it's statistically unlikely to have a path
// longer than ~300 bytes. See the the PR description to get stats for your own machine.
// longer than ~300 bytes. See the PR description to get stats for your own machine.
// https://github.com/nix-rust/nix/pull/1656
//
// By being smaller than a memory page, we also avoid the compiler inserting a probe frame:

View File

@@ -387,7 +387,7 @@ impl<'a> Nmount<'a> {
}
#[cfg(target_os = "freebsd")]
impl<'a> Drop for Nmount<'a> {
impl Drop for Nmount<'_> {
fn drop(&mut self) {
for (iov, is_owned) in self.iov.iter().zip(self.is_owned.iter()) {
if *is_owned {

View File

@@ -260,9 +260,11 @@ pub fn mq_getattr(mqd: &MqdT) -> Result<MqAttr> {
})
}
/// Set the attributes of the message queue. Only `O_NONBLOCK` can be set, everything else will be ignored
/// Returns the old attributes
/// It is recommend to use the `mq_set_nonblock()` and `mq_remove_nonblock()` convenience functions as they are easier to use
/// Set the attributes of the message queue. Only `O_NONBLOCK` can be set,
/// everything else will be ignored. Returns the old attributes.
///
/// It is recommend to use the `mq_set_nonblock()` and `mq_remove_nonblock()`
/// convenience functions as they are easier to use.
///
/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_setattr.html)
pub fn mq_setattr(mqd: &MqdT, newattr: &MqAttr) -> Result<MqAttr> {

View File

@@ -51,7 +51,7 @@ libc_bitflags!(
IFF_BROADCAST as IflagsType;
/// Internal debugging flag. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
#[cfg(not(target_os = "haiku"))]
#[cfg(not(any(target_os = "haiku", target_os = "cygwin")))]
IFF_DEBUG as IflagsType;
/// Interface is a loopback interface. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
@@ -66,7 +66,8 @@ libc_bitflags!(
solarish,
apple_targets,
target_os = "fuchsia",
target_os = "netbsd"))]
target_os = "netbsd",
target_os = "cygwin"))]
IFF_NOTRAILERS as IflagsType;
/// Interface manages own routes.
#[cfg(any(target_os = "dragonfly"))]
@@ -77,7 +78,8 @@ libc_bitflags!(
linux_android,
bsd,
solarish,
target_os = "fuchsia"))]
target_os = "fuchsia",
target_os = "cygwin"))]
IFF_RUNNING as IflagsType;
/// No arp protocol, L2 destination address not set. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
@@ -87,6 +89,7 @@ libc_bitflags!(
IFF_PROMISC as IflagsType;
/// Receive all multicast packets. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
#[cfg(not(target_os = "cygwin"))]
IFF_ALLMULTI as IflagsType;
/// Master of a load balancing bundle. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
@@ -145,7 +148,7 @@ libc_bitflags!(
#[cfg(solarish)]
IFF_PRIVATE as IflagsType;
/// Driver signals L1 up. Volatile.
#[cfg(any(target_os = "fuchsia", target_os = "linux"))]
#[cfg(any(target_os = "fuchsia", target_os = "linux", target_os = "cygwin"))]
IFF_LOWER_UP;
/// Interface is in polling mode.
#[cfg(any(target_os = "dragonfly"))]
@@ -157,7 +160,7 @@ libc_bitflags!(
#[cfg(solarish)]
IFF_NOXMIT as IflagsType;
/// Driver signals dormant. Volatile.
#[cfg(any(target_os = "fuchsia", target_os = "linux"))]
#[cfg(any(target_os = "fuchsia", target_os = "linux", target_os = "cygwin"))]
IFF_DORMANT;
/// User-requested promisc mode.
#[cfg(freebsdlike)]

View File

@@ -2,7 +2,7 @@
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd};
use crate::errno::Errno;
pub use crate::poll_timeout::PollTimeout;
pub use crate::poll_timeout::{PollTimeout, PollTimeoutTryFromError};
use crate::Result;
/// This is a wrapper around `libc::pollfd`.
@@ -11,10 +11,10 @@ use crate::Result;
/// [`ppoll`](fn.ppoll.html) functions to specify the events of interest
/// for a specific file descriptor.
///
/// After a call to `poll` or `ppoll`, the events that occurred can be
/// retrieved by calling [`revents()`](#method.revents) on the `PollFd`.
/// After a call to `poll` or `ppoll`, the events that occurred can be retrieved by calling
/// [`revents()`](#method.revents) on the `PollFd` object from the array passed to `poll`.
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct PollFd<'fd> {
pollfd: libc::pollfd,
_fd: std::marker::PhantomData<BorrowedFd<'fd>>,
@@ -33,11 +33,8 @@ impl<'fd> PollFd<'fd> {
/// # };
/// let (r, w) = pipe().unwrap();
/// let pfd = PollFd::new(r.as_fd(), PollFlags::POLLIN);
/// let mut fds = [pfd];
/// poll(&mut fds, PollTimeout::NONE).unwrap();
/// let mut buf = [0u8; 80];
/// read(r.as_raw_fd(), &mut buf[..]);
/// ```
/// These are placed in an array and passed to [`poll`] or [`ppoll`](fn.ppoll.html).
// Unlike I/O functions, constructors like this must take `BorrowedFd`
// instead of AsFd or &AsFd. Otherwise, an `OwnedFd` argument would be
// dropped at the end of the method, leaving the structure referencing a
@@ -61,7 +58,7 @@ impl<'fd> PollFd<'fd> {
/// Returns the events that occurred in the last call to `poll` or `ppoll`. Will only return
/// `None` if the kernel provides status flags that Nix does not know about.
pub fn revents(self) -> Option<PollFlags> {
pub fn revents(&self) -> Option<PollFlags> {
PollFlags::from_bits(self.pollfd.revents)
}
@@ -71,7 +68,7 @@ impl<'fd> PollFd<'fd> {
/// Equivalent to `x.revents()? != PollFlags::empty()`.
///
/// This is marginally more efficient than [`PollFd::all`].
pub fn any(self) -> Option<bool> {
pub fn any(&self) -> Option<bool> {
Some(self.revents()? != PollFlags::empty())
}
@@ -81,12 +78,12 @@ impl<'fd> PollFd<'fd> {
/// Equivalent to `x.revents()? & x.events() == x.events()`.
///
/// This is marginally less efficient than [`PollFd::any`].
pub fn all(self) -> Option<bool> {
pub fn all(&self) -> Option<bool> {
Some(self.revents()? & self.events() == self.events())
}
/// The events of interest for this `PollFd`.
pub fn events(self) -> PollFlags {
pub fn events(&self) -> PollFlags {
PollFlags::from_bits(self.pollfd.events).unwrap()
}
@@ -96,7 +93,7 @@ impl<'fd> PollFd<'fd> {
}
}
impl<'fd> AsFd for PollFd<'fd> {
impl AsFd for PollFd<'_> {
fn as_fd(&self) -> BorrowedFd<'_> {
// Safety:
//
@@ -196,6 +193,34 @@ libc_bitflags! {
/// in timeout means an infinite timeout. Specifying a timeout of
/// [`PollTimeout::ZERO`] causes `poll()` to return immediately, even if no file
/// descriptors are ready.
///
/// The return value contains the number of `fds` which have selected events ([`PollFd::revents`]).
///
/// # Examples
/// ```no_run
/// # use std::os::unix::io::{AsFd, AsRawFd, FromRawFd};
/// # use nix::{
/// # poll::{PollTimeout, PollFd, PollFlags, poll},
/// # unistd::{pipe, read}
/// # };
/// let (r0, w0) = pipe().unwrap();
/// let (r1, w1) = pipe().unwrap();
///
/// let mut pollfds = [
/// PollFd::new(r0.as_fd(), PollFlags::POLLIN),
/// PollFd::new(r1.as_fd(), PollFlags::POLLIN),
/// ];
///
/// let nready = poll(&mut pollfds, PollTimeout::NONE).unwrap();
/// assert!(nready >= 1); // Since there is no timeout
///
/// let mut buf = [0u8; 80];
/// if pollfds[0].any().unwrap_or_default() {
/// read(&r0, &mut buf[..]);
/// } else if pollfds[1].any().unwrap_or_default() {
/// read(&r1, &mut buf[..]);
/// }
/// ```
pub fn poll<T: Into<PollTimeout>>(
fds: &mut [PollFd],
timeout: T,
@@ -217,7 +242,7 @@ feature! {
/// descriptor becomes ready or until a signal is caught.
/// ([`poll(2)`](https://man7.org/linux/man-pages/man2/poll.2.html))
///
/// `ppoll` behaves like `poll`, but let you specify what signals may interrupt it
/// `ppoll` behaves like [`poll`], but let you specify what signals may interrupt it
/// with the `sigmask` argument. If you want `ppoll` to block indefinitely,
/// specify `None` as `timeout` (it is like `timeout = -1` for `poll`).
/// If `sigmask` is `None`, then no signal mask manipulation is performed,

View File

@@ -52,6 +52,17 @@ pub enum ForkptyResult {
#[derive(Debug)]
pub struct PtyMaster(OwnedFd);
impl PtyMaster {
/// Constructs a `PytMaster` wrapping an existing `OwnedFd`.
///
/// # Safety
///
/// `OwnedFd` is a valid `PtyMaster`.
pub unsafe fn from_owned_fd(fd: OwnedFd) -> Self {
Self(fd)
}
}
impl AsRawFd for PtyMaster {
fn as_raw_fd(&self) -> RawFd {
self.0.as_raw_fd()
@@ -64,6 +75,12 @@ impl AsFd for PtyMaster {
}
}
impl From<PtyMaster> for OwnedFd {
fn from(value: PtyMaster) -> Self {
value.0
}
}
impl IntoRawFd for PtyMaster {
fn into_raw_fd(self) -> RawFd {
let fd = self.0;
@@ -73,7 +90,7 @@ impl IntoRawFd for PtyMaster {
impl io::Read for PtyMaster {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
unistd::read(self.0.as_raw_fd(), buf).map_err(io::Error::from)
unistd::read(&self.0, buf).map_err(io::Error::from)
}
}
@@ -88,7 +105,7 @@ impl io::Write for PtyMaster {
impl io::Read for &PtyMaster {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
unistd::read(self.0.as_raw_fd(), buf).map_err(io::Error::from)
unistd::read(&self.0, buf).map_err(io::Error::from)
}
}
@@ -317,9 +334,9 @@ feature! {
/// # Safety
///
/// In a multithreaded program, only [async-signal-safe] functions like `pause`
/// and `_exit` may be called by the child (the parent isn't restricted). Note
/// that memory allocation may **not** be async-signal-safe and thus must be
/// prevented.
/// and `_exit` may be called by the child (the parent isn't restricted) until
/// a call of `execve(2)`. Note that memory allocation may **not** be
/// async-signal-safe and thus must be prevented.
///
/// Those functions are only a small subset of your operating system's API, so
/// special care must be taken to only invoke code you can control and audit.

View File

@@ -120,7 +120,10 @@ mod sched_linux_like {
let ptr = stack.as_mut_ptr().add(stack.len());
let ptr_aligned = ptr.sub(ptr as usize % 16);
libc::clone(
mem::transmute(
mem::transmute::<
extern "C" fn(*mut Box<dyn FnMut() -> isize>) -> i32,
extern "C" fn(*mut libc::c_void) -> i32,
>(
callback
as extern "C" fn(*mut Box<dyn FnMut() -> isize>) -> i32,
),

431
third_party/rust/nix/src/spawn.rs vendored Normal file
View File

@@ -0,0 +1,431 @@
//! Safe wrappers around posix_spawn* functions found in the libc "spawn.h" header.
use std::{ffi::CStr, mem, os::fd::RawFd};
#[cfg(any(feature = "fs", feature = "term"))]
use crate::fcntl::OFlag;
#[cfg(feature = "signal")]
use crate::sys::signal::SigSet;
#[cfg(feature = "fs")]
use crate::sys::stat::Mode;
use crate::{errno::Errno, unistd::Pid, NixPath, Result};
/// A spawn attributes object. See [posix_spawnattr_t](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_init.html).
#[repr(transparent)]
#[derive(Debug)]
pub struct PosixSpawnAttr {
attr: libc::posix_spawnattr_t,
}
impl PosixSpawnAttr {
/// Initialize the spawn attributes object. See
/// [posix_spawnattr_init](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_init.html).
#[doc(alias("posix_spawnattr_init"))]
pub fn init() -> Result<PosixSpawnAttr> {
let mut attr = mem::MaybeUninit::uninit();
let res = unsafe { libc::posix_spawnattr_init(attr.as_mut_ptr()) };
Errno::result(res)?;
let attr = unsafe { attr.assume_init() };
Ok(PosixSpawnAttr { attr })
}
/// Reinitialize the spawn attributes object.
/// This is a wrapper around
/// [posix_spawnattr_destroy](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_destroy.html)
/// followed by
/// [posix_spawnattr_init](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_init.html).
#[doc(alias("posix_spawnattr_destroy"))]
pub fn reinit(mut self) -> Result<PosixSpawnAttr> {
let res = unsafe {
libc::posix_spawnattr_destroy(
&mut self.attr as *mut libc::posix_spawnattr_t,
)
};
Errno::result(res)?;
let res = unsafe {
libc::posix_spawnattr_init(
&mut self.attr as *mut libc::posix_spawnattr_t,
)
};
Errno::result(res)?;
Ok(self)
}
/// Set spawn flags. See
/// [posix_spawnattr_setflags](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setflags.html).
#[doc(alias("posix_spawnattr_setflags"))]
pub fn set_flags(&mut self, flags: PosixSpawnFlags) -> Result<()> {
let res = unsafe {
libc::posix_spawnattr_setflags(
&mut self.attr as *mut libc::posix_spawnattr_t,
flags.bits() as libc::c_short,
)
};
Errno::result(res)?;
Ok(())
}
/// Get spawn flags. See
/// [posix_spawnattr_getflags](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_getflags.html).
#[doc(alias("posix_spawnattr_getflags"))]
pub fn flags(&self) -> Result<PosixSpawnFlags> {
let mut flags: libc::c_short = 0;
let res = unsafe {
libc::posix_spawnattr_getflags(
&self.attr as *const libc::posix_spawnattr_t,
&mut flags,
)
};
Errno::result(res)?;
Ok(PosixSpawnFlags::from_bits_truncate(flags.into()))
}
/// Set spawn pgroup. See
/// [posix_spawnattr_setpgroup](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setpgroup.html).
#[doc(alias("posix_spawnattr_setpgroup"))]
pub fn set_pgroup(&mut self, pgroup: Pid) -> Result<()> {
let res = unsafe {
libc::posix_spawnattr_setpgroup(
&mut self.attr as *mut libc::posix_spawnattr_t,
pgroup.as_raw(),
)
};
Errno::result(res)?;
Ok(())
}
/// Get spawn pgroup. See
/// [posix_spawnattr_getpgroup](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_getpgroup.html).
#[doc(alias("posix_spawnattr_getpgroup"))]
pub fn pgroup(&self) -> Result<Pid> {
let mut pid: libc::pid_t = 0;
let res = unsafe {
libc::posix_spawnattr_getpgroup(
&self.attr as *const libc::posix_spawnattr_t,
&mut pid,
)
};
Errno::result(res)?;
Ok(Pid::from_raw(pid))
}
feature! {
#![feature = "signal"]
/// Set spawn sigdefault. See
/// [posix_spawnattr_setsigdefault](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setsigdefault.html).
#[doc(alias("posix_spawnattr_setsigdefault"))]
pub fn set_sigdefault(&mut self, sigdefault: &SigSet) -> Result<()> {
let res = unsafe {
libc::posix_spawnattr_setsigdefault(
&mut self.attr as *mut libc::posix_spawnattr_t,
sigdefault.as_ref(),
)
};
Errno::result(res)?;
Ok(())
}
/// Get spawn sigdefault. See
/// [posix_spawnattr_getsigdefault](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_getsigdefault.html).
#[doc(alias("posix_spawnattr_getsigdefault"))]
pub fn sigdefault(&self) -> Result<SigSet> {
let mut sigset = mem::MaybeUninit::uninit();
let res = unsafe {
libc::posix_spawnattr_getsigdefault(
&self.attr as *const libc::posix_spawnattr_t,
sigset.as_mut_ptr(),
)
};
Errno::result(res)?;
let sigdefault =
unsafe { SigSet::from_sigset_t_unchecked(sigset.assume_init()) };
Ok(sigdefault)
}
/// Set spawn sigmask. See
/// [posix_spawnattr_setsigmask](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setsigmask.html).
#[doc(alias("posix_spawnattr_setsigmask"))]
pub fn set_sigmask(&mut self, sigdefault: &SigSet) -> Result<()> {
let res = unsafe {
libc::posix_spawnattr_setsigmask(
&mut self.attr as *mut libc::posix_spawnattr_t,
sigdefault.as_ref(),
)
};
Errno::result(res)?;
Ok(())
}
/// Get spawn sigmask. See
/// [posix_spawnattr_getsigmask](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_getsigmask.html).
#[doc(alias("posix_spawnattr_getsigmask"))]
pub fn sigmask(&self) -> Result<SigSet> {
let mut sigset = mem::MaybeUninit::uninit();
let res = unsafe {
libc::posix_spawnattr_getsigmask(
&self.attr as *const libc::posix_spawnattr_t,
sigset.as_mut_ptr(),
)
};
Errno::result(res)?;
let sigdefault =
unsafe { SigSet::from_sigset_t_unchecked(sigset.assume_init()) };
Ok(sigdefault)
}
}
}
impl Drop for PosixSpawnAttr {
fn drop(&mut self) {
unsafe {
libc::posix_spawnattr_destroy(
&mut self.attr as *mut libc::posix_spawnattr_t,
);
}
}
}
libc_bitflags!(
/// Process attributes to be changed in the new process image when invoking [`posix_spawn`]
/// or [`posix_spawnp`]. See
/// [posix_spawn](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn.html).
pub struct PosixSpawnFlags: libc::c_int {
/// Reset effective user ID of the child process to parent's real user ID.
POSIX_SPAWN_RESETIDS;
/// Put the child in a process group specified by the spawn-pgroup attribute. See
/// [posix_spawnattr_setpgroup](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setpgroup.html).
POSIX_SPAWN_SETPGROUP;
/// Force set signals to default signal handling in child process. See
/// [posix_spawnattr_setsigdefault](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setsigdefault.html).
#[cfg(feature = "signal")]
POSIX_SPAWN_SETSIGDEF;
/// Set signal mask of child process. See
/// [posix_spawnattr_setsigmask](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setsigmask.html).
#[cfg(feature = "signal")]
POSIX_SPAWN_SETSIGMASK;
// TODO: Add support for the following two flags whenever support for
// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sched.h.html
// is added to nix.
// POSIX_SPAWN_SETSCHEDPARAM;
// POSIX_SPAWN_SETSCHEDULER;
}
);
/// A spawn file actions object. See [posix_spawn_file_actions_t](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_addclose.html).
#[repr(transparent)]
#[derive(Debug)]
pub struct PosixSpawnFileActions {
fa: libc::posix_spawn_file_actions_t,
}
impl PosixSpawnFileActions {
/// Initialize the spawn file actions object. See
/// [posix_spawn_file_actions_init](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_init.html).
#[doc(alias("posix_spawn_file_actions_init"))]
pub fn init() -> Result<PosixSpawnFileActions> {
let mut actions = mem::MaybeUninit::uninit();
let res = unsafe {
libc::posix_spawn_file_actions_init(actions.as_mut_ptr())
};
Errno::result(res)?;
Ok(unsafe {
PosixSpawnFileActions {
fa: actions.assume_init(),
}
})
}
/// Reinitialize the spawn file actions object.
/// This is a wrapper around
/// [posix_spawn_file_actions_destroy](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_destroy.html).
/// followed by
/// [posix_spawn_file_actions_init](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_init.html).
#[doc(alias("posix_spawn_file_actions_destroy"))]
pub fn reinit(mut self) -> Result<PosixSpawnFileActions> {
let res = unsafe {
libc::posix_spawn_file_actions_destroy(
&mut self.fa as *mut libc::posix_spawn_file_actions_t,
)
};
Errno::result(res)?;
let res = unsafe {
libc::posix_spawn_file_actions_init(
&mut self.fa as *mut libc::posix_spawn_file_actions_t,
)
};
Errno::result(res)?;
Ok(self)
}
/// Add a [dup2](https://pubs.opengroup.org/onlinepubs/9699919799/functions/dup2.html) action. See
/// [posix_spawn_file_actions_adddup2](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_adddup2.html).
#[doc(alias("posix_spawn_file_actions_adddup2"))]
pub fn add_dup2(&mut self, fd: RawFd, newfd: RawFd) -> Result<()> {
let res = unsafe {
libc::posix_spawn_file_actions_adddup2(
&mut self.fa as *mut libc::posix_spawn_file_actions_t,
fd,
newfd,
)
};
Errno::result(res)?;
Ok(())
}
feature! {
#![all(feature = "fs", feature = "term")]
/// Add an open action. See
/// [posix_spawn_file_actions_addopen](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_addopen.html).
#[doc(alias("posix_spawn_file_actions_addopen"))]
pub fn add_open<P: ?Sized + NixPath>(
&mut self,
fd: RawFd,
path: &P,
oflag: OFlag,
mode: Mode,
) -> Result<()> {
let res = path.with_nix_path(|cstr| unsafe {
libc::posix_spawn_file_actions_addopen(
&mut self.fa as *mut libc::posix_spawn_file_actions_t,
fd,
cstr.as_ptr(),
oflag.bits(),
mode.bits(),
)
})?;
Errno::result(res)?;
Ok(())
}
}
/// Add a close action. See
/// [posix_spawn_file_actions_addclose](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_addclose.html).
#[doc(alias("posix_spawn_file_actions_addclose"))]
pub fn add_close(&mut self, fd: RawFd) -> Result<()> {
let res = unsafe {
libc::posix_spawn_file_actions_addclose(
&mut self.fa as *mut libc::posix_spawn_file_actions_t,
fd,
)
};
Errno::result(res)?;
Ok(())
}
}
impl Drop for PosixSpawnFileActions {
fn drop(&mut self) {
unsafe {
libc::posix_spawn_file_actions_destroy(
&mut self.fa as *mut libc::posix_spawn_file_actions_t,
);
}
}
}
// The POSIX standard requires those `args` and `envp` to be of type `*const *mut [c_char]`,
// but implementations won't modify them, making the `mut` type redundant. Considering this,
// Nix does not expose this mutability, but we have to change the interface when calling the
// underlying libc interfaces , this helper function does the conversion job.
//
// SAFETY:
// It is safe to add the mutability in types as implementations won't mutable them.
unsafe fn to_exec_array<S: AsRef<CStr>>(args: &[S]) -> Vec<*mut libc::c_char> {
let mut v: Vec<*mut libc::c_char> = args
.iter()
.map(|s| s.as_ref().as_ptr().cast_mut())
.collect();
v.push(std::ptr::null_mut());
v
}
/// Create a new child process from the specified process image. See
/// [posix_spawn](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn.html).
pub fn posix_spawn<P, SA, SE>(
path: &P,
file_actions: &PosixSpawnFileActions,
attr: &PosixSpawnAttr,
args: &[SA],
envp: &[SE],
) -> Result<Pid>
where
P: NixPath + ?Sized,
SA: AsRef<CStr>,
SE: AsRef<CStr>,
{
let mut pid = 0;
let ret = unsafe {
let args_p = to_exec_array(args);
let env_p = to_exec_array(envp);
path.with_nix_path(|c_str| {
libc::posix_spawn(
&mut pid as *mut libc::pid_t,
c_str.as_ptr(),
&file_actions.fa as *const libc::posix_spawn_file_actions_t,
&attr.attr as *const libc::posix_spawnattr_t,
args_p.as_ptr(),
env_p.as_ptr(),
)
})?
};
if ret != 0 {
return Err(Errno::from_raw(ret));
}
Ok(Pid::from_raw(pid))
}
/// Create a new child process from the specified process image. See
/// [posix_spawnp](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnp.html).
pub fn posix_spawnp<SA: AsRef<CStr>, SE: AsRef<CStr>>(
path: &CStr,
file_actions: &PosixSpawnFileActions,
attr: &PosixSpawnAttr,
args: &[SA],
envp: &[SE],
) -> Result<Pid> {
let mut pid = 0;
let ret = unsafe {
let args_p = to_exec_array(args);
let env_p = to_exec_array(envp);
libc::posix_spawnp(
&mut pid as *mut libc::pid_t,
path.as_ptr(),
&file_actions.fa as *const libc::posix_spawn_file_actions_t,
&attr.attr as *const libc::posix_spawnattr_t,
args_p.as_ptr(),
env_p.as_ptr(),
)
};
if ret != 0 {
return Err(Errno::from_raw(ret));
}
Ok(Pid::from_raw(pid))
}

View File

@@ -193,7 +193,7 @@ impl<'a> AioCb<'a> {
}
}
impl<'a> Debug for AioCb<'a> {
impl Debug for AioCb<'_> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("AioCb")
.field("aiocb", &self.aiocb.0)
@@ -202,7 +202,7 @@ impl<'a> Debug for AioCb<'a> {
}
}
impl<'a> Drop for AioCb<'a> {
impl Drop for AioCb<'_> {
/// If the `AioCb` has no remaining state in the kernel, just drop it.
/// Otherwise, dropping constitutes a resource leak, which is an error
fn drop(&mut self) {
@@ -455,10 +455,9 @@ impl<'a> AioFsync<'a> {
/// * `fd`: File descriptor to sync.
/// * `mode`: Whether to sync file metadata too, or just data.
/// * `prio`: If POSIX Prioritized IO is supported, then the
/// operation will be prioritized at the process's
/// priority level minus `prio`.
/// * `sigev_notify`: Determines how you will be notified of event
/// completion.
/// operation will be prioritized at the process's priority level minus
/// `prio`.
/// * `sigev_notify`: Determines how you will be notified of event completion.
pub fn new(
fd: BorrowedFd<'a>,
mode: AioFsyncMode,
@@ -499,7 +498,7 @@ impl<'a> Aio for AioFsync<'a> {
// AioFsync does not need AsMut, since it can't be used with lio_listio
impl<'a> AsRef<libc::aiocb> for AioFsync<'a> {
impl AsRef<libc::aiocb> for AioFsync<'_> {
fn as_ref(&self) -> &libc::aiocb {
&self.aiocb.aiocb.0
}
@@ -573,11 +572,9 @@ impl<'a> AioRead<'a> {
/// * `fd`: File descriptor to read from
/// * `offs`: File offset
/// * `buf`: A memory buffer. It must outlive the `AioRead`.
/// * `prio`: If POSIX Prioritized IO is supported, then the
/// operation will be prioritized at the process's
/// priority level minus `prio`
/// * `sigev_notify`: Determines how you will be notified of event
/// completion.
/// * `prio`: If POSIX Prioritized IO is supported, then the operation
/// will be prioritized at the process's priority level minus `prio`
/// * `sigev_notify`: Determines how you will be notified of event completion.
pub fn new(
fd: BorrowedFd<'a>,
offs: off_t,
@@ -609,13 +606,13 @@ impl<'a> Aio for AioRead<'a> {
aio_methods!(aio_read);
}
impl<'a> AsMut<libc::aiocb> for AioRead<'a> {
impl AsMut<libc::aiocb> for AioRead<'_> {
fn as_mut(&mut self) -> &mut libc::aiocb {
&mut self.aiocb.aiocb.0
}
}
impl<'a> AsRef<libc::aiocb> for AioRead<'a> {
impl AsRef<libc::aiocb> for AioRead<'_> {
fn as_ref(&self) -> &libc::aiocb {
&self.aiocb.aiocb.0
}
@@ -732,14 +729,14 @@ impl<'a> Aio for AioReadv<'a> {
}
#[cfg(target_os = "freebsd")]
impl<'a> AsMut<libc::aiocb> for AioReadv<'a> {
impl AsMut<libc::aiocb> for AioReadv<'_> {
fn as_mut(&mut self) -> &mut libc::aiocb {
&mut self.aiocb.aiocb.0
}
}
#[cfg(target_os = "freebsd")]
impl<'a> AsRef<libc::aiocb> for AioReadv<'a> {
impl AsRef<libc::aiocb> for AioReadv<'_> {
fn as_ref(&self) -> &libc::aiocb {
&self.aiocb.aiocb.0
}
@@ -805,11 +802,9 @@ impl<'a> AioWrite<'a> {
/// * `fd`: File descriptor to write to
/// * `offs`: File offset
/// * `buf`: A memory buffer. It must outlive the `AioWrite`.
/// * `prio`: If POSIX Prioritized IO is supported, then the
/// operation will be prioritized at the process's
/// priority level minus `prio`
/// * `sigev_notify`: Determines how you will be notified of event
/// completion.
/// * `prio`: If POSIX Prioritized IO is supported, then the operation
/// will be prioritized at the process's priority level minus `prio`
/// * `sigev_notify`: Determines how you will be notified of event completion.
pub fn new(
fd: BorrowedFd<'a>,
offs: off_t,
@@ -845,13 +840,13 @@ impl<'a> Aio for AioWrite<'a> {
aio_methods!(aio_write);
}
impl<'a> AsMut<libc::aiocb> for AioWrite<'a> {
impl AsMut<libc::aiocb> for AioWrite<'_> {
fn as_mut(&mut self) -> &mut libc::aiocb {
&mut self.aiocb.aiocb.0
}
}
impl<'a> AsRef<libc::aiocb> for AioWrite<'a> {
impl AsRef<libc::aiocb> for AioWrite<'_> {
fn as_ref(&self) -> &libc::aiocb {
&self.aiocb.aiocb.0
}
@@ -965,14 +960,14 @@ impl<'a> Aio for AioWritev<'a> {
}
#[cfg(target_os = "freebsd")]
impl<'a> AsMut<libc::aiocb> for AioWritev<'a> {
impl AsMut<libc::aiocb> for AioWritev<'_> {
fn as_mut(&mut self) -> &mut libc::aiocb {
&mut self.aiocb.aiocb.0
}
}
#[cfg(target_os = "freebsd")]
impl<'a> AsRef<libc::aiocb> for AioWritev<'a> {
impl AsRef<libc::aiocb> for AioWritev<'_> {
fn as_ref(&self) -> &libc::aiocb {
&self.aiocb.aiocb.0
}

View File

@@ -1,5 +1,6 @@
use crate::errno::Errno;
pub use crate::poll_timeout::PollTimeout as EpollTimeout;
pub use crate::poll_timeout::PollTimeoutTryFromError as EpollTimeoutTryFromError;
use crate::Result;
use libc::{self, c_int};
use std::mem;
@@ -88,7 +89,7 @@ impl EpollEvent {
/// epoll.add(&eventfd, EpollEvent::new(EpollFlags::EPOLLIN,DATA))?;
///
/// // Arm eventfd & Time wait
/// eventfd.arm()?;
/// eventfd.write(1)?;
/// let now = Instant::now();
///
/// // Wait on event
@@ -205,7 +206,10 @@ pub fn epoll_create1(flags: EpollCreateFlags) -> Result<RawFd> {
Errno::result(res)
}
#[deprecated(since = "0.27.0", note = "Use Epoll::epoll_ctl() instead")]
#[deprecated(
since = "0.27.0",
note = "Use corresponding Epoll methods instead"
)]
#[inline]
pub fn epoll_ctl<'a, T>(
epfd: RawFd,

View File

@@ -173,7 +173,7 @@ 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 {
pub struct EvFlags: type_of_event_flag {
#[allow(missing_docs)]
EV_ADD;
#[allow(missing_docs)]
@@ -216,6 +216,10 @@ libc_bitflags! {
}
}
#[deprecated(since = "0.30.0", note = "Use `EvFlags instead`")]
/// The deprecated EventFlag type alias
pub type EventFlag = EvFlags;
libc_bitflags!(
/// Filter-specific flags. See the man page for details.
// There's no useful documentation we can write for the individual flags
@@ -347,7 +351,7 @@ impl KEvent {
pub fn new(
ident: uintptr_t,
filter: EventFilter,
flags: EventFlag,
flags: EvFlags,
fflags: FilterFlag,
data: intptr_t,
udata: intptr_t,
@@ -382,8 +386,8 @@ impl KEvent {
/// 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()
pub fn flags(&self) -> EvFlags {
EvFlags::from_bits(self.kevent.flags).unwrap()
}
/// Filter-specific flags.
@@ -443,7 +447,7 @@ pub fn ev_set(
ev: &mut KEvent,
ident: usize,
filter: EventFilter,
flags: EventFlag,
flags: EvFlags,
fflags: FilterFlag,
udata: intptr_t,
) {

View File

@@ -1,69 +1,102 @@
use crate::errno::Errno;
use crate::{Result,unistd};
use std::os::unix::io::{FromRawFd, OwnedFd, AsRawFd, AsFd, RawFd, BorrowedFd};
use crate::{unistd, Result};
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd};
libc_bitflags! {
/// Eventfd flags.
pub struct EfdFlags: libc::c_int {
/// Set the close-on-exec (`FD_CLOEXEC`) flag on the new event file descriptor.
EFD_CLOEXEC; // Since Linux 2.6.27/FreeBSD 13.0
/// Set the `O_NONBLOCK` file status flag on the new event file description.
EFD_NONBLOCK; // Since Linux 2.6.27/FreeBSD 13.0
/// Provide semaphore-like semantics for reads from the new event file
/// descriptor.
EFD_SEMAPHORE; // Since Linux 2.6.30/FreeBSD 13.0
}
}
#[deprecated(since = "0.28.0", note = "Use EventFd::from_value_and_flags() instead")]
#[deprecated(
since = "0.28.0",
note = "Use EventFd::from_value_and_flags() instead"
)]
#[allow(missing_docs)]
pub fn eventfd(initval: libc::c_uint, flags: EfdFlags) -> Result<OwnedFd> {
let res = unsafe { libc::eventfd(initval, flags.bits()) };
Errno::result(res).map(|r| unsafe { OwnedFd::from_raw_fd(r) })
}
/// An eventfd file descriptor.
#[derive(Debug)]
#[repr(transparent)]
pub struct EventFd(OwnedFd);
impl EventFd {
/// [`EventFd::from_value_and_flags`] with `init_val = 0` and `flags = EfdFlags::empty()`.
pub fn new() -> Result<Self> {
Self::from_value_and_flags(0, EfdFlags::empty())
}
/// Constructs [`EventFd`] with the given `init_val` and `flags`.
///
///
/// Wrapper around [`libc::eventfd`].
pub fn from_value_and_flags(init_val: u32, flags: EfdFlags) -> Result<Self> {
pub fn from_value_and_flags(
init_val: u32,
flags: EfdFlags,
) -> Result<Self> {
let res = unsafe { libc::eventfd(init_val, flags.bits()) };
Errno::result(res).map(|r| Self(unsafe { OwnedFd::from_raw_fd(r) }))
}
/// [`EventFd::from_value_and_flags`] with `init_val = 0` and given `flags`.
pub fn from_flags(flags: EfdFlags) -> Result<Self> {
Self::from_value_and_flags(0, flags)
}
/// [`EventFd::from_value_and_flags`] with given `init_val` and `flags = EfdFlags::empty()`.
pub fn from_value(init_val: u32) -> Result<Self> {
Self::from_value_and_flags(init_val, EfdFlags::empty())
}
/// Arms `self`, a following call to `poll`, `select` or `epoll` will return immediately.
///
/// [`EventFd::write`] with `1`.
pub fn arm(&self) -> Result<usize> {
self.write(1)
/// Constructs an `EventFd` wrapping an existing `OwnedFd`.
///
/// # Safety
///
/// `OwnedFd` is a valid eventfd.
pub unsafe fn from_owned_fd(fd: OwnedFd) -> Self {
Self(fd)
}
/// Defuses `self`, a following call to `poll`, `select` or `epoll` will block.
///
/// [`EventFd::write`] with `0`.
pub fn defuse(&self) -> Result<usize> {
self.write(0)
}
/// Enqueues `value` triggers.
///
/// Enqueues `value` triggers, i.e., adds the integer value supplied in `value`
/// to the counter.
///
/// The next `value` calls to `poll`, `select` or `epoll` will return immediately.
///
///
/// [`EventFd::write`] with `value`.
pub fn write(&self, value: u64) -> Result<usize> {
unistd::write(&self.0,&value.to_ne_bytes())
pub fn write(&self, value: u64) -> Result<usize> {
unistd::write(&self.0, &value.to_ne_bytes())
}
// Reads the value from the file descriptor.
/// Reads the value from the file descriptor.
///
/// * If [`EFD_SEMAPHORE`](EfdFlags::EFD_SEMAPHORE) was not specified and
/// the eventfd counter has a nonzero value, then this function returns
/// an `u64` containing that value, and the counter's value is reset to
/// zero.
///
/// * If [`EFD_SEMAPHORE`](EfdFlags::EFD_SEMAPHORE) was specified and the
/// eventfd counter has a nonzero value, then this function returns an
/// `u64` containing the value 1, and the counter's value is decremented
/// by 1.
///
/// * If the eventfd counter is zero at the time of this call, then the
/// call either blocks until the counter becomes nonzero (at which time,
/// this function proceeds as described above) or fails with the error
/// `EAGAIN` if the file descriptor has been made nonblocking with
/// [`EFD_NONBLOCK`](EfdFlags::EFD_NONBLOCK).
pub fn read(&self) -> Result<u64> {
let mut arr = [0; std::mem::size_of::<u64>()];
unistd::read(self.0.as_raw_fd(),&mut arr)?;
unistd::read(&self.0, &mut arr)?;
Ok(u64::from_ne_bytes(arr))
}
}
@@ -77,8 +110,9 @@ impl AsRawFd for EventFd {
self.0.as_raw_fd()
}
}
impl From<EventFd> for OwnedFd {
fn from(x: EventFd) -> OwnedFd {
x.0
fn from(value: EventFd) -> Self {
value.0
}
}

View File

@@ -11,7 +11,7 @@
//! [fanotify(7)](https://man7.org/linux/man-pages/man7/fanotify.7.html).
use crate::errno::Errno;
use crate::fcntl::{at_rawfd, OFlag};
use crate::fcntl::OFlag;
use crate::unistd::{close, read, write};
use crate::{NixPath, Result};
use std::marker::PhantomData;
@@ -20,8 +20,8 @@ use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd};
use std::ptr;
libc_bitflags! {
/// Mask for defining which events shall be listened with
/// [`fanotify_mark`](fn.fanotify_mark.html) and for querying notifications.
/// Mask for defining which events shall be listened with [`Fanotify::mark()`]
/// and for querying notifications.
pub struct MaskFlags: u64 {
/// File was accessed.
FAN_ACCESS;
@@ -80,7 +80,7 @@ libc_bitflags! {
}
libc_bitflags! {
/// Configuration options for [`fanotify_init`](fn.fanotify_init.html).
/// Configuration options for [`Fanotify::init()`].
pub struct InitFlags: libc::c_uint {
/// Close-on-exec flag set on the file descriptor.
FAN_CLOEXEC;
@@ -162,7 +162,7 @@ impl From<EventFFlags> for OFlag {
}
libc_bitflags! {
/// Configuration options for [`fanotify_mark`](fn.fanotify_mark.html).
/// Configuration options for [`Fanotify::mark()`].
pub struct MarkFlags: libc::c_uint {
/// Add the events to the marks.
FAN_MARK_ADD;
@@ -198,8 +198,8 @@ libc_bitflags! {
/// Compile version number of fanotify API.
pub const FANOTIFY_METADATA_VERSION: u8 = libc::FANOTIFY_METADATA_VERSION;
/// Abstract over `libc::fanotify_event_metadata`, which represents an event
/// received via `Fanotify::read_events`.
/// Abstract over [`libc::fanotify_event_metadata`], which represents an event
/// received via [`Fanotify::read_events`].
// Is not Clone due to fd field, to avoid use-after-close scenarios.
#[derive(Debug, Eq, Hash, PartialEq)]
#[repr(transparent)]
@@ -281,7 +281,7 @@ impl<'a> FanotifyResponse<'a> {
}
libc_bitflags! {
/// Response to be wrapped in `FanotifyResponse` and sent to the `Fanotify`
/// Response to be wrapped in [`FanotifyResponse`] and sent to the [`Fanotify`]
/// group to allow or deny an event.
pub struct Response: u32 {
/// Allow the event.
@@ -317,16 +317,15 @@ impl Fanotify {
}
/// Add, remove, or modify an fanotify mark on a filesystem object.
/// If `dirfd` is `None`, `AT_FDCWD` is used.
///
/// Returns a Result containing either `()` on success or errno otherwise.
///
/// For more information, see [fanotify_mark(2)](https://man7.org/linux/man-pages/man7/fanotify_mark.2.html).
pub fn mark<P: ?Sized + NixPath>(
pub fn mark<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
&self,
flags: MarkFlags,
mask: MaskFlags,
dirfd: Option<RawFd>,
dirfd: Fd,
path: Option<&P>,
) -> Result<()> {
let res = crate::with_opt_nix_path(path, |p| unsafe {
@@ -334,7 +333,7 @@ impl Fanotify {
self.fd.as_raw_fd(),
flags.bits(),
mask.bits(),
at_rawfd(dirfd),
dirfd.as_fd().as_raw_fd(),
p,
)
})?;
@@ -362,7 +361,7 @@ impl Fanotify {
let mut events = Vec::new();
let mut offset = 0;
let nread = read(self.fd.as_raw_fd(), &mut buffer)?;
let nread = read(&self.fd, &mut buffer)?;
while (nread - offset) >= metadata_size {
let metadata = unsafe {
@@ -419,3 +418,29 @@ impl AsFd for Fanotify {
self.fd.as_fd()
}
}
impl AsRawFd for Fanotify {
fn as_raw_fd(&self) -> RawFd
{
self.fd.as_raw_fd()
}
}
impl From<Fanotify> for OwnedFd {
fn from(value: Fanotify) -> Self {
value.fd
}
}
impl Fanotify {
/// Constructs a `Fanotify` wrapping an existing `OwnedFd`.
///
/// # Safety
///
/// `OwnedFd` is a valid `Fanotify`.
pub unsafe fn from_owned_fd(fd: OwnedFd) -> Self {
Self {
fd
}
}
}

View File

@@ -201,7 +201,7 @@ impl Inotify {
let mut events = Vec::new();
let mut offset = 0;
let nread = read(self.fd.as_raw_fd(), &mut buffer)?;
let nread = read(&self.fd, &mut buffer)?;
while (nread - offset) >= header_size {
let event = unsafe {
@@ -239,6 +239,17 @@ impl Inotify {
Ok(events)
}
/// Constructs an `Inotify` wrapping an existing `OwnedFd`.
///
/// # Safety
///
/// `OwnedFd` is a valid `Inotify`.
pub unsafe fn from_owned_fd(fd: OwnedFd) -> Self {
Self {
fd
}
}
}
impl FromRawFd for Inotify {
@@ -254,3 +265,9 @@ impl AsFd for Inotify {
self.fd.as_fd()
}
}
impl From<Inotify> for OwnedFd {
fn from(value: Inotify) -> Self {
value.fd
}
}

View File

@@ -1,10 +1,20 @@
use cfg_if::cfg_if;
/// The datatype used for the ioctl number
#[cfg(any(target_os = "android", target_env = "musl"))]
#[cfg(any(
target_os = "android",
target_os = "fuchsia",
target_env = "musl",
target_env = "ohos"
))]
#[doc(hidden)]
pub type ioctl_num_type = ::libc::c_int;
#[cfg(not(any(target_os = "android", target_env = "musl")))]
#[cfg(not(any(
target_os = "android",
target_os = "fuchsia",
target_env = "musl",
target_env = "ohos"
)))]
#[doc(hidden)]
pub type ioctl_num_type = ::libc::c_ulong;
/// The datatype used for the 3rd argument

View File

@@ -227,11 +227,11 @@
//! ```
use cfg_if::cfg_if;
#[cfg(any(linux_android, target_os = "redox"))]
#[cfg(any(linux_android, target_os = "fuchsia", target_os = "redox"))]
#[macro_use]
mod linux;
#[cfg(any(linux_android, target_os = "redox"))]
#[cfg(any(linux_android, target_os = "fuchsia", target_os = "redox"))]
pub use self::linux::*;
#[cfg(any(bsd, solarish, target_os = "haiku",))]
@@ -655,8 +655,10 @@ macro_rules! ioctl_readwrite {
pub unsafe fn $name(fd: $crate::libc::c_int,
data: *mut $ty)
-> $crate::Result<$crate::libc::c_int> {
let ioty = $ioty;
let nr = $nr;
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_readwrite!($ioty, $nr, ::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::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data))
}
}
)

View File

@@ -4,12 +4,11 @@ use cfg_if::cfg_if;
use std::os::unix::io::{FromRawFd, OwnedFd, RawFd};
use crate::errno::Errno;
use crate::Result;
use std::ffi::CStr;
use crate::{NixPath, Result};
libc_bitflags!(
/// Options that change the behavior of [`memfd_create`].
pub struct MemFdCreateFlag: libc::c_uint {
pub struct MFdFlags: libc::c_uint {
/// Set the close-on-exec ([`FD_CLOEXEC`]) flag on the new file descriptor.
///
/// By default, the new file descriptor is set to remain open across an [`execve`]
@@ -38,6 +37,18 @@ libc_bitflags!(
/// [`memfd_create(2)`]: https://man7.org/linux/man-pages/man2/memfd_create.2.html
#[cfg(linux_android)]
MFD_HUGETLB;
/// Shift to get the huge page size.
#[cfg(target_env = "ohos")]
MFD_HUGE_SHIFT;
/// Mask to get the huge page size.
#[cfg(target_env = "ohos")]
MFD_HUGE_MASK;
/// hugetlb size of 64KB.
#[cfg(target_env = "ohos")]
MFD_HUGE_64KB;
/// hugetlb size of 512KB.
#[cfg(target_env = "ohos")]
MFD_HUGE_512KB;
/// Following are to be used with [`MFD_HUGETLB`], indicating the desired hugetlb size.
///
/// See also the hugetlb filesystem in [`memfd_create(2)`].
@@ -75,6 +86,10 @@ libc_bitflags!(
}
);
#[deprecated(since = "0.30.0", note = "Use `MFdFlags instead`")]
/// The deprecated MemFdCreateFlag type alias
pub type MemFdCreateFlag = MFdFlags;
/// Creates an anonymous file that lives in memory, and return a file-descriptor to it.
///
/// The file behaves like a regular file, and so can be modified, truncated, memory-mapped, and so on.
@@ -84,25 +99,31 @@ libc_bitflags!(
///
/// [`memfd_create(2)`]: https://man7.org/linux/man-pages/man2/memfd_create.2.html
#[inline] // Delays codegen, preventing linker errors with dylibs and --no-allow-shlib-undefined
pub fn memfd_create(name: &CStr, flags: MemFdCreateFlag) -> Result<OwnedFd> {
let res = unsafe {
cfg_if! {
pub fn memfd_create<P: NixPath + ?Sized>(
name: &P,
flags: MFdFlags,
) -> Result<OwnedFd> {
let res = name.with_nix_path(|cstr| {
unsafe {
cfg_if! {
if #[cfg(all(
// Android does not have a memfd_create symbol
not(target_os = "android"),
any(
target_os = "freebsd",
// If the OS is Linux, gnu and musl expose a memfd_create symbol but not uclibc
// If the OS is Linux, gnu/musl/ohos expose a memfd_create symbol but not uclibc
target_env = "gnu",
target_env = "musl",
target_env = "ohos"
)))]
{
libc::memfd_create(name.as_ptr(), flags.bits())
libc::memfd_create(cstr.as_ptr(), flags.bits())
} else {
libc::syscall(libc::SYS_memfd_create, name.as_ptr(), flags.bits())
libc::syscall(libc::SYS_memfd_create, cstr.as_ptr(), flags.bits())
}
}
};
}
})?;
Errno::result(res).map(|r| unsafe { OwnedFd::from_raw_fd(r as RawFd) })
}

View File

@@ -38,9 +38,13 @@ libc_bitflags! {
/// Additional parameters for [`mmap`].
pub struct MapFlags: c_int {
/// Compatibility flag. Ignored.
#[cfg(not(any(target_os = "solaris", target_os = "redox")))]
MAP_FILE;
/// Share this mapping. Mutually exclusive with `MAP_PRIVATE`.
MAP_SHARED;
/// Force mmap to check and fail on unknown flags. This also enables `MAP_SYNC`.
#[cfg(target_os = "linux")]
MAP_SHARED_VALIDATE;
/// Create a private copy-on-write mapping. Mutually exclusive with `MAP_SHARED`.
MAP_PRIVATE;
/// Place the mapping at exactly the address specified in `addr`.
@@ -60,6 +64,7 @@ libc_bitflags! {
#[cfg(any(all(linux_android,
any(target_arch = "x86", target_arch = "x86_64")),
all(target_os = "linux", target_env = "musl", any(target_arch = "x86", target_arch = "x86_64")),
all(target_os = "linux", target_env = "ohos", target_arch = "x86_64"),
all(target_os = "freebsd", target_pointer_width = "64")))]
MAP_32BIT;
/// Used for stacks; indicates to the kernel that the mapping should extend downward in memory.
@@ -77,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(freebsdlike, target_os = "aix", target_os = "hurd")))]
#[cfg(not(any(freebsdlike, target_os = "aix", target_os = "hurd", target_os = "redox")))]
MAP_NORESERVE;
/// Populate page tables for a mapping.
#[cfg(linux_android)]
@@ -140,8 +145,12 @@ libc_bitflags! {
#[cfg(any(freebsdlike, netbsdlike))]
MAP_HASSEMAPHORE;
/// Region grows down, like a stack.
#[cfg(any(linux_android, freebsdlike, target_os = "openbsd"))]
#[cfg(any(linux_android, freebsdlike, netbsdlike))]
MAP_STACK;
/// Do not write through the page caches, write directly to the file. Used for Direct Access (DAX) enabled file systems.
// Available on Linux glibc and musl, MIPS* target excluded.
#[cfg(all(target_os = "linux", not(any(target_arch = "mips", target_arch = "mips64", target_arch = "mips32r6", target_arch = "mips64r6")), not(target_env = "uclibc")))]
MAP_SYNC;
/// Pages in this mapping are not retained in the kernel's memory cache.
#[cfg(apple_targets)]
MAP_NOCACHE;
@@ -197,6 +206,10 @@ libc_bitflags! {
/// Place the mapping at exactly the address specified in `new_address`.
#[cfg(target_os = "linux")]
MREMAP_FIXED;
/// Works in conjunction with `MREMAP_MAYMOVE` but does not unmap `old_address`.
/// Note that, in this case, `old_size` and `new_size` must be the same.
#[cfg(target_os = "linux")]
MREMAP_DONTUNMAP;
/// Place the mapping at exactly the address specified in `new_address`.
#[cfg(target_os = "netbsd")]
MAP_FIXED;
@@ -268,7 +281,7 @@ libc_enum! {
#[cfg(linux_android)]
MADV_DODUMP,
/// Specify that the application no longer needs the pages in the given range.
#[cfg(not(any(target_os = "aix", target_os = "hurd")))]
#[cfg(not(any(target_os = "aix", target_os = "hurd", target_os = "cygwin", target_os = "redox")))]
MADV_FREE,
/// Request that the system not flush the current range to disk unless it needs to.
#[cfg(freebsdlike)]
@@ -304,6 +317,25 @@ libc_enum! {
#[cfg(apple_targets)]
#[allow(missing_docs)]
MADV_CAN_REUSE,
/// Reclaim the address range when applicable.
#[cfg(linux_android)]
MADV_PAGEOUT,
/// Deactivate the address range when applicable.
#[cfg(linux_android)]
MADV_COLD,
/// After fork, the adress range is zero filled.
#[cfg(linux_android)]
MADV_WIPEONFORK,
/// Undo `MADV_WIPEONFORK` when it applied.
#[cfg(linux_android)]
MADV_KEEPONFORK,
/// Pre-load the address range for reading to reduce page-fault latency.
#[cfg(linux_android)]
MADV_POPULATE_READ,
/// Pre-fault the address range for writing to reduce page-fault
/// latency on subsequent writes.
#[cfg(linux_android)]
MADV_POPULATE_WRITE,
}
}
@@ -325,7 +357,7 @@ libc_bitflags! {
}
}
#[cfg(not(target_os = "haiku"))]
#[cfg(not(any(target_os = "haiku", target_os = "cygwin", target_os = "redox")))]
libc_bitflags! {
/// Flags for [`mlockall`].
pub struct MlockAllFlags: c_int {
@@ -368,7 +400,7 @@ pub unsafe fn munlock(addr: NonNull<c_void>, length: size_t) -> Result<()> {
/// Locked pages never move to the swap area. For more information, see [`mlockall(2)`].
///
/// [`mlockall(2)`]: https://man7.org/linux/man-pages/man2/mlockall.2.html
#[cfg(not(target_os = "haiku"))]
#[cfg(not(any(target_os = "haiku", target_os = "cygwin", target_os = "redox")))]
pub fn mlockall(flags: MlockAllFlags) -> Result<()> {
unsafe { Errno::result(libc::mlockall(flags.bits())) }.map(drop)
}
@@ -378,7 +410,7 @@ pub fn mlockall(flags: MlockAllFlags) -> Result<()> {
/// For more information, see [`munlockall(2)`].
///
/// [`munlockall(2)`]: https://man7.org/linux/man-pages/man2/munlockall.2.html
#[cfg(not(target_os = "haiku"))]
#[cfg(not(any(target_os = "haiku", target_os = "cygwin", target_os = "redox")))]
pub fn munlockall() -> Result<()> {
unsafe { Errno::result(libc::munlockall()) }.map(drop)
}
@@ -407,7 +439,7 @@ pub unsafe fn mmap<F: AsFd>(
libc::mmap(ptr, length.into(), prot.bits(), flags.bits(), fd, offset)
};
if ret == libc::MAP_FAILED {
if std::ptr::eq(ret, libc::MAP_FAILED) {
Err(Errno::last())
} else {
// SAFETY: `libc::mmap` returns a valid non-null pointer or `libc::MAP_FAILED`, thus `ret`
@@ -439,7 +471,7 @@ pub unsafe fn mmap_anonymous(
libc::mmap(ptr, length.into(), prot.bits(), flags.bits(), -1, 0)
};
if ret == libc::MAP_FAILED {
if std::ptr::eq(ret, libc::MAP_FAILED) {
Err(Errno::last())
} else {
// SAFETY: `libc::mmap` returns a valid non-null pointer or `libc::MAP_FAILED`, thus `ret`
@@ -488,7 +520,7 @@ pub unsafe fn mremap(
)
};
if ret == libc::MAP_FAILED {
if std::ptr::eq(ret, libc::MAP_FAILED) {
Err(Errno::last())
} else {
// SAFETY: `libc::mremap` returns a valid non-null pointer or `libc::MAP_FAILED`, thus `ret`

View File

@@ -1,7 +1,10 @@
//! Mostly platform-specific functionality
#[cfg(any(
freebsdlike,
all(target_os = "linux", not(target_env = "uclibc")),
all(
target_os = "linux",
not(any(target_env = "uclibc", target_env = "ohos"))
),
apple_targets,
target_os = "netbsd"
))]
@@ -20,8 +23,8 @@ feature! {
#[cfg(bsd)]
pub mod event;
/// Event file descriptor.
#[cfg(any(linux_android, target_os = "freebsd"))]
#[allow(missing_docs)]
pub mod eventfd;
}
@@ -31,7 +34,13 @@ feature! {
pub mod fanotify;
}
#[cfg(any(bsd, linux_android, target_os = "redox", solarish))]
#[cfg(any(
bsd,
linux_android,
solarish,
target_os = "fuchsia",
target_os = "redox",
))]
#[cfg(feature = "ioctl")]
#[cfg_attr(docsrs, doc(cfg(feature = "ioctl")))]
#[macro_use]
@@ -43,7 +52,6 @@ feature! {
pub mod memfd;
}
#[cfg(not(target_os = "redox"))]
feature! {
#![feature = "mman"]
pub mod mman;
@@ -128,7 +136,13 @@ feature! {
pub mod stat;
}
#[cfg(any(linux_android, freebsdlike, apple_targets, target_os = "openbsd"))]
#[cfg(any(
linux_android,
freebsdlike,
apple_targets,
target_os = "openbsd",
target_os = "cygwin"
))]
feature! {
#![feature = "fs"]
pub mod statfs;

View File

@@ -20,7 +20,7 @@ libc_bitflags! {
/// [`mmap(2)`]: https://man7.org/linux/man-pages/man2/mmap.2.html
ADDR_LIMIT_3GB;
/// User-space function pointers to signal handlers point to descriptors.
#[cfg(not(any(target_env = "musl", target_env = "uclibc")))]
#[cfg(not(any(target_env = "musl", target_env = "uclibc", target_env = "ohos")))]
FDPIC_FUNCPTRS;
/// Map page 0 as read-only.
MMAP_PAGE_ZERO;
@@ -41,7 +41,7 @@ libc_bitflags! {
/// version number.
///
/// [`uname(2)`]: https://man7.org/linux/man-pages/man2/uname.2.html
#[cfg(not(any(target_env = "musl", target_env = "uclibc")))]
#[cfg(not(any(target_env = "musl", target_env = "uclibc", target_env = "ohos")))]
UNAME26;
/// No effects.
WHOLE_SECONDS;

View File

@@ -166,7 +166,7 @@ pub fn get_name() -> Result<CString> {
/// 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<()> {
pub fn set_timerslack(ns: c_ulong) -> Result<()> {
let res = unsafe { libc::prctl(libc::PR_SET_TIMERSLACK, ns, 0, 0, 0) };
Errno::result(res).map(drop)

View File

@@ -14,11 +14,10 @@ pub type AddressType = *mut ::libc::c_void;
target_os = "linux",
any(
all(
target_arch = "x86_64",
any(target_arch = "x86_64", target_arch = "aarch64"),
any(target_env = "gnu", target_env = "musl")
),
all(target_arch = "x86", target_env = "gnu"),
all(target_arch = "aarch64", target_env = "gnu"),
all(target_arch = "riscv64", target_env = "gnu"),
),
))]
@@ -37,8 +36,8 @@ cfg_if! {
}
libc_enum! {
#[cfg_attr(not(any(target_env = "musl", target_env = "uclibc", target_os = "android")), repr(u32))]
#[cfg_attr(any(target_env = "musl", target_env = "uclibc", target_os = "android"), repr(i32))]
#[cfg_attr(not(any(target_env = "musl", target_env = "uclibc", target_os = "android", target_env = "ohos")), repr(u32))]
#[cfg_attr(any(target_env = "musl", target_env = "uclibc", target_os = "android", target_env = "ohos"), repr(i32))]
/// Ptrace Request enum defining the action to be taken.
#[non_exhaustive]
pub enum Request {
@@ -54,6 +53,7 @@ libc_enum! {
PTRACE_SINGLESTEP,
#[cfg(any(all(target_os = "android", target_pointer_width = "32"),
all(target_os = "linux", any(target_env = "musl",
target_env = "ohos",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
@@ -63,6 +63,7 @@ libc_enum! {
PTRACE_GETREGS,
#[cfg(any(all(target_os = "android", target_pointer_width = "32"),
all(target_os = "linux", any(target_env = "musl",
target_env = "ohos",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
@@ -72,6 +73,7 @@ libc_enum! {
PTRACE_SETREGS,
#[cfg(any(all(target_os = "android", target_pointer_width = "32"),
all(target_os = "linux", any(target_env = "musl",
target_env = "ohos",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
@@ -81,6 +83,7 @@ libc_enum! {
PTRACE_GETFPREGS,
#[cfg(any(all(target_os = "android", target_pointer_width = "32"),
all(target_os = "linux", any(target_env = "musl",
target_env = "ohos",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
@@ -91,6 +94,7 @@ libc_enum! {
PTRACE_ATTACH,
PTRACE_DETACH,
#[cfg(all(target_os = "linux", any(target_env = "musl",
target_env = "ohos",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
@@ -99,6 +103,7 @@ libc_enum! {
target_arch = "x86_64")))]
PTRACE_GETFPXREGS,
#[cfg(all(target_os = "linux", any(target_env = "musl",
target_env = "ohos",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
@@ -141,6 +146,8 @@ libc_enum! {
#[cfg(all(target_os = "linux", target_env = "gnu",
any(target_arch = "x86", target_arch = "x86_64")))]
PTRACE_SYSEMU_SINGLESTEP,
#[cfg(all(target_os = "linux", target_env = "gnu"))]
PTRACE_GET_SYSCALL_INFO,
}
}
@@ -174,13 +181,21 @@ libc_enum! {
#[cfg(all(
target_os = "linux",
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64",
)
all(
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64",
)
),
all(
target_env = "musl",
target_arch = "aarch64",
)
),
))]
libc_enum! {
#[repr(i32)]
@@ -197,13 +212,21 @@ libc_enum! {
#[cfg(all(
target_os = "linux",
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64",
)
all(
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64",
)
),
all(
target_env = "musl",
target_arch = "aarch64",
)
),
))]
/// Represents register set areas, such as general-purpose registers or
/// floating-point registers.
@@ -220,15 +243,24 @@ pub unsafe trait RegisterSet {
type Regs;
}
#[cfg(all(
target_os = "linux",
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64",
)
all(
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64",
)
),
all(
target_env = "musl",
target_arch = "aarch64",
)
),
))]
/// Register sets used in [`getregset`] and [`setregset`]
pub mod regset {
@@ -334,8 +366,13 @@ pub fn getregs(pid: Pid) -> Result<user_regs_struct> {
/// [ptrace(2)]: https://www.man7.org/linux/man-pages/man2/ptrace.2.html
#[cfg(all(
target_os = "linux",
target_env = "gnu",
any(target_arch = "aarch64", target_arch = "riscv64")
any(
all(
target_arch = "aarch64",
any(target_env = "gnu", target_env = "musl")
),
all(target_arch = "riscv64", target_env = "gnu")
)
))]
pub fn getregs(pid: Pid) -> Result<user_regs_struct> {
getregset::<regset::NT_PRSTATUS>(pid)
@@ -344,12 +381,17 @@ pub fn getregs(pid: Pid) -> Result<user_regs_struct> {
/// Get a particular set of user registers, as with `ptrace(PTRACE_GETREGSET, ...)`
#[cfg(all(
target_os = "linux",
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64",
all(
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64"
)
),
all(target_env = "musl", target_arch = "aarch64")
)
))]
pub fn getregset<S: RegisterSet>(pid: Pid) -> Result<S::Regs> {
@@ -408,8 +450,13 @@ pub fn setregs(pid: Pid, regs: user_regs_struct) -> Result<()> {
/// [ptrace(2)]: https://www.man7.org/linux/man-pages/man2/ptrace.2.html
#[cfg(all(
target_os = "linux",
target_env = "gnu",
any(target_arch = "aarch64", target_arch = "riscv64")
any(
all(
target_env = "gnu",
any(target_arch = "aarch64", target_arch = "riscv64")
),
all(target_env = "musl", target_arch = "aarch64")
)
))]
pub fn setregs(pid: Pid, regs: user_regs_struct) -> Result<()> {
setregset::<regset::NT_PRSTATUS>(pid, regs)
@@ -418,12 +465,17 @@ pub fn setregs(pid: Pid, regs: user_regs_struct) -> Result<()> {
/// Set a particular set of user registers, as with `ptrace(PTRACE_SETREGSET, ...)`
#[cfg(all(
target_os = "linux",
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64",
all(
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64"
)
),
all(target_env = "musl", target_arch = "aarch64")
)
))]
pub fn setregset<S: RegisterSet>(pid: Pid, mut regs: S::Regs) -> Result<()> {
@@ -517,6 +569,13 @@ pub fn setsiginfo(pid: Pid, sig: &siginfo_t) -> Result<()> {
}
}
/// Get the informations of the syscall that caused the stop, as with
/// `ptrace(PTRACE_GET_SYSCALL_INFO, ...`.
#[cfg(all(target_os = "linux", target_env = "gnu"))]
pub fn syscall_info(pid: Pid) -> Result<libc::ptrace_syscall_info> {
ptrace_get_data::<libc::ptrace_syscall_info>(Request::PTRACE_GET_SYSCALL_INFO, pid)
}
/// Sets the process as traceable, as with `ptrace(PTRACE_TRACEME, ...)`
///
/// Indicates that this process is to be traced by its parent.

View File

@@ -19,7 +19,8 @@ cfg_if! {
bsd,
target_os = "android",
target_os = "aix",
all(target_os = "linux", not(target_env = "gnu"))
all(target_os = "linux", not(target_env = "gnu")),
target_os = "cygwin"
))]{
use libc::rlimit;
}
@@ -49,7 +50,8 @@ libc_enum! {
bsd,
target_os = "android",
target_os = "aix",
all(target_os = "linux", not(any(target_env = "gnu", target_env = "uclibc")))
all(target_os = "linux", not(any(target_env = "gnu", target_env = "uclibc"))),
target_os = "cygwin"
), repr(i32))]
#[non_exhaustive]
pub enum Resource {

View File

@@ -115,7 +115,7 @@ impl<'fd> FdSet<'fd> {
}
}
impl<'fd> Default for FdSet<'fd> {
impl Default for FdSet<'_> {
fn default() -> Self {
Self::new()
}
@@ -128,7 +128,7 @@ pub struct Fds<'a, 'fd> {
range: Range<usize>,
}
impl<'a, 'fd> Iterator for Fds<'a, 'fd> {
impl<'fd> Iterator for Fds<'_, 'fd> {
type Item = BorrowedFd<'fd>;
fn next(&mut self) -> Option<Self::Item> {
@@ -148,7 +148,7 @@ impl<'a, 'fd> Iterator for Fds<'a, 'fd> {
}
}
impl<'a, 'fd> DoubleEndedIterator for Fds<'a, 'fd> {
impl<'fd> DoubleEndedIterator for Fds<'_, 'fd> {
#[inline]
fn next_back(&mut self) -> Option<BorrowedFd<'fd>> {
while let Some(i) = self.range.next_back() {
@@ -161,7 +161,7 @@ impl<'a, 'fd> DoubleEndedIterator for Fds<'a, 'fd> {
}
}
impl<'a, 'fd> FusedIterator for Fds<'a, 'fd> {}
impl FusedIterator for Fds<'_, '_> {}
/// Monitors file descriptors for readiness
///

View File

@@ -1,6 +1,3 @@
// Portions of this file are Copyright 2014 The Rust Project Developers.
// See https://www.rust-lang.org/policies/licenses.
//! Operating system signals.
use crate::errno::Errno;
@@ -10,8 +7,6 @@ use std::fmt;
use std::hash::{Hash, Hasher};
use std::mem;
use std::ops::BitOr;
#[cfg(freebsdlike)]
use std::os::unix::io::RawFd;
use std::ptr;
use std::str::FromStr;
@@ -72,6 +67,7 @@ libc_enum! {
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "sparc",
target_arch = "sparc64"))))]
SIGSTKFLT,
/// To parent on child stop or exit
@@ -114,7 +110,8 @@ libc_enum! {
SIGEMT,
#[cfg(not(any(linux_android, target_os = "emscripten",
target_os = "fuchsia", target_os = "redox",
target_os = "haiku", target_os = "aix")))]
target_os = "haiku", target_os = "aix",
target_os = "solaris", target_os = "cygwin")))]
/// Information request
SIGINFO,
}
@@ -152,6 +149,7 @@ impl FromStr for Signal {
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "sparc",
target_arch = "sparc64"
))
))]
@@ -191,7 +189,9 @@ impl FromStr for Signal {
target_os = "fuchsia",
target_os = "redox",
target_os = "aix",
target_os = "haiku"
target_os = "haiku",
target_os = "solaris",
target_os = "cygwin"
)))]
"SIGINFO" => Signal::SIGINFO,
_ => return Err(Errno::EINVAL),
@@ -234,6 +234,7 @@ impl Signal {
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "sparc",
target_arch = "sparc64"
))
))]
@@ -274,7 +275,9 @@ impl Signal {
target_os = "fuchsia",
target_os = "redox",
target_os = "aix",
target_os = "haiku"
target_os = "haiku",
target_os = "solaris",
target_os = "cygwin"
)))]
Signal::SIGINFO => "SIGINFO",
}
@@ -321,6 +324,7 @@ const SIGNALS: [Signal; 28] = [
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "sparc",
target_arch = "sparc64"
))
))]
@@ -338,6 +342,7 @@ const SIGNALS: [Signal; 31] = [
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "sparc",
target_arch = "sparc64"
)
))]
@@ -356,13 +361,23 @@ const SIGNALS: [Signal; 30] = [
SIGURG, SIGPOLL, SIGIO, SIGSTOP, SIGTSTP, SIGCONT, SIGTTIN, SIGTTOU,
SIGVTALRM, SIGPROF, SIGXCPU, SIGXFSZ, SIGTRAP,
];
#[cfg(any(target_os = "solaris", target_os = "cygwin"))]
#[cfg(feature = "signal")]
const SIGNALS: [Signal; 30] = [
SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGBUS, SIGFPE, SIGKILL,
SIGUSR1, SIGSEGV, SIGUSR2, SIGPIPE, SIGALRM, SIGTERM, SIGCHLD, SIGCONT,
SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGXCPU, SIGXFSZ, SIGVTALRM,
SIGPROF, SIGWINCH, SIGIO, SIGSYS, SIGEMT,
];
#[cfg(not(any(
linux_android,
target_os = "fuchsia",
target_os = "emscripten",
target_os = "aix",
target_os = "redox",
target_os = "haiku"
target_os = "haiku",
target_os = "solaris",
target_os = "cygwin"
)))]
#[cfg(feature = "signal")]
const SIGNALS: [Signal; 31] = [
@@ -745,11 +760,11 @@ pub enum SigHandler {
/// Request that the signal be ignored.
SigIgn,
/// Use the given signal-catching function, which takes in the signal.
Handler(extern fn(libc::c_int)),
Handler(extern "C" fn(libc::c_int)),
/// Use the given signal-catching function, which takes in the signal, information about how
/// the signal was generated, and a pointer to the threads `ucontext_t`.
#[cfg(not(target_os = "redox"))]
SigAction(extern fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void))
SigAction(extern "C" fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void))
}
/// Action to take on receipt of a signal. Corresponds to `sigaction`.
@@ -772,27 +787,14 @@ 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) {
unsafe {
(*p).sa_sigaction = match handler {
SigHandler::SigDfl => libc::SIG_DFL,
SigHandler::SigIgn => libc::SIG_IGN,
SigHandler::Handler(f) => f as *const extern fn(libc::c_int) as usize,
SigHandler::Handler(f) => f as *const extern "C" fn(libc::c_int) as usize,
#[cfg(not(target_os = "redox"))]
SigHandler::SigAction(f) => f as *const extern fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void) as usize,
};
}
}
#[cfg(target_os = "aix")]
unsafe fn install_sig(p: *mut libc::sigaction, handler: SigHandler) {
unsafe {
(*p).sa_union.__su_sigaction = match handler {
SigHandler::SigDfl => unsafe { mem::transmute::<usize, extern "C" fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void)>(libc::SIG_DFL) },
SigHandler::SigIgn => unsafe { mem::transmute::<usize, extern "C" fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void)>(libc::SIG_IGN) },
SigHandler::Handler(f) => unsafe { mem::transmute::<extern "C" fn(i32), extern "C" fn(i32, *mut libc::siginfo_t, *mut libc::c_void)>(f) },
SigHandler::SigAction(f) => f,
SigHandler::SigAction(f) => f as *const extern "C" fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void) as usize,
};
}
}
@@ -824,7 +826,6 @@ 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,
@@ -840,9 +841,9 @@ impl SigAction {
// ensured that it is correctly initialized.
unsafe{
*(&p as *const usize
as *const extern fn(_, _, _))
as *const extern "C" fn(_, _, _))
}
as extern fn(_, _, _)),
as extern "C" fn(_, _, _)),
p => SigHandler::Handler(
// Safe for one of two reasons:
// * The SigHandler was created by SigHandler::new, in which
@@ -852,29 +853,9 @@ impl SigAction {
// ensured that it is correctly initialized.
unsafe{
*(&p as *const usize
as *const extern fn(libc::c_int))
as *const extern "C" fn(libc::c_int))
}
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)),
}
as extern "C" fn(libc::c_int)),
}
}
}
@@ -935,7 +916,7 @@ pub unsafe fn sigaction(signal: Signal, sigaction: &SigAction) -> Result<SigActi
/// # use nix::sys::signal::{self, Signal, SigHandler};
/// static SIGNALED: AtomicBool = AtomicBool::new(false);
///
/// extern fn handle_sigint(signal: libc::c_int) {
/// extern "C" fn handle_sigint(signal: libc::c_int) {
/// let signal = Signal::try_from(signal).unwrap();
/// SIGNALED.store(signal == Signal::SIGINT, Ordering::Relaxed);
/// }
@@ -972,7 +953,7 @@ pub unsafe fn signal(signal: Signal, handler: SigHandler) -> Result<SigHandler>
libc::SIG_DFL => SigHandler::SigDfl,
libc::SIG_IGN => SigHandler::SigIgn,
p => SigHandler::Handler(
unsafe { *(&p as *const usize as *const extern fn(libc::c_int)) } as extern fn(libc::c_int)),
unsafe { *(&p as *const usize as *const extern "C" fn(libc::c_int)) } as extern "C" fn(libc::c_int)),
}
})
}
@@ -1114,8 +1095,8 @@ pub type type_of_thread_id = libc::pid_t;
// as a pointer, because neither libc nor the kernel ever dereference it. nix
// therefore presents it as an intptr_t, which is how kevent uses it.
#[cfg(not(any(target_os = "fuchsia", target_os = "hurd", target_os = "openbsd", target_os = "redox")))]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum SigevNotify {
#[derive(Clone, Copy, Debug)]
pub enum SigevNotify<'fd> {
/// No notification will be delivered
SigevNone,
/// Notify by delivering a signal to the process.
@@ -1131,7 +1112,7 @@ pub enum SigevNotify {
#[cfg(freebsdlike)]
SigevKevent {
/// File descriptor of the kqueue to notify.
kq: RawFd,
kq: std::os::fd::BorrowedFd<'fd>,
/// Will be contained in the kevent's `udata` field.
udata: libc::intptr_t
},
@@ -1140,11 +1121,11 @@ pub enum SigevNotify {
#[cfg(feature = "event")]
SigevKeventFlags {
/// File descriptor of the kqueue to notify.
kq: RawFd,
kq: std::os::fd::BorrowedFd<'fd>,
/// Will be contained in the kevent's `udata` field.
udata: libc::intptr_t,
/// Flags that will be set on the delivered event. See `kevent(2)`.
flags: crate::sys::event::EventFlag
flags: crate::sys::event::EvFlags
},
/// Notify by delivering a signal to a thread.
#[cfg(any(
@@ -1161,6 +1142,14 @@ pub enum SigevNotify {
/// structure of the queued signal.
si_value: libc::intptr_t
},
/// A helper variant to resolve the unused parameter (`'fd`) problem on
/// platforms other than FreeBSD and DragonFlyBSD.
///
/// This variant can never be constructed due to the usage of an enum with 0
/// variants.
#[doc(hidden)]
#[cfg(not(freebsdlike))]
_Unreachable(&'fd std::convert::Infallible),
}
}
@@ -1337,15 +1326,19 @@ mod sigevent {
},
#[cfg(freebsdlike)]
SigevNotify::SigevKevent{kq, udata} => {
use std::os::fd::AsRawFd;
sev.sigev_notify = libc::SIGEV_KEVENT;
sev.sigev_signo = kq;
sev.sigev_signo = kq.as_raw_fd();
sev.sigev_value.sival_ptr = udata as *mut libc::c_void;
},
#[cfg(target_os = "freebsd")]
#[cfg(feature = "event")]
SigevNotify::SigevKeventFlags{kq, udata, flags} => {
use std::os::fd::AsRawFd;
sev.sigev_notify = libc::SIGEV_KEVENT;
sev.sigev_signo = kq;
sev.sigev_signo = kq.as_raw_fd();
sev.sigev_value.sival_ptr = udata as *mut libc::c_void;
sev._sigev_un._kevent_flags = flags.bits();
},
@@ -1363,6 +1356,8 @@ mod sigevent {
sev.sigev_value.sival_ptr = si_value as *mut libc::c_void;
sev.sigev_notify_thread_id = thread_id;
}
#[cfg(not(freebsdlike))]
SigevNotify::_Unreachable(_) => unreachable!("This variant could never be constructed")
}
SigEvent{sigevent: sev}
}
@@ -1398,7 +1393,7 @@ mod sigevent {
}
}
impl<'a> From<&'a libc::sigevent> for SigEvent {
impl From<&'_ libc::sigevent> for SigEvent {
#[cfg(target_os = "freebsd")]
fn from(sigevent: &libc::sigevent) -> Self {
// Safe because they're really the same structure. See

View File

@@ -18,6 +18,8 @@
use crate::errno::Errno;
pub use crate::sys::signal::{self, SigSet};
use crate::Result;
/// Information of a received signal, the return type of [`SignalFd::read_signal()`].
pub use libc::signalfd_siginfo as siginfo;
use std::mem;
@@ -125,6 +127,15 @@ impl SignalFd {
}
}
/// Constructs a `SignalFd` wrapping an existing `OwnedFd`.
///
/// # Safety
///
/// `OwnedFd` is a valid `SignalFd`.
pub unsafe fn from_owned_fd(fd: OwnedFd) -> Self {
Self(fd)
}
fn update(&self, mask: &SigSet, flags: SfdFlags) -> Result<()> {
let raw_fd = self.0.as_raw_fd();
unsafe {
@@ -145,6 +156,12 @@ impl AsRawFd for SignalFd {
}
}
impl From<SignalFd> for OwnedFd {
fn from(value: SignalFd) -> Self {
value.0
}
}
impl Iterator for SignalFd {
type Item = siginfo;

View File

@@ -66,7 +66,7 @@ pub enum AddressFamily {
#[cfg(linux_android)]
Netlink = libc::AF_NETLINK,
/// Kernel interface for interacting with the routing table
#[cfg(not(any(linux_android, target_os = "redox")))]
#[cfg(not(any(linux_android, target_os = "redox", target_os = "cygwin")))]
Route = libc::PF_ROUTE,
/// Low level packet interface (see [`packet(7)`](https://man7.org/linux/man-pages/man7/packet.7.html))
#[cfg(any(linux_android, solarish, target_os = "fuchsia"))]
@@ -78,7 +78,7 @@ pub enum AddressFamily {
#[cfg(linux_android)]
Ax25 = libc::AF_AX25,
/// IPX - Novell protocols
#[cfg(not(any(target_os = "aix", target_os = "redox")))]
#[cfg(not(any(target_os = "aix", target_os = "redox", target_os = "cygwin")))]
Ipx = libc::AF_IPX,
/// AppleTalk
#[cfg(not(target_os = "redox"))]
@@ -164,6 +164,7 @@ pub enum AddressFamily {
apple_targets,
target_os = "hurd",
target_os = "redox",
target_os = "cygwin",
)))]
Bluetooth = libc::AF_BLUETOOTH,
/// IUCV (inter-user communication vehicle) z/VM protocol for
@@ -180,6 +181,7 @@ pub enum AddressFamily {
target_os = "haiku",
target_os = "hurd",
target_os = "redox",
target_os = "cygwin",
)))]
Isdn = libc::AF_ISDN,
/// Nokia cellular modem IPC/RPC interface
@@ -263,7 +265,7 @@ impl AddressFamily {
libc::AF_NETLINK => Some(AddressFamily::Netlink),
#[cfg(apple_targets)]
libc::AF_SYSTEM => Some(AddressFamily::System),
#[cfg(not(any(linux_android, target_os = "redox")))]
#[cfg(not(any(linux_android, target_os = "redox", target_os = "cygwin")))]
libc::PF_ROUTE => Some(AddressFamily::Route),
#[cfg(linux_android)]
libc::AF_PACKET => Some(AddressFamily::Packet),
@@ -446,6 +448,7 @@ impl UnixAddr {
target_os = "fuchsia",
solarish,
target_os = "redox",
target_os = "cygwin",
))]
{
UnixAddr { sun, sun_len }
@@ -510,6 +513,7 @@ impl UnixAddr {
target_os = "fuchsia",
solarish,
target_os = "redox",
target_os = "cygwin",
))]
{
self.sun_len
@@ -526,7 +530,8 @@ impl SockaddrLike for UnixAddr {
linux_android,
target_os = "fuchsia",
solarish,
target_os = "redox"
target_os = "redox",
target_os = "cygwin",
))]
fn len(&self) -> libc::socklen_t {
self.sun_len.into()
@@ -556,6 +561,7 @@ impl SockaddrLike for UnixAddr {
target_os = "fuchsia",
solarish,
target_os = "redox",
target_os = "cygwin",
))] {
let su_len = len.unwrap_or(
mem::size_of::<libc::sockaddr_un>() as libc::socklen_t
@@ -655,15 +661,13 @@ pub trait SockaddrLike: private::SockaddrLikePriv {
///
/// # Arguments
///
/// - `addr`: raw pointer to something that can be cast to a
/// `libc::sockaddr`. For example, `libc::sockaddr_in`,
/// `libc::sockaddr_in6`, etc.
/// - `len`: For fixed-width types like `sockaddr_in`, it will be
/// validated if present and ignored if not. For variable-width
/// types it is required and must be the total length of valid
/// data. For example, if `addr` points to a
/// named `sockaddr_un`, then `len` must be the length of the
/// structure up to but not including the trailing NUL.
/// - `addr`: raw pointer to something that can be cast to a `libc::sockaddr`.
/// For example, `libc::sockaddr_in`, `libc::sockaddr_in6`, etc.
/// - `len`: For fixed-width types like `sockaddr_in`, it will be validated
/// if present and ignored if not. For variable-width types it is required
/// and must be the total length of valid data. For example, if `addr`
/// points to a named `sockaddr_un`, then `len` must be the length of the
/// structure up to but not including the trailing NUL.
///
/// # Safety
///
@@ -919,6 +923,13 @@ impl From<SockaddrIn> for net::SocketAddrV4 {
}
}
#[cfg(feature = "net")]
impl From<SockaddrIn> for net::SocketAddr {
fn from(addr: SockaddrIn) -> Self {
net::SocketAddr::from(net::SocketAddrV4::from(addr))
}
}
#[cfg(feature = "net")]
impl From<SockaddrIn> for libc::sockaddr_in {
fn from(sin: SockaddrIn) -> libc::sockaddr_in {
@@ -1075,6 +1086,13 @@ impl From<SockaddrIn6> for net::SocketAddrV6 {
}
}
#[cfg(feature = "net")]
impl From<SockaddrIn6> for net::SocketAddr {
fn from(addr: SockaddrIn6) -> Self {
net::SocketAddr::from(net::SocketAddrV6::from(addr))
}
}
#[cfg(feature = "net")]
impl std::str::FromStr for SockaddrIn6 {
type Err = net::AddrParseError;
@@ -1110,7 +1128,7 @@ pub union SockaddrStorage {
alg: AlgAddr,
#[cfg(all(
feature = "net",
not(any(target_os = "hurd", target_os = "redox"))
not(any(target_os = "hurd", target_os = "redox", target_os = "cygwin"))
))]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
dl: LinkAddr,
@@ -1154,6 +1172,7 @@ impl SockaddrLike for SockaddrStorage {
linux_android,
target_os = "fuchsia",
solarish,
target_os = "cygwin",
))]
if i32::from(ss.ss_family) == libc::AF_UNIX {
// Safe because we UnixAddr is strictly smaller than
@@ -1210,7 +1229,7 @@ impl SockaddrLike for SockaddrStorage {
}
}
#[cfg(any(linux_android, target_os = "fuchsia", solarish))]
#[cfg(any(linux_android, target_os = "fuchsia", solarish, target_os = "cygwin"))]
fn len(&self) -> libc::socklen_t {
match self.as_unix_addr() {
// The UnixAddr type knows its own length
@@ -1272,6 +1291,7 @@ impl SockaddrStorage {
if #[cfg(any(linux_android,
target_os = "fuchsia",
solarish,
target_os = "cygwin",
))]
{
let p = unsafe{ &self.ss as *const libc::sockaddr_storage };
@@ -1301,6 +1321,7 @@ impl SockaddrStorage {
if #[cfg(any(linux_android,
target_os = "fuchsia",
solarish,
target_os = "cygwin",
))]
{
let p = unsafe{ &self.ss as *const libc::sockaddr_storage };
@@ -2176,7 +2197,7 @@ mod tests {
}
}
#[cfg(not(any(target_os = "hurd", target_os = "redox")))]
#[cfg(not(any(target_os = "hurd", target_os = "redox", target_os = "cygwin")))]
#[allow(clippy::cast_ptr_alignment)]
mod link {
#[cfg(any(apple_targets, solarish))]
@@ -2200,7 +2221,7 @@ mod tests {
sdl_slen: 0,
..unsafe { mem::zeroed() }
});
format!("{la}");
let _ = format!("{la}");
}
#[cfg(all(
@@ -2407,7 +2428,7 @@ mod tests {
let addr = UnixAddr::new_abstract(name.as_bytes()).unwrap();
let sun_path1 =
unsafe { &(*addr.as_ptr()).sun_path[..addr.path_len()] };
unsafe { &(&(*addr.as_ptr()).sun_path)[..addr.path_len()] };
let sun_path2 = [
0, 110, 105, 120, 0, 97, 98, 115, 116, 114, 97, 99, 116, 0,
116, 101, 115, 116,

View File

@@ -39,10 +39,22 @@ pub use self::addr::{SockaddrLike, SockaddrStorage};
pub use self::addr::{AddressFamily, UnixAddr};
#[cfg(not(solarish))]
pub use self::addr::{AddressFamily, UnixAddr};
#[cfg(not(any(solarish, target_os = "haiku", target_os = "hurd", target_os = "redox")))]
#[cfg(not(any(
solarish,
target_os = "haiku",
target_os = "hurd",
target_os = "redox",
target_os = "cygwin",
)))]
#[cfg(feature = "net")]
pub use self::addr::{LinkAddr, SockaddrIn, SockaddrIn6};
#[cfg(any(solarish, target_os = "haiku", target_os = "hurd", target_os = "redox"))]
#[cfg(any(
solarish,
target_os = "haiku",
target_os = "hurd",
target_os = "redox",
target_os = "cygwin",
))]
#[cfg(feature = "net")]
pub use self::addr::{SockaddrIn, SockaddrIn6};
@@ -199,10 +211,28 @@ pub enum SockProtocol {
// The protocol number is fed into the socket syscall in network byte order.
#[cfg(linux_android)]
EthAll = (libc::ETH_P_ALL as u16).to_be() as i32,
#[cfg(linux_android)]
/// Packet filter on loopback traffic
EthLoop = (libc::ETH_P_LOOP as u16).to_be() as i32,
/// Packet filter on IPv4 traffic
#[cfg(linux_android)]
#[cfg(target_endian = "big")]
EthIp = libc::ETH_P_IP,
/// Packet filter on IPv6 traffic
#[cfg(linux_android)]
EthIpv6 = (libc::ETH_P_IPV6 as u16).to_be() as i32,
/// ICMP protocol ([icmp(7)](https://man7.org/linux/man-pages/man7/icmp.7.html))
Icmp = libc::IPPROTO_ICMP,
/// ICMPv6 protocol (ICMP over IPv6)
IcmpV6 = libc::IPPROTO_ICMPV6,
/// SCTP ([sctp(7)](https://man7.org/linux/man-pages/man7/sctp.7.html))
#[cfg(any(
apple_targets,
linux_android,
target_os = "freebsd",
target_os = "netbsd"
))]
Sctp = libc::IPPROTO_SCTP,
}
impl SockProtocol {
@@ -223,6 +253,14 @@ impl SockProtocol {
#[cfg(apple_targets)]
#[allow(non_upper_case_globals)]
pub const KextEvent: SockProtocol = SockProtocol::Icmp; // Matches libc::SYSPROTO_EVENT
/// Packet filter on IPv4 traffic
// NOTE: placed here due to conflict (little endian arch) with SockProtocol::NetLinkISCI
#[cfg(linux_android)]
#[allow(non_upper_case_globals)]
#[cfg(target_endian = "little")]
pub const EthIp: SockProtocol = unsafe { std::mem::transmute::<i32, SockProtocol>((libc::ETH_P_IP as u16).to_be() as i32) };
}
#[cfg(linux_android)]
libc_bitflags! {
@@ -346,6 +384,9 @@ libc_bitflags! {
target_os = "fuchsia",
target_os = "freebsd"))]
MSG_WAITFORONE;
/// Indicates that this message is not a user message but an SCTP notification.
#[cfg(target_os = "linux")]
MSG_NOTIFICATION;
}
}
@@ -499,6 +540,63 @@ cfg_if! {
}
}
cfg_if! {
if #[cfg(apple_targets)] {
use std::fmt;
/// Return type of [`LocalPeerToken`].
///
/// The audit token is an opaque token which identifies Mach tasks and
/// senders of Mach messages as subjects to the BSM audit system. Only
/// the appropriate BSM library routines should be used to interpret
/// the contents of the audit token as the representation of the
/// subject identity within the token may change over time.
///
/// Starting with macOS 11, almost all audit functions have been
/// deprecated (see the system header `bsm/libbsm.h`), do not use them
/// if your program target more recent versions of macOS.
///
/// [`LocalPeerToken`]: crate::sys::socket::sockopt::LocalPeerToken
#[repr(C)]
#[derive(Default, Copy, Clone, PartialEq, Eq, Hash)]
pub struct audit_token_t {
/// Value of the token.
///
/// This is considered an opaque value, do not rely on its format.
pub val: [libc::c_uint; 8],
}
// Make the debug representation a hex string to make it shorter and clearer.
impl fmt::Debug for audit_token_t {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("audit_token_t")
.field(&format!("0x{:08X}", self))
.finish()
}
}
impl fmt::LowerHex for audit_token_t {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for v in self.val {
fmt::LowerHex::fmt(&v, f)?;
}
Ok(())
}
}
impl fmt::UpperHex for audit_token_t {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for v in self.val {
fmt::UpperHex::fmt(&v, f)?;
}
Ok(())
}
}
}
}
feature! {
#![feature = "net"]
/// Request for multicast socket operations
@@ -573,7 +671,7 @@ macro_rules! cmsg_space {
( $( $x:ty ),* ) => {
{
let space = 0 $(+ $crate::sys::socket::cmsg_space::<$x>())*;
Vec::<u8>::with_capacity(space)
vec![0u8; space]
}
}
}
@@ -599,7 +697,7 @@ pub struct RecvMsg<'a, 's, S> {
mhdr: msghdr,
}
impl<'a, S> RecvMsg<'a, '_, S> {
impl<S> RecvMsg<'_, '_, S> {
/// Iterate over the valid control messages pointed to by this msghdr. If
/// allocated space for CMSGs was too small it is not safe to iterate,
/// instead return an `Error::ENOBUFS` error.
@@ -623,7 +721,7 @@ pub struct CmsgIterator<'a> {
mhdr: &'a msghdr
}
impl<'a> Iterator for CmsgIterator<'a> {
impl Iterator for CmsgIterator<'_> {
type Item = ControlMessageOwned;
fn next(&mut self) -> Option<ControlMessageOwned> {
@@ -769,6 +867,43 @@ pub enum ControlMessageOwned {
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv6OrigDstAddr(libc::sockaddr_in6),
/// Time-to-Live (TTL) header field of the incoming IPv4 packet.
///
/// [Further reading](https://www.man7.org/linux/man-pages/man7/ip.7.html)
#[cfg(linux_android)]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv4Ttl(i32),
/// Time-to-Live (TTL) header field of the incoming IPv4 packet.
///
/// [Further reading](https://datatracker.ietf.org/doc/html/rfc3542.html)
#[cfg(target_os = "freebsd")]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv4Ttl(u8),
/// Hop Limit header field of the incoming IPv6 packet.
///
/// [Further reading for Linux](https://www.man7.org/linux/man-pages/man7/ip.7.html)
/// [Further reading for FreeBSD](https://datatracker.ietf.org/doc/html/rfc3542.html)
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv6HopLimit(i32),
/// Retrieve the DSCP (ToS) header field of the incoming IPv4 packet.
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv4Tos(u8),
/// Retrieve the DSCP (Traffic Class) header field of the incoming IPv6 packet.
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv6TClass(i32),
/// UDP Generic Receive Offload (GRO) allows receiving multiple UDP
/// packets from a single sender.
/// Fixed-size payloads are following one by one in a receive buffer.
@@ -809,7 +944,6 @@ pub enum ControlMessageOwned {
TlsGetRecordType(TlsGetRecordType),
/// Catch-all variant for unimplemented cmsg types.
#[doc(hidden)]
Unknown(UnknownCmsg),
}
@@ -891,7 +1025,7 @@ impl ControlMessageOwned {
let cred: libc::cmsgcred = unsafe { ptr::read_unaligned(p as *const _) };
ControlMessageOwned::ScmCreds(cred.into())
}
#[cfg(not(any(target_os = "aix", target_os = "haiku")))]
#[cfg(not(any(target_os = "aix", target_os = "haiku", target_os = "cygwin")))]
(libc::SOL_SOCKET, libc::SCM_TIMESTAMP) => {
let tv: libc::timeval = unsafe { ptr::read_unaligned(p as *const _) };
ControlMessageOwned::ScmTimestamp(TimeVal::from(tv))
@@ -987,9 +1121,48 @@ impl ControlMessageOwned {
let content_type = unsafe { ptr::read_unaligned(p as *const u8) };
ControlMessageOwned::TlsGetRecordType(content_type.into())
},
#[cfg(linux_android)]
#[cfg(feature = "net")]
(libc::IPPROTO_IP, libc::IP_TTL) => {
let ttl = unsafe { ptr::read_unaligned(p as *const i32) };
ControlMessageOwned::Ipv4Ttl(ttl)
},
#[cfg(target_os = "freebsd")]
#[cfg(feature = "net")]
(libc::IPPROTO_IP, libc::IP_RECVTTL) => {
let ttl: u8 = unsafe { ptr::read_unaligned(p as *const u8) };
ControlMessageOwned::Ipv4Ttl(ttl)
},
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
(libc::IPPROTO_IPV6, libc::IPV6_HOPLIMIT) => {
let ttl = unsafe { ptr::read_unaligned(p as *const i32) };
ControlMessageOwned::Ipv6HopLimit(ttl)
},
#[cfg(linux_android)]
#[cfg(feature = "net")]
(libc::IPPROTO_IP, libc::IP_TOS) => {
let tos = unsafe { ptr::read_unaligned(p as *const u8) };
ControlMessageOwned::Ipv4Tos(tos)
},
#[cfg(target_os = "freebsd")]
#[cfg(feature = "net")]
(libc::IPPROTO_IP, libc::IP_RECVTOS) => {
let tos = unsafe { ptr::read_unaligned(p as *const u8) };
ControlMessageOwned::Ipv4Tos(tos)
},
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
(libc::IPPROTO_IPV6, libc::IPV6_TCLASS) => {
let tc = unsafe { ptr::read_unaligned(p as *const i32) };
ControlMessageOwned::Ipv6TClass(tc)
},
(_, _) => {
let sl = unsafe { std::slice::from_raw_parts(p, len) };
let ucmsg = UnknownCmsg(*header, Vec::<u8>::from(sl));
let ucmsg = UnknownCmsg {
cmsg_header: *header,
data_bytes: Vec::<u8>::from(sl),
};
ControlMessageOwned::Unknown(ucmsg)
}
}
@@ -1124,6 +1297,18 @@ pub enum ControlMessage<'a> {
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv4SendSrcAddr(&'a libc::in_addr),
/// Configure the Time-to-Live for v4 traffic.
#[cfg(linux_android)]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv4Ttl(&'a libc::c_int),
/// Configure the Time-to-Live for v4 traffic.
#[cfg(target_os = "freebsd")]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv4Ttl(&'a libc::c_uchar),
/// Configure the hop limit for v6 multicast traffic.
///
/// Set the IPv6 hop limit for this message. The argument is an integer
@@ -1138,9 +1323,9 @@ pub enum ControlMessage<'a> {
Ipv6HopLimit(&'a libc::c_int),
/// SO_RXQ_OVFL indicates that an unsigned 32 bit value
/// ancilliary msg (cmsg) should be attached to recieved
/// ancillary msg (cmsg) should be attached to received
/// skbs indicating the number of packets dropped by the
/// socket between the last recieved packet and this
/// socket between the last received packet and this
/// received packet.
#[cfg(any(linux_android, target_os = "fuchsia"))]
RxqOvfl(&'a u32),
@@ -1152,14 +1337,34 @@ pub enum ControlMessage<'a> {
/// page.
#[cfg(target_os = "linux")]
TxTime(&'a u64),
/// Configure DSCP / IP TOS for outgoing v4 packets.
///
/// Further information can be found [here](https://en.wikipedia.org/wiki/Differentiated_services).
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv4Tos(&'a u8),
/// Configure DSCP / IPv6 TCLASS for outgoing v6 packets.
///
/// Further information can be found [here](https://en.wikipedia.org/wiki/Differentiated_services).
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv6TClass(&'a i32),
}
// An opaque structure used to prevent cmsghdr from being a public type
#[doc(hidden)]
/// Control messages that are currently not supported by Nix.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct UnknownCmsg(cmsghdr, Vec<u8>);
pub struct UnknownCmsg {
/// Control message header.
pub cmsg_header: cmsghdr,
/// Bytes of the control message data.
pub data_bytes: Vec<u8>
}
impl<'a> ControlMessage<'a> {
impl ControlMessage<'_> {
/// The value of CMSG_SPACE on this message.
/// Safe because CMSG_SPACE is always safe
fn space(&self) -> usize {
@@ -1169,13 +1374,15 @@ impl<'a> ControlMessage<'a> {
/// The value of CMSG_LEN on this message.
/// Safe because CMSG_LEN is always safe
#[cfg(any(target_os = "android",
all(target_os = "linux", not(target_env = "musl"))))]
all(target_os = "linux", not(any(target_env = "musl", target_env = "ohos"))),
target_os = "cygwin"))]
fn cmsg_len(&self) -> usize {
unsafe{CMSG_LEN(self.len() as libc::c_uint) as usize}
}
#[cfg(not(any(target_os = "android",
all(target_os = "linux", not(target_env = "musl")))))]
all(target_os = "linux", not(any(target_env = "musl", target_env = "ohos"))),
target_os = "cygwin")))]
fn cmsg_len(&self) -> libc::c_uint {
unsafe{CMSG_LEN(self.len() as libc::c_uint)}
}
@@ -1245,6 +1452,12 @@ impl<'a> ControlMessage<'a> {
#[cfg(any(freebsdlike, netbsdlike))]
#[cfg(feature = "net")]
ControlMessage::Ipv4SendSrcAddr(addr) => addr as *const _ as *const u8,
#[cfg(linux_android)]
#[cfg(feature = "net")]
ControlMessage::Ipv4Ttl(ttl) => ttl as *const i32 as *const u8,
#[cfg(target_os = "freebsd")]
#[cfg(feature = "net")]
ControlMessage::Ipv4Ttl(ttl) => ttl as *const u8,
#[cfg(any(linux_android, freebsdlike, apple_targets, target_os = "haiku"))]
#[cfg(feature = "net")]
ControlMessage::Ipv6HopLimit(limit) => limit as *const _ as *const u8,
@@ -1256,6 +1469,16 @@ impl<'a> ControlMessage<'a> {
ControlMessage::TxTime(tx_time) => {
tx_time as *const _ as *const u8
},
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
ControlMessage::Ipv4Tos(tos) => {
tos as *const _
},
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
ControlMessage::Ipv6TClass(tclass) => {
tclass as *const _ as *const u8
},
};
unsafe {
ptr::copy_nonoverlapping(
@@ -1307,6 +1530,11 @@ impl<'a> ControlMessage<'a> {
#[cfg(any(freebsdlike, netbsdlike))]
#[cfg(feature = "net")]
ControlMessage::Ipv4SendSrcAddr(addr) => mem::size_of_val(addr),
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
ControlMessage::Ipv4Ttl(ttl) => {
mem::size_of_val(ttl)
},
#[cfg(any(linux_android, freebsdlike, apple_targets, target_os = "haiku"))]
#[cfg(feature = "net")]
ControlMessage::Ipv6HopLimit(limit) => {
@@ -1320,6 +1548,16 @@ impl<'a> ControlMessage<'a> {
ControlMessage::TxTime(tx_time) => {
mem::size_of_val(tx_time)
},
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
ControlMessage::Ipv4Tos(tos) => {
mem::size_of_val(tos)
},
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
ControlMessage::Ipv6TClass(tclass) => {
mem::size_of_val(tclass)
},
}
}
@@ -1347,6 +1585,9 @@ impl<'a> ControlMessage<'a> {
#[cfg(any(freebsdlike, netbsdlike))]
#[cfg(feature = "net")]
ControlMessage::Ipv4SendSrcAddr(_) => libc::IPPROTO_IP,
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
ControlMessage::Ipv4Ttl(_) => libc::IPPROTO_IP,
#[cfg(any(linux_android, freebsdlike, apple_targets, target_os = "haiku"))]
#[cfg(feature = "net")]
ControlMessage::Ipv6HopLimit(_) => libc::IPPROTO_IPV6,
@@ -1354,6 +1595,12 @@ impl<'a> ControlMessage<'a> {
ControlMessage::RxqOvfl(_) => libc::SOL_SOCKET,
#[cfg(target_os = "linux")]
ControlMessage::TxTime(_) => libc::SOL_SOCKET,
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
ControlMessage::Ipv4Tos(_) => libc::IPPROTO_IP,
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
ControlMessage::Ipv6TClass(_) => libc::IPPROTO_IPV6,
}
}
@@ -1392,6 +1639,9 @@ impl<'a> ControlMessage<'a> {
#[cfg(any(freebsdlike, netbsdlike))]
#[cfg(feature = "net")]
ControlMessage::Ipv4SendSrcAddr(_) => libc::IP_SENDSRCADDR,
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
ControlMessage::Ipv4Ttl(_) => libc::IP_TTL,
#[cfg(any(linux_android, freebsdlike, apple_targets, target_os = "haiku"))]
#[cfg(feature = "net")]
ControlMessage::Ipv6HopLimit(_) => libc::IPV6_HOPLIMIT,
@@ -1403,6 +1653,16 @@ impl<'a> ControlMessage<'a> {
ControlMessage::TxTime(_) => {
libc::SCM_TXTIME
},
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
ControlMessage::Ipv4Tos(_) => {
libc::IP_TOS
},
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
ControlMessage::Ipv6TClass(_) => {
libc::IPV6_TCLASS
},
}
}
@@ -1443,6 +1703,7 @@ impl<'a> ControlMessage<'a> {
/// sendmsg::<()>(fd1.as_raw_fd(), &iov, &[cmsg], MsgFlags::empty(), None).unwrap();
/// ```
/// When directing to a specific address, the generic type will be inferred.
/// Note that SCM_RIGHTS ancillary data are valid only for AF_UNIX sockets on Solaris.
/// ```
/// # use nix::sys::socket::*;
/// # use nix::unistd::pipe;
@@ -1457,6 +1718,7 @@ impl<'a> ControlMessage<'a> {
/// let iov = [IoSlice::new(b"hello")];
/// let fds = [r.as_raw_fd()];
/// let cmsg = ControlMessage::ScmRights(&fds);
/// #[cfg(not(target_os = "solaris"))]
/// sendmsg(fd.as_raw_fd(), &iov, &[cmsg], MsgFlags::empty(), Some(&localhost)).unwrap();
/// ```
pub fn sendmsg<S>(fd: RawFd, iov: &[IoSlice<'_>], cmsgs: &[ControlMessage],
@@ -1643,11 +1905,9 @@ impl<S> MultiHeaders<S> {
/// call to recvmmsg(). In the current implementation, however, the error code can be
/// overwritten in the meantime by an unrelated network event on a socket, for example an
/// incoming ICMP packet.
// On aarch64 linux using recvmmsg and trying to get hardware/kernel timestamps might not
// always produce the desired results - see https://github.com/nix-rust/nix/pull/1744 for more
// details
#[cfg(any(linux_android, target_os = "freebsd", target_os = "netbsd"))]
pub fn recvmmsg<'a, XS, S, I>(
fd: RawFd,
@@ -1844,7 +2104,10 @@ unsafe fn pack_mhdr_to_receive<S>(
let mut mhdr = mem::MaybeUninit::<msghdr>::zeroed();
let p = mhdr.as_mut_ptr();
unsafe {
(*p).msg_name = address as *mut c_void;
// it is important to use as_mut_ptr() here since S can be
// a zero sized type representing by a dangling pointer.
// as_mut_ptr() handles this case and uses a null pointer instead
(*p).msg_name = (*address).as_mut_ptr() as *mut c_void;
(*p).msg_namelen = S::size();
(*p).msg_iov = iov_buffer as *mut iovec;
(*p).msg_iovlen = iov_buffer_len as _;
@@ -1923,7 +2186,7 @@ fn pack_mhdr_to_send<'a, I, C, S>(
/// # References
/// [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<u8>>,
mut cmsg_buffer: Option<&'a mut [u8]>,
flags: MsgFlags) -> Result<RecvMsg<'a, 'outer, S>>
where S: SockaddrLike + 'a,
'inner: 'outer
@@ -1931,7 +2194,7 @@ pub fn recvmsg<'a, 'outer, 'inner, S>(fd: RawFd, iov: &'outer mut [IoSliceMut<'i
let mut address = mem::MaybeUninit::uninit();
let (msg_control, msg_controllen) = cmsg_buffer.as_mut()
.map(|v| (v.as_mut_ptr(), v.capacity()))
.map(|v| (v.as_mut_ptr(), v.len()))
.unwrap_or((ptr::null_mut(), 0));
let mut mhdr = unsafe {
pack_mhdr_to_receive(iov.as_mut().as_mut_ptr(), iov.len(), msg_control, msg_controllen, address.as_mut_ptr())
@@ -2036,7 +2299,7 @@ impl Backlog {
let val = val.into();
if !(MIN..Self::MAXCONN.0).contains(&val) {
if !(MIN..=Self::MAXCONN.0).contains(&val) {
return Err(Errno::EINVAL);
}
@@ -2207,7 +2470,7 @@ pub trait GetSockOpt: Copy {
/// Represents a socket option that can be set.
pub trait SetSockOpt: Clone {
type Val;
type Val: ?Sized;
/// Set the value of this socket option on the given socket.
fn set<F: AsFd>(&self, fd: &F, val: &Self::Val) -> Result<()>;
@@ -2302,4 +2565,3 @@ pub fn shutdown(df: RawFd, how: Shutdown) -> Result<()> {
Errno::result(shutdown(df, how)).map(drop)
}
}

View File

@@ -1,13 +1,18 @@
//! Socket options as used by `setsockopt` and `getsockopt`.
use super::{GetSockOpt, SetSockOpt};
use crate::errno::Errno;
#[cfg(any(linux_android, target_os = "illumos"))]
use super::SetSockOpt;
use crate::sys::time::TimeVal;
use crate::Result;
#[cfg(any(linux_android, target_os = "illumos"))]
use crate::{errno::Errno, Result};
use cfg_if::cfg_if;
use libc::{self, c_int, c_void, socklen_t};
use std::ffi::{CStr, CString, OsStr, OsString};
#[cfg(apple_targets)]
use std::ffi::CString;
use std::ffi::{CStr, OsStr, OsString};
use std::mem::{self, MaybeUninit};
use std::os::fd::OwnedFd;
use std::os::unix::ffi::OsStrExt;
#[cfg(any(linux_android, target_os = "illumos"))]
use std::os::unix::io::{AsFd, AsRawFd};
// Constants
@@ -22,40 +27,49 @@ const TCP_CA_NAME_MAX: usize = 16;
/// This macro aims to help implementing `SetSockOpt` for different socket options that accept
/// different kinds of data to be used with `setsockopt`.
///
/// Instead of using this macro directly consider using `sockopt_impl!`, especially if the option
/// you are implementing represents a simple type.
/// Instead of using this macro directly consider using [`sockopt_impl!`](crate::sockopt_impl),
/// especially if the option you are implementing represents a simple type.
///
/// # Arguments
///
/// * `$name:ident`: name of the type you want to implement `SetSockOpt` for.
/// * `$level:expr` : socket layer, or a `protocol level`: could be *raw sockets*
/// (`libc::SOL_SOCKET`), *ip protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`),
/// and more. Please refer to your system manual for more options. Will be passed as the second
/// argument (`level`) to the `setsockopt` call.
/// (`libc::SOL_SOCKET`), *ip protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`),
/// and more. Please refer to your system manual for more options. Will be passed as the second
/// argument (`level`) to the `setsockopt` call.
/// * `$flag:path`: a flag name to set. Some examples: `libc::SO_REUSEADDR`, `libc::TCP_NODELAY`,
/// `libc::IP_ADD_MEMBERSHIP` and others. Will be passed as the third argument (`option_name`)
/// to the `setsockopt` call.
/// `libc::IP_ADD_MEMBERSHIP` and others. Will be passed as the third argument (`option_name`)
/// to the `setsockopt` call.
/// * Type of the value that you are going to set.
/// * Type that implements the `Set` trait for the type from the previous item (like `SetBool` for
/// `bool`, `SetUsize` for `usize`, etc.).
/// * Type that implements the `Set` trait for the type from the previous item
/// (like `SetBool` for `bool`, `SetUsize` for `usize`, etc.).
#[macro_export]
macro_rules! setsockopt_impl {
($name:ident, $level:expr, $flag:path, $ty:ty, $setter:ty) => {
impl SetSockOpt for $name {
#[allow(deprecated)] // to allow we have deprecated socket option
impl $crate::sys::socket::SetSockOpt for $name {
type Val = $ty;
fn set<F: AsFd>(&self, fd: &F, val: &$ty) -> Result<()> {
unsafe {
let setter: $setter = Set::new(val);
let res = libc::setsockopt(
fn set<F: std::os::unix::io::AsFd>(
&self,
fd: &F,
val: &$ty,
) -> $crate::Result<()> {
use std::os::fd::AsRawFd;
use $crate::sys::socket::sockopt::Set;
let setter: $setter = Set::new(val);
let level = $level;
let flag = $flag;
let res = unsafe {
libc::setsockopt(
fd.as_fd().as_raw_fd(),
$level,
$flag,
level,
flag,
setter.ffi_ptr(),
setter.ffi_len(),
);
Errno::result(res).map(drop)
}
)
};
$crate::errno::Errno::result(res).map(drop)
}
}
};
@@ -67,44 +81,68 @@ macro_rules! setsockopt_impl {
/// This macro aims to help implementing `GetSockOpt` for different socket options that accept
/// different kinds of data to be use with `getsockopt`.
///
/// Instead of using this macro directly consider using `sockopt_impl!`, especially if the option
/// you are implementing represents a simple type.
/// Instead of using this macro directly consider using [`sockopt_impl!`](crate::sockopt_impl),
/// especially if the option you are implementing represents a simple type.
///
/// # Arguments
///
/// * Name of the type you want to implement `GetSockOpt` for.
/// * Socket layer, or a `protocol level`: could be *raw sockets* (`lic::SOL_SOCKET`), *ip
/// protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`), and more. Please refer
/// to your system manual for more options. Will be passed as the second argument (`level`) to
/// the `getsockopt` call.
/// protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`), and more. Please refer
/// to your system manual for more options. Will be passed as the second argument (`level`) to
/// the `getsockopt` call.
/// * A flag to set. Some examples: `libc::SO_REUSEADDR`, `libc::TCP_NODELAY`,
/// `libc::SO_ORIGINAL_DST` and others. Will be passed as the third argument (`option_name`) to
/// the `getsockopt` call.
/// `libc::SO_ORIGINAL_DST` and others. Will be passed as the third argument (`option_name`) to
/// the `getsockopt` call.
/// * Type of the value that you are going to get.
/// * Type that implements the `Get` trait for the type from the previous item (`GetBool` for
/// `bool`, `GetUsize` for `usize`, etc.).
/// `bool`, `GetUsize` for `usize`, etc.).
#[macro_export]
macro_rules! getsockopt_impl {
($name:ident, $level:expr, $flag:path, $ty:ty, $getter:ty) => {
impl GetSockOpt for $name {
#[allow(deprecated)] // to allow we have deprecated socket option
impl $crate::sys::socket::GetSockOpt for $name {
type Val = $ty;
fn get<F: AsFd>(&self, fd: &F) -> Result<$ty> {
unsafe {
let mut getter: $getter = Get::uninit();
let res = libc::getsockopt(
fn get<F: std::os::unix::io::AsFd>(
&self,
fd: &F,
) -> $crate::Result<$ty> {
use std::os::fd::AsRawFd;
use $crate::sys::socket::sockopt::Get;
let mut getter: $getter = Get::uninit();
let level = $level;
let flag = $flag;
let res = unsafe {
libc::getsockopt(
fd.as_fd().as_raw_fd(),
$level,
$flag,
level,
flag,
getter.ffi_ptr(),
getter.ffi_len(),
);
Errno::result(res)?;
)
};
$crate::errno::Errno::result(res)?;
match <$ty>::try_from(getter.assume_init()) {
Err(_) => Err(Errno::EINVAL),
Ok(r) => Ok(r),
}
// getter is definitely initialized now
let gotten = unsafe { getter.assume_init() };
match <$ty>::try_from(gotten) {
// In most `getsockopt_impl!` implementations, `assume_init()`
// returns `$ty`, so calling `$ty`::try_from($ty) will always
// succeed. which makes the following `Err(_)` branch
// unreachable.
//
// However, there is indeed one exception, `sockopt::SockType`,
// `assume_init()` returns an `i32`, but `$ty` is `super::SockType`,
// this exception necessitates the use of that `try_from()`,
// and we have to allow the unreachable pattern wraning.
//
// For the reason why we are using `i32` as the underlying
// buffer type for this socket option, see issue:
// https://github.com/nix-rust/nix/issues/1819
#[allow(unreachable_patterns)]
Err(_) => Err($crate::errno::Errno::EINVAL),
Ok(r) => Ok(r),
}
}
}
@@ -124,72 +162,90 @@ macro_rules! getsockopt_impl {
/// # Arguments
///
/// * `GetOnly`, `SetOnly` or `Both`: whether you want to implement only getter, only setter or
/// both of them.
/// both of them.
/// * `$name:ident`: name of type `GetSockOpt`/`SetSockOpt` will be implemented for.
/// * `$level:expr` : socket layer, or a `protocol level`: could be *raw sockets*
/// (`libc::SOL_SOCKET`), *ip protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`),
/// and more. Please refer to your system manual for more options. Will be passed as the second
/// argument (`level`) to the `getsockopt`/`setsockopt` call.
/// (`libc::SOL_SOCKET`), *ip protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`),
/// and more. Please refer to your system manual for more options. Will be passed as the second
/// argument (`level`) to the `getsockopt`/`setsockopt` call.
/// * `$flag:path`: a flag name to set. Some examples: `libc::SO_REUSEADDR`, `libc::TCP_NODELAY`,
/// `libc::IP_ADD_MEMBERSHIP` and others. Will be passed as the third argument (`option_name`)
/// to the `setsockopt`/`getsockopt` call.
/// `libc::IP_ADD_MEMBERSHIP` and others. Will be passed as the third argument (`option_name`)
/// to the `setsockopt`/`getsockopt` call.
/// * `$ty:ty`: type of the value that will be get/set.
/// * `$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(unused_macro_rules)]
#[macro_export]
macro_rules! sockopt_impl {
($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, bool) => {
sockopt_impl!($(#[$attr])*
$name, GetOnly, $level, $flag, bool, GetBool);
$name, GetOnly, $level, $flag, bool, $crate::sys::socket::sockopt::GetBool);
};
($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, u8) => {
sockopt_impl!($(#[$attr])* $name, GetOnly, $level, $flag, u8, GetU8);
sockopt_impl!($(#[$attr])* $name, GetOnly, $level, $flag, u8, $crate::sys::socket::sockopt::GetU8);
};
($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, usize) =>
{
sockopt_impl!($(#[$attr])*
$name, GetOnly, $level, $flag, usize, GetUsize);
$name, GetOnly, $level, $flag, usize, $crate::sys::socket::sockopt::GetUsize);
};
($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, OwnedFd) =>
{
sockopt_impl!($(#[$attr])*
$name, GetOnly, $level, $flag, OwnedFd, $crate::sys::socket::sockopt::GetOwnedFd);
};
($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, bool) => {
sockopt_impl!($(#[$attr])*
$name, SetOnly, $level, $flag, bool, SetBool);
$name, SetOnly, $level, $flag, bool, $crate::sys::socket::sockopt::SetBool);
};
($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, u8) => {
sockopt_impl!($(#[$attr])* $name, SetOnly, $level, $flag, u8, SetU8);
sockopt_impl!($(#[$attr])* $name, SetOnly, $level, $flag, u8, $crate::sys::socket::sockopt::SetU8);
};
($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, usize) =>
{
sockopt_impl!($(#[$attr])*
$name, SetOnly, $level, $flag, usize, SetUsize);
$name, SetOnly, $level, $flag, usize, $crate::sys::socket::sockopt::SetUsize);
};
($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, OwnedFd) =>
{
sockopt_impl!($(#[$attr])*
$name, SetOnly, $level, $flag, OwnedFd, $crate::sys::socket::sockopt::SetOwnedFd);
};
($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, bool) => {
sockopt_impl!($(#[$attr])*
$name, Both, $level, $flag, bool, GetBool, SetBool);
$name, Both, $level, $flag, bool, $crate::sys::socket::sockopt::GetBool, $crate::sys::socket::sockopt::SetBool);
};
($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, u8) => {
sockopt_impl!($(#[$attr])*
$name, Both, $level, $flag, u8, GetU8, SetU8);
$name, Both, $level, $flag, u8, $crate::sys::socket::sockopt::GetU8, $crate::sys::socket::sockopt::SetU8);
};
($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, usize) => {
sockopt_impl!($(#[$attr])*
$name, Both, $level, $flag, usize, GetUsize, SetUsize);
$name, Both, $level, $flag, usize, $crate::sys::socket::sockopt::GetUsize, $crate::sys::socket::sockopt::SetUsize);
};
($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, OwnedFd) => {
sockopt_impl!($(#[$attr])*
$name, Both, $level, $flag, OwnedFd, $crate::sys::socket::sockopt::GetOwnedFd, $crate::sys::socket::sockopt::SetOwnedFd);
};
($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path,
OsString<$array:ty>) =>
{
sockopt_impl!($(#[$attr])*
$name, Both, $level, $flag, OsString, GetOsString<$array>,
SetOsString);
$name, Both, $level, $flag, std::ffi::OsString, $crate::sys::socket::sockopt::GetOsString<$array>,
$crate::sys::socket::sockopt::SetOsString);
};
/*
@@ -199,7 +255,7 @@ macro_rules! sockopt_impl {
($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, $ty:ty) =>
{
sockopt_impl!($(#[$attr])*
$name, GetOnly, $level, $flag, $ty, GetStruct<$ty>);
$name, GetOnly, $level, $flag, $ty, $crate::sys::socket::sockopt::GetStruct<$ty>);
};
($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, $ty:ty,
@@ -215,7 +271,7 @@ macro_rules! sockopt_impl {
($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, $ty:ty) =>
{
sockopt_impl!($(#[$attr])*
$name, SetOnly, $level, $flag, $ty, SetStruct<$ty>);
$name, SetOnly, $level, $flag, $ty, $crate::sys::socket::sockopt::SetStruct<$ty>);
};
($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, $ty:ty,
@@ -241,8 +297,8 @@ macro_rules! sockopt_impl {
($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, $ty:ty) => {
sockopt_impl!($(#[$attr])*
$name, Both, $level, $flag, $ty, GetStruct<$ty>,
SetStruct<$ty>);
$name, Both, $level, $flag, $ty, $crate::sys::socket::sockopt::GetStruct<$ty>,
$crate::sys::socket::sockopt::SetStruct<$ty>);
};
}
@@ -260,7 +316,7 @@ sockopt_impl!(
libc::SO_REUSEADDR,
bool
);
#[cfg(not(solarish))]
#[cfg(not(any(solarish, target_os = "cygwin")))]
sockopt_impl!(
/// Permits multiple AF_INET or AF_INET6 sockets to be bound to an
/// identical socket address.
@@ -280,16 +336,44 @@ sockopt_impl!(
libc::SO_REUSEPORT_LB,
bool
);
#[cfg(target_os = "freebsd")]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
/// Select or query the set of functions that TCP will use for this connection. This allows a
/// user to select an alternate TCP stack.
TcpFunctionBlk,
Both,
libc::IPPROTO_TCP,
libc::TCP_FUNCTION_BLK,
libc::tcp_function_set
);
#[cfg(target_os = "freebsd")]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
/// Query the alias name of the set of function of the socket's TCP stack.
/// Uses the same field for the main name when getting from TCP_FUNCTION_BLK.
/// Empty if no alias.
TcpFunctionAlias,
GetOnly,
libc::IPPROTO_TCP,
libc::TCP_FUNCTION_ALIAS,
libc::tcp_function_set
);
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
/// Used to disable Nagle's algorithm.
///
/// Nagle's algorithm:
///
/// Under most circumstances, TCP sends data when it is presented; when
/// outstanding data has not yet been acknowledged, it gathers small amounts
/// of output to be sent in a single packet once an acknowledgement is
/// received. For a small number of clients, such as window systems that
/// send a stream of mouse events which receive no replies, this
/// packetization may cause significant delays. The boolean option
/// TCP_NODELAY defeats this algorithm.
/// packetization may cause significant delays. The boolean option, when
/// enabled, defeats this algorithm.
TcpNoDelay,
Both,
libc::IPPROTO_TCP,
@@ -297,7 +381,7 @@ sockopt_impl!(
bool
);
sockopt_impl!(
/// When enabled, a close(2) or shutdown(2) will not return until all
/// When enabled, a close(2) or shutdown(2) will not return until all
/// queued messages for the socket have been successfully sent or the
/// linger timeout has been reached.
Linger,
@@ -306,6 +390,15 @@ sockopt_impl!(
libc::SO_LINGER,
libc::linger
);
#[cfg(apple_targets)]
sockopt_impl!(
/// Same as `SO_LINGER`, but the duration is in seconds rather than kernel ticks.
LingerSec,
Both,
libc::SOL_SOCKET,
libc::SO_LINGER_SEC,
libc::linger
);
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
@@ -398,9 +491,10 @@ sockopt_impl!(
libc::SO_PRIORITY,
libc::c_int
);
#[cfg(target_os = "linux")]
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
sockopt_impl!(
#[deprecated(since = "0.30.0", note = "Use Ipv4Tos instead")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
/// Set or receive the Type-Of-Service (TOS) field that is
/// sent with every IP packet originating from this socket
@@ -410,17 +504,51 @@ sockopt_impl!(
libc::IP_TOS,
libc::c_int
);
#[cfg(target_os = "linux")]
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
/// Traffic class associated with outgoing packets
/// Set or receive the Type-Of-Service (TOS) field that is
/// sent with every IP packet originating from this socket
Ipv4Tos,
Both,
libc::IPPROTO_IP,
libc::IP_TOS,
libc::c_int
);
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
/// If enabled, the IP_TOS ancillary message is passed with incoming packets.
IpRecvTos,
Both,
libc::IPPROTO_IP,
libc::IP_RECVTOS,
bool
);
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
/// Set the traffic class associated with outgoing packets.
Ipv6TClass,
Both,
libc::IPPROTO_IPV6,
libc::IPV6_TCLASS,
libc::c_int
);
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
/// If enabled, the IPV6_TCLASS ancillary message is passed with incoming packets.
Ipv6RecvTClass,
Both,
libc::IPPROTO_IPV6,
libc::IPV6_RECVTCLASS,
bool
);
#[cfg(any(linux_android, target_os = "fuchsia"))]
#[cfg(feature = "net")]
sockopt_impl!(
@@ -504,7 +632,7 @@ sockopt_impl!(
libc::SO_KEEPALIVE,
bool
);
#[cfg(any(freebsdlike, apple_targets))]
#[cfg(freebsdlike)]
sockopt_impl!(
/// Get the credentials of the peer process of a connected unix domain
/// socket.
@@ -515,14 +643,34 @@ sockopt_impl!(
super::XuCred
);
#[cfg(apple_targets)]
sockopt_impl!(
/// Get the credentials of the peer process of a connected unix domain
/// socket.
LocalPeerCred,
GetOnly,
libc::SOL_LOCAL,
libc::LOCAL_PEERCRED,
super::XuCred
);
#[cfg(apple_targets)]
sockopt_impl!(
/// Get the PID of the peer process of a connected unix domain socket.
LocalPeerPid,
GetOnly,
0,
libc::SOL_LOCAL,
libc::LOCAL_PEERPID,
libc::c_int
);
#[cfg(apple_targets)]
sockopt_impl!(
/// Get the audit token of the peer process of a connected unix domain
/// socket.
LocalPeerToken,
GetOnly,
libc::SOL_LOCAL,
libc::LOCAL_PEERTOKEN,
super::audit_token_t
);
#[cfg(linux_android)]
sockopt_impl!(
/// Return the credentials of the foreign process connected to this socket.
@@ -532,6 +680,15 @@ sockopt_impl!(
libc::SO_PEERCRED,
super::UnixCredentials
);
#[cfg(target_os = "linux")]
sockopt_impl!(
/// Return the pidfd of the foreign process connected to this socket.
PeerPidfd,
GetOnly,
libc::SOL_SOCKET,
libc::SO_PEERPIDFD,
OwnedFd
);
#[cfg(target_os = "freebsd")]
#[cfg(feature = "net")]
sockopt_impl!(
@@ -568,7 +725,7 @@ sockopt_impl!(
u32
);
cfg_if! {
if #[cfg(linux_android)] {
if #[cfg(any(linux_android, apple_targets))] {
sockopt_impl!(
/// The maximum segment size for outgoing TCP packets.
TcpMaxSeg, Both, libc::IPPROTO_TCP, libc::TCP_MAXSEG, u32);
@@ -749,7 +906,13 @@ sockopt_impl!(
libc::SO_TIMESTAMPING,
super::TimestampingFlag
);
#[cfg(not(any(target_os = "aix", target_os = "haiku", target_os = "hurd", target_os = "redox")))]
#[cfg(not(any(
target_os = "aix",
target_os = "haiku",
target_os = "hurd",
target_os = "redox",
target_os = "cygwin"
)))]
sockopt_impl!(
/// Enable or disable the receiving of the `SO_TIMESTAMP` control message.
ReceiveTimestamp,
@@ -896,7 +1059,7 @@ sockopt_impl!(
libc::IP_PKTINFO,
bool
);
#[cfg(any(linux_android, target_os = "freebsd", apple_targets, netbsdlike))]
#[cfg(any(linux_android, bsd))]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
@@ -908,6 +1071,19 @@ sockopt_impl!(
libc::IPV6_RECVPKTINFO,
bool
);
#[cfg(any(linux_android, bsd))]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
/// Pass an `IPV6_PKTINFO` ancillary message that contains a in6_pktinfo
/// structure that supplies some information about the incoming packet.
Ipv6PacketInfo,
Both,
libc::IPPROTO_IPV6,
libc::IPV6_PKTINFO,
bool
);
#[cfg(bsd)]
#[cfg(feature = "net")]
sockopt_impl!(
@@ -1036,6 +1212,17 @@ sockopt_impl!(
libc::IP_TTL,
libc::c_int
);
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
sockopt_impl!(
/// Enables a receiving socket to retrieve the Time-to-Live (TTL) field
/// from incoming IPv4 packets.
Ipv4RecvTtl,
Both,
libc::IPPROTO_IP,
libc::IP_RECVTTL,
bool
);
#[cfg(any(apple_targets, linux_android, target_os = "freebsd"))]
sockopt_impl!(
/// Set the unicast hop limit for the socket.
@@ -1047,6 +1234,17 @@ sockopt_impl!(
);
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
sockopt_impl!(
/// Enables a receiving socket to retrieve the Hop Limit field
/// (similar to TTL in IPv4) from incoming IPv6 packets.
Ipv6RecvHopLimit,
Both,
libc::IPPROTO_IPV6,
libc::IPV6_RECVHOPLIMIT,
bool
);
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
/// The `recvmsg(2)` call will return the destination IP address for a UDP
@@ -1087,6 +1285,30 @@ sockopt_impl!(
GetCString<[u8; libc::IFNAMSIZ]>
);
#[cfg(solarish)]
sockopt_impl!(
/// Enable/disable exclusive binding.
/// Prevent multiple sockets to bind to the same
/// address:port, neutralizing `SO_REUSEADDR` effect.
ExclBind,
Both,
libc::SOL_SOCKET,
libc::SO_EXCLBIND,
bool
);
#[cfg(target_os = "linux")]
sockopt_impl!(
/// To be used with `ReusePort`,
/// we can then attach a BPF (classic)
/// to set how the packets are assigned
/// to the socket (e.g. cpu distribution).
AttachReusePortCbpf,
SetOnly,
libc::SOL_SOCKET,
libc::SO_ATTACH_REUSEPORT_CBPF,
libc::sock_fprog
);
#[allow(missing_docs)]
// Not documented by Linux!
#[cfg(linux_android)]
@@ -1300,7 +1522,58 @@ impl SetSockOpt for TcpTlsRx {
}
}
#[cfg(target_os = "illumos")]
#[derive(Copy, Clone, Debug)]
/// Attach a named filter to this socket to be able to
/// defer when anough byte had been buffered by the kernel
pub struct FilterAttach;
#[cfg(target_os = "illumos")]
impl SetSockOpt for FilterAttach {
type Val = OsStr;
fn set<F: AsFd>(&self, fd: &F, val: &Self::Val) -> Result<()> {
if val.len() > libc::FILNAME_MAX as usize {
return Err(Errno::EINVAL);
}
unsafe {
let res = libc::setsockopt(
fd.as_fd().as_raw_fd(),
libc::SOL_FILTER,
libc::FIL_ATTACH,
val.as_bytes().as_ptr().cast(),
val.len() as libc::socklen_t,
);
Errno::result(res).map(drop)
}
}
}
#[cfg(target_os = "illumos")]
#[derive(Copy, Clone, Debug)]
/// Detach a socket filter previously attached with FIL_ATTACH
pub struct FilterDetach;
#[cfg(target_os = "illumos")]
impl SetSockOpt for FilterDetach {
type Val = OsStr;
fn set<F: AsFd>(&self, fd: &F, val: &Self::Val) -> Result<()> {
if val.len() > libc::FILNAME_MAX as usize {
return Err(Errno::EINVAL);
}
unsafe {
let res = libc::setsockopt(
fd.as_fd().as_raw_fd(),
libc::SOL_FILTER,
libc::FIL_DETACH,
val.as_bytes().as_ptr().cast(),
val.len() as libc::socklen_t,
);
Errno::result(res).map(drop)
}
}
}
/*
*
* ===== Accessor helpers =====
@@ -1308,7 +1581,9 @@ impl SetSockOpt for TcpTlsRx {
*/
/// Helper trait that describes what is expected from a `GetSockOpt` getter.
trait Get<T> {
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
pub trait Get<T> {
/// Returns an uninitialized value.
fn uninit() -> Self;
/// Returns a pointer to the stored value. This pointer will be passed to the system's
@@ -1322,7 +1597,9 @@ trait Get<T> {
}
/// Helper trait that describes what is expected from a `SetSockOpt` setter.
trait Set<'a, T> {
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
pub trait Set<'a, T> {
/// Initialize the setter with a given value.
fn new(val: &'a T) -> Self;
/// Returns a pointer to the stored value. This pointer will be passed to the system's
@@ -1334,7 +1611,10 @@ trait Set<'a, T> {
}
/// Getter for an arbitrary `struct`.
struct GetStruct<T> {
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
#[derive(Debug)]
pub struct GetStruct<T> {
len: socklen_t,
val: MaybeUninit<T>,
}
@@ -1366,7 +1646,10 @@ impl<T> Get<T> for GetStruct<T> {
}
/// Setter for an arbitrary `struct`.
struct SetStruct<'a, T: 'static> {
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
#[derive(Debug)]
pub struct SetStruct<'a, T: 'static> {
ptr: &'a T,
}
@@ -1385,7 +1668,10 @@ impl<'a, T> Set<'a, T> for SetStruct<'a, T> {
}
/// Getter for a boolean value.
struct GetBool {
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
#[derive(Clone, Copy, Debug)]
pub struct GetBool {
len: socklen_t,
val: MaybeUninit<c_int>,
}
@@ -1417,7 +1703,10 @@ impl Get<bool> for GetBool {
}
/// Setter for a boolean value.
struct SetBool {
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct SetBool {
val: c_int,
}
@@ -1438,11 +1727,16 @@ impl<'a> Set<'a, bool> for SetBool {
}
/// Getter for an `u8` value.
struct GetU8 {
#[cfg(feature = "net")]
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
#[derive(Clone, Copy, Debug)]
pub struct GetU8 {
len: socklen_t,
val: MaybeUninit<u8>,
}
#[cfg(feature = "net")]
impl Get<u8> for GetU8 {
fn uninit() -> Self {
GetU8 {
@@ -1470,10 +1764,14 @@ impl Get<u8> for GetU8 {
}
/// Setter for an `u8` value.
struct SetU8 {
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct SetU8 {
val: u8,
}
#[cfg(feature = "net")]
impl<'a> Set<'a, u8> for SetU8 {
fn new(val: &'a u8) -> SetU8 {
SetU8 { val: *val }
@@ -1489,7 +1787,10 @@ impl<'a> Set<'a, u8> for SetU8 {
}
/// Getter for an `usize` value.
struct GetUsize {
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
#[derive(Clone, Copy, Debug)]
pub struct GetUsize {
len: socklen_t,
val: MaybeUninit<c_int>,
}
@@ -1521,7 +1822,10 @@ impl Get<usize> for GetUsize {
}
/// Setter for an `usize` value.
struct SetUsize {
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct SetUsize {
val: c_int,
}
@@ -1539,8 +1843,73 @@ impl<'a> Set<'a, usize> for SetUsize {
}
}
/// Getter for a `OwnedFd` value.
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
#[derive(Clone, Copy, Debug)]
pub struct GetOwnedFd {
len: socklen_t,
val: MaybeUninit<c_int>,
}
impl Get<OwnedFd> for GetOwnedFd {
fn uninit() -> Self {
GetOwnedFd {
len: mem::size_of::<c_int>() as socklen_t,
val: MaybeUninit::uninit(),
}
}
fn ffi_ptr(&mut self) -> *mut c_void {
self.val.as_mut_ptr().cast()
}
fn ffi_len(&mut self) -> *mut socklen_t {
&mut self.len
}
unsafe fn assume_init(self) -> OwnedFd {
use std::os::fd::{FromRawFd, RawFd};
assert_eq!(
self.len as usize,
mem::size_of::<c_int>(),
"invalid getsockopt implementation"
);
unsafe { OwnedFd::from_raw_fd(self.val.assume_init() as RawFd) }
}
}
/// Setter for an `OwnedFd` value.
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct SetOwnedFd {
val: c_int,
}
impl<'a> Set<'a, OwnedFd> for SetOwnedFd {
fn new(val: &'a OwnedFd) -> SetOwnedFd {
use std::os::fd::AsRawFd;
SetOwnedFd { val: val.as_raw_fd() as c_int }
}
fn ffi_ptr(&self) -> *const c_void {
&self.val as *const c_int as *const c_void
}
fn ffi_len(&self) -> socklen_t {
mem::size_of_val(&self.val) as socklen_t
}
}
/// Getter for a `OsString` value.
struct GetOsString<T: AsMut<[u8]>> {
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
#[derive(Debug)]
pub struct GetOsString<T: AsMut<[u8]>> {
len: socklen_t,
val: MaybeUninit<T>,
}
@@ -1564,17 +1933,29 @@ impl<T: AsMut<[u8]>> Get<OsString> for GetOsString<T> {
unsafe fn assume_init(self) -> OsString {
let len = self.len as usize;
let mut v = unsafe { self.val.assume_init() };
OsStr::from_bytes(&v.as_mut()[0..len]).to_owned()
if let Ok(cs) = CStr::from_bytes_until_nul(&v.as_mut()[0..len]) {
// It's legal for the kernel to return any number of NULs at the
// end of the string. C applications don't care, after all.
OsStr::from_bytes(cs.to_bytes())
} else {
// Even zero NULs is possible.
OsStr::from_bytes(&v.as_mut()[0..len])
}
.to_owned()
}
}
/// Setter for a `OsString` value.
struct SetOsString<'a> {
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
#[doc(hidden)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct SetOsString<'a> {
val: &'a OsStr,
}
#[cfg(any(target_os = "freebsd", linux_android, target_os = "illumos"))]
impl<'a> Set<'a, OsString> for SetOsString<'a> {
fn new(val: &'a OsString) -> SetOsString {
fn new(val: &OsString) -> SetOsString {
SetOsString {
val: val.as_os_str(),
}
@@ -1590,11 +1971,13 @@ impl<'a> Set<'a, OsString> for SetOsString<'a> {
}
/// Getter for a `CString` value.
#[cfg(apple_targets)]
struct GetCString<T: AsMut<[u8]>> {
len: socklen_t,
val: MaybeUninit<T>,
}
#[cfg(apple_targets)]
impl<T: AsMut<[u8]>> Get<CString> for GetCString<T> {
fn uninit() -> Self {
GetCString {

View File

@@ -6,11 +6,10 @@ pub use libc::stat as FileStat;
pub use libc::{dev_t, mode_t};
#[cfg(not(target_os = "redox"))]
use crate::fcntl::{at_rawfd, AtFlags};
use crate::fcntl::AtFlags;
use crate::sys::time::{TimeSpec, TimeVal};
use crate::{errno::Errno, NixPath, Result};
use std::mem;
use std::os::unix::io::RawFd;
libc_bitflags!(
/// "File type" flags for `mknod` and related functions.
@@ -168,17 +167,18 @@ pub fn mknod<P: ?Sized + NixPath>(
/// Create a special or ordinary file, relative to a given directory.
#[cfg(not(any(apple_targets, target_os = "redox", target_os = "haiku")))]
pub fn mknodat<P: ?Sized + NixPath>(
dirfd: Option<RawFd>,
pub fn mknodat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
dirfd: Fd,
path: &P,
kind: SFlag,
perm: Mode,
dev: dev_t,
) -> Result<()> {
let dirfd = at_rawfd(dirfd);
use std::os::fd::AsRawFd;
let res = path.with_nix_path(|cstr| unsafe {
libc::mknodat(
dirfd,
dirfd.as_fd().as_raw_fd(),
cstr.as_ptr(),
kind.bits() | perm.bits() as mode_t,
dev,
@@ -233,9 +233,11 @@ pub fn lstat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat> {
Ok(unsafe { dst.assume_init() })
}
pub fn fstat(fd: RawFd) -> Result<FileStat> {
pub fn fstat<Fd: std::os::fd::AsFd>(fd: Fd) -> Result<FileStat> {
use std::os::fd::AsRawFd;
let mut dst = mem::MaybeUninit::uninit();
let res = unsafe { libc::fstat(fd, dst.as_mut_ptr()) };
let res = unsafe { libc::fstat(fd.as_fd().as_raw_fd(), dst.as_mut_ptr()) };
Errno::result(res)?;
@@ -243,16 +245,17 @@ pub fn fstat(fd: RawFd) -> Result<FileStat> {
}
#[cfg(not(target_os = "redox"))]
pub fn fstatat<P: ?Sized + NixPath>(
dirfd: Option<RawFd>,
pub fn fstatat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
dirfd: Fd,
pathname: &P,
f: AtFlags,
) -> Result<FileStat> {
let dirfd = at_rawfd(dirfd);
use std::os::fd::AsRawFd;
let mut dst = mem::MaybeUninit::uninit();
let res = pathname.with_nix_path(|cstr| unsafe {
libc::fstatat(
dirfd,
dirfd.as_fd().as_raw_fd(),
cstr.as_ptr(),
dst.as_mut_ptr(),
f.bits() as libc::c_int,
@@ -269,8 +272,11 @@ pub fn fstatat<P: ?Sized + NixPath>(
/// # References
///
/// [fchmod(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmod.html).
pub fn fchmod(fd: RawFd, mode: Mode) -> Result<()> {
let res = unsafe { libc::fchmod(fd, mode.bits() as mode_t) };
pub fn fchmod<Fd: std::os::fd::AsFd>(fd: Fd, mode: Mode) -> Result<()> {
use std::os::fd::AsRawFd;
let res =
unsafe { libc::fchmod(fd.as_fd().as_raw_fd(), mode.bits() as mode_t) };
Errno::result(res).map(drop)
}
@@ -286,12 +292,12 @@ pub enum FchmodatFlags {
///
/// The file to be changed is determined relative to the directory associated
/// with the file descriptor `dirfd` or the current working directory
/// if `dirfd` is `None`.
/// if `dirfd` is [`AT_FDCWD`](crate::fcntl::AT_FDCWD).
///
/// If `flag` is `FchmodatFlags::NoFollowSymlink` and `path` names a symbolic link,
/// then the mode of the symbolic link is changed.
///
/// `fchmodat(None, path, mode, FchmodatFlags::FollowSymlink)` is identical to
/// `fchmodat(AT_FDCWD, path, mode, FchmodatFlags::FollowSymlink)` is identical to
/// a call `libc::chmod(path, mode)`. That's why `chmod` is unimplemented
/// in the `nix` crate.
///
@@ -299,19 +305,21 @@ pub enum FchmodatFlags {
///
/// [fchmodat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmodat.html).
#[cfg(not(target_os = "redox"))]
pub fn fchmodat<P: ?Sized + NixPath>(
dirfd: Option<RawFd>,
pub fn fchmodat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
dirfd: Fd,
path: &P,
mode: Mode,
flag: FchmodatFlags,
) -> Result<()> {
use std::os::fd::AsRawFd;
let atflag = match flag {
FchmodatFlags::FollowSymlink => AtFlags::empty(),
FchmodatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW,
};
let res = path.with_nix_path(|cstr| unsafe {
libc::fchmodat(
at_rawfd(dirfd),
dirfd.as_fd().as_raw_fd(),
cstr.as_ptr(),
mode.bits() as mode_t,
atflag.bits() as libc::c_int,
@@ -383,9 +391,15 @@ pub fn lutimes<P: ?Sized + NixPath>(
///
/// [futimens(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html).
#[inline]
pub fn futimens(fd: RawFd, atime: &TimeSpec, mtime: &TimeSpec) -> Result<()> {
pub fn futimens<Fd: std::os::fd::AsFd>(
fd: Fd,
atime: &TimeSpec,
mtime: &TimeSpec,
) -> Result<()> {
use std::os::fd::AsRawFd;
let times: [libc::timespec; 2] = [*atime.as_ref(), *mtime.as_ref()];
let res = unsafe { libc::futimens(fd, &times[0]) };
let res = unsafe { libc::futimens(fd.as_fd().as_raw_fd(), &times[0]) };
Errno::result(res).map(drop)
}
@@ -402,12 +416,12 @@ pub enum UtimensatFlags {
///
/// The file to be changed is determined relative to the directory associated
/// with the file descriptor `dirfd` or the current working directory
/// if `dirfd` is `None`.
/// if `dirfd` is [`AT_FDCWD`](crate::fcntl::AT_FDCWD).
///
/// If `flag` is `UtimensatFlags::NoFollowSymlink` and `path` names a symbolic link,
/// then the mode of the symbolic link is changed.
///
/// `utimensat(None, path, times, UtimensatFlags::FollowSymlink)` is identical to
/// `utimensat(AT_FDCWD, path, times, UtimensatFlags::FollowSymlink)` is identical to
/// `utimes(path, times)`. The latter is a deprecated API so prefer using the
/// former if the platforms you care about support it.
///
@@ -418,13 +432,15 @@ pub enum UtimensatFlags {
///
/// [utimensat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/utimens.html).
#[cfg(not(target_os = "redox"))]
pub fn utimensat<P: ?Sized + NixPath>(
dirfd: Option<RawFd>,
pub fn utimensat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
dirfd: Fd,
path: &P,
atime: &TimeSpec,
mtime: &TimeSpec,
flag: UtimensatFlags,
) -> Result<()> {
use std::os::fd::AsRawFd;
let atflag = match flag {
UtimensatFlags::FollowSymlink => AtFlags::empty(),
UtimensatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW,
@@ -432,7 +448,7 @@ pub fn utimensat<P: ?Sized + NixPath>(
let times: [libc::timespec; 2] = [*atime.as_ref(), *mtime.as_ref()];
let res = path.with_nix_path(|cstr| unsafe {
libc::utimensat(
at_rawfd(dirfd),
dirfd.as_fd().as_raw_fd(),
cstr.as_ptr(),
&times[0],
atflag.bits() as libc::c_int,
@@ -442,15 +458,28 @@ pub fn utimensat<P: ?Sized + NixPath>(
Errno::result(res).map(drop)
}
/// Create a directory at the path specified by `dirfd` and `path`.
///
/// If `path` is a relative path, then it is interpreted relative to the directory
/// referred to by the file descriptor `dirfd`. (One can use [`AT_FDCWD`][link] to
/// specify the current working directory in `dirfd`). If `path` is absolute,
/// then `dirfd` is ignored.
///
/// [link]: crate::fcntl::AT_FDCWD
#[cfg(not(target_os = "redox"))]
pub fn mkdirat<P: ?Sized + NixPath>(
fd: Option<RawFd>,
pub fn mkdirat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
dirfd: Fd,
path: &P,
mode: Mode,
) -> Result<()> {
let fd = at_rawfd(fd);
use std::os::fd::AsRawFd;
let res = path.with_nix_path(|cstr| unsafe {
libc::mkdirat(fd, cstr.as_ptr(), mode.bits() as mode_t)
libc::mkdirat(
dirfd.as_fd().as_raw_fd(),
cstr.as_ptr(),
mode.bits() as mode_t,
)
})?;
Errno::result(res).map(drop)

View File

@@ -1,7 +1,7 @@
//! Get filesystem statistics, non-portably
//!
//! See [`statvfs`](crate::sys::statvfs) for a portable alternative.
#[cfg(not(linux_android))]
#[cfg(not(any(linux_android, target_os = "cygwin")))]
use std::ffi::CStr;
use std::fmt::{self, Debug};
use std::mem;
@@ -19,24 +19,27 @@ use crate::{errno::Errno, NixPath, Result};
#[cfg(target_os = "android")]
pub type fsid_t = libc::__fsid_t;
/// Identifies a mounted file system
#[cfg(not(target_os = "android"))]
#[cfg(not(any(target_os = "android", target_os = "cygwin")))]
pub type fsid_t = libc::fsid_t;
/// Identifies a mounted file system
#[cfg(target_os = "cygwin")]
pub type fsid_t = libc::c_long;
cfg_if! {
if #[cfg(any(linux_android, target_os = "fuchsia"))] {
type type_of_statfs = libc::statfs64;
const LIBC_FSTATFS: unsafe extern fn
const LIBC_FSTATFS: unsafe extern "C" fn
(fd: libc::c_int, buf: *mut type_of_statfs) -> libc::c_int
= libc::fstatfs64;
const LIBC_STATFS: unsafe extern fn
const LIBC_STATFS: unsafe extern "C" fn
(path: *const libc::c_char, buf: *mut type_of_statfs) -> libc::c_int
= libc::statfs64;
} else {
type type_of_statfs = libc::statfs;
const LIBC_FSTATFS: unsafe extern fn
const LIBC_FSTATFS: unsafe extern "C" fn
(fd: libc::c_int, buf: *mut type_of_statfs) -> libc::c_int
= libc::fstatfs;
const LIBC_STATFS: unsafe extern fn
const LIBC_STATFS: unsafe extern "C" fn
(path: *const libc::c_char, buf: *mut type_of_statfs) -> libc::c_int
= libc::statfs;
}
@@ -51,11 +54,13 @@ pub struct Statfs(type_of_statfs);
type fs_type_t = u32;
#[cfg(target_os = "android")]
type fs_type_t = libc::c_ulong;
#[cfg(all(target_os = "linux", target_arch = "s390x", not(target_env = "musl")))]
#[cfg(all(
target_os = "linux",
target_arch = "s390x",
not(target_env = "musl")
))]
type fs_type_t = libc::c_uint;
#[cfg(all(target_os = "linux", target_env = "musl"))]
type fs_type_t = libc::c_ulong;
#[cfg(all(target_os = "linux", target_env = "ohos"))]
#[cfg(all(target_os = "linux", any(target_env = "musl", target_env = "ohos")))]
type fs_type_t = libc::c_ulong;
#[cfg(all(target_os = "linux", target_env = "uclibc"))]
type fs_type_t = libc::c_int;
@@ -69,6 +74,8 @@ type fs_type_t = libc::c_int;
))
))]
type fs_type_t = libc::__fsword_t;
#[cfg(target_os = "cygwin")]
type fs_type_t = libc::c_long;
/// Describes the file system type as known by the operating system.
#[cfg(any(
@@ -81,6 +88,7 @@ type fs_type_t = libc::__fsword_t;
target_os = "linux",
not(any(target_arch = "s390x", target_env = "musl"))
),
target_os = "cygwin",
))]
#[derive(Eq, Copy, Clone, PartialEq, Debug)]
pub struct FsType(pub fs_type_t);
@@ -299,7 +307,7 @@ impl Statfs {
}
/// Magic code defining system type
#[cfg(not(linux_android))]
#[cfg(not(any(linux_android, target_os = "cygwin")))]
pub fn filesystem_type_name(&self) -> &str {
let c_str = unsafe { CStr::from_ptr(self.0.f_fstypename.as_ptr()) };
c_str.to_str().unwrap()
@@ -318,7 +326,11 @@ impl Statfs {
}
/// Optimal transfer block size
#[cfg(all(target_os = "linux", target_arch = "s390x", not(target_env = "musl")))]
#[cfg(all(
target_os = "linux",
target_arch = "s390x",
not(target_env = "musl")
))]
pub fn optimal_transfer_size(&self) -> u32 {
self.0.f_bsize
}
@@ -373,7 +385,11 @@ impl Statfs {
/// Size of a block
// f_bsize on linux: https://github.com/torvalds/linux/blob/master/fs/nfs/super.c#L471
#[cfg(all(target_os = "linux", target_arch = "s390x", not(target_env = "musl")))]
#[cfg(all(
target_os = "linux",
target_arch = "s390x",
not(target_env = "musl")
))]
pub fn block_size(&self) -> u32 {
self.0.f_bsize
}
@@ -427,7 +443,7 @@ impl Statfs {
}
/// Size of a block
#[cfg(target_os = "dragonfly")]
#[cfg(any(target_os = "dragonfly", target_os = "cygwin"))]
pub fn block_size(&self) -> libc::c_long {
self.0.f_bsize
}
@@ -454,7 +470,11 @@ impl Statfs {
}
/// Maximum length of filenames
#[cfg(all(target_os = "linux", target_arch = "s390x", not(target_env = "musl")))]
#[cfg(all(
target_os = "linux",
target_arch = "s390x",
not(target_env = "musl")
))]
pub fn maximum_name_length(&self) -> u32 {
self.0.f_namelen
}
@@ -504,7 +524,7 @@ impl Statfs {
}
/// Total data blocks in filesystem
#[cfg(target_os = "dragonfly")]
#[cfg(any(target_os = "dragonfly", target_os = "cygwin"))]
pub fn blocks(&self) -> libc::c_long {
self.0.f_blocks
}
@@ -528,7 +548,7 @@ impl Statfs {
}
/// Free blocks in filesystem
#[cfg(target_os = "dragonfly")]
#[cfg(any(target_os = "dragonfly", target_os = "cygwin"))]
pub fn blocks_free(&self) -> libc::c_long {
self.0.f_bfree
}
@@ -546,7 +566,7 @@ impl Statfs {
}
/// Free blocks available to unprivileged user
#[cfg(target_os = "dragonfly")]
#[cfg(any(target_os = "dragonfly", target_os = "cygwin"))]
pub fn blocks_available(&self) -> libc::c_long {
self.0.f_bavail
}
@@ -576,7 +596,7 @@ impl Statfs {
}
/// Total file nodes in filesystem
#[cfg(target_os = "dragonfly")]
#[cfg(any(target_os = "dragonfly", target_os = "cygwin"))]
pub fn files(&self) -> libc::c_long {
self.0.f_files
}
@@ -599,7 +619,7 @@ impl Statfs {
}
/// Free file nodes in filesystem
#[cfg(target_os = "dragonfly")]
#[cfg(any(target_os = "dragonfly", target_os = "cygwin"))]
pub fn files_free(&self) -> libc::c_long {
self.0.f_ffree
}
@@ -625,6 +645,7 @@ impl Statfs {
impl Debug for Statfs {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut ds = f.debug_struct("Statfs");
#[cfg(not(target_os = "cygwin"))]
ds.field("optimal_transfer_size", &self.optimal_transfer_size());
ds.field("block_size", &self.block_size());
ds.field("blocks", &self.blocks());

View File

@@ -418,7 +418,7 @@ libc_enum! {
VEOL,
VEOL2,
VERASE,
#[cfg(any(freebsdlike, solarish))]
#[cfg(any(freebsdlike, target_os = "illumos"))]
VERASE2,
VINTR,
VKILL,
@@ -431,7 +431,7 @@ libc_enum! {
#[cfg(not(target_os = "haiku"))]
VREPRINT,
VSTART,
#[cfg(any(bsd, solarish))]
#[cfg(any(bsd, target_os = "illumos"))]
VSTATUS,
VSTOP,
VSUSP,
@@ -461,7 +461,7 @@ impl SpecialCharacterIndices {
}
pub use libc::NCCS;
#[cfg(any(linux_android, target_os = "aix", bsd))]
#[cfg(any(bsd, linux_android, target_os = "aix", target_os = "solaris"))]
pub use libc::_POSIX_VDISABLE;
libc_bitflags! {
@@ -667,7 +667,7 @@ libc_bitflags! {
ECHOK;
ECHO;
ECHONL;
#[cfg(not(target_os = "redox"))]
#[cfg(not(any(target_os = "redox", target_os = "cygwin")))]
ECHOPRT;
#[cfg(not(target_os = "redox"))]
ECHOCTL;
@@ -676,14 +676,14 @@ libc_bitflags! {
#[cfg(bsd)]
ALTWERASE;
IEXTEN;
#[cfg(not(any(target_os = "redox", target_os = "haiku", target_os = "aix")))]
#[cfg(not(any(target_os = "redox", target_os = "haiku", target_os = "aix", target_os = "cygwin")))]
EXTPROC;
TOSTOP;
#[cfg(not(target_os = "redox"))]
FLUSHO;
#[cfg(bsd)]
NOKERNINFO;
#[cfg(not(target_os = "redox"))]
#[cfg(not(any(target_os = "redox", target_os = "cygwin")))]
PENDIN;
NOFLSH;
}

View File

@@ -1,4 +1,7 @@
#[cfg_attr(target_env = "musl", allow(deprecated))]
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)]
// https://github.com/rust-lang/libc/issues/1848
pub use libc::{suseconds_t, time_t};
use libc::{timespec, timeval};
@@ -253,7 +256,10 @@ impl PartialOrd for TimeSpec {
impl TimeValLike for TimeSpec {
#[inline]
#[cfg_attr(target_env = "musl", allow(deprecated))]
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)]
// https://github.com/rust-lang/libc/issues/1848
fn seconds(seconds: i64) -> TimeSpec {
assert!(
@@ -286,7 +292,10 @@ impl TimeValLike for TimeSpec {
/// Makes a new `TimeSpec` with given number of nanoseconds.
#[inline]
#[cfg_attr(target_env = "musl", allow(deprecated))]
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)]
// https://github.com/rust-lang/libc/issues/1848
fn nanoseconds(nanoseconds: i64) -> TimeSpec {
let (secs, nanos) = div_mod_floor_64(nanoseconds, NANOS_PER_SEC);
@@ -340,7 +349,10 @@ impl TimeSpec {
TimeSpec::new(0, libc::UTIME_NOW as timespec_tv_nsec_t);
/// Construct a new `TimeSpec` from its components
#[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)] // https://github.com/rust-lang/libc/issues/1848
pub const fn new(seconds: time_t, nanoseconds: timespec_tv_nsec_t) -> Self {
let mut ts = zero_init_timespec();
ts.tv_sec = seconds;
@@ -356,7 +368,10 @@ impl TimeSpec {
}
}
#[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)] // https://github.com/rust-lang/libc/issues/1848
pub const fn tv_sec(&self) -> time_t {
self.0.tv_sec
}
@@ -365,7 +380,10 @@ impl TimeSpec {
self.0.tv_nsec
}
#[cfg_attr(target_env = "musl", allow(deprecated))]
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)]
// https://github.com/rust-lang/libc/issues/1848
pub const fn from_duration(duration: Duration) -> Self {
let mut ts = zero_init_timespec();
@@ -506,7 +524,10 @@ impl TimeValLike for TimeVal {
(TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&seconds),
"TimeVal out of bounds; seconds={seconds}"
);
#[cfg_attr(target_env = "musl", allow(deprecated))]
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)]
// https://github.com/rust-lang/libc/issues/1848
TimeVal(timeval {
tv_sec: seconds as time_t,
@@ -531,7 +552,10 @@ impl TimeValLike for TimeVal {
(TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&secs),
"TimeVal out of bounds"
);
#[cfg_attr(target_env = "musl", allow(deprecated))]
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)]
// https://github.com/rust-lang/libc/issues/1848
TimeVal(timeval {
tv_sec: secs as time_t,
@@ -549,7 +573,10 @@ impl TimeValLike for TimeVal {
(TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&secs),
"TimeVal out of bounds"
);
#[cfg_attr(target_env = "musl", allow(deprecated))]
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)]
// https://github.com/rust-lang/libc/issues/1848
TimeVal(timeval {
tv_sec: secs as time_t,
@@ -586,7 +613,10 @@ impl TimeValLike for TimeVal {
impl TimeVal {
/// Construct a new `TimeVal` from its components
#[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)] // https://github.com/rust-lang/libc/issues/1848
pub const fn new(seconds: time_t, microseconds: suseconds_t) -> Self {
Self(timeval {
tv_sec: seconds,
@@ -602,7 +632,10 @@ impl TimeVal {
}
}
#[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)] // https://github.com/rust-lang/libc/issues/1848
pub const fn tv_sec(&self) -> time_t {
self.0.tv_sec
}

View File

@@ -95,7 +95,7 @@ impl Timer {
/// There are 3 types of alarms you can set:
///
/// - one shot: the alarm will trigger once after the specified amount of
/// time.
/// time.
/// Example: I want an alarm to go off in 60s and then disable itself.
///
/// - interval: the alarm will trigger every specified interval of time.

View File

@@ -58,6 +58,12 @@ impl FromRawFd for TimerFd {
}
}
impl From<TimerFd> for OwnedFd {
fn from(value: TimerFd) -> Self {
value.fd
}
}
libc_enum! {
/// The type of the clock used to mark the progress of the timer. For more
/// details on each kind of clock, please refer to [timerfd_create(2)](https://man7.org/linux/man-pages/man2/timerfd_create.2.html).
@@ -113,7 +119,7 @@ impl TimerFd {
/// There are 3 types of alarms you can set:
///
/// - one shot: the alarm will trigger once after the specified amount of
/// time.
/// time.
/// Example: I want an alarm to go off in 60s and then disable itself.
///
/// - interval: the alarm will trigger every specified interval of time.
@@ -208,7 +214,7 @@ 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.as_fd().as_raw_fd(), &mut [0u8; 8]) {
while let Err(e) = read(&self.fd, &mut [0u8; 8]) {
if e == Errno::ECANCELED {
break;
}
@@ -219,4 +225,16 @@ impl TimerFd {
Ok(())
}
/// Constructs a `TimerFd` wrapping an existing `OwnedFd`.
///
/// # Safety
///
/// `OwnedFd` is a valid `TimerFd`.
pub unsafe fn from_owned_fd(fd: OwnedFd) -> Self {
Self {
fd
}
}
}

View File

@@ -53,7 +53,7 @@ pub fn readv<Fd: AsFd>(fd: Fd, iov: &mut [IoSliceMut<'_>]) -> Result<usize> {
/// or an error occurs. The file offset is not changed.
///
/// See also: [`writev`](fn.writev.html) and [`pwrite`](fn.pwrite.html)
#[cfg(not(any(target_os = "redox", target_os = "haiku", target_os = "solaris")))]
#[cfg(not(any(target_os = "redox", target_os = "haiku", target_os = "solaris", target_os = "cygwin")))]
pub fn pwritev<Fd: AsFd>(
fd: Fd,
iov: &[IoSlice<'_>],
@@ -82,7 +82,7 @@ pub fn pwritev<Fd: AsFd>(
/// changed.
///
/// See also: [`readv`](fn.readv.html) and [`pread`](fn.pread.html)
#[cfg(not(any(target_os = "redox", target_os = "haiku", target_os = "solaris")))]
#[cfg(not(any(target_os = "redox", target_os = "haiku", target_os = "solaris", target_os = "cygwin")))]
// 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)]

293
third_party/rust/nix/src/syslog.rs vendored Normal file
View File

@@ -0,0 +1,293 @@
//! Interfaces for controlling system log.
use crate::{NixPath, Result};
use std::ffi::OsStr;
use std::ptr;
/// Logging options of subsequent [`syslog`] calls can be set by calling [`openlog`].
///
/// The parameter `ident` is a string that will be prepended to every message. The `logopt`
/// argument specifies logging options. The `facility` parameter encodes a default facility to be
/// assigned to all messages that do not have an explicit facility encoded.
//
// On Linux, the `ident` argument needs to have static lifetime according to the
// man page:
//
// The argument ident in the call of openlog() is probably stored as-is. Thus,
// if the string it points to is changed, syslog() may start prepending the changed
// string, and if the string it points to ceases to exist, the results are
// undefined. Most portable is to use a string constant.
#[cfg(target_os = "linux")]
pub fn openlog(
ident: Option<&'static std::ffi::CStr>,
logopt: LogFlags,
facility: Facility,
) -> Result<()> {
let logopt = logopt.bits();
let facility = facility as libc::c_int;
match ident {
None => unsafe {
libc::openlog(ptr::null(), logopt, facility);
},
Some(ident) => ident.with_nix_path(|ident| unsafe {
libc::openlog(ident.as_ptr(), logopt, facility);
})?,
}
Ok(())
}
/// Logging options of subsequent [`syslog`] calls can be set by calling [`openlog`].
///
/// The parameter `ident` is a string that will be prepended to every message. The `logopt`
/// argument specifies logging options. The `facility` parameter encodes a default facility to be
/// assigned to all messages that do not have an explicit facility encoded.
#[cfg(not(target_os = "linux"))]
pub fn openlog<S: AsRef<OsStr> + ?Sized>(
ident: Option<&S>,
logopt: LogFlags,
facility: Facility,
) -> Result<()> {
let logopt = logopt.bits();
let facility = facility as libc::c_int;
match ident.map(OsStr::new) {
None => unsafe {
libc::openlog(ptr::null(), logopt, facility);
},
Some(ident) => ident.with_nix_path(|ident| unsafe {
libc::openlog(ident.as_ptr(), logopt, facility);
})?,
}
Ok(())
}
/// Writes message to the system message logger.
///
/// The message is then written to the system console, log files, logged-in users, or forwarded
/// to other machines as appropriate.
///
/// # Examples
///
/// ```rust
/// use nix::syslog::{openlog, syslog, Facility, LogFlags, Severity, Priority};
///
/// let priority = Priority::new(Severity::LOG_EMERG, Facility::LOG_USER);
/// syslog(priority, "Hello, nix!").unwrap();
///
/// // use `format!` to format the message
/// let name = "syslog";
/// syslog(priority, &format!("Hello, {name}!")).unwrap();
/// ```
pub fn syslog<P, S>(priority: P, message: &S) -> Result<()>
where
P: Into<Priority>,
S: AsRef<OsStr> + ?Sized,
{
let priority = priority.into();
let formatter = OsStr::new("%s");
let message = OsStr::new(message);
formatter.with_nix_path(|formatter| {
message.with_nix_path(|message| unsafe {
libc::syslog(priority.0, formatter.as_ptr(), message.as_ptr())
})
})??;
Ok(())
}
/// Set the process-wide priority mask to `mask` and return the previous mask
/// value.
///
/// Calls to `syslog()` with a priority level not set in `mask` are ignored. The
/// default is to log all priorities.
///
/// If the `mask` argument is `None`, the current logmask is not modified, this
/// can be used to query the current log mask.
pub fn setlogmask(mask: Option<LogMask>) -> LogMask {
let mask = match mask {
Some(mask) => mask.0,
None => 0,
};
let prev_mask = unsafe { libc::setlogmask(mask) };
LogMask(prev_mask)
}
/// Closes the log file.
pub fn closelog() {
unsafe { libc::closelog() }
}
/// System log priority mask.
#[derive(Debug, Clone, Copy)]
pub struct LogMask(libc::c_int);
impl LogMask {
/// Creates a mask of all priorities up to and including `priority`.
#[doc(alias("LOG_UPTO"))]
pub fn up_to(priority: Severity) -> Self {
let pri = priority as libc::c_int;
Self((1 << (pri + 1)) - 1)
}
/// Creates a mask for the specified priority.
#[doc(alias("LOG_MASK"))]
pub fn of_priority(priority: Severity) -> Self {
let pri = priority as libc::c_int;
Self(1 << pri)
}
/// Returns if the mask for the specified `priority` is set.
pub fn contains(&self, priority: Severity) -> bool {
let priority = Self::of_priority(priority);
let and_result = *self & priority;
and_result.0 != 0
}
}
impl std::ops::BitOr for LogMask {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
Self(self.0 | rhs.0)
}
}
impl std::ops::BitAnd for LogMask {
type Output = Self;
fn bitand(self, rhs: Self) -> Self::Output {
Self(self.0 & rhs.0)
}
}
impl std::ops::BitOrAssign for LogMask {
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
impl std::ops::BitAndAssign for LogMask {
fn bitand_assign(&mut self, rhs: Self) {
self.0 &= rhs.0;
}
}
impl std::ops::Not for LogMask {
type Output = Self;
fn not(self) -> Self::Output {
Self(!self.0)
}
}
/// The priority for a log message.
#[derive(Debug, Clone, Copy)]
pub struct Priority(libc::c_int);
impl Priority {
/// Create a new priority from a facility and severity level.
pub fn new(severity: Severity, facility: Facility) -> Self {
let priority = (facility as libc::c_int) | (severity as libc::c_int);
Priority(priority)
}
}
impl From<Severity> for Priority {
fn from(severity: Severity) -> Self {
let priority = severity as libc::c_int;
Priority(priority)
}
}
libc_bitflags! {
/// Options for system logging.
pub struct LogFlags: libc::c_int {
/// Log the process id with each message: useful for identifying instantiations of
/// daemons.
LOG_PID;
/// If syslog() cannot pass the message to syslogd(8) it will attempt to write the
/// message to the console ("/dev/console").
LOG_CONS;
/// The converse of [`LOG_NDELAY`][LogFlags::LOG_NDELAY]; opening of the connection is
/// delayed until `syslog` is called.
///
/// This is the default, and need not be specified.
LOG_ODELAY;
/// Open the connection to syslogd(8) immediately. Normally the open is delayed until
/// the first message is logged. Useful for programs that need to manage the order in
/// which file descriptors are allocated.
LOG_NDELAY;
/// Write the message to standard error output as well to the system log.
#[cfg(not(any(solarish, target_os = "redox", target_os = "cygwin")))]
LOG_PERROR;
}
}
libc_enum! {
/// Severity levels for log messages.
#[repr(i32)]
#[non_exhaustive]
pub enum Severity {
/// A panic condition.
///
/// This is normally broadcast to all users.
LOG_EMERG,
/// A condition that should be corrected immediately, such as a corrupted system database.
LOG_ALERT,
/// Critical conditions, e.g., hard device errors.
LOG_CRIT,
/// Errors.
LOG_ERR,
/// Warning messages.
LOG_WARNING,
/// Conditions that are not error conditions, but should possibly be handled specially.
LOG_NOTICE,
/// Informational messages.
LOG_INFO,
/// Messages that contain information normally of use only when debugging a program.
LOG_DEBUG,
}
}
libc_enum! {
/// Facilities for log messages.
#[repr(i32)]
#[non_exhaustive]
pub enum Facility {
/// Messages generated by the kernel.
///
/// These cannot be generated by any user processes.
LOG_KERN,
/// Messages generated by random user processes.
///
/// This is the default facility identifier if none is specified.
LOG_USER,
/// The mail system.
LOG_MAIL,
/// System daemons, such as routed(8), that are not provided for explicitly by other facilities.
LOG_DAEMON,
/// The authorization system: login(1), su(1), getty(8), etc.
LOG_AUTH,
/// Messages generated internally by syslogd(8).
LOG_SYSLOG,
/// The line printer spooling system: cups-lpd(8), cupsd(8), etc.
LOG_LPR,
/// The network news system.
LOG_NEWS,
/// The uucp system.
LOG_UUCP,
/// Reserved for local use.
LOG_LOCAL0,
/// Reserved for local use.
LOG_LOCAL1,
/// Reserved for local use.
LOG_LOCAL2,
/// Reserved for local use.
LOG_LOCAL3,
/// Reserved for local use.
LOG_LOCAL4,
/// Reserved for local use.
LOG_LOCAL5,
/// Reserved for local use.
LOG_LOCAL6,
/// Reserved for local use.
LOG_LOCAL7,
}
}

View File

@@ -122,7 +122,10 @@ impl ClockId {
#[cfg(any(
target_os = "emscripten",
target_os = "fuchsia",
all(target_os = "linux", target_env = "musl")
all(
target_os = "linux",
any(target_env = "musl", target_env = "ohos")
)
))]
pub const CLOCK_SGI_CYCLE: ClockId = ClockId(libc::CLOCK_SGI_CYCLE);
/// International Atomic Time.

View File

@@ -1,9 +1,9 @@
#[cfg(not(target_env = "musl"))]
#[cfg(not(any(target_env = "musl", target_env = "ohos")))]
use crate::errno::Errno;
use crate::sys::signal::SigSet;
#[cfg(not(target_env = "musl"))]
#[cfg(not(any(target_env = "musl", target_env = "ohos")))]
use crate::Result;
#[cfg(not(target_env = "musl"))]
#[cfg(not(any(target_env = "musl", target_env = "ohos")))]
use std::mem;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
@@ -12,7 +12,7 @@ pub struct UContext {
}
impl UContext {
#[cfg(not(target_env = "musl"))]
#[cfg(not(any(target_env = "musl", target_env = "ohos")))]
pub fn get() -> Result<UContext> {
let mut context = mem::MaybeUninit::<libc::ucontext_t>::uninit();
let res = unsafe { libc::getcontext(context.as_mut_ptr()) };
@@ -23,7 +23,7 @@ impl UContext {
})
}
#[cfg(not(target_env = "musl"))]
#[cfg(not(any(target_env = "musl", target_env = "ohos")))]
pub fn set(&self) -> Result<()> {
let res = unsafe {
libc::setcontext(&self.context as *const libc::ucontext_t)

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,10 @@ mod test_signal;
#[cfg(any(
target_os = "freebsd",
apple_targets,
all(target_os = "linux", not(target_env = "uclibc")),
all(
target_os = "linux",
not(any(target_env = "uclibc", target_env = "ohos"))
),
target_os = "netbsd"
))]
mod test_aio;
@@ -16,7 +19,8 @@ mod test_aio;
target_os = "redox",
target_os = "fuchsia",
target_os = "haiku",
target_os = "hurd"
target_os = "hurd",
target_os = "cygwin"
)))]
mod test_ioctl;
#[cfg(not(target_os = "redox"))]
@@ -82,3 +86,13 @@ mod test_statfs;
target_os = "haiku"
)))]
mod test_resource;
// This test module should be enabled for both linux_android and freebsd, but
// the `memfd_create(2)` symbol is not available under Linux QEMU,
//
// https://github.com/nix-rust/nix/actions/runs/9427112650/job/25970870477
//
// and I haven't found a way to stop the linker from linking that symbol, so
// only enable this for FreeBSD for now.
#[cfg(target_os = "freebsd")]
mod test_memfd;

View File

@@ -6,6 +6,7 @@
#[cfg(all(
not(target_env = "musl"),
not(target_env = "uclibc"),
not(target_env = "ohos"),
any(
target_os = "linux",
apple_targets,

View File

@@ -1,5 +1,5 @@
use libc::intptr_t;
use nix::sys::event::{EventFilter, EventFlag, FilterFlag, KEvent};
use nix::sys::event::{EvFlags, EventFilter, FilterFlag, KEvent};
#[test]
fn test_struct_kevent() {
@@ -11,7 +11,7 @@ fn test_struct_kevent() {
let actual = KEvent::new(
0xdead_beef,
EventFilter::EVFILT_READ,
EventFlag::EV_ONESHOT | EventFlag::EV_ADD,
EvFlags::EV_ONESHOT | EvFlags::EV_ADD,
FilterFlag::NOTE_CHILD | FilterFlag::NOTE_EXIT,
data,
udata,
@@ -32,7 +32,7 @@ fn test_kevent_filter() {
let actual = KEvent::new(
0xdead_beef,
EventFilter::EVFILT_READ,
EventFlag::EV_ONESHOT | EventFlag::EV_ADD,
EvFlags::EV_ONESHOT | EvFlags::EV_ADD,
FilterFlag::NOTE_CHILD | FilterFlag::NOTE_EXIT,
0x1337,
udata,

View File

@@ -1,5 +1,6 @@
use crate::*;
use nix::errno::Errno;
use nix::fcntl::AT_FDCWD;
use nix::sys::fanotify::{
EventFFlags, Fanotify, FanotifyResponse, InitFlags, MarkFlags, MaskFlags,
Response,
@@ -36,7 +37,7 @@ fn test_fanotify_notifications() {
.mark(
MarkFlags::FAN_MARK_ADD,
MaskFlags::FAN_OPEN | MaskFlags::FAN_MODIFY | MaskFlags::FAN_CLOSE,
None,
AT_FDCWD,
Some(&tempfile),
)
.unwrap();
@@ -99,7 +100,7 @@ fn test_fanotify_responses() {
.mark(
MarkFlags::FAN_MARK_ADD,
MaskFlags::FAN_OPEN_PERM,
None,
AT_FDCWD,
Some(&tempfile),
)
.unwrap();
@@ -182,7 +183,7 @@ fn test_fanotify_overflow() {
.mark(
MarkFlags::FAN_MARK_ADD,
MaskFlags::FAN_OPEN,
None,
AT_FDCWD,
Some(&tempfile),
)
.unwrap();

View File

@@ -0,0 +1,20 @@
#[test]
fn test_memfd_create() {
use nix::sys::memfd::memfd_create;
use nix::sys::memfd::MFdFlags;
use nix::unistd::lseek;
use nix::unistd::read;
use nix::unistd::{write, Whence};
let fd =
memfd_create("test_memfd_create_name", MFdFlags::MFD_CLOEXEC).unwrap();
let contents = b"hello";
assert_eq!(write(&fd, contents).unwrap(), 5);
lseek(&fd, 0, Whence::SeekSet).unwrap();
let mut buf = vec![0_u8; contents.len()];
assert_eq!(read(&fd, &mut buf).unwrap(), 5);
assert_eq!(contents, buf.as_slice());
}

View File

@@ -119,3 +119,82 @@ fn test_mremap_shrink() {
// The first KB should still be accessible and have the old data in it.
assert_eq!(slice[ONE_K - 1], 0xFF);
}
#[test]
#[cfg(target_os = "linux")]
fn test_mremap_dontunmap() {
use nix::libc::size_t;
use nix::sys::mman::{mremap, MRemapFlags};
use std::num::NonZeroUsize;
use std::ptr::NonNull;
const ONE_K: size_t = 1024;
let one_k_non_zero = NonZeroUsize::new(ONE_K).unwrap();
let slice: &mut [u8] = unsafe {
let mem = mmap_anonymous(
None,
one_k_non_zero,
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
MapFlags::MAP_PRIVATE,
)
.unwrap();
std::slice::from_raw_parts_mut(mem.as_ptr().cast(), ONE_K)
};
// because we do not unmap `slice`, `old_size` and `new_size`
// need to be equal or `EINVAL` is set.
let _new_slice: &mut [u8] = unsafe {
let mem = mremap(
NonNull::from(&mut slice[..]).cast(),
ONE_K,
ONE_K,
MRemapFlags::MREMAP_MAYMOVE | MRemapFlags::MREMAP_DONTUNMAP,
None,
)
.unwrap();
std::slice::from_raw_parts_mut(mem.cast().as_ptr(), 10 * ONE_K)
};
}
#[test]
#[cfg(target_os = "linux")]
fn test_madv_wipeonfork() {
use nix::libc::size_t;
use nix::sys::mman::{madvise, MmapAdvise};
use nix::unistd::{fork, ForkResult};
use std::num::NonZeroUsize;
const ONE_K: size_t = 1024;
let ten_one_k = NonZeroUsize::new(10 * ONE_K).unwrap();
let slice: &mut [u8] = unsafe {
let mem = mmap_anonymous(
None,
ten_one_k,
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
MapFlags::MAP_PRIVATE,
)
.unwrap();
madvise(mem, ONE_K, MmapAdvise::MADV_WIPEONFORK)
.expect("madvise failed");
std::slice::from_raw_parts_mut(mem.as_ptr().cast(), ONE_K)
};
slice[ONE_K - 1] = 0xFF;
let _m = crate::FORK_MTX.lock();
unsafe {
let res = fork().expect("fork failed");
match res {
ForkResult::Child => {
// that s the whole point of MADV_WIPEONFORK
assert_eq!(slice[ONE_K - 1], 0x00);
libc::_exit(0);
}
ForkResult::Parent { child } => {
nix::sys::signal::kill(child, nix::sys::signal::SIGTERM)
.unwrap();
let _ = nix::sys::wait::wait().unwrap();
}
}
}
}

View File

@@ -89,16 +89,20 @@ mod test_prctl {
#[cfg_attr(qemu, ignore)]
#[test]
fn test_get_set_timerslack() {
let original = prctl::get_timerslack().unwrap();
let original = prctl::get_timerslack().unwrap() as libc::c_ulong;
let slack = 60_000;
prctl::set_timerslack(slack).unwrap();
let res = prctl::get_timerslack().unwrap();
assert_eq!(slack, res as u64);
let res = prctl::get_timerslack().unwrap() as libc::c_ulong;
assert_eq!(slack, res);
prctl::set_timerslack(original as u64).unwrap();
prctl::set_timerslack(original).unwrap();
}
// Loongarch need to use a newer QEMU that disabled these PRCTL subcodes/methods.
// https://github.com/qemu/qemu/commit/220717a6f46a99031a5b1af964bbf4dec1310440
// So we should ignore them when testing in QEMU environments.
#[cfg_attr(all(qemu, target_arch = "loongarch64"), ignore)]
#[test]
fn test_disable_enable_perf_events() {
prctl::task_perf_events_disable().unwrap();
@@ -112,6 +116,10 @@ mod test_prctl {
assert!(no_new_privs);
}
// Loongarch need to use a newer QEMU that disabled these PRCTL subcodes/methods
// https://github.com/qemu/qemu/commit/220717a6f46a99031a5b1af964bbf4dec1310440
// So we should ignore them when testing in QEMU environments.
#[cfg_attr(all(qemu, target_arch = "loongarch64"), ignore)]
#[test]
fn test_get_set_thp_disable() {
let original = prctl::get_thp_disable().unwrap();
@@ -123,7 +131,12 @@ mod test_prctl {
prctl::set_thp_disable(original).unwrap();
}
// Ignore this test under QEMU, as it started failing after updating the Linux CI
// runner image, for reasons unknown.
//
// See: https://github.com/nix-rust/nix/issues/2418
#[test]
#[cfg_attr(qemu, ignore)]
fn test_set_vma_anon_name() {
use nix::errno::Errno;
use nix::sys::mman;

View File

@@ -1,13 +1,23 @@
use nix::sys::pthread::*;
#[cfg(any(target_env = "musl", target_os = "redox"))]
#[cfg(any(
target_env = "musl",
target_os = "redox",
target_env = "ohos",
target_os = "cygwin"
))]
#[test]
fn test_pthread_self() {
let tid = pthread_self();
assert!(!tid.is_null());
}
#[cfg(not(any(target_env = "musl", target_os = "redox")))]
#[cfg(not(any(
target_env = "musl",
target_os = "redox",
target_env = "ohos",
target_os = "cygwin"
)))]
#[test]
fn test_pthread_self() {
let tid = pthread_self();

View File

@@ -179,12 +179,17 @@ fn test_ptrace_interrupt() {
// ptrace::{setoptions, getregs} are only available in these platforms
#[cfg(all(
target_os = "linux",
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64",
all(
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64"
)
),
all(target_env = "musl", target_arch = "aarch64")
)
))]
#[test]
@@ -292,12 +297,17 @@ fn test_ptrace_syscall() {
#[cfg(all(
target_os = "linux",
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64",
all(
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64"
)
),
all(target_env = "musl", target_arch = "aarch64")
)
))]
#[test]
@@ -371,3 +381,32 @@ fn test_ptrace_regsets() {
}
}
}
#[cfg(all(target_os = "linux", target_env = "gnu"))]
#[test]
fn test_ptrace_syscall_info() {
use nix::sys::ptrace;
use nix::sys::wait::{waitpid, WaitStatus};
use nix::unistd::fork;
use nix::unistd::ForkResult::*;
require_capability!("test_ptrace_syscall_info", CAP_SYS_PTRACE);
let _m = crate::FORK_MTX.lock();
match unsafe { fork() }.expect("Error: Fork Failed") {
Child => {
ptrace::traceme().unwrap();
std::thread::sleep(std::time::Duration::from_millis(1000));
unsafe {
::libc::_exit(0);
}
}
Parent { child } => loop {
if let Ok(WaitStatus::Exited(_, 0)) = waitpid(child, None) {
break;
}
let si = ptrace::syscall_info(child).unwrap();
assert!(si.op >= libc::PTRACE_SYSCALL_INFO_ENTRY);
},
}
}

View File

@@ -10,6 +10,7 @@ use nix::sys::resource::{getrusage, UsageWho};
/// to put the new soft limit in effect, and then getrlimit() once more to ensure the limits have
/// been updated.
#[test]
#[cfg_attr(target_os = "cygwin", ignore)]
pub fn test_resource_limits_nofile() {
let (mut soft_limit, hard_limit) =
getrlimit(Resource::RLIMIT_NOFILE).unwrap();

View File

@@ -200,7 +200,7 @@ pub fn test_path_to_sock_addr() {
let expect: &[c_char] =
unsafe { slice::from_raw_parts(path.as_ptr().cast(), path.len()) };
assert_eq!(unsafe { &(*addr.as_ptr()).sun_path[..8] }, expect);
assert_eq!(unsafe { &(&(*addr.as_ptr()).sun_path)[..8] }, expect);
assert_eq!(addr.path(), Some(actual));
}
@@ -321,12 +321,13 @@ pub fn test_socketpair() {
.unwrap();
write(&fd1, b"hello").unwrap();
let mut buf = [0; 5];
read(fd2.as_raw_fd(), &mut buf).unwrap();
read(&fd2, &mut buf).unwrap();
assert_eq!(&buf[..], b"hello");
}
#[test]
#[cfg_attr(target_os = "cygwin", ignore)]
pub fn test_recvmsg_sockaddr_un() {
use nix::sys::socket::{
self, bind, socket, AddressFamily, MsgFlags, SockFlag, SockType,
@@ -841,6 +842,7 @@ pub fn test_recvmsg_ebadf() {
// 2.12.0. https://bugs.launchpad.net/qemu/+bug/1701808
#[cfg_attr(qemu, ignore)]
#[test]
#[cfg_attr(target_os = "cygwin", ignore)]
pub fn test_scm_rights() {
use nix::sys::socket::{
recvmsg, sendmsg, socketpair, AddressFamily, ControlMessage,
@@ -908,14 +910,24 @@ pub fn test_scm_rights() {
// Ensure that the received file descriptor works
write(&w, b"world").unwrap();
let mut buf = [0u8; 5];
read(received_r.as_raw_fd(), &mut buf).unwrap();
// SAFETY:
// should be safe since we don't use it after close
let borrowed_received_r =
unsafe { std::os::fd::BorrowedFd::borrow_raw(received_r) };
read(borrowed_received_r, &mut buf).unwrap();
assert_eq!(&buf[..], b"world");
close(received_r).unwrap();
}
// Disable the test on emulated platforms due to not enabled support of AF_ALG in QEMU from rust cross
// 1. Disable the test on emulated platforms due to not enabled support of
// AF_ALG in QEMU from rust cross
// 2. Disable the test on aarch64/Linux CI because bind() fails with ENOENT
// https://github.com/nix-rust/nix/issues/1352
#[cfg(linux_android)]
#[cfg_attr(qemu, ignore)]
#[cfg_attr(
any(qemu, all(target_os = "linux", target_arch = "aarch64")),
ignore
)]
#[test]
pub fn test_af_alg_cipher() {
use nix::sys::socket::sockopt::AlgSetKey;
@@ -926,11 +938,6 @@ pub fn test_af_alg_cipher() {
use nix::unistd::read;
use std::io::IoSlice;
skip_if_cirrus!("Fails for an unknown reason Cirrus CI. Bug #1352");
// Travis's seccomp profile blocks AF_ALG
// https://docs.docker.com/engine/security/seccomp/
skip_if_seccomp!(test_af_alg_cipher);
let alg_type = "skcipher";
let alg_name = "ctr-aes-aesni";
// 256-bits secret key
@@ -975,8 +982,12 @@ pub fn test_af_alg_cipher() {
// allocate buffer for encrypted data
let mut encrypted = vec![0u8; payload_len];
// SAFETY:
// should be safe since session_socket won't be closed before the use of this borrowed one
let borrowed_session_socket =
unsafe { std::os::fd::BorrowedFd::borrow_raw(session_socket) };
let num_bytes =
read(session_socket.as_raw_fd(), &mut encrypted).expect("read encrypt");
read(borrowed_session_socket, &mut encrypted).expect("read encrypt");
assert_eq!(num_bytes, payload_len);
let iov = IoSlice::new(&encrypted);
@@ -998,8 +1009,12 @@ pub fn test_af_alg_cipher() {
// allocate buffer for decrypted data
let mut decrypted = vec![0u8; payload_len];
// SAFETY:
// should be safe since session_socket won't be closed before the use of this borrowed one
let borrowed_session_socket =
unsafe { std::os::fd::BorrowedFd::borrow_raw(session_socket) };
let num_bytes =
read(session_socket.as_raw_fd(), &mut decrypted).expect("read decrypt");
read(borrowed_session_socket, &mut decrypted).expect("read decrypt");
assert_eq!(num_bytes, payload_len);
assert_eq!(decrypted, payload);
@@ -1087,8 +1102,12 @@ pub fn test_af_alg_aead() {
// allocate buffer for encrypted data
let mut encrypted =
vec![0u8; (assoc_size as usize) + payload_len + auth_size];
// SAFETY:
// should be safe since session_socket won't be closed before the use of this borrowed one
let borrowed_session_socket =
unsafe { std::os::fd::BorrowedFd::borrow_raw(session_socket) };
let num_bytes =
read(session_socket.as_raw_fd(), &mut encrypted).expect("read encrypt");
read(borrowed_session_socket, &mut encrypted).expect("read encrypt");
assert_eq!(num_bytes, payload_len + auth_size + (assoc_size as usize));
for i in 0..assoc_size {
@@ -1122,10 +1141,16 @@ pub fn test_af_alg_aead() {
// authentication tag memory is only needed in the output buffer for encryption
// and in the input buffer for decryption.
// Do not block on read, as we may have fewer bytes than buffer size
fcntl(session_socket, FcntlArg::F_SETFL(OFlag::O_NONBLOCK))
// SAFETY:
//
// `session_socket` will be valid for the lifetime of this test
// TODO: remove this workaround when accept(2) becomes I/O-safe.
let borrowed_fd =
unsafe { std::os::fd::BorrowedFd::borrow_raw(session_socket) };
fcntl(borrowed_fd, FcntlArg::F_SETFL(OFlag::O_NONBLOCK))
.expect("fcntl non_blocking");
let num_bytes =
read(session_socket.as_raw_fd(), &mut decrypted).expect("read decrypt");
let num_bytes = read(borrowed_fd, &mut decrypted).expect("read decrypt");
assert!(num_bytes >= payload_len + (assoc_size as usize));
assert_eq!(
@@ -1304,6 +1329,7 @@ pub fn test_sendmsg_ipv4sendsrcaddr() {
// 2.12.0. https://bugs.launchpad.net/qemu/+bug/1701808
#[cfg_attr(qemu, ignore)]
#[test]
#[cfg_attr(target_os = "cygwin", ignore)]
fn test_scm_rights_single_cmsg_multiple_fds() {
use nix::sys::socket::{
recvmsg, sendmsg, ControlMessage, ControlMessageOwned, MsgFlags,
@@ -1615,7 +1641,11 @@ fn test_impl_scm_credentials_and_rights(
// Ensure that the received file descriptor works
write(&w, b"world").unwrap();
let mut buf = [0u8; 5];
read(received_r.as_raw_fd(), &mut buf).unwrap();
// SAFETY:
// It should be safe if we don't use this BorrowedFd after close.
let received_r_borrowed =
unsafe { std::os::fd::BorrowedFd::borrow_raw(received_r) };
read(received_r_borrowed, &mut buf).unwrap();
assert_eq!(&buf[..], b"world");
close(received_r).unwrap();
@@ -1659,17 +1689,28 @@ pub fn test_named_unixdomain() {
let s3 = accept(s1.as_raw_fd()).expect("accept failed");
// SAFETY:
// It should be safe considering that s3 will be open within this test
let s3 = unsafe { std::os::fd::BorrowedFd::borrow_raw(s3) };
let mut buf = [0; 5];
read(s3.as_raw_fd(), &mut buf).unwrap();
read(s3, &mut buf).unwrap();
thr.join().unwrap();
assert_eq!(&buf[..], b"hello");
}
#[test]
pub fn test_listen_maxbacklog() {
use nix::sys::socket::Backlog;
assert!(Backlog::new(libc::SOMAXCONN).is_ok());
}
#[test]
pub fn test_listen_wrongbacklog() {
use nix::sys::socket::Backlog;
#[cfg(not(target_os = "cygwin"))]
assert!(Backlog::new(libc::SOMAXCONN + 1).is_err());
assert!(Backlog::new(-2).is_err());
}
@@ -2544,6 +2585,191 @@ fn test_recvmsg_rxq_ovfl() {
assert_eq!(drop_counter, 1);
}
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
// qemu doesn't seem to be emulating this correctly in these architectures
#[cfg_attr(
all(
qemu,
any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
)
),
ignore
)]
#[test]
pub fn test_ip_tos_udp() {
use nix::sys::socket::ControlMessageOwned;
use nix::sys::socket::{
bind, recvmsg, sendmsg, setsockopt, socket, sockopt, ControlMessage,
MsgFlags, SockFlag, SockType, SockaddrIn,
};
let sock_addr = SockaddrIn::from_str("127.0.0.1:6909").unwrap();
let rsock = socket(
AddressFamily::Inet,
SockType::Datagram,
SockFlag::empty(),
None,
)
.unwrap();
setsockopt(&rsock, sockopt::IpRecvTos, &true).unwrap();
bind(rsock.as_raw_fd(), &sock_addr).unwrap();
let sbuf = [0u8; 2048];
let iov1 = [std::io::IoSlice::new(&sbuf)];
let mut rbuf = [0u8; 2048];
let mut iov2 = [std::io::IoSliceMut::new(&mut rbuf)];
let mut rcmsg = cmsg_space!(libc::c_int);
let ssock = socket(
AddressFamily::Inet,
SockType::Datagram,
SockFlag::empty(),
None,
)
.expect("send socket failed");
setsockopt(&ssock, sockopt::Ipv4Tos, &20).unwrap();
// Test the sendmsg control message and check the received packet has the same TOS.
let scmsg = ControlMessage::Ipv4Tos(&20);
sendmsg(
ssock.as_raw_fd(),
&iov1,
&[scmsg],
MsgFlags::empty(),
Some(&sock_addr),
)
.unwrap();
// TODO: this test is weak, but testing for the actual ToS value results in sporadic
// failures in CI where the ToS in the message header is not the one set by the
// sender, so for now the test only checks for the presence of the ToS in the message
// header.
let mut tc = None;
let recv = recvmsg::<()>(
rsock.as_raw_fd(),
&mut iov2,
Some(&mut rcmsg),
MsgFlags::empty(),
)
.unwrap();
for c in recv.cmsgs().unwrap() {
if let ControlMessageOwned::Ipv4Tos(t) = c {
tc = Some(t);
}
}
assert!(tc.is_some());
}
#[cfg(target_os = "linux")]
// qemu doesn't seem to be emulating this correctly in these architectures
#[cfg_attr(
all(
qemu,
any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
)
),
ignore
)]
#[cfg(feature = "net")]
#[test]
pub fn test_ipv6_tclass_udp() {
use nix::sys::socket::ControlMessageOwned;
use nix::sys::socket::{
bind, recvmsg, sendmsg, setsockopt, socket, sockopt, ControlMessage,
MsgFlags, SockFlag, SockType, SockaddrIn6,
};
let std_sa = SocketAddrV6::from_str("[::1]:6902").unwrap();
let sock_addr: SockaddrIn6 = SockaddrIn6::from(std_sa);
let rsock = socket(
AddressFamily::Inet6,
SockType::Datagram,
SockFlag::empty(),
None,
)
.unwrap();
setsockopt(&rsock, sockopt::Ipv6RecvTClass, &true).unwrap();
// This bind call with IPV6_RECVTCLASS fails on the Linux aarch64 target with EADDRNOTAVAIL,
// so the test will only run if `bind` does not return an error..
if bind(rsock.as_raw_fd(), &sock_addr).is_ok() {
let sbuf = [0u8; 2048];
let iov1 = [std::io::IoSlice::new(&sbuf)];
let mut rbuf = [0u8; 2048];
let mut iov2 = [std::io::IoSliceMut::new(&mut rbuf)];
let mut rcmsg = cmsg_space!(libc::c_int);
let ssock = socket(
AddressFamily::Inet6,
SockType::Datagram,
SockFlag::empty(),
None,
)
.expect("send socket failed");
setsockopt(&ssock, sockopt::Ipv6TClass, &10).unwrap();
sendmsg(
ssock.as_raw_fd(),
&iov1,
&[],
MsgFlags::empty(),
Some(&sock_addr),
)
.unwrap();
let mut tc = None;
let recv = recvmsg::<()>(
rsock.as_raw_fd(),
&mut iov2,
Some(&mut rcmsg),
MsgFlags::empty(),
)
.unwrap();
for c in recv.cmsgs().unwrap() {
if let ControlMessageOwned::Ipv6TClass(t) = c {
tc = Some(t);
}
}
assert_eq!(tc, Some(10));
let scmsg = ControlMessage::Ipv6TClass(&20);
sendmsg(
ssock.as_raw_fd(),
&iov1,
&[scmsg],
MsgFlags::empty(),
Some(&sock_addr),
)
.unwrap();
let mut tc = None;
let recv = recvmsg::<()>(
rsock.as_raw_fd(),
&mut iov2,
Some(&mut rcmsg),
MsgFlags::empty(),
)
.unwrap();
for c in recv.cmsgs().unwrap() {
if let ControlMessageOwned::Ipv6TClass(t) = c {
tc = Some(t);
}
}
assert_eq!(tc, Some(20));
}
}
#[cfg(linux_android)]
mod linux_errqueue {
use super::FromStr;
@@ -2693,7 +2919,7 @@ mod linux_errqueue {
)
.unwrap();
// The sent message / destination associated with the error is returned:
assert_eq!(msg.bytes, MESSAGE_CONTENTS.as_bytes().len());
assert_eq!(msg.bytes, MESSAGE_CONTENTS.len());
// recvmsg(2): "The original destination address of the datagram that caused the error is
// supplied via msg_name;" however, this is not literally true. E.g., an earlier version
// of this test used 0.0.0.0 (::0) as the destination address, which was mutated into
@@ -2930,7 +3156,12 @@ fn can_use_cmsg_space() {
let _ = cmsg_space!(u8);
}
#[cfg(not(any(linux_android, target_os = "redox", target_os = "haiku")))]
#[cfg(not(any(
linux_android,
target_os = "redox",
target_os = "haiku",
target_os = "cygwin"
)))]
#[test]
fn can_open_routing_socket() {
use nix::sys::socket::{socket, AddressFamily, SockFlag, SockType};

View File

@@ -4,7 +4,7 @@ use nix::sys::socket::{
getsockopt, setsockopt, socket, sockopt, AddressFamily, SockFlag,
SockProtocol, SockType,
};
use rand::{thread_rng, Rng};
use rand::{rng, Rng};
use std::os::unix::io::{AsRawFd, FromRawFd, OwnedFd};
// NB: FreeBSD supports LOCAL_PEERCRED for SOCK_SEQPACKET, but OSX does not.
@@ -66,6 +66,36 @@ pub fn test_local_peer_pid() {
assert_eq!(pid, std::process::id() as _);
}
#[cfg(apple_targets)]
#[test]
pub fn test_local_peer_token() {
use nix::sys::socket::{audit_token_t, socketpair};
#[link(name = "bsm", kind = "dylib")]
extern "C" {
/// Extract the process ID from an `audit_token_t`, used to identify
/// Mach tasks and senders of Mach messages as subjects of the audit
/// system.
///
/// - `atoken`: The Mach audit token.
/// - Returns: The process ID extracted from the Mach audit token.
fn audit_token_to_pid(atoken: audit_token_t) -> libc::pid_t;
}
let (fd1, _fd2) = socketpair(
AddressFamily::Unix,
SockType::Stream,
None,
SockFlag::empty(),
)
.unwrap();
let audit_token = getsockopt(&fd1, sockopt::LocalPeerToken).unwrap();
assert_eq!(
unsafe { audit_token_to_pid(audit_token) },
std::process::id() as _
);
}
#[cfg(target_os = "linux")]
#[test]
fn is_so_mark_functional() {
@@ -94,7 +124,7 @@ fn test_so_buf() {
SockProtocol::Udp,
)
.unwrap();
let bufsize: usize = thread_rng().gen_range(4096..131_072);
let bufsize: usize = rng().random_range(4096..131_072);
setsockopt(&fd, sockopt::SndBuf, &bufsize).unwrap();
let actual = getsockopt(&fd, sockopt::SndBuf).unwrap();
assert!(actual >= bufsize);
@@ -129,11 +159,11 @@ fn test_so_listen_q_limit() {
}
#[test]
#[cfg_attr(target_os = "cygwin", ignore)]
fn test_so_tcp_maxseg() {
use nix::sys::socket::{
accept, bind, connect, getsockname, listen, Backlog, SockaddrIn,
};
use nix::unistd::write;
use std::net::SocketAddrV4;
use std::str::FromStr;
@@ -153,14 +183,12 @@ fn test_so_tcp_maxseg() {
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
// than `segsize`
let segsize: u32 = 873;
assert!(initial < segsize);
cfg_if! {
if #[cfg(linux_android)] {
let segsize: u32 = 873;
assert!(initial < segsize);
setsockopt(&rsock, sockopt::TcpMaxSeg, &segsize).unwrap();
} else {
assert!(initial < 700);
}
}
@@ -172,20 +200,38 @@ fn test_so_tcp_maxseg() {
SockProtocol::Tcp,
)
.unwrap();
connect(ssock.as_raw_fd(), &sock_addr).unwrap();
let rsess = accept(rsock.as_raw_fd()).unwrap();
let rsess = unsafe { OwnedFd::from_raw_fd(rsess) };
write(&rsess, b"hello").unwrap();
let actual = getsockopt(&ssock, sockopt::TcpMaxSeg).unwrap();
// Actual max segment size takes header lengths into account, max IPv4 options (60 bytes) + max
// TCP options (40 bytes) are subtracted from the requested maximum as a lower boundary.
cfg_if! {
if #[cfg(linux_android)] {
assert!((segsize - 100) <= actual);
assert!(actual <= segsize);
if #[cfg(apple_targets)] {
// on apple targets (and unlike linux), we can only set the MSS on a *connected*
// socket. Also, the same MSS can't be read using getsockopt from the other end.
assert_ne!(segsize, getsockopt(&rsess, sockopt::TcpMaxSeg).unwrap());
setsockopt(&rsess, sockopt::TcpMaxSeg, &segsize).unwrap();
assert_eq!(segsize, getsockopt(&rsess, sockopt::TcpMaxSeg).unwrap());
assert_ne!(segsize, getsockopt(&ssock, sockopt::TcpMaxSeg).unwrap());
setsockopt(&ssock, sockopt::TcpMaxSeg, &segsize).unwrap();
assert_eq!(segsize, getsockopt(&ssock, sockopt::TcpMaxSeg).unwrap());
} else {
assert!(initial < actual);
assert!(536 < actual);
use nix::unistd::write;
write(&rsess, b"hello").unwrap();
let actual = getsockopt(&ssock, sockopt::TcpMaxSeg).unwrap();
// Actual max segment size takes header lengths into account, max IPv4 options (60 bytes) + max
// TCP options (40 bytes) are subtracted from the requested maximum as a lower boundary.
if cfg!(linux_android) {
assert!((segsize - 100) <= actual);
assert!(actual <= segsize);
} else {
assert!(initial < actual);
assert!(536 < actual);
}
}
}
}
@@ -211,6 +257,8 @@ fn test_so_type_unknown() {
use nix::errno::Errno;
require_capability!("test_so_type", CAP_NET_RAW);
// SOCK_PACKET is deprecated, but since it is used for testing here, we allow it
#[allow(deprecated)]
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) };
@@ -219,16 +267,16 @@ fn test_so_type_unknown() {
}
// The CI doesn't supported getsockopt and setsockopt on emulated processors.
// It's believed that a QEMU issue, the tests run ok on a fully emulated system.
// Current CI just run the binary with QEMU but the Kernel remains the same as the host.
// It's believed to be a QEMU issue; the tests run ok on a fully emulated
// system. Current CI just runs the binary with QEMU but the kernel remains the
// same as the host.
// So the syscall doesn't work properly unless the kernel is also emulated.
#[test]
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
any(target_os = "freebsd", target_os = "linux")
))]
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
#[cfg_attr(qemu, ignore)]
fn test_tcp_congestion() {
use std::ffi::OsString;
use std::os::unix::ffi::OsStrExt;
let fd = socket(
AddressFamily::Inet,
@@ -239,6 +287,10 @@ fn test_tcp_congestion() {
.unwrap();
let val = getsockopt(&fd, sockopt::TcpCongestion).unwrap();
let bytes = val.as_os_str().as_bytes();
for b in bytes.iter() {
assert_ne!(*b, 0, "OsString should contain no embedded NULs: {val:?}");
}
setsockopt(&fd, sockopt::TcpCongestion, &val).unwrap();
setsockopt(
@@ -251,6 +303,35 @@ fn test_tcp_congestion() {
assert_eq!(getsockopt(&fd, sockopt::TcpCongestion).unwrap(), val);
}
#[test]
#[cfg(target_os = "freebsd")]
fn test_tcp_function_blk_alias() {
use std::ffi::CStr;
let fd = socket(
AddressFamily::Inet,
SockType::Stream,
SockFlag::empty(),
None,
)
.unwrap();
let tfs = getsockopt(&fd, sockopt::TcpFunctionBlk).unwrap();
let name = unsafe { CStr::from_ptr(tfs.function_set_name.as_ptr()) };
assert!(!name.to_bytes().is_empty());
let aliastfs = getsockopt(&fd, sockopt::TcpFunctionAlias).unwrap();
let aliasname =
unsafe { CStr::from_ptr(aliastfs.function_set_name.as_ptr()) };
// freebsd default tcp stack has no alias.
assert!(aliasname.to_bytes().is_empty());
// We can't know at compile time what options are available. So just test the setter by a
// no-op set.
// TODO: test if we can load for example BBR tcp stack kernel module.
setsockopt(&fd, sockopt::TcpFunctionBlk, &tfs).unwrap();
}
#[test]
#[cfg(linux_android)]
fn test_bindtodevice() {
@@ -469,7 +550,7 @@ fn test_so_priority() {
}
#[test]
#[cfg(target_os = "linux")]
#[cfg(any(linux_android, target_os = "freebsd"))]
fn test_ip_tos() {
let fd = socket(
AddressFamily::Inet,
@@ -479,12 +560,12 @@ 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::Ipv4Tos, &tos).unwrap();
assert_eq!(getsockopt(&fd, sockopt::Ipv4Tos).unwrap(), tos);
}
#[test]
#[cfg(target_os = "linux")]
#[cfg(any(linux_android, target_os = "freebsd"))]
// Disable the test under emulation because it fails in Cirrus-CI. Lack
// of QEMU support is suspected.
#[cfg_attr(qemu, ignore)]
@@ -683,6 +764,46 @@ fn can_get_peercred_on_unix_socket() {
assert_ne!(a_cred.pid(), 0);
}
#[cfg(target_os = "linux")]
fn pid_from_pidfd(pidfd: OwnedFd) -> u32 {
use std::fs::read_to_string;
let fd = pidfd.as_raw_fd();
let fdinfo = read_to_string(format!("/proc/self/fdinfo/{fd}")).unwrap();
let pidline = fdinfo.split('\n').find(|s| s.starts_with("Pid:")).unwrap();
pidline.split('\t').next_back().unwrap().parse().unwrap()
}
#[cfg(target_os = "linux")]
#[test]
fn can_get_peerpidfd_on_unix_socket() {
use nix::sys::socket::{socketpair, sockopt, SockFlag, SockType};
let (a, b) = socketpair(
AddressFamily::Unix,
SockType::Stream,
None,
SockFlag::empty(),
)
.unwrap();
match (
getsockopt(&a, sockopt::PeerPidfd),
getsockopt(&b, sockopt::PeerPidfd),
) {
(Ok(a_pidfd), Ok(b_pidfd)) => {
let a_pid = pid_from_pidfd(a_pidfd);
let b_pid = pid_from_pidfd(b_pidfd);
assert_eq!(a_pid, b_pid);
assert_ne!(a_pid, 0);
}
(Err(nix::Error::ENOPROTOOPT), Err(nix::Error::ENOPROTOOPT)) => {
// Pidfd can still be unsupported on some CI runners
}
(Err(err), _) | (_, Err(err)) => panic!("{err:?}"),
};
}
#[test]
fn is_socket_type_unix() {
use nix::sys::socket::{socketpair, sockopt, SockFlag, SockType};
@@ -877,3 +998,275 @@ fn test_reuseport_lb() {
setsockopt(&fd, sockopt::ReusePortLb, &true).unwrap();
assert!(getsockopt(&fd, sockopt::ReusePortLb).unwrap());
}
#[test]
#[cfg(any(linux_android, target_os = "freebsd"))]
fn test_ipv4_recv_ttl_opts() {
let fd = socket(
AddressFamily::Inet,
SockType::Stream,
SockFlag::empty(),
SockProtocol::Tcp,
)
.unwrap();
setsockopt(&fd, sockopt::Ipv4RecvTtl, &true)
.expect("setting IP_RECVTTL on an inet stream socket should succeed");
setsockopt(&fd, sockopt::Ipv4RecvTtl, &false)
.expect("unsetting IP_RECVTTL on an inet stream socket should succeed");
let fdd = socket(
AddressFamily::Inet,
SockType::Datagram,
SockFlag::empty(),
None,
)
.unwrap();
setsockopt(&fdd, sockopt::Ipv4RecvTtl, &true)
.expect("setting IP_RECVTTL on an inet datagram socket should succeed");
setsockopt(&fdd, sockopt::Ipv4RecvTtl, &false).expect(
"unsetting IP_RECVTTL on an inet datagram socket should succeed",
);
}
#[test]
#[cfg(any(linux_android, target_os = "freebsd"))]
fn test_ipv6_recv_hop_limit_opts() {
let fd = socket(
AddressFamily::Inet6,
SockType::Stream,
SockFlag::empty(),
SockProtocol::Tcp,
)
.unwrap();
setsockopt(&fd, sockopt::Ipv6RecvHopLimit, &true).expect(
"setting IPV6_RECVHOPLIMIT on an inet6 stream socket should succeed",
);
setsockopt(&fd, sockopt::Ipv6RecvHopLimit, &false).expect(
"unsetting IPV6_RECVHOPLIMIT on an inet6 stream socket should succeed",
);
let fdd = socket(
AddressFamily::Inet6,
SockType::Datagram,
SockFlag::empty(),
None,
)
.unwrap();
setsockopt(&fdd, sockopt::Ipv6RecvHopLimit, &true).expect(
"setting IPV6_RECVHOPLIMIT on an inet6 datagram socket should succeed",
);
setsockopt(&fdd, sockopt::Ipv6RecvHopLimit, &false).expect(
"unsetting IPV6_RECVHOPLIMIT on an inet6 datagram socket should succeed",
);
}
#[test]
#[cfg(any(linux_android, target_os = "freebsd"))]
fn test_ipv4_recv_tos_opts() {
let fd = socket(
AddressFamily::Inet,
SockType::Stream,
SockFlag::empty(),
SockProtocol::Tcp,
)
.unwrap();
setsockopt(&fd, sockopt::IpRecvTos, &true)
.expect("setting IP_RECVTOS on an inet stream socket should succeed");
setsockopt(&fd, sockopt::IpRecvTos, &false)
.expect("unsetting IP_RECVTOS on an inet stream socket should succeed");
let fdd = socket(
AddressFamily::Inet,
SockType::Datagram,
SockFlag::empty(),
None,
)
.unwrap();
setsockopt(&fdd, sockopt::IpRecvTos, &true)
.expect("setting IP_RECVTOS on an inet datagram socket should succeed");
setsockopt(&fdd, sockopt::IpRecvTos, &false).expect(
"unsetting IP_RECVTOS on an inet datagram socket should succeed",
);
}
#[test]
#[cfg(any(linux_android, target_os = "freebsd"))]
fn test_ipv6_recv_traffic_class_opts() {
let fd = socket(
AddressFamily::Inet6,
SockType::Stream,
SockFlag::empty(),
SockProtocol::Tcp,
)
.unwrap();
setsockopt(&fd, sockopt::Ipv6RecvTClass, &true).expect(
"setting IPV6_RECVTCLASS on an inet6 stream socket should succeed",
);
setsockopt(&fd, sockopt::Ipv6RecvTClass, &false).expect(
"unsetting IPV6_RECVTCLASS on an inet6 stream socket should succeed",
);
let fdd = socket(
AddressFamily::Inet6,
SockType::Datagram,
SockFlag::empty(),
None,
)
.unwrap();
setsockopt(&fdd, sockopt::Ipv6RecvTClass, &true).expect(
"setting IPV6_RECVTCLASS on an inet6 datagram socket should succeed",
);
setsockopt(&fdd, sockopt::Ipv6RecvTClass, &false).expect(
"unsetting IPV6_RECVTCLASS on an inet6 datagram socket should succeed",
);
}
#[cfg(apple_targets)]
#[test]
fn test_linger_sec() {
let fd = socket(
AddressFamily::Inet,
SockType::Stream,
SockFlag::empty(),
None,
)
.unwrap();
let set_linger = libc::linger {
l_onoff: 1,
l_linger: 1,
};
setsockopt(&fd, sockopt::LingerSec, &set_linger).unwrap();
let get_linger = getsockopt(&fd, sockopt::Linger).unwrap();
assert_eq!(get_linger.l_linger, set_linger.l_linger * 100);
}
/// Users should be able to define their own sockopts.
mod sockopt_impl {
use nix::sys::socket::{
getsockopt, setsockopt, socket, AddressFamily, SockFlag, SockProtocol,
SockType,
};
sockopt_impl!(KeepAlive, Both, libc::SOL_SOCKET, libc::SO_KEEPALIVE, bool);
#[test]
fn test_so_tcp_keepalive() {
let fd = socket(
AddressFamily::Inet,
SockType::Stream,
SockFlag::empty(),
SockProtocol::Tcp,
)
.unwrap();
setsockopt(&fd, KeepAlive, &true).unwrap();
assert!(getsockopt(&fd, KeepAlive).unwrap());
}
sockopt_impl!(
Linger,
Both,
libc::SOL_SOCKET,
libc::SO_LINGER,
libc::linger
);
#[test]
fn test_linger() {
let fd = socket(
AddressFamily::Inet,
SockType::Stream,
SockFlag::empty(),
None,
)
.unwrap();
let set_linger = libc::linger {
l_onoff: 1,
l_linger: 42,
};
setsockopt(&fd, Linger, &set_linger).unwrap();
let get_linger = getsockopt(&fd, Linger).unwrap();
assert_eq!(get_linger.l_linger, set_linger.l_linger);
}
}
#[cfg(solarish)]
#[test]
fn test_exclbind() {
use nix::errno::Errno;
use nix::sys::socket::{
bind, socket, AddressFamily, SockFlag, SockType, SockaddrIn,
};
use std::net::SocketAddrV4;
use std::str::FromStr;
let fd1 = socket(
AddressFamily::Inet,
SockType::Stream,
SockFlag::empty(),
None,
)
.unwrap();
let addr = SocketAddrV4::from_str("127.0.0.1:8081").unwrap();
let excl = true;
setsockopt(&fd1, sockopt::ExclBind, &excl).unwrap();
bind(fd1.as_raw_fd(), &SockaddrIn::from(addr)).unwrap();
assert_eq!(getsockopt(&fd1, sockopt::ExclBind), Ok(true));
let fd2 = socket(
AddressFamily::Inet,
SockType::Stream,
SockFlag::empty(),
None,
)
.unwrap();
assert_eq!(
bind(fd2.as_raw_fd(), &SockaddrIn::from(addr)),
Err(Errno::EADDRINUSE)
);
}
#[cfg(target_os = "illumos")]
#[test]
fn test_solfilter() {
use nix::errno::Errno;
let s = socket(
AddressFamily::Inet,
SockType::Stream,
SockFlag::empty(),
SockProtocol::Tcp,
)
.unwrap();
let data = std::ffi::OsStr::new("httpf");
let attach = sockopt::FilterAttach;
let detach = sockopt::FilterDetach;
// These 2 options won't work unless the needed kernel module is installed:
// https://github.com/nix-rust/nix/pull/2611#issuecomment-2750237782
//
// So we only test the binding here
assert_eq!(Err(Errno::ENOENT), setsockopt(&s, attach, data));
assert_eq!(Err(Errno::ENOENT), setsockopt(&s, detach, data));
}
#[cfg(target_os = "linux")]
#[test]
pub fn test_so_attach_reuseport_cbpf() {
let fd = socket(
AddressFamily::Inet6,
SockType::Datagram,
SockFlag::empty(),
None,
)
.unwrap();
setsockopt(&fd, sockopt::ReusePort, &true).unwrap();
setsockopt(&fd, sockopt::ReuseAddr, &true).unwrap();
let mut flt: [libc::sock_filter; 2] = unsafe { std::mem::zeroed() };
flt[0].code = (libc::BPF_LD | libc::BPF_W | libc::BPF_ABS) as u16;
flt[0].k = (libc::SKF_AD_OFF + libc::SKF_AD_CPU) as u32;
flt[1].code = (libc::BPF_RET | 0x10) as u16;
let fp = libc::sock_fprog {
len: flt.len() as u16,
filter: flt.as_mut_ptr(),
};
setsockopt(&fd, sockopt::AttachReusePortCbpf, &fp).unwrap_or_else(|e| {
assert_eq!(e, nix::errno::Errno::ENOPROTOOPT);
});
}

View File

@@ -1,3 +1,485 @@
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use std::fs;
use std::fs::File;
#[cfg(not(target_os = "redox"))]
use std::os::unix::fs::symlink;
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use std::os::unix::fs::PermissionsExt;
#[cfg(not(target_os = "redox"))]
use std::path::Path;
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use std::time::{Duration, UNIX_EPOCH};
use libc::mode_t;
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
use libc::{S_IFLNK, S_IFMT};
#[cfg(not(target_os = "redox"))]
use nix::errno::Errno;
#[cfg(not(target_os = "redox"))]
use nix::fcntl;
#[cfg(any(
target_os = "linux",
apple_targets,
target_os = "freebsd",
target_os = "netbsd"
))]
use nix::sys::stat::lutimes;
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use nix::sys::stat::utimensat;
#[cfg(not(target_os = "redox"))]
use nix::sys::stat::FchmodatFlags;
use nix::sys::stat::Mode;
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use nix::sys::stat::UtimensatFlags;
#[cfg(not(target_os = "redox"))]
use nix::sys::stat::{self};
use nix::sys::stat::{fchmod, stat};
#[cfg(not(target_os = "redox"))]
use nix::sys::stat::{fchmodat, mkdirat};
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use nix::sys::stat::{futimens, utimes};
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
use nix::sys::stat::FileStat;
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use nix::sys::time::{TimeSpec, TimeVal, TimeValLike};
#[cfg(not(target_os = "redox"))]
use nix::unistd::chdir;
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
use nix::Result;
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
fn assert_stat_results(stat_result: Result<FileStat>) {
let stats = stat_result.expect("stat call failed");
assert!(stats.st_dev > 0); // must be positive integer, exact number machine dependent
assert!(stats.st_ino > 0); // inode is positive integer, exact number machine dependent
assert!(stats.st_mode > 0); // must be positive integer
assert_eq!(stats.st_nlink, 1); // there links created, must be 1
assert_eq!(stats.st_size, 0); // size is 0 because we did not write anything to the file
assert!(stats.st_blksize > 0); // must be positive integer, exact number machine dependent
assert!(stats.st_blocks <= 16); // Up to 16 blocks can be allocated for a blank file
}
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
// (Android's st_blocks is ulonglong which is always non-negative.)
#[cfg_attr(target_os = "android", allow(unused_comparisons))]
#[allow(clippy::absurd_extreme_comparisons)] // Not absurd on all OSes
fn assert_lstat_results(stat_result: Result<FileStat>) {
let stats = stat_result.expect("stat call failed");
assert!(stats.st_dev > 0); // must be positive integer, exact number machine dependent
assert!(stats.st_ino > 0); // inode is positive integer, exact number machine dependent
assert!(stats.st_mode > 0); // must be positive integer
// st_mode is c_uint (u32 on Android) while S_IFMT is mode_t
// (u16 on Android), and that will be a compile error.
// On other platforms they are the same (either both are u16 or u32).
assert_eq!(
(stats.st_mode as usize) & (S_IFMT as usize),
S_IFLNK as usize
); // should be a link
assert_eq!(stats.st_nlink, 1); // there links created, must be 1
assert!(stats.st_size > 0); // size is > 0 because it points to another file
assert!(stats.st_blksize > 0); // must be positive integer, exact number machine dependent
// st_blocks depends on whether the machine's file system uses fast
// or slow symlinks, so just make sure it's not negative
assert!(stats.st_blocks >= 0);
}
#[test]
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
fn test_stat_and_fstat() {
use nix::sys::stat::fstat;
let tempdir = tempfile::tempdir().unwrap();
let filename = tempdir.path().join("foo.txt");
let file = File::create(&filename).unwrap();
let stat_result = stat(&filename);
assert_stat_results(stat_result);
let fstat_result = fstat(&file);
assert_stat_results(fstat_result);
}
#[test]
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
fn test_fstatat() {
let tempdir = tempfile::tempdir().unwrap();
let filename = tempdir.path().join("foo.txt");
File::create(&filename).unwrap();
let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
let result = stat::fstatat(&dirfd, &filename, fcntl::AtFlags::empty());
assert_stat_results(result);
}
#[test]
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
fn test_stat_fstat_lstat() {
use nix::sys::stat::{fstat, lstat};
let tempdir = tempfile::tempdir().unwrap();
let filename = tempdir.path().join("bar.txt");
let linkname = tempdir.path().join("barlink");
File::create(&filename).unwrap();
symlink("bar.txt", &linkname).unwrap();
let link = File::open(&linkname).unwrap();
// should be the same result as calling stat,
// since it's a regular file
let stat_result = stat(&filename);
assert_stat_results(stat_result);
let lstat_result = lstat(&linkname);
assert_lstat_results(lstat_result);
let fstat_result = fstat(&link);
assert_stat_results(fstat_result);
}
#[test]
fn test_fchmod() {
let tempdir = tempfile::tempdir().unwrap();
let filename = tempdir.path().join("foo.txt");
let file = File::create(&filename).unwrap();
let mut mode1 = Mode::empty();
mode1.insert(Mode::S_IRUSR);
mode1.insert(Mode::S_IWUSR);
fchmod(&file, mode1).unwrap();
let file_stat1 = stat(&filename).unwrap();
assert_eq!(file_stat1.st_mode as mode_t & 0o7777, mode1.bits());
let mut mode2 = Mode::empty();
mode2.insert(Mode::S_IROTH);
fchmod(&file, mode2).unwrap();
let file_stat2 = stat(&filename).unwrap();
assert_eq!(file_stat2.st_mode as mode_t & 0o7777, mode2.bits());
}
#[test]
#[cfg(not(target_os = "redox"))]
fn test_fchmodat() {
let _dr = crate::DirRestore::new();
let tempdir = tempfile::tempdir().unwrap();
let filename = "foo.txt";
let fullpath = tempdir.path().join(filename);
File::create(&fullpath).unwrap();
let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
let mut mode1 = Mode::empty();
mode1.insert(Mode::S_IRUSR);
mode1.insert(Mode::S_IWUSR);
fchmodat(&dirfd, filename, mode1, FchmodatFlags::FollowSymlink).unwrap();
let file_stat1 = stat(&fullpath).unwrap();
assert_eq!(file_stat1.st_mode as mode_t & 0o7777, mode1.bits());
chdir(tempdir.path()).unwrap();
let mut mode2 = Mode::empty();
mode2.insert(Mode::S_IROTH);
fchmodat(
fcntl::AT_FDCWD,
filename,
mode2,
FchmodatFlags::FollowSymlink,
)
.unwrap();
let file_stat2 = stat(&fullpath).unwrap();
assert_eq!(file_stat2.st_mode as mode_t & 0o7777, mode2.bits());
}
/// Asserts that the atime and mtime in a file's metadata match expected values.
///
/// The atime and mtime are expressed with a resolution of seconds because some file systems
/// (like macOS's HFS+) do not have higher granularity.
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn assert_times_eq(
exp_atime_sec: u64,
exp_mtime_sec: u64,
attr: &fs::Metadata,
) {
assert_eq!(
Duration::new(exp_atime_sec, 0),
attr.accessed().unwrap().duration_since(UNIX_EPOCH).unwrap()
);
assert_eq!(
Duration::new(exp_mtime_sec, 0),
attr.modified().unwrap().duration_since(UNIX_EPOCH).unwrap()
);
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_utimes() {
let tempdir = tempfile::tempdir().unwrap();
let fullpath = tempdir.path().join("file");
drop(File::create(&fullpath).unwrap());
utimes(&fullpath, &TimeVal::seconds(9990), &TimeVal::seconds(5550))
.unwrap();
assert_times_eq(9990, 5550, &fs::metadata(&fullpath).unwrap());
}
#[test]
#[cfg(any(
target_os = "linux",
apple_targets,
target_os = "freebsd",
target_os = "netbsd"
))]
fn test_lutimes() {
let tempdir = tempfile::tempdir().unwrap();
let target = tempdir.path().join("target");
let fullpath = tempdir.path().join("symlink");
drop(File::create(&target).unwrap());
symlink(&target, &fullpath).unwrap();
let exp_target_metadata = fs::symlink_metadata(&target).unwrap();
lutimes(&fullpath, &TimeVal::seconds(4560), &TimeVal::seconds(1230))
.unwrap();
assert_times_eq(4560, 1230, &fs::symlink_metadata(&fullpath).unwrap());
let target_metadata = fs::symlink_metadata(&target).unwrap();
assert_eq!(
exp_target_metadata.accessed().unwrap(),
target_metadata.accessed().unwrap(),
"atime of symlink target was unexpectedly modified"
);
assert_eq!(
exp_target_metadata.modified().unwrap(),
target_metadata.modified().unwrap(),
"mtime of symlink target was unexpectedly modified"
);
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_futimens() {
let tempdir = tempfile::tempdir().unwrap();
let fullpath = tempdir.path().join("file");
drop(File::create(&fullpath).unwrap());
let fd = fcntl::open(&fullpath, fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
futimens(&fd, &TimeSpec::seconds(10), &TimeSpec::seconds(20)).unwrap();
assert_times_eq(10, 20, &fs::metadata(&fullpath).unwrap());
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_utimensat() {
let _dr = crate::DirRestore::new();
let tempdir = tempfile::tempdir().unwrap();
let filename = "foo.txt";
let fullpath = tempdir.path().join(filename);
drop(File::create(&fullpath).unwrap());
let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
utimensat(
&dirfd,
filename,
&TimeSpec::seconds(12345),
&TimeSpec::seconds(678),
UtimensatFlags::FollowSymlink,
)
.unwrap();
assert_times_eq(12345, 678, &fs::metadata(&fullpath).unwrap());
chdir(tempdir.path()).unwrap();
utimensat(
fcntl::AT_FDCWD,
filename,
&TimeSpec::seconds(500),
&TimeSpec::seconds(800),
UtimensatFlags::FollowSymlink,
)
.unwrap();
assert_times_eq(500, 800, &fs::metadata(&fullpath).unwrap());
}
#[test]
#[cfg(not(target_os = "redox"))]
fn test_mkdirat_success_path() {
let tempdir = tempfile::tempdir().unwrap();
let filename = "example_subdir";
let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
mkdirat(&dirfd, filename, Mode::S_IRWXU).expect("mkdirat failed");
assert!(Path::exists(&tempdir.path().join(filename)));
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_mkdirat_success_mode() {
let expected_bits =
stat::SFlag::S_IFDIR.bits() | stat::Mode::S_IRWXU.bits();
let tempdir = tempfile::tempdir().unwrap();
let filename = "example_subdir";
let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
mkdirat(&dirfd, filename, Mode::S_IRWXU).expect("mkdirat failed");
let permissions = fs::metadata(tempdir.path().join(filename))
.unwrap()
.permissions();
let mode = permissions.mode();
assert_eq!(mode as mode_t, expected_bits)
}
#[test]
#[cfg(not(target_os = "redox"))]
fn test_mkdirat_fail() {
let tempdir = tempfile::tempdir().unwrap();
let not_dir_filename = "example_not_dir";
let filename = "example_subdir_dir";
let dirfd = fcntl::open(
&tempdir.path().join(not_dir_filename),
fcntl::OFlag::O_CREAT,
stat::Mode::empty(),
)
.unwrap();
let result = mkdirat(dirfd, filename, Mode::S_IRWXU).unwrap_err();
assert_eq!(result, Errno::ENOTDIR);
}
#[test]
#[cfg(not(any(
freebsdlike,
apple_targets,
target_os = "haiku",
target_os = "redox",
target_os = "solaris"
)))]
fn test_mknod() {
use stat::{lstat, mknod, SFlag};
let file_name = "test_file";
let tempdir = tempfile::tempdir().unwrap();
let target = tempdir.path().join(file_name);
mknod(&target, SFlag::S_IFREG, Mode::S_IRWXU, 0).unwrap();
let mode = lstat(&target).unwrap().st_mode as mode_t;
assert_eq!(mode & libc::S_IFREG, libc::S_IFREG);
assert_eq!(mode & libc::S_IRWXU, libc::S_IRWXU);
}
#[test]
#[cfg(not(any(
solarish,
freebsdlike,
apple_targets,
target_os = "haiku",
target_os = "redox"
)))]
fn test_mknodat() {
use fcntl::{AtFlags, OFlag};
use nix::dir::Dir;
use stat::{fstatat, mknodat, SFlag};
let file_name = "test_file";
let tempdir = tempfile::tempdir().unwrap();
let target_dir =
Dir::open(tempdir.path(), OFlag::O_DIRECTORY, Mode::S_IRWXU).unwrap();
mknodat(&target_dir, file_name, SFlag::S_IFREG, Mode::S_IRWXU, 0).unwrap();
let mode = fstatat(&target_dir, file_name, AtFlags::AT_SYMLINK_NOFOLLOW)
.unwrap()
.st_mode as mode_t;
assert_eq!(mode & libc::S_IFREG, libc::S_IFREG);
assert_eq!(mode & libc::S_IRWXU, libc::S_IRWXU);
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_futimens_unchanged() {
let tempdir = tempfile::tempdir().unwrap();
let fullpath = tempdir.path().join("file");
drop(File::create(&fullpath).unwrap());
let fd = fcntl::open(&fullpath, fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
let old_atime = fs::metadata(fullpath.as_path())
.unwrap()
.accessed()
.unwrap();
let old_mtime = fs::metadata(fullpath.as_path())
.unwrap()
.modified()
.unwrap();
futimens(&fd, &TimeSpec::UTIME_OMIT, &TimeSpec::UTIME_OMIT).unwrap();
let new_atime = fs::metadata(fullpath.as_path())
.unwrap()
.accessed()
.unwrap();
let new_mtime = fs::metadata(fullpath.as_path())
.unwrap()
.modified()
.unwrap();
assert_eq!(old_atime, new_atime);
assert_eq!(old_mtime, new_mtime);
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_utimensat_unchanged() {
let _dr = crate::DirRestore::new();
let tempdir = tempfile::tempdir().unwrap();
let filename = "foo.txt";
let fullpath = tempdir.path().join(filename);
drop(File::create(&fullpath).unwrap());
let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
let old_atime = fs::metadata(fullpath.as_path())
.unwrap()
.accessed()
.unwrap();
let old_mtime = fs::metadata(fullpath.as_path())
.unwrap()
.modified()
.unwrap();
utimensat(
&dirfd,
filename,
&TimeSpec::UTIME_OMIT,
&TimeSpec::UTIME_OMIT,
UtimensatFlags::NoFollowSymlink,
)
.unwrap();
let new_atime = fs::metadata(fullpath.as_path())
.unwrap()
.accessed()
.unwrap();
let new_mtime = fs::metadata(fullpath.as_path())
.unwrap()
.modified()
.unwrap();
assert_eq!(old_atime, new_atime);
assert_eq!(old_mtime, new_mtime);
}
// The conversion is not useless on all platforms.
#[allow(clippy::useless_conversion)]
#[cfg(target_os = "freebsd")]
@@ -7,23 +489,20 @@ fn test_chflags() {
sys::stat::{fstat, FileFlag},
unistd::chflags,
};
use std::os::unix::io::AsRawFd;
use tempfile::NamedTempFile;
let f = NamedTempFile::new().unwrap();
let initial = FileFlag::from_bits_truncate(
fstat(f.as_raw_fd()).unwrap().st_flags.into(),
);
let initial =
FileFlag::from_bits_truncate(fstat(&f).unwrap().st_flags.into());
// UF_OFFLINE is preserved by all FreeBSD file systems, but not interpreted
// in any way, so it's handy for testing.
let commanded = initial ^ FileFlag::UF_OFFLINE;
chflags(f.path(), commanded).unwrap();
let changed = FileFlag::from_bits_truncate(
fstat(f.as_raw_fd()).unwrap().st_flags.into(),
);
let changed =
FileFlag::from_bits_truncate(fstat(&f).unwrap().st_flags.into());
assert_eq!(commanded, changed);
}

View File

@@ -1,4 +1,4 @@
use std::os::unix::io::{AsFd, AsRawFd};
use std::os::unix::io::AsFd;
use tempfile::tempfile;
use nix::errno::Errno;
@@ -80,6 +80,7 @@ fn test_output_flags() {
// Test modifying local flags
#[test]
#[cfg(not(target_os = "solaris"))]
fn test_local_flags() {
// openpty uses ptname(3) internally
let _m = crate::PTSNAME_MTX.lock();
@@ -100,10 +101,10 @@ fn test_local_flags() {
let pty = openpty(None, &termios).unwrap();
// Set the master is in nonblocking mode or reading will never return.
let flags = fcntl::fcntl(pty.master.as_raw_fd(), fcntl::F_GETFL).unwrap();
let flags = fcntl::fcntl(&pty.master, fcntl::F_GETFL).unwrap();
let new_flags =
fcntl::OFlag::from_bits_truncate(flags) | fcntl::OFlag::O_NONBLOCK;
fcntl::fcntl(pty.master.as_raw_fd(), fcntl::F_SETFL(new_flags)).unwrap();
fcntl::fcntl(pty.master.as_fd(), fcntl::F_SETFL(new_flags)).unwrap();
// Write into the master
let string = "foofoofoo\r";
@@ -111,6 +112,6 @@ fn test_local_flags() {
// 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.as_raw_fd(), &mut buf).unwrap_err();
let read = read(&pty.master, &mut buf).unwrap_err();
assert_eq!(read, Errno::EAGAIN);
}

View File

@@ -1,10 +1,9 @@
use nix::sys::uio::*;
use nix::unistd::*;
use rand::distributions::Alphanumeric;
use rand::{thread_rng, Rng};
use rand::distr::Alphanumeric;
use rand::{rng, Rng};
use std::fs::OpenOptions;
use std::io::IoSlice;
use std::os::unix::io::AsRawFd;
use std::{cmp, iter};
#[cfg(not(target_os = "redox"))]
@@ -15,10 +14,12 @@ use tempfile::tempdir;
use tempfile::tempfile;
#[test]
// On Solaris sometimes wrtitev() returns EINVAL.
#[cfg(not(target_os = "solaris"))]
fn test_writev() {
let mut to_write = Vec::with_capacity(16 * 128);
for _ in 0..16 {
let s: String = thread_rng()
let s: String = rng()
.sample_iter(&Alphanumeric)
.map(char::from)
.take(128)
@@ -34,7 +35,7 @@ fn test_writev() {
let slice_len = if left <= 64 {
left
} else {
thread_rng().gen_range(64..cmp::min(256, left))
rng().random_range(64..cmp::min(256, left))
};
let b = &to_write[consumed..consumed + slice_len];
iovecs.push(IoSlice::new(b));
@@ -49,7 +50,7 @@ fn test_writev() {
let written = write_res.expect("couldn't write");
// Check whether we written all data
assert_eq!(to_write.len(), written);
let read_res = read(reader.as_raw_fd(), &mut read_buf[..]);
let read_res = read(&reader, &mut read_buf[..]);
let read = read_res.expect("couldn't read");
// Check we have read as much as we written
assert_eq!(read, written);
@@ -60,7 +61,7 @@ fn test_writev() {
#[test]
#[cfg(not(target_os = "redox"))]
fn test_readv() {
let s: String = thread_rng()
let s: String = rng()
.sample_iter(&Alphanumeric)
.map(char::from)
.take(128)
@@ -73,7 +74,7 @@ fn test_readv() {
let vec_len = if left <= 64 {
left
} else {
thread_rng().gen_range(64..cmp::min(256, left))
rng().random_range(64..cmp::min(256, left))
};
let v: Vec<u8> = iter::repeat(0u8).take(vec_len).collect();
storage.push(v);
@@ -143,7 +144,8 @@ fn test_pread() {
#[cfg(not(any(
target_os = "redox",
target_os = "haiku",
target_os = "solaris"
target_os = "solaris",
target_os = "cygwin"
)))]
fn test_pwritev() {
use std::io::Read;
@@ -182,7 +184,8 @@ fn test_pwritev() {
#[cfg(not(any(
target_os = "redox",
target_os = "haiku",
target_os = "solaris"
target_os = "solaris",
target_os = "cygwin"
)))]
fn test_preadv() {
use std::io::Write;
@@ -228,7 +231,6 @@ fn test_process_vm_readv() {
use nix::sys::signal::*;
use nix::sys::wait::*;
use nix::unistd::ForkResult::*;
use std::os::unix::io::AsRawFd;
require_capability!("test_process_vm_readv", CAP_SYS_PTRACE);
let _m = crate::FORK_MTX.lock();
@@ -242,7 +244,7 @@ fn test_process_vm_readv() {
Parent { child } => {
drop(w);
// wait for child
read(r.as_raw_fd(), &mut [0u8]).unwrap();
read(&r, &mut [0u8]).unwrap();
drop(r);
let ptr = vector.as_ptr() as usize;

View File

@@ -1,6 +1,6 @@
#[macro_use]
extern crate cfg_if;
#[cfg_attr(not(any(target_os = "redox", target_os = "haiku")), macro_use)]
#[cfg_attr(not(any(target_os = "redox")), macro_use)]
extern crate nix;
#[macro_use]
@@ -13,7 +13,11 @@ mod test_errno;
mod test_fcntl;
#[cfg(linux_android)]
mod test_kmod;
#[cfg(any(freebsdlike, target_os = "linux", target_os = "netbsd"))]
#[cfg(any(
freebsdlike,
all(target_os = "linux", not(target_env = "ohos")),
target_os = "netbsd"
))]
mod test_mq;
#[cfg(not(target_os = "redox"))]
mod test_net;
@@ -33,13 +37,23 @@ mod test_pty;
mod test_sched;
#[cfg(any(linux_android, freebsdlike, apple_targets, solarish))]
mod test_sendfile;
mod test_stat;
#[cfg(any(
target_os = "freebsd",
target_os = "haiku",
target_os = "linux",
target_os = "netbsd",
apple_targets
))]
mod test_spawn;
mod test_syslog;
mod test_time;
mod test_unistd;
use nix::unistd::{chdir, getcwd, read};
use parking_lot::{Mutex, RwLock, RwLockWriteGuard};
use std::os::unix::io::{AsFd, AsRawFd};
use std::os::unix::io::AsFd;
use std::path::PathBuf;
/// Helper function analogous to `std::io::Read::read_exact`, but for `Fd`s
@@ -48,13 +62,14 @@ fn read_exact<Fd: AsFd>(f: Fd, buf: &mut [u8]) {
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.as_fd().as_raw_fd(), remaining).unwrap();
len += read(&f, remaining).unwrap();
}
}
/// Any test that creates child processes or can be affected by child processes must grab this mutex, regardless
/// of what it does with those children. It must hold the mutex until the
/// child processes are waited upon.
/// Any test that creates child processes or can be affected by child processes
/// must grab this mutex, regardless of what it does with those children.
///
/// It must hold the mutex until the child processes are waited upon.
pub static FORK_MTX: Mutex<()> = Mutex::new(());
/// Any test that changes the process's current working directory must grab
/// the RwLock exclusively. Any process that cares about the current
@@ -76,7 +91,7 @@ struct DirRestore<'a> {
_g: RwLockWriteGuard<'a, ()>,
}
impl<'a> DirRestore<'a> {
impl DirRestore<'_> {
fn new() -> Self {
let guard = crate::CWD_LOCK.write();
DirRestore {
@@ -86,7 +101,7 @@ impl<'a> DirRestore<'a> {
}
}
impl<'a> Drop for DirRestore<'a> {
impl Drop for DirRestore<'_> {
fn drop(&mut self) {
let r = chdir(&self.d);
if std::thread::panicking() {

View File

@@ -16,7 +16,6 @@ fn flags() -> OFlag {
}
#[test]
#[allow(clippy::unnecessary_sort_by)] // False positive
fn read() {
let tmp = tempdir().unwrap();
File::create(tmp.path().join("foo")).unwrap();
@@ -57,9 +56,3 @@ fn rewind() {
assert_eq!(entries1, entries2);
assert_eq!(entries2, entries3);
}
#[cfg(not(target_os = "haiku"))]
#[test]
fn ebadf() {
assert_eq!(Dir::from_fd(-1).unwrap_err(), nix::Error::EBADF);
}

View File

@@ -21,7 +21,7 @@ use nix::fcntl::{renameat2, RenameFlags};
#[cfg(not(target_os = "redox"))]
use nix::sys::stat::Mode;
#[cfg(not(target_os = "redox"))]
use nix::unistd::{close, read};
use nix::unistd::read;
#[cfg(not(target_os = "redox"))]
use std::fs::File;
#[cfg(not(target_os = "redox"))]
@@ -45,7 +45,7 @@ fn test_openat() {
open(tmp.path().parent().unwrap(), OFlag::empty(), Mode::empty())
.unwrap();
let fd = openat(
Some(dirfd),
dirfd,
tmp.path().file_name().unwrap(),
OFlag::O_RDONLY,
Mode::empty(),
@@ -53,11 +53,8 @@ fn test_openat() {
.unwrap();
let mut buf = [0u8; 1024];
assert_eq!(4, read(fd, &mut buf).unwrap());
assert_eq!(4, read(&fd, &mut buf).unwrap());
assert_eq!(CONTENTS, &buf[0..4]);
close(fd).unwrap();
close(dirfd).unwrap();
}
#[test]
@@ -85,11 +82,8 @@ fn test_openat2() {
.unwrap();
let mut buf = [0u8; 1024];
assert_eq!(4, read(fd, &mut buf).unwrap());
assert_eq!(4, read(&fd, &mut buf).unwrap());
assert_eq!(CONTENTS, &buf[0..4]);
close(fd).unwrap();
close(dirfd).unwrap();
}
#[test]
@@ -115,7 +109,7 @@ fn test_openat2_forbidden() {
.flags(OFlag::O_RDONLY)
.resolve(ResolveFlag::RESOLVE_BENEATH),
);
assert_eq!(Err(Errno::EXDEV), res);
assert_eq!(res.unwrap_err(), Errno::EXDEV);
}
#[test]
@@ -129,13 +123,11 @@ fn test_renameat() {
let new_dir = tempfile::tempdir().unwrap();
let new_dirfd =
open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap();
renameat(&old_dirfd, "old", &new_dirfd, "new").unwrap();
assert_eq!(
renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap_err(),
renameat(&old_dirfd, "old", &new_dirfd, "new").unwrap_err(),
Errno::ENOENT
);
close(old_dirfd).unwrap();
close(new_dirfd).unwrap();
assert!(new_dir.path().join("new").exists());
}
@@ -158,27 +150,13 @@ fn test_renameat2_behaves_like_renameat_with_no_flags() {
let new_dir = tempfile::tempdir().unwrap();
let new_dirfd =
open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
renameat2(
Some(old_dirfd),
"old",
Some(new_dirfd),
"new",
RenameFlags::empty(),
)
.unwrap();
renameat2(&old_dirfd, "old", &new_dirfd, "new", RenameFlags::empty())
.unwrap();
assert_eq!(
renameat2(
Some(old_dirfd),
"old",
Some(new_dirfd),
"new",
RenameFlags::empty()
)
.unwrap_err(),
renameat2(&old_dirfd, "old", &new_dirfd, "new", RenameFlags::empty())
.unwrap_err(),
Errno::ENOENT
);
close(old_dirfd).unwrap();
close(new_dirfd).unwrap();
assert!(new_dir.path().join("new").exists());
}
@@ -210,9 +188,9 @@ fn test_renameat2_exchange() {
new_f.write_all(b"new").unwrap();
}
renameat2(
Some(old_dirfd),
&old_dirfd,
"old",
Some(new_dirfd),
&new_dirfd,
"new",
RenameFlags::RENAME_EXCHANGE,
)
@@ -225,8 +203,6 @@ fn test_renameat2_exchange() {
let mut old_f = File::open(&old_path).unwrap();
old_f.read_to_string(&mut buf).unwrap();
assert_eq!(buf, "new");
close(old_dirfd).unwrap();
close(new_dirfd).unwrap();
}
#[test]
@@ -252,17 +228,15 @@ fn test_renameat2_noreplace() {
File::create(new_path).unwrap();
assert_eq!(
renameat2(
Some(old_dirfd),
&old_dirfd,
"old",
Some(new_dirfd),
&new_dirfd,
"new",
RenameFlags::RENAME_NOREPLACE
)
.unwrap_err(),
Errno::EEXIST
);
close(old_dirfd).unwrap();
close(new_dirfd).unwrap();
assert!(new_dir.path().join("new").exists());
assert!(old_dir.path().join("old").exists());
}
@@ -280,7 +254,7 @@ fn test_readlink() {
assert_eq!(readlink(&dst).unwrap().to_str().unwrap(), expected_dir);
assert_eq!(
readlinkat(Some(dirfd), "b").unwrap().to_str().unwrap(),
readlinkat(dirfd, "b").unwrap().to_str().unwrap(),
expected_dir
);
}
@@ -301,7 +275,6 @@ fn test_readlink() {
#[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";
@@ -312,14 +285,7 @@ fn test_copy_file_range() {
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();
copy_file_range(&tmp1, Some(&mut from_offset), &tmp2, None, 3).unwrap();
let mut res: String = String::new();
tmp2.rewind().unwrap();
@@ -334,7 +300,6 @@ mod linux_android {
use libc::loff_t;
use std::io::prelude::*;
use std::io::IoSlice;
use std::os::unix::prelude::*;
use nix::fcntl::*;
use nix::unistd::{pipe, read, write};
@@ -360,7 +325,7 @@ mod linux_android {
assert_eq!(2, res);
let mut buf = [0u8; 1024];
assert_eq!(2, read(rd.as_raw_fd(), &mut buf).unwrap());
assert_eq!(2, read(&rd, &mut buf).unwrap());
assert_eq!(b"f1", &buf[0..2]);
assert_eq!(7, offset);
}
@@ -379,11 +344,11 @@ mod linux_android {
let mut buf = [0u8; 1024];
// Check the tee'd bytes are at rd2.
assert_eq!(2, read(rd2.as_raw_fd(), &mut buf).unwrap());
assert_eq!(2, read(&rd2, &mut buf).unwrap());
assert_eq!(b"ab", &buf[0..2]);
// Check all the bytes are still at rd1.
assert_eq!(3, read(rd1.as_raw_fd(), &mut buf).unwrap());
assert_eq!(3, read(&rd1, &mut buf).unwrap());
assert_eq!(b"abc", &buf[0..3]);
}
@@ -401,7 +366,7 @@ mod linux_android {
// Check the bytes can be read at rd.
let mut buf = [0u8; 32];
assert_eq!(6, read(rd.as_raw_fd(), &mut buf).unwrap());
assert_eq!(6, read(&rd, &mut buf).unwrap());
assert_eq!(b"abcdef", &buf[0..6]);
}
@@ -410,12 +375,11 @@ mod linux_android {
fn test_fallocate() {
let tmp = NamedTempFile::new().unwrap();
let fd = tmp.as_raw_fd();
fallocate(fd, FallocateFlags::empty(), 0, 100).unwrap();
fallocate(&tmp, FallocateFlags::empty(), 0, 100).unwrap();
// Check if we read exactly 100 bytes
let mut buf = [0u8; 200];
assert_eq!(100, read(fd, &mut buf).unwrap());
assert_eq!(100, read(&tmp, &mut buf).unwrap());
}
// The tests below are disabled for the listed targets
@@ -432,7 +396,6 @@ mod linux_android {
let tmp = NamedTempFile::new().unwrap();
let fd = tmp.as_raw_fd();
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
@@ -440,7 +403,7 @@ mod linux_android {
// skip the test.
skip!("/proc/locks does not work on overlayfs");
}
let inode = fstat(fd).expect("fstat failed").st_ino as usize;
let inode = fstat(&tmp).expect("fstat failed").st_ino as usize;
let mut flock: libc::flock = unsafe {
mem::zeroed() // required for Linux/mips
@@ -450,14 +413,15 @@ mod linux_android {
flock.l_start = 0;
flock.l_len = 0;
flock.l_pid = 0;
fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("write lock failed");
fcntl(&tmp, FcntlArg::F_OFD_SETLKW(&flock)).expect("write lock failed");
assert_eq!(
Some(("OFDLCK".to_string(), "WRITE".to_string())),
lock_info(inode)
);
flock.l_type = libc::F_UNLCK as libc::c_short;
fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("write unlock failed");
fcntl(&tmp, FcntlArg::F_OFD_SETLKW(&flock))
.expect("write unlock failed");
assert_eq!(None, lock_info(inode));
}
@@ -470,7 +434,6 @@ mod linux_android {
let tmp = NamedTempFile::new().unwrap();
let fd = tmp.as_raw_fd();
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
@@ -478,7 +441,7 @@ mod linux_android {
// skip the test.
skip!("/proc/locks does not work on overlayfs");
}
let inode = fstat(fd).expect("fstat failed").st_ino as usize;
let inode = fstat(&tmp).expect("fstat failed").st_ino as usize;
let mut flock: libc::flock = unsafe {
mem::zeroed() // required for Linux/mips
@@ -488,14 +451,15 @@ mod linux_android {
flock.l_start = 0;
flock.l_len = 0;
flock.l_pid = 0;
fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("read lock failed");
fcntl(&tmp, FcntlArg::F_OFD_SETLKW(&flock)).expect("read lock failed");
assert_eq!(
Some(("OFDLCK".to_string(), "READ".to_string())),
lock_info(inode)
);
flock.l_type = libc::F_UNLCK as libc::c_short;
fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("read unlock failed");
fcntl(&tmp, FcntlArg::F_OFD_SETLKW(&flock))
.expect("read unlock failed");
assert_eq!(None, lock_info(inode));
}
@@ -530,30 +494,23 @@ mod linux_android {
target_os = "freebsd"
))]
mod test_posix_fadvise {
use nix::errno::Errno;
use nix::fcntl::*;
use nix::unistd::pipe;
use std::os::unix::io::AsRawFd;
use tempfile::NamedTempFile;
#[test]
fn test_success() {
let tmp = NamedTempFile::new().unwrap();
let fd = tmp.as_raw_fd();
posix_fadvise(fd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED)
posix_fadvise(&tmp, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED)
.expect("posix_fadvise failed");
}
#[test]
fn test_errno() {
let (rd, _wr) = pipe().unwrap();
let res = posix_fadvise(
rd.as_raw_fd(),
0,
100,
PosixFadviseAdvice::POSIX_FADV_WILLNEED,
);
let res =
posix_fadvise(&rd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED);
assert_eq!(res, Err(Errno::ESPIPE));
}
}
@@ -570,15 +527,14 @@ mod test_posix_fallocate {
use nix::errno::Errno;
use nix::fcntl::*;
use nix::unistd::pipe;
use std::{io::Read, os::unix::io::AsRawFd};
use std::io::Read;
use tempfile::NamedTempFile;
#[test]
fn success() {
const LEN: usize = 100;
let mut tmp = NamedTempFile::new().unwrap();
let fd = tmp.as_raw_fd();
let res = posix_fallocate(fd, 0, LEN as libc::off_t);
let res = posix_fallocate(&tmp, 0, LEN as libc::off_t);
match res {
Ok(_) => {
let mut data = [1u8; LEN];
@@ -600,7 +556,7 @@ mod test_posix_fallocate {
#[test]
fn errno() {
let (rd, _wr) = pipe().unwrap();
let err = posix_fallocate(rd.as_raw_fd(), 0, 100).unwrap_err();
let err = posix_fallocate(&rd, 0, 100).unwrap_err();
match err {
Errno::EINVAL | Errno::ENODEV | Errno::ESPIPE | Errno::EBADF => (),
errno => panic!("unexpected errno {errno}",),
@@ -612,13 +568,12 @@ mod test_posix_fallocate {
#[test]
fn test_f_get_path() {
use nix::fcntl::*;
use std::{os::unix::io::AsRawFd, path::PathBuf};
use std::path::PathBuf;
let tmp = NamedTempFile::new().unwrap();
let fd = tmp.as_raw_fd();
let mut path = PathBuf::new();
let res =
fcntl(fd, FcntlArg::F_GETPATH(&mut path)).expect("get path failed");
fcntl(&tmp, FcntlArg::F_GETPATH(&mut path)).expect("get path failed");
assert_ne!(res, -1);
assert_eq!(
path.as_path().canonicalize().unwrap(),
@@ -628,14 +583,31 @@ fn test_f_get_path() {
#[cfg(apple_targets)]
#[test]
fn test_f_get_path_nofirmlink() {
fn test_f_preallocate() {
use nix::fcntl::*;
use std::{os::unix::io::AsRawFd, path::PathBuf};
let tmp = NamedTempFile::new().unwrap();
let fd = tmp.as_raw_fd();
let mut st: libc::fstore_t = unsafe { std::mem::zeroed() };
st.fst_flags = libc::F_ALLOCATECONTIG as libc::c_uint;
st.fst_posmode = libc::F_PEOFPOSMODE;
st.fst_length = 1024;
let res = fcntl(tmp, FcntlArg::F_PREALLOCATE(&mut st))
.expect("preallocation failed");
assert_eq!(res, 0);
assert!(st.fst_bytesalloc > 0);
}
#[cfg(apple_targets)]
#[test]
fn test_f_get_path_nofirmlink() {
use nix::fcntl::*;
use std::path::PathBuf;
let tmp = NamedTempFile::new().unwrap();
let mut path = PathBuf::new();
let res = fcntl(fd, FcntlArg::F_GETPATH_NOFIRMLINK(&mut path))
let res = fcntl(&tmp, FcntlArg::F_GETPATH_NOFIRMLINK(&mut path))
.expect("get path failed");
let mut tmpstr = String::from("/System/Volumes/Data");
tmpstr.push_str(
@@ -662,7 +634,7 @@ fn test_f_get_path_nofirmlink() {
#[test]
fn test_f_kinfo() {
use nix::fcntl::*;
use std::{os::unix::io::AsRawFd, path::PathBuf};
use std::path::PathBuf;
let tmp = NamedTempFile::new().unwrap();
// With TMPDIR set with UFS, the vnode name is not entered
@@ -670,9 +642,9 @@ fn test_f_kinfo() {
// Therefore, we reopen the tempfile a second time for the test
// to pass.
let tmp2 = File::open(tmp.path()).unwrap();
let fd = tmp2.as_raw_fd();
let mut path = PathBuf::new();
let res = fcntl(fd, FcntlArg::F_KINFO(&mut path)).expect("get path failed");
let res =
fcntl(&tmp2, FcntlArg::F_KINFO(&mut path)).expect("get path failed");
assert_ne!(res, -1);
assert_eq!(path, tmp.path());
}
@@ -778,3 +750,71 @@ mod test_flock {
.expect_err("Should not have been able to lock the file");
}
}
#[cfg(apple_targets)]
#[test]
fn test_f_rdadvise() {
use nix::fcntl::*;
let contents = vec![1; 1024];
let mut buf = [0; 1024];
let mut tmp = NamedTempFile::new().unwrap();
tmp.write_all(&contents).unwrap();
let fd = open(tmp.path(), OFlag::empty(), Mode::empty()).unwrap();
let rad = libc::radvisory {
ra_offset: 0,
ra_count: contents.len() as _,
};
let res = fcntl(&tmp, FcntlArg::F_RDADVISE(rad)).expect("rdadivse failed");
assert_ne!(res, -1);
assert_eq!(contents.len(), read(&fd, &mut buf).unwrap());
assert_eq!(contents, &buf[0..contents.len()]);
}
#[cfg(apple_targets)]
#[test]
fn test_f_log2phys() {
use nix::fcntl::*;
const CONTENTS: &[u8] = b"abcd";
let mut tmp = NamedTempFile::new().unwrap();
tmp.write_all(CONTENTS).unwrap();
let mut offset: libc::off_t = 0;
let mut res = fcntl(&tmp, FcntlArg::F_LOG2PHYS(&mut offset))
.expect("log2phys failed");
assert_ne!(res, -1);
assert_ne!(offset, 0);
let mut info: libc::log2phys = unsafe { std::mem::zeroed() };
info.l2p_contigbytes = CONTENTS.len() as _;
info.l2p_devoffset = 3;
res = fcntl(&tmp, FcntlArg::F_LOG2PHYS_EXT(&mut info))
.expect("log2phys failed");
assert_ne!(res, -1);
assert_ne!({ info.l2p_devoffset }, 3);
}
#[cfg(apple_targets)]
#[test]
fn test_f_transferextents() {
use nix::fcntl::*;
use std::os::fd::AsRawFd;
let tmp1 = NamedTempFile::new().unwrap();
let tmp2 = NamedTempFile::new().unwrap();
let res = fcntl(&tmp1, FcntlArg::F_TRANSFEREXTENTS(tmp2.as_raw_fd()))
.expect("transferextents failed");
assert_ne!(res, -1);
}
#[cfg(target_os = "freebsd")]
#[test]
fn test_f_readahead() {
use nix::fcntl::*;
let tmp = NamedTempFile::new().unwrap();
let mut res = fcntl(&tmp, FcntlArg::F_READAHEAD(1_000_000))
.expect("read ahead failed");
assert_ne!(res, -1);
res = fcntl(&tmp, FcntlArg::F_READAHEAD(-1024)).expect("read ahead failed");
assert_ne!(res, -1);
}

View File

@@ -10,11 +10,13 @@ const LOOPBACK: &[u8] = b"lo0";
const LOOPBACK: &[u8] = b"loop";
#[test]
#[cfg_attr(target_os = "cygwin", ignore)]
fn test_if_nametoindex() {
if_nametoindex(LOOPBACK).expect("assertion failed");
}
#[test]
#[cfg_attr(target_os = "cygwin", ignore)]
fn test_if_indextoname() {
let loopback_index = if_nametoindex(LOOPBACK).expect("assertion failed");
assert_eq!(

View File

@@ -108,20 +108,23 @@ fn open_ptty_pair() -> (PtyMaster, File) {
// after opening a device path returned from ptsname().
let ptem = b"ptem\0";
let ldterm = b"ldterm\0";
let r = unsafe { ioctl(slave_fd, I_FIND, ldterm.as_ptr()) };
let r = unsafe { ioctl(slave_fd.as_raw_fd(), I_FIND, ldterm.as_ptr()) };
if r < 0 {
panic!("I_FIND failure");
} else if r == 0 {
if unsafe { ioctl(slave_fd, I_PUSH, ptem.as_ptr()) } < 0 {
if unsafe { ioctl(slave_fd.as_raw_fd(), I_PUSH, ptem.as_ptr()) } < 0
{
panic!("I_PUSH ptem failure");
}
if unsafe { ioctl(slave_fd, I_PUSH, ldterm.as_ptr()) } < 0 {
if unsafe { ioctl(slave_fd.as_raw_fd(), I_PUSH, ldterm.as_ptr()) }
< 0
{
panic!("I_PUSH ldterm failure");
}
}
}
let slave = unsafe { File::from_raw_fd(slave_fd) };
let slave = File::from(slave_fd);
(master, slave)
}
@@ -145,6 +148,7 @@ fn make_raw<Fd: AsFd>(fd: Fd) {
/// Test `io::Read` on the PTTY master
#[test]
#[cfg(not(target_os = "solaris"))]
fn test_read_ptty_pair() {
let (mut master, mut slave) = open_ptty_pair();
make_raw(&slave);

View File

@@ -7,10 +7,12 @@ use tempfile::tempfile;
cfg_if! {
if #[cfg(linux_android)] {
use nix::unistd::{pipe, read};
use std::os::unix::io::AsRawFd;
} else if #[cfg(any(freebsdlike, apple_targets, solarish))] {
} else if #[cfg(any(freebsdlike, apple_targets))] {
use std::net::Shutdown;
use std::os::unix::net::UnixStream;
} else if #[cfg(solarish)] {
use std::net::Shutdown;
use std::net::{TcpListener, TcpStream};
}
}
@@ -28,7 +30,7 @@ fn test_sendfile_linux() {
assert_eq!(2, res);
let mut buf = [0u8; 1024];
assert_eq!(2, read(rd.as_raw_fd(), &mut buf).unwrap());
assert_eq!(2, read(&rd, &mut buf).unwrap());
assert_eq!(b"f1", &buf[0..2]);
assert_eq!(7, offset);
}
@@ -47,7 +49,7 @@ fn test_sendfile64_linux() {
assert_eq!(2, res);
let mut buf = [0u8; 1024];
assert_eq!(2, read(rd.as_raw_fd(), &mut buf).unwrap());
assert_eq!(2, read(&rd, &mut buf).unwrap());
assert_eq!(b"f1", &buf[0..2]);
assert_eq!(7, offset);
}
@@ -95,7 +97,7 @@ fn test_sendfile_freebsd() {
+ &trailer_strings.concat();
// Verify the message that was sent
assert_eq!(bytes_written as usize, expected_string.as_bytes().len());
assert_eq!(bytes_written as usize, expected_string.len());
let mut read_string = String::new();
let bytes_read = rd.read_to_string(&mut read_string).unwrap();
@@ -144,7 +146,7 @@ fn test_sendfile_dragonfly() {
+ &trailer_strings.concat();
// Verify the message that was sent
assert_eq!(bytes_written as usize, expected_string.as_bytes().len());
assert_eq!(bytes_written as usize, expected_string.len());
let mut read_string = String::new();
let bytes_read = rd.read_to_string(&mut read_string).unwrap();
@@ -223,7 +225,11 @@ fn test_sendfilev() {
trailer_data
.write_all(trailer_strings.concat().as_bytes())
.unwrap();
let (mut rd, wr) = UnixStream::pair().unwrap();
// Create a TCP socket pair (listener and client)
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
let addr = listener.local_addr().unwrap();
let mut rd = TcpStream::connect(addr).unwrap();
let (wr, _) = listener.accept().unwrap();
let vec: &[SendfileVec] = &[
SendfileVec::new(
header_data.as_fd(),
@@ -244,7 +250,7 @@ fn test_sendfilev() {
let (res, bytes_written) = sendfilev(&wr, vec);
assert!(res.is_ok());
wr.shutdown(Shutdown::Both).unwrap();
wr.shutdown(Shutdown::Write).unwrap();
// Prepare the expected result
let expected_string = header_strings.concat()

193
third_party/rust/nix/test/test_spawn.rs vendored Normal file
View File

@@ -0,0 +1,193 @@
use super::FORK_MTX;
use nix::errno::Errno;
use nix::spawn::{self, PosixSpawnAttr, PosixSpawnFileActions};
use nix::sys::signal;
use nix::sys::wait::{waitpid, WaitPidFlag, WaitStatus};
use std::ffi::{CStr, CString};
/// Helper function to find a binary in the $PATH
fn which(exe_name: &str) -> Option<std::path::PathBuf> {
std::env::var_os("PATH").and_then(|paths| {
std::env::split_paths(&paths)
.filter_map(|dir| {
let full_path = dir.join(exe_name);
if full_path.is_file() {
Some(full_path)
} else {
None
}
})
.next()
})
}
#[test]
fn spawn_true() {
let _guard = FORK_MTX.lock();
let bin = which("true").unwrap();
let args = &[
CString::new("true").unwrap(),
CString::new("story").unwrap(),
];
let vars: &[CString] = &[];
let actions = PosixSpawnFileActions::init().unwrap();
let attr = PosixSpawnAttr::init().unwrap();
let pid =
spawn::posix_spawn(bin.as_path(), &actions, &attr, args, vars).unwrap();
let status = waitpid(pid, Some(WaitPidFlag::empty())).unwrap();
match status {
WaitStatus::Exited(wpid, ret) => {
assert_eq!(pid, wpid);
assert_eq!(ret, 0);
}
_ => {
panic!("Invalid WaitStatus");
}
};
}
#[test]
fn spawn_sleep() {
let _guard = FORK_MTX.lock();
let bin = which("sleep").unwrap();
let args = &[CString::new("sleep").unwrap(), CString::new("30").unwrap()];
let vars: &[CString] = &[];
let actions = PosixSpawnFileActions::init().unwrap();
let attr = PosixSpawnAttr::init().unwrap();
let pid =
spawn::posix_spawn(bin.as_path(), &actions, &attr, args, vars).unwrap();
let status =
waitpid(pid, WaitPidFlag::from_bits(WaitPidFlag::WNOHANG.bits()))
.unwrap();
match status {
WaitStatus::StillAlive => {}
_ => {
panic!("Invalid WaitStatus");
}
};
signal::kill(pid, signal::SIGTERM).unwrap();
let status = waitpid(pid, Some(WaitPidFlag::empty())).unwrap();
match status {
WaitStatus::Signaled(wpid, wsignal, _) => {
assert_eq!(pid, wpid);
assert_eq!(wsignal, signal::SIGTERM);
}
_ => {
panic!("Invalid WaitStatus");
}
};
}
#[test]
// `posix_spawn(path_not_exist)` succeeds under QEMU, so ignore the test. No need
// to investigate the root cause, this test still works in native environments, which
// is sufficient to test the binding.
#[cfg_attr(qemu, ignore)]
fn spawn_cmd_does_not_exist() {
let _guard = FORK_MTX.lock();
let args = &[CString::new("buzz").unwrap()];
let envs: &[CString] = &[];
let actions = PosixSpawnFileActions::init().unwrap();
let attr = PosixSpawnAttr::init().unwrap();
let bin = "2b7433c4-523b-470c-abb5-d7ee9fd295d5-fdasf";
let errno =
spawn::posix_spawn(bin, &actions, &attr, args, envs).unwrap_err();
assert_eq!(errno, Errno::ENOENT);
}
#[test]
fn spawnp_true() {
let _guard = FORK_MTX.lock();
let bin = &CString::new("true").unwrap();
let args = &[
CString::new("true").unwrap(),
CString::new("story").unwrap(),
];
let vars: &[CString] = &[];
let actions = PosixSpawnFileActions::init().unwrap();
let attr = PosixSpawnAttr::init().unwrap();
let pid = spawn::posix_spawnp(bin, &actions, &attr, args, vars).unwrap();
let status = waitpid(pid, Some(WaitPidFlag::empty())).unwrap();
match status {
WaitStatus::Exited(wpid, ret) => {
assert_eq!(pid, wpid);
assert_eq!(ret, 0);
}
_ => {
panic!("Invalid WaitStatus");
}
};
}
#[test]
fn spawnp_sleep() {
let _guard = FORK_MTX.lock();
let bin = &CString::new("sleep").unwrap();
let args = &[CString::new("sleep").unwrap(), CString::new("30").unwrap()];
let vars: &[CString] = &[];
let actions = PosixSpawnFileActions::init().unwrap();
let attr = PosixSpawnAttr::init().unwrap();
let pid = spawn::posix_spawnp(bin, &actions, &attr, args, vars).unwrap();
let status =
waitpid(pid, WaitPidFlag::from_bits(WaitPidFlag::WNOHANG.bits()))
.unwrap();
match status {
WaitStatus::StillAlive => {}
_ => {
panic!("Invalid WaitStatus");
}
};
signal::kill(pid, signal::SIGTERM).unwrap();
let status = waitpid(pid, Some(WaitPidFlag::empty())).unwrap();
match status {
WaitStatus::Signaled(wpid, wsignal, _) => {
assert_eq!(pid, wpid);
assert_eq!(wsignal, signal::SIGTERM);
}
_ => {
panic!("Invalid WaitStatus");
}
};
}
#[test]
// `posix_spawnp(bin_not_exist)` succeeds under QEMU, so ignore the test. No need
// to investigate the root cause, this test still works in native environments, which
// is sufficient to test the binding.
#[cfg_attr(qemu, ignore)]
fn spawnp_cmd_does_not_exist() {
let _guard = FORK_MTX.lock();
let args = &[CString::new("buzz").unwrap()];
let envs: &[CString] = &[];
let actions = PosixSpawnFileActions::init().unwrap();
let attr = PosixSpawnAttr::init().unwrap();
let bin = CStr::from_bytes_with_nul(
"2b7433c4-523b-470c-abb5-d7ee9fd295d5-fdasf\0".as_bytes(),
)
.unwrap();
let errno =
spawn::posix_spawnp(bin, &actions, &attr, args, envs).unwrap_err();
assert_eq!(errno, Errno::ENOENT);
}

View File

@@ -1,487 +0,0 @@
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use std::fs;
use std::fs::File;
#[cfg(not(target_os = "redox"))]
use std::os::unix::fs::symlink;
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use std::os::unix::fs::PermissionsExt;
use std::os::unix::prelude::AsRawFd;
#[cfg(not(target_os = "redox"))]
use std::path::Path;
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use std::time::{Duration, UNIX_EPOCH};
use libc::mode_t;
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
use libc::{S_IFLNK, S_IFMT};
#[cfg(not(target_os = "redox"))]
use nix::errno::Errno;
#[cfg(not(target_os = "redox"))]
use nix::fcntl;
#[cfg(any(
target_os = "linux",
apple_targets,
target_os = "freebsd",
target_os = "netbsd"
))]
use nix::sys::stat::lutimes;
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use nix::sys::stat::utimensat;
#[cfg(not(target_os = "redox"))]
use nix::sys::stat::FchmodatFlags;
use nix::sys::stat::Mode;
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use nix::sys::stat::UtimensatFlags;
#[cfg(not(target_os = "redox"))]
use nix::sys::stat::{self};
use nix::sys::stat::{fchmod, stat};
#[cfg(not(target_os = "redox"))]
use nix::sys::stat::{fchmodat, mkdirat};
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use nix::sys::stat::{futimens, utimes};
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
use nix::sys::stat::FileStat;
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use nix::sys::time::{TimeSpec, TimeVal, TimeValLike};
#[cfg(not(target_os = "redox"))]
use nix::unistd::chdir;
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
use nix::Result;
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
fn assert_stat_results(stat_result: Result<FileStat>) {
let stats = stat_result.expect("stat call failed");
assert!(stats.st_dev > 0); // must be positive integer, exact number machine dependent
assert!(stats.st_ino > 0); // inode is positive integer, exact number machine dependent
assert!(stats.st_mode > 0); // must be positive integer
assert_eq!(stats.st_nlink, 1); // there links created, must be 1
assert_eq!(stats.st_size, 0); // size is 0 because we did not write anything to the file
assert!(stats.st_blksize > 0); // must be positive integer, exact number machine dependent
assert!(stats.st_blocks <= 16); // Up to 16 blocks can be allocated for a blank file
}
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
// (Android's st_blocks is ulonglong which is always non-negative.)
#[cfg_attr(target_os = "android", allow(unused_comparisons))]
#[allow(clippy::absurd_extreme_comparisons)] // Not absurd on all OSes
fn assert_lstat_results(stat_result: Result<FileStat>) {
let stats = stat_result.expect("stat call failed");
assert!(stats.st_dev > 0); // must be positive integer, exact number machine dependent
assert!(stats.st_ino > 0); // inode is positive integer, exact number machine dependent
assert!(stats.st_mode > 0); // must be positive integer
// st_mode is c_uint (u32 on Android) while S_IFMT is mode_t
// (u16 on Android), and that will be a compile error.
// On other platforms they are the same (either both are u16 or u32).
assert_eq!(
(stats.st_mode as usize) & (S_IFMT as usize),
S_IFLNK as usize
); // should be a link
assert_eq!(stats.st_nlink, 1); // there links created, must be 1
assert!(stats.st_size > 0); // size is > 0 because it points to another file
assert!(stats.st_blksize > 0); // must be positive integer, exact number machine dependent
// st_blocks depends on whether the machine's file system uses fast
// or slow symlinks, so just make sure it's not negative
assert!(stats.st_blocks >= 0);
}
#[test]
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
fn test_stat_and_fstat() {
use nix::sys::stat::fstat;
let tempdir = tempfile::tempdir().unwrap();
let filename = tempdir.path().join("foo.txt");
let file = File::create(&filename).unwrap();
let stat_result = stat(&filename);
assert_stat_results(stat_result);
let fstat_result = fstat(file.as_raw_fd());
assert_stat_results(fstat_result);
}
#[test]
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
fn test_fstatat() {
let tempdir = tempfile::tempdir().unwrap();
let filename = tempdir.path().join("foo.txt");
File::create(&filename).unwrap();
let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty());
let result =
stat::fstatat(Some(dirfd.unwrap()), &filename, fcntl::AtFlags::empty());
assert_stat_results(result);
}
#[test]
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
fn test_stat_fstat_lstat() {
use nix::sys::stat::{fstat, lstat};
let tempdir = tempfile::tempdir().unwrap();
let filename = tempdir.path().join("bar.txt");
let linkname = tempdir.path().join("barlink");
File::create(&filename).unwrap();
symlink("bar.txt", &linkname).unwrap();
let link = File::open(&linkname).unwrap();
// should be the same result as calling stat,
// since it's a regular file
let stat_result = stat(&filename);
assert_stat_results(stat_result);
let lstat_result = lstat(&linkname);
assert_lstat_results(lstat_result);
let fstat_result = fstat(link.as_raw_fd());
assert_stat_results(fstat_result);
}
#[test]
fn test_fchmod() {
let tempdir = tempfile::tempdir().unwrap();
let filename = tempdir.path().join("foo.txt");
let file = File::create(&filename).unwrap();
let mut mode1 = Mode::empty();
mode1.insert(Mode::S_IRUSR);
mode1.insert(Mode::S_IWUSR);
fchmod(file.as_raw_fd(), mode1).unwrap();
let file_stat1 = stat(&filename).unwrap();
assert_eq!(file_stat1.st_mode as mode_t & 0o7777, mode1.bits());
let mut mode2 = Mode::empty();
mode2.insert(Mode::S_IROTH);
fchmod(file.as_raw_fd(), mode2).unwrap();
let file_stat2 = stat(&filename).unwrap();
assert_eq!(file_stat2.st_mode as mode_t & 0o7777, mode2.bits());
}
#[test]
#[cfg(not(target_os = "redox"))]
fn test_fchmodat() {
let _dr = crate::DirRestore::new();
let tempdir = tempfile::tempdir().unwrap();
let filename = "foo.txt";
let fullpath = tempdir.path().join(filename);
File::create(&fullpath).unwrap();
let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
let mut mode1 = Mode::empty();
mode1.insert(Mode::S_IRUSR);
mode1.insert(Mode::S_IWUSR);
fchmodat(Some(dirfd), filename, mode1, FchmodatFlags::FollowSymlink)
.unwrap();
let file_stat1 = stat(&fullpath).unwrap();
assert_eq!(file_stat1.st_mode as mode_t & 0o7777, mode1.bits());
chdir(tempdir.path()).unwrap();
let mut mode2 = Mode::empty();
mode2.insert(Mode::S_IROTH);
fchmodat(None, filename, mode2, FchmodatFlags::FollowSymlink).unwrap();
let file_stat2 = stat(&fullpath).unwrap();
assert_eq!(file_stat2.st_mode as mode_t & 0o7777, mode2.bits());
}
/// Asserts that the atime and mtime in a file's metadata match expected values.
///
/// The atime and mtime are expressed with a resolution of seconds because some file systems
/// (like macOS's HFS+) do not have higher granularity.
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn assert_times_eq(
exp_atime_sec: u64,
exp_mtime_sec: u64,
attr: &fs::Metadata,
) {
assert_eq!(
Duration::new(exp_atime_sec, 0),
attr.accessed().unwrap().duration_since(UNIX_EPOCH).unwrap()
);
assert_eq!(
Duration::new(exp_mtime_sec, 0),
attr.modified().unwrap().duration_since(UNIX_EPOCH).unwrap()
);
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_utimes() {
let tempdir = tempfile::tempdir().unwrap();
let fullpath = tempdir.path().join("file");
drop(File::create(&fullpath).unwrap());
utimes(&fullpath, &TimeVal::seconds(9990), &TimeVal::seconds(5550))
.unwrap();
assert_times_eq(9990, 5550, &fs::metadata(&fullpath).unwrap());
}
#[test]
#[cfg(any(
target_os = "linux",
apple_targets,
target_os = "freebsd",
target_os = "netbsd"
))]
fn test_lutimes() {
let tempdir = tempfile::tempdir().unwrap();
let target = tempdir.path().join("target");
let fullpath = tempdir.path().join("symlink");
drop(File::create(&target).unwrap());
symlink(&target, &fullpath).unwrap();
let exp_target_metadata = fs::symlink_metadata(&target).unwrap();
lutimes(&fullpath, &TimeVal::seconds(4560), &TimeVal::seconds(1230))
.unwrap();
assert_times_eq(4560, 1230, &fs::symlink_metadata(&fullpath).unwrap());
let target_metadata = fs::symlink_metadata(&target).unwrap();
assert_eq!(
exp_target_metadata.accessed().unwrap(),
target_metadata.accessed().unwrap(),
"atime of symlink target was unexpectedly modified"
);
assert_eq!(
exp_target_metadata.modified().unwrap(),
target_metadata.modified().unwrap(),
"mtime of symlink target was unexpectedly modified"
);
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_futimens() {
let tempdir = tempfile::tempdir().unwrap();
let fullpath = tempdir.path().join("file");
drop(File::create(&fullpath).unwrap());
let fd = fcntl::open(&fullpath, fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
futimens(fd, &TimeSpec::seconds(10), &TimeSpec::seconds(20)).unwrap();
assert_times_eq(10, 20, &fs::metadata(&fullpath).unwrap());
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_utimensat() {
let _dr = crate::DirRestore::new();
let tempdir = tempfile::tempdir().unwrap();
let filename = "foo.txt";
let fullpath = tempdir.path().join(filename);
drop(File::create(&fullpath).unwrap());
let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
utimensat(
Some(dirfd),
filename,
&TimeSpec::seconds(12345),
&TimeSpec::seconds(678),
UtimensatFlags::FollowSymlink,
)
.unwrap();
assert_times_eq(12345, 678, &fs::metadata(&fullpath).unwrap());
chdir(tempdir.path()).unwrap();
utimensat(
None,
filename,
&TimeSpec::seconds(500),
&TimeSpec::seconds(800),
UtimensatFlags::FollowSymlink,
)
.unwrap();
assert_times_eq(500, 800, &fs::metadata(&fullpath).unwrap());
}
#[test]
#[cfg(not(target_os = "redox"))]
fn test_mkdirat_success_path() {
let tempdir = tempfile::tempdir().unwrap();
let filename = "example_subdir";
let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
mkdirat(Some(dirfd), filename, Mode::S_IRWXU).expect("mkdirat failed");
assert!(Path::exists(&tempdir.path().join(filename)));
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_mkdirat_success_mode() {
let expected_bits =
stat::SFlag::S_IFDIR.bits() | stat::Mode::S_IRWXU.bits();
let tempdir = tempfile::tempdir().unwrap();
let filename = "example_subdir";
let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
mkdirat(Some(dirfd), filename, Mode::S_IRWXU).expect("mkdirat failed");
let permissions = fs::metadata(tempdir.path().join(filename))
.unwrap()
.permissions();
let mode = permissions.mode();
assert_eq!(mode as mode_t, expected_bits)
}
#[test]
#[cfg(not(target_os = "redox"))]
fn test_mkdirat_fail() {
let tempdir = tempfile::tempdir().unwrap();
let not_dir_filename = "example_not_dir";
let filename = "example_subdir_dir";
let dirfd = fcntl::open(
&tempdir.path().join(not_dir_filename),
fcntl::OFlag::O_CREAT,
stat::Mode::empty(),
)
.unwrap();
let result = mkdirat(Some(dirfd), filename, Mode::S_IRWXU).unwrap_err();
assert_eq!(result, Errno::ENOTDIR);
}
#[test]
#[cfg(not(any(
freebsdlike,
apple_targets,
target_os = "haiku",
target_os = "redox"
)))]
fn test_mknod() {
use stat::{lstat, mknod, SFlag};
let file_name = "test_file";
let tempdir = tempfile::tempdir().unwrap();
let target = tempdir.path().join(file_name);
mknod(&target, SFlag::S_IFREG, Mode::S_IRWXU, 0).unwrap();
let mode = lstat(&target).unwrap().st_mode as mode_t;
assert_eq!(mode & libc::S_IFREG, libc::S_IFREG);
assert_eq!(mode & libc::S_IRWXU, libc::S_IRWXU);
}
#[test]
#[cfg(not(any(
solarish,
freebsdlike,
apple_targets,
target_os = "haiku",
target_os = "redox"
)))]
fn test_mknodat() {
use fcntl::{AtFlags, OFlag};
use nix::dir::Dir;
use stat::{fstatat, mknodat, SFlag};
let file_name = "test_file";
let tempdir = tempfile::tempdir().unwrap();
let target_dir =
Dir::open(tempdir.path(), OFlag::O_DIRECTORY, Mode::S_IRWXU).unwrap();
mknodat(
Some(target_dir.as_raw_fd()),
file_name,
SFlag::S_IFREG,
Mode::S_IRWXU,
0,
)
.unwrap();
let mode = fstatat(
Some(target_dir.as_raw_fd()),
file_name,
AtFlags::AT_SYMLINK_NOFOLLOW,
)
.unwrap()
.st_mode as mode_t;
assert_eq!(mode & libc::S_IFREG, libc::S_IFREG);
assert_eq!(mode & libc::S_IRWXU, libc::S_IRWXU);
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_futimens_unchanged() {
let tempdir = tempfile::tempdir().unwrap();
let fullpath = tempdir.path().join("file");
drop(File::create(&fullpath).unwrap());
let fd = fcntl::open(&fullpath, fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
let old_atime = fs::metadata(fullpath.as_path())
.unwrap()
.accessed()
.unwrap();
let old_mtime = fs::metadata(fullpath.as_path())
.unwrap()
.modified()
.unwrap();
futimens(fd, &TimeSpec::UTIME_OMIT, &TimeSpec::UTIME_OMIT).unwrap();
let new_atime = fs::metadata(fullpath.as_path())
.unwrap()
.accessed()
.unwrap();
let new_mtime = fs::metadata(fullpath.as_path())
.unwrap()
.modified()
.unwrap();
assert_eq!(old_atime, new_atime);
assert_eq!(old_mtime, new_mtime);
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_utimensat_unchanged() {
let _dr = crate::DirRestore::new();
let tempdir = tempfile::tempdir().unwrap();
let filename = "foo.txt";
let fullpath = tempdir.path().join(filename);
drop(File::create(&fullpath).unwrap());
let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
let old_atime = fs::metadata(fullpath.as_path())
.unwrap()
.accessed()
.unwrap();
let old_mtime = fs::metadata(fullpath.as_path())
.unwrap()
.modified()
.unwrap();
utimensat(
Some(dirfd),
filename,
&TimeSpec::UTIME_OMIT,
&TimeSpec::UTIME_OMIT,
UtimensatFlags::NoFollowSymlink,
)
.unwrap();
let new_atime = fs::metadata(fullpath.as_path())
.unwrap()
.accessed()
.unwrap();
let new_mtime = fs::metadata(fullpath.as_path())
.unwrap()
.modified()
.unwrap();
assert_eq!(old_atime, new_atime);
assert_eq!(old_mtime, new_mtime);
}

View File

@@ -0,0 +1,38 @@
use nix::syslog::{openlog, syslog, Facility, LogFlags, Severity};
#[test]
fn test_syslog_hello_world() {
let flags = LogFlags::LOG_PID;
#[cfg(not(target_os = "linux"))]
openlog(None::<&str>, flags, Facility::LOG_USER).unwrap();
#[cfg(target_os = "linux")]
openlog(None, flags, Facility::LOG_USER).unwrap();
syslog(Severity::LOG_EMERG, "Hello, nix!").unwrap();
let name = "syslog";
syslog(Severity::LOG_NOTICE, &format!("Hello, {name}!")).unwrap();
}
#[test]
#[cfg(target_os = "linux")]
fn test_openlog_with_ident() {
use std::ffi::CStr;
const IDENT: &CStr = unsafe {
CStr::from_bytes_with_nul_unchecked(b"test_openlog_with_ident\0")
};
let flags = LogFlags::LOG_PID;
openlog(Some(IDENT), flags, Facility::LOG_USER).unwrap();
syslog(Severity::LOG_EMERG, "Hello, ident!").unwrap();
}
#[test]
#[cfg(not(target_os = "linux"))]
fn test_openlog_with_ident() {
let flags = LogFlags::LOG_PID;
openlog(Some("test_openlog_with_ident"), flags, Facility::LOG_USER)
.unwrap();
syslog(Severity::LOG_EMERG, "Hello, ident!").unwrap();
}

View File

@@ -26,7 +26,6 @@ use std::ffi::CString;
use std::fs::DirBuilder;
use std::fs::{self, File};
use std::io::Write;
use std::os::unix::prelude::*;
#[cfg(not(any(
target_os = "fuchsia",
target_os = "redox",
@@ -121,8 +120,7 @@ fn test_mkstemp() {
let result = mkstemp(&path);
match result {
Ok((fd, path)) => {
close(fd).unwrap();
Ok((_, path)) => {
unlink(path.as_path()).unwrap();
}
Err(e) => panic!("mkstemp failed: {e}"),
@@ -163,12 +161,14 @@ fn test_mkfifo_directory() {
target_os = "haiku"
)))]
fn test_mkfifoat_none() {
use nix::fcntl::AT_FDCWD;
let _m = crate::CWD_LOCK.read();
let tempdir = tempdir().unwrap();
let mkfifoat_fifo = tempdir.path().join("mkfifoat_fifo");
mkfifoat(None, &mkfifoat_fifo, Mode::S_IRUSR).unwrap();
mkfifoat(AT_FDCWD, &mkfifoat_fifo, Mode::S_IRUSR).unwrap();
let stats = stat::stat(&mkfifoat_fifo).unwrap();
let typ = stat::SFlag::from_bits_truncate(stats.st_mode);
@@ -189,11 +189,10 @@ fn test_mkfifoat() {
let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
let mkfifoat_name = "mkfifoat_name";
mkfifoat(Some(dirfd), mkfifoat_name, Mode::S_IRUSR).unwrap();
mkfifoat(&dirfd, mkfifoat_name, Mode::S_IRUSR).unwrap();
let stats =
stat::fstatat(Some(dirfd), mkfifoat_name, fcntl::AtFlags::empty())
.unwrap();
stat::fstatat(&dirfd, mkfifoat_name, fcntl::AtFlags::empty()).unwrap();
let typ = stat::SFlag::from_bits_truncate(stats.st_mode);
assert_eq!(typ, SFlag::S_IFIFO);
}
@@ -206,10 +205,12 @@ fn test_mkfifoat() {
target_os = "haiku"
)))]
fn test_mkfifoat_directory_none() {
use nix::fcntl::AT_FDCWD;
let _m = crate::CWD_LOCK.read();
// mkfifoat should fail if a directory is given
mkfifoat(None, &env::temp_dir(), Mode::S_IRUSR)
mkfifoat(AT_FDCWD, &env::temp_dir(), Mode::S_IRUSR)
.expect_err("assertion failed");
}
@@ -225,9 +226,9 @@ fn test_mkfifoat_directory() {
let tempdir = tempdir().unwrap();
let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
let mkfifoat_dir = "mkfifoat_dir";
stat::mkdirat(Some(dirfd), mkfifoat_dir, Mode::S_IRUSR).unwrap();
stat::mkdirat(&dirfd, mkfifoat_dir, Mode::S_IRUSR).unwrap();
mkfifoat(Some(dirfd), mkfifoat_dir, Mode::S_IRUSR)
mkfifoat(&dirfd, mkfifoat_dir, Mode::S_IRUSR)
.expect_err("assertion failed");
}
@@ -385,7 +386,7 @@ macro_rules! execve_test_factory (
match unsafe{fork()}.unwrap() {
Child => {
// Make `writer` be the stdout of the new process.
dup2(writer.as_raw_fd(), 1).unwrap();
nix::unistd::dup2_stdout(&writer).unwrap();
let r = syscall();
let _ = std::io::stderr()
.write_all(format!("{:?}", r).as_bytes());
@@ -399,7 +400,7 @@ macro_rules! execve_test_factory (
assert_eq!(ws, Ok(WaitStatus::Exited(child, 0)));
// Read 1024 bytes.
let mut buf = [0u8; 1024];
read(reader.as_raw_fd(), &mut buf).unwrap();
read(&reader, &mut buf).unwrap();
// It should contain the things we printed using `/bin/sh`.
let string = String::from_utf8_lossy(&buf);
assert!(string.contains("nix!!!"));
@@ -432,12 +433,12 @@ macro_rules! execve_test_factory (
cfg_if! {
if #[cfg(target_os = "android")] {
execve_test_factory!(test_execve, execve, CString::new("/system/bin/sh").unwrap().as_c_str());
execve_test_factory!(test_fexecve, fexecve, File::open("/system/bin/sh").unwrap().into_raw_fd());
execve_test_factory!(test_fexecve, fexecve, &File::open("/system/bin/sh").unwrap());
} else if #[cfg(any(freebsdlike, target_os = "linux", target_os = "hurd"))] {
// These tests frequently fail on musl, probably due to
// https://github.com/nix-rust/nix/issues/555
execve_test_factory!(test_execve, execve, CString::new("/bin/sh").unwrap().as_c_str());
execve_test_factory!(test_fexecve, fexecve, File::open("/bin/sh").unwrap().into_raw_fd());
execve_test_factory!(test_fexecve, fexecve, &File::open("/bin/sh").unwrap());
} else if #[cfg(any(solarish, apple_targets, netbsdlike))] {
execve_test_factory!(test_execve, execve, CString::new("/bin/sh").unwrap().as_c_str());
// No fexecve() on ios, macos, NetBSD, OpenBSD.
@@ -456,21 +457,21 @@ cfg_if! {
if #[cfg(target_os = "android")] {
use nix::fcntl::AtFlags;
execve_test_factory!(test_execveat_empty, execveat,
Some(File::open("/system/bin/sh").unwrap().into_raw_fd()),
&File::open("/system/bin/sh").unwrap(),
"", AtFlags::AT_EMPTY_PATH);
execve_test_factory!(test_execveat_relative, execveat,
Some(File::open("/system/bin/").unwrap().into_raw_fd()),
&File::open("/system/bin/").unwrap(),
"./sh", AtFlags::empty());
execve_test_factory!(test_execveat_absolute, execveat,
Some(File::open("/").unwrap().into_raw_fd()),
&File::open("/").unwrap(),
"/system/bin/sh", AtFlags::empty());
} else if #[cfg(all(target_os = "linux", any(target_arch ="x86_64", target_arch ="x86")))] {
use nix::fcntl::AtFlags;
execve_test_factory!(test_execveat_empty, execveat, Some(File::open("/bin/sh").unwrap().into_raw_fd()),
execve_test_factory!(test_execveat_empty, execveat, &File::open("/bin/sh").unwrap(),
"", AtFlags::AT_EMPTY_PATH);
execve_test_factory!(test_execveat_relative, execveat, Some(File::open("/bin/").unwrap().into_raw_fd()),
execve_test_factory!(test_execveat_relative, execveat, &File::open("/bin/").unwrap(),
"./sh", AtFlags::empty());
execve_test_factory!(test_execveat_absolute, execveat, Some(File::open("/").unwrap().into_raw_fd()),
execve_test_factory!(test_execveat_absolute, execveat, &File::open("/").unwrap(),
"/bin/sh", AtFlags::empty());
}
}
@@ -483,12 +484,10 @@ fn test_fchdir() {
let tmpdir = tempdir().unwrap();
let tmpdir_path = tmpdir.path().canonicalize().unwrap();
let tmpdir_fd = File::open(&tmpdir_path).unwrap().into_raw_fd();
let tmpdir_fd = File::open(&tmpdir_path).unwrap();
fchdir(tmpdir_fd).expect("assertion failed");
fchdir(&tmpdir_fd).expect("assertion failed");
assert_eq!(getcwd().unwrap(), tmpdir_path);
close(tmpdir_fd).expect("assertion failed");
}
#[test]
@@ -542,19 +541,18 @@ fn test_fchown() {
let uid = Some(getuid());
let gid = Some(getgid());
let path = tempfile().unwrap();
let fd = path.as_raw_fd();
let file = tempfile().unwrap();
fchown(fd, uid, gid).unwrap();
fchown(fd, uid, None).unwrap();
fchown(fd, None, gid).unwrap();
fchown(999999999, uid, gid).unwrap_err();
fchown(&file, uid, gid).unwrap();
fchown(&file, uid, None).unwrap();
fchown(&file, None, gid).unwrap();
}
#[test]
#[cfg(not(target_os = "redox"))]
fn test_fchownat() {
use nix::fcntl::AtFlags;
use nix::fcntl::AT_FDCWD;
let _dr = crate::DirRestore::new();
// Testing for anything other than our own UID/GID is hard.
@@ -569,13 +567,13 @@ fn test_fchownat() {
let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
fchownat(Some(dirfd), "file", uid, gid, AtFlags::empty()).unwrap();
fchownat(&dirfd, "file", uid, gid, AtFlags::empty()).unwrap();
chdir(tempdir.path()).unwrap();
fchownat(None, "file", uid, gid, AtFlags::empty()).unwrap();
fchownat(AT_FDCWD, "file", uid, gid, AtFlags::empty()).unwrap();
fs::remove_file(&path).unwrap();
fchownat(None, "file", uid, gid, AtFlags::empty()).unwrap_err();
fchownat(AT_FDCWD, "file", uid, gid, AtFlags::empty()).unwrap_err();
}
#[test]
@@ -585,7 +583,7 @@ fn test_lseek() {
tmp.write_all(CONTENTS).unwrap();
let offset: off_t = 5;
lseek(tmp.as_raw_fd(), offset, Whence::SeekSet).unwrap();
lseek(&tmp, offset, Whence::SeekSet).unwrap();
let mut buf = [0u8; 7];
crate::read_exact(&tmp, &mut buf);
@@ -599,7 +597,7 @@ fn test_lseek64() {
let mut tmp = tempfile().unwrap();
tmp.write_all(CONTENTS).unwrap();
lseek64(tmp.as_raw_fd(), 5, Whence::SeekSet).unwrap();
lseek64(&tmp, 5, Whence::SeekSet).unwrap();
let mut buf = [0u8; 7];
crate::read_exact(&tmp, &mut buf);
@@ -633,7 +631,8 @@ cfg_if! {
#[cfg(not(any(
target_os = "redox",
target_os = "fuchsia",
target_os = "haiku"
target_os = "haiku",
target_os = "cygwin"
)))]
fn test_acct() {
use std::process::Command;
@@ -733,12 +732,12 @@ fn test_getresgid() {
fn test_pipe() {
let (fd0, fd1) = pipe().unwrap();
let m0 = stat::SFlag::from_bits_truncate(
stat::fstat(fd0.as_raw_fd()).unwrap().st_mode as mode_t,
stat::fstat(&fd0).unwrap().st_mode as mode_t,
);
// S_IFIFO means it's a pipe
assert_eq!(m0, SFlag::S_IFIFO);
let m1 = stat::SFlag::from_bits_truncate(
stat::fstat(fd1.as_raw_fd()).unwrap().st_mode as mode_t,
stat::fstat(&fd1).unwrap().st_mode as mode_t,
);
assert_eq!(m1, SFlag::S_IFIFO);
}
@@ -758,13 +757,11 @@ fn test_pipe2() {
use nix::fcntl::{fcntl, FcntlArg, FdFlag};
let (fd0, fd1) = pipe2(OFlag::O_CLOEXEC).unwrap();
let f0 = FdFlag::from_bits_truncate(
fcntl(fd0.as_raw_fd(), FcntlArg::F_GETFD).unwrap(),
);
let f0 =
FdFlag::from_bits_truncate(fcntl(&fd0, FcntlArg::F_GETFD).unwrap());
assert!(f0.contains(FdFlag::FD_CLOEXEC));
let f1 = FdFlag::from_bits_truncate(
fcntl(fd1.as_raw_fd(), FcntlArg::F_GETFD).unwrap(),
);
let f1 =
FdFlag::from_bits_truncate(fcntl(&fd1, FcntlArg::F_GETFD).unwrap());
assert!(f1.contains(FdFlag::FD_CLOEXEC));
}
@@ -872,13 +869,15 @@ fn test_canceling_alarm() {
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_symlinkat() {
use nix::fcntl::AT_FDCWD;
let _m = crate::CWD_LOCK.read();
let tempdir = tempdir().unwrap();
let target = tempdir.path().join("a");
let linkpath = tempdir.path().join("b");
symlinkat(&target, None, &linkpath).unwrap();
symlinkat(&target, AT_FDCWD, &linkpath).unwrap();
assert_eq!(
readlink(&linkpath).unwrap().to_str().unwrap(),
target.to_str().unwrap()
@@ -887,7 +886,7 @@ fn test_symlinkat() {
let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
let target = "c";
let linkpath = "d";
symlinkat(target, Some(dirfd), linkpath).unwrap();
symlinkat(target, &dirfd, linkpath).unwrap();
assert_eq!(
readlink(&tempdir.path().join(linkpath))
.unwrap()
@@ -919,9 +918,43 @@ fn test_linkat_file() {
// Attempt hard link file at relative path
linkat(
Some(dirfd),
&dirfd,
oldfilename,
Some(dirfd),
&dirfd,
newfilename,
AtFlags::AT_SYMLINK_FOLLOW,
)
.unwrap();
assert!(newfilepath.exists());
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
/// This test is the same as [test_linkat_file], but ensures that two different types can be used
/// as the path arguments.
fn test_linkat_pathtypes() {
use nix::fcntl::AtFlags;
let tempdir = tempdir().unwrap();
let oldfilename = "foo.txt";
let oldfilepath = tempdir.path().join(oldfilename);
let newfilename = "bar.txt";
let newfilepath = tempdir.path().join(newfilename);
// Create file
File::create(oldfilepath).unwrap();
// Get file descriptor for base directory
let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
// Attempt hard link file at relative path
linkat(
&dirfd,
PathBuf::from(oldfilename).as_path(),
&dirfd,
newfilename,
AtFlags::AT_SYMLINK_FOLLOW,
)
@@ -933,6 +966,7 @@ fn test_linkat_file() {
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_linkat_olddirfd_none() {
use nix::fcntl::AtFlags;
use nix::fcntl::AT_FDCWD;
let _dr = crate::DirRestore::new();
@@ -958,9 +992,9 @@ fn test_linkat_olddirfd_none() {
// Attempt hard link file using curent working directory as relative path for old file path
chdir(tempdir_oldfile.path()).unwrap();
linkat(
None,
AT_FDCWD,
oldfilename,
Some(dirfd),
&dirfd,
newfilename,
AtFlags::AT_SYMLINK_FOLLOW,
)
@@ -972,6 +1006,7 @@ fn test_linkat_olddirfd_none() {
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_linkat_newdirfd_none() {
use nix::fcntl::AtFlags;
use nix::fcntl::AT_FDCWD;
let _dr = crate::DirRestore::new();
@@ -997,9 +1032,9 @@ fn test_linkat_newdirfd_none() {
// Attempt hard link file using current working directory as relative path for new file path
chdir(tempdir_newfile.path()).unwrap();
linkat(
Some(dirfd),
&dirfd,
oldfilename,
None,
AT_FDCWD,
newfilename,
AtFlags::AT_SYMLINK_FOLLOW,
)
@@ -1011,6 +1046,7 @@ fn test_linkat_newdirfd_none() {
#[cfg(not(any(apple_targets, target_os = "redox", target_os = "haiku")))]
fn test_linkat_no_follow_symlink() {
use nix::fcntl::AtFlags;
use nix::fcntl::AT_FDCWD;
let _m = crate::CWD_LOCK.read();
@@ -1028,7 +1064,7 @@ fn test_linkat_no_follow_symlink() {
File::create(&oldfilepath).unwrap();
// Create symlink to file
symlinkat(&oldfilepath, None, &symoldfilepath).unwrap();
symlinkat(&oldfilepath, AT_FDCWD, &symoldfilepath).unwrap();
// Get file descriptor for base directory
let dirfd =
@@ -1037,9 +1073,9 @@ fn test_linkat_no_follow_symlink() {
// Attempt link symlink of file at relative path
linkat(
Some(dirfd),
&dirfd,
symoldfilename,
Some(dirfd),
&dirfd,
newfilename,
AtFlags::empty(),
)
@@ -1056,6 +1092,7 @@ fn test_linkat_no_follow_symlink() {
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_linkat_follow_symlink() {
use nix::fcntl::AtFlags;
use nix::fcntl::AT_FDCWD;
let _m = crate::CWD_LOCK.read();
@@ -1073,7 +1110,7 @@ fn test_linkat_follow_symlink() {
File::create(&oldfilepath).unwrap();
// Create symlink to file
symlinkat(&oldfilepath, None, &symoldfilepath).unwrap();
symlinkat(&oldfilepath, AT_FDCWD, &symoldfilepath).unwrap();
// Get file descriptor for base directory
let dirfd =
@@ -1082,9 +1119,9 @@ fn test_linkat_follow_symlink() {
// Attempt link target of symlink of file at relative path
linkat(
Some(dirfd),
&dirfd,
symoldfilename,
Some(dirfd),
&dirfd,
newfilename,
AtFlags::AT_SYMLINK_FOLLOW,
)
@@ -1120,7 +1157,7 @@ fn test_unlinkat_dir_noremovedir() {
// Attempt unlink dir at relative path without proper flag
let err_result =
unlinkat(Some(dirfd), dirname, UnlinkatFlags::NoRemoveDir).unwrap_err();
unlinkat(&dirfd, dirname, UnlinkatFlags::NoRemoveDir).unwrap_err();
assert!(err_result == Errno::EISDIR || err_result == Errno::EPERM);
}
@@ -1140,7 +1177,7 @@ fn test_unlinkat_dir_removedir() {
.unwrap();
// Attempt unlink dir at relative path with proper flag
unlinkat(Some(dirfd), dirname, UnlinkatFlags::RemoveDir).unwrap();
unlinkat(&dirfd, dirname, UnlinkatFlags::RemoveDir).unwrap();
assert!(!dirpath.exists());
}
@@ -1160,7 +1197,7 @@ fn test_unlinkat_file() {
.unwrap();
// Attempt unlink file at relative path
unlinkat(Some(dirfd), filename, UnlinkatFlags::NoRemoveDir).unwrap();
unlinkat(&dirfd, filename, UnlinkatFlags::NoRemoveDir).unwrap();
assert!(!filepath.exists());
}
@@ -1186,12 +1223,16 @@ fn test_access_file_exists() {
#[cfg(not(target_os = "redox"))]
#[test]
fn test_user_into_passwd() {
// get the UID of the "nobody" user
#[cfg(not(target_os = "haiku"))]
let test_username = "nobody";
// "nobody" unavailable on haiku
#[cfg(target_os = "haiku")]
let test_username = "user";
let test_username = if cfg!(target_os = "haiku") {
// "nobody" unavailable on haiku
"user"
} else if cfg!(target_os = "cygwin") {
// the Windows admin user
"Administrator"
} else {
// get the UID of the "nobody" user
"nobody"
};
let nobody = User::from_name(test_username).unwrap().unwrap();
let pwd: libc::passwd = nobody.into();
@@ -1243,6 +1284,8 @@ fn test_setfsuid() {
target_os = "haiku"
)))]
fn test_ttyname() {
use std::os::fd::AsRawFd;
let fd = posix_openpt(OFlag::O_RDWR).expect("posix_openpt failed");
assert!(fd.as_raw_fd() > 0);
@@ -1291,10 +1334,12 @@ fn test_getpeereid() {
#[cfg(not(target_os = "redox"))]
fn test_faccessat_none_not_existing() {
use nix::fcntl::AtFlags;
use nix::fcntl::AT_FDCWD;
let tempdir = tempfile::tempdir().unwrap();
let dir = tempdir.path().join("does_not_exist.txt");
assert_eq!(
faccessat(None, &dir, AccessFlags::F_OK, AtFlags::empty())
faccessat(AT_FDCWD, &dir, AccessFlags::F_OK, AtFlags::empty())
.err()
.unwrap(),
Errno::ENOENT
@@ -1305,18 +1350,14 @@ fn test_faccessat_none_not_existing() {
#[cfg(not(target_os = "redox"))]
fn test_faccessat_not_existing() {
use nix::fcntl::AtFlags;
let tempdir = tempfile::tempdir().unwrap();
let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
let not_exist_file = "does_not_exist.txt";
assert_eq!(
faccessat(
Some(dirfd),
not_exist_file,
AccessFlags::F_OK,
AtFlags::empty(),
)
.err()
.unwrap(),
faccessat(&dirfd, not_exist_file, AccessFlags::F_OK, AtFlags::empty(),)
.err()
.unwrap(),
Errno::ENOENT
);
}
@@ -1325,11 +1366,13 @@ fn test_faccessat_not_existing() {
#[cfg(not(target_os = "redox"))]
fn test_faccessat_none_file_exists() {
use nix::fcntl::AtFlags;
use nix::fcntl::AT_FDCWD;
let tempdir = tempfile::tempdir().unwrap();
let path = tempdir.path().join("does_exist.txt");
let _file = File::create(path.clone()).unwrap();
assert!(faccessat(
None,
AT_FDCWD,
&path,
AccessFlags::R_OK | AccessFlags::W_OK,
AtFlags::empty(),
@@ -1341,13 +1384,14 @@ fn test_faccessat_none_file_exists() {
#[cfg(not(target_os = "redox"))]
fn test_faccessat_file_exists() {
use nix::fcntl::AtFlags;
let tempdir = tempfile::tempdir().unwrap();
let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
let exist_file = "does_exist.txt";
let path = tempdir.path().join(exist_file);
let _file = File::create(path.clone()).unwrap();
assert!(faccessat(
Some(dirfd),
&dirfd,
&path,
AccessFlags::R_OK | AccessFlags::W_OK,
AtFlags::empty(),

View File

@@ -12,7 +12,7 @@ num-derive = "0.4"
num-traits = "0.2"
[target."cfg(any(target_os = \"android\", target_os = \"linux\", target_os = \"macos\"))".dependencies]
nix = { version = "0.29", features = ["process"] }
nix = { version = "0.30", features = ["process"] }
[target."cfg(any(target_os = \"android\", target_os = \"linux\"))".dependencies]
minidump-writer = "0.10"

View File

@@ -11,7 +11,7 @@ num-traits = "0.2"
thiserror = "2"
[target."cfg(any(target_os = \"android\", target_os = \"linux\", target_os = \"macos\"))".dependencies]
nix = { version = "0.29", features = ["fs", "poll", "socket", "uio"] }
nix = { version = "0.30", features = ["fs", "poll", "socket", "uio"] }
[target."cfg(any(target_os = \"android\", target_os = \"linux\"))".dependencies]
minidump-writer = "0.10"

View File

@@ -28,7 +28,7 @@ env_logger = { version = "0.10", default-features = false }
android_logger = "0.12"
[target."cfg(any(target_os = \"android\", target_os = \"linux\"))".dependencies]
nix = { version = "0.29", features = ["poll", "socket", "uio"] }
nix = { version = "0.30", features = ["poll", "socket", "uio"] }
minidump-writer = { version = "0.10" }
rust_minidump_writer_linux = { path = "../rust_minidump_writer_linux" }
@@ -43,7 +43,7 @@ windows-sys = { version = "0.52", features = [
] }
[target."cfg(target_os = \"macos\")".dependencies]
nix = { version = "0.29", features = ["fs", "poll", "socket", "uio"] }
nix = { version = "0.30", features = ["fs", "poll", "socket", "uio"] }
[build-dependencies]
cc = "1"