diff options
Diffstat (limited to 'src/mscorlib/src/System/IO/FileStream.cs')
-rw-r--r-- | src/mscorlib/src/System/IO/FileStream.cs | 2695 |
1 files changed, 0 insertions, 2695 deletions
diff --git a/src/mscorlib/src/System/IO/FileStream.cs b/src/mscorlib/src/System/IO/FileStream.cs deleted file mode 100644 index deef30c480..0000000000 --- a/src/mscorlib/src/System/IO/FileStream.cs +++ /dev/null @@ -1,2695 +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. - -/*============================================================ -** -** -** -** -** -** Purpose: Exposes a Stream around a file, with full -** synchronous and asychronous support, and buffering. -** -** -===========================================================*/ -using System; -using Microsoft.Win32; -using Microsoft.Win32.SafeHandles; -using System.Security; -#if FEATURE_MACL -using System.Security.AccessControl; -#endif -using System.Security.Permissions; -using System.Threading; -using System.Threading.Tasks; -using System.Runtime.InteropServices; -#if FEATURE_REMOTING -using System.Runtime.Remoting.Messaging; -#endif -using System.Runtime.CompilerServices; -using System.Globalization; -using System.Runtime.Versioning; -using System.Diagnostics.Contracts; -using System.Diagnostics.Tracing; - -/* - * FileStream supports different modes of accessing the disk - async mode - * and sync mode. They are two completely different codepaths in the - * sync & async methods (ie, Read/Write vs. BeginRead/BeginWrite). File - * handles in NT can be opened in only sync or overlapped (async) mode, - * and we have to deal with this pain. Stream has implementations of - * the sync methods in terms of the async ones, so we'll - * call through to our base class to get those methods when necessary. - * - * Also buffering is added into FileStream as well. Folded in the - * code from BufferedStream, so all the comments about it being mostly - * aggressive (and the possible perf improvement) apply to FileStream as - * well. Also added some buffering to the async code paths. - * - * Class Invariants: - * The class has one buffer, shared for reading & writing. It can only be - * used for one or the other at any point in time - not both. The following - * should be true: - * 0 <= _readPos <= _readLen < _bufferSize - * 0 <= _writePos < _bufferSize - * _readPos == _readLen && _readPos > 0 implies the read buffer is valid, - * but we're at the end of the buffer. - * _readPos == _readLen == 0 means the read buffer contains garbage. - * Either _writePos can be greater than 0, or _readLen & _readPos can be - * greater than zero, but neither can be greater than zero at the same time. - * - */ - -namespace System.IO { - - // This is an internal object implementing IAsyncResult with fields - // for all of the relevant data necessary to complete the IO operation. - // This is used by AsyncFSCallback and all of the async methods. - // We should probably make this a nested type of FileStream. But - // I don't know how to define a nested class in mscorlib.h - - unsafe internal sealed class FileStreamAsyncResult : IAsyncResult - { - // README: - // If you modify the order of these fields, make sure to update - // the native VM definition of this class as well!!! - // User code callback - private AsyncCallback _userCallback; - private Object _userStateObject; - private ManualResetEvent _waitHandle; - [System.Security.SecurityCritical] - private SafeFileHandle _handle; // For cancellation support. - - [SecurityCritical] - private NativeOverlapped* _overlapped; - internal NativeOverlapped* OverLapped { [SecurityCritical]get { return _overlapped; } } - internal bool IsAsync { [SecuritySafeCritical]get { return _overlapped != null; } } - - - internal int _EndXxxCalled; // Whether we've called EndXxx already. - private int _numBytes; // number of bytes read OR written - internal int NumBytes { get { return _numBytes; } } - - private int _errorCode; - internal int ErrorCode { get { return _errorCode; } } - - private int _numBufferedBytes; - internal int NumBufferedBytes { get { return _numBufferedBytes; } } - - internal int NumBytesRead { get { return _numBytes + _numBufferedBytes; } } - - private bool _isWrite; // Whether this is a read or a write - internal bool IsWrite { get { return _isWrite; } } - - private bool _isComplete; // Value for IsCompleted property - private bool _completedSynchronously; // Which thread called callback - - // The NativeOverlapped struct keeps a GCHandle to this IAsyncResult object. - // So if the user doesn't call EndRead/EndWrite, a finalizer won't help because - // it'll never get called. - - // Overlapped class will take care of the async IO operations in progress - // when an appdomain unload occurs. - - [System.Security.SecurityCritical] // auto-generated - private unsafe static IOCompletionCallback s_IOCallback; - - [SecuritySafeCritical] - internal FileStreamAsyncResult( - int numBufferedBytes, - byte[] bytes, - SafeFileHandle handle, - AsyncCallback userCallback, - Object userStateObject, - bool isWrite) - { - _userCallback = userCallback; - _userStateObject = userStateObject; - _isWrite = isWrite; - _numBufferedBytes = numBufferedBytes; - _handle = handle; - - // For Synchronous IO, I could go with either a callback and using - // the managed Monitor class, or I could create a handle and wait on it. - ManualResetEvent waitHandle = new ManualResetEvent(false); - _waitHandle = waitHandle; - - // Create a managed overlapped class - // We will set the file offsets later - Overlapped overlapped = new Overlapped(0, 0, IntPtr.Zero, this); - - // Pack the Overlapped class, and store it in the async result - if (userCallback != null) - { - var ioCallback = s_IOCallback; // cached static delegate; delay initialized due to it being SecurityCritical - if (ioCallback == null) s_IOCallback = ioCallback = new IOCompletionCallback(AsyncFSCallback); - _overlapped = overlapped.Pack(ioCallback, bytes); - } - else - { - _overlapped = overlapped.UnsafePack(null, bytes); - } - - Contract.Assert(_overlapped != null, "Did Overlapped.Pack or Overlapped.UnsafePack just return a null?"); - } - - internal static FileStreamAsyncResult CreateBufferedReadResult(int numBufferedBytes, AsyncCallback userCallback, Object userStateObject, bool isWrite) - { - FileStreamAsyncResult asyncResult = new FileStreamAsyncResult(numBufferedBytes, userCallback, userStateObject, isWrite); - asyncResult.CallUserCallback(); - return asyncResult; - } - - // This creates a synchronous Async Result. We should consider making this a separate class and maybe merge it with - // System.IO.Stream.SynchronousAsyncResult - private FileStreamAsyncResult(int numBufferedBytes, AsyncCallback userCallback, Object userStateObject, bool isWrite) - { - _userCallback = userCallback; - _userStateObject = userStateObject; - _isWrite = isWrite; - _numBufferedBytes = numBufferedBytes; - } - - public Object AsyncState - { - get { return _userStateObject; } - } - - public bool IsCompleted - { - get { return _isComplete; } - } - - public WaitHandle AsyncWaitHandle - { - [System.Security.SecuritySafeCritical] // auto-generated - get { - // Consider uncommenting this someday soon - the EventHandle - // in the Overlapped struct is really useless half of the - // time today since the OS doesn't signal it. If users call - // EndXxx after the OS call happened to complete, there's no - // reason to create a synchronization primitive here. Fixing - // this will save us some perf, assuming we can correctly - // initialize the ManualResetEvent. - if (_waitHandle == null) { - ManualResetEvent mre = new ManualResetEvent(false); - if (_overlapped != null && _overlapped->EventHandle != IntPtr.Zero) { - mre.SafeWaitHandle = new SafeWaitHandle(_overlapped->EventHandle, true); - } - - // make sure only one thread sets _waitHandle - if (Interlocked.CompareExchange<ManualResetEvent>(ref _waitHandle, mre, null) == null) { - if (_isComplete) - _waitHandle.Set(); - } - else { - // There's a slight but acceptable race condition if we weren't - // the thread that set _waitHandle and this code path - // returns before the code in the if statement - // executes (on the other thread). However, the - // caller is waiting for the wait handle to be set, - // which will still happen. - mre.Close(); - } - } - return _waitHandle; - } - } - - // Returns true iff the user callback was called by the thread that - // called BeginRead or BeginWrite. If we use an async delegate or - // threadpool thread internally, this will be false. This is used - // by code to determine whether a successive call to BeginRead needs - // to be done on their main thread or in their callback to avoid a - // stack overflow on many reads or writes. - public bool CompletedSynchronously - { - get { return _completedSynchronously; } - } - - private void CallUserCallbackWorker() - { - _isComplete = true; - - // ensure _isComplete is set before reading _waitHandle - Thread.MemoryBarrier(); - if (_waitHandle != null) - _waitHandle.Set(); - - _userCallback(this); - } - - internal void CallUserCallback() - { - // Convenience method for me, since I have to do this in a number - // of places in the buffering code for fake IAsyncResults. - // AsyncFSCallback intentionally does not use this method. - - if (_userCallback != null) { - // Call user's callback on a threadpool thread. - // Set completedSynchronously to false, since it's on another - // thread, not the main thread. - _completedSynchronously = false; - ThreadPool.QueueUserWorkItem(state => ((FileStreamAsyncResult)state).CallUserCallbackWorker(), this); - } - else { - _isComplete = true; - - // ensure _isComplete is set before reading _waitHandle - Thread.MemoryBarrier(); - if (_waitHandle != null) - _waitHandle.Set(); - } - } - - [SecurityCritical] - internal void ReleaseNativeResource() - { - // Free memory & GC handles. - if (this._overlapped != null) - Overlapped.Free(_overlapped); - } - - internal void Wait() - { - if (_waitHandle != null) - { - // We must block to ensure that AsyncFSCallback has completed, - // and we should close the WaitHandle in here. AsyncFSCallback - // and the hand-ported imitation version in COMThreadPool.cpp - // are the only places that set this event. - try - { - _waitHandle.WaitOne(); - Contract.Assert(_isComplete == true, "FileStreamAsyncResult::Wait - AsyncFSCallback didn't set _isComplete to true!"); - } - finally - { - _waitHandle.Close(); - } - } - } - - // When doing IO asynchronously (ie, _isAsync==true), this callback is - // called by a free thread in the threadpool when the IO operation - // completes. - [System.Security.SecurityCritical] // auto-generated - unsafe private static void AsyncFSCallback(uint errorCode, uint numBytes, NativeOverlapped* pOverlapped) - { - BCLDebug.Log(String.Format("AsyncFSCallback called. errorCode: " + errorCode + " numBytes: " + numBytes)); - - // Unpack overlapped - Overlapped overlapped = Overlapped.Unpack(pOverlapped); - // Free the overlapped struct in EndRead/EndWrite. - - // Extract async result from overlapped - FileStreamAsyncResult asyncResult = - (FileStreamAsyncResult)overlapped.AsyncResult; - asyncResult._numBytes = (int)numBytes; - - if (FrameworkEventSource.IsInitialized && FrameworkEventSource.Log.IsEnabled(EventLevel.Informational, FrameworkEventSource.Keywords.ThreadTransfer)) - FrameworkEventSource.Log.ThreadTransferReceive((long)(asyncResult.OverLapped), 2, string.Empty); - - // Handle reading from & writing to closed pipes. While I'm not sure - // this is entirely necessary anymore, maybe it's possible for - // an async read on a pipe to be issued and then the pipe is closed, - // returning this error. This may very well be necessary. - if (errorCode == FileStream.ERROR_BROKEN_PIPE || errorCode == FileStream.ERROR_NO_DATA) - errorCode = 0; - - asyncResult._errorCode = (int)errorCode; - - // Call the user-provided callback. It can and often should - // call EndRead or EndWrite. There's no reason to use an async - // delegate here - we're already on a threadpool thread. - // IAsyncResult's completedSynchronously property must return - // false here, saying the user callback was called on another thread. - asyncResult._completedSynchronously = false; - asyncResult._isComplete = true; - - // ensure _isComplete is set before reading _waitHandle - Thread.MemoryBarrier(); - - // The OS does not signal this event. We must do it ourselves. - ManualResetEvent wh = asyncResult._waitHandle; - if (wh != null) - { - Contract.Assert(!wh.SafeWaitHandle.IsClosed, "ManualResetEvent already closed!"); - bool r = wh.Set(); - Contract.Assert(r, "ManualResetEvent::Set failed!"); - if (!r) __Error.WinIOError(); - } - - AsyncCallback userCallback = asyncResult._userCallback; - if (userCallback != null) - userCallback(asyncResult); - } - - [SecuritySafeCritical] - [HostProtection(ExternalThreading = true)] - internal void Cancel() - { - Contract.Assert(_handle != null, "_handle should not be null."); - Contract.Assert(_overlapped != null, "Cancel should only be called on true asynchronous FileStreamAsyncResult, i.e. _overlapped is not null"); - - if (IsCompleted) - return; - - if (_handle.IsInvalid) - return; - - bool r = Win32Native.CancelIoEx(_handle, _overlapped); - if (!r) - { - int errorCode = Marshal.GetLastWin32Error(); - - // ERROR_NOT_FOUND is returned if CancelIoEx cannot find the request to cancel. - // This probably means that the IO operation has completed. - if (errorCode != Win32Native.ERROR_NOT_FOUND) - __Error.WinIOError(errorCode, String.Empty); - } - } - } - - [ComVisible(true)] - public class FileStream : Stream - { - internal const int DefaultBufferSize = 4096; - - private byte[] _buffer; // Shared read/write buffer. Alloc on first use. - private String _fileName; // Fully qualified file name. - private bool _isAsync; // Whether we opened the handle for overlapped IO - private bool _canRead; - private bool _canWrite; - private bool _canSeek; - private bool _exposedHandle; // Could other code be using this handle? - private bool _isPipe; // Whether to disable async buffering code. - private int _readPos; // Read pointer within shared buffer. - private int _readLen; // Number of bytes read in buffer from file. - private int _writePos; // Write pointer within shared buffer. - private int _bufferSize; // Length of internal buffer, if it's allocated. - [System.Security.SecurityCritical] // auto-generated - private SafeFileHandle _handle; - private long _pos; // Cache current location in the file. - private long _appendStart;// When appending, prevent overwriting file. - private static AsyncCallback s_endReadTask; - private static AsyncCallback s_endWriteTask; - private static Action<object> s_cancelReadHandler; - private static Action<object> s_cancelWriteHandler; - - //This exists only to support IsolatedStorageFileStream. - //Any changes to FileStream must include the corresponding changes in IsolatedStorage. - internal FileStream() { - } -#if FEATURE_CORECLR - [System.Security.SecuritySafeCritical] - public FileStream(String path, FileMode mode) - : this(path, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), FileShare.Read, DefaultBufferSize, FileOptions.None, Path.GetFileName(path), false, false, true) { - } - - [System.Security.SecuritySafeCritical] - public FileStream(String path, FileMode mode, FileAccess access) - : this(path, mode, access, FileShare.Read, DefaultBufferSize, FileOptions.None, Path.GetFileName(path), false, false, true) { - } - - [System.Security.SecuritySafeCritical] - public FileStream(String path, FileMode mode, FileAccess access, FileShare share) - : this(path, mode, access, share, DefaultBufferSize, FileOptions.None, Path.GetFileName(path), false, false, true) { - } - - [System.Security.SecuritySafeCritical] - public FileStream(String path, FileMode mode, FileAccess access, FileShare share, int bufferSize) - : this(path, mode, access, share, bufferSize, FileOptions.None, Path.GetFileName(path), false, false, true) - { - } - -#else // FEATURE_CORECLR - [System.Security.SecuritySafeCritical] // auto-generated - public FileStream(String path, FileMode mode) - : this(path, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), FileShare.Read, DefaultBufferSize, FileOptions.None, Path.GetFileName(path), false) { - } - - [System.Security.SecuritySafeCritical] // auto-generated - public FileStream(String path, FileMode mode, FileAccess access) - : this(path, mode, access, FileShare.Read, DefaultBufferSize, FileOptions.None, Path.GetFileName(path), false) { - } - - [System.Security.SecuritySafeCritical] // auto-generated - public FileStream(String path, FileMode mode, FileAccess access, FileShare share) - : this(path, mode, access, share, DefaultBufferSize, FileOptions.None, Path.GetFileName(path), false) { - } - - [System.Security.SecuritySafeCritical] // auto-generated - public FileStream(String path, FileMode mode, FileAccess access, FileShare share, int bufferSize) - : this(path, mode, access, share, bufferSize, FileOptions.None, Path.GetFileName(path), false) - { - } -#endif // FEATURE_CORECLR - - [System.Security.SecuritySafeCritical] // auto-generated - public FileStream(String path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) - : this(path, mode, access, share, bufferSize, options, Path.GetFileName(path), false) - { - } - - #if FEATURE_CORECLR - [System.Security.SecurityCritical] // auto-generated - #else - [System.Security.SecuritySafeCritical] - #endif - public FileStream(String path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync) - : this(path, mode, access, share, bufferSize, (useAsync ? FileOptions.Asynchronous : FileOptions.None), Path.GetFileName(path), false) - { - } - -#if FEATURE_MACL - // This constructor is done differently to avoid loading a few more - // classes, and more importantly, to build correctly on Rotor. - [System.Security.SecuritySafeCritical] // auto-generated - public FileStream(String path, FileMode mode, FileSystemRights rights, FileShare share, int bufferSize, FileOptions options, FileSecurity fileSecurity) - { - Object pinningHandle; - Win32Native.SECURITY_ATTRIBUTES secAttrs = GetSecAttrs(share, fileSecurity, out pinningHandle); - try { - Init(path, mode, (FileAccess)0, (int)rights, true, share, bufferSize, options, secAttrs, Path.GetFileName(path), false, false, false); - } - finally { - if (pinningHandle != null) { - GCHandle pinHandle = (GCHandle) pinningHandle; - pinHandle.Free(); - } - } - } - - [System.Security.SecuritySafeCritical] // auto-generated - public FileStream(String path, FileMode mode, FileSystemRights rights, FileShare share, int bufferSize, FileOptions options) - { - Win32Native.SECURITY_ATTRIBUTES secAttrs = GetSecAttrs(share); - Init(path, mode, (FileAccess)0, (int)rights, true, share, bufferSize, options, secAttrs, Path.GetFileName(path), false, false, false); - } -#endif - - [System.Security.SecurityCritical] // auto-generated - internal FileStream(String path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options, String msgPath, bool bFromProxy) - { - Win32Native.SECURITY_ATTRIBUTES secAttrs = GetSecAttrs(share); - Init(path, mode, access, 0, false, share, bufferSize, options, secAttrs, msgPath, bFromProxy, false, false); - } - - [System.Security.SecurityCritical] - internal FileStream(String path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options, String msgPath, bool bFromProxy, bool useLongPath) - { - Win32Native.SECURITY_ATTRIBUTES secAttrs = GetSecAttrs(share); - Init(path, mode, access, 0, false, share, bufferSize, options, secAttrs, msgPath, bFromProxy, useLongPath, false); - } - - [System.Security.SecurityCritical] - internal FileStream(String path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options, String msgPath, bool bFromProxy, bool useLongPath, bool checkHost) - { - Win32Native.SECURITY_ATTRIBUTES secAttrs = GetSecAttrs(share); - Init(path, mode, access, 0, false, share, bufferSize, options, secAttrs, msgPath, bFromProxy, useLongPath, checkHost); - } - - // AccessControl namespace is not defined in Rotor - [System.Security.SecuritySafeCritical] - private void Init(String path, FileMode mode, FileAccess access, int rights, bool useRights, FileShare share, int bufferSize, FileOptions options, Win32Native.SECURITY_ATTRIBUTES secAttrs, String msgPath, bool bFromProxy, bool useLongPath, bool checkHost) - { - if (path == null) - throw new ArgumentNullException("path", Environment.GetResourceString("ArgumentNull_Path")); - if (path.Length == 0) - throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath")); - Contract.EndContractBlock(); - -#if FEATURE_MACL - FileSystemRights fileSystemRights = (FileSystemRights)rights; -#endif - // msgPath must be safe to hand back to untrusted code. - - _fileName = msgPath; // To handle odd cases of finalizing partially constructed objects. - _exposedHandle = false; - - // don't include inheritable in our bounds check for share - FileShare tempshare = share & ~FileShare.Inheritable; - String badArg = null; - - if (mode < FileMode.CreateNew || mode > FileMode.Append) - badArg = "mode"; - else if (!useRights && (access < FileAccess.Read || access > FileAccess.ReadWrite)) - badArg = "access"; -#if FEATURE_MACL - else if (useRights && (fileSystemRights < FileSystemRights.ReadData || fileSystemRights > FileSystemRights.FullControl)) - badArg = "rights"; -#endif - else if (tempshare < FileShare.None || tempshare > (FileShare.ReadWrite | FileShare.Delete)) - badArg = "share"; - - if (badArg != null) - throw new ArgumentOutOfRangeException(badArg, Environment.GetResourceString("ArgumentOutOfRange_Enum")); - - // NOTE: any change to FileOptions enum needs to be matched here in the error validation - if (options != FileOptions.None && (options & ~(FileOptions.WriteThrough | FileOptions.Asynchronous | FileOptions.RandomAccess | FileOptions.DeleteOnClose | FileOptions.SequentialScan | FileOptions.Encrypted | (FileOptions)0x20000000 /* NoBuffering */)) != 0) - throw new ArgumentOutOfRangeException("options", Environment.GetResourceString("ArgumentOutOfRange_Enum")); - - if (bufferSize <= 0) - throw new ArgumentOutOfRangeException("bufferSize", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum")); - - // Write access validation -#if FEATURE_MACL - if ((!useRights && (access & FileAccess.Write) == 0) - || (useRights && (fileSystemRights & FileSystemRights.Write) == 0)) -#else - if (!useRights && (access & FileAccess.Write) == 0) -#endif //FEATURE_MACL - { - if (mode==FileMode.Truncate || mode==FileMode.CreateNew || mode==FileMode.Create || mode==FileMode.Append) { - // No write access - if (!useRights) - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFileMode&AccessCombo", mode, access)); -#if FEATURE_MACL - else - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFileMode&RightsCombo", mode, fileSystemRights)); -#endif //FEATURE_MACL - } - } - -#if FEATURE_MACL - // FileMode.Truncate only works with GENERIC_WRITE (FileAccess.Write), source:MSDN - // For backcomp use FileAccess.Write when FileSystemRights.Write is specified - if (useRights && (mode == FileMode.Truncate)) { - if (fileSystemRights == FileSystemRights.Write) { - useRights = false; - access = FileAccess.Write; - } - else { - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFileModeTruncate&RightsCombo", mode, fileSystemRights)); - } - } -#endif - - int fAccess; - if (!useRights) { - fAccess = access == FileAccess.Read? GENERIC_READ: - access == FileAccess.Write? GENERIC_WRITE: - GENERIC_READ | GENERIC_WRITE; - } - else { - fAccess = rights; - } - - // Get absolute path - Security needs this to prevent something - // like trying to create a file in c:\tmp with the name - // "..\WinNT\System32\ntoskrnl.exe". Store it for user convenience. - int maxPath = useLongPath ? Path.MaxLongPath : Path.MaxPath; - String filePath = Path.NormalizePath(path, true, maxPath); - - _fileName = filePath; - - // Prevent access to your disk drives as raw block devices. - if (filePath.StartsWith("\\\\.\\", StringComparison.Ordinal)) - throw new ArgumentException(Environment.GetResourceString("Arg_DevicesNotSupported")); - - // In 4.0, we always construct a FileIOPermission object below. - // If filePath contained a ':', we would throw a NotSupportedException in - // System.Security.Util.StringExpressionSet.CanonicalizePath. - // If filePath contained other illegal characters, we would throw an ArgumentException in - // FileIOPermission.CheckIllegalCharacters. - // In 4.5 we on longer construct the FileIOPermission object in full trust. - // To preserve the 4.0 behavior we do an explicit check for ':' here and also call Path.CheckInvalidPathChars. - // Note that we need to call CheckInvalidPathChars before checking for ':' because that is what FileIOPermission does. - - Path.CheckInvalidPathChars(filePath, true); - -#if !PLATFORM_UNIX - if (filePath.IndexOf( ':', 2 ) != -1) - throw new NotSupportedException( Environment.GetResourceString( "Argument_PathFormatNotSupported" ) ); -#endif // !PLATFORM_UNIX - - bool read = false; - -#if FEATURE_MACL - if ((!useRights && (access & FileAccess.Read) != 0) || (useRights && (fileSystemRights & FileSystemRights.ReadAndExecute) != 0)) -#else - if (!useRights && (access & FileAccess.Read) != 0) -#endif //FEATURE_MACL - { - if (mode == FileMode.Append) - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidAppendMode")); - else - read = true; - } - - // All demands in full trust domains are no-ops, so skip -#if FEATURE_CAS_POLICY - if (!CodeAccessSecurityEngine.QuickCheckForAllDemands()) -#endif // FEATURE_CAS_POLICY - { - // Build up security permissions required, as well as validate we - // have a sensible set of parameters. IE, creating a brand new file - // for reading doesn't make much sense. - FileIOPermissionAccess secAccess = FileIOPermissionAccess.NoAccess; - - if (read) - { - Contract.Assert(mode != FileMode.Append); - secAccess = secAccess | FileIOPermissionAccess.Read; - } - - // I can't think of any combos of FileMode we should disallow if we - // don't have read access. Writing would pretty much always be valid - // in those cases. - - // For any FileSystemRights other than ReadAndExecute, demand Write permission - // This is probably bit overkill for TakeOwnership etc but we don't have any - // matching FileIOPermissionAccess to demand. It is better that we ask for Write permission. - -#if FEATURE_MACL - // FileMode.OpenOrCreate & FileSystemRights.Synchronize can create 0-byte file; demand write - if ((!useRights && (access & FileAccess.Write) != 0) - || (useRights && (fileSystemRights & (FileSystemRights.Write | FileSystemRights.Delete - | FileSystemRights.DeleteSubdirectoriesAndFiles - | FileSystemRights.ChangePermissions - | FileSystemRights.TakeOwnership)) != 0) - || (useRights && ((fileSystemRights & FileSystemRights.Synchronize) != 0) - && mode==FileMode.OpenOrCreate) - ) -#else - if (!useRights && (access & FileAccess.Write) != 0) -#endif //FEATURE_MACL - { - if (mode==FileMode.Append) - secAccess = secAccess | FileIOPermissionAccess.Append; - else - secAccess = secAccess | FileIOPermissionAccess.Write; - } - -#if FEATURE_MACL - bool specifiedAcl; - unsafe { - specifiedAcl = secAttrs != null && secAttrs.pSecurityDescriptor != null; - } - - AccessControlActions control = specifiedAcl ? AccessControlActions.Change : AccessControlActions.None; - new FileIOPermission(secAccess, control, new String[] { filePath }, false, false).Demand(); -#else -#if FEATURE_CORECLR - if (checkHost) { - FileSecurityState state = new FileSecurityState(FileSecurityState.ToFileSecurityState(secAccess), path, filePath); - state.EnsureState(); - } -#else - new FileIOPermission(secAccess, new String[] { filePath }, false, false).Demand(); -#endif // FEATURE_CORECLR -#endif - } - - // Our Inheritable bit was stolen from Windows, but should be set in - // the security attributes class. Don't leave this bit set. - share &= ~FileShare.Inheritable; - - bool seekToEnd = (mode==FileMode.Append); - // Must use a valid Win32 constant here... - if (mode == FileMode.Append) - mode = FileMode.OpenOrCreate; - - // WRT async IO, do the right thing for whatever platform we're on. - // This way, someone can easily write code that opens a file - // asynchronously no matter what their platform is. - if ((options & FileOptions.Asynchronous) != 0) - _isAsync = true; - else - options &= ~FileOptions.Asynchronous; - - int flagsAndAttributes = (int) options; - -#if !PLATFORM_UNIX - // 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 - flagsAndAttributes |= (Win32Native.SECURITY_SQOS_PRESENT | Win32Native.SECURITY_ANONYMOUS); -#endif - - // Don't pop up a dialog for reading from an emtpy floppy drive - int oldMode = Win32Native.SetErrorMode(Win32Native.SEM_FAILCRITICALERRORS); - try { - String tempPath = filePath; - if (useLongPath) - tempPath = Path.AddLongPathPrefix(tempPath); - _handle = Win32Native.SafeCreateFile(tempPath, fAccess, share, secAttrs, mode, flagsAndAttributes, IntPtr.Zero); - - if (_handle.IsInvalid) { - // Return a meaningful exception, using the RELATIVE path to - // the file to avoid returning extra information to the caller - // unless they have path discovery permission, in which case - // the full path is fine & useful. - - // 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==__Error.ERROR_PATH_NOT_FOUND && filePath.Equals(Directory.InternalGetDirectoryRoot(filePath))) - errorCode = __Error.ERROR_ACCESS_DENIED; - - // We need to give an exception, and preferably it would include - // the fully qualified path name. Do security check here. If - // we fail, give back the msgPath, which should not reveal much. - // While this logic is largely duplicated in - // __Error.WinIOError, we need this for - // IsolatedStorageFileStream. - bool canGiveFullPath = false; - - if (!bFromProxy) - { - try { -#if !FEATURE_CORECLR - new FileIOPermission(FileIOPermissionAccess.PathDiscovery, new String[] { _fileName }, false, false ).Demand(); -#endif - canGiveFullPath = true; - } - catch(SecurityException) {} - } - - if (canGiveFullPath) - __Error.WinIOError(errorCode, _fileName); - else - __Error.WinIOError(errorCode, msgPath); - } - } - finally { - Win32Native.SetErrorMode(oldMode); - } - - // Disallow access to all non-file devices from the FileStream - // 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 = Win32Native.GetFileType(_handle); - if (fileType != Win32Native.FILE_TYPE_DISK) { - _handle.Close(); - throw new NotSupportedException(Environment.GetResourceString("NotSupported_FileStreamOnNonFiles")); - } - - // This is necessary for async IO using IO Completion ports via our - // managed Threadpool API's. This (theoretically) calls the OS's - // BindIoCompletionCallback method, and passes in a stub for the - // LPOVERLAPPED_COMPLETION_ROUTINE. This stub looks at the Overlapped - // struct for this request and gets a delegate to a managed callback - // from there, which it then calls on a threadpool thread. (We allocate - // our native OVERLAPPED structs 2 pointers too large and store EE state - // & GC handles there, one to an IAsyncResult, the other to a delegate.) - if (_isAsync) { - bool b = false; - // BindHandle requires UnmanagedCode permission -#pragma warning disable 618 - new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); -#pragma warning restore 618 - try { - b = ThreadPool.BindHandle(_handle); - } - finally { - CodeAccessPermission.RevertAssert(); - if (!b) { - // We should close the handle so that the handle is not open until SafeFileHandle GC - Contract.Assert(!_exposedHandle, "Are we closing handle that we exposed/not own, how?"); - _handle.Close(); - } - } - if (!b) - throw new IOException(Environment.GetResourceString("IO.IO_BindHandleFailed")); - } - - if (!useRights) { - _canRead = (access & FileAccess.Read) != 0; - _canWrite = (access & FileAccess.Write) != 0; - } -#if FEATURE_MACL - else { - _canRead = (fileSystemRights & FileSystemRights.ReadData) != 0; - _canWrite = ((fileSystemRights & FileSystemRights.WriteData) != 0) - || ((fileSystemRights & FileSystemRights.AppendData) != 0); - } -#endif //FEATURE_MACL - - _canSeek = true; - _isPipe = false; - _pos = 0; - _bufferSize = bufferSize; - _readPos = 0; - _readLen = 0; - _writePos = 0; - - // For Append mode... - if (seekToEnd) { - _appendStart = SeekCore(0, SeekOrigin.End); - } - else { - _appendStart = -1; - } - } - - [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access) instead. http://go.microsoft.com/fwlink/?linkid=14202")] - public FileStream(IntPtr handle, FileAccess access) - : this(handle, access, true, DefaultBufferSize, false) { - } - - [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access) instead, and optionally make a new SafeFileHandle with ownsHandle=false if needed. http://go.microsoft.com/fwlink/?linkid=14202")] - public FileStream(IntPtr handle, FileAccess access, bool ownsHandle) - : this(handle, access, ownsHandle, DefaultBufferSize, false) { - } - - [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access, int bufferSize) instead, and optionally make a new SafeFileHandle with ownsHandle=false if needed. http://go.microsoft.com/fwlink/?linkid=14202")] - public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize) - : this(handle, access, ownsHandle, bufferSize, false) { - } - - // We explicitly do a Demand, not a LinkDemand here. - [System.Security.SecuritySafeCritical] // auto-generated - [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) instead, and optionally make a new SafeFileHandle with ownsHandle=false if needed. http://go.microsoft.com/fwlink/?linkid=14202")] -#pragma warning disable 618 - [SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.UnmanagedCode)] -#pragma warning restore 618 - public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync) - : this(new SafeFileHandle(handle, ownsHandle), access, bufferSize, isAsync) { - } - - [System.Security.SecuritySafeCritical] // auto-generated - public FileStream(SafeFileHandle handle, FileAccess access) - : this(handle, access, DefaultBufferSize, false) { - } - - [System.Security.SecuritySafeCritical] // auto-generated - public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize) - : this(handle, access, bufferSize, false) { - } - - [System.Security.SecuritySafeCritical] // auto-generated -#pragma warning disable 618 - [SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.UnmanagedCode)] -#pragma warning restore 618 - public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) { - // To ensure we don't leak a handle, put it in a SafeFileHandle first - if (handle.IsInvalid) - throw new ArgumentException(Environment.GetResourceString("Arg_InvalidHandle"), "handle"); - Contract.EndContractBlock(); - - _handle = handle; - _exposedHandle = true; - - // Now validate arguments. - if (access < FileAccess.Read || access > FileAccess.ReadWrite) - throw new ArgumentOutOfRangeException("access", Environment.GetResourceString("ArgumentOutOfRange_Enum")); - if (bufferSize <= 0) - throw new ArgumentOutOfRangeException("bufferSize", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum")); - - int handleType = Win32Native.GetFileType(_handle); - Contract.Assert(handleType == Win32Native.FILE_TYPE_DISK || handleType == Win32Native.FILE_TYPE_PIPE || handleType == Win32Native.FILE_TYPE_CHAR, "FileStream was passed an unknown file type!"); - _isAsync = isAsync; - _canRead = 0 != (access & FileAccess.Read); - _canWrite = 0 != (access & FileAccess.Write); - _canSeek = handleType == Win32Native.FILE_TYPE_DISK; - _bufferSize = bufferSize; - _readPos = 0; - _readLen = 0; - _writePos = 0; - _fileName = null; - _isPipe = handleType == Win32Native.FILE_TYPE_PIPE; - - // This is necessary for async IO using IO Completion ports via our - // managed Threadpool API's. This calls the OS's - // BindIoCompletionCallback method, and passes in a stub for the - // LPOVERLAPPED_COMPLETION_ROUTINE. This stub looks at the Overlapped - // struct for this request and gets a delegate to a managed callback - // from there, which it then calls on a threadpool thread. (We allocate - // our native OVERLAPPED structs 2 pointers too large and store EE - // state & a handle to a delegate there.) -#if !FEATURE_CORECLR - if (_isAsync) { - bool b = false; - try { - b = ThreadPool.BindHandle(_handle); - } - catch (ApplicationException) { - // If you passed in a synchronous handle and told us to use - // it asynchronously, throw here. - throw new ArgumentException(Environment.GetResourceString("Arg_HandleNotAsync")); - } - if (!b) { - throw new IOException(Environment.GetResourceString("IO.IO_BindHandleFailed")); - } - } - else { -#endif // FEATURE_CORECLR - if (handleType != Win32Native.FILE_TYPE_PIPE) - VerifyHandleIsSync(); -#if !FEATURE_CORECLR - } -#endif // FEATURE_CORECLR - - if (_canSeek) - SeekCore(0, SeekOrigin.Current); - else - _pos = 0; - } - - [System.Security.SecuritySafeCritical] // auto-generated - private static Win32Native.SECURITY_ATTRIBUTES GetSecAttrs(FileShare share) - { - Win32Native.SECURITY_ATTRIBUTES secAttrs = null; - if ((share & FileShare.Inheritable) != 0) { - secAttrs = new Win32Native.SECURITY_ATTRIBUTES(); - secAttrs.nLength = (int)Marshal.SizeOf(secAttrs); - - secAttrs.bInheritHandle = 1; - } - return secAttrs; - } - -#if FEATURE_MACL - // If pinningHandle is not null, caller must free it AFTER the call to - // CreateFile has returned. - [System.Security.SecuritySafeCritical] // auto-generated - private unsafe static Win32Native.SECURITY_ATTRIBUTES GetSecAttrs(FileShare share, FileSecurity fileSecurity, out Object pinningHandle) - { - pinningHandle = null; - Win32Native.SECURITY_ATTRIBUTES secAttrs = null; - if ((share & FileShare.Inheritable) != 0 || fileSecurity != null) { - secAttrs = new Win32Native.SECURITY_ATTRIBUTES(); - secAttrs.nLength = (int)Marshal.SizeOf(secAttrs); - - if ((share & FileShare.Inheritable) != 0) { - secAttrs.bInheritHandle = 1; - } - - // For ACL's, get the security descriptor from the FileSecurity. - if (fileSecurity != null) { - byte[] sd = fileSecurity.GetSecurityDescriptorBinaryForm(); - pinningHandle = GCHandle.Alloc(sd, GCHandleType.Pinned); - fixed(byte* pSecDescriptor = sd) - secAttrs.pSecurityDescriptor = pSecDescriptor; - } - } - return secAttrs; - } -#endif - - // Verifies that this handle supports synchronous IO operations (unless you - // didn't open it for either reading or writing). - [System.Security.SecuritySafeCritical] // auto-generated - private unsafe void VerifyHandleIsSync() - { - // 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 - // FileStream's thread). - - // Do NOT change this to use a byte[] of length 0, or test test won't - // work. Our ReadFile & WriteFile methods are special cased to return - // for arrays of length 0, since we'd get an IndexOutOfRangeException - // while using C#'s fixed syntax. - byte[] bytes = new byte[1]; - int hr = 0; - int r = 0; - - // 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 (CanRead) { - r = ReadFileNative(_handle, bytes, 0, 0, null, out hr); - } - else if (CanWrite) { - r = WriteFileNative(_handle, bytes, 0, 0, null, out hr); - } - - if (hr==ERROR_INVALID_PARAMETER) - throw new ArgumentException(Environment.GetResourceString("Arg_HandleNotSync")); - if (hr == Win32Native.ERROR_INVALID_HANDLE) - __Error.WinIOError(hr, "<OS handle>"); - } - - - public override bool CanRead { - [Pure] - get { return _canRead; } - } - - public override bool CanWrite { - [Pure] - get { return _canWrite; } - } - - public override bool CanSeek { - [Pure] - get { return _canSeek; } - } - - public virtual bool IsAsync { - get { return _isAsync; } - } - - public override long Length { - [System.Security.SecuritySafeCritical] // auto-generated - get { - if (_handle.IsClosed) __Error.FileNotOpen(); - if (!CanSeek) __Error.SeekNotSupported(); - int hi = 0, lo = 0; - - lo = Win32Native.GetFileSize(_handle, out hi); - - if (lo==-1) { // Check for either an error or a 4GB - 1 byte file. - int hr = Marshal.GetLastWin32Error(); - if (hr != 0) - __Error.WinIOError(hr, String.Empty); - } - long len = (((long)hi) << 32) | ((uint) lo); - // If we're writing near the end of the file, we must include our - // internal buffer in our Length calculation. Don't flush because - // we use the length of the file in our async write method. - if (_writePos > 0 && _pos + _writePos > len) - len = _writePos + _pos; - return len; - } - } - - public String Name { - [System.Security.SecuritySafeCritical] - get { - if (_fileName == null) - return Environment.GetResourceString("IO_UnknownFileName"); -#if FEATURE_CORECLR - FileSecurityState sourceState = new FileSecurityState(FileSecurityStateAccess.PathDiscovery, String.Empty, _fileName); - sourceState.EnsureState(); -#else - new FileIOPermission(FileIOPermissionAccess.PathDiscovery, new String[] { _fileName }, false, false).Demand(); -#endif - return _fileName; - } - } - - internal String NameInternal { - get { - if (_fileName == null) - return "<UnknownFileName>"; - return _fileName; - } - } - - public override long Position { - [System.Security.SecuritySafeCritical] // auto-generated - get { - if (_handle.IsClosed) __Error.FileNotOpen(); - if (!CanSeek) __Error.SeekNotSupported(); - - Contract.Assert((_readPos == 0 && _readLen == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLen), "We're either reading or writing, but not both."); - - // Verify that internal position is in sync with the handle - if (_exposedHandle) - VerifyOSHandlePosition(); - - // Compensate for buffer that we read from the handle (_readLen) Vs what the user - // read so far from the internel buffer (_readPos). Of course add any unwrittern - // buffered data - return _pos + (_readPos - _readLen + _writePos); - } - set { - if (value < 0) throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - Contract.EndContractBlock(); - if (_writePos > 0) FlushWrite(false); - _readPos = 0; - _readLen = 0; - Seek(value, SeekOrigin.Begin); - } - } - -#if FEATURE_MACL - [System.Security.SecuritySafeCritical] // auto-generated - public FileSecurity GetAccessControl() - { - if (_handle.IsClosed) __Error.FileNotOpen(); - return new FileSecurity(_handle, _fileName, AccessControlSections.Access | AccessControlSections.Owner | AccessControlSections.Group); - } - - [System.Security.SecuritySafeCritical] // auto-generated - public void SetAccessControl(FileSecurity fileSecurity) - { - if (fileSecurity == null) - throw new ArgumentNullException("fileSecurity"); - Contract.EndContractBlock(); - - if (_handle.IsClosed) __Error.FileNotOpen(); - - fileSecurity.Persist(_handle, _fileName); - } -#endif - - [System.Security.SecuritySafeCritical] // auto-generated - protected override void Dispose(bool disposing) - { - // Nothing will be done differently based on whether we are - // disposing vs. finalizing. This is taking advantage of the - // weak ordering between normal finalizable objects & critical - // finalizable objects, which I included in the SafeHandle - // design for FileStream, which would often "just work" when - // finalized. - try { - if (_handle != null && !_handle.IsClosed) { - // Flush data to disk iff we were writing. After - // thinking about this, we also don't need to flush - // our read position, regardless of whether the handle - // was exposed to the user. They probably would NOT - // want us to do this. - if (_writePos > 0) { - FlushWrite(!disposing); - } - } - } - finally { - if (_handle != null && !_handle.IsClosed) - _handle.Dispose(); - - _canRead = false; - _canWrite = false; - _canSeek = false; - // Don't set the buffer to null, to avoid a NullReferenceException - // when users have a race condition in their code (ie, they call - // Close when calling another method on Stream like Read). - //_buffer = null; - base.Dispose(disposing); - } - } - - [System.Security.SecuritySafeCritical] // auto-generated - ~FileStream() - { - if (_handle != null) { - BCLDebug.Correctness(_handle.IsClosed, "You didn't close a FileStream & it got finalized. Name: \""+_fileName+"\""); - Dispose(false); - } - } - - public override void Flush() - { - Flush(false); - } - - [System.Security.SecuritySafeCritical] - public virtual void Flush(Boolean flushToDisk) - { - // This code is duplicated in Dispose - if (_handle.IsClosed) __Error.FileNotOpen(); - - FlushInternalBuffer(); - - if (flushToDisk && CanWrite) - { - FlushOSBuffer(); - } - } - - private void FlushInternalBuffer() - { - if (_writePos > 0) - { - FlushWrite(false); - } - else if (_readPos < _readLen && CanSeek) - { - FlushRead(); - } - } - - [System.Security.SecuritySafeCritical] - private void FlushOSBuffer() - { - if (!Win32Native.FlushFileBuffers(_handle)) - { - __Error.WinIOError(); - } - } - - // Reading is done by blocks from the file, but someone could read - // 1 byte from the buffer then write. At that point, the OS's file - // pointer is out of sync with the stream's position. All write - // functions should call this function to preserve the position in the file. - private void FlushRead() { - Contract.Assert(_writePos == 0, "FileStream: Write buffer must be empty in FlushRead!"); - if (_readPos - _readLen != 0) { - Contract.Assert(CanSeek, "FileStream will lose buffered read data now."); - SeekCore(_readPos - _readLen, SeekOrigin.Current); - } - _readPos = 0; - _readLen = 0; - } - - // Writes are buffered. Anytime the buffer fills up - // (_writePos + delta > _bufferSize) or the buffer switches to reading - // and there is left over data (_writePos > 0), this function must be called. - private void FlushWrite(bool calledFromFinalizer) { - Contract.Assert(_readPos == 0 && _readLen == 0, "FileStream: Read buffer must be empty in FlushWrite!"); - - if (_isAsync) { - IAsyncResult asyncResult = BeginWriteCore(_buffer, 0, _writePos, null, null); - // With our Whidbey async IO & overlapped support for AD unloads, - // we don't strictly need to block here to release resources - // since that support takes care of the pinning & freeing the - // overlapped struct. We need to do this when called from - // Close so that the handle is closed when Close returns, but - // we do't need to call EndWrite from the finalizer. - // Additionally, if we do call EndWrite, we block forever - // because AD unloads prevent us from running the managed - // callback from the IO completion port. Blocking here when - // called from the finalizer during AD unload is clearly wrong, - // but we can't use any sort of test for whether the AD is - // unloading because if we weren't unloading, an AD unload - // could happen on a separate thread before we call EndWrite. - if (!calledFromFinalizer) - EndWrite(asyncResult); - } - else - WriteCore(_buffer, 0, _writePos); - - _writePos = 0; - } - - - [Obsolete("This property has been deprecated. Please use FileStream's SafeFileHandle property instead. http://go.microsoft.com/fwlink/?linkid=14202")] - public virtual IntPtr Handle { - [System.Security.SecurityCritical] // auto-generated_required -#if !FEATURE_CORECLR - [SecurityPermissionAttribute(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] -#endif - get { - Flush(); - // Explicitly dump any buffered data, since the user could move our - // position or write to the file. - _readPos = 0; - _readLen = 0; - _writePos = 0; - _exposedHandle = true; - - return _handle.DangerousGetHandle(); - } - } - - public virtual SafeFileHandle SafeFileHandle { - [System.Security.SecurityCritical] // auto-generated_required -#if !FEATURE_CORECLR - [SecurityPermissionAttribute(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] -#endif - get { - Flush(); - // Explicitly dump any buffered data, since the user could move our - // position or write to the file. - _readPos = 0; - _readLen = 0; - _writePos = 0; - _exposedHandle = true; - - return _handle; - } - } - - [System.Security.SecuritySafeCritical] // auto-generated - public override void SetLength(long value) - { - if (value < 0) - throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - Contract.EndContractBlock(); - - if (_handle.IsClosed) __Error.FileNotOpen(); - if (!CanSeek) __Error.SeekNotSupported(); - if (!CanWrite) __Error.WriteNotSupported(); - - // Handle buffering updates. - if (_writePos > 0) { - FlushWrite(false); - } - else if (_readPos < _readLen) { - FlushRead(); - } - _readPos = 0; - _readLen = 0; - - if (_appendStart != -1 && value < _appendStart) - throw new IOException(Environment.GetResourceString("IO.IO_SetLengthAppendTruncate")); - SetLengthCore(value); - } - - // We absolutely need this method broken out so that BeginWriteCore can call - // a method without having to go through buffering code that might call - // FlushWrite. - [System.Security.SecuritySafeCritical] // auto-generated - private void SetLengthCore(long value) - { - Contract.Assert(value >= 0, "value >= 0"); - long origPos = _pos; - - if (_exposedHandle) - VerifyOSHandlePosition(); - if (_pos != value) - SeekCore(value, SeekOrigin.Begin); - if (!Win32Native.SetEndOfFile(_handle)) { - int hr = Marshal.GetLastWin32Error(); - if (hr==__Error.ERROR_INVALID_PARAMETER) - throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_FileLengthTooBig")); - __Error.WinIOError(hr, String.Empty); - } - // Return file pointer to where it was before setting length - if (origPos != value) { - if (origPos < value) - SeekCore(origPos, SeekOrigin.Begin); - else - SeekCore(0, SeekOrigin.End); - } - } - - [System.Security.SecuritySafeCritical] // auto-generated - public override int Read([In, Out] byte[] array, int offset, int count) { - if (array==null) - throw new ArgumentNullException("array", Environment.GetResourceString("ArgumentNull_Buffer")); - if (offset < 0) - throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (count < 0) - throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (array.Length - offset < count) - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); - Contract.EndContractBlock(); - - if (_handle.IsClosed) __Error.FileNotOpen(); - - Contract.Assert((_readPos==0 && _readLen==0 && _writePos >= 0) || (_writePos==0 && _readPos <= _readLen), "We're either reading or writing, but not both."); - - bool isBlocked = false; - int n = _readLen - _readPos; - // if the read buffer is empty, read into either user's array or our - // buffer, depending on number of bytes user asked for and buffer size. - if (n == 0) { - if (!CanRead) __Error.ReadNotSupported(); - if (_writePos > 0) FlushWrite(false); - if (!CanSeek || (count >= _bufferSize)) { - n = ReadCore(array, offset, count); - // Throw away read buffer. - _readPos = 0; - _readLen = 0; - return n; - } - if (_buffer == null) _buffer = new byte[_bufferSize]; - n = ReadCore(_buffer, 0, _bufferSize); - if (n == 0) return 0; - isBlocked = n < _bufferSize; - _readPos = 0; - _readLen = n; - } - // Now copy min of count or numBytesAvailable (ie, near EOF) to array. - if (n > count) n = count; - Buffer.InternalBlockCopy(_buffer, _readPos, array, offset, n); - _readPos += n; - - // We may have read less than the number of bytes the user asked - // for, but that is part of the Stream contract. Reading again for - // more data may cause us to block if we're using a device with - // no clear end of file, such as a serial port or pipe. If we - // blocked here & this code was used with redirected pipes for a - // process's standard output, this can lead to deadlocks involving - // two processes. But leave this here for files to avoid what would - // probably be a breaking change. -- - - // If we are reading from a device with no clear EOF like a - // serial port or a pipe, this will cause us to block incorrectly. - if (!_isPipe) { - // If we hit the end of the buffer and didn't have enough bytes, we must - // read some more from the underlying stream. However, if we got - // fewer bytes from the underlying stream than we asked for (ie, we're - // probably blocked), don't ask for more bytes. - if (n < count && !isBlocked) { - Contract.Assert(_readPos == _readLen, "Read buffer should be empty!"); - int moreBytesRead = ReadCore(array, offset + n, count - n); - n += moreBytesRead; - // We've just made our buffer inconsistent with our position - // pointer. We must throw away the read buffer. - _readPos = 0; - _readLen = 0; - } - } - - return n; - } - - [System.Security.SecuritySafeCritical] // auto-generated - private unsafe int ReadCore(byte[] buffer, int offset, int count) { - Contract.Assert(!_handle.IsClosed, "!_handle.IsClosed"); - Contract.Assert(CanRead, "CanRead"); - - Contract.Assert(buffer != null, "buffer != null"); - Contract.Assert(_writePos == 0, "_writePos == 0"); - Contract.Assert(offset >= 0, "offset is negative"); - Contract.Assert(count >= 0, "count is negative"); - - if (_isAsync) { - IAsyncResult result = BeginReadCore(buffer, offset, count, null, null, 0); - return EndRead(result); - } - - // Make sure we are reading from the right spot - if (_exposedHandle) - VerifyOSHandlePosition(); - - int hr = 0; - int r = ReadFileNative(_handle, buffer, offset, count, null, out hr); - if (r == -1) { - // For pipes, ERROR_BROKEN_PIPE is the normal end of the pipe. - if (hr == ERROR_BROKEN_PIPE) { - r = 0; - } - else { - if (hr == ERROR_INVALID_PARAMETER) - throw new ArgumentException(Environment.GetResourceString("Arg_HandleNotSync")); - - __Error.WinIOError(hr, String.Empty); - } - } - Contract.Assert(r >= 0, "FileStream's ReadCore is likely broken."); - _pos += r; - - return r; - } - - [System.Security.SecuritySafeCritical] // auto-generated - public override long Seek(long offset, SeekOrigin origin) { - if (origin<SeekOrigin.Begin || origin>SeekOrigin.End) - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidSeekOrigin")); - Contract.EndContractBlock(); - if (_handle.IsClosed) __Error.FileNotOpen(); - if (!CanSeek) __Error.SeekNotSupported(); - - Contract.Assert((_readPos==0 && _readLen==0 && _writePos >= 0) || (_writePos==0 && _readPos <= _readLen), "We're either reading or writing, but not both."); - - // If we've got bytes in our buffer to write, write them out. - // If we've read in and consumed some bytes, we'll have to adjust - // our seek positions ONLY IF we're seeking relative to the current - // position in the stream. This simulates doing a seek to the new - // position, then a read for the number of bytes we have in our buffer. - if (_writePos > 0) { - FlushWrite(false); - } - else if (origin == SeekOrigin.Current) { - // Don't call FlushRead here, which would have caused an infinite - // loop. Simply adjust the seek origin. This isn't necessary - // if we're seeking relative to the beginning or end of the stream. - offset -= (_readLen - _readPos); - } - - // Verify that internal position is in sync with the handle - if (_exposedHandle) - VerifyOSHandlePosition(); - - long oldPos = _pos + (_readPos - _readLen); - long pos = SeekCore(offset, origin); - - // Prevent users from overwriting data in a file that was opened in - // append mode. - if (_appendStart != -1 && pos < _appendStart) { - SeekCore(oldPos, SeekOrigin.Begin); - throw new IOException(Environment.GetResourceString("IO.IO_SeekAppendOverwrite")); - } - - // We now must update the read buffer. We can in some cases simply - // update _readPos within the buffer, copy around the buffer so our - // Position property is still correct, and avoid having to do more - // reads from the disk. Otherwise, discard the buffer's contents. - if (_readLen > 0) { - // We can optimize the following condition: - // oldPos - _readPos <= pos < oldPos + _readLen - _readPos - if (oldPos == pos) { - if (_readPos > 0) { - //Console.WriteLine("Seek: seeked for 0, adjusting buffer back by: "+_readPos+" _readLen: "+_readLen); - Buffer.InternalBlockCopy(_buffer, _readPos, _buffer, 0, _readLen - _readPos); - _readLen -= _readPos; - _readPos = 0; - } - // If we still have buffered data, we must update the stream's - // position so our Position property is correct. - if (_readLen > 0) - SeekCore(_readLen, SeekOrigin.Current); - } - else if (oldPos - _readPos < pos && pos < oldPos + _readLen - _readPos) { - int diff = (int)(pos - oldPos); - //Console.WriteLine("Seek: diff was "+diff+", readpos was "+_readPos+" adjusting buffer - shrinking by "+ (_readPos + diff)); - Buffer.InternalBlockCopy(_buffer, _readPos+diff, _buffer, 0, _readLen - (_readPos + diff)); - _readLen -= (_readPos + diff); - _readPos = 0; - if (_readLen > 0) - SeekCore(_readLen, SeekOrigin.Current); - } - else { - // Lose the read buffer. - _readPos = 0; - _readLen = 0; - } - Contract.Assert(_readLen >= 0 && _readPos <= _readLen, "_readLen should be nonnegative, and _readPos should be less than or equal _readLen"); - Contract.Assert(pos == Position, "Seek optimization: pos != Position! Buffer math was mangled."); - } - return pos; - } - - // This doesn't do argument checking. Necessary for SetLength, which must - // set the file pointer beyond the end of the file. This will update the - // internal position - [System.Security.SecuritySafeCritical] // auto-generated - private long SeekCore(long offset, SeekOrigin origin) { - Contract.Assert(!_handle.IsClosed && CanSeek, "!_handle.IsClosed && CanSeek"); - Contract.Assert(origin>=SeekOrigin.Begin && origin<=SeekOrigin.End, "origin>=SeekOrigin.Begin && origin<=SeekOrigin.End"); - int hr = 0; - long ret = 0; - - ret = Win32Native.SetFilePointer(_handle, offset, origin, out hr); - if (ret == -1) { - // #errorInvalidHandle - // If ERROR_INVALID_HANDLE is returned, it doesn't suffice to set - // the handle as invalid; the handle must also be closed. - // - // Marking the handle as invalid but not closing the handle - // resulted in exceptions during finalization and locked column - // values (due to invalid but unclosed handle) in SQL FileStream - // scenarios. - // - // A more mainstream scenario involves accessing a file on a - // network share. ERROR_INVALID_HANDLE may occur because the network - // connection was dropped and the server closed the handle. However, - // the client side handle is still open and even valid for certain - // operations. - // - // Note that Dispose doesn't throw so we don't need to special case. - // SetHandleAsInvalid only sets _closed field to true (without - // actually closing handle) so we don't need to call that as well. - if (hr == Win32Native.ERROR_INVALID_HANDLE) - _handle.Dispose(); - __Error.WinIOError(hr, String.Empty); - } - - _pos = ret; - return ret; - } - - // Checks the position of the OS's handle equals what we expect it to. - // This will fail if someone else moved the FileStream's handle or if - // we've hit a bug in FileStream's position updating code. - private void VerifyOSHandlePosition() - { - if (!CanSeek) - return; - - // SeekCore will override the current _pos, so save it now - long oldPos = _pos; - long curPos = SeekCore(0, SeekOrigin.Current); - - if (curPos != oldPos) { - // For reads, this is non-fatal but we still could have returned corrupted - // data in some cases. So discard the internal buffer. Potential MDA - _readPos = 0; - _readLen = 0; - if(_writePos > 0) { - // Discard the buffer and let the user know! - _writePos = 0; - throw new IOException(Environment.GetResourceString("IO.IO_FileStreamHandlePosition")); - } - } - } - - [System.Security.SecuritySafeCritical] // auto-generated - public override void Write(byte[] array, int offset, int count) { - if (array==null) - throw new ArgumentNullException("array", Environment.GetResourceString("ArgumentNull_Buffer")); - if (offset < 0) - throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (count < 0) - throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (array.Length - offset < count) - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); - Contract.EndContractBlock(); - - if (_handle.IsClosed) __Error.FileNotOpen(); - - if (_writePos == 0) - { - // Ensure we can write to the stream, and ready buffer for writing. - if (!CanWrite) __Error.WriteNotSupported(); - if (_readPos < _readLen) FlushRead(); - _readPos = 0; - _readLen = 0; - } - - // If our buffer has data in it, copy data from the user's array into - // the buffer, and if we can fit it all there, return. Otherwise, write - // the buffer to disk and copy any remaining data into our buffer. - // The assumption here is memcpy is cheaper than disk (or net) IO. - // (10 milliseconds to disk vs. ~20-30 microseconds for a 4K memcpy) - // So the extra copying will reduce the total number of writes, in - // non-pathological cases (ie, write 1 byte, then write for the buffer - // size repeatedly) - if (_writePos > 0) { - int numBytes = _bufferSize - _writePos; // space left in buffer - if (numBytes > 0) { - if (numBytes > count) - numBytes = count; - Buffer.InternalBlockCopy(array, offset, _buffer, _writePos, numBytes); - _writePos += numBytes; - if (count==numBytes) return; - offset += numBytes; - count -= numBytes; - } - // Reset our buffer. We essentially want to call FlushWrite - // without calling Flush on the underlying Stream. - - if (_isAsync) { - IAsyncResult result = BeginWriteCore(_buffer, 0, _writePos, null, null); - EndWrite(result); - } - else - { - WriteCore(_buffer, 0, _writePos); - } - - _writePos = 0; - } - // If the buffer would slow writes down, avoid buffer completely. - if (count >= _bufferSize) { - Contract.Assert(_writePos == 0, "FileStream cannot have buffered data to write here! Your stream will be corrupted."); - WriteCore(array, offset, count); - return; - } - else if (count == 0) - return; // Don't allocate a buffer then call memcpy for 0 bytes. - if (_buffer==null) _buffer = new byte[_bufferSize]; - // Copy remaining bytes into buffer, to write at a later date. - Buffer.InternalBlockCopy(array, offset, _buffer, _writePos, count); - _writePos = count; - return; - } - - [System.Security.SecuritySafeCritical] // auto-generated - private unsafe void WriteCore(byte[] buffer, int offset, int count) { - Contract.Assert(!_handle.IsClosed, "!_handle.IsClosed"); - Contract.Assert(CanWrite, "CanWrite"); - - Contract.Assert(buffer != null, "buffer != null"); - Contract.Assert(_readPos == _readLen, "_readPos == _readLen"); - Contract.Assert(offset >= 0, "offset is negative"); - Contract.Assert(count >= 0, "count is negative"); - - if (_isAsync) { - IAsyncResult result = BeginWriteCore(buffer, offset, count, null, null); - EndWrite(result); - return; - } - - // Make sure we are writing to the position that we think we are - if (_exposedHandle) - VerifyOSHandlePosition(); - - int hr = 0; - int r = WriteFileNative(_handle, buffer, offset, count, null, out hr); - if (r == -1) { - // For pipes, ERROR_NO_DATA is not an error, but the pipe is closing. - if (hr == ERROR_NO_DATA) { - r = 0; - } - else { - // ERROR_INVALID_PARAMETER may be returned for writes - // where the position is too large (ie, writing at Int64.MaxValue - // on Win9x) OR for synchronous writes to a handle opened - // asynchronously. - if (hr == ERROR_INVALID_PARAMETER) - throw new IOException(Environment.GetResourceString("IO.IO_FileTooLongOrHandleNotSync")); - __Error.WinIOError(hr, String.Empty); - } - } - Contract.Assert(r >= 0, "FileStream's WriteCore is likely broken."); - _pos += r; - return; - } - - - [System.Security.SecuritySafeCritical] // auto-generated - [HostProtection(ExternalThreading = true)] - public override IAsyncResult BeginRead(byte[] array, int offset, int numBytes, AsyncCallback userCallback, Object stateObject) - { - if (array==null) - throw new ArgumentNullException("array"); - if (offset < 0) - throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (numBytes < 0) - throw new ArgumentOutOfRangeException("numBytes", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (array.Length - offset < numBytes) - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); - Contract.EndContractBlock(); - - if (_handle.IsClosed) __Error.FileNotOpen(); - - if (!_isAsync) - return base.BeginRead(array, offset, numBytes, userCallback, stateObject); - else - return BeginReadAsync(array, offset, numBytes, userCallback, stateObject); - } - - [System.Security.SecuritySafeCritical] // auto-generated - [HostProtection(ExternalThreading = true)] - private FileStreamAsyncResult BeginReadAsync(byte[] array, int offset, int numBytes, AsyncCallback userCallback, Object stateObject) - { - Contract.Assert(_isAsync); - - if (!CanRead) __Error.ReadNotSupported(); - - Contract.Assert((_readPos == 0 && _readLen == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLen), "We're either reading or writing, but not both."); - - if (_isPipe) - { - // When redirecting stdout & stderr with the Process class, it's easy to deadlock your - // parent & child processes when doing writes 4K at a time. The - // OS appears to use a 4K buffer internally. If you write to a - // pipe that is full, you will block until someone read from - // that pipe. If you try reading from an empty pipe and - // FileStream's BeginRead blocks waiting for data to fill it's - // internal buffer, you will be blocked. In a case where a child - // process writes to stdout & stderr while a parent process tries - // reading from both, you can easily get into a deadlock here. - // To avoid this deadlock, don't buffer when doing async IO on - // pipes. But don't completely ignore buffered data either. - if (_readPos < _readLen) - { - int n = _readLen - _readPos; - if (n > numBytes) n = numBytes; - Buffer.InternalBlockCopy(_buffer, _readPos, array, offset, n); - _readPos += n; - - // Return a synchronous FileStreamAsyncResult - return FileStreamAsyncResult.CreateBufferedReadResult(n, userCallback, stateObject, false); - } - else - { - Contract.Assert(_writePos == 0, "FileStream must not have buffered write data here! Pipes should be unidirectional."); - return BeginReadCore(array, offset, numBytes, userCallback, stateObject, 0); - } - } - - Contract.Assert(!_isPipe, "Should not be a pipe."); - - // Handle buffering. - if (_writePos > 0) FlushWrite(false); - if (_readPos == _readLen) - { - // I can't see how to handle buffering of async requests when - // filling the buffer asynchronously, without a lot of complexity. - // The problems I see are issuing an async read, we do an async - // read to fill the buffer, then someone issues another read - // (either synchronously or asynchronously) before the first one - // returns. This would involve some sort of complex buffer locking - // that we probably don't want to get into, at least not in V1. - // If we did a sync read to fill the buffer, we could avoid the - // problem, and any async read less than 64K gets turned into a - // synchronous read by NT anyways... -- - - if (numBytes < _bufferSize) - { - if (_buffer == null) _buffer = new byte[_bufferSize]; - IAsyncResult bufferRead = BeginReadCore(_buffer, 0, _bufferSize, null, null, 0); - _readLen = EndRead(bufferRead); - int n = _readLen; - if (n > numBytes) n = numBytes; - Buffer.InternalBlockCopy(_buffer, 0, array, offset, n); - _readPos = n; - - // Return a synchronous FileStreamAsyncResult - return FileStreamAsyncResult.CreateBufferedReadResult(n, userCallback, stateObject, false); - } - else - { - // Here we're making our position pointer inconsistent - // with our read buffer. Throw away the read buffer's contents. - _readPos = 0; - _readLen = 0; - return BeginReadCore(array, offset, numBytes, userCallback, stateObject, 0); - } - } - else - { - int n = _readLen - _readPos; - if (n > numBytes) n = numBytes; - Buffer.InternalBlockCopy(_buffer, _readPos, array, offset, n); - _readPos += n; - - if (n >= numBytes) - { - // Return a synchronous FileStreamAsyncResult - return FileStreamAsyncResult.CreateBufferedReadResult(n, userCallback, stateObject, false); - } - else - { - // For streams with no clear EOF like serial ports or pipes - // we cannot read more data without causing an app to block - // incorrectly. Pipes don't go down this path - // though. This code needs to be fixed. - // Throw away read buffer. - _readPos = 0; - _readLen = 0; - return BeginReadCore(array, offset + n, numBytes - n, userCallback, stateObject, n); - } - // WARNING: all state on asyncResult objects must be set before - // we call ReadFile in BeginReadCore, since the OS can run our - // callback & the user's callback before ReadFile returns. - } - } - - [System.Security.SecuritySafeCritical] // auto-generated - unsafe private FileStreamAsyncResult BeginReadCore(byte[] bytes, int offset, int numBytes, AsyncCallback userCallback, Object stateObject, int numBufferedBytesRead) - { - Contract.Assert(!_handle.IsClosed, "!_handle.IsClosed"); - Contract.Assert(CanRead, "CanRead"); - Contract.Assert(bytes != null, "bytes != null"); - Contract.Assert(_writePos == 0, "_writePos == 0"); - Contract.Assert(_isAsync, "BeginReadCore doesn't work on synchronous file streams!"); - Contract.Assert(offset >= 0, "offset is negative"); - Contract.Assert(numBytes >= 0, "numBytes is negative"); - - // Create and store async stream class library specific data in the async result - - // Must pass in _numBufferedBytes here to ensure all the state on the IAsyncResult - // object is set before we call ReadFile, which gives the OS an - // opportunity to run our callback (including the user callback & - // the call to EndRead) before ReadFile has returned. - FileStreamAsyncResult asyncResult = new FileStreamAsyncResult(numBufferedBytesRead, bytes, _handle, userCallback, stateObject, false); - NativeOverlapped* intOverlapped = asyncResult.OverLapped; - - // Calculate position in the file we should be at after the read is done - if (CanSeek) { - long len = Length; - - // Make sure we are reading from the position that we think we are - if (_exposedHandle) - VerifyOSHandlePosition(); - - if (_pos + numBytes > len) { - if (_pos <= len) - numBytes = (int) (len - _pos); - else - numBytes = 0; - } - - // Now set the position to read from in the NativeOverlapped struct - // For pipes, we should leave the offset fields set to 0. - intOverlapped->OffsetLow = unchecked((int)_pos); - intOverlapped->OffsetHigh = (int)(_pos>>32); - - // When using overlapped IO, the OS is not supposed to - // touch the file pointer location at all. We will adjust it - // ourselves. This isn't threadsafe. - - // WriteFile should not update the file pointer when writing - // in overlapped mode, according to MSDN. But it does update - // the file pointer when writing to a UNC path! - // So changed the code below to seek to an absolute - // location, not a relative one. ReadFile seems consistent though. - SeekCore(numBytes, SeekOrigin.Current); - } - - if (FrameworkEventSource.IsInitialized && FrameworkEventSource.Log.IsEnabled(EventLevel.Informational, FrameworkEventSource.Keywords.ThreadTransfer)) - FrameworkEventSource.Log.ThreadTransferSend((long)(asyncResult.OverLapped), 2, string.Empty, false); - - // queue an async ReadFile operation and pass in a packed overlapped - int hr = 0; - int r = ReadFileNative(_handle, bytes, offset, numBytes, intOverlapped, out hr); - // ReadFile, the OS version, will return 0 on failure. But - // my ReadFileNative wrapper returns -1. My wrapper will return - // the following: - // On error, r==-1. - // On async requests that are still pending, r==-1 w/ hr==ERROR_IO_PENDING - // on async requests that completed sequentially, r==0 - // You will NEVER RELIABLY be able to get the number of bytes - // read back from this call when using overlapped structures! You must - // not pass in a non-null lpNumBytesRead to ReadFile when using - // overlapped structures! This is by design NT behavior. - if (r==-1 && numBytes!=-1) { - - // For pipes, when they hit EOF, they will come here. - if (hr == ERROR_BROKEN_PIPE) { - // Not an error, but EOF. AsyncFSCallback will NOT be - // called. Call the user callback here. - - // We clear the overlapped status bit for this special case. - // Failure to do so looks like we are freeing a pending overlapped later. - intOverlapped->InternalLow = IntPtr.Zero; - asyncResult.CallUserCallback(); - // EndRead will free the Overlapped struct correctly. - } - else if (hr != ERROR_IO_PENDING) { - if (!_handle.IsClosed && CanSeek) // Update Position - It could be anywhere. - SeekCore(0, SeekOrigin.Current); - - if (hr == ERROR_HANDLE_EOF) - __Error.EndOfFile(); - else - __Error.WinIOError(hr, String.Empty); - } - } - else { - // Due to a workaround for a race condition in NT's ReadFile & - // WriteFile routines, we will always be returning 0 from ReadFileNative - // when we do async IO instead of the number of bytes read, - // irregardless of whether the operation completed - // synchronously or asynchronously. We absolutely must not - // set asyncResult._numBytes here, since will never have correct - // results. - //Console.WriteLine("ReadFile returned: "+r+" (0x"+Int32.Format(r, "x")+") The IO completed synchronously, but the user callback was called on a separate thread"); - } - - return asyncResult; - } - - [System.Security.SecuritySafeCritical] // Although the unsafe code is only required in PAL, the block is wide scoped. Leave it here for desktop to ensure it's reviewed. - public unsafe override int EndRead(IAsyncResult asyncResult) - { - // There are 3 significantly different IAsyncResults we'll accept - // here. One is from Stream::BeginRead. The other two are variations - // on our FileStreamAsyncResult. One is from BeginReadCore, - // while the other is from the BeginRead buffering wrapper. - if (asyncResult==null) - throw new ArgumentNullException("asyncResult"); - Contract.EndContractBlock(); - - if (!_isAsync) - return base.EndRead(asyncResult); - - FileStreamAsyncResult afsar = asyncResult as FileStreamAsyncResult; - if (afsar==null || afsar.IsWrite) - __Error.WrongAsyncResult(); - - // Ensure we don't have any race conditions by doing an interlocked - // CompareExchange here. Avoids corrupting memory via freeing the - // NativeOverlapped class or GCHandle twice. -- - if (1 == Interlocked.CompareExchange(ref afsar._EndXxxCalled, 1, 0)) - __Error.EndReadCalledTwice(); - - // Obtain the WaitHandle, but don't use public property in case we - // delay initialize the manual reset event in the future. - afsar.Wait(); - - // Free memory & GC handles. - afsar.ReleaseNativeResource(); - - // Now check for any error during the read. - if (afsar.ErrorCode != 0) - __Error.WinIOError(afsar.ErrorCode, String.Empty); - - return afsar.NumBytesRead; - } - - // Reads a byte from the file stream. Returns the byte cast to an int - // or -1 if reading from the end of the stream. - [System.Security.SecuritySafeCritical] // auto-generated - public override int ReadByte() { - if (_handle.IsClosed) __Error.FileNotOpen(); - if (_readLen==0 && !CanRead) __Error.ReadNotSupported(); - Contract.Assert((_readPos==0 && _readLen==0 && _writePos >= 0) || (_writePos==0 && _readPos <= _readLen), "We're either reading or writing, but not both."); - if (_readPos == _readLen) { - if (_writePos > 0) FlushWrite(false); - Contract.Assert(_bufferSize > 0, "_bufferSize > 0"); - if (_buffer == null) _buffer = new byte[_bufferSize]; - _readLen = ReadCore(_buffer, 0, _bufferSize); - _readPos = 0; - } - if (_readPos == _readLen) - return -1; - - int result = _buffer[_readPos]; - _readPos++; - return result; - } - - - [System.Security.SecuritySafeCritical] // auto-generated - [HostProtection(ExternalThreading=true)] - public override IAsyncResult BeginWrite(byte[] array, int offset, int numBytes, AsyncCallback userCallback, Object stateObject) - { - if (array==null) - throw new ArgumentNullException("array"); - if (offset < 0) - throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (numBytes < 0) - throw new ArgumentOutOfRangeException("numBytes", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (array.Length - offset < numBytes) - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); - Contract.EndContractBlock(); - - if (_handle.IsClosed) __Error.FileNotOpen(); - - if (!_isAsync) - return base.BeginWrite(array, offset, numBytes, userCallback, stateObject); - else - return BeginWriteAsync(array, offset, numBytes, userCallback, stateObject); - } - - [System.Security.SecuritySafeCritical] // auto-generated - [HostProtection(ExternalThreading = true)] - private FileStreamAsyncResult BeginWriteAsync(byte[] array, int offset, int numBytes, AsyncCallback userCallback, Object stateObject) - { - Contract.Assert(_isAsync); - - if (!CanWrite) __Error.WriteNotSupported(); - - Contract.Assert((_readPos == 0 && _readLen == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLen), "We're either reading or writing, but not both."); - - if (_isPipe) - { - // When redirecting stdout & stderr with the Process class, it's easy to deadlock your - // parent & child processes when doing writes 4K at a time. The - // OS appears to use a 4K buffer internally. If you write to a - // pipe that is full, you will block until someone read from - // that pipe. If you try reading from an empty pipe and - // FileStream's BeginRead blocks waiting for data to fill it's - // internal buffer, you will be blocked. In a case where a child - // process writes to stdout & stderr while a parent process tries - // reading from both, you can easily get into a deadlock here. - // To avoid this deadlock, don't buffer when doing async IO on - // pipes. - Contract.Assert(_readPos == 0 && _readLen == 0, "FileStream must not have buffered data here! Pipes should be unidirectional."); - - if (_writePos > 0) - FlushWrite(false); - - return BeginWriteCore(array, offset, numBytes, userCallback, stateObject); - } - - // Handle buffering. - if (_writePos == 0) - { - if (_readPos < _readLen) FlushRead(); - _readPos = 0; - _readLen = 0; - } - - int n = _bufferSize - _writePos; - if (numBytes <= n) - { - if (_writePos == 0) _buffer = new byte[_bufferSize]; - Buffer.InternalBlockCopy(array, offset, _buffer, _writePos, numBytes); - _writePos += numBytes; - - // Return a synchronous FileStreamAsyncResult - return FileStreamAsyncResult.CreateBufferedReadResult(numBytes, userCallback, stateObject, true); - } - - if (_writePos > 0) - FlushWrite(false); - - return BeginWriteCore(array, offset, numBytes, userCallback, stateObject); - } - - [System.Security.SecuritySafeCritical] // auto-generated - unsafe private FileStreamAsyncResult BeginWriteCore(byte[] bytes, int offset, int numBytes, AsyncCallback userCallback, Object stateObject) - { - Contract.Assert(!_handle.IsClosed, "!_handle.IsClosed"); - Contract.Assert(CanWrite, "CanWrite"); - Contract.Assert(bytes != null, "bytes != null"); - Contract.Assert(_readPos == _readLen, "_readPos == _readLen"); - Contract.Assert(_isAsync, "BeginWriteCore doesn't work on synchronous file streams!"); - Contract.Assert(offset >= 0, "offset is negative"); - Contract.Assert(numBytes >= 0, "numBytes is negative"); - - // Create and store async stream class library specific data in the async result - FileStreamAsyncResult asyncResult = new FileStreamAsyncResult(0, bytes, _handle, userCallback, stateObject, true); - NativeOverlapped* intOverlapped = asyncResult.OverLapped; - - if (CanSeek) { - // Make sure we set the length of the file appropriately. - long len = Length; - //Console.WriteLine("BeginWrite - Calculating end pos. pos: "+pos+" len: "+len+" numBytes: "+numBytes); - - // Make sure we are writing to the position that we think we are - if (_exposedHandle) - VerifyOSHandlePosition(); - - if (_pos + numBytes > len) { - //Console.WriteLine("BeginWrite - Setting length to: "+(pos + numBytes)); - SetLengthCore(_pos + numBytes); - } - - // Now set the position to read from in the NativeOverlapped struct - // For pipes, we should leave the offset fields set to 0. - intOverlapped->OffsetLow = (int)_pos; - intOverlapped->OffsetHigh = (int)(_pos>>32); - - // When using overlapped IO, the OS is not supposed to - // touch the file pointer location at all. We will adjust it - // ourselves. This isn't threadsafe. - - SeekCore(numBytes, SeekOrigin.Current); - } - - //Console.WriteLine("BeginWrite finishing. pos: "+pos+" numBytes: "+numBytes+" _pos: "+_pos+" Position: "+Position); - - if (FrameworkEventSource.IsInitialized && FrameworkEventSource.Log.IsEnabled(EventLevel.Informational, FrameworkEventSource.Keywords.ThreadTransfer)) - FrameworkEventSource.Log.ThreadTransferSend((long)(asyncResult.OverLapped), 2, string.Empty, false); - - int hr = 0; - // queue an async WriteFile operation and pass in a packed overlapped - int r = WriteFileNative(_handle, bytes, offset, numBytes, intOverlapped, out hr); - - // WriteFile, the OS version, will return 0 on failure. But - // my WriteFileNative wrapper returns -1. My wrapper will return - // the following: - // On error, r==-1. - // On async requests that are still pending, r==-1 w/ hr==ERROR_IO_PENDING - // On async requests that completed sequentially, r==0 - // You will NEVER RELIABLY be able to get the number of bytes - // written back from this call when using overlapped IO! You must - // not pass in a non-null lpNumBytesWritten to WriteFile when using - // overlapped structures! This is ByDesign NT behavior. - if (r==-1 && numBytes!=-1) { - //Console.WriteLine("WriteFile returned 0; Write will complete asynchronously (if hr==3e5) hr: 0x{0:x}", hr); - - // For pipes, when they are closed on the other side, they will come here. - if (hr == ERROR_NO_DATA) { - // Not an error, but EOF. AsyncFSCallback will NOT be - // called. Call the user callback here. - asyncResult.CallUserCallback(); - // EndWrite will free the Overlapped struct correctly. - } - else if (hr != ERROR_IO_PENDING) { - if (!_handle.IsClosed && CanSeek) // Update Position - It could be anywhere. - SeekCore(0, SeekOrigin.Current); - - if (hr == ERROR_HANDLE_EOF) - __Error.EndOfFile(); - else - __Error.WinIOError(hr, String.Empty); - } - } - else { - // Due to a workaround for a race condition in NT's ReadFile & - // WriteFile routines, we will always be returning 0 from WriteFileNative - // when we do async IO instead of the number of bytes written, - // irregardless of whether the operation completed - // synchronously or asynchronously. We absolutely must not - // set asyncResult._numBytes here, since will never have correct - // results. - //Console.WriteLine("WriteFile returned: "+r+" (0x"+Int32.Format(r, "x")+") The IO completed synchronously, but the user callback was called on another thread."); - } - - return asyncResult; - } - - [System.Security.SecuritySafeCritical] // Although the unsafe code is only required in PAL, the block is wide scoped. Leave it here for desktop to ensure it's reviewed. - public unsafe override void EndWrite(IAsyncResult asyncResult) - { - if (asyncResult==null) - throw new ArgumentNullException("asyncResult"); - Contract.EndContractBlock(); - - if (!_isAsync) { - base.EndWrite(asyncResult); - return; - } - - FileStreamAsyncResult afsar = asyncResult as FileStreamAsyncResult; - if (afsar==null || !afsar.IsWrite) - __Error.WrongAsyncResult(); - - // Ensure we can't have any race conditions by doing an interlocked - // CompareExchange here. Avoids corrupting memory via freeing the - // NativeOverlapped class or GCHandle twice. -- - if (1 == Interlocked.CompareExchange(ref afsar._EndXxxCalled, 1, 0)) - __Error.EndWriteCalledTwice(); - - // Obtain the WaitHandle, but don't use public property in case we - // delay initialize the manual reset event in the future. - afsar.Wait(); - - // Free memory & GC handles. - afsar.ReleaseNativeResource(); - - // Now check for any error during the write. - if (afsar.ErrorCode != 0) - __Error.WinIOError(afsar.ErrorCode, String.Empty); - - // Number of bytes written is afsar._numBytes + afsar._numBufferedBytes. - return; - } - - [System.Security.SecuritySafeCritical] // auto-generated - public override void WriteByte(byte value) - { - if (_handle.IsClosed) __Error.FileNotOpen(); - if (_writePos==0) { - if (!CanWrite) __Error.WriteNotSupported(); - if (_readPos < _readLen) FlushRead(); - _readPos = 0; - _readLen = 0; - Contract.Assert(_bufferSize > 0, "_bufferSize > 0"); - if (_buffer==null) _buffer = new byte[_bufferSize]; - } - if (_writePos == _bufferSize) - FlushWrite(false); - - _buffer[_writePos] = value; - _writePos++; - } - - [System.Security.SecuritySafeCritical] // auto-generated - public virtual void Lock(long position, long length) { - if (position < 0 || length < 0) - throw new ArgumentOutOfRangeException((position < 0 ? "position" : "length"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - Contract.EndContractBlock(); - if (_handle.IsClosed) __Error.FileNotOpen(); - - int positionLow = unchecked((int)(position )); - int positionHigh = unchecked((int)(position >> 32)); - int lengthLow = unchecked((int)(length )); - int lengthHigh = unchecked((int)(length >> 32)); - - if (!Win32Native.LockFile(_handle, positionLow, positionHigh, lengthLow, lengthHigh)) - __Error.WinIOError(); - } - - [System.Security.SecuritySafeCritical] // auto-generated - public virtual void Unlock(long position, long length) { - if (position < 0 || length < 0) - throw new ArgumentOutOfRangeException((position < 0 ? "position" : "length"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - Contract.EndContractBlock(); - if (_handle.IsClosed) __Error.FileNotOpen(); - - int positionLow = unchecked((int)(position )); - int positionHigh = unchecked((int)(position >> 32)); - int lengthLow = unchecked((int)(length )); - int lengthHigh = unchecked((int)(length >> 32)); - - if (!Win32Native.UnlockFile(_handle, positionLow, positionHigh, lengthLow, lengthHigh)) - __Error.WinIOError(); - } - - // Windows API definitions, from winbase.h and others - - private const int FILE_ATTRIBUTE_NORMAL = 0x00000080; - private const int FILE_ATTRIBUTE_ENCRYPTED = 0x00004000; - private const int FILE_FLAG_OVERLAPPED = 0x40000000; - internal const int GENERIC_READ = unchecked((int)0x80000000); - private const int GENERIC_WRITE = 0x40000000; - - private const int FILE_BEGIN = 0; - private const int FILE_CURRENT = 1; - private const int FILE_END = 2; - - // Error codes (not HRESULTS), from winerror.h - internal const int ERROR_BROKEN_PIPE = 109; - internal const int ERROR_NO_DATA = 232; - private const int ERROR_HANDLE_EOF = 38; - private const int ERROR_INVALID_PARAMETER = 87; - private const int ERROR_IO_PENDING = 997; - - - // __ConsoleStream also uses this code. - [System.Security.SecurityCritical] // auto-generated - private unsafe int ReadFileNative(SafeFileHandle handle, byte[] bytes, int offset, int count, NativeOverlapped* overlapped, out int hr) - { - Contract.Requires(handle != null, "handle != null"); - Contract.Requires(offset >= 0, "offset >= 0"); - Contract.Requires(count >= 0, "count >= 0"); - Contract.Requires(bytes != null, "bytes != null"); - // Don't corrupt memory when multiple threads are erroneously writing - // to this stream simultaneously. - if (bytes.Length - offset < count) - throw new IndexOutOfRangeException(Environment.GetResourceString("IndexOutOfRange_IORaceCondition")); - Contract.EndContractBlock(); - - Contract.Assert((_isAsync && overlapped != null) || (!_isAsync && overlapped == null), "Async IO parameter mismatch in call to ReadFileNative."); - - // You can't use the fixed statement on an array of length 0. - if (bytes.Length==0) { - hr = 0; - return 0; - } - - int r = 0; - int numBytesRead = 0; - - fixed(byte* p = bytes) { - if (_isAsync) - r = Win32Native.ReadFile(handle, p + offset, count, IntPtr.Zero, overlapped); - else - r = Win32Native.ReadFile(handle, p + offset, count, out numBytesRead, IntPtr.Zero); - } - - if (r==0) { - hr = Marshal.GetLastWin32Error(); - // We should never silently drop an error here without some - // extra work. We must make sure that BeginReadCore won't return an - // IAsyncResult that will cause EndRead to block, since the OS won't - // call AsyncFSCallback for us. - if (hr == ERROR_BROKEN_PIPE || hr == Win32Native.ERROR_PIPE_NOT_CONNECTED) { - // This handle was a pipe, and it's done. Not an error, but EOF. - // However, the OS will not call AsyncFSCallback! - // Let the caller handle this, since BeginReadCore & ReadCore - // need to do different things. - return -1; - } - - // See code:#errorInvalidHandle in "private long SeekCore(long offset, SeekOrigin origin)". - if (hr == Win32Native.ERROR_INVALID_HANDLE) - _handle.Dispose(); - - return -1; - } - else - hr = 0; - return numBytesRead; - } - - [System.Security.SecurityCritical] // auto-generated - private unsafe int WriteFileNative(SafeFileHandle handle, byte[] bytes, int offset, int count, NativeOverlapped* overlapped, out int hr) { - Contract.Requires(handle != null, "handle != null"); - Contract.Requires(offset >= 0, "offset >= 0"); - Contract.Requires(count >= 0, "count >= 0"); - Contract.Requires(bytes != null, "bytes != null"); - // Don't corrupt memory when multiple threads are erroneously writing - // to this stream simultaneously. (the OS is reading from - // the array we pass to WriteFile, but if we read beyond the end and - // that memory isn't allocated, we could get an AV.) - if (bytes.Length - offset < count) - throw new IndexOutOfRangeException(Environment.GetResourceString("IndexOutOfRange_IORaceCondition")); - Contract.EndContractBlock(); - - Contract.Assert((_isAsync && overlapped != null) || (!_isAsync && overlapped == null), "Async IO parameter missmatch in call to WriteFileNative."); - - // You can't use the fixed statement on an array of length 0. - if (bytes.Length==0) { - hr = 0; - return 0; - } - - int numBytesWritten = 0; - int r = 0; - - fixed(byte* p = bytes) { - if (_isAsync) - r = Win32Native.WriteFile(handle, p + offset, count, IntPtr.Zero, overlapped); - else - r = Win32Native.WriteFile(handle, p + offset, count, out numBytesWritten, IntPtr.Zero); - } - - if (r==0) { - hr = Marshal.GetLastWin32Error(); - // We should never silently drop an error here without some - // extra work. We must make sure that BeginWriteCore won't return an - // IAsyncResult that will cause EndWrite to block, since the OS won't - // call AsyncFSCallback for us. - - if (hr==ERROR_NO_DATA) { - // This handle was a pipe, and the pipe is being closed on the - // other side. Let the caller handle this, since BeginWriteCore - // & WriteCore need to do different things. - return -1; - } - - // See code:#errorInvalidHandle in "private long SeekCore(long offset, SeekOrigin origin)". - if (hr == Win32Native.ERROR_INVALID_HANDLE) - _handle.Dispose(); - - return -1; - } - else - hr = 0; - return numBytesWritten; - } - - - [HostProtection(ExternalThreading = true)] - [ComVisible(false)] - [SecuritySafeCritical] - public override Task<int> ReadAsync(Byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - if (buffer == null) - throw new ArgumentNullException("buffer"); - if (offset < 0) - throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (count < 0) - throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (buffer.Length - offset < count) - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); - Contract.EndContractBlock(); - - // If we have been inherited into a subclass, the following implementation could be incorrect - // since it does not call through to Read() or BeginRead() which a subclass might have overriden. - // To be safe we will only use this implementation in cases where we know it is safe to do so, - // and delegate to our base class (which will call into Read/BeginRead) when we are not sure. - if (this.GetType() != typeof(FileStream)) - return base.ReadAsync(buffer, offset, count, cancellationToken); - - if (cancellationToken.IsCancellationRequested) - return Task.FromCanceled<int>(cancellationToken); - - if (_handle.IsClosed) - __Error.FileNotOpen(); - - // If async IO is not supported on this platform or - // if this FileStream was not opened with FileOptions.Asynchronous. - if (!_isAsync) - return base.ReadAsync(buffer, offset, count, cancellationToken); - - var readTask = new FileStreamReadWriteTask<int>(cancellationToken); - var endReadTask = s_endReadTask; - if (endReadTask == null) s_endReadTask = endReadTask = EndReadTask; // benign initialization race condition - readTask._asyncResult = BeginReadAsync(buffer, offset, count, endReadTask, readTask); - - if (readTask._asyncResult.IsAsync && cancellationToken.CanBeCanceled) - { - var cancelReadHandler = s_cancelReadHandler; - if (cancelReadHandler == null) s_cancelReadHandler = cancelReadHandler = CancelTask<int>; // benign initialization race condition - readTask._registration = cancellationToken.Register(cancelReadHandler, readTask); - - // In case the task is completed right before we register the cancellation callback. - if (readTask._asyncResult.IsCompleted) - readTask._registration.Dispose(); - } - - return readTask; - } - - [HostProtection(ExternalThreading = true)] - [ComVisible(false)] - [SecuritySafeCritical] - public override Task WriteAsync(Byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - if (buffer == null) - throw new ArgumentNullException("buffer"); - if (offset < 0) - throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (count < 0) - throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (buffer.Length - offset < count) - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); - Contract.EndContractBlock(); - - // If we have been inherited into a subclass, the following implementation could be incorrect - // since it does not call through to Write() or BeginWrite() which a subclass might have overriden. - // To be safe we will only use this implementation in cases where we know it is safe to do so, - // and delegate to our base class (which will call into Write/BeginWrite) when we are not sure. - if (this.GetType() != typeof(FileStream)) - return base.WriteAsync(buffer, offset, count, cancellationToken); - - if (cancellationToken.IsCancellationRequested) - return Task.FromCanceled(cancellationToken); - - if (_handle.IsClosed) - __Error.FileNotOpen(); - - // If async IO is not supported on this platform or - // if this FileStream was not opened with FileOptions.Asynchronous. - if (!_isAsync) - return base.WriteAsync(buffer, offset, count, cancellationToken); - - var writeTask = new FileStreamReadWriteTask<VoidTaskResult>(cancellationToken); - var endWriteTask = s_endWriteTask; - if (endWriteTask == null) s_endWriteTask = endWriteTask = EndWriteTask; // benign initialization race condition - writeTask._asyncResult = BeginWriteAsync(buffer, offset, count, endWriteTask, writeTask); - - if (writeTask._asyncResult.IsAsync && cancellationToken.CanBeCanceled) - { - var cancelWriteHandler = s_cancelWriteHandler; - if (cancelWriteHandler == null) s_cancelWriteHandler = cancelWriteHandler = CancelTask<VoidTaskResult>; // benign initialization race condition - writeTask._registration = cancellationToken.Register(cancelWriteHandler, writeTask); - - // In case the task is completed right before we register the cancellation callback. - if (writeTask._asyncResult.IsCompleted) - writeTask._registration.Dispose(); - } - - return writeTask; - } - - // The task instance returned from ReadAsync and WriteAsync. - // Also stores all of the state necessary for those calls to avoid closures and extraneous delegate allocations. - private sealed class FileStreamReadWriteTask<T> : Task<T> - { - internal CancellationToken _cancellationToken; - internal CancellationTokenRegistration _registration; - internal FileStreamAsyncResult _asyncResult; // initialized after Begin call completes - - internal FileStreamReadWriteTask(CancellationToken cancellationToken) : base() - { - _cancellationToken = cancellationToken; - } - } - - // Cancellation callback for both ReadAsync and WriteAsync. - [SecuritySafeCritical] - private static void CancelTask<T>(object state) - { - var task = state as FileStreamReadWriteTask<T>; - Contract.Assert(task != null); - FileStreamAsyncResult asyncResult = task._asyncResult; - - // This method is used as both the completion callback and the cancellation callback. - // We should try to cancel the operation if this is running as the completion callback - // or if cancellation is not applicable: - // 1. asyncResult is not a FileStreamAsyncResult - // 2. asyncResult.IsAsync is false: asyncResult is a "synchronous" FileStreamAsyncResult. - // 3. The asyncResult is completed: this should never happen. - Contract.Assert((!asyncResult.IsWrite && typeof(T) == typeof(int)) || - (asyncResult.IsWrite && typeof(T) == typeof(VoidTaskResult))); - Contract.Assert(asyncResult != null); - Contract.Assert(asyncResult.IsAsync); - - try - { - // Cancel the overlapped read and set the task to cancelled state. - if (!asyncResult.IsCompleted) - asyncResult.Cancel(); - } - catch (Exception ex) - { - task.TrySetException(ex); - } - } - - // Completion callback for ReadAsync - [SecuritySafeCritical] - private static void EndReadTask(IAsyncResult iar) - { - FileStreamAsyncResult asyncResult = iar as FileStreamAsyncResult; - Contract.Assert(asyncResult != null); - Contract.Assert(asyncResult.IsCompleted, "How can we end up in the completion callback if the IAsyncResult is not completed?"); - - var readTask = asyncResult.AsyncState as FileStreamReadWriteTask<int>; - Contract.Assert(readTask != null); - - try - { - if (asyncResult.IsAsync) - { - asyncResult.ReleaseNativeResource(); - - // release the resource held by CancellationTokenRegistration - readTask._registration.Dispose(); - } - - if (asyncResult.ErrorCode == Win32Native.ERROR_OPERATION_ABORTED) - { - var cancellationToken = readTask._cancellationToken; - Contract.Assert(cancellationToken.IsCancellationRequested, "How can the IO operation be aborted if cancellation was not requested?"); - readTask.TrySetCanceled(cancellationToken); - } - else - readTask.TrySetResult(asyncResult.NumBytesRead); - } - catch (Exception ex) - { - readTask.TrySetException(ex); - } - } - - // Completion callback for WriteAsync - [SecuritySafeCritical] - private static void EndWriteTask(IAsyncResult iar) - { - var asyncResult = iar as FileStreamAsyncResult; - Contract.Assert(asyncResult != null); - Contract.Assert(asyncResult.IsCompleted, "How can we end up in the completion callback if the IAsyncResult is not completed?"); - - var writeTask = iar.AsyncState as FileStreamReadWriteTask<VoidTaskResult>; - Contract.Assert(writeTask != null); - - try - { - if (asyncResult.IsAsync) - { - asyncResult.ReleaseNativeResource(); - - // release the resource held by CancellationTokenRegistration - writeTask._registration.Dispose(); - } - - if (asyncResult.ErrorCode == Win32Native.ERROR_OPERATION_ABORTED) - { - var cancellationToken = writeTask._cancellationToken; - Contract.Assert(cancellationToken.IsCancellationRequested, "How can the IO operation be aborted if cancellation was not requested?"); - writeTask.TrySetCanceled(cancellationToken); - } - else - writeTask.TrySetResult(default(VoidTaskResult)); - } - catch (Exception ex) - { - writeTask.TrySetException(ex); - } - } - - // Unlike Flush(), FlushAsync() always flushes to disk. This is intentional. - // Legend is that we chose not to flush the OS file buffers in Flush() in fear of - // perf problems with frequent, long running FlushFileBuffers() calls. But we don't - // have that problem with FlushAsync() because we will call FlushFileBuffers() in the background. - [HostProtection(ExternalThreading = true)] - [ComVisible(false)] - [System.Security.SecuritySafeCritical] - public override Task FlushAsync(CancellationToken cancellationToken) - { - // If we have been inherited into a subclass, the following implementation could be incorrect - // since it does not call through to Flush() which a subclass might have overriden. To be safe - // we will only use this implementation in cases where we know it is safe to do so, - // and delegate to our base class (which will call into Flush) when we are not sure. - if (this.GetType() != typeof(FileStream)) - return base.FlushAsync(cancellationToken); - - if (cancellationToken.IsCancellationRequested) - return Task.FromCanceled(cancellationToken); - - if (_handle.IsClosed) - __Error.FileNotOpen(); - - // The always synchronous data transfer between the OS and the internal buffer is intentional - // because this is needed to allow concurrent async IO requests. Concurrent data transfer - // between the OS and the internal buffer will result in race conditions. Since FlushWrite and - // FlushRead modify internal state of the stream and transfer data between the OS and the - // internal buffer, they cannot be truly async. We will, however, flush the OS file buffers - // asynchronously because it doesn't modify any internal state of the stream and is potentially - // a long running process. - try - { - FlushInternalBuffer(); - } - catch (Exception e) - { - return Task.FromException(e); - } - - if (CanWrite) - return Task.Factory.StartNew( - state => ((FileStream)state).FlushOSBuffer(), - this, - cancellationToken, - TaskCreationOptions.DenyChildAttach, - TaskScheduler.Default); - else - return Task.CompletedTask; - } - - } -} |