From ce4c35b9ed42a582e0a00bdd90b410f6c23b788c Mon Sep 17 00:00:00 2001 From: jeffbi Date: Wed, 7 Jun 2017 17:13:51 -0700 Subject: [PATCH] Allow use of long paths (#3960) When calling Windows native API to determine if an item exists, ensure the path is prepended with "\\?\" when the path is a long path. Fixes #3891 --- .../engine/Utils.cs | 18 +++++++- .../FileSystem.Tests.ps1 | 41 +++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/System.Management.Automation/engine/Utils.cs b/src/System.Management.Automation/engine/Utils.cs index 9b8c4294a..781e09864 100644 --- a/src/System.Management.Automation/engine/Utils.cs +++ b/src/System.Management.Automation/engine/Utils.cs @@ -1153,8 +1153,22 @@ namespace System.Management.Automation internal class NativeMethods { - [DllImport(PinvokeDllNames.GetFileAttributesDllName, CharSet = CharSet.Unicode, SetLastError = true)] - internal static extern int GetFileAttributes(string lpFileName); + private static string EnsureLongPathPrefixIfNeeded(string path) + { + if (path.Length >= MAX_PATH && !path.StartsWith(@"\\?\", StringComparison.Ordinal)) + return @"\\?\" + path; + + return path; + } + + [DllImport(PinvokeDllNames.GetFileAttributesDllName, EntryPoint = "GetFileAttributesW", CharSet = CharSet.Unicode, SetLastError = true)] + private static extern int GetFileAttributesPrivate(string lpFileName); + + internal static int GetFileAttributes(string fileName) + { + fileName = EnsureLongPathPrefixIfNeeded(fileName); + return GetFileAttributesPrivate(fileName); + } [Flags] internal enum FileAttributes diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/FileSystem.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/FileSystem.Tests.ps1 index b513a206a..ace6982ed 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/FileSystem.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/FileSystem.Tests.ps1 @@ -663,6 +663,47 @@ Describe "Copy-Item can avoid copying an item onto itself" -Tags "CI", "RequireA } } +Describe "Handling long paths" -Tags "CI" { + BeforeAll { + $longDir = 'a' * 250 + $longSubDir = 'b' * 250 + $fileName = "file1.txt" + $topPath = Join-Path $TestDrive $longDir + $longDirPath = Join-Path $topPath $longSubDir + $longFilePath = Join-Path $longDirPath $fileName + $cwd = Get-Location + } + BeforeEach { + New-Item -ItemType File -Path $longFilePath -Force | Out-Null + } + AfterEach { + Remove-Item -Path $topPath -Force -Recurse -ErrorAction SilentlyContinue + Set-Location $cwd + } + + It "Can remove a file via a long path" { + Remove-Item -Path $longFilePath -ErrorVariable e -ErrorAction SilentlyContinue + $e | Should BeNullOrEmpty + $longFilePath | Should Not Exist + } + It "Can rename a file via a long path" { + $newFileName = "new-file.txt" + $newPath = Join-Path $longDirPath $newFileName + Rename-Item -Path $longFilePath -NewName $newFileName + $longFilePath | Should Not Exist + $newPath | Should Exist + } + It "Can change into a directory via a long path" { + Set-Location -Path $longDirPath -ErrorVariable e -ErrorAction SilentlyContinue + $e | Should BeNullOrEmpty + $c = Get-Location + $fileName | Should Exist + } + It "Can use Test-Path to check for a file via a long path" { + Test-Path $longFilePath | Should Be $true + } +} + Describe "Extended FileSystem Item/Content Cmdlet Provider Tests" -Tags "Feature" { BeforeAll { $testDir = "testDir"