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/16 15:48:38 UTC

[2/2] mina-sshd git commit: [SSHD-541] Re-use incoming request buffer for outgoing response (where applicable)

[SSHD-541] Re-use incoming request buffer for outgoing response (where applicable)


Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/0d86de53
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/0d86de53
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/0d86de53

Branch: refs/heads/master
Commit: 0d86de5384857e29b5a6f9e679fcdbf2e5b32f51
Parents: 1c80bb6
Author: Lyor Goldstein <lg...@vmware.com>
Authored: Thu Jul 16 16:42:28 2015 +0300
Committer: Lyor Goldstein <lg...@vmware.com>
Committed: Thu Jul 16 16:44:54 2015 +0300

----------------------------------------------------------------------
 .../sshd/agent/common/AbstractAgentClient.java  |  98 ++++---
 .../sshd/agent/local/AgentForwardedChannel.java |   6 +-
 .../agent/local/ChannelAgentForwarding.java     |   1 -
 .../org/apache/sshd/client/SftpException.java   |  44 ----
 .../subsystem/sftp/AbstractSftpClient.java      | 258 +++++++++++++------
 .../subsystem/sftp/DefaultSftpClient.java       |  45 ++--
 .../client/subsystem/sftp/SftpException.java    |  44 ++++
 .../client/subsystem/sftp/SftpFileChannel.java  |   1 -
 .../subsystem/sftp/SftpFileSystemProvider.java  |   5 +-
 .../impl/AbstractSftpClientExtension.java       |   8 +-
 .../sshd/common/channel/AbstractChannel.java    |  69 ++---
 .../sshd/common/channel/RequestHandler.java     |  49 +++-
 .../common/forward/DefaultTcpipForwarder.java   |   7 +-
 .../session/AbstractConnectionService.java      |  62 +++--
 .../sshd/common/session/AbstractSession.java    |  33 +--
 .../openssh/AbstractOpenSSHExtensionParser.java |   2 +-
 .../sshd/common/util/io/FileInfoExtractor.java  |  85 ++++++
 .../global/CancelTcpipForwardHandler.java       |  11 +-
 .../sshd/server/global/TcpipForwardHandler.java |  11 +-
 .../server/subsystem/sftp/SftpSubsystem.java    | 222 ++++++++++++----
 .../sshd/server/x11/X11ForwardSupport.java      |   7 +-
 .../org/apache/sshd/client/scp/ScpTest.java     |  44 +++-
 .../subsystem/sftp/SftpFileSystemTest.java      |  24 +-
 .../sshd/client/subsystem/sftp/SftpTest.java    |   9 +-
 .../impl/AbstractCheckFileExtensionTest.java    |   2 +-
 .../impl/AbstractMD5HashExtensionTest.java      |   2 +-
 .../impl/CopyFileExtensionImplTest.java         |   2 +-
 27 files changed, 800 insertions(+), 351 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0d86de53/sshd-core/src/main/java/org/apache/sshd/agent/common/AbstractAgentClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/agent/common/AbstractAgentClient.java b/sshd-core/src/main/java/org/apache/sshd/agent/common/AbstractAgentClient.java
index 6940bcd..0730cf6 100644
--- a/sshd-core/src/main/java/org/apache/sshd/agent/common/AbstractAgentClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/agent/common/AbstractAgentClient.java
@@ -29,12 +29,14 @@ import static org.apache.sshd.agent.SshAgentConstants.SSH2_AGENT_SIGN_RESPONSE;
 import static org.apache.sshd.agent.SshAgentConstants.SSH_AGENT_SUCCESS;
 
 import java.io.IOException;
