Ensure the System.Web.UI.WebControls.RepeatInfo_Autogen test runs under en-US
[mono.git] / mcs / class / referencesource / mscorlib / system / io / filesystemenumerable.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 /*============================================================
7 **
8 ** Class:  FileSystemEnumerable
9 ** 
10 ** <OWNER>[....]</OWNER>
11 **
12 **
13 ** Purpose: Enumerates files and dirs
14 **
15 ===========================================================*/
16
17 using System;
18 using System.Collections;
19 using System.Collections.Generic;
20 using System.Security;
21 using System.Security.Permissions;
22 using Microsoft.Win32;
23 using Microsoft.Win32.SafeHandles;
24 using System.Text;
25 using System.Runtime.InteropServices;
26 using System.Globalization;
27 using System.Runtime.Versioning;
28 using System.Diagnostics.Contracts;
29 using System.Threading;
30
31 namespace System.IO
32 {
33
34     // Overview:
35     // The key methods instantiate FileSystemEnumerableIterators. These compose the iterator with search result
36     // handlers that instantiate the FileInfo, DirectoryInfo, String, etc. The handlers then perform any
37     // additional required permission demands. 
38     internal static class FileSystemEnumerableFactory
39     {
40         internal static IEnumerable<String> CreateFileNameIterator(String path, String originalUserPath, String searchPattern,
41                                                                     bool includeFiles, bool includeDirs, SearchOption searchOption, bool checkHost)
42         {
43             Contract.Requires(path != null);
44             Contract.Requires(originalUserPath != null);
45             Contract.Requires(searchPattern != null);
46
47             SearchResultHandler<String> handler = new StringResultHandler(includeFiles, includeDirs);
48             return new FileSystemEnumerableIterator<String>(path, originalUserPath, searchPattern, searchOption, handler, checkHost);
49         }
50
51         internal static IEnumerable<FileInfo> CreateFileInfoIterator(String path, String originalUserPath, String searchPattern, SearchOption searchOption)
52         {
53             Contract.Requires(path != null);
54             Contract.Requires(originalUserPath != null);
55             Contract.Requires(searchPattern != null);
56
57             SearchResultHandler<FileInfo> handler = new FileInfoResultHandler();
58             return new FileSystemEnumerableIterator<FileInfo>(path, originalUserPath, searchPattern, searchOption, handler, true);
59         }
60
61         internal static IEnumerable<DirectoryInfo> CreateDirectoryInfoIterator(String path, String originalUserPath, String searchPattern, SearchOption searchOption)
62         {
63
64             Contract.Requires(path != null);
65             Contract.Requires(originalUserPath != null);
66             Contract.Requires(searchPattern != null);
67
68             SearchResultHandler<DirectoryInfo> handler = new DirectoryInfoResultHandler();
69             return new FileSystemEnumerableIterator<DirectoryInfo>(path, originalUserPath, searchPattern, searchOption, handler, true);
70         }
71
72         internal static IEnumerable<FileSystemInfo> CreateFileSystemInfoIterator(String path, String originalUserPath, String searchPattern, SearchOption searchOption)
73         {
74             Contract.Requires(path != null);
75             Contract.Requires(originalUserPath != null);
76             Contract.Requires(searchPattern != null);
77
78             SearchResultHandler<FileSystemInfo> handler = new FileSystemInfoResultHandler();
79             return new FileSystemEnumerableIterator<FileSystemInfo>(path, originalUserPath, searchPattern, searchOption, handler, true);
80         }
81     }
82
83     // Abstract Iterator, borrowed from Linq. Used in anticipation of need for similar enumerables
84     // in the future
85     abstract internal class Iterator<TSource> : IEnumerable<TSource>, IEnumerator<TSource>
86     {
87         int threadId;
88         internal int state;
89         internal TSource current;
90
91         public Iterator()
92         {
93             threadId = Thread.CurrentThread.ManagedThreadId;
94         }
95
96         public TSource Current
97         {
98             get { return current; }
99         }
100
101         protected abstract Iterator<TSource> Clone();
102
103         public void Dispose()
104         {
105             Dispose(true);
106             GC.SuppressFinalize(this);
107         }
108
109         protected virtual void Dispose(bool disposing)
110         {
111             current = default(TSource);
112             state = -1;
113         }
114
115         public IEnumerator<TSource> GetEnumerator()
116         {
117             if (threadId == Thread.CurrentThread.ManagedThreadId && state == 0)
118             {
119                 state = 1;
120                 return this;
121             }
122
123             Iterator<TSource> duplicate = Clone();
124             duplicate.state = 1;
125             return duplicate;
126         }
127
128         public abstract bool MoveNext();
129
130         object IEnumerator.Current
131         {
132             get { return Current; }
133         }
134
135         IEnumerator IEnumerable.GetEnumerator()
136         {
137             return GetEnumerator();
138         }
139
140         void IEnumerator.Reset()
141         {
142             throw new NotSupportedException();
143         }
144
145     }
146
147     // Overview:
148     // Enumerates file system entries matching the search parameters. For recursive searches this
149     // searches through all the sub dirs and executes the search criteria against every dir.
150     // 
151     // Generic implementation:
152     // FileSystemEnumerableIterator is generic. When it gets a WIN32_FIND_DATA, it calls the 
153     // result handler to create an instance of the generic type. 
154     // 
155     // Usage:
156     // Use FileSystemEnumerableFactory to obtain FSEnumerables that can enumerate file system 
157     // entries as String path names, FileInfos, DirectoryInfos, or FileSystemInfos.
158     // 
159     // Security:
160     // For all the dirs/files returned, demands path discovery permission for their parent folders
161     internal class FileSystemEnumerableIterator<TSource> : Iterator<TSource>
162     {
163
164         private const int STATE_INIT = 1;
165         private const int STATE_SEARCH_NEXT_DIR = 2;
166         private const int STATE_FIND_NEXT_FILE = 3;
167         private const int STATE_FINISH = 4;
168
169         private SearchResultHandler<TSource> _resultHandler;
170         private List<Directory.SearchData> searchStack;
171         private Directory.SearchData searchData;
172         private String searchCriteria;
173         [System.Security.SecurityCritical]
174         SafeFindHandle _hnd = null;
175         bool needsParentPathDiscoveryDemand;
176
177         // empty means we know in advance that we won\92t find any search results, which can happen if:
178         // 1. we don\92t have a search pattern
179         // 2. we\92re enumerating only the top directory and found no matches during the first call
180         // This flag allows us to return early for these cases. We can\92t know this in advance for
181         // SearchOption.AllDirectories because we do a \93*\94 search for subdirs and then use the
182         // searchPattern at each directory level.
183         bool empty;
184
185         private String userPath;
186         private SearchOption searchOption;
187         private String fullPath;
188         private String normalizedSearchPath;
189         private int oldMode;
190         private bool _checkHost;
191
192         [System.Security.SecuritySafeCritical]
193         internal FileSystemEnumerableIterator(String path, String originalUserPath, String searchPattern, SearchOption searchOption, SearchResultHandler<TSource> resultHandler, bool checkHost)
194         {
195             Contract.Requires(path != null);
196             Contract.Requires(originalUserPath != null);
197             Contract.Requires(searchPattern != null);
198             Contract.Requires(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly);
199             Contract.Requires(resultHandler != null);
200
201             oldMode = Win32Native.SetErrorMode(Win32Native.SEM_FAILCRITICALERRORS);
202
203             searchStack = new List<Directory.SearchData>();
204
205             String normalizedSearchPattern = NormalizeSearchPattern(searchPattern);
206
207             if (normalizedSearchPattern.Length == 0)
208             {
209                 empty = true;
210             }
211             else
212             {
213                 _resultHandler = resultHandler;
214                 this.searchOption = searchOption;
215
216                 fullPath = Path.GetFullPathInternal(path);
217                 String fullSearchString = GetFullSearchString(fullPath, normalizedSearchPattern);
218                 normalizedSearchPath = Path.GetDirectoryName(fullSearchString);
219
220                 // permission demands
221                 String[] demandPaths = new String[2];
222                 // Any illegal chars such as *, ? will be caught by FileIOPermission.HasIllegalCharacters
223                 demandPaths[0] = Directory.GetDemandDir(fullPath, true);
224                 // For filters like foo\*.cs we need to verify if the directory foo is not denied access.
225                 // Do a demand on the combined path so that we can fail early in case of deny
226                 demandPaths[1] = Directory.GetDemandDir(normalizedSearchPath, true);
227                 _checkHost = checkHost;
228 #if FEATURE_CORECLR
229                 if (checkHost)
230                 {
231                     FileSecurityState state1 = new FileSecurityState(FileSecurityStateAccess.PathDiscovery, String.Empty, demandPaths[0]);
232                     state1.EnsureState();
233                     FileSecurityState state2 = new FileSecurityState(FileSecurityStateAccess.PathDiscovery, String.Empty, demandPaths[1]);
234                     state2.EnsureState();
235                 }
236 #else
237                 FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery, demandPaths, false, false);
238 #endif
239
240                 // normalize search criteria
241                 searchCriteria = GetNormalizedSearchCriteria(fullSearchString, normalizedSearchPath);
242
243                 // fix up user path
244                 String searchPatternDirName = Path.GetDirectoryName(normalizedSearchPattern);
245                 String userPathTemp = originalUserPath;
246                 if (searchPatternDirName != null && searchPatternDirName.Length != 0)
247                 {
248                     userPathTemp = Path.Combine(userPathTemp, searchPatternDirName);
249                 }
250                 this.userPath = userPathTemp;
251
252                 searchData = new Directory.SearchData(normalizedSearchPath, this.userPath, searchOption);
253
254                 CommonInit();
255             }
256
257         }
258
259         [System.Security.SecurityCritical]
260         private void CommonInit()
261         {
262             Contract.Assert(searchCriteria != null && searchData != null, "searchCriteria and searchData should be initialized");
263
264             // Execute searchCriteria against the current directory
265             String searchPath = Path.InternalCombine(searchData.fullPath, searchCriteria);
266
267             Win32Native.WIN32_FIND_DATA data = new Win32Native.WIN32_FIND_DATA();
268
269             // Open a Find handle
270             _hnd = Win32Native.FindFirstFile(searchPath, data);
271
272             if (_hnd.IsInvalid)
273             {
274                 int hr = Marshal.GetLastWin32Error();
275                 if (hr != Win32Native.ERROR_FILE_NOT_FOUND && hr != Win32Native.ERROR_NO_MORE_FILES)
276                 {
277                     HandleError(hr, searchData.fullPath);
278                 }
279                 else
280                 {
281                     // flag this as empty only if we're searching just top directory
282                     // Used in fast path for top directory only
283                     empty = searchData.searchOption == SearchOption.TopDirectoryOnly;
284                 }
285             }
286             // fast path for TopDirectoryOnly. If we have a result, go ahead and set it to 
287             // current. If empty, dispose handle.
288             if (searchData.searchOption == SearchOption.TopDirectoryOnly)
289             {
290                 if (empty)
291                 {
292                     _hnd.Dispose();
293                 }
294                 else
295                 {
296                     SearchResult searchResult = CreateSearchResult(searchData, data);
297                     if (_resultHandler.IsResultIncluded(searchResult))
298                     {
299                         current = _resultHandler.CreateObject(searchResult);
300                     }
301                 }
302             }
303             // for AllDirectories, we first recurse into dirs, so cleanup and add searchData 
304             // to the stack
305             else
306             {
307                 _hnd.Dispose();
308                 searchStack.Add(searchData);
309             }
310         }
311
312         [System.Security.SecuritySafeCritical]
313         private FileSystemEnumerableIterator(String fullPath, String normalizedSearchPath, String searchCriteria, String userPath, SearchOption searchOption, SearchResultHandler<TSource> resultHandler, bool checkHost)
314         {
315             this.fullPath = fullPath;
316             this.normalizedSearchPath = normalizedSearchPath;
317             this.searchCriteria = searchCriteria;
318             this._resultHandler = resultHandler;
319             this.userPath = userPath;
320             this.searchOption = searchOption;
321             this._checkHost = checkHost;
322
323             searchStack = new List<Directory.SearchData>();
324
325             if (searchCriteria != null)
326             {
327                 // permission demands
328                 String[] demandPaths = new String[2];
329                 // Any illegal chars such as *, ? will be caught by FileIOPermission.HasIllegalCharacters
330                 demandPaths[0] = Directory.GetDemandDir(fullPath, true);
331                 // For filters like foo\*.cs we need to verify if the directory foo is not denied access.
332                 // Do a demand on the combined path so that we can fail early in case of deny
333                 demandPaths[1] = Directory.GetDemandDir(normalizedSearchPath, true);
334 #if FEATURE_CORECLR
335                 if (checkHost) 
336                 {
337                     FileSecurityState state1 = new FileSecurityState(FileSecurityStateAccess.PathDiscovery, String.Empty, demandPaths[0]);
338                     state1.EnsureState();
339                     FileSecurityState state2 = new FileSecurityState(FileSecurityStateAccess.PathDiscovery, String.Empty, demandPaths[1]);
340                     state2.EnsureState();
341                 }
342 #else
343                 FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery, demandPaths, false, false);
344 #endif
345                 searchData = new Directory.SearchData(normalizedSearchPath, userPath, searchOption);
346                 CommonInit();
347             }
348             else
349             {
350                 empty = true;
351             }
352         }
353
354         protected override Iterator<TSource> Clone()
355         {
356             return new FileSystemEnumerableIterator<TSource>(fullPath, normalizedSearchPath, searchCriteria, userPath, searchOption, _resultHandler, _checkHost);
357         }
358
359         [System.Security.SecuritySafeCritical]
360         protected override void Dispose(bool disposing)
361         {
362             try
363             {
364                 if (_hnd != null)
365                 {
366                     _hnd.Dispose();
367                 }
368             }
369             finally
370             {
371                 Win32Native.SetErrorMode(oldMode);
372                 base.Dispose(disposing);
373             }
374         }
375
376         [System.Security.SecuritySafeCritical]
377         public override bool MoveNext()
378         {
379             Win32Native.WIN32_FIND_DATA data = new Win32Native.WIN32_FIND_DATA();
380             switch (state)
381             {
382                 case STATE_INIT:
383                     {
384                         if (empty)
385                         {
386                             state = STATE_FINISH;
387                             goto case STATE_FINISH;
388                         }
389                         if (searchData.searchOption == SearchOption.TopDirectoryOnly)
390                         {
391                             state = STATE_FIND_NEXT_FILE;
392                             if (current != null)
393                             {
394                                 return true;
395                             }
396                             else
397                             {
398                                 goto case STATE_FIND_NEXT_FILE;
399                             }
400                         }
401                         else
402                         {
403                             state = STATE_SEARCH_NEXT_DIR;
404                             goto case STATE_SEARCH_NEXT_DIR;
405                         }
406                     }
407                 case STATE_SEARCH_NEXT_DIR:
408                     {
409                         Contract.Assert(searchData.searchOption != SearchOption.TopDirectoryOnly, "should not reach this code path if searchOption == TopDirectoryOnly");
410                         // Traverse directory structure. We need to get '*'
411                         while (searchStack.Count > 0)
412                         {
413                             searchData = searchStack[0];
414                             Contract.Assert((searchData.fullPath != null), "fullpath can't be null!");
415                             searchStack.RemoveAt(0);
416
417                             // Traverse the subdirs
418                             AddSearchableDirsToStack(searchData);
419
420                             // Execute searchCriteria against the current directory
421                             String searchPath = Path.InternalCombine(searchData.fullPath, searchCriteria);
422
423                             // Open a Find handle
424                             _hnd = Win32Native.FindFirstFile(searchPath, data);
425                             if (_hnd.IsInvalid)
426                             {
427                                 int hr = Marshal.GetLastWin32Error();
428                                 if (hr == Win32Native.ERROR_FILE_NOT_FOUND || hr == Win32Native.ERROR_NO_MORE_FILES || hr == Win32Native.ERROR_PATH_NOT_FOUND)
429                                     continue;
430
431                                 _hnd.Dispose();
432                                 HandleError(hr, searchData.fullPath);
433                             }
434
435                             state = STATE_FIND_NEXT_FILE;
436                             needsParentPathDiscoveryDemand = true;
437                             SearchResult searchResult = CreateSearchResult(searchData, data);
438                             if (_resultHandler.IsResultIncluded(searchResult))
439                             {
440                                 if (needsParentPathDiscoveryDemand)
441                                 {
442                                     DoDemand(searchData.fullPath);
443                                     needsParentPathDiscoveryDemand = false;
444                                 }
445                                 current = _resultHandler.CreateObject(searchResult);
446                                 return true;
447                             }
448                             else
449                             {
450                                 goto case STATE_FIND_NEXT_FILE;
451                             }
452                         }
453                         state = STATE_FINISH;
454                         goto case STATE_FINISH;
455                     }
456                 case STATE_FIND_NEXT_FILE:
457                     {
458                         if (searchData != null && _hnd != null)
459                         {
460                             // Keep asking for more matching files/dirs, add it to the list 
461                             while (Win32Native.FindNextFile(_hnd, data))
462                             {
463                                 SearchResult searchResult = CreateSearchResult(searchData, data);
464                                 if (_resultHandler.IsResultIncluded(searchResult))
465                                 {
466                                     if (needsParentPathDiscoveryDemand)
467                                     {
468                                         DoDemand(searchData.fullPath);
469                                         needsParentPathDiscoveryDemand = false;
470                                     }
471                                     current = _resultHandler.CreateObject(searchResult);
472                                     return true;
473                                 }
474                             }
475
476                             // Make sure we quit with a sensible error.
477                             int hr = Marshal.GetLastWin32Error();
478
479                             if (_hnd != null)
480                                 _hnd.Dispose();
481
482                             // ERROR_FILE_NOT_FOUND is valid here because if the top level
483                             // dir doen't contain any subdirs and matching files then 
484                             // we will get here with this errorcode from the searchStack walk
485                             if ((hr != 0) && (hr != Win32Native.ERROR_NO_MORE_FILES)
486                                 && (hr != Win32Native.ERROR_FILE_NOT_FOUND))
487                             {
488                                 HandleError(hr, searchData.fullPath);
489                             }
490                         }
491                         if (searchData.searchOption == SearchOption.TopDirectoryOnly)
492                         {
493                             state = STATE_FINISH;
494                             goto case STATE_FINISH;
495                         }
496                         else
497                         {
498                             state = STATE_SEARCH_NEXT_DIR;
499                             goto case STATE_SEARCH_NEXT_DIR;
500                         }
501                     }
502                 case STATE_FINISH:
503                     {
504                         Dispose();
505                         break;
506                     }
507             }
508             return false;
509         }
510
511         [System.Security.SecurityCritical]
512         private SearchResult CreateSearchResult(Directory.SearchData localSearchData, Win32Native.WIN32_FIND_DATA findData)
513         {
514             String userPathFinal = Path.InternalCombine(localSearchData.userPath, findData.cFileName);
515             String fullPathFinal = Path.InternalCombine(localSearchData.fullPath, findData.cFileName);
516             return new SearchResult(fullPathFinal, userPathFinal, findData);
517         }
518
519         [System.Security.SecurityCritical]
520         private void HandleError(int hr, String path)
521         {
522             Dispose();
523             __Error.WinIOError(hr, path);
524         }
525
526         [System.Security.SecurityCritical]  // auto-generated
527         private void AddSearchableDirsToStack(Directory.SearchData localSearchData)
528         {
529             Contract.Requires(localSearchData != null);
530
531             String searchPath = Path.InternalCombine(localSearchData.fullPath, "*");
532             SafeFindHandle hnd = null;
533             Win32Native.WIN32_FIND_DATA data = new Win32Native.WIN32_FIND_DATA();
534             try
535             {
536                 // Get all files and dirs
537                 hnd = Win32Native.FindFirstFile(searchPath, data);
538
539                 if (hnd.IsInvalid)
540                 {
541                     int hr = Marshal.GetLastWin32Error();
542
543                     // This could happen if the dir doesn't contain any files.
544                     // Continue with the recursive search though, eventually
545                     // searchStack will become empty
546                     if (hr == Win32Native.ERROR_FILE_NOT_FOUND || hr == Win32Native.ERROR_NO_MORE_FILES || hr == Win32Native.ERROR_PATH_NOT_FOUND)
547                         return;
548
549                     HandleError(hr, localSearchData.fullPath);
550                 }
551
552                 // Add subdirs to searchStack. Exempt ReparsePoints as appropriate
553                 int incr = 0;
554                 do
555                 {
556                     if (FileSystemEnumerableHelpers.IsDir(data))
557                     {
558                         String tempFullPath = Path.InternalCombine(localSearchData.fullPath, data.cFileName);
559                         String tempUserPath = Path.InternalCombine(localSearchData.userPath, data.cFileName);
560
561                         SearchOption option = localSearchData.searchOption;
562
563 #if EXCLUDE_REPARSEPOINTS
564                         // Traverse reparse points depending on the searchoption specified
565                         if ((searchDataSubDir.searchOption == SearchOption.AllDirectories) && (0 != (data.dwFileAttributes & Win32Native.FILE_ATTRIBUTE_REPARSE_POINT)))
566                             option = SearchOption.TopDirectoryOnly; 
567 #endif
568                         // Setup search data for the sub directory and push it into the stack
569                         Directory.SearchData searchDataSubDir = new Directory.SearchData(tempFullPath, tempUserPath, option);
570
571                         searchStack.Insert(incr++, searchDataSubDir);
572                     }
573                 } while (Win32Native.FindNextFile(hnd, data));
574                 // We don't care about errors here
575             }
576             finally
577             {
578                 if (hnd != null)
579                     hnd.Dispose();
580             }
581         }
582
583         [System.Security.SecurityCritical]
584         internal void DoDemand(String fullPathToDemand)
585         {
586 #if FEATURE_CORECLR
587             if(_checkHost) {
588                 String demandDir = Directory.GetDemandDir(fullPathToDemand, true);
589                 FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.PathDiscovery, String.Empty, demandDir);
590                 state.EnsureState();
591             }
592 #else
593             String demandDir = Directory.GetDemandDir(fullPathToDemand, true);
594             FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery, demandDir, false, false);
595 #endif
596         }
597
598         private static String NormalizeSearchPattern(String searchPattern)
599         {
600             Contract.Requires(searchPattern != null);
601
602             // Win32 normalization trims only U+0020. 
603             String tempSearchPattern = searchPattern.TrimEnd(Path.TrimEndChars);
604
605             // Make this corner case more useful, like dir
606             if (tempSearchPattern.Equals("."))
607             {
608                 tempSearchPattern = "*";
609             }
610
611             Path.CheckSearchPattern(tempSearchPattern);
612             return tempSearchPattern;
613         }
614
615         private static String GetNormalizedSearchCriteria(String fullSearchString, String fullPathMod)
616         {
617             Contract.Requires(fullSearchString != null);
618             Contract.Requires(fullPathMod != null);
619             Contract.Requires(fullSearchString.Length >= fullPathMod.Length);
620
621             String searchCriteria = null;
622             char lastChar = fullPathMod[fullPathMod.Length - 1];
623             if (Path.IsDirectorySeparator(lastChar))
624             {
625                 // Can happen if the path is C:\temp, in which case GetDirectoryName would return C:\
626                 searchCriteria = fullSearchString.Substring(fullPathMod.Length);
627             }
628             else
629             {
630                 Contract.Assert(fullSearchString.Length > fullPathMod.Length);
631                 searchCriteria = fullSearchString.Substring(fullPathMod.Length + 1);
632             }
633             return searchCriteria;
634         }
635
636         private static String GetFullSearchString(String fullPath, String searchPattern)
637         {
638             Contract.Requires(fullPath != null);
639             Contract.Requires(searchPattern != null);
640
641             String tempStr = Path.InternalCombine(fullPath, searchPattern);
642
643             // If path ends in a trailing slash (\), append a * or we'll get a "Cannot find the file specified" exception
644             char lastChar = tempStr[tempStr.Length - 1];
645             if (Path.IsDirectorySeparator(lastChar) || lastChar == Path.VolumeSeparatorChar)
646             {
647                 tempStr = tempStr + '*';
648             }
649
650             return tempStr;
651         }
652     }
653
654     internal abstract class SearchResultHandler<TSource>
655     {
656
657         [System.Security.SecurityCritical]
658         internal abstract bool IsResultIncluded(SearchResult result);
659
660         [System.Security.SecurityCritical]
661         internal abstract TSource CreateObject(SearchResult result);
662
663     }
664
665     internal class StringResultHandler : SearchResultHandler<String>
666     {
667         private bool _includeFiles;
668         private bool _includeDirs;
669
670         internal StringResultHandler(bool includeFiles, bool includeDirs)
671         {
672             _includeFiles = includeFiles;
673             _includeDirs = includeDirs;
674         }
675
676         [System.Security.SecurityCritical]
677         internal override bool IsResultIncluded(SearchResult result)
678         {
679             bool includeFile = _includeFiles && FileSystemEnumerableHelpers.IsFile(result.FindData);
680             bool includeDir = _includeDirs && FileSystemEnumerableHelpers.IsDir(result.FindData);
681             Contract.Assert(!(includeFile && includeDir), result.FindData.cFileName + ": current item can't be both file and dir!");
682             return (includeFile || includeDir);
683         }
684
685         [System.Security.SecurityCritical]
686         internal override String CreateObject(SearchResult result)
687         {
688             return result.UserPath;
689         }
690     }
691
692     internal class FileInfoResultHandler : SearchResultHandler<FileInfo>
693     {
694         [System.Security.SecurityCritical]
695         internal override bool IsResultIncluded(SearchResult result)
696         {
697             return FileSystemEnumerableHelpers.IsFile(result.FindData);
698         }
699
700         [System.Security.SecurityCritical]
701         internal override FileInfo CreateObject(SearchResult result)
702         {
703             String name = result.FullPath;
704 #if FEATURE_CORECLR
705             FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Read, String.Empty, name);
706             state.EnsureState();
707 #else
708             FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, name, false, false);
709 #endif
710             FileInfo fi = new FileInfo(name, false);
711             fi.InitializeFrom(result.FindData);
712             return fi;
713         }
714     }
715
716     internal class DirectoryInfoResultHandler : SearchResultHandler<DirectoryInfo>
717     {
718         [System.Security.SecurityCritical]
719         internal override bool IsResultIncluded(SearchResult result)
720         {
721             return FileSystemEnumerableHelpers.IsDir(result.FindData);
722         }
723
724         [System.Security.SecurityCritical]
725         internal override DirectoryInfo CreateObject(SearchResult result)
726         {
727             String name = result.FullPath;
728             String permissionName = name + "\\.";
729             
730 #if FEATURE_CORECLR
731             FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Read, String.Empty, permissionName);
732             state.EnsureState();
733 #else
734             FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, permissionName, false, false);
735 #endif
736             DirectoryInfo di = new DirectoryInfo(name, false);
737             di.InitializeFrom(result.FindData);
738             return di;
739         }
740     }
741
742     internal class FileSystemInfoResultHandler : SearchResultHandler<FileSystemInfo>
743     {
744
745         [System.Security.SecurityCritical]
746         internal override bool IsResultIncluded(SearchResult result)
747         {
748             bool includeFile = FileSystemEnumerableHelpers.IsFile(result.FindData);
749             bool includeDir = FileSystemEnumerableHelpers.IsDir(result.FindData);
750             Contract.Assert(!(includeFile && includeDir), result.FindData.cFileName + ": current item can't be both file and dir!");
751
752             return (includeDir || includeFile);
753         }
754
755         [System.Security.SecurityCritical]
756         internal override FileSystemInfo CreateObject(SearchResult result)
757         {
758             bool isFile = FileSystemEnumerableHelpers.IsFile(result.FindData);
759             bool isDir = FileSystemEnumerableHelpers.IsDir(result.FindData);
760
761             if (isDir)
762             {
763                 String name = result.FullPath;
764                 String permissionName = name + "\\.";
765
766 #if FEATURE_CORECLR
767                 FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Read, String.Empty, permissionName);
768                 state.EnsureState();
769 #else
770                 FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, permissionName, false, false);
771 #endif
772                 DirectoryInfo di = new DirectoryInfo(name, false);
773                 di.InitializeFrom(result.FindData);
774                 return di;
775             }
776             else
777             {
778                 Contract.Assert(isFile);
779                 String name = result.FullPath;
780
781 #if FEATURE_CORECLR
782                 FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Read, String.Empty, name);
783                 state.EnsureState();
784 #else
785                 FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, name, false, false);
786 #endif
787                 FileInfo fi = new FileInfo(name, false);
788                 fi.InitializeFrom(result.FindData);
789                 return fi;
790             }
791         }
792
793     }
794
795     internal sealed class SearchResult
796     {
797         private String fullPath;     // fully-qualifed path
798         private String userPath;     // user-specified path
799         [System.Security.SecurityCritical]
800         private Win32Native.WIN32_FIND_DATA findData;
801
802         [System.Security.SecurityCritical]
803         internal SearchResult(String fullPath, String userPath, Win32Native.WIN32_FIND_DATA findData)
804         {
805             Contract.Requires(fullPath != null);
806             Contract.Requires(userPath != null);
807
808             this.fullPath = fullPath;
809             this.userPath = userPath;
810             this.findData = findData;
811         }
812
813         internal String FullPath
814         {
815             get { return fullPath; }
816         }
817
818         internal String UserPath
819         {
820             get { return userPath; }
821         }
822
823         internal Win32Native.WIN32_FIND_DATA FindData
824         {
825             [System.Security.SecurityCritical]
826             get { return findData; }
827         }
828
829     }
830
831     internal static class FileSystemEnumerableHelpers
832     {
833         [System.Security.SecurityCritical]  // auto-generated
834         internal static bool IsDir(Win32Native.WIN32_FIND_DATA data)
835         {
836             // Don't add "." nor ".."
837             return (0 != (data.dwFileAttributes & Win32Native.FILE_ATTRIBUTE_DIRECTORY))
838                                                 && !data.cFileName.Equals(".") && !data.cFileName.Equals("..");
839         }
840
841         [System.Security.SecurityCritical]  // auto-generated
842         internal static bool IsFile(Win32Native.WIN32_FIND_DATA data)
843         {
844             return 0 == (data.dwFileAttributes & Win32Native.FILE_ATTRIBUTE_DIRECTORY);
845         }
846
847     }
848 }
849