8000 Revert "Enhance Remove-Item to work with OneDrive (Second) (#15260)" … · PowerShell/PowerShell@0ae0b65 · GitHub
[go: up one dir, main page]

Skip to content

Commit 0ae0b65

Browse files
authored
Revert "Enhance Remove-Item to work with OneDrive (Second) (#15260)" (#15546)
1 parent 6c8d7c0 commit 0ae0b65

File tree

5 files changed

+149
-278
lines changed

5 files changed

+149
-278
lines changed

src/Microsoft.PowerShell.Commands.Management/commands/management/Navigation.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2699,7 +2699,7 @@ protected override void ProcessRecord()
26992699
try
27002700
{
27012701
System.IO.DirectoryInfo di = new(providerPath);
2702-
if (di != null && InternalSymbolicLinkLinkCodeMethods.IsReparsePointLikeSymlink(di))
2702+
if (di != null && (di.Attributes & System.IO.FileAttributes.ReparsePoint) != 0)
27032703
{
27042704
shouldRecurse = false;
27052705
treatAsFile = true;

src/System.Management.Automation/engine/Utils.cs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1863,7 +1863,7 @@ internal static string GetFormatStyleString(FormatStyle formatStyle)
18631863

18641864
if (ExperimentalFeature.IsEnabled("PSAnsiRendering"))
18651865
{
1866-
PSStyle psstyle = PSStyle.Instance;
1866+
PSStyle psstyle = PSStyle.Instance;
18671867
switch (formatStyle)
18681868
{
18691869
case FormatStyle.Reset:
@@ -2100,13 +2100,6 @@ public static class InternalTestHooks
21002100

21012101
internal static bool ThrowExdevErrorOnMoveDirectory;
21022102

2103-
// To emulate OneDrive behavior we use the hard-coded symlink.
2104-
// If OneDriveTestRecuseOn is false then the symlink works as regular symlink.
2105-
// If OneDriveTestRecuseOn is true then we resurce into the symlink as OneDrive should work.
2106-
internal static bool OneDriveTestOn;
2107-
internal static bool OneDriveTestRecurseOn;
2108-
internal static string OneDriveTestSymlinkName = "link-Beta";
2109-
21102103
/// <summary>This member is used for internal test purposes.</summary>
21112104
public static void SetTestHook(string property, object value)
21122105
{

src/System.Management.Automation/namespaces/FileSystemProvider.cs

Lines changed: 20 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1891,14 +1891,9 @@ private void Dir(
18911891
}
18921892

18931893
bool hidden = false;
1894-
bool checkReparsePoint = true;
18951894
if (!Force)
18961895
{
18971896
hidden = (recursiveDirectory.Attributes & FileAttributes.Hidden) != 0;
1898-
1899-
// We've already taken the expense of initializing the Attributes property here,
1900-
// so we can use that to avoid needing to call IsReparsePointLikeSymlink() later.
1901-
checkReparsePoint = (recursiveDirectory.Attributes & FileAttributes.ReparsePoint) != 0;
1 A93C 9021897
}
19031898

19041899
// if "Hidden" is explicitly specified anywhere in the attribute filter, then override
@@ -1912,7 +1907,7 @@ private void Dir(
19121907
// c) it is not a reparse point with a target (not OneDrive or an AppX link).
19131908
if (tracker == null)
19141909
{
1915-
if (checkReparsePoint && InternalSymbolicLinkLinkCodeMethods.IsReparsePointLikeSymlink(recursiveDirectory))
1910+
if (InternalSymbolicLinkLinkCodeMethods.IsReparsePointWithTarget(recursiveDirectory))
19161911
{
19171912
continue;
19181913
}
@@ -2064,7 +2059,7 @@ string ToModeString(FileSystemInfo fileSystemInfo)
20642059
public static string NameString(PSObject instance)
20652060
{
20662061
return instance?.BaseObject is FileSystemInfo fileInfo
2067-
? InternalSymbolicLinkLinkCodeMethods.IsReparsePointLikeSymlink(fileInfo)
2062+
? InternalSymbolicLinkLinkCodeMethods.IsReparsePointWithTarget(fileInfo)
20682063
? $"{fileInfo.Name} -> {InternalSymbolicLinkLinkCodeMethods.GetTarget(instance)}"
20692064
: fileInfo.Name
20702065
: string.Empty;
@@ -3104,31 +3099,22 @@ private void RemoveDirectoryInfoItem(DirectoryInfo directory, bool recurse, bool
31043099
continueRemoval = ShouldProcess(directory.FullName, action);
31053100
}
31063101

3107-
if (InternalSymbolicLinkLinkCodeMethods.IsReparsePointLikeSymlink(directory))
3102+
if (directory.Attributes.HasFlag(FileAttributes.ReparsePoint))
31083103
{
3109-
void WriteErrorHelper(Exception exception)
3110-
{
3111-
WriteError(new ErrorRecord(exception, errorId: "DeleteSymbolicLinkFailed", ErrorCategory.WriteError, directory));
3112-
}
3113-
31143104
try
31153105
{
3116-
if (InternalTestHooks.OneDriveTestOn)
3117-
{
3118-
WriteErrorHelper(new IOException());
3119-
return;
3120-
}
3121-
else
3122-
{
3123-
// Name surrogates should just be detached.
3124-
directory.Delete();
3125-
}
3106+
// TODO:
3107+
// Different symlinks seem to vary by behavior.
3108+
// In particular, OneDrive symlinks won't remove without recurse,
3109+
// but the .NET API here does not allow us to distinguish them.
3110+
// We may need to revisit using p/Invokes here to get the right behavior
3111+
directory.Delete();
31263112
}
31273113
catch (Exception e)
31283114
{
31293115
string error = StringUtil.Format(FileSystemProviderStrings.CannotRemoveItem, directory.FullName, e.Message);
31303116
var exception = new IOException(error, e);
3131-
WriteErrorHelper(exception);
3117+
WriteError(new ErrorRecord(exception, errorId: "DeleteSymbolicLinkFailed", ErrorCategory.WriteError, directory));
31323118
}
31333119

31343120
return;
@@ -8039,7 +8025,7 @@ protected override bool ReleaseHandle()
80398025
}
80408026

80418027
// SetLastError is false as the use of this API doesn't not require GetLastError() to be called
8042-
[DllImport(PinvokeDllNames.FindFirstFileDllName, EntryPoint = "FindFirstFileExW", SetLastError = true, CharSet = CharSet.Unicode)]
8028+
[DllImport(PinvokeDllNames.FindFirstFileDllName, EntryPoint = "FindFirstFileExW", SetLastError = false, CharSet = CharSet.Unicode)]
80438029
private static extern SafeFindHandle FindFirstFileEx(string lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, ref WIN32_FIND_DATA lpFindFileData, FINDEX_SEARCH_OPS fSearchOp, IntPtr lpSearchFilter, int dwAdditionalFlags);
80448030

80458031
internal enum FINDEX_INFO_LEVELS : uint
@@ -8230,50 +8216,28 @@ internal static bool IsReparsePoint(FileSystemInfo fileInfo)
82308216
return fileInfo.Attributes.HasFlag(System.IO.FileAttributes.ReparsePoint);
82318217
}
82328218

8233-
internal static bool IsReparsePointLikeSymlink(FileSystemInfo fileInfo)
8219+
internal static bool IsReparsePointWithTarget(FileSystemInfo fileInfo)
82348220
{
8235-
#if UNIX
8236-
// Reparse point on Unix is a symlink.
8237-
return IsReparsePoint(fileInfo);
8238-
#else
8239-
if (InternalTestHooks.OneDriveTestOn && fileInfo.Name == InternalTestHooks.OneDriveTestSymlinkName)
8221+
if (!IsReparsePoint(fileInfo))
82408222
{
8241-
return !InternalTestHooks.OneDriveTestRecurseOn;
8223+
return false;
82428224
}
8243-
8244-
WIN32_FIND_DATA data = default;
8245-
string fullPath = Path.TrimEndingDirectorySeparator(fileInfo.FullName);
8246-
using (var handle = FindFirstFileEx(fullPath, FINDEX_INFO_LEVELS.FindExInfoBasic, ref data, FINDEX_SEARCH_OPS.FindExSearchNameMatch, IntPtr.Zero, 0))
8225+
#if !UNIX
8226+
// It is a reparse point and we should check some reparse point tags.
8227+
var data = new WIN32_FIND_DATA();
8228+
using (var handle = FindFirstFileEx(fileInfo.FullName, FINDEX_INFO_LEVELS.FindExInfoBasic, ref data, FINDEX_SEARCH_OPS.FindExSearchNameMatch, IntPtr.Zero, 0))
82478229
{
8248-
if (handle.IsInvalid)
8249-
{
8250-
// Our handle could be invalidated by something else touching the filesystem,
8251-
// so ensure we deal with that possibility here
8252-
int lastError = Marshal.GetLastWin32Error();
8253-
throw new Win32Exception(lastError);
8254-
}
8255-
8256-
// We already have the file attribute information from our Win32 call,
8257-
// so no need to take the expense of the FileInfo.FileAttributes call
8258-
const int FILE_ATTRIBUTE_REPARSE_POINT = 0x0400;
8259-
if ((data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0)
8260-
{
8261-
// Not a reparse point.
8262-
return false;
8263-
}
8264-
82658230
// The name surrogate bit 0x20000000 is defined in https://docs.microsoft.com/windows/win32/fileio/reparse-point-tags
82668231
// Name surrogates (0x20000000) are reparse points that point to other named entities local to the filesystem
82678232
// (like symlinks and mount points).
82688233
// In the case of OneDrive, they are not name surrogates and would be safe to recurse into.
8269-
if ((data.dwReserved0 & 0x20000000) == 0 && (data.dwReserved0 != IO_REPARSE_TAG_APPEXECLINK))
8234+
if (!handle.IsInvalid && (data.dwReserved0 & 0x20000000) == 0 && (data.dwReserved0 != IO_REPARSE_TAG_APPEXECLINK))
82708235
{
82718236
return false;
82728237
}
82738238
}
8274-
8275-
return true;
82768239
#endif
8240+
return true;
82778241
}
82788242

82798243
internal static bool WinIsHardLink(FileSystemInfo fileInfo)

test/powershell/Modules/Microsoft.PowerShell.Management/FileSystem.Tests.ps1

Lines changed: 1 addition & 62 deletions
D119
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,7 @@ Describe "Hard link and symbolic link tests" -Tags "CI", "RequireAdminOnWindows"
570570
$omegaFile1 = Join-Path $omegaDir "OmegaFile1"
571571
$omegaFile2 = Join-Path $omegaDir "OmegaFile2"
572572
$betaDir = Join-Path $alphaDir "sub-Beta"
573-
$betaLink = Join-Path $alphaDir "link-Beta" # Don't change! The name is hard-coded in PowerShell for OneDrive tests.
573+
$betaLink = Join-Path $alphaDir "link-Beta"
574574
$betaFile1 = Join-Path $betaDir "BetaFile1.txt"
575575
$betaFile2 = Join-Path $betaDir "BetaFile2.txt"
576576
$betaFile3 = Join-Path $betaDir "BetaFile3.txt"
@@ -623,31 +623,6 @@ Describe "Hard link and symbolic link tests" -Tags "CI", "RequireAdminOnWindows"
623623
$ci = Get-ChildItem $alphaLink -Recurse -Name
624624
$ci.Count | Should -BeExactly 7 # returns 10 - unexpectly recurce in link-alpha\link-Beta. See https://github.com/PowerShell/PowerShell/issues/11614
625625
}
626-
It "Get-ChildItem will recurse into emulated OneDrive directory" -Skip:(-not $IsWindows) {
627-
# The test depends on the files created in previous test:
628-
#New-Item -ItemType SymbolicLink -Path $alphaLink -Value $alphaDir
629-
#New-Item -ItemType SymbolicLink -Path $betaLink -Value $betaDir
630-
631-
[System.Management.Automation.Internal.InternalTestHooks]::SetTestHook('OneDriveTestOn', $true)
632-
[System.Management.Automation.Internal.InternalTestHooks]::SetTestHook('OneDriveTestRecurseOn', $false)
633-
try
634-
{
635-
# '$betaDir' is a symlink - we don't follow symlinks
636-
# This emulates PowerShell 6.2 and below behavior.
637-
$ci = Get-ChildItem -Path $alphaDir -Recurse
638-
$ci.Count | Should -BeExactly 7
639-
640-
# Now we follow the symlink like on OneDrive.
641-
[System.Management.Automation.Internal.InternalTestHooks]::SetTestHook('OneDriveTestRecurseOn', $true)
642-
$ci = Get-ChildItem -Path $alphaDir -Recurse
643-
$ci.Count | Should -BeExactly 10
644-
}
645-
finally
646-
{
647-
[System.Management.Automation.Internal.InternalTestHooks]::SetTestHook('OneDriveTestRecurseOn', $false)
648-
[System.Management.Automation.Internal.InternalTestHooks]::SetTestHook('OneDriveTestOn', $false)
649-
}
650-
}
651626
It "Get-ChildItem will recurse into symlinks given -FollowSymlink, avoiding link loops" {
652627
New-Item -ItemType Directory -Path $gammaDir
653628
New-Item -ItemType SymbolicLink -Path $uponeLink -Value $betaDir
@@ -769,42 +744,6 @@ Describe "Hard link and symbolic link tests" -Tags "CI", "RequireAdminOnWindows"
769744
$childB.Count | Should -BeExactly $childA.Count
770745
$childB.Name | Should -BeExactly $childA.Name
771746
}
772-
It "Remove-Item will recurse into emulated OneDrive directory" -Skip:(-not $IsWindows) {
773-
$alphaDir = Join-Path $TestDrive "sub-alpha2"
774-
$alphaLink = Join-Path $TestDrive "link-alpha2"
775-
$alphaFile1 = Join-Path $alphaDir "AlphaFile1.txt"
776-
$betaDir = Join-Path $alphaDir "sub-Beta"
777-
$betaLink = Join-Path $alphaDir "link-Beta"
778-
$betaFile1 = Join-Path $betaDir "BetaFile1.txt"
779-
780-
New-Item -ItemType Directory -Path $alphaDir > $null
781-
New-Item -ItemType File -Path $alphaFile1 > $null
782-
New-Item -ItemType Directory -Path $betaDir > $null
783-
New-Item -ItemType File -Path $betaFile1 > $null
784-
785-
New-Item -ItemType SymbolicLink -Path $alphaLink -Value $alphaDir > $null
786-
New-Item -ItemType SymbolicLink -Path $betaLink -Value $betaDir > $null
787-
788-
[System.Management.Automation.Internal.InternalTestHooks]::SetTestHook('OneDriveTestOn', $true)
789-
[System.Management.Automation.Internal.InternalTestHooks]::SetTestHook('OneDriveTestRecurseOn', $false)
790-
try
791-
{
792-
# With the test hook turned on we don't remove '$betaDir' symlink.
793-
# This emulates PowerShell 7.1 and below behavior.
794-
{ Remove-Item -Path $betaLink -Recurse -ErrorAction Stop } | Should -Throw -ErrorId "DeleteSymbolicLinkFailed,Microsoft.PowerShell.Commands.RemoveItemCommand"
795-
796-
# Now we emulate OneDrive and follow the symlink like on OneDrive.
797-
[System.Management.Automation.Internal.InternalTestHooks]::SetTestHook('OneDriveTestRecurseOn', $true)
798-
Remove-Item -Path $betaLink -Recurse
799-
Test-Path -Path $betaLink | Should -BeFalse
800-
}
801-
finally
802-
{
803-
[System.Management.Automation.Internal.InternalTestHooks]::SetTestHook('OneDriveTestRecurseOn', $false)
804-
[System.Management.Automation.Internal.InternalTestHooks]::SetTestHook('OneDriveTestOn', $false)
805-
}
806-
}
807-
808747
}
809748
}
810749

0 commit comments

Comments
 (0)
0