VirtualBox

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

Last change on this file was 98618, checked in by vboxsync, 15 months ago

IPRT/pipe-win.cpp: Deal correctly with partial overlapping writes (shouldn't happen).

  • 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 98618 2023-02-17 12:40:37Z vboxsync $ */
2/** @file
3 * IPRT - Anonymous Pipes, Windows Implementation.
4 */
5
6/*
7 * Copyright (C) 2010-2023 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 pThis->cbBounceBufUsed -= cbWritten;
329 memmove(&pThis->pbBounceBuf[0], &pThis->pbBounceBuf[cbWritten], pThis->cbBounceBufUsed);
330 rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
331 if (!WriteFile(pThis->hPipe, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed, &cbWritten, &pThis->Overlapped))
332 {
333 DWORD const dwErr = GetLastError();
334 if (dwErr == ERROR_IO_PENDING)
335 rc = VINF_TRY_AGAIN;
336 else
337 {
338 pThis->fIOPending = false;
339 if (dwErr == ERROR_NO_DATA)
340 rc = VERR_BROKEN_PIPE;
341 else
342 rc = RTErrConvertFromWin32(GetLastError());
343 if (rc == VERR_BROKEN_PIPE)
344 pThis->fBrokenPipe = true;
345 }
346 break;
347 }
348 Assert(cbWritten > 0);
349 }
350 }
351 else
352 {
353 pThis->fIOPending = false;
354 rc = RTErrConvertFromWin32(GetLastError());
355 }
356 }
357 else if (dwRc == WAIT_TIMEOUT)
358 rc = VINF_TRY_AGAIN;
359 else
360 {
361 pThis->fIOPending = false;
362 if (dwRc == WAIT_ABANDONED)
363 rc = VERR_INVALID_HANDLE;
364 else
365 rc = RTErrConvertFromWin32(GetLastError());
366 }
367 return rc;
368}
369
370
371
372RTDECL(int) RTPipeCloseEx(RTPIPE hPipe, bool fLeaveOpen)
373{
374 RTPIPEINTERNAL *pThis = hPipe;
375 if (pThis == NIL_RTPIPE)
376 return VINF_SUCCESS;
377 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
378 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
379
380 /*
381 * Do the cleanup.
382 */
383 AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, ~RTPIPE_MAGIC, RTPIPE_MAGIC), VERR_INVALID_HANDLE);
384 RTCritSectEnter(&pThis->CritSect);
385 Assert(pThis->cUsers == 0);
386
387 if (!pThis->fRead && pThis->fIOPending)
388 rtPipeWriteCheckCompletion(pThis);
389
390 if (!fLeaveOpen && !pThis->fLeaveOpen)
391 CloseHandle(pThis->hPipe);
392 pThis->hPipe = INVALID_HANDLE_VALUE;
393
394 CloseHandle(pThis->Overlapped.hEvent);
395 pThis->Overlapped.hEvent = NULL;
396
397 RTMemFree(pThis->pbBounceBuf);
398 pThis->pbBounceBuf = NULL;
399
400 RTCritSectLeave(&pThis->CritSect);
401 RTCritSectDelete(&pThis->CritSect);
402
403 RTMemFree(pThis);
404
405 return VINF_SUCCESS;
406}
407
408
409RTDECL(int) RTPipeClose(RTPIPE hPipe)
410{
411 return RTPipeCloseEx(hPipe, false /*fLeaveOpen*/);
412}
413
414
415RTDECL(int) RTPipeFromNative(PRTPIPE phPipe, RTHCINTPTR hNativePipe, uint32_t fFlags)
416{
417 AssertPtrReturn(phPipe, VERR_INVALID_POINTER);
418 AssertReturn(!(fFlags & ~RTPIPE_N_VALID_MASK_FN), VERR_INVALID_PARAMETER);
419 AssertReturn(!!(fFlags & RTPIPE_N_READ) != !!(fFlags & RTPIPE_N_WRITE), VERR_INVALID_PARAMETER);
420
421 /*
422 * Get and validate the pipe handle info.
423 */
424 HANDLE hNative = (HANDLE)hNativePipe;
425 AssertReturn(GetFileType(hNative) == FILE_TYPE_PIPE, VERR_INVALID_HANDLE);
426
427 DWORD cMaxInstances;
428 DWORD fInfo;
429 if (!GetNamedPipeInfo(hNative, &fInfo, NULL, NULL, &cMaxInstances))
430 return RTErrConvertFromWin32(GetLastError());
431 /* Doesn't seem to matter to much if the pipe is message or byte type. Cygwin
432 seems to hand us such pipes when capturing output (@bugref{9397}), so just
433 ignore skip this check:
434 AssertReturn(!(fInfo & PIPE_TYPE_MESSAGE), VERR_INVALID_HANDLE); */
435 AssertReturn(cMaxInstances == 1, VERR_INVALID_HANDLE);
436
437 DWORD cInstances;
438 DWORD fState;
439 if (!GetNamedPipeHandleState(hNative, &fState, &cInstances, NULL, NULL, NULL, 0))
440 return RTErrConvertFromWin32(GetLastError());
441 AssertReturn(!(fState & PIPE_NOWAIT), VERR_INVALID_HANDLE);
442 AssertReturn(!(fState & PIPE_READMODE_MESSAGE), VERR_INVALID_HANDLE);
443 AssertReturn(cInstances <= 1, VERR_INVALID_HANDLE);
444
445 /*
446 * Looks kind of OK, create a handle so we can try rtPipeQueryNtInfo on it
447 * and see if we need to duplicate it to make that call work.
448 */
449 RTPIPEINTERNAL *pThis = (RTPIPEINTERNAL *)RTMemAllocZ(sizeof(RTPIPEINTERNAL));
450 if (!pThis)
451 return VERR_NO_MEMORY;
452 int rc = RTCritSectInit(&pThis->CritSect);
453 if (RT_SUCCESS(rc))
454 {
455 pThis->Overlapped.hEvent = CreateEvent(NULL, TRUE /*fManualReset*/,
456 TRUE /*fInitialState*/, NULL /*pName*/);
457 if (pThis->Overlapped.hEvent != NULL)
458 {
459 pThis->u32Magic = RTPIPE_MAGIC;
460 pThis->hPipe = hNative;
461 pThis->fRead = RT_BOOL(fFlags & RTPIPE_N_READ);
462 pThis->fLeaveOpen = RT_BOOL(fFlags & RTPIPE_N_LEAVE_OPEN);
463 //pThis->fIOPending = false;
464 //pThis->fZeroByteRead = false;
465 //pThis->fBrokenPipe = false;
466 //pThis->fPromisedWritable = false;
467 pThis->fCreatedInheritable = RT_BOOL(fFlags & RTPIPE_N_INHERIT);
468 //pThis->cUsers = 0;
469 //pThis->pbBounceBuf = NULL;
470 //pThis->cbBounceBufUsed = 0;
471 //pThis->cbBounceBufAlloc = 0;
472 pThis->hPollSet = NIL_RTPOLLSET;
473
474 HANDLE hNative2 = INVALID_HANDLE_VALUE;
475 FILE_PIPE_LOCAL_INFORMATION Info;
476 RT_ZERO(Info);
477 if ( g_pfnSetHandleInformation
478 && rtPipeQueryNtInfo(pThis, &Info))
479 rc = VINF_SUCCESS;
480 else
481 {
482 if (DuplicateHandle(GetCurrentProcess() /*hSrcProcess*/, hNative /*hSrcHandle*/,
483 GetCurrentProcess() /*hDstProcess*/, &hNative2 /*phDstHandle*/,
484 pThis->fRead ? GENERIC_READ : GENERIC_WRITE | FILE_READ_ATTRIBUTES /*dwDesiredAccess*/,
485 !!(fFlags & RTPIPE_N_INHERIT) /*fInheritHandle*/,
486 0 /*dwOptions*/))
487 {
488 pThis->hPipe = hNative2;
489 if (rtPipeQueryNtInfo(pThis, &Info))
490 {
491 pThis->fLeaveOpen = false;
492 rc = VINF_SUCCESS;
493 }
494 else
495 {
496 rc = VERR_ACCESS_DENIED;
497 CloseHandle(hNative2);
498 }
499 }
500 else
501 hNative2 = INVALID_HANDLE_VALUE;
502 }
503 if (RT_SUCCESS(rc))
504 {
505 /*
506 * Verify the pipe state and correct the inheritability.
507 */
508 AssertStmt( Info.NamedPipeState == FILE_PIPE_CONNECTED_STATE
509 || Info.NamedPipeState == FILE_PIPE_CLOSING_STATE
510 || Info.NamedPipeState == FILE_PIPE_DISCONNECTED_STATE,
511 VERR_INVALID_HANDLE);
512 AssertStmt( Info.NamedPipeConfiguration
513 == ( Info.NamedPipeEnd == FILE_PIPE_SERVER_END
514 ? (pThis->fRead ? FILE_PIPE_INBOUND : FILE_PIPE_OUTBOUND)
515 : (pThis->fRead ? FILE_PIPE_OUTBOUND : FILE_PIPE_INBOUND) )
516 || Info.NamedPipeConfiguration == FILE_PIPE_FULL_DUPLEX,
517 VERR_INVALID_HANDLE);
518 if ( RT_SUCCESS(rc)
519 && hNative2 == INVALID_HANDLE_VALUE
520 && !g_pfnSetHandleInformation(hNative,
521 HANDLE_FLAG_INHERIT /*dwMask*/,
522 fFlags & RTPIPE_N_INHERIT ? HANDLE_FLAG_INHERIT : 0))
523 {
524 rc = RTErrConvertFromWin32(GetLastError());
525 AssertMsgFailed(("%Rrc\n", rc));
526 }
527 if (RT_SUCCESS(rc))
528 {
529 /*
530 * Ok, we're good! If we replaced the handle, make sure it's not a standard
531 * handle if we think we need to close it.
532 */
533 if (hNative2 != INVALID_HANDLE_VALUE)
534 {
535 if ( !(fFlags & RTPIPE_N_LEAVE_OPEN)
536 && hNative != GetStdHandle(STD_INPUT_HANDLE)
537 && hNative != GetStdHandle(STD_OUTPUT_HANDLE)
538 && hNative != GetStdHandle(STD_ERROR_HANDLE) )
539 CloseHandle(hNative);
540 }
541 *phPipe = pThis;
542 return VINF_SUCCESS;
543 }
544 }
545
546 /* Bail out. */
547 if (hNative2 != INVALID_HANDLE_VALUE)
548 CloseHandle(hNative2);
549 CloseHandle(pThis->Overlapped.hEvent);
550 }
551 RTCritSectDelete(&pThis->CritSect);
552 }
553 RTMemFree(pThis);
554 return rc;
555}
556
557
558RTDECL(RTHCINTPTR) RTPipeToNative(RTPIPE hPipe)
559{
560 RTPIPEINTERNAL *pThis = hPipe;
561 AssertPtrReturn(pThis, -1);
562 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, -1);
563
564 return (RTHCINTPTR)pThis->hPipe;
565}
566
567
568RTDECL(int) RTPipeGetCreationInheritability(RTPIPE hPipe)
569{
570 RTPIPEINTERNAL *pThis = hPipe;
571 AssertPtrReturn(pThis, false);
572 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, false);
573
574 return pThis->fCreatedInheritable;
575}
576
577
578RTDECL(int) RTPipeRead(RTPIPE hPipe, void *pvBuf, size_t cbToRead, size_t *pcbRead)
579{
580 RTPIPEINTERNAL *pThis = hPipe;
581 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
582 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
583 AssertReturn(pThis->fRead, VERR_ACCESS_DENIED);
584 AssertPtr(pcbRead);
585 AssertPtr(pvBuf);
586
587 int rc = RTCritSectEnter(&pThis->CritSect);
588 if (RT_SUCCESS(rc))
589 {
590 /* No concurrent readers, sorry. */
591 if (pThis->cUsers == 0)
592 {
593 pThis->cUsers++;
594
595 /*
596 * Kick off a an overlapped read. It should return immediately if
597 * there are bytes in the buffer. If not, we'll cancel it and see
598 * what we get back.
599 */
600 rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
601 DWORD cbRead = 0;
602 if ( cbToRead == 0
603 || ReadFile(pThis->hPipe, pvBuf,
604 cbToRead <= ~(DWORD)0 ? (DWORD)cbToRead : ~(DWORD)0,
605 &cbRead, &pThis->Overlapped))
606 {
607 *pcbRead = cbRead;
608 rc = VINF_SUCCESS;
609 }
610 else if (GetLastError() == ERROR_IO_PENDING)
611 {
612 pThis->fIOPending = true;
613 RTCritSectLeave(&pThis->CritSect);
614
615 /* We use NtCancelIoFile here because the CancelIo API
616 providing access to it wasn't available till NT4. This
617 code needs to work (or at least load) with NT 3.1 */
618 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
619 NTSTATUS rcNt = NtCancelIoFile(pThis->hPipe, &Ios);
620 if (!NT_SUCCESS(rcNt))
621 WaitForSingleObject(pThis->Overlapped.hEvent, INFINITE);
622
623 if (GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/))
624 {
625 *pcbRead = cbRead;
626 rc = VINF_SUCCESS;
627 }
628 else if (GetLastError() == ERROR_OPERATION_ABORTED)
629 {
630 *pcbRead = 0;
631 rc = VINF_TRY_AGAIN;
632 }
633 else
634 rc = RTErrConvertFromWin32(GetLastError());
635
636 RTCritSectEnter(&pThis->CritSect);
637 pThis->fIOPending = false;
638 }
639 else
640 rc = RTErrConvertFromWin32(GetLastError());
641 if (rc == VERR_BROKEN_PIPE)
642 pThis->fBrokenPipe = true;
643
644 pThis->cUsers--;
645 }
646 else
647 rc = VERR_WRONG_ORDER;
648 RTCritSectLeave(&pThis->CritSect);
649 }
650 return rc;
651}
652
653
654RTDECL(int) RTPipeReadBlocking(RTPIPE hPipe, void *pvBuf, size_t cbToRead, size_t *pcbRead)
655{
656 RTPIPEINTERNAL *pThis = hPipe;
657 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
658 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
659 AssertReturn(pThis->fRead, VERR_ACCESS_DENIED);
660 AssertPtr(pvBuf);
661
662 int rc = RTCritSectEnter(&pThis->CritSect);
663 if (RT_SUCCESS(rc))
664 {
665 /* No concurrent readers, sorry. */
666 if (pThis->cUsers == 0)
667 {
668 pThis->cUsers++;
669
670 size_t cbTotalRead = 0;
671 while (cbToRead > 0)
672 {
673 /*
674 * Kick of a an overlapped read. It should return immediately if
675 * there is bytes in the buffer. If not, we'll cancel it and see
676 * what we get back.
677 */
678 rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
679 DWORD cbRead = 0;
680 pThis->fIOPending = true;
681 RTCritSectLeave(&pThis->CritSect);
682
683 if (ReadFile(pThis->hPipe, pvBuf,
684 cbToRead <= ~(DWORD)0 ? (DWORD)cbToRead : ~(DWORD)0,
685 &cbRead, &pThis->Overlapped))
686 rc = VINF_SUCCESS;
687 else if (GetLastError() == ERROR_IO_PENDING)
688 {
689 WaitForSingleObject(pThis->Overlapped.hEvent, INFINITE);
690 if (GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/))
691 rc = VINF_SUCCESS;
692 else
693 rc = RTErrConvertFromWin32(GetLastError());
694 }
695 else
696 rc = RTErrConvertFromWin32(GetLastError());
697
698 RTCritSectEnter(&pThis->CritSect);
699 pThis->fIOPending = false;
700 if (RT_FAILURE(rc))
701 break;
702
703 /* advance */
704 cbToRead -= cbRead;
705 cbTotalRead += cbRead;
706 pvBuf = (uint8_t *)pvBuf + cbRead;
707 }
708
709 if (rc == VERR_BROKEN_PIPE)
710 pThis->fBrokenPipe = true;
711
712 if (pcbRead)
713 {
714 *pcbRead = cbTotalRead;
715 if ( RT_FAILURE(rc)
716 && cbTotalRead
717 && rc != VERR_INVALID_POINTER)
718 rc = VINF_SUCCESS;
719 }
720
721 pThis->cUsers--;
722 }
723 else
724 rc = VERR_WRONG_ORDER;
725 RTCritSectLeave(&pThis->CritSect);
726 }
727 return rc;
728}
729
730
731RTDECL(int) RTPipeWrite(RTPIPE hPipe, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
732{
733 RTPIPEINTERNAL *pThis = hPipe;
734 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
735 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
736 AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
737 AssertPtr(pcbWritten);
738 AssertPtr(pvBuf);
739
740 int rc = RTCritSectEnter(&pThis->CritSect);
741 if (RT_SUCCESS(rc))
742 {
743 /* No concurrent writers, sorry. */
744 if (pThis->cUsers == 0)
745 {
746 pThis->cUsers++;
747
748 /* If I/O is pending, check if it has completed. */
749 if (pThis->fIOPending)
750 rc = rtPipeWriteCheckCompletion(pThis);
751 else
752 rc = VINF_SUCCESS;
753 if (rc == VINF_SUCCESS)
754 {
755 Assert(!pThis->fIOPending);
756
757 /* Adjust the number of bytes to write to fit into the current
758 buffer quota, unless we've promised stuff in RTPipeSelectOne.
759 WriteQuotaAvailable better not be zero when it shouldn't!! */
760 FILE_PIPE_LOCAL_INFORMATION Info;
761 if ( !pThis->fPromisedWritable
762 && cbToWrite > 0
763 && rtPipeQueryNtInfo(pThis, &Info))
764 {
765 if (Info.NamedPipeState == FILE_PIPE_CLOSING_STATE)
766 rc = VERR_BROKEN_PIPE;
767 /** @todo fixme: To get the pipe writing support to work the
768 * block below needs to be commented out until a
769 * way is found to address the problem of the incorrectly
770 * set field Info.WriteQuotaAvailable.
771 * Update: We now just write up to RTPIPE_NT_SIZE more. This is quite
772 * possibely what lead to the misunderstanding here wrt to
773 * WriteQuotaAvailable updating. */
774#if 0
775 else if ( cbToWrite >= Info.WriteQuotaAvailable
776 && Info.OutboundQuota != 0
777 && (Info.WriteQuotaAvailable || pThis->cbBounceBufAlloc)
778 )
779 {
780 cbToWrite = Info.WriteQuotaAvailable;
781 if (!cbToWrite)
782 rc = VINF_TRY_AGAIN;
783 }
784#endif
785 }
786 pThis->fPromisedWritable = false;
787
788 /* Do the bounce buffering. */
789 if ( pThis->cbBounceBufAlloc < cbToWrite
790 && pThis->cbBounceBufAlloc < RTPIPE_NT_SIZE)
791 {
792 if (cbToWrite > RTPIPE_NT_SIZE)
793 cbToWrite = RTPIPE_NT_SIZE;
794 void *pv = RTMemRealloc(pThis->pbBounceBuf, RT_ALIGN_Z(cbToWrite, _1K));
795 if (pv)
796 {
797 pThis->pbBounceBuf = (uint8_t *)pv;
798 pThis->cbBounceBufAlloc = RT_ALIGN_Z(cbToWrite, _1K);
799 }
800 else
801 rc = VERR_NO_MEMORY;
802 }
803 else if (cbToWrite > RTPIPE_NT_SIZE)
804 cbToWrite = RTPIPE_NT_SIZE;
805 if (RT_SUCCESS(rc) && cbToWrite)
806 {
807 memcpy(pThis->pbBounceBuf, pvBuf, cbToWrite);
808 pThis->cbBounceBufUsed = (uint32_t)cbToWrite;
809
810 /* Submit the write. */
811 rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
812 DWORD cbWritten = 0;
813 if (WriteFile(pThis->hPipe, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed,
814 &cbWritten, &pThis->Overlapped))
815 {
816 *pcbWritten = RT_MIN(cbWritten, cbToWrite); /* paranoia^3 */
817 rc = VINF_SUCCESS;
818 }
819 else if (GetLastError() == ERROR_IO_PENDING)
820 {
821 *pcbWritten = cbToWrite;
822 pThis->fIOPending = true;
823 rc = VINF_SUCCESS;
824 }
825 else if (GetLastError() == ERROR_NO_DATA)
826 rc = VERR_BROKEN_PIPE;
827 else
828 rc = RTErrConvertFromWin32(GetLastError());
829 }
830 else if (RT_SUCCESS(rc))
831 *pcbWritten = 0;
832 }
833 else if (RT_SUCCESS(rc))
834 *pcbWritten = 0;
835
836 if (rc == VERR_BROKEN_PIPE)
837 pThis->fBrokenPipe = true;
838
839 pThis->cUsers--;
840 }
841 else
842 rc = VERR_WRONG_ORDER;
843 RTCritSectLeave(&pThis->CritSect);
844 }
845 return rc;
846}
847
848
849RTDECL(int) RTPipeWriteBlocking(RTPIPE hPipe, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
850{
851 RTPIPEINTERNAL *pThis = hPipe;
852 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
853 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
854 AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
855 AssertPtr(pvBuf);
856 AssertPtrNull(pcbWritten);
857
858 int rc = RTCritSectEnter(&pThis->CritSect);
859 if (RT_SUCCESS(rc))
860 {
861 /* No concurrent writers, sorry. */
862 if (pThis->cUsers == 0)
863 {
864 pThis->cUsers++;
865
866 /*
867 * If I/O is pending, wait for it to complete.
868 */
869 if (pThis->fIOPending)
870 {
871 rc = rtPipeWriteCheckCompletion(pThis);
872 while (rc == VINF_TRY_AGAIN)
873 {
874 Assert(pThis->fIOPending);
875 HANDLE hEvent = pThis->Overlapped.hEvent;
876 RTCritSectLeave(&pThis->CritSect);
877 WaitForSingleObject(hEvent, INFINITE);
878 RTCritSectEnter(&pThis->CritSect);
879 }
880 }
881 if (RT_SUCCESS(rc))
882 {
883 Assert(!pThis->fIOPending);
884 pThis->fPromisedWritable = false;
885
886 /*
887 * Try write everything.
888 * No bounce buffering, cUsers protects us.
889 */
890 size_t cbTotalWritten = 0;
891 while (cbToWrite > 0)
892 {
893 rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
894 pThis->fIOPending = true;
895 RTCritSectLeave(&pThis->CritSect);
896
897 DWORD cbWritten = 0;
898 DWORD const cbToWriteInThisIteration = cbToWrite <= ~(DWORD)0 ? (DWORD)cbToWrite : ~(DWORD)0;
899 if (WriteFile(pThis->hPipe, pvBuf, cbToWriteInThisIteration, &cbWritten, &pThis->Overlapped))
900 rc = VINF_SUCCESS;
901 else if (GetLastError() == ERROR_IO_PENDING)
902 {
903 WaitForSingleObject(pThis->Overlapped.hEvent, INFINITE);
904 if (GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbWritten, TRUE /*fWait*/))
905 rc = VINF_SUCCESS;
906 else
907 rc = RTErrConvertFromWin32(GetLastError());
908 }
909 else if (GetLastError() == ERROR_NO_DATA)
910 rc = VERR_BROKEN_PIPE;
911 else
912 rc = RTErrConvertFromWin32(GetLastError());
913
914 RTCritSectEnter(&pThis->CritSect);
915 pThis->fIOPending = false;
916 if (RT_FAILURE(rc))
917 break;
918
919 /* advance */
920 if (cbWritten > cbToWriteInThisIteration) /* paranoia^3 */
921 cbWritten = cbToWriteInThisIteration;
922 pvBuf = (char const *)pvBuf + cbWritten;
923 cbTotalWritten += cbWritten;
924 cbToWrite -= cbWritten;
925 }
926
927 if (pcbWritten)
928 {
929 *pcbWritten = cbTotalWritten;
930 if ( RT_FAILURE(rc)
931 && cbTotalWritten
932 && rc != VERR_INVALID_POINTER)
933 rc = VINF_SUCCESS;
934 }
935 }
936
937 if (rc == VERR_BROKEN_PIPE)
938 pThis->fBrokenPipe = true;
939
940 pThis->cUsers--;
941 }
942 else
943 rc = VERR_WRONG_ORDER;
944 RTCritSectLeave(&pThis->CritSect);
945 }
946 return rc;
947
948#if 0 /** @todo r=bird: What's this? */
949 int rc = rtPipeTryBlocking(pThis);
950 if (RT_SUCCESS(rc))
951 {
952 size_t cbTotalWritten = 0;
953 while (cbToWrite > 0)
954 {
955 ssize_t cbWritten = write(pThis->fd, pvBuf, RT_MIN(cbToWrite, SSIZE_MAX));
956 if (cbWritten < 0)
957 {
958 rc = RTErrConvertFromErrno(errno);
959 break;
960 }
961
962 /* advance */
963 pvBuf = (char const *)pvBuf + cbWritten;
964 cbTotalWritten += cbWritten;
965 cbToWrite -= cbWritten;
966 }
967
968 if (pcbWritten)
969 {
970 *pcbWritten = cbTotalWritten;
971 if ( RT_FAILURE(rc)
972 && cbTotalWritten
973 && rc != VERR_INVALID_POINTER)
974 rc = VINF_SUCCESS;
975 }
976
977 ASMAtomicDecU32(&pThis->u32State);
978 }
979 return rc;
980#endif
981}
982
983
984RTDECL(int) RTPipeFlush(RTPIPE hPipe)
985{
986 RTPIPEINTERNAL *pThis = hPipe;
987 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
988 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
989 AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
990
991 if (!FlushFileBuffers(pThis->hPipe))
992 {
993 int rc = RTErrConvertFromWin32(GetLastError());
994 if (rc == VERR_BROKEN_PIPE)
995 pThis->fBrokenPipe = true;
996 return rc;
997 }
998 return VINF_SUCCESS;
999}
1000
1001
1002RTDECL(int) RTPipeSelectOne(RTPIPE hPipe, RTMSINTERVAL cMillies)
1003{
1004 RTPIPEINTERNAL *pThis = hPipe;
1005 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1006 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
1007
1008 uint64_t const StartMsTS = RTTimeMilliTS();
1009
1010 int rc = RTCritSectEnter(&pThis->CritSect);
1011 if (RT_FAILURE(rc))
1012 return rc;
1013 for (unsigned iLoop = 0;; iLoop++)
1014 {
1015 HANDLE hWait = INVALID_HANDLE_VALUE;
1016 if (pThis->fRead)
1017 {
1018 if (pThis->fIOPending)
1019 hWait = pThis->Overlapped.hEvent;
1020 else
1021 {
1022 /* Peek at the pipe buffer and see how many bytes it contains. */
1023 DWORD cbAvailable;
1024 if ( PeekNamedPipe(pThis->hPipe, NULL, 0, NULL, &cbAvailable, NULL)
1025 && cbAvailable > 0)
1026 {
1027 rc = VINF_SUCCESS;
1028 break;
1029 }
1030
1031 /* Start a zero byte read operation that we can wait on. */
1032 if (cMillies == 0)
1033 {
1034 rc = VERR_TIMEOUT;
1035 break;
1036 }
1037 AssertBreakStmt(pThis->cUsers == 0, rc = VERR_INTERNAL_ERROR_5);
1038 rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
1039 DWORD cbRead = 0;
1040 if (ReadFile(pThis->hPipe, pThis->abBuf, 0, &cbRead, &pThis->Overlapped))
1041 {
1042 rc = VINF_SUCCESS;
1043 if (iLoop > 10)
1044 RTThreadYield();
1045 }
1046 else if (GetLastError() == ERROR_IO_PENDING)
1047 {
1048 pThis->cUsers++;
1049 pThis->fIOPending = true;
1050 pThis->fZeroByteRead = true;
1051 hWait = pThis->Overlapped.hEvent;
1052 }
1053 else
1054 rc = RTErrConvertFromWin32(GetLastError());
1055 }
1056 }
1057 else
1058 {
1059 if (pThis->fIOPending)
1060 {
1061 rc = rtPipeWriteCheckCompletion(pThis);
1062 if (RT_FAILURE(rc))
1063 break;
1064 }
1065 if (pThis->fIOPending)
1066 hWait = pThis->Overlapped.hEvent;
1067 else
1068 {
1069 FILE_PIPE_LOCAL_INFORMATION Info;
1070#if 1
1071 /* We can always write one bounce buffer full of data regardless of
1072 the pipe buffer state. We must of course take this into account,
1073 or code like "Full write buffer" test in tstRTPipe gets confused. */
1074 rc = VINF_SUCCESS;
1075 if (rtPipeQueryNtInfo(pThis, &Info))
1076 {
1077 /* Check for broken pipe. */
1078 if (Info.NamedPipeState != FILE_PIPE_CLOSING_STATE)
1079 pThis->fPromisedWritable = true;
1080 else
1081 rc = VERR_BROKEN_PIPE;
1082 }
1083 else
1084 pThis->fPromisedWritable = true;
1085 break;
1086
1087#else /* old code: */
1088 if (rtPipeQueryNtInfo(pThis, &Info))
1089 {
1090 /* Check for broken pipe. */
1091 if (Info.NamedPipeState == FILE_PIPE_CLOSING_STATE)
1092 {
1093 rc = VERR_BROKEN_PIPE;
1094 break;
1095 }
1096
1097 /* Check for available write buffer space. */
1098 if (Info.WriteQuotaAvailable > 0)
1099 {
1100 pThis->fPromisedWritable = false;
1101 rc = VINF_SUCCESS;
1102 break;
1103 }
1104
1105 /* delayed buffer alloc or timeout: phony promise
1106 later: See if we still can associate a semaphore with
1107 the pipe, like on OS/2. */
1108 if (Info.OutboundQuota == 0 || cMillies)
1109 {
1110 pThis->fPromisedWritable = true;
1111 rc = VINF_SUCCESS;
1112 break;
1113 }
1114 }
1115 else
1116 {
1117 pThis->fPromisedWritable = true;
1118 rc = VINF_SUCCESS;
1119 break;
1120 }
1121#endif
1122 }
1123 }
1124 if (RT_FAILURE(rc))
1125 break;
1126
1127 /*
1128 * Check for timeout.
1129 */
1130 DWORD cMsMaxWait = INFINITE;
1131 if ( cMillies != RT_INDEFINITE_WAIT
1132 && ( hWait != INVALID_HANDLE_VALUE
1133 || iLoop > 10)
1134 )
1135 {
1136 uint64_t cElapsed = RTTimeMilliTS() - StartMsTS;
1137 if (cElapsed >= cMillies)
1138 {
1139 rc = VERR_TIMEOUT;
1140 break;
1141 }
1142 cMsMaxWait = cMillies - (uint32_t)cElapsed;
1143 }
1144
1145 /*
1146 * Wait.
1147 */
1148 if (hWait != INVALID_HANDLE_VALUE)
1149 {
1150 RTCritSectLeave(&pThis->CritSect);
1151
1152 DWORD dwRc = WaitForSingleObject(hWait, cMsMaxWait);
1153 if (dwRc == WAIT_OBJECT_0)
1154 rc = VINF_SUCCESS;
1155 else if (dwRc == WAIT_TIMEOUT)
1156 rc = VERR_TIMEOUT;
1157 else if (dwRc == WAIT_ABANDONED)
1158 rc = VERR_INVALID_HANDLE;
1159 else
1160 rc = RTErrConvertFromWin32(GetLastError());
1161 if ( RT_FAILURE(rc)
1162 && pThis->u32Magic != RTPIPE_MAGIC)
1163 return rc;
1164
1165 RTCritSectEnter(&pThis->CritSect);
1166 if (pThis->fZeroByteRead)
1167 {
1168 pThis->cUsers--;
1169 pThis->fIOPending = false;
1170 if (rc != VINF_SUCCESS)
1171 {
1172 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
1173 NtCancelIoFile(pThis->hPipe, &Ios);
1174 }
1175 DWORD cbRead = 0;
1176 GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/);
1177 }
1178 if (RT_FAILURE(rc))
1179 break;
1180 }
1181 }
1182
1183 if (rc == VERR_BROKEN_PIPE)
1184 pThis->fBrokenPipe = true;
1185
1186 RTCritSectLeave(&pThis->CritSect);
1187 return rc;
1188}
1189
1190
1191RTDECL(int) RTPipeQueryReadable(RTPIPE hPipe, size_t *pcbReadable)
1192{
1193 RTPIPEINTERNAL *pThis = hPipe;
1194 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1195 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
1196 AssertReturn(pThis->fRead, VERR_PIPE_NOT_READ);
1197 AssertPtrReturn(pcbReadable, VERR_INVALID_POINTER);
1198
1199 int rc = RTCritSectEnter(&pThis->CritSect);
1200 if (RT_FAILURE(rc))
1201 return rc;
1202
1203 /** @todo The file size should give the same info and be slightly faster... */
1204 DWORD cbAvailable = 0;
1205 if (PeekNamedPipe(pThis->hPipe, NULL, 0, NULL, &cbAvailable, NULL))
1206 {
1207#if ARCH_BITS == 32
1208 /*
1209 * Kludge!
1210 *
1211 * Prior to XP SP1 (?), the returned cbAvailable value was not adjusted
1212 * by the read position in the current message/buffer, so it could
1213 * potentially be too high. This may cause the caller to try read more
1214 * data than what's actually available, which may cause the read to
1215 * block when the caller thought it wouldn't.
1216 *
1217 * To get an accurate readable size, we have to provide an output
1218 * buffer and see how much we actually get back in it, as the data
1219 * peeking works correctly (as you would expect).
1220 */
1221 if (cbAvailable == 0 || g_enmWinVer >= kRTWinOSType_XP64)
1222 { /* No data available or kernel shouldn't be affected. */ }
1223 else
1224 {
1225 for (unsigned i = 0; ; i++)
1226 {
1227 uint8_t abBufStack[_16K];
1228 void *pvBufFree = NULL;
1229 void *pvBuf;
1230 DWORD cbBuf = RT_ALIGN_32(cbAvailable + i * 256, 64);
1231 if (cbBuf <= sizeof(abBufStack))
1232 {
1233 pvBuf = abBufStack;
1234 /* No cbBuf = sizeof(abBufStack) here! PeekNamedPipe bounce buffers the request on the heap. */
1235 }
1236 else
1237 {
1238 pvBufFree = pvBuf = RTMemTmpAlloc(cbBuf);
1239 if (!pvBuf)
1240 {
1241 rc = VERR_NO_TMP_MEMORY;
1242 cbAvailable = 1;
1243 break;
1244 }
1245 }
1246
1247 DWORD cbAvailable2 = 0;
1248 DWORD cbRead = 0;
1249 BOOL fRc = PeekNamedPipe(pThis->hPipe, pvBuf, cbBuf, &cbRead, &cbAvailable2, NULL);
1250 Log(("RTPipeQueryReadable: #%u: cbAvailable=%#x cbRead=%#x cbAvailable2=%#x (cbBuf=%#x)\n",
1251 i, cbAvailable, cbRead, cbAvailable2, cbBuf));
1252
1253 RTMemTmpFree(pvBufFree);
1254
1255 if (fRc)
1256 {
1257 if (cbAvailable2 <= cbBuf || i >= 10)
1258 cbAvailable = cbRead;
1259 else
1260 {
1261 cbAvailable = cbAvailable2;
1262 continue;
1263 }
1264 }
1265 else
1266 {
1267 rc = RTErrConvertFromWin32(GetLastError());
1268 cbAvailable = 1;
1269 }
1270 break;
1271 }
1272 }
1273#endif
1274 *pcbReadable = cbAvailable;
1275 }
1276 else
1277 rc = RTErrConvertFromWin32(GetLastError());
1278
1279 RTCritSectLeave(&pThis->CritSect);
1280 return rc;
1281}
1282
1283
1284RTDECL(int) RTPipeQueryInfo(RTPIPE hPipe, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1285{
1286 RTPIPEINTERNAL *pThis = hPipe;
1287 AssertPtrReturn(pThis, 0);
1288 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, 0);
1289
1290 int rc = RTCritSectEnter(&pThis->CritSect);
1291 AssertRCReturn(rc, 0);
1292
1293 rtPipeFakeQueryInfo(pObjInfo, enmAddAttr, pThis->fRead);
1294
1295 FILE_PIPE_LOCAL_INFORMATION Info;
1296 if (rtPipeQueryNtInfo(pThis, &Info))
1297 {
1298 pObjInfo->cbAllocated = pThis->fRead ? Info.InboundQuota : Info.OutboundQuota;
1299 pObjInfo->cbObject = pThis->fRead ? Info.ReadDataAvailable : Info.WriteQuotaAvailable;
1300 }
1301
1302 RTCritSectLeave(&pThis->CritSect);
1303 return VINF_SUCCESS;
1304}
1305
1306
1307int rtPipePollGetHandle(RTPIPE hPipe, uint32_t fEvents, PRTHCINTPTR phNative)
1308{
1309 RTPIPEINTERNAL *pThis = hPipe;
1310 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1311 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
1312
1313 AssertReturn(!(fEvents & RTPOLL_EVT_READ) || pThis->fRead, VERR_INVALID_PARAMETER);
1314 AssertReturn(!(fEvents & RTPOLL_EVT_WRITE) || !pThis->fRead, VERR_INVALID_PARAMETER);
1315
1316 /* Later: Try register an event handle with the pipe like on OS/2, there is
1317 a file control for doing this obviously intended for the OS/2 subsys.
1318 The question is whether this still exists on Vista and W7. */
1319 *phNative = (RTHCINTPTR)pThis->Overlapped.hEvent;
1320 return VINF_SUCCESS;
1321}
1322
1323
1324/**
1325 * Checks for pending events.
1326 *
1327 * @returns Event mask or 0.
1328 * @param pThis The pipe handle.
1329 * @param fEvents The desired events.
1330 */
1331static uint32_t rtPipePollCheck(RTPIPEINTERNAL *pThis, uint32_t fEvents)
1332{
1333 uint32_t fRetEvents = 0;
1334 if (pThis->fBrokenPipe)
1335 fRetEvents |= RTPOLL_EVT_ERROR;
1336 else if (pThis->fRead)
1337 {
1338 if (!pThis->fIOPending)
1339 {
1340 DWORD cbAvailable;
1341 if (PeekNamedPipe(pThis->hPipe, NULL, 0, NULL, &cbAvailable, NULL))
1342 {
1343 if ( (fEvents & RTPOLL_EVT_READ)
1344 && cbAvailable > 0)
1345 fRetEvents |= RTPOLL_EVT_READ;
1346 }
1347 else
1348 {
1349 if (GetLastError() == ERROR_BROKEN_PIPE)
1350 pThis->fBrokenPipe = true;
1351 fRetEvents |= RTPOLL_EVT_ERROR;
1352 }
1353 }
1354 }
1355 else
1356 {
1357 if (pThis->fIOPending)
1358 {
1359 rtPipeWriteCheckCompletion(pThis);
1360 if (pThis->fBrokenPipe)
1361 fRetEvents |= RTPOLL_EVT_ERROR;
1362 }
1363 if ( !pThis->fIOPending
1364 && !fRetEvents)
1365 {
1366 FILE_PIPE_LOCAL_INFORMATION Info;
1367 if (rtPipeQueryNtInfo(pThis, &Info))
1368 {
1369 /* Check for broken pipe. */
1370 if (Info.NamedPipeState == FILE_PIPE_CLOSING_STATE)
1371 {
1372 fRetEvents = RTPOLL_EVT_ERROR;
1373 pThis->fBrokenPipe = true;
1374 }
1375
1376 /* Check if there is available buffer space. */
1377 if ( !fRetEvents
1378 && (fEvents & RTPOLL_EVT_WRITE)
1379 && ( Info.WriteQuotaAvailable > 0
1380 || Info.OutboundQuota == 0)
1381 )
1382 fRetEvents |= RTPOLL_EVT_WRITE;
1383 }
1384 else if (fEvents & RTPOLL_EVT_WRITE)
1385 fRetEvents |= RTPOLL_EVT_WRITE;
1386 }
1387 }
1388
1389 return fRetEvents;
1390}
1391
1392
1393/**
1394 * Internal RTPoll helper that polls the pipe handle and, if @a fNoWait is
1395 * clear, starts whatever actions we've got running during the poll call.
1396 *
1397 * @returns 0 if no pending events, actions initiated if @a fNoWait is clear.
1398 * Event mask (in @a fEvents) and no actions if the handle is ready
1399 * already.
1400 * UINT32_MAX (asserted) if the pipe handle is busy in I/O or a
1401 * different poll set.
1402 *
1403 * @param hPipe The pipe handle.
1404 * @param hPollSet The poll set handle (for access checks).
1405 * @param fEvents The events we're polling for.
1406 * @param fFinalEntry Set if this is the final entry for this handle
1407 * in this poll set. This can be used for dealing
1408 * with duplicate entries.
1409 * @param fNoWait Set if it's a zero-wait poll call. Clear if
1410 * we'll wait for an event to occur.
1411 */
1412uint32_t rtPipePollStart(RTPIPE hPipe, RTPOLLSET hPollSet, uint32_t fEvents, bool fFinalEntry, bool fNoWait)
1413{
1414 /** @todo All this polling code could be optimized to make fewer system
1415 * calls; like for instance the ResetEvent calls. */
1416 RTPIPEINTERNAL *pThis = hPipe;
1417 AssertPtrReturn(pThis, UINT32_MAX);
1418 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, UINT32_MAX);
1419 RT_NOREF_PV(fFinalEntry);
1420
1421 int rc = RTCritSectEnter(&pThis->CritSect);
1422 AssertRCReturn(rc, UINT32_MAX);
1423
1424 /* Check that this is the only current use of this pipe. */
1425 uint32_t fRetEvents;
1426 if ( pThis->cUsers == 0
1427 || pThis->hPollSet == hPollSet)
1428 {
1429 /* Check what the current events are. */
1430 fRetEvents = rtPipePollCheck(pThis, fEvents);
1431 if ( !fRetEvents
1432 && !fNoWait)
1433 {
1434 /* Make sure the event semaphore has been reset. */
1435 if (!pThis->fIOPending)
1436 {
1437 rc = ResetEvent(pThis->Overlapped.hEvent);
1438 Assert(rc == TRUE);
1439 }
1440
1441 /* Kick off the zero byte read thing if applicable. */
1442 if ( !pThis->fIOPending
1443 && pThis->fRead
1444 && (fEvents & RTPOLL_EVT_READ)
1445 )
1446 {
1447 DWORD cbRead = 0;
1448 if (ReadFile(pThis->hPipe, pThis->abBuf, 0, &cbRead, &pThis->Overlapped))
1449 fRetEvents = rtPipePollCheck(pThis, fEvents);
1450 else if (GetLastError() == ERROR_IO_PENDING)
1451 {
1452 pThis->fIOPending = true;
1453 pThis->fZeroByteRead = true;
1454 }
1455 else
1456 fRetEvents = RTPOLL_EVT_ERROR;
1457 }
1458
1459 /* If we're still set for the waiting, record the poll set and
1460 mark the pipe used. */
1461 if (!fRetEvents)
1462 {
1463 pThis->cUsers++;
1464 pThis->hPollSet = hPollSet;
1465 }
1466 }
1467 }
1468 else
1469 {
1470 AssertFailed();
1471 fRetEvents = UINT32_MAX;
1472 }
1473
1474 RTCritSectLeave(&pThis->CritSect);
1475 return fRetEvents;
1476}
1477
1478
1479/**
1480 * Called after a WaitForMultipleObjects returned in order to check for pending
1481 * events and stop whatever actions that rtPipePollStart() initiated.
1482 *
1483 * @returns Event mask or 0.
1484 *
1485 * @param hPipe The pipe handle.
1486 * @param fEvents The events we're polling for.
1487 * @param fFinalEntry Set if this is the final entry for this handle
1488 * in this poll set. This can be used for dealing
1489 * with duplicate entries. Only keep in mind that
1490 * this method is called in reverse order, so the
1491 * first call will have this set (when the entire
1492 * set was processed).
1493 * @param fHarvestEvents Set if we should check for pending events.
1494 */
1495uint32_t rtPipePollDone(RTPIPE hPipe, uint32_t fEvents, bool fFinalEntry, bool fHarvestEvents)
1496{
1497 RTPIPEINTERNAL *pThis = hPipe;
1498 AssertPtrReturn(pThis, 0);
1499 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, 0);
1500 RT_NOREF_PV(fFinalEntry);
1501 RT_NOREF_PV(fHarvestEvents);
1502
1503 int rc = RTCritSectEnter(&pThis->CritSect);
1504 AssertRCReturn(rc, 0);
1505
1506 Assert(pThis->cUsers > 0);
1507
1508
1509 /* Cancel the zero byte read. */
1510 uint32_t fRetEvents = 0;
1511 if (pThis->fZeroByteRead)
1512 {
1513 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
1514 NtCancelIoFile(pThis->hPipe, &Ios);
1515
1516 DWORD cbRead = 0;
1517 if ( !GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/)
1518 && GetLastError() != ERROR_OPERATION_ABORTED)
1519 fRetEvents = RTPOLL_EVT_ERROR;
1520
1521 pThis->fIOPending = false;
1522 pThis->fZeroByteRead = false;
1523 }
1524
1525 /* harvest events. */
1526 fRetEvents |= rtPipePollCheck(pThis, fEvents);
1527
1528 /* update counters. */
1529 pThis->cUsers--;
1530 /** @todo This isn't sane, or is it? See OS/2 impl. */
1531 if (!pThis->cUsers)
1532 pThis->hPollSet = NIL_RTPOLLSET;
1533
1534 RTCritSectLeave(&pThis->CritSect);
1535 return fRetEvents;
1536}
1537
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use