Hi,

as a short backstory, we are looking at providing a SFTP endpoint for
legacy clients where they can upload files to a single root/default
directory and have them dynamically forwarded internally correctly. For
this purpose, all operations / file systems will be purely virtual. Even
listing of files won't be supported, just an upload operation (the root
directory will always appear to be empty).

When a file is finished uploaded (into server memory), we want to push the
file to an internal service. For the sake of this question, let's just say
it's a simple REST service that we push to. Workflow is basically:

1) User connects and is authenticated
2) Root directory appears to be empty
3) PUT <some file>
4) File is uploaded to server into server's memory
5) When upload is finished, server pushes file to its correct final
destination
6) Client receives OK

We have a working proof-of-concept of this that works really well. To do
this, we implemented a minimal NIO FileSystem.

The problem we are having is when [5] fails. The service we are pushing to
might be down, and then we want the client to get an error as well (=
please try again later). However, the problem is that we can not get the
server to report anything but success which means the client will think it
went through and the server is left with a file it couldn't process. We
don't want to have an internal retry-functionality inside the server but to
have the whole use-case synchronized.

To get back to the question, I have a hard time know if this is due to
incorrect usage of NIO FileSystem from my part, or if perhaps SSHD doesn't
support this usecase.

Our basic strategy is to eventually create a custom implementation of
FileChannel when FileSystemProvider#newFileChannel is called. Via debugging
inspection this appears to be callback we get on our FileSystemProvider
when an upload is initiated. This implementation handles the write()
operations to write the bytes into a local byte[], and then when eventually
UploadFileChannel#implCloseChannel() is called the byte[] is pushed.

implCloseChannel() can throw an IOException, but this doesn't seem to
matter. Regardless, from the client's point-of-view it doesn't matter if
this call is successful or not. Looking at the code, I can step in
SSHD-SFTP into:

Here, it seems like SSH_FXP_CLOSE would be sent if the closing of the
handle is not successful. I'm not sure if that would be the proper way of
reporting problems to the client, but regardless, it looks like this code
path isn't used. Because doClose(int, String) is called in the try-block,
and this code itself catch IOException in order to make callback to
listener. Should perhaps IOException be rethrown there?

protected void doClose(Buffer buffer, int id) throws IOException {
String handle = buffer.getString();
try {
doClose(id, handle);
} catch (IOException | RuntimeException e) {
sendStatus(prepareReply(buffer), id, e, SftpConstants.SSH_FXP_CLOSE,
handle);
return;
}

sendStatus(prepareReply(buffer), id, SftpConstants.SSH_FX_OK, "", "");
}
@Override
protected void doClose(int id, String handle) throws IOException {
Handle h = handles.remove(handle);
ServerSession session = getServerSession();
if (log.isDebugEnabled()) {
log.debug("doClose({})[id={}] SSH_FXP_CLOSE (handle={}[{}])",
  session, id, handle, h);
}

Handle nodeHandle = validateHandle(handle, h, Handle.class);
SftpEventListener listener = getSftpEventListenerProxy();
try {
listener.closing(session, handle, nodeHandle);
nodeHandle.close();
listener.closed(session, handle, nodeHandle, null);
} catch (IOException | RuntimeException e) {
listener.closed(session, handle, nodeHandle, e);
}
}

Or am I going about this the wrong way? I'm using apache-sshd-2.0.0.

Thanks in advance!

// Matthias

Reply via email to