Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
da73288
refactor(mknod): replace unsafe libc calls with nix crate for safety
mattsu2020 Jan 8, 2026
8f109b3
refactor(mknod): make FileType enum private
mattsu2020 Jan 9, 2026
2737497
refactor(mknod): make Config struct private
mattsu2020 Jan 9, 2026
0a23410
refactor: conditionally compile SELinux/SMACK security context features
mattsu2020 Jan 9, 2026
da1c3b8
refactor(mknod): remove lifetime from Config and own context field
mattsu2020 Jan 9, 2026
1fffa92
refactor(mknod): format SMACK context setting for code consistency
mattsu2020 Jan 9, 2026
8c4c16e
refactor(mknod): make Config struct fields private for better encapsu…
mattsu2020 Jan 9, 2026
7a2aa9f
Update src/uu/mknod/src/mknod.rs
mattsu2020 Jan 12, 2026
fee1a77
refactor(mknod): replace hardcoded permission mode with symbolic cons…
mattsu2020 Jan 12, 2026
605bb8c
Merge branch 'knod_refactoring' of https://github.com/mattsu2020/core…
mattsu2020 Jan 12, 2026
272ad61
fix(mknod): add explicit u32 cast to MODE_RW_UGO constant
mattsu2020 Jan 12, 2026
cef606c
style(mknod): condense MODE_RW_UGO constant to single line
mattsu2020 Jan 15, 2026
2085158
refactor(mknod): remove redundant cast in MODE_RW_UGO constant
mattsu2020 Jan 15, 2026
562124a
fix(mknod): cast mode constants to u32 on android, macos, freebsd, redox
mattsu2020 Jan 15, 2026
1b11f05
refactor(mknod): unify MODE_RW_UGO type using mode_t
mattsu2020 Jan 17, 2026
868e9f1
refactor(mknod): reorder libc imports and condense constant declaration
mattsu2020 Jan 17, 2026
36acc97
refactor(mknod): replace unnecessary casts with u32::from for MODE_RW…
mattsu2020 Jan 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/uu/mknod/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ path = "src/mknod.rs"

[dependencies]
clap = { workspace = true }
libc = { workspace = true }
uucore = { workspace = true, features = ["mode", "fs"] }
fluent = { workspace = true }
nix = { workspace = true }

[features]
selinux = ["uucore/selinux"]
Expand Down
148 changes: 80 additions & 68 deletions src/uu/mknod/src/mknod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,19 @@
// 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
// spell-checker:ignore (ToDO) parsemode makedev sysmacros perror IFBLK IFCHR IFIFO sflag

use clap::{Arg, ArgAction, Command, value_parser};
use libc::{S_IFBLK, S_IFCHR, S_IFIFO, S_IRGRP, S_IROTH, S_IRUSR, S_IWGRP, S_IWOTH, S_IWUSR};
use libc::{dev_t, mode_t};
use std::ffi::CString;
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 uucore::display::Quotable;
use uucore::error::{UResult, USimpleError, UUsageError, set_exit_code};
use uucore::format_usage;
use uucore::fs::makedev;
use uucore::translate;

const MODE_RW_UGO: mode_t = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
const MODE_RW_UGO: mode_t = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) as mode_t;

mod options {
pub const MODE: &str = "mode";
Expand All @@ -35,86 +34,94 @@
}