+import java.security.KeyPair;
 import java.security.PublicKey;
 import java.util.List;
 
 import org.apache.sshd.agent.SshAgent;
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.util.Pair;
+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;
@@ -51,21 +53,43 @@ public abstract class AbstractAgentClient extends AbstractLoggingBean {
 
     public synchronized void messageReceived(Buffer message) throws IOException {
         buffer.putBuffer(message);
-        if (buffer.available() < 4) {
+        int avail = buffer.available();
+        if (avail < 4) {
+            if (log.isTraceEnabled()) {
+                log.trace("Received message total length ({}) below minuimum ({})",
+                          Integer.valueOf(avail), Integer.valueOf(4)); 
+            }
             return;
         }
+
         int rpos = buffer.rpos();
         int len = buffer.getInt();
         buffer.rpos(rpos);
-        if (buffer.available() < len + 4) {
+        
+        avail = buffer.available();
+        if (avail < (len + 4)) {
+            if (log.isTraceEnabled()) {
+                log.trace("Received request length ({}) below minuimum ({})",
+                          Integer.valueOf(avail), Integer.valueOf(len + 4)); 
+            }
             return;
         }
-        Buffer rep = new ByteArrayBuffer();
+
+        // we can re-use the incoming message buffer since its data has been copied to the request buffer
+        Buffer rep = BufferUtils.clear(message);
         rep.putInt(0);
         rep.rpos(rep.wpos());
+        
+        Buffer req = new ByteArrayBuffer(buffer.getBytes());
+        int cmd = (-1);
         try {
-            process(new ByteArrayBuffer(buffer.getBytes()), rep);
+            cmd = req.getUByte();
+            process(cmd, req, rep);
         } catch (Exception e) {
+            if (log.isDebugEnabled()) {
+                log.debug("Failed ({}) to handle command={}: {}",
+                          e.getClass().getSimpleName(), Integer.valueOf(cmd), e.getMessage());
+            }
             rep.clear();
             rep.putInt(0);
             rep.rpos(rep.wpos());
@@ -75,45 +99,55 @@ public abstract class AbstractAgentClient extends AbstractLoggingBean {
         reply(prepare(rep));
     }
 
-    protected void process(Buffer req, Buffer rep) throws Exception {
-        int cmd = req.getUByte();
+    protected void process(int cmd, Buffer req, Buffer rep) throws Exception {
+        if (log.isDebugEnabled()) {
+            log.debug("process(cmd={})", Integer.valueOf(cmd));
+        }
         switch (cmd) {
             case SSH2_AGENTC_REQUEST_IDENTITIES: {
-                List<Pair<PublicKey,String>> keys = agent.getIdentities();
-                rep.putByte(SSH2_AGENT_IDENTITIES_ANSWER);
-                rep.putInt(keys.size());
-                for (Pair<PublicKey,String> key : keys) {
-                    rep.putPublicKey(key.getFirst());
-                    rep.putString(key.getSecond());
+                    List<Pair<PublicKey,String>> keys = agent.getIdentities();
+                    rep.putByte(SSH2_AGENT_IDENTITIES_ANSWER);
+                    rep.putInt(keys.size());
+                    for (Pair<PublicKey,String> key : keys) {
+                        rep.putPublicKey(key.getFirst());
+                        rep.putString(key.getSecond());
+                    }
                 }
                 break;
-            }
             case SSH2_AGENTC_SIGN_REQUEST: {
-                PublicKey key = req.getPublicKey();
-                byte[] data = req.getBytes();
-                int flags = req.getInt();
-                if (log.isDebugEnabled()) {
-                    log.debug("SSH2_AGENTC_SIGN_REQUEST key={}, flags=0x{}, data={}",
-                              key.getAlgorithm(), Integer.toHexString(flags), BufferUtils.printHex(':', data));
+                    PublicKey key = req.getPublicKey();
+                    byte[] data = req.getBytes();
+                    int flags = req.getInt();
+                    if (log.isDebugEnabled()) {
+                        log.debug("SSH2_AGENTC_SIGN_REQUEST key={}, flags=0x{}, data={}",
+                                  key.getAlgorithm(), Integer.toHexString(flags), BufferUtils.printHex(':', data));
+                    }
+                    String keyType = ValidateUtils.checkNotNullAndNotEmpty(
+                                            KeyUtils.getKeyType(key),
+                                            "Cannot resolve key type of %s",
+                                            key.getClass().getSimpleName());
+                    Buffer sig = new ByteArrayBuffer();
+                    sig.putString(keyType);
+                    sig.putBytes(agent.sign(key, data));
+                    rep.putByte(SSH2_AGENT_SIGN_RESPONSE);
+                    rep.putBytes(sig.array(), sig.rpos(), sig.available());
                 }
-                Buffer sig = new ByteArrayBuffer();
-                sig.putString(KeyUtils.getKeyType(key));
-                sig.putBytes(agent.sign(key, data));
-                rep.putByte(SSH2_AGENT_SIGN_RESPONSE);
-                rep.putBytes(sig.array(), sig.rpos(), sig.available());
                 break;
-            }
             case SSH2_AGENTC_ADD_IDENTITY: {
-                agent.addIdentity(req.getKeyPair(), req.getString());
-                rep.putByte(SSH_AGENT_SUCCESS);
+                    KeyPair kp = req.getKeyPair();
+                    String comment = req.getString();
+                    log.debug("SSH2_AGENTC_ADD_IDENTITY comment={}", comment);
+                    agent.addIdentity(kp, comment);
+                    rep.putByte(SSH_AGENT_SUCCESS);
+                }
                 break;
-            }
             case SSH2_AGENTC_REMOVE_IDENTITY: {
-                PublicKey key = req.getPublicKey();
-                agent.removeIdentity(key);
-                rep.putByte(SSH_AGENT_SUCCESS);
+                    PublicKey key = req.getPublicKey();
+                    log.debug("SSH2_AGENTC_REMOVE_IDENTITY {}", key.getClass().getSimpleName());
+                    agent.removeIdentity(key);
+                    rep.putByte(SSH_AGENT_SUCCESS);
+                }
                 break;
-            }
             case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
                 agent.removeAllIdentities();
                 rep.putByte(SSH_AGENT_SUCCESS);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0d86de53/sshd-core/src/main/java/org/apache/sshd/agent/local/AgentForwardedChannel.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/agent/local/AgentForwardedChannel.java b/sshd-core/src/main/java/org/apache/sshd/agent/local/AgentForwardedChannel.java
index d0f8c39..d9e7521 100644
--- a/sshd-core/src/main/java/org/apache/sshd/agent/local/AgentForwardedChannel.java
+++ b/sshd-core/src/main/java/org/apache/sshd/agent/local/AgentForwardedChannel.java
@@ -20,6 +20,7 @@ package org.apache.sshd.agent.local;
 
 import java.io.IOException;
 import java.io.InterruptedIOException;
+import java.io.OutputStream;
 import java.util.Queue;
 import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -68,8 +69,9 @@ public class AgentForwardedChannel extends AbstractClientChannel {
     protected Buffer request(Buffer buffer) throws IOException {
         synchronized (messages) {
             try {
-                getInvertedIn().write(buffer.array(), buffer.rpos(), buffer.available());
-                getInvertedIn().flush();
+                OutputStream outputStream = getInvertedIn();
+                outputStream.write(buffer.array(), buffer.rpos(), buffer.available());
+                outputStream.flush();
                 localWindow.consumeAndCheck(buffer.available());
                 if (messages.isEmpty()) {
                     messages.wait();

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0d86de53/sshd-core/src/main/java/org/apache/sshd/agent/local/ChannelAgentForwarding.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/agent/local/ChannelAgentForwarding.java b/sshd-core/src/main/java/org/apache/sshd/agent/local/ChannelAgentForwarding.java
index a8e0bd8..59eb2f8 100644
--- a/sshd-core/src/main/java/org/apache/sshd/agent/local/ChannelAgentForwarding.java
+++ b/sshd-core/src/main/java/org/apache/sshd/agent/local/ChannelAgentForwarding.java
@@ -107,5 +107,4 @@ public class ChannelAgentForwarding extends AbstractServerChannel {
             out.flush();
         }
     }
-
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0d86de53/sshd-core/src/main/java/org/apache/sshd/client/SftpException.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/SftpException.java b/sshd-core/src/main/java/org/apache/sshd/client/SftpException.java
deleted file mode 100644
index f06d406..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/SftpException.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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;
-
-import java.io.IOException;
-
-/**
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public class SftpException extends IOException {
-    private static final long serialVersionUID = 8096963562429466995L;
-    private final int status;
-
-    public SftpException(int status, String msg) {
-        super(msg);
-        this.status = status;
-    }
-
-    public int getStatus() {
-        return status;
-    }
-
-    @Override
-    public String toString() {
-        String message = getLocalizedMessage();
-        return "SFTP error (" + status + "): " + message;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0d86de53/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 f896a8d..8f92951 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
@@ -100,7 +100,6 @@ import java.util.Map;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
 
-import org.apache.sshd.client.SftpException;
 import org.apache.sshd.client.subsystem.sftp.extensions.BuiltinSftpClientExtensions;
 import org.apache.sshd.client.subsystem.sftp.extensions.SftpClientExtension;
 import org.apache.sshd.client.subsystem.sftp.extensions.SftpClientExtensionFactory;
@@ -119,7 +118,7 @@ import org.apache.sshd.common.util.logging.AbstractLoggingBean;
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
 public abstract class AbstractSftpClient extends AbstractLoggingBean implements SftpClient, RawSftpClient {
-    private final AtomicReference<Map<String,Object>> parsedExtensionsHolder = new AtomicReference<>(null);
+    private final AtomicReference<Map<String,Object>> parsedExtensionsHolder = new AtomicReference<Map<String,Object>>(null);
 
     protected AbstractSftpClient() {
         super();
@@ -248,7 +247,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
         Map<String,Object> parsed = parsedExtensionsHolder.get();
         if (parsed == null) {
             if ((parsed=ParserUtils.parse(extensions)) == null) {
-                parsed = Collections.emptyMap();
+                parsed = Collections.<String,Object>emptyMap();
             }
             parsedExtensionsHolder.set(parsed);
         }
@@ -256,6 +255,29 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
         return parsed;
     }
 
+    /**
+     * Sends the specified command, waits for the response and then invokes {@link #checkStatus(Buffer)}
+     * @param cmd The command to send
+     * @param request The request {@link Buffer}
+     * @throws IOException If failed to send, receive or check the returned status
+     * @see #send(int, Buffer)
+     * @see #receive(int)
+     * @see #checkStatus(Buffer)
+     */
+    protected void checkStatus(int cmd, Buffer request) throws IOException {
+        int reqId = send(cmd, request);
+        Buffer response = receive(reqId);
+        checkStatus(response);
+    }
+
+    /**
+     * Checks if the incoming response is an {@code SSH_FXP_STATUS} one,
+     * and if so whether the substatus is {@code SSH_FX_OK}.
+     * @param buffer The received response {@link Buffer}
+     * @throws IOException If response does not carry a status or carries
+     * a bad status code
+     * @see #checkStatus(int, int, String, String)
+     */
     protected void checkStatus(Buffer buffer) throws IOException {
         int length = buffer.getInt();
         int type = buffer.getUByte();
@@ -264,69 +286,126 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
             int substatus = buffer.getInt();
             String msg = buffer.getString();
             String lang = buffer.getString();
-            if (log.isTraceEnabled()) {
-                log.trace("checkStatus(id={}) - status: {} [{}] {}", id, substatus, lang, msg);
-            }
-
-            if (substatus != SSH_FX_OK) {
-                throw new SftpException(substatus, msg);
-            }
+            checkStatus(id, substatus, msg, lang);
         } else {
             throw new SshException("Unexpected SFTP packet received: type=" + type + ", id=" + id + ", length=" + length);
         }
     }
 
+    /**
+     * @param id The request id
+     * @param substatus The sub-status value
+     * @param msg The message
+     * @param lang The language
+     * @throws IOException if the sub-status is not {@code SSH_FX_OK}
+     * @see #throwStatusException(int, int, String, String)
+     */
+    protected void checkStatus(int id, int substatus, String msg, String lang) throws IOException {
+        if (log.isTraceEnabled()) {
+            log.trace("checkStatus(id=" + id + ") status: " + substatus + " [" + lang + "]" + msg );
+        }
+
+        if (substatus != SSH_FX_OK) {
+            throwStatusException(id, substatus, msg, lang);
+        }
+    }
+
+    protected void throwStatusException(int id, int substatus, String msg, String lang) throws IOException {
+        throw new SftpException(substatus, msg);
+    }
+
+    /**
+     * @param cmd Command to be sent
+     * @param request The {@link Buffer} containing the request
+     * @return The received handle identifier
+     * @throws IOException If failed to send/receive or process the response
+     * @see #send(int, Buffer)
+     * @see #receive(int)
+     * @see #checkHandle(Buffer)
+     */
+    protected byte[] checkHandle(int cmd, Buffer request) throws IOException {
+        int reqId = send(cmd, request);
+        Buffer response = receive(reqId);
+        return checkHandle(response);
+    }
+
     protected byte[] checkHandle(Buffer buffer) throws IOException {
         int length = buffer.getInt();
         int type = buffer.getUByte();
         int id = buffer.getInt();
+        if (type == SSH_FXP_HANDLE) {
+            return ValidateUtils.checkNotNullAndNotEmpty(buffer.getBytes(), "Null/empty handle in buffer", GenericUtils.EMPTY_OBJECT_ARRAY);
+        }
+
         if (type == SSH_FXP_STATUS) {
             int substatus = buffer.getInt();
             String msg = buffer.getString();
             String lang = buffer.getString();
             if (log.isTraceEnabled()) {
-                log.trace("checkHandle(id={}) - status: {} [{}] {}", id, substatus, lang, msg);
+                log.trace("checkHandle(id={}) - status: {} [{}] {}", Integer.valueOf(id), Integer.valueOf(substatus), lang, msg);
             }
-            throw new SftpException(substatus, msg);
-        } else if (type == SSH_FXP_HANDLE) {
-            return ValidateUtils.checkNotNullAndNotEmpty(buffer.getBytes(), "Null/empty handle in buffer");
-        } else {
-            throw new SshException("Unexpected SFTP packet received: type=" + type + ", id=" + id + ", length=" + length);
+            throwStatusException(id, substatus, msg, lang);
         }
+
+        throw new SshException("Unexpected SFTP packet received: type=" + type + ", id=" + id + ", length=" + length);
+    }
+
+    /**
+     * @param cmd Command to be sent
+     * @param request Request {@link Buffer}
+     * @return The decoded response {@link Attributes}
+     * @throws IOException If failed to send/receive or process the response
+     * @see #send(int, Buffer)
+     * @see #receive(int)
+     * @see #checkAttributes(Buffer)
+     */
+    protected Attributes checkAttributes(int cmd, Buffer request) throws IOException {
+        int reqId = send(cmd, request);
+        Buffer response = receive(reqId);
+        return checkAttributes(response);
     }
 
     protected Attributes checkAttributes(Buffer buffer) throws IOException {
         int length = buffer.getInt();
         int type = buffer.getUByte();
         int id = buffer.getInt();
+        if (type == SSH_FXP_ATTRS) {
+            return readAttributes(buffer);
+        }            
+
         if (type == SSH_FXP_STATUS) {
             int substatus = buffer.getInt();
             String msg = buffer.getString();
             String lang = buffer.getString();
             if (log.isTraceEnabled()) {
-                log.trace("checkAttributes(id={}) - status: {} [{}] {}", id, substatus, lang, msg);
+                log.trace("checkAttributes(id={}) - status: {} [{}] {}", Integer.valueOf(id), Integer.valueOf(substatus), lang, msg);
             }
-            throw new SftpException(substatus, msg);
-        } else if (type == SSH_FXP_ATTRS) {
-            return readAttributes(buffer);
-        } else {
-            throw new SshException("Unexpected SFTP packet received: type=" + type + ", id=" + id + ", length=" + length);
+            throwStatusException(id, substatus, msg, lang);
         }
+
+        throw new SshException("Unexpected SFTP packet received: type=" + type + ", id=" + id + ", length=" + length);
+    }
+
+    /**
+     * @param cmd Command to be sent
+     * @param request The request {@link Buffer}
+     * @return The retrieve name
+     * @throws IOException If failed to send/receive or process the response
+     * @see #send(int, Buffer)
+     * @see #receive(int)
+     * @see #checkOneName(Buffer)
+     */
+    protected String checkOneName(int cmd, Buffer request) throws IOException {
+        int reqId = send(cmd, request);
+        Buffer response = receive(reqId);
+        return checkOneName(response);
     }
 
     protected String checkOneName(Buffer buffer) throws IOException {
         int length = buffer.getInt();
         int type = buffer.getUByte();
         int id = buffer.getInt();
-        if (type == SSH_FXP_STATUS) {
-            int substatus = buffer.getInt();
-            String msg = buffer.getString();
-            String lang = buffer.getString();
-            if (log.isTraceEnabled()) {
-                log.trace("checkOneName(id={}) - status: {} [{}] {}", id, substatus, lang, msg);
-            }
-            throw new SftpException(substatus, msg);
-        } else if (type == SSH_FXP_NAME) {
+        if (type == SSH_FXP_NAME) {
             int len = buffer.getInt();
             if (len != 1) {
                 throw new SshException("SFTP error: received " + len + " names instead of 1");
@@ -338,12 +417,23 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
             }
             Attributes attrs = readAttributes(buffer);
             if (log.isTraceEnabled()) {
-                log.trace("checkOneName(id={}) ({})[{}]: {}", id, name, longName, attrs);
+                log.trace("checkOneName(id={}) ({})[{}]: {}", Integer.valueOf(id), name, longName, attrs);
             }
             return name;
-        } else {
-            throw new SshException("Unexpected SFTP packet received: type=" + type + ", id=" + id + ", length=" + length);
         }
+        
+        if (type == SSH_FXP_STATUS) {
+            int substatus = buffer.getInt();
+            String msg = buffer.getString();
+            String lang = buffer.getString();
+            if (log.isTraceEnabled()) {
+                log.trace("checkOneName(id={}) - status: {} [{}] {}", Integer.valueOf(id), Integer.valueOf(substatus), lang, msg);
+            }
+
+            throwStatusException(id, substatus, msg, lang);
+        }
+
+        throw new SshException("Unexpected SFTP packet received: type=" + type + ", id=" + id + ", length=" + length);
     }
 
     protected Attributes readAttributes(Buffer buffer) throws IOException {
@@ -605,7 +695,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
         }
         buffer.putInt(mode);
         writeAttributes(buffer, new Attributes());
-        return new DefaultCloseableHandle(this, checkHandle(receive(send(SSH_FXP_OPEN, buffer))));
+        return new DefaultCloseableHandle(this, checkHandle(SSH_FXP_OPEN, buffer));
     }
 
     @Override
@@ -617,7 +707,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
         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)));
+        checkStatus(SSH_FXP_CLOSE, buffer);
     }
 
     @Override
@@ -628,7 +718,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
 
         Buffer buffer = new ByteArrayBuffer(path.length() + Long.SIZE /* some extra fields */);
         buffer.putString(path);
-        checkStatus(receive(send(SSH_FXP_REMOVE, buffer)));
+        checkStatus(SSH_FXP_REMOVE, buffer);
     }
 
     @Override
@@ -663,7 +753,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
             throw new UnsupportedOperationException("rename(" + oldPath + " => " + newPath + ")"
                                                   + " - copy options can not be used with this SFTP version: " + options);
         }
-        checkStatus(receive(send(SSH_FXP_RENAME, buffer)));
+        checkStatus(SSH_FXP_RENAME, buffer);
     }
 
     @Override
@@ -677,33 +767,41 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
         buffer.putBytes(id);
         buffer.putLong(fileOffset);
         buffer.putInt(len);
-        return checkData(receive(send(SSH_FXP_READ, buffer)), dstOffset, dst);
+        return checkData(SSH_FXP_READ, buffer, dstOffset, dst);
+    }
+
+    protected int checkData(int cmd, Buffer request, int dstOffset, byte[] dst) throws IOException {
+        int reqId = send(cmd, request);
+        Buffer response = receive(reqId);
+        return checkData(response, dstOffset, dst);
     }
 
     protected int checkData(Buffer buffer, int dstoff, byte[] dst) throws IOException {
         int length = buffer.getInt();
         int type = buffer.getUByte();
         int id = buffer.getInt();
+        if (type == SSH_FXP_DATA) {
+            int len = buffer.getInt();
+            buffer.getRawBytes(dst, dstoff, len);
+            return len;
+        }
+
         if (type == SSH_FXP_STATUS) {
             int substatus = buffer.getInt();
             String msg = buffer.getString();
             String lang = buffer.getString();
             if (log.isTraceEnabled()) {
-                log.trace("checkData(id={}) - status: {} [{}] {}", id, substatus, lang, msg);
+                log.trace("checkData(id={}) - status: {} [{}] {}", Integer.valueOf(id), Integer.valueOf(substatus), lang, msg);
             }
 
             if (substatus == SSH_FX_EOF) {
                 return -1;
             }
 
-            throw new SftpException(substatus, msg);
-        } else if (type == SSH_FXP_DATA) {
-            int len = buffer.getInt();
-            buffer.getRawBytes(dst, dstoff, len);
-            return len;
-        } else {
-            throw new SshException("Unexpected SFTP packet received: type=" + type + ", id=" + id + ", length=" + length);
+            throwStatusException(id, substatus, msg, lang);
         }
+
+        throw new SshException("Unexpected SFTP packet received: type=" + type + ", id=" + id + ", length=" + length);
     }
 
     @Override
@@ -728,7 +826,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
         buffer.putBytes(id);
         buffer.putLong(fileOffset);
         buffer.putBytes(src, srcOffset, len);
-        checkStatus(receive(send(SSH_FXP_WRITE, buffer)));
+        checkStatus(SSH_FXP_WRITE, buffer);
     }
 
     @Override
