// ==++== // // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== /*============================================================ ** ** Class: FileSystemEnumerable ** ** Microsoft ** ** ** Purpose: Enumerates files and dirs ** ===========================================================*/ using System; using System.Collections; using System.Collections.Generic; using System.Security; using System.Security.Permissions; using Microsoft.Win32; using Microsoft.Win32.SafeHandles; using System.Text; using System.Runtime.InteropServices; using System.Globalization; using System.Runtime.Versioning; using System.Diagnostics.Contracts; using System.Threading; namespace System.IO { // Overview: // The key methods instantiate FileSystemEnumerableIterators. These compose the iterator with search result // handlers that instantiate the FileInfo, DirectoryInfo, String, etc. The handlers then perform any // additional required permission demands. internal static class FileSystemEnumerableFactory { internal static IEnumerable CreateFileNameIterator(String path, String originalUserPath, String searchPattern, bool includeFiles, bool includeDirs, SearchOption searchOption, bool checkHost) { Contract.Requires(path != null); Contract.Requires(originalUserPath != null); Contract.Requires(searchPattern != null); SearchResultHandler handler = new StringResultHandler(includeFiles, includeDirs); return new FileSystemEnumerableIterator(path, originalUserPath, searchPattern, searchOption, handler, checkHost); } internal static IEnumerable CreateFileInfoIterator(String path, String originalUserPath, String searchPattern, SearchOption searchOption) { Contract.Requires(path != null); Contract.Requires(originalUserPath != null); Contract.Requires(searchPattern != null); SearchResultHandler handler = new FileInfoResultHandler(); return new FileSystemEnumerableIterator(path, originalUserPath, searchPattern, searchOption, handler, true); } internal static IEnumerable CreateDirectoryInfoIterator(String path, String originalUserPath, String searchPattern, SearchOption searchOption) { Contract.Requires(path != null); Contract.Requires(originalUserPath != null); Contract.Requires(searchPattern != null); SearchResultHandler handler = new DirectoryInfoResultHandler(); return new FileSystemEnumerableIterator(path, originalUserPath, searchPattern, searchOption, handler, true); } internal static IEnumerable CreateFileSystemInfoIterator(String path, String originalUserPath, String searchPattern, SearchOption searchOption) { Contract.Requires(path != null); Contract.Requires(originalUserPath != null); Contract.Requires(searchPattern != null); SearchResultHandler handler = new FileSystemInfoResultHandler(); return new FileSystemEnumerableIterator(path, originalUserPath, searchPattern, searchOption, handler, true); } } // Abstract Iterator, borrowed from Linq. Used in anticipation of need for similar enumerables // in the future abstract internal class Iterator : IEnumerable, IEnumerator { int threadId; internal int state; internal TSource current; public Iterator() { threadId = Thread.CurrentThread.ManagedThreadId; } public TSource Current { get { return current; } } protected abstract Iterator Clone(); public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { current = default(TSource); state = -1; } public IEnumerator GetEnumerator() { if (threadId == Thread.CurrentThread.ManagedThreadId && state == 0) { state = 1; return this; } Iterator duplicate = Clone(); duplicate.state = 1; return duplicate; } public abstract bool MoveNext(); object IEnumerator.Current { get { return Current; } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } void IEnumerator.Reset() { throw new NotSupportedException(); } } // Overview: // Enumerates file system entries matching the search parameters. For recursive searches this // searches through all the sub dirs and executes the search criteria against every dir. // // Generic implementation: // FileSystemEnumerableIterator is generic. When it gets a WIN32_FIND_DATA, it calls the // result handler to create an instance of the generic type. // // Usage: // Use FileSystemEnumerableFactory to obtain FSEnumerables that can enumerate file system // entries as String path names, FileInfos, DirectoryInfos, or FileSystemInfos. // // Security: // For all the dirs/files returned, demands path discovery permission for their parent folders internal class FileSystemEnumerableIterator : Iterator { private const int STATE_INIT = 1; private const int STATE_SEARCH_NEXT_DIR = 2; private const int STATE_FIND_NEXT_FILE = 3; private const int STATE_FINISH = 4; private SearchResultHandler _resultHandler; private List searchStack; private Directory.SearchData searchData; private String searchCriteria; [System.Security.SecurityCritical] SafeFindHandle _hnd = null; bool needsParentPathDiscoveryDemand; // empty means we know in advance that we won't find any search results, which can happen if: // 1. we don't have a search pattern // 2. we're enumerating only the top directory and found no matches during the first call // This flag allows us to return early for these cases. We can't know this in advance for // SearchOption.AllDirectories because we do a "*" search for subdirs and then use the // searchPattern at each directory level. bool empty; private String userPath; private SearchOption searchOption; private String fullPath; private String normalizedSearchPath; #if !MONO private int oldMode; #endif private bool _checkHost; [System.Security.SecuritySafeCritical] internal FileSystemEnumerableIterator(String path, String originalUserPath, String searchPattern, SearchOption searchOption, SearchResultHandler resultHandler, bool checkHost) { Contract.Requires(path != null); Contract.Requires(originalUserPath != null); Contract.Requires(searchPattern != null); Contract.Requires(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly); Contract.Requires(resultHandler != null); #if !MONO // TODO: check if we need this on Windows oldMode = Win32Native.SetErrorMode(Win32Native.SEM_FAILCRITICALERRORS); #endif searchStack = new List(); String normalizedSearchPattern = NormalizeSearchPattern(searchPattern); if (normalizedSearchPattern.Length == 0) { empty = true; } else { _resultHandler = resultHandler; this.searchOption = searchOption; fullPath = Path.GetFullPathInternal(path); String fullSearchString = GetFullSearchString(fullPath, normalizedSearchPattern); normalizedSearchPath = Path.GetDirectoryName(fullSearchString); // permission demands String[] demandPaths = new String[2]; #if MONO // TODO: we don't call FileIOPermission.HasIllegalCharacters on Mono since CAS is disabled #else // Any illegal chars such as *, ? will be caught by FileIOPermission.HasIllegalCharacters #endif demandPaths[0] = Directory.GetDemandDir(fullPath, true); // For filters like foo\*.cs we need to verify if the directory foo is not denied access. // Do a demand on the combined path so that we can fail early in case of deny demandPaths[1] = Directory.GetDemandDir(normalizedSearchPath, true); _checkHost = checkHost; #if MONO_FEATURE_CAS #if FEATURE_CORECLR if (checkHost) { FileSecurityState state1 = new FileSecurityState(FileSecurityStateAccess.PathDiscovery, String.Empty, demandPaths[0]); state1.EnsureState(); FileSecurityState state2 = new FileSecurityState(FileSecurityStateAccess.PathDiscovery, String.Empty, demandPaths[1]); state2.EnsureState(); } #else FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery, demandPaths, false, false); #endif #endif // normalize search criteria searchCriteria = GetNormalizedSearchCriteria(fullSearchString, normalizedSearchPath); // fix up user path String searchPatternDirName = Path.GetDirectoryName(normalizedSearchPattern); String userPathTemp = originalUserPath; if (searchPatternDirName != null && searchPatternDirName.Length != 0) { userPathTemp = Path.Combine(userPathTemp, searchPatternDirName); } this.userPath = userPathTemp; searchData = new Directory.SearchData(normalizedSearchPath, this.userPath, searchOption); CommonInit(); } } [System.Security.SecurityCritical] private void CommonInit() { Contract.Assert(searchCriteria != null && searchData != null, "searchCriteria and searchData should be initialized"); // Execute searchCriteria against the current directory String searchPath = Path.InternalCombine(searchData.fullPath, searchCriteria); Win32Native.WIN32_FIND_DATA data = new Win32Native.WIN32_FIND_DATA(); // Open a Find handle #if MONO int error; _hnd = new SafeFindHandle (MonoIO.FindFirstFile (searchPath, out data.cFileName, out data.dwFileAttributes, out error)); #else _hnd = Win32Native.FindFirstFile(searchPath, data); #endif if (_hnd.IsInvalid) { #if MONO int hr = error; #else int hr = Marshal.GetLastWin32Error(); #endif if (hr != Win32Native.ERROR_FILE_NOT_FOUND && hr != Win32Native.ERROR_NO_MORE_FILES) { HandleError(hr, searchData.fullPath); } else { // flag this as empty only if we're searching just top directory // Used in fast path for top directory only empty = searchData.searchOption == SearchOption.TopDirectoryOnly; } } // fast path for TopDirectoryOnly. If we have a result, go ahead and set it to // current. If empty, dispose handle. if (searchData.searchOption == SearchOption.TopDirectoryOnly) { if (empty) { _hnd.Dispose(); } else { SearchResult searchResult = CreateSearchResult(searchData, data); if (_resultHandler.IsResultIncluded(searchResult)) { current = _resultHandler.CreateObject(searchResult); } } } // for AllDirectories, we first recurse into dirs, so cleanup and add searchData // to the stack else { _hnd.Dispose(); searchStack.Add(searchData); } } [System.Security.SecuritySafeCritical] private FileSystemEnumerableIterator(String fullPath, String normalizedSearchPath, String searchCriteria, String userPath, SearchOption searchOption, SearchResultHandler resultHandler, bool checkHost) { this.fullPath = fullPath; this.normalizedSearchPath = normalizedSearchPath; this.searchCriteria = searchCriteria; this._resultHandler = resultHandler; this.userPath = userPath; this.searchOption = searchOption; this._checkHost = checkHost; searchStack = new List(); if (searchCriteria != null) { // permission demands String[] demandPaths = new String[2]; // Any illegal chars such as *, ? will be caught by FileIOPermission.HasIllegalCharacters demandPaths[0] = Directory.GetDemandDir(fullPath, true); // For filters like foo\*.cs we need to verify if the directory foo is not denied access. // Do a demand on the combined path so that we can fail early in case of deny demandPaths[1] = Directory.GetDemandDir(normalizedSearchPath, true); #if MONO_FEATURE_CAS #if FEATURE_CORECLR if (checkHost) { FileSecurityState state1 = new FileSecurityState(FileSecurityStateAccess.PathDiscovery, String.Empty, demandPaths[0]); state1.EnsureState(); FileSecurityState state2 = new FileSecurityState(FileSecurityStateAccess.PathDiscovery, String.Empty, demandPaths[1]); state2.EnsureState(); } #else FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery, demandPaths, false, false); #endif #endif searchData = new Directory.SearchData(normalizedSearchPath, userPath, searchOption); CommonInit(); } else { empty = true; } } protected override Iterator Clone() { return new FileSystemEnumerableIterator(fullPath, normalizedSearchPath, searchCriteria, userPath, searchOption, _resultHandler, _checkHost); } [System.Security.SecuritySafeCritical] protected override void Dispose(bool disposing) { try { if (_hnd != null) { _hnd.Dispose(); } } finally { #if !MONO // TODO: check if we need this on Windows Win32Native.SetErrorMode(oldMode); #endif base.Dispose(disposing); } } [System.Security.SecuritySafeCritical] public override bool MoveNext() { Win32Native.WIN32_FIND_DATA data = new Win32Native.WIN32_FIND_DATA(); switch (state) { case STATE_INIT: { if (empty) { state = STATE_FINISH; goto case STATE_FINISH; } if (searchData.searchOption == SearchOption.TopDirectoryOnly) { state = STATE_FIND_NEXT_FILE; if (current != null) { return true; } else { goto case STATE_FIND_NEXT_FILE; } } else { state = STATE_SEARCH_NEXT_DIR; goto case STATE_SEARCH_NEXT_DIR; } } case STATE_SEARCH_NEXT_DIR: { Contract.Assert(searchData.searchOption != SearchOption.TopDirectoryOnly, "should not reach this code path if searchOption == TopDirectoryOnly"); // Traverse directory structure. We need to get '*' while (searchStack.Count > 0) { searchData = searchStack[0]; Contract.Assert((searchData.fullPath != null), "fullpath can't be null!"); searchStack.RemoveAt(0); // Traverse the subdirs AddSearchableDirsToStack(searchData); // Execute searchCriteria against the current directory String searchPath = Path.InternalCombine(searchData.fullPath, searchCriteria); // Open a Find handle #if MONO int error; _hnd = new SafeFindHandle (MonoIO.FindFirstFile (searchPath, out data.cFileName, out data.dwFileAttributes, out error)); #else _hnd = Win32Native.FindFirstFile(searchPath, data); #endif if (_hnd.IsInvalid) { #if MONO int hr = error; #else int hr = Marshal.GetLastWin32Error(); #endif if (hr == Win32Native.ERROR_FILE_NOT_FOUND || hr == Win32Native.ERROR_NO_MORE_FILES || hr == Win32Native.ERROR_PATH_NOT_FOUND) continue; _hnd.Dispose(); HandleError(hr, searchData.fullPath); } state = STATE_FIND_NEXT_FILE; needsParentPathDiscoveryDemand = true; SearchResult searchResult = CreateSearchResult(searchData, data); if (_resultHandler.IsResultIncluded(searchResult)) { if (needsParentPathDiscoveryDemand) { DoDemand(searchData.fullPath); needsParentPathDiscoveryDemand = false; } current = _resultHandler.CreateObject(searchResult); return true; } else { goto case STATE_FIND_NEXT_FILE; } } state = STATE_FINISH; goto case STATE_FINISH; } case STATE_FIND_NEXT_FILE: { if (searchData != null && _hnd != null) { // Keep asking for more matching files/dirs, add it to the list #if MONO int error; while (MonoIO.FindNextFile (_hnd.DangerousGetHandle(), out data.cFileName, out data.dwFileAttributes, out error)) #else while (Win32Native.FindNextFile(_hnd, data)) #endif { SearchResult searchResult = CreateSearchResult(searchData, data); if (_resultHandler.IsResultIncluded(searchResult)) { if (needsParentPathDiscoveryDemand) { DoDemand(searchData.fullPath); needsParentPathDiscoveryDemand = false; } current = _resultHandler.CreateObject(searchResult); return true; } } #if MONO int hr = error; #else // Make sure we quit with a sensible error. int hr = Marshal.GetLastWin32Error(); #endif if (_hnd != null) _hnd.Dispose(); // ERROR_FILE_NOT_FOUND is valid here because if the top level // dir doen't contain any subdirs and matching files then // we will get here with this errorcode from the searchStack walk if ((hr != 0) && (hr != Win32Native.ERROR_NO_MORE_FILES) && (hr != Win32Native.ERROR_FILE_NOT_FOUND)) { HandleError(hr, searchData.fullPath); } } if (searchData.searchOption == SearchOption.TopDirectoryOnly) { state = STATE_FINISH; goto case STATE_FINISH; } else { state = STATE_SEARCH_NEXT_DIR; goto case STATE_SEARCH_NEXT_DIR; } } case STATE_FINISH: { Dispose(); break; } } return false; } [System.Security.SecurityCritical] private SearchResult CreateSearchResult(Directory.SearchData localSearchData, Win32Native.WIN32_FIND_DATA findData) { String userPathFinal = Path.InternalCombine(localSearchData.userPath, findData.cFileName); String fullPathFinal = Path.InternalCombine(localSearchData.fullPath, findData.cFileName); return new SearchResult(fullPathFinal, userPathFinal, findData); } [System.Security.SecurityCritical] private void HandleError(int hr, String path) { Dispose(); __Error.WinIOError(hr, path); } [System.Security.SecurityCritical] // auto-generated private void AddSearchableDirsToStack(Directory.SearchData localSearchData) { Contract.Requires(localSearchData != null); String searchPath = Path.InternalCombine(localSearchData.fullPath, "*"); SafeFindHandle hnd = null; Win32Native.WIN32_FIND_DATA data = new Win32Native.WIN32_FIND_DATA(); try { // Get all files and dirs #if MONO int error; hnd = new SafeFindHandle (MonoIO.FindFirstFile (searchPath, out data.cFileName, out data.dwFileAttributes, out error)); #else hnd = Win32Native.FindFirstFile(searchPath, data); #endif if (hnd.IsInvalid) { #if MONO int hr = error; #else int hr = Marshal.GetLastWin32Error(); #endif // This could happen if the dir doesn't contain any files. // Continue with the recursive search though, eventually // searchStack will become empty if (hr == Win32Native.ERROR_FILE_NOT_FOUND || hr == Win32Native.ERROR_NO_MORE_FILES || hr == Win32Native.ERROR_PATH_NOT_FOUND) return; HandleError(hr, localSearchData.fullPath); } // Add subdirs to searchStack. Exempt ReparsePoints as appropriate int incr = 0; do { if (FileSystemEnumerableHelpers.IsDir(data)) { String tempFullPath = Path.InternalCombine(localSearchData.fullPath, data.cFileName); String tempUserPath = Path.InternalCombine(localSearchData.userPath, data.cFileName); SearchOption option = localSearchData.searchOption; #if EXCLUDE_REPARSEPOINTS // Traverse reparse points depending on the searchoption specified if ((searchDataSubDir.searchOption == SearchOption.AllDirectories) && (0 != (data.dwFileAttributes & Win32Native.FILE_ATTRIBUTE_REPARSE_POINT))) option = SearchOption.TopDirectoryOnly; #endif // Setup search data for the sub directory and push it into the stack Directory.SearchData searchDataSubDir = new Directory.SearchData(tempFullPath, tempUserPath, option); searchStack.Insert(incr++, searchDataSubDir); } #if MONO } while (MonoIO.FindNextFile (hnd.DangerousGetHandle(), out data.cFileName, out data.dwFileAttributes, out error)); #else } while (Win32Native.FindNextFile(hnd, data)); #endif // We don't care about errors here } finally { if (hnd != null) hnd.Dispose(); } } [System.Security.SecurityCritical] internal void DoDemand(String fullPathToDemand) { #if MONO_FEATURE_CAS #if FEATURE_CORECLR if(_checkHost) { String demandDir = Directory.GetDemandDir(fullPathToDemand, true); FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.PathDiscovery, String.Empty, demandDir); state.EnsureState(); } #else String demandDir = Directory.GetDemandDir(fullPathToDemand, true); FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery, demandDir, false, false); #endif #endif } private static String NormalizeSearchPattern(String searchPattern) { Contract.Requires(searchPattern != null); // Win32 normalization trims only U+0020. String tempSearchPattern = searchPattern.TrimEnd(Path.TrimEndChars); // Make this corner case more useful, like dir if (tempSearchPattern.Equals(".")) { tempSearchPattern = "*"; } Path.CheckSearchPattern(tempSearchPattern); return tempSearchPattern; } private static String GetNormalizedSearchCriteria(String fullSearchString, String fullPathMod) { Contract.Requires(fullSearchString != null); Contract.Requires(fullPathMod != null); Contract.Requires(fullSearchString.Length >= fullPathMod.Length); String searchCriteria = null; char lastChar = fullPathMod[fullPathMod.Length - 1]; if (Path.IsDirectorySeparator(lastChar)) { // Can happen if the path is C:\temp, in which case GetDirectoryName would return C:\ searchCriteria = fullSearchString.Substring(fullPathMod.Length); } else { Contract.Assert(fullSearchString.Length > fullPathMod.Length); searchCriteria = fullSearchString.Substring(fullPathMod.Length + 1); } return searchCriteria; } private static String GetFullSearchString(String fullPath, String searchPattern) { Contract.Requires(fullPath != null); Contract.Requires(searchPattern != null); String tempStr = Path.InternalCombine(fullPath, searchPattern); // If path ends in a trailing slash (\), append a * or we'll get a "Cannot find the file specified" exception char lastChar = tempStr[tempStr.Length - 1]; if (Path.IsDirectorySeparator(lastChar) || lastChar == Path.VolumeSeparatorChar) { tempStr = tempStr + '*'; } return tempStr; } } internal abstract class SearchResultHandler { [System.Security.SecurityCritical] internal abstract bool IsResultIncluded(SearchResult result); [System.Security.SecurityCritical] internal abstract TSource CreateObject(SearchResult result); } internal class StringResultHandler : SearchResultHandler { private bool _includeFiles; private bool _includeDirs; internal StringResultHandler(bool includeFiles, bool includeDirs) { _includeFiles = includeFiles; _includeDirs = includeDirs; } [System.Security.SecurityCritical] internal override bool IsResultIncluded(SearchResult result) { bool includeFile = _includeFiles && FileSystemEnumerableHelpers.IsFile(result.FindData); bool includeDir = _includeDirs && FileSystemEnumerableHelpers.IsDir(result.FindData); Contract.Assert(!(includeFile && includeDir), result.FindData.cFileName + ": current item can't be both file and dir!"); return (includeFile || includeDir); } [System.Security.SecurityCritical] internal override String CreateObject(SearchResult result) { return result.UserPath; } } internal class FileInfoResultHandler : SearchResultHandler { [System.Security.SecurityCritical] internal override bool IsResultIncluded(SearchResult result) { return FileSystemEnumerableHelpers.IsFile(result.FindData); } [System.Security.SecurityCritical] internal override FileInfo CreateObject(SearchResult result) { String name = result.FullPath; #if MONO_FEATURE_CAS #if FEATURE_CORECLR FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Read, String.Empty, name); state.EnsureState(); #else FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, name, false, false); #endif #endif FileInfo fi = new FileInfo(name, false); fi.InitializeFrom(result.FindData); return fi; } } internal class DirectoryInfoResultHandler : SearchResultHandler { [System.Security.SecurityCritical] internal override bool IsResultIncluded(SearchResult result) { return FileSystemEnumerableHelpers.IsDir(result.FindData); } [System.Security.SecurityCritical] internal override DirectoryInfo CreateObject(SearchResult result) { String name = result.FullPath; #if MONO_FEATURE_CAS String permissionName = name + "\\."; #if FEATURE_CORECLR FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Read, String.Empty, permissionName); state.EnsureState(); #else FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, permissionName, false, false); #endif #endif DirectoryInfo di = new DirectoryInfo(name, false); di.InitializeFrom(result.FindData); return di; } } internal class FileSystemInfoResultHandler : SearchResultHandler { [System.Security.SecurityCritical] internal override bool IsResultIncluded(SearchResult result) { bool includeFile = FileSystemEnumerableHelpers.IsFile(result.FindData); bool includeDir = FileSystemEnumerableHelpers.IsDir(result.FindData); Contract.Assert(!(includeFile && includeDir), result.FindData.cFileName + ": current item can't be both file and dir!"); return (includeDir || includeFile); } [System.Security.SecurityCritical] internal override FileSystemInfo CreateObject(SearchResult result) { bool isFile = FileSystemEnumerableHelpers.IsFile(result.FindData); bool isDir = FileSystemEnumerableHelpers.IsDir(result.FindData); if (isDir) { String name = result.FullPath; #if MONO_FEATURE_CAS String permissionName = name + "\\."; #if FEATURE_CORECLR FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Read, String.Empty, permissionName); state.EnsureState(); #else FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, permissionName, false, false); #endif #endif DirectoryInfo di = new DirectoryInfo(name, false); di.InitializeFrom(result.FindData); return di; } else { Contract.Assert(isFile); String name = result.FullPath; #if MONO_FEATURE_CAS #if FEATURE_CORECLR FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Read, String.Empty, name); state.EnsureState(); #else FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, name, false, false); #endif #endif FileInfo fi = new FileInfo(name, false); fi.InitializeFrom(result.FindData); return fi; } } } internal sealed class SearchResult { private String fullPath; // fully-qualifed path private String userPath; // user-specified path [System.Security.SecurityCritical] private Win32Native.WIN32_FIND_DATA findData; [System.Security.SecurityCritical] internal SearchResult(String fullPath, String userPath, Win32Native.WIN32_FIND_DATA findData) { Contract.Requires(fullPath != null); Contract.Requires(userPath != null); this.fullPath = fullPath; this.userPath = userPath; this.findData = findData; } internal String FullPath { get { return fullPath; } } internal String UserPath { get { return userPath; } } internal Win32Native.WIN32_FIND_DATA FindData { [System.Security.SecurityCritical] get { return findData; } } } internal static class FileSystemEnumerableHelpers { [System.Security.SecurityCritical] // auto-generated internal static bool IsDir(Win32Native.WIN32_FIND_DATA data) { // Don't add "." nor ".." #if MONO return (0 != (data.dwFileAttributes & (int)Win32Native.FILE_ATTRIBUTE_DIRECTORY)) #else return (0 != (data.dwFileAttributes & Win32Native.FILE_ATTRIBUTE_DIRECTORY)) #endif && !data.cFileName.Equals(".") && !data.cFileName.Equals(".."); } [System.Security.SecurityCritical] // auto-generated internal static bool IsFile(Win32Native.WIN32_FIND_DATA data) { #if MONO return 0 == (data.dwFileAttributes & (int)Win32Native.FILE_ATTRIBUTE_DIRECTORY); #else return 0 == (data.dwFileAttributes & Win32Native.FILE_ATTRIBUTE_DIRECTORY); #endif } } }