Fix NetStandard issue in System.IO.FileSystem.AccessControl in 5.0 (dotnet/corefx...
authorCarlos Sanchez Lopez <1175054+carlossanlop@users.noreply.github.com>
Wed, 13 Nov 2019 20:26:12 +0000 (12:26 -0800)
committerGitHub <noreply@github.com>
Wed, 13 Nov 2019 20:26:12 +0000 (12:26 -0800)
Fix NetStandard issue in System.IO.FileSystem.AccessControl

We built the fix incorrectly so that it would only apply for netcoreapp3.0 but customer needs it on netstandard2.0 (desktop + core).  Customer impact is that they’ll see a PlatformNotSupported exception rather than the new API.

Commit migrated from https://github.com/dotnet/corefx/commit/70150e6750c35c9a67803e9cddfda7b1db2ec487

src/libraries/Common/src/System/IO/FileSystem.Attributes.Windows.cs
src/libraries/Common/src/System/IO/FileSystem.DirectoryCreation.Windows.cs
src/libraries/Common/src/System/IO/PathInternal.Unix.cs
src/libraries/System.IO.FileSystem.AccessControl/src/System.IO.FileSystem.AccessControl.csproj
src/libraries/System.IO.FileSystem.AccessControl/src/System/IO/FileSystemAclExtensions.cs
src/libraries/System.IO.FileSystem.AccessControl/src/System/IO/FileSystemAclExtensions.netcoreapp.cs [deleted file]
src/libraries/System.IO.FileSystem.AccessControl/src/System/IO/FileSystemAclExtensions.netstandard.cs [deleted file]

index be7ddee..26cefbc 100644 (file)
@@ -57,7 +57,7 @@ namespace System.IO
             int errorCode = Interop.Errors.ERROR_SUCCESS;
 
             // Neither GetFileAttributes or FindFirstFile like trailing separators
-            path = Path.TrimEndingDirectorySeparator(path);
+            path = PathInternal.TrimEndingDirectorySeparator(path);
 
             using (DisableMediaInsertionPrompt.Create())
             {
index bd77c7e..f127e3d 100644 (file)
@@ -42,7 +42,7 @@ namespace System.IO
             int length = fullPath.Length;
 
             // We need to trim the trailing slash or the code will try to create 2 directories of the same name.
-            if (length >= 2 && Path.EndsInDirectorySeparator(fullPath.AsSpan()))
+            if (length >= 2 && PathInternal.EndsInDirectorySeparator(fullPath.AsSpan()))
             {
                 length--;
             }
index db0e38d..8aff492 100644 (file)
@@ -35,7 +35,6 @@ namespace System.IO
             return c == Path.DirectorySeparatorChar;
         }
 
-
         internal static bool IsPartiallyQualified(string path)
         {
             // This is much simpler than Windows where paths can be rooted, but not fully qualified (such as Drive Relative)
index 2a0038b..dbd19fd 100644 (file)
@@ -4,7 +4,11 @@
     <IsPartialFacadeAssembly Condition="'$(TargetsNetFx)' == 'true'">true</IsPartialFacadeAssembly>
     <GeneratePlatformNotSupportedAssemblyMessage Condition="'$(TargetsWindows)' != 'true'">SR.PlatformNotSupported_AccessControl</GeneratePlatformNotSupportedAssemblyMessage>
     <Configurations>net461-Windows_NT-Debug;net461-Windows_NT-Release;netcoreapp-Windows_NT-Debug;netcoreapp-Windows_NT-Release;netfx-Windows_NT-Debug;netfx-Windows_NT-Release;netstandard2.0-Debug;netstandard2.0-Release;netstandard2.0-Windows_NT-Debug;netstandard2.0-Windows_NT-Release</Configurations>
+    <Nullable>annotations</Nullable>
   </PropertyGroup>
+  <ItemGroup Condition="'$(TargetsNetCoreApp)' != 'true'">
+    <Compile Include="$(CommonPath)\System\Runtime\InteropServices\SuppressGCTransitionAttribute.internal.cs" />
+  </ItemGroup>
   <!-- Source includes -->
   <ItemGroup Condition="'$(TargetsWindows)' == 'true' and '$(TargetsNetFx)' != 'true'">
     <Compile Include="$(CommonPath)\Interop\Windows\Interop.Errors.cs" Link="Common\Interop\Windows\Interop.Errors.cs" />
     <Compile Include="System\Security\AccessControl\FileSystemAuditRule.cs" />
     <Compile Include="System\Security\AccessControl\FileSystemRights.cs" />
     <Compile Include="System\Security\AccessControl\FileSystemSecurity.cs" />
-  </ItemGroup>
-  <ItemGroup Condition="'$(TargetsNetCoreApp)' == 'true'">
-    <Compile Include="$(CoreLibDir)Interop\Windows\Interop.BOOL.cs" Link="Interop\Windows\Interop.BOOL.cs" />
-    <Compile Include="$(CoreLibDir)Interop\Windows\Kernel32\Interop.CreateFile.cs" Link="Interop\Windows\Interop.CreateFile.cs" />
-    <Compile Include="$(CoreLibDir)Interop\Windows\Kernel32\Interop.FILE_TIME.cs" Link="Interop\Windows\Interop.FILE_TIME.cs" />
-    <Compile Include="$(CoreLibDir)Interop\Windows\Kernel32\Interop.FileAttributes.cs" Link="Common\Interop\Windows\Interop.FileAttributes.cs" />
-    <Compile Include="$(CoreLibDir)Interop\Windows\Kernel32\Interop.FileTypes.cs" Link="Common\Interop\Windows\Interop.FileTypes.cs" />
-    <Compile Include="$(CoreLibDir)Interop\Windows\Kernel32\Interop.FindClose.cs" Link="Interop\Windows\Interop.FindClose.cs" />
-    <Compile Include="$(CoreLibDir)Interop\Windows\Kernel32\Interop.FindFirstFileEx.cs" Link="Interop\Windows\Interop.FindFirstFileEx.cs" />
-    <Compile Include="$(CoreLibDir)Interop\Windows\Kernel32\Interop.FormatMessage.cs" Link="Common\Interop\Windows\Interop.FormatMessage.cs" />
-    <Compile Include="$(CoreLibDir)Interop\Windows\Kernel32\Interop.GET_FILEEX_INFO_LEVELS.cs" Link="Interop\Windows\Interop.GET_FILEEX_INFO_LEVELS.cs" />
-    <Compile Include="$(CoreLibDir)Interop\Windows\Kernel32\Interop.GetFileAttributesEx.cs" Link="Common\Interop\Windows\Interop.GetFileAttributesEx.cs" />
-    <Compile Include="$(CoreLibDir)Interop\Windows\Kernel32\Interop.GetFileType_SafeHandle.cs" Link="Common\Interop\Windows\Interop.GetFileType_SafeHandle.cs" />
-    <Compile Include="$(CoreLibDir)Interop\Windows\Kernel32\Interop.GetFullPathNameW.cs" Link="Common\Interop\Windows\Interop.GetFullPathNameW.cs" />
-    <Compile Include="$(CoreLibDir)Interop\Windows\Kernel32\Interop.GetLongPathNameW.cs" Link="Common\Interop\Windows\Interop.GetLongPathNameW.cs" />
-    <Compile Include="$(CoreLibDir)Interop\Windows\Kernel32\Interop.GetLogicalDrives.cs" Link="Common\Interop\Windows\Interop.GetLogicalDrives.cs" />
-    <Compile Include="$(CoreLibDir)Interop\Windows\Kernel32\Interop.MAX_PATH.cs" Link="Interop\Windows\Interop.MAX_PATH.cs" />
-    <Compile Include="$(CoreLibDir)Interop\Windows\Kernel32\Interop.SecurityOptions.cs" Link="Interop\Windows\Interop.SecurityOptions.cs" />
-    <Compile Include="$(CoreLibDir)Interop\Windows\Kernel32\Interop.SECURITY_ATTRIBUTES.cs" Link="Interop\Windows\Interop.SECURITY_ATTRIBUTES.cs" />
-    <Compile Include="$(CoreLibDir)Interop\Windows\Kernel32\Interop.SetThreadErrorMode.cs" Link="Interop\Windows\Interop.SetThreadErrorMode.cs" />
-    <Compile Include="$(CoreLibDir)Interop\Windows\Kernel32\Interop.WIN32_FILE_ATTRIBUTE_DATA.cs" Link="Interop\Windows\Interop.WIN32_FILE_ATTRIBUTE_DATA.cs" />
-    <Compile Include="$(CoreLibDir)Interop\Windows\Kernel32\Interop.WIN32_FIND_DATA.cs" Link="Interop\Windows\Interop.WIN32_FIND_DATA.cs" />
-    <Compile Include="$(CoreLibDir)System\IO\DisableMediaInsertionPrompt.cs" Link="Common\System\IO\DisableMediaInsertionPrompt.cs" />
-    <Compile Include="$(CoreLibDir)System\IO\PathHelper.Windows.cs" Link="System\IO\PathHelper.Windows.cs" />
-    <Compile Include="$(CoreLibDir)System\IO\PathInternal.Windows.cs" Link="System\IO\PathInternal.Windows.cs" />
-    <Compile Include="$(CoreLibDir)System\IO\Win32Marshal.cs" Link="System\IO\Win32Marshal.cs" />
-    <Compile Include="$(CoreLibDir)System\Text\ValueStringBuilder.cs" Link="System\Text\ValueStringBuilder.cs" />
+    <Compile Include="$(CommonPath)\CoreLib\Interop\Windows\Interop.BOOL.cs" Link="Common\CoreLib\Interop\Windows\Interop.BOOL.cs" />
+    <Compile Include="$(CommonPath)\CoreLib\Interop\Windows\Kernel32\Interop.CreateFile.cs" Link="Common\CoreLib\Interop\Windows\Interop.CreateFile.cs" />
+    <Compile Include="$(CommonPath)\CoreLib\Interop\Windows\Kernel32\Interop.FILE_TIME.cs" Link="Common\CoreLib\Interop\Windows\Interop.FILE_TIME.cs" />
+    <Compile Include="$(CommonPath)\CoreLib\Interop\Windows\Kernel32\Interop.FileAttributes.cs" Link="Common\Interop\Windows\Interop.FileAttributes.cs" />
+    <Compile Include="$(CommonPath)\CoreLib\Interop\Windows\Kernel32\Interop.FileTypes.cs" Link="Common\Interop\Windows\Interop.FileTypes.cs" />
+    <Compile Include="$(CommonPath)\CoreLib\Interop\Windows\Kernel32\Interop.FindClose.cs" Link="Common\CoreLib\Interop\Windows\Interop.FindClose.cs" />
+    <Compile Include="$(CommonPath)\CoreLib\Interop\Windows\Kernel32\Interop.FindFirstFileEx.cs" Link="Common\CoreLib\Interop\Windows\Interop.FindFirstFileEx.cs" />
+    <Compile Include="$(CommonPath)\CoreLib\Interop\Windows\Kernel32\Interop.FormatMessage.cs" Link="Common\Interop\Windows\Interop.FormatMessage.cs" />
+    <Compile Include="$(CommonPath)\CoreLib\Interop\Windows\Kernel32\Interop.GET_FILEEX_INFO_LEVELS.cs" Link="Common\CoreLib\Interop\Windows\Interop.GET_FILEEX_INFO_LEVELS.cs" />
+    <Compile Include="$(CommonPath)\CoreLib\Interop\Windows\Kernel32\Interop.GetFileAttributesEx.cs" Link="Common\Interop\Windows\Interop.GetFileAttributesEx.cs" />
+    <Compile Include="$(CommonPath)\CoreLib\Interop\Windows\Kernel32\Interop.GetFileType_SafeHandle.cs" Link="Common\Interop\Windows\Interop.GetFileType_SafeHandle.cs" />
+    <Compile Include="$(CommonPath)\CoreLib\Interop\Windows\Kernel32\Interop.GetFullPathNameW.cs" Link="Common\Interop\Windows\Interop.GetFullPathNameW.cs" />
+    <Compile Include="$(CommonPath)\CoreLib\Interop\Windows\Kernel32\Interop.GetLongPathNameW.cs" Link="Common\Interop\Windows\Interop.GetLongPathNameW.cs" />
+    <Compile Include="$(CommonPath)\CoreLib\Interop\Windows\Kernel32\Interop.GetLogicalDrives.cs" Link="Common\Interop\Windows\Interop.GetLogicalDrives.cs" />
+    <Compile Include="$(CommonPath)\CoreLib\Interop\Windows\Kernel32\Interop.MAX_PATH.cs" Link="Common\CoreLib\Interop\Windows\Interop.MAX_PATH.cs" />
+    <Compile Include="$(CommonPath)\CoreLib\Interop\Windows\Kernel32\Interop.SecurityOptions.cs" Link="Common\CoreLib\Interop\Windows\Interop.SecurityOptions.cs" />
+    <Compile Include="$(CommonPath)\CoreLib\Interop\Windows\Kernel32\Interop.SECURITY_ATTRIBUTES.cs" Link="Common\CoreLib\Interop\Windows\Interop.SECURITY_ATTRIBUTES.cs" />
+    <Compile Include="$(CommonPath)\CoreLib\Interop\Windows\Kernel32\Interop.SetThreadErrorMode.cs" Link="Common\CoreLib\Interop\Windows\Interop.SetThreadErrorMode.cs" />
+    <Compile Include="$(CommonPath)\CoreLib\Interop\Windows\Kernel32\Interop.WIN32_FILE_ATTRIBUTE_DATA.cs" Link="Common\CoreLib\Interop\Windows\Interop.WIN32_FILE_ATTRIBUTE_DATA.cs" />
+    <Compile Include="$(CommonPath)\CoreLib\Interop\Windows\Kernel32\Interop.WIN32_FIND_DATA.cs" Link="Common\CoreLib\Interop\Windows\Interop.WIN32_FIND_DATA.cs" />
+    <Compile Include="$(CommonPath)\CoreLib\System\IO\DisableMediaInsertionPrompt.cs" Link="Common\System\IO\DisableMediaInsertionPrompt.cs" />
+    <Compile Include="$(CommonPath)\CoreLib\System\IO\PathHelper.Windows.cs" Link="Common\CoreLib\System\IO\PathHelper.Windows.cs" />
+    <Compile Include="$(CommonPath)\CoreLib\System\IO\PathInternal.cs" Link="Common\CoreLib\System\IO\PathInternal.cs" />
+    <Compile Include="$(CommonPath)\CoreLib\System\IO\PathInternal.Windows.cs" Link="Common\CoreLib\System\IO\PathInternal.Windows.cs" />
+    <Compile Include="$(CommonPath)\CoreLib\System\IO\Win32Marshal.cs" Link="Common\CoreLib\System\IO\Win32Marshal.cs" />
+    <Compile Include="$(CommonPath)\CoreLib\System\Text\ValueStringBuilder.cs" Link="Common\CoreLib\System\Text\ValueStringBuilder.cs" />
     <Compile Include="$(CommonPath)\Interop\Windows\Kernel32\Interop.GenericOperations.cs" Link="Common\Interop\Windows\Interop.GenericOperations.cs" />
     <Compile Include="$(CommonPath)\Interop\Windows\Interop.Libraries.cs" Link="Common\Interop\Windows\Interop.Libraries.cs" />
     <Compile Include="$(CommonPath)\Interop\Windows\Interop.LongFileTime.cs" Link="Common\Interop\Windows\Interop.LongFileTime.cs" />
     <Compile Include="$(CommonPath)\Microsoft\Win32\SafeHandles\SafeFindHandle.Windows.cs" Link="Common\Microsoft\Win32\SafeHandles\SafeFindHandle.Windows.cs" />
     <Compile Include="$(CommonPath)\System\IO\FileSystem.Attributes.Windows.cs" Link="Common\System\IO\FileSystem.Attributes.Windows.cs" />
     <Compile Include="$(CommonPath)\System\IO\FileSystem.DirectoryCreation.Windows.cs" Link="Common\System\IO\FileSystem.DirectoryCreation.Windows.cs" />
-    <Compile Include="System\IO\FileSystemAclExtensions.netcoreapp.cs" />
   </ItemGroup>
   <ItemGroup Condition="'$(TargetsNetFx)' == 'true'">
     <Compile Include="System\IO\FileSystemAclExtensions.net46.cs" />
   </ItemGroup>
-  <ItemGroup Condition="'$(TargetsWindows)' == 'true' and '$(TargetsNetStandard)' == 'true'">
-    <Compile Include="System\IO\FileSystemAclExtensions.netstandard.cs" />
-  </ItemGroup>
   <!-- Reference includes -->
   <ItemGroup Condition="'$(TargetsWindows)' == 'true' and '$(TargetsNetFx)' != 'true'">
     <Reference Include="System.Buffers" />
index aaab98d..1de6cd4 100644 (file)
@@ -2,6 +2,8 @@
 // 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.InteropServices;
 using System.Security.AccessControl;
 using Microsoft.Win32.SafeHandles;
 
@@ -72,5 +74,166 @@ namespace System.IO
 
             fileSecurity.Persist(handle, fileStream.Name);
         }
+
+        /// <summary>Creates a new directory, ensuring it is created with the specified directory security. If the directory already exists, nothing is done.</summary>
+        /// <param name="directoryInfo">The object describing a directory that does not exist in disk yet.</param>
+        /// <param name="directorySecurity">An object that determines the access control and audit security for the directory.</param>
+        /// <exception cref="ArgumentNullException"><paramref name="directoryInfo" /> or <paramref name="directorySecurity" /> is <see langword="null" />.</exception>
+        /// <exception cref="DirectoryNotFoundException">Could not find a part of the path.</exception>
+        /// <exception cref="UnauthorizedAccessException">Access to the path is denied.</exception>
+        /// <remarks>This extension method was added to .NET Core to bring the functionality that was provided by the `System.IO.DirectoryInfo.Create(System.Security.AccessControl.DirectorySecurity)` .NET Framework method.</remarks>
+        public static void Create(this DirectoryInfo directoryInfo, DirectorySecurity directorySecurity)
+        {
+            if (directoryInfo == null)
+                throw new ArgumentNullException(nameof(directoryInfo));
+
+            if (directorySecurity == null)
+                throw new ArgumentNullException(nameof(directorySecurity));
+
+            FileSystem.CreateDirectory(directoryInfo.FullName, directorySecurity.GetSecurityDescriptorBinaryForm());
+        }
+
+        /// <summary>
+        /// Creates a new file stream, ensuring it is created with the specified properties and security settings.
+        /// </summary>
+        /// <param name="fileInfo">The current instance describing a file that does not exist in disk yet.</param>
+        /// <param name="mode">One of the enumeration values that specifies how the operating system should open a file.</param>
+        /// <param name="rights">One of the enumeration values that defines the access rights to use when creating access and audit rules.</param>
+        /// <param name="share">One of the enumeration values for controlling the kind of access other FileStream objects can have to the same file.</param>
+        /// <param name="bufferSize">The number of bytes buffered for reads and writes to the file.</param>
+        /// <param name="options">One of the enumeration values that describes how to create or overwrite the file.</param>
+        /// <param name="fileSecurity">An object that determines the access control and audit security for the file.</param>
+        /// <returns>A file stream for the newly created file.</returns>
+        /// <exception cref="ArgumentException">The <paramref name="rights" /> and <paramref name="mode" /> combination is invalid.</exception>
+        /// <exception cref="ArgumentNullException"><paramref name="fileInfo" /> or <paramref name="fileSecurity" /> is <see langword="null" />.</exception>
+        /// <exception cref="ArgumentOutOfRangeException"><paramref name="mode" /> or <paramref name="share" /> are out of their legal enum range.
+        ///-or-
+        /// <paramref name="bufferSize" /> is not a positive number.</exception>
+        /// <exception cref="DirectoryNotFoundException">Could not find a part of the path.</exception>
+        /// <exception cref="IOException">An I/O error occurs.</exception>
+        /// <exception cref="UnauthorizedAccessException">Access to the path is denied.</exception>
+        /// <remarks>This extension method was added to .NET Core to bring the functionality that was provided by the `System.IO.FileStream.#ctor(System.String,System.IO.FileMode,System.Security.AccessControl.FileSystemRights,System.IO.FileShare,System.Int32,System.IO.FileOptions,System.Security.AccessControl.FileSecurity)` .NET Framework constructor.</remarks>
+        public static FileStream Create(this FileInfo fileInfo, FileMode mode, FileSystemRights rights, FileShare share, int bufferSize, FileOptions options, FileSecurity fileSecurity)
+        {
+            if (fileInfo == null)
+            {
+                throw new ArgumentNullException(nameof(fileInfo));
+            }
+
+            if (fileSecurity == null)
+            {
+                throw new ArgumentNullException(nameof(fileSecurity));
+            }
+
+            // don't include inheritable in our bounds check for share
+            FileShare tempshare = share & ~FileShare.Inheritable;
+
+            if (mode < FileMode.CreateNew || mode > FileMode.Append)
+            {
+                throw new ArgumentOutOfRangeException(nameof(mode), SR.ArgumentOutOfRange_Enum);
+            }
+
+            if (tempshare < FileShare.None || tempshare > (FileShare.ReadWrite | FileShare.Delete))
+            {
+                throw new ArgumentOutOfRangeException(nameof(share), SR.ArgumentOutOfRange_Enum);
+            }
+
+            if (bufferSize <= 0)
+            {
+                throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum);
+            }
+
+            // Do not allow using combinations of non-writing file system rights with writing file modes
+            if ((rights & FileSystemRights.Write) == 0 &&
+                (mode == FileMode.Truncate || mode == FileMode.CreateNew || mode == FileMode.Create || mode == FileMode.Append))
+            {
+                throw new ArgumentException(SR.Format(SR.Argument_InvalidFileModeAndFileSystemRightsCombo, mode, rights));
+            }
+
+            SafeFileHandle handle = CreateFileHandle(fileInfo.FullName, mode, rights, share, options, fileSecurity);
+
+            try
+            {
+                return new FileStream(handle, GetFileStreamFileAccess(rights), bufferSize, (options & FileOptions.Asynchronous) != 0);
+            }
+            catch
+            {
+                // If anything goes wrong while setting up the stream, make sure we deterministically dispose of the opened handle.
+                handle.Dispose();
+                throw;
+            }
+        }
+
+        // In the context of a FileStream, the only ACCESS_MASK ACE rights we care about are reading/writing data and the generic read/write rights.
+        // See: https://docs.microsoft.com/en-us/windows/win32/secauthz/access-mask
+        private static FileAccess GetFileStreamFileAccess(FileSystemRights rights)
+        {
+            FileAccess access = 0;
+            if ((rights & FileSystemRights.ReadData) != 0 || ((int)rights & Interop.Kernel32.GenericOperations.GENERIC_READ) != 0)
+            {
+                access = FileAccess.Read;
+            }
+            if ((rights & FileSystemRights.WriteData) != 0 || ((int)rights & Interop.Kernel32.GenericOperations.GENERIC_WRITE) != 0)
+            {
+                access = access == FileAccess.Read ? FileAccess.ReadWrite : FileAccess.Write;
+            }
+            return access;
+        }
+
+        private static unsafe SafeFileHandle CreateFileHandle(string fullPath, FileMode mode, FileSystemRights rights, FileShare share, FileOptions options, FileSecurity security)
+        {
+            Debug.Assert(fullPath != null);
+
+            // Must use a valid Win32 constant
+            if (mode == FileMode.Append)
+            {
+                mode = FileMode.OpenOrCreate;
+            }
+
+            // For mitigating local elevation of privilege attack through named pipes make sure we always call CreateFile with SECURITY_ANONYMOUS so that the
+            // named pipe server can't impersonate a high privileged client security context (note that this is the effective default on CreateFile2)
+            // SECURITY_SQOS_PRESENT flags that a SECURITY_ flag is present.
+            int flagsAndAttributes = (int)options | Interop.Kernel32.SecurityOptions.SECURITY_SQOS_PRESENT | Interop.Kernel32.SecurityOptions.SECURITY_ANONYMOUS;
+
+            SafeFileHandle handle;
+
+            fixed (byte* pSecurityDescriptor = security.GetSecurityDescriptorBinaryForm())
+            {
+                var secAttrs = new Interop.Kernel32.SECURITY_ATTRIBUTES
+                {
+                    nLength = (uint)sizeof(Interop.Kernel32.SECURITY_ATTRIBUTES),
+                    bInheritHandle = ((share & FileShare.Inheritable) != 0) ? Interop.BOOL.TRUE : Interop.BOOL.FALSE,
+                    lpSecurityDescriptor = (IntPtr)pSecurityDescriptor
+                };
+
+                using (DisableMediaInsertionPrompt.Create())
+                {
+                    handle = Interop.Kernel32.CreateFile(fullPath, (int)rights, share, ref secAttrs, mode, flagsAndAttributes, IntPtr.Zero);
+                    ValidateFileHandle(handle, fullPath);
+                }
+            }
+
+            return handle;
+        }
+
+        private static void ValidateFileHandle(SafeFileHandle handle, string fullPath)
+        {
+            if (handle.IsInvalid)
+            {
+                // Return a meaningful exception with the full path.
+
+                // NT5 oddity - when trying to open "C:\" as a FileStream,
+                // we usually get ERROR_PATH_NOT_FOUND from the OS.  We should
+                // probably be consistent w/ every other directory.
+                int errorCode = Marshal.GetLastWin32Error();
+
+                if (errorCode == Interop.Errors.ERROR_PATH_NOT_FOUND && fullPath.Length == Path.GetPathRoot(fullPath).Length)
+                {
+                    errorCode = Interop.Errors.ERROR_ACCESS_DENIED;
+                }
+
+                throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath);
+            }
+        }
     }
 }
