Repository: mina-sshd Updated Branches: refs/heads/master 3d5a8e70f -> ba91a674d
[SSHD-787] Delegated all SCP java.nio file related actions to ScpFileOpener Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/ba91a674 Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/ba91a674 Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/ba91a674 Branch: refs/heads/master Commit: ba91a674d4dd76c4516bfb421145630288e4aad7 Parents: 3d5a8e7 Author: Goldstein Lyor <[email protected]> Authored: Sun Dec 24 11:25:26 2017 +0200 Committer: Lyor Goldstein <[email protected]> Committed: Tue Dec 26 20:30:17 2017 +0200 ---------------------------------------------------------------------- .../apache/sshd/common/scp/ScpFileOpener.java | 92 +++++++++++++++++++- .../org/apache/sshd/common/scp/ScpHelper.java | 44 +++------- .../sshd/common/util/io/DirectoryScanner.java | 8 +- 3 files changed, 108 insertions(+), 36 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ba91a674/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 5b78fda..3e1f408 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 @@ -24,18 +24,27 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.file.AccessDeniedException; +import java.nio.file.DirectoryStream; +import java.nio.file.FileSystem; import java.nio.file.Files; +import java.nio.file.InvalidPathException; 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.BasicFileAttributes; import java.nio.file.attribute.FileTime; import java.nio.file.attribute.PosixFilePermission; +import java.util.Arrays; +import java.util.Collections; 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.GenericUtils; +import org.apache.sshd.common.util.SelectorUtils; +import org.apache.sshd.common.util.io.DirectoryScanner; import org.apache.sshd.common.util.io.IoUtils; /** @@ -54,7 +63,7 @@ public interface ScpFileOpener { * @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 + * @return The actual target file path for the incoming file/directory * @throws IOException If failed to resolve the file path * @see #updateFileProperties(Path, Set, ScpTimestamp) updateFileProperties */ @@ -105,6 +114,87 @@ public interface ScpFileOpener { } /** + * Invoked when required to send a pattern of files + * + * @param basedir The base directory - may be {@code null}/empty to indicate CWD + * @param pattern The required pattern + * @return The matching <U>relative paths</U> of the children to send + */ + default Iterable<String> getMatchingFilesToSend(String basedir, String pattern) { + String[] matches = new DirectoryScanner(basedir, pattern).scan(); + if (GenericUtils.isEmpty(matches)) { + return Collections.emptyList(); + } + + return Arrays.asList(matches); + } + + /** + * Invoked on a local path in order to decide whether it should be sent + * as a file or as a directory + * + * @param path The local {@link Path} + * @param options The {@link LinkOption}-s + * @return Whether to send the file as a regular one - <B>Note:</B> if {@code false} + * then the {@link #sendAsDirectory(Path, LinkOption...)} is consulted. + * @throws IOException If failed to decide + */ + default boolean sendAsRegularFile(Path path, LinkOption... options) throws IOException { + return Files.isRegularFile(path, options); + } + + /** + * Invoked on a local path in order to decide whether it should be sent + * as a file or as a directory + * + * @param path The local {@link Path} + * @param options The {@link LinkOption}-s + * @return Whether to send the file as a directory - <B>Note:</B> if {@code true} + * then {@link #getLocalFolderChildren(Path)} is consulted + * @throws IOException If failed to decide + */ + default boolean sendAsDirectory(Path path, LinkOption... options) throws IOException { + return Files.isDirectory(path, options); + } + + /** + * Invoked when required to send all children of a local directory + * + * @param path The local folder {@link Path}{ + * @return The {@link DirectoryStream} of children to send - <B>Note:</B> for each child + * the decision whether to send it as a file or a directory will be reached by consulting + * the respective {@link #sendAsRegularFile(Path, LinkOption...) sendAsRegularFile} and + * {@link #sendAsDirectory(Path, LinkOption...) sendAsDirectory} methods + * @throws IOException If failed to provide the children stream + * @see #sendAsDirectory(Path, LinkOption...) sendAsDirectory + */ + default DirectoryStream<Path> getLocalFolderChildren(Path path) throws IOException { + return Files.newDirectoryStream(path); + } + + default BasicFileAttributes getLocalBasicFileAttributes(Path path, LinkOption... options) throws IOException { + return Files.getFileAttributeView(path, BasicFileAttributeView.class, options).readAttributes(); + } + + default Set<PosixFilePermission> getLocalFilePermissions(Path path, LinkOption... options) throws IOException { + return IoUtils.getPermissions(path, options); + } + + /** + * @param fileSystem The <U>local</U> {@link FileSystem} on which local file should reside + * @param commandPath The command path using the <U>local</U> file separator + * @return The resolved absolute and normalized local {@link Path} + * @throws IOException If failed to resolve the path + * @throws InvalidPathException If invalid local path value + */ + default Path resolveLocalPath(FileSystem fileSystem, String commandPath) throws IOException, InvalidPathException { + String path = SelectorUtils.translateToLocalFileSystemPath(commandPath, File.separatorChar, fileSystem); + Path lcl = fileSystem.getPath(path); + Path abs = lcl.isAbsolute() ? lcl : lcl.toAbsolutePath(); + return abs.normalize(); + } + + /** * Invoked when a request to receive something is processed * * @param path The local target {@link Path} of the request http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ba91a674/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 e8183c8..ba8eb11 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 @@ -28,12 +28,10 @@ import java.io.StreamCorruptedException; import java.nio.charset.StandardCharsets; import java.nio.file.DirectoryStream; import java.nio.file.FileSystem; -import java.nio.file.Files; import java.nio.file.InvalidPathException; 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.BasicFileAttributes; import java.nio.file.attribute.FileTime; import java.nio.file.attribute.PosixFilePermission; @@ -49,8 +47,6 @@ import org.apache.sshd.common.scp.helpers.DefaultScpFileOpener; import org.apache.sshd.common.session.Session; import org.apache.sshd.common.session.SessionHolder; import org.apache.sshd.common.util.GenericUtils; -import org.apache.sshd.common.util.SelectorUtils; -import org.apache.sshd.common.util.io.DirectoryScanner; import org.apache.sshd.common.util.io.IoUtils; import org.apache.sshd.common.util.io.LimitInputStream; import org.apache.sshd.common.util.logging.AbstractLoggingBean; @@ -390,12 +386,12 @@ public class ScpHelper extends AbstractLoggingBean implements SessionHolder<Sess pattern = pattern.substring(lastSep + 1); } - String[] included = new DirectoryScanner(basedir, pattern).scan(); + Iterable<String> included = opener.getMatchingFilesToSend(basedir, pattern); for (String path : included) { Path file = resolveLocalPath(basedir, path); - if (Files.isRegularFile(file, options)) { + if (opener.sendAsRegularFile(file, options)) { sendFile(file, preserve, bufferSize); - } else if (Files.isDirectory(file, options)) { + } else if (opener.sendAsDirectory(file, options)) { if (!recursive) { if (log.isDebugEnabled()) { log.debug("send({}) {}: not a regular file", this, path); @@ -433,9 +429,9 @@ public class ScpHelper extends AbstractLoggingBean implements SessionHolder<Sess protected void send(Path local, boolean recursive, boolean preserve, int bufferSize, LinkOption... options) throws IOException { Path localPath = Objects.requireNonNull(local, "No local path").normalize().toAbsolutePath(); Path file = opener.resolveOutgoingFilePath(localPath, options); - if (Files.isRegularFile(file, options)) { + if (opener.sendAsRegularFile(file, options)) { sendFile(file, preserve, bufferSize); - } else if (Files.isDirectory(file, options)) { + } else if (opener.sendAsDirectory(file, options)) { if (!recursive) { throw new IOException(file + " not a regular file"); } else { @@ -456,15 +452,12 @@ public class ScpHelper extends AbstractLoggingBean implements SessionHolder<Sess /** * @param commandPath The command path using the <U>local</U> file separator - * @return The resolved absolute and normalized local path {@link Path} + * @return The resolved absolute and normalized local {@link Path} * @throws IOException If failed to resolve the path * @throws InvalidPathException If invalid local path value */ public Path resolveLocalPath(String commandPath) throws IOException, InvalidPathException { - String path = SelectorUtils.translateToLocalFileSystemPath(commandPath, File.separatorChar, fileSystem); - Path lcl = fileSystem.getPath(path); - Path abs = lcl.isAbsolute() ? lcl : lcl.toAbsolutePath(); - Path p = abs.normalize(); + Path p = opener.resolveLocalPath(fileSystem, commandPath); if (log.isTraceEnabled()) { log.trace("resolveLocalPath({}) {}: {}", this, commandPath, p); } @@ -593,8 +586,9 @@ public class ScpHelper extends AbstractLoggingBean implements SessionHolder<Sess this, path, preserve, bufferSize); } - BasicFileAttributes basic = Files.getFileAttributeView(path, BasicFileAttributeView.class).readAttributes(); + LinkOption[] options = IoUtils.getLinkOptions(true); if (preserve) { + BasicFileAttributes basic = opener.getLocalBasicFileAttributes(path, options); FileTime lastModified = basic.lastModifiedTime(); FileTime lastAccess = basic.lastAccessTime(); String cmd = "T" + lastModified.to(TimeUnit.SECONDS) + " " @@ -618,8 +612,7 @@ public class ScpHelper extends AbstractLoggingBean implements SessionHolder<Sess validateAckReplyCode(cmd, path, readyCode, false); } - LinkOption[] options = IoUtils.getLinkOptions(true); - Set<PosixFilePermission> perms = IoUtils.getPermissions(path, options); + Set<PosixFilePermission> perms = opener.getLocalFilePermissions(path, options); String octalPerms = (preserve || GenericUtils.isEmpty(perms)) ? DEFAULT_DIR_OCTAL_PERMISSIONS : getOctalPermissions(perms); String cmd = "D" + octalPerms + " " + "0" + " " + Objects.toString(path.getFileName(), null); if (log.isDebugEnabled()) { @@ -636,14 +629,14 @@ public class ScpHelper extends AbstractLoggingBean implements SessionHolder<Sess } validateAckReplyCode(cmd, path, readyCode, false); - try (DirectoryStream<Path> children = Files.newDirectoryStream(path)) { + try (DirectoryStream<Path> children = opener.getLocalFolderChildren(path)) { listener.startFolderEvent(FileOperation.SEND, path, perms); try { for (Path child : children) { - if (Files.isRegularFile(child, options)) { + if (opener.sendAsRegularFile(child, options)) { sendFile(child, preserve, bufferSize); - } else if (Files.isDirectory(child, options)) { + } else if (opener.sendAsDirectory(child, options)) { sendDir(child, preserve, bufferSize); } } @@ -668,11 +661,6 @@ public class ScpHelper extends AbstractLoggingBean implements SessionHolder<Sess validateAckReplyCode("E", path, readyCode, false); } - public static String getOctalPermissions(Path path, LinkOption... options) throws IOException { - Collection<PosixFilePermission> perms = IoUtils.getPermissions(path, options); - return getOctalPermissions(perms); - } - public static String getOctalPermissions(Collection<PosixFilePermission> perms) { int pf = 0; @@ -712,12 +700,6 @@ public class ScpHelper extends AbstractLoggingBean implements SessionHolder<Sess return String.format("%04o", pf); } - public static Set<PosixFilePermission> setOctalPermissions(Path path, String str) throws IOException { - Set<PosixFilePermission> perms = parseOctalPermissions(str); - IoUtils.setPermissions(path, perms); - return perms; - } - public static Set<PosixFilePermission> parseOctalPermissions(String str) { int perms = Integer.parseInt(str, 8); Set<PosixFilePermission> p = EnumSet.noneOf(PosixFilePermission.class); http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ba91a674/sshd-core/src/main/java/org/apache/sshd/common/util/io/DirectoryScanner.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/DirectoryScanner.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/DirectoryScanner.java index 6cd8ad9..8b4e9ba 100644 --- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/DirectoryScanner.java +++ b/sshd-core/src/main/java/org/apache/sshd/common/util/io/DirectoryScanner.java @@ -22,6 +22,7 @@ import java.io.File; import java.util.ArrayList; import java.util.List; +import org.apache.sshd.common.util.GenericUtils; import org.apache.sshd.common.util.SelectorUtils; /** @@ -250,8 +251,8 @@ public class DirectoryScanner { */ protected void scandir(File dir, String vpath) { String[] newfiles = dir.list(); - if (newfiles == null) { - newfiles = new String[0]; + if (GenericUtils.isEmpty(newfiles)) { + newfiles = GenericUtils.EMPTY_STRING_ARRAY; } for (String newfile : newfiles) { @@ -282,8 +283,7 @@ public class DirectoryScanner { */ public String[] getIncludedFiles() { String[] files = new String[filesIncluded.size()]; - filesIncluded.toArray(files); - return files; + return filesIncluded.toArray(files); } /**
