@@ -1848,9 +1848,11 @@ private void Dir(
18481848 // a) the user has asked to with the -FollowSymLinks switch parameter and
18491849 // b) the directory pointed to by the symlink has not already been visited,
18501850 // preventing symlink loops.
1851+ // c) it is not a name surrogate making it not a symlink
18511852 if ( tracker == null )
18521853 {
1853- if ( InternalSymbolicLinkLinkCodeMethods . IsReparsePoint ( recursiveDirectory ) )
1854+ if ( InternalSymbolicLinkLinkCodeMethods . IsReparsePoint ( recursiveDirectory ) &&
1855+ InternalSymbolicLinkLinkCodeMethods . IsNameSurrogateReparsePoint ( recursiveDirectory . FullName ) )
18541856 {
18551857 continue ;
18561858 }
@@ -7706,6 +7708,8 @@ public static class InternalSymbolicLinkLinkCodeMethods
77067708
77077709 private const string NonInterpretedPathPrefix = @"\??\" ;
77087710
7711+ private const int MAX_PATH = 260 ;
7712+
77097713 [ Flags ]
77107714 // dwDesiredAccess of CreateFile
77117715 internal enum FileDesiredAccess : uint
@@ -7846,6 +7850,39 @@ internal static extern IntPtr CreateFile(
78467850 FileAttributes dwFlagsAndAttributes ,
78477851 IntPtr hTemplateFile ) ;
78487852
7853+ [ DllImport ( PinvokeDllNames . FindFirstFileDllName , EntryPoint = "FindFirstFileExW" , SetLastError = true , CharSet = CharSet . Unicode ) ]
7854+ private static extern SafeFileHandle FindFirstFileEx ( string lpFileName , FINDEX_INFO_LEVELS fInfoLevelId , ref WIN32_FIND_DATA lpFindFileData , FINDEX_SEARCH_OPS fSearchOp , IntPtr lpSearchFilter , int dwAdditionalFlags ) ;
7855+
7856+ internal enum FINDEX_INFO_LEVELS : uint
7857+ {
7858+ FindExInfoStandard = 0x0u ,
7859+ FindExInfoBasic = 0x1u ,
7860+ FindExInfoMaxInfoLevel = 0x2u ,
7861+ }
7862+
7863+ internal enum FINDEX_SEARCH_OPS : uint
7864+ {
7865+ FindExSearchNameMatch = 0x0u ,
7866+ FindExSearchLimitToDirectories = 0x1u ,
7867+ FindExSearchLimitToDevices = 0x2u ,
7868+ FindExSearchMaxSearchOp = 0x3u ,
7869+ }
7870+
7871+ [ StructLayout ( LayoutKind . Sequential , CharSet = CharSet . Unicode ) ]
7872+ internal unsafe struct WIN32_FIND_DATA
7873+ {
7874+ internal uint dwFileAttributes ;
7875+ internal System . Runtime . InteropServices . ComTypes . FILETIME ftCreationTime ;
7876+ internal System . Runtime . InteropServices . ComTypes . FILETIME ftLastAccessTime ;
7877+ internal System . Runtime . InteropServices . ComTypes . FILETIME ftLastWriteTime ;
7878+ internal uint nFileSizeHigh ;
7879+ internal uint nFileSizeLow ;
7880+ internal uint dwReserved0 ;
7881+ internal uint dwReserved1 ;
7882+ internal fixed char cFileName [ MAX_PATH ] ;
7883+ internal fixed char cAlternateFileName [ 14 ] ;
7884+ }
7885+
78497886 /// <summary>
78507887 /// Gets the target of the specified reparse point.
78517888 /// </summary>
@@ -7993,6 +8030,25 @@ internal static bool IsReparsePoint(FileSystemInfo fileInfo)
79938030 : Platform . NonWindowsIsSymLink ( fileInfo ) ;
79948031 }
79958032
8033+ internal static bool IsNameSurrogateReparsePoint ( string filePath )
8034+ {
8035+ #if ! UNIX
8036+ var data = new WIN32_FIND_DATA ( ) ;
8037+ using ( SafeFileHandle handle = FindFirstFileEx ( filePath , FINDEX_INFO_LEVELS . FindExInfoBasic , ref data , FINDEX_SEARCH_OPS . FindExSearchNameMatch , IntPtr . Zero , 0 ) )
8038+ {
8039+ // Name surrogates are reparse points that point to other named entities local to the filesystem (like symlinks)
8040+ // In the case of OneDrive, they are not surrogates and would be safe to recurse into.
8041+ // This code is equivalent to the IsReparseTagNameSurrogate macro: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ntifs/nf-ntifs-isreparsetagnamesurrogate
8042+ if ( ! handle . IsInvalid && ( data . dwReserved0 & 0x20000000 ) == 0 )
8043+ {
8044+ return false ;
8045+ }
8046+ }
8047+ #endif
8048+ // true means the reparse point is a symlink
8049+ return true ;
8050+ }
8051+
79968052 internal static bool WinIsHardLink ( FileSystemInfo fileInfo )
79978053 {
79988054 bool isHardLink = false ;
0 commit comments