/// <summary> /// Encapsulates a find operation. /// </summary> /// <param name="directory">The directory to search in.</param> /// <param name="options">Enumeration options to use.</param> public FileSystemEnumerator(string directory, EnumerationOptions options = null) { _originalRootDirectory = directory ?? throw new ArgumentNullException(nameof(directory)); _rootDirectory = Path.GetFullPath(directory).TrimEnd(Path.DirectorySeparatorChar); _options = options ?? EnumerationOptions.Default; // We need to initialize the directory handle up front to ensure // we immediately throw IO exceptions for missing directory/etc. _directoryHandle = CreateDirectoryHandle(_rootDirectory); if (_directoryHandle == null) { _lastEntryFound = true; } _currentPath = _rootDirectory; try { _pathBuffer = ArrayPool <char> .Shared.Rent(StandardBufferSize); int size = Interop.Sys.ReadBufferSize; _entryBuffer = size > 0 ? ArrayPool <byte> .Shared.Rent(size) : null; } catch { // Close the directory handle right away if we fail to allocate CloseDirectoryHandle(); throw; } }
/// <summary> /// Get the next directory entry for the given handle. **Note** the actual memory used may be allocated /// by the OS and will be freed when the handle is closed. As such, the handle lifespan MUST be kept tightly /// controlled. The DirectoryEntry name cannot be accessed after the handle is closed. /// /// Call <see cref="ReadBufferSize"/> to see what size buffer to allocate. /// </summary> internal static int ReadDir(SafeDirectoryHandle dir, Span <byte> buffer, ref DirectoryEntry entry) { // The calling pattern for ReadDir is described in src/Native/Unix/System.Native/pal_io.cpp|.h Debug.Assert(buffer.Length >= ReadBufferSize, "should have a big enough buffer for the raw data"); // ReadBufferSize is zero when the native implementation does not support reading into a buffer. return(ReadDirR(dir.DangerousGetHandle(), ref MemoryMarshal.GetReference(buffer), ReadBufferSize, ref entry)); }
// The calling pattern for ReadDir is described in src/Native/System.Native/pal_readdir.cpp internal static int ReadDir(SafeDirectoryHandle dir, out DirectoryEntry outputEntry) { unsafe { // To reduce strcpys, alloc a buffer here and get the result from OS, then copy it over for the caller. byte *buffer = stackalloc byte[s_direntSize]; InternalDirectoryEntry temp; int ret = ReadDirR(dir, buffer, s_direntSize, out temp); outputEntry = ret == 0 ? new DirectoryEntry() { InodeName = GetDirectoryEntryName(temp), InodeType = temp.InodeType } : default(DirectoryEntry); return(ret); } }
private SafeDirectoryHandle CreateDirectoryHandle(string path) { // TODO: https://github.com/dotnet/corefx/issues/26715 // - Use IntPtr handle directly SafeDirectoryHandle handle = Interop.Sys.OpenDir(path); if (handle.IsInvalid) { Interop.ErrorInfo info = Interop.Sys.GetLastErrorInfo(); if ((_options.IgnoreInaccessible && IsAccessError(info.RawErrno)) || ContinueOnError(info.RawErrno)) { return(null); } throw Interop.GetExceptionForIoErrno(info, path, isDirectory: true); } return(handle); }
// The calling pattern for ReadDir is described in src/Native/System.Native/pal_readdir.cpp internal static int ReadDir(SafeDirectoryHandle dir, out DirectoryEntry outputEntry) { bool addedRef = false; try { // We avoid a native string copy into InternalDirectoryEntry. // - If the platform suppors reading into a buffer, the data is read directly into the buffer. The // data can be read as long as the buffer is valid. // - If the platform does not support reading into a buffer, the information returned in // InternalDirectoryEntry points to native memory owned by the SafeDirectoryHandle. The data is only // valid until the next call to CloseDir/ReadDir. We extend the reference until we have copied all data // to ensure it does not become invalid by a CloseDir; and we copy the data so our caller does not // use the native memory held by the SafeDirectoryHandle. dir.DangerousAddRef(ref addedRef); unsafe { // s_readBufferSize is zero when the native implementation does not support reading into a buffer. byte *buffer = stackalloc byte[s_readBufferSize]; InternalDirectoryEntry temp; int ret = ReadDirR(dir.DangerousGetHandle(), buffer, s_readBufferSize, out temp); // We copy data into DirectoryEntry to ensure there are no dangling references. outputEntry = ret == 0 ? new DirectoryEntry() { InodeName = GetDirectoryEntryName(temp), InodeType = temp.InodeType } : default(DirectoryEntry); return(ret); } } finally { if (addedRef) { dir.DangerousRelease(); } } }
private static unsafe extern int ReadDirR(SafeDirectoryHandle dir, byte *buffer, int bufferSize, out InternalDirectoryEntry outputEntry);
private void CloseDirectoryHandle() { _directoryHandle?.Dispose(); _directoryHandle = null; }
private void DequeueNextDirectory() { _currentPath = _pending.Dequeue(); _directoryHandle = CreateDirectoryHandle(_currentPath); }