mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
Unify CreateFile pinvoke in SMA (#18751)
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user