Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / clr / src / BCL / System / IO / FileStream.cs / 1305376 / FileStream.cs
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
/*============================================================
**
** Class: FileStream
**
** [....]
**
**
** 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_PAL && FEATURE_MACL
using System.Security.AccessControl;
#endif
using System.Security.Permissions;
using System.Threading;
using System.Runtime.InteropServices;
using System.Runtime.Remoting.Messaging;
using System.Runtime.CompilerServices;
using System.Globalization;
using System.Runtime.Versioning;
using System.Diagnostics.Contracts;
/*
* FileStream supports different modes of accessing the disk - async mode
* and [....] mode. They are two completely different codepaths in the
* [....] & async methods (ie, Read/Write vs. BeginRead/BeginWrite). File
* handles in NT can be opened in only [....] or overlapped (async) mode,
* and we have to deal with this pain. Stream has implementations of
* the [....] 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.
#if IO_CANCELLATION_ENABLED
unsafe internal sealed class FileStreamAsyncResult : ICancelableAsyncResult, System.Object
#if false // ugly hack to fix syntax for TrimSrc parser, which ignores #if directives
{
}
#endif
#else
unsafe internal sealed class FileStreamAsyncResult : IAsyncResult
#endif
{
// 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
internal AsyncCallback _userCallback;
internal Object _userStateObject;
internal ManualResetEvent _waitHandle;
[System.Security.SecurityCritical /*auto-generated*/]
internal SafeFileHandle _handle; // For cancellation support.
#if !FEATURE_PAL
internal NativeOverlapped* _overlapped;
#endif
internal int _EndXxxCalled; // Whether we've called EndXxx already.
internal int _numBytes; // number of bytes read OR written
internal int _errorCode;
internal int _numBufferedBytes;
internal bool _isWrite; // Whether this is a read or a write
internal bool _isComplete; // Value for IsCompleted property
internal bool _completedSynchronously; // Which thread called callback
// Adding in a finalizer here to catch the places
// where users didn't call EndRead or EndWrite on an IAsyncResult,
// willn't work. The unmanaged memory in the NativeOverlapped
// struct keeps a GCHandle to this IAsyncResult object alive, so this
// never get finalized.
public Object AsyncState
{
get { return _userStateObject; }
}
public bool IsCompleted
{
get { return _isComplete; }
}
public WaitHandle AsyncWaitHandle
{
#if !FEATURE_PAL
[System.Security.SecuritySafeCritical] // auto-generated
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
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(ref _waitHandle, mre, null) == null) {
if (_isComplete)
_waitHandle.Set();
}
else {
// There's a slight but acceptable ---- 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;
}
#else
get { return null; }
#endif //!FEATURE_PAL && FEATURE_MACL
}
// 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; }
}
internal static FileStreamAsyncResult CreateBufferedReadResult(int numBufferedBytes, AsyncCallback userCallback, Object userStateObject) {
FileStreamAsyncResult asyncResult = new FileStreamAsyncResult();
asyncResult._userCallback = userCallback;
asyncResult._userStateObject = userStateObject;
asyncResult._isWrite = false;
asyncResult._numBufferedBytes = numBufferedBytes;
return asyncResult;
}
private void CallUserCallbackWorker(Object callbackState)
{
_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(new WaitCallback(CallUserCallbackWorker));
}
else {
_isComplete = true;
// ensure _isComplete is set before reading _waitHandle
Thread.MemoryBarrier();
if (_waitHandle != null)
_waitHandle.Set();
}
}
#if IO_CANCELLATION_ENABLED
[HostProtection(ExternalThreading=true)]
public void Cancel()
{
#if !FEATURE_PAL
if (!Environment.IsWindowsVista)
throw new PlatformNotSupportedException(Environment.GetResourceString("PlatformNotSupported_RequiresLonghorn"));
if (_handle == null || _overlapped == null)
throw new NotSupportedException(Environment.GetResourceString("NotSupported_IOCancellation"));
if (_handle.IsInvalid)
__Error.StreamIsClosed();
Contract.EndContractBlock();
bool r = Win32Native.CancelIoEx(_handle, _overlapped);
if (!r)
__Error.WinIOError();
#else
throw new PlatformNotSupportedException(Environment.GetResourceString("PlatformNotSupported_RequiresLonghorn"));
#endif
}
#endif
}
[ComVisible(true)]
public class FileStream : Stream
{
internal const int DefaultBufferSize = 4096;
#if !FEATURE_PAL
#if FEATURE_CORECLR
// @todo: This #ifdef factoring fixes the public api async behavior for M6 with minimal risk. When more
// time is available, we should more rigorously scrub out all references for FileStreamAsyncResult and AsyncFSCallback
// (this requires corresponding changes in the src\vm tree as well.)
private static readonly bool _canUseAsync = false;
#else
private static readonly bool _canUseAsync = Environment.RunningOnWinNT;
#endif //FEATURE_CORECLR
[System.Security.SecurityCritical /*auto-generated*/]
private unsafe static readonly IOCompletionCallback IOCallback = new IOCompletionCallback(FileStream.AsyncFSCallback);
#else
private static readonly bool _canUseAsync = false;
#endif //!FEATURE_PAL
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.
[System.Security.SecuritySafeCritical] // static constructors should be safe to call
static FileStream()
{
}
//This exists only to support IsolatedStorageFileStream.
//Any changes to FileStream must include the corresponding changes in IsolatedStorage.
internal FileStream() {
}
[System.Security.SecuritySafeCritical] // auto-generated
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
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
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
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
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
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
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
public FileStream(String path, FileMode mode, FileAccess access, FileShare share, int bufferSize)
: this(path, mode, access, share, bufferSize, FileOptions.None, Path.GetFileName(path), false)
{
}
[System.Security.SecuritySafeCritical] // auto-generated
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
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)
{
}
[System.Security.SecuritySafeCritical] // auto-generated
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
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_PAL && 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
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
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);
}
finally {
if (pinningHandle != null) {
GCHandle pinHandle = (GCHandle) pinningHandle;
pinHandle.Free();
}
}
}
[System.Security.SecuritySafeCritical] // auto-generated
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
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);
}
#endif
[System.Security.SecurityCritical] // auto-generated
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
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);
}
[System.Security.SecurityCritical]
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
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);
}
// AccessControl namespace is not defined in Rotor
[System.Security.SecuritySafeCritical] // auto-generated
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
internal 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)
{
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_PAL && 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;
#if FEATURE_PAL
Contract.Assert(!useRights, "Specifying FileSystemRights is not supported on this platform!");
#endif
// 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_PAL && 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_PAL && FEATURE_MACL
if ((!useRights && (access & FileAccess.Write) == 0)
|| (useRights && (fileSystemRights & FileSystemRights.Write) == 0))
#else
if (!useRights && (access & FileAccess.Write) == 0)
#endif //!FEATURE_PAL && 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_PAL && FEATURE_MACL
else
throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFileMode&RightsCombo", mode, fileSystemRights));
#endif //!FEATURE_PAL && FEATURE_MACL
}
}
#if !FEATURE_PAL && 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"));
// 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 !FEATURE_PAL && FEATURE_MACL
if ((!useRights && (access & FileAccess.Read) != 0) || (useRights && (fileSystemRights & FileSystemRights.ReadAndExecute) != 0))
#else
if (!useRights && (access & FileAccess.Read) != 0)
#endif //!FEATURE_PAL && FEATURE_MACL
{
if (mode==FileMode.Append)
throw new ArgumentException(Environment.GetResourceString("Argument_InvalidAppendMode"));
else
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_PAL && 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_PAL && FEATURE_MACL
{
if (mode==FileMode.Append)
secAccess = secAccess | FileIOPermissionAccess.Append;
else
secAccess = secAccess | FileIOPermissionAccess.Write;
}
#if !FEATURE_PAL && 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
new FileIOPermission(secAccess, new String[] { filePath }, false, false).Demand();
#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 (_canUseAsync && (options & FileOptions.Asynchronous) != 0)
_isAsync = true;
else
options &= ~FileOptions.Asynchronous;
int flagsAndAttributes = (int) options;
#if !FEATURE_PAL
// 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, Win32Native.NULL);
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 {
new FileIOPermission(FileIOPermissionAccess.PathDiscovery, new String[] { _fileName }, false, false ).Demand();
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"));
}
#if !FEATURE_PAL
#if !FEATURE_CORECLR
// 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
new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
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"));
}
#endif // FEATURE_CORECLR
#endif //!FEATURE_PAL
if (!useRights) {
_canRead = (access & FileAccess.Read) != 0;
_canWrite = (access & FileAccess.Write) != 0;
}
#if !FEATURE_PAL && FEATURE_MACL
else {
_canRead = (fileSystemRights & FileSystemRights.ReadData) != 0;
_canWrite = ((fileSystemRights & FileSystemRights.WriteData) != 0)
|| ((fileSystemRights & FileSystemRights.AppendData) != 0);
}
#endif //!FEATURE_PAL && 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;
}
}
[System.Security.SecuritySafeCritical] // auto-generated
[Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access) instead. http://go.microsoft.com/fwlink/?linkid=14202")]
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
public FileStream(IntPtr handle, FileAccess access)
: this(handle, access, true, DefaultBufferSize, false) {
}
[System.Security.SecuritySafeCritical] // auto-generated
[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")]
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
public FileStream(IntPtr handle, FileAccess access, bool ownsHandle)
: this(handle, access, ownsHandle, DefaultBufferSize, false) {
}
[System.Security.SecuritySafeCritical] // auto-generated
[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")]
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
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")]
[SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.UnmanagedCode)]
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
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
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
public FileStream(SafeFileHandle handle, FileAccess access)
: this(handle, access, DefaultBufferSize, false) {
}
[System.Security.SecuritySafeCritical] // auto-generated
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize)
: this(handle, access, bufferSize, false) {
}
[System.Security.SecuritySafeCritical] // auto-generated
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
[SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.UnmanagedCode)]
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 && _canUseAsync; // On Win9x, just do the right thing.
_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;
#if !FEATURE_PAL
// 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
#else
if (handleType != Win32Native.FILE_TYPE_PIPE)
VerifyHandleIsSync();
#endif //!FEATURE_PAL
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_PAL && 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, "");
}
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] // auto-generated
get {
if (_fileName == null)
return Environment.GetResourceString("IO_UnknownFileName");
new FileIOPermission(FileIOPermissionAccess.PathDiscovery, new String[] { _fileName }, false, false ).Demand();
return _fileName;
}
}
internal String NameInternal {
get {
if (_fileName == null)
return "";
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 [....] 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);
}
[System.Security.SecuritySafeCritical] // auto-generated
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_PAL && FEATURE_MACL
[System.Security.SecuritySafeCritical] // auto-generated
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
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
#if !FEATURE_PAL
// 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
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
unsafe private static void AsyncFSCallback(uint errorCode, uint numBytes, NativeOverlapped* pOverlapped)
{
//Console.WriteLine("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;
// 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 == ERROR_BROKEN_PIPE || errorCode == ERROR_NO_DATA)
errorCode = 0;
asyncResult._errorCode = (int)errorCode;
//Console.WriteLine("AsyncFSCallback: errorCode: "+errorCode+" numBytes: "+numBytes+" was synchronous: "+asyncResult.CompletedSynchronously);
// 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);
}
#endif //!FEATURE_PAL
[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);
}
}
[System.Security.SecuritySafeCritical] // auto-generated
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();
if (_writePos > 0) {
FlushWrite(false);
if (flushToDisk) {
if (!Win32Native.FlushFileBuffers(_handle)) {
__Error.WinIOError();
}
}
}
else if (_readPos < _readLen && CanSeek) {
FlushRead();
}
_readPos = 0;
_readLen = 0;
}
// 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 [....] 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 !FEATURE_PAL
#if !FEATURE_CORECLR
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
#endif // FEATURE_CORECLR
WriteCore(_buffer, 0, _writePos);
_writePos = 0;
#else
WriteCore(_buffer, 0, _writePos);
_writePos = 0;
#endif //!FEATURE_PAL
}
[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
[SecurityPermissionAttribute(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
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
[SecurityPermissionAttribute(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
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 !FEATURE_PAL
#if !FEATURE_CORECLR
if (_isAsync) {
IAsyncResult result = BeginReadCore(buffer, offset, count, null, null, 0);
return EndRead(result);
}
#endif // FEATURE_CORECLR
#endif //!FEATURE_PAL
// 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 (originSeekOrigin.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 [....] 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) {
// For invalid handles, detect the error and mark our handle
// as closed to give slightly better error messages. Also
// help ensure we avoid handle recycling bugs.
if (hr == Win32Native.ERROR_INVALID_HANDLE)
_handle.SetHandleAsInvalid();
__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 !FEATURE_PAL
#if !FEATURE_CORECLR
if (_isAsync) {
IAsyncResult result = BeginWriteCore(_buffer, 0, _writePos, null, null);
EndWrite(result);
}
else {
#endif // FEATURE_CORECLR
WriteCore(_buffer, 0, _writePos);
#if !FEATURE_CORECLR
}
#endif // FEATURE_CORECLR
#else
WriteCore(_buffer, 0, _writePos);
#endif //!FEATURE_PAL
_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 !FEATURE_PAL
#if !FEATURE_CORECLR
if (_isAsync) {
IAsyncResult result = BeginWriteCore(buffer, offset, count, null, null);
EndWrite(result);
return;
}
#endif // FEATURE_CORECLR
#endif //!FEATURE_PAL
// 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 !FEATURE_PAL && FEATURE_ASYNC_IO
if (!_isAsync)
return base.BeginRead(array, offset, numBytes, userCallback, stateObject);
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.");
FileStreamAsyncResult asyncResult = null;
if (_isPipe) {
// Pipes are ----ed up, at least when you have 2 different pipes
// that you want to use simultaneously. 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;
asyncResult = FileStreamAsyncResult.CreateBufferedReadResult(n, userCallback, stateObject);
asyncResult.CallUserCallback();
return asyncResult;
}
Contract.Assert(_writePos == 0, "FileStream must not have buffered write data here! Pipes should be unidirectional.");
return BeginReadCore(array, offset, numBytes, userCallback, stateObject, 0);
}
// 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 [....] 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;
asyncResult = FileStreamAsyncResult.CreateBufferedReadResult(n, userCallback, stateObject);
asyncResult.CallUserCallback();
return asyncResult;
}
// 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 || _isPipe) {
asyncResult = FileStreamAsyncResult.CreateBufferedReadResult(n, userCallback, stateObject);
asyncResult.CallUserCallback();
return asyncResult;
}
// 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;
asyncResult = 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.
}
return asyncResult;
#else
return base.BeginRead(array, offset, numBytes, userCallback, stateObject);
#endif // !FEATURE_PAL && FEATURE_ASYNC_IO
}
#if !FEATURE_PAL
[System.Security.SecuritySafeCritical] // auto-generated
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.AppDomain, ResourceScope.AppDomain)]
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
FileStreamAsyncResult asyncResult = new FileStreamAsyncResult();
asyncResult._handle = _handle;
asyncResult._userCallback = userCallback;
asyncResult._userStateObject = stateObject;
asyncResult._isWrite = false;
// Must set this 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.
asyncResult._numBufferedBytes = numBufferedBytesRead;
// 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);
asyncResult._waitHandle = waitHandle;
// Create a managed overlapped class
// We will set the file offsets later
Overlapped overlapped = new Overlapped(0, 0, IntPtr.Zero, asyncResult);
// Pack the Overlapped class, and store it in the async result
NativeOverlapped* intOverlapped;
if (userCallback != null)
intOverlapped = overlapped.Pack(IOCallback, bytes);
else
intOverlapped = overlapped.UnsafePack(null, bytes);
asyncResult._overlapped = intOverlapped;
// 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);
}
// 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;
}
#endif //!FEATURE_PAL
[System.Security.SecuritySafeCritical] // auto-generated
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 !FEATURE_PAL && FEATURE_ASYNC_IO
if (!_isAsync)
return base.EndRead(asyncResult);
FileStreamAsyncResult afsar = asyncResult as FileStreamAsyncResult;
if (afsar==null || afsar._isWrite)
__Error.WrongAsyncResult();
// Ensure we can't get into any ----s 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.
WaitHandle wh = afsar._waitHandle;
if (wh != 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 {
wh.WaitOne();
Contract.Assert(afsar._isComplete == true, "FileStream::EndRead - AsyncFSCallback didn't set _isComplete to true!");
}
finally {
wh.Close();
}
}
// Free memory & GC handles.
NativeOverlapped* overlappedPtr = afsar._overlapped;
if (overlappedPtr != null)
Overlapped.Free(overlappedPtr);
// Now check for any error during the read.
if (afsar._errorCode != 0)
__Error.WinIOError(afsar._errorCode, String.Empty);
return afsar._numBytes + afsar._numBufferedBytes;
#else
return base.EndRead(asyncResult);
#endif // !FEATURE_PAL && FEATURE_ASYNC_IO
}
// 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 !FEATURE_PAL && FEATURE_ASYNC_IO
if (!_isAsync)
return base.BeginWrite(array, offset, numBytes, userCallback, stateObject);
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) {
// Pipes are ----ed up, at least when you have 2 different pipes
// that you want to use simultaneously. 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.
FileStreamAsyncResult asyncResult;
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;
asyncResult = new FileStreamAsyncResult();
asyncResult._userCallback = userCallback;
asyncResult._userStateObject = stateObject;
asyncResult._waitHandle = null;
asyncResult._isWrite = true;
asyncResult._numBufferedBytes = numBytes;
asyncResult.CallUserCallback();
return asyncResult;
}
if (_writePos > 0) FlushWrite(false);
return BeginWriteCore(array, offset, numBytes, userCallback, stateObject);
#else
return base.BeginWrite(array, offset, numBytes, userCallback, stateObject);
#endif // !FEATURE_PAL && FEATURE_ASYNC_IO
}
#if !FEATURE_PAL
[System.Security.SecuritySafeCritical] // auto-generated
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.AppDomain, ResourceScope.AppDomain)]
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();
asyncResult._handle = _handle;
asyncResult._userCallback = userCallback;
asyncResult._userStateObject = stateObject;
asyncResult._isWrite = true;
// 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);
asyncResult._waitHandle = waitHandle;
// Create a managed overlapped class
// We will set the file offsets later
Overlapped overlapped = new Overlapped(0, 0, IntPtr.Zero, asyncResult);
// Pack the Overlapped class, and store it in the async result
NativeOverlapped* intOverlapped;
if (userCallback != null)
intOverlapped = overlapped.Pack(IOCallback, bytes);
else
intOverlapped = overlapped.UnsafePack(null, bytes);
asyncResult._overlapped = intOverlapped;
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);
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;
}
#endif //!FEATURE_PAL
[System.Security.SecuritySafeCritical] // auto-generated
public unsafe override void EndWrite(IAsyncResult asyncResult)
{
if (asyncResult==null)
throw new ArgumentNullException("asyncResult");
Contract.EndContractBlock();
#if !FEATURE_PAL && FEATURE_ASYNC_IO
if (!_isAsync) {
base.EndWrite(asyncResult);
return;
}
FileStreamAsyncResult afsar = asyncResult as FileStreamAsyncResult;
if (afsar==null || !afsar._isWrite)
__Error.WrongAsyncResult();
// Ensure we can't get into any ----s 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.
WaitHandle wh = afsar._waitHandle;
if (wh != 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 {
wh.WaitOne();
Contract.Assert(afsar._isComplete == true, "FileStream::EndWrite - AsyncFSCallback didn't set _isComplete to true!");
}
finally {
wh.Close();
}
}
// Free memory & GC handles.
NativeOverlapped* overlappedPtr = afsar._overlapped;
if (overlappedPtr != null)
Overlapped.Free(overlappedPtr);
// 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;
#else
base.EndWrite(asyncResult);
#endif // !FEATURE_PAL && FEATURE_ASYNC_IO
}
[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
private const int ERROR_BROKEN_PIPE = 109;
private 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 ----up 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 ---- 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;
}
// For invalid handles, detect the error and mark our handle
// as closed to give slightly better error messages. Also
// help ensure we avoid handle recycling bugs.
if (hr == Win32Native.ERROR_INVALID_HANDLE)
_handle.SetHandleAsInvalid();
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 ----up 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 ---- 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;
}
// For invalid handles, detect the error and mark our handle
// as closed to give slightly better error messages. Also
// help ensure we avoid handle recycling bugs.
if (hr == Win32Native.ERROR_INVALID_HANDLE)
_handle.SetHandleAsInvalid();
return -1;
}
else
hr = 0;
return numBytesWritten;
}
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
/*============================================================
**
** Class: FileStream
**
** [....]
**
**
** 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_PAL && FEATURE_MACL
using System.Security.AccessControl;
#endif
using System.Security.Permissions;
using System.Threading;
using System.Runtime.InteropServices;
using System.Runtime.Remoting.Messaging;
using System.Runtime.CompilerServices;
using System.Globalization;
using System.Runtime.Versioning;
using System.Diagnostics.Contracts;
/*
* FileStream supports different modes of accessing the disk - async mode
* and [....] mode. They are two completely different codepaths in the
* [....] & async methods (ie, Read/Write vs. BeginRead/BeginWrite). File
* handles in NT can be opened in only [....] or overlapped (async) mode,
* and we have to deal with this pain. Stream has implementations of
* the [....] 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.
#if IO_CANCELLATION_ENABLED
unsafe internal sealed class FileStreamAsyncResult : ICancelableAsyncResult, System.Object
#if false // ugly hack to fix syntax for TrimSrc parser, which ignores #if directives
{
}
#endif
#else
unsafe internal sealed class FileStreamAsyncResult : IAsyncResult
#endif
{
// 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
internal AsyncCallback _userCallback;
internal Object _userStateObject;
internal ManualResetEvent _waitHandle;
[System.Security.SecurityCritical /*auto-generated*/]
internal SafeFileHandle _handle; // For cancellation support.
#if !FEATURE_PAL
internal NativeOverlapped* _overlapped;
#endif
internal int _EndXxxCalled; // Whether we've called EndXxx already.
internal int _numBytes; // number of bytes read OR written
internal int _errorCode;
internal int _numBufferedBytes;
internal bool _isWrite; // Whether this is a read or a write
internal bool _isComplete; // Value for IsCompleted property
internal bool _completedSynchronously; // Which thread called callback
// Adding in a finalizer here to catch the places
// where users didn't call EndRead or EndWrite on an IAsyncResult,
// willn't work. The unmanaged memory in the NativeOverlapped
// struct keeps a GCHandle to this IAsyncResult object alive, so this
// never get finalized.
public Object AsyncState
{
get { return _userStateObject; }
}
public bool IsCompleted
{
get { return _isComplete; }
}
public WaitHandle AsyncWaitHandle
{
#if !FEATURE_PAL
[System.Security.SecuritySafeCritical] // auto-generated
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
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(ref _waitHandle, mre, null) == null) {
if (_isComplete)
_waitHandle.Set();
}
else {
// There's a slight but acceptable ---- 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;
}
#else
get { return null; }
#endif //!FEATURE_PAL && FEATURE_MACL
}
// 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; }
}
internal static FileStreamAsyncResult CreateBufferedReadResult(int numBufferedBytes, AsyncCallback userCallback, Object userStateObject) {
FileStreamAsyncResult asyncResult = new FileStreamAsyncResult();
asyncResult._userCallback = userCallback;
asyncResult._userStateObject = userStateObject;
asyncResult._isWrite = false;
asyncResult._numBufferedBytes = numBufferedBytes;
return asyncResult;
}
private void CallUserCallbackWorker(Object callbackState)
{
_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(new WaitCallback(CallUserCallbackWorker));
}
else {
_isComplete = true;
// ensure _isComplete is set before reading _waitHandle
Thread.MemoryBarrier();
if (_waitHandle != null)
_waitHandle.Set();
}
}
#if IO_CANCELLATION_ENABLED
[HostProtection(ExternalThreading=true)]
public void Cancel()
{
#if !FEATURE_PAL
if (!Environment.IsWindowsVista)
throw new PlatformNotSupportedException(Environment.GetResourceString("PlatformNotSupported_RequiresLonghorn"));
if (_handle == null || _overlapped == null)
throw new NotSupportedException(Environment.GetResourceString("NotSupported_IOCancellation"));
if (_handle.IsInvalid)
__Error.StreamIsClosed();
Contract.EndContractBlock();
bool r = Win32Native.CancelIoEx(_handle, _overlapped);
if (!r)
__Error.WinIOError();
#else
throw new PlatformNotSupportedException(Environment.GetResourceString("PlatformNotSupported_RequiresLonghorn"));
#endif
}
#endif
}
[ComVisible(true)]
public class FileStream : Stream
{
internal const int DefaultBufferSize = 4096;
#if !FEATURE_PAL
#if FEATURE_CORECLR
// @todo: This #ifdef factoring fixes the public api async behavior for M6 with minimal risk. When more
// time is available, we should more rigorously scrub out all references for FileStreamAsyncResult and AsyncFSCallback
// (this requires corresponding changes in the src\vm tree as well.)
private static readonly bool _canUseAsync = false;
#else
private static readonly bool _canUseAsync = Environment.RunningOnWinNT;
#endif //FEATURE_CORECLR
[System.Security.SecurityCritical /*auto-generated*/]
private unsafe static readonly IOCompletionCallback IOCallback = new IOCompletionCallback(FileStream.AsyncFSCallback);
#else
private static readonly bool _canUseAsync = false;
#endif //!FEATURE_PAL
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.
[System.Security.SecuritySafeCritical] // static constructors should be safe to call
static FileStream()
{
}
//This exists only to support IsolatedStorageFileStream.
//Any changes to FileStream must include the corresponding changes in IsolatedStorage.
internal FileStream() {
}
[System.Security.SecuritySafeCritical] // auto-generated
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
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
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
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
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
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
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
public FileStream(String path, FileMode mode, FileAccess access, FileShare share, int bufferSize)
: this(path, mode, access, share, bufferSize, FileOptions.None, Path.GetFileName(path), false)
{
}
[System.Security.SecuritySafeCritical] // auto-generated
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
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)
{
}
[System.Security.SecuritySafeCritical] // auto-generated
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
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_PAL && 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
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
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);
}
finally {
if (pinningHandle != null) {
GCHandle pinHandle = (GCHandle) pinningHandle;
pinHandle.Free();
}
}
}
[System.Security.SecuritySafeCritical] // auto-generated
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
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);
}
#endif
[System.Security.SecurityCritical] // auto-generated
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
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);
}
[System.Security.SecurityCritical]
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
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);
}
// AccessControl namespace is not defined in Rotor
[System.Security.SecuritySafeCritical] // auto-generated
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
internal 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)
{
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_PAL && 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;
#if FEATURE_PAL
Contract.Assert(!useRights, "Specifying FileSystemRights is not supported on this platform!");
#endif
// 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_PAL && 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_PAL && FEATURE_MACL
if ((!useRights && (access & FileAccess.Write) == 0)
|| (useRights && (fileSystemRights & FileSystemRights.Write) == 0))
#else
if (!useRights && (access & FileAccess.Write) == 0)
#endif //!FEATURE_PAL && 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_PAL && FEATURE_MACL
else
throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFileMode&RightsCombo", mode, fileSystemRights));
#endif //!FEATURE_PAL && FEATURE_MACL
}
}
#if !FEATURE_PAL && 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"));
// 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 !FEATURE_PAL && FEATURE_MACL
if ((!useRights && (access & FileAccess.Read) != 0) || (useRights && (fileSystemRights & FileSystemRights.ReadAndExecute) != 0))
#else
if (!useRights && (access & FileAccess.Read) != 0)
#endif //!FEATURE_PAL && FEATURE_MACL
{
if (mode==FileMode.Append)
throw new ArgumentException(Environment.GetResourceString("Argument_InvalidAppendMode"));
else
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_PAL && 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_PAL && FEATURE_MACL
{
if (mode==FileMode.Append)
secAccess = secAccess | FileIOPermissionAccess.Append;
else
secAccess = secAccess | FileIOPermissionAccess.Write;
}
#if !FEATURE_PAL && 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
new FileIOPermission(secAccess, new String[] { filePath }, false, false).Demand();
#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 (_canUseAsync && (options & FileOptions.Asynchronous) != 0)
_isAsync = true;
else
options &= ~FileOptions.Asynchronous;
int flagsAndAttributes = (int) options;
#if !FEATURE_PAL
// 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, Win32Native.NULL);
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 {
new FileIOPermission(FileIOPermissionAccess.PathDiscovery, new String[] { _fileName }, false, false ).Demand();
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"));
}
#if !FEATURE_PAL
#if !FEATURE_CORECLR
// 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
new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
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"));
}
#endif // FEATURE_CORECLR
#endif //!FEATURE_PAL
if (!useRights) {
_canRead = (access & FileAccess.Read) != 0;
_canWrite = (access & FileAccess.Write) != 0;
}
#if !FEATURE_PAL && FEATURE_MACL
else {
_canRead = (fileSystemRights & FileSystemRights.ReadData) != 0;
_canWrite = ((fileSystemRights & FileSystemRights.WriteData) != 0)
|| ((fileSystemRights & FileSystemRights.AppendData) != 0);
}
#endif //!FEATURE_PAL && 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;
}
}
[System.Security.SecuritySafeCritical] // auto-generated
[Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access) instead. http://go.microsoft.com/fwlink/?linkid=14202")]
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
public FileStream(IntPtr handle, FileAccess access)
: this(handle, access, true, DefaultBufferSize, false) {
}
[System.Security.SecuritySafeCritical] // auto-generated
[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")]
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
public FileStream(IntPtr handle, FileAccess access, bool ownsHandle)
: this(handle, access, ownsHandle, DefaultBufferSize, false) {
}
[System.Security.SecuritySafeCritical] // auto-generated
[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")]
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
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")]
[SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.UnmanagedCode)]
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
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
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
public FileStream(SafeFileHandle handle, FileAccess access)
: this(handle, access, DefaultBufferSize, false) {
}
[System.Security.SecuritySafeCritical] // auto-generated
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize)
: this(handle, access, bufferSize, false) {
}
[System.Security.SecuritySafeCritical] // auto-generated
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
[SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.UnmanagedCode)]
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 && _canUseAsync; // On Win9x, just do the right thing.
_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;
#if !FEATURE_PAL
// 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
#else
if (handleType != Win32Native.FILE_TYPE_PIPE)
VerifyHandleIsSync();
#endif //!FEATURE_PAL
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_PAL && 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, "");
}
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] // auto-generated
get {
if (_fileName == null)
return Environment.GetResourceString("IO_UnknownFileName");
new FileIOPermission(FileIOPermissionAccess.PathDiscovery, new String[] { _fileName }, false, false ).Demand();
return _fileName;
}
}
internal String NameInternal {
get {
if (_fileName == null)
return "";
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 [....] 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);
}
[System.Security.SecuritySafeCritical] // auto-generated
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_PAL && FEATURE_MACL
[System.Security.SecuritySafeCritical] // auto-generated
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
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
#if !FEATURE_PAL
// 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
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
unsafe private static void AsyncFSCallback(uint errorCode, uint numBytes, NativeOverlapped* pOverlapped)
{
//Console.WriteLine("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;
// 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 == ERROR_BROKEN_PIPE || errorCode == ERROR_NO_DATA)
errorCode = 0;
asyncResult._errorCode = (int)errorCode;
//Console.WriteLine("AsyncFSCallback: errorCode: "+errorCode+" numBytes: "+numBytes+" was synchronous: "+asyncResult.CompletedSynchronously);
// 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);
}
#endif //!FEATURE_PAL
[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);
}
}
[System.Security.SecuritySafeCritical] // auto-generated
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();
if (_writePos > 0) {
FlushWrite(false);
if (flushToDisk) {
if (!Win32Native.FlushFileBuffers(_handle)) {
__Error.WinIOError();
}
}
}
else if (_readPos < _readLen && CanSeek) {
FlushRead();
}
_readPos = 0;
_readLen = 0;
}
// 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 [....] 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 !FEATURE_PAL
#if !FEATURE_CORECLR
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
#endif // FEATURE_CORECLR
WriteCore(_buffer, 0, _writePos);
_writePos = 0;
#else
WriteCore(_buffer, 0, _writePos);
_writePos = 0;
#endif //!FEATURE_PAL
}
[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
[SecurityPermissionAttribute(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
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
[SecurityPermissionAttribute(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
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 !FEATURE_PAL
#if !FEATURE_CORECLR
if (_isAsync) {
IAsyncResult result = BeginReadCore(buffer, offset, count, null, null, 0);
return EndRead(result);
}
#endif // FEATURE_CORECLR
#endif //!FEATURE_PAL
// 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 (originSeekOrigin.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 [....] 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) {
// For invalid handles, detect the error and mark our handle
// as closed to give slightly better error messages. Also
// help ensure we avoid handle recycling bugs.
if (hr == Win32Native.ERROR_INVALID_HANDLE)
_handle.SetHandleAsInvalid();
__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 !FEATURE_PAL
#if !FEATURE_CORECLR
if (_isAsync) {
IAsyncResult result = BeginWriteCore(_buffer, 0, _writePos, null, null);
EndWrite(result);
}
else {
#endif // FEATURE_CORECLR
WriteCore(_buffer, 0, _writePos);
#if !FEATURE_CORECLR
}
#endif // FEATURE_CORECLR
#else
WriteCore(_buffer, 0, _writePos);
#endif //!FEATURE_PAL
_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 !FEATURE_PAL
#if !FEATURE_CORECLR
if (_isAsync) {
IAsyncResult result = BeginWriteCore(buffer, offset, count, null, null);
EndWrite(result);
return;
}
#endif // FEATURE_CORECLR
#endif //!FEATURE_PAL
// 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 !FEATURE_PAL && FEATURE_ASYNC_IO
if (!_isAsync)
return base.BeginRead(array, offset, numBytes, userCallback, stateObject);
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.");
FileStreamAsyncResult asyncResult = null;
if (_isPipe) {
// Pipes are ----ed up, at least when you have 2 different pipes
// that you want to use simultaneously. 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;
asyncResult = FileStreamAsyncResult.CreateBufferedReadResult(n, userCallback, stateObject);
asyncResult.CallUserCallback();
return asyncResult;
}
Contract.Assert(_writePos == 0, "FileStream must not have buffered write data here! Pipes should be unidirectional.");
return BeginReadCore(array, offset, numBytes, userCallback, stateObject, 0);
}
// 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 [....] 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;
asyncResult = FileStreamAsyncResult.CreateBufferedReadResult(n, userCallback, stateObject);
asyncResult.CallUserCallback();
return asyncResult;
}
// 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 || _isPipe) {
asyncResult = FileStreamAsyncResult.CreateBufferedReadResult(n, userCallback, stateObject);
asyncResult.CallUserCallback();
return asyncResult;
}
// 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;
asyncResult = 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.
}
return asyncResult;
#else
return base.BeginRead(array, offset, numBytes, userCallback, stateObject);
#endif // !FEATURE_PAL && FEATURE_ASYNC_IO
}
#if !FEATURE_PAL
[System.Security.SecuritySafeCritical] // auto-generated
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.AppDomain, ResourceScope.AppDomain)]
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
FileStreamAsyncResult asyncResult = new FileStreamAsyncResult();
asyncResult._handle = _handle;
asyncResult._userCallback = userCallback;
asyncResult._userStateObject = stateObject;
asyncResult._isWrite = false;
// Must set this 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.
asyncResult._numBufferedBytes = numBufferedBytesRead;
// 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);
asyncResult._waitHandle = waitHandle;
// Create a managed overlapped class
// We will set the file offsets later
Overlapped overlapped = new Overlapped(0, 0, IntPtr.Zero, asyncResult);
// Pack the Overlapped class, and store it in the async result
NativeOverlapped* intOverlapped;
if (userCallback != null)
intOverlapped = overlapped.Pack(IOCallback, bytes);
else
intOverlapped = overlapped.UnsafePack(null, bytes);
asyncResult._overlapped = intOverlapped;
// 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);
}
// 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;
}
#endif //!FEATURE_PAL
[System.Security.SecuritySafeCritical] // auto-generated
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 !FEATURE_PAL && FEATURE_ASYNC_IO
if (!_isAsync)
return base.EndRead(asyncResult);
FileStreamAsyncResult afsar = asyncResult as FileStreamAsyncResult;
if (afsar==null || afsar._isWrite)
__Error.WrongAsyncResult();
// Ensure we can't get into any ----s 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.
WaitHandle wh = afsar._waitHandle;
if (wh != 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 {
wh.WaitOne();
Contract.Assert(afsar._isComplete == true, "FileStream::EndRead - AsyncFSCallback didn't set _isComplete to true!");
}
finally {
wh.Close();
}
}
// Free memory & GC handles.
NativeOverlapped* overlappedPtr = afsar._overlapped;
if (overlappedPtr != null)
Overlapped.Free(overlappedPtr);
// Now check for any error during the read.
if (afsar._errorCode != 0)
__Error.WinIOError(afsar._errorCode, String.Empty);
return afsar._numBytes + afsar._numBufferedBytes;
#else
return base.EndRead(asyncResult);
#endif // !FEATURE_PAL && FEATURE_ASYNC_IO
}
// 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 !FEATURE_PAL && FEATURE_ASYNC_IO
if (!_isAsync)
return base.BeginWrite(array, offset, numBytes, userCallback, stateObject);
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) {
// Pipes are ----ed up, at least when you have 2 different pipes
// that you want to use simultaneously. 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.
FileStreamAsyncResult asyncResult;
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;
asyncResult = new FileStreamAsyncResult();
asyncResult._userCallback = userCallback;
asyncResult._userStateObject = stateObject;
asyncResult._waitHandle = null;
asyncResult._isWrite = true;
asyncResult._numBufferedBytes = numBytes;
asyncResult.CallUserCallback();
return asyncResult;
}
if (_writePos > 0) FlushWrite(false);
return BeginWriteCore(array, offset, numBytes, userCallback, stateObject);
#else
return base.BeginWrite(array, offset, numBytes, userCallback, stateObject);
#endif // !FEATURE_PAL && FEATURE_ASYNC_IO
}
#if !FEATURE_PAL
[System.Security.SecuritySafeCritical] // auto-generated
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.AppDomain, ResourceScope.AppDomain)]
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();
asyncResult._handle = _handle;
asyncResult._userCallback = userCallback;
asyncResult._userStateObject = stateObject;
asyncResult._isWrite = true;
// 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);
asyncResult._waitHandle = waitHandle;
// Create a managed overlapped class
// We will set the file offsets later
Overlapped overlapped = new Overlapped(0, 0, IntPtr.Zero, asyncResult);
// Pack the Overlapped class, and store it in the async result
NativeOverlapped* intOverlapped;
if (userCallback != null)
intOverlapped = overlapped.Pack(IOCallback, bytes);
else
intOverlapped = overlapped.UnsafePack(null, bytes);
asyncResult._overlapped = intOverlapped;
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);
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;
}
#endif //!FEATURE_PAL
[System.Security.SecuritySafeCritical] // auto-generated
public unsafe override void EndWrite(IAsyncResult asyncResult)
{
if (asyncResult==null)
throw new ArgumentNullException("asyncResult");
Contract.EndContractBlock();
#if !FEATURE_PAL && FEATURE_ASYNC_IO
if (!_isAsync) {
base.EndWrite(asyncResult);
return;
}
FileStreamAsyncResult afsar = asyncResult as FileStreamAsyncResult;
if (afsar==null || !afsar._isWrite)
__Error.WrongAsyncResult();
// Ensure we can't get into any ----s 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.
WaitHandle wh = afsar._waitHandle;
if (wh != 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 {
wh.WaitOne();
Contract.Assert(afsar._isComplete == true, "FileStream::EndWrite - AsyncFSCallback didn't set _isComplete to true!");
}
finally {
wh.Close();
}
}
// Free memory & GC handles.
NativeOverlapped* overlappedPtr = afsar._overlapped;
if (overlappedPtr != null)
Overlapped.Free(overlappedPtr);
// 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;
#else
base.EndWrite(asyncResult);
#endif // !FEATURE_PAL && FEATURE_ASYNC_IO
}
[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
private const int ERROR_BROKEN_PIPE = 109;
private 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 ----up 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 ---- 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;
}
// For invalid handles, detect the error and mark our handle
// as closed to give slightly better error messages. Also
// help ensure we avoid handle recycling bugs.
if (hr == Win32Native.ERROR_INVALID_HANDLE)
_handle.SetHandleAsInvalid();
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 ----up 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 ---- 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;
}
// For invalid handles, detect the error and mark our handle
// as closed to give slightly better error messages. Also
// help ensure we avoid handle recycling bugs.
if (hr == Win32Native.ERROR_INVALID_HANDLE)
_handle.SetHandleAsInvalid();
return -1;
}
else
hr = 0;
return numBytesWritten;
}
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- BinaryConverter.cs
- RbTree.cs
- SortAction.cs
- DialogDivider.cs
- AsymmetricAlgorithm.cs
- Cursor.cs
- Section.cs
- Optimizer.cs
- RemoteWebConfigurationHostStream.cs
- ConnectionManagementElementCollection.cs
- VBCodeProvider.cs
- ProbeMatches11.cs
- RightsManagementPermission.cs
- CheckedListBox.cs
- SetStoryboardSpeedRatio.cs
- RTLAwareMessageBox.cs
- WebPart.cs
- __TransparentProxy.cs
- RadioButton.cs
- BinaryQueryOperator.cs
- XmlFormatExtensionAttribute.cs
- WebScriptEndpointElement.cs
- XmlNodeChangedEventArgs.cs
- ApplicationId.cs
- ToolStripContentPanelDesigner.cs
- BitmapEffectInputData.cs
- IPAddress.cs
- RadioButtonStandardAdapter.cs
- DocumentXPathNavigator.cs
- LiteralText.cs
- DetailsViewUpdateEventArgs.cs
- RawStylusInputCustomData.cs
- XmlElementList.cs
- WmfPlaceableFileHeader.cs
- SimpleBitVector32.cs
- StringCollection.cs
- FormsAuthenticationTicket.cs
- GraphicsContainer.cs
- TextBoxLine.cs
- MetadataCache.cs
- ConfigXmlWhitespace.cs
- ProgressPage.cs
- StateMachineTimers.cs
- SystemWebExtensionsSectionGroup.cs
- XsltFunctions.cs
- ClientSession.cs
- graph.cs
- MulticastDelegate.cs
- SiteMapDataSource.cs
- BatchServiceHost.cs
- SrgsRuleRef.cs
- WebRequestModuleElement.cs
- TemplatedAdorner.cs
- JumpPath.cs
- MonthCalendarDesigner.cs
- WindowsListViewSubItem.cs
- Clause.cs
- AsyncCodeActivityContext.cs
- ConcatQueryOperator.cs
- TextServicesProperty.cs
- InfoCardBaseException.cs
- ForeignConstraint.cs
- EditingCoordinator.cs
- TraceRecord.cs
- XamlTreeBuilderBamlRecordWriter.cs
- OrderByBuilder.cs
- OdbcStatementHandle.cs
- WebPartConnectVerb.cs
- COM2PropertyDescriptor.cs
- ValueHandle.cs
- StateDesigner.Layouts.cs
- mediaeventargs.cs
- WindowsAltTab.cs
- MulticastDelegate.cs
- Codec.cs
- WebOperationContext.cs
- InputBindingCollection.cs
- IPHostEntry.cs
- InvalidAsynchronousStateException.cs
- InvokeMemberBinder.cs
- CriticalHandle.cs
- KeyValuePair.cs
- SQLBytes.cs
- TimeoutHelper.cs
- CharacterMetrics.cs
- DynamicExpression.cs
- TabControlCancelEvent.cs
- XslNumber.cs
- TableDetailsCollection.cs
- XmlQualifiedName.cs
- COM2TypeInfoProcessor.cs
- PixelFormat.cs
- StrokeCollectionConverter.cs
- SchemaImporterExtensionsSection.cs
- EntityKey.cs
- TraceLog.cs
- LineInfo.cs
- httpapplicationstate.cs
- FormViewUpdateEventArgs.cs
- StorageEntitySetMapping.cs