From 9be4058da1eb1aaa16208a7cbc8088fc44f47630 Mon Sep 17 00:00:00 2001 From: mattsu Date: Sat, 16 May 2026 20:29:16 +0900 Subject: [PATCH 1/4] mknod: replace nix::sys::stat with rustix + libc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace `nix::sys::stat` usage with `rustix::fs` types and `libc::mknod`: - `nix::sys::stat::Mode` → `rustix::fs::Mode` - `nix::sys::stat::SFlag` → `rustix::fs::FileType` - `nix::sys::stat::umask` → `rustix::process::umask` with RAII `UmaskGuard` - `nix::sys::stat::mknod` → `libc::mknod` (rustix::fs::mknodat is unavailable on apple targets) - `nix::libc::S_I*` constants → `0o666` literal The `UmaskGuard` pattern (borrowed from mkdir) ensures umask is restored on drop, even on panic — an improvement over the previous manual save/restore. --- Cargo.lock | 3 +- src/uu/mknod/Cargo.toml | 3 +- src/uu/mknod/src/mknod.rs | 79 +++++++++++++++++++++++++-------------- 3 files changed, 55 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 275c7b23117..bbdb7b6f98c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3778,7 +3778,8 @@ version = "0.8.0" dependencies = [ "clap", "fluent", - "nix", + "libc", + "rustix", "uucore", ] diff --git a/src/uu/mknod/Cargo.toml b/src/uu/mknod/Cargo.toml index 03ad05b9d50..8ba32c343e6 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 } +rustix = { workspace = true, features = ["process", "fs"] } +libc = { workspace = true } [features] selinux = ["uucore/selinux"] diff --git a/src/uu/mknod/src/mknod.rs b/src/uu/mknod/src/mknod.rs index 9640806d818..bbf0a427cf6 100644 --- a/src/uu/mknod/src/mknod.rs +++ b/src/uu/mknod/src/mknod.rs @@ -3,11 +3,13 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore (ToDO) parsemode makedev sysmacros perror IFBLK IFCHR IFIFO sflag +// spell-checker:ignore (ToDO) parsemode makedev sysmacros perror + +use std::ffi::CString; 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 rustix::fs::{FileType as RustixFileType, Mode}; +use rustix::process::umask; use uucore::display::Quotable; use uucore::error::{UResult, USimpleError, UUsageError, set_exit_code}; @@ -15,8 +17,7 @@ use uucore::format_usage; use uucore::fs::makedev; use uucore::translate; -#[allow(clippy::unnecessary_cast)] -const MODE_RW_UGO: u32 = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) as u32; +const MODE_RW_UGO: u32 = 0o666; mod options { pub const MODE: &str = "mode"; @@ -27,7 +28,7 @@ mod options { pub const CONTEXT: &str = "context"; } -#[derive(Clone, PartialEq)] +#[derive(Clone, Copy, PartialEq)] enum FileType { Block, Character, @@ -35,11 +36,11 @@ enum FileType { } impl FileType { - fn as_sflag(&self) -> SFlag { + fn to_rustix(self) -> RustixFileType { match self { - Self::Block => SFlag::S_IFBLK, - Self::Character => SFlag::S_IFCHR, - Self::Fifo => SFlag::S_IFIFO, + Self::Block => RustixFileType::BlockDevice, + Self::Character => RustixFileType::CharacterDevice, + Self::Fifo => RustixFileType::Fifo, } } } @@ -71,34 +72,56 @@ struct Config { context: Option, } +/// RAII guard to restore umask on drop, ensuring cleanup even on panic. +struct UmaskGuard(Mode); + +impl UmaskGuard { + fn set(new_mask: Mode) -> Self { + let old_mask = umask(new_mask); + Self(old_mask) + } +} + +impl Drop for UmaskGuard { + fn drop(&mut self) { + umask(self.0); + } +} + +/// Create a special file using `mknod(2)`. +/// +/// Uses `libc::mknod` directly since `rustix::fs::mknodat` is unavailable on +/// Apple targets. Combines `file_type` (S_IF* bits) with `mode` (permission +/// bits) into the raw `mode_t` argument expected by the syscall. +fn do_mknod(path: &str, file_type: RustixFileType, mode: Mode, dev: u64) -> std::io::Result<()> { + let raw_mode = file_type.as_raw_mode() | mode.as_raw_mode(); + let c_path = CString::new(path)?; + let result = unsafe { libc::mknod(c_path.as_ptr(), raw_mode as _, dev as _) }; + if result == 0 { + Ok(()) + } else { + Err(std::io::Error::last_os_error()) + } +} + fn mknod(file_name: &str, config: Config) -> i32 { - // set umask to 0 and store previous umask - let have_prev_umask = if config.use_umask { + let _guard = if config.use_umask { None } else { - Some(nix_umask(Mode::empty())) + Some(UmaskGuard::set(Mode::empty())) }; - let mknod_err = nix_mknod( + let mknod_err = do_mknod( file_name, - config.file_type.as_sflag(), + config.file_type.to_rustix(), config.mode, - config.dev as _, + config.dev, ) .err(); let errno = if mknod_err.is_some() { -1 } else { 0 }; - // set umask back to original value - if let Some(prev_umask) = have_prev_umask { - nix_umask(prev_umask); - } - if let Some(err) = mknod_err { - eprintln!( - "{}: {}", - uucore::execution_phrase(), - std::io::Error::from(err) - ); + eprintln!("{}: {err}", uucore::execution_phrase()); } // Apply SELinux context if requested @@ -149,7 +172,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { parse_mode(str_mode).map_err(|e| USimpleError::new(1, e))? } }; - let mode = Mode::from_bits_truncate(mode_permissions as mode_t); + let mode = Mode::from_bits_truncate(mode_permissions as _); let file_name = matches .get_one::("name") @@ -190,7 +213,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let config = Config { mode, - file_type: file_type.clone(), + file_type: *file_type, use_umask, dev, #[cfg(any( From 5f297d2ffd04e36b6e3d7fb85143ba04407919aa Mon Sep 17 00:00:00 2001 From: mattsu Date: Sat, 16 May 2026 20:30:52 +0900 Subject: [PATCH 2/4] fix: add cspell ignore entries for RAII and mknodat --- src/uu/mknod/src/mknod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/mknod/src/mknod.rs b/src/uu/mknod/src/mknod.rs index bbf0a427cf6..8c75939391a 100644 --- a/src/uu/mknod/src/mknod.rs +++ b/src/uu/mknod/src/mknod.rs @@ -3,7 +3,7 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore (ToDO) parsemode makedev sysmacros perror +// spell-checker:ignore (ToDO) parsemode makedev sysmacros perror RAII mknodat use std::ffi::CString; From 25073f19fcaacc88895e94bf72a709db06a1b4bb Mon Sep 17 00:00:00 2001 From: mattsu Date: Sat, 16 May 2026 20:57:42 +0900 Subject: [PATCH 3/4] fix: address PR review feedback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Rename `to_rustix` → `to_file_type` to avoid embedding crate name - Use `let _ = writeln!(stderr, ...)` to prevent SIGABRT on /dev/full - Sort Cargo.toml dependencies alphabetically (libc before rustix) --- src/uu/mknod/Cargo.toml | 2 +- src/uu/mknod/src/mknod.rs | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/uu/mknod/Cargo.toml b/src/uu/mknod/Cargo.toml index 8ba32c343e6..72c67ce35de 100644 --- a/src/uu/mknod/Cargo.toml +++ b/src/uu/mknod/Cargo.toml @@ -24,8 +24,8 @@ doctest = false clap = { workspace = true } uucore = { workspace = true, features = ["mode", "fs"] } fluent = { workspace = true } -rustix = { workspace = true, features = ["process", "fs"] } libc = { workspace = true } +rustix = { workspace = true, features = ["process", "fs"] } [features] selinux = ["uucore/selinux"] diff --git a/src/uu/mknod/src/mknod.rs b/src/uu/mknod/src/mknod.rs index 8c75939391a..b531a86fd73 100644 --- a/src/uu/mknod/src/mknod.rs +++ b/src/uu/mknod/src/mknod.rs @@ -36,7 +36,7 @@ enum FileType { } impl FileType { - fn to_rustix(self) -> RustixFileType { + fn to_file_type(self) -> RustixFileType { match self { Self::Block => RustixFileType::BlockDevice, Self::Character => RustixFileType::CharacterDevice, @@ -113,7 +113,7 @@ fn mknod(file_name: &str, config: Config) -> i32 { let mknod_err = do_mknod( file_name, - config.file_type.to_rustix(), + config.file_type.to_file_type(), config.mode, config.dev, ) @@ -121,7 +121,12 @@ fn mknod(file_name: &str, config: Config) -> i32 { let errno = if mknod_err.is_some() { -1 } else { 0 }; if let Some(err) = mknod_err { - eprintln!("{}: {err}", uucore::execution_phrase()); + use std::io::Write as _; + let _ = writeln!( + std::io::stderr(), + "{}: {err}", + uucore::execution_phrase() + ); } // Apply SELinux context if requested From 000ee9de630069b04385896b4b0a8c24f06e6c61 Mon Sep 17 00:00:00 2001 From: mattsu Date: Sat, 16 May 2026 20:59:01 +0900 Subject: [PATCH 4/4] refactor(mknod): simplify error message formatting Replaced the multi-line writeln! macro call with a single-line version for better readability. --- src/uu/mknod/src/mknod.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/uu/mknod/src/mknod.rs b/src/uu/mknod/src/mknod.rs index b531a86fd73..21c6cbc40fc 100644 --- a/src/uu/mknod/src/mknod.rs +++ b/src/uu/mknod/src/mknod.rs @@ -122,11 +122,7 @@ fn mknod(file_name: &str, config: Config) -> i32 { if let Some(err) = mknod_err { use std::io::Write as _; - let _ = writeln!( - std::io::stderr(), - "{}: {err}", - uucore::execution_phrase() - ); + let _ = writeln!(std::io::stderr(), "{}: {err}", uucore::execution_phrase()); } // Apply SELinux context if requested