Imported Upstream version 5.16.0.100

Former-commit-id: 38faa55fb9669e35e7d8448b15c25dc447f25767
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2018-08-07 15:19:03 +00:00
parent 0a9828183b
commit 7d7f676260
4419 changed files with 170950 additions and 90273 deletions

View File

@@ -20,12 +20,15 @@ namespace System.IO
public static System.Collections.Generic.IEnumerable<string> EnumerateDirectories(string path) { throw null; }
public static System.Collections.Generic.IEnumerable<string> EnumerateDirectories(string path, string searchPattern) { throw null; }
public static System.Collections.Generic.IEnumerable<string> EnumerateDirectories(string path, string searchPattern, System.IO.SearchOption searchOption) { throw null; }
public static System.Collections.Generic.IEnumerable<string> EnumerateDirectories(string path, string searchPattern, System.IO.EnumerationOptions enumerationOptions) { throw null; }
public static System.Collections.Generic.IEnumerable<string> EnumerateFiles(string path) { throw null; }
public static System.Collections.Generic.IEnumerable<string> EnumerateFiles(string path, string searchPattern) { throw null; }
public static System.Collections.Generic.IEnumerable<string> EnumerateFiles(string path, string searchPattern, System.IO.SearchOption searchOption) { throw null; }
public static System.Collections.Generic.IEnumerable<string> EnumerateFiles(string path, string searchPattern, System.IO.EnumerationOptions enumerationOptions) { throw null; }
public static System.Collections.Generic.IEnumerable<string> EnumerateFileSystemEntries(string path) { throw null; }
public static System.Collections.Generic.IEnumerable<string> EnumerateFileSystemEntries(string path, string searchPattern) { throw null; }
public static System.Collections.Generic.IEnumerable<string> EnumerateFileSystemEntries(string path, string searchPattern, System.IO.SearchOption searchOption) { throw null; }
public static System.Collections.Generic.IEnumerable<string> EnumerateFileSystemEntries(string path, string searchPattern, System.IO.EnumerationOptions enumerationOptions) { throw null; }
public static bool Exists(string path) { throw null; }
public static System.DateTime GetCreationTime(string path) { throw null; }
public static System.DateTime GetCreationTimeUtc(string path) { throw null; }
@@ -33,13 +36,16 @@ namespace System.IO
public static string[] GetDirectories(string path) { throw null; }
public static string[] GetDirectories(string path, string searchPattern) { throw null; }
public static string[] GetDirectories(string path, string searchPattern, System.IO.SearchOption searchOption) { throw null; }
public static string[] GetDirectories(string path, string searchPattern, System.IO.EnumerationOptions enumerationOptions) { throw null; }
public static string GetDirectoryRoot(string path) { throw null; }
public static string[] GetFiles(string path) { throw null; }
public static string[] GetFiles(string path, string searchPattern) { throw null; }
public static string[] GetFiles(string path, string searchPattern, System.IO.SearchOption searchOption) { throw null; }
public static string[] GetFiles(string path, string searchPattern, System.IO.EnumerationOptions enumerationOptions) { throw null; }
public static string[] GetFileSystemEntries(string path) { throw null; }
public static string[] GetFileSystemEntries(string path, string searchPattern) { throw null; }
public static string[] GetFileSystemEntries(string path, string searchPattern, System.IO.SearchOption searchOption) { throw null; }
public static string[] GetFileSystemEntries(string path, string searchPattern, System.IO.EnumerationOptions enumerationOptions) { throw null; }
public static System.DateTime GetLastAccessTime(string path) { throw null; }
public static System.DateTime GetLastAccessTimeUtc(string path) { throw null; }
public static System.DateTime GetLastWriteTime(string path) { throw null; }
@@ -67,23 +73,29 @@ namespace System.IO
public override void Delete() { }
public void Delete(bool recursive) { }
public System.Collections.Generic.IEnumerable<System.IO.DirectoryInfo> EnumerateDirectories() { throw null; }
public System.Collections.Generic.IEnumerable<System.IO.DirectoryInfo> EnumerateDirectories(string searchPattern, System.IO.EnumerationOptions enumerationOptions) { throw null; }
public System.Collections.Generic.IEnumerable<System.IO.FileSystemInfo> EnumerateFileSystemInfos(string searchPattern, System.IO.EnumerationOptions enumerationOptions) { throw null; }
public System.Collections.Generic.IEnumerable<System.IO.DirectoryInfo> EnumerateDirectories(string searchPattern) { throw null; }
public System.Collections.Generic.IEnumerable<System.IO.DirectoryInfo> EnumerateDirectories(string searchPattern, System.IO.SearchOption searchOption) { throw null; }
public System.Collections.Generic.IEnumerable<System.IO.FileInfo> EnumerateFiles() { throw null; }
public System.Collections.Generic.IEnumerable<System.IO.FileInfo> EnumerateFiles(string searchPattern) { throw null; }
public System.Collections.Generic.IEnumerable<System.IO.FileInfo> EnumerateFiles(string searchPattern, System.IO.SearchOption searchOption) { throw null; }
public System.Collections.Generic.IEnumerable<System.IO.FileInfo> EnumerateFiles(string searchPattern, System.IO.EnumerationOptions enumerationOptions) { throw null; }
public System.Collections.Generic.IEnumerable<System.IO.FileSystemInfo> EnumerateFileSystemInfos() { throw null; }
public System.Collections.Generic.IEnumerable<System.IO.FileSystemInfo> EnumerateFileSystemInfos(string searchPattern) { throw null; }
public System.Collections.Generic.IEnumerable<System.IO.FileSystemInfo> EnumerateFileSystemInfos(string searchPattern, System.IO.SearchOption searchOption) { throw null; }
public System.Collections.Generic.IEnumerable<System.IO.DirectoryInfo> EnumerateDirectories(string searchPattern, System.IO.SearchOption searchOption) { throw null; }
public System.IO.DirectoryInfo[] GetDirectories() { throw null; }
public System.IO.DirectoryInfo[] GetDirectories(string searchPattern) { throw null; }
public System.IO.DirectoryInfo[] GetDirectories(string searchPattern, System.IO.SearchOption searchOption) { throw null; }
public System.IO.DirectoryInfo[] GetDirectories(string searchPattern, System.IO.EnumerationOptions enumerationOptions) { throw null; }
public System.IO.FileInfo[] GetFiles() { throw null; }
public System.IO.FileInfo[] GetFiles(string searchPattern) { throw null; }
public System.IO.FileInfo[] GetFiles(string searchPattern, System.IO.SearchOption searchOption) { throw null; }
public System.IO.FileInfo[] GetFiles(string searchPattern, System.IO.EnumerationOptions enumerationOptions) { throw null; }
public System.IO.FileSystemInfo[] GetFileSystemInfos() { throw null; }
public System.IO.FileSystemInfo[] GetFileSystemInfos(string searchPattern) { throw null; }
public System.IO.FileSystemInfo[] GetFileSystemInfos(string searchPattern, System.IO.SearchOption searchOption) { throw null; }
public System.IO.FileSystemInfo[] GetFileSystemInfos(string searchPattern, System.IO.EnumerationOptions enumerationOptions) { throw null; }
public void MoveTo(string destDirName) { }
public override string ToString() { throw null; }
}
@@ -211,4 +223,83 @@ namespace System.IO
AllDirectories = 1,
TopDirectoryOnly = 0,
}
public enum MatchType
{
Simple,
Win32
}
public enum MatchCasing
{
PlatformDefault,
CaseSensitive,
CaseInsensitive
}
public class EnumerationOptions
{
public EnumerationOptions() { }
public bool RecurseSubdirectories { get { throw null; } set { } }
public bool IgnoreInaccessible { get { throw null; } set { } }
public int BufferSize { get { throw null; } set { } }
public FileAttributes AttributesToSkip { get { throw null; } set { } }
public MatchType MatchType { get { throw null; } set { } }
public MatchCasing MatchCasing { get { throw null; } set { } }
public bool ReturnSpecialDirectories { get { throw null; } set { } }
}
}
namespace System.IO.Enumeration
{
public ref struct FileSystemEntry
{
public ReadOnlySpan<char> Directory { get { throw null; } }
public ReadOnlySpan<char> RootDirectory { get { throw null; } }
public ReadOnlySpan<char> OriginalRootDirectory { get { throw null; } }
public ReadOnlySpan<char> FileName { get { throw null; } }
public FileAttributes Attributes { get { throw null; } }
public long Length { get { throw null; } }
public DateTimeOffset CreationTimeUtc { get { throw null; } }
public DateTimeOffset LastAccessTimeUtc { get { throw null; } }
public DateTimeOffset LastWriteTimeUtc { get { throw null; } }
public bool IsDirectory { get { throw null; } }
public bool IsHidden { get { throw null; } }
public FileSystemInfo ToFileSystemInfo() { throw null; }
public string ToSpecifiedFullPath() { throw null; }
public string ToFullPath() { throw null; }
}
public abstract class FileSystemEnumerator<TResult> : Runtime.ConstrainedExecution.CriticalFinalizerObject, Collections.Generic.IEnumerator<TResult>
{
public FileSystemEnumerator(string directory, EnumerationOptions options = null) { }
protected virtual bool ShouldIncludeEntry(ref FileSystemEntry entry) { throw null; }
protected virtual bool ShouldRecurseIntoEntry(ref FileSystemEntry entry) { throw null; }
protected abstract TResult TransformEntry(ref FileSystemEntry entry);
protected virtual void OnDirectoryFinished(ReadOnlySpan<char> directory) { throw null; }
protected virtual bool ContinueOnError(int error) { throw null; }
public TResult Current { get { throw null; } }
object System.Collections.IEnumerator.Current { get { throw null; } }
public bool MoveNext() { throw null; }
public void Reset() { throw null; }
public void Dispose() { throw null; }
protected virtual void Dispose(bool disposing) { throw null; }
}
public class FileSystemEnumerable<TResult> : Collections.Generic.IEnumerable<TResult>
{
public FileSystemEnumerable(string directory, FindTransform transform, EnumerationOptions options = null) { }
public FindPredicate ShouldRecursePredicate { get { throw null; } set { } }
public FindPredicate ShouldIncludePredicate { get { throw null; } set { } }
public Collections.Generic.IEnumerator<TResult> GetEnumerator() { throw null; }
Collections.IEnumerator Collections.IEnumerable.GetEnumerator() { throw null; }
public delegate bool FindPredicate(ref FileSystemEntry entry);
public delegate TResult FindTransform(ref FileSystemEntry entry);
}
public static class FileSystemName
{
public static string TranslateWin32Expression(string expression) { throw null; }
public static bool MatchesWin32Expression(ReadOnlySpan<char> expression, ReadOnlySpan<char> name, bool ignoreCase = true) { throw null; }
public static bool MatchesSimpleExpression(ReadOnlySpan<char> expression, ReadOnlySpan<char> name, bool ignoreCase = true) { throw null; }
}
}

View File

@@ -0,0 +1,8 @@
Compat issues with assembly System.IO.FileSystem:
# These are now virtual in the implementation, which should be ok.
CannotMakeMemberAbstract : Member 'System.IO.FileSystemInfo.Exists' is abstract in the implementation but is not abstract in the contract.
CannotMakeMemberAbstract : Member 'System.IO.FileSystemInfo.Name' is abstract in the implementation but is not abstract in the contract.
CannotMakeMemberAbstract : Member 'System.IO.FileSystemInfo.Exists.get()' is abstract in the implementation but is not abstract in the contract.
CannotMakeMemberAbstract : Member 'System.IO.FileSystemInfo.Name.get()' is abstract in the implementation but is not abstract in the contract.
# C# generates backing fields for fixed buffers as public.
TypesMustExist : Type 'System.IO.Enumeration.FileSystemEntry.<_fileNameBuffer>e__FixedBuffer' does not exist in the implementation but it does exist in the contract.

View File

