@@ -11,59 +11,90 @@ namespace System.Runtime.Loader
1111{
1212 public partial class AssemblyLoadContext
1313 {
14+ internal struct OrdinalIgnoreCaseCultureComparer
15+ {
16+ public readonly string _cultureNameUpper ;
17+ public OrdinalIgnoreCaseCultureComparer ( string cultureName )
18+ {
19+ _cultureNameUpper = cultureName . ToUpperInvariant ( ) ;
20+ }
21+
22+ public bool Equals ( ref ReadOnlySpan < char > entryName )
23+ {
24+ if ( _cultureNameUpper . Length != entryName . Length )
25+ return false ;
26+
27+ for ( int i = 0 ; i < _cultureNameUpper . Length ; ++ i )
28+ {
29+ if ( _cultureNameUpper [ i ] != char . ToUpperInvariant ( entryName [ i ] ) )
30+ return false ;
31+ }
32+ return true ;
33+ }
34+ }
35+
1436 // Find satellite path using case insensitive culture name
15- internal static unsafe string ? FindCaseInsensitiveSatellitePath ( string parentDirectory , string cultureName , string assembly )
37+ internal static unsafe string ? FindCaseInsensitiveCultureName ( string parentDirectory , string cultureName , string assembly )
1638 {
1739 int bufferSize = Interop . Sys . GetReadDirRBufferSize ( ) ;
18- byte [ ] ? dirBuffer = null ;
40+
41+ byte [ ] dirBuffer = ArrayPool < byte > . Shared . Rent ( bufferSize ) ;
42+ IntPtr dirHandle = Interop . Sys . OpenDir ( parentDirectory ) ;
43+
44+ string ? result = null ;
45+
1946 try
2047 {
21- dirBuffer = ArrayPool < byte > . Shared . Rent ( bufferSize ) ;
22-
23- fixed ( byte * dirBufferPtr = dirBuffer )
48+ if ( dirHandle != IntPtr . Zero )
2449 {
25- IntPtr dirHandle = Interop . Sys . OpenDir ( parentDirectory ) ;
26- if ( dirHandle != IntPtr . Zero )
50+ try
2751 {
28- try
52+ fixed ( byte * dirBufferPtr = dirBuffer )
2953 {
54+ Span < char > nameBuffer = stackalloc char [ Interop . Sys . DirectoryEntry . NameBufferSize ] ;
55+ OrdinalIgnoreCaseCultureComparer cultureNameComparer = new OrdinalIgnoreCaseCultureComparer ( cultureName ) ;
56+
3057 Interop . Sys . DirectoryEntry dirent ;
3158 while ( Interop . Sys . ReadDirR ( dirHandle , dirBufferPtr , bufferSize , out dirent ) == 0 )
3259 {
3360 if ( dirent . InodeType != Interop . Sys . NodeType . DT_DIR )
3461 continue ;
3562
36- Span < char > nameBuffer = stackalloc char [ Interop . Sys . DirectoryEntry . NameBufferSize ] ;
3763 ReadOnlySpan < char > entryName = dirent . GetName ( nameBuffer ) ;
3864
39- if ( cultureName . Length != entryName . Length )
65+ if ( ! cultureNameComparer . Equals ( ref entryName ) )
4066 continue ;
4167
42- string entryNameString = entryName . ToString ( ) ;
43-
44- if ( ! cultureName . Equals ( entryNameString , StringComparison . InvariantCultureIgnoreCase ) )
45- continue ;
46-
47- string assemblyPath = $ "{ parentDirectory } /{ entryNameString } /{ assembly } .dll";
48-
49- if ( Internal . IO . File . InternalExists ( assemblyPath ) )
50- return assemblyPath ;
68+ if ( result == null )
69+ {
70+ // Convert to string because we will overwrite the backing buffer next loop
71+ result = entryName . ToString ( ) ;
72+ // We do not return here because we do not want to debug/allow cases where
73+ // there are multiple directories which match case insensitive cultureName
74+ //
75+ // Do an exhaustive search
76+ }
77+ else
78+ {
79+ // We found more than one directory with the same case insensitive name
80+ // return null to make this case predicatably fail.
81+ return null ;
82+ }
5183 }
5284 }
53- finally
54- {
55- if ( dirHandle != IntPtr . Zero )
56- Interop . Sys . CloseDir ( dirHandle ) ;
57- }
85+ }
86+ finally
87+ {
88+ Interop . Sys . CloseDir ( dirHandle ) ;
5889 }
5990 }
6091 }
6192 finally
6293 {
63- if ( dirBuffer != null )
64- ArrayPool < byte > . Shared . Return ( dirBuffer ) ;
94+ ArrayPool < byte > . Shared . Return ( dirBuffer ) ;
6595 }
66- return null ;
96+
97+ return result ;
6798 }
6899
69100 private Assembly ? ResolveSatelliteAssembly ( AssemblyName assemblyName )
@@ -93,20 +124,17 @@ public partial class AssemblyLoadContext
93124 string assemblyPath = $ "{ parentDirectory } /{ cultureName } /{ assemblyName . Name } .dll";
94125 if ( Internal . IO . File . InternalExists ( assemblyPath ) )
95126 {
96- Assembly satelliteAssembly = parentAlc . LoadFromAssemblyPath ( assemblyPath ) ;
97-
98- if ( satelliteAssembly != null )
99- return satelliteAssembly ;
127+ return parentAlc . LoadFromAssemblyPath ( assemblyPath ) ;
100128 }
101129 else if ( Path . IsCaseSensitive )
102130 {
103- string ? caseInsensitiveAssemblyPath = FindCaseInsensitiveSatellitePath ( parentDirectory , cultureName , assemblyName . Name ) ;
131+ string ? caseInsensitiveCultureName = FindCaseInsensitiveCultureName ( parentDirectory , cultureName , assemblyName . Name ) ;
104132
105- if ( caseInsensitiveAssemblyPath != null )
133+ if ( caseInsensitiveCultureName != null )
106134 {
107- Assembly satelliteAssembly = parentAlc . LoadFromAssemblyPath ( caseInsensitiveAssemblyPath ) ;
108-
109- return satelliteAssembly ;
135+ assemblyPath = $ " { parentDirectory } / { caseInsensitiveCultureName } / { assemblyName . Name } .dll" ;
136+ if ( Internal . IO . File . InternalExists ( assemblyPath ) )
137+ return parentAlc . LoadFromAssemblyPath ( assemblyPath ) ;
110138 }
111139 }
112140 return null ;
0 commit comments