@@ -746,7 +844,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
             buffer.putByte((byte) 0);
         }
 
-        checkStatus(receive(send(SSH_FXP_MKDIR, buffer)));
+        checkStatus(SSH_FXP_MKDIR, buffer);
     }
 
     @Override
@@ -757,7 +855,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
 
         Buffer buffer = new ByteArrayBuffer(path.length() +  Long.SIZE /* some extra fields */);
         buffer.putString(path);
-        checkStatus(receive(send(SSH_FXP_RMDIR, buffer)));
+        checkStatus(SSH_FXP_RMDIR, buffer);
     }
 
     @Override
@@ -768,7 +866,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
 
         Buffer buffer = new ByteArrayBuffer(path.length() + Long.SIZE /* some extra fields */);
         buffer.putString(path);
-        return new DefaultCloseableHandle(this, checkHandle(receive(send(SSH_FXP_OPENDIR, buffer))));
+        return new DefaultCloseableHandle(this, checkHandle(SSH_FXP_OPENDIR, buffer));
     }
 
     @Override
@@ -787,18 +885,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
         int length = buffer.getInt();
         int type = buffer.getUByte();
         int id = buffer.getInt();
-        if (type == SSH_FXP_STATUS) {
-            int substatus = buffer.getInt();
-            String msg = buffer.getString();
-            String lang = buffer.getString();
-            if (log.isTraceEnabled()) {
-                log.trace("checkDir(id={}) - status: {} [{}] {}", id, substatus, lang, msg);
-            }
-            if (substatus == SSH_FX_EOF) {
-                return null;
-            }
-            throw new SftpException(substatus, msg);
-        } else if (type == SSH_FXP_NAME) {
+        if (type == SSH_FXP_NAME) {
             int len = buffer.getInt();
             List<DirEntry> entries = new ArrayList<DirEntry>(len);
             for (int i = 0; i < len; i++) {
@@ -807,15 +894,30 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
                 String longName = (version == SFTP_V3) ? buffer.getString() : null;
                 Attributes attrs = readAttributes(buffer);
                 if (log.isTraceEnabled()) {
-                    log.trace("checkDir(id={})[{}] ({})[{}]: {}", id, i, name, longName, attrs);
+                    log.trace("checkDir(id={})[{}] ({})[{}]: {}", Integer.valueOf(id), Integer.valueOf(i), name, longName, attrs);
                 }
-
+    
                 entries.add(new DirEntry(name, longName, attrs));
             }
             return entries;
-        } else {
-            throw new SshException("Unexpected SFTP packet received: type=" + type + ", id=" + id + ", length=" + length);
         }
+
+        if (type == SSH_FXP_STATUS) {
+            int substatus = buffer.getInt();
+            String msg = buffer.getString();
+            String lang = buffer.getString();
+            if (log.isTraceEnabled()) {
+                log.trace("checkDir(id={}) - status: {} [{}] {}", Integer.valueOf(id), Integer.valueOf(substatus), lang, msg);
+            }
+
+            if (substatus == SSH_FX_EOF) {
+                return null;
+            }
+
+            throwStatusException(id, substatus, msg, lang);
+        }
+
+        throw new SshException("Unexpected SFTP packet received: type=" + type + ", id=" + id + ", length=" + length);
     }
 
     @Override
