You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by gn...@apache.org on 2015/02/23 16:30:13 UTC
[12/15] mina-sshd git commit: [SSHD-378] Switch to nio FileSystem api
for commands (scp and sftp subsystem)
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/345604ba/sshd-core/src/main/java/org/apache/sshd/common/file/root/RootedFileSystemProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/file/root/RootedFileSystemProvider.java b/sshd-core/src/main/java/org/apache/sshd/common/file/root/RootedFileSystemProvider.java
new file mode 100644
index 0000000..38e6181
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/file/root/RootedFileSystemProvider.java
@@ -0,0 +1,310 @@
+/*
+ * 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.common.file.root;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.channels.AsynchronousFileChannel;
+import java.nio.channels.FileChannel;
+import java.nio.channels.SeekableByteChannel;
+import java.nio.file.AccessMode;
+import java.nio.file.CopyOption;
+import java.nio.file.DirectoryStream;
+import java.nio.file.FileStore;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystemAlreadyExistsException;
+import java.nio.file.FileSystemNotFoundException;
+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.ProviderMismatchException;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.FileAttribute;
+import java.nio.file.attribute.FileAttributeView;
+import java.nio.file.spi.FileSystemProvider;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+
+/**
+ * File system provider which provides a rooted file system.
+ * The file system only gives access to files under the root directory.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class RootedFileSystemProvider extends FileSystemProvider {
+
+ final Map<Path, RootedFileSystem> fileSystems = new HashMap<>();
+
+ @Override
+ public String getScheme() {
+ return "root";
+ }
+
+ @Override
+ public FileSystem newFileSystem(URI uri, Map<String, ?> env) throws IOException {
+ Path path = uriToPath(uri);
+ synchronized (fileSystems)
+ {
+ Path localPath2 = null;
+ if (ensureDirectory(path))
+ {
+ localPath2 = path.toRealPath();
+ if (this.fileSystems.containsKey(localPath2)) {
+ throw new FileSystemAlreadyExistsException();
+ }
+ }
+ RootedFileSystem rootedFs = new RootedFileSystem(this, path, env);
+ this.fileSystems.put(localPath2, rootedFs);
+ return rootedFs;
+ }
+ }
+
+ @Override
+ public FileSystem getFileSystem(URI uri) {
+ synchronized (fileSystems) {
+ RootedFileSystem fileSystem = null;
+ try {
+ fileSystem = fileSystems.get(uriToPath(uri).toRealPath());
+ } catch (IOException ignore) {
+ }
+ if (fileSystem == null) {
+ throw new FileSystemNotFoundException();
+ }
+ return fileSystem;
+ }
+ }
+
+ @Override
+ public FileSystem newFileSystem(Path path, Map<String, ?> env) throws IOException {
+ ensureDirectory(path);
+ return new RootedFileSystem(this, path, env);
+ }
+
+ protected Path uriToPath(URI uri) {
+ String scheme = uri.getScheme();
+ if ((scheme == null) || (!scheme.equalsIgnoreCase(getScheme()))) {
+ throw new IllegalArgumentException("URI scheme is not '" + getScheme() + "'");
+ }
+ try {
+ String root = uri.getRawSchemeSpecificPart();
+ int i = root.indexOf("!/");
+ if (i != -1) {
+ root = root.substring(0, i);
+ }
+ return Paths.get(new URI(root)).toAbsolutePath();
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException(e.getMessage(), e);
+ }
+ }
+
+ private boolean ensureDirectory(Path path) {
+ if (!Files.isDirectory(path)) {
+ throw new UnsupportedOperationException();
+ }
+ return true;
+ }
+
+ @Override
+ public Path getPath(URI uri) {
+ String str = uri.getSchemeSpecificPart();
+ int i = str.indexOf("!/");
+ if (i == -1) {
+ throw new IllegalArgumentException("URI: " + uri + " does not contain path info ex. root:file://foo/bar!/");
+ }
+ return getFileSystem(uri).getPath(str.substring(i + 1));
+ }
+
+ @Override
+ public InputStream newInputStream(Path path, OpenOption... options) throws IOException {
+ Path r = unroot(path);
+ return provider(r).newInputStream(r, options);
+ }
+
+ @Override
+ public OutputStream newOutputStream(Path path, OpenOption... options) throws IOException {
+ Path r = unroot(path);
+ return provider(r).newOutputStream(r, options);
+ }
+
+ @Override
+ public FileChannel newFileChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
+ Path r = unroot(path);
+ return provider(r).newFileChannel(r, options, attrs);
+ }
+
+ @Override
+ public AsynchronousFileChannel newAsynchronousFileChannel(Path path, Set<? extends OpenOption> options, ExecutorService executor, FileAttribute<?>... attrs) throws IOException {
+ Path r = unroot(path);
+ return provider(r).newAsynchronousFileChannel(r, options, executor, attrs);
+ }
+
+ @Override
+ public SeekableByteChannel newByteChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
+ Path r = unroot(path);
+ return provider(r).newByteChannel(path, options, attrs);
+ }
+
+ @Override
+ public DirectoryStream<Path> newDirectoryStream(Path dir, DirectoryStream.Filter<? super Path> filter) throws IOException {
+ Path r = unroot(dir);
+ return provider(r).newDirectoryStream(r, filter);
+ }
+
+ @Override
+ public void createDirectory(Path dir, FileAttribute<?>... attrs) throws IOException {
+ Path r = unroot(dir);
+ provider(r).createDirectory(r, attrs);
+ }
+
+ @Override
+ public void createSymbolicLink(Path link, Path target, FileAttribute<?>... attrs) throws IOException {
+ Path l = unroot(link);
+ Path t = unroot(target, false);
+ provider(l).createSymbolicLink(l, t, attrs);
+ }
+
+ @Override
+ public void createLink(Path link, Path existing) throws IOException {
+ Path l = unroot(link);
+ Path e = unroot(existing);
+ provider(l).createLink(l, e);
+ }
+
+ @Override
+ public void delete(Path path) throws IOException {
+ Path r = unroot(path);
+ provider(r).delete(r);
+ }
+
+ @Override
+ public boolean deleteIfExists(Path path) throws IOException {
+ Path r = unroot(path);
+ return provider(r).deleteIfExists(r);
+ }
+
+ @Override
+ public Path readSymbolicLink(Path link) throws IOException {
+ Path r = unroot(link);
+ return root(link.getFileSystem(), provider(r).readSymbolicLink(r));
+
+ }
+
+ @Override
+ public void copy(Path source, Path target, CopyOption... options) throws IOException {
+ Path s = unroot(source);
+ Path t = unroot(target);
+ provider(s).copy(s, t, options);
+ }
+
+ @Override
+ public void move(Path source, Path target, CopyOption... options) throws IOException {
+ Path s = unroot(source);
+ Path t = unroot(target);
+ provider(s).move(s, t, options);
+ }
+
+ @Override
+ public boolean isSameFile(Path path, Path path2) throws IOException {
+ Path r = unroot(path);
+ Path r2 = unroot(path2);
+ return provider(r).isSameFile(r, r2);
+ }
+
+ @Override
+ public boolean isHidden(Path path) throws IOException {
+ Path r = unroot(path);
+ return provider(r).isHidden(r);
+ }
+
+ @Override
+ public FileStore getFileStore(Path path) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void checkAccess(Path path, AccessMode... modes) throws IOException {
+ Path r = unroot(path);
+ provider(r).checkAccess(r, modes);
+ }
+
+ @Override
+ public <V extends FileAttributeView> V getFileAttributeView(Path path, Class<V> type, LinkOption... options) {
+ Path r = unroot(path);
+ return provider(r).getFileAttributeView(r, type, options);
+ }
+
+ @Override
+ public <A extends BasicFileAttributes> A readAttributes(Path path, Class<A> type, LinkOption... options) throws IOException {
+ Path r = unroot(path);
+ return provider(r).readAttributes(r, type, options);
+ }
+
+ @Override
+ public Map<String, Object> readAttributes(Path path, String attributes, LinkOption... options) throws IOException {
+ Path r = unroot(path);
+ return provider(r).readAttributes(r, attributes, options);
+ }
+
+ @Override
+ public void setAttribute(Path path, String attribute, Object value, LinkOption... options) throws IOException {
+ Path r = unroot(path);
+ provider(r).setAttribute(r, attribute, value, options);
+ }
+
+ private FileSystemProvider provider(Path path) {
+ return path.getFileSystem().provider();
+ }
+
+ private Path root(FileSystem fs, Path nat) {
+ RootedFileSystem rfs = (RootedFileSystem) fs;
+ if (nat.isAbsolute()) {
+ return rfs.getPath("/" + rfs.getRoot().relativize(nat).toString());
+ } else {
+ return rfs.getPath(nat.toString());
+ }
+ }
+
+ private Path unroot(Path path) {
+ return unroot(path, true);
+ }
+
+ private Path unroot(Path path, boolean absolute) {
+ if (path == null) {
+ throw new NullPointerException();
+ }
+ if (!(path instanceof RootedPath)) {
+ throw new ProviderMismatchException();
+ }
+ RootedPath p = (RootedPath) path;
+ if (absolute || p.isAbsolute()) {
+ String r = p.toAbsolutePath().toString();
+ return p.getFileSystem().getRoot().resolve(r.substring(1));
+ } else {
+ return p.getFileName().getRoot().getFileSystem().getPath(p.toString());
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/345604ba/sshd-core/src/main/java/org/apache/sshd/common/file/root/RootedPath.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/file/root/RootedPath.java b/sshd-core/src/main/java/org/apache/sshd/common/file/root/RootedPath.java
new file mode 100644
index 0000000..e7be13e
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/file/root/RootedPath.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.common.file.root;
+
+import java.io.IOException;
+import java.net.URI;
+import java.nio.file.LinkOption;
+
+import org.apache.sshd.common.file.util.BasePath;
+import org.apache.sshd.common.file.util.ImmutableList;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class RootedPath extends BasePath<RootedPath, RootedFileSystem> {
+
+ public RootedPath(RootedFileSystem fileSystem, String root, ImmutableList<String> names) {
+ super(fileSystem, root, names);
+ }
+
+ public URI toUri() {
+ // TODO
+ return null;
+ }
+
+ public RootedPath toRealPath(LinkOption... options) throws IOException {
+ RootedPath absolute = toAbsolutePath();
+ fileSystem.provider().checkAccess(absolute);
+ return absolute;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/345604ba/sshd-core/src/main/java/org/apache/sshd/common/file/virtualfs/VirtualFileSystemFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/file/virtualfs/VirtualFileSystemFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/file/virtualfs/VirtualFileSystemFactory.java
index 21d6cc7..2d74509 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/file/virtualfs/VirtualFileSystemFactory.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/file/virtualfs/VirtualFileSystemFactory.java
@@ -18,14 +18,15 @@
*/
package org.apache.sshd.common.file.virtualfs;
-import java.util.HashMap;
+import java.io.IOException;
+import java.nio.file.FileSystem;
+import java.nio.file.Paths;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.sshd.common.Session;
import org.apache.sshd.common.file.FileSystemFactory;
-import org.apache.sshd.common.file.FileSystemView;
-import org.apache.sshd.common.file.nativefs.NativeFileSystemView;
+import org.apache.sshd.common.file.root.RootedFileSystemProvider;
/**
* SSHd file system factory to reduce the visibility to a physical folder.
@@ -69,11 +70,9 @@ public class VirtualFileSystemFactory implements FileSystemFactory {
return homeDir;
}
- public FileSystemView createFileSystemView(Session session) {
+ public FileSystem createFileSystem(Session session) throws IOException {
String dir = computeRootDir(session.getUsername());
- Map<String, String> roots = new HashMap<String, String>();
- roots.put("/", dir);
- return new NativeFileSystemView(session.getUsername(), roots, "/", '/', false);
+ return new RootedFileSystemProvider().newFileSystem(Paths.get(dir), null);
}
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/345604ba/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 517cb36..9827507 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
@@ -20,17 +20,26 @@ package org.apache.sshd.common.scp;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
+import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.nio.file.DirectoryStream;
+import java.nio.file.FileSystem;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.StandardOpenOption;
+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.EnumSet;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
import org.apache.sshd.common.SshException;
-import org.apache.sshd.common.file.FileSystemView;
-import org.apache.sshd.common.file.SshFile;
import org.apache.sshd.common.util.DirectoryScanner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -70,22 +79,22 @@ public class ScpHelper {
public static final int S_IWOTH = 0000002;
public static final int S_IXOTH = 0000001;
- protected final FileSystemView root;
+ protected final FileSystem fileSystem;
protected final InputStream in;
protected final OutputStream out;
- public ScpHelper(InputStream in, OutputStream out, FileSystemView root) {
+ public ScpHelper(InputStream in, OutputStream out, FileSystem fileSystem) {
this.in = in;
this.out = out;
- this.root = root;
+ this.fileSystem = fileSystem;
}
- public void receive(SshFile path, boolean recursive, boolean shouldBeDir, boolean preserve, int bufferSize) throws IOException {
+ public void receive(Path path, boolean recursive, boolean shouldBeDir, boolean preserve, int bufferSize) throws IOException {
if (shouldBeDir) {
- if (!path.doesExist()) {
+ if (!Files.exists(path)) {
throw new SshException("Target directory " + path.toString() + " does not exists");
}
- if (!path.isDirectory()) {
+ if (!Files.isDirectory(path)) {
throw new SshException("Target directory " + path.toString() + " is not a directory");
}
}
@@ -136,7 +145,7 @@ public class ScpHelper {
}
- public void receiveDir(String header, SshFile path, long[] time, boolean preserve, int bufferSize) throws IOException {
+ public void receiveDir(String header, Path path, long[] time, boolean preserve, int bufferSize) throws IOException {
if (log.isDebugEnabled()) {
log.debug("Receiving directory {}", path);
}
@@ -151,26 +160,26 @@ public class ScpHelper {
if (length != 0) {
throw new IOException("Expected 0 length for directory but got " + length);
}
- SshFile file;
- if (path.doesExist() && path.isDirectory()) {
- file = root.getFile(path, name);
- } else if (!path.doesExist() && path.getParentFile().doesExist() && path.getParentFile().isDirectory()) {
+ Path file;
+ if (Files.exists(path) && Files.isDirectory(path)) {
+ file = path.resolve(name);
+ } else if (!Files.exists(path) && Files.exists(path.getParent()) && Files.isDirectory(path.getParent())) {
file = path;
} else {
throw new IOException("Can not write to " + path);
}
- if (!(file.doesExist() && file.isDirectory()) && !file.mkdir()) {
- throw new IOException("Could not create directory " + file);
+ if (!(Files.exists(file) && Files.isDirectory(file))) {
+ Files.createDirectory(file);
}
if (preserve) {
- Map<SshFile.Attribute, Object> attrs = new HashMap<SshFile.Attribute, Object>();
- attrs.put(SshFile.Attribute.Permissions, fromOctalPerms(perms));
+ setOctalPerms(file, perms);
if (time != null) {
- attrs.put(SshFile.Attribute.LastModifiedTime, time[0]);
- attrs.put(SshFile.Attribute.LastAccessTime, time[1]);
+ Files.getFileAttributeView(file, BasicFileAttributeView.class)
+ .setTimes(FileTime.from(time[0], TimeUnit.SECONDS),
+ FileTime.from(time[1], TimeUnit.SECONDS),
+ null);
}
- file.setAttributes(attrs);
}
ack();
@@ -198,7 +207,7 @@ public class ScpHelper {
}
- public void receiveFile(String header, SshFile path, long[] time, boolean preserve, int bufferSize) throws IOException {
+ public void receiveFile(String header, Path path, long[] time, boolean preserve, int bufferSize) throws IOException {
if (log.isDebugEnabled()) {
log.debug("Receiving file {}", path);
}
@@ -211,7 +220,7 @@ public class ScpHelper {
}
String perms = header.substring(1, 5);
- long length = Long.parseLong(header.substring(6, header.indexOf(' ', 6)));
+ final 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("receiveFile(" + path + ") bad length in header: " + header);
@@ -225,50 +234,78 @@ public class ScpHelper {
}
- SshFile file;
- if (path.doesExist() && path.isDirectory()) {
- file = root.getFile(path, name);
- } else if (path.doesExist() && path.isFile()) {
+ Path file;
+ if (Files.exists(path) && Files.isDirectory(path)) {
+ file = path.resolve(name);
+ } else if (Files.exists(path) && Files.isRegularFile(path)) {
file = path;
- } else if (!path.doesExist() && path.getParentFile().doesExist() && path.getParentFile().isDirectory()) {
+ } else if (!Files.exists(path) && Files.exists(path.getParent()) && Files.isDirectory(path.getParent())) {
file = path;
} else {
throw new IOException("Can not write to " + path);
}
- if (file.doesExist() && file.isDirectory()) {
+ if (Files.exists(file) && Files.isDirectory(file)) {
throw new IOException("File is a directory: " + file);
- } else if (file.doesExist() && !file.isWritable()) {
+ } else if (Files.exists(file) && !Files.isWritable(file)) {
throw new IOException("Can not write to file: " + file);
}
- if (file.doesExist()) {
- file.truncate();
- }
- OutputStream os = file.createOutputStream(0);
- try {
- ack();
+ InputStream is = new FilterInputStream(in) {
+ long remaining = length;
+ @Override
+ public int read() throws IOException {
+ if (remaining > 0) {
+ remaining--;
+ return super.read();
+ } else{
+ return -1;
+ }
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ int nb = len;
+ if (nb > remaining) {
+ nb = (int) remaining;
+ }
+ if (nb > 0) {
+ int read = super.read(b, off, nb);
+ remaining -= read;
+ return read;
+ } else {
+ return -1;
+ }
+ }
- byte[] buffer = new byte[bufSize];
- while (length > 0) {
- int len = (int) Math.min(length, buffer.length);
- len = in.read(buffer, 0, len);
- if (len <= 0) {
- throw new IOException("End of stream reached");
+ @Override
+ public long skip(long n) throws IOException {
+ long skipped = super.skip(n);
+ remaining -= skipped;
+ return skipped;
+ }
+
+ @Override
+ public int available() throws IOException {
+ int av = super.available();
+ if (av > remaining) {
+ return (int) remaining;
+ } else {
+ return av;
}
- os.write(buffer, 0, len);
- length -= len;
}
- } finally {
- os.close();
+ };
+ try (OutputStream os = Files.newOutputStream(file)) {
+ ack();
+ copy(is, os, bufSize);
}
if (preserve) {
- Map<SshFile.Attribute, Object> attrs = new HashMap<SshFile.Attribute, Object>();
- attrs.put(SshFile.Attribute.Permissions, fromOctalPerms(perms));
+ setOctalPerms(file, perms);
if (time != null) {
- attrs.put(SshFile.Attribute.LastModifiedTime, time[0]);
- attrs.put(SshFile.Attribute.LastAccessTime, time[1]);
+ Files.getFileAttributeView(file, BasicFileAttributeView.class)
+ .setTimes(FileTime.from(time[0], TimeUnit.SECONDS),
+ FileTime.from(time[1], TimeUnit.SECONDS),
+ null);
}
- file.setAttributes(attrs);
}
ack();
@@ -309,10 +346,10 @@ public class ScpHelper {
}
String[] included = new DirectoryScanner(basedir, pattern).scan();
for (String path : included) {
- SshFile file = root.getFile(basedir + "/" + path);
- if (file.isFile()) {
+ Path file = fileSystem.getPath(basedir + "/" + path);
+ if (Files.isRegularFile(file)) {
sendFile(file, preserve, bufferSize);
- } else if (file.isDirectory()) {
+ } else if (Files.isDirectory(file)) {
if (!recursive) {
out.write(ScpHelper.WARNING);
out.write((path + " not a regular file\n").getBytes());
@@ -331,13 +368,13 @@ public class ScpHelper {
basedir = pattern.substring(0, lastSep);
pattern = pattern.substring(lastSep + 1);
}
- SshFile file = root.getFile(basedir + "/" + pattern);
- if (!file.doesExist()) {
+ Path file = fileSystem.getPath(basedir + "/" + pattern);
+ if (!Files.exists(file)) {
throw new IOException(file + ": no such file or directory");
}
- if (file.isFile()) {
+ if (Files.isRegularFile(file)) {
sendFile(file, preserve, bufferSize);
- } else if (file.isDirectory()) {
+ } else if (Files.isDirectory(file)) {
if (!recursive) {
throw new IOException(file + " not a regular file");
} else {
@@ -350,7 +387,7 @@ public class ScpHelper {
}
}
- public void sendFile(SshFile path, boolean preserve, int bufferSize) throws IOException {
+ public void sendFile(Path path, boolean preserve, int bufferSize) throws IOException {
if (log.isDebugEnabled()) {
log.debug("Sending file {}", path);
}
@@ -359,15 +396,15 @@ public class ScpHelper {
throw new IOException("sendFile(" + path + ") buffer size (" + bufferSize + ") below minimum (" + MIN_SEND_BUFFER_SIZE + ")");
}
- Map<SshFile.Attribute,Object> attrs = path.getAttributes(true);
+ BasicFileAttributes basic = Files.getFileAttributeView(path, BasicFileAttributeView.class).readAttributes();
if (preserve) {
StringBuffer buf = new StringBuffer();
buf.append("T");
- buf.append(attrs.get(SshFile.Attribute.LastModifiedTime));
+ buf.append(basic.lastModifiedTime().to(TimeUnit.SECONDS));
buf.append(" ");
buf.append("0");
buf.append(" ");
- buf.append(attrs.get(SshFile.Attribute.LastAccessTime));
+ buf.append(basic.lastAccessTime().to(TimeUnit.SECONDS));
buf.append(" ");
buf.append("0");
buf.append("\n");
@@ -378,17 +415,17 @@ public class ScpHelper {
StringBuffer buf = new StringBuffer();
buf.append("C");
- buf.append(preserve ? toOctalPerms((EnumSet<SshFile.Permission>) attrs.get(SshFile.Attribute.Permissions)) : "0644");
+ buf.append(preserve ? getOctalPerms(path) : "0644");
buf.append(" ");
- buf.append(attrs.get(SshFile.Attribute.Size)); // length
+ buf.append(basic.size()); // length
buf.append(" ");
- buf.append(path.getName());
+ buf.append(path.getFileName().toString());
buf.append("\n");
out.write(buf.toString().getBytes());
out.flush();
readAck(false);
- long fileSize = path.getSize();
+ long fileSize = Files.size(path);
if (fileSize < 0L) { // TODO consider throwing an exception...
log.warn("sendFile(" + path + ") bad file size: " + fileSize);
}
@@ -400,36 +437,27 @@ public class ScpHelper {
bufSize = MIN_SEND_BUFFER_SIZE;
}
- InputStream is = path.createInputStream(0);
- try {
- byte[] buffer = new byte[bufSize];
- for (;;) {
- int len = is.read(buffer, 0, buffer.length);
- if (len == -1) {
- break;
- }
- out.write(buffer, 0, len);
- }
- } finally {
- is.close();
+ // TODO: use bufSize
+ try (InputStream in = Files.newInputStream(path)) {
+ copy(in, out, bufSize);
}
ack();
readAck(false);
}
- public void sendDir(SshFile path, boolean preserve, int bufferSize) throws IOException {
+ public void sendDir(Path path, boolean preserve, int bufferSize) throws IOException {
if (log.isDebugEnabled()) {
log.debug("Sending directory {}", path);
}
- Map<SshFile.Attribute,Object> attrs = path.getAttributes(true);
+ BasicFileAttributes basic = Files.getFileAttributeView(path, BasicFileAttributeView.class).readAttributes();
if (preserve) {
StringBuffer buf = new StringBuffer();
buf.append("T");
- buf.append(attrs.get(SshFile.Attribute.LastModifiedTime));
+ buf.append(basic.lastModifiedTime().to(TimeUnit.SECONDS));
buf.append(" ");
buf.append("0");
buf.append(" ");
- buf.append(attrs.get(SshFile.Attribute.LastAccessTime));
+ buf.append(basic.lastAccessTime().to(TimeUnit.SECONDS));
buf.append(" ");
buf.append("0");
buf.append("\n");
@@ -440,21 +468,23 @@ public class ScpHelper {
StringBuffer buf = new StringBuffer();
buf.append("D");
- buf.append(preserve ? toOctalPerms((EnumSet<SshFile.Permission>) attrs.get(SshFile.Attribute.Permissions)) : "0755");
+ buf.append(preserve ? getOctalPerms(path) : "0755");
buf.append(" ");
buf.append("0"); // length
buf.append(" ");
- buf.append(path.getName());
+ buf.append(path.getFileName().toString());
buf.append("\n");
out.write(buf.toString().getBytes());
out.flush();
readAck(false);
- for (SshFile child : path.listSshFiles()) {
- if (child.isFile()) {
- sendFile(child, preserve, bufferSize);
- } else if (child.isDirectory()) {
- sendDir(child, preserve, bufferSize);
+ try (DirectoryStream<Path> children = Files.newDirectoryStream(path)) {
+ for (Path child : children) {
+ if (Files.isRegularFile(child)) {
+ sendFile(child, preserve, bufferSize);
+ } else if (Files.isDirectory(child)) {
+ sendDir(child, preserve, bufferSize);
+ }
}
}
@@ -468,55 +498,90 @@ public class ScpHelper {
return new long[] { Long.parseLong(numbers[0]), Long.parseLong(numbers[2]) };
}
- public static String toOctalPerms(EnumSet<SshFile.Permission> perms) {
+ public static String getOctalPerms(Path path) throws IOException {
int pf = 0;
- for (SshFile.Permission p : perms) {
- switch (p) {
- case UserRead: pf |= S_IRUSR; break;
- case UserWrite: pf |= S_IWUSR; break;
- case UserExecute: pf |= S_IXUSR; break;
- case GroupRead: pf |= S_IRGRP; break;
- case GroupWrite: pf |= S_IWGRP; break;
- case GroupExecute: pf |= S_IXGRP; break;
- case OthersRead: pf |= S_IROTH; break;
- case OthersWrite: pf |= S_IWOTH; break;
- case OthersExecute: pf |= S_IXOTH; break;
+ if (path.getFileSystem().supportedFileAttributeViews().contains("posix")) {
+ Set<PosixFilePermission> perms = Files.getPosixFilePermissions(path);
+ for (PosixFilePermission p : perms) {
+ switch (p) {
+ case OWNER_READ:
+ pf |= S_IRUSR;
+ break;
+ case OWNER_WRITE:
+ pf |= S_IWUSR;
+ break;
+ case OWNER_EXECUTE:
+ pf |= S_IXUSR;
+ break;
+ case GROUP_READ:
+ pf |= S_IRGRP;
+ break;
+ case GROUP_WRITE:
+ pf |= S_IWGRP;
+ break;
+ case GROUP_EXECUTE:
+ pf |= S_IXGRP;
+ break;
+ case OTHERS_READ:
+ pf |= S_IROTH;
+ break;
+ case OTHERS_WRITE:
+ pf |= S_IWOTH;
+ break;
+ case OTHERS_EXECUTE:
+ pf |= S_IXOTH;
+ break;
+ }
+ }
+ } else {
+ if (Files.isReadable(path)) {
+ pf |= S_IRUSR | S_IRGRP | S_IROTH;
+ }
+ if (Files.isWritable(path)) {
+ pf |= S_IWUSR | S_IWGRP | S_IWOTH;
+ }
+ if (Files.isExecutable(path)) {
+ pf |= S_IXUSR | S_IXGRP | S_IXOTH;
}
}
return String.format("%04o", pf);
}
- public static EnumSet<SshFile.Permission> fromOctalPerms(String str) {
+ public static void setOctalPerms(Path path, String str) throws IOException {
int perms = Integer.parseInt(str, 8);
- EnumSet<SshFile.Permission> p = EnumSet.noneOf(SshFile.Permission.class);
+ EnumSet<PosixFilePermission> p = EnumSet.noneOf(PosixFilePermission.class);
if ((perms & S_IRUSR) != 0) {
- p.add(SshFile.Permission.UserRead);
+ p.add(PosixFilePermission.OWNER_READ);
}
if ((perms & S_IWUSR) != 0) {
- p.add(SshFile.Permission.UserWrite);
+ p.add(PosixFilePermission.OWNER_WRITE);
}
if ((perms & S_IXUSR) != 0) {
- p.add(SshFile.Permission.UserExecute);
+ p.add(PosixFilePermission.OWNER_EXECUTE);
}
if ((perms & S_IRGRP) != 0) {
- p.add(SshFile.Permission.GroupRead);
+ p.add(PosixFilePermission.GROUP_READ);
}
if ((perms & S_IWGRP) != 0) {
- p.add(SshFile.Permission.GroupWrite);
+ p.add(PosixFilePermission.GROUP_WRITE);
}
if ((perms & S_IXGRP) != 0) {
- p.add(SshFile.Permission.GroupExecute);
+ p.add(PosixFilePermission.GROUP_EXECUTE);
}
if ((perms & S_IROTH) != 0) {
- p.add(SshFile.Permission.OthersRead);
+ p.add(PosixFilePermission.OTHERS_READ);
}
if ((perms & S_IWOTH) != 0) {
- p.add(SshFile.Permission.OthersWrite);
+ p.add(PosixFilePermission.OTHERS_WRITE);
}
if ((perms & S_IXOTH) != 0) {
- p.add(SshFile.Permission.OthersExecute);
+ p.add(PosixFilePermission.OTHERS_EXECUTE);
+ }
+ if (path.getFileSystem().supportedFileAttributeViews().contains("posix")) {
+ Files.setPosixFilePermissions(path, p);
+ } else {
+ log.warn("Unable to set file permissions because the underlying file system does not support posix permissions");
}
- return p;
}
public void ack() throws IOException {
@@ -545,4 +610,17 @@ public class ScpHelper {
return c;
}
+ private static long copy(InputStream source, OutputStream sink, int bufferSize)
+ throws IOException
+ {
+ long nread = 0L;
+ byte[] buf = new byte[bufferSize];
+ int n;
+ while ((n = source.read(buf)) > 0) {
+ sink.write(buf, 0, n);
+ nread += n;
+ }
+ return nread;
+ }
+
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/345604ba/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java b/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
index ab10ef4..ac4bab2 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
@@ -492,7 +492,7 @@ public class ChannelSession extends AbstractServerChannel {
// If the shell wants to be aware of the file system, let's do that too
if (command instanceof FileSystemAware) {
FileSystemFactory factory = ((ServerSession) session).getFactoryManager().getFileSystemFactory();
- ((FileSystemAware) command).setFileSystemView(factory.createFileSystemView(session));
+ ((FileSystemAware) command).setFileSystem(factory.createFileSystem(session));
}
// If the shell wants to use non-blocking io
if (command instanceof AsyncCommand) {
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/345604ba/sshd-core/src/main/java/org/apache/sshd/server/command/ScpCommand.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/command/ScpCommand.java b/sshd-core/src/main/java/org/apache/sshd/server/command/ScpCommand.java
index 68dc9ef..d68e0b9 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/command/ScpCommand.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/command/ScpCommand.java
@@ -25,9 +25,9 @@ import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
+import java.nio.file.FileSystem;
import org.apache.sshd.common.file.FileSystemAware;
-import org.apache.sshd.common.file.FileSystemView;
import org.apache.sshd.common.scp.ScpHelper;
import org.apache.sshd.common.util.ThreadUtils;
import org.apache.sshd.server.Command;
@@ -53,7 +53,7 @@ public class ScpCommand implements Command, Runnable, FileSystemAware {
protected boolean optF;
protected boolean optD;
protected boolean optP; // TODO: handle modification times
- protected FileSystemView root;
+ protected FileSystem fileSystem;
protected String path;
protected InputStream in;
protected OutputStream out;
@@ -237,8 +237,8 @@ public class ScpCommand implements Command, Runnable, FileSystemAware {
this.callback = callback;
}
- public void setFileSystemView(FileSystemView view) {
- this.root = view;
+ public void setFileSystem(FileSystem fs) {
+ this.fileSystem = fs;
}
public void start(Environment env) throws IOException {
@@ -274,15 +274,23 @@ public class ScpCommand implements Command, Runnable, FileSystemAware {
}
executors = null;
+
+ try {
+ fileSystem.close();
+ } catch (UnsupportedOperationException e) {
+ // Ignore
+ } catch (IOException e) {
+ log.debug("Error closing FileSystem", e);
+ }
}
public void run() {
int exitValue = ScpHelper.OK;
String exitMessage = null;
- ScpHelper helper = new ScpHelper(in, out, root);
+ ScpHelper helper = new ScpHelper(in, out, fileSystem);
try {
if (optT) {
- helper.receive(root.getFile(path), optR, optD, optP, receiveBufferSize);
+ helper.receive(fileSystem.getPath(path), optR, optD, optP, receiveBufferSize);
} else if (optF) {
helper.send(Collections.singletonList(path), optR, optP, sendBufferSize);
} else {