summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/r3/win/pipe-win.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Runtime/r3/win/pipe-win.cpp')
-rw-r--r--src/VBox/Runtime/r3/win/pipe-win.cpp1537
1 files changed, 1537 insertions, 0 deletions
diff --git a/src/VBox/Runtime/r3/win/pipe-win.cpp b/src/VBox/Runtime/r3/win/pipe-win.cpp
new file mode 100644
index 00000000..93bf9afc
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/pipe-win.cpp
@@ -0,0 +1,1537 @@
+/* $Id: pipe-win.cpp $ */
+/** @file
+ * IPRT - Anonymous Pipes, Windows Implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/nt/nt-and-windows.h>
+
+#include <iprt/pipe.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/critsect.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/poll.h>
+#include <iprt/process.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+#include "internal/pipe.h"
+#include "internal/magics.h"
+#include "internal-r3-win.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The pipe buffer size we prefer. */
+#define RTPIPE_NT_SIZE _64K
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct RTPIPEINTERNAL
+{
+ /** Magic value (RTPIPE_MAGIC). */
+ uint32_t u32Magic;
+ /** The pipe handle. */
+ HANDLE hPipe;
+ /** Set if this is the read end, clear if it's the write end. */
+ bool fRead;
+ /** RTPipeFromNative: Leave native handle open on RTPipeClose. */
+ bool fLeaveOpen;
+ /** Set if there is already pending I/O. */
+ bool fIOPending;
+ /** Set if the zero byte read that the poll code using is pending. */
+ bool fZeroByteRead;
+ /** Set if the pipe is broken. */
+ bool fBrokenPipe;
+ /** Set if we've promised that the handle is writable. */
+ bool fPromisedWritable;
+ /** Set if created inheritable. */
+ bool fCreatedInheritable;
+ /** Usage counter. */
+ uint32_t cUsers;
+ /** The overlapped I/O structure we use. */
+ OVERLAPPED Overlapped;
+ /** Bounce buffer for writes. */
+ uint8_t *pbBounceBuf;
+ /** Amount of used buffer space. */
+ size_t cbBounceBufUsed;
+ /** Amount of allocated buffer space. */
+ size_t cbBounceBufAlloc;
+ /** The handle of the poll set currently polling on this pipe.
+ * We can only have one poller at the time (lazy bird). */
+ RTPOLLSET hPollSet;
+ /** Critical section protecting the above members.
+ * (Taking the lazy/simple approach.) */
+ RTCRITSECT CritSect;
+ /** Buffer for the zero byte read. */
+ uint8_t abBuf[8];
+} RTPIPEINTERNAL;
+
+
+
+/**
+ * Wrapper for getting FILE_PIPE_LOCAL_INFORMATION via the NT API.
+ *
+ * @returns Success indicator (true/false).
+ * @param pThis The pipe.
+ * @param pInfo The info structure.
+ */
+static bool rtPipeQueryNtInfo(RTPIPEINTERNAL *pThis, FILE_PIPE_LOCAL_INFORMATION *pInfo)
+{
+ IO_STATUS_BLOCK Ios;
+ RT_ZERO(Ios);
+ RT_ZERO(*pInfo);
+ NTSTATUS rcNt = NtQueryInformationFile(pThis->hPipe, &Ios, pInfo, sizeof(*pInfo), FilePipeLocalInformation);
+ return rcNt >= 0;
+}
+
+
+RTDECL(int) RTPipeCreate(PRTPIPE phPipeRead, PRTPIPE phPipeWrite, uint32_t fFlags)
+{
+ AssertPtrReturn(phPipeRead, VERR_INVALID_POINTER);
+ AssertPtrReturn(phPipeWrite, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~RTPIPE_C_VALID_MASK), VERR_INVALID_PARAMETER);
+
+ /*
+ * Create the read end of the pipe.
+ */
+ DWORD dwErr;
+ HANDLE hPipeR;
+ HANDLE hPipeW;
+ int rc;
+ for (;;)
+ {
+ static volatile uint32_t g_iNextPipe = 0;
+ char szName[128];
+ RTStrPrintf(szName, sizeof(szName), "\\\\.\\pipe\\iprt-pipe-%u-%u", RTProcSelf(), ASMAtomicIncU32(&g_iNextPipe));
+
+ SECURITY_ATTRIBUTES SecurityAttributes;
+ PSECURITY_ATTRIBUTES pSecurityAttributes = NULL;
+ if (fFlags & RTPIPE_C_INHERIT_READ)
+ {
+ SecurityAttributes.nLength = sizeof(SecurityAttributes);
+ SecurityAttributes.lpSecurityDescriptor = NULL;
+ SecurityAttributes.bInheritHandle = TRUE;
+ pSecurityAttributes = &SecurityAttributes;
+ }
+
+ DWORD dwOpenMode = PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED;
+#ifdef FILE_FLAG_FIRST_PIPE_INSTANCE
+ dwOpenMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
+#endif
+
+ DWORD dwPipeMode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT;
+#ifdef PIPE_REJECT_REMOTE_CLIENTS
+ dwPipeMode |= PIPE_REJECT_REMOTE_CLIENTS;
+#endif
+
+ hPipeR = CreateNamedPipeA(szName, dwOpenMode, dwPipeMode, 1 /*nMaxInstances*/, RTPIPE_NT_SIZE, RTPIPE_NT_SIZE,
+ NMPWAIT_USE_DEFAULT_WAIT, pSecurityAttributes);
+#ifdef PIPE_REJECT_REMOTE_CLIENTS
+ if (hPipeR == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_PARAMETER)
+ {
+ dwPipeMode &= ~PIPE_REJECT_REMOTE_CLIENTS;
+ hPipeR = CreateNamedPipeA(szName, dwOpenMode, dwPipeMode, 1 /*nMaxInstances*/, RTPIPE_NT_SIZE, RTPIPE_NT_SIZE,
+ NMPWAIT_USE_DEFAULT_WAIT, pSecurityAttributes);
+ }
+#endif
+#ifdef FILE_FLAG_FIRST_PIPE_INSTANCE
+ if (hPipeR == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_PARAMETER)
+ {
+ dwOpenMode &= ~FILE_FLAG_FIRST_PIPE_INSTANCE;
+ hPipeR = CreateNamedPipeA(szName, dwOpenMode, dwPipeMode, 1 /*nMaxInstances*/, RTPIPE_NT_SIZE, RTPIPE_NT_SIZE,
+ NMPWAIT_USE_DEFAULT_WAIT, pSecurityAttributes);
+ }
+#endif
+ if (hPipeR != INVALID_HANDLE_VALUE)
+ {
+ /*
+ * Connect to the pipe (the write end).
+ * We add FILE_READ_ATTRIBUTES here to make sure we can query the
+ * pipe state later on.
+ */
+ pSecurityAttributes = NULL;
+ if (fFlags & RTPIPE_C_INHERIT_WRITE)
+ {
+ SecurityAttributes.nLength = sizeof(SecurityAttributes);
+ SecurityAttributes.lpSecurityDescriptor = NULL;
+ SecurityAttributes.bInheritHandle = TRUE;
+ pSecurityAttributes = &SecurityAttributes;
+ }
+
+ hPipeW = CreateFileA(szName,
+ GENERIC_WRITE | FILE_READ_ATTRIBUTES /*dwDesiredAccess*/,
+ 0 /*dwShareMode*/,
+ pSecurityAttributes,
+ OPEN_EXISTING /* dwCreationDisposition */,
+ FILE_FLAG_OVERLAPPED /*dwFlagsAndAttributes*/,
+ NULL /*hTemplateFile*/);
+ if (hPipeW != INVALID_HANDLE_VALUE)
+ break;
+ dwErr = GetLastError();
+ CloseHandle(hPipeR);
+ }
+ else
+ dwErr = GetLastError();
+ if ( dwErr != ERROR_PIPE_BUSY /* already exist - compatible */
+ && dwErr != ERROR_ACCESS_DENIED /* already exist - incompatible */)
+ return RTErrConvertFromWin32(dwErr);
+ /* else: try again with a new name */
+ }
+
+ /*
+ * Create the two handles.
+ */
+ RTPIPEINTERNAL *pThisR = (RTPIPEINTERNAL *)RTMemAllocZ(sizeof(RTPIPEINTERNAL));
+ if (pThisR)
+ {
+ RTPIPEINTERNAL *pThisW = (RTPIPEINTERNAL *)RTMemAllocZ(sizeof(RTPIPEINTERNAL));
+ if (pThisW)
+ {
+ rc = RTCritSectInit(&pThisR->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCritSectInit(&pThisW->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ pThisR->Overlapped.hEvent = CreateEvent(NULL, TRUE /*fManualReset*/,
+ TRUE /*fInitialState*/, NULL /*pName*/);
+ if (pThisR->Overlapped.hEvent != NULL)
+ {
+ pThisW->Overlapped.hEvent = CreateEvent(NULL, TRUE /*fManualReset*/,
+ TRUE /*fInitialState*/, NULL /*pName*/);
+ if (pThisW->Overlapped.hEvent != NULL)
+ {
+ pThisR->u32Magic = RTPIPE_MAGIC;
+ pThisW->u32Magic = RTPIPE_MAGIC;
+ pThisR->hPipe = hPipeR;
+ pThisW->hPipe = hPipeW;
+ pThisR->fRead = true;
+ pThisW->fRead = false;
+ pThisR->fLeaveOpen = false;
+ pThisW->fLeaveOpen = false;
+ //pThisR->fIOPending = false;
+ //pThisW->fIOPending = false;
+ //pThisR->fZeroByteRead = false;
+ //pThisW->fZeroByteRead = false;
+ //pThisR->fBrokenPipe = false;
+ //pThisW->fBrokenPipe = false;
+ //pThisW->fPromisedWritable = false;
+ //pThisR->fPromisedWritable = false;
+ pThisW->fCreatedInheritable = RT_BOOL(fFlags & RTPIPE_C_INHERIT_WRITE);
+ pThisR->fCreatedInheritable = RT_BOOL(fFlags & RTPIPE_C_INHERIT_READ);
+ //pThisR->cUsers = 0;
+ //pThisW->cUsers = 0;
+ //pThisR->pbBounceBuf = NULL;
+ //pThisW->pbBounceBuf = NULL;
+ //pThisR->cbBounceBufUsed = 0;
+ //pThisW->cbBounceBufUsed = 0;
+ //pThisR->cbBounceBufAlloc = 0;
+ //pThisW->cbBounceBufAlloc = 0;
+ pThisR->hPollSet = NIL_RTPOLLSET;
+ pThisW->hPollSet = NIL_RTPOLLSET;
+
+ *phPipeRead = pThisR;
+ *phPipeWrite = pThisW;
+ return VINF_SUCCESS;
+ }
+ CloseHandle(pThisR->Overlapped.hEvent);
+ }
+ RTCritSectDelete(&pThisW->CritSect);
+ }
+ RTCritSectDelete(&pThisR->CritSect);
+ }
+ RTMemFree(pThisW);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ RTMemFree(pThisR);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ CloseHandle(hPipeR);
+ CloseHandle(hPipeW);
+ return rc;
+}
+
+
+/**
+ * Common worker for handling I/O completion.
+ *
+ * This is used by RTPipeClose, RTPipeWrite and RTPipeWriteBlocking.
+ *
+ * @returns IPRT status code.
+ * @param pThis The pipe instance handle.
+ */
+static int rtPipeWriteCheckCompletion(RTPIPEINTERNAL *pThis)
+{
+ int rc;
+ DWORD dwRc = WaitForSingleObject(pThis->Overlapped.hEvent, 0);
+ if (dwRc == WAIT_OBJECT_0)
+ {
+ DWORD cbWritten = 0;
+ if (GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbWritten, TRUE))
+ {
+ for (;;)
+ {
+ if (cbWritten >= pThis->cbBounceBufUsed)
+ {
+ pThis->fIOPending = false;
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ /* resubmit the remainder of the buffer - can this actually happen? */
+ pThis->cbBounceBufUsed -= cbWritten;
+ memmove(&pThis->pbBounceBuf[0], &pThis->pbBounceBuf[cbWritten], pThis->cbBounceBufUsed);
+ rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
+ if (!WriteFile(pThis->hPipe, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed, &cbWritten, &pThis->Overlapped))
+ {
+ DWORD const dwErr = GetLastError();
+ if (dwErr == ERROR_IO_PENDING)
+ rc = VINF_TRY_AGAIN;
+ else
+ {
+ pThis->fIOPending = false;
+ if (dwErr == ERROR_NO_DATA)
+ rc = VERR_BROKEN_PIPE;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ if (rc == VERR_BROKEN_PIPE)
+ pThis->fBrokenPipe = true;
+ }
+ break;
+ }
+ Assert(cbWritten > 0);
+ }
+ }
+ else
+ {
+ pThis->fIOPending = false;
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ }
+ else if (dwRc == WAIT_TIMEOUT)
+ rc = VINF_TRY_AGAIN;
+ else
+ {
+ pThis->fIOPending = false;
+ if (dwRc == WAIT_ABANDONED)
+ rc = VERR_INVALID_HANDLE;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ return rc;
+}
+
+
+
+RTDECL(int) RTPipeCloseEx(RTPIPE hPipe, bool fLeaveOpen)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ if (pThis == NIL_RTPIPE)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Do the cleanup.
+ */
+ AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, ~RTPIPE_MAGIC, RTPIPE_MAGIC), VERR_INVALID_HANDLE);
+ RTCritSectEnter(&pThis->CritSect);
+ Assert(pThis->cUsers == 0);
+
+ if (!pThis->fRead && pThis->fIOPending)
+ rtPipeWriteCheckCompletion(pThis);
+
+ if (!fLeaveOpen && !pThis->fLeaveOpen)
+ CloseHandle(pThis->hPipe);
+ pThis->hPipe = INVALID_HANDLE_VALUE;
+
+ CloseHandle(pThis->Overlapped.hEvent);
+ pThis->Overlapped.hEvent = NULL;
+
+ RTMemFree(pThis->pbBounceBuf);
+ pThis->pbBounceBuf = NULL;
+
+ RTCritSectLeave(&pThis->CritSect);
+ RTCritSectDelete(&pThis->CritSect);
+
+ RTMemFree(pThis);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTPipeClose(RTPIPE hPipe)
+{
+ return RTPipeCloseEx(hPipe, false /*fLeaveOpen*/);
+}
+
+
+RTDECL(int) RTPipeFromNative(PRTPIPE phPipe, RTHCINTPTR hNativePipe, uint32_t fFlags)
+{
+ AssertPtrReturn(phPipe, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~RTPIPE_N_VALID_MASK_FN), VERR_INVALID_PARAMETER);
+ AssertReturn(!!(fFlags & RTPIPE_N_READ) != !!(fFlags & RTPIPE_N_WRITE), VERR_INVALID_PARAMETER);
+
+ /*
+ * Get and validate the pipe handle info.
+ */
+ HANDLE hNative = (HANDLE)hNativePipe;
+ AssertReturn(GetFileType(hNative) == FILE_TYPE_PIPE, VERR_INVALID_HANDLE);
+
+ DWORD cMaxInstances;
+ DWORD fInfo;
+ if (!GetNamedPipeInfo(hNative, &fInfo, NULL, NULL, &cMaxInstances))
+ return RTErrConvertFromWin32(GetLastError());
+ /* Doesn't seem to matter to much if the pipe is message or byte type. Cygwin
+ seems to hand us such pipes when capturing output (@bugref{9397}), so just
+ ignore skip this check:
+ AssertReturn(!(fInfo & PIPE_TYPE_MESSAGE), VERR_INVALID_HANDLE); */
+ AssertReturn(cMaxInstances == 1, VERR_INVALID_HANDLE);
+
+ DWORD cInstances;
+ DWORD fState;
+ if (!GetNamedPipeHandleState(hNative, &fState, &cInstances, NULL, NULL, NULL, 0))
+ return RTErrConvertFromWin32(GetLastError());
+ AssertReturn(!(fState & PIPE_NOWAIT), VERR_INVALID_HANDLE);
+ AssertReturn(!(fState & PIPE_READMODE_MESSAGE), VERR_INVALID_HANDLE);
+ AssertReturn(cInstances <= 1, VERR_INVALID_HANDLE);
+
+ /*
+ * Looks kind of OK, create a handle so we can try rtPipeQueryNtInfo on it
+ * and see if we need to duplicate it to make that call work.
+ */
+ RTPIPEINTERNAL *pThis = (RTPIPEINTERNAL *)RTMemAllocZ(sizeof(RTPIPEINTERNAL));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+ int rc = RTCritSectInit(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->Overlapped.hEvent = CreateEvent(NULL, TRUE /*fManualReset*/,
+ TRUE /*fInitialState*/, NULL /*pName*/);
+ if (pThis->Overlapped.hEvent != NULL)
+ {
+ pThis->u32Magic = RTPIPE_MAGIC;
+ pThis->hPipe = hNative;
+ pThis->fRead = RT_BOOL(fFlags & RTPIPE_N_READ);
+ pThis->fLeaveOpen = RT_BOOL(fFlags & RTPIPE_N_LEAVE_OPEN);
+ //pThis->fIOPending = false;
+ //pThis->fZeroByteRead = false;
+ //pThis->fBrokenPipe = false;
+ //pThis->fPromisedWritable = false;
+ pThis->fCreatedInheritable = RT_BOOL(fFlags & RTPIPE_N_INHERIT);
+ //pThis->cUsers = 0;
+ //pThis->pbBounceBuf = NULL;
+ //pThis->cbBounceBufUsed = 0;
+ //pThis->cbBounceBufAlloc = 0;
+ pThis->hPollSet = NIL_RTPOLLSET;
+
+ HANDLE hNative2 = INVALID_HANDLE_VALUE;
+ FILE_PIPE_LOCAL_INFORMATION Info;
+ RT_ZERO(Info);
+ if ( g_pfnSetHandleInformation
+ && rtPipeQueryNtInfo(pThis, &Info))
+ rc = VINF_SUCCESS;
+ else
+ {
+ if (DuplicateHandle(GetCurrentProcess() /*hSrcProcess*/, hNative /*hSrcHandle*/,
+ GetCurrentProcess() /*hDstProcess*/, &hNative2 /*phDstHandle*/,
+ pThis->fRead ? GENERIC_READ : GENERIC_WRITE | FILE_READ_ATTRIBUTES /*dwDesiredAccess*/,
+ !!(fFlags & RTPIPE_N_INHERIT) /*fInheritHandle*/,
+ 0 /*dwOptions*/))
+ {
+ pThis->hPipe = hNative2;
+ if (rtPipeQueryNtInfo(pThis, &Info))
+ {
+ pThis->fLeaveOpen = false;
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ rc = VERR_ACCESS_DENIED;
+ CloseHandle(hNative2);
+ }
+ }
+ else
+ hNative2 = INVALID_HANDLE_VALUE;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Verify the pipe state and correct the inheritability.
+ */
+ AssertStmt( Info.NamedPipeState == FILE_PIPE_CONNECTED_STATE
+ || Info.NamedPipeState == FILE_PIPE_CLOSING_STATE
+ || Info.NamedPipeState == FILE_PIPE_DISCONNECTED_STATE,
+ VERR_INVALID_HANDLE);
+ AssertStmt( Info.NamedPipeConfiguration
+ == ( Info.NamedPipeEnd == FILE_PIPE_SERVER_END
+ ? (pThis->fRead ? FILE_PIPE_INBOUND : FILE_PIPE_OUTBOUND)
+ : (pThis->fRead ? FILE_PIPE_OUTBOUND : FILE_PIPE_INBOUND) )
+ || Info.NamedPipeConfiguration == FILE_PIPE_FULL_DUPLEX,
+ VERR_INVALID_HANDLE);
+ if ( RT_SUCCESS(rc)
+ && hNative2 == INVALID_HANDLE_VALUE
+ && !g_pfnSetHandleInformation(hNative,
+ HANDLE_FLAG_INHERIT /*dwMask*/,
+ fFlags & RTPIPE_N_INHERIT ? HANDLE_FLAG_INHERIT : 0))
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ AssertMsgFailed(("%Rrc\n", rc));
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Ok, we're good! If we replaced the handle, make sure it's not a standard
+ * handle if we think we need to close it.
+ */
+ if (hNative2 != INVALID_HANDLE_VALUE)
+ {
+ if ( !(fFlags & RTPIPE_N_LEAVE_OPEN)
+ && hNative != GetStdHandle(STD_INPUT_HANDLE)
+ && hNative != GetStdHandle(STD_OUTPUT_HANDLE)
+ && hNative != GetStdHandle(STD_ERROR_HANDLE) )
+ CloseHandle(hNative);
+ }
+ *phPipe = pThis;
+ return VINF_SUCCESS;
+ }
+ }
+
+ /* Bail out. */
+ if (hNative2 != INVALID_HANDLE_VALUE)
+ CloseHandle(hNative2);
+ CloseHandle(pThis->Overlapped.hEvent);
+ }
+ RTCritSectDelete(&pThis->CritSect);
+ }
+ RTMemFree(pThis);
+ return rc;
+}
+
+
+RTDECL(RTHCINTPTR) RTPipeToNative(RTPIPE hPipe)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, -1);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, -1);
+
+ return (RTHCINTPTR)pThis->hPipe;
+}
+
+
+RTDECL(int) RTPipeGetCreationInheritability(RTPIPE hPipe)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, false);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, false);
+
+ return pThis->fCreatedInheritable;
+}
+
+
+RTDECL(int) RTPipeRead(RTPIPE hPipe, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->fRead, VERR_ACCESS_DENIED);
+ AssertPtr(pcbRead);
+ AssertPtr(pvBuf);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ /* No concurrent readers, sorry. */
+ if (pThis->cUsers == 0)
+ {
+ pThis->cUsers++;
+
+ /*
+ * Kick off a an overlapped read. It should return immediately if
+ * there are bytes in the buffer. If not, we'll cancel it and see
+ * what we get back.
+ */
+ rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
+ DWORD cbRead = 0;
+ if ( cbToRead == 0
+ || ReadFile(pThis->hPipe, pvBuf,
+ cbToRead <= ~(DWORD)0 ? (DWORD)cbToRead : ~(DWORD)0,
+ &cbRead, &pThis->Overlapped))
+ {
+ *pcbRead = cbRead;
+ rc = VINF_SUCCESS;
+ }
+ else if (GetLastError() == ERROR_IO_PENDING)
+ {
+ pThis->fIOPending = true;
+ RTCritSectLeave(&pThis->CritSect);
+
+ /* We use NtCancelIoFile here because the CancelIo API
+ providing access to it wasn't available till NT4. This
+ code needs to work (or at least load) with NT 3.1 */
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NTSTATUS rcNt = NtCancelIoFile(pThis->hPipe, &Ios);
+ if (!NT_SUCCESS(rcNt))
+ WaitForSingleObject(pThis->Overlapped.hEvent, INFINITE);
+
+ if (GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/))
+ {
+ *pcbRead = cbRead;
+ rc = VINF_SUCCESS;
+ }
+ else if (GetLastError() == ERROR_OPERATION_ABORTED)
+ {
+ *pcbRead = 0;
+ rc = VINF_TRY_AGAIN;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ RTCritSectEnter(&pThis->CritSect);
+ pThis->fIOPending = false;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ if (rc == VERR_BROKEN_PIPE)
+ pThis->fBrokenPipe = true;
+
+ pThis->cUsers--;
+ }
+ else
+ rc = VERR_WRONG_ORDER;
+ RTCritSectLeave(&pThis->CritSect);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTPipeReadBlocking(RTPIPE hPipe, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->fRead, VERR_ACCESS_DENIED);
+ AssertPtr(pvBuf);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ /* No concurrent readers, sorry. */
+ if (pThis->cUsers == 0)
+ {
+ pThis->cUsers++;
+
+ size_t cbTotalRead = 0;
+ while (cbToRead > 0)
+ {
+ /*
+ * Kick of a an overlapped read. It should return immediately if
+ * there is bytes in the buffer. If not, we'll cancel it and see
+ * what we get back.
+ */
+ rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
+ DWORD cbRead = 0;
+ pThis->fIOPending = true;
+ RTCritSectLeave(&pThis->CritSect);
+
+ if (ReadFile(pThis->hPipe, pvBuf,
+ cbToRead <= ~(DWORD)0 ? (DWORD)cbToRead : ~(DWORD)0,
+ &cbRead, &pThis->Overlapped))
+ rc = VINF_SUCCESS;
+ else if (GetLastError() == ERROR_IO_PENDING)
+ {
+ WaitForSingleObject(pThis->Overlapped.hEvent, INFINITE);
+ if (GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/))
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ RTCritSectEnter(&pThis->CritSect);
+ pThis->fIOPending = false;
+ if (RT_FAILURE(rc))
+ break;
+
+ /* advance */
+ cbToRead -= cbRead;
+ cbTotalRead += cbRead;
+ pvBuf = (uint8_t *)pvBuf + cbRead;
+ }
+
+ if (rc == VERR_BROKEN_PIPE)
+ pThis->fBrokenPipe = true;
+
+ if (pcbRead)
+ {
+ *pcbRead = cbTotalRead;
+ if ( RT_FAILURE(rc)
+ && cbTotalRead
+ && rc != VERR_INVALID_POINTER)
+ rc = VINF_SUCCESS;
+ }
+
+ pThis->cUsers--;
+ }
+ else
+ rc = VERR_WRONG_ORDER;
+ RTCritSectLeave(&pThis->CritSect);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTPipeWrite(RTPIPE hPipe, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
+ AssertPtr(pcbWritten);
+ AssertPtr(pvBuf);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ /* No concurrent writers, sorry. */
+ if (pThis->cUsers == 0)
+ {
+ pThis->cUsers++;
+
+ /* If I/O is pending, check if it has completed. */
+ if (pThis->fIOPending)
+ rc = rtPipeWriteCheckCompletion(pThis);
+ else
+ rc = VINF_SUCCESS;
+ if (rc == VINF_SUCCESS)
+ {
+ Assert(!pThis->fIOPending);
+
+ /* Adjust the number of bytes to write to fit into the current
+ buffer quota, unless we've promised stuff in RTPipeSelectOne.
+ WriteQuotaAvailable better not be zero when it shouldn't!! */
+ FILE_PIPE_LOCAL_INFORMATION Info;
+ if ( !pThis->fPromisedWritable
+ && cbToWrite > 0
+ && rtPipeQueryNtInfo(pThis, &Info))
+ {
+ if (Info.NamedPipeState == FILE_PIPE_CLOSING_STATE)
+ rc = VERR_BROKEN_PIPE;
+ /** @todo fixme: To get the pipe writing support to work the
+ * block below needs to be commented out until a
+ * way is found to address the problem of the incorrectly
+ * set field Info.WriteQuotaAvailable.
+ * Update: We now just write up to RTPIPE_NT_SIZE more. This is quite
+ * possibely what lead to the misunderstanding here wrt to
+ * WriteQuotaAvailable updating. */
+#if 0
+ else if ( cbToWrite >= Info.WriteQuotaAvailable
+ && Info.OutboundQuota != 0
+ && (Info.WriteQuotaAvailable || pThis->cbBounceBufAlloc)
+ )
+ {
+ cbToWrite = Info.WriteQuotaAvailable;
+ if (!cbToWrite)
+ rc = VINF_TRY_AGAIN;
+ }
+#endif
+ }
+ pThis->fPromisedWritable = false;
+
+ /* Do the bounce buffering. */
+ if ( pThis->cbBounceBufAlloc < cbToWrite
+ && pThis->cbBounceBufAlloc < RTPIPE_NT_SIZE)
+ {
+ if (cbToWrite > RTPIPE_NT_SIZE)
+ cbToWrite = RTPIPE_NT_SIZE;
+ void *pv = RTMemRealloc(pThis->pbBounceBuf, RT_ALIGN_Z(cbToWrite, _1K));
+ if (pv)
+ {
+ pThis->pbBounceBuf = (uint8_t *)pv;
+ pThis->cbBounceBufAlloc = RT_ALIGN_Z(cbToWrite, _1K);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else if (cbToWrite > RTPIPE_NT_SIZE)
+ cbToWrite = RTPIPE_NT_SIZE;
+ if (RT_SUCCESS(rc) && cbToWrite)
+ {
+ memcpy(pThis->pbBounceBuf, pvBuf, cbToWrite);
+ pThis->cbBounceBufUsed = (uint32_t)cbToWrite;
+
+ /* Submit the write. */
+ rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
+ DWORD cbWritten = 0;
+ if (WriteFile(pThis->hPipe, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed,
+ &cbWritten, &pThis->Overlapped))
+ {
+ *pcbWritten = RT_MIN(cbWritten, cbToWrite); /* paranoia^3 */
+ rc = VINF_SUCCESS;
+ }
+ else if (GetLastError() == ERROR_IO_PENDING)
+ {
+ *pcbWritten = cbToWrite;
+ pThis->fIOPending = true;
+ rc = VINF_SUCCESS;
+ }
+ else if (GetLastError() == ERROR_NO_DATA)
+ rc = VERR_BROKEN_PIPE;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else if (RT_SUCCESS(rc))
+ *pcbWritten = 0;
+ }
+ else if (RT_SUCCESS(rc))
+ *pcbWritten = 0;
+
+ if (rc == VERR_BROKEN_PIPE)
+ pThis->fBrokenPipe = true;
+
+ pThis->cUsers--;
+ }
+ else
+ rc = VERR_WRONG_ORDER;
+ RTCritSectLeave(&pThis->CritSect);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTPipeWriteBlocking(RTPIPE hPipe, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
+ AssertPtr(pvBuf);
+ AssertPtrNull(pcbWritten);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ /* No concurrent writers, sorry. */
+ if (pThis->cUsers == 0)
+ {
+ pThis->cUsers++;
+
+ /*
+ * If I/O is pending, wait for it to complete.
+ */
+ if (pThis->fIOPending)
+ {
+ rc = rtPipeWriteCheckCompletion(pThis);
+ while (rc == VINF_TRY_AGAIN)
+ {
+ Assert(pThis->fIOPending);
+ HANDLE hEvent = pThis->Overlapped.hEvent;
+ RTCritSectLeave(&pThis->CritSect);
+ WaitForSingleObject(hEvent, INFINITE);
+ RTCritSectEnter(&pThis->CritSect);
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ Assert(!pThis->fIOPending);
+ pThis->fPromisedWritable = false;
+
+ /*
+ * Try write everything.
+ * No bounce buffering, cUsers protects us.
+ */
+ size_t cbTotalWritten = 0;
+ while (cbToWrite > 0)
+ {
+ rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
+ pThis->fIOPending = true;
+ RTCritSectLeave(&pThis->CritSect);
+
+ DWORD cbWritten = 0;
+ DWORD const cbToWriteInThisIteration = cbToWrite <= ~(DWORD)0 ? (DWORD)cbToWrite : ~(DWORD)0;
+ if (WriteFile(pThis->hPipe, pvBuf, cbToWriteInThisIteration, &cbWritten, &pThis->Overlapped))
+ rc = VINF_SUCCESS;
+ else if (GetLastError() == ERROR_IO_PENDING)
+ {
+ WaitForSingleObject(pThis->Overlapped.hEvent, INFINITE);
+ if (GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbWritten, TRUE /*fWait*/))
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else if (GetLastError() == ERROR_NO_DATA)
+ rc = VERR_BROKEN_PIPE;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ RTCritSectEnter(&pThis->CritSect);
+ pThis->fIOPending = false;
+ if (RT_FAILURE(rc))
+ break;
+
+ /* advance */
+ if (cbWritten > cbToWriteInThisIteration) /* paranoia^3 */
+ cbWritten = cbToWriteInThisIteration;
+ pvBuf = (char const *)pvBuf + cbWritten;
+ cbTotalWritten += cbWritten;
+ cbToWrite -= cbWritten;
+ }
+
+ if (pcbWritten)
+ {
+ *pcbWritten = cbTotalWritten;
+ if ( RT_FAILURE(rc)
+ && cbTotalWritten
+ && rc != VERR_INVALID_POINTER)
+ rc = VINF_SUCCESS;
+ }
+ }
+
+ if (rc == VERR_BROKEN_PIPE)
+ pThis->fBrokenPipe = true;
+
+ pThis->cUsers--;
+ }
+ else
+ rc = VERR_WRONG_ORDER;
+ RTCritSectLeave(&pThis->CritSect);
+ }
+ return rc;
+
+#if 0 /** @todo r=bird: What's this? */
+ int rc = rtPipeTryBlocking(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ size_t cbTotalWritten = 0;
+ while (cbToWrite > 0)
+ {
+ ssize_t cbWritten = write(pThis->fd, pvBuf, RT_MIN(cbToWrite, SSIZE_MAX));
+ if (cbWritten < 0)
+ {
+ rc = RTErrConvertFromErrno(errno);
+ break;
+ }
+
+ /* advance */
+ pvBuf = (char const *)pvBuf + cbWritten;
+ cbTotalWritten += cbWritten;
+ cbToWrite -= cbWritten;
+ }
+
+ if (pcbWritten)
+ {
+ *pcbWritten = cbTotalWritten;
+ if ( RT_FAILURE(rc)
+ && cbTotalWritten
+ && rc != VERR_INVALID_POINTER)
+ rc = VINF_SUCCESS;
+ }
+
+ ASMAtomicDecU32(&pThis->u32State);
+ }
+ return rc;
+#endif
+}
+
+
+RTDECL(int) RTPipeFlush(RTPIPE hPipe)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
+
+ if (!FlushFileBuffers(pThis->hPipe))
+ {
+ int rc = RTErrConvertFromWin32(GetLastError());
+ if (rc == VERR_BROKEN_PIPE)
+ pThis->fBrokenPipe = true;
+ return rc;
+ }
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTPipeSelectOne(RTPIPE hPipe, RTMSINTERVAL cMillies)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+
+ uint64_t const StartMsTS = RTTimeMilliTS();
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_FAILURE(rc))
+ return rc;
+ for (unsigned iLoop = 0;; iLoop++)
+ {
+ HANDLE hWait = INVALID_HANDLE_VALUE;
+ if (pThis->fRead)
+ {
+ if (pThis->fIOPending)
+ hWait = pThis->Overlapped.hEvent;
+ else
+ {
+ /* Peek at the pipe buffer and see how many bytes it contains. */
+ DWORD cbAvailable;
+ if ( PeekNamedPipe(pThis->hPipe, NULL, 0, NULL, &cbAvailable, NULL)
+ && cbAvailable > 0)
+ {
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ /* Start a zero byte read operation that we can wait on. */
+ if (cMillies == 0)
+ {
+ rc = VERR_TIMEOUT;
+ break;
+ }
+ AssertBreakStmt(pThis->cUsers == 0, rc = VERR_INTERNAL_ERROR_5);
+ rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
+ DWORD cbRead = 0;
+ if (ReadFile(pThis->hPipe, pThis->abBuf, 0, &cbRead, &pThis->Overlapped))
+ {
+ rc = VINF_SUCCESS;
+ if (iLoop > 10)
+ RTThreadYield();
+ }
+ else if (GetLastError() == ERROR_IO_PENDING)
+ {
+ pThis->cUsers++;
+ pThis->fIOPending = true;
+ pThis->fZeroByteRead = true;
+ hWait = pThis->Overlapped.hEvent;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ }
+ else
+ {
+ if (pThis->fIOPending)
+ {
+ rc = rtPipeWriteCheckCompletion(pThis);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ if (pThis->fIOPending)
+ hWait = pThis->Overlapped.hEvent;
+ else
+ {
+ FILE_PIPE_LOCAL_INFORMATION Info;
+#if 1
+ /* We can always write one bounce buffer full of data regardless of
+ the pipe buffer state. We must of course take this into account,
+ or code like "Full write buffer" test in tstRTPipe gets confused. */
+ rc = VINF_SUCCESS;
+ if (rtPipeQueryNtInfo(pThis, &Info))
+ {
+ /* Check for broken pipe. */
+ if (Info.NamedPipeState != FILE_PIPE_CLOSING_STATE)
+ pThis->fPromisedWritable = true;
+ else
+ rc = VERR_BROKEN_PIPE;
+ }
+ else
+ pThis->fPromisedWritable = true;
+ break;
+
+#else /* old code: */
+ if (rtPipeQueryNtInfo(pThis, &Info))
+ {
+ /* Check for broken pipe. */
+ if (Info.NamedPipeState == FILE_PIPE_CLOSING_STATE)
+ {
+ rc = VERR_BROKEN_PIPE;
+ break;
+ }
+
+ /* Check for available write buffer space. */
+ if (Info.WriteQuotaAvailable > 0)
+ {
+ pThis->fPromisedWritable = false;
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ /* delayed buffer alloc or timeout: phony promise
+ later: See if we still can associate a semaphore with
+ the pipe, like on OS/2. */
+ if (Info.OutboundQuota == 0 || cMillies)
+ {
+ pThis->fPromisedWritable = true;
+ rc = VINF_SUCCESS;
+ break;
+ }
+ }
+ else
+ {
+ pThis->fPromisedWritable = true;
+ rc = VINF_SUCCESS;
+ break;
+ }
+#endif
+ }
+ }
+ if (RT_FAILURE(rc))
+ break;
+
+ /*
+ * Check for timeout.
+ */
+ DWORD cMsMaxWait = INFINITE;
+ if ( cMillies != RT_INDEFINITE_WAIT
+ && ( hWait != INVALID_HANDLE_VALUE
+ || iLoop > 10)
+ )
+ {
+ uint64_t cElapsed = RTTimeMilliTS() - StartMsTS;
+ if (cElapsed >= cMillies)
+ {
+ rc = VERR_TIMEOUT;
+ break;
+ }
+ cMsMaxWait = cMillies - (uint32_t)cElapsed;
+ }
+
+ /*
+ * Wait.
+ */
+ if (hWait != INVALID_HANDLE_VALUE)
+ {
+ RTCritSectLeave(&pThis->CritSect);
+
+ DWORD dwRc = WaitForSingleObject(hWait, cMsMaxWait);
+ if (dwRc == WAIT_OBJECT_0)
+ rc = VINF_SUCCESS;
+ else if (dwRc == WAIT_TIMEOUT)
+ rc = VERR_TIMEOUT;
+ else if (dwRc == WAIT_ABANDONED)
+ rc = VERR_INVALID_HANDLE;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ if ( RT_FAILURE(rc)
+ && pThis->u32Magic != RTPIPE_MAGIC)
+ return rc;
+
+ RTCritSectEnter(&pThis->CritSect);
+ if (pThis->fZeroByteRead)
+ {
+ pThis->cUsers--;
+ pThis->fIOPending = false;
+ if (rc != VINF_SUCCESS)
+ {
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NtCancelIoFile(pThis->hPipe, &Ios);
+ }
+ DWORD cbRead = 0;
+ GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/);
+ }
+ if (RT_FAILURE(rc))
+ break;
+ }
+ }
+
+ if (rc == VERR_BROKEN_PIPE)
+ pThis->fBrokenPipe = true;
+
+ RTCritSectLeave(&pThis->CritSect);
+ return rc;
+}
+
+
+RTDECL(int) RTPipeQueryReadable(RTPIPE hPipe, size_t *pcbReadable)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->fRead, VERR_PIPE_NOT_READ);
+ AssertPtrReturn(pcbReadable, VERR_INVALID_POINTER);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /** @todo The file size should give the same info and be slightly faster... */
+ DWORD cbAvailable = 0;
+ if (PeekNamedPipe(pThis->hPipe, NULL, 0, NULL, &cbAvailable, NULL))
+ {
+#if ARCH_BITS == 32
+ /*
+ * Kludge!
+ *
+ * Prior to XP SP1 (?), the returned cbAvailable value was not adjusted
+ * by the read position in the current message/buffer, so it could
+ * potentially be too high. This may cause the caller to try read more
+ * data than what's actually available, which may cause the read to
+ * block when the caller thought it wouldn't.
+ *
+ * To get an accurate readable size, we have to provide an output
+ * buffer and see how much we actually get back in it, as the data
+ * peeking works correctly (as you would expect).
+ */
+ if (cbAvailable == 0 || g_enmWinVer >= kRTWinOSType_XP64)
+ { /* No data available or kernel shouldn't be affected. */ }
+ else
+ {
+ for (unsigned i = 0; ; i++)
+ {
+ uint8_t abBufStack[_16K];
+ void *pvBufFree = NULL;
+ void *pvBuf;
+ DWORD cbBuf = RT_ALIGN_32(cbAvailable + i * 256, 64);
+ if (cbBuf <= sizeof(abBufStack))
+ {
+ pvBuf = abBufStack;
+ /* No cbBuf = sizeof(abBufStack) here! PeekNamedPipe bounce buffers the request on the heap. */
+ }
+ else
+ {
+ pvBufFree = pvBuf = RTMemTmpAlloc(cbBuf);
+ if (!pvBuf)
+ {
+ rc = VERR_NO_TMP_MEMORY;
+ cbAvailable = 1;
+ break;
+ }
+ }
+
+ DWORD cbAvailable2 = 0;
+ DWORD cbRead = 0;
+ BOOL fRc = PeekNamedPipe(pThis->hPipe, pvBuf, cbBuf, &cbRead, &cbAvailable2, NULL);
+ Log(("RTPipeQueryReadable: #%u: cbAvailable=%#x cbRead=%#x cbAvailable2=%#x (cbBuf=%#x)\n",
+ i, cbAvailable, cbRead, cbAvailable2, cbBuf));
+
+ RTMemTmpFree(pvBufFree);
+
+ if (fRc)
+ {
+ if (cbAvailable2 <= cbBuf || i >= 10)
+ cbAvailable = cbRead;
+ else
+ {
+ cbAvailable = cbAvailable2;
+ continue;
+ }
+ }
+ else
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ cbAvailable = 1;
+ }
+ break;
+ }
+ }
+#endif
+ *pcbReadable = cbAvailable;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ RTCritSectLeave(&pThis->CritSect);
+ return rc;
+}
+
+
+RTDECL(int) RTPipeQueryInfo(RTPIPE hPipe, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, 0);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, 0);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ AssertRCReturn(rc, 0);
+
+ rtPipeFakeQueryInfo(pObjInfo, enmAddAttr, pThis->fRead);
+
+ FILE_PIPE_LOCAL_INFORMATION Info;
+ if (rtPipeQueryNtInfo(pThis, &Info))
+ {
+ pObjInfo->cbAllocated = pThis->fRead ? Info.InboundQuota : Info.OutboundQuota;
+ pObjInfo->cbObject = pThis->fRead ? Info.ReadDataAvailable : Info.WriteQuotaAvailable;
+ }
+
+ RTCritSectLeave(&pThis->CritSect);
+ return VINF_SUCCESS;
+}
+
+
+int rtPipePollGetHandle(RTPIPE hPipe, uint32_t fEvents, PRTHCINTPTR phNative)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+
+ AssertReturn(!(fEvents & RTPOLL_EVT_READ) || pThis->fRead, VERR_INVALID_PARAMETER);
+ AssertReturn(!(fEvents & RTPOLL_EVT_WRITE) || !pThis->fRead, VERR_INVALID_PARAMETER);
+
+ /* Later: Try register an event handle with the pipe like on OS/2, there is
+ a file control for doing this obviously intended for the OS/2 subsys.
+ The question is whether this still exists on Vista and W7. */
+ *phNative = (RTHCINTPTR)pThis->Overlapped.hEvent;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Checks for pending events.
+ *
+ * @returns Event mask or 0.
+ * @param pThis The pipe handle.
+ * @param fEvents The desired events.
+ */
+static uint32_t rtPipePollCheck(RTPIPEINTERNAL *pThis, uint32_t fEvents)
+{
+ uint32_t fRetEvents = 0;
+ if (pThis->fBrokenPipe)
+ fRetEvents |= RTPOLL_EVT_ERROR;
+ else if (pThis->fRead)
+ {
+ if (!pThis->fIOPending)
+ {
+ DWORD cbAvailable;
+ if (PeekNamedPipe(pThis->hPipe, NULL, 0, NULL, &cbAvailable, NULL))
+ {
+ if ( (fEvents & RTPOLL_EVT_READ)
+ && cbAvailable > 0)
+ fRetEvents |= RTPOLL_EVT_READ;
+ }
+ else
+ {
+ if (GetLastError() == ERROR_BROKEN_PIPE)
+ pThis->fBrokenPipe = true;
+ fRetEvents |= RTPOLL_EVT_ERROR;
+ }
+ }
+ }
+ else
+ {
+ if (pThis->fIOPending)
+ {
+ rtPipeWriteCheckCompletion(pThis);
+ if (pThis->fBrokenPipe)
+ fRetEvents |= RTPOLL_EVT_ERROR;
+ }
+ if ( !pThis->fIOPending
+ && !fRetEvents)
+ {
+ FILE_PIPE_LOCAL_INFORMATION Info;
+ if (rtPipeQueryNtInfo(pThis, &Info))
+ {
+ /* Check for broken pipe. */
+ if (Info.NamedPipeState == FILE_PIPE_CLOSING_STATE)
+ {
+ fRetEvents = RTPOLL_EVT_ERROR;
+ pThis->fBrokenPipe = true;
+ }
+
+ /* Check if there is available buffer space. */
+ if ( !fRetEvents
+ && (fEvents & RTPOLL_EVT_WRITE)
+ && ( Info.WriteQuotaAvailable > 0
+ || Info.OutboundQuota == 0)
+ )
+ fRetEvents |= RTPOLL_EVT_WRITE;
+ }
+ else if (fEvents & RTPOLL_EVT_WRITE)
+ fRetEvents |= RTPOLL_EVT_WRITE;
+ }
+ }
+
+ return fRetEvents;
+}
+
+
+/**
+ * Internal RTPoll helper that polls the pipe handle and, if @a fNoWait is
+ * clear, starts whatever actions we've got running during the poll call.
+ *
+ * @returns 0 if no pending events, actions initiated if @a fNoWait is clear.
+ * Event mask (in @a fEvents) and no actions if the handle is ready
+ * already.
+ * UINT32_MAX (asserted) if the pipe handle is busy in I/O or a
+ * different poll set.
+ *
+ * @param hPipe The pipe handle.
+ * @param hPollSet The poll set handle (for access checks).
+ * @param fEvents The events we're polling for.
+ * @param fFinalEntry Set if this is the final entry for this handle
+ * in this poll set. This can be used for dealing
+ * with duplicate entries.
+ * @param fNoWait Set if it's a zero-wait poll call. Clear if
+ * we'll wait for an event to occur.
+ */
+uint32_t rtPipePollStart(RTPIPE hPipe, RTPOLLSET hPollSet, uint32_t fEvents, bool fFinalEntry, bool fNoWait)
+{
+ /** @todo All this polling code could be optimized to make fewer system
+ * calls; like for instance the ResetEvent calls. */
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, UINT32_MAX);
+ RT_NOREF_PV(fFinalEntry);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ AssertRCReturn(rc, UINT32_MAX);
+
+ /* Check that this is the only current use of this pipe. */
+ uint32_t fRetEvents;
+ if ( pThis->cUsers == 0
+ || pThis->hPollSet == hPollSet)
+ {
+ /* Check what the current events are. */
+ fRetEvents = rtPipePollCheck(pThis, fEvents);
+ if ( !fRetEvents
+ && !fNoWait)
+ {
+ /* Make sure the event semaphore has been reset. */
+ if (!pThis->fIOPending)
+ {
+ rc = ResetEvent(pThis->Overlapped.hEvent);
+ Assert(rc == TRUE);
+ }
+
+ /* Kick off the zero byte read thing if applicable. */
+ if ( !pThis->fIOPending
+ && pThis->fRead
+ && (fEvents & RTPOLL_EVT_READ)
+ )
+ {
+ DWORD cbRead = 0;
+ if (ReadFile(pThis->hPipe, pThis->abBuf, 0, &cbRead, &pThis->Overlapped))
+ fRetEvents = rtPipePollCheck(pThis, fEvents);
+ else if (GetLastError() == ERROR_IO_PENDING)
+ {
+ pThis->fIOPending = true;
+ pThis->fZeroByteRead = true;
+ }
+ else
+ fRetEvents = RTPOLL_EVT_ERROR;
+ }
+
+ /* If we're still set for the waiting, record the poll set and
+ mark the pipe used. */
+ if (!fRetEvents)
+ {
+ pThis->cUsers++;
+ pThis->hPollSet = hPollSet;
+ }
+ }
+ }
+ else
+ {
+ AssertFailed();
+ fRetEvents = UINT32_MAX;
+ }
+
+ RTCritSectLeave(&pThis->CritSect);
+ return fRetEvents;
+}
+
+
+/**
+ * Called after a WaitForMultipleObjects returned in order to check for pending
+ * events and stop whatever actions that rtPipePollStart() initiated.
+ *
+ * @returns Event mask or 0.
+ *
+ * @param hPipe The pipe handle.
+ * @param fEvents The events we're polling for.
+ * @param fFinalEntry Set if this is the final entry for this handle
+ * in this poll set. This can be used for dealing
+ * with duplicate entries. Only keep in mind that
+ * this method is called in reverse order, so the
+ * first call will have this set (when the entire
+ * set was processed).
+ * @param fHarvestEvents Set if we should check for pending events.
+ */
+uint32_t rtPipePollDone(RTPIPE hPipe, uint32_t fEvents, bool fFinalEntry, bool fHarvestEvents)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, 0);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, 0);
+ RT_NOREF_PV(fFinalEntry);
+ RT_NOREF_PV(fHarvestEvents);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ AssertRCReturn(rc, 0);
+
+ Assert(pThis->cUsers > 0);
+
+
+ /* Cancel the zero byte read. */
+ uint32_t fRetEvents = 0;
+ if (pThis->fZeroByteRead)
+ {
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NtCancelIoFile(pThis->hPipe, &Ios);
+
+ DWORD cbRead = 0;
+ if ( !GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/)
+ && GetLastError() != ERROR_OPERATION_ABORTED)
+ fRetEvents = RTPOLL_EVT_ERROR;
+
+ pThis->fIOPending = false;
+ pThis->fZeroByteRead = false;
+ }
+
+ /* harvest events. */
+ fRetEvents |= rtPipePollCheck(pThis, fEvents);
+
+ /* update counters. */
+ pThis->cUsers--;
+ /** @todo This isn't sane, or is it? See OS/2 impl. */
+ if (!pThis->cUsers)
+ pThis->hPollSet = NIL_RTPOLLSET;
+
+ RTCritSectLeave(&pThis->CritSect);
+ return fRetEvents;
+}
+