diff options
Diffstat (limited to 'src/System.Private.CoreLib/shared/System/IO/FileStream.Windows.cs')
-rw-r--r-- | src/System.Private.CoreLib/shared/System/IO/FileStream.Windows.cs | 140 |
1 files changed, 51 insertions, 89 deletions
diff --git a/src/System.Private.CoreLib/shared/System/IO/FileStream.Windows.cs b/src/System.Private.CoreLib/shared/System/IO/FileStream.Windows.cs index 291a30bb5..ddbac1bf7 100644 --- a/src/System.Private.CoreLib/shared/System/IO/FileStream.Windows.cs +++ b/src/System.Private.CoreLib/shared/System/IO/FileStream.Windows.cs @@ -52,17 +52,29 @@ namespace System.IO private PreAllocatedOverlapped _preallocatedOverlapped; // optimization for async ops to avoid per-op allocations private FileStreamCompletionSource _currentOverlappedOwner; // async op currently using the preallocated overlapped - private void Init(FileMode mode, FileShare share) + private void Init(FileMode mode, FileShare share, string originalPath) { - // Disallow access to all non-file devices from the Win32FileStream - // constructors that take a String. Everyone else can call - // CreateFile themselves then use the constructor that takes an - // IntPtr. Disallows "con:", "com1:", "lpt1:", etc. - int fileType = Interop.Kernel32.GetFileType(_fileHandle); - if (fileType != Interop.Kernel32.FileTypes.FILE_TYPE_DISK) + if (!PathInternal.IsExtended(originalPath)) { - _fileHandle.Dispose(); - throw new NotSupportedException(SR.NotSupported_FileStreamOnNonFiles); + // To help avoid stumbling into opening COM/LPT ports by accident, we will block on non file handles unless + // we were explicitly passed a path that has \\?\. GetFullPath() will turn paths like C:\foo\con.txt into + // \\.\CON, so we'll only allow the \\?\ syntax. + + int fileType = Interop.Kernel32.GetFileType(_fileHandle); + if (fileType != Interop.Kernel32.FileTypes.FILE_TYPE_DISK) + { + int errorCode = fileType == Interop.Kernel32.FileTypes.FILE_TYPE_UNKNOWN + ? Marshal.GetLastWin32Error() + : Interop.Errors.ERROR_SUCCESS; + + _fileHandle.Dispose(); + + if (errorCode != Interop.Errors.ERROR_SUCCESS) + { + throw Win32Marshal.GetExceptionForWin32Error(errorCode); + } + throw new NotSupportedException(SR.NotSupported_FileStreamOnNonFiles); + } } // This is necessary for async IO using IO Completion ports via our @@ -161,8 +173,7 @@ namespace System.IO } else if (!useAsyncIO) { - if (handleType != Interop.Kernel32.FileTypes.FILE_TYPE_PIPE) - VerifyHandleIsSync(handle, access); + VerifyHandleIsSync(handle, handleType, access); } if (_canSeek) @@ -171,75 +182,31 @@ namespace System.IO _filePosition = 0; } - private unsafe static Interop.Kernel32.SECURITY_ATTRIBUTES GetSecAttrs(FileShare share) + private static unsafe Interop.Kernel32.SECURITY_ATTRIBUTES GetSecAttrs(FileShare share) { - Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = default(Interop.Kernel32.SECURITY_ATTRIBUTES); + Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = default; if ((share & FileShare.Inheritable) != 0) { - secAttrs = new Interop.Kernel32.SECURITY_ATTRIBUTES(); - secAttrs.nLength = (uint)sizeof(Interop.Kernel32.SECURITY_ATTRIBUTES); - - secAttrs.bInheritHandle = Interop.BOOL.TRUE; - } - return secAttrs; - } - - // Verifies that this handle supports synchronous IO operations (unless you - // didn't open it for either reading or writing). - private unsafe static void VerifyHandleIsSync(SafeFileHandle handle, FileAccess access) - { - // Do NOT use this method on pipes. Reading or writing to a pipe may - // cause an app to block incorrectly, introducing a deadlock (depending - // on whether a write will wake up an already-blocked thread or this - // Win32FileStream's thread). - Debug.Assert(Interop.Kernel32.GetFileType(handle) != Interop.Kernel32.FileTypes.FILE_TYPE_PIPE); - - byte* bytes = stackalloc byte[1]; - int numBytesReadWritten; - int r = -1; - - // If the handle is a pipe, ReadFile will block until there - // has been a write on the other end. We'll just have to deal with it, - // For the read end of a pipe, you can mess up and - // accidentally read synchronously from an async pipe. - if ((access & FileAccess.Read) != 0) // don't use the virtual CanRead or CanWrite, as this may be used in the ctor - { - r = Interop.Kernel32.ReadFile(handle, bytes, 0, out numBytesReadWritten, IntPtr.Zero); - } - else if ((access & FileAccess.Write) != 0) // don't use the virtual CanRead or CanWrite, as this may be used in the ctor - { - r = Interop.Kernel32.WriteFile(handle, bytes, 0, out numBytesReadWritten, IntPtr.Zero); - } - - if (r == 0) - { - int errorCode = Marshal.GetLastWin32Error(); - switch (errorCode) + secAttrs = new Interop.Kernel32.SECURITY_ATTRIBUTES { - case Interop.Errors.ERROR_INVALID_PARAMETER: - throw new ArgumentException(SR.Arg_HandleNotSync, "handle"); - case Interop.Errors.ERROR_INVALID_HANDLE: - throw Win32Marshal.GetExceptionForWin32Error(errorCode); - } + nLength = (uint)sizeof(Interop.Kernel32.SECURITY_ATTRIBUTES), + bInheritHandle = Interop.BOOL.TRUE + }; } + return secAttrs; } private bool HasActiveBufferOperation - { - get { return _activeBufferOperation != null && !_activeBufferOperation.IsCompleted; } - } + => _activeBufferOperation != null && !_activeBufferOperation.IsCompleted; - public override bool CanSeek - { - get { return _canSeek; } - } + public override bool CanSeek => _canSeek; private unsafe long GetLengthInternal() { Interop.Kernel32.FILE_STANDARD_INFO info = new Interop.Kernel32.FILE_STANDARD_INFO(); if (!Interop.Kernel32.GetFileInformationByHandleEx(_fileHandle, Interop.Kernel32.FILE_INFO_BY_HANDLE_CLASS.FileStandardInfo, out info, (uint)sizeof(Interop.Kernel32.FILE_STANDARD_INFO))) - throw Win32Marshal.GetExceptionForLastWin32Error(); + throw Win32Marshal.GetExceptionForLastWin32Error(_path); long len = info.EndOfFile; // If we're writing near the end of the file, we must include our // internal buffer in our Length calculation. Don't flush because @@ -307,7 +274,7 @@ namespace System.IO { if (!Interop.Kernel32.FlushFileBuffers(_fileHandle)) { - throw Win32Marshal.GetExceptionForLastWin32Error(); + throw Win32Marshal.GetExceptionForLastWin32Error(_path); } } @@ -404,7 +371,7 @@ namespace System.IO int errorCode = Marshal.GetLastWin32Error(); if (errorCode == Interop.Errors.ERROR_INVALID_PARAMETER) throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_FileLengthTooBig); - throw Win32Marshal.GetExceptionForWin32Error(errorCode); + throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path); } // Return file pointer to where it was before setting length if (origPos != value) @@ -506,8 +473,7 @@ namespace System.IO // Make sure we are reading from the right spot VerifyOSHandlePosition(); - int errorCode = 0; - int r = ReadFileNative(_fileHandle, buffer, null, out errorCode); + int r = ReadFileNative(_fileHandle, buffer, null, out int errorCode); if (r == -1) { @@ -521,7 +487,7 @@ namespace System.IO if (errorCode == ERROR_INVALID_PARAMETER) throw new ArgumentException(SR.Arg_HandleNotSync, "_fileHandle"); - throw Win32Marshal.GetExceptionForWin32Error(errorCode); + throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path); } } Debug.Assert(r >= 0, "FileStream's ReadNative is likely broken."); @@ -621,17 +587,15 @@ namespace System.IO Debug.Assert(!fileHandle.IsClosed && _canSeek, "!fileHandle.IsClosed && _canSeek"); Debug.Assert(origin >= SeekOrigin.Begin && origin <= SeekOrigin.End, "origin >= SeekOrigin.Begin && origin <= SeekOrigin.End"); - long ret = 0; - - if (!Interop.Kernel32.SetFilePointerEx(fileHandle, offset, out ret, (uint)origin)) + if (!Interop.Kernel32.SetFilePointerEx(fileHandle, offset, out long ret, (uint)origin)) { if (closeInvalidHandle) { - throw Win32Marshal.GetExceptionForWin32Error(GetLastWin32ErrorAndDisposeHandleIfInvalid(throwIfInvalidHandle: false)); + throw Win32Marshal.GetExceptionForWin32Error(GetLastWin32ErrorAndDisposeHandleIfInvalid(), _path); } else { - throw Win32Marshal.GetExceptionForWin32Error(Marshal.GetLastWin32Error()); + throw Win32Marshal.GetExceptionForLastWin32Error(_path); } } @@ -739,7 +703,7 @@ namespace System.IO // to a handle opened asynchronously. if (errorCode == ERROR_INVALID_PARAMETER) throw new IOException(SR.IO_FileTooLongOrHandleNotSync); - throw Win32Marshal.GetExceptionForWin32Error(errorCode); + throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path); } } Debug.Assert(r >= 0, "FileStream's WriteCore is likely broken."); @@ -850,7 +814,7 @@ namespace System.IO } } - unsafe private Task<int> ReadNativeAsync(Memory<byte> destination, int numBufferedBytesRead, CancellationToken cancellationToken) + private unsafe Task<int> ReadNativeAsync(Memory<byte> destination, int numBufferedBytesRead, CancellationToken cancellationToken) { AssertCanRead(); Debug.Assert(_useAsyncIO, "ReadNativeAsync doesn't work on synchronous file streams!"); @@ -875,7 +839,7 @@ namespace System.IO } else { - destination = default(Memory<byte>); + destination = default; } } @@ -938,7 +902,7 @@ namespace System.IO } else { - throw Win32Marshal.GetExceptionForWin32Error(errorCode); + throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path); } } else if (cancellationToken.CanBeCanceled) // ERROR_IO_PENDING @@ -1141,7 +1105,7 @@ namespace System.IO } else { - throw Win32Marshal.GetExceptionForWin32Error(errorCode); + throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path); } } else if (cancellationToken.CanBeCanceled) // ERROR_IO_PENDING @@ -1238,7 +1202,7 @@ namespace System.IO } } - private int GetLastWin32ErrorAndDisposeHandleIfInvalid(bool throwIfInvalidHandle = false) + private int GetLastWin32ErrorAndDisposeHandleIfInvalid() { int errorCode = Marshal.GetLastWin32Error(); @@ -1262,9 +1226,6 @@ namespace System.IO if (errorCode == Interop.Errors.ERROR_INVALID_HANDLE) { _fileHandle.Dispose(); - - if (throwIfInvalidHandle) - throw Win32Marshal.GetExceptionForWin32Error(errorCode); } return errorCode; @@ -1418,7 +1379,7 @@ namespace System.IO break; default: // Everything else is an error (and there won't be a callback). - throw Win32Marshal.GetExceptionForWin32Error(errorCode); + throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path); } } @@ -1436,7 +1397,7 @@ namespace System.IO case Interop.Errors.ERROR_OPERATION_ABORTED: // canceled throw new OperationCanceledException(cancellationToken.IsCancellationRequested ? cancellationToken : new CancellationToken(true)); default: // error - throw Win32Marshal.GetExceptionForWin32Error((int)readAwaitable._errorCode); + throw Win32Marshal.GetExceptionForWin32Error((int)readAwaitable._errorCode, _path); } // Successful operation. If we got zero bytes, we're done: exit the read/write loop. @@ -1535,7 +1496,7 @@ namespace System.IO } /// <summary>Overlapped callback: store the results, then invoke the continuation delegate.</summary> - internal unsafe static void IOCallback(uint errorCode, uint numBytes, NativeOverlapped* pOVERLAP) + internal static unsafe void IOCallback(uint errorCode, uint numBytes, NativeOverlapped* pOVERLAP) { var awaitable = (AsyncCopyToAwaitable)ThreadPoolBoundHandle.GetNativeOverlappedState(pOVERLAP); @@ -1623,7 +1584,7 @@ namespace System.IO if (!Interop.Kernel32.LockFile(_fileHandle, positionLow, positionHigh, lengthLow, lengthHigh)) { - throw Win32Marshal.GetExceptionForLastWin32Error(); + throw Win32Marshal.GetExceptionForLastWin32Error(_path); } } @@ -1636,9 +1597,10 @@ namespace System.IO if (!Interop.Kernel32.UnlockFile(_fileHandle, positionLow, positionHigh, lengthLow, lengthHigh)) { - throw Win32Marshal.GetExceptionForLastWin32Error(); + throw Win32Marshal.GetExceptionForLastWin32Error(_path); } } + private SafeFileHandle ValidateFileHandle(SafeFileHandle fileHandle) { if (fileHandle.IsInvalid) |