Source:
public Integer invoke(File base, VirtualChannel channel) throws IOException { if (!base.exists()) { return 0; } assert target.channel == null; final File dest = new File(target.remote); final AtomicInteger count = new AtomicInteger(); scanner.scan(base, reading(new FileVisitor() { private boolean exceptionEncountered; private boolean logMessageShown; @Override public void visit(File f, String relativePath) throws IOException { if (f.isFile()) { File target = new File(dest, relativePath); mkdirsE(target.getParentFile()); Path targetPath = fileToPath(writing(target)); exceptionEncountered = exceptionEncountered || !tryCopyWithAttributes(f, targetPath); if (exceptionEncountered) { Files.copy(fileToPath(f), targetPath, StandardCopyOption.REPLACE_EXISTING); if (!logMessageShown) { LOGGER.log(Level.FINEST, "JENKINS-52325: Jenkins failed to retain attributes when copying to {0}, so proceeding without attributes.", dest.getAbsolutePath()); logMessageShown = true; } } count.incrementAndGet(); } } private boolean tryCopyWithAttributes(File f, Path targetPath) { try { Files.copy(fileToPath(f), targetPath, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING); } catch (IOException e) { LOGGER.log(Level.FINE, "Unable to copy: {0}", e.getMessage()); return false; } return true; } @Override public boolean understandsSymlink() { return true; } @Override public void visitSymlink(File link, String target, String relativePath) throws IOException { try { mkdirsE(new File(dest, relativePath).getParentFile()); writing(new File(dest, target)); Util.createSymlink(dest, target, relativePath, TaskListener.NULL); } catch (InterruptedException x) { throw new IOException(x); } count.incrementAndGet(); } })); return count.get(); }

Target:
public Integer invoke(File base, VirtualChannel channel) throws IOException { if (!base.exists()) { return 0; } assert target.channel == null; final File dest = new File(target.remote); final AtomicInteger count = new AtomicInteger(); scanner.scan(base, reading(new FileVisitor() { private boolean exceptionEncountered; private boolean logMessageShown; @Override public void visit(File f, String relativePath) throws IOException { if (f.isFile()) { File target = new File(dest, relativePath); mkdirsE(target.getParentFile()); Path targetPath = fileToPath(writing(target)); exceptionEncountered = exceptionEncountered || !tryCopyWithAttributes(f, targetPath); if (exceptionEncountered) { Files.copy(fileToPath(f), targetPath, StandardCopyOption.REPLACE_EXISTING); if (!logMessageShown) { LOGGER.log(Level.INFO, "JENKINS-52325: Jenkins failed to retain attributes when copying to {0}, so proceeding without attributes.", dest.getAbsolutePath()); logMessageShown = true; } } count.incrementAndGet(); } } private boolean tryCopyWithAttributes(File f, Path targetPath) { try { Files.copy(fileToPath(f), targetPath, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING); } catch (IOException e) { LOGGER.log(Level.FINE, "Unable to copy: {0}", e.getMessage()); return false; } return true; } @Override public boolean understandsSymlink() { return true; } @Override public void visitSymlink(File link, String target, String relativePath) throws IOException { try { mkdirsE(new File(dest, relativePath).getParentFile()); writing(new File(dest, target)); Util.createSymlink(dest, target, relativePath, TaskListener.NULL); } catch (InterruptedException x) { throw new IOException(x); } count.incrementAndGet(); } })); return count.get(); }