PageRenderTime 63ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/src/System.IO.FileSystem/src/System/IO/Win32FileSystemEnumerable.cs

https://gitlab.com/0072016/0072016-corefx-
C# | 668 lines | 517 code | 88 blank | 63 comment | 112 complexity | 8d6b2c15470dc1565a405f81a6849765 MD5 | raw file
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. using System.Collections.Generic;
  5. using System.Diagnostics;
  6. using System.Diagnostics.Contracts;
  7. using System.Runtime.InteropServices;
  8. using System.Security;
  9. using Microsoft.Win32.SafeHandles;
  10. namespace System.IO
  11. {
  12. // Overview:
  13. // The key methods instantiate Win32FileSystemEnumerableIterators. These compose the iterator with search result
  14. // handlers that instantiate the FileInfo, DirectoryInfo, string, etc. The handlers then perform any
  15. // additional required permission demands.
  16. internal static class Win32FileSystemEnumerableFactory
  17. {
  18. internal static IEnumerable<string> CreateFileNameIterator(string path, string originalUserPath, string searchPattern,
  19. bool includeFiles, bool includeDirs, SearchOption searchOption)
  20. {
  21. Contract.Requires(path != null);
  22. Contract.Requires(originalUserPath != null);
  23. Contract.Requires(searchPattern != null);
  24. SearchResultHandler<string> handler;
  25. if (includeFiles && includeDirs)
  26. {
  27. handler = SearchResultHandler.FileSystemPath;
  28. }
  29. else if (includeFiles)
  30. {
  31. handler = SearchResultHandler.FilePath;
  32. }
  33. else
  34. {
  35. Debug.Assert(includeDirs, "Should never be excluding both files and directories.");
  36. handler = SearchResultHandler.DirectoryPath;
  37. }
  38. return new Win32FileSystemEnumerableIterator<string>(path, originalUserPath, searchPattern, searchOption, handler);
  39. }
  40. internal static IEnumerable<FileInfo> CreateFileInfoIterator(string path, string originalUserPath, string searchPattern, SearchOption searchOption)
  41. {
  42. Contract.Requires(path != null);
  43. Contract.Requires(originalUserPath != null);
  44. Contract.Requires(searchPattern != null);
  45. return new Win32FileSystemEnumerableIterator<FileInfo>(path, originalUserPath, searchPattern, searchOption, SearchResultHandler.FileInfo);
  46. }
  47. internal static IEnumerable<DirectoryInfo> CreateDirectoryInfoIterator(string path, string originalUserPath, string searchPattern, SearchOption searchOption)
  48. {
  49. Contract.Requires(path != null);
  50. Contract.Requires(originalUserPath != null);
  51. Contract.Requires(searchPattern != null);
  52. return new Win32FileSystemEnumerableIterator<DirectoryInfo>(path, originalUserPath, searchPattern, searchOption, SearchResultHandler.DirectoryInfo);
  53. }
  54. internal static IEnumerable<FileSystemInfo> CreateFileSystemInfoIterator(string path, string originalUserPath, string searchPattern, SearchOption searchOption)
  55. {
  56. Contract.Requires(path != null);
  57. Contract.Requires(originalUserPath != null);
  58. Contract.Requires(searchPattern != null);
  59. return new Win32FileSystemEnumerableIterator<FileSystemInfo>(path, originalUserPath, searchPattern, searchOption, SearchResultHandler.FileSystemInfo);
  60. }
  61. }
  62. // Overview:
  63. // Enumerates file system entries matching the search parameters. For recursive searches this
  64. // searches through all the sub dirs and executes the search criteria against every dir.
  65. //
  66. // Generic implementation:
  67. // Win32FileSystemEnumerableIterator is generic. When it gets a WIN32_FIND_DATA, it calls the
  68. // result handler to create an instance of the generic type.
  69. //
  70. // Usage:
  71. // Use Win32FileSystemEnumerableFactory to obtain FSEnumerables that can enumerate file system
  72. // entries as string path names, FileInfos, DirectoryInfos, or FileSystemInfos.
  73. //
  74. // Security:
  75. // For all the dirs/files returned, demands path discovery permission for their parent folders
  76. internal class Win32FileSystemEnumerableIterator<TSource> : Iterator<TSource>
  77. {
  78. private const int STATE_INIT = 1;
  79. private const int STATE_SEARCH_NEXT_DIR = 2;
  80. private const int STATE_FIND_NEXT_FILE = 3;
  81. private const int STATE_FINISH = 4;
  82. private readonly SearchResultHandler<TSource> _resultHandler;
  83. private List<PathPair> _searchList;
  84. private PathPair _searchData;
  85. private readonly string _searchCriteria;
  86. [SecurityCritical]
  87. private SafeFindHandle _hnd = null;
  88. // empty means we know in advance that we won?t find any search results, which can happen if:
  89. // 1. we don?t have a search pattern
  90. // 2. we?re enumerating only the top directory and found no matches during the first call
  91. // This flag allows us to return early for these cases. We can?t know this in advance for
  92. // SearchOption.AllDirectories because we do a ?*? search for subdirs and then use the
  93. // searchPattern at each directory level.
  94. private bool _empty;
  95. private readonly string _userPath;
  96. private readonly SearchOption _searchOption;
  97. private readonly string _fullPath;
  98. private readonly string _normalizedSearchPath;
  99. private readonly uint _oldMode;
  100. [SecuritySafeCritical]
  101. internal Win32FileSystemEnumerableIterator(string path, string originalUserPath, string searchPattern, SearchOption searchOption, SearchResultHandler<TSource> resultHandler)
  102. {
  103. Contract.Requires(path != null);
  104. Contract.Requires(originalUserPath != null);
  105. Contract.Requires(searchPattern != null);
  106. Contract.Requires(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly);
  107. Contract.Requires(resultHandler != null);
  108. _oldMode = Interop.mincore.SetErrorMode(Interop.mincore.SEM_FAILCRITICALERRORS);
  109. string normalizedSearchPattern = NormalizeSearchPattern(searchPattern);
  110. if (normalizedSearchPattern.Length == 0)
  111. {
  112. _empty = true;
  113. }
  114. else
  115. {
  116. _resultHandler = resultHandler;
  117. _searchOption = searchOption;
  118. _fullPath = Path.GetFullPath(path);
  119. string fullSearchString = GetFullSearchString(_fullPath, normalizedSearchPattern);
  120. _normalizedSearchPath = Path.GetDirectoryName(fullSearchString);
  121. // normalize search criteria
  122. _searchCriteria = GetNormalizedSearchCriteria(fullSearchString, _normalizedSearchPath);
  123. // fix up user path
  124. string searchPatternDirName = Path.GetDirectoryName(normalizedSearchPattern);
  125. _userPath = string.IsNullOrEmpty(searchPatternDirName) ?
  126. originalUserPath :
  127. Path.Combine(originalUserPath, searchPatternDirName);
  128. _searchData = new PathPair(_userPath, _normalizedSearchPath);
  129. CommonInit();
  130. }
  131. }
  132. [SecurityCritical]
  133. private void CommonInit()
  134. {
  135. Debug.Assert(_searchCriteria != null, "searchCriteria should be initialized");
  136. // Execute searchCriteria against the current directory
  137. PathHelpers.ThrowIfEmptyOrRootedPath(_searchCriteria);
  138. string searchPath = Path.Combine(_searchData.FullPath, _searchCriteria);
  139. Interop.mincore.WIN32_FIND_DATA data = new Interop.mincore.WIN32_FIND_DATA();
  140. // Open a Find handle
  141. _hnd = Interop.mincore.FindFirstFile(searchPath, ref data);
  142. if (_hnd.IsInvalid)
  143. {
  144. int errorCode = Marshal.GetLastWin32Error();
  145. if (errorCode != Interop.mincore.Errors.ERROR_FILE_NOT_FOUND && errorCode != Interop.mincore.Errors.ERROR_NO_MORE_FILES)
  146. {
  147. HandleError(errorCode, _searchData.FullPath);
  148. }
  149. else
  150. {
  151. // flag this as empty only if we're searching just top directory
  152. // Used in fast path for top directory only
  153. _empty = _searchOption == SearchOption.TopDirectoryOnly;
  154. }
  155. }
  156. // fast path for TopDirectoryOnly. If we have a result, go ahead and set it to
  157. // current. If empty, dispose handle.
  158. if (_searchOption == SearchOption.TopDirectoryOnly)
  159. {
  160. if (_empty)
  161. {
  162. _hnd.Dispose();
  163. }
  164. else
  165. {
  166. TSource result;
  167. if (IsResultIncluded(ref data, out result))
  168. {
  169. current = result;
  170. }
  171. }
  172. }
  173. // for AllDirectories, we first recurse into dirs, so cleanup and add searchData
  174. // to the list
  175. else
  176. {
  177. _hnd.Dispose();
  178. _searchList = new List<PathPair>();
  179. _searchList.Add(_searchData);
  180. }
  181. }
  182. [SecuritySafeCritical]
  183. private Win32FileSystemEnumerableIterator(string fullPath, string normalizedSearchPath, string searchCriteria, string userPath, SearchOption searchOption, SearchResultHandler<TSource> resultHandler)
  184. {
  185. _fullPath = fullPath;
  186. _normalizedSearchPath = normalizedSearchPath;
  187. _searchCriteria = searchCriteria;
  188. _resultHandler = resultHandler;
  189. _userPath = userPath;
  190. _searchOption = searchOption;
  191. if (searchCriteria != null)
  192. {
  193. PathInternal.CheckInvalidPathChars(fullPath);
  194. if (PathInternal.HasWildCardCharacters(fullPath))
  195. throw new ArgumentException(SR.Argument_InvalidPathChars, nameof(fullPath));
  196. _searchData = new PathPair(userPath, normalizedSearchPath);
  197. CommonInit();
  198. }
  199. else
  200. {
  201. _empty = true;
  202. }
  203. }
  204. protected override Iterator<TSource> Clone()
  205. {
  206. return new Win32FileSystemEnumerableIterator<TSource>(_fullPath, _normalizedSearchPath, _searchCriteria, _userPath, _searchOption, _resultHandler);
  207. }
  208. [SecuritySafeCritical]
  209. protected override void Dispose(bool disposing)
  210. {
  211. try
  212. {
  213. if (_hnd != null)
  214. {
  215. _hnd.Dispose();
  216. }
  217. }
  218. finally
  219. {
  220. Interop.mincore.SetErrorMode(_oldMode);
  221. base.Dispose(disposing);
  222. }
  223. }
  224. [SecuritySafeCritical]
  225. public override bool MoveNext()
  226. {
  227. Interop.mincore.WIN32_FIND_DATA data = new Interop.mincore.WIN32_FIND_DATA();
  228. switch (state)
  229. {
  230. case STATE_INIT:
  231. {
  232. if (_empty)
  233. {
  234. state = STATE_FINISH;
  235. goto case STATE_FINISH;
  236. }
  237. if (_searchOption == SearchOption.TopDirectoryOnly)
  238. {
  239. state = STATE_FIND_NEXT_FILE;
  240. if (current != null)
  241. {
  242. return true;
  243. }
  244. else
  245. {
  246. goto case STATE_FIND_NEXT_FILE;
  247. }
  248. }
  249. else
  250. {
  251. state = STATE_SEARCH_NEXT_DIR;
  252. goto case STATE_SEARCH_NEXT_DIR;
  253. }
  254. }
  255. case STATE_SEARCH_NEXT_DIR:
  256. {
  257. Debug.Assert(_searchOption != SearchOption.TopDirectoryOnly, "should not reach this code path if searchOption == TopDirectoryOnly");
  258. Debug.Assert(_searchList != null, "_searchList should not be null");
  259. // Traverse directory structure. We need to get '*'
  260. while (_searchList.Count > 0)
  261. {
  262. int index = _searchList.Count - 1;
  263. _searchData = _searchList[index];
  264. Debug.Assert((_searchData.FullPath != null), "fullpath can't be null!");
  265. _searchList.RemoveAt(index);
  266. // Traverse the subdirs
  267. AddSearchableDirsToList(_searchData);
  268. // Execute searchCriteria against the current directory
  269. string searchPath = Path.Combine(_searchData.FullPath, _searchCriteria);
  270. // Open a Find handle
  271. _hnd = Interop.mincore.FindFirstFile(searchPath, ref data);
  272. if (_hnd.IsInvalid)
  273. {
  274. int errorCode = Marshal.GetLastWin32Error();
  275. if (errorCode == Interop.mincore.Errors.ERROR_FILE_NOT_FOUND || errorCode == Interop.mincore.Errors.ERROR_NO_MORE_FILES || errorCode == Interop.mincore.Errors.ERROR_PATH_NOT_FOUND)
  276. continue;
  277. _hnd.Dispose();
  278. HandleError(errorCode, _searchData.FullPath);
  279. }
  280. state = STATE_FIND_NEXT_FILE;
  281. TSource result;
  282. if (IsResultIncluded(ref data, out result))
  283. {
  284. current = result;
  285. return true;
  286. }
  287. else
  288. {
  289. goto case STATE_FIND_NEXT_FILE;
  290. }
  291. }
  292. state = STATE_FINISH;
  293. goto case STATE_FINISH;
  294. }
  295. case STATE_FIND_NEXT_FILE:
  296. {
  297. if (_hnd != null)
  298. {
  299. // Keep asking for more matching files/dirs, add it to the list
  300. while (Interop.mincore.FindNextFile(_hnd, ref data))
  301. {
  302. TSource result;
  303. if (IsResultIncluded(ref data, out result))
  304. {
  305. current = result;
  306. return true;
  307. }
  308. }
  309. // Make sure we quit with a sensible error.
  310. int errorCode = Marshal.GetLastWin32Error();
  311. if (_hnd != null)
  312. _hnd.Dispose();
  313. // ERROR_FILE_NOT_FOUND is valid here because if the top level
  314. // dir doesn't contain any subdirs and matching files then
  315. // we will get here with this errorcode from the _searchList walk
  316. if ((errorCode != 0) && (errorCode != Interop.mincore.Errors.ERROR_NO_MORE_FILES)
  317. && (errorCode != Interop.mincore.Errors.ERROR_FILE_NOT_FOUND))
  318. {
  319. HandleError(errorCode, _searchData.FullPath);
  320. }
  321. }
  322. if (_searchOption == SearchOption.TopDirectoryOnly)
  323. {
  324. state = STATE_FINISH;
  325. goto case STATE_FINISH;
  326. }
  327. else
  328. {
  329. state = STATE_SEARCH_NEXT_DIR;
  330. goto case STATE_SEARCH_NEXT_DIR;
  331. }
  332. }
  333. case STATE_FINISH:
  334. {
  335. Dispose();
  336. break;
  337. }
  338. }
  339. return false;
  340. }
  341. [SecurityCritical]
  342. private bool IsResultIncluded(ref Interop.mincore.WIN32_FIND_DATA findData, out TSource result)
  343. {
  344. Contract.Requires(findData.cFileName.Length != 0 && !Path.IsPathRooted(findData.cFileName),
  345. "Expected file system enumeration to not have empty file/directory name and not have rooted name");
  346. return _resultHandler.IsResultIncluded(_searchData.FullPath, _searchData.UserPath, ref findData, out result);
  347. }
  348. [SecurityCritical]
  349. private void HandleError(int errorCode, string path)
  350. {
  351. Dispose();
  352. throw Win32Marshal.GetExceptionForWin32Error(errorCode, path);
  353. }
  354. [SecurityCritical] // auto-generated
  355. private void AddSearchableDirsToList(PathPair localSearchData)
  356. {
  357. string searchPath = Path.Combine(localSearchData.FullPath, "*");
  358. SafeFindHandle hnd = null;
  359. Interop.mincore.WIN32_FIND_DATA data = new Interop.mincore.WIN32_FIND_DATA();
  360. try
  361. {
  362. // Get all files and dirs
  363. hnd = Interop.mincore.FindFirstFile(searchPath, ref data);
  364. if (hnd.IsInvalid)
  365. {
  366. int errorCode = Marshal.GetLastWin32Error();
  367. // This could happen if the dir doesn't contain any files.
  368. // Continue with the recursive search though, eventually
  369. // _searchList will become empty
  370. if (errorCode == Interop.mincore.Errors.ERROR_FILE_NOT_FOUND || errorCode == Interop.mincore.Errors.ERROR_NO_MORE_FILES || errorCode == Interop.mincore.Errors.ERROR_PATH_NOT_FOUND)
  371. return;
  372. HandleError(errorCode, localSearchData.FullPath);
  373. }
  374. // Add subdirs to _searchList. Exempt ReparsePoints as appropriate
  375. int initialCount = _searchList.Count;
  376. do
  377. {
  378. if (Win32FileSystemEnumerableHelpers.IsDir(ref data))
  379. {
  380. Debug.Assert(data.cFileName.Length != 0 && !Path.IsPathRooted(data.cFileName),
  381. "Expected file system enumeration to not have empty file/directory name and not have rooted name");
  382. string tempFullPath = Path.Combine(localSearchData.FullPath, data.cFileName);
  383. string tempUserPath = Path.Combine(localSearchData.UserPath, data.cFileName);
  384. // Setup search data for the sub directory and push it into the list
  385. PathPair searchDataSubDir = new PathPair(tempUserPath, tempFullPath);
  386. Debug.Assert(_searchList != null, "_searchList should not be null");
  387. _searchList.Add(searchDataSubDir);
  388. }
  389. } while (Interop.mincore.FindNextFile(hnd, ref data));
  390. // Reverse the items just added to maintain FIFO order
  391. if (_searchList.Count > initialCount)
  392. {
  393. _searchList.Reverse(initialCount, _searchList.Count - initialCount);
  394. }
  395. // We don't care about errors here
  396. }
  397. finally
  398. {
  399. if (hnd != null)
  400. hnd.Dispose();
  401. }
  402. }
  403. private static string NormalizeSearchPattern(string searchPattern)
  404. {
  405. Contract.Requires(searchPattern != null);
  406. // Win32 normalization trims only U+0020.
  407. string tempSearchPattern = searchPattern.TrimEnd(PathHelpers.TrimEndChars);
  408. // Make this corner case more useful, like dir
  409. if (tempSearchPattern.Equals("."))
  410. {
  411. tempSearchPattern = "*";
  412. }
  413. PathHelpers.CheckSearchPattern(tempSearchPattern);
  414. return tempSearchPattern;
  415. }
  416. private static string GetNormalizedSearchCriteria(string fullSearchString, string fullPathMod)
  417. {
  418. Contract.Requires(fullSearchString != null);
  419. Contract.Requires(fullPathMod != null);
  420. Contract.Requires(fullSearchString.Length >= fullPathMod.Length);
  421. string searchCriteria = null;
  422. char lastChar = fullPathMod[fullPathMod.Length - 1];
  423. if (PathInternal.IsDirectorySeparator(lastChar))
  424. {
  425. // Can happen if the path is C:\temp, in which case GetDirectoryName would return C:\
  426. searchCriteria = fullSearchString.Substring(fullPathMod.Length);
  427. }
  428. else
  429. {
  430. Debug.Assert(fullSearchString.Length > fullPathMod.Length);
  431. searchCriteria = fullSearchString.Substring(fullPathMod.Length + 1);
  432. }
  433. return searchCriteria;
  434. }
  435. private static string GetFullSearchString(string fullPath, string searchPattern)
  436. {
  437. Contract.Requires(fullPath != null);
  438. Contract.Requires(searchPattern != null);
  439. PathHelpers.ThrowIfEmptyOrRootedPath(searchPattern);
  440. string tempStr = Path.Combine(fullPath, searchPattern);
  441. // If path ends in a trailing slash (\), append a * or we'll get a "Cannot find the file specified" exception
  442. char lastChar = tempStr[tempStr.Length - 1];
  443. if (PathInternal.IsDirectorySeparator(lastChar) || lastChar == Path.VolumeSeparatorChar)
  444. {
  445. tempStr = tempStr + "*";
  446. }
  447. return tempStr;
  448. }
  449. }
  450. internal abstract class SearchResultHandler<TSource>
  451. {
  452. /// <summary>
  453. /// Returns true if the result should be included. If true, the <paramref name="result"/> parameter
  454. /// is set to the created result object, otherwise it is set to null.
  455. /// </summary>
  456. [SecurityCritical]
  457. internal abstract bool IsResultIncluded(string fullPath, string userPath, ref Interop.mincore.WIN32_FIND_DATA findData, out TSource result);
  458. }
  459. internal static class SearchResultHandler
  460. {
  461. private static SearchResultHandler<string> s_filePath;
  462. private static SearchResultHandler<string> s_directoryPath;
  463. private static SearchResultHandler<string> s_fileSystemPath;
  464. private static SearchResultHandler<FileInfo> s_fileInfo;
  465. private static SearchResultHandler<DirectoryInfo> s_directoryInfo;
  466. private static SearchResultHandler<FileSystemInfo> s_fileSystemInfo;
  467. internal static SearchResultHandler<string> FilePath
  468. {
  469. get { return s_filePath ?? (s_filePath = new StringResultHandler(includeFiles: true, includeDirs: false)); }
  470. }
  471. internal static SearchResultHandler<string> DirectoryPath
  472. {
  473. get { return s_directoryPath ?? (s_directoryPath = new StringResultHandler(includeFiles: false, includeDirs: true)); }
  474. }
  475. internal static SearchResultHandler<string> FileSystemPath
  476. {
  477. get { return s_fileSystemPath ?? (s_fileSystemPath = new StringResultHandler(includeFiles: true, includeDirs: true)); }
  478. }
  479. internal static SearchResultHandler<FileInfo> FileInfo
  480. {
  481. get { return s_fileInfo ?? (s_fileInfo = new FileInfoResultHandler()); }
  482. }
  483. internal static SearchResultHandler<DirectoryInfo> DirectoryInfo
  484. {
  485. get { return s_directoryInfo ?? (s_directoryInfo = new DirectoryInfoResultHandler()); }
  486. }
  487. internal static SearchResultHandler<FileSystemInfo> FileSystemInfo
  488. {
  489. get { return s_fileSystemInfo ?? (s_fileSystemInfo = new FileSystemInfoResultHandler()); }
  490. }
  491. private sealed class StringResultHandler : SearchResultHandler<string>
  492. {
  493. private readonly bool _includeFiles;
  494. private readonly bool _includeDirs;
  495. internal StringResultHandler(bool includeFiles, bool includeDirs)
  496. {
  497. _includeFiles = includeFiles;
  498. _includeDirs = includeDirs;
  499. }
  500. [SecurityCritical]
  501. internal override bool IsResultIncluded(string fullPath, string userPath, ref Interop.mincore.WIN32_FIND_DATA findData, out string result)
  502. {
  503. if ((_includeFiles && Win32FileSystemEnumerableHelpers.IsFile(ref findData)) ||
  504. (_includeDirs && Win32FileSystemEnumerableHelpers.IsDir(ref findData)))
  505. {
  506. result = Path.Combine(userPath, findData.cFileName);
  507. return true;
  508. }
  509. result = null;
  510. return false;
  511. }
  512. }
  513. private sealed class FileInfoResultHandler : SearchResultHandler<FileInfo>
  514. {
  515. [SecurityCritical]
  516. internal override bool IsResultIncluded(string fullPath, string userPath, ref Interop.mincore.WIN32_FIND_DATA findData, out FileInfo result)
  517. {
  518. if (Win32FileSystemEnumerableHelpers.IsFile(ref findData))
  519. {
  520. string fullPathFinal = Path.Combine(fullPath, findData.cFileName);
  521. result = new FileInfo(fullPathFinal, ref findData);
  522. return true;
  523. }
  524. result = null;
  525. return false;
  526. }
  527. }
  528. private sealed class DirectoryInfoResultHandler : SearchResultHandler<DirectoryInfo>
  529. {
  530. [SecurityCritical]
  531. internal override bool IsResultIncluded(string fullPath, string userPath, ref Interop.mincore.WIN32_FIND_DATA findData, out DirectoryInfo result)
  532. {
  533. if (Win32FileSystemEnumerableHelpers.IsDir(ref findData))
  534. {
  535. string fullPathFinal = Path.Combine(fullPath, findData.cFileName);
  536. result = new DirectoryInfo(fullPathFinal, ref findData);
  537. return true;
  538. }
  539. result = null;
  540. return false;
  541. }
  542. }
  543. private sealed class FileSystemInfoResultHandler : SearchResultHandler<FileSystemInfo>
  544. {
  545. [SecurityCritical]
  546. internal override bool IsResultIncluded(string fullPath, string userPath, ref Interop.mincore.WIN32_FIND_DATA findData, out FileSystemInfo result)
  547. {
  548. if (Win32FileSystemEnumerableHelpers.IsFile(ref findData))
  549. {
  550. string fullPathFinal = Path.Combine(fullPath, findData.cFileName);
  551. result = new FileInfo(fullPathFinal, ref findData);
  552. return true;
  553. }
  554. else if (Win32FileSystemEnumerableHelpers.IsDir(ref findData))
  555. {
  556. string fullPathFinal = Path.Combine(fullPath, findData.cFileName);
  557. result = new DirectoryInfo(fullPathFinal, ref findData);
  558. return true;
  559. }
  560. result = null;
  561. return false;
  562. }
  563. }
  564. }
  565. internal static class Win32FileSystemEnumerableHelpers
  566. {
  567. [SecurityCritical] // auto-generated
  568. internal static bool IsDir(ref Interop.mincore.WIN32_FIND_DATA data)
  569. {
  570. // Don't add "." nor ".."
  571. return (0 != (data.dwFileAttributes & Interop.mincore.FileAttributes.FILE_ATTRIBUTE_DIRECTORY))
  572. && !data.cFileName.Equals(".") && !data.cFileName.Equals("..");
  573. }
  574. [SecurityCritical] // auto-generated
  575. internal static bool IsFile(ref Interop.mincore.WIN32_FIND_DATA data)
  576. {
  577. return 0 == (data.dwFileAttributes & Interop.mincore.FileAttributes.FILE_ATTRIBUTE_DIRECTORY);
  578. }
  579. }
  580. }