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 {