PageRenderTime 60ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/src/System.IO.Pipes/src/System/IO/Pipes/PipeStream.Windows.cs

https://gitlab.com/0072016/0072016-corefx-
C# | 498 lines | 368 code | 61 blank | 69 comment | 66 complexity | b6e069d88df0966ca2d0cdcbac9bacfa 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.CodeAnalysis;
  6. using System.Runtime.InteropServices;
  7. using System.Security;
  8. using System.Threading;
  9. using System.Threading.Tasks;
  10. using Microsoft.Win32.SafeHandles;
  11. namespace System.IO.Pipes
  12. {
  13. public abstract partial class PipeStream : Stream
  14. {
  15. internal const bool CheckOperationsRequiresSetHandle = true;
  16. internal ThreadPoolBoundHandle _threadPoolBinding;
  17. internal static string GetPipePath(string serverName, string pipeName)
  18. {
  19. string normalizedPipePath = Path.GetFullPath(@"\\" + serverName + @"\pipe\" + pipeName);
  20. if (String.Equals(normalizedPipePath, @"\\.\pipe\" + AnonymousPipeName, StringComparison.OrdinalIgnoreCase))
  21. {
  22. throw new ArgumentOutOfRangeException(nameof(pipeName), SR.ArgumentOutOfRange_AnonymousReserved);
  23. }
  24. return normalizedPipePath;
  25. }
  26. /// <summary>Throws an exception if the supplied handle does not represent a valid pipe.</summary>
  27. /// <param name="safePipeHandle">The handle to validate.</param>
  28. internal void ValidateHandleIsPipe(SafePipeHandle safePipeHandle)
  29. {
  30. // Check that this handle is infact a handle to a pipe.
  31. if (Interop.mincore.GetFileType(safePipeHandle) != Interop.mincore.FileTypes.FILE_TYPE_PIPE)
  32. {
  33. throw new IOException(SR.IO_InvalidPipeHandle);
  34. }
  35. }
  36. /// <summary>Initializes the handle to be used asynchronously.</summary>
  37. /// <param name="handle">The handle.</param>
  38. private void InitializeAsyncHandle(SafePipeHandle handle)
  39. {
  40. // If the handle is of async type, bind the handle to the ThreadPool so that we can use
  41. // the async operations (it's needed so that our native callbacks get called).
  42. _threadPoolBinding = ThreadPoolBoundHandle.BindHandle(handle);
  43. }
  44. private void UninitializeAsyncHandle()
  45. {
  46. if (_threadPoolBinding != null)
  47. _threadPoolBinding.Dispose();
  48. }
  49. [SecurityCritical]
  50. private unsafe int ReadCore(byte[] buffer, int offset, int count)
  51. {
  52. int errorCode = 0;
  53. int r = ReadFileNative(_handle, buffer, offset, count, null, out errorCode);
  54. if (r == -1)
  55. {
  56. // If the other side has broken the connection, set state to Broken and return 0
  57. if (errorCode == Interop.mincore.Errors.ERROR_BROKEN_PIPE ||
  58. errorCode == Interop.mincore.Errors.ERROR_PIPE_NOT_CONNECTED)
  59. {
  60. State = PipeState.Broken;
  61. r = 0;
  62. }
  63. else
  64. {
  65. throw Win32Marshal.GetExceptionForWin32Error(errorCode, String.Empty);
  66. }
  67. }
  68. _isMessageComplete = (errorCode != Interop.mincore.Errors.ERROR_MORE_DATA);
  69. Debug.Assert(r >= 0, "PipeStream's ReadCore is likely broken.");
  70. return r;
  71. }
  72. [SecuritySafeCritical]
  73. private Task<int> ReadAsyncCore(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
  74. {
  75. var completionSource = new ReadWriteCompletionSource(this, buffer, cancellationToken, isWrite: false);
  76. // Queue an async ReadFile operation and pass in a packed overlapped
  77. int errorCode = 0;
  78. int r;
  79. unsafe
  80. {
  81. r = ReadFileNative(_handle, buffer, offset, count, completionSource.Overlapped, out errorCode);
  82. }
  83. // ReadFile, the OS version, will return 0 on failure, but this ReadFileNative wrapper
  84. // returns -1. This will return the following:
  85. // - On error, r==-1.
  86. // - On async requests that are still pending, r==-1 w/ hr==ERROR_IO_PENDING
  87. // - On async requests that completed sequentially, r==0
  88. //
  89. // You will NEVER RELIABLY be able to get the number of buffer read back from this call
  90. // when using overlapped structures! You must not pass in a non-null lpNumBytesRead to
  91. // ReadFile when using overlapped structures! This is by design NT behavior.
  92. if (r == -1)
  93. {
  94. switch (errorCode)
  95. {
  96. // One side has closed its handle or server disconnected.
  97. // Set the state to Broken and do some cleanup work
  98. case Interop.mincore.Errors.ERROR_BROKEN_PIPE:
  99. case Interop.mincore.Errors.ERROR_PIPE_NOT_CONNECTED:
  100. State = PipeState.Broken;
  101. unsafe
  102. {
  103. // Clear the overlapped status bit for this special case. Failure to do so looks
  104. // like we are freeing a pending overlapped.
  105. completionSource.Overlapped->InternalLow = IntPtr.Zero;
  106. }
  107. completionSource.ReleaseResources();
  108. UpdateMessageCompletion(true);
  109. return s_zeroTask;
  110. case Interop.mincore.Errors.ERROR_IO_PENDING:
  111. break;
  112. default:
  113. throw Win32Marshal.GetExceptionForWin32Error(errorCode);
  114. }
  115. }
  116. completionSource.RegisterForCancellation();
  117. return completionSource.Task;
  118. }
  119. [SecurityCritical]
  120. private unsafe void WriteCore(byte[] buffer, int offset, int count)
  121. {
  122. int errorCode = 0;
  123. int r = WriteFileNative(_handle, buffer, offset, count, null, out errorCode);
  124. if (r == -1)
  125. {
  126. throw WinIOError(errorCode);
  127. }
  128. Debug.Assert(r >= 0, "PipeStream's WriteCore is likely broken.");
  129. }
  130. [SecuritySafeCritical]
  131. private Task WriteAsyncCore(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
  132. {
  133. var completionSource = new ReadWriteCompletionSource(this, buffer, cancellationToken, isWrite: true);
  134. int errorCode = 0;
  135. // Queue an async WriteFile operation and pass in a packed overlapped
  136. int r;
  137. unsafe
  138. {
  139. r = WriteFileNative(_handle, buffer, offset, count, completionSource.Overlapped, out errorCode);
  140. }
  141. // WriteFile, the OS version, will return 0 on failure, but this WriteFileNative
  142. // wrapper returns -1. This will return the following:
  143. // - On error, r==-1.
  144. // - On async requests that are still pending, r==-1 w/ hr==ERROR_IO_PENDING
  145. // - On async requests that completed sequentially, r==0
  146. //
  147. // You will NEVER RELIABLY be able to get the number of buffer written back from this
  148. // call when using overlapped structures! You must not pass in a non-null
  149. // lpNumBytesWritten to WriteFile when using overlapped structures! This is by design
  150. // NT behavior.
  151. if (r == -1 && errorCode != Interop.mincore.Errors.ERROR_IO_PENDING)
  152. {
  153. completionSource.ReleaseResources();
  154. throw WinIOError(errorCode);
  155. }
  156. completionSource.RegisterForCancellation();
  157. return completionSource.Task;
  158. }
  159. // Blocks until the other end of the pipe has read in all written buffer.
  160. [SecurityCritical]
  161. public void WaitForPipeDrain()
  162. {
  163. CheckWriteOperations();
  164. if (!CanWrite)
  165. {
  166. throw Error.GetWriteNotSupported();
  167. }
  168. // Block until other end of the pipe has read everything.
  169. if (!Interop.mincore.FlushFileBuffers(_handle))
  170. {
  171. throw WinIOError(Marshal.GetLastWin32Error());
  172. }
  173. }
  174. // Gets the transmission mode for the pipe. This is virtual so that subclassing types can
  175. // override this in cases where only one mode is legal (such as anonymous pipes)
  176. public virtual PipeTransmissionMode TransmissionMode
  177. {
  178. [SecurityCritical]
  179. [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Security model of pipes: demand at creation but no subsequent demands")]
  180. get
  181. {
  182. CheckPipePropertyOperations();
  183. if (_isFromExistingHandle)
  184. {
  185. int pipeFlags;
  186. if (!Interop.mincore.GetNamedPipeInfo(_handle, out pipeFlags, IntPtr.Zero, IntPtr.Zero,
  187. IntPtr.Zero))
  188. {
  189. throw WinIOError(Marshal.GetLastWin32Error());
  190. }
  191. if ((pipeFlags & Interop.mincore.PipeOptions.PIPE_TYPE_MESSAGE) != 0)
  192. {
  193. return PipeTransmissionMode.Message;
  194. }
  195. else
  196. {
  197. return PipeTransmissionMode.Byte;
  198. }
  199. }
  200. else
  201. {
  202. return _transmissionMode;
  203. }
  204. }
  205. }
  206. // Gets the buffer size in the inbound direction for the pipe. This checks if pipe has read
  207. // access. If that passes, call to GetNamedPipeInfo will succeed.
  208. public virtual int InBufferSize
  209. {
  210. [SecurityCritical]
  211. [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")]
  212. get
  213. {
  214. CheckPipePropertyOperations();
  215. if (!CanRead)
  216. {
  217. throw new NotSupportedException(SR.NotSupported_UnreadableStream);
  218. }
  219. int inBufferSize;
  220. if (!Interop.mincore.GetNamedPipeInfo(_handle, IntPtr.Zero, IntPtr.Zero, out inBufferSize, IntPtr.Zero))
  221. {
  222. throw WinIOError(Marshal.GetLastWin32Error());
  223. }
  224. return inBufferSize;
  225. }
  226. }
  227. // Gets the buffer size in the outbound direction for the pipe. This uses cached version
  228. // if it's an outbound only pipe because GetNamedPipeInfo requires read access to the pipe.
  229. // However, returning cached is good fallback, especially if user specified a value in
  230. // the ctor.
  231. public virtual int OutBufferSize
  232. {
  233. [SecurityCritical]
  234. [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Security model of pipes: demand at creation but no subsequent demands")]
  235. get
  236. {
  237. CheckPipePropertyOperations();
  238. if (!CanWrite)
  239. {
  240. throw new NotSupportedException(SR.NotSupported_UnwritableStream);
  241. }
  242. int outBufferSize;
  243. // Use cached value if direction is out; otherwise get fresh version
  244. if (_pipeDirection == PipeDirection.Out)
  245. {
  246. outBufferSize = _outBufferSize;
  247. }
  248. else if (!Interop.mincore.GetNamedPipeInfo(_handle, IntPtr.Zero, out outBufferSize,
  249. IntPtr.Zero, IntPtr.Zero))
  250. {
  251. throw WinIOError(Marshal.GetLastWin32Error());
  252. }
  253. return outBufferSize;
  254. }
  255. }
  256. public virtual PipeTransmissionMode ReadMode
  257. {
  258. [SecurityCritical]
  259. get
  260. {
  261. CheckPipePropertyOperations();
  262. // get fresh value if it could be stale
  263. if (_isFromExistingHandle || IsHandleExposed)
  264. {
  265. UpdateReadMode();
  266. }
  267. return _readMode;
  268. }
  269. [SecurityCritical]
  270. [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Security model of pipes: demand at creation but no subsequent demands")]
  271. set
  272. {
  273. // Nothing fancy here. This is just a wrapper around the Win32 API. Note, that NamedPipeServerStream
  274. // and the AnonymousPipeStreams override this.
  275. CheckPipePropertyOperations();
  276. if (value < PipeTransmissionMode.Byte || value > PipeTransmissionMode.Message)
  277. {
  278. throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_TransmissionModeByteOrMsg);
  279. }
  280. unsafe
  281. {
  282. int pipeReadType = (int)value << 1;
  283. if (!Interop.mincore.SetNamedPipeHandleState(_handle, &pipeReadType, IntPtr.Zero, IntPtr.Zero))
  284. {
  285. throw WinIOError(Marshal.GetLastWin32Error());
  286. }
  287. else
  288. {
  289. _readMode = value;
  290. }
  291. }
  292. }
  293. }
  294. // -----------------------------
  295. // ---- PAL layer ends here ----
  296. // -----------------------------
  297. [SecurityCritical]
  298. private unsafe int ReadFileNative(SafePipeHandle handle, byte[] buffer, int offset, int count,
  299. NativeOverlapped* overlapped, out int errorCode)
  300. {
  301. DebugAssertReadWriteArgs(buffer, offset, count, handle);
  302. Debug.Assert((_isAsync && overlapped != null) || (!_isAsync && overlapped == null), "Async IO parameter screwup in call to ReadFileNative.");
  303. // You can't use the fixed statement on an array of length 0. Note that async callers
  304. // check to avoid calling this first, so they can call user's callback
  305. if (buffer.Length == 0)
  306. {
  307. errorCode = 0;
  308. return 0;
  309. }
  310. int r = 0;
  311. int numBytesRead = 0;
  312. fixed (byte* p = buffer)
  313. {
  314. if (_isAsync)
  315. {
  316. r = Interop.mincore.ReadFile(handle, p + offset, count, IntPtr.Zero, overlapped);
  317. }
  318. else
  319. {
  320. r = Interop.mincore.ReadFile(handle, p + offset, count, out numBytesRead, IntPtr.Zero);
  321. }
  322. }
  323. if (r == 0)
  324. {
  325. errorCode = Marshal.GetLastWin32Error();
  326. // In message mode, the ReadFile can inform us that there is more data to come.
  327. if (errorCode == Interop.mincore.Errors.ERROR_MORE_DATA)
  328. {
  329. return numBytesRead;
  330. }
  331. return -1;
  332. }
  333. else
  334. {
  335. errorCode = 0;
  336. }
  337. return numBytesRead;
  338. }
  339. [SecurityCritical]
  340. private unsafe int WriteFileNative(SafePipeHandle handle, byte[] buffer, int offset, int count,
  341. NativeOverlapped* overlapped, out int errorCode)
  342. {
  343. DebugAssertReadWriteArgs(buffer, offset, count, handle);
  344. Debug.Assert((_isAsync && overlapped != null) || (!_isAsync && overlapped == null), "Async IO parameter screwup in call to WriteFileNative.");
  345. // You can't use the fixed statement on an array of length 0. Note that async callers
  346. // check to avoid calling this first, so they can call user's callback
  347. if (buffer.Length == 0)
  348. {
  349. errorCode = 0;
  350. return 0;
  351. }
  352. int numBytesWritten = 0;
  353. int r = 0;
  354. fixed (byte* p = buffer)
  355. {
  356. if (_isAsync)
  357. {
  358. r = Interop.mincore.WriteFile(handle, p + offset, count, IntPtr.Zero, overlapped);
  359. }
  360. else
  361. {
  362. r = Interop.mincore.WriteFile(handle, p + offset, count, out numBytesWritten, IntPtr.Zero);
  363. }
  364. }
  365. if (r == 0)
  366. {
  367. errorCode = Marshal.GetLastWin32Error();
  368. return -1;
  369. }
  370. else
  371. {
  372. errorCode = 0;
  373. }
  374. return numBytesWritten;
  375. }
  376. [SecurityCritical]
  377. internal unsafe static Interop.mincore.SECURITY_ATTRIBUTES GetSecAttrs(HandleInheritability inheritability)
  378. {
  379. Interop.mincore.SECURITY_ATTRIBUTES secAttrs = default(Interop.mincore.SECURITY_ATTRIBUTES);
  380. if ((inheritability & HandleInheritability.Inheritable) != 0)
  381. {
  382. secAttrs = new Interop.mincore.SECURITY_ATTRIBUTES();
  383. secAttrs.nLength = (uint)sizeof(Interop.mincore.SECURITY_ATTRIBUTES);
  384. secAttrs.bInheritHandle = Interop.BOOL.TRUE;
  385. }
  386. return secAttrs;
  387. }
  388. /// <summary>
  389. /// Determine pipe read mode from Win32
  390. /// </summary>
  391. [SecurityCritical]
  392. private void UpdateReadMode()
  393. {
  394. int flags;
  395. if (!Interop.mincore.GetNamedPipeHandleState(SafePipeHandle, out flags, IntPtr.Zero, IntPtr.Zero,
  396. IntPtr.Zero, IntPtr.Zero, 0))
  397. {
  398. throw WinIOError(Marshal.GetLastWin32Error());
  399. }
  400. if ((flags & Interop.mincore.PipeOptions.PIPE_READMODE_MESSAGE) != 0)
  401. {
  402. _readMode = PipeTransmissionMode.Message;
  403. }
  404. else
  405. {
  406. _readMode = PipeTransmissionMode.Byte;
  407. }
  408. }
  409. /// <summary>
  410. /// Filter out all pipe related errors and do some cleanup before calling Error.WinIOError.
  411. /// </summary>
  412. /// <param name="errorCode"></param>
  413. [SecurityCritical]
  414. internal Exception WinIOError(int errorCode)
  415. {
  416. switch (errorCode)
  417. {
  418. case Interop.mincore.Errors.ERROR_BROKEN_PIPE:
  419. case Interop.mincore.Errors.ERROR_PIPE_NOT_CONNECTED:
  420. case Interop.mincore.Errors.ERROR_NO_DATA:
  421. // Other side has broken the connection
  422. _state = PipeState.Broken;
  423. return new IOException(SR.IO_PipeBroken, Win32Marshal.MakeHRFromErrorCode(errorCode));
  424. case Interop.mincore.Errors.ERROR_HANDLE_EOF:
  425. return Error.GetEndOfFile();
  426. case Interop.mincore.Errors.ERROR_INVALID_HANDLE:
  427. // For invalid handles, detect the error and mark our handle
  428. // as invalid to give slightly better error messages. Also
  429. // help ensure we avoid handle recycling bugs.
  430. _handle.SetHandleAsInvalid();
  431. _state = PipeState.Broken;
  432. break;
  433. }
  434. return Win32Marshal.GetExceptionForWin32Error(errorCode);
  435. }
  436. }
  437. }