fix(install): zero umask at startup so ancestor dirs are created at exact 0755 - #12715
fix(install): zero umask at startup so ancestor dirs are created at exact 0755#12715abendrothj wants to merge 4 commits into
Conversation
|
GNU testsuite comparison: |
Merging this PR will not alter performance
Comparing Footnotes
|
9cfc786 to
11d3c90
Compare
|
Rebased onto current main, dropped an empty |
…xact 0755 GNU install calls umask(0) early in main() so that ancestor directories created by -D/-d always get exactly DEFAULT_MODE (0755) regardless of the caller's umask. Our implementation passed DEFAULT_MODE to mkdirat / fs::create_dir_all without zeroing umask first, so restrictive umasks (e.g. 0027, 0077, 0111) produced wrong ancestor modes. Changes: - Add zero_umask() to uucore::mode using the existing rustix dependency - Call it at the top of install's uumain (unix only) - Fix the -d code path to use DirBuilder::mode(DEFAULT_MODE) instead of fs::create_dir_all, which defaults to 0777 and would create 0777 ancestors with umask now zeroed - Replace the broken probe-based assertions in two tests with direct assert_eq!(0o40_755) checks, matching the GNU-guaranteed value Fixes uutils#12714 Related: uutils#11363, uutils#12713
11d3c90 to
81625e6
Compare
There was a problem hiding this comment.
Pull request overview
This PR aligns install with GNU behavior by zeroing the process umask early so that ancestor directories created via -D/-d are reliably created at DEFAULT_MODE (0755), independent of the caller’s umask.
Changes:
- Add
uucore::mode::zero_umask()(unix-only) to set umask to 0. - Call
zero_umask()at the start ofinstall::uumainand adjust-ddirectory creation to useDirBuilder::mode(DEFAULT_MODE)(instead ofcreate_dir_all). - Update ancestor-mode tests to assert GNU’s guaranteed
0755behavior for ancestor directories.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| tests/by-util/test_install.rs | Updates ancestor permission assertions to match GNU’s fixed 0755 guarantee. |
| src/uucore/src/lib/features/safe_traversal.rs | Updates docs to clarify umask interaction and the need to zero umask for exact modes. |
| src/uucore/src/lib/features/mode.rs | Adds a unix-only zero_umask() helper based on rustix. |
| src/uu/install/src/install.rs | Zeros umask early; switches -d directory creation to DirBuilder::mode(DEFAULT_MODE); updates comments for new semantics. |
Suppressed comments (2)
tests/by-util/test_install.rs:108
- These assertions now hard-code the expected ancestor mode, but the test no longer exercises the original bug (behavior under a restrictive inherited umask). On typical CI umask=0022, ancestors would still be 0755 even if install didn’t zero umask, so this can pass while regressing. Set a restrictive umask for the spawned
installprocess (and keep the 0755 assertions) to ensure the test fails if zero-umask behavior is removed.
This issue also appears on line 143 of the same file.
ucmd.args(&[mode_arg, directories_arg, target_dir])
.succeeds()
.no_stderr();
tests/by-util/test_install.rs:146
- This test hard-codes 0755 for ancestor dirs, but it doesn’t currently validate the fix under a restrictive inherited umask. On common umask=0022, ancestors would still be 0755 even if
installstopped zeroing umask, so this could pass while regressing. Force a restrictive umask for the spawnedinstallprocess to ensure the test actually exercises the behavior change.
// GNU install zeros umask at startup and creates ancestor dirs at exactly
// 0755 (DEFAULT_MODE). --mode applies only to the final target.
assert_eq!(0o40_755_u32, at.metadata(ancestor1).permissions().mode());
assert_eq!(0o40_755_u32, at.metadata(ancestor2).permissions().mode());
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| // target files are created with exact modes rather than umask-modified ones. | ||
| // chmod is applied explicitly for the target, and ancestors always use | ||
| // DEFAULT_MODE (0755) without umask interference. | ||
| #[cfg(unix)] | ||
| uucore::mode::zero_umask(); |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.
Suppressed comments (2)
src/uucore/src/lib/features/safe_traversal.rs:1307
- This test asserts the created file mode is exactly 0o600, but file creation modes are always masked by the process umask; a restrictive umask could legitimately clear bits and make this assertion fail even if open_file_at_with_mode honors the requested mode. To keep the test robust across environments, assert that no permission bits outside the requested mode are set (umask can only remove bits).
assert_eq!(mode, 0o600);
src/uucore/src/lib/features/safe_traversal.rs:453
- The new open_file_at_with_mode docs imply the provided mode is applied as-is, but the kernel still applies the process umask to O_CREAT modes. Calling this out helps prevent callers from assuming they can get an exact mode without zeroing/restoring umask.
This issue also appears on line 1307 of the same file.
/// The mode is used only when creating a new file; an existing file keeps
/// its current permissions. Callers that will apply final permissions
/// later should use a restrictive initial mode so failures cannot leave a
/// partially-created file overly permissive.
Fixes #12714
Related: #11363, #12713
Root cause
GNU
installcallsumask(0)early inmain()so that ancestor directories created by-D/-dalways get exactlyDEFAULT_MODE(0755) regardless of the caller's umask. uutils passedDEFAULT_MODEtomkdirat/fs::create_dir_allwithout zeroing umask first, so restrictive umasks (e.g.0027,0077,0111) produced wrong ancestor modes.The existing tests masked this because they used a
mkdir-based probe to capture the expected permissions. With umask0002the probe yields0775while install produces0755— causing a spurious test failure that looked like a bug in the tests, not the implementation.Changes
src/uucore/src/lib/features/mode.rszero_umask()using the existingrustixdependency — sets process umask to 0 and returns the old valuesrc/uu/install/src/install.rsuucore::mode::zero_umask()at the top ofuumain(unix only), matching GNU's behaviour-dcode path (directory()) to useDirBuilder::mode(DEFAULT_MODE)instead offs::create_dir_all, which defaults to mode0777and would create0777ancestors now that the umask is zeroedtests/by-util/test_install.rstest_install_ancestors_mode_directoriesandtest_install_ancestors_mode_directories_with_filewithassert_eq!(0o40_755_u32, ...), which is the value GNU actually guaranteesTest plan
cargo test --features unix test_install— all 92 install tests pass0755