Skip to content

Commit df5d9cd

Browse files
committed
Merge branch 'master' into 3.0.0
2 parents b8f53b4 + bde5669 commit df5d9cd

3 files changed

Lines changed: 36 additions & 3 deletions

File tree

sshd-sftp/src/main/java/org/apache/sshd/sftp/server/FileHandle.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,21 @@ public int read(byte[] data, int doff, int length, long offset) throws IOExcepti
134134
@SuppressWarnings("resource")
135135
public int read(byte[] data, int doff, int length, long offset, AtomicReference<Boolean> eof) throws IOException {
136136
SeekableByteChannel channel = getFileChannel();
137+
if (length == 0 && offset >= channel.size()) {
138+
// Zero-length read at EOF. SFTP v3: draft RFC v2 says: "If [...] EOF is encountered before reading any
139+
// data, the server will respond with SSH_FXP_STATUS." (section 6.4). Later versions of the draft are
140+
// silent on this issue.
141+
//
142+
// See https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-02#section-6.4
143+
if (eof != null) {
144+
eof.set(true);
145+
}
146+
return -1;
147+
}
137148
channel = channel.position(offset);
138149
int l = channel.read(ByteBuffer.wrap(data, doff, length));
139-
if (l > 0 && eof != null && l < length) {
140-
eof.set(channel.position() >= channel.size());
150+
if (eof != null) {
151+
eof.set(l < 0 || (l < length && channel.position() >= channel.size()));
141152
}
142153
return l;
143154
}

sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystem.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -900,7 +900,7 @@ protected int doRead(
900900
session, id, Handle.safe(handle), h, offset, length);
901901
}
902902

903-
ValidateUtils.checkTrue(length > 0L, "Invalid read length: %d", length);
903+
ValidateUtils.checkTrue(length >= 0, "Invalid read length: %d", length);
904904
FileHandle fh = validateHandle(handle, h, FileHandle.class);
905905
SftpEventListener listener = getSftpEventListenerProxy();
906906
int readLen;

sshd-sftp/src/test/java/org/apache/sshd/sftp/client/SimpleSftpClientTest.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.nio.file.Path;
2626
import java.util.Collections;
2727
import java.util.EnumSet;
28+
import java.util.concurrent.atomic.AtomicReference;
2829

2930
import org.apache.sshd.common.file.FileSystemFactory;
3031
import org.apache.sshd.common.file.virtualfs.VirtualFileSystemFactory;
@@ -110,6 +111,27 @@ void sftpProxyCalls() throws Exception {
110111
byte[] local = Files.readAllBytes(clientFile);
111112
assertArrayEquals(written, local, "Mismatched remote written data");
112113

114+
// Test zero reads
115+
try (SftpClient.CloseableHandle h = sftp.open(remoteFilePath, SftpClient.OpenMode.Read)) {
116+
AtomicReference<Boolean> eof = new AtomicReference<>();
117+
byte[] buf = {};
118+
int bytesRead = sftp.read(h, 0, buf, eof);
119+
assertEquals(0, bytesRead);
120+
assertFalse(eof.get() != null && eof.get().booleanValue());
121+
bytesRead = sftp.read(h, written.length, buf, eof);
122+
assertEquals(-1, bytesRead);
123+
assertTrue(eof.get() != null && eof.get().booleanValue());
124+
bytesRead = sftp.read(h, 1, buf, eof);
125+
assertEquals(0, bytesRead);
126+
assertFalse(eof.get() != null && eof.get().booleanValue());
127+
bytesRead = sftp.read(h, written.length + 10, buf, eof);
128+
assertEquals(-1, bytesRead);
129+
assertTrue(eof.get() != null && eof.get().booleanValue());
130+
bytesRead = sftp.read(h, written.length - 1, buf, eof);
131+
assertEquals(0, bytesRead);
132+
assertFalse(eof.get() != null && eof.get().booleanValue());
133+
}
134+
113135
try (SftpClient.CloseableHandle h = sftp.openDir(remoteFileDir)) {
114136
boolean matchFound = false;
115137
for (SftpClient.DirEntry entry : sftp.listDir(h)) {

0 commit comments

Comments
 (0)