Skip to content

install: label only the directories it creates - #15333

Open
abendrothj wants to merge 1 commit into
uutils:mainfrom
abendrothj:install-context-created-dirs
Open

abendrothj wants to merge 1 commit into
uutils:mainfrom
abendrothj:install-context-created-dirs

Conversation

@abendrothj

@abendrothj abendrothj commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Closes #15332

With -Z or --context, install labelled directories after creating them, by walking up from the target and setting the context on every existing parent too, up to / for an absolute path. GNU labels only what it creates.

Directories are now created under the creation context, through uucore's FsCreateContext as mkdir does, so only new ones are labelled and nothing is relabelled afterwards. With -Z, each new directory gets the policy's default for its own name, as in GNU; for that, create_dir_all_safe takes a hook that runs before each missing directory is created. It also resolves a relative name whose parents are created by the same command. This also gives new directories under -Z the policy's default type, the directory half of #15091 (except GNU's tmp_t for an absolute name directly in /tmp). As in GNU 9.12, -d -Z still resets an existing target to its default context, and -d --context leaves an existing target alone. An unusable --context now fails before anything is done, as in GNU, also when the target directory exists. With SELinux off, no context is set, as in mkdir.

Checked as root on Fedora 44 (enforcing) against GNU 9.12, under /var/www and /home, for -d and -D with -Z and --context on new and existing directories, relative and absolute: labels and exit status match in all 22 cases. Added tests for -d and -D with --context, for the -Z reset of an existing directory, for relative names with missing parents, and for new parents of a .snapshots directory, which the policy types differently; each fails without the change.

Copilot AI balanced review requested due to automatic review settings October 14, 2026 05:14

