From e229f37dd921812e213e34913c94b632c6e54299 Mon Sep 17 00:00:00 2001 From: Matt Sicker Date: Fri, 4 May 2018 14:36:33 -0500 Subject: [PATCH] JENKINS-13128: Preserve copied file permissions and mtime (#3400) * JENKINS-13128: Preserve copied file permissions and mtime This fixes a bug where files copied locally do not preserve file permissions or last modification time. * Use IO utility methods for exception handling * Fix invalid path exception propagation * Revert hudson.Util --- core/src/main/java/hudson/FilePath.java | 3 +- .../java/hudson/model/ItemGroupMixIn.java | 5 ++- core/src/test/java/hudson/FilePathTest.java | 34 +++++++++++++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java index 5f5cddd6ab..c743e6fe06 100644 --- a/core/src/main/java/hudson/FilePath.java +++ b/core/src/main/java/hudson/FilePath.java @@ -2283,7 +2283,8 @@ public final class FilePath implements Serializable { if (f.isFile()) { File target = new File(dest, relativePath); mkdirsE(target.getParentFile()); - Util.copyFile(f, writing(target)); + Files.copy(fileToPath(f), fileToPath(writing(target)), + StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING); count.incrementAndGet(); } } diff --git a/core/src/main/java/hudson/model/ItemGroupMixIn.java b/core/src/main/java/hudson/model/ItemGroupMixIn.java index 13f72c3f07..195d4d8c0c 100644 --- a/core/src/main/java/hudson/model/ItemGroupMixIn.java +++ b/core/src/main/java/hudson/model/ItemGroupMixIn.java @@ -45,6 +45,8 @@ import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; @@ -239,7 +241,8 @@ public abstract class ItemGroupMixIn { T result = (T)createProject(src.getDescriptor(),name,false); // copy config - Util.copyFile(srcConfigFile.getFile(), Items.getConfigFile(result).getFile()); + Files.copy(Util.fileToPath(srcConfigFile.getFile()), Util.fileToPath(Items.getConfigFile(result).getFile()), + StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING); // reload from the new config final File rootDir = result.getRootDir(); diff --git a/core/src/test/java/hudson/FilePathTest.java b/core/src/test/java/hudson/FilePathTest.java index 57815f6618..2243bd1fc1 100644 --- a/core/src/test/java/hudson/FilePathTest.java +++ b/core/src/test/java/hudson/FilePathTest.java @@ -43,8 +43,11 @@ import java.net.URLConnection; import java.net.URLStreamHandler; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.attribute.FileTime; +import java.nio.file.attribute.PosixFilePermission; import java.util.ArrayList; import java.util.Arrays; +import java.util.EnumSet; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -52,6 +55,7 @@ import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import org.apache.commons.io.FileUtils; @@ -845,4 +849,34 @@ public class FilePathTest { assertTrue("junction target contents should not be deleted", Files.exists(targetContents)); assertFalse("could not delete target", Files.exists(toDelete)); } + + @Issue("JENKINS-13128") + @Test public void copyRecursivePreservesPosixFilePermissions() throws Exception { + assumeFalse("windows doesn't support posix file permissions", Functions.isWindows()); + File src = temp.newFolder("src"); + File dst = temp.newFolder("dst"); + Path sourceFile = Files.createFile(src.toPath().resolve("test-file")); + Set allRWX = EnumSet.allOf(PosixFilePermission.class); + Files.setPosixFilePermissions(sourceFile, allRWX); + FilePath f = new FilePath(src); + f.copyRecursiveTo(new FilePath(dst)); + Path destinationFile = dst.toPath().resolve("test-file"); + assertTrue("file was not copied", Files.exists(destinationFile)); + Set destinationPermissions = Files.getPosixFilePermissions(destinationFile); + assertEquals("file permissions not copied", allRWX, destinationPermissions); + } + + @Issue("JENKINS-13128") + @Test public void copyRecursivePreservesLastModifiedTime() throws Exception { + File src = temp.newFolder("src"); + File dst = temp.newFolder("dst"); + Path sourceFile = Files.createFile(src.toPath().resolve("test-file")); + FileTime mtime = FileTime.from(42L, TimeUnit.SECONDS); + Files.setLastModifiedTime(sourceFile, mtime); + FilePath f = new FilePath(src); + f.copyRecursiveTo(new FilePath(dst)); + Path destinationFile = dst.toPath().resolve("test-file"); + assertTrue("file was not copied", Files.exists(destinationFile)); + assertEquals("file mtime was not preserved", mtime, Files.getLastModifiedTime(destinationFile)); + } } -- GitLab