This is an automated email from the ASF dual-hosted git repository.

lgoldstein pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git


The following commit(s) were added to refs/heads/master by this push:
     new d567b95  [SSHD-967] Fixed extra bytes written at end of 
SftpRemotePathChannel#transferTo
d567b95 is described below

commit d567b9549b52f00266fa2d8d6d32544f6c253afa
Author: Lyor Goldstein <lgoldst...@apache.org>
AuthorDate: Fri Feb 21 14:00:14 2020 +0200

    [SSHD-967] Fixed extra bytes written at end of 
SftpRemotePathChannel#transferTo
---
 CHANGES.md                                         |   2 +
 .../subsystem/sftp/SftpRemotePathChannel.java      | 283 +++++++++++++++------
 .../subsystem/sftp/SftpRemotePathChannelTest.java  | 183 +++++++++++++
 .../sshd/client/subsystem/sftp/SftpTest.java       | 150 ++++++-----
 4 files changed, 464 insertions(+), 154 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index dd584ff..1a08198 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -13,3 +13,5 @@
 ## Behavioral changes and enhancements
 
 * [SSHD-964](https://issues.apache.org/jira/browse/SSHD-964) - Send 
SSH_MSG_CHANNEL_EOF when tunnel channel being closed.
+
+* [SSHD-967](https://issues.apache.org/jira/browse/SSHD-967) - Extra bytes 
written when `SftpRemotePathChannel#transferTo` is used.
\ No newline at end of file
diff --git 
a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpRemotePathChannel.java
 
b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpRemotePathChannel.java
index fc81e17..52e3149 100644
--- 
a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpRemotePathChannel.java
+++ 
b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpRemotePathChannel.java
@@ -20,6 +20,7 @@
 package org.apache.sshd.client.subsystem.sftp;
 
 import java.io.IOException;
+import java.io.StreamCorruptedException;
 import java.nio.ByteBuffer;
 import java.nio.MappedByteBuffer;
 import java.nio.channels.AsynchronousCloseException;
@@ -33,24 +34,29 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumSet;
-import java.util.List;
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.atomic.AtomicReference;
 
+import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.client.subsystem.sftp.SftpClient.Attributes;
 import org.apache.sshd.common.subsystem.sftp.SftpConstants;
 import org.apache.sshd.common.subsystem.sftp.SftpException;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.io.IoUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
  */
 public class SftpRemotePathChannel extends FileChannel {
+    /** Internal allocate buffer size when copying data to/from the channel */
     public static final String COPY_BUFSIZE_PROP = 
"sftp-channel-copy-buf-size";
+    /** Default value for {@value #COPY_BUFSIZE_PROP} setting */
     public static final int DEFAULT_TRANSFER_BUFFER_SIZE = 
IoUtils.DEFAULT_COPY_SIZE;
 
     public static final Set<SftpClient.OpenMode> READ_MODES =
@@ -64,16 +70,21 @@ public class SftpRemotePathChannel extends FileChannel {
                 SftpClient.OpenMode.Create,
                 SftpClient.OpenMode.Truncate));
 
+    protected final Logger log;
+    protected final Collection<SftpClient.OpenMode> modes;
+    protected final boolean closeOnExit;
+    protected final SftpClient sftp;
+    protected final SftpClient.CloseableHandle handle;
+    protected final Object lock = new Object();
+    protected final AtomicLong posTracker = new AtomicLong(0L);
+    protected final AtomicReference<Thread> blockingThreadHolder = new 
AtomicReference<>(null);
+
     private final String path;
-    private final Collection<SftpClient.OpenMode> modes;
-    private final boolean closeOnExit;
-    private final SftpClient sftp;
-    private final SftpClient.CloseableHandle handle;
-    private final Object lock = new Object();
-    private final AtomicLong posTracker = new AtomicLong(0L);
-    private final AtomicReference<Thread> blockingThreadHolder = new 
AtomicReference<>(null);
-
-    public SftpRemotePathChannel(String path, SftpClient sftp, boolean 
closeOnExit, Collection<SftpClient.OpenMode> modes) throws IOException {
+
+    public SftpRemotePathChannel(
+            String path, SftpClient sftp, boolean closeOnExit, 
Collection<SftpClient.OpenMode> modes)
+                throws IOException {
+        this.log = LoggerFactory.getLogger(getClass());
         this.path = ValidateUtils.checkNotNullAndNotEmpty(path, "No remote 
file path specified");
         this.modes = Objects.requireNonNull(modes, "No channel modes 
specified");
         this.sftp = Objects.requireNonNull(sftp, "No SFTP client instance");
@@ -87,41 +98,69 @@ public class SftpRemotePathChannel extends FileChannel {
 
     @Override
     public int read(ByteBuffer dst) throws IOException {
-        return (int) doRead(Collections.singletonList(dst), -1);
+        long totalRead = doRead(Collections.singletonList(dst), -1L);
+        if (totalRead >= Integer.MAX_VALUE) {
+            throw new StreamCorruptedException("Total read size exceeds 
integer: " + totalRead);
+        }
+        return (int) totalRead;
     }
 
     @Override
     public int read(ByteBuffer dst, long position) throws IOException {
-        if (position < 0) {
-            throw new IllegalArgumentException("read(" + getRemotePath() + ") 
illegal position to read from: " + position);
+        if (position < 0L) {
+            throw new IllegalArgumentException("read(" + getRemotePath() + ")"
+                + " illegal position to read from: " + position);
         }
-        return (int) doRead(Collections.singletonList(dst), position);
+
+        long totalRead = doRead(Collections.singletonList(dst), position);
+        if (totalRead >= Integer.MAX_VALUE) {
+            throw new StreamCorruptedException("Total read size exceeds 
integer: " + totalRead);
+        }
+        return (int) totalRead;
     }
 
     @Override
     public long read(ByteBuffer[] dsts, int offset, int length) throws 
IOException {
-        List<ByteBuffer> buffers = Arrays.asList(dsts).subList(offset, offset 
+ length);
-        return doRead(buffers, -1);
+        Collection<ByteBuffer> buffers = Arrays.asList(dsts)
+            .subList(offset, offset + length);
+        return doRead(buffers, -1L);
     }
 
-    protected long doRead(List<ByteBuffer> buffers, long position) throws 
IOException {
+    protected long doRead(Collection<? extends ByteBuffer> buffers, long 
position) throws IOException {
         ensureOpen(READ_MODES);
+
+        ClientSession clientSession = sftp.getClientSession();
+        int copySize = clientSession.getIntProperty(COPY_BUFSIZE_PROP, 
DEFAULT_TRANSFER_BUFFER_SIZE);
+        boolean debugEnabled = log.isDebugEnabled();
+        if (debugEnabled) {
+            log.debug("doRead({})[position={}] fill {} buffers using 
copySize={}",
+                this, position, buffers.size(), copySize);
+        }
+
+        boolean completed = false;
+        boolean eof = false;
+        long totalRead = 0;
+        int numBufsUsed = 0;
+
         synchronized (lock) {
-            boolean completed = false;
-            boolean eof = false;
             long curPos = (position >= 0L) ? position : posTracker.get();
             try {
-                long totalRead = 0;
-                beginBlocking();
+                beginBlocking("doRead");
+
                 loop:
                 for (ByteBuffer buffer : buffers) {
+                    numBufsUsed++;
+
                     while (buffer.remaining() > 0) {
                         ByteBuffer wrap = buffer;
                         if (!buffer.hasArray()) {
-                            wrap = 
ByteBuffer.allocate(Math.min(IoUtils.DEFAULT_COPY_SIZE, buffer.remaining()));
+                            wrap = ByteBuffer.allocate(Math.min(copySize, 
buffer.remaining()));
                         }
-                        int read = sftp.read(handle, curPos, wrap.array(), 
wrap.arrayOffset() + wrap.position(), wrap.remaining());
+
+                        int read = sftp.read(handle, curPos, wrap.array(),
+                            wrap.arrayOffset() + wrap.position(), 
wrap.remaining());
                         if (read > 0) {
+                            // reference equality on purpose
                             if (wrap == buffer) {
                                 wrap.position(wrap.position() + read);
                             } else {
@@ -136,60 +175,97 @@ public class SftpRemotePathChannel extends FileChannel {
                     }
                 }
                 completed = true;
-                if (totalRead > 0) {
-                    return totalRead;
-                }
-
-                if (eof) {
-                    return -1;
-                } else {
-                    return 0;
-                }
             } finally {
                 if (position < 0L) {
                     posTracker.set(curPos);
                 }
-                endBlocking(completed);
+                endBlocking("doRead", completed);
             }
         }
+
+        if (debugEnabled) {
+            log.debug("doRead({})[position={}] filled {}/{} with copySize={} - 
totalRead={}, completed={}, eof={}",
+                this, position, numBufsUsed, buffers.size(), copySize, 
totalRead, completed, eof);
+        }
+
+        if (totalRead > 0L) {
+            return totalRead;
+        }
+
+        if (eof) {
+            return -1L;
+        } else {
+            return 0L;
+        }
     }
 
     @Override
     public int write(ByteBuffer src) throws IOException {
-        return (int) doWrite(Collections.singletonList(src), -1);
+        long totalWritten = doWrite(Collections.singletonList(src), -1L);
+        if (totalWritten >= Integer.MAX_VALUE) {
+            throw new StreamCorruptedException("Total written size exceeds 
integer: " + totalWritten);
+        }
+
+        return (int) totalWritten;
     }
 
     @Override
     public int write(ByteBuffer src, long position) throws IOException {
         if (position < 0L) {
-            throw new IllegalArgumentException("write(" + getRemotePath() + ") 
illegal position to write to: " + position);
+            throw new IllegalArgumentException("write(" + getRemotePath() + ")"
+                + " illegal position to write to: " + position);
         }
-        return (int) doWrite(Collections.singletonList(src), position);
+
+        long totalWritten = doWrite(Collections.singletonList(src), position);
+        if (totalWritten >= Integer.MAX_VALUE) {
+            throw new StreamCorruptedException("Total written size exceeds 
integer: " + totalWritten);
+        }
+
+        return (int) totalWritten;
     }
 
     @Override
     public long write(ByteBuffer[] srcs, int offset, int length) throws 
IOException {
-        List<ByteBuffer> buffers = Arrays.asList(srcs).subList(offset, offset 
+ length);
-        return doWrite(buffers, -1);
+        Collection<ByteBuffer> buffers = Arrays.asList(srcs)
+            .subList(offset, offset + length);
+        return doWrite(buffers, -1L);
     }
 
-    protected long doWrite(List<ByteBuffer> buffers, long position) throws 
IOException {
+    protected long doWrite(Collection<? extends ByteBuffer> buffers, long 
position) throws IOException {
         ensureOpen(WRITE_MODES);
+
+        ClientSession clientSession = sftp.getClientSession();
+        int copySize = clientSession.getIntProperty(
+            COPY_BUFSIZE_PROP, DEFAULT_TRANSFER_BUFFER_SIZE);
+        boolean debugEnabled = log.isDebugEnabled();
+        if (debugEnabled) {
+            log.debug("doWrite({})[position={}] write {} buffers using 
copySize={}",
+                this, position, buffers.size(), copySize);
+        }
+
+        boolean completed = false;
+        long totalWritten = 0L;
+        int numBufsUsed = 0;
+
         synchronized (lock) {
-            boolean completed = false;
             long curPos = (position >= 0L) ? position : posTracker.get();
             try {
-                long totalWritten = 0L;
-                beginBlocking();
+                beginBlocking("doWrite");
+
                 for (ByteBuffer buffer : buffers) {
+                    numBufsUsed++;
+
                     while (buffer.remaining() > 0) {
                         ByteBuffer wrap = buffer;
                         if (!buffer.hasArray()) {
-                            wrap = 
ByteBuffer.allocate(Math.min(IoUtils.DEFAULT_COPY_SIZE, buffer.remaining()));
+                            wrap = ByteBuffer.allocate(Math.min(copySize, 
buffer.remaining()));
                             buffer.get(wrap.array(), wrap.arrayOffset(), 
wrap.remaining());
                         }
+
                         int written = wrap.remaining();
-                        sftp.write(handle, curPos, wrap.array(), 
wrap.arrayOffset() + wrap.position(), written);
+                        sftp.write(handle, curPos, wrap.array(),
+                            wrap.arrayOffset() + wrap.position(), written);
+                        // reference equality on purpose
                         if (wrap == buffer) {
                             wrap.position(wrap.position() + written);
                         }
@@ -198,14 +274,20 @@ public class SftpRemotePathChannel extends FileChannel {
                     }
                 }
                 completed = true;
-                return totalWritten;
             } finally {
                 if (position < 0L) {
                     posTracker.set(curPos);
                 }
-                endBlocking(completed);
+                endBlocking("doWrite", completed);
             }
         }
+
+        if (debugEnabled) {
+            log.debug("doWrite({})[position={}] used {}/{} with copySize={} - 
totalWritten={}, completed={}",
+                this, position, numBufsUsed, buffers.size(), copySize, 
totalWritten, completed);
+        }
+
+        return totalWritten;
     }
 
     @Override
@@ -217,7 +299,8 @@ public class SftpRemotePathChannel extends FileChannel {
     @Override
     public FileChannel position(long newPosition) throws IOException {
         if (newPosition < 0L) {
-            throw new IllegalArgumentException("position(" + getRemotePath() + 
") illegal file channel position: " + newPosition);
+            throw new IllegalArgumentException("position(" + getRemotePath() + 
")"
+                + " illegal file channel position: " + newPosition);
         }
 
         ensureOpen(Collections.emptySet());
@@ -228,7 +311,8 @@ public class SftpRemotePathChannel extends FileChannel {
     @Override
     public long size() throws IOException {
         ensureOpen(Collections.emptySet());
-        return sftp.stat(handle).getSize();
+        Attributes stat = sftp.stat(handle);
+        return stat.getSize();
     }
 
     @Override
@@ -245,24 +329,37 @@ public class SftpRemotePathChannel extends FileChannel {
 
     @Override
     public long transferTo(long position, long count, WritableByteChannel 
target) throws IOException {
-        if ((position < 0) || (count < 0)) {
-            throw new IllegalArgumentException("transferTo(" + getRemotePath() 
+ ") illegal position (" + position + ") or count (" + count + ")");
+        if ((position < 0L) || (count < 0L)) {
+            throw new IllegalArgumentException("transferTo(" + getRemotePath() 
+ ")"
+                + " illegal position (" + position + ") or count (" + count + 
")");
         }
         ensureOpen(READ_MODES);
+
+        ClientSession clientSession = sftp.getClientSession();
+        int copySize = clientSession.getIntProperty(
+            COPY_BUFSIZE_PROP, DEFAULT_TRANSFER_BUFFER_SIZE);
+        boolean debugEnabled = log.isDebugEnabled();
+        if (debugEnabled) {
+            log.debug("transferTo({})[position={}, count={}] use copySize={} 
for target={}",
+                this, position, count, copySize, target);
+        }
+
+        boolean completed = false;
+        boolean eof = false;
+        long curPos = position;
+        int bufSize = (int) Math.min(count, copySize);
+        byte[] buffer = new byte[bufSize];
+        long totalRead = 0L;
+
         synchronized (lock) {
-            boolean completed = false;
-            boolean eof = false;
-            long curPos = position;
             try {
-                beginBlocking();
+                beginBlocking("transferTo");
 
-                int bufSize = (int) Math.min(count, Short.MAX_VALUE + 1);
-                byte[] buffer = new byte[bufSize];
-                long totalRead = 0L;
                 while (totalRead < count) {
-                    int read = sftp.read(handle, curPos, buffer, 0, 
buffer.length);
+                    int read = sftp.read(handle, curPos, buffer, 0,
+                        (int) Math.min(count - totalRead, buffer.length));
                     if (read > 0) {
-                        ByteBuffer wrap = ByteBuffer.wrap(buffer);
+                        ByteBuffer wrap = ByteBuffer.wrap(buffer, 0, read);
                         while (wrap.remaining() > 0) {
                             target.write(wrap);
                         }
@@ -273,21 +370,35 @@ public class SftpRemotePathChannel extends FileChannel {
                     }
                 }
                 completed = true;
-                return totalRead > 0 ? totalRead : eof ? -1 : 0;
             } finally {
-                endBlocking(completed);
+                endBlocking("transferTo", completed);
             }
         }
+
+        if (debugEnabled) {
+            log.debug("transferTo({})[position={}, count={}] with copySize={} 
- totalRead={}, eo{} for target={}",
+                this, position, count, copySize, totalRead, eof, target);
+        }
+
+        return totalRead > 0L ? totalRead : eof ? -1L : 0L;
     }
 
     @Override
     public long transferFrom(ReadableByteChannel src, long position, long 
count) throws IOException {
-        if ((position < 0) || (count < 0)) {
-            throw new IllegalArgumentException("transferFrom(" + 
getRemotePath() + ") illegal position (" + position + ") or count (" + count + 
")");
+        if ((position < 0L) || (count < 0L)) {
+            throw new IllegalArgumentException("transferFrom(" + 
getRemotePath() + ")"
+                + " illegal position (" + position + ") or count (" + count + 
")");
         }
         ensureOpen(WRITE_MODES);
 
-        int copySize = 
sftp.getClientSession().getIntProperty(COPY_BUFSIZE_PROP, 
DEFAULT_TRANSFER_BUFFER_SIZE);
+        ClientSession clientSession = sftp.getClientSession();
+        int copySize = clientSession.getIntProperty(COPY_BUFSIZE_PROP, 
DEFAULT_TRANSFER_BUFFER_SIZE);
+        boolean debugEnabled = log.isDebugEnabled();
+        if (debugEnabled) {
+            log.debug("transferFrom({})[position={}, count={}] use copySize={} 
for source={}",
+                this, position, count, copySize, src);
+        }
+
         boolean completed = false;
         long curPos = (position >= 0L) ? position : posTracker.get();
         long totalRead = 0L;
@@ -295,10 +406,11 @@ public class SftpRemotePathChannel extends FileChannel {
 
         synchronized (lock) {
             try {
-                beginBlocking();
+                beginBlocking("transferFrom");
 
                 while (totalRead < count) {
-                    ByteBuffer wrap = ByteBuffer.wrap(buffer, 0, (int) 
Math.min(buffer.length, count - totalRead));
+                    ByteBuffer wrap = ByteBuffer.wrap(
+                        buffer, 0, (int) Math.min(buffer.length, count - 
totalRead));
                     int read = src.read(wrap);
                     if (read > 0) {
                         sftp.write(handle, curPos, buffer, 0, read);
@@ -309,16 +421,22 @@ public class SftpRemotePathChannel extends FileChannel {
                     }
                 }
                 completed = true;
-                return totalRead;
             } finally {
-                endBlocking(completed);
+                endBlocking("transferFrom", completed);
             }
         }
+
+        if (debugEnabled) {
+            log.debug("transferFrom({})[position={}, count={}] use copySize={} 
- totalRead={}, completed={} for source={}",
+                this, position, count, copySize, totalRead, completed, src);
+        }
+        return totalRead;
     }
 
     @Override
     public MappedByteBuffer map(MapMode mode, long position, long size) throws 
IOException {
-        throw new UnsupportedOperationException("map(" + getRemotePath() + 
")[" + mode + "," + position + "," + size + "] N/A");
+        throw new UnsupportedOperationException("map(" + getRemotePath() + ")"
+            + "[" + mode + "," + position + "," + size + "] N/A");
     }
 
     @Override
@@ -327,7 +445,7 @@ public class SftpRemotePathChannel extends FileChannel {
     }
 
     @Override
-    public FileLock tryLock(final long position, final long size, boolean 
shared) throws IOException {
+    public FileLock tryLock(long position, long size, boolean shared) throws 
IOException {
         ensureOpen(Collections.emptySet());
 
         try {
@@ -347,7 +465,6 @@ public class SftpRemotePathChannel extends FileChannel {
                 return acquiredBy().isOpen() && valid.get();
             }
 
-            @SuppressWarnings("synthetic-access")
             @Override
             public void release() throws IOException {
                 if (valid.compareAndSet(true, false)) {
@@ -359,6 +476,10 @@ public class SftpRemotePathChannel extends FileChannel {
 
     @Override
     protected void implCloseChannel() throws IOException {
+        if (log.isDebugEnabled()) {
+            log.debug("implCloseChannel({}) closeOnExit={}", this, 
closeOnExit);
+        }
+
         try {
             Thread thread = blockingThreadHolder.get();
             if (thread != null) {
@@ -375,12 +496,21 @@ public class SftpRemotePathChannel extends FileChannel {
         }
     }
 
-    private void beginBlocking() {
+    protected void beginBlocking(Object actionHint) {
+        if (log.isDebugEnabled()) {
+            log.debug("beginBlocking({})[{}]", this, actionHint);
+        }
+
         begin();
         blockingThreadHolder.set(Thread.currentThread());
     }
 
-    private void endBlocking(boolean completed) throws 
AsynchronousCloseException {
+    protected void endBlocking(Object actionHint, boolean completed)
+            throws AsynchronousCloseException {
+        if (log.isDebugEnabled()) {
+            log.debug("endBlocking({})[{}] completed={}", this, actionHint, 
completed);
+        }
+
         blockingThreadHolder.set(null);
         end(completed);
     }
@@ -390,8 +520,7 @@ public class SftpRemotePathChannel extends FileChannel {
      * at least one of the required ones
      *
      * @param reqModes The required modes - ignored if {@code null}/empty
-     * @throws IOException If channel not open or the required modes are not
-     *                     satisfied
+     * @throws IOException If channel not open or the required modes are not 
satisfied
      */
     private void ensureOpen(Collection<SftpClient.OpenMode> reqModes) throws 
IOException {
         if (!isOpen()) {
@@ -405,7 +534,9 @@ public class SftpRemotePathChannel extends FileChannel {
                 }
             }
 
-            throw new IOException("ensureOpen(" + getRemotePath() + ") current 
channel modes (" + this.modes + ") do contain any of the required: " + 
reqModes);
+            throw new IOException("ensureOpen(" + getRemotePath() + ")"
+                + " current channel modes (" + this.modes + ")"
+                + " do contain any of the required ones: " + reqModes);
         }
     }
 
diff --git 
a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpRemotePathChannelTest.java
 
b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpRemotePathChannelTest.java
new file mode 100644
index 0000000..2a496a9
--- /dev/null
+++ 
b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpRemotePathChannelTest.java
@@ -0,0 +1,183 @@
+/*
+ * 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;
+import java.io.Writer;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.util.Date;
+import java.util.EnumSet;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.common.subsystem.sftp.SftpConstants;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
+import org.junit.Before;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@SuppressWarnings("checkstyle:MethodCount")
+public class SftpRemotePathChannelTest extends AbstractSftpClientTestSupport {
+    public SftpRemotePathChannelTest() throws IOException {
+        super();
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        setupServer();
+    }
+
+    @Test   // see SSHD-697
+    public void testFileChannel() throws IOException {
+        Path targetPath = detectTargetFolder();
+        Path lclSftp = CommonTestSupportUtils.resolve(
+            targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, 
getClass().getSimpleName());
+        Path lclFile = 
assertHierarchyTargetFolderExists(lclSftp).resolve(getCurrentTestName() + 
".txt");
+        Files.deleteIfExists(lclFile);
+        byte[] expected = (getClass().getName() + "#" + getCurrentTestName() + 
"(" + new Date() + ")").getBytes(StandardCharsets.UTF_8);
+
+        try (ClientSession session = client.connect(getCurrentTestName(), 
TEST_LOCALHOST, port)
+                .verify(7L, TimeUnit.SECONDS)
+                .getSession()) {
+            session.addPasswordIdentity(getCurrentTestName());
+            session.auth().verify(5L, TimeUnit.SECONDS);
+
+            try (SftpClient sftp = createSftpClient(session)) {
+                Path parentPath = targetPath.getParent();
+                String remFilePath = 
CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, lclFile);
+
+                try (FileChannel fc = sftp.openRemotePathChannel(
+                        remFilePath, EnumSet.of(
+                            StandardOpenOption.CREATE, 
StandardOpenOption.READ, StandardOpenOption.WRITE))) {
+                    int writeLen = fc.write(ByteBuffer.wrap(expected));
+                    assertEquals("Mismatched written length", expected.length, 
writeLen);
+
+                    FileChannel fcPos = fc.position(0L);
+                    assertSame("Mismatched positioned file channel", fc, 
fcPos);
+
+                    byte[] actual = new byte[expected.length];
+                    int readLen = fc.read(ByteBuffer.wrap(actual));
+                    assertEquals("Mismatched read len", writeLen, readLen);
+                    assertArrayEquals("Mismatched read data", expected, 
actual);
+                }
+            }
+        }
+
+        byte[] actual = Files.readAllBytes(lclFile);
+        assertArrayEquals("Mismatched persisted data", expected, actual);
+    }
+
+    @Test   // see SSHD-967
+    public void testTransferToFileChannel() throws IOException {
+        Path targetPath = detectTargetFolder();
+        Path lclSftp = CommonTestSupportUtils.resolve(
+            targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, 
getClass().getSimpleName());
+        Path srcFile = 
assertHierarchyTargetFolderExists(lclSftp).resolve(getCurrentTestName() + 
"-src.txt");
+        Path parentPath = targetPath.getParent();
+
+        Files.deleteIfExists(srcFile);
+        try (Writer output = Files.newBufferedWriter(srcFile, 
StandardCharsets.UTF_8)) {
+            String seed = getClass().getName() + "#" + getCurrentTestName() + 
"(" + new Date() + ")";
+            for (long totalWritten = 0L;
+                totalWritten <= 
SftpRemotePathChannel.DEFAULT_TRANSFER_BUFFER_SIZE;
+                totalWritten += seed.length()) {
+                output.append(seed).append(System.lineSeparator());
+            }
+        }
+
+        byte[] expected = Files.readAllBytes(srcFile);
+        Path dstFile = srcFile.getParent().resolve(getCurrentTestName() + 
"-dst.txt");
+        Files.deleteIfExists(dstFile);
+
+        String remFilePath = 
CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, srcFile);
+        try (ClientSession session = client.connect(getCurrentTestName(), 
TEST_LOCALHOST, port)
+                .verify(7L, TimeUnit.SECONDS)
+                .getSession()) {
+            session.addPasswordIdentity(getCurrentTestName());
+            session.auth().verify(5L, TimeUnit.SECONDS);
+
+            try (SftpClient sftp = createSftpClient(session);
+                 FileChannel srcChannel = sftp.openRemotePathChannel(
+                     remFilePath, EnumSet.of(StandardOpenOption.READ));
+                 FileChannel dstChannel = FileChannel.open(dstFile,
+                      StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
+                long numXfered = srcChannel.transferTo(0L, expected.length, 
dstChannel);
+                assertEquals("Mismatched reported transfer count", 
expected.length, numXfered);
+            }
+        }
+
+        byte[] actual = Files.readAllBytes(dstFile);
+        assertEquals("Mismatched transfered size", expected.length, 
actual.length);
+        assertArrayEquals("Mismatched transferred data", expected, actual);
+    }
+
+    @Test   // see SSHD-967
+    public void testTransferFromFileChannel() throws IOException {
+        Path targetPath = detectTargetFolder();
+        Path lclSftp = CommonTestSupportUtils.resolve(
+            targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, 
getClass().getSimpleName());
+        Path srcFile = 
assertHierarchyTargetFolderExists(lclSftp).resolve(getCurrentTestName() + 
"-src.txt");
+        Path parentPath = targetPath.getParent();
+
+        Files.deleteIfExists(srcFile);
+        try (Writer output = Files.newBufferedWriter(srcFile, 
StandardCharsets.UTF_8)) {
+            String seed = getClass().getName() + "#" + getCurrentTestName() + 
"(" + new Date() + ")";
+            for (long totalWritten = 0L;
+                totalWritten <= 
SftpRemotePathChannel.DEFAULT_TRANSFER_BUFFER_SIZE;
+                totalWritten += seed.length()) {
+                output.append(seed).append(System.lineSeparator());
+            }
+        }
+
+        byte[] expected = Files.readAllBytes(srcFile);
+        Path dstFile = srcFile.getParent().resolve(getCurrentTestName() + 
"-dst.txt");
+        Files.deleteIfExists(dstFile);
+
+        String remFilePath = 
CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, dstFile);
+        try (ClientSession session = client.connect(getCurrentTestName(), 
TEST_LOCALHOST, port)
+                .verify(7L, TimeUnit.SECONDS)
+                .getSession()) {
+            session.addPasswordIdentity(getCurrentTestName());
+            session.auth().verify(5L, TimeUnit.SECONDS);
+
+            try (SftpClient sftp = createSftpClient(session);
+                 FileChannel dstChannel = sftp.openRemotePathChannel(
+                     remFilePath, EnumSet.of(StandardOpenOption.CREATE, 
StandardOpenOption.WRITE));
+                 FileChannel srcChannel = FileChannel.open(srcFile, 
StandardOpenOption.READ)) {
+                long numXfered = dstChannel.transferFrom(srcChannel, 0L, 
expected.length);
+                assertEquals("Mismatched reported transfer count", 
expected.length, numXfered);
+            }
+        }
+
+        byte[] actual = Files.readAllBytes(dstFile);
+        assertEquals("Mismatched transfered size", expected.length, 
actual.length);
+        assertArrayEquals("Mismatched transferred data", expected, actual);
+    }
+}
\ No newline at end of file
diff --git 
a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpTest.java 
b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpTest.java
index 17b11cf..9abf06b 100644
--- 
a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpTest.java
+++ 
b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpTest.java
@@ -26,8 +26,6 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.SocketTimeoutException;
-import java.nio.ByteBuffer;
-import java.nio.channels.FileChannel;
 import java.nio.channels.SeekableByteChannel;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.CopyOption;
@@ -44,7 +42,6 @@ import java.nio.file.attribute.PosixFilePermission;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.Date;
 import java.util.EnumSet;
 import java.util.LinkedList;
 import java.util.List;
@@ -159,7 +156,8 @@ public class SftpTest extends AbstractSftpClientTestSupport 
{
     public void testWriteOffsetIgnoredForAppendMode() throws IOException {
         Path targetPath = detectTargetFolder();
         Path parentPath = targetPath.getParent();
-        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, 
SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), 
getCurrentTestName());
+        Path lclSftp = CommonTestSupportUtils.resolve(
+            targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, 
getClass().getSimpleName(), getCurrentTestName());
         Path testFile = 
assertHierarchyTargetFolderExists(lclSftp).resolve("file.txt");
         Files.deleteIfExists(testFile);
 
@@ -178,7 +176,8 @@ public class SftpTest extends AbstractSftpClientTestSupport 
{
             try (SftpClient sftp = createSftpClient(session)) {
                 String file = 
CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, testFile);
 
-                try (CloseableHandle handle = sftp.open(file, OpenMode.Create, 
OpenMode.Write, OpenMode.Read, OpenMode.Append)) {
+                try (CloseableHandle handle = sftp.open(
+                        file, OpenMode.Create, OpenMode.Write, OpenMode.Read, 
OpenMode.Append)) {
                     sftp.write(handle, 7365L, expectedRandom);
                     byte[] actualRandom = new byte[expectedRandom.length];
                     int readLen = sftp.read(handle, 0L, actualRandom);
@@ -195,7 +194,8 @@ public class SftpTest extends AbstractSftpClientTestSupport 
{
         }
 
         byte[] actualBytes = Files.readAllBytes(testFile);
-        assertEquals("Mismatched result file size", expectedRandom.length + 
expectedText.length, actualBytes.length);
+        assertEquals("Mismatched result file size",
+            expectedRandom.length + expectedText.length, actualBytes.length);
 
         byte[] actualRandom = Arrays.copyOfRange(actualBytes, 0, 
expectedRandom.length);
         assertArrayEquals("Mismatched random part", expectedRandom, 
actualRandom);
@@ -208,7 +208,8 @@ public class SftpTest extends AbstractSftpClientTestSupport 
{
     public void testReadBufferLimit() throws Exception {
         Path targetPath = detectTargetFolder();
         Path parentPath = targetPath.getParent();
-        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, 
SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), 
getCurrentTestName());
+        Path lclSftp = CommonTestSupportUtils.resolve(
+            targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, 
getClass().getSimpleName(), getCurrentTestName());
         Path testFile = 
assertHierarchyTargetFolderExists(lclSftp).resolve("file.txt");
         byte[] expected = new byte[1024];
 
@@ -228,7 +229,8 @@ public class SftpTest extends AbstractSftpClientTestSupport 
{
                 byte[] actual = new byte[expected.length];
                 int maxAllowed = actual.length / 4;
                 // allow less than actual
-                PropertyResolverUtils.updateProperty(sshd, 
AbstractSftpSubsystemHelper.MAX_READDATA_PACKET_LENGTH_PROP, maxAllowed);
+                PropertyResolverUtils.updateProperty(
+                    sshd, 
AbstractSftpSubsystemHelper.MAX_READDATA_PACKET_LENGTH_PROP, maxAllowed);
                 try (CloseableHandle handle = sftp.open(file, OpenMode.Read)) {
                     int readLen = sftp.read(handle, 0L, actual);
                     assertEquals("Mismatched read len", maxAllowed, readLen);
@@ -283,8 +285,9 @@ public class SftpTest extends AbstractSftpClientTestSupport 
{
 
     private void testCannotEscapeRoot(boolean useAbsolutePath) throws 
Exception {
         Path targetPath = detectTargetFolder();
-        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, 
SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), 
getCurrentTestName());
-        assertHierarchyTargetFolderExists(lclSftp);
+        Path lclSftp = CommonTestSupportUtils.resolve(
+            targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, 
getClass().getSimpleName(), getCurrentTestName());
+        lclSftp = assertHierarchyTargetFolderExists(lclSftp);
         sshd.setFileSystemFactory(new VirtualFileSystemFactory(lclSftp));
 
         try (ClientSession session = client.connect(getCurrentTestName(), 
TEST_LOCALHOST, port)
@@ -351,7 +354,8 @@ public class SftpTest extends AbstractSftpClientTestSupport 
{
     public void testNormalizeRemotePathsValues() throws Exception {
         Path targetPath = detectTargetFolder();
         Path parentPath = targetPath.getParent();
-        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, 
SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), 
getCurrentTestName());
+        Path lclSftp = CommonTestSupportUtils.resolve(
+            targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, 
getClass().getSimpleName(), getCurrentTestName());
         Path testFile = 
assertHierarchyTargetFolderExists(lclSftp).resolve("file.txt");
         String file = 
CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, testFile);
         String[] comps = GenericUtils.split(file, '/');
@@ -405,7 +409,8 @@ public class SftpTest extends AbstractSftpClientTestSupport 
{
     public void testOpen() throws Exception {
         Path targetPath = detectTargetFolder();
         Path parentPath = targetPath.getParent();
-        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, 
SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), 
getCurrentTestName());
+        Path lclSftp = CommonTestSupportUtils.resolve(
+            targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, 
getClass().getSimpleName(), getCurrentTestName());
         Path clientFolder = lclSftp.resolve("client");
         Path testFile = clientFolder.resolve("file.txt");
         String file = 
CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, testFile);
@@ -458,7 +463,8 @@ public class SftpTest extends AbstractSftpClientTestSupport 
{
 
                 javaFile.setWritable(true, false);
 
-                try (SftpClient.CloseableHandle h = sftp.open(file, 
EnumSet.of(SftpClient.OpenMode.Truncate, SftpClient.OpenMode.Write))) {
+                try (SftpClient.CloseableHandle h = sftp.open(
+                        file, EnumSet.of(SftpClient.OpenMode.Truncate, 
SftpClient.OpenMode.Write))) {
                     // OK should succeed
                     assertTrue("Handle not marked as open for file=" + file, 
h.isOpen());
                 }
@@ -524,7 +530,8 @@ public class SftpTest extends AbstractSftpClientTestSupport 
{
             try (SftpClient sftp = createSftpClient(session)) {
                 Collection<PosixFilePermission> initialPermissions = 
IoUtils.getPermissions(testFile);
                 assertTrue("File does not have enough initial permissions: " + 
initialPermissions,
-                    
initialPermissions.containsAll(EnumSet.of(PosixFilePermission.OWNER_READ, 
PosixFilePermission.OWNER_WRITE)));
+                    initialPermissions.containsAll(
+                        EnumSet.of(PosixFilePermission.OWNER_READ, 
PosixFilePermission.OWNER_WRITE)));
 
                 try (CloseableHandle handle = sendRawAttributeImpactOpen(file, 
sftp)) {
                     outputDebugMessage("%s - handle=%s", getCurrentTestName(), 
handle);
@@ -541,7 +548,8 @@ public class SftpTest extends AbstractSftpClientTestSupport 
{
     }
 
     private CloseableHandle sendRawAttributeImpactOpen(String path, SftpClient 
sftpClient) throws Exception {
-        RawSftpClient sftp = assertObjectInstanceOf("Not a raw SFTP client 
used", RawSftpClient.class, sftpClient);
+        RawSftpClient sftp = assertObjectInstanceOf(
+            "Not a raw SFTP client used", RawSftpClient.class, sftpClient);
         Buffer buffer = new ByteArrayBuffer(path.length() + Long.SIZE, false);
         buffer.putString(path, StandardCharsets.UTF_8);
         //access
@@ -573,7 +581,8 @@ public class SftpTest extends AbstractSftpClientTestSupport 
{
     public void testInputStreamSkipAndReset() throws Exception {
         Path targetPath = detectTargetFolder();
         Path parentPath = targetPath.getParent();
-        Path localFile = CommonTestSupportUtils.resolve(targetPath, 
SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), 
getCurrentTestName());
+        Path localFile = CommonTestSupportUtils.resolve(
+            targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, 
getClass().getSimpleName(), getCurrentTestName());
         Files.createDirectories(localFile.getParent());
         byte[] data = (getClass().getName() + "#" + getCurrentTestName() + "[" 
+ localFile + "]").getBytes(StandardCharsets.UTF_8);
         Files.write(localFile, data, StandardOpenOption.CREATE);
@@ -584,7 +593,8 @@ public class SftpTest extends AbstractSftpClientTestSupport 
{
             session.auth().verify(5L, TimeUnit.SECONDS);
 
             try (SftpClient sftp = createSftpClient(session);
-                 InputStream stream = 
sftp.read(CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, 
localFile), OpenMode.Read)) {
+                 InputStream stream = sftp.read(
+                     
CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, localFile), 
OpenMode.Read)) {
                 assertFalse("Stream reported mark supported", 
stream.markSupported());
                 try {
                     stream.mark(data.length);
@@ -650,7 +660,8 @@ public class SftpTest extends AbstractSftpClientTestSupport 
{
 
                 @Override
                 public DirectoryStream<Path> openDirectory(
-                        ServerSession session, SftpEventListenerManager 
subsystem, DirectoryHandle dirHandle, Path dir, String handle)
+                        ServerSession session, SftpEventListenerManager 
subsystem,
+                        DirectoryHandle dirHandle, Path dir, String handle)
                             throws IOException {
                     dirHolder.set(dir);
                     return SftpFileSystemAccessor.super.openDirectory(session, 
subsystem, dirHandle, dir, handle);
@@ -801,8 +812,9 @@ public class SftpTest extends AbstractSftpClientTestSupport 
{
 
             @Override
             @SuppressWarnings("checkstyle:ParameterNumber")
-            public void read(ServerSession session, String remoteHandle, 
FileHandle localHandle,
-                    long offset, byte[] data, int dataOffset, int dataLen, int 
readLen, Throwable thrown) {
+            public void read(
+                    ServerSession session, String remoteHandle, FileHandle 
localHandle, long offset,
+                    byte[] data, int dataOffset, int dataLen, int readLen, 
Throwable thrown) {
                 readSize.addAndGet(readLen);
                 if (log.isDebugEnabled()) {
                     log.debug("read(" + session + ")[" + localHandle.getFile() 
+ "] offset=" + offset + ", requested=" + dataLen + ", read=" + readLen);
@@ -810,7 +822,8 @@ public class SftpTest extends AbstractSftpClientTestSupport 
{
             }
 
             @Override
-            public void read(ServerSession session, String remoteHandle, 
DirectoryHandle localHandle, Map<String, Path> entries) {
+            public void read(
+                    ServerSession session, String remoteHandle, 
DirectoryHandle localHandle, Map<String, Path> entries) {
                 int numEntries = GenericUtils.size(entries);
                 entriesCount.addAndGet(numEntries);
 
@@ -945,7 +958,8 @@ public class SftpTest extends AbstractSftpClientTestSupport 
{
             session.auth().verify(5L, TimeUnit.SECONDS);
 
             Path targetPath = detectTargetFolder();
-            Path lclSftp = CommonTestSupportUtils.resolve(targetPath, 
SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), 
getCurrentTestName());
+            Path lclSftp = CommonTestSupportUtils.resolve(
+                targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, 
getClass().getSimpleName(), getCurrentTestName());
             CommonTestSupportUtils.deleteRecursive(lclSftp);
 
             Path parentPath = targetPath.getParent();
@@ -963,13 +977,20 @@ public class SftpTest extends 
AbstractSftpClientTestSupport {
                 PropertyResolverUtils.updateProperty(clientChannel, 
AbstractSftpClient.WRITE_CHUNK_SIZE,
                     Math.min(SftpClient.DEFAULT_WRITE_BUFFER_SIZE, 
AbstractSftpClient.DEFAULT_WRITE_CHUNK_SIZE) - Byte.MAX_VALUE);
 
-                uploadAndVerifyFile(sftp, clientFolder, dir, 
SshConstants.SSH_REQUIRED_TOTAL_PACKET_LENGTH_SUPPORT - 1, 
"bufferMaxLenMinusOneFile.txt");
-                uploadAndVerifyFile(sftp, clientFolder, dir, 
SshConstants.SSH_REQUIRED_TOTAL_PACKET_LENGTH_SUPPORT, "bufferMaxLenFile.txt");
-                uploadAndVerifyFile(sftp, clientFolder, dir, 
SshConstants.SSH_REQUIRED_TOTAL_PACKET_LENGTH_SUPPORT + 1, 
"bufferMaxLenPlusOneFile.txt");
-                uploadAndVerifyFile(sftp, clientFolder, dir, (int) (1.5 * 
SshConstants.SSH_REQUIRED_TOTAL_PACKET_LENGTH_SUPPORT), 
"1point5BufferMaxLenFile.txt");
-                uploadAndVerifyFile(sftp, clientFolder, dir, (2 * 
SshConstants.SSH_REQUIRED_TOTAL_PACKET_LENGTH_SUPPORT) - 1, 
"2TimesBufferMaxLenMinusOneFile.txt");
-                uploadAndVerifyFile(sftp, clientFolder, dir, 2 * 
SshConstants.SSH_REQUIRED_TOTAL_PACKET_LENGTH_SUPPORT, 
"2TimesBufferMaxLenFile.txt");
-                uploadAndVerifyFile(sftp, clientFolder, dir, (2 * 
SshConstants.SSH_REQUIRED_TOTAL_PACKET_LENGTH_SUPPORT) + 1, 
"2TimesBufferMaxLenPlusOneFile.txt");
+                uploadAndVerifyFile(sftp, clientFolder, dir,
+                    SshConstants.SSH_REQUIRED_TOTAL_PACKET_LENGTH_SUPPORT - 1, 
"bufferMaxLenMinusOneFile.txt");
+                uploadAndVerifyFile(sftp, clientFolder, dir,
+                    SshConstants.SSH_REQUIRED_TOTAL_PACKET_LENGTH_SUPPORT, 
"bufferMaxLenFile.txt");
+                uploadAndVerifyFile(sftp, clientFolder, dir,
+                    SshConstants.SSH_REQUIRED_TOTAL_PACKET_LENGTH_SUPPORT + 1, 
"bufferMaxLenPlusOneFile.txt");
+                uploadAndVerifyFile(sftp, clientFolder, dir,
+                    (int) (1.5 * 
SshConstants.SSH_REQUIRED_TOTAL_PACKET_LENGTH_SUPPORT), 
"1point5BufferMaxLenFile.txt");
+                uploadAndVerifyFile(sftp, clientFolder, dir,
+                    (2 * 
SshConstants.SSH_REQUIRED_TOTAL_PACKET_LENGTH_SUPPORT) - 1, 
"2TimesBufferMaxLenMinusOneFile.txt");
+                uploadAndVerifyFile(sftp, clientFolder, dir,
+                    2 * SshConstants.SSH_REQUIRED_TOTAL_PACKET_LENGTH_SUPPORT, 
"2TimesBufferMaxLenFile.txt");
+                uploadAndVerifyFile(sftp, clientFolder, dir,
+                    (2 * 
SshConstants.SSH_REQUIRED_TOTAL_PACKET_LENGTH_SUPPORT) + 1, 
"2TimesBufferMaxLenPlusOneFile.txt");
                 uploadAndVerifyFile(sftp, clientFolder, dir, 200000, 
"largerFile.txt");
 
                 // test erroneous calls that check for negative values
@@ -986,7 +1007,8 @@ public class SftpTest extends 
AbstractSftpClientTestSupport {
         // generate random file and upload it
         String randomData = randomString(5);
         byte[] randomBytes = randomData.getBytes(StandardCharsets.UTF_8);
-        try (SftpClient.CloseableHandle handle = sftp.open(filePath, 
EnumSet.of(SftpClient.OpenMode.Write, SftpClient.OpenMode.Create))) {
+        try (SftpClient.CloseableHandle handle = sftp.open(
+                filePath, EnumSet.of(SftpClient.OpenMode.Write, 
SftpClient.OpenMode.Create))) {
             try {
                 sftp.write(handle, -1, randomBytes, 0, 0);
                 fail("should not have been able to write file with invalid 
file offset for " + filePath);
@@ -1023,11 +1045,14 @@ public class SftpTest extends 
AbstractSftpClientTestSupport {
         assertFalse("File should not be there: " + file.toString(), 
Files.exists(file));
     }
 
-    private void uploadAndVerifyFile(SftpClient sftp, Path clientFolder, 
String remoteDir, int size, String filename) throws Exception {
+    private void uploadAndVerifyFile(
+            SftpClient sftp, Path clientFolder, String remoteDir, int size, 
String filename)
+                throws Exception {
         // generate random file and upload it
         String remotePath = remoteDir + "/" + filename;
         String randomData = randomString(size);
-        try (SftpClient.CloseableHandle handle = sftp.open(remotePath, 
EnumSet.of(SftpClient.OpenMode.Write, SftpClient.OpenMode.Create))) {
+        try (SftpClient.CloseableHandle handle = sftp.open(
+                remotePath, EnumSet.of(SftpClient.OpenMode.Write, 
SftpClient.OpenMode.Create))) {
             sftp.write(handle, 0, randomData.getBytes(StandardCharsets.UTF_8), 
0, randomData.length());
         }
 
@@ -1046,7 +1071,8 @@ public class SftpTest extends 
AbstractSftpClientTestSupport {
         String d = getCurrentTestName() + "\n";
 
         Path targetPath = detectTargetFolder();
-        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, 
SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), 
getCurrentTestName());
+        Path lclSftp = CommonTestSupportUtils.resolve(
+            targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, 
getClass().getSimpleName(), getCurrentTestName());
         CommonTestSupportUtils.deleteRecursive(lclSftp);
 
         Path target = 
assertHierarchyTargetFolderExists(lclSftp).resolve("file.txt");
@@ -1072,7 +1098,8 @@ public class SftpTest extends 
AbstractSftpClientTestSupport {
     @Test
     public void testReadWriteWithOffset() throws Exception {
         Path targetPath = detectTargetFolder();
-        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, 
SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), 
getCurrentTestName());
+        Path lclSftp = CommonTestSupportUtils.resolve(
+            targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, 
getClass().getSimpleName(), getCurrentTestName());
         CommonTestSupportUtils.deleteRecursive(lclSftp);
 
         Path localPath = 
assertHierarchyTargetFolderExists(lclSftp).resolve("file.txt");
@@ -1150,7 +1177,8 @@ public class SftpTest extends 
AbstractSftpClientTestSupport {
     @Test
     public void testRename() throws Exception {
         Path targetPath = detectTargetFolder();
-        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, 
SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), 
getCurrentTestName());
+        Path lclSftp = CommonTestSupportUtils.resolve(
+            targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, 
getClass().getSimpleName(), getCurrentTestName());
         CommonTestSupportUtils.deleteRecursive(lclSftp);
 
         Path parentPath = targetPath.getParent();
@@ -1348,7 +1376,8 @@ public class SftpTest extends 
AbstractSftpClientTestSupport {
             } catch (SocketTimeoutException | EOFException | 
WindowClosedException | SshChannelClosedException e) {
                 // expected - ignored
             } finally {
-                PropertyResolverUtils.updateProperty(session, 
SftpClient.SFTP_CHANNEL_OPEN_TIMEOUT, SftpClient.DEFAULT_CHANNEL_OPEN_TIMEOUT);
+                PropertyResolverUtils.updateProperty(
+                    session, SftpClient.SFTP_CHANNEL_OPEN_TIMEOUT, 
SftpClient.DEFAULT_CHANNEL_OPEN_TIMEOUT);
             }
         } finally {
             sshd.setSubsystemFactories(factories);
@@ -1357,7 +1386,8 @@ public class SftpTest extends 
AbstractSftpClientTestSupport {
 
     private void testClient(FactoryManager manager, SftpClient sftp) throws 
Exception {
         Path targetPath = detectTargetFolder();
-        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, 
SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), 
getCurrentTestName());
+        Path lclSftp = CommonTestSupportUtils.resolve(
+            targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, 
getClass().getSimpleName(), getCurrentTestName());
         CommonTestSupportUtils.deleteRecursive(lclSftp);
 
         Path parentPath = targetPath.getParent();
@@ -1366,7 +1396,8 @@ public class SftpTest extends 
AbstractSftpClientTestSupport {
         sftp.mkdir(dir);
 
         String file = dir + "/" + getCurrentTestName() + "-file.txt";
-        try (SftpClient.CloseableHandle h = sftp.open(file, 
EnumSet.of(SftpClient.OpenMode.Write, SftpClient.OpenMode.Create))) {
+        try (SftpClient.CloseableHandle h = sftp.open(
+                file, EnumSet.of(SftpClient.OpenMode.Write, 
SftpClient.OpenMode.Create))) {
             byte[] d = "0123456789\n".getBytes(StandardCharsets.UTF_8);
             sftp.write(h, 0, d, 0, d.length);
             sftp.write(h, d.length, d, 0, d.length);
@@ -1466,7 +1497,8 @@ public class SftpTest extends 
AbstractSftpClientTestSupport {
             }
 
             @Override
-            public void linked(ServerSession session, Path src, Path target, 
boolean symLink, Throwable thrown) {
+            public void linked(
+                    ServerSession session, Path src, Path target, boolean 
symLink, Throwable thrown) {
                 LinkData data = linkDataHolder.get();
                 assertNotNull("No previous linking call", data);
                 assertSame("Mismatched source", data.getSource(), src);
@@ -1537,44 +1569,6 @@ public class SftpTest extends 
AbstractSftpClientTestSupport {
         assertNotNull("No symlink signalled", linkDataHolder.getAndSet(null));
     }
 
-    @Test   // see SSHD-697
-    public void testFileChannel() throws IOException {
-        Path targetPath = detectTargetFolder();
-        Path lclSftp = CommonTestSupportUtils.resolve(targetPath, 
SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
-        Path lclFile = lclSftp.resolve(getCurrentTestName() + ".txt");
-        Files.deleteIfExists(lclFile);
-        byte[] expected = (getClass().getName() + "#" + getCurrentTestName() + 
"(" + new Date() + ")").getBytes(StandardCharsets.UTF_8);
-
-        try (ClientSession session = client.connect(getCurrentTestName(), 
TEST_LOCALHOST, port)
-                .verify(7L, TimeUnit.SECONDS)
-                .getSession()) {
-            session.addPasswordIdentity(getCurrentTestName());
-            session.auth().verify(5L, TimeUnit.SECONDS);
-
-            try (SftpClient sftp = createSftpClient(session)) {
-                Path parentPath = targetPath.getParent();
-                String remFilePath = 
CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, lclFile);
-
-                try (FileChannel fc = sftp.openRemotePathChannel(
-                        remFilePath, EnumSet.of(StandardOpenOption.CREATE, 
StandardOpenOption.READ, StandardOpenOption.WRITE))) {
-                    int writeLen = fc.write(ByteBuffer.wrap(expected));
-                    assertEquals("Mismatched written length", expected.length, 
writeLen);
-
-                    FileChannel fcPos = fc.position(0L);
-                    assertSame("Mismatched positioned file channel", fc, 
fcPos);
-
-                    byte[] actual = new byte[expected.length];
-                    int readLen = fc.read(ByteBuffer.wrap(actual));
-                    assertEquals("Mismatched read len", writeLen, readLen);
-                    assertArrayEquals("Mismatched read data", expected, 
actual);
-                }
-            }
-        }
-
-        byte[] actual = Files.readAllBytes(lclFile);
-        assertArrayEquals("Mismatched persisted data", expected, actual);
-    }
-
     @Test   // see SSHD-903
     public void testForcedVersionNegotiation() throws Exception {
         PropertyResolverUtils.updateProperty(sshd, 
SftpSubsystemEnvironment.SFTP_VERSION, SftpConstants.SFTP_V3);
@@ -1601,7 +1595,7 @@ public class SftpTest extends 
AbstractSftpClientTestSupport {
                 bos.write(buffer, 0, count);
             }
 
-            return bos.toString();
+            return bos.toString(StandardCharsets.UTF_8.name());
         } finally {
             c.disconnect();
         }

Reply via email to