diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 9b0c263a2..63243744c 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -278,6 +278,7 @@ Matthew Warman
Matthew Watson
Matthew Yanos
Matthias Bhend
+Michael Barker
Michael Bayne
Michael Clarke
Michael Davey
diff --git a/WHATSNEW b/WHATSNEW
index 4253d63c6..c2c3da6af 100644
--- a/WHATSNEW
+++ b/WHATSNEW
@@ -27,6 +27,11 @@ Fixed bugs:
an incorrect compression level for a zip entry. This is now fixed.
Bugzilla Report 62686
+ * A filesystem "loop" caused due to symbolic links could trigger an
+ out of memory error in the org.apache.tools.ant.DirectoryScanner
+ This has now been fixed.
+ Bugzilla Report 62849
+
Other changes:
--------------
* generatekey task now supports SubjectAlternativeName during key
diff --git a/contributors.xml b/contributors.xml
index ebf03ee77..3c95e6e01 100644
--- a/contributors.xml
+++ b/contributors.xml
@@ -1155,6 +1155,10 @@
Matthias
Bhend
+
+ Michael
+ Barker
+
Michael
Bayne
diff --git a/src/etc/testcases/core/directoryscanner.xml b/src/etc/testcases/core/directoryscanner.xml
index 7e8683a4a..71eb88bf7 100644
--- a/src/etc/testcases/core/directoryscanner.xml
+++ b/src/etc/testcases/core/directoryscanner.xml
@@ -38,4 +38,9 @@
+
+
+
+
+
diff --git a/src/main/org/apache/tools/ant/DirectoryScanner.java b/src/main/org/apache/tools/ant/DirectoryScanner.java
index 71465dbc3..289239b78 100644
--- a/src/main/org/apache/tools/ant/DirectoryScanner.java
+++ b/src/main/org/apache/tools/ant/DirectoryScanner.java
@@ -1237,6 +1237,18 @@ public class DirectoryScanner
final String name = vpath + newFile;
final TokenizedPath newPath = new TokenizedPath(path, newFile);
final File file = new File(dir, newFile);
+
+ try {
+ // check if it's a filesystem "loop" due to symbolic links
+ if (FileUtils.getFileUtils().isLeadingPath(file.getAbsoluteFile(),
+ dir.getAbsoluteFile(), true)) {
+ continue;
+ }
+ } catch (IOException e) {
+ System.err.println("Failed to determine if " + file + " causes a " +
+ "filesystem loop due to symbolic link; continuing");
+ }
+
final String[] children = file.list();
if (children == null || (children.length == 0 && file.isFile())) {
if (isIncluded(newPath)) {
diff --git a/src/main/org/apache/tools/ant/taskdefs/Delete.java b/src/main/org/apache/tools/ant/taskdefs/Delete.java
index 22eefa132..bf810e869 100644
--- a/src/main/org/apache/tools/ant/taskdefs/Delete.java
+++ b/src/main/org/apache/tools/ant/taskdefs/Delete.java
@@ -18,6 +18,7 @@
package org.apache.tools.ant.taskdefs;
+import java.io.IOException;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -814,7 +815,19 @@ public class Delete extends MatchingTask {
}
for (String s : list) {
File f = new File(d, s);
- if (f.isDirectory()) {
+
+ boolean isFsLoop = false;
+
+ try {
+ isFsLoop = Files.isSymbolicLink(f.toPath()) &&
+ FileUtils.getFileUtils().isLeadingPath(f.getAbsoluteFile(),
+ d.getAbsoluteFile(), true);
+ } catch (IOException e) {
+ log("Failed to check if " + f + " causes a filesystem loop due to " +
+ "symbolic link; continuing");
+ }
+
+ if (f.isDirectory() && !isFsLoop) {
removeDir(f);
} else {
log("Deleting " + f.getAbsolutePath(), quiet ? Project.MSG_VERBOSE : verbosity);
diff --git a/src/tests/junit/org/apache/tools/ant/DirectoryScannerTest.java b/src/tests/junit/org/apache/tools/ant/DirectoryScannerTest.java
index 6dab26c11..e332a9c83 100644
--- a/src/tests/junit/org/apache/tools/ant/DirectoryScannerTest.java
+++ b/src/tests/junit/org/apache/tools/ant/DirectoryScannerTest.java
@@ -130,6 +130,20 @@ public class DirectoryScannerTest {
new String[] {"alpha/beta/gamma"});
}
+ @Test
+ public void testAllowRecursiveSymlinks() {
+
+ assumeTrue("Current system does not support Symlinks", supportsSymlinks);
+
+ buildRule.getProject().executeTarget("symlink-nested-setup");
+ DirectoryScanner ds = new DirectoryScanner();
+ ds.setBasedir(new File(buildRule.getProject().getProperty("output")));
+ ds.setIncludes(new String[] {"alpha/beta/gamma/"});
+ ds.scan();
+ compareFiles(ds, new String[] {"alpha/beta/gamma/gamma.xml"},
+ new String[] {"alpha/beta/gamma"});
+ }
+
@Test
public void testProhibitSymlinks() {
assumeTrue("Current system does not support Symlinks", supportsSymlinks);