@@ -3,10 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Security;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using Microsoft.Win32;
namespace Microsoft.Win32.SafeHandles
{

View File

@@ -7,6 +7,7 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<IsPartialFacadeAssembly>true</IsPartialFacadeAssembly>
<UWPCompatible Condition="'$(TargetGroup)' == 'uap' or '$(TargetGroup)' == 'uapaot'">true</UWPCompatible>
<ILLinkClearInitLocals>true</ILLinkClearInitLocals>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetsUnix)' == 'true'">
<NoWarn>$(NoWarn);414</NoWarn>
@@ -18,63 +19,43 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uap-Windows_NT-Debug|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uap-Windows_NT-Release|AnyCPU'" />
<ItemGroup>
<Compile Include="System\IO\CharSpanExtensions.cs" />
<Compile Include="System\IO\DosMatcher.cs" />
<Compile Include="System\IO\Enumeration\FileSystemEntry.cs" />
<Compile Include="System\IO\Enumeration\FileSystemEnumerable.cs" />
<Compile Include="System\IO\Enumeration\FileSystemEnumerableFactory.cs" />
<Compile Include="System\IO\Enumeration\FileSystemEnumerator.cs" />
<Compile Include="System\IO\Enumeration\FileSystemName.cs" />
<Compile Include="System\IO\MatchCasing.cs" />
<Compile Include="System\IO\MatchType.cs" />
<Compile Include="System\IO\Error.cs" />
<Compile Include="System\IO\Directory.cs" />
<Compile Include="System\IO\DirectoryInfo.cs" />
<Compile Include="System\IO\File.cs" />
<Compile Include="System\IO\FileInfo.cs" />
<Compile Include="System\IO\FileSystemInfo.cs" />
<Compile Include="System\IO\EnumerationOptions.cs" />
<Compile Include="System\IO\Iterator.cs" />
<Compile Include="System\IO\PathHelpers.cs" />
<Compile Include="System\IO\PathPair.cs" />
<Compile Include="System\IO\ReadLinesIterator.cs" />
<Compile Include="System\IO\SearchOption.cs" />
<Compile Include="System\IO\SearchTarget.cs" />
<Compile Include="$(CommonPath)\System\Collections\Generic\ArrayBuilder.cs">
<Link>Common\System\Collections\Generic\ArrayBuilder.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\System\Collections\Generic\EnumerableHelpers.cs">
<Link>Common\System\Collections\Generic\EnumerableHelpers.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\System\Collections\Generic\LargeArrayBuilder.cs">
<Link>Common\System\Collections\Generic\LargeArrayBuilder.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\System\IO\StringBuilderCache.cs">
<Link>Common\System\IO\StringBuilderCache.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\System\Text\ValueStringBuilder.cs">
<Link>Common\System\Text\ValueStringBuilder.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\System\IO\PathInternal.cs">
<Link>Common\System\IO\PathInternal.cs</Link>
<Compile Include="$(CommonPath)\CoreLib\System\Text\ValueStringBuilder.cs">
<Link>Common\CoreLib\System\Text\ValueStringBuilder.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\System\IO\PathInternal.CaseSensitivity.cs">
<Link>Common\System\IO\PathInternal.CaseSensitivity.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\System\Threading\Tasks\TaskToApm.cs">
<Link>Common\System\Threading\Tasks\TaskToApm.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\System\IO\StreamHelpers.CopyValidation.cs">
<Link>Common\System\IO\StreamHelpers.CopyValidation.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\CoreLib\System\IO\PathInternal.cs">
<Link>Common\CoreLib\System\IO\PathInternal.cs</Link>
</Compile>
</ItemGroup>
<!-- Windows -->
<ItemGroup Condition="'$(TargetsWindows)' == 'true'">
<Compile Include="System\IO\CharSpanExtensions.Windows.cs" />
<Compile Include="System\IO\Enumeration\FileSystemEnumerator.Windows.cs" />
<Compile Include="System\IO\DisableMediaInsertionPrompt.cs" />
<Compile Include="System\IO\DirectoryInfo.Windows.cs" />
<Compile Include="System\IO\FileInfo.Windows.cs" />
<Compile Include="System\IO\FindEnumerable.Windows.cs" />
<Compile Include="System\IO\FindEnumerableFactory.cs" />
<Compile Include="System\IO\FindPredicate.cs" />
<Compile Include="System\IO\FindTransform.cs" />
<Compile Include="System\IO\FindPredicates.cs" />
<Compile Include="System\IO\FindTransforms.cs" />
<Compile Include="System\IO\FileSystemInfo.Windows.cs" />
<Compile Include="System\IO\PathHelpers.Windows.cs" />
<Compile Include="System\IO\RawFindData.cs" />
<Compile Include="System\IO\Enumeration\FileSystemEntry.Windows.cs" />
<Compile Include="System\IO\FileSystem.Windows.cs" />
<Compile Include="Microsoft\Win32\SafeHandles\SafeFindHandle.Windows.cs" />
<Compile Include="$(CommonPath)\Interop\Windows\Interop.Libraries.cs">
@@ -95,11 +76,11 @@
<Compile Include="$(CommonPath)\System\IO\DriveInfoInternal.Win32.cs">
<Link>Common\System\IO\DriveInfoInternal.Win32.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\System\IO\PathInternal.Windows.cs">
<Link>Common\System\IO\PathInternal.Windows.cs</Link>
<Compile Include="$(CommonPath)\CoreLib\System\IO\PathInternal.Windows.cs">
<Link>Common\CoreLib\System\IO\PathInternal.Windows.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\System\IO\Win32Marshal.cs">
<Link>Common\System\IO\Win32Marshal.cs</Link>
<Compile Include="$(CommonPath)\CoreLib\System\IO\Win32Marshal.cs">
<Link>Common\CoreLib\System\IO\Win32Marshal.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Windows\Interop.BOOL.cs">
<Link>Common\Interop\Windows\Interop.BOOL.cs</Link>
@@ -107,51 +88,9 @@
<Compile Include="$(CommonPath)\Interop\Windows\kernel32\Interop.SECURITY_ATTRIBUTES.cs">
<Link>Common\Interop\Windows\Interop.SECURITY_ATTRIBUTES.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Windows\kernel32\Interop.SecurityOptions.cs">
<Link>Common\Interop\Windows\Interop.SecurityOptions.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Windows\kernel32\Interop.FileTypes.cs">
<Link>Common\Interop\Windows\Interop.FileTypes.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Windows\kernel32\Interop.GetFileType_SafeHandle.cs">
<Link>Common\Interop\Windows\Interop.GetFileType.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Windows\kernel32\Interop.FlushFileBuffers.cs">
<Link>Common\Interop\Windows\Interop.FlushFileBuffers.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Windows\kernel32\Interop.SetEndOfFile.cs">
<Link>Common\Interop\Windows\Interop.SetEndOfFile.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Windows\kernel32\Interop.SetFilePointerEx.cs">
<Link>Common\Interop\Windows\Interop.SetFilePointerEx.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Windows\kernel32\Interop.CancelIoEx.cs">
<Link>Common\Interop\Windows\Interop.CancelIoEx.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Windows\kernel32\Interop.ReadFile_SafeHandle_NativeOverlapped.cs">
<Link>Common\Interop\Windows\Interop.ReadFile_NativeOverlapped.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Windows\kernel32\Interop.WriteFile_SafeHandle_NativeOverlapped.cs">
<Link>Common\Interop\Windows\Interop.WriteFile_NativeOverlapped.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Windows\kernel32\Interop.ReadFile_SafeHandle_IntPtr.cs">
<Link>Common\Interop\Windows\Interop.ReadFile_IntPtr.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Windows\kernel32\Interop.WriteFile_SafeHandle_IntPtr.cs">
<Link>Common\Interop\Windows\Interop.WriteFile_IntPtr.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Windows\kernel32\Interop.SetFileInformationByHandle.cs">
<Link>Common\Interop\Windows\Interop.SetFileInformationByHandle.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Windows\kernel32\Interop.GetCurrentDirectory.cs">
<Link>Common\Interop\Windows\Interop.GetCurrentDirectory.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Windows\kernel32\Interop.SetCurrentDirectory.cs">
<Link>Common\Interop\Windows\Interop.SetCurrentDirectory.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Windows\kernel32\Interop.GetLongPathName.cs">
<Link>Common\Interop\Windows\Interop.GetLongPathName.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Windows\kernel32\Interop.FindNextFile.cs">
<Link>Common\Interop\Windows\Interop.FindNextFile.cs</Link>
</Compile>
@@ -188,7 +127,6 @@
<Compile Include="$(CommonPath)\Interop\Windows\kernel32\Interop.GET_FILEEX_INFO_LEVELS.cs">
<Link>Common\Interop\Windows\Interop.GET_FILEEX_INFO_LEVELS.cs</Link>
</Compile>
<Compile Include="System\IO\FileSystemInfo.Win32.cs" />
<Compile Include="$(CommonPath)\Interop\Windows\kernel32\Interop.SetThreadErrorMode.cs">
<Link>Common\Interop\Windows\Interop.SetThreadErrorMode.cs</Link>
</Compile>
@@ -246,7 +184,7 @@
</ItemGroup>
<!-- Windows : Win32 only -->
<ItemGroup Condition="'$(TargetsWindows)' == 'true' and '$(UWPCompatible)' != 'true'">
<Compile Include="System\IO\FindEnumerable.Win32.cs" />
<Compile Include="System\IO\Enumeration\FileSystemEnumerator.Win32.cs" />
<Compile Include="$(CommonPath)\Interop\Windows\kernel32\Interop.CreateFile.cs">
<Link>Common\Interop\Windows\Interop.CreateFile.cs</Link>
</Compile>
@@ -262,6 +200,9 @@
<Compile Include="$(CommonPath)\Interop\Windows\NtDll\Interop.NtQueryDirectoryFile.cs">
<Link>Common\Interop\Windows\Interop.NtQueryDirectoryFile.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Windows\NtDll\Interop.NtCreateFile.cs">
<Link>Common\Interop\Windows\Interop.NtCreateFile.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Windows\NtDll\Interop.FILE_INFORMATION_CLASS.cs">
<Link>Common\Interop\Windows\Interop.FILE_INFORMATION_CLASS.cs</Link>
</Compile>
@@ -274,7 +215,7 @@
</ItemGroup>
<!-- Windows : UAP - Win32 + WinRT -->
<ItemGroup Condition="'$(TargetsWindows)' == 'true' and '$(UWPCompatible)' == 'true'">
<Compile Include="System\IO\FindEnumerable.WinRT.cs" />
<Compile Include="System\IO\Enumeration\FileSystemEnumerator.WinRT.cs" />
<Compile Include="$(CommonPath)\Interop\Windows\kernel32\Interop.CreateFile2.cs">
<Link>Common\Interop\Windows\Interop.CreateFile2.cs</Link>
</Compile>
@@ -290,9 +231,10 @@
</ItemGroup>
<!-- Unix -->
<ItemGroup Condition="'$(TargetsUnix)' == 'true'">
<Compile Include="System\IO\CharSpanExtensions.Unix.cs" />
<Compile Include="System\IO\FileStatus.Unix.cs" />
<Compile Include="System\IO\Enumeration\FileSystemEntry.Unix.cs" />
<Compile Include="System\IO\Enumeration\FileSystemEnumerator.Unix.cs" />
<Compile Include="System\IO\FileSystemInfo.Unix.cs" />
<Compile Include="System\IO\PathHelpers.Unix.cs" />
<Compile Include="System\IO\FileSystem.Unix.cs" />
<Compile Include="$(CommonPath)\Interop\Unix\Interop.Libraries.cs">
<Link>Common\Interop\Unix\Interop.Libraries.cs</Link>
@@ -306,30 +248,12 @@
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.ChMod.cs">
<Link>Common\Interop\Unix\Interop.ChMod.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.Close.cs">
<Link>Common\Interop\Unix\Interop.Close.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.CopyFile.cs">
<Link>Common\Interop\Unix\Interop.CopyFile.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.FTruncate.cs">
<Link>Common\Interop\Unix\Interop.FTruncate.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.GetCwd.cs">
<Link>Common\Interop\Unix\Interop.GetCwd.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.Open.cs">
<Link>Common\Interop\Unix\Interop.Open.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.OpenFlags.cs">
<Link>Common\Interop\Unix\Interop.OpenFlags.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.MkDir.cs">
<Link>Common\Interop\Unix\Interop.MkDir.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.PathConf.cs">
<Link>Common\Interop\Unix\Interop.PathConf.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.Permissions.cs">
<Link>Common\Interop\Unix\Interop.Permissions.cs</Link>
</Compile>
@@ -345,36 +269,18 @@
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.Stat.cs">
<Link>Common\Interop\Unix\Interop.Stat.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.Stat.Span.cs">
<Link>Common\Interop\Unix\Interop.Stat.Span.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.ReadDir.cs">
<Link>Common\Interop\Unix\Interop.ReadDir.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.Access.cs">
<Link>Common\Interop\Unix\Interop.Access.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.ChDir.cs">
<Link>Common\Interop\Unix\Interop.ChDir.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.FLock.cs">
<Link>Common\Interop\Unix\Interop.FLock.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.FnMatch.cs">
<Link>Common\Interop\Unix\Interop.FnMatch.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.FSync.cs">
<Link>Common\Interop\Unix\Interop.FSync.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.LSeek.cs">
<Link>Common\Interop\Unix\Interop.LSeek.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.Link.cs">
<Link>Common\Interop\Unix\Interop.Link.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.MountPoints.cs">
<Link>Common\Interop\Unix\Interop.MountPoints.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.PosixFAdvise.cs">
<Link>Common\Interop\Unix\Interop.PosixFAdvise.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.Read.cs">
<Link>Common\Interop\Unix\Interop.Read.cs</Link>
</Compile>
@@ -384,27 +290,25 @@
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.RmDir.cs">
<Link>Common\Interop\Unix\Interop.RmDir.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.Write.cs">
<Link>Common\Interop\Unix\Interop.Write.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.UTime.cs">
<Link>Common\Interop\Unix\Interop.UTime.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Microsoft\Win32\SafeHandles\SafeDirectoryHandle.Unix.cs">
<Link>Common\Microsoft\Win32\SafeHandles\SafeDirectoryHandle.Unix.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\System\IO\PathInternal.Unix.cs">
<Link>Common\System\IO\PathInternal.Unix.cs</Link>
<Compile Include="$(CommonPath)\CoreLib\System\IO\PathInternal.Unix.cs">
<Link>Common\CoreLib\System\IO\PathInternal.Unix.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\System\IO\DriveInfoInternal.Unix.cs">
<Link>Common\System\IO\DriveInfoInternal.Unix.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\System\Text\ValueUtf8Converter.cs">
<Link>Common\System\Text\ValueUtf8Converter.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup>
<Reference Include="System.Buffers" />
<Reference Include="System.Collections" />
<Reference Include="System.Diagnostics.Debug" />
<Reference Include="System.Diagnostics.Tools" />
<Reference Include="System.Linq" />
<Reference Include="System.Memory" />
<Reference Include="System.Resources.ResourceManager" />
<Reference Include="System.Runtime" />
@@ -419,4 +323,4 @@
<Reference Include="System.Threading" />
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
</Project>
</Project>

