Skip to content

Commit 1ee59b3

Browse files
authored
Fix listDirectory() to follow symbolic links (#6821)
1 parent 7140030 commit 1ee59b3

File tree

2 files changed

+50
-3
lines changed

2 files changed

+50
-3
lines changed

modules/nf-commons/src/main/nextflow/extension/FilesEx.groovy

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import java.nio.ByteBuffer
2222
import java.nio.channels.SeekableByteChannel
2323
import java.nio.file.FileAlreadyExistsException
2424
import java.nio.file.FileSystemException
25+
import java.nio.file.FileVisitOption
2526
import java.nio.file.FileVisitResult
2627
import java.nio.file.Files
2728
import java.nio.file.LinkOption
@@ -649,17 +650,18 @@ class FilesEx {
649650
* @param self
650651
*/
651652
static Collection<Path> listDirectory(Path self) {
652-
return listFiles0(self)
653+
return listFiles0(self, null, true)
653654
}
654655

655-
private static Collection<Path> listFiles0(Path self, Closure<Boolean> filter=null) {
656+
private static Collection<Path> listFiles0(Path self, Closure<Boolean> filter=null, boolean followLinks=false) {
656657

657658
if( !self.isDirectory() )
658659
return null
659660

660661
final result = []
662+
final opts = followLinks ? EnumSet.of(FileVisitOption.FOLLOW_LINKS) : EnumSet.noneOf(FileVisitOption)
661663

662-
Files.walkFileTree(self, new SimpleFileVisitor<Path>() {
664+
Files.walkFileTree(self, opts, Integer.MAX_VALUE, new SimpleFileVisitor<Path>() {
663665

664666
FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
665667
if( filter == null || invokeFilter(filter, file, attrs) )

modules/nf-commons/src/test/nextflow/extension/FilesExTest.groovy

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,51 @@ class FilesExTest extends Specification {
603603
sourceFolder.deleteDir()
604604
}
605605

606+
def 'listFiles should not follow symlinked directory' () {
607+
608+
given:
609+
def folder = Files.createTempDirectory('test')
610+
def realDir = folder.resolve('realDir')
611+
Files.createDirectories(realDir)
612+
realDir.resolve('file1.txt').text = 'Hello1'
613+
realDir.resolve('file2.txt').text = 'Hello2'
614+
def linkDir = folder.resolve('linkDir')
615+
Files.createSymbolicLink(linkDir, realDir)
616+
617+
when:
618+
def paths = linkDir.listFiles()
619+
620+
then:
621+
// without FOLLOW_LINKS, walkFileTree treats the symlink as a file
622+
paths.size() == 1
623+
paths[0] == linkDir
624+
625+
cleanup:
626+
folder?.deleteDir()
627+
}
628+
629+
def 'listDirectory should follow symlinked directory' () {
630+
631+
given:
632+
def folder = Files.createTempDirectory('test')
633+
def realDir = folder.resolve('realDir')
634+
Files.createDirectories(realDir)
635+
realDir.resolve('file1.txt').text = 'Hello1'
636+
realDir.resolve('file2.txt').text = 'Hello2'
637+
def linkDir = folder.resolve('linkDir')
638+
Files.createSymbolicLink(linkDir, realDir)
639+
640+
when:
641+
def paths = linkDir.listDirectory()
642+
643+
then:
644+
paths.size() == 2
645+
paths.collect { it.name }.sort() == ['file1.txt', 'file2.txt']
646+
647+
cleanup:
648+
folder?.deleteDir()
649+
}
650+
606651

607652
def testSetReadonly() {
608653

0 commit comments

Comments
 (0)