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 2014/04/08 13:03:48 UTC

[1/2] git commit: [SSHD-310] Better file system abstraction to support multiple roots

Repository: mina-sshd
Updated Branches:
  refs/heads/master 83c1fc118 -> 441bdb919


[SSHD-310] Better file system abstraction to support multiple roots 

Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/61ebb206
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/61ebb206
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/61ebb206

Branch: refs/heads/master
Commit: 61ebb2063a75991d6f5c6e0ff7b2d56343bf2ec3
Parents: 83c1fc1
Author: Guillaume Nodet <gn...@apache.org>
Authored: Tue Apr 8 13:02:19 2014 +0200
Committer: Guillaume Nodet <gn...@apache.org>
Committed: Tue Apr 8 13:03:22 2014 +0200

----------------------------------------------------------------------
 .../apache/sshd/common/file/FileSystemView.java |   9 +
 .../file/nativefs/NativeFileSystemView.java     | 336 +++++++++++++++++--
 .../common/file/nativefs/NativeSshFile.java     |  67 ++--
 .../virtualfs/VirtualFileSystemFactory.java     |   8 +-
 .../file/virtualfs/VirtualFileSystemView.java   |  63 ----
 .../apache/sshd/server/sftp/SftpSubsystem.java  |   2 +-
 .../file/nativefs/NativeFileSystemViewTest.java | 115 +++++++
 .../common/file/nativefs/NativeSshFileTest.java |  10 +-
 8 files changed, 472 insertions(+), 138 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/61ebb206/sshd-core/src/main/java/org/apache/sshd/common/file/FileSystemView.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/file/FileSystemView.java b/sshd-core/src/main/java/org/apache/sshd/common/file/FileSystemView.java