View File

@@ -1,38 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
namespace System.IO
{
internal static partial class CharSpanExtensions
{
internal static unsafe bool EqualsOrdinal(ReadOnlySpan<char> first, ReadOnlySpan<char> second, bool ignoreCase = false)
{
if (first.Length != second.Length)
return false;
if (!ignoreCase)
return first.SequenceEqual(second);
fixed (char* fp = &MemoryMarshal.GetReference(first))
fixed (char* sp = &MemoryMarshal.GetReference(second))
{
char* f = fp;
char* s = sp;
for (int i = 0; i < first.Length; i++)
{
if (*f != *s && char.ToUpperInvariant(*f) != char.ToUpperInvariant(*s))
return false;
f++;
s++;
}
}
return true;
}
}
}

View File

@@ -1,44 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
namespace System.IO
{
internal static partial class CharSpanExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static unsafe int CompareOrdinal(ReadOnlySpan<char> first, ReadOnlySpan<char> second, bool ignoreCase = false)
{
int result = Interop.Kernel32.CompareStringOrdinal(
ref MemoryMarshal.GetReference(first),
first.Length,
ref MemoryMarshal.GetReference(second),
second.Length,
ignoreCase);
if (result == 0)
throw Win32Marshal.GetExceptionForWin32Error(Marshal.GetLastWin32Error());
// CSTR_LESS_THAN 1 // string 1 less than string 2
// CSTR_EQUAL 2 // string 1 equal to string 2
// CSTR_GREATER_THAN 3 // string 1 greater than string 2
return result - 2;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static bool EqualsOrdinal(ReadOnlySpan<char> first, ReadOnlySpan<char> second, bool ignoreCase = false)
{
if (first.Length != second.Length)
return false;
if (!ignoreCase)
return first.SequenceEqual(second);
return CompareOrdinal(first, second, ignoreCase) == 0;
}
}
}

View File

@@ -1,24 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace System.IO
{
internal static partial class CharSpanExtensions
{
public static bool EndsWithOrdinal(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, bool ignoreCase = false)
{
if (value.Length == 0)
return true;
else if (value.Length > span.Length)
return false;
span = span.Slice(span.Length - value.Length);
if (ignoreCase == false)
return span.SequenceEqual(value);
return EqualsOrdinal(span, value, ignoreCase);
}
}
}

View File

@@ -3,8 +3,8 @@
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Diagnostics;
using System.Security;
using System.IO.Enumeration;
using System.Linq;
namespace System.IO
{
@@ -40,26 +40,7 @@ namespace System.IO
return new DirectoryInfo(fullPath, null);
}
// Input to this method should already be fullpath. This method will ensure that we append
// the trailing slash only when appropriate.
internal static string EnsureTrailingDirectorySeparator(string fullPath)
{
string fullPathWithTrailingDirectorySeparator;
if (!PathHelpers.EndsInDirectorySeparator(fullPath))
fullPathWithTrailingDirectorySeparator = fullPath + PathHelpers.DirectorySeparatorCharAsString;
else
fullPathWithTrailingDirectorySeparator = fullPath;
return fullPathWithTrailingDirectorySeparator;
}
// Tests if the given path refers to an existing DirectoryInfo on disk.
//
// Your application must have Read permission to the directory's
// contents.
//
public static bool Exists(string path)
{
try
@@ -74,8 +55,6 @@ namespace System.IO
return FileSystem.DirectoryExists(fullPath);
}
catch (ArgumentException) { }
catch (NotSupportedException) { } // Security can throw this on ":"
catch (SecurityException) { }
catch (IOException) { }
catch (UnauthorizedAccessException) { }
@@ -148,286 +127,94 @@ namespace System.IO
return File.GetLastAccessTimeUtc(path);
}
// Returns an array of filenames in the DirectoryInfo specified by path
public static string[] GetFiles(string path)
{
if (path == null)
throw new ArgumentNullException(nameof(path));
public static string[] GetFiles(string path) => GetFiles(path, "*", enumerationOptions: EnumerationOptions.Compatible);
return InternalGetFiles(path, "*", SearchOption.TopDirectoryOnly);
}
public static string[] GetFiles(string path, string searchPattern) => GetFiles(path, searchPattern, enumerationOptions: EnumerationOptions.Compatible);
// Returns an array of Files in the current DirectoryInfo matching the
// given search pattern (i.e. "*.txt").
public static string[] GetFiles(string path, string searchPattern)
{
if (path == null)
throw new ArgumentNullException(nameof(path));
if (searchPattern == null)
throw new ArgumentNullException(nameof(searchPattern));
return InternalGetFiles(path, searchPattern, SearchOption.TopDirectoryOnly);
}
// Returns an array of Files in the current DirectoryInfo matching the
// given search pattern (i.e. "*.txt") and search option
public static string[] GetFiles(string path, string searchPattern, SearchOption searchOption)
{
if (path == null)
throw new ArgumentNullException(nameof(path));
if (searchPattern == null)
throw new ArgumentNullException(nameof(searchPattern));
if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories))
throw new ArgumentOutOfRangeException(nameof(searchOption), SR.ArgumentOutOfRange_Enum);
=> GetFiles(path, searchPattern, EnumerationOptions.FromSearchOption(searchOption));
return InternalGetFiles(path, searchPattern, searchOption);
}
public static string[] GetFiles(string path, string searchPattern, EnumerationOptions enumerationOptions)
=> InternalEnumeratePaths(path, searchPattern, SearchTarget.Files, enumerationOptions).ToArray();
// Returns an array of Files in the current DirectoryInfo matching the
// given search pattern (i.e. "*.txt") and search option
private static string[] InternalGetFiles(string path, string searchPattern, SearchOption searchOption)
{
Debug.Assert(path != null);
Debug.Assert(searchPattern != null);
Debug.Assert(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly);
public static string[] GetDirectories(string path) => GetDirectories(path, "*", enumerationOptions: EnumerationOptions.Compatible);
return InternalGetFileDirectoryNames(path, path, searchPattern, true, false, searchOption);
}
public static string[] GetDirectories(string path, string searchPattern) => GetDirectories(path, searchPattern, enumerationOptions: EnumerationOptions.Compatible);
// Returns an array of Directories in the current directory.
public static string[] GetDirectories(string path)
{
if (path == null)
throw new ArgumentNullException(nameof(path));
return InternalGetDirectories(path, "*", SearchOption.TopDirectoryOnly);
}
// Returns an array of Directories in the current DirectoryInfo matching the
// given search criteria (i.e. "*.txt").
public static string[] GetDirectories(string path, string searchPattern)
{
if (path == null)
throw new ArgumentNullException(nameof(path));
if (searchPattern == null)
throw new ArgumentNullException(nameof(searchPattern));
return InternalGetDirectories(path, searchPattern, SearchOption.TopDirectoryOnly);
}
// Returns an array of Directories in the current DirectoryInfo matching the
// given search criteria (i.e. "*.txt").
public static string[] GetDirectories(string path, string searchPattern, SearchOption searchOption)
{
if (path == null)
throw new ArgumentNullException(nameof(path));
if (searchPattern == null)
throw new ArgumentNullException(nameof(searchPattern));
if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories))
throw new ArgumentOutOfRangeException(nameof(searchOption), SR.ArgumentOutOfRange_Enum);
=> GetDirectories(path, searchPattern, EnumerationOptions.FromSearchOption(searchOption));
return InternalGetDirectories(path, searchPattern, searchOption);
}
public static string[] GetDirectories(string path, string searchPattern, EnumerationOptions enumerationOptions)
=> InternalEnumeratePaths(path, searchPattern, SearchTarget.Directories, enumerationOptions).ToArray();
// Returns an array of Directories in the current DirectoryInfo matching the
// given search criteria (i.e. "*.txt").
private static string[] InternalGetDirectories(string path, string searchPattern, SearchOption searchOption)
{
Debug.Assert(path != null);
Debug.Assert(searchPattern != null);
Debug.Assert(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly);
public static string[] GetFileSystemEntries(string path) => GetFileSystemEntries(path, "*", enumerationOptions: EnumerationOptions.Compatible);
return InternalGetFileDirectoryNames(path, path, searchPattern, false, true, searchOption);
}
public static string[] GetFileSystemEntries(string path, string searchPattern) => GetFileSystemEntries(path, searchPattern, enumerationOptions: EnumerationOptions.Compatible);
// Returns an array of strongly typed FileSystemInfo entries in the path
public static string[] GetFileSystemEntries(string path)
{
if (path == null)
throw new ArgumentNullException(nameof(path));
return InternalGetFileSystemEntries(path, "*", SearchOption.TopDirectoryOnly);
}
// Returns an array of strongly typed FileSystemInfo entries in the path with the
// given search criteria (i.e. "*.txt"). We disallow .. as a part of the search criteria
public static string[] GetFileSystemEntries(string path, string searchPattern)
{
if (path == null)
throw new ArgumentNullException(nameof(path));
if (searchPattern == null)
throw new ArgumentNullException(nameof(searchPattern));
return InternalGetFileSystemEntries(path, searchPattern, SearchOption.TopDirectoryOnly);
}
// Returns an array of strongly typed FileSystemInfo entries in the path with the
// given search criteria (i.e. "*.txt"). We disallow .. as a part of the search criteria
public static string[] GetFileSystemEntries(string path, string searchPattern, SearchOption searchOption)
{
if (path == null)
throw new ArgumentNullException(nameof(path));
if (searchPattern == null)
throw new ArgumentNullException(nameof(searchPattern));
if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories))
throw new ArgumentOutOfRangeException(nameof(searchOption), SR.ArgumentOutOfRange_Enum);
=> GetFileSystemEntries(path, searchPattern, EnumerationOptions.FromSearchOption(searchOption));
return InternalGetFileSystemEntries(path, searchPattern, searchOption);
}
public static string[] GetFileSystemEntries(string path, string searchPattern, EnumerationOptions enumerationOptions)
=> InternalEnumeratePaths(path, searchPattern, SearchTarget.Both, enumerationOptions).ToArray();
private static string[] InternalGetFileSystemEntries(string path, string searchPattern, SearchOption searchOption)
{
Debug.Assert(path != null);
Debug.Assert(searchPattern != null);
Debug.Assert(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly);
return InternalGetFileDirectoryNames(path, path, searchPattern, true, true, searchOption);
}
// Returns fully qualified user path of dirs/files that matches the search parameters.
// For recursive search this method will search through all the sub dirs and execute
// the given search criteria against every dir.
// For all the dirs/files returned, it will then demand path discovery permission for
// their parent folders (it will avoid duplicate permission checks)
internal static string[] InternalGetFileDirectoryNames(string path, string userPathOriginal, string searchPattern, bool includeFiles, bool includeDirs, SearchOption searchOption)
{
Debug.Assert(path != null);
Debug.Assert(userPathOriginal != null);
Debug.Assert(searchPattern != null);
Debug.Assert(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly);
IEnumerable<string> enumerable = FileSystem.EnumeratePaths(path, searchPattern, searchOption,
(includeFiles ? SearchTarget.Files : 0) | (includeDirs ? SearchTarget.Directories : 0));
return EnumerableHelpers.ToArray(enumerable);
}
public static IEnumerable<string> EnumerateDirectories(string path)
{
if (path == null)
throw new ArgumentNullException(nameof(path));
return InternalEnumerateDirectories(path, "*", SearchOption.TopDirectoryOnly);
}
public static IEnumerable<string> EnumerateDirectories(string path, string searchPattern)
internal static IEnumerable<string> InternalEnumeratePaths(
string path,
string searchPattern,
SearchTarget searchTarget,
EnumerationOptions options)
{
if (path == null)
throw new ArgumentNullException(nameof(path));
if (searchPattern == null)
throw new ArgumentNullException(nameof(searchPattern));
return InternalEnumerateDirectories(path, searchPattern, SearchOption.TopDirectoryOnly);
FileSystemEnumerableFactory.NormalizeInputs(ref path, ref searchPattern, options);
switch (searchTarget)
{
case SearchTarget.Files:
return FileSystemEnumerableFactory.UserFiles(path, searchPattern, options);
case SearchTarget.Directories:
return FileSystemEnumerableFactory.UserDirectories(path, searchPattern, options);
case SearchTarget.Both:
return FileSystemEnumerableFactory.UserEntries(path, searchPattern, options);
default:
throw new ArgumentOutOfRangeException(nameof(searchTarget));
}
}
public static IEnumerable<string> EnumerateDirectories(string path) => EnumerateDirectories(path, "*", enumerationOptions: EnumerationOptions.Compatible);
public static IEnumerable<string> EnumerateDirectories(string path, string searchPattern) => EnumerateDirectories(path, searchPattern, enumerationOptions: EnumerationOptions.Compatible);
public static IEnumerable<string> EnumerateDirectories(string path, string searchPattern, SearchOption searchOption)
{
if (path == null)
throw new ArgumentNullException(nameof(path));
if (searchPattern == null)
throw new ArgumentNullException(nameof(searchPattern));
if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories))
throw new ArgumentOutOfRangeException(nameof(searchOption), SR.ArgumentOutOfRange_Enum);
=> EnumerateDirectories(path, searchPattern, EnumerationOptions.FromSearchOption(searchOption));
return InternalEnumerateDirectories(path, searchPattern, searchOption);
}
public static IEnumerable<string> EnumerateDirectories(string path, string searchPattern, EnumerationOptions enumerationOptions)
=> InternalEnumeratePaths(path, searchPattern, SearchTarget.Directories, enumerationOptions);
private static IEnumerable<string> InternalEnumerateDirectories(string path, string searchPattern, SearchOption searchOption)
{
Debug.Assert(path != null);
Debug.Assert(searchPattern != null);
Debug.Assert(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly);
return EnumerateFileSystemNames(path, searchPattern, searchOption, false, true);
}
public static IEnumerable<string> EnumerateFiles(string path)
{
if (path == null)
throw new ArgumentNullException(nameof(path));
return InternalEnumerateFiles(path, "*", SearchOption.TopDirectoryOnly);
}
public static IEnumerable<string> EnumerateFiles(string path) => EnumerateFiles(path, "*", enumerationOptions: EnumerationOptions.Compatible);
public static IEnumerable<string> EnumerateFiles(string path, string searchPattern)
{
if (path == null)
throw new ArgumentNullException(nameof(path));
if (searchPattern == null)
throw new ArgumentNullException(nameof(searchPattern));
return InternalEnumerateFiles(path, searchPattern, SearchOption.TopDirectoryOnly);
}
=> EnumerateFiles(path, searchPattern, enumerationOptions: EnumerationOptions.Compatible);
public static IEnumerable<string> EnumerateFiles(string path, string searchPattern, SearchOption searchOption)
{
if (path == null)
throw new ArgumentNullException(nameof(path));
if (searchPattern == null)
throw new ArgumentNullException(nameof(searchPattern));
if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories))
throw new ArgumentOutOfRangeException(nameof(searchOption), SR.ArgumentOutOfRange_Enum);
=> EnumerateFiles(path, searchPattern, EnumerationOptions.FromSearchOption(searchOption));
return InternalEnumerateFiles(path, searchPattern, searchOption);
}
private static IEnumerable<string> InternalEnumerateFiles(string path, string searchPattern, SearchOption searchOption)
{
Debug.Assert(path != null);
Debug.Assert(searchPattern != null);
Debug.Assert(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly);
return EnumerateFileSystemNames(path, searchPattern, searchOption, true, false);
}
public static IEnumerable<string> EnumerateFiles(string path, string searchPattern, EnumerationOptions enumerationOptions)
=> InternalEnumeratePaths(path, searchPattern, SearchTarget.Files, enumerationOptions);
public static IEnumerable<string> EnumerateFileSystemEntries(string path)
{
if (path == null)
throw new ArgumentNullException(nameof(path));
return InternalEnumerateFileSystemEntries(path, "*", SearchOption.TopDirectoryOnly);
}
=> EnumerateFileSystemEntries(path, "*", enumerationOptions: EnumerationOptions.Compatible);
public static IEnumerable<string> EnumerateFileSystemEntries(string path, string searchPattern)
{
if (path == null)
throw new ArgumentNullException(nameof(path));
if (searchPattern == null)
throw new ArgumentNullException(nameof(searchPattern));
return InternalEnumerateFileSystemEntries(path, searchPattern, SearchOption.TopDirectoryOnly);
}
=> EnumerateFileSystemEntries(path, searchPattern, enumerationOptions: EnumerationOptions.Compatible);
public static IEnumerable<string> EnumerateFileSystemEntries(string path, string searchPattern, SearchOption searchOption)
{
if (path == null)
throw new ArgumentNullException(nameof(path));
if (searchPattern == null)
throw new ArgumentNullException(nameof(searchPattern));
if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories))
throw new ArgumentOutOfRangeException(nameof(searchOption), SR.ArgumentOutOfRange_Enum);
=> EnumerateFileSystemEntries(path, searchPattern, EnumerationOptions.FromSearchOption(searchOption));
return InternalEnumerateFileSystemEntries(path, searchPattern, searchOption);
}
private static IEnumerable<string> InternalEnumerateFileSystemEntries(string path, string searchPattern, SearchOption searchOption)
{
Debug.Assert(path != null);
Debug.Assert(searchPattern != null);
Debug.Assert(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly);
return EnumerateFileSystemNames(path, searchPattern, searchOption, true, true);
}
private static IEnumerable<string> EnumerateFileSystemNames(string path, string searchPattern, SearchOption searchOption,
bool includeFiles, bool includeDirs)
{
Debug.Assert(path != null);
Debug.Assert(searchPattern != null);
Debug.Assert(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly);
return FileSystem.EnumeratePaths(path, searchPattern, searchOption,
(includeFiles ? SearchTarget.Files : 0) | (includeDirs ? SearchTarget.Directories : 0));
}
public static IEnumerable<string> EnumerateFileSystemEntries(string path, string searchPattern, EnumerationOptions enumerationOptions)
=> InternalEnumeratePaths(path, searchPattern, SearchTarget.Both, enumerationOptions);
public static string GetDirectoryRoot(string path)
{
@@ -446,17 +233,7 @@ namespace System.IO
return path.Substring(0, PathInternal.GetRootLength(path));
}
/*===============================CurrentDirectory===============================
**Action: Provides a getter and setter for the current directory. The original
** current DirectoryInfo is the one from which the process was started.
**Returns: The current DirectoryInfo (from the getter). Void from the setter.
**Arguments: The current DirectoryInfo to which to switch to the setter.
**Exceptions:
==============================================================================*/
public static string GetCurrentDirectory()
{
return FileSystem.GetCurrentDirectory();
}
public static string GetCurrentDirectory() => Environment.CurrentDirectory;
public static void SetCurrentDirectory(string path)
{
@@ -465,9 +242,7 @@ namespace System.IO
if (path.Length == 0)
throw new ArgumentException(SR.Argument_PathEmpty, nameof(path));
string fulldestDirName = Path.GetFullPath(path);
FileSystem.SetCurrentDirectory(fulldestDirName);
Environment.CurrentDirectory = Path.GetFullPath(path);
}
public static void Move(string sourceDirName, string destDirName)
@@ -483,10 +258,10 @@ namespace System.IO
throw new ArgumentException(SR.Argument_EmptyFileName, nameof(destDirName));
string fullsourceDirName = Path.GetFullPath(sourceDirName);
string sourcePath = EnsureTrailingDirectorySeparator(fullsourceDirName);
string sourcePath = PathInternal.EnsureTrailingSeparator(fullsourceDirName);
string fulldestDirName = Path.GetFullPath(destDirName);
string destPath = EnsureTrailingDirectorySeparator(fulldestDirName);
string destPath = PathInternal.EnsureTrailingSeparator(fulldestDirName);
StringComparison pathComparison = PathInternal.StringComparison;

