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 2016/10/02 09:37:40 UTC
mina-sshd git commit: [SSHD-702] Add support for an
SftpFileChannelOpener
Repository: mina-sshd
Updated Branches:
refs/heads/master 8c9fe09eb -> 1448b79ba
[SSHD-702] Add support for an SftpFileChannelOpener
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/1448b79b
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/1448b79b
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/1448b79b
Branch: refs/heads/master
Commit: 1448b79ba5848c8cb12bf87c8a1d6369b514fafa
Parents: 8c9fe09
Author: Lyor Goldstein <ly...@gmail.com>
Authored: Sun Oct 2 12:39:45 2016 +0300
Committer: Lyor Goldstein <ly...@gmail.com>
Committed: Sun Oct 2 12:39:45 2016 +0300
----------------------------------------------------------------------
README.md | 15 +++
.../server/subsystem/sftp/DirectoryHandle.java | 14 +-
.../sshd/server/subsystem/sftp/FileHandle.java | 40 +++---
.../sshd/server/subsystem/sftp/Handle.java | 14 +-
.../subsystem/sftp/SftpFileSystemAccessor.java | 133 +++++++++++++++++++
.../sftp/SftpFileSystemAccessorManager.java | 28 ++++
.../server/subsystem/sftp/SftpSubsystem.java | 33 +++--
.../subsystem/sftp/SftpSubsystemFactory.java | 37 ++++--
.../sshd/client/subsystem/sftp/SftpTest.java | 94 +++++++++++++
.../client/subsystem/sftp/SftpVersionsTest.java | 4 +-
.../SpaceAvailableExtensionImplTest.java | 2 +-
.../openssh/helpers/OpenSSHExtensionsTest.java | 2 +-
12 files changed, 369 insertions(+), 47 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/1448b79b/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
index 7c1cdf4..290fc68 100644
--- a/README.md
+++ b/README.md
@@ -406,6 +406,7 @@ the version, and all we can do at the server is require a **specific** version v
configuration key. For more advanced restrictions on needs to sub-class `SftpSubSystem` and provide a non-default
`SftpSubsystemFactory` that uses the sub-classed code.
+
### Using `SftpFileSystemProvider` to create an `SftpFileSystem`
@@ -495,6 +496,20 @@ configuration keys and values.
```
+#### Tracking accessed location via `SftpFileSystemAccessor`
+
+One can override the default `SftpFileSystemAccessor` and thus be able to track all opened files and folders
+throughout the SFTP server subsystem code. The accessor is registered/overwritten in via the `SftpSubSystemFactory`:
+
+```java
+
+ SftpSubsystemFactory factory = new SftpSubsystemFactory.Builder()
+ .withFileSystemAccessor(new MySftpFileSystemAccessor())
+ .build();
+ server.setSubsystemFactories(Collections.singletonList(factory));
+
+```
+
### Supported SFTP extensions
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/1448b79b/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/DirectoryHandle.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/DirectoryHandle.java b/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/DirectoryHandle.java
index 0319f6a..82e691e 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/DirectoryHandle.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/DirectoryHandle.java
@@ -20,10 +20,11 @@ package org.apache.sshd.server.subsystem.sftp;
import java.io.IOException;
import java.nio.file.DirectoryStream;
-import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Iterator;
+import org.apache.sshd.server.session.ServerSession;
+
/**
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
@@ -36,11 +37,14 @@ public class DirectoryHandle extends Handle implements Iterator<Path> {
private DirectoryStream<Path> ds;
private Iterator<Path> fileList;
- public DirectoryHandle(Path file) throws IOException {
- super(file);
- ds = Files.newDirectoryStream(file);
+ public DirectoryHandle(SftpSubsystem subsystem, Path dir, String handle) throws IOException {
+ super(dir, handle);
+
+ SftpFileSystemAccessor accessor = subsystem.getFileSystemAccessor();
+ ServerSession session = subsystem.getServerSession();
+ ds = accessor.openDirectory(session, subsystem, dir, handle);
- Path parent = file.getParent();
+ Path parent = dir.getParent();
if (parent == null) {
sendDotDot = false; // if no parent then no need to send ".."
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/1448b79b/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/FileHandle.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/FileHandle.java b/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/FileHandle.java
index 075665a..fe3aa1e 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/FileHandle.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/FileHandle.java
@@ -20,8 +20,8 @@ package org.apache.sshd.server.subsystem.sftp;
import java.io.IOException;
import java.nio.ByteBuffer;
-import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
+import java.nio.channels.SeekableByteChannel;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.FileAttribute;
@@ -32,12 +32,14 @@ import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
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.io.IoUtils;
+import org.apache.sshd.server.session.ServerSession;
/**
@@ -46,11 +48,13 @@ import org.apache.sshd.common.util.io.IoUtils;
public class FileHandle extends Handle {
private final int access;
- private final FileChannel fileChannel;
+ private final SeekableByteChannel fileChannel;
private final List<FileLock> locks = new ArrayList<>();
+ private final SftpSubsystem subsystem;
- public FileHandle(SftpSubsystem sftpSubsystem, Path file, int flags, int access, Map<String, Object> attrs) throws IOException {
- super(file);
+ public FileHandle(SftpSubsystem subsystem, Path file, String handle, int flags, int access, Map<String, Object> attrs) throws IOException {
+ super(file, handle);
+ this.subsystem = Objects.requireNonNull(subsystem, "No subsystem instance provided");
this.access = access;
Set<StandardOpenOption> options = EnumSet.noneOf(StandardOpenOption.class);
@@ -99,17 +103,19 @@ public class FileHandle extends Handle {
FileAttribute<?>[] fileAttrs = GenericUtils.isEmpty(attributes)
? IoUtils.EMPTY_FILE_ATTRIBUTES
: attributes.toArray(new FileAttribute<?>[attributes.size()]);
- FileChannel channel;
+ SftpFileSystemAccessor accessor = subsystem.getFileSystemAccessor();
+ ServerSession session = subsystem.getServerSession();
+ SeekableByteChannel channel;
try {
- channel = FileChannel.open(file, options, fileAttrs);
+ channel = accessor.openFile(session, subsystem, file, handle, options, fileAttrs);
} catch (UnsupportedOperationException e) {
- channel = FileChannel.open(file, options);
- sftpSubsystem.doSetAttributes(file, attrs);
+ channel = accessor.openFile(session, subsystem, file, handle, options, IoUtils.EMPTY_FILE_ATTRIBUTES);
+ subsystem.doSetAttributes(file, attrs);
}
this.fileChannel = channel;
}
- public final FileChannel getFileChannel() {
+ public final SeekableByteChannel getFileChannel() {
return fileChannel;
}
@@ -126,7 +132,7 @@ public class FileHandle extends Handle {
}
public int read(byte[] data, int doff, int length, long offset) throws IOException {
- FileChannel channel = getFileChannel();
+ SeekableByteChannel channel = getFileChannel();
channel.position(offset);
long size = channel.size();
@@ -149,7 +155,7 @@ public class FileHandle extends Handle {
}
public void append(byte[] data, int doff, int length) throws IOException {
- FileChannel channel = getFileChannel();
+ SeekableByteChannel channel = getFileChannel();
write(data, doff, length, channel.size());
}
@@ -158,7 +164,7 @@ public class FileHandle extends Handle {
}
public void write(byte[] data, int doff, int length, long offset) throws IOException {
- FileChannel channel = getFileChannel();
+ SeekableByteChannel channel = getFileChannel();
channel.position(offset);
channel.write(ByteBuffer.wrap(data, doff, length));
}
@@ -167,16 +173,18 @@ public class FileHandle extends Handle {
public void close() throws IOException {
super.close();
- FileChannel channel = getFileChannel();
+ SeekableByteChannel channel = getFileChannel();
if (channel.isOpen()) {
channel.close();
}
}
public void lock(long offset, long length, int mask) throws IOException {
- FileChannel channel = getFileChannel();
+ SeekableByteChannel channel = getFileChannel();
long size = (length == 0L) ? channel.size() - offset : length;
- FileLock lock = channel.tryLock(offset, size, false);
+ SftpFileSystemAccessor accessor = subsystem.getFileSystemAccessor();
+ ServerSession session = subsystem.getServerSession();
+ FileLock lock = accessor.tryLock(session, subsystem, getFile(), getFileHandle(), channel, offset, size, false);
if (lock == null) {
throw new SftpException(SftpConstants.SSH_FX_BYTE_RANGE_LOCK_REFUSED,
"Overlapping lock held by another program on range [" + offset + "-" + (offset + length));
@@ -188,7 +196,7 @@ public class FileHandle extends Handle {
}
public void unlock(long offset, long length) throws IOException {
- FileChannel channel = getFileChannel();
+ SeekableByteChannel channel = getFileChannel();
long size = (length == 0L) ? channel.size() - offset : length;
FileLock lock = null;
for (Iterator<FileLock> iterator = locks.iterator(); iterator.hasNext();) {
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/1448b79b/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/Handle.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/Handle.java b/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/Handle.java
index 4b9547d..ad166a9 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/Handle.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/Handle.java
@@ -23,21 +23,29 @@ import java.nio.file.Path;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
+import org.apache.sshd.common.util.ValidateUtils;
+
/**
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
public abstract class Handle implements java.nio.channels.Channel {
private final AtomicBoolean closed = new AtomicBoolean(false);
- private Path file;
+ private final Path file;
+ private final String handle;
- protected Handle(Path file) {
- this.file = file;
+ protected Handle(Path file, String handle) {
+ this.file = Objects.requireNonNull(file, "No local file path");
+ this.handle = ValidateUtils.checkNotNullAndNotEmpty(handle, "No assigned handle for %s", file);
}
public Path getFile() {
return file;
}
+ public String getFileHandle() {
+ return handle;
+ }
+
@Override
public boolean isOpen() {
return !closed.get();
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/1448b79b/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpFileSystemAccessor.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpFileSystemAccessor.java b/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpFileSystemAccessor.java
new file mode 100644
index 0000000..8d4499e
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpFileSystemAccessor.java
@@ -0,0 +1,133 @@
+/*
+ * 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.server.subsystem.sftp;
+
+import java.io.IOException;
+import java.io.StreamCorruptedException;
+import java.nio.channels.Channel;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
+import java.nio.channels.SeekableByteChannel;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.nio.file.attribute.FileAttribute;
+import java.util.Set;
+
+import org.apache.sshd.server.session.ServerSession;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface SftpFileSystemAccessor {
+ SftpFileSystemAccessor DEFAULT = new SftpFileSystemAccessor() {
+ @Override
+ public String toString() {
+ return SftpFileSystemAccessor.class.getSimpleName() + "[DEFAULT]";
+ }
+ };
+
+ /**
+ * Called whenever a new file is opened
+ *
+ * @param session The {@link ServerSession} through which the request was received
+ * @param subsystem The SFTP subsystem instance that manages the session
+ * @param file The requested <U>local</U> file {@link Path}
+ * @param handle The assigned file handle through which the remote peer references this file.
+ * May be {@code null}/empty if the request is due to some internal functionality
+ * instead of due to peer requesting a handle to a file.
+ * @param options The requested {@link OpenOption}s
+ * @param attrs The requested {@link FileAttribute}s
+ * @return The opened {@link SeekableByteChannel}
+ * @throws IOException If failed to open
+ */
+ default SeekableByteChannel openFile(
+ ServerSession session, SftpEventListenerManager subsystem,
+ Path file, String handle, Set<? extends OpenOption> options, FileAttribute<?>... attrs)
+ throws IOException {
+ return FileChannel.open(file, options, attrs);
+ }
+
+ /**
+ * Called when locking a section of a file is requested
+ *
+ * @param session The {@link ServerSession} through which the request was received
+ * @param subsystem The SFTP subsystem instance that manages the session
+ * @param file The requested <U>local</U> file {@link Path}
+ * @param handle The assigned file handle through which the remote peer references this file
+ * @param channel The original {@link Channel} that was returned by {@link #openFile(ServerSession, SftpEventListenerManager, Path, String, Set, FileAttribute...)}
+ * @param position The position at which the locked region is to start - must be non-negative
+ * @param size The size of the locked region; must be non-negative, and the sum
+ * <tt>position</tt> + <tt>size</tt> must be non-negative
+ * @param shared {@code true} to request a shared lock, {@code false} to request an exclusive lock
+ * @return A lock object representing the newly-acquired lock, or {@code null}
+ * if the lock could not be acquired because another program holds an overlapping lock
+ * @throws IOException If failed to honor the request
+ * @see FileChannel#tryLock(long, long, boolean)
+ */
+ default FileLock tryLock(ServerSession session, SftpEventListenerManager subsystem,
+ Path file, String handle, Channel channel, long position, long size, boolean shared)
+ throws IOException {
+ if (!(channel instanceof FileChannel)) {
+ throw new StreamCorruptedException("Non file channel to lock: " + channel);
+ }
+
+ return ((FileChannel) channel).lock(position, size, shared);
+ }
+
+ /**
+ * Called when file meta-data re-synchronization is required
+ *
+ * @param session The {@link ServerSession} through which the request was received
+ * @param subsystem The SFTP subsystem instance that manages the session
+ * @param file The requested <U>local</U> file {@link Path}
+ * @param handle The assigned file handle through which the remote peer references this file
+ * @param channel The original {@link Channel} that was returned by {@link #openFile(ServerSession, SftpEventListenerManager, Path, String, Set, FileAttribute...)}
+ * @throws IOException If failed to execute the request
+ * @see FileChannel#force(boolean)
+ * @see <A HREF="https://github.com/openssh/openssh-portable/blob/master/PROTOCOL">OpenSSH - section 10</A>
+ */
+ default void syncFileData(ServerSession session, SftpEventListenerManager subsystem,
+ Path file, String handle, Channel channel)
+ throws IOException {
+ if (!(channel instanceof FileChannel)) {
+ throw new StreamCorruptedException("Non file channel to sync: " + channel);
+ }
+
+ ((FileChannel) channel).force(true);
+ }
+
+ /**
+ * Called when a new directory stream is requested
+ *
+ * @param session The {@link ServerSession} through which the request was received
+ * @param subsystem The SFTP subsystem instance that manages the session
+ * @param dir The requested <U>local</U> directory
+ * @param handle The assigned directory handle through which the remote peer references this directory
+ * @return The opened {@link DirectoryStream}
+ * @throws IOException If failed to open
+ */
+ default DirectoryStream<Path> openDirectory(
+ ServerSession session, SftpEventListenerManager subsystem, Path dir, String handle)
+ throws IOException {
+ return Files.newDirectoryStream(dir);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/1448b79b/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpFileSystemAccessorManager.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpFileSystemAccessorManager.java b/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpFileSystemAccessorManager.java
new file mode 100644
index 0000000..9619794
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpFileSystemAccessorManager.java
@@ -0,0 +1,28 @@
+/*
+ * 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.server.subsystem.sftp;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface SftpFileSystemAccessorManager {
+ SftpFileSystemAccessor getFileSystemAccessor();
+ void setFileSystemAccessor(SftpFileSystemAccessor accessor);
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/1448b79b/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java b/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java
index 9377172..dc1de62 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java
@@ -26,7 +26,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.io.StreamCorruptedException;
import java.nio.ByteBuffer;
-import java.nio.channels.FileChannel;
+import java.nio.channels.SeekableByteChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.AccessDeniedException;
import java.nio.file.CopyOption;
@@ -57,6 +57,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -280,6 +281,7 @@ public class SftpSubsystem
private final Collection<SftpEventListener> sftpEventListeners =
EventListenerUtils.synchronizedListenersSet();
private final SftpEventListener sftpEventListenerProxy;
+ private final SftpFileSystemAccessor fileSystemAccessor;
/**
* @param executorService The {@link ExecutorService} to be used by
@@ -290,9 +292,10 @@ public class SftpSubsystem
* service, which will be shutdown regardless
* @param policy The {@link UnsupportedAttributePolicy} to use if failed to access
* some local file attributes
+ * @param accessor The {@link SftpFileSystemAccessor} to use for opening files and directories
* @see ThreadUtils#newSingleThreadExecutor(String)
*/
- public SftpSubsystem(ExecutorService executorService, boolean shutdownOnExit, UnsupportedAttributePolicy policy) {
+ public SftpSubsystem(ExecutorService executorService, boolean shutdownOnExit, UnsupportedAttributePolicy policy, SftpFileSystemAccessor accessor) {
if (executorService == null) {
executors = ThreadUtils.newSingleThreadExecutor(getClass().getSimpleName());
shutdownExecutor = true; // we always close the ad-hoc executor service
@@ -301,7 +304,8 @@ public class SftpSubsystem
shutdownExecutor = shutdownOnExit;
}
- unsupportedAttributePolicy = ValidateUtils.checkNotNull(policy, "No policy provided");
+ unsupportedAttributePolicy = Objects.requireNonNull(policy, "No policy provided");
+ fileSystemAccessor = Objects.requireNonNull(accessor, "No accessor");
sftpEventListenerProxy = EventListenerUtils.proxyWrapper(SftpEventListener.class, getClass().getClassLoader(), sftpEventListeners);
}
@@ -313,6 +317,10 @@ public class SftpSubsystem
return unsupportedAttributePolicy;
}
+ public final SftpFileSystemAccessor getFileSystemAccessor() {
+ return fileSystemAccessor;
+ }
+
@Override
public SftpEventListener getSftpEventListenerProxy() {
return sftpEventListenerProxy;
@@ -689,8 +697,9 @@ public class SftpSubsystem
}
FileHandle fileHandle = validateHandle(handle, h, FileHandle.class);
- FileChannel channel = fileHandle.getFileChannel();
- channel.force(false);
+ SftpFileSystemAccessor accessor = getFileSystemAccessor();
+ ServerSession session = getServerSession();
+ accessor.syncFileData(session, this, fileHandle.getFile(), fileHandle.getFileHandle(), fileHandle.getFileChannel());
}
protected void doCheckFileHash(Buffer buffer, int id, String targetType) throws IOException {
@@ -794,7 +803,8 @@ public class SftpSubsystem
? new byte[Math.min((int) effectiveLength, IoUtils.DEFAULT_COPY_SIZE)]
: new byte[Math.min((int) effectiveLength, blockSize)];
ByteBuffer wb = ByteBuffer.wrap(digestBuf);
- try (FileChannel channel = FileChannel.open(file, IoUtils.EMPTY_OPEN_OPTIONS)) {
+ SftpFileSystemAccessor accessor = getFileSystemAccessor();
+ try (SeekableByteChannel channel = accessor.openFile(getServerSession(), this, file, "", Collections.emptySet())) {
channel.position(startOffset);
Digest digest = factory.create();
@@ -948,8 +958,8 @@ public class SftpSubsystem
ByteBuffer wb = ByteBuffer.wrap(digestBuf);
boolean hashMatches = false;
byte[] hashValue = null;
-
- try (FileChannel channel = FileChannel.open(path, StandardOpenOption.READ)) {
+ SftpFileSystemAccessor accessor = getFileSystemAccessor();
+ try (SeekableByteChannel channel = accessor.openFile(getServerSession(), this, path, null, EnumSet.of(StandardOpenOption.READ))) {
channel.position(startOffset);
/*
@@ -1878,7 +1888,7 @@ public class SftpSubsystem
throw new AccessDeniedException("Not readable: " + p);
} else {
String handle = generateFileHandle(p);
- DirectoryHandle dirHandle = new DirectoryHandle(p);
+ DirectoryHandle dirHandle = new DirectoryHandle(this, p, handle);
SftpEventListener listener = getSftpEventListenerProxy();
listener.open(getServerSession(), handle, dirHandle);
handles.put(handle, dirHandle);
@@ -2205,7 +2215,7 @@ public class SftpSubsystem
Path file = resolveFile(path);
String handle = generateFileHandle(file);
- FileHandle fileHandle = new FileHandle(this, file, pflags, access, attrs);
+ FileHandle fileHandle = new FileHandle(this, file, handle, pflags, access, attrs);
SftpEventListener listener = getSftpEventListenerProxy();
listener.open(getServerSession(), handle, fileHandle);
handles.put(handle, fileHandle);
@@ -2985,7 +2995,8 @@ public class SftpSubsystem
switch (attribute) {
case "size": {
long newSize = ((Number) value).longValue();
- try (FileChannel channel = FileChannel.open(file, StandardOpenOption.WRITE)) {
+ SftpFileSystemAccessor accessor = getFileSystemAccessor();
+ try (SeekableByteChannel channel = accessor.openFile(getServerSession(), this, file, null, EnumSet.of(StandardOpenOption.WRITE))) {
channel.truncate(newSize);
}
continue;
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/1448b79b/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystemFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystemFactory.java b/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystemFactory.java
index 3fe2721..f5bd228 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystemFactory.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystemFactory.java
@@ -19,12 +19,12 @@
package org.apache.sshd.server.subsystem.sftp;
+import java.util.Objects;
import java.util.concurrent.ExecutorService;
import org.apache.sshd.common.subsystem.sftp.SftpConstants;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.ObjectBuilder;
-import org.apache.sshd.common.util.ValidateUtils;
import org.apache.sshd.common.util.threads.ExecutorServiceConfigurer;
import org.apache.sshd.server.Command;
import org.apache.sshd.server.subsystem.SubsystemFactory;
@@ -32,7 +32,9 @@ import org.apache.sshd.server.subsystem.SubsystemFactory;
/**
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public class SftpSubsystemFactory extends AbstractSftpEventListenerManager implements SubsystemFactory, ExecutorServiceConfigurer, SftpEventListenerManager {
+public class SftpSubsystemFactory
+ extends AbstractSftpEventListenerManager
+ implements SubsystemFactory, ExecutorServiceConfigurer, SftpEventListenerManager, SftpFileSystemAccessorManager {
public static final String NAME = SftpConstants.SFTP_SUBSYSTEM_NAME;
public static final UnsupportedAttributePolicy DEFAULT_POLICY = UnsupportedAttributePolicy.Warn;
@@ -40,6 +42,7 @@ public class SftpSubsystemFactory extends AbstractSftpEventListenerManager imple
private ExecutorService executors;
private boolean shutdownExecutor;
private UnsupportedAttributePolicy policy = DEFAULT_POLICY;
+ private SftpFileSystemAccessor fileSystemAccessor = SftpFileSystemAccessor.DEFAULT;
public Builder() {
super();
@@ -56,7 +59,12 @@ public class SftpSubsystemFactory extends AbstractSftpEventListenerManager imple
}
public Builder withUnsupportedAttributePolicy(UnsupportedAttributePolicy p) {
- policy = ValidateUtils.checkNotNull(p, "No policy");
+ policy = Objects.requireNonNull(p, "No policy");
+ return this;
+ }
+
+ public Builder withFileSystemAccessor(SftpFileSystemAccessor accessor) {
+ fileSystemAccessor = Objects.requireNonNull(accessor, "No accessor");
return this;
}
@@ -66,6 +74,7 @@ public class SftpSubsystemFactory extends AbstractSftpEventListenerManager imple
factory.setExecutorService(executors);
factory.setShutdownOnExit(shutdownExecutor);
factory.setUnsupportedAttributePolicy(policy);
+ factory.setFileSystemAccessor(fileSystemAccessor);
GenericUtils.forEach(getRegisteredListeners(), factory::addSftpEventListener);
return factory;
}
@@ -74,6 +83,7 @@ public class SftpSubsystemFactory extends AbstractSftpEventListenerManager imple
private ExecutorService executors;
private boolean shutdownExecutor;
private UnsupportedAttributePolicy policy = DEFAULT_POLICY;
+ private SftpFileSystemAccessor fileSystemAccessor = SftpFileSystemAccessor.DEFAULT;
public SftpSubsystemFactory() {
super();
@@ -91,7 +101,7 @@ public class SftpSubsystemFactory extends AbstractSftpEventListenerManager imple
/**
* @param service The {@link ExecutorService} to be used by the {@link SftpSubsystem}
- * command when starting execution. If {@code null} then a single-threaded ad-hoc service is used.
+ * command when starting execution. If {@code null} then a single-threaded ad-hoc service is used.
*/
@Override
public void setExecutorService(ExecutorService service) {
@@ -105,7 +115,7 @@ public class SftpSubsystemFactory extends AbstractSftpEventListenerManager imple
/**
* @param shutdownOnExit If {@code true} the {@link ExecutorService#shutdownNow()}
- * will be called when subsystem terminates - unless it is the ad-hoc service, which
+ * will be called when subsystem terminates - unless it is the ad-hoc service, which
* will be shutdown regardless
*/
@Override
@@ -119,15 +129,26 @@ public class SftpSubsystemFactory extends AbstractSftpEventListenerManager imple
/**
* @param p The {@link UnsupportedAttributePolicy} to use if failed to access
- * some local file attributes - never {@code null}
+ * some local file attributes - never {@code null}
*/
public void setUnsupportedAttributePolicy(UnsupportedAttributePolicy p) {
- policy = ValidateUtils.checkNotNull(p, "No policy");
+ policy = Objects.requireNonNull(p, "No policy");
+ }
+
+ @Override
+ public SftpFileSystemAccessor getFileSystemAccessor() {
+ return fileSystemAccessor;
+ }
+
+ @Override
+ public void setFileSystemAccessor(SftpFileSystemAccessor accessor) {
+ fileSystemAccessor = Objects.requireNonNull(accessor, "No accessor");
}
@Override
public Command create() {
- SftpSubsystem subsystem = new SftpSubsystem(getExecutorService(), isShutdownOnExit(), getUnsupportedAttributePolicy());
+ SftpSubsystem subsystem =
+ new SftpSubsystem(getExecutorService(), isShutdownOnExit(), getUnsupportedAttributePolicy(), getFileSystemAccessor());
GenericUtils.forEach(getRegisteredListeners(), subsystem::addSftpEventListener);
return subsystem;
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/1448b79b/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpTest.java b/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpTest.java
index d2633d3..007206a 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpTest.java
@@ -29,14 +29,18 @@ import java.net.SocketTimeoutException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
+import java.nio.channels.SeekableByteChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.CopyOption;
+import java.nio.file.DirectoryStream;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.LinkOption;
+import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
+import java.nio.file.attribute.FileAttribute;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -45,6 +49,7 @@ import java.util.EnumSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import java.util.Vector;
@@ -58,7 +63,9 @@ import com.jcraft.jsch.JSch;
import org.apache.sshd.client.SshClient;
import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.client.subsystem.sftp.SftpClient.Attributes;
import org.apache.sshd.client.subsystem.sftp.SftpClient.CloseableHandle;
+import org.apache.sshd.client.subsystem.sftp.SftpClient.DirEntry;
import org.apache.sshd.client.subsystem.sftp.SftpClient.OpenMode;
import org.apache.sshd.client.subsystem.sftp.extensions.BuiltinSftpClientExtensions;
import org.apache.sshd.client.subsystem.sftp.extensions.SftpClientExtension;
@@ -92,6 +99,8 @@ import org.apache.sshd.server.subsystem.sftp.DirectoryHandle;
import org.apache.sshd.server.subsystem.sftp.FileHandle;
import org.apache.sshd.server.subsystem.sftp.Handle;
import org.apache.sshd.server.subsystem.sftp.SftpEventListener;
+import org.apache.sshd.server.subsystem.sftp.SftpEventListenerManager;
+import org.apache.sshd.server.subsystem.sftp.SftpFileSystemAccessor;
import org.apache.sshd.server.subsystem.sftp.SftpSubsystem;
import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
import org.apache.sshd.util.test.SimpleUserInfo;
@@ -516,6 +525,91 @@ public class SftpTest extends AbstractSftpClientTestSupport {
}
@Test
+ public void testSftpFileSystemAccessor() throws Exception {
+ List<NamedFactory<Command>> factories = sshd.getSubsystemFactories();
+ assertEquals("Mismatched subsystem factories count", 1, GenericUtils.size(factories));
+
+ NamedFactory<Command> f = factories.get(0);
+ assertObjectInstanceOf("Not an SFTP subsystem factory", SftpSubsystemFactory.class, f);
+
+ SftpSubsystemFactory factory = (SftpSubsystemFactory) f;
+ SftpFileSystemAccessor accessor = factory.getFileSystemAccessor();
+ try {
+ AtomicReference<Path> fileHolder = new AtomicReference<>();
+ AtomicReference<Path> dirHolder = new AtomicReference<>();
+ factory.setFileSystemAccessor(new SftpFileSystemAccessor() {
+ @Override
+ public SeekableByteChannel openFile(ServerSession session, SftpEventListenerManager subsystem, Path file,
+ String handle, Set<? extends OpenOption> options, FileAttribute<?>... attrs)
+ throws IOException {
+ fileHolder.set(file);
+ return SftpFileSystemAccessor.super.openFile(session, subsystem, file, handle, options, attrs);
+ }
+
+ @Override
+ public DirectoryStream<Path> openDirectory(
+ ServerSession session, SftpEventListenerManager subsystem, Path dir, String handle) throws IOException {
+ dirHolder.set(dir);
+ return SftpFileSystemAccessor.super.openDirectory(session, subsystem, dir, handle);
+ }
+
+ @Override
+ public String toString() {
+ return SftpFileSystemAccessor.class.getSimpleName() + "[" + getCurrentTestName() + "]";
+ }
+ });
+
+ Path targetPath = detectTargetFolder();
+ Path parentPath = targetPath.getParent();
+ Path localFile = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+ Files.createDirectories(localFile.getParent());
+ byte[] expected = (getClass().getName() + "#" + getCurrentTestName() + "[" + localFile + "]").getBytes(StandardCharsets.UTF_8);
+ Files.write(localFile, expected, StandardOpenOption.CREATE);
+ 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 = session.createSftpClient()) {
+ byte[] actual = new byte[expected.length];
+ try (InputStream stream = sftp.read(Utils.resolveRelativeRemotePath(parentPath, localFile), OpenMode.Read)) {
+ IoUtils.readFully(stream, actual);
+ }
+
+ Path remoteFile = fileHolder.getAndSet(null);
+ assertNotNull("No remote file holder value", remoteFile);
+ assertEquals("Mismatched opened local files", localFile.toFile(), remoteFile.toFile());
+ assertArrayEquals("Mismatched retrieved file contents", expected, actual);
+
+ Path localParent = localFile.getParent();
+ String localName = Objects.toString(localFile.getFileName(), null);
+ try (CloseableHandle handle = sftp.openDir(Utils.resolveRelativeRemotePath(parentPath, localParent))) {
+ List<DirEntry> entries = sftp.readDir(handle);
+ Path remoteParent = dirHolder.getAndSet(null);
+ assertNotNull("No remote folder holder value", remoteParent);
+ assertEquals("Mismatched opened folder", localParent.toFile(), remoteParent.toFile());
+ assertFalse("No dir entries", GenericUtils.isEmpty(entries));
+
+ for (DirEntry de : entries) {
+ Attributes attrs = de.getAttributes();
+ if (!attrs.isRegularFile()) {
+ continue;
+ }
+
+ if (localName.equals(de.getFilename())) {
+ return;
+ }
+ }
+
+ fail("Cannot find listing of " + localName);
+ }
+ }
+ }
+ } finally {
+ factory.setFileSystemAccessor(accessor); // restore original
+ }
+ }
+
+ @Test
@SuppressWarnings({"checkstyle:anoninnerlength", "checkstyle:methodlength"})
public void testClient() throws Exception {
List<NamedFactory<Command>> factories = sshd.getSubsystemFactories();
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/1448b79b/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpVersionsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpVersionsTest.java b/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpVersionsTest.java
index 58d2eed..ce911c1 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpVersionsTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpVersionsTest.java
@@ -196,7 +196,7 @@ public class SftpVersionsTest extends AbstractSftpClientTestSupport {
SftpSubsystemFactory factory = new SftpSubsystemFactory() {
@Override
public Command create() {
- SftpSubsystem subsystem = new SftpSubsystem(getExecutorService(), isShutdownOnExit(), getUnsupportedAttributePolicy()) {
+ SftpSubsystem subsystem = new SftpSubsystem(getExecutorService(), isShutdownOnExit(), getUnsupportedAttributePolicy(), getFileSystemAccessor()) {
@Override
protected Map<String, Object> resolveFileAttributes(Path file, int flags, LinkOption... options) throws IOException {
Map<String, Object> attrs = super.resolveFileAttributes(file, flags, options);
@@ -314,7 +314,7 @@ public class SftpVersionsTest extends AbstractSftpClientTestSupport {
SftpSubsystemFactory factory = new SftpSubsystemFactory() {
@Override
public Command create() {
- SftpSubsystem subsystem = new SftpSubsystem(getExecutorService(), isShutdownOnExit(), getUnsupportedAttributePolicy()) {
+ SftpSubsystem subsystem = new SftpSubsystem(getExecutorService(), isShutdownOnExit(), getUnsupportedAttributePolicy(), getFileSystemAccessor()) {
@Override
protected Map<String, Object> resolveFileAttributes(Path file, int flags, LinkOption... options) throws IOException {
Map<String, Object> attrs = super.resolveFileAttributes(file, flags, options);
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/1448b79b/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/SpaceAvailableExtensionImplTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/SpaceAvailableExtensionImplTest.java b/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/SpaceAvailableExtensionImplTest.java
index 9fb6775..3470adb 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/SpaceAvailableExtensionImplTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/SpaceAvailableExtensionImplTest.java
@@ -71,7 +71,7 @@ public class SpaceAvailableExtensionImplTest extends AbstractSftpClientTestSuppo
sshd.setSubsystemFactories(Collections.singletonList(new SftpSubsystemFactory() {
@Override
public Command create() {
- return new SftpSubsystem(getExecutorService(), isShutdownOnExit(), getUnsupportedAttributePolicy()) {
+ return new SftpSubsystem(getExecutorService(), isShutdownOnExit(), getUnsupportedAttributePolicy(), getFileSystemAccessor()) {
@Override
protected SpaceAvailableExtensionInfo doSpaceAvailable(int id, String path) throws IOException {
if (!queryPath.equals(path)) {
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/1448b79b/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/openssh/helpers/OpenSSHExtensionsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/openssh/helpers/OpenSSHExtensionsTest.java b/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/openssh/helpers/OpenSSHExtensionsTest.java
index 8557375..8799b14 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/openssh/helpers/OpenSSHExtensionsTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/openssh/helpers/OpenSSHExtensionsTest.java
@@ -124,7 +124,7 @@ public class OpenSSHExtensionsTest extends AbstractSftpClientTestSupport {
sshd.setSubsystemFactories(Collections.singletonList(new SftpSubsystemFactory() {
@Override
public Command create() {
- return new SftpSubsystem(getExecutorService(), isShutdownOnExit(), getUnsupportedAttributePolicy()) {
+ return new SftpSubsystem(getExecutorService(), isShutdownOnExit(), getUnsupportedAttributePolicy(), getFileSystemAccessor()) {
@Override
protected List<OpenSSHExtension> resolveOpenSSHExtensions(ServerSession session) {
List<OpenSSHExtension> original = super.resolveOpenSSHExtensions(session);