Skip to content

Remove-Item does not work correctly with long paths #15466

Closed

Description

@rjmholt

From #15260 (comment).

Rename-Item and Set-Location are also affected by this issue.

Steps to reproduce

$testdrive = "$env:TEMP/test"
$longDir = 'a' * 250
$longSubDir = 'b' * 250
$fileName = "file1.txt"
$topPath = Join-Path $TestDrive $longDir
$longDirPath = Join-Path $topPath $longSubDir
$longFilePath = Join-Path $longDirPath $fileName
new-item -itemtype file -path $longFilePath -force
remove-item -Path $longFilePath

Expected behavior

file is removed successfully

Actual behavior

> remove-item -Path $longFilePath
Remove-Item: The system cannot find the path specified.
C:\Users\rjmho
> get-error

Exception             :
    Type            : System.ComponentModel.Win32Exception
    NativeErrorCode : 3
    ErrorCode       : -2147467259
    TargetSite      :
        Name          : IsReparsePointLikeSymlink
        DeclaringType : Microsoft.PowerShell.Commands.InternalSymbolicLinkLinkCodeMethods
        MemberType    : Method
        Module        : System.Management.Automation.dll
    StackTrace      :
   at Microsoft.PowerShell.Commands.InternalSymbolicLinkLinkCodeMethods.IsReparsePointLikeSymlink(FileSystemInfo
fileInfo) in System.Management.Automation.dll:token 0x60016f5+0x57
   at Microsoft.PowerShell.Commands.RemoveItemCommand.ProcessRecord() in
Microsoft.PowerShell.Commands.Management.dll:token 0x60004dd+0x2e2
   at System.Management.Automation.CommandProcessor.ProcessRecord() in System.Management.Automation.dll:token
0x60020f6+0x1ae
    Message         : The system cannot find the path specified.
    Source          : System.Management.Automation
    HResult         : -2147467259
CategoryInfo          : NotSpecified: (:) [Remove-Item], Win32Exception
FullyQualifiedErrorId : System.ComponentModel.Win32Exception,Microsoft.PowerShell.Commands.RemoveItemCommand
InvocationInfo        :
    MyCommand        : Remove-Item
    ScriptLineNumber : 1
    OffsetInLine     : 1
    HistoryId        : 4
    Line             : remove-item -Path $longFilePath
    PositionMessage  : At line:1 char:1
                       + remove-item -Path $longFilePath
                       + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    InvocationName   : remove-item
    CommandOrigin    : Internal
ScriptStackTrace      : at <ScriptBlock>, <No file>: line 1

Environment data


Name                           Value
----                           -----
PSVersion                      7.2.0-preview.6
PSEdition                      Core
GitCommitId                    7.2.0-preview.6
OS                             Microsoft Windows 10.0.19042
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

Proposed solution

Implement long paths:

diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs
index b37b4d10c..da0e4cb2d 100644
--- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs
+++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs
@@ -8243,6 +8243,12 @@ namespace Microsoft.PowerShell.Commands

             WIN32_FIND_DATA data = default;
             string fullPath = Path.TrimEndingDirectorySeparator(fileInfo.FullName);
+
+            if (fullPath.Length > MAX_PATH)
+            {
+                fullPath = "\\\\?\\" + fullPath;
+            }
+
             using (var handle = FindFirstFileEx(fullPath, FINDEX_INFO_LEVELS.FindExInfoBasic, ref data, FINDEX_SEARCH_OPS.FindExSearchNameMatch, IntPtr.Zero, 0))
             {

Also ensure that the actual file exists before calling the Windows API on it so that the proper error can be thrown if not:

System.IO.DirectoryInfo di = new(providerPath);
if (di != null && InternalSymbolicLinkLinkCodeMethods.IsReparsePointLikeSymlink(di))

Activity

  1. jborean93 commented on May 26, 2021

    @jborean93
    Collaborator

    Why conditionally add \\?\ if it exceeds max path, if we control what APIs are called then we should always add that prefix. The benefit there is you are always running the same code even for short paths rather than sometimes testing this edge case.

    You also need to be careful when dealing with UNC paths, the prefix is \\?\UNC\$server\$share so just prefixing isn’t enough here.

  2. jborean93 commented on May 26, 2021

    @jborean93
    Collaborator

    Also while PowerShell’s location can probably be fine with paths that exceed MAX_PATH I’ve encountered problems in the past with trying to set a long path to the process’ working dir. I don’t think that’s something powershell can change and might cause unknown problems down the line when spawning new processes from powershell that inherit from the file system location (not 100% sure if it actually does today).

  3. rjmholt commented on May 27, 2021

    @rjmholt
    CollaboratorAuthor

    Sounds like we could use your review on the next PR to fix this Jordan Borean (@jborean93). (It's still in draft for a reason though)

    You also need to be careful when dealing with UNC paths, the prefix is \?\UNC$server$share so just prefixing isn’t enough here.

    Good to know

    Why conditionally add \?\ if it exceeds max path, if we control what APIs are called then we should always add that prefix

    That suggestion was just based on a principle of minimal perturbation. But if FindFirstFileExW is always going to succeed there, then that seems reasonable.

    Also while PowerShell’s location can probably be fine with paths that exceed MAX_PATH I’ve encountered problems in the past with trying to set a long path to the process’ working dir

    Yeah, in this case I think the issue is due to us calling the Windows API directly in an attempt to work around OneDrive weirdness. In the process case, I think .NET does a lot more under the hood (as it does in the Path APIs).

    It may also be that the right fix for us here is to abandon the efficiency that the original change was trying to get and simply look at the file twice, once to determine whether it's OneDrive and the second time to call through .NET and take advantage of its robustness.

  4. jborean93 commented on May 27, 2021

    @jborean93
    Collaborator

    I’ll have to add some comments when I’m next on a laptop. It’s hard to review actual code changes when looking from your phone :)

  5. rjmholt commented on May 27, 2021

    @rjmholt
    CollaboratorAuthor

    It’s hard to review actual code changes when looking from your phone :)

    Dw I was also on a phone today 🙂

  6. ghost removed
    Needs-TriageThe issue is new and needs to be triaged by a work group.
    on Aug 5, 2021
  7. svermaak commented on Aug 23, 2022

    @svermaak

    Still getting it in 7.2.5
    image

  8. iSazonov commented on Aug 24, 2022

    @iSazonov
    Collaborator

    svermaak Please open new issue with exact repro steps.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Resolution-FixedThe issue is fixed.WG-Engine-Providersbuilt-in PowerShell providers such as FileSystem, Certificates, Registry, etc.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions