diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index 3733458ebd..54b8bcaa56 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -395,7 +395,7 @@ namespace System.Management.Automation internal static bool NonWindowsCreateSymbolicLink(string path, string target) { // Linux doesn't care if target is a directory or not - return Unix.NativeMethods.CreateSymLink(path, target); + return Unix.NativeMethods.CreateSymLink(path, target) == 0; } internal static bool NonWindowsCreateHardLink(string path, string strTargetPath) @@ -597,9 +597,8 @@ namespace System.Management.Automation internal static extern int SetDate(SetDateInfoInternal info); [DllImport(psLib, CharSet = CharSet.Ansi, SetLastError = true)] - [return: MarshalAs(UnmanagedType.I1)] - internal static extern bool CreateSymLink([MarshalAs(UnmanagedType.LPStr)]string filePath, - [MarshalAs(UnmanagedType.LPStr)]string target); + internal static extern int CreateSymLink([MarshalAs(UnmanagedType.LPStr)]string filePath, + [MarshalAs(UnmanagedType.LPStr)]string target); [DllImport(psLib, CharSet = CharSet.Ansi, SetLastError = true)] internal static extern int CreateHardLink([MarshalAs(UnmanagedType.LPStr)]string filePath, diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs index 015cd8bdac..1b7f76257c 100644 --- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs +++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs @@ -2208,14 +2208,11 @@ namespace Microsoft.PowerShell.Commands if (itemType == ItemType.SymbolicLink) { - if (Platform.IsWindows) - { - success = WinCreateSymbolicLink(path, strTargetPath, isDirectory); - } - else - { - success = Platform.NonWindowsCreateSymbolicLink(path, strTargetPath); - } +#if UNIX + success = Platform.NonWindowsCreateSymbolicLink(path, strTargetPath); +#else + success = WinCreateSymbolicLink(path, strTargetPath, isDirectory); +#endif } else if (itemType == ItemType.HardLink) { diff --git a/src/libpsl-native/src/createsymlink.cpp b/src/libpsl-native/src/createsymlink.cpp index 6cd712781c..22ea389a30 100644 --- a/src/libpsl-native/src/createsymlink.cpp +++ b/src/libpsl-native/src/createsymlink.cpp @@ -4,6 +4,7 @@ #include "createsymlink.h" +#include #include #include #include @@ -26,82 +27,15 @@ //! char* is marshaled as an LPStr, which on Linux is UTF-8. //! @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_FILE_NOT_FOUND: the system cannot find the file specified -//! - ERROR_INVALID_ADDRESS: attempt to access invalid address -//! - ERROR_STOPPED_ON_SYMLINK: the operation stopped after reaching a symbolic link -//! - ERROR_GEN_FAILURE: device attached to the system is not functioning -//! - ERROR_NO_SUCH_USER: there was no corresponding entry in the utmp-file -//! - ERROR_INVALID_NAME: filename, directory name, or volume label syntax is incorrect -//! - ERROR_BUFFER_OVERFLOW: file name is too long -//! - ERROR_INVALID_FUNCTION: incorrect function -//! - ERROR_BAD_PATH_NAME: pathname is too long, or contains invalid characters -//! -//! @retval boolean successful +//! @retval 0 if successful, -1 otherwise //! -bool CreateSymLink(const char *link, const char *target) +int32_t CreateSymLink(const char *link, const char *target) { + assert(link); + assert(target); + errno = 0; - // Check parameters - if (!link || !target) - { - errno = ERROR_INVALID_PARAMETER; - return false; - } - - int ret = symlink(target, link); - - if (ret == 0) - { - return true; - } - - switch(errno) - { - case EACCES: - errno = ERROR_ACCESS_DENIED; - break; - case EDQUOT: - errno = ERROR_DISK_FULL; - break; - case EEXIST: - errno = ERROR_FILE_EXISTS; - break; - case EFAULT: - errno = ERROR_INVALID_ADDRESS; - break; - case EIO: - errno = ERROR_GEN_FAILURE; - break; - case ELOOP: - errno = ERROR_STOPPED_ON_SYMLINK; - break; - case ENAMETOOLONG: - errno = ERROR_BAD_PATH_NAME; - break; - case ENOENT: - errno = ERROR_FILE_NOT_FOUND; - break; - case ENOMEM: - errno = ERROR_OUTOFMEMORY; - break; - case ENOTDIR: - errno = ERROR_INVALID_NAME; - break; - case ENOSPC: - errno = ERROR_DISK_FULL; - break; - case EPERM: - errno = ERROR_GEN_FAILURE; - break; - default: - errno = ERROR_INVALID_FUNCTION; - } - - return false; + return symlink(target, link); } diff --git a/src/libpsl-native/src/createsymlink.h b/src/libpsl-native/src/createsymlink.h index e2f4bd3d18..d40821d837 100644 --- a/src/libpsl-native/src/createsymlink.h +++ b/src/libpsl-native/src/createsymlink.h @@ -6,6 +6,6 @@ PAL_BEGIN_EXTERNC -bool CreateSymLink(const char *link, const char *target); +int32_t 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 1aab3379a0..9bd96f346f 100644 --- a/src/libpsl-native/test/test-createsymlink.cpp +++ b/src/libpsl-native/test/test-createsymlink.cpp @@ -39,12 +39,12 @@ protected: EXPECT_TRUE(dir != NULL); // Create symbolic link to file - bool ret1 = CreateSymLink(fileSymLink.c_str(), file); - EXPECT_TRUE(ret1); + int ret = CreateSymLink(fileSymLink.c_str(), file); + EXPECT_EQ(0, ret); // Create symbolic link to directory - bool ret2 = CreateSymLink(dirSymLink.c_str(), dir); - EXPECT_TRUE(ret2); + ret = CreateSymLink(dirSymLink.c_str(), dir); + EXPECT_EQ(0, ret); } ~CreateSymLinkTest() @@ -65,13 +65,6 @@ protected: } }; -TEST_F(CreateSymLinkTest, FilePathNameIsNull) -{ - bool retVal = CreateSymLink(NULL, NULL); - EXPECT_FALSE(retVal); - EXPECT_EQ(ERROR_INVALID_PARAMETER, errno); -} - TEST_F(CreateSymLinkTest, FilePathNameDoesNotExist) { std::string invalidFile = "/tmp/symlinktest_invalidFile"; @@ -82,8 +75,8 @@ TEST_F(CreateSymLinkTest, FilePathNameDoesNotExist) unlink(invalidLink.c_str()); // Linux allows creation of symbolic link that points to an invalid file - bool retVal = CreateSymLink(invalidLink.c_str(), invalidFile.c_str()); - EXPECT_TRUE(retVal); + int ret = CreateSymLink(invalidLink.c_str(), invalidFile.c_str()); + EXPECT_EQ(0, ret); std::string target = FollowSymLink(invalidLink.c_str()); EXPECT_EQ(target, invalidFile); @@ -93,8 +86,8 @@ TEST_F(CreateSymLinkTest, FilePathNameDoesNotExist) TEST_F(CreateSymLinkTest, SymLinkToFile) { - bool retVal = IsSymLink(fileSymLink.c_str()); - EXPECT_TRUE(retVal); + bool ret = IsSymLink(fileSymLink.c_str()); + EXPECT_TRUE(ret); std::string target = FollowSymLink(fileSymLink.c_str()); char buffer[PATH_MAX]; @@ -104,8 +97,8 @@ TEST_F(CreateSymLinkTest, SymLinkToFile) TEST_F(CreateSymLinkTest, SymLinkToDirectory) { - bool retVal = IsSymLink(dirSymLink.c_str()); - EXPECT_TRUE(retVal); + bool ret = IsSymLink(dirSymLink.c_str()); + EXPECT_TRUE(ret); std::string target = FollowSymLink(dirSymLink.c_str()); char buffer[PATH_MAX]; @@ -115,7 +108,7 @@ TEST_F(CreateSymLinkTest, SymLinkToDirectory) TEST_F(CreateSymLinkTest, SymLinkAgain) { - bool retVal = CreateSymLink(fileSymLink.c_str(), file); - EXPECT_FALSE(retVal); - EXPECT_EQ(ERROR_FILE_EXISTS, errno); + int ret = CreateSymLink(fileSymLink.c_str(), file); + EXPECT_EQ(-1, ret); + EXPECT_EQ(EEXIST, errno); } diff --git a/src/libpsl-native/test/test-issymlink.cpp b/src/libpsl-native/test/test-issymlink.cpp index 512a4053a9..463c124c10 100644 --- a/src/libpsl-native/test/test-issymlink.cpp +++ b/src/libpsl-native/test/test-issymlink.cpp @@ -65,7 +65,7 @@ TEST_F(isSymLinkTest, FilePathNameDoesNotExist) { std::string invalidFile = "/tmp/symlinktest_invalidFile"; EXPECT_FALSE(IsSymLink(invalidFile.c_str())); - EXPECT_EQ(ERROR_FILE_NOT_FOUND, errno); + EXPECT_EQ(ENOENT, errno); } TEST_F(isSymLinkTest, NormalFileIsNotSymLink) 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 9197ad9d1c..8a1d87c200 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/New-Item.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/New-Item.Tests.ps1 @@ -171,4 +171,14 @@ Describe "New-Item with links" -Tags @('CI', 'RequireAdminOnWindows') { # Remove the link explicitly to avoid broken symlink issue Remove-Item $FullyQualifiedLink -Force } + + It "Should error correctly when failing to create a symbolic link" -Skip:($IsWindows -or $IsElevated) { + # This test expects that /sbin exists but is not writable by the user + try { + New-Item -ItemType SymbolicLink -Path "/sbin/powershell-test" -Target $FullyQualifiedFolder -ErrorAction Stop + throw "Execution OK" + } catch { + $_.FullyQualifiedErrorId | Should Be "NewItemSymbolicLinkElevationRequired,Microsoft.PowerShell.Commands.NewItemCommand" + } + } }