View File

@@ -1,18 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
namespace System.IO
{
partial class DirectoryInfo
{
internal unsafe DirectoryInfo(string fullPath, string fileName, ref RawFindData findData)
: this(fullPath, fileName: fileName, isNormalized: true)
{
Debug.Assert(fileName.Equals(Path.GetFileName(fullPath)));
Init(findData._info);
}
}
}

View File

@@ -4,17 +4,17 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO.Enumeration;
using System.Linq;
namespace System.IO
{
[Serializable]
public sealed partial class DirectoryInfo : FileSystemInfo
{
private string _name;
public DirectoryInfo(string path)
{
Init(originalPath: PathHelpers.ShouldReviseDirectoryPathToCurrent(path) ? "." : path,
Init(originalPath: path,
fullPath: Path.GetFullPath(path),
isNormalized: true);
}
@@ -30,328 +30,155 @@ namespace System.IO
OriginalPath = originalPath ?? throw new ArgumentNullException("path");
fullPath = fullPath ?? originalPath;
Debug.Assert(!isNormalized || !PathInternal.IsPartiallyQualified(fullPath), "should be fully qualified if normalized");
fullPath = isNormalized ? fullPath : Path.GetFullPath(fullPath);
_name = fileName ?? (PathHelpers.IsRoot(fullPath) ?
_name = fileName ?? (PathInternal.IsRoot(fullPath) ?
fullPath :
Path.GetFileName(PathHelpers.TrimEndingDirectorySeparator(fullPath)));
Path.GetFileName(PathInternal.TrimEndingDirectorySeparator(fullPath.AsSpan()))).ToString();
FullPath = fullPath;
DisplayPath = PathHelpers.ShouldReviseDirectoryPathToCurrent(originalPath) ? "." : originalPath;
}
public override string Name => _name;
public DirectoryInfo Parent
{
get
{
string s = FullPath;
// FullPath might end in either "parent\child" or "parent\child", and in either case we want
// FullPath might end in either "parent\child" or "parent\child\", and in either case we want
// the parent of child, not the child. Trim off an ending directory separator if there is one,
// but don't mangle the root.
if (!PathHelpers.IsRoot(s))
{
s = PathHelpers.TrimEndingDirectorySeparator(s);
}
string parentName = Path.GetDirectoryName(s);
string parentName = Path.GetDirectoryName(PathInternal.IsRoot(FullPath) ? FullPath : PathInternal.TrimEndingDirectorySeparator(FullPath));
return parentName != null ?
new DirectoryInfo(parentName, null) :
null;
}
}
public DirectoryInfo CreateSubdirectory(string path)
{
if (path == null)
throw new ArgumentNullException(nameof(path));
if (PathInternal.IsEffectivelyEmpty(path))
throw new ArgumentException(SR.Argument_PathEmpty, nameof(path));
if (Path.IsPathRooted(path))
throw new ArgumentException(SR.Arg_Path2IsRooted, nameof(path));
return CreateSubdirectoryHelper(path);
}
string fullPath = Path.GetFullPath(Path.Combine(FullPath, path));
private DirectoryInfo CreateSubdirectoryHelper(string path)
{
Debug.Assert(path != null);
PathHelpers.ThrowIfEmptyOrRootedPath(path);
string newDirs = Path.Combine(FullPath, path);
string fullPath = Path.GetFullPath(newDirs);
if (0 != string.Compare(FullPath, 0, fullPath, 0, FullPath.Length, PathInternal.StringComparison))
if (fullPath.Length < FullPath.Length
|| (fullPath.Length > FullPath.Length && !PathInternal.IsDirectorySeparator(fullPath[FullPath.Length]))
|| string.Compare(FullPath, 0, fullPath, 0, FullPath.Length, PathInternal.StringComparison) != 0)
{
throw new ArgumentException(SR.Format(SR.Argument_InvalidSubPath, path, DisplayPath), nameof(path));
throw new ArgumentException(SR.Format(SR.Argument_InvalidSubPath, path, FullPath), nameof(path));
}
FileSystem.CreateDirectory(fullPath);
// Check for read permission to directory we hand back by calling this constructor.
return new DirectoryInfo(fullPath);
}
public void Create()
{
FileSystem.CreateDirectory(FullPath);
}
// Tests if the given path refers to an existing DirectoryInfo on disk.
//
// Your application must have Read permission to the directory's
// contents.
//
public override bool Exists
{
get
{
try
{
return ExistsCore;
}
catch
{
return false;
}
}
}
// Returns an array of Files in the current DirectoryInfo matching the
// given search criteria (i.e. "*.txt").
public FileInfo[] GetFiles(string searchPattern)
{
if (searchPattern == null)
throw new ArgumentNullException(nameof(searchPattern));
return InternalGetFiles(searchPattern, SearchOption.TopDirectoryOnly);
}
// Returns an array of Files in the current DirectoryInfo matching the
// given search criteria (i.e. "*.txt").
public FileInfo[] GetFiles(string searchPattern, SearchOption searchOption)
{
if (searchPattern == null)
throw new ArgumentNullException(nameof(searchPattern));
if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories))
throw new ArgumentOutOfRangeException(nameof(searchOption), SR.ArgumentOutOfRange_Enum);
return InternalGetFiles(searchPattern, searchOption);
}
// Returns an array of Files in the current DirectoryInfo matching the
// given search criteria (i.e. "*.txt").
private FileInfo[] InternalGetFiles(string searchPattern, SearchOption searchOption)
{
Debug.Assert(searchPattern != null);
Debug.Assert(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly);
IEnumerable<FileInfo> enumerable = (IEnumerable<FileInfo>)FileSystem.EnumerateFileSystemInfos(FullPath, searchPattern, searchOption, SearchTarget.Files);
return EnumerableHelpers.ToArray(enumerable);
}
public void Create() => FileSystem.CreateDirectory(FullPath);
// Returns an array of Files in the DirectoryInfo specified by path
public FileInfo[] GetFiles()
{
return InternalGetFiles("*", SearchOption.TopDirectoryOnly);
}
public FileInfo[] GetFiles() => GetFiles("*", enumerationOptions: EnumerationOptions.Compatible);
// Returns an array of Directories in the current directory.
public DirectoryInfo[] GetDirectories()
{
return InternalGetDirectories("*", SearchOption.TopDirectoryOnly);
}
// Returns an array of Files in the current DirectoryInfo matching the
// given search criteria (i.e. "*.txt").
public FileInfo[] GetFiles(string searchPattern) => GetFiles(searchPattern, enumerationOptions: EnumerationOptions.Compatible);
public FileInfo[] GetFiles(string searchPattern, SearchOption searchOption)
=> GetFiles(searchPattern, EnumerationOptions.FromSearchOption(searchOption));
public FileInfo[] GetFiles(string searchPattern, EnumerationOptions enumerationOptions)
=> ((IEnumerable<FileInfo>)InternalEnumerateInfos(FullPath, searchPattern, SearchTarget.Files, enumerationOptions)).ToArray();
// Returns an array of strongly typed FileSystemInfo entries which will contain a listing
// of all the files and directories.
public FileSystemInfo[] GetFileSystemInfos() => GetFileSystemInfos("*", enumerationOptions: EnumerationOptions.Compatible);
// Returns an array of strongly typed FileSystemInfo entries in the path with the
// given search criteria (i.e. "*.txt").
public FileSystemInfo[] GetFileSystemInfos(string searchPattern)
{
if (searchPattern == null)
throw new ArgumentNullException(nameof(searchPattern));
=> GetFileSystemInfos(searchPattern, enumerationOptions: EnumerationOptions.Compatible);
return InternalGetFileSystemInfos(searchPattern, SearchOption.TopDirectoryOnly);
}
// Returns an array of strongly typed FileSystemInfo entries in the path with the
// given search criteria (i.e. "*.txt").
public FileSystemInfo[] GetFileSystemInfos(string searchPattern, SearchOption searchOption)
{
if (searchPattern == null)
throw new ArgumentNullException(nameof(searchPattern));
if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories))
throw new ArgumentOutOfRangeException(nameof(searchOption), SR.ArgumentOutOfRange_Enum);
=> GetFileSystemInfos(searchPattern, EnumerationOptions.FromSearchOption(searchOption));
return InternalGetFileSystemInfos(searchPattern, searchOption);
}
public FileSystemInfo[] GetFileSystemInfos(string searchPattern, EnumerationOptions enumerationOptions)
=> InternalEnumerateInfos(FullPath, searchPattern, SearchTarget.Both, enumerationOptions).ToArray();
// Returns an array of strongly typed FileSystemInfo entries in the path with the
// given search criteria (i.e. "*.txt").
private FileSystemInfo[] InternalGetFileSystemInfos(string searchPattern, SearchOption searchOption)
{
Debug.Assert(searchPattern != null);
Debug.Assert(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly);
IEnumerable<FileSystemInfo> enumerable = FileSystem.EnumerateFileSystemInfos(FullPath, searchPattern, searchOption, SearchTarget.Both);
return EnumerableHelpers.ToArray(enumerable);
}
// Returns an array of strongly typed FileSystemInfo entries which will contain a listing
// of all the files and directories.
public FileSystemInfo[] GetFileSystemInfos()
{
return InternalGetFileSystemInfos("*", SearchOption.TopDirectoryOnly);
}
// Returns an array of Directories in the current directory.
public DirectoryInfo[] GetDirectories() => GetDirectories("*", enumerationOptions: EnumerationOptions.Compatible);
// Returns an array of Directories in the current DirectoryInfo matching the
// given search criteria (i.e. "System*" could match the System & System32
// directories).
public DirectoryInfo[] GetDirectories(string searchPattern)
{
if (searchPattern == null)
throw new ArgumentNullException(nameof(searchPattern));
// given search criteria (i.e. "System*" could match the System & System32 directories).
public DirectoryInfo[] GetDirectories(string searchPattern) => GetDirectories(searchPattern, enumerationOptions: EnumerationOptions.Compatible);
return InternalGetDirectories(searchPattern, SearchOption.TopDirectoryOnly);
}
// Returns an array of Directories in the current DirectoryInfo matching the
// given search criteria (i.e. "System*" could match the System & System32
// directories).
public DirectoryInfo[] GetDirectories(string searchPattern, SearchOption searchOption)
{
if (searchPattern == null)
throw new ArgumentNullException(nameof(searchPattern));
if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories))
throw new ArgumentOutOfRangeException(nameof(searchOption), SR.ArgumentOutOfRange_Enum);
=> GetDirectories(searchPattern, EnumerationOptions.FromSearchOption(searchOption));
return InternalGetDirectories(searchPattern, searchOption);
}
// Returns an array of Directories in the current DirectoryInfo matching the
// given search criteria (i.e. "System*" could match the System & System32
// directories).
private DirectoryInfo[] InternalGetDirectories(string searchPattern, SearchOption searchOption)
{
Debug.Assert(searchPattern != null);
Debug.Assert(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly);
IEnumerable<DirectoryInfo> enumerable = (IEnumerable<DirectoryInfo>)FileSystem.EnumerateFileSystemInfos(FullPath, searchPattern, searchOption, SearchTarget.Directories);
return EnumerableHelpers.ToArray(enumerable);
}
public DirectoryInfo[] GetDirectories(string searchPattern, EnumerationOptions enumerationOptions)
=> ((IEnumerable<DirectoryInfo>)InternalEnumerateInfos(FullPath, searchPattern, SearchTarget.Directories, enumerationOptions)).ToArray();
public IEnumerable<DirectoryInfo> EnumerateDirectories()
{
return InternalEnumerateDirectories("*", SearchOption.TopDirectoryOnly);
}
=> EnumerateDirectories("*", enumerationOptions: EnumerationOptions.Compatible);
public IEnumerable<DirectoryInfo> EnumerateDirectories(string searchPattern)
{
if (searchPattern == null)
throw new ArgumentNullException(nameof(searchPattern));
return InternalEnumerateDirectories(searchPattern, SearchOption.TopDirectoryOnly);
}
=> EnumerateDirectories(searchPattern, enumerationOptions: EnumerationOptions.Compatible);
public IEnumerable<DirectoryInfo> EnumerateDirectories(string searchPattern, SearchOption searchOption)
{
if (searchPattern == null)
throw new ArgumentNullException(nameof(searchPattern));
if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories))
throw new ArgumentOutOfRangeException(nameof(searchOption), SR.ArgumentOutOfRange_Enum);
=> EnumerateDirectories(searchPattern, EnumerationOptions.FromSearchOption(searchOption));
return InternalEnumerateDirectories(searchPattern, searchOption);
}
private IEnumerable<DirectoryInfo> InternalEnumerateDirectories(string searchPattern, SearchOption searchOption)
{
Debug.Assert(searchPattern != null);
Debug.Assert(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly);
return (IEnumerable<DirectoryInfo>)FileSystem.EnumerateFileSystemInfos(FullPath, searchPattern, searchOption, SearchTarget.Directories);
}
public IEnumerable<DirectoryInfo> EnumerateDirectories(string searchPattern, EnumerationOptions enumerationOptions)
=> (IEnumerable<DirectoryInfo>)InternalEnumerateInfos(FullPath, searchPattern, SearchTarget.Directories, enumerationOptions);
public IEnumerable<FileInfo> EnumerateFiles()
{
return InternalEnumerateFiles("*", SearchOption.TopDirectoryOnly);
}
=> EnumerateFiles("*", enumerationOptions: EnumerationOptions.Compatible);
public IEnumerable<FileInfo> EnumerateFiles(string searchPattern)
{
if (searchPattern == null)
throw new ArgumentNullException(nameof(searchPattern));
return InternalEnumerateFiles(searchPattern, SearchOption.TopDirectoryOnly);
}
public IEnumerable<FileInfo> EnumerateFiles(string searchPattern) => EnumerateFiles(searchPattern, enumerationOptions: EnumerationOptions.Compatible);
public IEnumerable<FileInfo> EnumerateFiles(string searchPattern, SearchOption searchOption)
{
if (searchPattern == null)
throw new ArgumentNullException(nameof(searchPattern));
if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories))
throw new ArgumentOutOfRangeException(nameof(searchOption), SR.ArgumentOutOfRange_Enum);
=> EnumerateFiles(searchPattern, EnumerationOptions.FromSearchOption(searchOption));
return InternalEnumerateFiles(searchPattern, searchOption);
}
public IEnumerable<FileInfo> EnumerateFiles(string searchPattern, EnumerationOptions enumerationOptions)
=> (IEnumerable<FileInfo>)InternalEnumerateInfos(FullPath, searchPattern, SearchTarget.Files, enumerationOptions);
private IEnumerable<FileInfo> InternalEnumerateFiles(string searchPattern, SearchOption searchOption)
{
Debug.Assert(searchPattern != null);
Debug.Assert(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly);
return (IEnumerable<FileInfo>)FileSystem.EnumerateFileSystemInfos(FullPath, searchPattern, searchOption, SearchTarget.Files);
}
public IEnumerable<FileSystemInfo> EnumerateFileSystemInfos()
{
return InternalEnumerateFileSystemInfos("*", SearchOption.TopDirectoryOnly);
}
public IEnumerable<FileSystemInfo> EnumerateFileSystemInfos() => EnumerateFileSystemInfos("*", enumerationOptions: EnumerationOptions.Compatible);
public IEnumerable<FileSystemInfo> EnumerateFileSystemInfos(string searchPattern)
{
if (searchPattern == null)
throw new ArgumentNullException(nameof(searchPattern));
return InternalEnumerateFileSystemInfos(searchPattern, SearchOption.TopDirectoryOnly);
}
=> EnumerateFileSystemInfos(searchPattern, enumerationOptions: EnumerationOptions.Compatible);
public IEnumerable<FileSystemInfo> EnumerateFileSystemInfos(string searchPattern, SearchOption searchOption)
=> EnumerateFileSystemInfos(searchPattern, EnumerationOptions.FromSearchOption(searchOption));
public IEnumerable<FileSystemInfo> EnumerateFileSystemInfos(string searchPattern, EnumerationOptions enumerationOptions)
=> InternalEnumerateInfos(FullPath, searchPattern, SearchTarget.Both, enumerationOptions);
internal static IEnumerable<FileSystemInfo> InternalEnumerateInfos(
string path,
string searchPattern,
SearchTarget searchTarget,
EnumerationOptions options)
{
Debug.Assert(path != null);
if (searchPattern == null)
throw new ArgumentNullException(nameof(searchPattern));
if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories))
throw new ArgumentOutOfRangeException(nameof(searchOption), SR.ArgumentOutOfRange_Enum);
return InternalEnumerateFileSystemInfos(searchPattern, searchOption);
}
FileSystemEnumerableFactory.NormalizeInputs(ref path, ref searchPattern, options);
private IEnumerable<FileSystemInfo> InternalEnumerateFileSystemInfos(string searchPattern, SearchOption searchOption)
{
Debug.Assert(searchPattern != null);
Debug.Assert(searchOption == SearchOption.AllDirectories || searchOption == SearchOption.TopDirectoryOnly);
return FileSystem.EnumerateFileSystemInfos(FullPath, searchPattern, searchOption, SearchTarget.Both);
}
// Returns the root portion of the given path. The resulting string
// consists of those rightmost characters of the path that constitute the
// root of the path. Possible patterns for the resulting string are: An
// empty string (a relative path on the current drive), "\" (an absolute
// path on the current drive), "X:" (a relative path on a given drive,
// where X is the drive letter), "X:\" (an absolute path on a given drive),
// and "\\server\share" (a UNC path for a given server and share name).
// The resulting string is null if path is null.
//
public DirectoryInfo Root
{
get
switch (searchTarget)
{
string rootPath = Path.GetPathRoot(FullPath);
return new DirectoryInfo(rootPath);
case SearchTarget.Directories:
return FileSystemEnumerableFactory.DirectoryInfos(path, searchPattern, options);
case SearchTarget.Files:
return FileSystemEnumerableFactory.FileInfos(path, searchPattern, options);
case SearchTarget.Both:
return FileSystemEnumerableFactory.FileSystemInfos(path, searchPattern, options);
default:
throw new ArgumentException(SR.ArgumentOutOfRange_Enum, nameof(searchTarget));
}
}
public DirectoryInfo Root => new DirectoryInfo(Path.GetPathRoot(FullPath));
public void MoveTo(string destDirName)
{
if (destDirName == null)
@@ -360,24 +187,17 @@ namespace System.IO
throw new ArgumentException(SR.Argument_EmptyFileName, nameof(destDirName));
string destination = Path.GetFullPath(destDirName);
string destinationWithSeparator = destination;
if (destinationWithSeparator[destinationWithSeparator.Length - 1] != Path.DirectorySeparatorChar)
destinationWithSeparator = destinationWithSeparator + PathHelpers.DirectorySeparatorCharAsString;
string fullSourcePath;
if (FullPath.Length > 0 && FullPath[FullPath.Length - 1] == Path.DirectorySeparatorChar)
fullSourcePath = FullPath;
else
fullSourcePath = FullPath + PathHelpers.DirectorySeparatorCharAsString;
string destinationWithSeparator = PathInternal.EnsureTrailingSeparator(destination);
string sourceWithSeparator = PathInternal.EnsureTrailingSeparator(FullPath);
StringComparison pathComparison = PathInternal.StringComparison;
if (string.Equals(fullSourcePath, destinationWithSeparator, pathComparison))
if (string.Equals(sourceWithSeparator, destinationWithSeparator, PathInternal.StringComparison))
throw new IOException(SR.IO_SourceDestMustBeDifferent);
string sourceRoot = Path.GetPathRoot(fullSourcePath);
string sourceRoot = Path.GetPathRoot(sourceWithSeparator);
string destinationRoot = Path.GetPathRoot(destinationWithSeparator);
if (!string.Equals(sourceRoot, destinationRoot, pathComparison))
if (!string.Equals(sourceRoot, destinationRoot, PathInternal.StringComparison))
throw new IOException(SR.IO_SourceDestMustHaveSameRoot);
// Windows will throw if the source file/directory doesn't exist, we preemptively check
@@ -385,7 +205,7 @@ namespace System.IO
if (!Exists && !FileSystem.FileExists(FullPath))
throw new DirectoryNotFoundException(SR.Format(SR.IO_PathNotFound_Path, FullPath));
if (FileSystem.DirectoryExists(destinationWithSeparator))
if (FileSystem.DirectoryExists(destination))
throw new IOException(SR.Format(SR.IO_AlreadyExists_Name, destinationWithSeparator));
FileSystem.MoveDirectory(FullPath, destination);
@@ -399,22 +219,8 @@ namespace System.IO
Invalidate();
}
public override void Delete()
{
FileSystem.RemoveDirectory(FullPath, false);
}
public override void Delete() => FileSystem.RemoveDirectory(FullPath, recursive: false);
public void Delete(bool recursive)
{
FileSystem.RemoveDirectory(FullPath, recursive);
}
/// <summary>
/// Returns the original path. Use FullPath or Name properties for the path / directory name.
/// </summary>
public override string ToString()
{
return DisplayPath;
}
public void Delete(bool recursive) => FileSystem.RemoveDirectory(FullPath, recursive);
}
}

