Repository: mina-sshd Updated Branches: refs/heads/master 8136bf615 -> 65d7f65d1
[SSHD-787] Moved some more hardcoded logic to ScpFileOpener to allow user override of default behavior Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/65d7f65d Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/65d7f65d Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/65d7f65d Branch: refs/heads/master Commit: 65d7f65d1bb1b1b4545386c24ccbc33129066737 Parents: 8136bf6 Author: Goldstein Lyor <[email protected]> Authored: Wed Dec 20 12:07:29 2017 +0200 Committer: Goldstein Lyor <[email protected]> Committed: Wed Dec 20 12:08:05 2017 +0200 ---------------------------------------------------------------------- .../apache/sshd/common/scp/ScpFileOpener.java | 131 +++++++++++++++++++ .../org/apache/sshd/common/scp/ScpHelper.java | 92 +------------ 2 files changed, 138 insertions(+), 85 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/65d7f65d/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpFileOpener.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpFileOpener.java b/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpFileOpener.java index 887d649..3ef8c28 100644 --- a/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpFileOpener.java +++ b/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpFileOpener.java @@ -19,13 +19,24 @@ package org.apache.sshd.common.scp; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.file.AccessDeniedException; +import java.nio.file.Files; +import java.nio.file.LinkOption; import java.nio.file.OpenOption; import java.nio.file.Path; +import java.nio.file.attribute.BasicFileAttributeView; +import java.nio.file.attribute.FileTime; +import java.nio.file.attribute.PosixFilePermission; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import org.apache.sshd.common.SshException; import org.apache.sshd.common.session.Session; +import org.apache.sshd.common.util.io.IoUtils; /** * Plug-in mechanism for users to intervene in the SCP process - e.g., @@ -36,6 +47,115 @@ import org.apache.sshd.common.session.Session; */ public interface ScpFileOpener { /** + * Invoked when receiving a new file to via a directory command + * + * @param localPath The target local path + * @param name The target file name + * @param boolean preserve Whether requested to preserve the permissions and timestamp + * @param permissions The requested file permissions + * @param time The requested {@link ScpTimestamp} - may be {@code null} if nothing to update + * @return The actual target file path + * @throws IOException If failed to resolve the file path + * @see #updateFileProperties(Path, Set, ScpTimestamp) updateFileProperties + */ + default Path resolveIncomingFilePath( + Path localPath, String name, boolean preserve, Set<PosixFilePermission> permissions, ScpTimestamp time) + throws IOException { + LinkOption[] options = IoUtils.getLinkOptions(true); + Boolean status = IoUtils.checkFileExists(localPath, options); + if (status == null) { + throw new AccessDeniedException("Receive directory existence status cannot be determined: " + localPath); + } + + Path file = null; + if (status && Files.isDirectory(localPath, options)) { + String localName = name.replace('/', File.separatorChar); + file = localPath.resolve(localName); + } else if (!status) { + Path parent = localPath.getParent(); + + status = IoUtils.checkFileExists(parent, options); + if (status == null) { + throw new AccessDeniedException("Receive directory parent (" + parent + ") existence status cannot be determined for " + localPath); + } + + if (status && Files.isDirectory(parent, options)) { + file = localPath; + } + } + + if (file == null) { + throw new IOException("Cannot write to " + localPath); + } + + status = IoUtils.checkFileExists(file, options); + if (status == null) { + throw new AccessDeniedException("Receive directory file existence status cannot be determined: " + file); + } + + if (!(status && Files.isDirectory(file, options))) { + Files.createDirectory(file); + } + + if (preserve) { + updateFileProperties(file, permissions, time); + } + + return file; + } + + /** + * Invoked when a request to receive something is processed + * + * @param path The local target {@link Path} of the request + * @param recursive Whether the request is recursive + * @param shouldBeDir Whether target path is expected to be a directory + * @param preserve Whether target path is expected to preserve attributes (permissions, times) + * @return The effective target path - default=same as input + * @throws IOException If failed to resolve target location + */ + default Path resolveIncomingReceiveLocation( + Path path, boolean recursive, boolean shouldBeDir, boolean preserve) + throws IOException { + if (!shouldBeDir) { + return path; + } + LinkOption[] options = IoUtils.getLinkOptions(true); + Boolean status = IoUtils.checkFileExists(path, options); + if (status == null) { + throw new SshException("Target directory " + path + " is most like inaccessible"); + } + if (!status) { + throw new SshException("Target directory " + path + " does not exist"); + } + if (!Files.isDirectory(path, options)) { + throw new SshException("Target directory " + path + " is not a directory"); + } + + return path; + } + + /** + * Called when there is a candidate file/folder for sending + * + * @param localPath The original file/folder {@link Path} for sending + * @param options The {@link LinkOption}-s to use for validation + * @return The effective outgoing file path (default=same as input) + * @throws IOException If failed to resolve + */ + default Path resolveOutgoingFilePath(Path localPath, LinkOption... options) throws IOException { + Boolean status = IoUtils.checkFileExists(localPath, options); + if (status == null) { + throw new AccessDeniedException("Send file existence status cannot be determined: " + localPath); + } + if (!status) { + throw new IOException(localPath + ": no such file or directory"); + } + + return localPath; + } + + /** * Create an input stream to read from a file * * @param session The {@link Session} requesting the access @@ -56,4 +176,15 @@ public interface ScpFileOpener { * @throws IOException If failed to open the file */ OutputStream openWrite(Session session, Path file, OpenOption... options) throws IOException; + + static void updateFileProperties(Path file, Set<PosixFilePermission> perms, ScpTimestamp time) throws IOException { + IoUtils.setPermissions(file, perms); + + if (time != null) { + BasicFileAttributeView view = Files.getFileAttributeView(file, BasicFileAttributeView.class); + FileTime lastModified = FileTime.from(time.getLastModifiedTime(), TimeUnit.MILLISECONDS); + FileTime lastAccess = FileTime.from(time.getLastAccessTime(), TimeUnit.MILLISECONDS); + view.setTimes(lastModified, lastAccess, null); + } + } } http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/65d7f65d/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpHelper.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpHelper.java b/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpHelper.java index 0e75e83..6075234 100644 --- a/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpHelper.java +++ b/sshd-core/src/main/java/org/apache/sshd/common/scp/ScpHelper.java @@ -26,7 +26,6 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.StreamCorruptedException; import java.nio.charset.StandardCharsets; -import java.nio.file.AccessDeniedException; import java.nio.file.DirectoryStream; import java.nio.file.FileSystem; import java.nio.file.Files; @@ -44,7 +43,6 @@ import java.util.Objects; import java.util.Set; import java.util.concurrent.TimeUnit; -import org.apache.sshd.common.SshException; import org.apache.sshd.common.file.util.MockPath; import org.apache.sshd.common.scp.ScpTransferEventListener.FileOperation; import org.apache.sshd.common.scp.helpers.DefaultScpFileOpener; @@ -167,21 +165,8 @@ public class ScpHelper extends AbstractLoggingBean implements SessionHolder<Sess } public void receive(Path local, boolean recursive, boolean shouldBeDir, boolean preserve, int bufferSize) throws IOException { - Path path = Objects.requireNonNull(local, "No local path").normalize().toAbsolutePath(); - if (shouldBeDir) { - LinkOption[] options = IoUtils.getLinkOptions(true); - Boolean status = IoUtils.checkFileExists(path, options); - if (status == null) { - throw new SshException("Target directory " + path + " is most like inaccessible"); - } - if (!status) { - throw new SshException("Target directory " + path + " does not exist"); - } - if (!Files.isDirectory(path, options)) { - throw new SshException("Target directory " + path + " is not a directory"); - } - } - + Path localPath = Objects.requireNonNull(local, "No local path").normalize().toAbsolutePath(); + Path path = opener.resolveIncomingReceiveLocation(localPath, recursive, shouldBeDir, preserve); receive((line, isDir, time) -> { if (recursive && isDir) { receiveDir(line, path, time, preserve, bufferSize); @@ -255,50 +240,11 @@ public class ScpHelper extends AbstractLoggingBean implements SessionHolder<Sess Set<PosixFilePermission> perms = parseOctalPermissions(header.substring(1, 5)); int length = Integer.parseInt(header.substring(6, header.indexOf(' ', 6))); String name = header.substring(header.indexOf(' ', 6) + 1); - if (length != 0) { - throw new IOException("Expected 0 length for directory but got " + length); - } - - LinkOption[] options = IoUtils.getLinkOptions(true); - Boolean status = IoUtils.checkFileExists(path, options); - if (status == null) { - throw new AccessDeniedException("Receive directory existence status cannot be determined: " + path); - } - - Path file = null; - if (status && Files.isDirectory(path, options)) { - String localName = name.replace('/', File.separatorChar); - file = path.resolve(localName); - } else if (!status) { - Path parent = path.getParent(); - - status = IoUtils.checkFileExists(parent, options); - if (status == null) { - throw new AccessDeniedException("Receive directory parent (" + parent + ") existence status cannot be determined for " + path); - } - - if (status && Files.isDirectory(parent, options)) { - file = path; - } - } - - if (file == null) { - throw new IOException("Cannot write to " + path); + throw new IOException("Expected 0 length for directory=" + name + " but got " + length); } - status = IoUtils.checkFileExists(file, options); - if (status == null) { - throw new AccessDeniedException("Receive directory file existence status cannot be determined: " + file); - } - - if (!(status && Files.isDirectory(file, options))) { - Files.createDirectory(file); - } - - if (preserve) { - updateFileProperties(file, perms, time); - } + Path file = opener.resolveIncomingFilePath(path, name, preserve, perms, time); ack(); @@ -353,7 +299,7 @@ public class ScpHelper extends AbstractLoggingBean implements SessionHolder<Sess } Set<PosixFilePermission> perms = parseOctalPermissions(header.substring(1, 5)); - final long length = Long.parseLong(header.substring(6, header.indexOf(' ', 6))); + long length = Long.parseLong(header.substring(6, header.indexOf(' ', 6))); String name = header.substring(header.indexOf(' ', 6) + 1); if (length < 0L) { // TODO consider throwing an exception... log.warn("receiveStream({})[{}] bad length in header: {}", this, resolver, header); @@ -405,23 +351,6 @@ public class ScpHelper extends AbstractLoggingBean implements SessionHolder<Sess validateAckReplyCode("receiveStream", resolver, replyCode, false); } - protected void updateFileProperties(Path file, Set<PosixFilePermission> perms, ScpTimestamp time) throws IOException { - if (log.isTraceEnabled()) { - log.trace("updateFileProperties({}) {} permissions={}, time={}", this, file, perms, time); - } - IoUtils.setPermissions(file, perms); - - if (time != null) { - BasicFileAttributeView view = Files.getFileAttributeView(file, BasicFileAttributeView.class); - FileTime lastModified = FileTime.from(time.getLastModifiedTime(), TimeUnit.MILLISECONDS); - FileTime lastAccess = FileTime.from(time.getLastAccessTime(), TimeUnit.MILLISECONDS); - if (log.isTraceEnabled()) { - log.trace("updateFileProperties({}) {} last-modified={}, last-access={}", this, file, lastModified, lastAccess); - } - view.setTimes(lastModified, lastAccess, null); - } - } - public String readLine() throws IOException { return readLine(false); } @@ -506,15 +435,8 @@ public class ScpHelper extends AbstractLoggingBean implements SessionHolder<Sess } protected void send(Path local, boolean recursive, boolean preserve, int bufferSize, LinkOption... options) throws IOException { - Path file = Objects.requireNonNull(local, "No local path").normalize().toAbsolutePath(); - Boolean status = IoUtils.checkFileExists(file, options); - if (status == null) { - throw new AccessDeniedException("Send file existence status cannot be determined: " + file); - } - if (!status) { - throw new IOException(file + ": no such file or directory"); - } - + Path localPath = Objects.requireNonNull(local, "No local path").normalize().toAbsolutePath(); + Path file = opener.resolveOutgoingFilePath(localPath, options); if (Files.isRegularFile(file, options)) { sendFile(file, preserve, bufferSize); } else if (Files.isDirectory(file, options)) {
