Skip to content

Commit 80b6a21

Browse files
authored
Merge pull request #7635 from sylvestre/temp-selinux-impl
mkdir: add the selinux support
2 parents 0d23be3 + fe77174 commit 80b6a21

File tree

10 files changed

+315
-23
lines changed

10 files changed

+315
-23
lines changed

.github/workflows/CICD.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1188,7 +1188,7 @@ jobs:
11881188
- run: rsync -v -a -e ssh . lima-default:~/work/
11891189
- name: Setup Rust and other build deps in VM
11901190
run: |
1191-
lima sudo dnf install gcc g++ git rustup libselinux-devel clang-devel -y
1191+
lima sudo dnf install gcc g++ git rustup libselinux-devel clang-devel attr -y
11921192
lima rustup-init -y --default-toolchain stable
11931193
- name: Verify SELinux Status
11941194
run: |

Cargo.lock

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

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ feat_selinux = [
5050
"cp/selinux",
5151
"id/selinux",
5252
"ls/selinux",
53+
"mkdir/selinux",
5354
"selinux",
5455
"feat_require_selinux",
5556
]

src/uu/mkdir/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ path = "src/mkdir.rs"
2121
clap = { workspace = true }
2222
uucore = { workspace = true, features = ["fs", "mode", "fsxattr"] }
2323

24+
[features]
25+
selinux = ["uucore/selinux"]
2426

2527
[[bin]]
2628
name = "mkdir"

src/uu/mkdir/src/mkdir.rs

Lines changed: 70 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,26 @@ mod options {
2929
pub const PARENTS: &str = "parents";
3030
pub const VERBOSE: &str = "verbose";
3131
pub const DIRS: &str = "dirs";
32+
pub const SELINUX: &str = "z";
33+
pub const CONTEXT: &str = "context";
34+
}
35+
36+
/// Configuration for directory creation.
37+
pub struct Config<'a> {
38+
/// Create parent directories as needed.
39+
pub recursive: bool,
40+
41+
/// File permissions (octal).
42+
pub mode: u32,
43+
44+
/// Print message for each created directory.
45+
pub verbose: bool,
46+
47+
/// Set SELinux security context.
48+
pub set_selinux_context: bool,
49+
50+
/// Specific SELinux context.
51+
pub context: Option<&'a String>,
3252
}
3353

3454
#[cfg(windows)]
@@ -91,8 +111,21 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
91111
let verbose = matches.get_flag(options::VERBOSE);
92112
let recursive = matches.get_flag(options::PARENTS);
93113

