From a73f58b0f3c15d6af53263a7b72bc35337b612dc Mon Sep 17 00:00:00 2001 From: xtqqczze <45661989+xtqqczze@users.noreply.github.com> Date: Thu, 15 Oct 2026 12:32:39 +0100 Subject: [PATCH 1/3] fixup! uucore: print "Operation not permitted" for EPERM Use `libc::EPERM` instead of `nix::errno::Errno::EPERM` to avoid referencing the optional `nix` dependency when it is not enabled. --- src/uucore/Cargo.toml | 1 + src/uucore/src/lib/mods/error.rs | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/uucore/Cargo.toml b/src/uucore/Cargo.toml index b1867dae76e..861b8594e6c 100644 --- a/src/uucore/Cargo.toml +++ b/src/uucore/Cargo.toml @@ -106,6 +106,7 @@ dns-lookup = { workspace = true, optional = true } rustix = { workspace = true, optional = true } [target.'cfg(unix)'.dependencies] +libc = { workspace = true } # utmpx is unix-only, so its dependencies must not be pulled into other targets # (the uptime feature enables utmpx and now builds on windows too). nix = { workspace = true, optional = true } diff --git a/src/uucore/src/lib/mods/error.rs b/src/uucore/src/lib/mods/error.rs index 3d3d02f56be..5ae23e1fd05 100644 --- a/src/uucore/src/lib/mods/error.rs +++ b/src/uucore/src/lib/mods/error.rs @@ -440,9 +440,7 @@ impl Display for UIoError { // writable file owned by someone else) and "Permission denied" // for EACCES. Prefer the errno when available (#15020). #[cfg(unix)] - PermissionDenied - if self.inner.raw_os_error() == Some(nix::errno::Errno::EPERM as i32) => - { + PermissionDenied if self.inner.raw_os_error() == Some(libc::EPERM as _) => { "Operation not permitted" } PermissionDenied => "Permission denied", From b62f263fd8df66807e893752cae5e93bccee69ae Mon Sep 17 00:00:00 2001 From: xtqqczze <45661989+xtqqczze@users.noreply.github.com> Date: Thu, 15 Oct 2026 14:31:23 +0100 Subject: [PATCH 2/3] uucore: build with --no-default-features ```sh RUSTC_BOOTSTRAP=1 cargo clippy -q -Zbuild-std=std,panic_abort -p uucore --all-targets --no-default-features --target=powerpc64-ibm-aix --target=i686-linux-android --target=x86_64-linux-android --target=x86_64-pc-cygwin --target=aarch64-apple-darwin --target=x86_64-unknown-dragonfly --target=x86_64-unknown-freebsd --target=x86_64-unknown-fuchsia --target=x86_64-unknown-haiku --target=x86_64-unknown-hurd-gnu --target=aarch64-unknown-illumos --target=x86_64-unknown-linux-gnu --target=i686-unknown-netbsd --target=x86_64-unknown-netbsd --target=x86_64-unknown-openbsd --target=x86_64-unknown-redox --target=x86_64-pc-solaris --target=wasm32-wasip1 --target=x86_64-pc-windows-gnu ``` --- src/uucore/Cargo.toml | 1 + src/uucore/src/lib/features/i18n/datetime.rs | 1 - src/uucore/src/lib/features/signals.rs | 39 +++++++++++++++++++- src/uucore/src/lib/lib.rs | 28 +------------- src/uucore/src/lib/mods/error.rs | 10 ++--- src/uucore/src/lib/mods/panic.rs | 23 ------------ src/uucore_procs/src/lib.rs | 2 +- 7 files changed, 46 insertions(+), 58 deletions(-) diff --git a/src/uucore/Cargo.toml b/src/uucore/Cargo.toml index 861b8594e6c..35de8d9e84e 100644 --- a/src/uucore/Cargo.toml +++ b/src/uucore/Cargo.toml @@ -185,6 +185,7 @@ i18n-datetime = [ "icu_datetime", "jiff-icu", "jiff", + "libc", ] mode = ["libc", "quoting-style", "rustix/process"] perms = ["entries", "libc", "safe-traversal", "walkdir"] diff --git a/src/uucore/src/lib/features/i18n/datetime.rs b/src/uucore/src/lib/features/i18n/datetime.rs index 144555fccaa..0c08a6e50ad 100644 --- a/src/uucore/src/lib/features/i18n/datetime.rs +++ b/src/uucore/src/lib/features/i18n/datetime.rs @@ -218,7 +218,6 @@ pub fn get_locale_months() -> Option<&'static [Vec; 12]> { ))] fn get_locale_months_inner() -> Option<[Vec; 12]> { use core::ffi::CStr; - use nix::libc; let abmon_items: [libc::nl_item; 12] = [ libc::ABMON_1, diff --git a/src/uucore/src/lib/features/signals.rs b/src/uucore/src/lib/features/signals.rs index 02ed0d0c0db..7be58ad73e0 100644 --- a/src/uucore/src/lib/features/signals.rs +++ b/src/uucore/src/lib/features/signals.rs @@ -12,12 +12,12 @@ #[cfg(unix)] use nix::errno::Errno; -#[cfg(any(target_os = "linux", target_os = "android"))] +#[cfg(unix)] use nix::libc; #[cfg(unix)] use nix::sys::signal::{ SaFlags, SigAction, SigHandler, SigHandler::SigDfl, SigHandler::SigIgn, SigSet, Signal, - Signal::SIGINT, Signal::SIGPIPE, sigaction, signal, + Signal::SIGBUS, Signal::SIGINT, Signal::SIGPIPE, Signal::SIGSEGV, sigaction, signal, }; /// The default signal value. @@ -528,6 +528,41 @@ pub static ALL_SIGNALS: [&str; 32] = [ "URG", "XCPU", "XFSZ", "VTALRM", "PROF", "WINCH", "POLL", "PWR", "SYS", ]; +/// Disables the custom signal handlers installed by Rust for stack-overflow handling. With those custom signal handlers processes ignore the first SIGBUS and SIGSEGV signal they receive. +/// See for details. +#[cfg(unix)] +pub fn disable_rust_signal_handlers() -> Result<(), Errno> { + unsafe { + sigaction( + SIGSEGV, + &SigAction::new(SigDfl, SaFlags::empty(), SigSet::all()), + ) + }?; + unsafe { + sigaction( + SIGBUS, + &SigAction::new(SigDfl, SaFlags::empty(), SigSet::all()), + ) + }?; + Ok(()) +} + +/// Preserve inherited SIGPIPE settings from parent process. +/// +/// Rust unconditionally sets SIGPIPE to `SIG_IGN` on startup. This function +/// checks if the parent process (e.g., `env --default-signal=PIPE`) intended +/// for SIGPIPE to be set to default by checking the `RUST_SIGPIPE` environment +/// variable. If set to "default", it restores SIGPIPE to `SIG_DFL`. +#[cfg(unix)] +pub fn preserve_inherited_sigpipe() { + // Check if parent specified that SIGPIPE should be default + if std::env::var_os("RUST_SIGPIPE").is_some_and(|v| v == "default") { + unsafe { libc::signal(libc::SIGPIPE, libc::SIG_DFL) }; + // Remove the environment variable so child processes don't inherit it incorrectly + unsafe { std::env::remove_var("RUST_SIGPIPE") }; + } +} + /// Returns the signal number for a given signal name or value. pub fn signal_by_name_or_value(signal_name_or_value: &str) -> Option { let signal_name_upcase = signal_name_or_value.to_uppercase(); diff --git a/src/uucore/src/lib/lib.rs b/src/uucore/src/lib/lib.rs index 2eca5a546f6..9a8b3d26f33 100644 --- a/src/uucore/src/lib/lib.rs +++ b/src/uucore/src/lib/lib.rs @@ -181,12 +181,6 @@ pub use crate::features::smack; //## core functions -#[cfg(unix)] -use nix::errno::Errno; -#[cfg(unix)] -use nix::sys::signal::{ - SaFlags, SigAction, SigHandler::SigDfl, SigSet, Signal::SIGBUS, Signal::SIGSEGV, sigaction, -}; use std::borrow::Cow; use std::ffi::{OsStr, OsString}; use std::io::{BufRead, BufReader}; @@ -199,25 +193,6 @@ use std::str; use std::str::Utf8Chunk; use std::sync::{LazyLock, atomic::Ordering}; -/// Disables the custom signal handlers installed by Rust for stack-overflow handling. With those custom signal handlers processes ignore the first SIGBUS and SIGSEGV signal they receive. -/// See for details. -#[cfg(unix)] -pub fn disable_rust_signal_handlers() -> Result<(), Errno> { - unsafe { - sigaction( - SIGSEGV, - &SigAction::new(SigDfl, SaFlags::empty(), SigSet::all()), - ) - }?; - unsafe { - sigaction( - SIGBUS, - &SigAction::new(SigDfl, SaFlags::empty(), SigSet::all()), - ) - }?; - Ok(()) -} - pub fn get_canonical_util_name(util_name: &str) -> &str { // remove the "uu_" prefix let util_name = &util_name[3..]; @@ -238,7 +213,8 @@ macro_rules! bin_inner { use uucore::locale; // Preserve inherited SIGPIPE settings (e.g., from env --default-signal=PIPE) - uucore::panic::preserve_inherited_sigpipe(); + #[cfg(all(unix, not(target_os = "fuchsia")))] + uucore::signals::preserve_inherited_sigpipe(); // suppress extraneous error output for SIGPIPE failures/panics uucore::panic::mute_sigpipe_panic(); diff --git a/src/uucore/src/lib/mods/error.rs b/src/uucore/src/lib/mods/error.rs index 5ae23e1fd05..494fd4f64ff 100644 --- a/src/uucore/src/lib/mods/error.rs +++ b/src/uucore/src/lib/mods/error.rs @@ -578,7 +578,7 @@ impl From for Box { /// // prints "fix me please!: Permission denied" /// println!("{}", uio_result.unwrap_err()); /// ``` -#[cfg(unix)] +#[cfg(all(feature = "nix", unix))] impl FromIo> for Result { fn map_err_context(self, context: impl FnOnce() -> String) -> UResult { self.map_err(|e| { @@ -590,7 +590,7 @@ impl FromIo> for Result { } } -#[cfg(unix)] +#[cfg(all(feature = "nix", unix))] impl FromIo> for nix::Error { fn map_err_context(self, context: impl FnOnce() -> String) -> UResult { Err(Box::new(UIoError { @@ -600,7 +600,7 @@ impl FromIo> for nix::Error { } } -#[cfg(unix)] +#[cfg(all(feature = "nix", unix))] impl From for UIoError { fn from(f: nix::Error) -> Self { Self { @@ -610,7 +610,7 @@ impl From for UIoError { } } -#[cfg(unix)] +#[cfg(all(feature = "nix", unix))] impl From for Box { fn from(f: nix::Error) -> Self { let u_error: UIoError = f.into(); @@ -899,7 +899,7 @@ mod tests { } #[test] - #[cfg(unix)] + #[cfg(all(feature = "nix", unix))] fn test_nix_error_conversion() { use super::{FromIo, UIoError}; use nix::errno::Errno; diff --git a/src/uucore/src/lib/mods/panic.rs b/src/uucore/src/lib/mods/panic.rs index a0e77b03b4f..0eae6a9d8d2 100644 --- a/src/uucore/src/lib/mods/panic.rs +++ b/src/uucore/src/lib/mods/panic.rs @@ -42,26 +42,3 @@ pub fn mute_sigpipe_panic() { } })); } - -/// Preserve inherited SIGPIPE settings from parent process. -/// -/// Rust unconditionally sets SIGPIPE to `SIG_IGN` on startup. This function -/// checks if the parent process (e.g., `env --default-signal=PIPE`) intended -/// for SIGPIPE to be set to default by checking the `RUST_SIGPIPE` environment -/// variable. If set to "default", it restores SIGPIPE to `SIG_DFL`. -#[cfg(unix)] -pub fn preserve_inherited_sigpipe() { - use nix::libc; - - // Check if parent specified that SIGPIPE should be default - if std::env::var_os("RUST_SIGPIPE").is_some_and(|v| v == "default") { - unsafe { libc::signal(libc::SIGPIPE, libc::SIG_DFL) }; - // Remove the environment variable so child processes don't inherit it incorrectly - unsafe { std::env::remove_var("RUST_SIGPIPE") }; - } -} - -#[cfg(not(unix))] -pub fn preserve_inherited_sigpipe() { - // No-op on non-Unix platforms -} diff --git a/src/uucore_procs/src/lib.rs b/src/uucore_procs/src/lib.rs index e74bbd3c888..eb39ee77ca3 100644 --- a/src/uucore_procs/src/lib.rs +++ b/src/uucore_procs/src/lib.rs @@ -50,7 +50,7 @@ pub fn main(args: TokenStream, stream: TokenStream) -> TokenStream { // disable rust signal handlers (otherwise processes don't dump core after e.g. one SIGSEGV) #[cfg(all(#signals, unix))] - uucore::disable_rust_signal_handlers().expect("Disabling rust signal handlers failed"); + uucore::signals::disable_rust_signal_handlers().expect("Disabling rust signal handlers failed"); let result = uumain(args); match result { Ok(()) => uucore::error::get_exit_code(), From b56f970e7ebb5b70bc1ef853d578ccf214956f1d Mon Sep 17 00:00:00 2001 From: xtqqczze <45661989+xtqqczze@users.noreply.github.com> Date: Sun, 25 Oct 2026 19:13:24 +0000 Subject: [PATCH 3/3] build(uucore): fix feature configuration --- src/uucore/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/uucore/Cargo.toml b/src/uucore/Cargo.toml index 35de8d9e84e..367fbfd230e 100644 --- a/src/uucore/Cargo.toml +++ b/src/uucore/Cargo.toml @@ -152,7 +152,7 @@ utmp-classic = { workspace = true, optional = true } [features] default = ["signals"] # * non-default features -backup-control = [] +backup-control = ["fs"] colors = [] diagnostics = ["dep:ariadne"] checksum = ["base64-simd", "quoting-style", "rustix/fs", "sum"] @@ -188,14 +188,14 @@ i18n-datetime = [ "libc", ] mode = ["libc", "quoting-style", "rustix/process"] -perms = ["entries", "libc", "safe-traversal", "walkdir"] +perms = ["entries", "fs", "libc", "safe-traversal", "walkdir"] buf-copy = ["pipes"] parser-num = ["extendedbigdecimal", "num-traits"] parser-size = ["parser-num", "procfs"] parser-glob = ["glob"] parser = ["parser-num", "parser-size", "parser-glob"] pipes = ["fs", "rustix/pipe"] -process = ["libc", "nix/user", "rustix/process", "windows-sys"] +process = ["libc", "nix/signal", "nix/user", "rustix/process", "windows-sys"] proc-info = ["tty", "walkdir"] quoting-style = ["i18n-common"] ranges = []