View File

@@ -0,0 +1,150 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
namespace System.IO.Enumeration
{
/// <summary>
/// Lower level view of FileSystemInfo used for processing and filtering find results.
/// </summary>
public unsafe ref partial struct FileSystemEntry
{
private const int FileNameBufferSize = 256;
internal Interop.Sys.DirectoryEntry _directoryEntry;
private FileStatus _status;
private Span<char> _pathBuffer;
private ReadOnlySpan<char> _fullPath;
private ReadOnlySpan<char> _fileName;
private fixed char _fileNameBuffer[FileNameBufferSize];
private FileAttributes _initialAttributes;
internal static FileAttributes Initialize(
ref FileSystemEntry entry,
Interop.Sys.DirectoryEntry directoryEntry,
ReadOnlySpan<char> directory,
ReadOnlySpan<char> rootDirectory,
ReadOnlySpan<char> originalRootDirectory,
Span<char> pathBuffer)
{
entry._directoryEntry = directoryEntry;
entry.Directory = directory;
entry.RootDirectory = rootDirectory;
entry.OriginalRootDirectory = originalRootDirectory;
entry._pathBuffer = pathBuffer;
entry._fullPath = ReadOnlySpan<char>.Empty;
entry._fileName = ReadOnlySpan<char>.Empty;
// IMPORTANT: Attribute logic must match the logic in FileStatus
bool isDirectory = false;
if (directoryEntry.InodeType == Interop.Sys.NodeType.DT_DIR)
{
// We know it's a directory.
isDirectory = true;
}
else if ((directoryEntry.InodeType == Interop.Sys.NodeType.DT_LNK
|| directoryEntry.InodeType == Interop.Sys.NodeType.DT_UNKNOWN)
&& (Interop.Sys.Stat(entry.FullPath, out Interop.Sys.FileStatus targetStatus) >= 0
|| Interop.Sys.LStat(entry.FullPath, out targetStatus) >= 0))
{
// Symlink or unknown: Stat to it to see if we can resolve it to a directory. If Stat fails,
// it could be because the symlink is broken, we don't have permissions, etc., in which
// case fall back to using LStat to evaluate based on the symlink itself.
isDirectory = (targetStatus.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFDIR;
}
entry._status = default;
FileStatus.Initialize(ref entry._status, isDirectory);
FileAttributes attributes = default;
if (directoryEntry.InodeType == Interop.Sys.NodeType.DT_LNK)
attributes |= FileAttributes.ReparsePoint;
if (isDirectory)
attributes |= FileAttributes.Directory;
if (directoryEntry.Name[0] == '.')
attributes |= FileAttributes.Hidden;
if (attributes == default)
attributes = FileAttributes.Normal;
entry._initialAttributes = attributes;
return attributes;
}
private ReadOnlySpan<char> FullPath
{
get
{
if (_fullPath.Length == 0)
{
Debug.Assert(Directory.Length + FileName.Length < _pathBuffer.Length,
$"directory ({Directory.Length} chars) & name ({Directory.Length} chars) too long for buffer ({_pathBuffer.Length} chars)");
Path.TryJoin(Directory, FileName, _pathBuffer, out int charsWritten);
Debug.Assert(charsWritten > 0, "didn't write any chars to buffer");
_fullPath = _pathBuffer.Slice(0, charsWritten);
}
return _fullPath;
}
}
public ReadOnlySpan<char> FileName
{
get
{
if (_directoryEntry.NameLength != 0 && _fileName.Length == 0)
{
fixed (char* c = _fileNameBuffer)
{
Span<char> buffer = new Span<char>(c, FileNameBufferSize);
_fileName = _directoryEntry.GetName(buffer);
}
}
return _fileName;
}
}
/// <summary>
/// The full path of the directory this entry resides in.
/// </summary>
public ReadOnlySpan<char> Directory { get; private set; }
/// <summary>
/// The full path of the root directory used for the enumeration.
/// </summary>
public ReadOnlySpan<char> RootDirectory { get; private set; }
/// <summary>
/// The root directory for the enumeration as specified in the constructor.
/// </summary>
public ReadOnlySpan<char> OriginalRootDirectory { get; private set; }
// Windows never fails getting attributes, length, or time as that information comes back
// with the native enumeration struct. As such we must not throw here.
public FileAttributes Attributes
// It would be hard to rationalize if the attributes change after our initial find.
=> _initialAttributes | (_status.IsReadOnly(FullPath, continueOnError: true) ? FileAttributes.ReadOnly : 0);
public long Length => _status.GetLength(FullPath, continueOnError: true);
public DateTimeOffset CreationTimeUtc => _status.GetCreationTime(FullPath, continueOnError: true);
public DateTimeOffset LastAccessTimeUtc => _status.GetLastAccessTime(FullPath, continueOnError: true);
public DateTimeOffset LastWriteTimeUtc => _status.GetLastWriteTime(FullPath, continueOnError: true);
public bool IsDirectory => _status.InitiallyDirectory;
public bool IsHidden => _directoryEntry.Name[0] == '.';
public FileSystemInfo ToFileSystemInfo()
{
string fullPath = ToFullPath();
return FileSystemInfo.Create(fullPath, new string(FileName), ref _status);
}
/// <summary>
/// Returns the full path of the find result.
/// </summary>
public string ToFullPath() =>
new string(FullPath);
}
}

View File

@@ -0,0 +1,84 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace System.IO.Enumeration
{
/// <summary>
/// Lower level view of FileSystemInfo used for processing and filtering find results.
/// </summary>
public unsafe ref partial struct FileSystemEntry
{
internal static void Initialize(
ref FileSystemEntry entry,
Interop.NtDll.FILE_FULL_DIR_INFORMATION* info,
ReadOnlySpan<char> directory,
ReadOnlySpan<char> rootDirectory,
ReadOnlySpan<char> originalRootDirectory)
{
entry._info = info;
entry.Directory = directory;
entry.RootDirectory = rootDirectory;
entry.OriginalRootDirectory = originalRootDirectory;
}
internal unsafe Interop.NtDll.FILE_FULL_DIR_INFORMATION* _info;
/// <summary>
/// The full path of the directory this entry resides in.
/// </summary>
public ReadOnlySpan<char> Directory { get; private set; }
/// <summary>
/// The full path of the root directory used for the enumeration.
/// </summary>
public ReadOnlySpan<char> RootDirectory { get; private set; }
/// <summary>
/// The root directory for the enumeration as specified in the constructor.
/// </summary>
public ReadOnlySpan<char> OriginalRootDirectory { get; private set; }
/// <summary>
/// The file name for this entry.
/// </summary>
public ReadOnlySpan<char> FileName => _info->FileName;
/// <summary>
/// The attributes for this entry.
/// </summary>
public FileAttributes Attributes => _info->FileAttributes;
/// <summary>
/// The length of file in bytes.
/// </summary>
public long Length => _info->EndOfFile;
/// <summary>
/// The creation time for the entry or the oldest available time stamp if the
/// operating system does not support creation time stamps.
/// </summary>
public DateTimeOffset CreationTimeUtc => _info->CreationTime.ToDateTimeOffset();
public DateTimeOffset LastAccessTimeUtc => _info->LastAccessTime.ToDateTimeOffset();
public DateTimeOffset LastWriteTimeUtc => _info->LastWriteTime.ToDateTimeOffset();
/// <summary>
/// Returns true if this entry is a directory.
/// </summary>
public bool IsDirectory => (Attributes & FileAttributes.Directory) != 0;
/// <summary>
/// Returns true if the file has the hidden attribute.
/// </summary>
public bool IsHidden => (Attributes & FileAttributes.Hidden) != 0;
public FileSystemInfo ToFileSystemInfo()
=> FileSystemInfo.Create(Path.Join(Directory, FileName), ref this);
/// <summary>
/// Returns the full path of the find result.
/// </summary>
public string ToFullPath() =>
Path.Join(Directory, FileName);
}
}

View File

@@ -0,0 +1,43 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace System.IO.Enumeration
{
public ref partial struct FileSystemEntry
{
/// <summary>
/// Returns the full path for find results, based on the initially provided path.
/// </summary>
public string ToSpecifiedFullPath()
{
// We want to provide the enumerated segment of the path appended to the originally specified path. This is
// the behavior of the various Directory APIs that return a list of strings.
//
// RootDirectory has the final separator trimmed, OriginalRootDirectory does not. Our legacy behavior would
// effectively account for this by appending subdirectory names as it recursed. As such we need to trim one
// separator when combining with the relative path (Directory.Slice(RootDirectory.Length)).
//
// Original => Root => Directory => FileName => relativePath => Specified
// C:\foo C:\foo C:\foo bar "" C:\foo\bar
// C:\foo\ C:\foo C:\foo bar "" C:\foo\bar
// C:\foo/ C:\foo C:\foo bar "" C:\foo/bar
// C:\foo\\ C:\foo C:\foo bar "" C:\foo\\bar
// C:\foo C:\foo C:\foo\bar jar "bar" C:\foo\bar\jar
// C:\foo\ C:\foo C:\foo\bar jar "bar" C:\foo\bar\jar
// C:\foo/ C:\foo C:\foo\bar jar "bar" C:\foo/bar\jar
// If we're at the top level directory the Directory and RootDirectory will be identical. As there are no
// trailing slashes in play, once we're in a subdirectory, slicing off the root will leave us with an
// initial separator. We need to trim that off if it exists, but it isn't needed if the original root
// didn't have a separator. Join() would handle it if we did trim it, not doing so is an optimization.
ReadOnlySpan<char> relativePath = Directory.Slice(RootDirectory.Length);
if (PathInternal.EndsInDirectorySeparator(OriginalRootDirectory) && PathInternal.StartsWithDirectorySeparator(relativePath))
relativePath = relativePath.Slice(1);
return Path.Join(OriginalRootDirectory, relativePath, FileName);
}
}
}

View File

@@ -0,0 +1,69 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections;
using System.Collections.Generic;
using System.Threading;
namespace System.IO.Enumeration
{
/// <summary>
/// Enumerable that allows utilizing custom filter predicates and tranform delegates.
/// </summary>
public class FileSystemEnumerable<TResult> : IEnumerable<TResult>
{
private DelegateEnumerator _enumerator;
private readonly FindTransform _transform;
private readonly EnumerationOptions _options;
private readonly string _directory;
public FileSystemEnumerable(string directory, FindTransform transform, EnumerationOptions options = null)
{
_directory = directory ?? throw new ArgumentNullException(nameof(directory));
_transform = transform ?? throw new ArgumentNullException(nameof(transform));
_options = options ?? EnumerationOptions.Default;
// We need to create the enumerator up front to ensure that we throw I/O exceptions for
// the root directory on creation of the enumerable.
_enumerator = new DelegateEnumerator(this);
}
public FindPredicate ShouldIncludePredicate { get; set; }
public FindPredicate ShouldRecursePredicate { get; set; }
public IEnumerator<TResult> GetEnumerator()
{
return Interlocked.Exchange(ref _enumerator, null) ?? new DelegateEnumerator(this);
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
/// <summary>
/// Delegate for filtering out find results.
/// </summary>
public delegate bool FindPredicate(ref FileSystemEntry entry);
/// <summary>
/// Delegate for transforming raw find data into a result.
/// </summary>
public delegate TResult FindTransform(ref FileSystemEntry entry);
private sealed class DelegateEnumerator : FileSystemEnumerator<TResult>
{
private readonly FileSystemEnumerable<TResult> _enumerable;
public DelegateEnumerator(FileSystemEnumerable<TResult> enumerable)
: base(enumerable._directory, enumerable._options)
{
_enumerable = enumerable;
}
protected override TResult TransformEntry(ref FileSystemEntry entry) => _enumerable._transform(ref entry);
protected override bool ShouldRecurseIntoEntry(ref FileSystemEntry entry)
=> _enumerable.ShouldRecursePredicate?.Invoke(ref entry) ?? true;
protected override bool ShouldIncludeEntry(ref FileSystemEntry entry)
=> _enumerable.ShouldIncludePredicate?.Invoke(ref entry) ?? true;
}
}
}

View File

@@ -0,0 +1,173 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file ref the project root for more information.
using System.Collections.Generic;
namespace System.IO.Enumeration
{
internal static class FileSystemEnumerableFactory
{
// These all have special meaning in DOS name matching. '\' is the escaping character (which conveniently
// is the directory separator and cannot be part of any path segment in Windows). The other three are the
// special case wildcards that we'll convert some * and ? into. They're also valid as filenames on Unix,
// which is not true in Windows and as such we'll escape any that occur on the input string.
private readonly static char[] s_unixEscapeChars = { '\\', '"', '<', '>' };
internal static void NormalizeInputs(ref string directory, ref string expression, EnumerationOptions options)
{
if (Path.IsPathRooted(expression))
throw new ArgumentException(SR.Arg_Path2IsRooted, nameof(expression));
// We always allowed breaking the passed ref directory and filter to be separated
// any way the user wanted. Looking for "C:\foo\*.cs" could be passed as "C:\" and
// "foo\*.cs" or "C:\foo" and "*.cs", for example. As such we need to combine and
// split the inputs if the expression contains a directory separator.
//
// We also allowed for expression to be "foo\" which would translate to "foo\*".
ReadOnlySpan<char> directoryName = Path.GetDirectoryName(expression.AsSpan());
if (directoryName.Length != 0)
{
// Need to fix up the input paths
directory = Path.Join(directory, directoryName);
expression = expression.Substring(directoryName.Length + 1);
}
switch (options.MatchType)
{
case MatchType.Win32:
if (string.IsNullOrEmpty(expression) || expression == "." || expression == "*.*")
{
// Historically we always treated "." as "*"
expression = "*";
}
else
{
if (Path.DirectorySeparatorChar != '\\' && expression.IndexOfAny(s_unixEscapeChars) != -1)
{
// Backslash isn't the default separator, need to escape (e.g. Unix)
expression = expression.Replace("\\", "\\\\");
// Also need to escape the other special wild characters ('"', '<', and '>')
expression = expression.Replace("\"", "\\\"");
expression = expression.Replace(">", "\\>");
expression = expression.Replace("<", "\\<");
}
// Need to convert the expression to match Win32 behavior
expression = FileSystemName.TranslateWin32Expression(expression);
}
break;
case MatchType.Simple:
break;
default:
throw new ArgumentOutOfRangeException(nameof(options));
}
}
private static bool MatchesPattern(string expression, ReadOnlySpan<char> name, EnumerationOptions options)
{
bool ignoreCase = (options.MatchCasing == MatchCasing.PlatformDefault && !PathInternal.IsCaseSensitive)
|| options.MatchCasing == MatchCasing.CaseInsensitive;
switch (options.MatchType)
{
case MatchType.Simple:
return FileSystemName.MatchesSimpleExpression(expression, name, ignoreCase);
case MatchType.Win32:
return FileSystemName.MatchesWin32Expression(expression, name, ignoreCase);
default:
throw new ArgumentOutOfRangeException(nameof(options));
}
}
internal static IEnumerable<string> UserFiles(string directory,
string expression,
EnumerationOptions options)
{
return new FileSystemEnumerable<string>(
directory,
(ref FileSystemEntry entry) => entry.ToSpecifiedFullPath(),
options)
{
ShouldIncludePredicate = (ref FileSystemEntry entry) =>
!entry.IsDirectory && MatchesPattern(expression, entry.FileName, options)
};
}
internal static IEnumerable<string> UserDirectories(string directory,
string expression,
EnumerationOptions options)
{
return new FileSystemEnumerable<string>(
directory,
(ref FileSystemEntry entry) => entry.ToSpecifiedFullPath(),
options)
{
ShouldIncludePredicate = (ref FileSystemEntry entry) =>
entry.IsDirectory && MatchesPattern(expression, entry.FileName, options)
};
}
internal static IEnumerable<string> UserEntries(string directory,
string expression,
EnumerationOptions options)
{
return new FileSystemEnumerable<string>(
directory,
(ref FileSystemEntry entry) => entry.ToSpecifiedFullPath(),
options)
{
ShouldIncludePredicate = (ref FileSystemEntry entry) =>
MatchesPattern(expression, entry.FileName, options)
};
}
internal static IEnumerable<FileInfo> FileInfos(
string directory,
string expression,
EnumerationOptions options)
{
return new FileSystemEnumerable<FileInfo>(
directory,
(ref FileSystemEntry entry) => (FileInfo)entry.ToFileSystemInfo(),
options)
{
ShouldIncludePredicate = (ref FileSystemEntry entry) =>
!entry.IsDirectory && MatchesPattern(expression, entry.FileName, options)
};
}
internal static IEnumerable<DirectoryInfo> DirectoryInfos(
string directory,
string expression,
EnumerationOptions options)
{
return new FileSystemEnumerable<DirectoryInfo>(
directory,
(ref FileSystemEntry entry) => (DirectoryInfo)entry.ToFileSystemInfo(),
options)
{
ShouldIncludePredicate = (ref FileSystemEntry entry) =>
entry.IsDirectory && MatchesPattern(expression, entry.FileName, options)
};
}
internal static IEnumerable<FileSystemInfo> FileSystemInfos(
string directory,
string expression,
EnumerationOptions options)
{
return new FileSystemEnumerable<FileSystemInfo>(
directory,
(ref FileSystemEntry entry) => entry.ToFileSystemInfo(),
options)
{
ShouldIncludePredicate = (ref FileSystemEntry entry) =>
MatchesPattern(expression, entry.FileName, options)
};
}
}
}

View File

@@ -0,0 +1,230 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Buffers;
using System.Collections.Generic;
using System.Runtime.ConstrainedExecution;
using System.Threading;
namespace System.IO.Enumeration
{
public unsafe abstract partial class FileSystemEnumerator<TResult> : CriticalFinalizerObject, IEnumerator<TResult>
{
// The largest supported path on Unix is 4K bytes of UTF-8 (most only support 1K)
private const int StandardBufferSize = 4096;
private readonly string _originalRootDirectory;
private readonly string _rootDirectory;
private readonly EnumerationOptions _options;
private readonly object _lock = new object();
private string _currentPath;
private IntPtr _directoryHandle;
private bool _lastEntryFound;
private Queue<string> _pending;
private Interop.Sys.DirectoryEntry _entry;
private TResult _current;
// Used for creating full paths
private char[] _pathBuffer;
// Used to get the raw entry data
private byte[] _entryBuffer;
/// <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 = PathInternal.TrimEndingDirectorySeparator(Path.GetFullPath(directory));
_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 == IntPtr.Zero)
_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;
}
}
private bool InternalContinueOnError(int error)
=> (_options.IgnoreInaccessible && IsAccessError(error)) || ContinueOnError(error);
private static bool IsAccessError(int error)
=> error == (int)Interop.Error.EACCES || error == (int)Interop.Error.EBADF
|| error == (int)Interop.Error.EPERM;
private IntPtr CreateDirectoryHandle(string path)
{
IntPtr handle = Interop.Sys.OpenDir(path);
if (handle == IntPtr.Zero)
{
Interop.ErrorInfo info = Interop.Sys.GetLastErrorInfo();
if (InternalContinueOnError(info.RawErrno))
{
return IntPtr.Zero;
}
throw Interop.GetExceptionForIoErrno(info, path, isDirectory: true);
}
return handle;
}
private void CloseDirectoryHandle()
{
IntPtr handle = Interlocked.Exchange(ref _directoryHandle, IntPtr.Zero);
if (handle != IntPtr.Zero)
Interop.Sys.CloseDir(handle);
}
public bool MoveNext()
{
if (_lastEntryFound)
return false;
FileSystemEntry entry = default;
lock (_lock)
{
if (_lastEntryFound)
return false;
// If HAVE_READDIR_R is defined for the platform FindNextEntry depends on _entryBuffer being fixed since
// _entry will point to a string in the middle of the array. If the array is not fixed GC can move it after
// the native call and _entry will point to a bogus file name.
fixed (byte* _ = _entryBuffer)
{
do
{
FindNextEntry();
if (_lastEntryFound)
return false;
FileAttributes attributes = FileSystemEntry.Initialize(
ref entry, _entry, _currentPath, _rootDirectory, _originalRootDirectory, new Span<char>(_pathBuffer));
bool isDirectory = (attributes & FileAttributes.Directory) != 0;
bool isSpecialDirectory = false;
if (isDirectory)
{
// Subdirectory found
if (_entry.Name[0] == '.' && (_entry.Name[1] == 0 || (_entry.Name[1] == '.' && _entry.Name[2] == 0)))
{
// "." or "..", don't process unless the option is set
if (!_options.ReturnSpecialDirectories)
continue;
isSpecialDirectory = true;
}
}
if (!isSpecialDirectory && _options.AttributesToSkip != 0)
{
if ((_options.AttributesToSkip & FileAttributes.ReadOnly) != 0)
{
// ReadOnly is the only attribute that requires hitting entry.Attributes (which hits the disk)
attributes = entry.Attributes;
}
if ((_options.AttributesToSkip & attributes) != 0)
{
continue;
}
}
if (isDirectory && !isSpecialDirectory)
{
if (_options.RecurseSubdirectories && ShouldRecurseIntoEntry(ref entry))
{
// Recursion is on and the directory was accepted, Queue it
if (_pending == null)
_pending = new Queue<string>();
_pending.Enqueue(Path.Join(_currentPath, entry.FileName));
}
}
if (ShouldIncludeEntry(ref entry))
{
_current = TransformEntry(ref entry);
return true;
}
} while (true);
}
}
}
private unsafe void FindNextEntry()
{
Span<byte> buffer = _entryBuffer == null ? Span<byte>.Empty : new Span<byte>(_entryBuffer);
int result = Interop.Sys.ReadDir(_directoryHandle, buffer, ref _entry);
switch (result)
{
case -1:
// End of directory
DirectoryFinished();
break;
case 0:
// Success
break;
default:
// Error
if (InternalContinueOnError(result))
{
DirectoryFinished();
break;
}
else
{
throw Interop.GetExceptionForIoErrno(new Interop.ErrorInfo(result), _currentPath, isDirectory: true);
}
}
}
private void DequeueNextDirectory()
{
_currentPath = _pending.Dequeue();
_directoryHandle = CreateDirectoryHandle(_currentPath);
}
private void InternalDispose(bool disposing)
{
// It is possible to fail to allocate the lock, but the finalizer will still run
if (_lock != null)
{
lock(_lock)
{
_lastEntryFound = true;
_pending = null;
CloseDirectoryHandle();
if (_pathBuffer != null)
ArrayPool<char>.Shared.Return(_pathBuffer);
_pathBuffer = null;
if (_entryBuffer != null)
ArrayPool<byte>.Shared.Return(_entryBuffer);
_entryBuffer = null;
}
}
Dispose(disposing);
}
}
}

