The Labs \ Source Viewer \ SSCLI \ System.IO \ FileStream

  1. // ==++==
  2. //
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. //
  14. // ==--==
  15. /*============================================================
  16. **
  17. ** Class:  FileStream
  18. **
  19. **
  20. ** Purpose: Exposes a Stream around a file, with full
  21. ** synchronous and asychronous support, and buffering.
  22. **
  23. **
  24. ===========================================================*/
  25. using System;
  26. using Microsoft.Win32;
  27. using Microsoft.Win32.SafeHandles;
  28. using System.Security;
  29. using System.Security.Permissions;
  30. using System.Threading;
  31. using System.Runtime.InteropServices;
  32. using System.Runtime.Remoting.Messaging;
  33. using System.Runtime.CompilerServices;
  34. using System.Globalization;
  35. using System.Runtime.Versioning;
  36. /*
  37. * FileStream supports different modes of accessing the disk - async mode
  38. * and sync mode.  They are two completely different codepaths in the
  39. * sync & async methods (ie, Read/Write vs. BeginRead/BeginWrite).  File
  40. * handles in NT can be opened in only sync or overlapped (async) mode,
  41. * and we have to deal with this pain.  Stream has implementations of
  42. * the sync methods in terms of the async ones, so we'll
  43. * call through to our base class to get those methods when necessary.
  44. *
  45. * Also buffering is added into FileStream as well. Folded in the
  46. * code from BufferedStream, so all the comments about it being mostly
  47. * aggressive (and the possible perf improvement) apply to FileStream as
  48. * well.  Also added some buffering to the async code paths.
  49. *
  50. * Class Invariants:
  51. * The class has one buffer, shared for reading & writing.  It can only be
  52. * used for one or the other at any point in time - not both.  The following
  53. * should be true:
  54. *  0 <= _readPos <= _readLen < _bufferSize
  55. *  0 <= _writePos < _bufferSize
  56. *  _readPos == _readLen && _readPos > 0 implies the read buffer is valid,
  57. *    but we're at the end of the buffer.
  58. *  _readPos == _readLen == 0 means the read buffer contains garbage.
  59. *  Either _writePos can be greater than 0, or _readLen & _readPos can be
  60. *    greater than zero, but neither can be greater than zero at the same time.
  61. *
  62. */
  63. namespace System.IO
  64. {
  65.     // This is an internal object implementing IAsyncResult with fields
  66.     // for all of the relevant data necessary to complete the IO operation.
  67.     // This is used by AsyncFSCallback and all of the async methods.
  68.     unsafe internal sealed class FileStreamAsyncResult : IAsyncResult
  69.     {
  70.         // README:
  71.         // If you modify the order of these fields, make sure to update
  72.         // the native VM definition of this class as well!!!
  73.        
  74.         // User code callback
  75.         internal AsyncCallback _userCallback;
  76.         internal object _userStateObject;
  77.         internal ManualResetEvent _waitHandle;
  78.         internal SafeFileHandle _handle;
  79.         // For cancellation support.
  80.         internal int _EndXxxCalled;
  81.         // Whether we've called EndXxx already.
  82.         internal int _numBytes;
  83.         // number of bytes read OR written
  84.         internal int _errorCode;
  85.         internal int _numBufferedBytes;
  86.        
  87.         internal bool _isWrite;
  88.         // Whether this is a read or a write
  89.         internal bool _isComplete;
  90.         // Value for IsCompleted property
  91.         internal bool _completedSynchronously;
  92.        
  93.        
  94.         public object AsyncState {
  95.             get { return _userStateObject; }
  96.         }
  97.        
  98.         public bool IsCompleted {
  99.             get { return _isComplete; }
  100.         }
  101.        
  102.         public WaitHandle AsyncWaitHandle {
  103.             get { return null; }
  104.         }
  105.        
  106.         // Returns true iff the user callback was called by the thread that
  107.         // called BeginRead or BeginWrite. If we use an async delegate or
  108.         // threadpool thread internally, this will be false. This is used
  109.         // by code to determine whether a successive call to BeginRead needs
  110.         // to be done on their main thread or in their callback to avoid a
  111.         // stack overflow on many reads or writes.
  112.         public bool CompletedSynchronously {
  113.             get { return _completedSynchronously; }
  114.         }
  115.        
  116.         static internal FileStreamAsyncResult CreateBufferedReadResult(int numBufferedBytes, AsyncCallback userCallback, object userStateObject)
  117.         {
  118.             FileStreamAsyncResult asyncResult = new FileStreamAsyncResult();
  119.             asyncResult._userCallback = userCallback;
  120.             asyncResult._userStateObject = userStateObject;
  121.             asyncResult._isWrite = false;
  122.             asyncResult._numBufferedBytes = numBufferedBytes;
  123.             return asyncResult;
  124.         }
  125.        
  126.         private void CallUserCallbackWorker(object callbackState)
  127.         {
  128.             _isComplete = true;
  129.             if (_waitHandle != null)
  130.                 _waitHandle.Set();
  131.             _userCallback(this);
  132.         }
  133.        
  134.         internal void CallUserCallback()
  135.         {
  136.             // Convenience method for me, since I have to do this in a number
  137.             // of places in the buffering code for fake IAsyncResults.
  138.             // AsyncFSCallback intentionally does not use this method.
  139.            
  140.             if (_userCallback != null) {
  141.                 // Call user's callback on a threadpool thread.
  142.                 // Set completedSynchronously to false, since it's on another
  143.                 // thread, not the main thread.
  144.                 _completedSynchronously = false;
  145.                 ThreadPool.QueueUserWorkItem(new WaitCallback(CallUserCallbackWorker));
  146.             }
  147.             else {
  148.                 _isComplete = true;
  149.                 if (_waitHandle != null)
  150.                     _waitHandle.Set();
  151.             }
  152.         }
  153.        
  154.        
  155.     }
  156.    
  157.     [ComVisible(true)]
  158.     public class FileStream : Stream
  159.     {
  160.         internal const int DefaultBufferSize = 4096;
  161.        
  162.        
  163.         private static readonly bool _canUseAsync = false;
  164.        
  165.         private byte[] _buffer;
  166.         // Shared read/write buffer. Alloc on first use.
  167.         private string _fileName;
  168.         // Fully qualified file name.
  169.         private bool _isAsync;
  170.         // Whether we opened the handle for overlapped IO
  171.         private bool _canRead;
  172.         private bool _canWrite;
  173.         private bool _canSeek;
  174.         private bool _exposedHandle;
  175.         // Could other code be using this handle?
  176.         private bool _isPipe;
  177.         // Whether to disable async buffering code.
  178.         private int _readPos;
  179.         // Read pointer within shared buffer.
  180.         private int _readLen;
  181.         // Number of bytes read in buffer from file.
  182.         private int _writePos;
  183.         // Write pointer within shared buffer.
  184.         private int _bufferSize;
  185.         // Length of internal buffer, if it's allocated.
  186.         private SafeFileHandle _handle;
  187.         private long _pos;
  188.         // Cache current location in the file.
  189.         private long _appendStart;
  190.         // When appending, prevent overwriting file.
  191.        
  192.         //This exists only to support IsolatedStorageFileStream.
  193.         //Any changes to FileStream must include the corresponding changes in IsolatedStorage.
  194.         internal FileStream()
  195.         {
  196.             _fileName = null;
  197.             _handle = null;
  198.         }
  199.        
  200.         [ResourceExposure(ResourceScope.Machine)]
  201.         [ResourceConsumption(ResourceScope.Machine)]
  202.         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)
  203.         {
  204.         }
  205.        
  206.         [ResourceExposure(ResourceScope.Machine)]
  207.         [ResourceConsumption(ResourceScope.Machine)]
  208.         public FileStream(string path, FileMode mode, FileAccess access) : this(path, mode, access, FileShare.Read, DefaultBufferSize, FileOptions.None, Path.GetFileName(path), false)
  209.         {
  210.         }
  211.        
  212.         [ResourceExposure(ResourceScope.Machine)]
  213.         [ResourceConsumption(ResourceScope.Machine)]
  214.         public FileStream(string path, FileMode mode, FileAccess access, FileShare share) : this(path, mode, access, share, DefaultBufferSize, FileOptions.None, Path.GetFileName(path), false)
  215.         {
  216.         }
  217.        
  218.         [ResourceExposure(ResourceScope.Machine)]
  219.         [ResourceConsumption(ResourceScope.Machine)]
  220.         public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize) : this(path, mode, access, share, bufferSize, FileOptions.None, Path.GetFileName(path), false)
  221.         {
  222.         }
  223.        
  224.         [ResourceExposure(ResourceScope.Machine)]
  225.         [ResourceConsumption(ResourceScope.Machine)]
  226.         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)
  227.         {
  228.         }
  229.        
  230.         [ResourceExposure(ResourceScope.Machine)]
  231.         [ResourceConsumption(ResourceScope.Machine)]
  232.         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)
  233.         {
  234.         }
  235.        
  236.        
  237.         [ResourceExposure(ResourceScope.Machine)]
  238.         [ResourceConsumption(ResourceScope.Machine)]
  239.         internal FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options, string msgPath, bool bFromProxy)
  240.         {
  241.             Win32Native.SECURITY_ATTRIBUTES secAttrs = GetSecAttrs(share);
  242.             Init(path, mode, access, 0, false, share, bufferSize, options, secAttrs, msgPath,
  243.             bFromProxy);
  244.            
  245.         }
  246.        
  247.         // AccessControl namespace is not defined in Rotor
  248.         [ResourceExposure(ResourceScope.Machine)]
  249.         [ResourceConsumption(ResourceScope.Machine)]
  250.         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,
  251.         bool bFromProxy)
  252.         {
  253.             // msgPath must be safe to hand back to untrusted code.
  254.            
  255.             _fileName = msgPath;
  256.             // To handle odd cases of finalizing partially constructed objects.
  257.             _exposedHandle = false;
  258.            
  259.             if (path == null)
  260.                 throw new ArgumentNullException("path", Environment.GetResourceString("ArgumentNull_Path"));
  261.             if (path.Length == 0)
  262.                 throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath"));
  263.            
  264.             BCLDebug.Assert(!useRights, "Specifying FileSystemRights is not supported on this platform!");
  265.            
  266.             // don't include inheritable in our bounds check for share
  267.             FileShare tempshare = share & ~FileShare.Inheritable;
  268.             string badArg = null;
  269.            
  270.             if (mode < FileMode.CreateNew || mode > FileMode.Append)
  271.                 badArg = "mode";
  272.             else if (!useRights && (access < FileAccess.Read || access > FileAccess.ReadWrite))
  273.                 badArg = "access";
  274.             else if (tempshare < FileShare.None || tempshare > (FileShare.ReadWrite | FileShare.Delete))
  275.                 badArg = "share";
  276.            
  277.             if (badArg != null)
  278.                 throw new ArgumentOutOfRangeException(badArg, Environment.GetResourceString("ArgumentOutOfRange_Enum"));
  279.            
  280.             // NOTE: any change to FileOptions enum needs to be matched here in the error validation
  281.             if (options != FileOptions.None && (options & ~(FileOptions.WriteThrough | FileOptions.Asynchronous | FileOptions.RandomAccess | FileOptions.DeleteOnClose | FileOptions.SequentialScan | FileOptions.Encrypted | (FileOptions)536870912)) != 0)
  282.                 /* NoBuffering */                throw new ArgumentOutOfRangeException("options", Environment.GetResourceString("ArgumentOutOfRange_Enum"));
  283.            
  284.             if (bufferSize <= 0)
  285.                 throw new ArgumentOutOfRangeException("bufferSize", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
  286.            
  287.             // Write access validation
  288.             if (!useRights && (access & FileAccess.Write) == 0) {
  289.                 if (mode == FileMode.Truncate || mode == FileMode.CreateNew || mode == FileMode.Create || mode == FileMode.Append) {
  290.                     // No write access
  291.                     if (!useRights)
  292.                         throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("Argument_InvalidFileMode&AccessCombo"), mode, access));
  293.                 }
  294.             }
  295.            
  296.            
  297.             int fAccess;
  298.             if (!useRights) {
  299.                 fAccess = access == FileAccess.Read ? GENERIC_READ : access == FileAccess.Write ? GENERIC_WRITE : GENERIC_READ | GENERIC_WRITE;
  300.             }
  301.             else {
  302.                 fAccess = rights;
  303.             }
  304.            
  305.             // Get absolute path - Security needs this to prevent something
  306.             // like trying to create a file in c:\tmp with the name
  307.             // "..\WinNT\System32\ntoskrnl.exe". Store it for user convenience.
  308.             string filePath = Path.GetFullPathInternal(path);
  309.            
  310.             _fileName = filePath;
  311.            
  312.             // Prevent access to your disk drives as raw block devices.
  313.             if (filePath.StartsWith("\\\\.\\", StringComparison.Ordinal))
  314.                 throw new ArgumentException(Environment.GetResourceString("Arg_DevicesNotSupported"));
  315.            
  316.             // Build up security permissions required, as well as validate we
  317.             // have a sensible set of parameters. IE, creating a brand new file
  318.             // for reading doesn't make much sense.
  319.             FileIOPermissionAccess secAccess = FileIOPermissionAccess.NoAccess;
  320.             if (!useRights && (access & FileAccess.Read) != 0) {
  321.                 if (mode == FileMode.Append)
  322.                     throw new ArgumentException(Environment.GetResourceString("Argument_InvalidAppendMode"));
  323.                 else
  324.                     secAccess = secAccess | FileIOPermissionAccess.Read;
  325.             }
  326.             // I can't think of any combos of FileMode we should disallow if we
  327.             // don't have read access. Writing would pretty much always be valid
  328.             // in those cases.
  329.            
  330.             // For any FileSystemRights other than ReadAndExecute, demand Write permission
  331.             // This is probably bit overkill for TakeOwnership etc but we don't have any
  332.             // matching FileIOPermissionAccess to demand. It is better that we ask for Write permission.
  333.            
  334.             if (!useRights && (access & FileAccess.Write) != 0) {
  335.                 if (mode == FileMode.Append)
  336.                     secAccess = secAccess | FileIOPermissionAccess.Append;
  337.                 else
  338.                     secAccess = secAccess | FileIOPermissionAccess.Write;
  339.             }
  340.            
  341.             new FileIOPermission(secAccess, new string[] {filePath}, false, false).Demand();
  342.             // Our Inheritable bit was stolen from Windows, but should be set in
  343.             // the security attributes class. Don't leave this bit set.
  344.             share &= ~FileShare.Inheritable;
  345.            
  346.             bool seekToEnd = (mode == FileMode.Append);
  347.             // Must use a valid Win32 constant here...
  348.             if (mode == FileMode.Append)
  349.                 mode = FileMode.OpenOrCreate;
  350.            
  351.             // WRT async IO, do the right thing for whatever platform we're on.
  352.             // This way, someone can easily write code that opens a file
  353.             // asynchronously no matter what their platform is.
  354.             if (_canUseAsync && (options & FileOptions.Asynchronous) != 0)
  355.                 _isAsync = true;
  356.             else
  357.                 options &= ~FileOptions.Asynchronous;
  358.            
  359.             int flagsAndAttributes = (int)options;
  360.            
  361.             // Don't pop up a dialog for reading from an emtpy floppy drive
  362.             int oldMode = Win32Native.SetErrorMode(Win32Native.SEM_FAILCRITICALERRORS);
  363.             try {
  364.                 _handle = Win32Native.SafeCreateFile(filePath, fAccess, share, secAttrs, mode, flagsAndAttributes, Win32Native.NULL);
  365.                
  366.                 if (_handle.IsInvalid) {
  367.                     // Return a meaningful exception, using the RELATIVE path to
  368.                     // the file to avoid returning extra information to the caller
  369.                     // unless they have path discovery permission, in which case
  370.                     // the full path is fine & useful.
  371.                    
  372.                     // NT5 oddity - when trying to open "C:\" as a FileStream,
  373.                     // we usually get ERROR_PATH_NOT_FOUND from the OS. We should
  374.                     // probably be consistent w/ every other directory.
  375.                     int errorCode = Marshal.GetLastWin32Error();
  376.                     if (errorCode == __Error.ERROR_PATH_NOT_FOUND && filePath.Equals(Directory.InternalGetDirectoryRoot(filePath)))
  377.                         errorCode = __Error.ERROR_ACCESS_DENIED;
  378.                    
  379.                     // We need to give an exception, and preferably it would include
  380.                     // the fully qualified path name. Do security check here. If
  381.                     // we fail, give back the msgPath, which should not reveal much.
  382.                     // While this logic is largely duplicated in
  383.                     // __Error.WinIOError, we need this for
  384.                     // IsolatedStorageFileStream.
  385.                     bool canGiveFullPath = false;
  386.                    
  387.                     if (!bFromProxy) {
  388.                         try {
  389.                             new FileIOPermission(FileIOPermissionAccess.PathDiscovery, new string[] {_fileName}, false, false).Demand();
  390.                             canGiveFullPath = true;
  391.                         }
  392.                         catch (SecurityException) {
  393.                         }
  394.                     }
  395.                    
  396.                     if (canGiveFullPath)
  397.                         __Error.WinIOError(errorCode, _fileName);
  398.                     else
  399.                         __Error.WinIOError(errorCode, msgPath);
  400.                 }
  401.             }
  402.             finally {
  403.                 Win32Native.SetErrorMode(oldMode);
  404.             }
  405.            
  406.             // Disallow access to all non-file devices from the FileStream
  407.             // constructors that take a String. Everyone else can call
  408.             // CreateFile themselves then use the constructor that takes an
  409.             // IntPtr. Disallows "con:", "com1:", "lpt1:", etc.
  410.             int fileType = Win32Native.GetFileType(_handle);
  411.             if (fileType != Win32Native.FILE_TYPE_DISK) {
  412.                 _handle.Close();
  413.                 throw new NotSupportedException(Environment.GetResourceString("NotSupported_FileStreamOnNonFiles"));
  414.             }
  415.            
  416.             if (!useRights) {
  417.                 _canRead = (access & FileAccess.Read) != 0;
  418.                 _canWrite = (access & FileAccess.Write) != 0;
  419.             }
  420.            
  421.             _canSeek = true;
  422.             _isPipe = false;
  423.             _pos = 0;
  424.             _bufferSize = bufferSize;
  425.             _readPos = 0;
  426.             _readLen = 0;
  427.             _writePos = 0;
  428.            
  429.             // For Append mode...
  430.             if (seekToEnd) {
  431.                 _appendStart = SeekCore(0, SeekOrigin.End);
  432.             }
  433.             else {
  434.                 _appendStart = -1;
  435.             }
  436.         }
  437.        
  438.         [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access) instead. http://go.microsoft.com/fwlink/?linkid=14202")]
  439.         [ResourceExposure(ResourceScope.Machine)]
  440.         [ResourceConsumption(ResourceScope.Machine)]
  441.         public FileStream(IntPtr handle, FileAccess access) : this(handle, access, true, DefaultBufferSize, false)
  442.         {
  443.         }
  444.        
  445.         [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")]
  446.         [ResourceExposure(ResourceScope.Machine)]
  447.         [ResourceConsumption(ResourceScope.Machine)]
  448.         public FileStream(IntPtr handle, FileAccess access, bool ownsHandle) : this(handle, access, ownsHandle, DefaultBufferSize, false)
  449.         {
  450.         }
  451.        
  452.         [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")]
  453.         [ResourceExposure(ResourceScope.Machine)]
  454.         [ResourceConsumption(ResourceScope.Machine)]
  455.         public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize) : this(handle, access, ownsHandle, bufferSize, false)
  456.         {
  457.         }
  458.        
  459.         // We explicitly do a Demand, not a LinkDemand here.
  460.         [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")]
  461.         [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
  462.         [ResourceExposure(ResourceScope.Machine)]
  463.         [ResourceConsumption(ResourceScope.Machine)]
  464.         public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync) : this(new SafeFileHandle(handle, ownsHandle), access, bufferSize, isAsync)
  465.         {
  466.         }
  467.        
  468.         [ResourceExposure(ResourceScope.Machine)]
  469.         [ResourceConsumption(ResourceScope.Machine)]
  470.         public FileStream(SafeFileHandle handle, FileAccess access) : this(handle, access, DefaultBufferSize, false)
  471.         {
  472.         }
  473.        
  474.         [ResourceExposure(ResourceScope.Machine)]
  475.         [ResourceConsumption(ResourceScope.Machine)]
  476.         public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize) : this(handle, access, bufferSize, false)
  477.         {
  478.         }
  479.        
  480.         [ResourceExposure(ResourceScope.Machine)]
  481.         [ResourceConsumption(ResourceScope.Machine)]
  482.         [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
  483.         public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync)
  484.         {
  485.             // To ensure we don't leak a handle, put it in a SafeFileHandle first
  486.             if (handle.IsInvalid)
  487.                 throw new ArgumentException(Environment.GetResourceString("Arg_InvalidHandle"), "handle");
  488.            
  489.             _handle = handle;
  490.             _exposedHandle = true;
  491.            
  492.             // Now validate arguments.
  493.             if (access < FileAccess.Read || access > FileAccess.ReadWrite)
  494.                 throw new ArgumentOutOfRangeException("access", Environment.GetResourceString("ArgumentOutOfRange_Enum"));
  495.             if (bufferSize <= 0)
  496.                 throw new ArgumentOutOfRangeException("bufferSize", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
  497.            
  498.             int handleType = Win32Native.GetFileType(_handle);
  499.             BCLDebug.Assert(handleType == Win32Native.FILE_TYPE_DISK || handleType == Win32Native.FILE_TYPE_PIPE || handleType == Win32Native.FILE_TYPE_CHAR, "FileStream was passed an unknown file type!");
  500.             _isAsync = isAsync && _canUseAsync;
  501.             // On Win9x, just do the right thing.
  502.             _canRead = 0 != (access & FileAccess.Read);
  503.             _canWrite = 0 != (access & FileAccess.Write);
  504.             _canSeek = handleType == Win32Native.FILE_TYPE_DISK;
  505.             _bufferSize = bufferSize;
  506.             _readPos = 0;
  507.             _readLen = 0;
  508.             _writePos = 0;
  509.             _fileName = null;
  510.             _isPipe = handleType == Win32Native.FILE_TYPE_PIPE;
  511.            
  512.             if (handleType != Win32Native.FILE_TYPE_PIPE)
  513.                 VerifyHandleIsSync();
  514.             if (_canSeek)
  515.                 SeekCore(0, SeekOrigin.Current);
  516.             else
  517.                 _pos = 0;
  518.         }
  519.        
  520.         private static Win32Native.SECURITY_ATTRIBUTES GetSecAttrs(FileShare share)
  521.         {
  522.             Win32Native.SECURITY_ATTRIBUTES secAttrs = null;
  523.             if ((share & FileShare.Inheritable) != 0) {
  524.                 secAttrs = new Win32Native.SECURITY_ATTRIBUTES();
  525.                 secAttrs.nLength = (int)Marshal.SizeOf(secAttrs);
  526.                
  527.                 secAttrs.bInheritHandle = 1;
  528.             }
  529.             return secAttrs;
  530.         }
  531.        
  532.        
  533.         // Verifies that this handle supports synchronous IO operations (unless you
  534.         // didn't open it for either reading or writing).
  535.         unsafe private void VerifyHandleIsSync()
  536.         {
  537.             // Do NOT use this method on pipes. Reading or writing to a pipe may
  538.             // cause an app to block incorrectly, introducing a deadlock (depending
  539.             // on whether a write will wake up an already-blocked thread or this
  540.             // FileStream's thread).
  541.            
  542.             // Do NOT change this to use a byte[] of length 0, or test test won't
  543.             // work. Our ReadFile & WriteFile methods are special cased to return
  544.             // for arrays of length 0, since we'd get an IndexOutOfRangeException
  545.             // while using C#'s fixed syntax.
  546.             byte[] bytes = new byte[1];
  547.             int hr = 0;
  548.             int r = 0;
  549.            
  550.             // If the handle is a pipe, ReadFile will block until there
  551.             // has been a write on the other end. We'll just have to deal with it,
  552.             // For the read end of a pipe, you can mess up and
  553.             // accidentally read synchronously from an async pipe.
  554.             if (CanRead) {
  555.                 r = ReadFileNative(_handle, bytes, 0, 0, null, out hr);
  556.             }
  557.             else if (CanWrite) {
  558.                 r = WriteFileNative(_handle, bytes, 0, 0, null, out hr);
  559.             }
  560.            
  561.             if (hr == ERROR_INVALID_PARAMETER)
  562.                 throw new ArgumentException(Environment.GetResourceString("Arg_HandleNotSync"));
  563.             if (hr == Win32Native.ERROR_INVALID_HANDLE)
  564.                 __Error.WinIOError(hr, "<OS handle>");
  565.         }
  566.        
  567.        
  568.         public override bool CanRead {
  569.             get { return _canRead; }
  570.         }
  571.        
  572.         public override bool CanWrite {
  573.             get { return _canWrite; }
  574.         }
  575.        
  576.         public override bool CanSeek {
  577.             get { return _canSeek; }
  578.         }
  579.        
  580.         public virtual bool IsAsync {
  581.             get { return _isAsync; }
  582.         }
  583.        
  584.         public override long Length {
  585.             get {
  586.                 if (_handle.IsClosed)
  587.                     __Error.FileNotOpen();
  588.                 if (!CanSeek)
  589.                     __Error.SeekNotSupported();
  590.                 int hi = 0;
  591.                 int lo = 0;
  592.                
  593.                 lo = Win32Native.GetFileSize(_handle, out hi);
  594.                
  595.                 if (lo == -1) {
  596.                     // Check for either an error or a 4GB - 1 byte file.
  597.                     int hr = Marshal.GetLastWin32Error();
  598.                     if (hr != 0)
  599.                         __Error.WinIOError(hr, String.Empty);
  600.                 }
  601.                 long len = (((long)hi) << 32) | ((uint)lo);
  602.                 // If we're writing near the end of the file, we must include our
  603.                 // internal buffer in our Length calculation. Don't flush because
  604.                 // we use the length of the file in our async write method.
  605.                 if (_writePos > 0 && _pos + _writePos > len)
  606.                     len = _writePos + _pos;
  607.                 return len;
  608.             }
  609.         }
  610.        
  611.         public string Name {
  612.             get {
  613.                 if (_fileName == null)
  614.                     return Environment.GetResourceString("IO_UnknownFileName");
  615.                 new FileIOPermission(FileIOPermissionAccess.PathDiscovery, new string[] {_fileName}, false, false).Demand();
  616.                 return _fileName;
  617.             }
  618.         }
  619.        
  620.         internal string NameInternal {
  621.             get {
  622.                 if (_fileName == null)
  623.                     return "<UnknownFileName>";
  624.                 return _fileName;
  625.             }
  626.         }
  627.        
  628.         public override long Position {
  629.             get {
  630.                 if (_handle.IsClosed)
  631.                     __Error.FileNotOpen();
  632.                 if (!CanSeek)
  633.                     __Error.SeekNotSupported();
  634.                 BCLDebug.Assert((_readPos == 0 && _readLen == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLen), "We're either reading or writing, but not both.");
  635.                
  636.                 // Verify that internal position is in sync with the handle
  637.                 if (_exposedHandle)
  638.                     VerifyOSHandlePosition();
  639.                
  640.                 // Compensate for buffer that we read from the handle (_readLen) Vs what the user
  641.                 // read so far from the internel buffer (_readPos). Of course add any unwrittern
  642.                 // buffered data
  643.                 return _pos + (_readPos - _readLen + _writePos);
  644.             }
  645.             set {
  646.                 if (value < 0)
  647.                     throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  648.                 if (_writePos > 0)
  649.                     FlushWrite(false);
  650.                 _readPos = 0;
  651.                 _readLen = 0;
  652.                 Seek(value, SeekOrigin.Begin);
  653.             }
  654.         }
  655.        
  656.        
  657.        
  658.         protected override void Dispose(bool disposing)
  659.         {
  660.             // Nothing will be done differently based on whether we are
  661.             // disposing vs. finalizing. This is taking advantage of the
  662.             // weak ordering between normal finalizable objects & critical
  663.             // finalizable objects, which I included in the SafeHandle
  664.             // design for FileStream, which would often "just work" when
  665.             // finalized.
  666.             try {
  667.                 if (_handle != null && !_handle.IsClosed) {
  668.                     // Flush data to disk iff we were writing. After
  669.                     // thinking about this, we also don't need to flush
  670.                     // our read position, regardless of whether the handle
  671.                     // was exposed to the user. They probably would NOT
  672.                     // want us to do this.
  673.                     if (_writePos > 0) {
  674.                         FlushWrite(!disposing);
  675.                     }
  676.                 }
  677.             }
  678.             finally {
  679.                 if (_handle != null && !_handle.IsClosed)
  680.                     _handle.Dispose();
  681.                
  682.                 _canRead = false;
  683.                 _canWrite = false;
  684.                 _canSeek = false;
  685.                 // Don't set the buffer to null, to avoid a NullReferenceException
  686.                 // when users have a race condition in their code (ie, they call
  687.                 // Close when calling another method on Stream like Read).
  688.                 //_buffer = null;
  689.                 base.Dispose(disposing);
  690.             }
  691.         }
  692.        
  693.         ~FileStream()
  694.         {
  695.             if (_handle != null) {
  696.                 BCLDebug.Correctness(_handle.IsClosed, "You didn't close a FileStream & it got finalized. Name: \"" + _fileName + "\"");
  697.                 Dispose(false);
  698.             }
  699.         }
  700.        
  701.         public override void Flush()
  702.         {
  703.             // This code is duplicated in Dispose
  704.             if (_handle.IsClosed)
  705.                 __Error.FileNotOpen();
  706.             if (_writePos > 0) {
  707.                 FlushWrite(false);
  708.             }
  709.             else if (_readPos < _readLen && CanSeek) {
  710.                 FlushRead();
  711.             }
  712.         }
  713.        
  714.         // Reading is done by blocks from the file, but someone could read
  715.         // 1 byte from the buffer then write. At that point, the OS's file
  716.         // pointer is out of sync with the stream's position. All write
  717.         // functions should call this function to preserve the position in the file.
  718.         private void FlushRead()
  719.         {
  720.             BCLDebug.Assert(_writePos == 0, "FileStream: Write buffer must be empty in FlushRead!");
  721.             if (_readPos - _readLen != 0) {
  722.                 BCLDebug.Assert(CanSeek, "FileStream will lose buffered read data now.");
  723.                 SeekCore(_readPos - _readLen, SeekOrigin.Current);
  724.             }
  725.             _readPos = 0;
  726.             _readLen = 0;
  727.         }
  728.        
  729.         // Writes are buffered. Anytime the buffer fills up
  730.         // (_writePos + delta > _bufferSize) or the buffer switches to reading
  731.         // and there is left over data (_writePos > 0), this function must be called.
  732.         private void FlushWrite(bool calledFromFinalizer)
  733.         {
  734.             BCLDebug.Assert(_readPos == 0 && _readLen == 0, "FileStream: Read buffer must be empty in FlushWrite!");
  735.            
  736.             WriteCore(_buffer, 0, _writePos);
  737.             _writePos = 0;
  738.         }
  739.        
  740.        
  741.         [Obsolete("This property has been deprecated. Please use FileStream's SafeFileHandle property instead. http://go.microsoft.com/fwlink/?linkid=14202")]
  742.         public virtual IntPtr Handle {
  743.             [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
  744.             [SecurityPermissionAttribute(SecurityAction.InheritanceDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
  745.             [ResourceExposure(ResourceScope.Machine)]
  746.             [ResourceConsumption(ResourceScope.Machine)]
  747.             get {
  748.                 Flush();
  749.                 // Explicitly dump any buffered data, since the user could move our
  750.                 // position or write to the file.
  751.                 _readPos = 0;
  752.                 _readLen = 0;
  753.                 _writePos = 0;
  754.                 _exposedHandle = true;
  755.                
  756.                 return _handle.DangerousGetHandle();
  757.             }
  758.         }
  759.        
  760.         public virtual SafeFileHandle SafeFileHandle {
  761.             [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
  762.             [SecurityPermissionAttribute(SecurityAction.InheritanceDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
  763.             get {
  764.                 Flush();
  765.                 // Explicitly dump any buffered data, since the user could move our
  766.                 // position or write to the file.
  767.                 _readPos = 0;
  768.                 _readLen = 0;
  769.                 _writePos = 0;
  770.                 _exposedHandle = true;
  771.                
  772.                 return _handle;
  773.             }
  774.         }
  775.        
  776.         public override void SetLength(long value)
  777.         {
  778.             if (value < 0)
  779.                 throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  780.             if (_handle.IsClosed)
  781.                 __Error.FileNotOpen();
  782.             if (!CanSeek)
  783.                 __Error.SeekNotSupported();
  784.             if (!CanWrite)
  785.                 __Error.WriteNotSupported();
  786.             // Handle buffering updates.
  787.             if (_writePos > 0) {
  788.                 FlushWrite(false);
  789.             }
  790.             else if (_readPos < _readLen) {
  791.                 FlushRead();
  792.             }
  793.             if (_appendStart != -1 && value < _appendStart)
  794.                 throw new IOException(Environment.GetResourceString("IO.IO_SetLengthAppendTruncate"));
  795.             SetLengthCore(value);
  796.         }
  797.        
  798.         private void SetLengthCore(long value)
  799.         {
  800.             BCLDebug.Assert(value >= 0, "value >= 0");
  801.             long origPos = _pos;
  802.            
  803.             if (_exposedHandle)
  804.                 VerifyOSHandlePosition();
  805.             if (_pos != value)
  806.                 SeekCore(value, SeekOrigin.Begin);
  807.             if (!Win32Native.SetEndOfFile(_handle)) {
  808.                 int hr = Marshal.GetLastWin32Error();
  809.                 if (hr == __Error.ERROR_INVALID_PARAMETER)
  810.                     throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_FileLengthTooBig"));
  811.                 __Error.WinIOError(hr, String.Empty);
  812.             }
  813.             // Return file pointer to where it was before setting length
  814.             if (origPos != value) {
  815.                 if (origPos < value)
  816.                     SeekCore(origPos, SeekOrigin.Begin);
  817.                 else
  818.                     SeekCore(0, SeekOrigin.End);
  819.             }
  820.         }
  821.        
  822.         public override int Read(        [In(), Out()]
  823. byte[] array, int offset, int count)
  824.         {
  825.             if (array == null)
  826.                 throw new ArgumentNullException("array", Environment.GetResourceString("ArgumentNull_Buffer"));
  827.             if (offset < 0)
  828.                 throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  829.             if (count < 0)
  830.                 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  831.             if (array.Length - offset < count)
  832.                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
  833.            
  834.             if (_handle.IsClosed)
  835.                 __Error.FileNotOpen();
  836.            
  837.             BCLDebug.Assert((_readPos == 0 && _readLen == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLen), "We're either reading or writing, but not both.");
  838.            
  839.             bool isBlocked = false;
  840.             int n = _readLen - _readPos;
  841.             // if the read buffer is empty, read into either user's array or our
  842.             // buffer, depending on number of bytes user asked for and buffer size.
  843.             if (n == 0) {
  844.                 if (!CanRead)
  845.                     __Error.ReadNotSupported();
  846.                 if (_writePos > 0)
  847.                     FlushWrite(false);
  848.                 if (!CanSeek || (count >= _bufferSize)) {
  849.                     n = ReadCore(array, offset, count);
  850.                     // Throw away read buffer.
  851.                     _readPos = 0;
  852.                     _readLen = 0;
  853.                     return n;
  854.                 }
  855.                 if (_buffer == null)
  856.                     _buffer = new byte[_bufferSize];
  857.                 n = ReadCore(_buffer, 0, _bufferSize);
  858.                 if (n == 0)
  859.                     return 0;
  860.                 isBlocked = n < _bufferSize;
  861.                 _readPos = 0;
  862.                 _readLen = n;
  863.             }
  864.             // Now copy min of count or numBytesAvailable (ie, near EOF) to array.
  865.             if (n > count)
  866.                 n = count;
  867.             Buffer.InternalBlockCopy(_buffer, _readPos, array, offset, n);
  868.             _readPos += n;
  869.            
  870.            
  871.             if (!_isPipe) {
  872.                 // If we hit the end of the buffer and didn't have enough bytes, we must
  873.                 // read some more from the underlying stream. However, if we got
  874.                 // fewer bytes from the underlying stream than we asked for (ie, we're
  875.                 // probably blocked), don't ask for more bytes.
  876.                 if (n < count && !isBlocked) {
  877.                     BCLDebug.Assert(_readPos == _readLen, "Read buffer should be empty!");
  878.                     int moreBytesRead = ReadCore(array, offset + n, count - n);
  879.                     n += moreBytesRead;
  880.                     // We've just made our buffer inconsistent with our position
  881.                     // pointer. We must throw away the read buffer.
  882.                     _readPos = 0;
  883.                     _readLen = 0;
  884.                 }
  885.             }
  886.            
  887.             return n;
  888.         }
  889.        
  890.         unsafe private int ReadCore(byte[] buffer, int offset, int count)
  891.         {
  892.             BCLDebug.Assert(!_handle.IsClosed, "!_handle.IsClosed");
  893.             BCLDebug.Assert(CanRead, "CanRead");
  894.            
  895.             BCLDebug.Assert(buffer != null, "buffer != null");
  896.             BCLDebug.Assert(_writePos == 0, "_writePos == 0");
  897.             BCLDebug.Assert(offset >= 0, "offset is negative");
  898.             BCLDebug.Assert(count >= 0, "count is negative");
  899.            
  900.             // Make sure we are reading from the right spot
  901.             if (_exposedHandle)
  902.                 VerifyOSHandlePosition();
  903.            
  904.             int hr = 0;
  905.             int r = ReadFileNative(_handle, buffer, offset, count, null, out hr);
  906.             if (r == -1) {
  907.                 if (hr == ERROR_BROKEN_PIPE) {
  908.                     r = 0;
  909.                 }
  910.                 else {
  911.                     if (hr == ERROR_INVALID_PARAMETER)
  912.                         throw new ArgumentException(Environment.GetResourceString("Arg_HandleNotSync"));
  913.                    
  914.                     __Error.WinIOError(hr, String.Empty);
  915.                 }
  916.             }
  917.             BCLDebug.Assert(r >= 0, "FileStream's ReadCore is likely broken.");
  918.             _pos += r;
  919.            
  920.             return r;
  921.         }
  922.        
  923.         public override long Seek(long offset, SeekOrigin origin)
  924.         {
  925.             if (origin < SeekOrigin.Begin || origin > SeekOrigin.End)
  926.                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidSeekOrigin"));
  927.             if (_handle.IsClosed)
  928.                 __Error.FileNotOpen();
  929.             if (!CanSeek)
  930.                 __Error.SeekNotSupported();
  931.            
  932.             BCLDebug.Assert((_readPos == 0 && _readLen == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLen), "We're either reading or writing, but not both.");
  933.            
  934.             // If we've got bytes in our buffer to write, write them out.
  935.             // If we've read in and consumed some bytes, we'll have to adjust
  936.             // our seek positions ONLY IF we're seeking relative to the current
  937.             // position in the stream. This simulates doing a seek to the new
  938.             // position, then a read for the number of bytes we have in our buffer.
  939.             if (_writePos > 0) {
  940.                 FlushWrite(false);
  941.             }
  942.             else if (origin == SeekOrigin.Current) {
  943.                 // Don't call FlushRead here, which would have caused an infinite
  944.                 // loop. Simply adjust the seek origin. This isn't necessary
  945.                 // if we're seeking relative to the beginning or end of the stream.
  946.                 offset -= (_readLen - _readPos);
  947.             }
  948.            
  949.             // Verify that internal position is in sync with the handle
  950.             if (_exposedHandle)
  951.                 VerifyOSHandlePosition();
  952.            
  953.             long oldPos = _pos + (_readPos - _readLen);
  954.             long pos = SeekCore(offset, origin);
  955.            
  956.             // Prevent users from overwriting data in a file that was opened in
  957.             // append mode.
  958.             if (_appendStart != -1 && pos < _appendStart) {
  959.                 SeekCore(oldPos, SeekOrigin.Begin);
  960.                 throw new IOException(Environment.GetResourceString("IO.IO_SeekAppendOverwrite"));
  961.             }
  962.            
  963.             // We now must update the read buffer. We can in some cases simply
  964.             // update _readPos within the buffer, copy around the buffer so our
  965.             // Position property is still correct, and avoid having to do more
  966.             // reads from the disk. Otherwise, discard the buffer's contents.
  967.             if (_readLen > 0) {
  968.                 // We can optimize the following condition:
  969.                 // oldPos - _readPos <= pos < oldPos + _readLen - _readPos
  970.                 if (oldPos == pos) {
  971.                     if (_readPos > 0) {
  972.                         //Console.WriteLine("Seek: seeked for 0, adjusting buffer back by: "+_readPos+" _readLen: "+_readLen);
  973.                         Buffer.InternalBlockCopy(_buffer, _readPos, _buffer, 0, _readLen - _readPos);
  974.                         _readLen -= _readPos;
  975.                         _readPos = 0;
  976.                     }
  977.                     // If we still have buffered data, we must update the stream's
  978.                     // position so our Position property is correct.
  979.                     if (_readLen > 0)
  980.                         SeekCore(_readLen, SeekOrigin.Current);
  981.                 }
  982.                 else if (oldPos - _readPos < pos && pos < oldPos + _readLen - _readPos) {
  983.                     int diff = (int)(pos - oldPos);
  984.                     //Console.WriteLine("Seek: diff was "+diff+", readpos was "+_readPos+" adjusting buffer - shrinking by "+ (_readPos + diff));
  985.                     Buffer.InternalBlockCopy(_buffer, _readPos + diff, _buffer, 0, _readLen - (_readPos + diff));
  986.                     _readLen -= (_readPos + diff);
  987.                     _readPos = 0;
  988.                     if (_readLen > 0)
  989.                         SeekCore(_readLen, SeekOrigin.Current);
  990.                 }
  991.                 else {
  992.                     // Lose the read buffer.
  993.                     _readPos = 0;
  994.                     _readLen = 0;
  995.                 }
  996.                 BCLDebug.Assert(_readLen >= 0 && _readPos <= _readLen, "_readLen should be nonnegative, and _readPos should be less than or equal _readLen");
  997.                 BCLDebug.Assert(pos == Position, "Seek optimization: pos != Position! Buffer math was mangled.");
  998.             }
  999.             return pos;
  1000.         }
  1001.        
  1002.         // This doesn't do argument checking. Necessary for SetLength, which must
  1003.         // set the file pointer beyond the end of the file. This will update the
  1004.         // internal position
  1005.         private long SeekCore(long offset, SeekOrigin origin)
  1006.         {
  1007.             BCLDebug.Assert(!_handle.IsClosed && CanSeek, "!_handle.IsClosed && CanSeek");
  1008.             BCLDebug.Assert(origin >= SeekOrigin.Begin && origin <= SeekOrigin.End, "origin>=SeekOrigin.Begin && origin<=SeekOrigin.End");
  1009.             int hr = 0;
  1010.             long ret = 0;
  1011.            
  1012.             ret = Win32Native.SetFilePointer(_handle, offset, origin, out hr);
  1013.             if (ret == -1) {
  1014.                 // For invalid handles, detect the error and mark our handle
  1015.                 // as closed to give slightly better error messages. Also
  1016.                 // help ensure we avoid handle recycling bugs.
  1017.                 if (hr == Win32Native.ERROR_INVALID_HANDLE)
  1018.                     _handle.SetHandleAsInvalid();
  1019.                 __Error.WinIOError(hr, String.Empty);
  1020.             }
  1021.            
  1022.             _pos = ret;
  1023.             return ret;
  1024.         }
  1025.        
  1026.         // Checks the position of the OS's handle equals what we expect it to.
  1027.         // This will fail if someone else moved the FileStream's handle or if
  1028.         // we've hit a bug in FileStream's position updating code.
  1029.         private void VerifyOSHandlePosition()
  1030.         {
  1031.             if (!CanSeek)
  1032.                 return;
  1033.            
  1034.             // SeekCore will override the current _pos, so save it now
  1035.             long oldPos = _pos;
  1036.             long curPos = SeekCore(0, SeekOrigin.Current);
  1037.            
  1038.             if (curPos != oldPos) {
  1039.                 // For reads, this is non-fatal but we still could have returned corrupted
  1040.                 // data in some cases. So discard the internal buffer. Potential MDA
  1041.                 _readPos = 0;
  1042.                 _readLen = 0;
  1043.                 if (_writePos > 0) {
  1044.                     // Discard the buffer and let the user know!
  1045.                     _writePos = 0;
  1046.                     throw new IOException(Environment.GetResourceString("IO.IO_FileStreamHandlePosition"));
  1047.                 }
  1048.             }
  1049.         }
  1050.        
  1051.         public override void Write(byte[] array, int offset, int count)
  1052.         {
  1053.             if (array == null)
  1054.                 throw new ArgumentNullException("array", Environment.GetResourceString("ArgumentNull_Buffer"));
  1055.             if (offset < 0)
  1056.                 throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  1057.             if (count < 0)
  1058.                 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  1059.             if (array.Length - offset < count)
  1060.                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
  1061.            
  1062.             if (_handle.IsClosed)
  1063.                 __Error.FileNotOpen();
  1064.             if (_writePos == 0) {
  1065.                 // Ensure we can write to the stream, and ready buffer for writing.
  1066.                 if (!CanWrite)
  1067.                     __Error.WriteNotSupported();
  1068.                 if (_readPos < _readLen)
  1069.                     FlushRead();
  1070.                 _readPos = 0;
  1071.                 _readLen = 0;
  1072.             }
  1073.            
  1074.             // If our buffer has data in it, copy data from the user's array into
  1075.             // the buffer, and if we can fit it all there, return. Otherwise, write
  1076.             // the buffer to disk and copy any remaining data into our buffer.
  1077.             // The assumption here is memcpy is cheaper than disk (or net) IO.
  1078.             // (10 milliseconds to disk vs. ~20-30 microseconds for a 4K memcpy)
  1079.             // So the extra copying will reduce the total number of writes, in
  1080.             // non-pathological cases (ie, write 1 byte, then write for the buffer
  1081.             // size repeatedly)
  1082.             if (_writePos > 0) {
  1083.                 int numBytes = _bufferSize - _writePos;
  1084.                 // space left in buffer
  1085.                 if (numBytes > 0) {
  1086.                     if (numBytes > count)
  1087.                         numBytes = count;
  1088.                     Buffer.InternalBlockCopy(array, offset, _buffer, _writePos, numBytes);
  1089.                     _writePos += numBytes;
  1090.                     if (count == numBytes)
  1091.                         return;
  1092.                     offset += numBytes;
  1093.                     count -= numBytes;
  1094.                 }
  1095.                 // Reset our buffer. We essentially want to call FlushWrite
  1096.                 // without calling Flush on the underlying Stream.
  1097.                 WriteCore(_buffer, 0, _writePos);
  1098.                 _writePos = 0;
  1099.             }
  1100.             // If the buffer would slow writes down, avoid buffer completely.
  1101.             if (count >= _bufferSize) {
  1102.                 BCLDebug.Assert(_writePos == 0, "FileStream cannot have buffered data to write here! Your stream will be corrupted.");
  1103.                 WriteCore(array, offset, count);
  1104.                 return;
  1105.             }
  1106.             else if (count == 0)
  1107.                 return;
  1108.             // Don't allocate a buffer then call memcpy for 0 bytes.
  1109.             if (_buffer == null)
  1110.                 _buffer = new byte[_bufferSize];
  1111.             // Copy remaining bytes into buffer, to write at a later date.
  1112.             Buffer.InternalBlockCopy(array, offset, _buffer, _writePos, count);
  1113.             _writePos = count;
  1114.             return;
  1115.         }
  1116.        
  1117.         unsafe private void WriteCore(byte[] buffer, int offset, int count)
  1118.         {
  1119.             BCLDebug.Assert(!_handle.IsClosed, "!_handle.IsClosed");
  1120.             BCLDebug.Assert(CanWrite, "CanWrite");
  1121.            
  1122.             BCLDebug.Assert(buffer != null, "buffer != null");
  1123.             BCLDebug.Assert(_readPos == _readLen, "_readPos == _readLen");
  1124.             BCLDebug.Assert(offset >= 0, "offset is negative");
  1125.             BCLDebug.Assert(count >= 0, "count is negative");
  1126.            
  1127.             // Make sure we are writing to the position that we think we are
  1128.             if (_exposedHandle)
  1129.                 VerifyOSHandlePosition();
  1130.            
  1131.             int hr = 0;
  1132.             int r = WriteFileNative(_handle, buffer, offset, count, null, out hr);
  1133.             if (r == -1) {
  1134.                 // For pipes, ERROR_NO_DATA is not an error, but the pipe is closing.
  1135.                 if (hr == ERROR_NO_DATA) {
  1136.                     r = 0;
  1137.                 }
  1138.                 else {
  1139.                     // ERROR_INVALID_PARAMETER may be returned for writes
  1140.                     // where the position is too large (ie, writing at Int64.MaxValue
  1141.                     // on Win9x) OR for synchronous writes to a handle opened
  1142.                     // asynchronously.
  1143.                     if (hr == ERROR_INVALID_PARAMETER)
  1144.                         throw new IOException(Environment.GetResourceString("IO.IO_FileTooLongOrHandleNotSync"));
  1145.                     __Error.WinIOError(hr, String.Empty);
  1146.                 }
  1147.             }
  1148.             BCLDebug.Assert(r >= 0, "FileStream's WriteCore is likely broken.");
  1149.             _pos += r;
  1150.             return;
  1151.         }
  1152.        
  1153.         [HostProtection(ExternalThreading = true)]
  1154.         public override IAsyncResult BeginRead(byte[] array, int offset, int numBytes, AsyncCallback userCallback, object stateObject)
  1155.         {
  1156.             if (array == null)
  1157.                 throw new ArgumentNullException("array");
  1158.             if (offset < 0)
  1159.                 throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  1160.             if (numBytes < 0)
  1161.                 throw new ArgumentOutOfRangeException("numBytes", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  1162.             if (array.Length - offset < numBytes)
  1163.                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
  1164.            
  1165.             if (_handle.IsClosed)
  1166.                 __Error.FileNotOpen();
  1167.             return base.BeginRead(array, offset, numBytes, userCallback, stateObject);
  1168.         }
  1169.        
  1170.        
  1171.         unsafe public override int EndRead(IAsyncResult asyncResult)
  1172.         {
  1173.             // There are 3 significantly different IAsyncResults we'll accept
  1174.             // here. One is from Stream::BeginRead. The other two are variations
  1175.             // on our FileStreamAsyncResult. One is from BeginReadCore,
  1176.             // while the other is from the BeginRead buffering wrapper.
  1177.             if (asyncResult == null)
  1178.                 throw new ArgumentNullException("asyncResult");
  1179.            
  1180.             return base.EndRead(asyncResult);
  1181.         }
  1182.        
  1183.         // Reads a byte from the file stream. Returns the byte cast to an int
  1184.         // or -1 if reading from the end of the stream.
  1185.         public override int ReadByte()
  1186.         {
  1187.             if (_handle.IsClosed)
  1188.                 __Error.FileNotOpen();
  1189.             if (_readLen == 0 && !CanRead)
  1190.                 __Error.ReadNotSupported();
  1191.             BCLDebug.Assert((_readPos == 0 && _readLen == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLen), "We're either reading or writing, but not both.");
  1192.             if (_readPos == _readLen) {
  1193.                 if (_writePos > 0)
  1194.                     FlushWrite(false);
  1195.                 BCLDebug.Assert(_bufferSize > 0, "_bufferSize > 0");
  1196.                 if (_buffer == null)
  1197.                     _buffer = new byte[_bufferSize];
  1198.                 _readLen = ReadCore(_buffer, 0, _bufferSize);
  1199.                 _readPos = 0;
  1200.             }
  1201.             if (_readPos == _readLen)
  1202.                 return -1;
  1203.            
  1204.             int result = _buffer[_readPos];
  1205.             _readPos++;
  1206.             return result;
  1207.         }
  1208.        
  1209.        
  1210.         [HostProtection(ExternalThreading = true)]
  1211.         public override IAsyncResult BeginWrite(byte[] array, int offset, int numBytes, AsyncCallback userCallback, object stateObject)
  1212.         {
  1213.             if (array == null)
  1214.                 throw new ArgumentNullException("array");
  1215.             if (offset < 0)
  1216.                 throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  1217.             if (numBytes < 0)
  1218.                 throw new ArgumentOutOfRangeException("numBytes", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  1219.             if (array.Length - offset < numBytes)
  1220.                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
  1221.            
  1222.             if (_handle.IsClosed)
  1223.                 __Error.FileNotOpen();
  1224.            
  1225.            
  1226.             return base.BeginWrite(array, offset, numBytes, userCallback, stateObject);
  1227.         }
  1228.        
  1229.        
  1230.         unsafe public override void EndWrite(IAsyncResult asyncResult)
  1231.         {
  1232.             if (asyncResult == null)
  1233.                 throw new ArgumentNullException("asyncResult");
  1234.            
  1235.             base.EndWrite(asyncResult);
  1236.         }
  1237.        
  1238.         public override void WriteByte(byte value)
  1239.         {
  1240.             if (_handle.IsClosed)
  1241.                 __Error.FileNotOpen();
  1242.             if (_writePos == 0) {
  1243.                 if (!CanWrite)
  1244.                     __Error.WriteNotSupported();
  1245.                 if (_readPos < _readLen)
  1246.                     FlushRead();
  1247.                 _readPos = 0;
  1248.                 _readLen = 0;
  1249.                 BCLDebug.Assert(_bufferSize > 0, "_bufferSize > 0");
  1250.                 if (_buffer == null)
  1251.                     _buffer = new byte[_bufferSize];
  1252.             }
  1253.             if (_writePos == _bufferSize)
  1254.                 FlushWrite(false);
  1255.            
  1256.             _buffer[_writePos] = value;
  1257.             _writePos++;
  1258.         }
  1259.        
  1260.         public virtual void Lock(long position, long length)
  1261.         {
  1262.             if (_handle.IsClosed)
  1263.                 __Error.FileNotOpen();
  1264.             if (position < 0 || length < 0)
  1265.                 throw new ArgumentOutOfRangeException((position < 0 ? "position" : "length"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  1266.            
  1267.             int positionLow = unchecked((int)(position));
  1268.             int positionHigh = unchecked((int)(position >> 32));
  1269.             int lengthLow = unchecked((int)(length));
  1270.             int lengthHigh = unchecked((int)(length >> 32));
  1271.            
  1272.             if (!Win32Native.LockFile(_handle, positionLow, positionHigh, lengthLow, lengthHigh))
  1273.                 __Error.WinIOError();
  1274.         }
  1275.        
  1276.         public virtual void Unlock(long position, long length)
  1277.         {
  1278.             if (_handle.IsClosed)
  1279.                 __Error.FileNotOpen();
  1280.             if (position < 0 || length < 0)
  1281.                 throw new ArgumentOutOfRangeException((position < 0 ? "position" : "length"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  1282.            
  1283.             int positionLow = unchecked((int)(position));
  1284.             int positionHigh = unchecked((int)(position >> 32));
  1285.             int lengthLow = unchecked((int)(length));
  1286.             int lengthHigh = unchecked((int)(length >> 32));
  1287.            
  1288.             if (!Win32Native.UnlockFile(_handle, positionLow, positionHigh, lengthLow, lengthHigh))
  1289.                 __Error.WinIOError();
  1290.         }
  1291.        
  1292.         // Windows API definitions, from winbase.h and others
  1293.        
  1294.         private const int FILE_ATTRIBUTE_NORMAL = 128;
  1295.         private const int FILE_ATTRIBUTE_ENCRYPTED = 16384;
  1296.         private const int FILE_FLAG_OVERLAPPED = 1073741824;
  1297.         internal const int GENERIC_READ = unchecked((int)2147483648u);
  1298.         private const int GENERIC_WRITE = 1073741824;
  1299.        
  1300.         private const int FILE_BEGIN = 0;
  1301.         private const int FILE_CURRENT = 1;
  1302.         private const int FILE_END = 2;
  1303.        
  1304.         // Error codes (not HRESULTS), from winerror.h
  1305.         private const int ERROR_BROKEN_PIPE = 109;
  1306.         private const int ERROR_NO_DATA = 232;
  1307.         private const int ERROR_HANDLE_EOF = 38;
  1308.         private const int ERROR_INVALID_PARAMETER = 87;
  1309.         private const int ERROR_IO_PENDING = 997;
  1310.        
  1311.        
  1312.         // __ConsoleStream also uses this code.
  1313.         unsafe private int ReadFileNative(SafeFileHandle handle, byte[] bytes, int offset, int count, NativeOverlapped* overlapped, out int hr)
  1314.         {
  1315.             BCLDebug.Assert(handle != null, "handle != null");
  1316.             BCLDebug.Assert(offset >= 0, "offset >= 0");
  1317.             BCLDebug.Assert(count >= 0, "count >= 0");
  1318.             BCLDebug.Assert(bytes != null, "bytes != null");
  1319.            
  1320.             BCLDebug.Assert((_isAsync && overlapped != null) || (!_isAsync && overlapped == null), "Async IO parameter screwup in call to ReadFileNative.");
  1321.            
  1322.             // Don't corrupt memory when multiple threads are erroneously writing
  1323.             // to this stream simultaneously.
  1324.             if (bytes.Length - offset < count)
  1325.                 throw new IndexOutOfRangeException(Environment.GetResourceString("IndexOutOfRange_IORaceCondition"));
  1326.            
  1327.             // You can't use the fixed statement on an array of length 0.
  1328.             if (bytes.Length == 0) {
  1329.                 hr = 0;
  1330.                 return 0;
  1331.             }
  1332.            
  1333.             int r = 0;
  1334.             int numBytesRead = 0;
  1335.            
  1336.             fixed (byte* p = bytes) {
  1337.                 if (_isAsync)
  1338.                     r = Win32Native.ReadFile(handle, p + offset, count, IntPtr.Zero, overlapped);
  1339.                 else
  1340.                     r = Win32Native.ReadFile(handle, p + offset, count, out numBytesRead, IntPtr.Zero);
  1341.             }
  1342.            
  1343.             if (r == 0) {
  1344.                 hr = Marshal.GetLastWin32Error();
  1345.                 // We should never silently swallow an error here without some
  1346.                 // extra work. We must make sure that BeginReadCore won't return an
  1347.                 // IAsyncResult that will cause EndRead to block, since the OS won't
  1348.                 // call AsyncFSCallback for us.
  1349.                 if (hr == ERROR_BROKEN_PIPE || hr == Win32Native.ERROR_PIPE_NOT_CONNECTED) {
  1350.                     // This handle was a pipe, and it's done. Not an error, but EOF.
  1351.                     // However, the OS will not call AsyncFSCallback!
  1352.                     // Let the caller handle this, since BeginReadCore & ReadCore
  1353.                     // need to do different things.
  1354.                     return -1;
  1355.                 }
  1356.                
  1357.                 // For invalid handles, detect the error and mark our handle
  1358.                 // as closed to give slightly better error messages. Also
  1359.                 // help ensure we avoid handle recycling bugs.
  1360.                 if (hr == Win32Native.ERROR_INVALID_HANDLE)
  1361.                     _handle.SetHandleAsInvalid();
  1362.                
  1363.                 return -1;
  1364.             }
  1365.             else
  1366.                 hr = 0;
  1367.             return numBytesRead;
  1368.         }
  1369.        
  1370.         unsafe private int WriteFileNative(SafeFileHandle handle, byte[] bytes, int offset, int count, NativeOverlapped* overlapped, out int hr)
  1371.         {
  1372.             BCLDebug.Assert(handle != null, "handle != null");
  1373.             BCLDebug.Assert(offset >= 0, "offset >= 0");
  1374.             BCLDebug.Assert(count >= 0, "count >= 0");
  1375.             BCLDebug.Assert(bytes != null, "bytes != null");
  1376.            
  1377.             BCLDebug.Assert((_isAsync && overlapped != null) || (!_isAsync && overlapped == null), "Async IO parameter screwup in call to WriteFileNative.");
  1378.            
  1379.             // Don't corrupt memory when multiple threads are erroneously writing
  1380.             // to this stream simultaneously. (the OS is reading from
  1381.             // the array we pass to WriteFile, but if we read beyond the end and
  1382.             // that memory isn't allocated, we could get an AV.)
  1383.             if (bytes.Length - offset < count)
  1384.                 throw new IndexOutOfRangeException(Environment.GetResourceString("IndexOutOfRange_IORaceCondition"));
  1385.            
  1386.             // You can't use the fixed statement on an array of length 0.
  1387.             if (bytes.Length == 0) {
  1388.                 hr = 0;
  1389.                 return 0;
  1390.             }
  1391.            
  1392.             int numBytesWritten = 0;
  1393.             int r = 0;
  1394.            
  1395.             fixed (byte* p = bytes) {
  1396.                 if (_isAsync)
  1397.                     r = Win32Native.WriteFile(handle, p + offset, count, IntPtr.Zero, overlapped);
  1398.                 else
  1399.                     r = Win32Native.WriteFile(handle, p + offset, count, out numBytesWritten, IntPtr.Zero);
  1400.             }
  1401.            
  1402.             if (r == 0) {
  1403.                 hr = Marshal.GetLastWin32Error();
  1404.                 // We should never silently swallow an error here without some
  1405.                 // extra work. We must make sure that BeginWriteCore won't return an
  1406.                 // IAsyncResult that will cause EndWrite to block, since the OS won't
  1407.                 // call AsyncFSCallback for us.
  1408.                
  1409.                 if (hr == ERROR_NO_DATA) {
  1410.                     // This handle was a pipe, and the pipe is being closed on the
  1411.                     // other side. Let the caller handle this, since BeginWriteCore
  1412.                     // & WriteCore need to do different things.
  1413.                     return -1;
  1414.                 }
  1415.                
  1416.                 // For invalid handles, detect the error and mark our handle
  1417.                 // as closed to give slightly better error messages. Also
  1418.                 // help ensure we avoid handle recycling bugs.
  1419.                 if (hr == Win32Native.ERROR_INVALID_HANDLE)
  1420.                     _handle.SetHandleAsInvalid();
  1421.                
  1422.                 return -1;
  1423.             }
  1424.             else
  1425.                 hr = 0;
  1426.             return numBytesWritten;
  1427.         }
  1428.     }
  1429. }

Developer Fusion