http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/DefaultGroupPrincipal.java ---------------------------------------------------------------------- diff --git a/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/DefaultGroupPrincipal.java b/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/DefaultGroupPrincipal.java new file mode 100644 index 0000000..acf3118 --- /dev/null +++ b/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/DefaultGroupPrincipal.java @@ -0,0 +1,32 @@ +/* + * 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.nio.file.attribute.GroupPrincipal; + +/** + * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> + */ +public class DefaultGroupPrincipal extends PrincipalBase implements GroupPrincipal { + + public DefaultGroupPrincipal(String name) { + super(name); + } + +}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/DefaultUserPrincipal.java ---------------------------------------------------------------------- diff --git a/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/DefaultUserPrincipal.java b/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/DefaultUserPrincipal.java new file mode 100644 index 0000000..d71d772 --- /dev/null +++ b/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/DefaultUserPrincipal.java @@ -0,0 +1,32 @@ +/* + * 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.nio.file.attribute.UserPrincipal; + +/** + * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> + */ +public class DefaultUserPrincipal extends PrincipalBase implements UserPrincipal { + + public DefaultUserPrincipal(String name) { + super(name); + } + +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/DirectoryHandle.java ---------------------------------------------------------------------- diff --git a/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/DirectoryHandle.java b/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/DirectoryHandle.java new file mode 100644 index 0000000..0ae60cf --- /dev/null +++ b/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/DirectoryHandle.java @@ -0,0 +1,109 @@ +/* + * 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.nio.file.DirectoryStream; +import java.nio.file.Path; +import java.util.Iterator; + +import org.apache.sshd.server.session.ServerSession; + +/** + * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> + */ +public class DirectoryHandle extends Handle implements Iterator<Path> { + + private boolean done; + private boolean sendDotDot = true; + private boolean sendDot = true; + // the directory should be read once at "open directory" + private DirectoryStream<Path> ds; + private Iterator<Path> fileList; + + public DirectoryHandle(SftpSubsystem subsystem, Path dir, String handle) throws IOException { + super(dir, handle); + signalHandleOpening(subsystem); + + SftpFileSystemAccessor accessor = subsystem.getFileSystemAccessor(); + ServerSession session = subsystem.getServerSession(); + ds = accessor.openDirectory(session, subsystem, dir, handle); + + Path parent = dir.getParent(); + if (parent == null) { + sendDotDot = false; // if no parent then no need to send ".." + } + fileList = ds.iterator(); + + try { + signalHandleOpen(subsystem); + } catch (IOException e) { + close(); + throw e; + } + } + + public boolean isDone() { + return done; + } + + public void markDone() { + this.done = true; + // allow the garbage collector to do the job + this.fileList = null; + } + + public boolean isSendDot() { + return sendDot; + } + + public void markDotSent() { + sendDot = false; + } + + public boolean isSendDotDot() { + return sendDotDot; + } + + public void markDotDotSent() { + sendDotDot = false; + } + + @Override + public boolean hasNext() { + return fileList.hasNext(); + } + + @Override + public Path next() { + return fileList.next(); + } + + @Override + public void remove() { + throw new UnsupportedOperationException("Not allowed to remove " + toString()); + } + + @Override + public void close() throws IOException { + super.close(); + markDone(); // just making sure + ds.close(); + } +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/FileHandle.java ---------------------------------------------------------------------- diff --git a/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/FileHandle.java b/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/FileHandle.java new file mode 100644 index 0000000..b499524 --- /dev/null +++ b/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/FileHandle.java @@ -0,0 +1,270 @@ +/* + * 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.nio.ByteBuffer; +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; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +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; + +/** + * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> + */ +public class FileHandle extends Handle { + private final int access; + private final SeekableByteChannel fileChannel; + private final List<FileLock> locks = new ArrayList<>(); + private final SftpSubsystem subsystem; + private final Set<StandardOpenOption> openOptions; + private final Collection<FileAttribute<?>> fileAttributes; + + 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; + this.openOptions = Collections.unmodifiableSet(getOpenOptions(flags, access)); + this.fileAttributes = Collections.unmodifiableCollection(toFileAttributes(attrs)); + signalHandleOpening(subsystem); + + FileAttribute<?>[] fileAttrs = GenericUtils.isEmpty(fileAttributes) + ? IoUtils.EMPTY_FILE_ATTRIBUTES + : fileAttributes.toArray(new FileAttribute<?>[fileAttributes.size()]); + SftpFileSystemAccessor accessor = subsystem.getFileSystemAccessor(); + ServerSession session = subsystem.getServerSession(); + SeekableByteChannel channel; + try { + channel = accessor.openFile(session, subsystem, file, handle, openOptions, fileAttrs); + } catch (UnsupportedOperationException e) { + channel = accessor.openFile(session, subsystem, file, handle, openOptions, IoUtils.EMPTY_FILE_ATTRIBUTES); + subsystem.doSetAttributes(file, attrs); + } + this.fileChannel = channel; + + try { + signalHandleOpen(subsystem); + } catch (IOException e) { + close(); + throw e; + } + } + + public final Set<StandardOpenOption> getOpenOptions() { + return openOptions; + } + + public final Collection<FileAttribute<?>> getFileAttributes() { + return fileAttributes; + } + + public final SeekableByteChannel getFileChannel() { + return fileChannel; + } + + public int getAccessMask() { + return access; + } + + public boolean isOpenAppend() { + return SftpConstants.ACE4_APPEND_DATA == (getAccessMask() & SftpConstants.ACE4_APPEND_DATA); + } + + public int read(byte[] data, long offset) throws IOException { + return read(data, 0, data.length, offset); + } + + public int read(byte[] data, int doff, int length, long offset) throws IOException { + SeekableByteChannel channel = getFileChannel(); + channel = channel.position(offset); + return channel.read(ByteBuffer.wrap(data, doff, length)); + } + + public void append(byte[] data) throws IOException { + append(data, 0, data.length); + } + + public void append(byte[] data, int doff, int length) throws IOException { + SeekableByteChannel channel = getFileChannel(); + write(data, doff, length, channel.size()); + } + + public void write(byte[] data, long offset) throws IOException { + write(data, 0, data.length, offset); + } + + public void write(byte[] data, int doff, int length, long offset) throws IOException { + SeekableByteChannel channel = getFileChannel(); + channel = channel.position(offset); + channel.write(ByteBuffer.wrap(data, doff, length)); + } + + @Override + public void close() throws IOException { + super.close(); + + SeekableByteChannel channel = getFileChannel(); + if (channel.isOpen()) { + channel.close(); + } + } + + public void lock(long offset, long length, int mask) throws IOException { + SeekableByteChannel channel = getFileChannel(); + long size = (length == 0L) ? channel.size() - offset : length; + 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)); + } + + synchronized (locks) { + locks.add(lock); + } + } + + public void unlock(long offset, long length) throws IOException { + SeekableByteChannel channel = getFileChannel(); + long size = (length == 0L) ? channel.size() - offset : length; + FileLock lock = null; + for (Iterator<FileLock> iterator = locks.iterator(); iterator.hasNext();) { + FileLock l = iterator.next(); + if ((l.position() == offset) && (l.size() == size)) { + iterator.remove(); + lock = l; + break; + } + } + if (lock == null) { + throw new SftpException(SftpConstants.SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK, + "No matching lock found on range [" + offset + "-" + (offset + length)); + } + + lock.release(); + } + + public static Collection<FileAttribute<?>> toFileAttributes(Map<String, Object> attrs) { + if (GenericUtils.isEmpty(attrs)) { + return Collections.emptyList(); + } + + Collection<FileAttribute<?>> attributes = null; + // Cannot use forEach because the referenced attributes variable is not effectively final + for (Map.Entry<String, Object> attr : attrs.entrySet()) { + FileAttribute<?> fileAttr = toFileAttribute(attr.getKey(), attr.getValue()); + if (fileAttr == null) { + continue; + } + if (attributes == null) { + attributes = new LinkedList<>(); + } + attributes.add(fileAttr); + } + + return (attributes == null) ? Collections.emptyList() : attributes; + } + + public static FileAttribute<?> toFileAttribute(String key, Object val) { + // Some ignored attributes sent by the SFTP client + if ("isOther".equals(key)) { + if ((Boolean) val) { + throw new IllegalArgumentException("Not allowed to use " + key + "=" + val); + } + return null; + } else if ("isRegular".equals(key)) { + if (!(Boolean) val) { + throw new IllegalArgumentException("Not allowed to use " + key + "=" + val); + } + return null; + } + + return new FileAttribute<Object>() { + private final String s = key + "=" + val; + + @Override + public String name() { + return key; + } + + @Override + public Object value() { + return val; + } + + @Override + public String toString() { + return s; + } + }; + } + + public static Set<StandardOpenOption> getOpenOptions(int flags, int access) { + Set<StandardOpenOption> options = EnumSet.noneOf(StandardOpenOption.class); + if (((access & SftpConstants.ACE4_READ_DATA) != 0) || ((access & SftpConstants.ACE4_READ_ATTRIBUTES) != 0)) { + options.add(StandardOpenOption.READ); + } + if (((access & SftpConstants.ACE4_WRITE_DATA) != 0) || ((access & SftpConstants.ACE4_WRITE_ATTRIBUTES) != 0)) { + options.add(StandardOpenOption.WRITE); + } + + int accessDisposition = flags & SftpConstants.SSH_FXF_ACCESS_DISPOSITION; + switch (accessDisposition) { + case SftpConstants.SSH_FXF_CREATE_NEW: + options.add(StandardOpenOption.CREATE_NEW); + break; + case SftpConstants.SSH_FXF_CREATE_TRUNCATE: + options.add(StandardOpenOption.CREATE); + options.add(StandardOpenOption.TRUNCATE_EXISTING); + break; + case SftpConstants.SSH_FXF_OPEN_EXISTING: + break; + case SftpConstants.SSH_FXF_OPEN_OR_CREATE: + options.add(StandardOpenOption.CREATE); + break; + case SftpConstants.SSH_FXF_TRUNCATE_EXISTING: + options.add(StandardOpenOption.TRUNCATE_EXISTING); + break; + default: // ignored + } + if ((flags & SftpConstants.SSH_FXF_APPEND_DATA) != 0) { + options.add(StandardOpenOption.APPEND); + } + + return options; + } +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/Handle.java ---------------------------------------------------------------------- diff --git a/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/Handle.java b/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/Handle.java new file mode 100644 index 0000000..a860eec --- /dev/null +++ b/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/Handle.java @@ -0,0 +1,79 @@ +/* + * 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.nio.file.Path; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.sshd.common.util.ValidateUtils; +import org.apache.sshd.server.session.ServerSession; + +/** + * @author <a href="mailto:d...@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 final Path file; + private final String handle; + + 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); + } + + protected void signalHandleOpening(SftpSubsystem subsystem) throws IOException { + SftpEventListener listener = subsystem.getSftpEventListenerProxy(); + ServerSession session = subsystem.getServerSession(); + listener.opening(session, handle, this); + } + + protected void signalHandleOpen(SftpSubsystem subsystem) throws IOException { + SftpEventListener listener = subsystem.getSftpEventListenerProxy(); + ServerSession session = subsystem.getServerSession(); + listener.open(session, handle, this); + } + + public Path getFile() { + return file; + } + + public String getFileHandle() { + return handle; + } + + @Override + public boolean isOpen() { + return !closed.get(); + } + + @Override + public void close() throws IOException { + if (!closed.getAndSet(true)) { + //noinspection UnnecessaryReturnStatement + return; // debug breakpoint + } + } + + @Override + public String toString() { + return Objects.toString(getFile()); + } +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/InvalidHandleException.java ---------------------------------------------------------------------- diff --git a/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/InvalidHandleException.java b/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/InvalidHandleException.java new file mode 100644 index 0000000..af7b147 --- /dev/null +++ b/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/InvalidHandleException.java @@ -0,0 +1,32 @@ +/* + * 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; + +/** + * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> + */ +public class InvalidHandleException extends IOException { + private static final long serialVersionUID = -1686077114375131889L; + + public InvalidHandleException(String handle, Handle h, Class<? extends Handle> expected) { + super(handle + "[" + h + "] is not a " + expected.getSimpleName()); + } +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/PrincipalBase.java ---------------------------------------------------------------------- diff --git a/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/PrincipalBase.java b/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/PrincipalBase.java new file mode 100644 index 0000000..310c3b4 --- /dev/null +++ b/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/PrincipalBase.java @@ -0,0 +1,65 @@ +/* + * 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.security.Principal; +import java.util.Objects; + +/** + * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> + */ +public class PrincipalBase implements Principal { + + private final String name; + + public PrincipalBase(String name) { + if (name == null) { + throw new IllegalArgumentException("name is null"); + } + this.name = name; + } + + @Override + public final String getName() { + return name; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if ((o == null) || (getClass() != o.getClass())) { + return false; + } + + Principal that = (Principal) o; + return Objects.equals(getName(), that.getName()); + } + + @Override + public int hashCode() { + return Objects.hashCode(getName()); + } + + @Override + public String toString() { + return getName(); + } +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpErrorStatusDataHandler.java ---------------------------------------------------------------------- diff --git a/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpErrorStatusDataHandler.java b/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpErrorStatusDataHandler.java new file mode 100644 index 0000000..1498ba2 --- /dev/null +++ b/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpErrorStatusDataHandler.java @@ -0,0 +1,83 @@ +/* + * 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 org.apache.sshd.common.subsystem.sftp.SftpHelper; + +/** + * Invoked in order to format failed commands messages + * + * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> + */ +public interface SftpErrorStatusDataHandler { + SftpErrorStatusDataHandler DEFAULT = new SftpErrorStatusDataHandler() { + @Override + public String toString() { + return SftpErrorStatusDataHandler.class.getSimpleName() + "[DEFAULT]"; + } + }; + + /** + * @param sftpSubsystem The SFTP subsystem instance + * @param id The command identifier + * @param e Thrown exception + * @param cmd The command that was attempted + * @param args The relevant command arguments - <B>Note:</B> provided only for + * <U>logging</U> purposes and subject to type and/or order change at any version + * @return The relevant sub-status to send as failure indication for the failed command + * @see SftpHelper#resolveSubstatus(Throwable) + */ + default int resolveSubStatus(SftpSubsystemEnvironment sftpSubsystem, int id, Throwable e, int cmd, Object... args) { + return SftpHelper.resolveSubstatus(e); + } + + /** + * @param sftpSubsystem The SFTP subsystem instance + * @param id The command identifier + * @param e Thrown exception + * @param subStatus The sub-status code obtained from invocation of + * {@link #resolveSubStatus(SftpSubsystemEnvironment, int, Throwable, int, Object...) resolveSubStatus} + * @param cmd The command that was attempted + * @param args The relevant command arguments - <B>Note:</B> provided only for + * <U>logging</U> purposes and subject to type and/or order change at any version + * @return The human readable text message that explains the failure reason + * @see SftpHelper#resolveStatusMessage(int) + */ + default String resolveErrorMessage( + SftpSubsystemEnvironment sftpSubsystem, int id, Throwable e, int subStatus, int cmd, Object... args) { + return SftpHelper.resolveStatusMessage(subStatus); + } + + /** + * @param sftpSubsystem The SFTP subsystem instance + * @param id The command identifier + * @param e Thrown exception + * @param subStatus The sub-status code obtained from invocation of + * {@link #resolveSubStatus(SftpSubsystemEnvironment, int, Throwable, int, Object...) resolveSubStatus} + * @param cmd The command that was attempted + * @param args The relevant command arguments - <B>Note:</B> provided only for + * <U>logging</U> purposes and subject to type and/or order change at any version + * @return The error message language tag - recommend returning empty string + */ + default String resolveErrorLanguage( + SftpSubsystemEnvironment sftpSubsystem, int id, Throwable e, int subStatus, int cmd, Object... args) { + return ""; + } +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpEventListener.java ---------------------------------------------------------------------- diff --git a/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpEventListener.java b/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpEventListener.java new file mode 100644 index 0000000..c518af3 --- /dev/null +++ b/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpEventListener.java @@ -0,0 +1,396 @@ +/* + * 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.nio.file.CopyOption; +import java.nio.file.Path; +import java.util.Collection; +import java.util.Map; + +import org.apache.sshd.common.util.SshdEventListener; +import org.apache.sshd.server.session.ServerSession; + +/** + * Can be used register for SFTP events. <B>Note:</B> it does not expose + * the entire set of available SFTP commands and responses (e.g., no reports + * for initialization, extensions, parameters re-negotiation, etc...); + * + * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> + */ +public interface SftpEventListener extends SshdEventListener { + /** + * Called when the SFTP protocol has been initialized + * + * @param session The {@link ServerSession} through which the request was handled + * @param version The negotiated SFTP version + */ + default void initialized(ServerSession session, int version) { + // ignored + } + + /** + * Called when subsystem is destroyed since it was closed + * + * @param session The associated {@link ServerSession} + */ + default void destroying(ServerSession session) { + // ignored + } + + /** + * Specified file / directory is being opened + * + * @param session The {@link ServerSession} through which the request was handled + * @param remoteHandle The (opaque) assigned handle for the file / directory + * @param localHandle The associated file / directory {@link Handle} + * @throws IOException If failed to handle the call + */ + default void opening(ServerSession session, String remoteHandle, Handle localHandle) + throws IOException { + // ignored + } + + /** + * Specified file / directory has been opened + * + * @param session The {@link ServerSession} through which the request was handled + * @param remoteHandle The (opaque) assigned handle for the file / directory + * @param localHandle The associated file / directory {@link Handle} + * @throws IOException If failed to handle the call + */ + default void open(ServerSession session, String remoteHandle, Handle localHandle) + throws IOException { + // ignored + } + + /** + * Result of reading entries from a directory - <B>Note:</B> it may be a + * <U>partial</U> result if the directory contains more entries than can + * be accommodated in the response + * + * @param session The {@link ServerSession} through which the request was handled + * @param remoteHandle The (opaque) assigned handle for the directory + * @param localHandle The associated {@link DirectoryHandle} + * @param entries A {@link Map} of the listed entries - key = short name, + * value = {@link Path} of the sub-entry + * @throws IOException If failed to handle the call + */ + default void read(ServerSession session, String remoteHandle, DirectoryHandle localHandle, Map<String, Path> entries) + throws IOException { + // ignored + } + + /** + * Preparing to read from a file + * + * @param session The {@link ServerSession} through which the request was handled + * @param remoteHandle The (opaque) assigned handle for the file + * @param localHandle The associated {@link FileHandle} + * @param offset Offset in file from which to read + * @param data Buffer holding the read data + * @param dataOffset Offset of read data in buffer + * @param dataLen Requested read length + * @throws IOException If failed to handle the call + */ + default void reading(ServerSession session, String remoteHandle, FileHandle localHandle, + long offset, byte[] data, int dataOffset, int dataLen) throws IOException { + // ignored + } + + /** + * Result of reading from a file + * + * @param session The {@link ServerSession} through which the request was handled + * @param remoteHandle The (opaque) assigned handle for the file + * @param localHandle The associated {@link FileHandle} + * @param offset Offset in file from which to read + * @param data Buffer holding the read data + * @param dataOffset Offset of read data in buffer + * @param dataLen Requested read length + * @param readLen Actual read length - negative if thrown exception provided + * @param thrown Non-{@code null} if read failed due to this exception + * @throws IOException If failed to handle the call + */ + default void read(ServerSession session, String remoteHandle, FileHandle localHandle, + long offset, byte[] data, int dataOffset, int dataLen, int readLen, Throwable thrown) + throws IOException { + // ignored + } + + /** + * Preparing to write to file + * + * @param session The {@link ServerSession} through which the request was handled + * @param remoteHandle The (opaque) assigned handle for the file + * @param localHandle The associated {@link FileHandle} + * @param offset Offset in file to which to write + * @param data Buffer holding the written data + * @param dataOffset Offset of write data in buffer + * @param dataLen Requested write length + * @throws IOException If failed to handle the call + */ + default void writing(ServerSession session, String remoteHandle, FileHandle localHandle, + long offset, byte[] data, int dataOffset, int dataLen) + throws IOException { + // ignored + } + + /** + * Finished to writing to file + * + * @param session The {@link ServerSession} through which the request was handled + * @param remoteHandle The (opaque) assigned handle for the file + * @param localHandle The associated {@link FileHandle} + * @param offset Offset in file to which to write + * @param data Buffer holding the written data + * @param dataOffset Offset of write data in buffer + * @param dataLen Requested write length + * @param thrown The reason for failing to write - {@code null} if successful + * @throws IOException If failed to handle the call + */ + default void written(ServerSession session, String remoteHandle, FileHandle localHandle, + long offset, byte[] data, int dataOffset, int dataLen, Throwable thrown) + throws IOException { + // ignored + } + + /** + * Called <U>prior</U> to blocking a file section + * + * @param session The {@link ServerSession} through which the request was handled + * @param remoteHandle The (opaque) assigned handle for the file + * @param localHandle The associated {@link FileHandle} + * @param offset Offset in file for locking + * @param length Section size for locking + * @param mask Lock mask flags - see {@code SSH_FXP_BLOCK} message + * @throws IOException If failed to handle the call + * @see #blocked(ServerSession, String, FileHandle, long, long, int, Throwable) + */ + default void blocking(ServerSession session, String remoteHandle, FileHandle localHandle, long offset, long length, int mask) + throws IOException { + // ignored + } + + /** + * Called <U>after</U> blocking a file section + * + * @param session The {@link ServerSession} through which the request was handled + * @param remoteHandle The (opaque) assigned handle for the file + * @param localHandle The associated {@link FileHandle} + * @param offset Offset in file for locking + * @param length Section size for locking + * @param mask Lock mask flags - see {@code SSH_FXP_BLOCK} message + * @param thrown If not-{@code null} then the reason for the failure to execute + * @throws IOException If failed to handle the call + */ + default void blocked(ServerSession session, String remoteHandle, FileHandle localHandle, long offset, long length, int mask, Throwable thrown) + throws IOException { + // ignored + } + + /** + * Called <U>prior</U> to un-blocking a file section + * + * @param session The {@link ServerSession} through which the request was handled + * @param remoteHandle The (opaque) assigned handle for the file + * @param localHandle The associated {@link FileHandle} + * @param offset Offset in file for un-locking + * @param length Section size for un-locking + * @throws IOException If failed to handle the call + */ + default void unblocking(ServerSession session, String remoteHandle, FileHandle localHandle, long offset, long length) + throws IOException { + // ignored + } + + /** + * Called <U>prior</U> to un-blocking a file section + * + * @param session The {@link ServerSession} through which the request was handled + * @param remoteHandle The (opaque) assigned handle for the file + * @param localHandle The associated {@link FileHandle} + * @param offset Offset in file for un-locking + * @param length Section size for un-locking + * @param thrown If not-{@code null} then the reason for the failure to execute + * @throws IOException If failed to handle the call + */ + default void unblocked(ServerSession session, String remoteHandle, FileHandle localHandle, long offset, long length, Throwable thrown) + throws IOException { + // ignored + } + + /** + * Specified file / directory has been closed + * + * @param session The {@link ServerSession} through which the request was handled + * @param remoteHandle The (opaque) assigned handle for the file / directory + * @param localHandle The associated file / directory {@link Handle} + */ + default void close(ServerSession session, String remoteHandle, Handle localHandle) { + // ignored + } + + /** + * Called <U>prior</U> to creating a directory + * + * @param session The {@link ServerSession} through which the request was handled + * @param path Directory {@link Path} to be created + * @param attrs Requested associated attributes to set + * @throws IOException If failed to handle the call + * @see #created(ServerSession, Path, Map, Throwable) + */ + default void creating(ServerSession session, Path path, Map<String, ?> attrs) + throws IOException { + // ignored + } + + /** + * Called <U>after</U> creating a directory + * + * @param session The {@link ServerSession} through which the request was handled + * @param path Directory {@link Path} to be created + * @param attrs Requested associated attributes to set + * @param thrown If not-{@code null} then the reason for the failure to execute + * @throws IOException If failed to handle the call + */ + default void created(ServerSession session, Path path, Map<String, ?> attrs, Throwable thrown) + throws IOException { + // ignored + } + + /** + * Called <U>prior</U> to renaming a file / directory + * + * @param session The {@link ServerSession} through which the request was handled + * @param srcPath The source {@link Path} + * @param dstPath The target {@link Path} + * @param opts The resolved renaming options + * @throws IOException If failed to handle the call + * @see #moved(ServerSession, Path, Path, Collection, Throwable) + */ + default void moving(ServerSession session, Path srcPath, Path dstPath, Collection<CopyOption> opts) + throws IOException { + // ignored + } + + /** + * Called <U>after</U> renaming a file / directory + * + * @param session The {@link ServerSession} through which the request was handled + * @param srcPath The source {@link Path} + * @param dstPath The target {@link Path} + * @param opts The resolved renaming options + * @param thrown If not-{@code null} then the reason for the failure to execute + * @throws IOException If failed to handle the call + */ + default void moved(ServerSession session, Path srcPath, Path dstPath, Collection<CopyOption> opts, Throwable thrown) + throws IOException { + // ignored + } + + /** + * Called <U>prior</U> to removing a file / directory + * + * @param session The {@link ServerSession} through which the request was handled + * @param path The {@link Path} about to be removed + * @throws IOException If failed to handle the call + * @see #removed(ServerSession, Path, Throwable) + */ + default void removing(ServerSession session, Path path) throws IOException { + // ignored + } + + /** + * Called <U>after</U> a file / directory has been removed + * + * @param session The {@link ServerSession} through which the request was handled + * @param path The {@link Path} to be removed + * @param thrown If not-{@code null} then the reason for the failure to execute + * @throws IOException If failed to handle the call + */ + default void removed(ServerSession session, Path path, Throwable thrown) throws IOException { + // ignored + } + + /** + * Called <U>prior</U> to creating a link + * + * @param session The {@link ServerSession} through which the request was handled + * @param source The source {@link Path} + * @param target The target {@link Path} + * @param symLink {@code true} = symbolic link + * @throws IOException If failed to handle the call + * @see #linked(ServerSession, Path, Path, boolean, Throwable) + */ + default void linking(ServerSession session, Path source, Path target, boolean symLink) + throws IOException { + // ignored + } + + /** + * Called <U>after</U> creating a link + * + * @param session The {@link ServerSession} through which the request was handled + * @param source The source {@link Path} + * @param target The target {@link Path} + * @param symLink {@code true} = symbolic link + * @param thrown If not-{@code null} then the reason for the failure to execute + * @throws IOException If failed to handle the call + */ + default void linked(ServerSession session, Path source, Path target, boolean symLink, Throwable thrown) + throws IOException { + // ignored + } + + /** + * Called <U>prior</U> to modifying the attributes of a file / directory + * + * @param session The {@link ServerSession} through which the request was handled + * @param path The file / directory {@link Path} to be modified + * @param attrs The attributes {@link Map} - names and values depend on the + * O/S, view, type, etc... + * @throws IOException If failed to handle the call + * @see #modifiedAttributes(ServerSession, Path, Map, Throwable) + */ + default void modifyingAttributes(ServerSession session, Path path, Map<String, ?> attrs) + throws IOException { + // ignored + } + + /** + * Called <U>after</U> modifying the attributes of a file / directory + * + * @param session The {@link ServerSession} through which the request was handled + * @param path The file / directory {@link Path} to be modified + * @param attrs The attributes {@link Map} - names and values depend on the + * O/S, view, type, etc... + * @param thrown If not-{@code null} then the reason for the failure to execute + * @throws IOException If failed to handle the call + */ + default void modifiedAttributes(ServerSession session, Path path, Map<String, ?> attrs, Throwable thrown) + throws IOException { + // ignored + } + + static <L extends SftpEventListener> L validateListener(L listener) { + return SshdEventListener.validateListener(listener, SftpEventListener.class.getSimpleName()); + } +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpEventListenerManager.java ---------------------------------------------------------------------- diff --git a/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpEventListenerManager.java b/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpEventListenerManager.java new file mode 100644 index 0000000..3f91033 --- /dev/null +++ b/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpEventListenerManager.java @@ -0,0 +1,48 @@ +/* + * 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:d...@mina.apache.org">Apache MINA SSHD Project</a> + */ +public interface SftpEventListenerManager { + /** + * @return An instance representing <U>all</U> the currently + * registered listeners. Any method invocation is <U>replicated</U> + * to the actually registered listeners + */ + SftpEventListener getSftpEventListenerProxy(); + + /** + * Register a listener instance + * + * @param listener The {@link SftpEventListener} instance to add - never {@code null} + * @return {@code true} if listener is a previously un-registered one + */ + boolean addSftpEventListener(SftpEventListener listener); + + /** + * Remove a listener instance + * + * @param listener The {@link SftpEventListener} instance to remove - never {@code null} + * @return {@code true} if listener is a (removed) registered one + */ + boolean removeSftpEventListener(SftpEventListener listener); +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpFileSystemAccessor.java ---------------------------------------------------------------------- diff --git a/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpFileSystemAccessor.java b/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpFileSystemAccessor.java new file mode 100644 index 0000000..86aa670 --- /dev/null +++ b/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpFileSystemAccessor.java @@ -0,0 +1,155 @@ +/* + * 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.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.sshd.common.util.GenericUtils; +import org.apache.sshd.common.util.io.FileInfoExtractor; +import org.apache.sshd.server.session.ServerSession; + +/** + * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> + */ +public interface SftpFileSystemAccessor { + List<String> DEFAULT_UNIX_VIEW = Collections.singletonList("unix:*"); + + /** + * A {@link Map} of {@link FileInfoExtractor}s to be used to complete + * attributes that are deemed important enough to warrant an extra + * effort if not accessible via the file system attributes views + */ + Map<String, FileInfoExtractor<?>> FILEATTRS_RESOLVERS = + GenericUtils.<String, FileInfoExtractor<?>>mapBuilder(String.CASE_INSENSITIVE_ORDER) + .put("isRegularFile", FileInfoExtractor.ISREG) + .put("isDirectory", FileInfoExtractor.ISDIR) + .put("isSymbolicLink", FileInfoExtractor.ISSYMLINK) + .put("permissions", FileInfoExtractor.PERMISSIONS) + .put("size", FileInfoExtractor.SIZE) + .put("lastModifiedTime", FileInfoExtractor.LASTMODIFIED) + .immutable(); + + 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/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpFileSystemAccessorManager.java ---------------------------------------------------------------------- diff --git a/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpFileSystemAccessorManager.java b/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpFileSystemAccessorManager.java new file mode 100644 index 0000000..616f9ce --- /dev/null +++ b/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpFileSystemAccessorManager.java @@ -0,0 +1,29 @@ +/* + * 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:d...@mina.apache.org">Apache MINA SSHD Project</a> + */ +public interface SftpFileSystemAccessorManager { + SftpFileSystemAccessor getFileSystemAccessor(); + + void setFileSystemAccessor(SftpFileSystemAccessor accessor); +}