Skip to content

Commit 3a05fa9

Browse files
authored
Merge pull request #7908 from sylvestre/selinux-installa
selinux: start the support of install
2 parents 0fd6e88 + 38861cc commit 3a05fa9

File tree

5 files changed

+350
-70
lines changed

5 files changed

+350
-70
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ feat_acl = ["cp/feat_acl"]
4949
feat_selinux = [
5050
"cp/selinux",
5151
"id/selinux",
52+
"install/selinux",
5253
"ls/selinux",
5354
"mkdir/selinux",
5455
"mkfifo/selinux",

src/uu/install/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ uucore = { workspace = true, features = [
3333
"process",
3434
] }
3535

36+
[features]
37+
selinux = ["uucore/selinux"]
38+
3639
[[bin]]
3740
name = "install"
3841
path = "src/main.rs"

src/uu/install/src/install.rs

Lines changed: 44 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ use uucore::fs::dir_strip_dot_for_creation;
2525
use uucore::mode::get_umask;
2626
use uucore::perms::{Verbosity, VerbosityLevel, wrap_chown};
2727
use uucore::process::{getegid, geteuid};
28+
#[cfg(feature = "selinux")]
29+
use uucore::selinux::{contexts_differ, set_selinux_security_context};
2830
use uucore::{format_usage, help_about, help_usage, show, show_error, show_if_err};
2931

3032
#[cfg(unix)]
@@ -51,13 +53,12 @@ pub struct Behavior {
5153
create_leading: bool,
5254
target_dir: Option<String>,
5355
no_target_dir: bool,
56+
preserve_context: bool,
57+
context: Option<String>,
5458
}
5559

5660
#[derive(Error, Debug)]
5761
enum InstallError {
58-
#[error("Unimplemented feature: {0}")]
59-
Unimplemented(String),
60-
6162
#[error("{} with -d requires at least one argument.", uucore::util_name())]
6263
DirNeedsArg,
6364

@@ -108,14 +109,15 @@ enum InstallError {
108109

109110
#[error("extra operand {}\n{}", .0.quote(), .1.quote())]
110111
ExtraOperand(String, String),
112+
113+
#[cfg(feature = "selinux")]
114+
#[error("{}", .0)]
115+
SelinuxContextFailed(String),
111116
}
112117

113118
impl UError for InstallError {
114119
fn code(&self) -> i32 {
115-
match self {
116-
Self::Unimplemented(_) => 2,
117-
_ => 1,
118-
}
120+
1
119121
}
120122

121123
fn usage(&self) -> bool {
@@ -172,8 +174,6 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
172174
.map(|v| v.map(ToString::to_string).collect())
173175
.unwrap_or_default();
174176

175-
check_unimplemented(&matches)?;
176-
177177
let behavior = behavior(&matches)?;
178178

179179
match behavior.main_function {
@@ -295,21 +295,20 @@ pub fn uu_app() -> Command {
295295
.action(ArgAction::SetTrue),
296296
)
297297
.arg(
298-
// TODO implement flag
299298
Arg::new(OPT_PRESERVE_CONTEXT)
300299
.short('P')
301300
.long(OPT_PRESERVE_CONTEXT)
302-
.help("(unimplemented) preserve security context")
301+
.help("preserve security context")
303302
.action(ArgAction::SetTrue),
304303
)
305304
.arg(
306-
// TODO implement flag
307305
Arg::new(OPT_CONTEXT)
308306
.short('Z')
309307
.long(OPT_CONTEXT)
310-
.help("(unimplemented) set security context of files and directories")
308+
.help("set security context of files and directories")
311309
.value_name("CONTEXT")
312-
.action(ArgAction::SetTrue),
310+
.value_parser(clap::value_parser!(String))
311+
.num_args(0..=1),
313312
)
314313
.arg(
315314
Arg::new(ARG_FILES)
@@ -319,25 +318,6 @@ pub fn uu_app() -> Command {
319318
)
320319
}
321320

322-
/// Check for unimplemented command line arguments.
323-
///
324-
/// Either return the degenerate Ok value, or an Err with string.
325-
///
326-
/// # Errors
327-
///
328-
/// Error datum is a string of the unimplemented argument.
329-
///
330-
///
331-
fn check_unimplemented(matches: &ArgMatches) -> UResult<()> {
332-
if matches.get_flag(OPT_PRESERVE_CONTEXT) {
333-
Err(InstallError::Unimplemented(String::from("--preserve-context, -P")).into())
334-
} else if matches.get_flag(OPT_CONTEXT) {
335-
Err(InstallError::Unimplemented(String::from("--context, -Z")).into())
336-
} else {
337-
Ok(())
338-
}
339-
}
340-
341321
/// Determine behavior, given command line arguments.
342322
///
343323
/// If successful, returns a filled-out Behavior struct.
@@ -415,6 +395,8 @@ fn behavior(matches: &ArgMatches) -> UResult<Behavior> {
415395
}
416396
};
417397

398+
let context = matches.get_one::<String>(OPT_CONTEXT).cloned();
399+
418400
Ok(Behavior {
419401
main_function,
420402
specified_mode,
@@ -435,6 +417,8 @@ fn behavior(matches: &ArgMatches) -> UResult<Behavior> {
435417
create_leading: matches.get_flag(OPT_CREATE_LEADING),
436418
target_dir,
437419
no_target_dir,
420+
preserve_context: matches.get_flag(OPT_PRESERVE_CONTEXT),
421+
context,
438422
})
439423
}
440424

@@ -485,6 +469,10 @@ fn directory(paths: &[String], b: &Behavior) -> UResult<()> {
485469
}
486470

487471
show_if_err!(chown_optional_user_group(path, b));
472+
473+
// Set SELinux context for directory if needed
474+
#[cfg(feature = "selinux")]
475+
show_if_err!(set_selinux_context(path, b));
488476
}
489477
// If the exit code was set, or show! has been called at least once
490478
// (which sets the exit code as well), function execution will end after
@@ -941,6 +929,14 @@ fn copy(from: &Path, to: &Path, b: &Behavior) -> UResult<()> {
941929
preserve_timestamps(from, to)?;
942930
}
943931

932+
#[cfg(feature = "selinux")]
933+
if b.preserve_context {
934+
uucore::selinux::preserve_security_context(from, to)
935+
.map_err(|e| InstallError::SelinuxContextFailed(e.to_string()))?;
936+
} else if b.context.is_some() {
937+
set_selinux_context(to, b)?;
938+
}
939+
944940
if b.verbose {
945941
print!("{} -> {}", from.quote(), to.quote());
946942
match backup_path {
@@ -1012,6 +1008,11 @@ fn need_copy(from: &Path, to: &Path, b: &Behavior) -> UResult<bool> {
10121008
return Ok(true);
10131009
}
10141010

1011+
#[cfg(feature = "selinux")]
1012+
if b.preserve_context && contexts_differ(from, to) {
1013+
return Ok(true);
1014+
}
1015+
10151016
// TODO: if -P (#1809) and from/to contexts mismatch, return true.
10161017

10171018
// Check if the owner ID is specified and differs from the destination file's owner.
@@ -1042,3 +1043,13 @@ fn need_copy(from: &Path, to: &Path, b: &Behavior) -> UResult<bool> {
10421043

10431044
Ok(false)
10441045
}
1046+
1047+
#[cfg(feature = "selinux")]
1048+
fn set_selinux_context(path: &Path, behavior: &Behavior) -> UResult<()> {
1049+
if !behavior.preserve_context && behavior.context.is_some() {
1050+
// Use the provided context set by -Z/--context
1051+
set_selinux_security_context(path, behavior.context.as_ref())
1052+
.map_err(|e| InstallError::SelinuxContextFailed(e.to_string()))?;
1053+
}
1054+
Ok(())
1055+
}

0 commit comments

Comments
 (0)