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))); + } + }