Unify CreateFile pinvoke in SMA (#18751)

This commit is contained in:
Ilya
2022-12-15 11:05:07 +05:00
committed by GitHub
parent bea1c6ee0e
commit 218c6cea15
8 changed files with 211 additions and 253 deletions
@@ -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);
}
/// <summary>Is this a hardlink.</summary>
/// <param name="handle">The handle to a file.</param>
/// <returns>A boolean that represents whether the item is a hardlink.</returns>
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;
}
/// <summary>Determine if the item is a hardlink.</summary>
/// <param name="fs">A FileSystemInfo to check to determine if it is a hardlink.</param>
/// <returns>A boolean that represents whether the item is a hardlink.</returns>
@@ -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);
}
}
}
@@ -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;
@@ -24,7 +24,6 @@ internal static partial class Interop
// HINSTANCE == PVOID == nint
nint resultCode = 0;
const int MAX_PATH = 260;
Span<char> buffer = stackalloc char[MAX_PATH];
unsafe
{
@@ -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);
}
}
}
@@ -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
/// </summary>
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
@@ -7497,13 +7497,6 @@ namespace Microsoft.PowerShell.Commands
/// </summary>
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);
/// <summary>
/// Gets the target of the specified reparse point.
/// </summary>
@@ -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<REPARSE_DATA_BUFFER_SYMBOLICLINK>();
@@ -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<REPARSE_DATA_BUFFER_SYMBOLICLINK>(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<UInt64, UInt64> 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
@@ -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
/// <summary>
/// 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).
/// </summary>
/// <param name="path">File path.</param>
/// <returns>File path (with extended prefix if the path is long path).</returns>
[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 == '.';
}
/// <summary>
/// Returns true if the given character is a valid drive letter
/// </summary>