diff --git a/src/libraries/System.IO.FileSystem.AccessControl/src/System/IO/FileSystemAclExtensions.netcoreapp.cs b/src/libraries/System.IO.FileSystem.AccessControl/src/System/IO/FileSystemAclExtensions.netcoreapp.cs
deleted file mode 100644 (file)
index cd5afca..0000000
+++ /dev/null
@@ -1,175 +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;
-using System.Runtime.InteropServices;
-using System.Security.AccessControl;
-using Microsoft.Win32.SafeHandles;
-
-namespace System.IO
-{
-    public static partial class FileSystemAclExtensions
-    {
-        /// <summary>Creates a new directory, ensuring it is created with the specified directory security. If the directory already exists, nothing is done.</summary>
-        /// <param name="directoryInfo">The object describing a directory that does not exist in disk yet.</param>
-        /// <param name="directorySecurity">An object that determines the access control and audit security for the directory.</param>
-        /// <exception cref="ArgumentNullException"><paramref name="directoryInfo" /> or <paramref name="directorySecurity" /> is <see langword="null" />.</exception>
-        /// <exception cref="DirectoryNotFoundException">Could not find a part of the path.</exception>
-        /// <exception cref="UnauthorizedAccessException">Access to the path is denied.</exception>
-        /// <remarks>This extension method was added to .NET Core to bring the functionality that was provided by the `System.IO.DirectoryInfo.Create(System.Security.AccessControl.DirectorySecurity)` .NET Framework method.</remarks>
-        public static void Create(this DirectoryInfo directoryInfo, DirectorySecurity directorySecurity)
-        {
-            if (directoryInfo == null)
-                throw new ArgumentNullException(nameof(directoryInfo));
-
-            if (directorySecurity == null)
-                throw new ArgumentNullException(nameof(directorySecurity));
-
-            FileSystem.CreateDirectory(directoryInfo.FullName, directorySecurity.GetSecurityDescriptorBinaryForm());
-        }
-
-        /// <summary>
-        /// Creates a new file stream, ensuring it is created with the specified properties and security settings.
-        /// </summary>
-        /// <param name="fileInfo">The current instance describing a file that does not exist in disk yet.</param>
-        /// <param name="mode">One of the enumeration values that specifies how the operating system should open a file.</param>
-        /// <param name="rights">One of the enumeration values that defines the access rights to use when creating access and audit rules.</param>
-        /// <param name="share">One of the enumeration values for controlling the kind of access other FileStream objects can have to the same file.</param>
-        /// <param name="bufferSize">The number of bytes buffered for reads and writes to the file.</param>
-        /// <param name="options">One of the enumeration values that describes how to create or overwrite the file.</param>
-        /// <param name="fileSecurity">An object that determines the access control and audit security for the file.</param>
-        /// <returns>A file stream for the newly created file.</returns>
-        /// <exception cref="ArgumentException">The <paramref name="rights" /> and <paramref name="mode" /> combination is invalid.</exception>
-        /// <exception cref="ArgumentNullException"><paramref name="fileInfo" /> or <paramref name="fileSecurity" /> is <see langword="null" />.</exception>
-        /// <exception cref="ArgumentOutOfRangeException"><paramref name="mode" /> or <paramref name="share" /> are out of their legal enum range.
-        ///-or-
-        /// <paramref name="bufferSize" /> is not a positive number.</exception>
-        /// <exception cref="DirectoryNotFoundException">Could not find a part of the path.</exception>
-        /// <exception cref="IOException">An I/O error occurs.</exception>
-        /// <exception cref="UnauthorizedAccessException">Access to the path is denied.</exception>
-        /// <remarks>This extension method was added to .NET Core to bring the functionality that was provided by the `System.IO.FileStream.#ctor(System.String,System.IO.FileMode,System.Security.AccessControl.FileSystemRights,System.IO.FileShare,System.Int32,System.IO.FileOptions,System.Security.AccessControl.FileSecurity)` .NET Framework constructor.</remarks>
-        public static FileStream Create(this FileInfo fileInfo, FileMode mode, FileSystemRights rights, FileShare share, int bufferSize, FileOptions options, FileSecurity fileSecurity)
-        {
-            if (fileInfo == null)
-            {
-                throw new ArgumentNullException(nameof(fileInfo));
-            }
-
-            if (fileSecurity == null)
-            {
-                throw new ArgumentNullException(nameof(fileSecurity));
-            }
-
-            // don't include inheritable in our bounds check for share
-            FileShare tempshare = share & ~FileShare.Inheritable;
-
-            if (mode < FileMode.CreateNew || mode > FileMode.Append)
-            {
-                throw new ArgumentOutOfRangeException(nameof(mode), SR.ArgumentOutOfRange_Enum);
-            }
-
-            if (tempshare < FileShare.None || tempshare > (FileShare.ReadWrite | FileShare.Delete))
-            {
-                throw new ArgumentOutOfRangeException(nameof(share), SR.ArgumentOutOfRange_Enum);
-            }
-
-            if (bufferSize <= 0)
-            {
-                throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum);
-            }
-
-            // Do not allow using combinations of non-writing file system rights with writing file modes
-            if ((rights & FileSystemRights.Write) == 0 &&
-                (mode == FileMode.Truncate || mode == FileMode.CreateNew || mode == FileMode.Create || mode == FileMode.Append))
-            {
-                throw new ArgumentException(SR.Format(SR.Argument_InvalidFileModeAndFileSystemRightsCombo, mode, rights));
-            }
-
-            SafeFileHandle handle = CreateFileHandle(fileInfo.FullName, mode, rights, share, options, fileSecurity);
-
-            try
-            {
-                return new FileStream(handle, GetFileStreamFileAccess(rights), bufferSize, (options & FileOptions.Asynchronous) != 0);
-            }
-            catch
-            {
-                // If anything goes wrong while setting up the stream, make sure we deterministically dispose of the opened handle.
-                handle.Dispose();
-                throw;
-            }
-        }
-
-        // In the context of a FileStream, the only ACCESS_MASK ACE rights we care about are reading/writing data and the generic read/write rights.
-        // See: https://docs.microsoft.com/en-us/windows/win32/secauthz/access-mask
-        private static FileAccess GetFileStreamFileAccess(FileSystemRights rights)
-        {
-            FileAccess access = 0;
-            if ((rights & FileSystemRights.ReadData) != 0 || ((int)rights & Interop.Kernel32.GenericOperations.GENERIC_READ) != 0)
-            {
-                access = FileAccess.Read;
-            }
-            if ((rights & FileSystemRights.WriteData) != 0 || ((int)rights & Interop.Kernel32.GenericOperations.GENERIC_WRITE) != 0)
-            {
-                access = access == FileAccess.Read ? FileAccess.ReadWrite : FileAccess.Write;
-            }
-            return access;
-        }
-
-        private static unsafe SafeFileHandle CreateFileHandle(string fullPath, FileMode mode, FileSystemRights rights, FileShare share, FileOptions options, FileSecurity security)
-        {
-            Debug.Assert(fullPath != null);
-
-            // Must use a valid Win32 constant
-            if (mode == FileMode.Append)
-            {
-                mode = FileMode.OpenOrCreate;
-            }
-
-            // For mitigating local elevation of privilege attack through named pipes make sure we always call CreateFile with SECURITY_ANONYMOUS so that the
-            // named pipe server can't impersonate a high privileged client security context (note that this is the effective default on CreateFile2)
-            // SECURITY_SQOS_PRESENT flags that a SECURITY_ flag is present.
-            int flagsAndAttributes = (int)options | Interop.Kernel32.SecurityOptions.SECURITY_SQOS_PRESENT | Interop.Kernel32.SecurityOptions.SECURITY_ANONYMOUS;
-
-            SafeFileHandle handle;
-
-            fixed (byte* pSecurityDescriptor = security.GetSecurityDescriptorBinaryForm())
-            {
-                var secAttrs = new Interop.Kernel32.SECURITY_ATTRIBUTES
-                {
-                    nLength = (uint)sizeof(Interop.Kernel32.SECURITY_ATTRIBUTES),
-                    bInheritHandle = ((share & FileShare.Inheritable) != 0) ? Interop.BOOL.TRUE : Interop.BOOL.FALSE,
-                    lpSecurityDescriptor = (IntPtr)pSecurityDescriptor
-                };
-
-                using (DisableMediaInsertionPrompt.Create())
-                {
-                    handle = Interop.Kernel32.CreateFile(fullPath, (int)rights, share, ref secAttrs, mode, flagsAndAttributes, IntPtr.Zero);
-                    ValidateFileHandle(handle, fullPath);
-                }
-            }
-
-            return handle;
-        }
-
-        private static void ValidateFileHandle(SafeFileHandle handle, string fullPath)
-        {
-            if (handle.IsInvalid)
-            {
-                // Return a meaningful exception with the full path.
-
-                // NT5 oddity - when trying to open "C:\" as a FileStream,
-                // we usually get ERROR_PATH_NOT_FOUND from the OS.  We should
-                // probably be consistent w/ every other directory.
-                int errorCode = Marshal.GetLastWin32Error();
-
-                if (errorCode == Interop.Errors.ERROR_PATH_NOT_FOUND && fullPath.Length == Path.GetPathRoot(fullPath).Length)
-                {
-                    errorCode = Interop.Errors.ERROR_ACCESS_DENIED;
-                }
-
-                throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath);
-            }
-        }
-    }
-}
diff --git a/src/libraries/System.IO.FileSystem.AccessControl/src/System/IO/FileSystemAclExtensions.netstandard.cs b/src/libraries/System.IO.FileSystem.AccessControl/src/System/IO/FileSystemAclExtensions.netstandard.cs
deleted file mode 100644 (file)
index 4432071..0000000
+++ /dev/null
@@ -1,21 +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.Security.AccessControl;
-
-namespace System.IO
-{
-    public static partial class FileSystemAclExtensions
-    {
-        public static FileStream Create(this FileInfo fileInfo, FileMode mode, FileSystemRights rights, FileShare share, int bufferSize, FileOptions options, FileSecurity fileSecurity)
-        {
-            throw new PlatformNotSupportedException();
-        }
-
-        public static void Create(this DirectoryInfo directoryInfo, DirectorySecurity directorySecurity)
-        {
-            throw new PlatformNotSupportedException();
-        }
-    }
-}