Repository: mina-sshd Updated Branches: refs/heads/master b2240515a -> 862f4f029
Do not modify SCP file permissions if timestamp attribute should be preserved Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/862f4f02 Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/862f4f02 Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/862f4f02 Branch: refs/heads/master Commit: 862f4f0294268e7b496c5127596694eec4df2a25 Parents: b224051 Author: Goldstein Lyor <l...@c-b4.com> Authored: Mon Aug 7 11:30:15 2017 +0300 Committer: Goldstein Lyor <l...@c-b4.com> Committed: Mon Aug 7 11:31:08 2017 +0300 ---------------------------------------------------------------------- .../sshd/client/scp/DefaultScpClient.java | 15 +++--- .../subsystem/sftp/SftpFileSystemProvider.java | 6 +-- .../org/apache/sshd/common/scp/ScpHelper.java | 16 ++++--- .../org/apache/sshd/client/scp/ScpTest.java | 50 +++++++++++++------- 4 files changed, 54 insertions(+), 33 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/862f4f02/sshd-core/src/main/java/org/apache/sshd/client/scp/DefaultScpClient.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/client/scp/DefaultScpClient.java b/sshd-core/src/main/java/org/apache/sshd/client/scp/DefaultScpClient.java index 854929a..d69252c 100644 --- a/sshd-core/src/main/java/org/apache/sshd/client/scp/DefaultScpClient.java +++ b/sshd-core/src/main/java/org/apache/sshd/client/scp/DefaultScpClient.java @@ -114,21 +114,22 @@ public class DefaultScpClient extends AbstractScpClient { } @Override - public void upload(final InputStream local, final String remote, final long size, final Collection<PosixFilePermission> perms, final ScpTimestamp time) throws IOException { + public void upload(InputStream local, String remote, long size, Collection<PosixFilePermission> perms, ScpTimestamp time) throws IOException { int namePos = ValidateUtils.checkNotNullAndNotEmpty(remote, "No remote location specified").lastIndexOf('/'); - final String name = (namePos < 0) - ? remote - : ValidateUtils.checkNotNullAndNotEmpty(remote.substring(namePos + 1), "No name value in remote=%s", remote); - final String cmd = ScpClient.createSendCommand(remote, (time != null) ? EnumSet.of(Option.PreserveAttributes) : Collections.emptySet()); + String name = (namePos < 0) + ? remote + : ValidateUtils.checkNotNullAndNotEmpty(remote.substring(namePos + 1), "No name value in remote=%s", remote); + Collection<Option> options = (time != null) ? EnumSet.of(Option.PreserveAttributes) : Collections.emptySet(); + String cmd = ScpClient.createSendCommand(remote, options); ClientSession session = getClientSession(); ChannelExec channel = openCommandChannel(session, cmd); try (InputStream invOut = channel.getInvertedOut(); OutputStream invIn = channel.getInvertedIn()) { // NOTE: we use a mock file system since we expect no invocations for it ScpHelper helper = new ScpHelper(session, invOut, invIn, new MockFileSystem(remote), opener, listener); - final Path mockPath = new MockPath(remote); + Path mockPath = new MockPath(remote); helper.sendStream(new DefaultScpStreamResolver(name, mockPath, perms, time, size, local, cmd), - time != null, ScpHelper.DEFAULT_SEND_BUFFER_SIZE); + options.contains(Option.PreserveAttributes), ScpHelper.DEFAULT_SEND_BUFFER_SIZE); handleCommandExitStatus(cmd, channel); } finally { channel.close(false); http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/862f4f02/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemProvider.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemProvider.java b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemProvider.java index fb24d73..f8af4f5 100644 --- a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemProvider.java +++ b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemProvider.java @@ -60,7 +60,6 @@ import java.util.Collection; import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -1089,11 +1088,12 @@ public class SftpFileSystemProvider extends FileSystemProvider { } public static String getOctalPermissions(int perms) { - return getOctalPermissions(permissionsToAttributes(perms)); + Collection<PosixFilePermission> attrs = permissionsToAttributes(perms); + return getOctalPermissions(attrs); } public static Set<PosixFilePermission> permissionsToAttributes(int perms) { - Set<PosixFilePermission> p = new HashSet<>(); + Set<PosixFilePermission> p = EnumSet.noneOf(PosixFilePermission.class); if ((perms & SftpConstants.S_IRUSR) == SftpConstants.S_IRUSR) { p.add(PosixFilePermission.OWNER_READ); } http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/862f4f02/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpHelper.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpHelper.java b/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpHelper.java index 56f03de..3f584d2 100644 --- a/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpHelper.java +++ b/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpHelper.java @@ -97,6 +97,9 @@ public class ScpHelper extends AbstractLoggingBean implements SessionHolder<Sess public static final int S_IWOTH = 0000002; public static final int S_IXOTH = 0000001; + public static final String DEFAULT_DIR_OCTAL_PERMISSIONS = "0755"; + public static final String DEFAULT_FILE_OCTAL_PERMISSIONS = "0644"; + protected final InputStream in; protected final OutputStream out; protected final FileSystem fileSystem; @@ -120,7 +123,7 @@ public class ScpHelper extends AbstractLoggingBean implements SessionHolder<Sess return sessionInstance; } - public void receiveFileStream(final OutputStream local, final int bufferSize) throws IOException { + public void receiveFileStream(OutputStream local, int bufferSize) throws IOException { receive((line, isDir, timestamp) -> { if (isDir) { throw new StreamCorruptedException("Cannot download a directory into a file stream: " + line); @@ -161,7 +164,7 @@ public class ScpHelper extends AbstractLoggingBean implements SessionHolder<Sess }); } - public void receive(Path local, final boolean recursive, boolean shouldBeDir, final boolean preserve, final int bufferSize) throws IOException { + public void receive(Path local, boolean recursive, boolean shouldBeDir, boolean preserve, final int bufferSize) throws IOException { Path path = Objects.requireNonNull(local, "No local path").normalize().toAbsolutePath(); if (shouldBeDir) { LinkOption[] options = IoUtils.getLinkOptions(true); @@ -602,7 +605,7 @@ public class ScpHelper extends AbstractLoggingBean implements SessionHolder<Sess } Set<PosixFilePermission> perms = EnumSet.copyOf(resolver.getPermissions()); - String octalPerms = preserve ? getOctalPermissions(perms) : "0644"; + String octalPerms = GenericUtils.isEmpty(perms) ? DEFAULT_FILE_OCTAL_PERMISSIONS : getOctalPermissions(perms); String fileName = resolver.getFileName(); String cmd = "C" + octalPerms + " " + fileSize + " " + fileName; if (log.isDebugEnabled()) { @@ -697,8 +700,8 @@ public class ScpHelper extends AbstractLoggingBean implements SessionHolder<Sess LinkOption[] options = IoUtils.getLinkOptions(true); Set<PosixFilePermission> perms = IoUtils.getPermissions(path, options); - String cmd = "D" + (preserve ? getOctalPermissions(perms) : "0755") + " " - + "0" + " " + path.getFileName().toString(); + String octalPerms = GenericUtils.isEmpty(perms) ? DEFAULT_DIR_OCTAL_PERMISSIONS : getOctalPermissions(perms); + String cmd = "D" + octalPerms + " " + "0" + " " + Objects.toString(path.getFileName(), null); if (log.isDebugEnabled()) { log.debug("sendDir({})[{}] send 'D' command: {}", this, path, cmd); } @@ -747,7 +750,8 @@ public class ScpHelper extends AbstractLoggingBean implements SessionHolder<Sess } public static String getOctalPermissions(Path path, LinkOption... options) throws IOException { - return getOctalPermissions(IoUtils.getPermissions(path, options)); + Collection<PosixFilePermission> perms = IoUtils.getPermissions(path, options); + return getOctalPermissions(perms); } public static String getOctalPermissions(Collection<PosixFilePermission> perms) { http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/862f4f02/sshd-core/src/test/java/org/apache/sshd/client/scp/ScpTest.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/test/java/org/apache/sshd/client/scp/ScpTest.java b/sshd-core/src/test/java/org/apache/sshd/client/scp/ScpTest.java index bbe1166..42907d6 100644 --- a/sshd-core/src/test/java/org/apache/sshd/client/scp/ScpTest.java +++ b/sshd-core/src/test/java/org/apache/sshd/client/scp/ScpTest.java @@ -901,24 +901,24 @@ public class ScpTest extends BaseTestSupport { File target = new File(unixPath); Utils.deleteRecursive(root); root.mkdirs(); - assertTrue(root.exists()); + assertTrue("Failed to ensure existence of " + root, root.exists()); target.delete(); - assertFalse(target.exists()); - sendFile(session, unixPath, fileName, data); + assertFalse("Failed to delete 1st time: " + target, target.exists()); + sendFile(session, unixPath, target, data); assertFileLength(target, data.length(), TimeUnit.SECONDS.toMillis(5L)); target.delete(); - assertFalse(target.exists()); - sendFile(session, unixDir, fileName, data); + assertFalse("Failed to delete 2nd time: " + target, target.exists()); + sendFile(session, unixDir, target, data); assertFileLength(target, data.length(), TimeUnit.SECONDS.toMillis(5L)); sendFileError(session, "target", ScpHelper.SCP_COMMAND_PREFIX, data); readFileError(session, unixDir); - assertEquals("Mismatched file data", data, readFile(session, unixPath, target.length())); - assertEquals("Mismatched dir data", data, readDir(session, unixDir, fileName, target.length())); + assertEquals("Mismatched file data", data, readFile(session, unixPath, target)); + assertEquals("Mismatched dir data", data, readDir(session, unixDir, target)); target.delete(); root.delete(); @@ -989,22 +989,26 @@ public class ScpTest extends BaseTestSupport { } } - protected String readFile(com.jcraft.jsch.Session session, String path, long expectedSize) throws Exception { + protected String readFile(com.jcraft.jsch.Session session, String path, File target) throws Exception { ChannelExec c = (ChannelExec) session.openChannel(Channel.CHANNEL_EXEC); c.setCommand("scp -f " + path); c.connect(); - int namePos = path.lastIndexOf('/'); - String fileName = (namePos >= 0) ? path.substring(namePos + 1) : path; + String fileName = target.getName(); try (OutputStream os = c.getOutputStream(); InputStream is = c.getInputStream()) { os.write(0); os.flush(); + String header = readLine(is); - assertEquals("Mismatched header for " + path, "C0644 " + expectedSize + " " + fileName, header); + Collection<PosixFilePermission> perms = IoUtils.getPermissions(target.toPath()); + String octalPerms = ScpHelper.getOctalPermissions(perms); + String expHeader = "C" + octalPerms + " " + target.length() + " " + fileName; + assertEquals("Mismatched header for " + path, expHeader, header); - int length = Integer.parseInt(header.substring(6, header.indexOf(' ', 6))); + String lenValue = header.substring(6, header.indexOf(' ', 6)); + int length = Integer.parseInt(lenValue); os.write(0); os.flush(); @@ -1022,7 +1026,7 @@ public class ScpTest extends BaseTestSupport { } } - protected String readDir(com.jcraft.jsch.Session session, String path, String fileName, long expectedSize) throws Exception { + protected String readDir(com.jcraft.jsch.Session session, String path, File target) throws Exception { ChannelExec c = (ChannelExec) session.openChannel(Channel.CHANNEL_EXEC); c.setCommand("scp -r -f " + path); c.connect(); @@ -1033,12 +1037,19 @@ public class ScpTest extends BaseTestSupport { os.flush(); String header = readLine(is); - assertTrue("Bad header prefix for " + path + ": " + header, header.startsWith("D0755 0 ")); + File parent = target.getParentFile(); + Collection<PosixFilePermission> perms = IoUtils.getPermissions(parent.toPath()); + String octalPerms = ScpHelper.getOctalPermissions(perms); + String expPrefix = "D" + octalPerms + " 0 "; + assertTrue("Bad header prefix for " + path + ": " + header, header.startsWith(expPrefix)); os.write(0); os.flush(); header = readLine(is); - assertEquals("Mismatched dir header for " + path, "C0644 " + expectedSize + " " + fileName, header); + perms = IoUtils.getPermissions(target.toPath()); + octalPerms = ScpHelper.getOctalPermissions(perms); + String expHeader = "C" + octalPerms + " " + target.length() + " " + target.getName(); + assertEquals("Mismatched dir header for " + path, expHeader, header); int length = Integer.parseInt(header.substring(6, header.indexOf(' ', 6))); os.write(0); os.flush(); @@ -1079,7 +1090,7 @@ public class ScpTest extends BaseTestSupport { } } - protected void sendFile(com.jcraft.jsch.Session session, String path, String name, String data) throws Exception { + protected void sendFile(com.jcraft.jsch.Session session, String path, File target, String data) throws Exception { ChannelExec c = (ChannelExec) session.openChannel(Channel.CHANNEL_EXEC); String command = "scp -t " + path; c.setCommand(command); @@ -1089,7 +1100,12 @@ public class ScpTest extends BaseTestSupport { InputStream is = c.getInputStream()) { assertAckReceived(is, command); - assertAckReceived(os, is, "C7777 " + data.length() + " " + name); + + File parent = target.getParentFile(); + Collection<PosixFilePermission> perms = IoUtils.getPermissions(parent.toPath()); + String octalPerms = ScpHelper.getOctalPermissions(perms); + String name = target.getName(); + assertAckReceived(os, is, "C" + octalPerms + " " + data.length() + " " + name); os.write(data.getBytes(StandardCharsets.UTF_8)); os.flush();