VirtualBox

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

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

IPRT: Switched pipe-win.cpp to use the nt-and-windows.h header and remove duplicate definitions. bugref:10261

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 54.8 KB
Line 
1/* $Id: pipe-win.cpp 96495 2022-08-25 15:17:52Z 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 if (!CancelIo(pThis->hPipe))
615 WaitForSingleObject(pThis->Overlapped.hEvent, INFINITE);
616 if (GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/))
617 {
618 *pcbRead = cbRead;
619 rc = VINF_SUCCESS;
620 }
621 else if (GetLastError() == ERROR_OPERATION_ABORTED)
622 {
623 *pcbRead = 0;
624 rc = VINF_TRY_AGAIN;
625 }
626 else
627 rc = RTErrConvertFromWin32(GetLastError());
628
629 RTCritSectEnter(&pThis->CritSect);
630 pThis->fIOPending = false;
631 }
632 else
633 rc = RTErrConvertFromWin32(GetLastError());
634 if (rc == VERR_BROKEN_PIPE)
635 pThis->fBrokenPipe = true;
636
637 pThis->cUsers--;
638 }
639 else
640 rc = VERR_WRONG_ORDER;
641 RTCritSectLeave(&pThis->CritSect);
642 }
643 return rc;
644}
645
646
647RTDECL(int) RTPipeReadBlocking(RTPIPE hPipe, void *pvBuf, size_t cbToRead, size_t *pcbRead)
648{
649 RTPIPEINTERNAL *pThis = hPipe;
650 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
651 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
652 AssertReturn(pThis->fRead, VERR_ACCESS_DENIED);
653 AssertPtr(pvBuf);
654
655 int rc = RTCritSectEnter(&pThis->CritSect);
656 if (RT_SUCCESS(rc))
657 {
658 /* No concurrent readers, sorry. */
659 if (pThis->cUsers == 0)
660 {
661 pThis->cUsers++;
662
663 size_t cbTotalRead = 0;
664 while (cbToRead > 0)
665 {
666 /*
667 * Kick of a an overlapped read. It should return immediately if
668 * there is bytes in the buffer. If not, we'll cancel it and see
669 * what we get back.
670 */
671 rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
672 DWORD cbRead = 0;
673 pThis->fIOPending = true;
674 RTCritSectLeave(&pThis->CritSect);
675
676 if (ReadFile(pThis->hPipe, pvBuf,
677 cbToRead <= ~(DWORD)0 ? (DWORD)cbToRead : ~(DWORD)0,
678 &cbRead, &pThis->Overlapped))
679 rc = VINF_SUCCESS;
680 else if (GetLastError() == ERROR_IO_PENDING)
681 {
682 WaitForSingleObject(pThis->Overlapped.hEvent, INFINITE);
683 if (GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/))
684 rc = VINF_SUCCESS;
685 else
686 rc = RTErrConvertFromWin32(GetLastError());
687 }
688 else
689 rc = RTErrConvertFromWin32(GetLastError());
690
691 RTCritSectEnter(&pThis->CritSect);
692 pThis->fIOPending = false;
693 if (RT_FAILURE(rc))
694 break;
695
696 /* advance */
697 cbToRead -= cbRead;
698 cbTotalRead += cbRead;
699 pvBuf = (uint8_t *)pvBuf + cbRead;
700 }
701
702 if (rc == VERR_BROKEN_PIPE)
703 pThis->fBrokenPipe = true;
704
705 if (pcbRead)
706 {
707 *pcbRead = cbTotalRead;
708 if ( RT_FAILURE(rc)
709 && cbTotalRead
710 && rc != VERR_INVALID_POINTER)
711 rc = VINF_SUCCESS;
712 }
713
714 pThis->cUsers--;
715 }
716 else
717 rc = VERR_WRONG_ORDER;
718 RTCritSectLeave(&pThis->CritSect);
719 }
720 return rc;
721}
722
723
724RTDECL(int) RTPipeWrite(RTPIPE hPipe, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
725{
726 RTPIPEINTERNAL *pThis = hPipe;
727 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
728 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
729 AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
730 AssertPtr(pcbWritten);
731 AssertPtr(pvBuf);
732
733 int rc = RTCritSectEnter(&pThis->CritSect);
734 if (RT_SUCCESS(rc))
735 {
736 /* No concurrent writers, sorry. */
737 if (pThis->cUsers == 0)
738 {
739 pThis->cUsers++;
740
741 /* If I/O is pending, check if it has completed. */
742 if (pThis->fIOPending)
743 rc = rtPipeWriteCheckCompletion(pThis);
744 else
745 rc = VINF_SUCCESS;
746 if (rc == VINF_SUCCESS)
747 {
748 Assert(!pThis->fIOPending);
749
750 /* Adjust the number of bytes to write to fit into the current
751 buffer quota, unless we've promised stuff in RTPipeSelectOne.
752 WriteQuotaAvailable better not be zero when it shouldn't!! */
753 FILE_PIPE_LOCAL_INFORMATION Info;
754 if ( !pThis->fPromisedWritable
755 && cbToWrite > 0
756 && rtPipeQueryNtInfo(pThis, &Info))
757 {
758 if (Info.NamedPipeState == FILE_PIPE_CLOSING_STATE)
759 rc = VERR_BROKEN_PIPE;
760 /** @todo fixme: To get the pipe writing support to work the
761 * block below needs to be commented out until a
762 * way is found to address the problem of the incorrectly
763 * set field Info.WriteQuotaAvailable.
764 * Update: We now just write up to RTPIPE_NT_SIZE more. This is quite
765 * possibely what lead to the misunderstanding here wrt to
766 * WriteQuotaAvailable updating. */
767#if 0
768 else if ( cbToWrite >= Info.WriteQuotaAvailable
769 && Info.OutboundQuota != 0
770 && (Info.WriteQuotaAvailable || pThis->cbBounceBufAlloc)
771 )
772 {
773 cbToWrite = Info.WriteQuotaAvailable;
774 if (!cbToWrite)
775 rc = VINF_TRY_AGAIN;
776 }
777#endif
778 }
779 pThis->fPromisedWritable = false;
780
781 /* Do the bounce buffering. */
782 if ( pThis->cbBounceBufAlloc < cbToWrite
783 && pThis->cbBounceBufAlloc < RTPIPE_NT_SIZE)
784 {
785 if (cbToWrite > RTPIPE_NT_SIZE)
786 cbToWrite = RTPIPE_NT_SIZE;
787 void *pv = RTMemRealloc(pThis->pbBounceBuf, RT_ALIGN_Z(cbToWrite, _1K));
788 if (pv)
789 {
790 pThis->pbBounceBuf = (uint8_t *)pv;
791 pThis->cbBounceBufAlloc = RT_ALIGN_Z(cbToWrite, _1K);
792 }
793 else
794 rc = VERR_NO_MEMORY;
795 }
796 else if (cbToWrite > RTPIPE_NT_SIZE)
797 cbToWrite = RTPIPE_NT_SIZE;
798 if (RT_SUCCESS(rc) && cbToWrite)
799 {
800 memcpy(pThis->pbBounceBuf, pvBuf, cbToWrite);
801 pThis->cbBounceBufUsed = (uint32_t)cbToWrite;
802
803 /* Submit the write. */
804 rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
805 DWORD cbWritten = 0;
806 if (WriteFile(pThis->hPipe, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed,
807 &cbWritten, &pThis->Overlapped))
808 {
809 *pcbWritten = RT_MIN(cbWritten, cbToWrite); /* paranoia^3 */
810 rc = VINF_SUCCESS;
811 }
812 else if (GetLastError() == ERROR_IO_PENDING)
813 {
814 *pcbWritten = cbToWrite;
815 pThis->fIOPending = true;
816 rc = VINF_SUCCESS;
817 }
818 else if (GetLastError() == ERROR_NO_DATA)
819 rc = VERR_BROKEN_PIPE;
820 else
821 rc = RTErrConvertFromWin32(GetLastError());
822 }
823 else if (RT_SUCCESS(rc))
824 *pcbWritten = 0;
825 }
826 else if (RT_SUCCESS(rc))
827 *pcbWritten = 0;
828
829 if (rc == VERR_BROKEN_PIPE)
830 pThis->fBrokenPipe = true;
831
832 pThis->cUsers--;
833 }
834 else
835 rc = VERR_WRONG_ORDER;
836 RTCritSectLeave(&pThis->CritSect);
837 }
838 return rc;
839}
840
841
842RTDECL(int) RTPipeWriteBlocking(RTPIPE hPipe, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
843{
844 RTPIPEINTERNAL *pThis = hPipe;
845 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
846 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
847 AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
848 AssertPtr(pvBuf);
849 AssertPtrNull(pcbWritten);
850
851 int rc = RTCritSectEnter(&pThis->CritSect);
852 if (RT_SUCCESS(rc))
853 {
854 /* No concurrent writers, sorry. */
855 if (pThis->cUsers == 0)
856 {
857 pThis->cUsers++;
858
859 /*
860 * If I/O is pending, wait for it to complete.
861 */
862 if (pThis->fIOPending)
863 {
864 rc = rtPipeWriteCheckCompletion(pThis);
865 while (rc == VINF_TRY_AGAIN)
866 {
867 Assert(pThis->fIOPending);
868 HANDLE hEvent = pThis->Overlapped.hEvent;
869 RTCritSectLeave(&pThis->CritSect);
870 WaitForSingleObject(hEvent, INFINITE);
871 RTCritSectEnter(&pThis->CritSect);
872 }
873 }
874 if (RT_SUCCESS(rc))
875 {
876 Assert(!pThis->fIOPending);
877 pThis->fPromisedWritable = false;
878
879 /*
880 * Try write everything.
881 * No bounce buffering, cUsers protects us.
882 */
883 size_t cbTotalWritten = 0;
884 while (cbToWrite > 0)
885 {
886 rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
887 pThis->fIOPending = true;
888 RTCritSectLeave(&pThis->CritSect);
889
890 DWORD cbWritten = 0;
891 DWORD const cbToWriteInThisIteration = cbToWrite <= ~(DWORD)0 ? (DWORD)cbToWrite : ~(DWORD)0;
892 if (WriteFile(pThis->hPipe, pvBuf, cbToWriteInThisIteration, &cbWritten, &pThis->Overlapped))
893 rc = VINF_SUCCESS;
894 else if (GetLastError() == ERROR_IO_PENDING)
895 {
896 WaitForSingleObject(pThis->Overlapped.hEvent, INFINITE);
897 if (GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbWritten, TRUE /*fWait*/))
898 rc = VINF_SUCCESS;
899 else
900 rc = RTErrConvertFromWin32(GetLastError());
901 }
902 else if (GetLastError() == ERROR_NO_DATA)
903 rc = VERR_BROKEN_PIPE;
904 else
905 rc = RTErrConvertFromWin32(GetLastError());
906
907 RTCritSectEnter(&pThis->CritSect);
908 pThis->fIOPending = false;
909 if (RT_FAILURE(rc))
910 break;
911
912 /* advance */
913 if (cbWritten > cbToWriteInThisIteration) /* paranoia^3 */
914 cbWritten = cbToWriteInThisIteration;
915 pvBuf = (char const *)pvBuf + cbWritten;
916 cbTotalWritten += cbWritten;
917 cbToWrite -= cbWritten;
918 }
919
920 if (pcbWritten)
921 {
922 *pcbWritten = cbTotalWritten;
923 if ( RT_FAILURE(rc)
924 && cbTotalWritten
925 && rc != VERR_INVALID_POINTER)
926 rc = VINF_SUCCESS;
927 }
928 }
929
930 if (rc == VERR_BROKEN_PIPE)
931 pThis->fBrokenPipe = true;
932
933 pThis->cUsers--;
934 }
935 else
936 rc = VERR_WRONG_ORDER;
937 RTCritSectLeave(&pThis->CritSect);
938 }
939 return rc;
940
941#if 0 /** @todo r=bird: What's this? */
942 int rc = rtPipeTryBlocking(pThis);
943 if (RT_SUCCESS(rc))
944 {
945 size_t cbTotalWritten = 0;
946 while (cbToWrite > 0)
947 {
948 ssize_t cbWritten = write(pThis->fd, pvBuf, RT_MIN(cbToWrite, SSIZE_MAX));
949 if (cbWritten < 0)
950 {
951 rc = RTErrConvertFromErrno(errno);
952 break;
953 }
954
955 /* advance */
956 pvBuf = (char const *)pvBuf + cbWritten;
957 cbTotalWritten += cbWritten;
958 cbToWrite -= cbWritten;
959 }
960
961 if (pcbWritten)
962 {
963 *pcbWritten = cbTotalWritten;
964 if ( RT_FAILURE(rc)
965 && cbTotalWritten
966 && rc != VERR_INVALID_POINTER)
967 rc = VINF_SUCCESS;
968 }
969
970 ASMAtomicDecU32(&pThis->u32State);
971 }
972 return rc;
973#endif
974}
975
976
977RTDECL(int) RTPipeFlush(RTPIPE hPipe)
978{
979 RTPIPEINTERNAL *pThis = hPipe;
980 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
981 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
982 AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
983
984 if (!FlushFileBuffers(pThis->hPipe))
985 {
986 int rc = RTErrConvertFromWin32(GetLastError());
987 if (rc == VERR_BROKEN_PIPE)
988 pThis->fBrokenPipe = true;
989 return rc;
990 }
991 return VINF_SUCCESS;
992}
993
994
995RTDECL(int) RTPipeSelectOne(RTPIPE hPipe, RTMSINTERVAL cMillies)
996{
997 RTPIPEINTERNAL *pThis = hPipe;
998 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
999 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
1000
1001 uint64_t const StartMsTS = RTTimeMilliTS();
1002
1003 int rc = RTCritSectEnter(&pThis->CritSect);
1004 if (RT_FAILURE(rc))
1005 return rc;
1006 for (unsigned iLoop = 0;; iLoop++)
1007 {
1008 HANDLE hWait = INVALID_HANDLE_VALUE;
1009 if (pThis->fRead)
1010 {
1011 if (pThis->fIOPending)
1012 hWait = pThis->Overlapped.hEvent;
1013 else
1014 {
1015 /* Peek at the pipe buffer and see how many bytes it contains. */
1016 DWORD cbAvailable;
1017 if ( PeekNamedPipe(pThis->hPipe, NULL, 0, NULL, &cbAvailable, NULL)
1018 && cbAvailable > 0)
1019 {
1020 rc = VINF_SUCCESS;
1021 break;
1022 }
1023
1024 /* Start a zero byte read operation that we can wait on. */
1025 if (cMillies == 0)
1026 {
1027 rc = VERR_TIMEOUT;
1028 break;
1029 }
1030 AssertBreakStmt(pThis->cUsers == 0, rc = VERR_INTERNAL_ERROR_5);
1031 rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
1032 DWORD cbRead = 0;
1033 if (ReadFile(pThis->hPipe, pThis->abBuf, 0, &cbRead, &pThis->Overlapped))
1034 {
1035 rc = VINF_SUCCESS;
1036 if (iLoop > 10)
1037 RTThreadYield();
1038 }
1039 else if (GetLastError() == ERROR_IO_PENDING)
1040 {
1041 pThis->cUsers++;
1042 pThis->fIOPending = true;
1043 pThis->fZeroByteRead = true;
1044 hWait = pThis->Overlapped.hEvent;
1045 }
1046 else
1047 rc = RTErrConvertFromWin32(GetLastError());
1048 }
1049 }
1050 else
1051 {
1052 if (pThis->fIOPending)
1053 {
1054 rc = rtPipeWriteCheckCompletion(pThis);
1055 if (RT_FAILURE(rc))
1056 break;
1057 }
1058 if (pThis->fIOPending)
1059 hWait = pThis->Overlapped.hEvent;
1060 else
1061 {
1062 FILE_PIPE_LOCAL_INFORMATION Info;
1063#if 1
1064 /* We can always write one bounce buffer full of data regardless of
1065 the pipe buffer state. We must of course take this into account,
1066 or code like "Full write buffer" test in tstRTPipe gets confused. */
1067 rc = VINF_SUCCESS;
1068 if (rtPipeQueryNtInfo(pThis, &Info))
1069 {
1070 /* Check for broken pipe. */
1071 if (Info.NamedPipeState != FILE_PIPE_CLOSING_STATE)
1072 pThis->fPromisedWritable = true;
1073 else
1074 rc = VERR_BROKEN_PIPE;
1075 }
1076 else
1077 pThis->fPromisedWritable = true;
1078 break;
1079
1080#else /* old code: */
1081 if (rtPipeQueryNtInfo(pThis, &Info))
1082 {
1083 /* Check for broken pipe. */
1084 if (Info.NamedPipeState == FILE_PIPE_CLOSING_STATE)
1085 {
1086 rc = VERR_BROKEN_PIPE;
1087 break;
1088 }
1089
1090 /* Check for available write buffer space. */
1091 if (Info.WriteQuotaAvailable > 0)
1092 {
1093 pThis->fPromisedWritable = false;
1094 rc = VINF_SUCCESS;
1095 break;
1096 }
1097
1098 /* delayed buffer alloc or timeout: phony promise
1099 later: See if we still can associate a semaphore with
1100 the pipe, like on OS/2. */
1101 if (Info.OutboundQuota == 0 || cMillies)
1102 {
1103 pThis->fPromisedWritable = true;
1104 rc = VINF_SUCCESS;
1105 break;
1106 }
1107 }
1108 else
1109 {
1110 pThis->fPromisedWritable = true;
1111 rc = VINF_SUCCESS;
1112 break;
1113 }
1114#endif
1115 }
1116 }
1117 if (RT_FAILURE(rc))
1118 break;
1119
1120 /*
1121 * Check for timeout.
1122 */
1123 DWORD cMsMaxWait = INFINITE;
1124 if ( cMillies != RT_INDEFINITE_WAIT
1125 && ( hWait != INVALID_HANDLE_VALUE
1126 || iLoop > 10)
1127 )
1128 {
1129 uint64_t cElapsed = RTTimeMilliTS() - StartMsTS;
1130 if (cElapsed >= cMillies)
1131 {
1132 rc = VERR_TIMEOUT;
1133 break;
1134 }
1135 cMsMaxWait = cMillies - (uint32_t)cElapsed;
1136 }
1137
1138 /*
1139 * Wait.
1140 */
1141 if (hWait != INVALID_HANDLE_VALUE)
1142 {
1143 RTCritSectLeave(&pThis->CritSect);
1144
1145 DWORD dwRc = WaitForSingleObject(hWait, cMsMaxWait);
1146 if (dwRc == WAIT_OBJECT_0)
1147 rc = VINF_SUCCESS;
1148 else if (dwRc == WAIT_TIMEOUT)
1149 rc = VERR_TIMEOUT;
1150 else if (dwRc == WAIT_ABANDONED)
1151 rc = VERR_INVALID_HANDLE;
1152 else
1153 rc = RTErrConvertFromWin32(GetLastError());
1154 if ( RT_FAILURE(rc)
1155 && pThis->u32Magic != RTPIPE_MAGIC)
1156 return rc;
1157
1158 RTCritSectEnter(&pThis->CritSect);
1159 if (pThis->fZeroByteRead)
1160 {
1161 pThis->cUsers--;
1162 pThis->fIOPending = false;
1163 if (rc != VINF_SUCCESS)
1164 CancelIo(pThis->hPipe);
1165 DWORD cbRead = 0;
1166 GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/);
1167 }
1168 if (RT_FAILURE(rc))
1169 break;
1170 }
1171 }
1172
1173 if (rc == VERR_BROKEN_PIPE)
1174 pThis->fBrokenPipe = true;
1175
1176 RTCritSectLeave(&pThis->CritSect);
1177 return rc;
1178}
1179
1180
1181RTDECL(int) RTPipeQueryReadable(RTPIPE hPipe, size_t *pcbReadable)
1182{
1183 RTPIPEINTERNAL *pThis = hPipe;
1184 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1185 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
1186 AssertReturn(pThis->fRead, VERR_PIPE_NOT_READ);
1187 AssertPtrReturn(pcbReadable, VERR_INVALID_POINTER);
1188
1189 int rc = RTCritSectEnter(&pThis->CritSect);
1190 if (RT_FAILURE(rc))
1191 return rc;
1192
1193 /** @todo The file size should give the same info and be slightly faster... */
1194 DWORD cbAvailable = 0;
1195 if (PeekNamedPipe(pThis->hPipe, NULL, 0, NULL, &cbAvailable, NULL))
1196 {
1197#if ARCH_BITS == 32
1198 /*
1199 * Kludge!
1200 *
1201 * Prior to XP SP1 (?), the returned cbAvailable value was not adjusted
1202 * by the read position in the current message/buffer, so it could
1203 * potentially be too high. This may cause the caller to try read more
1204 * data than what's actually available, which may cause the read to
1205 * block when the caller thought it wouldn't.
1206 *
1207 * To get an accurate readable size, we have to provide an output
1208 * buffer and see how much we actually get back in it, as the data
1209 * peeking works correctly (as you would expect).
1210 */
1211 if (cbAvailable == 0 || g_enmWinVer >= kRTWinOSType_XP64)
1212 { /* No data available or kernel shouldn't be affected. */ }
1213 else
1214 {
1215 for (unsigned i = 0; ; i++)
1216 {
1217 uint8_t abBufStack[_16K];
1218 void *pvBufFree = NULL;
1219 void *pvBuf;
1220 DWORD cbBuf = RT_ALIGN_32(cbAvailable + i * 256, 64);
1221 if (cbBuf <= sizeof(abBufStack))
1222 {
1223 pvBuf = abBufStack;
1224 /* No cbBuf = sizeof(abBufStack) here! PeekNamedPipe bounce buffers the request on the heap. */
1225 }
1226 else
1227 {
1228 pvBufFree = pvBuf = RTMemTmpAlloc(cbBuf);
1229 if (!pvBuf)
1230 {
1231 rc = VERR_NO_TMP_MEMORY;
1232 cbAvailable = 1;
1233 break;
1234 }
1235 }
1236
1237 DWORD cbAvailable2 = 0;
1238 DWORD cbRead = 0;
1239 BOOL fRc = PeekNamedPipe(pThis->hPipe, pvBuf, cbBuf, &cbRead, &cbAvailable2, NULL);
1240 Log(("RTPipeQueryReadable: #%u: cbAvailable=%#x cbRead=%#x cbAvailable2=%#x (cbBuf=%#x)\n",
1241 i, cbAvailable, cbRead, cbAvailable2, cbBuf));
1242
1243 RTMemTmpFree(pvBufFree);
1244
1245 if (fRc)
1246 {
1247 if (cbAvailable2 <= cbBuf || i >= 10)
1248 cbAvailable = cbRead;
1249 else
1250 {
1251 cbAvailable = cbAvailable2;
1252 continue;
1253 }
1254 }
1255 else
1256 {
1257 rc = RTErrConvertFromWin32(GetLastError());
1258 cbAvailable = 1;
1259 }
1260 break;
1261 }
1262 }
1263#endif
1264 *pcbReadable = cbAvailable;
1265 }
1266 else
1267 rc = RTErrConvertFromWin32(GetLastError());
1268
1269 RTCritSectLeave(&pThis->CritSect);
1270 return rc;
1271}
1272
1273
1274RTDECL(int) RTPipeQueryInfo(RTPIPE hPipe, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1275{
1276 RTPIPEINTERNAL *pThis = hPipe;
1277 AssertPtrReturn(pThis, 0);
1278 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, 0);
1279
1280 int rc = RTCritSectEnter(&pThis->CritSect);
1281 AssertRCReturn(rc, 0);
1282
1283 rtPipeFakeQueryInfo(pObjInfo, enmAddAttr, pThis->fRead);
1284
1285 FILE_PIPE_LOCAL_INFORMATION Info;
1286 if (rtPipeQueryNtInfo(pThis, &Info))
1287 {
1288 pObjInfo->cbAllocated = pThis->fRead ? Info.InboundQuota : Info.OutboundQuota;
1289 pObjInfo->cbObject = pThis->fRead ? Info.ReadDataAvailable : Info.WriteQuotaAvailable;
1290 }
1291
1292 RTCritSectLeave(&pThis->CritSect);
1293 return VINF_SUCCESS;
1294}
1295
1296
1297int rtPipePollGetHandle(RTPIPE hPipe, uint32_t fEvents, PRTHCINTPTR phNative)
1298{
1299 RTPIPEINTERNAL *pThis = hPipe;
1300 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1301 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
1302
1303 AssertReturn(!(fEvents & RTPOLL_EVT_READ) || pThis->fRead, VERR_INVALID_PARAMETER);
1304 AssertReturn(!(fEvents & RTPOLL_EVT_WRITE) || !pThis->fRead, VERR_INVALID_PARAMETER);
1305
1306 /* Later: Try register an event handle with the pipe like on OS/2, there is
1307 a file control for doing this obviously intended for the OS/2 subsys.
1308 The question is whether this still exists on Vista and W7. */
1309 *phNative = (RTHCINTPTR)pThis->Overlapped.hEvent;
1310 return VINF_SUCCESS;
1311}
1312
1313
1314/**
1315 * Checks for pending events.
1316 *
1317 * @returns Event mask or 0.
1318 * @param pThis The pipe handle.
1319 * @param fEvents The desired events.
1320 */
1321static uint32_t rtPipePollCheck(RTPIPEINTERNAL *pThis, uint32_t fEvents)
1322{
1323 uint32_t fRetEvents = 0;
1324 if (pThis->fBrokenPipe)
1325 fRetEvents |= RTPOLL_EVT_ERROR;
1326 else if (pThis->fRead)
1327 {
1328 if (!pThis->fIOPending)
1329 {
1330 DWORD cbAvailable;
1331 if (PeekNamedPipe(pThis->hPipe, NULL, 0, NULL, &cbAvailable, NULL))
1332 {
1333 if ( (fEvents & RTPOLL_EVT_READ)
1334 && cbAvailable > 0)
1335 fRetEvents |= RTPOLL_EVT_READ;
1336 }
1337 else
1338 {
1339 if (GetLastError() == ERROR_BROKEN_PIPE)
1340 pThis->fBrokenPipe = true;
1341 fRetEvents |= RTPOLL_EVT_ERROR;
1342 }
1343 }
1344 }
1345 else
1346 {
1347 if (pThis->fIOPending)
1348 {
1349 rtPipeWriteCheckCompletion(pThis);
1350 if (pThis->fBrokenPipe)
1351 fRetEvents |= RTPOLL_EVT_ERROR;
1352 }
1353 if ( !pThis->fIOPending
1354 && !fRetEvents)
1355 {
1356 FILE_PIPE_LOCAL_INFORMATION Info;
1357 if (rtPipeQueryNtInfo(pThis, &Info))
1358 {
1359 /* Check for broken pipe. */
1360 if (Info.NamedPipeState == FILE_PIPE_CLOSING_STATE)
1361 {
1362 fRetEvents = RTPOLL_EVT_ERROR;
1363 pThis->fBrokenPipe = true;
1364 }
1365
1366 /* Check if there is available buffer space. */
1367 if ( !fRetEvents
1368 && (fEvents & RTPOLL_EVT_WRITE)
1369 && ( Info.WriteQuotaAvailable > 0
1370 || Info.OutboundQuota == 0)
1371 )
1372 fRetEvents |= RTPOLL_EVT_WRITE;
1373 }
1374 else if (fEvents & RTPOLL_EVT_WRITE)
1375 fRetEvents |= RTPOLL_EVT_WRITE;
1376 }
1377 }
1378
1379 return fRetEvents;
1380}
1381
1382
1383/**
1384 * Internal RTPoll helper that polls the pipe handle and, if @a fNoWait is
1385 * clear, starts whatever actions we've got running during the poll call.
1386 *
1387 * @returns 0 if no pending events, actions initiated if @a fNoWait is clear.
1388 * Event mask (in @a fEvents) and no actions if the handle is ready
1389 * already.
1390 * UINT32_MAX (asserted) if the pipe handle is busy in I/O or a
1391 * different poll set.
1392 *
1393 * @param hPipe The pipe handle.
1394 * @param hPollSet The poll set handle (for access checks).
1395 * @param fEvents The events we're polling for.
1396 * @param fFinalEntry Set if this is the final entry for this handle
1397 * in this poll set. This can be used for dealing
1398 * with duplicate entries.
1399 * @param fNoWait Set if it's a zero-wait poll call. Clear if
1400 * we'll wait for an event to occur.
1401 */
1402uint32_t rtPipePollStart(RTPIPE hPipe, RTPOLLSET hPollSet, uint32_t fEvents, bool fFinalEntry, bool fNoWait)
1403{
1404 /** @todo All this polling code could be optimized to make fewer system
1405 * calls; like for instance the ResetEvent calls. */
1406 RTPIPEINTERNAL *pThis = hPipe;
1407 AssertPtrReturn(pThis, UINT32_MAX);
1408 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, UINT32_MAX);
1409 RT_NOREF_PV(fFinalEntry);
1410
1411 int rc = RTCritSectEnter(&pThis->CritSect);
1412 AssertRCReturn(rc, UINT32_MAX);
1413
1414 /* Check that this is the only current use of this pipe. */
1415 uint32_t fRetEvents;
1416 if ( pThis->cUsers == 0
1417 || pThis->hPollSet == hPollSet)
1418 {
1419 /* Check what the current events are. */
1420 fRetEvents = rtPipePollCheck(pThis, fEvents);
1421 if ( !fRetEvents
1422 && !fNoWait)
1423 {
1424 /* Make sure the event semaphore has been reset. */
1425 if (!pThis->fIOPending)
1426 {
1427 rc = ResetEvent(pThis->Overlapped.hEvent);
1428 Assert(rc == TRUE);
1429 }
1430
1431 /* Kick off the zero byte read thing if applicable. */
1432 if ( !pThis->fIOPending
1433 && pThis->fRead
1434 && (fEvents & RTPOLL_EVT_READ)
1435 )
1436 {
1437 DWORD cbRead = 0;
1438 if (ReadFile(pThis->hPipe, pThis->abBuf, 0, &cbRead, &pThis->Overlapped))
1439 fRetEvents = rtPipePollCheck(pThis, fEvents);
1440 else if (GetLastError() == ERROR_IO_PENDING)
1441 {
1442 pThis->fIOPending = true;
1443 pThis->fZeroByteRead = true;
1444 }
1445 else
1446 fRetEvents = RTPOLL_EVT_ERROR;
1447 }
1448
1449 /* If we're still set for the waiting, record the poll set and
1450 mark the pipe used. */
1451 if (!fRetEvents)
1452 {
1453 pThis->cUsers++;
1454 pThis->hPollSet = hPollSet;
1455 }
1456 }
1457 }
1458 else
1459 {
1460 AssertFailed();
1461 fRetEvents = UINT32_MAX;
1462 }
1463
1464 RTCritSectLeave(&pThis->CritSect);
1465 return fRetEvents;
1466}
1467
1468
1469/**
1470 * Called after a WaitForMultipleObjects returned in order to check for pending
1471 * events and stop whatever actions that rtPipePollStart() initiated.
1472 *
1473 * @returns Event mask or 0.
1474 *
1475 * @param hPipe The pipe handle.
1476 * @param fEvents The events we're polling for.
1477 * @param fFinalEntry Set if this is the final entry for this handle
1478 * in this poll set. This can be used for dealing
1479 * with duplicate entries. Only keep in mind that
1480 * this method is called in reverse order, so the
1481 * first call will have this set (when the entire
1482 * set was processed).
1483 * @param fHarvestEvents Set if we should check for pending events.
1484 */
1485uint32_t rtPipePollDone(RTPIPE hPipe, uint32_t fEvents, bool fFinalEntry, bool fHarvestEvents)
1486{
1487 RTPIPEINTERNAL *pThis = hPipe;
1488 AssertPtrReturn(pThis, 0);
1489 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, 0);
1490 RT_NOREF_PV(fFinalEntry);
1491 RT_NOREF_PV(fHarvestEvents);
1492
1493 int rc = RTCritSectEnter(&pThis->CritSect);
1494 AssertRCReturn(rc, 0);
1495
1496 Assert(pThis->cUsers > 0);
1497
1498
1499 /* Cancel the zero byte read. */
1500 uint32_t fRetEvents = 0;
1501 if (pThis->fZeroByteRead)
1502 {
1503 CancelIo(pThis->hPipe);
1504 DWORD cbRead = 0;
1505 if ( !GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/)
1506 && GetLastError() != ERROR_OPERATION_ABORTED)
1507 fRetEvents = RTPOLL_EVT_ERROR;
1508
1509 pThis->fIOPending = false;
1510 pThis->fZeroByteRead = false;
1511 }
1512
1513 /* harvest events. */
1514 fRetEvents |= rtPipePollCheck(pThis, fEvents);
1515
1516 /* update counters. */
1517 pThis->cUsers--;
1518 /** @todo This isn't sane, or is it? See OS/2 impl. */
1519 if (!pThis->cUsers)
1520 pThis->hPollSet = NIL_RTPOLLSET;
1521
1522 RTCritSectLeave(&pThis->CritSect);
1523 return fRetEvents;
1524}
1525
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