PageRenderTime 53ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/src/System.IO.FileSystem/src/System/IO/Win32FileStream.cs

https://gitlab.com/0072016/0072016-corefx-
C# | 1125 lines | 759 code | 131 blank | 235 comment | 247 complexity | c93a05b6756ce9453aabce6ea2783b2b MD5 | raw file
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. using System.Diagnostics;
  5. using System.Diagnostics.Contracts;
  6. using System.Runtime.InteropServices;
  7. using System.Threading;
  8. using System.Threading.Tasks;
  9. using Microsoft.Win32.SafeHandles;
  10. /*
  11. * Win32FileStream supports different modes of accessing the disk - async mode
  12. * and sync mode. They are two completely different codepaths in the
  13. * sync & async methods (i.e. Read/Write vs. ReadAsync/WriteAsync). File
  14. * handles in NT can be opened in only sync or overlapped (async) mode,
  15. * and we have to deal with this pain. Stream has implementations of
  16. * the sync methods in terms of the async ones, so we'll
  17. * call through to our base class to get those methods when necessary.
  18. *
  19. * Also buffering is added into Win32FileStream as well. Folded in the
  20. * code from BufferedStream, so all the comments about it being mostly
  21. * aggressive (and the possible perf improvement) apply to Win32FileStream as
  22. * well. Also added some buffering to the async code paths.
  23. *
  24. * Class Invariants:
  25. * The class has one buffer, shared for reading & writing. It can only be
  26. * used for one or the other at any point in time - not both. The following
  27. * should be true:
  28. * 0 <= _readPos <= _readLen < _bufferSize
  29. * 0 <= _writePos < _bufferSize
  30. * _readPos == _readLen && _readPos > 0 implies the read buffer is valid,
  31. * but we're at the end of the buffer.
  32. * _readPos == _readLen == 0 means the read buffer contains garbage.
  33. * Either _writePos can be greater than 0, or _readLen & _readPos can be
  34. * greater than zero, but neither can be greater than zero at the same time.
  35. *
  36. */
  37. namespace System.IO
  38. {
  39. internal sealed partial class Win32FileStream : FileStreamBase
  40. {
  41. internal const int DefaultBufferSize = 4096;
  42. internal const bool DefaultUseAsync = true;
  43. internal const bool DefaultIsAsync = false;
  44. private byte[] _buffer; // Shared read/write buffer. Alloc on first use.
  45. private String _fileName; // Fully qualified file name.
  46. private bool _isAsync; // Whether we opened the handle for overlapped IO
  47. private bool _canRead;
  48. private bool _canWrite;
  49. private bool _canSeek;
  50. private bool _exposedHandle; // Could other code be using this handle?
  51. private bool _isPipe; // Whether to disable async buffering code.
  52. private int _readPos; // Read pointer within shared buffer.
  53. private int _readLen; // Number of bytes read in buffer from file.
  54. private int _writePos; // Write pointer within shared buffer.
  55. private int _bufferSize; // Length of internal buffer, if it's allocated.
  56. private SafeFileHandle _handle;
  57. private long _pos; // Cache current location in the file.
  58. private long _appendStart;// When appending, prevent overwriting file.
  59. private static unsafe IOCompletionCallback s_ioCallback = FileStreamCompletionSource.IOCallback;
  60. private Task<int> _lastSynchronouslyCompletedTask = null; // cached task for read ops that complete synchronously
  61. private Task _activeBufferOperation = null; // tracks in-progress async ops using the buffer
  62. private PreAllocatedOverlapped _preallocatedOverlapped; // optimization for async ops to avoid per-op allocations
  63. private FileStreamCompletionSource _currentOverlappedOwner; // async op currently using the preallocated overlapped
  64. [System.Security.SecuritySafeCritical]
  65. public Win32FileStream(String path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options, FileStream parent) : base(parent)
  66. {
  67. Interop.mincore.SECURITY_ATTRIBUTES secAttrs = GetSecAttrs(share);
  68. _exposedHandle = false;
  69. int fAccess =
  70. ((access & FileAccess.Read) == FileAccess.Read ? GENERIC_READ : 0) |
  71. ((access & FileAccess.Write) == FileAccess.Write ? GENERIC_WRITE : 0);
  72. _fileName = path;
  73. // Our Inheritable bit was stolen from Windows, but should be set in
  74. // the security attributes class. Don't leave this bit set.
  75. share &= ~FileShare.Inheritable;
  76. bool seekToEnd = (mode == FileMode.Append);
  77. // Must use a valid Win32 constant here...
  78. if (mode == FileMode.Append)
  79. mode = FileMode.OpenOrCreate;
  80. if ((options & FileOptions.Asynchronous) != 0)
  81. _isAsync = true;
  82. int flagsAndAttributes = (int)options;
  83. // For mitigating local elevation of privilege attack through named pipes
  84. // make sure we always call CreateFile with SECURITY_ANONYMOUS so that the
  85. // named pipe server can't impersonate a high privileged client security context
  86. flagsAndAttributes |= (Interop.mincore.SecurityOptions.SECURITY_SQOS_PRESENT | Interop.mincore.SecurityOptions.SECURITY_ANONYMOUS);
  87. // Don't pop up a dialog for reading from an empty floppy drive
  88. uint oldMode = Interop.mincore.SetErrorMode(Interop.mincore.SEM_FAILCRITICALERRORS);
  89. try
  90. {
  91. _handle = Interop.mincore.SafeCreateFile(path, fAccess, share, ref secAttrs, mode, flagsAndAttributes, IntPtr.Zero);
  92. _handle.IsAsync = _isAsync;
  93. if (_handle.IsInvalid)
  94. {
  95. // Return a meaningful exception with the full path.
  96. // NT5 oddity - when trying to open "C:\" as a Win32FileStream,
  97. // we usually get ERROR_PATH_NOT_FOUND from the OS. We should
  98. // probably be consistent w/ every other directory.
  99. int errorCode = Marshal.GetLastWin32Error();
  100. if (errorCode == Interop.mincore.Errors.ERROR_PATH_NOT_FOUND && path.Equals(Directory.InternalGetDirectoryRoot(path)))
  101. errorCode = Interop.mincore.Errors.ERROR_ACCESS_DENIED;
  102. throw Win32Marshal.GetExceptionForWin32Error(errorCode, _fileName);
  103. }
  104. }
  105. finally
  106. {
  107. Interop.mincore.SetErrorMode(oldMode);
  108. }
  109. // Disallow access to all non-file devices from the Win32FileStream
  110. // constructors that take a String. Everyone else can call
  111. // CreateFile themselves then use the constructor that takes an
  112. // IntPtr. Disallows "con:", "com1:", "lpt1:", etc.
  113. int fileType = Interop.mincore.GetFileType(_handle);
  114. if (fileType != Interop.mincore.FileTypes.FILE_TYPE_DISK)
  115. {
  116. _handle.Dispose();
  117. throw new NotSupportedException(SR.NotSupported_FileStreamOnNonFiles);
  118. }
  119. // This is necessary for async IO using IO Completion ports via our
  120. // managed Threadpool API's. This (theoretically) calls the OS's
  121. // BindIoCompletionCallback method, and passes in a stub for the
  122. // LPOVERLAPPED_COMPLETION_ROUTINE. This stub looks at the Overlapped
  123. // struct for this request and gets a delegate to a managed callback
  124. // from there, which it then calls on a threadpool thread. (We allocate
  125. // our native OVERLAPPED structs 2 pointers too large and store EE state
  126. // & GC handles there, one to an IAsyncResult, the other to a delegate.)
  127. if (_isAsync)
  128. {
  129. try
  130. {
  131. _handle.ThreadPoolBinding = ThreadPoolBoundHandle.BindHandle(_handle);
  132. }
  133. catch (ArgumentException ex)
  134. {
  135. throw new IOException(SR.IO_BindHandleFailed, ex);
  136. }
  137. finally
  138. {
  139. if (_handle.ThreadPoolBinding == null)
  140. {
  141. // We should close the handle so that the handle is not open until SafeFileHandle GC
  142. Debug.Assert(!_exposedHandle, "Are we closing handle that we exposed/not own, how?");
  143. _handle.Dispose();
  144. }
  145. }
  146. }
  147. _canRead = (access & FileAccess.Read) != 0;
  148. _canWrite = (access & FileAccess.Write) != 0;
  149. _canSeek = true;
  150. _isPipe = false;
  151. _pos = 0;
  152. _bufferSize = bufferSize;
  153. _readPos = 0;
  154. _readLen = 0;
  155. _writePos = 0;
  156. // For Append mode...
  157. if (seekToEnd)
  158. {
  159. _appendStart = SeekCore(0, SeekOrigin.End);
  160. }
  161. else
  162. {
  163. _appendStart = -1;
  164. }
  165. }
  166. [System.Security.SecuritySafeCritical] // auto-generated
  167. public Win32FileStream(SafeFileHandle handle, FileAccess access, FileStream parent)
  168. : this(handle, access, DefaultBufferSize, GetDefaultIsAsync(handle), GetSuppressBindHandle(handle), parent)
  169. {
  170. }
  171. [System.Security.SecuritySafeCritical] // auto-generated
  172. public Win32FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, FileStream parent)
  173. : this(handle, access, bufferSize, GetDefaultIsAsync(handle), GetSuppressBindHandle(handle), parent)
  174. {
  175. }
  176. [System.Security.SecuritySafeCritical] // auto-generated
  177. public Win32FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync, FileStream parent)
  178. : this(handle, access, bufferSize, isAsync, GetSuppressBindHandle(handle), parent)
  179. {
  180. }
  181. private Win32FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync, bool suppressBindHandle, FileStream parent)
  182. : base(parent)
  183. {
  184. // To ensure we don't leak a handle, put it in a SafeFileHandle first
  185. if (handle.IsInvalid)
  186. throw new ArgumentException(SR.Arg_InvalidHandle, nameof(handle));
  187. Contract.EndContractBlock();
  188. _handle = handle;
  189. _exposedHandle = true;
  190. // Now validate arguments.
  191. if (access < FileAccess.Read || access > FileAccess.ReadWrite)
  192. throw new ArgumentOutOfRangeException(nameof(access), SR.ArgumentOutOfRange_Enum);
  193. if (bufferSize <= 0)
  194. throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum);
  195. int handleType = Interop.mincore.GetFileType(_handle);
  196. Debug.Assert(handleType == Interop.mincore.FileTypes.FILE_TYPE_DISK || handleType == Interop.mincore.FileTypes.FILE_TYPE_PIPE || handleType == Interop.mincore.FileTypes.FILE_TYPE_CHAR, "FileStream was passed an unknown file type!");
  197. _isAsync = isAsync;
  198. _canRead = 0 != (access & FileAccess.Read);
  199. _canWrite = 0 != (access & FileAccess.Write);
  200. _canSeek = handleType == Interop.mincore.FileTypes.FILE_TYPE_DISK;
  201. _bufferSize = bufferSize;
  202. _readPos = 0;
  203. _readLen = 0;
  204. _writePos = 0;
  205. _fileName = null;
  206. _isPipe = handleType == Interop.mincore.FileTypes.FILE_TYPE_PIPE;
  207. // This is necessary for async IO using IO Completion ports via our
  208. // managed Threadpool API's. This calls the OS's
  209. // BindIoCompletionCallback method, and passes in a stub for the
  210. // LPOVERLAPPED_COMPLETION_ROUTINE. This stub looks at the Overlapped
  211. // struct for this request and gets a delegate to a managed callback
  212. // from there, which it then calls on a threadpool thread. (We allocate
  213. // our native OVERLAPPED structs 2 pointers too large and store EE
  214. // state & a handle to a delegate there.)
  215. //
  216. // If, however, we've already bound this file handle to our completion port,
  217. // don't try to bind it again because it will fail. A handle can only be
  218. // bound to a single completion port at a time.
  219. if (_isAsync && !suppressBindHandle)
  220. {
  221. try
  222. {
  223. _handle.ThreadPoolBinding = ThreadPoolBoundHandle.BindHandle(_handle);
  224. }
  225. catch (Exception ex)
  226. {
  227. // If you passed in a synchronous handle and told us to use
  228. // it asynchronously, throw here.
  229. throw new ArgumentException(SR.Arg_HandleNotAsync, nameof(handle), ex);
  230. }
  231. }
  232. else if (!_isAsync)
  233. {
  234. if (handleType != Interop.mincore.FileTypes.FILE_TYPE_PIPE)
  235. VerifyHandleIsSync();
  236. }
  237. if (_canSeek)
  238. SeekCore(0, SeekOrigin.Current);
  239. else
  240. _pos = 0;
  241. }
  242. private static bool GetDefaultIsAsync(SafeFileHandle handle)
  243. {
  244. return handle.IsAsync.HasValue ? handle.IsAsync.Value : DefaultIsAsync;
  245. }
  246. private static bool GetSuppressBindHandle(SafeFileHandle handle)
  247. {
  248. return handle.IsAsync.HasValue ? handle.IsAsync.Value : false;
  249. }
  250. [System.Security.SecuritySafeCritical] // auto-generated
  251. private unsafe static Interop.mincore.SECURITY_ATTRIBUTES GetSecAttrs(FileShare share)
  252. {
  253. Interop.mincore.SECURITY_ATTRIBUTES secAttrs = default(Interop.mincore.SECURITY_ATTRIBUTES);
  254. if ((share & FileShare.Inheritable) != 0)
  255. {
  256. secAttrs = new Interop.mincore.SECURITY_ATTRIBUTES();
  257. secAttrs.nLength = (uint)sizeof(Interop.mincore.SECURITY_ATTRIBUTES);
  258. secAttrs.bInheritHandle = Interop.BOOL.TRUE;
  259. }
  260. return secAttrs;
  261. }
  262. // Verifies that this handle supports synchronous IO operations (unless you
  263. // didn't open it for either reading or writing).
  264. [System.Security.SecuritySafeCritical] // auto-generated
  265. private unsafe void VerifyHandleIsSync()
  266. {
  267. Debug.Assert(!_isAsync);
  268. // Do NOT use this method on pipes. Reading or writing to a pipe may
  269. // cause an app to block incorrectly, introducing a deadlock (depending
  270. // on whether a write will wake up an already-blocked thread or this
  271. // Win32FileStream's thread).
  272. Debug.Assert(Interop.mincore.GetFileType(_handle) != Interop.mincore.FileTypes.FILE_TYPE_PIPE);
  273. byte* bytes = stackalloc byte[1];
  274. int numBytesReadWritten;
  275. int r = -1;
  276. // If the handle is a pipe, ReadFile will block until there
  277. // has been a write on the other end. We'll just have to deal with it,
  278. // For the read end of a pipe, you can mess up and
  279. // accidentally read synchronously from an async pipe.
  280. if (_canRead)
  281. r = Interop.mincore.ReadFile(_handle, bytes, 0, out numBytesReadWritten, IntPtr.Zero);
  282. else if (_canWrite)
  283. r = Interop.mincore.WriteFile(_handle, bytes, 0, out numBytesReadWritten, IntPtr.Zero);
  284. if (r == 0)
  285. {
  286. int errorCode = GetLastWin32ErrorAndDisposeHandleIfInvalid(throwIfInvalidHandle: true);
  287. if (errorCode == ERROR_INVALID_PARAMETER)
  288. throw new ArgumentException(SR.Arg_HandleNotSync, "handle");
  289. }
  290. }
  291. private bool HasActiveBufferOperation
  292. {
  293. get { return _activeBufferOperation != null && !_activeBufferOperation.IsCompleted; }
  294. }
  295. public override bool CanRead
  296. {
  297. [Pure]
  298. get
  299. { return _canRead; }
  300. }
  301. public override bool CanWrite
  302. {
  303. [Pure]
  304. get
  305. { return _canWrite; }
  306. }
  307. public override bool CanSeek
  308. {
  309. [Pure]
  310. get
  311. { return _canSeek; }
  312. }
  313. public override bool IsAsync
  314. {
  315. get { return _isAsync; }
  316. }
  317. public override long Length
  318. {
  319. [System.Security.SecuritySafeCritical] // auto-generated
  320. get
  321. {
  322. if (_handle.IsClosed) throw Error.GetFileNotOpen();
  323. if (!_parent.CanSeek) throw Error.GetSeekNotSupported();
  324. Interop.mincore.FILE_STANDARD_INFO info = new Interop.mincore.FILE_STANDARD_INFO();
  325. if (!Interop.mincore.GetFileInformationByHandleEx(_handle, Interop.mincore.FILE_INFO_BY_HANDLE_CLASS.FileStandardInfo, out info, (uint)Marshal.SizeOf<Interop.mincore.FILE_STANDARD_INFO>()))
  326. throw Win32Marshal.GetExceptionForLastWin32Error();
  327. long len = info.EndOfFile;
  328. // If we're writing near the end of the file, we must include our
  329. // internal buffer in our Length calculation. Don't flush because
  330. // we use the length of the file in our async write method.
  331. if (_writePos > 0 && _pos + _writePos > len)
  332. len = _writePos + _pos;
  333. return len;
  334. }
  335. }
  336. public override String Name
  337. {
  338. [System.Security.SecuritySafeCritical]
  339. get
  340. {
  341. if (_fileName == null)
  342. return SR.IO_UnknownFileName;
  343. return _fileName;
  344. }
  345. }
  346. public override long Position
  347. {
  348. [System.Security.SecuritySafeCritical] // auto-generated
  349. get
  350. {
  351. if (_handle.IsClosed) throw Error.GetFileNotOpen();
  352. if (!_parent.CanSeek) throw Error.GetSeekNotSupported();
  353. Debug.Assert((_readPos == 0 && _readLen == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLen), "We're either reading or writing, but not both.");
  354. // Verify that internal position is in sync with the handle
  355. if (_exposedHandle)
  356. VerifyOSHandlePosition();
  357. // Compensate for buffer that we read from the handle (_readLen) Vs what the user
  358. // read so far from the internal buffer (_readPos). Of course add any unwritten
  359. // buffered data
  360. return _pos + (_readPos - _readLen + _writePos);
  361. }
  362. set
  363. {
  364. if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum);
  365. Contract.EndContractBlock();
  366. if (_writePos > 0) FlushWrite(false);
  367. _readPos = 0;
  368. _readLen = 0;
  369. _parent.Seek(value, SeekOrigin.Begin);
  370. }
  371. }
  372. [System.Security.SecuritySafeCritical] // auto-generated
  373. protected override void Dispose(bool disposing)
  374. {
  375. // Nothing will be done differently based on whether we are
  376. // disposing vs. finalizing. This is taking advantage of the
  377. // weak ordering between normal finalizable objects & critical
  378. // finalizable objects, which I included in the SafeHandle
  379. // design for Win32FileStream, which would often "just work" when
  380. // finalized.
  381. try
  382. {
  383. if (_handle != null && !_handle.IsClosed)
  384. {
  385. // Flush data to disk iff we were writing. After
  386. // thinking about this, we also don't need to flush
  387. // our read position, regardless of whether the handle
  388. // was exposed to the user. They probably would NOT
  389. // want us to do this.
  390. if (_writePos > 0)
  391. {
  392. FlushWrite(!disposing);
  393. }
  394. }
  395. }
  396. finally
  397. {
  398. if (_handle != null && !_handle.IsClosed)
  399. _handle.Dispose();
  400. if (_preallocatedOverlapped != null)
  401. _preallocatedOverlapped.Dispose();
  402. if (_handle.ThreadPoolBinding != null)
  403. _handle.ThreadPoolBinding.Dispose();
  404. _canRead = false;
  405. _canWrite = false;
  406. _canSeek = false;
  407. // Don't set the buffer to null, to avoid a NullReferenceException
  408. // when users have a race condition in their code (i.e. they call
  409. // Close when calling another method on Stream like Read).
  410. //_buffer = null;
  411. base.Dispose(disposing);
  412. }
  413. }
  414. public override void Flush()
  415. {
  416. // Make sure that we call through the public virtual API
  417. _parent.Flush(false);
  418. }
  419. [System.Security.SecuritySafeCritical]
  420. public override void Flush(Boolean flushToDisk)
  421. {
  422. // This code is duplicated in _parent.Dispose
  423. if (_handle.IsClosed) throw Error.GetFileNotOpen();
  424. FlushInternalBuffer();
  425. if (flushToDisk && _parent.CanWrite)
  426. {
  427. FlushOSBuffer();
  428. }
  429. }
  430. private void FlushInternalBuffer()
  431. {
  432. if (_writePos > 0)
  433. {
  434. FlushWrite(false);
  435. }
  436. else if (_readPos < _readLen && _parent.CanSeek)
  437. {
  438. FlushRead();
  439. }
  440. }
  441. [System.Security.SecuritySafeCritical]
  442. private void FlushOSBuffer()
  443. {
  444. if (!Interop.mincore.FlushFileBuffers(_handle))
  445. {
  446. throw Win32Marshal.GetExceptionForLastWin32Error();
  447. }
  448. }
  449. // Reading is done by blocks from the file, but someone could read
  450. // 1 byte from the buffer then write. At that point, the OS's file
  451. // pointer is out of sync with the stream's position. All write
  452. // functions should call this function to preserve the position in the file.
  453. private void FlushRead()
  454. {
  455. Debug.Assert(_writePos == 0, "FileStream: Write buffer must be empty in FlushRead!");
  456. if (_readPos - _readLen != 0)
  457. {
  458. Debug.Assert(_parent.CanSeek, "FileStream will lose buffered read data now.");
  459. SeekCore(_readPos - _readLen, SeekOrigin.Current);
  460. }
  461. _readPos = 0;
  462. _readLen = 0;
  463. }
  464. // Returns a task that flushes the internal write buffer
  465. private Task FlushWriteAsync(CancellationToken cancellationToken)
  466. {
  467. Debug.Assert(_isAsync);
  468. Debug.Assert(_readPos == 0 && _readLen == 0, "FileStream: Read buffer must be empty in FlushWriteAsync!");
  469. // If the buffer is already flushed, don't spin up the OS write
  470. if (_writePos == 0) return Task.CompletedTask;
  471. Task flushTask = WriteInternalCoreAsync(_buffer, 0, _writePos, cancellationToken);
  472. _writePos = 0;
  473. // Update the active buffer operation
  474. _activeBufferOperation = HasActiveBufferOperation ?
  475. Task.WhenAll(_activeBufferOperation, flushTask) :
  476. flushTask;
  477. return flushTask;
  478. }
  479. // Writes are buffered. Anytime the buffer fills up
  480. // (_writePos + delta > _bufferSize) or the buffer switches to reading
  481. // and there is left over data (_writePos > 0), this function must be called.
  482. private void FlushWrite(bool calledFromFinalizer)
  483. {
  484. Debug.Assert(_readPos == 0 && _readLen == 0, "FileStream: Read buffer must be empty in FlushWrite!");
  485. if (_isAsync)
  486. {
  487. Task writeTask = FlushWriteAsync(CancellationToken.None);
  488. // With our Whidbey async IO & overlapped support for AD unloads,
  489. // we don't strictly need to block here to release resources
  490. // since that support takes care of the pinning & freeing the
  491. // overlapped struct. We need to do this when called from
  492. // Close so that the handle is closed when Close returns, but
  493. // we don't need to call EndWrite from the finalizer.
  494. // Additionally, if we do call EndWrite, we block forever
  495. // because AD unloads prevent us from running the managed
  496. // callback from the IO completion port. Blocking here when
  497. // called from the finalizer during AD unload is clearly wrong,
  498. // but we can't use any sort of test for whether the AD is
  499. // unloading because if we weren't unloading, an AD unload
  500. // could happen on a separate thread before we call EndWrite.
  501. if (!calledFromFinalizer)
  502. {
  503. writeTask.GetAwaiter().GetResult();
  504. }
  505. }
  506. else
  507. {
  508. WriteCore(_buffer, 0, _writePos);
  509. }
  510. _writePos = 0;
  511. }
  512. public override SafeFileHandle SafeFileHandle
  513. {
  514. [System.Security.SecurityCritical] // auto-generated_required
  515. get
  516. {
  517. _parent.Flush();
  518. // Explicitly dump any buffered data, since the user could move our
  519. // position or write to the file.
  520. _readPos = 0;
  521. _readLen = 0;
  522. _writePos = 0;
  523. _exposedHandle = true;
  524. return _handle;
  525. }
  526. }
  527. [System.Security.SecuritySafeCritical] // auto-generated
  528. public override void SetLength(long value)
  529. {
  530. if (value < 0)
  531. throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum);
  532. Contract.EndContractBlock();
  533. if (_handle.IsClosed) throw Error.GetFileNotOpen();
  534. if (!_parent.CanSeek) throw Error.GetSeekNotSupported();
  535. if (!_parent.CanWrite) throw Error.GetWriteNotSupported();
  536. // Handle buffering updates.
  537. if (_writePos > 0)
  538. {
  539. FlushWrite(false);
  540. }
  541. else if (_readPos < _readLen)
  542. {
  543. FlushRead();
  544. }
  545. _readPos = 0;
  546. _readLen = 0;
  547. if (_appendStart != -1 && value < _appendStart)
  548. throw new IOException(SR.IO_SetLengthAppendTruncate);
  549. SetLengthCore(value);
  550. }
  551. // We absolutely need this method broken out so that WriteInternalCoreAsync can call
  552. // a method without having to go through buffering code that might call FlushWrite.
  553. [System.Security.SecuritySafeCritical] // auto-generated
  554. private void SetLengthCore(long value)
  555. {
  556. Debug.Assert(value >= 0, "value >= 0");
  557. long origPos = _pos;
  558. if (_exposedHandle)
  559. VerifyOSHandlePosition();
  560. if (_pos != value)
  561. SeekCore(value, SeekOrigin.Begin);
  562. if (!Interop.mincore.SetEndOfFile(_handle))
  563. {
  564. int errorCode = Marshal.GetLastWin32Error();
  565. if (errorCode == Interop.mincore.Errors.ERROR_INVALID_PARAMETER)
  566. throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_FileLengthTooBig);
  567. throw Win32Marshal.GetExceptionForWin32Error(errorCode);
  568. }
  569. // Return file pointer to where it was before setting length
  570. if (origPos != value)
  571. {
  572. if (origPos < value)
  573. SeekCore(origPos, SeekOrigin.Begin);
  574. else
  575. SeekCore(0, SeekOrigin.End);
  576. }
  577. }
  578. [System.Security.SecuritySafeCritical] // auto-generated
  579. public override int Read([In, Out] byte[] array, int offset, int count)
  580. {
  581. if (array == null)
  582. throw new ArgumentNullException(nameof(array), SR.ArgumentNull_Buffer);
  583. if (offset < 0)
  584. throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
  585. if (count < 0)
  586. throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
  587. if (array.Length - offset < count)
  588. throw new ArgumentException(SR.Argument_InvalidOffLen /*, no good single parameter name to pass*/);
  589. Contract.EndContractBlock();
  590. if (_handle.IsClosed) throw Error.GetFileNotOpen();
  591. Debug.Assert((_readPos == 0 && _readLen == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLen), "We're either reading or writing, but not both.");
  592. bool isBlocked = false;
  593. int n = _readLen - _readPos;
  594. // if the read buffer is empty, read into either user's array or our
  595. // buffer, depending on number of bytes user asked for and buffer size.
  596. if (n == 0)
  597. {
  598. if (!_parent.CanRead) throw Error.GetReadNotSupported();
  599. if (_writePos > 0) FlushWrite(false);
  600. if (!_parent.CanSeek || (count >= _bufferSize))
  601. {
  602. n = ReadCore(array, offset, count);
  603. // Throw away read buffer.
  604. _readPos = 0;
  605. _readLen = 0;
  606. return n;
  607. }
  608. EnsureBufferAllocated();
  609. n = ReadCore(_buffer, 0, _bufferSize);
  610. if (n == 0) return 0;
  611. isBlocked = n < _bufferSize;
  612. _readPos = 0;
  613. _readLen = n;
  614. }
  615. // Now copy min of count or numBytesAvailable (i.e. near EOF) to array.
  616. if (n > count) n = count;
  617. Buffer.BlockCopy(_buffer, _readPos, array, offset, n);
  618. _readPos += n;
  619. // We may have read less than the number of bytes the user asked
  620. // for, but that is part of the Stream contract. Reading again for
  621. // more data may cause us to block if we're using a device with
  622. // no clear end of file, such as a serial port or pipe. If we
  623. // blocked here & this code was used with redirected pipes for a
  624. // process's standard output, this can lead to deadlocks involving
  625. // two processes. But leave this here for files to avoid what would
  626. // probably be a breaking change. --
  627. // If we are reading from a device with no clear EOF like a
  628. // serial port or a pipe, this will cause us to block incorrectly.
  629. if (!_isPipe)
  630. {
  631. // If we hit the end of the buffer and didn't have enough bytes, we must
  632. // read some more from the underlying stream. However, if we got
  633. // fewer bytes from the underlying stream than we asked for (i.e. we're
  634. // probably blocked), don't ask for more bytes.
  635. if (n < count && !isBlocked)
  636. {
  637. Debug.Assert(_readPos == _readLen, "Read buffer should be empty!");
  638. int moreBytesRead = ReadCore(array, offset + n, count - n);
  639. n += moreBytesRead;
  640. // We've just made our buffer inconsistent with our position
  641. // pointer. We must throw away the read buffer.
  642. _readPos = 0;
  643. _readLen = 0;
  644. }
  645. }
  646. return n;
  647. }
  648. [System.Security.SecuritySafeCritical] // auto-generated
  649. private unsafe int ReadCore(byte[] buffer, int offset, int count)
  650. {
  651. Debug.Assert(!_handle.IsClosed, "!_handle.IsClosed");
  652. Debug.Assert(_parent.CanRead, "_parent.CanRead");
  653. Debug.Assert(buffer != null, "buffer != null");
  654. Debug.Assert(_writePos == 0, "_writePos == 0");
  655. Debug.Assert(offset >= 0, "offset is negative");
  656. Debug.Assert(count >= 0, "count is negative");
  657. if (_isAsync)
  658. {
  659. return ReadInternalCoreAsync(buffer, offset, count, 0, CancellationToken.None).GetAwaiter().GetResult();
  660. }
  661. // Make sure we are reading from the right spot
  662. if (_exposedHandle)
  663. VerifyOSHandlePosition();
  664. int errorCode = 0;
  665. int r = ReadFileNative(_handle, buffer, offset, count, null, out errorCode);
  666. if (r == -1)
  667. {
  668. // For pipes, ERROR_BROKEN_PIPE is the normal end of the pipe.
  669. if (errorCode == ERROR_BROKEN_PIPE)
  670. {
  671. r = 0;
  672. }
  673. else
  674. {
  675. if (errorCode == ERROR_INVALID_PARAMETER)
  676. throw new ArgumentException(SR.Arg_HandleNotSync, "handle");
  677. throw Win32Marshal.GetExceptionForWin32Error(errorCode);
  678. }
  679. }
  680. Debug.Assert(r >= 0, "FileStream's ReadCore is likely broken.");
  681. _pos += r;
  682. return r;
  683. }
  684. [System.Security.SecuritySafeCritical] // auto-generated
  685. public override long Seek(long offset, SeekOrigin origin)
  686. {
  687. if (origin < SeekOrigin.Begin || origin > SeekOrigin.End)
  688. throw new ArgumentException(SR.Argument_InvalidSeekOrigin, nameof(origin));
  689. Contract.EndContractBlock();
  690. if (_handle.IsClosed) throw Error.GetFileNotOpen();
  691. if (!_parent.CanSeek) throw Error.GetSeekNotSupported();
  692. Debug.Assert((_readPos == 0 && _readLen == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLen), "We're either reading or writing, but not both.");
  693. // If we've got bytes in our buffer to write, write them out.
  694. // If we've read in and consumed some bytes, we'll have to adjust
  695. // our seek positions ONLY IF we're seeking relative to the current
  696. // position in the stream. This simulates doing a seek to the new
  697. // position, then a read for the number of bytes we have in our buffer.
  698. if (_writePos > 0)
  699. {
  700. FlushWrite(false);
  701. }
  702. else if (origin == SeekOrigin.Current)
  703. {
  704. // Don't call FlushRead here, which would have caused an infinite
  705. // loop. Simply adjust the seek origin. This isn't necessary
  706. // if we're seeking relative to the beginning or end of the stream.
  707. offset -= (_readLen - _readPos);
  708. }
  709. // Verify that internal position is in sync with the handle
  710. if (_exposedHandle)
  711. VerifyOSHandlePosition();
  712. long oldPos = _pos + (_readPos - _readLen);
  713. long pos = SeekCore(offset, origin);
  714. // Prevent users from overwriting data in a file that was opened in
  715. // append mode.
  716. if (_appendStart != -1 && pos < _appendStart)
  717. {
  718. SeekCore(oldPos, SeekOrigin.Begin);
  719. throw new IOException(SR.IO_SeekAppendOverwrite);
  720. }
  721. // We now must update the read buffer. We can in some cases simply
  722. // update _readPos within the buffer, copy around the buffer so our
  723. // Position property is still correct, and avoid having to do more
  724. // reads from the disk. Otherwise, discard the buffer's contents.
  725. if (_readLen > 0)
  726. {
  727. // We can optimize the following condition:
  728. // oldPos - _readPos <= pos < oldPos + _readLen - _readPos
  729. if (oldPos == pos)
  730. {
  731. if (_readPos > 0)
  732. {
  733. //Console.WriteLine("Seek: seeked for 0, adjusting buffer back by: "+_readPos+" _readLen: "+_readLen);
  734. Buffer.BlockCopy(_buffer, _readPos, _buffer, 0, _readLen - _readPos);
  735. _readLen -= _readPos;
  736. _readPos = 0;
  737. }
  738. // If we still have buffered data, we must update the stream's
  739. // position so our Position property is correct.
  740. if (_readLen > 0)
  741. SeekCore(_readLen, SeekOrigin.Current);
  742. }
  743. else if (oldPos - _readPos < pos && pos < oldPos + _readLen - _readPos)
  744. {
  745. int diff = (int)(pos - oldPos);
  746. //Console.WriteLine("Seek: diff was "+diff+", readpos was "+_readPos+" adjusting buffer - shrinking by "+ (_readPos + diff));
  747. Buffer.BlockCopy(_buffer, _readPos + diff, _buffer, 0, _readLen - (_readPos + diff));
  748. _readLen -= (_readPos + diff);
  749. _readPos = 0;
  750. if (_readLen > 0)
  751. SeekCore(_readLen, SeekOrigin.Current);
  752. }
  753. else
  754. {
  755. // Lose the read buffer.
  756. _readPos = 0;
  757. _readLen = 0;
  758. }
  759. Debug.Assert(_readLen >= 0 && _readPos <= _readLen, "_readLen should be nonnegative, and _readPos should be less than or equal _readLen");
  760. Debug.Assert(pos == _parent.Position, "Seek optimization: pos != Position! Buffer math was mangled.");
  761. }
  762. return pos;
  763. }
  764. // This doesn't do argument checking. Necessary for SetLength, which must
  765. // set the file pointer beyond the end of the file. This will update the
  766. // internal position
  767. // This is called during construction so it should avoid any virtual
  768. // calls
  769. [System.Security.SecuritySafeCritical] // auto-generated
  770. private long SeekCore(long offset, SeekOrigin origin)
  771. {
  772. Debug.Assert(!_handle.IsClosed && _canSeek, "!_handle.IsClosed && _parent.CanSeek");
  773. Debug.Assert(origin >= SeekOrigin.Begin && origin <= SeekOrigin.End, "origin>=SeekOrigin.Begin && origin<=SeekOrigin.End");
  774. long ret = 0;
  775. if (!Interop.mincore.SetFilePointerEx(_handle, offset, out ret, (uint)origin))
  776. {
  777. int errorCode = GetLastWin32ErrorAndDisposeHandleIfInvalid();
  778. throw Win32Marshal.GetExceptionForWin32Error(errorCode);
  779. }
  780. _pos = ret;
  781. return ret;
  782. }
  783. private void EnsureBufferAllocated()
  784. {
  785. if (_buffer == null)
  786. {
  787. AllocateBuffer();
  788. }
  789. }
  790. private void AllocateBuffer()
  791. {
  792. Debug.Assert(_buffer == null);
  793. Debug.Assert(_preallocatedOverlapped == null);
  794. _buffer = new byte[_bufferSize];
  795. if (_isAsync)
  796. {
  797. _preallocatedOverlapped = new PreAllocatedOverlapped(s_ioCallback, this, _buffer);
  798. }
  799. }
  800. // Checks the position of the OS's handle equals what we expect it to.
  801. // This will fail if someone else moved the Win32FileStream's handle or if
  802. // our position updating code is incorrect.
  803. private void VerifyOSHandlePosition()
  804. {
  805. if (!_parent.CanSeek)
  806. return;
  807. // SeekCore will override the current _pos, so save it now
  808. long oldPos = _pos;
  809. long curPos = SeekCore(0, SeekOrigin.Current);
  810. if (curPos != oldPos)
  811. {
  812. // For reads, this is non-fatal but we still could have returned corrupted
  813. // data in some cases. So discard the internal buffer. Potential MDA
  814. _readPos = 0;
  815. _readLen = 0;
  816. if (_writePos > 0)
  817. {
  818. // Discard the buffer and let the user know!
  819. _writePos = 0;
  820. throw new IOException(SR.IO_FileStreamHandlePosition);
  821. }
  822. }
  823. }
  824. [System.Security.SecuritySafeCritical] // auto-generated
  825. public override void Write(byte[] array, int offset, int count)
  826. {
  827. if (array == null)
  828. throw new ArgumentNullException(nameof(array), SR.ArgumentNull_Buffer);
  829. if (offset < 0)
  830. throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
  831. if (count < 0)
  832. throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
  833. if (array.Length - offset < count)
  834. throw new ArgumentException(SR.Argument_InvalidOffLen /*, no good single parameter name to pass*/);
  835. Contract.EndContractBlock();
  836. if (_handle.IsClosed) throw Error.GetFileNotOpen();
  837. if (_writePos == 0)
  838. {
  839. // Ensure we can write to the stream, and ready buffer for writing.
  840. if (!_parent.CanWrite) throw Error.GetWriteNotSupported();
  841. if (_readPos < _readLen) FlushRead();
  842. _readPos = 0;
  843. _readLen = 0;
  844. }
  845. // If our buffer has data in it, copy data from the user's array into
  846. // the buffer, and if we can fit it all there, return. Otherwise, write
  847. // the buffer to disk and copy any remaining data into our buffer.
  848. // The assumption here is memcpy is cheaper than disk (or net) IO.
  849. // (10 milliseconds to disk vs. ~20-30 microseconds for a 4K memcpy)
  850. // So the extra copying will reduce the total number of writes, in
  851. // non-pathological cases (i.e. write 1 byte, then write for the buffer
  852. // size repeatedly)
  853. if (_writePos > 0)
  854. {
  855. int numBytes = _bufferSize - _writePos; // space left in buffer
  856. if (numBytes > 0)
  857. {
  858. if (numBytes > count)
  859. numBytes = count;
  860. Buffer.BlockCopy(array, offset, _buffer, _writePos, numBytes);
  861. _writePos += numBytes;
  862. if (count == numBytes) return;
  863. offset += numBytes;
  864. count -= numBytes;
  865. }
  866. // Reset our buffer. We essentially want to call FlushWrite
  867. // without calling Flush on the underlying Stream.
  868. if (_isAsync)
  869. {
  870. WriteInternalCoreAsync(_buffer, 0, _writePos, CancellationToken.None).GetAwaiter().GetResult();
  871. }
  872. else
  873. {
  874. WriteCore(_buffer, 0, _writePos);
  875. }
  876. _writePos = 0;
  877. }
  878. // If the buffer would slow writes down, avoid buffer completely.
  879. if (count >= _bufferSize)
  880. {
  881. Debug.Assert(_writePos == 0, "FileStream cannot have buffered data to write here! Your stream will be corrupted.");
  882. WriteCore(array, offset, count);
  883. return;
  884. }
  885. else if (count == 0)
  886. return; // Don't allocate a buffer then call memcpy for 0 bytes.
  887. EnsureBufferAllocated();
  888. // Copy remaining bytes into buffer, to write at a later date.
  889. Buffer.BlockCopy(array, offset, _buffer, _writePos, count);
  890. _writePos = count;
  891. return;
  892. }
  893. [System.Security.SecuritySafeCritical] // auto-generated
  894. private unsafe void WriteCore(byte[] buffer, int offset, int count)
  895. {
  896. Debug.Assert(!_handle.IsClosed, "!_handle.IsClosed");
  897. Debug.Assert(_parent.CanWrite, "_parent.CanWrite");
  898. Debug.Assert(buffer != null, "buffer != null");
  899. Debug.Assert(_readPos == _readLen, "_readPos == _readLen");
  900. Debug.Assert(offset >= 0, "offset is negative");
  901. Debug.Assert(count >= 0, "count is negative");
  902. if (_isAsync)
  903. {
  904. WriteInternalCoreAsync(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult();
  905. return;
  906. }
  907. // Make sure we are writing to the position that we think we are
  908. if (_exposedHandle)
  909. VerifyOSHandlePosition();
  910. int errorCode = 0;
  911. int r = WriteFileNative(_handle, buffer, offset, count, null, out errorCode);
  912. if (r == -1)
  913. {
  914. // For pipes, ERROR_NO_DATA is not an error, but the pipe is closing.
  915. if (errorCode == ERROR_NO_DATA)
  916. {
  917. r = 0;
  918. }
  919. else
  920. {
  921. // ERROR_INVALID_PARAMETER may be returned for writes
  922. // where the position is too large (i.e. writing at Int64.MaxValue
  923. // on Win9x) OR for synchronous writes to a handle opened
  924. // asynchronously.
  925. if (errorCode == ERROR_INVALID_PARAMETER)
  926. throw new IOException(SR.IO_FileTooLongOrHandleNotSync);
  927. throw Win32Marshal.GetExceptionForWin32Error(errorCode);
  928. }
  929. }
  930. Debug.Assert(r >= 0, "FileStream's WriteCore is likely broken.");
  931. _pos += r;
  932. return;
  933. }
  934. [System.Security.SecuritySafeCritical] // auto-generated
  935. private Task<int> ReadInternalAsync(byte[] array, int offset, int numBytes, CancellationToken cancellationToken)
  936. {
  937. Debug.Assert(_isAsync);
  938. if (!_parent.CanRead) throw Error.GetReadNotSupported();
  939. Debug.Assert((_readPos == 0 && _readLen == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLen), "We're either reading or writing, but not both.");
  940. if (_isPipe)
  941. {
  942. // Pipes are tricky, at least when you have 2 different pipes
  943. // that you want to use simultaneously. When redirecting stdout
  944. // & stderr with the Process class, it's easy to deadlock your
  945. // parent & child processes when doing writes 4K at a time. The
  946. // OS appears to use a 4K buffer internally. If you write to a
  947. // pipe that is full, you will block until someone read from
  948. // that pipe. If you try reading from an empty pipe and
  949. // Win32FileStream's ReadAsync blocks waiting for data to fill it's
  950. // internal buffer, you will be blocked. In a case where a child
  951. // process writes to stdout & stderr while a parent process tries
  952. // reading from both, you can easily get into a deadlock here.
  953. // To avoid this deadlock, don't buffer when doing async IO on
  954. // pipes. But don't completely ignore buffered data either.
  955. if (_readPos < _readLen)
  956. {
  957. int n = _readLen - _readPos;
  958. if (n > numBytes) n = numBytes;
  959. Buffer.BlockCopy(_buffer, _readPos, array, offset, n);
  960. _readPos += n;
  961. // Return a completed task
  962. return TaskFromResultOrCache(n);
  963. }
  964. else
  965. {
  966. Debug.Assert(_writePos == 0, "Win32FileStream must not have buffered write data here! Pipes should be unidirectional.");
  967. return ReadInternalCoreAsync(array, offset, numBytes, 0, cancellationToken);
  968. }
  969. }
  970. Debug.Assert(!_isPipe, "Should not be a pipe.");
  971. // Handle buffering.
  972. if (_writePos > 0) FlushWrite(false);
  973. if (_readPos == _readLen)
  974. {
  975. // I can't see how to handle buffering of async requests when
  976. // filling the buffer asynchronously, without a lot of complexity.
  977. // The problems I see are issuing an async read, we do an async
  978. // read to fill the buffer, then someone issues another read
  979. // (either synchronously or asynchronously) before the first one
  980. // returns. This would involve some sort of complex buffer locking
  981. // that we probably don't want to get into, at least not in V1.
  982. // If we did a sync read to fill the buffer, we could avoid the
  983. // problem, and any async read less than 64K gets turned into a
  984. // synchronous read by NT anyways... --
  985. if (numBytes < _bufferSize)
  986. {
  987. EnsureBufferAllocated();
  988. Task<int> readTask = ReadInternalCoreAsync(_buffer, 0, _bufferSize, 0, cancellationToken);
  989. _readLen = readTask.GetAwaiter().GetResult();
  990. int n = _readLen;
  991. if (n > numBytes) n = numBytes;
  992. Buffer.BlockCopy(_buffer, 0, array, offset, n);
  993. _readPos = n;
  994. // Return a completed task (recycling the one above if possible)