This is an automated email from the ASF dual-hosted git repository.

jaikiran pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ant-ivy.git


The following commit(s) were added to refs/heads/master by this push:
     new 75d8d79  IVY-1602 Prevent cache corruption when attempting to copy a 
file to a destination which resolves back to the same source file
75d8d79 is described below

commit 75d8d7980628ec8ae5b149fa49a4d8de6a14417c
Author: Jaikiran Pai <jaiki...@apache.org>
AuthorDate: Fri Feb 1 11:40:35 2019 +0530

    IVY-1602 Prevent cache corruption when attempting to copy a file to a 
destination which resolves back to the same source file
---
 src/java/org/apache/ivy/util/FileUtil.java     | 24 +++++++++
 test/java/org/apache/ivy/ant/FileUtilTest.java | 68 ++++++++++++++++++++++++++
 2 files changed, 92 insertions(+)

diff --git a/src/java/org/apache/ivy/util/FileUtil.java 
b/src/java/org/apache/ivy/util/FileUtil.java
index 9c780f2..c845364 100644
--- a/src/java/org/apache/ivy/util/FileUtil.java
+++ b/src/java/org/apache/ivy/util/FileUtil.java
@@ -34,6 +34,7 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.URL;
 import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -104,6 +105,16 @@ public final class FileUtil {
         return true;
     }
 
+    /**
+     * This is the same as calling {@link #copy(File, File, 
CopyProgressListener, boolean)} with
+     * {@code overwrite} param as {@code true}
+     *
+     * @param src  The source to copy
+     * @param dest The destination
+     * @param l    A {@link CopyProgressListener}. Can be null
+     * @return Returns true if the file was copied. Else returns false
+     * @throws IOException If any exception occurs during the copy operation
+     */
     public static boolean copy(File src, File dest, CopyProgressListener l) 
throws IOException {
         return copy(src, dest, l, false);
     }
@@ -163,6 +174,19 @@ public final class FileUtil {
             return deepCopy(src, dest, l, overwrite);
         }
         // else it is a file copy
+        // check if it's the same file (the src and the dest). if they are the 
same, skip the copy
+        try {
+            if (Files.isSameFile(src.toPath(), dest.toPath())) {
+                Message.verbose("Skipping copy of file " + src + " to " + dest 
+ " since they are the same file");
+                // we consider the file as copied if overwrite is true
+                return overwrite;
+            }
+        } catch (NoSuchFileException nsfe) {
+            // ignore and move on and attempt the copy
+        } catch (IOException ioe) {
+            // log and move on and attempt the copy
+            Message.verbose("Could not determine if " + src + " and dest " + 
dest + " are the same file", ioe);
+        }
         copy(new FileInputStream(src), dest, l);
         long srcLen = src.length();
         long destLen = dest.length();
diff --git a/test/java/org/apache/ivy/ant/FileUtilTest.java 
b/test/java/org/apache/ivy/ant/FileUtilTest.java
index 7eb28d6..a3ca469 100644
--- a/test/java/org/apache/ivy/ant/FileUtilTest.java
+++ b/test/java/org/apache/ivy/ant/FileUtilTest.java
@@ -17,10 +17,21 @@
  */
 package org.apache.ivy.ant;
 
+import org.apache.ivy.util.CopyProgressListener;
 import org.apache.ivy.util.FileUtil;
+import org.apache.ivy.util.Message;
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
 import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -33,6 +44,24 @@ import static org.junit.Assert.assertTrue;
  */
 public class FileUtilTest {
 
+    private static boolean symlinkCapable = false;
+
+    @BeforeClass
+    public static void beforeClass() {
+        try {
+            final Path tmpFile = Files.createTempFile(null, null);
+            tmpFile.toFile().deleteOnExit();
+            final Path symlink = 
Files.createSymbolicLink(Paths.get(Files.createTempDirectory(null).toString(),
+                    "symlink-test-file"), tmpFile);
+            symlinkCapable = true;
+            symlink.toFile().deleteOnExit();
+        } catch (IOException ioe) {
+            // ignore and move on
+            symlinkCapable = false;
+            Message.info("Current system isn't considered to have symlink 
capability due to ", ioe);
+        }
+    }
+
     /**
      * Tests that {@link FileUtil#normalize(String)} works as expected for 
some basic file paths
      */
@@ -83,4 +112,43 @@ public class FileUtilTest {
         }
     }
 
+    /**
+     * Tests that the call to {@link FileUtil#copy(File, File, 
CopyProgressListener)} doesn't corrupt
+     * the source file if the destination file resolves back to the source 
file being copied
+     *
+     * @throws Exception
+     * @see <a 
href="https://issues.apache.org/jira/browse/IVY-1602";>IVY-1602</a> for more 
details
+     */
+    @Test
+    public void testCopyOfSameFile() throws Exception {
+        Assume.assumeTrue("Skipping test due to system not having symlink 
capability", symlinkCapable);
+        final Path srcDir = Files.createTempDirectory(null);
+        srcDir.toFile().deleteOnExit();
+        // create a src file
+        final Path srcFile = Paths.get(srcDir.toString(), "helloworld.txt");
+        srcFile.toFile().deleteOnExit();
+        final byte[] fileContent = "Hello 
world!!!".getBytes(StandardCharsets.UTF_8);
+        Files.write(srcFile, fileContent);
+
+        final Path destDir = 
Paths.get(Files.createTempDirectory(null).toString(), "symlink-dest");
+        destDir.toFile().deleteOnExit();
+        // now create a symlink to the dir containing the src file we intend 
to copy later
+        Files.createSymbolicLink(destDir, srcDir);
+        // at this point destDir is a symlink to the srcDir and the srcDir 
contains the srcFile.
+        // we now attempt to copy the srcFile to a destination which resolves 
back the same srcFile
+        final Path destFile = Paths.get(destDir.toString(), 
srcFile.getFileName().toString());
+        FileUtil.copy(srcFile.toFile(), destFile.toFile(), null, false);
+        // make sure the copy didn't corrupt the source file
+        Assert.assertTrue("Unexpected content in source file " + srcFile, 
Arrays.equals(fileContent, Files.readAllBytes(srcFile)));
+        // also check the dest file has the same content as source file after 
the copy operation
+        Assert.assertTrue("Unexpected content in dest file " + destFile, 
Arrays.equals(fileContent, Files.readAllBytes(destFile)));
+
+        // do the same tests now with overwrite = true
+        FileUtil.copy(srcFile.toFile(), destFile.toFile(), null, true);
+        // make sure the copy didn't corrupt the source file
+        Assert.assertTrue("Unexpected content in source file " + srcFile, 
Arrays.equals(fileContent, Files.readAllBytes(srcFile)));
+        // also check the dest file has the same content as source file after 
the copy operation
+        Assert.assertTrue("Unexpected content in dest file " + destFile, 
Arrays.equals(fileContent, Files.readAllBytes(destFile)));
+    }
+
 }

Reply via email to