View File

@@ -0,0 +1,78 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace System.IO.Enumeration
{
public partial class FileSystemEnumerator<TResult>
{
/// <returns>'true' if new data was found</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private unsafe bool GetData()
{
Debug.Assert(_directoryHandle != (IntPtr)(-1) && _directoryHandle != IntPtr.Zero && !_lastEntryFound);
int status = Interop.NtDll.NtQueryDirectoryFile(
FileHandle: _directoryHandle,
Event: IntPtr.Zero,
ApcRoutine: IntPtr.Zero,
ApcContext: IntPtr.Zero,
IoStatusBlock: out Interop.NtDll.IO_STATUS_BLOCK statusBlock,
FileInformation: _buffer,
Length: (uint)_buffer.Length,
FileInformationClass: Interop.NtDll.FILE_INFORMATION_CLASS.FileFullDirectoryInformation,
ReturnSingleEntry: Interop.BOOLEAN.FALSE,
FileName: null,
RestartScan: Interop.BOOLEAN.FALSE);
switch ((uint)status)
{
case Interop.StatusOptions.STATUS_NO_MORE_FILES:
DirectoryFinished();
return false;
case Interop.StatusOptions.STATUS_SUCCESS:
Debug.Assert(statusBlock.Information.ToInt64() != 0);
return true;
default:
int error = (int)Interop.NtDll.RtlNtStatusToDosError(status);
// Note that there are many NT status codes that convert to ERROR_ACCESS_DENIED.
if ((error == Interop.Errors.ERROR_ACCESS_DENIED && _options.IgnoreInaccessible) || ContinueOnError(error))
{
DirectoryFinished();
return false;
}
throw Win32Marshal.GetExceptionForWin32Error(error, _currentPath);
}
}
private IntPtr CreateRelativeDirectoryHandle(ReadOnlySpan<char> relativePath, string fullPath)
{
(int status, IntPtr handle) = Interop.NtDll.CreateFile(
relativePath,
_directoryHandle,
Interop.NtDll.CreateDisposition.FILE_OPEN,
Interop.NtDll.DesiredAccess.FILE_LIST_DIRECTORY | Interop.NtDll.DesiredAccess.SYNCHRONIZE,
createOptions: Interop.NtDll.CreateOptions.FILE_SYNCHRONOUS_IO_NONALERT | Interop.NtDll.CreateOptions.FILE_DIRECTORY_FILE
| Interop.NtDll.CreateOptions.FILE_OPEN_FOR_BACKUP_INTENT);
switch ((uint)status)
{
case Interop.StatusOptions.STATUS_SUCCESS:
return handle;
default:
int error = (int)Interop.NtDll.RtlNtStatusToDosError(status);
// Note that there are many NT status codes that convert to ERROR_ACCESS_DENIED.
if ((error == Interop.Errors.ERROR_ACCESS_DENIED && _options.IgnoreInaccessible) || ContinueOnError(error))
{
return IntPtr.Zero;
}
throw Win32Marshal.GetExceptionForWin32Error(error, fullPath);
}
}
}
}

