nix升级到0.30.1版本

Signed-off-by: ljy9810 <longjianyin@h-partners.com>
This commit is contained in:
ljy9810
2025-10-14 20:15:12 +08:00
parent bb33ca22b3
commit 683c1defaa
132 changed files with 18532 additions and 9899 deletions
+8 -259
View File
@@ -9,7 +9,7 @@ env:
RUSTFLAGS: -D warnings
RUSTDOCFLAGS: -D warnings
TOOL: cargo
MSRV: 1.56.1
MSRV: 1.69.0
ZFLAGS:
# Tests that don't require executing the build binaries
@@ -18,9 +18,9 @@ build: &BUILD
- . $HOME/.cargo/env || true
- $TOOL -Vv
- rustc -Vv
- $TOOL $BUILD $ZFLAGS --target $TARGET --all-targets
- $TOOL doc $ZFLAGS --no-deps --target $TARGET
- $TOOL clippy $ZFLAGS --target $TARGET --all-targets -- $CLIPPYFLAGS
- $TOOL $BUILD $ZFLAGS --target $TARGET --all-targets --all-features
- $TOOL doc $ZFLAGS --no-deps --target $TARGET --all-features
- $TOOL clippy $ZFLAGS --target $TARGET --all-targets --all-features -- $CLIPPYFLAGS
- if [ -z "$NOHACK" ]; then mkdir -p $HOME/.cargo/bin; export PATH=$HOME/.cargo/bin:$PATH; fi
- if [ -z "$NOHACK" ]; then curl -LsSf https://github.com/taiki-e/cargo-hack/releases/latest/download/cargo-hack-${HOST:-$TARGET}.tar.gz | tar xzf - -C ~/.cargo/bin; fi
- if [ -z "$NOHACK" ]; then $TOOL hack $ZFLAGS check --target $TARGET --each-feature; fi
@@ -40,12 +40,10 @@ task:
env:
TARGET: x86_64-unknown-freebsd
matrix:
- name: FreeBSD 12 amd64 & i686
freebsd_instance:
image: freebsd-12-3-release-amd64
- name: FreeBSD 14 amd64 & i686
freebsd_instance:
image_family: freebsd-14-0-snap
image: freebsd-14-1-release-amd64-ufs
cpu: 1
# Enable tests that would fail on FreeBSD 12
RUSTFLAGS: --cfg fbsd14 -D warnings
RUSTDOCFLAGS: --cfg fbsd14
@@ -56,262 +54,13 @@ task:
- . $HOME/.cargo/env
- rustup target add i686-unknown-freebsd
- rustup component add clippy
- cp Cargo.lock.msrv Cargo.lock
<< : *TEST
i386_test_script:
- . $HOME/.cargo/env
- cargo build --target i686-unknown-freebsd
- cargo doc --no-deps --target i686-unknown-freebsd
- cargo build --target i686-unknown-freebsd --all-features
- cargo doc --no-deps --target i686-unknown-freebsd --all-features
- cargo test --target i686-unknown-freebsd
i386_feature_script:
- . $HOME/.cargo/env
- if [ -z "$NOHACK" ]; then cargo hack check --each-feature --target i686-unknown-freebsd; fi
before_cache_script: rm -rf $CARGO_HOME/registry/index
# Test macOS aarch64 in a full VM
task:
name: macOS aarch64
env:
TARGET: aarch64-apple-darwin
macos_instance:
image: ghcr.io/cirruslabs/macos-ventura-base:latest
setup_script:
- curl --proto '=https' --tlsv1.2 -sSf -o rustup.sh https://sh.rustup.rs
- sh rustup.sh -y --profile=minimal --default-toolchain $MSRV
- . $HOME/.cargo/env
- rustup component add clippy
- cp Cargo.lock.msrv Cargo.lock
<< : *TEST
before_cache_script: rm -rf $CARGO_HOME/registry/index
# Use cross for QEMU-based testing
# cross needs to execute Docker, so we must use Cirrus's Docker Builder task.
task:
env:
RUST_TEST_THREADS: 1 # QEMU works best with 1 thread
HOME: /tmp/home
HOST: x86_64-unknown-linux-gnu
PATH: $HOME/.cargo/bin:$PATH
RUSTFLAGS: --cfg qemu -D warnings
TOOL: cross
matrix:
- name: Linux arm gnueabi
env:
TARGET: arm-unknown-linux-gnueabi
- name: Linux armv7 gnueabihf
env:
TARGET: armv7-unknown-linux-gnueabihf
- name: Linux i686
env:
TARGET: i686-unknown-linux-gnu
- name: Linux i686 musl
env:
TARGET: i686-unknown-linux-musl
- name: Linux MIPS
env:
TARGET: mips-unknown-linux-gnu
- name: Linux MIPS64
env:
TARGET: mips64-unknown-linux-gnuabi64
- name: Linux MIPS64 el
env:
TARGET: mips64el-unknown-linux-gnuabi64
- name: Linux mipsel
env:
TARGET: mipsel-unknown-linux-gnu
- name: Linux powerpc64le
env:
TARGET: powerpc64le-unknown-linux-gnu
compute_engine_instance:
image_project: cirrus-images
image: family/docker-builder
platform: linux
cpu: 1 # Since QEMU will only use 1 thread
memory: 4G
setup_script:
- mkdir /tmp/home
- curl --proto '=https' --tlsv1.2 -sSf -o rustup.sh https://sh.rustup.rs
- sh rustup.sh -y --profile=minimal --default-toolchain $MSRV
- . $HOME/.cargo/env
- cargo install cross --version 0.2.1 --locked # cross 0.2.2 bumped the MSRV to 1.58.1
- cp Cargo.lock.msrv Cargo.lock
<< : *TEST
before_cache_script: rm -rf $CARGO_HOME/registry/index
# Tasks for Linux native builds
task:
matrix:
- name: Linux aarch64
arm_container:
image: rust:1.56
env:
TARGET: aarch64-unknown-linux-gnu
- name: Linux x86_64
container:
image: rust:1.56
env:
TARGET: x86_64-unknown-linux-gnu
- name: Linux x86_64 musl
container:
image: rust:1.56
env:
TARGET: x86_64-unknown-linux-musl
setup_script:
- rustup target add $TARGET
- rustup component add clippy
- cp Cargo.lock.msrv Cargo.lock
<< : *TEST
before_cache_script: rm -rf $CARGO_HOME/registry/index
task:
name: Rust Stable
container:
image: rust:latest
env:
TARGET: x86_64-unknown-linux-gnu
setup_script:
- rustup component add clippy
<< : *TEST
before_cache_script: rm -rf $CARGO_HOME/registry/index
# Tasks for cross-compiling, but no testing
task:
container:
image: rust:1.56
env:
BUILD: check
HOST: x86_64-unknown-linux-gnu
matrix:
# Cross claims to support Android, but when it tries to run Nix's tests it
# reports undefined symbol references.
- name: Android aarch64
env:
TARGET: aarch64-linux-android
- name: Android arm
env:
TARGET: arm-linux-androideabi
- name: Android armv7
env:
TARGET: armv7-linux-androideabi
- name: Android i686
env:
TARGET: i686-linux-android
- name: Android x86_64
env:
TARGET: x86_64-linux-android
- name: Linux arm-musleabi
env:
TARGET: arm-unknown-linux-musleabi
- name: Fuchsia x86_64
env:
TARGET: x86_64-fuchsia
- name: Illumos
env:
TARGET: x86_64-unknown-illumos
# Cross claims to support running tests on iOS, but it actually doesn't.
# https://github.com/rust-embedded/cross/issues/535
- name: iOS aarch64
env:
# cargo hack tries to invoke the iphonesimulator SDK for iOS
NOHACK: 1
TARGET: aarch64-apple-ios
- name: iOS x86_64
env:
# cargo hack tries to invoke the iphonesimulator SDK for iOS
NOHACK: 1
TARGET: x86_64-apple-ios
# Cross testing on powerpc fails with "undefined reference to renameat2".
# Perhaps cross is using too-old a version?
- name: Linux powerpc
env:
TARGET: powerpc-unknown-linux-gnu
# Cross claims to support Linux powerpc64, but it really doesn't.
# https://github.com/rust-embedded/cross/issues/441
- name: Linux powerpc64
env:
TARGET: powerpc64-unknown-linux-gnu
- name: Linux s390x
env:
TARGET: s390x-unknown-linux-gnu
- name: Linux x32
env:
TARGET: x86_64-unknown-linux-gnux32
- name: macOS x86_64
env:
TARGET: x86_64-apple-darwin
- name: NetBSD x86_64
env:
TARGET: x86_64-unknown-netbsd
setup_script:
- rustup target add $TARGET
- rustup component add clippy
- cp Cargo.lock.msrv Cargo.lock
<< : *BUILD
before_cache_script: rm -rf $CARGO_HOME/registry/index
task:
container:
# Redox's MSRV policy is unclear. Until they define it, use nightly.
image: rustlang/rust:nightly
env:
BUILD: check
name: Redox x86_64
env:
HOST: x86_64-unknown-linux-gnu
TARGET: x86_64-unknown-redox
CLIPPYFLAGS: -D warnings
setup_script:
- rustup target add $TARGET
- rustup component add clippy
<< : *BUILD
before_cache_script: rm -rf $CARGO_HOME/registry/index
## Rust Tier 3 targets can't use Rustup
task:
container:
image: rustlang/rust:nightly
env:
BUILD: check
HOST: x86_64-unknown-linux-gnu
ZFLAGS: -Zbuild-std
CLIPPYFLAGS: -D warnings
matrix:
- name: DragonFly BSD x86_64
env:
TARGET: x86_64-unknown-dragonfly
- name: OpenBSD x86_64
env:
TARGET: x86_64-unknown-openbsd
- name: Linux armv7 uclibceabihf
env:
TARGET: armv7-unknown-linux-uclibceabihf
- name: Haiku x86_64
env:
TARGET: x86_64-unknown-haiku
setup_script:
- rustup component add rust-src
<< : *BUILD
before_cache_script: rm -rf $CARGO_HOME/registry/index
# Test that we can build with the lowest version of all dependencies.
# "cargo test" doesn't work because some of our dev-dependencies, like
# rand, can't build with their own minimal dependencies.
task:
name: Minver
env:
HOST: x86_64-unknown-linux-gnu
container:
image: rustlang/rust:nightly
setup_script:
- cargo update -Zminimal-versions
check_script:
- cargo check
before_cache_script: rm -rf $CARGO_HOME/registry/index
# Tasks that checks if the code is formatted right using `cargo fmt` tool
task:
name: Rust Formatter
container:
image: rust:latest
setup_script: rustup component add rustfmt
test_script: cargo fmt --all -- --check **/*.rs
+7
View File
@@ -0,0 +1,7 @@
## What does this PR do
## Checklist:
- [ ] I have read `CONTRIBUTING.md`
- [ ] I have written necessary tests and rustdoc comments
- [ ] A change log has been added if this PR modifies nix's API
+72
View File
@@ -0,0 +1,72 @@
name: 'Build'
description: 'Build nix'
inputs:
# This is required
TARGET:
required: true
BUILD:
required: false
default: build
CLIPPYFLAGS:
required: false
default: -D warnings -A unknown-lints
RUSTFLAGS:
required: false
default: -D warnings -A unknown-lints
RUSTDOCFLAGS:
required: false
default: -D warnings
TOOL:
description: 'Tool used to involve the BUILD command, can be cargo or cross'
required: false
default: cargo
ZFLAGS:
required: false
default:
NOHACK:
description: "whether to run cargo hack"
required: false
default: false
runs:
using: "composite"
steps:
- name: set up Rust env
shell: bash
run: |
echo "RUSTFLAGS=${{ inputs.RUSTFLAGS }}" >> $GITHUB_ENV
echo "RUSTDOCFLAGS=${{ inputs.RUSTDOCFLAGS }}" >> $GITHUB_ENV
- name: debug info
shell: bash
run: |
${{ inputs.TOOL }} -Vv
rustc -Vv
- name: build
shell: bash
run: ${{ inputs.TOOL }} ${{ inputs.BUILD }} ${{ inputs.ZFLAGS }} --target ${{ inputs.TARGET }} --all-targets --all-features
- name: doc
shell: bash
run: ${{ inputs.TOOL }} doc ${{ inputs.ZFLAGS }} --no-deps --target ${{ inputs.TARGET }} --all-features
- name: clippy
shell: bash
run: ${{ inputs.TOOL}} clippy ${{ inputs.ZFLAGS }} --target ${{ inputs.TARGET }} --all-targets --all-features -- ${{ inputs.CLIPPYFLAGS }}
- name: Set up cargo-hack
if: inputs.NOHACK == 'false'
uses: taiki-e/install-action@cargo-hack
- name: run cargo hack
shell: bash
if: inputs.NOHACK == 'false'
run: ${{ inputs.TOOL }} hack ${{ inputs.ZFLAGS }} check --target ${{ inputs.TARGET }} --each-feature
@@ -0,0 +1,46 @@
name: 'Check new CHANGELOG'
description: 'Check new CHANGELOG kind and PR number'
runs:
using: "composite"
steps:
- name: Get newly added CHANGELOGs
id: new-changelogs
uses: tj-actions/changed-files@v44
with:
# Only checek the files under the `changelog` directory
files: changelog/**
- name: Check them
shell: bash
if: steps.new-changelogs.outputs.added_files_count != 0
env:
NEW_CHANGELOGS: ${{ steps.new-changelogs.outputs.added_files }}
PR_NUMBER: ${{ github.event.number }}
run: |
# `cl` will be something like "changelog/1.added.md"
for cl in ${NEW_CHANGELOGS}; do
# Trim the directory name
prefix="changelog/"; trimmed_cl=${cl/#$prefix}; cl="${trimmed_cl}";
# parse it
IFS='.' read id kind file_extension <<< "${cl}"
# Check the kind field
if [ "$kind" != "added" ] && [ "$kind" != "changed" ] && [ "$kind" != "fixed" ] && [ "$kind" != "removed" ]; then
echo "Invalid CHANGELOG kind [${kind}] from [${cl}], available options are [added, changed, fixed, removed]";
exit 1;
fi
# Check the file extension
if [ "$file_extension" != "md" ]; then
echo "Invalid file extension [${file_extension}] from [${cl}], it should be [md]";
exit 1;
fi
# Check PR number
if [ "$id" != "$PR_NUMBER" ]; then
echo "Mismatched PR number [${id}] from [${cl}], it should be ${PR_NUMBER}";
exit 1;
fi
done
+32
View File
@@ -0,0 +1,32 @@
name: 'Test'
description: 'Test nix'
inputs:
# This is required
TARGET:
required: true
SUDO:
description: 'Set it to an empty string to run the tests as the current user, leave it with the default value to test with "sudo"'
required: false
default: sudo --preserve-env=HOME
TOOL:
description: 'Tool used to involve the test command, can be cargo or cross'
required: false
default: cargo
RUSTFLAGS:
required: false
default: -D warnings -A unknown-lints
runs:
using: "composite"
steps:
- name: set up Rust env
shell: bash
run: |
echo "RUSTFLAGS=${{ inputs.RUSTFLAGS }}" >> $GITHUB_ENV
- name: test
shell: bash
run: ${{ inputs.SUDO }} $(which ${{ inputs.TOOL }}) test --target ${{ inputs.TARGET }}
+18
View File
@@ -0,0 +1,18 @@
name: Check new CHANGELOGs
on:
pull_request:
types: [opened, synchronize, reopened]
permissions:
contents: read
jobs:
check_new_changelog:
runs-on: ubuntu-24.04
steps:
- name: checkout
uses: actions/checkout@v4
- name: check new CHANGELOG
uses: ./.github/actions/check_new_changelog
+441
View File
@@ -0,0 +1,441 @@
name: CI
on:
pull_request:
types: [opened, synchronize, reopened]
push:
branches:
- master
merge_group:
types: [checks_requested]
permissions:
contents: read
env:
MSRV: 1.69.0
# Rust's Loongarch support merged in 1.71.0
MSRV_LOONGARCH: 1.71.0
# Minimal Rust version to support all 3 official OpenHarmony targets as tier2
MSRV_OHOS: 1.78.0
RUSTFLAGS: -Dwarnings
jobs:
macos:
runs-on: macos-13
env:
TARGET: x86_64-apple-darwin
steps:
- name: checkout
uses: actions/checkout@v4
- name: setup Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: '${{ env.MSRV }}'
components: clippy
- name: build
uses: ./.github/actions/build
with:
TARGET: '${{ env.TARGET }}'
- name: test
uses: ./.github/actions/test
with:
TARGET: '${{ env.TARGET }}'
- name: before_cache_script
run: sudo rm -rf $CARGO_HOME/registry/index
macos-aarch64:
runs-on: macos-latest
env:
TARGET: aarch64-apple-darwin
steps:
- name: checkout
uses: actions/checkout@v4
- name: setup Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: '${{ env.MSRV }}'
components: clippy
- name: build
uses: ./.github/actions/build
with:
TARGET: "${{ env.TARGET }}"
- name: test
uses: ./.github/actions/test
with:
TARGET: "${{ env.TARGET }}"
- name: before_cache_script
run: sudo rm -rf $CARGO_HOME/registry/index
# Use cross for QEMU-based testing
# cross needs to execute Docker, GitHub Action already has it installed
cross:
runs-on: ubuntu-24.04
needs: [rustfmt, minver, macos, x86_64_linux_native_builds, rust_stable]
strategy:
fail-fast: false
matrix:
target: [
arm-unknown-linux-gnueabi,
armv7-unknown-linux-gnueabihf,
i686-unknown-linux-gnu,
i686-unknown-linux-musl,
# Disable MIPS CIs, see https://github.com/nix-rust/nix/issues/2593
# for detailed info.
#
# mips-unknown-linux-gnu,
# mips64-unknown-linux-gnuabi64,
# mips64el-unknown-linux-gnuabi64,
# mipsel-unknown-linux-gnu,
powerpc64le-unknown-linux-gnu,
loongarch64-unknown-linux-gnu,
]
steps:
- name: checkout
uses: actions/checkout@v4
- name: setup Rust
uses: dtolnay/rust-toolchain@master
with:
# Use a newer version rustc if the target is Loongarch, remove this workaround after MSRV is newer than 1.71.0
toolchain: "${{ matrix.target == 'loongarch64-unknown-linux-gnu' && env.MSRV_LOONGARCH || env.MSRV }}"
components: clippy
# cross relies on docker or podman, GitHub Acton already has it installed.
- name: Set up cross
uses: taiki-e/install-action@v2
with:
tool: cross@0.2.5
- name: build
uses: ./.github/actions/build
with:
TARGET: '${{ matrix.target }}'
TOOL: cross
RUSTFLAGS: --cfg qemu -D warnings
- name: test
uses: ./.github/actions/test
with:
TARGET: '${{ matrix.target }}'
SUDO: ""
TOOL: cross
RUSTFLAGS: --cfg qemu -D warnings
- name: before_cache_script
run: rm -rf $CARGO_HOME/registry/index
# Tasks for x86_64 Linux native builds
x86_64_linux_native_builds:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
target: [
x86_64-unknown-linux-gnu,
x86_64-unknown-linux-musl,
]
steps:
- name: checkout
uses: actions/checkout@v4
- name: setup Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: '${{ env.MSRV }}'
components: clippy
- name: install targets
run: rustup target add ${{ matrix.target }}
- name: build
uses: ./.github/actions/build
with:
TARGET: '${{ matrix.TARGET }}'
- name: test
uses: ./.github/actions/test
with:
TARGET: '${{ matrix.TARGET }}'
- name: before_cache_script
run: sudo rm -rf $CARGO_HOME/registry/index;
# Tasks for aarch64 Linux native builds
aarch64_linux_native_builds:
runs-on: ubuntu-24.04-arm
strategy:
fail-fast: false
matrix:
target: [
aarch64-unknown-linux-gnu,
aarch64-unknown-linux-musl,
]
steps:
- name: checkout
uses: actions/checkout@v4
- name: setup Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: '${{ env.MSRV }}'
components: clippy
- name: install targets
run: rustup target add ${{ matrix.target }}
- name: build
uses: ./.github/actions/build
with:
TARGET: '${{ matrix.TARGET }}'
- name: test
uses: ./.github/actions/test
with:
TARGET: '${{ matrix.TARGET }}'
- name: before_cache_script
run: sudo rm -rf $CARGO_HOME/registry/index;
rust_stable:
runs-on: ubuntu-latest
env:
TARGET: x86_64-unknown-linux-gnu
steps:
- name: checkout
uses: actions/checkout@v4
- name: setup Rust
uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- name: build
uses: ./.github/actions/build
with:
TARGET: '${{ env.TARGET }}'
- name: test
uses: ./.github/actions/test
with:
TARGET: '${{ env.TARGET }}'
- name: before_cache_script
run: sudo rm -rf $CARGO_HOME/registry/index
# Tasks for cross-compiling, but no testing
cross_compiling:
runs-on: ubuntu-latest
needs: [rustfmt, minver, macos, x86_64_linux_native_builds, rust_stable]
env:
BUILD: check
strategy:
fail-fast: false
matrix:
include:
# Cross claims to support Android, but when it tries to run Nix's tests it
# reports undefined symbol references.
- target: aarch64-linux-android
- target: arm-linux-androideabi
- target: armv7-linux-androideabi
- target: i686-linux-android
- target: x86_64-linux-android
- target: arm-unknown-linux-musleabi
- target: x86_64-unknown-fuchsia
- target: x86_64-unknown-illumos
# Cross claims to support running tests on iOS, but it actually doesn't.
# https://github.com/rust-embedded/cross/issues/535
- target: aarch64-apple-ios
# cargo hack tries to invoke the iphonesimulator SDK for iOS
NOHACK: true
# Cross claims to support Linux powerpc64, but it really doesn't.
# https://github.com/rust-embedded/cross/issues/441
- target: powerpc64-unknown-linux-gnu
- target: s390x-unknown-linux-gnu
- target: x86_64-unknown-linux-gnux32
- target: x86_64-unknown-netbsd
- target: aarch64-unknown-linux-ohos
- target: armv7-unknown-linux-ohos
- target: x86_64-unknown-linux-ohos
steps:
- name: checkout
uses: actions/checkout@v4
- name: setup Rust
uses: dtolnay/rust-toolchain@master
with:
# Use a newer version rustc if it is OpenHarmony, remove this workaround after MSRV is newer than 1.78.0
toolchain: "${{ contains(matrix.target, 'ohos') && env.MSRV_OHOS || env.MSRV }}"
components: clippy
- name: install targets
run: rustup target add ${{ matrix.target }}
- name: build
uses: ./.github/actions/build
with:
TARGET: '${{ matrix.target }}'
BUILD: '${{ env.BUILD }}'
NOHACK: '${{ matrix.NOHACK }}'
- name: before_cache_script
run: rm -rf $CARGO_HOME/registry/index
redox:
runs-on: ubuntu-latest
needs: [rustfmt, minver, macos, x86_64_linux_native_builds, rust_stable]
env:
TARGET: x86_64-unknown-redox
CLIPPYFLAGS: -D warnings
BUILD: check
steps:
- name: checkout
uses: actions/checkout@v4
- name: setup Rust
# Redox's MSRV policy is unclear. Until they define it, use nightly.
uses: dtolnay/rust-toolchain@nightly
with:
components: clippy
- name: install targets
run: rustup target add ${{ env.TARGET }}
- name: build
uses: ./.github/actions/build
with:
TARGET: '${{ env.TARGET }}'
BUILD: '${{ env.BUILD }}'
CLIPPYFLAGS: '${{ env.CLIPPYFLAGS }}'
- name: before_cache_script
run: rm -rf $CARGO_HOME/registry/index
# Rust Tier 3 targets can't use Rustup
tier3:
runs-on: ubuntu-latest
env:
BUILD: check
ZFLAGS: -Zbuild-std
CLIPPYFLAGS: -D warnings
strategy:
fail-fast: false
matrix:
include:
- target: x86_64-unknown-dragonfly
- target: x86_64-unknown-openbsd
- target: x86_64-unknown-haiku
- target: armv7-unknown-linux-uclibceabihf
# Disable Hurd due to
# 1. https://github.com/rust-lang/libc/issues/4421
# 2. https://github.com/nix-rust/nix/pull/2635#issuecomment-2842062528
#
# We can bring it back when 1 gets fixed and it is applied to the std lib
# - target: i686-unknown-hurd-gnu
steps:
- name: checkout
uses: actions/checkout@v4
- name: setup Rust
uses: dtolnay/rust-toolchain@nightly
with:
components: clippy
- name: install src
run: rustup component add rust-src
- name: build
uses: ./.github/actions/build
with:
TARGET: '${{ matrix.target }}'
BUILD: '${{ env.BUILD }}'
ZFLAGS: '${{ env.ZFLAGS }}'
CLIPPYFLAGS: '${{ env.CLIPPYFLAGS }}'
- name: before_cache_script
run: rm -rf $CARGO_HOME/registry/index
solaris:
name: solaris (x86_64-pc-solaris)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: build and test
uses: vmactions/solaris-vm@v1
with:
release: "11.4-gcc"
usesh: true
mem: 4096
copyback: false
prepare: |
source <(curl -s https://raw.githubusercontent.com/psumbera/solaris-rust/refs/heads/main/sh.rust-web-install)
echo "~~~~ rustc --version ~~~~"
rustc --version
echo "~~~~ Solaris-version ~~~~"
uname -a
run: |
export PATH=$HOME/.rust_solaris/bin:$PATH
cargo build --target x86_64-pc-solaris --all-targets --all-features && sudo cargo test
# Test that we can build with the lowest version of all dependencies.
# "cargo test" doesn't work because some of our dev-dependencies, like
# rand, can't build with their own minimal dependencies.
minver:
runs-on: ubuntu-latest
env:
TARGET: x86_64-unknown-linux-gnu
steps:
- name: checkout
uses: actions/checkout@v4
- name: setup Rust
uses: dtolnay/rust-toolchain@nightly
- name: setup
run: cargo update -Zdirect-minimal-versions
- name: check
run: cargo check
- name: before_cache_script
run: rm -rf $CARGO_HOME/registry/index
# Tasks that checks if the code is formatted right using `cargo fmt` tool
rustfmt:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- name: Check format
run: cargo fmt --all -- --check **/*.rs
- name: before_cache_script
run: rm -rf $CARGO_HOME/registry/index
+6 -4
View File
@@ -19,8 +19,8 @@ ohos_cargo_crate("lib") {
crate_root = "src/lib.rs"
sources = ["src/lib.rs"]
edition = "2018"
cargo_pkg_version = "0.26.4"
edition = "2021"
cargo_pkg_version = "0.30.1"
cargo_pkg_authors = "The nix-rust Project Developers"
cargo_pkg_name = "nix"
cargo_pkg_description = "Rust friendly bindings to *nix APIs"
@@ -30,14 +30,15 @@ ohos_cargo_crate("lib") {
"//third_party/rust/crates/libc:lib",
"//third_party/rust/crates/memoffset:lib",
"//third_party/rust/crates/pin-utils:lib",
"//third_party/rust/crates/static-assertions-rs:lib",
]
features = [
"acct",
"aio",
"default",
"dir",
"env",
"event",
"fanotify",
"feature",
"fs",
"hostname",
@@ -47,9 +48,9 @@ ohos_cargo_crate("lib") {
"memoffset",
"mman",
"mount",
"mqueue",
"net",
"personality",
"pin-utils",
"poll",
"process",
"pthread",
@@ -60,6 +61,7 @@ ohos_cargo_crate("lib") {
"sched",
"signal",
"socket",
"syslog",
"term",
"time",
"ucontext",
+542 -10
View File
@@ -1,20 +1,549 @@
# Change Log
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](https://semver.org/).
## [0.26.4] - 2023-08-28
# Change Log
## [0.30.1] - 2025-05-04
### Fixed
- Fixed an unintended API change in release 0.26.3, due to the upgrade of the
bitflags dependency.
([#2117](https://github.com/nix-rust/nix/pull/2117))
- 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
### Added
- Add `getregset()/setregset()` for Linux/glibc/x86/x86_64/aarch64/riscv64 and
`getregs()/setregs()` for Linux/glibc/aarch64/riscv64
([#2044](https://github.com/nix-rust/nix/pull/2044))
- Add socket option Ipv6Ttl for apple targets.
([#2287](https://github.com/nix-rust/nix/pull/2287))
- Add socket option UtunIfname.
([#2325](https://github.com/nix-rust/nix/pull/2325))
- make SigAction repr(transparent) & can be converted to the libc raw type
([#2326](https://github.com/nix-rust/nix/pull/2326))
- Add `From` trait implementation for conversions between `sockaddr_in` and
`SockaddrIn`, `sockaddr_in6` and `SockaddrIn6`
([#2328](https://github.com/nix-rust/nix/pull/2328))
- Add socket option ReusePortLb for FreeBSD.
([#2332](https://github.com/nix-rust/nix/pull/2332))
- Added support for openat2 on linux.
([#2339](https://github.com/nix-rust/nix/pull/2339))
- Add if_indextoname function.
([#2340](https://github.com/nix-rust/nix/pull/2340))
- Add `mount` and `unmount` API for apple targets.
([#2347](https://github.com/nix-rust/nix/pull/2347))
- Added `_PC_MIN_HOLE_SIZE` for `pathconf` and `fpathconf`.
([#2349](https://github.com/nix-rust/nix/pull/2349))
- Added `impl AsFd for pty::PtyMaster`
([#2355](https://github.com/nix-rust/nix/pull/2355))
- Add `open` flag `O_SEARCH` to AIX, Empscripten, FreeBSD, Fuchsia, solarish,
WASI ([#2374](https://github.com/nix-rust/nix/pull/2374))
- Add prctl function `prctl_set_vma_anon_name` for Linux/Android.
([#2378](https://github.com/nix-rust/nix/pull/2378))
- Add `sync(2)` for `apple_targets/solarish/haiku/aix/hurd`, `syncfs(2)` for
`hurd` and `fdatasync(2)` for `aix/hurd`
([#2379](https://github.com/nix-rust/nix/pull/2379))
- Add fdatasync support for Apple targets.
([#2380](https://github.com/nix-rust/nix/pull/2380))
- Add `fcntl::OFlag::O_PATH` for FreeBSD and Fuchsia
([#2382](https://github.com/nix-rust/nix/pull/2382))
- Added `PathconfVar::MIN_HOLE_SIZE` for apple_targets.
([#2388](https://github.com/nix-rust/nix/pull/2388))
- Add `open` flag `O_SEARCH` to apple_targets
([#2391](https://github.com/nix-rust/nix/pull/2391))
- `O_DSYNC` may now be used with `aio_fsync` and `fcntl` on FreeBSD.
([#2404](https://github.com/nix-rust/nix/pull/2404))
- Added `Flock::relock` for upgrading and downgrading locks.
([#2407](https://github.com/nix-rust/nix/pull/2407))
### Changed
- Change the `ForkptyResult` type to the following repr so that the
uninitialized
`master` field won't be accessed in the child process:
```rs
pub enum ForkptyResult {
Parent {
child: Pid,
master: OwnedFd,
},
Child,
}
``` ([#2315](https://github.com/nix-rust/nix/pull/2315))
- Updated `cfg_aliases` dependency from version 0.1 to 0.2
([#2322](https://github.com/nix-rust/nix/pull/2322))
- Change the signature of `ptrace::write` and `ptrace::write_user` to make them
safe ([#2324](https://github.com/nix-rust/nix/pull/2324))
- Allow use of `SignalFd` through shared reference
Like with many other file descriptors, concurrent use of signalfds is safe.
Changing the signal mask of and reading signals from a signalfd can now be
done
with the `SignalFd` API even if other references to it exist.
([#2367](https://github.com/nix-rust/nix/pull/2367))
- Changed tee, splice and vmsplice RawFd arguments to AsFd.
([#2387](https://github.com/nix-rust/nix/pull/2387))
- Added I/O safety to the sys/aio module. Most functions that previously
accepted a `AsRawFd` argument now accept an `AsFd` instead.
([#2401](https://github.com/nix-rust/nix/pull/2401))
- `RecvMsg::cmsgs()` now returns a `Result`, and checks that cmsgs were not
truncated. ([#2413](https://github.com/nix-rust/nix/pull/2413))
### Fixed
- No longer panics when the `fanotify` queue overflows.
([#2399](https://github.com/nix-rust/nix/pull/2399))
- Fixed ControlMessageOwned::UdpGroSegments wrapped type from u16 to i32 to
reflect the used kernel's one.
([#2406](https://github.com/nix-rust/nix/pull/2406))
## [0.28.0] - 2024-02-24
### Added
- Added `mkdtemp` wrapper ([#1297](https://github.com/nix-rust/nix/pull/1297))
- Add associated constants `UTIME_OMIT` `UTIME_NOW` for `TimeSpec`
([#1879](https://github.com/nix-rust/nix/pull/1879))
- Added `EventFd` type. ([#1945](https://github.com/nix-rust/nix/pull/1945))
- - Added `impl From<Signal> for SigSet`.
- Added `impl std::ops::BitOr for SigSet`.
- Added `impl std::ops::BitOr for Signal`.
- Added `impl std::ops::BitOr<Signal> for SigSet`
([#1959](https://github.com/nix-rust/nix/pull/1959))
- Added `TlsGetRecordType` control message type and corresponding enum for
linux ([#2065](https://github.com/nix-rust/nix/pull/2065))
- Added `Ipv6HopLimit` to `::nix::sys::socket::ControlMessage` for Linux,
MacOS, FreeBSD, DragonflyBSD, Android, iOS and Haiku.
([#2074](https://github.com/nix-rust/nix/pull/2074))
- Added `Icmp` and `IcmpV6` to `SockProtocol`
([#2103](https://github.com/nix-rust/nix/pull/2103))
- Added rfork support for FreeBSD in `unistd`
([#2121](https://github.com/nix-rust/nix/pull/2121))
- Added `MapFlags::map_hugetlb_with_size_log2` method for Linux targets
([#2125](https://github.com/nix-rust/nix/pull/2125))
- Added `mmap_anonymous` function
([#2127](https://github.com/nix-rust/nix/pull/2127))
- Added `mips32r6` and `mips64r6` support for signal, ioctl and ptrace
([#2138](https://github.com/nix-rust/nix/pull/2138))
- Added `F_GETPATH` FcntlFlags entry on Apple/NetBSD/DragonflyBSD for
`::nix::fcntl`. ([#2142](https://github.com/nix-rust/nix/pull/2142))
- Added `F_KINFO` FcntlFlags entry on FreeBSD for `::nix::fcntl`.
([#2152](https://github.com/nix-rust/nix/pull/2152))
- Added `F_GETPATH_NOFIRMLINK` and `F_BARRIERFSYNC` FcntlFlags entry
on Apple for `::nix::fcntl`.
([#2155](https://github.com/nix-rust/nix/pull/2155))
- Added newtype `Flock` to automatically unlock a held flock upon drop.
Added `Flockable` trait to represent valid types for `Flock`.
([#2170](https://github.com/nix-rust/nix/pull/2170))
- Added `SetSockOpt` impls to enable Linux Kernel TLS on a TCP socket and to
import TLS parameters. ([#2175](https://github.com/nix-rust/nix/pull/2175))
- - Added the `::nix::sys::socket::SocketTimestamp` enum for configuring the
`TsClock` (a.k.a `SO_TS_CLOCK`) sockopt
- Added FreeBSD's `ScmRealtime` and `ScmMonotonic` as new options in
`::nix::sys::socket::ControlMessageOwned`
([#2187](https://github.com/nix-rust/nix/pull/2187))
- Added new fanotify API: wrappers for `fanotify_init` and `fanotify_mark`
([#2194](https://github.com/nix-rust/nix/pull/2194))
- Added `SpecialCharacterindices` support for haiku.
([#2195](https://github.com/nix-rust/nix/pull/2195))
- Added `sys::sendfile` support for solaris/illumos.
([#2198](https://github.com/nix-rust/nix/pull/2198))
- impl Display for InterfaceFlags
([#2206](https://github.com/nix-rust/nix/pull/2206))
- Added `sendfilev` in sys::sendfile for solarish
([#2207](https://github.com/nix-rust/nix/pull/2207))
- Added `fctrl::SealFlag::F_SEAL_FUTURE_WRITE`
([#2213](https://github.com/nix-rust/nix/pull/2213))
- Added `Ipv6MulticastHops` as socket option to set and read.
([#2234](https://github.com/nix-rust/nix/pull/2234))
- Enable `ControlMessageOwned::Ipv4RecvIf` and
`ControlMessageOwned::Ipv4RecvDstAddr` for DragonFlyBSD
([#2240](https://github.com/nix-rust/nix/pull/2240))
- `ClockId::set_time()` and `time::clock_settime()` are now enabled on macOS
([#2241](https://github.com/nix-rust/nix/pull/2241))
- Added `IpBindAddressNoPort` sockopt to support `IP_BIND_ADDRESS_NO_PORT`
available on linux. ([#2244](https://github.com/nix-rust/nix/pull/2244))
- Enable `MapFlags::map_hugetlb_with_size_log2` method for Android/Fuchsia
([#2245](https://github.com/nix-rust/nix/pull/2245))
- Added `TcpFastOpenConnect` sockopt to support `TCP_FASTOPEN_CONNECT`
available on linux. ([#2247](https://github.com/nix-rust/nix/pull/2247))
- Add `reboot(2)` for OpenBSD/NetBSD
([#2251](https://github.com/nix-rust/nix/pull/2251))
- Added new `MemFdCreateFlag` constants to `sys::memfd` on Linux and Android
related to hugetlbfs support.
([#2252](https://github.com/nix-rust/nix/pull/2252))
- Expose the inner fd of `Kqueue` through:
* impl AsFd for Kqueue
* impl From\<Kqueue\> for OwnedFd
([#2258](https://github.com/nix-rust/nix/pull/2258))
- Added `sys::eventfd` support on FreeBSD
([#2259](https://github.com/nix-rust/nix/pull/2259))
- Added `MmapFlags::MAP_FIXED` constant in `sys::mman` for netbsd and openbsd
([#2260](https://github.com/nix-rust/nix/pull/2260))
- Added the `SO_LISTENQLIMIT` sockopt.
([#2263](https://github.com/nix-rust/nix/pull/2263))
- Enable the `AT_EMPTY_PATH` flag for the `fchownat()` function
([#2267](https://github.com/nix-rust/nix/pull/2267))
- Add `AtFlags::AT_EMPTY_PATH` for FreeBSD and Hurd
([#2270](https://github.com/nix-rust/nix/pull/2270))
- Enable `OFlag::O_DIRECTORY for Solarish
([#2275](https://github.com/nix-rust/nix/pull/2275))
- Added the `Backlog` wrapper type for the `listen` call.
([#2276](https://github.com/nix-rust/nix/pull/2276))
- Add `clock_nanosleep()` ([#2277](https://github.com/nix-rust/nix/pull/2277))
- Enabled `O_DIRECT` in `fcntl::OFlags` for solarish
([#2278](https://github.com/nix-rust/nix/pull/2278))
- Added a new API sigsuspend.
([#2279](https://github.com/nix-rust/nix/pull/2279))
- - Added `errno::Errno::set` function
- Added `errno::Errno::set_raw` function
- Added `errno::Errno::last_raw` function
- Added `errno::Errno::from_raw` function
([#2283](https://github.com/nix-rust/nix/pull/2283))
- Enable the `AT_EMPTY_PATH` flag for the `linkat()` function
([#2284](https://github.com/nix-rust/nix/pull/2284))
- Enable unistd::{sync, syncfs} for Android
([#2296](https://github.com/nix-rust/nix/pull/2296))
### Changed
- `poll` now takes `PollTimeout` replacing `libc::c_int`.
([#1876](https://github.com/nix-rust/nix/pull/1876))
- Deprecated `sys::eventfd::eventfd`.
([#1945](https://github.com/nix-rust/nix/pull/1945))
- `mmap`, `mmap_anonymous`, `munmap`, `mremap`, `madvise`, `msync`, `mprotect`,
`munlock` and `mlock` updated to use `NonNull`.
([#2000](https://github.com/nix-rust/nix/pull/2000))
- `mmap` function now accepts `F` instead of `Option<F>`
([#2127](https://github.com/nix-rust/nix/pull/2127))
- `PollFd::new` now takes a `BorrowedFd` argument, with relaxed lifetime
requirements relative to the previous version.
([#2134](https://github.com/nix-rust/nix/pull/2134))
- `FdSet::{insert, remove, contains}` now take `BorrowedFd` arguments, and have
relaxed lifetime requirements relative to 0.27.1.
([#2136](https://github.com/nix-rust/nix/pull/2136))
- The following APIs now take an implementation of `AsFd` rather than a
`RawFd`:
- `unistd::tcgetpgrp`
- `unistd::tcsetpgrp`
- `unistd::fpathconf`
- `unistd::ttyname`
- `unistd::getpeereid` ([#2137](https://github.com/nix-rust/nix/pull/2137))
- Changed `openat()` and `Dir::openat()`, now take optional `dirfd`s
([#2139](https://github.com/nix-rust/nix/pull/2139))
- The MSRV is now 1.69 ([#2144](https://github.com/nix-rust/nix/pull/2144))
- Changed function `SockaddrIn::ip()` to return `net::Ipv4Addr` and refactored
`SocketAddrV6::ip()` to be `const`
([#2151](https://github.com/nix-rust/nix/pull/2151))
- The following APIs now take optional `dirfd`s:
- `readlinkat()`
- `fstatat()`
- `mknodat()`
- `mkdirat()`
- `execveat()`
([#2157](https://github.com/nix-rust/nix/pull/2157))
- `Epoll::wait` now takes `EpollTimeout` replacing `isize`.
([#2202](https://github.com/nix-rust/nix/pull/2202))
- - Deprecated `errno::errno()` function (use `Errno::last_raw()`)
- Deprecated `errno::from_i32()` function (use `Errno::from_raw()`)
- Deprecated `errno::Errno::from_i32()` function (use `Errno::from_raw()`)
([#2283](https://github.com/nix-rust/nix/pull/2283))
### Fixed
- Fix `SigSet` incorrect implementation of `Eq`, `PartialEq` and `Hash`
([#1946](https://github.com/nix-rust/nix/pull/1946))
- Fixed `::sys::socket::sockopt::IpMulticastTtl` by fixing the value of optlen
passed to `libc::setsockopt` and added tests.
([#2072](https://github.com/nix-rust/nix/pull/2072))
- Fixed the function signature of `recvmmsg`, potentially causing UB
([#2119](https://github.com/nix-rust/nix/pull/2119))
- Fix `SignalFd::set_mask`. In 0.27.0 it would actually close the file
descriptor. ([#2141](https://github.com/nix-rust/nix/pull/2141))
- Fixed UnixAddr::new for haiku, it did not record the `sun_len` value as
needed.
Fixed `sys::socket::addr::from_raw_parts` and
`sys::socket::Sockaddrlike::len` build for solaris.
([#2242](https://github.com/nix-rust/nix/pull/2242))
- Fixed solaris build globally.
([#2248](https://github.com/nix-rust/nix/pull/2248))
- Changed the `dup3` wrapper to perform a real call to `dup3` instead of
emulating it via `dup2` and `fcntl` to get rid of race condition
([#2268](https://github.com/nix-rust/nix/pull/2268))
- Fixed `::unistd::Group::members` using read_unaligned to avoid crash on
misaligned pointers ([#2311](https://github.com/nix-rust/nix/pull/2311))
### Removed
- The `FchownatFlags` type has been deprecated, please use `AtFlags` instead.
([#2267](https://github.com/nix-rust/nix/pull/2267))
- Removed the `dup3` wrapper on macOS, which was emulated via `dup2` and
`fcntl` and could cause a race condition. The `dup3` system call is not
supported on macOS. ([#2268](https://github.com/nix-rust/nix/pull/2268))
- The `LinkatFlags` type has been deprecated, please use `AtFlags` instead.
([#2284](https://github.com/nix-rust/nix/pull/2284))
## [0.27.1] - 2023-08-28
### Fixed
- Fixed generating the documentation on docs.rs.
([#2111](https://github.com/nix-rust/nix/pull/2111))
## [0.27.0] - 2023-08-28
### Added
- Added `AT_EACCESS` to `AtFlags` on all platforms but android
([#1995](https://github.com/nix-rust/nix/pull/1995))
- Add `PF_ROUTE` to `SockType` on macOS, iOS, all of the BSDs, Fuchsia, Haiku, Illumos.
([#1867](https://github.com/nix-rust/nix/pull/1867))
- Added `nix::ucontext` module on `aarch64-unknown-linux-gnu`.
(#[1662](https://github.com/nix-rust/nix/pull/1662))
- Added `CanRaw` to `SockProtocol` and `CanBcm` as a separate `SocProtocol` constant.
([#1912](https://github.com/nix-rust/nix/pull/1912))
- Added `Generic` and `NFLOG` to `SockProtocol`.
([#2092](https://github.com/nix-rust/nix/pull/2092))
- Added `mq_timedreceive` to `::nix::mqueue`.
([#1966])(https://github.com/nix-rust/nix/pull/1966)
- Added `LocalPeerPid` to `nix::sys::socket::sockopt` for macOS. ([#1967](https://github.com/nix-rust/nix/pull/1967))
- Added `TFD_TIMER_CANCEL_ON_SET` to `::nix::sys::time::TimerSetTimeFlags` on Linux and Android.
([#2040](https://github.com/nix-rust/nix/pull/2040))
- Added `SOF_TIMESTAMPING_OPT_ID` and `SOF_TIMESTAMPING_OPT_TSONLY` to `nix::sys::socket::TimestampingFlag`.
([#2048](https://github.com/nix-rust/nix/pull/2048))
- Enabled socket timestamping options on Android. ([#2077](https://github.com/nix-rust/nix/pull/2077))
- Added vsock support for macOS ([#2056](https://github.com/nix-rust/nix/pull/2056))
- Added `SO_SETFIB` and `SO_USER_COOKIE` to `nix::sys::socket::sockopt` for FreeBSD.
([#2085](https://github.com/nix-rust/nix/pull/2085))
- Added `SO_RTABLE` for OpenBSD and `SO_ACCEPTFILTER` for FreeBSD/NetBSD to `nix::sys::socket::sockopt`.
([#2085](https://github.com/nix-rust/nix/pull/2085))
- Added `MSG_WAITFORONE` to `MsgFlags` on Android, Fuchsia, Linux, NetBSD,
FreeBSD, OpenBSD, and Solaris.
([#2014](https://github.com/nix-rust/nix/pull/2014))
- Added `SO_TS_CLOCK` for FreeBSD to `nix::sys::socket::sockopt`.
([#2093](https://github.com/nix-rust/nix/pull/2093))
- Added support for prctl in Linux.
(#[1550](https://github.com/nix-rust/nix/pull/1550))
- `nix::socket` and `nix::select` are now available on Redox.
([#2012](https://github.com/nix-rust/nix/pull/2012))
- Implemented AsFd, AsRawFd, FromRawFd, and IntoRawFd for `mqueue::MqdT`.
([#2097](https://github.com/nix-rust/nix/pull/2097))
- Add the ability to set `kevent_flags` on `SigEvent`.
([#1731](https://github.com/nix-rust/nix/pull/1731))
### Changed
- All Cargo features have been removed from the default set. Users will need to
specify which features they depend on in their Cargo.toml.
([#2091](https://github.com/nix-rust/nix/pull/2091))
- Implemented I/O safety for many, but not all, of Nix's APIs. Many public
functions argument and return types have changed:
| Original Type | New Type |
| ------------- | --------------------- |
| AsRawFd | AsFd |
| RawFd | BorrowedFd or OwnedFd |
(#[1906](https://github.com/nix-rust/nix/pull/1906))
- Use I/O safety with `copy_file_range`, and expose it on FreeBSD.
(#[1906](https://github.com/nix-rust/nix/pull/1906))
- The MSRV is now 1.65
([#1862](https://github.com/nix-rust/nix/pull/1862))
([#2104](https://github.com/nix-rust/nix/pull/2104))
- The epoll interface now uses a type.
([#1882](https://github.com/nix-rust/nix/pull/1882))
- With I/O-safe type applied in `pty::OpenptyResult` and `pty::ForkptyResult`,
users no longer need to manually close the file descriptors in these types.
([#1921](https://github.com/nix-rust/nix/pull/1921))
- Refactored `name` parameter of `mq_open` and `mq_unlink` to be generic over
`NixPath`.
([#2102](https://github.com/nix-rust/nix/pull/2102)).
- Made `clone` unsafe, like `fork`.
([#1993](https://github.com/nix-rust/nix/pull/1993))
### Removed
- `sys::event::{kevent, kevent_ts}` are deprecated in favor of
`sys::kevent::Kqueue::kevent`, and `sys::event::kqueue` is deprecated in
favor of `sys::kevent::Kqueue::new`.
([#1943](https://github.com/nix-rust/nix/pull/1943))
- Removed deprecated IoVec API.
([#1855](https://github.com/nix-rust/nix/pull/1855))
- Removed deprecated net APIs.
([#1861](https://github.com/nix-rust/nix/pull/1861))
- `nix::sys::signalfd::signalfd` is deprecated. Use
`nix::sys::signalfd::SignalFd` instead.
([#1938](https://github.com/nix-rust/nix/pull/1938))
- Removed `SigEvent` support on Fuchsia, where it was unsound.
([#2079](https://github.com/nix-rust/nix/pull/2079))
- Removed `flock` from `::nix::fcntl` on Solaris.
([#2082](https://github.com/nix-rust/nix/pull/2082))
## [0.26.3] - 2023-08-27
### Fixed
- Fix: send `ETH_P_ALL` in htons format
- Fix: send `ETH_P_ALL` in htons format
([#1925](https://github.com/nix-rust/nix/pull/1925))
- Fix: `recvmsg` now sets the length of the received `sockaddr_un` field
correctly on Linux platforms. ([#2041](https://github.com/nix-rust/nix/pull/2041))
@@ -27,8 +556,11 @@ This project adheres to [Semantic Versioning](https://semver.org/).
([#2095](https://github.com/nix-rust/nix/pull/2095))
## [0.26.2] - 2023-01-18
### Fixed
- Fix `SockaddrIn6` bug that was swapping flowinfo and scope_id byte ordering.
- Fix `SockaddrIn6` bug that was swapping `flowinfo` and `scope_id` byte
ordering.
([#1964](https://github.com/nix-rust/nix/pull/1964))
## [0.26.1] - 2022-11-29
@@ -99,7 +631,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
([#1824](https://github.com/nix-rust/nix/pull/1824))
- Workaround XNU bug causing netmasks returned by `getifaddrs` to misbehave.
([#1788](https://github.com/nix-rust/nix/pull/1788))
### Removed
- Removed deprecated error constants and conversions.
@@ -232,7 +764,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
(#[1563](https://github.com/nix-rust/nix/pull/1563))
- Added `process_vm_readv` and `process_vm_writev` on Android.
(#[1557](https://github.com/nix-rust/nix/pull/1557))
- Added `nix::uncontext` module on s390x.
- Added `nix::ucontext` module on s390x.
(#[1662](https://github.com/nix-rust/nix/pull/1662))
- Implemented `Extend`, `FromIterator`, and `IntoIterator` for `SigSet` and
added `SigSet::iter` and `SigSetIter`.
+37 -26
View File
@@ -61,11 +61,32 @@ pull' model described there.
Please make pull requests against the `master` branch.
If you change the API by way of adding, removing or changing something or if
you fix a bug, please add an appropriate note to the [change log][cl]. We
follow the conventions of [Keep A CHANGELOG][kacl].
you fix a bug, please add an appropriate note, every note should be a new markdown
file under the [changelog directory][cl] stating the change made by your pull request,
the filename should be in the following format:
[cl]: https://github.com/nix-rust/nix/blob/master/CHANGELOG.md
[kacl]: https://github.com/olivierlacan/keep-a-changelog/tree/18adb5f5be7a898d046f6a4acb93e39dcf40c4ad
```
<PULL_REQUEST_ID>.<TYPE>.md
```
These are 4 `TYPE`s available:
1. `added`
2. `changed`
3. `fixed`
4. `removed`
Let's say you have added a new API to nix, then a change log like this should
be added (assume it is PR #0)
```md
# file: 0.added.md
Added a new API xxx
```
And having multiple change logs for one PR is allowed.
[cl]: https://github.com/nix-rust/nix/tree/master/changelog
[pr-docs]: https://help.github.com/articles/using-pull-requests/
## Testing
@@ -75,36 +96,26 @@ requests to include tests where they make sense. For example, when fixing a bug,
add a test that would have failed without the fix.
After you've made your change, make sure the tests pass in your development
environment. We also have [continuous integration set up on
Cirrus-CI][cirrus-ci], which might find some issues on other platforms. The CI
will run once you open a pull request.
There is also infrastructure for running tests for other targets
locally. More information is available in the [CI Readme][ci-readme].
environment. We also have continuous integration set up on [Cirrus-CI][cirrus-ci]
and GitHub Action, which might find some issues on other platforms. The CI will
run once you open a pull request.
[cirrus-ci]: https://cirrus-ci.com/github/nix-rust/nix
[ci-readme]: ci/README.md
### Disabling a test in the CI environment
Sometimes there are features that cannot be tested in the CI environment.
To stop a test from running under CI, add `skip_if_cirrus!()` to it. Please
Sometimes there are features that cannot be tested in the CI environment. To
stop a test from running under CI, add `skip_if_cirrus!()` to it. Please
describe the reason it shouldn't run under CI, and a link to an issue if
possible!
possible! Other tests cannot be run under QEMU, which is used for some
architectures. To skip them, add a `#[cfg_attr(qemu, ignore)]` attribute to
the test.
## bors, the bot who merges all the PRs
All pull requests are merged via [bors], an integration bot. After the
pull request has been reviewed, the reviewer will leave a comment like
> bors r+
to let bors know that it was approved. Then bors will check that it passes
tests when merged with the latest changes in the `master` branch, and
merge if the tests succeed.
[bors]: https://bors-ng.github.io/
## GitHub Merge Queues
We use GitHub merge queues to ensure that subtle merge conflicts won't result
in failing code. If you add or remove a CI job, remember to adjust the
required status checks in the repository's branch protection rules!
## API conventions
+72 -4
View File
@@ -17,8 +17,15 @@ We follow the conventions laid out in [Keep A CHANGELOG][kacl].
## libc constants, functions and structs
We do not define integer constants ourselves, but use or reexport them from the
[libc crate][libc].
We do not define ffi functions or their associated constants and types ourselves,
but use or reexport them from the [libc crate][libc], if your PR uses something
that does not exist in the libc crate, you should add it to libc first. Once
your libc PR gets merged, you can adjust our `libc` dependency to include that
libc change. Use a git dependency if necessary.
```toml
libc = { git = "https://github.com/rust-lang/libc", rev = "the commit includes your libc PR", ... }
```
We use the functions exported from [libc][libc] instead of writing our own
`extern` declarations.
@@ -37,6 +44,16 @@ impl SigSet {
When creating newtypes, we use Rust's `CamelCase` type naming convention.
## cfg gates
When creating operating-system-specific functionality, we gate it by
`#[cfg(target_os = ...)]`. If **MORE THAN ONE operating system** is affected, we
prefer to use the cfg aliases defined in build.rs, like `#[cfg(bsd)]`.
Please **DO NOT** use cfg aliases for **ONLY ONE** system as [they are bad][mismatched_target_os].
[mismatched_target_os]: https://rust-lang.github.io/rust-clippy/master/index.html#/mismatched_target_os
## Bitflags
Many C functions have flags parameters that are combined from constants using
@@ -57,9 +74,9 @@ libc_bitflags!{
PROT_READ;
PROT_WRITE;
PROT_EXEC;
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
PROT_GROWSDOWN;
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
PROT_GROWSUP;
}
}
@@ -84,3 +101,54 @@ the variable.
[enum]: https://doc.rust-lang.org/reference.html#enumerations
[libc]: https://crates.io/crates/libc/
[std_MaybeUninit]: https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html
## Pointer type casting
We prefer [`cast()`], [`cast_mut()`] and [`cast_const()`] to cast pointer types
over the `as` keyword because it is much more difficult to accidentally change
type or mutability that way.
[`cast()`]: https://doc.rust-lang.org/std/primitive.pointer.html#method.cast
[`cast_mut()`]: https://doc.rust-lang.org/std/primitive.pointer.html#method.cast_mut
[`cast_const()`]: https://doc.rust-lang.org/std/primitive.pointer.html#method.cast_const
## Remove/deprecate an interface
In Nix, if we want to remove something, we don't do it immediately, instead, we
deprecate it for at least one release before removing it.
To deprecate an interface, put the following attribute on the top of it:
```
#[deprecated(since = "<Version>", note = "<Note to our user>")]
```
`<Version>` is the version where this interface will be deprecated, in most
cases, it will be the version of the next release. And a user-friendly note
should be added. Normally, there should be a new interface that will replace
the old one, so a note should be something like: "`<New Interface>` should be
used instead".
## Where to put a test
If you want to add a test for a feature that is in `xxx.rs`, then the test should
be put in the corresponding `test_xxx.rs` file unless you cannot do this, e.g.,
the test involves private stuff and thus cannot be added outside of Nix, then
it is allowed to leave the test in `xxx.rs`.
## Syscall/libc function error handling
Most syscall and libc functions return an [`ErrnoSentinel`][trait] value on error,
we has a nice utility function [`Errno::result()`][util] to convert it to the
Rusty `Result<T, Errno>` type, e.g., here is how `dup(2)` uses it:
```rs
pub fn dup(oldfd: RawFd) -> Result<RawFd> {
let res = unsafe { libc::dup(oldfd) };
Errno::result(res)
}
```
[trait]: https://docs.rs/nix/latest/nix/errno/trait.ErrnoSentinel.html
[util]: https://docs.rs/nix/latest/nix/errno/enum.Errno.html#method.result
+25 -28
View File
@@ -1,16 +1,17 @@
[package]
name = "nix"
description = "Rust friendly bindings to *nix APIs"
edition = "2018"
version = "0.26.4"
rust-version = "1.56"
edition = "2021"
version = "0.30.1"
rust-version = "1.69"
authors = ["The nix-rust Project Developers"]
repository = "https://github.com/nix-rust/nix"
license = "MIT"
categories = ["os::unix-apis"]
include = ["src/**/*", "test/**/*", "LICENSE", "README.md", "CHANGELOG.md"]
include = ["build.rs", "src/**/*", "test/**/*", "LICENSE", "README.md", "CHANGELOG.md"]
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
targets = [
"x86_64-unknown-linux-gnu",
@@ -21,34 +22,27 @@ 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"
]
[dependencies]
libc = { version = "0.2.137", features = [ "extra_traits" ] }
bitflags = "1.1"
libc = { version = "0.2.171", features = ["extra_traits"] }
bitflags = "2.3.3"
cfg-if = "1.0"
pin-utils = { version = "0.1.0", optional = true }
[target.'cfg(not(target_os = "redox"))'.dependencies]
memoffset = { version = "0.7", optional = true }
memoffset = { version = "0.9", optional = true }
[features]
default = [
"acct", "aio", "dir", "env", "event", "feature", "fs",
"hostname", "inotify", "ioctl", "kmod", "mman", "mount", "mqueue",
"net", "personality", "poll", "process", "pthread", "ptrace", "quota",
"reboot", "resource", "sched", "signal", "socket", "term", "time",
"ucontext", "uio", "user", "zerocopy",
]
default = []
acct = []
aio = ["pin-utils"]
dir = ["fs"]
env = []
event = []
event = ["poll"]
fanotify = []
feature = []
fs = []
hostname = []
@@ -70,6 +64,7 @@ resource = []
sched = ["process"]
signal = ["process"]
socket = ["memoffset"]
syslog = []
term = []
time = []
ucontext = ["signal"]
@@ -79,11 +74,15 @@ zerocopy = ["fs", "uio"]
[dev-dependencies]
assert-impl = "0.1"
lazy_static = "1.4"
parking_lot = "0.12"
rand = "0.8"
tempfile = "3.3"
rand = "0.9"
tempfile = "3.7.1"
semver = "1.0.7"
nix = { path = ".", features = ["acct", "aio", "dir", "env", "event", "fanotify",
"feature", "fs", "hostname", "inotify", "ioctl", "kmod", "mman", "mount", "mqueue",
"net", "personality", "poll", "pthread", "ptrace", "quota", "process", "reboot",
"resource", "sched", "signal", "socket", "syslog", "term", "time", "ucontext", "uio",
"user", "zerocopy"] }
[target.'cfg(any(target_os = "android", target_os = "linux"))'.dev-dependencies]
caps = "0.5.3"
@@ -91,6 +90,9 @@ caps = "0.5.3"
[target.'cfg(target_os = "freebsd")'.dev-dependencies]
sysctl = "0.4"
[build-dependencies]
cfg_aliases = "0.2.1"
[[test]]
name = "test"
path = "test/test.rs"
@@ -104,10 +106,5 @@ name = "test-clearenv"
path = "test/test_clearenv.rs"
[[test]]
name = "test-mount"
path = "test/test_mount.rs"
harness = false
[[test]]
name = "test-ptymaster-drop"
path = "test/test_ptymaster_drop.rs"
name = "test-prctl"
path = "test/sys/test_prctl.rs"
+3
View File
@@ -3,3 +3,6 @@ passthrough = [
"RUSTFLAGS",
"RUST_TEST_THREADS"
]
[target.loongarch64-unknown-linux-gnu]
image = "ghcr.io/cross-rs/loongarch64-unknown-linux-gnu:edge"
+4 -3
View File
@@ -3,14 +3,15 @@
"Name": "nix",
"License": "MIT",
"License File": "LICENSE",
"Version Number": "0.26.4",
"Version Number": "0.30.1",
"Owner": "fangting12@huawei.com",
"Upstream URL": "https://github.com/nix-rust/nix",
"Description": "A Rust library that provides support for interacting with Unix-like operating systems.",
"Dependencies": [
"Static Assertions",
"libc",
"bitflags"
"memoffset",
"autocfg",
"cfg-if",
"pin-utils"
]
}
+68 -47
View File
@@ -2,8 +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)
[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
@@ -30,7 +31,7 @@ pub fn gethostname() -> Result<OsString>;
## Supported Platforms
nix target support consists of two tiers. While nix attempts to support all
nix target support consists of three tiers. While nix attempts to support all
platforms supported by [libc](https://github.com/rust-lang/libc), only some
platforms are actively supported due to either technical or manpower
limitations. Support for platforms is split into three tiers:
@@ -41,55 +42,75 @@ limitations. Support for platforms is split into three tiers:
blocks the inclusion of new code. Tests may be run, but failures
in tests don't block the inclusion of new code.
* Tier 3 - Builds for this target are run in CI. Failures during the build
*do not* block the inclusion of new code. Testing may be run, but
failures in tests don't block the inclusion of new code.
*do not* necessarily block the inclusion of new code. That is, at
our discretion a Tier 3 target may be dropped at any time, if it
would otherwise block development.
Platforms not listed are supported on a best-effort basis, relying on our users
to report any problems.
The following targets are supported by `nix`:
Tier 1:
* aarch64-apple-darwin
* aarch64-unknown-linux-gnu
* arm-unknown-linux-gnueabi
* armv7-unknown-linux-gnueabihf
* i686-unknown-freebsd
* i686-unknown-linux-gnu
* i686-unknown-linux-musl
* mips-unknown-linux-gnu
* mips64-unknown-linux-gnuabi64
* mips64el-unknown-linux-gnuabi64
* mipsel-unknown-linux-gnu
* powerpc64le-unknown-linux-gnu
* x86_64-unknown-freebsd
* x86_64-unknown-linux-gnu
* x86_64-unknown-linux-musl
Tier 2:
* aarch64-apple-ios
* aarch64-linux-android
* arm-linux-androideabi
* arm-unknown-linux-musleabi
* armv7-linux-androideabi
* i686-linux-android
* powerpc-unknown-linux-gnu
* s390x-unknown-linux-gnu
* x86_64-apple-ios
* x86_64-linux-android
* x86_64-apple-darwin
* x86_64-unknown-illumos
* x86_64-unknown-netbsd
Tier 3:
* armv7-unknown-linux-uclibceabihf
* x86_64-fuchsia
* x86_64-unknown-dragonfly
* x86_64-unknown-haiku
* x86_64-unknown-linux-gnux32
* x86_64-unknown-openbsd
* x86_64-unknown-redox
<table>
<tr>
<th>Tier 1</th>
<th>Tier 2</th>
<th>Tier 3</th>
</tr>
<tr>
<td>
<ul>
<li>aarch64-apple-darwin</li>
<li>aarch64-unknown-linux-gnu</li>
<li>arm-unknown-linux-gnueabi</li>
<li>armv7-unknown-linux-gnueabihf</li>
<li>i686-unknown-freebsd</li>
<li>i686-unknown-linux-gnu</li>
<li>i686-unknown-linux-musl</li>
<li>mips-unknown-linux-gnu</li>
<li>mips64-unknown-linux-gnuabi64</li>
<li>mips64el-unknown-linux-gnuabi64</li>
<li>mipsel-unknown-linux-gnu</li>
<li>powerpc64le-unknown-linux-gnu</li>
<li>x86_64-unknown-freebsd</li>
<li>x86_64-unknown-linux-gnu</li>
<li>x86_64-unknown-linux-musl</li>
</ul>
</td>
<td>
<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-unknown-fuchsia</li>
<li>x86_64-unknown-dragonfly</li>
<li>x86_64-unknown-haiku</li>
<li>x86_64-unknown-linux-gnux32</li>
<li>x86_64-unknown-openbsd</li>
<li>x86_64-unknown-redox</li>
<li>i686-unknown-hurd-gnu</li>
</td>
</tr>
</table>
## Minimum Supported Rust Version (MSRV)
nix is supported on Rust 1.56.1 and higher. Its MSRV will not be
nix is supported on Rust 1.69 and higher. Its MSRV will not be
changed in the future without bumping the major or minor version.
## Contributing
@@ -97,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
+54 -7
View File
@@ -4,16 +4,63 @@ library.
# Before Release
Nix uses [cargo release](https://github.com/crate-ci/cargo-release) to automate
the release process. Based on changes since the last release, pick a new
version number following semver conventions. For nix, a change that drops
the release process. Based on changes since the last release, pick a new
version number following semver conventions. For Nix, a change that drops
support for some Rust versions counts as a breaking change, and requires a
major bump.
The release is prepared as follows:
- Ask for a new libc version if, necessary. It usually is. Then update the
dependency in Cargo.toml accordingly.
> NOTE: the following procedure should be done directly against the master
> branch of the repo.
- Clone the `nix-rust/nix` repository with your preferred way, and `cd` to it:
```sh
$ git clone https://github.com/nix-rust/nix.git
$ cd nix
```
- If we are using `libc` from git, replace it with a usable release from crates.io.
```diff
[dependencies]
-libc = { git = "https://github.com/rust-lang/libc", rev = "<Revision>", features = ["extra_traits"] }
+libc = { version = "<Version>", features = ["extra_traits"] }
```
- Update the version number in `Cargo.toml`
- Generate `CHANGELOG.md` for this release by
```sh
$ towncrier build --version=<VERSION> --yes
Loading template...
Finding news fragments...
Rendering news fragments...
Writing to newsfile...
Staging newsfile...
Removing the following files:
nix/changelog/xxxx.xxxx.md
nix/changelog/xxxx.xxxx.md
...
nix/changelog/xxxx.xxxx.md
Removing news fragments...
Done!
```
- Push the changes made by the above steps to the master branch
- Ensure you have a crates.io token
1. With the `publish-update` scope
2. Can be used for crate `nix`
3. It is set via `cargo login`
If not, create a new token [here](https://crates.io/settings/tokens), and set
it.
- Confirm that everything's ready for a release by running
`cargo release <patch|minor|major>`
- Create the release with `cargo release -x <patch|minor|major>`
- Push the created tag to GitHub.
`cargo release <VERSION>`
- Create the release with `cargo release -x <VERSION>`, this step will publish
the version to crates.io and push the new version tag to GitHub.
- Congratulations on a new Nix release!
-54
View File
@@ -1,54 +0,0 @@
status = [
"Android aarch64",
"Android arm",
"Android armv7",
"Android i686",
"Android x86_64",
"DragonFly BSD x86_64",
"FreeBSD 12 amd64 & i686",
"FreeBSD 14 amd64 & i686",
"Fuchsia x86_64",
"Linux MIPS",
"Linux MIPS64 el",
"Linux MIPS64",
"Linux aarch64",
"Linux arm gnueabi",
"Linux arm-musleabi",
"Linux armv7 gnueabihf",
"Linux armv7 uclibceabihf",
"Linux i686 musl",
"Linux i686",
"Linux mipsel",
"Linux powerpc",
"Linux powerpc64",
"Linux powerpc64le",
"Linux s390x",
"Linux x32",
"Linux x86_64 musl",
"Linux x86_64",
"macOS aarch64",
"macOS x86_64",
"Minver",
"NetBSD x86_64",
"OpenBSD x86_64",
"Redox x86_64",
"Rust Stable",
"Rust Formatter",
"iOS aarch64",
"iOS x86_64",
"Illumos",
"Haiku x86_64",
]
# Set bors's timeout to 1 hour
#
# bors's timeout should always be at least twice as long as the test suite
# takes. This is to allow the CI provider to fast-fail a test; if one of the
# builders immediately reports a failure, then bors will move on to the next
# batch, leaving the slower builders to work through the already-doomed run and
# the next one.
#
# At the time this was written, nix's test suite took about twenty minutes to
# run. The timeout was raised to one hour to give nix room to grow and time
# for delays on Cirrus's end.
timeout_sec = 3600
+35
View File
@@ -0,0 +1,35 @@
use cfg_aliases::cfg_aliases;
fn main() {
cfg_aliases! {
android: { target_os = "android" },
dragonfly: { target_os = "dragonfly" },
ios: { target_os = "ios" },
freebsd: { target_os = "freebsd" },
illumos: { target_os = "illumos" },
linux: { target_os = "linux" },
macos: { target_os = "macos" },
netbsd: { target_os = "netbsd" },
openbsd: { target_os = "openbsd" },
solaris: { target_os = "solaris" },
watchos: { target_os = "watchos" },
tvos: { target_os = "tvos" },
visionos: { target_os = "visionos" },
// cfg aliases we would like to use
apple_targets: { any(ios, macos, watchos, tvos, visionos) },
bsd: { any(freebsd, dragonfly, netbsd, openbsd, apple_targets) },
bsd_without_apple: { any(freebsd, dragonfly, netbsd, openbsd) },
linux_android: { any(android, linux) },
freebsdlike: { any(dragonfly, freebsd) },
netbsdlike: { any(netbsd, openbsd) },
solarish: { any(illumos, solaris) },
}
// 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\"))");
}
+5
View File
@@ -0,0 +1,5 @@
Do not remove this file. This is used to keep the `changelog` dir around after
generating new changelog file.
Without this, `towncrier` would remove the changelog files as well as the
directory if it is empty.
+55
View File
@@ -0,0 +1,55 @@
//! Print all interfaces and interface addresses on the system, in a format
//! similar to ifconfig(8).
#![cfg(feature = "net")]
#[cfg(any(bsd, linux_android, target_os = "illumos"))]
fn main() {
use nix::ifaddrs::getifaddrs;
use nix::sys::socket::{SockaddrLike, SockaddrStorage};
let addrs = getifaddrs().unwrap();
let mut ifname = None;
for addr in addrs {
if ifname.as_ref() != Some(&addr.interface_name) {
if ifname.is_some() {
println!();
}
ifname = Some(addr.interface_name.clone());
println!(
"{}: flags={:x}<{}>",
addr.interface_name,
addr.flags.bits(),
addr.flags
);
}
if let Some(dl) = addr.address.as_ref().unwrap().as_link_addr() {
if dl.addr().is_none() {
continue;
}
}
let family = addr
.address
.as_ref()
.and_then(SockaddrStorage::family)
.map(|af| format!("{af:?}"))
.unwrap_or("".to_owned());
match (
&addr.address,
&addr.netmask,
&addr.broadcast,
&addr.destination,
) {
(Some(a), Some(nm), Some(b), None) => {
println!("\t{family} {a} netmask {nm} broadcast {b}")
}
(Some(a), Some(nm), None, None) => {
println!("\t{family} {a} netmask {nm}")
}
(Some(a), None, None, None) => println!("\t{family} {a}"),
(Some(a), None, None, Some(d)) => println!("\t{family} {a} -> {d}"),
x => todo!("{x:?}"),
}
}
}
#[cfg(not(any(bsd, linux_android, target_os = "illumos")))]
fn main() {}
+8
View File
@@ -0,0 +1,8 @@
# If no sub-command is given, simply list all the available options
_default:
just --list
# Build the doc
doc *args='':
RUSTDOCFLAGS='--cfg docsrs' cargo +nightly doc --all-features --no-deps {{args}}
-4
View File
@@ -1,4 +0,0 @@
pre-release-replacements = [
{ file="CHANGELOG.md", search="Unreleased", replace="{{version}}" },
{ file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}" }
]
+90 -41
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: 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 success or failure.
/// 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()) }
}
@@ -224,16 +282,15 @@ impl Entry {
#[allow(clippy::unnecessary_cast)]
pub fn ino(&self) -> u64 {
cfg_if! {
if #[cfg(any(target_os = "android",
if #[cfg(any(target_os = "aix",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "l4re",
target_os = "linux",
target_os = "macos",
target_os = "solaris"))] {
target_os = "hurd",
target_os = "cygwin",
solarish,
linux_android,
apple_targets))] {
self.0.d_ino as u64
} else {
u64::from(self.0.d_fileno)
@@ -243,7 +300,7 @@ impl Entry {
/// Returns the bare file name of this directory entry without any other leading path component.
pub fn file_name(&self) -> &ffi::CStr {
unsafe { ::std::ffi::CStr::from_ptr(self.0.d_name.as_ptr()) }
unsafe { ffi::CStr::from_ptr(self.0.d_name.as_ptr()) }
}
/// Returns the type of this directory entry, if known.
@@ -252,11 +309,7 @@ impl Entry {
/// notably, some Linux filesystems don't implement this. The caller should use `stat` or
/// `fstat` if this returns `None`.
pub fn file_type(&self) -> Option<Type> {
#[cfg(not(any(
target_os = "illumos",
target_os = "solaris",
target_os = "haiku"
)))]
#[cfg(not(any(solarish, target_os = "aix", target_os = "haiku")))]
match self.0.d_type {
libc::DT_FIFO => Some(Type::Fifo),
libc::DT_CHR => Some(Type::CharacterDevice),
@@ -269,11 +322,7 @@ impl Entry {
}
// illumos, Solaris, and Haiku systems do not have the d_type member at all:
#[cfg(any(
target_os = "illumos",
target_os = "solaris",
target_os = "haiku"
))]
#[cfg(any(solarish, target_os = "aix", target_os = "haiku"))]
None
}
}
+3 -4
View File
@@ -40,13 +40,12 @@ impl std::error::Error for ClearEnvError {}
/// thread safety must still be upheld.
pub unsafe fn clearenv() -> std::result::Result<(), ClearEnvError> {
cfg_if! {
if #[cfg(any(target_os = "fuchsia",
if #[cfg(any(linux_android,
target_os = "fuchsia",
target_os = "wasi",
target_env = "uclibc",
target_os = "linux",
target_os = "android",
target_os = "emscripten"))] {
let ret = libc::clearenv();
let ret = unsafe { libc::clearenv() };
} else {
use std::env;
for (name, _) in env::vars_os() {
+1243 -517
View File
File diff suppressed because it is too large Load Diff
+1005 -268
View File
File diff suppressed because it is too large Load Diff
+16 -15
View File
@@ -1,11 +1,12 @@
//! Feature tests for OS functionality
pub use self::os::*;
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(any(linux_android, target_os = "emscripten"))]
mod os {
use crate::sys::utsname::uname;
use crate::Result;
use std::os::unix::ffi::OsStrExt;
use std::sync::atomic::{AtomicUsize, Ordering};
// Features:
// * atomic cloexec on socket: 2.6.27
@@ -72,15 +73,15 @@ mod os {
}
fn kernel_version() -> Result<usize> {
static mut KERNEL_VERS: usize = 0;
static KERNEL_VERS: AtomicUsize = AtomicUsize::new(0);
let mut kernel_vers = KERNEL_VERS.load(Ordering::Relaxed);
unsafe {
if KERNEL_VERS == 0 {
KERNEL_VERS = parse_kernel_version()?;
}
Ok(KERNEL_VERS)
if kernel_vers == 0 {
kernel_vers = parse_kernel_version()?;
KERNEL_VERS.store(kernel_vers, Ordering::Relaxed);
}
Ok(kernel_vers)
}
/// Check if the OS supports atomic close-on-exec for sockets
@@ -91,18 +92,18 @@ mod os {
}
#[test]
pub fn test_parsing_kernel_version() {
fn test_parsing_kernel_version() {
assert!(kernel_version().unwrap() > 0);
}
}
#[cfg(any(
target_os = "dragonfly", // Since ???
target_os = "freebsd", // Since 10.0
freebsdlike, // FreeBSD since 10.0 DragonFlyBSD since ???
netbsdlike, // NetBSD since 6.0 OpenBSD since 5.7
target_os = "hurd", // Since glibc 2.28
target_os = "illumos", // Since ???
target_os = "netbsd", // Since 6.0
target_os = "openbsd", // Since 5.7
target_os = "redox", // Since 1-july-2020
target_os = "cygwin",
))]
mod os {
/// Check if the OS supports atomic close-on-exec for sockets
@@ -112,8 +113,8 @@ mod os {
}
#[cfg(any(
target_os = "macos",
target_os = "ios",
target_os = "aix",
apple_targets,
target_os = "fuchsia",
target_os = "haiku",
target_os = "solaris"
+22 -18
View File
@@ -4,7 +4,7 @@
//! of interfaces and their associated addresses.
use cfg_if::cfg_if;
#[cfg(any(target_os = "ios", target_os = "macos"))]
#[cfg(apple_targets)]
use std::convert::TryFrom;
use std::ffi;
use std::iter::Iterator;
@@ -33,7 +33,7 @@ pub struct InterfaceAddress {
}
cfg_if! {
if #[cfg(any(target_os = "android", target_os = "emscripten", target_os = "fuchsia", target_os = "linux"))] {
if #[cfg(any(linux_android, target_os = "emscripten", target_os = "fuchsia"))] {
fn get_ifu_from_sockaddr(info: &libc::ifaddrs) -> *const libc::sockaddr {
info.ifa_ifu
}
@@ -53,7 +53,7 @@ cfg_if! {
/// ss_len field to sizeof(sockaddr_storage). This is supposedly valid as all
/// members of the sockaddr_storage are "ok" with being zeroed out (there are
/// no pointers).
#[cfg(any(target_os = "ios", target_os = "macos"))]
#[cfg(apple_targets)]
unsafe fn workaround_xnu_bug(info: &libc::ifaddrs) -> Option<SockaddrStorage> {
let src_sock = info.ifa_netmask;
if src_sock.is_null() {
@@ -62,22 +62,24 @@ unsafe fn workaround_xnu_bug(info: &libc::ifaddrs) -> Option<SockaddrStorage> {
let mut dst_sock = mem::MaybeUninit::<libc::sockaddr_storage>::zeroed();
// memcpy only sa_len bytes, assume the rest is zero
std::ptr::copy_nonoverlapping(
src_sock as *const u8,
dst_sock.as_mut_ptr() as *mut u8,
(*src_sock).sa_len.into(),
);
let dst_sock = unsafe {
// memcpy only sa_len bytes, assume the rest is zero
std::ptr::copy_nonoverlapping(
src_sock as *const u8,
dst_sock.as_mut_ptr().cast(),
(*src_sock).sa_len.into(),
);
// Initialize ss_len to sizeof(libc::sockaddr_storage).
(*dst_sock.as_mut_ptr()).ss_len =
u8::try_from(mem::size_of::<libc::sockaddr_storage>()).unwrap();
let dst_sock = dst_sock.assume_init();
// Initialize ss_len to sizeof(libc::sockaddr_storage).
(*dst_sock.as_mut_ptr()).ss_len =
u8::try_from(mem::size_of::<libc::sockaddr_storage>()).unwrap();
dst_sock.assume_init()
};
let dst_sock_ptr =
&dst_sock as *const libc::sockaddr_storage as *const libc::sockaddr;
SockaddrStorage::from_raw(dst_sock_ptr, None)
unsafe { SockaddrStorage::from_raw(dst_sock_ptr, None) }
}
impl InterfaceAddress {
@@ -85,14 +87,16 @@ impl InterfaceAddress {
fn from_libc_ifaddrs(info: &libc::ifaddrs) -> InterfaceAddress {
let ifname = unsafe { ffi::CStr::from_ptr(info.ifa_name) };
let address = unsafe { SockaddrStorage::from_raw(info.ifa_addr, None) };
#[cfg(any(target_os = "ios", target_os = "macos"))]
#[cfg(apple_targets)]
let netmask = unsafe { workaround_xnu_bug(info) };
#[cfg(not(any(target_os = "ios", target_os = "macos")))]
#[cfg(not(apple_targets))]
let netmask =
unsafe { SockaddrStorage::from_raw(info.ifa_netmask, None) };
let mut addr = InterfaceAddress {
interface_name: ifname.to_string_lossy().to_string(),
flags: InterfaceFlags::from_bits_truncate(info.ifa_flags as i32),
interface_name: ifname.to_string_lossy().into_owned(),
flags: InterfaceFlags::from_bits_truncate(
info.ifa_flags as IflagsType,
),
address,
netmask,
broadcast: None,
+8 -4
View File
@@ -3,7 +3,7 @@
//! For more details see
use std::ffi::CStr;
use std::os::unix::io::AsRawFd;
use std::os::unix::io::{AsFd, AsRawFd};
use crate::errno::Errno;
use crate::Result;
@@ -79,15 +79,15 @@ libc_bitflags!(
/// ```
///
/// See [`man init_module(2)`](https://man7.org/linux/man-pages/man2/init_module.2.html) for more information.
pub fn finit_module<T: AsRawFd>(
fd: &T,
pub fn finit_module<Fd: AsFd>(
fd: Fd,
param_values: &CStr,
flags: ModuleInitFlags,
) -> Result<()> {
let res = unsafe {
libc::syscall(
libc::SYS_finit_module,
fd.as_raw_fd(),
fd.as_fd().as_raw_fd(),
param_values.as_ptr(),
flags.bits(),
)
@@ -102,7 +102,11 @@ libc_bitflags!(
/// See [`man delete_module(2)`](https://man7.org/linux/man-pages/man2/delete_module.2.html)
/// for a detailed description how these flags work.
pub struct DeleteModuleFlags: libc::c_int {
/// `delete_module` will return immediately, with an error, if the module has a nonzero
/// reference count.
O_NONBLOCK;
/// `delete_module` will unload the module immediately, regardless of whether it has a
/// nonzero reference count.
O_TRUNC;
}
);
+105 -26
View File
@@ -12,11 +12,12 @@
//! * `dir` - Stuff relating to directory iteration
//! * `env` - Manipulate environment variables
//! * `event` - Event-driven APIs, like `kqueue` and `epoll`
//! * `fanotify` - Linux's `fanotify` filesystem events monitoring API
//! * `feature` - Query characteristics of the OS at runtime
//! * `fs` - File system functionality
//! * `hostname` - Get and set the system's hostname
//! * `inotify` - Linux's `inotify` file system notification API
//! * `ioctl` - The `ioctl` syscall, and wrappers for my specific instances
//! * `ioctl` - The `ioctl` syscall, and wrappers for many specific instances
//! * `kmod` - Load and unload kernel modules
//! * `mman` - Stuff relating to memory management
//! * `mount` - Mount and unmount file systems
@@ -33,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
@@ -41,20 +43,65 @@
//! * `zerocopy` - APIs like `sendfile` and `copy_file_range`
#![crate_name = "nix"]
#![cfg(unix)]
#![cfg_attr(docsrs, doc(cfg(all())))]
#![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(feature = "default"), allow(unused_imports))]
#![cfg_attr(
not(all(
feature = "acct",
feature = "aio",
feature = "dir",
feature = "env",
feature = "event",
feature = "fanotify",
feature = "feature",
feature = "fs",
feature = "hostname",
feature = "inotify",
feature = "ioctl",
feature = "kmod",
feature = "mman",
feature = "mount",
feature = "mqueue",
feature = "net",
feature = "personality",
feature = "poll",
feature = "process",
feature = "pthread",
feature = "ptrace",
feature = "quota",
feature = "reboot",
feature = "resource",
feature = "sched",
feature = "socket",
feature = "signal",
feature = "syslog",
feature = "term",
feature = "time",
feature = "ucontext",
feature = "uio",
feature = "user",
feature = "zerocopy",
)),
allow(unused_imports)
)]
#![deny(unstable_features)]
#![deny(missing_copy_implementations)]
#![deny(missing_debug_implementations)]
#![warn(missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![deny(clippy::cast_ptr_alignment)]
#![allow(clippy::bad_bit_mask)]
#![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;
@@ -81,30 +128,22 @@ feature! {
#[deny(missing_docs)]
pub mod features;
}
#[allow(missing_docs)]
pub mod fcntl;
feature! {
#![feature = "net"]
#[cfg(any(target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "illumos",
target_os = "openbsd"))]
#[cfg(any(linux_android,
bsd,
solarish))]
#[deny(missing_docs)]
pub mod ifaddrs;
#[cfg(not(target_os = "redox"))]
#[deny(missing_docs)]
pub mod net;
}
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(linux_android)]
feature! {
#![feature = "kmod"]
#[allow(missing_docs)]
pub mod kmod;
}
feature! {
@@ -112,9 +151,8 @@ feature! {
pub mod mount;
}
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "linux",
freebsdlike,
all(target_os = "linux", not(target_env = "ohos")),
target_os = "netbsd"
))]
feature! {
@@ -138,23 +176,46 @@ feature! {
pub mod sys;
feature! {
#![feature = "time"]
#[allow(missing_docs)]
pub mod time;
}
// This can be implemented for other platforms as soon as libc
// provides bindings for them.
#[cfg(all(
target_os = "linux",
any(target_arch = "s390x", target_arch = "x86", target_arch = "x86_64")
any(
target_arch = "aarch64",
target_arch = "s390x",
target_arch = "x86",
target_arch = "x86_64"
)
))]
feature! {
#![feature = "ucontext"]
#[allow(missing_docs)]
pub mod ucontext;
}
#[allow(missing_docs)]
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;
@@ -175,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.
@@ -259,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:
@@ -271,7 +332,7 @@ impl NixPath for [u8] {
}
let mut buf = MaybeUninit::<[u8; MAX_STACK_ALLOCATION]>::uninit();
let buf_ptr = buf.as_mut_ptr() as *mut u8;
let buf_ptr = buf.as_mut_ptr().cast();
unsafe {
ptr::copy_nonoverlapping(self.as_ptr(), buf_ptr, self.len());
@@ -332,3 +393,21 @@ impl NixPath for PathBuf {
self.as_os_str().with_nix_path(f)
}
}
/// Like `NixPath::with_nix_path()`, but allow the `path` argument to be optional.
///
/// A NULL pointer will be provided if `path.is_none()`.
#[cfg(any(
all(apple_targets, feature = "mount"),
all(linux_android, any(feature = "mount", feature = "fanotify"))
))]
pub(crate) fn with_opt_nix_path<P, T, F>(path: Option<&P>, f: F) -> Result<T>
where
P: ?Sized + NixPath,
F: FnOnce(*const libc::c_char) -> T,
{
match path {
Some(path) => path.with_nix_path(|p_str| f(p_str.as_ptr())),
None => Ok(f(ptr::null())),
}
}
+8 -5
View File
@@ -27,9 +27,9 @@ macro_rules! feature {
/// /// PROT_WRITE enables write protect
/// PROT_WRITE;
/// PROT_EXEC;
/// #[cfg(any(target_os = "linux", target_os = "android"))]
/// #[cfg(linux_android)]
/// PROT_GROWSDOWN;
/// #[cfg(any(target_os = "linux", target_os = "android"))]
/// #[cfg(linux_android)]
/// PROT_GROWSUP;
/// }
/// }
@@ -63,6 +63,8 @@ macro_rules! libc_bitflags {
}
) => {
::bitflags::bitflags! {
#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[repr(transparent)]
$(#[$outer])*
pub struct $BitFlags: $T {
$(
@@ -87,15 +89,14 @@ macro_rules! libc_bitflags {
/// PROT_READ,
/// PROT_WRITE,
/// PROT_EXEC,
/// #[cfg(any(target_os = "linux", target_os = "android"))]
/// #[cfg(linux_android)]
/// PROT_GROWSDOWN,
/// #[cfg(any(target_os = "linux", target_os = "android"))]
/// #[cfg(linux_android)]
/// PROT_GROWSUP,
/// }
/// }
/// ```
// Some targets don't use all rules.
#[allow(unknown_lints)]
#[allow(unused_macro_rules)]
macro_rules! libc_enum {
// Exit rule.
@@ -133,6 +134,8 @@ macro_rules! libc_enum {
impl ::std::convert::TryFrom<$repr> for $BitFlags {
type Error = $crate::Error;
#[allow(unused_doc_comments)]
#[allow(deprecated)]
#[allow(unused_attributes)]
fn try_from(x: $repr) -> $crate::Result<Self> {
match x {
$($try_froms)*
+111
View File
@@ -0,0 +1,111 @@
use crate::{Errno, NixPath, Result};
use libc::c_int;
libc_bitflags!(
/// Used with [`mount()`] and [`unmount()`].
pub struct MntFlags: c_int {
/// Do not interpret special files on the filesystem.
MNT_NODEV;
/// Enable data protection on the filesystem if the filesystem is configured for it.
MNT_CPROTECT;
/// file system is quarantined
MNT_QUARANTINE;
/// filesystem is stored locally
MNT_LOCAL;
/// quotas are enabled on filesystem
MNT_QUOTA;
/// identifies the root filesystem
MNT_ROOTFS;
/// file system is not appropriate path to user data
MNT_DONTBROWSE;
/// VFS will ignore ownership information on filesystem objects
MNT_IGNORE_OWNERSHIP;
/// filesystem was mounted by automounter
MNT_AUTOMOUNTED;
/// filesystem is journaled
MNT_JOURNALED;
/// Don't allow user extended attributes
MNT_NOUSERXATTR;
/// filesystem should defer writes
MNT_DEFWRITE;
/// don't block unmount if not responding
MNT_NOBLOCK;
/// file system is exported
MNT_EXPORTED;
/// file system written asynchronously
MNT_ASYNC;
/// Force a read-write mount even if the file system appears to be
/// unclean.
MNT_FORCE;
/// MAC support for objects.
MNT_MULTILABEL;
/// Do not update access times.
MNT_NOATIME;
/// Disallow program execution.
MNT_NOEXEC;
/// Do not honor setuid or setgid bits on files when executing them.
MNT_NOSUID;
/// Mount read-only.
MNT_RDONLY;
/// Causes the vfs subsystem to update its data structures pertaining to
/// the specified already mounted file system.
MNT_RELOAD;
/// Create a snapshot of the file system.
MNT_SNAPSHOT;
/// All I/O to the file system should be done synchronously.
MNT_SYNCHRONOUS;
/// Union with underlying fs.
MNT_UNION;
/// Indicates that the mount command is being applied to an already
/// mounted file system.
MNT_UPDATE;
}
);
/// Mount a file system.
///
/// # Arguments
/// - `source` - Specifies the file system. e.g. `/dev/sd0`.
/// - `target` - Specifies the destination. e.g. `/mnt`.
/// - `flags` - Optional flags controlling the mount.
/// - `data` - Optional file system specific data.
///
/// # see also
/// [`mount`](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/mount.2.html)
pub fn mount<
P1: ?Sized + NixPath,
P2: ?Sized + NixPath,
P3: ?Sized + NixPath,
>(
source: &P1,
target: &P2,
flags: MntFlags,
data: Option<&P3>,
) -> Result<()> {
let res = source.with_nix_path(|s| {
target.with_nix_path(|t| {
crate::with_opt_nix_path(data, |d| unsafe {
libc::mount(
s.as_ptr(),
t.as_ptr(),
flags.bits(),
d.cast_mut().cast(),
)
})
})
})???;
Errno::result(res).map(drop)
}
/// Umount the file system mounted at `target`.
pub fn unmount<P>(target: &P, flags: MntFlags) -> Result<()>
where
P: ?Sized + NixPath,
{
let res = target.with_nix_path(|cstr| unsafe {
libc::unmount(cstr.as_ptr(), flags.bits())
})?;
Errno::result(res).map(drop)
}
@@ -17,36 +17,29 @@ libc_bitflags!(
pub struct MntFlags: c_int {
/// ACL support enabled.
#[cfg(any(target_os = "netbsd", target_os = "freebsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
MNT_ACLS;
/// All I/O to the file system should be done asynchronously.
MNT_ASYNC;
/// dir should instead be a file system ID encoded as “FSID:val0:val1”.
#[cfg(target_os = "freebsd")]
#[cfg_attr(docsrs, doc(cfg(all())))]
MNT_BYFSID;
/// Force a read-write mount even if the file system appears to be
/// unclean.
MNT_FORCE;
/// GEOM journal support enabled.
#[cfg(target_os = "freebsd")]
#[cfg_attr(docsrs, doc(cfg(all())))]
MNT_GJOURNAL;
/// MAC support for objects.
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(target_os = "freebsd")]
MNT_MULTILABEL;
/// Disable read clustering.
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(freebsdlike)]
MNT_NOCLUSTERR;
/// Disable write clustering.
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(freebsdlike)]
MNT_NOCLUSTERW;
/// Enable NFS version 4 ACLs.
#[cfg(target_os = "freebsd")]
#[cfg_attr(docsrs, doc(cfg(all())))]
MNT_NFS4ACLS;
/// Do not update access times.
MNT_NOATIME;
@@ -55,8 +48,7 @@ libc_bitflags!(
/// Do not honor setuid or setgid bits on files when executing them.
MNT_NOSUID;
/// Do not follow symlinks.
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(freebsdlike)]
MNT_NOSYMFOLLOW;
/// Mount read-only.
MNT_RDONLY;
@@ -66,39 +58,28 @@ libc_bitflags!(
/// Create a snapshot of the file system.
///
/// See [mksnap_ffs(8)](https://www.freebsd.org/cgi/man.cgi?query=mksnap_ffs)
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(target_os = "freebsd")]
MNT_SNAPSHOT;
/// Using soft updates.
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(any(freebsdlike, netbsdlike))]
MNT_SOFTDEP;
/// Directories with the SUID bit set chown new files to their own
/// owner.
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(freebsdlike)]
MNT_SUIDDIR;
/// All I/O to the file system should be done synchronously.
MNT_SYNCHRONOUS;
/// Union with underlying fs.
#[cfg(any(
target_os = "macos",
target_os = "freebsd",
target_os = "netbsd"
))]
#[cfg_attr(docsrs, doc(cfg(all())))]
MNT_UNION;
/// Indicates that the mount command is being applied to an already
/// mounted file system.
MNT_UPDATE;
/// Check vnode use counts.
#[cfg(target_os = "freebsd")]
#[cfg_attr(docsrs, doc(cfg(all())))]
MNT_NONBUSY;
}
);
@@ -198,7 +179,6 @@ pub type NmountResult = std::result::Result<(), NmountError>;
/// * [`nmount(2)`](https://www.freebsd.org/cgi/man.cgi?query=nmount)
/// * [`nullfs(5)`](https://www.freebsd.org/cgi/man.cgi?query=nullfs)
#[cfg(target_os = "freebsd")]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[derive(Debug, Default)]
pub struct Nmount<'a> {
// n.b. notgull: In reality, this is a list that contains
@@ -210,12 +190,11 @@ pub struct Nmount<'a> {
}
#[cfg(target_os = "freebsd")]
#[cfg_attr(docsrs, doc(cfg(all())))]
impl<'a> Nmount<'a> {
/// Helper function to push a slice onto the `iov` array.
fn push_slice(&mut self, val: &'a [u8], is_owned: bool) {
self.iov.push(libc::iovec {
iov_base: val.as_ptr() as *mut _,
iov_base: val.as_ptr().cast_mut().cast(),
iov_len: val.len(),
});
self.is_owned.push(is_owned);
@@ -386,23 +365,20 @@ impl<'a> Nmount<'a> {
// SAFETY: we are pushing a mutable iovec here, so we can't use
// the above method
self.iov.push(libc::iovec {
iov_base: errmsg.as_mut_ptr() as *mut c_void,
iov_base: errmsg.as_mut_ptr().cast(),
iov_len: errmsg.len(),
});
let niov = self.iov.len() as c_uint;
let iovp = self.iov.as_mut_ptr();
let res = unsafe { libc::nmount(iovp, niov, flags.bits) };
let res = unsafe { libc::nmount(iovp, niov, flags.bits()) };
match Errno::result(res) {
Ok(_) => Ok(()),
Err(error) => {
let errmsg = match errmsg.iter().position(|&x| x == 0) {
None => None,
Some(0) => None,
Some(n) => {
let sl = &errmsg[0..n + 1];
Some(CStr::from_bytes_with_nul(sl).unwrap())
}
let errmsg = if errmsg[0] == 0 {
None
} else {
CStr::from_bytes_until_nul(&errmsg[..]).ok()
};
Err(NmountError::new(error, errmsg))
}
@@ -411,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 {
@@ -446,7 +422,7 @@ where
P: ?Sized + NixPath,
{
let res = mountpoint.with_nix_path(|cstr| unsafe {
libc::unmount(cstr.as_ptr(), flags.bits)
libc::unmount(cstr.as_ptr(), flags.bits())
})?;
Errno::result(res).map(drop)
+54 -17
View File
@@ -1,9 +1,9 @@
#![allow(missing_docs)]
use crate::errno::Errno;
use crate::{NixPath, Result};
use libc::{self, c_int, c_ulong};
libc_bitflags!(
/// Used with [`mount`].
pub struct MsFlags: c_ulong {
/// Mount read-only
MS_RDONLY;
@@ -27,36 +27,80 @@ libc_bitflags!(
MS_NODIRATIME;
/// Linux 2.4.0 - Bind directory at different place
MS_BIND;
/// Move an existing mount to a new location
MS_MOVE;
/// Used to create a recursive bind mount.
MS_REC;
/// Suppress the display of certain kernel warning messages.
MS_SILENT;
/// VFS does not apply the umask
MS_POSIXACL;
/// The resulting mount cannot subsequently be bind mounted.
MS_UNBINDABLE;
/// Make this mount point private.
MS_PRIVATE;
/// If this is a shared mount point that is a member of a peer group
/// that contains other members, convert it to a slave mount.
MS_SLAVE;
/// Make this mount point shared.
MS_SHARED;
/// When a file on this filesystem is accessed, update the file's
/// last access time (atime) only if the current value of atime is
/// less than or equal to the file's last modification time (mtime) or
/// last status change time (ctime).
MS_RELATIME;
/// Mount request came from within the kernel
#[deprecated(since = "0.27.0", note = "Should only be used in-kernel")]
MS_KERNMOUNT;
/// Update inode I_version field
MS_I_VERSION;
/// Always update the last access time (atime) when files on this
/// filesystem are accessed.
MS_STRICTATIME;
/// Reduce on-disk updates of inode timestamps (atime, mtime, ctime) by
/// maintaining these changes only in memory.
MS_LAZYTIME;
#[deprecated(since = "0.27.0", note = "Should only be used in-kernel")]
#[allow(missing_docs)] // Not documented in Linux
MS_ACTIVE;
#[deprecated(since = "0.27.0", note = "Should only be used in-kernel")]
#[allow(missing_docs)] // Not documented in Linux
MS_NOUSER;
#[allow(missing_docs)] // Not documented in Linux; possibly kernel-only
MS_RMT_MASK;
#[allow(missing_docs)] // Not documented in Linux; possibly kernel-only
MS_MGC_VAL;
#[allow(missing_docs)] // Not documented in Linux; possibly kernel-only
MS_MGC_MSK;
}
);
libc_bitflags!(
/// Used with [`umount2].
pub struct MntFlags: c_int {
/// Attempt to unmount even if still in use, aborting pending requests.
MNT_FORCE;
/// Lazy unmount. Disconnect the file system immediately, but don't
/// actually unmount it until it ceases to be busy.
MNT_DETACH;
/// Mark the mount point as expired.
MNT_EXPIRE;
/// Don't dereference `target` if it is a symlink.
UMOUNT_NOFOLLOW;
}
);
/// Mount a file system.
///
/// # Arguments
/// - `source` - Specifies the file system. e.g. `/dev/sd0`.
/// - `target` - Specifies the destination. e.g. `/mnt`.
/// - `fstype` - The file system type, e.g. `ext4`.
/// - `flags` - Optional flags controlling the mount.
/// - `data` - Optional file system specific data.
///
/// # See Also
/// [`mount`](https://man7.org/linux/man-pages/man2/mount.2.html)
pub fn mount<
P1: ?Sized + NixPath,
P2: ?Sized + NixPath,
@@ -69,26 +113,15 @@ pub fn mount<
flags: MsFlags,
data: Option<&P4>,
) -> Result<()> {
fn with_opt_nix_path<P, T, F>(p: Option<&P>, f: F) -> Result<T>
where
P: ?Sized + NixPath,
F: FnOnce(*const libc::c_char) -> T,
{
match p {
Some(path) => path.with_nix_path(|p_str| f(p_str.as_ptr())),
None => Ok(f(std::ptr::null())),
}
}
let res = with_opt_nix_path(source, |s| {
let res = crate::with_opt_nix_path(source, |s| {
target.with_nix_path(|t| {
with_opt_nix_path(fstype, |ty| {
with_opt_nix_path(data, |d| unsafe {
crate::with_opt_nix_path(fstype, |ty| {
crate::with_opt_nix_path(data, |d| unsafe {
libc::mount(
s,
t.as_ptr(),
ty,
flags.bits,
flags.bits(),
d as *const libc::c_void,
)
})
@@ -99,6 +132,7 @@ pub fn mount<
Errno::result(res).map(drop)
}
/// Unmount the file system mounted at `target`.
pub fn umount<P: ?Sized + NixPath>(target: &P) -> Result<()> {
let res =
target.with_nix_path(|cstr| unsafe { libc::umount(cstr.as_ptr()) })?;
@@ -106,9 +140,12 @@ pub fn umount<P: ?Sized + NixPath>(target: &P) -> Result<()> {
Errno::result(res).map(drop)
}
/// Unmount the file system mounted at `target`.
///
/// See also [`umount`](https://man7.org/linux/man-pages/man2/umount.2.html)
pub fn umount2<P: ?Sized + NixPath>(target: &P, flags: MntFlags) -> Result<()> {
let res = target.with_nix_path(|cstr| unsafe {
libc::umount2(cstr.as_ptr(), flags.bits)
libc::umount2(cstr.as_ptr(), flags.bits())
})?;
Errno::result(res).map(drop)
+12 -20
View File
@@ -1,26 +1,18 @@
//! Mount file systems
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
mod linux;
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(linux_android)]
pub use self::linux::*;
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"
))]
#[cfg_attr(docsrs, doc(cfg(all())))]
mod bsd;
#[cfg(bsd_without_apple)]
mod bsd_without_apple;
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"
))]
pub use self::bsd::*;
#[cfg(bsd_without_apple)]
pub use self::bsd_without_apple::*;
#[cfg(apple_targets)]
mod apple;
#[cfg(apple_targets)]
pub use self::apple::*;
+104 -27
View File
@@ -9,16 +9,16 @@
//! use nix::sys::stat::Mode;
//!
//! const MSG_SIZE: mq_attr_member_t = 32;
//! let mq_name= CString::new("/a_nix_test_queue").unwrap();
//! let mq_name= "/a_nix_test_queue";
//!
//! let oflag0 = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
//! let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
//! let mqd0 = mq_open(&mq_name, oflag0, mode, None).unwrap();
//! let mqd0 = mq_open(mq_name, oflag0, mode, None).unwrap();
//! let msg_to_send = b"msg_1";
//! mq_send(&mqd0, msg_to_send, 1).unwrap();
//!
//! let oflag1 = MQ_OFlag::O_CREAT | MQ_OFlag::O_RDONLY;
//! let mqd1 = mq_open(&mq_name, oflag1, mode, None).unwrap();
//! let mqd1 = mq_open(mq_name, oflag1, mode, None).unwrap();
//! let mut buf = [0u8; 32];
//! let mut prio = 0u32;
//! let len = mq_receive(&mqd1, &mut buf, &mut prio).unwrap();
@@ -31,12 +31,20 @@
//! [Further reading and details on the C API](https://man7.org/linux/man-pages/man7/mq_overview.7.html)
use crate::errno::Errno;
use crate::NixPath;
use crate::Result;
use crate::sys::stat::Mode;
use libc::{self, c_char, mqd_t, size_t};
use std::ffi::CStr;
use libc::{self, mqd_t, size_t};
use std::mem;
#[cfg(any(
target_os = "linux",
target_os = "netbsd",
target_os = "dragonfly"
))]
use std::os::unix::io::{
AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd,
};
libc_bitflags! {
/// Used with [`mq_open`].
@@ -80,11 +88,9 @@ pub struct MqdT(mqd_t);
// See https://sourceware.org/bugzilla/show_bug.cgi?id=21279
/// Size of a message queue attribute member
#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub type mq_attr_member_t = i64;
/// Size of a message queue attribute member
#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub type mq_attr_member_t = libc::c_long;
impl MqAttr {
@@ -139,33 +145,41 @@ impl MqAttr {
/// Open a message queue
///
/// See also [`mq_open(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_open.html)
// The mode.bits cast is only lossless on some OSes
// The mode.bits() cast is only lossless on some OSes
#[allow(clippy::cast_lossless)]
pub fn mq_open(
name: &CStr,
pub fn mq_open<P>(
name: &P,
oflag: MQ_OFlag,
mode: Mode,
attr: Option<&MqAttr>,
) -> Result<MqdT> {
let res = match attr {
) -> Result<MqdT>
where
P: ?Sized + NixPath,
{
let res = name.with_nix_path(|cstr| match attr {
Some(mq_attr) => unsafe {
libc::mq_open(
name.as_ptr(),
cstr.as_ptr(),
oflag.bits(),
mode.bits() as libc::c_int,
&mq_attr.mq_attr as *const libc::mq_attr,
)
},
None => unsafe { libc::mq_open(name.as_ptr(), oflag.bits()) },
};
None => unsafe { libc::mq_open(cstr.as_ptr(), oflag.bits()) },
})?;
Errno::result(res).map(MqdT)
}
/// Remove a message queue
///
/// See also [`mq_unlink(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_unlink.html)
pub fn mq_unlink(name: &CStr) -> Result<()> {
let res = unsafe { libc::mq_unlink(name.as_ptr()) };
pub fn mq_unlink<P>(name: &P) -> Result<()>
where
P: ?Sized + NixPath,
{
let res =
name.with_nix_path(|cstr| unsafe { libc::mq_unlink(cstr.as_ptr()) })?;
Errno::result(res).map(drop)
}
@@ -189,7 +203,7 @@ pub fn mq_receive(
let res = unsafe {
libc::mq_receive(
mqdes.0,
message.as_mut_ptr() as *mut c_char,
message.as_mut_ptr().cast(),
len,
msg_prio as *mut u32,
)
@@ -197,17 +211,38 @@ pub fn mq_receive(
Errno::result(res).map(|r| r as usize)
}
feature! {
#![feature = "time"]
use crate::sys::time::TimeSpec;
/// Receive a message from a message queue with a timeout
///
/// See also ['mq_timedreceive(2)'](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_receive.html)
pub fn mq_timedreceive(
mqdes: &MqdT,
message: &mut [u8],
msg_prio: &mut u32,
abstime: &TimeSpec,
) -> Result<usize> {
let len = message.len() as size_t;
let res = unsafe {
libc::mq_timedreceive(
mqdes.0,
message.as_mut_ptr().cast(),
len,
msg_prio as *mut u32,
abstime.as_ref(),
)
};
Errno::result(res).map(|r| r as usize)
}
}
/// Send a message to a message queue
///
/// See also [`mq_send(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html)
pub fn mq_send(mqdes: &MqdT, message: &[u8], msq_prio: u32) -> Result<()> {
let res = unsafe {
libc::mq_send(
mqdes.0,
message.as_ptr() as *const c_char,
message.len(),
msq_prio,
)
libc::mq_send(mqdes.0, message.as_ptr().cast(), message.len(), msq_prio)
};
Errno::result(res).map(drop)
}
@@ -225,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> {
@@ -274,3 +311,43 @@ pub fn mq_remove_nonblock(mqd: &MqdT) -> Result<MqAttr> {
);
mq_setattr(mqd, &newattr)
}
#[cfg(any(target_os = "linux", target_os = "netbsd", target_os = "dragonfly"))]
impl AsFd for MqdT {
/// Borrow the underlying message queue descriptor.
fn as_fd(&self) -> BorrowedFd {
// SAFETY: [MqdT] will only contain a valid fd by construction.
unsafe { BorrowedFd::borrow_raw(self.0) }
}
}
#[cfg(any(target_os = "linux", target_os = "netbsd", target_os = "dragonfly"))]
impl AsRawFd for MqdT {
/// Return the underlying message queue descriptor.
///
/// Returned descriptor is a "shallow copy" of the descriptor, so it refers
/// to the same underlying kernel object as `self`.
fn as_raw_fd(&self) -> RawFd {
self.0
}
}
#[cfg(any(target_os = "linux", target_os = "netbsd", target_os = "dragonfly"))]
impl FromRawFd for MqdT {
/// Construct an [MqdT] from [RawFd].
///
/// # Safety
/// The `fd` given must be a valid and open file descriptor for a message
/// queue.
unsafe fn from_raw_fd(fd: RawFd) -> MqdT {
MqdT(fd)
}
}
#[cfg(any(target_os = "linux", target_os = "netbsd", target_os = "dragonfly"))]
impl IntoRawFd for MqdT {
/// Consume this [MqdT] and return a [RawFd].
fn into_raw_fd(self) -> RawFd {
self.0
}
}
+143 -208
View File
@@ -3,10 +3,18 @@
//! Uses Linux and/or POSIX functions to resolve interface names like "eth0"
//! or "socan1" into device numbers.
use crate::{Error, NixPath, Result};
use libc::c_uint;
use std::{ffi::{CStr, CString}, fmt};
use crate::{errno::Errno, Error, NixPath, Result};
use libc::{c_uint, IF_NAMESIZE};
/// Resolve an interface into a interface number.
#[cfg(not(solarish))]
/// type alias for InterfaceFlags
pub type IflagsType = libc::c_int;
#[cfg(solarish)]
/// type alias for InterfaceFlags
pub type IflagsType = libc::c_longlong;
/// Resolve an interface into an interface number.
pub fn if_nametoindex<P: ?Sized + NixPath>(name: &P) -> Result<c_uint> {
let if_index = name
.with_nix_path(|name| unsafe { libc::if_nametoindex(name.as_ptr()) })?;
@@ -18,324 +26,254 @@ pub fn if_nametoindex<P: ?Sized + NixPath>(name: &P) -> Result<c_uint> {
}
}
/// Resolve an interface number into an interface.
pub fn if_indextoname(index: c_uint) -> Result<CString> {
// We need to allocate this anyway, so doing it directly is faster.
let mut buf = vec![0u8; IF_NAMESIZE];
let return_buf = unsafe {
libc::if_indextoname(index, buf.as_mut_ptr().cast())
};
Errno::result(return_buf.cast())?;
Ok(CStr::from_bytes_until_nul(buf.as_slice()).unwrap().to_owned())
}
libc_bitflags!(
/// Standard interface flags, used by `getifaddrs`
pub struct InterfaceFlags: libc::c_int {
pub struct InterfaceFlags: IflagsType {
/// Interface is running. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
IFF_UP;
IFF_UP as IflagsType;
/// Valid broadcast address set. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
IFF_BROADCAST;
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"))]
IFF_DEBUG;
#[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))
IFF_LOOPBACK;
IFF_LOOPBACK as IflagsType;
/// Interface is a point-to-point link. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
IFF_POINTOPOINT;
IFF_POINTOPOINT as IflagsType;
/// Avoid use of trailers. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
#[cfg(any(target_os = "android",
#[cfg(any(
linux_android,
solarish,
apple_targets,
target_os = "fuchsia",
target_os = "ios",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "illumos",
target_os = "solaris"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_NOTRAILERS;
target_os = "cygwin"))]
IFF_NOTRAILERS as IflagsType;
/// Interface manages own routes.
#[cfg(any(target_os = "dragonfly"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_SMART;
IFF_SMART as IflagsType;
/// Resources allocated. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
#[cfg(any(target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
#[cfg(any(
linux_android,
bsd,
solarish,
target_os = "fuchsia",
target_os = "illumos",
target_os = "ios",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_RUNNING;
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))
IFF_NOARP;
IFF_NOARP as IflagsType;
/// Interface is in promiscuous mode. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
IFF_PROMISC;
IFF_PROMISC as IflagsType;
/// Receive all multicast packets. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
IFF_ALLMULTI;
#[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))
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(any(linux_android, target_os = "fuchsia"))]
IFF_MASTER;
/// transmission in progress, tx hardware queue is full
#[cfg(any(target_os = "freebsd",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "ios"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(any(target_os = "freebsd", apple_targets, netbsdlike))]
IFF_OACTIVE;
/// Protocol code on board.
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_INTELLIGENT;
#[cfg(solarish)]
IFF_INTELLIGENT as IflagsType;
/// Slave of a load balancing bundle. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(any(linux_android, target_os = "fuchsia"))]
IFF_SLAVE;
/// Can't hear own transmissions.
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(bsd)]
IFF_SIMPLEX;
/// Supports multicast. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
IFF_MULTICAST;
IFF_MULTICAST as IflagsType;
/// Per link layer defined bit.
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "ios"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(bsd)]
IFF_LINK0;
/// Multicast using broadcast.
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_MULTI_BCAST;
#[cfg(solarish)]
IFF_MULTI_BCAST as IflagsType;
/// Is able to select media type via ifmap. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(any(linux_android, target_os = "fuchsia"))]
IFF_PORTSEL;
/// Per link layer defined bit.
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "ios"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(bsd)]
IFF_LINK1;
/// Non-unique address.
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_UNNUMBERED;
#[cfg(solarish)]
IFF_UNNUMBERED as IflagsType;
/// Auto media selection active. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(any(linux_android, target_os = "fuchsia"))]
IFF_AUTOMEDIA;
/// Per link layer defined bit.
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "ios"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(bsd)]
IFF_LINK2;
/// Use alternate physical connection.
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "macos",
target_os = "ios"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(any(freebsdlike, apple_targets))]
IFF_ALTPHYS;
/// DHCP controls interface.
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_DHCPRUNNING;
#[cfg(solarish)]
IFF_DHCPRUNNING as IflagsType;
/// The addresses are lost when the interface goes down. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(any(linux_android, target_os = "fuchsia"))]
IFF_DYNAMIC;
/// Do not advertise.
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_PRIVATE;
#[cfg(solarish)]
IFF_PRIVATE as IflagsType;
/// Driver signals L1 up. Volatile.
#[cfg(any(target_os = "fuchsia", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(any(target_os = "fuchsia", target_os = "linux", target_os = "cygwin"))]
IFF_LOWER_UP;
/// Interface is in polling mode.
#[cfg(any(target_os = "dragonfly"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_POLLING_COMPAT;
/// Unconfigurable using ioctl(2).
#[cfg(any(target_os = "freebsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_CANTCONFIG;
/// Do not transmit packets.
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_NOXMIT;
#[cfg(solarish)]
IFF_NOXMIT as IflagsType;
/// Driver signals dormant. Volatile.
#[cfg(any(target_os = "fuchsia", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(any(target_os = "fuchsia", target_os = "linux", target_os = "cygwin"))]
IFF_DORMANT;
/// User-requested promisc mode.
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(freebsdlike)]
IFF_PPROMISC;
/// Just on-link subnet.
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_NOLOCAL;
#[cfg(solarish)]
IFF_NOLOCAL as IflagsType;
/// Echo sent packets. Volatile.
#[cfg(any(target_os = "fuchsia", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_ECHO;
/// User-requested monitor mode.
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(freebsdlike)]
IFF_MONITOR;
/// Address is deprecated.
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_DEPRECATED;
#[cfg(solarish)]
IFF_DEPRECATED as IflagsType;
/// Static ARP.
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(freebsdlike)]
IFF_STATICARP;
/// Address from stateless addrconf.
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_ADDRCONF;
#[cfg(solarish)]
IFF_ADDRCONF as IflagsType;
/// Interface is in polling mode.
#[cfg(any(target_os = "dragonfly"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_NPOLLING;
/// Router on interface.
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_ROUTER;
#[cfg(solarish)]
IFF_ROUTER as IflagsType;
/// Interface is in polling mode.
#[cfg(any(target_os = "dragonfly"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_IDIRECT;
/// Interface is winding down
#[cfg(any(target_os = "freebsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_DYING;
/// No NUD on interface.
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_NONUD;
#[cfg(solarish)]
IFF_NONUD as IflagsType;
/// Interface is being renamed
#[cfg(any(target_os = "freebsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_RENAMING;
/// Anycast address.
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_ANYCAST;
#[cfg(solarish)]
IFF_ANYCAST as IflagsType;
/// Don't exchange routing info.
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_NORTEXCH;
#[cfg(solarish)]
IFF_NORTEXCH as IflagsType;
/// Do not provide packet information
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_NO_PI as libc::c_int;
#[cfg(any(linux_android, target_os = "fuchsia"))]
IFF_NO_PI as IflagsType;
/// TUN device (no Ethernet headers)
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_TUN as libc::c_int;
#[cfg(any(linux_android, target_os = "fuchsia"))]
IFF_TUN as IflagsType;
/// TAP device
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_TAP as libc::c_int;
#[cfg(any(linux_android, target_os = "fuchsia"))]
IFF_TAP as IflagsType;
/// IPv4 interface.
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_IPV4;
#[cfg(solarish)]
IFF_IPV4 as IflagsType;
/// IPv6 interface.
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_IPV6;
#[cfg(solarish)]
IFF_IPV6 as IflagsType;
/// in.mpathd test address
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_NOFAILOVER;
#[cfg(solarish)]
IFF_NOFAILOVER as IflagsType;
/// Interface has failed
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_FAILED;
#[cfg(solarish)]
IFF_FAILED as IflagsType;
/// Interface is a hot-spare
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_STANDBY;
#[cfg(solarish)]
IFF_STANDBY as IflagsType;
/// Functioning but not used
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_INACTIVE;
#[cfg(solarish)]
IFF_INACTIVE as IflagsType;
/// Interface is offline
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_OFFLINE;
#[cfg(target_os = "solaris")]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_COS_ENABLED;
/// Prefer as source addr.
#[cfg(target_os = "solaris")]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_PREFERRED;
#[cfg(solarish)]
IFF_OFFLINE as IflagsType;
/// Has CoS marking supported
#[cfg(solarish)]
IFF_COS_ENABLED as IflagsType;
/// Prefer as source addr
#[cfg(solarish)]
IFF_PREFERRED as IflagsType;
/// RFC3041
#[cfg(target_os = "solaris")]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_TEMPORARY;
/// MTU set with SIOCSLIFMTU
#[cfg(target_os = "solaris")]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_FIXEDMTU;
/// Cannot send / receive packets
#[cfg(target_os = "solaris")]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_VIRTUAL;
#[cfg(solarish)]
IFF_TEMPORARY as IflagsType;
/// MTU set
#[cfg(solarish)]
IFF_FIXEDMTU as IflagsType;
/// Cannot send/receive packets
#[cfg(solarish)]
IFF_VIRTUAL as IflagsType;
/// Local address in use
#[cfg(target_os = "solaris")]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_DUPLICATE;
#[cfg(solarish)]
IFF_DUPLICATE as IflagsType;
/// IPMP IP interface
#[cfg(target_os = "solaris")]
#[cfg_attr(docsrs, doc(cfg(all())))]
IFF_IPMP;
#[cfg(solarish)]
IFF_IPMP as IflagsType;
}
);
impl fmt::Display for InterfaceFlags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
bitflags::parser::to_writer(self, f)
}
}
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
bsd,
target_os = "fuchsia",
target_os = "ios",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
solarish,
))]
#[cfg_attr(docsrs, doc(cfg(all())))]
mod if_nameindex {
use super::*;
@@ -372,6 +310,7 @@ mod if_nameindex {
}
/// A list of the network interfaces available on this system. Obtained from [`if_nameindex()`].
#[repr(transparent)]
pub struct Interfaces {
ptr: NonNull<libc::if_nameindex>,
}
@@ -387,7 +326,7 @@ mod if_nameindex {
/// null-terminated, so calling this calculates the length. If random access isn't needed,
/// [`Interfaces::iter()`] should be used instead.
pub fn to_slice(&self) -> &[Interface] {
let ifs = self.ptr.as_ptr() as *const Interface;
let ifs = self.ptr.as_ptr().cast();
let len = self.iter().count();
unsafe { std::slice::from_raw_parts(ifs, len) }
}
@@ -457,13 +396,9 @@ mod if_nameindex {
}
}
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
bsd,
target_os = "fuchsia",
target_os = "ios",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
solarish,
))]
pub use if_nameindex::*;
+101 -29
View File
@@ -1,7 +1,8 @@
//! Wait for events to trigger on specific file descriptors
use std::os::unix::io::{AsRawFd, RawFd};
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd};
use crate::errno::Errno;
pub use crate::poll_timeout::{PollTimeout, PollTimeoutTryFromError};
use crate::Result;
/// This is a wrapper around `libc::pollfd`.
@@ -10,30 +11,54 @@ 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)]
pub struct PollFd {
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct PollFd<'fd> {
pollfd: libc::pollfd,
_fd: std::marker::PhantomData<BorrowedFd<'fd>>,
}
impl PollFd {
impl<'fd> PollFd<'fd> {
/// Creates a new `PollFd` specifying the events of interest
/// for a given file descriptor.
pub const fn new(fd: RawFd, events: PollFlags) -> PollFd {
///
/// # Examples
/// ```no_run
/// # use std::os::unix::io::{AsFd, AsRawFd, FromRawFd};
/// # use nix::{
/// # poll::{PollTimeout, PollFd, PollFlags, poll},
/// # unistd::{pipe, read}
/// # };
/// let (r, w) = pipe().unwrap();
/// let pfd = PollFd::new(r.as_fd(), PollFlags::POLLIN);
/// ```
/// 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
// closed file descriptor. For example:
//
// ```rust
// let (r, _) = pipe().unwrap();
// let pollfd = PollFd::new(r, flag); // Drops the OwnedFd
// // Do something with `pollfd`, which uses the CLOSED fd.
// ```
pub fn new(fd: BorrowedFd<'fd>, events: PollFlags) -> PollFd<'fd> {
PollFd {
pollfd: libc::pollfd {
fd,
fd: fd.as_raw_fd(),
events: events.bits(),
revents: PollFlags::empty().bits(),
},
_fd: std::marker::PhantomData,
}
}
/// 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)
}
@@ -43,7 +68,7 @@ impl PollFd {
/// 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())
}
@@ -53,12 +78,12 @@ impl PollFd {
/// 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()
}
@@ -68,9 +93,29 @@ impl PollFd {
}
}
impl AsRawFd for PollFd {
fn as_raw_fd(&self) -> RawFd {
self.pollfd.fd
impl AsFd for PollFd<'_> {
fn as_fd(&self) -> BorrowedFd<'_> {
// Safety:
//
// BorrowedFd::borrow_raw(RawFd) requires that the raw fd being passed
// must remain open for the duration of the returned BorrowedFd, this is
// guaranteed as the returned BorrowedFd has the lifetime parameter same
// as `self`:
// "fn as_fd<'self>(&'self self) -> BorrowedFd<'self>"
// which means that `self` (PollFd) is guaranteed to outlive the returned
// BorrowedFd. (Lifetime: PollFd > BorrowedFd)
//
// And the lifetime parameter of PollFd::new(fd, ...) ensures that `fd`
// (an owned file descriptor) must outlive the returned PollFd:
// "pub fn new<Fd: AsFd>(fd: &'fd Fd, events: PollFlags) -> PollFd<'fd>"
// (Lifetime: Owned fd > PollFd)
//
// With two above relationships, we can conclude that the `Owned file
// descriptor` will outlive the returned BorrowedFd,
// (Lifetime: Owned fd > BorrowedFd)
// i.e., the raw fd being passed will remain valid for the lifetime of
// the returned BorrowedFd.
unsafe { BorrowedFd::borrow_raw(self.pollfd.fd) }
}
}
@@ -97,19 +142,15 @@ libc_bitflags! {
POLLOUT;
/// Equivalent to [`POLLIN`](constant.POLLIN.html)
#[cfg(not(target_os = "redox"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
POLLRDNORM;
#[cfg(not(target_os = "redox"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
/// Equivalent to [`POLLOUT`](constant.POLLOUT.html)
POLLWRNORM;
/// Priority band data can be read (generally unused on Linux).
#[cfg(not(target_os = "redox"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
POLLRDBAND;
/// Priority data may be written.
#[cfg(not(target_os = "redox"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
POLLWRBAND;
/// Error condition (only returned in
/// [`PollFd::revents`](struct.PollFd.html#method.revents);
@@ -148,16 +189,47 @@ libc_bitflags! {
///
/// Note that the timeout interval will be rounded up to the system clock
/// granularity, and kernel scheduling delays mean that the blocking
/// interval may overrun by a small amount. Specifying a negative value
/// in timeout means an infinite timeout. Specifying a timeout of zero
/// causes `poll()` to return immediately, even if no file descriptors are
/// ready.
pub fn poll(fds: &mut [PollFd], timeout: libc::c_int) -> Result<libc::c_int> {
/// interval may overrun by a small amount. Specifying a [`PollTimeout::NONE`]
/// 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,
) -> Result<libc::c_int> {
let res = unsafe {
libc::poll(
fds.as_mut_ptr() as *mut libc::pollfd,
fds.as_mut_ptr().cast(),
fds.len() as libc::nfds_t,
timeout,
i32::from(timeout.into()),
)
};
@@ -170,14 +242,14 @@ 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,
/// so in that case `ppoll` differs from `poll` only in the precision of the
/// timeout argument.
///
#[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "linux"))]
#[cfg(any(linux_android, freebsdlike))]
pub fn ppoll(
fds: &mut [PollFd],
timeout: Option<crate::sys::time::TimeSpec>,
@@ -187,7 +259,7 @@ pub fn ppoll(
let timeout = timeout.as_ref().map_or(core::ptr::null(), |r| r.as_ref());
let sigmask = sigmask.as_ref().map_or(core::ptr::null(), |r| r.as_ref());
let res = unsafe {
libc::ppoll(fds.as_mut_ptr() as *mut libc::pollfd,
libc::ppoll(fds.as_mut_ptr().cast(),
fds.len() as libc::nfds_t,
timeout,
sigmask)
+224
View File
@@ -0,0 +1,224 @@
use std::time::Duration;
/// PollTimeout argument for polling.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
pub struct PollTimeout(i32);
impl PollTimeout {
/// Blocks indefinitely.
///
/// > Specifying a negative value in timeout means an infinite timeout.
pub const NONE: Self = Self(-1);
/// Returns immediately.
///
/// > Specifying a timeout of zero causes poll() to return immediately, even if no file
/// > descriptors are ready.
pub const ZERO: Self = Self(0);
/// Blocks for at most [`i32::MAX`] milliseconds.
pub const MAX: Self = Self(i32::MAX);
/// Returns if `self` equals [`PollTimeout::NONE`].
pub fn is_none(&self) -> bool {
// > Specifying a negative value in timeout means an infinite timeout.
*self <= Self::NONE
}
/// Returns if `self` does not equal [`PollTimeout::NONE`].
pub fn is_some(&self) -> bool {
!self.is_none()
}
/// Returns the timeout in milliseconds if there is some, otherwise returns `None`.
pub fn as_millis(&self) -> Option<u32> {
self.is_some().then_some(u32::try_from(self.0).unwrap())
}
/// Returns the timeout as a `Duration` if there is some, otherwise returns `None`.
pub fn duration(&self) -> Option<Duration> {
self.as_millis()
.map(|x| Duration::from_millis(u64::from(x)))
}
}
/// Error type for integer conversions into `PollTimeout`.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PollTimeoutTryFromError {
/// Passing a value less than -1 is invalid on some systems, see
/// <https://man.freebsd.org/cgi/man.cgi?poll#end>.
TooNegative,
/// Passing a value greater than `i32::MAX` is invalid.
TooPositive,
}
impl std::fmt::Display for PollTimeoutTryFromError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::TooNegative => write!(f, "Passed a negative timeout less than -1."),
Self::TooPositive => write!(f, "Passed a positive timeout greater than `i32::MAX` milliseconds.")
}
}
}
impl std::error::Error for PollTimeoutTryFromError {}
impl<T: Into<PollTimeout>> From<Option<T>> for PollTimeout {
fn from(x: Option<T>) -> Self {
x.map_or(Self::NONE, |x| x.into())
}
}
impl TryFrom<Duration> for PollTimeout {
type Error = PollTimeoutTryFromError;
fn try_from(x: Duration) -> std::result::Result<Self, Self::Error> {
Ok(Self(
i32::try_from(x.as_millis())
.map_err(|_| PollTimeoutTryFromError::TooPositive)?,
))
}
}
impl TryFrom<u128> for PollTimeout {
type Error = PollTimeoutTryFromError;
fn try_from(x: u128) -> std::result::Result<Self, Self::Error> {
Ok(Self(
i32::try_from(x)
.map_err(|_| PollTimeoutTryFromError::TooPositive)?,
))
}
}
impl TryFrom<u64> for PollTimeout {
type Error = PollTimeoutTryFromError;
fn try_from(x: u64) -> std::result::Result<Self, Self::Error> {
Ok(Self(
i32::try_from(x)
.map_err(|_| PollTimeoutTryFromError::TooPositive)?,
))
}
}
impl TryFrom<u32> for PollTimeout {
type Error = PollTimeoutTryFromError;
fn try_from(x: u32) -> std::result::Result<Self, Self::Error> {
Ok(Self(
i32::try_from(x)
.map_err(|_| PollTimeoutTryFromError::TooPositive)?,
))
}
}
impl From<u16> for PollTimeout {
fn from(x: u16) -> Self {
Self(i32::from(x))
}
}
impl From<u8> for PollTimeout {
fn from(x: u8) -> Self {
Self(i32::from(x))
}
}
impl TryFrom<i128> for PollTimeout {
type Error = PollTimeoutTryFromError;
fn try_from(x: i128) -> std::result::Result<Self, Self::Error> {
match x {
..=-2 => Err(PollTimeoutTryFromError::TooNegative),
-1.. => Ok(Self(
i32::try_from(x)
.map_err(|_| PollTimeoutTryFromError::TooPositive)?,
)),
}
}
}
impl TryFrom<i64> for PollTimeout {
type Error = PollTimeoutTryFromError;
fn try_from(x: i64) -> std::result::Result<Self, Self::Error> {
match x {
..=-2 => Err(PollTimeoutTryFromError::TooNegative),
-1.. => Ok(Self(
i32::try_from(x)
.map_err(|_| PollTimeoutTryFromError::TooPositive)?,
)),
}
}
}
impl TryFrom<i32> for PollTimeout {
type Error = PollTimeoutTryFromError;
fn try_from(x: i32) -> std::result::Result<Self, Self::Error> {
match x {
..=-2 => Err(PollTimeoutTryFromError::TooNegative),
-1.. => Ok(Self(x)),
}
}
}
impl TryFrom<i16> for PollTimeout {
type Error = PollTimeoutTryFromError;
fn try_from(x: i16) -> std::result::Result<Self, Self::Error> {
match x {
..=-2 => Err(PollTimeoutTryFromError::TooNegative),
-1.. => Ok(Self(i32::from(x))),
}
}
}
impl TryFrom<i8> for PollTimeout {
type Error = PollTimeoutTryFromError;
fn try_from(x: i8) -> std::result::Result<Self, Self::Error> {
match x {
..=-2 => Err(PollTimeoutTryFromError::TooNegative),
-1.. => Ok(Self(i32::from(x))),
}
}
}
impl TryFrom<PollTimeout> for Duration {
type Error = ();
fn try_from(x: PollTimeout) -> std::result::Result<Self, ()> {
x.duration().ok_or(())
}
}
impl TryFrom<PollTimeout> for u128 {
type Error = <Self as TryFrom<i32>>::Error;
fn try_from(x: PollTimeout) -> std::result::Result<Self, Self::Error> {
Self::try_from(x.0)
}
}
impl TryFrom<PollTimeout> for u64 {
type Error = <Self as TryFrom<i32>>::Error;
fn try_from(x: PollTimeout) -> std::result::Result<Self, Self::Error> {
Self::try_from(x.0)
}
}
impl TryFrom<PollTimeout> for u32 {
type Error = <Self as TryFrom<i32>>::Error;
fn try_from(x: PollTimeout) -> std::result::Result<Self, Self::Error> {
Self::try_from(x.0)
}
}
impl TryFrom<PollTimeout> for u16 {
type Error = <Self as TryFrom<i32>>::Error;
fn try_from(x: PollTimeout) -> std::result::Result<Self, Self::Error> {
Self::try_from(x.0)
}
}
impl TryFrom<PollTimeout> for u8 {
type Error = <Self as TryFrom<i32>>::Error;
fn try_from(x: PollTimeout) -> std::result::Result<Self, Self::Error> {
Self::try_from(x.0)
}
}
impl From<PollTimeout> for i128 {
fn from(x: PollTimeout) -> Self {
Self::from(x.0)
}
}
impl From<PollTimeout> for i64 {
fn from(x: PollTimeout) -> Self {
Self::from(x.0)
}
}
impl From<PollTimeout> for i32 {
fn from(x: PollTimeout) -> Self {
x.0
}
}
impl TryFrom<PollTimeout> for i16 {
type Error = <Self as TryFrom<i32>>::Error;
fn try_from(x: PollTimeout) -> std::result::Result<Self, Self::Error> {
Self::try_from(x.0)
}
}
impl TryFrom<PollTimeout> for i8 {
type Error = <Self as TryFrom<i32>>::Error;
fn try_from(x: PollTimeout) -> std::result::Result<Self, Self::Error> {
Self::try_from(x.0)
}
}
+89 -67
View File
@@ -5,89 +5,98 @@ pub use libc::winsize as Winsize;
use std::ffi::CStr;
use std::io;
#[cfg(not(target_os = "aix"))]
use std::mem;
use std::os::unix::prelude::*;
use crate::errno::Errno;
#[cfg(not(target_os = "aix"))]
use crate::sys::termios::Termios;
#[cfg(feature = "process")]
use crate::unistd::{ForkResult, Pid};
#[cfg(all(feature = "process", not(target_os = "aix")))]
use crate::unistd::Pid;
use crate::{fcntl, unistd, Result};
/// Representation of a master/slave pty pair
///
/// This is returned by `openpty`. Note that this type does *not* implement `Drop`, so the user
/// must manually close the file descriptors.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
/// This is returned by [`openpty`].
#[derive(Debug)]
pub struct OpenptyResult {
/// The master port in a virtual pty pair
pub master: RawFd,
pub master: OwnedFd,
/// The slave port in a virtual pty pair
pub slave: RawFd,
pub slave: OwnedFd,
}
feature! {
#![feature = "process"]
/// Representation of a master with a forked pty
///
/// This is returned by `forkpty`. Note that this type does *not* implement `Drop`, so the user
/// must manually close the file descriptors.
#[derive(Clone, Copy, Debug)]
pub struct ForkptyResult {
/// The master port in a virtual pty pair
pub master: RawFd,
/// Metadata about forked process
pub fork_result: ForkResult,
/// A successful result of [`forkpty()`].
#[derive(Debug)]
pub enum ForkptyResult {
/// This is the parent process of the underlying fork.
Parent {
/// The PID of the fork's child process
child: Pid,
/// A file descriptor referring to master side of the pseudoterminal of
/// the child process.
master: OwnedFd,
},
/// This is the child process of the underlying fork.
Child,
}
}
/// Representation of the Master device in a master/slave pty pair
///
/// While this datatype is a thin wrapper around `RawFd`, it enforces that the available PTY
/// functions are given the correct file descriptor. Additionally this type implements `Drop`,
/// so that when it's consumed or goes out of scope, it's automatically cleaned-up.
#[derive(Debug, Eq, Hash, PartialEq)]
pub struct PtyMaster(RawFd);
/// While this datatype is a thin wrapper around `OwnedFd`, it enforces that the available PTY
/// functions are given the correct file descriptor.
#[derive(Debug)]
pub struct PtyMaster(OwnedFd);
impl 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
self.0.as_raw_fd()
}
}
impl AsFd for PtyMaster {
fn as_fd(&self) -> BorrowedFd<'_> {
self.0.as_fd()
}
}
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;
mem::forget(self);
fd
}
}
impl Drop for PtyMaster {
fn drop(&mut self) {
// On drop, we ignore errors like EINTR and EIO because there's no clear
// way to handle them, we can't return anything, and (on FreeBSD at
// least) the file descriptor is deallocated in these cases. However,
// we must panic on EBADF, because it is always an error to close an
// invalid file descriptor. That frequently indicates a double-close
// condition, which can cause confusing errors for future I/O
// operations.
let e = unistd::close(self.0);
if e == Err(Errno::EBADF) {
panic!("Closing an invalid file descriptor!");
};
fd.into_raw_fd()
}
}
impl io::Read for PtyMaster {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
unistd::read(self.0, buf).map_err(io::Error::from)
unistd::read(&self.0, buf).map_err(io::Error::from)
}
}
impl io::Write for PtyMaster {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
unistd::write(self.0, buf).map_err(io::Error::from)
unistd::write(&self.0, buf).map_err(io::Error::from)
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
@@ -96,13 +105,13 @@ impl io::Write for PtyMaster {
impl io::Read for &PtyMaster {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
unistd::read(self.0, buf).map_err(io::Error::from)
unistd::read(&self.0, buf).map_err(io::Error::from)
}
}
impl io::Write for &PtyMaster {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
unistd::write(self.0, buf).map_err(io::Error::from)
unistd::write(&self.0, buf).map_err(io::Error::from)
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
@@ -164,7 +173,7 @@ pub fn posix_openpt(flags: fcntl::OFlag) -> Result<PtyMaster> {
return Err(Errno::last());
}
Ok(PtyMaster(fd))
Ok(PtyMaster(unsafe { OwnedFd::from_raw_fd(fd) }))
}
/// Get the name of the slave pseudoterminal (see
@@ -185,12 +194,12 @@ pub fn posix_openpt(flags: fcntl::OFlag) -> Result<PtyMaster> {
/// For a threadsafe and non-`unsafe` alternative on Linux, see `ptsname_r()`.
#[inline]
pub unsafe fn ptsname(fd: &PtyMaster) -> Result<String> {
let name_ptr = libc::ptsname(fd.as_raw_fd());
let name_ptr = unsafe { libc::ptsname(fd.as_raw_fd()) };
if name_ptr.is_null() {
return Err(Errno::last());
}
let name = CStr::from_ptr(name_ptr);
let name = unsafe { CStr::from_ptr(name_ptr) };
Ok(name.to_string_lossy().into_owned())
}
@@ -203,8 +212,7 @@ pub unsafe fn ptsname(fd: &PtyMaster) -> Result<String> {
///
/// This value is useful for opening the slave ptty once the master has already been opened with
/// `posix_openpt()`.
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
#[inline]
pub fn ptsname_r(fd: &PtyMaster) -> Result<String> {
let mut name_buf = Vec::<libc::c_char>::with_capacity(64);
@@ -244,6 +252,7 @@ pub fn unlockpt(fd: &PtyMaster) -> Result<()> {
/// the values in `winsize`. If `termios` is not `None`, the pseudoterminal's
/// terminal settings of the slave will be set to the values in `termios`.
#[inline]
#[cfg(not(target_os = "aix"))]
pub fn openpty<
'a,
'b,
@@ -308,17 +317,15 @@ pub fn openpty<
unsafe {
Ok(OpenptyResult {
master: master.assume_init(),
slave: slave.assume_init(),
master: OwnedFd::from_raw_fd(master.assume_init()),
slave: OwnedFd::from_raw_fd(slave.assume_init()),
})
}
}
feature! {
#![feature = "process"]
/// Create a new pseudoterminal, returning the master file descriptor and forked pid.
/// in `ForkptyResult`
/// (see [`forkpty`](https://man7.org/linux/man-pages/man3/forkpty.3.html)).
/// Create a new process operating in a pseudoterminal.
///
/// If `winsize` is not `None`, the window size of the slave will be set to
/// the values in `winsize`. If `termios` is not `None`, the pseudoterminal's
@@ -327,14 +334,20 @@ 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.
///
/// [async-signal-safe]: https://man7.org/linux/man-pages/man7/signal-safety.7.html
///
/// # Reference
///
/// * [FreeBSD](https://man.freebsd.org/cgi/man.cgi?query=forkpty)
/// * [Linux](https://man7.org/linux/man-pages/man3/forkpty.3.html)
#[cfg(not(target_os = "aix"))]
pub unsafe fn forkpty<'a, 'b, T: Into<Option<&'a Winsize>>, U: Into<Option<&'b Termios>>>(
winsize: T,
termios: U,
@@ -356,16 +369,25 @@ pub unsafe fn forkpty<'a, 'b, T: Into<Option<&'a Winsize>>, U: Into<Option<&'b T
.map(|ws| ws as *const Winsize as *mut _)
.unwrap_or(ptr::null_mut());
let res = libc::forkpty(master.as_mut_ptr(), ptr::null_mut(), term, win);
let res = unsafe { libc::forkpty(master.as_mut_ptr(), ptr::null_mut(), term, win) };
let fork_result = Errno::result(res).map(|res| match res {
0 => ForkResult::Child,
res => ForkResult::Parent { child: Pid::from_raw(res) },
})?;
let success_ret = Errno::result(res)?;
let forkpty_result = match success_ret {
// In the child process
0 => ForkptyResult::Child,
// In the parent process
child_pid => {
// SAFETY:
// 1. The master buffer is guaranteed to be initialized in the parent process
// 2. OwnedFd::from_raw_fd won't panic as the fd is a valid file descriptor
let master = unsafe { OwnedFd::from_raw_fd( master.assume_init() ) };
ForkptyResult::Parent {
master,
child: Pid::from_raw(child_pid),
}
}
};
Ok(ForkptyResult {
master: master.assume_init(),
fork_result,
})
Ok(forkpty_result)
}
}
+23 -21
View File
@@ -4,11 +4,10 @@
//! [sched.h](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sched.h.html)
use crate::{Errno, Result};
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(linux_android)]
pub use self::sched_linux_like::*;
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
mod sched_linux_like {
use crate::errno::Errno;
use crate::unistd::Pid;
@@ -16,7 +15,7 @@ mod sched_linux_like {
use libc::{self, c_int, c_void};
use std::mem;
use std::option::Option;
use std::os::unix::io::RawFd;
use std::os::unix::io::{AsFd, AsRawFd};
// For some functions taking with a parameter of type CloneFlags,
// only a subset of these flags have an effect.
@@ -95,7 +94,17 @@ mod sched_linux_like {
/// address need not be the highest address of the region. Nix will take
/// care of that requirement. The user only needs to provide a reference to
/// a normally allocated buffer.
pub fn clone(
///
/// # Safety
///
/// Because `clone` creates a child process with its stack located in
/// `stack` without specifying the size of the stack, special care must be
/// taken to ensure that the child process does not overflow the provided
/// stack space.
///
/// See [`fork`](crate::unistd::fork) for additional safety concerns related
/// to executing child processes.
pub unsafe fn clone(
mut cb: CloneCb,
stack: &mut [u8],
flags: CloneFlags,
@@ -106,12 +115,15 @@ mod sched_linux_like {
(*cb)() as c_int
}
let combined = flags.bits() | signal.unwrap_or(0);
let res = unsafe {
let combined = flags.bits() | signal.unwrap_or(0);
let ptr = stack.as_mut_ptr().add(stack.len());
let ptr_aligned = ptr.sub(ptr as usize % 16);
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,
),
@@ -136,27 +148,17 @@ mod sched_linux_like {
/// reassociate thread with a namespace
///
/// See also [setns(2)](https://man7.org/linux/man-pages/man2/setns.2.html)
pub fn setns(fd: RawFd, nstype: CloneFlags) -> Result<()> {
let res = unsafe { libc::setns(fd, nstype.bits()) };
pub fn setns<Fd: AsFd>(fd: Fd, nstype: CloneFlags) -> Result<()> {
let res = unsafe { libc::setns(fd.as_fd().as_raw_fd(), nstype.bits()) };
Errno::result(res).map(drop)
}
}
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "linux"
))]
#[cfg(any(linux_android, freebsdlike))]
pub use self::sched_affinity::*;
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "linux"
))]
#[cfg(any(linux_android, freebsdlike))]
mod sched_affinity {
use crate::errno::Errno;
use crate::unistd::Pid;
+431
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))
}
+108 -152
View File
@@ -30,12 +30,12 @@ use std::{
fmt::{self, Debug},
marker::{PhantomData, PhantomPinned},
mem,
os::unix::io::RawFd,
os::unix::io::{AsFd, AsRawFd, BorrowedFd},
pin::Pin,
ptr, thread,
};
use libc::{c_void, off_t};
use libc::off_t;
use pin_utils::unsafe_pinned;
use crate::{
@@ -53,12 +53,10 @@ libc_enum! {
/// do it like `fsync`
O_SYNC,
/// on supported operating systems only, do it like `fdatasync`
#[cfg(any(target_os = "ios",
#[cfg(any(apple_targets,
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
target_os = "freebsd",
netbsdlike))]
O_DSYNC
}
impl TryFrom<i32>
@@ -105,7 +103,7 @@ unsafe impl Sync for LibcAiocb {}
// provide polymorphism at the wrong level. Instead, the best place for
// polymorphism is at the level of `Futures`.
#[repr(C)]
struct AioCb {
struct AioCb<'a> {
aiocb: LibcAiocb,
/// Could this `AioCb` potentially have any in-kernel state?
// It would be really nice to perform the in-progress check entirely at
@@ -115,9 +113,10 @@ struct AioCb {
// that there's no way to write an AioCb constructor that neither boxes
// the object itself, nor moves it during return.
in_progress: bool,
_fd: PhantomData<BorrowedFd<'a>>,
}
impl AioCb {
impl<'a> AioCb<'a> {
pin_utils::unsafe_unpinned!(aiocb: LibcAiocb);
fn aio_return(mut self: Pin<&mut Self>) -> Result<usize> {
@@ -142,18 +141,23 @@ impl AioCb {
}
}
fn common_init(fd: RawFd, prio: i32, sigev_notify: SigevNotify) -> Self {
fn common_init(
fd: BorrowedFd<'a>,
prio: i32,
sigev_notify: SigevNotify,
) -> Self {
// Use mem::zeroed instead of explicitly zeroing each field, because the
// number and name of reserved fields is OS-dependent. On some OSes,
// some reserved fields are used the kernel for state, and must be
// explicitly zeroed when allocated.
let mut a = unsafe { mem::zeroed::<libc::aiocb>() };
a.aio_fildes = fd;
a.aio_fildes = fd.as_raw_fd();
a.aio_reqprio = prio;
a.aio_sigevent = SigEvent::new(sigev_notify).sigevent();
AioCb {
aiocb: LibcAiocb(a),
in_progress: false,
_fd: PhantomData,
}
}
@@ -161,9 +165,9 @@ impl AioCb {
let r = unsafe { libc::aio_error(&self.aiocb().0) };
match r {
0 => Ok(()),
num if num > 0 => Err(Errno::from_i32(num)),
num if num > 0 => Err(Errno::from_raw(num)),
-1 => Err(Errno::last()),
num => panic!("unknown aio_error return value {:?}", num),
num => panic!("unknown aio_error return value {num:?}"),
}
}
@@ -189,7 +193,7 @@ impl AioCb {
}
}
impl Debug for AioCb {
impl Debug for AioCb<'_> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("AioCb")
.field("aiocb", &self.aiocb.0)
@@ -198,7 +202,7 @@ impl Debug for AioCb {
}
}
impl Drop for AioCb {
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) {
@@ -246,11 +250,11 @@ pub trait Aio {
/// # use nix::sys::signal::SigevNotify;
/// # use std::{thread, time};
/// # use std::io::Write;
/// # use std::os::unix::io::AsRawFd;
/// # use std::os::unix::io::AsFd;
/// # use tempfile::tempfile;
/// let wbuf = b"CDEF";
/// let mut f = tempfile().unwrap();
/// let mut aiocb = Box::pin(AioWrite::new(f.as_raw_fd(),
/// let mut aiocb = Box::pin(AioWrite::new(f.as_fd(),
/// 2, //offset
/// &wbuf[..],
/// 0, //priority
@@ -287,11 +291,11 @@ pub trait Aio {
/// # use nix::sys::aio::*;
/// # use nix::sys::signal::SigevNotify;
/// # use std::{thread, time};
/// # use std::os::unix::io::AsRawFd;
/// # use std::os::unix::io::AsFd;
/// # use tempfile::tempfile;
/// const WBUF: &[u8] = b"abcdef123456";
/// let mut f = tempfile().unwrap();
/// let mut aiocb = Box::pin(AioWrite::new(f.as_raw_fd(),
/// let mut aiocb = Box::pin(AioWrite::new(f.as_fd(),
/// 2, //offset
/// WBUF,
/// 0, //priority
@@ -309,7 +313,7 @@ pub trait Aio {
fn error(self: Pin<&mut Self>) -> Result<()>;
/// Returns the underlying file descriptor associated with the operation.
fn fd(&self) -> RawFd;
fn fd(&self) -> BorrowedFd;
/// Does this operation currently have any in-kernel state?
///
@@ -324,10 +328,10 @@ pub trait Aio {
/// # use nix::sys::aio::*;
/// # use nix::sys::signal::SigevNotify::SigevNone;
/// # use std::{thread, time};
/// # use std::os::unix::io::AsRawFd;
/// # use std::os::unix::io::AsFd;
/// # use tempfile::tempfile;
/// let f = tempfile().unwrap();
/// let mut aiof = Box::pin(AioFsync::new(f.as_raw_fd(), AioFsyncMode::O_SYNC,
/// let mut aiof = Box::pin(AioFsync::new(f.as_fd(), AioFsyncMode::O_SYNC,
/// 0, SigevNone));
/// assert!(!aiof.as_mut().in_progress());
/// aiof.as_mut().submit().expect("aio_fsync failed early");
@@ -367,8 +371,10 @@ macro_rules! aio_methods {
self.aiocb().error()
}
fn fd(&self) -> RawFd {
self.aiocb.aiocb.0.aio_fildes
fn fd(&self) -> BorrowedFd<'a> {
// safe because self's lifetime is the same as the original file
// descriptor.
unsafe { BorrowedFd::borrow_raw(self.aiocb.aiocb.0.aio_fildes) }
}
fn in_progress(&self) -> bool {
@@ -416,10 +422,10 @@ macro_rules! aio_methods {
/// # use nix::sys::aio::*;
/// # use nix::sys::signal::SigevNotify::SigevNone;
/// # use std::{thread, time};
/// # use std::os::unix::io::AsRawFd;
/// # use std::os::unix::io::AsFd;
/// # use tempfile::tempfile;
/// let f = tempfile().unwrap();
/// let mut aiof = Box::pin(AioFsync::new(f.as_raw_fd(), AioFsyncMode::O_SYNC,
/// let mut aiof = Box::pin(AioFsync::new(f.as_fd(), AioFsyncMode::O_SYNC,
/// 0, SigevNone));
/// aiof.as_mut().submit().expect("aio_fsync failed early");
/// while (aiof.as_mut().error() == Err(Errno::EINPROGRESS)) {
@@ -429,13 +435,13 @@ macro_rules! aio_methods {
/// ```
#[derive(Debug)]
#[repr(transparent)]
pub struct AioFsync {
aiocb: AioCb,
pub struct AioFsync<'a> {
aiocb: AioCb<'a>,
_pin: PhantomPinned,
}
impl AioFsync {
unsafe_pinned!(aiocb: AioCb);
impl<'a> AioFsync<'a> {
unsafe_pinned!(aiocb: AioCb<'a>);
/// Returns the operation's fsync mode: data and metadata or data only?
pub fn mode(&self) -> AioFsyncMode {
@@ -449,12 +455,11 @@ impl AioFsync {
/// * `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: RawFd,
fd: BorrowedFd<'a>,
mode: AioFsyncMode,
prio: i32,
sigev_notify: SigevNotify,
@@ -472,7 +477,7 @@ impl AioFsync {
}
}
impl Aio for AioFsync {
impl<'a> Aio for AioFsync<'a> {
type Output = ();
aio_methods!();
@@ -493,7 +498,7 @@ impl Aio for AioFsync {
// AioFsync does not need AsMut, since it can't be used with lio_listio
impl AsRef<libc::aiocb> for AioFsync {
impl AsRef<libc::aiocb> for AioFsync<'_> {
fn as_ref(&self) -> &libc::aiocb {
&self.aiocb.aiocb.0
}
@@ -515,7 +520,7 @@ impl AsRef<libc::aiocb> for AioFsync {
/// # use nix::sys::signal::SigevNotify;
/// # use std::{thread, time};
/// # use std::io::Write;
/// # use std::os::unix::io::AsRawFd;
/// # use std::os::unix::io::AsFd;
/// # use tempfile::tempfile;
/// const INITIAL: &[u8] = b"abcdef123456";
/// const LEN: usize = 4;
@@ -525,7 +530,7 @@ impl AsRef<libc::aiocb> for AioFsync {
/// {
/// let mut aior = Box::pin(
/// AioRead::new(
/// f.as_raw_fd(),
/// f.as_fd(),
/// 2, //offset
/// &mut rbuf,
/// 0, //priority
@@ -543,13 +548,13 @@ impl AsRef<libc::aiocb> for AioFsync {
#[derive(Debug)]
#[repr(transparent)]
pub struct AioRead<'a> {
aiocb: AioCb,
aiocb: AioCb<'a>,
_data: PhantomData<&'a [u8]>,
_pin: PhantomPinned,
}
impl<'a> AioRead<'a> {
unsafe_pinned!(aiocb: AioCb);
unsafe_pinned!(aiocb: AioCb<'a>);
/// Returns the requested length of the aio operation in bytes
///
@@ -567,13 +572,11 @@ 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: RawFd,
fd: BorrowedFd<'a>,
offs: off_t,
buf: &'a mut [u8],
prio: i32,
@@ -581,7 +584,7 @@ impl<'a> AioRead<'a> {
) -> Self {
let mut aiocb = AioCb::common_init(fd, prio, sigev_notify);
aiocb.aiocb.0.aio_nbytes = buf.len();
aiocb.aiocb.0.aio_buf = buf.as_mut_ptr() as *mut c_void;
aiocb.aiocb.0.aio_buf = buf.as_mut_ptr().cast();
aiocb.aiocb.0.aio_lio_opcode = libc::LIO_READ;
aiocb.aiocb.0.aio_offset = offs;
AioRead {
@@ -603,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
}
@@ -632,7 +635,7 @@ impl<'a> AsRef<libc::aiocb> for AioRead<'a> {
/// # use nix::sys::signal::SigevNotify;
/// # use std::{thread, time};
/// # use std::io::{IoSliceMut, Write};
/// # use std::os::unix::io::AsRawFd;
/// # use std::os::unix::io::AsFd;
/// # use tempfile::tempfile;
/// const INITIAL: &[u8] = b"abcdef123456";
/// let mut rbuf0 = vec![0; 4];
@@ -644,7 +647,7 @@ impl<'a> AsRef<libc::aiocb> for AioRead<'a> {
/// {
/// let mut aior = Box::pin(
/// AioReadv::new(
/// f.as_raw_fd(),
/// f.as_fd(),
/// 2, //offset
/// &mut rbufs,
/// 0, //priority
@@ -664,14 +667,14 @@ impl<'a> AsRef<libc::aiocb> for AioRead<'a> {
#[derive(Debug)]
#[repr(transparent)]
pub struct AioReadv<'a> {
aiocb: AioCb,
aiocb: AioCb<'a>,
_data: PhantomData<&'a [&'a [u8]]>,
_pin: PhantomPinned,
}
#[cfg(target_os = "freebsd")]
impl<'a> AioReadv<'a> {
unsafe_pinned!(aiocb: AioCb);
unsafe_pinned!(aiocb: AioCb<'a>);
/// Returns the number of buffers the operation will read into.
pub fn iovlen(&self) -> usize {
@@ -692,7 +695,7 @@ impl<'a> AioReadv<'a> {
/// * `sigev_notify`: Determines how you will be notified of event
/// completion.
pub fn new(
fd: RawFd,
fd: BorrowedFd<'a>,
offs: off_t,
bufs: &mut [IoSliceMut<'a>],
prio: i32,
@@ -702,7 +705,7 @@ impl<'a> AioReadv<'a> {
// In vectored mode, aio_nbytes stores the length of the iovec array,
// not the byte count.
aiocb.aiocb.0.aio_nbytes = bufs.len();
aiocb.aiocb.0.aio_buf = bufs.as_mut_ptr() as *mut c_void;
aiocb.aiocb.0.aio_buf = bufs.as_mut_ptr().cast();
aiocb.aiocb.0.aio_lio_opcode = libc::LIO_READV;
aiocb.aiocb.0.aio_offset = offs;
AioReadv {
@@ -726,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
}
@@ -753,13 +756,13 @@ impl<'a> AsRef<libc::aiocb> for AioReadv<'a> {
/// # use nix::sys::aio::*;
/// # use nix::sys::signal::SigevNotify;
/// # use std::{thread, time};
/// # use std::os::unix::io::AsRawFd;
/// # use std::os::unix::io::AsFd;
/// # use tempfile::tempfile;
/// const WBUF: &[u8] = b"abcdef123456";
/// let mut f = tempfile().unwrap();
/// let mut aiow = Box::pin(
/// AioWrite::new(
/// f.as_raw_fd(),
/// f.as_fd(),
/// 2, //offset
/// WBUF,
/// 0, //priority
@@ -775,13 +778,13 @@ impl<'a> AsRef<libc::aiocb> for AioReadv<'a> {
#[derive(Debug)]
#[repr(transparent)]
pub struct AioWrite<'a> {
aiocb: AioCb,
aiocb: AioCb<'a>,
_data: PhantomData<&'a [u8]>,
_pin: PhantomPinned,
}
impl<'a> AioWrite<'a> {
unsafe_pinned!(aiocb: AioCb);
unsafe_pinned!(aiocb: AioCb<'a>);
/// Returns the requested length of the aio operation in bytes
///
@@ -799,13 +802,11 @@ 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: RawFd,
fd: BorrowedFd<'a>,
offs: off_t,
buf: &'a [u8],
prio: i32,
@@ -817,7 +818,7 @@ impl<'a> AioWrite<'a> {
// but technically its only unsafe to dereference it, not to create
// it. Type Safety guarantees that we'll never pass aiocb to
// aio_read or aio_readv.
aiocb.aiocb.0.aio_buf = buf.as_ptr() as *mut c_void;
aiocb.aiocb.0.aio_buf = buf.as_ptr().cast_mut().cast();
aiocb.aiocb.0.aio_lio_opcode = libc::LIO_WRITE;
aiocb.aiocb.0.aio_offset = offs;
AioWrite {
@@ -839,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
}
@@ -867,7 +868,7 @@ impl<'a> AsRef<libc::aiocb> for AioWrite<'a> {
/// # use nix::sys::signal::SigevNotify;
/// # use std::{thread, time};
/// # use std::io::IoSlice;
/// # use std::os::unix::io::AsRawFd;
/// # use std::os::unix::io::AsFd;
/// # use tempfile::tempfile;
/// const wbuf0: &[u8] = b"abcdef";
/// const wbuf1: &[u8] = b"123456";
@@ -876,7 +877,7 @@ impl<'a> AsRef<libc::aiocb> for AioWrite<'a> {
/// let mut f = tempfile().unwrap();
/// let mut aiow = Box::pin(
/// AioWritev::new(
/// f.as_raw_fd(),
/// f.as_fd(),
/// 2, //offset
/// &wbufs,
/// 0, //priority
@@ -893,14 +894,14 @@ impl<'a> AsRef<libc::aiocb> for AioWrite<'a> {
#[derive(Debug)]
#[repr(transparent)]
pub struct AioWritev<'a> {
aiocb: AioCb,
aiocb: AioCb<'a>,
_data: PhantomData<&'a [&'a [u8]]>,
_pin: PhantomPinned,
}
#[cfg(target_os = "freebsd")]
impl<'a> AioWritev<'a> {
unsafe_pinned!(aiocb: AioCb);
unsafe_pinned!(aiocb: AioCb<'a>);
/// Returns the number of buffers the operation will read into.
pub fn iovlen(&self) -> usize {
@@ -921,7 +922,7 @@ impl<'a> AioWritev<'a> {
/// * `sigev_notify`: Determines how you will be notified of event
/// completion.
pub fn new(
fd: RawFd,
fd: BorrowedFd<'a>,
offs: off_t,
bufs: &[IoSlice<'a>],
prio: i32,
@@ -935,7 +936,7 @@ impl<'a> AioWritev<'a> {
// but technically its only unsafe to dereference it, not to create
// it. Type Safety guarantees that we'll never pass aiocb to
// aio_read or aio_readv.
aiocb.aiocb.0.aio_buf = bufs.as_ptr() as *mut c_void;
aiocb.aiocb.0.aio_buf = bufs.as_ptr().cast_mut().cast();
aiocb.aiocb.0.aio_lio_opcode = libc::LIO_WRITEV;
aiocb.aiocb.0.aio_offset = offs;
AioWritev {
@@ -959,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
}
@@ -986,17 +987,17 @@ impl<'a> AsRef<libc::aiocb> for AioWritev<'a> {
/// # use nix::sys::signal::SigevNotify;
/// # use std::{thread, time};
/// # use std::io::Write;
/// # use std::os::unix::io::AsRawFd;
/// # use std::os::unix::io::AsFd;
/// # use tempfile::tempfile;
/// let wbuf = b"CDEF";
/// let mut f = tempfile().unwrap();
/// let mut aiocb = Box::pin(AioWrite::new(f.as_raw_fd(),
/// let mut aiocb = Box::pin(AioWrite::new(f.as_fd(),
/// 2, //offset
/// &wbuf[..],
/// 0, //priority
/// SigevNotify::SigevNone));
/// aiocb.as_mut().submit().unwrap();
/// let cs = aio_cancel_all(f.as_raw_fd()).unwrap();
/// let cs = aio_cancel_all(f.as_fd()).unwrap();
/// if cs == AioCancelStat::AioNotCanceled {
/// while (aiocb.as_mut().error() == Err(Errno::EINPROGRESS)) {
/// thread::sleep(time::Duration::from_millis(10));
@@ -1009,8 +1010,8 @@ impl<'a> AsRef<libc::aiocb> for AioWritev<'a> {
/// # References
///
/// [`aio_cancel`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_cancel.html)
pub fn aio_cancel_all(fd: RawFd) -> Result<AioCancelStat> {
match unsafe { libc::aio_cancel(fd, ptr::null_mut()) } {
pub fn aio_cancel_all<F: AsFd>(fd: F) -> Result<AioCancelStat> {
match unsafe { libc::aio_cancel(fd.as_fd().as_raw_fd(), ptr::null_mut()) } {
libc::AIO_CANCELED => Ok(AioCancelStat::AioCanceled),
libc::AIO_NOTCANCELED => Ok(AioCancelStat::AioNotCanceled),
libc::AIO_ALLDONE => Ok(AioCancelStat::AioAllDone),
@@ -1031,18 +1032,18 @@ pub fn aio_cancel_all(fd: RawFd) -> Result<AioCancelStat> {
/// ```
/// # use nix::sys::aio::*;
/// # use nix::sys::signal::SigevNotify;
/// # use std::os::unix::io::AsRawFd;
/// # use std::os::unix::io::AsFd;
/// # use tempfile::tempfile;
/// const WBUF: &[u8] = b"abcdef123456";
/// let mut f = tempfile().unwrap();
/// let mut aiocb = Box::pin(AioWrite::new(f.as_raw_fd(),
/// let mut aiocb = Box::pin(AioWrite::new(f.as_fd(),
/// 2, //offset
/// WBUF,
/// 0, //priority
/// SigevNotify::SigevNone));
/// aiocb.as_mut().submit().unwrap();
/// aio_suspend(&[&*aiocb], None).expect("aio_suspend failed");
/// assert_eq!(aiocb.as_mut().aio_return().unwrap() as usize, WBUF.len());
/// assert_eq!(aiocb.as_mut().aio_return().unwrap(), WBUF.len());
/// ```
/// # References
///
@@ -1051,8 +1052,15 @@ pub fn aio_suspend(
list: &[&dyn AsRef<libc::aiocb>],
timeout: Option<TimeSpec>,
) -> Result<()> {
let p = list as *const [&dyn AsRef<libc::aiocb>]
as *const [*const libc::aiocb] as *const *const libc::aiocb;
// Note that this allocation could be eliminated by making the argument
// generic, and accepting arguments like &[AioWrite]. But that would
// prevent using aio_suspend to wait on a heterogeneous list of mixed
// operations.
let v = list
.iter()
.map(|x| x.as_ref() as *const libc::aiocb)
.collect::<Vec<*const libc::aiocb>>();
let p = v.as_ptr();
let timep = match timeout {
None => ptr::null::<libc::timespec>(),
Some(x) => x.as_ref() as *const libc::timespec,
@@ -1074,14 +1082,14 @@ pub fn aio_suspend(
/// This mode is useful for otherwise-synchronous programs that want to execute
/// a handful of I/O operations in parallel.
/// ```
/// # use std::os::unix::io::AsRawFd;
/// # use std::os::unix::io::AsFd;
/// # use nix::sys::aio::*;
/// # use nix::sys::signal::SigevNotify;
/// # use tempfile::tempfile;
/// const WBUF: &[u8] = b"abcdef123456";
/// let mut f = tempfile().unwrap();
/// let mut aiow = Box::pin(AioWrite::new(
/// f.as_raw_fd(),
/// f.as_fd(),
/// 2, // offset
/// WBUF,
/// 0, // priority
@@ -1098,7 +1106,7 @@ pub fn aio_suspend(
/// technique for reducing overall context-switch overhead, especially when
/// combined with kqueue.
/// ```
/// # use std::os::unix::io::AsRawFd;
/// # use std::os::unix::io::AsFd;
/// # use std::thread;
/// # use std::time;
/// # use nix::errno::Errno;
@@ -1108,7 +1116,7 @@ pub fn aio_suspend(
/// const WBUF: &[u8] = b"abcdef123456";
/// let mut f = tempfile().unwrap();
/// let mut aiow = Box::pin(AioWrite::new(
/// f.as_raw_fd(),
/// f.as_fd(),
/// 2, // offset
/// WBUF,
/// 0, // priority
@@ -1132,18 +1140,15 @@ pub fn aio_suspend(
/// possibly resubmit some.
/// ```
/// # use libc::c_int;
/// # use std::os::unix::io::AsRawFd;
/// # use std::os::unix::io::AsFd;
/// # use std::sync::atomic::{AtomicBool, Ordering};
/// # use std::thread;
/// # use std::time;
/// # use lazy_static::lazy_static;
/// # use nix::errno::Errno;
/// # use nix::sys::aio::*;
/// # use nix::sys::signal::*;
/// # use tempfile::tempfile;
/// lazy_static! {
/// pub static ref SIGNALED: AtomicBool = AtomicBool::new(false);
/// }
/// pub static SIGNALED: AtomicBool = AtomicBool::new(false);
///
/// extern fn sigfunc(_: c_int) {
/// SIGNALED.store(true, Ordering::Relaxed);
@@ -1157,7 +1162,7 @@ pub fn aio_suspend(
/// const WBUF: &[u8] = b"abcdef123456";
/// let mut f = tempfile().unwrap();
/// let mut aiow = Box::pin(AioWrite::new(
/// f.as_raw_fd(),
/// f.as_fd(),
/// 2, // offset
/// WBUF,
/// 0, // priority
@@ -1172,6 +1177,10 @@ pub fn aio_suspend(
/// // notification, we know that all operations are complete.
/// assert_eq!(aiow.as_mut().aio_return().unwrap(), WBUF.len());
/// ```
#[deprecated(
since = "0.27.0",
note = "https://github.com/nix-rust/nix/issues/2017"
)]
pub fn lio_listio(
mode: LioMode,
list: &mut [Pin<&mut dyn AsMut<libc::aiocb>>],
@@ -1186,56 +1195,3 @@ pub fn lio_listio(
})
.map(drop)
}
#[cfg(test)]
mod t {
use super::*;
/// aio_suspend relies on casting Rust Aio* struct pointers to libc::aiocb
/// pointers. This test ensures that such casts are valid.
#[test]
fn casting() {
let sev = SigevNotify::SigevNone;
let aiof = AioFsync::new(666, AioFsyncMode::O_SYNC, 0, sev);
assert_eq!(
aiof.as_ref() as *const libc::aiocb,
&aiof as *const AioFsync as *const libc::aiocb
);
let mut rbuf = [];
let aior = AioRead::new(666, 0, &mut rbuf, 0, sev);
assert_eq!(
aior.as_ref() as *const libc::aiocb,
&aior as *const AioRead as *const libc::aiocb
);
let wbuf = [];
let aiow = AioWrite::new(666, 0, &wbuf, 0, sev);
assert_eq!(
aiow.as_ref() as *const libc::aiocb,
&aiow as *const AioWrite as *const libc::aiocb
);
}
#[cfg(target_os = "freebsd")]
#[test]
fn casting_vectored() {
let sev = SigevNotify::SigevNone;
let mut rbuf = [];
let mut rbufs = [IoSliceMut::new(&mut rbuf)];
let aiorv = AioReadv::new(666, 0, &mut rbufs[..], 0, sev);
assert_eq!(
aiorv.as_ref() as *const libc::aiocb,
&aiorv as *const AioReadv as *const libc::aiocb
);
let wbuf = [];
let wbufs = [IoSlice::new(&wbuf)];
let aiowv = AioWritev::new(666, 0, &wbufs, 0, sev);
assert_eq!(
aiowv.as_ref() as *const libc::aiocb,
&aiowv as *const AioWritev as *const libc::aiocb
);
}
}
+131 -4
View File
@@ -1,9 +1,10 @@
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;
use std::os::unix::io::RawFd;
use std::ptr;
use std::os::unix::io::{AsFd, AsRawFd, FromRawFd, OwnedFd, RawFd};
libc_bitflags!(
pub struct EpollFlags: c_int {
@@ -70,6 +71,126 @@ impl EpollEvent {
}
}
/// A safe wrapper around [`epoll`](https://man7.org/linux/man-pages/man7/epoll.7.html).
/// ```
/// # use nix::sys::{epoll::{EpollTimeout, Epoll, EpollEvent, EpollFlags, EpollCreateFlags}, eventfd::{EventFd, EfdFlags}};
/// # use nix::unistd::write;
/// # use std::os::unix::io::{OwnedFd, FromRawFd, AsFd};
/// # use std::time::{Instant, Duration};
/// # fn main() -> nix::Result<()> {
/// const DATA: u64 = 17;
/// const MILLIS: u8 = 100;
///
/// // Create epoll
/// let epoll = Epoll::new(EpollCreateFlags::empty())?;
///
/// // Create eventfd & Add event
/// let eventfd = EventFd::new()?;
/// epoll.add(&eventfd, EpollEvent::new(EpollFlags::EPOLLIN,DATA))?;
///
/// // Arm eventfd & Time wait
/// eventfd.write(1)?;
/// let now = Instant::now();
///
/// // Wait on event
/// let mut events = [EpollEvent::empty()];
/// epoll.wait(&mut events, MILLIS)?;
///
/// // Assert data correct & timeout didn't occur
/// assert_eq!(events[0].data(), DATA);
/// assert!(now.elapsed().as_millis() < MILLIS.into());
/// # Ok(())
/// # }
/// ```
#[derive(Debug)]
pub struct Epoll(pub OwnedFd);
impl Epoll {
/// Creates a new epoll instance and returns a file descriptor referring to that instance.
///
/// [`epoll_create1`](https://man7.org/linux/man-pages/man2/epoll_create1.2.html).
pub fn new(flags: EpollCreateFlags) -> Result<Self> {
let res = unsafe { libc::epoll_create1(flags.bits()) };
let fd = Errno::result(res)?;
let owned_fd = unsafe { OwnedFd::from_raw_fd(fd) };
Ok(Self(owned_fd))
}
/// Add an entry to the interest list of the epoll file descriptor for
/// specified in events.
///
/// [`epoll_ctl`](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html) with `EPOLL_CTL_ADD`.
pub fn add<Fd: AsFd>(&self, fd: Fd, mut event: EpollEvent) -> Result<()> {
self.epoll_ctl(EpollOp::EpollCtlAdd, fd, &mut event)
}
/// Remove (deregister) the target file descriptor `fd` from the interest list.
///
/// [`epoll_ctl`](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html) with `EPOLL_CTL_DEL` .
pub fn delete<Fd: AsFd>(&self, fd: Fd) -> Result<()> {
self.epoll_ctl(EpollOp::EpollCtlDel, fd, None)
}
/// Change the settings associated with `fd` in the interest list to the new settings specified
/// in `event`.
///
/// [`epoll_ctl`](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html) with `EPOLL_CTL_MOD`.
pub fn modify<Fd: AsFd>(
&self,
fd: Fd,
event: &mut EpollEvent,
) -> Result<()> {
self.epoll_ctl(EpollOp::EpollCtlMod, fd, event)
}
/// Waits for I/O events, blocking the calling thread if no events are currently available.
/// (This can be thought of as fetching items from the ready list of the epoll instance.)
///
/// [`epoll_wait`](https://man7.org/linux/man-pages/man2/epoll_wait.2.html)
pub fn wait<T: Into<EpollTimeout>>(
&self,
events: &mut [EpollEvent],
timeout: T,
) -> Result<usize> {
let res = unsafe {
libc::epoll_wait(
self.0.as_raw_fd(),
events.as_mut_ptr().cast(),
events.len() as c_int,
timeout.into().into(),
)
};
Errno::result(res).map(|r| r as usize)
}
/// This system call is used to add, modify, or remove entries in the interest list of the epoll
/// instance referred to by `self`. It requests that the operation `op` be performed for the
/// target file descriptor, `fd`.
///
/// When possible prefer [`Epoll::add`], [`Epoll::delete`] and [`Epoll::modify`].
///
/// [`epoll_ctl`](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html)
fn epoll_ctl<'a, Fd: AsFd, T>(
&self,
op: EpollOp,
fd: Fd,
event: T,
) -> Result<()>
where
T: Into<Option<&'a mut EpollEvent>>,
{
let event: Option<&mut EpollEvent> = event.into();
let ptr = event
.map(|x| &mut x.event as *mut libc::epoll_event)
.unwrap_or(std::ptr::null_mut());
unsafe {
Errno::result(libc::epoll_ctl(
self.0.as_raw_fd(),
op as c_int,
fd.as_fd().as_raw_fd(),
ptr,
))
.map(drop)
}
}
}
#[deprecated(since = "0.27.0", note = "Use Epoll::new() instead")]
#[inline]
pub fn epoll_create() -> Result<RawFd> {
let res = unsafe { libc::epoll_create(1024) };
@@ -77,6 +198,7 @@ pub fn epoll_create() -> Result<RawFd> {
Errno::result(res)
}
#[deprecated(since = "0.27.0", note = "Use Epoll::new() instead")]
#[inline]
pub fn epoll_create1(flags: EpollCreateFlags) -> Result<RawFd> {
let res = unsafe { libc::epoll_create1(flags.bits()) };
@@ -84,6 +206,10 @@ pub fn epoll_create1(flags: EpollCreateFlags) -> Result<RawFd> {
Errno::result(res)
}
#[deprecated(
since = "0.27.0",
note = "Use corresponding Epoll methods instead"
)]
#[inline]
pub fn epoll_ctl<'a, T>(
epfd: RawFd,
@@ -102,13 +228,14 @@ where
if let Some(ref mut event) = event {
libc::epoll_ctl(epfd, op as c_int, fd, &mut event.event)
} else {
libc::epoll_ctl(epfd, op as c_int, fd, ptr::null_mut())
libc::epoll_ctl(epfd, op as c_int, fd, std::ptr::null_mut())
}
};
Errno::result(res).map(drop)
}
}
#[deprecated(since = "0.27.0", note = "Use Epoll::wait() instead")]
#[inline]
pub fn epoll_wait(
epfd: RawFd,
@@ -118,7 +245,7 @@ pub fn epoll_wait(
let res = unsafe {
libc::epoll_wait(
epfd,
events.as_mut_ptr() as *mut libc::epoll_event,
events.as_mut_ptr().cast(),
events.len() as c_int,
timeout_ms as c_int,
)
+238 -152
View File
@@ -1,5 +1,7 @@
/* TOOD: Implement for other kqueue based systems
*/
//! Kernel event notification mechanism
//!
//! # See Also
//! [kqueue(2)](https://www.freebsd.org/cgi/man.cgi?query=kqueue)
use crate::{Errno, Result};
#[cfg(not(target_os = "netbsd"))]
@@ -8,23 +10,88 @@ use libc::{c_int, c_long, intptr_t, time_t, timespec, uintptr_t};
use libc::{c_long, intptr_t, size_t, time_t, timespec, uintptr_t};
use std::convert::TryInto;
use std::mem;
use std::os::unix::io::RawFd;
use std::os::fd::{AsFd, BorrowedFd};
use std::os::unix::io::{AsRawFd, FromRawFd, OwnedFd};
use std::ptr;
// Redefine kevent in terms of programmer-friendly enums and bitfields.
/// A kernel event queue. Used to notify a process of various asynchronous
/// events.
#[repr(C)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct KEvent {
kevent: libc::kevent,
}
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "openbsd"
))]
/// A kernel event queue.
///
/// Used by the kernel to notify the process of various types of asynchronous
/// events.
#[repr(transparent)]
#[derive(Debug)]
pub struct Kqueue(OwnedFd);
impl AsFd for Kqueue {
fn as_fd(&self) -> BorrowedFd<'_> {
self.0.as_fd()
}
}
impl From<Kqueue> for OwnedFd {
fn from(value: Kqueue) -> Self {
value.0
}
}
impl Kqueue {
/// Create a new kernel event queue.
pub fn new() -> Result<Self> {
let res = unsafe { libc::kqueue() };
Errno::result(res).map(|fd| unsafe { Self(OwnedFd::from_raw_fd(fd)) })
}
/// Register new events with the kqueue, and return any pending events to
/// the user.
///
/// This method will block until either the timeout expires, or a registered
/// event triggers a notification.
///
/// # Arguments
/// - `changelist` - Any new kevents to register for notifications.
/// - `eventlist` - Storage space for the kernel to return notifications.
/// - `timeout` - An optional timeout.
///
/// # Returns
/// Returns the number of events placed in the `eventlist`. If an error
/// occurs while processing an element of the `changelist` and there is
/// enough room in the `eventlist`, then the event will be placed in the
/// `eventlist` with `EV_ERROR` set in `flags` and the system error in
/// `data`.
pub fn kevent(
&self,
changelist: &[KEvent],
eventlist: &mut [KEvent],
timeout_opt: Option<timespec>,
) -> Result<usize> {
let res = unsafe {
libc::kevent(
self.0.as_raw_fd(),
changelist.as_ptr().cast(),
changelist.len() as type_of_nchanges,
eventlist.as_mut_ptr().cast(),
eventlist.len() as type_of_nchanges,
if let Some(ref timeout) = timeout_opt {
timeout as *const timespec
} else {
ptr::null()
},
)
};
Errno::result(res).map(|r| r as usize)
}
}
#[cfg(any(freebsdlike, apple_targets, target_os = "openbsd"))]
type type_of_udata = *mut libc::c_void;
#[cfg(target_os = "netbsd")]
type type_of_udata = intptr_t;
@@ -37,22 +104,31 @@ libc_enum! {
#[cfg_attr(target_os = "netbsd", repr(u32))]
#[cfg_attr(not(target_os = "netbsd"), repr(i16))]
#[non_exhaustive]
/// Kqueue filter types. These are all the different types of event that a
/// kqueue can notify for.
pub enum EventFilter {
/// Notifies on the completion of a POSIX AIO operation.
EVFILT_AIO,
/// Returns whenever there is no remaining data in the write buffer
#[cfg(target_os = "freebsd")]
/// Returns whenever there is no remaining data in the write buffer
EVFILT_EMPTY,
#[cfg(target_os = "dragonfly")]
/// Takes a descriptor as the identifier, and returns whenever one of
/// the specified exceptional conditions has occurred on the descriptor.
EVFILT_EXCEPT,
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos"))]
#[cfg(any(freebsdlike, apple_targets))]
/// Establishes a file system monitor.
EVFILT_FS,
#[cfg(target_os = "freebsd")]
/// Notify for completion of a list of POSIX AIO operations.
/// # See Also
/// [lio_listio(2)](https://www.freebsd.org/cgi/man.cgi?query=lio_listio)
EVFILT_LIO,
#[cfg(any(target_os = "ios", target_os = "macos"))]
#[cfg(apple_targets)]
/// Mach portsets
EVFILT_MACHPORT,
/// Notifies when a process performs one or more of the requested
/// events.
EVFILT_PROC,
/// Returns events associated with the process referenced by a given
/// process descriptor, created by `pdfork()`. The events to monitor are:
@@ -60,157 +136,207 @@ libc_enum! {
/// - NOTE_EXIT: the process has exited. The exit status will be stored in data.
#[cfg(target_os = "freebsd")]
EVFILT_PROCDESC,
/// Takes a file descriptor as the identifier, and notifies whenever
/// there is data available to read.
EVFILT_READ,
/// Returns whenever an asynchronous `sendfile()` call completes.
#[cfg(target_os = "freebsd")]
#[doc(hidden)]
#[deprecated(since = "0.27.0", note = "Never fully implemented by the OS")]
EVFILT_SENDFILE,
/// Takes a signal number to monitor as the identifier and notifies when
/// the given signal is delivered to the process.
EVFILT_SIGNAL,
/// Establishes a timer and notifies when the timer expires.
EVFILT_TIMER,
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos"))]
#[cfg(any(freebsdlike, apple_targets))]
/// Notifies only when explicitly requested by the user.
EVFILT_USER,
#[cfg(any(target_os = "ios", target_os = "macos"))]
#[cfg(apple_targets)]
/// Virtual memory events
EVFILT_VM,
/// Notifies when a requested event happens on a specified file.
EVFILT_VNODE,
/// Takes a file descriptor as the identifier, and notifies whenever
/// it is possible to write to the file without blocking.
EVFILT_WRITE,
}
impl TryFrom<type_of_event_filter>
}
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "openbsd"
))]
#[cfg(any(freebsdlike, apple_targets, target_os = "openbsd"))]
#[doc(hidden)]
pub type type_of_event_flag = u16;
#[cfg(target_os = "netbsd")]
#[doc(hidden)]
pub type type_of_event_flag = u32;
libc_bitflags! {
pub struct EventFlag: type_of_event_flag {
/// 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 EvFlags: type_of_event_flag {
#[allow(missing_docs)]
EV_ADD;
#[allow(missing_docs)]
EV_CLEAR;
#[allow(missing_docs)]
EV_DELETE;
#[allow(missing_docs)]
EV_DISABLE;
#[cfg(any(target_os = "dragonfly", target_os = "freebsd",
target_os = "ios", target_os = "macos",
target_os = "netbsd", target_os = "openbsd"))]
#[cfg(bsd)]
#[allow(missing_docs)]
EV_DISPATCH;
#[cfg(target_os = "freebsd")]
#[allow(missing_docs)]
EV_DROP;
#[allow(missing_docs)]
EV_ENABLE;
#[allow(missing_docs)]
EV_EOF;
#[allow(missing_docs)]
EV_ERROR;
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[cfg(apple_targets)]
#[allow(missing_docs)]
EV_FLAG0;
#[allow(missing_docs)]
EV_FLAG1;
#[cfg(target_os = "dragonfly")]
#[allow(missing_docs)]
EV_NODATA;
#[allow(missing_docs)]
EV_ONESHOT;
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[cfg(apple_targets)]
#[allow(missing_docs)]
EV_OOBAND;
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[cfg(apple_targets)]
#[allow(missing_docs)]
EV_POLL;
#[cfg(any(target_os = "dragonfly", target_os = "freebsd",
target_os = "ios", target_os = "macos",
target_os = "netbsd", target_os = "openbsd"))]
#[cfg(bsd)]
#[allow(missing_docs)]
EV_RECEIPT;
}
}
#[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
// that wouldn't simply be repeating the man page.
#[allow(missing_docs)]
pub struct FilterFlag: u32 {
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[cfg(apple_targets)]
#[allow(missing_docs)]
NOTE_ABSOLUTE;
#[allow(missing_docs)]
NOTE_ATTRIB;
#[allow(missing_docs)]
NOTE_CHILD;
#[allow(missing_docs)]
NOTE_DELETE;
#[cfg(target_os = "openbsd")]
#[allow(missing_docs)]
NOTE_EOF;
#[allow(missing_docs)]
NOTE_EXEC;
#[allow(missing_docs)]
NOTE_EXIT;
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[cfg(apple_targets)]
#[allow(missing_docs)]
NOTE_EXITSTATUS;
#[allow(missing_docs)]
NOTE_EXTEND;
#[cfg(any(target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "dragonfly"))]
#[cfg(any(apple_targets, freebsdlike))]
#[allow(missing_docs)]
NOTE_FFAND;
#[cfg(any(target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "dragonfly"))]
#[cfg(any(apple_targets, freebsdlike))]
#[allow(missing_docs)]
NOTE_FFCOPY;
#[cfg(any(target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "dragonfly"))]
#[cfg(any(apple_targets, freebsdlike))]
#[allow(missing_docs)]
NOTE_FFCTRLMASK;
#[cfg(any(target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "dragonfly"))]
#[cfg(any(apple_targets, freebsdlike))]
#[allow(missing_docs)]
NOTE_FFLAGSMASK;
#[cfg(any(target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "dragonfly"))]
#[cfg(any(apple_targets, freebsdlike))]
#[allow(missing_docs)]
NOTE_FFNOP;
#[cfg(any(target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "dragonfly"))]
#[cfg(any(apple_targets, freebsdlike))]
#[allow(missing_docs)]
NOTE_FFOR;
#[allow(missing_docs)]
NOTE_FORK;
#[allow(missing_docs)]
NOTE_LINK;
#[allow(missing_docs)]
NOTE_LOWAT;
#[cfg(target_os = "freebsd")]
#[allow(missing_docs)]
NOTE_MSECONDS;
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[cfg(apple_targets)]
#[allow(missing_docs)]
NOTE_NONE;
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
#[cfg(any(
apple_targets,
target_os = "freebsd"))]
#[allow(missing_docs)]
NOTE_NSECONDS;
#[cfg(target_os = "dragonfly")]
#[allow(missing_docs)]
NOTE_OOB;
#[allow(missing_docs)]
NOTE_PCTRLMASK;
#[allow(missing_docs)]
NOTE_PDATAMASK;
#[allow(missing_docs)]
NOTE_RENAME;
#[allow(missing_docs)]
NOTE_REVOKE;
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
#[cfg(any(
apple_targets,
target_os = "freebsd"))]
#[allow(missing_docs)]
NOTE_SECONDS;
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[cfg(apple_targets)]
#[allow(missing_docs)]
NOTE_SIGNAL;
#[allow(missing_docs)]
NOTE_TRACK;
#[allow(missing_docs)]
NOTE_TRACKERR;
#[cfg(any(target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "dragonfly"))]
#[cfg(any(apple_targets, freebsdlike))]
#[allow(missing_docs)]
NOTE_TRIGGER;
#[cfg(target_os = "openbsd")]
#[allow(missing_docs)]
NOTE_TRUNCATE;
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
#[cfg(any(
apple_targets,
target_os = "freebsd"))]
#[allow(missing_docs)]
NOTE_USECONDS;
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[cfg(apple_targets)]
#[allow(missing_docs)]
NOTE_VM_ERROR;
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[cfg(apple_targets)]
#[allow(missing_docs)]
NOTE_VM_PRESSURE;
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[cfg(apple_targets)]
#[allow(missing_docs)]
NOTE_VM_PRESSURE_SUDDEN_TERMINATE;
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[cfg(apple_targets)]
#[allow(missing_docs)]
NOTE_VM_PRESSURE_TERMINATE;
#[allow(missing_docs)]
NOTE_WRITE;
}
);
pub fn kqueue() -> Result<RawFd> {
let res = unsafe { libc::kqueue() };
Errno::result(res)
#[allow(missing_docs)]
#[deprecated(since = "0.27.0", note = "Use KEvent::new instead")]
pub fn kqueue() -> Result<Kqueue> {
Kqueue::new()
}
// KEvent can't derive Send because on some operating systems, udata is defined
@@ -220,10 +346,12 @@ unsafe impl Send for KEvent {}
impl KEvent {
#[allow(clippy::needless_update)] // Not needless on all platforms.
/// Construct a new `KEvent` suitable for submission to the kernel via the
/// `changelist` argument of [`Kqueue::kevent`].
pub fn new(
ident: uintptr_t,
filter: EventFilter,
flags: EventFlag,
flags: EvFlags,
fflags: FilterFlag,
data: intptr_t,
udata: intptr_t,
@@ -242,33 +370,46 @@ impl KEvent {
}
}
/// Value used to identify this event. The exact interpretation is
/// determined by the attached filter, but often is a raw file descriptor.
pub fn ident(&self) -> uintptr_t {
self.kevent.ident
}
/// Identifies the kernel filter used to process this event.
///
/// Will only return an error if the kernel reports an event via a filter
/// that is unknown to Nix.
pub fn filter(&self) -> Result<EventFilter> {
self.kevent.filter.try_into()
}
pub fn flags(&self) -> EventFlag {
EventFlag::from_bits(self.kevent.flags).unwrap()
/// Flags control what the kernel will do when this event is added with
/// [`Kqueue::kevent`].
pub fn flags(&self) -> EvFlags {
EvFlags::from_bits(self.kevent.flags).unwrap()
}
/// Filter-specific flags.
pub fn fflags(&self) -> FilterFlag {
FilterFlag::from_bits(self.kevent.fflags).unwrap()
}
/// Filter-specific data value.
pub fn data(&self) -> intptr_t {
self.kevent.data as intptr_t
}
/// Opaque user-defined value passed through the kernel unchanged.
pub fn udata(&self) -> intptr_t {
self.kevent.udata as intptr_t
}
}
#[allow(missing_docs)]
#[deprecated(since = "0.27.0", note = "Use Kqueue::kevent instead")]
pub fn kevent(
kq: RawFd,
kq: &Kqueue,
changelist: &[KEvent],
eventlist: &mut [KEvent],
timeout_ms: usize,
@@ -279,50 +420,34 @@ pub fn kevent(
tv_nsec: ((timeout_ms % 1000) * 1_000_000) as c_long,
};
kevent_ts(kq, changelist, eventlist, Some(timeout))
kq.kevent(changelist, eventlist, Some(timeout))
}
#[cfg(any(
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "openbsd"
))]
#[cfg(any(apple_targets, freebsdlike, target_os = "openbsd"))]
type type_of_nchanges = c_int;
#[cfg(target_os = "netbsd")]
type type_of_nchanges = size_t;
#[allow(missing_docs)]
#[deprecated(since = "0.27.0", note = "Use Kqueue::kevent instead")]
pub fn kevent_ts(
kq: RawFd,
kq: &Kqueue,
changelist: &[KEvent],
eventlist: &mut [KEvent],
timeout_opt: Option<timespec>,
) -> Result<usize> {
let res = unsafe {
libc::kevent(
kq,
changelist.as_ptr() as *const libc::kevent,
changelist.len() as type_of_nchanges,
eventlist.as_mut_ptr() as *mut libc::kevent,
eventlist.len() as type_of_nchanges,
if let Some(ref timeout) = timeout_opt {
timeout as *const timespec
} else {
ptr::null()
},
)
};
Errno::result(res).map(|r| r as usize)
kq.kevent(changelist, eventlist, timeout_opt)
}
/// Modify an existing [`KEvent`].
// Probably should deprecate. Would anybody ever use it over `KEvent::new`?
#[deprecated(since = "0.27.0", note = "Use Kqueue::kevent instead")]
#[inline]
pub fn ev_set(
ev: &mut KEvent,
ident: usize,
filter: EventFilter,
flags: EventFlag,
flags: EvFlags,
fflags: FilterFlag,
udata: intptr_t,
) {
@@ -333,42 +458,3 @@ pub fn ev_set(
ev.kevent.data = 0;
ev.kevent.udata = udata as type_of_udata;
}
#[test]
fn test_struct_kevent() {
use std::mem;
let udata: intptr_t = 12345;
let actual = KEvent::new(
0xdead_beef,
EventFilter::EVFILT_READ,
EventFlag::EV_ONESHOT | EventFlag::EV_ADD,
FilterFlag::NOTE_CHILD | FilterFlag::NOTE_EXIT,
0x1337,
udata,
);
assert_eq!(0xdead_beef, actual.ident());
let filter = actual.kevent.filter;
assert_eq!(libc::EVFILT_READ, filter);
assert_eq!(libc::EV_ONESHOT | libc::EV_ADD, actual.flags().bits());
assert_eq!(libc::NOTE_CHILD | libc::NOTE_EXIT, actual.fflags().bits());
assert_eq!(0x1337, actual.data());
assert_eq!(udata as type_of_udata, actual.udata() as type_of_udata);
assert_eq!(mem::size_of::<libc::kevent>(), mem::size_of::<KEvent>());
}
#[test]
fn test_kevent_filter() {
let udata: intptr_t = 12345;
let actual = KEvent::new(
0xdead_beef,
EventFilter::EVFILT_READ,
EventFlag::EV_ONESHOT | EventFlag::EV_ADD,
FilterFlag::NOTE_CHILD | FilterFlag::NOTE_EXIT,
0x1337,
udata,
);
assert_eq!(EventFilter::EVFILT_READ, actual.filter().unwrap());
}
+108 -7
View File
@@ -1,17 +1,118 @@
use crate::errno::Errno;
use crate::Result;
use std::os::unix::io::RawFd;
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 {
EFD_CLOEXEC; // Since Linux 2.6.27
EFD_NONBLOCK; // Since Linux 2.6.27
EFD_SEMAPHORE; // Since Linux 2.6.30
/// 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
}
}
pub fn eventfd(initval: libc::c_uint, flags: EfdFlags) -> Result<RawFd> {
#[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| r as RawFd)
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> {
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())
}
/// Constructs an `EventFd` wrapping an existing `OwnedFd`.
///
/// # Safety
///
/// `OwnedFd` is a valid eventfd.
pub unsafe fn from_owned_fd(fd: OwnedFd) -> Self {
Self(fd)
}
/// 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())
}
/// 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, &mut arr)?;
Ok(u64::from_ne_bytes(arr))
}
}
impl AsFd for EventFd {
fn as_fd(&self) -> BorrowedFd {
self.0.as_fd()
}
}
impl AsRawFd for EventFd {
fn as_raw_fd(&self) -> RawFd {
self.0.as_raw_fd()
}
}
impl From<EventFd> for OwnedFd {
fn from(value: EventFd) -> Self {
value.0
}
}
+446
View File
@@ -0,0 +1,446 @@
//! Monitoring API for filesystem events.
//!
//! Fanotify is a Linux-only API to monitor filesystems events.
//!
//! Additional capabilities compared to the `inotify` API include the ability to
//! monitor all of the objects in a mounted filesystem, the ability to make
//! access permission decisions, and the possibility to read or modify files
//! before access by other applications.
//!
//! For more documentation, please read
//! [fanotify(7)](https://man7.org/linux/man-pages/man7/fanotify.7.html).
use crate::errno::Errno;
use crate::fcntl::OFlag;
use crate::unistd::{close, read, write};
use crate::{NixPath, Result};
use std::marker::PhantomData;
use std::mem::{size_of, MaybeUninit};
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()`]
/// and for querying notifications.
pub struct MaskFlags: u64 {
/// File was accessed.
FAN_ACCESS;
/// File was modified.
FAN_MODIFY;
/// Metadata has changed. Since Linux 5.1.
FAN_ATTRIB;
/// Writtable file was closed.
FAN_CLOSE_WRITE;
/// Unwrittable file was closed.
FAN_CLOSE_NOWRITE;
/// File was opened.
FAN_OPEN;
/// File was moved from X. Since Linux 5.1.
FAN_MOVED_FROM;
/// File was moved to Y. Since Linux 5.1.
FAN_MOVED_TO;
/// Subfile was created. Since Linux 5.1.
FAN_CREATE;
/// Subfile was deleted. Since Linux 5.1.
FAN_DELETE;
/// Self was deleted. Since Linux 5.1.
FAN_DELETE_SELF;
/// Self was moved. Since Linux 5.1.
FAN_MOVE_SELF;
/// File was opened for execution. Since Linux 5.0.
FAN_OPEN_EXEC;
/// Event queue overflowed.
FAN_Q_OVERFLOW;
/// Filesystem error. Since Linux 5.16.
FAN_FS_ERROR;
/// Permission to open file was requested.
FAN_OPEN_PERM;
/// Permission to access file was requested.
FAN_ACCESS_PERM;
/// Permission to open file for execution was requested. Since Linux
/// 5.0.
FAN_OPEN_EXEC_PERM;
/// Interested in child events.
FAN_EVENT_ON_CHILD;
/// File was renamed. Since Linux 5.17.
FAN_RENAME;
/// Event occurred against dir.
FAN_ONDIR;
/// Combination of `FAN_CLOSE_WRITE` and `FAN_CLOSE_NOWRITE`.
FAN_CLOSE;
/// Combination of `FAN_MOVED_FROM` and `FAN_MOVED_TO`.
FAN_MOVE;
}
}
libc_bitflags! {
/// Configuration options for [`Fanotify::init()`].
pub struct InitFlags: libc::c_uint {
/// Close-on-exec flag set on the file descriptor.
FAN_CLOEXEC;
/// Nonblocking flag set on the file descriptor.
FAN_NONBLOCK;
/// Receipt of events notifications.
FAN_CLASS_NOTIF;
/// Receipt of events for permission decisions, after they contain final
/// data.
FAN_CLASS_CONTENT;
/// Receipt of events for permission decisions, before they contain
/// final data.
FAN_CLASS_PRE_CONTENT;
/// Remove the limit on the number of events in the event queue.
///
/// Prior to Linux kernel 5.13, this limit was hardcoded to 16384. After
/// 5.13, one can change it via file `/proc/sys/fs/fanotify/max_queued_events`.
///
/// See `fanotify(7)` for details about this limit. Use of this flag
/// requires the `CAP_SYS_ADMIN` capability.
FAN_UNLIMITED_QUEUE;
/// Remove the limit on the number of fanotify marks per user.
///
/// Prior to Linux kernel 5.13, this limit was hardcoded to 8192 (per
/// group, not per user). After 5.13, one can change it via file
/// `/proc/sys/fs/fanotify/max_user_marks`.
///
/// See `fanotify(7)` for details about this limit. Use of this flag
/// requires the `CAP_SYS_ADMIN` capability.
FAN_UNLIMITED_MARKS;
/// Make `FanotifyEvent::pid` return pidfd. Since Linux 5.15.
FAN_REPORT_PIDFD;
/// Make `FanotifyEvent::pid` return thread id. Since Linux 4.20.
FAN_REPORT_TID;
}
}
libc_bitflags! {
/// File status flags for fanotify events file descriptors.
pub struct EventFFlags: libc::c_uint {
/// Read only access.
O_RDONLY as libc::c_uint;
/// Write only access.
O_WRONLY as libc::c_uint;
/// Read and write access.
O_RDWR as libc::c_uint;
/// Support for files exceeded 2 GB.
O_LARGEFILE as libc::c_uint;
/// Close-on-exec flag for the file descriptor. Since Linux 3.18.
O_CLOEXEC as libc::c_uint;
/// Append mode for the file descriptor.
O_APPEND as libc::c_uint;
/// Synchronized I/O data integrity completion.
O_DSYNC as libc::c_uint;
/// No file last access time update.
O_NOATIME as libc::c_uint;
/// Nonblocking mode for the file descriptor.
O_NONBLOCK as libc::c_uint;
/// Synchronized I/O file integrity completion.
O_SYNC as libc::c_uint;
}
}
impl TryFrom<OFlag> for EventFFlags {
type Error = Errno;
fn try_from(o_flag: OFlag) -> Result<Self> {
EventFFlags::from_bits(o_flag.bits() as u32).ok_or(Errno::EINVAL)
}
}
impl From<EventFFlags> for OFlag {
fn from(event_f_flags: EventFFlags) -> Self {
OFlag::from_bits_retain(event_f_flags.bits() as i32)
}
}
libc_bitflags! {
/// Configuration options for [`Fanotify::mark()`].
pub struct MarkFlags: libc::c_uint {
/// Add the events to the marks.
FAN_MARK_ADD;
/// Remove the events to the marks.
FAN_MARK_REMOVE;
/// Don't follow symlinks, mark them.
FAN_MARK_DONT_FOLLOW;
/// Raise an error if filesystem to be marked is not a directory.
FAN_MARK_ONLYDIR;
/// Events added to or removed from the marks.
FAN_MARK_IGNORED_MASK;
/// Ignore mask shall survive modify events.
FAN_MARK_IGNORED_SURV_MODIFY;
/// Remove all marks.
FAN_MARK_FLUSH;
/// Do not pin inode object in the inode cache. Since Linux 5.19.
FAN_MARK_EVICTABLE;
/// Events added to or removed from the marks. Since Linux 6.0.
FAN_MARK_IGNORE;
/// Default flag.
FAN_MARK_INODE;
/// Mark the mount specified by pathname.
FAN_MARK_MOUNT;
/// Mark the filesystem specified by pathname. Since Linux 4.20.
FAN_MARK_FILESYSTEM;
/// Combination of `FAN_MARK_IGNORE` and `FAN_MARK_IGNORED_SURV_MODIFY`.
FAN_MARK_IGNORE_SURV;
}
}
/// 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`].
// Is not Clone due to fd field, to avoid use-after-close scenarios.
#[derive(Debug, Eq, Hash, PartialEq)]
#[repr(transparent)]
#[allow(missing_copy_implementations)]
pub struct FanotifyEvent(libc::fanotify_event_metadata);
impl FanotifyEvent {
/// Version number for the structure. It must be compared to
/// `FANOTIFY_METADATA_VERSION` to verify compile version and runtime
/// version does match. It can be done with the
/// `FanotifyEvent::check_version` method.
pub fn version(&self) -> u8 {
self.0.vers
}
/// Checks that compile fanotify API version is equal to the version of the
/// event.
pub fn check_version(&self) -> bool {
self.version() == FANOTIFY_METADATA_VERSION
}
/// Mask flags of the events.
pub fn mask(&self) -> MaskFlags {
MaskFlags::from_bits_truncate(self.0.mask)
}
/// The file descriptor of the event. If the value is `None` when reading
/// from the fanotify group, this event is to notify that a group queue
/// overflow occured.
pub fn fd(&self) -> Option<BorrowedFd> {
if self.0.fd == libc::FAN_NOFD {
None
} else {
// SAFETY: self.0.fd will be opened for the lifetime of `Self`,
// which is longer than the lifetime of the returned BorrowedFd, so
// it is safe.
Some(unsafe { BorrowedFd::borrow_raw(self.0.fd) })
}
}
/// PID of the process that caused the event. TID in case flag
/// `FAN_REPORT_TID` was set at group initialization.
pub fn pid(&self) -> i32 {
self.0.pid
}
}
impl Drop for FanotifyEvent {
fn drop(&mut self) {
if self.0.fd == libc::FAN_NOFD {
return;
}
let e = close(self.0.fd);
if !std::thread::panicking() && e == Err(Errno::EBADF) {
panic!("Closing an invalid file descriptor!");
};
}
}
/// Abstraction over the structure to be sent to allow or deny a given event.
#[derive(Debug)]
#[repr(transparent)]
pub struct FanotifyResponse<'a> {
inner: libc::fanotify_response,
_borrowed_fd: PhantomData<BorrowedFd<'a>>,
}
impl<'a> FanotifyResponse<'a> {
/// Create a new response.
pub fn new(fd: BorrowedFd<'a>, response: Response) -> Self {
Self {
inner: libc::fanotify_response {
fd: fd.as_raw_fd(),
response: response.bits(),
},
_borrowed_fd: PhantomData,
}
}
}
libc_bitflags! {
/// 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.
FAN_ALLOW;
/// Deny the event.
FAN_DENY;
}
}
/// A fanotify group. This is also a file descriptor that can feed to other
/// interfaces consuming file descriptors.
#[derive(Debug)]
pub struct Fanotify {
fd: OwnedFd,
}
impl Fanotify {
/// Initialize a new fanotify group.
///
/// Returns a Result containing a Fanotify instance.
///
/// For more information, see [fanotify_init(2)](https://man7.org/linux/man-pages/man7/fanotify_init.2.html).
pub fn init(
flags: InitFlags,
event_f_flags: EventFFlags,
) -> Result<Fanotify> {
let res = Errno::result(unsafe {
libc::fanotify_init(flags.bits(), event_f_flags.bits())
});
res.map(|fd| Fanotify {
fd: unsafe { OwnedFd::from_raw_fd(fd) },
})
}
/// Add, remove, or modify an fanotify mark on a filesystem object.
///
/// 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<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
&self,
flags: MarkFlags,
mask: MaskFlags,
dirfd: Fd,
path: Option<&P>,
) -> Result<()> {
let res = crate::with_opt_nix_path(path, |p| unsafe {
libc::fanotify_mark(
self.fd.as_raw_fd(),
flags.bits(),
mask.bits(),
dirfd.as_fd().as_raw_fd(),
p,
)
})?;
Errno::result(res).map(|_| ())
}
/// Read incoming events from the fanotify group.
///
/// Returns a Result containing either a `Vec` of events on success or errno
/// otherwise.
///
/// # Errors
///
/// Possible errors can be those that are explicitly listed in
/// [fanotify(2)](https://man7.org/linux/man-pages/man7/fanotify.2.html) in
/// addition to the possible errors caused by `read` call.
/// In particular, `EAGAIN` is returned when no event is available on a
/// group that has been initialized with the flag `InitFlags::FAN_NONBLOCK`,
/// thus making this method nonblocking.
pub fn read_events(&self) -> Result<Vec<FanotifyEvent>> {
let metadata_size = size_of::<libc::fanotify_event_metadata>();
const BUFSIZ: usize = 4096;
let mut buffer = [0u8; BUFSIZ];
let mut events = Vec::new();
let mut offset = 0;
let nread = read(&self.fd, &mut buffer)?;
while (nread - offset) >= metadata_size {
let metadata = unsafe {
let mut metadata =
MaybeUninit::<libc::fanotify_event_metadata>::uninit();
ptr::copy_nonoverlapping(
buffer.as_ptr().add(offset),
metadata.as_mut_ptr().cast(),
(BUFSIZ - offset).min(metadata_size),
);
metadata.assume_init()
};
events.push(FanotifyEvent(metadata));
offset += metadata.event_len as usize;
}
Ok(events)
}
/// Write an event response on the fanotify group.
///
/// Returns a Result containing either `()` on success or errno otherwise.
///
/// # Errors
///
/// Possible errors can be those that are explicitly listed in
/// [fanotify(2)](https://man7.org/linux/man-pages/man7/fanotify.2.html) in
/// addition to the possible errors caused by `write` call.
/// In particular, `EAGAIN` or `EWOULDBLOCK` is returned when no event is
/// available on a group that has been initialized with the flag
/// `InitFlags::FAN_NONBLOCK`, thus making this method nonblocking.
pub fn write_response(&self, response: FanotifyResponse) -> Result<()> {
write(self.fd.as_fd(), unsafe {
std::slice::from_raw_parts(
(&response.inner as *const libc::fanotify_response).cast(),
size_of::<libc::fanotify_response>(),
)
})?;
Ok(())
}
}
impl FromRawFd for Fanotify {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
Fanotify {
fd: unsafe { OwnedFd::from_raw_fd(fd) },
}
}
}
impl AsFd for Fanotify {
fn as_fd(&'_ self) -> BorrowedFd<'_> {
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
}
}
}
+41 -16
View File
@@ -32,7 +32,7 @@ use libc::{c_char, c_int};
use std::ffi::{CStr, OsStr, OsString};
use std::mem::{size_of, MaybeUninit};
use std::os::unix::ffi::OsStrExt;
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd};
use std::ptr;
libc_bitflags! {
@@ -101,9 +101,9 @@ libc_bitflags! {
/// An inotify instance. This is also a file descriptor, you can feed it to
/// other interfaces consuming file descriptors, epoll for example.
#[derive(Debug, Clone, Copy)]
#[derive(Debug)]
pub struct Inotify {
fd: RawFd,
fd: OwnedFd,
}
/// This object is returned when you create a new watch on an inotify instance.
@@ -143,7 +143,9 @@ impl Inotify {
pub fn init(flags: InitFlags) -> Result<Inotify> {
let res = Errno::result(unsafe { libc::inotify_init1(flags.bits()) });
res.map(|fd| Inotify { fd })
res.map(|fd| Inotify {
fd: unsafe { OwnedFd::from_raw_fd(fd) },
})
}
/// Adds a new watch on the target file or directory.
@@ -152,12 +154,16 @@ impl Inotify {
///
/// For more information see, [inotify_add_watch(2)](https://man7.org/linux/man-pages/man2/inotify_add_watch.2.html).
pub fn add_watch<P: ?Sized + NixPath>(
self,
&self,
path: &P,
mask: AddWatchFlags,
) -> Result<WatchDescriptor> {
let res = path.with_nix_path(|cstr| unsafe {
libc::inotify_add_watch(self.fd, cstr.as_ptr(), mask.bits())
libc::inotify_add_watch(
self.fd.as_raw_fd(),
cstr.as_ptr(),
mask.bits(),
)
})?;
Errno::result(res).map(|wd| WatchDescriptor { wd })
@@ -169,7 +175,7 @@ impl Inotify {
/// Returns an EINVAL error if the watch descriptor is invalid.
///
/// For more information see, [inotify_rm_watch(2)](https://man7.org/linux/man-pages/man2/inotify_rm_watch.2.html).
pub fn rm_watch(self, wd: WatchDescriptor) -> Result<()> {
pub fn rm_watch(&self, wd: WatchDescriptor) -> Result<()> {
cfg_if! {
if #[cfg(target_os = "linux")] {
let arg = wd.wd;
@@ -177,7 +183,7 @@ impl Inotify {
let arg = wd.wd as u32;
}
}
let res = unsafe { libc::inotify_rm_watch(self.fd, arg) };
let res = unsafe { libc::inotify_rm_watch(self.fd.as_raw_fd(), arg) };
Errno::result(res).map(drop)
}
@@ -188,21 +194,21 @@ impl Inotify {
///
/// Returns as many events as available. If the call was non blocking and no
/// events could be read then the EAGAIN error is returned.
pub fn read_events(self) -> Result<Vec<InotifyEvent>> {
pub fn read_events(&self) -> Result<Vec<InotifyEvent>> {
let header_size = size_of::<libc::inotify_event>();
const BUFSIZ: usize = 4096;
let mut buffer = [0u8; BUFSIZ];
let mut events = Vec::new();
let mut offset = 0;
let nread = read(self.fd, &mut buffer)?;
let nread = read(&self.fd, &mut buffer)?;
while (nread - offset) >= header_size {
let event = unsafe {
let mut event = MaybeUninit::<libc::inotify_event>::uninit();
ptr::copy_nonoverlapping(
buffer.as_ptr().add(offset),
event.as_mut_ptr() as *mut u8,
event.as_mut_ptr().cast(),
(BUFSIZ - offset).min(header_size),
);
event.assume_init()
@@ -233,16 +239,35 @@ impl Inotify {
Ok(events)
}
}
impl AsRawFd for Inotify {
fn as_raw_fd(&self) -> RawFd {
self.fd
/// 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 {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
Inotify { fd }
Inotify {
fd: unsafe { OwnedFd::from_raw_fd(fd) },
}
}
}
impl AsFd for Inotify {
fn as_fd(&'_ self) -> BorrowedFd<'_> {
self.fd.as_fd()
}
}
impl From<Inotify> for OwnedFd {
fn from(value: Inotify) -> Self {
value.fd
}
}
+2 -2
View File
@@ -1,10 +1,10 @@
/// The datatype used for the ioctl number
#[doc(hidden)]
#[cfg(not(target_os = "illumos"))]
#[cfg(not(solarish))]
pub type ioctl_num_type = ::libc::c_ulong;
#[doc(hidden)]
#[cfg(target_os = "illumos")]
#[cfg(solarish)]
pub type ioctl_num_type = ::libc::c_int;
/// The datatype used for the 3rd argument
+51 -44
View File
@@ -1,8 +1,20 @@
use cfg_if::cfg_if;
/// The datatype used for the ioctl number
#[cfg(any(target_os = "android", target_env = "musl", target_env = "ohos"))]
#[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", target_env = "ohos")))]
#[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
@@ -14,48 +26,43 @@ pub const NRBITS: ioctl_num_type = 8;
#[doc(hidden)]
pub const TYPEBITS: ioctl_num_type = 8;
#[cfg(any(
target_arch = "mips",
target_arch = "mips64",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "sparc64"
))]
mod consts {
#[doc(hidden)]
pub const NONE: u8 = 1;
#[doc(hidden)]
pub const READ: u8 = 2;
#[doc(hidden)]
pub const WRITE: u8 = 4;
#[doc(hidden)]
pub const SIZEBITS: u8 = 13;
#[doc(hidden)]
pub const DIRBITS: u8 = 3;
}
// "Generic" ioctl protocol
#[cfg(any(
target_arch = "x86",
target_arch = "arm",
target_arch = "s390x",
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "riscv32",
target_arch = "riscv64",
target_arch = "loongarch64"
))]
mod consts {
#[doc(hidden)]
pub const NONE: u8 = 0;
#[doc(hidden)]
pub const READ: u8 = 2;
#[doc(hidden)]
pub const WRITE: u8 = 1;
#[doc(hidden)]
pub const SIZEBITS: u8 = 14;
#[doc(hidden)]
pub const DIRBITS: u8 = 2;
cfg_if! {
if #[cfg(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "sparc64"
))] {
mod consts {
#[doc(hidden)]
pub const NONE: u8 = 1;
#[doc(hidden)]
pub const READ: u8 = 2;
#[doc(hidden)]
pub const WRITE: u8 = 4;
#[doc(hidden)]
pub const SIZEBITS: u8 = 13;
#[doc(hidden)]
pub const DIRBITS: u8 = 3;
}
} else {
// "Generic" ioctl protocol
mod consts {
#[doc(hidden)]
pub const NONE: u8 = 0;
#[doc(hidden)]
pub const READ: u8 = 2;
#[doc(hidden)]
pub const WRITE: u8 = 1;
#[doc(hidden)]
pub const SIZEBITS: u8 = 14;
#[doc(hidden)]
pub const DIRBITS: u8 = 2;
}
}
}
pub use self::consts::*;
+63 -51
View File
@@ -72,7 +72,7 @@
//! # const SPI_IOC_MAGIC: u8 = b'k'; // Defined in linux/spi/spidev.h
//! # const SPI_IOC_TYPE_MODE: u8 = 1;
//! pub unsafe fn spi_read_mode(fd: c_int, data: *mut u8) -> Result<c_int> {
//! let res = libc::ioctl(fd, request_code_read!(SPI_IOC_MAGIC, SPI_IOC_TYPE_MODE, mem::size_of::<u8>()), data);
//! let res = unsafe { libc::ioctl(fd, request_code_read!(SPI_IOC_MAGIC, SPI_IOC_TYPE_MODE, mem::size_of::<u8>()), data) };
//! Errno::result(res)
//! }
//! # fn main() {}
@@ -121,11 +121,11 @@
//!
//! ```
//! # #[macro_use] extern crate nix;
//! # #[cfg(any(target_os = "android", target_os = "linux"))]
//! # #[cfg(linux_android)]
//! # use nix::libc::TCGETS as TCGETS;
//! # #[cfg(any(target_os = "android", target_os = "linux"))]
//! # #[cfg(linux_android)]
//! # use nix::libc::termios as termios;
//! # #[cfg(any(target_os = "android", target_os = "linux"))]
//! # #[cfg(linux_android)]
//! ioctl_read_bad!(tcgets, TCGETS, termios);
//! # fn main() {}
//! ```
@@ -179,9 +179,13 @@
//! # const SPI_IOC_TYPE_MESSAGE: u8 = 0;
//! # pub struct spi_ioc_transfer(u64);
//! pub unsafe fn spi_message(fd: c_int, data: &mut [spi_ioc_transfer]) -> Result<c_int> {
//! let res = libc::ioctl(fd,
//! request_code_write!(SPI_IOC_MAGIC, SPI_IOC_TYPE_MESSAGE, data.len() * mem::size_of::<spi_ioc_transfer>()),
//! data);
//! let res = unsafe {
//! libc::ioctl(
//! fd,
//! request_code_write!(SPI_IOC_MAGIC, SPI_IOC_TYPE_MESSAGE, data.len() * mem::size_of::<spi_ioc_transfer>()),
//! data
//! )
//! };
//! Errno::result(res)
//! }
//! # fn main() {}
@@ -223,40 +227,18 @@
//! ```
use cfg_if::cfg_if;
#[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
#[cfg(any(linux_android, target_os = "fuchsia", target_os = "redox"))]
#[macro_use]
mod linux;
#[cfg(any(
target_os = "android",
target_os = "linux",
target_os = "redox"
))]
#[cfg(any(linux_android, target_os = "fuchsia", target_os = "redox"))]
pub use self::linux::*;
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "haiku",
target_os = "openbsd"
))]
#[cfg(any(bsd, solarish, target_os = "haiku",))]
#[macro_use]
mod bsd;
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "haiku",
target_os = "openbsd"
))]
#[cfg(any(bsd, solarish, target_os = "haiku",))]
pub use self::bsd::*;
/// Convert raw ioctl return value to a Nix result
@@ -305,7 +287,9 @@ macro_rules! ioctl_none {
$(#[$attr])*
pub unsafe fn $name(fd: $crate::libc::c_int)
-> $crate::Result<$crate::libc::c_int> {
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_none!($ioty, $nr) as $crate::sys::ioctl::ioctl_num_type))
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_none!($ioty, $nr) as $crate::sys::ioctl::ioctl_num_type))
}
}
)
}
@@ -345,7 +329,9 @@ macro_rules! ioctl_none_bad {
$(#[$attr])*
pub unsafe fn $name(fd: $crate::libc::c_int)
-> $crate::Result<$crate::libc::c_int> {
convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type))
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type))
}
}
)
}
@@ -383,7 +369,9 @@ macro_rules! ioctl_read {
pub unsafe fn $name(fd: $crate::libc::c_int,
data: *mut $ty)
-> $crate::Result<$crate::libc::c_int> {
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_read!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data))
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_read!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data))
}
}
)
}
@@ -408,7 +396,7 @@ macro_rules! ioctl_read {
///
/// ```
/// # #[macro_use] extern crate nix;
/// # #[cfg(any(target_os = "android", target_os = "linux"))]
/// # #[cfg(linux_android)]
/// ioctl_read_bad!(tcgets, libc::TCGETS, libc::termios);
/// # fn main() {}
/// ```
@@ -419,7 +407,9 @@ macro_rules! ioctl_read_bad {
pub unsafe fn $name(fd: $crate::libc::c_int,
data: *mut $ty)
-> $crate::Result<$crate::libc::c_int> {
convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data))
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data))
}
}
)
}
@@ -456,7 +446,9 @@ macro_rules! ioctl_write_ptr {
pub unsafe fn $name(fd: $crate::libc::c_int,
data: *const $ty)
-> $crate::Result<$crate::libc::c_int> {
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_write!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data))
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_write!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data))
}
}
)
}
@@ -481,7 +473,7 @@ macro_rules! ioctl_write_ptr {
///
/// ```
/// # #[macro_use] extern crate nix;
/// # #[cfg(any(target_os = "android", target_os = "linux"))]
/// # #[cfg(linux_android)]
/// ioctl_write_ptr_bad!(tcsets, libc::TCSETS, libc::termios);
/// # fn main() {}
/// ```
@@ -492,13 +484,15 @@ macro_rules! ioctl_write_ptr_bad {
pub unsafe fn $name(fd: $crate::libc::c_int,
data: *const $ty)
-> $crate::Result<$crate::libc::c_int> {
convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data))
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data))
}
}
)
}
cfg_if! {
if #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] {
if #[cfg(freebsdlike)] {
/// Generates a wrapper function for a ioctl that writes an integer to the kernel.
///
/// The arguments to this macro are:
@@ -533,7 +527,9 @@ cfg_if! {
pub unsafe fn $name(fd: $crate::libc::c_int,
data: $crate::sys::ioctl::ioctl_param_type)
-> $crate::Result<$crate::libc::c_int> {
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_write_int!($ioty, $nr) as $crate::sys::ioctl::ioctl_num_type, data))
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_write_int!($ioty, $nr) as $crate::sys::ioctl::ioctl_num_type, data))
}
}
)
}
@@ -574,7 +570,9 @@ cfg_if! {
pub unsafe fn $name(fd: $crate::libc::c_int,
data: $crate::sys::ioctl::ioctl_param_type)
-> $crate::Result<$crate::libc::c_int> {
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_write!($ioty, $nr, ::std::mem::size_of::<$crate::libc::c_int>()) as $crate::sys::ioctl::ioctl_num_type, data))
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_write!($ioty, $nr, ::std::mem::size_of::<$crate::libc::c_int>()) as $crate::sys::ioctl::ioctl_num_type, data))
}
}
)
}
@@ -600,7 +598,7 @@ cfg_if! {
///
/// ```
/// # #[macro_use] extern crate nix;
/// # #[cfg(any(target_os = "android", target_os = "linux"))]
/// # #[cfg(linux_android)]
/// ioctl_write_int_bad!(tcsbrk, libc::TCSBRK);
/// # fn main() {}
/// ```
@@ -618,7 +616,9 @@ macro_rules! ioctl_write_int_bad {
pub unsafe fn $name(fd: $crate::libc::c_int,
data: $crate::libc::c_int)
-> $crate::Result<$crate::libc::c_int> {
convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data))
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data))
}
}
)
}
@@ -655,7 +655,11 @@ macro_rules! ioctl_readwrite {
pub unsafe fn $name(fd: $crate::libc::c_int,
data: *mut $ty)
-> $crate::Result<$crate::libc::c_int> {
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_readwrite!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data))
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))
}
}
)
}
@@ -683,7 +687,9 @@ macro_rules! ioctl_readwrite_bad {
pub unsafe fn $name(fd: $crate::libc::c_int,
data: *mut $ty)
-> $crate::Result<$crate::libc::c_int> {
convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data))
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data))
}
}
)
}
@@ -712,7 +718,9 @@ macro_rules! ioctl_read_buf {
pub unsafe fn $name(fd: $crate::libc::c_int,
data: &mut [$ty])
-> $crate::Result<$crate::libc::c_int> {
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_read!($ioty, $nr, ::std::mem::size_of_val(data)) as $crate::sys::ioctl::ioctl_num_type, data))
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_read!($ioty, $nr, ::std::mem::size_of_val(data)) as $crate::sys::ioctl::ioctl_num_type, data.as_mut_ptr()))
}
}
)
}
@@ -751,7 +759,9 @@ macro_rules! ioctl_write_buf {
pub unsafe fn $name(fd: $crate::libc::c_int,
data: &[$ty])
-> $crate::Result<$crate::libc::c_int> {
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_write!($ioty, $nr, ::std::mem::size_of_val(data)) as $crate::sys::ioctl::ioctl_num_type, data))
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_write!($ioty, $nr, ::std::mem::size_of_val(data)) as $crate::sys::ioctl::ioctl_num_type, data.as_ptr()))
}
}
)
}
@@ -780,7 +790,9 @@ macro_rules! ioctl_readwrite_buf {
pub unsafe fn $name(fd: $crate::libc::c_int,
data: &mut [$ty])
-> $crate::Result<$crate::libc::c_int> {
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_readwrite!($ioty, $nr, ::std::mem::size_of_val(data)) as $crate::sys::ioctl::ioctl_num_type, data))
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_readwrite!($ioty, $nr, ::std::mem::size_of_val(data)) as $crate::sys::ioctl::ioctl_num_type, data.as_mut_ptr()))
}
}
)
}
+77 -13
View File
@@ -1,15 +1,14 @@
//! Interfaces for managing memory-backed files.
use cfg_if::cfg_if;
use std::os::unix::io::RawFd;
use std::os::unix::io::{FromRawFd, OwnedFd, RawFd};
use crate::errno::Errno;
use crate::Result;
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`]
@@ -29,9 +28,68 @@ libc_bitflags!(
///
/// [`memfd_create(2)`]: https://man7.org/linux/man-pages/man2/memfd_create.2.html
MFD_ALLOW_SEALING;
/// Anonymous file will be created using huge pages. It should be safe now to
/// combine with [`MFD_ALLOW_SEALING`] too.
/// However, despite its presence, on FreeBSD it is unimplemented for now (ENOSYS).
///
/// See also the hugetlb filesystem in [`memfd_create(2)`].
///
/// [`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)`].
///
/// [`memfd_create(2)`]: https://man7.org/linux/man-pages/man2/memfd_create.2.html
#[cfg(linux_android)]
MFD_HUGE_1MB;
/// hugetlb size of 2MB.
#[cfg(linux_android)]
MFD_HUGE_2MB;
/// hugetlb size of 8MB.
#[cfg(linux_android)]
MFD_HUGE_8MB;
/// hugetlb size of 16MB.
#[cfg(linux_android)]
MFD_HUGE_16MB;
/// hugetlb size of 32MB.
#[cfg(linux_android)]
MFD_HUGE_32MB;
/// hugetlb size of 256MB.
#[cfg(linux_android)]
MFD_HUGE_256MB;
/// hugetlb size of 512MB.
#[cfg(linux_android)]
MFD_HUGE_512MB;
/// hugetlb size of 1GB.
#[cfg(linux_android)]
MFD_HUGE_1GB;
/// hugetlb size of 2GB.
#[cfg(linux_android)]
MFD_HUGE_2GB;
/// hugetlb size of 16GB.
#[cfg(linux_android)]
MFD_HUGE_16GB;
}
);
#[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.
@@ -40,26 +98,32 @@ libc_bitflags!(
/// For more information, see [`memfd_create(2)`].
///
/// [`memfd_create(2)`]: https://man7.org/linux/man-pages/man2/memfd_create.2.html
pub fn memfd_create(name: &CStr, flags: MemFdCreateFlag) -> Result<RawFd> {
let res = unsafe {
cfg_if! {
#[inline] // Delays codegen, preventing linker errors with dylibs and --no-allow-shlib-undefined
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",
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| r as RawFd)
Errno::result(res).map(|r| unsafe { OwnedFd::from_raw_fd(r as RawFd) })
}
+226 -158
View File
@@ -1,14 +1,18 @@
//! Memory management declarations.
use crate::errno::Errno;
#[cfg(not(any(target_os = "android", target_env = "ohos")))]
#[cfg(not(target_os = "android"))]
use crate::NixPath;
use crate::Result;
#[cfg(not(any(target_os = "android", target_env = "ohos")))]
#[cfg(not(target_os = "android"))]
#[cfg(feature = "fs")]
use crate::{fcntl::OFlag, sys::stat::Mode};
use libc::{self, c_int, c_void, off_t, size_t};
use std::{os::unix::io::RawFd, num::NonZeroUsize};
use std::ptr::NonNull;
use std::{
num::NonZeroUsize,
os::unix::io::{AsFd, AsRawFd},
};
libc_bitflags! {
/// Desired memory protection of a memory mapping.
@@ -22,12 +26,10 @@ libc_bitflags! {
/// Pages can be executed
PROT_EXEC;
/// Apply protection up to the end of a mapping that grows upwards.
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
PROT_GROWSDOWN;
/// Apply protection down to the beginning of a mapping that grows downwards.
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
PROT_GROWSUP;
}
}
@@ -36,154 +38,161 @@ 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`.
MAP_FIXED;
/// Place the mapping at exactly the address specified in `addr`, but never clobber an existing range.
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(all())))]
MAP_FIXED_NOREPLACE;
/// To be used with `MAP_FIXED`, to forbid the system
/// to select a different address than the one specified.
#[cfg(target_os = "freebsd")]
#[cfg_attr(docsrs, doc(cfg(all())))]
MAP_EXCL;
/// Synonym for `MAP_ANONYMOUS`.
MAP_ANON;
/// The mapping is not backed by any file.
MAP_ANONYMOUS;
/// Put the mapping into the first 2GB of the process address space.
#[cfg(any(all(any(target_os = "android", target_os = "linux"),
#[cfg(any(all(linux_android,
any(target_arch = "x86", target_arch = "x86_64")),
all(target_os = "linux", any(target_env = "musl", target_env = "ohos"), 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")))]
#[cfg_attr(docsrs, doc(cfg(all())))]
MAP_32BIT;
/// Used for stacks; indicates to the kernel that the mapping should extend downward in memory.
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
MAP_GROWSDOWN;
/// Compatibility flag. Ignored.
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
MAP_DENYWRITE;
/// Compatibility flag. Ignored.
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
MAP_EXECUTABLE;
/// Mark the mmaped region to be locked in the same way as `mlock(2)`.
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
MAP_LOCKED;
/// Do not reserve swap space for this mapping.
///
/// This was removed in FreeBSD 11 and is unused in DragonFlyBSD.
#[cfg(not(any(target_os = "dragonfly", target_os = "freebsd")))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(not(any(freebsdlike, target_os = "aix", target_os = "hurd", target_os = "redox")))]
MAP_NORESERVE;
/// Populate page tables for a mapping.
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
MAP_POPULATE;
/// Only meaningful when used with `MAP_POPULATE`. Don't perform read-ahead.
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
MAP_NONBLOCK;
/// Allocate the mapping using "huge pages."
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
MAP_HUGETLB;
/// Make use of 64KB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(all())))]
MAP_HUGE_64KB;
/// Make use of 512KB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(all())))]
MAP_HUGE_512KB;
/// Make use of 1MB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(all())))]
MAP_HUGE_1MB;
/// Make use of 2MB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(all())))]
MAP_HUGE_2MB;
/// Make use of 8MB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(all())))]
MAP_HUGE_8MB;
/// Make use of 16MB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(all())))]
MAP_HUGE_16MB;
/// Make use of 32MB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(all())))]
MAP_HUGE_32MB;
/// Make use of 256MB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(all())))]
MAP_HUGE_256MB;
/// Make use of 512MB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(all())))]
MAP_HUGE_512MB;
/// Make use of 1GB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(all())))]
MAP_HUGE_1GB;
/// Make use of 2GB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(all())))]
MAP_HUGE_2GB;
/// Make use of 16GB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(all())))]
MAP_HUGE_16GB;
/// Lock the mapped region into memory as with `mlock(2)`.
#[cfg(target_os = "netbsd")]
#[cfg_attr(docsrs, doc(cfg(all())))]
MAP_WIRED;
/// Causes dirtied data in the specified range to be flushed to disk only when necessary.
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(freebsdlike)]
MAP_NOSYNC;
/// Rename private pages to a file.
///
/// This was removed in FreeBSD 11 and is unused in DragonFlyBSD.
#[cfg(any(target_os = "netbsd", target_os = "openbsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(netbsdlike)]
MAP_RENAME;
/// Region may contain semaphores.
#[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(any(freebsdlike, netbsdlike))]
MAP_HASSEMAPHORE;
/// Region grows down, like a stack.
#[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "linux", target_os = "openbsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[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(any(target_os = "ios", target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(apple_targets)]
MAP_NOCACHE;
/// Allows the W/X bit on the page, it's necessary on aarch64 architecture.
#[cfg(any(target_os = "ios", target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(apple_targets)]
MAP_JIT;
/// Allows to use large pages, underlying alignment based on size.
#[cfg(target_os = "freebsd")]
#[cfg_attr(docsrs, doc(cfg(all())))]
MAP_ALIGNED_SUPER;
/// Pages will be discarded in the core dumps.
#[cfg(target_os = "openbsd")]
#[cfg_attr(docsrs, doc(cfg(all())))]
MAP_CONCEAL;
/// Attempt to place the mapping at exactly the address specified in `addr`.
/// it's a default behavior on OpenBSD.
#[cfg(netbsdlike)]
MAP_TRYFIXED;
}
}
impl MapFlags {
/// Create `MAP_HUGETLB` with provided size of huge page.
///
/// Under the hood it computes `MAP_HUGETLB | (huge_page_size_log2 << libc::MAP_HUGE_SHIFT`).
/// `huge_page_size_log2` denotes logarithm of huge page size to use and should be
/// between 16 and 63 (inclusively).
///
/// ```
/// # use nix::sys::mman::MapFlags;
/// let f = MapFlags::map_hugetlb_with_size_log2(30).unwrap();
/// assert_eq!(f, MapFlags::MAP_HUGETLB | MapFlags::MAP_HUGE_1GB);
/// ```
#[cfg(any(linux_android, target_os = "fuchsia"))]
pub fn map_hugetlb_with_size_log2(
huge_page_size_log2: u32,
) -> Option<Self> {
if (16..=63).contains(&huge_page_size_log2) {
let flag = libc::MAP_HUGETLB
| (huge_page_size_log2 << libc::MAP_HUGE_SHIFT) as i32;
Some(Self(flag.into()))
} else {
None
}
}
}
@@ -193,19 +202,19 @@ libc_bitflags! {
pub struct MRemapFlags: c_int {
/// Permit the kernel to relocate the mapping to a new virtual address, if necessary.
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(all())))]
MREMAP_MAYMOVE;
/// Place the mapping at exactly the address specified in `new_address`.
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(all())))]
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")]
#[cfg_attr(docsrs, doc(cfg(all())))]
MAP_FIXED;
/// Allows to duplicate the mapping to be able to apply different flags on the copy.
#[cfg(target_os = "netbsd")]
#[cfg_attr(docsrs, doc(cfg(all())))]
MAP_REMAPDUP;
}
}
@@ -228,30 +237,24 @@ libc_enum! {
/// Do not expect access in the near future.
MADV_DONTNEED,
/// Free up a given range of pages and its associated backing store.
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
MADV_REMOVE,
/// Do not make pages in this range available to the child after a `fork(2)`.
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
MADV_DONTFORK,
/// Undo the effect of `MADV_DONTFORK`.
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
MADV_DOFORK,
/// Poison the given pages.
///
/// Subsequent references to those pages are treated like hardware memory corruption.
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
MADV_HWPOISON,
/// Enable Kernel Samepage Merging (KSM) for the given pages.
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
MADV_MERGEABLE,
/// Undo the effect of `MADV_MERGEABLE`
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
MADV_UNMERGEABLE,
/// Preserve the memory of each page but offline the original page.
#[cfg(any(target_os = "android",
@@ -266,68 +269,73 @@ libc_enum! {
target_arch = "sparc64"))))]
MADV_SOFT_OFFLINE,
/// Enable Transparent Huge Pages (THP) for pages in the given range.
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
MADV_HUGEPAGE,
/// Undo the effect of `MADV_HUGEPAGE`.
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
MADV_NOHUGEPAGE,
/// Exclude the given range from a core dump.
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
MADV_DONTDUMP,
/// Undo the effect of an earlier `MADV_DONTDUMP`.
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[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", target_os = "cygwin", target_os = "redox")))]
MADV_FREE,
/// Request that the system not flush the current range to disk unless it needs to.
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(freebsdlike)]
MADV_NOSYNC,
/// Undoes the effects of `MADV_NOSYNC` for any future pages dirtied within the given range.
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(freebsdlike)]
MADV_AUTOSYNC,
/// Region is not included in a core file.
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(freebsdlike)]
MADV_NOCORE,
/// Include region in a core file
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(freebsdlike)]
MADV_CORE,
/// This process should not be killed when swap space is exhausted.
#[cfg(any(target_os = "freebsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
MADV_PROTECT,
/// Invalidate the hardware page table for the given region.
#[cfg(target_os = "dragonfly")]
#[cfg_attr(docsrs, doc(cfg(all())))]
MADV_INVAL,
/// Set the offset of the page directory page to `value` for the virtual page table.
#[cfg(target_os = "dragonfly")]
#[cfg_attr(docsrs, doc(cfg(all())))]
MADV_SETMAP,
/// Indicates that the application will not need the data in the given range.
#[cfg(any(target_os = "ios", target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(apple_targets)]
MADV_ZERO_WIRED_PAGES,
/// Pages can be reused (by anyone).
#[cfg(any(target_os = "ios", target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(apple_targets)]
MADV_FREE_REUSABLE,
/// Caller wants to reuse those pages.
#[cfg(any(target_os = "ios", target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(apple_targets)]
MADV_FREE_REUSE,
// Darwin doesn't document this flag's behavior.
#[cfg(any(target_os = "ios", target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[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,
}
}
@@ -339,19 +347,17 @@ libc_bitflags! {
/// Invalidate all cached data.
MS_INVALIDATE;
/// Invalidate pages, but leave them mapped.
#[cfg(any(target_os = "ios", target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(apple_targets)]
MS_KILLPAGES;
/// Deactivate pages, but leave them mapped.
#[cfg(any(target_os = "ios", target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(apple_targets)]
MS_DEACTIVATE;
/// Perform an update and wait for it to complete.
MS_SYNC;
}
}
#[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 {
@@ -372,8 +378,8 @@ libc_bitflags! {
/// `addr` must meet all the requirements described in the [`mlock(2)`] man page.
///
/// [`mlock(2)`]: https://man7.org/linux/man-pages/man2/mlock.2.html
pub unsafe fn mlock(addr: *const c_void, length: size_t) -> Result<()> {
Errno::result(libc::mlock(addr, length)).map(drop)
pub unsafe fn mlock(addr: NonNull<c_void>, length: size_t) -> Result<()> {
unsafe { Errno::result(libc::mlock(addr.as_ptr(), length)).map(drop) }
}
/// Unlocks all memory pages that contain part of the address range with
@@ -385,8 +391,8 @@ pub unsafe fn mlock(addr: *const c_void, length: size_t) -> Result<()> {
/// page.
///
/// [`munlock(2)`]: https://man7.org/linux/man-pages/man2/munlock.2.html
pub unsafe fn munlock(addr: *const c_void, length: size_t) -> Result<()> {
Errno::result(libc::munlock(addr, length)).map(drop)
pub unsafe fn munlock(addr: NonNull<c_void>, length: size_t) -> Result<()> {
unsafe { Errno::result(libc::munlock(addr.as_ptr(), length)).map(drop) }
}
/// Locks all memory pages mapped into this process' address space.
@@ -394,7 +400,7 @@ pub unsafe fn munlock(addr: *const 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)
}
@@ -404,37 +410,73 @@ 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)
}
/// allocate memory, or map files or devices into memory
/// Allocate memory, or map files or devices into memory
///
/// For anonymous mappings (`MAP_ANON`/`MAP_ANONYMOUS`), see [mmap_anonymous].
///
/// # Safety
///
/// See the [`mmap(2)`] man page for detailed requirements.
///
/// [`mmap(2)`]: https://man7.org/linux/man-pages/man2/mmap.2.html
pub unsafe fn mmap(
pub unsafe fn mmap<F: AsFd>(
addr: Option<NonZeroUsize>,
length: NonZeroUsize,
prot: ProtFlags,
flags: MapFlags,
fd: RawFd,
f: F,
offset: off_t,
) -> Result<*mut c_void> {
let ptr = addr.map_or(
std::ptr::null_mut(),
|a| usize::from(a) as *mut c_void
);
let ret = libc::mmap(ptr, length.into(), prot.bits(), flags.bits(), fd, offset);
) -> Result<NonNull<c_void>> {
let ptr = addr.map_or(std::ptr::null_mut(), |a| a.get() as *mut c_void);
if ret == libc::MAP_FAILED {
let fd = f.as_fd().as_raw_fd();
let ret = unsafe {
libc::mmap(ptr, length.into(), prot.bits(), flags.bits(), fd, offset)
};
if std::ptr::eq(ret, libc::MAP_FAILED) {
Err(Errno::last())
} else {
Ok(ret)
// SAFETY: `libc::mmap` returns a valid non-null pointer or `libc::MAP_FAILED`, thus `ret`
// will be non-null here.
Ok(unsafe { NonNull::new_unchecked(ret) })
}
}
/// Create an anonymous memory mapping.
///
/// This function is a wrapper around [`mmap`]:
/// `mmap(ptr, len, prot, MAP_ANONYMOUS | flags, -1, 0)`.
///
/// # Safety
///
/// See the [`mmap(2)`] man page for detailed requirements.
///
/// [`mmap(2)`]: https://man7.org/linux/man-pages/man2/mmap.2.html
pub unsafe fn mmap_anonymous(
addr: Option<NonZeroUsize>,
length: NonZeroUsize,
prot: ProtFlags,
flags: MapFlags,
) -> Result<NonNull<c_void>> {
let ptr = addr.map_or(std::ptr::null_mut(), |a| a.get() as *mut c_void);
let flags = MapFlags::MAP_ANONYMOUS | flags;
let ret = unsafe {
libc::mmap(ptr, length.into(), prot.bits(), flags.bits(), -1, 0)
};
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`
// will be non-null here.
Ok(unsafe { NonNull::new_unchecked(ret) })
}
}
@@ -447,33 +489,43 @@ pub unsafe fn mmap(
/// detailed requirements.
#[cfg(any(target_os = "linux", target_os = "netbsd"))]
pub unsafe fn mremap(
addr: *mut c_void,
addr: NonNull<c_void>,
old_size: size_t,
new_size: size_t,
flags: MRemapFlags,
new_address: Option<*mut c_void>,
) -> Result<*mut c_void> {
new_address: Option<NonNull<c_void>>,
) -> Result<NonNull<c_void>> {
#[cfg(target_os = "linux")]
let ret = libc::mremap(
addr,
old_size,
new_size,
flags.bits(),
new_address.unwrap_or(std::ptr::null_mut()),
);
let ret = unsafe {
libc::mremap(
addr.as_ptr(),
old_size,
new_size,
flags.bits(),
new_address
.map(NonNull::as_ptr)
.unwrap_or(std::ptr::null_mut()),
)
};
#[cfg(target_os = "netbsd")]
let ret = libc::mremap(
addr,
old_size,
new_address.unwrap_or(std::ptr::null_mut()),
new_size,
flags.bits(),
);
let ret = unsafe {
libc::mremap(
addr.as_ptr(),
old_size,
new_address
.map(NonNull::as_ptr)
.unwrap_or(std::ptr::null_mut()),
new_size,
flags.bits(),
)
};
if ret == libc::MAP_FAILED {
if std::ptr::eq(ret, libc::MAP_FAILED) {
Err(Errno::last())
} else {
Ok(ret)
// SAFETY: `libc::mremap` returns a valid non-null pointer or `libc::MAP_FAILED`, thus `ret`
// will be non-null here.
Ok(unsafe { NonNull::new_unchecked(ret) })
}
}
@@ -485,8 +537,8 @@ pub unsafe fn mremap(
/// page.
///
/// [`munmap(2)`]: https://man7.org/linux/man-pages/man2/munmap.2.html
pub unsafe fn munmap(addr: *mut c_void, len: size_t) -> Result<()> {
Errno::result(libc::munmap(addr, len)).map(drop)
pub unsafe fn munmap(addr: NonNull<c_void>, len: size_t) -> Result<()> {
unsafe { Errno::result(libc::munmap(addr.as_ptr(), len)).map(drop) }
}
/// give advice about use of memory
@@ -497,12 +549,16 @@ pub unsafe fn munmap(addr: *mut c_void, len: size_t) -> Result<()> {
/// [`MmapAdvise::MADV_FREE`].
///
/// [`madvise(2)`]: https://man7.org/linux/man-pages/man2/madvise.2.html
#[allow(rustdoc::broken_intra_doc_links)] // For Hurd as `MADV_FREE` is not available on it
pub unsafe fn madvise(
addr: *mut c_void,
addr: NonNull<c_void>,
length: size_t,
advise: MmapAdvise,
) -> Result<()> {
Errno::result(libc::madvise(addr, length, advise as i32)).map(drop)
unsafe {
Errno::result(libc::madvise(addr.as_ptr(), length, advise as i32))
.map(drop)
}
}
/// Set protection of memory mapping.
@@ -517,26 +573,30 @@ pub unsafe fn madvise(
///
/// ```
/// # use nix::libc::size_t;
/// # use nix::sys::mman::{mmap, mprotect, MapFlags, ProtFlags};
/// # use nix::sys::mman::{mmap_anonymous, mprotect, MapFlags, ProtFlags};
/// # use std::ptr;
/// # use std::os::unix::io::BorrowedFd;
/// const ONE_K: size_t = 1024;
/// let one_k_non_zero = std::num::NonZeroUsize::new(ONE_K).unwrap();
/// let mut slice: &mut [u8] = unsafe {
/// let mem = mmap(None, one_k_non_zero, ProtFlags::PROT_NONE,
/// MapFlags::MAP_ANON | MapFlags::MAP_PRIVATE, -1, 0).unwrap();
/// let mem = mmap_anonymous(None, one_k_non_zero, ProtFlags::PROT_NONE, MapFlags::MAP_PRIVATE)
/// .unwrap();
/// mprotect(mem, ONE_K, ProtFlags::PROT_READ | ProtFlags::PROT_WRITE).unwrap();
/// std::slice::from_raw_parts_mut(mem as *mut u8, ONE_K)
/// std::slice::from_raw_parts_mut(mem.as_ptr().cast(), ONE_K)
/// };
/// assert_eq!(slice[0], 0x00);
/// slice[0] = 0xFF;
/// assert_eq!(slice[0], 0xFF);
/// ```
pub unsafe fn mprotect(
addr: *mut c_void,
addr: NonNull<c_void>,
length: size_t,
prot: ProtFlags,
) -> Result<()> {
Errno::result(libc::mprotect(addr, length, prot.bits())).map(drop)
unsafe {
Errno::result(libc::mprotect(addr.as_ptr(), length, prot.bits()))
.map(drop)
}
}
/// synchronize a mapped region
@@ -548,14 +608,17 @@ pub unsafe fn mprotect(
///
/// [`msync(2)`]: https://man7.org/linux/man-pages/man2/msync.2.html
pub unsafe fn msync(
addr: *mut c_void,
addr: NonNull<c_void>,
length: size_t,
flags: MsFlags,
) -> Result<()> {
Errno::result(libc::msync(addr, length, flags.bits())).map(drop)
unsafe {
Errno::result(libc::msync(addr.as_ptr(), length, flags.bits()))
.map(drop)
}
}
#[cfg(not(any(target_os = "android", target_env = "ohos")))]
#[cfg(not(target_os = "android"))]
feature! {
#![feature = "fs"]
/// Creates and opens a new, or opens an existing, POSIX shared memory object.
@@ -567,21 +630,26 @@ pub fn shm_open<P>(
name: &P,
flag: OFlag,
mode: Mode
) -> Result<RawFd>
) -> Result<std::os::unix::io::OwnedFd>
where P: ?Sized + NixPath
{
use std::os::unix::io::{FromRawFd, OwnedFd};
let ret = name.with_nix_path(|cstr| {
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[cfg(apple_targets)]
unsafe {
libc::shm_open(cstr.as_ptr(), flag.bits(), mode.bits() as libc::c_uint)
}
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
#[cfg(not(apple_targets))]
unsafe {
libc::shm_open(cstr.as_ptr(), flag.bits(), mode.bits() as libc::mode_t)
}
})?;
Errno::result(ret)
match ret {
-1 => Err(Errno::last()),
fd => Ok(unsafe{ OwnedFd::from_raw_fd(fd) })
}
}
}
@@ -590,7 +658,7 @@ pub fn shm_open<P>(
/// For more information, see [`shm_unlink(3)`].
///
/// [`shm_unlink(3)`]: https://man7.org/linux/man-pages/man3/shm_unlink.3.html
#[cfg(not(any(target_os = "android", target_env = "ohos")))]
#[cfg(not(target_os = "android"))]
pub fn shm_unlink<P: ?Sized + NixPath>(name: &P) -> Result<()> {
let ret =
name.with_nix_path(|cstr| unsafe { libc::shm_unlink(cstr.as_ptr()) })?;
+41 -60
View File
@@ -1,10 +1,11 @@
//! Mostly platform-specific functionality
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
all(target_os = "linux", not(target_env = "uclibc")),
target_os = "macos",
freebsdlike,
all(
target_os = "linux",
not(any(target_env = "uclibc", target_env = "ohos"))
),
apple_targets,
target_os = "netbsd"
))]
feature! {
@@ -15,48 +16,42 @@ feature! {
feature! {
#![feature = "event"]
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub mod epoll;
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"))]
#[allow(missing_docs)]
#[cfg(bsd)]
pub mod event;
#[cfg(any(target_os = "android", target_os = "linux"))]
#[allow(missing_docs)]
/// Event file descriptor.
#[cfg(any(linux_android, target_os = "freebsd"))]
pub mod eventfd;
}
#[cfg(target_os = "linux")]
feature! {
#![feature = "fanotify"]
pub mod fanotify;
}
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "linux",
bsd,
linux_android,
solarish,
target_os = "fuchsia",
target_os = "redox",
target_os = "macos",
target_os = "netbsd",
target_os = "illumos",
target_os = "openbsd"
))]
#[cfg(feature = "ioctl")]
#[cfg_attr(docsrs, doc(cfg(feature = "ioctl")))]
#[macro_use]
pub mod ioctl;
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
#[cfg(any(linux_android, target_os = "freebsd"))]
feature! {
#![feature = "fs"]
pub mod memfd;
}
#[cfg(not(target_os = "redox"))]
feature! {
#![feature = "mman"]
pub mod mman;
@@ -68,20 +63,18 @@ feature! {
pub mod personality;
}
#[cfg(target_os = "linux")]
feature! {
#![feature = "process"]
pub mod prctl;
}
feature! {
#![feature = "pthread"]
pub mod pthread;
}
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"
))]
#[cfg(any(linux_android, bsd))]
feature! {
#![feature = "ptrace"]
#[allow(missing_docs)]
@@ -94,7 +87,7 @@ feature! {
pub mod quota;
}
#[cfg(target_os = "linux")]
#[cfg(any(target_os = "linux", netbsdlike))]
feature! {
#![feature = "reboot"]
pub mod reboot;
@@ -103,7 +96,7 @@ feature! {
#[cfg(not(any(
target_os = "redox",
target_os = "fuchsia",
target_os = "illumos",
solarish,
target_os = "haiku"
)))]
feature! {
@@ -111,20 +104,12 @@ feature! {
pub mod resource;
}
#[cfg(not(target_os = "redox"))]
feature! {
#![feature = "poll"]
pub mod select;
}
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "linux",
target_os = "macos"
))]
#[cfg(any(linux_android, freebsdlike, apple_targets, solarish))]
feature! {
#![feature = "zerocopy"]
pub mod sendfile;
@@ -132,14 +117,13 @@ feature! {
pub mod signal;
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(linux_android)]
feature! {
#![feature = "signal"]
#[allow(missing_docs)]
pub mod signalfd;
}
#[cfg(not(target_os = "redox"))]
feature! {
#![feature = "socket"]
#[allow(missing_docs)]
@@ -153,13 +137,11 @@ feature! {
}
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "linux",
target_os = "macos",
target_os = "openbsd"
linux_android,
freebsdlike,
apple_targets,
target_os = "openbsd",
target_os = "cygwin"
))]
feature! {
#![feature = "fs"]
@@ -171,8 +153,7 @@ feature! {
pub mod statvfs;
}
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub mod sysinfo;
@@ -200,13 +181,13 @@ feature! {
pub mod wait;
}
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(linux_android)]
feature! {
#![feature = "inotify"]
pub mod inotify;
}
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(linux_android)]
feature! {
#![feature = "time"]
pub mod timerfd;
@@ -215,7 +196,7 @@ feature! {
#[cfg(all(
any(
target_os = "freebsd",
target_os = "illumos",
solarish,
target_os = "linux",
target_os = "netbsd"
),
+2 -4
View File
@@ -20,8 +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 = "ohos", target_env = "uclibc")))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(not(any(target_env = "musl", target_env = "uclibc", target_env = "ohos")))]
FDPIC_FUNCPTRS;
/// Map page 0 as read-only.
MMAP_PAGE_ZERO;
@@ -42,8 +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 = "ohos", target_env = "uclibc")))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(not(any(target_env = "musl", target_env = "uclibc", target_env = "ohos")))]
UNAME26;
/// No effects.
WHOLE_SECONDS;
+228
View File
@@ -0,0 +1,228 @@
//! prctl is a Linux-only API for performing operations on a process or thread.
//!
//! Note that careless use of some prctl() operations can confuse the user-space run-time
//! environment, so these operations should be used with care.
//!
//! For more documentation, please read [prctl(2)](https://man7.org/linux/man-pages/man2/prctl.2.html).
use crate::errno::Errno;
use crate::sys::signal::Signal;
use crate::Result;
use libc::{c_int, c_ulong, c_void};
use std::convert::TryFrom;
use std::ffi::{CStr, CString};
use std::num::NonZeroUsize;
use std::ptr::NonNull;
libc_enum! {
/// The type of hardware memory corruption kill policy for the thread.
#[repr(i32)]
#[non_exhaustive]
#[allow(non_camel_case_types)]
pub enum PrctlMCEKillPolicy {
/// The thread will receive SIGBUS as soon as a memory corruption is detected.
PR_MCE_KILL_EARLY,
/// The process is killed only when it accesses a corrupted page.
PR_MCE_KILL_LATE,
/// Uses the system-wide default.
PR_MCE_KILL_DEFAULT,
}
impl TryFrom<i32>
}
fn prctl_set_bool(option: c_int, status: bool) -> Result<()> {
let res = unsafe { libc::prctl(option, status as c_ulong, 0, 0, 0) };
Errno::result(res).map(drop)
}
fn prctl_get_bool(option: c_int) -> Result<bool> {
let res = unsafe { libc::prctl(option, 0, 0, 0, 0) };
Errno::result(res).map(|res| res != 0)
}
/// Set the "child subreaper" attribute for this process
pub fn set_child_subreaper(attribute: bool) -> Result<()> {
prctl_set_bool(libc::PR_SET_CHILD_SUBREAPER, attribute)
}
/// Get the "child subreaper" attribute for this process
pub fn get_child_subreaper() -> Result<bool> {
// prctl writes into this var
let mut subreaper: c_int = 0;
let res = unsafe {
libc::prctl(libc::PR_GET_CHILD_SUBREAPER, &mut subreaper, 0, 0, 0)
};
Errno::result(res).map(|_| subreaper != 0)
}
/// Set the dumpable attribute which determines if core dumps are created for this process.
pub fn set_dumpable(attribute: bool) -> Result<()> {
prctl_set_bool(libc::PR_SET_DUMPABLE, attribute)
}
/// Get the dumpable attribute for this process.
pub fn get_dumpable() -> Result<bool> {
prctl_get_bool(libc::PR_GET_DUMPABLE)
}
/// Set the "keep capabilities" attribute for this process. This causes the thread to retain
/// capabilities even if it switches its UID to a nonzero value.
pub fn set_keepcaps(attribute: bool) -> Result<()> {
prctl_set_bool(libc::PR_SET_KEEPCAPS, attribute)
}
/// Get the "keep capabilities" attribute for this process
pub fn get_keepcaps() -> Result<bool> {
prctl_get_bool(libc::PR_GET_KEEPCAPS)
}
/// Clear the thread memory corruption kill policy and use the system-wide default
pub fn clear_mce_kill() -> Result<()> {
let res = unsafe {
libc::prctl(libc::PR_MCE_KILL, libc::PR_MCE_KILL_CLEAR, 0, 0, 0)
};
Errno::result(res).map(drop)
}
/// Set the thread memory corruption kill policy
pub fn set_mce_kill(policy: PrctlMCEKillPolicy) -> Result<()> {
let res = unsafe {
libc::prctl(
libc::PR_MCE_KILL,
libc::PR_MCE_KILL_SET,
policy as c_ulong,
0,
0,
)
};
Errno::result(res).map(drop)
}
/// Get the thread memory corruption kill policy
pub fn get_mce_kill() -> Result<PrctlMCEKillPolicy> {
let res = unsafe { libc::prctl(libc::PR_MCE_KILL_GET, 0, 0, 0, 0) };
match Errno::result(res) {
Ok(val) => Ok(PrctlMCEKillPolicy::try_from(val)?),
Err(e) => Err(e),
}
}
/// Set the parent-death signal of the calling process. This is the signal that the calling process
/// will get when its parent dies.
pub fn set_pdeathsig<T: Into<Option<Signal>>>(signal: T) -> Result<()> {
let sig = match signal.into() {
Some(s) => s as c_int,
None => 0,
};
let res = unsafe { libc::prctl(libc::PR_SET_PDEATHSIG, sig, 0, 0, 0) };
Errno::result(res).map(drop)
}
/// Returns the current parent-death signal
pub fn get_pdeathsig() -> Result<Option<Signal>> {
// prctl writes into this var
let mut sig: c_int = 0;
let res = unsafe { libc::prctl(libc::PR_GET_PDEATHSIG, &mut sig, 0, 0, 0) };
match Errno::result(res) {
Ok(_) => Ok(match sig {
0 => None,
_ => Some(Signal::try_from(sig)?),
}),
Err(e) => Err(e),
}
}
/// Set the name of the calling thread. Strings longer than 15 bytes will be truncated.
pub fn set_name(name: &CStr) -> Result<()> {
let res = unsafe { libc::prctl(libc::PR_SET_NAME, name.as_ptr(), 0, 0, 0) };
Errno::result(res).map(drop)
}
/// Return the name of the calling thread
pub fn get_name() -> Result<CString> {
// Size of buffer determined by linux/sched.h TASK_COMM_LEN
let buf = [0u8; 16];
let res = unsafe { libc::prctl(libc::PR_GET_NAME, &buf, 0, 0, 0) };
Errno::result(res).and_then(|_| {
CStr::from_bytes_until_nul(&buf)
.map(CStr::to_owned)
.map_err(|_| Errno::EINVAL)
})
}
/// 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: c_ulong) -> Result<()> {
let res = unsafe { libc::prctl(libc::PR_SET_TIMERSLACK, ns, 0, 0, 0) };
Errno::result(res).map(drop)
}
/// Get the timerslack for the calling thread.
pub fn get_timerslack() -> Result<i32> {
let res = unsafe { libc::prctl(libc::PR_GET_TIMERSLACK, 0, 0, 0, 0) };
Errno::result(res)
}
/// Disable all performance counters attached to the calling process.
pub fn task_perf_events_disable() -> Result<()> {
let res =
unsafe { libc::prctl(libc::PR_TASK_PERF_EVENTS_DISABLE, 0, 0, 0, 0) };
Errno::result(res).map(drop)
}
/// Enable all performance counters attached to the calling process.
pub fn task_perf_events_enable() -> Result<()> {
let res =
unsafe { libc::prctl(libc::PR_TASK_PERF_EVENTS_ENABLE, 0, 0, 0, 0) };
Errno::result(res).map(drop)
}
/// Set the calling threads "no new privs" attribute. Once set this option can not be unset.
pub fn set_no_new_privs() -> Result<()> {
prctl_set_bool(libc::PR_SET_NO_NEW_PRIVS, true) // Cannot be unset
}
/// Get the "no new privs" attribute for the calling thread.
pub fn get_no_new_privs() -> Result<bool> {
prctl_get_bool(libc::PR_GET_NO_NEW_PRIVS)
}
/// Set the state of the "THP disable" flag for the calling thread. Setting this disables
/// transparent huge pages.
pub fn set_thp_disable(flag: bool) -> Result<()> {
prctl_set_bool(libc::PR_SET_THP_DISABLE, flag)
}
/// Get the "THP disable" flag for the calling thread.
pub fn get_thp_disable() -> Result<bool> {
prctl_get_bool(libc::PR_GET_THP_DISABLE)
}
/// Set an identifier (or reset it) to the address memory range.
pub fn set_vma_anon_name(addr: NonNull<c_void>, length: NonZeroUsize, name: Option<&CStr>) -> Result<()> {
let nameref = match name {
Some(n) => n.as_ptr(),
_ => std::ptr::null()
};
let res = unsafe { libc::prctl(libc::PR_SET_VMA, libc::PR_SET_VMA_ANON_NAME, addr.as_ptr(), length, nameref) };
Errno::result(res).map(drop)
}
+17 -25
View File
@@ -9,10 +9,7 @@ use std::ptr;
pub type RequestType = c_int;
cfg_if! {
if #[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "macos",
target_os = "openbsd"))] {
if #[cfg(any(freebsdlike, apple_targets, target_os = "openbsd"))] {
#[doc(hidden)]
pub type AddressType = *mut ::libc::c_char;
} else {
@@ -29,33 +26,26 @@ libc_enum! {
PT_TRACE_ME,
PT_READ_I,
PT_READ_D,
#[cfg(target_os = "macos")]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(apple_targets)]
PT_READ_U,
PT_WRITE_I,
PT_WRITE_D,
#[cfg(target_os = "macos")]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(apple_targets)]
PT_WRITE_U,
PT_CONTINUE,
PT_KILL,
#[cfg(any(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "macos"),
#[cfg(any(any(freebsdlike, apple_targets),
all(target_os = "openbsd", target_arch = "x86_64"),
all(target_os = "netbsd", any(target_arch = "x86_64",
target_arch = "powerpc"))))]
PT_STEP,
PT_ATTACH,
PT_DETACH,
#[cfg(target_os = "macos")]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(apple_targets)]
PT_SIGEXC,
#[cfg(target_os = "macos")]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(apple_targets)]
PT_THUPDATE,
#[cfg(target_os = "macos")]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(apple_targets)]
PT_ATTACHEXC
}
}
@@ -66,13 +56,15 @@ unsafe fn ptrace_other(
addr: AddressType,
data: c_int,
) -> Result<c_int> {
Errno::result(libc::ptrace(
request as RequestType,
libc::pid_t::from(pid),
addr,
data,
))
.map(|_| 0)
unsafe {
Errno::result(libc::ptrace(
request as RequestType,
libc::pid_t::from(pid),
addr,
data,
))
.map(|_| 0)
}
}
/// Sets the process as traceable, as with `ptrace(PT_TRACEME, ...)`
@@ -157,7 +149,7 @@ pub fn kill(pid: Pid) -> Result<()> {
/// }
/// ```
#[cfg(any(
any(target_os = "dragonfly", target_os = "freebsd", target_os = "macos"),
any(freebsdlike, apple_targets),
all(target_os = "openbsd", target_arch = "x86_64"),
all(
target_os = "netbsd",
+313 -56
View File
@@ -14,11 +14,12 @@ pub type AddressType = *mut ::libc::c_void;
target_os = "linux",
any(
all(
target_arch = "x86_64",
any(target_env = "gnu", target_env = "musl", target_env = "ohos")
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 = "x86", target_env = "gnu"),
all(target_arch = "riscv64", target_env = "gnu"),
),
))]
use libc::user_regs_struct;
@@ -35,8 +36,8 @@ cfg_if! {
}
libc_enum! {
#[cfg_attr(not(any(target_env = "musl", target_env = "ohos", target_env = "uclibc", target_os = "android")), repr(u32))]
#[cfg_attr(any(target_env = "musl", target_env = "ohos", 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,7 +55,9 @@ libc_enum! {
all(target_os = "linux", any(target_env = "musl",
target_env = "ohos",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "x86_64",
target_pointer_width = "32"))))]
PTRACE_GETREGS,
@@ -62,7 +65,9 @@ libc_enum! {
all(target_os = "linux", any(target_env = "musl",
target_env = "ohos",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "x86_64",
target_pointer_width = "32"))))]
PTRACE_SETREGS,
@@ -70,7 +75,9 @@ libc_enum! {
all(target_os = "linux", any(target_env = "musl",
target_env = "ohos",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "x86_64",
target_pointer_width = "32"))))]
PTRACE_GETFPREGS,
@@ -78,7 +85,9 @@ libc_enum! {
all(target_os = "linux", any(target_env = "musl",
target_env = "ohos",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "x86_64",
target_pointer_width = "32"))))]
PTRACE_SETFPREGS,
@@ -87,14 +96,18 @@ libc_enum! {
#[cfg(all(target_os = "linux", any(target_env = "musl",
target_env = "ohos",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "x86",
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",
target_arch = "mips64r6",
target_arch = "x86",
target_arch = "x86_64")))]
PTRACE_SETFPXREGS,
@@ -104,22 +117,28 @@ libc_enum! {
PTRACE_GETSIGINFO,
PTRACE_SETSIGINFO,
#[cfg(all(target_os = "linux", not(any(target_arch = "mips",
target_arch = "mips64"))))]
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"))))]
PTRACE_GETREGSET,
#[cfg(all(target_os = "linux", not(any(target_arch = "mips",
target_arch = "mips64"))))]
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"))))]
PTRACE_SETREGSET,
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(all())))]
PTRACE_SEIZE,
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(all())))]
PTRACE_INTERRUPT,
#[cfg(all(target_os = "linux", not(any(target_arch = "mips",
target_arch = "mips64"))))]
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"))))]
PTRACE_LISTEN,
#[cfg(all(target_os = "linux", not(any(target_arch = "mips",
target_arch = "mips64"))))]
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"))))]
PTRACE_PEEKSIGINFO,
#[cfg(all(target_os = "linux", target_env = "gnu",
any(target_arch = "x86", target_arch = "x86_64")))]
@@ -127,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,
}
}
@@ -158,6 +179,117 @@ libc_enum! {
}
}
#[cfg(all(
target_os = "linux",
any(
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)]
/// Defines a specific register set, as used in `PTRACE_GETREGSET` and `PTRACE_SETREGSET`.
#[non_exhaustive]
pub enum RegisterSetValue {
NT_PRSTATUS,
NT_PRFPREG,
NT_PRPSINFO,
NT_TASKSTRUCT,
NT_AUXV,
}
}
#[cfg(all(
target_os = "linux",
any(
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.
///
/// # Safety
///
/// This trait is marked unsafe, since implementation of the trait must match
/// ptrace's request `VALUE` and return data type `Regs`.
pub unsafe trait RegisterSet {
/// Corresponding type of registers in the kernel.
const VALUE: RegisterSetValue;
/// Struct representing the register space.
type Regs;
}
#[cfg(all(
target_os = "linux",
any(
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 {
use super::*;
#[derive(Debug, Clone, Copy)]
/// General-purpose registers.
pub enum NT_PRSTATUS {}
unsafe impl RegisterSet for NT_PRSTATUS {
const VALUE: RegisterSetValue = RegisterSetValue::NT_PRSTATUS;
type Regs = user_regs_struct;
}
#[derive(Debug, Clone, Copy)]
/// Floating-point registers.
pub enum NT_PRFPREG {}
unsafe impl RegisterSet for NT_PRFPREG {
const VALUE: RegisterSetValue = RegisterSetValue::NT_PRFPREG;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
type Regs = libc::user_fpregs_struct;
#[cfg(target_arch = "aarch64")]
type Regs = libc::user_fpsimd_struct;
#[cfg(target_arch = "riscv64")]
type Regs = libc::__riscv_mc_d_ext_state;
}
}
libc_bitflags! {
/// Ptrace options used in conjunction with the PTRACE_SETOPTIONS request.
/// See `man ptrace` for more details.
@@ -205,12 +337,18 @@ fn ptrace_peek(
}
/// Get user registers, as with `ptrace(PTRACE_GETREGS, ...)`
///
/// Note that since `PTRACE_GETREGS` are not available on all platforms (as in [ptrace(2)]),
/// `ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, ...)` is used instead to achieve the same effect
/// on aarch64 and riscv64.
///
/// [ptrace(2)]: https://www.man7.org/linux/man-pages/man2/ptrace.2.html
#[cfg(all(
target_os = "linux",
any(
all(
target_arch = "x86_64",
any(target_env = "gnu", target_env = "musl", target_env = "ohos")
any(target_env = "gnu", target_env = "musl")
),
all(target_arch = "x86", target_env = "gnu")
)
@@ -219,13 +357,74 @@ pub fn getregs(pid: Pid) -> Result<user_regs_struct> {
ptrace_get_data::<user_regs_struct>(Request::PTRACE_GETREGS, pid)
}
/// Get user registers, as with `ptrace(PTRACE_GETREGS, ...)`
///
/// Note that since `PTRACE_GETREGS` are not available on all platforms (as in [ptrace(2)]),
/// `ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, ...)` is used instead to achieve the same effect
/// on aarch64 and riscv64.
///
/// [ptrace(2)]: https://www.man7.org/linux/man-pages/man2/ptrace.2.html
#[cfg(all(
target_os = "linux",
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)
}
/// Get a particular set of user registers, as with `ptrace(PTRACE_GETREGSET, ...)`
#[cfg(all(
target_os = "linux",
any(
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> {
let request = Request::PTRACE_GETREGSET;
let mut data = mem::MaybeUninit::<S::Regs>::uninit();
let mut iov = libc::iovec {
iov_base: data.as_mut_ptr().cast(),
iov_len: mem::size_of::<S::Regs>(),
};
unsafe {
ptrace_other(
request,
pid,
S::VALUE as i32 as AddressType,
(&mut iov as *mut libc::iovec).cast(),
)?;
};
Ok(unsafe { data.assume_init() })
}
/// Set user registers, as with `ptrace(PTRACE_SETREGS, ...)`
///
/// Note that since `PTRACE_SETREGS` are not available on all platforms (as in [ptrace(2)]),
/// `ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, ...)` is used instead to achieve the same effect
/// on aarch64 and riscv64.
///
/// [ptrace(2)]: https://www.man7.org/linux/man-pages/man2/ptrace.2.html
#[cfg(all(
target_os = "linux",
any(
all(
target_arch = "x86_64",
any(target_env = "gnu", target_env = "musl", target_env = "ohos")
any(target_env = "gnu", target_env = "musl")
),
all(target_arch = "x86", target_env = "gnu")
)
@@ -236,24 +435,77 @@ pub fn setregs(pid: Pid, regs: user_regs_struct) -> Result<()> {
Request::PTRACE_SETREGS as RequestType,
libc::pid_t::from(pid),
ptr::null_mut::<c_void>(),
&regs as *const _ as *const c_void,
&regs as *const user_regs_struct as *const c_void,
)
};
Errno::result(res).map(drop)
}
/// Set user registers, as with `ptrace(PTRACE_SETREGS, ...)`
///
/// Note that since `PTRACE_SETREGS` are not available on all platforms (as in [ptrace(2)]),
/// `ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, ...)` is used instead to achieve the same effect
/// on aarch64 and riscv64.
///
/// [ptrace(2)]: https://www.man7.org/linux/man-pages/man2/ptrace.2.html
#[cfg(all(
target_os = "linux",
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)
}
/// Set a particular set of user registers, as with `ptrace(PTRACE_SETREGSET, ...)`
#[cfg(all(
target_os = "linux",
any(
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<()> {
let mut iov = libc::iovec {
iov_base: (&mut regs as *mut S::Regs).cast(),
iov_len: mem::size_of::<S::Regs>(),
};
unsafe {
ptrace_other(
Request::PTRACE_SETREGSET,
pid,
S::VALUE as i32 as AddressType,
(&mut iov as *mut libc::iovec).cast(),
)?;
}
Ok(())
}
/// Function for ptrace requests that return values from the data field.
/// Some ptrace get requests populate structs or larger elements than `c_long`
/// and therefore use the data field to return values. This function handles these
/// requests.
fn ptrace_get_data<T>(request: Request, pid: Pid) -> Result<T> {
let mut data = mem::MaybeUninit::uninit();
let mut data = mem::MaybeUninit::<T>::uninit();
let res = unsafe {
libc::ptrace(
request as RequestType,
libc::pid_t::from(pid),
ptr::null_mut::<T>(),
data.as_mut_ptr() as *const _ as *const c_void,
data.as_mut_ptr(),
)
};
Errno::result(res)?;
@@ -266,16 +518,18 @@ unsafe fn ptrace_other(
addr: AddressType,
data: *mut c_void,
) -> Result<c_long> {
Errno::result(libc::ptrace(
request as RequestType,
libc::pid_t::from(pid),
addr,
data,
))
.map(|_| 0)
unsafe {
Errno::result(libc::ptrace(
request as RequestType,
libc::pid_t::from(pid),
addr,
data,
))
.map(|_| 0)
}
}
/// Set options, as with `ptrace(PTRACE_SETOPTIONS,...)`.
/// Set options, as with `ptrace(PTRACE_SETOPTIONS, ...)`.
pub fn setoptions(pid: Pid, options: Options) -> Result<()> {
let res = unsafe {
libc::ptrace(
@@ -288,17 +542,17 @@ pub fn setoptions(pid: Pid, options: Options) -> Result<()> {
Errno::result(res).map(drop)
}
/// Gets a ptrace event as described by `ptrace(PTRACE_GETEVENTMSG,...)`
/// Gets a ptrace event as described by `ptrace(PTRACE_GETEVENTMSG, ...)`
pub fn getevent(pid: Pid) -> Result<c_long> {
ptrace_get_data::<c_long>(Request::PTRACE_GETEVENTMSG, pid)
}
/// Get siginfo as with `ptrace(PTRACE_GETSIGINFO,...)`
/// Get siginfo as with `ptrace(PTRACE_GETSIGINFO, ...)`
pub fn getsiginfo(pid: Pid) -> Result<siginfo_t> {
ptrace_get_data::<siginfo_t>(Request::PTRACE_GETSIGINFO, pid)
}
/// Set siginfo as with `ptrace(PTRACE_SETSIGINFO,...)`
/// Set siginfo as with `ptrace(PTRACE_SETSIGINFO, ...)`
pub fn setsiginfo(pid: Pid, sig: &siginfo_t) -> Result<()> {
let ret = unsafe {
Errno::clear();
@@ -315,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.
@@ -387,7 +648,6 @@ pub fn attach(pid: Pid) -> Result<()> {
///
/// Attaches to the process specified in pid, making it a tracee of the calling process.
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn seize(pid: Pid, options: Options) -> Result<()> {
unsafe {
ptrace_other(
@@ -434,7 +694,6 @@ pub fn cont<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
///
/// This request is equivalent to `ptrace(PTRACE_INTERRUPT, ...)`
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn interrupt(pid: Pid) -> Result<()> {
unsafe {
ptrace_other(
@@ -523,42 +782,40 @@ pub fn sysemu_step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
}
}
/// Reads a word from a processes memory at the given address
/// Reads a word from a processes memory at the given address, as with
/// ptrace(PTRACE_PEEKDATA, ...)
pub fn read(pid: Pid, addr: AddressType) -> Result<c_long> {
ptrace_peek(Request::PTRACE_PEEKDATA, pid, addr, ptr::null_mut())
}
/// Writes a word into the processes memory at the given address
///
/// # Safety
///
/// The `data` argument is passed directly to `ptrace(2)`. Read that man page
/// for guidance.
pub unsafe fn write(
pid: Pid,
addr: AddressType,
data: *mut c_void,
) -> Result<()> {
ptrace_other(Request::PTRACE_POKEDATA, pid, addr, data).map(drop)
/// Writes a word into the processes memory at the given address, as with
/// ptrace(PTRACE_POKEDATA, ...)
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn write(pid: Pid, addr: AddressType, data: c_long) -> Result<()> {
unsafe {
// Safety(not_unsafe_ptr_arg_deref):
// `ptrace_other` is a common abstract
// but in `PTRACE_POKEDATA` situation, `data` is exactly what will be wtitten
ptrace_other(Request::PTRACE_POKEDATA, pid, addr, data as *mut c_void)
.map(drop)
}
}
/// Reads a word from a user area at `offset`.
/// Reads a word from a user area at `offset`, as with ptrace(PTRACE_PEEKUSER, ...).
/// The user struct definition can be found in `/usr/include/sys/user.h`.
pub fn read_user(pid: Pid, offset: AddressType) -> Result<c_long> {
ptrace_peek(Request::PTRACE_PEEKUSER, pid, offset, ptr::null_mut())
}
/// Writes a word to a user area at `offset`.
/// Writes a word to a user area at `offset`, as with ptrace(PTRACE_POKEUSER, ...).
/// The user struct definition can be found in `/usr/include/sys/user.h`.
///
/// # Safety
///
/// The `data` argument is passed directly to `ptrace(2)`. Read that man page
/// for guidance.
pub unsafe fn write_user(
pid: Pid,
offset: AddressType,
data: *mut c_void,
) -> Result<()> {
ptrace_other(Request::PTRACE_POKEUSER, pid, offset, data).map(drop)
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn write_user(pid: Pid, offset: AddressType, data: c_long) -> Result<()> {
unsafe {
// Safety(not_unsafe_ptr_arg_deref):
// `ptrace_other` is a common abstract
// but in `PTRACE_POKEDATA` situation, `data` is exactly what will be wtitten
ptrace_other(Request::PTRACE_POKEUSER, pid, offset, data as *mut c_void)
.map(drop)
}
}
+4 -16
View File
@@ -1,25 +1,13 @@
//! Provides helpers for making ptrace system calls
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(linux_android)]
mod linux;
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(linux_android)]
pub use self::linux::*;
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"
))]
#[cfg(bsd)]
mod bsd;
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"
))]
#[cfg(bsd)]
pub use self::bsd::*;
+4 -5
View File
@@ -21,9 +21,8 @@ use std::{mem, ptr};
struct QuotaCmd(QuotaSubCmd, QuotaType);
impl QuotaCmd {
#[allow(unused_unsafe)]
fn as_int(&self) -> c_int {
unsafe { libc::QCMD(self.0 as i32, self.1 as i32) }
libc::QCMD(self.0 as i32, self.1 as i32)
}
}
@@ -265,7 +264,7 @@ pub fn quotactl_on<P: ?Sized + NixPath>(
) -> Result<()> {
quota_file.with_nix_path(|path| {
let mut path_copy = path.to_bytes_with_nul().to_owned();
let p: *mut c_char = path_copy.as_mut_ptr() as *mut c_char;
let p: *mut c_char = path_copy.as_mut_ptr().cast();
quotactl(
QuotaCmd(QuotaSubCmd::Q_QUOTAON, which),
Some(special),
@@ -309,12 +308,12 @@ pub fn quotactl_get<P: ?Sized + NixPath>(
special: &P,
id: c_int,
) -> Result<Dqblk> {
let mut dqblk = mem::MaybeUninit::uninit();
let mut dqblk = mem::MaybeUninit::<libc::dqblk>::uninit();
quotactl(
QuotaCmd(QuotaSubCmd::Q_GETQUOTA, which),
Some(special),
id,
dqblk.as_mut_ptr() as *mut c_char,
dqblk.as_mut_ptr().cast(),
)?;
Ok(unsafe { Dqblk(dqblk.assume_init()) })
}
+133 -40
View File
@@ -1,48 +1,141 @@
//! Reboot/shutdown or enable/disable Ctrl-Alt-Delete.
//! Reboot/shutdown
//!
//! On Linux, This can also be used to enable/disable Ctrl-Alt-Delete.
use crate::errno::Errno;
use crate::Result;
use cfg_if::cfg_if;
use std::convert::Infallible;
use std::mem::drop;
libc_enum! {
/// How exactly should the system be rebooted.
///
/// See [`set_cad_enabled()`](fn.set_cad_enabled.html) for
/// enabling/disabling Ctrl-Alt-Delete.
#[repr(i32)]
#[non_exhaustive]
pub enum RebootMode {
/// Halt the system.
RB_HALT_SYSTEM,
/// Execute a kernel that has been loaded earlier with
/// [`kexec_load(2)`](https://man7.org/linux/man-pages/man2/kexec_load.2.html).
RB_KEXEC,
/// Stop the system and switch off power, if possible.
RB_POWER_OFF,
/// Restart the system.
RB_AUTOBOOT,
// we do not support Restart2.
/// Suspend the system using software suspend.
RB_SW_SUSPEND,
cfg_if! {
if #[cfg(target_os = "linux")] {
use std::mem::drop;
libc_enum! {
/// How exactly should the system be rebooted.
///
/// See [`set_cad_enabled()`](fn.set_cad_enabled.html) for
/// enabling/disabling Ctrl-Alt-Delete.
#[repr(i32)]
#[non_exhaustive]
pub enum RebootMode {
/// Halt the system.
RB_HALT_SYSTEM,
/// Execute a kernel that has been loaded earlier with
/// [`kexec_load(2)`](https://man7.org/linux/man-pages/man2/kexec_load.2.html).
RB_KEXEC,
/// Stop the system and switch off power, if possible.
RB_POWER_OFF,
/// Restart the system.
RB_AUTOBOOT,
// we do not support Restart2.
/// Suspend the system using software suspend.
RB_SW_SUSPEND,
}
}
/// Reboots or shuts down the system.
pub fn reboot(how: RebootMode) -> Result<Infallible> {
unsafe { libc::reboot(how as libc::c_int) };
Err(Errno::last())
}
/// Enable or disable the reboot keystroke (Ctrl-Alt-Delete).
///
/// Corresponds to calling `reboot(RB_ENABLE_CAD)` or `reboot(RB_DISABLE_CAD)` in C.
pub fn set_cad_enabled(enable: bool) -> Result<()> {
let cmd = if enable {
libc::RB_ENABLE_CAD
} else {
libc::RB_DISABLE_CAD
};
let res = unsafe { libc::reboot(cmd) };
Errno::result(res).map(drop)
}
} else if #[cfg(netbsdlike)] {
use libc::c_int;
libc_bitflags! {
/// How exactly should the system be rebooted.
pub struct RebootMode: c_int {
/// The default, causing the system to reboot in its usual fashion.
RB_AUTOBOOT;
/// Interpreted by the bootstrap program itself, causing it to
/// prompt on the console as to what file should be booted.
/// Normally, the system is booted from the file “xx(0,0)bsd”,
/// where xx is the default disk name, without prompting for
/// the file name.
RB_ASKNAME;
/// Dump kernel memory before rebooting; see `savecore(8)` for
/// more information.
RB_DUMP;
/// The processor is simply halted; no reboot takes place.
RB_HALT;
/// Power off the system if the system hardware supports the
/// function, otherwise it has no effect.
///
/// Should be used in conjunction with `RB_HALT`.
RB_POWERDOWN;
/// By default, the system will halt if `reboot()` is called during
/// startup (before the system has finished autoconfiguration), even
/// if `RB_HALT` is not specified. This is because `panic(9)`s
/// during startup will probably just repeat on the next boot.
/// Use of this option implies that the user has requested the
/// action specified (for example, using the `ddb(4)` boot reboot
/// command), so the system will reboot if a halt is not explicitly
/// requested.
#[cfg(target_os = "openbsd")]
RB_USERREQ;
/// Load the symbol table and enable a built-in debugger in the
/// system. This option will have no useful function if the kernel
/// is not configured for debugging. Several other options have
/// different meaning if combined with this option, although their
/// use may not be possible via the `reboot()` call. See `ddb(4)` for
/// more information.
RB_KDB;
/// Normally, the disks are sync'd (see `sync(8)`) before the
/// processor is halted or rebooted. This option may be useful
/// if file system changes have been made manually or if the
/// processor is on fire.
RB_NOSYNC;
/// Normally, the reboot procedure involves an automatic disk
/// consistency check and then multi-user operations. `RB_SINGLE`
/// prevents this, booting the system with a single-user shell on
/// the console. `RB_SINGLE` is actually interpreted by the `init(8)`
/// program in the newly booted system.
///
/// When no options are given (i.e., `RB_AUTOBOOT` is used), the
/// system is rebooted from file /bsd in the root file system of
/// unit 0 of a disk chosen in a processor specific way. An automatic
/// consistency check of the disks is normally performed (see `fsck(8)`).
RB_SINGLE;
/// Initially invoke the `userconf(4)` facility when the system
/// starts up again, if it has been compiled into the kernel
/// that is loaded.
#[cfg(target_os = "netbsd")]
RB_USERCONF;
/// Don't update the hardware clock from the system clock, presumably
/// because the system clock is suspect.
#[cfg(target_os = "openbsd")]
RB_TIMEBAD;
}
}
/// Reboot system or halt processor
///
/// For more information, see the man pages:
///
/// * [NetBSD](https://man.netbsd.org/reboot.2)
/// * [OpenBSD](https://man.openbsd.org/reboot.2)
#[cfg(netbsdlike)]
pub fn reboot(how: RebootMode) -> Result<Infallible> {
#[cfg(target_os = "openbsd")]
unsafe { libc::reboot(how.bits()) };
#[cfg(target_os = "netbsd")]
unsafe { libc::reboot(how.bits(), std::ptr::null_mut()) };
Err(Errno::last())
}
}
}
/// Reboots or shuts down the system.
pub fn reboot(how: RebootMode) -> Result<Infallible> {
unsafe { libc::reboot(how as libc::c_int) };
Err(Errno::last())
}
/// Enable or disable the reboot keystroke (Ctrl-Alt-Delete).
///
/// Corresponds to calling `reboot(RB_ENABLE_CAD)` or `reboot(RB_DISABLE_CAD)` in C.
pub fn set_cad_enabled(enable: bool) -> Result<()> {
let cmd = if enable {
libc::RB_ENABLE_CAD
} else {
libc::RB_DISABLE_CAD
};
let res = unsafe { libc::reboot(cmd) };
Errno::result(res).map(drop)
}
+41 -82
View File
@@ -10,17 +10,17 @@ pub use libc::RLIM_INFINITY;
use std::mem;
cfg_if! {
if #[cfg(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")))]{
if #[cfg(any(
all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")),
target_os = "hurd"
))]{
use libc::{__rlimit_resource_t, rlimit};
} else if #[cfg(any(
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd",
target_os = "macos",
target_os = "ios",
bsd,
target_os = "android",
target_os = "dragonfly",
all(target_os = "linux", not(target_env = "gnu"))
target_os = "aix",
all(target_os = "linux", not(target_env = "gnu")),
target_os = "cygwin"
))]{
use libc::rlimit;
}
@@ -42,21 +42,20 @@ libc_enum! {
//
// https://gcc.gnu.org/legacy-ml/gcc/2015-08/msg00441.html
// https://github.com/rust-lang/libc/blob/master/src/unix/linux_like/linux/gnu/mod.rs
#[cfg_attr(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")), repr(u32))]
#[cfg_attr(any(
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd",
target_os = "macos",
target_os = "ios",
all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")),
target_os = "hurd"
), repr(u32))]
#[cfg_attr(any(
bsd,
target_os = "android",
target_os = "dragonfly",
all(target_os = "linux", not(any(target_env = "gnu", target_env = "uclibc")))
target_os = "aix",
all(target_os = "linux", not(any(target_env = "gnu", target_env = "uclibc"))),
target_os = "cygwin"
), repr(i32))]
#[non_exhaustive]
pub enum Resource {
#[cfg(not(any(target_os = "freebsd", target_os = "netbsd", target_os = "openbsd")))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(not(any(target_os = "freebsd", netbsdlike)))]
/// The maximum amount (in bytes) of virtual memory the process is
/// allowed to map.
RLIMIT_AS,
@@ -75,100 +74,78 @@ libc_enum! {
RLIMIT_STACK,
#[cfg(target_os = "freebsd")]
#[cfg_attr(docsrs, doc(cfg(all())))]
/// The maximum number of kqueues this user id is allowed to create.
RLIMIT_KQUEUES,
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
/// A limit on the combined number of flock locks and fcntl leases that
/// this process may establish.
RLIMIT_LOCKS,
#[cfg(any(
target_os = "android",
target_os = "freebsd",
target_os = "openbsd",
target_os = "linux",
target_os = "netbsd"
))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(any(linux_android, target_os = "freebsd", netbsdlike))]
/// The maximum size (in bytes) which a process may lock into memory
/// using the mlock(2) system call.
RLIMIT_MEMLOCK,
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
/// A limit on the number of bytes that can be allocated for POSIX
/// message queues for the real user ID of the calling process.
RLIMIT_MSGQUEUE,
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
/// A ceiling to which the process's nice value can be raised using
/// setpriority or nice.
RLIMIT_NICE,
#[cfg(any(
target_os = "android",
linux_android,
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "linux",
netbsdlike,
target_os = "aix",
))]
#[cfg_attr(docsrs, doc(cfg(all())))]
/// The maximum number of simultaneous processes for this user id.
RLIMIT_NPROC,
#[cfg(target_os = "freebsd")]
#[cfg_attr(docsrs, doc(cfg(all())))]
/// The maximum number of pseudo-terminals this user id is allowed to
/// create.
RLIMIT_NPTS,
#[cfg(any(target_os = "android",
#[cfg(any(linux_android,
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "linux",
netbsdlike,
target_os = "aix",
))]
#[cfg_attr(docsrs, doc(cfg(all())))]
/// When there is memory pressure and swap is available, prioritize
/// eviction of a process' resident pages beyond this amount (in bytes).
RLIMIT_RSS,
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
/// A ceiling on the real-time priority that may be set for this process
/// using sched_setscheduler and sched_set param.
RLIMIT_RTPRIO,
#[cfg(any(target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
/// A limit (in microseconds) on the amount of CPU time that a process
/// scheduled under a real-time scheduling policy may con sume without
/// making a blocking system call.
RLIMIT_RTTIME,
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
/// A limit on the number of signals that may be queued for the real
/// user ID of the calling process.
RLIMIT_SIGPENDING,
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(freebsdlike)]
/// The maximum size (in bytes) of socket buffer usage for this user.
RLIMIT_SBSIZE,
#[cfg(target_os = "freebsd")]
#[cfg_attr(docsrs, doc(cfg(all())))]
/// The maximum size (in bytes) of the swap space that may be reserved
/// or used by all of this user id's processes.
RLIMIT_SWAP,
#[cfg(target_os = "freebsd")]
#[cfg_attr(docsrs, doc(cfg(all())))]
/// An alias for RLIMIT_AS.
RLIMIT_VMEM,
}
@@ -202,7 +179,10 @@ pub fn getrlimit(resource: Resource) -> Result<(rlim_t, rlim_t)> {
let mut old_rlim = mem::MaybeUninit::<rlimit>::uninit();
cfg_if! {
if #[cfg(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")))]{
if #[cfg(any(
all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")),
target_os = "hurd"
))] {
let res = unsafe { libc::getrlimit(resource as __rlimit_resource_t, old_rlim.as_mut_ptr()) };
} else {
let res = unsafe { libc::getrlimit(resource as c_int, old_rlim.as_mut_ptr()) };
@@ -255,7 +235,10 @@ pub fn setrlimit(
rlim_max: hard_limit,
};
cfg_if! {
if #[cfg(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")))]{
if #[cfg(any(
all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")),
target_os = "hurd",
))]{
let res = unsafe { libc::setrlimit(resource as __rlimit_resource_t, &new_rlim as *const rlimit) };
}else{
let res = unsafe { libc::setrlimit(resource as c_int, &new_rlim as *const rlimit) };
@@ -277,7 +260,6 @@ libc_enum! {
RUSAGE_CHILDREN,
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
/// Resource usage for the calling thread.
RUSAGE_THREAD,
}
@@ -313,7 +295,9 @@ impl Usage {
TimeVal::from(self.0.ru_stime)
}
/// The resident set size at its peak, in kilobytes.
/// The resident set size at its peak,
#[cfg_attr(apple_targets, doc = " in bytes.")]
#[cfg_attr(not(apple_targets), doc = " in kilobytes.")]
pub fn max_rss(&self) -> c_long {
self.0.ru_maxrss
}
@@ -416,28 +400,3 @@ pub fn getrusage(who: UsageWho) -> Result<Usage> {
Errno::result(res).map(|_| Usage(rusage.assume_init()))
}
}
#[cfg(test)]
mod test {
use super::{getrusage, UsageWho};
#[test]
pub fn test_self_cpu_time() {
// Make sure some CPU time is used.
let mut numbers: Vec<i32> = (1..1_000_000).collect();
numbers.iter_mut().for_each(|item| *item *= 2);
// FIXME: this is here to help ensure the compiler does not optimize the whole
// thing away. Replace the assert with test::black_box once stabilized.
assert_eq!(numbers[100..200].iter().sum::<i32>(), 30_100);
let usage = getrusage(UsageWho::RUSAGE_SELF)
.expect("Failed to call getrusage for SELF");
let rusage = usage.as_ref();
let user = usage.user_time();
assert!(user.tv_sec() > 0 || user.tv_usec() > 0);
assert_eq!(user.tv_sec(), rusage.ru_utime.tv_sec);
assert_eq!(user.tv_usec(), rusage.ru_utime.tv_usec);
}
}
+65 -201
View File
@@ -7,7 +7,7 @@ use std::convert::TryFrom;
use std::iter::FusedIterator;
use std::mem;
use std::ops::Range;
use std::os::unix::io::RawFd;
use std::os::unix::io::{AsRawFd, BorrowedFd, RawFd};
use std::ptr::{null, null_mut};
pub use libc::FD_SETSIZE;
@@ -15,7 +15,10 @@ pub use libc::FD_SETSIZE;
/// Contains a set of file descriptors used by [`select`]
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct FdSet(libc::fd_set);
pub struct FdSet<'fd> {
set: libc::fd_set,
_fd: std::marker::PhantomData<BorrowedFd<'fd>>,
}
fn assert_fd_valid(fd: RawFd) {
assert!(
@@ -24,37 +27,40 @@ fn assert_fd_valid(fd: RawFd) {
);
}
impl FdSet {
impl<'fd> FdSet<'fd> {
/// Create an empty `FdSet`
pub fn new() -> FdSet {
pub fn new() -> FdSet<'fd> {
let mut fdset = mem::MaybeUninit::uninit();
unsafe {
libc::FD_ZERO(fdset.as_mut_ptr());
FdSet(fdset.assume_init())
Self {
set: fdset.assume_init(),
_fd: std::marker::PhantomData,
}
}
}
/// Add a file descriptor to an `FdSet`
pub fn insert(&mut self, fd: RawFd) {
assert_fd_valid(fd);
unsafe { libc::FD_SET(fd, &mut self.0) };
pub fn insert(&mut self, fd: BorrowedFd<'fd>) {
assert_fd_valid(fd.as_raw_fd());
unsafe { libc::FD_SET(fd.as_raw_fd(), &mut self.set) };
}
/// Remove a file descriptor from an `FdSet`
pub fn remove(&mut self, fd: RawFd) {
assert_fd_valid(fd);
unsafe { libc::FD_CLR(fd, &mut self.0) };
pub fn remove(&mut self, fd: BorrowedFd<'fd>) {
assert_fd_valid(fd.as_raw_fd());
unsafe { libc::FD_CLR(fd.as_raw_fd(), &mut self.set) };
}
/// Test an `FdSet` for the presence of a certain file descriptor.
pub fn contains(&self, fd: RawFd) -> bool {
assert_fd_valid(fd);
unsafe { libc::FD_ISSET(fd, &self.0) }
pub fn contains(&self, fd: BorrowedFd<'fd>) -> bool {
assert_fd_valid(fd.as_raw_fd());
unsafe { libc::FD_ISSET(fd.as_raw_fd(), &self.set) }
}
/// Remove all file descriptors from this `FdSet`.
pub fn clear(&mut self) {
unsafe { libc::FD_ZERO(&mut self.0) };
unsafe { libc::FD_ZERO(&mut self.set) };
}
/// Finds the highest file descriptor in the set.
@@ -66,15 +72,18 @@ impl FdSet {
/// # Example
///
/// ```
/// # use std::os::unix::io::{AsRawFd, BorrowedFd};
/// # use nix::sys::select::FdSet;
/// let fd_four = unsafe {BorrowedFd::borrow_raw(4)};
/// let fd_nine = unsafe {BorrowedFd::borrow_raw(9)};
/// let mut set = FdSet::new();
/// set.insert(4);
/// set.insert(9);
/// assert_eq!(set.highest(), Some(9));
/// set.insert(fd_four);
/// set.insert(fd_nine);
/// assert_eq!(set.highest().map(|borrowed_fd|borrowed_fd.as_raw_fd()), Some(9));
/// ```
///
/// [`select`]: fn.select.html
pub fn highest(&self) -> Option<RawFd> {
pub fn highest(&self) -> Option<BorrowedFd<'_>> {
self.fds(None).next_back()
}
@@ -88,11 +97,13 @@ impl FdSet {
///
/// ```
/// # use nix::sys::select::FdSet;
/// # use std::os::unix::io::RawFd;
/// # use std::os::unix::io::{AsRawFd, BorrowedFd, RawFd};
/// let mut set = FdSet::new();
/// set.insert(4);
/// set.insert(9);
/// let fds: Vec<RawFd> = set.fds(None).collect();
/// let fd_four = unsafe {BorrowedFd::borrow_raw(4)};
/// let fd_nine = unsafe {BorrowedFd::borrow_raw(9)};
/// set.insert(fd_four);
/// set.insert(fd_nine);
/// let fds: Vec<RawFd> = set.fds(None).map(|borrowed_fd|borrowed_fd.as_raw_fd()).collect();
/// assert_eq!(fds, vec![4, 9]);
/// ```
#[inline]
@@ -104,7 +115,7 @@ impl FdSet {
}
}
impl Default for FdSet {
impl Default for FdSet<'_> {
fn default() -> Self {
Self::new()
}
@@ -112,18 +123,19 @@ impl Default for FdSet {
/// Iterator over `FdSet`.
#[derive(Debug)]
pub struct Fds<'a> {
set: &'a FdSet,
pub struct Fds<'a, 'fd> {
set: &'a FdSet<'fd>,
range: Range<usize>,
}
impl<'a> Iterator for Fds<'a> {
type Item = RawFd;
impl<'fd> Iterator for Fds<'_, 'fd> {
type Item = BorrowedFd<'fd>;
fn next(&mut self) -> Option<RawFd> {
fn next(&mut self) -> Option<Self::Item> {
for i in &mut self.range {
if self.set.contains(i as RawFd) {
return Some(i as RawFd);
let borrowed_i = unsafe { BorrowedFd::borrow_raw(i as RawFd) };
if self.set.contains(borrowed_i) {
return Some(borrowed_i);
}
}
None
@@ -136,19 +148,20 @@ impl<'a> Iterator for Fds<'a> {
}
}
impl<'a> DoubleEndedIterator for Fds<'a> {
impl<'fd> DoubleEndedIterator for Fds<'_, 'fd> {
#[inline]
fn next_back(&mut self) -> Option<RawFd> {
fn next_back(&mut self) -> Option<BorrowedFd<'fd>> {
while let Some(i) = self.range.next_back() {
if self.set.contains(i as RawFd) {
return Some(i as RawFd);
let borrowed_i = unsafe { BorrowedFd::borrow_raw(i as RawFd) };
if self.set.contains(borrowed_i) {
return Some(borrowed_i);
}
}
None
}
}
impl<'a> FusedIterator for Fds<'a> {}
impl FusedIterator for Fds<'_, '_> {}
/// Monitors file descriptors for readiness
///
@@ -173,7 +186,7 @@ impl<'a> FusedIterator for Fds<'a> {}
/// [select(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/select.html)
///
/// [`FdSet::highest`]: struct.FdSet.html#method.highest
pub fn select<'a, N, R, W, E, T>(
pub fn select<'a, 'fd, N, R, W, E, T>(
nfds: N,
readfds: R,
writefds: W,
@@ -181,10 +194,11 @@ pub fn select<'a, N, R, W, E, T>(
timeout: T,
) -> Result<c_int>
where
'fd: 'a,
N: Into<Option<c_int>>,
R: Into<Option<&'a mut FdSet>>,
W: Into<Option<&'a mut FdSet>>,
E: Into<Option<&'a mut FdSet>>,
R: Into<Option<&'a mut FdSet<'fd>>>,
W: Into<Option<&'a mut FdSet<'fd>>>,
E: Into<Option<&'a mut FdSet<'fd>>>,
T: Into<Option<&'a mut TimeVal>>,
{
let mut readfds = readfds.into();
@@ -197,7 +211,11 @@ where
.iter_mut()
.chain(writefds.iter_mut())
.chain(errorfds.iter_mut())
.map(|set| set.highest().unwrap_or(-1))
.map(|set| {
set.highest()
.map(|borrowed_fd| borrowed_fd.as_raw_fd())
.unwrap_or(-1)
})
.max()
.unwrap_or(-1)
+ 1
@@ -256,17 +274,18 @@ use crate::sys::signal::SigSet;
/// [The new pselect() system call](https://lwn.net/Articles/176911/)
///
/// [`FdSet::highest`]: struct.FdSet.html#method.highest
pub fn pselect<'a, N, R, W, E, T, S>(nfds: N,
pub fn pselect<'a, 'fd, N, R, W, E, T, S>(nfds: N,
readfds: R,
writefds: W,
errorfds: E,
timeout: T,
sigmask: S) -> Result<c_int>
where
'fd: 'a,
N: Into<Option<c_int>>,
R: Into<Option<&'a mut FdSet>>,
W: Into<Option<&'a mut FdSet>>,
E: Into<Option<&'a mut FdSet>>,
R: Into<Option<&'a mut FdSet<'fd>>>,
W: Into<Option<&'a mut FdSet<'fd>>>,
E: Into<Option<&'a mut FdSet<'fd>>>,
T: Into<Option<&'a TimeSpec>>,
S: Into<Option<&'a SigSet>>,
{
@@ -280,7 +299,7 @@ where
readfds.iter_mut()
.chain(writefds.iter_mut())
.chain(errorfds.iter_mut())
.map(|set| set.highest().unwrap_or(-1))
.map(|set| set.highest().map(|borrowed_fd|borrowed_fd.as_raw_fd()).unwrap_or(-1))
.max()
.unwrap_or(-1) + 1
});
@@ -298,158 +317,3 @@ where
Errno::result(res)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sys::time::{TimeVal, TimeValLike};
use crate::unistd::{pipe, write};
use std::os::unix::io::RawFd;
#[test]
fn fdset_insert() {
let mut fd_set = FdSet::new();
for i in 0..FD_SETSIZE {
assert!(!fd_set.contains(i as RawFd));
}
fd_set.insert(7);
assert!(fd_set.contains(7));
}
#[test]
fn fdset_remove() {
let mut fd_set = FdSet::new();
for i in 0..FD_SETSIZE {
assert!(!fd_set.contains(i as RawFd));
}
fd_set.insert(7);
fd_set.remove(7);
for i in 0..FD_SETSIZE {
assert!(!fd_set.contains(i as RawFd));
}
}
#[test]
fn fdset_clear() {
let mut fd_set = FdSet::new();
fd_set.insert(1);
fd_set.insert((FD_SETSIZE / 2) as RawFd);
fd_set.insert((FD_SETSIZE - 1) as RawFd);
fd_set.clear();
for i in 0..FD_SETSIZE {
assert!(!fd_set.contains(i as RawFd));
}
}
#[test]
fn fdset_highest() {
let mut set = FdSet::new();
assert_eq!(set.highest(), None);
set.insert(0);
assert_eq!(set.highest(), Some(0));
set.insert(90);
assert_eq!(set.highest(), Some(90));
set.remove(0);
assert_eq!(set.highest(), Some(90));
set.remove(90);
assert_eq!(set.highest(), None);
set.insert(4);
set.insert(5);
set.insert(7);
assert_eq!(set.highest(), Some(7));
}
#[test]
fn fdset_fds() {
let mut set = FdSet::new();
assert_eq!(set.fds(None).collect::<Vec<_>>(), vec![]);
set.insert(0);
assert_eq!(set.fds(None).collect::<Vec<_>>(), vec![0]);
set.insert(90);
assert_eq!(set.fds(None).collect::<Vec<_>>(), vec![0, 90]);
// highest limit
assert_eq!(set.fds(Some(89)).collect::<Vec<_>>(), vec![0]);
assert_eq!(set.fds(Some(90)).collect::<Vec<_>>(), vec![0, 90]);
}
#[test]
fn test_select() {
let (r1, w1) = pipe().unwrap();
write(w1, b"hi!").unwrap();
let (r2, _w2) = pipe().unwrap();
let mut fd_set = FdSet::new();
fd_set.insert(r1);
fd_set.insert(r2);
let mut timeout = TimeVal::seconds(10);
assert_eq!(
1,
select(None, &mut fd_set, None, None, &mut timeout).unwrap()
);
assert!(fd_set.contains(r1));
assert!(!fd_set.contains(r2));
}
#[test]
fn test_select_nfds() {
let (r1, w1) = pipe().unwrap();
write(w1, b"hi!").unwrap();
let (r2, _w2) = pipe().unwrap();
let mut fd_set = FdSet::new();
fd_set.insert(r1);
fd_set.insert(r2);
let mut timeout = TimeVal::seconds(10);
assert_eq!(
1,
select(
Some(fd_set.highest().unwrap() + 1),
&mut fd_set,
None,
None,
&mut timeout
)
.unwrap()
);
assert!(fd_set.contains(r1));
assert!(!fd_set.contains(r2));
}
#[test]
fn test_select_nfds2() {
let (r1, w1) = pipe().unwrap();
write(w1, b"hi!").unwrap();
let (r2, _w2) = pipe().unwrap();
let mut fd_set = FdSet::new();
fd_set.insert(r1);
fd_set.insert(r2);
let mut timeout = TimeVal::seconds(10);
assert_eq!(
1,
select(
::std::cmp::max(r1, r2) + 1,
&mut fd_set,
None,
None,
&mut timeout
)
.unwrap()
);
assert!(fd_set.contains(r1));
assert!(!fd_set.contains(r2));
}
}
+126 -52
View File
@@ -1,7 +1,7 @@
//! Send data from a file to a socket, bypassing userland.
use cfg_if::cfg_if;
use std::os::unix::io::RawFd;
use std::os::unix::io::{AsFd, AsRawFd};
use std::ptr;
use libc::{self, off_t};
@@ -20,19 +20,26 @@ use crate::Result;
///
/// `in_fd` must support `mmap`-like operations and therefore cannot be a socket.
///
/// For more information, see [the sendfile(2) man page.](https://man7.org/linux/man-pages/man2/sendfile.2.html)
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn sendfile(
out_fd: RawFd,
in_fd: RawFd,
/// For more information, see [the sendfile(2) man page.](https://man7.org/linux/man-pages/man2/sendfile.2.html) for Linux,
/// see [the sendfile(2) man page.](https://docs.oracle.com/cd/E88353_01/html/E37843/sendfile-3c.html) for Solaris.
#[cfg(any(linux_android, solarish))]
pub fn sendfile<F1: AsFd, F2: AsFd>(
out_fd: F1,
in_fd: F2,
offset: Option<&mut off_t>,
count: usize,
) -> Result<usize> {
let offset = offset
.map(|offset| offset as *mut _)
.unwrap_or(ptr::null_mut());
let ret = unsafe { libc::sendfile(out_fd, in_fd, offset, count) };
let ret = unsafe {
libc::sendfile(
out_fd.as_fd().as_raw_fd(),
in_fd.as_fd().as_raw_fd(),
offset,
count,
)
};
Errno::result(ret).map(|r| r as usize)
}
@@ -49,61 +56,103 @@ pub fn sendfile(
///
/// For more information, see [the sendfile(2) man page.](https://man7.org/linux/man-pages/man2/sendfile.2.html)
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn sendfile64(
out_fd: RawFd,
in_fd: RawFd,
pub fn sendfile64<F1: AsFd, F2: AsFd>(
out_fd: F1,
in_fd: F2,
offset: Option<&mut libc::off64_t>,
count: usize,
) -> Result<usize> {
let offset = offset
.map(|offset| offset as *mut _)
.unwrap_or(ptr::null_mut());
let ret = unsafe { libc::sendfile64(out_fd, in_fd, offset, count) };
let ret = unsafe {
libc::sendfile64(
out_fd.as_fd().as_raw_fd(),
in_fd.as_fd().as_raw_fd(),
offset,
count,
)
};
Errno::result(ret).map(|r| r as usize)
}
cfg_if! {
if #[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos"))] {
if #[cfg(any(freebsdlike, apple_targets))] {
use std::io::IoSlice;
#[derive(Clone, Debug)]
struct SendfileHeaderTrailer<'a>(
libc::sf_hdtr,
Option<Vec<IoSlice<'a>>>,
Option<Vec<IoSlice<'a>>>,
);
struct SendfileHeaderTrailer<'a> {
raw: libc::sf_hdtr,
_headers: Option<Vec<IoSlice<'a>>>,
_trailers: Option<Vec<IoSlice<'a>>>,
}
impl<'a> SendfileHeaderTrailer<'a> {
fn new(
headers: Option<&'a [&'a [u8]]>,
trailers: Option<&'a [&'a [u8]]>
) -> SendfileHeaderTrailer<'a> {
let header_iovecs: Option<Vec<IoSlice<'_>>> =
let mut header_iovecs: Option<Vec<IoSlice<'_>>> =
headers.map(|s| s.iter().map(|b| IoSlice::new(b)).collect());
let trailer_iovecs: Option<Vec<IoSlice<'_>>> =
let mut trailer_iovecs: Option<Vec<IoSlice<'_>>> =
trailers.map(|s| s.iter().map(|b| IoSlice::new(b)).collect());
SendfileHeaderTrailer(
libc::sf_hdtr {
SendfileHeaderTrailer {
raw: libc::sf_hdtr {
headers: {
header_iovecs
.as_ref()
.map_or(ptr::null(), |v| v.as_ptr()) as *mut libc::iovec
.as_mut()
.map_or(ptr::null_mut(), |v| v.as_mut_ptr())
.cast()
},
hdr_cnt: header_iovecs.as_ref().map(|v| v.len()).unwrap_or(0) as i32,
trailers: {
trailer_iovecs
.as_ref()
.map_or(ptr::null(), |v| v.as_ptr()) as *mut libc::iovec
.as_mut()
.map_or(ptr::null_mut(), |v| v.as_mut_ptr())
.cast()
},
trl_cnt: trailer_iovecs.as_ref().map(|v| v.len()).unwrap_or(0) as i32
},
header_iovecs,
trailer_iovecs,
)
_headers: header_iovecs,
_trailers: trailer_iovecs,
}
}
}
} else if #[cfg(solarish)] {
use std::os::unix::io::BorrowedFd;
use std::marker::PhantomData;
#[derive(Debug, Copy, Clone)]
/// Mapping of the raw C sendfilevec_t struct
pub struct SendfileVec<'fd> {
raw: libc::sendfilevec_t,
phantom: PhantomData<BorrowedFd<'fd>>
}
impl<'fd> SendfileVec<'fd> {
/// initialises SendfileVec to send data directly from the process's address space
/// same in C with sfv_fd set to SFV_FD_SELF.
pub fn newself(
off: off_t,
len: usize
) -> Self {
Self{raw: libc::sendfilevec_t{sfv_fd: libc::SFV_FD_SELF, sfv_flag: 0, sfv_off: off, sfv_len: len}, phantom: PhantomData}
}
/// initialises SendfileVec to send data from `fd`.
pub fn new(
fd: BorrowedFd<'fd>,
off: off_t,
len: usize
) -> SendfileVec<'fd> {
Self{raw: libc::sendfilevec_t{sfv_fd: fd.as_raw_fd(), sfv_flag: 0, sfv_off:off, sfv_len: len}, phantom: PhantomData}
}
}
impl From<SendfileVec<'_>> for libc::sendfilevec_t {
fn from<'fd>(vec: SendfileVec) -> libc::sendfilevec_t {
vec.raw
}
}
}
@@ -156,9 +205,9 @@ cfg_if! {
/// For more information, see
/// [the sendfile(2) man page.](https://www.freebsd.org/cgi/man.cgi?query=sendfile&sektion=2)
#[allow(clippy::too_many_arguments)]
pub fn sendfile(
in_fd: RawFd,
out_sock: RawFd,
pub fn sendfile<F1: AsFd, F2: AsFd>(
in_fd: F1,
out_sock: F2,
offset: off_t,
count: Option<usize>,
headers: Option<&[&[u8]]>,
@@ -173,10 +222,10 @@ cfg_if! {
let flags: u32 = (ra32 << 16) | (flags.bits() as u32);
let mut bytes_sent: off_t = 0;
let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers));
let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.0 as *const libc::sf_hdtr);
let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.raw as *const libc::sf_hdtr);
let return_code = unsafe {
libc::sendfile(in_fd,
out_sock,
libc::sendfile(in_fd.as_fd().as_raw_fd(),
out_sock.as_fd().as_raw_fd(),
offset,
count.unwrap_or(0),
hdtr_ptr as *mut libc::sf_hdtr,
@@ -206,9 +255,9 @@ cfg_if! {
///
/// For more information, see
/// [the sendfile(2) man page.](https://leaf.dragonflybsd.org/cgi/web-man?command=sendfile&section=2)
pub fn sendfile(
in_fd: RawFd,
out_sock: RawFd,
pub fn sendfile<F1: AsFd, F2: AsFd>(
in_fd: F1,
out_sock: F2,
offset: off_t,
count: Option<usize>,
headers: Option<&[&[u8]]>,
@@ -216,10 +265,10 @@ cfg_if! {
) -> (Result<()>, off_t) {
let mut bytes_sent: off_t = 0;
let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers));
let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.0 as *const libc::sf_hdtr);
let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.raw as *const libc::sf_hdtr);
let return_code = unsafe {
libc::sendfile(in_fd,
out_sock,
libc::sendfile(in_fd.as_fd().as_raw_fd(),
out_sock.as_fd().as_raw_fd(),
offset,
count.unwrap_or(0),
hdtr_ptr as *mut libc::sf_hdtr,
@@ -228,7 +277,7 @@ cfg_if! {
};
(Errno::result(return_code).and(Ok(())), bytes_sent)
}
} else if #[cfg(any(target_os = "ios", target_os = "macos"))] {
} else if #[cfg(apple_targets)] {
/// Read bytes from `in_fd` starting at `offset` and write up to `count` bytes to
/// `out_sock`.
///
@@ -252,9 +301,9 @@ cfg_if! {
///
/// For more information, see
/// [the sendfile(2) man page.](https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man2/sendfile.2.html)
pub fn sendfile(
in_fd: RawFd,
out_sock: RawFd,
pub fn sendfile<F1: AsFd, F2: AsFd>(
in_fd: F1,
out_sock: F2,
offset: off_t,
count: Option<off_t>,
headers: Option<&[&[u8]]>,
@@ -262,10 +311,10 @@ cfg_if! {
) -> (Result<()>, off_t) {
let mut len = count.unwrap_or(0);
let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers));
let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.0 as *const libc::sf_hdtr);
let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.raw as *const libc::sf_hdtr);
let return_code = unsafe {
libc::sendfile(in_fd,
out_sock,
libc::sendfile(in_fd.as_fd().as_raw_fd(),
out_sock.as_fd().as_raw_fd(),
offset,
&mut len as *mut off_t,
hdtr_ptr as *mut libc::sf_hdtr,
@@ -273,5 +322,30 @@ cfg_if! {
};
(Errno::result(return_code).and(Ok(())), len)
}
} else if #[cfg(solarish)] {
/// Write data from the vec arrays to `out_sock` and returns a `Result` and a
/// count of bytes written.
///
/// Each `SendfileVec` set needs to be instantiated either with `SendfileVec::new` or
/// `SendfileVec::newself`.
///
/// The former allows to send data from a file descriptor through `fd`,
/// from an offset `off` and for a given amount of data `len`.
///
/// The latter allows to send data from the process's address space, from an offset `off`
/// and for a given amount of data `len`.
///
/// For more information, see
/// [the sendfilev(3) man page.](https://illumos.org/man/3EXT/sendfilev)
pub fn sendfilev<F: AsFd>(
out_sock: F,
vec: &[SendfileVec]
) -> (Result<()>, usize) {
let mut len = 0usize;
let return_code = unsafe {
libc::sendfilev(out_sock.as_fd().as_raw_fd(), vec.as_ptr() as *const libc::sendfilevec_t, vec.len() as i32, &mut len)
};
(Errno::result(return_code).and(Ok(())), len)
}
}
}
+435 -371
View File
File diff suppressed because it is too large Load Diff
+54 -55
View File
@@ -17,12 +17,13 @@
//! signal handlers.
use crate::errno::Errno;
pub use crate::sys::signal::{self, SigSet};
use crate::unistd;
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;
use std::os::unix::io::{AsRawFd, RawFd};
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd};
libc_bitflags! {
pub struct SfdFlags: libc::c_int {
@@ -31,7 +32,6 @@ libc_bitflags! {
}
}
pub const SIGNALFD_NEW: RawFd = -1;
#[deprecated(since = "0.23.0", note = "use mem::size_of::<siginfo>() instead")]
pub const SIGNALFD_SIGINFO_SIZE: usize = mem::size_of::<siginfo>();
@@ -46,13 +46,24 @@ pub const SIGNALFD_SIGINFO_SIZE: usize = mem::size_of::<siginfo>();
/// signalfd (the default handler will be invoked instead).
///
/// See [the signalfd man page for more information](https://man7.org/linux/man-pages/man2/signalfd.2.html)
pub fn signalfd(fd: RawFd, mask: &SigSet, flags: SfdFlags) -> Result<RawFd> {
#[deprecated(since = "0.27.0", note = "Use SignalFd instead")]
pub fn signalfd<F: AsFd>(
fd: Option<F>,
mask: &SigSet,
flags: SfdFlags,
) -> Result<OwnedFd> {
_signalfd(fd, mask, flags)
}
fn _signalfd<F: AsFd>(
fd: Option<F>,
mask: &SigSet,
flags: SfdFlags,
) -> Result<OwnedFd> {
let raw_fd = fd.map_or(-1, |x| x.as_fd().as_raw_fd());
unsafe {
Errno::result(libc::signalfd(
fd as libc::c_int,
mask.as_ref(),
flags.bits(),
))
Errno::result(libc::signalfd(raw_fd, mask.as_ref(), flags.bits()))
.map(|raw_fd| FromRawFd::from_raw_fd(raw_fd))
}
}
@@ -82,8 +93,8 @@ pub fn signalfd(fd: RawFd, mask: &SigSet, flags: SfdFlags) -> Result<RawFd> {
/// Err(err) => (), // some error happend
/// }
/// ```
#[derive(Debug, Eq, Hash, PartialEq)]
pub struct SignalFd(RawFd);
#[derive(Debug)]
pub struct SignalFd(OwnedFd);
impl SignalFd {
pub fn new(mask: &SigSet) -> Result<SignalFd> {
@@ -91,21 +102,21 @@ impl SignalFd {
}
pub fn with_flags(mask: &SigSet, flags: SfdFlags) -> Result<SignalFd> {
let fd = signalfd(SIGNALFD_NEW, mask, flags)?;
let fd = _signalfd(None::<OwnedFd>, mask, flags)?;
Ok(SignalFd(fd))
}
pub fn set_mask(&mut self, mask: &SigSet) -> Result<()> {
signalfd(self.0, mask, SfdFlags::empty()).map(drop)
pub fn set_mask(&self, mask: &SigSet) -> Result<()> {
self.update(mask, SfdFlags::empty())
}
pub fn read_signal(&mut self) -> Result<Option<siginfo>> {
pub fn read_signal(&self) -> Result<Option<siginfo>> {
let mut buffer = mem::MaybeUninit::<siginfo>::uninit();
let size = mem::size_of_val(&buffer);
let res = Errno::result(unsafe {
libc::read(self.0, buffer.as_mut_ptr() as *mut libc::c_void, size)
libc::read(self.0.as_raw_fd(), buffer.as_mut_ptr().cast(), size)
})
.map(|r| r as usize);
match res {
@@ -115,20 +126,39 @@ impl SignalFd {
Err(error) => Err(error),
}
}
}
impl Drop for SignalFd {
fn drop(&mut self) {
let e = unistd::close(self.0);
if !std::thread::panicking() && e == Err(Errno::EBADF) {
panic!("Closing an invalid file descriptor!");
};
/// 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 {
Errno::result(libc::signalfd(raw_fd, mask.as_ref(), flags.bits()))
.map(drop)
}
}
}
impl AsFd for SignalFd {
fn as_fd(&self) -> BorrowedFd {
self.0.as_fd()
}
}
impl AsRawFd for SignalFd {
fn as_raw_fd(&self) -> RawFd {
self.0
self.0.as_raw_fd()
}
}
impl From<SignalFd> for OwnedFd {
fn from(value: SignalFd) -> Self {
value.0
}
}
@@ -142,34 +172,3 @@ impl Iterator for SignalFd {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn create_signalfd() {
let mask = SigSet::empty();
SignalFd::new(&mask).unwrap();
}
#[test]
fn create_signalfd_with_opts() {
let mask = SigSet::empty();
SignalFd::with_flags(
&mask,
SfdFlags::SFD_CLOEXEC | SfdFlags::SFD_NONBLOCK,
)
.unwrap();
}
#[test]
fn read_empty_signalfd() {
let mask = SigSet::empty();
let mut fd =
SignalFd::with_flags(&mask, SfdFlags::SFD_NONBLOCK).unwrap();
let res = fd.read_signal();
assert!(res.unwrap().is_none());
}
}
+408 -1252
View File
File diff suppressed because it is too large Load Diff
+760 -685
View File
File diff suppressed because it is too large Load Diff
+853 -272
View File
File diff suppressed because it is too large Load Diff
+80 -74
View File
@@ -1,20 +1,15 @@
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "openbsd"))]
#[cfg(any(apple_targets, target_os = "openbsd"))]
pub use libc::c_uint;
#[cfg(any(
target_os = "netbsd",
target_os = "freebsd",
target_os = "dragonfly"
))]
#[cfg(any(target_os = "netbsd", freebsdlike))]
pub use libc::c_ulong;
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.
@@ -43,7 +38,7 @@ libc_bitflags! {
S_IXUSR;
/// Read write and execute for group.
S_IRWXG;
/// Read fr group.
/// Read for group.
S_IRGRP;
/// Write for group.
S_IWGRP;
@@ -65,26 +60,14 @@ libc_bitflags! {
}
}
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "openbsd"))]
#[cfg(any(apple_targets, target_os = "openbsd"))]
pub type type_of_file_flag = c_uint;
#[cfg(any(
target_os = "netbsd",
target_os = "freebsd",
target_os = "dragonfly"
))]
#[cfg(any(freebsdlike, target_os = "netbsd"))]
pub type type_of_file_flag = c_ulong;
#[cfg(any(
target_os = "openbsd",
target_os = "netbsd",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "macos",
target_os = "ios"
))]
#[cfg(bsd)]
libc_bitflags! {
/// File flags.
#[cfg_attr(docsrs, doc(cfg(all())))]
pub struct FileFlag: type_of_file_flag {
/// The file may only be appended to.
SF_APPEND;
@@ -101,7 +84,7 @@ libc_bitflags! {
#[cfg(any(target_os = "dragonfly"))]
SF_NOHISTORY;
/// The file may not be renamed or deleted.
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
#[cfg(freebsdlike)]
SF_NOUNLINK;
/// Mask of superuser changeable flags
SF_SETTABLE;
@@ -121,14 +104,13 @@ libc_bitflags! {
#[cfg(any(target_os = "dragonfly"))]
UF_CACHE;
/// File is compressed at the file system level.
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[cfg(apple_targets)]
UF_COMPRESSED;
/// The file may be hidden from directory listings at the application's
/// discretion.
#[cfg(any(
target_os = "freebsd",
target_os = "macos",
target_os = "ios",
apple_targets,
))]
UF_HIDDEN;
/// The file may not be changed.
@@ -138,7 +120,7 @@ libc_bitflags! {
#[cfg(any(target_os = "dragonfly"))]
UF_NOHISTORY;
/// The file may not be renamed or deleted.
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
#[cfg(freebsdlike)]
UF_NOUNLINK;
/// The file is offline, or has the Windows and CIFS
/// `FILE_ATTRIBUTE_OFFLINE` attribute.
@@ -162,7 +144,7 @@ libc_bitflags! {
#[cfg(any(target_os = "freebsd"))]
UF_SYSTEM;
/// File renames and deletes are tracked.
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[cfg(apple_targets)]
UF_TRACKED;
#[cfg(any(target_os = "dragonfly"))]
UF_XLINK;
@@ -177,32 +159,28 @@ pub fn mknod<P: ?Sized + NixPath>(
dev: dev_t,
) -> Result<()> {
let res = path.with_nix_path(|cstr| unsafe {
libc::mknod(cstr.as_ptr(), kind.bits | perm.bits() as mode_t, dev)
libc::mknod(cstr.as_ptr(), kind.bits() | perm.bits() as mode_t, dev)
})?;
Errno::result(res).map(drop)
}
/// Create a special or ordinary file, relative to a given directory.
#[cfg(not(any(
target_os = "ios",
target_os = "macos",
target_os = "redox",
target_os = "haiku"
)))]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn mknodat<P: ?Sized + NixPath>(
dirfd: RawFd,
#[cfg(not(any(apple_targets, target_os = "redox", target_os = "haiku")))]
pub fn mknodat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
dirfd: Fd,
path: &P,
kind: SFlag,
perm: Mode,
dev: dev_t,
) -> Result<()> {
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,
kind.bits() | perm.bits() as mode_t,
dev,
)
})?;
@@ -211,19 +189,16 @@ pub fn mknodat<P: ?Sized + NixPath>(
}
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub const fn major(dev: dev_t) -> u64 {
((dev >> 32) & 0xffff_f000) | ((dev >> 8) & 0x0000_0fff)
}
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub const fn minor(dev: dev_t) -> u64 {
((dev >> 12) & 0xffff_ff00) | ((dev) & 0x0000_00ff)
}
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub const fn makedev(major: u64, minor: u64) -> dev_t {
((major & 0xffff_f000) << 32)
| ((major & 0x0000_0fff) << 8)
@@ -258,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)?;
@@ -268,16 +245,17 @@ pub fn fstat(fd: RawFd) -> Result<FileStat> {
}
#[cfg(not(target_os = "redox"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn fstatat<P: ?Sized + NixPath>(
dirfd: RawFd,
pub fn fstatat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
dirfd: Fd,
pathname: &P,
f: AtFlags,
) -> Result<FileStat> {
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,
@@ -294,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)
}
@@ -311,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.
///
@@ -324,20 +305,21 @@ pub enum FchmodatFlags {
///
/// [fchmodat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmodat.html).
#[cfg(not(target_os = "redox"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
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,12 +365,10 @@ pub fn utimes<P: ?Sized + NixPath>(
#[cfg(any(
target_os = "linux",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
apple_targets,
target_os = "freebsd",
target_os = "netbsd"
))]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn lutimes<P: ?Sized + NixPath>(
path: &P,
atime: &TimeVal,
@@ -404,13 +384,22 @@ pub fn lutimes<P: ?Sized + NixPath>(
/// Change the access and modification times of the file specified by a file descriptor.
///
/// If you want to set the timestamp to now, use `TimeSpec::UTIME_NOW`. Use
/// `TimeSpec::UTIME_OMIT` if you don't want to change it.
///
/// # References
///
/// [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)
}
@@ -427,27 +416,31 @@ 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.
///
/// If you want to set the timestamp to now, use `TimeSpec::UTIME_NOW`. Use
/// `TimeSpec::UTIME_OMIT` if you don't want to change it.
///
/// # References
///
/// [utimensat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/utimens.html).
#[cfg(not(target_os = "redox"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
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,
@@ -455,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,
@@ -465,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"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn mkdirat<P: ?Sized + NixPath>(
fd: RawFd,
pub fn mkdirat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
dirfd: Fd,
path: &P,
mode: Mode,
) -> Result<()> {
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)
+127 -289
View File
@@ -1,24 +1,15 @@
//! Get filesystem statistics, non-portably
//!
//! See [`statvfs`](crate::sys::statvfs) for a portable alternative.
#[cfg(not(any(target_os = "linux", target_os = "android")))]
#[cfg(not(any(linux_android, target_os = "cygwin")))]
use std::ffi::CStr;
use std::fmt::{self, Debug};
use std::mem;
use std::os::unix::io::AsRawFd;
use std::os::unix::io::{AsFd, AsRawFd};
use cfg_if::cfg_if;
#[cfg(all(
feature = "mount",
any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"
)
))]
#[cfg(all(feature = "mount", bsd))]
use crate::mount::MntFlags;
#[cfg(target_os = "linux")]
use crate::sys::statvfs::FsFlags;
@@ -26,28 +17,29 @@ use crate::{errno::Errno, NixPath, Result};
/// Identifies a mounted file system
#[cfg(target_os = "android")]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub type fsid_t = libc::__fsid_t;
/// Identifies a mounted file system
#[cfg(not(target_os = "android"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[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(target_os = "android", target_os = "fuchsia", target_os = "linux"))] {
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;
}
@@ -62,7 +54,11 @@ 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"))]
#[cfg(all(
target_os = "linux",
target_arch = "s390x",
not(target_env = "musl")
))]
type fs_type_t = libc::c_uint;
#[cfg(all(target_os = "linux", any(target_env = "musl", target_env = "ohos")))]
type fs_type_t = libc::c_ulong;
@@ -78,223 +74,224 @@ 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(
target_os = "freebsd",
target_os = "android",
all(target_os = "linux", target_arch = "s390x"),
all(target_os = "linux", any(target_env = "musl", target_env = "ohos")),
all(target_os = "linux", target_env = "musl"),
all(target_os = "linux", target_env = "ohos"),
all(
target_os = "linux",
not(any(target_arch = "s390x", target_env = "musl", target_env = "ohos"))
not(any(target_arch = "s390x", target_env = "musl"))
),
target_os = "cygwin",
))]
#[derive(Eq, Copy, Clone, PartialEq, Debug)]
pub struct FsType(pub fs_type_t);
// These constants are defined without documentation in the Linux headers, so we
// can't very well document them here.
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const ADFS_SUPER_MAGIC: FsType =
FsType(libc::ADFS_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const AFFS_SUPER_MAGIC: FsType =
FsType(libc::AFFS_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const AFS_SUPER_MAGIC: FsType = FsType(libc::AFS_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const AUTOFS_SUPER_MAGIC: FsType =
FsType(libc::AUTOFS_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const BPF_FS_MAGIC: FsType = FsType(libc::BPF_FS_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const BTRFS_SUPER_MAGIC: FsType =
FsType(libc::BTRFS_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const CGROUP2_SUPER_MAGIC: FsType =
FsType(libc::CGROUP2_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const CGROUP_SUPER_MAGIC: FsType =
FsType(libc::CGROUP_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const CODA_SUPER_MAGIC: FsType =
FsType(libc::CODA_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const CRAMFS_MAGIC: FsType = FsType(libc::CRAMFS_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const DEBUGFS_MAGIC: FsType = FsType(libc::DEBUGFS_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const DEVPTS_SUPER_MAGIC: FsType =
FsType(libc::DEVPTS_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const ECRYPTFS_SUPER_MAGIC: FsType =
FsType(libc::ECRYPTFS_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const EFS_SUPER_MAGIC: FsType = FsType(libc::EFS_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const EXT2_SUPER_MAGIC: FsType =
FsType(libc::EXT2_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const EXT3_SUPER_MAGIC: FsType =
FsType(libc::EXT3_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const EXT4_SUPER_MAGIC: FsType =
FsType(libc::EXT4_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const F2FS_SUPER_MAGIC: FsType =
FsType(libc::F2FS_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const FUSE_SUPER_MAGIC: FsType =
FsType(libc::FUSE_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const FUTEXFS_SUPER_MAGIC: FsType =
FsType(libc::FUTEXFS_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const HOSTFS_SUPER_MAGIC: FsType =
FsType(libc::HOSTFS_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const HPFS_SUPER_MAGIC: FsType =
FsType(libc::HPFS_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const HUGETLBFS_MAGIC: FsType = FsType(libc::HUGETLBFS_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const ISOFS_SUPER_MAGIC: FsType =
FsType(libc::ISOFS_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const JFFS2_SUPER_MAGIC: FsType =
FsType(libc::JFFS2_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const MINIX2_SUPER_MAGIC2: FsType =
FsType(libc::MINIX2_SUPER_MAGIC2 as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const MINIX2_SUPER_MAGIC: FsType =
FsType(libc::MINIX2_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const MINIX3_SUPER_MAGIC: FsType =
FsType(libc::MINIX3_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const MINIX_SUPER_MAGIC2: FsType =
FsType(libc::MINIX_SUPER_MAGIC2 as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const MINIX_SUPER_MAGIC: FsType =
FsType(libc::MINIX_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const MSDOS_SUPER_MAGIC: FsType =
FsType(libc::MSDOS_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const NCP_SUPER_MAGIC: FsType = FsType(libc::NCP_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const NFS_SUPER_MAGIC: FsType = FsType(libc::NFS_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const NILFS_SUPER_MAGIC: FsType =
FsType(libc::NILFS_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const OCFS2_SUPER_MAGIC: FsType =
FsType(libc::OCFS2_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const OPENPROM_SUPER_MAGIC: FsType =
FsType(libc::OPENPROM_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const OVERLAYFS_SUPER_MAGIC: FsType =
FsType(libc::OVERLAYFS_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const PROC_SUPER_MAGIC: FsType =
FsType(libc::PROC_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const QNX4_SUPER_MAGIC: FsType =
FsType(libc::QNX4_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const QNX6_SUPER_MAGIC: FsType =
FsType(libc::QNX6_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const RDTGROUP_SUPER_MAGIC: FsType =
FsType(libc::RDTGROUP_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const REISERFS_SUPER_MAGIC: FsType =
FsType(libc::REISERFS_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const SECURITYFS_MAGIC: FsType =
FsType(libc::SECURITYFS_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const SELINUX_MAGIC: FsType = FsType(libc::SELINUX_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const SMACK_MAGIC: FsType = FsType(libc::SMACK_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const SMB_SUPER_MAGIC: FsType = FsType(libc::SMB_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const SYSFS_MAGIC: FsType = FsType(libc::SYSFS_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const TMPFS_MAGIC: FsType = FsType(libc::TMPFS_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const TRACEFS_MAGIC: FsType = FsType(libc::TRACEFS_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const UDF_SUPER_MAGIC: FsType = FsType(libc::UDF_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const USBDEVICE_SUPER_MAGIC: FsType =
FsType(libc::USBDEVICE_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const XENFS_SUPER_MAGIC: FsType =
FsType(libc::XENFS_SUPER_MAGIC as fs_type_t);
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const NSFS_MAGIC: FsType = FsType(libc::NSFS_MAGIC as fs_type_t);
#[cfg(all(
any(target_os = "linux", target_os = "android"),
not(any(target_env = "musl", target_env = "ohos"))
))]
#[cfg(all(linux_android, not(target_env = "musl"), not(target_env = "ohos")))]
#[allow(missing_docs)]
pub const XFS_SUPER_MAGIC: FsType = FsType(libc::XFS_SUPER_MAGIC as fs_type_t);
@@ -303,39 +300,37 @@ impl Statfs {
#[cfg(not(any(
target_os = "openbsd",
target_os = "dragonfly",
target_os = "ios",
target_os = "macos"
apple_targets,
)))]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn filesystem_type(&self) -> FsType {
FsType(self.0.f_type)
}
/// Magic code defining system type
#[cfg(not(any(target_os = "linux", target_os = "android")))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[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()
}
/// Optimal transfer block size
#[cfg(any(target_os = "ios", target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(apple_targets)]
pub fn optimal_transfer_size(&self) -> i32 {
self.0.f_iosize
}
/// Optimal transfer block size
#[cfg(target_os = "openbsd")]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn optimal_transfer_size(&self) -> u32 {
self.0.f_iosize
}
/// Optimal transfer block size
#[cfg(all(target_os = "linux", target_arch = "s390x"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(all(
target_os = "linux",
target_arch = "s390x",
not(target_env = "musl")
))]
pub fn optimal_transfer_size(&self) -> u32 {
self.0.f_bsize
}
@@ -343,9 +338,9 @@ impl Statfs {
/// Optimal transfer block size
#[cfg(any(
target_os = "android",
all(target_os = "linux", any(target_env = "musl", target_env = "ohos"))
all(target_os = "linux", target_env = "musl"),
all(target_os = "linux", target_env = "ohos")
))]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn optimal_transfer_size(&self) -> libc::c_ulong {
self.0.f_bsize
}
@@ -360,51 +355,55 @@ impl Statfs {
target_env = "uclibc"
))
))]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn optimal_transfer_size(&self) -> libc::__fsword_t {
self.0.f_bsize
}
/// Optimal transfer block size
#[cfg(all(target_os = "linux", target_env = "uclibc"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn optimal_transfer_size(&self) -> libc::c_int {
self.0.f_bsize
}
/// Optimal transfer block size
#[cfg(target_os = "dragonfly")]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn optimal_transfer_size(&self) -> libc::c_long {
self.0.f_iosize
}
/// Optimal transfer block size
#[cfg(target_os = "freebsd")]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn optimal_transfer_size(&self) -> u64 {
self.0.f_iosize
}
/// Size of a block
#[cfg(any(target_os = "ios", target_os = "macos", target_os = "openbsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(any(apple_targets, target_os = "openbsd"))]
pub fn block_size(&self) -> u32 {
self.0.f_bsize
}
/// 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"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(all(
target_os = "linux",
target_arch = "s390x",
not(target_env = "musl")
))]
pub fn block_size(&self) -> u32 {
self.0.f_bsize
}
/// 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", any(target_env = "musl", target_env = "ohos")))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(all(target_os = "linux", target_env = "musl"))]
pub fn block_size(&self) -> libc::c_ulong {
self.0.f_bsize
}
/// 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_env = "ohos"))]
pub fn block_size(&self) -> libc::c_ulong {
self.0.f_bsize
}
@@ -412,7 +411,6 @@ 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_env = "uclibc"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn block_size(&self) -> libc::c_int {
self.0.f_bsize
}
@@ -428,44 +426,30 @@ impl Statfs {
target_env = "uclibc"
))
))]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn block_size(&self) -> libc::__fsword_t {
self.0.f_bsize
}
/// Size of a block
#[cfg(target_os = "freebsd")]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn block_size(&self) -> u64 {
self.0.f_bsize
}
/// Size of a block
#[cfg(target_os = "android")]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn block_size(&self) -> libc::c_ulong {
self.0.f_bsize
}
/// Size of a block
#[cfg(target_os = "dragonfly")]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(any(target_os = "dragonfly", target_os = "cygwin"))]
pub fn block_size(&self) -> libc::c_long {
self.0.f_bsize
}
/// Get the mount flags
#[cfg(all(
feature = "mount",
any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"
)
))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(all(feature = "mount", bsd))]
#[allow(clippy::unnecessary_cast)] // Not unnecessary on all arches
pub fn flags(&self) -> MntFlags {
MntFlags::from_bits_truncate(self.0.f_flags as i32)
@@ -475,35 +459,34 @@ impl Statfs {
// The f_flags field exists on Android and Fuchsia too, but without man
// pages I can't tell if it can be cast to FsFlags.
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn flags(&self) -> FsFlags {
FsFlags::from_bits_truncate(self.0.f_flags as libc::c_ulong)
}
/// Maximum length of filenames
#[cfg(any(target_os = "freebsd", target_os = "openbsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn maximum_name_length(&self) -> u32 {
self.0.f_namemax
}
/// Maximum length of filenames
#[cfg(all(target_os = "linux", target_arch = "s390x"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(all(
target_os = "linux",
target_arch = "s390x",
not(target_env = "musl")
))]
pub fn maximum_name_length(&self) -> u32 {
self.0.f_namelen
}
/// Maximum length of filenames
#[cfg(all(target_os = "linux", any(target_env = "musl", target_env = "ohos")))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(all(target_os = "linux", target_env = "musl"))]
pub fn maximum_name_length(&self) -> libc::c_ulong {
self.0.f_namelen
}
/// Maximum length of filenames
#[cfg(all(target_os = "linux", target_env = "uclibc"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn maximum_name_length(&self) -> libc::c_int {
self.0.f_namelen
}
@@ -518,170 +501,137 @@ impl Statfs {
target_env = "uclibc"
))
))]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn maximum_name_length(&self) -> libc::__fsword_t {
self.0.f_namelen
}
/// Maximum length of filenames
#[cfg(target_os = "android")]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn maximum_name_length(&self) -> libc::c_ulong {
self.0.f_namelen
}
/// Total data blocks in filesystem
#[cfg(any(
target_os = "ios",
target_os = "macos",
target_os = "android",
apple_targets,
linux_android,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "openbsd",
target_os = "linux",
))]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn blocks(&self) -> u64 {
self.0.f_blocks
}
/// Total data blocks in filesystem
#[cfg(target_os = "dragonfly")]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(any(target_os = "dragonfly", target_os = "cygwin"))]
pub fn blocks(&self) -> libc::c_long {
self.0.f_blocks
}
/// Total data blocks in filesystem
#[cfg(target_os = "emscripten")]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn blocks(&self) -> u32 {
self.0.f_blocks
}
/// Free blocks in filesystem
#[cfg(any(
target_os = "ios",
target_os = "macos",
target_os = "android",
apple_targets,
linux_android,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "openbsd",
target_os = "linux",
))]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn blocks_free(&self) -> u64 {
self.0.f_bfree
}
/// Free blocks in filesystem
#[cfg(target_os = "dragonfly")]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(any(target_os = "dragonfly", target_os = "cygwin"))]
pub fn blocks_free(&self) -> libc::c_long {
self.0.f_bfree
}
/// Free blocks in filesystem
#[cfg(target_os = "emscripten")]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn blocks_free(&self) -> u32 {
self.0.f_bfree
}
/// Free blocks available to unprivileged user
#[cfg(any(
target_os = "ios",
target_os = "macos",
target_os = "android",
target_os = "fuchsia",
target_os = "linux",
))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(any(apple_targets, linux_android, target_os = "fuchsia"))]
pub fn blocks_available(&self) -> u64 {
self.0.f_bavail
}
/// Free blocks available to unprivileged user
#[cfg(target_os = "dragonfly")]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(any(target_os = "dragonfly", target_os = "cygwin"))]
pub fn blocks_available(&self) -> libc::c_long {
self.0.f_bavail
}
/// Free blocks available to unprivileged user
#[cfg(any(target_os = "freebsd", target_os = "openbsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn blocks_available(&self) -> i64 {
self.0.f_bavail
}
/// Free blocks available to unprivileged user
#[cfg(target_os = "emscripten")]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn blocks_available(&self) -> u32 {
self.0.f_bavail
}
/// Total file nodes in filesystem
#[cfg(any(
target_os = "ios",
target_os = "macos",
target_os = "android",
apple_targets,
linux_android,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "openbsd",
target_os = "linux",
))]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn files(&self) -> u64 {
self.0.f_files
}
/// Total file nodes in filesystem
#[cfg(target_os = "dragonfly")]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(any(target_os = "dragonfly", target_os = "cygwin"))]
pub fn files(&self) -> libc::c_long {
self.0.f_files
}
/// Total file nodes in filesystem
#[cfg(target_os = "emscripten")]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn files(&self) -> u32 {
self.0.f_files
}
/// Free file nodes in filesystem
#[cfg(any(
target_os = "ios",
target_os = "macos",
target_os = "android",
apple_targets,
linux_android,
target_os = "fuchsia",
target_os = "openbsd",
target_os = "linux",
))]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn files_free(&self) -> u64 {
self.0.f_ffree
}
/// Free file nodes in filesystem
#[cfg(target_os = "dragonfly")]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(any(target_os = "dragonfly", target_os = "cygwin"))]
pub fn files_free(&self) -> libc::c_long {
self.0.f_ffree
}
/// Free file nodes in filesystem
#[cfg(target_os = "freebsd")]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn files_free(&self) -> i64 {
self.0.f_ffree
}
/// Free file nodes in filesystem
#[cfg(target_os = "emscripten")]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn files_free(&self) -> u32 {
self.0.f_ffree
}
@@ -695,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());
@@ -703,16 +654,7 @@ impl Debug for Statfs {
ds.field("files", &self.files());
ds.field("files_free", &self.files_free());
ds.field("filesystem_id", &self.filesystem_id());
#[cfg(all(
feature = "mount",
any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"
)
))]
#[cfg(all(feature = "mount", bsd))]
ds.field("flags", &self.flags());
ds.finish()
}
@@ -744,114 +686,10 @@ pub fn statfs<P: ?Sized + NixPath>(path: &P) -> Result<Statfs> {
/// # Arguments
///
/// `fd` - File descriptor of any open file within the file system to describe
pub fn fstatfs<T: AsRawFd>(fd: &T) -> Result<Statfs> {
pub fn fstatfs<Fd: AsFd>(fd: Fd) -> Result<Statfs> {
unsafe {
let mut stat = mem::MaybeUninit::<type_of_statfs>::uninit();
Errno::result(LIBC_FSTATFS(fd.as_raw_fd(), stat.as_mut_ptr()))
Errno::result(LIBC_FSTATFS(fd.as_fd().as_raw_fd(), stat.as_mut_ptr()))
.map(|_| Statfs(stat.assume_init()))
}
}
#[cfg(test)]
mod test {
use std::fs::File;
use crate::sys::statfs::*;
use crate::sys::statvfs::*;
use std::path::Path;
#[test]
fn statfs_call() {
check_statfs("/tmp");
check_statfs("/dev");
check_statfs("/run");
check_statfs("/");
}
#[test]
fn fstatfs_call() {
check_fstatfs("/tmp");
check_fstatfs("/dev");
check_fstatfs("/run");
check_fstatfs("/");
}
fn check_fstatfs(path: &str) {
if !Path::new(path).exists() {
return;
}
let vfs = statvfs(path.as_bytes()).unwrap();
let file = File::open(path).unwrap();
let fs = fstatfs(&file).unwrap();
assert_fs_equals(fs, vfs);
}
fn check_statfs(path: &str) {
if !Path::new(path).exists() {
return;
}
let vfs = statvfs(path.as_bytes()).unwrap();
let fs = statfs(path.as_bytes()).unwrap();
assert_fs_equals(fs, vfs);
}
// The cast is not unnecessary on all platforms.
#[allow(clippy::unnecessary_cast)]
fn assert_fs_equals(fs: Statfs, vfs: Statvfs) {
assert_eq!(fs.files() as u64, vfs.files() as u64);
assert_eq!(fs.blocks() as u64, vfs.blocks() as u64);
assert_eq!(fs.block_size() as u64, vfs.fragment_size() as u64);
}
// This test is ignored because files_free/blocks_free can change after statvfs call and before
// statfs call.
#[test]
#[ignore]
fn statfs_call_strict() {
check_statfs_strict("/tmp");
check_statfs_strict("/dev");
check_statfs_strict("/run");
check_statfs_strict("/");
}
// This test is ignored because files_free/blocks_free can change after statvfs call and before
// fstatfs call.
#[test]
#[ignore]
fn fstatfs_call_strict() {
check_fstatfs_strict("/tmp");
check_fstatfs_strict("/dev");
check_fstatfs_strict("/run");
check_fstatfs_strict("/");
}
fn check_fstatfs_strict(path: &str) {
if !Path::new(path).exists() {
return;
}
let vfs = statvfs(path.as_bytes());
let file = File::open(path).unwrap();
let fs = fstatfs(&file);
assert_fs_equals_strict(fs.unwrap(), vfs.unwrap())
}
fn check_statfs_strict(path: &str) {
if !Path::new(path).exists() {
return;
}
let vfs = statvfs(path.as_bytes());
let fs = statfs(path.as_bytes());
assert_fs_equals_strict(fs.unwrap(), vfs.unwrap())
}
// The cast is not unnecessary on all platforms.
#[allow(clippy::unnecessary_cast)]
fn assert_fs_equals_strict(fs: Statfs, vfs: Statvfs) {
assert_eq!(fs.files_free() as u64, vfs.files_free() as u64);
assert_eq!(fs.blocks_free() as u64, vfs.blocks_free() as u64);
assert_eq!(fs.blocks_available() as u64, vfs.blocks_available() as u64);
assert_eq!(fs.files() as u64, vfs.files() as u64);
assert_eq!(fs.blocks() as u64, vfs.blocks() as u64);
assert_eq!(fs.block_size() as u64, vfs.fragment_size() as u64);
}
}
+16 -39
View File
@@ -3,7 +3,7 @@
//! See [the man pages](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fstatvfs.html)
//! for more details.
use std::mem;
use std::os::unix::io::AsRawFd;
use std::os::unix::io::{AsFd, AsRawFd};
use libc::{self, c_ulong};
@@ -12,7 +12,6 @@ use crate::{errno::Errno, NixPath, Result};
#[cfg(not(target_os = "redox"))]
libc_bitflags!(
/// File system mount Flags
#[repr(C)]
#[derive(Default)]
pub struct FsFlags: c_ulong {
/// Read Only
@@ -22,44 +21,34 @@ libc_bitflags!(
#[cfg(not(target_os = "haiku"))]
ST_NOSUID;
/// Do not interpret character or block-special devices
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
ST_NODEV;
/// Do not allow execution of binaries on the filesystem
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
ST_NOEXEC;
/// All IO should be done synchronously
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
ST_SYNCHRONOUS;
/// Allow mandatory locks on the filesystem
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
ST_MANDLOCK;
/// Write on file/directory/symlink
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(all())))]
ST_WRITE;
/// Append-only file
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(all())))]
ST_APPEND;
/// Immutable file
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(all())))]
ST_IMMUTABLE;
/// Do not update access times on files
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
ST_NOATIME;
/// Do not update access times on files
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
ST_NODIRATIME;
/// Update access time relative to modify/change time
#[cfg(any(target_os = "android", all(target_os = "linux", not(any(target_env = "musl", target_env = "ohos")))))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(any(target_os = "android", all(target_os = "linux", not(target_env = "musl"), not(target_env = "ohos"))))]
ST_RELATIME;
}
);
@@ -115,13 +104,18 @@ impl Statvfs {
}
/// Get the file system id
#[cfg(not(target_os = "hurd"))]
pub fn filesystem_id(&self) -> c_ulong {
self.0.f_fsid
}
/// Get the file system id
#[cfg(target_os = "hurd")]
pub fn filesystem_id(&self) -> u64 {
self.0.f_fsid
}
/// Get the mount flags
#[cfg(not(target_os = "redox"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn flags(&self) -> FsFlags {
FsFlags::from_bits_truncate(self.0.f_flag)
}
@@ -146,28 +140,11 @@ pub fn statvfs<P: ?Sized + NixPath>(path: &P) -> Result<Statvfs> {
}
/// Return a `Statvfs` object with information about `fd`
pub fn fstatvfs<T: AsRawFd>(fd: &T) -> Result<Statvfs> {
pub fn fstatvfs<Fd: AsFd>(fd: Fd) -> Result<Statvfs> {
unsafe {
Errno::clear();
let mut stat = mem::MaybeUninit::<libc::statvfs>::uninit();
Errno::result(libc::fstatvfs(fd.as_raw_fd(), stat.as_mut_ptr()))
Errno::result(libc::fstatvfs(fd.as_fd().as_raw_fd(), stat.as_mut_ptr()))
.map(|_| Statvfs(stat.assume_init()))
}
}
#[cfg(test)]
mod test {
use crate::sys::statvfs::*;
use std::fs::File;
#[test]
fn statvfs_call() {
statvfs(&b"/"[..]).unwrap();
}
#[test]
fn fstatvfs_call() {
let root = File::open("/").unwrap();
fstatvfs(&root).unwrap();
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
use libc::{self, SI_LOAD_SHIFT};
use libc::SI_LOAD_SHIFT;
use std::time::Duration;
use std::{cmp, mem};
+162 -464
View File
@@ -85,28 +85,8 @@
//!
//! On non-BSDs, `cfgetispeed()` and `cfgetospeed()` both return a `BaudRate`:
//!
#![cfg_attr(
any(
target_os = "freebsd",
target_os = "dragonfly",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"
),
doc = " ```rust,ignore"
)]
#![cfg_attr(
not(any(
target_os = "freebsd",
target_os = "dragonfly",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"
)),
doc = " ```rust"
)]
#![cfg_attr(bsd, doc = " ```rust,ignore")]
#![cfg_attr(not(bsd), doc = " ```rust")]
//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetspeed, Termios};
//! # fn main() {
//! # let mut t: Termios = unsafe { std::mem::zeroed() };
@@ -118,28 +98,8 @@
//!
//! But on the BSDs, `cfgetispeed()` and `cfgetospeed()` both return `u32`s:
//!
#![cfg_attr(
any(
target_os = "freebsd",
target_os = "dragonfly",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"
),
doc = " ```rust"
)]
#![cfg_attr(
not(any(
target_os = "freebsd",
target_os = "dragonfly",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"
)),
doc = " ```rust,ignore"
)]
#![cfg_attr(bsd, doc = " ```rust")]
#![cfg_attr(not(bsd), doc = " ```rust,ignore")]
//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetspeed, Termios};
//! # fn main() {
//! # let mut t: Termios = unsafe { std::mem::zeroed() };
@@ -151,28 +111,8 @@
//!
//! It's trivial to convert from a `BaudRate` to a `u32` on BSDs:
//!
#![cfg_attr(
any(
target_os = "freebsd",
target_os = "dragonfly",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"
),
doc = " ```rust"
)]
#![cfg_attr(
not(any(
target_os = "freebsd",
target_os = "dragonfly",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"
)),
doc = " ```rust,ignore"
)]
#![cfg_attr(bsd, doc = " ```rust")]
#![cfg_attr(not(bsd), doc = " ```rust,ignore")]
//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfsetspeed, Termios};
//! # fn main() {
//! # let mut t: Termios = unsafe { std::mem::zeroed() };
@@ -185,28 +125,8 @@
//! And on BSDs you can specify arbitrary baud rates (**note** this depends on hardware support)
//! by specifying baud rates directly using `u32`s:
//!
#![cfg_attr(
any(
target_os = "freebsd",
target_os = "dragonfly",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"
),
doc = " ```rust"
)]
#![cfg_attr(
not(any(
target_os = "freebsd",
target_os = "dragonfly",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"
)),
doc = " ```rust,ignore"
)]
#![cfg_attr(bsd, doc = " ```rust")]
#![cfg_attr(not(bsd), doc = " ```rust,ignore")]
//! # use nix::sys::termios::{cfsetispeed, cfsetospeed, cfsetspeed, Termios};
//! # fn main() {
//! # let mut t: Termios = unsafe { std::mem::zeroed() };
@@ -222,7 +142,7 @@ use libc::{self, c_int, tcflag_t};
use std::cell::{Ref, RefCell};
use std::convert::From;
use std::mem;
use std::os::unix::io::RawFd;
use std::os::unix::io::{AsFd, AsRawFd};
#[cfg(feature = "process")]
use crate::unistd::Pid;
@@ -246,7 +166,7 @@ pub struct Termios {
/// Control characters (see `termios.c_cc` documentation)
pub control_chars: [libc::cc_t; NCCS],
/// Line discipline (see `termios.c_line` documentation)
#[cfg(any(target_os = "linux", target_os = "android",))]
#[cfg(linux_android)]
pub line_discipline: libc::cc_t,
/// Line discipline (see `termios.c_line` documentation)
#[cfg(target_os = "haiku")]
@@ -266,11 +186,7 @@ impl Termios {
termios.c_cflag = self.control_flags.bits();
termios.c_lflag = self.local_flags.bits();
termios.c_cc = self.control_chars;
#[cfg(any(
target_os = "linux",
target_os = "android",
target_os = "haiku",
))]
#[cfg(any(linux_android, target_os = "haiku"))]
{
termios.c_line = self.line_discipline;
}
@@ -292,11 +208,7 @@ impl Termios {
termios.c_cflag = self.control_flags.bits();
termios.c_lflag = self.local_flags.bits();
termios.c_cc = self.control_chars;
#[cfg(any(
target_os = "linux",
target_os = "android",
target_os = "haiku",
))]
#[cfg(any(linux_android, target_os = "haiku"))]
{
termios.c_line = self.line_discipline;
}
@@ -309,14 +221,10 @@ impl Termios {
let termios = *self.inner.borrow_mut();
self.input_flags = InputFlags::from_bits_truncate(termios.c_iflag);
self.output_flags = OutputFlags::from_bits_truncate(termios.c_oflag);
self.control_flags = ControlFlags::from_bits_truncate(termios.c_cflag);
self.control_flags = ControlFlags::from_bits_retain(termios.c_cflag);
self.local_flags = LocalFlags::from_bits_truncate(termios.c_lflag);
self.control_chars = termios.c_cc;
#[cfg(any(
target_os = "linux",
target_os = "android",
target_os = "haiku",
))]
#[cfg(any(linux_android, target_os = "haiku"))]
{
self.line_discipline = termios.c_line;
}
@@ -332,11 +240,7 @@ impl From<libc::termios> for Termios {
control_flags: ControlFlags::from_bits_truncate(termios.c_cflag),
local_flags: LocalFlags::from_bits_truncate(termios.c_lflag),
control_chars: termios.c_cc,
#[cfg(any(
target_os = "linux",
target_os = "android",
target_os = "haiku",
))]
#[cfg(any(linux_android, target_os = "haiku"))]
line_discipline: termios.c_line,
}
}
@@ -355,9 +259,14 @@ libc_enum! {
/// enum.
///
/// B0 is special and will disable the port.
#[cfg_attr(all(any(target_os = "haiku"), target_pointer_width = "64"), repr(u8))]
#[cfg_attr(all(any(target_os = "ios", target_os = "macos"), target_pointer_width = "64"), repr(u64))]
#[cfg_attr(not(all(any(target_os = "ios", target_os = "macos", target_os = "haiku"), target_pointer_width = "64")), repr(u32))]
#[cfg_attr(target_os = "haiku", repr(u8))]
#[cfg_attr(target_os = "hurd", repr(i32))]
#[cfg_attr(all(apple_targets, target_pointer_width = "64"), repr(u64))]
#[cfg_attr(all(
not(all(apple_targets, target_pointer_width = "64")),
not(target_os = "haiku"),
not(target_os = "hurd")
), repr(u32))]
#[non_exhaustive]
pub enum BaudRate {
B0,
@@ -373,104 +282,62 @@ libc_enum! {
B1800,
B2400,
B4800,
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(bsd)]
B7200,
B9600,
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(bsd)]
B14400,
B19200,
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(bsd)]
B28800,
B38400,
#[cfg(not(target_os = "aix"))]
B57600,
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(bsd)]
B76800,
#[cfg(not(target_os = "aix"))]
B115200,
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(solarish)]
B153600,
#[cfg(not(target_os = "aix"))]
B230400,
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(solarish)]
B307200,
#[cfg(any(target_os = "android",
#[cfg(any(linux_android,
solarish,
target_os = "freebsd",
target_os = "illumos",
target_os = "linux",
target_os = "netbsd",
target_os = "solaris"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
target_os = "netbsd"))]
B460800,
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
B500000,
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
B576000,
#[cfg(any(target_os = "android",
#[cfg(any(linux_android,
solarish,
target_os = "freebsd",
target_os = "illumos",
target_os = "linux",
target_os = "netbsd",
target_os = "solaris"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
target_os = "netbsd"))]
B921600,
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
B1000000,
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
B1152000,
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
B1500000,
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
B2000000,
#[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
#[cfg_attr(docsrs, doc(cfg(all())))]
B2500000,
#[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
#[cfg_attr(docsrs, doc(cfg(all())))]
B3000000,
#[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
#[cfg_attr(docsrs, doc(cfg(all())))]
B3500000,
#[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
#[cfg_attr(docsrs, doc(cfg(all())))]
B4000000,
}
impl TryFrom<libc::speed_t>
}
#[cfg(any(
target_os = "freebsd",
target_os = "dragonfly",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"
))]
#[cfg(bsd)]
impl From<BaudRate> for u32 {
fn from(b: BaudRate) -> u32 {
b as u32
@@ -536,74 +403,57 @@ libc_enum! {
}
// TODO: Make this usable directly as a slice index.
#[cfg(not(target_os = "haiku"))]
libc_enum! {
/// Indices into the `termios.c_cc` array for special characters.
#[repr(usize)]
#[non_exhaustive]
pub enum SpecialCharacterIndices {
#[cfg(not(any(target_os = "aix", target_os = "haiku")))]
VDISCARD,
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "illumos",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(any(bsd,
solarish,
target_os = "aix"))]
VDSUSP,
VEOF,
VEOL,
VEOL2,
VERASE,
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "illumos",
target_os = "solaris"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(any(freebsdlike, target_os = "illumos"))]
VERASE2,
VINTR,
VKILL,
#[cfg(not(target_os = "haiku"))]
VLNEXT,
#[cfg(not(any(all(target_os = "linux", target_arch = "sparc64"),
target_os = "illumos", target_os = "solaris")))]
#[cfg_attr(docsrs, doc(cfg(all())))]
solarish, target_os = "aix", target_os = "haiku")))]
VMIN,
VQUIT,
#[cfg(not(target_os = "haiku"))]
VREPRINT,
VSTART,
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "illumos",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(any(bsd, target_os = "illumos"))]
VSTATUS,
VSTOP,
VSUSP,
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(all())))]
VSWTC,
#[cfg(any(target_os = "haiku", target_os = "illumos", target_os = "solaris"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(any(solarish, target_os = "haiku"))]
VSWTCH,
#[cfg(not(any(all(target_os = "linux", target_arch = "sparc64"),
target_os = "illumos", target_os = "solaris")))]
#[cfg_attr(docsrs, doc(cfg(all())))]
solarish, target_os = "aix", target_os = "haiku")))]
VTIME,
#[cfg(not(any(target_os = "aix", target_os = "haiku")))]
VWERASE,
#[cfg(target_os = "dragonfly")]
#[cfg_attr(docsrs, doc(cfg(all())))]
VCHECKPT,
}
}
#[cfg(any(
all(target_os = "linux", target_arch = "sparc64"),
target_os = "illumos",
target_os = "solaris"
solarish,
target_os = "aix",
target_os = "haiku",
))]
impl SpecialCharacterIndices {
pub const VMIN: SpecialCharacterIndices = SpecialCharacterIndices::VEOF;
@@ -611,16 +461,7 @@ impl SpecialCharacterIndices {
}
pub use libc::NCCS;
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"
))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(any(bsd, linux_android, target_os = "aix", target_os = "solaris"))]
pub use libc::_POSIX_VDISABLE;
libc_bitflags! {
@@ -638,13 +479,10 @@ libc_bitflags! {
IXON;
IXOFF;
#[cfg(not(target_os = "redox"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
IXANY;
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
#[cfg_attr(docsrs, doc(cfg(all())))]
IMAXBEL;
#[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(any(linux_android, apple_targets))]
IUTF8;
}
}
@@ -653,209 +491,119 @@ libc_bitflags! {
/// Flags for configuring the output mode of a terminal
pub struct OutputFlags: tcflag_t {
OPOST;
#[cfg(any(target_os = "android",
#[cfg(any(linux_android,
target_os = "haiku",
target_os = "linux",
target_os = "openbsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
OLCUC;
ONLCR;
OCRNL as tcflag_t;
ONOCR as tcflag_t;
ONLRET as tcflag_t;
#[cfg(any(target_os = "android",
#[cfg(any(linux_android,
target_os = "haiku",
target_os = "ios",
target_os = "linux",
target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
OFILL as tcflag_t;
#[cfg(any(target_os = "android",
target_os = "haiku",
target_os = "ios",
target_os = "linux",
target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
apple_targets))]
OFDEL as tcflag_t;
#[cfg(any(target_os = "android",
#[cfg(any(linux_android,
target_os = "haiku",
target_os = "ios",
target_os = "linux",
target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
apple_targets))]
NL0 as tcflag_t;
#[cfg(any(target_os = "android",
#[cfg(any(linux_android,
target_os = "haiku",
target_os = "ios",
target_os = "linux",
target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
apple_targets))]
NL1 as tcflag_t;
#[cfg(any(target_os = "android",
#[cfg(any(linux_android,
target_os = "haiku",
target_os = "ios",
target_os = "linux",
target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
apple_targets))]
CR0 as tcflag_t;
#[cfg(any(target_os = "android",
#[cfg(any(linux_android,
target_os = "haiku",
target_os = "ios",
target_os = "linux",
target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
apple_targets))]
CR1 as tcflag_t;
#[cfg(any(target_os = "android",
#[cfg(any(linux_android,
target_os = "haiku",
target_os = "ios",
target_os = "linux",
target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
apple_targets))]
CR2 as tcflag_t;
#[cfg(any(target_os = "android",
#[cfg(any(linux_android,
target_os = "haiku",
target_os = "ios",
target_os = "linux",
target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
apple_targets))]
CR3 as tcflag_t;
#[cfg(any(target_os = "android",
#[cfg(any(linux_android,
target_os = "freebsd",
target_os = "haiku",
target_os = "ios",
target_os = "linux",
target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
apple_targets))]
TAB0 as tcflag_t;
#[cfg(any(target_os = "android",
#[cfg(any(linux_android,
target_os = "haiku",
target_os = "ios",
target_os = "linux",
target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
apple_targets))]
TAB1 as tcflag_t;
#[cfg(any(target_os = "android",
#[cfg(any(linux_android,
target_os = "haiku",
target_os = "ios",
target_os = "linux",
target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
apple_targets))]
TAB2 as tcflag_t;
#[cfg(any(target_os = "android",
#[cfg(any(linux_android,
target_os = "freebsd",
target_os = "haiku",
target_os = "ios",
target_os = "linux",
target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
apple_targets))]
TAB3 as tcflag_t;
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
XTABS;
#[cfg(any(target_os = "android",
#[cfg(any(linux_android,
target_os = "haiku",
target_os = "ios",
target_os = "linux",
target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
apple_targets))]
BS0 as tcflag_t;
#[cfg(any(target_os = "android",
#[cfg(any(linux_android,
target_os = "haiku",
target_os = "ios",
target_os = "linux",
target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
apple_targets))]
BS1 as tcflag_t;
#[cfg(any(target_os = "android",
#[cfg(any(linux_android,
target_os = "haiku",
target_os = "ios",
target_os = "linux",
target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
apple_targets))]
VT0 as tcflag_t;
#[cfg(any(target_os = "android",
#[cfg(any(linux_android,
target_os = "haiku",
target_os = "ios",
target_os = "linux",
target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
apple_targets))]
VT1 as tcflag_t;
#[cfg(any(target_os = "android",
#[cfg(any(linux_android,
target_os = "haiku",
target_os = "ios",
target_os = "linux",
target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
apple_targets))]
FF0 as tcflag_t;
#[cfg(any(target_os = "android",
#[cfg(any(linux_android,
target_os = "haiku",
target_os = "ios",
target_os = "linux",
target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
apple_targets))]
FF1 as tcflag_t;
#[cfg(any(target_os = "freebsd",
target_os = "dragonfly",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(bsd)]
OXTABS;
#[cfg(any(target_os = "freebsd",
target_os = "dragonfly",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(bsd)]
ONOEOT as tcflag_t;
// Bitmasks for use with OutputFlags to select specific settings
// These should be moved to be a mask once https://github.com/rust-lang-nursery/bitflags/issues/110
// is resolved.
#[cfg(any(target_os = "android",
#[cfg(any(linux_android,
target_os = "haiku",
target_os = "ios",
target_os = "linux",
target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
apple_targets))]
NLDLY as tcflag_t; // FIXME: Datatype needs to be corrected in libc for mac
#[cfg(any(target_os = "android",
#[cfg(any(linux_android,
target_os = "haiku",
target_os = "ios",
target_os = "linux",
target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
apple_targets))]
CRDLY as tcflag_t;
#[cfg(any(target_os = "android",
#[cfg(any(linux_android,
target_os = "freebsd",
target_os = "haiku",
target_os = "ios",
target_os = "linux",
target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
apple_targets))]
TABDLY as tcflag_t;
#[cfg(any(target_os = "android",
#[cfg(any(linux_android,
target_os = "haiku",
target_os = "ios",
target_os = "linux",
target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
apple_targets))]
BSDLY as tcflag_t;
#[cfg(any(target_os = "android",
#[cfg(any(linux_android,
target_os = "haiku",
target_os = "ios",
target_os = "linux",
target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
apple_targets))]
VTDLY as tcflag_t;
#[cfg(any(target_os = "android",
#[cfg(any(linux_android,
target_os = "haiku",
target_os = "ios",
target_os = "linux",
target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
apple_targets))]
FFDLY as tcflag_t;
}
}
@@ -863,13 +611,7 @@ libc_bitflags! {
libc_bitflags! {
/// Flags for setting the control mode of a terminal
pub struct ControlFlags: tcflag_t {
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(bsd)]
CIGNORE;
CS5;
CS6;
@@ -881,55 +623,31 @@ libc_bitflags! {
PARODD;
HUPCL;
CLOCAL;
#[cfg(not(target_os = "redox"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(not(any(target_os = "redox", target_os = "aix")))]
CRTSCTS;
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
CBAUD;
#[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "mips"))))]
#[cfg_attr(docsrs, doc(cfg(all())))]
CMSPAR;
#[cfg(any(target_os = "android",
all(target_os = "linux",
not(any(target_arch = "powerpc", target_arch = "powerpc64")))))]
CIBAUD;
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
CBAUDEX;
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(bsd)]
MDMBUF;
#[cfg(any(target_os = "netbsd", target_os = "openbsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(netbsdlike)]
CHWFLOW;
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(any(freebsdlike, netbsdlike))]
CCTS_OFLOW;
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(any(freebsdlike, netbsdlike))]
CRTS_IFLOW;
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(freebsdlike)]
CDTR_IFLOW;
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(freebsdlike)]
CDSR_OFLOW;
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(freebsdlike)]
CCAR_OFLOW;
// Bitmasks for use with ControlFlags to select specific settings
@@ -944,58 +662,35 @@ libc_bitflags! {
/// Flags for setting any local modes
pub struct LocalFlags: tcflag_t {
#[cfg(not(target_os = "redox"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
ECHOKE;
ECHOE;
ECHOK;
ECHO;
ECHONL;
#[cfg(not(target_os = "redox"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(not(any(target_os = "redox", target_os = "cygwin")))]
ECHOPRT;
#[cfg(not(target_os = "redox"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
ECHOCTL;
ISIG;
ICANON;
#[cfg(any(target_os = "freebsd",
target_os = "dragonfly",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(bsd)]
ALTWERASE;
IEXTEN;
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(not(any(target_os = "redox", target_os = "haiku", target_os = "aix", target_os = "cygwin")))]
EXTPROC;
TOSTOP;
#[cfg(not(target_os = "redox"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
FLUSHO;
#[cfg(any(target_os = "freebsd",
target_os = "dragonfly",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(bsd)]
NOKERNINFO;
#[cfg(not(target_os = "redox"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(not(any(target_os = "redox", target_os = "cygwin")))]
PENDIN;
NOFLSH;
}
}
cfg_if! {
if #[cfg(any(target_os = "freebsd",
target_os = "dragonfly",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"))] {
if #[cfg(bsd)] {
/// Get input baud rate (see
/// [cfgetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetispeed.html)).
///
@@ -1128,7 +823,6 @@ pub fn cfmakeraw(termios: &mut Termios) {
///
/// Note that this is a non-standard function, available on FreeBSD.
#[cfg(target_os = "freebsd")]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn cfmakesane(termios: &mut Termios) {
let inner_termios = unsafe { termios.get_libc_termios_mut() };
unsafe {
@@ -1143,10 +837,12 @@ pub fn cfmakesane(termios: &mut Termios) {
/// `tcgetattr()` returns a `Termios` structure with the current configuration for a port. Modifying
/// this structure *will not* reconfigure the port, instead the modifications should be done to
/// the `Termios` structure and then the port should be reconfigured using `tcsetattr()`.
pub fn tcgetattr(fd: RawFd) -> Result<Termios> {
pub fn tcgetattr<Fd: AsFd>(fd: Fd) -> Result<Termios> {
let mut termios = mem::MaybeUninit::uninit();
let res = unsafe { libc::tcgetattr(fd, termios.as_mut_ptr()) };
let res = unsafe {
libc::tcgetattr(fd.as_fd().as_raw_fd(), termios.as_mut_ptr())
};
Errno::result(res)?;
@@ -1159,18 +855,26 @@ pub fn tcgetattr(fd: RawFd) -> Result<Termios> {
/// `tcsetattr()` reconfigures the given port based on a given `Termios` structure. This change
/// takes affect at a time specified by `actions`. Note that this function may return success if
/// *any* of the parameters were successfully set, not only if all were set successfully.
pub fn tcsetattr(fd: RawFd, actions: SetArg, termios: &Termios) -> Result<()> {
pub fn tcsetattr<Fd: AsFd>(
fd: Fd,
actions: SetArg,
termios: &Termios,
) -> Result<()> {
let inner_termios = termios.get_libc_termios();
Errno::result(unsafe {
libc::tcsetattr(fd, actions as c_int, &*inner_termios)
libc::tcsetattr(
fd.as_fd().as_raw_fd(),
actions as c_int,
&*inner_termios,
)
})
.map(drop)
}
/// Block until all output data is written (see
/// [tcdrain(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcdrain.html)).
pub fn tcdrain(fd: RawFd) -> Result<()> {
Errno::result(unsafe { libc::tcdrain(fd) }).map(drop)
pub fn tcdrain<Fd: AsFd>(fd: Fd) -> Result<()> {
Errno::result(unsafe { libc::tcdrain(fd.as_fd().as_raw_fd()) }).map(drop)
}
/// Suspend or resume the transmission or reception of data (see
@@ -1178,8 +882,11 @@ pub fn tcdrain(fd: RawFd) -> Result<()> {
///
/// `tcflow()` suspends of resumes the transmission or reception of data for the given port
/// depending on the value of `action`.
pub fn tcflow(fd: RawFd, action: FlowArg) -> Result<()> {
Errno::result(unsafe { libc::tcflow(fd, action as c_int) }).map(drop)
pub fn tcflow<Fd: AsFd>(fd: Fd, action: FlowArg) -> Result<()> {
Errno::result(unsafe {
libc::tcflow(fd.as_fd().as_raw_fd(), action as c_int)
})
.map(drop)
}
/// Discard data in the output or input queue (see
@@ -1187,8 +894,11 @@ pub fn tcflow(fd: RawFd, action: FlowArg) -> Result<()> {
///
/// `tcflush()` will discard data for a terminal port in the input queue, output queue, or both
/// depending on the value of `action`.
pub fn tcflush(fd: RawFd, action: FlushArg) -> Result<()> {
Errno::result(unsafe { libc::tcflush(fd, action as c_int) }).map(drop)
pub fn tcflush<Fd: AsFd>(fd: Fd, action: FlushArg) -> Result<()> {
Errno::result(unsafe {
libc::tcflush(fd.as_fd().as_raw_fd(), action as c_int)
})
.map(drop)
}
/// Send a break for a specific duration (see
@@ -1196,32 +906,20 @@ pub fn tcflush(fd: RawFd, action: FlushArg) -> Result<()> {
///
/// When using asynchronous data transmission `tcsendbreak()` will transmit a continuous stream
/// of zero-valued bits for an implementation-defined duration.
pub fn tcsendbreak(fd: RawFd, duration: c_int) -> Result<()> {
Errno::result(unsafe { libc::tcsendbreak(fd, duration) }).map(drop)
pub fn tcsendbreak<Fd: AsFd>(fd: Fd, duration: c_int) -> Result<()> {
Errno::result(unsafe {
libc::tcsendbreak(fd.as_fd().as_raw_fd(), duration)
})
.map(drop)
}
feature! {
#![feature = "process"]
/// Get the session controlled by the given terminal (see
/// [tcgetsid(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetsid.html)).
pub fn tcgetsid(fd: RawFd) -> Result<Pid> {
let res = unsafe { libc::tcgetsid(fd) };
pub fn tcgetsid<Fd: AsFd>(fd: Fd) -> Result<Pid> {
let res = unsafe { libc::tcgetsid(fd.as_fd().as_raw_fd()) };
Errno::result(res).map(Pid::from_raw)
}
}
#[cfg(test)]
mod test {
use super::*;
use std::convert::TryFrom;
#[test]
fn try_from() {
assert_eq!(Ok(BaudRate::B0), BaudRate::try_from(libc::B0));
#[cfg(not(target_os = "haiku"))]
BaudRate::try_from(999999999).expect_err("assertion failed");
#[cfg(target_os = "haiku")]
BaudRate::try_from(99).expect_err("assertion failed");
}
}
+76 -135
View File
@@ -1,8 +1,10 @@
#[cfg_attr(any(target_env = "musl", target_env = "ohos"), 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};
use std::convert::From;
use std::time::Duration;
use std::{cmp, fmt, ops};
@@ -18,7 +20,7 @@ const fn zero_init_timespec() -> timespec {
all(
any(
target_os = "freebsd",
target_os = "illumos",
solarish,
target_os = "linux",
target_os = "netbsd"
),
@@ -88,21 +90,19 @@ pub(crate) mod timer {
Interval(TimeSpec),
}
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(linux_android)]
bitflags! {
/// Flags that are used for arming the timer.
#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct TimerSetTimeFlags: libc::c_int {
const TFD_TIMER_ABSTIME = libc::TFD_TIMER_ABSTIME;
const TFD_TIMER_CANCEL_ON_SET = libc::TFD_TIMER_CANCEL_ON_SET;
}
}
#[cfg(any(
target_os = "freebsd",
target_os = "netbsd",
target_os = "dragonfly",
target_os = "illumos"
))]
#[cfg(any(freebsdlike, target_os = "netbsd", solarish))]
bitflags! {
/// Flags that are used for arming the timer.
#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct TimerSetTimeFlags: libc::c_int {
const TFD_TIMER_ABSTIME = libc::TIMER_ABSTIME;
}
@@ -256,13 +256,15 @@ impl PartialOrd for TimeSpec {
impl TimeValLike for TimeSpec {
#[inline]
#[cfg_attr(any(target_env = "musl", target_env = "ohos"), 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!(
(TS_MIN_SECONDS..=TS_MAX_SECONDS).contains(&seconds),
"TimeSpec out of bounds; seconds={}",
seconds
"TimeSpec out of bounds; seconds={seconds}",
);
let mut ts = zero_init_timespec();
ts.tv_sec = seconds as time_t;
@@ -290,7 +292,10 @@ impl TimeValLike for TimeSpec {
/// Makes a new `TimeSpec` with given number of nanoseconds.
#[inline]
#[cfg_attr(any(target_env = "musl", target_env = "ohos"), 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);
@@ -332,8 +337,22 @@ impl TimeValLike for TimeSpec {
}
impl TimeSpec {
/// Leave the timestamp unchanged.
#[cfg(not(target_os = "redox"))]
// At the time of writing this PR, redox does not support this feature
pub const UTIME_OMIT: TimeSpec =
TimeSpec::new(0, libc::UTIME_OMIT as timespec_tv_nsec_t);
/// Update the timestamp to `Now`
// At the time of writing this PR, redox does not support this feature
#[cfg(not(target_os = "redox"))]
pub const UTIME_NOW: TimeSpec =
TimeSpec::new(0, libc::UTIME_NOW as timespec_tv_nsec_t);
/// Construct a new `TimeSpec` from its components
#[cfg_attr(any(target_env = "musl", target_env = "ohos"), 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;
@@ -349,7 +368,10 @@ impl TimeSpec {
}
}
#[cfg_attr(any(target_env = "musl", target_env = "ohos"), 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
}
@@ -358,7 +380,10 @@ impl TimeSpec {
self.0.tv_nsec
}
#[cfg_attr(any(target_env = "musl", target_env = "ohos"), 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();
@@ -428,20 +453,20 @@ impl fmt::Display for TimeSpec {
let sec = abs.tv_sec();
write!(f, "{}", sign)?;
write!(f, "{sign}")?;
if abs.tv_nsec() == 0 {
if abs.tv_sec() == 1 {
write!(f, "{} second", sec)?;
if sec == 1 {
write!(f, "1 second")?;
} else {
write!(f, "{} seconds", sec)?;
write!(f, "{sec} seconds")?;
}
} else if abs.tv_nsec() % 1_000_000 == 0 {
write!(f, "{}.{:03} seconds", sec, abs.tv_nsec() / 1_000_000)?;
write!(f, "{sec}.{:03} seconds", abs.tv_nsec() / 1_000_000)?;
} else if abs.tv_nsec() % 1_000 == 0 {
write!(f, "{}.{:06} seconds", sec, abs.tv_nsec() / 1_000)?;
write!(f, "{sec}.{:06} seconds", abs.tv_nsec() / 1_000)?;
} else {
write!(f, "{}.{:09} seconds", sec, abs.tv_nsec())?;
write!(f, "{sec}.{:09} seconds", abs.tv_nsec())?;
}
Ok(())
@@ -497,10 +522,12 @@ impl TimeValLike for TimeVal {
fn seconds(seconds: i64) -> TimeVal {
assert!(
(TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&seconds),
"TimeVal out of bounds; seconds={}",
seconds
"TimeVal out of bounds; seconds={seconds}"
);
#[cfg_attr(any(target_env = "musl", target_env = "ohos"), 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,
@@ -525,7 +552,10 @@ impl TimeValLike for TimeVal {
(TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&secs),
"TimeVal out of bounds"
);
#[cfg_attr(any(target_env = "musl", target_env = "ohos"), 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,
@@ -543,7 +573,10 @@ impl TimeValLike for TimeVal {
(TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&secs),
"TimeVal out of bounds"
);
#[cfg_attr(any(target_env = "musl", target_env = "ohos"), 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,
@@ -580,7 +613,10 @@ impl TimeValLike for TimeVal {
impl TimeVal {
/// Construct a new `TimeVal` from its components
#[cfg_attr(any(target_env = "musl", target_env = "ohos"), 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,
@@ -596,7 +632,10 @@ impl TimeVal {
}
}
#[cfg_attr(any(target_env = "musl", target_env = "ohos"), 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
}
@@ -662,18 +701,18 @@ impl fmt::Display for TimeVal {
let sec = abs.tv_sec();
write!(f, "{}", sign)?;
write!(f, "{sign}")?;
if abs.tv_usec() == 0 {
if abs.tv_sec() == 1 {
write!(f, "{} second", sec)?;
if sec == 1 {
write!(f, "1 second")?;
} else {
write!(f, "{} seconds", sec)?;
write!(f, "{sec} seconds")?;
}
} else if abs.tv_usec() % 1000 == 0 {
write!(f, "{}.{:03} seconds", sec, abs.tv_usec() / 1000)?;
write!(f, "{sec}.{:03} seconds", abs.tv_usec() / 1000)?;
} else {
write!(f, "{}.{:06} seconds", sec, abs.tv_usec())?;
write!(f, "{sec}.{:06} seconds", abs.tv_usec())?;
}
Ok(())
@@ -711,101 +750,3 @@ fn mod_floor_64(this: i64, other: i64) -> i64 {
fn div_rem_64(this: i64, other: i64) -> (i64, i64) {
(this / other, this % other)
}
#[cfg(test)]
mod test {
use super::{TimeSpec, TimeVal, TimeValLike};
use std::time::Duration;
#[test]
pub fn test_timespec() {
assert_ne!(TimeSpec::seconds(1), TimeSpec::zero());
assert_eq!(
TimeSpec::seconds(1) + TimeSpec::seconds(2),
TimeSpec::seconds(3)
);
assert_eq!(
TimeSpec::minutes(3) + TimeSpec::seconds(2),
TimeSpec::seconds(182)
);
}
#[test]
pub fn test_timespec_from() {
let duration = Duration::new(123, 123_456_789);
let timespec = TimeSpec::nanoseconds(123_123_456_789);
assert_eq!(TimeSpec::from(duration), timespec);
assert_eq!(Duration::from(timespec), duration);
}
#[test]
pub fn test_timespec_neg() {
let a = TimeSpec::seconds(1) + TimeSpec::nanoseconds(123);
let b = TimeSpec::seconds(-1) + TimeSpec::nanoseconds(-123);
assert_eq!(a, -b);
}
#[test]
pub fn test_timespec_ord() {
assert_eq!(TimeSpec::seconds(1), TimeSpec::nanoseconds(1_000_000_000));
assert!(TimeSpec::seconds(1) < TimeSpec::nanoseconds(1_000_000_001));
assert!(TimeSpec::seconds(1) > TimeSpec::nanoseconds(999_999_999));
assert!(TimeSpec::seconds(-1) < TimeSpec::nanoseconds(-999_999_999));
assert!(TimeSpec::seconds(-1) > TimeSpec::nanoseconds(-1_000_000_001));
}
#[test]
pub fn test_timespec_fmt() {
assert_eq!(TimeSpec::zero().to_string(), "0 seconds");
assert_eq!(TimeSpec::seconds(42).to_string(), "42 seconds");
assert_eq!(TimeSpec::milliseconds(42).to_string(), "0.042 seconds");
assert_eq!(TimeSpec::microseconds(42).to_string(), "0.000042 seconds");
assert_eq!(
TimeSpec::nanoseconds(42).to_string(),
"0.000000042 seconds"
);
assert_eq!(TimeSpec::seconds(-86401).to_string(), "-86401 seconds");
}
#[test]
pub fn test_timeval() {
assert_ne!(TimeVal::seconds(1), TimeVal::zero());
assert_eq!(
TimeVal::seconds(1) + TimeVal::seconds(2),
TimeVal::seconds(3)
);
assert_eq!(
TimeVal::minutes(3) + TimeVal::seconds(2),
TimeVal::seconds(182)
);
}
#[test]
pub fn test_timeval_ord() {
assert_eq!(TimeVal::seconds(1), TimeVal::microseconds(1_000_000));
assert!(TimeVal::seconds(1) < TimeVal::microseconds(1_000_001));
assert!(TimeVal::seconds(1) > TimeVal::microseconds(999_999));
assert!(TimeVal::seconds(-1) < TimeVal::microseconds(-999_999));
assert!(TimeVal::seconds(-1) > TimeVal::microseconds(-1_000_001));
}
#[test]
pub fn test_timeval_neg() {
let a = TimeVal::seconds(1) + TimeVal::microseconds(123);
let b = TimeVal::seconds(-1) + TimeVal::microseconds(-123);
assert_eq!(a, -b);
}
#[test]
pub fn test_timeval_fmt() {
assert_eq!(TimeVal::zero().to_string(), "0 seconds");
assert_eq!(TimeVal::seconds(42).to_string(), "42 seconds");
assert_eq!(TimeVal::milliseconds(42).to_string(), "0.042 seconds");
assert_eq!(TimeVal::microseconds(42).to_string(), "0.000042 seconds");
assert_eq!(TimeVal::nanoseconds(1402).to_string(), "0.000001 seconds");
assert_eq!(TimeVal::seconds(-86401).to_string(), "-86401 seconds");
}
}
+1 -1
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.
+47 -21
View File
@@ -33,24 +33,34 @@ pub use crate::sys::time::timer::{Expiration, TimerSetTimeFlags};
use crate::unistd::read;
use crate::{errno::Errno, Result};
use libc::c_int;
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd};
/// A timerfd instance. This is also a file descriptor, you can feed it to
/// other interfaces consuming file descriptors, epoll for example.
/// other interfaces taking file descriptors as arguments, [`epoll`] for example.
///
/// [`epoll`]: crate::sys::epoll
#[derive(Debug)]
pub struct TimerFd {
fd: RawFd,
fd: OwnedFd,
}
impl AsRawFd for TimerFd {
fn as_raw_fd(&self) -> RawFd {
self.fd
impl AsFd for TimerFd {
fn as_fd(&self) -> BorrowedFd<'_> {
self.fd.as_fd()
}
}
impl FromRawFd for TimerFd {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
TimerFd { fd }
TimerFd {
fd: unsafe { OwnedFd::from_raw_fd(fd) },
}
}
}
impl From<TimerFd> for OwnedFd {
fn from(value: TimerFd) -> Self {
value.fd
}
}
@@ -97,7 +107,9 @@ impl TimerFd {
Errno::result(unsafe {
libc::timerfd_create(clockid as i32, flags.bits())
})
.map(|fd| Self { fd })
.map(|fd| Self {
fd: unsafe { OwnedFd::from_raw_fd(fd) },
})
}
/// Sets a new alarm on the timer.
@@ -107,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.
@@ -129,6 +141,13 @@ impl TimerFd {
/// Then the one shot TimeSpec and the delay TimeSpec of the delayed
/// interval are going to be interpreted as absolute.
///
/// # Cancel on a clock change
///
/// If you set a `TFD_TIMER_CANCEL_ON_SET` alongside `TFD_TIMER_ABSTIME`
/// and the clock for this timer is `CLOCK_REALTIME` or `CLOCK_REALTIME_ALARM`,
/// then this timer is marked as cancelable if the real-time clock undergoes
/// a discontinuous change.
///
/// # Disabling alarms
///
/// Note: Only one alarm can be set for any given timer. Setting a new alarm
@@ -145,7 +164,7 @@ impl TimerFd {
let timerspec: TimerSpec = expiration.into();
Errno::result(unsafe {
libc::timerfd_settime(
self.fd,
self.fd.as_fd().as_raw_fd(),
flags.bits(),
timerspec.as_ref(),
std::ptr::null_mut(),
@@ -159,7 +178,10 @@ impl TimerFd {
pub fn get(&self) -> Result<Option<Expiration>> {
let mut timerspec = TimerSpec::none();
Errno::result(unsafe {
libc::timerfd_gettime(self.fd, timerspec.as_mut())
libc::timerfd_gettime(
self.fd.as_fd().as_raw_fd(),
timerspec.as_mut(),
)
})
.map(|_| {
if timerspec.as_ref().it_interval.tv_sec == 0
@@ -179,7 +201,7 @@ impl TimerFd {
pub fn unset(&self) -> Result<()> {
Errno::result(unsafe {
libc::timerfd_settime(
self.fd,
self.fd.as_fd().as_raw_fd(),
TimerSetTimeFlags::empty().bits(),
TimerSpec::none().as_ref(),
std::ptr::null_mut(),
@@ -192,7 +214,10 @@ impl TimerFd {
///
/// Note: If the alarm is unset, then you will wait forever.
pub fn wait(&self) -> Result<()> {
while let Err(e) = read(self.fd, &mut [0u8; 8]) {
while let Err(e) = read(&self.fd, &mut [0u8; 8]) {
if e == Errno::ECANCELED {
break;
}
if e != Errno::EINTR {
return Err(e);
}
@@ -200,15 +225,16 @@ impl TimerFd {
Ok(())
}
}
impl Drop for TimerFd {
fn drop(&mut self) {
if !std::thread::panicking() {
let result = Errno::result(unsafe { libc::close(self.fd) });
if let Err(Errno::EBADF) = result {
panic!("close of TimerFd encountered EBADF");
}
/// Constructs a `TimerFd` wrapping an existing `OwnedFd`.
///
/// # Safety
///
/// `OwnedFd` is a valid `TimerFd`.
pub unsafe fn from_owned_fd(fd: OwnedFd) -> Self {
Self {
fd
}
}
}
+40 -103
View File
@@ -2,15 +2,14 @@
use crate::errno::Errno;
use crate::Result;
use libc::{self, c_int, c_void, off_t, size_t};
use libc::{self, c_int, off_t, size_t};
use std::io::{IoSlice, IoSliceMut};
use std::marker::PhantomData;
use std::os::unix::io::RawFd;
use std::os::unix::io::{AsFd, AsRawFd};
/// Low-level vectored write to a raw file descriptor
///
/// See also [writev(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/writev.html)
pub fn writev(fd: RawFd, iov: &[IoSlice<'_>]) -> Result<usize> {
pub fn writev<Fd: AsFd>(fd: Fd, iov: &[IoSlice<'_>]) -> Result<usize> {
// SAFETY: to quote the documentation for `IoSlice`:
//
// [IoSlice] is semantically a wrapper around a &[u8], but is
@@ -19,7 +18,11 @@ pub fn writev(fd: RawFd, iov: &[IoSlice<'_>]) -> Result<usize> {
//
// Because it is ABI compatible, a pointer cast here is valid
let res = unsafe {
libc::writev(fd, iov.as_ptr() as *const libc::iovec, iov.len() as c_int)
libc::writev(
fd.as_fd().as_raw_fd(),
iov.as_ptr().cast(),
iov.len() as c_int,
)
};
Errno::result(res).map(|r| r as usize)
@@ -31,10 +34,14 @@ pub fn writev(fd: RawFd, iov: &[IoSlice<'_>]) -> Result<usize> {
// Clippy doesn't know that we need to pass iov mutably only because the
// mutation happens after converting iov to a pointer
#[allow(clippy::needless_pass_by_ref_mut)]
pub fn readv(fd: RawFd, iov: &mut [IoSliceMut<'_>]) -> Result<usize> {
pub fn readv<Fd: AsFd>(fd: Fd, iov: &mut [IoSliceMut<'_>]) -> Result<usize> {
// SAFETY: same as in writev(), IoSliceMut is ABI-compatible with iovec
let res = unsafe {
libc::readv(fd, iov.as_ptr() as *const libc::iovec, iov.len() as c_int)
libc::readv(
fd.as_fd().as_raw_fd(),
iov.as_ptr().cast(),
iov.len() as c_int,
)
};
Errno::result(res).map(|r| r as usize)
@@ -46,17 +53,20 @@ pub fn readv(fd: RawFd, 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")))]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn pwritev(fd: RawFd, iov: &[IoSlice<'_>], offset: off_t) -> Result<usize> {
#[cfg(not(any(target_os = "redox", target_os = "haiku", target_os = "solaris", target_os = "cygwin")))]
pub fn pwritev<Fd: AsFd>(
fd: Fd,
iov: &[IoSlice<'_>],
offset: off_t,
) -> Result<usize> {
#[cfg(target_env = "uclibc")]
let offset = offset as libc::off64_t; // uclibc doesn't use off_t
// SAFETY: same as in writev()
let res = unsafe {
libc::pwritev(
fd,
iov.as_ptr() as *const libc::iovec,
fd.as_fd().as_raw_fd(),
iov.as_ptr().cast(),
iov.len() as c_int,
offset,
)
@@ -72,13 +82,12 @@ pub fn pwritev(fd: RawFd, iov: &[IoSlice<'_>], offset: off_t) -> Result<usize> {
/// changed.
///
/// See also: [`readv`](fn.readv.html) and [`pread`](fn.pread.html)
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[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)]
pub fn preadv(
fd: RawFd,
pub fn preadv<Fd: AsFd>(
fd: Fd,
iov: &mut [IoSliceMut<'_>],
offset: off_t,
) -> Result<usize> {
@@ -88,8 +97,8 @@ pub fn preadv(
// SAFETY: same as in readv()
let res = unsafe {
libc::preadv(
fd,
iov.as_ptr() as *const libc::iovec,
fd.as_fd().as_raw_fd(),
iov.as_ptr().cast(),
iov.len() as c_int,
offset,
)
@@ -102,11 +111,11 @@ pub fn preadv(
///
/// See also [pwrite(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pwrite.html)
// TODO: move to unistd
pub fn pwrite(fd: RawFd, buf: &[u8], offset: off_t) -> Result<usize> {
pub fn pwrite<Fd: AsFd>(fd: Fd, buf: &[u8], offset: off_t) -> Result<usize> {
let res = unsafe {
libc::pwrite(
fd,
buf.as_ptr() as *const c_void,
fd.as_fd().as_raw_fd(),
buf.as_ptr().cast(),
buf.len() as size_t,
offset,
)
@@ -119,11 +128,11 @@ pub fn pwrite(fd: RawFd, buf: &[u8], offset: off_t) -> Result<usize> {
///
/// See also [pread(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pread.html)
// TODO: move to unistd
pub fn pread(fd: RawFd, buf: &mut [u8], offset: off_t) -> Result<usize> {
pub fn pread<Fd: AsFd>(fd: Fd, buf: &mut [u8], offset: off_t) -> Result<usize> {
let res = unsafe {
libc::pread(
fd,
buf.as_mut_ptr() as *mut c_void,
fd.as_fd().as_raw_fd(),
buf.as_mut_ptr().cast(),
buf.len() as size_t,
offset,
)
@@ -140,8 +149,7 @@ pub fn pread(fd: RawFd, buf: &mut [u8], offset: off_t) -> Result<usize> {
/// therefore not represented in Rust by an actual slice as `IoSlice` is. It
/// is used with [`process_vm_readv`](fn.process_vm_readv.html)
/// and [`process_vm_writev`](fn.process_vm_writev.html).
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
#[repr(C)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct RemoteIoVec {
@@ -151,77 +159,6 @@ pub struct RemoteIoVec {
pub len: usize,
}
/// A vector of buffers.
///
/// Vectored I/O methods like [`writev`] and [`readv`] use this structure for
/// both reading and writing. Each `IoVec` specifies the base address and
/// length of an area in memory.
#[deprecated(
since = "0.24.0",
note = "`IoVec` is no longer used in the public interface, use `IoSlice` or `IoSliceMut` instead"
)]
#[repr(transparent)]
#[allow(renamed_and_removed_lints)]
#[allow(clippy::unknown_clippy_lints)]
// Clippy false positive: https://github.com/rust-lang/rust-clippy/issues/8867
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct IoVec<T>(pub(crate) libc::iovec, PhantomData<T>);
#[allow(deprecated)]
impl<T> IoVec<T> {
/// View the `IoVec` as a Rust slice.
#[deprecated(
since = "0.24.0",
note = "Use the `Deref` impl of `IoSlice` or `IoSliceMut` instead"
)]
#[inline]
pub fn as_slice(&self) -> &[u8] {
use std::slice;
unsafe {
slice::from_raw_parts(self.0.iov_base as *const u8, self.0.iov_len)
}
}
}
#[allow(deprecated)]
impl<'a> IoVec<&'a [u8]> {
/// Create an `IoVec` from a Rust slice.
#[deprecated(since = "0.24.0", note = "Use `IoSlice::new` instead")]
pub fn from_slice(buf: &'a [u8]) -> IoVec<&'a [u8]> {
IoVec(
libc::iovec {
iov_base: buf.as_ptr() as *mut c_void,
iov_len: buf.len() as size_t,
},
PhantomData,
)
}
}
#[allow(deprecated)]
impl<'a> IoVec<&'a mut [u8]> {
/// Create an `IoVec` from a mutable Rust slice.
#[deprecated(since = "0.24.0", note = "Use `IoSliceMut::new` instead")]
pub fn from_mut_slice(buf: &'a mut [u8]) -> IoVec<&'a mut [u8]> {
IoVec(
libc::iovec {
iov_base: buf.as_mut_ptr() as *mut c_void,
iov_len: buf.len() as size_t,
},
PhantomData,
)
}
}
// The only reason IoVec isn't automatically Send+Sync is because libc::iovec
// contains raw pointers.
#[allow(deprecated)]
unsafe impl<T> Send for IoVec<T> where T: Send {}
#[allow(deprecated)]
unsafe impl<T> Sync for IoVec<T> where T: Sync {}
feature! {
#![feature = "process"]
@@ -245,7 +182,7 @@ feature! {
/// [ptrace]: ../ptrace/index.html
/// [`IoSlice`]: https://doc.rust-lang.org/std/io/struct.IoSlice.html
/// [`RemoteIoVec`]: struct.RemoteIoVec.html
#[cfg(all(any(target_os = "linux", target_os = "android"), not(target_env = "uclibc")))]
#[cfg(all(linux_android, not(target_env = "uclibc")))]
pub fn process_vm_writev(
pid: crate::unistd::Pid,
local_iov: &[IoSlice<'_>],
@@ -253,8 +190,8 @@ pub fn process_vm_writev(
{
let res = unsafe {
libc::process_vm_writev(pid.into(),
local_iov.as_ptr() as *const libc::iovec, local_iov.len() as libc::c_ulong,
remote_iov.as_ptr() as *const libc::iovec, remote_iov.len() as libc::c_ulong, 0)
local_iov.as_ptr().cast(), local_iov.len() as libc::c_ulong,
remote_iov.as_ptr().cast(), remote_iov.len() as libc::c_ulong, 0)
};
Errno::result(res).map(|r| r as usize)
@@ -280,7 +217,7 @@ pub fn process_vm_writev(
/// [`ptrace`]: ../ptrace/index.html
/// [`IoSliceMut`]: https://doc.rust-lang.org/std/io/struct.IoSliceMut.html
/// [`RemoteIoVec`]: struct.RemoteIoVec.html
#[cfg(all(any(target_os = "linux", target_os = "android"), not(target_env = "uclibc")))]
#[cfg(all(linux_android, not(target_env = "uclibc")))]
pub fn process_vm_readv(
pid: crate::unistd::Pid,
local_iov: &mut [IoSliceMut<'_>],
@@ -288,8 +225,8 @@ pub fn process_vm_readv(
{
let res = unsafe {
libc::process_vm_readv(pid.into(),
local_iov.as_ptr() as *const libc::iovec, local_iov.len() as libc::c_ulong,
remote_iov.as_ptr() as *const libc::iovec, remote_iov.len() as libc::c_ulong, 0)
local_iov.as_ptr().cast(), local_iov.len() as libc::c_ulong,
remote_iov.as_ptr().cast(), remote_iov.len() as libc::c_ulong, 0)
};
Errno::result(res).map(|r| r as usize)
+1 -22
View File
@@ -37,7 +37,7 @@ impl UtsName {
}
/// NIS or YP domain name of this machine.
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(linux_android)]
pub fn domainname(&self) -> &OsStr {
cast_and_trim(&self.0.domainname)
}
@@ -62,24 +62,3 @@ fn cast_and_trim(slice: &[c_char]) -> &OsStr {
OsStr::from_bytes(bytes)
}
#[cfg(test)]
mod test {
#[cfg(target_os = "linux")]
#[test]
pub fn test_uname_linux() {
assert_eq!(super::uname().unwrap().sysname(), "Linux");
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[test]
pub fn test_uname_darwin() {
assert_eq!(super::uname().unwrap().sysname(), "Darwin");
}
#[cfg(target_os = "freebsd")]
#[test]
pub fn test_uname_freebsd() {
assert_eq!(super::uname().unwrap().sysname(), "FreeBSD");
}
}
+32 -39
View File
@@ -10,7 +10,7 @@ use std::convert::TryFrom;
target_os = "android",
all(target_os = "linux", not(target_env = "uclibc")),
))]
use std::os::unix::io::RawFd;
use std::os::unix::io::{AsRawFd, BorrowedFd};
libc_bitflags!(
/// Controls the behavior of [`waitpid`].
@@ -24,53 +24,41 @@ libc_bitflags!(
/// [`SIGSTOP`](crate::sys::signal::Signal::SIGSTOP) signal.
WUNTRACED;
/// Report the status of selected processes which have terminated.
#[cfg(any(target_os = "android",
#[cfg(any(linux_android,
apple_targets,
target_os = "freebsd",
target_os = "haiku",
target_os = "ios",
target_os = "linux",
target_os = "redox",
target_os = "macos",
target_os = "netbsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
WEXITED;
/// Report the status of selected processes that have continued from a
/// job control stop by receiving a
/// [`SIGCONT`](crate::sys::signal::Signal::SIGCONT) signal.
WCONTINUED;
/// An alias for WUNTRACED.
#[cfg(any(target_os = "android",
#[cfg(any(linux_android,
apple_targets,
target_os = "freebsd",
target_os = "haiku",
target_os = "ios",
target_os = "linux",
target_os = "redox",
target_os = "macos",
target_os = "netbsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
WSTOPPED;
/// Don't reap, just poll status.
#[cfg(any(target_os = "android",
#[cfg(any(linux_android,
apple_targets,
target_os = "freebsd",
target_os = "haiku",
target_os = "ios",
target_os = "linux",
target_os = "redox",
target_os = "macos",
target_os = "netbsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
WNOWAIT;
/// Don't wait on children of other threads in this group
#[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(any(linux_android, target_os = "redox"))]
__WNOTHREAD;
/// Wait on all children, regardless of type
#[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(any(linux_android, target_os = "redox"))]
__WALL;
/// Wait for "clone" children only.
#[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(any(linux_android, target_os = "redox"))]
__WCLONE;
}
);
@@ -107,16 +95,14 @@ pub enum WaitStatus {
///
/// [`nix::sys::ptrace`]: ../ptrace/index.html
/// [`ptrace`(2)]: https://man7.org/linux/man-pages/man2/ptrace.2.html
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
PtraceEvent(Pid, Signal, c_int),
/// The traced process was stopped by execution of a system call,
/// and `PTRACE_O_TRACESYSGOOD` is in effect. See [`ptrace`(2)] for
/// more information.
///
/// [`ptrace`(2)]: https://man7.org/linux/man-pages/man2/ptrace.2.html
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(linux_android)]
PtraceSyscall(Pid),
/// The process was previously stopped but has resumed execution
/// after receiving a `SIGCONT` signal. This is only reported if
@@ -139,7 +125,7 @@ impl WaitStatus {
Some(p)
}
StillAlive => None,
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(linux_android)]
PtraceEvent(p, _, _) | PtraceSyscall(p) => Some(p),
}
}
@@ -173,7 +159,7 @@ fn stop_signal(status: i32) -> Result<Signal> {
Signal::try_from(libc::WSTOPSIG(status))
}
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(linux_android)]
fn syscall_stop(status: i32) -> bool {
// From ptrace(2), setting PTRACE_O_TRACESYSGOOD has the effect
// of delivering SIGTRAP | 0x80 as the signal number for syscall
@@ -182,7 +168,7 @@ fn syscall_stop(status: i32) -> bool {
libc::WSTOPSIG(status) == libc::SIGTRAP | 0x80
}
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(linux_android)]
fn stop_additional(status: i32) -> c_int {
(status >> 16) as c_int
}
@@ -216,7 +202,7 @@ impl WaitStatus {
WaitStatus::Signaled(pid, term_signal(status)?, dumped_core(status))
} else if stopped(status) {
cfg_if! {
if #[cfg(any(target_os = "android", target_os = "linux"))] {
if #[cfg(linux_android)] {
fn decode_stopped(pid: Pid, status: i32) -> Result<WaitStatus> {
let status_additional = stop_additional(status);
Ok(if syscall_stop(status) {
@@ -259,7 +245,7 @@ impl WaitStatus {
all(target_os = "linux", not(target_env = "uclibc")),
))]
unsafe fn from_siginfo(siginfo: &libc::siginfo_t) -> Result<WaitStatus> {
let si_pid = siginfo.si_pid();
let si_pid = unsafe { siginfo.si_pid() };
if si_pid == 0 {
return Ok(WaitStatus::StillAlive);
}
@@ -267,7 +253,7 @@ impl WaitStatus {
assert_eq!(siginfo.si_signo, libc::SIGCHLD);
let pid = Pid::from_raw(si_pid);
let si_status = siginfo.si_status();
let si_status = unsafe { siginfo.si_status() };
let status = match siginfo.si_code {
libc::CLD_EXITED => WaitStatus::Exited(pid, si_status),
@@ -280,7 +266,7 @@ impl WaitStatus {
WaitStatus::Stopped(pid, Signal::try_from(si_status)?)
}
libc::CLD_CONTINUED => WaitStatus::Continued(pid),
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(linux_android)]
libc::CLD_TRAPPED => {
if si_status == libc::SIGTRAP | 0x80 {
WaitStatus::PtraceSyscall(pid)
@@ -343,8 +329,8 @@ pub fn wait() -> Result<WaitStatus> {
target_os = "haiku",
all(target_os = "linux", not(target_env = "uclibc")),
))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Id {
#[derive(Debug)]
pub enum Id<'fd> {
/// Wait for any child
All,
/// Wait for the child whose process ID matches the given PID
@@ -354,8 +340,12 @@ pub enum Id {
/// If the PID is zero, the caller's process group is used since Linux 5.4.
PGid(Pid),
/// Wait for the child referred to by the given PID file descriptor
#[cfg(any(target_os = "android", target_os = "linux"))]
PIDFd(RawFd),
#[cfg(linux_android)]
PIDFd(BorrowedFd<'fd>),
/// A helper variant to resolve the unused parameter (`'fd`) problem on platforms
/// other than Linux and Android.
#[doc(hidden)]
_Unreachable(std::marker::PhantomData<&'fd std::convert::Infallible>),
}
/// Wait for a process to change status
@@ -372,8 +362,11 @@ pub fn waitid(id: Id, flags: WaitPidFlag) -> Result<WaitStatus> {
Id::All => (libc::P_ALL, 0),
Id::Pid(pid) => (libc::P_PID, pid.as_raw() as libc::id_t),
Id::PGid(pid) => (libc::P_PGID, pid.as_raw() as libc::id_t),
#[cfg(any(target_os = "android", target_os = "linux"))]
Id::PIDFd(fd) => (libc::P_PIDFD, fd as libc::id_t),
#[cfg(linux_android)]
Id::PIDFd(fd) => (libc::P_PIDFD, fd.as_raw_fd() as libc::id_t),
Id::_Unreachable(_) => {
unreachable!("This variant could never be constructed")
}
};
let siginfo = unsafe {
+293
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,
}
}
+123 -118
View File
@@ -1,11 +1,6 @@
//! Sleep, query system clocks, and set system clock
use crate::sys::time::TimeSpec;
#[cfg(any(
target_os = "freebsd",
target_os = "dragonfly",
target_os = "linux",
target_os = "android",
target_os = "emscripten",
))]
#[cfg(any(freebsdlike, linux_android, target_os = "emscripten"))]
#[cfg(feature = "process")]
use crate::unistd::Pid;
use crate::{Errno, Result};
@@ -14,8 +9,7 @@ use std::mem::MaybeUninit;
/// Clock identifier
///
/// Newtype pattern around `clockid_t` (which is just alias). It prevents bugs caused by
/// accidentally passing wrong value.
/// Newtype pattern around [`libc::clockid_t`].
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct ClockId(clockid_t);
@@ -28,14 +22,7 @@ impl ClockId {
feature! {
#![feature = "process"]
/// Returns `ClockId` of a `pid` CPU-time clock
#[cfg(any(
target_os = "freebsd",
target_os = "dragonfly",
target_os = "linux",
target_os = "android",
target_os = "emscripten",
))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(any(freebsdlike, linux_android, target_os = "emscripten"))]
pub fn pid_cpu_clock_id(pid: Pid) -> Result<Self> {
clock_getcpuclockid(pid)
}
@@ -43,7 +30,6 @@ impl ClockId {
/// Returns resolution of the clock id
#[cfg(not(target_os = "redox"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn res(self) -> Result<TimeSpec> {
clock_getres(self)
}
@@ -55,12 +41,12 @@ impl ClockId {
/// Sets time to `timespec` on the clock id
#[cfg(not(any(
target_os = "macos",
target_os = "ios",
target_os = "tvos",
target_os = "watchos",
target_os = "redox",
target_os = "hermit",
target_os = "hermit"
)))]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn set_time(self, timespec: TimeSpec) -> Result<()> {
clock_settime(self, timespec)
}
@@ -70,135 +56,106 @@ impl ClockId {
self.0
}
#[cfg(any(
target_os = "android",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "linux"
))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(any(linux_android, target_os = "emscripten", target_os = "fuchsia"))]
/// Starts at zero when the kernel boots and increments monotonically in SI seconds while the
/// machine is running.
pub const CLOCK_BOOTTIME: ClockId = ClockId(libc::CLOCK_BOOTTIME);
#[cfg(any(
target_os = "android",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "linux"
))]
#[cfg_attr(docsrs, doc(cfg(all())))]
/// Like [`CLOCK_BOOTTIME`](ClockId::CLOCK_BOOTTIME), but will wake the system if it is
/// suspended..
#[cfg(any(linux_android, target_os = "emscripten", target_os = "fuchsia"))]
pub const CLOCK_BOOTTIME_ALARM: ClockId =
ClockId(libc::CLOCK_BOOTTIME_ALARM);
/// Increments in SI seconds.
pub const CLOCK_MONOTONIC: ClockId = ClockId(libc::CLOCK_MONOTONIC);
#[cfg(any(
target_os = "android",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "linux"
))]
#[cfg_attr(docsrs, doc(cfg(all())))]
/// Like [`CLOCK_MONOTONIC`](ClockId::CLOCK_MONOTONIC), but optimized for execution time at the expense of accuracy.
#[cfg(any(linux_android, target_os = "emscripten", target_os = "fuchsia"))]
pub const CLOCK_MONOTONIC_COARSE: ClockId =
ClockId(libc::CLOCK_MONOTONIC_COARSE);
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(freebsdlike)]
/// Like [`CLOCK_MONOTONIC`](ClockId::CLOCK_MONOTONIC), but optimized for execution time at the expense of accuracy.
pub const CLOCK_MONOTONIC_FAST: ClockId =
ClockId(libc::CLOCK_MONOTONIC_FAST);
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(freebsdlike)]
/// Like [`CLOCK_MONOTONIC`](ClockId::CLOCK_MONOTONIC), but optimized for accuracy at the expense of execution time.
pub const CLOCK_MONOTONIC_PRECISE: ClockId =
ClockId(libc::CLOCK_MONOTONIC_PRECISE);
#[cfg(any(
target_os = "android",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "linux"
))]
#[cfg_attr(docsrs, doc(cfg(all())))]
/// Similar to [`CLOCK_MONOTONIC`](ClockId::CLOCK_MONOTONIC), but provides access to a raw
/// hardware-based time that is not subject to NTP adjustments.
#[cfg(any(linux_android, target_os = "emscripten", target_os = "fuchsia"))]
pub const CLOCK_MONOTONIC_RAW: ClockId = ClockId(libc::CLOCK_MONOTONIC_RAW);
#[cfg(any(
target_os = "android",
linux_android,
apple_targets,
freebsdlike,
target_os = "emscripten",
target_os = "fuchsia",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "redox",
target_os = "linux"
))]
#[cfg_attr(docsrs, doc(cfg(all())))]
/// Returns the execution time of the calling process.
pub const CLOCK_PROCESS_CPUTIME_ID: ClockId =
ClockId(libc::CLOCK_PROCESS_CPUTIME_ID);
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(freebsdlike)]
/// Increments when the CPU is running in user or kernel mode
pub const CLOCK_PROF: ClockId = ClockId(libc::CLOCK_PROF);
/// Increments as a wall clock should.
pub const CLOCK_REALTIME: ClockId = ClockId(libc::CLOCK_REALTIME);
#[cfg(any(
target_os = "android",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "linux"
))]
#[cfg_attr(docsrs, doc(cfg(all())))]
/// Like [`CLOCK_REALTIME`](ClockId::CLOCK_REALTIME), but not settable.
#[cfg(any(linux_android, target_os = "emscripten", target_os = "fuchsia"))]
pub const CLOCK_REALTIME_ALARM: ClockId =
ClockId(libc::CLOCK_REALTIME_ALARM);
#[cfg(any(
target_os = "android",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "linux"
))]
#[cfg_attr(docsrs, doc(cfg(all())))]
/// Like [`CLOCK_REALTIME`](ClockId::CLOCK_REALTIME), but optimized for execution time at the expense of accuracy.
#[cfg(any(linux_android, target_os = "emscripten", target_os = "fuchsia"))]
pub const CLOCK_REALTIME_COARSE: ClockId =
ClockId(libc::CLOCK_REALTIME_COARSE);
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(freebsdlike)]
/// Like [`CLOCK_REALTIME`](ClockId::CLOCK_REALTIME), but optimized for execution time at the expense of accuracy.
pub const CLOCK_REALTIME_FAST: ClockId = ClockId(libc::CLOCK_REALTIME_FAST);
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(freebsdlike)]
/// Like [`CLOCK_REALTIME`](ClockId::CLOCK_REALTIME), but optimized for accuracy at the expense of execution time.
pub const CLOCK_REALTIME_PRECISE: ClockId =
ClockId(libc::CLOCK_REALTIME_PRECISE);
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(freebsdlike)]
/// Returns the current second without performing a full time counter query, using an in-kernel
/// cached value of the current second.
pub const CLOCK_SECOND: ClockId = ClockId(libc::CLOCK_SECOND);
#[allow(missing_docs)] // Undocumented on Linux!
#[cfg(any(
target_os = "emscripten",
target_os = "fuchsia",
all(target_os = "linux", any(target_env = "musl", target_env = "ohos"))
all(
target_os = "linux",
any(target_env = "musl", target_env = "ohos")
)
))]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub const CLOCK_SGI_CYCLE: ClockId = ClockId(libc::CLOCK_SGI_CYCLE);
#[cfg(any(
target_os = "android",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "linux"
))]
#[cfg_attr(docsrs, doc(cfg(all())))]
/// International Atomic Time.
///
/// A nonsettable system-wide clock derived from wall-clock time but ignoring leap seconds.
#[cfg(any(linux_android, target_os = "emscripten", target_os = "fuchsia"))]
pub const CLOCK_TAI: ClockId = ClockId(libc::CLOCK_TAI);
#[cfg(any(
target_os = "android",
linux_android,
apple_targets,
freebsdlike,
target_os = "emscripten",
target_os = "fuchsia",
target_os = "ios",
target_os = "macos",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "linux"
))]
#[cfg_attr(docsrs, doc(cfg(all())))]
/// Returns the execution time of the calling thread.
pub const CLOCK_THREAD_CPUTIME_ID: ClockId =
ClockId(libc::CLOCK_THREAD_CPUTIME_ID);
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(freebsdlike)]
/// Starts at zero when the kernel boots and increments monotonically in SI seconds while the
/// machine is running.
pub const CLOCK_UPTIME: ClockId = ClockId(libc::CLOCK_UPTIME);
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(freebsdlike)]
/// Like [`CLOCK_UPTIME`](ClockId::CLOCK_UPTIME), but optimized for execution time at the expense of accuracy.
pub const CLOCK_UPTIME_FAST: ClockId = ClockId(libc::CLOCK_UPTIME_FAST);
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(freebsdlike)]
/// Like [`CLOCK_UPTIME`](ClockId::CLOCK_UPTIME), but optimized for accuracy at the expense of execution time.
pub const CLOCK_UPTIME_PRECISE: ClockId =
ClockId(libc::CLOCK_UPTIME_PRECISE);
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[cfg(freebsdlike)]
/// Increments only when the CPU is running in user mode on behalf of the calling process.
pub const CLOCK_VIRTUAL: ClockId = ClockId(libc::CLOCK_VIRTUAL);
}
@@ -223,7 +180,6 @@ impl std::fmt::Display for ClockId {
/// Get the resolution of the specified clock, (see
/// [clock_getres(2)](https://pubs.opengroup.org/onlinepubs/7908799/xsh/clock_getres.html)).
#[cfg(not(target_os = "redox"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn clock_getres(clock_id: ClockId) -> Result<TimeSpec> {
let mut c_time: MaybeUninit<libc::timespec> = MaybeUninit::uninit();
let ret =
@@ -247,12 +203,12 @@ pub fn clock_gettime(clock_id: ClockId) -> Result<TimeSpec> {
/// Set the time of the specified clock, (see
/// [clock_settime(2)](https://pubs.opengroup.org/onlinepubs/7908799/xsh/clock_settime.html)).
#[cfg(not(any(
target_os = "macos",
target_os = "ios",
target_os = "tvos",
target_os = "watchos",
target_os = "redox",
target_os = "hermit",
target_os = "hermit"
)))]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn clock_settime(clock_id: ClockId, timespec: TimeSpec) -> Result<()> {
let ret =
unsafe { libc::clock_settime(clock_id.as_raw(), timespec.as_ref()) };
@@ -261,13 +217,7 @@ pub fn clock_settime(clock_id: ClockId, timespec: TimeSpec) -> Result<()> {
/// Get the clock id of the specified process id, (see
/// [clock_getcpuclockid(3)](https://pubs.opengroup.org/onlinepubs/009695399/functions/clock_getcpuclockid.html)).
#[cfg(any(
target_os = "freebsd",
target_os = "dragonfly",
target_os = "linux",
target_os = "android",
target_os = "emscripten",
))]
#[cfg(any(freebsdlike, linux_android, target_os = "emscripten"))]
#[cfg(feature = "process")]
#[cfg_attr(docsrs, doc(cfg(feature = "process")))]
pub fn clock_getcpuclockid(pid: Pid) -> Result<ClockId> {
@@ -278,6 +228,61 @@ pub fn clock_getcpuclockid(pid: Pid) -> Result<ClockId> {
let res = unsafe { clk_id.assume_init() };
Ok(ClockId::from(res))
} else {
Err(Errno::from_i32(ret))
Err(Errno::from_raw(ret))
}
}
#[cfg(any(
linux_android,
solarish,
freebsdlike,
target_os = "netbsd",
target_os = "hurd",
target_os = "aix"
))]
libc_bitflags! {
/// Flags that are used for arming the timer.
pub struct ClockNanosleepFlags: libc::c_int {
/// Indicates that a requested time value should be treated as absolute instead of
/// relative.
TIMER_ABSTIME;
}
}
/// Suspend execution of this thread for the amount of time specified by `request`
/// and measured against the clock speficied by `clock_id`.
///
/// If `flags` is [`TIMER_ABSTIME`](ClockNanosleepFlags::TIMER_ABSTIME), this function will suspend
/// execution until the time value of clock_id reaches the absolute time specified by `request`. If
/// a signal is caught by a signal-catching function, or a signal causes the process to terminate,
/// this sleep is interrrupted.
///
/// see also [man 3 clock_nanosleep](https://pubs.opengroup.org/onlinepubs/009695399/functions/clock_nanosleep.html)
#[cfg(any(
linux_android,
solarish,
freebsdlike,
target_os = "netbsd",
target_os = "hurd",
target_os = "aix"
))]
pub fn clock_nanosleep(
clock_id: ClockId,
flags: ClockNanosleepFlags,
request: &TimeSpec,
) -> Result<TimeSpec> {
let mut remain = TimeSpec::new(0, 0);
let ret = unsafe {
libc::clock_nanosleep(
clock_id.as_raw(),
flags.bits(),
request.as_ref() as *const _,
remain.as_mut() as *mut _,
)
};
if ret == 0 {
Ok(remain)
} else {
Err(Errno::from_raw(ret))
}
}
+1541 -914
View File
File diff suppressed because it is too large Load Diff
+7 -7
View File
@@ -2,18 +2,18 @@ use cfg_if::cfg_if;
#[macro_export]
macro_rules! skip {
($($reason: expr),+) => {
($($reason: expr),+) => {{
use ::std::io::{self, Write};
let stderr = io::stderr();
let mut handle = stderr.lock();
writeln!(handle, $($reason),+).unwrap();
return;
}
}}
}
cfg_if! {
if #[cfg(any(target_os = "android", target_os = "linux"))] {
if #[cfg(linux_android)] {
#[macro_export] macro_rules! require_capability {
($name:expr, $capname:ident) => {
use ::caps::{Capability, CapSet, has_cap};
@@ -37,8 +37,8 @@ cfg_if! {
#[macro_export]
macro_rules! require_mount {
($name:expr) => {
use ::sysctl::{CtlValue, Sysctl};
use nix::unistd::Uid;
use sysctl::{CtlValue, Sysctl};
let ctl = ::sysctl::Ctl::new("vfs.usermount").unwrap();
if !Uid::current().is_root() && CtlValue::Int(0) == ctl.value().unwrap()
@@ -51,7 +51,7 @@ macro_rules! require_mount {
};
}
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
#[macro_export]
macro_rules! skip_if_cirrus {
($reason:expr) => {
@@ -65,7 +65,7 @@ macro_rules! skip_if_cirrus {
#[macro_export]
macro_rules! skip_if_jailed {
($name:expr) => {
use ::sysctl::{CtlValue, Sysctl};
use sysctl::{CtlValue, Sysctl};
let ctl = ::sysctl::Ctl::new("security.jail.jailed").unwrap();
if let CtlValue::Int(1) = ctl.value().unwrap() {
@@ -87,7 +87,7 @@ macro_rules! skip_if_not_root {
}
cfg_if! {
if #[cfg(any(target_os = "android", target_os = "linux"))] {
if #[cfg(linux_android)] {
#[macro_export] macro_rules! skip_if_seccomp {
($name:expr) => {
if let Ok(s) = std::fs::read_to_string("/proc/self/status") {
+6
View File
@@ -0,0 +1,6 @@
#[cfg(target_os = "linux")]
mod test_mount;
#[cfg(apple_targets)]
mod test_mount_apple;
#[cfg(target_os = "freebsd")]
mod test_nmount;
+189
View File
@@ -0,0 +1,189 @@
use std::fs::{self, File};
use std::io::{Read, Write};
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::fs::PermissionsExt;
use std::process::Command;
use libc::{EACCES, EROFS};
use nix::mount::{mount, umount, MsFlags};
use nix::sys::stat::{self, Mode};
use crate::*;
static SCRIPT_CONTENTS: &[u8] = b"#!/bin/sh
exit 23";
const EXPECTED_STATUS: i32 = 23;
const NONE: Option<&'static [u8]> = None;
#[test]
fn test_mount_tmpfs_without_flags_allows_rwx() {
require_capability!(
"test_mount_tmpfs_without_flags_allows_rwx",
CAP_SYS_ADMIN
);
let tempdir = tempfile::tempdir().unwrap();
mount(
NONE,
tempdir.path(),
Some(b"tmpfs".as_ref()),
MsFlags::empty(),
NONE,
)
.unwrap_or_else(|e| panic!("mount failed: {e}"));
let test_path = tempdir.path().join("test");
// Verify write.
fs::OpenOptions::new()
.create(true)
.write(true)
.mode((Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO).bits())
.open(&test_path)
.and_then(|mut f| f.write(SCRIPT_CONTENTS))
.unwrap_or_else(|e| panic!("write failed: {e}"));
// Verify read.
let mut buf = Vec::new();
File::open(&test_path)
.and_then(|mut f| f.read_to_end(&mut buf))
.unwrap_or_else(|e| panic!("read failed: {e}"));
assert_eq!(buf, SCRIPT_CONTENTS);
// while forking and unmounting prevent other child processes
let _m = FORK_MTX.lock();
// Verify execute.
assert_eq!(
EXPECTED_STATUS,
Command::new(&test_path)
.status()
.unwrap_or_else(|e| panic!("exec failed: {e}"))
.code()
.unwrap_or_else(|| panic!("child killed by signal"))
);
umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {e}"));
}
#[test]
fn test_mount_rdonly_disallows_write() {
require_capability!("test_mount_rdonly_disallows_write", CAP_SYS_ADMIN);
let tempdir = tempfile::tempdir().unwrap();
mount(
NONE,
tempdir.path(),
Some(b"tmpfs".as_ref()),
MsFlags::MS_RDONLY,
NONE,
)
.unwrap_or_else(|e| panic!("mount failed: {e}"));
// EROFS: Read-only file system
assert_eq!(
EROFS,
File::create(tempdir.path().join("test"))
.unwrap_err()
.raw_os_error()
.unwrap()
);
umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {e}"));
}
#[test]
fn test_mount_noexec_disallows_exec() {
require_capability!("test_mount_noexec_disallows_exec", CAP_SYS_ADMIN);
let tempdir = tempfile::tempdir().unwrap();
mount(
NONE,
tempdir.path(),
Some(b"tmpfs".as_ref()),
MsFlags::MS_NOEXEC,
NONE,
)
.unwrap_or_else(|e| panic!("mount failed: {e}"));
let test_path = tempdir.path().join("test");
fs::OpenOptions::new()
.create(true)
.write(true)
.mode((Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO).bits())
.open(&test_path)
.and_then(|mut f| f.write(SCRIPT_CONTENTS))
.unwrap_or_else(|e| panic!("write failed: {e}"));
// Verify that we cannot execute despite a+x permissions being set.
let mode = stat::Mode::from_bits_truncate(
fs::metadata(&test_path)
.map(|md| md.permissions().mode())
.unwrap_or_else(|e| panic!("metadata failed: {e}")),
);
assert!(
mode.contains(Mode::S_IXUSR | Mode::S_IXGRP | Mode::S_IXOTH),
"{:?} did not have execute permissions",
&test_path
);
// while forking and unmounting prevent other child processes
let _m = FORK_MTX.lock();
// EACCES: Permission denied
assert_eq!(
EACCES,
Command::new(&test_path)
.status()
.unwrap_err()
.raw_os_error()
.unwrap()
);
umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {e}"));
}
#[test]
fn test_mount_bind() {
require_capability!("test_mount_bind", CAP_SYS_ADMIN);
let tempdir = tempfile::tempdir().unwrap();
let file_name = "test";
{
let mount_point = tempfile::tempdir().unwrap();
mount(
Some(tempdir.path()),
mount_point.path(),
NONE,
MsFlags::MS_BIND,
NONE,
)
.unwrap_or_else(|e| panic!("mount failed: {e}"));
fs::OpenOptions::new()
.create(true)
.write(true)
.mode((Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO).bits())
.open(mount_point.path().join(file_name))
.and_then(|mut f| f.write(SCRIPT_CONTENTS))
.unwrap_or_else(|e| panic!("write failed: {e}"));
// wait for child processes to prevent EBUSY
let _m = FORK_MTX.lock();
umount(mount_point.path())
.unwrap_or_else(|e| panic!("umount failed: {e}"));
}
// Verify the file written in the mount shows up in source directory, even
// after unmounting.
let mut buf = Vec::new();
File::open(tempdir.path().join(file_name))
.and_then(|mut f| f.read_to_end(&mut buf))
.unwrap_or_else(|e| panic!("read failed: {e}"));
assert_eq!(buf, SCRIPT_CONTENTS);
}
+8
View File
@@ -0,0 +1,8 @@
use nix::errno::Errno;
use nix::mount::{mount, MntFlags};
#[test]
fn test_mount() {
let res = mount::<str, str, str>("", "", MntFlags::empty(), None);
assert_eq!(res, Err(Errno::ENOENT));
}
+54 -16
View File
@@ -7,16 +7,20 @@ mod test_signal;
// cases on DragonFly.
#[cfg(any(
target_os = "freebsd",
target_os = "ios",
all(target_os = "linux", not(target_env = "uclibc")),
target_os = "macos",
apple_targets,
all(
target_os = "linux",
not(any(target_env = "uclibc", target_env = "ohos"))
),
target_os = "netbsd"
))]
mod test_aio;
#[cfg(not(any(
target_os = "redox",
target_os = "fuchsia",
target_os = "haiku"
target_os = "haiku",
target_os = "hurd",
target_os = "cygwin"
)))]
mod test_ioctl;
#[cfg(not(target_os = "redox"))]
@@ -30,7 +34,7 @@ mod test_socket;
#[cfg(not(any(target_os = "redox")))]
mod test_sockopt;
mod test_stat;
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(linux_android)]
mod test_sysinfo;
#[cfg(not(any(
target_os = "redox",
@@ -41,20 +45,54 @@ mod test_termios;
mod test_uio;
mod test_wait;
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(linux_android)]
mod test_epoll;
#[cfg(target_os = "linux")]
mod test_fanotify;
#[cfg(target_os = "linux")]
mod test_inotify;
mod test_pthread;
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"
))]
#[cfg(any(linux_android, freebsdlike, netbsdlike, apple_targets))]
mod test_ptrace;
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(linux_android)]
mod test_timerfd;
#[cfg(all(
any(
target_os = "freebsd",
solarish,
target_os = "linux",
target_os = "netbsd"
),
feature = "time",
feature = "signal"
))]
mod test_timer;
#[cfg(bsd)]
mod test_event;
mod test_statvfs;
mod test_time;
mod test_utsname;
#[cfg(any(linux_android, freebsdlike, apple_targets, target_os = "openbsd"))]
mod test_statfs;
#[cfg(not(any(
target_os = "redox",
target_os = "fuchsia",
solarish,
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;
+153 -94
View File
@@ -1,7 +1,7 @@
use std::{
io::{Read, Seek, Write},
ops::Deref,
os::unix::io::AsRawFd,
os::unix::io::{AsFd, AsRawFd, BorrowedFd},
pin::Pin,
sync::atomic::{AtomicBool, Ordering},
thread, time,
@@ -21,9 +21,7 @@ use nix::{
};
use tempfile::tempfile;
lazy_static! {
pub static ref SIGNALED: AtomicBool = AtomicBool::new(false);
}
pub static SIGNALED: AtomicBool = AtomicBool::new(false);
extern "C" fn sigfunc(_: c_int) {
SIGNALED.store(true, Ordering::Relaxed);
@@ -47,8 +45,9 @@ mod aio_fsync {
#[test]
fn test_accessors() {
let f = tempfile().unwrap();
let aiocb = AioFsync::new(
1001,
f.as_fd(),
AioFsyncMode::O_SYNC,
42,
SigevNotify::SigevSignal {
@@ -56,7 +55,7 @@ mod aio_fsync {
si_value: 99,
},
);
assert_eq!(1001, aiocb.fd());
assert_eq!(f.as_raw_fd(), aiocb.fd().as_raw_fd());
assert_eq!(AioFsyncMode::O_SYNC, aiocb.mode());
assert_eq!(42, aiocb.priority());
let sev = aiocb.sigevent().sigevent();
@@ -69,21 +68,17 @@ mod aio_fsync {
// Skip on Linux, because Linux's AIO implementation can't detect errors
// synchronously
#[test]
#[cfg(any(target_os = "freebsd", target_os = "macos"))]
#[cfg_attr(any(target_os = "android", target_os = "linux"), ignore)]
fn error() {
use std::mem;
const INITIAL: &[u8] = b"abcdef123456";
// Create an invalid AioFsyncMode
let mode = unsafe { mem::transmute(666) };
let mode = unsafe { mem::transmute::<i32, AioFsyncMode>(666) };
let mut f = tempfile().unwrap();
f.write_all(INITIAL).unwrap();
let mut aiof = Box::pin(AioFsync::new(
f.as_raw_fd(),
mode,
0,
SigevNotify::SigevNone,
));
let mut aiof =
Box::pin(AioFsync::new(f.as_fd(), mode, 0, SigevNotify::SigevNone));
let err = aiof.as_mut().submit();
err.expect_err("assertion failed");
}
@@ -94,9 +89,8 @@ mod aio_fsync {
const INITIAL: &[u8] = b"abcdef123456";
let mut f = tempfile().unwrap();
f.write_all(INITIAL).unwrap();
let fd = f.as_raw_fd();
let mut aiof = Box::pin(AioFsync::new(
fd,
f.as_fd(),
AioFsyncMode::O_SYNC,
0,
SigevNotify::SigevNone,
@@ -112,9 +106,10 @@ mod aio_read {
#[test]
fn test_accessors() {
let f = tempfile().unwrap();
let mut rbuf = vec![0; 4];
let aiocb = AioRead::new(
1001,
f.as_fd(),
2, //offset
&mut rbuf,
42, //priority
@@ -123,7 +118,7 @@ mod aio_read {
si_value: 99,
},
);
assert_eq!(1001, aiocb.fd());
assert_eq!(f.as_raw_fd(), aiocb.fd().as_raw_fd());
assert_eq!(4, aiocb.nbytes());
assert_eq!(2, aiocb.offset());
assert_eq!(42, aiocb.priority());
@@ -142,7 +137,7 @@ mod aio_read {
let mut rbuf = vec![0; 4];
let mut f = tempfile().unwrap();
f.write_all(INITIAL).unwrap();
let fd = f.as_raw_fd();
let fd = f.as_fd();
let mut aior =
Box::pin(AioRead::new(fd, 2, &mut rbuf, 0, SigevNotify::SigevNone));
aior.as_mut().submit().unwrap();
@@ -159,14 +154,14 @@ mod aio_read {
// Skip on Linux, because Linux's AIO implementation can't detect errors
// synchronously
#[test]
#[cfg(any(target_os = "freebsd", target_os = "macos"))]
#[cfg(any(target_os = "freebsd", apple_targets))]
fn error() {
const INITIAL: &[u8] = b"abcdef123456";
let mut rbuf = vec![0; 4];
let mut f = tempfile().unwrap();
f.write_all(INITIAL).unwrap();
let mut aior = Box::pin(AioRead::new(
f.as_raw_fd(),
f.as_fd(),
-1, //an invalid offset
&mut rbuf,
0, //priority
@@ -186,7 +181,7 @@ mod aio_read {
let mut f = tempfile().unwrap();
f.write_all(INITIAL).unwrap();
{
let fd = f.as_raw_fd();
let fd = f.as_fd();
let mut aior = Box::pin(AioRead::new(
fd,
2,
@@ -213,7 +208,7 @@ mod aio_read {
let mut f = tempfile().unwrap();
f.write_all(INITIAL).unwrap();
{
let fd = f.as_raw_fd();
let fd = f.as_fd();
let mut aior =
AioRead::new(fd, 2, &mut rbuf, 0, SigevNotify::SigevNone);
let mut aior = unsafe { Pin::new_unchecked(&mut aior) };
@@ -236,12 +231,13 @@ mod aio_readv {
#[test]
fn test_accessors() {
let f = tempfile().unwrap();
let mut rbuf0 = vec![0; 4];
let mut rbuf1 = vec![0; 8];
let mut rbufs =
[IoSliceMut::new(&mut rbuf0), IoSliceMut::new(&mut rbuf1)];
let aiocb = AioReadv::new(
1001,
f.as_fd(),
2, //offset
&mut rbufs,
42, //priority
@@ -250,7 +246,7 @@ mod aio_readv {
si_value: 99,
},
);
assert_eq!(1001, aiocb.fd());
assert_eq!(f.as_raw_fd(), aiocb.fd().as_raw_fd());
assert_eq!(2, aiocb.iovlen());
assert_eq!(2, aiocb.offset());
assert_eq!(42, aiocb.priority());
@@ -272,7 +268,7 @@ mod aio_readv {
let mut f = tempfile().unwrap();
f.write_all(INITIAL).unwrap();
{
let fd = f.as_raw_fd();
let fd = f.as_fd();
let mut aior = Box::pin(AioReadv::new(
fd,
2,
@@ -299,9 +295,10 @@ mod aio_write {
#[test]
fn test_accessors() {
let f = tempfile().unwrap();
let wbuf = vec![0; 4];
let aiocb = AioWrite::new(
1001,
f.as_fd(),
2, //offset
&wbuf,
42, //priority
@@ -310,7 +307,7 @@ mod aio_write {
si_value: 99,
},
);
assert_eq!(1001, aiocb.fd());
assert_eq!(f.as_raw_fd(), aiocb.fd().as_raw_fd());
assert_eq!(4, aiocb.nbytes());
assert_eq!(2, aiocb.offset());
assert_eq!(42, aiocb.priority());
@@ -329,7 +326,7 @@ mod aio_write {
let f = tempfile().unwrap();
let mut aiow = Box::pin(AioWrite::new(
f.as_raw_fd(),
f.as_fd(),
0,
wbuf,
0,
@@ -358,18 +355,20 @@ mod aio_write {
let mut f = tempfile().unwrap();
f.write_all(INITIAL).unwrap();
let mut aiow = Box::pin(AioWrite::new(
f.as_raw_fd(),
2,
&wbuf,
0,
SigevNotify::SigevNone,
));
aiow.as_mut().submit().unwrap();
{
let mut aiow = Box::pin(AioWrite::new(
f.as_fd(),
2,
&wbuf,
0,
SigevNotify::SigevNone,
));
aiow.as_mut().submit().unwrap();
let err = poll_aio!(&mut aiow);
assert_eq!(err, Ok(()));
assert_eq!(aiow.as_mut().aio_return().unwrap(), wbuf.len());
let err = poll_aio!(&mut aiow);
assert_eq!(err, Ok(()));
assert_eq!(aiow.as_mut().aio_return().unwrap(), wbuf.len());
}
f.rewind().unwrap();
let len = f.read_to_end(&mut rbuf).unwrap();
@@ -388,19 +387,21 @@ mod aio_write {
let mut f = tempfile().unwrap();
f.write_all(INITIAL).unwrap();
let mut aiow = AioWrite::new(
f.as_raw_fd(),
2, //offset
&wbuf,
0, //priority
SigevNotify::SigevNone,
);
let mut aiow = unsafe { Pin::new_unchecked(&mut aiow) };
aiow.as_mut().submit().unwrap();
{
let mut aiow = AioWrite::new(
f.as_fd(),
2, //offset
&wbuf,
0, //priority
SigevNotify::SigevNone,
);
let mut aiow = unsafe { Pin::new_unchecked(&mut aiow) };
aiow.as_mut().submit().unwrap();
let err = poll_aio!(&mut aiow);
assert_eq!(err, Ok(()));
assert_eq!(aiow.as_mut().aio_return().unwrap(), wbuf.len());
let err = poll_aio!(&mut aiow);
assert_eq!(err, Ok(()));
assert_eq!(aiow.as_mut().aio_return().unwrap(), wbuf.len());
}
f.rewind().unwrap();
let len = f.read_to_end(&mut rbuf).unwrap();
@@ -413,12 +414,14 @@ mod aio_write {
// Skip on Linux, because Linux's AIO implementation can't detect errors
// synchronously
#[test]
#[cfg(any(target_os = "freebsd", target_os = "macos"))]
#[cfg_attr(any(target_os = "android", target_os = "linux"), ignore)]
fn error() {
// Not I/O safe! Deliberately create an invalid fd.
let fd = unsafe { BorrowedFd::borrow_raw(666) };
let wbuf = "CDEF".to_string().into_bytes();
let mut aiow = Box::pin(AioWrite::new(
666, // An invalid file descriptor
0, //offset
fd,
0, //offset
&wbuf,
0, //priority
SigevNotify::SigevNone,
@@ -437,11 +440,12 @@ mod aio_writev {
#[test]
fn test_accessors() {
let f = tempfile().unwrap();
let wbuf0 = vec![0; 4];
let wbuf1 = vec![0; 8];
let wbufs = [IoSlice::new(&wbuf0), IoSlice::new(&wbuf1)];
let aiocb = AioWritev::new(
1001,
f.as_fd(),
2, //offset
&wbufs,
42, //priority
@@ -450,7 +454,7 @@ mod aio_writev {
si_value: 99,
},
);
assert_eq!(1001, aiocb.fd());
assert_eq!(f.as_raw_fd(), aiocb.fd().as_raw_fd());
assert_eq!(2, aiocb.iovlen());
assert_eq!(2, aiocb.offset());
assert_eq!(42, aiocb.priority());
@@ -474,18 +478,20 @@ mod aio_writev {
let mut f = tempfile().unwrap();
f.write_all(INITIAL).unwrap();
let mut aiow = Box::pin(AioWritev::new(
f.as_raw_fd(),
1,
&wbufs,
0,
SigevNotify::SigevNone,
));
aiow.as_mut().submit().unwrap();
{
let mut aiow = Box::pin(AioWritev::new(
f.as_fd(),
1,
&wbufs,
0,
SigevNotify::SigevNone,
));
aiow.as_mut().submit().unwrap();
let err = poll_aio!(&mut aiow);
assert_eq!(err, Ok(()));
assert_eq!(aiow.as_mut().aio_return().unwrap(), wlen);
let err = poll_aio!(&mut aiow);
assert_eq!(err, Ok(()));
assert_eq!(aiow.as_mut().aio_return().unwrap(), wlen);
}
f.rewind().unwrap();
let len = f.read_to_end(&mut rbuf).unwrap();
@@ -500,7 +506,9 @@ mod aio_writev {
any(
all(target_env = "musl", target_arch = "x86_64"),
target_arch = "mips",
target_arch = "mips64"
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"
),
ignore
)]
@@ -521,22 +529,25 @@ fn sigev_signal() {
let mut f = tempfile().unwrap();
f.write_all(INITIAL).unwrap();
let mut aiow = Box::pin(AioWrite::new(
f.as_raw_fd(),
2, //offset
WBUF,
0, //priority
SigevNotify::SigevSignal {
signal: Signal::SIGUSR2,
si_value: 0, //TODO: validate in sigfunc
},
));
aiow.as_mut().submit().unwrap();
while !SIGNALED.load(Ordering::Relaxed) {
thread::sleep(time::Duration::from_millis(10));
{
let mut aiow = Box::pin(AioWrite::new(
f.as_fd(),
2, //offset
WBUF,
0, //priority
SigevNotify::SigevSignal {
signal: Signal::SIGUSR2,
si_value: 0, //TODO: validate in sigfunc
},
));
aiow.as_mut().submit().unwrap();
while !SIGNALED.load(Ordering::Relaxed) {
thread::sleep(time::Duration::from_millis(10));
}
assert_eq!(aiow.as_mut().aio_return().unwrap(), WBUF.len());
}
assert_eq!(aiow.as_mut().aio_return().unwrap(), WBUF.len());
f.rewind().unwrap();
let len = f.read_to_end(&mut rbuf).unwrap();
assert_eq!(len, EXPECT.len());
@@ -551,7 +562,7 @@ fn test_aio_cancel_all() {
let f = tempfile().unwrap();
let mut aiocb = Box::pin(AioWrite::new(
f.as_raw_fd(),
f.as_fd(),
0, //offset
wbuf,
0, //priority
@@ -561,7 +572,7 @@ fn test_aio_cancel_all() {
let err = aiocb.as_mut().error();
assert!(err == Ok(()) || err == Err(Errno::EINPROGRESS));
aio_cancel_all(f.as_raw_fd()).unwrap();
aio_cancel_all(f.as_fd()).unwrap();
// Wait for aiocb to complete, but don't care whether it succeeded
let _ = poll_aio!(&mut aiocb);
@@ -569,12 +580,6 @@ fn test_aio_cancel_all() {
}
#[test]
// On Cirrus on Linux, this test fails due to a glibc bug.
// https://github.com/nix-rust/nix/issues/1099
#[cfg_attr(target_os = "linux", ignore)]
// On Cirrus, aio_suspend is failing with EINVAL
// https://github.com/nix-rust/nix/issues/1361
#[cfg_attr(target_os = "macos", ignore)]
fn test_aio_suspend() {
const INITIAL: &[u8] = b"abcdef123456";
const WBUF: &[u8] = b"CDEFG";
@@ -585,7 +590,7 @@ fn test_aio_suspend() {
f.write_all(INITIAL).unwrap();
let mut wcb = Box::pin(AioWrite::new(
f.as_raw_fd(),
f.as_fd(),
2, //offset
WBUF,
0, //priority
@@ -593,7 +598,7 @@ fn test_aio_suspend() {
));
let mut rcb = Box::pin(AioRead::new(
f.as_raw_fd(),
f.as_fd(),
8, //offset
&mut rbuf,
0, //priority
@@ -610,7 +615,7 @@ fn test_aio_suspend() {
let r = aio_suspend(&cbbuf[..], Some(timeout));
match r {
Err(Errno::EINTR) => continue,
Err(e) => panic!("aio_suspend returned {:?}", e),
Err(e) => panic!("aio_suspend returned {e:?}"),
Ok(_) => (),
};
}
@@ -624,3 +629,57 @@ fn test_aio_suspend() {
assert_eq!(wcb.as_mut().aio_return().unwrap(), WBUF.len());
assert_eq!(rcb.as_mut().aio_return().unwrap(), rlen);
}
/// aio_suspend relies on casting Rust Aio* struct pointers to libc::aiocb
/// pointers. This test ensures that such casts are valid.
#[test]
fn casting() {
let sev = SigevNotify::SigevNone;
// Only safe because we'll never await the futures
let fd = unsafe { BorrowedFd::borrow_raw(666) };
let aiof = AioFsync::new(fd, AioFsyncMode::O_SYNC, 0, sev);
assert_eq!(
aiof.as_ref() as *const libc::aiocb,
&aiof as *const AioFsync as *const libc::aiocb
);
let mut rbuf = [];
let aior = AioRead::new(fd, 0, &mut rbuf, 0, sev);
assert_eq!(
aior.as_ref() as *const libc::aiocb,
&aior as *const AioRead as *const libc::aiocb
);
let wbuf = [];
let aiow = AioWrite::new(fd, 0, &wbuf, 0, sev);
assert_eq!(
aiow.as_ref() as *const libc::aiocb,
&aiow as *const AioWrite as *const libc::aiocb
);
}
#[cfg(target_os = "freebsd")]
#[test]
fn casting_vectored() {
use std::io::{IoSlice, IoSliceMut};
let sev = SigevNotify::SigevNone;
let mut rbuf = [];
let mut rbufs = [IoSliceMut::new(&mut rbuf)];
// Only safe because we'll never await the futures
let fd = unsafe { BorrowedFd::borrow_raw(666) };
let aiorv = AioReadv::new(fd, 0, &mut rbufs[..], 0, sev);
assert_eq!(
aiorv.as_ref() as *const libc::aiocb,
&aiorv as *const AioReadv as *const libc::aiocb
);
let wbuf = [];
let wbufs = [IoSlice::new(&wbuf)];
let aiowv = AioWritev::new(fd, 0, &wbufs, 0, sev);
assert_eq!(
aiowv.as_ref() as *const libc::aiocb,
&aiowv as *const AioWritev as *const libc::aiocb
);
}
+4 -4
View File
@@ -6,10 +6,10 @@
#[cfg(all(
not(target_env = "musl"),
not(target_env = "uclibc"),
not(target_env = "ohos"),
any(
target_os = "linux",
target_os = "ios",
target_os = "macos",
apple_targets,
target_os = "freebsd",
target_os = "netbsd"
)
@@ -17,7 +17,7 @@
fn test_drop() {
use nix::sys::aio::*;
use nix::sys::signal::*;
use std::os::unix::io::AsRawFd;
use std::os::unix::io::AsFd;
use tempfile::tempfile;
const WBUF: &[u8] = b"CDEF";
@@ -25,7 +25,7 @@ fn test_drop() {
let f = tempfile().unwrap();
f.set_len(6).unwrap();
let mut aiocb = Box::pin(AioWrite::new(
f.as_raw_fd(),
f.as_fd(),
2, //offset
WBUF,
0, //priority
+2
View File
@@ -1,3 +1,5 @@
#![allow(deprecated)]
use nix::errno::Errno;
use nix::sys::epoll::{epoll_create1, epoll_ctl};
use nix::sys::epoll::{EpollCreateFlags, EpollEvent, EpollFlags, EpollOp};
+41
View File
@@ -0,0 +1,41 @@
use libc::intptr_t;
use nix::sys::event::{EvFlags, EventFilter, FilterFlag, KEvent};
#[test]
fn test_struct_kevent() {
use std::mem;
let udata: intptr_t = 12345;
let data: intptr_t = 0x1337;
let actual = KEvent::new(
0xdead_beef,
EventFilter::EVFILT_READ,
EvFlags::EV_ONESHOT | EvFlags::EV_ADD,
FilterFlag::NOTE_CHILD | FilterFlag::NOTE_EXIT,
data,
udata,
);
assert_eq!(0xdead_beef, actual.ident());
assert_eq!(EventFilter::EVFILT_READ, actual.filter().unwrap());
assert_eq!(libc::EV_ONESHOT | libc::EV_ADD, actual.flags().bits());
assert_eq!(libc::NOTE_CHILD | libc::NOTE_EXIT, actual.fflags().bits());
assert_eq!(data, actual.data());
assert_eq!(udata, actual.udata());
assert_eq!(mem::size_of::<libc::kevent>(), mem::size_of::<KEvent>());
}
#[test]
fn test_kevent_filter() {
let udata: intptr_t = 12345;
let actual = KEvent::new(
0xdead_beef,
EventFilter::EVFILT_READ,
EvFlags::EV_ONESHOT | EvFlags::EV_ADD,
FilterFlag::NOTE_CHILD | FilterFlag::NOTE_EXIT,
0x1337,
udata,
);
assert_eq!(EventFilter::EVFILT_READ, actual.filter().unwrap());
}
+220
View File
@@ -0,0 +1,220 @@
use crate::*;
use nix::errno::Errno;
use nix::fcntl::AT_FDCWD;
use nix::sys::fanotify::{
EventFFlags, Fanotify, FanotifyResponse, InitFlags, MarkFlags, MaskFlags,
Response,
};
use std::fs::{read_link, read_to_string, File, OpenOptions};
use std::io::ErrorKind;
use std::io::{Read, Write};
use std::os::fd::AsRawFd;
use std::thread;
#[test]
/// Run fanotify tests sequentially to avoid tmp files races
pub fn test_fanotify() {
require_capability!("test_fanotify", CAP_SYS_ADMIN);
test_fanotify_notifications();
test_fanotify_responses();
test_fanotify_overflow();
}
fn test_fanotify_notifications() {
let group =
Fanotify::init(InitFlags::FAN_CLASS_NOTIF, EventFFlags::O_RDONLY)
.unwrap();
let tempdir = tempfile::tempdir().unwrap();
let tempfile = tempdir.path().join("test");
OpenOptions::new()
.write(true)
.create_new(true)
.open(&tempfile)
.unwrap();
group
.mark(
MarkFlags::FAN_MARK_ADD,
MaskFlags::FAN_OPEN | MaskFlags::FAN_MODIFY | MaskFlags::FAN_CLOSE,
AT_FDCWD,
Some(&tempfile),
)
.unwrap();
// modify test file
{
let mut f = OpenOptions::new().write(true).open(&tempfile).unwrap();
f.write_all(b"hello").unwrap();
}
let mut events = group.read_events().unwrap();
assert_eq!(events.len(), 1, "should have read exactly one event");
let event = events.pop().unwrap();
assert!(event.check_version());
assert_eq!(
event.mask(),
MaskFlags::FAN_OPEN
| MaskFlags::FAN_MODIFY
| MaskFlags::FAN_CLOSE_WRITE
);
let fd_opt = event.fd();
let fd = fd_opt.as_ref().unwrap();
let path = read_link(format!("/proc/self/fd/{}", fd.as_raw_fd())).unwrap();
assert_eq!(path, tempfile);
// read test file
{
let mut f = File::open(&tempfile).unwrap();
let mut s = String::new();
f.read_to_string(&mut s).unwrap();
}
let mut events = group.read_events().unwrap();
assert_eq!(events.len(), 1, "should have read exactly one event");
let event = events.pop().unwrap();
assert!(event.check_version());
assert_eq!(
event.mask(),
MaskFlags::FAN_OPEN | MaskFlags::FAN_CLOSE_NOWRITE
);
let fd_opt = event.fd();
let fd = fd_opt.as_ref().unwrap();
let path = read_link(format!("/proc/self/fd/{}", fd.as_raw_fd())).unwrap();
assert_eq!(path, tempfile);
}
fn test_fanotify_responses() {
let group =
Fanotify::init(InitFlags::FAN_CLASS_CONTENT, EventFFlags::O_RDONLY)
.unwrap();
let tempdir = tempfile::tempdir().unwrap();
let tempfile = tempdir.path().join("test");
OpenOptions::new()
.write(true)
.create_new(true)
.open(&tempfile)
.unwrap();
group
.mark(
MarkFlags::FAN_MARK_ADD,
MaskFlags::FAN_OPEN_PERM,
AT_FDCWD,
Some(&tempfile),
)
.unwrap();
let file_thread = thread::spawn({
let tempfile = tempfile.clone();
move || {
// first open, should fail
let Err(e) = File::open(&tempfile) else {
panic!("The first open should fail");
};
assert_eq!(e.kind(), ErrorKind::PermissionDenied);
// second open, should succeed
File::open(&tempfile).unwrap();
}
});
// Deny the first open try
let mut events = group.read_events().unwrap();
assert_eq!(events.len(), 1, "should have read exactly one event");
let event = events.pop().unwrap();
assert!(event.check_version());
assert_eq!(event.mask(), MaskFlags::FAN_OPEN_PERM);
let fd_opt = event.fd();
let fd = fd_opt.as_ref().unwrap();
let path = read_link(format!("/proc/self/fd/{}", fd.as_raw_fd())).unwrap();
assert_eq!(path, tempfile);
group
.write_response(FanotifyResponse::new(*fd, Response::FAN_DENY))
.unwrap();
// Allow the second open try
let mut events = group.read_events().unwrap();
assert_eq!(events.len(), 1, "should have read exactly one event");
let event = events.pop().unwrap();
assert!(event.check_version());
assert_eq!(event.mask(), MaskFlags::FAN_OPEN_PERM);
let fd_opt = event.fd();
let fd = fd_opt.as_ref().unwrap();
let path = read_link(format!("/proc/self/fd/{}", fd.as_raw_fd())).unwrap();
assert_eq!(path, tempfile);
group
.write_response(FanotifyResponse::new(*fd, Response::FAN_ALLOW))
.unwrap();
file_thread.join().unwrap();
}
fn test_fanotify_overflow() {
let max_events: usize =
read_to_string("/proc/sys/fs/fanotify/max_queued_events")
.unwrap()
.trim()
.parse()
.unwrap();
// make sure the kernel is configured with the default value,
// just so this test doesn't run forever
assert_eq!(max_events, 16384);
let group = Fanotify::init(
InitFlags::FAN_CLASS_NOTIF
| InitFlags::FAN_REPORT_TID
| InitFlags::FAN_NONBLOCK,
EventFFlags::O_RDONLY,
)
.unwrap();
let tempdir = tempfile::tempdir().unwrap();
let tempfile = tempdir.path().join("test");
OpenOptions::new()
.write(true)
.create_new(true)
.open(&tempfile)
.unwrap();
group
.mark(
MarkFlags::FAN_MARK_ADD,
MaskFlags::FAN_OPEN,
AT_FDCWD,
Some(&tempfile),
)
.unwrap();
thread::scope(|s| {
// perform 10 more events to demonstrate some will be dropped
for _ in 0..(max_events + 10) {
s.spawn(|| {
File::open(&tempfile).unwrap();
});
}
});
// flush the queue until it's empty
let mut n = 0;
let mut last_event = None;
loop {
match group.read_events() {
Ok(events) => {
n += events.len();
if let Some(event) = events.last() {
last_event = Some(event.mask());
}
}
Err(e) if e == Errno::EWOULDBLOCK => break,
Err(e) => panic!("{e:?}"),
}
}
// make sure we read all we expected.
// the +1 is for the overflow event.
assert_eq!(n, max_events + 1);
assert_eq!(last_event, Some(MaskFlags::FAN_Q_OVERFLOW));
}
+20 -13
View File
@@ -28,7 +28,7 @@ ioctl_readwrite_buf!(readwritebuf_test, 0, 0, u32);
// TODO: Need a way to compute these constants at test time. Using precomputed
// values is fragile and needs to be maintained.
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(linux_android)]
mod linux {
// The cast is not unnecessary on all platforms.
#[allow(clippy::unnecessary_cast)]
@@ -36,7 +36,9 @@ mod linux {
fn test_op_none() {
if cfg!(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "powerpc",
target_arch = "powerpc64"
)) {
@@ -54,7 +56,9 @@ mod linux {
fn test_op_write() {
if cfg!(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "powerpc",
target_arch = "powerpc64"
)) {
@@ -69,7 +73,11 @@ mod linux {
#[cfg(target_pointer_width = "64")]
#[test]
fn test_op_write_64() {
if cfg!(any(target_arch = "mips64", target_arch = "powerpc64")) {
if cfg!(any(
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "powerpc64"
)) {
assert_eq!(
request_code_write!(b'z', 10, 1u64 << 32) as u32,
0x8000_7A0A
@@ -88,7 +96,9 @@ mod linux {
fn test_op_read() {
if cfg!(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "powerpc",
target_arch = "powerpc64"
)) {
@@ -103,7 +113,11 @@ mod linux {
#[cfg(target_pointer_width = "64")]
#[test]
fn test_op_read_64() {
if cfg!(any(target_arch = "mips64", target_arch = "powerpc64")) {
if cfg!(any(
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "powerpc64"
)) {
assert_eq!(
request_code_read!(b'z', 10, 1u64 << 32) as u32,
0x4000_7A0A
@@ -134,14 +148,7 @@ mod linux {
}
}
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"
))]
#[cfg(bsd)]
mod bsd {
#[test]
fn test_op_none() {
@@ -149,7 +156,7 @@ mod bsd {
assert_eq!(request_code_none!(b'a', 255), 0x2000_61FF);
}
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
#[cfg(freebsdlike)]
#[test]
fn test_op_write_int() {
assert_eq!(request_code_write_int!(b'v', 4), 0x2004_7604);
@@ -193,7 +200,7 @@ mod bsd {
}
}
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(linux_android)]
mod linux_ioctls {
use std::mem;
use std::os::unix::io::AsRawFd;
+20
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());
}
+105 -27
View File
@@ -1,44 +1,44 @@
use nix::sys::mman::{mmap, MapFlags, ProtFlags};
#![allow(clippy::redundant_slicing)]
use nix::sys::mman::{mmap_anonymous, MapFlags, ProtFlags};
use std::num::NonZeroUsize;
#[test]
fn test_mmap_anonymous() {
unsafe {
let ptr = mmap(
let mut ptr = mmap_anonymous(
None,
NonZeroUsize::new(1).unwrap(),
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
MapFlags::MAP_PRIVATE | MapFlags::MAP_ANONYMOUS,
-1,
0,
MapFlags::MAP_PRIVATE,
)
.unwrap() as *mut u8;
assert_eq!(*ptr, 0x00u8);
*ptr = 0xffu8;
assert_eq!(*ptr, 0xffu8);
.unwrap()
.cast::<u8>();
assert_eq!(*ptr.as_ref(), 0x00u8);
*ptr.as_mut() = 0xffu8;
assert_eq!(*ptr.as_ref(), 0xffu8);
}
}
#[test]
#[cfg(any(target_os = "linux", target_os = "netbsd"))]
fn test_mremap_grow() {
use nix::libc::{c_void, size_t};
use nix::libc::size_t;
use nix::sys::mman::{mremap, MRemapFlags};
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(
let mem = mmap_anonymous(
None,
one_k_non_zero,
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
MapFlags::MAP_ANONYMOUS | MapFlags::MAP_PRIVATE,
-1,
0,
MapFlags::MAP_PRIVATE,
)
.unwrap();
std::slice::from_raw_parts_mut(mem as *mut u8, ONE_K)
std::slice::from_raw_parts_mut(mem.as_ptr().cast(), ONE_K)
};
assert_eq!(slice[ONE_K - 1], 0x00);
slice[ONE_K - 1] = 0xFF;
@@ -47,7 +47,7 @@ fn test_mremap_grow() {
let slice: &mut [u8] = unsafe {
#[cfg(target_os = "linux")]
let mem = mremap(
slice.as_mut_ptr() as *mut c_void,
NonNull::from(&mut slice[..]).cast(),
ONE_K,
10 * ONE_K,
MRemapFlags::MREMAP_MAYMOVE,
@@ -56,14 +56,14 @@ fn test_mremap_grow() {
.unwrap();
#[cfg(target_os = "netbsd")]
let mem = mremap(
slice.as_mut_ptr() as *mut c_void,
NonNull::from(&mut slice[..]).cast(),
ONE_K,
10 * ONE_K,
MRemapFlags::MAP_REMAPDUP,
None,
)
.unwrap();
std::slice::from_raw_parts_mut(mem as *mut u8, 10 * ONE_K)
std::slice::from_raw_parts_mut(mem.cast().as_ptr(), 10 * ONE_K)
};
// The first KB should still have the old data in it.
@@ -80,23 +80,22 @@ fn test_mremap_grow() {
// Segfaults for unknown reasons under QEMU for 32-bit targets
#[cfg_attr(all(target_pointer_width = "32", qemu), ignore)]
fn test_mremap_shrink() {
use nix::libc::{c_void, size_t};
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 ten_one_k = NonZeroUsize::new(10 * ONE_K).unwrap();
let slice: &mut [u8] = unsafe {
let mem = mmap(
let mem = mmap_anonymous(
None,
ten_one_k,
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
MapFlags::MAP_ANONYMOUS | MapFlags::MAP_PRIVATE,
-1,
0,
MapFlags::MAP_PRIVATE,
)
.unwrap();
std::slice::from_raw_parts_mut(mem as *mut u8, ONE_K)
std::slice::from_raw_parts_mut(mem.as_ptr().cast(), ONE_K)
};
assert_eq!(slice[ONE_K - 1], 0x00);
slice[ONE_K - 1] = 0xFF;
@@ -104,7 +103,7 @@ fn test_mremap_shrink() {
let slice: &mut [u8] = unsafe {
let mem = mremap(
slice.as_mut_ptr() as *mut c_void,
NonNull::from(&mut slice[..]).cast(),
ten_one_k.into(),
ONE_K,
MRemapFlags::empty(),
@@ -113,10 +112,89 @@ fn test_mremap_shrink() {
.unwrap();
// Since we didn't supply MREMAP_MAYMOVE, the address should be the
// same.
assert_eq!(mem, slice.as_mut_ptr() as *mut c_void);
std::slice::from_raw_parts_mut(mem as *mut u8, ONE_K)
assert_eq!(mem.as_ptr(), NonNull::from(&mut slice[..]).cast().as_ptr());
std::slice::from_raw_parts_mut(mem.as_ptr().cast(), ONE_K)
};
// 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();
}
}
}
}
+172
View File
@@ -0,0 +1,172 @@
#[cfg(target_os = "linux")]
#[cfg(feature = "process")]
mod test_prctl {
use std::ffi::CStr;
use nix::sys::prctl;
#[cfg_attr(qemu, ignore)]
#[test]
fn test_get_set_subreaper() {
let original = prctl::get_child_subreaper().unwrap();
prctl::set_child_subreaper(true).unwrap();
let subreaper = prctl::get_child_subreaper().unwrap();
assert!(subreaper);
prctl::set_child_subreaper(original).unwrap();
}
#[test]
fn test_get_set_dumpable() {
let original = prctl::get_dumpable().unwrap();
prctl::set_dumpable(false).unwrap();
let dumpable = prctl::get_dumpable().unwrap();
assert!(!dumpable);
prctl::set_dumpable(original).unwrap();
}
#[test]
fn test_get_set_keepcaps() {
let original = prctl::get_keepcaps().unwrap();
prctl::set_keepcaps(true).unwrap();
let keepcaps = prctl::get_keepcaps().unwrap();
assert!(keepcaps);
prctl::set_keepcaps(original).unwrap();
}
#[test]
fn test_get_set_clear_mce_kill() {
use prctl::PrctlMCEKillPolicy::*;
prctl::set_mce_kill(PR_MCE_KILL_LATE).unwrap();
let mce = prctl::get_mce_kill().unwrap();
assert_eq!(mce, PR_MCE_KILL_LATE);
prctl::clear_mce_kill().unwrap();
let mce = prctl::get_mce_kill().unwrap();
assert_eq!(mce, PR_MCE_KILL_DEFAULT);
}
#[cfg_attr(qemu, ignore)]
#[test]
fn test_get_set_pdeathsig() {
use nix::sys::signal::Signal;
let original = prctl::get_pdeathsig().unwrap();
prctl::set_pdeathsig(Signal::SIGUSR1).unwrap();
let sig = prctl::get_pdeathsig().unwrap();
assert_eq!(sig, Some(Signal::SIGUSR1));
prctl::set_pdeathsig(original).unwrap();
}
#[test]
fn test_get_set_name() {
let original = prctl::get_name().unwrap();
let long_name =
CStr::from_bytes_with_nul(b"0123456789abcdefghijklmn\0").unwrap();
prctl::set_name(long_name).unwrap();
let res = prctl::get_name().unwrap();
// name truncated by kernel to TASK_COMM_LEN
assert_eq!(&long_name.to_str().unwrap()[..15], res.to_str().unwrap());
let short_name = CStr::from_bytes_with_nul(b"01234567\0").unwrap();
prctl::set_name(short_name).unwrap();
let res = prctl::get_name().unwrap();
assert_eq!(short_name.to_str().unwrap(), res.to_str().unwrap());
prctl::set_name(&original).unwrap();
}
#[cfg_attr(qemu, ignore)]
#[test]
fn test_get_set_timerslack() {
let original = prctl::get_timerslack().unwrap() as libc::c_ulong;
let slack = 60_000;
prctl::set_timerslack(slack).unwrap();
let res = prctl::get_timerslack().unwrap() as libc::c_ulong;
assert_eq!(slack, res);
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();
prctl::task_perf_events_enable().unwrap();
}
#[test]
fn test_get_set_no_new_privs() {
prctl::set_no_new_privs().unwrap();
let no_new_privs = prctl::get_no_new_privs().unwrap();
assert!(no_new_privs);
}
// 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();
prctl::set_thp_disable(true).unwrap();
let thp_disable = prctl::get_thp_disable().unwrap();
assert!(thp_disable);
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;
use std::num::NonZeroUsize;
const ONE_K: libc::size_t = 1024;
let sz = NonZeroUsize::new(ONE_K).unwrap();
let ptr = unsafe {
mman::mmap_anonymous(
None,
sz,
mman::ProtFlags::PROT_READ,
mman::MapFlags::MAP_SHARED,
)
.unwrap()
};
let err = prctl::set_vma_anon_name(
ptr,
sz,
Some(CStr::from_bytes_with_nul(b"[,$\0").unwrap()),
)
.unwrap_err();
assert_eq!(err, Errno::EINVAL);
// `CONFIG_ANON_VMA_NAME` kernel config might not be set
prctl::set_vma_anon_name(
ptr,
sz,
Some(CStr::from_bytes_with_nul(b"Nix\0").unwrap()),
)
.unwrap_or_default();
prctl::set_vma_anon_name(ptr, sz, None).unwrap_or_default();
}
}
+12 -2
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();
+147 -10
View File
@@ -1,16 +1,16 @@
#[cfg(all(
target_os = "linux",
any(target_arch = "x86_64", target_arch = "x86"),
target_env = "gnu"
target_env = "gnu",
any(target_arch = "x86_64", target_arch = "x86")
))]
use memoffset::offset_of;
use nix::errno::Errno;
use nix::sys::ptrace;
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(linux_android)]
use nix::sys::ptrace::Options;
use nix::unistd::getpid;
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(linux_android)]
use std::mem;
use crate::*;
@@ -28,7 +28,7 @@ fn test_ptrace() {
// Just make sure ptrace_setoptions can be called at all, for now.
#[test]
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(linux_android)]
fn test_ptrace_setoptions() {
require_capability!("test_ptrace_setoptions", CAP_SYS_PTRACE);
let err = ptrace::setoptions(getpid(), Options::PTRACE_O_TRACESYSGOOD)
@@ -38,7 +38,7 @@ fn test_ptrace_setoptions() {
// Just make sure ptrace_getevent can be called at all, for now.
#[test]
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(linux_android)]
fn test_ptrace_getevent() {
require_capability!("test_ptrace_getevent", CAP_SYS_PTRACE);
let err = ptrace::getevent(getpid()).unwrap_err();
@@ -47,7 +47,7 @@ fn test_ptrace_getevent() {
// Just make sure ptrace_getsiginfo can be called at all, for now.
#[test]
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(linux_android)]
fn test_ptrace_getsiginfo() {
require_capability!("test_ptrace_getsiginfo", CAP_SYS_PTRACE);
if let Err(Errno::EOPNOTSUPP) = ptrace::getsiginfo(getpid()) {
@@ -57,7 +57,7 @@ fn test_ptrace_getsiginfo() {
// Just make sure ptrace_setsiginfo can be called at all, for now.
#[test]
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(linux_android)]
fn test_ptrace_setsiginfo() {
require_capability!("test_ptrace_setsiginfo", CAP_SYS_PTRACE);
let siginfo = unsafe { mem::zeroed() };
@@ -179,8 +179,18 @@ fn test_ptrace_interrupt() {
// ptrace::{setoptions, getregs} are only available in these platforms
#[cfg(all(
target_os = "linux",
any(target_arch = "x86_64", target_arch = "x86"),
target_env = "gnu"
any(
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]
fn test_ptrace_syscall() {
@@ -226,12 +236,21 @@ fn test_ptrace_syscall() {
let get_syscall_id =
|| ptrace::getregs(child).unwrap().orig_eax as libc::c_long;
#[cfg(target_arch = "aarch64")]
let get_syscall_id =
|| ptrace::getregs(child).unwrap().regs[8] as libc::c_long;
#[cfg(target_arch = "riscv64")]
let get_syscall_id =
|| ptrace::getregs(child).unwrap().a7 as libc::c_long;
// this duplicates `get_syscall_id` for the purpose of testing `ptrace::read_user`.
#[cfg(target_arch = "x86_64")]
let rax_offset = offset_of!(libc::user_regs_struct, orig_rax);
#[cfg(target_arch = "x86")]
let rax_offset = offset_of!(libc::user_regs_struct, orig_eax);
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
let get_syscall_from_user_area = || {
// Find the offset of `user.regs.rax` (or `user.regs.eax` for x86)
let rax_offset = offset_of!(libc::user, regs) + rax_offset;
@@ -246,6 +265,7 @@ fn test_ptrace_syscall() {
Ok(WaitStatus::PtraceSyscall(child))
);
assert_eq!(get_syscall_id(), ::libc::SYS_kill);
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
assert_eq!(get_syscall_from_user_area(), ::libc::SYS_kill);
// kill exit
@@ -255,6 +275,7 @@ fn test_ptrace_syscall() {
Ok(WaitStatus::PtraceSyscall(child))
);
assert_eq!(get_syscall_id(), ::libc::SYS_kill);
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
assert_eq!(get_syscall_from_user_area(), ::libc::SYS_kill);
// receive signal
@@ -273,3 +294,119 @@ fn test_ptrace_syscall() {
}
}
}
#[cfg(all(
target_os = "linux",
any(
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]
fn test_ptrace_regsets() {
use nix::sys::ptrace::{self, getregset, regset, setregset};
use nix::sys::signal::*;
use nix::sys::wait::{waitpid, WaitStatus};
use nix::unistd::fork;
use nix::unistd::ForkResult::*;
require_capability!("test_ptrace_regsets", CAP_SYS_PTRACE);
let _m = crate::FORK_MTX.lock();
match unsafe { fork() }.expect("Error: Fork Failed") {
Child => {
ptrace::traceme().unwrap();
// As recommended by ptrace(2), raise SIGTRAP to pause the child
// until the parent is ready to continue
loop {
raise(Signal::SIGTRAP).unwrap();
}
}
Parent { child } => {
assert_eq!(
waitpid(child, None),
Ok(WaitStatus::Stopped(child, Signal::SIGTRAP))
);
let mut regstruct =
getregset::<regset::NT_PRSTATUS>(child).unwrap();
let mut fpregstruct =
getregset::<regset::NT_PRFPREG>(child).unwrap();
#[cfg(target_arch = "x86_64")]
let (reg, fpreg) =
(&mut regstruct.r15, &mut fpregstruct.st_space[5]);
#[cfg(target_arch = "x86")]
let (reg, fpreg) =
(&mut regstruct.edx, &mut fpregstruct.st_space[5]);
#[cfg(target_arch = "aarch64")]
let (reg, fpreg) =
(&mut regstruct.regs[16], &mut fpregstruct.vregs[5]);
#[cfg(target_arch = "riscv64")]
let (reg, fpreg) = (&mut regstruct.t1, &mut fpregstruct.__f[5]);
*reg = 0xdeadbeefu32 as _;
*fpreg = 0xfeedfaceu32 as _;
let _ = setregset::<regset::NT_PRSTATUS>(child, regstruct);
regstruct = getregset::<regset::NT_PRSTATUS>(child).unwrap();
let _ = setregset::<regset::NT_PRFPREG>(child, fpregstruct);
fpregstruct = getregset::<regset::NT_PRFPREG>(child).unwrap();
#[cfg(target_arch = "x86_64")]
let (reg, fpreg) = (regstruct.r15, fpregstruct.st_space[5]);
#[cfg(target_arch = "x86")]
let (reg, fpreg) = (regstruct.edx, fpregstruct.st_space[5]);
#[cfg(target_arch = "aarch64")]
let (reg, fpreg) = (regstruct.regs[16], fpregstruct.vregs[5]);
#[cfg(target_arch = "riscv64")]
let (reg, fpreg) = (regstruct.t1, fpregstruct.__f[5]);
assert_eq!(reg, 0xdeadbeefu32 as _);
assert_eq!(fpreg, 0xfeedfaceu32 as _);
ptrace::cont(child, Some(Signal::SIGKILL)).unwrap();
match waitpid(child, None) {
Ok(WaitStatus::Signaled(pid, Signal::SIGKILL, _))
if pid == child => {}
_ => panic!("The process should have been killed"),
}
}
}
}
#[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);
},
}
}

Some files were not shown because too many files have changed in this diff Show More