index 0730341..e2de5d5 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/file/FileSystemView.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/file/FileSystemView.java
@@ -41,4 +41,13 @@ public interface FileSystemView {
      */
     SshFile getFile(SshFile baseDir, String file);
 
+    /**
+     * Return a view of this file system which has a single root
+     * and uses '/' as a file separator.
+     * If the file system has multiple roots, they are mapped
+     * to directories inside that single root.
+     * @return a normalized file system view
+     */
+    FileSystemView getNormalizedView();
+
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/61ebb206/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeFileSystemView.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeFileSystemView.java b/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeFileSystemView.java
index 69bc2d2..9816b1e 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeFileSystemView.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeFileSystemView.java
@@ -20,12 +20,21 @@
 package org.apache.sshd.common.file.nativefs;
 
 import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
 
 import org.apache.sshd.common.file.FileSystemView;
 import org.apache.sshd.common.file.SshFile;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import static org.apache.sshd.common.file.nativefs.NativeSshFile.normalizeSeparateChar;
+
 /**
  * <strong>Internal class, do not use directly.</strong>
  * 
@@ -36,22 +45,25 @@ import org.slf4j.LoggerFactory;
  */
 public class NativeFileSystemView implements FileSystemView {
 
-    private final Logger LOG = LoggerFactory
-    .getLogger(NativeFileSystemView.class);
+    private final Logger LOG = LoggerFactory.getLogger(NativeFileSystemView.class);
+
 
+    private Map<String, String> roots;
 
     // the first and the last character will always be '/'
-    // It is always with respect to the root directory.
-    private String currDir;
+    // It is always with respect to one of the roots.
+    private String current;
 
     private String userName;
 
+    private char separator;
+
     private boolean caseInsensitive = false;
 
     /**
      * Constructor - internal do not use directly, use {@link NativeFileSystemFactory} instead
      */
-    protected NativeFileSystemView(String userName) {
+    public NativeFileSystemView(String userName) {
         this(userName, false);
     }
 
@@ -59,28 +71,89 @@ public class NativeFileSystemView implements FileSystemView {
      * Constructor - internal do not use directly, use {@link NativeFileSystemFactory} instead
      */
     public NativeFileSystemView(String userName, boolean caseInsensitive) {
+        this(userName, getAllRoots(), System.getProperty("user.dir"), File.separatorChar, caseInsensitive);
+    }
+
+    /**
+     * Constructor - internal do not use directly, use {@link NativeFileSystemFactory} instead
+     */
+    public NativeFileSystemView(String userName, Map<String, String> roots, String current) {
+        this(userName, roots, current, File.separatorChar, false);
+    }
+
+    /**
+     * Constructor - internal do not use directly, use {@link NativeFileSystemFactory} instead
+     *
+     * @param userName the user name
+     * @param roots known root mapping, key is the virtual root name, value is the physical file
+     * @param current the virtual current dir
+     */
+    public NativeFileSystemView(String userName, Map<String, String> roots, String current, char separator, boolean caseInsensitive) {
         if (userName == null) {
             throw new IllegalArgumentException("user can not be null");
         }
+        // Normalize roots
+        Map<String, String> verRoots = new LinkedHashMap<String, String>();
+        for (String r : roots.keySet()) {
+            String virtual = appendSlash(normalizeSeparateChar(r));
+            String physical = appendSlash(normalizeSeparateChar(roots.get(r)));
+            verRoots.put(virtual, physical);
+        }
+        // add last '/' if necessary
+        current = appendSlash(normalizeSeparateChar(current));
+        // Verify the current dir is relative to a known root
+        String root = null;
+        for (String r : verRoots.keySet()) {
+            if (current.startsWith(r)) {
+                root = r;
+                break;
+            }
+        }
+        if (root == null) {
+            throw new IllegalArgumentException("Current dir " + current + " does not start from a known root: " + new ArrayList<String>(verRoots.keySet()));
+        }
 
+        this.separator = separator;
         this.caseInsensitive = caseInsensitive;
-
-        currDir = getVirtualUserDir();
+        this.roots = verRoots;
+        this.current = current;
         this.userName = userName;
+        LOG.debug("Native filesystem view created for user \"{}\" with current dir \"{}\"", userName, this.current);
+    }
 
-        // add last '/' if necessary
-        LOG.debug("Native filesystem view created for user \"{}\" with root \"{}\"", userName, currDir);
+    private String appendSlash(String path) {
+        return path.endsWith("/") ? path : path + "/";
+    }
+
+    private static Map<String, String> getAllRoots() {
+        Map<String, String> roots = new LinkedHashMap<String, String>();
+        if (isWindows) {
+            for (File file : File.listRoots()) {
+                if (file.exists()) {
+                    String root = file.toString();
+                    String name = root.substring(0, root.length() - 1);
+                    roots.put(name, root);
+                }
+            }
+        } else {
+            roots.put("/", "/");
+        }
+        return roots;
     }
 
     public String getUserName() {
         return userName;
     }
 
+    public char getSeparator() {
+        return separator;
+    }
+
     /**
      * Get file object.
      */
     public SshFile getFile(String file) {
-        return getFile(currDir, file);
+        return getFile(current, file);
     }
 
     public SshFile getFile(SshFile baseDir, String file) {
@@ -88,35 +161,48 @@ public class NativeFileSystemView implements FileSystemView {
     }
 
     protected SshFile getFile(String dir, String file) {
+        dir = appendSlash(normalizeSeparateChar(dir));
+        file = normalizeSeparateChar(file);
+        // Compute root + non rooted absolute file
+        String root = null;
+        if (roots.size() > 1 && file.startsWith("/")) {
+            file = file.substring(1);
+        }
+        for (String r : roots.keySet()) {
+            if (!file.isEmpty() && r.equals(file + "/")) {
+                file += "/";
+            }
+            if (file.startsWith(r)) {
+                root = r;
+                file = "/" + file.substring(r.length());
+                break;
+            }
+        }
+        if (root == null) {
+            // file is relative to dir
+            file = dir + file;
+            for (String r : roots.keySet()) {
+                if (file.startsWith(r)) {
+                    root = r;
+                    file = "/" + file.substring(r.length());
+                    break;
+                }
+            }
+        }
+        if (root == null) {
+            throw new IllegalStateException("Could not find root dir for file(" + dir + ", " + file + ")");
+        }
+        // Physical root
+        String physicalRoot = roots.get(root);
         // get actual file object
-        String physicalName = NativeSshFile.getPhysicalName(getPhysicalUserDir(),
-                dir, file, caseInsensitive);
+        String physicalName = NativeSshFile.getPhysicalName(physicalRoot, "/", file, caseInsensitive);
         File fileObj = new File(physicalName);
 
         // strip the root directory and return
-        String userFileName = physicalName.substring(getPhysicalUserDir().length() - 1);
+        String userFileName = root + physicalName.substring(physicalRoot.length());
         return createNativeSshFile(userFileName, fileObj, userName);
     }
 
-    /**
-     * Returns the physical user directory, for accessing the according files or directories.
-     *  
-     * @return The physical user directory.
-     */
-    public String getPhysicalUserDir() {
-    	return "/";
-	}
-
-	/**
-	 * Returns the virtual user directory.
-	 * The will be shown in sftp client. It is relative to the physical user directory. 
-	 * 
-	 * @return The virtual user directory.
-	 */
-	public String getVirtualUserDir() {
-		return System.getProperty("user.dir");
-	}
-
     static boolean isJava7;
     static boolean isWindows;
     static {
@@ -139,11 +225,191 @@ public class NativeFileSystemView implements FileSystemView {
         isWindows = win;
     }
 
-    public NativeSshFile createNativeSshFile(final String fileName2, final File fileObj, final String userName2) {
+    public NativeSshFile createNativeSshFile(String name, File file, String userName) {
+        name = deNormalizeSeparateChar(name);
         if (isJava7 && !isWindows) {
-            return new NativeSshFileNio(this, fileName2, fileObj, userName2);
+            return new NativeSshFileNio(this, name, file, userName);
         } else {
-		    return new NativeSshFile(this, fileName2, fileObj, userName2);
+		    return new NativeSshFile(this, name, file, userName);
         }
 	}
+
+    /**
+     * Normalize separate character. Separate character should be '/' always.
+     */
+    public final String deNormalizeSeparateChar(final String pathName) {
+        return pathName.replace('/', separator);
+    }
+
+    public FileSystemView getNormalizedView() {
+        if (roots.size() == 1 && roots.containsKey("/") && separator == '/') {
+            return this;
+        }
+        return new NativeFileSystemView(userName, roots, current, '/', caseInsensitive) {
+            public SshFile getFile(String file) {
+                return getFile(reroot(current), file);
+            }
+
+            public SshFile getFile(SshFile baseDir, String file) {
+                return getFile(baseDir.getAbsolutePath(), file);
+            }
+
+            public FileSystemView getNormalizedView() {
+                return this;
+            }
+
+            protected String reroot(String file) {
+                file = appendSlash(file);
+                for (String r : roots.keySet()) {
+                    if (file.startsWith(r)) {
+                        return "/" + normalizeRoot(r) + file.substring(r.length());
+                    }
+                }
+                throw new IllegalArgumentException();
+            }
+
+            protected SshFile getFile(String dir, String file) {
+                dir = appendSlash(normalizeSeparateChar(dir));
+                file = normalizeSeparateChar(file);
+                // Compute root + non rooted absolute file
+                if (!file.startsWith("/")) {
+                    file = dir + file;
+                }
+                // get actual file object
+                String userFileName = NativeSshFile.getPhysicalName("/", "/", file, caseInsensitive);
+                if (userFileName.equals("/")) {
+                    return new RootFile();
+                }
+                int idx = userFileName.indexOf("/", 1);
+                if (idx < 0) {
+                    String root = userFileName + "/";
+                    String physRoot = null;
+                    for (String r : roots.keySet()) {
+                        if (normalizeRoot(r).equals(root)) {
+                            physRoot = roots.get(r);
+                            break;
+                        }
+                    }
+                    if (physRoot == null) {
+                        throw new IllegalArgumentException("Unknown root " + userFileName);
+                    }
+                    File fileObj = new File(physRoot);
+                    userFileName = normalizeSeparateChar(userFileName);
+                    return createNativeSshFile(userFileName, fileObj, userName);
+                } else {
+                    String root = userFileName.substring(1, idx) + "/";
+                    String physRoot = null;
+                    for (String r : roots.keySet()) {
+                        if (normalizeRoot(r).equals(root)) {
+                            physRoot = roots.get(r);
+                            break;
+                        }
+                    }
+                    if (physRoot == null) {
+                        throw new IllegalArgumentException("Unknown root " + userFileName);
+                    }
+                    File fileObj = new File(physRoot + userFileName.substring(idx + 1));
+                    userFileName = normalizeSeparateChar(userFileName);
+                    return createNativeSshFile(userFileName, fileObj, userName);
+                }
+            }
+        };
+    }
+
+    protected static String normalizeRoot(String root) {
+        return root.replace(":", "");
+    }
+
+    class RootFile implements SshFile {
+        public String getAbsolutePath() {
+            return "/";
+        }
+        public String getName() {
+            return "/";
+        }
+        public Map<Attribute, Object> getAttributes(boolean followLinks) throws IOException {
+            return null;
+        }
+        public void setAttributes(Map<Attribute, Object> attributes) throws IOException {
+            throw new UnsupportedOperationException();
+        }
+        public Object getAttribute(Attribute attribute, boolean followLinks) throws IOException {
+            return null;
+        }
+        public void setAttribute(Attribute attribute, Object value) throws IOException {
+            throw new UnsupportedOperationException();
+        }
+        public String readSymbolicLink() throws IOException {
+            return null;
+        }
+        public void createSymbolicLink(SshFile destination) throws IOException {
+        }
+        public String getOwner() {
+            return null;
+        }
+        public boolean isDirectory() {
+            return true;
+        }
+        public boolean isFile() {
+            return false;
+        }
+        public boolean doesExist() {
+            return true;
+        }
+        public boolean isReadable() {
+            return true;
+        }
+        public boolean isWritable() {
+            return false;
+        }
+        public boolean isExecutable() {
+            return false;
+        }
+        public boolean isRemovable() {
+            return false;
+        }
+        public SshFile getParentFile() {
+            return null;
+        }
+        public long getLastModified() {
+            return 0;
+        }
+        public boolean setLastModified(long time) {
+            return false;
+        }
+        public long getSize() {
+            return 0;
+        }
+        public boolean mkdir() {
+            return false;
+        }
+        public boolean delete() {
+            return false;
+        }
+        public boolean create() throws IOException {
+            return false;
+        }
+        public void truncate() throws IOException {
+        }
+        public boolean move(SshFile destination) {
+            return false;
+        }
+        public List<SshFile> listSshFiles() {
+            List<SshFile> list = new ArrayList<SshFile>();
+            for (String root : roots.keySet()) {
+                String display = normalizeRoot(root);
+                display = "/" + display.substring(display.length() - 1);
+                list.add(createNativeSshFile(display, new File(roots.get(root)), userName));
+            }
+            return list;
+        }
+        public OutputStream createOutputStream(long offset) throws IOException {
+            return null;
+        }
+        public InputStream createInputStream(long offset) throws IOException {
+            return null;
+        }
+        public void handleClose() throws IOException {
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/61ebb206/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeSshFile.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeSshFile.java b/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeSshFile.java
index ee2626c..0ece66c 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeSshFile.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/file/nativefs/NativeSshFile.java
@@ -52,8 +52,7 @@ public class NativeSshFile implements SshFile {
     protected static final Logger LOG = LoggerFactory.getLogger(NativeSshFile.class);
 
     // the file name with respect to the user root.
-    // The path separator character will be '/' and
-    // it will always begin with '/'.
+    // The path separator character will be '/'.
     protected String fileName;
 
     protected File file;
@@ -78,8 +77,6 @@ public class NativeSshFile implements SshFile {
 
         if (fileName.length() == 0) {
             throw new IllegalArgumentException("fileName can not be empty");
-        } else if (fileName.charAt(0) != '/') {
-            throw new IllegalArgumentException("fileName must be an absolute path");
         }
 
         this.fileName = fileName;
@@ -96,10 +93,12 @@ public class NativeSshFile implements SshFile {
      */
     public String getAbsolutePath() {
 
+        char separator = nativeFileSystemView.getSeparator();
+
         // strip the last '/' if necessary
         String fullName = fileName;
         int filelen = fullName.length();
-        if ((filelen != 1) && (fullName.charAt(filelen - 1) == '/')) {
+        if (fileName.indexOf(separator) != filelen - 1 && (fullName.charAt(filelen - 1) == separator)) {
             fullName = fullName.substring(0, filelen - 1);
         }
 
@@ -111,20 +110,22 @@ public class NativeSshFile implements SshFile {
      */
     public String getName() {
 
+        char separator = nativeFileSystemView.getSeparator();
+
         // root - the short name will be '/'
-        if (fileName.equals("/")) {
-            return "/";
+        if (fileName.indexOf(separator) == fileName.length() - 1) {
+            return fileName;
         }
 
         // strip the last '/'
         String shortName = fileName;
         int filelen = fileName.length();
-        if (shortName.charAt(filelen - 1) == '/') {
+        if (shortName.charAt(filelen - 1) == separator) {
             shortName = shortName.substring(0, filelen - 1);
         }
 
         // return from the last '/'
-        int slashIndex = shortName.lastIndexOf('/');
+        int slashIndex = shortName.lastIndexOf(separator);
         if (slashIndex != -1) {
             shortName = shortName.substring(slashIndex + 1);
         }
@@ -234,8 +235,10 @@ public class NativeSshFile implements SshFile {
      */
     public boolean isRemovable() {
 
+        char separator = nativeFileSystemView.getSeparator();
+
         // root cannot be deleted
-        if ("/".equals(fileName)) {
+        if (fileName.indexOf(separator) == fileName.length() - 1) {
             return false;
         }
 
@@ -251,12 +254,16 @@ public class NativeSshFile implements SshFile {
 //        }
 
         // In order to maintain consistency, when possible we delete the last '/' character in the String
-        int indexOfSlash = fullName.lastIndexOf('/');
+        int indexOfSlash = fullName.lastIndexOf(separator);
         String parentFullName;
         if (indexOfSlash == 0) {
             parentFullName = "/";
         } else {
-            parentFullName = fullName.substring(0, indexOfSlash);
+            if (fullName.indexOf(separator) == indexOfSlash) {
+                parentFullName = fullName.substring(0, indexOfSlash + 1);
+            } else {
+                parentFullName = fullName.substring(0, indexOfSlash);
+            }
         }
 
         // we check if the parent FileObject is writable.
@@ -266,12 +273,19 @@ public class NativeSshFile implements SshFile {
     }
 
     public SshFile getParentFile() {
-        int indexOfSlash = getAbsolutePath().lastIndexOf('/');
+        char separator = nativeFileSystemView.getSeparator();
+
+        String path = getAbsolutePath();
+        int indexOfSlash = path.lastIndexOf(separator);
         String parentFullName;
         if (indexOfSlash == 0) {
             parentFullName = "/";
         } else {
-            parentFullName = getAbsolutePath().substring(0, indexOfSlash);
+            if (path.indexOf(separator) == indexOfSlash) {
+                parentFullName = path.substring(0, indexOfSlash + 1);
+            } else {
+                parentFullName = path.substring(0, indexOfSlash);
+            }
         }
 
         // we check if the parent FileObject is writable.
@@ -363,10 +377,12 @@ public class NativeSshFile implements SshFile {
             }
         });
 
+        char separator = nativeFileSystemView.getSeparator();
+
         // get the virtual name of the base directory
         String virtualFileStr = getAbsolutePath();
-        if (virtualFileStr.charAt(virtualFileStr.length() - 1) != '/') {
-            virtualFileStr += '/';
+        if (virtualFileStr.charAt(virtualFileStr.length() - 1) != separator) {
+            virtualFileStr += separator;
         }
 
         // now return all the files under the directory
@@ -448,20 +464,10 @@ public class NativeSshFile implements SshFile {
      * Normalize separate character. Separate character should be '/' always.
      */
     public final static String normalizeSeparateChar(final String pathName) {
-        String normalizedPathName = pathName.replace(File.separatorChar, '/');
-        normalizedPathName = normalizedPathName.replace('\\', '/');
+        String normalizedPathName = pathName.replace('\\', '/');
         return normalizedPathName;
     }
 
-    public final static String normalizePath(final String pathName) {
-        // Support windows drive as
-        if (pathName.matches("[A-Z]:\\\\.*")) {
-            return "/" + normalizeSeparateChar(pathName);
-        } else {
-            return normalizeSeparateChar(pathName);
-        }
-    }
-
     /**
      * Get the physical canonical file name. It works like
      * File.getCanonicalPath().
@@ -477,11 +483,6 @@ public class NativeSshFile implements SshFile {
      *         will never be null.
      */
     public final static String getPhysicalName(final String rootDir,
-            final String currDir, final String fileName) {
-        return getPhysicalName(rootDir, currDir, fileName, false);
-    }
-
-    public final static String getPhysicalName(final String rootDir,
             final String currDir, final String fileName,
             final boolean caseInsensitive) {
 
@@ -491,7 +492,7 @@ public class NativeSshFile implements SshFile {
             normalizedRootDir += '/';
         }
 
-        String normalizedFileName = normalizePath(fileName);
+        String normalizedFileName = normalizeSeparateChar(fileName);
         String resArg;
         String normalizedCurrDir = currDir;
         if (normalizedFileName.charAt(0) != '/') {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/61ebb206/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 4c99b52..052d864 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,12 +18,14 @@
  */
 package org.apache.sshd.common.file.virtualfs;
 
+import java.util.HashMap;
 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;
 
 /**
  * SSHd file system factory to reduce the visibility to a physical folder.
@@ -68,8 +70,10 @@ public class VirtualFileSystemFactory implements FileSystemFactory {
     }
 
     public FileSystemView createFileSystemView(Session session) {
-        return new VirtualFileSystemView(session.getUsername(),
-                                         computeRootDir(session.getUsername()));
+        String dir = computeRootDir(session.getUsername());
+        Map<String, String> roots = new HashMap<String, String>();
+        roots.put("/", dir);
+        return new NativeFileSystemView(session.getUsername(), roots, "/");
     }
 
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/61ebb206/sshd-core/src/main/java/org/apache/sshd/common/file/virtualfs/VirtualFileSystemView.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/file/virtualfs/VirtualFileSystemView.java b/sshd-core/src/main/java/org/apache/sshd/common/file/virtualfs/VirtualFileSystemView.java
deleted file mode 100644
index 0c96cf8..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/file/virtualfs/VirtualFileSystemView.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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.virtualfs;
-
-import java.io.File;
-
-import org.apache.sshd.common.file.SshFile;
-import org.apache.sshd.common.file.nativefs.NativeFileSystemView;
-import org.apache.sshd.common.file.nativefs.NativeSshFile;
-
-/**
- * Virtual file system view reduced to a physical folder
- */
-public class VirtualFileSystemView extends NativeFileSystemView {
-
-    private String location;
-
-    public VirtualFileSystemView(String username, String location) {
-        super(username);
-        if (location.endsWith("/")) {
-            location = location.substring(0, location.length() - 1);
-        }
-        this.location = location;
-    }
-
-    @Override
-    public String getVirtualUserDir() {
-        return "/";
-    }
-
-    @Override
-    public String getPhysicalUserDir() {
-        return location;
-    }
-
-    @Override
-    protected SshFile getFile(String dir, String file) {
-        // get actual file object
-        String location = getPhysicalUserDir();
-        String physicalName = NativeSshFile.getPhysicalName(location, dir, file, false);
-        File fileObj = new File(physicalName);
-        // strip the root directory and return
-        String karafFileName = physicalName.substring(location.length());
-        return createNativeSshFile(karafFileName, fileObj, getUserName());
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/61ebb206/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java b/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
index df56bd4..bb244b2 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
@@ -1094,7 +1094,7 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
     }
 
     private SshFile resolveFile(String path) {
-    	return this.root.getFile(path);
+    	return this.root.getNormalizedView().getFile(path);
     }
 
     private final static String[] MONTHS = { "Jan", "Feb", "Mar", "Apr", "May",

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/61ebb206/sshd-core/src/test/java/org/apache/sshd/common/file/nativefs/NativeFileSystemViewTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/file/nativefs/NativeFileSystemViewTest.java b/sshd-core/src/test/java/org/apache/sshd/common/file/nativefs/NativeFileSystemViewTest.java
new file mode 100644
index 0000000..a77d950
--- /dev/null
+++ b/sshd-core/src/test/java/org/apache/sshd/common/file/nativefs/NativeFileSystemViewTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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.nativefs;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.sshd.common.file.FileSystemView;
+import org.apache.sshd.common.file.SshFile;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+public class NativeFileSystemViewTest {
+
+    @Test
+    public void testResolveWithVirtualRoots() throws Exception {
+
+        Map<String, String> roots = new HashMap<String, String>();
+        roots.put("A:", "/fs/rootA");
+        roots.put("Z:", "/fs/rootZ");
+        String current = "Z:/git";
+        NativeFileSystemView view = new NativeFileSystemView("user", roots, current, '/', false);
+
+        assertEquals("Z:/git/foo/bar", view.getFile("foo/bar").getAbsolutePath());
+        assertEquals("Z:/foo/bar", view.getFile("../foo/bar").getAbsolutePath());
+        assertEquals("A:/temp", view.getFile("A:/./a/../temp").getAbsolutePath());
+        assertEquals("A:/temp", view.getFile("A:/../../temp").getAbsolutePath());
+
+        FileSystemView normView = view.getNormalizedView();
+
+        assertEquals("/Z/git/foo/bar", normView.getFile("foo/bar").getAbsolutePath());
+        assertEquals("/Z/foo/bar", normView.getFile("../foo/bar").getAbsolutePath());
+        assertEquals("/A/temp", normView.getFile("/A/./a/../temp").getAbsolutePath());
+        assertEquals("/Z/temp", normView.getFile("/A/../Z/temp").getAbsolutePath());
+    }
+
+    @Test
+    public void testResolveWithVirtualRootsWithBackslash() throws Exception {
+
+        Map<String, String> roots = new HashMap<String, String>();
+        roots.put("A:", "/fs/rootA");
+        roots.put("Z:", "/fs/rootZ");
+        String current = "Z:/git";
+        NativeFileSystemView view = new NativeFileSystemView("user", roots, current, '\\', false);
+
+        assertEquals("Z:\\git\\foo\\bar", view.getFile("/Z:/git/foo/bar").getAbsolutePath());
+        assertEquals("Z:\\git\\foo\\bar", view.getFile("foo/bar").getAbsolutePath());
+        assertEquals("Z:\\git\\foo", view.getFile("foo/bar").getParentFile().getAbsolutePath());
+        assertEquals("Z:\\git", view.getFile("foo/bar").getParentFile().getParentFile().getAbsolutePath());
+        assertEquals("Z:\\", view.getFile("foo/bar").getParentFile().getParentFile().getParentFile().getAbsolutePath());
+        assertFalse(view.getFile("foo/bar").getParentFile().getParentFile().getParentFile().isRemovable());
+        assertEquals("Z:\\foo\\bar", view.getFile("../foo/bar").getAbsolutePath());
+        assertEquals("A:\\temp", view.getFile("A:/./a/../temp").getAbsolutePath());
+        assertEquals("A:\\temp", view.getFile("A:/../../temp").getAbsolutePath());
+
+        FileSystemView normView = view.getNormalizedView();
+
+        assertEquals("/Z/git/foo/bar", normView.getFile("foo/bar").getAbsolutePath());
+        assertEquals("/Z/git/foo", normView.getFile("foo/bar").getParentFile().getAbsolutePath());
+        assertEquals("/Z/foo/bar", normView.getFile("../foo/bar").getAbsolutePath());
+        assertEquals("/A/temp", normView.getFile("/A/./a/../temp").getAbsolutePath());
+        assertEquals("/Z/temp", normView.getFile("/A/../Z/temp").getAbsolutePath());
+    }
+
+    @Test
+    public void testResolveWithPhysicalRoots() throws Exception {
+
+        Map<String, String> roots = new HashMap<String, String>();
+        roots.put("V:", "A:/bar");
+        roots.put("X:", "B:");
+        String current = "X:/git";
+        NativeFileSystemView view = new NativeFileSystemView("user", roots, current, '/', false);
+
+        assertEquals("X:/git/foo/bar", view.getFile("foo/bar").getAbsolutePath());
+
+        assertEquals("X:/foo/bar", view.getFile("X:/foo/bar").getAbsolutePath());
+        assertEquals(new File("B:/foo/bar").toString(), ((NativeSshFile) view.getFile("X:/foo/bar")).getNativeFile().toString());
+
+        assertEquals("X:/foo/bar", view.getFile("../foo/bar").getAbsolutePath());
+        assertEquals(new File("B:/foo/bar").toString(), ((NativeSshFile) view.getFile("../foo/bar")).getNativeFile().toString());
+
+        assertEquals("V:/temp", view.getFile("V:/./a/../temp").getAbsolutePath());
+
+        assertEquals("V:/temp", view.getFile("V:/../../temp").getAbsolutePath());
+        assertEquals(new File("A:/bar/temp").toString(), ((NativeSshFile) view.getFile("V:/../../temp")).getNativeFile().toString());
+
+        assertEquals("X:/", view.getFile("..").getAbsolutePath());
+
+        SshFile cur = view.getFile(".");
+        assertEquals("X:/git", cur.getAbsolutePath());
+        cur = view.getFile(cur, "..");
+        assertEquals("X:/", cur.getAbsolutePath());
+
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/61ebb206/sshd-core/src/test/java/org/apache/sshd/common/file/nativefs/NativeSshFileTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/file/nativefs/NativeSshFileTest.java b/sshd-core/src/test/java/org/apache/sshd/common/file/nativefs/NativeSshFileTest.java
index 0d6f77c..7ff2787 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/file/nativefs/NativeSshFileTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/file/nativefs/NativeSshFileTest.java
@@ -27,10 +27,12 @@ public class NativeSshFileTest extends BaseTest {
 
     @Test
     public void testResolve() {
-        assertEquals("/Z:/git/mina-sshd/sshd-core/target/scp/remote/out.txt",
-                NativeSshFile.getPhysicalName("/", "Z:\\git\\mina-sshd\\sshd-core", "Z:\\git\\mina-sshd\\sshd-core\\target\\scp\\remote\\out.txt", false));
-        assertEquals("/Z:/git/mina-sshd/sshd-core/target/scp/remote/out.txt",
-                NativeSshFile.getPhysicalName("/", "Z:\\git\\mina-sshd\\sshd-core", "/Z:/git/mina-sshd/sshd-core/target/scp/remote/out.txt", false));
+        assertEquals("Z:/git/mina-sshd/sshd-core/target/scp/remote/out.txt",
+                NativeSshFile.getPhysicalName("Z:\\git/", "Z:\\git\\mina-sshd\\sshd-core", "\\mina-sshd\\sshd-core\\target\\scp\\remote\\out.txt", false));
+        assertEquals("Z:/git/mina-sshd/sshd-core/target/scp/remote/out.txt",
+                NativeSshFile.getPhysicalName("Z:/", "Z:\\git\\mina-sshd\\sshd-core", "\\git\\mina-sshd\\sshd-core\\target\\scp\\remote\\out.txt", false));
+        assertEquals("Z:/git/mina-sshd/sshd-core/target/scp/remote/out.txt",
+                NativeSshFile.getPhysicalName("Z:/", "Z:\\git\\mina-sshd\\sshd-core", "/git/mina-sshd/sshd-core/target/scp/remote/out.txt", false));
 
         assertEquals("/bar", NativeSshFile.getPhysicalName("/", "/foo", "/bar", false));
         assertEquals("/bar", NativeSshFile.getPhysicalName("/", "/", "/bar", false));


[2/2] git commit: Improve tests on slow machines and on windows

Posted by gn...@apache.org.
Improve tests on slow machines and on windows

Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/441bdb91
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/441bdb91
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/441bdb91

Branch: refs/heads/master
Commit: 441bdb91915ede93670487c21bf80ee7e9571272
Parents: 61ebb20
Author: Guillaume Nodet <gn...@apache.org>
Authored: Tue Apr 8 13:03:37 2014 +0200
Committer: Guillaume Nodet <gn...@apache.org>
Committed: Tue Apr 8 13:03:37 2014 +0200

----------------------------------------------------------------------
 sshd-core/src/test/java/org/apache/sshd/KeepAliveTest.java     | 6 +++---
 sshd-core/src/test/java/org/apache/sshd/ServerTest.java        | 4 ++--
 sshd-core/src/test/java/org/apache/sshd/SftpTest.java          | 5 +++++
 .../src/test/java/org/apache/sshd/SinglePublicKeyAuthTest.java | 1 +
 4 files changed, 11 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/441bdb91/sshd-core/src/test/java/org/apache/sshd/KeepAliveTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/KeepAliveTest.java b/sshd-core/src/test/java/org/apache/sshd/KeepAliveTest.java
index a652d42..3a5c496 100644
--- a/sshd-core/src/test/java/org/apache/sshd/KeepAliveTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/KeepAliveTest.java
@@ -45,9 +45,9 @@ public class KeepAliveTest extends BaseTest {
     private SshServer sshd;
     private int port;
 
-    private int heartbeat = 1000;
-    private int timeout = 2000;
-    private int wait = 4000;
+    private int heartbeat = 2000;
+    private int timeout = 4000;
+    private int wait = 8000;
 
     @Before
     public void setUp() throws Exception {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/441bdb91/sshd-core/src/test/java/org/apache/sshd/ServerTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/ServerTest.java b/sshd-core/src/test/java/org/apache/sshd/ServerTest.java
index 4f4e496..e79fe23 100644
--- a/sshd-core/src/test/java/org/apache/sshd/ServerTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/ServerTest.java
@@ -132,12 +132,12 @@ public class ServerTest extends BaseTest {
 
     @Test
     public void testAuthenticationTimeout() throws Exception {
-        sshd.getProperties().put(SshServer.AUTH_TIMEOUT, "1000");
+        sshd.getProperties().put(SshServer.AUTH_TIMEOUT, "5000");
 
         client = SshClient.setUpDefaultClient();
         client.start();
         ClientSession s = client.connect("localhost", port).await().getSession();
-        int res = s.waitFor(ClientSession.CLOSED, 5000);
+        int res = s.waitFor(ClientSession.CLOSED, 10000);
         assertEquals("Session should be closed", ClientSession.CLOSED | ClientSession.WAIT_AUTH, res);
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/441bdb91/sshd-core/src/test/java/org/apache/sshd/SftpTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/SftpTest.java b/sshd-core/src/test/java/org/apache/sshd/SftpTest.java
index 256fa85..7d3a95e 100644
--- a/sshd-core/src/test/java/org/apache/sshd/SftpTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/SftpTest.java
@@ -237,6 +237,11 @@ public class SftpTest extends BaseTest {
 
     @Test
     public void testCreateSymbolicLink() throws Exception {
+        // Do not execute on windows as the file system does not support symlinks
+        if (System.getProperty("os.name").toLowerCase().contains("win")) {
+            return;
+        }
+
         File root = new File("target/sftp");
         String unixPath = "target/sftp/out.txt";
         String linkUnixPath = "target/sftp/link.txt";

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/441bdb91/sshd-core/src/test/java/org/apache/sshd/SinglePublicKeyAuthTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/SinglePublicKeyAuthTest.java b/sshd-core/src/test/java/org/apache/sshd/SinglePublicKeyAuthTest.java
index 29efe15..43fb8bf 100644
--- a/sshd-core/src/test/java/org/apache/sshd/SinglePublicKeyAuthTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/SinglePublicKeyAuthTest.java
@@ -107,6 +107,7 @@ public class SinglePublicKeyAuthTest extends BaseTest {
         assertEquals(1, count.get(KeyUtils.getFingerPrint(pairRsaBad.getPublic())).get());
         assertEquals(1, count.get(KeyUtils.getFingerPrint(pairRsa.getPublic())).get());
         client.close(false).await();
+        Thread.sleep(100);
         assertTrue(auth.getCache().isEmpty());
     }