You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
create_dir_all_safe now returns the failing prefix along with its error, and the ancestor search walks up past files, symlink loops and unreadable directories instead of giving up with the whole path. A name that exists but can't be descended into reports EEXIST when it doesn't resolve and ENOTDIR when it resolves to a non-directory, which is what GNU prints. When the parent of the failing component can't be searched at all, that parent is named instead - the difference between r--/x, which GNU reports as r--, and r-x/x, which it reports as r-x/x. The message string gained a place for the errno.
install -d keeps creating directories path-based. I tried routing it through the same fd walk and it broke write-only directories: DirFd::open needs read permission, mkdir only needs write and execute, so install -d wx-dir/sub started failing where GNU succeeds. There's a regression test for that now. -D has had the same limitation since it moved to the fd walk, which is #14778; this PR does not change it.
Verified on Linux against GNU 9.10: -D messages match over 23 invocations covering symlinks, dangling symlinks, plain files, symlink loops, unreadable, read-only and write-only parents, trailing and doubled slashes. No GNU source was read.
Conflicts with #12715 in the two -d hunks. Whichever lands second resolves to with_umask(0, || create_dir_all_safe(&path_to_create, DEFAULT_MODE)) with show!(InstallError::CreateDirFailed(e.path, e.error)).
The reason will be displayed to describe this comment to others. Learn more.
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Nothing converts a CreateDirError into an io::Error, and doing so would
silently drop the path the type exists to carry. Also document why the
access(2) call in blame() is sound.
Report the failing path component for nested mkdir errors
src/uu/install/src/install.rs:517
install -d still passes the entire path_to_create as the error path, so a nested failure such as install -d dangling/sub (or regular/sub where regular is a file) will continue to report dangling/sub/regular/sub instead of the failing component. Keep the path-based mkdir behavior for write-only parents, but track the component that failed (or add a path-based helper that returns it) before constructing CreateDirFailed.
The reason will be displayed to describe this comment to others. Learn more.
It only decides which error gets printed. If the target is an existing non-directory, install stops with "Not a directory"; otherwise nothing is done with the result. If the path changes after the check, the directories are still created through create_dir_all_safe, which works through directory descriptors (mkdirat, openat with O_DIRECTORY) and refuses a file or dangling symlink at that name by itself. So a race can only change which message you get, to "cannot create directory". The check isn't new either: main already does the same exists()/is_dir() check on the -t target; this moves it after the trailing-slash trim.
`install -d` creates its directories by path, so it still reported the
whole path when that failed, e.g. 'dangling/sub' where GNU names
'dangling'. Name the first component that is not an existing directory,
through the same blame logic create_dir_all_safe uses for -D.
The new blame branch is what implements the documented r--/x versus r-x/x distinction, but the added tests only cover a regular file, a dangling symlink, and the successful -d write-only case. Add an integration test for an unsearchable parent (and ideally the loop/read-only error cases described by the PR) so this error-path naming behavior cannot regress.
The parent-versus-component choice in blame() had no test: cover a parent
that cannot be searched (named) against one that can (the component is
named), and a symlink loop in the leading directories, for -D and -d.
Remove GNU test path reference from PR description
tests/by-util/test_install.rs:2891
The PR description references the GNU test path tests/install/basic-1. Please remove that path reference and summarize the observed behavior instead; this repository must not include references to GNU source/test paths.
This branch has not been deployed
No deployments
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
installnamed the whole leading-directory path instead of the component that failed, and with-Dgave no reason at all:create_dir_all_safenow returns the failing prefix along with its error, and the ancestor search walks up past files, symlink loops and unreadable directories instead of giving up with the whole path. A name that exists but can't be descended into reports EEXIST when it doesn't resolve and ENOTDIR when it resolves to a non-directory, which is what GNU prints. When the parent of the failing component can't be searched at all, that parent is named instead - the difference betweenr--/x, which GNU reports asr--, andr-x/x, which it reports asr-x/x. The message string gained a place for the errno.install -dkeeps creating directories path-based. I tried routing it through the same fd walk and it broke write-only directories:DirFd::openneeds read permission,mkdironly needs write and execute, soinstall -d wx-dir/substarted failing where GNU succeeds. There's a regression test for that now.-Dhas had the same limitation since it moved to the fd walk, which is #14778; this PR does not change it.Verified on Linux against GNU 9.10:
-Dmessages match over 23 invocations covering symlinks, dangling symlinks, plain files, symlink loops, unreadable, read-only and write-only parents, trailing and doubled slashes. No GNU source was read.Closes #14995