From 87ac51d3c22bcf7cfd0dc07cb0bd04a496e0d428 Mon Sep 17 00:00:00 2001
From: Stefan Bodewig <bodewig@apache.org>
Date: Sat, 4 Jul 2020 18:03:13 +0200
Subject: [PATCH] fallback to a separate owner-only tempdir if possible

---
 src/main/org/apache/tools/ant/MagicNames.java | 10 +++
 .../org/apache/tools/ant/util/FileUtils.java  | 36 +++++++++--
 .../apache/tools/ant/util/FileUtilsTest.java  | 64 +++++++++++++++++++
 3 files changed, 105 insertions(+), 5 deletions(-)

diff --git a/src/main/org/apache/tools/ant/MagicNames.java b/src/main/org/apache/tools/ant/MagicNames.java
index 5cf2fa8fa3..8ced505789 100644
--- a/src/main/org/apache/tools/ant/MagicNames.java
+++ b/src/main/org/apache/tools/ant/MagicNames.java
@@ -337,5 +337,15 @@ private MagicNames() {
      * @since Ant 1.10.8
      */
     public static final String TMPDIR = "ant.tmpdir";
+
+    /**
+     * Magic property that will be set to override java.io.tmpdir
+     * system property as the location for Ant's default temporary
+     * directory if a temp file is created and {@link #TMPDIR} is not
+     * set.
+     * Value: {@value}
+     * @since Ant 1.10.9
+     */
+    public static final String AUTO_TMPDIR = "ant.auto.tmpdir";
 }
 
diff --git a/src/main/org/apache/tools/ant/util/FileUtils.java b/src/main/org/apache/tools/ant/util/FileUtils.java
index 46671848c9..d835438fe7 100644
--- a/src/main/org/apache/tools/ant/util/FileUtils.java
+++ b/src/main/org/apache/tools/ant/util/FileUtils.java
@@ -110,6 +110,11 @@
             PosixFilePermissions.asFileAttribute(EnumSet.of(PosixFilePermission.OWNER_READ,
                 PosixFilePermission.OWNER_WRITE))
         };