impl FileType {
fn as_mode(&self) -> mode_t {
fn as_sflag(&self) -> SFlag {
match self {
Self::Block => S_IFBLK,
Self::Character => S_IFCHR,
Self::Fifo => S_IFIFO,
Self::Block => SFlag::S_IFBLK,
Self::Character => SFlag::S_IFCHR,
Self::Fifo => SFlag::S_IFIFO,
}
}
}

/// Configuration for special inode creation.
pub struct Config<'a> {
/// bitmask of inode mode (permissions and file type)
pub mode: mode_t,
struct Config {
/// Permission bits for the inode
mode: Mode,

file_type: FileType,

/// when false, the exact mode bits will be set
pub use_umask: bool,
use_umask: bool,

pub dev: dev_t,
dev: u64,

/// Set security context (SELinux/SMACK).
pub set_security_context: bool,
#[cfg(any(feature = "selinux", feature = "smack"))]
set_security_context: bool,

/// Specific security context (SELinux/SMACK).
pub context: Option<&'a String>,
#[cfg(any(feature = "selinux", feature = "smack"))]
context: Option<String>,
}

fn mknod(file_name: &str, config: Config) -> i32 {
let c_str = CString::new(file_name).expect("Failed to convert to CString");

unsafe {
// set umask to 0 and store previous umask
let have_prev_umask = if config.use_umask {
None
} else {
Some(libc::umask(0))
};
// set umask to 0 and store previous umask
let have_prev_umask = if config.use_umask {
None
} else {
Some(nix_umask(Mode::empty()))
};

let errno = libc::mknod(c_str.as_ptr(), config.mode, config.dev);
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 };

// set umask back to original value
if let Some(prev_umask) = have_prev_umask {
libc::umask(prev_umask);
}
// set umask back to original value
if let Some(prev_umask) = have_prev_umask {
nix_umask(prev_umask);
}

if errno == -1 {
let c_str = CString::new(uucore::execution_phrase().as_bytes())
.expect("Failed to convert to CString");
// shows the error from the mknod syscall
libc::perror(c_str.as_ptr());
}
if let Some(err) = mknod_err {
eprintln!(
"{}: {}",
uucore::execution_phrase(),
std::io::Error::from(err)
);
}

// Apply SELinux context if requested
#[cfg(feature = "selinux")]
if config.set_security_context {
if let Err(e) = uucore::selinux::set_selinux_security_context(
std::path::Path::new(file_name),
config.context,
) {
// if it fails, delete the file
let _ = std::fs::remove_dir(file_name);
eprintln!("{}: {}", uucore::util_name(), e);
return 1;
}
// Apply SELinux context if requested
#[cfg(feature = "selinux")]
if config.set_security_context {
if let Err(e) = uucore::selinux::set_selinux_security_context(
std::path::Path::new(file_name),
config.context.as_ref(),
) {
// if it fails, delete the file
let _ = std::fs::remove_dir(file_name);
eprintln!("{}: {}", uucore::util_name(), e);
return 1;
}
}

// Apply SMACK context if requested
#[cfg(feature = "smack")]
if config.set_security_context {
if let Err(e) =
uucore::smack::set_smack_label_and_cleanup(file_name, config.context, |p| {
std::fs::remove_file(p)
})
{
eprintln!("{}: {}", uucore::util_name(), e);
return 1;
}
// Apply SMACK context if requested
#[cfg(feature = "smack")]
if config.set_security_context {
if let Err(e) =
uucore::smack::set_smack_label_and_cleanup(file_name, config.context.as_ref(), |p| {
std::fs::remove_file(p)
})
{
eprintln!("{}: {}", uucore::util_name(), e);
return 1;
}

errno
}

errno
}

#[uucore::main]
Expand All @@ -125,21 +132,23 @@

let mut use_umask = true;
let mode_permissions = match matches.get_one::<String>("mode") {
None => MODE_RW_UGO,
None => u32::from(MODE_RW_UGO),

Check failure on line 135 in src/uu/mknod/src/mknod.rs

View workflow job for this annotation

GitHub Actions / Style and Lint (unix)

ERROR: `cargo clippy`: useless conversion to the same type: `u32` (file:'src/uu/mknod/src/mknod.rs', line:135)
Some(str_mode) => {
use_umask = false;
parse_mode(str_mode).map_err(|e| USimpleError::new(1, e))?
}
};
let mode = mode_permissions | file_type.as_mode();
let mode = Mode::from_bits_truncate(mode_permissions as nix::libc::mode_t);

let file_name = matches
.get_one::<String>("name")
.expect("Missing argument 'NAME'");

// Extract the security context related flags and options
#[cfg(any(feature = "selinux", feature = "smack"))]
let set_security_context = matches.get_flag(options::SECURITY_CONTEXT);
let context = matches.get_one::<String>(options::CONTEXT);
#[cfg(any(feature = "selinux", feature = "smack"))]
let context = matches.get_one::<String>(options::CONTEXT).cloned();

let dev = match (
file_type,
Expand All @@ -153,7 +162,7 @@
translate!("mknod-error-fifo-no-major-minor"),
));
}
(_, Some(&major), Some(&minor)) => makedev(major as _, minor as _),
(_, Some(&major), Some(&minor)) => makedev(major as _, minor as _) as u64,
_ => {
return Err(UUsageError::new(
1,
Expand All @@ -164,9 +173,12 @@

let config = Config {
mode,
file_type: file_type.clone(),
use_umask,
dev,
#[cfg(any(feature = "selinux", feature = "smack"))]
set_security_context: set_security_context || context.is_some(),
#[cfg(any(feature = "selinux", feature = "smack"))]
context,
};

Expand Down Expand Up @@ -234,8 +246,8 @@
}

#[allow(clippy::unnecessary_cast)]
fn parse_mode(str_mode: &str) -> Result<mode_t, String> {
let default_mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) as u32;
fn parse_mode(str_mode: &str) -> Result<u32, String> {
let default_mode = u32::from(MODE_RW_UGO);

Check failure on line 250 in src/uu/mknod/src/mknod.rs

View workflow job for this annotation

GitHub Actions / Style and Lint (unix)

ERROR: `cargo clippy`: useless conversion to the same type: `u32` (file:'src/uu/mknod/src/mknod.rs', line:250)
uucore::mode::parse_chmod(default_mode, str_mode, true, uucore::mode::get_umask())
.map_err(|e| {
translate!(
Expand All @@ -247,7 +259,7 @@
if mode > 0o777 {
Err(translate!("mknod-error-mode-permission-bits-only"))
} else {
Ok(mode as mode_t)
Ok(mode)
}
})
}
Expand Down
Loading