View File

@@ -5,12 +5,12 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace System.IO
namespace System.IO.Enumeration
{
internal partial class FindEnumerable<TResult, TState>
public partial class FileSystemEnumerator<TResult>
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe bool GetData()
private unsafe bool GetData()
{
if (!Interop.Kernel32.GetFileInformationByHandleEx(
_directoryHandle,
@@ -24,12 +24,26 @@ namespace System.IO
case Interop.Errors.ERROR_NO_MORE_FILES:
DirectoryFinished();
return false;
default:
throw Win32Marshal.GetExceptionForWin32Error(error, _currentPath);
case Interop.Errors.ERROR_ACCESS_DENIED:
if (_options.IgnoreInaccessible)
{
return false;
}
break;
}
if (!ContinueOnError(error))
throw Win32Marshal.GetExceptionForWin32Error(error, _currentPath);
}
return true;
}
private IntPtr CreateRelativeDirectoryHandle(ReadOnlySpan<char> relativePath, string fullPath)
{
// We don't have access to any APIs that allow us to pass in a base handle in UAP,
// just call our "normal" handle open.
return CreateDirectoryHandle(fullPath);
}
}
}

View File

@@ -0,0 +1,234 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Buffers;
using System.Collections.Generic;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Threading;
namespace System.IO.Enumeration
{
public unsafe abstract partial class FileSystemEnumerator<TResult> : CriticalFinalizerObject, IEnumerator<TResult>
{
private const int StandardBufferSize = 4096;
// We need to have enough room for at least a single entry. The filename alone can be 512 bytes, we'll ensure we have
// a reasonable buffer for all of the other metadata as well.
private const int MinimumBufferSize = 1024;
private readonly string _originalRootDirectory;
private readonly string _rootDirectory;
private readonly EnumerationOptions _options;
private readonly object _lock = new object();
private Interop.NtDll.FILE_FULL_DIR_INFORMATION* _entry;
private TResult _current;
private byte[] _buffer;
private IntPtr _directoryHandle;
private string _currentPath;
private bool _lastEntryFound;
private Queue<(IntPtr Handle, string Path)> _pending;
private GCHandle _pinnedBuffer;
/// <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 = PathInternal.TrimEndingDirectorySeparator(Path.GetFullPath(directory));
_options = options ?? EnumerationOptions.Default;
// We'll only suppress the media insertion prompt on the topmost directory as that is the
// most likely scenario and we don't want to take the perf hit for large enumerations.
// (We weren't consistent with how we handled this historically.)
using (new DisableMediaInsertionPrompt())
{
// 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 == IntPtr.Zero)
_lastEntryFound = true;
}
_currentPath = _rootDirectory;
int requestedBufferSize = _options.BufferSize;
int bufferSize = requestedBufferSize <= 0 ? StandardBufferSize
: Math.Max(MinimumBufferSize, requestedBufferSize);
try
{
_buffer = ArrayPool<byte>.Shared.Rent(bufferSize);
_pinnedBuffer = GCHandle.Alloc(_buffer, GCHandleType.Pinned);
}
catch
{
// Close the directory handle right away if we fail to allocate
CloseDirectoryHandle();
throw;
}
}
private void CloseDirectoryHandle()
{
// As handles can be reused we want to be extra careful to close handles only once
IntPtr handle = Interlocked.Exchange(ref _directoryHandle, IntPtr.Zero);
if (handle != IntPtr.Zero)
Interop.Kernel32.CloseHandle(handle);
}
/// <summary>
/// Simple wrapper to allow creating a file handle for an existing directory.
/// </summary>
private IntPtr CreateDirectoryHandle(string path)
{
IntPtr handle = Interop.Kernel32.CreateFile_IntPtr(
path,
Interop.Kernel32.FileOperations.FILE_LIST_DIRECTORY,
FileShare.ReadWrite | FileShare.Delete,
FileMode.Open,
Interop.Kernel32.FileOperations.FILE_FLAG_BACKUP_SEMANTICS);
if (handle == IntPtr.Zero || handle == (IntPtr)(-1))
{
int error = Marshal.GetLastWin32Error();
if ((error == Interop.Errors.ERROR_ACCESS_DENIED &&
_options.IgnoreInaccessible) || ContinueOnError(error))
{
return IntPtr.Zero;
}
if (error == Interop.Errors.ERROR_FILE_NOT_FOUND)
{
// Historically we throw directory not found rather than file not found
error = Interop.Errors.ERROR_PATH_NOT_FOUND;
}
throw Win32Marshal.GetExceptionForWin32Error(error, path);
}
return handle;
}
public bool MoveNext()
{
if (_lastEntryFound)
return false;
FileSystemEntry entry = default;
lock (_lock)
{
if (_lastEntryFound)
return false;
do
{
FindNextEntry();
if (_lastEntryFound)
return false;
// Calling the constructor inside the try block would create a second instance on the stack.
FileSystemEntry.Initialize(ref entry, _entry, _currentPath, _rootDirectory, _originalRootDirectory);
// Skip specified attributes
if ((_entry->FileAttributes & _options.AttributesToSkip) != 0)
continue;
if ((_entry->FileAttributes & FileAttributes.Directory) != 0)
{
// Subdirectory found
if (!(_entry->FileName.Length > 2 || _entry->FileName[0] != '.' || (_entry->FileName.Length == 2 && _entry->FileName[1] != '.')))
{
// "." or "..", don't process unless the option is set
if (!_options.ReturnSpecialDirectories)
continue;
}
else if (_options.RecurseSubdirectories && ShouldRecurseIntoEntry(ref entry))
{
// Recursion is on and the directory was accepted, Queue it
string subDirectory = Path.Join(_currentPath, _entry->FileName);
IntPtr subDirectoryHandle = CreateRelativeDirectoryHandle(_entry->FileName, subDirectory);
if (subDirectoryHandle != IntPtr.Zero)
{
try
{
if (_pending == null)
_pending = new Queue<(IntPtr, string)>();
_pending.Enqueue((subDirectoryHandle, subDirectory));
}
catch
{
// Couldn't queue the handle, close it and rethrow
Interop.Kernel32.CloseHandle(subDirectoryHandle);
throw;
}
}
}
}
if (ShouldIncludeEntry(ref entry))
{
_current = TransformEntry(ref entry);
return true;
}
} while (true);
}
}
private unsafe void FindNextEntry()
{
_entry = Interop.NtDll.FILE_FULL_DIR_INFORMATION.GetNextInfo(_entry);
if (_entry != null)
return;
// We need more data
if (GetData())
_entry = (Interop.NtDll.FILE_FULL_DIR_INFORMATION*)_pinnedBuffer.AddrOfPinnedObject();
}
private void DequeueNextDirectory()
{
(_directoryHandle, _currentPath) = _pending.Dequeue();
}
private void InternalDispose(bool disposing)
{
// It is possible to fail to allocate the lock, but the finalizer will still run
if (_lock != null)
{
lock (_lock)
{
_lastEntryFound = true;
CloseDirectoryHandle();
if (_pending != null)
{
while (_pending.Count > 0)
Interop.Kernel32.CloseHandle(_pending.Dequeue().Handle);
_pending = null;
}
if (_pinnedBuffer.IsAllocated)
_pinnedBuffer.Free();
if (_buffer != null)
ArrayPool<byte>.Shared.Return(_buffer);
_buffer = null;
}
}
Dispose(disposing);
}
}
}

