You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by lg...@apache.org on 2015/07/06 15:16:55 UTC
mina-sshd git commit: [SSHD-528] Use byte[] as the SFTP Handle
identifier
Repository: mina-sshd
Updated Branches:
refs/heads/master f7f21bc85 -> 883efa01b
[SSHD-528] Use byte[] as the SFTP Handle identifier
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/883efa01
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/883efa01
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/883efa01
Branch: refs/heads/master
Commit: 883efa01b2213687a04b5a2702a1606a5b11d3f9
Parents: f7f21bc
Author: Lyor Goldstein <lg...@vmware.com>
Authored: Mon Jul 6 16:16:44 2015 +0300
Committer: Lyor Goldstein <lg...@vmware.com>
Committed: Mon Jul 6 16:16:44 2015 +0300
----------------------------------------------------------------------
.../java/org/apache/sshd/client/SshClient.java | 1 +
.../subsystem/sftp/AbstractSftpClient.java | 45 +--
.../subsystem/sftp/DefaultCloseableHandle.java | 2 +-
.../sshd/client/subsystem/sftp/SftpClient.java | 51 ++-
.../sshd/client/subsystem/sftp/SftpCommand.java | 335 +++++++++++++++++++
.../subsystem/sftp/SftpFileSystemProvider.java | 152 +++++++--
.../impl/AbstractMD5HashExtension.java | 8 +-
.../extensions/impl/MD5HandleExtensionImpl.java | 2 +-
.../sshd/common/random/AbstractRandom.java | 34 ++
.../apache/sshd/common/random/JceRandom.java | 2 +-
.../org/apache/sshd/common/random/Random.java | 7 +-
.../common/random/SingletonRandomFactory.java | 2 +-
.../org/apache/sshd/common/scp/ScpHelper.java | 20 +-
.../apache/sshd/common/util/SecurityUtils.java | 3 +-
.../sshd/common/util/buffer/BufferUtils.java | 5 +-
.../server/subsystem/sftp/SftpSubsystem.java | 55 ++-
.../org/apache/sshd/client/scp/ScpTest.java | 2 +-
.../sftp/DefaultCloseableHandleTest.java | 9 +-
.../client/subsystem/sftp/SftpCommandMain.java | 31 ++
19 files changed, 674 insertions(+), 92 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/883efa01/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java b/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
index ae0b758..85b19ac 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
@@ -584,6 +584,7 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
if (error) {
System.err.println("usage: ssh [-A|-a] [-v[v][v]] [-D socksPort] [-l login] [-p port] [-o option=value] hostname/user@host [command]");
System.exit(-1);
+ return;
}
try(SshClient client = (SshClient) session.getFactoryManager()) {
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/883efa01/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/AbstractSftpClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/AbstractSftpClient.java b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/AbstractSftpClient.java
index bd1988c..2461fbd 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/AbstractSftpClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/AbstractSftpClient.java
@@ -277,7 +277,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
}
}
- protected String checkHandle(Buffer buffer) throws IOException {
+ protected byte[] checkHandle(Buffer buffer) throws IOException {
int length = buffer.getInt();
int type = buffer.getUByte();
int id = buffer.getInt();
@@ -290,8 +290,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
}
throw new SftpException(substatus, msg);
} else if (type == SSH_FXP_HANDLE) {
- String handle = ValidateUtils.checkNotNullAndNotEmpty(buffer.getString(), "Null/empty handle in buffer", GenericUtils.EMPTY_OBJECT_ARRAY);
- return handle;
+ return ValidateUtils.checkNotNullAndNotEmpty(buffer.getBytes(), "Null/empty handle in buffer", GenericUtils.EMPTY_OBJECT_ARRAY);
} else {
throw new SshException("Unexpected SFTP packet received: type=" + type + ", id=" + id + ", length=" + length);
}
@@ -616,8 +615,9 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
throw new IOException("close(" + handle + ") client is closed");
}
- Buffer buffer = new ByteArrayBuffer(handle.id.length() + Long.SIZE /* some extra fields */);
- buffer.putString(handle.id);
+ byte[] id = handle.getIdentifier();
+ Buffer buffer = new ByteArrayBuffer(id.length + Long.SIZE /* some extra fields */);
+ buffer.putBytes(id);
checkStatus(receive(send(SSH_FXP_CLOSE, buffer)));
}
@@ -673,8 +673,9 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
throw new IOException("read(" + handle + "/" + fileOffset + ")[" + dstOffset + "/" + len + "] client is closed");
}
- Buffer buffer = new ByteArrayBuffer(handle.id.length() + Long.SIZE /* some extra fields */);
- buffer.putString(handle.id);
+ byte[] id = handle.getIdentifier();
+ Buffer buffer = new ByteArrayBuffer(id.length + Long.SIZE /* some extra fields */);
+ buffer.putBytes(id);
buffer.putLong(fileOffset);
buffer.putInt(len);
return checkData(receive(send(SSH_FXP_READ, buffer)), dstOffset, dst);
@@ -723,8 +724,9 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
throw new IOException("write(" + handle + "/" + fileOffset + ")[" + srcOffset + "/" + len + "] client is closed");
}
- Buffer buffer = new ByteArrayBuffer(handle.id.length() + len + Long.SIZE /* some extra fields */);
- buffer.putString(handle.id);
+ byte[] id = handle.getIdentifier();
+ Buffer buffer = new ByteArrayBuffer(id.length + len + Long.SIZE /* some extra fields */);
+ buffer.putBytes(id);
buffer.putLong(fileOffset);
buffer.putBytes(src, srcOffset, len);
checkStatus(receive(send(SSH_FXP_WRITE, buffer)));
@@ -776,8 +778,9 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
throw new IOException("readDir(" + handle + ") client is closed");
}
- Buffer buffer = new ByteArrayBuffer(handle.id.length() + Long.SIZE /* some extra fields */);
- buffer.putString(handle.id);
+ byte[] id = handle.getIdentifier();
+ Buffer buffer = new ByteArrayBuffer(id.length + Byte.SIZE /* some extra fields */);
+ buffer.putBytes(id);
return checkDir(receive(send(SSH_FXP_READDIR, buffer)));
}
@@ -867,8 +870,9 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
throw new IOException("stat(" + handle + ") client is closed");
}
- Buffer buffer = new ByteArrayBuffer();
- buffer.putString(handle.id);
+ byte[] id = handle.getIdentifier();
+ Buffer buffer = new ByteArrayBuffer(id.length + Byte.SIZE /* a bit extra */);
+ buffer.putBytes(id);
int version = getVersion();
if (version >= SFTP_V4) {
@@ -896,8 +900,9 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
throw new IOException("setStat(" + handle + ")[" + attributes + "] client is closed");
}
- Buffer buffer = new ByteArrayBuffer();
- buffer.putString(handle.id);
+ byte[] id = handle.getIdentifier();
+ Buffer buffer = new ByteArrayBuffer(id.length + (2 * Long.SIZE) /* some extras */);
+ buffer.putBytes(id);
writeAttributes(buffer, attributes);
checkStatus(receive(send(SSH_FXP_FSETSTAT, buffer)));
}
@@ -942,8 +947,9 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
throw new IOException("lock(" + handle + ")[offset=" + offset + ", length=" + length + ", mask=0x" + Integer.toHexString(mask) + "] client is closed");
}
- Buffer buffer = new ByteArrayBuffer();
- buffer.putString(handle.id);
+ byte[] id = handle.getIdentifier();
+ Buffer buffer = new ByteArrayBuffer(id.length + Long.SIZE /* a bit extra */);
+ buffer.putBytes(id);
buffer.putLong(offset);
buffer.putLong(length);
buffer.putInt(mask);
@@ -956,8 +962,9 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
throw new IOException("unlock" + handle + ")[offset=" + offset + ", length=" + length + "] client is closed");
}
- Buffer buffer = new ByteArrayBuffer();
- buffer.putString(handle.id);
+ byte[] id = handle.getIdentifier();
+ Buffer buffer = new ByteArrayBuffer(id.length + Long.SIZE /* a bit extra */);
+ buffer.putBytes(id);
buffer.putLong(offset);
buffer.putLong(length);
checkStatus(receive(send(SSH_FXP_UNBLOCK, buffer)));
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/883efa01/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/DefaultCloseableHandle.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/DefaultCloseableHandle.java b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/DefaultCloseableHandle.java
index 868ff10..842e63a 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/DefaultCloseableHandle.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/DefaultCloseableHandle.java
@@ -32,7 +32,7 @@ public class DefaultCloseableHandle extends CloseableHandle {
private final AtomicBoolean open = new AtomicBoolean(true);
private final SftpClient client;
- public DefaultCloseableHandle(SftpClient client, String id) {
+ public DefaultCloseableHandle(SftpClient client, byte[] id) {
super(id);
this.client = ValidateUtils.checkNotNull(client, "No client for id=%s", id);
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/883efa01/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpClient.java b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpClient.java
index bc3d5f5..fb73149 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpClient.java
@@ -40,6 +40,8 @@ import org.apache.sshd.client.subsystem.SubsystemClient;
import org.apache.sshd.client.subsystem.sftp.extensions.SftpClientExtension;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.bouncycastle.util.Arrays;
/**
* @author <a href="http://mina.apache.org">Apache MINA Project</a>
@@ -72,19 +74,56 @@ public interface SftpClient extends SubsystemClient {
}
public static class Handle {
- public final String id;
- public Handle(String id) {
- this.id = ValidateUtils.checkNotNullAndNotEmpty(id, "No handle ID", GenericUtils.EMPTY_OBJECT_ARRAY);
+ private final byte[] id;
+
+ public Handle(byte[] id) {
+ // clone the original so the handle is imutable
+ this.id = ValidateUtils.checkNotNullAndNotEmpty(id, "No handle ID", GenericUtils.EMPTY_OBJECT_ARRAY).clone();
+ }
+
+ /**
+ * @return A <U>cloned</U> instance of the identifier in order to
+ * avoid inadvertent modifications to the handle contents
+ */
+ public byte[] getIdentifier() {
+ return id.clone();
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(id);
}
-
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+
+ if (obj == this) {
+ return true;
+ }
+
+ // we do not ask getClass() == obj.getClass() in order to allow for derived classes equality
+ if (!(obj instanceof Handle)) {
+ return false;
+ }
+
+ if (Arrays.areEqual(id, ((Handle) obj).id)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
@Override
public String toString() {
- return id;
+ return BufferUtils.printHex(BufferUtils.EMPTY_HEX_SEPARATOR, id);
}
}
public static abstract class CloseableHandle extends Handle implements Channel, Closeable {
- protected CloseableHandle(String id) {
+ protected CloseableHandle(byte[] id) {
super(id);
}
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/883efa01/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpCommand.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpCommand.java b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpCommand.java
new file mode 100644
index 0000000..c0d7edd
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpCommand.java
@@ -0,0 +1,335 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.subsystem.sftp;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.nio.channels.Channel;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.apache.sshd.client.SshClient;
+import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.io.NoCloseInputStream;
+
+/**
+ * Implements a simple command line SFTP client similar to the Linux one
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class SftpCommand implements Channel {
+ private final SftpClient client;
+ private String cwdRemote;
+ private final Map<String,CommandExecutor> commandsMap =
+ Collections.unmodifiableMap(new TreeMap<String,CommandExecutor>() {
+ private static final long serialVersionUID = 1L; // we're not serializing it
+
+ {
+ for (CommandExecutor e : Arrays.<CommandExecutor>asList(
+ new CommandExecutor() {
+ @Override
+ public String getName() {
+ return "exit";
+ }
+
+ @Override
+ public boolean executeCommand(String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception {
+ ValidateUtils.checkTrue(GenericUtils.isEmpty(args), "Unexpected arguments: %s", args);
+ stdout.println("Exiting");
+ return true;
+ }
+ },
+ new CommandExecutor() {
+ @Override
+ public String getName() {
+ return "pwd";
+ }
+
+ @Override
+ public boolean executeCommand(String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception {
+ ValidateUtils.checkTrue(GenericUtils.isEmpty(args), "Unexpected arguments: %s", args);
+ stdout.append('\t').println(getCurrentRemoteDirectory());
+ return false;
+ }
+ },
+ new CommandExecutor() {
+ @Override
+ public String getName() {
+ return "version";
+ }
+
+ @Override
+ public boolean executeCommand(String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception {
+ ValidateUtils.checkTrue(GenericUtils.isEmpty(args), "Unexpected arguments: %s", args);
+ SftpClient sftp = getClient();
+ stdout.append('\t').println(sftp.getVersion());
+ return false;
+ }
+ },
+ new CommandExecutor() {
+ @Override
+ public String getName() {
+ return "cd";
+ }
+
+ @Override
+ public boolean executeCommand(String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception {
+ ValidateUtils.checkNotNullAndNotEmpty(args, "No remote directory specified", args);
+
+ String newPath = resolveRemotePath(args);
+ SftpClient sftp = getClient();
+ setCurrentRemoteDirectory(sftp.canonicalPath(newPath));
+ return false;
+ }
+ },
+ new CommandExecutor() {
+ @Override
+ public String getName() {
+ return "mkdir";
+ }
+
+ @Override
+ public boolean executeCommand(String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception {
+ ValidateUtils.checkNotNullAndNotEmpty(args, "No remote directory specified", args);
+
+ String path = resolveRemotePath(args);
+ SftpClient sftp = getClient();
+ sftp.mkdir(path);
+ return false;
+ }
+ },
+ new CommandExecutor() {
+ @Override
+ public String getName() {
+ return "ls";
+ }
+
+ @Override
+ public boolean executeCommand(String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception {
+ String[] comps = GenericUtils.split(args, ' ');
+ // ignore all flag
+ String pathArg = GenericUtils.isEmpty(comps) ? null : comps[comps.length - 1];
+ String cwd = getCurrentRemoteDirectory();
+ if (GenericUtils.isEmpty(pathArg) || (pathArg.charAt(0) == '-')) {
+ pathArg = cwd;
+ }
+
+ String path = resolveRemotePath(pathArg);
+ SftpClient sftp = getClient();
+ for (SftpClient.DirEntry entry : sftp.readDir(path)) {
+ SftpClient.Attributes attrs = entry.attributes;
+ stdout.append('\t').append(entry.filename)
+ .append('\t').append(Long.toString(attrs.size))
+ .append('\t').println(SftpFileSystemProvider.getRWXPermissions(attrs.perms));
+ }
+ return false;
+ }
+ },
+ new CommandExecutor() {
+ @Override
+ public String getName() {
+ return "rm";
+ }
+
+ @Override
+ public boolean executeCommand(String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception {
+ ValidateUtils.checkNotNullAndNotEmpty(args, "No remote directory specified", args);
+
+ String path = resolveRemotePath(args);
+ SftpClient sftp = getClient();
+ sftp.remove(path);
+ return false;
+ }
+ },
+ new CommandExecutor() {
+ @Override
+ public String getName() {
+ return "rmdir";
+ }
+
+ @Override
+ public boolean executeCommand(String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception {
+ ValidateUtils.checkNotNullAndNotEmpty(args, "No remote directory specified", args);
+
+ String path = resolveRemotePath(args);
+ SftpClient sftp = getClient();
+ sftp.rmdir(path);
+ return false;
+ }
+ },
+ new CommandExecutor() {
+ @Override
+ public String getName() {
+ return "rename";
+ }
+
+ @Override
+ public boolean executeCommand(String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception {
+ String[] comps = GenericUtils.split(args, ' ');
+ ValidateUtils.checkTrue(GenericUtils.length(comps) == 2, "Invalid number of arguments: %s", args);
+
+ String oldPath = resolveRemotePath(comps[0]);
+ String newPath = resolveRemotePath(comps[1]);
+ SftpClient sftp = getClient();
+ sftp.rename(oldPath, newPath);
+ return false;
+ }
+ },
+ new CommandExecutor() {
+ @Override
+ public String getName() {
+ return "help";
+ }
+
+ @Override
+ @SuppressWarnings("synthetic-access")
+ public boolean executeCommand(String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception {
+ ValidateUtils.checkTrue(GenericUtils.isEmpty(args), "Unexpected arguments: %s", args);
+ for (String cmd : commandsMap.keySet()) {
+ stdout.append('\t').println(cmd);
+ }
+ return false;
+ }
+ }
+ )) {
+ put(e.getName(), e);
+ }
+ }
+ });
+
+ public SftpCommand(SftpClient client) {
+ this.client = ValidateUtils.checkNotNull(client, "No client", GenericUtils.EMPTY_OBJECT_ARRAY);
+ }
+
+ public final SftpClient getClient() {
+ return client;
+ }
+
+ public void doInteractive(BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception {
+ SftpClient sftp = getClient();
+ setCurrentRemoteDirectory(sftp.canonicalPath("."));
+ while(true) {
+ stdout.append(getCurrentRemoteDirectory()).append(" > ").flush();
+ String line = stdin.readLine();
+ if (line == null) { // EOF
+ break;
+ }
+
+ line = line.trim();
+ if (GenericUtils.isEmpty(line)) {
+ continue;
+ }
+
+ String cmd, args;
+ int pos = line.indexOf(' ');
+ if (pos > 0) {
+ cmd = line.substring(0, pos);
+ args = line.substring(pos + 1).trim();
+ } else {
+ cmd = line;
+ args = "";
+ }
+
+ CommandExecutor exec = commandsMap.get(cmd);
+ try {
+ if (exec == null) {
+ stderr.append("Unknown command: ").println(line);
+ } else {
+ try {
+ if (exec.executeCommand(args, stdin, stdout, stderr)) {
+ break;
+ }
+ } catch(Exception e) {
+ stderr.append(e.getClass().getSimpleName()).append(": ").println(e.getMessage());
+ } finally {
+ stdout.flush();
+ }
+ }
+ } finally {
+ stderr.flush(); // just makings sure
+ }
+ }
+ }
+
+ protected String resolveRemotePath(String pathArg) {
+ String cwd = getCurrentRemoteDirectory();
+ if (GenericUtils.isEmpty(pathArg)) {
+ return cwd;
+ }
+
+ if (pathArg.charAt(0) == '/') {
+ return pathArg;
+ } else {
+ return cwd + "/" + pathArg;
+ }
+ }
+ public String getCurrentRemoteDirectory() {
+ return cwdRemote;
+ }
+
+ public void setCurrentRemoteDirectory(String path) {
+ cwdRemote = path;
+ }
+
+ @Override
+ public boolean isOpen() {
+ return client.isOpen();
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (isOpen()) {
+ client.close();
+ }
+ }
+
+ public static interface CommandExecutor extends NamedResource {
+ // return value is whether to stop running
+ boolean executeCommand(String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+
+ public static void main(String[] args) throws Exception {
+ PrintStream stdout=System.out, stderr=System.err;
+ try(BufferedReader stdin = new BufferedReader(new InputStreamReader(new NoCloseInputStream(System.in)))) {
+ ClientSession session=SshClient.setupClientSession("-P", stdin, stdout, stderr, args);
+ if (session == null) {
+ System.err.println("usage: sftp [-l login] [-P port] [-o option=value] hostname/user@host");
+ System.exit(-1);
+ return;
+ }
+
+ try(SshClient client = (SshClient) session.getFactoryManager()) {
+ try(SftpCommand sftp = new SftpCommand(session.createSftpClient())) {
+ sftp.doInteractive(stdin, stdout, stderr);
+ }
+ } finally {
+ session.close();
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/883efa01/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 796f678..072b85d 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
@@ -18,17 +18,6 @@
*/
package org.apache.sshd.client.subsystem.sftp;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.SFTP_V3;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.S_IRGRP;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.S_IROTH;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.S_IRUSR;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.S_IWGRP;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.S_IWOTH;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.S_IWUSR;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.S_IXGRP;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.S_IXOTH;
-import static org.apache.sshd.common.subsystem.sftp.SftpConstants.S_IXUSR;
-
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -390,7 +379,7 @@ public class SftpFileSystemProvider extends FileSystemProvider {
sftp.mkdir(dir.toString());
} catch (SftpException e) {
int sftpStatus=e.getStatus();
- if ((sftp.getVersion() == SFTP_V3) && (sftpStatus == SftpConstants.SSH_FX_FAILURE)) {
+ if ((sftp.getVersion() == SftpConstants.SFTP_V3) && (sftpStatus == SftpConstants.SSH_FX_FAILURE)) {
try {
Attributes attributes = sftp.stat(dir.toString());
if (attributes != null) {
@@ -940,31 +929,31 @@ public class SftpFileSystemProvider extends FileSystemProvider {
for (PosixFilePermission p : perms) {
switch (p) {
case OWNER_READ:
- pf |= S_IRUSR;
+ pf |= SftpConstants.S_IRUSR;
break;
case OWNER_WRITE:
- pf |= S_IWUSR;
+ pf |= SftpConstants.S_IWUSR;
break;
case OWNER_EXECUTE:
- pf |= S_IXUSR;
+ pf |= SftpConstants.S_IXUSR;
break;
case GROUP_READ:
- pf |= S_IRGRP;
+ pf |= SftpConstants.S_IRGRP;
break;
case GROUP_WRITE:
- pf |= S_IWGRP;
+ pf |= SftpConstants.S_IWGRP;
break;
case GROUP_EXECUTE:
- pf |= S_IXGRP;
+ pf |= SftpConstants.S_IXGRP;
break;
case OTHERS_READ:
- pf |= S_IROTH;
+ pf |= SftpConstants.S_IROTH;
break;
case OTHERS_WRITE:
- pf |= S_IWOTH;
+ pf |= SftpConstants.S_IWOTH;
break;
case OTHERS_EXECUTE:
- pf |= S_IXOTH;
+ pf |= SftpConstants.S_IXOTH;
break;
default:
if (log.isTraceEnabled()) {
@@ -976,39 +965,142 @@ public class SftpFileSystemProvider extends FileSystemProvider {
return pf;
}
+ public static String getRWXPermissions(int perms) {
+ StringBuilder sb=new StringBuilder(10 /* 3 * rwx + (d)irectory */);
+ if ((perms & SftpConstants.S_IFLNK) != 0) {
+ sb.append('l');
+ } else if ((perms & SftpConstants.S_IFDIR) != 0) {
+ sb.append('d');
+ } else {
+ sb.append('-');
+ }
+
+ if ((perms & SftpConstants.S_IRUSR) != 0) {
+ sb.append('r');
+ } else {
+ sb.append('-');
+ }
+ if ((perms & SftpConstants.S_IWUSR) != 0) {
+ sb.append('w');
+ } else {
+ sb.append('-');
+ }
+ if ((perms & SftpConstants.S_IXUSR) != 0) {
+ sb.append('x');
+ } else {
+ sb.append('-');
+ }
+
+ if ((perms & SftpConstants.S_IRGRP) != 0) {
+ sb.append('r');
+ } else {
+ sb.append('-');
+ }
+ if ((perms & SftpConstants.S_IWGRP) != 0) {
+ sb.append('w');
+ } else {
+ sb.append('-');
+ }
+ if ((perms & SftpConstants.S_IXGRP) != 0) {
+ sb.append('x');
+ } else {
+ sb.append('-');
+ }
+
+ if ((perms & SftpConstants.S_IROTH) != 0) {
+ sb.append('r');
+ } else {
+ sb.append('-');
+ }
+ if ((perms & SftpConstants.S_IWOTH) != 0) {
+ sb.append('w');
+ } else {
+ sb.append('-');
+ }
+ if ((perms & SftpConstants.S_IXOTH) != 0) {
+ sb.append('x');
+ } else {
+ sb.append('-');
+ }
+
+ return sb.toString();
+ }
+
+ public static String getOctalPermissions(int perms) {
+ return getOctalPermissions(permissionsToAttributes(perms));
+ }
public static Set<PosixFilePermission> permissionsToAttributes(int perms) {
Set<PosixFilePermission> p = new HashSet<>();
- if ((perms & S_IRUSR) != 0) {
+ if ((perms & SftpConstants.S_IRUSR) != 0) {
p.add(PosixFilePermission.OWNER_READ);
}
- if ((perms & S_IWUSR) != 0) {
+ if ((perms & SftpConstants.S_IWUSR) != 0) {
p.add(PosixFilePermission.OWNER_WRITE);
}
- if ((perms & S_IXUSR) != 0) {
+ if ((perms & SftpConstants.S_IXUSR) != 0) {
p.add(PosixFilePermission.OWNER_EXECUTE);
}
- if ((perms & S_IRGRP) != 0) {
+ if ((perms & SftpConstants.S_IRGRP) != 0) {
p.add(PosixFilePermission.GROUP_READ);
}
- if ((perms & S_IWGRP) != 0) {
+ if ((perms & SftpConstants.S_IWGRP) != 0) {
p.add(PosixFilePermission.GROUP_WRITE);
}
- if ((perms & S_IXGRP) != 0) {
+ if ((perms & SftpConstants.S_IXGRP) != 0) {
p.add(PosixFilePermission.GROUP_EXECUTE);
}
- if ((perms & S_IROTH) != 0) {
+ if ((perms & SftpConstants.S_IROTH) != 0) {
p.add(PosixFilePermission.OTHERS_READ);
}
- if ((perms & S_IWOTH) != 0) {
+ if ((perms & SftpConstants.S_IWOTH) != 0) {
p.add(PosixFilePermission.OTHERS_WRITE);
}
- if ((perms & S_IXOTH) != 0) {
+ if ((perms & SftpConstants.S_IXOTH) != 0) {
p.add(PosixFilePermission.OTHERS_EXECUTE);
}
return p;
}
+ public static String getOctalPermissions(Collection<PosixFilePermission> perms) {
+ int pf = 0;
+
+ for (PosixFilePermission p : perms) {
+ switch (p) {
+ case OWNER_READ:
+ pf |= SftpConstants.S_IRUSR;
+ break;
+ case OWNER_WRITE:
+ pf |= SftpConstants.S_IWUSR;
+ break;
+ case OWNER_EXECUTE:
+ pf |= SftpConstants.S_IXUSR;
+ break;
+ case GROUP_READ:
+ pf |= SftpConstants.S_IRGRP;
+ break;
+ case GROUP_WRITE:
+ pf |= SftpConstants.S_IWGRP;
+ break;
+ case GROUP_EXECUTE:
+ pf |= SftpConstants.S_IXGRP;
+ break;
+ case OTHERS_READ:
+ pf |= SftpConstants.S_IROTH;
+ break;
+ case OTHERS_WRITE:
+ pf |= SftpConstants.S_IWOTH;
+ break;
+ case OTHERS_EXECUTE:
+ pf |= SftpConstants.S_IXOTH;
+ break;
+ default: // ignored
+ }
+ }
+
+ return String.format("%04o", Integer.valueOf(pf));
+ }
+
/**
* Uses the host, port and username to create a unique identifier
* @param uri The {@link URI} - <B>Note:</B> not checked to make sure
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/883efa01/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/impl/AbstractMD5HashExtension.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/impl/AbstractMD5HashExtension.java b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/impl/AbstractMD5HashExtension.java
index 12c51ca..7aa7778 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/impl/AbstractMD5HashExtension.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/impl/AbstractMD5HashExtension.java
@@ -38,11 +38,15 @@ public abstract class AbstractMD5HashExtension extends AbstractSftpClientExtensi
super(name, client, raw, extras);
}
- protected byte[] doGetHash(String target, long offset, long length, byte[] quickHash) throws IOException {
+ protected byte[] doGetHash(Object target, long offset, long length, byte[] quickHash) throws IOException {
Buffer buffer = new ByteArrayBuffer();
String opcode = getName();
buffer.putString(opcode);
- buffer.putString(target);
+ if (target instanceof CharSequence) {
+ buffer.putString(target.toString());
+ } else {
+ buffer.putBytes((byte[]) target);
+ }
buffer.putLong(offset);
buffer.putLong(length);
buffer.putBytes((quickHash == null) ? GenericUtils.EMPTY_BYTE_ARRAY : quickHash);
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/883efa01/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/impl/MD5HandleExtensionImpl.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/impl/MD5HandleExtensionImpl.java b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/impl/MD5HandleExtensionImpl.java
index e6ab476..b5a482f 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/impl/MD5HandleExtensionImpl.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/impl/MD5HandleExtensionImpl.java
@@ -37,7 +37,7 @@ public class MD5HandleExtensionImpl extends AbstractMD5HashExtension implements
@Override
public byte[] getHash(SftpClient.Handle handle, long offset, long length, byte[] quickHash) throws IOException {
- return doGetHash(handle.id, offset, length, quickHash);
+ return doGetHash(handle.getIdentifier(), offset, length, quickHash);
}
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/883efa01/sshd-core/src/main/java/org/apache/sshd/common/random/AbstractRandom.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/random/AbstractRandom.java b/sshd-core/src/main/java/org/apache/sshd/common/random/AbstractRandom.java
new file mode 100644
index 0000000..372de9b
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/random/AbstractRandom.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.random;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractRandom implements Random {
+ protected AbstractRandom() {
+ super();
+ }
+
+ @Override // TODO in JDK-8 make this a default method
+ public void fill(byte[] bytes) {
+ fill(bytes, 0, bytes.length);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/883efa01/sshd-core/src/main/java/org/apache/sshd/common/random/JceRandom.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/random/JceRandom.java b/sshd-core/src/main/java/org/apache/sshd/common/random/JceRandom.java
index 6aaec25..7e1b3a5 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/random/JceRandom.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/random/JceRandom.java
@@ -25,7 +25,7 @@ import java.security.SecureRandom;
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public class JceRandom implements Random {
+public class JceRandom extends AbstractRandom {
private byte[] tmp = new byte[16];
private final SecureRandom random = new SecureRandom();
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/883efa01/sshd-core/src/main/java/org/apache/sshd/common/random/Random.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/random/Random.java b/sshd-core/src/main/java/org/apache/sshd/common/random/Random.java
index e3f2c78..1729001 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/random/Random.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/random/Random.java
@@ -24,10 +24,15 @@ package org.apache.sshd.common.random;
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
public interface Random {
+ /**
+ * Fill the buffer with random values
+ * @param bytes The bytes to fill
+ * @see #fill(byte[], int, int)
+ */
+ void fill(byte[] bytes); // TODO in JDK-8 make this a default method
/**
* Fill part of bytes with random values.
- *
* @param bytes byte array to be filled.
* @param start index to start filling at.
* @param len length of segment to fill.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/883efa01/sshd-core/src/main/java/org/apache/sshd/common/random/SingletonRandomFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/random/SingletonRandomFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/random/SingletonRandomFactory.java
index 8fc7d48..b53b2da 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/random/SingletonRandomFactory.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/random/SingletonRandomFactory.java
@@ -27,7 +27,7 @@ import org.apache.sshd.common.OptionalFeature;
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public class SingletonRandomFactory implements Random, RandomFactory {
+public class SingletonRandomFactory extends AbstractRandom implements RandomFactory {
private final NamedFactory<Random> factory;
private final Random random;
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/883efa01/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 1614985..16b9733 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
@@ -213,7 +213,7 @@ public class ScpHelper extends AbstractLoggingBean {
throw new IOException("Expected a D message but got '" + header + "'");
}
- Set<PosixFilePermission> perms = parseOctalPerms(header.substring(1, 5));
+ Set<PosixFilePermission> perms = parseOctalPermissions(header.substring(1, 5));
int length = Integer.parseInt(header.substring(6, header.indexOf(' ', 6)));
String name = header.substring(header.indexOf(' ', 6) + 1);
@@ -310,7 +310,7 @@ public class ScpHelper extends AbstractLoggingBean {
throw new IOException("receiveStream(" + resolver + ") buffer size (" + bufferSize + ") below minimum (" + MIN_RECEIVE_BUFFER_SIZE + ")");
}
- Set<PosixFilePermission> perms = parseOctalPerms(header.substring(1, 5));
+ Set<PosixFilePermission> perms = parseOctalPermissions(header.substring(1, 5));
final long length = Long.parseLong(header.substring(6, header.indexOf(' ', 6)));
String name = header.substring(header.indexOf(' ', 6) + 1);
if (length < 0L) { // TODO consider throwing an exception...
@@ -528,7 +528,7 @@ public class ScpHelper extends AbstractLoggingBean {
}
Set<PosixFilePermission> perms = EnumSet.copyOf(resolver.getPermissions());
- String octalPerms = preserve ? getOctalPerms(perms) : "0644";
+ String octalPerms = preserve ? getOctalPermissions(perms) : "0644";
String fileName = resolver.getFileName();
String cmd = new StringBuilder(octalPerms.length() + fileName.length() + Long.SIZE /* some extra delimiters */)
.append('C').append(octalPerms)
@@ -581,7 +581,7 @@ public class ScpHelper extends AbstractLoggingBean {
Set<PosixFilePermission> perms = IoUtils.getPermissions(path, options);
StringBuilder buf = new StringBuilder();
buf.append("D");
- buf.append(preserve ? getOctalPerms(perms) : "0755");
+ buf.append(preserve ? getOctalPermissions(perms) : "0755");
buf.append(" ");
buf.append("0"); // length
buf.append(" ");
@@ -615,11 +615,11 @@ public class ScpHelper extends AbstractLoggingBean {
readAck(false);
}
- public static String getOctalPerms(Path path, LinkOption ... options) throws IOException {
- return getOctalPerms(IoUtils.getPermissions(path, options));
+ public static String getOctalPermissions(Path path, LinkOption ... options) throws IOException {
+ return getOctalPermissions(IoUtils.getPermissions(path, options));
}
- public static String getOctalPerms(Collection<PosixFilePermission> perms) {
+ public static String getOctalPermissions(Collection<PosixFilePermission> perms) {
int pf = 0;
for (PosixFilePermission p : perms) {
@@ -658,13 +658,13 @@ public class ScpHelper extends AbstractLoggingBean {
return String.format("%04o", Integer.valueOf(pf));
}
- public static Set<PosixFilePermission> setOctalPerms(Path path, String str) throws IOException {
- Set<PosixFilePermission> perms = parseOctalPerms(str);
+ public static Set<PosixFilePermission> setOctalPermissions(Path path, String str) throws IOException {
+ Set<PosixFilePermission> perms = parseOctalPermissions(str);
IoUtils.setPermissions(path, perms);
return perms;
}
- public static Set<PosixFilePermission> parseOctalPerms(String str) {
+ public static Set<PosixFilePermission> parseOctalPermissions(String str) {
int perms = Integer.parseInt(str, 8);
Set<PosixFilePermission> p = EnumSet.noneOf(PosixFilePermission.class);
if ((perms & S_IRUSR) != 0) {
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/883efa01/sshd-core/src/main/java/org/apache/sshd/common/util/SecurityUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/SecurityUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/SecurityUtils.java
index 5c6d6fe..aa80440 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/SecurityUtils.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/SecurityUtils.java
@@ -42,6 +42,7 @@ import javax.crypto.Mac;
import org.apache.sshd.common.config.keys.FilePasswordProvider;
import org.apache.sshd.common.keyprovider.AbstractClassLoadableResourceKeyPairProvider;
import org.apache.sshd.common.keyprovider.AbstractFileKeyPairProvider;
+import org.apache.sshd.common.random.AbstractRandom;
import org.apache.sshd.common.random.JceRandomFactory;
import org.apache.sshd.common.random.Random;
import org.apache.sshd.common.random.RandomFactory;
@@ -312,7 +313,7 @@ public class SecurityUtils {
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
- private static class BouncyCastleRandom implements Random {
+ private static class BouncyCastleRandom extends AbstractRandom {
private final RandomGenerator random;
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/883efa01/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java
index 42697bc..0e3cb09 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java
@@ -41,8 +41,9 @@ public class BufferUtils {
return printHex(array, 0, GenericUtils.length(array), sep);
}
+ public static final char DEFAULT_HEX_SEPARATOR = ' ', EMPTY_HEX_SEPARATOR = '\0';
public static String printHex(byte[] array, int offset, int len) {
- return printHex(array, offset, len, ' ');
+ return printHex(array, offset, len, DEFAULT_HEX_SEPARATOR);
}
public static final String HEX_DIGITS="0123456789abcdef";
@@ -55,7 +56,7 @@ public class BufferUtils {
StringBuilder sb = new StringBuilder(len * 3 /* 2 HEX + sep */);
for (int curOffset = offset, maxOffset = offset + len; curOffset < maxOffset; curOffset++) {
byte b = array[curOffset];
- if (sb.length() > 0) {
+ if ((sb.length() > 0) && (sep != EMPTY_HEX_SEPARATOR)) {
sb.append(sep);
}
sb.append(HEX_DIGITS.charAt((b >> 4) & 0x0F));
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/883efa01/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java b/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java
index 0ff4943..43e3bc2 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java
@@ -72,21 +72,23 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
-import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
+import org.apache.sshd.common.Factory;
import org.apache.sshd.common.FactoryManager;
import org.apache.sshd.common.FactoryManagerUtils;
import org.apache.sshd.common.config.VersionProperties;
import org.apache.sshd.common.digest.BuiltinDigests;
import org.apache.sshd.common.digest.Digest;
import org.apache.sshd.common.file.FileSystemAware;
+import org.apache.sshd.common.random.Random;
import org.apache.sshd.common.subsystem.sftp.SftpConstants;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.OsUtils;
import org.apache.sshd.common.util.SelectorUtils;
+import org.apache.sshd.common.util.ValidateUtils;
import org.apache.sshd.common.util.buffer.Buffer;
import org.apache.sshd.common.util.buffer.BufferUtils;
import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
@@ -110,6 +112,16 @@ public class SftpSubsystem extends AbstractLoggingBean implements Command, Runna
* Properties key for the maximum of available open handles per session.
*/
public static final String MAX_OPEN_HANDLES_PER_SESSION = "max-open-handles-per-session";
+ public static final int DEFAULT_MAX_OPEN_HANDLES = Integer.MAX_VALUE;
+
+ /**
+ * Size in bytes of the opaque handle value
+ * @see #DEFAULT_FILE_HANDLE_SIZE
+ */
+ public static final String FILE_HANDLE_SIZE = "sftp-handle-size";
+ public static final int MIN_FILE_FILE_HANDLE_SIZE = 4; // ~uint32
+ public static final int DEFAULT_FILE_HANDLE_SIZE = 16;
+ public static final int MAX_FILE_FILE_HANDLE_SIZE = 64; // ~sha512
/**
* Force the use of a given sftp version
@@ -170,12 +182,14 @@ public class SftpSubsystem extends AbstractLoggingBean implements Command, Runna
private OutputStream out;
private OutputStream err;
private Environment env;
+ private Random randomizer;
+ private int fileHandleSize = DEFAULT_FILE_HANDLE_SIZE;
private ServerSession session;
- private boolean closed = false;
+ private boolean closed;
private ExecutorService executors;
private boolean shutdownExecutor;
private Future<?> pendingFuture;
- private final byte[] workBuf = new byte[Integer.SIZE / Byte.SIZE]; // TODO in JDK-8 use Integer.BYTES
+ private byte[] workBuf = new byte[Math.max(DEFAULT_FILE_HANDLE_SIZE, Integer.SIZE / Byte.SIZE)]; // TODO in JDK-8 use Integer.BYTES
private FileSystem fileSystem = FileSystems.getDefault();
private Path defaultDir = fileSystem.getPath(System.getProperty("user.dir"));
private long requestsCount;
@@ -428,6 +442,17 @@ public class SftpSubsystem extends AbstractLoggingBean implements Command, Runna
@Override
public void setSession(ServerSession session) {
this.session = session;
+
+ FactoryManager manager = session.getFactoryManager();
+ Factory<? extends Random> factory = manager.getRandomFactory();
+ this.randomizer = factory.create();
+ this.fileHandleSize = FactoryManagerUtils.getIntProperty(manager, FILE_HANDLE_SIZE, DEFAULT_FILE_HANDLE_SIZE);
+ ValidateUtils.checkTrue(this.fileHandleSize >= MIN_FILE_FILE_HANDLE_SIZE, "File handle size too small: %d", this.fileHandleSize);
+ ValidateUtils.checkTrue(this.fileHandleSize <= MAX_FILE_FILE_HANDLE_SIZE, "File handle size too big: %d", this.fileHandleSize);
+
+ if (workBuf.length < this.fileHandleSize) {
+ workBuf = new byte[this.fileHandleSize];
+ }
}
@Override
@@ -707,8 +732,8 @@ public class SftpSubsystem extends AbstractLoggingBean implements Command, Runna
Digest digest = BuiltinDigests.md5.create();
digest.init();
- byte[] workBuf = new byte[(int) Math.min(effectiveLength, SftpConstants.MD5_QUICK_HASH_SIZE)];
- ByteBuffer bb = ByteBuffer.wrap(workBuf);
+ byte[] digestBuf = new byte[(int) Math.min(effectiveLength, SftpConstants.MD5_QUICK_HASH_SIZE)];
+ ByteBuffer bb = ByteBuffer.wrap(digestBuf);
boolean hashMatches = false;
byte[] hashValue = null;
@@ -729,7 +754,7 @@ public class SftpSubsystem extends AbstractLoggingBean implements Command, Runna
} else {
int readLen = channel.read(bb);
effectiveLength -= readLen;
- digest.update(workBuf, 0, readLen);
+ digest.update(digestBuf, 0, readLen);
hashValue = digest.digest();
hashMatches = Arrays.equals(quickCheckHash, hashValue);
@@ -744,7 +769,7 @@ public class SftpSubsystem extends AbstractLoggingBean implements Command, Runna
if (effectiveLength > 0L) {
digest = BuiltinDigests.md5.create();
digest.init();
- digest.update(workBuf, 0, readLen);
+ digest.update(digestBuf, 0, readLen);
hashValue = null; // start again
}
} else {
@@ -761,7 +786,7 @@ public class SftpSubsystem extends AbstractLoggingBean implements Command, Runna
bb.clear();
int readLen = channel.read(bb);
effectiveLength -= readLen;
- digest.update(workBuf, 0, readLen);
+ digest.update(digestBuf, 0, readLen);
}
if (hashValue == null) { // check if did any more iterations after the quick hash
@@ -1282,7 +1307,7 @@ public class SftpSubsystem extends AbstractLoggingBean implements Command, Runna
} else if (!Files.isReadable(p)) {
sendStatus(id, SSH_FX_PERMISSION_DENIED, path);
} else {
- String handle = UUID.randomUUID().toString();
+ String handle = generateFileHandle(p);
handles.put(handle, new DirectoryHandle(p));
sendHandle(id, handle);
}
@@ -1447,7 +1472,7 @@ public class SftpSubsystem extends AbstractLoggingBean implements Command, Runna
protected void doOpen(Buffer buffer, int id) throws IOException {
int curHandleCount = handles.size();
- int maxHandleCount = FactoryManagerUtils.getIntProperty(session, MAX_OPEN_HANDLES_PER_SESSION, Integer.MAX_VALUE);
+ int maxHandleCount = FactoryManagerUtils.getIntProperty(session, MAX_OPEN_HANDLES_PER_SESSION, DEFAULT_MAX_OPEN_HANDLES);
if (curHandleCount > maxHandleCount) {
sendStatus(id, SSH_FX_FAILURE, "Too many open handles: current=" + curHandleCount + ", max.=" + maxHandleCount);
return;
@@ -1512,7 +1537,7 @@ public class SftpSubsystem extends AbstractLoggingBean implements Command, Runna
}
try {
Path file = resolveFile(path);
- String handle = UUID.randomUUID().toString();
+ String handle = generateFileHandle(file);
handles.put(handle, new FileHandle(file, pflags, access, attrs));
sendHandle(id, handle);
} catch (IOException e) {
@@ -1520,6 +1545,14 @@ public class SftpSubsystem extends AbstractLoggingBean implements Command, Runna
}
}
+ // we stringify our handles and treat them as such on decoding as well as it is easier to use as a map key
+ protected String generateFileHandle(Path file) {
+ randomizer.fill(workBuf, 0, fileHandleSize);
+ String handle = BufferUtils.printHex(workBuf, 0, fileHandleSize, BufferUtils.EMPTY_HEX_SEPARATOR);
+ log.trace("generateFileHandle({}) {}", file, handle);
+ return handle;
+ }
+
protected void doInit(Buffer buffer, int id) throws IOException {
if (log.isDebugEnabled()) {
log.debug("Received SSH_FXP_INIT (version={})", Integer.valueOf(id));
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/883efa01/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 00f3d27..895852c 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
@@ -723,7 +723,7 @@ public class ScpTest extends BaseTestSupport {
String remotePath = Utils.resolveRelativeRemotePath(parentPath, remoteDir);
String fileName = getCurrentTestName() + ".txt";
Path remoteFile = remoteDir.resolve(fileName);
- String mode = ScpHelper.getOctalPerms(EnumSet.of(
+ String mode = ScpHelper.getOctalPermissions(EnumSet.of(
PosixFilePermission.OTHERS_READ, PosixFilePermission.OTHERS_WRITE,
PosixFilePermission.GROUP_READ, PosixFilePermission.GROUP_WRITE,
PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/883efa01/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/DefaultCloseableHandleTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/DefaultCloseableHandleTest.java b/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/DefaultCloseableHandleTest.java
index 8dbab78..1f9c6db 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/DefaultCloseableHandleTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/DefaultCloseableHandleTest.java
@@ -20,10 +20,9 @@
package org.apache.sshd.client.subsystem.sftp;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicBoolean;
-import org.apache.sshd.client.subsystem.sftp.DefaultCloseableHandle;
-import org.apache.sshd.client.subsystem.sftp.SftpClient;
import org.apache.sshd.client.subsystem.sftp.SftpClient.CloseableHandle;
import org.apache.sshd.client.subsystem.sftp.SftpClient.Handle;
import org.apache.sshd.util.BaseTestSupport;
@@ -46,14 +45,14 @@ public class DefaultCloseableHandleTest extends BaseTestSupport {
@Test
public void testChannelBehavior() throws IOException {
- final String id = getCurrentTestName();
+ final byte[] id = getCurrentTestName().getBytes(StandardCharsets.UTF_8);
SftpClient client = Mockito.mock(SftpClient.class);
Mockito.doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
Object[] args = invocation.getArguments();
Handle handle = (Handle) args[0];
- assertEquals("Mismatched closing handle", id, handle.id);
+ assertArrayEquals("Mismatched closing handle", id, handle.getIdentifier());
return null;
}
}).when(client).close(Matchers.any(Handle.class));
@@ -82,7 +81,7 @@ public class DefaultCloseableHandleTest extends BaseTestSupport {
}
}).when(client).close(Matchers.any(Handle.class));
- CloseableHandle handle = new DefaultCloseableHandle(client, getCurrentTestName());
+ CloseableHandle handle = new DefaultCloseableHandle(client, getCurrentTestName().getBytes(StandardCharsets.UTF_8));
for (int index=0; index < Byte.SIZE; index++) {
handle.close();
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/883efa01/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpCommandMain.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpCommandMain.java b/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpCommandMain.java
new file mode 100644
index 0000000..8cca06f
--- /dev/null
+++ b/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpCommandMain.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.client.subsystem.sftp;
+
+/**
+ * Just a test class used to invoke {@link SftpCommand#main(String[])} in
+ * order to have logging - which is in {@code test} scope
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class SftpCommandMain {
+ public static void main(String[] args) throws Exception {
+ SftpCommand.main(args);
+ }
+}