summaryrefslogtreecommitdiff
path: root/src/mscorlib/src/System/IO/FileStream.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/mscorlib/src/System/IO/FileStream.cs')
-rw-r--r--src/mscorlib/src/System/IO/FileStream.cs2695
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;
- }
-
- }
-}