Skip to content

Commit 8a9476f

Browse files
authored
[REPL] Fix shell completion error on unreadable symlink (#51851)
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.
1 parent 199cac7 commit 8a9476f

File tree

2 files changed

+31
-13
lines changed

2 files changed

+31
-13
lines changed

stdlib/REPL/src/REPLCompletions.jl

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -329,8 +329,19 @@ function complete_path(path::AbstractString, pos::Int;
329329
for file in filesinpath
330330
# In a perfect world, we would filter on whether the file is executable
331331
# here, or even on whether the current user can execute the file in question.
332-
if startswith(file, prefix) && isfile(joinpath(pathdir, file))
333-
push!(matches, file)
332+
try
333+
if startswith(file, prefix) && isfile(joinpath(pathdir, file))
334+
push!(matches, file)
335+
end
336+
catch e
337+
# `isfile()` can throw in rare cases such as when probing a
338+
# symlink that points to a file within a directory we do not
339+
# have read access to.
340+
if isa(e, Base.IOError)
341+
continue
342+
else
343+
rethrow()
344+
end
334345
end
335346
end
336347
end

stdlib/REPL/test/replcompletions.jl

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,28 +1119,35 @@ let s, c, r
11191119
end
11201120

11211121
# Tests detecting of files in the env path (in shell mode)
1122-
let path, file
1123-
path = tempdir()
1124-
unreadable = joinpath(tempdir(), "replcompletion-unreadable")
1122+
mktempdir() do path
1123+
unreadable = joinpath(path, "replcompletion-unreadable")
1124+
file = joinpath(path, "tmp-executable")
1125+
touch(file)
1126+
chmod(file, 0o755)
1127+
mkdir(unreadable)
1128+
hidden_file = joinpath(unreadable, "hidden")
1129+
touch(hidden_file)
11251130

1126-
try
1127-
file = joinpath(path, "tmp-executable")
1128-
touch(file)
1129-
chmod(file, 0o755)
1130-
mkdir(unreadable)
1131-
chmod(unreadable, 0o000)
1131+
# Create symlink to a file that is in an unreadable directory
1132+
chmod(hidden_file, 0o755)
1133+
chmod(unreadable, 0o000)
1134+
symlink(hidden_file, joinpath(path, "replcompletions-link"))
11321135

1136+
try
11331137
# PATH can also contain folders which we aren't actually allowed to read.
11341138
withenv("PATH" => string(path, ":", unreadable)) do
11351139
s = "tmp-execu"
11361140
c,r = test_scomplete(s)
11371141
@test "tmp-executable" in c
11381142
@test r == 1:9
11391143
@test s[r] == "tmp-execu"
1144+
1145+
c,r = test_scomplete("replcompletions-link")
1146+
@test isempty(c)
11401147
end
11411148
finally
1142-
rm(file)
1143-
rm(unreadable)
1149+
# If we don't fix the permissions here, our cleanup fails.
1150+
chmod(unreadable, 0o700)
11441151
end
11451152
end
11461153

0 commit comments

Comments
 (0)