+    private static final FileAttribute[] TMPDIR_ATTRIBUTES =
+        new FileAttribute[] {
+            PosixFilePermissions.asFileAttribute(EnumSet.of(PosixFilePermission.OWNER_READ,
+                PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_EXECUTE))
+        };
     private static final FileAttribute[] NO_TMPFILE_ATTRIBUTES = new FileAttribute[0];
 
     /**
@@ -991,14 +996,35 @@ public File createTempFile(String prefix, String suffix, File parentDir,
     public File createTempFile(final Project project, String prefix, String suffix,
             final File parentDir, final boolean deleteOnExit, final boolean createFile) {
         File result;
-        final String parent;
+        String p = null;
         if (parentDir != null) {
-            parent = parentDir.getPath();
+            p = parentDir.getPath();
         } else if (project != null && project.getProperty(MagicNames.TMPDIR) != null) {
-            parent = project.getProperty(MagicNames.TMPDIR);
-        } else {
-            parent = System.getProperty("java.io.tmpdir");
+            p = project.getProperty(MagicNames.TMPDIR);
+        } else if (project != null && deleteOnExit) {
+            if (project.getProperty(MagicNames.AUTO_TMPDIR) != null) {
+                p = project.getProperty(MagicNames.AUTO_TMPDIR);
+            } else {
+                final Path systemTempDirPath =
+                    new File(System.getProperty("java.io.tmpdir")).toPath();
+                final PosixFileAttributeView systemTempDirPosixAttributes =
+                    Files.getFileAttributeView(systemTempDirPath, PosixFileAttributeView.class);
+                if (systemTempDirPosixAttributes != null) {
+                    // no reason to create an extra temp dir if we cannot set permissions
+                    try {
+                        final File projectTempDir = Files.createTempDirectory(systemTempDirPath,
+                            "ant", TMPDIR_ATTRIBUTES)
+                            .toFile();
+                        projectTempDir.deleteOnExit();
+                        p = projectTempDir.getAbsolutePath();
+                        project.setProperty(MagicNames.AUTO_TMPDIR, p);
+                    } catch (IOException ex) {
+                        // silently fall back to system temp directory
+                    }
+                }
+            }
         }
+        final String parent = p != null ? p : System.getProperty("java.io.tmpdir");
         if (prefix == null) {
             prefix = NULL_PLACEHOLDER;
         }
diff --git a/src/tests/junit/org/apache/tools/ant/util/FileUtilsTest.java b/src/tests/junit/org/apache/tools/ant/util/FileUtilsTest.java
index da46520038..d5448a6ac6 100644
--- a/src/tests/junit/org/apache/tools/ant/util/FileUtilsTest.java
+++ b/src/tests/junit/org/apache/tools/ant/util/FileUtilsTest.java
@@ -50,6 +50,8 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeFalse;
@@ -450,6 +452,68 @@ public void testCreateTempFile() throws IOException {
                 tmp2.getAbsolutePath()));
     }
 
+    @Test
+    public void createTempFileUsesAntTmpDirIfSetAndDeleteOnExitIsTrue() throws IOException {
+        final Project project = new Project();
+        final File projectTmpDir = folder.newFolder("subdir");
+        project.setProperty("ant.tmpdir", projectTmpDir.getAbsolutePath());
+        final File tmpFile = getFileUtils().createTempFile(project, null, null, null, true, true);
+        assertTrue(tmpFile + " must be child of " + projectTmpDir,
+                   tmpFile.getAbsolutePath().startsWith(projectTmpDir.getAbsolutePath()));
+    }
+
+    @Test
+    public void createTempFileUsesAntTmpDirIfSetAndDeleteOnExitIsFalse() throws IOException {
+        final Project project = new Project();
+        final File projectTmpDir = folder.newFolder("subdir");
+        project.setProperty("ant.tmpdir", projectTmpDir.getAbsolutePath());
+        final File tmpFile = getFileUtils().createTempFile(project, null, null, null, false, true);
+        assertTrue(tmpFile + " must be child of " + projectTmpDir,
+                   tmpFile.getAbsolutePath().startsWith(projectTmpDir.getAbsolutePath()));
+    }
+
+    @Test
+    public void createTempFileCreatesAutoTmpDirIfDeleteOnExitIsTrueOnUnix() throws IOException {
+        assumeFalse("Test doesn't run on DOS", Os.isFamily("dos"));
+        final Project project = new Project();
+        final File tmpFile = getFileUtils().createTempFile(project, null, null, null, true, true);
+        final String autoTempDir = project.getProperty("ant.auto.tmpdir");
+        assertNotNull(autoTempDir);
+        assertTrue(tmpFile + " must be child of " + autoTempDir,
+                   tmpFile.getAbsolutePath().startsWith(autoTempDir));
+    }
+
+    @Test
+    public void createTempFileDoesntCreateAutoTmpDirIfDeleteOnExitIsFalse() throws IOException {
+        final Project project = new Project();
+        final File tmpFile = getFileUtils().createTempFile(project, null, null, null, false, true);
+        assertNull(project.getProperty("ant.auto.tmpdir"));
+    }
+
+    @Test
+    public void createTempFileReusesAutoTmpDirIfDeleteOnExitIsTrueOnUnix() throws IOException {
+        assumeFalse("Test doesn't run on DOS", Os.isFamily("dos"));
+        final Project project = new Project();
+        final File tmpFile = getFileUtils().createTempFile(project, null, null, null, true, true);
+        final String autoTempDir = project.getProperty("ant.auto.tmpdir");
+        assertNotNull(autoTempDir);
+        final File tmpFile2 = getFileUtils().createTempFile(project, null, null, null, true, true);
+        assertTrue(tmpFile2 + " must be child of " + autoTempDir,
+                   tmpFile2.getAbsolutePath().startsWith(autoTempDir));
+    }
+
+    @Test
+    public void createTempFileDoesntReusesAutoTmpDirIfDeleteOnExitIsFalse() throws IOException {
+        assumeFalse("Test doesn't run on DOS", Os.isFamily("dos"));
+        final Project project = new Project();
+        final File tmpFile = getFileUtils().createTempFile(project, null, null, null, true, true);
+        final String autoTempDir = project.getProperty("ant.auto.tmpdir");
+        assertNotNull(autoTempDir);
+        final File tmpFile2 = getFileUtils().createTempFile(project, null, null, null, false, true);
+        assertFalse(tmpFile2 + " must not be child of " + autoTempDir,
+                    tmpFile2.getAbsolutePath().startsWith(autoTempDir));
+    }
+
     /**
      * Test contentEquals
      */
