-
Notifications
You must be signed in to change notification settings - Fork 8.1k
Use .Net Core File.Delete() method to remove symbolic links and file streams #7017
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
26d6ff6
9682680
48ef128
8b76a94
ee31490
c7c9c9e
3cd12b3
4e8e536
e72ee88
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -174,9 +174,6 @@ | |
| <data name="TypeNotSupported" xml:space="preserve"> | ||
| <value>'{0}' is not supported in this system.</value> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @bergmeister It is out the PR. Please push new PR or open new tracking issue. |
||
| </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> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2874,41 +2874,20 @@ private void RemoveDirectoryInfoItem(DirectoryInfo directory, bool recurse, bool | |
| continueRemoval = ShouldProcess(directory.FullName, action); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It might be out the scope for this PR, but we don't check
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's postpone to new PR.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see a comment before the line says that we ask the confirmation only for removing root directory or recurse - but seems current behavior is that we remove the junction point as an object (not directory and no recurse) so we don't ask confirmation. |
||
| } | ||
|
|
||
| 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 @@ private static bool WinCreateJunction(string path, string target) | |
| } | ||
| } | ||
|
|
||
| [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 @@ internal static void DeleteFileStream(string path, string streamName) | |
| } | ||
| 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 @@ internal static extern SafeFileHandle CreateFile(string lpFileName, | |
| 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( | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This used to write out a non-terminating error, and now throw an exception which is a terminating error. I'm afraid that's not right.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. For reference https://docs.microsoft.com/en-us/powershell/developer/cmdlet/cmdlet-error-reporting#terminating-and-nonterminating-errors