Skip to content

Commit 10d864a

Browse files
mKnod: Refactor to remove unsafe code (#10138)
--------- Co-authored-by: Sylvestre Ledru <sylvestre@debian.org>
1 parent cb400de commit 10d864a

3 files changed

Lines changed: 82 additions & 70 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/uu/mknod/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ path = "src/mknod.rs"
2121

2222
[dependencies]
2323
clap = { workspace = true }
24-
libc = { workspace = true }
2524
uucore = { workspace = true, features = ["mode", "fs"] }
2625
fluent = { workspace = true }
26+
nix = { workspace = true }
2727

2828
[features]
2929
selinux = ["uucore/selinux"]

src/uu/mknod/src/mknod.rs

Lines changed: 80 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,20 @@
33
// For the full copyright and license information, please view the LICENSE
44
// file that was distributed with this source code.
55

6-
// spell-checker:ignore (ToDO) parsemode makedev sysmacros perror IFBLK IFCHR IFIFO
6+
// spell-checker:ignore (ToDO) parsemode makedev sysmacros perror IFBLK IFCHR IFIFO sflag
77

88
use clap::{Arg, ArgAction, Command, value_parser};
9-
use libc::{S_IFBLK, S_IFCHR, S_IFIFO, S_IRGRP, S_IROTH, S_IRUSR, S_IWGRP, S_IWOTH, S_IWUSR};
10-
use libc::{dev_t, mode_t};
11-
use std::ffi::CString;
9+
use nix::libc::{S_IRGRP, S_IROTH, S_IRUSR, S_IWGRP, S_IWOTH, S_IWUSR, mode_t};
10+
use nix::sys::stat::{Mode, SFlag, mknod as nix_mknod, umask as nix_umask};
1211

1312
use uucore::display::Quotable;
1413
use uucore::error::{UResult, USimpleError, UUsageError, set_exit_code};
1514
use uucore::format_usage;
1615
use uucore::fs::makedev;
1716
use uucore::translate;
1817

19-
const MODE_RW_UGO: mode_t = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
18+
#[allow(clippy::unnecessary_cast)]
19+
const MODE_RW_UGO: u32 = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) as u32;
2020

