tomaswolf opened a new pull request #206:
URL: https://github.com/apache/mina-sshd/pull/206
An Apache MINA sshd SFTP server configured to use an SftpFileSystem
pointing to yet another SFTP server would serve directory listings
only very slowly.
This was caused by the SFTP server implementation using Java FileSystem
abstractions itself for listing directories. Using java.nio.file.Files,
it ended up doing essentially the following when receiving a
SSH_FXP_READDIR command:
```
try(DirectoryStream<Path> list = Files.newDirectoryStream(dir)) {
Map<Path, BasicFileAttributes> toSend = new HashMap<>();
list.iterator().forEach(p ->
toSend.put(p, Files.readAttributes(p, BasicFileAttributes.class))
);
replyToClient(SSH_FXP_NAME, toSend);
};
```
(Not literally. This omits a lot of special handling for empty
directories, not sending too large messages back to the client, and
so on. But ultimately it boiled down to the above.)
That is fine when the server-side file system from which files are
served is local on the server. But when that file system itself is
a remote file system, `newDirectoryStream` is a remote call sending
SSH_FXP_OPENDIR and SSH_FXP_READDIR to the upstream server, and
additionally each of these `readAttributes()` calls is yet another
remote call sending SSH_FXP_LSTAT. This slows down getting the
directory listing to the client tremendously.
The most annoying part of all this is that the SSH_FXP_READDIR to the
upstream SFTP server in `newDirectoryStream` _already returned all the
attributes_, but this is lost because the Java NIO File abstractions
have no real support for getting a directory listing including
attributes in one fell swoop. (No, using a FileVisitor and
Files.walkFileTree with depth 1 wouldn't help.)
So detect this case in the server's SftpSubsystem, and bypass the Java
FileSystem abstraction if the file system is itself an SftpFileSystem.
Simply issue SSH_FXP_READDIR requests and forward the whole reply, which
includes file names and attributes, directly to the client. For reading
a directory containing 2000 files, this eliminates 10040 SSH_FXP_LSTAT
calls; only the directory itself is stat'ed.
A corollary to this is that clients in general should avoid listing
directories on an SftpFileSystem via java.nio.file.Files _if they also
need the file attributes_. Instead do
```
SftpFileSystem fs = ...;
try (SftpClient client = fs.getClient();
CloseableHandle dir = client.openDir(remDirPath)) {
for (SftpClient.DirEntry entry : client.listDir(dir)) {
Path path = dir.getFile().resolve(entry.getFilename());
SftpClient.Attributes = entry.getAttributes();
// Do whatever you need with it
}
}
```
Implementation note: another idea is to cache the attributes read in
SSH_FXP_READDIR in `newDirectoryStream` on the Path objects returned,
and make `readAttributes` return these cached attributes while the
stream is not closed yet. Technically, this is doable; it'd be a hack
similar to what Java's own UnixFileSystem does (and FileVisitor takes
advantage of it). However, it would break some edge cases, like a
client writing to one of the files during the directory stream
iteration, and then reading the attributes again and expecting the
new attributes but still getting the cached ones.
Side note: the SFTP protocol has no command to get _only_ a listing
of names. SSH_FXP_READDIR _always_ returns both names and attributes.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]