VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/win/pipe-win.cpp@ 96507

Last change on this file since 96507 was 96505, checked in by vboxsync, 2 years ago

IPRT: Dynamically resolve GetProcessAffinityMask, SetThreadAffinityMask, CreateIoCompletionPort, GetQueuedCompletionStatus and PostQueuedCompletionStatus too so related (ValKit) code loads on ancient NT versions. bugref:10261

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 55.4 KB
Line 
1/* $Id: pipe-win.cpp 96505 2022-08-25 22:44:04Z vboxsync $ */
2/** @file
3 * IPRT - Anonymous Pipes, Windows Implementation.
4 */
5
6/*
7 * Copyright (C) 2010-2022 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include <iprt/nt/nt-and-windows.h>
42
43#include <iprt/pipe.h>
44#include "internal/iprt.h"
45
46#include <iprt/asm.h>
47#include <iprt/assert.h>
48#include <iprt/critsect.h>
49#include <iprt/err.h>
50#include <iprt/log.h>
51#include <iprt/mem.h>
52#include <iprt/string.h>
53#include <iprt/poll.h>
54#include <iprt/process.h>
55#include <iprt/thread.h>
56#include <iprt/time.h>
57#include "internal/pipe.h"
58#include "internal/magics.h"
59#include "internal-r3-win.h"
60
61
62/*********************************************************************************************************************************
63* Defined Constants And Macros *
64*********************************************************************************************************************************/
65/** The pipe buffer size we prefer. */
66#define RTPIPE_NT_SIZE _64K
67
68
69/*********************************************************************************************************************************
70* Structures and Typedefs *
71*********************************************************************************************************************************/
72typedef struct RTPIPEINTERNAL
73{
74 /** Magic value (RTPIPE_MAGIC). */
75 uint32_t u32Magic;
76 /** The pipe handle. */
77 HANDLE hPipe;
78 /** Set if this is the read end, clear if it's the write end. */
79 bool fRead;
80 /** RTPipeFromNative: Leave native handle open on RTPipeClose. */
81 bool fLeaveOpen;
82 /** Set if there is already pending I/O. */
83 bool fIOPending;
84 /** Set if the zero byte read that the poll code using is pending. */
85 bool fZeroByteRead;
86 /** Set if the pipe is broken. */
87 bool fBrokenPipe;
88 /** Set if we've promised that the handle is writable. */
89 bool fPromisedWritable;
90 /** Set if created inheritable. */
91 bool fCreatedInheritable;
92 /** Usage counter. */
93 uint32_t cUsers;
94 /** The overlapped I/O structure we use. */
95 OVERLAPPED Overlapped;
96 /** Bounce buffer for writes. */
97 uint8_t *pbBounceBuf;
98 /** Amount of used buffer space. */
99 size_t cbBounceBufUsed;
100 /** Amount of allocated buffer space. */
101 size_t cbBounceBufAlloc;
102 /** The handle of the poll set currently polling on this pipe.
103 * We can only have one poller at the time (lazy bird). */
104 RTPOLLSET hPollSet;
105 /** Critical section protecting the above members.
106 * (Taking the lazy/simple approach.) */
107 RTCRITSECT CritSect;
108 /** Buffer for the zero byte read. */
109 uint8_t abBuf[8];
110} RTPIPEINTERNAL;
111
112
113
114/**
115 * Wrapper for getting FILE_PIPE_LOCAL_INFORMATION via the NT API.
116 *
117 * @returns Success indicator (true/false).
118 * @param pThis The pipe.
119 * @param pInfo The info structure.
120 */
121static bool rtPipeQueryNtInfo(RTPIPEINTERNAL *pThis, FILE_PIPE_LOCAL_INFORMATION *pInfo)
122{
123 IO_STATUS_BLOCK Ios;
124 RT_ZERO(Ios);
125 RT_ZERO(*pInfo);
126 NTSTATUS rcNt = NtQueryInformationFile(pThis->hPipe, &Ios, pInfo, sizeof(*pInfo), FilePipeLocalInformation);
127 return rcNt >= 0;
128}
129
130
131RTDECL(int) RTPipeCreate(PRTPIPE phPipeRead, PRTPIPE phPipeWrite, uint32_t fFlags)
132{
133 AssertPtrReturn(phPipeRead, VERR_INVALID_POINTER);
134 AssertPtrReturn(phPipeWrite, VERR_INVALID_POINTER);
135 AssertReturn(!(fFlags & ~RTPIPE_C_VALID_MASK), VERR_INVALID_PARAMETER);
136
137 /*
138 * Create the read end of the pipe.
139 */
140 DWORD dwErr;
141 HANDLE hPipeR;
142 HANDLE hPipeW;
143 int rc;
144 for (;;)
145 {
146 static volatile uint32_t g_iNextPipe = 0;
147 char szName[128];
148 RTStrPrintf(szName, sizeof(szName), "\\\\.\\pipe\\iprt-pipe-%u-%u", RTProcSelf(), ASMAtomicIncU32(&g_iNextPipe));
149
150 SECURITY_ATTRIBUTES SecurityAttributes;
151 PSECURITY_ATTRIBUTES pSecurityAttributes = NULL;
152 if (fFlags & RTPIPE_C_INHERIT_READ)
153 {
154 SecurityAttributes.nLength = sizeof(SecurityAttributes);
155 SecurityAttributes.lpSecurityDescriptor = NULL;
156 SecurityAttributes.bInheritHandle = TRUE;
157 pSecurityAttributes = &SecurityAttributes;
158 }
159
160 DWORD dwOpenMode = PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED;
161#ifdef FILE_FLAG_FIRST_PIPE_INSTANCE
162 dwOpenMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
163#endif
164
165 DWORD dwPipeMode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT;
166#ifdef PIPE_REJECT_REMOTE_CLIENTS
167 dwPipeMode |= PIPE_REJECT_REMOTE_CLIENTS;
168#endif
169
170 hPipeR = CreateNamedPipeA(szName, dwOpenMode, dwPipeMode, 1 /*nMaxInstances*/, RTPIPE_NT_SIZE, RTPIPE_NT_SIZE,
171 NMPWAIT_USE_DEFAULT_WAIT, pSecurityAttributes);
172#ifdef PIPE_REJECT_REMOTE_CLIENTS
173 if (hPipeR == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_PARAMETER)
174 {
175 dwPipeMode &= ~PIPE_REJECT_REMOTE_CLIENTS;
176 hPipeR = CreateNamedPipeA(szName, dwOpenMode, dwPipeMode, 1 /*nMaxInstances*/, RTPIPE_NT_SIZE, RTPIPE_NT_SIZE,
177 NMPWAIT_USE_DEFAULT_WAIT, pSecurityAttributes);
178 }
179#endif
180#ifdef FILE_FLAG_FIRST_PIPE_INSTANCE
181 if (hPipeR == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_PARAMETER)
182 {
183 dwOpenMode &= ~FILE_FLAG_FIRST_PIPE_INSTANCE;
184 hPipeR = CreateNamedPipeA(szName, dwOpenMode, dwPipeMode, 1 /*nMaxInstances*/, RTPIPE_NT_SIZE, RTPIPE_NT_SIZE,
185 NMPWAIT_USE_DEFAULT_WAIT, pSecurityAttributes);
186 }
187#endif
188 if (hPipeR != INVALID_HANDLE_VALUE)
189 {
190 /*
191 * Connect to the pipe (the write end).
192 * We add FILE_READ_ATTRIBUTES here to make sure we can query the
193 * pipe state later on.
194 */
195 pSecurityAttributes = NULL;
196 if (fFlags & RTPIPE_C_INHERIT_WRITE)
197 {
198 SecurityAttributes.nLength = sizeof(SecurityAttributes);
199 SecurityAttributes.lpSecurityDescriptor = NULL;
200 SecurityAttributes.bInheritHandle = TRUE;
201 pSecurityAttributes = &SecurityAttributes;
202 }
203
204 hPipeW = CreateFileA(szName,
205 GENERIC_WRITE | FILE_READ_ATTRIBUTES /*dwDesiredAccess*/,
206 0 /*dwShareMode*/,
207 pSecurityAttributes,
208 OPEN_EXISTING /* dwCreationDisposition */,
209 FILE_FLAG_OVERLAPPED /*dwFlagsAndAttributes*/,
210 NULL /*hTemplateFile*/);
211 if (hPipeW != INVALID_HANDLE_VALUE)
212 break;
213 dwErr = GetLastError();
214 CloseHandle(hPipeR);
215 }
216 else
217 dwErr = GetLastError();
218 if ( dwErr != ERROR_PIPE_BUSY /* already exist - compatible */
219 && dwErr != ERROR_ACCESS_DENIED /* already exist - incompatible */)
220 return RTErrConvertFromWin32(dwErr);
221 /* else: try again with a new name */
222 }
223
224 /*
225 * Create the two handles.
226 */
227 RTPIPEINTERNAL *pThisR = (RTPIPEINTERNAL *)RTMemAllocZ(sizeof(RTPIPEINTERNAL));
228 if (pThisR)
229 {
230 RTPIPEINTERNAL *pThisW = (RTPIPEINTERNAL *)RTMemAllocZ(sizeof(RTPIPEINTERNAL));
231 if (pThisW)
232 {
233 rc = RTCritSectInit(&pThisR->CritSect);
234 if (RT_SUCCESS(rc))
235 {
236 rc = RTCritSectInit(&pThisW->CritSect);
237 if (RT_SUCCESS(rc))
238 {
239 pThisR->Overlapped.hEvent = CreateEvent(NULL, TRUE /*fManualReset*/,
240 TRUE /*fInitialState*/, NULL /*pName*/);
241 if (pThisR->Overlapped.hEvent != NULL)
242 {
243 pThisW->Overlapped.hEvent = CreateEvent(NULL, TRUE /*fManualReset*/,
244 TRUE /*fInitialState*/, NULL /*pName*/);
245 if (pThisW->Overlapped.hEvent != NULL)
246 {
247 pThisR->u32Magic = RTPIPE_MAGIC;
248 pThisW->u32Magic = RTPIPE_MAGIC;
249 pThisR->hPipe = hPipeR;
250 pThisW->hPipe = hPipeW;
251 pThisR->fRead = true;
252 pThisW->fRead = false;
253 pThisR->fLeaveOpen = false;
254 pThisW->fLeaveOpen = false;
255 //pThisR->fIOPending = false;
256 //pThisW->fIOPending = false;
257 //pThisR->fZeroByteRead = false;
258 //pThisW->fZeroByteRead = false;
259 //pThisR->fBrokenPipe = false;
260 //pThisW->fBrokenPipe = false;
261 //pThisW->fPromisedWritable = false;
262 //pThisR->fPromisedWritable = false;
263 pThisW->fCreatedInheritable = RT_BOOL(fFlags & RTPIPE_C_INHERIT_WRITE);
264 pThisR->fCreatedInheritable = RT_BOOL(fFlags & RTPIPE_C_INHERIT_READ);
265 //pThisR->cUsers = 0;
266 //pThisW->cUsers = 0;
267 //pThisR->pbBounceBuf = NULL;
268 //pThisW->pbBounceBuf = NULL;
269 //pThisR->cbBounceBufUsed = 0;
270 //pThisW->cbBounceBufUsed = 0;
271 //pThisR->cbBounceBufAlloc = 0;
272 //pThisW->cbBounceBufAlloc = 0;
273 pThisR->hPollSet = NIL_RTPOLLSET;
274 pThisW->hPollSet = NIL_RTPOLLSET;
275
276 *phPipeRead = pThisR;
277 *phPipeWrite = pThisW;
278 return VINF_SUCCESS;
279 }
280 CloseHandle(pThisR->Overlapped.hEvent);
281 }
282 RTCritSectDelete(&pThisW->CritSect);
283 }
284 RTCritSectDelete(&pThisR->CritSect);
285 }
286 RTMemFree(pThisW);
287 }
288 else
289 rc = VERR_NO_MEMORY;
290 RTMemFree(pThisR);
291 }
292 else
293 rc = VERR_NO_MEMORY;
294
295 CloseHandle(hPipeR);
296 CloseHandle(hPipeW);
297 return rc;
298}
299
300
301/**
302 * Common worker for handling I/O completion.
303 *
304 * This is used by RTPipeClose, RTPipeWrite and RTPipeWriteBlocking.
305 *
306 * @returns IPRT status code.
307 * @param pThis The pipe instance handle.
308 */
309static int rtPipeWriteCheckCompletion(RTPIPEINTERNAL *pThis)
310{
311 int rc;
312 DWORD dwRc = WaitForSingleObject(pThis->Overlapped.hEvent, 0);
313 if (dwRc == WAIT_OBJECT_0)
314 {
315 DWORD cbWritten = 0;
316 if (GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbWritten, TRUE))
317 {
318 for (;;)
319 {
320 if (cbWritten >= pThis->cbBounceBufUsed)
321 {
322 pThis->fIOPending = false;
323 rc = VINF_SUCCESS;
324 break;
325 }
326
327 /* resubmit the remainder of the buffer - can this actually happen? */
328 memmove(&pThis->pbBounceBuf[0], &pThis->pbBounceBuf[cbWritten], pThis->cbBounceBufUsed - cbWritten);
329 rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
330 if (!WriteFile(pThis->hPipe, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed,
331 &cbWritten, &pThis->Overlapped))
332 {
333 if (GetLastError() == ERROR_IO_PENDING)
334 rc = VINF_TRY_AGAIN;
335 else
336 {
337 pThis->fIOPending = false;
338 if (GetLastError() == ERROR_NO_DATA)
339 rc = VERR_BROKEN_PIPE;
340 else
341 rc = RTErrConvertFromWin32(GetLastError());
342 if (rc == VERR_BROKEN_PIPE)
343 pThis->fBrokenPipe = true;
344 }
345 break;
346 }
347 Assert(cbWritten > 0);
348 }
349 }
350 else
351 {
352 pThis->fIOPending = false;
353 rc = RTErrConvertFromWin32(GetLastError());
354 }
355 }
356 else if (dwRc == WAIT_TIMEOUT)
357 rc = VINF_TRY_AGAIN;
358 else
359 {
360 pThis->fIOPending = false;
361 if (dwRc == WAIT_ABANDONED)
362 rc = VERR_INVALID_HANDLE;
363 else
364 rc = RTErrConvertFromWin32(GetLastError());
365 }
366 return rc;
367}
368
369
370
371RTDECL(int) RTPipeCloseEx(RTPIPE hPipe, bool fLeaveOpen)
372{
373 RTPIPEINTERNAL *pThis = hPipe;
374 if (pThis == NIL_RTPIPE)
375 return VINF_SUCCESS;
376 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
377 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
378
379 /*
380 * Do the cleanup.
381 */
382 AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, ~RTPIPE_MAGIC, RTPIPE_MAGIC), VERR_INVALID_HANDLE);
383 RTCritSectEnter(&pThis->CritSect);
384 Assert(pThis->cUsers == 0);
385
386 if (!pThis->fRead && pThis->fIOPending)
387 rtPipeWriteCheckCompletion(pThis);
388
389 if (!fLeaveOpen && !pThis->fLeaveOpen)
390 CloseHandle(pThis->hPipe);
391 pThis->hPipe = INVALID_HANDLE_VALUE;
392
393 CloseHandle(pThis->Overlapped.hEvent);
394 pThis->Overlapped.hEvent = NULL;
395
396 RTMemFree(pThis->pbBounceBuf);
397 pThis->pbBounceBuf = NULL;
398
399 RTCritSectLeave(&pThis->CritSect);
400 RTCritSectDelete(&pThis->CritSect);
401
402 RTMemFree(pThis);
403
404 return VINF_SUCCESS;
405}
406
407
408RTDECL(int) RTPipeClose(RTPIPE hPipe)
409{
410 return RTPipeCloseEx(hPipe, false /*fLeaveOpen*/);
411}
412
413
414RTDECL(int) RTPipeFromNative(PRTPIPE phPipe, RTHCINTPTR hNativePipe, uint32_t fFlags)
415{
416 AssertPtrReturn(phPipe, VERR_INVALID_POINTER);
417 AssertReturn(!(fFlags & ~RTPIPE_N_VALID_MASK_FN), VERR_INVALID_PARAMETER);
418 AssertReturn(!!(fFlags & RTPIPE_N_READ) != !!(fFlags & RTPIPE_N_WRITE), VERR_INVALID_PARAMETER);
419
420 /*
421 * Get and validate the pipe handle info.
422 */
423 HANDLE hNative = (HANDLE)hNativePipe;
424 AssertReturn(GetFileType(hNative) == FILE_TYPE_PIPE, VERR_INVALID_HANDLE);
425
426 DWORD cMaxInstances;
427 DWORD fInfo;
428 if (!GetNamedPipeInfo(hNative, &fInfo, NULL, NULL, &cMaxInstances))
429 return RTErrConvertFromWin32(GetLastError());
430 /* Doesn't seem to matter to much if the pipe is message or byte type. Cygwin
431 seems to hand us such pipes when capturing output (@bugref{9397}), so just
432 ignore skip this check:
433 AssertReturn(!(fInfo & PIPE_TYPE_MESSAGE), VERR_INVALID_HANDLE); */
434 AssertReturn(cMaxInstances == 1, VERR_INVALID_HANDLE);
435
436 DWORD cInstances;
437 DWORD fState;
438 if (!GetNamedPipeHandleState(hNative, &fState, &cInstances, NULL, NULL, NULL, 0))
439 return RTErrConvertFromWin32(GetLastError());
440 AssertReturn(!(fState & PIPE_NOWAIT), VERR_INVALID_HANDLE);
441 AssertReturn(!(fState & PIPE_READMODE_MESSAGE), VERR_INVALID_HANDLE);
442 AssertReturn(cInstances <= 1, VERR_INVALID_HANDLE);
443
444 /*
445 * Looks kind of OK, create a handle so we can try rtPipeQueryNtInfo on it
446 * and see if we need to duplicate it to make that call work.
447 */
448 RTPIPEINTERNAL *pThis = (RTPIPEINTERNAL *)RTMemAllocZ(sizeof(RTPIPEINTERNAL));
449 if (!pThis)
450 return VERR_NO_MEMORY;
451 int rc = RTCritSectInit(&pThis->CritSect);
452 if (RT_SUCCESS(rc))
453 {
454 pThis->Overlapped.hEvent = CreateEvent(NULL, TRUE /*fManualReset*/,
455 TRUE /*fInitialState*/, NULL /*pName*/);
456 if (pThis->Overlapped.hEvent != NULL)
457 {
458 pThis->u32Magic = RTPIPE_MAGIC;
459 pThis->hPipe = hNative;
460 pThis->fRead = RT_BOOL(fFlags & RTPIPE_N_READ);
461 pThis->fLeaveOpen = RT_BOOL(fFlags & RTPIPE_N_LEAVE_OPEN);
462 //pThis->fIOPending = false;
463 //pThis->fZeroByteRead = false;
464 //pThis->fBrokenPipe = false;
465 //pThis->fPromisedWritable = false;
466 pThis->fCreatedInheritable = RT_BOOL(fFlags & RTPIPE_N_INHERIT);
467 //pThis->cUsers = 0;
468 //pThis->pbBounceBuf = NULL;
469 //pThis->cbBounceBufUsed = 0;
470 //pThis->cbBounceBufAlloc = 0;
471 pThis->hPollSet = NIL_RTPOLLSET;
472
473 HANDLE hNative2 = INVALID_HANDLE_VALUE;
474 FILE_PIPE_LOCAL_INFORMATION Info;
475 RT_ZERO(Info);
476 if ( g_pfnSetHandleInformation
477 && rtPipeQueryNtInfo(pThis, &Info))
478 rc = VINF_SUCCESS;
479 else
480 {
481 if (DuplicateHandle(GetCurrentProcess() /*hSrcProcess*/, hNative /*hSrcHandle*/,
482 GetCurrentProcess() /*hDstProcess*/, &hNative2 /*phDstHandle*/,
483 pThis->fRead ? GENERIC_READ : GENERIC_WRITE | FILE_READ_ATTRIBUTES /*dwDesiredAccess*/,
484 !!(fFlags & RTPIPE_N_INHERIT) /*fInheritHandle*/,
485 0 /*dwOptions*/))
486 {
487 pThis->hPipe = hNative2;
488 if (rtPipeQueryNtInfo(pThis, &Info))
489 {
490 pThis->fLeaveOpen = false;
491 rc = VINF_SUCCESS;
492 }
493 else
494 {
495 rc = VERR_ACCESS_DENIED;
496 CloseHandle(hNative2);
497 }
498 }
499 else
500 hNative2 = INVALID_HANDLE_VALUE;
501 }
502 if (RT_SUCCESS(rc))
503 {
504 /*
505 * Verify the pipe state and correct the inheritability.
506 */
507 AssertStmt( Info.NamedPipeState == FILE_PIPE_CONNECTED_STATE
508 || Info.NamedPipeState == FILE_PIPE_CLOSING_STATE
509 || Info.NamedPipeState == FILE_PIPE_DISCONNECTED_STATE,
510 VERR_INVALID_HANDLE);
511 AssertStmt( Info.NamedPipeConfiguration
512 == ( Info.NamedPipeEnd == FILE_PIPE_SERVER_END
513 ? (pThis->fRead ? FILE_PIPE_INBOUND : FILE_PIPE_OUTBOUND)
514 : (pThis->fRead ? FILE_PIPE_OUTBOUND : FILE_PIPE_INBOUND) )
515 || Info.NamedPipeConfiguration == FILE_PIPE_FULL_DUPLEX,
516 VERR_INVALID_HANDLE);
517 if ( RT_SUCCESS(rc)
518 && hNative2 == INVALID_HANDLE_VALUE
519 && !g_pfnSetHandleInformation(hNative,
520 HANDLE_FLAG_INHERIT /*dwMask*/,
521 fFlags & RTPIPE_N_INHERIT ? HANDLE_FLAG_INHERIT : 0))
522 {
523 rc = RTErrConvertFromWin32(GetLastError());
524 AssertMsgFailed(("%Rrc\n", rc));
525 }
526 if (RT_SUCCESS(rc))
527 {
528 /*
529 * Ok, we're good! If we replaced the handle, make sure it's not a standard
530 * handle if we think we need to close it.
531 */
532 if (hNative2 != INVALID_HANDLE_VALUE)
533 {
534 if ( !(fFlags & RTPIPE_N_LEAVE_OPEN)
535 && hNative != GetStdHandle(STD_INPUT_HANDLE)
536 && hNative != GetStdHandle(STD_OUTPUT_HANDLE)
537 && hNative != GetStdHandle(STD_ERROR_HANDLE) )
538 CloseHandle(hNative);
539 }
540 *phPipe = pThis;
541 return VINF_SUCCESS;
542 }
543 }
544
545 /* Bail out. */
546 if (hNative2 != INVALID_HANDLE_VALUE)
547 CloseHandle(hNative2);
548 CloseHandle(pThis->Overlapped.hEvent);
549 }
550 RTCritSectDelete(&pThis->CritSect);
551 }
552 RTMemFree(pThis);
553 return rc;
554}
555
556
557RTDECL(RTHCINTPTR) RTPipeToNative(RTPIPE hPipe)
558{
559 RTPIPEINTERNAL *pThis = hPipe;
560 AssertPtrReturn(pThis, -1);
561 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, -1);
562
563 return (RTHCINTPTR)pThis->hPipe;
564}
565
566
567RTDECL(int) RTPipeGetCreationInheritability(RTPIPE hPipe)
568{
569 RTPIPEINTERNAL *pThis = hPipe;
570 AssertPtrReturn(pThis, false);
571 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, false);
572
573 return pThis->fCreatedInheritable;
574}
575
576
577RTDECL(int) RTPipeRead(RTPIPE hPipe, void *pvBuf, size_t cbToRead, size_t *pcbRead)
578{
579 RTPIPEINTERNAL *pThis = hPipe;
580 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
581 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
582 AssertReturn(pThis->fRead, VERR_ACCESS_DENIED);
583 AssertPtr(pcbRead);
584 AssertPtr(pvBuf);
585
586 int rc = RTCritSectEnter(&pThis->CritSect);
587 if (RT_SUCCESS(rc))
588 {
589 /* No concurrent readers, sorry. */
590 if (pThis->cUsers == 0)
591 {
592 pThis->cUsers++;
593
594 /*
595 * Kick off a an overlapped read. It should return immediately if
596 * there are bytes in the buffer. If not, we'll cancel it and see
597 * what we get back.
598 */
599 rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
600 DWORD cbRead = 0;
601 if ( cbToRead == 0
602 || ReadFile(pThis->hPipe, pvBuf,
603 cbToRead <= ~(DWORD)0 ? (DWORD)cbToRead : ~(DWORD)0,
604 &cbRead, &pThis->Overlapped))
605 {
606 *pcbRead = cbRead;
607 rc = VINF_SUCCESS;
608 }
609 else if (GetLastError() == ERROR_IO_PENDING)
610 {
611 pThis->fIOPending = true;
612 RTCritSectLeave(&pThis->CritSect);
613
614 /* We use NtCancelIoFile here because the CancelIo API
615 providing access to it wasn't available till NT4. This
616 code needs to work (or at least load) with NT 3.1 */
617 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
618 NTSTATUS rcNt = NtCancelIoFile(pThis->hPipe, &Ios);
619 if (!NT_SUCCESS(rcNt))
620 WaitForSingleObject(pThis->Overlapped.hEvent, INFINITE);
621
622 if (GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/))
623 {
624 *pcbRead = cbRead;
625 rc = VINF_SUCCESS;
626 }
627 else if (GetLastError() == ERROR_OPERATION_ABORTED)
628 {
629 *pcbRead = 0;
630 rc = VINF_TRY_AGAIN;
631 }
632 else
633 rc = RTErrConvertFromWin32(GetLastError());
634
635 RTCritSectEnter(&pThis->CritSect);
636 pThis->fIOPending = false;
637 }
638 else
639 rc = RTErrConvertFromWin32(GetLastError());
640 if (rc == VERR_BROKEN_PIPE)
641 pThis->fBrokenPipe = true;
642
643 pThis->cUsers--;
644 }
645 else
646 rc = VERR_WRONG_ORDER;
647 RTCritSectLeave(&pThis->CritSect);
648 }
649 return rc;
650}
651
652
653RTDECL(int) RTPipeReadBlocking(RTPIPE hPipe, void *pvBuf, size_t cbToRead, size_t *pcbRead)
654{
655 RTPIPEINTERNAL *pThis = hPipe;
656 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
657 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
658 AssertReturn(pThis->fRead, VERR_ACCESS_DENIED);
659 AssertPtr(pvBuf);
660
661 int rc = RTCritSectEnter(&pThis->CritSect);
662 if (RT_SUCCESS(rc))
663 {
664 /* No concurrent readers, sorry. */
665 if (pThis->cUsers == 0)
666 {
667 pThis->cUsers++;
668
669 size_t cbTotalRead = 0;
670 while (cbToRead > 0)
671 {
672 /*
673 * Kick of a an overlapped read. It should return immediately if
674 * there is bytes in the buffer. If not, we'll cancel it and see
675 * what we get back.
676 */
677 rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
678 DWORD cbRead = 0;
679 pThis->fIOPending = true;
680 RTCritSectLeave(&pThis->CritSect);
681
682 if (ReadFile(pThis->hPipe, pvBuf,
683 cbToRead <= ~(DWORD)0 ? (DWORD)cbToRead : ~(DWORD)0,
684 &cbRead, &pThis->Overlapped))
685 rc = VINF_SUCCESS;
686 else if (GetLastError() == ERROR_IO_PENDING)
687 {
688 WaitForSingleObject(pThis->Overlapped.hEvent, INFINITE);
689 if (GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/))
690 rc = VINF_SUCCESS;
691 else
692 rc = RTErrConvertFromWin32(GetLastError());
693 }
694 else
695 rc = RTErrConvertFromWin32(GetLastError());
696
697 RTCritSectEnter(&pThis->CritSect);
698 pThis->fIOPending = false;
699 if (RT_FAILURE(rc))
700 break;
701
702 /* advance */
703 cbToRead -= cbRead;
704 cbTotalRead += cbRead;
705 pvBuf = (uint8_t *)pvBuf + cbRead;
706 }
707
708 if (rc == VERR_BROKEN_PIPE)
709 pThis->fBrokenPipe = true;
710
711 if (pcbRead)
712 {
713 *pcbRead = cbTotalRead;
714 if ( RT_FAILURE(rc)
715 && cbTotalRead
716 && rc != VERR_INVALID_POINTER)
717 rc = VINF_SUCCESS;
718 }
719
720 pThis->cUsers--;
721 }
722 else
723 rc = VERR_WRONG_ORDER;
724 RTCritSectLeave(&pThis->CritSect);
725 }
726 return rc;
727}
728
729
730RTDECL(int) RTPipeWrite(RTPIPE hPipe, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
731{
732 RTPIPEINTERNAL *pThis = hPipe;
733 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
734 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
735 AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
736 AssertPtr(pcbWritten);
737 AssertPtr(pvBuf);
738
739 int rc = RTCritSectEnter(&pThis->CritSect);
740 if (RT_SUCCESS(rc))
741 {
742 /* No concurrent writers, sorry. */
743 if (pThis->cUsers == 0)
744 {
745 pThis->cUsers++;
746
747 /* If I/O is pending, check if it has completed. */
748 if (pThis->fIOPending)
749 rc = rtPipeWriteCheckCompletion(pThis);
750 else
751 rc = VINF_SUCCESS;
752 if (rc == VINF_SUCCESS)
753 {
754 Assert(!pThis->fIOPending);
755
756 /* Adjust the number of bytes to write to fit into the current
757 buffer quota, unless we've promised stuff in RTPipeSelectOne.
758 WriteQuotaAvailable better not be zero when it shouldn't!! */
759 FILE_PIPE_LOCAL_INFORMATION Info;
760 if ( !pThis->fPromisedWritable
761 && cbToWrite > 0
762 && rtPipeQueryNtInfo(pThis, &Info))
763 {
764 if (Info.NamedPipeState == FILE_PIPE_CLOSING_STATE)
765 rc = VERR_BROKEN_PIPE;
766 /** @todo fixme: To get the pipe writing support to work the
767 * block below needs to be commented out until a
768 * way is found to address the problem of the incorrectly
769 * set field Info.WriteQuotaAvailable.
770 * Update: We now just write up to RTPIPE_NT_SIZE more. This is quite
771 * possibely what lead to the misunderstanding here wrt to
772 * WriteQuotaAvailable updating. */
773#if 0
774 else if ( cbToWrite >= Info.WriteQuotaAvailable
775 && Info.OutboundQuota != 0
776 && (Info.WriteQuotaAvailable || pThis->cbBounceBufAlloc)
777 )
778 {
779 cbToWrite = Info.WriteQuotaAvailable;
780 if (!cbToWrite)
781 rc = VINF_TRY_AGAIN;
782 }
783#endif
784 }
785 pThis->fPromisedWritable = false;
786
787 /* Do the bounce buffering. */
788 if ( pThis->cbBounceBufAlloc < cbToWrite
789 && pThis->cbBounceBufAlloc < RTPIPE_NT_SIZE)
790 {
791 if (cbToWrite > RTPIPE_NT_SIZE)
792 cbToWrite = RTPIPE_NT_SIZE;
793 void *pv = RTMemRealloc(pThis->pbBounceBuf, RT_ALIGN_Z(cbToWrite, _1K));
794 if (pv)
795 {
796 pThis->pbBounceBuf = (uint8_t *)pv;
797 pThis->cbBounceBufAlloc = RT_ALIGN_Z(cbToWrite, _1K);
798 }
799 else
800 rc = VERR_NO_MEMORY;
801 }
802 else if (cbToWrite > RTPIPE_NT_SIZE)
803 cbToWrite = RTPIPE_NT_SIZE;
804 if (RT_SUCCESS(rc) && cbToWrite)
805 {
806 memcpy(pThis->pbBounceBuf, pvBuf, cbToWrite);
807 pThis->cbBounceBufUsed = (uint32_t)cbToWrite;
808
809 /* Submit the write. */
810 rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
811 DWORD cbWritten = 0;
812 if (WriteFile(pThis->hPipe, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed,
813 &cbWritten, &pThis->Overlapped))
814 {
815 *pcbWritten = RT_MIN(cbWritten, cbToWrite); /* paranoia^3 */
816 rc = VINF_SUCCESS;
817 }
818 else if (GetLastError() == ERROR_IO_PENDING)
819 {
820 *pcbWritten = cbToWrite;
821 pThis->fIOPending = true;
822 rc = VINF_SUCCESS;
823 }
824 else if (GetLastError() == ERROR_NO_DATA)
825 rc = VERR_BROKEN_PIPE;
826 else
827 rc = RTErrConvertFromWin32(GetLastError());
828 }
829 else if (RT_SUCCESS(rc))
830 *pcbWritten = 0;
831 }
832 else if (RT_SUCCESS(rc))
833 *pcbWritten = 0;
834
835 if (rc == VERR_BROKEN_PIPE)
836 pThis->fBrokenPipe = true;
837
838 pThis->cUsers--;
839 }
840 else
841 rc = VERR_WRONG_ORDER;
842 RTCritSectLeave(&pThis->CritSect);
843 }
844 return rc;
845}
846
847
848RTDECL(int) RTPipeWriteBlocking(RTPIPE hPipe, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
849{
850 RTPIPEINTERNAL *pThis = hPipe;
851 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
852 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
853 AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
854 AssertPtr(pvBuf);
855 AssertPtrNull(pcbWritten);
856
857 int rc = RTCritSectEnter(&pThis->CritSect);
858 if (RT_SUCCESS(rc))
859 {
860 /* No concurrent writers, sorry. */
861 if (pThis->cUsers == 0)
862 {
863 pThis->cUsers++;
864
865 /*
866 * If I/O is pending, wait for it to complete.
867 */
868 if (pThis->fIOPending)
869 {
870 rc = rtPipeWriteCheckCompletion(pThis);
871 while (rc == VINF_TRY_AGAIN)
872 {
873 Assert(pThis->fIOPending);
874 HANDLE hEvent = pThis->Overlapped.hEvent;
875 RTCritSectLeave(&pThis->CritSect);
876 WaitForSingleObject(hEvent, INFINITE);
877 RTCritSectEnter(&pThis->CritSect);
878 }
879 }
880 if (RT_SUCCESS(rc))
881 {
882 Assert(!pThis->fIOPending);
883 pThis->fPromisedWritable = false;
884
885 /*
886 * Try write everything.
887 * No bounce buffering, cUsers protects us.
888 */
889 size_t cbTotalWritten = 0;
890 while (cbToWrite > 0)
891 {
892 rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
893 pThis->fIOPending = true;
894 RTCritSectLeave(&pThis->CritSect);
895
896 DWORD cbWritten = 0;
897 DWORD const cbToWriteInThisIteration = cbToWrite <= ~(DWORD)0 ? (DWORD)cbToWrite : ~(DWORD)0;
898 if (WriteFile(pThis->hPipe, pvBuf, cbToWriteInThisIteration, &cbWritten, &pThis->Overlapped))
899 rc = VINF_SUCCESS;
900 else if (GetLastError() == ERROR_IO_PENDING)
901 {
902 WaitForSingleObject(pThis->Overlapped.hEvent, INFINITE);
903 if (GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbWritten, TRUE /*fWait*/))
904 rc = VINF_SUCCESS;
905 else
906 rc = RTErrConvertFromWin32(GetLastError());
907 }
908 else if (GetLastError() == ERROR_NO_DATA)
909 rc = VERR_BROKEN_PIPE;
910 else
911 rc = RTErrConvertFromWin32(GetLastError());
912
913 RTCritSectEnter(&pThis->CritSect);
914 pThis->fIOPending = false;
915 if (RT_FAILURE(rc))
916 break;
917
918 /* advance */
919 if (cbWritten > cbToWriteInThisIteration) /* paranoia^3 */
920 cbWritten = cbToWriteInThisIteration;
921 pvBuf = (char const *)pvBuf + cbWritten;
922 cbTotalWritten += cbWritten;
923 cbToWrite -= cbWritten;
924 }
925
926 if (pcbWritten)
927 {
928 *pcbWritten = cbTotalWritten;
929 if ( RT_FAILURE(rc)
930 && cbTotalWritten
931 && rc != VERR_INVALID_POINTER)
932 rc = VINF_SUCCESS;
933 }
934 }
935
936 if (rc == VERR_BROKEN_PIPE)
937 pThis->fBrokenPipe = true;
938
939 pThis->cUsers--;
940 }
941 else
942 rc = VERR_WRONG_ORDER;
943 RTCritSectLeave(&pThis->CritSect);
944 }
945 return rc;
946
947#if 0 /** @todo r=bird: What's this? */
948 int rc = rtPipeTryBlocking(pThis);
949 if (RT_SUCCESS(rc))
950 {
951 size_t cbTotalWritten = 0;
952 while (cbToWrite > 0)
953 {
954 ssize_t cbWritten = write(pThis->fd, pvBuf, RT_MIN(cbToWrite, SSIZE_MAX));
955 if (cbWritten < 0)
956 {
957 rc = RTErrConvertFromErrno(errno);
958 break;
959 }
960
961 /* advance */
962 pvBuf = (char const *)pvBuf + cbWritten;
963 cbTotalWritten += cbWritten;
964 cbToWrite -= cbWritten;
965 }
966
967 if (pcbWritten)
968 {
969 *pcbWritten = cbTotalWritten;
970 if ( RT_FAILURE(rc)
971 && cbTotalWritten
972 && rc != VERR_INVALID_POINTER)
973 rc = VINF_SUCCESS;
974 }
975
976 ASMAtomicDecU32(&pThis->u32State);
977 }
978 return rc;
979#endif
980}
981
982
983RTDECL(int) RTPipeFlush(RTPIPE hPipe)
984{
985 RTPIPEINTERNAL *pThis = hPipe;
986 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
987 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
988 AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
989
990 if (!FlushFileBuffers(pThis->hPipe))
991 {
992 int rc = RTErrConvertFromWin32(GetLastError());
993 if (rc == VERR_BROKEN_PIPE)
994 pThis->fBrokenPipe = true;
995 return rc;
996 }
997 return VINF_SUCCESS;
998}
999
1000
1001RTDECL(int) RTPipeSelectOne(RTPIPE hPipe, RTMSINTERVAL cMillies)
1002{
1003 RTPIPEINTERNAL *pThis = hPipe;
1004 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1005 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
1006
1007 uint64_t const StartMsTS = RTTimeMilliTS();
1008
1009 int rc = RTCritSectEnter(&pThis->CritSect);
1010 if (RT_FAILURE(rc))
1011 return rc;
1012 for (unsigned iLoop = 0;; iLoop++)
1013 {
1014 HANDLE hWait = INVALID_HANDLE_VALUE;
1015 if (pThis->fRead)
1016 {
1017 if (pThis->fIOPending)
1018 hWait = pThis->Overlapped.hEvent;
1019 else
1020 {
1021 /* Peek at the pipe buffer and see how many bytes it contains. */
1022 DWORD cbAvailable;
1023 if ( PeekNamedPipe(pThis->hPipe, NULL, 0, NULL, &cbAvailable, NULL)
1024 && cbAvailable > 0)
1025 {
1026 rc = VINF_SUCCESS;
1027 break;
1028 }
1029
1030 /* Start a zero byte read operation that we can wait on. */
1031 if (cMillies == 0)
1032 {
1033 rc = VERR_TIMEOUT;
1034 break;
1035 }
1036 AssertBreakStmt(pThis->cUsers == 0, rc = VERR_INTERNAL_ERROR_5);
1037 rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
1038 DWORD cbRead = 0;
1039 if (ReadFile(pThis->hPipe, pThis->abBuf, 0, &cbRead, &pThis->Overlapped))
1040 {
1041 rc = VINF_SUCCESS;
1042 if (iLoop > 10)
1043 RTThreadYield();
1044 }
1045 else if (GetLastError() == ERROR_IO_PENDING)
1046 {
1047 pThis->cUsers++;
1048 pThis->fIOPending = true;
1049 pThis->fZeroByteRead = true;
1050 hWait = pThis->Overlapped.hEvent;
1051 }
1052 else
1053 rc = RTErrConvertFromWin32(GetLastError());
1054 }
1055 }
1056 else
1057 {
1058 if (pThis->fIOPending)
1059 {
1060 rc = rtPipeWriteCheckCompletion(pThis);
1061 if (RT_FAILURE(rc))
1062 break;
1063 }
1064 if (pThis->fIOPending)
1065 hWait = pThis->Overlapped.hEvent;
1066 else
1067 {
1068 FILE_PIPE_LOCAL_INFORMATION Info;
1069#if 1
1070 /* We can always write one bounce buffer full of data regardless of
1071 the pipe buffer state. We must of course take this into account,
1072 or code like "Full write buffer" test in tstRTPipe gets confused. */
1073 rc = VINF_SUCCESS;
1074 if (rtPipeQueryNtInfo(pThis, &Info))
1075 {
1076 /* Check for broken pipe. */
1077 if (Info.NamedPipeState != FILE_PIPE_CLOSING_STATE)
1078 pThis->fPromisedWritable = true;
1079 else
1080 rc = VERR_BROKEN_PIPE;
1081 }
1082 else
1083 pThis->fPromisedWritable = true;
1084 break;
1085
1086#else /* old code: */
1087 if (rtPipeQueryNtInfo(pThis, &Info))
1088 {
1089 /* Check for broken pipe. */
1090 if (Info.NamedPipeState == FILE_PIPE_CLOSING_STATE)
1091 {
1092 rc = VERR_BROKEN_PIPE;
1093 break;
1094 }
1095
1096 /* Check for available write buffer space. */
1097 if (Info.WriteQuotaAvailable > 0)
1098 {
1099 pThis->fPromisedWritable = false;
1100 rc = VINF_SUCCESS;
1101 break;
1102 }
1103
1104 /* delayed buffer alloc or timeout: phony promise
1105 later: See if we still can associate a semaphore with
1106 the pipe, like on OS/2. */
1107 if (Info.OutboundQuota == 0 || cMillies)
1108 {
1109 pThis->fPromisedWritable = true;
1110 rc = VINF_SUCCESS;
1111 break;
1112 }
1113 }
1114 else
1115 {
1116 pThis->fPromisedWritable = true;
1117 rc = VINF_SUCCESS;
1118 break;
1119 }
1120#endif
1121 }
1122 }
1123 if (RT_FAILURE(rc))
1124 break;
1125
1126 /*
1127 * Check for timeout.
1128 */
1129 DWORD cMsMaxWait = INFINITE;
1130 if ( cMillies != RT_INDEFINITE_WAIT
1131 && ( hWait != INVALID_HANDLE_VALUE
1132 || iLoop > 10)
1133 )
1134 {
1135 uint64_t cElapsed = RTTimeMilliTS() - StartMsTS;
1136 if (cElapsed >= cMillies)
1137 {
1138 rc = VERR_TIMEOUT;
1139 break;
1140 }
1141 cMsMaxWait = cMillies - (uint32_t)cElapsed;
1142 }
1143
1144 /*
1145 * Wait.
1146 */
1147 if (hWait != INVALID_HANDLE_VALUE)
1148 {
1149 RTCritSectLeave(&pThis->CritSect);
1150
1151 DWORD dwRc = WaitForSingleObject(hWait, cMsMaxWait);
1152 if (dwRc == WAIT_OBJECT_0)
1153 rc = VINF_SUCCESS;
1154 else if (dwRc == WAIT_TIMEOUT)
1155 rc = VERR_TIMEOUT;
1156 else if (dwRc == WAIT_ABANDONED)
1157 rc = VERR_INVALID_HANDLE;
1158 else
1159 rc = RTErrConvertFromWin32(GetLastError());
1160 if ( RT_FAILURE(rc)
1161 && pThis->u32Magic != RTPIPE_MAGIC)
1162 return rc;
1163
1164 RTCritSectEnter(&pThis->CritSect);
1165 if (pThis->fZeroByteRead)
1166 {
1167 pThis->cUsers--;
1168 pThis->fIOPending = false;
1169 if (rc != VINF_SUCCESS)
1170 {
1171 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
1172 NtCancelIoFile(pThis->hPipe, &Ios);
1173 }
1174 DWORD cbRead = 0;
1175 GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/);
1176 }
1177 if (RT_FAILURE(rc))
1178 break;
1179 }
1180 }
1181
1182 if (rc == VERR_BROKEN_PIPE)
1183 pThis->fBrokenPipe = true;
1184
1185 RTCritSectLeave(&pThis->CritSect);
1186 return rc;
1187}
1188
1189
1190RTDECL(int) RTPipeQueryReadable(RTPIPE hPipe, size_t *pcbReadable)
1191{
1192 RTPIPEINTERNAL *pThis = hPipe;
1193 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1194 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
1195 AssertReturn(pThis->fRead, VERR_PIPE_NOT_READ);
1196 AssertPtrReturn(pcbReadable, VERR_INVALID_POINTER);
1197
1198 int rc = RTCritSectEnter(&pThis->CritSect);
1199 if (RT_FAILURE(rc))
1200 return rc;
1201
1202 /** @todo The file size should give the same info and be slightly faster... */
1203 DWORD cbAvailable = 0;
1204 if (PeekNamedPipe(pThis->hPipe, NULL, 0, NULL, &cbAvailable, NULL))
1205 {
1206#if ARCH_BITS == 32
1207 /*
1208 * Kludge!
1209 *
1210 * Prior to XP SP1 (?), the returned cbAvailable value was not adjusted
1211 * by the read position in the current message/buffer, so it could
1212 * potentially be too high. This may cause the caller to try read more
1213 * data than what's actually available, which may cause the read to
1214 * block when the caller thought it wouldn't.
1215 *
1216 * To get an accurate readable size, we have to provide an output
1217 * buffer and see how much we actually get back in it, as the data
1218 * peeking works correctly (as you would expect).
1219 */
1220 if (cbAvailable == 0 || g_enmWinVer >= kRTWinOSType_XP64)
1221 { /* No data available or kernel shouldn't be affected. */ }
1222 else
1223 {
1224 for (unsigned i = 0; ; i++)
1225 {
1226 uint8_t abBufStack[_16K];
1227 void *pvBufFree = NULL;
1228 void *pvBuf;
1229 DWORD cbBuf = RT_ALIGN_32(cbAvailable + i * 256, 64);
1230 if (cbBuf <= sizeof(abBufStack))
1231 {
1232 pvBuf = abBufStack;
1233 /* No cbBuf = sizeof(abBufStack) here! PeekNamedPipe bounce buffers the request on the heap. */
1234 }
1235 else
1236 {
1237 pvBufFree = pvBuf = RTMemTmpAlloc(cbBuf);
1238 if (!pvBuf)
1239 {
1240 rc = VERR_NO_TMP_MEMORY;
1241 cbAvailable = 1;
1242 break;
1243 }
1244 }
1245
1246 DWORD cbAvailable2 = 0;
1247 DWORD cbRead = 0;
1248 BOOL fRc = PeekNamedPipe(pThis->hPipe, pvBuf, cbBuf, &cbRead, &cbAvailable2, NULL);
1249 Log(("RTPipeQueryReadable: #%u: cbAvailable=%#x cbRead=%#x cbAvailable2=%#x (cbBuf=%#x)\n",
1250 i, cbAvailable, cbRead, cbAvailable2, cbBuf));
1251
1252 RTMemTmpFree(pvBufFree);
1253
1254 if (fRc)
1255 {
1256 if (cbAvailable2 <= cbBuf || i >= 10)
1257 cbAvailable = cbRead;
1258 else
1259 {
1260 cbAvailable = cbAvailable2;
1261 continue;
1262 }
1263 }
1264 else
1265 {
1266 rc = RTErrConvertFromWin32(GetLastError());
1267 cbAvailable = 1;
1268 }
1269 break;
1270 }
1271 }
1272#endif
1273 *pcbReadable = cbAvailable;
1274 }
1275 else
1276 rc = RTErrConvertFromWin32(GetLastError());
1277
1278 RTCritSectLeave(&pThis->CritSect);
1279 return rc;
1280}
1281
1282
1283RTDECL(int) RTPipeQueryInfo(RTPIPE hPipe, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1284{
1285 RTPIPEINTERNAL *pThis = hPipe;
1286 AssertPtrReturn(pThis, 0);
1287 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, 0);
1288
1289 int rc = RTCritSectEnter(&pThis->CritSect);
1290 AssertRCReturn(rc, 0);
1291
1292 rtPipeFakeQueryInfo(pObjInfo, enmAddAttr, pThis->fRead);
1293
1294 FILE_PIPE_LOCAL_INFORMATION Info;
1295 if (rtPipeQueryNtInfo(pThis, &Info))
1296 {
1297 pObjInfo->cbAllocated = pThis->fRead ? Info.InboundQuota : Info.OutboundQuota;
1298 pObjInfo->cbObject = pThis->fRead ? Info.ReadDataAvailable : Info.WriteQuotaAvailable;
1299 }
1300
1301 RTCritSectLeave(&pThis->CritSect);
1302 return VINF_SUCCESS;
1303}
1304
1305
1306int rtPipePollGetHandle(RTPIPE hPipe, uint32_t fEvents, PRTHCINTPTR phNative)
1307{
1308 RTPIPEINTERNAL *pThis = hPipe;
1309 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1310 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
1311
1312 AssertReturn(!(fEvents & RTPOLL_EVT_READ) || pThis->fRead, VERR_INVALID_PARAMETER);
1313 AssertReturn(!(fEvents & RTPOLL_EVT_WRITE) || !pThis->fRead, VERR_INVALID_PARAMETER);
1314
1315 /* Later: Try register an event handle with the pipe like on OS/2, there is
1316 a file control for doing this obviously intended for the OS/2 subsys.
1317 The question is whether this still exists on Vista and W7. */
1318 *phNative = (RTHCINTPTR)pThis->Overlapped.hEvent;
1319 return VINF_SUCCESS;
1320}
1321
1322
1323/**
1324 * Checks for pending events.
1325 *
1326 * @returns Event mask or 0.
1327 * @param pThis The pipe handle.
1328 * @param fEvents The desired events.
1329 */
1330static uint32_t rtPipePollCheck(RTPIPEINTERNAL *pThis, uint32_t fEvents)
1331{
1332 uint32_t fRetEvents = 0;
1333 if (pThis->fBrokenPipe)
1334 fRetEvents |= RTPOLL_EVT_ERROR;
1335 else if (pThis->fRead)
1336 {
1337 if (!pThis->fIOPending)
1338 {
1339 DWORD cbAvailable;
1340 if (PeekNamedPipe(pThis->hPipe, NULL, 0, NULL, &cbAvailable, NULL))
1341 {
1342 if ( (fEvents & RTPOLL_EVT_READ)
1343 && cbAvailable > 0)
1344 fRetEvents |= RTPOLL_EVT_READ;
1345 }
1346 else
1347 {
1348 if (GetLastError() == ERROR_BROKEN_PIPE)
1349 pThis->fBrokenPipe = true;
1350 fRetEvents |= RTPOLL_EVT_ERROR;
1351 }
1352 }
1353 }
1354 else
1355 {
1356 if (pThis->fIOPending)
1357 {
1358 rtPipeWriteCheckCompletion(pThis);
1359 if (pThis->fBrokenPipe)
1360 fRetEvents |= RTPOLL_EVT_ERROR;
1361 }
1362 if ( !pThis->fIOPending
1363 && !fRetEvents)
1364 {
1365 FILE_PIPE_LOCAL_INFORMATION Info;
1366 if (rtPipeQueryNtInfo(pThis, &Info))
1367 {
1368 /* Check for broken pipe. */
1369 if (Info.NamedPipeState == FILE_PIPE_CLOSING_STATE)
1370 {
1371 fRetEvents = RTPOLL_EVT_ERROR;
1372 pThis->fBrokenPipe = true;
1373 }
1374
1375 /* Check if there is available buffer space. */
1376 if ( !fRetEvents
1377 && (fEvents & RTPOLL_EVT_WRITE)
1378 && ( Info.WriteQuotaAvailable > 0
1379 || Info.OutboundQuota == 0)
1380 )
1381 fRetEvents |= RTPOLL_EVT_WRITE;
1382 }
1383 else if (fEvents & RTPOLL_EVT_WRITE)
1384 fRetEvents |= RTPOLL_EVT_WRITE;
1385 }
1386 }
1387
1388 return fRetEvents;
1389}
1390
1391
1392/**
1393 * Internal RTPoll helper that polls the pipe handle and, if @a fNoWait is
1394 * clear, starts whatever actions we've got running during the poll call.
1395 *
1396 * @returns 0 if no pending events, actions initiated if @a fNoWait is clear.
1397 * Event mask (in @a fEvents) and no actions if the handle is ready
1398 * already.
1399 * UINT32_MAX (asserted) if the pipe handle is busy in I/O or a
1400 * different poll set.
1401 *
1402 * @param hPipe The pipe handle.
1403 * @param hPollSet The poll set handle (for access checks).
1404 * @param fEvents The events we're polling for.
1405 * @param fFinalEntry Set if this is the final entry for this handle
1406 * in this poll set. This can be used for dealing
1407 * with duplicate entries.
1408 * @param fNoWait Set if it's a zero-wait poll call. Clear if
1409 * we'll wait for an event to occur.
1410 */
1411uint32_t rtPipePollStart(RTPIPE hPipe, RTPOLLSET hPollSet, uint32_t fEvents, bool fFinalEntry, bool fNoWait)
1412{
1413 /** @todo All this polling code could be optimized to make fewer system
1414 * calls; like for instance the ResetEvent calls. */
1415 RTPIPEINTERNAL *pThis = hPipe;
1416 AssertPtrReturn(pThis, UINT32_MAX);
1417 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, UINT32_MAX);
1418 RT_NOREF_PV(fFinalEntry);
1419
1420 int rc = RTCritSectEnter(&pThis->CritSect);
1421 AssertRCReturn(rc, UINT32_MAX);
1422
1423 /* Check that this is the only current use of this pipe. */
1424 uint32_t fRetEvents;
1425 if ( pThis->cUsers == 0
1426 || pThis->hPollSet == hPollSet)
1427 {
1428 /* Check what the current events are. */
1429 fRetEvents = rtPipePollCheck(pThis, fEvents);
1430 if ( !fRetEvents
1431 && !fNoWait)
1432 {
1433 /* Make sure the event semaphore has been reset. */
1434 if (!pThis->fIOPending)
1435 {
1436 rc = ResetEvent(pThis->Overlapped.hEvent);
1437 Assert(rc == TRUE);
1438 }
1439
1440 /* Kick off the zero byte read thing if applicable. */
1441 if ( !pThis->fIOPending
1442 && pThis->fRead
1443 && (fEvents & RTPOLL_EVT_READ)
1444 )
1445 {
1446 DWORD cbRead = 0;
1447 if (ReadFile(pThis->hPipe, pThis->abBuf, 0, &cbRead, &pThis->Overlapped))
1448 fRetEvents = rtPipePollCheck(pThis, fEvents);
1449 else if (GetLastError() == ERROR_IO_PENDING)
1450 {
1451 pThis->fIOPending = true;
1452 pThis->fZeroByteRead = true;
1453 }
1454 else
1455 fRetEvents = RTPOLL_EVT_ERROR;
1456 }
1457
1458 /* If we're still set for the waiting, record the poll set and
1459 mark the pipe used. */
1460 if (!fRetEvents)
1461 {
1462 pThis->cUsers++;
1463 pThis->hPollSet = hPollSet;
1464 }
1465 }
1466 }
1467 else
1468 {
1469 AssertFailed();
1470 fRetEvents = UINT32_MAX;
1471 }
1472
1473 RTCritSectLeave(&pThis->CritSect);
1474 return fRetEvents;
1475}
1476
1477
1478/**
1479 * Called after a WaitForMultipleObjects returned in order to check for pending
1480 * events and stop whatever actions that rtPipePollStart() initiated.
1481 *
1482 * @returns Event mask or 0.
1483 *
1484 * @param hPipe The pipe handle.
1485 * @param fEvents The events we're polling for.
1486 * @param fFinalEntry Set if this is the final entry for this handle
1487 * in this poll set. This can be used for dealing
1488 * with duplicate entries. Only keep in mind that
1489 * this method is called in reverse order, so the
1490 * first call will have this set (when the entire
1491 * set was processed).
1492 * @param fHarvestEvents Set if we should check for pending events.
1493 */
1494uint32_t rtPipePollDone(RTPIPE hPipe, uint32_t fEvents, bool fFinalEntry, bool fHarvestEvents)
1495{
1496 RTPIPEINTERNAL *pThis = hPipe;
1497 AssertPtrReturn(pThis, 0);
1498 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, 0);
1499 RT_NOREF_PV(fFinalEntry);
1500 RT_NOREF_PV(fHarvestEvents);
1501
1502 int rc = RTCritSectEnter(&pThis->CritSect);
1503 AssertRCReturn(rc, 0);
1504
1505 Assert(pThis->cUsers > 0);
1506
1507
1508 /* Cancel the zero byte read. */
1509 uint32_t fRetEvents = 0;
1510 if (pThis->fZeroByteRead)
1511 {
1512 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
1513 NtCancelIoFile(pThis->hPipe, &Ios);
1514
1515 DWORD cbRead = 0;
1516 if ( !GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/)
1517 && GetLastError() != ERROR_OPERATION_ABORTED)
1518 fRetEvents = RTPOLL_EVT_ERROR;
1519
1520 pThis->fIOPending = false;
1521 pThis->fZeroByteRead = false;
1522 }
1523
1524 /* harvest events. */
1525 fRetEvents |= rtPipePollCheck(pThis, fEvents);
1526
1527 /* update counters. */
1528 pThis->cUsers--;
1529 /** @todo This isn't sane, or is it? See OS/2 impl. */
1530 if (!pThis->cUsers)
1531 pThis->hPollSet = NIL_RTPOLLSET;
1532
1533 RTCritSectLeave(&pThis->CritSect);
1534 return fRetEvents;
1535}
1536
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette