diff --git a/.cirrus.yml b/.cirrus.yml index c1c213a12..901904404 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -4,7 +4,7 @@ task: name: stable x86_64-unknown-freebsd-14 freebsd_instance: - image_family: freebsd-14-2 + image_family: freebsd-14-3 setup_script: - curl https://sh.rustup.rs -sSf --output rustup.sh - sh rustup.sh --default-toolchain stable -y --profile=minimal @@ -17,7 +17,20 @@ task: task: name: stable x86_64-unknown-freebsd-15 freebsd_instance: - image_family: freebsd-15-0-snap + image_family: freebsd-15-0-amd64-ufs + setup_script: + - curl https://sh.rustup.rs -sSf --output rustup.sh + - sh rustup.sh --default-toolchain stable -y --profile=minimal + - . $HOME/.cargo/env + - rustup default stable + test_script: + - . $HOME/.cargo/env + - cargo test --workspace --features=all-apis + +task: + name: stable x86_64-unknown-freebsd-16 + freebsd_instance: + image_family: freebsd-16-0-snap setup_script: - curl https://sh.rustup.rs -sSf --output rustup.sh - sh rustup.sh --default-toolchain stable -y --profile=minimal diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f553a007b..58ab02e02 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -55,8 +55,20 @@ jobs: cargo update --package=regex --precise=1.9.0 cargo update --package=half --precise=2.2.1 cargo update --package=flate2 --precise=1.0.35 + cargo update --package=syn --precise=2.0.106 cargo update --package=textwrap --precise=0.16.1 cargo update --package=once_cell --precise=1.20.3 + cargo update --package=parking_lot --precise=0.12.3 + cargo update --package=parking_lot_core --precise=0.9.10 + cargo update --package=lock_api --precise=0.4.12 + cargo update --package=rayon --precise=1.10.0 + cargo update --package=rayon-core --precise=1.12.1 + cargo update --package=windows-sys@0.61.2 --precise=0.60.2 + cargo update --package=quote --precise=1.0.41 + cargo update --package=ryu --precise=1.0.20 + cargo update --package=itoa --precise=1.0.15 + cargo update --package=serde_json --precise=1.0.145 + cargo update --package=log --precise=0.4.28 - run: > rustup target add @@ -101,8 +113,8 @@ jobs: - run: cargo check --workspace --release -vv --target=x86_64-unknown-linux-musl --features=all-apis --all-targets - run: cargo check --workspace --release -vv --target=x86_64-unknown-linux-musl --features=use-libc,all-apis --all-targets - run: cargo check --workspace --release -vv --target=x86_64-unknown-linux-gnux32 --features=all-apis --all-targets - - run: cargo check --workspace --release -vv --target=x86_64-linux-android --features=all-apis --all-targets - - run: cargo check --workspace --release -vv --target=i686-linux-android --features=all-apis --all-targets + - run: cargo check --workspace --release -vv --target=x86_64-linux-android --features=use-libc,net,std,alloc,event,fs,mm,param,pipe,use-libc-auxv,libc_errno --all-targets + - run: cargo check --workspace --release -vv --target=i686-linux-android --features=use-libc,net,std,alloc,event,fs,mm,param,pipe,use-libc-auxv,libc_errno --all-targets - run: cargo check --workspace --release -vv --target=x86_64-apple-darwin --features=all-apis --all-targets - run: cargo check --workspace --release -vv --target=x86_64-unknown-freebsd --features=all-apis --all-targets - run: cargo check --workspace --release -vv --target=x86_64-unknown-netbsd --features=all-apis --all-targets @@ -129,11 +141,11 @@ jobs: - run: cargo check --workspace --release -vv --target=powerpc64le-unknown-linux-gnu --features=all-apis --all-targets - run: cargo check --workspace --release -vv --target=armv5te-unknown-linux-gnueabi --features=all-apis --all-targets - run: cargo check --workspace --release -vv --target=s390x-unknown-linux-gnu --features=all-apis --all-targets - - run: cargo check --workspace --release -vv --target=arm-linux-androideabi --features=all-apis --all-targets + - run: cargo check --workspace --release -vv --target=arm-linux-androideabi --features=use-libc,net,std,alloc,event,fs,mm,param,pipe,use-libc-auxv,libc_errno --all-targets - run: cargo check --workspace --release -vv --target=sparc64-unknown-linux-gnu --features=all-apis --all-targets - run: cargo check --workspace --release -vv --target=sparcv9-sun-solaris --features=all-apis --all-targets - run: cargo check --workspace --release -vv --target=aarch64-apple-ios --features=all-apis --all-targets - - run: cargo check --workspace --release -vv --target=aarch64-linux-android --features=all-apis --all-targets + - run: cargo check --workspace --release -vv --target=aarch64-linux-android --features=use-libc,net,std,alloc,event,fs,mm,param,pipe,use-libc-auxv,libc_errno --all-targets - run: cargo check --workspace --release -vv --target=i686-pc-windows-msvc --features=all-apis --all-targets check_no_default_features: @@ -227,9 +239,12 @@ jobs: - run: cargo check -Z build-std --target=riscv32imc-esp-espidf --features=all-apis - run: cargo check -Z build-std --target=aarch64-unknown-nto-qnx710 --features=all-apis - run: cargo check -Z build-std --target=x86_64-pc-nto-qnx710 --features=all-apis - - run: cargo check -Z build-std --target=armv6k-nintendo-3ds --all-features - - run: cargo check -Z build-std --target=armv7-sony-vita-newlibeabihf --features=all-apis - - run: cargo check -Z build-std --target=powerpc64-ibm-aix --features=all-apis + # Temporarily disable due to build errors on nightly. + # - run: cargo check -Z build-std --target=armv6k-nintendo-3ds --all-features + # Temporarily disable due to build errors on nightly. + # - run: cargo check -Z build-std --target=armv7-sony-vita-newlibeabihf --features=all-apis + # Temporarily disable due to build errors on nightly. + # - run: cargo check -Z build-std --target=powerpc64-ibm-aix --features=all-apis - run: cargo check -Z build-std --target=mipsel-unknown-linux-gnu --features=all-apis - run: cargo check -Z build-std --target=mips64el-unknown-linux-gnuabi64 --features=all-apis - run: cargo check -Z build-std --target=powerpc-unknown-linux-musl --features=all-apis @@ -246,13 +261,45 @@ jobs: RUSTFLAGS: --cfg rustix_use_experimental_features strategy: matrix: - build: [ubuntu, ubuntu-20.04, i686-linux, aarch64-linux, powerpc-linux, powerpc64le-linux, riscv64-linux, s390x-linux, arm-linux, ubuntu-stable, i686-linux-stable, aarch64-linux-stable, riscv64-linux-stable, s390x-linux-stable, powerpc-linux-stable, powerpc64le-linux-stable, arm-linux-stable, ubuntu-1.63, i686-linux-1.63, aarch64-linux-1.63, riscv64-linux-1.63, s390x-linux-1.63, powerpc64le-linux, powerpc64le-linux-1.63, arm-linux-1.63, macos-latest, macos-13, windows, windows-2019, musl] + build: + [ + ubuntu, + ubuntu-22.04, + i686-linux, + aarch64-linux, + powerpc-linux, + powerpc64le-linux, + riscv64-linux, + s390x-linux, + arm-linux, + ubuntu-stable, + i686-linux-stable, + aarch64-linux-stable, + riscv64-linux-stable, + s390x-linux-stable, + powerpc-linux-stable, + powerpc64le-linux-stable, + arm-linux-stable, + ubuntu-1.63, + i686-linux-1.63, + aarch64-linux-1.63, + riscv64-linux-1.63, + s390x-linux-1.63, + powerpc64le-linux, + powerpc64le-linux-1.63, + arm-linux-1.63, + macos-latest, + macos-15-intel, + macos-14, + windows, + musl, + ] include: - build: ubuntu os: ubuntu-latest rust: nightly - - build: ubuntu-20.04 - os: ubuntu-20.04 + - build: ubuntu-22.04 + os: ubuntu-22.04 rust: nightly - build: i686-linux os: ubuntu-latest @@ -446,15 +493,15 @@ jobs: - build: macos-latest os: macos-latest rust: stable - - build: macos-13 - os: macos-13 + - build: macos-15-intel + os: macos-15-intel + rust: stable + - build: macos-14 + os: macos-14 rust: stable - build: windows os: windows-latest rust: nightly - - build: windows-2019 - os: windows-2019 - rust: nightly - build: musl os: ubuntu-latest rust: stable @@ -534,8 +581,20 @@ jobs: cargo update --package=regex --precise=1.9.0 cargo update --package=half --precise=2.2.1 cargo update --package=flate2 --precise=1.0.35 + cargo update --package=syn --precise=2.0.106 cargo update --package=textwrap --precise=0.16.1 cargo update --package=once_cell --precise=1.20.3 + cargo update --package=parking_lot --precise=0.12.3 + cargo update --package=parking_lot_core --precise=0.9.10 + cargo update --package=lock_api --precise=0.4.12 + cargo update --package=rayon --precise=1.10.0 + cargo update --package=rayon-core --precise=1.12.1 + cargo update --package=windows-sys@0.61.2 --precise=0.60.2 + cargo update --package=quote --precise=1.0.41 + cargo update --package=ryu --precise=1.0.20 + cargo update --package=itoa --precise=1.0.15 + cargo update --package=serde_json --precise=1.0.145 + cargo update --package=log --precise=0.4.28 - run: | cargo test --verbose --features=all-apis --release --workspace -- --nocapture diff --git a/CHANGES.md b/CHANGES.md index fd5770da6..a13b83606 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -66,12 +66,6 @@ constant. [`IORING_REGISTER_FILES_SKIP`]: https://docs.rs/rustix/1/rustix/io_uring/constant.IORING_REGISTER_FILES_SKIP.html [`rustix::fs::CWD`]: https://docs.rs/rustix/1/rustix/fs/constant.CWD.html -[`rustix::io_uring::io_uring_register`] now has a [`IoringRegisterFlags`] -argument, and `rustix::io_uring::io_uring_register_with` is removed. - -[`rustix::io_uring::io_uring_register`]: https://docs.rs/rustix/1/rustix/io_uring/fn.io_uring_register.html -[`IoringRegisterFlags`]: https://docs.rs/rustix/1/rustix/io_uring/struct.IoringRegisterFlags.html - Several structs in [`rustix::io_uring`] are now marked `#[non_exhaustive]` because they contain padding or reserved fields. Instead of constructing them with field values and `..Default::default()`, construct them with diff --git a/Cargo.toml b/Cargo.toml index c184e0808..89a3fad33 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustix" -version = "1.0.6" +version = "1.1.3" authors = [ "Dan Gohman ", "Jakub Konka ", @@ -15,13 +15,16 @@ categories = ["os::unix-apis", "date-and-time", "filesystem", "network-programmi include = ["src", "build.rs", "Cargo.toml", "COPYRIGHT", "LICENSE*", "/*.md", "benches"] rust-version = "1.63" +[hints] +# Most users use a fraction of the rustix API surface area, so this reduces compilation times +mostly-unused = true + [dependencies] bitflags = { version = "2.4.0", default-features = false } # Special dependencies used in rustc-dep-of-std mode. core = { version = "1.0.0", optional = true, package = "rustc-std-workspace-core" } rustc-std-workspace-alloc = { version = "1.0.0", optional = true } # not aliased here but in lib.rs because of name collision with the alloc feature -compiler_builtins = { version = '0.1.49', optional = true } # Dependencies for platforms where linux_raw is supported, in addition to libc: # @@ -30,9 +33,9 @@ compiler_builtins = { version = '0.1.49', optional = true } # libc backend can be selected via adding `--cfg=rustix_use_libc` to # `RUSTFLAGS` or enabling the `use-libc` cargo feature. [target.'cfg(all(not(rustix_use_libc), not(miri), target_os = "linux", any(target_endian = "little", any(target_arch = "s390x", target_arch = "powerpc")), any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", all(rustix_use_experimental_asm, target_arch = "powerpc"), all(rustix_use_experimental_asm, target_arch = "powerpc64"), all(rustix_use_experimental_asm, target_arch = "s390x"), all(rustix_use_experimental_asm, target_arch = "mips"), all(rustix_use_experimental_asm, target_arch = "mips32r6"), all(rustix_use_experimental_asm, target_arch = "mips64"), all(rustix_use_experimental_asm, target_arch = "mips64r6"), target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64"))))'.dependencies] -linux-raw-sys = { version = "0.9.2", default-features = false, features = ["general", "errno", "ioctl", "no_std", "elf"] } +linux-raw-sys = { version = "0.11.0", default-features = false, features = ["auxvec", "general", "errno", "ioctl", "no_std", "elf"] } libc_errno = { package = "errno", version = "0.3.10", default-features = false, optional = true } -libc = { version = "0.2.168", default-features = false, optional = true } +libc = { version = "0.2.177", default-features = false, optional = true } # Dependencies for platforms where only libc is supported: # @@ -40,18 +43,18 @@ libc = { version = "0.2.168", default-features = false, optional = true } # backend, so enable its dependencies unconditionally. [target.'cfg(all(not(windows), any(rustix_use_libc, miri, not(all(target_os = "linux", any(target_endian = "little", any(target_arch = "s390x", target_arch = "powerpc")), any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", all(rustix_use_experimental_asm, target_arch = "powerpc"), all(rustix_use_experimental_asm, target_arch = "powerpc64"), all(rustix_use_experimental_asm, target_arch = "s390x"), all(rustix_use_experimental_asm, target_arch = "mips"), all(rustix_use_experimental_asm, target_arch = "mips32r6"), all(rustix_use_experimental_asm, target_arch = "mips64"), all(rustix_use_experimental_asm, target_arch = "mips64r6"), target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64")))))))'.dependencies] libc_errno = { package = "errno", version = "0.3.10", default-features = false } -libc = { version = "0.2.168", default-features = false } +libc = { version = "0.2.177", default-features = false } -# Additional dependencies for Linux with the libc backend: +# Additional dependencies for Linux and Android with the libc backend: # # Some syscalls do not have libc wrappers, such as in `io_uring`. For these, # the libc backend uses the linux-raw-sys ABI and `libc::syscall`. -[target.'cfg(all(any(target_os = "android", target_os = "linux"), any(rustix_use_libc, miri, not(all(target_os = "linux", any(target_endian = "little", any(target_arch = "s390x", target_arch = "powerpc")), any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", all(rustix_use_experimental_asm, target_arch = "powerpc"), all(rustix_use_experimental_asm, target_arch = "powerpc64"), all(rustix_use_experimental_asm, target_arch = "s390x"), all(rustix_use_experimental_asm, target_arch = "mips"), all(rustix_use_experimental_asm, target_arch = "mips32r6"), all(rustix_use_experimental_asm, target_arch = "mips64"), all(rustix_use_experimental_asm, target_arch = "mips64r6"), target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64")))))))'.dependencies] -linux-raw-sys = { version = "0.9.2", default-features = false, features = ["general", "ioctl", "no_std"] } +[target.'cfg(all(any(target_os = "linux", target_os = "android"), any(rustix_use_libc, miri, not(all(target_os = "linux", any(target_endian = "little", any(target_arch = "s390x", target_arch = "powerpc")), any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", all(rustix_use_experimental_asm, target_arch = "powerpc"), all(rustix_use_experimental_asm, target_arch = "powerpc64"), all(rustix_use_experimental_asm, target_arch = "s390x"), all(rustix_use_experimental_asm, target_arch = "mips"), all(rustix_use_experimental_asm, target_arch = "mips32r6"), all(rustix_use_experimental_asm, target_arch = "mips64"), all(rustix_use_experimental_asm, target_arch = "mips64r6"), target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64")))))))'.dependencies] +linux-raw-sys = { version = "0.11.0", default-features = false, features = ["general", "ioctl", "no_std"] } # For the libc backend on Windows, use the Winsock API in windows-sys. [target.'cfg(windows)'.dependencies.windows-sys] -version = ">=0.52, <0.60" +version = ">=0.52, <0.62" features = [ "Win32_Foundation", "Win32_Networking_WinSock", @@ -66,7 +69,7 @@ default-features = false [dev-dependencies] tempfile = "3.5.0" -libc = "0.2.168" +libc = "0.2.171" libc_errno = { package = "errno", version = "0.3.10", default-features = false } serial_test = "2.0.0" memoffset = "0.9.0" @@ -229,10 +232,8 @@ alloc = [] rustc-dep-of-std = [ "core", "rustc-std-workspace-alloc", - "compiler_builtins", "linux-raw-sys/rustc-dep-of-std", "bitflags/rustc-dep-of-std", - "compiler_builtins?/rustc-dep-of-std", ] # Enable `rustix::io::try_close`. The rustix developers do not intend the @@ -259,8 +260,12 @@ check-cfg = [ 'cfg(linux_kernel)', 'cfg(linux_like)', 'cfg(linux_raw)', + 'cfg(linux_raw_dep)', + 'cfg(lower_upper_exp_for_non_zero)', + 'cfg(sanitize_memory)', 'cfg(netbsdlike)', 'cfg(rustc_attrs)', + 'cfg(rustc_diagnostics)', 'cfg(solarish)', 'cfg(staged_api)', 'cfg(static_assertions)', diff --git a/build.rs b/build.rs index d2d6541f4..1677ece45 100644 --- a/build.rs +++ b/build.rs @@ -1,5 +1,6 @@ use std::env::var; use std::io::Write as _; +use std::path::PathBuf; /// The directory for inline asm. const ASM_PATH: &str = "src/backend/linux_raw/arch"; @@ -35,6 +36,10 @@ fn main() { // enable the libc backend even if rustix is depended on transitively. let cfg_use_libc = var("CARGO_CFG_RUSTIX_USE_LIBC").is_ok(); + // Check for `RUSTFLAGS=--cfg=rustix_no_linux_raw`. This allows Linux users to + // enable the libc backend without the linux raw dependency. + let cfg_no_linux_raw = var("CARGO_CFG_RUSTIX_NO_LINUX_RAW").is_ok(); + // Check for `--features=rustc-dep-of-std`. let rustc_dep_of_std = var("CARGO_FEATURE_RUSTC_DEP_OF_STD").is_ok(); @@ -79,6 +84,15 @@ fn main() { use_feature("static_assertions"); } + // `LowerExp`/`UpperExp` for `NonZeroI32` etc. + if has_lower_upper_exp_for_non_zero() { + use_feature("lower_upper_exp_for_non_zero"); + } + + if can_compile("#[diagnostic::on_unimplemented()] trait Foo {}") { + use_feature("rustc_diagnostics") + } + // WASI support can utilize wasi_ext if present. if os == "wasi" { use_feature_or_nothing("wasi_ext"); @@ -103,10 +117,15 @@ fn main() { || arch.starts_with("mips")) && !rustix_use_experimental_asm); if libc { + if (os == "linux" || os == "android") && !cfg_no_linux_raw { + use_feature("linux_raw_dep"); + } + // Use the libc backend. use_feature("libc"); } else { // Use the linux_raw backend. + use_feature("linux_raw_dep"); use_feature("linux_raw"); if rustix_use_experimental_asm { use_feature("asm_experimental_arch"); @@ -188,6 +207,12 @@ fn use_thumb_mode() -> bool { !can_compile("pub unsafe fn f() { core::arch::asm!(\"udf #16\", in(\"r7\") 0); }") } +fn has_lower_upper_exp_for_non_zero() -> bool { + // LowerExp/UpperExp for NonZero* were added in Rust 1.84. + // + can_compile("fn a(x: &core::num::NonZeroI32, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { core::fmt::LowerExp::fmt(x, f) }") +} + fn use_feature_or_nothing(feature: &str) { if has_feature(feature) { use_feature(feature); @@ -229,12 +254,14 @@ fn can_compile>(test: T) -> bool { std::process::Command::new(rustc) }; + let out_dir = var("OUT_DIR").unwrap(); + let out_file = PathBuf::from(out_dir).join("rustix_test_can_compile"); cmd.arg("--crate-type=rlib") // Don't require `main`. .arg("--emit=metadata") // Do as little as possible but still parse. .arg("--target") .arg(target) .arg("-o") - .arg("-") + .arg(out_file) .stdout(Stdio::null()); // We don't care about the output (only whether it builds or not) // If Cargo wants to set RUSTFLAGS, use that. diff --git a/src/backend/libc/c.rs b/src/backend/libc/c.rs index 2f737c21c..5eeff953f 100644 --- a/src/backend/libc/c.rs +++ b/src/backend/libc/c.rs @@ -19,68 +19,57 @@ pub(crate) const NFS_SUPER_MAGIC: u32 = 0x0000_6969; pub(crate) const EXIT_SIGNALED_SIGABRT: c_int = 128 + SIGABRT as c_int; // TODO: Upstream these. -#[cfg(all(linux_kernel, feature = "net"))] +#[cfg(all(linux_raw_dep, feature = "net"))] pub(crate) const ETH_P_TSN: c_int = linux_raw_sys::if_ether::ETH_P_TSN as _; -#[cfg(all(linux_kernel, feature = "net"))] +#[cfg(all(linux_raw_dep, feature = "net"))] pub(crate) const ETH_P_ERSPAN2: c_int = linux_raw_sys::if_ether::ETH_P_ERSPAN2 as _; -#[cfg(all(linux_kernel, feature = "net"))] +#[cfg(all(linux_raw_dep, feature = "net"))] pub(crate) const ETH_P_ERSPAN: c_int = linux_raw_sys::if_ether::ETH_P_ERSPAN as _; -#[cfg(all(linux_kernel, feature = "net"))] +#[cfg(all(linux_raw_dep, feature = "net"))] pub(crate) const ETH_P_PROFINET: c_int = linux_raw_sys::if_ether::ETH_P_PROFINET as _; -#[cfg(all(linux_kernel, feature = "net"))] +#[cfg(all(linux_raw_dep, feature = "net"))] pub(crate) const ETH_P_REALTEK: c_int = linux_raw_sys::if_ether::ETH_P_REALTEK as _; -#[cfg(all(linux_kernel, feature = "net"))] +#[cfg(all(linux_raw_dep, feature = "net"))] pub(crate) const ETH_P_ETHERCAT: c_int = linux_raw_sys::if_ether::ETH_P_ETHERCAT as _; -#[cfg(all(linux_kernel, feature = "net"))] +#[cfg(all(linux_raw_dep, feature = "net"))] pub(crate) const ETH_P_PREAUTH: c_int = linux_raw_sys::if_ether::ETH_P_PREAUTH as _; -#[cfg(all(linux_kernel, feature = "net"))] +#[cfg(all(linux_raw_dep, feature = "net"))] pub(crate) const ETH_P_LLDP: c_int = linux_raw_sys::if_ether::ETH_P_LLDP as _; -#[cfg(all(linux_kernel, feature = "net"))] +#[cfg(all(linux_raw_dep, feature = "net"))] pub(crate) const ETH_P_MRP: c_int = linux_raw_sys::if_ether::ETH_P_MRP as _; -#[cfg(all(linux_kernel, feature = "net"))] +#[cfg(all(linux_raw_dep, feature = "net"))] pub(crate) const ETH_P_NCSI: c_int = linux_raw_sys::if_ether::ETH_P_NCSI as _; -#[cfg(all(linux_kernel, feature = "net"))] +#[cfg(all(linux_raw_dep, feature = "net"))] pub(crate) const ETH_P_CFM: c_int = linux_raw_sys::if_ether::ETH_P_CFM as _; -#[cfg(all(linux_kernel, feature = "net"))] +#[cfg(all(linux_raw_dep, feature = "net"))] pub(crate) const ETH_P_IBOE: c_int = linux_raw_sys::if_ether::ETH_P_IBOE as _; -#[cfg(all(linux_kernel, feature = "net"))] +#[cfg(all(linux_raw_dep, feature = "net"))] pub(crate) const ETH_P_HSR: c_int = linux_raw_sys::if_ether::ETH_P_HSR as _; -#[cfg(all(linux_kernel, feature = "net"))] +#[cfg(all(linux_raw_dep, feature = "net"))] pub(crate) const ETH_P_NSH: c_int = linux_raw_sys::if_ether::ETH_P_NSH as _; -#[cfg(all(linux_kernel, feature = "net"))] +#[cfg(all(linux_raw_dep, feature = "net"))] pub(crate) const ETH_P_DSA_8021Q: c_int = linux_raw_sys::if_ether::ETH_P_DSA_8021Q as _; -#[cfg(all(linux_kernel, feature = "net"))] +#[cfg(all(linux_raw_dep, feature = "net"))] pub(crate) const ETH_P_DSA_A5PSW: c_int = linux_raw_sys::if_ether::ETH_P_DSA_A5PSW as _; -#[cfg(all(linux_kernel, feature = "net"))] +#[cfg(all(linux_raw_dep, feature = "net"))] pub(crate) const ETH_P_IFE: c_int = linux_raw_sys::if_ether::ETH_P_IFE as _; -#[cfg(all(linux_kernel, feature = "net"))] +#[cfg(all(linux_raw_dep, feature = "net"))] pub(crate) const ETH_P_CAN: c_int = linux_raw_sys::if_ether::ETH_P_CAN as _; -#[cfg(all(linux_kernel, feature = "net"))] +#[cfg(all(linux_raw_dep, feature = "net"))] pub(crate) const ETH_P_CANXL: c_int = linux_raw_sys::if_ether::ETH_P_CANXL as _; -#[cfg(all(linux_kernel, feature = "net"))] +#[cfg(all(linux_raw_dep, feature = "net"))] pub(crate) const ETH_P_XDSA: c_int = linux_raw_sys::if_ether::ETH_P_XDSA as _; -#[cfg(all(linux_kernel, feature = "net"))] +#[cfg(all(linux_raw_dep, feature = "net"))] pub(crate) const ETH_P_MAP: c_int = linux_raw_sys::if_ether::ETH_P_MAP as _; -#[cfg(all(linux_kernel, feature = "net"))] +#[cfg(all(linux_raw_dep, feature = "net"))] pub(crate) const ETH_P_MCTP: c_int = linux_raw_sys::if_ether::ETH_P_MCTP as _; - -#[cfg(all( - linux_kernel, - any( - target_arch = "mips", - target_arch = "mips32r6", - target_arch = "mips64", - target_arch = "mips64r6", - target_arch = "sparc", - target_arch = "sparc64" - ) -))] -pub(crate) const SIGEMT: c_int = linux_raw_sys::general::SIGEMT as _; +#[cfg(all(linux_raw_dep, feature = "mount"))] +pub(crate) const MS_NOSYMFOLLOW: c_ulong = linux_raw_sys::general::MS_NOSYMFOLLOW as _; // TODO: Upstream these. -#[cfg(all(linux_kernel, feature = "termios"))] +#[cfg(all(linux_raw_dep, feature = "termios"))] pub(crate) const IUCLC: tcflag_t = linux_raw_sys::general::IUCLC as _; -#[cfg(all(linux_kernel, feature = "termios"))] +#[cfg(all(linux_raw_dep, feature = "termios"))] pub(crate) const XCASE: tcflag_t = linux_raw_sys::general::XCASE as _; #[cfg(target_os = "aix")] @@ -89,7 +78,7 @@ pub(crate) const MSG_DONTWAIT: c_int = MSG_NONBLOCK; // `O_LARGEFILE` can be automatically set by the kernel on Linux: // // so libc implementations may leave it undefined or defined to zero. -#[cfg(linux_kernel)] +#[cfg(linux_raw_dep)] pub(crate) const O_LARGEFILE: c_int = linux_raw_sys::general::O_LARGEFILE as _; // Gated under `_LARGEFILE_SOURCE` but automatically set by the kernel. @@ -489,7 +478,12 @@ pub(super) use readwrite_pv64v2::{preadv64v2 as preadv2, pwritev64v2 as pwritev2 #[cfg(feature = "fs")] #[cfg(all( linux_like, - not(any(target_os = "android", target_os = "emscripten", target_env = "gnu")) + linux_raw_dep, + not(any( + target_os = "emscripten", + target_env = "gnu", + all(target_arch = "loongarch64", target_env = "musl") + )) ))] mod statx_flags { pub(crate) use linux_raw_sys::general::{ @@ -507,7 +501,13 @@ mod statx_flags { #[cfg(feature = "fs")] #[cfg(all( linux_like, - not(any(target_os = "android", target_os = "emscripten", target_env = "gnu")) + linux_raw_dep, + not(any( + target_os = "android", + target_os = "emscripten", + target_env = "gnu", + all(target_arch = "loongarch64", target_env = "musl") + )) ))] pub(crate) use statx_flags::*; @@ -515,9 +515,59 @@ pub(crate) use statx_flags::*; #[cfg(target_os = "android")] pub(crate) use __fsid_t as fsid_t; -#[cfg(feature = "mm")] -#[cfg(target_os = "android")] -pub(crate) const MAP_DROPPABLE: c_int = bitcast!(linux_raw_sys::general::MAP_DROPPABLE); +// FreeBSD added `timerfd_*` in FreeBSD 14. NetBSD added then in NetBSD 10. +#[cfg(all(feature = "time", any(target_os = "freebsd", target_os = "netbsd")))] +syscall!(pub(crate) fn timerfd_create( + clockid: c_int, + flags: c_int +) via SYS_timerfd_create -> c_int); +#[cfg(all(feature = "time", any(target_os = "freebsd", target_os = "netbsd")))] +syscall!(pub(crate) fn timerfd_gettime( + fd: c_int, + curr_value: *mut itimerspec +) via SYS_timerfd_gettime -> c_int); +#[cfg(all(feature = "time", any(target_os = "freebsd", target_os = "netbsd")))] +syscall!(pub(crate) fn timerfd_settime( + fd: c_int, + flags: c_int, + new_value: *const itimerspec, + old_value: *mut itimerspec +) via SYS_timerfd_settime -> c_int); + +#[cfg(all(feature = "time", target_os = "illumos"))] +extern "C" { + pub(crate) fn timerfd_create(clockid: c_int, flags: c_int) -> c_int; + pub(crate) fn timerfd_gettime(fd: c_int, curr_value: *mut itimerspec) -> c_int; + pub(crate) fn timerfd_settime( + fd: c_int, + flags: c_int, + new_value: *const itimerspec, + old_value: *mut itimerspec, + ) -> c_int; +} + +// illumos and NetBSD timerfd support. +// Submitted upstream in . + +// +#[cfg(all(feature = "time", target_os = "illumos"))] +pub(crate) const TFD_CLOEXEC: i32 = 0o2000000; +#[cfg(all(feature = "time", target_os = "illumos"))] +pub(crate) const TFD_NONBLOCK: i32 = 0o4000; +#[cfg(all(feature = "time", target_os = "illumos"))] +pub(crate) const TFD_TIMER_ABSTIME: i32 = 1 << 0; +#[cfg(all(feature = "time", target_os = "illumos"))] +pub(crate) const TFD_TIMER_CANCEL_ON_SET: i32 = 1 << 1; + +// +#[cfg(all(feature = "time", target_os = "netbsd"))] +pub(crate) const TFD_CLOEXEC: i32 = O_CLOEXEC; +#[cfg(all(feature = "time", target_os = "netbsd"))] +pub(crate) const TFD_NONBLOCK: i32 = O_NONBLOCK; +#[cfg(all(feature = "time", target_os = "netbsd"))] +pub(crate) const TFD_TIMER_ABSTIME: i32 = O_WRONLY; +#[cfg(all(feature = "time", target_os = "netbsd"))] +pub(crate) const TFD_TIMER_CANCEL_ON_SET: i32 = O_RDWR; #[cfg(test)] mod tests { diff --git a/src/backend/libc/fs/dir.rs b/src/backend/libc/fs/dir.rs index cd7f232d3..3c4c3896b 100644 --- a/src/backend/libc/fs/dir.rs +++ b/src/backend/libc/fs/dir.rs @@ -24,13 +24,7 @@ use crate::fs::{fstat, Stat}; target_os = "wasi", )))] use crate::fs::{fstatfs, StatFs}; -#[cfg(not(any( - solarish, - target_os = "haiku", - target_os = "redox", - target_os = "vita", - target_os = "wasi" -)))] +#[cfg(not(any(solarish, target_os = "vita", target_os = "wasi")))] use crate::fs::{fstatvfs, StatVfs}; use crate::io; #[cfg(not(any(target_os = "fuchsia", target_os = "vita", target_os = "wasi")))] @@ -81,6 +75,27 @@ impl Dir { } } + /// Returns the file descriptor associated with the directory stream. + /// + /// The file descriptor is used internally by the directory stream. As a result, it is useful + /// only for functions which do not depend or alter the file position. + /// + /// # References + /// + /// - [POSIX] + /// + /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/dirfd.html + #[inline] + #[doc(alias = "dirfd")] + pub fn fd<'a>(&'a self) -> io::Result> { + let raw_fd = unsafe { c::dirfd(self.libc_dir.as_ptr()) }; + if raw_fd < 0 { + Err(io::Errno::last_os_error()) + } else { + Ok(unsafe { BorrowedFd::borrow_raw(raw_fd) }) + } + } + /// Borrow `fd` and construct a `Dir` that reads entries from the given /// directory file descriptor. #[inline] @@ -243,9 +258,7 @@ impl Dir { /// `fstatvfs(self)` #[cfg(not(any( solarish, - target_os = "haiku", target_os = "horizon", - target_os = "redox", target_os = "vita", target_os = "wasi" )))] diff --git a/src/backend/libc/fs/makedev.rs b/src/backend/libc/fs/makedev.rs index a55211845..188075506 100644 --- a/src/backend/libc/fs/makedev.rs +++ b/src/backend/libc/fs/makedev.rs @@ -14,6 +14,7 @@ use crate::fs::Dev; target_os = "aix", target_os = "android", target_os = "emscripten", + target_os = "redox", )))] #[inline] pub(crate) fn makedev(maj: u32, min: u32) -> Dev { @@ -44,6 +45,17 @@ pub(crate) fn makedev(maj: u32, min: u32) -> Dev { | (u64::from(min) & 0x0000_00ff_u64) } +#[cfg(target_os = "redox")] +#[inline] +pub(crate) fn makedev(maj: u32, min: u32) -> Dev { + // Redox's makedev is reportedly similar to 32-bit Android's but the return + // type is signed. + ((i64::from(maj) & 0xffff_f000_i64) << 32) + | ((i64::from(maj) & 0x0000_0fff_i64) << 8) + | ((i64::from(min) & 0xffff_ff00_i64) << 12) + | (i64::from(min) & 0x0000_00ff_i64) +} + #[cfg(target_os = "emscripten")] #[inline] pub(crate) fn makedev(maj: u32, min: u32) -> Dev { @@ -70,7 +82,8 @@ pub(crate) fn makedev(maj: u32, min: u32) -> Dev { freebsdlike, target_os = "android", target_os = "emscripten", - target_os = "netbsd" + target_os = "netbsd", + target_os = "redox" )))] #[inline] pub(crate) fn major(dev: Dev) -> u32 { @@ -89,7 +102,10 @@ pub(crate) fn major(dev: Dev) -> u32 { (unsafe { c::major(dev) }) as u32 } -#[cfg(all(target_os = "android", target_pointer_width = "32"))] +#[cfg(any( + all(target_os = "android", target_pointer_width = "32"), + target_os = "redox" +))] #[inline] pub(crate) fn major(dev: Dev) -> u32 { // 32-bit Android's `dev_t` is 32-bit, but its `st_dev` is 64-bit, so we do @@ -109,7 +125,8 @@ pub(crate) fn major(dev: Dev) -> u32 { freebsdlike, target_os = "android", target_os = "emscripten", - target_os = "netbsd" + target_os = "netbsd", + target_os = "redox", )))] #[inline] pub(crate) fn minor(dev: Dev) -> u32 { @@ -128,7 +145,10 @@ pub(crate) fn minor(dev: Dev) -> u32 { (unsafe { c::minor(dev) }) as u32 } -#[cfg(all(target_os = "android", target_pointer_width = "32"))] +#[cfg(any( + all(target_os = "android", target_pointer_width = "32"), + target_os = "redox" +))] #[inline] pub(crate) fn minor(dev: Dev) -> u32 { // 32-bit Android's `dev_t` is 32-bit, but its `st_dev` is 64-bit, so we do diff --git a/src/backend/libc/fs/mod.rs b/src/backend/libc/fs/mod.rs index 264b955c2..e5e821ea7 100644 --- a/src/backend/libc/fs/mod.rs +++ b/src/backend/libc/fs/mod.rs @@ -6,7 +6,6 @@ pub mod inotify; target_os = "espidf", target_os = "haiku", target_os = "horizon", - target_os = "redox", target_os = "vita", target_os = "wasi" )))] @@ -16,9 +15,12 @@ pub(crate) mod syscalls; pub(crate) mod types; // TODO: Fix linux-raw-sys to define ioctl codes for sparc. -#[cfg(all(linux_kernel, any(target_arch = "sparc", target_arch = "sparc64")))] +#[cfg(all(linux_raw_dep, any(target_arch = "sparc", target_arch = "sparc64")))] pub(crate) const EXT4_IOC_RESIZE_FS: crate::ioctl::Opcode = 0x8008_6610; -#[cfg(all(linux_kernel, not(any(target_arch = "sparc", target_arch = "sparc64"))))] +#[cfg(all( + linux_raw_dep, + not(any(target_arch = "sparc", target_arch = "sparc64")) +))] pub(crate) const EXT4_IOC_RESIZE_FS: crate::ioctl::Opcode = linux_raw_sys::ioctl::EXT4_IOC_RESIZE_FS as crate::ioctl::Opcode; diff --git a/src/backend/libc/fs/syscalls.rs b/src/backend/libc/fs/syscalls.rs index f2ac4f73d..8cbd3a2c1 100644 --- a/src/backend/libc/fs/syscalls.rs +++ b/src/backend/libc/fs/syscalls.rs @@ -60,7 +60,7 @@ use crate::fs::Timestamps; )))] use crate::fs::{Dev, FileType}; use crate::fs::{Mode, OFlags, SeekFrom, Stat}; -#[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))] +#[cfg(not(target_os = "wasi"))] use crate::fs::{StatVfs, StatVfsMountFlags}; use crate::io; #[cfg(all(target_env = "gnu", fix_y2038))] @@ -271,7 +271,7 @@ pub(crate) fn statfs(filename: &CStr) -> io::Result { } } -#[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))] +#[cfg(not(target_os = "wasi"))] #[inline] pub(crate) fn statvfs(filename: &CStr) -> io::Result { unsafe { @@ -1591,7 +1591,7 @@ pub(crate) fn fstatfs(fd: BorrowedFd<'_>) -> io::Result { } } -#[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))] +#[cfg(not(target_os = "wasi"))] pub(crate) fn fstatvfs(fd: BorrowedFd<'_>) -> io::Result { let mut statvfs = MaybeUninit::::uninit(); unsafe { @@ -1600,7 +1600,7 @@ pub(crate) fn fstatvfs(fd: BorrowedFd<'_>) -> io::Result { } } -#[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))] +#[cfg(not(target_os = "wasi"))] fn libc_statvfs_to_statvfs(from: c::statvfs) -> StatVfs { StatVfs { f_bsize: from.f_bsize as u64, @@ -1840,7 +1840,7 @@ pub(crate) fn memfd_create(name: &CStr, flags: MemfdFlags) -> io::Result, path: &CStr, @@ -2053,9 +2053,9 @@ pub(crate) fn statx( // doesn't represent all the known flags. // // [it's deprecated]: https://patchwork.kernel.org/project/linux-fsdevel/patch/20200505095915.11275-7-mszeredi@redhat.com/ - #[cfg(not(any(target_os = "android", target_env = "musl")))] + #[cfg(any(not(linux_raw_dep), not(target_env = "musl")))] const STATX__RESERVED: u32 = c::STATX__RESERVED as u32; - #[cfg(any(target_os = "android", target_env = "musl"))] + #[cfg(target_env = "musl")] const STATX__RESERVED: u32 = linux_raw_sys::general::STATX__RESERVED; if (mask.bits() & STATX__RESERVED) == STATX__RESERVED { return Err(io::Errno::INVAL); diff --git a/src/backend/libc/fs/types.rs b/src/backend/libc/fs/types.rs index 8f1f47927..d570c5b28 100644 --- a/src/backend/libc/fs/types.rs +++ b/src/backend/libc/fs/types.rs @@ -736,6 +736,9 @@ bitflags! { /// `F_SEAL_FUTURE_WRITE` (since Linux 5.1) #[cfg(linux_kernel)] const FUTURE_WRITE = bitcast!(c::F_SEAL_FUTURE_WRITE); + /// `F_SEAL_EXEC` (since Linux 6.3) + #[cfg(linux_kernel)] + const EXEC = bitcast!(c::F_SEAL_EXEC); /// const _ = !0; @@ -842,7 +845,7 @@ bitflags! { } } -#[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))] +#[cfg(not(target_os = "wasi"))] bitflags! { /// `ST_*` constants for use with [`StatVfs`]. #[repr(transparent)] @@ -874,11 +877,11 @@ bitflags! { const NOEXEC = c::ST_NOEXEC as u64; /// `ST_NOSUID` - #[cfg(not(any(target_os = "espidf", target_os = "horizon", target_os = "vita")))] + #[cfg(not(any(target_os = "espidf", target_os = "haiku", target_os = "horizon", target_os = "redox", target_os = "vita")))] const NOSUID = c::ST_NOSUID as u64; /// `ST_RDONLY` - #[cfg(not(any(target_os = "espidf", target_os = "horizon", target_os = "vita")))] + #[cfg(not(any(target_os = "espidf", target_os = "haiku", target_os = "horizon", target_os = "redox", target_os = "vita")))] const RDONLY = c::ST_RDONLY as u64; /// `ST_RELATIME` @@ -1074,7 +1077,7 @@ pub type Fsid = c::fsid_t; /// /// [`statvfs`]: crate::fs::statvfs /// [`fstatvfs`]: crate::fs::fstatvfs -#[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))] +#[cfg(not(target_os = "wasi"))] #[allow(missing_docs)] pub struct StatVfs { pub f_bsize: u64, diff --git a/src/backend/libc/io/syscalls.rs b/src/backend/libc/io/syscalls.rs index f1231aaf4..d91e983c2 100644 --- a/src/backend/libc/io/syscalls.rs +++ b/src/backend/libc/io/syscalls.rs @@ -13,7 +13,7 @@ use crate::fd::{AsFd as _, BorrowedFd, OwnedFd, RawFd}; target_os = "wasi" )))] use crate::io::DupFlags; -#[cfg(linux_kernel)] +#[cfg(all(linux_kernel, not(target_os = "android")))] use crate::io::ReadWriteFlags; use crate::io::{self, FdFlags}; use crate::ioctl::{IoctlOutput, Opcode}; @@ -150,7 +150,7 @@ pub(crate) fn pwritev(fd: BorrowedFd<'_>, bufs: &[IoSlice<'_>], offset: u64) -> } } -#[cfg(linux_kernel)] +#[cfg(all(linux_kernel, not(target_os = "android")))] pub(crate) fn preadv2( fd: BorrowedFd<'_>, bufs: &mut [IoSliceMut<'_>], @@ -170,7 +170,7 @@ pub(crate) fn preadv2( } } -#[cfg(linux_kernel)] +#[cfg(all(linux_kernel, not(target_os = "android")))] pub(crate) fn pwritev2( fd: BorrowedFd<'_>, bufs: &[IoSlice<'_>], diff --git a/src/backend/libc/io/types.rs b/src/backend/libc/io/types.rs index 510206f98..b434b68af 100644 --- a/src/backend/libc/io/types.rs +++ b/src/backend/libc/io/types.rs @@ -17,7 +17,7 @@ bitflags! { } } -#[cfg(linux_kernel)] +#[cfg(all(linux_kernel, not(target_os = "android")))] bitflags! { /// `RWF_*` constants for use with [`preadv2`] and [`pwritev2`]. /// @@ -27,15 +27,15 @@ bitflags! { #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct ReadWriteFlags: u32 { /// `RWF_DSYNC` (since Linux 4.7) - const DSYNC = linux_raw_sys::general::RWF_DSYNC; + const DSYNC = libc::RWF_DSYNC as u32; /// `RWF_HIPRI` (since Linux 4.6) - const HIPRI = linux_raw_sys::general::RWF_HIPRI; + const HIPRI = libc::RWF_HIPRI as u32; /// `RWF_SYNC` (since Linux 4.7) - const SYNC = linux_raw_sys::general::RWF_SYNC; + const SYNC = libc::RWF_SYNC as u32; /// `RWF_NOWAIT` (since Linux 4.14) - const NOWAIT = linux_raw_sys::general::RWF_NOWAIT; + const NOWAIT = libc::RWF_NOWAIT as u32; /// `RWF_APPEND` (since Linux 4.16) - const APPEND = linux_raw_sys::general::RWF_APPEND; + const APPEND = libc::RWF_APPEND as u32; /// const _ = !0; diff --git a/src/backend/libc/mm/types.rs b/src/backend/libc/mm/types.rs index 0b99e3c48..6f7d24f5a 100644 --- a/src/backend/libc/mm/types.rs +++ b/src/backend/libc/mm/types.rs @@ -44,19 +44,19 @@ bitflags! { #[cfg(linux_kernel)] const GROWSDOWN = bitcast!(c::PROT_GROWSDOWN); /// `PROT_SEM` - #[cfg(linux_kernel)] + #[cfg(linux_raw_dep)] const SEM = linux_raw_sys::general::PROT_SEM; /// `PROT_BTI` - #[cfg(all(linux_kernel, target_arch = "aarch64"))] + #[cfg(all(linux_raw_dep, target_arch = "aarch64"))] const BTI = linux_raw_sys::general::PROT_BTI; /// `PROT_MTE` - #[cfg(all(linux_kernel, target_arch = "aarch64"))] + #[cfg(all(linux_raw_dep, target_arch = "aarch64"))] const MTE = linux_raw_sys::general::PROT_MTE; /// `PROT_SAO` - #[cfg(all(linux_kernel, any(target_arch = "powerpc", target_arch = "powerpc64")))] + #[cfg(all(linux_raw_dep, any(target_arch = "powerpc", target_arch = "powerpc64")))] const SAO = linux_raw_sys::general::PROT_SAO; /// `PROT_ADI` - #[cfg(all(linux_kernel, any(target_arch = "sparc", target_arch = "sparc64")))] + #[cfg(all(linux_raw_dep, any(target_arch = "sparc", target_arch = "sparc64")))] const ADI = linux_raw_sys::general::PROT_ADI; /// @@ -252,7 +252,7 @@ bitflags! { #[cfg(any())] const UNINITIALIZED = bitcast!(c::MAP_UNINITIALIZED); /// `MAP_DROPPABLE` - #[cfg(linux_kernel)] + #[cfg(all(linux_kernel, not(target_os = "android")))] const DROPPABLE = bitcast!(c::MAP_DROPPABLE); /// diff --git a/src/backend/libc/mod.rs b/src/backend/libc/mod.rs index ca8afbb30..b4fd3ef46 100644 --- a/src/backend/libc/mod.rs +++ b/src/backend/libc/mod.rs @@ -109,7 +109,7 @@ pub(crate) mod event; #[cfg(feature = "fs")] pub(crate) mod fs; pub(crate) mod io; -#[cfg(linux_kernel)] +#[cfg(all(linux_kernel, not(target_os = "android")))] #[cfg(feature = "io_uring")] pub(crate) mod io_uring; #[cfg(not(any( diff --git a/src/backend/libc/mount/types.rs b/src/backend/libc/mount/types.rs index 9b88a7530..b5c7aadeb 100644 --- a/src/backend/libc/mount/types.rs +++ b/src/backend/libc/mount/types.rs @@ -56,6 +56,10 @@ bitflags! { /// `MS_SYNCHRONOUS` const SYNCHRONOUS = c::MS_SYNCHRONOUS; + /// `MS_NOSYMFOLLOW` + #[cfg(linux_raw_dep)] + const NOSYMFOLLOW = c::MS_NOSYMFOLLOW; + /// const _ = !0; } diff --git a/src/backend/libc/net/mod.rs b/src/backend/libc/net/mod.rs index da7e1df8f..201362625 100644 --- a/src/backend/libc/net/mod.rs +++ b/src/backend/libc/net/mod.rs @@ -4,7 +4,6 @@ pub(crate) mod ext; windows, target_os = "espidf", target_os = "horizon", - target_os = "redox", target_os = "vita" )))] pub(crate) mod msghdr; diff --git a/src/backend/libc/net/msghdr.rs b/src/backend/libc/net/msghdr.rs index fe5471b95..f46ac7e29 100644 --- a/src/backend/libc/net/msghdr.rs +++ b/src/backend/libc/net/msghdr.rs @@ -16,6 +16,7 @@ use core::mem::zeroed; not(any(windows, target_os = "espidf", target_os = "wasi")), any( target_os = "android", + target_os = "redox", all( target_os = "linux", not(target_env = "musl"), @@ -30,15 +31,10 @@ fn msg_iov_len(len: usize) -> c::size_t { /// Convert the value to the `msg_iovlen` field of a `msghdr` struct. #[cfg(all( - not(any( - windows, - target_os = "espidf", - target_os = "redox", - target_os = "vita", - target_os = "wasi" - )), + not(any(windows, target_os = "espidf", target_os = "vita", target_os = "wasi")), not(any( target_os = "android", + target_os = "redox", all( target_os = "linux", not(target_env = "musl"), @@ -83,7 +79,6 @@ fn msg_control_len(len: usize) -> c::socklen_t { target_os = "haiku", target_os = "hurd", target_os = "nto", - target_os = "redox", target_os = "vita", target_os = "wasi", )))] @@ -179,7 +174,7 @@ pub(crate) unsafe fn with_msghdr( } /// Create a zero-initialized message header struct value. -#[cfg(all(unix, not(target_os = "redox")))] +#[cfg(unix)] pub(crate) fn zero_msghdr() -> c::msghdr { // SAFETY: We can't initialize all the fields by value because on some // platforms the `msghdr` struct in the libc crate contains private padding diff --git a/src/backend/libc/net/netdevice.rs b/src/backend/libc/net/netdevice.rs index 887f768db..afdbb67fb 100644 --- a/src/backend/libc/net/netdevice.rs +++ b/src/backend/libc/net/netdevice.rs @@ -2,15 +2,11 @@ #![allow(unsafe_code)] -#[cfg(feature = "alloc")] -use crate::alloc::string::String; use crate::backend::c; use crate::backend::io::syscalls::ioctl; use crate::fd::BorrowedFd; use crate::io; -#[cfg(feature = "alloc")] -use c::SIOCGIFNAME; -use c::{__c_anonymous_ifr_ifru, c_char, ifreq, IFNAMSIZ, SIOCGIFINDEX}; +use c::{__c_anonymous_ifr_ifru, c_char, ifreq, IFNAMSIZ, SIOCGIFINDEX, SIOCGIFNAME}; pub(crate) fn name_to_index(fd: BorrowedFd<'_>, if_name: &str) -> io::Result { let if_name_bytes = if_name.as_bytes(); @@ -31,8 +27,7 @@ pub(crate) fn name_to_index(fd: BorrowedFd<'_>, if_name: &str) -> io::Result, index: u32) -> io::Result { +pub(crate) fn index_to_name(fd: BorrowedFd<'_>, index: u32) -> io::Result<(usize, [u8; 16])> { let mut ifreq = ifreq { ifr_name: [0; 16], ifr_ifru: __c_anonymous_ifr_ifru { @@ -43,12 +38,12 @@ pub(crate) fn index_to_name(fd: BorrowedFd<'_>, index: u32) -> io::Result(fd: BorrowedFd<'_>, level: i32, optname: i32) -> io::Result { @@ -502,6 +506,36 @@ pub(crate) fn ipv6_mtu(fd: BorrowedFd<'_>) -> io::Result { getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_MTU) } +#[cfg(linux_kernel)] +#[inline] +pub(crate) fn set_ip_mtu_discover( + fd: BorrowedFd<'_>, + value: Ipv4PathMtuDiscovery, +) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IP, c::IP_MTU_DISCOVER, value) +} + +#[cfg(linux_kernel)] +#[inline] +pub(crate) fn ip_mtu_discover(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::IPPROTO_IP, c::IP_MTU_DISCOVER) +} + +#[cfg(linux_kernel)] +#[inline] +pub(crate) fn set_ipv6_mtu_discover( + fd: BorrowedFd<'_>, + value: Ipv6PathMtuDiscovery, +) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_MTU_DISCOVER, value) +} + +#[cfg(linux_kernel)] +#[inline] +pub(crate) fn ipv6_mtu_discover(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_MTU_DISCOVER) +} + #[inline] pub(crate) fn set_ip_multicast_if(fd: BorrowedFd<'_>, value: &Ipv4Addr) -> io::Result<()> { setsockopt(fd, c::IPPROTO_IP, c::IP_MULTICAST_IF, to_imr_addr(value)) @@ -1060,6 +1094,35 @@ pub(crate) fn socket_peercred(fd: BorrowedFd<'_>) -> io::Result { getsockopt(fd, c::SOL_SOCKET, c::SO_PEERCRED) } +#[cfg(all(target_os = "linux", feature = "time"))] +#[inline] +pub(crate) fn set_txtime( + fd: BorrowedFd<'_>, + clockid: ClockId, + flags: TxTimeFlags, +) -> io::Result<()> { + setsockopt( + fd, + c::SOL_SOCKET, + c::SO_TXTIME, + c::sock_txtime { + clockid: clockid as _, + flags: flags.bits(), + }, + ) +} + +#[cfg(all(target_os = "linux", feature = "time"))] +#[inline] +pub(crate) fn get_txtime(fd: BorrowedFd<'_>) -> io::Result<(ClockId, TxTimeFlags)> { + let txtime: c::sock_txtime = getsockopt(fd, c::SOL_SOCKET, c::SO_TXTIME)?; + + Ok(( + txtime.clockid.try_into().map_err(|_| io::Errno::RANGE)?, + TxTimeFlags::from_bits(txtime.flags).ok_or(io::Errno::RANGE)?, + )) +} + #[cfg(target_os = "linux")] #[inline] pub(crate) fn set_xdp_umem_reg(fd: BorrowedFd<'_>, value: XdpUmemReg) -> io::Result<()> { @@ -1090,7 +1153,7 @@ pub(crate) fn set_xdp_rx_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result setsockopt(fd, c::SOL_XDP, c::XDP_RX_RING, value) } -#[cfg(target_os = "linux")] +#[cfg(all(linux_raw_dep, target_os = "linux"))] #[inline] pub(crate) fn xdp_mmap_offsets(fd: BorrowedFd<'_>) -> io::Result { // The kernel will write `xdp_mmap_offsets` or `xdp_mmap_offsets_v1` to the @@ -1177,7 +1240,7 @@ pub(crate) fn xdp_mmap_offsets(fd: BorrowedFd<'_>) -> io::Result } } -#[cfg(target_os = "linux")] +#[cfg(all(linux_raw_dep, target_os = "linux"))] #[inline] pub(crate) fn xdp_statistics(fd: BorrowedFd<'_>) -> io::Result { let mut optlen = size_of::().try_into().unwrap(); @@ -1306,10 +1369,11 @@ fn to_ipv6mr_interface(interface: u32) -> c::c_uint { } // `getsockopt` and `setsockopt` represent boolean values as integers. -#[cfg(not(windows))] +// +// On Windows, this should use `BOOL`, however windows-sys moved its `BOOL` +// from `windows_sys::Win32::Foundation::BOOL` to `windows_sys::core::BOOL` +// in windows-sys 0.60, and we'd prefer type RawSocketBool = c::c_int; -#[cfg(windows)] -type RawSocketBool = BOOL; // Wrap `RawSocketBool` in a newtype to discourage misuse. #[repr(transparent)] diff --git a/src/backend/libc/net/syscalls.rs b/src/backend/libc/net/syscalls.rs index aafea0025..e9138143d 100644 --- a/src/backend/libc/net/syscalls.rs +++ b/src/backend/libc/net/syscalls.rs @@ -21,7 +21,6 @@ use core::ptr::null_mut; windows, target_os = "espidf", target_os = "horizon", - target_os = "redox", target_os = "vita" )))] use { @@ -187,7 +186,6 @@ pub(crate) fn accept(sockfd: BorrowedFd<'_>) -> io::Result { windows, target_os = "espidf", target_os = "horizon", - target_os = "redox", target_os = "vita" )))] pub(crate) fn recvmsg( @@ -222,7 +220,6 @@ pub(crate) fn recvmsg( windows, target_os = "espidf", target_os = "horizon", - target_os = "redox", target_os = "vita" )))] pub(crate) fn sendmsg( @@ -245,7 +242,6 @@ pub(crate) fn sendmsg( windows, target_os = "espidf", target_os = "horizon", - target_os = "redox", target_os = "vita" )))] pub(crate) fn sendmsg_addr( diff --git a/src/backend/libc/termios/syscalls.rs b/src/backend/libc/termios/syscalls.rs index e27f0b2e0..dbe4a6e03 100644 --- a/src/backend/libc/termios/syscalls.rs +++ b/src/backend/libc/termios/syscalls.rs @@ -493,7 +493,20 @@ pub(crate) fn set_input_speed(termios: &mut Termios, arbitrary_speed: u32) -> io #[cfg(not(any(target_os = "espidf", target_os = "nto", target_os = "wasi")))] #[inline] pub(crate) fn cfmakeraw(termios: &mut Termios) { - unsafe { c::cfmakeraw(as_mut_ptr(termios).cast()) } + unsafe { + // On AIX, cfmakeraw() has a return type of 'int' instead of 'void'. + // If the argument 'termios' is NULL, it returns -1; otherwise, it returns 0. + // We believe it is safe to ignore the return value. + #[cfg(target_os = "aix")] + { + let _ = c::cfmakeraw(as_mut_ptr(termios).cast()); + } + + #[cfg(not(target_os = "aix"))] + { + c::cfmakeraw(as_mut_ptr(termios).cast()); + } + } } pub(crate) fn isatty(fd: BorrowedFd<'_>) -> bool { diff --git a/src/backend/libc/thread/syscalls.rs b/src/backend/libc/thread/syscalls.rs index 9198a7fbe..84356fee4 100644 --- a/src/backend/libc/thread/syscalls.rs +++ b/src/backend/libc/thread/syscalls.rs @@ -343,8 +343,8 @@ pub(crate) fn setns(fd: BorrowedFd<'_>, nstype: c::c_int) -> io::Result io::Result<()> { - unsafe { ret(c::unshare(flags.bits() as i32)) } +pub(crate) unsafe fn unshare(flags: crate::thread::UnshareFlags) -> io::Result<()> { + ret(c::unshare(flags.bits() as i32)) } #[cfg(linux_kernel)] @@ -398,9 +398,9 @@ pub(crate) fn setuid_thread(uid: crate::ugid::Uid) -> io::Result<()> { #[cfg(linux_kernel)] #[inline] pub(crate) fn setresuid_thread( - ruid: crate::ugid::Uid, - euid: crate::ugid::Uid, - suid: crate::ugid::Uid, + ruid: Option, + euid: Option, + suid: Option, ) -> io::Result<()> { #[cfg(any(target_arch = "x86", target_arch = "arm", target_arch = "sparc"))] const SYS: c::c_long = c::SYS_setresuid32 as c::c_long; @@ -411,7 +411,13 @@ pub(crate) fn setresuid_thread( fn setresuid(ruid: c::uid_t, euid: c::uid_t, suid: c::uid_t) via SYS -> c::c_int } - unsafe { ret(setresuid(ruid.as_raw(), euid.as_raw(), suid.as_raw())) } + unsafe { + ret(setresuid( + ruid.map_or(-1_i32 as u32, |x| x.as_raw()), + euid.map_or(-1_i32 as u32, |x| x.as_raw()), + suid.map_or(-1_i32 as u32, |x| x.as_raw()), + )) + } } #[cfg(linux_kernel)] @@ -427,9 +433,9 @@ pub(crate) fn setgid_thread(gid: crate::ugid::Gid) -> io::Result<()> { #[cfg(linux_kernel)] #[inline] pub(crate) fn setresgid_thread( - rgid: crate::ugid::Gid, - egid: crate::ugid::Gid, - sgid: crate::ugid::Gid, + rgid: Option, + egid: Option, + sgid: Option, ) -> io::Result<()> { #[cfg(any(target_arch = "x86", target_arch = "arm", target_arch = "sparc"))] const SYS: c::c_long = c::SYS_setresgid32 as c::c_long; @@ -440,7 +446,13 @@ pub(crate) fn setresgid_thread( fn setresgid(rgid: c::gid_t, egid: c::gid_t, sgid: c::gid_t) via SYS -> c::c_int } - unsafe { ret(setresgid(rgid.as_raw(), egid.as_raw(), sgid.as_raw())) } + unsafe { + ret(setresgid( + rgid.map_or(-1_i32 as u32, |x| x.as_raw()), + egid.map_or(-1_i32 as u32, |x| x.as_raw()), + sgid.map_or(-1_i32 as u32, |x| x.as_raw()), + )) + } } /// # Safety diff --git a/src/backend/libc/time/syscalls.rs b/src/backend/libc/time/syscalls.rs index c17a9d2f0..8c730cef0 100644 --- a/src/backend/libc/time/syscalls.rs +++ b/src/backend/libc/time/syscalls.rs @@ -2,7 +2,13 @@ use crate::backend::c; use crate::backend::conv::ret; -#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "netbsd" +))] #[cfg(any(all(target_env = "gnu", fix_y2038), not(fix_y2038)))] use crate::backend::time::types::LibcItimerspec; #[cfg(not(target_os = "wasi"))] @@ -21,7 +27,13 @@ use crate::timespec::as_libc_timespec_ptr; use crate::timespec::LibcTimespec; use crate::timespec::Timespec; use core::mem::MaybeUninit; -#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "netbsd" +))] use { crate::backend::conv::{borrowed_fd, ret_owned_fd}, crate::fd::{BorrowedFd, OwnedFd}, @@ -34,10 +46,10 @@ weak!(fn __clock_gettime64(c::clockid_t, *mut LibcTimespec) -> c::c_int); weak!(fn __clock_settime64(c::clockid_t, *const LibcTimespec) -> c::c_int); #[cfg(all(target_env = "gnu", fix_y2038))] weak!(fn __clock_getres64(c::clockid_t, *mut LibcTimespec) -> c::c_int); -#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(any(linux_kernel, target_os = "freebsd", target_os = "fuchsia"))] #[cfg(all(target_env = "gnu", fix_y2038))] weak!(fn __timerfd_gettime64(c::c_int, *mut LibcItimerspec) -> c::c_int); -#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(any(linux_kernel, target_os = "freebsd", target_os = "fuchsia"))] #[cfg(all(target_env = "gnu", fix_y2038))] weak!(fn __timerfd_settime64(c::c_int, c::c_int, *const LibcItimerspec, *mut LibcItimerspec) -> c::c_int); @@ -172,7 +184,7 @@ pub(crate) fn clock_gettime_dynamic(id: DynamicClockId<'_>) -> io::Result c::CLOCK_REALTIME_ALARM, #[cfg(linux_kernel)] @@ -298,12 +310,24 @@ fn clock_settime_old(id: ClockId, timespec: Timespec) -> io::Result<()> { unsafe { ret(c::clock_settime(id as c::clockid_t, &old_timespec)) } } -#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "netbsd" +))] pub(crate) fn timerfd_create(id: TimerfdClockId, flags: TimerfdFlags) -> io::Result { unsafe { ret_owned_fd(c::timerfd_create(id as c::clockid_t, bitflags_bits!(flags))) } } -#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "netbsd" +))] pub(crate) fn timerfd_settime( fd: BorrowedFd<'_>, flags: TimerfdTimerFlags, @@ -345,7 +369,13 @@ pub(crate) fn timerfd_settime( } } -#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "netbsd" +))] #[cfg(fix_y2038)] fn timerfd_settime_old( fd: BorrowedFd<'_>, @@ -412,7 +442,13 @@ fn timerfd_settime_old( }) } -#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "netbsd" +))] pub(crate) fn timerfd_gettime(fd: BorrowedFd<'_>) -> io::Result { // Old 32-bit version: libc has `timerfd_gettime` but it is not y2038 safe // by default. But there may be a `__timerfd_gettime64` we can use. @@ -443,7 +479,13 @@ pub(crate) fn timerfd_gettime(fd: BorrowedFd<'_>) -> io::Result { } } -#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "netbsd" +))] #[cfg(fix_y2038)] fn timerfd_gettime_old(fd: BorrowedFd<'_>) -> io::Result { let mut old_result = MaybeUninit::::uninit(); diff --git a/src/backend/libc/time/types.rs b/src/backend/libc/time/types.rs index 0a7fd83c3..b6e0bdc4e 100644 --- a/src/backend/libc/time/types.rs +++ b/src/backend/libc/time/types.rs @@ -1,21 +1,57 @@ -#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "netbsd" +))] use crate::backend::c; -#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "netbsd" +))] use crate::time::Itimerspec; -#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "netbsd" +))] #[cfg(fix_y2038)] use crate::timespec::LibcTimespec; -#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "netbsd" +))] use bitflags::bitflags; /// On most platforms, `LibcItimerspec` is just `Itimerspec`. -#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "netbsd" +))] #[cfg(not(fix_y2038))] pub(crate) type LibcItimerspec = Itimerspec; /// On 32-bit glibc platforms, `LibcTimespec` differs from `Timespec`, so we /// define our own struct, with bidirectional `From` impls. -#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "netbsd" +))] #[cfg(fix_y2038)] #[repr(C)] #[derive(Debug, Clone)] @@ -24,7 +60,13 @@ pub(crate) struct LibcItimerspec { pub it_value: LibcTimespec, } -#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "netbsd" +))] #[cfg(fix_y2038)] impl From for Itimerspec { #[inline] @@ -36,7 +78,13 @@ impl From for Itimerspec { } } -#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "netbsd" +))] #[cfg(fix_y2038)] impl From for LibcItimerspec { #[inline] @@ -48,7 +96,13 @@ impl From for LibcItimerspec { } } -#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "netbsd" +))] #[cfg(not(fix_y2038))] pub(crate) fn as_libc_itimerspec_ptr(itimerspec: &Itimerspec) -> *const c::itimerspec { #[cfg(test)] @@ -58,7 +112,13 @@ pub(crate) fn as_libc_itimerspec_ptr(itimerspec: &Itimerspec) -> *const c::itime crate::utils::as_ptr(itimerspec).cast::() } -#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "netbsd" +))] #[cfg(not(fix_y2038))] pub(crate) fn as_libc_itimerspec_mut_ptr( itimerspec: &mut core::mem::MaybeUninit, @@ -70,7 +130,13 @@ pub(crate) fn as_libc_itimerspec_mut_ptr( itimerspec.as_mut_ptr().cast::() } -#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "netbsd" +))] bitflags! { /// `TFD_*` flags for use with [`timerfd_create`]. /// @@ -91,7 +157,13 @@ bitflags! { } } -#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "netbsd" +))] bitflags! { /// `TFD_TIMER_*` flags for use with [`timerfd_settime`]. /// @@ -104,7 +176,7 @@ bitflags! { const ABSTIME = bitcast!(c::TFD_TIMER_ABSTIME); /// `TFD_TIMER_CANCEL_ON_SET` - #[cfg(linux_kernel)] + #[cfg(not(target_os = "fuchsia"))] #[doc(alias = "TFD_TIMER_CANCEL_ON_SET")] const CANCEL_ON_SET = bitcast!(c::TFD_TIMER_CANCEL_ON_SET); @@ -116,7 +188,13 @@ bitflags! { /// `CLOCK_*` constants for use with [`timerfd_create`]. /// /// [`timerfd_create`]: crate::time::timerfd_create -#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "netbsd" +))] #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] #[repr(u32)] #[non_exhaustive] @@ -146,6 +224,7 @@ pub enum TimerfdClockId { /// This clock is similar to `Monotonic`, but does advance while the system /// is suspended. #[doc(alias = "CLOCK_BOOTTIME")] + #[cfg(any(linux_kernel, target_os = "fuchsia", target_os = "openbsd"))] Boottime = bitcast!(c::CLOCK_BOOTTIME), /// `CLOCK_REALTIME_ALARM`—Like `Realtime`, but wakes a suspended system. @@ -153,6 +232,7 @@ pub enum TimerfdClockId { /// This clock is like `Realtime`, but can wake up a suspended system. /// /// Use of this clock requires the `CAP_WAKE_ALARM` Linux capability. + #[cfg(linux_kernel)] #[doc(alias = "CLOCK_REALTIME_ALARM")] RealtimeAlarm = bitcast!(c::CLOCK_REALTIME_ALARM), @@ -161,6 +241,7 @@ pub enum TimerfdClockId { /// This clock is like `Boottime`, but can wake up a suspended system. /// /// Use of this clock requires the `CAP_WAKE_ALARM` Linux capability. + #[cfg(any(linux_kernel, target_os = "cygwin", target_os = "fuchsia"))] #[doc(alias = "CLOCK_BOOTTIME_ALARM")] BoottimeAlarm = bitcast!(c::CLOCK_BOOTTIME_ALARM), } @@ -170,7 +251,13 @@ mod tests { #[allow(unused_imports)] use super::*; - #[cfg(any(linux_kernel, target_os = "fuchsia"))] + #[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "netbsd" + ))] #[test] fn test_types() { assert_eq_size!(TimerfdFlags, c::c_int); diff --git a/src/backend/linux_raw/arch/powerpc.rs b/src/backend/linux_raw/arch/powerpc.rs index 481b49f46..68ea140a6 100644 --- a/src/backend/linux_raw/arch/powerpc.rs +++ b/src/backend/linux_raw/arch/powerpc.rs @@ -34,6 +34,8 @@ pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber<'_>) -> Ret lateout("r11") _, lateout("r12") _, lateout("cr0") _, + lateout("ctr") _, + lateout("xer") _, options(nostack, preserves_flags, readonly) ); FromAsm::from_asm(r0) @@ -59,6 +61,8 @@ pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, lateout("r11") _, lateout("r12") _, lateout("cr0") _, + lateout("ctr") _, + lateout("xer") _, options(nostack, preserves_flags) ); FromAsm::from_asm(r0) @@ -87,6 +91,8 @@ pub(in crate::backend) unsafe fn syscall1_readonly( lateout("r11") _, lateout("r12") _, lateout("cr0") _, + lateout("ctr") _, + lateout("xer") _, options(nostack, preserves_flags, readonly) ); FromAsm::from_asm(r0) @@ -127,6 +133,8 @@ pub(in crate::backend) unsafe fn syscall2( lateout("r11") _, lateout("r12") _, lateout("cr0") _, + lateout("ctr") _, + lateout("xer") _, options(nostack, preserves_flags) ); FromAsm::from_asm(r0) @@ -156,6 +164,8 @@ pub(in crate::backend) unsafe fn syscall2_readonly( lateout("r11") _, lateout("r12") _, lateout("cr0") _, + lateout("ctr") _, + lateout("xer") _, options(nostack, preserves_flags, readonly) ); FromAsm::from_asm(r0) @@ -186,6 +196,8 @@ pub(in crate::backend) unsafe fn syscall3( lateout("r11") _, lateout("r12") _, lateout("cr0") _, + lateout("ctr") _, + lateout("xer") _, options(nostack, preserves_flags) ); FromAsm::from_asm(r0) @@ -216,6 +228,8 @@ pub(in crate::backend) unsafe fn syscall3_readonly( lateout("r11") _, lateout("r12") _, lateout("cr0") _, + lateout("ctr") _, + lateout("xer") _, options(nostack, preserves_flags, readonly) ); FromAsm::from_asm(r0) @@ -247,6 +261,8 @@ pub(in crate::backend) unsafe fn syscall4( lateout("r11") _, lateout("r12") _, lateout("cr0") _, + lateout("ctr") _, + lateout("xer") _, options(nostack, preserves_flags) ); FromAsm::from_asm(r0) @@ -278,6 +294,8 @@ pub(in crate::backend) unsafe fn syscall4_readonly( lateout("r11") _, lateout("r12") _, lateout("cr0") _, + lateout("ctr") _, + lateout("xer") _, options(nostack, preserves_flags, readonly) ); FromAsm::from_asm(r0) @@ -310,6 +328,8 @@ pub(in crate::backend) unsafe fn syscall5( lateout("r11") _, lateout("r12") _, lateout("cr0") _, + lateout("ctr") _, + lateout("xer") _, options(nostack, preserves_flags) ); FromAsm::from_asm(r0) @@ -342,6 +362,8 @@ pub(in crate::backend) unsafe fn syscall5_readonly( lateout("r11") _, lateout("r12") _, lateout("cr0") _, + lateout("ctr") _, + lateout("xer") _, options(nostack, preserves_flags, readonly) ); FromAsm::from_asm(r0) @@ -375,6 +397,8 @@ pub(in crate::backend) unsafe fn syscall6( lateout("r11") _, lateout("r12") _, lateout("cr0") _, + lateout("ctr") _, + lateout("xer") _, options(nostack, preserves_flags) ); FromAsm::from_asm(r0) @@ -408,6 +432,8 @@ pub(in crate::backend) unsafe fn syscall6_readonly( lateout("r11") _, lateout("r12") _, lateout("cr0") _, + lateout("ctr") _, + lateout("xer") _, options(nostack, preserves_flags, readonly) ); FromAsm::from_asm(r0) diff --git a/src/backend/linux_raw/arch/powerpc64.rs b/src/backend/linux_raw/arch/powerpc64.rs index f21ed6aa1..d48501394 100644 --- a/src/backend/linux_raw/arch/powerpc64.rs +++ b/src/backend/linux_raw/arch/powerpc64.rs @@ -34,6 +34,8 @@ pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber<'_>) -> Ret lateout("r11") _, lateout("r12") _, lateout("cr0") _, + lateout("ctr") _, + lateout("xer") _, options(nostack, preserves_flags, readonly) ); FromAsm::from_asm(r0) @@ -59,6 +61,8 @@ pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, lateout("r11") _, lateout("r12") _, lateout("cr0") _, + lateout("ctr") _, + lateout("xer") _, options(nostack, preserves_flags) ); FromAsm::from_asm(r0) @@ -87,6 +91,8 @@ pub(in crate::backend) unsafe fn syscall1_readonly( lateout("r11") _, lateout("r12") _, lateout("cr0") _, + lateout("ctr") _, + lateout("xer") _, options(nostack, preserves_flags, readonly) ); FromAsm::from_asm(r0) @@ -127,6 +133,8 @@ pub(in crate::backend) unsafe fn syscall2( lateout("r11") _, lateout("r12") _, lateout("cr0") _, + lateout("ctr") _, + lateout("xer") _, options(nostack, preserves_flags) ); FromAsm::from_asm(r0) @@ -156,6 +164,8 @@ pub(in crate::backend) unsafe fn syscall2_readonly( lateout("r11") _, lateout("r12") _, lateout("cr0") _, + lateout("ctr") _, + lateout("xer") _, options(nostack, preserves_flags, readonly) ); FromAsm::from_asm(r0) @@ -186,6 +196,8 @@ pub(in crate::backend) unsafe fn syscall3( lateout("r11") _, lateout("r12") _, lateout("cr0") _, + lateout("ctr") _, + lateout("xer") _, options(nostack, preserves_flags) ); FromAsm::from_asm(r0) @@ -216,6 +228,8 @@ pub(in crate::backend) unsafe fn syscall3_readonly( lateout("r11") _, lateout("r12") _, lateout("cr0") _, + lateout("ctr") _, + lateout("xer") _, options(nostack, preserves_flags, readonly) ); FromAsm::from_asm(r0) @@ -247,6 +261,8 @@ pub(in crate::backend) unsafe fn syscall4( lateout("r11") _, lateout("r12") _, lateout("cr0") _, + lateout("ctr") _, + lateout("xer") _, options(nostack, preserves_flags) ); FromAsm::from_asm(r0) @@ -278,6 +294,8 @@ pub(in crate::backend) unsafe fn syscall4_readonly( lateout("r11") _, lateout("r12") _, lateout("cr0") _, + lateout("ctr") _, + lateout("xer") _, options(nostack, preserves_flags, readonly) ); FromAsm::from_asm(r0) @@ -310,6 +328,8 @@ pub(in crate::backend) unsafe fn syscall5( lateout("r11") _, lateout("r12") _, lateout("cr0") _, + lateout("ctr") _, + lateout("xer") _, options(nostack, preserves_flags) ); FromAsm::from_asm(r0) @@ -342,6 +362,8 @@ pub(in crate::backend) unsafe fn syscall5_readonly( lateout("r11") _, lateout("r12") _, lateout("cr0") _, + lateout("ctr") _, + lateout("xer") _, options(nostack, preserves_flags, readonly) ); FromAsm::from_asm(r0) @@ -375,6 +397,8 @@ pub(in crate::backend) unsafe fn syscall6( lateout("r11") _, lateout("r12") _, lateout("cr0") _, + lateout("ctr") _, + lateout("xer") _, options(nostack, preserves_flags) ); FromAsm::from_asm(r0) @@ -408,6 +432,8 @@ pub(in crate::backend) unsafe fn syscall6_readonly( lateout("r11") _, lateout("r12") _, lateout("cr0") _, + lateout("ctr") _, + lateout("xer") _, options(nostack, preserves_flags, readonly) ); FromAsm::from_asm(r0) diff --git a/src/backend/linux_raw/c.rs b/src/backend/linux_raw/c.rs index 755dadcde..762cdd479 100644 --- a/src/backend/linux_raw/c.rs +++ b/src/backend/linux_raw/c.rs @@ -10,7 +10,7 @@ pub(crate) type size_t = usize; pub(crate) use linux_raw_sys::ctypes::*; pub(crate) use linux_raw_sys::errno::{EBADF, EINVAL}; pub(crate) use linux_raw_sys::general::{__kernel_fd_set as fd_set, __FD_SETSIZE as FD_SETSIZE}; -pub(crate) use linux_raw_sys::ioctl::{FIONBIO, FIONREAD}; +pub(crate) use linux_raw_sys::ioctl::{FIOCLEX, FIONBIO, FIONCLEX, FIONREAD}; // Import the kernel's `uid_t` and `gid_t` if they're 32-bit. #[cfg(feature = "thread")] pub(crate) use linux_raw_sys::general::futex_waitv; @@ -84,14 +84,17 @@ pub(crate) use linux_raw_sys::{ AF_APPLETALK, AF_ASH, AF_ATMPVC, AF_ATMSVC, AF_AX25, AF_BLUETOOTH, AF_BRIDGE, AF_CAN, AF_ECONET, AF_IEEE802154, AF_INET, AF_INET6, AF_IPX, AF_IRDA, AF_ISDN, AF_IUCV, AF_KEY, AF_LLC, AF_NETBEUI, AF_NETLINK, AF_NETROM, AF_PACKET, AF_PHONET, AF_PPPOX, AF_RDS, AF_ROSE, - AF_RXRPC, AF_SECURITY, AF_SNA, AF_TIPC, AF_UNIX, AF_UNSPEC, AF_WANPIPE, AF_X25, AF_XDP, - IP6T_SO_ORIGINAL_DST, IPPROTO_FRAGMENT, IPPROTO_ICMPV6, IPPROTO_MH, IPPROTO_ROUTING, - IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP, IPV6_FREEBIND, IPV6_MULTICAST_HOPS, - IPV6_MULTICAST_LOOP, IPV6_RECVTCLASS, IPV6_TCLASS, IPV6_UNICAST_HOPS, IPV6_V6ONLY, - IP_ADD_MEMBERSHIP, IP_ADD_SOURCE_MEMBERSHIP, IP_DROP_MEMBERSHIP, IP_DROP_SOURCE_MEMBERSHIP, - IP_FREEBIND, IP_MULTICAST_LOOP, IP_MULTICAST_TTL, IP_RECVTOS, IP_TOS, IP_TTL, - MSG_CMSG_CLOEXEC, MSG_CONFIRM, MSG_CTRUNC, MSG_DONTROUTE, MSG_DONTWAIT, MSG_EOR, - MSG_ERRQUEUE, MSG_MORE, MSG_NOSIGNAL, MSG_OOB, MSG_PEEK, MSG_TRUNC, MSG_WAITALL, + AF_RXRPC, AF_SECURITY, AF_SNA, AF_TIPC, AF_UNIX, AF_UNSPEC, AF_VSOCK, AF_WANPIPE, AF_X25, + AF_XDP, IP6T_SO_ORIGINAL_DST, IPPROTO_FRAGMENT, IPPROTO_ICMPV6, IPPROTO_MH, + IPPROTO_ROUTING, IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP, IPV6_FREEBIND, + IPV6_MULTICAST_HOPS, IPV6_MULTICAST_LOOP, IPV6_PMTUDISC_DO, IPV6_PMTUDISC_DONT, + IPV6_PMTUDISC_INTERFACE, IPV6_PMTUDISC_OMIT, IPV6_PMTUDISC_PROBE, IPV6_PMTUDISC_WANT, + IPV6_RECVTCLASS, IPV6_TCLASS, IPV6_UNICAST_HOPS, IPV6_V6ONLY, IP_ADD_MEMBERSHIP, + IP_ADD_SOURCE_MEMBERSHIP, IP_DROP_MEMBERSHIP, IP_DROP_SOURCE_MEMBERSHIP, IP_FREEBIND, + IP_MULTICAST_LOOP, IP_MULTICAST_TTL, IP_PMTUDISC_DO, IP_PMTUDISC_DONT, + IP_PMTUDISC_INTERFACE, IP_PMTUDISC_OMIT, IP_PMTUDISC_PROBE, IP_PMTUDISC_WANT, IP_RECVTOS, + IP_TOS, IP_TTL, MSG_CMSG_CLOEXEC, MSG_CONFIRM, MSG_CTRUNC, MSG_DONTROUTE, MSG_DONTWAIT, + MSG_EOR, MSG_ERRQUEUE, MSG_MORE, MSG_NOSIGNAL, MSG_OOB, MSG_PEEK, MSG_TRUNC, MSG_WAITALL, SCM_CREDENTIALS, SCM_RIGHTS, SHUT_RD, SHUT_RDWR, SHUT_WR, SOCK_DGRAM, SOCK_RAW, SOCK_RDM, SOCK_SEQPACKET, SOCK_STREAM, SOL_SOCKET, SOL_XDP, SO_ACCEPTCONN, SO_BROADCAST, SO_COOKIE, SO_DOMAIN, SO_ERROR, SO_INCOMING_CPU, SO_KEEPALIVE, SO_LINGER, SO_OOBINLINE, @@ -114,6 +117,19 @@ pub(crate) use linux_raw_sys::{ }, }; +#[cfg(any(feature = "io_uring", feature = "time", feature = "thread"))] +pub use linux_raw_sys::general::__kernel_clockid_t as clockid_t; + +#[cfg(feature = "net")] +pub use linux_raw_sys::net::{sock_txtime, SCM_TXTIME, SO_TXTIME}; + +#[cfg(all(feature = "net", feature = "time"))] +pub(crate) const SOF_TXTIME_DEADLINE_MODE: u32 = + linux_raw_sys::net::txtime_flags::SOF_TXTIME_DEADLINE_MODE as _; +#[cfg(all(feature = "net", feature = "time"))] +pub(crate) const SOF_TXTIME_REPORT_ERRORS: u32 = + linux_raw_sys::net::txtime_flags::SOF_TXTIME_REPORT_ERRORS as _; + // Cast away bindgen's `enum` type to make these consistent with the other // `setsockopt`/`getsockopt` level values. #[cfg(feature = "net")] diff --git a/src/backend/linux_raw/conv.rs b/src/backend/linux_raw/conv.rs index 901451ae6..3d4693fe1 100644 --- a/src/backend/linux_raw/conv.rs +++ b/src/backend/linux_raw/conv.rs @@ -842,6 +842,14 @@ impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { } } +#[cfg(feature = "thread")] +impl<'a, Num: ArgNumber> From> for ArgReg<'a, Num> { + #[inline] + fn from(t: Option) -> Self { + c_uint(t.map_or(-1_i32 as u32, |x| x.as_raw())) + } +} + #[cfg(any(feature = "process", feature = "thread"))] impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { #[inline] @@ -850,6 +858,14 @@ impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { } } +#[cfg(feature = "thread")] +impl<'a, Num: ArgNumber> From> for ArgReg<'a, Num> { + #[inline] + fn from(t: Option) -> Self { + c_uint(t.map_or(-1_i32 as u32, |x| x.as_raw())) + } +} + #[cfg(feature = "runtime")] impl<'a, Num: ArgNumber> From for ArgReg<'a, Num> { #[inline] diff --git a/src/backend/linux_raw/fs/dir.rs b/src/backend/linux_raw/fs/dir.rs index 31ebc5437..ef02b470f 100644 --- a/src/backend/linux_raw/fs/dir.rs +++ b/src/backend/linux_raw/fs/dir.rs @@ -50,6 +50,22 @@ impl Dir { }) } + /// Returns the file descriptor associated with the directory stream. + /// + /// The file descriptor is used internally by the directory stream. As a result, it is useful + /// only for functions which do not depend or alter the file position. + /// + /// # References + /// + /// - [POSIX] + /// + /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/dirfd.html + #[inline] + #[doc(alias = "dirfd")] + pub fn fd<'a>(&'a self) -> io::Result> { + Ok(self.fd.as_fd()) + } + /// Borrow `fd` and construct a `Dir` that reads entries from the given /// directory file descriptor. #[inline] diff --git a/src/backend/linux_raw/fs/syscalls.rs b/src/backend/linux_raw/fs/syscalls.rs index 872dd8e35..908272f45 100644 --- a/src/backend/linux_raw/fs/syscalls.rs +++ b/src/backend/linux_raw/fs/syscalls.rs @@ -14,6 +14,10 @@ use crate::backend::conv::fs::oflags_for_open_how; target_arch = "riscv64", target_arch = "mips", target_arch = "mips32r6", + all( + target_pointer_width = "32", + any(target_arch = "arm", target_arch = "powerpc"), + ) ))] use crate::backend::conv::zero; use crate::backend::conv::{ @@ -509,6 +513,10 @@ pub(crate) fn fstat(fd: BorrowedFd<'_>) -> io::Result { unsafe { let mut result = MaybeUninit::::uninit(); ret(syscall!(__NR_fstat, fd, &mut result))?; + + #[cfg(sanitize_memory)] + crate::msan::unpoison_maybe_uninit(&result); + Ok(result.assume_init()) } } diff --git a/src/backend/linux_raw/fs/types.rs b/src/backend/linux_raw/fs/types.rs index ce6461c3d..fc19a3f47 100644 --- a/src/backend/linux_raw/fs/types.rs +++ b/src/backend/linux_raw/fs/types.rs @@ -496,6 +496,8 @@ bitflags! { const WRITE = linux_raw_sys::general::F_SEAL_WRITE; /// `F_SEAL_FUTURE_WRITE` (since Linux 5.1) const FUTURE_WRITE = linux_raw_sys::general::F_SEAL_FUTURE_WRITE; + /// `F_SEAL_EXEC` (since Linux 6.3) + const EXEC = linux_raw_sys::general::F_SEAL_EXEC; /// const _ = !0; diff --git a/src/backend/linux_raw/io/syscalls.rs b/src/backend/linux_raw/io/syscalls.rs index f40dfe5e2..527f5fe94 100644 --- a/src/backend/linux_raw/io/syscalls.rs +++ b/src/backend/linux_raw/io/syscalls.rs @@ -33,7 +33,14 @@ use linux_raw_sys::general::{F_DUPFD_CLOEXEC, F_GETFD, F_SETFD}; #[inline] pub(crate) unsafe fn read(fd: BorrowedFd<'_>, buf: (*mut u8, usize)) -> io::Result { - ret_usize(syscall!(__NR_read, fd, buf.0, pass_usize(buf.1))) + let r = ret_usize(syscall!(__NR_read, fd, buf.0, pass_usize(buf.1))); + + #[cfg(sanitize_memory)] + if let Ok(len) = r { + crate::msan::unpoison(buf.0, len); + } + + r } #[inline] @@ -52,17 +59,16 @@ pub(crate) unsafe fn pread( target_arch = "powerpc" ), ))] - { - ret_usize(syscall!( - __NR_pread64, - fd, - buf.0, - pass_usize(buf.1), - zero(), - hi(pos), - lo(pos) - )) - } + let r = ret_usize(syscall!( + __NR_pread64, + fd, + buf.0, + pass_usize(buf.1), + zero(), + hi(pos), + lo(pos) + )); + #[cfg(all( target_pointer_width = "32", not(any( @@ -72,24 +78,30 @@ pub(crate) unsafe fn pread( target_arch = "powerpc" )), ))] - { - ret_usize(syscall!( - __NR_pread64, - fd, - buf.0, - pass_usize(buf.1), - hi(pos), - lo(pos) - )) - } + let r = ret_usize(syscall!( + __NR_pread64, + fd, + buf.0, + pass_usize(buf.1), + hi(pos), + lo(pos) + )); + #[cfg(target_pointer_width = "64")] - ret_usize(syscall!( + let r = ret_usize(syscall!( __NR_pread64, fd, buf.0, pass_usize(buf.1), loff_t_from_u64(pos) - )) + )); + + #[cfg(sanitize_memory)] + if let Ok(len) = r { + crate::msan::unpoison(buf.0, len); + } + + r } #[inline] diff --git a/src/backend/linux_raw/io_uring/syscalls.rs b/src/backend/linux_raw/io_uring/syscalls.rs index 3e61b7aa6..36edd4c0c 100644 --- a/src/backend/linux_raw/io_uring/syscalls.rs +++ b/src/backend/linux_raw/io_uring/syscalls.rs @@ -29,7 +29,9 @@ pub(crate) unsafe fn io_uring_register( arg: *const c_void, nr_args: u32, ) -> io::Result { - ret_c_uint(syscall_readonly!( + // This is not `syscall_readonly` because when `opcode` is + // `IoringRegisterOp::RegisterRingFds`, `arg`'s pointee is mutated. + ret_c_uint(syscall!( __NR_io_uring_register, fd, c_uint(opcode as u32), @@ -46,7 +48,9 @@ pub(crate) unsafe fn io_uring_register_with( arg: *const c_void, nr_args: u32, ) -> io::Result { - ret_c_uint(syscall_readonly!( + // This is not `syscall_readonly` because when `opcode` is + // `IoringRegisterOp::RegisterRingFds`, `arg`'s pointee is mutated. + ret_c_uint(syscall!( __NR_io_uring_register, fd, c_uint((opcode as u32) | bitflags_bits!(flags)), diff --git a/src/backend/linux_raw/net/netdevice.rs b/src/backend/linux_raw/net/netdevice.rs index d3c2a9640..1736d9da8 100644 --- a/src/backend/linux_raw/net/netdevice.rs +++ b/src/backend/linux_raw/net/netdevice.rs @@ -8,12 +8,8 @@ use crate::io; use core::ptr::addr_of_mut; use core::{slice, str}; use linux_raw_sys::ctypes::c_char; -use linux_raw_sys::ioctl::SIOCGIFINDEX; -#[cfg(feature = "alloc")] -use linux_raw_sys::ioctl::SIOCGIFNAME; +use linux_raw_sys::ioctl::{SIOCGIFINDEX, SIOCGIFNAME}; use linux_raw_sys::net::{ifreq, ifreq__bindgen_ty_1, ifreq__bindgen_ty_2, IFNAMSIZ}; -#[cfg(feature = "alloc")] -use {alloc::borrow::ToOwned, alloc::string::String}; pub(crate) fn name_to_index(fd: BorrowedFd<'_>, if_name: &str) -> io::Result { let if_name_bytes = if_name.as_bytes(); @@ -40,8 +36,7 @@ pub(crate) fn name_to_index(fd: BorrowedFd<'_>, if_name: &str) -> io::Result, index: u32) -> io::Result { +pub(crate) fn index_to_name(fd: BorrowedFd<'_>, index: u32) -> io::Result<(usize, [u8; 16])> { let mut ifreq = ifreq { ifr_ifrn: ifreq__bindgen_ty_1 { ifrn_name: [0; 16] }, ifr_ifru: ifreq__bindgen_ty_2 { @@ -61,9 +56,10 @@ pub(crate) fn index_to_name(fd: BorrowedFd<'_>, index: u32) -> io::Result(), ifrn_name.len()) }; - str::from_utf8(ifrn_name) - .map_err(|_| io::Errno::ILSEQ) - .map(ToOwned::to_owned) + let mut name_buf = [0; 16]; + name_buf[..ifrn_name.len()].copy_from_slice(ifrn_name); + + Ok((nul_byte, name_buf)) } else { Err(io::Errno::INVAL) } diff --git a/src/backend/linux_raw/net/sockopt.rs b/src/backend/linux_raw/net/sockopt.rs index 3e5ea1fd8..85c65b085 100644 --- a/src/backend/linux_raw/net/sockopt.rs +++ b/src/backend/linux_raw/net/sockopt.rs @@ -7,13 +7,17 @@ use crate::backend::c; use crate::backend::conv::{by_mut, c_uint, ret, socklen_t}; +#[cfg(all(target_os = "linux", feature = "time"))] +use crate::clockid::ClockId; use crate::fd::BorrowedFd; #[cfg(feature = "alloc")] use crate::ffi::CStr; use crate::io; -use crate::net::sockopt::Timeout; +use crate::net::sockopt::{Ipv4PathMtuDiscovery, Ipv6PathMtuDiscovery, Timeout}; #[cfg(target_os = "linux")] use crate::net::xdp::{XdpMmapOffsets, XdpOptionsFlags, XdpRingOffset, XdpStatistics, XdpUmemReg}; +#[cfg(all(target_os = "linux", feature = "time"))] +use crate::net::TxTimeFlags; use crate::net::{ AddressFamily, Ipv4Addr, Ipv6Addr, Protocol, RawProtocol, SocketAddrBuf, SocketAddrV4, SocketAddrV6, SocketType, UCred, @@ -25,7 +29,9 @@ use alloc::string::String; use core::mem::{size_of, MaybeUninit}; use core::time::Duration; use linux_raw_sys::general::{__kernel_old_timeval, __kernel_sock_timeval}; -use linux_raw_sys::net::{IPV6_MTU, IPV6_MULTICAST_IF, IP_MTU, IP_MULTICAST_IF}; +use linux_raw_sys::net::{ + IPV6_MTU, IPV6_MTU_DISCOVER, IPV6_MULTICAST_IF, IP_MTU, IP_MTU_DISCOVER, IP_MULTICAST_IF, +}; #[cfg(target_os = "linux")] use linux_raw_sys::xdp::{xdp_mmap_offsets, xdp_statistics, xdp_statistics_v1}; #[cfg(target_arch = "x86")] @@ -462,6 +468,32 @@ pub(crate) fn ipv6_mtu(fd: BorrowedFd<'_>) -> io::Result { getsockopt(fd, c::IPPROTO_IPV6, IPV6_MTU) } +#[inline] +pub(crate) fn set_ip_mtu_discover( + fd: BorrowedFd<'_>, + value: Ipv4PathMtuDiscovery, +) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IP, IP_MTU_DISCOVER, value) +} + +#[inline] +pub(crate) fn ip_mtu_discover(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::IPPROTO_IP, IP_MTU_DISCOVER) +} + +#[inline] +pub(crate) fn set_ipv6_mtu_discover( + fd: BorrowedFd<'_>, + value: Ipv6PathMtuDiscovery, +) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IPV6, IPV6_MTU_DISCOVER, value) +} + +#[inline] +pub(crate) fn ipv6_mtu_discover(fd: BorrowedFd<'_>) -> io::Result { + getsockopt(fd, c::IPPROTO_IPV6, IPV6_MTU_DISCOVER) +} + #[inline] pub(crate) fn set_ip_multicast_if_with_ifindex( fd: BorrowedFd<'_>, @@ -848,6 +880,35 @@ pub(crate) fn socket_peercred(fd: BorrowedFd<'_>) -> io::Result { getsockopt(fd, c::SOL_SOCKET, linux_raw_sys::net::SO_PEERCRED) } +#[cfg(all(target_os = "linux", feature = "time"))] +#[inline] +pub(crate) fn set_txtime( + fd: BorrowedFd<'_>, + clockid: ClockId, + flags: TxTimeFlags, +) -> io::Result<()> { + setsockopt( + fd, + c::SOL_SOCKET, + c::SO_TXTIME, + c::sock_txtime { + clockid: clockid as _, + flags: flags.bits(), + }, + ) +} + +#[cfg(all(target_os = "linux", feature = "time"))] +#[inline] +pub(crate) fn get_txtime(fd: BorrowedFd<'_>) -> io::Result<(ClockId, TxTimeFlags)> { + let txtime: c::sock_txtime = getsockopt(fd, c::SOL_SOCKET, c::SO_TXTIME)?; + + Ok(( + txtime.clockid.try_into().map_err(|_| io::Errno::RANGE)?, + TxTimeFlags::from_bits(txtime.flags).ok_or(io::Errno::RANGE)?, + )) +} + #[cfg(target_os = "linux")] #[inline] pub(crate) fn set_xdp_umem_reg(fd: BorrowedFd<'_>, value: XdpUmemReg) -> io::Result<()> { diff --git a/src/backend/linux_raw/param/auxv.rs b/src/backend/linux_raw/param/auxv.rs index d748219a9..2c02b5bd7 100644 --- a/src/backend/linux_raw/param/auxv.rs +++ b/src/backend/linux_raw/param/auxv.rs @@ -20,15 +20,14 @@ use core::ptr::{null_mut, read_unaligned, NonNull}; use core::sync::atomic::AtomicU8; use core::sync::atomic::Ordering::Relaxed; use core::sync::atomic::{AtomicPtr, AtomicUsize}; -use linux_raw_sys::elf::*; -use linux_raw_sys::general::{ - AT_BASE, AT_CLKTCK, AT_EXECFN, AT_HWCAP, AT_HWCAP2, AT_MINSIGSTKSZ, AT_NULL, AT_PAGESZ, - AT_SYSINFO_EHDR, +use linux_raw_sys::auxvec::{ + AT_CLKTCK, AT_EXECFN, AT_HWCAP, AT_HWCAP2, AT_MINSIGSTKSZ, AT_NULL, AT_PAGESZ, AT_SYSINFO_EHDR, }; #[cfg(feature = "runtime")] -use linux_raw_sys::general::{ +use linux_raw_sys::auxvec::{ AT_EGID, AT_ENTRY, AT_EUID, AT_GID, AT_PHDR, AT_PHENT, AT_PHNUM, AT_RANDOM, AT_SECURE, AT_UID, }; +use linux_raw_sys::elf::*; #[cfg(feature = "alloc")] use {alloc::borrow::Cow, alloc::vec}; @@ -401,13 +400,12 @@ unsafe fn init_from_aux_iter(aux_iter: impl Iterator) -> Opti AT_HWCAP2 => hwcap2 = a_val as usize, AT_MINSIGSTKSZ => minsigstksz = a_val as usize, AT_EXECFN => execfn = check_raw_pointer::(a_val as *mut _)?.as_ptr(), - AT_SYSINFO_EHDR => sysinfo_ehdr = check_elf_base(a_val as *mut _)?.as_ptr(), - AT_BASE => { - // The `AT_BASE` value can be null in a static executable that - // doesn't use a dynamic linker. If so, ignore it. - if !a_val.is_null() { - let _ = check_elf_base(a_val.cast())?; + // Use the `AT_SYSINFO_EHDR` if it matches the platform rustix is + // compiled for. + AT_SYSINFO_EHDR => { + if let Some(value) = check_elf_base(a_val as *mut _) { + sysinfo_ehdr = value.as_ptr(); } } @@ -448,8 +446,7 @@ unsafe fn init_from_aux_iter(aux_iter: impl Iterator) -> Opti secure = 2; } - // The base and sysinfo_ehdr (if present) matches our platform. Accept the - // aux values. + // Accept the aux values. PAGE_SIZE.store(pagesz, Relaxed); CLOCK_TICKS_PER_SECOND.store(clktck, Relaxed); HWCAP.store(hwcap, Relaxed); diff --git a/src/backend/linux_raw/param/init.rs b/src/backend/linux_raw/param/init.rs index de1e59492..dd58cf60b 100644 --- a/src/backend/linux_raw/param/init.rs +++ b/src/backend/linux_raw/param/init.rs @@ -13,12 +13,12 @@ use core::ptr::{null_mut, read, NonNull}; #[cfg(feature = "runtime")] use core::sync::atomic::AtomicBool; use core::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; -use linux_raw_sys::elf::*; -use linux_raw_sys::general::{ +use linux_raw_sys::auxvec::{ AT_CLKTCK, AT_EXECFN, AT_HWCAP, AT_HWCAP2, AT_MINSIGSTKSZ, AT_NULL, AT_PAGESZ, AT_SYSINFO_EHDR, }; #[cfg(feature = "runtime")] -use linux_raw_sys::general::{AT_ENTRY, AT_PHDR, AT_PHENT, AT_PHNUM, AT_RANDOM, AT_SECURE}; +use linux_raw_sys::auxvec::{AT_ENTRY, AT_PHDR, AT_PHENT, AT_PHNUM, AT_RANDOM, AT_SECURE}; +use linux_raw_sys::elf::*; #[cfg(feature = "param")] #[inline] diff --git a/src/backend/linux_raw/rand/syscalls.rs b/src/backend/linux_raw/rand/syscalls.rs index acea3968c..ad5a657ef 100644 --- a/src/backend/linux_raw/rand/syscalls.rs +++ b/src/backend/linux_raw/rand/syscalls.rs @@ -11,5 +11,12 @@ use crate::rand::GetRandomFlags; #[inline] pub(crate) unsafe fn getrandom(buf: (*mut u8, usize), flags: GetRandomFlags) -> io::Result { - ret_usize(syscall!(__NR_getrandom, buf.0, pass_usize(buf.1), flags)) + let r = ret_usize(syscall!(__NR_getrandom, buf.0, pass_usize(buf.1), flags)); + + #[cfg(sanitize_memory)] + if let Ok(len) = r { + crate::msan::unpoison(buf.0, len); + } + + r } diff --git a/src/backend/linux_raw/thread/syscalls.rs b/src/backend/linux_raw/thread/syscalls.rs index 352e06150..c22e74a9f 100644 --- a/src/backend/linux_raw/thread/syscalls.rs +++ b/src/backend/linux_raw/thread/syscalls.rs @@ -372,8 +372,8 @@ pub(crate) fn setns(fd: BorrowedFd<'_>, nstype: c::c_int) -> io::Result io::Result<()> { - unsafe { ret(syscall_readonly!(__NR_unshare, flags)) } +pub(crate) unsafe fn unshare(flags: crate::thread::UnshareFlags) -> io::Result<()> { + ret(syscall_readonly!(__NR_unshare, flags)) } #[inline] @@ -405,9 +405,9 @@ pub(crate) fn setuid_thread(uid: crate::ugid::Uid) -> io::Result<()> { #[inline] pub(crate) fn setresuid_thread( - ruid: crate::ugid::Uid, - euid: crate::ugid::Uid, - suid: crate::ugid::Uid, + ruid: Option, + euid: Option, + suid: Option, ) -> io::Result<()> { #[cfg(any(target_arch = "x86", target_arch = "arm", target_arch = "sparc"))] unsafe { @@ -426,9 +426,9 @@ pub(crate) fn setgid_thread(gid: crate::ugid::Gid) -> io::Result<()> { #[inline] pub(crate) fn setresgid_thread( - rgid: crate::ugid::Gid, - egid: crate::ugid::Gid, - sgid: crate::ugid::Gid, + rgid: Option, + egid: Option, + sgid: Option, ) -> io::Result<()> { #[cfg(any(target_arch = "x86", target_arch = "arm", target_arch = "sparc"))] unsafe { diff --git a/src/buffer.rs b/src/buffer.rs index 6c86826f0..3584c5b39 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -94,6 +94,15 @@ use core::slice; /// "captured variable cannot escape `FnMut` closure body", /// use an explicit loop instead of `retry_on_intr`, assuming you're using /// that. See `error_retry_closure_uninit` in examples/buffer_errors.rs. +#[cfg_attr( + rustc_diagnostics, + diagnostic::on_unimplemented( + message = "rustix does not accept `{Self}` buffers", + label = "Unsupported buffer type", + note = "only (potentially uninitialized) byte arrays, slices, and Vecs are supported", + note = "please read the docs: https://docs.rs/rustix/latest/rustix/buffer/trait.Buffer.html" + ) +)] pub trait Buffer: private::Sealed {} // Implement `Buffer` for all the types that implement `Sealed`. diff --git a/src/clockid.rs b/src/clockid.rs index ae300f2bc..d274380f2 100644 --- a/src/clockid.rs +++ b/src/clockid.rs @@ -1,5 +1,6 @@ use crate::backend::c; use crate::fd::BorrowedFd; +use crate::io; /// `CLOCK_*` constants for use with [`clock_gettime`]. /// @@ -97,6 +98,51 @@ pub enum ClockId { BoottimeAlarm = bitcast!(c::CLOCK_BOOTTIME_ALARM), } +#[cfg(not(any(apple, target_os = "wasi")))] +impl TryFrom for ClockId { + type Error = io::Errno; + + fn try_from(value: c::clockid_t) -> Result { + match value { + c::CLOCK_REALTIME => Ok(ClockId::Realtime), + c::CLOCK_MONOTONIC => Ok(ClockId::Monotonic), + #[cfg(any(freebsdlike, target_os = "openbsd"))] + c::CLOCK_UPTIME => Ok(ClockId::Uptime), + #[cfg(not(any( + solarish, + target_os = "horizon", + target_os = "netbsd", + target_os = "redox", + target_os = "vita" + )))] + c::CLOCK_PROCESS_CPUTIME_ID => Ok(ClockId::ProcessCPUTime), + #[cfg(not(any( + solarish, + target_os = "horizon", + target_os = "netbsd", + target_os = "redox", + target_os = "vita" + )))] + c::CLOCK_THREAD_CPUTIME_ID => Ok(ClockId::ThreadCPUTime), + #[cfg(any(linux_kernel, target_os = "freebsd"))] + c::CLOCK_REALTIME_COARSE => Ok(ClockId::RealtimeCoarse), + #[cfg(any(linux_kernel, target_os = "freebsd"))] + c::CLOCK_MONOTONIC_COARSE => Ok(ClockId::MonotonicCoarse), + #[cfg(linux_kernel)] + c::CLOCK_MONOTONIC_RAW => Ok(ClockId::MonotonicRaw), + #[cfg(linux_kernel)] + c::CLOCK_REALTIME_ALARM => Ok(ClockId::RealtimeAlarm), + #[cfg(all(linux_kernel, feature = "linux_4_11"))] + c::CLOCK_TAI => Ok(ClockId::Tai), + #[cfg(any(linux_kernel, target_os = "fuchsia", target_os = "openbsd"))] + c::CLOCK_BOOTTIME => Ok(ClockId::Boottime), + #[cfg(any(linux_kernel, target_os = "fuchsia"))] + c::CLOCK_BOOTTIME_ALARM => Ok(ClockId::BoottimeAlarm), + _ => Err(io::Errno::RANGE), + } + } +} + /// `CLOCK_*` constants for use with [`clock_gettime`]. /// /// These constants are always supported at runtime, so `clock_gettime` never @@ -127,6 +173,21 @@ pub enum ClockId { ThreadCPUTime = c::CLOCK_THREAD_CPUTIME_ID, } +#[cfg(apple)] +impl TryFrom for ClockId { + type Error = io::Errno; + + fn try_from(value: c::clockid_t) -> Result { + match value { + c::CLOCK_REALTIME => Ok(ClockId::Realtime), + c::CLOCK_MONOTONIC => Ok(ClockId::Monotonic), + c::CLOCK_PROCESS_CPUTIME_ID => Ok(ClockId::ProcessCPUTime), + c::CLOCK_THREAD_CPUTIME_ID => Ok(ClockId::ThreadCPUTime), + _ => Err(io::Errno::RANGE), + } + } +} + /// `CLOCK_*` constants for use with [`clock_gettime_dynamic`]. /// /// These constants may be unsupported at runtime, depending on the OS version, @@ -145,7 +206,7 @@ pub enum DynamicClockId<'a> { Dynamic(BorrowedFd<'a>), /// `CLOCK_REALTIME_ALARM` - #[cfg(linux_kernel)] + #[cfg(any(linux_kernel, target_os = "fuchsia"))] #[doc(alias = "CLOCK_REALTIME_ALARM")] RealtimeAlarm, diff --git a/src/cstr.rs b/src/cstr.rs index 75eb61b90..46cde2f87 100644 --- a/src/cstr.rs +++ b/src/cstr.rs @@ -36,8 +36,8 @@ macro_rules! cstr { // We don't use std's `CStr::from_bytes_with_nul`; as of this writing, // that function isn't defined as `#[inline]` in std and doesn't // constant-fold away. - assert!( - !$str.bytes().any(|b| b == b'\0'), + ::core::assert!( + !::core::iter::Iterator::any(&mut ::core::primitive::str::bytes($str), |b| b == b'\0'), "cstr argument contains embedded NUL bytes", ); @@ -51,7 +51,9 @@ macro_rules! cstr { // contain embedded NULs above, and we append or own NUL terminator // here. unsafe { - $crate::ffi::CStr::from_bytes_with_nul_unchecked(concat!($str, "\0").as_bytes()) + $crate::ffi::CStr::from_bytes_with_nul_unchecked( + ::core::concat!($str, "\0").as_bytes(), + ) } } }}; @@ -83,4 +85,25 @@ mod tests { fn test_invalid_empty_cstr() { let _ = cstr!("\0"); } + + #[no_implicit_prelude] + mod hygiene { + #[allow(unused_macros)] + #[test] + fn macro_hygiene() { + macro_rules! assert { + ($($tt:tt)*) => { + ::core::panic!("cstr! called the wrong assert! macro"); + }; + } + macro_rules! concat { + ($($tt:tt)*) => {{ + let v: &str = ::core::panic!("cstr! called the wrong concat! macro"); + v + }}; + } + + let _ = cstr!("foo"); + } + } } diff --git a/src/fs/abs.rs b/src/fs/abs.rs index 236a833ad..6642fee65 100644 --- a/src/fs/abs.rs +++ b/src/fs/abs.rs @@ -15,7 +15,7 @@ use crate::fs::Access; target_os = "wasi", )))] use crate::fs::StatFs; -#[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))] +#[cfg(not(any(target_os = "wasi")))] use crate::fs::StatVfs; use crate::fs::{Mode, OFlags, Stat}; #[cfg(not(target_os = "wasi"))] @@ -283,7 +283,7 @@ pub fn statfs(path: P) -> io::Result { /// /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/statvfs.html /// [Linux]: https://man7.org/linux/man-pages/man2/statvfs.2.html -#[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))] +#[cfg(not(any(target_os = "wasi")))] #[inline] pub fn statvfs(path: P) -> io::Result { path.into_with_c_str(backend::fs::syscalls::statvfs) diff --git a/src/fs/at.rs b/src/fs/at.rs index 4ad2df895..bddb53473 100644 --- a/src/fs/at.rs +++ b/src/fs/at.rs @@ -469,6 +469,24 @@ pub fn mknodat( }) } +/// `mkfifoat(dirfd, path, mode)`—Make a FIFO special file. +/// +/// # References +/// - [POSIX] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/mkfifoat.html +#[cfg(not(any( + apple, + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "wasi" +)))] +#[inline] +pub fn mkfifoat(dirfd: Fd, path: P, mode: Mode) -> io::Result<()> { + mknodat(dirfd, path, FileType::Fifo, mode, 0) +} + /// `fchownat(dirfd, path, owner, group, flags)`—Sets file or directory /// ownership. /// diff --git a/src/fs/constants.rs b/src/fs/constants.rs index 5f1063428..a4f8e6e07 100644 --- a/src/fs/constants.rs +++ b/src/fs/constants.rs @@ -56,12 +56,12 @@ mod tests { #[test] fn test_layouts() { - #[cfg(linux_kernel)] + #[cfg(linux_raw_dep)] assert_eq_size!(FsWord, linux_raw_sys::general::__fsword_t); // Don't test against `__kernel_mode_t` on platforms where it's a // `u16`. - #[cfg(linux_kernel)] + #[cfg(linux_raw_dep)] #[cfg(not(any( target_arch = "x86", target_arch = "sparc", @@ -70,7 +70,7 @@ mod tests { )))] assert_eq_size!(RawMode, linux_raw_sys::general::__kernel_mode_t); - #[cfg(linux_kernel)] + #[cfg(linux_raw_dep)] #[cfg(any( target_arch = "x86", target_arch = "sparc", @@ -347,7 +347,11 @@ mod tests { #[cfg(not(libc))] // not in libc yet check_renamed_struct_field!(Statx, statx, stx_atomic_write_segments_max); #[cfg(linux_raw)] - check_renamed_struct_field!(Statx, statx, __spare1); + check_renamed_struct_field!(Statx, statx, stx_dio_read_offset_align); + #[cfg(linux_raw)] + check_renamed_struct_field!(Statx, statx, stx_atomic_write_unit_max_opt); + #[cfg(linux_raw)] + check_renamed_struct_field!(Statx, statx, __spare2); #[cfg(linux_raw)] check_renamed_struct_field!(Statx, statx, __spare3); } diff --git a/src/fs/fd.rs b/src/fs/fd.rs index 7ce1b3a46..61cd3f47d 100644 --- a/src/fs/fd.rs +++ b/src/fs/fd.rs @@ -40,7 +40,7 @@ use backend::fs::types::Stat; target_os = "wasi", )))] use backend::fs::types::StatFs; -#[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))] +#[cfg(not(target_os = "wasi"))] use backend::fs::types::StatVfs; /// Timestamps used by [`utimensat`] and [`futimens`]. @@ -196,7 +196,7 @@ pub fn fstatfs(fd: Fd) -> io::Result { /// /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/fstatvfs.html /// [Linux]: https://man7.org/linux/man-pages/man2/fstatvfs.2.html -#[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))] +#[cfg(not(target_os = "wasi"))] #[inline] pub fn fstatvfs(fd: Fd) -> io::Result { backend::fs::syscalls::fstatvfs(fd.as_fd()) diff --git a/src/fs/ioctl.rs b/src/fs/ioctl.rs index c126fdd19..16e0dda12 100644 --- a/src/fs/ioctl.rs +++ b/src/fs/ioctl.rs @@ -58,7 +58,7 @@ pub fn ioctl_ficlone(fd: Fd, src_fd: SrcFd) -> io::Result } /// `ioctl(fd, EXT4_IOC_RESIZE_FS, blocks)`—Resize ext4 filesystem on fd. -#[cfg(linux_kernel)] +#[cfg(linux_raw_dep)] #[inline] #[doc(alias = "EXT4_IOC_RESIZE_FS")] pub fn ext4_ioc_resize_fs(fd: Fd, blocks: u64) -> io::Result<()> { @@ -94,7 +94,7 @@ unsafe impl ioctl::Ioctl for Ficlone<'_> { } } -#[cfg(linux_kernel)] +#[cfg(linux_raw_dep)] bitflags! { /// `FS_*` constants for use with [`ioctl_getflags`]. /// @@ -136,7 +136,7 @@ bitflags! { /// `ioctl(fd, FS_IOC_GETFLAGS)`—Returns the [inode flags] attributes /// /// [inode flags]: https://man7.org/linux/man-pages/man2/ioctl_iflags.2.html -#[cfg(linux_kernel)] +#[cfg(linux_raw_dep)] #[inline] #[doc(alias = "FS_IOC_GETFLAGS")] pub fn ioctl_getflags(fd: Fd) -> io::Result { @@ -153,7 +153,7 @@ pub fn ioctl_getflags(fd: Fd) -> io::Result { /// `ioctl(fd, FS_IOC_SETFLAGS)`—Modify the [inode flags] attributes /// /// [inode flags]: https://man7.org/linux/man-pages/man2/ioctl_iflags.2.html -#[cfg(linux_kernel)] +#[cfg(linux_raw_dep)] #[inline] #[doc(alias = "FS_IOC_SETFLAGS")] pub fn ioctl_setflags(fd: Fd, flags: IFlags) -> io::Result<()> { diff --git a/src/fs/mod.rs b/src/fs/mod.rs index 505925f72..35ca17af0 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -30,7 +30,7 @@ pub(crate) mod fd; mod getpath; #[cfg(not(target_os = "wasi"))] // WASI doesn't have get[gpu]id. mod id; -#[cfg(linux_kernel)] +#[cfg(linux_raw_dep)] pub mod inotify; #[cfg(linux_kernel)] mod ioctl; @@ -38,14 +38,13 @@ mod ioctl; target_os = "espidf", target_os = "haiku", target_os = "horizon", - target_os = "redox", target_os = "vita", target_os = "wasi" )))] mod makedev; #[cfg(any(linux_kernel, target_os = "freebsd"))] mod memfd_create; -#[cfg(linux_kernel)] +#[cfg(linux_raw_dep)] mod openat2; #[cfg(linux_kernel)] mod raw_dir; @@ -103,14 +102,13 @@ pub use ioctl::*; target_os = "espidf", target_os = "haiku", target_os = "horizon", - target_os = "redox", target_os = "vita", target_os = "wasi" )))] pub use makedev::*; #[cfg(any(linux_kernel, target_os = "freebsd"))] pub use memfd_create::memfd_create; -#[cfg(linux_kernel)] +#[cfg(linux_raw_dep)] pub use openat2::openat2; #[cfg(linux_kernel)] pub use raw_dir::{RawDir, RawDirEntry}; diff --git a/src/fs/raw_dir.rs b/src/fs/raw_dir.rs index 7fec6fd71..4b14be38a 100644 --- a/src/fs/raw_dir.rs +++ b/src/fs/raw_dir.rs @@ -7,6 +7,10 @@ use crate::fs::FileType; use crate::io; use core::fmt; use core::mem::{align_of, MaybeUninit}; + +#[cfg(not(linux_raw_dep))] +use libc::dirent64 as linux_dirent64; +#[cfg(linux_raw_dep)] use linux_raw_sys::general::linux_dirent64; /// A directory iterator implemented with getdents. @@ -234,3 +238,17 @@ impl<'buf, Fd: AsFd> RawDir<'buf, Fd> { self.offset >= self.initialized } } + +/// ```compile_fail +/// use rustix::fs::{CWD, Mode, OFlags, RawDir, openat}; +/// use std::mem::MaybeUninit; +/// +/// let mut buf = [MaybeUninit::uninit(); 47]; +/// let fd = openat(CWD, c".", OFlags::DIRECTORY, Mode::empty()).unwrap(); +/// let mut iter = RawDir::new(fd, &mut buf); +/// let item1 = iter.next().unwrap(); +/// let item2 = iter.next().unwrap(); +/// println!("{item2:?}"); +/// println!("{item1:?}"); +/// ``` +fn _doctest() {} diff --git a/src/fs/statx.rs b/src/fs/statx.rs index f1d995ec3..f8be29f7d 100644 --- a/src/fs/statx.rs +++ b/src/fs/statx.rs @@ -44,8 +44,10 @@ pub struct Statx { pub stx_atomic_write_unit_min: u32, pub stx_atomic_write_unit_max: u32, pub stx_atomic_write_segments_max: u32, - pub(crate) __spare1: [u32; 1], - pub(crate) __spare3: [u64; 9], + pub stx_dio_read_offset_align: u32, + pub stx_atomic_write_unit_max_opt: u32, + pub __spare2: [u32; 1usize], + pub __spare3: [u64; 8usize], } /// `struct statx_timestamp` for use with [`Statx`]. diff --git a/src/io/ioctl.rs b/src/io/ioctl.rs index b47269f12..33bf07cf5 100644 --- a/src/io/ioctl.rs +++ b/src/io/ioctl.rs @@ -16,7 +16,10 @@ use backend::fd::AsFd; /// /// This is similar to `fcntl(fd, F_SETFD, FD_CLOEXEC)`, except that it avoids /// clearing any other flags that might be set. -#[cfg(apple)] +/// +/// Linux: Note that `ioctl` can not be used on `OFlags::PATH` file +/// descriptors. +#[cfg(any(apple, linux_kernel))] #[inline] #[doc(alias = "FIOCLEX")] #[doc(alias = "FD_CLOEXEC")] @@ -28,6 +31,24 @@ pub fn ioctl_fioclex(fd: Fd) -> io::Result<()> { } } +/// `ioctl(fd, FIONCLEX, NULL)`—Remove the close-on-exec flag. +/// +/// This is similar to `fcntl_setfd(fd, FdFlags::empty())`, except that it avoids +/// clearing any other flags that might be set. +/// +/// Linux: Note that `ioctl` can not be used on `OFlags::PATH` file +/// descriptors. +#[cfg(any(apple, linux_kernel))] +#[inline] +#[doc(alias = "FIONCLEX")] +pub fn ioctl_fionclex(fd: Fd) -> io::Result<()> { + // SAFETY: `FIONCLEX` is a no-argument setter opcode. + unsafe { + let ctl = ioctl::NoArg::<{ c::FIONCLEX }>::new(); + ioctl::ioctl(fd, ctl) + } +} + /// `ioctl(fd, FIONBIO, &value)`—Enables or disables non-blocking mode. /// /// # References diff --git a/src/io/read_write.rs b/src/io/read_write.rs index 572c7b651..0e0969103 100644 --- a/src/io/read_write.rs +++ b/src/io/read_write.rs @@ -10,7 +10,7 @@ use backend::fd::AsFd; #[cfg(not(windows))] pub use crate::maybe_polyfill::io::{IoSlice, IoSliceMut}; -#[cfg(linux_kernel)] +#[cfg(all(linux_kernel, not(target_os = "android")))] pub use backend::io::types::ReadWriteFlags; /// `read(fd, buf)`—Reads from a stream. @@ -277,7 +277,7 @@ pub fn pwritev(fd: Fd, bufs: &[IoSlice<'_>], offset: u64) -> io::Resul /// /// [Linux]: https://man7.org/linux/man-pages/man2/preadv2.2.html /// [glibc]: https://sourceware.org/glibc/manual/latest/html_node/Scatter_002dGather.html#index-preadv64v2 -#[cfg(linux_kernel)] +#[cfg(all(linux_kernel, not(target_os = "android")))] #[inline] pub fn preadv2( fd: Fd, @@ -298,7 +298,7 @@ pub fn preadv2( /// /// [Linux]: https://man7.org/linux/man-pages/man2/pwritev2.2.html /// [glibc]: https://sourceware.org/glibc/manual/latest/html_node/Scatter_002dGather.html#index-pwritev64v2 -#[cfg(linux_kernel)] +#[cfg(all(linux_kernel, not(target_os = "android")))] #[inline] pub fn pwritev2( fd: Fd, diff --git a/src/io_uring/mod.rs b/src/io_uring/mod.rs index 5e3756812..f4084ef17 100644 --- a/src/io_uring/mod.rs +++ b/src/io_uring/mod.rs @@ -106,6 +106,9 @@ pub unsafe fn io_uring_setup(entries: u32, params: &mut io_uring_params) -> io:: /// responsible for ensuring that memory and resources are only accessed in /// valid ways. /// +/// If `opcode` is `IoringRegisterOp::RegisterRingFds`, `arg` must point to +/// mutable memory, despite being `*const`. +/// /// # References /// - [Linux] /// @@ -129,6 +132,9 @@ pub unsafe fn io_uring_register( /// responsible for ensuring that memory and resources are only accessed in /// valid ways. /// +/// If `opcode` is `IoringRegisterOp::RegisterRingFds`, `arg` must point to +/// mutable memory, despite being `*const`. +/// /// # References /// - [Linux] /// diff --git a/src/ioctl/linux.rs b/src/ioctl/linux.rs index 7215228a7..8d96a1b28 100644 --- a/src/ioctl/linux.rs +++ b/src/ioctl/linux.rs @@ -88,12 +88,12 @@ mod tests { #[allow(unused_imports)] use super::*; - #[cfg(not(any( + #[cfg(all(linux_raw_dep, not(any( // These have no ioctl opcodes defined in linux_raw_sys so we can't use // that as a known-good value for this test. target_arch = "sparc", target_arch = "sparc64" -)))] +))))] #[test] fn check_known_opcodes() { use crate::backend::c::{c_long, c_uint}; diff --git a/src/ioctl/mod.rs b/src/ioctl/mod.rs index e3e8f8e1b..70c690530 100644 --- a/src/ioctl/mod.rs +++ b/src/ioctl/mod.rs @@ -18,20 +18,20 @@ use crate::fd::{AsFd, BorrowedFd}; use crate::ffi as c; use crate::io::Result; -#[cfg(any(linux_kernel, bsd))] +#[cfg(any(linux_kernel, bsd, target_os = "redox"))] use core::mem; pub use patterns::*; mod patterns; -#[cfg(linux_kernel)] +#[cfg(any(linux_kernel, target_os = "redox"))] mod linux; #[cfg(bsd)] mod bsd; -#[cfg(linux_kernel)] +#[cfg(any(linux_kernel, target_os = "redox"))] use linux as platform; #[cfg(bsd)] @@ -198,7 +198,7 @@ pub unsafe trait Ioctl { /// /// If you're writing a driver and defining your own ioctl numbers, it's /// recommended to use these functions to compute them. -#[cfg(any(linux_kernel, bsd))] +#[cfg(any(linux_kernel, bsd, target_os = "redox"))] pub mod opcode { use super::*; @@ -344,7 +344,7 @@ type _Opcode = c::c_uint; #[cfg(windows)] type _Opcode = i32; -#[cfg(linux_kernel)] +#[cfg(linux_raw_dep)] #[cfg(not(any(target_arch = "sparc", target_arch = "sparc64")))] #[cfg(test)] mod tests { diff --git a/src/lib.rs b/src/lib.rs index 1621262ed..8d4a5e521 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -122,6 +122,12 @@ #![allow(clippy::useless_conversion)] // This clippy lint gets too many false positives. #![allow(clippy::needless_lifetimes)] +// Until `unnecessary_transmutes` is recognized by our MSRV, don't warn about +// it being unrecognized. +#![allow(unknown_lints)] +// Until `cast_signed` and `cast_unsigned` are supported by our MSRV, don't +// warn about transmutes that could be changed to them. +#![allow(unnecessary_transmutes)] // Redox and WASI have enough differences that it isn't worth precisely // conditionalizing all the `use`s for them. Similar for if we don't have // "all-apis". @@ -172,6 +178,8 @@ pub(crate) mod maybe_polyfill; pub(crate) mod check_types; #[macro_use] pub(crate) mod bitcast; +#[cfg(sanitize_memory)] +pub(crate) mod msan; // linux_raw: Weak symbols are used by the use-libc-auxv feature for // glibc 2.15 support. @@ -188,7 +196,6 @@ mod weak; // Pick the backend implementation to use. #[cfg_attr(libc, path = "backend/libc/mod.rs")] #[cfg_attr(linux_raw, path = "backend/linux_raw/mod.rs")] -#[cfg_attr(wasi, path = "backend/wasi/mod.rs")] mod backend; /// Export the `*Fd` types and traits that are used in rustix's public API. @@ -218,7 +225,7 @@ pub mod ffi; #[cfg_attr(docsrs, doc(cfg(feature = "fs")))] pub mod fs; pub mod io; -#[cfg(linux_kernel)] +#[cfg(all(linux_kernel, not(target_os = "android")))] #[cfg(feature = "io_uring")] #[cfg_attr(docsrs, doc(cfg(feature = "io_uring")))] pub mod io_uring; diff --git a/src/mount/fsopen.rs b/src/mount/fsopen.rs index bd4b7be8d..638782f04 100644 --- a/src/mount/fsopen.rs +++ b/src/mount/fsopen.rs @@ -3,7 +3,7 @@ use crate::backend::mount::types::{ FsMountFlags, FsOpenFlags, FsPickFlags, MountAttrFlags, MoveMountFlags, OpenTreeFlags, }; -use crate::fd::{BorrowedFd, OwnedFd}; +use crate::fd::{AsFd, OwnedFd}; use crate::{backend, io, path}; /// `fsopen(fs_name, flags)` @@ -24,12 +24,12 @@ pub fn fsopen(fs_name: Fs, flags: FsOpenFlags) -> io::Result, +pub fn fsmount( + fs_fd: Fd, flags: FsMountFlags, attr_flags: MountAttrFlags, ) -> io::Result { - backend::mount::syscalls::fsmount(fs_fd, flags, attr_flags) + backend::mount::syscalls::fsmount(fs_fd.as_fd(), flags, attr_flags) } /// `move_mount(from_dfd, from_pathname, to_dfd, to_pathname, flags)` @@ -43,13 +43,15 @@ pub fn fsmount( /// [`mount_move`]: crate::mount::mount_move /// [Unfinished draft]: https://github.com/sunfishcode/linux-mount-api-documentation/blob/main/move_mount.md #[inline] -pub fn move_mount( - from_dfd: BorrowedFd<'_>, +pub fn move_mount( + from_dfd: FromFd, from_pathname: From, - to_dfd: BorrowedFd<'_>, + to_dfd: ToFd, to_pathname: To, flags: MoveMountFlags, ) -> io::Result<()> { + let from_dfd = from_dfd.as_fd(); + let to_dfd = to_dfd.as_fd(); from_pathname.into_with_c_str(|from_pathname| { to_pathname.into_with_c_str(|to_pathname| { backend::mount::syscalls::move_mount( @@ -70,11 +72,12 @@ pub fn move_mount( /// /// [Unfinished draft]: https://github.com/sunfishcode/linux-mount-api-documentation/blob/main/open_tree.md #[inline] -pub fn open_tree( - dfd: BorrowedFd<'_>, +pub fn open_tree( + dfd: Fd, filename: Path, flags: OpenTreeFlags, ) -> io::Result { + let dfd = dfd.as_fd(); filename.into_with_c_str(|filename| backend::mount::syscalls::open_tree(dfd, filename, flags)) } @@ -85,11 +88,12 @@ pub fn open_tree( /// /// [Unfinished draft]: https://github.com/sunfishcode/linux-mount-api-documentation/blob/main/fspick.md #[inline] -pub fn fspick( - dfd: BorrowedFd<'_>, +pub fn fspick( + dfd: Fd, path: Path, flags: FsPickFlags, ) -> io::Result { + let dfd = dfd.as_fd(); path.into_with_c_str(|path| backend::mount::syscalls::fspick(dfd, path, flags)) } @@ -101,7 +105,8 @@ pub fn fspick( /// [Unfinished draft]: https://github.com/sunfishcode/linux-mount-api-documentation/blob/main/fsconfig.md #[inline] #[doc(alias = "fsconfig")] -pub fn fsconfig_set_flag(fs_fd: BorrowedFd<'_>, key: Key) -> io::Result<()> { +pub fn fsconfig_set_flag(fs_fd: Fd, key: Key) -> io::Result<()> { + let fs_fd = fs_fd.as_fd(); key.into_with_c_str(|key| backend::mount::syscalls::fsconfig_set_flag(fs_fd, key)) } @@ -113,11 +118,12 @@ pub fn fsconfig_set_flag(fs_fd: BorrowedFd<'_>, key: Key) -> io: /// [Unfinished draft]: https://github.com/sunfishcode/linux-mount-api-documentation/blob/main/fsconfig.md #[inline] #[doc(alias = "fsconfig")] -pub fn fsconfig_set_string( - fs_fd: BorrowedFd<'_>, +pub fn fsconfig_set_string( + fs_fd: Fd, key: Key, value: Value, ) -> io::Result<()> { + let fs_fd = fs_fd.as_fd(); key.into_with_c_str(|key| { value.into_with_c_str(|value| { backend::mount::syscalls::fsconfig_set_string(fs_fd, key, value) @@ -133,11 +139,12 @@ pub fn fsconfig_set_string( /// [Unfinished draft]: https://github.com/sunfishcode/linux-mount-api-documentation/blob/main/fsconfig.md #[inline] #[doc(alias = "fsconfig")] -pub fn fsconfig_set_binary( - fs_fd: BorrowedFd<'_>, +pub fn fsconfig_set_binary( + fs_fd: Fd, key: Key, value: &[u8], ) -> io::Result<()> { + let fs_fd = fs_fd.as_fd(); key.into_with_c_str(|key| backend::mount::syscalls::fsconfig_set_binary(fs_fd, key, value)) } @@ -149,12 +156,14 @@ pub fn fsconfig_set_binary( /// [Unfinished draft]: https://github.com/sunfishcode/linux-mount-api-documentation/blob/main/fsconfig.md #[inline] #[doc(alias = "fsconfig")] -pub fn fsconfig_set_path( - fs_fd: BorrowedFd<'_>, +pub fn fsconfig_set_path( + fs_fd: Fd, key: Key, path: Path, - fd: BorrowedFd<'_>, + fd: AuxFd, ) -> io::Result<()> { + let fs_fd = fs_fd.as_fd(); + let fd = fd.as_fd(); key.into_with_c_str(|key| { path.into_with_c_str(|path| { backend::mount::syscalls::fsconfig_set_path(fs_fd, key, path, fd) @@ -170,11 +179,13 @@ pub fn fsconfig_set_path( /// [Unfinished draft]: https://github.com/sunfishcode/linux-mount-api-documentation/blob/main/fsconfig.md #[inline] #[doc(alias = "fsconfig")] -pub fn fsconfig_set_path_empty( - fs_fd: BorrowedFd<'_>, +pub fn fsconfig_set_path_empty( + fs_fd: Fd, key: Key, - fd: BorrowedFd<'_>, + fd: AuxFd, ) -> io::Result<()> { + let fs_fd = fs_fd.as_fd(); + let fd = fd.as_fd(); key.into_with_c_str(|key| backend::mount::syscalls::fsconfig_set_path_empty(fs_fd, key, fd)) } @@ -186,11 +197,13 @@ pub fn fsconfig_set_path_empty( /// [Unfinished draft]: https://github.com/sunfishcode/linux-mount-api-documentation/blob/main/fsconfig.md #[inline] #[doc(alias = "fsconfig")] -pub fn fsconfig_set_fd( - fs_fd: BorrowedFd<'_>, +pub fn fsconfig_set_fd( + fs_fd: Fd, key: Key, - fd: BorrowedFd<'_>, + fd: AuxFd, ) -> io::Result<()> { + let fs_fd = fs_fd.as_fd(); + let fd = fd.as_fd(); key.into_with_c_str(|key| backend::mount::syscalls::fsconfig_set_fd(fs_fd, key, fd)) } @@ -202,8 +215,8 @@ pub fn fsconfig_set_fd( /// [Unfinished draft]: https://github.com/sunfishcode/linux-mount-api-documentation/blob/main/fsconfig.md #[inline] #[doc(alias = "fsconfig")] -pub fn fsconfig_create(fs_fd: BorrowedFd<'_>) -> io::Result<()> { - backend::mount::syscalls::fsconfig_create(fs_fd) +pub fn fsconfig_create(fs_fd: Fd) -> io::Result<()> { + backend::mount::syscalls::fsconfig_create(fs_fd.as_fd()) } /// `fsconfig(fs_fd, FSCONFIG_CMD_RECONFIGURE, key, NULL, 0)` @@ -214,8 +227,8 @@ pub fn fsconfig_create(fs_fd: BorrowedFd<'_>) -> io::Result<()> { /// [Unfinished draft]: https://github.com/sunfishcode/linux-mount-api-documentation/blob/main/fsconfig.md #[inline] #[doc(alias = "fsconfig")] -pub fn fsconfig_reconfigure(fs_fd: BorrowedFd<'_>) -> io::Result<()> { - backend::mount::syscalls::fsconfig_reconfigure(fs_fd) +pub fn fsconfig_reconfigure(fs_fd: Fd) -> io::Result<()> { + backend::mount::syscalls::fsconfig_reconfigure(fs_fd.as_fd()) } /// `fsconfig(fs_fd, FSCONFIG_CMD_CREATE_EXCL, key, NULL, 0)` @@ -228,6 +241,6 @@ pub fn fsconfig_reconfigure(fs_fd: BorrowedFd<'_>) -> io::Result<()> { /// [Unfinished draft]: https://github.com/sunfishcode/linux-mount-api-documentation/blob/main/fsconfig.md #[inline] #[doc(alias = "fsconfig")] -pub fn fsconfig_create_exclusive(fs_fd: BorrowedFd<'_>) -> io::Result<()> { - backend::mount::syscalls::fsconfig_create_excl(fs_fd) +pub fn fsconfig_create_exclusive(fs_fd: Fd) -> io::Result<()> { + backend::mount::syscalls::fsconfig_create_excl(fs_fd.as_fd()) } diff --git a/src/msan.rs b/src/msan.rs new file mode 100644 index 000000000..027ad09e3 --- /dev/null +++ b/src/msan.rs @@ -0,0 +1,12 @@ +use core::ffi::c_void; +use core::mem::size_of; + +extern "C" { + /// + #[link_name = "__msan_unpoison"] + pub(crate) fn unpoison(a: *const c_void, size: usize); +} + +pub(crate) fn unpoison_maybe_uninit(t: &MaybeUninit) { + unpoison(t.as_ptr(), size_of::()) +} diff --git a/src/net/netdevice.rs b/src/net/netdevice.rs index 1ddd918ef..54872f52a 100644 --- a/src/net/netdevice.rs +++ b/src/net/netdevice.rs @@ -19,7 +19,7 @@ use crate::fd::AsFd; use crate::io; #[cfg(feature = "alloc")] -use alloc::string::String; +use alloc::{borrow::ToOwned, string::String}; /// `ioctl(fd, SIOCGIFINDEX, ifreq)`—Returns the interface index for a given /// name. @@ -42,6 +42,8 @@ pub fn name_to_index(fd: Fd, if_name: &str) -> io::Result { /// /// See the [module-level documentation] for information about `fd` usage. /// +/// See also [`index_to_name_inlined`] which does not require `alloc` feature. +/// /// # References /// - [Linux] /// @@ -52,12 +54,84 @@ pub fn name_to_index(fd: Fd, if_name: &str) -> io::Result { #[cfg(feature = "alloc")] #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] pub fn index_to_name(fd: Fd, index: u32) -> io::Result { - crate::backend::net::netdevice::index_to_name(fd.as_fd(), index) + let (len, ifrn_name) = crate::backend::net::netdevice::index_to_name(fd.as_fd(), index)?; + + core::str::from_utf8(&ifrn_name[..len]) + .map_err(|_| io::Errno::ILSEQ) + .map(ToOwned::to_owned) +} + +/// `ioctl(fd, SIOCGIFNAME, ifreq)`—Returns the interface name for a given +/// index. +/// +/// See the [module-level documentation] for information about `fd` usage. +/// +/// # References +/// - [Linux] +/// +/// [module-level documentation]: self +/// [Linux]: https://man7.org/linux/man-pages/man7/netdevice.7.html +#[inline] +#[doc(alias = "SIOCGIFNAME")] +pub fn index_to_name_inlined(fd: Fd, index: u32) -> io::Result { + let (len, ifrn_name) = crate::backend::net::netdevice::index_to_name(fd.as_fd(), index)?; + + // Check if the name is valid UTF-8. + core::str::from_utf8(&ifrn_name[..len]) + .map_err(|_| io::Errno::ILSEQ) + .map(|_| InlinedName { + len, + name: ifrn_name, + }) +} + +/// The inlined interface name. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub struct InlinedName { + len: usize, + name: [u8; 16], +} + +impl InlinedName { + /// Returns the str representation of the inlined name. + pub fn as_str(&self) -> &str { + self.as_ref() + } + + /// Returns the bytes representation of the inlined name. + pub fn as_bytes(&self) -> &[u8] { + self.as_ref() + } +} + +impl AsRef<[u8]> for InlinedName { + fn as_ref(&self) -> &[u8] { + &self.name[..self.len] + } +} + +impl AsRef for InlinedName { + fn as_ref(&self) -> &str { + // SAFETY: `InlinedName` is constructed with valid UTF-8. + core::str::from_utf8(&self.name[..self.len]).unwrap() + } +} + +impl core::borrow::Borrow for InlinedName { + fn borrow(&self) -> &str { + self.as_ref() + } +} + +impl core::fmt::Display for InlinedName { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + self.as_str().fmt(f) + } } #[cfg(test)] mod tests { - use crate::backend::net::netdevice::{index_to_name, name_to_index}; + use super::{index_to_name, index_to_name_inlined, name_to_index}; use crate::fd::AsFd; use crate::net::{AddressFamily, SocketFlags, SocketType}; @@ -81,6 +155,31 @@ mod tests { assert_eq!(Ok(loopback_index), name_to_index(fd.as_fd(), "lo")); } + #[test] + fn test_index_to_name_inlined() { + let fd = crate::net::socket_with( + AddressFamily::INET, + SocketType::DGRAM, + SocketFlags::CLOEXEC, + None, + ) + .unwrap(); + + let loopback_index = std::fs::read_to_string("/sys/class/net/lo/ifindex") + .unwrap() + .as_str() + .split_at(1) + .0 + .parse::() + .unwrap(); + assert_eq!( + "lo", + index_to_name_inlined(fd.as_fd(), loopback_index) + .unwrap() + .as_str(), + ); + } + #[test] #[cfg(feature = "alloc")] fn test_index_to_name() { diff --git a/src/net/send_recv/mod.rs b/src/net/send_recv/mod.rs index 0aac6d2eb..a7056cb3e 100644 --- a/src/net/send_recv/mod.rs +++ b/src/net/send_recv/mod.rs @@ -15,7 +15,6 @@ pub use backend::net::send_recv::{RecvFlags, ReturnFlags, SendFlags}; windows, target_os = "espidf", target_os = "horizon", - target_os = "redox", target_os = "vita" )))] mod msg; @@ -24,7 +23,6 @@ mod msg; windows, target_os = "espidf", target_os = "horizon", - target_os = "redox", target_os = "vita" )))] pub use msg::*; diff --git a/src/net/send_recv/msg.rs b/src/net/send_recv/msg.rs index 7df60a5e7..234e9933a 100644 --- a/src/net/send_recv/msg.rs +++ b/src/net/send_recv/msg.rs @@ -66,6 +66,11 @@ macro_rules! cmsg_space { $len * ::core::mem::size_of::<$crate::net::UCred>(), ) }; + (TxTime($len:expr)) => { + $crate::net::__cmsg_space( + $len * ::core::mem::size_of::<::core::primitive::u64>(), + ) + }; // Combo Rules ($firstid:ident($firstex:expr), $($restid:ident($restex:expr)),*) => {{ @@ -94,12 +99,17 @@ macro_rules! cmsg_aligned_space { $len * ::core::mem::size_of::<$crate::net::UCred>(), ) }; + (TxTime($len:expr)) => { + $crate::net::__cmsg_aligned_space( + $len * ::core::mem::size_of::<::core::primitive::u64>(), + ) + }; // Combo Rules ($firstid:ident($firstex:expr), $($restid:ident($restex:expr)),*) => {{ - let sum = cmsg_aligned_space!($firstid($firstex)); + let sum = $crate::cmsg_aligned_space!($firstid($firstex)); $( - let sum = sum + cmsg_aligned_space!($restid($restex)); + let sum = sum + $crate::cmsg_aligned_space!($restid($restex)); )* sum }}; @@ -138,6 +148,13 @@ pub enum SendAncillaryMessage<'slice, 'fd> { #[cfg(linux_kernel)] #[doc(alias = "SCM_CREDENTIAL")] ScmCredentials(UCred), + /// Transmission time, in nanoseconds. The value will be interpreted by + /// whichever clock was configured on the socket with [`set_txtime`]. + /// + /// [`set_txtime`]: crate::net::sockopt::set_txtime + #[cfg(target_os = "linux")] + #[doc(alias = "SCM_TXTIME")] + TxTime(u64), } impl SendAncillaryMessage<'_, '_> { @@ -150,6 +167,8 @@ impl SendAncillaryMessage<'_, '_> { Self::ScmRights(slice) => cmsg_space!(ScmRights(slice.len())), #[cfg(linux_kernel)] Self::ScmCredentials(_) => cmsg_space!(ScmCredentials(1)), + #[cfg(target_os = "linux")] + Self::TxTime(_) => cmsg_space!(TxTime(1)), } } } @@ -291,6 +310,13 @@ impl<'buf, 'slice, 'fd> SendAncillaryBuffer<'buf, 'slice, 'fd> { }; self.push_ancillary(ucred_bytes, c::SOL_SOCKET as _, c::SCM_CREDENTIALS as _) } + #[cfg(target_os = "linux")] + SendAncillaryMessage::TxTime(tx_time) => { + let tx_time_bytes = unsafe { + slice::from_raw_parts(addr_of!(tx_time).cast::(), size_of_val(&tx_time)) + }; + self.push_ancillary(tx_time_bytes, c::SOL_SOCKET as _, c::SO_TXTIME as _) + } } } @@ -959,3 +985,42 @@ mod messages { impl FusedIterator for Messages<'_> {} } + +#[cfg(test)] +mod tests { + #[no_implicit_prelude] + mod hygiene { + #[allow(unused_macros)] + #[test] + fn macro_hygiene() { + // This `u64` is `!Sized`, so `cmsg_space!` will fail if it tries to get its size with + // `size_of()`. + #[allow(dead_code, non_camel_case_types)] + struct u64([u8]); + + // Ensure that when `cmsg*_space!` calls itself recursively, it really calls itself and + // not these macros. + macro_rules! cmsg_space { + ($($tt:tt)*) => {{ + let v: usize = ::core::panic!("Wrong cmsg_space! macro called"); + v + }}; + } + macro_rules! cmsg_aligned_space { + ($($tt:tt)*) => {{ + let v: usize = ::core::panic!("Wrong cmsg_aligned_space! macro called"); + v + }}; + } + + crate::cmsg_space!(ScmRights(1)); + crate::cmsg_space!(TxTime(1)); + #[cfg(linux_kernel)] + { + crate::cmsg_space!(ScmCredentials(1)); + crate::cmsg_space!(ScmRights(1), ScmCredentials(1), TxTime(1)); + crate::cmsg_aligned_space!(ScmRights(1), ScmCredentials(1), TxTime(1)); + } + } + } +} diff --git a/src/net/sockopt.rs b/src/net/sockopt.rs index ef8176aea..796a114be 100644 --- a/src/net/sockopt.rs +++ b/src/net/sockopt.rs @@ -143,6 +143,8 @@ #![doc(alias = "getsockopt")] #![doc(alias = "setsockopt")] +#[cfg(all(target_os = "linux", feature = "time"))] +use crate::clockid::ClockId; #[cfg(target_os = "linux")] use crate::net::xdp::{XdpMmapOffsets, XdpOptionsFlags, XdpStatistics, XdpUmemReg}; #[cfg(not(any( @@ -172,6 +174,8 @@ use crate::net::Protocol; use crate::net::SocketAddrV4; #[cfg(linux_kernel)] use crate::net::SocketAddrV6; +#[cfg(all(target_os = "linux", feature = "time"))] +use crate::net::TxTimeFlags; use crate::net::{Ipv4Addr, Ipv6Addr, SocketType}; use crate::{backend, io}; #[cfg(feature = "alloc")] @@ -198,6 +202,110 @@ pub enum Timeout { Send = c::SO_SNDTIMEO as _, } +/// A type for holding raw integer IPv4 Path MTU Discovery options. +#[cfg(linux_kernel)] +pub type RawIpv4PathMtuDiscovery = i32; + +/// IPv4 Path MTU Discovery option values (`IP_PMTUDISC_*`) for use with +/// [`set_ip_mtu_discover`] and [`ip_mtu_discover`]. +/// +/// # References +/// - [Linux] +/// - [Linux INET header] +/// +/// [Linux]: https://man7.org/linux/man-pages/man7/ip.7.html +/// [Linux INET header]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/in.h?h=v6.14#n135 +#[cfg(linux_kernel)] +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] +#[repr(transparent)] +pub struct Ipv4PathMtuDiscovery(RawIpv4PathMtuDiscovery); + +#[cfg(linux_kernel)] +impl Ipv4PathMtuDiscovery { + /// `IP_PMTUDISC_DONT` + #[doc(alias = "IP_PMTUDISC_DONT")] + pub const DONT: Self = Self(c::IP_PMTUDISC_DONT as _); + /// `IP_PMTUDISC_WANT` + #[doc(alias = "IP_PMTUDISC_WANT")] + pub const WANT: Self = Self(c::IP_PMTUDISC_WANT as _); + /// `IP_PMTUDISC_DO` + #[doc(alias = "IP_PMTUDISC_DO")] + pub const DO: Self = Self(c::IP_PMTUDISC_DO as _); + /// `IP_PMTUDISC_PROBE` + #[doc(alias = "IP_PMTUDISC_PROBE")] + pub const PROBE: Self = Self(c::IP_PMTUDISC_PROBE as _); + /// `IP_PMTUDISC_INTERFACE` + #[doc(alias = "IP_PMTUDISC_INTERFACE")] + pub const INTERFACE: Self = Self(c::IP_PMTUDISC_INTERFACE as _); + /// `IP_PMTUDISC_OMIT` + #[doc(alias = "IP_PMTUDISC_OMIT")] + pub const OMIT: Self = Self(c::IP_PMTUDISC_OMIT as _); + + /// Constructs an option from a raw integer. + #[inline] + pub const fn from_raw(raw: RawIpv4PathMtuDiscovery) -> Self { + Self(raw) + } + + /// Returns the raw integer for this option. + #[inline] + pub const fn as_raw(self) -> RawIpv4PathMtuDiscovery { + self.0 + } +} + +/// A type for holding raw integer IPv6 Path MTU Discovery options. +#[cfg(linux_kernel)] +pub type RawIpv6PathMtuDiscovery = i32; + +/// IPv6 Path MTU Discovery option values (`IPV6_PMTUDISC_*`) for use with +/// [`set_ipv6_mtu_discover`] and [`ipv6_mtu_discover`]. +/// +/// # References +/// - [Linux] +/// - [Linux INET6 header] +/// +/// [Linux]: https://man7.org/linux/man-pages/man7/ipv6.7.html +/// [Linux INET6 header]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/in6.h?h=v6.14#n185 +#[cfg(linux_kernel)] +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] +#[repr(transparent)] +pub struct Ipv6PathMtuDiscovery(RawIpv6PathMtuDiscovery); + +#[cfg(linux_kernel)] +impl Ipv6PathMtuDiscovery { + /// `IPV6_PMTUDISC_DONT` + #[doc(alias = "IPV6_PMTUDISC_DONT")] + pub const DONT: Self = Self(c::IPV6_PMTUDISC_DONT as _); + /// `IPV6_PMTUDISC_WANT` + #[doc(alias = "IPV6_PMTUDISC_WANT")] + pub const WANT: Self = Self(c::IPV6_PMTUDISC_WANT as _); + /// `IPV6_PMTUDISC_DO` + #[doc(alias = "IPV6_PMTUDISC_DO")] + pub const DO: Self = Self(c::IPV6_PMTUDISC_DO as _); + /// `IPV6_PMTUDISC_PROBE` + #[doc(alias = "IPV6_PMTUDISC_PROBE")] + pub const PROBE: Self = Self(c::IPV6_PMTUDISC_PROBE as _); + /// `IPV6_PMTUDISC_INTERFACE` + #[doc(alias = "IPV6_PMTUDISC_INTERFACE")] + pub const INTERFACE: Self = Self(c::IPV6_PMTUDISC_INTERFACE as _); + /// `IPV6_PMTUDISC_OMIT` + #[doc(alias = "IPV6_PMTUDISC_OMIT")] + pub const OMIT: Self = Self(c::IPV6_PMTUDISC_OMIT as _); + + /// Constructs an option from a raw integer. + #[inline] + pub const fn from_raw(raw: RawIpv6PathMtuDiscovery) -> Self { + Self(raw) + } + + /// Returns the raw integer for this option. + #[inline] + pub const fn as_raw(self) -> RawIpv6PathMtuDiscovery { + self.0 + } +} + /// `getsockopt(fd, SOL_SOCKET, SO_TYPE)`—Returns the type of a socket. /// /// See the [module-level documentation] for more. @@ -686,6 +794,54 @@ pub fn ipv6_mtu(fd: Fd) -> io::Result { backend::net::sockopt::ipv6_mtu(fd.as_fd()) } +/// `setsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER, value)` +/// +/// See the [module-level documentation] for more. +/// +/// [module-level documentation]: self#references-for-get_ip_-and-set_ip_-functions +#[cfg(linux_kernel)] +#[inline] +#[doc(alias = "IP_MTU_DISCOVER")] +pub fn set_ip_mtu_discover(fd: Fd, value: Ipv4PathMtuDiscovery) -> io::Result<()> { + backend::net::sockopt::set_ip_mtu_discover(fd.as_fd(), value) +} + +/// `getsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER)` +/// +/// See the [module-level documentation] for more. +/// +/// [module-level documentation]: self#references-for-get_ip_-and-set_ip_-functions +#[cfg(linux_kernel)] +#[inline] +#[doc(alias = "IP_MTU_DISCOVER")] +pub fn ip_mtu_discover(fd: Fd) -> io::Result { + backend::net::sockopt::ip_mtu_discover(fd.as_fd()) +} + +/// `setsockopt(fd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, value)` +/// +/// See the [module-level documentation] for more. +/// +/// [module-level documentation]: self#references-for-get_ipv6_-and-set_ipv6_-functions +#[cfg(linux_kernel)] +#[inline] +#[doc(alias = "IPV6_MTU_DISCOVER")] +pub fn set_ipv6_mtu_discover(fd: Fd, value: Ipv6PathMtuDiscovery) -> io::Result<()> { + backend::net::sockopt::set_ipv6_mtu_discover(fd.as_fd(), value) +} + +/// `getsockopt(fd, IPPROTO_IPV6, IPV6_MTU_DISCOVER)` +/// +/// See the [module-level documentation] for more. +/// +/// [module-level documentation]: self#references-for-get_ipv6_-and-set_ipv6_-functions +#[cfg(linux_kernel)] +#[inline] +#[doc(alias = "IPV6_MTU_DISCOVER")] +pub fn ipv6_mtu_discover(fd: Fd) -> io::Result { + backend::net::sockopt::ipv6_mtu_discover(fd.as_fd()) +} + /// `setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, value)` /// /// See the [module-level documentation] for more. @@ -1536,6 +1692,20 @@ pub fn socket_peercred(fd: Fd) -> io::Result { backend::net::sockopt::socket_peercred(fd.as_fd()) } +/// `getsockopt(fd, SOL_SOCKET, SO_TXTIME)` — Get transmission timing configuration. +#[cfg(all(target_os = "linux", feature = "time"))] +#[doc(alias = "SO_TXTIME")] +pub fn get_txtime(fd: Fd) -> io::Result<(ClockId, TxTimeFlags)> { + backend::net::sockopt::get_txtime(fd.as_fd()) +} + +/// `setsockopt(fd, SOL_SOCKET, SO_TXTIME)` — Configure transmission timing. +#[cfg(all(target_os = "linux", feature = "time"))] +#[doc(alias = "SO_TXTIME")] +pub fn set_txtime(fd: Fd, clockid: ClockId, flags: TxTimeFlags) -> io::Result<()> { + backend::net::sockopt::set_txtime(fd.as_fd(), clockid, flags) +} + /// `setsockopt(fd, SOL_XDP, XDP_UMEM_REG, value)` /// /// On kernel versions only supporting v1, the flags are ignored. @@ -1604,7 +1774,7 @@ pub fn set_xdp_rx_ring_size(fd: Fd, value: u32) -> io::Result<()> { /// - [Linux] /// /// [Linux]: https://www.kernel.org/doc/html/next/networking/af_xdp.html -#[cfg(target_os = "linux")] +#[cfg(all(linux_raw_dep, target_os = "linux"))] #[doc(alias = "XDP_MMAP_OFFSETS")] pub fn xdp_mmap_offsets(fd: Fd) -> io::Result { backend::net::sockopt::xdp_mmap_offsets(fd.as_fd()) @@ -1616,7 +1786,7 @@ pub fn xdp_mmap_offsets(fd: Fd) -> io::Result { /// - [Linux] /// /// [Linux]: https://www.kernel.org/doc/html/next/networking/af_xdp.html#xdp-statistics-getsockopt -#[cfg(target_os = "linux")] +#[cfg(all(linux_raw_dep, target_os = "linux"))] #[doc(alias = "XDP_STATISTICS")] pub fn xdp_statistics(fd: Fd) -> io::Result { backend::net::sockopt::xdp_statistics(fd.as_fd()) diff --git a/src/net/types.rs b/src/net/types.rs index 057f944d0..370f2468f 100644 --- a/src/net/types.rs +++ b/src/net/types.rs @@ -752,7 +752,7 @@ impl AddressFamily { #[cfg(apple)] pub const UTUN: Self = Self(c::AF_UTUN as _); /// `AF_VSOCK` - #[cfg(any(apple, target_os = "emscripten", target_os = "fuchsia"))] + #[cfg(any(apple, linux_kernel, target_os = "emscripten", target_os = "fuchsia"))] pub const VSOCK: Self = Self(c::AF_VSOCK as _); /// `AF_XDP` #[cfg(target_os = "linux")] @@ -1321,10 +1321,10 @@ pub mod eth { #[cfg(linux_kernel)] pub const PUPAT: Protocol = Protocol(new_raw_protocol((c::ETH_P_PUPAT as u16).to_be() as u32)); /// `ETH_P_TSN` - #[cfg(linux_kernel)] + #[cfg(linux_raw_dep)] pub const TSN: Protocol = Protocol(new_raw_protocol((c::ETH_P_TSN as u16).to_be() as u32)); /// `ETH_P_ERSPAN2` - #[cfg(linux_kernel)] + #[cfg(linux_raw_dep)] pub const ERSPAN2: Protocol = Protocol(new_raw_protocol((c::ETH_P_ERSPAN2 as u16).to_be() as u32)); /// `ETH_P_IP` @@ -1395,7 +1395,7 @@ pub mod eth { pub const P_8021Q: Protocol = Protocol(new_raw_protocol((c::ETH_P_8021Q as u16).to_be() as u32)); /// `ETH_P_ERSPAN` - #[cfg(linux_kernel)] + #[cfg(linux_raw_dep)] pub const ERSPAN: Protocol = Protocol(new_raw_protocol((c::ETH_P_ERSPAN as u16).to_be() as u32)); /// `ETH_P_IPX` @@ -1445,18 +1445,18 @@ pub mod eth { #[cfg(linux_kernel)] pub const PAE: Protocol = Protocol(new_raw_protocol((c::ETH_P_PAE as u16).to_be() as u32)); /// `ETH_P_PROFINET` - #[cfg(linux_kernel)] + #[cfg(linux_raw_dep)] pub const PROFINET: Protocol = Protocol(new_raw_protocol((c::ETH_P_PROFINET as u16).to_be() as u32)); /// `ETH_P_REALTEK` - #[cfg(linux_kernel)] + #[cfg(linux_raw_dep)] pub const REALTEK: Protocol = Protocol(new_raw_protocol((c::ETH_P_REALTEK as u16).to_be() as u32)); /// `ETH_P_AOE` #[cfg(linux_kernel)] pub const AOE: Protocol = Protocol(new_raw_protocol((c::ETH_P_AOE as u16).to_be() as u32)); /// `ETH_P_ETHERCAT` - #[cfg(linux_kernel)] + #[cfg(linux_raw_dep)] pub const ETHERCAT: Protocol = Protocol(new_raw_protocol((c::ETH_P_ETHERCAT as u16).to_be() as u32)); /// `ETH_P_8021AD` @@ -1468,17 +1468,17 @@ pub mod eth { pub const P_802_EX1: Protocol = Protocol(new_raw_protocol((c::ETH_P_802_EX1 as u16).to_be() as u32)); /// `ETH_P_PREAUTH` - #[cfg(linux_kernel)] + #[cfg(linux_raw_dep)] pub const PREAUTH: Protocol = Protocol(new_raw_protocol((c::ETH_P_PREAUTH as u16).to_be() as u32)); /// `ETH_P_TIPC` #[cfg(linux_kernel)] pub const TIPC: Protocol = Protocol(new_raw_protocol((c::ETH_P_TIPC as u16).to_be() as u32)); /// `ETH_P_LLDP` - #[cfg(linux_kernel)] + #[cfg(linux_raw_dep)] pub const LLDP: Protocol = Protocol(new_raw_protocol((c::ETH_P_LLDP as u16).to_be() as u32)); /// `ETH_P_MRP` - #[cfg(linux_kernel)] + #[cfg(linux_raw_dep)] pub const MRP: Protocol = Protocol(new_raw_protocol((c::ETH_P_MRP as u16).to_be() as u32)); /// `ETH_P_MACSEC` #[cfg(linux_kernel)] @@ -1495,19 +1495,19 @@ pub mod eth { #[cfg(linux_kernel)] pub const P_1588: Protocol = Protocol(new_raw_protocol((c::ETH_P_1588 as u16).to_be() as u32)); /// `ETH_P_NCSI` - #[cfg(linux_kernel)] + #[cfg(linux_raw_dep)] pub const NCSI: Protocol = Protocol(new_raw_protocol((c::ETH_P_NCSI as u16).to_be() as u32)); /// `ETH_P_PRP` #[cfg(linux_kernel)] pub const PRP: Protocol = Protocol(new_raw_protocol((c::ETH_P_PRP as u16).to_be() as u32)); /// `ETH_P_CFM` - #[cfg(linux_kernel)] + #[cfg(linux_raw_dep)] pub const CFM: Protocol = Protocol(new_raw_protocol((c::ETH_P_CFM as u16).to_be() as u32)); /// `ETH_P_FCOE` #[cfg(linux_kernel)] pub const FCOE: Protocol = Protocol(new_raw_protocol((c::ETH_P_FCOE as u16).to_be() as u32)); /// `ETH_P_IBOE` - #[cfg(linux_kernel)] + #[cfg(linux_raw_dep)] pub const IBOE: Protocol = Protocol(new_raw_protocol((c::ETH_P_IBOE as u16).to_be() as u32)); /// `ETH_P_TDLS` #[cfg(linux_kernel)] @@ -1520,10 +1520,10 @@ pub mod eth { pub const P_80221: Protocol = Protocol(new_raw_protocol((c::ETH_P_80221 as u16).to_be() as u32)); /// `ETH_P_HSR` - #[cfg(linux_kernel)] + #[cfg(linux_raw_dep)] pub const HSR: Protocol = Protocol(new_raw_protocol((c::ETH_P_HSR as u16).to_be() as u32)); /// `ETH_P_NSH` - #[cfg(linux_kernel)] + #[cfg(linux_raw_dep)] pub const NSH: Protocol = Protocol(new_raw_protocol((c::ETH_P_NSH as u16).to_be() as u32)); /// `ETH_P_LOOPBACK` #[cfg(linux_kernel)] @@ -1542,15 +1542,15 @@ pub mod eth { #[cfg(linux_kernel)] pub const EDSA: Protocol = Protocol(new_raw_protocol((c::ETH_P_EDSA as u16).to_be() as u32)); /// `ETH_P_DSA_8021Q` - #[cfg(linux_kernel)] + #[cfg(linux_raw_dep)] pub const DSA_8021Q: Protocol = Protocol(new_raw_protocol((c::ETH_P_DSA_8021Q as u16).to_be() as u32)); /// `ETH_P_DSA_A5PSW` - #[cfg(linux_kernel)] + #[cfg(linux_raw_dep)] pub const DSA_A5PSW: Protocol = Protocol(new_raw_protocol((c::ETH_P_DSA_A5PSW as u16).to_be() as u32)); /// `ETH_P_IFE` - #[cfg(linux_kernel)] + #[cfg(linux_raw_dep)] pub const IFE: Protocol = Protocol(new_raw_protocol((c::ETH_P_IFE as u16).to_be() as u32)); /// `ETH_P_AF_IUCV` #[cfg(linux_kernel)] @@ -1568,7 +1568,7 @@ pub mod eth { #[cfg(linux_kernel)] pub const AX25: Protocol = Protocol(new_raw_protocol((c::ETH_P_AX25 as u16).to_be() as u32)); /// `ETH_P_ALL` - #[cfg(linux_kernel)] + #[cfg(linux_raw_dep)] pub const ALL: Protocol = Protocol(new_raw_protocol((c::ETH_P_ALL as u16).to_be() as u32)); /// `ETH_P_802_2` #[cfg(linux_kernel)] @@ -1593,13 +1593,13 @@ pub mod eth { pub const LOCALTALK: Protocol = Protocol(new_raw_protocol((c::ETH_P_LOCALTALK as u16).to_be() as u32)); /// `ETH_P_CAN` - #[cfg(linux_kernel)] + #[cfg(linux_raw_dep)] pub const CAN: Protocol = Protocol(new_raw_protocol((c::ETH_P_CAN as u16).to_be() as u32)); /// `ETH_P_CANFD` #[cfg(linux_kernel)] pub const CANFD: Protocol = Protocol(new_raw_protocol((c::ETH_P_CANFD as u16).to_be() as u32)); /// `ETH_P_CANXL` - #[cfg(linux_kernel)] + #[cfg(linux_raw_dep)] pub const CANXL: Protocol = Protocol(new_raw_protocol((c::ETH_P_CANXL as u16).to_be() as u32)); /// `ETH_P_PPPTALK` #[cfg(linux_kernel)] @@ -1632,7 +1632,7 @@ pub mod eth { pub const ARCNET: Protocol = Protocol(new_raw_protocol((c::ETH_P_ARCNET as u16).to_be() as u32)); /// `ETH_P_DSA` - #[cfg(linux_kernel)] + #[cfg(linux_raw_dep)] pub const DSA: Protocol = Protocol(new_raw_protocol((c::ETH_P_DSA as u16).to_be() as u32)); /// `ETH_P_TRAILER` #[cfg(linux_kernel)] @@ -1650,13 +1650,13 @@ pub mod eth { #[cfg(linux_kernel)] pub const CAIF: Protocol = Protocol(new_raw_protocol((c::ETH_P_CAIF as u16).to_be() as u32)); /// `ETH_P_XDSA` - #[cfg(linux_kernel)] + #[cfg(linux_raw_dep)] pub const XDSA: Protocol = Protocol(new_raw_protocol((c::ETH_P_XDSA as u16).to_be() as u32)); /// `ETH_P_MAP` - #[cfg(linux_kernel)] + #[cfg(linux_raw_dep)] pub const MAP: Protocol = Protocol(new_raw_protocol((c::ETH_P_MAP as u16).to_be() as u32)); /// `ETH_P_MCTP` - #[cfg(linux_kernel)] + #[cfg(linux_raw_dep)] pub const MCTP: Protocol = Protocol(new_raw_protocol((c::ETH_P_MCTP as u16).to_be() as u32)); } @@ -1722,6 +1722,21 @@ bitflags! { } } +#[cfg(all(target_os = "linux", feature = "time"))] +bitflags! { + /// Flags for use with [`set_txtime`]. + /// + /// [`set_txtime`]: crate::net::sockopt::set_txtime + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct TxTimeFlags: u32 { + /// `SOF_TXTIME_DEADLINE_MODE` + const DEADLINE_MODE = bitcast!(c::SOF_TXTIME_DEADLINE_MODE); + /// `SOF_TXTIME_REPORT_ERRORS` + const REPORT_ERRORS = bitcast!(c::SOF_TXTIME_REPORT_ERRORS); + } +} + /// `AF_XDP` related types and constants. #[cfg(target_os = "linux")] pub mod xdp { diff --git a/src/not_implemented.rs b/src/not_implemented.rs index e8b162a88..7220295b6 100644 --- a/src/not_implemented.rs +++ b/src/not_implemented.rs @@ -297,7 +297,6 @@ pub mod yet { not_implemented!(cachestat); not_implemented!(fanotify_init); not_implemented!(fanotify_mark); - not_implemented!(getifaddrs); not_implemented!(signalfd); not_implemented!(mount_setattr); not_implemented!(extattr_delete_fd); diff --git a/src/path/arg.rs b/src/path/arg.rs index 9edeb57ff..851d3a69b 100644 --- a/src/path/arg.rs +++ b/src/path/arg.rs @@ -229,7 +229,6 @@ impl Arg for String { } #[cfg(feature = "std")] -#[cfg(any(not(target_os = "wasi"), not(target_env = "p2"), wasip2))] impl Arg for &OsStr { #[inline] fn as_str(&self) -> io::Result<&str> { @@ -243,9 +242,7 @@ impl Arg for &OsStr { #[inline] fn as_cow_c_str(&self) -> io::Result> { - Ok(Cow::Owned( - CString::new(self.as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, - )) + self.into_c_str() } #[inline] @@ -253,9 +250,10 @@ impl Arg for &OsStr { where Self: 'b, { - Ok(Cow::Owned( - CString::new(self.as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, - )) + #[cfg(all(target_os = "wasi", target_env = "p2", not(wasip2)))] + return self.to_str().ok_or(io::Errno::INVAL)?.into_c_str(); + #[cfg(any(wasip2, not(all(target_os = "wasi", target_env = "p2"))))] + return self.as_bytes().into_c_str(); } #[inline] @@ -264,12 +262,15 @@ impl Arg for &OsStr { Self: Sized, F: FnOnce(&CStr) -> io::Result, { - with_c_str(self.as_bytes(), f) + #[cfg(all(target_os = "wasi", target_env = "p2", not(wasip2)))] + return self.as_str()?.into_with_c_str(f); + + #[cfg(any(wasip2, not(all(target_os = "wasi", target_env = "p2"))))] + return self.as_bytes().into_with_c_str(f); } } #[cfg(feature = "std")] -#[cfg(any(not(target_os = "wasi"), not(target_env = "p2"), wasip2))] impl Arg for &OsString { #[inline] fn as_str(&self) -> io::Result<&str> { @@ -283,10 +284,7 @@ impl Arg for &OsString { #[inline] fn as_cow_c_str(&self) -> io::Result> { - Ok(Cow::Owned( - CString::new(OsString::as_os_str(self).as_bytes()) - .map_err(|_cstr_err| io::Errno::INVAL)?, - )) + self.as_os_str().into_c_str() } #[inline] @@ -303,12 +301,11 @@ impl Arg for &OsString { Self: Sized, F: FnOnce(&CStr) -> io::Result, { - with_c_str(self.as_bytes(), f) + self.as_os_str().into_with_c_str(f) } } #[cfg(feature = "std")] -#[cfg(any(not(target_os = "wasi"), not(target_env = "p2"), wasip2))] impl Arg for OsString { #[inline] fn as_str(&self) -> io::Result<&str> { @@ -322,9 +319,7 @@ impl Arg for OsString { #[inline] fn as_cow_c_str(&self) -> io::Result> { - Ok(Cow::Owned( - CString::new(self.as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, - )) + self.as_os_str().into_c_str() } #[inline] @@ -332,9 +327,13 @@ impl Arg for OsString { where Self: 'b, { - Ok(Cow::Owned( - CString::new(self.into_vec()).map_err(|_cstr_err| io::Errno::INVAL)?, - )) + #[cfg(all(target_os = "wasi", target_env = "p2", not(wasip2)))] + return self + .into_string() + .map_err(|_strng_err| io::Errno::INVAL)? + .into_c_str(); + #[cfg(any(wasip2, not(all(target_os = "wasi", target_env = "p2"))))] + self.into_vec().into_c_str() } #[inline] @@ -343,12 +342,11 @@ impl Arg for OsString { Self: Sized, F: FnOnce(&CStr) -> io::Result, { - f(&CString::new(self.into_vec()).map_err(|_cstr_err| io::Errno::INVAL)?) + f(&self.into_c_str()?) } } #[cfg(feature = "std")] -#[cfg(any(not(target_os = "wasi"), not(target_env = "p2"), wasip2))] impl Arg for &Path { #[inline] fn as_str(&self) -> io::Result<&str> { @@ -362,9 +360,7 @@ impl Arg for &Path { #[inline] fn as_cow_c_str(&self) -> io::Result> { - Ok(Cow::Owned( - CString::new(self.as_os_str().as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, - )) + self.as_os_str().into_c_str() } #[inline] @@ -372,9 +368,7 @@ impl Arg for &Path { where Self: 'b, { - Ok(Cow::Owned( - CString::new(self.as_os_str().as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, - )) + self.as_os_str().into_c_str() } #[inline] @@ -383,19 +377,15 @@ impl Arg for &Path { Self: Sized, F: FnOnce(&CStr) -> io::Result, { - with_c_str(self.as_os_str().as_bytes(), f) + self.as_os_str().into_with_c_str(f) } } #[cfg(feature = "std")] -#[cfg(any(not(target_os = "wasi"), not(target_env = "p2"), wasip2))] impl Arg for &PathBuf { #[inline] fn as_str(&self) -> io::Result<&str> { - PathBuf::as_path(self) - .as_os_str() - .to_str() - .ok_or(io::Errno::INVAL) + self.as_os_str().to_str().ok_or(io::Errno::INVAL) } #[inline] @@ -405,10 +395,7 @@ impl Arg for &PathBuf { #[inline] fn as_cow_c_str(&self) -> io::Result> { - Ok(Cow::Owned( - CString::new(PathBuf::as_path(self).as_os_str().as_bytes()) - .map_err(|_cstr_err| io::Errno::INVAL)?, - )) + self.as_os_str().into_c_str() } #[inline] @@ -425,12 +412,11 @@ impl Arg for &PathBuf { Self: Sized, F: FnOnce(&CStr) -> io::Result, { - with_c_str(self.as_os_str().as_bytes(), f) + self.as_os_str().into_with_c_str(f) } } #[cfg(feature = "std")] -#[cfg(any(not(target_os = "wasi"), not(target_env = "p2"), wasip2))] impl Arg for PathBuf { #[inline] fn as_str(&self) -> io::Result<&str> { @@ -444,9 +430,7 @@ impl Arg for PathBuf { #[inline] fn as_cow_c_str(&self) -> io::Result> { - Ok(Cow::Owned( - CString::new(self.as_os_str().as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, - )) + self.as_os_str().into_c_str() } #[inline] @@ -454,9 +438,7 @@ impl Arg for PathBuf { where Self: 'b, { - Ok(Cow::Owned( - CString::new(self.into_os_string().into_vec()).map_err(|_cstr_err| io::Errno::INVAL)?, - )) + self.into_os_string().into_c_str() } #[inline] @@ -465,10 +447,7 @@ impl Arg for PathBuf { Self: Sized, F: FnOnce(&CStr) -> io::Result, { - f( - &CString::new(self.into_os_string().into_vec()) - .map_err(|_cstr_err| io::Errno::INVAL)?, - ) + self.into_os_string().into_with_c_str(f) } } @@ -513,12 +492,12 @@ impl Arg for &CStr { impl Arg for &CString { #[inline] fn as_str(&self) -> io::Result<&str> { - unimplemented!() + self.to_str().map_err(|_utf8_err| io::Errno::INVAL) } #[inline] fn to_string_lossy(&self) -> Cow<'_, str> { - unimplemented!() + CStr::to_string_lossy(self) } #[inline] @@ -623,7 +602,6 @@ impl<'a> Arg for Cow<'a, str> { } #[cfg(feature = "std")] -#[cfg(any(not(target_os = "wasi"), not(target_env = "p2"), wasip2))] impl<'a> Arg for Cow<'a, OsStr> { #[inline] fn as_str(&self) -> io::Result<&str> { @@ -637,9 +615,7 @@ impl<'a> Arg for Cow<'a, OsStr> { #[inline] fn as_cow_c_str(&self) -> io::Result> { - Ok(Cow::Owned( - CString::new(self.as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, - )) + (&**self).into_c_str() } #[inline] @@ -647,13 +623,10 @@ impl<'a> Arg for Cow<'a, OsStr> { where Self: 'b, { - Ok(Cow::Owned( - match self { - Cow::Owned(os) => CString::new(os.into_vec()), - Cow::Borrowed(os) => CString::new(os.as_bytes()), - } - .map_err(|_cstr_err| io::Errno::INVAL)?, - )) + match self { + Cow::Owned(os) => os.into_c_str(), + Cow::Borrowed(os) => os.into_c_str(), + } } #[inline] @@ -662,7 +635,7 @@ impl<'a> Arg for Cow<'a, OsStr> { Self: Sized, F: FnOnce(&CStr) -> io::Result, { - with_c_str(self.as_bytes(), f) + (&*self).into_with_c_str(f) } } @@ -703,7 +676,6 @@ impl<'a> Arg for Cow<'a, CStr> { } #[cfg(feature = "std")] -#[cfg(any(not(target_os = "wasi"), not(target_env = "p2"), wasip2))] impl<'a> Arg for Component<'a> { #[inline] fn as_str(&self) -> io::Result<&str> { @@ -717,9 +689,7 @@ impl<'a> Arg for Component<'a> { #[inline] fn as_cow_c_str(&self) -> io::Result> { - Ok(Cow::Owned( - CString::new(self.as_os_str().as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, - )) + self.as_os_str().into_c_str() } #[inline] @@ -727,9 +697,7 @@ impl<'a> Arg for Component<'a> { where Self: 'b, { - Ok(Cow::Owned( - CString::new(self.as_os_str().as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, - )) + self.as_os_str().into_c_str() } #[inline] @@ -738,12 +706,11 @@ impl<'a> Arg for Component<'a> { Self: Sized, F: FnOnce(&CStr) -> io::Result, { - with_c_str(self.as_os_str().as_bytes(), f) + self.as_os_str().into_with_c_str(f) } } #[cfg(feature = "std")] -#[cfg(any(not(target_os = "wasi"), not(target_env = "p2"), wasip2))] impl<'a> Arg for Components<'a> { #[inline] fn as_str(&self) -> io::Result<&str> { @@ -757,10 +724,7 @@ impl<'a> Arg for Components<'a> { #[inline] fn as_cow_c_str(&self) -> io::Result> { - Ok(Cow::Owned( - CString::new(self.as_path().as_os_str().as_bytes()) - .map_err(|_cstr_err| io::Errno::INVAL)?, - )) + self.as_path().into_c_str() } #[inline] @@ -768,10 +732,7 @@ impl<'a> Arg for Components<'a> { where Self: 'b, { - Ok(Cow::Owned( - CString::new(self.as_path().as_os_str().as_bytes()) - .map_err(|_cstr_err| io::Errno::INVAL)?, - )) + self.as_path().into_c_str() } #[inline] @@ -780,12 +741,11 @@ impl<'a> Arg for Components<'a> { Self: Sized, F: FnOnce(&CStr) -> io::Result, { - with_c_str(self.as_path().as_os_str().as_bytes(), f) + self.as_path().into_with_c_str(f) } } #[cfg(feature = "std")] -#[cfg(any(not(target_os = "wasi"), not(target_env = "p2"), wasip2))] impl<'a> Arg for Iter<'a> { #[inline] fn as_str(&self) -> io::Result<&str> { @@ -799,10 +759,7 @@ impl<'a> Arg for Iter<'a> { #[inline] fn as_cow_c_str(&self) -> io::Result> { - Ok(Cow::Owned( - CString::new(self.as_path().as_os_str().as_bytes()) - .map_err(|_cstr_err| io::Errno::INVAL)?, - )) + self.as_path().into_c_str() } #[inline] @@ -810,10 +767,7 @@ impl<'a> Arg for Iter<'a> { where Self: 'b, { - Ok(Cow::Owned( - CString::new(self.as_path().as_os_str().as_bytes()) - .map_err(|_cstr_err| io::Errno::INVAL)?, - )) + self.as_path().into_c_str() } #[inline] @@ -822,7 +776,7 @@ impl<'a> Arg for Iter<'a> { Self: Sized, F: FnOnce(&CStr) -> io::Result, { - with_c_str(self.as_path().as_os_str().as_bytes(), f) + self.as_path().into_with_c_str(f) } } @@ -910,7 +864,6 @@ impl Arg for &Vec { } #[cfg(feature = "alloc")] -#[cfg(any(not(target_os = "wasi"), not(target_env = "p2"), wasip2))] impl Arg for Vec { #[inline] fn as_str(&self) -> io::Result<&str> { diff --git a/src/pid.rs b/src/pid.rs index d09cba0e3..fc0dc9e68 100644 --- a/src/pid.rs +++ b/src/pid.rs @@ -2,7 +2,7 @@ #![allow(unsafe_code)] -use core::num::NonZeroI32; +use core::{fmt, num::NonZeroI32}; /// A process identifier as a raw integer. pub type RawPid = i32; @@ -41,7 +41,7 @@ impl Pid { /// [pidfd]: https://man7.org/linux/man-pages/man2/pidfd_open.2.html #[inline] pub const fn from_raw(raw: RawPid) -> Option { - debug_assert!(raw > 0); + debug_assert!(raw >= 0); match NonZeroI32::new(raw) { Some(non_zero) => Some(Self(non_zero)), None => None, @@ -78,6 +78,14 @@ impl Pid { self.0 } + /// Converts a `Pid` into a `RawPid`. + /// + /// This is the same as `self.as_raw_nonzero().get()`. + #[inline] + pub const fn as_raw_pid(self) -> RawPid { + self.0.get() + } + /// Converts an `Option` into a `RawPid`. #[inline] pub const fn as_raw(pid: Option) -> RawPid { @@ -94,6 +102,44 @@ impl Pid { } } +impl fmt::Display for Pid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} +impl fmt::Binary for Pid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} +impl fmt::Octal for Pid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} +impl fmt::LowerHex for Pid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} +impl fmt::UpperHex for Pid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} +#[cfg(lower_upper_exp_for_non_zero)] +impl fmt::LowerExp for Pid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} +#[cfg(lower_upper_exp_for_non_zero)] +impl fmt::UpperExp for Pid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + #[cfg(test)] mod tests { use super::*; @@ -115,4 +161,22 @@ mod tests { transmute::, RawPid>(Some(Pid::from_raw_unchecked(4567))) }); } + + #[test] + fn test_ctors() { + use std::num::NonZeroI32; + assert!(Pid::from_raw(0).is_none()); + assert_eq!( + Pid::from_raw(77).unwrap().as_raw_nonzero(), + NonZeroI32::new(77).unwrap() + ); + assert_eq!(Pid::from_raw(77).unwrap().as_raw_pid(), 77); + assert_eq!(Pid::as_raw(Pid::from_raw(77)), 77); + } + + #[test] + fn test_specials() { + assert!(Pid::from_raw(1).unwrap().is_init()); + assert_eq!(Pid::from_raw(1).unwrap(), Pid::INIT); + } } diff --git a/src/prctl.rs b/src/prctl.rs index 1a31a8cc4..ab8ba2429 100644 --- a/src/prctl.rs +++ b/src/prctl.rs @@ -10,6 +10,7 @@ use bitflags::bitflags; use core::mem::MaybeUninit; use core::ptr::null_mut; +#[cfg(linux_raw_dep)] bitflags! { /// `PR_PAC_AP*` #[repr(transparent)] diff --git a/src/process/prctl.rs b/src/process/prctl.rs index 8b08409c1..7c56305c3 100644 --- a/src/process/prctl.rs +++ b/src/process/prctl.rs @@ -1083,6 +1083,7 @@ const PR_PAC_GET_ENABLED_KEYS: c_int = 61; /// [`prctl(PR_PAC_GET_ENABLED_KEYS,…)`]: https://www.kernel.org/doc/html/v6.13/arch/arm64/pointer-authentication.html #[inline] #[doc(alias = "PR_PAC_GET_ENABLED_KEYS")] +#[cfg(linux_raw_dep)] pub fn enabled_pointer_authentication_keys() -> io::Result { let r = unsafe { prctl_1arg(PR_PAC_GET_ENABLED_KEYS)? } as c_uint; PointerAuthenticationKeys::from_bits(r).ok_or(io::Errno::RANGE) @@ -1103,6 +1104,7 @@ const PR_PAC_SET_ENABLED_KEYS: c_int = 60; /// [`prctl(PR_PAC_SET_ENABLED_KEYS,…)`]: https://www.kernel.org/doc/html/v6.13/arch/arm64/pointer-authentication.html #[inline] #[doc(alias = "PR_PAC_SET_ENABLED_KEYS")] +#[cfg(linux_raw_dep)] pub unsafe fn configure_pointer_authentication_keys< Config: Iterator, >( diff --git a/src/termios/types.rs b/src/termios/types.rs index d9589087c..899fa74b6 100644 --- a/src/termios/types.rs +++ b/src/termios/types.rs @@ -275,7 +275,7 @@ bitflags! { const ICRNL = c::ICRNL; /// `IUCLC` - #[cfg(any(linux_kernel, solarish, target_os = "aix", target_os = "haiku", target_os = "nto"))] + #[cfg(any(linux_raw_dep, solarish, target_os = "aix", target_os = "haiku", target_os = "nto"))] const IUCLC = c::IUCLC; /// `IXON` @@ -597,7 +597,7 @@ bitflags! { #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct LocalModes: types::tcflag_t { /// `XCASE` - #[cfg(any(linux_kernel, target_arch = "s390x", target_os = "haiku"))] + #[cfg(any(linux_raw_dep, target_arch = "s390x", target_os = "haiku"))] const XCASE = c::XCASE; /// `ECHOCTL` diff --git a/src/thread/futex.rs b/src/thread/futex.rs index 7ac49e69f..9862997d4 100644 --- a/src/thread/futex.rs +++ b/src/thread/futex.rs @@ -2,7 +2,7 @@ //! //! Futex is a very low-level mechanism for implementing concurrency primitives //! such as mutexes, rwlocks, and condvars. For a higher-level API that -//! provides those abstractions, see [rustix-futex-syntax]. +//! provides those abstractions, see [rustix-futex-sync]. //! //! # Examples //! diff --git a/src/thread/id.rs b/src/thread/id.rs index aa67c9eaa..a299628f3 100644 --- a/src/thread/id.rs +++ b/src/thread/id.rs @@ -105,8 +105,13 @@ pub fn set_thread_uid(uid: Uid) -> io::Result<()> { /// [Linux]: https://man7.org/linux/man-pages/man2/setresuid.2.html /// [linux_notes]: https://man7.org/linux/man-pages/man2/setresuid.2.html#NOTES #[inline] -pub fn set_thread_res_uid(ruid: Uid, euid: Uid, suid: Uid) -> io::Result<()> { - backend::thread::syscalls::setresuid_thread(ruid, euid, suid) +pub fn set_thread_res_uid(ruid: R, euid: E, suid: S) -> io::Result<()> +where + R: Into>, + E: Into>, + S: Into>, +{ + backend::thread::syscalls::setresuid_thread(ruid.into(), euid.into(), suid.into()) } /// `setgid(gid)`—Sets the effective group ID of the current thread. @@ -154,8 +159,13 @@ pub fn set_thread_gid(gid: Gid) -> io::Result<()> { /// [Linux]: https://man7.org/linux/man-pages/man2/setresgid.2.html /// [linux_notes]: https://man7.org/linux/man-pages/man2/setresgid.2.html#NOTES #[inline] -pub fn set_thread_res_gid(rgid: Gid, egid: Gid, sgid: Gid) -> io::Result<()> { - backend::thread::syscalls::setresgid_thread(rgid, egid, sgid) +pub fn set_thread_res_gid(rgid: R, egid: E, sgid: S) -> io::Result<()> +where + R: Into>, + E: Into>, + S: Into>, +{ + backend::thread::syscalls::setresgid_thread(rgid.into(), egid.into(), sgid.into()) } /// `setgroups(groups)`—Sets the supplementary group IDs for the calling diff --git a/src/thread/libcap.rs b/src/thread/libcap.rs index 0a0fbb4ca..af5ab58a8 100644 --- a/src/thread/libcap.rs +++ b/src/thread/libcap.rs @@ -8,18 +8,22 @@ use crate::{backend, io}; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct CapabilitySets { /// `__user_cap_data_struct.effective` - pub effective: CapabilityFlags, + pub effective: CapabilitySet, /// `__user_cap_data_struct.permitted` - pub permitted: CapabilityFlags, + pub permitted: CapabilitySet, /// `__user_cap_data_struct.inheritable` - pub inheritable: CapabilityFlags, + pub inheritable: CapabilitySet, } +/// Previous name of `CapabilitySet`. +#[deprecated(since = "1.1.0", note = "Renamed to CapabilitySet")] +pub type CapabilityFlags = CapabilitySet; + bitflags! { /// `CAP_*` constants. #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] - pub struct CapabilityFlags: u64 { + pub struct CapabilitySet: u64 { /// `CAP_CHOWN` const CHOWN = 1 << linux_raw_sys::general::CAP_CHOWN; /// `CAP_DAC_OVERRIDE` @@ -156,9 +160,9 @@ fn capget(pid: Option) -> io::Result { // The kernel returns a partitioned bitset that we just combined above. Ok(CapabilitySets { - effective: CapabilityFlags::from_bits_retain(effective), - permitted: CapabilityFlags::from_bits_retain(permitted), - inheritable: CapabilityFlags::from_bits_retain(inheritable), + effective: CapabilitySet::from_bits_retain(effective), + permitted: CapabilitySet::from_bits_retain(permitted), + inheritable: CapabilitySet::from_bits_retain(inheritable), }) } diff --git a/src/thread/mod.rs b/src/thread/mod.rs index e32b5945c..26d1de427 100644 --- a/src/thread/mod.rs +++ b/src/thread/mod.rs @@ -23,7 +23,9 @@ pub use clock::*; #[cfg(linux_kernel)] pub use id::*; #[cfg(linux_kernel)] -pub use libcap::{capabilities, set_capabilities, CapabilityFlags, CapabilitySets}; +// #[expect(deprecated, reason = "CapabilityFlags is deprecated")] +#[allow(deprecated)] +pub use libcap::{capabilities, set_capabilities, CapabilityFlags, CapabilitySet, CapabilitySets}; #[cfg(linux_kernel)] pub use membarrier::*; #[cfg(linux_kernel)] diff --git a/src/thread/prctl.rs b/src/thread/prctl.rs index 4719b1eb4..ad6def3ff 100644 --- a/src/thread/prctl.rs +++ b/src/thread/prctl.rs @@ -21,12 +21,15 @@ use crate::backend::prctl::syscalls; use crate::ffi::CString; use crate::ffi::{c_int, c_uint, c_void, CStr}; use crate::io; +use crate::io::Errno; use crate::pid::Pid; -use crate::prctl::{ - prctl_1arg, prctl_2args, prctl_3args, prctl_get_at_arg2_optional, PointerAuthenticationKeys, -}; +#[cfg(linux_raw_dep)] +use crate::prctl::PointerAuthenticationKeys; +use crate::prctl::{prctl_1arg, prctl_2args, prctl_3args, prctl_get_at_arg2_optional}; use crate::utils::as_ptr; +use super::CapabilitySet; + // // PR_GET_KEEPCAPS/PR_SET_KEEPCAPS // @@ -178,6 +181,7 @@ pub fn set_secure_computing_mode(mode: SecureComputingMode) -> io::Result<()> { const PR_CAPBSET_READ: c_int = 23; /// Linux per-thread capability. +#[deprecated(since = "1.1.0", note = "Use CapabilitySet with a single bit instead")] #[derive(Copy, Clone, Debug, Eq, PartialEq)] #[repr(u32)] #[non_exhaustive] @@ -383,6 +387,75 @@ pub enum Capability { CheckpointRestore = linux_raw_sys::general::CAP_CHECKPOINT_RESTORE, } +mod private { + pub trait Sealed {} + pub struct Token; + + #[allow(deprecated)] + impl Sealed for crate::thread::Capability {} + impl Sealed for crate::thread::CapabilitySet {} +} +/// Compatibility trait to keep existing code that uses the deprecated [`Capability`] type working. +/// +/// This trait and its methods are sealed. It must not be used downstream. +pub trait CompatCapability: private::Sealed + Copy { + #[doc(hidden)] + fn as_capability_set(self, _: private::Token) -> CapabilitySet; +} +#[allow(deprecated)] +impl CompatCapability for Capability { + fn as_capability_set(self, _: private::Token) -> CapabilitySet { + match self { + Self::ChangeOwnership => CapabilitySet::CHOWN, + Self::DACOverride => CapabilitySet::DAC_OVERRIDE, + Self::DACReadSearch => CapabilitySet::DAC_READ_SEARCH, + Self::FileOwner => CapabilitySet::FOWNER, + Self::FileSetID => CapabilitySet::FSETID, + Self::Kill => CapabilitySet::KILL, + Self::SetGroupID => CapabilitySet::SETGID, + Self::SetUserID => CapabilitySet::SETUID, + Self::SetPermittedCapabilities => CapabilitySet::SETPCAP, + Self::LinuxImmutable => CapabilitySet::LINUX_IMMUTABLE, + Self::NetBindService => CapabilitySet::NET_BIND_SERVICE, + Self::NetBroadcast => CapabilitySet::NET_BROADCAST, + Self::NetAdmin => CapabilitySet::NET_ADMIN, + Self::NetRaw => CapabilitySet::NET_RAW, + Self::IPCLock => CapabilitySet::IPC_LOCK, + Self::IPCOwner => CapabilitySet::IPC_OWNER, + Self::SystemModule => CapabilitySet::SYS_MODULE, + Self::SystemRawIO => CapabilitySet::SYS_RAWIO, + Self::SystemChangeRoot => CapabilitySet::SYS_CHROOT, + Self::SystemProcessTrace => CapabilitySet::SYS_PTRACE, + Self::SystemProcessAccounting => CapabilitySet::SYS_PACCT, + Self::SystemAdmin => CapabilitySet::SYS_ADMIN, + Self::SystemBoot => CapabilitySet::SYS_BOOT, + Self::SystemNice => CapabilitySet::SYS_NICE, + Self::SystemResource => CapabilitySet::SYS_RESOURCE, + Self::SystemTime => CapabilitySet::SYS_TIME, + Self::SystemTTYConfig => CapabilitySet::SYS_TTY_CONFIG, + Self::MakeNode => CapabilitySet::MKNOD, + Self::Lease => CapabilitySet::LEASE, + Self::AuditWrite => CapabilitySet::AUDIT_WRITE, + Self::AuditControl => CapabilitySet::AUDIT_CONTROL, + Self::SetFileCapabilities => CapabilitySet::SETFCAP, + Self::MACOverride => CapabilitySet::MAC_OVERRIDE, + Self::MACAdmin => CapabilitySet::MAC_ADMIN, + Self::SystemLog => CapabilitySet::SYSLOG, + Self::WakeAlarm => CapabilitySet::WAKE_ALARM, + Self::BlockSuspend => CapabilitySet::BLOCK_SUSPEND, + Self::AuditRead => CapabilitySet::AUDIT_READ, + Self::PerformanceMonitoring => CapabilitySet::PERFMON, + Self::BerkeleyPacketFilters => CapabilitySet::BPF, + Self::CheckpointRestore => CapabilitySet::CHECKPOINT_RESTORE, + } + } +} +impl CompatCapability for CapabilitySet { + fn as_capability_set(self, _: private::Token) -> CapabilitySet { + self + } +} + /// Check if the specified capability is in the calling thread's capability /// bounding set. /// @@ -391,8 +464,15 @@ pub enum Capability { /// /// [`prctl(PR_CAPBSET_READ,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] -pub fn capability_is_in_bounding_set(capability: Capability) -> io::Result { - unsafe { prctl_2args(PR_CAPBSET_READ, capability as usize as *mut _) }.map(|r| r != 0) +pub fn capability_is_in_bounding_set(capability: impl CompatCapability) -> io::Result { + let capset = capability.as_capability_set(private::Token).bits(); + if capset.count_ones() != 1 { + return Err(Errno::INVAL); + } + let cap = capset.trailing_zeros(); + + // as *mut _ should be ptr::without_provenance_mut but our MSRV does not allow it. + unsafe { prctl_2args(PR_CAPBSET_READ, cap as usize as *mut _) }.map(|r| r != 0) } const PR_CAPBSET_DROP: c_int = 24; @@ -406,8 +486,15 @@ const PR_CAPBSET_DROP: c_int = 24; /// /// [`prctl(PR_CAPBSET_DROP,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] -pub fn remove_capability_from_bounding_set(capability: Capability) -> io::Result<()> { - unsafe { prctl_2args(PR_CAPBSET_DROP, capability as usize as *mut _) }.map(|_r| ()) +pub fn remove_capability_from_bounding_set(capability: impl CompatCapability) -> io::Result<()> { + let capset = capability.as_capability_set(private::Token).bits(); + if capset.count_ones() != 1 { + return Err(Errno::INVAL); + } + let cap = capset.trailing_zeros(); + + // as *mut _ should be ptr::without_provenance_mut but our MSRV does not allow it. + unsafe { prctl_2args(PR_CAPBSET_DROP, cap as usize as *mut _) }.map(|_r| ()) } // @@ -608,9 +695,22 @@ const PR_CAP_AMBIENT_IS_SET: usize = 1; /// /// [`prctl(PR_CAP_AMBIENT,PR_CAP_AMBIENT_IS_SET,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] -pub fn capability_is_in_ambient_set(capability: Capability) -> io::Result { - let cap = capability as usize as *mut _; - unsafe { prctl_3args(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET as *mut _, cap) }.map(|r| r != 0) +pub fn capability_is_in_ambient_set(capability: impl CompatCapability) -> io::Result { + let capset = capability.as_capability_set(private::Token).bits(); + if capset.count_ones() != 1 { + return Err(Errno::INVAL); + } + let cap = capset.trailing_zeros(); + + unsafe { + prctl_3args( + PR_CAP_AMBIENT, + PR_CAP_AMBIENT_IS_SET as *mut _, + // as *mut _ should be ptr::without_provenance_mut but our MSRV does not allow it. + cap as usize as *mut _, + ) + } + .map(|r| r != 0) } const PR_CAP_AMBIENT_CLEAR_ALL: usize = 4; @@ -636,15 +736,30 @@ const PR_CAP_AMBIENT_LOWER: usize = 3; /// /// [`prctl(PR_CAP_AMBIENT,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] -pub fn configure_capability_in_ambient_set(capability: Capability, enable: bool) -> io::Result<()> { +pub fn configure_capability_in_ambient_set( + capability: impl CompatCapability, + enable: bool, +) -> io::Result<()> { let sub_operation = if enable { PR_CAP_AMBIENT_RAISE } else { PR_CAP_AMBIENT_LOWER }; - let cap = capability as usize as *mut _; + let capset = capability.as_capability_set(private::Token).bits(); + if capset.count_ones() != 1 { + return Err(Errno::INVAL); + } + let cap = capset.trailing_zeros(); - unsafe { prctl_3args(PR_CAP_AMBIENT, sub_operation as *mut _, cap) }.map(|_r| ()) + unsafe { + prctl_3args( + PR_CAP_AMBIENT, + sub_operation as *mut _, + // as *mut _ should be ptr::without_provenance_mut but our MSRV does not allow it. + cap as usize as *mut _, + ) + } + .map(|_r| ()) } // @@ -736,6 +851,7 @@ const PR_PAC_RESET_KEYS: c_int = 54; /// /// [`prctl(PR_PAC_RESET_KEYS,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] +#[cfg(linux_raw_dep)] pub unsafe fn reset_pointer_authentication_keys( keys: Option, ) -> io::Result<()> { diff --git a/src/thread/setns.rs b/src/thread/setns.rs index 0eaee2f2a..9ec1205a9 100644 --- a/src/thread/setns.rs +++ b/src/thread/setns.rs @@ -1,3 +1,10 @@ +//! Thread-specific namespace functions. +//! +//! # Safety +//! +//! The `unshare` function can cause threads to use different file descriptor tables. +#![allow(unsafe_code)] + use bitflags::bitflags; use linux_raw_sys::general::{ CLONE_FILES, CLONE_FS, CLONE_NEWCGROUP, CLONE_NEWIPC, CLONE_NEWNET, CLONE_NEWNS, CLONE_NEWPID, @@ -127,13 +134,29 @@ pub fn move_into_thread_name_spaces( syscalls::setns(fd, allowed_types.bits() as c_int).map(|_r| ()) } +/// `unshare(flags)`—Deprecated in favor of [`unshare_unsafe`]. +/// +/// This function should be unsafe; see the safety comment on `unshare_unsafe`. +#[deprecated(since = "1.1.0", note = "Use `unshare_unsafe`")] +pub fn unshare(flags: UnshareFlags) -> io::Result<()> { + // SAFETY: This is not actually safe. This function is deprecated and users + // should use `unshare_unsafe` instead. + unsafe { syscalls::unshare(flags) } +} + /// `unshare(flags)`—Disassociate parts of the current thread's execution /// context with other threads. /// +/// # Safety +/// +/// When using `UnshareFlags::FILES`, this function can cause one thread to be +/// unable to use file descriptors created on a different thread. Callers must +/// ensure that threads never observe file descriptors from unshared tables. +/// /// # References /// - [Linux] /// /// [Linux]: https://man7.org/linux/man-pages/man2/unshare.2.html -pub fn unshare(flags: UnshareFlags) -> io::Result<()> { +pub unsafe fn unshare_unsafe(flags: UnshareFlags) -> io::Result<()> { syscalls::unshare(flags) } diff --git a/src/time/mod.rs b/src/time/mod.rs index c633e767f..8aeec874a 100644 --- a/src/time/mod.rs +++ b/src/time/mod.rs @@ -1,11 +1,23 @@ //! Time-related operations. mod clock; -#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "netbsd" +))] mod timerfd; // TODO: Convert WASI'S clock APIs to use handles rather than ambient clock // identifiers, update `wasi-libc`, and then add support in `rustix`. pub use clock::*; -#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "netbsd" +))] pub use timerfd::*; diff --git a/src/time/timerfd.rs b/src/time/timerfd.rs index d1d2a17f7..b9a8cc7f8 100644 --- a/src/time/timerfd.rs +++ b/src/time/timerfd.rs @@ -25,8 +25,14 @@ pub struct Itimerspec { /// /// # References /// - [Linux] +/// - [FreeBSD] +/// - [illumos] +/// - [NetBSD] /// /// [Linux]: https://man7.org/linux/man-pages/man2/timerfd_create.2.html +/// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=timerfd_create&sektion=2 +/// [illumos]: https://illumos.org/man/3C/timerfd_create +/// [NetBSD]: https://man.netbsd.org/timerfd_create.2 #[inline] pub fn timerfd_create(clockid: TimerfdClockId, flags: TimerfdFlags) -> io::Result { backend::time::syscalls::timerfd_create(clockid, flags) @@ -36,8 +42,14 @@ pub fn timerfd_create(clockid: TimerfdClockId, flags: TimerfdFlags) -> io::Resul /// /// # References /// - [Linux] +/// - [FreeBSD] +/// - [illumos] +/// - [NetBSD] /// /// [Linux]: https://man7.org/linux/man-pages/man2/timerfd_settime.2.html +/// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=timerfd_settime&sektion=2 +/// [illumos]: https://illumos.org/man/3C/timerfd_settime +/// [NetBSD]: https://man.netbsd.org/timerfd_settime.2 #[inline] pub fn timerfd_settime( fd: Fd, @@ -51,8 +63,14 @@ pub fn timerfd_settime( /// /// # References /// - [Linux] +/// - [FreeBSD] +/// - [illumos] +/// - [NetBSD] /// /// [Linux]: https://man7.org/linux/man-pages/man2/timerfd_gettime.2.html +/// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=timerfd_gettime&sektion=2 +/// [illumos]: https://illumos.org/man/3C/timerfd_gettime +/// [NetBSD]: https://man.netbsd.org/timerfd_gettime.2 #[inline] pub fn timerfd_gettime(fd: Fd) -> io::Result { backend::time::syscalls::timerfd_gettime(fd.as_fd()) diff --git a/src/timespec.rs b/src/timespec.rs index f5404ba42..a6458bbca 100644 --- a/src/timespec.rs +++ b/src/timespec.rs @@ -411,7 +411,7 @@ mod tests { } // Test that `Timespec` matches Linux's `__kernel_timespec`. - #[cfg(linux_kernel)] + #[cfg(linux_raw_dep)] #[test] fn test_against_kernel_timespec() { assert_eq_size!(Timespec, linux_raw_sys::general::__kernel_timespec); diff --git a/src/ugid.rs b/src/ugid.rs index 386ceb72d..11edb19b3 100644 --- a/src/ugid.rs +++ b/src/ugid.rs @@ -1,5 +1,7 @@ //! User and Group ID types. +use core::fmt; + use crate::backend::c; use crate::ffi; @@ -86,6 +88,78 @@ impl Gid { } } +impl fmt::Display for Uid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} +impl fmt::Binary for Uid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} +impl fmt::Octal for Uid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} +impl fmt::LowerHex for Uid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} +impl fmt::UpperHex for Uid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} +impl fmt::LowerExp for Uid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} +impl fmt::UpperExp for Uid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl fmt::Display for Gid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} +impl fmt::Binary for Gid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} +impl fmt::Octal for Gid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} +impl fmt::LowerHex for Gid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} +impl fmt::UpperHex for Gid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} +impl fmt::LowerExp for Gid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} +impl fmt::UpperExp for Gid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + // Return the raw value of the IDs. In case of `None` it returns `!0` since it // has the same bit pattern as `-1` indicating no change to the owner/group ID. pub(crate) fn translate_fchown_args( diff --git a/src/weak.rs b/src/weak.rs index 036ddeea4..b17e4b1ce 100644 --- a/src/weak.rs +++ b/src/weak.rs @@ -146,8 +146,8 @@ unsafe fn fetch(name: &str) -> *mut c_void { #[cfg(not(linux_kernel))] macro_rules! syscall { - (fn $name:ident($($arg_name:ident: $t:ty),*) via $_sys_name:ident -> $ret:ty) => ( - unsafe fn $name($($arg_name: $t),*) -> $ret { + ($vis:vis fn $name:ident($($arg_name:ident: $t:ty),*) via $_sys_name:ident -> $ret:ty) => ( + $vis unsafe fn $name($($arg_name: $t),*) -> $ret { weak! { fn $name($($t),*) -> $ret } if let Some(fun) = $name.get() { @@ -162,8 +162,8 @@ macro_rules! syscall { #[cfg(linux_kernel)] macro_rules! syscall { - (fn $name:ident($($arg_name:ident: $t:ty),*) via $sys_name:ident -> $ret:ty) => ( - unsafe fn $name($($arg_name:$t),*) -> $ret { + ($vis:vis fn $name:ident($($arg_name:ident: $t:ty),*) via $sys_name:ident -> $ret:ty) => ( + $vis unsafe fn $name($($arg_name:$t),*) -> $ret { // This looks like a hack, but `concat_idents` only accepts idents // (not paths). use libc::*; diff --git a/tests/backends.rs b/tests/backends.rs index 13daec687..3ded514ff 100644 --- a/tests/backends.rs +++ b/tests/backends.rs @@ -2,9 +2,10 @@ //! various configurations, including backend configurations, and tests that //! they behave as expected. -use std::process::Command; +use std::process::{Command, Stdio}; #[test] +#[ignore] // TODO: re-enable until tempfile is updated fn test_backends() { // Pick an arbitrary platform where linux_raw is enabled by default and // ensure that the use-default crate uses it. @@ -142,7 +143,8 @@ fn has_dependency( .arg("--quiet") .arg("--edges=normal") .arg(format!("--invert={}", dependency)) - .current_dir(dir); + .current_dir(dir) + .stderr(Stdio::inherit()); command.args(args); for (key, value) in envs { diff --git a/tests/event/select.rs b/tests/event/select.rs index 6bdec54a0..13b502a34 100644 --- a/tests/event/select.rs +++ b/tests/event/select.rs @@ -5,6 +5,7 @@ use rustix::event::{ use rustix::fd::{AsRawFd as _, RawFd}; #[cfg(feature = "pipe")] #[cfg(not(windows))] +#[allow(unused_imports)] use rustix::fd::{FromRawFd as _, OwnedFd}; use rustix::io::retry_on_intr; use serial_test::serial; diff --git a/tests/fs/file.rs b/tests/fs/file.rs index 0dc497fdf..cb7e0c7fe 100644 --- a/tests/fs/file.rs +++ b/tests/fs/file.rs @@ -152,7 +152,7 @@ fn test_file() { assert!(statfs.f_blocks > 0); } - #[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))] + #[cfg(not(any(target_os = "redox", target_os = "wasi")))] { let statvfs = rustix::fs::fstatvfs(&file).unwrap(); assert!(statvfs.f_frsize > 0); diff --git a/tests/fs/flock.rs b/tests/fs/flock.rs index c90da58fb..537059492 100644 --- a/tests/fs/flock.rs +++ b/tests/fs/flock.rs @@ -3,29 +3,30 @@ fn test_flock() { use rustix::fs::{flock, openat, FlockOperation, Mode, OFlags, CWD}; - let f = openat(CWD, "Cargo.toml", OFlags::RDONLY, Mode::empty()).unwrap(); + let tf = tempfile::NamedTempFile::new().unwrap(); + let f = openat(CWD, tf.path(), OFlags::RDONLY, Mode::empty()).unwrap(); flock(&f, FlockOperation::LockExclusive).unwrap(); flock(&f, FlockOperation::Unlock).unwrap(); - let g = openat(CWD, "Cargo.toml", OFlags::RDONLY, Mode::empty()).unwrap(); + let g = openat(CWD, tf.path(), OFlags::RDONLY, Mode::empty()).unwrap(); flock(&g, FlockOperation::LockExclusive).unwrap(); flock(&g, FlockOperation::Unlock).unwrap(); drop(f); drop(g); - let f = openat(CWD, "Cargo.toml", OFlags::RDONLY, Mode::empty()).unwrap(); + let f = openat(CWD, tf.path(), OFlags::RDONLY, Mode::empty()).unwrap(); flock(&f, FlockOperation::LockShared).unwrap(); - let g = openat(CWD, "Cargo.toml", OFlags::RDONLY, Mode::empty()).unwrap(); + let g = openat(CWD, tf.path(), OFlags::RDONLY, Mode::empty()).unwrap(); flock(&g, FlockOperation::LockShared).unwrap(); flock(&f, FlockOperation::Unlock).unwrap(); flock(&g, FlockOperation::Unlock).unwrap(); drop(f); drop(g); - let f = openat(CWD, "Cargo.toml", OFlags::RDONLY, Mode::empty()).unwrap(); + let f = openat(CWD, tf.path(), OFlags::RDONLY, Mode::empty()).unwrap(); flock(&f, FlockOperation::LockShared).unwrap(); flock(&f, FlockOperation::LockExclusive).unwrap(); flock(&f, FlockOperation::Unlock).unwrap(); - let g = openat(CWD, "Cargo.toml", OFlags::RDONLY, Mode::empty()).unwrap(); + let g = openat(CWD, tf.path(), OFlags::RDONLY, Mode::empty()).unwrap(); flock(&g, FlockOperation::LockShared).unwrap(); flock(&g, FlockOperation::LockExclusive).unwrap(); flock(&g, FlockOperation::Unlock).unwrap(); diff --git a/tests/fs/main.rs b/tests/fs/main.rs index e2f810824..84ac2aef3 100644 --- a/tests/fs/main.rs +++ b/tests/fs/main.rs @@ -20,21 +20,21 @@ mod file; #[cfg(not(target_os = "wasi"))] mod flock; mod futimens; -#[cfg(linux_kernel)] +#[cfg(linux_raw_dep)] mod inotify; mod invalid_offset; #[cfg(not(target_os = "redox"))] mod ioctl; mod linkat; mod long_paths; -#[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))] +#[cfg(not(any(target_os = "haiku", target_os = "wasi")))] mod makedev; mod mkdirat; mod mknodat; mod negative_timestamp; #[cfg(linux_kernel)] mod openat; -#[cfg(linux_kernel)] +#[cfg(linux_raw_dep)] mod openat2; #[cfg(not(target_os = "redox"))] mod readdir; diff --git a/tests/fs/statx.rs b/tests/fs/statx.rs index 05a7571d4..2fd0fcb0d 100644 --- a/tests/fs/statx.rs +++ b/tests/fs/statx.rs @@ -7,7 +7,11 @@ fn test_statx_unknown_flags() { // It's ok (though still unwise) to construct flags values that have // unknown bits. Exclude `STATX__RESERVED` here as that evokes an explicit // failure; that's tested separately below. - let too_many_flags = StatxFlags::from_bits_retain(!linux_raw_sys::general::STATX__RESERVED); + #[cfg(not(linux_raw_dep))] + const STATX__RESERVED: u32 = libc::STATX__RESERVED as u32; + #[cfg(linux_raw_dep)] + const STATX__RESERVED: u32 = linux_raw_sys::general::STATX__RESERVED; + let too_many_flags = StatxFlags::from_bits_retain(!STATX__RESERVED); // It's also ok to pass such flags to `statx`. let result = match rustix::fs::statx(&f, "Cargo.toml", AtFlags::empty(), too_many_flags) { @@ -31,7 +35,11 @@ fn test_statx_reserved() { // It's ok (though still unwise) to construct a `STATX__RESERVED` flag // value but `statx` should reliably fail with `INVAL`. - let reserved = StatxFlags::from_bits_retain(linux_raw_sys::general::STATX__RESERVED); + #[cfg(not(linux_raw_dep))] + const STATX__RESERVED: u32 = libc::STATX__RESERVED as u32; + #[cfg(linux_raw_dep)] + const STATX__RESERVED: u32 = linux_raw_sys::general::STATX__RESERVED; + let reserved = StatxFlags::from_bits_retain(STATX__RESERVED); match rustix::fs::statx(&f, "Cargo.toml", AtFlags::empty(), reserved) { Ok(_) => panic!("statx succeeded with `STATX__RESERVED`"), Err(err) => assert_eq!(err, rustix::io::Errno::INVAL), diff --git a/tests/fs/xattr.rs b/tests/fs/xattr.rs index 936ec7437..ba2ff17aa 100644 --- a/tests/fs/xattr.rs +++ b/tests/fs/xattr.rs @@ -85,8 +85,14 @@ fn xattr_basic() { .raw_os_error(), enodata ); - assert_eq!(rustix::fs::listxattr("Cargo.toml", &mut empty).unwrap(), 0); - assert_eq!(rustix::fs::llistxattr("Cargo.toml", &mut empty).unwrap(), 0); + assert_eq!( + rustix::fs::listxattr("Cargo.toml", &mut empty).unwrap(), + libc_listxattr("Cargo.toml") + ); + assert_eq!( + rustix::fs::llistxattr("Cargo.toml", &mut empty).unwrap(), + libc_listxattr("Cargo.toml") + ); assert_eq!( rustix::fs::removexattr("Cargo.toml", "user.test") .unwrap_err() @@ -113,7 +119,10 @@ fn xattr_basic() { .raw_os_error(), enodata ); - assert_eq!(rustix::fs::flistxattr(&file, &mut empty).unwrap(), 0); + assert_eq!( + rustix::fs::flistxattr(&file, &mut empty).unwrap(), + libc_listxattr("Cargo.toml") + ); assert_eq!( rustix::fs::fremovexattr(&file, "user.test") .unwrap_err() @@ -121,3 +130,25 @@ fn xattr_basic() { enodata ); } + +/// To check the correctness of the tested implementations of *listxattr(), their output can be +/// compared to an external implementation, in this case listxattr() from the libc crate. +fn libc_listxattr(path: &str) -> usize { + let path = std::ffi::CString::new(path).unwrap(); + let path: *const _ = path.as_ptr(); + + let list = std::ffi::CString::new("").unwrap(); + let list = list.as_ptr() as *mut _; + + ({ + #[cfg(not(apple))] + unsafe { + libc::listxattr(path, list, 0) + } + + #[cfg(apple)] + unsafe { + libc::listxattr(path, list, 0, 0) + } + }) as usize +} diff --git a/tests/io/ioctl.rs b/tests/io/ioctl.rs index 721d5e09b..77b0a76f0 100644 --- a/tests/io/ioctl.rs +++ b/tests/io/ioctl.rs @@ -1,5 +1,5 @@ // `ioctl_fionread` on Windows doesn't work on files. -#[cfg(not(any(windows, target_os = "cygwin")))] +#[cfg(not(any(windows, target_os = "cygwin", target_os = "haiku")))] #[test] fn test_ioctls() { let file = std::fs::File::open("Cargo.toml").unwrap(); diff --git a/tests/io/read_write.rs b/tests/io/read_write.rs index e264c997a..be3b047d5 100644 --- a/tests/io/read_write.rs +++ b/tests/io/read_write.rs @@ -202,7 +202,7 @@ fn test_rwf_values() { ); } -#[cfg(linux_kernel)] +#[cfg(all(linux_raw_dep, not(target_os = "android")))] #[cfg(feature = "fs")] #[test] fn test_pwritev2() { @@ -256,7 +256,7 @@ fn test_pwritev2() { assert_eq!(&buf, b"world"); } -#[cfg(linux_kernel)] +#[cfg(all(linux_raw_dep, not(target_os = "android")))] #[cfg(all(feature = "net", feature = "pipe"))] #[test] fn test_preadv2_nowait() { @@ -308,7 +308,7 @@ fn test_preadv2_nowait() { fn test_p_offsets() { use rustix::fs::{openat, Mode, OFlags, CWD}; use rustix::io::{pread, preadv, pwrite, pwritev}; - #[cfg(linux_kernel)] + #[cfg(all(linux_raw_dep, not(target_os = "android")))] use rustix::io::{preadv2, pwritev2, ReadWriteFlags}; let mut buf = [0_u8; 5]; @@ -343,7 +343,7 @@ fn test_p_offsets() { Ok(_) => {} Err(e) => panic!("pwritev failed with an unexpected error: {:?}", e), } - #[cfg(linux_kernel)] + #[cfg(all(linux_raw_dep, not(target_os = "android")))] { match preadv2( &f, @@ -393,7 +393,7 @@ fn test_p_offsets() { Err(e) => panic!("pwritev failed with an unexpected error: {:?}", e), } } - #[cfg(linux_kernel)] + #[cfg(all(linux_raw_dep, not(target_os = "android")))] { match preadv2( &f, diff --git a/tests/io_uring/register.rs b/tests/io_uring/register.rs index 71e779570..02d6287aa 100644 --- a/tests/io_uring/register.rs +++ b/tests/io_uring/register.rs @@ -15,7 +15,7 @@ fn do_register( fd: FD, registered_fd: bool, opcode: IoringRegisterOp, - arg: *const c_void, + arg: *mut c_void, arg_nr: u32, ) -> Result<()> where @@ -27,8 +27,10 @@ where IoringRegisterFlags::default() }; + // Cast `arg` to `*const c_void` to match the current API. See + // . unsafe { - io_uring_register_with(fd, opcode, flags, arg, arg_nr)?; + io_uring_register_with(fd, opcode, flags, arg as *const c_void, arg_nr)?; } Ok(()) @@ -43,7 +45,7 @@ fn register_ring(fd: BorrowedFd<'_>) -> Result> { fd, false, IoringRegisterOp::RegisterRingFds, - (&update as *const io_uring_rsrc_update).cast::(), + (&mut update as *mut io_uring_rsrc_update).cast::(), 1, )?; @@ -63,7 +65,7 @@ where fd, true, IoringRegisterOp::UnregisterRingFds, - (&update as *const io_uring_rsrc_update).cast::(), + (&update as *const io_uring_rsrc_update as *mut io_uring_rsrc_update).cast::(), 1, )?; @@ -81,7 +83,7 @@ where fd, true, IoringRegisterOp::RegisterIowqMaxWorkers, - (&iowq_max_workers as *const [u32; 2]).cast::(), + (&iowq_max_workers as *const [u32; 2] as *mut [u32; 2]).cast::(), 2, )?; @@ -96,7 +98,7 @@ where fd, false, IoringRegisterOp::RegisterPbufRing, - (reg as *const io_uring_buf_reg).cast::(), + (reg as *const io_uring_buf_reg as *mut io_uring_buf_reg).cast::(), 1, ) } diff --git a/tests/net/addr.rs b/tests/net/addr.rs index 4abe2a4dd..8961221b8 100644 --- a/tests/net/addr.rs +++ b/tests/net/addr.rs @@ -28,22 +28,23 @@ fn encode_decode() { fn test_unix_addr() { use rustix::cstr; use rustix::net::SocketAddrUnix; + use std::borrow::Cow; assert_eq!( SocketAddrUnix::new("/").unwrap().path().unwrap(), - cstr!("/").into() + Cow::from(cstr!("/")) ); assert_eq!( SocketAddrUnix::new("//").unwrap().path().unwrap(), - cstr!("//").into() + Cow::from(cstr!("//")) ); assert_eq!( SocketAddrUnix::new("/foo/bar").unwrap().path().unwrap(), - cstr!("/foo/bar").into() + Cow::from(cstr!("/foo/bar")) ); assert_eq!( SocketAddrUnix::new("foo").unwrap().path().unwrap(), - cstr!("foo").into() + Cow::from(cstr!("foo")) ); SocketAddrUnix::new("/foo\0/bar").unwrap_err(); assert!(SocketAddrUnix::new("").unwrap().path().is_none()); diff --git a/tests/net/sockopt.rs b/tests/net/sockopt.rs index 93cb34093..d139d9a5c 100644 --- a/tests/net/sockopt.rs +++ b/tests/net/sockopt.rs @@ -9,7 +9,11 @@ use rustix::io; target_env = "newlib" ))] use rustix::net::ipproto; +#[cfg(all(target_os = "linux", feature = "time"))] +use rustix::net::TxTimeFlags; use rustix::net::{sockopt, AddressFamily, SocketType}; +#[cfg(all(target_os = "linux", feature = "time"))] +use rustix::time::ClockId; use std::net::Ipv4Addr; use std::time::Duration; @@ -36,8 +40,6 @@ fn test_sockopts_socket(s: &OwnedFd) { assert!(!sockopt::socket_broadcast(s).unwrap()); // On a new socket we shouldn't have a linger yet. assert!(sockopt::socket_linger(s).unwrap().is_none()); - #[cfg(linux_kernel)] - assert!(!sockopt::socket_passcred(s).unwrap()); // On a new socket we shouldn't have an error yet. assert_eq!(sockopt::socket_error(s).unwrap(), Ok(())); @@ -115,15 +117,6 @@ fn test_sockopts_socket(s: &OwnedFd) { // Check that we have a linger of at least the time we set. assert!(sockopt::socket_linger(s).unwrap().unwrap() >= Duration::new(1, 1)); - #[cfg(linux_kernel)] - { - // Set the passcred flag; - sockopt::set_socket_passcred(s, true).unwrap(); - - // Check that the passcred flag is set. - assert!(sockopt::socket_passcred(s).unwrap()); - } - // Set the receive buffer size. let size = sockopt::socket_recv_buffer_size(s).unwrap(); sockopt::set_socket_recv_buffer_size(s, size * 2).unwrap(); @@ -314,6 +307,7 @@ fn test_sockopts_ipv4() { #[cfg(not(any( apple, windows, + target_os = "aix", target_os = "cygwin", target_os = "dragonfly", target_os = "emscripten", @@ -522,6 +516,18 @@ fn test_sockopts_ipv6() { test_sockopts_tcp(&s); } +#[cfg(linux_kernel)] +#[test] +fn test_socket_passcred() { + crate::init(); + + let s = rustix::net::socket(AddressFamily::UNIX, SocketType::STREAM, None).unwrap(); + + assert_eq!(sockopt::socket_passcred(&s), Ok(false)); + sockopt::set_socket_passcred(&s, true).unwrap(); + assert_eq!(sockopt::socket_passcred(&s), Ok(true)); +} + #[cfg(any(linux_kernel, target_os = "cygwin"))] #[test] fn test_socketopts_ip_mtu() { @@ -565,6 +571,34 @@ fn test_socketopts_ipv6_mtu() { } } +#[cfg(linux_kernel)] +#[test] +fn test_ip_mtu_discover() { + crate::init(); + + // IPv4 + { + use sockopt::Ipv4PathMtuDiscovery as P; + + let s = rustix::net::socket(AddressFamily::INET, SocketType::DGRAM, None).unwrap(); + for val in [P::DONT, P::WANT, P::DO, P::PROBE, P::INTERFACE, P::OMIT] { + sockopt::set_ip_mtu_discover(&s, val).unwrap(); + assert_eq!(sockopt::ip_mtu_discover(&s), Ok(val)); + } + } + + // IPv6 + { + use sockopt::Ipv6PathMtuDiscovery as P; + + let s = rustix::net::socket(AddressFamily::INET6, SocketType::DGRAM, None).unwrap(); + for val in [P::DONT, P::WANT, P::DO, P::PROBE, P::INTERFACE, P::OMIT] { + sockopt::set_ipv6_mtu_discover(&s, val).unwrap(); + assert_eq!(sockopt::ipv6_mtu_discover(&s), Ok(val)); + } + } +} + #[test] fn test_sockopts_multicast_ifv4() { crate::init(); @@ -633,3 +667,24 @@ fn test_sockopts_multicast_ifv6() { Err(e) => panic!("{e}"), } } + +#[test] +#[cfg(all(target_os = "linux", feature = "time"))] +fn test_sockopts_txtime() { + crate::init(); + + let s = rustix::net::socket(AddressFamily::INET, SocketType::DGRAM, None).unwrap(); + + match sockopt::set_txtime(&s, ClockId::Monotonic, TxTimeFlags::DEADLINE_MODE) { + Ok(()) => { + assert_eq!( + sockopt::get_txtime(&s).unwrap(), + (ClockId::Monotonic, TxTimeFlags::DEADLINE_MODE) + ); + } + Err(e) if e.to_string().contains("Protocol not available") => { + // Skip test on unsupported platforms + } + Err(e) => panic!("{e}"), + } +} diff --git a/tests/net/unix.rs b/tests/net/unix.rs index e1341aa50..57f52f0f8 100644 --- a/tests/net/unix.rs +++ b/tests/net/unix.rs @@ -6,6 +6,9 @@ // macOS. #![cfg(not(any(apple, target_os = "espidf", target_os = "redox", target_os = "wasi")))] #![cfg(feature = "fs")] +#![allow(unused_imports)] +#![allow(dead_code)] +#![allow(unused_variables)] use rustix::fs::{unlinkat, AtFlags, CWD}; use rustix::io::{read, write}; @@ -92,6 +95,7 @@ fn client(ready: Arc<(Mutex, Condvar)>, path: &Path, runs: &[(&[&str], i32 } #[test] +#[cfg(not(target_os = "freebsd"))] // TODO: Investigate why these tests fail on FreeBSD. fn test_unix() { crate::init(); @@ -128,6 +132,7 @@ fn test_unix() { } #[cfg(not(any(target_os = "espidf", target_os = "redox", target_os = "wasi")))] +#[cfg(not(target_os = "freebsd"))] // TODO: Investigate why these tests fail on FreeBSD. fn do_test_unix_msg(addr: SocketAddrUnix) { use rustix::io::{IoSlice, IoSliceMut}; use rustix::net::{recvmsg, sendmsg, RecvFlags, ReturnFlags, SendFlags}; @@ -362,6 +367,7 @@ fn do_test_unix_msg_unconnected(addr: SocketAddrUnix) { } #[cfg(not(any(target_os = "espidf", target_os = "redox", target_os = "wasi")))] +#[cfg(not(target_os = "freebsd"))] // TODO: Investigate why these tests fail on FreeBSD. #[test] fn test_unix_msg() { use rustix::ffi::CString; @@ -445,6 +451,7 @@ fn test_abstract_unix_msg_unconnected() { #[cfg(not(any(target_os = "redox", target_os = "wasi")))] #[cfg(feature = "pipe")] +#[cfg(not(target_os = "freebsd"))] // TODO: Investigate why these tests fail on FreeBSD. #[test] fn test_unix_msg_with_scm_rights() { crate::init(); @@ -741,6 +748,7 @@ fn test_unix_peercred_implicit() { /// over multiple control messages. #[cfg(not(any(target_os = "redox", target_os = "wasi")))] #[cfg(feature = "pipe")] +#[cfg(not(target_os = "freebsd"))] // TODO: Investigate why these tests fail on FreeBSD. #[test] fn test_unix_msg_with_combo() { crate::init(); @@ -984,7 +992,7 @@ fn test_long_named_address() { let lens = [ span_of!(libc::sockaddr_un, sun_path).len(), - #[cfg(linux_kernel)] + #[cfg(linux_raw_dep)] span_of!(linux_raw_sys::net::sockaddr_un, sun_path).len(), ]; diff --git a/tests/net/unix_alloc.rs b/tests/net/unix_alloc.rs index 3e0774e7d..49a5f854d 100644 --- a/tests/net/unix_alloc.rs +++ b/tests/net/unix_alloc.rs @@ -4,6 +4,9 @@ // macOS. #![cfg(not(any(apple, target_os = "espidf", target_os = "redox", target_os = "wasi")))] #![cfg(feature = "fs")] +#![allow(unused_imports)] +#![allow(dead_code)] +#![allow(unused_variables)] use rustix::fs::{unlinkat, AtFlags, CWD}; use rustix::io::{read, write}; @@ -89,6 +92,7 @@ fn client(ready: Arc<(Mutex, Condvar)>, path: &Path, runs: &[(&[&str], i32 } #[test] +#[cfg(not(target_os = "freebsd"))] // TODO: Investigate why these tests fail on FreeBSD. fn test_unix() { crate::init(); @@ -125,6 +129,7 @@ fn test_unix() { } #[cfg(not(any(target_os = "espidf", target_os = "redox", target_os = "wasi")))] +#[cfg(not(target_os = "freebsd"))] // TODO: Investigate why these tests fail on FreeBSD. fn do_test_unix_msg(addr: SocketAddrUnix) { use rustix::io::{IoSlice, IoSliceMut}; use rustix::net::{recvmsg, sendmsg, RecvFlags, ReturnFlags, SendFlags}; @@ -359,6 +364,7 @@ fn do_test_unix_msg_unconnected(addr: SocketAddrUnix) { } #[cfg(not(any(target_os = "espidf", target_os = "redox", target_os = "wasi")))] +#[cfg(not(target_os = "freebsd"))] // TODO: Investigate why these tests fail on FreeBSD. #[test] fn test_unix_msg() { use rustix::ffi::CString; @@ -442,6 +448,7 @@ fn test_abstract_unix_msg_unconnected() { #[cfg(not(any(target_os = "redox", target_os = "wasi")))] #[cfg(feature = "pipe")] +#[cfg(not(target_os = "freebsd"))] // TODO: Investigate why these tests fail on FreeBSD. #[test] fn test_unix_msg_with_scm_rights() { crate::init(); @@ -681,6 +688,7 @@ fn test_unix_peercred() { /// over multiple control messages. #[cfg(not(any(target_os = "redox", target_os = "wasi")))] #[cfg(feature = "pipe")] +#[cfg(not(target_os = "freebsd"))] // TODO: Investigate why these tests fail on FreeBSD. #[test] fn test_unix_msg_with_combo() { crate::init(); diff --git a/tests/net/v4.rs b/tests/net/v4.rs index bb11a5293..0f87ea459 100644 --- a/tests/net/v4.rs +++ b/tests/net/v4.rs @@ -287,3 +287,41 @@ fn test_v4_sendmmsg() { client.join().unwrap(); server.join().unwrap(); } + +#[test] +#[cfg(all(target_os = "linux", feature = "time"))] +fn test_v4_txtime() { + crate::init(); + + use std::mem::MaybeUninit; + use std::time; + + use rustix::io::IoSlice; + use rustix::net::{sendmsg, sockopt, SendAncillaryBuffer, SendAncillaryMessage, TxTimeFlags}; + use rustix::time::ClockId; + + let data_socket = socket(AddressFamily::INET, SocketType::DGRAM, None).unwrap(); + let addr = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 12345); + connect(&data_socket, &addr).unwrap(); + + match sockopt::set_txtime(&data_socket, ClockId::Monotonic, TxTimeFlags::empty()) { + Ok(_) => (), + // Skip on unsupported platforms. + Err(e) if e.to_string().contains("Protocol not available") => return, + Err(e) => panic!("{e}"), + } + + let mut space = [MaybeUninit::uninit(); rustix::cmsg_space!(TxTime(1))]; + let mut cmsg_buffer = SendAncillaryBuffer::new(&mut space); + + let t = time::UNIX_EPOCH.elapsed().unwrap() + time::Duration::from_millis(100); + cmsg_buffer.push(SendAncillaryMessage::TxTime(t.as_nanos() as u64)); + + sendmsg( + &data_socket, + &[IoSlice::new(b"hello, world")], + &mut cmsg_buffer, + SendFlags::empty(), + ) + .unwrap(); +} diff --git a/tests/net/v6.rs b/tests/net/v6.rs index 4016c18ad..b42477e7f 100644 --- a/tests/net/v6.rs +++ b/tests/net/v6.rs @@ -286,3 +286,41 @@ fn test_v6_sendmmsg() { client.join().unwrap(); server.join().unwrap(); } + +#[test] +#[cfg(all(target_os = "linux", feature = "time"))] +fn test_v6_txtime() { + crate::init(); + + use std::mem::MaybeUninit; + use std::time; + + use rustix::io::IoSlice; + use rustix::net::{sendmsg, sockopt, SendAncillaryBuffer, SendAncillaryMessage, TxTimeFlags}; + use rustix::time::ClockId; + + let data_socket = socket(AddressFamily::INET6, SocketType::DGRAM, None).unwrap(); + let addr = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 12345, 0, 0); + connect(&data_socket, &addr).unwrap(); + + match sockopt::set_txtime(&data_socket, ClockId::Monotonic, TxTimeFlags::empty()) { + Ok(_) => (), + // Skip on unsupported platforms. + Err(e) if e.to_string().contains("Protocol not available") => return, + Err(e) => panic!("{e}"), + } + + let mut space = [MaybeUninit::uninit(); rustix::cmsg_space!(TxTime(1))]; + let mut cmsg_buffer = SendAncillaryBuffer::new(&mut space); + + let t = time::UNIX_EPOCH.elapsed().unwrap() + time::Duration::from_millis(100); + cmsg_buffer.push(SendAncillaryMessage::TxTime(t.as_nanos() as u64)); + + sendmsg( + &data_socket, + &[IoSlice::new(b"hello, world")], + &mut cmsg_buffer, + SendFlags::empty(), + ) + .unwrap(); +} diff --git a/tests/path/arg.rs b/tests/path/arg.rs index f1bfd7536..8222e2612 100644 --- a/tests/path/arg.rs +++ b/tests/path/arg.rs @@ -16,126 +16,243 @@ fn test_arg() { let t: &str = "hello"; assert_eq!("hello", Arg::as_str(&t).unwrap()); assert_eq!("hello".to_owned(), Arg::to_string_lossy(&t)); - assert_eq!(cstr!("hello"), Borrow::borrow(&t.as_cow_c_str().unwrap())); - assert_eq!(cstr!("hello"), Borrow::borrow(&t.into_c_str().unwrap())); + assert_eq!( + cstr!("hello"), + Borrow::::borrow(&t.as_cow_c_str().unwrap()) + ); + assert_eq!( + cstr!("hello"), + Borrow::::borrow(&t.into_c_str().unwrap()) + ); let t: String = "hello".to_owned(); assert_eq!("hello", Arg::as_str(&t).unwrap()); assert_eq!("hello".to_owned(), Arg::to_string_lossy(&t)); - assert_eq!(cstr!("hello"), Borrow::borrow(&t.as_cow_c_str().unwrap())); - assert_eq!(cstr!("hello"), Borrow::borrow(&t.into_c_str().unwrap())); + assert_eq!( + cstr!("hello"), + Borrow::::borrow(&t.as_cow_c_str().unwrap()) + ); + assert_eq!( + cstr!("hello"), + Borrow::::borrow(&t.into_c_str().unwrap()) + ); let t: &OsStr = OsStr::new("hello"); assert_eq!("hello", t.as_str().unwrap()); assert_eq!("hello".to_owned(), Arg::to_string_lossy(&t)); - assert_eq!(cstr!("hello"), Borrow::borrow(&t.as_cow_c_str().unwrap())); - assert_eq!(cstr!("hello"), Borrow::borrow(&t.into_c_str().unwrap())); + assert_eq!( + cstr!("hello"), + Borrow::::borrow(&t.as_cow_c_str().unwrap()) + ); + assert_eq!( + cstr!("hello"), + Borrow::::borrow(&t.into_c_str().unwrap()) + ); let t: OsString = OsString::from("hello".to_owned()); assert_eq!("hello", t.as_str().unwrap()); assert_eq!("hello".to_owned(), Arg::to_string_lossy(&t)); - assert_eq!(cstr!("hello"), Borrow::borrow(&t.as_cow_c_str().unwrap())); - assert_eq!(cstr!("hello"), Borrow::borrow(&t.into_c_str().unwrap())); + assert_eq!( + cstr!("hello"), + Borrow::::borrow(&t.as_cow_c_str().unwrap()) + ); + assert_eq!( + cstr!("hello"), + Borrow::::borrow(&t.into_c_str().unwrap()) + ); let t: &Path = Path::new("hello"); assert_eq!("hello", t.as_str().unwrap()); assert_eq!("hello".to_owned(), Arg::to_string_lossy(&t)); - assert_eq!(cstr!("hello"), Borrow::borrow(&t.as_cow_c_str().unwrap())); - assert_eq!(cstr!("hello"), Borrow::borrow(&t.into_c_str().unwrap())); + assert_eq!( + cstr!("hello"), + Borrow::::borrow(&t.as_cow_c_str().unwrap()) + ); + assert_eq!( + cstr!("hello"), + Borrow::::borrow(&t.into_c_str().unwrap()) + ); let t: PathBuf = PathBuf::from("hello".to_owned()); assert_eq!("hello", t.as_str().unwrap()); assert_eq!("hello".to_owned(), Arg::to_string_lossy(&t)); - assert_eq!(cstr!("hello"), Borrow::borrow(&t.as_cow_c_str().unwrap())); - assert_eq!(cstr!("hello"), Borrow::borrow(&t.into_c_str().unwrap())); + assert_eq!( + cstr!("hello"), + Borrow::::borrow(&t.as_cow_c_str().unwrap()) + ); + assert_eq!( + cstr!("hello"), + Borrow::::borrow(&t.into_c_str().unwrap()) + ); let t: &CStr = cstr!("hello"); assert_eq!("hello", t.as_str().unwrap()); assert_eq!("hello".to_owned(), Arg::to_string_lossy(&t)); - assert_eq!(cstr!("hello"), Borrow::borrow(&t.as_cow_c_str().unwrap())); - assert_eq!(cstr!("hello"), Borrow::borrow(&t.into_c_str().unwrap())); + assert_eq!( + cstr!("hello"), + Borrow::::borrow(&t.as_cow_c_str().unwrap()) + ); + assert_eq!( + cstr!("hello"), + Borrow::::borrow(&t.into_c_str().unwrap()) + ); let t: CString = cstr!("hello").to_owned(); assert_eq!("hello", t.as_str().unwrap()); assert_eq!("hello".to_owned(), Arg::to_string_lossy(&t)); assert_eq!( cstr!("hello"), - Borrow::borrow(&Arg::as_cow_c_str(&t).unwrap()) + Borrow::::borrow(&Arg::as_cow_c_str(&t).unwrap()) + ); + assert_eq!( + cstr!("hello"), + Borrow::::borrow(&t.into_c_str().unwrap()) ); - assert_eq!(cstr!("hello"), Borrow::borrow(&t.into_c_str().unwrap())); let t: Components<'_> = Path::new("hello").components(); assert_eq!("hello", t.as_str().unwrap()); assert_eq!("hello".to_owned(), Arg::to_string_lossy(&t)); - assert_eq!(cstr!("hello"), Borrow::borrow(&t.as_cow_c_str().unwrap())); - assert_eq!(cstr!("hello"), Borrow::borrow(&t.into_c_str().unwrap())); + assert_eq!( + cstr!("hello"), + Borrow::::borrow(&t.as_cow_c_str().unwrap()) + ); + assert_eq!( + cstr!("hello"), + Borrow::::borrow(&t.into_c_str().unwrap()) + ); let t: Component<'_> = Path::new("hello").components().next().unwrap(); assert_eq!("hello", t.as_str().unwrap()); assert_eq!("hello".to_owned(), Arg::to_string_lossy(&t)); - assert_eq!(cstr!("hello"), Borrow::borrow(&t.as_cow_c_str().unwrap())); - assert_eq!(cstr!("hello"), Borrow::borrow(&t.into_c_str().unwrap())); + assert_eq!( + cstr!("hello"), + Borrow::::borrow(&t.as_cow_c_str().unwrap()) + ); + assert_eq!( + cstr!("hello"), + Borrow::::borrow(&t.into_c_str().unwrap()) + ); let t: Iter<'_> = Path::new("hello").iter(); assert_eq!("hello", t.as_str().unwrap()); assert_eq!("hello".to_owned(), Arg::to_string_lossy(&t)); - assert_eq!(cstr!("hello"), Borrow::borrow(&t.as_cow_c_str().unwrap())); - assert_eq!(cstr!("hello"), Borrow::borrow(&t.into_c_str().unwrap())); + assert_eq!( + cstr!("hello"), + Borrow::::borrow(&t.as_cow_c_str().unwrap()) + ); + assert_eq!( + cstr!("hello"), + Borrow::::borrow(&t.into_c_str().unwrap()) + ); let t: Cow<'_, str> = Cow::Borrowed("hello"); assert_eq!("hello", t.as_str().unwrap()); assert_eq!("hello".to_owned(), Arg::to_string_lossy(&t)); - assert_eq!(cstr!("hello"), Borrow::borrow(&t.as_cow_c_str().unwrap())); - assert_eq!(cstr!("hello"), Borrow::borrow(&t.into_c_str().unwrap())); + assert_eq!( + cstr!("hello"), + Borrow::::borrow(&t.as_cow_c_str().unwrap()) + ); + assert_eq!( + cstr!("hello"), + Borrow::::borrow(&t.into_c_str().unwrap()) + ); let t: Cow<'_, str> = Cow::Owned("hello".to_owned()); assert_eq!("hello", t.as_str().unwrap()); assert_eq!("hello".to_owned(), Arg::to_string_lossy(&t)); - assert_eq!(cstr!("hello"), Borrow::borrow(&t.as_cow_c_str().unwrap())); - assert_eq!(cstr!("hello"), Borrow::borrow(&t.into_c_str().unwrap())); + assert_eq!( + cstr!("hello"), + Borrow::::borrow(&t.as_cow_c_str().unwrap()) + ); + assert_eq!( + cstr!("hello"), + Borrow::::borrow(&t.into_c_str().unwrap()) + ); let t: Cow<'_, OsStr> = Cow::Borrowed(OsStr::new("hello")); assert_eq!("hello", t.as_str().unwrap()); assert_eq!("hello".to_owned(), Arg::to_string_lossy(&t)); - assert_eq!(cstr!("hello"), Borrow::borrow(&t.as_cow_c_str().unwrap())); - assert_eq!(cstr!("hello"), Borrow::borrow(&t.into_c_str().unwrap())); + assert_eq!( + cstr!("hello"), + Borrow::::borrow(&t.as_cow_c_str().unwrap()) + ); + assert_eq!( + cstr!("hello"), + Borrow::::borrow(&t.into_c_str().unwrap()) + ); let t: Cow<'_, OsStr> = Cow::Owned(OsString::from("hello".to_owned())); assert_eq!("hello", t.as_str().unwrap()); assert_eq!("hello".to_owned(), Arg::to_string_lossy(&t)); - assert_eq!(cstr!("hello"), Borrow::borrow(&t.as_cow_c_str().unwrap())); - assert_eq!(cstr!("hello"), Borrow::borrow(&t.into_c_str().unwrap())); + assert_eq!( + cstr!("hello"), + Borrow::::borrow(&t.as_cow_c_str().unwrap()) + ); + assert_eq!( + cstr!("hello"), + Borrow::::borrow(&t.into_c_str().unwrap()) + ); let t: Cow<'_, CStr> = Cow::Borrowed(cstr!("hello")); assert_eq!("hello", t.as_str().unwrap()); assert_eq!("hello".to_owned(), Arg::to_string_lossy(&t)); - assert_eq!(cstr!("hello"), Borrow::borrow(&t.as_cow_c_str().unwrap())); - assert_eq!(cstr!("hello"), Borrow::borrow(&t.into_c_str().unwrap())); + assert_eq!( + cstr!("hello"), + Borrow::::borrow(&t.as_cow_c_str().unwrap()) + ); + assert_eq!( + cstr!("hello"), + Borrow::::borrow(&t.into_c_str().unwrap()) + ); let t: Cow<'_, CStr> = Cow::Owned(cstr!("hello").to_owned()); assert_eq!("hello", t.as_str().unwrap()); assert_eq!("hello".to_owned(), Arg::to_string_lossy(&t)); - assert_eq!(cstr!("hello"), Borrow::borrow(&t.as_cow_c_str().unwrap())); - assert_eq!(cstr!("hello"), Borrow::borrow(&t.into_c_str().unwrap())); + assert_eq!( + cstr!("hello"), + Borrow::::borrow(&t.as_cow_c_str().unwrap()) + ); + assert_eq!( + cstr!("hello"), + Borrow::::borrow(&t.into_c_str().unwrap()) + ); let t: &[u8] = b"hello"; assert_eq!("hello", t.as_str().unwrap()); assert_eq!("hello".to_owned(), Arg::to_string_lossy(&t)); - assert_eq!(cstr!("hello"), Borrow::borrow(&t.as_cow_c_str().unwrap())); - assert_eq!(cstr!("hello"), Borrow::borrow(&t.into_c_str().unwrap())); + assert_eq!( + cstr!("hello"), + Borrow::::borrow(&t.as_cow_c_str().unwrap()) + ); + assert_eq!( + cstr!("hello"), + Borrow::::borrow(&t.into_c_str().unwrap()) + ); let t: Vec = b"hello".to_vec(); assert_eq!("hello", t.as_str().unwrap()); assert_eq!("hello".to_owned(), Arg::to_string_lossy(&t)); - assert_eq!(cstr!("hello"), Borrow::borrow(&t.as_cow_c_str().unwrap())); - assert_eq!(cstr!("hello"), Borrow::borrow(&t.into_c_str().unwrap())); + assert_eq!( + cstr!("hello"), + Borrow::::borrow(&t.as_cow_c_str().unwrap()) + ); + assert_eq!( + cstr!("hello"), + Borrow::::borrow(&t.into_c_str().unwrap()) + ); let t: DecInt = DecInt::new(43110); assert_eq!("43110", t.as_str()); assert_eq!("43110".to_owned(), Arg::to_string_lossy(&t)); - assert_eq!(cstr!("43110"), Borrow::borrow(&t.as_cow_c_str().unwrap())); + assert_eq!( + cstr!("43110"), + Borrow::::borrow(&t.as_cow_c_str().unwrap()) + ); assert_eq!(cstr!("43110"), t.as_c_str()); - assert_eq!(cstr!("43110"), Borrow::borrow(&t.into_c_str().unwrap())); + assert_eq!( + cstr!("43110"), + Borrow::::borrow(&t.into_c_str().unwrap()) + ); } #[test] diff --git a/tests/process/prctl.rs b/tests/process/prctl.rs index 248fbc6a6..6ec816be1 100644 --- a/tests/process/prctl.rs +++ b/tests/process/prctl.rs @@ -7,7 +7,7 @@ use { use rustix::process::*; #[cfg(feature = "thread")] -use rustix::thread::Capability; +use rustix::thread::CapabilitySet; #[test] fn test_parent_process_death_signal() { @@ -87,7 +87,7 @@ fn test_speculative_feature_state() { #[cfg(feature = "thread")] #[test] fn test_is_io_flusher() { - if !thread_has_capability(Capability::SystemResource).unwrap() { + if !thread_has_capability(CapabilitySet::SYS_RESOURCE).unwrap() { eprintln!("test_is_io_flusher: Test skipped due to missing capability: CAP_SYS_RESOURCE."); return; } @@ -99,7 +99,7 @@ fn test_is_io_flusher() { #[cfg(feature = "system")] #[test] fn test_virtual_memory_map_config_struct_size() { - if !thread_has_capability(Capability::SystemResource).unwrap() { + if !thread_has_capability(CapabilitySet::SYS_RESOURCE).unwrap() { eprintln!( "test_virtual_memory_map_config_struct_size: Test skipped due to missing capability: \ CAP_SYS_RESOURCE." @@ -129,7 +129,7 @@ fn test_floating_point_emulation_control() { // #[cfg(feature = "thread")] -pub(crate) fn thread_has_capability(capability: Capability) -> io::Result { +pub(crate) fn thread_has_capability(capability: CapabilitySet) -> io::Result { const _LINUX_CAPABILITY_VERSION_3: u32 = 0x2008_0522; #[repr(C)] @@ -175,7 +175,9 @@ pub(crate) fn thread_has_capability(capability: Capability) -> io::Result return Err(io::Error::last_os_error()); } - let cap_index = capability as u32; + let cap_bits = capability.bits(); + assert_eq!(cap_bits.count_ones(), 1); + let cap_index = cap_bits.leading_zeros(); let (data_index, cap_index) = if cap_index < 32 { (0, cap_index) } else { diff --git a/tests/system/reboot.rs b/tests/system/reboot.rs index 2bc146c57..7369d757a 100644 --- a/tests/system/reboot.rs +++ b/tests/system/reboot.rs @@ -3,15 +3,15 @@ fn test_reboot() { use rustix::io::Errno; use rustix::system::{self, RebootCommand}; - use rustix::thread::{self, CapabilityFlags}; + use rustix::thread::{self, CapabilitySet}; let mut capabilities = thread::capabilities(None).expect("Failed to get capabilities"); - capabilities.effective.set(CapabilityFlags::SYS_BOOT, false); + capabilities.effective.set(CapabilitySet::SYS_BOOT, false); thread::set_capabilities(None, capabilities).expect("Failed to set capabilities"); - // The reboot syscall requires the `CapabilityFlags::SYS_BOOT` permission + // The reboot syscall requires the `CapabilitySet::SYS_BOOT` permission // to be called, otherwise [`Errno::PERM`] is returned assert_eq!(system::reboot(RebootCommand::Restart), Err(Errno::PERM)); } diff --git a/tests/thread/prctl.rs b/tests/thread/prctl.rs index 358ae5208..d9a98383b 100644 --- a/tests/thread/prctl.rs +++ b/tests/thread/prctl.rs @@ -18,7 +18,12 @@ fn test_name() { #[test] fn test_capability_is_in_bounding_set() { - dbg!(capability_is_in_bounding_set(Capability::ChangeOwnership).unwrap()); + dbg!(capability_is_in_bounding_set(CapabilitySet::CHOWN).unwrap()); + dbg!(capability_is_in_bounding_set( + #[allow(deprecated)] + Capability::ChangeOwnership + ) + .unwrap()); } #[test] @@ -38,7 +43,12 @@ fn test_no_new_privs() { #[test] fn test_capability_is_in_ambient_set() { - dbg!(capability_is_in_ambient_set(Capability::ChangeOwnership).unwrap()); + dbg!(capability_is_in_ambient_set(CapabilitySet::CHOWN).unwrap()); + dbg!(capability_is_in_ambient_set( + #[allow(deprecated)] + Capability::ChangeOwnership + ) + .unwrap()); } #[cfg(target_arch = "aarch64")] diff --git a/tests/time/main.rs b/tests/time/main.rs index 2e1bc2578..b819f5f34 100644 --- a/tests/time/main.rs +++ b/tests/time/main.rs @@ -14,7 +14,13 @@ mod monotonic; all(apple, not(target_os = "macos")) )))] mod settime; -#[cfg(linux_kernel)] +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "netbsd" +))] mod timerfd; mod timespec; #[cfg(not(any(target_os = "redox", target_os = "wasi")))] diff --git a/tests/time/monotonic.rs b/tests/time/monotonic.rs index d0f50216b..6f3d21a68 100644 --- a/tests/time/monotonic.rs +++ b/tests/time/monotonic.rs @@ -59,10 +59,12 @@ fn test_monotonic_clock_vs_libc() { let r = unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut after) }; assert_eq!(r, 0); + #[allow(clippy::useless_conversion)] let before = Timespec { tv_sec: before.tv_sec.try_into().unwrap(), tv_nsec: before.tv_nsec.try_into().unwrap(), }; + #[allow(clippy::useless_conversion)] let after = Timespec { tv_sec: after.tv_sec.try_into().unwrap(), tv_nsec: after.tv_nsec.try_into().unwrap(), diff --git a/tests/time/timerfd.rs b/tests/time/timerfd.rs index 6ad4dd72e..961441a52 100644 --- a/tests/time/timerfd.rs +++ b/tests/time/timerfd.rs @@ -5,7 +5,11 @@ use rustix::time::{ #[test] fn test_timerfd() { - let fd = timerfd_create(TimerfdClockId::Monotonic, TimerfdFlags::CLOEXEC).unwrap(); + let fd = match timerfd_create(TimerfdClockId::Monotonic, TimerfdFlags::CLOEXEC) { + Ok(fd) => fd, + Err(rustix::io::Errno::NOSYS) => return, + Err(err) => Err(err).unwrap(), + }; let set = Itimerspec { it_interval: Timespec { @@ -39,12 +43,20 @@ fn test_timerfd() { /// times are monotonic because that would race with the timer repeating. #[test] fn test_timerfd_with_interval() { - let fd = timerfd_create(TimerfdClockId::Monotonic, TimerfdFlags::CLOEXEC).unwrap(); + let fd = match timerfd_create(TimerfdClockId::Monotonic, TimerfdFlags::CLOEXEC) { + Ok(fd) => fd, + Err(rustix::io::Errno::NOSYS) => return, + Err(err) => Err(err).unwrap(), + }; + // An `Itimerspec` with an initial value and an interval. + // + // For the interval, use a value of more than 200000 nanoseconds, as + // illumos appears not to support values smaller than that. let set = Itimerspec { it_interval: Timespec { tv_sec: 0, - tv_nsec: 6, + tv_nsec: 200001, }, it_value: Timespec { tv_sec: 1, diff --git a/tests/time/wall.rs b/tests/time/wall.rs index ceab007b5..c8a8a2856 100644 --- a/tests/time/wall.rs +++ b/tests/time/wall.rs @@ -15,10 +15,12 @@ fn test_wall_clock() { let r = unsafe { libc::clock_gettime(libc::CLOCK_REALTIME, &mut after) }; assert_eq!(r, 0); + #[allow(clippy::useless_conversion)] let before = Timespec { tv_sec: before.tv_sec.try_into().unwrap(), tv_nsec: before.tv_nsec.try_into().unwrap(), }; + #[allow(clippy::useless_conversion)] let after = Timespec { tv_sec: after.tv_sec.try_into().unwrap(), tv_nsec: after.tv_nsec.try_into().unwrap(),