This is an automated email from the ASF dual-hosted git repository.

gnodet pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git

commit 1ffbbf2023f90ef8a6f8d63f70d810a7a3f43633
Author: Guillaume Nodet <gno...@gmail.com>
AuthorDate: Mon Jun 8 10:05:34 2020 +0200

    [SSHD-1009] Fix users home on OSX and support for symlinks
---
 .../file/nativefs/NativeFileSystemFactory.java     |  2 +-
 .../java/org/apache/sshd/common/util/OsUtils.java  | 52 ++++++++++++--------
 .../org/apache/sshd/common/util/OsUtilsTest.java   | 24 +++++++---
 .../java/org/apache/sshd/server/scp/ScpShell.java  | 56 ++++++++++++++++------
 4 files changed, 93 insertions(+), 41 deletions(-)

diff --git 
a/sshd-common/src/main/java/org/apache/sshd/common/file/nativefs/NativeFileSystemFactory.java
 
b/sshd-common/src/main/java/org/apache/sshd/common/file/nativefs/NativeFileSystemFactory.java
index 784fc94..6d61105 100644
--- 
a/sshd-common/src/main/java/org/apache/sshd/common/file/nativefs/NativeFileSystemFactory.java
+++ 
b/sshd-common/src/main/java/org/apache/sshd/common/file/nativefs/NativeFileSystemFactory.java
@@ -41,7 +41,7 @@ import 
org.apache.sshd.common.util.logging.AbstractLoggingBean;
  * @author <a href="http://mina.apache.org";>Apache MINA Project</a>
  */
 public class NativeFileSystemFactory extends AbstractLoggingBean implements 
