Skip to content

Commit 178ac97

Browse files
authored
walkdir: avoid symlink loops when follow_symlinks == false (#35006)
* walkdir: avoid symlink loops when `follow_symlinks == false` Because `isdir()` attempts to dereference symlinks, attempting to `walkdir()` trees that contain symlink loops errors out. This change modifies `walkdir()` to treat all symlinks as files when `follow_symlinks == false`. * rm: When checking `filemode()`, use `lstat()` to avoid following symlinks
1 parent 4ee9be2 commit 178ac97

File tree

2 files changed

+43
-8
lines changed

2 files changed

+43
-8
lines changed

base/file.jl

+7-4
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ function rm(path::AbstractString; force::Bool=false, recursive::Bool=false)
261261
try
262262
@static if Sys.iswindows()
263263
# is writable on windows actually means "is deletable"
264-
if (filemode(path) & 0o222) == 0
264+
if (filemode(lstat(path)) & 0o222) == 0
265265
chmod(path, 0o777)
266266
end
267267
end
@@ -853,10 +853,13 @@ function walkdir(root; topdown=true, follow_symlinks=false, onerror=throw)
853853
dirs = Vector{eltype(content)}()
854854
files = Vector{eltype(content)}()
855855
for name in content
856-
if isdir(joinpath(root, name))
857-
push!(dirs, name)
858-
else
856+
path = joinpath(root, name)
857+
858+
# If we're not following symlinks, then treat all symlinks as files
859+
if (!follow_symlinks && islink(path)) || !isdir(path)
859860
push!(files, name)
861+
else
862+
push!(dirs, name)
860863
end
861864
end
862865

test/file.jl

+36-4
Original file line numberDiff line numberDiff line change
@@ -1218,8 +1218,18 @@ cd(dirwalk) do
12181218

12191219
root, dirs, files = take!(chnl)
12201220
@test root == joinpath(".", "sub_dir1")
1221-
@test dirs == (has_symlinks ? ["link", "subsub_dir1", "subsub_dir2"] : ["subsub_dir1", "subsub_dir2"])
1222-
@test files == ["file1", "file2"]
1221+
if has_symlinks
1222+
if follow_symlinks
1223+
@test dirs == ["link", "subsub_dir1", "subsub_dir2"]
1224+
@test files == ["file1", "file2"]
1225+
else
1226+
@test dirs == ["subsub_dir1", "subsub_dir2"]
1227+
@test files == ["file1", "file2", "link"]
1228+
end
1229+
else
1230+
@test dirs == ["subsub_dir1", "subsub_dir2"]
1231+
@test files == ["file1", "file2"]
1232+
end
12231233

12241234
root, dirs, files = take!(chnl)
12251235
if follow_symlinks
@@ -1256,8 +1266,18 @@ cd(dirwalk) do
12561266
root, dirs, files = take!(chnl)
12571267
end
12581268
@test root == joinpath(".", "sub_dir1")
1259-
@test dirs == (has_symlinks ? ["link", "subsub_dir1", "subsub_dir2"] : ["subsub_dir1", "subsub_dir2"])
1260-
@test files == ["file1", "file2"]
1269+
if has_symlinks
1270+
if follow_symlinks
1271+
@test dirs == ["link", "subsub_dir1", "subsub_dir2"]
1272+
@test files == ["file1", "file2"]
1273+
else
1274+
@test dirs == ["subsub_dir1", "subsub_dir2"]
1275+
@test files == ["file1", "file2", "link"]
1276+
end
1277+
else
1278+
@test dirs == ["subsub_dir1", "subsub_dir2"]
1279+
@test files == ["file1", "file2"]
1280+
end
12611281

12621282
root, dirs, files = take!(chnl)
12631283
@test root == joinpath(".", "sub_dir2")
@@ -1289,6 +1309,18 @@ cd(dirwalk) do
12891309
@test root == joinpath(".", "sub_dir2")
12901310
@test dirs == []
12911311
@test files == ["file_dir2"]
1312+
1313+
# Test that symlink loops don't cause errors
1314+
if has_symlinks
1315+
mkdir(joinpath(".", "sub_dir3"))
1316+
symlink("foo", joinpath(".", "sub_dir3", "foo"))
1317+
1318+
@test_throws Base.IOError walkdir(joinpath(".", "sub_dir3"); follow_symlinks=true)
1319+
root, dirs, files = take!(walkdir(joinpath(".", "sub_dir3"); follow_symlinks=false))
1320+
@test root == joinpath(".", "sub_dir3")
1321+
@test dirs == []
1322+
@test files == ["foo"]
1323+
end
12921324
end
12931325
rm(dirwalk, recursive=true)
12941326

0 commit comments

Comments
 (0)