Use .Net Core File.Delete() method to remove symbolic links and alternate streams (#7017)

.Net Core support processing symbolic links and alternate streams, and thus we can remove our P/Invoke code.
This commit is contained in:
Ilya
2018-07-01 00:08:28 -07:00
committed by Dongbo Wang
parent d04fef469b
commit 0a3555064a
5 changed files with 15 additions and 137 deletions
@@ -10,6 +10,7 @@ using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Management.Automation;
using System.Management.Automation.Internal;
@@ -119,18 +120,9 @@ namespace Microsoft.PowerShell.Commands
{
AlternateDataStreamUtilities.DeleteFileStream(path, "Zone.Identifier");
}
catch (Win32Exception accessException)
catch (Exception e)
{
// NativeErrorCode=2 - File not found.
// If the block stream not found the 'path' was not blocked and we successfully return.
if (accessException.NativeErrorCode != 2)
{
WriteError(new ErrorRecord(accessException, "RemoveItemUnauthorizedAccessError", ErrorCategory.PermissionDenied, path));
}
else
{
WriteVerbose(StringUtil.Format(UtilityCommonStrings.NoZoneIdentifierFileStream, path));
}
WriteError(new ErrorRecord(e, "RemoveItemUnableToAccessFile", ErrorCategory.ResourceUnavailable, path));
}
}
}
@@ -174,9 +174,6 @@
<data name="TypeNotSupported" xml:space="preserve">
<value>'{0}' is not supported in this system.</value>
</data>
<data name="NoZoneIdentifierFileStream" xml:space="preserve">
<value>The file is not blocked: {0}</value>
</data>
<data name="ConvertToJsonProcessValueVerboseMessage" xml:space="preserve">
<value>Processing object of type [{0}] at depth {1}</value>
</data>
@@ -2874,41 +2874,20 @@ namespace Microsoft.PowerShell.Commands
continueRemoval = ShouldProcess(directory.FullName, action);
}
if ((directory.Attributes & FileAttributes.ReparsePoint) != 0)
if (directory.Attributes.HasFlag(FileAttributes.ReparsePoint))
{
bool success = InternalSymbolicLinkLinkCodeMethods.DeleteJunction(directory.FullName);
if (!success)
{
string error = StringUtil.Format(FileSystemProviderStrings.CannotRemoveItem, directory.FullName);
Exception e = new IOException(error);
WriteError(new ErrorRecord(e, "DeleteJunctionFailed", ErrorCategory.WriteError, directory));
return;
}
try
{
if (!Utils.ItemExists(directory.FullName, out bool _))
{
// Directory does not exist
return;
}
directory.Delete();
}
catch (Exception accessException)
catch (Exception e)
{
ErrorRecord errorRecord = new ErrorRecord(accessException, "RemoveFileSystemItemUnAuthorizedAccess", ErrorCategory.PermissionDenied, directory);
ErrorDetails errorDetails =
new ErrorDetails(this, "FileSystemProviderStrings",
"CannotRemoveItem",
directory.FullName,
accessException.Message);
errorRecord.ErrorDetails = errorDetails;
WriteError(errorRecord);
return;
string error = StringUtil.Format(FileSystemProviderStrings.CannotRemoveItem, directory.FullName);
Exception exception = new IOException(error, e);
WriteError(new ErrorRecord(exception, "DeleteSymbolicLinkFailed", ErrorCategory.WriteError, directory));
}
return;
}
if (continueRemoval)
@@ -8336,91 +8315,6 @@ namespace Microsoft.PowerShell.Commands
}
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods")]
internal static bool DeleteJunction(string junctionPath)
{
bool result = false;
if (!String.IsNullOrEmpty(junctionPath))
{
if (!Platform.IsWindows)
{
// For non-Windows platform, treat it as a file. Just delete it.
try
{
File.Delete(junctionPath);
return true;
}
catch
{
return false;
}
}
using (SafeHandle handle = OpenReparsePoint(junctionPath, FileDesiredAccess.GenericWrite))
{
bool success = false;
int inOutBufferSize = Marshal.SizeOf<REPARSE_GUID_DATA_BUFFER>();
IntPtr outBuffer = Marshal.AllocHGlobal(inOutBufferSize);
IntPtr inBuffer = Marshal.AllocHGlobal(inOutBufferSize);
try
{
handle.DangerousAddRef(ref success);
IntPtr dangerousHandle = handle.DangerousGetHandle();
int bytesReturned;
// Do a FSCTL_GET_REPARSE_POINT first because the ReparseTag could be
// IO_REPARSE_TAG_MOUNT_POINT or IO_REPARSE_TAG_SYMLINK.
// Using the wrong one results in mismatched-tag error.
REPARSE_GUID_DATA_BUFFER junctionData = new REPARSE_GUID_DATA_BUFFER();
Marshal.StructureToPtr<REPARSE_GUID_DATA_BUFFER>(junctionData, outBuffer, false);
result = DeviceIoControl(dangerousHandle, FSCTL_GET_REPARSE_POINT, IntPtr.Zero, 0,
outBuffer, inOutBufferSize, out bytesReturned, IntPtr.Zero);
if (!result)
{
int lastError = Marshal.GetLastWin32Error();
throw new Win32Exception(lastError);
}
junctionData = Marshal.PtrToStructure<REPARSE_GUID_DATA_BUFFER>(outBuffer);
junctionData.ReparseDataLength = 0;
junctionData.DataBuffer = new char[MAX_REPARSE_SIZE];
Marshal.StructureToPtr<REPARSE_GUID_DATA_BUFFER>(junctionData, inBuffer, false);
// To delete a reparse point:
// ReparseDataLength must be 0
// inBufferSize must be REPARSE_GUID_DATA_BUFFER_HEADER_SIZE
result = DeviceIoControl(dangerousHandle, FSCTL_DELETE_REPARSE_POINT, inBuffer, REPARSE_GUID_DATA_BUFFER_HEADER_SIZE, IntPtr.Zero, 0, out bytesReturned, IntPtr.Zero);
if (!result)
{
int lastError = Marshal.GetLastWin32Error();
throw new Win32Exception(lastError);
}
}
finally
{
if (success)
{
handle.DangerousRelease();
}
Marshal.FreeHGlobal(outBuffer);
Marshal.FreeHGlobal(inBuffer);
}
}
}
else
{
throw new ArgumentNullException("junctionPath");
}
return result;
}
private static SafeFileHandle OpenReparsePoint(string reparsePoint, FileDesiredAccess accessMode)
{
#if UNIX
@@ -8585,11 +8479,7 @@ namespace System.Management.Automation.Internal
}
string resultPath = path + adjustedStreamName;
if (!NativeMethods.DeleteFile(resultPath))
{
int error = Marshal.GetLastWin32Error();
throw new Win32Exception(error);
}
File.Delete(resultPath);
}
internal static void SetZoneOfOrigin(string path, SecurityZone securityZone)
@@ -8617,9 +8507,6 @@ namespace System.Management.Automation.Internal
IntPtr lpSecurityAttributes, FileMode dwCreationDisposition,
int dwFlagsAndAttributes, IntPtr hTemplateFile);
[DllImport(PinvokeDllNames.DeleteFileDllName, CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern bool DeleteFile(string lpFileName);
[DllImport(PinvokeDllNames.FindFirstStreamDllName, ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
[SuppressMessage("Microsoft.Globalization", "CA2101:SpecifyMarshalingForPInvokeStringArguments", MessageId = "AlternateStreamNativeData.Name")]
internal static extern SafeFindHandle FindFirstStreamW(
@@ -242,6 +242,8 @@ Describe "New-Item with links" -Tags @('CI', 'RequireAdminOnWindows') {
# Remove the link explicitly to avoid broken symlink issue
Remove-Item $FullyQualifiedLink -Force
# Test a code path removing a symbolic link (reparse point)
Test-Path $FullyQualifiedLink | Should -BeFalse
}
It "New-Item -ItemType SymbolicLink should understand directory path ending with slash" {
@@ -64,6 +64,6 @@ Describe "Unblock-File" -Tags "CI" {
$TestFileCreated = Get-ChildItem $TestFile
$TestFileCreated.IsReadOnly | Should -BeTrue
{ Unblock-File -LiteralPath $TestFile -ErrorAction Stop } | Should -Throw -ErrorId "RemoveItemUnauthorizedAccessError,Microsoft.PowerShell.Commands.UnblockFileCommand"
{ Unblock-File -LiteralPath $TestFile -ErrorAction Stop } | Should -Throw -ErrorId "RemoveItemUnableToAccessFile,Microsoft.PowerShell.Commands.UnblockFileCommand"
}
}