From a4cfd1ce74eff609db27b3b82038af4cbdacf2f1 Mon Sep 17 00:00:00 2001 From: Sergei Vorobev Date: Thu, 14 Jul 2016 12:45:45 -0700 Subject: [PATCH 01/42] Add a note about produced binaries for FullCLR --- docs/building/windows-full.md | 39 +++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/docs/building/windows-full.md b/docs/building/windows-full.md index f75060cddb..7e294ad56a 100644 --- a/docs/building/windows-full.md +++ b/docs/building/windows-full.md @@ -57,16 +57,38 @@ output will *not* have a runtime identifier in the path. Thus the output location of `powershell.exe` will be `./src/Microsoft.PowerShell.ConsoleHost/bin/Debug/net451/powershell.exe` -While building is easy, running FullCLR version is not as simple as -CoreCLR version. +Build manually +============== + +The build contains the following steps: + +- generating Visual Studio project: `cmake` +- building `powershell.exe` from generated solution: `msbuild + powershell.sln` +- building managed DLLs: `dotnet publish --runtime net451` + + +What I can do with the produced binaries? +========================================= + +Creating a deployable package out of them is **not a supported scenario**. + +The reason why we are building these binaries is +we have components (i.e. workflows) that are not currently available in the CoreClr version. +We want to make sure that CoreClr PowerShell changes don't introduce regressions in FullClr PowerShelll. + +It's possible to run (for test purposes) the dev version of these binaries as follows. + +Running Dev version of FullClr PowerShell +----------------------------------------- + +Running FullCLR version is not as simple as CoreCLR version. If you just run `./powershell.exe`, you will get a `powershell` process, but all the interesting DLLs (such as `System.Management.Automation.dll`) would be loaded from the Global Assembly Cache (GAC), not your output directory. -[@lzybkr](https://github.com/lzybkr) wrote a module to deal with it -and run side-by-side. ```powershell Start-DevPowerShell @@ -88,12 +110,3 @@ Start-DevPowerShell -binDir (Split-Path -Parent (Get-PSOutput)) The default for produced `powershell.exe` is x64. You can control it with `Start-PSBuild -FullCLR -NativeHostArch x86` -Build manually -============== - -The build contains the following steps: - -- generating Visual Studio project: `cmake` -- building `powershell.exe` from generated solution: `msbuild - powershell.sln` -- building managed DLLs: `dotnet publish --runtime net451` From 3cd396e6e6c1710b35c9931e24f6ae8aa5cc9202 Mon Sep 17 00:00:00 2001 From: Sergei Vorobev Date: Thu, 14 Jul 2016 12:48:34 -0700 Subject: [PATCH 02/42] Correct a note about produced binaries for FullCLR --- docs/building/windows-full.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/building/windows-full.md b/docs/building/windows-full.md index 7e294ad56a..f9c7e23fcf 100644 --- a/docs/building/windows-full.md +++ b/docs/building/windows-full.md @@ -89,6 +89,7 @@ process, but all the interesting DLLs (such as `System.Management.Automation.dll`) would be loaded from the Global Assembly Cache (GAC), not your output directory. +Use `Start-DevPowerShell` helper funciton, to workaround it with `$env:DEVPATH` ```powershell Start-DevPowerShell From 955d5f8bcbb38ba184b3f6ac8559ccb010ce852c Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Wed, 13 Jul 2016 20:27:34 -0700 Subject: [PATCH 03/42] Add bool IsDirectory(char*) to libpsl-native --- src/libpsl-native/src/CMakeLists.txt | 1 + src/libpsl-native/src/isdirectory.cpp | 51 +++++++++++++++++++++ src/libpsl-native/src/isdirectory.h | 10 ++++ src/libpsl-native/test/CMakeLists.txt | 1 + src/libpsl-native/test/test-isdirectory.cpp | 31 +++++++++++++ 5 files changed, 94 insertions(+) create mode 100644 src/libpsl-native/src/isdirectory.cpp create mode 100644 src/libpsl-native/src/isdirectory.h create mode 100644 src/libpsl-native/test/test-isdirectory.cpp diff --git a/src/libpsl-native/src/CMakeLists.txt b/src/libpsl-native/src/CMakeLists.txt index 8b3d0f19a9..8fa6f89ec9 100644 --- a/src/libpsl-native/src/CMakeLists.txt +++ b/src/libpsl-native/src/CMakeLists.txt @@ -8,6 +8,7 @@ add_library(psl-native SHARED getcomputername.cpp getlinkcount.cpp getfullyqualifiedname.cpp + isdirectory.cpp issymlink.cpp isexecutable.cpp setdate.cpp diff --git a/src/libpsl-native/src/isdirectory.cpp b/src/libpsl-native/src/isdirectory.cpp new file mode 100644 index 0000000000..01ca0d9bb1 --- /dev/null +++ b/src/libpsl-native/src/isdirectory.cpp @@ -0,0 +1,51 @@ +//! @file isdirectory.cpp +//! @author Andrew Schwartzmeyer +//! @brief returns if the path is a directory + +#include +#include +#include +#include +#include +#include +#include "getstat.h" +#include "getpwuid.h" +#include "getfileowner.h" +#include "isdirectory.h" + +//! @brief returns if the path is a directory; uses stat and so follows symlinks +//! +//! IsDirectory +//! +//! @param[in] path +//! @parblock +//! A pointer to the buffer that contains the file name +//! +//! char* is marshaled as an LPStr, which on Linux is UTF-8. +//! @endparblock +//! +//! @exception errno Passes this error via errno to GetLastError: +//! - ERROR_INVALID_PARAMETER: parameter is not valid +//! +//! @retval true if directory, false otherwise +//! +bool IsDirectory(const char* path) +{ + int32_t ret = 0; + errno = 0; + + if (!path) + { + errno = ERROR_INVALID_PARAMETER; + return false; + } + + struct stat buf; + ret = GetStat(path, &buf); + if (ret != 0) + { + return false; + } + + return S_ISDIR(buf.st_mode); +} diff --git a/src/libpsl-native/src/isdirectory.h b/src/libpsl-native/src/isdirectory.h new file mode 100644 index 0000000000..746d1c5793 --- /dev/null +++ b/src/libpsl-native/src/isdirectory.h @@ -0,0 +1,10 @@ +#pragma once + +#include "pal.h" +#include + +PAL_BEGIN_EXTERNC + +bool IsDirectory(const char* path); + +PAL_END_EXTERNC diff --git a/src/libpsl-native/test/CMakeLists.txt b/src/libpsl-native/test/CMakeLists.txt index ed4440d2a3..d3859cb7bd 100644 --- a/src/libpsl-native/test/CMakeLists.txt +++ b/src/libpsl-native/test/CMakeLists.txt @@ -9,6 +9,7 @@ add_executable(psl-native-test test-getcomputername.cpp test-getlinkcount.cpp test-getfullyqualifiedname.cpp + test-isdirectory.cpp test-issymlink.cpp test-isexecutable.cpp test-createsymlink.cpp diff --git a/src/libpsl-native/test/test-isdirectory.cpp b/src/libpsl-native/test/test-isdirectory.cpp new file mode 100644 index 0000000000..27c840cba7 --- /dev/null +++ b/src/libpsl-native/test/test-isdirectory.cpp @@ -0,0 +1,31 @@ +//! @file test-isdirectory.cpp +//! @author Andrew Schwartzmeyer +//! @brief Tests IsDirectory + +#include +#include +#include +#include "isdirectory.h" + +TEST(IsDirectoryTest, RootIsDirectory) +{ + EXPECT_TRUE(IsDirectory("/")); +} + +TEST(IsDirectoryTest, BinLsIsNotDirectory) +{ + EXPECT_FALSE(IsDirectory("/bin/ls")); +} + + +TEST(IsDirectoryTest, ReturnsFalseForFakeDirectory) +{ + EXPECT_FALSE(IsDirectory("SomeMadeUpFileNameThatDoesNotExist")); + EXPECT_EQ(errno, ERROR_FILE_NOT_FOUND); +} + +TEST(IsDirectoryTest, ReturnsFalseForNullInput) +{ + EXPECT_FALSE(IsDirectory(NULL)); + EXPECT_EQ(errno, ERROR_INVALID_PARAMETER); +} From 3dacf45789d76d64606adddd420d35759ea743a0 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Wed, 13 Jul 2016 20:34:03 -0700 Subject: [PATCH 04/42] Revert NativeItemExists changes from baseline --- .../engine/Utils.cs | 86 +------------------ 1 file changed, 4 insertions(+), 82 deletions(-) diff --git a/src/System.Management.Automation/engine/Utils.cs b/src/System.Management.Automation/engine/Utils.cs index 8dd8f96bea..4cd8002989 100644 --- a/src/System.Management.Automation/engine/Utils.cs +++ b/src/System.Management.Automation/engine/Utils.cs @@ -984,38 +984,9 @@ namespace System.Management.Automation return NativeItemExists(path, out unusedIsDirectory, out unusedException); } + // This is done through P/Invoke since File.Exists and Directory.Exists pay 13% performance degradation + // through the CAS checks, and are terribly slow for network paths. internal static bool NativeItemExists(string path, out bool isDirectory, out Exception exception) - { - if (Platform.UseDotNetToQueryFileAttributes()) - { - // TODO:PSL replace by System.IO.File.GetAttributes - exception = null; - if (NativeDirectoryExists(path)) - { - isDirectory = true; - return true; - } - else if (NativeFileExists(path)) - { - isDirectory = false; - return true; - } - else - { - isDirectory = false; - return false; - } - } - else - { - // This is done through P/Invoke since File.Exists and Directory.Exists - // pay 13% performance degradation through the CAS checks, and are - // terribly slow for network paths. - return WinNativeItemExists(path,out isDirectory,out exception); - } - } - - internal static bool WinNativeItemExists(string path, out bool isDirectory, out Exception exception) { exception = null; @@ -1059,21 +1030,9 @@ namespace System.Management.Automation return true; } + // This is done through P/Invoke since we pay 13% performance degradation + // through the CAS checks required by File.Exists and Directory.Exists internal static bool NativeFileExists(string path) - { - if (Platform.UseDotNetToQueryFileAttributes()) - { - return System.IO.File.Exists(path); - } - else - { - // This is done through P/Invoke since we pay 13% performance degradation - // through the CAS checks required by File.Exists and Directory.Exists - return WinNativeFileExists(path); - } - } - - internal static bool WinNativeFileExists(string path) { bool isDirectory; Exception ioException; @@ -1090,18 +1049,6 @@ namespace System.Management.Automation // This is done through P/Invoke since we pay 13% performance degradation // through the CAS checks required by File.Exists and Directory.Exists internal static bool NativeDirectoryExists(string path) - { - if (Platform.UseDotNetToQueryFileAttributes()) - { - return System.IO.Directory.Exists(path); - } - else - { - return WinNativeDirectoryExists(path); - } - } - - internal static bool WinNativeDirectoryExists(string path) { bool isDirectory; Exception ioException; @@ -1116,31 +1063,6 @@ namespace System.Management.Automation } internal static void NativeEnumerateDirectory(string directory, out List directories, out List files) - { - if (Platform.UseDotNetToQueryFileAttributes()) - { - IEnumerable fileEnum = System.IO.Directory.EnumerateFiles(directory); - IEnumerable dirEnum = System.IO.Directory.EnumerateDirectories(directory); - - files = new List(); - directories = new List(); - - foreach (string entry in fileEnum) - { - files.Add(directory + "/" + entry); - } - foreach (string entry in dirEnum) - { - directories.Add(directory + "/" + entry); - } - } - else - { - WinNativeEnumerateDirectory(directory, out directories, out files); - } - } - - internal static void WinNativeEnumerateDirectory(string directory, out List directories, out List files) { IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); NativeMethods.WIN32_FIND_DATA findData; From 90a5382b30053019324a5d591c66073b41994a45 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Wed, 13 Jul 2016 20:50:00 -0700 Subject: [PATCH 05/42] Add bool IsFile(char*) to libpsl-native --- src/libpsl-native/src/CMakeLists.txt | 1 + src/libpsl-native/src/isfile.cpp | 40 ++++++++++++++++++++++++++ src/libpsl-native/src/isfile.h | 10 +++++++ src/libpsl-native/test/CMakeLists.txt | 1 + src/libpsl-native/test/test-isfile.cpp | 30 +++++++++++++++++++ 5 files changed, 82 insertions(+) create mode 100644 src/libpsl-native/src/isfile.cpp create mode 100644 src/libpsl-native/src/isfile.h create mode 100644 src/libpsl-native/test/test-isfile.cpp diff --git a/src/libpsl-native/src/CMakeLists.txt b/src/libpsl-native/src/CMakeLists.txt index 8fa6f89ec9..22e8f5dd85 100644 --- a/src/libpsl-native/src/CMakeLists.txt +++ b/src/libpsl-native/src/CMakeLists.txt @@ -8,6 +8,7 @@ add_library(psl-native SHARED getcomputername.cpp getlinkcount.cpp getfullyqualifiedname.cpp + isfile.cpp isdirectory.cpp issymlink.cpp isexecutable.cpp diff --git a/src/libpsl-native/src/isfile.cpp b/src/libpsl-native/src/isfile.cpp new file mode 100644 index 0000000000..b2cead6cd7 --- /dev/null +++ b/src/libpsl-native/src/isfile.cpp @@ -0,0 +1,40 @@ +//! @file isfile.cpp +//! @author Andrew Schwartzmeyer +//! @brief returns if the path exists + +#include +#include +#include +#include +#include +#include +#include "isfile.h" + +//! @brief returns if the path is a file or directory +//! +//! IsFile +//! +//! @param[in] path +//! @parblock +//! A pointer to the buffer that contains the file name +//! +//! char* is marshaled as an LPStr, which on Linux is UTF-8. +//! @endparblock +//! +//! @exception errno Passes this error via errno to GetLastError: +//! - ERROR_INVALID_PARAMETER: parameter is not valid +//! +//! @retval true if path exists, false otherwise +//! +bool IsFile(const char* path) +{ + errno = 0; + + if (!path) + { + errno = ERROR_INVALID_PARAMETER; + return false; + } + + return access(path, F_OK) != -1; +} diff --git a/src/libpsl-native/src/isfile.h b/src/libpsl-native/src/isfile.h new file mode 100644 index 0000000000..53a15f97d3 --- /dev/null +++ b/src/libpsl-native/src/isfile.h @@ -0,0 +1,10 @@ +#pragma once + +#include "pal.h" +#include + +PAL_BEGIN_EXTERNC + +bool IsFile(const char* path); + +PAL_END_EXTERNC diff --git a/src/libpsl-native/test/CMakeLists.txt b/src/libpsl-native/test/CMakeLists.txt index d3859cb7bd..2f57741b0c 100644 --- a/src/libpsl-native/test/CMakeLists.txt +++ b/src/libpsl-native/test/CMakeLists.txt @@ -10,6 +10,7 @@ add_executable(psl-native-test test-getlinkcount.cpp test-getfullyqualifiedname.cpp test-isdirectory.cpp + test-isfile.cpp test-issymlink.cpp test-isexecutable.cpp test-createsymlink.cpp diff --git a/src/libpsl-native/test/test-isfile.cpp b/src/libpsl-native/test/test-isfile.cpp new file mode 100644 index 0000000000..09d72f5bfa --- /dev/null +++ b/src/libpsl-native/test/test-isfile.cpp @@ -0,0 +1,30 @@ +//! @file test-isfile.cpp +//! @author Andrew Schwartzmeyer +//! @brief Tests Isfile + +#include +#include +#include +#include "isfile.h" + +TEST(IsFileTest, RootIsFile) +{ + EXPECT_TRUE(IsFile("/")); +} + +TEST(IsFileTest, BinLsIsFile) +{ + EXPECT_TRUE(IsFile("/bin/ls")); +} + +TEST(IsFileTest, CannotGetOwnerOfFakeFile) +{ + EXPECT_FALSE(IsFile("SomeMadeUpFileNameThatDoesNotExist")); + EXPECT_EQ(errno, ERROR_FILE_NOT_FOUND); +} + +TEST(IsFileTest, ReturnsFalseForNullInput) +{ + EXPECT_FALSE(IsFile(NULL)); + EXPECT_EQ(errno, ERROR_INVALID_PARAMETER); +} From 77a9b4b5d22c377e1f637a574d76b073772f45cd Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Wed, 13 Jul 2016 21:02:00 -0700 Subject: [PATCH 06/42] Remove unnecessary throws in CorePsPlatform --- .../CoreCLR/CorePsPlatform.cs | 164 ++++-------------- 1 file changed, 35 insertions(+), 129 deletions(-) diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index eb9d3b2624..5a4dfccb69 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -318,22 +318,12 @@ namespace System.Management.Automation internal static bool NonWindowsIsHardLink(FileSystemInfo fileInfo) { - if (!IsWindows) - { - return LinuxPlatform.IsHardLink(fileInfo); - } - - throw new PlatformNotSupportedException(); + return LinuxPlatform.IsHardLink(fileInfo); } internal static bool NonWindowsIsSymLink(FileSystemInfo fileInfo) { - if (!IsWindows) - { - return LinuxPlatform.IsSymLink(fileInfo); - } - - throw new PlatformNotSupportedException(); + return LinuxPlatform.IsSymLink(fileInfo); } internal static string NonWindowsInternalGetTarget(SafeFileHandle handle) @@ -344,166 +334,91 @@ namespace System.Management.Automation internal static string NonWindowsInternalGetTarget(string path) { - if (!IsWindows) - { - return LinuxPlatform.FollowSymLink(path); - } - else - { - throw new PlatformNotSupportedException(); - } + return LinuxPlatform.FollowSymLink(path); } internal static string NonWindowsGetUserFromPid(int path) { - if (!IsWindows) - { - return LinuxPlatform.GetUserFromPid(path); - } - else - { - throw new PlatformNotSupportedException(); - } + return LinuxPlatform.GetUserFromPid(path); } #if CORECLR internal static string NonWindowsGetFolderPath(SpecialFolder folder) { - if (!IsWindows) - { - return LinuxPlatform.GetFolderPath(folder); - } - else - { - throw new PlatformNotSupportedException(); - } + return LinuxPlatform.GetFolderPath(folder); } #endif internal static string NonWindowsInternalGetLinkType(FileSystemInfo fileInfo) { - if (!IsWindows) + if (NonWindowsIsSymLink(fileInfo)) { - if (NonWindowsIsSymLink(fileInfo)) - { - return "SymbolicLink"; - } - if (NonWindowsIsHardLink(fileInfo)) - { - return "HardLink"; - } - return null; - } - else - { - throw new PlatformNotSupportedException(); + return "SymbolicLink"; } + if (NonWindowsIsHardLink(fileInfo)) + { + return "HardLink"; + } + + return null; } internal static bool NonWindowsCreateSymbolicLink(string path, string strTargetPath, bool isDirectory) { - if (!IsWindows) - { - // Linux doesn't care if target is a directory or not - return LinuxPlatform.CreateSymbolicLink(path, strTargetPath); - } - else - { - throw new PlatformNotSupportedException(); - } + // Linux doesn't care if target is a directory or not + return LinuxPlatform.CreateSymbolicLink(path, strTargetPath); } internal static bool NonWindowsCreateHardLink(string path, string strTargetPath) { - if (!IsWindows) - { - return LinuxPlatform.CreateHardLink(path, strTargetPath); - } - else - { - throw new PlatformNotSupportedException(); - } + return LinuxPlatform.CreateHardLink(path, strTargetPath); } internal static void NonWindowsSetDate(DateTime dateToUse) { - if (!IsWindows) - { - LinuxPlatform.SetDateInfoInternal date = new LinuxPlatform.SetDateInfoInternal(dateToUse); - LinuxPlatform.SetDate(date); - } - else - { - throw new PlatformNotSupportedException(); - } + LinuxPlatform.SetDateInfoInternal date = new LinuxPlatform.SetDateInfoInternal(dateToUse); + LinuxPlatform.SetDate(date); } internal static string NonWindowsGetDomainName() { - if (!IsWindows) + string fullyQualifiedName = LinuxPlatform.Native.GetFullyQualifiedName(); + if (string.IsNullOrEmpty(fullyQualifiedName)) { - string fullyQualifiedName = LinuxPlatform.Native.GetFullyQualifiedName(); - if (string.IsNullOrEmpty(fullyQualifiedName)) - { - int lastError = Marshal.GetLastWin32Error(); - throw new InvalidOperationException("LinuxPlatform.NonWindowsGetDomainName error: " + lastError); - } - - int index = fullyQualifiedName.IndexOf('.'); - if (index >= 0) - { - return fullyQualifiedName.Substring(index + 1); - } - - return ""; + int lastError = Marshal.GetLastWin32Error(); + throw new InvalidOperationException("LinuxPlatform.NonWindowsGetDomainName error: " + lastError); } - else + + int index = fullyQualifiedName.IndexOf('.'); + if (index >= 0) { - throw new PlatformNotSupportedException(); + return fullyQualifiedName.Substring(index + 1); } + + return ""; } internal static string NonWindowsGetUserName() { - if (!IsWindows) - { - return LinuxPlatform.UserName; - } - else - { - throw new PlatformNotSupportedException(); - } + return LinuxPlatform.UserName; } // Hostname in this context seems to be the FQDN internal static string NonWindowsGetHostName() { - if (!IsWindows) + string hostName = LinuxPlatform.Native.GetFullyQualifiedName(); + if (string.IsNullOrEmpty(hostName)) { - string hostName = LinuxPlatform.Native.GetFullyQualifiedName(); - if (string.IsNullOrEmpty(hostName)) - { - int lastError = Marshal.GetLastWin32Error(); - throw new InvalidOperationException("LinuxPlatform.NonWindowsHostName error: " + lastError); - } - return hostName; - - } - else - { - throw new PlatformNotSupportedException(); + int lastError = Marshal.GetLastWin32Error(); + throw new InvalidOperationException("LinuxPlatform.NonWindowsHostName error: " + lastError); } + return hostName; } internal static bool NonWindowsIsExecutable(string path) { - if (!IsWindows) - { - return LinuxPlatform.IsExecutable(path); - } - - throw new PlatformNotSupportedException(); + return LinuxPlatform.IsExecutable(path); } internal static uint NonWindowsGetThreadId() @@ -512,15 +427,6 @@ namespace System.Management.Automation return 0; } - /// - /// This exception is meant to be thrown if a code path is not supported due - /// to platform restrictions - /// - internal class PlatformNotSupportedException : System.Exception - { - public PlatformNotSupportedException() : base() {} - } - /// /// This models the native call CommandLineToArgvW in managed code. /// From 551e2e6fc4fb6ed470092ab3fa3de69764f3f062 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Wed, 13 Jul 2016 21:10:43 -0700 Subject: [PATCH 07/42] Format CorePsPlatform --- .../CoreCLR/CorePsPlatform.cs | 98 +++++++++---------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index 5a4dfccb69..104eed8bc9 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -143,12 +143,12 @@ namespace System.Management.Automation //the user has set XDG_DATA_HOME corresponding to module path if (String.IsNullOrEmpty(xdgdatahome)){ - // create the xdg folder if needed - if (!Directory.Exists(xdgDataHomeDefault)) - { - Directory.CreateDirectory(xdgDataHomeDefault); - } - return xdgDataHomeDefault; + // create the xdg folder if needed + if (!Directory.Exists(xdgDataHomeDefault)) + { + Directory.CreateDirectory(xdgDataHomeDefault); + } + return xdgDataHomeDefault; } else { @@ -159,12 +159,12 @@ namespace System.Management.Automation //the user has set XDG_DATA_HOME corresponding to module path if (String.IsNullOrEmpty(xdgdatahome)){ - //xdg values have not been set - if (!Directory.Exists(xdgModuleDefault)) //module folder not always guaranteed to exist - { - Directory.CreateDirectory(xdgModuleDefault); - } - return xdgModuleDefault; + //xdg values have not been set + if (!Directory.Exists(xdgModuleDefault)) //module folder not always guaranteed to exist + { + Directory.CreateDirectory(xdgModuleDefault); + } + return xdgModuleDefault; } else { @@ -175,7 +175,7 @@ namespace System.Management.Automation //the user has set XDG_CACHE_HOME if (String.IsNullOrEmpty(xdgcachehome)) { - //xdg values have not been set + //xdg values have not been set if (!Directory.Exists(xdgCacheDefault)) //module folder not always guaranteed to exist { Directory.CreateDirectory(xdgCacheDefault); @@ -342,12 +342,12 @@ namespace System.Management.Automation return LinuxPlatform.GetUserFromPid(path); } -#if CORECLR + #if CORECLR internal static string NonWindowsGetFolderPath(SpecialFolder folder) { return LinuxPlatform.GetFolderPath(folder); } -#endif + #endif internal static string NonWindowsInternalGetLinkType(FileSystemInfo fileInfo) { @@ -526,40 +526,40 @@ namespace System.Management.Automation } } -#if CORECLR + #if CORECLR public static string GetFolderPath(SpecialFolder folder) { string s = null; switch (folder) { - case SpecialFolder.ProgramFiles: - s = "/bin"; - break; - case SpecialFolder.ProgramFilesX86: - s = "/usr/bin"; - break; - case SpecialFolder.System: - s = "/sbin"; - break; - case SpecialFolder.SystemX86: - s = "/sbin"; - break; - case SpecialFolder.Personal: - s = System.Environment.GetEnvironmentVariable("HOME"); - break; - case SpecialFolder.LocalApplicationData: - s = System.IO.Path.Combine(System.Environment.GetEnvironmentVariable("HOME"), ".config"); - if (!System.IO.Directory.Exists(s)) - { - System.IO.Directory.CreateDirectory(s); - } - break; - default: - throw new NotSupportedException(); + case SpecialFolder.ProgramFiles: + s = "/bin"; + break; + case SpecialFolder.ProgramFilesX86: + s = "/usr/bin"; + break; + case SpecialFolder.System: + s = "/sbin"; + break; + case SpecialFolder.SystemX86: + s = "/sbin"; + break; + case SpecialFolder.Personal: + s = System.Environment.GetEnvironmentVariable("HOME"); + break; + case SpecialFolder.LocalApplicationData: + s = System.IO.Path.Combine(System.Environment.GetEnvironmentVariable("HOME"), ".config"); + if (!System.IO.Directory.Exists(s)) + { + System.IO.Directory.CreateDirectory(s); + } + break; + default: + throw new NotSupportedException(); } return s; } -#endif + #endif public static bool IsHardLink(ref IntPtr handle) { @@ -602,12 +602,12 @@ namespace System.Management.Automation switch(ret) { case 1: - return true; + return true; case 0: - return false; + return false; default: - int lastError = Marshal.GetLastWin32Error(); - throw new InvalidOperationException("LinuxPlatform.IsSymLink error: " + lastError); + int lastError = Marshal.GetLastWin32Error(); + throw new InvalidOperationException("LinuxPlatform.IsSymLink error: " + lastError); } } @@ -622,12 +622,12 @@ namespace System.Management.Automation switch(ret) { case 1: - return true; + return true; case 0: - return false; + return false; default: - int lastError = Marshal.GetLastWin32Error(); - throw new InvalidOperationException("LinuxPlatform.IsExecutable error: " + lastError); + int lastError = Marshal.GetLastWin32Error(); + throw new InvalidOperationException("LinuxPlatform.IsExecutable error: " + lastError); } } From 6a23655b2f1e6e7632d766bd0ac69d5882221b72 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Wed, 13 Jul 2016 21:11:34 -0700 Subject: [PATCH 08/42] Rename LinuxPlatform to Unix Since it supports both Linux and OS X. This follows the .NET Core convention. --- .../CoreCLR/CorePsPlatform.cs | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index 104eed8bc9..3487d41137 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -313,17 +313,17 @@ namespace System.Management.Automation internal static bool NonWindowsIsHardLink(ref IntPtr handle) { - return LinuxPlatform.IsHardLink(ref handle); + return Unix.IsHardLink(ref handle); } internal static bool NonWindowsIsHardLink(FileSystemInfo fileInfo) { - return LinuxPlatform.IsHardLink(fileInfo); + return Unix.IsHardLink(fileInfo); } internal static bool NonWindowsIsSymLink(FileSystemInfo fileInfo) { - return LinuxPlatform.IsSymLink(fileInfo); + return Unix.IsSymLink(fileInfo); } internal static string NonWindowsInternalGetTarget(SafeFileHandle handle) @@ -334,18 +334,18 @@ namespace System.Management.Automation internal static string NonWindowsInternalGetTarget(string path) { - return LinuxPlatform.FollowSymLink(path); + return Unix.FollowSymLink(path); } internal static string NonWindowsGetUserFromPid(int path) { - return LinuxPlatform.GetUserFromPid(path); + return Unix.GetUserFromPid(path); } #if CORECLR internal static string NonWindowsGetFolderPath(SpecialFolder folder) { - return LinuxPlatform.GetFolderPath(folder); + return Unix.GetFolderPath(folder); } #endif @@ -367,27 +367,27 @@ namespace System.Management.Automation internal static bool NonWindowsCreateSymbolicLink(string path, string strTargetPath, bool isDirectory) { // Linux doesn't care if target is a directory or not - return LinuxPlatform.CreateSymbolicLink(path, strTargetPath); + return Unix.CreateSymbolicLink(path, strTargetPath); } internal static bool NonWindowsCreateHardLink(string path, string strTargetPath) { - return LinuxPlatform.CreateHardLink(path, strTargetPath); + return Unix.CreateHardLink(path, strTargetPath); } internal static void NonWindowsSetDate(DateTime dateToUse) { - LinuxPlatform.SetDateInfoInternal date = new LinuxPlatform.SetDateInfoInternal(dateToUse); - LinuxPlatform.SetDate(date); + Unix.SetDateInfoInternal date = new Unix.SetDateInfoInternal(dateToUse); + Unix.SetDate(date); } internal static string NonWindowsGetDomainName() { - string fullyQualifiedName = LinuxPlatform.Native.GetFullyQualifiedName(); + string fullyQualifiedName = Unix.Native.GetFullyQualifiedName(); if (string.IsNullOrEmpty(fullyQualifiedName)) { int lastError = Marshal.GetLastWin32Error(); - throw new InvalidOperationException("LinuxPlatform.NonWindowsGetDomainName error: " + lastError); + throw new InvalidOperationException("Unix.NonWindowsGetDomainName error: " + lastError); } int index = fullyQualifiedName.IndexOf('.'); @@ -401,24 +401,24 @@ namespace System.Management.Automation internal static string NonWindowsGetUserName() { - return LinuxPlatform.UserName; + return Unix.UserName; } // Hostname in this context seems to be the FQDN internal static string NonWindowsGetHostName() { - string hostName = LinuxPlatform.Native.GetFullyQualifiedName(); + string hostName = Unix.Native.GetFullyQualifiedName(); if (string.IsNullOrEmpty(hostName)) { int lastError = Marshal.GetLastWin32Error(); - throw new InvalidOperationException("LinuxPlatform.NonWindowsHostName error: " + lastError); + throw new InvalidOperationException("Unix.NonWindowsHostName error: " + lastError); } return hostName; } internal static bool NonWindowsIsExecutable(string path) { - return LinuxPlatform.IsExecutable(path); + return Unix.IsExecutable(path); } internal static uint NonWindowsGetThreadId() @@ -487,7 +487,7 @@ namespace System.Management.Automation } } - internal static class LinuxPlatform + internal static class Unix { private static string _userName; public static string UserName @@ -500,7 +500,7 @@ namespace System.Management.Automation if (string.IsNullOrEmpty(_userName)) { int lastError = Marshal.GetLastWin32Error(); - throw new InvalidOperationException("LinuxPlatform.UserName error: " + lastError); + throw new InvalidOperationException("Unix.UserName error: " + lastError); } } return _userName; @@ -585,7 +585,7 @@ namespace System.Management.Automation else { int lastError = Marshal.GetLastWin32Error(); - throw new InvalidOperationException("LinuxPlatform.IsHardLink error: " + lastError); + throw new InvalidOperationException("Unix.IsHardLink error: " + lastError); } } @@ -607,7 +607,7 @@ namespace System.Management.Automation return false; default: int lastError = Marshal.GetLastWin32Error(); - throw new InvalidOperationException("LinuxPlatform.IsSymLink error: " + lastError); + throw new InvalidOperationException("Unix.IsSymLink error: " + lastError); } } @@ -627,7 +627,7 @@ namespace System.Management.Automation return false; default: int lastError = Marshal.GetLastWin32Error(); - throw new InvalidOperationException("LinuxPlatform.IsExecutable error: " + lastError); + throw new InvalidOperationException("Unix.IsExecutable error: " + lastError); } } @@ -637,7 +637,7 @@ namespace System.Management.Automation if (ret == -1) { int lastError = Marshal.GetLastWin32Error(); - throw new InvalidOperationException("LinuxPlatform.NonWindowsSetDate error: " + lastError); + throw new InvalidOperationException("Unix.NonWindowsSetDate error: " + lastError); } } From 093587fecbdf51e252ddca0a1ff0fd0372e6b147 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Wed, 13 Jul 2016 21:12:45 -0700 Subject: [PATCH 09/42] Rename Native so it is Unix.NativeMethods Like the rest of PowerShell. --- .../CoreCLR/CorePsPlatform.cs | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index 3487d41137..27d5d2091f 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -383,7 +383,7 @@ namespace System.Management.Automation internal static string NonWindowsGetDomainName() { - string fullyQualifiedName = Unix.Native.GetFullyQualifiedName(); + string fullyQualifiedName = Unix.NativeMethods.GetFullyQualifiedName(); if (string.IsNullOrEmpty(fullyQualifiedName)) { int lastError = Marshal.GetLastWin32Error(); @@ -407,7 +407,7 @@ namespace System.Management.Automation // Hostname in this context seems to be the FQDN internal static string NonWindowsGetHostName() { - string hostName = Unix.Native.GetFullyQualifiedName(); + string hostName = Unix.NativeMethods.GetFullyQualifiedName(); if (string.IsNullOrEmpty(hostName)) { int lastError = Marshal.GetLastWin32Error(); @@ -496,7 +496,7 @@ namespace System.Management.Automation { if (string.IsNullOrEmpty(_userName)) { - _userName = Native.GetUserName(); + _userName = NativeMethods.GetUserName(); if (string.IsNullOrEmpty(_userName)) { int lastError = Marshal.GetLastWin32Error(); @@ -577,7 +577,7 @@ namespace System.Management.Automation int count; string filePath = fs.FullName; - int ret = Native.GetLinkCount(filePath, out count); + int ret = NativeMethods.GetLinkCount(filePath, out count); if (ret == 1) { return count > 1; @@ -598,7 +598,7 @@ namespace System.Management.Automation } string filePath = fs.FullName; - int ret = Native.IsSymLink(filePath); + int ret = NativeMethods.IsSymLink(filePath); switch(ret) { case 1: @@ -618,7 +618,7 @@ namespace System.Management.Automation return false; } - int ret = Native.IsExecutable(filePath); + int ret = NativeMethods.IsExecutable(filePath); switch(ret) { case 1: @@ -633,7 +633,7 @@ namespace System.Management.Automation public static void SetDate(SetDateInfoInternal info) { - int ret = Native.SetDate(info); + int ret = NativeMethods.SetDate(info); if (ret == -1) { int lastError = Marshal.GetLastWin32Error(); @@ -643,24 +643,24 @@ namespace System.Management.Automation public static bool CreateSymbolicLink(string path, string strTargetPath) { - int ret = Native.CreateSymLink(path, strTargetPath); + int ret = NativeMethods.CreateSymLink(path, strTargetPath); return ret == 1 ? true : false; } public static bool CreateHardLink(string path, string strTargetPath) { - int ret = Native.CreateHardLink(path, strTargetPath); + int ret = NativeMethods.CreateHardLink(path, strTargetPath); return ret == 1 ? true : false; } public static string FollowSymLink(string path) { - return Native.FollowSymLink(path); + return NativeMethods.FollowSymLink(path); } public static string GetUserFromPid(int pid) { - return Native.GetUserFromPid(pid); + return NativeMethods.GetUserFromPid(pid); } [StructLayout(LayoutKind.Sequential)] @@ -694,7 +694,7 @@ namespace System.Management.Automation } } - internal static class Native + internal static class NativeMethods { const string psLib = "libpsl-native"; From 4e518fe1c7bcca9c7a7cbbc978dd4829afa288fd Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Wed, 13 Jul 2016 21:15:45 -0700 Subject: [PATCH 10/42] Remove Platform class override of PlatformNotSupportedException This was confusing and unused in most places, exception itself removed in cleanup. --- .../namespaces/FileSystemProvider.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs index 39c60e005b..50a84e98b1 100644 --- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs +++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs @@ -496,7 +496,7 @@ namespace Microsoft.PowerShell.Commands } else { - throw new Platform.PlatformNotSupportedException(); + throw new PlatformNotSupportedException(); } } @@ -753,7 +753,7 @@ namespace Microsoft.PowerShell.Commands } else { - throw new Platform.PlatformNotSupportedException(); + throw new PlatformNotSupportedException(); } } @@ -8202,7 +8202,7 @@ namespace Microsoft.PowerShell.Commands { if (!Platform.IsWindows) { - throw new Platform.PlatformNotSupportedException(); + throw new PlatformNotSupportedException(); } using (SafeFileHandle handle = OpenReparsePoint(filePath, FileDesiredAccess.GenericRead)) @@ -8601,7 +8601,7 @@ namespace Microsoft.PowerShell.Commands } else { - throw new Platform.PlatformNotSupportedException(); + throw new PlatformNotSupportedException(); } } From e89fa7b1dc779e8fa28b28b80658e8bac462da0e Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Wed, 13 Jul 2016 21:39:34 -0700 Subject: [PATCH 11/42] Clean up IsExecutable and IsSymLink --- .../CoreCLR/CorePsPlatform.cs | 8 ++- src/libpsl-native/src/isexecutable.cpp | 58 +++---------------- src/libpsl-native/src/isexecutable.h | 3 +- src/libpsl-native/src/issymlink.cpp | 27 ++++----- src/libpsl-native/src/issymlink.h | 3 +- src/libpsl-native/test/test-isexecutable.cpp | 30 ++++------ src/libpsl-native/test/test-issymlink.cpp | 47 +++++---------- 7 files changed, 56 insertions(+), 120 deletions(-) diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index 27d5d2091f..5d3a05290a 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -700,6 +700,8 @@ namespace System.Management.Automation // Ansi is a misnomer, it is hardcoded to UTF-8 on Linux and OS X + // C bools are 1 byte and so must be marshaled as I1 + [DllImport(psLib, CharSet = CharSet.Ansi, SetLastError = true)] [return: MarshalAs(UnmanagedType.LPStr)] internal static extern string GetUserName(); @@ -708,10 +710,12 @@ namespace System.Management.Automation internal static extern int GetLinkCount([MarshalAs(UnmanagedType.LPStr)]string filePath, out int linkCount); [DllImport(psLib, CharSet = CharSet.Ansi, SetLastError = true)] - internal static extern int IsSymLink([MarshalAs(UnmanagedType.LPStr)]string filePath); + [return: MarshalAs(UnmanagedType.I1)] + internal static extern bool IsSymLink([MarshalAs(UnmanagedType.LPStr)]string filePath); [DllImport(psLib, CharSet = CharSet.Ansi, SetLastError = true)] - internal static extern int IsExecutable([MarshalAs(UnmanagedType.LPStr)]string filePath); + [return: MarshalAs(UnmanagedType.I1)] + internal static extern bool IsExecutable([MarshalAs(UnmanagedType.LPStr)]string filePath); [DllImport(psLib, CharSet = CharSet.Ansi, SetLastError = true)] [return: MarshalAs(UnmanagedType.LPStr)] diff --git a/src/libpsl-native/src/isexecutable.cpp b/src/libpsl-native/src/isexecutable.cpp index 38f6ad1844..a06fab57ab 100644 --- a/src/libpsl-native/src/isexecutable.cpp +++ b/src/libpsl-native/src/isexecutable.cpp @@ -1,5 +1,5 @@ -//! @file isExecutable.cpp -//! @author George FLeming +//! @file isexecutable.cpp +//! @author George Fleming //! @brief returns whether a file is executable #include @@ -11,7 +11,7 @@ //! //! IsExecutable //! -//! @param[in] fileName +//! @param[in] path //! @parblock //! A pointer to the buffer that contains the file name //! @@ -19,63 +19,21 @@ //! @endparblock //! //! @exception errno Passes these errors via errno to GetLastError: -//! - ERROR_FILE_NOT_FOUND: the system cannot find the file specified //! - ERROR_INVALID_ADDRESS: attempt to access invalid address -//! - ERROR_GEN_FAILURE: device attached to the system is not functioning -//! - ERROR_INVALID_NAME: filename, directory name, or volume label syntax is incorrect -//! - ERROR_INVALID_FUNCTION: incorrect function -//! - ERROR_INVALID_PARAMETER: parameter to access(2) call is incorrect //! -//! @retval 1 if path is an executable -//! @retval 0 if path is not a executable -//! @retval -1 If the function fails.. To get extended error information, call GetLastError. +//! @retval true if path is an executable, false otherwise //! -int32_t IsExecutable(const char* fileName) +bool IsExecutable(const char* path) { errno = 0; // Check parameters - if (!fileName) + if (!path) { errno = ERROR_INVALID_PARAMETER; - return -1; + return false; } - int returnCode = access(fileName, X_OK); - - if (returnCode == 0) - { - return 1; - } - - switch(errno) - { - case EACCES: - return 0; - case EBADF: - case ENOENT: - errno = ERROR_FILE_NOT_FOUND; - break; - case EFAULT: - errno = ERROR_INVALID_ADDRESS; - break; - case ELOOP: - errno = ERROR_STOPPED_ON_SYMLINK; - break; - case EIO: - case ENOMEM: - errno = ERROR_GEN_FAILURE; - break; - case ENOTDIR: - case ENAMETOOLONG: - errno = ERROR_INVALID_NAME; - break; - case EINVAL: - errno = ERROR_INVALID_PARAMETER; - break; - default: - errno = ERROR_INVALID_FUNCTION; - } - return -1; + return access(path, X_OK) != -1; } diff --git a/src/libpsl-native/src/isexecutable.h b/src/libpsl-native/src/isexecutable.h index da4e9f8f36..f4278b954e 100644 --- a/src/libpsl-native/src/isexecutable.h +++ b/src/libpsl-native/src/isexecutable.h @@ -1,9 +1,10 @@ #pragma once #include "pal.h" +#include PAL_BEGIN_EXTERNC -int32_t IsExecutable(const char* fileName); +bool IsExecutable(const char* path); PAL_END_EXTERNC diff --git a/src/libpsl-native/src/issymlink.cpp b/src/libpsl-native/src/issymlink.cpp index d4ce011cba..dfd37d8bc5 100644 --- a/src/libpsl-native/src/issymlink.cpp +++ b/src/libpsl-native/src/issymlink.cpp @@ -9,11 +9,11 @@ #include #include "issymlink.h" -//! @brief IsSymLink determines if path is a symbolic link +//! @brief IsSymlink determines if path is a symbolic link //! -//! IsSymLink +//! IsSymlink //! -//! @param[in] fileName +//! @param[in] path //! @parblock //! A pointer to the buffer that contains the file name //! @@ -34,28 +34,25 @@ //! - ERROR_INVALID_FUNCTION: incorrect function //! - ERROR_BAD_PATH_NAME: pathname is too long, or contains invalid characters //! -//! @retval 1 if path is a symbolic link -//! @retval 0 if path is not a symbolic link -//! @retval -1 If the function fails.. To get extended error information, call GetLastError. +//! @retval true if path is a symbolic link, false otherwise //! -int32_t IsSymLink(const char* fileName) +bool IsSymLink(const char* path) { - - errno = 0; + errno = 0; // Check parameters - if (!fileName) + if (!path) { errno = ERROR_INVALID_PARAMETER; - return -1; + return false; } struct stat statBuf; - int returnCode = lstat(fileName, &statBuf); + int ret = lstat(path, &statBuf); - if (returnCode != 0) + if (ret != 0) { switch(errno) { @@ -89,8 +86,8 @@ int32_t IsSymLink(const char* fileName) default: errno = ERROR_INVALID_FUNCTION; } - return -1; + return false; } - return S_ISLNK(statBuf.st_mode) ? 1 : 0; + return S_ISLNK(statBuf.st_mode); } diff --git a/src/libpsl-native/src/issymlink.h b/src/libpsl-native/src/issymlink.h index 066a4060c1..ebe3733c5a 100644 --- a/src/libpsl-native/src/issymlink.h +++ b/src/libpsl-native/src/issymlink.h @@ -1,9 +1,10 @@ #pragma once #include "pal.h" +#include PAL_BEGIN_EXTERNC -int32_t IsSymLink(const char* fileName); +bool IsSymLink(const char* path); PAL_END_EXTERNC diff --git a/src/libpsl-native/test/test-isexecutable.cpp b/src/libpsl-native/test/test-isexecutable.cpp index 36955912ff..d97d7d3159 100644 --- a/src/libpsl-native/test/test-isexecutable.cpp +++ b/src/libpsl-native/test/test-isexecutable.cpp @@ -33,62 +33,52 @@ protected: // First create a file int fd = mkstemp(fileTemplateBuf); EXPECT_TRUE(fd != -1); - file = fileTemplateBuf; + file = fileTemplateBuf; } ~IsExecutableTest() { - int ret; - - ret = unlink(file); - EXPECT_EQ(0, ret); + EXPECT_FALSE(unlink(file)); } void ChangeFilePermission(const char* file, mode_t mode) { - int ret = chmod(file, mode); - EXPECT_EQ(ret, 0); + EXPECT_FALSE(chmod(file, mode)); } }; TEST_F(IsExecutableTest, FilePathNameIsNull) { - int32_t retVal = IsExecutable(NULL); - EXPECT_EQ(retVal, -1); + EXPECT_FALSE(IsExecutable(NULL)); EXPECT_EQ(ERROR_INVALID_PARAMETER, errno); } TEST_F(IsExecutableTest, FilePathNameDoesNotExist) { std::string invalidFile = "/tmp/isexecutabletest_invalidFile"; - int32_t retVal = IsExecutable(invalidFile.c_str()); - EXPECT_EQ(retVal, -1); + EXPECT_FALSE(IsExecutable(invalidFile.c_str())); EXPECT_EQ(ERROR_FILE_NOT_FOUND, errno); } TEST_F(IsExecutableTest, NormalFileIsNotIsexecutable) { - int32_t retVal = IsExecutable(file); - EXPECT_EQ(0, retVal); - + EXPECT_FALSE(IsExecutable(file)); + ChangeFilePermission(file, mode_444); - retVal = IsExecutable(file); - EXPECT_EQ(0, retVal); + EXPECT_FALSE(IsExecutable(file)); } TEST_F(IsExecutableTest, FilePermission_700) { ChangeFilePermission(file, mode_700); - int32_t retVal = IsExecutable(file); - EXPECT_EQ(1, retVal); + EXPECT_TRUE(IsExecutable(file)); } TEST_F(IsExecutableTest, FilePermission_777) { ChangeFilePermission(file, mode_777); - int32_t retVal = IsExecutable(file); - EXPECT_EQ(1, retVal); + EXPECT_TRUE(IsExecutable(file)); } diff --git a/src/libpsl-native/test/test-issymlink.cpp b/src/libpsl-native/test/test-issymlink.cpp index 4aaa3eb541..512a4053a9 100644 --- a/src/libpsl-native/test/test-issymlink.cpp +++ b/src/libpsl-native/test/test-issymlink.cpp @@ -30,75 +30,60 @@ protected: // First create a file int fd = mkstemp(fileTemplateBuf); EXPECT_TRUE(fd != -1); - file = fileTemplateBuf; + file = fileTemplateBuf; - // Create a temp directory - dir = mkdtemp(dirTemplateBuf); + // Create a temp directory + dir = mkdtemp(dirTemplateBuf); EXPECT_TRUE(dir != NULL); // Create symbolic link to file - int ret1 = symlink(file, fileSymLink.c_str()); - EXPECT_EQ(ret1, 0); - + EXPECT_FALSE(symlink(file, fileSymLink.c_str())); + // Create symbolic link to directory - int ret2 = symlink(dir, dirSymLink.c_str()); - EXPECT_EQ(ret2, 0); + EXPECT_FALSE(symlink(dir, dirSymLink.c_str())); } ~isSymLinkTest() { - int ret; + EXPECT_FALSE(unlink(fileSymLink.c_str())); - ret = unlink(fileSymLink.c_str()); - EXPECT_EQ(0, ret); + EXPECT_FALSE(unlink(dirSymLink.c_str())); - ret = unlink(dirSymLink.c_str()); - EXPECT_EQ(0, ret); + EXPECT_FALSE(unlink(file)); - ret = unlink(file); - EXPECT_EQ(0, ret); - - ret = rmdir(dir); - EXPECT_EQ(0, ret); + EXPECT_FALSE(rmdir(dir)); } }; TEST_F(isSymLinkTest, FilePathNameIsNull) { - int retVal = IsSymLink(NULL); - EXPECT_EQ(retVal, -1); + EXPECT_FALSE(IsSymLink(NULL)); EXPECT_EQ(ERROR_INVALID_PARAMETER, errno); } TEST_F(isSymLinkTest, FilePathNameDoesNotExist) { std::string invalidFile = "/tmp/symlinktest_invalidFile"; - int retVal = IsSymLink(invalidFile.c_str()); - EXPECT_EQ(retVal, -1); + EXPECT_FALSE(IsSymLink(invalidFile.c_str())); EXPECT_EQ(ERROR_FILE_NOT_FOUND, errno); } TEST_F(isSymLinkTest, NormalFileIsNotSymLink) { - int retVal = IsSymLink(file); - EXPECT_EQ(0, retVal); + EXPECT_FALSE(IsSymLink(file)); } TEST_F(isSymLinkTest, SymLinkToFile) { - int retVal = IsSymLink(fileSymLink.c_str()); - EXPECT_EQ(1, retVal); + EXPECT_TRUE(IsSymLink(fileSymLink.c_str())); } TEST_F(isSymLinkTest, NormalDirectoryIsNotSymbLink) { - int retVal = IsSymLink(dir); - EXPECT_EQ(0, retVal); + EXPECT_FALSE(IsSymLink(dir)); } TEST_F(isSymLinkTest, SymLinkToDirectory) { - int retVal = IsSymLink(dirSymLink.c_str()); - EXPECT_EQ(1, retVal); + EXPECT_TRUE(IsSymLink(dirSymLink.c_str())); } - From 5ef851a1704f277a3174ded0c9a534fba701f8c7 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Wed, 13 Jul 2016 21:44:04 -0700 Subject: [PATCH 12/42] Port NativeItemExists for Linux Without using .NET --- .../CoreCLR/CorePsPlatform.cs | 54 ++++++++----------- .../engine/Utils.cs | 5 ++ 2 files changed, 26 insertions(+), 33 deletions(-) diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index 5d3a05290a..1050e96b6b 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -416,6 +416,16 @@ namespace System.Management.Automation return hostName; } + internal static bool NonWindowsIsFile(string path) + { + return Unix.NativeMethods.IsFile(path); + } + + internal static bool NonWindowsIsDirectory(string path) + { + return Unix.NativeMethods.IsDirectory(path); + } + internal static bool NonWindowsIsExecutable(string path) { return Unix.IsExecutable(path); @@ -592,43 +602,12 @@ namespace System.Management.Automation public static bool IsSymLink(FileSystemInfo fs) { - if (!fs.Exists) - { - return false; - } - - string filePath = fs.FullName; - int ret = NativeMethods.IsSymLink(filePath); - switch(ret) - { - case 1: - return true; - case 0: - return false; - default: - int lastError = Marshal.GetLastWin32Error(); - throw new InvalidOperationException("Unix.IsSymLink error: " + lastError); - } + return NativeMethods.IsSymLink(fs.FullName); } public static bool IsExecutable(string filePath) { - if (!File.Exists(filePath)) - { - return false; - } - - int ret = NativeMethods.IsExecutable(filePath); - switch(ret) - { - case 1: - return true; - case 0: - return false; - default: - int lastError = Marshal.GetLastWin32Error(); - throw new InvalidOperationException("Unix.IsExecutable error: " + lastError); - } + return NativeMethods.IsExecutable(filePath); } public static void SetDate(SetDateInfoInternal info) @@ -739,6 +718,15 @@ namespace System.Management.Automation [DllImport(psLib, CharSet = CharSet.Ansi, SetLastError = true)] [return: MarshalAs(UnmanagedType.LPStr)] internal static extern string GetUserFromPid(int pid); + + [DllImport(psLib, CharSet = CharSet.Ansi, SetLastError = true)] + [return: MarshalAs(UnmanagedType.I1)] + internal static extern bool IsFile([MarshalAs(UnmanagedType.LPStr)]string filePath); + + [DllImport(psLib, CharSet = CharSet.Ansi, SetLastError = true)] + [return: MarshalAs(UnmanagedType.I1)] + internal static extern bool IsDirectory([MarshalAs(UnmanagedType.LPStr)]string filePath); + } } diff --git a/src/System.Management.Automation/engine/Utils.cs b/src/System.Management.Automation/engine/Utils.cs index 4cd8002989..ec06e5250d 100644 --- a/src/System.Management.Automation/engine/Utils.cs +++ b/src/System.Management.Automation/engine/Utils.cs @@ -995,6 +995,10 @@ namespace System.Management.Automation isDirectory = false; return false; } +#if LINUX + isDirectory = Platform.NonWindowsIsDirectory(path); + return Platform.NonWindowsIsFile(path); +#else if (IsReservedDeviceName(path)) { @@ -1028,6 +1032,7 @@ namespace System.Management.Automation ((int)NativeMethods.FileAttributes.Directory); return true; +#endif } // This is done through P/Invoke since we pay 13% performance degradation From 46cfa268121dffa6b230e19c41db4c88fddf3813 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Thu, 14 Jul 2016 11:25:13 -0700 Subject: [PATCH 13/42] Remove Platform.HasCom query --- .../CoreCLR/CorePsPlatform.cs | 8 -------- .../engine/runtime/Binding/Binders.cs | 13 +++++-------- .../utils/ExtensionMethods.cs | 9 +++------ test/csharp/test_CorePsPlatform.cs | 6 ------ 4 files changed, 8 insertions(+), 28 deletions(-) diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index 1050e96b6b..8c016c54e9 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -217,14 +217,6 @@ namespace System.Management.Automation } - // ComObjectType is null on CoreCLR for Linux since there is - // no COM support on Linux - internal static bool HasCom() - { - // TODO: catch exception from Type.IsComObject - return IsWindows; - } - // The Antimalware Scan Interface is not supported on Linux internal static bool HasAmsi() { diff --git a/src/System.Management.Automation/engine/runtime/Binding/Binders.cs b/src/System.Management.Automation/engine/runtime/Binding/Binders.cs index 50592a4e4d..8d598b13bc 100644 --- a/src/System.Management.Automation/engine/runtime/Binding/Binders.cs +++ b/src/System.Management.Automation/engine/runtime/Binding/Binders.cs @@ -677,14 +677,11 @@ namespace System.Management.Automation.Language private static TypeInfo GetComObjectType() { - if (Platform.HasCom()) - { - return typeof(object).GetTypeInfo().Assembly.GetType("System.__ComObject").GetTypeInfo(); - } - else - { - return null; - } +#if LINUX + return null; +#else + return typeof(object).GetTypeInfo().Assembly.GetType("System.__ComObject").GetTypeInfo(); +#endif } internal static bool IsComObject(object obj) diff --git a/src/System.Management.Automation/utils/ExtensionMethods.cs b/src/System.Management.Automation/utils/ExtensionMethods.cs index 0e58cc55f4..1c1c76f199 100644 --- a/src/System.Management.Automation/utils/ExtensionMethods.cs +++ b/src/System.Management.Automation/utils/ExtensionMethods.cs @@ -130,12 +130,9 @@ namespace System.Management.Automation [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static bool IsComObject(this Type type) { - if (!Platform.HasCom()) - { - return false; - } - -#if CORECLR // Type.IsComObject(Type) is not in CoreCLR +#if LINUX + return false; +#elif CORECLR // Type.IsComObject(Type) is not in CoreCLR return ComObjectType.IsAssignableFrom(type); #else return type.IsCOMObject; diff --git a/test/csharp/test_CorePsPlatform.cs b/test/csharp/test_CorePsPlatform.cs index d0132b121d..ce979e818d 100644 --- a/test/csharp/test_CorePsPlatform.cs +++ b/test/csharp/test_CorePsPlatform.cs @@ -15,12 +15,6 @@ namespace PSTests Assert.True(Platform.IsCore); } - [Fact] - public static void TestHasCom() - { - Assert.False(Platform.HasCom()); - } - [Fact] public static void TestHasAmsi() { From c9a4197af1ad7d473dcdd85c768714c3b2b92f54 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Thu, 14 Jul 2016 11:29:55 -0700 Subject: [PATCH 14/42] Remove Platform.HasAmsi query --- .../CoreCLR/CorePsPlatform.cs | 6 --- .../security/SecuritySupport.cs | 50 ++++++------------- test/csharp/test_CorePsPlatform.cs | 6 --- 3 files changed, 14 insertions(+), 48 deletions(-) diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index 8c016c54e9..f560517169 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -217,12 +217,6 @@ namespace System.Management.Automation } - // The Antimalware Scan Interface is not supported on Linux - internal static bool HasAmsi() - { - return IsWindows; - } - // This is mainly with respect to the auto-mounting of // disconnected network drives on Windows internal static bool HasDriveAutoMounting() diff --git a/src/System.Management.Automation/security/SecuritySupport.cs b/src/System.Management.Automation/security/SecuritySupport.cs index 05ae9f77dc..f66d8094c5 100644 --- a/src/System.Management.Automation/security/SecuritySupport.cs +++ b/src/System.Management.Automation/security/SecuritySupport.cs @@ -1633,14 +1633,11 @@ namespace System.Management.Automation /// AMSI_RESULT_DETECTED if malware was detected in the sample. internal static AmsiNativeMethods.AMSI_RESULT ScanContent(string content, string sourceMetadata) { - if (Platform.HasAmsi()) - { - return WinScanContent(content,sourceMetadata); - } - else - { - return AmsiNativeMethods.AMSI_RESULT.AMSI_RESULT_NOT_DETECTED; - } +#if LINUX + return AmsiNativeMethods.AMSI_RESULT.AMSI_RESULT_NOT_DETECTED; +#else + return WinScanContent(content,sourceMetadata); +#endif } internal static AmsiNativeMethods.AMSI_RESULT WinScanContent(string content, string sourceMetadata) @@ -1730,14 +1727,9 @@ namespace System.Management.Automation internal static void CurrentDomain_ProcessExit(object sender, EventArgs e) { - if (Platform.HasAmsi()) - { - VerifyAmsiUninitializeCalled(); - } - else - { - throw new PlatformNotSupportedException(); - } +#if !LINUX + VerifyAmsiUninitializeCalled(); +#endif } [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] @@ -1753,16 +1745,9 @@ namespace System.Management.Automation /// internal static void CloseSession() { - if (Platform.HasAmsi()) - { - WinCloseSession(); - } - else - { - // Porting note: cannot throw here because this is called whether or not - // AMSI was initialized in the first place - return; - } +#if !LINUX + WinCloseSession(); +#endif } internal static void WinCloseSession() @@ -1789,16 +1774,9 @@ namespace System.Management.Automation /// internal static void Uninitialize() { - if (Platform.HasAmsi()) - { - WinUninitialize(); - } - else - { - // Porting note: cannot throw here because this is called whether or not - // AMSI was initialized in the first place - return; - } +#if !LINUX + WinUninitialize(); +#endif } internal static void WinUninitialize() diff --git a/test/csharp/test_CorePsPlatform.cs b/test/csharp/test_CorePsPlatform.cs index ce979e818d..8709dccab5 100644 --- a/test/csharp/test_CorePsPlatform.cs +++ b/test/csharp/test_CorePsPlatform.cs @@ -15,12 +15,6 @@ namespace PSTests Assert.True(Platform.IsCore); } - [Fact] - public static void TestHasAmsi() - { - Assert.False(Platform.HasAmsi()); - } - [Fact] public static void TestHasDriveAutoMounting() { From c2cd059aa80ad79557cbad56c2c721a6bbdea90c Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Thu, 14 Jul 2016 11:40:07 -0700 Subject: [PATCH 15/42] Remove Platform.HasDriveAutoMounting query --- src/System.Management.Automation/CoreCLR/CorePsPlatform.cs | 7 ------- .../engine/SessionStateDriveAPIs.cs | 5 +++-- test/csharp/test_CorePsPlatform.cs | 6 ------ 3 files changed, 3 insertions(+), 15 deletions(-) diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index f560517169..35ba1a55ec 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -217,13 +217,6 @@ namespace System.Management.Automation } - // This is mainly with respect to the auto-mounting of - // disconnected network drives on Windows - internal static bool HasDriveAutoMounting() - { - return IsWindows; - } - // Linux does not have a registry internal static bool HasRegistrySupport() { diff --git a/src/System.Management.Automation/engine/SessionStateDriveAPIs.cs b/src/System.Management.Automation/engine/SessionStateDriveAPIs.cs index d7e0f8e384..6de20eee21 100644 --- a/src/System.Management.Automation/engine/SessionStateDriveAPIs.cs +++ b/src/System.Management.Automation/engine/SessionStateDriveAPIs.cs @@ -1469,11 +1469,12 @@ namespace System.Management.Automation bool driveIsValid = true; // If the drive is auto-mounted, ensure that it still exists, or remove the drive. - // note: not all platforms support drive auto-mounting - if (Platform.HasDriveAutoMounting() && (drive.IsAutoMounted || IsAStaleVhdMountedDrive(drive))) +#if !LINUX + if (drive.IsAutoMounted || IsAStaleVhdMountedDrive(drive)) { driveIsValid = ValidateOrRemoveAutoMountedDrive(drive, lookupScope); } +#endif if(drive.Name.Length == 1) { if(!(driveNames.Contains(drive.Name))) diff --git a/test/csharp/test_CorePsPlatform.cs b/test/csharp/test_CorePsPlatform.cs index 8709dccab5..44784983f3 100644 --- a/test/csharp/test_CorePsPlatform.cs +++ b/test/csharp/test_CorePsPlatform.cs @@ -15,12 +15,6 @@ namespace PSTests Assert.True(Platform.IsCore); } - [Fact] - public static void TestHasDriveAutoMounting() - { - Assert.False(Platform.HasDriveAutoMounting()); - } - [Fact] public static void TestHasRegistrySupport() { From 73a1a51f69c64ee27f9cc18b7dad1eb59fab1ed0 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Thu, 14 Jul 2016 11:54:54 -0700 Subject: [PATCH 16/42] Remove Platform.HasRegistry query --- .../CoreCLR/CorePsExtensions.cs | 23 ++++++++----------- .../CoreCLR/CorePsPlatform.cs | 6 ----- .../engine/CommandDiscovery.cs | 7 ++---- .../engine/PSVersionInfo.cs | 7 ++---- .../engine/Utils.cs | 13 ++++------- .../remoting/commands/remotingcommandutil.cs | 11 ++++----- .../namespaces/RegistryProvider.cs | 7 ++---- .../namespaces/SafeRegistryHandle.cs | 8 +++---- test/csharp/test_CorePsPlatform.cs | 6 ----- 9 files changed, 29 insertions(+), 59 deletions(-) diff --git a/src/System.Management.Automation/CoreCLR/CorePsExtensions.cs b/src/System.Management.Automation/CoreCLR/CorePsExtensions.cs index 5674327cdd..59a864d01c 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsExtensions.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsExtensions.cs @@ -893,13 +893,10 @@ namespace System.Management.Automation return GetEnvironmentVariables(); } - if (!Platform.HasRegistrySupport()) - { - // Porting note: Does not throw a PlatformUnsupported because - // GetEnvironmentVariable cannot throw, and we want the same interface. - return null; - } - +#if LINUX + return null; +#else + if( target == EnvironmentVariableTarget.Machine) { using (RegistryKey environmentKey = @@ -916,6 +913,7 @@ namespace System.Management.Automation return GetRegistryKeyNameValuePairs(environmentKey); } } +#endif } /// @@ -956,13 +954,9 @@ namespace System.Management.Automation return System.Environment.GetEnvironmentVariable(variable); } - if (!Platform.HasRegistrySupport()) - { - // Porting note: This cannot throw since it would otherwise throw while - // creating a runspace. Returning null is appropriate to signal that there - // is no value for a registry key with no registry. - return null; - } +#if LINUX + return null; +#else if (target == EnvironmentVariableTarget.Machine) { @@ -985,6 +979,7 @@ namespace System.Management.Automation return value; } } +#endif } #endregion EnvironmentVariable_Extensions diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index 35ba1a55ec..134291ed64 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -217,12 +217,6 @@ namespace System.Management.Automation } - // Linux does not have a registry - internal static bool HasRegistrySupport() - { - return IsWindows; - } - // Linux does not have PowerShell execution policies internal static bool HasExecutionPolicy() { diff --git a/src/System.Management.Automation/engine/CommandDiscovery.cs b/src/System.Management.Automation/engine/CommandDiscovery.cs index 2d0e2e917e..23c95b4324 100644 --- a/src/System.Management.Automation/engine/CommandDiscovery.cs +++ b/src/System.Management.Automation/engine/CommandDiscovery.cs @@ -1903,13 +1903,9 @@ namespace System.Management.Automation /// internal static string GetShellPathFromRegistry(string shellID) { - if (!Platform.HasRegistrySupport()) - { - return null; - } - string result = null; +#if !LINUX try { RegistryKey shellKey = Registry.LocalMachine.OpenSubKey(Utils.GetRegistryConfigurationPath(shellID)); @@ -1935,6 +1931,7 @@ namespace System.Management.Automation catch (ArgumentException) { } +#endif return result; } diff --git a/src/System.Management.Automation/engine/PSVersionInfo.cs b/src/System.Management.Automation/engine/PSVersionInfo.cs index d500e7e857..92aaee14eb 100644 --- a/src/System.Management.Automation/engine/PSVersionInfo.cs +++ b/src/System.Management.Automation/engine/PSVersionInfo.cs @@ -103,13 +103,9 @@ namespace System.Management.Automation // Gets the current WSMan stack version from the registry. private static Version GetWSManStackVersion() { - if (!Platform.HasRegistrySupport()) - { - return new Version(1, 0); - } - Version version = null; +#if !LINUX try { using (RegistryKey wsManStackVersionKey = Registry.LocalMachine.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\WSMAN")) @@ -133,6 +129,7 @@ namespace System.Management.Automation catch (FormatException) { } catch (OverflowException) { } catch (InvalidCastException) { } +#endif return version ?? System.Management.Automation.Remoting.Client.WSManNativeApi.WSMAN_STACK_VERSION; } diff --git a/src/System.Management.Automation/engine/Utils.cs b/src/System.Management.Automation/engine/Utils.cs index ec06e5250d..878908be5e 100644 --- a/src/System.Management.Automation/engine/Utils.cs +++ b/src/System.Management.Automation/engine/Utils.cs @@ -368,14 +368,11 @@ namespace System.Management.Automation /// internal static bool IsWinPEHost() { - if (Platform.HasRegistrySupport()) - { - return WinIsWinPEHost(); - } - else - { - return false; - } +#if LINUX + return false; +#else + return WinIsWinPEHost(); +#endif } internal static bool WinIsWinPEHost() diff --git a/src/System.Management.Automation/engine/remoting/commands/remotingcommandutil.cs b/src/System.Management.Automation/engine/remoting/commands/remotingcommandutil.cs index 2f0fa35d53..c3e9cc0066 100644 --- a/src/System.Management.Automation/engine/remoting/commands/remotingcommandutil.cs +++ b/src/System.Management.Automation/engine/remoting/commands/remotingcommandutil.cs @@ -96,12 +96,10 @@ namespace Microsoft.PowerShell.Commands /// internal static void CheckRemotingCmdletPrerequisites() { - //If the operating system (ie Linux) does not support the registry, simply return to avoid calls to the Windows registry. - if (!Platform.HasRegistrySupport()) - { - return; - } - +#if LINUX + // TODO: check that PSRP requirements are installed + return; +#else bool notSupported = true; String WSManKeyPath = "Software\\Microsoft\\Windows\\CurrentVersion\\WSMAN\\"; @@ -155,6 +153,7 @@ namespace Microsoft.PowerShell.Commands throw new InvalidOperationException( "Windows PowerShell remoting features are not enabled or not supported on this machine.\nThis may be because you do not have the correct version of WS-Management installed or this version of Windows does not support remoting currently.\n For more information, type 'get-help about_remote_requirements'."); } +#endif } /// diff --git a/src/System.Management.Automation/namespaces/RegistryProvider.cs b/src/System.Management.Automation/namespaces/RegistryProvider.cs index 1951f59eea..9a168ff7f8 100644 --- a/src/System.Management.Automation/namespaces/RegistryProvider.cs +++ b/src/System.Management.Automation/namespaces/RegistryProvider.cs @@ -136,11 +136,7 @@ namespace Microsoft.PowerShell.Commands { Collection drives = new Collection(); - if (!Platform.HasRegistrySupport()) - { - return drives; - } - +#if !LINUX drives.Add( new PSDriveInfo( "HKLM", @@ -156,6 +152,7 @@ namespace Microsoft.PowerShell.Commands "HKEY_CURRENT_USER", RegistryProviderStrings.HKCUDriveDescription, null)); +#endif return drives; } // InitializeDefaultDrives diff --git a/src/System.Management.Automation/namespaces/SafeRegistryHandle.cs b/src/System.Management.Automation/namespaces/SafeRegistryHandle.cs index 1e817966b9..80120a7025 100644 --- a/src/System.Management.Automation/namespaces/SafeRegistryHandle.cs +++ b/src/System.Management.Automation/namespaces/SafeRegistryHandle.cs @@ -58,13 +58,13 @@ namespace Microsoft.PowerShell.Commands.Internal override protected bool ReleaseHandle() { - if (!Platform.HasRegistrySupport()) - { - return true; - } +#if LINUX + return true; +#else // Returns a Win32 error code, 0 for success int r = RegCloseKey(handle); return r == 0; +#endif } } } diff --git a/test/csharp/test_CorePsPlatform.cs b/test/csharp/test_CorePsPlatform.cs index 44784983f3..80d3878918 100644 --- a/test/csharp/test_CorePsPlatform.cs +++ b/test/csharp/test_CorePsPlatform.cs @@ -15,12 +15,6 @@ namespace PSTests Assert.True(Platform.IsCore); } - [Fact] - public static void TestHasRegistrySupport() - { - Assert.False(Platform.HasRegistrySupport()); - } - [Fact] public static void TestGetUserName() { From e6c037c666ac35ea16d5e9920688f59c756de038 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Thu, 14 Jul 2016 14:31:31 -0700 Subject: [PATCH 17/42] Remove Platform.HasExecutionPolicy query Also change Get-ExecutionPolicy to not throw, but to return Unrestricted (which is the effect of the actual policy). --- .../CoreCLR/CorePsPlatform.cs | 6 ---- .../security/SecuritySupport.cs | 18 ++++++------ .../ExecutionPolicy.Tests.ps1 | 28 +++++++++---------- 3 files changed, 22 insertions(+), 30 deletions(-) diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index 134291ed64..6edfbaa90c 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -217,12 +217,6 @@ namespace System.Management.Automation } - // Linux does not have PowerShell execution policies - internal static bool HasExecutionPolicy() - { - return IsWindows; - } - // Linux has a single rooted file system internal static bool HasSingleRootFilesystem() { diff --git a/src/System.Management.Automation/security/SecuritySupport.cs b/src/System.Management.Automation/security/SecuritySupport.cs index f66d8094c5..8d3fc91f2d 100644 --- a/src/System.Management.Automation/security/SecuritySupport.cs +++ b/src/System.Management.Automation/security/SecuritySupport.cs @@ -138,11 +138,9 @@ namespace System.Management.Automation internal static void SetExecutionPolicy(ExecutionPolicyScope scope, ExecutionPolicy policy, string shellId) { - if (!Platform.HasExecutionPolicy()) - { - throw new PlatformNotSupportedException(); - } - +#if LINUX + throw new PlatformNotSupportedException(); +#else string executionPolicy = "Restricted"; string preferenceKey = Utils.GetRegistryConfigurationPath(shellId); const string PolicyKeyValueName = "ExecutionPolicy"; @@ -227,6 +225,7 @@ namespace System.Management.Automation break; } } +#endif } // Clean up the parents of a registry key as long as they @@ -283,11 +282,9 @@ namespace System.Management.Automation internal static ExecutionPolicy GetExecutionPolicy(string shellId, ExecutionPolicyScope scope) { - if (!Platform.HasExecutionPolicy()) - { - throw new PlatformNotSupportedException(); - } - +#if LINUX + return ExecutionPolicy.Unrestricted; +#else switch (scope) { case ExecutionPolicyScope.Process: @@ -368,6 +365,7 @@ namespace System.Management.Automation } return ExecutionPolicy.Restricted; +#endif } internal static ExecutionPolicy ParseExecutionPolicy(string policy) diff --git a/test/powershell/Modules/Microsoft.PowerShell.Security/ExecutionPolicy.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Security/ExecutionPolicy.Tests.ps1 index b5631218d5..cdc5a239cc 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Security/ExecutionPolicy.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Security/ExecutionPolicy.Tests.ps1 @@ -1,24 +1,24 @@ Describe "ExecutionPolicy" { Context "Check Get-ExecutionPolicy behavior" { - It "Should throw PlatformNotSupported when not on Windows" -Skip:$IsWindows { - { Get-ExecutionPolicy } | Should Throw "Operation is not supported on this platform." - } + It "Should unrestricted when not on Windows" -Skip:$IsWindows { + Get-ExecutionPolicy | Should Be Unrestricted + } - It "Should return Microsoft.Powershell.ExecutionPolicy PSObject on Windows" -Skip:($IsLinux -Or $IsOSX) { - (Get-ExecutionPolicy).GetType() | Should Be Microsoft.Powershell.ExecutionPolicy - } + It "Should return Microsoft.Powershell.ExecutionPolicy PSObject on Windows" -Skip:($IsLinux -Or $IsOSX) { + (Get-ExecutionPolicy).GetType() | Should Be Microsoft.Powershell.ExecutionPolicy + } } Context "Check Set-ExecutionPolicy behavior" { - It "Should throw PlatformNotSupported when not on Windows" -Skip:$IsWindows { - { Set-ExecutionPolicy Unrestricted } | Should Throw "Operation is not supported on this platform." - } + It "Should throw PlatformNotSupported when not on Windows" -Skip:$IsWindows { + { Set-ExecutionPolicy Unrestricted } | Should Throw "Operation is not supported on this platform." + } - It "Should succeed on Windows" -Skip:($IsLinux -Or $IsOSX) { - # We use the Process scope to avoid affecting the system - # Unrestricted is assumed "safe", otherwise these tests would not be running - { Set-ExecutionPolicy -Force -Scope Process -ExecutionPolicy Unrestricted } | Should Not Throw - } + It "Should succeed on Windows" -Skip:($IsLinux -Or $IsOSX) { + # We use the Process scope to avoid affecting the system + # Unrestricted is assumed "safe", otherwise these tests would not be running + { Set-ExecutionPolicy -Force -Scope Process -ExecutionPolicy Unrestricted } | Should Not Throw + } } } From 527f30a0139ac2b7ca73bb401786102d1ba9c4f4 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Thu, 14 Jul 2016 14:36:41 -0700 Subject: [PATCH 18/42] Remove Platform.HasSingleRootFilesystem query --- .../CoreCLR/CorePsPlatform.cs | 8 +------- .../namespaces/FileSystemProvider.cs | 4 +++- .../namespaces/LocationGlobber.cs | 9 ++++++--- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index 6edfbaa90c..c4edc350c0 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -217,18 +217,12 @@ namespace System.Management.Automation } - // Linux has a single rooted file system - internal static bool HasSingleRootFilesystem() - { - return !IsWindows; - } - // Linux has no notion of file shares. It has mount points // instead, which are subdirectories of its single-root // filesystem. internal static bool HasFileShares() { - return !HasSingleRootFilesystem(); + return IsWindows(); } // Linux has no support for UNC, just mounts in a single rooted hierarchy diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs index 50a84e98b1..6af52e291c 100644 --- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs +++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs @@ -947,12 +947,14 @@ namespace Microsoft.PowerShell.Commands root = newDrive.RootDirectory.FullName; } +#if LINUX // Porting notes: On platforms with single root filesystems, only // add the filesystem with the root "/" to the initial drive list, // otherwise path handling will not work correctly because there // is no : available to separate the filesystems from each other - if (Platform.HasSingleRootFilesystem() && root != StringLiterals.DefaultPathSeparatorString) + if (root != StringLiterals.DefaultPathSeparatorString) continue; +#endif // Porting notes: On non-windows platforms .net can report two // drives with the same root, make sure to only add one of those diff --git a/src/System.Management.Automation/namespaces/LocationGlobber.cs b/src/System.Management.Automation/namespaces/LocationGlobber.cs index 313e9e0e6c..c150dd7179 100644 --- a/src/System.Management.Automation/namespaces/LocationGlobber.cs +++ b/src/System.Management.Automation/namespaces/LocationGlobber.cs @@ -1589,9 +1589,12 @@ namespace System.Management.Automation /// internal static bool IsSingleFileSystemAbsolutePath(string path) { - return Platform.HasSingleRootFilesystem() && - (path.StartsWith(StringLiterals.DefaultPathSeparatorString, StringComparison.Ordinal) || - path.StartsWith(StringLiterals.AlternatePathSeparatorString, StringComparison.Ordinal)); +#if LINUX + return path.StartsWith(StringLiterals.DefaultPathSeparatorString, StringComparison.Ordinal) + || path.StartsWith(StringLiterals.AlternatePathSeparatorString, StringComparison.Ordinal); +#else + return false; +#endif } // IsSingleFileSystemAbsolutePath /// From 58fe228dc0c4d418a9e2fddfdfa2a33d6e613856 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Thu, 14 Jul 2016 14:38:19 -0700 Subject: [PATCH 19/42] Remove Platform.HasFileShares query --- .../CoreCLR/CorePsPlatform.cs | 8 -------- .../engine/CommandCompletion/CompletionCompleters.cs | 10 ++++------ 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index c4edc350c0..4a9bda08ca 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -217,14 +217,6 @@ namespace System.Management.Automation } - // Linux has no notion of file shares. It has mount points - // instead, which are subdirectories of its single-root - // filesystem. - internal static bool HasFileShares() - { - return IsWindows(); - } - // Linux has no support for UNC, just mounts in a single rooted hierarchy // the UNC equivalent of a "network drive" aka mount would be the mount itself internal static bool HasUNCSupport() diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs index 8c3eb882bf..d3fba6c141 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs @@ -4464,12 +4464,9 @@ namespace System.Management.Automation internal static List GetFileShares(string machine, bool ignoreHidden) { - // Platform notes: don't throw an exception since this is being used in auto-completion - if (!Platform.HasFileShares()) - { - return new List(); - } - +#if LINUX + return new List(); +#else IntPtr shBuf; uint numEntries; uint totalEntries; @@ -4495,6 +4492,7 @@ namespace System.Management.Automation } } return shares; +#endif } private static bool CheckFileExtension(string path, HashSet extension) From 2249afcd0ab8f06e0cff9e031a9b97eea2ca9818 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Thu, 14 Jul 2016 14:51:59 -0700 Subject: [PATCH 20/42] Remove Platform.HasUNCSupport query --- .../CoreCLR/CorePsPlatform.cs | 7 ------- src/System.Management.Automation/engine/Utils.cs | 8 ++++---- .../namespaces/FileSystemProvider.cs | 14 +++++--------- 3 files changed, 9 insertions(+), 20 deletions(-) diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index 4a9bda08ca..7082e29c11 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -217,13 +217,6 @@ namespace System.Management.Automation } - // Linux has no support for UNC, just mounts in a single rooted hierarchy - // the UNC equivalent of a "network drive" aka mount would be the mount itself - internal static bool HasUNCSupport() - { - return IsWindows; - } - // Linux uses .net to query file attributes internal static bool UseDotNetToQueryFileAttributes() { diff --git a/src/System.Management.Automation/engine/Utils.cs b/src/System.Management.Automation/engine/Utils.cs index 878908be5e..7c5cdbe3ac 100644 --- a/src/System.Management.Automation/engine/Utils.cs +++ b/src/System.Management.Automation/engine/Utils.cs @@ -1129,12 +1129,12 @@ namespace System.Management.Automation internal static bool PathIsUnc(string path) { - if (!Platform.HasUNCSupport()) - { - return false; - } +#if LINUX + return false; +#else Uri uri; return !string.IsNullOrEmpty(path) && Uri.TryCreate(path, UriKind.Absolute, out uri) && uri.IsUnc; +#endif } internal class NativeMethods diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs index 6af52e291c..60664ee731 100644 --- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs +++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs @@ -685,15 +685,11 @@ namespace Microsoft.PowerShell.Commands /// internal static string GetUNCForNetworkDrive(string driveName) { - // Porting note: on systems that do not support UNC paths, the UNC equivalent is the path itself - if (Platform.HasUNCSupport()) - { - return WinGetUNCForNetworkDrive(driveName); - } - else - { - return driveName; - } +#if LINUX + return driveName; +#else + return WinGetUNCForNetworkDrive(driveName); +#endif } private static string WinGetUNCForNetworkDrive(string driveName) From ab882cb04d2cbfb4798521828b2c703b9b3cbe24 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Thu, 14 Jul 2016 14:54:51 -0700 Subject: [PATCH 21/42] Remove Platform.UseDotNetToQueryFileAttributes query --- .../CoreCLR/CorePsPlatform.cs | 6 -- .../namespaces/FileSystemProvider.cs | 79 +++++++++---------- 2 files changed, 39 insertions(+), 46 deletions(-) diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index 7082e29c11..1e7e979e52 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -217,12 +217,6 @@ namespace System.Management.Automation } - // Linux uses .net to query file attributes - internal static bool UseDotNetToQueryFileAttributes() - { - return true; - } - // Linux does not have group policy support internal static bool HasGroupPolicySupport() { diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs index 60664ee731..893337255c 100644 --- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs +++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs @@ -6969,48 +6969,47 @@ namespace Microsoft.PowerShell.Commands internal static int SafeGetFileAttributes(string path) { - if (Platform.UseDotNetToQueryFileAttributes()) - { - System.IO.FileAttributes attr = System.IO.File.GetAttributes(path); +#if LINUX + System.IO.FileAttributes attr = System.IO.File.GetAttributes(path); - int result = 0; - if ((attr & FileAttributes.Archive) == FileAttributes.Archive) - result |= 0x20; - if ((attr & FileAttributes.Compressed) == FileAttributes.Compressed) - result |= 0x800; - if ((attr & FileAttributes.Device) == FileAttributes.Device) - result |= 0x40; - if ((attr & FileAttributes.Directory) == FileAttributes.Directory) - result |= 0x10; - if ((attr & FileAttributes.Encrypted) == FileAttributes.Encrypted) - result |= 0x4000; - if ((attr & FileAttributes.Hidden) == FileAttributes.Hidden) - result |= 0x2; - if ((attr & FileAttributes.IntegrityStream) == FileAttributes.IntegrityStream) - result |= 0x8000; - if ((attr & FileAttributes.Normal) == FileAttributes.Normal) - result |= 0x80; - if ((attr & FileAttributes.NoScrubData) == FileAttributes.NoScrubData) - result |= 0x20000; - if ((attr & FileAttributes.NotContentIndexed) == FileAttributes.NotContentIndexed) - result |= 0x2000; - if ((attr & FileAttributes.Offline) == FileAttributes.Offline) - result |= 0x1000; - if ((attr & FileAttributes.ReadOnly) == FileAttributes.ReadOnly) - result |= 0x1; - if ((attr & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint) - result |= 0x400; - if ((attr & FileAttributes.SparseFile) == FileAttributes.SparseFile) - result |= 0x200; - if ((attr & FileAttributes.System) == FileAttributes.System) - result |= 0x4; - if ((attr & FileAttributes.Temporary) == FileAttributes.Temporary) - result |= 0x100; + int result = 0; + if ((attr & FileAttributes.Archive) == FileAttributes.Archive) + result |= 0x20; + if ((attr & FileAttributes.Compressed) == FileAttributes.Compressed) + result |= 0x800; + if ((attr & FileAttributes.Device) == FileAttributes.Device) + result |= 0x40; + if ((attr & FileAttributes.Directory) == FileAttributes.Directory) + result |= 0x10; + if ((attr & FileAttributes.Encrypted) == FileAttributes.Encrypted) + result |= 0x4000; + if ((attr & FileAttributes.Hidden) == FileAttributes.Hidden) + result |= 0x2; + if ((attr & FileAttributes.IntegrityStream) == FileAttributes.IntegrityStream) + result |= 0x8000; + if ((attr & FileAttributes.Normal) == FileAttributes.Normal) + result |= 0x80; + if ((attr & FileAttributes.NoScrubData) == FileAttributes.NoScrubData) + result |= 0x20000; + if ((attr & FileAttributes.NotContentIndexed) == FileAttributes.NotContentIndexed) + result |= 0x2000; + if ((attr & FileAttributes.Offline) == FileAttributes.Offline) + result |= 0x1000; + if ((attr & FileAttributes.ReadOnly) == FileAttributes.ReadOnly) + result |= 0x1; + if ((attr & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint) + result |= 0x400; + if ((attr & FileAttributes.SparseFile) == FileAttributes.SparseFile) + result |= 0x200; + if ((attr & FileAttributes.System) == FileAttributes.System) + result |= 0x4; + if ((attr & FileAttributes.Temporary) == FileAttributes.Temporary) + result |= 0x100; - return result; - } - else - return WinSafeGetFileAttributes(path); + return result; +#else + return WinSafeGetFileAttributes(path); +#endif } internal static int WinSafeGetFileAttributes(string path) From 662e3909027db661d3a0413f8a76d04dbb635126 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Thu, 14 Jul 2016 14:57:46 -0700 Subject: [PATCH 22/42] Remove Platform.HasGroupPolicySupport query --- .../CoreCLR/CorePsPlatform.cs | 6 ------ src/System.Management.Automation/engine/Utils.cs | 11 ++++------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index 1e7e979e52..2cac3ebd5d 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -217,12 +217,6 @@ namespace System.Management.Automation } - // Linux does not have group policy support - internal static bool HasGroupPolicySupport() - { - return IsWindows; - } - // non-windows does not have network drive support internal static bool HasNetworkDriveSupport() { diff --git a/src/System.Management.Automation/engine/Utils.cs b/src/System.Management.Automation/engine/Utils.cs index 7c5cdbe3ac..87c2962eaa 100644 --- a/src/System.Management.Automation/engine/Utils.cs +++ b/src/System.Management.Automation/engine/Utils.cs @@ -649,13 +649,9 @@ namespace System.Management.Automation internal static Dictionary GetGroupPolicySetting(string groupPolicyBase, string settingName, RegistryKey[] preferenceOrder) { - if (!Platform.HasGroupPolicySupport()) - { - // Porting note: No group policy support means we have an empty set of - // policies. - return _emptyDictionary; - } - +#if LINUX + return _emptyDictionary; +#else lock (cachedGroupPolicySettings) { // Return cached information, if we have it @@ -730,6 +726,7 @@ namespace System.Management.Automation return settings; } +#endif } static ConcurrentDictionary> cachedGroupPolicySettings = new ConcurrentDictionary>(); From 465e55d32a903f704954d3d74aae12f9771403d1 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Thu, 14 Jul 2016 15:41:17 -0700 Subject: [PATCH 23/42] Remove Platform.HasNetworkDriveSupport query --- .../CoreCLR/CorePsPlatform.cs | 6 ---- .../namespaces/FileSystemProvider.cs | 33 +++++++++---------- 2 files changed, 15 insertions(+), 24 deletions(-) diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index 2cac3ebd5d..7f72c54fb8 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -217,12 +217,6 @@ namespace System.Management.Automation } - // non-windows does not have network drive support - internal static bool HasNetworkDriveSupport() - { - return IsWindows; - } - // non-windows does not have reparse points internal static bool SupportsReparsePoints() { diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs index 893337255c..c9d7236b2c 100644 --- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs +++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs @@ -743,14 +743,11 @@ namespace Microsoft.PowerShell.Commands /// internal static string GetSubstitutedPathForNetworkDosDevice(string driveName) { - if (Platform.HasNetworkDriveSupport()) - { - return WinGetSubstitutedPathForNetworkDosDevice(driveName); - } - else - { - throw new PlatformNotSupportedException(); - } +#if LINUX + throw new PlatformNotSupportedException(); +#else + return WinGetSubstitutedPathForNetworkDosDevice(driveName); +#endif } private static string WinGetSubstitutedPathForNetworkDosDevice(string driveName) @@ -922,15 +919,15 @@ namespace Microsoft.PowerShell.Commands if (newDrive.DriveType == DriveType.Network) { // Platform notes: This is important because certain mount - // points on non-Windows are enumerated as drives by .net, but + // points on non-Windows are enumerated as drives by .NET, but // the platform itself then has no real network drive support // as required by this context. Solution: check for network // drive support before using it. - if (!Platform.HasNetworkDriveSupport()) - { - continue; - } +#if LINUX + continue; +#else displayRoot = GetRootPathForNetworkDriveOrDosDevice(newDrive); +#endif } if (newDrive.DriveType == DriveType.Fixed) @@ -7071,11 +7068,11 @@ namespace Microsoft.PowerShell.Commands /// internal static bool PathIsNetworkPath(string path) { - // no other logic possible for now on linux - if (Platform.HasNetworkDriveSupport()) - return WinPathIsNetworkPath(path); - else - return false; +#if LINUX + return false; +#else + return WinPathIsNetworkPath(path); +#endif } internal static bool WinPathIsNetworkPath(string path) From dbe16ca5d72a59431bfa0dd7043ed08abc0dbe0e Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Thu, 14 Jul 2016 15:44:47 -0700 Subject: [PATCH 24/42] Remove Platform.SupportsReparsePoints query --- .../CoreCLR/CorePsPlatform.cs | 6 ------ .../namespaces/FileSystemProvider.cs | 13 +++++-------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index 7f72c54fb8..5f780cef1f 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -217,12 +217,6 @@ namespace System.Management.Automation } - // non-windows does not have reparse points - internal static bool SupportsReparsePoints() - { - return IsWindows; - } - // non-windows does not support removing drives internal static bool SupportsRemoveDrive() { diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs index c9d7236b2c..4b4c024c70 100644 --- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs +++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs @@ -8589,14 +8589,11 @@ namespace Microsoft.PowerShell.Commands private static SafeFileHandle OpenReparsePoint(string reparsePoint, FileDesiredAccess accessMode) { - if (Platform.SupportsReparsePoints()) - { - return WinOpenReparsePoint(reparsePoint,accessMode); - } - else - { - throw new PlatformNotSupportedException(); - } +#if LINUX + throw new PlatformNotSupportedException(); +#else + return WinOpenReparsePoint(reparsePoint,accessMode); +#endif } private static SafeFileHandle WinOpenReparsePoint(string reparsePoint, FileDesiredAccess accessMode) From a9484e45e91a4bc575ff21928847171fc72c6eae Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Thu, 14 Jul 2016 15:46:59 -0700 Subject: [PATCH 25/42] Remove Platform.SupportsRemoveDrive query --- .../CoreCLR/CorePsPlatform.cs | 6 ------ .../namespaces/FileSystemProvider.cs | 14 +++++--------- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index 5f780cef1f..8f10fa2bdc 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -217,12 +217,6 @@ namespace System.Management.Automation } - // non-windows does not support removing drives - internal static bool SupportsRemoveDrive() - { - return IsWindows; - } - // Platform methods prefixed NonWindows are: // - non-windows by the definition of the IsWindows method above // - here, because porting to Linux and other operating systems diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs index 4b4c024c70..d5e370e6cd 100644 --- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs +++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs @@ -607,15 +607,11 @@ namespace Microsoft.PowerShell.Commands /// protected override PSDriveInfo RemoveDrive(PSDriveInfo drive) { - // if removing the drive is not supported, just return the drive - if (Platform.SupportsRemoveDrive()) - { - return WinRemoveDrive(drive); - } - else - { - return drive; - } +#if LINUX + return drive; +#else + return WinRemoveDrive(drive); +#endif } private PSDriveInfo WinRemoveDrive(PSDriveInfo drive) From 89508ae99daa3811f588864d4f7c4432fc139194 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Thu, 14 Jul 2016 15:58:37 -0700 Subject: [PATCH 26/42] Revert IsWinPEHost changes --- .../engine/Utils.cs | 66 +++++-------------- 1 file changed, 16 insertions(+), 50 deletions(-) diff --git a/src/System.Management.Automation/engine/Utils.cs b/src/System.Management.Automation/engine/Utils.cs index 87c2962eaa..51d990c9a7 100644 --- a/src/System.Management.Automation/engine/Utils.cs +++ b/src/System.Management.Automation/engine/Utils.cs @@ -62,15 +62,6 @@ namespace System.Management.Automation return CombineHashCodes(CombineHashCodes(h1, h2, h3, h4), CombineHashCodes(h5, h6, h7, h8)); } - /// - /// IsWinPEHost indicates if the machine on which PowerShell is hosted is WinPE or not. - /// This is a helper variable used to kep track if the IsWinPE() helper method has - /// already checked for the WinPE specific registry key or not. - /// If the WinPE specific registry key has not yet been checked even - /// once then this variable will point to null. - /// - internal static bool? isWinPEHost = null; - /// /// The existence of the following registry confirms that the host machine is a WinPE /// HKLM\System\CurrentControlSet\Control\MiniNT @@ -368,55 +359,30 @@ namespace System.Management.Automation /// internal static bool IsWinPEHost() { -#if LINUX - return false; -#else - return WinIsWinPEHost(); -#endif - } - - internal static bool WinIsWinPEHost() - { RegistryKey winPEKey = null; - if (!isWinPEHost.HasValue) + try { - try - { - // The existence of the following registry confirms that the host machine is a WinPE - // HKLM\System\CurrentControlSet\Control\MiniNT - winPEKey = Registry.LocalMachine.OpenSubKey(WinPEIdentificationRegKey); + // The existence of the following registry confirms that the host machine is a WinPE + // HKLM\System\CurrentControlSet\Control\MiniNT + winPEKey = Registry.LocalMachine.OpenSubKey(WinPEIdentificationRegKey); - if (null != winPEKey) - { - isWinPEHost = true; - } - else - { - isWinPEHost = false; - } - } - catch (ArgumentException) { } - catch (SecurityException) { } - catch (ObjectDisposedException) { } - finally + return winPEKey != null; + } + catch (ArgumentException) { } + catch (SecurityException) { } + catch (ObjectDisposedException) { } + finally + { + if (winPEKey != null) { - if (winPEKey != null) - { - winPEKey.Dispose(); - } + winPEKey.Dispose(); } } - if (isWinPEHost.HasValue) - { - return (bool)isWinPEHost; - } - else - { - return false; - } - } + return false; + } + #region Versioning related methods From ad94ddb3f1808ce7d79c3c6c6e1ddfcf3814f20c Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Thu, 14 Jul 2016 15:59:54 -0700 Subject: [PATCH 27/42] Guard IsWinPEHost --- src/System.Management.Automation/engine/Utils.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/System.Management.Automation/engine/Utils.cs b/src/System.Management.Automation/engine/Utils.cs index 51d990c9a7..d5b29ad010 100644 --- a/src/System.Management.Automation/engine/Utils.cs +++ b/src/System.Management.Automation/engine/Utils.cs @@ -359,6 +359,7 @@ namespace System.Management.Automation /// internal static bool IsWinPEHost() { +#if !LINUX RegistryKey winPEKey = null; try @@ -379,7 +380,7 @@ namespace System.Management.Automation winPEKey.Dispose(); } } - +#endif return false; } From 32e0a7dcaa5f8c764cf4a06c37c6adc35d21de66 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Thu, 14 Jul 2016 16:12:09 -0700 Subject: [PATCH 28/42] Format New-Item.Tests.ps1 --- .../New-Item.Tests.ps1 | 80 +++++++++---------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/New-Item.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/New-Item.Tests.ps1 index 593f892ad9..155f2226f0 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/New-Item.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/New-Item.Tests.ps1 @@ -2,17 +2,17 @@ function Clean-State { if (Test-Path $FullyQualifiedLink) { - Remove-Item $FullyQualifiedLink -Force + Remove-Item $FullyQualifiedLink -Force } if (Test-Path $FullyQualifiedFile) { - Remove-Item $FullyQualifiedFile -Force + Remove-Item $FullyQualifiedFile -Force } if (Test-Path $FullyQualifiedFolder) { - Remove-Item $FullyQualifiedFolder -Force + Remove-Item $FullyQualifiedFolder -Force } } @@ -26,17 +26,17 @@ Describe "New-Item" { $FullyQualifiedLink = Join-Path -Path $tmpDirectory -ChildPath $testlink BeforeEach { - Clean-State + Clean-State } It "should call the function without error" { - { New-Item -Name $testfile -Path $tmpDirectory -ItemType file } | Should Not Throw + { New-Item -Name $testfile -Path $tmpDirectory -ItemType file } | Should Not Throw } It "Should create a file without error" { - New-Item -Name $testfile -Path $tmpDirectory -ItemType file + New-Item -Name $testfile -Path $tmpDirectory -ItemType file - Test-Path $FullyQualifiedFile | Should Be $true + Test-Path $FullyQualifiedFile | Should Be $true $fileInfo = Get-ChildItem $FullyQualifiedFile $fileInfo.Target | Should Be $null @@ -44,72 +44,72 @@ Describe "New-Item" { } It "Should create a folder without an error" { - New-Item -Name newDirectory -Path $tmpDirectory -ItemType directory + New-Item -Name newDirectory -Path $tmpDirectory -ItemType directory - Test-Path $FullyQualifiedFolder | Should Be $true + Test-Path $FullyQualifiedFolder | Should Be $true } It "Should create a file using the ni alias" { - ni -Name $testfile -Path $tmpDirectory -ItemType file + ni -Name $testfile -Path $tmpDirectory -ItemType file - Test-Path $FullyQualifiedFile | Should Be $true + Test-Path $FullyQualifiedFile | Should Be $true } It "Should create a file using the Type alias instead of ItemType" { - New-Item -Name $testfile -Path $tmpDirectory -Type file + New-Item -Name $testfile -Path $tmpDirectory -Type file - Test-Path $FullyQualifiedFile | Should Be $true + Test-Path $FullyQualifiedFile | Should Be $true } It "Should create a file with sample text inside the file using the Value switch" { - $expected = "This is test string" - New-Item -Name $testfile -Path $tmpDirectory -ItemType file -Value $expected + $expected = "This is test string" + New-Item -Name $testfile -Path $tmpDirectory -ItemType file -Value $expected - Test-Path $FullyQualifiedFile | Should Be $true + Test-Path $FullyQualifiedFile | Should Be $true - Get-Content $FullyQualifiedFile | Should Be $expected + Get-Content $FullyQualifiedFile | Should Be $expected } It "Should not create a file when the Name switch is not used and only a directory specified" { - #errorAction used because permissions issue in Windows - New-Item -Path $tmpDirectory -ItemType file -ErrorAction SilentlyContinue + #errorAction used because permissions issue in Windows + New-Item -Path $tmpDirectory -ItemType file -ErrorAction SilentlyContinue - Test-Path $FullyQualifiedFile | Should Be $false + Test-Path $FullyQualifiedFile | Should Be $false } It "Should create a file when the Name switch is not used but a fully qualified path is specified" { - New-Item -Path $FullyQualifiedFile -ItemType file + New-Item -Path $FullyQualifiedFile -ItemType file - Test-Path $FullyQualifiedFile | Should Be $true + Test-Path $FullyQualifiedFile | Should Be $true } It "Should be able to create a multiple items in different directories" { - $FullyQualifiedFile2 = Join-Path -Path $tmpDirectory -ChildPath test2.txt - New-Item -ItemType file -Path $FullyQualifiedFile, $FullyQualifiedFile2 + $FullyQualifiedFile2 = Join-Path -Path $tmpDirectory -ChildPath test2.txt + New-Item -ItemType file -Path $FullyQualifiedFile, $FullyQualifiedFile2 - Test-Path $FullyQualifiedFile | Should Be $true - Test-Path $FullyQualifiedFile2 | Should Be $true + Test-Path $FullyQualifiedFile | Should Be $true + Test-Path $FullyQualifiedFile2 | Should Be $true - Remove-Item $FullyQualifiedFile2 + Remove-Item $FullyQualifiedFile2 } It "Should be able to call the whatif switch without error" { - { New-Item -Name testfile.txt -Path $tmpDirectory -ItemType file -WhatIf } | Should Not Throw + { New-Item -Name testfile.txt -Path $tmpDirectory -ItemType file -WhatIf } | Should Not Throw } It "Should not create a new file when the whatif switch is used" { - New-Item -Name $testfile -Path $tmpDirectory -ItemType file -WhatIf + New-Item -Name $testfile -Path $tmpDirectory -ItemType file -WhatIf - Test-Path $FullyQualifiedFile | Should Be $false + Test-Path $FullyQualifiedFile | Should Be $false } It "Should create a symbolic link of a file without error" { - New-Item -Name $testfile -Path $tmpDirectory -ItemType file - Test-Path $FullyQualifiedFile | Should Be $true + New-Item -Name $testfile -Path $tmpDirectory -ItemType file + Test-Path $FullyQualifiedFile | Should Be $true - New-Item -ItemType SymbolicLink -Target $FullyQualifiedFile -Name $testlink -Path $tmpDirectory - Test-Path $FullyQualifiedLink | Should Be $true + New-Item -ItemType SymbolicLink -Target $FullyQualifiedFile -Name $testlink -Path $tmpDirectory + Test-Path $FullyQualifiedLink | Should Be $true $fileInfo = Get-ChildItem $FullyQualifiedLink $fileInfo.Target | Should Match ([regex]::Escape($FullyQualifiedFile)) @@ -128,18 +128,18 @@ Describe "New-Item" { } It "Should create a symbolic link from directory without error" { - New-Item -Name $testFolder -Path $tmpDirectory -ItemType directory - Test-Path $FullyQualifiedFolder | Should Be $true + New-Item -Name $testFolder -Path $tmpDirectory -ItemType directory + Test-Path $FullyQualifiedFolder | Should Be $true - New-Item -ItemType SymbolicLink -Target $FullyQualifiedFolder -Name $testlink -Path $tmpDirectory - Test-Path $FullyQualifiedLink | Should Be $true + New-Item -ItemType SymbolicLink -Target $FullyQualifiedFolder -Name $testlink -Path $tmpDirectory + Test-Path $FullyQualifiedLink | Should Be $true $fileInfo = Get-ChildItem $FullyQualifiedLink $fileInfo.Target | Should Match ([regex]::Escape($FullyQualifiedFolder)) $fileInfo.LinkType | Should Be "SymbolicLink" - # Remove the link explicitly to avoid broken symlink issue - Remove-Item $FullyQualifiedLink -Force + # Remove the link explicitly to avoid broken symlink issue + Remove-Item $FullyQualifiedLink -Force } It "Should create a hard link of a file without error" { From 35e24ce6d44b63ad6582d6ade8363f614348aa99 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Thu, 14 Jul 2016 17:13:45 -0700 Subject: [PATCH 29/42] Clean up CreateSymLink() --- .../CoreCLR/CorePsPlatform.cs | 13 ++----- .../namespaces/FileSystemProvider.cs | 2 +- src/libpsl-native/src/createsymlink.cpp | 18 ++++----- src/libpsl-native/src/createsymlink.h | 3 +- src/libpsl-native/test/test-createsymlink.cpp | 38 +++++++++---------- 5 files changed, 35 insertions(+), 39 deletions(-) diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index 8f10fa2bdc..80d434265a 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -280,10 +280,10 @@ namespace System.Management.Automation return null; } - internal static bool NonWindowsCreateSymbolicLink(string path, string strTargetPath, bool isDirectory) + internal static bool NonWindowsCreateSymbolicLink(string path, string target) { // Linux doesn't care if target is a directory or not - return Unix.CreateSymbolicLink(path, strTargetPath); + return Unix.NativeMethods.CreateSymLink(path, target); } internal static bool NonWindowsCreateHardLink(string path, string strTargetPath) @@ -536,12 +536,6 @@ namespace System.Management.Automation } } - public static bool CreateSymbolicLink(string path, string strTargetPath) - { - int ret = NativeMethods.CreateSymLink(path, strTargetPath); - return ret == 1 ? true : false; - } - public static bool CreateHardLink(string path, string strTargetPath) { int ret = NativeMethods.CreateHardLink(path, strTargetPath); @@ -620,7 +614,8 @@ namespace System.Management.Automation internal static extern int SetDate(SetDateInfoInternal info); [DllImport(psLib, CharSet = CharSet.Ansi, SetLastError = true)] - internal static extern int CreateSymLink([MarshalAs(UnmanagedType.LPStr)]string filePath, + [return: MarshalAs(UnmanagedType.I1)] + internal static extern bool CreateSymLink([MarshalAs(UnmanagedType.LPStr)]string filePath, [MarshalAs(UnmanagedType.LPStr)]string target); [DllImport(psLib, CharSet = CharSet.Ansi, SetLastError = true)] diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs index d5e370e6cd..225caeffc7 100644 --- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs +++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs @@ -2220,7 +2220,7 @@ namespace Microsoft.PowerShell.Commands } else { - success = Platform.NonWindowsCreateSymbolicLink(path,strTargetPath,isDirectory); + success = Platform.NonWindowsCreateSymbolicLink(path,strTargetPath); } } else if(itemType == ItemType.HardLink) diff --git a/src/libpsl-native/src/createsymlink.cpp b/src/libpsl-native/src/createsymlink.cpp index 4ea8d6fd9d..b9ce2b18d1 100644 --- a/src/libpsl-native/src/createsymlink.cpp +++ b/src/libpsl-native/src/createsymlink.cpp @@ -39,26 +39,25 @@ //! - ERROR_INVALID_FUNCTION: incorrect function //! - ERROR_BAD_PATH_NAME: pathname is too long, or contains invalid characters //! -//! @retval 1 if creation is successful -//! @retval 0 if createion failed +//! @retval boolean successful //! -int32_t CreateSymLink(const char *link, const char *target) +bool CreateSymLink(const char *link, const char *target) { - errno = 0; + errno = 0; // Check parameters if (!link || !target) { errno = ERROR_INVALID_PARAMETER; - return 0; + return false; } - int returnCode = symlink(target, link); + int ret = symlink(target, link); - if (returnCode == 0) + if (ret == 0) { - return 1; + return true; } switch(errno) @@ -102,5 +101,6 @@ int32_t CreateSymLink(const char *link, const char *target) default: errno = ERROR_INVALID_FUNCTION; } - return 0; + + return false; } diff --git a/src/libpsl-native/src/createsymlink.h b/src/libpsl-native/src/createsymlink.h index e47bce3fa1..45da928902 100644 --- a/src/libpsl-native/src/createsymlink.h +++ b/src/libpsl-native/src/createsymlink.h @@ -1,9 +1,10 @@ #pragma once #include "pal.h" +#include PAL_BEGIN_EXTERNC -int32_t CreateSymLink(const char *link, const char *target); +bool CreateSymLink(const char *link, const char *target); PAL_END_EXTERNC diff --git a/src/libpsl-native/test/test-createsymlink.cpp b/src/libpsl-native/test/test-createsymlink.cpp index 7bb9997b30..1aab3379a0 100644 --- a/src/libpsl-native/test/test-createsymlink.cpp +++ b/src/libpsl-native/test/test-createsymlink.cpp @@ -39,36 +39,36 @@ protected: EXPECT_TRUE(dir != NULL); // Create symbolic link to file - int ret1 = CreateSymLink(fileSymLink.c_str(), file); - EXPECT_EQ(ret1, 1); + bool ret1 = CreateSymLink(fileSymLink.c_str(), file); + EXPECT_TRUE(ret1); // Create symbolic link to directory - int ret2 = CreateSymLink(dirSymLink.c_str(), dir); - EXPECT_EQ(ret2, 1); + bool ret2 = CreateSymLink(dirSymLink.c_str(), dir); + EXPECT_TRUE(ret2); } ~CreateSymLinkTest() { - int ret; + bool ret; ret = unlink(fileSymLink.c_str()); - EXPECT_EQ(0, ret); + EXPECT_FALSE(ret); ret = unlink(dirSymLink.c_str()); - EXPECT_EQ(0, ret); + EXPECT_FALSE(ret); ret = unlink(file); - EXPECT_EQ(0, ret); + EXPECT_FALSE(ret); ret = rmdir(dir); - EXPECT_EQ(0, ret); + EXPECT_FALSE(ret); } }; TEST_F(CreateSymLinkTest, FilePathNameIsNull) { - int retVal = CreateSymLink(NULL, NULL); - EXPECT_EQ(retVal, 0); + bool retVal = CreateSymLink(NULL, NULL); + EXPECT_FALSE(retVal); EXPECT_EQ(ERROR_INVALID_PARAMETER, errno); } @@ -82,8 +82,8 @@ TEST_F(CreateSymLinkTest, FilePathNameDoesNotExist) unlink(invalidLink.c_str()); // Linux allows creation of symbolic link that points to an invalid file - int retVal = CreateSymLink(invalidLink.c_str(), invalidFile.c_str()); - EXPECT_EQ(retVal, 1); + bool retVal = CreateSymLink(invalidLink.c_str(), invalidFile.c_str()); + EXPECT_TRUE(retVal); std::string target = FollowSymLink(invalidLink.c_str()); EXPECT_EQ(target, invalidFile); @@ -93,8 +93,8 @@ TEST_F(CreateSymLinkTest, FilePathNameDoesNotExist) TEST_F(CreateSymLinkTest, SymLinkToFile) { - int retVal = IsSymLink(fileSymLink.c_str()); - EXPECT_EQ(1, retVal); + bool retVal = IsSymLink(fileSymLink.c_str()); + EXPECT_TRUE(retVal); std::string target = FollowSymLink(fileSymLink.c_str()); char buffer[PATH_MAX]; @@ -104,8 +104,8 @@ TEST_F(CreateSymLinkTest, SymLinkToFile) TEST_F(CreateSymLinkTest, SymLinkToDirectory) { - int retVal = IsSymLink(dirSymLink.c_str()); - EXPECT_EQ(1, retVal); + bool retVal = IsSymLink(dirSymLink.c_str()); + EXPECT_TRUE(retVal); std::string target = FollowSymLink(dirSymLink.c_str()); char buffer[PATH_MAX]; @@ -115,7 +115,7 @@ TEST_F(CreateSymLinkTest, SymLinkToDirectory) TEST_F(CreateSymLinkTest, SymLinkAgain) { - int retVal = CreateSymLink(fileSymLink.c_str(), file); - EXPECT_EQ(0, retVal); + bool retVal = CreateSymLink(fileSymLink.c_str(), file); + EXPECT_FALSE(retVal); EXPECT_EQ(ERROR_FILE_EXISTS, errno); } From 74c3262f03c4ea1c842402d265f93a83df5e3864 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Thu, 14 Jul 2016 17:24:00 -0700 Subject: [PATCH 30/42] Fix failing New-Item tests PowerShell expects the behavior of NativeItemExists to not follow symbolic links. --- src/libpsl-native/src/isfile.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libpsl-native/src/isfile.cpp b/src/libpsl-native/src/isfile.cpp index b2cead6cd7..89c415f472 100644 --- a/src/libpsl-native/src/isfile.cpp +++ b/src/libpsl-native/src/isfile.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include "isfile.h" @@ -36,5 +37,6 @@ bool IsFile(const char* path) return false; } - return access(path, F_OK) != -1; + // Resolve relative paths to cwd of process, and do not follow symlinks + return faccessat(AT_FDCWD, path, F_OK, AT_SYMLINK_NOFOLLOW) != -1; } From 70aa6af9604ca5c8ac2e4c8ffe8b96d4d47de20d Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Thu, 14 Jul 2016 17:30:54 -0700 Subject: [PATCH 31/42] Remove unnecessary indirection from Platform.Unix --- .../CoreCLR/CorePsPlatform.cs | 28 +++---------------- 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index 80d434265a..e4a8701b6e 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -239,7 +239,7 @@ namespace System.Management.Automation internal static bool NonWindowsIsSymLink(FileSystemInfo fileInfo) { - return Unix.IsSymLink(fileInfo); + return Unix.NativeMethods.IsSymLink(fileInfo.FullName); } internal static string NonWindowsInternalGetTarget(SafeFileHandle handle) @@ -250,12 +250,12 @@ namespace System.Management.Automation internal static string NonWindowsInternalGetTarget(string path) { - return Unix.FollowSymLink(path); + return Unix.NativeMethods.FollowSymLink(path); } internal static string NonWindowsGetUserFromPid(int path) { - return Unix.GetUserFromPid(path); + return Unix.NativeMethods.GetUserFromPid(path); } #if CORECLR @@ -344,7 +344,7 @@ namespace System.Management.Automation internal static bool NonWindowsIsExecutable(string path) { - return Unix.IsExecutable(path); + return Unix.NativeMethods.IsExecutable(path); } internal static uint NonWindowsGetThreadId() @@ -516,16 +516,6 @@ namespace System.Management.Automation } - public static bool IsSymLink(FileSystemInfo fs) - { - return NativeMethods.IsSymLink(fs.FullName); - } - - public static bool IsExecutable(string filePath) - { - return NativeMethods.IsExecutable(filePath); - } - public static void SetDate(SetDateInfoInternal info) { int ret = NativeMethods.SetDate(info); @@ -542,16 +532,6 @@ namespace System.Management.Automation return ret == 1 ? true : false; } - public static string FollowSymLink(string path) - { - return NativeMethods.FollowSymLink(path); - } - - public static string GetUserFromPid(int pid) - { - return NativeMethods.GetUserFromPid(pid); - } - [StructLayout(LayoutKind.Sequential)] internal class SetDateInfoInternal { From eb517bea4159e2ec8c789bd92054ffcecc435ae6 Mon Sep 17 00:00:00 2001 From: Jianyun Tao Date: Thu, 14 Jul 2016 17:50:56 -0700 Subject: [PATCH 32/42] Removed Get-Help example. Fixed typos. --- .../debugging-from-commandline.md | 30 ++++----- .../learning-powershell.md | 2 +- .../powershell-beginners-guide.md | 63 ++++++++++++------- docs/learning-powerShell/using-ise.md | 26 ++++---- docs/learning-powerShell/using-vscode.md | 27 ++++---- 5 files changed, 85 insertions(+), 63 deletions(-) diff --git a/docs/learning-powerShell/debugging-from-commandline.md b/docs/learning-powerShell/debugging-from-commandline.md index b4568bd39a..bde85777db 100644 --- a/docs/learning-powerShell/debugging-from-commandline.md +++ b/docs/learning-powerShell/debugging-from-commandline.md @@ -1,7 +1,7 @@ Debugging in PowerShell Command-line ===== -As we know we can debug PowerShell code via GUI tools like [VS Code](./using-vscode.md#debugging-with-vs-code) or [ISE](./using-ise.md#debugging-with-ise). In addition we can directly perform debugging within the PowerShell command-line session by using the PowerShell debugger cmdlets. This document demonstrates how to use the cmdlets for the PowerShell command-line debugging. We will cover two topics: set a debug breakpoint on a line of code and on a variable. +As we know, we can debug PowerShell code via GUI tools like [VS Code](./using-vscode.md#debugging-with-vs-code) or [ISE](./using-ise.md#debugging-with-ise). In addition, we can directly perform debugging within the PowerShell command-line session by using the PowerShell debugger cmdlets. This document demonstrates how to use the cmdlets for the PowerShell command-line debugging. We will cover the following topics: setting a debug breakpoint on a line of code and on a variable. Let's use the following code snippet as our sample script. @@ -21,17 +21,17 @@ Write-Host "$result Celsius" ``` - **1. Set a Breakpoint on a Line** + **1. Setting a Breakpoint on a Line** - Open a [PowerShell editor](learning-powershell.md#powershell-editor) -- Save the above code snippet to a file, let's say "test.ps1" +- Save the above code snippet to a file. For example, "test.ps1" - Go to your command-line PowerShell - Clear existing breakpoints if any ```PowerShell PS /home/jen/debug>Get-PSBreakpoint | Remove-PSBreakpoint ``` -- Use **Set-PSBreakpoint** cmdlet to set a debug breakpoint, say set it to line 5 +- Use **Set-PSBreakpoint** cmdlet to set a debug breakpoint. In this case, we will set it to line 5 ```PowerShell PS /home/jen/debug>Set-PSBreakpoint -Line 5 -Script ./test.ps1 @@ -40,7 +40,7 @@ ID Script Line Command Variable Action -- ------ ---- ------- -------- ------ 0 test.ps1 5 ``` -- Run the script, test.ps1. As we have set a breakpoint, it is expected the program will break into the debugger at the line 5. +- Run the script "test.ps1". As we have set a breakpoint, it is expected the program will break into the debugger at the line 5. ```PowerShell @@ -54,10 +54,10 @@ At /home/jen/debug/test.ps1:5 char:1 [DBG]: PS /home/jen/debug>> ``` -- The PowerShell prompt has been changed to **[DBG]: PS /home/jen/debug>>** as you may noticed. This means - we have entered into the debug mode. To watch the variables like $celsius, simply type $celsius as below. -- To exit from the debugging, type **"q"** -- To get help for the debugging commands, simple type **"?"** +- The PowerShell prompt now has the prefix **[DBG]:** as you may noticed. This means + we have entered into the debug mode. To watch the variables like $celsius, simply type **$celsius** as below. +- To exit from the debugging, type **q** +- To get help for the debugging commands, simply type **?**. The following is an example of debugging output. ```PowerShell [DBG]: PS /home/jen/debug>> $celsius @@ -103,13 +103,13 @@ PS /home/jen/debug> ``` -**2. Set a Breakpoint on a Variable** -- Clear existing breakpoints if any +**2. Setting a Breakpoint on a Variable** +- Clear existing breakpoints if there are any ```PowerShell PS /home/jen/debug>Get-PSBreakpoint | Remove-PSBreakpoint ``` -- Use **Set-PSBreakpoint** cmdlet to set a debug breakpoint, say set it to line 5 +- Use **Set-PSBreakpoint** cmdlet to set a debug breakpoint. In this case, we set it to line 5 ```PowerShell @@ -117,10 +117,10 @@ PS /home/jen/debug> ``` -- Run the script, test.ps1. +- Run the script "test.ps1" Once hit the debug breakpoint, we can type **l** to list the source code that debugger is currently executing. As we can see line 3 has an asterisk at the front, meaning that's the line the program is currently executing and broke into the debugger as illustrated below. -- Type **q** to exit from the debugging mode +- Type **q** to exit from the debugging mode. The following is an example of debugging output. ```PowerShell @@ -165,7 +165,7 @@ PS /home/jen/debug> ``` -Now you know the basics of the PowerShell debugging from PowerShell command-line. For further learning read the following articles. +Now you know the basics of the PowerShell debugging from PowerShell command-line. For further learning, read the following articles. More Reading diff --git a/docs/learning-powerShell/learning-powershell.md b/docs/learning-powerShell/learning-powershell.md index c348295b12..a22bf78c73 100644 --- a/docs/learning-powerShell/learning-powershell.md +++ b/docs/learning-powerShell/learning-powershell.md @@ -25,7 +25,7 @@ TODO: Raghu for setup-dev-environment.md Getting Started with PowerShell ---- -PowerShell command has a Verb-None structure with a set of parameters. It's easy to learn and use PowerShell. For example, "Get-Process" will display all the running processes on your system. Let's walk through with a few examples by clicking on the [PowerShell Beginner's Guide](powershell-beginners-guide.md). +PowerShell command has a Verb-Noun structure with a set of parameters. It's easy to learn and use PowerShell. For example, "Get-Process" will display all the running processes on your system. Let's walk through with a few examples by clicking on the [PowerShell Beginner's Guide](powershell-beginners-guide.md). Now you have learned the basics of PowerShell. Please continue reading: Editor, Debugger, and Testing Tool if you want to do some development work in PowerShell. diff --git a/docs/learning-powerShell/powershell-beginners-guide.md b/docs/learning-powerShell/powershell-beginners-guide.md index bf04234068..b4f5a0e7bc 100644 --- a/docs/learning-powerShell/powershell-beginners-guide.md +++ b/docs/learning-powerShell/powershell-beginners-guide.md @@ -11,11 +11,17 @@ First you need to launch a PowerShell session by following the [Installing Power Getting Familiar with PowerShell Commands --- +In this section, you will learn how to +- create a file, delete a file and change file directory +- find syntax of PowerShell Cmdlets +- discover what version of PowerShell you are currently using +- exit PowerShell session +- and more -As mentioned above PowerShell commands is designed to have Verb-Noun structure, for instance Get-Process, Set-Location, Clear-Host, etc. Let’s exercise some of the basic PowerShell commands also known as **cmdlets**. +As mentioned above, PowerShell commands is designed to have Verb-Noun structure, for instance Get-Process, Set-Location, Clear-Host, etc. Let’s exercise some of the basic PowerShell commands, also known as **cmdlets**. -Please note that we will use the PowerShell prompt sign **PS />** in the following examples as it shows on Linux. -It looks like **PS C:\>** on Windows. +Please note that we will use the PowerShell prompt sign **PS />** as it appears on Linux in the following examples. +It is shown as **PS C:\>** on Windows. **1. Get-Process**: displays the processes running on your system. @@ -119,10 +125,10 @@ Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 7/7/2016 7:17 PM 0 test.ps1 ``` -You can use the **-value** parameter to add some data to your file. For example, the following command adds the phrase "Write-Host 'Hello There'" as a file content to the test.ps1. Because the test.txt file exists already, we use **-force** parameter to replace the existing content. +You can use the **-Value** parameter to add some data to your file. For example, the following command adds the phrase "Write-Host 'Hello There'" as a file content to the test.ps1. Because the test.ps1 file exists already, we use **-Force** parameter to replace the existing content. ```PowerShell -PS /home/jen> New-Item -Path ./test.ps1 -Value "Write-Host 'hello there'" -force +PS /home/jen> New-Item -Path ./test.ps1 -Value "Write-Host 'hello there'" -Force Directory: /home/jen @@ -132,7 +138,7 @@ Mode LastWriteTime Length Name -a---- 7/7/2016 7:19 PM 24 test.ps1 ``` -There are other ways to add some data to a file, for example, you can use Set-Content to set the file contents: +There are other ways to add some data to a file. For example, you can use Set-Content to set the file contents: ```PowerShell PS /home/jen>Set-Content -Path ./test.ps1 -Value "Write-Host 'hello there again!'" @@ -162,19 +168,34 @@ This cmdlet will delete the file /home/jen/test.ps1: ```PowerShell PS /home/jen> Remove-Item ./test.ps1 ``` -**9. Exit**: - to exit the PowerShell session, type "exit" + +**9. $PSVersionTable**: displays the version of PowerShell you are currently using + +Type **$PSVersionTable** in your PowerShell session, you will see the following. "PSVersion" indicates the +PowerShell version that you are using. + +```PowerShell +Name Value +---- ----- +PSVersion 5.1.10032.0 +PSEdition Linux +PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...} +BuildVersion 3.0.0.0 +CLRVersion +WSManStackVersion 1.0 +PSRemotingProtocolVersion 2.3 +SerializationVersion 1.1.0.1 + +``` + +**10. Exit**: to exit the PowerShell session, type "exit" ```PowerShell PS /home/jen> exit ``` Need Help? ---- -The most important command in PowerShell is possibly the Get-Help, which allows you to quickly learn PowerShell without having to surfing around the Internet. The Get-Help cmdlet also shows you how PowerShell commands work with examples. - - -PS />**Get-Help** - -You can use this cmdlet to get help with any PowerShell commands. +The most important command in PowerShell is possibly the Get-Help, which allows you to quickly learn PowerShell without having to surf around the Internet. The Get-Help cmdlet also shows you how PowerShell commands work with examples. PS />**Get-Help -Name Get-Process** @@ -184,14 +205,14 @@ It shows the syntax and other technical information of the Get-Process cmdlet. PS />**Get-Help -Name Get-Process -Examples** It displays the examples how to use the Get-Process cmdlet. -If you use **-full** parameter, i.e., "Get-Help -Name Get-Process -Full", it will display more technical information. +If you use **-Full** parameter, i.e., "Get-Help -Name Get-Process -Full", it will display more technical information. Discover All Commands Available on Your System ---- -You want to discover what PowerShell cmdlets available on your system. Simple, just run "Get-Command" as below. +You want to discover what PowerShell cmdlets available on your system? Simple, just run "Get-Command" as below. PS /> **Get-Command** @@ -203,14 +224,14 @@ If you want to know the syntax of Get-Process cmdlet, type PS /> **Get-Command Get-Process -Syntax** -If you want to know how to sue the get-process, type +If you want to know how to use the Get-Process, type -PS /> **Get-Help Get-Process -example** +PS /> **Get-Help Get-Process -Example** PowerShell Pipeline '|' ---- -Sometimes when you run Get-ChildItem or "dir", you want to get a list of files in a descending order. To archive that, type: +Sometimes when you run Get-ChildItem or "dir", you want to get a list of files in a descending order. To achieve that, type: ```PowerShell PS /home/jen> dir | sort -Descending ``` @@ -229,10 +250,10 @@ Mode LastWriteTime Length Name ``` How to Create and Run PowerShell scripts ---- -- You can use ISE, VS Code, or any favorite editor to create a PowerShell script and save the script with a .ps1 file extension (helloworld.ps1 in the example) -- To run the script, cd to your current folder and type ./helloworld.ps1 +- You can use ISE, VS Code or your favorite editor to create a PowerShell script and save the script with a .ps1 file extension (for example, helloworld.ps1) +- To run the script, cd to your current folder and type ./yourscript.ps1 (for example, ./helloworld.ps1). -See [Running PowerShell Scripts Is as Easy as 1-2-3] [run-ps] for more details. +Note: if you are using Windows, make sure you set the PowerShell's execution policy to "RemoteSigned" in this case. See [Running PowerShell Scripts Is as Easy as 1-2-3] [run-ps] for more details. [run-ps]:http://windowsitpro.com/powershell/running-powershell-scripts-easy-1-2-3 diff --git a/docs/learning-powerShell/using-ise.md b/docs/learning-powerShell/using-ise.md index 59724b2786..06f34504d1 100644 --- a/docs/learning-powerShell/using-ise.md +++ b/docs/learning-powerShell/using-ise.md @@ -1,14 +1,14 @@ Using PowerShell Integrated Scripting Environment (ISE) ==== -The PowerShell ISE works on Windows. If you are not using Windows, please see [Using VS Code](./using-vscode.md). +The PowerShell ISE only works on Windows. If you are not using Windows, please see [Using VS Code](./using-vscode.md). Editing with ISE --- - Launch PowerShell ISE - * Press Windows Key -> type PowerShell ISE, click on PowerShell ISE to launch the ISE + * Press Windows Key -> type "PowerShell ISE", click on PowerShell ISE to launch the ISE - Create a new PowerShell Script - * Click on File -> New - * Add a few lines of PowerShell scripts in your newly created file, for example, + * Click on **File->New** + * Add a few lines of PowerShell code in your newly created file. In this case, we will use the following script snippet. ```PowerShell # Convert Fahrenheit to Celsius @@ -25,21 +25,21 @@ Write-Host "$result Celsius" ``` * **Note**: You can find more examples [here](http://examples.oreilly.com/9780596528492/). -- Save the script file - * Click on File-> Save As -> type "helloworld.ps1" -- Close the helloworld.ps1 - * File-> Close -- Reopen the helloworld.ps1 - * File-> Open, then choose helloworld.ps1. +- To save the script file + * Click on **File->Save As**. Then type "helloworld.ps1" +- To close the helloworld.ps1 file + * **File->Close** +- To reopen the helloworld.ps1 file + * **File->Open**, then choose helloworld.ps1 - For more details, go to [How to Write and Run Scripts in the Windows PowerShell ISE](https://msdn.microsoft.com/en-us/powershell/scripting/core-powershell/ise/how-to-write-and-run-scripts-in-the-windows-powershell-ise). Debugging with ISE ---- -To execute the entire script file, you can press **F5**; to execute several lines of your scripts, simply select them and press **F8**. However sometimes you would like to stop the execution on a particular line in order to exam some variables to check if the program runs as expected. In that case, you may follow the steps below. Let's take the helloworld.ps1 as an example and assume line 17 is the place where you want to stop. +To execute the entire script file, you can press **F5**. To execute several lines of your scripts, simply select them and press **F8**. If you would like to stop the execution on a particular line in order to examine some variables to check if the program runs as expected. In that case, you may follow the steps below. Let's take the helloworld.ps1 as an example and assume line 17 is the place where you want to stop. -- Set a break point: Move mouse over on the line 17, and press **F9**. You will see the line 17 is highlighted which means a breakpoint gets set. +- Set a break point: Move mouse over on the line 17, and press **F9**. You will see the line 17 is highlighted, which means a breakpoint is set. - Press **F5** to run the script - Enter 80 (or any number in Fahrenheit) from the command line prompt - Notice that the ISE output pane becomes “[DBG]: PS C:\Test>>” prompt. This means the program is in the debugging mode. It stops at Line 17: @@ -52,7 +52,7 @@ Hit Line breakpoint on 'C:\test\helloword.ps1:17' ``` -- From the output pane, you can type $celsius and $fahrenheit to exam the values of these variables to see if they are correct. +- From the output pane, you can type $celsius and $fahrenheit to examine the values of these variables to see if they are correct. ```PowerShell [DBG]: PS C:\Test>> $celsius diff --git a/docs/learning-powerShell/using-vscode.md b/docs/learning-powerShell/using-vscode.md index ebe677f595..38a7d3dc4c 100644 --- a/docs/learning-powerShell/using-vscode.md +++ b/docs/learning-powerShell/using-vscode.md @@ -1,11 +1,11 @@ Using Visual Studio Code (VS Code) ==== -If you are working on Linux and OS X, you cannot use ISE because it is not supported on these platforms. In this case you can choose your favorite editor to write PowerShell scripts. Here we choose VS Code as a PowerShell editor as an example. +If you are working on Linux and OS X, you cannot use ISE because it is not supported on these platforms. In this case, you can choose your favorite editor to write PowerShell scripts. Here we choose VS Code as a PowerShell editor as an example. -You can use VS Code on Windows with PowerShell V5 by using Windows 10 or by installing [Windows Management Framework 5.0 RTM](https://www.microsoft.com/en-us/download/details.aspx?id=50395) for down-level Windows OSs. +You can use VS Code on Windows with PowerShell V5 by using Windows 10 or by installing [Windows Management Framework 5.0 RTM](https://www.microsoft.com/en-us/download/details.aspx?id=50395) for down-level Windows OSs (e.g. Windows 8.1, etc.). -Before starting it, please make sure PowerShell exists on your system. Follow the [Installing PowerShell](./learning-powershell.md#installing-powershell) instruction you can install the PowerShell and launch the PowerShell session. +Before starting it, please make sure PowerShell exists on your system. By following the [Installing PowerShell](./learning-powershell.md#installing-powershell) instructions you can install PowerShell and launch a PowerShell session. Editing with VS Code ---- @@ -28,28 +28,28 @@ Editing with VS Code - Press **F1** (or **Ctrl+Shift+P**) which opens up the “Command Palette” inside the VS Code app. -- In the command palette, type **ext install** and hit Enter. It will show all VS Code extensions available on your system. -- Choose PowerShell and click on Install, you will see something like below +- In the command palette, type **ext install** and hit **Enter**. It will show all VS Code extensions available on your system. +- Choose PowerShell and click on **Install**, you will see something like below ![VSCode](vscode.png) - After the install, you will see the **Install** button turns to **Enable**. -- Click on Enable and OK -- Now you are ready for editing. for example, to create a new file, click File->New; to save it, click File->Save and then provide - a file name, let's say "helloworld.ps1"; to close the file, click on "x"; to exit the VS Code, File->Exit. +- Click on **Enable** and **OK** +- Now you are ready for editing. For example, to create a new file, click **File->New**. To save it, click **File->Save** and then provide + a file name, let's say "helloworld.ps1". To close the file, click on "x" next to the file name. To exit VS Code, **File->Exit**. Debugging with VS Code ---- -- Open a file folder (**File->Open Folder**) where contains the PowerShell modules or scripts you have written already and want to debug. In this example, we saved helloworld.ps1 under home/jen/debug. Thus we select the "debug" folder and open it in VS Code. +- Open a file folder (**File->Open Folder**) that contains the PowerShell modules or scripts you have written already and want to debug. In this example, we saved the helloworld.ps1 under a directory called "demo". Thus we select the "demo" folder and open it in VS Code. - Creating the Debug Configuration (launch.json) - Because some information regarding your scripts is needed for debugger to start executing your script, we need to set up the debug config First. This is one time only to debug PowerShell scripts under your current folder. + Because some information regarding your scripts is needed for debugger to start executing your script, we need to set up the debug config first. This is one-time process to debug PowerShell scripts under your current folder. In our case, the "demo" folder. * Click on the **Debug** icon (or **Ctrl+Shift+D**) - * Click on the **Settings** icon that looks like a gear. The VS Code will prompt you to **Select Environment**. Choose **PowerShell**. Then the VS code will auto create a debug configuration settings file in the same folder. It looks like the following: + * Click on the **Settings** icon that looks like a gear. VS Code will prompt you to **Select Environment**. Choose **PowerShell**. Then VS code will auto create a debug configuration settings file in the same folder. It looks like the following: ```json { "version": "0.2.0", @@ -73,9 +73,10 @@ Debugging with VS Code ] } ``` -- Once the debug configuration is established, now go to your helloworld.ps1 and set a breakpoint by pressing **F9** on a line you wish to debug break into. +- Once the debug configuration is established, go to your helloworld.ps1 and set a breakpoint by pressing **F9** on a line you wish to debug. - To disable the breakpoint, press **F9** again. -- Press **F5** to let the run. +- Press **F5** to run the script. The execution should stop on the line you put the breakpoint on. +- Press **F5** to continue running the script. There are a few blogs that may be helpful to get you started using PowerShell extension for VS Code From da28473dd621f16042804a98c76d2c13478dafaf Mon Sep 17 00:00:00 2001 From: Jianyun Tao Date: Thu, 14 Jul 2016 18:06:50 -0700 Subject: [PATCH 33/42] Removed ls alias as an example because it does not exist on Linux. --- docs/learning-powerShell/learning-powershell.md | 6 +++--- .../learning-powerShell/powershell-beginners-guide.md | 11 ++++++----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/learning-powerShell/learning-powershell.md b/docs/learning-powerShell/learning-powershell.md index a22bf78c73..2d3d28341e 100644 --- a/docs/learning-powerShell/learning-powershell.md +++ b/docs/learning-powerShell/learning-powershell.md @@ -27,12 +27,12 @@ Getting Started with PowerShell ---- PowerShell command has a Verb-Noun structure with a set of parameters. It's easy to learn and use PowerShell. For example, "Get-Process" will display all the running processes on your system. Let's walk through with a few examples by clicking on the [PowerShell Beginner's Guide](powershell-beginners-guide.md). -Now you have learned the basics of PowerShell. Please continue reading: Editor, Debugger, and Testing Tool if you want to do some development work in PowerShell. +Now you have learned the basics of PowerShell. Please continue reading if you want to do some development work in PowerShell. PowerShell Editor ---- -In this section, you will create a PowerShell script using PowerShell editor. You can certainly use your favorite editor to write scripts. As an example, we use Visual Studio Code (VS Code) which works for Windows, Linux, or OS X. Click on the following link to start create your first PowerShell script, let's say helloworld.ps1. +In this section, you will create a PowerShell script using PowerShell editor. You can use your favorite editor to write scripts. As an example, we will use Visual Studio Code (VS Code) which works for Windows, Linux, or OS X. Click on the following link to start create your first PowerShell script, let's say helloworld.ps1. - [Using Visual Studio Code (VS Code)][use-vscode-editor] @@ -44,7 +44,7 @@ On Windows, you can also use [PowerShell Integrated Scripting Environment (ISE)] PowerShell Debugger ---- -Assuming you have written a PowerShell script which may contains a software bug, you would like to fix the issue via debugging. As an example, we use VS Code. Click on the link below to start debugging: +Assuming you have written a PowerShell script which may contain a software bug, you can fix the issue via debugging. As an example, we will use VS Code. Click on the link below to start debugging: - [Using Visual Studio Code (VS Code)][use-vscode-debugger] - [PowerShell Command-line Debugging][cli-debugging] diff --git a/docs/learning-powerShell/powershell-beginners-guide.md b/docs/learning-powerShell/powershell-beginners-guide.md index b4f5a0e7bc..7d2dd1013f 100644 --- a/docs/learning-powerShell/powershell-beginners-guide.md +++ b/docs/learning-powerShell/powershell-beginners-guide.md @@ -13,15 +13,16 @@ Getting Familiar with PowerShell Commands --- In this section, you will learn how to - create a file, delete a file and change file directory -- find syntax of PowerShell Cmdlets +- find syntax of PowerShell cmdlets +- get help if you needed - discover what version of PowerShell you are currently using -- exit PowerShell session +- exit a PowerShell session - and more As mentioned above, PowerShell commands is designed to have Verb-Noun structure, for instance Get-Process, Set-Location, Clear-Host, etc. Let’s exercise some of the basic PowerShell commands, also known as **cmdlets**. Please note that we will use the PowerShell prompt sign **PS />** as it appears on Linux in the following examples. -It is shown as **PS C:\>** on Windows. +It is shown as **PS C:\\>** on Windows. **1. Get-Process**: displays the processes running on your system. @@ -94,7 +95,7 @@ PS /> cls PS /> Set-Location /home PS /home> ``` -**5. ls or dir - Get-ChildItem**: list all items in the specified location +**5. dir - Get-ChildItem**: list all items in the specified location ```PowerShell Get all files under the current directory: @@ -171,7 +172,7 @@ PS /home/jen> Remove-Item ./test.ps1 **9. $PSVersionTable**: displays the version of PowerShell you are currently using -Type **$PSVersionTable** in your PowerShell session, you will see the following. "PSVersion" indicates the +Type **$PSVersionTable** in your PowerShell session, you will see something like below. "PSVersion" indicates the PowerShell version that you are using. ```PowerShell From ed39a00f2913183db009d4a5e420443decda090c Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Thu, 14 Jul 2016 19:38:43 -0700 Subject: [PATCH 34/42] Add GetLStat faccessat() does not support AT_SYMLINK_NOFOLLOW on OS X, so instead, we use the success of lstat() to determine if the file (not following symbolic links) exists. Also allowed for some refactoring. --- src/libpsl-native/src/CMakeLists.txt | 1 + src/libpsl-native/src/getlstat.cpp | 95 +++++++++++++++++++++++++++ src/libpsl-native/src/getlstat.h | 10 +++ src/libpsl-native/src/getstat.cpp | 3 +- src/libpsl-native/src/isdirectory.cpp | 3 +- src/libpsl-native/src/isfile.cpp | 5 +- src/libpsl-native/src/issymlink.cpp | 41 ++---------- 7 files changed, 115 insertions(+), 43 deletions(-) create mode 100644 src/libpsl-native/src/getlstat.cpp create mode 100644 src/libpsl-native/src/getlstat.h diff --git a/src/libpsl-native/src/CMakeLists.txt b/src/libpsl-native/src/CMakeLists.txt index 22e8f5dd85..5d4c432b40 100644 --- a/src/libpsl-native/src/CMakeLists.txt +++ b/src/libpsl-native/src/CMakeLists.txt @@ -1,5 +1,6 @@ add_library(psl-native SHARED getstat.cpp + getlstat.cpp getpwuid.cpp getuserfrompid.cpp getfileowner.cpp diff --git a/src/libpsl-native/src/getlstat.cpp b/src/libpsl-native/src/getlstat.cpp new file mode 100644 index 0000000000..d2fcb69697 --- /dev/null +++ b/src/libpsl-native/src/getlstat.cpp @@ -0,0 +1,95 @@ +//! @file getlstat.cpp +//! @author Andrew Schwartzmeyer +//! @brief returns the lstat of a file + +#include +#include +#include +#include +#include +#include +#include "getlstat.h" + +//! @brief GetLStat returns the lstat of a file. This simply delegates to the +//! lstat() system call and maps errno to the expected values for GetLastError. +//! +//! GetLstat +//! +//! @param[in] path +//! @parblock +//! A pointer to the buffer that contains the file name +//! +//! char* is marshaled as an LPStr, which on Linux is UTF-8. +//! @endparblock +//! +//! @param[in] lstat +//! @parblock +//! A pointer to the buffer in which to place the lstat information +//! @endparblock +//! +//! @exception errno Passes these errors via errno to GetLastError: +//! - ERROR_INVALID_PARAMETER: parameter is not valid +//! - ERROR_FILE_NOT_FOUND: file does not exist +//! - ERROR_ACCESS_DENIED: access is denied +//! - ERROR_INVALID_ADDRESS: attempt to access invalid address +//! - ERROR_STOPPED_ON_SYMLINK: too many symbolic links +//! - ERROR_GEN_FAILURE: I/O error occurred +//! - ERROR_INVALID_NAME: file provided is not a symbolic link +//! - ERROR_INVALID_FUNCTION: incorrect function +//! - ERROR_BAD_PATH_NAME: pathname is too long +//! - ERROR_OUTOFMEMORY insufficient kernel memory +//! +//! @retval 0 if successful +//! @retval -1 if failed +//! + +int32_t GetLStat(const char* path, struct stat* buf) +{ + errno = 0; + + if (!path) + { + errno = ERROR_INVALID_PARAMETER; + return -1; + } + + int32_t ret = lstat(path, buf); + + if (ret != 0) + { + switch(errno) + { + case EACCES: + errno = ERROR_ACCESS_DENIED; + break; + case EBADF: + errno = ERROR_FILE_NOT_FOUND; + break; + case EFAULT: + errno = ERROR_INVALID_ADDRESS; + break; + case ELOOP: + errno = ERROR_STOPPED_ON_SYMLINK; + break; + case ENAMETOOLONG: + errno = ERROR_GEN_FAILURE; + break; + case ENOENT: + errno = ERROR_FILE_NOT_FOUND; + break; + case ENOMEM: + errno = ERROR_NO_SUCH_USER; + break; + case ENOTDIR: + errno = ERROR_INVALID_NAME; + break; + case EOVERFLOW: + errno = ERROR_BUFFER_OVERFLOW; + break; + default: + errno = ERROR_INVALID_FUNCTION; + } + } + + return ret; +} diff --git a/src/libpsl-native/src/getlstat.h b/src/libpsl-native/src/getlstat.h new file mode 100644 index 0000000000..ba746dd35a --- /dev/null +++ b/src/libpsl-native/src/getlstat.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include "pal.h" + +PAL_BEGIN_EXTERNC + +int32_t GetLStat(const char* path, struct stat* buf); + +PAL_END_EXTERNC diff --git a/src/libpsl-native/src/getstat.cpp b/src/libpsl-native/src/getstat.cpp index 3e89d1a6ed..8fd6662219 100644 --- a/src/libpsl-native/src/getstat.cpp +++ b/src/libpsl-native/src/getstat.cpp @@ -89,8 +89,7 @@ int32_t GetStat(const char* path, struct stat* buf) default: errno = ERROR_INVALID_FUNCTION; } - return -1; } - return 0; + return ret; } diff --git a/src/libpsl-native/src/isdirectory.cpp b/src/libpsl-native/src/isdirectory.cpp index 01ca0d9bb1..f8b6bfe742 100644 --- a/src/libpsl-native/src/isdirectory.cpp +++ b/src/libpsl-native/src/isdirectory.cpp @@ -31,7 +31,6 @@ //! bool IsDirectory(const char* path) { - int32_t ret = 0; errno = 0; if (!path) @@ -41,7 +40,7 @@ bool IsDirectory(const char* path) } struct stat buf; - ret = GetStat(path, &buf); + int32_t ret = GetStat(path, &buf); if (ret != 0) { return false; diff --git a/src/libpsl-native/src/isfile.cpp b/src/libpsl-native/src/isfile.cpp index 89c415f472..1f6d0c59e5 100644 --- a/src/libpsl-native/src/isfile.cpp +++ b/src/libpsl-native/src/isfile.cpp @@ -9,6 +9,7 @@ #include #include #include +#include "getlstat.h" #include "isfile.h" //! @brief returns if the path is a file or directory @@ -37,6 +38,6 @@ bool IsFile(const char* path) return false; } - // Resolve relative paths to cwd of process, and do not follow symlinks - return faccessat(AT_FDCWD, path, F_OK, AT_SYMLINK_NOFOLLOW) != -1; + struct stat buf; + return GetLStat(path, &buf) == 0; } diff --git a/src/libpsl-native/src/issymlink.cpp b/src/libpsl-native/src/issymlink.cpp index dfd37d8bc5..87a8a2e8db 100644 --- a/src/libpsl-native/src/issymlink.cpp +++ b/src/libpsl-native/src/issymlink.cpp @@ -7,6 +7,7 @@ #include #include #include +#include "getlstat.h" #include "issymlink.h" //! @brief IsSymlink determines if path is a symbolic link @@ -48,46 +49,12 @@ bool IsSymLink(const char* path) return false; } - struct stat statBuf; - - int ret = lstat(path, &statBuf); - + struct stat buf; + int32_t ret = GetLStat(path, &buf); if (ret != 0) { - switch(errno) - { - case EACCES: - errno = ERROR_ACCESS_DENIED; - break; - case EBADF: - errno = ERROR_FILE_NOT_FOUND; - break; - case EFAULT: - errno = ERROR_INVALID_ADDRESS; - break; - case ELOOP: - errno = ERROR_STOPPED_ON_SYMLINK; - break; - case ENAMETOOLONG: - errno = ERROR_GEN_FAILURE; - break; - case ENOENT: - errno = ERROR_FILE_NOT_FOUND; - break; - case ENOMEM: - errno = ERROR_NO_SUCH_USER; - break; - case ENOTDIR: - errno = ERROR_INVALID_NAME; - break; - case EOVERFLOW: - errno = ERROR_BUFFER_OVERFLOW; - break; - default: - errno = ERROR_INVALID_FUNCTION; - } return false; } - return S_ISLNK(statBuf.st_mode); + return S_ISLNK(buf.st_mode); } From 368b6a5c6c949defafa51c6ec2c3038844fbd13c Mon Sep 17 00:00:00 2001 From: JumpingYang001 Date: Fri, 8 Jul 2016 01:44:51 -0700 Subject: [PATCH 35/42] Add Parser Pester Test Part 1 --- test/powershell/enginecore/Parser.Test.ps1 | 617 +++++++++++++++++++++ 1 file changed, 617 insertions(+) create mode 100644 test/powershell/enginecore/Parser.Test.ps1 diff --git a/test/powershell/enginecore/Parser.Test.ps1 b/test/powershell/enginecore/Parser.Test.ps1 new file mode 100644 index 0000000000..be503bc2fe --- /dev/null +++ b/test/powershell/enginecore/Parser.Test.ps1 @@ -0,0 +1,617 @@ +Describe "ParserTests (admin\monad\tests\monad\src\engine\core\ParserTests.cs)" { + BeforeAll { + $functionDefinitionFile = Join-Path -Path $TestDrive -ChildPath "functionDefinition.ps1" + $functionDefinition = @" + function testcmd-parserbvt + { + [CmdletBinding()] + param ( + [Parameter(Position = 0)] + [string] `$Property1 = "unset", + + [Parameter(Position = 1)] + [string] `$Property2 = "unset", + + [Parameter(Position = 2)] + [string] `$Property3 = "unset", + + [Parameter()] + [ValidateSet("default","array","object","nestedobject","struct","mshobject","nulltostring")] + [string]`$ReturnType = "default" + ) + + BEGIN {} + + PROCESS { + if ( ! `$ReturnType ) { + `$ReturnType = "default" + } + + switch ( `$ReturnType ) + { + "default" { + `$result = "`$Property1;`$Property2;`$Property3" + break + } + "array" { + `$result = 1,2,3 + break + } + "object" { + `$result = new-object psobject + break + } + "nestedobject" { + `$result = [pscustomobject]@{Name="John";Person=[pscustomobject]@{Name="John";Age=30}} + break + } + "struct" { + `$result = [pscustomobject]@{Name="John";Age=30} + break + } + "mshobject" { + `$result = new-object psobject + break + } + "nulltostring" { + `$result = $null + break + } + default { + throw ([invalidoperationexception]::new("ReturnType parameter wasn't of any Expected value!")) + break + } + } + return `$result + } + + END {} + } +"@ + $functionDefinition>$functionDefinitionFile + + $PowerShell = [powershell]::Create() + function ExecuteCommand { + param ([string]$command) + try { + $PowerShell.AddScript(". $functionDefinitionFile").Invoke() + $PowerShell.AddScript($command).Invoke() + } + finally { + $PowerShell.Commands.Clear() + } + } + } + BeforeEach { + $testfile = Join-Path -Path $TestDrive -ChildPath "testfile.ps1" + $winshellfile = Join-Path -Path $TestDrive -ChildPath "testfile.cmd" + $testfolder1 = Join-Path -Path $TestDrive -ChildPath "dir1" + $testfolder2 = Join-Path -Path $testfolder1 -ChildPath "dir2" + if(-not(Test-Path $testfolder1)) + { + New-Item $testfolder1 -Type Directory + } + if(-not(Test-Path $testfolder2)) + { + New-Item $testfolder2 -Type Directory + } + $testdirfile1 = Join-Path -Path $testfolder2 -ChildPath "testdirfile1.txt" + $testdirfile2 = Join-Path -Path $testfolder2 -ChildPath "testdirfile2.txt" + "">$testdirfile1 + "">$testdirfile2 + } + AfterEach { + $PowerShell.Commands.Clear() + if(Test-Path $testfile) + { + Remove-Item $testfile + } + if(Test-Path $winshellfile) + { + Remove-Item $winshellfile + } + if(Test-Path $testdirfile1) + { + Remove-Item $testdirfile1 + } + if(Test-Path $testdirfile2) + { + Remove-Item $testdirfile2 + } + if(Test-Path $testfolder2) + { + Remove-Item $testfolder2 + } + if(Test-Path $testfolder1) + { + Remove-Item $testfolder1 + } + } + + AfterAll { + if(Test-Path $functionDefinitionFile) + { + Remove-Item $functionDefinitionFile + } + } + + It "Throws a syntax error when parsing a string without a closing quote. (line 164)" { + try { + ExecuteCommand '"This is a test' + throw "Execution OK" + } + catch { + $_.FullyQualifiedErrorId | Should be "IncompleteParseException" + } + } + + It "Throws an error if an open parenthesis is not closed (line 176)" { + try { + ExecuteCommand "(" + throw "Execution OK" + } + catch { + $_.FullyQualifiedErrorId | should be "IncompleteParseException" + } + } + + It "Throws an exception if the the first statement starts with an empty pipe element (line 188)" { + try { + ExecuteCommand "| get-location" + throw "Execution OK" + } + catch { + $_.FullyQualifiedErrorId | should be "ParseException" + } + } + + It "Throws an CommandNotFoundException exception if using a label in front of an if statement is not allowed. (line 225)"{ + ExecuteCommand ":foo if ($x -eq 3) { 1 }" + $PowerShell.HadErrors | should be $true + $PowerShell.Streams.Error.FullyQualifiedErrorId | should be "CommandNotFoundException" + } + + It "Pipe an expression into a value expression. (line 237)" { + try { + ExecuteCommand "testcmd-parserbvt | 3" + throw "Execution OK" + } + catch { + $_.FullyQualifiedErrorId | should be "ParseException" + } + + try { + ExecuteCommand "testcmd-parserbvt | $(1 + 1)" + throw "Execution OK" + } + catch { + $_.FullyQualifiedErrorId | should be "ParseException" + } + + try { + ExecuteCommand "testcmd-parserbvt | 'abc'" + throw "Execution OK" + } + catch { + $_.FullyQualifiedErrorId | should be "ParseException" + } + } + + It "Throws when you pipe into a value expression (line 238)" { + foreach($command in "1;2;3|3",'1;2;3|$(1+1)',"1;2;3|'abc'") { + try { + ExecuteCommand $command + throw "Execution OK" + } + catch { + $_.FullyQualifiedErrorId | Should be "ParseException" + } + } + } + + It "Throws an incomplete parse exeception when a comma follows an expression (line 247)" { + try { + ExecuteCommand "(1+ 1)," + throw "Execution OK" + } + catch { + $_.FullyQualifiedErrorId | Should be "IncompleteParseException" + } + } + + + It "Test that invoke has a higher precedence for a script than for an executable. (line 279)" { + "1">$testfile + $result = ExecuteCommand ". $testfile" + $result | should be 1 + } + + It "This test will check that a path is correctly interpreted when using '..' and '.' (line 364)" { + $result = ExecuteCommand "set-location $TestDrive; get-childitem dir1\.\.\.\..\dir1\.\dir2\..\..\dir1\.\dir2" + $result.Count | should be 2 + $result[0].Name | should be "testdirfile1.txt" + $result[1].Name | should be "testdirfile2.txt" + } + + It "This test will check that the parser can handle a mix of forward slashes and back slashes in the path (line 417)" { + $result = ExecuteCommand "get-childitem $TestDrive/dir1/./.\.\../dir1/.\dir2\../..\dir1\.\dir2" + $result.Count | should be 2 + $result[0].Name | should be "testdirfile1.txt" + $result[1].Name | should be "testdirfile2.txt" + } + + It "This test checks that the asterisk globs as expected. (line 545)" { + $result = ExecuteCommand "get-childitem $TestDrive/dir1\dir2\*.txt" + $result.Count | should be 2 + $result[0].Name | should be "testdirfile1.txt" + $result[1].Name | should be "testdirfile2.txt" + } + + It "This test checks that we can use a range for globbing: [1-2] (line 557)" { + $result = ExecuteCommand "get-childitem $TestDrive/dir1\dir2\testdirfile[1-2].txt" + $result.Count | should be 2 + $result[0].Name | should be "testdirfile1.txt" + $result[1].Name | should be "testdirfile2.txt" + } + + It "This test will check that escaping the $ sigil inside single quotes simply returns the $ character. (line 583)" { + $result = ExecuteCommand "'`$'" + $result | should be "`$" + } + + It "Test that escaping a space just returns that space. (line 593)" { + $result = ExecuteCommand '"foo` bar"' + $result | should be "foo bar" + } + + It "Test that escaping any character with no special meaning just returns that char. (line 602)" { + $result = ExecuteCommand '"fo`obar"' + $result | should be "foobar" + } + + It "Test that we support all of the C# escape sequences. We use the ` instead of \. (line 613)" -Pending { + $sequences = @('"`\\"', '"`0"', '"`a"', '"`b"', '"`f"', '"`n"', '"`r"', '"`t"', '"`v"') + $expectedSeq = @('\\', "\0", '\a', '\b', '\f', '\n', '\r', '\t', '\v') + $i = 0 + for(;$i -lt $sequences.Count;$i++) + { + $result = ExecuteCommand $sequences[$i] + $result | should be $expectedSeq[$i] + } + } + + It "This test checks that array substitution occurs inside double quotes. (line 646)" { + $result = ExecuteCommand '$MyArray = "a","b";"Hello $MyArray"' + $result | should be "Hello a b" + } + + It "This tests declaring an array in nested variable tables. (line 761)" { + $result = ExecuteCommand "`$Variable:vtbl1:vtbl2:b=@(5,6);`$Variable:vtbl1:vtbl2:b" + $result.Count | should be 2 + $result[0] | should be 5 + $result[1] | should be 6 + } + + It "Test a simple multiple assignment. (line 773)" { + $result = ExecuteCommand '$one,$two = 1,2,3; "One = $one"; "Two = $two"' + $result.Count | should be 2 + $result[0] | should be "One = 1" + $result[1] | should be "Two = 2 3" + } + + It "Tests script, global and local scopes from a function inside a script. (line 824)" { + "`$var = 'script';function func { `$var; `$var = 'local'; `$local:var; `$script:var; `$global:var };func;`$var;">$testfile + ExecuteCommand "`$var = 'global'" + $result = ExecuteCommand "$testfile" + $result.Count | should be 5 + $result[0] | should be "script" + $result[1] | should be "local" + $result[2] | should be "script" + $result[3] | should be "global" + $result[4] | should be "script" + } + + It "Use break inside of a loop that is inside another loop. (line 945)" { + $commands = " while (1) { 1; while(1) { 2; break; 3; }; 4; break; 5; } ", + " for (;;) { 1; for(;;) { 2; break; 3; }; 4; break; 5; } ", + " foreach(`$a in 1,2,3) { 1; foreach( `$b in 1,2,3 ) { 2; break; 3; }; 4; break; 5; } " + $results = "1", "2", "4" + $i = 0 + for(;$i -lt $commands.Count;$i++) + { + $result = ExecuteCommand $commands[$i] + $result | should be $results + } + } + + It "Use break in two loops with same label. (line 967)" { + $commands = " :foo while (1) { 1; :foo while(1) { 2; break foo; 3; }; 4; break; 5; } ", + " :foo for (;;) { 1; :foo for(;;) { 2; break foo; 3; }; 4; break; 5; } ", + " :foo foreach(`$a in 1,2,3) { 1; :foo foreach( `$b in 1,2,3 ) { 2; break foo; 3; }; 4; break; 5; } " + $results = "1", "2", "4" + $i = 0 + for(;$i -lt $commands.Count;$i++) + { + $result = ExecuteCommand $commands[$i] + $result | should be $results + } + } + + It "Try continue inside of different loop statements. (line 1039)" { + $commands = " `$a = 0; while (`$a -lt 2) { `$a; `$a += 1; continue; 2; } ", + " for (`$a = 0;`$a -lt 2; `$a += 1) { 9; continue; 3; } ", + " foreach(`$a in 0,1) { `$a; continue; 2; } " + $result = ExecuteCommand $commands[0] + $result | should be "0", "1" + $result = ExecuteCommand $commands[1] + $result | should be "9", "9" + $result = ExecuteCommand $commands[2] + $result | should be "0", "1" + } + + It "Use a label to continue an inner loop. (line 1059)" { + $commands = " `$x = 0; while (`$x -lt 1) { `$x += 1; `$x; `$a = 0; :foo while(`$a -lt 2) { `$a += 1; `$a; continue foo; 3; }; 4; continue; 5; } ", + " for (`$x = 0;`$x -lt 1;`$x += 1) { 1; :foo for(`$a = 0; `$a -lt 2; `$a += 1) { `$a; continue foo; 3; }; 4; continue; 5; } ", + " foreach(`$a in 1) { 1; :foo foreach( `$b in 1,2 ) { `$b; continue foo; 3; }; 4; continue; 5; } " + $result = ExecuteCommand $commands[0] + $result | should be "1", "1", "2", "4" + $result = ExecuteCommand $commands[1] + $result | should be "1", "0", "1", "4" + $result = ExecuteCommand $commands[2] + $result | should be "1", "1", "2", "4" + } + + It "Use continue with a label on a nested loop. (line 1059)" { + $commands = " `$x = 0; :foo while (`$x -lt 2) { `$x; `$x += 1; :bar while(1) { 2; continue foo; 3; }; 4; continue; 5; } ", + " :foo for (`$x = 0;`$x -lt 2;`$x += 1) { 1; :bar for(;;) { 2; continue foo; 3; }; 4; continue; 5; } ", + " :foo foreach(`$a in 1,2) { 1; :bar foreach( `$b in 1,2,3 ) { 2; continue foo; 3; }; 4; continue; 5; } " + $result = ExecuteCommand $commands[0] + $result | should be "0", "2", "1", "2" + $result = ExecuteCommand $commands[1] + $result | should be "1", "2", "1", "2" + $result = ExecuteCommand $commands[2] + $result | should be "1", "2", "1", "2" + } + + It "This test will check that it is a syntax error to use if without a code block. (line 1141)" { + try { + ExecuteCommand 'if ("true")' + throw "Execution OK" + } + catch { + $_.FullyQualifiedErrorId | Should be "IncompleteParseException" + } + } + + It "This test will check that it is a syntax error if the if condition is not complete. (line 1150)" { + try { + ExecuteCommand 'if (' + throw "Execution OK" + } + catch { + $_.FullyQualifiedErrorId | Should be "IncompleteParseException" + } + } + + It "This test will check that it is a syntax error to have an if condition without parentheses. (line 1159)" { + try { + ExecuteCommand 'if "true" { 1} else {2}' + throw "Execution OK" + } + catch { + $_.FullyQualifiedErrorId | Should be "ParseException" + } + } + + It "This test will check that the parser throws a syntax error when the if condition is missing the closing parentheses. (line 1168)" { + try { + ExecuteCommand 'if ("true" { 1};' + throw "Execution OK" + } + catch { + $_.FullyQualifiedErrorId | Should be "ParseException" + } + } + + It "This test will check that it is a syntax error to have an else keyword without the corresponding code block. (line 1177)" { + try { + ExecuteCommand 'if ("true") {1} else' + throw "Execution OK" + } + catch { + $_.FullyQualifiedErrorId | Should be "IncompleteParseException" + } + } + + It "This test will check that the parser throws a syntax error when a foreach loop is not complete. (line 1238)" { + try { + ExecuteCommand '$count=0;$files = $(get-childitem / -filter *.txt );foreach ($i ;$count' + throw "Execution OK" + } + catch { + $_.FullyQualifiedErrorId | Should be "ParseException" + } + } + + It "This test will check that the parser throws a syntax error if the foreach loop is not complete. (line 1248)" { + try { + ExecuteCommand '$count=0;$files = $(get-childitem / -filter *.txt );foreach ($i in ;$count' + throw "Execution OK" + } + catch { + $_.FullyQualifiedErrorId | Should be "ParseException" + } + } + + It "This will test that the parser throws a syntax error if the foreach loop is missing a closing parentheses. (line 1258)" { + try { + ExecuteCommand '$count=0;$files = $(get-childitem / -filter *.txt );foreach ($i in $files ;$count' + throw "Execution OK" + } + catch { + $_.FullyQualifiedErrorId | Should be "ParseException" + } + } + + It "Test that if an exception is thrown from the try block it will be caught in the apprpropriate catch block and that the finally block will run regardless of whether an exception is thrown. (line 1317)" { + $result = ExecuteCommand 'try { try { throw (new-object System.ArgumentException) } catch [System.DivideByZeroException] { } finally { "Finally" } } catch { $_.Exception.GetType().FullName }' + $result | should be "Finally", "System.ArgumentException" + } + + It "Test that null can be passed to a method that expects a reference type. (line 1439)" { + $result = ExecuteCommand '$test = "String";$test.CompareTo($())' + $result | should be 1 + } + + It "Tests that command expansion operators can be used as a parameter to an object method. (line 1507)" { + $result = ExecuteCommand '$test = "String";$test.SubString($("hello" | foreach-object { $_.length - 2 } ))' + $result | should be "ing" + } + + It "Test that & can be used as a parameter as long as it is quoted. (line 1606)" { + $result = ExecuteCommand 'testcmd-parserbvt `&get-childitem' + $result | should be "&get-childitem;unset;unset" + $result = ExecuteCommand 'testcmd-parserbvt `&*' + $result | should be "&*;unset;unset" + } + + It "Run a command with parameters. (line 1621)" { + $result = ExecuteCommand 'testcmd-parserBVT -Property1 set' + $result | should be "set;unset;unset" + } + + It "Test that typing a number at the command line will return that number. (line 1630)" { + $result = ExecuteCommand '3' + $result | should be "3" + } + + It "This test will check that an msh script can be run without invoking. (line 1641)" { + "1">$testfile + $result = ExecuteCommand ". $testfile" + $result | should be 1 + } + + It "Test that an alias is resolved before a function. (line 1657)" { + $result = ExecuteCommand 'set-alias parserInvokeTest testcmd-parserBVT;function parserInvokeTest { 3 };parserInvokeTest' + $result | should be "unset;unset;unset" + } + + It "Test that functions are resolved before cmdlets. (line 1678)"{ + $result = ExecuteCommand 'function testcmd-parserBVT { 3 };testcmd-parserBVT' + $result | should be "3" + } + + It "Check that a command that uses shell execute can be run from the command line and that no exception is thrown. (line 1702)" -Skip:($IsLinux -Or $IsOSX){ + "@echo Hello, I'm a Cmd script!">$winshellfile + { ExecuteCommand "$winshellfile" } | Should Not Throw + } + + Context "Boolean Tests (starting at line 1723 to line 1772)" { + $testData = @( + @{ Script = '"False"'; Expected = $true } + @{ Script = 'if ("A") { $true } else { $false }'; Expected = $true } + @{ Script = 'if (" ") { $true } else { $false }'; Expected = $true } + @{ Script = 'if ("String with spaces") { $true } else { $false }'; Expected = $true } + @{ Script = 'if ("DoubleQuoted") { $true } else { $false }'; Expected = $true } + @{ Script = 'if ("StringWithNullVar$aEmbedded") { $true } else { $false }'; Expected = $true } + @{ Script = 'if (0) { $true } else { $false }'; Expected = $false } + @{ Script = '$a = $(0);if ($a) { $true } else { $false }'; Expected = $false } + @{ Script = '$obj = testcmd-parserBVT -ReturnType object;if ($obj) { $true } else { $false }'; Expected = $true } + ) + It "