FileStream.cs 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883
  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.Runtime.Serialization;
  5. using System.Threading;
  6. using System.Threading.Tasks;
  7. using Microsoft.Win32.SafeHandles;
  8. using System.Diagnostics;
  9. namespace System.IO
  10. {
  11. public partial class FileStream : Stream
  12. {
  13. private const FileShare DefaultShare = FileShare.Read;
  14. private const bool DefaultIsAsync = false;
  15. internal const int DefaultBufferSize = 4096;
  16. private byte[]? _buffer;
  17. private int _bufferLength;
  18. private readonly SafeFileHandle _fileHandle; // only ever null if ctor throws
  19. /// <summary>Whether the file is opened for reading, writing, or both.</summary>
  20. private readonly FileAccess _access;
  21. /// <summary>The path to the opened file.</summary>
  22. private readonly string? _path;
  23. /// <summary>The next available byte to be read from the _buffer.</summary>
  24. private int _readPos;
  25. /// <summary>The number of valid bytes in _buffer.</summary>
  26. private int _readLength;
  27. /// <summary>The next location in which a write should occur to the buffer.</summary>
  28. private int _writePos;
  29. /// <summary>
  30. /// Whether asynchronous read/write/flush operations should be performed using async I/O.
  31. /// On Windows FileOptions.Asynchronous controls how the file handle is configured,
  32. /// and then as a result how operations are issued against that file handle. On Unix,
  33. /// there isn't any distinction around how file descriptors are created for async vs
  34. /// sync, but we still differentiate how the operations are issued in order to provide
  35. /// similar behavioral semantics and performance characteristics as on Windows. On
  36. /// Windows, if non-async, async read/write requests just delegate to the base stream,
  37. /// and no attempt is made to synchronize between sync and async operations on the stream;
  38. /// if async, then async read/write requests are implemented specially, and sync read/write
  39. /// requests are coordinated with async ones by implementing the sync ones over the async
  40. /// ones. On Unix, we do something similar. If non-async, async read/write requests just
  41. /// delegate to the base stream, and no attempt is made to synchronize. If async, we use
  42. /// a semaphore to coordinate both sync and async operations.
  43. /// </summary>
  44. private readonly bool _useAsyncIO;
  45. /// <summary>cached task for read ops that complete synchronously</summary>
  46. private Task<int>? _lastSynchronouslyCompletedTask = null;
  47. /// <summary>
  48. /// Currently cached position in the stream. This should always mirror the underlying file's actual position,
  49. /// and should only ever be out of sync if another stream with access to this same file manipulates it, at which
  50. /// point we attempt to error out.
  51. /// </summary>
  52. private long _filePosition;
  53. /// <summary>Whether the file stream's handle has been exposed.</summary>
  54. private bool _exposedHandle;
  55. /// <summary>Caches whether Serialization Guard has been disabled for file writes</summary>
  56. private static int s_cachedSerializationSwitch = 0;
  57. [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access) instead. https://go.microsoft.com/fwlink/?linkid=14202")]
  58. public FileStream(IntPtr handle, FileAccess access)
  59. : this(handle, access, true, DefaultBufferSize, false)
  60. {
  61. }
  62. [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. https://go.microsoft.com/fwlink/?linkid=14202")]
  63. public FileStream(IntPtr handle, FileAccess access, bool ownsHandle)
  64. : this(handle, access, ownsHandle, DefaultBufferSize, false)
  65. {
  66. }
  67. [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. https://go.microsoft.com/fwlink/?linkid=14202")]
  68. public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize)
  69. : this(handle, access, ownsHandle, bufferSize, false)
  70. {
  71. }
  72. [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. https://go.microsoft.com/fwlink/?linkid=14202")]
  73. public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync)
  74. {
  75. SafeFileHandle safeHandle = new SafeFileHandle(handle, ownsHandle: ownsHandle);
  76. try
  77. {
  78. ValidateAndInitFromHandle(safeHandle, access, bufferSize, isAsync);
  79. }
  80. catch
  81. {
  82. // We don't want to take ownership of closing passed in handles
  83. // *unless* the constructor completes successfully.
  84. GC.SuppressFinalize(safeHandle);
  85. // This would also prevent Close from being called, but is unnecessary
  86. // as we've removed the object from the finalizer queue.
  87. //
  88. // safeHandle.SetHandleAsInvalid();
  89. throw;
  90. }
  91. // Note: Cleaner to set the following fields in ValidateAndInitFromHandle,
  92. // but we can't as they're readonly.
  93. _access = access;
  94. _useAsyncIO = isAsync;
  95. // As the handle was passed in, we must set the handle field at the very end to
  96. // avoid the finalizer closing the handle when we throw errors.
  97. _fileHandle = safeHandle;
  98. }
  99. public FileStream(SafeFileHandle handle, FileAccess access)
  100. : this(handle, access, DefaultBufferSize)
  101. {
  102. }
  103. public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize)
  104. : this(handle, access, bufferSize, GetDefaultIsAsync(handle))
  105. {
  106. }
  107. private void ValidateAndInitFromHandle(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync)
  108. {
  109. if (handle.IsInvalid)
  110. throw new ArgumentException(SR.Arg_InvalidHandle, nameof(handle));
  111. if (access < FileAccess.Read || access > FileAccess.ReadWrite)
  112. throw new ArgumentOutOfRangeException(nameof(access), SR.ArgumentOutOfRange_Enum);
  113. if (bufferSize <= 0)
  114. throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum);
  115. if (handle.IsClosed)
  116. throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed);
  117. if (handle.IsAsync.HasValue && isAsync != handle.IsAsync.GetValueOrDefault())
  118. throw new ArgumentException(SR.Arg_HandleNotAsync, nameof(handle));
  119. _exposedHandle = true;
  120. _bufferLength = bufferSize;
  121. InitFromHandle(handle, access, isAsync);
  122. }
  123. public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync)
  124. {
  125. ValidateAndInitFromHandle(handle, access, bufferSize, isAsync);
  126. // Note: Cleaner to set the following fields in ValidateAndInitFromHandle,
  127. // but we can't as they're readonly.
  128. _access = access;
  129. _useAsyncIO = isAsync;
  130. // As the handle was passed in, we must set the handle field at the very end to
  131. // avoid the finalizer closing the handle when we throw errors.
  132. _fileHandle = handle;
  133. }
  134. public FileStream(string path, FileMode mode) :
  135. this(path, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), DefaultShare, DefaultBufferSize, DefaultIsAsync)
  136. { }
  137. public FileStream(string path, FileMode mode, FileAccess access) :
  138. this(path, mode, access, DefaultShare, DefaultBufferSize, DefaultIsAsync)
  139. { }
  140. public FileStream(string path, FileMode mode, FileAccess access, FileShare share) :
  141. this(path, mode, access, share, DefaultBufferSize, DefaultIsAsync)
  142. { }
  143. public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize) :
  144. this(path, mode, access, share, bufferSize, DefaultIsAsync)
  145. { }
  146. public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync) :
  147. this(path, mode, access, share, bufferSize, useAsync ? FileOptions.Asynchronous : FileOptions.None)
  148. { }
  149. public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options)
  150. {
  151. if (path == null)
  152. throw new ArgumentNullException(nameof(path), SR.ArgumentNull_Path);
  153. if (path.Length == 0)
  154. throw new ArgumentException(SR.Argument_EmptyPath, nameof(path));
  155. // don't include inheritable in our bounds check for share
  156. FileShare tempshare = share & ~FileShare.Inheritable;
  157. string? badArg = null;
  158. if (mode < FileMode.CreateNew || mode > FileMode.Append)
  159. badArg = nameof(mode);
  160. else if (access < FileAccess.Read || access > FileAccess.ReadWrite)
  161. badArg = nameof(access);
  162. else if (tempshare < FileShare.None || tempshare > (FileShare.ReadWrite | FileShare.Delete))
  163. badArg = nameof(share);
  164. if (badArg != null)
  165. throw new ArgumentOutOfRangeException(badArg, SR.ArgumentOutOfRange_Enum);
  166. // NOTE: any change to FileOptions enum needs to be matched here in the error validation
  167. if (options != FileOptions.None && (options & ~(FileOptions.WriteThrough | FileOptions.Asynchronous | FileOptions.RandomAccess | FileOptions.DeleteOnClose | FileOptions.SequentialScan | FileOptions.Encrypted | (FileOptions)0x20000000 /* NoBuffering */)) != 0)
  168. throw new ArgumentOutOfRangeException(nameof(options), SR.ArgumentOutOfRange_Enum);
  169. if (bufferSize <= 0)
  170. throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum);
  171. // Write access validation
  172. if ((access & FileAccess.Write) == 0)
  173. {
  174. if (mode == FileMode.Truncate || mode == FileMode.CreateNew || mode == FileMode.Create || mode == FileMode.Append)
  175. {
  176. // No write access, mode and access disagree but flag access since mode comes first
  177. throw new ArgumentException(SR.Format(SR.Argument_InvalidFileModeAndAccessCombo, mode, access), nameof(access));
  178. }
  179. }
  180. if ((access & FileAccess.Read) != 0 && mode == FileMode.Append)
  181. throw new ArgumentException(SR.Argument_InvalidAppendMode, nameof(access));
  182. string fullPath = Path.GetFullPath(path);
  183. _path = fullPath;
  184. _access = access;
  185. _bufferLength = bufferSize;
  186. if ((options & FileOptions.Asynchronous) != 0)
  187. _useAsyncIO = true;
  188. if ((access & FileAccess.Write) == FileAccess.Write)
  189. {
  190. SerializationInfo.ThrowIfDeserializationInProgress("AllowFileWrites", ref s_cachedSerializationSwitch);
  191. }
  192. _fileHandle = OpenHandle(mode, share, options);
  193. try
  194. {
  195. Init(mode, share, path);
  196. }
  197. catch
  198. {
  199. // If anything goes wrong while setting up the stream, make sure we deterministically dispose
  200. // of the opened handle.
  201. _fileHandle.Dispose();
  202. _fileHandle = null!;
  203. throw;
  204. }
  205. }
  206. [Obsolete("This property has been deprecated. Please use FileStream's SafeFileHandle property instead. https://go.microsoft.com/fwlink/?linkid=14202")]
  207. public virtual IntPtr Handle { get { return SafeFileHandle.DangerousGetHandle(); } }
  208. public virtual void Lock(long position, long length)
  209. {
  210. if (position < 0 || length < 0)
  211. {
  212. throw new ArgumentOutOfRangeException(position < 0 ? nameof(position) : nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum);
  213. }
  214. if (_fileHandle.IsClosed)
  215. {
  216. throw Error.GetFileNotOpen();
  217. }
  218. LockInternal(position, length);
  219. }
  220. public virtual void Unlock(long position, long length)
  221. {
  222. if (position < 0 || length < 0)
  223. {
  224. throw new ArgumentOutOfRangeException(position < 0 ? nameof(position) : nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum);
  225. }
  226. if (_fileHandle.IsClosed)
  227. {
  228. throw Error.GetFileNotOpen();
  229. }
  230. UnlockInternal(position, length);
  231. }
  232. public override Task FlushAsync(CancellationToken cancellationToken)
  233. {
  234. // If we have been inherited into a subclass, the following implementation could be incorrect
  235. // since it does not call through to Flush() which a subclass might have overridden. To be safe
  236. // we will only use this implementation in cases where we know it is safe to do so,
  237. // and delegate to our base class (which will call into Flush) when we are not sure.
  238. if (GetType() != typeof(FileStream))
  239. return base.FlushAsync(cancellationToken);
  240. return FlushAsyncInternal(cancellationToken);
  241. }
  242. public override int Read(byte[] array, int offset, int count)
  243. {
  244. ValidateReadWriteArgs(array, offset, count);
  245. return _useAsyncIO ?
  246. ReadAsyncTask(array, offset, count, CancellationToken.None).GetAwaiter().GetResult() :
  247. ReadSpan(new Span<byte>(array, offset, count));
  248. }
  249. public override int Read(Span<byte> buffer)
  250. {
  251. if (GetType() == typeof(FileStream) && !_useAsyncIO)
  252. {
  253. if (_fileHandle.IsClosed)
  254. {
  255. throw Error.GetFileNotOpen();
  256. }
  257. return ReadSpan(buffer);
  258. }
  259. else
  260. {
  261. // This type is derived from FileStream and/or the stream is in async mode. If this is a
  262. // derived type, it may have overridden Read(byte[], int, int) prior to this Read(Span<byte>)
  263. // overload being introduced. In that case, this Read(Span<byte>) overload should use the behavior
  264. // of Read(byte[],int,int) overload. Or if the stream is in async mode, we can't call the
  265. // synchronous ReadSpan, so we similarly call the base Read, which will turn delegate to
  266. // Read(byte[],int,int), which will do the right thing if we're in async mode.
  267. return base.Read(buffer);
  268. }
  269. }
  270. public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
  271. {
  272. if (buffer == null)
  273. throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
  274. if (offset < 0)
  275. throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
  276. if (count < 0)
  277. throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
  278. if (buffer.Length - offset < count)
  279. throw new ArgumentException(SR.Argument_InvalidOffLen /*, no good single parameter name to pass*/);
  280. // If we have been inherited into a subclass, the following implementation could be incorrect
  281. // since it does not call through to Read() which a subclass might have overridden.
  282. // To be safe we will only use this implementation in cases where we know it is safe to do so,
  283. // and delegate to our base class (which will call into Read/ReadAsync) when we are not sure.
  284. // Similarly, if we weren't opened for asynchronous I/O, call to the base implementation so that
  285. // Read is invoked asynchronously.
  286. if (GetType() != typeof(FileStream) || !_useAsyncIO)
  287. return base.ReadAsync(buffer, offset, count, cancellationToken);
  288. if (cancellationToken.IsCancellationRequested)
  289. return Task.FromCanceled<int>(cancellationToken);
  290. if (IsClosed)
  291. throw Error.GetFileNotOpen();
  292. return ReadAsyncTask(buffer, offset, count, cancellationToken);
  293. }
  294. public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
  295. {
  296. if (!_useAsyncIO || GetType() != typeof(FileStream))
  297. {
  298. // If we're not using async I/O, delegate to the base, which will queue a call to Read.
  299. // Or if this isn't a concrete FileStream, a derived type may have overridden ReadAsync(byte[],...),
  300. // which was introduced first, so delegate to the base which will delegate to that.
  301. return base.ReadAsync(buffer, cancellationToken);
  302. }
  303. if (cancellationToken.IsCancellationRequested)
  304. {
  305. return new ValueTask<int>(Task.FromCanceled<int>(cancellationToken));
  306. }
  307. if (IsClosed)
  308. {
  309. throw Error.GetFileNotOpen();
  310. }
  311. Task<int>? t = ReadAsyncInternal(buffer, cancellationToken, out int synchronousResult);
  312. return t != null ?
  313. new ValueTask<int>(t) :
  314. new ValueTask<int>(synchronousResult);
  315. }
  316. private Task<int> ReadAsyncTask(byte[] array, int offset, int count, CancellationToken cancellationToken)
  317. {
  318. Task<int>? t = ReadAsyncInternal(new Memory<byte>(array, offset, count), cancellationToken, out int synchronousResult);
  319. if (t == null)
  320. {
  321. t = _lastSynchronouslyCompletedTask;
  322. Debug.Assert(t == null || t.IsCompletedSuccessfully, "Cached task should have completed successfully");
  323. if (t == null || t.Result != synchronousResult)
  324. {
  325. _lastSynchronouslyCompletedTask = t = Task.FromResult(synchronousResult);
  326. }
  327. }
  328. return t;
  329. }
  330. public override void Write(byte[] array, int offset, int count)
  331. {
  332. ValidateReadWriteArgs(array, offset, count);
  333. if (_useAsyncIO)
  334. {
  335. WriteAsyncInternal(new ReadOnlyMemory<byte>(array, offset, count), CancellationToken.None).GetAwaiter().GetResult();
  336. }
  337. else
  338. {
  339. WriteSpan(new ReadOnlySpan<byte>(array, offset, count));
  340. }
  341. }
  342. public override void Write(ReadOnlySpan<byte> buffer)
  343. {
  344. if (GetType() == typeof(FileStream) && !_useAsyncIO)
  345. {
  346. if (_fileHandle.IsClosed)
  347. {
  348. throw Error.GetFileNotOpen();
  349. }
  350. WriteSpan(buffer);
  351. }
  352. else
  353. {
  354. // This type is derived from FileStream and/or the stream is in async mode. If this is a
  355. // derived type, it may have overridden Write(byte[], int, int) prior to this Write(ReadOnlySpan<byte>)
  356. // overload being introduced. In that case, this Write(ReadOnlySpan<byte>) overload should use the behavior
  357. // of Write(byte[],int,int) overload. Or if the stream is in async mode, we can't call the
  358. // synchronous WriteSpan, so we similarly call the base Write, which will turn delegate to
  359. // Write(byte[],int,int), which will do the right thing if we're in async mode.
  360. base.Write(buffer);
  361. }
  362. }
  363. public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
  364. {
  365. if (buffer == null)
  366. throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
  367. if (offset < 0)
  368. throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
  369. if (count < 0)
  370. throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
  371. if (buffer.Length - offset < count)
  372. throw new ArgumentException(SR.Argument_InvalidOffLen /*, no good single parameter name to pass*/);
  373. // If we have been inherited into a subclass, the following implementation could be incorrect
  374. // since it does not call through to Write() or WriteAsync() which a subclass might have overridden.
  375. // To be safe we will only use this implementation in cases where we know it is safe to do so,
  376. // and delegate to our base class (which will call into Write/WriteAsync) when we are not sure.
  377. if (!_useAsyncIO || GetType() != typeof(FileStream))
  378. return base.WriteAsync(buffer, offset, count, cancellationToken);
  379. if (cancellationToken.IsCancellationRequested)
  380. return Task.FromCanceled(cancellationToken);
  381. if (IsClosed)
  382. throw Error.GetFileNotOpen();
  383. return WriteAsyncInternal(new ReadOnlyMemory<byte>(buffer, offset, count), cancellationToken).AsTask();
  384. }
  385. public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
  386. {
  387. if (!_useAsyncIO || GetType() != typeof(FileStream))
  388. {
  389. // If we're not using async I/O, delegate to the base, which will queue a call to Write.
  390. // Or if this isn't a concrete FileStream, a derived type may have overridden WriteAsync(byte[],...),
  391. // which was introduced first, so delegate to the base which will delegate to that.
  392. return base.WriteAsync(buffer, cancellationToken);
  393. }
  394. if (cancellationToken.IsCancellationRequested)
  395. {
  396. return new ValueTask(Task.FromCanceled<int>(cancellationToken));
  397. }
  398. if (IsClosed)
  399. {
  400. throw Error.GetFileNotOpen();
  401. }
  402. return WriteAsyncInternal(buffer, cancellationToken);
  403. }
  404. /// <summary>
  405. /// Clears buffers for this stream and causes any buffered data to be written to the file.
  406. /// </summary>
  407. public override void Flush()
  408. {
  409. // Make sure that we call through the public virtual API
  410. Flush(flushToDisk: false);
  411. }
  412. /// <summary>
  413. /// Clears buffers for this stream, and if <param name="flushToDisk"/> is true,
  414. /// causes any buffered data to be written to the file.
  415. /// </summary>
  416. public virtual void Flush(bool flushToDisk)
  417. {
  418. if (IsClosed) throw Error.GetFileNotOpen();
  419. FlushInternalBuffer();
  420. if (flushToDisk && CanWrite)
  421. {
  422. FlushOSBuffer();
  423. }
  424. }
  425. /// <summary>Gets a value indicating whether the current stream supports reading.</summary>
  426. public override bool CanRead
  427. {
  428. get { return !_fileHandle.IsClosed && (_access & FileAccess.Read) != 0; }
  429. }
  430. /// <summary>Gets a value indicating whether the current stream supports writing.</summary>
  431. public override bool CanWrite
  432. {
  433. get { return !_fileHandle.IsClosed && (_access & FileAccess.Write) != 0; }
  434. }
  435. /// <summary>Validates arguments to Read and Write and throws resulting exceptions.</summary>
  436. /// <param name="array">The buffer to read from or write to.</param>
  437. /// <param name="offset">The zero-based offset into the array.</param>
  438. /// <param name="count">The maximum number of bytes to read or write.</param>
  439. private void ValidateReadWriteArgs(byte[] array, int offset, int count)
  440. {
  441. if (array == null)
  442. throw new ArgumentNullException(nameof(array), SR.ArgumentNull_Buffer);
  443. if (offset < 0)
  444. throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
  445. if (count < 0)
  446. throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
  447. if (array.Length - offset < count)
  448. throw new ArgumentException(SR.Argument_InvalidOffLen /*, no good single parameter name to pass*/);
  449. if (_fileHandle.IsClosed)
  450. throw Error.GetFileNotOpen();
  451. }
  452. /// <summary>Sets the length of this stream to the given value.</summary>
  453. /// <param name="value">The new length of the stream.</param>
  454. public override void SetLength(long value)
  455. {
  456. if (value < 0)
  457. throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum);
  458. if (_fileHandle.IsClosed)
  459. throw Error.GetFileNotOpen();
  460. if (!CanSeek)
  461. throw Error.GetSeekNotSupported();
  462. if (!CanWrite)
  463. throw Error.GetWriteNotSupported();
  464. SetLengthInternal(value);
  465. }
  466. public virtual SafeFileHandle SafeFileHandle
  467. {
  468. get
  469. {
  470. Flush();
  471. _exposedHandle = true;
  472. return _fileHandle;
  473. }
  474. }
  475. /// <summary>Gets the path that was passed to the constructor.</summary>
  476. public virtual string Name { get { return _path ?? SR.IO_UnknownFileName; } }
  477. /// <summary>Gets a value indicating whether the stream was opened for I/O to be performed synchronously or asynchronously.</summary>
  478. public virtual bool IsAsync
  479. {
  480. get { return _useAsyncIO; }
  481. }
  482. /// <summary>Gets the length of the stream in bytes.</summary>
  483. public override long Length
  484. {
  485. get
  486. {
  487. if (_fileHandle.IsClosed) throw Error.GetFileNotOpen();
  488. if (!CanSeek) throw Error.GetSeekNotSupported();
  489. return GetLengthInternal();
  490. }
  491. }
  492. /// <summary>
  493. /// Verify that the actual position of the OS's handle equals what we expect it to.
  494. /// This will fail if someone else moved the UnixFileStream's handle or if
  495. /// our position updating code is incorrect.
  496. /// </summary>
  497. private void VerifyOSHandlePosition()
  498. {
  499. bool verifyPosition = _exposedHandle; // in release, only verify if we've given out the handle such that someone else could be manipulating it
  500. #if DEBUG
  501. verifyPosition = true; // in debug, always make sure our position matches what the OS says it should be
  502. #endif
  503. if (verifyPosition && CanSeek)
  504. {
  505. long oldPos = _filePosition; // SeekCore will override the current _position, so save it now
  506. long curPos = SeekCore(_fileHandle, 0, SeekOrigin.Current);
  507. if (oldPos != curPos)
  508. {
  509. // For reads, this is non-fatal but we still could have returned corrupted
  510. // data in some cases, so discard the internal buffer. For writes,
  511. // this is a problem; discard the buffer and error out.
  512. _readPos = _readLength = 0;
  513. if (_writePos > 0)
  514. {
  515. _writePos = 0;
  516. throw new IOException(SR.IO_FileStreamHandlePosition);
  517. }
  518. }
  519. }
  520. }
  521. /// <summary>Verifies that state relating to the read/write buffer is consistent.</summary>
  522. [Conditional("DEBUG")]
  523. private void AssertBufferInvariants()
  524. {
  525. // Read buffer values must be in range: 0 <= _bufferReadPos <= _bufferReadLength <= _bufferLength
  526. Debug.Assert(0 <= _readPos && _readPos <= _readLength && _readLength <= _bufferLength);
  527. // Write buffer values must be in range: 0 <= _bufferWritePos <= _bufferLength
  528. Debug.Assert(0 <= _writePos && _writePos <= _bufferLength);
  529. // Read buffering and write buffering can't both be active
  530. Debug.Assert((_readPos == 0 && _readLength == 0) || _writePos == 0);
  531. }
  532. /// <summary>Validates that we're ready to read from the stream.</summary>
  533. private void PrepareForReading()
  534. {
  535. if (_fileHandle.IsClosed)
  536. throw Error.GetFileNotOpen();
  537. if (_readLength == 0 && !CanRead)
  538. throw Error.GetReadNotSupported();
  539. AssertBufferInvariants();
  540. }
  541. /// <summary>Gets or sets the position within the current stream</summary>
  542. public override long Position
  543. {
  544. get
  545. {
  546. if (_fileHandle.IsClosed)
  547. throw Error.GetFileNotOpen();
  548. if (!CanSeek)
  549. throw Error.GetSeekNotSupported();
  550. AssertBufferInvariants();
  551. VerifyOSHandlePosition();
  552. // We may have read data into our buffer from the handle, such that the handle position
  553. // is artificially further along than the consumer's view of the stream's position.
  554. // Thus, when reading, our position is really starting from the handle position negatively
  555. // offset by the number of bytes in the buffer and positively offset by the number of
  556. // bytes into that buffer we've read. When writing, both the read length and position
  557. // must be zero, and our position is just the handle position offset positive by how many
  558. // bytes we've written into the buffer.
  559. return (_filePosition - _readLength) + _readPos + _writePos;
  560. }
  561. set
  562. {
  563. if (value < 0)
  564. throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum);
  565. Seek(value, SeekOrigin.Begin);
  566. }
  567. }
  568. internal virtual bool IsClosed => _fileHandle.IsClosed;
  569. private static bool IsIoRelatedException(Exception e) =>
  570. // These all derive from IOException
  571. // DirectoryNotFoundException
  572. // DriveNotFoundException
  573. // EndOfStreamException
  574. // FileLoadException
  575. // FileNotFoundException
  576. // PathTooLongException
  577. // PipeException
  578. e is IOException ||
  579. // Note that SecurityException is only thrown on runtimes that support CAS
  580. // e is SecurityException ||
  581. e is UnauthorizedAccessException ||
  582. e is NotSupportedException ||
  583. (e is ArgumentException && !(e is ArgumentNullException));
  584. /// <summary>
  585. /// Gets the array used for buffering reading and writing.
  586. /// If the array hasn't been allocated, this will lazily allocate it.
  587. /// </summary>
  588. /// <returns>The buffer.</returns>
  589. private byte[] GetBuffer()
  590. {
  591. Debug.Assert(_buffer == null || _buffer.Length == _bufferLength);
  592. if (_buffer == null)
  593. {
  594. _buffer = new byte[_bufferLength];
  595. OnBufferAllocated();
  596. }
  597. return _buffer;
  598. }
  599. partial void OnBufferAllocated();
  600. /// <summary>
  601. /// Flushes the internal read/write buffer for this stream. If write data has been buffered,
  602. /// that data is written out to the underlying file. Or if data has been buffered for
  603. /// reading from the stream, the data is dumped and our position in the underlying file
  604. /// is rewound as necessary. This does not flush the OS buffer.
  605. /// </summary>
  606. private void FlushInternalBuffer()
  607. {
  608. AssertBufferInvariants();
  609. if (_writePos > 0)
  610. {
  611. FlushWriteBuffer();
  612. }
  613. else if (_readPos < _readLength && CanSeek)
  614. {
  615. FlushReadBuffer();
  616. }
  617. }
  618. /// <summary>Dumps any read data in the buffer and rewinds our position in the stream, accordingly, as necessary.</summary>
  619. private void FlushReadBuffer()
  620. {
  621. // Reading is done by blocks from the file, but someone could read
  622. // 1 byte from the buffer then write. At that point, the OS's file
  623. // pointer is out of sync with the stream's position. All write
  624. // functions should call this function to preserve the position in the file.
  625. AssertBufferInvariants();
  626. Debug.Assert(_writePos == 0, "FileStream: Write buffer must be empty in FlushReadBuffer!");
  627. int rewind = _readPos - _readLength;
  628. if (rewind != 0)
  629. {
  630. Debug.Assert(CanSeek, "FileStream will lose buffered read data now.");
  631. SeekCore(_fileHandle, rewind, SeekOrigin.Current);
  632. }
  633. _readPos = _readLength = 0;
  634. }
  635. /// <summary>
  636. /// Reads a byte from the file stream. Returns the byte cast to an int
  637. /// or -1 if reading from the end of the stream.
  638. /// </summary>
  639. public override int ReadByte()
  640. {
  641. PrepareForReading();
  642. byte[] buffer = GetBuffer();
  643. if (_readPos == _readLength)
  644. {
  645. FlushWriteBuffer();
  646. _readLength = FillReadBufferForReadByte();
  647. _readPos = 0;
  648. if (_readLength == 0)
  649. {
  650. return -1;
  651. }
  652. }
  653. return buffer[_readPos++];
  654. }
  655. /// <summary>
  656. /// Writes a byte to the current position in the stream and advances the position
  657. /// within the stream by one byte.
  658. /// </summary>
  659. /// <param name="value">The byte to write to the stream.</param>
  660. public override void WriteByte(byte value)
  661. {
  662. PrepareForWriting();
  663. // Flush the write buffer if it's full
  664. if (_writePos == _bufferLength)
  665. FlushWriteBufferForWriteByte();
  666. // We now have space in the buffer. Store the byte.
  667. GetBuffer()[_writePos++] = value;
  668. }
  669. /// <summary>
  670. /// Validates that we're ready to write to the stream,
  671. /// including flushing a read buffer if necessary.
  672. /// </summary>
  673. private void PrepareForWriting()
  674. {
  675. if (_fileHandle.IsClosed)
  676. throw Error.GetFileNotOpen();
  677. // Make sure we're good to write. We only need to do this if there's nothing already
  678. // in our write buffer, since if there is something in the buffer, we've already done
  679. // this checking and flushing.
  680. if (_writePos == 0)
  681. {
  682. if (!CanWrite) throw Error.GetWriteNotSupported();
  683. FlushReadBuffer();
  684. Debug.Assert(_bufferLength > 0, "_bufferSize > 0");
  685. }
  686. }
  687. ~FileStream()
  688. {
  689. // Preserved for compatibility since FileStream has defined a
  690. // finalizer in past releases and derived classes may depend
  691. // on Dispose(false) call.
  692. Dispose(false);
  693. }
  694. public override IAsyncResult BeginRead(byte[] array, int offset, int numBytes, AsyncCallback callback, object? state)
  695. {
  696. if (array == null)
  697. throw new ArgumentNullException(nameof(array));
  698. if (offset < 0)
  699. throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
  700. if (numBytes < 0)
  701. throw new ArgumentOutOfRangeException(nameof(numBytes), SR.ArgumentOutOfRange_NeedNonNegNum);
  702. if (array.Length - offset < numBytes)
  703. throw new ArgumentException(SR.Argument_InvalidOffLen);
  704. if (IsClosed) throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed);
  705. if (!CanRead) throw new NotSupportedException(SR.NotSupported_UnreadableStream);
  706. if (!IsAsync)
  707. return base.BeginRead(array, offset, numBytes, callback, state);
  708. else
  709. return TaskToApm.Begin(ReadAsyncTask(array, offset, numBytes, CancellationToken.None), callback, state);
  710. }
  711. public override IAsyncResult BeginWrite(byte[] array, int offset, int numBytes, AsyncCallback callback, object? state)
  712. {
  713. if (array == null)
  714. throw new ArgumentNullException(nameof(array));
  715. if (offset < 0)
  716. throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
  717. if (numBytes < 0)
  718. throw new ArgumentOutOfRangeException(nameof(numBytes), SR.ArgumentOutOfRange_NeedNonNegNum);
  719. if (array.Length - offset < numBytes)
  720. throw new ArgumentException(SR.Argument_InvalidOffLen);
  721. if (IsClosed) throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed);
  722. if (!CanWrite) throw new NotSupportedException(SR.NotSupported_UnwritableStream);
  723. if (!IsAsync)
  724. return base.BeginWrite(array, offset, numBytes, callback, state);
  725. else
  726. return TaskToApm.Begin(WriteAsyncInternal(new ReadOnlyMemory<byte>(array, offset, numBytes), CancellationToken.None).AsTask(), callback, state);
  727. }
  728. public override int EndRead(IAsyncResult asyncResult)
  729. {
  730. if (asyncResult == null)
  731. throw new ArgumentNullException(nameof(asyncResult));
  732. if (!IsAsync)
  733. return base.EndRead(asyncResult);
  734. else
  735. return TaskToApm.End<int>(asyncResult);
  736. }
  737. public override void EndWrite(IAsyncResult asyncResult)
  738. {
  739. if (asyncResult == null)
  740. throw new ArgumentNullException(nameof(asyncResult));
  741. if (!IsAsync)
  742. base.EndWrite(asyncResult);
  743. else
  744. TaskToApm.End(asyncResult);
  745. }
  746. }
  747. }