FileSystemFactory {
-    public static final String DEFAULT_USERS_HOME = OsUtils.isWin32() ? 
"C:\\Users" : "/home";
+    public static final String DEFAULT_USERS_HOME = OsUtils.isWin32() ? 
"C:\\Users" : OsUtils.isOSX() ? "/Users" : "/home";
 
     public static final NativeFileSystemFactory INSTANCE = new 
NativeFileSystemFactory();
 
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/OsUtils.java 
b/sshd-common/src/main/java/org/apache/sshd/common/util/OsUtils.java
index 2f5d8f9..182afe0 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/util/OsUtils.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/OsUtils.java
@@ -60,7 +60,7 @@ public final class OsUtils {
 
     private static final AtomicReference<String> CURRENT_USER_HOLDER = new 
AtomicReference<>(null);
     private static final AtomicReference<VersionInfo> JAVA_VERSION_HOLDER = 
new AtomicReference<>(null);
-    private static final AtomicReference<Boolean> OS_TYPE_HOLDER = new 
AtomicReference<>(null);
+    private static final AtomicReference<String> OS_TYPE_HOLDER = new 
AtomicReference<>(null);
 
     private OsUtils() {
         throw new UnsupportedOperationException("No instance allowed");
@@ -70,42 +70,56 @@ public final class OsUtils {
      * @return true if the host is a UNIX system (and not Windows).
      */
     public static boolean isUNIX() {
-        return !isWin32();
+        return !isWin32() && !isOSX();
+    }
+
+    /**
+     * @return true if the host is a OSX (and not Windows or Unix).
+     */
+    public static boolean isOSX() {
+        return getOS().contains("mac");
     }
 
     /**
      * @return true if the host is Windows (and not UNIX).
      * @see    #OS_TYPE_OVERRIDE_PROP
-     * @see    #setWin32(Boolean)
+     * @see    #setOS(String)
      */
     public static boolean isWin32() {
-        Boolean typeValue;
+        return getOS().contains("windows");
+    }
+
+    /**
+     * Can be used to enforce Win32 or Linux report from {@link #isWin32()}, 
{@link #isOSX()} or {@link #isUNIX()}
+     * 
+     * @param os The value to set - if {@code null} then O/S type is 
auto-detected
+     * @see      #isWin32()
+     * @see      #isOSX()
+     * @see      #isUNIX()
+     */
+    public static void setOS(String os) {
+        synchronized (OS_TYPE_HOLDER) {
+            OS_TYPE_HOLDER.set(os);
+        }
+    }
+
+    /**
+     * Retrieve the OS
+     */
+    private static String getOS() {
+        String typeValue;
         synchronized (OS_TYPE_HOLDER) {
             typeValue = OS_TYPE_HOLDER.get();
             if (typeValue != null) { // is it the 1st time
                 return typeValue;
             }
-
             String value = System.getProperty(OS_TYPE_OVERRIDE_PROP, 
System.getProperty("os.name"));
-            typeValue = 
GenericUtils.trimToEmpty(value).toLowerCase().contains("windows");
+            typeValue = GenericUtils.trimToEmpty(value).toLowerCase();
             OS_TYPE_HOLDER.set(typeValue);
         }
-
         return typeValue;
     }
 
-    /**
-     * Can be used to enforce Win32 or Linux report from {@link #isWin32()} or 
{@link #isUNIX()}
-     * 
-     * @param win32 The value to set - if {@code null} then O/S type is 
auto-detected
-     * @see         #isWin32()
-     */
-    public static void setWin32(Boolean win32) {
-        synchronized (OS_TYPE_HOLDER) {
-            OS_TYPE_HOLDER.set(win32);
-        }
-    }
-
     public static String resolveDefaultInteractiveShellCommand() {
         return resolveDefaultInteractiveShellCommand(isWin32());
     }
diff --git 
a/sshd-common/src/test/java/org/apache/sshd/common/util/OsUtilsTest.java 
b/sshd-common/src/test/java/org/apache/sshd/common/util/OsUtilsTest.java
index 3fb66a5..5470d0f 100644
--- a/sshd-common/src/test/java/org/apache/sshd/common/util/OsUtilsTest.java
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/OsUtilsTest.java
@@ -42,7 +42,7 @@ public class OsUtilsTest extends JUnitTestSupport {
     public void testSetOsTypeByProperty() {
         try {
             for (String osType : new String[] { "Some-Windows", "Some-Linux" 
}) {
-                OsUtils.setWin32(null); // force re-detection
+                OsUtils.setOS(null); // force re-detection
 
                 try {
                     boolean expected = osType.contains("Windows");
@@ -54,19 +54,29 @@ public class OsUtilsTest extends JUnitTestSupport {
                 }
             }
         } finally {
-            OsUtils.setWin32(null); // force re-detection
+            OsUtils.setOS(null); // force re-detection
         }
     }
 
     @Test
     public void testSetOsTypeProgrammatically() {
         try {
-            for (boolean expected : new boolean[] { true, false }) {
-                OsUtils.setWin32(expected); // force value
-                assertEquals("Mismatched detection value", expected, 
OsUtils.isWin32());
-            }
+            OsUtils.setOS("windows 10");
+            assertEquals("Mismatched detection value", false, OsUtils.isOSX());
+            assertEquals("Mismatched detection value", false, 
OsUtils.isUNIX());
+            assertEquals("Mismatched detection value", true, 
OsUtils.isWin32());
+
+            OsUtils.setOS("mac os");
+            assertEquals("Mismatched detection value", true, OsUtils.isOSX());
+            assertEquals("Mismatched detection value", false, 
OsUtils.isUNIX());
+            assertEquals("Mismatched detection value", false, 
OsUtils.isWin32());
+
+            OsUtils.setOS("linux");
+            assertEquals("Mismatched detection value", false, OsUtils.isOSX());
+            assertEquals("Mismatched detection value", true, OsUtils.isUNIX());
+            assertEquals("Mismatched detection value", false, 
OsUtils.isWin32());
         } finally {
-            OsUtils.setWin32(null); // force re-detection
+            OsUtils.setOS(null); // force re-detection
         }
     }
 
diff --git a/sshd-scp/src/main/java/org/apache/sshd/server/scp/ScpShell.java 
b/sshd-scp/src/main/java/org/apache/sshd/server/scp/ScpShell.java
index c215986..669e2aa 100644
--- a/sshd-scp/src/main/java/org/apache/sshd/server/scp/ScpShell.java
+++ b/sshd-scp/src/main/java/org/apache/sshd/server/scp/ScpShell.java
@@ -27,6 +27,7 @@ import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.FileSystem;
 import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
 import java.nio.file.Path;
 import java.nio.file.attribute.FileTime;
 import java.nio.file.attribute.PosixFilePermission;
@@ -142,8 +143,7 @@ public class ScpShell extends AbstractFileSystemCommand {
         super.setFileSystemFactory(factory, session);
     }
 
-    protected void println(
-            String cmd, Object x, OutputStream out, Charset cs) {
+    protected void println(String cmd, Object x, OutputStream out, Charset cs) 
{
         try {
             String s = x.toString();
             if (log.isDebugEnabled()) {
@@ -603,8 +603,10 @@ public class ScpShell extends AbstractFileSystemCommand {
     protected void ls(String[] argv) throws Exception {
         // find options
         boolean optListAll = false;
+        boolean optDirAsPlain = false;
         boolean optLong = false;
         boolean optFullTime = false;
+        String path = null;
         for (int k = 1; k < argv.length; k++) {
             String argValue = argv[k];
             if (GenericUtils.isEmpty(argValue)) {
@@ -628,6 +630,9 @@ public class ScpShell extends AbstractFileSystemCommand {
                         case 'a':
                             optListAll = true;
                             break;
+                        case 'd':
+                            optDirAsPlain = true;
+                            break;
                         case 'l':
                             optLong = true;
                             break;
@@ -636,17 +641,19 @@ public class ScpShell extends AbstractFileSystemCommand {
                             return;
                     }
                 }
+            } else if (path == null) {
+                path = argValue;
             } else {
                 signalError(argv[0], "unsupported option: " + argValue);
                 return;
             }
         }
 
-        doLs(argv, optListAll, optLong, optFullTime);
+        doLs(argv[0], path, optListAll, optLong, optFullTime);
     }
 
     protected void doLs(
-            String argv[], boolean optListAll, boolean optLong, boolean 
optFullTime)
+            String cmd, String path, boolean optListAll, boolean optLong, 
boolean optFullTime)
             throws Exception {
         // list current directory content
         Predicate<Path> filter = p -> {
@@ -656,15 +663,26 @@ public class ScpShell extends AbstractFileSystemCommand {
         };
 
         // TODO make sure not listing above user's home directory
-        String[] synth = currentDir.toString().equals("/") ? new String[] { 
"." } : new String[] { ".", ".." };
+        Stream<Path> files = path != null
+                ? Stream.of(currentDir.resolve(path))
+                : Stream.concat(Stream.of(".", "..").map(currentDir::resolve), 
Files.list(currentDir));
         OutputStream stdout = getOutputStream();
-        Stream.concat(Stream.of(synth).map(currentDir::resolve), 
Files.list(currentDir))
+        OutputStream stderr = getErrorStream();
+        variables.put(STATUS, 0);
+        files
                 .filter(filter)
                 .map(p -> new PathEntry(p, currentDir))
                 .sorted()
-                .map(p -> p.display(optLong, optFullTime))
-                .forEach(str -> println(argv[0], str, stdout, 
nameEncodingCharset));
-        variables.put(STATUS, 0);
+                .forEach(p -> {
+                    try {
+                        String str = p.display(optLong, optFullTime);
+                        println(cmd, str, stdout, nameEncodingCharset);
+                    } catch (NoSuchFileException e) {
+                        println(cmd, cmd + ": " + p.path.toString() + ": no 
such file or directory", stderr,
+                                nameEncodingCharset);
+                        variables.put(STATUS, 1);
+                    }
+                });
     }
 
     protected static class PathEntry implements Comparable<PathEntry> {
@@ -692,7 +710,11 @@ public class ScpShell extends AbstractFileSystemCommand {
             return Objects.toString(abs);
         }
 
-        public String display(boolean optLongDisplay, boolean optFullTime) {
+        public String display(boolean optLongDisplay, boolean optFullTime) 
throws NoSuchFileException {
+            if (attributes.isEmpty()) {
+                throw new NoSuchFileException(path.toString());
+            }
+
             String abbrev = shortDisplay();
             if (!optLongDisplay) {
                 return abbrev;
@@ -765,7 +787,11 @@ public class ScpShell extends AbstractFileSystemCommand {
                     // ignore
                 }
             }
-            return path.toString();
+            String str = path.toString();
+            if (str.isEmpty()) {
+                return abs.getFileName().toString();
+            }
+            return str;
         }
 
         protected static String toString(FileTime time, boolean optFullTime) {
@@ -791,14 +817,16 @@ public class ScpShell extends AbstractFileSystemCommand {
             for (String view : views) {
                 try {
                     Map<String, Object> ta = Files.readAttributes(
-                            path, view + ":*", IoUtils.EMPTY_LINK_OPTIONS);
+                            path, view + ":*", IoUtils.getLinkOptions(false));
                     ta.forEach(attrs::putIfAbsent);
                 } catch (IOException e) {
                     // Ignore
                 }
             }
-            attrs.computeIfAbsent("isExecutable", s -> 
Files.isExecutable(path));
-            attrs.computeIfAbsent("permissions", s -> 
IoUtils.getPermissionsFromFile(path.toFile()));
+            if (!attrs.isEmpty()) {
+                attrs.computeIfAbsent("isExecutable", s -> 
Files.isExecutable(path));
+                attrs.computeIfAbsent("permissions", s -> 
IoUtils.getPermissionsFromFile(path.toFile()));
+            }
             return attrs;
         }
     }

Reply via email to