private static Interop.libc.SafeDirHandle OpenDirectory(string fullPath) { Interop.libc.SafeDirHandle handle = Interop.libc.opendir(fullPath); if (handle.IsInvalid) { throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), fullPath, isDirectory: true); } return(handle); }
private IEnumerator <T> Enumerate(Interop.libc.SafeDirHandle dirHandle) { if (dirHandle == null) { // Empty search yield break; } Debug.Assert(!dirHandle.IsInvalid); Debug.Assert(!dirHandle.IsClosed); // Maintain a stack of the directories to explore, in the case of SearchOption.AllDirectories // Lazily-initialized only if we find subdirectories that will be explored. Stack <PathPair> toExplore = null; PathPair dirPath = _initialDirectory; while (dirHandle != null) { try { // Read each entry from the enumerator IntPtr curEntry; while ((curEntry = Interop.libc.readdir(dirHandle)) != IntPtr.Zero) // no validation needed for readdir { string name = Interop.libc.GetDirEntName(curEntry); // Get from the dir entry whether the entry is a file or directory. // We classify everything as a file unless we know it to be a directory, // e.g. a FIFO will be classified as a file. bool isDir; switch (Interop.libc.GetDirEntType(curEntry)) { case Interop.libc.DType.DT_DIR: // We know it's a directory. isDir = true; break; case Interop.libc.DType.DT_LNK: case Interop.libc.DType.DT_UNKNOWN: // It's a symlink or unknown: stat to it to see if we can resolve it to a directory. // If we can't (e.g.symlink to a file, broken symlink, etc.), we'll just treat it as a file. Interop.ErrorInfo errnoIgnored; isDir = DirectoryExists(Path.Combine(dirPath.FullPath, name), out errnoIgnored); break; default: // Otherwise, treat it as a file. This includes regular files, // FIFOs, etc. isDir = false; break; } // Yield the result if the user has asked for it. In the case of directories, // always explore it by pushing it onto the stack, regardless of whether // we're returning directories. if (isDir) { if (!ShouldIgnoreDirectory(name)) { if (_includeDirectories && Interop.libc.fnmatch(_searchPattern, name, Interop.libc.FnmatchFlags.None) == 0) { yield return(_translateResult(Path.Combine(dirPath.UserPath, name), /*isDirectory*/ true)); } if (_searchOption == SearchOption.AllDirectories) { if (toExplore == null) { toExplore = new Stack <PathPair>(); } toExplore.Push(new PathPair(Path.Combine(dirPath.UserPath, name), Path.Combine(dirPath.FullPath, name))); } } } else if (_includeFiles && Interop.libc.fnmatch(_searchPattern, name, Interop.libc.FnmatchFlags.None) == 0) { yield return(_translateResult(Path.Combine(dirPath.UserPath, name), /*isDirectory*/ false)); } } } finally { // Close the directory enumerator dirHandle.Dispose(); dirHandle = null; } if (toExplore != null && toExplore.Count > 0) { // Open the next directory. dirPath = toExplore.Pop(); dirHandle = OpenDirectory(dirPath.FullPath); } } }
private IEnumerator <T> Enumerate(Interop.libc.SafeDirHandle dirHandle) { if (dirHandle == null) { // Empty search yield break; } Debug.Assert(!dirHandle.IsInvalid); Debug.Assert(!dirHandle.IsClosed); // Maintain a stack of the directories to explore, in the case of SearchOption.AllDirectories // Lazily-initialized only if we find subdirectories that will be explored. Stack <PathPair> toExplore = null; PathPair dirPath = _initialDirectory; while (dirHandle != null) { try { // Read each entry from the enumerator IntPtr curEntry; while ((curEntry = Interop.libc.readdir(dirHandle)) != IntPtr.Zero) // no validation needed for readdir { string name = Interop.libc.GetDirEntName(curEntry); // Get from the dir entry whether the entry is a file or directory. // If we're not sure from the dir entry itself, stat to the entry. bool isDir = false, isFile = false; switch (Interop.libc.GetDirEntType(curEntry)) { case Interop.libc.DType.DT_DIR: isDir = true; break; case Interop.libc.DType.DT_REG: isFile = true; break; case Interop.libc.DType.DT_LNK: case Interop.libc.DType.DT_UNKNOWN: string fullPath = Path.Combine(dirPath.FullPath, name); Interop.libcoreclrpal.fileinfo fileinfo; while (Interop.CheckIo(Interop.libcoreclrpal.GetFileInformationFromPath(fullPath, out fileinfo), fullPath)) { ; } isDir = (fileinfo.mode & Interop.libcoreclrpal.FileTypes.S_IFMT) == Interop.libcoreclrpal.FileTypes.S_IFDIR; isFile = (fileinfo.mode & Interop.libcoreclrpal.FileTypes.S_IFMT) == Interop.libcoreclrpal.FileTypes.S_IFREG; break; } bool matchesSearchPattern = (isFile || isDir) && Interop.libc.fnmatch(_searchPattern, name, Interop.libc.FnmatchFlags.None) == 0; // Yield the result if the user has asked for it. In the case of directories, // always explore it by pushing it onto the stack, regardless of whether // we're returning directories. if (isDir && !ShouldIgnoreDirectory(name)) { if (_includeDirectories && matchesSearchPattern) { yield return(_translateResult(Path.Combine(dirPath.UserPath, name), /*isDirectory*/ true)); } if (_searchOption == SearchOption.AllDirectories) { if (toExplore == null) { toExplore = new Stack <PathPair>(); } toExplore.Push(new PathPair(Path.Combine(dirPath.UserPath, name), Path.Combine(dirPath.FullPath, name))); } } else if (isFile && _includeFiles && matchesSearchPattern) { yield return(_translateResult(Path.Combine(dirPath.UserPath, name), /*isDirectory*/ false)); } } } finally { // Close the directory enumerator dirHandle.Dispose(); dirHandle = null; } if (toExplore != null && toExplore.Count > 0) { // Open the next directory. dirPath = toExplore.Pop(); dirHandle = OpenDirectory(dirPath.FullPath); } } }