@@ -826,7 +928,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
 
         Buffer buffer = new ByteArrayBuffer();
         buffer.putString(path);
-        return checkOneName(receive(send(SSH_FXP_REALPATH, buffer)));
+        return checkOneName(SSH_FXP_REALPATH, buffer);
     }
 
     @Override
@@ -843,7 +945,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
             buffer.putInt(SSH_FILEXFER_ATTR_ALL);
         }
 
-        return checkAttributes(receive(send(SSH_FXP_STAT, buffer)));
+        return checkAttributes(SSH_FXP_STAT, buffer);
     }
 
     @Override
@@ -860,7 +962,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
             buffer.putInt(SSH_FILEXFER_ATTR_ALL);
         }
 
-        return checkAttributes(receive(send(SSH_FXP_LSTAT, buffer)));
+        return checkAttributes(SSH_FXP_LSTAT, buffer);
     }
 
     @Override
@@ -878,7 +980,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
             buffer.putInt(SSH_FILEXFER_ATTR_ALL);
         }
 
-        return checkAttributes(receive(send(SSH_FXP_FSTAT, buffer)));
+        return checkAttributes(SSH_FXP_FSTAT, buffer);
     }
 
     @Override
@@ -890,7 +992,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
         Buffer buffer = new ByteArrayBuffer();
         buffer.putString(path);
         writeAttributes(buffer, attributes);
-        checkStatus(receive(send(SSH_FXP_SETSTAT, buffer)));
+        checkStatus(SSH_FXP_SETSTAT, buffer);
     }
 
     @Override
@@ -903,7 +1005,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
         Buffer buffer = new ByteArrayBuffer(id.length + (2 * Long.SIZE) /* some extras */);
         buffer.putBytes(id);
         writeAttributes(buffer, attributes);
-        checkStatus(receive(send(SSH_FXP_FSETSTAT, buffer)));
+        checkStatus(SSH_FXP_FSETSTAT, buffer);
     }
 
     @Override
@@ -914,7 +1016,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
 
         Buffer buffer = new ByteArrayBuffer(path.length() + Long.SIZE /* some extra fields */);
         buffer.putString(path);
-        return checkOneName(receive(send(SSH_FXP_READLINK, buffer)));
+        return checkOneName(SSH_FXP_READLINK, buffer);
     }
 
     @Override
@@ -931,12 +1033,12 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
             }
             buffer.putString(targetPath);
             buffer.putString(linkPath);
-            checkStatus(receive(send(SSH_FXP_SYMLINK, buffer)));
+            checkStatus(SSH_FXP_SYMLINK, buffer);
         } else {
             buffer.putString(targetPath);
             buffer.putString(linkPath);
             buffer.putBoolean(symbolic);
-            checkStatus(receive(send(SSH_FXP_LINK, buffer)));
+            checkStatus(SSH_FXP_LINK, buffer);
         }
     }
 
@@ -952,7 +1054,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
         buffer.putLong(offset);
         buffer.putLong(length);
         buffer.putInt(mask);
-        checkStatus(receive(send(SSH_FXP_BLOCK, buffer)));
+        checkStatus(SSH_FXP_BLOCK, buffer);
     }
 
     @Override
@@ -966,7 +1068,7 @@ public abstract class AbstractSftpClient extends AbstractLoggingBean implements
         buffer.putBytes(id);
         buffer.putLong(offset);
         buffer.putLong(length);
