From 13bc6453fe4c31a2f053a6ebb28638a20136227f Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Wed, 16 Jul 2008 08:07:44 +0000 Subject: [PATCH] Add preservelastmodified to scp. PR 33939. Based on patch by Sandra Metz. git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@677187 13f79535-47bb-0310-9956-ffa450edef68 --- CONTRIBUTORS | 1 + WHATSNEW | 4 + contributors.xml | 4 + docs/manual/OptionalTasks/scp.html | 9 +++ .../tools/ant/taskdefs/optional/ssh/Scp.java | 16 +++- .../taskdefs/optional/ssh/ScpFromMessage.java | 80 +++++++++++++++++-- .../optional/ssh/ScpFromMessageBySftp.java | 39 +++++++-- 7 files changed, 138 insertions(+), 15 deletions(-) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 328895b44..706793e99 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -236,6 +236,7 @@ Roman Ivashin Ronen Mashal Russell Gold Sam Ruby +Sandra Metz Scott Carlson Scott Ellsworth Scott M. Stirling diff --git a/WHATSNEW b/WHATSNEW index 07abf66c4..8d15c2e31 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -168,6 +168,10 @@ Other changes: * now supports input in a way similar to Bugzilla report 39197. + * can now preserve the file modification time when downloading + files. + Bugzilla Issue 33939. + Changes from Ant 1.7.0 TO Ant 1.7.1 ============================================= diff --git a/contributors.xml b/contributors.xml index cee92e86c..a9c51256e 100644 --- a/contributors.xml +++ b/contributors.xml @@ -955,6 +955,10 @@ Sam Ruby + + Sandra + Metz + Scott Carlson diff --git a/docs/manual/OptionalTasks/scp.html b/docs/manual/OptionalTasks/scp.html index 380c89d87..6d7a43d48 100644 --- a/docs/manual/OptionalTasks/scp.html +++ b/docs/manual/OptionalTasks/scp.html @@ -174,6 +174,15 @@ for more information. This task has been tested with jsch-0.1.2 and later.

server that doesn't support scp1. since Ant 1.7 No; defaults to false. + + preserveLastModified + Determines whether the last modification + timestamp of downloaded files is preserved. It only works when + transferring from a remote to a local system and probably doesn't + work with a server that doesn't support SSH2. since Ant + 1.8.0 + No; defaults to false. +

Parameters specified as nested elements

diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/ssh/Scp.java b/src/main/org/apache/tools/ant/taskdefs/optional/ssh/Scp.java index 878d428e6..7ba4b20e2 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/ssh/Scp.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/ssh/Scp.java @@ -49,6 +49,7 @@ public class Scp extends SSHBase { private String fromUri; private String toUri; + private boolean preserveLastModified = false; private List fileSets = null; private boolean isFromRemote, isToRemote; private boolean isSftp = false; @@ -116,6 +117,15 @@ public class Scp extends SSHBase { this.isToRemote = false; } + /** + * Sets flag to determine if file timestamp from + * remote system is to be preserved during copy. + * @since Ant 1.8.0 + */ + public void setPreservelastmodified(boolean yesOrNo) { + this.preserveLastModified = yesOrNo; + } + /** * Similiar to {@link #setTodir setTodir} but explicitly states * that the directory is a remote. @@ -231,12 +241,14 @@ public class Scp extends SSHBase { message = new ScpFromMessage(getVerbose(), session, file, getProject().resolveFile(toPath), - fromSshUri.endsWith("*")); + fromSshUri.endsWith("*"), + preserveLastModified); } else { message = new ScpFromMessageBySftp(getVerbose(), session, file, getProject().resolveFile(toPath), - fromSshUri.endsWith("*")); + fromSshUri.endsWith("*"), + preserveLastModified); } log("Receiving file: " + file); message.setLogListener(this); diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessage.java b/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessage.java index 0050400da..8e963b138 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessage.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessage.java @@ -27,7 +27,11 @@ import java.io.FileOutputStream; import java.io.ByteArrayOutputStream; import com.jcraft.jsch.JSchException; import com.jcraft.jsch.Session; +import com.jcraft.jsch.SftpATTRS; +import com.jcraft.jsch.SftpException; import com.jcraft.jsch.Channel; +import com.jcraft.jsch.ChannelSftp; +import org.apache.tools.ant.util.FileUtils; /** * A helper object representing an scp download. @@ -41,6 +45,7 @@ public class ScpFromMessage extends AbstractSshMessage { private String remoteFile; private File localFile; private boolean isRecursive = false; + private boolean preserveLastModified = false; /** * Constructor for ScpFromMessage @@ -74,10 +79,7 @@ public class ScpFromMessage extends AbstractSshMessage { String aRemoteFile, File aLocalFile, boolean recursive) { - super(verbose, session); - this.remoteFile = aRemoteFile; - this.localFile = aLocalFile; - this.isRecursive = recursive; + this(false, session, aRemoteFile, aLocalFile, recursive, false); } /** @@ -94,6 +96,30 @@ public class ScpFromMessage extends AbstractSshMessage { this(false, session, aRemoteFile, aLocalFile, recursive); } + /** + * Constructor for ScpFromMessage. + * @param verbose if true log extra information + * @param session the Scp session to use + * @param aRemoteFile the remote file name + * @param aLocalFile the local file + * @param recursive if true use recursion (-r option to scp) + * @param preservceLastModified whether to preserve file + * modification times + * @since Ant 1.8.0 + */ + public ScpFromMessage(boolean verbose, + Session session, + String aRemoteFile, + File aLocalFile, + boolean recursive, + boolean preserveLastModified) { + super(verbose, session); + this.remoteFile = aRemoteFile; + this.localFile = aLocalFile; + this.isRecursive = recursive; + this.preserveLastModified = preserveLastModified; + } + /** * Carry out the transfer. * @throws IOException on i/o errors @@ -123,9 +149,14 @@ public class ScpFromMessage extends AbstractSshMessage { log("done\n"); } + protected boolean getPreserveLastModified() { + return preserveLastModified; + } + private void startRemoteCpProtocol(InputStream in, OutputStream out, - File localFile) throws IOException { + File localFile) + throws IOException, JSchException { File startFile = localFile; while (true) { // C0644 filesize filename - header for a regular file @@ -147,7 +178,7 @@ public class ScpFromMessage extends AbstractSshMessage { parseAndFetchFile(serverResponse, startFile, out, in); } else if (serverResponse.charAt(0) == 'D') { startFile = parseAndCreateDirectory(serverResponse, - startFile); + startFile); sendAck(out); } else if (serverResponse.charAt(0) == 'E') { startFile = startFile.getParentFile(); @@ -178,7 +209,8 @@ public class ScpFromMessage extends AbstractSshMessage { private void parseAndFetchFile(String serverResponse, File localFile, OutputStream out, - InputStream in) throws IOException { + InputStream in) + throws IOException, JSchException { int start = 0; int end = serverResponse.indexOf(" ", start + 1); start = end + 1; @@ -197,7 +229,8 @@ public class ScpFromMessage extends AbstractSshMessage { private void fetchFile(File localFile, long filesize, OutputStream out, - InputStream in) throws IOException { + InputStream in) + throws IOException, JSchException { byte[] buf = new byte[BUFFER_SIZE]; sendAck(out); @@ -241,6 +274,37 @@ public class ScpFromMessage extends AbstractSshMessage { fos.flush(); fos.close(); } + + if (getPreserveLastModified()) { + setLastModified(localFile); + } } + private void setLastModified(File localFile) throws JSchException { + SftpATTRS fileAttributes = null; + String remotePath = null; + ChannelSftp channel = openSftpChannel(); + channel.connect(); + try { + fileAttributes = channel.lstat(remoteDir(remoteFile) + + localFile.getName()); + } catch (SftpException e) { + throw new JSchException("failed to stat remote file", e); + } + FileUtils.getFileUtils().setFileLastModified(localFile, + ((long) fileAttributes + .getMTime()) + * 1000); + } + + /** + * returns the directory part of the remote file, if any. + */ + private static String remoteDir(String remoteFile) { + int index = remoteFile.lastIndexOf("/"); + if (index < 0) { + index = remoteFile.lastIndexOf("\\"); + } + return index > -1 ? remoteFile.substring(0, index + 1) : ""; + } } diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessageBySftp.java b/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessageBySftp.java index 3669e3f2e..01a642673 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessageBySftp.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessageBySftp.java @@ -28,6 +28,8 @@ import com.jcraft.jsch.SftpException; import com.jcraft.jsch.SftpATTRS; import com.jcraft.jsch.SftpProgressMonitor; +import org.apache.tools.ant.util.FileUtils; + /** * A helper object representing an scp download. */ @@ -54,11 +56,7 @@ public class ScpFromMessageBySftp extends ScpFromMessage { String aRemoteFile, File aLocalFile, boolean recursive) { - super(verbose, session); - this.verbose = verbose; - this.remoteFile = aRemoteFile; - this.localFile = aLocalFile; - this.isRecursive = recursive; + this(verbose, session, aRemoteFile, aLocalFile, recursive, false); } /** @@ -75,6 +73,31 @@ public class ScpFromMessageBySftp extends ScpFromMessage { this(false, session, aRemoteFile, aLocalFile, recursive); } + /** + * Constructor for ScpFromMessageBySftp. + * @param verbose if true log extra information + * @param session the Scp session to use + * @param aRemoteFile the remote file name + * @param aLocalFile the local file + * @param recursive if true use recursion + * @param preservceLastModified whether to preserve file + * modification times + * @since Ant 1.8.0 + */ + public ScpFromMessageBySftp(boolean verbose, + Session session, + String aRemoteFile, + File aLocalFile, + boolean recursive, + boolean preserveLastModified) { + super(verbose, session, aRemoteFile, aLocalFile, recursive, + preserveLastModified); + this.verbose = verbose; + this.remoteFile = aRemoteFile; + this.localFile = aLocalFile; + this.isRecursive = recursive; + } + /** * Carry out the transfer. * @throws IOException on i/o errors @@ -171,5 +194,11 @@ public class ScpFromMessageBySftp extends ScpFromMessage { long endTime = System.currentTimeMillis(); logStats(startTime, endTime, (int) totalLength); } + if (getPreserveLastModified()) { + FileUtils.getFileUtils().setFileLastModified(localFile, + ((long) le.getAttrs() + .getMTime()) + * 1000); + } } }