pipe.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855
  1. /*++
  2. Copyright (c) 2013 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. pipe.c
  5. Abstract:
  6. This module implements support for pipes.
  7. Author:
  8. Evan Green 25-Apr-2013
  9. Environment:
  10. Kernel
  11. --*/
  12. //
  13. // ------------------------------------------------------------------- Includes
  14. //
  15. #include <minoca/kernel/kernel.h>
  16. #include "iop.h"
  17. //
  18. // ---------------------------------------------------------------- Definitions
  19. //
  20. //
  21. // Define pipe flags.
  22. //
  23. //
  24. // This flag is set if the pipe has a name in the object manager directory.
  25. // Note that normal named pipes coming from the file system do not have this
  26. // flag set.
  27. //
  28. #define PIPE_FLAG_OBJECT_NAMED 0x00000001
  29. //
  30. // ------------------------------------------------------ Data Type Definitions
  31. //
  32. /*++
  33. Structure Description:
  34. This structure defines a data pipe.
  35. Members:
  36. Header - Stores the standard object header.
  37. Flags - Stores the flags used when the pipe was created.
  38. StreamBuffer - Stores a pointer to the stream buffer backing the pipe.
  39. ReaderCount - Stores the number of readers that have the pipe open.
  40. WriterCount - Stores the number of writers that have the pipe open.
  41. --*/
  42. typedef struct _PIPE {
  43. OBJECT_HEADER Header;
  44. ULONG Flags;
  45. PSTREAM_BUFFER StreamBuffer;
  46. ULONG ReaderCount;
  47. ULONG WriterCount;
  48. } PIPE, *PPIPE;
  49. /*++
  50. Structure Description:
  51. This structure defines the parameters needed to create a pipe.
  52. Members:
  53. BufferSize - Stores the suggested size for the internal stream buffer.
  54. Supply 0 to use the system default size.
  55. --*/
  56. typedef struct _PIPE_CREATION_PARAMETERS {
  57. ULONG BufferSize;
  58. } PIPE_CREATION_PARAMETERS, *PPIPE_CREATION_PARAMETERS;
  59. //
  60. // ----------------------------------------------- Internal Function Prototypes
  61. //
  62. VOID
  63. IopDestroyPipe (
  64. PVOID PipeObject
  65. );
  66. //
  67. // -------------------------------------------------------------------- Globals
  68. //
  69. //
  70. // Store a pointer to the pipes directory.
  71. //
  72. POBJECT_HEADER IoPipeDirectory;
  73. //
  74. // ------------------------------------------------------------------ Functions
  75. //
  76. KERNEL_API
  77. KSTATUS
  78. IoCreatePipe (
  79. BOOL FromKernelMode,
  80. PIO_HANDLE Directory,
  81. PSTR Path,
  82. ULONG PathLength,
  83. ULONG OpenFlags,
  84. FILE_PERMISSIONS CreatePermissions,
  85. PIO_HANDLE *ReadHandle,
  86. PIO_HANDLE *WriteHandle
  87. )
  88. /*++
  89. Routine Description:
  90. This routine creates and opens a new pipe.
  91. Arguments:
  92. FromKernelMode - Supplies a boolean indicating whether this request is
  93. originating from kernel mode (and should use the root path as a base)
  94. or user mode.
  95. Directory - Supplies an optional pointer to an open handle to a directory
  96. for relative paths. Supply NULL to use the current working directory.
  97. Path - Supplies an optional pointer to the path to open.
  98. PathLength - Supplies the length of the path buffer in bytes, including the
  99. null terminator.
  100. OpenFlags - Supplies the open flags for the pipe. See OPEN_FLAG_*
  101. definitions. OPEN_FLAG_CREATE and OPEN_FLAG_FAIL_IF_EXISTS are
  102. automatically applied.
  103. CreatePermissions - Supplies the permissions to apply to the created pipe.
  104. ReadHandle - Supplies a pointer where a handle to the read side of the pipe
  105. will be returned.
  106. WriteHandle - Supplies a pointer where a handle to the write side of the
  107. pipe will be returned.
  108. Return Value:
  109. Status code.
  110. --*/
  111. {
  112. KSTATUS Status;
  113. *ReadHandle = NULL;
  114. *WriteHandle = NULL;
  115. //
  116. // Create and open the read side.
  117. //
  118. Status = IopOpen(FromKernelMode,
  119. Directory,
  120. Path,
  121. PathLength,
  122. IO_ACCESS_READ,
  123. OpenFlags | OPEN_FLAG_CREATE | OPEN_FLAG_FAIL_IF_EXISTS,
  124. IoObjectPipe,
  125. NULL,
  126. CreatePermissions,
  127. ReadHandle);
  128. if (!KSUCCESS(Status)) {
  129. goto CreatePipeEnd;
  130. }
  131. //
  132. // Also open the write side.
  133. //
  134. Status = IopOpenPathPoint(&((*ReadHandle)->PathPoint),
  135. IO_ACCESS_WRITE,
  136. OpenFlags,
  137. WriteHandle);
  138. if (!KSUCCESS(Status)) {
  139. goto CreatePipeEnd;
  140. }
  141. CreatePipeEnd:
  142. if (!KSUCCESS(Status)) {
  143. if (*ReadHandle != NULL) {
  144. IoClose(*ReadHandle);
  145. *ReadHandle = NULL;
  146. }
  147. if (*WriteHandle != NULL) {
  148. IoClose(*WriteHandle);
  149. *WriteHandle = NULL;
  150. }
  151. }
  152. return Status;
  153. }
  154. POBJECT_HEADER
  155. IopGetPipeDirectory (
  156. VOID
  157. )
  158. /*++
  159. Routine Description:
  160. This routine returns the pipe root directory in the object system. This is
  161. the only place in the object system pipe creation is allowed.
  162. Arguments:
  163. None.
  164. Return Value:
  165. Returns a pointer to the pipe directory.
  166. --*/
  167. {
  168. return IoPipeDirectory;
  169. }
  170. KSTATUS
  171. IopCreatePipe (
  172. PSTR Name,
  173. ULONG NameSize,
  174. FILE_PERMISSIONS Permissions,
  175. PFILE_OBJECT *FileObject
  176. )
  177. /*++
  178. Routine Description:
  179. This routine actually creates a new pipe.
  180. Arguments:
  181. Name - Supplies an optional pointer to the pipe name. This is only used for
  182. named pipes created in the pipe directory.
  183. NameSize - Supplies the size of the name in bytes including the null
  184. terminator.
  185. Permissions - Supplies the permissions to give to the file object.
  186. FileObject - Supplies a pointer where a pointer to a newly created pipe
  187. file object will be returned on success.
  188. Return Value:
  189. Status code.
  190. --*/
  191. {
  192. BOOL Created;
  193. PPIPE ExistingPipe;
  194. FILE_PROPERTIES FileProperties;
  195. PFILE_OBJECT NewFileObject;
  196. PPIPE NewPipe;
  197. KSTATUS Status;
  198. PKTHREAD Thread;
  199. NewFileObject = NULL;
  200. NewPipe = NULL;
  201. //
  202. // Make sure there is not already an existing pipe by the same name. The
  203. // caller should have the appropriate locks to make the check and create
  204. // synchronous.
  205. //
  206. if (Name != NULL) {
  207. ExistingPipe = ObFindObject(Name, NameSize, IoPipeDirectory);
  208. if (ExistingPipe != NULL) {
  209. ObReleaseReference(ExistingPipe);
  210. Status = STATUS_FILE_EXISTS;
  211. goto CreatePipeEnd;
  212. }
  213. }
  214. //
  215. // Create the actual object. This reference is transferred to the file
  216. // object's special I/O member on success.
  217. //
  218. NewPipe = ObCreateObject(ObjectPipe,
  219. IoPipeDirectory,
  220. Name,
  221. NameSize,
  222. sizeof(PIPE),
  223. IopDestroyPipe,
  224. 0,
  225. IO_ALLOCATION_TAG);
  226. if (NewPipe == NULL) {
  227. Status = STATUS_INSUFFICIENT_RESOURCES;
  228. goto CreatePipeEnd;
  229. }
  230. //
  231. // Record if the pipe got a name in the pipe directory.
  232. //
  233. if (Name != NULL) {
  234. NewPipe->Flags |= PIPE_FLAG_OBJECT_NAMED;
  235. }
  236. //
  237. // Create a file object if needed.
  238. //
  239. if (*FileObject == NULL) {
  240. Thread = KeGetCurrentThread();
  241. IopFillOutFilePropertiesForObject(&FileProperties, &(NewPipe->Header));
  242. FileProperties.Permissions = Permissions;
  243. FileProperties.Type = IoObjectPipe;
  244. FileProperties.UserId = Thread->Identity.EffectiveUserId;
  245. FileProperties.GroupId = Thread->Identity.EffectiveGroupId;
  246. Status = IopCreateOrLookupFileObject(&FileProperties,
  247. ObGetRootObject(),
  248. 0,
  249. &NewFileObject,
  250. &Created);
  251. if (!KSUCCESS(Status)) {
  252. //
  253. // Release the references added by filling out the file properties.
  254. //
  255. ObReleaseReference(NewPipe);
  256. goto CreatePipeEnd;
  257. }
  258. ASSERT(Created != FALSE);
  259. *FileObject = NewFileObject;
  260. }
  261. ASSERT((*FileObject)->Properties.Type == IoObjectPipe);
  262. //
  263. // Now fill in the pipe with the I/O object state.
  264. //
  265. ASSERT((*FileObject)->IoState != NULL);
  266. NewPipe->StreamBuffer = IoCreateStreamBuffer((*FileObject)->IoState,
  267. 0,
  268. 0,
  269. PIPE_ATOMIC_WRITE_SIZE);
  270. if (NewPipe->StreamBuffer == NULL) {
  271. Status = STATUS_INSUFFICIENT_RESOURCES;
  272. goto CreatePipeEnd;
  273. }
  274. //
  275. // Now that the pipe's ready, release anyone else who happened to find this
  276. // file object in the mean time.
  277. //
  278. ASSERT(((*FileObject)->SpecialIo == NULL) &&
  279. ((KeGetEventState((*FileObject)->ReadyEvent) == NotSignaled) ||
  280. (KeGetEventState((*FileObject)->ReadyEvent) ==
  281. NotSignaledWithWaiters)));
  282. (*FileObject)->SpecialIo = NewPipe;
  283. NewPipe = NULL;
  284. Status = STATUS_SUCCESS;
  285. CreatePipeEnd:
  286. //
  287. // On both success and failure, the file object's ready event needs to be
  288. // signaled. Other threads may be waiting on the event.
  289. //
  290. if (*FileObject != NULL) {
  291. KeSignalEvent((*FileObject)->ReadyEvent, SignalOptionSignalAll);
  292. }
  293. if (!KSUCCESS(Status)) {
  294. if (NewFileObject != NULL) {
  295. *FileObject = NULL;
  296. IopFileObjectReleaseReference(NewFileObject);
  297. }
  298. if (NewPipe != NULL) {
  299. ObReleaseReference(NewPipe);
  300. NewPipe = NULL;
  301. }
  302. }
  303. return Status;
  304. }
  305. KSTATUS
  306. IopUnlinkPipe (
  307. PFILE_OBJECT FileObject,
  308. PBOOL Unlinked
  309. )
  310. /*++
  311. Routine Description:
  312. This routine unlinks a pipe from the accessible namespace.
  313. Arguments:
  314. FileObject - Supplies a pointer to the pipe's file object.
  315. Unlinked - Supplies a pointer to a boolean that receives whether or not the
  316. terminal was successfully unlinked.
  317. Return Value:
  318. Status code.
  319. --*/
  320. {
  321. PPIPE Pipe;
  322. KSTATUS Status;
  323. ASSERT(FileObject->Properties.Type == IoObjectPipe);
  324. ASSERT(KeIsSharedExclusiveLockHeldExclusive(FileObject->Lock) != FALSE);
  325. Pipe = FileObject->SpecialIo;
  326. ASSERT(Pipe != NULL);
  327. ASSERT((Pipe->Flags & PIPE_FLAG_OBJECT_NAMED) != 0);
  328. *Unlinked = FALSE;
  329. Status = ObUnlinkObject(Pipe);
  330. if (KSUCCESS(Status)) {
  331. *Unlinked = TRUE;
  332. }
  333. return Status;
  334. }
  335. KSTATUS
  336. IopOpenPipe (
  337. PIO_HANDLE IoHandle
  338. )
  339. /*++
  340. Routine Description:
  341. This routine is called when a pipe is opened.
  342. Arguments:
  343. IoHandle - Supplies a pointer to the new I/O handle.
  344. Return Value:
  345. Status code.
  346. --*/
  347. {
  348. PFILE_OBJECT FileObject;
  349. PIO_OBJECT_STATE IoState;
  350. BOOL NonBlocking;
  351. PPIPE Pipe;
  352. BOOL PipeOpened;
  353. ULONG ReturnedEvents;
  354. KSTATUS Status;
  355. PipeOpened = FALSE;
  356. FileObject = IoHandle->FileObject;
  357. ASSERT(FileObject->Properties.Type == IoObjectPipe);
  358. KeAcquireSharedExclusiveLockExclusive(FileObject->Lock);
  359. Pipe = FileObject->SpecialIo;
  360. if (Pipe == NULL) {
  361. ASSERT(FALSE);
  362. Status = STATUS_TOO_LATE;
  363. goto OpenPipeEnd;
  364. }
  365. if ((IoHandle->Access & IO_ACCESS_EXECUTE) != 0) {
  366. Status = STATUS_INVALID_PARAMETER;
  367. goto OpenPipeEnd;
  368. }
  369. IoState = IoStreamBufferGetIoObjectState(Pipe->StreamBuffer);
  370. if ((IoHandle->Access & IO_ACCESS_READ) != 0) {
  371. Pipe->ReaderCount += 1;
  372. //
  373. // Clear the error event.
  374. //
  375. IoSetIoObjectState(IoState, POLL_EVENT_ERROR, FALSE);
  376. }
  377. if ((IoHandle->Access & IO_ACCESS_WRITE) != 0) {
  378. Pipe->WriterCount += 1;
  379. //
  380. // Clear the disconnect event.
  381. //
  382. IoSetIoObjectState(IoState, POLL_EVENT_DISCONNECTED, FALSE);
  383. }
  384. PipeOpened = TRUE;
  385. //
  386. // Determine whether this is a blocking or non-blocking open. The initial
  387. // create/open call is also set non-blocking, and this relies a bit on the
  388. // fact that the read end is opened first.
  389. //
  390. NonBlocking = FALSE;
  391. if (((IoHandle->OpenFlags & OPEN_FLAG_NON_BLOCKING) != 0) ||
  392. ((IoHandle->OpenFlags &
  393. (OPEN_FLAG_CREATE | OPEN_FLAG_FAIL_IF_EXISTS)) ==
  394. (OPEN_FLAG_CREATE | OPEN_FLAG_FAIL_IF_EXISTS))) {
  395. NonBlocking = TRUE;
  396. }
  397. //
  398. // In non-blocking mode, open access for write only returns an error if no
  399. // process currently has the pipe open for reading.
  400. //
  401. if (NonBlocking != FALSE) {
  402. if (((IoHandle->Access & IO_ACCESS_WRITE) != 0) &&
  403. (Pipe->ReaderCount == 0)) {
  404. Status = STATUS_NO_SUCH_DEVICE_OR_ADDRESS;
  405. goto OpenPipeEnd;
  406. }
  407. //
  408. // Handle a blocking open on a pipe, which blocks until the other end
  409. // connects.
  410. //
  411. } else {
  412. //
  413. // If there's no jelly for your peanut buffer, wait for some to arrive.
  414. // Borrow the write event to block on.
  415. //
  416. if ((((IoHandle->Access & IO_ACCESS_WRITE) != 0) &&
  417. (Pipe->ReaderCount == 0)) ||
  418. (((IoHandle->Access & IO_ACCESS_READ) != 0) &&
  419. (Pipe->WriterCount == 0))) {
  420. IoSetIoObjectState(IoState, POLL_EVENT_OUT, FALSE);
  421. KeReleaseSharedExclusiveLockExclusive(FileObject->Lock);
  422. Status = IoWaitForIoObjectState(IoState,
  423. POLL_EVENT_OUT | POLL_EVENT_ERROR,
  424. TRUE,
  425. WAIT_TIME_INDEFINITE,
  426. &ReturnedEvents);
  427. KeAcquireSharedExclusiveLockExclusive(FileObject->Lock);
  428. if (!KSUCCESS(Status)) {
  429. goto OpenPipeEnd;
  430. }
  431. if ((ReturnedEvents & POLL_EVENT_OUT) == 0) {
  432. Status = STATUS_NOT_READY;
  433. goto OpenPipeEnd;
  434. }
  435. }
  436. }
  437. //
  438. // Reset the I/O object state, which sets the in and out poll events
  439. // properly.
  440. //
  441. Status = IoStreamBufferConnect(Pipe->StreamBuffer);
  442. if (!KSUCCESS(Status)) {
  443. goto OpenPipeEnd;
  444. }
  445. Status = STATUS_SUCCESS;
  446. OpenPipeEnd:
  447. KeReleaseSharedExclusiveLockExclusive(FileObject->Lock);
  448. if (!KSUCCESS(Status)) {
  449. if (PipeOpened != FALSE) {
  450. IopClosePipe(IoHandle);
  451. }
  452. }
  453. return Status;
  454. }
  455. KSTATUS
  456. IopClosePipe (
  457. PIO_HANDLE IoHandle
  458. )
  459. /*++
  460. Routine Description:
  461. This routine is called when a pipe is closed.
  462. Arguments:
  463. IoHandle - Supplies a pointer to the I/O handle being closed.
  464. Return Value:
  465. Status code.
  466. --*/
  467. {
  468. PFILE_OBJECT FileObject;
  469. PFILE_PROPERTIES FileProperties;
  470. PIO_OBJECT_STATE IoState;
  471. BOOL LockHeld;
  472. PPIPE Pipe;
  473. FileObject = IoHandle->FileObject;
  474. FileProperties = &(FileObject->Properties);
  475. ASSERT(FileProperties->Type == IoObjectPipe);
  476. KeAcquireSharedExclusiveLockExclusive(FileObject->Lock);
  477. LockHeld = TRUE;
  478. Pipe = FileObject->SpecialIo;
  479. IoState = IoStreamBufferGetIoObjectState(Pipe->StreamBuffer);
  480. if ((IoHandle->Access & IO_ACCESS_READ) != 0) {
  481. Pipe->ReaderCount -= 1;
  482. if (Pipe->ReaderCount == 0) {
  483. //
  484. // The last reader just closed, so clear the hangup event and the
  485. // out event. Set the error event.
  486. //
  487. IoSetIoObjectState(IoState,
  488. POLL_EVENT_OUT | POLL_EVENT_DISCONNECTED,
  489. FALSE);
  490. IoSetIoObjectState(IoState, POLL_EVENT_ERROR, TRUE);
  491. }
  492. }
  493. if ((IoHandle->Access & IO_ACCESS_WRITE) != 0) {
  494. Pipe->WriterCount -= 1;
  495. if (Pipe->WriterCount == 0) {
  496. //
  497. // Clear the out event, set the hangup event, and set the read
  498. // event.
  499. //
  500. IoSetIoObjectState(IoState, POLL_EVENT_OUT, FALSE);
  501. IoSetIoObjectState(IoState,
  502. POLL_EVENT_DISCONNECTED | POLL_EVENT_IN,
  503. TRUE);
  504. }
  505. }
  506. //
  507. // Pipes that are named in the object directory need to be unlinked on
  508. // the last close. Check to see if the reader and writer counts are both
  509. // zero. If so, unlink the object. It may be that another thread is about
  510. // to open the pipe for read and/or write. This is OK, it's got a reference
  511. // on the file object and can proceed without concern. When it closes the
  512. // pipe it will attempt the unlink again, but that's fine. No new lookups
  513. // can occur after the first unlink attempt.
  514. //
  515. if ((Pipe->Flags & PIPE_FLAG_OBJECT_NAMED) != 0) {
  516. if ((Pipe->WriterCount == 0) && (Pipe->ReaderCount == 0)) {
  517. KeReleaseSharedExclusiveLockExclusive(FileObject->Lock);
  518. LockHeld = FALSE;
  519. IopDeleteByHandle(TRUE, IoHandle, 0);
  520. }
  521. }
  522. if (LockHeld != FALSE) {
  523. KeReleaseSharedExclusiveLockExclusive(FileObject->Lock);
  524. }
  525. return STATUS_SUCCESS;
  526. }
  527. KSTATUS
  528. IopPerformPipeIoOperation (
  529. PIO_HANDLE Handle,
  530. PIO_CONTEXT IoContext
  531. )
  532. /*++
  533. Routine Description:
  534. This routine reads from or writes to a pipe.
  535. Arguments:
  536. Handle - Supplies a pointer to the pipe I/O handle.
  537. IoContext - Supplies a pointer to the I/O context.
  538. Return Value:
  539. Status code. A failing status code does not necessarily mean no I/O made it
  540. in or out. Check the bytes completed value in the I/O context to find out
  541. how much occurred.
  542. --*/
  543. {
  544. PFILE_OBJECT FileObject;
  545. BOOL NonBlocking;
  546. PPIPE Pipe;
  547. UINTN PipeBytesCompleted;
  548. KSTATUS Status;
  549. FileObject = Handle->FileObject;
  550. ASSERT(IoContext->IoBuffer != NULL);
  551. ASSERT(FileObject->Properties.Type == IoObjectPipe);
  552. Pipe = FileObject->SpecialIo;
  553. PipeBytesCompleted = 0;
  554. NonBlocking = FALSE;
  555. if (IoContext->Write != FALSE) {
  556. //
  557. // If there are no readers, send a pipe signal to the calling
  558. // application.
  559. //
  560. if (Pipe->ReaderCount == 0) {
  561. Status = STATUS_BROKEN_PIPE;
  562. } else {
  563. Status = IoWriteStreamBuffer(Pipe->StreamBuffer,
  564. IoContext->IoBuffer,
  565. IoContext->SizeInBytes,
  566. IoContext->TimeoutInMilliseconds,
  567. NonBlocking,
  568. &PipeBytesCompleted);
  569. }
  570. } else {
  571. if (Pipe->WriterCount == 0) {
  572. NonBlocking = TRUE;
  573. }
  574. Status = IoReadStreamBuffer(Pipe->StreamBuffer,
  575. IoContext->IoBuffer,
  576. IoContext->SizeInBytes,
  577. IoContext->TimeoutInMilliseconds,
  578. NonBlocking,
  579. &PipeBytesCompleted);
  580. if ((Status == STATUS_NO_DATA_AVAILABLE) && (Pipe->WriterCount == 0)) {
  581. ASSERT(PipeBytesCompleted == 0);
  582. Status = STATUS_END_OF_FILE;
  583. }
  584. }
  585. IoContext->BytesCompleted = PipeBytesCompleted;
  586. return Status;
  587. }
  588. //
  589. // --------------------------------------------------------- Internal Functions
  590. //
  591. VOID
  592. IopDestroyPipe (
  593. PVOID PipeObject
  594. )
  595. /*++
  596. Routine Description:
  597. This routine destroys all resources associated with a pipe.
  598. Arguments:
  599. PipeObject - Supplies a pointer to the pipe object being destroyed.
  600. Return Value:
  601. None.
  602. --*/
  603. {
  604. PPIPE Pipe;
  605. Pipe = (PPIPE)PipeObject;
  606. if (Pipe->StreamBuffer != NULL) {
  607. IoDestroyStreamBuffer(Pipe->StreamBuffer);
  608. }
  609. return;
  610. }