Skip to content

Commit 4ac75f3

Browse files
authored
Merge pull request #30 from arduino/improve_recursive_dirread
Do not stop `ReadDirRecursive*` on broken symlinks
2 parents 3226a11 + 075c5c8 commit 4ac75f3

File tree

18 files changed

+95
-32
lines changed

18 files changed

+95
-32
lines changed

Diff for: list.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ func (p *PathList) FilterDirs() {
7878
func (p *PathList) FilterOutDirs() {
7979
res := (*p)[:0]
8080
for _, path := range *p {
81-
if path.IsNotDir() {
81+
if !path.IsDir() {
8282
res = append(res, path)
8383
}
8484
}
@@ -96,7 +96,7 @@ func (p *PathList) FilterOutHiddenFiles() {
9696
func (p *PathList) Filter(acceptorFunc func(*Path) bool) {
9797
res := (*p)[:0]
9898
for _, path := range *p {
99-
if acceptorFunc(path) {
99+
if acceptorFunc(path) {
100100
res = append(res, path)
101101
}
102102
}

Diff for: paths.go

+7
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,13 @@ func (p *Path) CopyDirTo(dst *Path) error {
418418
return nil
419419
}
420420

421+
// Chmod changes the mode of the named file to mode. If the file is a
422+
// symbolic link, it changes the mode of the link's target. If there
423+
// is an error, it will be of type *os.PathError.
424+
func (p *Path) Chmod(mode fs.FileMode) error {
425+
return os.Chmod(p.path, mode)
426+
}
427+
421428
// Chtimes changes the access and modification times of the named file,
422429
// similar to the Unix utime() or utimes() functions.
423430
func (p *Path) Chtimes(atime, mtime time.Time) error {

Diff for: paths_test.go

+40-16
Original file line numberDiff line numberDiff line change
@@ -284,25 +284,49 @@ func TestFilterDirs(t *testing.T) {
284284
}
285285

286286
func TestFilterOutDirs(t *testing.T) {
287-
testPath := New("testdata", "fileset")
287+
{
288+
testPath := New("testdata", "fileset")
288289

289-
list, err := testPath.ReadDir()
290-
require.NoError(t, err)
291-
require.Len(t, list, 6)
290+
list, err := testPath.ReadDir()
291+
require.NoError(t, err)
292+
require.Len(t, list, 6)
293+
294+
pathEqualsTo(t, "testdata/fileset/anotherFile", list[0])
295+
pathEqualsTo(t, "testdata/fileset/file", list[1])
296+
pathEqualsTo(t, "testdata/fileset/folder", list[2])
297+
pathEqualsTo(t, "testdata/fileset/symlinktofolder", list[3])
298+
pathEqualsTo(t, "testdata/fileset/test.txt", list[4])
299+
pathEqualsTo(t, "testdata/fileset/test.txt.gz", list[5])
300+
301+
list.FilterOutDirs()
302+
require.Len(t, list, 4)
303+
pathEqualsTo(t, "testdata/fileset/anotherFile", list[0])
304+
pathEqualsTo(t, "testdata/fileset/file", list[1])
305+
pathEqualsTo(t, "testdata/fileset/test.txt", list[2])
306+
pathEqualsTo(t, "testdata/fileset/test.txt.gz", list[3])
307+
}
292308

293-
pathEqualsTo(t, "testdata/fileset/anotherFile", list[0])
294-
pathEqualsTo(t, "testdata/fileset/file", list[1])
295-
pathEqualsTo(t, "testdata/fileset/folder", list[2])
296-
pathEqualsTo(t, "testdata/fileset/symlinktofolder", list[3])
297-
pathEqualsTo(t, "testdata/fileset/test.txt", list[4])
298-
pathEqualsTo(t, "testdata/fileset/test.txt.gz", list[5])
309+
{
310+
list, err := New("testdata", "broken_symlink", "dir_1").ReadDirRecursive()
311+
require.NoError(t, err)
299312

300-
list.FilterOutDirs()
301-
require.Len(t, list, 4)
302-
pathEqualsTo(t, "testdata/fileset/anotherFile", list[0])
303-
pathEqualsTo(t, "testdata/fileset/file", list[1])
304-
pathEqualsTo(t, "testdata/fileset/test.txt", list[2])
305-
pathEqualsTo(t, "testdata/fileset/test.txt.gz", list[3])
313+
require.Len(t, list, 7)
314+
pathEqualsTo(t, "testdata/broken_symlink/dir_1/broken_link", list[0])
315+
pathEqualsTo(t, "testdata/broken_symlink/dir_1/file2", list[1])
316+
pathEqualsTo(t, "testdata/broken_symlink/dir_1/linked_dir", list[2])
317+
pathEqualsTo(t, "testdata/broken_symlink/dir_1/linked_dir/file1", list[3])
318+
pathEqualsTo(t, "testdata/broken_symlink/dir_1/linked_file", list[4])
319+
pathEqualsTo(t, "testdata/broken_symlink/dir_1/real_dir", list[5])
320+
pathEqualsTo(t, "testdata/broken_symlink/dir_1/real_dir/file1", list[6])
321+
322+
list.FilterOutDirs()
323+
require.Len(t, list, 5)
324+
pathEqualsTo(t, "testdata/broken_symlink/dir_1/broken_link", list[0])
325+
pathEqualsTo(t, "testdata/broken_symlink/dir_1/file2", list[1])
326+
pathEqualsTo(t, "testdata/broken_symlink/dir_1/linked_dir/file1", list[2])
327+
pathEqualsTo(t, "testdata/broken_symlink/dir_1/linked_file", list[3])
328+
pathEqualsTo(t, "testdata/broken_symlink/dir_1/real_dir/file1", list[4])
329+
}
306330
}
307331

308332
func TestEquivalentPaths(t *testing.T) {

Diff for: readdir.go

+1-3
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,7 @@ func (p *Path) ReadDirRecursiveFiltered(recursionFilter ReadDirFilter, filters .
116116
}
117117

118118
if recursionFilter == nil || recursionFilter(path) {
119-
if isDir, err := path.IsDirCheck(); err != nil {
120-
return nil, err
121-
} else if isDir {
119+
if path.IsDir() {
122120
subPaths, err := search(path)
123121
if err != nil {
124122
return nil, err

Diff for: readdir_test.go

+38-11
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ package paths
3131

3232
import (
3333
"fmt"
34+
"io/fs"
3435
"os"
36+
"runtime"
3537
"testing"
3638
"time"
3739

@@ -250,21 +252,11 @@ func TestReadDirRecursiveFiltered(t *testing.T) {
250252
func TestReadDirRecursiveLoopDetection(t *testing.T) {
251253
loopsPath := New("testdata", "loops")
252254
unbuondedReaddir := func(testdir string) (PathList, error) {
253-
// This is required to unbound the recursion, otherwise it will stop
254-
// when the paths becomes too long due to the symlink loop: this is not
255-
// what we want, we are looking for an early detection of the loop.
256-
skipBrokenLinks := func(p *Path) bool {
257-
_, err := p.Stat()
258-
return err == nil
259-
}
260-
261255
var files PathList
262256
var err error
263257
done := make(chan bool)
264258
go func() {
265-
files, err = loopsPath.Join(testdir).ReadDirRecursiveFiltered(
266-
skipBrokenLinks,
267-
)
259+
files, err = loopsPath.Join(testdir).ReadDirRecursive()
268260
done <- true
269261
}()
270262
require.Eventually(
@@ -313,4 +305,39 @@ func TestReadDirRecursiveLoopDetection(t *testing.T) {
313305
pathEqualsTo(t, "testdata/loops/regular_2/dir2/dir1/file1", l[4])
314306
pathEqualsTo(t, "testdata/loops/regular_2/dir2/file2", l[5])
315307
}
308+
309+
{
310+
l, err := unbuondedReaddir("regular_3")
311+
require.NoError(t, err)
312+
require.Len(t, l, 7)
313+
l.Sort()
314+
pathEqualsTo(t, "testdata/loops/regular_3/dir1", l[0])
315+
pathEqualsTo(t, "testdata/loops/regular_3/dir1/file1", l[1])
316+
pathEqualsTo(t, "testdata/loops/regular_3/dir2", l[2])
317+
pathEqualsTo(t, "testdata/loops/regular_3/dir2/dir1", l[3])
318+
pathEqualsTo(t, "testdata/loops/regular_3/dir2/dir1/file1", l[4])
319+
pathEqualsTo(t, "testdata/loops/regular_3/dir2/file2", l[5])
320+
pathEqualsTo(t, "testdata/loops/regular_3/link", l[6]) // broken symlink is reported in files
321+
}
322+
323+
if runtime.GOOS != "windows" {
324+
dir1 := loopsPath.Join("regular_4_with_permission_error", "dir1")
325+
326+
l, err := unbuondedReaddir("regular_4_with_permission_error")
327+
require.NoError(t, err)
328+
require.NotEmpty(t, l)
329+
330+
dir1Stat, err := dir1.Stat()
331+
require.NoError(t, err)
332+
err = dir1.Chmod(fs.FileMode(0)) // Enforce permission error
333+
require.NoError(t, err)
334+
t.Cleanup(func() {
335+
// Restore normal permission after the test
336+
dir1.Chmod(dir1Stat.Mode())
337+
})
338+
339+
l, err = unbuondedReaddir("regular_4_with_permission_error")
340+
require.Error(t, err)
341+
require.Nil(t, l)
342+
}
316343
}

Diff for: testdata/broken_symlink/dir_1/broken_link

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
broken

Diff for: testdata/broken_symlink/dir_1/file2

Whitespace-only changes.

Diff for: testdata/broken_symlink/dir_1/linked_dir

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
real_dir

Diff for: testdata/broken_symlink/dir_1/linked_file

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
file2

Diff for: testdata/broken_symlink/dir_1/real_dir/file1

Whitespace-only changes.

Diff for: testdata/loops/regular_3/dir1/file1

Whitespace-only changes.

Diff for: testdata/loops/regular_3/dir2/dir1

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../dir1

Diff for: testdata/loops/regular_3/dir2/file2

Whitespace-only changes.

Diff for: testdata/loops/regular_3/link

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
broken

Diff for: testdata/loops/regular_4_with_permission_error/dir1/file1

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../dir1

Diff for: testdata/loops/regular_4_with_permission_error/dir2/file2

Whitespace-only changes.

Diff for: testdata/loops/regular_4_with_permission_error/link

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
broken

0 commit comments

Comments
 (0)