Copilot AI left a comment

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Comment thread src/uu/install/src/install.rs Outdated
match directory_creation_context(&target, b) {
Ok(guard) => guard,
Err(e) => {
show!(e);

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.

Is this stdout? or show_error!?

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 stderr: show! prints like show_error! and also sets the exit code to 1, which GNU returns here.

@github-actions

Copy link
Copy Markdown

GNU testsuite comparison:

GNU test failed: tests/install/install-Z-selinux. tests/install/install-Z-selinux is passing on 'main'. Maybe you have to rebase?
GNU test failed: tests/tail/truncate. tests/tail/truncate is passing on 'main'. Maybe you have to rebase?

Copilot AI balanced review requested due to automatic review settings October 14, 2026 06:02
@abendrothj
abendrothj force-pushed the install-context-created-dirs branch from 3ddad2f to 69c9757 Compare October 14, 2026 06:02

Copilot AI left a comment

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@codspeed

codspeed Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Merging this PR will not alter performance

1 warning, 1 note

⚠️ 118 benchmarks are dominated by system calls

System calls cannot be simulated consistently and are left out of the measure. The reported value understates the real cost. Switch to the Walltime instrument to measure them.

Learn more about system calls

See affected benchmarks

ℹ️ 1 benchmark has no flamegraph

The flamegraph wasn't generated: most of the time was spent in runtime internals, which would leave nothing useful to analyze.

See affected benchmarks

✅ 339 untouched benchmarks
⏩ 132 skipped benchmarks1


Comparing abendrothj:install-context-created-dirs (bb549c8) with main (ff0c589)2

Open in CodSpeed

Footnotes

  1. 132 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

  2. No successful run was found on main (732b9c5) during the generation of this report, so ff0c589 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report. ↩

#[cfg(all(feature = "selinux", any(target_os = "linux", target_os = "android")))]
fn test_selinux_invalid_context_existing_directory() {
if !uucore::selinux::is_selinux_enabled() {
println!("Skipping SELinux test: SELinux is not enabled");

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 don't think there's any sense of using println! in tests, what do you think @xtqqczze?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I don't know, but we do the same in test_selinux.

Comment thread src/uu/install/src/install.rs Outdated
let _selinux_guard = {
let target = dir_strip_dot_for_creation(path);
match directory_creation_context(&target, b) {
Ok(guard) => guard,

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.

dir_strip_dot_for_creation(path) is computed again a few lines below, could be shared?

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.

Done.

// Like GNU, -Z resets an existing target to its default context; --context doesn't.
#[cfg(all(feature = "selinux", any(target_os = "linux", target_os = "android")))]
if b.privileged && b.default_context && is_selinux_enabled() {
show_if_err!(set_selinux_security_context(path, None));

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.

why not set_selinux_default_context(path) as before?
and please add a test for this -Z reset of an existing dir

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.

set_selinux_default_context doesn't ask the policy: it keeps the dir's own context, so nothing gets reset. set_selinux_security_context(path, None) looks the default up. Added test_selinux_default_context_resets_existing_directory, which fails with the old call.

Comment thread src/uu/install/src/install.rs Outdated
let mode = uucore::libc::S_IFDIR | DEFAULT_MODE as uucore::libc::mode_t;
uucore::selinux::FsCreateContext::new(path, Some(mode), get_context_for_selinux(b))
.map(Some)
.map_err(|e| uucore::error::USimpleError::new(1, e.to_string()))

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.

please use InstallError::SelinuxContextFailed like the other selinux errors here

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.

Done.

Comment thread tests/by-util/test_install.rs Outdated
// parent keeps its context.
#[test]
#[cfg(all(feature = "selinux", any(target_os = "linux", target_os = "android")))]
fn test_selinux_context_only_on_created_directories() {

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.

could you please split it into two tests, one for -d and one for -D?

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.

Done.

// -Z looks the name up in the policy, which lists absolute names only: a relative
// name must get the same label as the absolute one.
#[test]
#[cfg(all(feature = "selinux", any(target_os = "linux", target_os = "android")))]

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.

this belongs to #15336, please rebase once it is merged

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.

Yes, I'll rebase once #15336 is in.

@abendrothj
abendrothj force-pushed the install-context-created-dirs branch from 69c9757 to c28736c Compare October 14, 2026 22:08
Copilot AI balanced review requested due to automatic review settings October 14, 2026 22:08

Copilot AI left a comment

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Comment thread src/uu/install/src/install.rs Outdated
/// Traverses from the given starting path up to existing parent directories.
/// Returns a vector of paths in reverse order (from parent to child).
/// With -Z or --context, label the directories created while the returned guard lives.
/// One policy lookup for `path` serves all of them; GNU looks up each component.

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.

so with -Z the created parents get the leaf's type, e.g. install -d -Z /var/www/cgi-bin/x labels /var/www as cgi content?
GNU looks up each one, could we do the same?

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.

Yes, good catch. Each new directory is now looked up by its own name, as GNU does (create_dir_all_safe takes a hook for it). Checked against GNU 9.12 with /var/www/…/cgi-bin/… as root.

Comment thread tests/by-util/test_install.rs Outdated
Copilot AI balanced review requested due to automatic review settings October 23, 2026 07:56
@abendrothj
abendrothj force-pushed the install-context-created-dirs branch from c28736c to f145794 Compare October 23, 2026 07:56

Copilot AI left a comment

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing. Review the Copilot code review billing settings.

@github-actions

Copy link
Copy Markdown

Binary size comparison:

Individual binary size comparison VS main (threshold: >=5% AND >=4 KB).

Total size of compared binaries: 155.04 MB (-304 KB, -0.19%)

Significant per-binary changes:
  date     2.61 MB ->    2.30 MB  (-316 KB, -11.81%)

@sylvestre
sylvestre force-pushed the install-context-created-dirs branch from f145794 to bb549c8 Compare October 25, 2026 09:31
Copilot AI balanced review requested due to automatic review settings October 25, 2026 09:31

Copilot AI left a comment

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.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing. Review the Copilot code review billing settings.

@sylvestre

Copy link
Copy Markdown
Contributor

rebased on main; after #12715 create_dir_all_with now creates with DEFAULT_MODE, otherwise the umask 0 gave 0777 ancestors

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

install -d/-D with -Z or --context relabels directories that already exist

6 participants