diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs
index 39ba394842..1790cc72b5 100644
--- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs
+++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs
@@ -365,11 +365,6 @@ namespace System.Management.Automation
// - only to be used with the IsWindows feature query, and only if
// no other more specific feature query makes sense
- internal static bool NonWindowsIsHardLink(ref IntPtr handle)
- {
- return Unix.IsHardLink(ref handle);
- }
-
internal static bool NonWindowsIsHardLink(FileSystemInfo fileInfo)
{
return Unix.IsHardLink(fileInfo);
@@ -716,15 +711,6 @@ namespace System.Management.Automation
return (ErrorCategory)Unix.NativeMethods.GetErrorCategory(errno);
}
- /// Is this a hardlink.
- /// The handle to a file.
- /// A boolean that represents whether the item is a hardlink.
- public static bool IsHardLink(ref IntPtr handle)
- {
- // TODO:PSL implement using fstat to query inode refcount to see if it is a hard link
- return false;
- }
-
/// Determine if the item is a hardlink.
/// A FileSystemInfo to check to determine if it is a hardlink.
/// A boolean that represents whether the item is a hardlink.
diff --git a/src/System.Management.Automation/engine/Interop/Windows/CreateFile.cs b/src/System.Management.Automation/engine/Interop/Windows/CreateFile.cs
new file mode 100644
index 0000000000..fa1552c91c
--- /dev/null
+++ b/src/System.Management.Automation/engine/Interop/Windows/CreateFile.cs
@@ -0,0 +1,95 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+#nullable enable
+
+using System;
+using System.IO;
+using System.Management.Automation;
+using System.Runtime.InteropServices;
+
+using Microsoft.Win32.SafeHandles;
+
+internal static partial class Interop
+{
+ internal static unsafe partial class Windows
+ {
+ // dwDesiredAccess of CreateFile
+ [Flags]
+ internal enum FileDesiredAccess : uint
+ {
+ GenericZero = 0,
+ GenericRead = 0x80000000,
+ GenericWrite = 0x40000000,
+ GenericExecute = 0x20000000,
+ GenericAll = 0x10000000,
+ }
+
+ // dwFlagsAndAttributes
+ [Flags]
+ internal enum FileAttributes : uint
+ {
+ Readonly = 0x00000001,
+ Hidden = 0x00000002,
+ System = 0x00000004,
+ Archive = 0x00000020,
+ Encrypted = 0x00004000,
+ Write_Through = 0x80000000,
+ Overlapped = 0x40000000,
+ NoBuffering = 0x20000000,
+ RandomAccess = 0x10000000,
+ SequentialScan = 0x08000000,
+ DeleteOnClose = 0x04000000,
+ BackupSemantics = 0x02000000,
+ PosixSemantics = 0x01000000,
+ OpenReparsePoint = 0x00200000,
+ OpenNoRecall = 0x00100000,
+ SessionAware = 0x00800000,
+ Normal = 0x00000080
+ }
+
+ // WARNING: This method does not implicitly handle long paths. Use CreateFile.
+ [LibraryImport("api-ms-win-core-file-l1-1-0.dll", EntryPoint = "CreateFileW", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)]
+ private static unsafe partial SafeFileHandle CreateFilePrivate(
+ string lpFileName,
+ uint dwDesiredAccess,
+ FileShare dwShareMode,
+ nint lpSecurityAttributes,
+ FileMode dwCreationDisposition,
+ FileAttributes dwFlagsAndAttributes,
+ IntPtr hTemplateFile);
+
+ [LibraryImport("api-ms-win-core-file-l1-1-0.dll", EntryPoint = "CreateFileW", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)]
+ private static unsafe partial nint CreateFileWithPipeHandlePrivate(
+ string lpFileName,
+ uint dwDesiredAccess,
+ FileShare dwShareMode,
+ nint lpSecurityAttributes,
+ FileMode dwCreationDisposition,
+ FileAttributes dwFlagsAndAttributes,
+ IntPtr hTemplateFile);
+
+ internal static unsafe SafeFileHandle CreateFileWithSafeFileHandle(
+ string lpFileName,
+ FileAccess dwDesiredAccess,
+ FileShare dwShareMode,
+ FileMode dwCreationDisposition,
+ FileAttributes dwFlagsAndAttributes)
+ {
+ lpFileName = Path.TrimEndingDirectorySeparator(lpFileName);
+ lpFileName = PathUtils.EnsureExtendedPrefixIfNeeded(lpFileName);
+
+ return CreateFilePrivate(lpFileName, (uint)dwDesiredAccess, dwShareMode, nint.Zero, dwCreationDisposition, dwFlagsAndAttributes, nint.Zero);
+ }
+
+ internal static unsafe nint CreateFileWithPipeHandle(
+ string lpFileName,
+ FileAccess dwDesiredAccess,
+ FileShare dwShareMode,
+ FileMode dwCreationDisposition,
+ FileAttributes dwFlagsAndAttributes)
+ {
+ return CreateFileWithPipeHandlePrivate(lpFileName, (uint)dwDesiredAccess, dwShareMode, nint.Zero, dwCreationDisposition, dwFlagsAndAttributes, nint.Zero);
+ }
+ }
+}
diff --git a/src/System.Management.Automation/engine/Interop/Windows/Errors.cs b/src/System.Management.Automation/engine/Interop/Windows/Errors.cs
index d165493321..bef9e17219 100644
--- a/src/System.Management.Automation/engine/Interop/Windows/Errors.cs
+++ b/src/System.Management.Automation/engine/Interop/Windows/Errors.cs
@@ -9,6 +9,7 @@ internal static partial class Interop
{
// List of error constants https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes
internal const int ERROR_SUCCESS = 0;
+ internal const int ERROR_FILE_NOT_FOUND = 2;
internal const int ERROR_GEN_FAILURE = 31;
internal const int ERROR_NOT_SUPPORTED = 50;
internal const int ERROR_NO_NETWORK = 1222;
diff --git a/src/System.Management.Automation/engine/Interop/Windows/FindExecutable.cs b/src/System.Management.Automation/engine/Interop/Windows/FindExecutable.cs
index 7b52c317c8..2184e57a51 100644
--- a/src/System.Management.Automation/engine/Interop/Windows/FindExecutable.cs
+++ b/src/System.Management.Automation/engine/Interop/Windows/FindExecutable.cs
@@ -24,7 +24,6 @@ internal static partial class Interop
// HINSTANCE == PVOID == nint
nint resultCode = 0;
- const int MAX_PATH = 260;
Span buffer = stackalloc char[MAX_PATH];
unsafe
{
diff --git a/src/System.Management.Automation/engine/Interop/Windows/FindFirstFile.cs b/src/System.Management.Automation/engine/Interop/Windows/FindFirstFile.cs
index b3cd84b028..1873ada4bf 100644
--- a/src/System.Management.Automation/engine/Interop/Windows/FindFirstFile.cs
+++ b/src/System.Management.Automation/engine/Interop/Windows/FindFirstFile.cs
@@ -4,6 +4,8 @@
#nullable enable
using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Management.Automation;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
@@ -50,6 +52,14 @@ internal static partial class Interop
// We use 'FindFirstFileW' instead of 'FindFirstFileExW' because the latter doesn't work correctly with Unicode file names on FAT32.
// See https://github.com/PowerShell/PowerShell/issues/16804
[LibraryImport("api-ms-win-core-file-l1-1-0.dll", EntryPoint = "FindFirstFileW", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)]
- internal static partial SafeFindHandle FindFirstFile(string lpFileName, ref WIN32_FIND_DATA lpFindFileData);
+ private static partial SafeFindHandle FindFirstFileW(string lpFileName, ref WIN32_FIND_DATA lpFindFileData);
+
+ internal static SafeFindHandle FindFirstFile(string lpFileName, ref WIN32_FIND_DATA lpFindFileData)
+ {
+ lpFileName = Path.TrimEndingDirectorySeparator(lpFileName);
+ lpFileName = PathUtils.EnsureExtendedPrefixIfNeeded(lpFileName);
+
+ return FindFirstFileW(lpFileName, ref lpFindFileData);
+ }
}
}
diff --git a/src/System.Management.Automation/engine/remoting/common/RemoteSessionNamedPipe.cs b/src/System.Management.Automation/engine/remoting/common/RemoteSessionNamedPipe.cs
index 13ded67a04..e80c51fb81 100644
--- a/src/System.Management.Automation/engine/remoting/common/RemoteSessionNamedPipe.cs
+++ b/src/System.Management.Automation/engine/remoting/common/RemoteSessionNamedPipe.cs
@@ -190,26 +190,6 @@ namespace System.Management.Automation.Remoting
internal const uint ERROR_IO_INCOMPLETE = 996;
internal const uint ERROR_IO_PENDING = 997;
- // File function constants
- internal const uint GENERIC_READ = 0x80000000;
- internal const uint GENERIC_WRITE = 0x40000000;
- internal const uint GENERIC_EXECUTE = 0x20000000;
- internal const uint GENERIC_ALL = 0x10000000;
-
- internal const uint CREATE_NEW = 1;
- internal const uint CREATE_ALWAYS = 2;
- internal const uint OPEN_EXISTING = 3;
- internal const uint OPEN_ALWAYS = 4;
- internal const uint TRUNCATE_EXISTING = 5;
-
- internal const uint SECURITY_IMPERSONATIONLEVEL_ANONYMOUS = 0;
- internal const uint SECURITY_IMPERSONATIONLEVEL_IDENTIFICATION = 1;
- internal const uint SECURITY_IMPERSONATIONLEVEL_IMPERSONATION = 2;
- internal const uint SECURITY_IMPERSONATIONLEVEL_DELEGATION = 3;
-
- // Infinite timeout
- internal const uint INFINITE = 0xFFFFFFFF;
-
#endregion
#region Data structures
@@ -265,15 +245,6 @@ namespace System.Management.Automation.Remoting
return securityAttributes;
}
- [DllImport(PinvokeDllNames.CreateFileDllName, SetLastError = true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
- internal static extern SafePipeHandle CreateFile(
- string lpFileName,
- uint dwDesiredAccess,
- uint dwShareMode,
- IntPtr SecurityAttributes,
- uint dwCreationDisposition,
- uint dwFlagsAndAttributes,
- IntPtr hTemplateFile);
#endregion
}
@@ -1279,30 +1250,34 @@ namespace System.Management.Automation.Remoting
///
protected override NamedPipeClientStream DoConnect(int timeout)
{
+#if UNIX
+ // TODO: `CreateFileWithSafePipeHandle` pinvoke below clearly says
+ // that the code is only for Windows and we could exclude
+ // a lot of code from compilation on Unix.
+ throw new NotSupportedException(nameof(DoConnect));
+#else
//
// WaitNamedPipe API is not supported by Windows Server container now, so we need to repeatedly
// attempt connection to pipe server until timeout expires.
//
int startTime = Environment.TickCount;
int elapsedTime = 0;
- SafePipeHandle pipeHandle = null;
+ nint handle;
do
{
// Get handle to pipe.
- pipeHandle = NamedPipeNative.CreateFile(
+ handle = Interop.Windows.CreateFileWithPipeHandle(
lpFileName: PipeName,
- dwDesiredAccess: NamedPipeNative.GENERIC_READ | NamedPipeNative.GENERIC_WRITE,
- dwShareMode: 0,
- SecurityAttributes: IntPtr.Zero,
- dwCreationDisposition: NamedPipeNative.OPEN_EXISTING,
- dwFlagsAndAttributes: NamedPipeNative.FILE_FLAG_OVERLAPPED,
- hTemplateFile: IntPtr.Zero);
+ FileAccess.ReadWrite,
+ FileShare.None,
+ FileMode.Open,
+ Interop.Windows.FileAttributes.Overlapped);
- int lastError = Marshal.GetLastWin32Error();
- if (pipeHandle.IsInvalid)
+ if (handle == nint.Zero || handle == (nint)(-1))
{
- if (lastError == NamedPipeNative.ERROR_FILE_NOT_FOUND)
+ int lastError = Marshal.GetLastPInvokeError();
+ if (lastError == Interop.Windows.ERROR_FILE_NOT_FOUND)
{
elapsedTime = unchecked(Environment.TickCount - startTime);
Thread.Sleep(100);
@@ -1320,19 +1295,22 @@ namespace System.Management.Automation.Remoting
}
} while (elapsedTime < timeout);
+ SafePipeHandle pipeHandle = null;
try
{
+ pipeHandle = new SafePipeHandle(handle, ownsHandle: true);
return new NamedPipeClientStream(
PipeDirection.InOut,
- true,
- true,
+ isAsync: true,
+ isConnected: true,
pipeHandle);
}
catch (Exception)
{
- pipeHandle.Dispose();
+ pipeHandle?.Dispose();
throw;
}
+#endif
}
#endregion
diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs
index 51d84f8f6a..7edf50b514 100644
--- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs
+++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs
@@ -7497,13 +7497,6 @@ namespace Microsoft.PowerShell.Commands
///
public static partial class InternalSymbolicLinkLinkCodeMethods
{
- // This size comes from measuring the size of the header of REPARSE_GUID_DATA_BUFFER
- private const int REPARSE_GUID_DATA_BUFFER_HEADER_SIZE = 24;
-
- // Maximum reparse buffer info size. The max user defined reparse
- // data is 16KB, plus there's a header.
- private const int MAX_REPARSE_SIZE = (16 * 1024) + REPARSE_GUID_DATA_BUFFER_HEADER_SIZE;
-
private const int FSCTL_GET_REPARSE_POINT = 0x000900A8;
private const int FSCTL_SET_REPARSE_POINT = 0x000900A4;
@@ -7518,62 +7511,6 @@ namespace Microsoft.PowerShell.Commands
private const string NonInterpretedPathPrefix = @"\??\";
- private const int MAX_PATH = 260;
-
- [Flags]
- // dwDesiredAccess of CreateFile
- internal enum FileDesiredAccess : uint
- {
- GenericZero = 0,
- GenericRead = 0x80000000,
- GenericWrite = 0x40000000,
- GenericExecute = 0x20000000,
- GenericAll = 0x10000000,
- }
-
- [Flags]
- // dwShareMode of CreateFile
- internal enum FileShareMode : uint
- {
- None = 0x00000000,
- Read = 0x00000001,
- Write = 0x00000002,
- Delete = 0x00000004,
- }
-
- // dwCreationDisposition of CreateFile
- internal enum FileCreationDisposition : uint
- {
- New = 1,
- CreateAlways = 2,
- OpenExisting = 3,
- OpenAlways = 4,
- TruncateExisting = 5,
- }
-
- [Flags]
- // dwFlagsAndAttributes
- internal enum FileAttributes : uint
- {
- Readonly = 0x00000001,
- Hidden = 0x00000002,
- System = 0x00000004,
- Archive = 0x00000020,
- Encrypted = 0x00004000,
- Write_Through = 0x80000000,
- Overlapped = 0x40000000,
- NoBuffering = 0x20000000,
- RandomAccess = 0x10000000,
- SequentialScan = 0x08000000,
- DeleteOnClose = 0x04000000,
- BackupSemantics = 0x02000000,
- PosixSemantics = 0x01000000,
- OpenReparsePoint = 0x00200000,
- OpenNoRecall = 0x00100000,
- SessionAware = 0x00800000,
- Normal = 0x00000080
- }
-
[StructLayout(LayoutKind.Sequential)]
private struct REPARSE_DATA_BUFFER_SYMBOLICLINK
{
@@ -7626,29 +7563,6 @@ namespace Microsoft.PowerShell.Commands
public uint dwHighDateTime;
}
- [StructLayout(LayoutKind.Sequential)]
- private struct GUID
- {
- public uint Data1;
- public ushort Data2;
- public ushort Data3;
-
- [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
- public char[] Data4;
- }
-
- [StructLayout(LayoutKind.Sequential)]
- private struct REPARSE_GUID_DATA_BUFFER
- {
- public uint ReparseTag;
- public ushort ReparseDataLength;
- public ushort Reserved;
- public GUID ReparseGuid;
-
- [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_REPARSE_SIZE)]
- public char[] DataBuffer;
- }
-
[LibraryImport(PinvokeDllNames.DeviceIoControlDllName, StringMarshalling = StringMarshalling.Utf16, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static partial bool DeviceIoControl(IntPtr hDevice, uint dwIoControlCode,
@@ -7662,16 +7576,6 @@ namespace Microsoft.PowerShell.Commands
IntPtr hFile,
out BY_HANDLE_FILE_INFORMATION lpFileInformation);
- [LibraryImport(PinvokeDllNames.CreateFileDllName, EntryPoint = "CreateFileW", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)]
- internal static partial IntPtr CreateFile(
- string lpFileName,
- FileDesiredAccess dwDesiredAccess,
- FileShareMode dwShareMode,
- IntPtr lpSecurityAttributes,
- FileCreationDisposition dwCreationDisposition,
- FileAttributes dwFlagsAndAttributes,
- IntPtr hTemplateFile);
-
///
/// Gets the target of the specified reparse point.
///
@@ -7729,16 +7633,14 @@ namespace Microsoft.PowerShell.Commands
private static string InternalGetLinkType(FileSystemInfo fileInfo)
{
- if (Platform.IsWindows)
- {
- return WinInternalGetLinkType(fileInfo.FullName);
- }
- else
- {
- return Platform.NonWindowsInternalGetLinkType(fileInfo);
- }
+#if UNIX
+ return Platform.NonWindowsInternalGetLinkType(fileInfo);
+#else
+ return WinInternalGetLinkType(fileInfo.FullName);
+#endif
}
+#if !UNIX
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods")]
private static string WinInternalGetLinkType(string filePath)
{
@@ -7746,7 +7648,7 @@ namespace Microsoft.PowerShell.Commands
// If this parameter is zero, the application can query certain metadata
// such as file, directory, or device attributes without accessing
// that file or device, even if GENERIC_READ access would have been denied.
- using (SafeFileHandle handle = WinOpenReparsePoint(filePath, FileDesiredAccess.GenericZero))
+ using (SafeFileHandle handle = WinOpenReparsePoint(filePath, (FileAccess)0))
{
int outBufferSize = Marshal.SizeOf();
@@ -7778,7 +7680,7 @@ namespace Microsoft.PowerShell.Commands
if (!result)
{
// It's not a reparse point or the file system doesn't support reparse points.
- return IsHardLink(ref dangerousHandle) ? "HardLink" : null;
+ return WinIsHardLink(ref dangerousHandle) ? "HardLink" : null;
}
REPARSE_DATA_BUFFER_SYMBOLICLINK reparseDataBuffer = Marshal.PtrToStructure(outBuffer);
@@ -7811,13 +7713,28 @@ namespace Microsoft.PowerShell.Commands
}
}
}
+#endif
internal static bool IsHardLink(FileSystemInfo fileInfo)
{
#if UNIX
return Platform.NonWindowsIsHardLink(fileInfo);
#else
- return WinIsHardLink(fileInfo);
+ bool isHardLink = false;
+
+ // only check for hard link if the item is not directory
+ if ((fileInfo.Attributes & System.IO.FileAttributes.Directory) != System.IO.FileAttributes.Directory)
+ {
+ SafeFileHandle handle = Interop.Windows.CreateFileWithSafeFileHandle(fileInfo.FullName, FileAccess.Read, FileShare.Read, FileMode.Open, Interop.Windows.FileAttributes.Normal);
+
+ using (handle)
+ {
+ var dangerousHandle = handle.DangerousGetHandle();
+ isHardLink = InternalSymbolicLinkLinkCodeMethods.WinIsHardLink(ref dangerousHandle);
+ }
+ }
+
+ return isHardLink;
#endif
}
@@ -7838,13 +7755,7 @@ namespace Microsoft.PowerShell.Commands
}
Interop.Windows.WIN32_FIND_DATA data = default;
- string fullPath = Path.TrimEndingDirectorySeparator(fileInfo.FullName);
- if (fullPath.Length >= MAX_PATH)
- {
- fullPath = PathUtils.EnsureExtendedPrefix(fullPath);
- }
-
- using (Interop.Windows.SafeFindHandle handle = Interop.Windows.FindFirstFile(fullPath, ref data))
+ using (Interop.Windows.SafeFindHandle handle = Interop.Windows.FindFirstFile(fileInfo.FullName, ref data))
{
if (handle.IsInvalid)
{
@@ -7877,43 +7788,6 @@ namespace Microsoft.PowerShell.Commands
#endif
}
- internal static bool WinIsHardLink(FileSystemInfo fileInfo)
- {
- bool isHardLink = false;
-
- // only check for hard link if the item is not directory
- if ((fileInfo.Attributes & System.IO.FileAttributes.Directory) != System.IO.FileAttributes.Directory)
- {
- IntPtr nativeHandle = InternalSymbolicLinkLinkCodeMethods.CreateFile(
- fileInfo.FullName,
- InternalSymbolicLinkLinkCodeMethods.FileDesiredAccess.GenericRead,
- InternalSymbolicLinkLinkCodeMethods.FileShareMode.Read,
- IntPtr.Zero,
- InternalSymbolicLinkLinkCodeMethods.FileCreationDisposition.OpenExisting,
- InternalSymbolicLinkLinkCodeMethods.FileAttributes.Normal,
- IntPtr.Zero);
-
- using (SafeFileHandle handle = new SafeFileHandle(nativeHandle, true))
- {
- bool success = false;
-
- try
- {
- handle.DangerousAddRef(ref success);
- IntPtr dangerousHandle = handle.DangerousGetHandle();
- isHardLink = InternalSymbolicLinkLinkCodeMethods.IsHardLink(ref dangerousHandle);
- }
- finally
- {
- if (success)
- handle.DangerousRelease();
- }
- }
- }
-
- return isHardLink;
- }
-
internal static bool IsSameFileSystemItem(string pathOne, string pathTwo)
{
#if UNIX
@@ -7926,13 +7800,10 @@ namespace Microsoft.PowerShell.Commands
#if !UNIX
private static bool WinIsSameFileSystemItem(string pathOne, string pathTwo)
{
- const FileAccess access = FileAccess.Read;
- const FileShare share = FileShare.Read;
- const FileMode creation = FileMode.Open;
- const FileAttributes attributes = FileAttributes.BackupSemantics | FileAttributes.PosixSemantics;
+ const Interop.Windows.FileAttributes Attributes = Interop.Windows.FileAttributes.BackupSemantics | Interop.Windows.FileAttributes.PosixSemantics;
- using (var sfOne = AlternateDataStreamUtilities.NativeMethods.CreateFile(pathOne, access, share, IntPtr.Zero, creation, (int)attributes, IntPtr.Zero))
- using (var sfTwo = AlternateDataStreamUtilities.NativeMethods.CreateFile(pathTwo, access, share, IntPtr.Zero, creation, (int)attributes, IntPtr.Zero))
+ using (var sfOne = Interop.Windows.CreateFileWithSafeFileHandle(pathOne, FileAccess.Read, FileShare.Read, FileMode.Open, Attributes))
+ using (var sfTwo = Interop.Windows.CreateFileWithSafeFileHandle(pathTwo, FileAccess.Read, FileShare.Read, FileMode.Open, Attributes))
{
if (!sfOne.IsInvalid && !sfTwo.IsInvalid)
{
@@ -7965,12 +7836,9 @@ namespace Microsoft.PowerShell.Commands
#if !UNIX
private static bool WinGetInodeData(string path, out System.ValueTuple inodeData)
{
- const FileAccess access = FileAccess.Read;
- const FileShare share = FileShare.Read;
- const FileMode creation = FileMode.Open;
- const FileAttributes attributes = FileAttributes.BackupSemantics | FileAttributes.PosixSemantics;
+ const Interop.Windows.FileAttributes Attributes = Interop.Windows.FileAttributes.BackupSemantics | Interop.Windows.FileAttributes.PosixSemantics;
- using (var sf = AlternateDataStreamUtilities.NativeMethods.CreateFile(path, access, share, IntPtr.Zero, creation, (int)attributes, IntPtr.Zero))
+ using (var sf = Interop.Windows.CreateFileWithSafeFileHandle(path, FileAccess.Read, FileShare.Read, FileMode.Open, Attributes))
{
if (!sf.IsInvalid)
{
@@ -7994,15 +7862,6 @@ namespace Microsoft.PowerShell.Commands
}
#endif
- internal static bool IsHardLink(ref IntPtr handle)
- {
-#if UNIX
- return Platform.NonWindowsIsHardLink(ref handle);
-#else
- return WinIsHardLink(ref handle);
-#endif
- }
-
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods")]
internal static bool WinIsHardLink(ref IntPtr handle)
{
@@ -8013,18 +7872,9 @@ namespace Microsoft.PowerShell.Commands
internal static bool CreateJunction(string path, string target)
{
- // this is a purely Windows specific feature, no feature flag used for that reason.
- if (Platform.IsWindows)
- {
- return WinCreateJunction(path, target);
- }
-
+#if UNIX
return false;
- }
-
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods")]
- private static bool WinCreateJunction(string path, string target)
- {
+#else
if (string.IsNullOrEmpty(path))
{
throw new ArgumentNullException(nameof(path));
@@ -8035,7 +7885,7 @@ namespace Microsoft.PowerShell.Commands
throw new ArgumentNullException(nameof(target));
}
- using (SafeHandle handle = WinOpenReparsePoint(path, FileDesiredAccess.GenericWrite))
+ using (SafeHandle handle = WinOpenReparsePoint(path, FileAccess.Write))
{
byte[] mountPointBytes = Encoding.Unicode.GetBytes(NonInterpretedPathPrefix + Path.GetFullPath(target));
@@ -8082,25 +7932,27 @@ namespace Microsoft.PowerShell.Commands
}
}
}
+#endif
}
- private static SafeFileHandle WinOpenReparsePoint(string reparsePoint, FileDesiredAccess accessMode)
+#if !UNIX
+ private static SafeFileHandle WinOpenReparsePoint(string reparsePoint, FileAccess accessMode)
{
- IntPtr nativeHandle = CreateFile(reparsePoint, accessMode,
- FileShareMode.Read | FileShareMode.Write | FileShareMode.Delete,
- IntPtr.Zero, FileCreationDisposition.OpenExisting,
- FileAttributes.BackupSemantics | FileAttributes.OpenReparsePoint,
- IntPtr.Zero);
+ const Interop.Windows.FileAttributes Attributes = Interop.Windows.FileAttributes.BackupSemantics | Interop.Windows.FileAttributes.OpenReparsePoint;
- int lastError = Marshal.GetLastWin32Error();
+ SafeFileHandle reparsePointHandle = Interop.Windows.CreateFileWithSafeFileHandle(reparsePoint, accessMode, FileShare.ReadWrite | FileShare.Delete, FileMode.Open, Attributes);
- if (lastError != 0)
+ if (reparsePointHandle.IsInvalid)
+ {
+ // Save last error since Dispose() will do another pinvoke.
+ int lastError = Marshal.GetLastPInvokeError();
+ reparsePointHandle.Dispose();
throw new Win32Exception(lastError);
-
- SafeFileHandle reparsePointHandle = new SafeFileHandle(nativeHandle, true);
+ }
return reparsePointHandle;
}
+#endif
}
#endregion
diff --git a/src/System.Management.Automation/utils/PathUtils.cs b/src/System.Management.Automation/utils/PathUtils.cs
index 48d0acb5f4..558ca25f2f 100644
--- a/src/System.Management.Automation/utils/PathUtils.cs
+++ b/src/System.Management.Automation/utils/PathUtils.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT License.
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Management.Automation.Internal;
@@ -473,11 +474,35 @@ namespace System.Management.Automation
}
#region Helpers for long paths from .Net Runtime
-
+
// Code here is copied from .NET's internal path helper implementation:
// https://github.com/dotnet/runtime/blob/dcce0f56e10f5ac9539354b049341a2d7c0cdebf/src/libraries/System.Private.CoreLib/src/System/IO/PathInternal.Windows.cs
// It has been left as a verbatim copy.
+#nullable enable
+
+ ///
+ /// Adds the extended path prefix (\\?\) if not already a device path, IF the path is not relative,
+ /// AND the path is more than 259 characters. (> MAX_PATH + null). This will also insert the extended
+ /// prefix if the path ends with a period or a space. Trailing periods and spaces are normally eaten
+ /// away from paths during normalization, but if we see such a path at this point it should be
+ /// normalized and has retained the final characters. (Typically from one of the *Info classes).
+ ///
+ /// File path.
+ /// File path (with extended prefix if the path is long path).
+ [return: NotNullIfNotNull(nameof(path))]
+ internal static string? EnsureExtendedPrefixIfNeeded(string? path)
+ {
+ if (path != null && (path.Length >= MaxShortPath || EndsWithPeriodOrSpace(path)))
+ {
+ return EnsureExtendedPrefix(path);
+ }
+ else
+ {
+ return path;
+ }
+ }
+
internal static string EnsureExtendedPrefix(string path)
{
if (IsPartiallyQualified(path) || IsDevice(path))
@@ -495,10 +520,22 @@ namespace System.Management.Automation
private const string UncDevicePrefixToInsert = @"?\UNC\";
private const string UncExtendedPathPrefix = @"\\?\UNC\";
private const string DevicePathPrefix = @"\\.\";
+ private const int MaxShortPath = 260;
// \\?\, \\.\, \??\
private const int DevicePrefixLength = 4;
+ private static bool EndsWithPeriodOrSpace(string? path)
+ {
+ if (string.IsNullOrEmpty(path))
+ {
+ return false;
+ }
+
+ char c = path[path.Length - 1];
+ return c == ' ' || c == '.';
+ }
+
///
/// Returns true if the given character is a valid drive letter
///