-        checkStatus(receive(send(SSH_FXP_UNBLOCK, buffer)));
+        checkStatus(SSH_FXP_UNBLOCK, buffer);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0d86de53/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/DefaultSftpClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/DefaultSftpClient.java b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/DefaultSftpClient.java
index 12830d5..dd313db 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/DefaultSftpClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/DefaultSftpClient.java
@@ -39,7 +39,6 @@ import java.util.Set;
 import java.util.TreeMap;
 import java.util.concurrent.atomic.AtomicInteger;
 
-import org.apache.sshd.client.SftpException;
 import org.apache.sshd.client.channel.ChannelSubsystem;
 import org.apache.sshd.client.session.ClientSession;
 import org.apache.sshd.common.FactoryManagerUtils;
@@ -48,6 +47,7 @@ import org.apache.sshd.common.subsystem.sftp.SftpConstants;
 import org.apache.sshd.common.subsystem.sftp.extensions.ParserUtils;
 import org.apache.sshd.common.subsystem.sftp.extensions.VersionsParser.Versions;
 import org.apache.sshd.common.util.GenericUtils;
+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;
@@ -58,19 +58,18 @@ import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
 public class DefaultSftpClient extends AbstractSftpClient {
     private final ClientSession clientSession;
     private final ChannelSubsystem channel;
-    private final Map<Integer, Buffer> messages;
+    private final Map<Integer, Buffer> messages = new HashMap<>();
     private final AtomicInteger cmdId = new AtomicInteger(100);
     private final Buffer receiveBuffer = new ByteArrayBuffer();
     private final byte[] workBuf = new byte[Integer.SIZE / Byte.SIZE];  // TODO in JDK-8 use Integer.BYTES
     private boolean closing;
     private int version;
-    private final Map<String,byte[]> extensions = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+    private final Map<String,byte[]> extensions = new TreeMap<String,byte[]>(String.CASE_INSENSITIVE_ORDER);
     private final Map<String,byte[]> exposedExtensions = Collections.unmodifiableMap(extensions);
 
     public DefaultSftpClient(ClientSession clientSession) throws IOException {
-        this.clientSession = clientSession;
+        this.clientSession = ValidateUtils.checkNotNull(clientSession, "No client session", GenericUtils.EMPTY_OBJECT_ARRAY);
         this.channel = clientSession.createSubsystemChannel(SftpConstants.SFTP_SUBSYSTEM_NAME);
-        this.messages = new HashMap<>();
         this.channel.setOut(new OutputStream() {
             @Override
             public void write(int b) throws IOException {
@@ -190,38 +189,44 @@ public class DefaultSftpClient extends AbstractSftpClient {
         int id = buffer.getInt();
         buffer.rpos(0);
         synchronized (messages) {
-            messages.put(id, buffer);
+            messages.put(Integer.valueOf(id), buffer);
             messages.notifyAll();
         }
     }
 
     @Override
     public int send(int cmd, Buffer buffer) throws IOException {
-        int id = cmdId.incrementAndGet();
+        int id = cmdId.incrementAndGet(), len = buffer.available();
+        if (log.isTraceEnabled()) {
+            log.trace("send(cmd={}, len={}) id = {}",
+                      Integer.valueOf(cmd), Integer.valueOf(len), Integer.valueOf(id));
+        }
+
         OutputStream dos = channel.getInvertedIn();
         BufferUtils.writeInt(dos, 1 /* cmd */ + (Integer.SIZE / Byte.SIZE) /* id */ + buffer.available(), workBuf);
         dos.write(cmd & 0xFF);
         BufferUtils.writeInt(dos, id, workBuf);
-        dos.write(buffer.array(), buffer.rpos(), buffer.available());
+        dos.write(buffer.array(), buffer.rpos(), len);
         dos.flush();
         return id;
     }
 
     @Override
     public Buffer receive(int id) throws IOException {
+        Integer reqId = Integer.valueOf(id);
         synchronized (messages) {
-            while (true) {
+            for (int count=1; ; count++) {
                 if (closing) {
                     throw new SshException("Channel has been closed");
                 }
-                Buffer buffer = messages.remove(id);
+                Buffer buffer = messages.remove(reqId);
                 if (buffer != null) {
                     return buffer;
                 }
                 try {
                     messages.wait();
                 } catch (InterruptedException e) {
-                    throw (IOException) new InterruptedIOException("Interrupted while waiting for messages").initCause(e);
+                    throw (IOException) new InterruptedIOException("Interrupted while waiting for messages at iteration #" + count).initCause(e);
                 }
             }
         }
@@ -292,10 +297,10 @@ public class DefaultSftpClient extends AbstractSftpClient {
             String msg = buffer.getString();
             String lang = buffer.getString();
             if (log.isTraceEnabled()) {
-                log.trace("init(id={}) - status: {} [{}] {}", id, substatus, lang, msg);
+                log.trace("init(id={}) - status: {} [{}] {}", Integer.valueOf(id), Integer.valueOf(substatus), lang, msg);
             }
 
-            throw new SftpException(substatus, msg);
+            throwStatusException(id, substatus, msg, lang);
         } else {
             throw new SshException("Unexpected SFTP packet received: type=" + type + ", id=" + id + ", length=" + length);
         }
@@ -308,22 +313,24 @@ public class DefaultSftpClient extends AbstractSftpClient {
      */
     public int negotiateVersion(SftpVersionSelector selector) throws IOException {
         int current = getVersion();
-        Set<Integer> available = GenericUtils.asSortedSet(Collections.singleton(current));
+        Set<Integer> available = GenericUtils.asSortedSet(Collections.singleton(Integer.valueOf(current)));
         Map<String,?> parsed = getParsedServerExtensions();
         Collection<String> extensions = ParserUtils.supportedExtensions(parsed);
-        if (!GenericUtils.isEmpty(extensions) && extensions.contains(SftpConstants.EXT_VERSELECT)) {
+        if ((GenericUtils.size(extensions) > 0) && extensions.contains(SftpConstants.EXT_VERSELECT)) {
             Versions vers = GenericUtils.isEmpty(parsed) ? null : (Versions) parsed.get(SftpConstants.EXT_VERSIONS);
             Collection<String> reported = (vers == null) ? null : vers.versions;
             if (GenericUtils.size(reported) > 0) {
                 for (String v : reported) {
-                    available.add(Integer.valueOf(v));
+                    if (!available.add(Integer.valueOf(v))) {
+                        continue;
+                    }
                 }
             }
         }
 
-        int selected = selector.selectVersion(current, new ArrayList<>(available));
+        int selected = selector.selectVersion(current, new ArrayList<Integer>(available));
         if (log.isDebugEnabled()) {
-            log.debug("negotiateVersion({}) {} -> {}", current, available, selected);
+            log.debug("negotiateVersion({}) {} -> {}", Integer.valueOf(current), available, Integer.valueOf(selected));
         }
 
         if (selected == current) {
@@ -339,7 +346,7 @@ public class DefaultSftpClient extends AbstractSftpClient {
                                          + (Integer.SIZE / Byte.SIZE) + verVal.length());
         buffer.putString(SftpConstants.EXT_VERSELECT);
         buffer.putString(verVal);
-        checkStatus(receive(send(SftpConstants.SSH_FXP_EXTENDED, buffer)));
+        checkStatus(SftpConstants.SSH_FXP_EXTENDED, buffer);
         version = selected;
         return selected;
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0d86de53/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpException.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpException.java b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpException.java
new file mode 100644
index 0000000..3228269
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpException.java
@@ -0,0 +1,44 @@
+/*
+ * 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.IOException;
+
+/**
+ * @author <a href="http://mina.apache.org">Apache MINA Project</a>
+ */
+public class SftpException extends IOException {
+    private static final long serialVersionUID = 8096963562429466995L;
+    private final int status;
+
+    public SftpException(int status, String msg) {
+        super(msg);
+        this.status = status;
+    }
+
+    public int getStatus() {
+        return status;
+    }
+
+    @Override
+    public String toString() {
+        String message = getMessage();
+        return "SFTP error (" + getStatus() + "): " + message;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0d86de53/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileChannel.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileChannel.java b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileChannel.java
index 53dd6ca..19ded8c 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileChannel.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileChannel.java
@@ -41,7 +41,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.atomic.AtomicReference;
 
-import org.apache.sshd.client.SftpException;
 import org.apache.sshd.common.FactoryManagerUtils;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0d86de53/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 986f015..b0ecafc 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
@@ -66,7 +66,6 @@ import java.util.NoSuchElementException;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
-import org.apache.sshd.client.SftpException;
 import org.apache.sshd.client.SshClient;
 import org.apache.sshd.client.session.ClientSession;
 import org.apache.sshd.client.subsystem.sftp.SftpClient.Attributes;
@@ -319,7 +318,7 @@ public class SftpFileSystemProvider extends FileSystemProvider {
             public Iterator<Path> iterator() {
                 return new Iterator<Path>() {
                     @SuppressWarnings("synthetic-access")
-                    private final Iterator<SftpClient.DirEntry> it = iter.iterator();
+                    private final Iterator<SftpClient.DirEntry> it = (iter == null) ? null : iter.iterator();
                     private boolean dotIgnored, dotdotIgnored;
                     private SftpClient.DirEntry curEntry = nextEntry();
 
@@ -340,7 +339,7 @@ public class SftpFileSystemProvider extends FileSystemProvider {
                     }
 
                     private SftpClient.DirEntry nextEntry() {
-                        while(it.hasNext()) {
+                        while((it != null) && it.hasNext()) {
                             SftpClient.DirEntry entry = it.next();
                             String name = entry.filename;
                             if (".".equals(name) && (!dotIgnored)) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0d86de53/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/impl/AbstractSftpClientExtension.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/impl/AbstractSftpClientExtension.java b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/impl/AbstractSftpClientExtension.java
index ccd55de..11b23f1 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/impl/AbstractSftpClientExtension.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/impl/AbstractSftpClientExtension.java
@@ -24,9 +24,9 @@ import java.io.StreamCorruptedException;
 import java.util.Collection;
 import java.util.Map;
 
-import org.apache.sshd.client.SftpException;
 import org.apache.sshd.client.subsystem.sftp.RawSftpClient;
 import org.apache.sshd.client.subsystem.sftp.SftpClient;
+import org.apache.sshd.client.subsystem.sftp.SftpException;
 import org.apache.sshd.client.subsystem.sftp.SftpClient.Handle;
 import org.apache.sshd.client.subsystem.sftp.extensions.SftpClientExtension;
 import org.apache.sshd.common.SshException;
@@ -188,7 +188,7 @@ public abstract class AbstractSftpClientExtension extends AbstractLoggingBean im
             }
 
             if (substatus != SftpConstants.SSH_FX_OK) {
-                throw new SftpException(substatus, msg);
+                throwStatusException(id, substatus, msg, lang);
             }
             
             return null;
@@ -198,4 +198,8 @@ public abstract class AbstractSftpClientExtension extends AbstractLoggingBean im
             throw new SshException("Unexpected SFTP packet received: type=" + type + ", id=" + id + ", length=" + length);
         }
     }
+    
+    protected void throwStatusException(int id, int substatus, String msg, String lang) throws IOException {
+        throw new SftpException(substatus, msg);
+    }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0d86de53/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java b/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java
index 783cd17..2ec3f3f 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java
@@ -39,6 +39,7 @@ import org.apache.sshd.common.session.ConnectionService;
 import org.apache.sshd.common.session.Session;
 import org.apache.sshd.common.util.CloseableUtils;
 import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.Int2IntFunction;
 import org.apache.sshd.common.util.buffer.Buffer;
 import org.apache.sshd.common.util.buffer.BufferUtils;
 import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
@@ -58,7 +59,7 @@ public abstract class AbstractChannel
 
     public static final long DEFAULT_CHANNEL_CLOSE_TIMEOUT = 5000;
 
-    protected enum GracefulState {
+    protected static enum GracefulState {
         Opened, CloseSent, CloseReceived, Closed
     }
 
@@ -71,9 +72,9 @@ public abstract class AbstractChannel
     protected int id;
     protected int recipient;
     private final AtomicBoolean eof = new AtomicBoolean(false);
-    protected AtomicReference<GracefulState> gracefulState = new AtomicReference<>(GracefulState.Opened);
+    protected AtomicReference<GracefulState> gracefulState = new AtomicReference<GracefulState>(GracefulState.Opened);
     protected final DefaultCloseFuture gracefulFuture = new DefaultCloseFuture(lock);
-    protected final List<RequestHandler<Channel>> handlers = new ArrayList<>();
+    protected final List<RequestHandler<Channel>> handlers = new ArrayList<RequestHandler<Channel>>();
 
     protected AbstractChannel() {
         super();
@@ -137,8 +138,10 @@ public abstract class AbstractChannel
         String req = buffer.getString();
         boolean wantReply = buffer.getBoolean();
         if (log.isDebugEnabled()) {
-            log.debug("Received SSH_MSG_CHANNEL_REQUEST {} on channel {} (wantReply {})", req, this, wantReply);
+            log.debug("Received SSH_MSG_CHANNEL_REQUEST {} on channel {} (wantReply {})",
+                      req, this, Boolean.valueOf(wantReply));
         }
+
         for (RequestHandler<Channel> handler : handlers) {
             RequestHandler.Result result;
             try {
@@ -147,36 +150,44 @@ public abstract class AbstractChannel
                 log.warn("Error processing channel request " + req, e);
                 result = RequestHandler.Result.ReplyFailure;
             }
-            switch (result) {
-                case Replied:
-                    return;
-                case ReplySuccess:
-                    if (wantReply) {
-                        buffer = session.createBuffer(SshConstants.SSH_MSG_CHANNEL_SUCCESS);
-                        buffer.putInt(recipient);
-                        session.writePacket(buffer);
-                    }
-                    return;
-                case ReplyFailure:
-                    if (wantReply) {
-                        buffer = session.createBuffer(SshConstants.SSH_MSG_CHANNEL_FAILURE);
-                        buffer.putInt(recipient);
-                        session.writePacket(buffer);
-                    }
-                    return;
-                default:
-                    if (log.isTraceEnabled()) {
-                        log.trace("{}#process({}): {}", handler.getClass().getSimpleName(), req, result);
-                    }
+            
+            // if Unsupported then check the next handler in line
+            if (RequestHandler.Result.Unsupported.equals(result)) {
+                if (log.isTraceEnabled()) {
+                    log.trace("{}#process({}): {}", handler.getClass().getSimpleName(), req, result);
+                }
+            } else {
+                sendResponse(buffer, req, result, wantReply);
+                return;
             }
         }
 
+        // none of the handlers processed the request
         log.warn("Unknown channel request: {}", req);
-        if (wantReply) {
-            buffer = session.createBuffer(SshConstants.SSH_MSG_CHANNEL_FAILURE);
-            buffer.putInt(recipient);
-            session.writePacket(buffer);
+        sendResponse(buffer, req, RequestHandler.Result.Unsupported, wantReply);
+    }
+
+    protected void sendResponse(Buffer buffer, String req, RequestHandler.Result result, boolean wantReply) throws IOException {
+        if (log.isDebugEnabled()) {
+            log.debug("sendResponse({}) result={}, want-reply={}", req, result, Boolean.valueOf(wantReply));
         }
+
+        if (RequestHandler.Result.Replied.equals(result) || (!wantReply)) {
+            return;
+        }
+
+        byte cmd = RequestHandler.Result.ReplySuccess.equals(result)
+                 ? SshConstants.SSH_MSG_CHANNEL_SUCCESS
+                 : SshConstants.SSH_MSG_CHANNEL_FAILURE
+                 ;
+        buffer.clear();
+        // leave room for the SSH header
+        buffer.ensureCapacity(5 + 1 + (Integer.SIZE / Byte.SIZE), Int2IntFunction.Utils.add(Byte.SIZE));
+        buffer.rpos(5);
+        buffer.wpos(5);
+        buffer.putByte(cmd);
+        buffer.putInt(recipient);
+        session.writePacket(buffer);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0d86de53/sshd-core/src/main/java/org/apache/sshd/common/channel/RequestHandler.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/channel/RequestHandler.java b/sshd-core/src/main/java/org/apache/sshd/common/channel/RequestHandler.java
index 8c262ef..4f653f1 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/channel/RequestHandler.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/channel/RequestHandler.java
@@ -18,6 +18,11 @@
  */
 package org.apache.sshd.common.channel;
 
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Set;
+
+import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.buffer.Buffer;
 
 /**
@@ -31,20 +36,42 @@ public interface RequestHandler<T> {
         Unsupported,
         Replied,
         ReplySuccess,
-        ReplyFailure
+        ReplyFailure;
+        
+        public static final Set<Result> VALUES =
+                Collections.unmodifiableSet(EnumSet.allOf(Result.class));
+
+        /**
+         * @param name The result name - ignored if {@code null}/empty
+         * @return The matching {@link Result} value (case <U>insensitive</U>)
+         * or {@code null} if no match found
+         */
+        public static Result fromName(String name) {
+            if (GenericUtils.isEmpty(name)) {
+                return null;
+            }
+            
+            for (Result r : VALUES) {
+                if (name.equalsIgnoreCase(r.name())) {
+                    return r;
+                }
+            }
+            
+            return null;
+        }
     }
 
     /**
-     * Process the ssh-connection global request.
-     * If an exception is thrown, the ConnectionService will send a failure message if needed
-     * and the request will be considered handled.
-     *
-     * @param t
-     * @param request
-     * @param wantReply
-     * @param buffer
-     * @return
-     * @throws Exception
+     * Process an SSH request. If an exception is thrown, the ConnectionService
+     * will send a failure message if needed and the request will be considered handled.
+     * @param t The input parameter
+     * @param request The request string
+     * @param wantReply Whether a reply is requested
+     * @param buffer The {@link Buffer} with request specific data
+     * @return The {@link Result}
+     * @throws Exception If failed to handle the request - <B>Note:</B> in
+     * order to signal an unsupported request the {@link Result#Unsupported}
+     * value should be returned
      */
     Result process(T t, String request, boolean wantReply, Buffer buffer) throws Exception;
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0d86de53/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultTcpipForwarder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultTcpipForwarder.java b/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultTcpipForwarder.java
index 8af95ad..aad59eb 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultTcpipForwarder.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultTcpipForwarder.java
@@ -19,6 +19,7 @@
 package org.apache.sshd.common.forward;
 
 import java.io.IOException;
+import java.io.OutputStream;
 import java.net.InetSocketAddress;
 import java.net.SocketAddress;
 import java.util.HashMap;
@@ -412,8 +413,10 @@ public class DefaultTcpipForwarder extends CloseableUtils.AbstractInnerCloseable
             Buffer buffer = new ByteArrayBuffer();
             buffer.putBuffer(message);
             channel.waitFor(ClientChannel.OPENED | ClientChannel.CLOSED, Long.MAX_VALUE);
-            channel.getInvertedIn().write(buffer.array(), buffer.rpos(), buffer.available());
-            channel.getInvertedIn().flush();
+            
+            OutputStream outputStream = channel.getInvertedIn();
+            outputStream.write(buffer.array(), buffer.rpos(), buffer.available());
+            outputStream.flush();
         }
 
         @Override

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0d86de53/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractConnectionService.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractConnectionService.java b/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractConnectionService.java
index 3d43fa2..47c6cc4 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractConnectionService.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractConnectionService.java
@@ -55,6 +55,7 @@ import org.apache.sshd.common.forward.TcpipForwarderFactory;
 import org.apache.sshd.common.future.SshFutureListener;
 import org.apache.sshd.common.util.CloseableUtils;
 import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.Int2IntFunction;
 import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.buffer.Buffer;
 import org.apache.sshd.server.channel.OpenChannelException;
@@ -419,9 +420,12 @@ public abstract class AbstractConnectionService extends CloseableUtils.AbstractI
     protected void globalRequest(Buffer buffer) throws Exception {
         String req = buffer.getString();
         boolean wantReply = buffer.getBoolean();
-        log.debug("Received SSH_MSG_GLOBAL_REQUEST {}", req);
+        if (log.isDebugEnabled()) {
+            log.debug("Received SSH_MSG_GLOBAL_REQUEST {} want-reply={}", req, Boolean.valueOf(wantReply));
+        }
+
         List<RequestHandler<ConnectionService>> handlers = session.getFactoryManager().getGlobalRequestHandlers();
-        if (handlers != null) {
+        if (GenericUtils.size(handlers) > 0) {
             for (RequestHandler<ConnectionService> handler : handlers) {
                 RequestHandler.Result result;
                 try {
@@ -430,33 +434,43 @@ public abstract class AbstractConnectionService extends CloseableUtils.AbstractI
                     log.warn("Error processing global request " + req, e);
                     result = RequestHandler.Result.ReplyFailure;
                 }
-                switch (result) {
-                    case Replied:
-                        return;
-                    case ReplySuccess:
-                        if (wantReply) {
-                            buffer = session.createBuffer(SshConstants.SSH_MSG_REQUEST_SUCCESS);
-                            session.writePacket(buffer);
-                        }
-                        return;
-                    case ReplyFailure:
-                        if (wantReply) {
-                            buffer = session.createBuffer(SshConstants.SSH_MSG_REQUEST_FAILURE);
-                            session.writePacket(buffer);
-                        }
-                        return;
-                    default:
-                        if (log.isTraceEnabled()) {
-                            log.trace("globalRequest({}) {}#process: {}", req, handler.getClass().getSimpleName(), result);
-                        }
+
+                // if Unsupported then check the next handler in line
+                if (RequestHandler.Result.Unsupported.equals(result)) {
+                    if (log.isTraceEnabled()) {
+                        log.trace("{}#process({}): {}", handler.getClass().getSimpleName(), req, result);
+                    }
+                } else {
+                    sendResponse(buffer, req, result, wantReply);
+                    return;
                 }
             }
         }
+
         log.warn("Unknown global request: {}", req);
-        if (wantReply) {
-            buffer = session.createBuffer(SshConstants.SSH_MSG_REQUEST_FAILURE);
-            session.writePacket(buffer);
+        sendResponse(buffer, req, RequestHandler.Result.Unsupported, wantReply);
+    }
+
+    protected void sendResponse(Buffer buffer, String req, RequestHandler.Result result, boolean wantReply) throws IOException {
+        if (log.isDebugEnabled()) {
+            log.debug("sendResponse({}) result={}, want-reply={}", req, result, Boolean.valueOf(wantReply));
         }
+
+        if (RequestHandler.Result.Replied.equals(result) || (!wantReply)) {
+            return;
+        }
+
+        byte cmd = RequestHandler.Result.ReplySuccess.equals(result)
+                 ? SshConstants.SSH_MSG_CHANNEL_SUCCESS
+                 : SshConstants.SSH_MSG_CHANNEL_FAILURE
+                 ;
+        buffer.clear();
+        // leave room for the SSH header
+        buffer.ensureCapacity(5 + 1 + (Integer.SIZE / Byte.SIZE), Int2IntFunction.Utils.add(Byte.SIZE));
+        buffer.rpos(5);
+        buffer.wpos(5);
+        buffer.putByte(cmd);
+        session.writePacket(buffer);
     }
 
     protected void requestSuccess(Buffer buffer) throws Exception {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0d86de53/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java b/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
index 535301c..aabc4cb 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
@@ -37,6 +37,7 @@ import java.util.Map;
 import java.util.Queue;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
@@ -181,7 +182,7 @@ public abstract class AbstractSession extends CloseableUtils.AbstractInnerClosea
      */
     public AbstractSession(boolean isServer, FactoryManager factoryManager, IoSession ioSession) {
         this.isServer = isServer;
-        this.factoryManager = factoryManager;
+        this.factoryManager = ValidateUtils.checkNotNull(factoryManager, "No factory manager provided", GenericUtils.EMPTY_OBJECT_ARRAY);
         this.ioSession = ioSession;
         sessionListenerProxy = EventListenerUtils.proxyWrapper(SessionListener.class, getClass().getClassLoader(), listeners);
         random = factoryManager.getRandomFactory().create();
@@ -552,23 +553,25 @@ public abstract class AbstractSession extends CloseableUtils.AbstractInnerClosea
 
     @SuppressWarnings("unchecked")
     @Override
-    public IoWriteFuture writePacket(Buffer buffer, long timeout, TimeUnit unit) throws IOException {
+    public IoWriteFuture writePacket(Buffer buffer, final long timeout, final TimeUnit unit) throws IOException {
         final IoWriteFuture writeFuture = writePacket(buffer);
         final DefaultSshFuture<IoWriteFuture> future = (DefaultSshFuture<IoWriteFuture>) writeFuture;
-        final ScheduledFuture<?> sched = factoryManager.getScheduledExecutorService().schedule(new Runnable() {
-            @SuppressWarnings("synthetic-access")
-            @Override
-            public void run() {
-                log.info("Timeout writing packet.");
-                future.setValue(new TimeoutException());
-            }
-        }, timeout, unit);
+        ScheduledExecutorService executor = factoryManager.getScheduledExecutorService(); 
+        final ScheduledFuture<?> sched = executor.schedule(new Runnable() {
+                @SuppressWarnings("synthetic-access")
+                @Override
+                public void run() {
+                    Throwable t = new TimeoutException("Timeout writing packet: " + timeout + " " + unit);
+                    log.info(t.getMessage());
+                    future.setValue(t);
+                }
+            }, timeout, unit);
         future.addListener(new SshFutureListener<IoWriteFuture>() {
-            @Override
-            public void operationComplete(IoWriteFuture future) {
-                sched.cancel(false);
-            }
-        });
+                @Override
+                public void operationComplete(IoWriteFuture future) {
+                    sched.cancel(false);
+                }
+            });
         return writeFuture;
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0d86de53/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/AbstractOpenSSHExtensionParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/AbstractOpenSSHExtensionParser.java b/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/AbstractOpenSSHExtensionParser.java
index c6905f5..d408e3b 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/AbstractOpenSSHExtensionParser.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/openssh/AbstractOpenSSHExtensionParser.java
@@ -90,7 +90,7 @@ public abstract class AbstractOpenSSHExtensionParser extends AbstractParser<Open
             try {
                 return getClass().cast(super.clone());
             } catch(CloneNotSupportedException e) {
-                throw new RuntimeException("Unexpected clone exception: " + e.getLocalizedMessage());
+                throw new RuntimeException("Unexpected clone exception " + toString() + ": " + e.getMessage());
             }
         }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0d86de53/sshd-core/src/main/java/org/apache/sshd/common/util/io/FileInfoExtractor.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/FileInfoExtractor.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/FileInfoExtractor.java
new file mode 100644
index 0000000..56be141
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/io/FileInfoExtractor.java
@@ -0,0 +1,85 @@
+/*
+ * 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.util.io;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.nio.file.attribute.FileTime;
+import java.nio.file.attribute.PosixFilePermission;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface FileInfoExtractor<T> {
+    T infoOf(Path file, LinkOption ... options) throws IOException;
+    
+    FileInfoExtractor<Boolean> EXISTS = new FileInfoExtractor<Boolean>() {
+            @Override
+            public Boolean infoOf(Path file, LinkOption... options) throws IOException {
+                return Boolean.valueOf(Files.exists(file, options));
+            }
+        };
+   
+    FileInfoExtractor<Boolean> ISDIR = new FileInfoExtractor<Boolean>() {
+            @Override
+            public Boolean infoOf(Path file, LinkOption... options) throws IOException {
+                return Boolean.valueOf(Files.isDirectory(file, options));
+            }
+        };
+
+    FileInfoExtractor<Boolean> ISREG = new FileInfoExtractor<Boolean>() {
+            @Override
+            public Boolean infoOf(Path file, LinkOption... options) throws IOException {
+                return Boolean.valueOf(Files.isRegularFile(file, options));
+            }
+        };
+
+    FileInfoExtractor<Boolean> ISSYMLINK = new FileInfoExtractor<Boolean>() {
+            @Override
+            public Boolean infoOf(Path file, LinkOption... options) throws IOException {
+                return Boolean.valueOf(Files.isSymbolicLink(file));
+            }
+        };
+
+    FileInfoExtractor<Long> SIZE = new FileInfoExtractor<Long>() {
+            @Override
+            public Long infoOf(Path file, LinkOption... options) throws IOException {
+                return Long.valueOf(Files.size(file));
+            }
+        };
+
+    FileInfoExtractor<Set<PosixFilePermission>> PERMISSIONS = new FileInfoExtractor<Set<PosixFilePermission>>() {
+            @Override
+            public Set<PosixFilePermission> infoOf(Path file, LinkOption... options) throws IOException {
+                return IoUtils.getPermissions(file, options);
+            }
+        };
+
+    FileInfoExtractor<FileTime> LASTMODIFIED = new FileInfoExtractor<FileTime>() {
+        @Override
+        public FileTime infoOf(Path file, LinkOption... options) throws IOException {
+            return Files.getLastModifiedTime(file, options);
+        }
+        
+    };
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0d86de53/sshd-core/src/main/java/org/apache/sshd/server/global/CancelTcpipForwardHandler.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/global/CancelTcpipForwardHandler.java b/sshd-core/src/main/java/org/apache/sshd/server/global/CancelTcpipForwardHandler.java
index ebc6914..557af0a 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/global/CancelTcpipForwardHandler.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/global/CancelTcpipForwardHandler.java
@@ -24,6 +24,7 @@ import org.apache.sshd.common.forward.TcpipForwarder;
 import org.apache.sshd.common.session.ConnectionService;
 import org.apache.sshd.common.session.ConnectionServiceRequestHandler;
 import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.util.Int2IntFunction;
 import org.apache.sshd.common.util.buffer.Buffer;
 import org.apache.sshd.common.util.logging.AbstractLoggingBean;
 
@@ -54,9 +55,15 @@ public class CancelTcpipForwardHandler extends AbstractLoggingBean implements Co
             forwarder.localPortForwardingCancelled(socketAddress);
 
             if (wantReply) {
-                Session session = connectionService.getSession();
-                buffer = session.createBuffer(SshConstants.SSH_MSG_REQUEST_SUCCESS);
+                buffer.clear();
+                // leave room for the SSH header
+                buffer.ensureCapacity(5 + 1 + (Integer.SIZE / Byte.SIZE), Int2IntFunction.Utils.add(Byte.SIZE));
+                buffer.rpos(5);
+                buffer.wpos(5);
+                buffer.putByte(SshConstants.SSH_MSG_REQUEST_SUCCESS);
                 buffer.putInt(port);
+
+                Session session = connectionService.getSession();
                 session.writePacket(buffer);
             }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0d86de53/sshd-core/src/main/java/org/apache/sshd/server/global/TcpipForwardHandler.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/global/TcpipForwardHandler.java b/sshd-core/src/main/java/org/apache/sshd/server/global/TcpipForwardHandler.java
index 33d64b0..3f5dc4d 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/global/TcpipForwardHandler.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/global/TcpipForwardHandler.java
@@ -24,6 +24,7 @@ import org.apache.sshd.common.forward.TcpipForwarder;
 import org.apache.sshd.common.session.ConnectionService;
 import org.apache.sshd.common.session.ConnectionServiceRequestHandler;
 import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.util.Int2IntFunction;
 import org.apache.sshd.common.util.buffer.Buffer;
 import org.apache.sshd.common.util.logging.AbstractLoggingBean;
 
@@ -54,9 +55,15 @@ public class TcpipForwardHandler extends AbstractLoggingBean implements Connecti
 
             port = bound.getPort();
             if (wantReply){
-                Session session = connectionService.getSession();
-                buffer = session.createBuffer(SshConstants.SSH_MSG_REQUEST_SUCCESS);
+                buffer.clear();
+                // leave room for the SSH header
+                buffer.ensureCapacity(5 + 1 + (Integer.SIZE / Byte.SIZE), Int2IntFunction.Utils.add(Byte.SIZE));
+                buffer.rpos(5);
+                buffer.wpos(5);
+                buffer.putByte(SshConstants.SSH_MSG_REQUEST_SUCCESS);
                 buffer.putInt(port);
+
+                Session session = connectionService.getSession();
                 session.writePacket(buffer);
             }
             return Result.Replied;