114+
// Extract the SELinux related flags and options
115+
let set_selinux_context = matches.get_flag(options::SELINUX);
116+
let context = matches.get_one::<String>(options::CONTEXT);
117+
94118
match get_mode(&matches, mode_had_minus_prefix) {
95-
Ok(mode) => exec(dirs, recursive, mode, verbose),
119+
Ok(mode) => {
120+
let config = Config {
121+
recursive,
122+
mode,
123+
verbose,
124+
set_selinux_context: set_selinux_context || context.is_some(),
125+
context,
126+
};
127+
exec(dirs, &config)
128+
}
96129
Err(f) => Err(USimpleError::new(1, f)),
97130
}
98131
}
@@ -124,6 +157,15 @@ pub fn uu_app() -> Command {
124157
.help("print a message for each printed directory")
125158
.action(ArgAction::SetTrue),
126159
)
160+
.arg(
161+
Arg::new(options::SELINUX)
162+
.short('Z')
163+
.help("set SELinux security context of each created directory to the default type")
164+
.action(ArgAction::SetTrue),
165+
)
166+
.arg(Arg::new(options::CONTEXT).long(options::CONTEXT).value_name("CTX").help(
167+
"like -Z, or if CTX is specified then set the SELinux or SMACK security context to CTX",
168+
))
127169
.arg(
128170
Arg::new(options::DIRS)
129171
.action(ArgAction::Append)
@@ -137,12 +179,12 @@ pub fn uu_app() -> Command {
137179
/**
138180
* Create the list of new directories
139181
*/
140-
fn exec(dirs: ValuesRef<OsString>, recursive: bool, mode: u32, verbose: bool) -> UResult<()> {
182+
fn exec(dirs: ValuesRef<OsString>, config: &Config) -> UResult<()> {
141183
for dir in dirs {
142184
let path_buf = PathBuf::from(dir);
143185
let path = path_buf.as_path();
144186

145-
show_if_err!(mkdir(path, recursive, mode, verbose));
187+
show_if_err!(mkdir(path, config));
146188
}
147189
Ok(())
148190
}
@@ -160,7 +202,7 @@ fn exec(dirs: ValuesRef<OsString>, recursive: bool, mode: u32, verbose: bool) ->
160202
///
161203
/// To match the GNU behavior, a path with the last directory being a single dot
162204
/// (like `some/path/to/.`) is created (with the dot stripped).
163-
pub fn mkdir(path: &Path, recursive: bool, mode: u32, verbose: bool) -> UResult<()> {
205+
pub fn mkdir(path: &Path, config: &Config) -> UResult<()> {
164206
if path.as_os_str().is_empty() {
165207
return Err(USimpleError::new(
166208
1,
@@ -173,7 +215,7 @@ pub fn mkdir(path: &Path, recursive: bool, mode: u32, verbose: bool) -> UResult<
173215
// std::fs::create_dir("foo/."); fails in pure Rust
174216
let path_buf = dir_strip_dot_for_creation(path);
175217
let path = path_buf.as_path();
176-
create_dir(path, recursive, verbose, false, mode)
218+
create_dir(path, false, config)
177219
}
178220

179221
#[cfg(any(unix, target_os = "redox"))]
@@ -194,15 +236,9 @@ fn chmod(_path: &Path, _mode: u32) -> UResult<()> {
194236
// Return true if the directory at `path` has been created by this call.
195237
// `is_parent` argument is not used on windows
196238
#[allow(unused_variables)]
197-
fn create_dir(
198-
path: &Path,
199-
recursive: bool,
200-
verbose: bool,
201-
is_parent: bool,
202-
mode: u32,
203-
) -> UResult<()> {
239+
fn create_dir(path: &Path, is_parent: bool, config: &Config) -> UResult<()> {
204240
let path_exists = path.exists();
205-
if path_exists && !recursive {
241+
if path_exists && !config.recursive {
206242
return Err(USimpleError::new(
207243
1,
208244
format!("{}: File exists", path.display()),
@@ -212,9 +248,9 @@ fn create_dir(
212248
return Ok(());
213249
}
214250

215-
if recursive {
251+
if config.recursive {
216252
match path.parent() {
217-
Some(p) => create_dir(p, recursive, verbose, true, mode)?,
253+
Some(p) => create_dir(p, true, config)?,
218254
None => {
219255
USimpleError::new(1, "failed to create whole tree");
220256
}
@@ -223,7 +259,7 @@ fn create_dir(
223259

224260
match std::fs::create_dir(path) {
225261
Ok(()) => {
226-
if verbose {
262+
if config.verbose {
227263
println!(
228264
"{}: created directory {}",
229265
uucore::util_name(),
@@ -233,7 +269,7 @@ fn create_dir(
233269

234270
#[cfg(all(unix, target_os = "linux"))]
235271
let new_mode = if path_exists {
236-
mode
272+
config.mode
237273
} else {
238274
// TODO: Make this macos and freebsd compatible by creating a function to get permission bits from
239275
// acl in extended attributes
@@ -242,19 +278,33 @@ fn create_dir(
242278
if is_parent {
243279
(!mode::get_umask() & 0o777) | 0o300 | acl_perm_bits
244280
} else {
245-
mode | acl_perm_bits
281+
config.mode | acl_perm_bits
246282
}
247283
};
248284
#[cfg(all(unix, not(target_os = "linux")))]
249285
let new_mode = if is_parent {
250286
(!mode::get_umask() & 0o777) | 0o300
251287
} else {
252-
mode
288+
config.mode
253289
};
254290
#[cfg(windows)]
255-
let new_mode = mode;
291+
let new_mode = config.mode;
256292

257293
chmod(path, new_mode)?;
294+
295+
// Apply SELinux context if requested
296+
#[cfg(feature = "selinux")]
297+
if config.set_selinux_context && uucore::selinux::check_selinux_enabled().is_ok() {
298+
if let Err(e) = uucore::selinux::set_selinux_security_context(path, config.context)
299+
{
300+
let _ = std::fs::remove_dir(path);
301+
return Err(USimpleError::new(
302+
1,
303+
format!("failed to set SELinux security context: {}", e),
304+
));
305+
}
306+
}
307+
258308
Ok(())
259309
}
260310

src/uucore/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ crc32fast = { workspace = true, optional = true }
5858
regex = { workspace = true, optional = true }
5959
bigdecimal = { workspace = true, optional = true }
6060
num-traits = { workspace = true, optional = true }
61+
selinux = { workspace = true, optional = true }
6162

6263
[target.'cfg(unix)'.dependencies]
6364
walkdir = { workspace = true, optional = true }
@@ -105,13 +106,14 @@ format = [
105106
mode = ["libc"]
106107
perms = ["entries", "libc", "walkdir"]
107108
buf-copy = []
109+
parser = ["extendedbigdecimal", "glob", "num-traits"]
108110
pipes = []
109111
process = ["libc"]
110112
proc-info = ["tty", "walkdir"]
111113
quoting-style = []
112114
ranges = []
113115
ringbuffer = []
114-
parser = ["extendedbigdecimal", "glob", "num-traits"]
116+
selinux = ["dep:selinux"]
115117
signals = []
116118
sum = [
117119
"digest",

src/uucore/src/lib/features.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ pub mod tty;
6666

6767
#[cfg(all(unix, feature = "fsxattr"))]
6868
pub mod fsxattr;
69+
#[cfg(all(target_os = "linux", feature = "selinux"))]
70+
pub mod selinux;
6971
#[cfg(all(unix, not(target_os = "fuchsia"), feature = "signals"))]
7072
pub mod signals;
7173
#[cfg(all(

0 commit comments

Comments
 (0)