View File

@@ -0,0 +1,89 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections;
using System.Collections.Generic;
using System.Runtime.ConstrainedExecution;
namespace System.IO.Enumeration
{
public unsafe abstract partial class FileSystemEnumerator<TResult> : CriticalFinalizerObject, IEnumerator<TResult>
{
/// <summary>
/// Return true if the given file system entry should be included in the results.
/// </summary>
protected virtual bool ShouldIncludeEntry(ref FileSystemEntry entry) => true;
/// <summary>
/// Return true if the directory entry given should be recursed into.
/// </summary>
protected virtual bool ShouldRecurseIntoEntry(ref FileSystemEntry entry) => true;
/// <summary>
/// Generate the result type from the current entry;
/// </summary>
protected abstract TResult TransformEntry(ref FileSystemEntry entry);
/// <summary>
/// Called whenever the end of a directory is reached.
/// </summary>
/// <param name="directory">The path of the directory that finished.</param>
protected virtual void OnDirectoryFinished(ReadOnlySpan<char> directory) { }
/// <summary>
/// Called when a native API returns an error. Return true to continue, or false
/// to throw the default exception for the given error.
/// </summary>
/// <param name="error">The native error code.</param>
protected virtual bool ContinueOnError(int error) => false;
public TResult Current => _current;
object IEnumerator.Current => Current;
private void DirectoryFinished()
{
_entry = default;
// Close the handle now that we're done
CloseDirectoryHandle();
OnDirectoryFinished(_currentPath);
if (_pending == null || _pending.Count == 0)
{
_lastEntryFound = true;
}
else
{
// Grab the next directory to parse
DequeueNextDirectory();
FindNextEntry();
}
}
public void Reset()
{
throw new NotSupportedException();
}
public void Dispose()
{
InternalDispose(disposing: true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Override for any additional cleanup.
/// </summary>
/// <param name="disposing">True if called while disposing. False if called from finalizer.</param>
protected virtual void Dispose(bool disposing)
{
}
~FileSystemEnumerator()
{
InternalDispose(disposing: false);
}
}
}

Some files were not shown because too many files have changed in this diff Show More