diff --git a/Cargo.lock b/Cargo.lock index 3b6c0bfaf62..61f9a5a60e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3708,7 +3708,7 @@ version = "0.8.0" dependencies = [ "clap", "fluent", - "nix", + "rustix", "uucore", ] @@ -3787,7 +3787,7 @@ version = "0.8.0" dependencies = [ "clap", "fluent", - "nix", + "rustix", "uucore", ] @@ -3797,7 +3797,8 @@ version = "0.8.0" dependencies = [ "clap", "fluent", - "nix", + "libc", + "rustix", "uucore", ] diff --git a/src/uu/kill/Cargo.toml b/src/uu/kill/Cargo.toml index 9f36aba5074..51d81c3c20f 100644 --- a/src/uu/kill/Cargo.toml +++ b/src/uu/kill/Cargo.toml @@ -25,7 +25,7 @@ uucore = { workspace = true, features = ["signals"] } fluent = { workspace = true } [target.'cfg(unix)'.dependencies] -nix = { workspace = true, features = ["signal"] } +rustix = { workspace = true, features = ["process"] } [[bin]] name = "kill" diff --git a/src/uu/kill/src/kill.rs b/src/uu/kill/src/kill.rs index e92a47da3b4..9d22e0d64af 100644 --- a/src/uu/kill/src/kill.rs +++ b/src/uu/kill/src/kill.rs @@ -250,11 +250,43 @@ fn parse_pids(pids: &[String]) -> UResult> { .collect() } -fn kill(sig: Option, pids: &[i32]) { +fn kill(sig: i32, pids: &[i32]) { + use rustix::process::{ + Pid, Signal, kill_current_process_group, kill_process, kill_process_group, + test_kill_current_process_group, test_kill_process, test_kill_process_group, + }; + for &pid in pids { - if let Err(e) = signal::kill(Pid::from_raw(pid), sig) { + let esrch = || Err(rustix::io::Errno::SRCH); + let result = if sig == 0 { + // Signal 0: test if process/group exists + match pid.cmp(&0) { + std::cmp::Ordering::Greater => { + Pid::from_raw(pid).map_or_else(esrch, test_kill_process) + } + std::cmp::Ordering::Equal => test_kill_current_process_group(), + std::cmp::Ordering::Less => { + Pid::from_raw(-pid).map_or_else(esrch, test_kill_process_group) + } + } + } else { + // SAFETY: sig is a non-zero value from user input; the kernel + // will reject truly invalid signal numbers with EINVAL. + let signal = unsafe { Signal::from_raw_unchecked(sig) }; + match pid.cmp(&0) { + std::cmp::Ordering::Greater => { + Pid::from_raw(pid).map_or_else(esrch, |p| kill_process(p, signal)) + } + std::cmp::Ordering::Equal => kill_current_process_group(signal), + std::cmp::Ordering::Less => { + Pid::from_raw(-pid).map_or_else(esrch, |p| kill_process_group(p, signal)) + } + } + }; + + if let Err(e) = result { show!( - Error::from_raw_os_error(e as i32) + Error::from(e) .map_err_context(|| { translate!("kill-error-sending-signal", "pid" => pid) }) ); } diff --git a/src/uu/mkfifo/Cargo.toml b/src/uu/mkfifo/Cargo.toml index 63f1d8f4748..9ee1440152a 100644 --- a/src/uu/mkfifo/Cargo.toml +++ b/src/uu/mkfifo/Cargo.toml @@ -25,7 +25,8 @@ uucore = { workspace = true, features = ["fs", "mode"] } fluent = { workspace = true } [target.'cfg(unix)'.dependencies] -nix = { workspace = true, features = ["fs"] } +libc = { workspace = true } +rustix = { workspace = true, features = ["fs"] } [features] selinux = ["uucore/selinux"] diff --git a/src/uu/mkfifo/src/mkfifo.rs b/src/uu/mkfifo/src/mkfifo.rs index a48a4894ee6..4dea289c008 100644 --- a/src/uu/mkfifo/src/mkfifo.rs +++ b/src/uu/mkfifo/src/mkfifo.rs @@ -4,8 +4,6 @@ // file that was distributed with this source code. use clap::{Arg, ArgAction, Command, value_parser}; -use nix::sys::stat::Mode; -use nix::unistd::mkfifo; use std::fs; use std::os::unix::fs::PermissionsExt; use uucore::display::Quotable; @@ -48,7 +46,10 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { }; for f in fifos { - if mkfifo(f.as_str(), Mode::from_bits_truncate(0o666)).is_err() { + let c_path = std::ffi::CString::new(f.as_str()).unwrap(); + // SAFETY: c_path is a valid null-terminated C string + let ret = unsafe { libc::mkfifo(c_path.as_ptr(), 0o666) }; + if ret != 0 { show!(USimpleError::new( 1, translate!("mkfifo-error-cannot-create-fifo", "path" => f.quote()), diff --git a/src/uu/mknod/Cargo.toml b/src/uu/mknod/Cargo.toml index 03ad05b9d50..2883fdb20ee 100644 --- a/src/uu/mknod/Cargo.toml +++ b/src/uu/mknod/Cargo.toml @@ -24,7 +24,8 @@ doctest = false clap = { workspace = true } uucore = { workspace = true, features = ["mode", "fs"] } fluent = { workspace = true } -nix = { workspace = true } +libc = { workspace = true } +rustix = { workspace = true, features = ["fs", "process"] } [features] selinux = ["uucore/selinux"] diff --git a/src/uu/mknod/src/mknod.rs b/src/uu/mknod/src/mknod.rs index 39ac62cb62d..0f74693c765 100644 --- a/src/uu/mknod/src/mknod.rs +++ b/src/uu/mknod/src/mknod.rs @@ -6,8 +6,7 @@ // spell-checker:ignore (ToDO) parsemode makedev sysmacros perror IFBLK IFCHR IFIFO sflag use clap::{Arg, ArgAction, Command, value_parser}; -use nix::libc::{S_IRGRP, S_IROTH, S_IRUSR, S_IWGRP, S_IWOTH, S_IWUSR, mode_t}; -use nix::sys::stat::{Mode, SFlag, mknod as nix_mknod, umask as nix_umask}; +use libc::{S_IRGRP, S_IROTH, S_IRUSR, S_IWGRP, S_IWOTH, S_IWUSR, mode_t}; use uucore::display::Quotable; use uucore::error::{UResult, USimpleError, UUsageError, set_exit_code}; @@ -28,28 +27,18 @@ mod options { } #[derive(Clone, PartialEq)] -enum FileType { +enum FileTypeArg { Block, Character, Fifo, } -impl FileType { - fn as_sflag(&self) -> SFlag { - match self { - Self::Block => SFlag::S_IFBLK, - Self::Character => SFlag::S_IFCHR, - Self::Fifo => SFlag::S_IFIFO, - } - } -} - /// Configuration for special inode creation. struct Config { /// Permission bits for the inode mode: Mode, - file_type: FileType, + file_type: FileTypeArg, /// when false, the exact mode bits will be set use_umask: bool, @@ -66,34 +55,46 @@ struct Config { } fn mknod(file_name: &str, config: Config) -> i32 { + use rustix::fs::Mode; + use rustix::process::umask; + // set umask to 0 and store previous umask let have_prev_umask = if config.use_umask { None } else { - Some(nix_umask(Mode::empty())) + Some(umask(Mode::empty())) }; - let mknod_err = nix_mknod( - file_name, - config.file_type.as_sflag(), - config.mode, - config.dev as _, - ) - .err(); - let errno = if mknod_err.is_some() { -1 } else { 0 }; + let file_type_bits: mode_t = match config.file_type { + FileTypeArg::Block => libc::S_IFBLK, + FileTypeArg::Character => libc::S_IFCHR, + FileTypeArg::Fifo => libc::S_IFIFO, + }; + let c_path = std::ffi::CString::new(file_name).unwrap(); + // SAFETY: c_path is a valid null-terminated C string + let ret = unsafe { + libc::mknod( + c_path.as_ptr(), + file_type_bits | config.mode as mode_t, + config.dev as _, + ) + }; // set umask back to original value if let Some(prev_umask) = have_prev_umask { - nix_umask(prev_umask); + umask(prev_umask); } - if let Some(err) = mknod_err { + let errno = if ret != 0 { eprintln!( "{}: {}", uucore::execution_phrase(), std::io::Error::from(err) ); - } + -1 + } else { + 0 + }; // Apply SELinux context if requested #[cfg(feature = "selinux")] @@ -131,7 +132,7 @@ fn mknod(file_name: &str, config: Config) -> i32 { pub fn uumain(args: impl uucore::Args) -> UResult<()> { let matches = uucore::clap_localization::handle_clap_result(uu_app(), args)?; - let file_type = matches.get_one::("type").unwrap(); + let file_type = matches.get_one::("type").unwrap(); let mut use_umask = true; let mode_permissions = match matches.get_one::("mode") { @@ -158,8 +159,8 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { matches.get_one::(options::MAJOR), matches.get_one::(options::MINOR), ) { - (FileType::Fifo, None, None) => 0, - (FileType::Fifo, _, _) => { + (FileTypeArg::Fifo, None, None) => 0, + (FileTypeArg::Fifo, _, _) => { return Err(UUsageError::new( 1, translate!("mknod-error-fifo-no-major-minor"), @@ -266,16 +267,16 @@ fn parse_mode(str_mode: &str) -> Result { }) } -fn parse_type(tpe: &str) -> Result { +fn parse_type(tpe: &str) -> Result { // Only check the first character, to allow mnemonic usage like // 'mknod /dev/rst0 character 18 0'. tpe.chars() .next() .ok_or_else(|| translate!("mknod-error-missing-device-type")) .and_then(|first_char| match first_char { - 'b' => Ok(FileType::Block), - 'c' | 'u' => Ok(FileType::Character), - 'p' => Ok(FileType::Fifo), + 'b' => Ok(FileTypeArg::Block), + 'c' | 'u' => Ok(FileTypeArg::Character), + 'p' => Ok(FileTypeArg::Fifo), _ => Err(translate!("mknod-error-invalid-device-type", "type" => tpe.quote())), }) } diff --git a/src/uu/sort/Cargo.toml b/src/uu/sort/Cargo.toml index 09763a4f9b0..c8ceec88162 100644 --- a/src/uu/sort/Cargo.toml +++ b/src/uu/sort/Cargo.toml @@ -51,7 +51,8 @@ foldhash = { workspace = true } ctrlc = { workspace = true } [target.'cfg(unix)'.dependencies] -nix = { workspace = true, features = ["resource"] } +libc = { workspace = true } +rustix = { workspace = true, features = ["process", "param", "fs"] } [dev-dependencies] divan = { workspace = true } diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index 8b321b55d53..d13906eb658 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -1436,9 +1436,10 @@ pub(crate) fn current_open_fd_count() -> Option { let mut count = 0usize; for fd in 0..limit { - let fd = fd as libc::c_int; - // Probe with libc::fcntl because the fd may be invalid. - if unsafe { libc::fcntl(fd, libc::F_GETFD) } != -1 { + let fd = fd as std::os::fd::RawFd; + // SAFETY: We are only probing whether the fd is valid via fcntl_getfd; + // the borrowed fd is not used beyond this call. + if rustix::io::fcntl_getfd(unsafe { std::os::fd::BorrowedFd::borrow_raw(fd) }).is_ok() { count = count.saturating_add(1); } } diff --git a/src/uu/sync/src/sync.rs b/src/uu/sync/src/sync.rs index b34d0ac0bd1..7d7e5125d1a 100644 --- a/src/uu/sync/src/sync.rs +++ b/src/uu/sync/src/sync.rs @@ -6,12 +6,6 @@ /* Last synced with: sync (GNU coreutils) 8.13 */ use clap::{Arg, ArgAction, Command}; -#[cfg(any(target_os = "linux", target_os = "android"))] -use nix::errno::Errno; -#[cfg(any(target_os = "linux", target_os = "android"))] -use nix::fcntl::{OFlag, open}; -#[cfg(any(target_os = "linux", target_os = "android"))] -use nix::sys::stat::Mode; use std::path::Path; use uucore::display::Quotable; use uucore::error::{UResult, USimpleError, get_exit_code, set_exit_code}; @@ -235,13 +229,22 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { #[cfg(any(target_os = "linux", target_os = "android"))] { let path = Path::new(&f); - if let Err(e) = open(path, OFlag::O_NONBLOCK, Mode::empty()) { - if e != Errno::EACCES || (e == Errno::EACCES && path.is_dir()) { - show_error!( - "{}", - translate!("sync-error-opening-file", "file" => f.quote(), "err" => e.desc()) - ); - set_exit_code(1); + match rustix::fs::open( + path, + rustix::fs::OFlags::NONBLOCK, + rustix::fs::Mode::empty(), + ) { + Ok(_fd) => { /* OwnedFd auto-closes on drop */ } + Err(e) => { + let is_eacces = e == rustix::io::Errno::ACCESS; + if !is_eacces || path.is_dir() { + let err = std::io::Error::from(e); + show_error!( + "{}", + translate!("sync-error-opening-file", "file" => f.quote(), "err" => err) + ); + set_exit_code(1); + } } } }