Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Windows (NTFS): Inconsistent support for the long-path prefix (\\?\) in filesystem paths #10805

Copy link
Copy link
@mklement0

Description

@mklement0
Issue body actions

Note: This is a generalization of #4439. @SteveL-MSFT, if you agree, please close the latter.

Prefixing full, normalized, native filesystem paths with \\?\ allows targeting filesystem items whose path is longer than the legacy limit of 259 characters.

Update:

  • Prefix \\?\ isn't needed in PS Core (in .NET Core altogether), so long paths just work as-is, even if not enabled system-wide.
  • However, old code may still use it, and even new code may have to, when creating cross-edition scripts.
  • Additionally, and separately, \\?\ is useful for targeting files or directories with irregular names, such as ones with trailing spaces, so you can remove them with Remove-Item, for instance.

As an aside: In Windows 10 you can now opt in system-wide to support long paths, but individual applications must also opt in (PowerShell indirectly does) - see https://blogs.msdn.microsoft.com/jeremykuhne/2016/07/30/net-4-6-2-and-long-paths-on-windows-10/

As of PowerShell Core 7.3.0-preview.2, the inconsistent support for \\?\ is as follows:

  • With the -Path parameter, \\?\ isn't supported at all , so wildcard expressions cannot be used.

    • Get-Item and Get-ChildItem output nothing, and Remove-Item is a quiet no-op (silent failure) too.
  • With the -LiteralPath parameter in combination with \\?\:

    • Only file paths can be targeted.
    • Directory paths exhibit the following behavior:
      • Get-Item and Remove-Item complain about not finding the path.
        • Adding -Force makes Get-Item output a broken DirectoryInfo instance.
        • Using Remove-Item without -Recurse on a nonempty directory presents the usual confirmation prompt (implying the ability to recognize the path as existent), but then fails on confirmation.
      • Get-ChildItem reports the root directory's content instead(!)

Additionally, \\?\ paths do not work in the following cases:

Note:

  • Some of these problems are regressions from Windows PowerShell, where only the invocation / Start-Process tests fail and > only with a new file.

  • I haven't looked into whether invoking an executable with an overly long path is supported in principle by the underlying APIs.

Steps to reproduce

Run the following from a Pester test script (*.Tests.ps1) on Windows:

Describe "Support for long paths, via \\?\" {
  BeforeAll {
    Push-Location (Convert-Path TestDrive:\)
    $dirPath = $PWD.ProviderPath
    $PrefixedDir = "\\?\$dirPath"
    $fileName = ('a' * 248 + '.cmd')
    $fileNameAlt = ('b' * 248)
    $PrefixedFullName = "$PrefixedDir\$fileName"
    $PrefixedFullNameAlt = "$PrefixedDir\$fileNameAlt"
    # Create the file with the overly long path using .NET,
    # to avoid issues with New-Item
    [IO.File]::WriteAllText($PrefixedFullName, '')
  }
  It "Get-ChildItem -LiteralPath" {
    Get-ChildItem -LiteralPath $PrefixedFullName | % FullName | Should -Be $PrefixedFullName
  }
  It "Get-ChildItem -Path" {
    Get-ChildItem -Path $PrefixedFullName | % FullName | Should -Be $PrefixedFullName
  }
  It "Remove-Item -LiteralPath" {
    { Remove-Item -LiteralPath $PrefixedFullName } | Should -Not -Throw
    # Recreate the file.
    [IO.File]::WriteAllText($PrefixedFullName, '')
  }
  It "Remove-Item -Path" {
    { Remove-Item -Path $PrefixedFullName } | Should -Not -Throw
    # Recreate the file, if necessary
    [IO.File]::WriteAllText($PrefixedFullName, '')
  }
  It "> with new file" {
    { '' >  $PrefixedFullNameAlt } | Should -Not -Throw
  }
  It "> / >> with existing file." {
    { '@echo Hi.' >  $PrefixedFullName } | Should -Not -Throw
    { 'REM ' >> $PrefixedFullName } | Should -Not -Throw
  }
  It "Invocation / &" {
    & $PrefixedFullName | Should -Be 'Hi.'
  }
  It "Start-Process" {
    Start-Process -FilePath $PrefixedFullName
  }
  It "New-Item" {
    { New-Item -Force -Type File $PrefixedFullName } | Should -Not -Throw
  }
  It "Set-Location -LiteralPath" {
    { Set-Location -EA Stop -LiteralPath $PrefixedDir } | Should -Not -Throw
  }
  It "Set-Location -Path" {
    { Set-Location -EA Stop -Path $PrefixedDir } | Should -Not -Throw
  }
  AfterAll {
    # Use .NET to remove the overly long path, so that Pester itself doesn't 
    # fail on trying to remove the dir. underlying TestDrive:
    [IO.File]::Delete($PrefixedFullName)
    [IO.File]::Delete($PrefixedFullNameAlt)
    Pop-Location
  }
}

Expected behavior

All tests should pass.

Actual behavior

All tests but the first one fail, with various error messages.

Environment data

PowerShell Core 7.0.0-preview.4
Reactions are currently unavailable

Metadata

Metadata

Assignees

No one assigned

    Labels

    Area-FileSystem-Providerspecific to the FileSystem providerspecific to the FileSystem providerIssue-BugIssue has been identified as a bug in the productIssue has been identified as a bug in the productWG-Engine-Providersbuilt-in PowerShell providers such as FileSystem, Certificates, Registry, etc.built-in PowerShell providers such as FileSystem, Certificates, Registry, etc.WG-NeedsReviewNeeds a review by the labeled Working GroupNeeds a review by the labeled Working Group

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      Morty Proxy This is a proxified and sanitized view of the page, visit original site.