VirtualBox

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

最後變更 在這個檔案從108426是 108426,由 vboxsync 提交於 2 週 前

iprt/pipe-win.cpp: Added todo about asynchronous (FILE_FLAG_OVERLAPPED) and child processes.

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

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