2121
mod options {
2222
pub const MODE: &str = "mode";
@@ -35,86 +35,94 @@ enum FileType {
3535
}
3636

3737
impl FileType {
38-
fn as_mode(&self) -> mode_t {
38+
fn as_sflag(&self) -> SFlag {
3939
match self {
40-
Self::Block => S_IFBLK,
41-
Self::Character => S_IFCHR,
42-
Self::Fifo => S_IFIFO,
40+
Self::Block => SFlag::S_IFBLK,
41+
Self::Character => SFlag::S_IFCHR,
42+
Self::Fifo => SFlag::S_IFIFO,
4343
}
4444
}
4545
}
4646

4747
/// Configuration for special inode creation.
48-
pub struct Config<'a> {
49-
/// bitmask of inode mode (permissions and file type)
50-
pub mode: mode_t,
48+
struct Config {
49+
/// Permission bits for the inode
50+
mode: Mode,
51+
52+
file_type: FileType,
5153

5254
/// when false, the exact mode bits will be set
53-
pub use_umask: bool,
55+
use_umask: bool,
5456

55-
pub dev: dev_t,
57+
dev: u64,
5658

5759
/// Set security context (SELinux/SMACK).
58-
pub set_security_context: bool,
60+
#[cfg(any(feature = "selinux", feature = "smack"))]
61+
set_security_context: bool,
5962

6063
/// Specific security context (SELinux/SMACK).
61-
pub context: Option<&'a String>,
64+
#[cfg(any(feature = "selinux", feature = "smack"))]
65+
context: Option<String>,
6266
}
6367

6468
fn mknod(file_name: &str, config: Config) -> i32 {
65-
let c_str = CString::new(file_name).expect("Failed to convert to CString");
66-
67-
unsafe {
68-
// set umask to 0 and store previous umask
69-
let have_prev_umask = if config.use_umask {
70-
None
71-
} else {
72-
Some(libc::umask(0))
73-
};
69+
// set umask to 0 and store previous umask
70+
let have_prev_umask = if config.use_umask {
71+
None
72+
} else {
73+
Some(nix_umask(Mode::empty()))
74+
};
7475

75-
let errno = libc::mknod(c_str.as_ptr(), config.mode, config.dev);
76+
let mknod_err = nix_mknod(
77+
file_name,
78+
config.file_type.as_sflag(),
79+
config.mode,
80+
config.dev as _,
81+
)
82+
.err();
83+
let errno = if mknod_err.is_some() { -1 } else { 0 };
7684

77-
// set umask back to original value
78-
if let Some(prev_umask) = have_prev_umask {
79-
libc::umask(prev_umask);
80-
}
85+
// set umask back to original value
86+
if let Some(prev_umask) = have_prev_umask {
87+
nix_umask(prev_umask);
88+
}
8189

82-
if errno == -1 {
83-
let c_str = CString::new(uucore::execution_phrase().as_bytes())
84-
.expect("Failed to convert to CString");
85-
// shows the error from the mknod syscall
86-
libc::perror(c_str.as_ptr());
87-
}
90+
if let Some(err) = mknod_err {
91+
eprintln!(
92+
"{}: {}",
93+
uucore::execution_phrase(),
94+
std::io::Error::from(err)
95+
);
96+
}
8897

89-
// Apply SELinux context if requested
90-
#[cfg(feature = "selinux")]
91-
if config.set_security_context {
92-
if let Err(e) = uucore::selinux::set_selinux_security_context(
93-
std::path::Path::new(file_name),
94-
config.context,
95-
) {
96-
// if it fails, delete the file
97-
let _ = std::fs::remove_file(file_name);
98-
eprintln!("{}: {e}", uucore::util_name());
99-
return 1;
100-
}
98+
// Apply SELinux context if requested
99+
#[cfg(feature = "selinux")]
100+
if config.set_security_context {
101+
if let Err(e) = uucore::selinux::set_selinux_security_context(
102+
std::path::Path::new(file_name),
103+
config.context.as_ref(),
104+
) {
105+
// if it fails, delete the file
106+
let _ = std::fs::remove_file(file_name);
107+
eprintln!("{}: {e}", uucore::util_name());
108+
return 1;
101109
}
110+
}
102111

103-
// Apply SMACK context if requested
104-
#[cfg(feature = "smack")]
105-
if config.set_security_context {
106-
if let Err(e) =
107-
uucore::smack::set_smack_label_and_cleanup(file_name, config.context, |p| {
108-
std::fs::remove_file(p)
109-
})
110-
{
111-
eprintln!("{}: {e}", uucore::util_name());
112-
return 1;
113-
}
112+
// Apply SMACK context if requested
113+
#[cfg(feature = "smack")]
114+
if config.set_security_context {
115+
if let Err(e) =
116+
uucore::smack::set_smack_label_and_cleanup(file_name, config.context.as_ref(), |p| {
117+
std::fs::remove_file(p)
118+
})
119+
{
120+
eprintln!("{}: {e}", uucore::util_name());
121+
return 1;
114122
}
115-
116-
errno
117123
}
124+
125+
errno
118126
}
119127

120128
#[uucore::main]
@@ -131,15 +139,17 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
131139
parse_mode(str_mode).map_err(|e| USimpleError::new(1, e))?
132140
}
133141
};
134-
let mode = mode_permissions | file_type.as_mode();
142+
let mode = Mode::from_bits_truncate(mode_permissions as mode_t);
135143

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

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

144154
let dev = match (
145155
file_type,
@@ -153,7 +163,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
153163
translate!("mknod-error-fifo-no-major-minor"),
154164
));
155165
}
156-
(_, Some(&major), Some(&minor)) => makedev(major as _, minor as _),
166+
(_, Some(&major), Some(&minor)) => makedev(major as _, minor as _) as u64,
157167
_ => {
158168
return Err(UUsageError::new(
159169
1,
@@ -164,9 +174,12 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
164174

165175
let config = Config {
166176
mode,
177+
file_type: file_type.clone(),
167178
use_umask,
168179
dev,
180+
#[cfg(any(feature = "selinux", feature = "smack"))]
169181
set_security_context: set_security_context || context.is_some(),
182+
#[cfg(any(feature = "selinux", feature = "smack"))]
170183
context,
171184
};
172185

@@ -233,9 +246,8 @@ pub fn uu_app() -> Command {
233246
)
234247
}
235248

236-
#[allow(clippy::unnecessary_cast)]
237-
fn parse_mode(str_mode: &str) -> Result<mode_t, String> {
238-
let default_mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) as u32;
249+
fn parse_mode(str_mode: &str) -> Result<u32, String> {
250+
let default_mode = MODE_RW_UGO;
239251
uucore::mode::parse_chmod(default_mode, str_mode, true, uucore::mode::get_umask())
240252
.map_err(|e| {
241253
translate!(
@@ -247,7 +259,7 @@ fn parse_mode(str_mode: &str) -> Result<mode_t, String> {
247259
if mode > 0o777 {
248260
Err(translate!("mknod-error-mode-permission-bits-only"))
249261
} else {
250-
Ok(mode as mode_t)
262+
Ok(mode)
251263
}
252264
})
253265
}

0 commit comments

Comments
 (0)