Skip to content

Commit

Permalink
[REPL] Fix shell completion error on unreadable symlink (JuliaLang#51851
Browse files Browse the repository at this point in the history
)

In macOS there is a file, `/usr/sbin/weakpass_edit` that is a symlink to
a directory that is only readable by `root`. Our REPL shell completion
attempts to call `isfile()` when entering the shell mode and pressing
`w`, throwing an ugly error into the REPL.

This PR fixes the root cause (skipping files that fail the `isfile()`
condition) and adds a test that synthesizes an analogous condition.
  • Loading branch information
staticfloat committed Oct 27, 2023
1 parent 199cac7 commit 8a9476f
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 13 deletions.
15 changes: 13 additions & 2 deletions stdlib/REPL/src/REPLCompletions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -329,8 +329,19 @@ function complete_path(path::AbstractString, pos::Int;
for file in filesinpath
# In a perfect world, we would filter on whether the file is executable
# here, or even on whether the current user can execute the file in question.
if startswith(file, prefix) && isfile(joinpath(pathdir, file))
push!(matches, file)
try
if startswith(file, prefix) && isfile(joinpath(pathdir, file))
push!(matches, file)
end
catch e
# `isfile()` can throw in rare cases such as when probing a
# symlink that points to a file within a directory we do not
# have read access to.
if isa(e, Base.IOError)
continue
else
rethrow()
end
end
end
end
Expand Down
29 changes: 18 additions & 11 deletions stdlib/REPL/test/replcompletions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1119,28 +1119,35 @@ let s, c, r
end

# Tests detecting of files in the env path (in shell mode)
let path, file
path = tempdir()
unreadable = joinpath(tempdir(), "replcompletion-unreadable")
mktempdir() do path
unreadable = joinpath(path, "replcompletion-unreadable")
file = joinpath(path, "tmp-executable")
touch(file)
chmod(file, 0o755)
mkdir(unreadable)
hidden_file = joinpath(unreadable, "hidden")
touch(hidden_file)

try
file = joinpath(path, "tmp-executable")
touch(file)
chmod(file, 0o755)
mkdir(unreadable)
chmod(unreadable, 0o000)
# Create symlink to a file that is in an unreadable directory
chmod(hidden_file, 0o755)
chmod(unreadable, 0o000)
symlink(hidden_file, joinpath(path, "replcompletions-link"))

try
# PATH can also contain folders which we aren't actually allowed to read.
withenv("PATH" => string(path, ":", unreadable)) do
s = "tmp-execu"
c,r = test_scomplete(s)
@test "tmp-executable" in c
@test r == 1:9
@test s[r] == "tmp-execu"

c,r = test_scomplete("replcompletions-link")
@test isempty(c)
end
finally
rm(file)
rm(unreadable)
# If we don't fix the permissions here, our cleanup fails.
chmod(unreadable, 0o700)
end
end

Expand Down

0 comments on commit 8a9476f

Please sign in to comment.