Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling Path Too Long Exception with New-PSDrive

Tags:

powershell

I am recursing a deep folder structure in order to retreive all folder paths like so:

$subFolders = Get-ChildItem $rootFolder -Recurse -Directory  -ErrorVariable folderErrors | Select-Object -ExpandProperty FullName

NOTE: $rootFolder in my case is a network share. i.e. "\\server\DeptDir$\somefolder"

The $folderErrors variable is correctly capturing all the FileTooLong exceptions so I want to create new PSDrives using the long Paths in order to recurse those long paths.

So I create a new PSDrive using this cmdlet:

new-psdrive -Name "long1" -PSProvider FileSystem -Root $folderErrors[0].CategoryInfo.TargetName

However, after creating a new PSDrive I am still getting PathTooLong Exceptions.

PS C:\>> cd long1:
PS long1:\>> dir
dir : The specified path, file name, or both are too long. The fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters.
At line:1 char:1
+ dir
+ ~~~
    + CategoryInfo          : ReadError: (\\svr01\Dep...\Fibrebond ECO\:String) [Get-ChildItem], PathTooLongException
    + FullyQualifiedErrorId : DirIOError,Microsoft.PowerShell.Commands.GetChildItemCommand

I see no other way around this problem. Am I doing something incorrectly? Why is the new PSDrive throwing PathTooLong when I am creating a drive at the location where the path is too long?

Thanks

like image 948
ChiliYago Avatar asked Sep 19 '17 19:09

ChiliYago


1 Answers

There is a local policy that is now available since Windows anniversary update.

Requirements are :

  • Windows Management Framework 5.1

  • .Net Framework 4.6.2 or more recent

  • Windows 10 / Windows server 2016 (Build 1607 or newer)

This policy can be enabled using the following snippet.

#GPEdit location:  Configuration>Administrative Templates>System>FileSystem 
Set-ItemProperty 'HKLM:\System\CurrentControlSet\Control\FileSystem' -Name 'LongPathsEnabled' -value 1

Otherwise, you can actually get to the paths longer than 260 characters by making your call to the unicode version of Windows API.

There's a catch though. This work only in Powershell 5.1 minimum.

From there, instead of making your call the standard way:

get-childitem -Path 'C:\Very long path' -Recurse  

You will need to use the following prefix: \\?\

Example

get-childitem -LiteralPath '\\?\C:\Very long path' -Recurse 

For UNC path, this is slightly different, the prefix being \\?\UNC\ instead of \\

get-childitem -LiteralPath '\\?\UNC\127.0.0.1\c$\Very long path\' -Recurse

Important

When calling Get-ChildItem unicode version, you should use the -LiteralPath parameter instead of Path

From Microsoft documentation

-LiteralPath

Specifies a path to one or more locations. Unlike the -Path parameter, the value of the -LiteralPath parameter is used exactly as it is typed. No characters are interpreted as wildcards. If the path includes escape characters, enclose them in single quotation marks. Single quotation marks tell Windows PowerShell not to interpret any characters as escape sequences.

source

Example

(get-childitem -LiteralPath '\\?\UNC\127.0.0.1\This is a folder$' -Recurse) | 
ft @{'n'='Path length';'e'={$_.FullName.length}}, FullName 

output Get-Childitem with long paths

Here is the actual functional test I made to create the very long repository, query it to produce the output above and confirm I could create repository with more than 260 characters and view them.

Function CreateVeryLongPath([String]$Root,[Switch]$IsUNC,$FolderName = 'Dummy Folder',$iterations = 200) {
    $Base = '\\?\'
    if ($IsUNC) {$Base = '\\?\UNC\'}

    $CurrentPath = $Base + $Root + $FolderName + '\'

    For ($i=0;$i -le $iterations;$i++) {

    New-Item -Path $CurrentPath -Force -ItemType Directory | Out-Null
    $currentPath = $CurrentPath +  $FolderName + '\'
    }
}

Function QueryVeryLongPath([String]$Root,[Switch]$IsUNC) {
    $Base = '\\?\'
    if ($IsUNC) {$Base = '\\?\UNC\';$Root = $Root.substring(2,$Root.Length -2)}

    $BasePath = $Base + $Root
    Get-ChildItem -LiteralPath $BasePath -Recurse | ft @{'n'='Length';'e'={$_.FullName.Length}},FullName
}



CreateVeryLongPath -Root 'C:\__tmp\' -FolderName 'This is a folder'
QueryVeryLongPath -Root 'C:\__tmp\Dummy Folder11\' 

#UNC - tested on a UNC share path 
CreateVeryLongPath -Root '\\ServerName\ShareName\' -FolderName 'This is a folder' -IsUNC
QueryVeryLongPath -Root '\\ServerName\ShareName\' -IsUNC

Worth to mention

During my research, I saw people mentioning using RoboCopy then parse its output. I am not particularly fond of this approach so I won't elaborate on it. (edit: Years later, I discovered that Robocopy is part of Windows and not some third-party utility. I guess that would be an ok approach too, although I prefer a pure Powershell solution)

I also saw AlphaFS being mentionned a couple of time which is a library that allows to overcome the 260 characters limitation too. It is open sourced on Github and there's even a (I did not test it though) Get-AlphaFSChildItem built on it available on Technet here

Other references

Long Paths in .Net

Naming Files, Paths, and Namespaces

like image 105
Sage Pourpre Avatar answered Sep 18 '22 13:09

Sage Pourpre