Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ feat_acl = ["cp/feat_acl"]
# "feat_selinux" == enable support for SELinux Security Context (by using `--features feat_selinux`)
# NOTE:
# * The selinux(-sys) crate requires `libselinux` headers and shared library to be accessible in the C toolchain at compile time.
# * Running a uutils compiled with `feat_selinux` requires an SELinux enabled Kernel at run time.
# * A uutils compiled with `feat_selinux` runs fine on a kernel without SELinux enabled; SELinux-specific
# behavior is just skipped at runtime (see `uucore::selinux::is_selinux_enabled`).
feat_selinux = [
"cp/selinux",
"feat_require_selinux",
Expand All @@ -97,7 +98,7 @@ feat_selinux = [
]
# "feat_smack" == enable support for SMACK Security Context (by using `--features feat_smack`)
# NOTE:
# * Running a uutils compiled with `feat_smack` requires a SMACK enabled Kernel at run time.
# * A uutils compiled with `feat_smack` runs fine on a kernel without SMACK enabled; SMACK-specific behavior is just skipped at runtime (see `uucore::smack::is_smack_enabled`).
feat_smack = [
"id/smack",
"ls/smack",
Expand Down
3 changes: 3 additions & 0 deletions src/uu/mkdir/locales/en-US.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,8 @@ mkdir-error-file-exists = { $path }: File exists
mkdir-error-failed-to-create-tree = failed to create whole tree
mkdir-error-cannot-set-permissions = cannot set permissions { $path }

# Warning messages
mkdir-warning-context-not-selinux = ignoring --context; it requires an SELinux/SMACK-enabled kernel

# Verbose output
mkdir-verbose-created-directory = { $util_name }: created directory { $path }
3 changes: 3 additions & 0 deletions src/uu/mkdir/locales/fr-FR.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,8 @@ mkdir-error-file-exists = { $path } : Le fichier existe
mkdir-error-failed-to-create-tree = échec de la création de l'arborescence complète
mkdir-error-cannot-set-permissions = impossible de définir les permissions { $path }

# Messages d'avertissement
mkdir-warning-context-not-selinux = --context est ignoré ; cela nécessite un noyau avec SELinux/SMACK activé

# Sortie détaillée
mkdir-verbose-created-directory = { $util_name } : répertoire créé { $path }
30 changes: 22 additions & 8 deletions src/uu/mkdir/src/mkdir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ use uucore::translate;

#[cfg(not(windows))]
use uucore::mode;
#[cfg(any(
all(feature = "selinux", any(target_os = "android", target_os = "linux")),
all(feature = "smack", target_os = "linux")
))]
use uucore::show_warning;
use uucore::{display::Quotable, fs::dir_strip_dot_for_creation};
use uucore::{format_usage, show_if_err};

Expand Down Expand Up @@ -321,20 +326,29 @@ fn create_single_dir(path: &Path, is_parent: bool, config: &Config) -> UResult<(

// Apply SELinux context if requested
#[cfg(all(feature = "selinux", any(target_os = "android", target_os = "linux")))]
if config.set_security_context && uucore::selinux::is_selinux_enabled() {
if let Err(e) = uucore::selinux::set_selinux_security_context(path, config.context)
{
let _ = std::fs::remove_dir(path);
return Err(USimpleError::new(1, e.to_string()));
if config.set_security_context {
if uucore::selinux::is_selinux_enabled() {
if let Err(e) =
uucore::selinux::set_selinux_security_context(path, config.context)
{
let _ = std::fs::remove_dir(path);
return Err(USimpleError::new(1, e.to_string()));
}
} else {
show_warning!("{}", translate!("mkdir-warning-context-not-selinux"));
}
}

// Apply SMACK context if requested
#[cfg(all(feature = "smack", target_os = "linux"))]
if config.set_security_context {
uucore::smack::set_smack_label_and_cleanup(path, config.context, |p| {
std::fs::remove_dir(p)
})?;
if uucore::smack::is_smack_enabled() {
uucore::smack::set_smack_label_and_cleanup(path, config.context, |p| {
std::fs::remove_dir(p)
})?;
} else {
show_warning!("{}", translate!("mkdir-warning-context-not-selinux"));
}
}
Ok(())
}
Expand Down
48 changes: 48 additions & 0 deletions tests/by-util/test_mkdir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,54 @@ fn test_selinux() {
}
}

#[test]
#[cfg(all(
feature = "feat_selinux",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Running a uutils compiled with feat_selinux requires an SELinux enabled Kernel at run time, so I'm not sure what this is testing.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's testing that when the binary has selinux support compiled in but the kernel doesn't have it enabled, mkdir still creates the dir and just warns, matching GNU. That's a real state, feat_selinux only means libselinux is linked in, it doesn't need an SELinux kernel to run. Checked it here, getenforce says Disabled, and the test actually hit the assertions instead of skipping.

Though you're right it never runs in our own CI. The only job that tests with feat_selinux boots a Fedora VM with SELinux on, the lint job with the feature only runs clippy. So this test always self-skips on our runners even though it's correct locally. Can drop it if you'd rather not carry something CI never exercises.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd assumed it wasn't a real state due to this comment:

# * Running a uutils compiled with `feat_selinux` requires an SELinux enabled Kernel at run time.

I guess the comment is incorrect.

This does expose a wider issue if we don't run CI for feat_selinux without SELinux kernel.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense that's where that came from, the comment was just wrong. Pushed a fix for it.

Agreed on the CI gap too, that's real. Feels like its own thing separate from this PR though — want me to open an issue for it, or would you rather just add a plain ubuntu-latest feat_selinux test job here directly?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CI failures should be resolved by #13329.

I think it makes sense to open a separate issue for the missing CI coverage.

Does the same apply for SMACK here:

# * Running a uutils compiled with `feat_smack` requires a SMACK enabled Kernel at run time.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it applies, and it's worse there. is_smack_enabled() is the same kind of safe runtime check (Path::new("/sys/fs/smackfs").exists()), comment was wrong the same way. But set_smack_label_and_cleanup was also silently returning Ok when SMACK isn't enabled, no warning at all, so mkdir was just pretending --context worked. Fixed the comment and wired the SMACK branch to warn the same way SELinux does now, reusing the existing mkdir-warning-context-not-selinux string since it already says "SELinux/SMACK" generically. Also feat_smack isn't built in any CI job at all currently, not even for lint, so this had zero coverage anywhere. Added a test for it locally, confirmed it hits the real assertion here.

any(target_os = "linux", target_os = "android")
))]
fn test_selinux_context_warns_when_kernel_not_enabled() {
// --context should still create the directory on a kernel without
// SELinux/SMACK, just with a warning that it had no effect (matches GNU).
// On a kernel that does have it enabled, that's test_selinux's job above.
if uucore::selinux::is_selinux_enabled() {
return;
}

let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let dest = "test_dir_context_warning";

new_ucmd!()
.arg("--context=unconfined_u:object_r:user_tmp_t:s0")
.arg(at.plus_as_string(dest))
.succeeds()
.no_stdout()
.stderr_contains("ignoring --context");

assert!(at.dir_exists(dest));
}

#[test]
#[cfg(all(feature = "feat_smack", target_os = "linux"))]
fn test_smack_context_warns_when_kernel_not_enabled() {
if uucore::smack::is_smack_enabled() {
return;
}

let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let dest = "test_dir_smack_context_warning";

new_ucmd!()
.arg("--context=some-label")
.arg(at.plus_as_string(dest))
.succeeds()
.no_stdout()
.stderr_contains("ignoring --context");

assert!(at.dir_exists(dest));
}

#[test]
#[cfg(all(
feature = "feat_selinux",
Expand Down
Loading